@vyuhlabs/dxkit 2.5.0 → 2.5.1
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 +77 -0
- package/dist/analyzers/tools/graphify.d.ts.map +1 -1
- package/dist/analyzers/tools/graphify.js +9 -5
- package/dist/analyzers/tools/graphify.js.map +1 -1
- package/dist/analyzers/tools/tool-registry.d.ts +19 -1
- package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
- package/dist/analyzers/tools/tool-registry.js +25 -0
- package/dist/analyzers/tools/tool-registry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +38 -1
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +18 -11
- package/dist/doctor.js.map +1 -1
- package/dist/generator.d.ts +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +81 -135
- package/dist/generator.js.map +1 -1
- package/dist/hooks-cli.d.ts +20 -0
- package/dist/hooks-cli.d.ts.map +1 -0
- package/dist/hooks-cli.js +145 -0
- package/dist/hooks-cli.js.map +1 -0
- package/dist/languages/csharp.d.ts.map +1 -1
- package/dist/languages/csharp.js +4 -0
- package/dist/languages/csharp.js.map +1 -1
- package/dist/languages/go.d.ts.map +1 -1
- package/dist/languages/go.js +4 -0
- package/dist/languages/go.js.map +1 -1
- package/dist/languages/index.d.ts +18 -0
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +32 -0
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/java.d.ts.map +1 -1
- package/dist/languages/java.js +4 -0
- package/dist/languages/java.js.map +1 -1
- package/dist/languages/kotlin.d.ts.map +1 -1
- package/dist/languages/kotlin.js +9 -0
- package/dist/languages/kotlin.js.map +1 -1
- package/dist/languages/python.d.ts.map +1 -1
- package/dist/languages/python.js +4 -0
- package/dist/languages/python.js.map +1 -1
- package/dist/languages/ruby.d.ts.map +1 -1
- package/dist/languages/ruby.js +4 -0
- package/dist/languages/ruby.js.map +1 -1
- package/dist/languages/rust.d.ts.map +1 -1
- package/dist/languages/rust.js +4 -0
- package/dist/languages/rust.js.map +1 -1
- package/dist/languages/types.d.ts +27 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript.d.ts.map +1 -1
- package/dist/languages/typescript.js +5 -0
- package/dist/languages/typescript.js.map +1 -1
- package/dist/ship-installers.d.ts +6 -0
- package/dist/ship-installers.d.ts.map +1 -1
- package/dist/ship-installers.js +120 -5
- package/dist/ship-installers.js.map +1 -1
- package/dist/tools-cli.d.ts.map +1 -1
- package/dist/tools-cli.js +45 -9
- package/dist/tools-cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/.claude/skills/dxkit-action/SKILL.md +150 -0
- package/templates/.claude/skills/dxkit-config/SKILL.md +124 -0
- package/templates/.claude/skills/dxkit-hooks/SKILL.md +109 -0
- package/templates/.claude/skills/dxkit-init/SKILL.md +93 -0
- package/templates/.claude/skills/dxkit-learn/SKILL.md +84 -0
- package/templates/.claude/skills/dxkit-reports/SKILL.md +111 -0
- package/templates/.devcontainer/devcontainer.json +7 -33
- package/templates/.devcontainer/post-create.sh +18 -4
- package/templates/AGENTS.md.template +137 -0
- package/templates/CLAUDE.md.template +16 -111
- package/dist/codebase-scanner.d.ts +0 -36
- package/dist/codebase-scanner.d.ts.map +0 -1
- package/dist/codebase-scanner.js +0 -687
- package/dist/codebase-scanner.js.map +0 -1
- package/templates/.claude/agents/doc-writer.md +0 -107
- package/templates/.claude/agents/knowledge-bot.md +0 -64
- package/templates/.claude/agents/onboarding.md +0 -62
- package/templates/.claude/agents/quality-reviewer.md +0 -85
- package/templates/.claude/agents-available/code-reviewer.md +0 -29
- package/templates/.claude/agents-available/codebase-explorer.md +0 -100
- package/templates/.claude/agents-available/dashboard-builder.md +0 -433
- package/templates/.claude/agents-available/debugger.md +0 -29
- package/templates/.claude/agents-available/dependency-mapper.md +0 -80
- package/templates/.claude/agents-available/dev-report.md +0 -108
- package/templates/.claude/agents-available/doc-writer.md +0 -107
- package/templates/.claude/agents-available/feature-builder.md +0 -163
- package/templates/.claude/agents-available/feature-planner.md +0 -185
- package/templates/.claude/agents-available/health-auditor.md +0 -95
- package/templates/.claude/agents-available/hooks-configurator.md +0 -211
- package/templates/.claude/agents-available/knowledge-bot.md +0 -62
- package/templates/.claude/agents-available/plan-executor.md +0 -133
- package/templates/.claude/agents-available/strategic-planner.md +0 -141
- package/templates/.claude/agents-available/test-gap-finder.md +0 -67
- package/templates/.claude/agents-available/test-writer.md +0 -34
- package/templates/.claude/agents-available/vulnerability-scanner.md +0 -173
- package/templates/.claude/commands/ask.md +0 -7
- package/templates/.claude/commands/build-feature.md +0 -26
- package/templates/.claude/commands/build.md.template +0 -30
- package/templates/.claude/commands/check.md.template +0 -43
- package/templates/.claude/commands/dashboard.md +0 -28
- package/templates/.claude/commands/deps.md +0 -15
- package/templates/.claude/commands/dev-report.md +0 -50
- package/templates/.claude/commands/docs.md +0 -21
- package/templates/.claude/commands/doctor.md +0 -29
- package/templates/.claude/commands/enable-agent.md +0 -12
- package/templates/.claude/commands/execute-plan.md +0 -25
- package/templates/.claude/commands/explore-codebase.md +0 -12
- package/templates/.claude/commands/export-pdf.md +0 -30
- package/templates/.claude/commands/feature.md +0 -25
- package/templates/.claude/commands/fix-issue.md +0 -12
- package/templates/.claude/commands/fix.md.template +0 -32
- package/templates/.claude/commands/health.md +0 -58
- package/templates/.claude/commands/help.md +0 -36
- package/templates/.claude/commands/learn.md +0 -48
- package/templates/.claude/commands/onboarding.md +0 -21
- package/templates/.claude/commands/plan.md +0 -20
- package/templates/.claude/commands/quality.md.template +0 -65
- package/templates/.claude/commands/session-end.md +0 -40
- package/templates/.claude/commands/session-start.md +0 -30
- package/templates/.claude/commands/setup-hooks.md +0 -18
- package/templates/.claude/commands/stealth-mode.md +0 -17
- package/templates/.claude/commands/test-gaps.md +0 -49
- package/templates/.claude/commands/test.md.template +0 -40
- package/templates/.claude/commands/vulnerabilities.md +0 -49
- package/templates/.claude/skills/build/SKILL.md.template +0 -90
- package/templates/.claude/skills/deploy/SKILL.md.template +0 -111
- package/templates/.claude/skills/deploy/references/gotchas.md +0 -5
- package/templates/.claude/skills/doctor/SKILL.md +0 -31
- package/templates/.claude/skills/gcloud/SKILL.md +0 -66
- package/templates/.claude/skills/gcloud/references/gotchas.md +0 -5
- package/templates/.claude/skills/learned/SKILL.md +0 -55
- package/templates/.claude/skills/learned/references/conventions.md +0 -11
- package/templates/.claude/skills/learned/references/deny-recommendations.md +0 -18
- package/templates/.claude/skills/learned/references/gotchas.md +0 -11
- package/templates/.claude/skills/pulumi/SKILL.md +0 -73
- package/templates/.claude/skills/quality/SKILL.md.template +0 -89
- package/templates/.claude/skills/quality/references/gotchas.md +0 -5
- package/templates/.claude/skills/review/SKILL.md.template +0 -74
- package/templates/.claude/skills/scaffold/SKILL.md.template +0 -113
- package/templates/.claude/skills/secrets/SKILL.md +0 -51
- package/templates/.claude/skills/session/SKILL.md +0 -32
- package/templates/.claude/skills/test/SKILL.md.template +0 -116
- package/templates/.claude/skills/test/references/gotchas.md +0 -5
package/dist/codebase-scanner.js
DELETED
|
@@ -1,687 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.scanCodebase = scanCodebase;
|
|
37
|
-
exports.renderCodebaseSkill = renderCodebaseSkill;
|
|
38
|
-
exports.renderArchitectureRef = renderArchitectureRef;
|
|
39
|
-
const fs = __importStar(require("fs"));
|
|
40
|
-
const path = __importStar(require("path"));
|
|
41
|
-
const IGNORE_DIRS = new Set([
|
|
42
|
-
'node_modules',
|
|
43
|
-
'vendor',
|
|
44
|
-
'.venv',
|
|
45
|
-
'venv',
|
|
46
|
-
'target',
|
|
47
|
-
'bin',
|
|
48
|
-
'obj',
|
|
49
|
-
'.git',
|
|
50
|
-
'.next',
|
|
51
|
-
'__pycache__',
|
|
52
|
-
'dist',
|
|
53
|
-
'build',
|
|
54
|
-
'.tox',
|
|
55
|
-
'.eggs',
|
|
56
|
-
'htmlcov',
|
|
57
|
-
'coverage',
|
|
58
|
-
'.mypy_cache',
|
|
59
|
-
'.pytest_cache',
|
|
60
|
-
'.ruff_cache',
|
|
61
|
-
]);
|
|
62
|
-
const BINARY_EXTENSIONS = new Set([
|
|
63
|
-
'.png',
|
|
64
|
-
'.jpg',
|
|
65
|
-
'.jpeg',
|
|
66
|
-
'.gif',
|
|
67
|
-
'.ico',
|
|
68
|
-
'.webp',
|
|
69
|
-
'.svg',
|
|
70
|
-
'.woff',
|
|
71
|
-
'.woff2',
|
|
72
|
-
'.ttf',
|
|
73
|
-
'.eot',
|
|
74
|
-
'.pdf',
|
|
75
|
-
'.zip',
|
|
76
|
-
'.tar',
|
|
77
|
-
'.gz',
|
|
78
|
-
'.br',
|
|
79
|
-
'.exe',
|
|
80
|
-
'.dll',
|
|
81
|
-
'.so',
|
|
82
|
-
'.dylib',
|
|
83
|
-
'.lock',
|
|
84
|
-
'.sum',
|
|
85
|
-
]);
|
|
86
|
-
const MAX_FILE_SIZE = 100 * 1024; // 100KB
|
|
87
|
-
// --- File Walking ---
|
|
88
|
-
function walkFiles(cwd, callback, maxDepth = 5) {
|
|
89
|
-
function walk(dir, depth) {
|
|
90
|
-
if (depth > maxDepth)
|
|
91
|
-
return;
|
|
92
|
-
let entries;
|
|
93
|
-
try {
|
|
94
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
for (const entry of entries) {
|
|
100
|
-
if (entry.name.startsWith('.') && entry.name !== '.env.example')
|
|
101
|
-
continue;
|
|
102
|
-
const full = path.join(dir, entry.name);
|
|
103
|
-
const rel = path.relative(cwd, full);
|
|
104
|
-
if (entry.isDirectory()) {
|
|
105
|
-
if (!IGNORE_DIRS.has(entry.name))
|
|
106
|
-
walk(full, depth + 1);
|
|
107
|
-
}
|
|
108
|
-
else if (entry.isFile()) {
|
|
109
|
-
const ext = path.extname(entry.name).toLowerCase();
|
|
110
|
-
if (BINARY_EXTENSIONS.has(ext))
|
|
111
|
-
continue;
|
|
112
|
-
try {
|
|
113
|
-
const stat = fs.statSync(full);
|
|
114
|
-
if (stat.size > MAX_FILE_SIZE)
|
|
115
|
-
continue;
|
|
116
|
-
const content = fs.readFileSync(full, 'utf-8');
|
|
117
|
-
callback(rel, content);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
/* skip unreadable */
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
walk(cwd, 0);
|
|
126
|
-
}
|
|
127
|
-
// --- Entry Points ---
|
|
128
|
-
const ENTRY_POINT_FILES = {
|
|
129
|
-
'main.go': 'Go entry point',
|
|
130
|
-
'app.py': 'Python application',
|
|
131
|
-
'main.py': 'Python entry point',
|
|
132
|
-
'manage.py': 'Django management',
|
|
133
|
-
'index.ts': 'Node.js entry (TypeScript)',
|
|
134
|
-
'index.js': 'Node.js entry',
|
|
135
|
-
'server.ts': 'Node.js server (TypeScript)',
|
|
136
|
-
'server.js': 'Node.js server',
|
|
137
|
-
'Program.cs': 'C# entry point',
|
|
138
|
-
'Startup.cs': 'C# ASP.NET startup',
|
|
139
|
-
'main.rs': 'Rust entry point',
|
|
140
|
-
};
|
|
141
|
-
function findEntryPoints(cwd) {
|
|
142
|
-
const results = [];
|
|
143
|
-
const seen = new Set();
|
|
144
|
-
walkFiles(cwd, (rel, content) => {
|
|
145
|
-
const basename = path.basename(rel);
|
|
146
|
-
// Known entry point filenames
|
|
147
|
-
if (ENTRY_POINT_FILES[basename] && !seen.has(rel)) {
|
|
148
|
-
results.push({ file: rel, type: ENTRY_POINT_FILES[basename] });
|
|
149
|
-
seen.add(rel);
|
|
150
|
-
}
|
|
151
|
-
// Python __main__ pattern
|
|
152
|
-
if (basename.endsWith('.py') &&
|
|
153
|
-
content.includes('__name__') &&
|
|
154
|
-
content.includes('__main__') &&
|
|
155
|
-
!seen.has(rel)) {
|
|
156
|
-
results.push({ file: rel, type: 'Python __main__ entry' });
|
|
157
|
-
seen.add(rel);
|
|
158
|
-
}
|
|
159
|
-
// Go func main pattern (for non-main.go files)
|
|
160
|
-
if (basename.endsWith('.go') &&
|
|
161
|
-
/^func\s+main\s*\(/m.test(content) &&
|
|
162
|
-
basename !== 'main.go' &&
|
|
163
|
-
!seen.has(rel)) {
|
|
164
|
-
results.push({ file: rel, type: 'Go main function' });
|
|
165
|
-
seen.add(rel);
|
|
166
|
-
}
|
|
167
|
-
}, 3); // shallow depth for entry points
|
|
168
|
-
// Check package.json main field
|
|
169
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
170
|
-
if (fs.existsSync(pkgPath)) {
|
|
171
|
-
try {
|
|
172
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
173
|
-
if (pkg.main && !seen.has(pkg.main)) {
|
|
174
|
-
results.push({ file: pkg.main, type: 'package.json main' });
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
catch {
|
|
178
|
-
/* ignore */
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return results;
|
|
182
|
-
}
|
|
183
|
-
// --- API Routes ---
|
|
184
|
-
const ROUTE_PATTERNS = [
|
|
185
|
-
// Python FastAPI/Flask decorators
|
|
186
|
-
{
|
|
187
|
-
ext: /\.py$/,
|
|
188
|
-
regex: /@(?:app|router)\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)/gi,
|
|
189
|
-
methodGroup: 1,
|
|
190
|
-
pathGroup: 2,
|
|
191
|
-
},
|
|
192
|
-
// Python Flask @app.route
|
|
193
|
-
{
|
|
194
|
-
ext: /\.py$/,
|
|
195
|
-
regex: /@(?:app|blueprint)\.route\s*\(\s*['"]([^'"]+)['"]/gi,
|
|
196
|
-
methodGroup: -1,
|
|
197
|
-
pathGroup: 1,
|
|
198
|
-
},
|
|
199
|
-
// Express/Node — only match when path starts with /
|
|
200
|
-
{
|
|
201
|
-
ext: /\.[tj]sx?$/,
|
|
202
|
-
regex: /(?:app|router|this\.app)\.(get|post|put|delete|patch|all)\s*\(\s*['"](\/.+?)['"]/gi,
|
|
203
|
-
methodGroup: 1,
|
|
204
|
-
pathGroup: 2,
|
|
205
|
-
},
|
|
206
|
-
// LoopBack decorators — @get('/path'), @post('/path'), etc.
|
|
207
|
-
{
|
|
208
|
-
ext: /\.[tj]sx?$/,
|
|
209
|
-
regex: /@(get|post|put|del|patch)\s*\(\s*['"]([^'"]+)/gi,
|
|
210
|
-
methodGroup: 1,
|
|
211
|
-
pathGroup: 2,
|
|
212
|
-
},
|
|
213
|
-
// Go standard library and popular routers
|
|
214
|
-
{ ext: /\.go$/, regex: /(?:HandleFunc|Handle)\s*\(\s*"([^"]+)"/g, methodGroup: -1, pathGroup: 1 },
|
|
215
|
-
// Go Gin/Echo/Fiber
|
|
216
|
-
{
|
|
217
|
-
ext: /\.go$/,
|
|
218
|
-
regex: /\.(GET|POST|PUT|DELETE|PATCH)\s*\(\s*"([^"]+)"/gi,
|
|
219
|
-
methodGroup: 1,
|
|
220
|
-
pathGroup: 2,
|
|
221
|
-
},
|
|
222
|
-
// C# attributes
|
|
223
|
-
{
|
|
224
|
-
ext: /\.cs$/,
|
|
225
|
-
regex: /\[Http(Get|Post|Put|Delete|Patch)(?:\s*\("([^"]*)"\))?\]/g,
|
|
226
|
-
methodGroup: 1,
|
|
227
|
-
pathGroup: 2,
|
|
228
|
-
},
|
|
229
|
-
// C# minimal API
|
|
230
|
-
{
|
|
231
|
-
ext: /\.cs$/,
|
|
232
|
-
regex: /app\.Map(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/gi,
|
|
233
|
-
methodGroup: 1,
|
|
234
|
-
pathGroup: 2,
|
|
235
|
-
},
|
|
236
|
-
];
|
|
237
|
-
function isSourceFile(rel) {
|
|
238
|
-
// Exclude test files, docs, and generated files from API route detection
|
|
239
|
-
const lower = rel.toLowerCase();
|
|
240
|
-
if (lower.endsWith('_test.go') || lower.includes('test_') || lower.includes('/tests/'))
|
|
241
|
-
return false;
|
|
242
|
-
if (lower.endsWith('.test.ts') ||
|
|
243
|
-
lower.endsWith('.test.js') ||
|
|
244
|
-
lower.endsWith('.spec.ts') ||
|
|
245
|
-
lower.endsWith('.spec.js'))
|
|
246
|
-
return false;
|
|
247
|
-
if (lower.includes('/docs/') || lower.includes('/documentation/'))
|
|
248
|
-
return false;
|
|
249
|
-
if (lower.endsWith('.md') ||
|
|
250
|
-
lower.endsWith('.mdx') ||
|
|
251
|
-
lower.endsWith('.txt') ||
|
|
252
|
-
lower.endsWith('.rst'))
|
|
253
|
-
return false;
|
|
254
|
-
if (lower.includes('example') || lower.includes('sample') || lower.includes('fixture'))
|
|
255
|
-
return false;
|
|
256
|
-
return true;
|
|
257
|
-
}
|
|
258
|
-
function findApiRoutes(cwd) {
|
|
259
|
-
const results = [];
|
|
260
|
-
walkFiles(cwd, (rel, content) => {
|
|
261
|
-
if (!isSourceFile(rel))
|
|
262
|
-
return;
|
|
263
|
-
for (const pattern of ROUTE_PATTERNS) {
|
|
264
|
-
if (!pattern.ext.test(rel))
|
|
265
|
-
continue;
|
|
266
|
-
const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
267
|
-
let match;
|
|
268
|
-
while ((match = regex.exec(content)) !== null) {
|
|
269
|
-
const method = pattern.methodGroup === -1 ? 'ANY' : (match[pattern.methodGroup] || 'GET').toUpperCase();
|
|
270
|
-
const routePath = match[pattern.pathGroup] || '/';
|
|
271
|
-
results.push({ method, path: routePath, file: rel });
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
// Next.js API routes (file-based)
|
|
275
|
-
if (rel.includes('app/api/') || rel.includes('pages/api/')) {
|
|
276
|
-
const routePath = '/' +
|
|
277
|
-
rel
|
|
278
|
-
.replace(/^.*(?:app|pages)\//, '')
|
|
279
|
-
.replace(/\/route\.[tj]sx?$/, '')
|
|
280
|
-
.replace(/\/index\.[tj]sx?$/, '')
|
|
281
|
-
.replace(/\.[tj]sx?$/, '');
|
|
282
|
-
results.push({ method: 'ANY', path: routePath, file: rel });
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
return results.slice(0, 50); // cap at 50 to avoid bloat
|
|
286
|
-
}
|
|
287
|
-
// --- Directory Classification ---
|
|
288
|
-
const DIR_PURPOSES = {
|
|
289
|
-
src: 'Source code',
|
|
290
|
-
lib: 'Library code',
|
|
291
|
-
pkg: 'Packages',
|
|
292
|
-
internal: 'Internal packages (Go)',
|
|
293
|
-
cmd: 'CLI commands (Go)',
|
|
294
|
-
tests: 'Test files',
|
|
295
|
-
test: 'Test files',
|
|
296
|
-
__tests__: 'Test files (Jest)',
|
|
297
|
-
spec: 'Test specifications',
|
|
298
|
-
docs: 'Documentation',
|
|
299
|
-
documentation: 'Documentation',
|
|
300
|
-
scripts: 'Utility scripts',
|
|
301
|
-
tools: 'Development tools',
|
|
302
|
-
config: 'Configuration',
|
|
303
|
-
configs: 'Configuration',
|
|
304
|
-
migrations: 'Database migrations',
|
|
305
|
-
db: 'Database',
|
|
306
|
-
api: 'API layer',
|
|
307
|
-
routes: 'Route handlers',
|
|
308
|
-
controllers: 'Controllers',
|
|
309
|
-
handlers: 'Request handlers',
|
|
310
|
-
models: 'Data models',
|
|
311
|
-
services: 'Service layer',
|
|
312
|
-
middleware: 'Middleware',
|
|
313
|
-
components: 'UI components',
|
|
314
|
-
pages: 'Page components',
|
|
315
|
-
app: 'Application (Next.js App Router)',
|
|
316
|
-
public: 'Static assets',
|
|
317
|
-
static: 'Static files',
|
|
318
|
-
assets: 'Assets',
|
|
319
|
-
frontend: 'Frontend application',
|
|
320
|
-
backend: 'Backend application',
|
|
321
|
-
infra: 'Infrastructure code',
|
|
322
|
-
deploy: 'Deployment configuration',
|
|
323
|
-
'.github': 'GitHub workflows and config',
|
|
324
|
-
};
|
|
325
|
-
function classifyDirectories(cwd) {
|
|
326
|
-
const results = [];
|
|
327
|
-
try {
|
|
328
|
-
for (const entry of fs.readdirSync(cwd, { withFileTypes: true })) {
|
|
329
|
-
if (!entry.isDirectory())
|
|
330
|
-
continue;
|
|
331
|
-
if (entry.name.startsWith('.') && entry.name !== '.github')
|
|
332
|
-
continue;
|
|
333
|
-
if (IGNORE_DIRS.has(entry.name))
|
|
334
|
-
continue;
|
|
335
|
-
const purpose = DIR_PURPOSES[entry.name];
|
|
336
|
-
if (purpose) {
|
|
337
|
-
results.push({ path: entry.name + '/', purpose });
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
// Check one level deep for common patterns (services/python/, src/api/, etc.)
|
|
341
|
-
for (const dir of ['src', 'services', 'packages', 'apps']) {
|
|
342
|
-
const subdir = path.join(cwd, dir);
|
|
343
|
-
if (!fs.existsSync(subdir))
|
|
344
|
-
continue;
|
|
345
|
-
try {
|
|
346
|
-
for (const sub of fs.readdirSync(subdir, { withFileTypes: true })) {
|
|
347
|
-
if (!sub.isDirectory() || IGNORE_DIRS.has(sub.name))
|
|
348
|
-
continue;
|
|
349
|
-
const purpose = DIR_PURPOSES[sub.name];
|
|
350
|
-
if (purpose) {
|
|
351
|
-
results.push({ path: `${dir}/${sub.name}/`, purpose });
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
catch {
|
|
356
|
-
/* skip */
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
catch {
|
|
361
|
-
/* skip */
|
|
362
|
-
}
|
|
363
|
-
return results;
|
|
364
|
-
}
|
|
365
|
-
// --- Test Patterns ---
|
|
366
|
-
function detectTestPatterns(cwd) {
|
|
367
|
-
const results = [];
|
|
368
|
-
// pytest
|
|
369
|
-
if (fs.existsSync(path.join(cwd, 'pytest.ini')) || fs.existsSync(path.join(cwd, 'conftest.py'))) {
|
|
370
|
-
results.push({ framework: 'pytest', location: 'tests/', pattern: 'test_*.py' });
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
try {
|
|
374
|
-
const pyproject = fs.readFileSync(path.join(cwd, 'pyproject.toml'), 'utf-8');
|
|
375
|
-
if (pyproject.includes('[tool.pytest')) {
|
|
376
|
-
results.push({ framework: 'pytest', location: 'tests/', pattern: 'test_*.py' });
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
catch {
|
|
380
|
-
/* no pyproject */
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
// go test
|
|
384
|
-
let hasGoTests = false;
|
|
385
|
-
walkFiles(cwd, (rel) => {
|
|
386
|
-
if (rel.endsWith('_test.go'))
|
|
387
|
-
hasGoTests = true;
|
|
388
|
-
}, 2);
|
|
389
|
-
if (hasGoTests) {
|
|
390
|
-
results.push({ framework: 'go test', location: 'alongside source', pattern: '*_test.go' });
|
|
391
|
-
}
|
|
392
|
-
// Jest
|
|
393
|
-
if (fs.existsSync(path.join(cwd, 'jest.config.js')) ||
|
|
394
|
-
fs.existsSync(path.join(cwd, 'jest.config.ts'))) {
|
|
395
|
-
results.push({
|
|
396
|
-
framework: 'jest',
|
|
397
|
-
location: '__tests__/ or *.test.ts',
|
|
398
|
-
pattern: '*.test.{ts,tsx,js,jsx}',
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
else {
|
|
402
|
-
try {
|
|
403
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8'));
|
|
404
|
-
if (pkg.jest || pkg.devDependencies?.jest) {
|
|
405
|
-
results.push({
|
|
406
|
-
framework: 'jest',
|
|
407
|
-
location: '__tests__/ or *.test.ts',
|
|
408
|
-
pattern: '*.test.{ts,tsx,js,jsx}',
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
catch {
|
|
413
|
-
/* no package.json */
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
// xUnit / NUnit (C#)
|
|
417
|
-
walkFiles(cwd, (rel, content) => {
|
|
418
|
-
if (rel.endsWith('.csproj') &&
|
|
419
|
-
(content.includes('xunit') || content.includes('nunit') || content.includes('MSTest'))) {
|
|
420
|
-
const framework = content.includes('xunit')
|
|
421
|
-
? 'xUnit'
|
|
422
|
-
: content.includes('nunit')
|
|
423
|
-
? 'NUnit'
|
|
424
|
-
: 'MSTest';
|
|
425
|
-
results.push({
|
|
426
|
-
framework,
|
|
427
|
-
location: '*.Tests projects',
|
|
428
|
-
pattern: '*.cs with [Fact]/[Test]',
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
}, 2);
|
|
432
|
-
// cargo test
|
|
433
|
-
if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) {
|
|
434
|
-
results.push({
|
|
435
|
-
framework: 'cargo test',
|
|
436
|
-
location: 'src/ (#[cfg(test)]) + tests/',
|
|
437
|
-
pattern: '#[test] functions',
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
return results;
|
|
441
|
-
}
|
|
442
|
-
// --- Config Files ---
|
|
443
|
-
const CONFIG_FILES = {
|
|
444
|
-
'package.json': 'Node.js package manifest',
|
|
445
|
-
'pyproject.toml': 'Python project config',
|
|
446
|
-
'go.mod': 'Go module definition',
|
|
447
|
-
'Cargo.toml': 'Rust package manifest',
|
|
448
|
-
Makefile: 'Build automation',
|
|
449
|
-
'docker-compose.yml': 'Docker services',
|
|
450
|
-
'docker-compose.yaml': 'Docker services',
|
|
451
|
-
Dockerfile: 'Container build',
|
|
452
|
-
'.env.example': 'Environment template',
|
|
453
|
-
'tsconfig.json': 'TypeScript config',
|
|
454
|
-
'.eslintrc.js': 'ESLint config',
|
|
455
|
-
'.eslintrc.json': 'ESLint config',
|
|
456
|
-
'ruff.toml': 'Python linter config',
|
|
457
|
-
'.golangci.yml': 'Go linter config',
|
|
458
|
-
'pytest.ini': 'Python test config',
|
|
459
|
-
'.pre-commit-config.yaml': 'Pre-commit hooks',
|
|
460
|
-
'.editorconfig': 'Editor config',
|
|
461
|
-
'Pulumi.yaml': 'Pulumi IaC config',
|
|
462
|
-
'appsettings.json': 'C# application config',
|
|
463
|
-
'global.json': '.NET SDK config',
|
|
464
|
-
};
|
|
465
|
-
function findConfigFiles(cwd) {
|
|
466
|
-
const results = [];
|
|
467
|
-
for (const [file, purpose] of Object.entries(CONFIG_FILES)) {
|
|
468
|
-
if (fs.existsSync(path.join(cwd, file))) {
|
|
469
|
-
results.push({ file, purpose });
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
return results;
|
|
473
|
-
}
|
|
474
|
-
// --- Convention Detection ---
|
|
475
|
-
function detectConventions(cwd) {
|
|
476
|
-
const conventions = [];
|
|
477
|
-
const fileNames = [];
|
|
478
|
-
walkFiles(cwd, (rel) => {
|
|
479
|
-
fileNames.push(path.basename(rel, path.extname(rel)));
|
|
480
|
-
}, 2);
|
|
481
|
-
// Naming convention
|
|
482
|
-
const snakeCount = fileNames.filter((n) => n.includes('_') && !n.includes('-')).length;
|
|
483
|
-
const kebabCount = fileNames.filter((n) => n.includes('-') && !n.includes('_')).length;
|
|
484
|
-
const camelCount = fileNames.filter((n) => /^[a-z][a-zA-Z]+$/.test(n) && !n.includes('_') && !n.includes('-')).length;
|
|
485
|
-
const pascalCount = fileNames.filter((n) => /^[A-Z][a-zA-Z]+$/.test(n)).length;
|
|
486
|
-
const max = Math.max(snakeCount, kebabCount, camelCount, pascalCount);
|
|
487
|
-
if (max > 5) {
|
|
488
|
-
if (snakeCount === max)
|
|
489
|
-
conventions.push({
|
|
490
|
-
pattern: 'snake_case',
|
|
491
|
-
description: 'File names use snake_case convention',
|
|
492
|
-
});
|
|
493
|
-
else if (kebabCount === max)
|
|
494
|
-
conventions.push({
|
|
495
|
-
pattern: 'kebab-case',
|
|
496
|
-
description: 'File names use kebab-case convention',
|
|
497
|
-
});
|
|
498
|
-
else if (pascalCount === max)
|
|
499
|
-
conventions.push({
|
|
500
|
-
pattern: 'PascalCase',
|
|
501
|
-
description: 'File names use PascalCase convention',
|
|
502
|
-
});
|
|
503
|
-
else if (camelCount === max)
|
|
504
|
-
conventions.push({
|
|
505
|
-
pattern: 'camelCase',
|
|
506
|
-
description: 'File names use camelCase convention',
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
// Test location
|
|
510
|
-
if (fs.existsSync(path.join(cwd, 'tests')) || fs.existsSync(path.join(cwd, 'test'))) {
|
|
511
|
-
conventions.push({
|
|
512
|
-
pattern: 'Separate test directory',
|
|
513
|
-
description: 'Tests in dedicated tests/ directory',
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
return conventions;
|
|
517
|
-
}
|
|
518
|
-
// --- Main Scanner ---
|
|
519
|
-
const TEST_FILE_PATTERNS = /(_test\.go$|\.test\.[tj]sx?$|\.spec\.[tj]sx?$|test_.*\.py$|Tests?\.cs$|_test\.rs$)/;
|
|
520
|
-
const SOURCE_FILE_EXTENSIONS = /\.(ts|tsx|js|jsx|py|go|rs|cs|java)$/;
|
|
521
|
-
function scanCodebase(cwd) {
|
|
522
|
-
let fileCount = 0;
|
|
523
|
-
let testFileCount = 0;
|
|
524
|
-
let sourceFileCount = 0;
|
|
525
|
-
const languageBreakdown = {};
|
|
526
|
-
const EXT_TO_LANG = {
|
|
527
|
-
'.ts': 'TypeScript',
|
|
528
|
-
'.tsx': 'TypeScript',
|
|
529
|
-
'.js': 'JavaScript',
|
|
530
|
-
'.jsx': 'JavaScript',
|
|
531
|
-
'.py': 'Python',
|
|
532
|
-
'.go': 'Go',
|
|
533
|
-
'.rs': 'Rust',
|
|
534
|
-
'.cs': 'C#',
|
|
535
|
-
'.java': 'Java',
|
|
536
|
-
};
|
|
537
|
-
walkFiles(cwd, (rel) => {
|
|
538
|
-
fileCount++;
|
|
539
|
-
if (TEST_FILE_PATTERNS.test(rel) ||
|
|
540
|
-
rel.includes('__tests__/') ||
|
|
541
|
-
rel.includes('/tests/') ||
|
|
542
|
-
rel.includes('/test/')) {
|
|
543
|
-
testFileCount++;
|
|
544
|
-
}
|
|
545
|
-
else if (SOURCE_FILE_EXTENSIONS.test(rel)) {
|
|
546
|
-
sourceFileCount++;
|
|
547
|
-
}
|
|
548
|
-
const ext = path.extname(rel).toLowerCase();
|
|
549
|
-
const lang = EXT_TO_LANG[ext];
|
|
550
|
-
if (lang) {
|
|
551
|
-
languageBreakdown[lang] = (languageBreakdown[lang] || 0) + 1;
|
|
552
|
-
}
|
|
553
|
-
}, 4);
|
|
554
|
-
return {
|
|
555
|
-
entryPoints: findEntryPoints(cwd),
|
|
556
|
-
directories: classifyDirectories(cwd),
|
|
557
|
-
testPatterns: detectTestPatterns(cwd),
|
|
558
|
-
apiEndpoints: findApiRoutes(cwd),
|
|
559
|
-
configFiles: findConfigFiles(cwd),
|
|
560
|
-
conventions: detectConventions(cwd),
|
|
561
|
-
fileCount,
|
|
562
|
-
testFileCount,
|
|
563
|
-
sourceFileCount,
|
|
564
|
-
languageBreakdown,
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
// --- Render Functions ---
|
|
568
|
-
function renderCodebaseSkill(analysis) {
|
|
569
|
-
const lines = [
|
|
570
|
-
'---',
|
|
571
|
-
'name: codebase',
|
|
572
|
-
'description: Architecture overview and navigation guide for this project. Check before starting any task to understand structure, entry points, and conventions.',
|
|
573
|
-
'---',
|
|
574
|
-
'',
|
|
575
|
-
'# Codebase Overview',
|
|
576
|
-
'',
|
|
577
|
-
`Scanned ${analysis.fileCount} files.`,
|
|
578
|
-
'',
|
|
579
|
-
];
|
|
580
|
-
// Language breakdown
|
|
581
|
-
const langs = Object.entries(analysis.languageBreakdown).sort((a, b) => b[1] - a[1]);
|
|
582
|
-
if (langs.length > 0) {
|
|
583
|
-
lines.push('## Languages', '');
|
|
584
|
-
for (const [lang, count] of langs) {
|
|
585
|
-
lines.push(`- **${lang}**: ${count} files`);
|
|
586
|
-
}
|
|
587
|
-
lines.push('');
|
|
588
|
-
}
|
|
589
|
-
if (analysis.entryPoints.length) {
|
|
590
|
-
lines.push('## Entry Points', '');
|
|
591
|
-
for (const ep of analysis.entryPoints) {
|
|
592
|
-
lines.push(`- \`${ep.file}\` — ${ep.type}`);
|
|
593
|
-
}
|
|
594
|
-
lines.push('');
|
|
595
|
-
}
|
|
596
|
-
if (analysis.directories.length) {
|
|
597
|
-
lines.push('## Key Directories', '');
|
|
598
|
-
for (const dir of analysis.directories) {
|
|
599
|
-
lines.push(`- \`${dir.path}\` — ${dir.purpose}`);
|
|
600
|
-
}
|
|
601
|
-
lines.push('');
|
|
602
|
-
}
|
|
603
|
-
if (analysis.apiEndpoints.length) {
|
|
604
|
-
lines.push('## API Surface', '');
|
|
605
|
-
for (const ep of analysis.apiEndpoints.slice(0, 30)) {
|
|
606
|
-
lines.push(`- ${ep.method} ${ep.path} (\`${ep.file}\`)`);
|
|
607
|
-
}
|
|
608
|
-
if (analysis.apiEndpoints.length > 30) {
|
|
609
|
-
lines.push(`- ... and ${analysis.apiEndpoints.length - 30} more (see references/architecture.md)`);
|
|
610
|
-
}
|
|
611
|
-
lines.push('');
|
|
612
|
-
}
|
|
613
|
-
// Test overview
|
|
614
|
-
lines.push('## Testing', '');
|
|
615
|
-
lines.push(`- **${analysis.testFileCount}** test files found across **${analysis.sourceFileCount}** source files`);
|
|
616
|
-
if (analysis.testFileCount === 0) {
|
|
617
|
-
lines.push('- **No tests found.** This project needs test infrastructure.');
|
|
618
|
-
}
|
|
619
|
-
else if (analysis.sourceFileCount > 50 &&
|
|
620
|
-
analysis.testFileCount < analysis.sourceFileCount * 0.1) {
|
|
621
|
-
lines.push('- **Minimal test presence.** Most code paths are likely untested.');
|
|
622
|
-
}
|
|
623
|
-
if (analysis.testPatterns.length) {
|
|
624
|
-
for (const tp of analysis.testPatterns) {
|
|
625
|
-
lines.push(`- **${tp.framework}** — ${tp.location} (${tp.pattern})`);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
lines.push('');
|
|
629
|
-
if (analysis.configFiles.length) {
|
|
630
|
-
lines.push('## Configuration', '');
|
|
631
|
-
for (const cf of analysis.configFiles) {
|
|
632
|
-
lines.push(`- \`${cf.file}\` — ${cf.purpose}`);
|
|
633
|
-
}
|
|
634
|
-
lines.push('');
|
|
635
|
-
}
|
|
636
|
-
if (analysis.conventions.length) {
|
|
637
|
-
lines.push('## Detected Conventions', '');
|
|
638
|
-
for (const c of analysis.conventions) {
|
|
639
|
-
lines.push(`- **${c.pattern}** — ${c.description}`);
|
|
640
|
-
}
|
|
641
|
-
lines.push('');
|
|
642
|
-
}
|
|
643
|
-
lines.push('---', '', '*Generated by [VyuhLabs DXKit](https://www.npmjs.com/package/@vyuhlabs/dxkit) codebase scanner*', '');
|
|
644
|
-
return lines.join('\n');
|
|
645
|
-
}
|
|
646
|
-
function renderArchitectureRef(analysis) {
|
|
647
|
-
const lines = [
|
|
648
|
-
'# Architecture Reference',
|
|
649
|
-
'',
|
|
650
|
-
'<!-- Auto-generated by VyuhLabs DXKit codebase scanner. -->',
|
|
651
|
-
'<!-- This file is evolving — your edits will be preserved across updates. -->',
|
|
652
|
-
'<!-- Use `npx @vyuhlabs/dxkit update --rescan` to regenerate. -->',
|
|
653
|
-
'',
|
|
654
|
-
];
|
|
655
|
-
if (analysis.entryPoints.length) {
|
|
656
|
-
lines.push('## Entry Points', '');
|
|
657
|
-
for (const ep of analysis.entryPoints) {
|
|
658
|
-
lines.push(`### \`${ep.file}\``, `- Type: ${ep.type}`, '');
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
if (analysis.directories.length) {
|
|
662
|
-
lines.push('## Directory Map', '');
|
|
663
|
-
lines.push('```');
|
|
664
|
-
for (const dir of analysis.directories) {
|
|
665
|
-
lines.push(`${dir.path.padEnd(30)} # ${dir.purpose}`);
|
|
666
|
-
}
|
|
667
|
-
lines.push('```', '');
|
|
668
|
-
}
|
|
669
|
-
if (analysis.apiEndpoints.length) {
|
|
670
|
-
lines.push('## API Endpoints', '');
|
|
671
|
-
lines.push('| Method | Path | File |', '|--------|------|------|');
|
|
672
|
-
for (const ep of analysis.apiEndpoints) {
|
|
673
|
-
lines.push(`| ${ep.method} | ${ep.path} | \`${ep.file}\` |`);
|
|
674
|
-
}
|
|
675
|
-
lines.push('');
|
|
676
|
-
}
|
|
677
|
-
if (analysis.configFiles.length) {
|
|
678
|
-
lines.push('## Config Inventory', '');
|
|
679
|
-
for (const cf of analysis.configFiles) {
|
|
680
|
-
lines.push(`- \`${cf.file}\` — ${cf.purpose}`);
|
|
681
|
-
}
|
|
682
|
-
lines.push('');
|
|
683
|
-
}
|
|
684
|
-
lines.push('---', '', '*Generated by [VyuhLabs DXKit](https://www.npmjs.com/package/@vyuhlabs/dxkit) codebase scanner*', '');
|
|
685
|
-
return lines.join('\n');
|
|
686
|
-
}
|
|
687
|
-
//# sourceMappingURL=codebase-scanner.js.map
|