Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go-linter-driven-development/agents/go-code-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Your job: Analyze the code and return a **structured report** that the orchestra

<step number="1" name="Load Pre-Commit Review Skill">

Automatically use the @pre-commit-review skill to guide your analysis. This skill contains:
**Use the Skill tool** to invoke `Skill(go-linter-driven-development:pre-commit-review)` to load design analysis guidance. This skill contains:
- Detection checklist for 8 design issue categories
- Juiciness scoring algorithm for primitive obsession
- Examples of good vs bad patterns
Expand Down
15 changes: 15 additions & 0 deletions go-linter-driven-development/agents/quality-analyzer.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,21 @@ normalized_issue:
message: "Cognitive complexity 18 (>15)"
raw_output: "..."
```

**Linter Categorization Reference:**

| Linter | Category | Severity | Routes To |
|--------|----------|----------|-----------|
| `nestif` | complexity | high | @refactoring (storify → early returns) |
| `cyclop`, `gocognit` | complexity | high | @refactoring (storify → extract type) |
| `funlen` | complexity | medium | @refactoring (storify → extract function) |
| `argument-limit` (revive) | design | high | @code-designing (options struct) |
| `function-result-limit` (revive) | design | high | @code-designing (result type) |
| `confusing-results` (revive) | design | medium | @code-designing (named result type) |
| `early-return` (revive) | style | low | @refactoring (early return pattern) |
| `file-length-limit` (revive) | design | high | @code-designing (file splitting) |
| `wrapcheck` | bug | medium | Direct fix (error wrapping) |
| `varnamelen` | style | low | Direct fix (rename variable) |
</phase>

<phase name="D" title="Find Overlapping Issues">
Expand Down
2 changes: 1 addition & 1 deletion go-linter-driven-development/commands/go-ldd-autopilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ allowed-tools:
- Skill(go-linter-driven-development:linter-driven-development)
---

Invoke the @linter-driven-development skill to run the complete autopilot workflow from design through commit-ready.
**Use the Skill tool** to invoke `Skill(go-linter-driven-development:linter-driven-development)` to run the complete autopilot workflow from design through commit-ready.

⏱️ **Estimated Duration**: 5-15 minutes (depends on feature complexity and issues found)

Expand Down
10 changes: 8 additions & 2 deletions go-linter-driven-development/commands/go-ldd-quickfix.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Execute the quality gates loop for already-implemented code that needs cleanup.

⏱️ **Estimated Duration**: 2-5 minutes (depends on number of issues found)

Run these phases from @linter-driven-development skill:
**Use the Skill tool** to invoke `Skill(go-linter-driven-development:linter-driven-development)` and run these phases:

**Phase 2**: Parallel Analysis
- Discover project test/lint commands
Expand All @@ -29,8 +29,14 @@ Run these phases from @linter-driven-development skill:
- Re-verify with parallel analysis (incremental review mode)
- Repeat until all green

**Phase 5**: Orchestrator Review (after linter clean)
- Check types with >15 methods (god object threshold)
- If found: Apply @refactoring for storification first
- Then apply @code-designing for composition (service extraction)
- Re-verify with linter

**Loop until**:
✅ Tests pass | ✅ Linter clean | ✅ Review clean
✅ Tests pass | ✅ Linter clean | ✅ Review clean | ✅ No god objects (≤15 methods per type)

Use this when code is already written but needs to pass quality gates.
Skip the implementation phase (Phase 1) and go straight to fixing issues.
79 changes: 79 additions & 0 deletions go-linter-driven-development/hooks/check-package-sizes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Portable package-size gate for the go-linter-driven-development plugin.
#
# Fires as a PostToolUse hook after Write / Edit / MultiEdit. Scans Go source
# roots under $CLAUDE_PROJECT_DIR and counts non-test, non-generated .go files
# per directory at one level deep.
#
# Thresholds (also documented in the refactoring and pre-commit-review skills):
# >=13 files = RED -> exit 2, stderr is fed back to Claude as a blocking
# error so the violation is acknowledged before more
# code lands in the oversized package.
# 8-12 files = YELLOW -> exit 0 with stdout advisory, no block.
# <=7 files = GREEN -> silent, exit 0.
#
# Guards:
# - no-op unless $CLAUDE_PROJECT_DIR/go.mod exists (not a Go project)
# - no-op if none of internal/, cmd/, pkg/ exist
#
# Uses only POSIX-portable tools: find, wc, tr, sort. No jq / python / task.

set -u

PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"

[[ -f "$PROJECT_DIR/go.mod" ]] || exit 0

YELLOW_MIN=8
RED_MIN=13

roots=()
for d in internal cmd pkg; do
[[ -d "$PROJECT_DIR/$d" ]] && roots+=("$PROJECT_DIR/$d")
done

(( ${#roots[@]} == 0 )) && exit 0

red_lines=()
yellow_lines=()

while IFS= read -r dir; do
[[ -z "$dir" ]] && continue
count=$(find "$dir" -maxdepth 1 -type f -name '*.go' \
-not -name '*_test.go' \
-not -name '*_gen.go' \
-not -name '*.pb.go' \
2>/dev/null | wc -l | tr -d ' ')

rel=${dir#$PROJECT_DIR/}
if (( count >= RED_MIN )); then
red_lines+=(" $rel: $count non-test .go files (RED zone, must decompose)")
elif (( count >= YELLOW_MIN )); then
yellow_lines+=(" $rel: $count non-test .go files (YELLOW zone, design review before next file)")
fi
done < <(find "${roots[@]}" -type d \
-not -path '*/vendor/*' \
-not -path '*/testdata/*' \
2>/dev/null | sort -u)

if (( ${#red_lines[@]} > 0 )); then
{
echo "⛔ Package size gate — RED zone detected:"
printf '%s\n' "${red_lines[@]}"
echo ""
echo "These packages MUST be decomposed before more code lands."
echo "Apply the 3-step design review (see refactoring skill <package_decomposition>):"
echo " 1. Does the package name reflect a real-world domain concept (not a role/container)?"
echo " 2. Are types well-scoped, or are there big structs hiding sub-types or primitive-obsession fields?"
echo " 3. Only after the type review, decide: sub-packages, new leaf types, or both."
} >&2
exit 2
fi

if (( ${#yellow_lines[@]} > 0 )); then
echo "⚠️ Package size gate — YELLOW zone:"
printf '%s\n' "${yellow_lines[@]}"
echo "Design review recommended before the next file lands in these packages."
fi

exit 0
15 changes: 15 additions & 0 deletions go-linter-driven-development/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/check-package-sizes.sh"
}
]
}
]
}
}
133 changes: 133 additions & 0 deletions go-linter-driven-development/skills/code-designing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ Use when planning new features or identifying need for new types during refactor
**Reference**: See `reference.md` for complete design principles and examples.
</objective>

<skill_invocation>
**CRITICAL**: When this skill says "Use @skill-name" or routes to "@skill-name", you MUST use the **Skill tool** explicitly.

| Notation | Skill Tool Call |
|----------|-----------------|
| @testing | `Skill(go-linter-driven-development:testing)` |

**DO NOT** just reference the skill - actually invoke it using the Skill tool.
</skill_invocation>

<quick_start>
1. **Analyze Architecture**: Check for vertical vs horizontal slicing
2. **Understand Domain**: Identify problem domain, concepts, invariants
Expand All @@ -31,6 +41,11 @@ Ready to implement? Use @testing skill for test structure.
- Refactoring reveals need for new types (complexity extraction)
- Linter failures suggest types should be introduced
- When you need to think through domain modeling
- **`argument-limit`** linter failure (>4 parameters) → Design options struct
- **`function-result-limit`** linter failure (>3 returns) → Design result type
- **`confusing-results`** linter failure → Design named result type
- **`file-length-limit`** linter failure (>450 lines) → Analyze and split juicy types to own files
- **PostToolUse package-size hook** reports yellow/red zone → design-time intervention: re-model with sub-packages *before* the zone escalates (full decomposition playbook in @refactoring `<package_decomposition>`)
</when_to_use>

<purpose>
Expand Down Expand Up @@ -146,6 +161,31 @@ domain/user.go
services/user_service.go
repository/user_repository.go
```

**Package naming method** (for feature and sub-package design):

1. **Model the real-world relationship.** Ask: "What IS this system? What does it DO? What does it operate ON?"
- A worker HAS a job → `worker/` + `worker/job/` (`job.ID`, `job.Status`)
- A compiler HAS tokens → `compiler/` + `compiler/token/`
- A scheduler HAS tasks → `scheduler/` + `scheduler/task/`
2. **The parent names the actor/system** (the thing that does the work).
3. **The sub-package names the domain object** (the thing being acted upon) — this is where your `pkg.Type` call sites live.
4. **Test**: say `pkg.Type` out loud. `job.ID` sounds right. `domain.ID` sounds like Java.

**Package-name anti-patterns** (never use — they describe roles or act as dumping grounds):
- Role names: `handlers/`, `types/`, `model/`
- Generic containers: `common/`, `shared/`, `core/`, `base/`, `util/`, `helpers/`, `domain/`

**Import direction** (strictly downward — plan this up front to avoid cycles):
```
leaf types (domain) ← (nothing)
sub-packages ← leaf types
parent ← leaf types + sub-packages
cmd/ ← everything
```
If the parent needs sub-package logic AND the sub-package needs parent types, extract the shared types into a leaf sub-package from day one.

**When decomposing an existing package** (red/yellow zone), see @refactoring `<package_decomposition>` for the full 3-step design review and phased migration.
</plan_package_structure>

<design_orchestrating_types>
Expand Down Expand Up @@ -191,6 +231,97 @@ Check design against (see reference.md):
- [ ] Each type owns its validation; composed self-validating types are trusted, not re-validated
</review_against_principles>

<linter_triggered_patterns>
**When invoked by linter failures, apply these patterns:**

<pattern name="options_struct" trigger="argument-limit (>4 params)">
```go
// BEFORE - Too many parameters
func CreateUser(name string, email string, age int, role string, dept string) (*User, error)

// AFTER - Options struct
type CreateUserOptions struct {
Name string
Email string
Age int
Role string
Dept string
}

func CreateUser(opts CreateUserOptions) (*User, error)
```
**Design Tip**: Add validation in a constructor: `NewCreateUserOptions(...) (CreateUserOptions, error)`
</pattern>

<pattern name="result_type" trigger="function-result-limit (>3 returns)">
```go
// BEFORE - Too many return values
func ParseConfig(path string) (config Config, warnings []string, version int, error)

// AFTER - Result type
type ParseConfigResult struct {
Config Config
Warnings []string
Version int
}

func ParseConfig(path string) (ParseConfigResult, error)
```
</pattern>

<pattern name="named_result_type" trigger="confusing-results">
```go
// BEFORE - Confusing (string, string, error)
func ParseAddress(raw string) (string, string, error) // Which is host? Which is port?

// AFTER - Named result type
type ParsedAddress struct {
Host string
Port string
}

func ParseAddress(raw string) (ParsedAddress, error)
```
</pattern>

<pattern name="file_splitting" trigger="file-length-limit (revive) - file > 450 lines">
**Step 1: Analyze file structure**

| File Pattern | Action |
|--------------|--------|
| Multiple juicy types | Move each juicy type to its own file |
| Single god type | Extract method clusters via composition OR extract juicy logic from methods |
| Long functions, few types | Route to @refactoring first (storify → extract functions) |

**Step 2: Apply juiciness test**

"Juicy" types (deserve their own file):
- Types with ≥2 methods
- Types with complex validation
- Types with transformations/parsing
- Enums WITH methods (behavior makes them juicy)

"Anemic" types (can stay grouped in types.go or similar):
- Simple enums (const block only)
- DTOs with no methods
- Type aliases

**Step 3: For god types** (>15 methods)

| Option | When to Use | Pattern |
|--------|-------------|---------|
| **Storify first** | Methods are hard to read | Apply storification → reveals hidden structure |
| **Extract via composition** | Method clusters exist | Identify cluster → extract to new type → compose |
| **Extract juicy logic** | Primitive obsession inside methods | Find logic on primitives → extract to self-validating type |

**Routing**: God types require two-phase refactoring:
1. **@refactoring** (first): Storify methods to reveal structure
2. **@code-designing** (then): Design service composition

See @refactoring skill → `god_object_decomposition` pattern for detailed mechanics.
</pattern>
</linter_triggered_patterns>

</workflow>

<output_format>
Expand Down Expand Up @@ -269,6 +400,8 @@ Design phase is complete when ALL of the following are true:
- [ ] Core domain types identified with validation rules
- [ ] Self-validating type design documented
- [ ] Package structure follows vertical slice pattern
- [ ] Package names reflect real-world domain concepts (not role names like `handlers/`/`types/` or containers like `common/`/`domain/`); `pkg.Type` reads like English
- [ ] Import direction is strictly downward (leaf types ← sub-packages ← parent ← cmd/)
- [ ] Design decisions documented with rationale
- [ ] Pre-code review questions answered satisfactorily
- [ ] Design plan output presented to user
Expand Down
1 change: 0 additions & 1 deletion go-linter-driven-development/skills/documentation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ allowed-tools:
- Glob
- Write
- Edit
- mcp__ide__getDiagnostics
---

<objective>
Expand Down
Loading