delimit-cli 4.1.2 → 4.1.3
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/bin/delimit-cli.js +77 -35
- package/package.json +1 -1
package/bin/delimit-cli.js
CHANGED
|
@@ -2333,9 +2333,36 @@ program
|
|
|
2333
2333
|
.command('scan [path]')
|
|
2334
2334
|
.description('Scan a project or OpenAPI spec for governance insights')
|
|
2335
2335
|
.action(async (specPath) => {
|
|
2336
|
-
const target = specPath
|
|
2336
|
+
const target = specPath ? path.resolve(specPath) : process.cwd();
|
|
2337
2337
|
console.log(chalk.bold('\n Delimit Scan\n'));
|
|
2338
2338
|
|
|
2339
|
+
// Resolve gateway dir: installed server > bundled in npm package
|
|
2340
|
+
const bundledGateway = path.join(__dirname, '..', 'gateway');
|
|
2341
|
+
const serverDir = (continuityContext.serverDir && continuityContext.serverDir !== 'undefined' && fs.existsSync(continuityContext.serverDir))
|
|
2342
|
+
? continuityContext.serverDir
|
|
2343
|
+
: fs.existsSync(bundledGateway) ? bundledGateway : null;
|
|
2344
|
+
|
|
2345
|
+
if (!serverDir) {
|
|
2346
|
+
console.log(chalk.yellow(' Gateway not found. Installing...\n'));
|
|
2347
|
+
try {
|
|
2348
|
+
execSync('npx delimit-cli setup --yes', { stdio: 'inherit', timeout: 60000 });
|
|
2349
|
+
// Retry after setup
|
|
2350
|
+
console.log(chalk.green('\n Setup complete. Re-running scan...\n'));
|
|
2351
|
+
execSync(`npx delimit-cli scan ${specPath || ''}`, { stdio: 'inherit', timeout: 30000 });
|
|
2352
|
+
} catch {
|
|
2353
|
+
console.log(chalk.red('\n Auto-setup failed. Run manually: npx delimit-cli setup'));
|
|
2354
|
+
}
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// Check Python + yaml dependency
|
|
2359
|
+
try {
|
|
2360
|
+
execSync('python3 -c "import yaml"', { stdio: 'ignore', timeout: 5000 });
|
|
2361
|
+
} catch {
|
|
2362
|
+
console.log(chalk.yellow(' Installing Python dependency (pyyaml)...\n'));
|
|
2363
|
+
try { execSync('pip3 install pyyaml -q', { stdio: 'ignore', timeout: 30000 }); } catch {}
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2339
2366
|
// Detect if target is a spec file or a project directory
|
|
2340
2367
|
const isFile = fs.existsSync(target) && fs.statSync(target).isFile();
|
|
2341
2368
|
|
|
@@ -2344,8 +2371,8 @@ program
|
|
|
2344
2371
|
console.log(chalk.gray(` Analyzing ${target}...\n`));
|
|
2345
2372
|
try {
|
|
2346
2373
|
const result = execSync(
|
|
2347
|
-
`python3 -c "import sys,json; sys.path.insert(0,'${
|
|
2348
|
-
{ encoding: 'utf-8', timeout: 15000, cwd:
|
|
2374
|
+
`python3 -c "import sys,json; sys.path.insert(0,'${serverDir}'); from core.spec_health import score_spec; import yaml; spec=yaml.safe_load(open('${target}')); r=score_spec(spec); print(json.dumps(r))"`,
|
|
2375
|
+
{ encoding: 'utf-8', timeout: 15000, cwd: serverDir }
|
|
2349
2376
|
);
|
|
2350
2377
|
const health = JSON.parse(result);
|
|
2351
2378
|
const gradeColors = { A: 'green', B: 'blue', C: 'yellow', D: 'red', F: 'red' };
|
|
@@ -2371,48 +2398,63 @@ program
|
|
|
2371
2398
|
console.log(chalk.red(` Error: ${e.message}`));
|
|
2372
2399
|
}
|
|
2373
2400
|
} else {
|
|
2374
|
-
// Project directory — find specs
|
|
2375
|
-
console.log(chalk.gray(` Scanning ${
|
|
2401
|
+
// Project directory — find specs using simple glob, no server.py needed
|
|
2402
|
+
console.log(chalk.gray(` Scanning ${target}...\n`));
|
|
2376
2403
|
try {
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
)
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
console.log(chalk.gray(`\n Running spec health on ${specs.files[0]}...`));
|
|
2404
|
+
// Find OpenAPI/Swagger specs
|
|
2405
|
+
const specPatterns = ['openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.yml', 'swagger.json'];
|
|
2406
|
+
const found = [];
|
|
2407
|
+
const _findSpecs = (dir, depth) => {
|
|
2408
|
+
if (depth > 4) return;
|
|
2409
|
+
try {
|
|
2410
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2411
|
+
if (entry.name === 'node_modules' || entry.name === '.next' || entry.name === 'venv' || entry.name === '.git') continue;
|
|
2412
|
+
const full = path.join(dir, entry.name);
|
|
2413
|
+
if (entry.isFile() && specPatterns.includes(entry.name.toLowerCase())) {
|
|
2414
|
+
found.push(path.relative(target, full));
|
|
2415
|
+
} else if (entry.isDirectory()) {
|
|
2416
|
+
_findSpecs(full, depth + 1);
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
} catch {}
|
|
2420
|
+
};
|
|
2421
|
+
_findSpecs(target, 0);
|
|
2422
|
+
|
|
2423
|
+
if (found.length > 0) {
|
|
2424
|
+
console.log(` ${chalk.green('✓')} Found ${found.length} OpenAPI spec(s): ${found.slice(0, 3).join(', ')}`);
|
|
2425
|
+
// Run health on the first spec
|
|
2426
|
+
const specFile = path.join(target, found[0]);
|
|
2427
|
+
console.log(chalk.gray(`\n Scoring ${found[0]}...\n`));
|
|
2402
2428
|
try {
|
|
2403
2429
|
const healthResult = execSync(
|
|
2404
|
-
`python3 -c "import sys,json; sys.path.insert(0,'${
|
|
2405
|
-
{ encoding: 'utf-8', timeout: 15000, cwd:
|
|
2430
|
+
`python3 -c "import sys,json; sys.path.insert(0,'${serverDir}'); from core.spec_health import score_spec; import yaml; spec=yaml.safe_load(open('${specFile}')); r=score_spec(spec); print(json.dumps(r))"`,
|
|
2431
|
+
{ encoding: 'utf-8', timeout: 15000, cwd: serverDir }
|
|
2406
2432
|
);
|
|
2407
2433
|
const health = JSON.parse(healthResult);
|
|
2408
2434
|
const gradeColors = { A: 'green', B: 'blue', C: 'yellow', D: 'red', F: 'red' };
|
|
2409
2435
|
const gradeColor = gradeColors[health.grade] || 'white';
|
|
2410
|
-
console.log(
|
|
2436
|
+
console.log(` ${chalk[gradeColor].bold(health.grade)} ${chalk.white.bold(health.overall_score + '/100')} ${chalk.gray('Spec Health Score')}\n`);
|
|
2437
|
+
for (const [dim, data] of Object.entries(health.dimensions || {})) {
|
|
2438
|
+
const score = data.score || 0;
|
|
2439
|
+
const bar = '\u2588'.repeat(Math.round(score / 5)) + '\u2591'.repeat(20 - Math.round(score / 5));
|
|
2440
|
+
const color = score >= 70 ? 'green' : score >= 40 ? 'yellow' : 'red';
|
|
2441
|
+
console.log(` ${chalk.gray(dim.padEnd(16))} ${chalk[color](bar)} ${score}`);
|
|
2442
|
+
}
|
|
2443
|
+
if (health.recommendations && health.recommendations.length > 0) {
|
|
2444
|
+
console.log(chalk.bold('\n Recommendations:\n'));
|
|
2445
|
+
health.recommendations.slice(0, 5).forEach(r => {
|
|
2446
|
+
const text = typeof r === 'object' ? (r.recommendation || r.text || JSON.stringify(r)) : r;
|
|
2447
|
+
console.log(` ${chalk.yellow('\u2192')} ${text}`);
|
|
2448
|
+
});
|
|
2449
|
+
}
|
|
2411
2450
|
} catch {}
|
|
2451
|
+
} else {
|
|
2452
|
+
console.log(` ${chalk.yellow('\u2014')} No OpenAPI specs found in this directory`);
|
|
2453
|
+
console.log(chalk.gray(' Tip: point at a spec file directly: npx delimit-cli scan openapi.yaml'));
|
|
2412
2454
|
}
|
|
2413
2455
|
console.log(chalk.bold('\n Next:\n'));
|
|
2414
|
-
console.log(` ${chalk.green('npx delimit-cli init')} ${chalk.gray('
|
|
2415
|
-
console.log(` ${chalk.green('npx delimit-cli setup')} ${chalk.gray('
|
|
2456
|
+
console.log(` ${chalk.green('npx delimit-cli init')} ${chalk.gray('\u2014 set up governance')}`);
|
|
2457
|
+
console.log(` ${chalk.green('npx delimit-cli setup')} ${chalk.gray('\u2014 configure AI assistants')}\n`);
|
|
2416
2458
|
} catch (e) {
|
|
2417
2459
|
console.log(chalk.red(` Error: ${e.message}`));
|
|
2418
2460
|
}
|
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.
|
|
4
|
+
"version": "4.1.3",
|
|
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": [
|