delimit-cli 3.14.0 → 3.14.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.
- package/bin/delimit-cli.js +204 -0
- package/package.json +1 -1
package/bin/delimit-cli.js
CHANGED
|
@@ -5,6 +5,7 @@ const axios = require('axios');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { execSync, spawn } = require('child_process');
|
|
8
|
+
const os = require('os');
|
|
8
9
|
const chalk = require('chalk');
|
|
9
10
|
const inquirer = require('inquirer');
|
|
10
11
|
const DelimitAuthSetup = require('../lib/auth-setup');
|
|
@@ -1331,7 +1332,210 @@ jobs:
|
|
|
1331
1332
|
}
|
|
1332
1333
|
console.log(` ${chalk.bold('delimit doctor')} — verify setup`);
|
|
1333
1334
|
console.log(` ${chalk.bold('delimit explain')} — human-readable report`);
|
|
1335
|
+
|
|
1336
|
+
// Beta capture after init (LED-263)
|
|
1337
|
+
if (!options.yes) {
|
|
1338
|
+
try {
|
|
1339
|
+
const betaAns = await inquirer.prompt([{
|
|
1340
|
+
type: 'input',
|
|
1341
|
+
name: 'email',
|
|
1342
|
+
message: chalk.blue(' Join the beta? Enter your email (or press Enter to skip):'),
|
|
1343
|
+
}]);
|
|
1344
|
+
if (betaAns.email && betaAns.email.includes('@')) {
|
|
1345
|
+
try {
|
|
1346
|
+
await axios.post('https://delimit.ai/api/subscribe', { email: betaAns.email, source: 'cli-init' });
|
|
1347
|
+
console.log(chalk.green(' Thanks! You\'re on the list.\n'));
|
|
1348
|
+
} catch {
|
|
1349
|
+
console.log(chalk.green(' Thanks! Visit https://delimit.ai to stay updated.\n'));
|
|
1350
|
+
}
|
|
1351
|
+
} else {
|
|
1352
|
+
console.log('');
|
|
1353
|
+
}
|
|
1354
|
+
} catch {}
|
|
1355
|
+
} else {
|
|
1356
|
+
console.log('');
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
// Demo command — prove governance value in 5 minutes (LED-262)
|
|
1361
|
+
program
|
|
1362
|
+
.command('demo')
|
|
1363
|
+
.description('Run a self-contained governance demo — proves value in under 5 minutes')
|
|
1364
|
+
.action(async () => {
|
|
1365
|
+
const tmpDir = path.join(os.tmpdir(), `delimit-demo-${Date.now()}`);
|
|
1366
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
1367
|
+
|
|
1368
|
+
console.log(chalk.bold('\n Delimit Governance Demo\n'));
|
|
1369
|
+
console.log(chalk.gray(` Working in ${tmpDir}\n`));
|
|
1370
|
+
|
|
1371
|
+
// Step 1: Create a sample API spec
|
|
1372
|
+
console.log(chalk.bold(' Step 1: Creating sample API spec...'));
|
|
1373
|
+
const baseSpec = {
|
|
1374
|
+
openapi: '3.0.3',
|
|
1375
|
+
info: { title: 'Pet Store API', version: '1.0.0' },
|
|
1376
|
+
paths: {
|
|
1377
|
+
'/pets': {
|
|
1378
|
+
get: {
|
|
1379
|
+
summary: 'List all pets',
|
|
1380
|
+
operationId: 'listPets',
|
|
1381
|
+
parameters: [
|
|
1382
|
+
{ name: 'limit', in: 'query', required: false, schema: { type: 'integer' } },
|
|
1383
|
+
],
|
|
1384
|
+
responses: { '200': { description: 'A list of pets', content: { 'application/json': { schema: { type: 'array', items: { '$ref': '#/components/schemas/Pet' } } } } } },
|
|
1385
|
+
},
|
|
1386
|
+
post: {
|
|
1387
|
+
summary: 'Create a pet',
|
|
1388
|
+
operationId: 'createPet',
|
|
1389
|
+
requestBody: { content: { 'application/json': { schema: { '$ref': '#/components/schemas/Pet' } } } },
|
|
1390
|
+
responses: { '201': { description: 'Pet created' } },
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
'/pets/{petId}': {
|
|
1394
|
+
get: {
|
|
1395
|
+
summary: 'Get a pet by ID',
|
|
1396
|
+
operationId: 'showPetById',
|
|
1397
|
+
parameters: [{ name: 'petId', in: 'path', required: true, schema: { type: 'string' } }],
|
|
1398
|
+
responses: { '200': { description: 'A pet', content: { 'application/json': { schema: { '$ref': '#/components/schemas/Pet' } } } } },
|
|
1399
|
+
},
|
|
1400
|
+
},
|
|
1401
|
+
},
|
|
1402
|
+
components: {
|
|
1403
|
+
schemas: {
|
|
1404
|
+
Pet: {
|
|
1405
|
+
type: 'object',
|
|
1406
|
+
required: ['id', 'name'],
|
|
1407
|
+
properties: {
|
|
1408
|
+
id: { type: 'integer', format: 'int64' },
|
|
1409
|
+
name: { type: 'string' },
|
|
1410
|
+
tag: { type: 'string' },
|
|
1411
|
+
},
|
|
1412
|
+
},
|
|
1413
|
+
},
|
|
1414
|
+
},
|
|
1415
|
+
};
|
|
1416
|
+
|
|
1417
|
+
const baseSpecPath = path.join(tmpDir, 'openapi-v1.yaml');
|
|
1418
|
+
fs.writeFileSync(baseSpecPath, yaml.dump(baseSpec));
|
|
1419
|
+
console.log(chalk.green(' Created openapi-v1.yaml (3 endpoints, 1 schema)\n'));
|
|
1420
|
+
|
|
1421
|
+
// Step 2: Introduce breaking changes
|
|
1422
|
+
console.log(chalk.bold(' Step 2: Introducing breaking changes...'));
|
|
1423
|
+
const changedSpec = JSON.parse(JSON.stringify(baseSpec));
|
|
1424
|
+
changedSpec.info.version = '2.0.0';
|
|
1425
|
+
|
|
1426
|
+
// Breaking: remove endpoint
|
|
1427
|
+
delete changedSpec.paths['/pets/{petId}'];
|
|
1428
|
+
// Breaking: add required parameter
|
|
1429
|
+
changedSpec.paths['/pets'].get.parameters.push(
|
|
1430
|
+
{ name: 'owner_id', in: 'query', required: true, schema: { type: 'string' } }
|
|
1431
|
+
);
|
|
1432
|
+
// Breaking: remove response field
|
|
1433
|
+
delete changedSpec.components.schemas.Pet.properties.tag;
|
|
1434
|
+
// Non-breaking: add endpoint
|
|
1435
|
+
changedSpec.paths['/pets/search'] = {
|
|
1436
|
+
get: {
|
|
1437
|
+
summary: 'Search pets',
|
|
1438
|
+
operationId: 'searchPets',
|
|
1439
|
+
parameters: [{ name: 'q', in: 'query', required: true, schema: { type: 'string' } }],
|
|
1440
|
+
responses: { '200': { description: 'Search results' } },
|
|
1441
|
+
},
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1444
|
+
const changedSpecPath = path.join(tmpDir, 'openapi-v2.yaml');
|
|
1445
|
+
fs.writeFileSync(changedSpecPath, yaml.dump(changedSpec));
|
|
1446
|
+
console.log(chalk.red(' Removed: GET /pets/{petId}'));
|
|
1447
|
+
console.log(chalk.red(' Added required param: owner_id on GET /pets'));
|
|
1448
|
+
console.log(chalk.red(' Removed field: Pet.tag'));
|
|
1449
|
+
console.log(chalk.green(' Added: GET /pets/search'));
|
|
1334
1450
|
console.log('');
|
|
1451
|
+
|
|
1452
|
+
// Step 3: Run Delimit lint
|
|
1453
|
+
console.log(chalk.bold(' Step 3: Running governance check...\n'));
|
|
1454
|
+
try {
|
|
1455
|
+
const result = apiEngine.lint(baseSpecPath, changedSpecPath, { policy: 'strict' });
|
|
1456
|
+
|
|
1457
|
+
if (result && result.summary) {
|
|
1458
|
+
const s = result.summary;
|
|
1459
|
+
const breaking = s.breaking || s.breaking_changes || 0;
|
|
1460
|
+
const total = s.total || s.total_changes || 0;
|
|
1461
|
+
const safe = total - breaking;
|
|
1462
|
+
|
|
1463
|
+
if (breaking > 0) {
|
|
1464
|
+
console.log(chalk.red.bold(` BLOCKED — ${breaking} breaking change(s) detected\n`));
|
|
1465
|
+
} else {
|
|
1466
|
+
console.log(chalk.green.bold(` PASSED — No breaking changes\n`));
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// Show violations
|
|
1470
|
+
const violations = result.violations || [];
|
|
1471
|
+
if (violations.length > 0) {
|
|
1472
|
+
console.log(chalk.bold(' Violations:'));
|
|
1473
|
+
violations.forEach((v, i) => {
|
|
1474
|
+
const icon = v.severity === 'error' ? chalk.red(' BLOCK') : chalk.yellow(' WARN ');
|
|
1475
|
+
console.log(` ${icon} ${v.message}`);
|
|
1476
|
+
if (v.path) console.log(chalk.gray(` ${v.path}`));
|
|
1477
|
+
});
|
|
1478
|
+
console.log('');
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// Semver
|
|
1482
|
+
if (result.semver) {
|
|
1483
|
+
console.log(` Semver: ${chalk.bold(result.semver.bump?.toUpperCase() || 'MAJOR')}`);
|
|
1484
|
+
if (result.semver.next_version) {
|
|
1485
|
+
console.log(` Next version: ${chalk.bold(result.semver.next_version)}`);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// Show safe changes
|
|
1490
|
+
if (safe > 0) {
|
|
1491
|
+
console.log(chalk.green(`\n ${safe} additive change(s) also detected`));
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
} catch (err) {
|
|
1495
|
+
console.log(chalk.yellow(` Lint error: ${err.message}`));
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// Step 4: Show governance gates
|
|
1499
|
+
console.log(chalk.bold('\n Governance Gates:'));
|
|
1500
|
+
console.log(` ${chalk.red('X')} API Lint ${chalk.gray('→ semver → gov_evaluate')}`);
|
|
1501
|
+
console.log(` ${chalk.red('X')} Policy Compliance ${chalk.gray('→ evidence_collect')}`);
|
|
1502
|
+
console.log(` ${chalk.green('+')} Security Audit ${chalk.gray('→ evidence_collect → notify')}`);
|
|
1503
|
+
console.log(` ${chalk.red('X')} Deploy Readiness ${chalk.gray('→ deploy_plan → security_audit')}`);
|
|
1504
|
+
console.log(chalk.red.bold('\n Deploy BLOCKED until all gates pass.\n'));
|
|
1505
|
+
|
|
1506
|
+
// Step 5: Show what would happen with the fix
|
|
1507
|
+
console.log(chalk.bold(' What Delimit does:'));
|
|
1508
|
+
console.log(chalk.gray(' 1. Detects the 3 breaking changes automatically'));
|
|
1509
|
+
console.log(chalk.gray(' 2. Evaluates against your policy (strict/default/relaxed)'));
|
|
1510
|
+
console.log(chalk.gray(' 3. Blocks the deploy via governance gates'));
|
|
1511
|
+
console.log(chalk.gray(' 4. Records evidence for audit trail'));
|
|
1512
|
+
console.log(chalk.gray(' 5. Posts remediation guide on the PR'));
|
|
1513
|
+
console.log(chalk.gray(' 6. Tracks in the ledger for cross-model continuity'));
|
|
1514
|
+
|
|
1515
|
+
console.log(chalk.bold('\n Try it on your project:'));
|
|
1516
|
+
console.log(` ${chalk.green('npx delimit-cli init')} — set up governance`);
|
|
1517
|
+
console.log(` ${chalk.green('npx delimit-cli lint')} — lint your API spec`);
|
|
1518
|
+
console.log(` ${chalk.green('npx delimit-cli setup')} — configure AI assistants\n`);
|
|
1519
|
+
|
|
1520
|
+
// Beta capture (LED-263)
|
|
1521
|
+
try {
|
|
1522
|
+
const betaAns = await inquirer.prompt([{
|
|
1523
|
+
type: 'input',
|
|
1524
|
+
name: 'email',
|
|
1525
|
+
message: chalk.blue('Join the beta? Enter your email (or press Enter to skip):'),
|
|
1526
|
+
}]);
|
|
1527
|
+
if (betaAns.email && betaAns.email.includes('@')) {
|
|
1528
|
+
try {
|
|
1529
|
+
await axios.post('https://delimit.ai/api/subscribe', { email: betaAns.email, source: 'cli-demo' });
|
|
1530
|
+
console.log(chalk.green('\n Thanks! You\'re on the list. We\'ll keep you in the loop.\n'));
|
|
1531
|
+
} catch {
|
|
1532
|
+
console.log(chalk.green('\n Thanks! Visit https://delimit.ai to stay updated.\n'));
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
} catch {}
|
|
1536
|
+
|
|
1537
|
+
// Cleanup
|
|
1538
|
+
try { fs.rmSync(tmpDir, { recursive: true }); } catch {}
|
|
1335
1539
|
});
|
|
1336
1540
|
|
|
1337
1541
|
// Doctor command — verify setup is correct
|
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": "3.14.
|
|
4
|
+
"version": "3.14.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": [
|