@nebutra/next-unicorn-skill 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +96 -9
- package/README.zh-CN.md +474 -0
- package/SKILL.md +108 -30
- package/dist/analyzer/code-organization-analyzer.d.ts +26 -0
- package/dist/analyzer/code-organization-analyzer.d.ts.map +1 -0
- package/dist/analyzer/code-organization-analyzer.js +389 -0
- package/dist/analyzer/code-organization-analyzer.js.map +1 -0
- package/dist/analyzer/pattern-catalog.d.ts.map +1 -1
- package/dist/analyzer/pattern-catalog.js +33 -0
- package/dist/analyzer/pattern-catalog.js.map +1 -1
- package/dist/analyzer/scanner.d.ts +2 -0
- package/dist/analyzer/scanner.d.ts.map +1 -1
- package/dist/analyzer/scanner.js +16 -1
- package/dist/analyzer/scanner.js.map +1 -1
- package/dist/analyzer/structure-analyzer.d.ts +3 -1
- package/dist/analyzer/structure-analyzer.d.ts.map +1 -1
- package/dist/analyzer/structure-analyzer.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/schemas/input.schema.d.ts +4 -4
- package/dist/schemas/input.schema.d.ts.map +1 -1
- package/dist/schemas/input.schema.js +1 -0
- package/dist/schemas/input.schema.js.map +1 -1
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
name: analyze-and-recommend-third-party-optimizations
|
|
3
3
|
description: >-
|
|
4
4
|
Scan a codebase to identify hand-rolled implementations that should be replaced
|
|
5
|
-
by third-party libraries,
|
|
6
|
-
|
|
5
|
+
by third-party libraries, identify missing capabilities, and detect code
|
|
6
|
+
organization issues (directory structure, naming, circular deps, barrel bloat).
|
|
7
|
+
Produce structured migration plans with Context7-verified recommendations.
|
|
7
8
|
Use when analyzing technical debt, auditing dependency health, reviewing
|
|
8
|
-
hand-rolled code, planning library migrations,
|
|
9
|
+
hand-rolled code, planning library migrations, assessing capability gaps,
|
|
10
|
+
or auditing project structure and module organization.
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
# Analyze and Recommend Third-Party Optimizations
|
|
@@ -13,15 +15,27 @@ description: >-
|
|
|
13
15
|
## Architecture
|
|
14
16
|
|
|
15
17
|
```
|
|
16
|
-
Scanner (deterministic)
|
|
17
|
-
Regex
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
Scanner (deterministic) → AI Agent (generative) → Pipeline (deterministic)
|
|
19
|
+
1. Regex: detect hand-rolled code 1. Recommend library replacements Score, plan, audit,
|
|
20
|
+
2. FS: detect code org issues 2. Identify capability gaps filter, serialize
|
|
21
|
+
(god-dirs, circular deps, 3. Recommend org patterns + tooling
|
|
22
|
+
naming, barrel bloat) using knowledge + Context7
|
|
20
23
|
```
|
|
21
24
|
|
|
22
25
|
**Design constraints**:
|
|
23
26
|
- No hardcoded library recommendations — evaluate project context dynamically
|
|
24
27
|
- Two analysis modes: **replacement** (hand-rolled code found) and **gap** (capability missing entirely)
|
|
28
|
+
- **Human-in-the-loop**: 4 gates at irreversible, preference-driven, or costly decision points
|
|
29
|
+
|
|
30
|
+
## Gate Protocol
|
|
31
|
+
|
|
32
|
+
Present structured choices at gates. NEVER skip or proceed without user response.
|
|
33
|
+
|
|
34
|
+
**Format** — table of findings/options + lettered choices + your recommendation with 1-sentence rationale. For high-impact gates, add a SWOT table. See `references/code-organization-workflow.md` for full Gate examples.
|
|
35
|
+
|
|
36
|
+
**Rules**:
|
|
37
|
+
- If user says "do it all automatically", ask "confirm skip ALL gates?" first
|
|
38
|
+
- After user decides, execute automatically and report results with rollback instructions
|
|
25
39
|
|
|
26
40
|
## Standard Operating Procedure
|
|
27
41
|
|
|
@@ -34,43 +48,83 @@ Parse and validate `InputSchema` JSON via Zod. Read `src/schemas/input.schema.ts
|
|
|
34
48
|
Run `scanCodebase(input)`. The scanner:
|
|
35
49
|
|
|
36
50
|
1. Detect workspace roots for monorepo support
|
|
37
|
-
2. Match code against
|
|
38
|
-
3.
|
|
39
|
-
4. Return `ScanResult` with
|
|
51
|
+
2. Match code against 30+ domain patterns (i18n, auth, state-management, code-organization, etc.)
|
|
52
|
+
3. Run structural analysis: design system layers (monorepo), code organization (all projects)
|
|
53
|
+
4. Return `ScanResult` with:
|
|
54
|
+
- `detections` — hand-rolled code patterns found
|
|
55
|
+
- `structuralFindings` — architectural + code organization issues
|
|
56
|
+
- `codeOrganizationStats` — project-wide metrics (file counts, naming conventions, circular dep count)
|
|
57
|
+
- `workspaces` — monorepo workspace info
|
|
58
|
+
|
|
59
|
+
Detections and findings contain no recommendations — only facts.
|
|
40
60
|
|
|
41
|
-
|
|
61
|
+
### GATE 1: Triage Detections
|
|
62
|
+
|
|
63
|
+
**Why**: Each accepted detection costs a Context7 verification call. False positives waste quota.
|
|
64
|
+
|
|
65
|
+
Present scan results as a triage table with columns: #, Domain, Pattern, File, Confidence, Action. Offer options: (A) accept all, (B) skip specific numbers, (C) high-confidence only. See `references/code-organization-workflow.md#gate-1-triage-example` for format.
|
|
66
|
+
|
|
67
|
+
Wait for user response. Proceed with accepted detections only.
|
|
42
68
|
|
|
43
69
|
### Step 2.5: Gap Analysis (AI Agent)
|
|
44
70
|
|
|
45
|
-
|
|
71
|
+
Analyze what the project is **missing entirely**:
|
|
72
|
+
|
|
73
|
+
1. **Installed dependencies** — low-level tools that should be upgraded to platform-level solutions
|
|
74
|
+
2. **Monorepo structure** — missing architectural layers (e.g., shared token package, shared config preset)
|
|
75
|
+
3. **Cross-cutting concerns** — absent capabilities: structured logging, error monitoring, rate limiting, transactional email, type-safe API layer
|
|
76
|
+
4. **Architecture patterns** — opportunities for multi-package solutions (e.g., design-tokens → tailwind-config → ui)
|
|
77
|
+
|
|
78
|
+
Analyze at three levels: **single library gap**, **ecosystem gap**, **architecture gap**.
|
|
79
|
+
|
|
80
|
+
Provide each gap as a `GapRecommendation`. Read `src/index.ts` for the interface.
|
|
81
|
+
|
|
82
|
+
**Design system gaps** — two paths:
|
|
83
|
+
- **No existing frontend**: Read `references/design-system-sources.md` for curated repos and sparse-checkout workflow.
|
|
84
|
+
- **Existing frontend**: Read `references/design-system-extraction.md` for extraction workflow, then `references/design-system-sources.md` for implementation.
|
|
85
|
+
|
|
86
|
+
### Step 2.7: Code Organization Analysis
|
|
87
|
+
|
|
88
|
+
#### Phase A — Collect facts (MUST use tools, DO NOT estimate)
|
|
89
|
+
|
|
90
|
+
You cannot infer file counts, naming conventions, or import cycles from knowledge. You MUST read the filesystem.
|
|
91
|
+
|
|
92
|
+
**If using the npm library** — `scanResult.structuralFindings` and `scanResult.codeOrganizationStats` already contain all findings. Skip to Phase B.
|
|
93
|
+
|
|
94
|
+
**If not** — run the shell commands in `references/code-organization-workflow.md#phase-a-shell-commands-for-collecting-facts`.
|
|
46
95
|
|
|
47
|
-
|
|
48
|
-
2. **Monorepo structure** — identify missing architectural layers (e.g., shared token package, shared config preset)
|
|
49
|
-
3. **Cross-cutting concerns** — identify absent capabilities: structured logging, error monitoring, rate limiting, event-driven workflows, transactional email, type-safe API layer
|
|
50
|
-
4. **Architecture patterns** — identify opportunities for multi-package solutions (e.g., design-tokens → tailwind-config → ui three-layer architecture for design systems)
|
|
96
|
+
Record each finding with: **directory/file path, count, type**. These are facts.
|
|
51
97
|
|
|
52
|
-
|
|
53
|
-
- **Single library gap**: missing one tool (e.g., no form validation library)
|
|
54
|
-
- **Ecosystem gap**: missing a coordinated set of tools (e.g., no observability stack)
|
|
55
|
-
- **Architecture gap**: missing an entire structural layer (e.g., no design system, no shared config)
|
|
98
|
+
#### Phase B — Recommend solutions (use your knowledge + Context7)
|
|
56
99
|
|
|
57
|
-
|
|
100
|
+
For each finding, apply the MUST do / MUST NOT do decision tree in `references/code-organization-workflow.md#phase-b-decision-tree`. Do NOT recommend tools without Context7 verification.
|
|
58
101
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
102
|
+
For worked examples showing the full Fact → Read → Recommend flow, see `references/code-organization-workflow.md#phase-b-worked-examples`.
|
|
103
|
+
|
|
104
|
+
**Skip rules** — skip a finding if:
|
|
105
|
+
- Directory is in `tests/`, `__tests__/`, `__mocks__/`, `fixtures/`, `generated/`, `.storybook/`
|
|
106
|
+
- File is auto-generated (has `// @generated` or `/* eslint-disable */` at top)
|
|
107
|
+
- Directory has <3 files (too few to judge)
|
|
108
|
+
|
|
109
|
+
### GATE 2: Code Organization Preferences
|
|
110
|
+
|
|
111
|
+
**Why**: Organization pattern and naming convention are team preferences, not technical correctness.
|
|
112
|
+
|
|
113
|
+
Present only if structural findings exist. For each preference, present a SWOT comparison with lettered options. See `references/code-organization-workflow.md#gate-2-swot-examples` for format.
|
|
114
|
+
|
|
115
|
+
Wait for user response on each preference. Proceed to Step 3 with confirmed choices.
|
|
62
116
|
|
|
63
117
|
### Step 3: Recommend Solutions (AI Agent)
|
|
64
118
|
|
|
65
|
-
For each
|
|
119
|
+
For each accepted detection, recommend a **solution**:
|
|
66
120
|
|
|
67
|
-
1. **Stack coherence** —
|
|
121
|
+
1. **Stack coherence** — consider how libraries fit the project's overall stack
|
|
68
122
|
2. **Ecosystem composition** — recommend companion libraries that work together
|
|
69
123
|
3. **Rationale** — explain WHY this choice fits this project's framework, runtime, and scale
|
|
70
124
|
4. **Anti-patterns** — what NOT to use and why
|
|
71
125
|
5. **Alternatives** — different solutions for different architectural contexts
|
|
72
|
-
6. **Migration snippet** —
|
|
73
|
-
7. **Context7 verification** — call `resolve-library-id` + `query-docs` to confirm
|
|
126
|
+
6. **Migration snippet** — read the detected code (file path + line range) and generate before/after examples
|
|
127
|
+
7. **Context7 verification** — call `resolve-library-id` + `query-docs` to confirm existence and get latest docs
|
|
74
128
|
|
|
75
129
|
Read `src/index.ts` for the `LibraryRecommendation` interface. Return `null` to skip a detection.
|
|
76
130
|
|
|
@@ -80,12 +134,20 @@ Read `src/index.ts` for the `LibraryRecommendation` interface. Return `null` to
|
|
|
80
134
|
- Library is already in project dependencies (suggest version update instead)
|
|
81
135
|
- Hand-rolled code is simpler than the library (3-line utility vs 50KB dep)
|
|
82
136
|
|
|
137
|
+
### GATE 3: Accept/Reject Recommendations
|
|
138
|
+
|
|
139
|
+
**Why**: Each recommendation has real migration cost. User may have business, timeline, or architectural reasons to defer or reject.
|
|
140
|
+
|
|
141
|
+
Present ALL recommendations (replacements + gaps + code org tooling) as a decision table with columns: #, Domain, Replace what, With what, Risk, Effort, Context7, Decision. Offer options: (A) accept all, (B) accept specific, (C) low-risk only, (D) defer all. See `references/code-organization-workflow.md#gate-3-recommendation-table-example` for format.
|
|
142
|
+
|
|
143
|
+
Wait for user response. Rejected items are excluded from migration plan, scoring, and PRs.
|
|
144
|
+
|
|
83
145
|
### Step 4–7: Score, Plan, Audit, Serialize
|
|
84
146
|
|
|
85
147
|
The pipeline handles these automatically:
|
|
86
|
-
- **Scoring**: confidence-based dimension scores (overridable
|
|
148
|
+
- **Scoring**: confidence-based dimension scores (overridable via `dimensionHints`)
|
|
87
149
|
- **Migration plan**: auto-grouped by risk (low/medium/high), sorted by file co-location
|
|
88
|
-
- **UX audit**: provide via `uxAudit` option
|
|
150
|
+
- **UX audit**: provide via `uxAudit` option. Evaluate 8 categories (accessibility, error/empty/loading states, form validation, performance feel, copy consistency, design system alignment)
|
|
89
151
|
- **Constraints**: license allowlist filtering, dependency conflict detection, JSON serialization
|
|
90
152
|
|
|
91
153
|
### Optional Steps
|
|
@@ -94,6 +156,22 @@ The pipeline handles these automatically:
|
|
|
94
156
|
- **Step 9**: Auto-update existing dependencies (`registryClient`)
|
|
95
157
|
- **Step 10**: PR auto-creation via GitHub/GitLab (`platformClient` + `gitOps`)
|
|
96
158
|
|
|
159
|
+
### GATE 4: Before Irreversible Actions
|
|
160
|
+
|
|
161
|
+
**Why**: Creating PRs pushes branches to remote and notifies team members. File migrations modify the codebase. Both are irreversible.
|
|
162
|
+
|
|
163
|
+
Present only if Step 10 or file migration is about to execute. Show PR table and file migration table with rollback commands. Offer options: (A) execute all, (B) PRs only, (C) migration only, (D) dry run, (E) abort. See `references/code-organization-workflow.md#gate-4-execution-confirmation-example` for format.
|
|
164
|
+
|
|
165
|
+
Wait for user response. After execution, report results with rollback instructions.
|
|
166
|
+
|
|
167
|
+
## MCP Integration
|
|
168
|
+
|
|
169
|
+
Prefer MCP tools when available; fall back to shell commands if not.
|
|
170
|
+
|
|
171
|
+
- **Context7 MCP** (required) — `resolve-library-id` + `query-docs` for library verification
|
|
172
|
+
- **GitHub MCP** (preferred for PRs) — structured PR create/update/query; fallback: `gh` CLI
|
|
173
|
+
- **Git MCP / GitKraken MCP** (preferred for scaffold) — structured repo browse/sparse-checkout; fallback: `git` CLI
|
|
174
|
+
|
|
97
175
|
## Output
|
|
98
176
|
|
|
99
177
|
Single `OutputSchema` JSON containing:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { StructuralFinding } from './structure-analyzer.js';
|
|
2
|
+
export interface CodeOrganizationAnalysis {
|
|
3
|
+
findings: StructuralFinding[];
|
|
4
|
+
stats: {
|
|
5
|
+
totalSourceFiles: number;
|
|
6
|
+
maxDirectoryDepth: number;
|
|
7
|
+
/** Naming convention distribution: kebab → count, camelCase → count, etc. */
|
|
8
|
+
namingConventions: Record<string, number>;
|
|
9
|
+
circularDependencyCount: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Analyze project code organization for structural issues.
|
|
14
|
+
*
|
|
15
|
+
* Deterministic filesystem analysis that Claude cannot replicate:
|
|
16
|
+
* - Counts files per directory (god directories)
|
|
17
|
+
* - Reads filenames to check naming conventions
|
|
18
|
+
* - Measures directory nesting depth
|
|
19
|
+
* - Counts re-exports in barrel files
|
|
20
|
+
* - Builds import graph to detect circular dependencies
|
|
21
|
+
*
|
|
22
|
+
* Recommendations (which pattern to adopt, which tools to use)
|
|
23
|
+
* are the AI agent's responsibility.
|
|
24
|
+
*/
|
|
25
|
+
export declare function analyzeCodeOrganization(repoPath: string): CodeOrganizationAnalysis;
|
|
26
|
+
//# sourceMappingURL=code-organization-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-organization-analyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/code-organization-analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAMjE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,KAAK,EAAE;QACL,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,6EAA6E;QAC7E,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1C,uBAAuB,EAAE,MAAM,CAAC;KACjC,CAAC;CACH;AAuND;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,wBAAwB,CAmMlF"}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Constants — thresholds for code organization heuristics
|
|
5
|
+
//
|
|
6
|
+
// These thresholds are informed by the directory structures of
|
|
7
|
+
// Vercel (Next.js), Stripe (API docs), Linear, Supabase, shadcn/ui, Dub,
|
|
8
|
+
// and other reference Silicon Valley products. They represent the point
|
|
9
|
+
// where hand-rolled organization becomes unmaintainable and tools
|
|
10
|
+
// (eslint-plugin-import, knip, Barrel-file analyzers) are justified.
|
|
11
|
+
//
|
|
12
|
+
// Claude cannot determine these values without filesystem access.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/** Max source files in a single directory before it becomes a "god directory" */
|
|
15
|
+
const GOD_DIRECTORY_THRESHOLD = 15;
|
|
16
|
+
/** Max directory nesting depth from project root (src/) */
|
|
17
|
+
const MAX_NESTING_DEPTH = 5;
|
|
18
|
+
/** Max re-export lines in a barrel file */
|
|
19
|
+
const BARREL_BLOAT_THRESHOLD = 10;
|
|
20
|
+
/** Max files in a catch-all directory (utils/, helpers/, shared/, common/, lib/) */
|
|
21
|
+
const CATCH_ALL_THRESHOLD = 10;
|
|
22
|
+
/** Source file extensions to consider */
|
|
23
|
+
const SOURCE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);
|
|
24
|
+
/** Directories to skip during analysis */
|
|
25
|
+
const SKIP_DIRS = new Set([
|
|
26
|
+
'node_modules', '.git', 'dist', 'build', '.next', '__pycache__',
|
|
27
|
+
'.venv', 'venv', 'target', 'vendor', '.turbo', 'coverage',
|
|
28
|
+
]);
|
|
29
|
+
/** Catch-all directory names that should be split by domain */
|
|
30
|
+
const CATCH_ALL_NAMES = new Set(['utils', 'helpers', 'common', 'shared', 'lib']);
|
|
31
|
+
function detectNamingConvention(filename) {
|
|
32
|
+
// Strip extension and any leading dot
|
|
33
|
+
const name = filename.replace(/\.[^.]+$/, '').replace(/^\./, '');
|
|
34
|
+
if (!name || name === 'index')
|
|
35
|
+
return 'unknown';
|
|
36
|
+
if (/^[A-Z][A-Z0-9_]+$/.test(name))
|
|
37
|
+
return 'SCREAMING_SNAKE';
|
|
38
|
+
if (/^[A-Z][a-zA-Z0-9]*$/.test(name))
|
|
39
|
+
return 'PascalCase';
|
|
40
|
+
if (/^[a-z][a-zA-Z0-9]*$/.test(name))
|
|
41
|
+
return 'camelCase';
|
|
42
|
+
if (/^[a-z][a-z0-9]*(-[a-z0-9]+)+$/.test(name))
|
|
43
|
+
return 'kebab-case';
|
|
44
|
+
if (/^[a-z][a-z0-9]*(_[a-z0-9]+)+$/.test(name))
|
|
45
|
+
return 'snake_case';
|
|
46
|
+
// Mixed patterns
|
|
47
|
+
if (name.includes('-'))
|
|
48
|
+
return 'kebab-case';
|
|
49
|
+
if (name.includes('_'))
|
|
50
|
+
return 'snake_case';
|
|
51
|
+
if (/^[A-Z]/.test(name))
|
|
52
|
+
return 'PascalCase';
|
|
53
|
+
return 'camelCase';
|
|
54
|
+
}
|
|
55
|
+
function* walkDirEntries(dir, repoPath) {
|
|
56
|
+
let entries;
|
|
57
|
+
try {
|
|
58
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const fullPath = path.join(dir, entry.name);
|
|
65
|
+
const relativePath = path.relative(repoPath, fullPath);
|
|
66
|
+
if (entry.isDirectory()) {
|
|
67
|
+
if (!SKIP_DIRS.has(entry.name)) {
|
|
68
|
+
yield { name: entry.name, fullPath, relativePath, isDirectory: true };
|
|
69
|
+
yield* walkDirEntries(fullPath, repoPath);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (entry.isFile()) {
|
|
73
|
+
yield { name: entry.name, fullPath, relativePath, isDirectory: false };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Import graph for circular dependency detection
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
/** Regex patterns to extract import/require paths from source files */
|
|
81
|
+
const IMPORT_PATTERNS = [
|
|
82
|
+
/(?:import\s+(?:[\s\S]*?\s+from\s+)?['"`])(\.{1,2}\/[^'"`\n]+)['"`]/g,
|
|
83
|
+
/(?:import\s*\(\s*['"`])(\.{1,2}\/[^'"`\n]+)['"`]/g,
|
|
84
|
+
/require\s*\(\s*['"`](\.{1,2}\/[^'"`\n]+)['"`]\s*\)/g,
|
|
85
|
+
/export\s+(?:\*|{[^}]*})\s+from\s+['"`](\.{1,2}\/[^'"`\n]+)['"`]/g,
|
|
86
|
+
];
|
|
87
|
+
function extractImports(filePath) {
|
|
88
|
+
let content;
|
|
89
|
+
try {
|
|
90
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
if (content.length > 500_000)
|
|
96
|
+
return [];
|
|
97
|
+
const imports = [];
|
|
98
|
+
for (const pattern of IMPORT_PATTERNS) {
|
|
99
|
+
// Reset regex state
|
|
100
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
101
|
+
let match;
|
|
102
|
+
while ((match = regex.exec(content)) !== null) {
|
|
103
|
+
if (match[1])
|
|
104
|
+
imports.push(match[1]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return imports;
|
|
108
|
+
}
|
|
109
|
+
function resolveImportPath(fromFile, importPath) {
|
|
110
|
+
const fromDir = path.dirname(fromFile);
|
|
111
|
+
let resolved = path.resolve(fromDir, importPath);
|
|
112
|
+
// Try extensions if no extension present
|
|
113
|
+
const ext = path.extname(resolved);
|
|
114
|
+
if (!ext || !SOURCE_EXTS.has(ext)) {
|
|
115
|
+
for (const tryExt of ['.ts', '.tsx', '.js', '.jsx']) {
|
|
116
|
+
if (fs.existsSync(resolved + tryExt))
|
|
117
|
+
return resolved + tryExt;
|
|
118
|
+
}
|
|
119
|
+
// Try index file
|
|
120
|
+
const indexPath = path.join(resolved, 'index');
|
|
121
|
+
for (const tryExt of ['.ts', '.tsx', '.js', '.jsx']) {
|
|
122
|
+
if (fs.existsSync(indexPath + tryExt))
|
|
123
|
+
return indexPath + tryExt;
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return fs.existsSync(resolved) ? resolved : null;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Detect circular dependencies using DFS cycle detection.
|
|
131
|
+
* Returns arrays of file paths forming cycles.
|
|
132
|
+
*/
|
|
133
|
+
function detectCircularDependencies(sourceFiles, repoPath) {
|
|
134
|
+
// Build adjacency list
|
|
135
|
+
const graph = new Map();
|
|
136
|
+
for (const file of sourceFiles) {
|
|
137
|
+
const imports = extractImports(file);
|
|
138
|
+
const resolved = [];
|
|
139
|
+
for (const imp of imports) {
|
|
140
|
+
const target = resolveImportPath(file, imp);
|
|
141
|
+
if (target && sourceFiles.includes(target)) {
|
|
142
|
+
resolved.push(target);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
graph.set(file, resolved);
|
|
146
|
+
}
|
|
147
|
+
// DFS cycle detection
|
|
148
|
+
const cycles = [];
|
|
149
|
+
const visited = new Set();
|
|
150
|
+
const onStack = new Set();
|
|
151
|
+
const pathStack = [];
|
|
152
|
+
const seen = new Set(); // deduplicate cycles
|
|
153
|
+
function dfs(node) {
|
|
154
|
+
visited.add(node);
|
|
155
|
+
onStack.add(node);
|
|
156
|
+
pathStack.push(node);
|
|
157
|
+
const neighbors = graph.get(node) ?? [];
|
|
158
|
+
for (const neighbor of neighbors) {
|
|
159
|
+
if (!visited.has(neighbor)) {
|
|
160
|
+
dfs(neighbor);
|
|
161
|
+
}
|
|
162
|
+
else if (onStack.has(neighbor)) {
|
|
163
|
+
// Found a cycle — extract it
|
|
164
|
+
const cycleStart = pathStack.indexOf(neighbor);
|
|
165
|
+
if (cycleStart >= 0) {
|
|
166
|
+
const cycle = pathStack.slice(cycleStart).map((f) => path.relative(repoPath, f));
|
|
167
|
+
const key = [...cycle].sort().join('|');
|
|
168
|
+
if (!seen.has(key)) {
|
|
169
|
+
seen.add(key);
|
|
170
|
+
cycles.push(cycle);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
pathStack.pop();
|
|
176
|
+
onStack.delete(node);
|
|
177
|
+
}
|
|
178
|
+
for (const file of sourceFiles) {
|
|
179
|
+
if (!visited.has(file)) {
|
|
180
|
+
dfs(file);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return cycles;
|
|
184
|
+
}
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// analyzeCodeOrganization — main entry point
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
/**
|
|
189
|
+
* Analyze project code organization for structural issues.
|
|
190
|
+
*
|
|
191
|
+
* Deterministic filesystem analysis that Claude cannot replicate:
|
|
192
|
+
* - Counts files per directory (god directories)
|
|
193
|
+
* - Reads filenames to check naming conventions
|
|
194
|
+
* - Measures directory nesting depth
|
|
195
|
+
* - Counts re-exports in barrel files
|
|
196
|
+
* - Builds import graph to detect circular dependencies
|
|
197
|
+
*
|
|
198
|
+
* Recommendations (which pattern to adopt, which tools to use)
|
|
199
|
+
* are the AI agent's responsibility.
|
|
200
|
+
*/
|
|
201
|
+
export function analyzeCodeOrganization(repoPath) {
|
|
202
|
+
const findings = [];
|
|
203
|
+
const allSourceFiles = [];
|
|
204
|
+
const namingConventions = {};
|
|
205
|
+
// --- Collect directory info ---
|
|
206
|
+
const dirFileCount = new Map();
|
|
207
|
+
let maxDepth = 0;
|
|
208
|
+
for (const entry of walkDirEntries(repoPath, repoPath)) {
|
|
209
|
+
if (entry.isDirectory) {
|
|
210
|
+
// Track nesting depth
|
|
211
|
+
const depth = entry.relativePath.split(path.sep).length;
|
|
212
|
+
if (depth > maxDepth)
|
|
213
|
+
maxDepth = depth;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
const ext = path.extname(entry.name);
|
|
217
|
+
if (!SOURCE_EXTS.has(ext))
|
|
218
|
+
continue;
|
|
219
|
+
allSourceFiles.push(entry.fullPath);
|
|
220
|
+
// Track naming convention
|
|
221
|
+
const convention = detectNamingConvention(entry.name);
|
|
222
|
+
if (convention !== 'unknown') {
|
|
223
|
+
namingConventions[convention] = (namingConventions[convention] ?? 0) + 1;
|
|
224
|
+
}
|
|
225
|
+
// Track files per directory
|
|
226
|
+
const dir = path.dirname(entry.relativePath);
|
|
227
|
+
const existing = dirFileCount.get(dir);
|
|
228
|
+
if (existing) {
|
|
229
|
+
existing.count++;
|
|
230
|
+
existing.files.push(entry.name);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
dirFileCount.set(dir, { count: 1, files: [entry.name] });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// --- Check 1: God directories ---
|
|
238
|
+
for (const [dir, info] of dirFileCount) {
|
|
239
|
+
if (info.count > GOD_DIRECTORY_THRESHOLD) {
|
|
240
|
+
findings.push({
|
|
241
|
+
type: 'god-directory',
|
|
242
|
+
domain: 'code-organization',
|
|
243
|
+
description: `Directory "${dir}" contains ${info.count} source files (threshold: ${GOD_DIRECTORY_THRESHOLD}). Split by feature or domain.`,
|
|
244
|
+
paths: [dir],
|
|
245
|
+
severity: info.count > GOD_DIRECTORY_THRESHOLD * 2 ? 'critical' : 'warning',
|
|
246
|
+
metadata: { fileCount: info.count, threshold: GOD_DIRECTORY_THRESHOLD },
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// --- Check 2: Mixed naming conventions per directory ---
|
|
251
|
+
for (const [dir, info] of dirFileCount) {
|
|
252
|
+
if (info.files.length < 3)
|
|
253
|
+
continue; // need enough files to judge
|
|
254
|
+
const conventions = new Map();
|
|
255
|
+
for (const file of info.files) {
|
|
256
|
+
const conv = detectNamingConvention(file);
|
|
257
|
+
if (conv === 'unknown')
|
|
258
|
+
continue;
|
|
259
|
+
const list = conventions.get(conv) ?? [];
|
|
260
|
+
list.push(file);
|
|
261
|
+
conventions.set(conv, list);
|
|
262
|
+
}
|
|
263
|
+
// Filter out conventions with only 1 file (noise)
|
|
264
|
+
const significant = [...conventions.entries()].filter(([, files]) => files.length >= 2);
|
|
265
|
+
if (significant.length >= 2) {
|
|
266
|
+
const summary = significant
|
|
267
|
+
.map(([conv, files]) => `${conv} (${files.length} files)`)
|
|
268
|
+
.join(', ');
|
|
269
|
+
findings.push({
|
|
270
|
+
type: 'mixed-naming-convention',
|
|
271
|
+
domain: 'code-organization',
|
|
272
|
+
description: `Directory "${dir}" uses mixed naming conventions: ${summary}. Choose one convention and enforce it.`,
|
|
273
|
+
paths: [dir],
|
|
274
|
+
severity: 'warning',
|
|
275
|
+
metadata: {
|
|
276
|
+
conventions: Object.fromEntries(significant.map(([k, v]) => [k, v.length])),
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// --- Check 3: Deep nesting ---
|
|
282
|
+
for (const entry of walkDirEntries(repoPath, repoPath)) {
|
|
283
|
+
if (!entry.isDirectory)
|
|
284
|
+
continue;
|
|
285
|
+
const parts = entry.relativePath.split(path.sep);
|
|
286
|
+
// Only measure from src/ or packages/ onwards
|
|
287
|
+
const srcIdx = parts.indexOf('src');
|
|
288
|
+
const depth = srcIdx >= 0 ? parts.length - srcIdx : parts.length;
|
|
289
|
+
if (depth > MAX_NESTING_DEPTH) {
|
|
290
|
+
findings.push({
|
|
291
|
+
type: 'deep-nesting',
|
|
292
|
+
domain: 'code-organization',
|
|
293
|
+
description: `Directory "${entry.relativePath}" is ${depth} levels deep (max: ${MAX_NESTING_DEPTH}). Consider flattening or using path aliases.`,
|
|
294
|
+
paths: [entry.relativePath],
|
|
295
|
+
severity: 'warning',
|
|
296
|
+
metadata: { depth, threshold: MAX_NESTING_DEPTH },
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// --- Check 4: Barrel bloat (index files with many re-exports) ---
|
|
301
|
+
for (const file of allSourceFiles) {
|
|
302
|
+
const basename = path.basename(file, path.extname(file));
|
|
303
|
+
if (basename !== 'index')
|
|
304
|
+
continue;
|
|
305
|
+
let content;
|
|
306
|
+
try {
|
|
307
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const reexportCount = (content.match(/export\s+(?:\*|{[^}]*})\s+from\s+['"`]/g) ?? []).length;
|
|
313
|
+
if (reexportCount > BARREL_BLOAT_THRESHOLD) {
|
|
314
|
+
const relativePath = path.relative(repoPath, file);
|
|
315
|
+
findings.push({
|
|
316
|
+
type: 'barrel-bloat',
|
|
317
|
+
domain: 'code-organization',
|
|
318
|
+
description: `Barrel file "${relativePath}" has ${reexportCount} re-exports (threshold: ${BARREL_BLOAT_THRESHOLD}). Consider direct imports or code-splitting.`,
|
|
319
|
+
paths: [relativePath],
|
|
320
|
+
severity: reexportCount > BARREL_BLOAT_THRESHOLD * 2 ? 'critical' : 'warning',
|
|
321
|
+
metadata: { reexportCount, threshold: BARREL_BLOAT_THRESHOLD },
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// --- Check 5: Catch-all directories ---
|
|
326
|
+
for (const [dir, info] of dirFileCount) {
|
|
327
|
+
const dirName = path.basename(dir);
|
|
328
|
+
if (!CATCH_ALL_NAMES.has(dirName))
|
|
329
|
+
continue;
|
|
330
|
+
if (info.count > CATCH_ALL_THRESHOLD) {
|
|
331
|
+
findings.push({
|
|
332
|
+
type: 'catch-all-directory',
|
|
333
|
+
domain: 'code-organization',
|
|
334
|
+
description: `Catch-all directory "${dir}" contains ${info.count} files (threshold: ${CATCH_ALL_THRESHOLD}). Split into domain-specific modules (e.g., utils/date, utils/string, utils/validation).`,
|
|
335
|
+
paths: [dir],
|
|
336
|
+
severity: info.count > CATCH_ALL_THRESHOLD * 2 ? 'critical' : 'warning',
|
|
337
|
+
metadata: { fileCount: info.count, threshold: CATCH_ALL_THRESHOLD },
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// --- Check 6: Mixed export styles (default + multiple named in same file) ---
|
|
342
|
+
for (const file of allSourceFiles) {
|
|
343
|
+
const basename = path.basename(file, path.extname(file));
|
|
344
|
+
if (basename === 'index')
|
|
345
|
+
continue; // barrel files are expected to have many exports
|
|
346
|
+
let content;
|
|
347
|
+
try {
|
|
348
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const hasDefaultExport = /export\s+default\s/.test(content);
|
|
354
|
+
const namedExportCount = (content.match(/export\s+(?:function|class|const|let|var|enum|type|interface)\s+\w/g) ?? []).length;
|
|
355
|
+
if (hasDefaultExport && namedExportCount >= 3) {
|
|
356
|
+
const relativePath = path.relative(repoPath, file);
|
|
357
|
+
findings.push({
|
|
358
|
+
type: 'god-directory', // reuse closest type — file is a "god module"
|
|
359
|
+
domain: 'code-organization',
|
|
360
|
+
description: `File "${relativePath}" mixes a default export with ${namedExportCount} named exports. One module, one responsibility — split into separate files.`,
|
|
361
|
+
paths: [relativePath],
|
|
362
|
+
severity: 'info',
|
|
363
|
+
metadata: { hasDefaultExport, namedExportCount, issue: 'mixed-export-style' },
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// --- Check 7: Circular dependencies ---
|
|
368
|
+
const cycles = detectCircularDependencies(allSourceFiles, repoPath);
|
|
369
|
+
for (const cycle of cycles) {
|
|
370
|
+
findings.push({
|
|
371
|
+
type: 'circular-dependency',
|
|
372
|
+
domain: 'code-organization',
|
|
373
|
+
description: `Circular dependency detected: ${cycle.join(' → ')} → ${cycle[0]}`,
|
|
374
|
+
paths: cycle,
|
|
375
|
+
severity: cycle.length > 3 ? 'critical' : 'warning',
|
|
376
|
+
metadata: { cycleLength: cycle.length },
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
findings,
|
|
381
|
+
stats: {
|
|
382
|
+
totalSourceFiles: allSourceFiles.length,
|
|
383
|
+
maxDirectoryDepth: maxDepth,
|
|
384
|
+
namingConventions,
|
|
385
|
+
circularDependencyCount: cycles.length,
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=code-organization-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-organization-analyzer.js","sourceRoot":"","sources":["../../src/analyzer/code-organization-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pattern-catalog.d.ts","sourceRoot":"","sources":["../../src/analyzer/pattern-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnE;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,MAAM,EAAE,gBAAgB,CAAC;IACzB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"pattern-catalog.d.ts","sourceRoot":"","sources":["../../src/analyzer/pattern-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnE;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,MAAM,EAAE,gBAAgB,CAAC;IACzB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,EAAE,CAmsBvD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAExE"}
|