@nebutra/next-unicorn-skill 1.0.6 → 1.0.7
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 +29 -0
- package/README.md +96 -9
- package/README.zh-CN.md +474 -0
- package/SKILL.md +119 -11
- 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,10 +15,11 @@ 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**:
|
|
@@ -34,11 +37,16 @@ Parse and validate `InputSchema` JSON via Zod. Read `src/schemas/input.schema.ts
|
|
|
34
37
|
Run `scanCodebase(input)`. The scanner:
|
|
35
38
|
|
|
36
39
|
1. Detect workspace roots for monorepo support
|
|
37
|
-
2. Match code against
|
|
38
|
-
3.
|
|
39
|
-
4.
|
|
40
|
+
2. Match code against 30+ domain patterns (i18n, auth, state-management, code-organization, etc.)
|
|
41
|
+
3. Run structural analysis: design system layers (monorepo), code organization (all projects)
|
|
42
|
+
4. Record each detection with: file path, line range, pattern category, confidence score, domain
|
|
43
|
+
5. Return `ScanResult` with:
|
|
44
|
+
- `detections` — hand-rolled code patterns found
|
|
45
|
+
- `structuralFindings` — architectural + code organization issues
|
|
46
|
+
- `codeOrganizationStats` — project-wide metrics (file counts, naming conventions, circular dep count)
|
|
47
|
+
- `workspaces` — monorepo workspace info
|
|
40
48
|
|
|
41
|
-
Detections contain no
|
|
49
|
+
Detections and findings contain no recommendations — only facts. The AI agent interprets them.
|
|
42
50
|
|
|
43
51
|
### Step 2.5: Gap Analysis (AI Agent)
|
|
44
52
|
|
|
@@ -60,6 +68,98 @@ Provide each gap as a `GapRecommendation`. Read `src/index.ts` for the interface
|
|
|
60
68
|
- **No existing frontend**: Scaffold from reference repos. Read `references/design-system-sources.md` for curated sources and sparse-checkout workflow.
|
|
61
69
|
- **Existing frontend without formal design system**: First extract the spec (audit → tokens → classify → document) via `references/design-system-extraction.md`, then implement the architecture via `references/design-system-sources.md`.
|
|
62
70
|
|
|
71
|
+
### Step 2.7: Code Organization Analysis
|
|
72
|
+
|
|
73
|
+
#### Phase A — Deterministic: Collect facts (MUST use tools, DO NOT estimate)
|
|
74
|
+
|
|
75
|
+
You cannot infer file counts, naming conventions, or import cycles from knowledge. You MUST read the filesystem.
|
|
76
|
+
|
|
77
|
+
**If using the npm library** — `scanResult.structuralFindings` and `scanResult.codeOrganizationStats` already contain all findings. Skip to Phase B.
|
|
78
|
+
|
|
79
|
+
**If not using the npm library** — run these shell commands to collect facts:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# 1. God directories: find directories with >15 source files
|
|
83
|
+
find src -type d -exec sh -c 'count=$(find "$1" -maxdepth 1 -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) | wc -l); [ "$count" -gt 15 ] && echo "$1: $count files"' _ {} \;
|
|
84
|
+
|
|
85
|
+
# 2. Mixed naming: list filenames per directory for manual inspection
|
|
86
|
+
ls -1 src/components/ # check if kebab-case and camelCase are mixed
|
|
87
|
+
|
|
88
|
+
# 3. Deep nesting: find directories >5 levels deep from src/
|
|
89
|
+
find src -mindepth 6 -type d
|
|
90
|
+
|
|
91
|
+
# 4. Barrel bloat: count re-exports in index files
|
|
92
|
+
grep -c "export.*from" src/**/index.ts
|
|
93
|
+
|
|
94
|
+
# 5. Catch-all directories: count files in utils/helpers/shared
|
|
95
|
+
find src/utils src/helpers src/shared src/common src/lib -maxdepth 1 -type f 2>/dev/null | wc -l
|
|
96
|
+
|
|
97
|
+
# 6. Circular dependencies: use npx if madge is not installed
|
|
98
|
+
npx madge --circular --extensions ts,tsx src/
|
|
99
|
+
|
|
100
|
+
# 7. Deep relative imports: find ../../../ patterns
|
|
101
|
+
grep -rn "from ['\"]\.\.\/\.\.\/\.\.\/" src/ --include="*.ts" --include="*.tsx"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Record each finding with: **directory/file path, count, type**. These are facts.
|
|
105
|
+
|
|
106
|
+
#### Phase B — Generative: Recommend solutions (use your knowledge + Context7)
|
|
107
|
+
|
|
108
|
+
For each finding from Phase A, apply the decision tree below. **Do NOT recommend tools without Context7 verification.**
|
|
109
|
+
|
|
110
|
+
| Finding type | You MUST do | You MUST NOT do |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `god-directory` | Read the files in that dir, group by domain, recommend split. Reference: Next.js App Router colocation, Linear feature-packages. | Guess file count. Say "probably too many files." |
|
|
113
|
+
| `mixed-naming-convention` | Check project framework → pick ONE convention. Next.js pages=kebab, React components=PascalCase, utils=camelCase. | Recommend "both are fine." Must pick one. |
|
|
114
|
+
| `deep-nesting` | Recommend `@/` path aliases. Read `tsconfig.json` to check if paths already exist. Generate the config change. | Say "consider flattening" without generating the actual config. |
|
|
115
|
+
| `barrel-bloat` | Recommend direct imports or namespace imports. Context7 verify `knip` for dead export detection. | Ignore it. Barrel bloat causes tree-shaking failures. |
|
|
116
|
+
| `catch-all-directory` | Read the actual files, group by domain (date, string, validation, etc.), recommend specific directory structure. | Say "split by concern" without reading what's actually in the files. |
|
|
117
|
+
| `circular-dependency` | Read the files in the cycle, understand WHY they import each other, recommend dependency inversion or extract shared module. Context7 verify `eslint-plugin-import`. | Just say "remove circular deps." Must explain the refactoring. |
|
|
118
|
+
| `org-deep-relative-import` | Same as `deep-nesting` — recommend path aliases. | Skip it. |
|
|
119
|
+
| `org-barrel-reexport-wildcard` | Recommend named re-exports `export { X } from` instead of `export *`. Explain namespace pollution risk. | Ignore it. |
|
|
120
|
+
| `org-catch-all-utils-import` | Same as `catch-all-directory` — recommend domain-specific modules. | Skip it. |
|
|
121
|
+
|
|
122
|
+
#### Phase B examples
|
|
123
|
+
|
|
124
|
+
**Example 1 — god-directory finding**:
|
|
125
|
+
```
|
|
126
|
+
Fact: src/components/ has 23 source files
|
|
127
|
+
```
|
|
128
|
+
Read those 23 files. You find: Button, Card, Modal, Table, Form, Input, Select, Checkbox...
|
|
129
|
+
|
|
130
|
+
Recommend:
|
|
131
|
+
```
|
|
132
|
+
src/components/
|
|
133
|
+
├── ui/ ← primitives (Button, Input, Select, Checkbox)
|
|
134
|
+
├── data/ ← data display (Table, Card, DataGrid)
|
|
135
|
+
├── overlay/ ← overlays (Modal, Dialog, Drawer, Tooltip)
|
|
136
|
+
└── form/ ← form elements (Form, FormField, FormError)
|
|
137
|
+
```
|
|
138
|
+
Reference: shadcn/ui organizes by interaction type. Radix UI uses similar grouping.
|
|
139
|
+
|
|
140
|
+
**Example 2 — circular-dependency finding**:
|
|
141
|
+
```
|
|
142
|
+
Fact: src/auth/session.ts → src/db/user.ts → src/auth/session.ts
|
|
143
|
+
```
|
|
144
|
+
Read both files. You find: `session.ts` imports `getUserById`, `user.ts` imports `getSession` for auth checks.
|
|
145
|
+
|
|
146
|
+
Recommend: Extract `src/auth/types.ts` with shared interfaces. Both files import from types instead of each other. Context7 verify `eslint-plugin-import/no-cycle`.
|
|
147
|
+
|
|
148
|
+
**Example 3 — mixed-naming finding**:
|
|
149
|
+
```
|
|
150
|
+
Fact: src/utils/ has kebab-case (5 files) + camelCase (3 files)
|
|
151
|
+
```
|
|
152
|
+
Check package.json → framework is Next.js → convention is kebab-case for files.
|
|
153
|
+
|
|
154
|
+
Recommend: Rename the 3 camelCase files. Context7 verify `eslint-plugin-unicorn/filename-case` for CI enforcement.
|
|
155
|
+
|
|
156
|
+
#### Skip rules
|
|
157
|
+
|
|
158
|
+
Skip a code organization finding if:
|
|
159
|
+
- Directory is in `tests/`, `__tests__/`, `__mocks__/`, `fixtures/`, `generated/`, `.storybook/`
|
|
160
|
+
- File is auto-generated (has `// @generated` or `/* eslint-disable */` at top)
|
|
161
|
+
- Directory has <3 files (too few to judge naming convention)
|
|
162
|
+
|
|
63
163
|
### Step 3: Recommend Solutions (AI Agent)
|
|
64
164
|
|
|
65
165
|
For each scanner detection, recommend a **solution**. Consider:
|
|
@@ -94,6 +194,14 @@ The pipeline handles these automatically:
|
|
|
94
194
|
- **Step 9**: Auto-update existing dependencies (`registryClient`)
|
|
95
195
|
- **Step 10**: PR auto-creation via GitHub/GitLab (`platformClient` + `gitOps`)
|
|
96
196
|
|
|
197
|
+
## MCP Integration
|
|
198
|
+
|
|
199
|
+
Prefer MCP tools when available; fall back to shell commands if not.
|
|
200
|
+
|
|
201
|
+
- **Context7 MCP** (required) — `resolve-library-id` + `query-docs` for library verification
|
|
202
|
+
- **GitHub MCP** (preferred for PRs) — structured PR create/update/query; fallback: `gh` CLI
|
|
203
|
+
- **Git MCP / GitKraken MCP** (preferred for scaffold) — structured repo browse/sparse-checkout; fallback: `git` CLI
|
|
204
|
+
|
|
97
205
|
## Output
|
|
98
206
|
|
|
99
207
|
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"}
|
|
@@ -544,6 +544,39 @@ export function getPatternCatalog() {
|
|
|
544
544
|
// -----------------------------------------------------------------------
|
|
545
545
|
// H. Delivery / Quality / DevEx
|
|
546
546
|
// -----------------------------------------------------------------------
|
|
547
|
+
// code-organization
|
|
548
|
+
{
|
|
549
|
+
id: 'org-deep-relative-import',
|
|
550
|
+
domain: 'code-organization',
|
|
551
|
+
description: 'Deep relative import path (3+ parent traversals) indicating poor module structure',
|
|
552
|
+
filePatterns: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
|
553
|
+
codePatterns: [
|
|
554
|
+
/(?:import|from)\s+['"`]\.\.\/\.\.\/\.\.\//,
|
|
555
|
+
/require\s*\(\s*['"`]\.\.\/\.\.\/\.\.\//,
|
|
556
|
+
],
|
|
557
|
+
confidenceBase: 0.75,
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
id: 'org-barrel-reexport-wildcard',
|
|
561
|
+
domain: 'code-organization',
|
|
562
|
+
description: 'Wildcard barrel re-export (export * from) — risks namespace pollution and circular deps',
|
|
563
|
+
filePatterns: ['**/index.ts', '**/index.tsx', '**/index.js', '**/index.jsx'],
|
|
564
|
+
codePatterns: [
|
|
565
|
+
/export\s+\*\s+from\s+['"`]/,
|
|
566
|
+
],
|
|
567
|
+
confidenceBase: 0.65,
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
id: 'org-catch-all-utils-import',
|
|
571
|
+
domain: 'code-organization',
|
|
572
|
+
description: 'Import from catch-all utils/helpers/common directory — should be split by domain',
|
|
573
|
+
filePatterns: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
|
574
|
+
codePatterns: [
|
|
575
|
+
/(?:import|from)\s+['"`][.\/]*(?:utils|helpers|common|shared|lib)\/(?!index)/,
|
|
576
|
+
/require\s*\(\s*['"`][.\/]*(?:utils|helpers|common|shared|lib)\//,
|
|
577
|
+
],
|
|
578
|
+
confidenceBase: 0.5,
|
|
579
|
+
},
|
|
547
580
|
// testing-strategy
|
|
548
581
|
{
|
|
549
582
|
id: 'test-manual-assertions',
|