|
| 1 | +--- |
| 2 | +name: tech-debt-finder |
| 3 | +description: > |
| 4 | + Use when the user says "find tech debt", "audit code quality", "what needs cleanup", |
| 5 | + "show me debt", "code health check", "scan for smells", or wants a comprehensive |
| 6 | + quality assessment before planning a refactor or sprint. |
| 7 | +argument-hint: "[path/to/scope or 'full']" |
| 8 | +allowed-tools: Bash, Read, Glob, Grep, Agent |
| 9 | +--- |
| 10 | + |
| 11 | +# Tech Debt Finder |
| 12 | + |
| 13 | +Scans the codebase for architectural and code-level technical debt, producing a prioritized report and creating a GitHub issue with actionable findings. |
| 14 | + |
| 15 | +**Announce at start:** "Using tech-debt-finder to scan for technical debt..." |
| 16 | + |
| 17 | +--- |
| 18 | + |
| 19 | +## Configuration |
| 20 | + |
| 21 | +| Setting | Default | Description | |
| 22 | +|------------------------|----------------------------|---------------------------------| |
| 23 | +| **Scope** | `$ARGUMENTS` or repo root | Directory to scan | |
| 24 | +| **nesting_depth** | 4 | Deep nesting threshold | |
| 25 | + |
| 26 | +--- |
| 27 | + |
| 28 | +## Execution Flow |
| 29 | + |
| 30 | +### Step 1: Resolve Scope |
| 31 | + |
| 32 | +Parse `$ARGUMENTS`: |
| 33 | +- If a path is provided, scope all scans to that directory |
| 34 | +- If empty or `full`, scan from repo root |
| 35 | +- Identify all Python packages (look for `pyproject.toml` or `src/` dirs) |
| 36 | +- Exclude: `node_modules/`, `.venv/`, `__pycache__/`, `build/`, `dist/`, `.git/` |
| 37 | + |
| 38 | +### Step 2: Dispatch Parallel Scanners |
| 39 | + |
| 40 | +Launch **3 agents in parallel** using the Agent tool. Each agent receives the resolved scope path and configuration. Each agent returns structured findings as a list of `{category, item, severity, detail, file, line}`. |
| 41 | + |
| 42 | +Each agent is free to use whatever detection approach works best — grep, AST parsing, external tools (radon, ruff, pip-audit), file reading, or any combination. The goal is accurate detection, not a specific technique. |
| 43 | + |
| 44 | +**Calibration:** This codebase generally follows established practices (DRY, single responsibility, explicit error handling). Set a high bar for findings — flag genuine violations, not borderline cases. |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +**Agent A — Dependency & Environment Scanner** |
| 49 | + |
| 50 | +Detect debt in these categories: |
| 51 | + |
| 52 | +| Debt Type | What It Looks Like | Severity | |
| 53 | +|-----------|--------------------|----------| |
| 54 | +| **Known CVE** | A dependency has a published security vulnerability (use `pip-audit` or `safety` if available) | Critical | |
| 55 | +| **Outdated dependency** | Package >1 major version behind latest | High | |
| 56 | +| **Outdated dependency** | Package >1 minor version behind latest | Medium | |
| 57 | +| **Unused dependency** | Dependency declared in `pyproject.toml` but never imported in source or tests | Low | |
| 58 | +| **Circular dependency signal** | `if TYPE_CHECKING:` blocks or function-level imports used as circular import workarounds | Medium | |
| 59 | +| **Pinning gap** | Dependency with no upper bound on major version (e.g. `>=2.0` with no `<3`) | Low | |
| 60 | + |
| 61 | +Return findings with category `dependency`. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +**Agent B — Structural & Complexity Scanner** |
| 66 | + |
| 67 | +Detect debt in these categories: |
| 68 | + |
| 69 | +| Debt Type | What It Looks Like | Severity | |
| 70 | +|-----------|--------------------|----------| |
| 71 | +| **God module** | Python file that is excessively long — too many responsibilities in one module | High | |
| 72 | +| **High cyclomatic complexity** | Function with complexity >= 16 (use `radon` if available) | High | |
| 73 | +| **Moderate cyclomatic complexity** | Function with complexity 11–15 | Medium | |
| 74 | +| **Deep nesting** | Code indented beyond `nesting_depth` levels | Medium | |
| 75 | +| **Business logic leakage** | ORM/DB calls, HTTP client calls, or validation logic in controller/handler/view files instead of the appropriate layer | High | |
| 76 | +| **Import direction violation** | Lower layer importing from higher layer (e.g. models importing from providers, utils importing from domain code) | High | |
| 77 | +| **Code duplication** | Substantial blocks of near-identical code across files — same logic with only minor variations (variable names, literals). Look for duplicated functions, repeated conditional chains, and copy-pasted blocks | Medium | |
| 78 | + |
| 79 | +Return findings with category `architecture`, `complexity`, or `duplication`. |
| 80 | + |
| 81 | +--- |
| 82 | + |
| 83 | +**Agent C — Code Pattern Scanner** |
| 84 | + |
| 85 | +Detect debt in these categories: |
| 86 | + |
| 87 | +| # | Debt Type | What It Looks Like | Example | Severity | Category | |
| 88 | +|---|-----------|-------------------|---------|----------|----------| |
| 89 | +| 1 | **Bare except** | `except:` with no exception type — catches SystemExit, KeyboardInterrupt | `except:\n pass` | Critical | error-handling | |
| 90 | +| 2 | **Swallowed exception** | `except Exception` where the body is only `pass` or `continue` — no logging, no re-raise | `except Exception:\n pass` | High | error-handling | |
| 91 | +| 3 | **Generic catch without re-raise** | `except Exception` block that never re-raises — callers never know the operation failed | `except Exception as e:\n logger.error(e)` | Medium | error-handling | |
| 92 | +| 4 | **Any overuse** | `-> Any` or `: Any` in non-test files, especially `dict[str, Any]` where a TypedDict/model would be better | `def get_config() -> Any:` | Medium | code-smell | |
| 93 | +| 5 | **Blocking call in async** | `time.sleep`, synchronous `requests.*`, or blocking file I/O inside an `async def` | `async def fetch():\n time.sleep(5)` | High | async | |
| 94 | +| 6 | **Mutable default argument** | `def f(x=[])` or `def f(x={})` — the default is shared across all calls, a correctness bug | `def add(item, items=[]):` | High | code-smell | |
| 95 | +| 7 | **Magic numbers** | Numeric literals >2 digits in non-test source, excluding common constants (0, 1, 100, HTTP status codes) | `timeout = 8473` | Medium | code-smell | |
| 96 | +| 8 | **Dead code — commented out** | Commented-out function defs, imports, returns, or class definitions. Git has the history | `# def old_handler():` | Low | code-smell | |
| 97 | +| 9 | **Global mutable state** | Module-level mutable collections (dicts, lists, sets) that get mutated at runtime — thread-safety risk in async contexts | `CACHE = {}\n...\nCACHE[key] = val` | Medium | code-smell | |
| 98 | + |
| 99 | +Return findings with their respective categories. |
| 100 | + |
| 101 | +--- |
| 102 | + |
| 103 | +### Step 3: Collect & Classify |
| 104 | + |
| 105 | +Merge all agent results into a single list. Classify by severity: |
| 106 | + |
| 107 | +| Severity | Criteria | |
| 108 | +|-------------|---------------------------------------------------------------------------| |
| 109 | +| **Critical** | Known CVEs, bare `except:` with `pass` | |
| 110 | +| **High** | God modules, swallowed exceptions, blocking in async, layer violations, mutable defaults | |
| 111 | +| **Medium** | High complexity (radon), `Any` overuse, deep nesting, magic numbers, duplication | |
| 112 | +| **Low** | Unused deps, commented-out code | |
| 113 | + |
| 114 | +**Deduplication:** If the same `file:line` appears in multiple categories, keep the highest severity instance. |
| 115 | + |
| 116 | +**Sorting:** Within each severity group, sort by file path then line number. |
| 117 | + |
| 118 | +### Step 4: Print Report to Terminal |
| 119 | + |
| 120 | +Output the report directly to the user: |
| 121 | + |
| 122 | +``` |
| 123 | +## Tech Debt Report — {scope} |
| 124 | +**Scanned:** {file_count} files | **Date:** {YYYY-MM-DD} |
| 125 | +**Findings:** {critical} critical, {high} high, {medium} medium, {low} low |
| 126 | +
|
| 127 | +### Critical ({count}) |
| 128 | +| File | Line | Finding | Category | |
| 129 | +|------|------|---------|----------| |
| 130 | +| ... | ... | ... | ... | |
| 131 | +
|
| 132 | +### High ({count}) |
| 133 | +| File | Line | Finding | Category | |
| 134 | +|------|------|---------|----------| |
| 135 | +| ... | ... | ... | ... | |
| 136 | +
|
| 137 | +### Medium ({count}) |
| 138 | +| File | Line | Finding | Category | |
| 139 | +|------|------|---------|----------| |
| 140 | +| ... | ... | ... | ... | |
| 141 | +
|
| 142 | +### Low ({count}) |
| 143 | +| File | Line | Finding | Category | |
| 144 | +|------|------|---------|----------| |
| 145 | +| ... | ... | ... | ... | |
| 146 | +
|
| 147 | +### Summary |
| 148 | +
|
| 149 | +| Category | Critical | High | Medium | Low | Total | |
| 150 | +|-----------------|----------|------|--------|-----|-------| |
| 151 | +| dependency | ... | ... | ... | ... | ... | |
| 152 | +| architecture | ... | ... | ... | ... | ... | |
| 153 | +| complexity | ... | ... | ... | ... | ... | |
| 154 | +| error-handling | ... | ... | ... | ... | ... | |
| 155 | +| code-smell | ... | ... | ... | ... | ... | |
| 156 | +| async | ... | ... | ... | ... | ... | |
| 157 | +| duplication | ... | ... | ... | ... | ... | |
| 158 | +
|
| 159 | +### Hotspots (files with 3+ findings) |
| 160 | +
|
| 161 | +| File | Findings | Highest Severity | |
| 162 | +|------|----------|------------------| |
| 163 | +| ... | ... | ... | |
| 164 | +``` |
| 165 | + |
| 166 | +### Step 5: Create GitHub Issue |
| 167 | + |
| 168 | +Automatically create a GitHub issue with the findings. No confirmation needed. |
| 169 | + |
| 170 | +**5a. Ensure the `tech-debt` label exists:** |
| 171 | + |
| 172 | +```bash |
| 173 | +gh label create "tech-debt" --color "D93F0B" --description "Technical debt findings" 2>/dev/null || true |
| 174 | +``` |
| 175 | + |
| 176 | +**5b. Build the issue body.** |
| 177 | + |
| 178 | +Format every finding as a checkbox so the team can track resolution: |
| 179 | + |
| 180 | +```markdown |
| 181 | +# Tech Debt Audit — {scope} |
| 182 | + |
| 183 | +**Scanned:** {file_count} files | **Date:** {YYYY-MM-DD} |
| 184 | +**Findings:** {critical} critical, {high} high, {medium} medium, {low} low |
| 185 | + |
| 186 | +--- |
| 187 | + |
| 188 | +## Critical |
| 189 | +- [ ] `file.py:42` — Known CVE in dependency X *(dependency)* |
| 190 | +- [ ] `file.py:88` — Bare `except:` with `pass` *(error-handling)* |
| 191 | + |
| 192 | +## High |
| 193 | +- [ ] `provider.py` — God module, too many responsibilities *(architecture)* |
| 194 | +- [ ] `handler.py:55` — Exception swallowed, no re-raise *(error-handling)* |
| 195 | +- [ ] `utils.py:30` — `time.sleep` inside `async def` *(async)* |
| 196 | + |
| 197 | +## Medium |
| 198 | +- [ ] `api.py:120` — Cyclomatic complexity 14 *(complexity)* |
| 199 | +- [ ] `models.py:45` — `-> Any` return type *(code-smell)* |
| 200 | +- [ ] `config.py:33` — Magic number `8473` *(code-smell)* |
| 201 | +- [ ] `handlers.py` + `views.py` — Near-identical request validation logic *(duplication)* |
| 202 | + |
| 203 | +## Low |
| 204 | +- [ ] `utils.py:10` — Commented-out function definition *(code-smell)* |
| 205 | + |
| 206 | +--- |
| 207 | + |
| 208 | +## Summary |
| 209 | + |
| 210 | +| Category | Critical | High | Medium | Low | Total | |
| 211 | +|----------|----------|------|--------|-----|-------| |
| 212 | +| ... | ... | ... | ... | ... | ... | |
| 213 | + |
| 214 | +## Hotspots (files with 3+ findings) |
| 215 | + |
| 216 | +| File | Findings | Highest Severity | |
| 217 | +|------|----------|------------------| |
| 218 | +| ... | ... | ... | |
| 219 | + |
| 220 | +## Recommended Actions |
| 221 | + |
| 222 | +1. **Critical** — fix immediately, security/correctness risks |
| 223 | +2. **High** — schedule for current sprint, use `/refactor` for code-level fixes |
| 224 | +3. **Medium** — plan for next sprint, use `/planning` for architectural items |
| 225 | +4. **Low** — address opportunistically during related work |
| 226 | +``` |
| 227 | + |
| 228 | +**5c. Handle body size limit.** |
| 229 | + |
| 230 | +GitHub issues have a 65536 character body limit. If the body exceeds 60000 characters: |
| 231 | +1. Truncate **Low** findings first, replacing with `- [ ] ... and {N} more low-severity findings` |
| 232 | +2. If still too large, truncate **Medium** findings similarly |
| 233 | +3. Never truncate Critical or High findings |
| 234 | + |
| 235 | +**5d. Create the issue:** |
| 236 | + |
| 237 | +```bash |
| 238 | +gh issue create \ |
| 239 | + --title "Tech Debt Audit — {YYYY-MM-DD} — {scope}" \ |
| 240 | + --label "tech-debt" \ |
| 241 | + --body "$(cat <<'EOF' |
| 242 | +{issue body} |
| 243 | +EOF |
| 244 | +)" |
| 245 | +``` |
| 246 | + |
| 247 | +**5e. Print the issue URL** to the user after creation. |
| 248 | + |
| 249 | +--- |
| 250 | + |
| 251 | +## Error Handling |
| 252 | + |
| 253 | +| Scenario | Action | |
| 254 | +|----------|--------| |
| 255 | +| `radon` not installed | Skip complexity checks. Note in report: "Complexity checks skipped — install `radon`" | |
| 256 | +| `pip-audit` not installed | Skip CVE checks. Note in report: "CVE checks skipped — install `pip-audit`" | |
| 257 | +| Scope path doesn't exist | Report error and **stop** | |
| 258 | +| No Python files in scope | Report "no files to scan" and **stop** | |
| 259 | +| An agent fails | Report partial results from successful agents. Note which scanner failed and why | |
| 260 | +| `gh` not authenticated | Print report to terminal. Warn: "GitHub issue creation skipped — run `gh auth login`" | |
| 261 | +| Issue body exceeds limit | Truncate Low then Medium findings (see Step 5c) | |
| 262 | + |
| 263 | +--- |
| 264 | + |
| 265 | +## What This Skill Does NOT Do |
| 266 | + |
| 267 | +- Does not fix debt — use `/refactor` or `/orchestrate` for that |
| 268 | +- Does not replace `ruff` or `pyright` — complements them with higher-level analysis |
| 269 | +- Does not run tests or measure coverage — use `/coverage-guard` for that |
| 270 | +- Does not modify any source files |
| 271 | +- Does not track debt over time — each run is a fresh scan |
| 272 | +- Does not analyze git history for bug hotspots |
0 commit comments