delimit-cli 3.14.40 → 3.14.42
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 +118 -0
- package/bin/delimit-setup.js +140 -26
- package/package.json +1 -1
package/bin/delimit-cli.js
CHANGED
|
@@ -1647,6 +1647,124 @@ program
|
|
|
1647
1647
|
} catch {}
|
|
1648
1648
|
});
|
|
1649
1649
|
|
|
1650
|
+
// Resume command — show what was happening last session (STR-047)
|
|
1651
|
+
program
|
|
1652
|
+
.command('resume')
|
|
1653
|
+
.description('Show what you were working on — context from your last session')
|
|
1654
|
+
.action(async () => {
|
|
1655
|
+
console.log(chalk.bold('\n Delimit — Resume Work\n'));
|
|
1656
|
+
|
|
1657
|
+
const DELIMIT_HOME = path.join(os.homedir(), '.delimit');
|
|
1658
|
+
|
|
1659
|
+
// 1. Last session handoff
|
|
1660
|
+
const sessionsDir = path.join(DELIMIT_HOME, 'sessions');
|
|
1661
|
+
if (fs.existsSync(sessionsDir)) {
|
|
1662
|
+
try {
|
|
1663
|
+
const sessions = fs.readdirSync(sessionsDir)
|
|
1664
|
+
.filter(f => f.endsWith('.json'))
|
|
1665
|
+
.sort()
|
|
1666
|
+
.reverse();
|
|
1667
|
+
|
|
1668
|
+
if (sessions.length > 0) {
|
|
1669
|
+
const lastSession = JSON.parse(fs.readFileSync(path.join(sessionsDir, sessions[0]), 'utf-8'));
|
|
1670
|
+
const ts = sessions[0].replace('session_', '').replace('.json', '').replace(/_/g, ' ');
|
|
1671
|
+
console.log(chalk.bold(' Last session:') + chalk.gray(` ${ts}`));
|
|
1672
|
+
if (lastSession.summary) {
|
|
1673
|
+
console.log(chalk.gray(` ${lastSession.summary.substring(0, 200)}`));
|
|
1674
|
+
}
|
|
1675
|
+
if (lastSession.tasks_completed && lastSession.tasks_completed.length > 0) {
|
|
1676
|
+
console.log(chalk.green(` ${lastSession.tasks_completed.length} task(s) completed`));
|
|
1677
|
+
}
|
|
1678
|
+
if (lastSession.pending && lastSession.pending.length > 0) {
|
|
1679
|
+
console.log(chalk.yellow(` ${lastSession.pending.length} item(s) pending`));
|
|
1680
|
+
lastSession.pending.slice(0, 3).forEach(p => {
|
|
1681
|
+
console.log(chalk.gray(` • ${typeof p === 'string' ? p : p.title || JSON.stringify(p).substring(0, 80)}`));
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
console.log('');
|
|
1685
|
+
}
|
|
1686
|
+
} catch {}
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// 2. Open ledger items
|
|
1690
|
+
const ledgerDir = path.join(DELIMIT_HOME, 'ledger');
|
|
1691
|
+
if (fs.existsSync(ledgerDir)) {
|
|
1692
|
+
try {
|
|
1693
|
+
const ledgerFiles = fs.readdirSync(ledgerDir).filter(f => f.endsWith('.json'));
|
|
1694
|
+
let openItems = [];
|
|
1695
|
+
for (const lf of ledgerFiles) {
|
|
1696
|
+
try {
|
|
1697
|
+
const items = JSON.parse(fs.readFileSync(path.join(ledgerDir, lf), 'utf-8'));
|
|
1698
|
+
if (Array.isArray(items)) {
|
|
1699
|
+
openItems.push(...items.filter(i => i.status === 'open'));
|
|
1700
|
+
}
|
|
1701
|
+
} catch {}
|
|
1702
|
+
}
|
|
1703
|
+
if (openItems.length > 0) {
|
|
1704
|
+
// Sort by priority
|
|
1705
|
+
const priorityOrder = { P0: 0, P1: 1, P2: 2 };
|
|
1706
|
+
openItems.sort((a, b) => (priorityOrder[a.priority] || 3) - (priorityOrder[b.priority] || 3));
|
|
1707
|
+
|
|
1708
|
+
console.log(chalk.bold(` Open items: ${openItems.length}`));
|
|
1709
|
+
const p0 = openItems.filter(i => i.priority === 'P0');
|
|
1710
|
+
const p1 = openItems.filter(i => i.priority === 'P1');
|
|
1711
|
+
if (p0.length > 0) {
|
|
1712
|
+
console.log(chalk.red(` ${p0.length} urgent (P0):`));
|
|
1713
|
+
p0.slice(0, 3).forEach(i => console.log(chalk.gray(` ${i.id}: ${i.title.substring(0, 60)}`)));
|
|
1714
|
+
}
|
|
1715
|
+
if (p1.length > 0) {
|
|
1716
|
+
console.log(chalk.yellow(` ${p1.length} important (P1)`));
|
|
1717
|
+
}
|
|
1718
|
+
console.log('');
|
|
1719
|
+
}
|
|
1720
|
+
} catch {}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// 3. Recent memory
|
|
1724
|
+
const memoryDir = path.join(DELIMIT_HOME, 'memory');
|
|
1725
|
+
if (fs.existsSync(memoryDir)) {
|
|
1726
|
+
try {
|
|
1727
|
+
const memFiles = fs.readdirSync(memoryDir)
|
|
1728
|
+
.filter(f => f.endsWith('.json'))
|
|
1729
|
+
.map(f => ({ name: f, mtime: fs.statSync(path.join(memoryDir, f)).mtimeMs }))
|
|
1730
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
1731
|
+
.slice(0, 3);
|
|
1732
|
+
|
|
1733
|
+
if (memFiles.length > 0) {
|
|
1734
|
+
console.log(chalk.bold(' Recent memory:'));
|
|
1735
|
+
for (const mf of memFiles) {
|
|
1736
|
+
try {
|
|
1737
|
+
const mem = JSON.parse(fs.readFileSync(path.join(memoryDir, mf.name), 'utf-8'));
|
|
1738
|
+
const key = mem.key || mf.name.replace('.json', '');
|
|
1739
|
+
const val = (mem.content || mem.value || '').substring(0, 60);
|
|
1740
|
+
console.log(chalk.gray(` ${key}: ${val}`));
|
|
1741
|
+
} catch {}
|
|
1742
|
+
}
|
|
1743
|
+
console.log('');
|
|
1744
|
+
}
|
|
1745
|
+
} catch {}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
// 4. Git context
|
|
1749
|
+
try {
|
|
1750
|
+
const branch = execSync('git branch --show-current 2>/dev/null', { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
1751
|
+
const lastCommit = execSync('git log --oneline -1 2>/dev/null', { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
1752
|
+
const status = execSync('git status --porcelain 2>/dev/null', { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
1753
|
+
const changed = status ? status.split('\n').length : 0;
|
|
1754
|
+
|
|
1755
|
+
console.log(chalk.bold(' Git:'));
|
|
1756
|
+
console.log(chalk.gray(` Branch: ${branch}`));
|
|
1757
|
+
console.log(chalk.gray(` Last: ${lastCommit.substring(0, 60)}`));
|
|
1758
|
+
if (changed > 0) console.log(chalk.yellow(` ${changed} uncommitted file(s)`));
|
|
1759
|
+
console.log('');
|
|
1760
|
+
} catch {}
|
|
1761
|
+
|
|
1762
|
+
// 5. Suggested action
|
|
1763
|
+
console.log(chalk.bold(' Start here:'));
|
|
1764
|
+
console.log(` ${chalk.green('Ask your AI:')} "Check the ledger and work on the highest priority item"`);
|
|
1765
|
+
console.log('');
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1650
1768
|
// Try command — zero-risk demo with Markdown report artifact (LED-264)
|
|
1651
1769
|
program
|
|
1652
1770
|
.command('try')
|
package/bin/delimit-setup.js
CHANGED
|
@@ -322,19 +322,32 @@ async function main() {
|
|
|
322
322
|
const serverDir = path.join(DELIMIT_HOME, 'server');
|
|
323
323
|
const correctEntry = `\n[mcp_servers.delimit]\ncommand = "${python}"\nargs = ["${actualServer}"]\ncwd = "${serverDir}"\n\n[mcp_servers.delimit.env]\nPYTHONPATH = "${serverDir}:${path.join(serverDir, 'ai')}"\n`;
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
325
|
+
// Remove ALL existing delimit MCP entries (prevents duplicates)
|
|
326
|
+
const existed = toml.includes('mcp_servers.delimit');
|
|
327
|
+
const lines = toml.split('\n');
|
|
328
|
+
const cleaned = [];
|
|
329
|
+
let skipBlock = false;
|
|
330
|
+
for (const line of lines) {
|
|
331
|
+
if (line.match(/^\[mcp_servers\.delimit/)) {
|
|
332
|
+
skipBlock = true;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (skipBlock && (line.startsWith('[') || line.trim() === '')) {
|
|
336
|
+
if (line.startsWith('[') && !line.match(/^\[mcp_servers\.delimit/)) {
|
|
337
|
+
skipBlock = false;
|
|
338
|
+
cleaned.push(line);
|
|
339
|
+
}
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (!skipBlock) {
|
|
343
|
+
cleaned.push(line);
|
|
344
|
+
}
|
|
337
345
|
}
|
|
346
|
+
toml = cleaned.join('\n').replace(/\n{3,}/g, '\n\n').trim();
|
|
347
|
+
toml += correctEntry;
|
|
348
|
+
fs.writeFileSync(CODEX_CONFIG, toml, { mode: 0o644 });
|
|
349
|
+
await logp(` ${green('✓')} ${existed ? 'Updated' : 'Added'} Delimit in Codex config`);
|
|
350
|
+
configuredTools.push('Codex');
|
|
338
351
|
} catch (e) {
|
|
339
352
|
log(` ${yellow('!')} Could not configure Codex: ${e.message}`);
|
|
340
353
|
}
|
|
@@ -986,21 +999,122 @@ exit 127
|
|
|
986
999
|
|
|
987
1000
|
log('');
|
|
988
1001
|
|
|
989
|
-
//
|
|
990
|
-
log(
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1002
|
+
// Project scan — show what Delimit already knows (STR-046)
|
|
1003
|
+
log(` ${bold('Your project:')}`);
|
|
1004
|
+
const cwd = process.cwd();
|
|
1005
|
+
let projectFindings = 0;
|
|
1006
|
+
|
|
1007
|
+
// Detect framework
|
|
1008
|
+
const frameworks = [
|
|
1009
|
+
{ file: 'package.json', check: 'express', label: 'Express API' },
|
|
1010
|
+
{ file: 'package.json', check: 'fastify', label: 'Fastify API' },
|
|
1011
|
+
{ file: 'package.json', check: 'next', label: 'Next.js' },
|
|
1012
|
+
{ file: 'package.json', check: '@nestjs', label: 'NestJS' },
|
|
1013
|
+
{ file: 'requirements.txt', check: 'fastapi', label: 'FastAPI' },
|
|
1014
|
+
{ file: 'requirements.txt', check: 'django', label: 'Django' },
|
|
1015
|
+
{ file: 'requirements.txt', check: 'flask', label: 'Flask' },
|
|
1016
|
+
{ file: 'pyproject.toml', check: 'fastapi', label: 'FastAPI' },
|
|
1017
|
+
];
|
|
1018
|
+
for (const fw of frameworks) {
|
|
1019
|
+
const fwPath = path.join(cwd, fw.file);
|
|
1020
|
+
try {
|
|
1021
|
+
if (fs.existsSync(fwPath) && fs.readFileSync(fwPath, 'utf-8').toLowerCase().includes(fw.check)) {
|
|
1022
|
+
await logp(` ${green('✓')} Framework: ${fw.label}`);
|
|
1023
|
+
projectFindings++;
|
|
1024
|
+
break;
|
|
1025
|
+
}
|
|
1026
|
+
} catch {}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// Detect OpenAPI specs
|
|
1030
|
+
const specPatterns = ['openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.json', 'api.yaml'];
|
|
1031
|
+
let specFound = false;
|
|
1032
|
+
for (const sp of specPatterns) {
|
|
1033
|
+
if (fs.existsSync(path.join(cwd, sp))) {
|
|
1034
|
+
await logp(` ${green('✓')} API spec: ${sp}`);
|
|
1035
|
+
specFound = true;
|
|
1036
|
+
projectFindings++;
|
|
1037
|
+
break;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (!specFound) {
|
|
1041
|
+
await logp(` ${dim(' No API spec found — run')} ${blue('delimit-cli lint')} ${dim('to detect one')}`);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// Count tests
|
|
1045
|
+
const testDirs = ['tests', 'test', '__tests__', 'spec'];
|
|
1046
|
+
for (const td of testDirs) {
|
|
1047
|
+
const testPath = path.join(cwd, td);
|
|
1048
|
+
if (fs.existsSync(testPath)) {
|
|
1049
|
+
try {
|
|
1050
|
+
const testFiles = fs.readdirSync(testPath).filter(f => f.includes('test') || f.includes('spec'));
|
|
1051
|
+
if (testFiles.length > 0) {
|
|
1052
|
+
await logp(` ${green('✓')} Tests: ${testFiles.length} test files in ${td}/`);
|
|
1053
|
+
projectFindings++;
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
} catch {}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Check git status
|
|
1061
|
+
try {
|
|
1062
|
+
const gitStatus = execSync('git log --oneline -1 2>/dev/null', { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
1063
|
+
if (gitStatus) {
|
|
1064
|
+
await logp(` ${green('✓')} Git: ${gitStatus.substring(0, 50)}`);
|
|
1065
|
+
projectFindings++;
|
|
1066
|
+
}
|
|
1067
|
+
} catch {}
|
|
1068
|
+
|
|
1069
|
+
// Check for existing .delimit context
|
|
1070
|
+
const ledgerDir = path.join(os.homedir(), '.delimit', 'ledger');
|
|
1071
|
+
if (fs.existsSync(ledgerDir)) {
|
|
1072
|
+
try {
|
|
1073
|
+
const ledgerFiles = fs.readdirSync(ledgerDir).filter(f => f.endsWith('.json'));
|
|
1074
|
+
let totalItems = 0;
|
|
1075
|
+
for (const lf of ledgerFiles) {
|
|
1076
|
+
try {
|
|
1077
|
+
const items = JSON.parse(fs.readFileSync(path.join(ledgerDir, lf), 'utf-8'));
|
|
1078
|
+
if (Array.isArray(items)) totalItems += items.filter(i => i.status === 'open').length;
|
|
1079
|
+
} catch {}
|
|
1080
|
+
}
|
|
1081
|
+
if (totalItems > 0) {
|
|
1082
|
+
await logp(` ${green('✓')} Ledger: ${totalItems} open items across ventures`);
|
|
1083
|
+
projectFindings++;
|
|
1084
|
+
}
|
|
1085
|
+
} catch {}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// Check for sessions
|
|
1089
|
+
const sessionsDir = path.join(os.homedir(), '.delimit', 'sessions');
|
|
1090
|
+
if (fs.existsSync(sessionsDir)) {
|
|
1091
|
+
try {
|
|
1092
|
+
const sessions = fs.readdirSync(sessionsDir).filter(f => f.endsWith('.json'));
|
|
1093
|
+
if (sessions.length > 0) {
|
|
1094
|
+
await logp(` ${green('✓')} Sessions: ${sessions.length} handoff(s) saved`);
|
|
1095
|
+
projectFindings++;
|
|
1096
|
+
}
|
|
1097
|
+
} catch {}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
if (projectFindings === 0) {
|
|
1101
|
+
log(` ${dim(' New project — run')} ${blue('delimit-cli demo')} ${dim('to see governance in action')}`);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
log('');
|
|
1105
|
+
|
|
1106
|
+
// Suggested next action based on findings
|
|
1107
|
+
log(` ${bold('Next action:')}`);
|
|
1108
|
+
if (specFound) {
|
|
1109
|
+
log(` ${green('delimit-cli lint')} — check your API spec for breaking changes`);
|
|
1110
|
+
} else if (projectFindings > 0) {
|
|
1111
|
+
log(` ${green('delimit-cli scan')} — detect API specs and potential issues`);
|
|
1112
|
+
} else {
|
|
1113
|
+
log(` ${green('delimit-cli demo')} — see governance in action (30 seconds)`);
|
|
1114
|
+
}
|
|
1000
1115
|
log('');
|
|
1001
|
-
log(` ${dim('
|
|
1002
|
-
log(` ${dim('
|
|
1003
|
-
log(` ${dim('Agents:')} ${AGENTS_DIR}`);
|
|
1116
|
+
log(` ${dim('Docs: https://delimit.ai/docs')}`);
|
|
1117
|
+
log(` ${dim('Try: https://delimit.ai/try')}`);
|
|
1004
1118
|
log('');
|
|
1005
1119
|
log(` ${bold('Keep Building.')}`);
|
|
1006
1120
|
log('');
|
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.42",
|
|
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": [
|