@claude-flow/cli 3.5.56 → 3.5.58
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/dist/src/commands/analyze.d.ts.map +1 -1
- package/dist/src/commands/analyze.js +250 -25
- package/dist/src/commands/analyze.js.map +1 -1
- package/dist/src/commands/benchmark.d.ts.map +1 -1
- package/dist/src/commands/benchmark.js +2 -1
- package/dist/src/commands/benchmark.js.map +1 -1
- package/dist/src/commands/embeddings.js +3 -3
- package/dist/src/commands/embeddings.js.map +1 -1
- package/dist/src/commands/providers.d.ts.map +1 -1
- package/dist/src/commands/providers.js +1 -0
- package/dist/src/commands/providers.js.map +1 -1
- package/dist/src/mcp-tools/daa-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/daa-tools.js +41 -20
- package/dist/src/mcp-tools/daa-tools.js.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +6 -10
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/system-tools.js +82 -5
- package/dist/src/mcp-tools/system-tools.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,aAAa,CAAC;AA8rE1E,eAAO,MAAM,cAAc,EAAE,OAsF5B,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -19,6 +19,7 @@ import * as path from 'path';
|
|
|
19
19
|
import * as fs from 'fs/promises';
|
|
20
20
|
import { writeFile } from 'fs/promises';
|
|
21
21
|
import { resolve } from 'path';
|
|
22
|
+
import { execSync } from 'child_process';
|
|
22
23
|
// Dynamic import for AST analyzer
|
|
23
24
|
async function getASTAnalyzer() {
|
|
24
25
|
try {
|
|
@@ -251,23 +252,146 @@ const codeCommand = {
|
|
|
251
252
|
{ command: 'claude-flow analyze code --type complexity', description: 'Run complexity analysis' },
|
|
252
253
|
],
|
|
253
254
|
action: async (ctx) => {
|
|
254
|
-
const
|
|
255
|
+
const targetPath = resolve(ctx.flags.path || '.');
|
|
255
256
|
const analysisType = ctx.flags.type || 'quality';
|
|
257
|
+
const formatJson = ctx.flags.format === 'json';
|
|
256
258
|
output.writeln();
|
|
257
259
|
output.writeln(output.bold('Code Analysis'));
|
|
258
260
|
output.writeln(output.dim('-'.repeat(50)));
|
|
259
|
-
output.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
261
|
+
const spinner = output.createSpinner({ text: `Analyzing ${targetPath}...`, spinner: 'dots' });
|
|
262
|
+
spinner.start();
|
|
263
|
+
try {
|
|
264
|
+
const files = await scanSourceFiles(targetPath);
|
|
265
|
+
if (files.length === 0) {
|
|
266
|
+
spinner.stop();
|
|
267
|
+
output.printWarning('No source files found');
|
|
268
|
+
return { success: true };
|
|
269
|
+
}
|
|
270
|
+
const fileStats = [];
|
|
271
|
+
for (const filePath of files) {
|
|
272
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
273
|
+
const lines = content.split('\n');
|
|
274
|
+
const nonEmpty = lines.filter(l => l.trim().length > 0 && !/^\s*(\/\/|\/\*|\*\s|#)/.test(l)).length;
|
|
275
|
+
const todos = (content.match(/\b(TODO|FIXME|HACK|XXX)\b/gi) || []).length;
|
|
276
|
+
const fns = (content.match(/(?:export\s+)?(?:async\s+)?function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g) || []).length;
|
|
277
|
+
const imps = (content.match(/^import\s+/gm) || []).length + (content.match(/require\s*\(/g) || []).length;
|
|
278
|
+
let maxNesting = 0;
|
|
279
|
+
let nesting = 0;
|
|
280
|
+
for (const line of lines) {
|
|
281
|
+
nesting += (line.match(/\{/g) || []).length;
|
|
282
|
+
nesting -= (line.match(/\}/g) || []).length;
|
|
283
|
+
if (nesting > maxNesting)
|
|
284
|
+
maxNesting = nesting;
|
|
285
|
+
}
|
|
286
|
+
const securityIssues = [];
|
|
287
|
+
if (/\beval\s*\(/.test(content))
|
|
288
|
+
securityIssues.push('eval()');
|
|
289
|
+
if (/\bexec\s*\(/.test(content))
|
|
290
|
+
securityIssues.push('exec()');
|
|
291
|
+
if (/\.innerHTML\s*=/.test(content))
|
|
292
|
+
securityIssues.push('innerHTML');
|
|
293
|
+
if (/dangerouslySetInnerHTML/.test(content))
|
|
294
|
+
securityIssues.push('dangerouslySetInnerHTML');
|
|
295
|
+
if (/['"](?:password|secret|api[_-]?key|token)\s*[:=]\s*['"][^'"]{3,}['"]/i.test(content))
|
|
296
|
+
securityIssues.push('hardcoded secret');
|
|
297
|
+
if (/new\s+Function\s*\(/.test(content))
|
|
298
|
+
securityIssues.push('new Function()');
|
|
299
|
+
fileStats.push({
|
|
300
|
+
file: filePath,
|
|
301
|
+
loc: nonEmpty,
|
|
302
|
+
todos,
|
|
303
|
+
functions: fns,
|
|
304
|
+
imports: imps,
|
|
305
|
+
maxNesting,
|
|
306
|
+
securityIssues,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
spinner.stop();
|
|
310
|
+
const totalLoc = fileStats.reduce((s, f) => s + f.loc, 0);
|
|
311
|
+
const totalTodos = fileStats.reduce((s, f) => s + f.todos, 0);
|
|
312
|
+
const totalFunctions = fileStats.reduce((s, f) => s + f.functions, 0);
|
|
313
|
+
const totalImports = fileStats.reduce((s, f) => s + f.imports, 0);
|
|
314
|
+
const avgFileSize = Math.round(totalLoc / files.length);
|
|
315
|
+
const longestFile = fileStats.reduce((a, b) => a.loc > b.loc ? a : b);
|
|
316
|
+
const avgFnPerFile = (totalFunctions / files.length).toFixed(1);
|
|
317
|
+
const deepestNesting = fileStats.reduce((a, b) => a.maxNesting > b.maxNesting ? a : b);
|
|
318
|
+
const allSecurityIssues = fileStats.filter(f => f.securityIssues.length > 0);
|
|
319
|
+
if (formatJson) {
|
|
320
|
+
const jsonData = { type: analysisType, path: targetPath, files: files.length, totalLoc, totalTodos, totalFunctions, totalImports, avgFileSize, fileStats: fileStats.map(f => ({ relativePath: path.relative(targetPath, f.file), loc: f.loc, todos: f.todos, functions: f.functions, imports: f.imports, maxNesting: f.maxNesting, securityIssues: f.securityIssues })) };
|
|
321
|
+
output.printJson(jsonData);
|
|
322
|
+
return { success: true, data: jsonData };
|
|
323
|
+
}
|
|
324
|
+
if (analysisType === 'quality') {
|
|
325
|
+
output.printBox([`Files: ${files.length}`, `Lines of Code: ${totalLoc.toLocaleString()}`, `Avg File Size: ${avgFileSize} LOC`, `TODO/FIXME: ${totalTodos}`, `Functions: ${totalFunctions}`, `Imports: ${totalImports}`].join('\n'), 'Quality Summary');
|
|
326
|
+
output.writeln();
|
|
327
|
+
output.writeln(output.bold('Largest Files'));
|
|
328
|
+
output.writeln(output.dim('-'.repeat(60)));
|
|
329
|
+
const top10 = [...fileStats].sort((a, b) => b.loc - a.loc).slice(0, 10);
|
|
330
|
+
output.printTable({
|
|
331
|
+
columns: [
|
|
332
|
+
{ key: 'file', header: 'File', width: 45 },
|
|
333
|
+
{ key: 'loc', header: 'LOC', width: 8, align: 'right' },
|
|
334
|
+
{ key: 'fns', header: 'Fns', width: 6, align: 'right' },
|
|
335
|
+
{ key: 'todos', header: 'TODOs', width: 7, align: 'right' },
|
|
336
|
+
],
|
|
337
|
+
data: top10.map(f => ({ file: path.relative(targetPath, f.file), loc: f.loc, fns: f.functions, todos: f.todos })),
|
|
338
|
+
});
|
|
339
|
+
if (totalTodos > 0) {
|
|
340
|
+
output.writeln();
|
|
341
|
+
output.printWarning(`${totalTodos} TODO/FIXME comments found across ${fileStats.filter(f => f.todos > 0).length} files`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else if (analysisType === 'complexity') {
|
|
345
|
+
output.printBox([`Files: ${files.length}`, `Total Functions: ${totalFunctions}`, `Avg Functions/File: ${avgFnPerFile}`, `Deepest Nesting: ${deepestNesting.maxNesting} levels (${path.relative(targetPath, deepestNesting.file)})`, `Longest File: ${longestFile.loc} LOC (${path.relative(targetPath, longestFile.file)})`].join('\n'), 'Complexity Summary');
|
|
346
|
+
output.writeln();
|
|
347
|
+
output.writeln(output.bold('High Complexity Files (nesting > 5)'));
|
|
348
|
+
output.writeln(output.dim('-'.repeat(60)));
|
|
349
|
+
const complex = fileStats.filter(f => f.maxNesting > 5).sort((a, b) => b.maxNesting - a.maxNesting);
|
|
350
|
+
if (complex.length === 0) {
|
|
351
|
+
output.printSuccess('No files with excessive nesting detected');
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
output.printTable({
|
|
355
|
+
columns: [
|
|
356
|
+
{ key: 'file', header: 'File', width: 45 },
|
|
357
|
+
{ key: 'nesting', header: 'Max Nest', width: 10, align: 'right' },
|
|
358
|
+
{ key: 'fns', header: 'Fns', width: 6, align: 'right' },
|
|
359
|
+
{ key: 'loc', header: 'LOC', width: 8, align: 'right' },
|
|
360
|
+
],
|
|
361
|
+
data: complex.slice(0, 15).map(f => ({ file: path.relative(targetPath, f.file), nesting: f.maxNesting, fns: f.functions, loc: f.loc })),
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
else if (analysisType === 'security') {
|
|
366
|
+
output.printBox([`Files Scanned: ${files.length}`, `Files with Issues: ${allSecurityIssues.length}`, `Total Issues: ${allSecurityIssues.reduce((s, f) => s + f.securityIssues.length, 0)}`].join('\n'), 'Security Summary');
|
|
367
|
+
if (allSecurityIssues.length === 0) {
|
|
368
|
+
output.writeln();
|
|
369
|
+
output.printSuccess('No common security patterns detected');
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
output.writeln();
|
|
373
|
+
output.writeln(output.bold('Security Concerns'));
|
|
374
|
+
output.writeln(output.dim('-'.repeat(60)));
|
|
375
|
+
output.printTable({
|
|
376
|
+
columns: [
|
|
377
|
+
{ key: 'file', header: 'File', width: 40 },
|
|
378
|
+
{ key: 'issues', header: 'Issues', width: 35 },
|
|
379
|
+
],
|
|
380
|
+
data: allSecurityIssues.map(f => ({ file: path.relative(targetPath, f.file), issues: f.securityIssues.join(', ') })),
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
output.printWarning(`Unknown analysis type: ${analysisType}. Use quality, complexity, or security.`);
|
|
386
|
+
}
|
|
387
|
+
return { success: true };
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
spinner.stop();
|
|
391
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
392
|
+
output.printError(`Code analysis failed: ${message}`);
|
|
393
|
+
return { success: false, exitCode: 1 };
|
|
394
|
+
}
|
|
271
395
|
},
|
|
272
396
|
};
|
|
273
397
|
// ============================================================================
|
|
@@ -1106,21 +1230,122 @@ const depsCommand = {
|
|
|
1106
1230
|
action: async (ctx) => {
|
|
1107
1231
|
const showOutdated = ctx.flags.outdated;
|
|
1108
1232
|
const checkSecurity = ctx.flags.security;
|
|
1233
|
+
const formatJson = ctx.flags.format === 'json';
|
|
1109
1234
|
output.writeln();
|
|
1110
1235
|
output.writeln(output.bold('Dependency Analysis'));
|
|
1111
1236
|
output.writeln(output.dim('-'.repeat(50)));
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1237
|
+
try {
|
|
1238
|
+
const pkgPath = resolve('package.json');
|
|
1239
|
+
let pkgContent;
|
|
1240
|
+
try {
|
|
1241
|
+
pkgContent = await fs.readFile(pkgPath, 'utf-8');
|
|
1242
|
+
}
|
|
1243
|
+
catch {
|
|
1244
|
+
output.printError('No package.json found in current directory');
|
|
1245
|
+
return { success: false, exitCode: 1 };
|
|
1246
|
+
}
|
|
1247
|
+
const pkg = JSON.parse(pkgContent);
|
|
1248
|
+
const deps = Object.entries(pkg.dependencies || {});
|
|
1249
|
+
const devDeps = Object.entries(pkg.devDependencies || {});
|
|
1250
|
+
const optDeps = Object.entries(pkg.optionalDependencies || {});
|
|
1251
|
+
const peerDeps = Object.entries(pkg.peerDependencies || {});
|
|
1252
|
+
const total = deps.length + devDeps.length + optDeps.length + peerDeps.length;
|
|
1253
|
+
if (formatJson && !showOutdated && !checkSecurity) {
|
|
1254
|
+
const jsonData = { name: pkg.name, version: pkg.version, dependencies: deps.length, devDependencies: devDeps.length, optionalDependencies: optDeps.length, peerDependencies: peerDeps.length, total };
|
|
1255
|
+
output.printJson(jsonData);
|
|
1256
|
+
return { success: true, data: jsonData };
|
|
1257
|
+
}
|
|
1258
|
+
output.printBox([`Package: ${pkg.name || 'unknown'} @ ${pkg.version || '0.0.0'}`, `Dependencies: ${deps.length}`, `Dev Dependencies: ${devDeps.length}`, `Optional: ${optDeps.length}`, `Peer: ${peerDeps.length}`, `Total: ${total}`].join('\n'), 'Dependency Summary');
|
|
1259
|
+
if (showOutdated) {
|
|
1260
|
+
output.writeln();
|
|
1261
|
+
output.writeln(output.bold('Outdated Check'));
|
|
1262
|
+
output.writeln(output.dim('-'.repeat(60)));
|
|
1263
|
+
const outdated = [];
|
|
1264
|
+
const checkDeps = async (entries, category) => {
|
|
1265
|
+
for (const [name, declared] of entries) {
|
|
1266
|
+
try {
|
|
1267
|
+
const installedPkg = resolve('node_modules', name, 'package.json');
|
|
1268
|
+
const raw = await fs.readFile(installedPkg, 'utf-8');
|
|
1269
|
+
const installedContent = JSON.parse(raw);
|
|
1270
|
+
const installed = installedContent.version || 'unknown';
|
|
1271
|
+
const cleanDeclared = declared.replace(/^[\^~>=<]+/, '');
|
|
1272
|
+
if (installed !== cleanDeclared) {
|
|
1273
|
+
outdated.push({ name, declared: declared, installed, category });
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
catch {
|
|
1277
|
+
outdated.push({ name, declared: declared, installed: 'not installed', category });
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
await checkDeps(deps, 'prod');
|
|
1282
|
+
await checkDeps(devDeps, 'dev');
|
|
1283
|
+
if (outdated.length === 0) {
|
|
1284
|
+
output.printSuccess('All dependencies match declared versions');
|
|
1285
|
+
}
|
|
1286
|
+
else {
|
|
1287
|
+
output.printTable({
|
|
1288
|
+
columns: [
|
|
1289
|
+
{ key: 'name', header: 'Package', width: 30 },
|
|
1290
|
+
{ key: 'declared', header: 'Declared', width: 14 },
|
|
1291
|
+
{ key: 'installed', header: 'Installed', width: 14 },
|
|
1292
|
+
{ key: 'category', header: 'Type', width: 6 },
|
|
1293
|
+
],
|
|
1294
|
+
data: outdated.slice(0, 30),
|
|
1295
|
+
});
|
|
1296
|
+
if (outdated.length > 30) {
|
|
1297
|
+
output.writeln(output.dim(` ... and ${outdated.length - 30} more`));
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (checkSecurity) {
|
|
1302
|
+
output.writeln();
|
|
1303
|
+
output.writeln(output.bold('Security Audit'));
|
|
1304
|
+
output.writeln(output.dim('-'.repeat(60)));
|
|
1305
|
+
try {
|
|
1306
|
+
const auditRaw = execSync('npm audit --json 2>/dev/null', { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
|
|
1307
|
+
const audit = JSON.parse(auditRaw);
|
|
1308
|
+
const vulns = audit.metadata?.vulnerabilities || audit.vulnerabilities || {};
|
|
1309
|
+
const info = vulns.info || 0;
|
|
1310
|
+
const low = vulns.low || 0;
|
|
1311
|
+
const moderate = vulns.moderate || 0;
|
|
1312
|
+
const high = vulns.high || 0;
|
|
1313
|
+
const critical = vulns.critical || 0;
|
|
1314
|
+
const totalVulns = info + low + moderate + high + critical;
|
|
1315
|
+
if (totalVulns === 0) {
|
|
1316
|
+
output.printSuccess('No known vulnerabilities found');
|
|
1317
|
+
}
|
|
1318
|
+
else {
|
|
1319
|
+
output.printTable({
|
|
1320
|
+
columns: [
|
|
1321
|
+
{ key: 'severity', header: 'Severity', width: 12 },
|
|
1322
|
+
{ key: 'count', header: 'Count', width: 8, align: 'right' },
|
|
1323
|
+
],
|
|
1324
|
+
data: [
|
|
1325
|
+
...(critical > 0 ? [{ severity: 'Critical', count: critical }] : []),
|
|
1326
|
+
...(high > 0 ? [{ severity: 'High', count: high }] : []),
|
|
1327
|
+
...(moderate > 0 ? [{ severity: 'Moderate', count: moderate }] : []),
|
|
1328
|
+
...(low > 0 ? [{ severity: 'Low', count: low }] : []),
|
|
1329
|
+
...(info > 0 ? [{ severity: 'Info', count: info }] : []),
|
|
1330
|
+
{ severity: 'Total', count: totalVulns },
|
|
1331
|
+
],
|
|
1332
|
+
});
|
|
1333
|
+
if (critical > 0 || high > 0) {
|
|
1334
|
+
output.printWarning(`${critical + high} high/critical vulnerabilities found. Run 'npm audit' for details.`);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
catch {
|
|
1339
|
+
output.printWarning('npm audit failed. Ensure npm is available and node_modules is installed.');
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return { success: true };
|
|
1343
|
+
}
|
|
1344
|
+
catch (error) {
|
|
1345
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1346
|
+
output.printError(`Dependency analysis failed: ${message}`);
|
|
1347
|
+
return { success: false, exitCode: 1 };
|
|
1348
|
+
}
|
|
1124
1349
|
},
|
|
1125
1350
|
};
|
|
1126
1351
|
// ============================================================================
|