Skip to content

Commit af3d5d8

Browse files
authored
Merge pull request #35 from wai-coding/dev
feat: richer analysis rules and export polish
2 parents 43d2d95 + e3d45ab commit af3d5d8

18 files changed

Lines changed: 703 additions & 31 deletions

README.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@ Manual code review is time-consuming and inconsistent. InspectoRepo provides det
2424
| Rule | Severity | Description |
2525
|------|----------|-------------|
2626
| `unused-imports` | warn | Detects unused import specifiers (default, namespace, named) and suggests removal with a safe proposed patch |
27-
| `complexity-hotspot` | warn | Flags functions with high cyclomatic-like complexity (≥ 12) and suggests refactoring strategies |
27+
| `complexity-hotspot` | warn | Flags functions with high cyclomatic-like complexity (≥ 12) with specific contributor breakdown (nested conditionals, loops, ternaries, logical chains) and tailored suggestions |
2828
| `optional-chaining` | info | Detects monotonic guard chains like `a && a.b && a.b.c` and suggests optional chaining (`a?.b?.c`) |
2929
| `boolean-simplification` | info | Simplifies `x === true`, `x === false`, `!!x`, and `x ? true : false` patterns |
3030
| `early-return` | info | Detects unnecessary block-style early returns and suggests single-line guard clauses |
31+
| `no-debugger` | warn | Detects `debugger` statements left in code — auto-fixable |
32+
| `no-empty-catch` | warn | Flags empty catch blocks that silently hide errors — report only |
33+
| `no-useless-return` | info | Detects redundant `return;` at the end of functions — auto-fixable |
34+
| `ts-diagnostics` | error | Reports high-confidence TypeScript compiler diagnostics (unreachable code, duplicate identifiers, missing names, type mismatches) — report only |
3135

3236
## Tech Stack
3337

@@ -88,6 +92,8 @@ npm test
8892
| `npm test` | Run Vitest tests |
8993
| `npm run repopack` | Generate repomix exports |
9094

95+
> **Export packs:** `npm run repopack` generates four versioned files under `ai/exports/`. Use `repo-pack-latest-vN.md` for a quick lightweight review — it includes project structure and core source without docs, screenshots, or scripts.
96+
9197
## Demo
9298

9399
Try InspectoRepo locally in three steps:
@@ -140,7 +146,7 @@ Apply safe code fixes interactively:
140146
inspectorepo fix ./my-project
141147
```
142148

143-
The fix command runs analysis, finds issues with safe auto-fix suggestions, shows a preview of each proposed change, and asks for confirmation before applying. Only `optional-chaining`, `boolean-simplification`, and `unused-imports` rules support auto-fix. `complexity-hotspot` is never auto-applied.
149+
The fix command runs analysis, finds issues with safe auto-fix suggestions, shows a preview of each proposed change, and asks for confirmation before applying. Rules with auto-fix support: `optional-chaining`, `boolean-simplification`, `unused-imports`, `early-return`, `no-debugger`, and `no-useless-return`. Advisory rules like `complexity-hotspot`, `no-empty-catch`, and `ts-diagnostics` are never auto-applied.
144150

145151
### Fix Preview Mode
146152

@@ -196,13 +202,17 @@ The CLI uses the same analysis engine as the web UI. Output is deterministic —
196202

197203
## Rules
198204

199-
| Rule | Severity | Description |
200-
|------|----------|-------------|
201-
| `unused-imports` | warn | Detects unused import specifiers and suggests removal |
202-
| `complexity-hotspot` | warn | Flags high-complexity functions (≥ 12) with refactor suggestions |
203-
| `optional-chaining` | info | Suggests `?.` for monotonic guard chains |
204-
| `boolean-simplification` | info | Simplifies redundant boolean expressions |
205-
| `early-return` | info | Detects unnecessary block-style early returns |
205+
| Rule | Severity | Auto-fix | Description |
206+
|------|----------|----------|-------------|
207+
| `unused-imports` | warn || Detects unused import specifiers and suggests removal |
208+
| `complexity-hotspot` | warn || Flags high-complexity functions with specific contributor breakdown and tailored suggestions |
209+
| `optional-chaining` | info || Suggests `?.` for monotonic guard chains |
210+
| `boolean-simplification` | info || Simplifies redundant boolean expressions |
211+
| `early-return` | info || Detects unnecessary block-style early returns |
212+
| `no-debugger` | warn || Detects `debugger` statements left in code |
213+
| `no-empty-catch` | warn || Flags empty catch blocks that silently hide errors |
214+
| `no-useless-return` | info || Detects redundant `return;` at the end of functions |
215+
| `ts-diagnostics` | error || Reports high-confidence TypeScript compiler diagnostics |
206216

207217
## Configuration
208218

@@ -215,7 +225,11 @@ Create `.inspectorepo.json` in your project root to configure which rules run an
215225
"unused-imports": "warn",
216226
"complexity-hotspot": "off",
217227
"boolean-simplification": "warn",
218-
"early-return": "warn"
228+
"early-return": "warn",
229+
"no-debugger": "warn",
230+
"no-empty-catch": "warn",
231+
"no-useless-return": "warn",
232+
"ts-diagnostics": "off"
219233
}
220234
}
221235
```

ai/scripts/generate-repomix-exports.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,11 @@ const BANNED_BULLET_PATTERNS: RegExp[] = [
207207
/\bpatterns?\s+match/i,
208208
/\.ts[x]?[:\s]/i,
209209
/Checks? passed/i,
210-
// Milestone-title-style bullets (e.g. "M19 Web UI improvements")
210+
// Milestone-title-style bullets (e.g. "M19 Web UI improvements", "V3 Platform Milestone")
211211
/^M\d+\b/i,
212212
/\bM\d+\b.*\bM\d+\b/i,
213+
/^V\d+\s/i,
214+
/\bMilestone\b/i,
213215
];
214216

215217
function isBannedBullet(bullet: string): boolean {
@@ -521,15 +523,13 @@ const ROADMAP: RoadmapItem[] = [
521523
{ label: 'Summary-only CLI mode for fast CI checks', implemented: true },
522524
{ label: 'Lightweight repo-pack-latest export mode', implemented: true },
523525
{ label: 'Web app onboarding About section and empty state', implemented: true },
526+
{ label: 'Richer complexity warnings with contributor breakdown', implemented: true },
527+
{ label: 'Conservative analysis rules (no-debugger, no-empty-catch, no-useless-return, ts-diagnostics)', implemented: true },
524528
{ label: 'Deploy web app as a hosted service', implemented: false },
525529
{ label: 'Rule dependency graph and cascade analysis', implemented: false },
526530
{ label: 'Performance profiling for large codebases', implemented: false },
527531
{ label: 'VS Code extension inline fix suggestions', implemented: false },
528532
];
529-
{ label: 'Rule dependency graph and cascade analysis', implemented: false },
530-
{ label: 'Performance profiling for large codebases', implemented: false },
531-
{ label: 'VS Code extension inline fix suggestions', implemented: false },
532-
];
533533

534534
function generateNextMilestoneSection(): string {
535535
const future = ROADMAP.filter(r => !r.implemented).map(r => r.label);
@@ -674,8 +674,8 @@ ${formatGroupedFiles(milestoneFiles)}
674674
\`\`\`
675675
676676
## Known Limitations
677-
- Auto-fix supports 4 rules (optional-chaining, boolean-simplification, unused-imports, early-return) — more planned
678-
- Complexity-hotspot is advisory only — no auto-fix support
677+
- Auto-fix supports 6 rules (optional-chaining, boolean-simplification, unused-imports, early-return, no-debugger, no-useless-return) — more planned
678+
- Complexity-hotspot, no-empty-catch, and ts-diagnostics are advisory only — no auto-fix support
679679
- Browser folder picker requires Chrome/Edge (File System Access API)
680680
681681
## Next Milestone
@@ -690,6 +690,7 @@ npm run repopack
690690
This scans \`ai/exports/\` for existing versioned files, increments the version, runs repomix, and writes:
691691
- \`ai/exports/repo-pack-full-vN.md\` — full repository pack
692692
- \`ai/exports/repo-pack-core-vN.md\` — core-only pack (no docs/screenshots/.github)
693+
- \`ai/exports/repo-pack-latest-vN.md\` — lightweight pack for quick review (no docs/screenshots/.github/scripts)
693694
- \`ai/exports/changes-summary-vN.md\` — this file
694695
`;
695696

apps/web/src/components/MainPanel.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ export function MainPanel({ report, selectedIssue, onSelectIssue }: MainPanelPro
158158
{issue.suggestion.summary}
159159
</div>
160160
)}
161+
{issue.suggestion.details && (
162+
<div className="issue-expanded-suggestion">
163+
<span className="issue-expanded-suggestion-label">Details:</span>{' '}
164+
{issue.suggestion.details}
165+
</div>
166+
)}
161167
</div>
162168
)}
163169
</div>

docs/agent-worklog.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,42 @@ Development log for InspectoRepo. Each entry describes what was implemented, why
44

55
---
66

7+
## 2026-03-11 — Richer Analysis Rules and Export Polish
8+
9+
### What was implemented
10+
11+
- **Enriched complexity-hotspot warnings** — each complexity issue now includes function name, complexity score, contributor breakdown (if statements, loops, ternaries, logical chains, switch cases, nesting depth), and specific tailored suggestions instead of generic messages.
12+
- **New rule: `no-debugger`** — detects `debugger` statements left in code. Auto-fixable by removing the statement.
13+
- **New rule: `no-empty-catch`** — flags empty catch blocks that silently swallow errors. Report-only (no auto-fix).
14+
- **New rule: `no-useless-return`** — detects redundant `return;` at the end of function bodies. Auto-fixable.
15+
- **New rule: `ts-diagnostics`** — reports high-confidence TypeScript compiler diagnostics (unreachable code, duplicate identifiers, missing names, type mismatches). Report-only.
16+
- **Extended auto-fix support**`no-debugger` and `no-useless-return` now supported in the fix engine (preview + apply modes).
17+
- **Export workflow polish** — human summary generation now bans milestone-title bullets and raw commit prefixes more strictly. Regenerate section lists all 4 export files. repo-pack-latest documented as the preferred lightweight pack.
18+
- **Web UI details panel** — expanded issue view now shows both suggestion summary and details for richer context (e.g. complexity contributors).
19+
- **Preset updates** — all four presets (`recommended`, `strict`, `cleanup`, `react`) updated to include the new rules with conservative defaults.
20+
21+
### Why
22+
23+
The previous complexity warnings were too generic — every function got the same message regardless of what was actually making it complex. The new rules (`no-debugger`, `no-empty-catch`, `no-useless-return`, `ts-diagnostics`) are all syntax-exact or diagnostics-backed, making them highly reliable with zero false positives for their defined scope.
24+
25+
### How to verify
26+
27+
```bash
28+
npm run lint
29+
npm run typecheck
30+
npm run build
31+
npm test
32+
```
33+
34+
### Design decisions
35+
36+
- **Complexity breakdown is tracked per-function** — a `ComplexityBreakdown` struct accumulates counts for each contributor type during the recursive walk. This makes the output deterministic and the rule predictable.
37+
- **New rules are conservative**`no-debugger` and `no-empty-catch` are exact syntax matches with no ambiguity. `no-useless-return` only flags bare `return;` as the last statement. `ts-diagnostics` uses a curated subset of TS error codes.
38+
- **Auto-fix only for safe removals**`no-debugger` (remove statement) and `no-useless-return` (remove redundant return) are trivially safe. `no-empty-catch` and `ts-diagnostics` are never auto-fixed.
39+
- **ts-diagnostics off by default** — in `recommended` and `cleanup` presets, because in-memory ts-morph projects may not resolve all external types. Enabled in `strict` preset.
40+
41+
---
42+
743
## 2026-03-11 — V3 Platform Milestone
844

945
### What was implemented

docs/code-walkthrough.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,25 @@ Skips side-effect imports (`import './polyfill'`).
246246

247247
### `src/rules/complexity-hotspot.ts`
248248

249-
Counts control-flow complexity per function: if/else, switch cases, ternaries, logical operators, loops, try/catch, plus nesting depth bonus. Flags functions scoring ≥ 12.
249+
Counts control-flow complexity per function: if/else, switch cases, ternaries, logical operators, loops, try/catch, plus nesting depth bonus. Each function gets a `ComplexityBreakdown` struct tracking counts for each contributor type (ifStatements, loops, ternaries, logicalChains, switchCases, catchClauses, maxNestingDepth). Flags functions scoring ≥ 12.
250+
251+
Messages are specific: `Function "renderDashboard" has high complexity (22) driven by nested conditionals and ternaries.` Details include contributor counts (e.g. "4 if statements, 3 ternaries, nesting depth 3"). Suggestions are tailored to what was actually found (e.g. "replace nested conditionals with early returns" when if-statements dominate).
252+
253+
### `src/rules/no-debugger.ts`
254+
255+
Detects `debugger` statements via `SyntaxKind.DebuggerStatement`. Auto-fixable by removing the statement line. Severity: warn.
256+
257+
### `src/rules/no-empty-catch.ts`
258+
259+
Detects catch blocks with no statements inside via `SyntaxKind.CatchClause``getBlock().getStatements().length === 0`. Report-only (no auto-fix). Severity: warn.
260+
261+
### `src/rules/no-useless-return.ts`
262+
263+
Detects bare `return;` as the final statement of a function body. Only flags when the return text is exactly `"return;"` — does not flag returns with values. Auto-fixable by removing the redundant line. Severity: info.
264+
265+
### `src/rules/ts-diagnostics.ts`
266+
267+
Reports selected high-confidence TypeScript diagnostics from the analyzed file using `sourceFile.getPreEmitDiagnostics()`. Only maps a conservative subset of diagnostic codes: TS7027 (unreachable code), TS2300 (duplicate identifier), TS2304 (cannot find name), TS2339 (property does not exist), TS2554 (argument count mismatch), TS2322 (type assignment mismatch). Off by default in recommended/cleanup presets; enabled in strict preset.
250268

251269
### `src/rules/optional-chaining.ts`
252270

@@ -284,7 +302,7 @@ Rule preset system:
284302
- `isValidPreset(name)` — type guard checking if a string is a valid `PresetName`
285303
- `getPresetNames()` — returns the list of available preset names
286304

287-
Presets: `recommended` (all warn), `strict` (unused-imports + complexity at error), `cleanup` (complexity off, style rules on), `react` (unused-imports at error for TS+React projects).
305+
Presets: `recommended` (all warn, ts-diagnostics off), `strict` (unused-imports + complexity + no-debugger + no-empty-catch at error, ts-diagnostics on), `cleanup` (complexity off, ts-diagnostics off, style/cleanup rules on), `react` (unused-imports at error for TS+React projects, ts-diagnostics off).
288306

289307
### `src/config.ts`
290308

packages/cli/src/fixer.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ describe('isAutoFixable', () => {
4747
expect(isAutoFixable(issue)).toBe(false);
4848
});
4949

50+
it('returns true for no-debugger with diff', () => {
51+
const issue = makeIssue('no-debugger', '- debugger;');
52+
expect(isAutoFixable(issue)).toBe(true);
53+
});
54+
55+
it('returns true for no-useless-return with diff', () => {
56+
const issue = makeIssue('no-useless-return', '- return;');
57+
expect(isAutoFixable(issue)).toBe(true);
58+
});
59+
60+
it('returns false for no-empty-catch (no auto-fix)', () => {
61+
const issue = makeIssue('no-empty-catch');
62+
expect(isAutoFixable(issue)).toBe(false);
63+
});
64+
5065
it('returns false when no diff is provided', () => {
5166
const issue = makeIssue('optional-chaining');
5267
expect(isAutoFixable(issue)).toBe(false);

packages/cli/src/fixer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const SAFE_RULE_IDS = new Set([
88
'boolean-simplification',
99
'unused-imports',
1010
'early-return',
11+
'no-debugger',
12+
'no-useless-return',
1113
]);
1214

1315
export function isAutoFixable(issue: Issue): boolean {

packages/core/src/analyzer.test.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ describe('unused-imports rule', () => {
218218
});
219219

220220
describe('complexity-hotspot rule', () => {
221-
it('flags functions above threshold', () => {
221+
it('flags functions above threshold with function name and score', () => {
222222
// Build a function with many branches to exceed threshold of 12
223223
const code = [
224224
'export function complex(a: number, b: number, c: boolean) {',
@@ -252,8 +252,52 @@ describe('complexity-hotspot rule', () => {
252252
options: { rules: [complexityHotspotRule] },
253253
});
254254
expect(report.issues.length).toBe(1);
255-
expect(report.issues[0].ruleId).toBe('complexity-hotspot');
256-
expect(report.issues[0].severity).toBe('warn');
255+
const issue = report.issues[0];
256+
expect(issue.ruleId).toBe('complexity-hotspot');
257+
expect(issue.severity).toBe('warn');
258+
// Function name and score must appear in message
259+
expect(issue.message).toContain('"complex"');
260+
expect(issue.message).toMatch(/\(\d+\)/);
261+
// Contributors must appear in details
262+
expect(issue.suggestion.details).toMatch(/if statement/);
263+
expect(issue.suggestion.details).toMatch(/Complexity score: \d+/);
264+
// Suggestion should be specific, not generic
265+
expect(issue.suggestion.summary).toMatch(/Consider:/);
266+
});
267+
268+
it('includes contributor counts in details', () => {
269+
const code = [
270+
'export function branchy(x: number) {',
271+
' if (x > 0) {',
272+
' if (x > 1) {',
273+
' if (x > 2) {',
274+
' for (let i = 0; i < x; i++) {',
275+
' while (i > 0) {',
276+
' if (i % 2 === 0) {',
277+
' console.log(i);',
278+
' }',
279+
' }',
280+
' }',
281+
' }',
282+
' }',
283+
' }',
284+
' const a = x > 0 ? 1 : 0;',
285+
' const b = x > 1 ? 1 : 0;',
286+
' return a || b;',
287+
'}',
288+
].join('\n');
289+
290+
const files = [{ path: 'src/test.ts', content: code }];
291+
const report = analyzeCodebase({
292+
files,
293+
selectedDirectories: ['src'],
294+
options: { rules: [complexityHotspotRule] },
295+
});
296+
expect(report.issues.length).toBe(1);
297+
const details = report.issues[0].suggestion.details;
298+
expect(details).toContain('if statement');
299+
expect(details).toContain('loop');
300+
expect(details).toContain('ternary');
257301
});
258302

259303
it('does not flag simple functions', () => {

packages/core/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export {
99
optionalChainingRule,
1010
booleanSimplificationRule,
1111
earlyReturnRule,
12+
noDebuggerRule,
13+
noEmptyCatchRule,
14+
noUselessReturnRule,
15+
tsDiagnosticsRule,
1216
} from './rules/index.js';
1317
export {
1418
isExcludedDir,

packages/core/src/presets.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,43 @@ const PRESETS: Record<PresetName, RuleConfig> = {
99
'optional-chaining': 'warn',
1010
'boolean-simplification': 'warn',
1111
'early-return': 'warn',
12+
'no-debugger': 'warn',
13+
'no-empty-catch': 'warn',
14+
'no-useless-return': 'warn',
15+
'ts-diagnostics': 'off',
1216
},
1317
strict: {
1418
'unused-imports': 'error',
1519
'complexity-hotspot': 'error',
1620
'optional-chaining': 'warn',
1721
'boolean-simplification': 'warn',
1822
'early-return': 'warn',
23+
'no-debugger': 'error',
24+
'no-empty-catch': 'error',
25+
'no-useless-return': 'warn',
26+
'ts-diagnostics': 'error',
1927
},
2028
cleanup: {
2129
'unused-imports': 'warn',
2230
'complexity-hotspot': 'off',
2331
'optional-chaining': 'warn',
2432
'boolean-simplification': 'warn',
2533
'early-return': 'warn',
34+
'no-debugger': 'warn',
35+
'no-empty-catch': 'warn',
36+
'no-useless-return': 'warn',
37+
'ts-diagnostics': 'off',
2638
},
2739
react: {
2840
'unused-imports': 'error',
2941
'complexity-hotspot': 'warn',
3042
'optional-chaining': 'warn',
3143
'boolean-simplification': 'warn',
3244
'early-return': 'warn',
45+
'no-debugger': 'warn',
46+
'no-empty-catch': 'warn',
47+
'no-useless-return': 'warn',
48+
'ts-diagnostics': 'off',
3349
},
3450
};
3551

0 commit comments

Comments
 (0)