delimit-cli 4.1.40 → 4.1.41
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/README.md +1 -0
- package/bin/delimit-cli.js +188 -80
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -169,6 +169,7 @@ npx delimit-cli recall --export # Export as markdown
|
|
|
169
169
|
npx delimit-cli forget abc123 # Delete a memory by ID
|
|
170
170
|
npx delimit-cli models # Configure deliberation API keys (BYOK wizard)
|
|
171
171
|
npx delimit-cli models --status # Show current model config
|
|
172
|
+
npx delimit-cli status # Compact dashboard of your Delimit setup
|
|
172
173
|
npx delimit-cli doctor # Check setup health
|
|
173
174
|
npx delimit-cli uninstall --dry-run # Preview removal
|
|
174
175
|
```
|
package/bin/delimit-cli.js
CHANGED
|
@@ -128,6 +128,20 @@ if (process.env.DELIMIT_DEBUG_CONTINUITY === '1') {
|
|
|
128
128
|
console.log('');
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// Helper to format a timestamp as relative time (e.g. "2h ago", "3d ago")
|
|
132
|
+
function _relativeTime(ts) {
|
|
133
|
+
const diff = Date.now() - ts;
|
|
134
|
+
const mins = Math.floor(diff / 60000);
|
|
135
|
+
if (mins < 1) return 'just now';
|
|
136
|
+
if (mins < 60) return mins + 'm ago';
|
|
137
|
+
const hrs = Math.floor(mins / 60);
|
|
138
|
+
if (hrs < 24) return hrs + 'h ago';
|
|
139
|
+
const days = Math.floor(hrs / 24);
|
|
140
|
+
if (days < 30) return days + 'd ago';
|
|
141
|
+
const months = Math.floor(days / 30);
|
|
142
|
+
return months + 'mo ago';
|
|
143
|
+
}
|
|
144
|
+
|
|
131
145
|
// Helper to check if agent is running
|
|
132
146
|
async function checkAgent() {
|
|
133
147
|
try {
|
|
@@ -311,99 +325,193 @@ program
|
|
|
311
325
|
// Status command
|
|
312
326
|
program
|
|
313
327
|
.command('status')
|
|
314
|
-
.description('Show
|
|
328
|
+
.description('Show a compact dashboard of your Delimit setup')
|
|
315
329
|
|
|
316
330
|
.option('--verbose', 'Show detailed status')
|
|
317
331
|
.action(async (options) => {
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
332
|
+
const homedir = os.homedir();
|
|
333
|
+
const delimitHome = path.join(homedir, '.delimit');
|
|
334
|
+
const target = process.cwd();
|
|
335
|
+
|
|
336
|
+
console.log(chalk.bold('\n Delimit Status\n'));
|
|
337
|
+
|
|
338
|
+
// --- Memory stats ---
|
|
339
|
+
const memoryDir = path.join(delimitHome, 'memory');
|
|
340
|
+
let memTotal = 0;
|
|
341
|
+
let memRecent = 0;
|
|
342
|
+
let recentMemories = [];
|
|
343
|
+
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
344
|
+
try {
|
|
345
|
+
const memFiles = fs.readdirSync(memoryDir).filter(f => f.startsWith('mem-') && f.endsWith('.json'));
|
|
346
|
+
memTotal = memFiles.length;
|
|
347
|
+
for (const f of memFiles) {
|
|
348
|
+
try {
|
|
349
|
+
const data = JSON.parse(fs.readFileSync(path.join(memoryDir, f), 'utf-8'));
|
|
350
|
+
const ts = new Date(data.created_at || data.timestamp || data.created || 0).getTime();
|
|
351
|
+
if (ts > oneWeekAgo) memRecent++;
|
|
352
|
+
recentMemories.push({ text: data.text || data.content || '', tags: data.tags || [], ts });
|
|
353
|
+
} catch {}
|
|
354
|
+
}
|
|
355
|
+
recentMemories.sort((a, b) => b.ts - a.ts);
|
|
356
|
+
recentMemories = recentMemories.slice(0, 3);
|
|
357
|
+
} catch {}
|
|
358
|
+
console.log(` Memory: ${chalk.white.bold(memTotal)} memories${memRecent > 0 ? ` (${memRecent} this week)` : ''}`);
|
|
322
359
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
console.log(` Default Mode: ${data.defaultMode}`);
|
|
360
|
+
// --- Governance / Policy ---
|
|
361
|
+
const policyPath = path.join(target, '.delimit', 'policies.yml');
|
|
362
|
+
let policyLabel = chalk.gray('none');
|
|
363
|
+
let hasPolicy = false;
|
|
364
|
+
if (fs.existsSync(policyPath)) {
|
|
365
|
+
hasPolicy = true;
|
|
366
|
+
try {
|
|
367
|
+
const policyContent = yaml.load(fs.readFileSync(policyPath, 'utf-8'));
|
|
368
|
+
const preset = policyContent?.preset || policyContent?.name || 'custom';
|
|
369
|
+
policyLabel = chalk.green(preset + ' policy');
|
|
370
|
+
} catch {
|
|
371
|
+
policyLabel = chalk.green('custom policy');
|
|
336
372
|
}
|
|
337
|
-
|
|
338
|
-
|
|
373
|
+
}
|
|
374
|
+
// Count tracked specs
|
|
375
|
+
const specPatterns = ['openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.yml', 'swagger.json'];
|
|
376
|
+
let specCount = 0;
|
|
377
|
+
const _countSpecs = (dir, depth) => {
|
|
378
|
+
if (depth > 3) return;
|
|
379
|
+
try {
|
|
380
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
381
|
+
if (['node_modules', '.next', 'venv', '.git'].includes(entry.name)) continue;
|
|
382
|
+
const full = path.join(dir, entry.name);
|
|
383
|
+
if (entry.isFile() && specPatterns.includes(entry.name.toLowerCase())) {
|
|
384
|
+
specCount++;
|
|
385
|
+
} else if (entry.isDirectory()) {
|
|
386
|
+
_countSpecs(full, depth + 1);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
} catch {}
|
|
390
|
+
};
|
|
391
|
+
_countSpecs(target, 0);
|
|
392
|
+
const specLabel = specCount > 0 ? `${specCount} spec${specCount > 1 ? 's' : ''} tracked` : chalk.gray('no specs');
|
|
393
|
+
console.log(` Governance: ${policyLabel}${hasPolicy ? ' | ' : ' | '}${specLabel}`);
|
|
394
|
+
|
|
395
|
+
// --- Git hooks ---
|
|
396
|
+
const preCommitPath = path.join(target, '.git', 'hooks', 'pre-commit');
|
|
397
|
+
let hasGitHooks = false;
|
|
398
|
+
try {
|
|
399
|
+
const hookContent = fs.readFileSync(preCommitPath, 'utf-8');
|
|
400
|
+
hasGitHooks = hookContent.includes('delimit');
|
|
401
|
+
} catch {}
|
|
402
|
+
console.log(` Git Hooks: ${hasGitHooks ? chalk.green('pre-commit installed') : chalk.gray('not installed')}`);
|
|
403
|
+
|
|
404
|
+
// --- CI ---
|
|
405
|
+
const workflowPath = path.join(target, '.github', 'workflows', 'api-governance.yml');
|
|
406
|
+
const hasCI = fs.existsSync(workflowPath);
|
|
407
|
+
console.log(` CI: ${hasCI ? chalk.green('GitHub Action active') : chalk.gray('not configured')}`);
|
|
408
|
+
|
|
409
|
+
// --- MCP ---
|
|
410
|
+
const mcpConfigPath = path.join(homedir, '.mcp.json');
|
|
411
|
+
let hasMcp = false;
|
|
412
|
+
let toolCount = 0;
|
|
413
|
+
try {
|
|
414
|
+
const mcpContent = fs.readFileSync(mcpConfigPath, 'utf-8');
|
|
415
|
+
hasMcp = mcpContent.includes('delimit');
|
|
416
|
+
} catch {}
|
|
417
|
+
if (hasMcp) {
|
|
418
|
+
// Count tools from server.py if available
|
|
419
|
+
const serverPyPaths = [
|
|
420
|
+
path.join(delimitHome, 'server', 'ai', 'server.py'),
|
|
421
|
+
path.join(delimitHome, 'server', 'server.py'),
|
|
422
|
+
];
|
|
423
|
+
for (const sp of serverPyPaths) {
|
|
424
|
+
try {
|
|
425
|
+
const serverContent = fs.readFileSync(sp, 'utf-8');
|
|
426
|
+
const toolMatches = serverContent.match(/@mcp\.tool/g);
|
|
427
|
+
if (toolMatches) {
|
|
428
|
+
toolCount = toolMatches.length;
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
} catch {}
|
|
339
432
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
console.log(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
433
|
+
console.log(` MCP: ${chalk.green('connected')}${toolCount > 0 ? ` (${toolCount} tools)` : ''}`);
|
|
434
|
+
} else {
|
|
435
|
+
console.log(` MCP: ${chalk.gray('not configured')}`);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// --- Models ---
|
|
439
|
+
const modelsPath = path.join(delimitHome, 'models.json');
|
|
440
|
+
let modelsLabel = chalk.gray('none configured');
|
|
441
|
+
try {
|
|
442
|
+
const modelsData = JSON.parse(fs.readFileSync(modelsPath, 'utf-8'));
|
|
443
|
+
const modelNames = [];
|
|
444
|
+
for (const [key, val] of Object.entries(modelsData)) {
|
|
445
|
+
if (val && typeof val === 'object' && (val.api_key || val.enabled !== false)) {
|
|
446
|
+
modelNames.push(key.charAt(0).toUpperCase() + key.slice(1));
|
|
349
447
|
}
|
|
350
|
-
} else {
|
|
351
|
-
console.log(' No policies loaded');
|
|
352
448
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
console.log('\n' + chalk.bold('Activity:'));
|
|
356
|
-
console.log(` Audit Log Entries: ${data.auditLogSize}`);
|
|
357
|
-
if (data.lastDecision) {
|
|
358
|
-
const timeSince = Date.now() - new Date(data.lastDecision.timestamp);
|
|
359
|
-
const minutes = Math.floor(timeSince / 60000);
|
|
360
|
-
console.log(` Last Decision: ${minutes} minutes ago (${data.lastDecision.action})`);
|
|
449
|
+
if (modelNames.length > 0) {
|
|
450
|
+
modelsLabel = chalk.white(modelNames.join(' + ')) + chalk.gray(' (BYOK)');
|
|
361
451
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
452
|
+
} catch {}
|
|
453
|
+
console.log(` Models: ${modelsLabel}`);
|
|
454
|
+
|
|
455
|
+
// --- License ---
|
|
456
|
+
const licensePath = path.join(delimitHome, 'license.json');
|
|
457
|
+
let licenseLabel = chalk.gray('Free');
|
|
458
|
+
try {
|
|
459
|
+
const licenseData = JSON.parse(fs.readFileSync(licensePath, 'utf-8'));
|
|
460
|
+
const tier = licenseData.tier || licenseData.plan || 'Free';
|
|
461
|
+
const active = licenseData.status === 'active' || licenseData.valid === true;
|
|
462
|
+
if (tier.toLowerCase() !== 'free') {
|
|
463
|
+
licenseLabel = active ? chalk.green(`${tier} (active)`) : chalk.yellow(`${tier} (${licenseData.status || 'unknown'})`);
|
|
464
|
+
}
|
|
465
|
+
} catch {}
|
|
466
|
+
console.log(` License: ${licenseLabel}`);
|
|
467
|
+
|
|
468
|
+
// --- Recent memories ---
|
|
469
|
+
if (recentMemories.length > 0) {
|
|
470
|
+
console.log(chalk.bold('\n Recent memories:'));
|
|
471
|
+
for (const mem of recentMemories) {
|
|
472
|
+
const ago = _relativeTime(mem.ts);
|
|
473
|
+
const tagStr = mem.tags.length > 0 ? ' ' + chalk.gray(mem.tags.map(t => '#' + t).join(' ')) : '';
|
|
474
|
+
const text = mem.text.length > 55 ? mem.text.slice(0, 55) + '...' : mem.text;
|
|
475
|
+
console.log(` ${chalk.gray('[' + ago + ']')} ${text}${tagStr}`);
|
|
373
476
|
}
|
|
374
477
|
}
|
|
375
|
-
|
|
376
|
-
//
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
// Git hooks
|
|
478
|
+
|
|
479
|
+
// --- Last session ---
|
|
480
|
+
const sessionsDir = path.join(delimitHome, 'sessions');
|
|
380
481
|
try {
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
//
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
482
|
+
const sessFiles = fs.readdirSync(sessionsDir)
|
|
483
|
+
.filter(f => f.startsWith('session_') && f.endsWith('.json'))
|
|
484
|
+
.sort()
|
|
485
|
+
.reverse();
|
|
486
|
+
if (sessFiles.length > 0) {
|
|
487
|
+
const latest = JSON.parse(fs.readFileSync(path.join(sessionsDir, sessFiles[0]), 'utf-8'));
|
|
488
|
+
const summary = latest.summary || latest.description || latest.title || null;
|
|
489
|
+
if (summary) {
|
|
490
|
+
const truncated = summary.length > 60 ? summary.slice(0, 60) + '...' : summary;
|
|
491
|
+
console.log(chalk.bold('\n Last session: ') + truncated);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
} catch {}
|
|
495
|
+
|
|
496
|
+
// --- Governance readiness ---
|
|
497
|
+
const hasSpecs = specCount > 0;
|
|
498
|
+
const checks = [
|
|
499
|
+
{ name: 'API spec', done: hasSpecs },
|
|
500
|
+
{ name: 'Policy', done: hasPolicy },
|
|
501
|
+
{ name: 'CI gate', done: hasCI },
|
|
502
|
+
{ name: 'Git hooks', done: hasGitHooks },
|
|
503
|
+
{ name: 'MCP', done: hasMcp },
|
|
504
|
+
];
|
|
505
|
+
const score = checks.filter(c => c.done).length;
|
|
506
|
+
|
|
507
|
+
console.log(chalk.bold(`\n Governance readiness: ${score}/${checks.length}`));
|
|
508
|
+
console.log(' ' + checks.map(c => c.done ? chalk.green('\u25cf') + ' ' + c.name : chalk.gray('\u25cb') + ' ' + chalk.gray(c.name)).join(' '));
|
|
509
|
+
console.log('');
|
|
510
|
+
|
|
405
511
|
if (options.verbose) {
|
|
406
|
-
console.log(
|
|
512
|
+
console.log(chalk.bold(' Continuity Context:'));
|
|
513
|
+
console.log(formatContinuityReport(continuityContext).split('\n').slice(1).map(line => ' ' + line.trimStart()).join('\n'));
|
|
514
|
+
console.log('');
|
|
407
515
|
}
|
|
408
516
|
});
|
|
409
517
|
|
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.41",
|
|
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": [
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"url": "https://github.com/delimit-ai/delimit-mcp-server",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "4.1.
|
|
10
|
+
"version": "4.1.40",
|
|
11
11
|
"websiteUrl": "https://delimit.ai",
|
|
12
12
|
"packages": [
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"identifier": "delimit-cli",
|
|
16
|
-
"version": "4.1.
|
|
16
|
+
"version": "4.1.40",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
}
|