delimit-cli 4.1.1 → 4.1.2

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.
Files changed (2) hide show
  1. package/bin/delimit-cli.js +91 -0
  2. package/package.json +1 -1
@@ -2328,6 +2328,97 @@ program
2328
2328
  console.log('');
2329
2329
  });
2330
2330
 
2331
+ // Scan command — instant governance analysis of any project or spec
2332
+ program
2333
+ .command('scan [path]')
2334
+ .description('Scan a project or OpenAPI spec for governance insights')
2335
+ .action(async (specPath) => {
2336
+ const target = specPath || '.';
2337
+ console.log(chalk.bold('\n Delimit Scan\n'));
2338
+
2339
+ // Detect if target is a spec file or a project directory
2340
+ const isFile = fs.existsSync(target) && fs.statSync(target).isFile();
2341
+
2342
+ if (isFile) {
2343
+ // Spec file — run spec health + show results
2344
+ console.log(chalk.gray(` Analyzing ${target}...\n`));
2345
+ try {
2346
+ const result = execSync(
2347
+ `python3 -c "import sys,json; sys.path.insert(0,'${continuityContext.serverDir}'); from core.spec_health import score_spec; import yaml; spec=yaml.safe_load(open('${target}')); r=score_spec(spec); print(json.dumps(r))"`,
2348
+ { encoding: 'utf-8', timeout: 15000, cwd: continuityContext.serverDir }
2349
+ );
2350
+ const health = JSON.parse(result);
2351
+ const gradeColors = { A: 'green', B: 'blue', C: 'yellow', D: 'red', F: 'red' };
2352
+ const gradeColor = gradeColors[health.grade] || 'white';
2353
+ console.log(` ${chalk[gradeColor].bold(health.grade)} ${chalk.white.bold(health.overall_score + '/100')} ${chalk.gray('Spec Health Score')}\n`);
2354
+ for (const [dim, data] of Object.entries(health.dimensions || {})) {
2355
+ const score = data.score || 0;
2356
+ const bar = '█'.repeat(Math.round(score / 5)) + '░'.repeat(20 - Math.round(score / 5));
2357
+ const color = score >= 70 ? 'green' : score >= 40 ? 'yellow' : 'red';
2358
+ console.log(` ${chalk.gray(dim.padEnd(16))} ${chalk[color](bar)} ${score}`);
2359
+ }
2360
+ if (health.recommendations && health.recommendations.length > 0) {
2361
+ console.log(chalk.bold('\n Recommendations:\n'));
2362
+ health.recommendations.slice(0, 5).forEach(r => {
2363
+ const text = typeof r === 'object' ? (r.recommendation || r.text || JSON.stringify(r)) : r;
2364
+ console.log(` ${chalk.yellow('→')} ${text}`);
2365
+ });
2366
+ }
2367
+ console.log(chalk.bold('\n Next steps:\n'));
2368
+ console.log(` ${chalk.green('npx delimit-cli lint')} ${target} ${chalk.gray('— check for breaking changes')}`);
2369
+ console.log(` ${chalk.green('npx delimit-cli init')} ${chalk.gray('— set up governance for this project')}\n`);
2370
+ } catch (e) {
2371
+ console.log(chalk.red(` Error: ${e.message}`));
2372
+ }
2373
+ } else {
2374
+ // Project directory — find specs and scan
2375
+ console.log(chalk.gray(` Scanning ${path.resolve(target)}...\n`));
2376
+ try {
2377
+ const result = execSync(
2378
+ `python3 -c "import sys,json; sys.path.insert(0,'${continuityContext.serverDir}'); from ai.server import delimit_scan; r=delimit_scan.fn('${target}') if hasattr(delimit_scan,'fn') else delimit_scan('${target}'); print(json.dumps(r))"`,
2379
+ { encoding: 'utf-8', timeout: 30000, cwd: continuityContext.serverDir }
2380
+ );
2381
+ const scan = JSON.parse(result);
2382
+ const findings = scan.findings || [];
2383
+ const specs = findings.find(f => f.type === 'openapi_specs');
2384
+ if (specs) {
2385
+ console.log(` ${chalk.green('✓')} Found ${specs.count} OpenAPI spec(s): ${specs.files.slice(0, 3).join(', ')}`);
2386
+ } else {
2387
+ console.log(` ${chalk.yellow('—')} No OpenAPI specs found`);
2388
+ }
2389
+ const frameworks = findings.filter(f => f.type === 'framework');
2390
+ frameworks.forEach(f => console.log(` ${chalk.green('✓')} Framework: ${f.name}`));
2391
+ const suggestions = scan.suggestions || [];
2392
+ if (suggestions.length > 0) {
2393
+ console.log(chalk.bold('\n Suggestions:\n'));
2394
+ suggestions.slice(0, 3).forEach(s => {
2395
+ console.log(` ${chalk.blue('→')} ${s.detail}`);
2396
+ });
2397
+ }
2398
+ // If spec found, run health on it
2399
+ if (specs && specs.files.length > 0) {
2400
+ const specFile = path.join(target, specs.files[0]);
2401
+ console.log(chalk.gray(`\n Running spec health on ${specs.files[0]}...`));
2402
+ try {
2403
+ const healthResult = execSync(
2404
+ `python3 -c "import sys,json; sys.path.insert(0,'${continuityContext.serverDir}'); from core.spec_health import score_spec; import yaml; spec=yaml.safe_load(open('${specFile}')); r=score_spec(spec); print(json.dumps(r))"`,
2405
+ { encoding: 'utf-8', timeout: 15000, cwd: continuityContext.serverDir }
2406
+ );
2407
+ const health = JSON.parse(healthResult);
2408
+ const gradeColors = { A: 'green', B: 'blue', C: 'yellow', D: 'red', F: 'red' };
2409
+ const gradeColor = gradeColors[health.grade] || 'white';
2410
+ console.log(`\n ${chalk[gradeColor].bold(health.grade)} ${chalk.white.bold(health.overall_score + '/100')} ${chalk.gray('Spec Health Score')}`);
2411
+ } catch {}
2412
+ }
2413
+ console.log(chalk.bold('\n Next:\n'));
2414
+ console.log(` ${chalk.green('npx delimit-cli init')} ${chalk.gray('— set up governance')}`);
2415
+ console.log(` ${chalk.green('npx delimit-cli setup')} ${chalk.gray('— configure AI assistants')}\n`);
2416
+ } catch (e) {
2417
+ console.log(chalk.red(` Error: ${e.message}`));
2418
+ }
2419
+ }
2420
+ });
2421
+
2331
2422
  // Try command — zero-risk demo with Markdown report artifact (LED-264)
2332
2423
  program
2333
2424
  .command('try')
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "delimit-cli",
3
3
  "mcpName": "io.github.delimit-ai/delimit-mcp-server",
4
- "version": "4.1.1",
4
+ "version": "4.1.2",
5
5
  "description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
6
6
  "main": "index.js",
7
7
  "files": [