delimit-cli 4.7.3 → 4.7.5
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 +152 -1
- package/bin/delimit-setup.js +87 -118
- package/bin/delimit.js +10 -25
- package/gateway/ai/backends/governance_bridge.py +52 -0
- package/gateway/ai/backends/repo_bridge.py +12 -0
- package/gateway/ai/backends/tools_infra.py +43 -1
- package/gateway/ai/cli_contract.py +12 -0
- package/gateway/ai/custom_gemini_repl.py +80 -0
- package/gateway/ai/delimit_daemon.py +8 -0
- package/gateway/ai/gemini_vertex_shim.py +38 -0
- package/gateway/ai/license_core.cpython-310-x86_64-linux-gnu.so +0 -0
- package/gateway/ai/release_sync.py +43 -8
- package/gateway/ai/route_daemon.py +98 -0
- package/gateway/ai/server.py +71 -1
- package/gateway/ai/session_phoenix.py +101 -136
- package/gateway/ai/supabase_sync.py +58 -0
- package/gateway/ai/swarm.py +2 -0
- package/gateway/ai/tui.py +143 -0
- package/gateway/core/ci_formatter.py +89 -61
- package/gateway/core/diff_engine_v2.py +208 -627
- package/gateway/core/explainer.py +67 -34
- package/lib/ai-sbom-engine.js +1 -0
- package/lib/auth-setup.js +10 -1
- package/lib/chat-repl.js +247 -0
- package/lib/cross-model-hooks.js +111 -0
- package/lib/timeline-engine.js +60 -0
- package/lib/wrap-engine.js +67 -11
- package/package.json +1 -1
package/bin/delimit-setup.js
CHANGED
|
@@ -8,17 +8,14 @@
|
|
|
8
8
|
* 3. Installs default agents into ~/.claude/agents/
|
|
9
9
|
* 4. Prints next steps
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
11
|
const fs = require('fs');
|
|
13
12
|
const path = require('path');
|
|
14
13
|
const { execSync } = require('child_process');
|
|
15
14
|
const os = require('os');
|
|
16
|
-
|
|
17
15
|
const DELIMIT_HOME = path.join(os.homedir(), '.delimit');
|
|
18
16
|
const MCP_CONFIG = path.join(os.homedir(), '.mcp.json');
|
|
19
17
|
const CLAUDE_DIR = path.join(os.homedir(), '.claude');
|
|
20
18
|
const AGENTS_DIR = path.join(CLAUDE_DIR, 'agents');
|
|
21
|
-
|
|
22
19
|
// Colors
|
|
23
20
|
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
24
21
|
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
@@ -28,12 +25,10 @@ const magenta = (s) => `\x1b[1;35m${s}\x1b[0m`;
|
|
|
28
25
|
const orange = (s) => `\x1b[1;33m${s}\x1b[0m`;
|
|
29
26
|
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
30
27
|
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
31
|
-
|
|
32
28
|
function log(msg) { console.log(msg); }
|
|
33
29
|
function step(n, msg) { log(`\n${blue(`[${n}]`)} ${msg}`); }
|
|
34
30
|
function pause(ms = 150) { return new Promise(r => setTimeout(r, ms)); }
|
|
35
31
|
async function logp(msg, ms = 180) { console.log(msg); await pause(ms); }
|
|
36
|
-
|
|
37
32
|
function findGitDir(startDir) {
|
|
38
33
|
let dir = startDir;
|
|
39
34
|
while (dir !== path.dirname(dir)) {
|
|
@@ -51,7 +46,6 @@ function findGitDir(startDir) {
|
|
|
51
46
|
}
|
|
52
47
|
return null;
|
|
53
48
|
}
|
|
54
|
-
|
|
55
49
|
/**
|
|
56
50
|
* Recursively find OpenAPI/Swagger spec files, ignoring node_modules.
|
|
57
51
|
*/
|
|
@@ -72,7 +66,6 @@ function findSpecFiles(dir, depth = 0) {
|
|
|
72
66
|
} catch {}
|
|
73
67
|
return results;
|
|
74
68
|
}
|
|
75
|
-
|
|
76
69
|
async function main() {
|
|
77
70
|
// Self-update check: ensure we're running the latest version (skip if already re-execed)
|
|
78
71
|
const _pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
@@ -87,7 +80,6 @@ async function main() {
|
|
|
87
80
|
}
|
|
88
81
|
} catch { /* offline or timeout — continue with current version */ }
|
|
89
82
|
}
|
|
90
|
-
|
|
91
83
|
log('');
|
|
92
84
|
log(purple(' ____ ________ ______ _____________'));
|
|
93
85
|
log(purple(' / __ \\/ ____/ / / _/ |/ / _/_ __/'));
|
|
@@ -98,10 +90,8 @@ async function main() {
|
|
|
98
90
|
const _pkgNow = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
99
91
|
log(dim(` v${_pkgNow.version}`));
|
|
100
92
|
log('');
|
|
101
|
-
|
|
102
93
|
// Step 1: Check prerequisites
|
|
103
94
|
step(1, 'Checking prerequisites...');
|
|
104
|
-
|
|
105
95
|
// Python 3.9+
|
|
106
96
|
let python = null;
|
|
107
97
|
for (const cmd of ['python3', 'python']) {
|
|
@@ -119,7 +109,6 @@ async function main() {
|
|
|
119
109
|
log(` ${yellow('✗')} Python 3.9+ not found. Install Python first.`);
|
|
120
110
|
process.exit(1);
|
|
121
111
|
}
|
|
122
|
-
|
|
123
112
|
// Check if Claude Code is available
|
|
124
113
|
let hasClaude = false;
|
|
125
114
|
try {
|
|
@@ -129,7 +118,6 @@ async function main() {
|
|
|
129
118
|
} catch {
|
|
130
119
|
log(` ${yellow('!')} Claude Code not detected — MCP config will still be created`);
|
|
131
120
|
}
|
|
132
|
-
|
|
133
121
|
// Show what we're about to do and ask for confirmation
|
|
134
122
|
log('');
|
|
135
123
|
log(` ${blue('What Delimit will do:')}`);
|
|
@@ -145,7 +133,6 @@ async function main() {
|
|
|
145
133
|
log('');
|
|
146
134
|
log(` ${dim('Undo anytime:')} rm -rf ~/.delimit && delimit uninstall`);
|
|
147
135
|
log('');
|
|
148
|
-
|
|
149
136
|
const inquirerTop = (() => { try { return require('inquirer'); } catch { return null; } })();
|
|
150
137
|
if (inquirerTop && process.stdin.isTTY) {
|
|
151
138
|
try {
|
|
@@ -162,17 +149,14 @@ async function main() {
|
|
|
162
149
|
} catch {}
|
|
163
150
|
}
|
|
164
151
|
log('');
|
|
165
|
-
|
|
166
152
|
// Step 2: Install Delimit MCP server
|
|
167
153
|
step(2, 'Installing Delimit MCP server...');
|
|
168
|
-
|
|
169
154
|
// Create ~/.delimit directory
|
|
170
155
|
fs.mkdirSync(path.join(DELIMIT_HOME, 'server', 'core', 'zero_spec'), { recursive: true });
|
|
171
156
|
fs.mkdirSync(path.join(DELIMIT_HOME, 'server', 'tasks'), { recursive: true });
|
|
172
157
|
fs.mkdirSync(path.join(DELIMIT_HOME, 'deploys'), { recursive: true });
|
|
173
158
|
fs.mkdirSync(path.join(DELIMIT_HOME, 'ledger'), { recursive: true });
|
|
174
159
|
fs.mkdirSync(path.join(DELIMIT_HOME, 'evidence'), { recursive: true });
|
|
175
|
-
|
|
176
160
|
// Copy the gateway core from our bundled copy
|
|
177
161
|
// Skip if server dirs are symlinks (dev machine using gateway source directly)
|
|
178
162
|
const serverAiDir = path.join(DELIMIT_HOME, 'server', 'ai');
|
|
@@ -193,13 +177,11 @@ async function main() {
|
|
|
193
177
|
log(` ${yellow('!')} Could not download. Clone manually: git clone https://github.com/delimit-ai/delimit-gateway.git ~/.delimit/server`);
|
|
194
178
|
}
|
|
195
179
|
}
|
|
196
|
-
|
|
197
180
|
// Copy the MCP server file
|
|
198
181
|
const serverSource = path.join(__dirname, '..', 'mcp-server.py');
|
|
199
182
|
if (fs.existsSync(serverSource)) {
|
|
200
183
|
fs.copyFileSync(serverSource, path.join(DELIMIT_HOME, 'server', 'mcp-server.py'));
|
|
201
184
|
}
|
|
202
|
-
|
|
203
185
|
// Download compiled Pro modules (platform-specific)
|
|
204
186
|
const proDir = path.join(DELIMIT_HOME, 'server', 'ai');
|
|
205
187
|
const pyVer = (() => { try { return execSync(`${python} -c "import sys; print(f'cp{sys.version_info.major}{sys.version_info.minor}')"`, { encoding: 'utf-8' }).trim(); } catch { return 'cp310'; } })();
|
|
@@ -209,7 +191,6 @@ async function main() {
|
|
|
209
191
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
210
192
|
const proVersion = pkg.proModuleVersion || '3.8.2';
|
|
211
193
|
const proUrl = `https://delimit.ai/releases/v${proVersion}/delimit-pro-${artifact}.tar.gz`;
|
|
212
|
-
|
|
213
194
|
try {
|
|
214
195
|
const proTarball = path.join(DELIMIT_HOME, 'pro.tar.gz');
|
|
215
196
|
execSync(`curl -sL "${proUrl}" -o "${proTarball}" --fail`, { stdio: 'pipe', timeout: 30000 });
|
|
@@ -219,13 +200,11 @@ async function main() {
|
|
|
219
200
|
} catch {
|
|
220
201
|
log(` ${dim(' Pro modules not available for ${artifact} — free tools work fine')}`);
|
|
221
202
|
}
|
|
222
|
-
|
|
223
203
|
// Re-copy gateway source AFTER Pro modules to ensure full files aren't overwritten by stubs
|
|
224
204
|
// Skip if dev symlinks are in place
|
|
225
205
|
if (fs.existsSync(gatewaySource) && !isDevSymlink) {
|
|
226
206
|
copyDir(gatewaySource, path.join(DELIMIT_HOME, 'server'));
|
|
227
207
|
}
|
|
228
|
-
|
|
229
208
|
// Remove stale .so binaries that shadow updated .py source files
|
|
230
209
|
// Python loads .so before .py, so old compiled stubs block new source
|
|
231
210
|
const aiDir = path.join(DELIMIT_HOME, 'server', 'ai');
|
|
@@ -244,7 +223,6 @@ async function main() {
|
|
|
244
223
|
}
|
|
245
224
|
} catch { /* ignore cleanup errors */ }
|
|
246
225
|
}
|
|
247
|
-
|
|
248
226
|
// Install Python deps into isolated venv with pinned versions
|
|
249
227
|
log(` ${dim(' Installing Python dependencies...')}`);
|
|
250
228
|
const venvDir = path.join(DELIMIT_HOME, 'venv');
|
|
@@ -276,7 +254,6 @@ async function main() {
|
|
|
276
254
|
log(` ${yellow('!')} pip install failed — run manually: pip install fastmcp pyyaml pydantic packaging pytest`);
|
|
277
255
|
}
|
|
278
256
|
}
|
|
279
|
-
|
|
280
257
|
// LED-1084 week 2: build the content-grounding feature whitelist.
|
|
281
258
|
// Populates ~/.delimit/content/grounding/features.json with every
|
|
282
259
|
// @mcp.tool() entry + every CLI subcommand we ship. Drafters then
|
|
@@ -299,10 +276,8 @@ async function main() {
|
|
|
299
276
|
// which makes the gate strict but doesn't break anything. Customers
|
|
300
277
|
// can rebuild manually later: `python -m ai.content_grounding.features build`.
|
|
301
278
|
}
|
|
302
|
-
|
|
303
279
|
// Step 3: Configure Claude Code MCP
|
|
304
280
|
step(3, 'Configuring Claude Code MCP...');
|
|
305
|
-
|
|
306
281
|
const configuredTools = [];
|
|
307
282
|
let mcpConfig = {};
|
|
308
283
|
if (fs.existsSync(MCP_CONFIG)) {
|
|
@@ -311,14 +286,11 @@ async function main() {
|
|
|
311
286
|
} catch {}
|
|
312
287
|
}
|
|
313
288
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
314
|
-
|
|
315
289
|
const serverPath = path.join(DELIMIT_HOME, 'server', 'ai', 'server.py');
|
|
316
290
|
const serverPathAlt = path.join(DELIMIT_HOME, 'server', 'mcp-server.py');
|
|
317
291
|
const actualServer = fs.existsSync(serverPath) ? serverPath : serverPathAlt;
|
|
318
|
-
|
|
319
292
|
// Always update paths to match this machine's directory structure
|
|
320
293
|
const delimitMcp = {
|
|
321
|
-
type: 'stdio',
|
|
322
294
|
command: python,
|
|
323
295
|
args: [actualServer],
|
|
324
296
|
cwd: path.join(DELIMIT_HOME, 'server'),
|
|
@@ -337,7 +309,6 @@ async function main() {
|
|
|
337
309
|
await logp(` ${green('✓')} Added delimit to ${MCP_CONFIG}`);
|
|
338
310
|
configuredTools.push('Claude Code');
|
|
339
311
|
}
|
|
340
|
-
|
|
341
312
|
// Auto-approve all Delimit tools in Claude Code settings.json
|
|
342
313
|
const CLAUDE_SETTINGS = path.join(CLAUDE_DIR, 'settings.json');
|
|
343
314
|
try {
|
|
@@ -356,7 +327,6 @@ async function main() {
|
|
|
356
327
|
} catch (e) {
|
|
357
328
|
log(` ${yellow('!')} Could not set Claude Code permissions: ${e.message}`);
|
|
358
329
|
}
|
|
359
|
-
|
|
360
330
|
// Step 3b: Configure Codex MCP (if installed)
|
|
361
331
|
const CODEX_CONFIG = path.join(os.homedir(), '.codex', 'config.toml');
|
|
362
332
|
// Create config.toml if .codex dir exists or codex is in PATH
|
|
@@ -379,7 +349,6 @@ async function main() {
|
|
|
379
349
|
const serverDir = path.join(DELIMIT_HOME, 'server');
|
|
380
350
|
// approval_policy = "never" means auto-approve all tools from this server (no per-prompt confirmations)
|
|
381
351
|
const correctEntry = `\n[mcp_servers.delimit]\ncommand = "${python}"\nargs = ["${actualServer}"]\ncwd = "${serverDir}"\napproval_policy = "never"\n\n[mcp_servers.delimit.env]\nPYTHONPATH = "${serverDir}:${path.join(serverDir, 'ai')}"\n`;
|
|
382
|
-
|
|
383
352
|
// Remove ALL existing delimit MCP entries (prevents duplicates)
|
|
384
353
|
const existed = toml.includes('mcp_servers.delimit');
|
|
385
354
|
const lines = toml.split('\n');
|
|
@@ -410,7 +379,6 @@ async function main() {
|
|
|
410
379
|
log(` ${yellow('!')} Could not configure Codex: ${e.message}`);
|
|
411
380
|
}
|
|
412
381
|
}
|
|
413
|
-
|
|
414
382
|
// Step 3c: Configure Cursor MCP (if installed)
|
|
415
383
|
const CURSOR_CONFIG = path.join(os.homedir(), '.cursor', 'mcp.json');
|
|
416
384
|
if (fs.existsSync(path.join(os.homedir(), '.cursor'))) {
|
|
@@ -439,7 +407,6 @@ async function main() {
|
|
|
439
407
|
log(` ${yellow('!')} Could not configure Cursor: ${e.message}`);
|
|
440
408
|
}
|
|
441
409
|
}
|
|
442
|
-
|
|
443
410
|
// Step 3d: Configure Gemini CLI (if installed)
|
|
444
411
|
const GEMINI_DIR = path.join(os.homedir(), '.gemini');
|
|
445
412
|
const GEMINI_CONFIG = path.join(GEMINI_DIR, 'settings.json');
|
|
@@ -480,7 +447,45 @@ async function main() {
|
|
|
480
447
|
log(` ${yellow('!')} Could not configure Gemini CLI: ${e.message}`);
|
|
481
448
|
}
|
|
482
449
|
}
|
|
483
|
-
|
|
450
|
+
// Step 3e: Configure Antigravity CLI (if installed)
|
|
451
|
+
const ANTIGRAVITY_DIR = path.join(os.homedir(), '.gemini', 'antigravity-cli');
|
|
452
|
+
const ANTIGRAVITY_CONFIG = path.join(ANTIGRAVITY_DIR, 'settings.json');
|
|
453
|
+
if (fs.existsSync(ANTIGRAVITY_DIR)) {
|
|
454
|
+
try {
|
|
455
|
+
let antigravityConfig = {};
|
|
456
|
+
if (fs.existsSync(ANTIGRAVITY_CONFIG)) {
|
|
457
|
+
antigravityConfig = JSON.parse(fs.readFileSync(ANTIGRAVITY_CONFIG, 'utf-8'));
|
|
458
|
+
}
|
|
459
|
+
if (!antigravityConfig.mcpServers) antigravityConfig.mcpServers = {};
|
|
460
|
+
const antigravityExisted = !!antigravityConfig.mcpServers.delimit;
|
|
461
|
+
antigravityConfig.mcpServers.delimit = {
|
|
462
|
+
type: 'stdio',
|
|
463
|
+
command: python,
|
|
464
|
+
args: [actualServer],
|
|
465
|
+
cwd: path.join(DELIMIT_HOME, 'server'),
|
|
466
|
+
env: { PYTHONPATH: path.join(DELIMIT_HOME, 'server') }
|
|
467
|
+
};
|
|
468
|
+
if (!antigravityConfig.general) antigravityConfig.general = {};
|
|
469
|
+
if (!antigravityConfig.general.defaultApprovalMode) {
|
|
470
|
+
antigravityConfig.general.defaultApprovalMode = 'auto_edit';
|
|
471
|
+
}
|
|
472
|
+
fs.writeFileSync(ANTIGRAVITY_CONFIG, JSON.stringify(antigravityConfig, null, 2));
|
|
473
|
+
if (antigravityExisted) {
|
|
474
|
+
await logp(` ${green('✓')} Updated Delimit paths in Antigravity CLI config`);
|
|
475
|
+
configuredTools.push('Antigravity CLI');
|
|
476
|
+
} else {
|
|
477
|
+
await logp(` ${green('✓')} Added delimit to Antigravity CLI (${ANTIGRAVITY_CONFIG})`);
|
|
478
|
+
configuredTools.push('Antigravity CLI');
|
|
479
|
+
}
|
|
480
|
+
// Add governance instructions
|
|
481
|
+
if (!antigravityConfig.customInstructions || !antigravityConfig.customInstructions.includes('delimit_ledger_context')) {
|
|
482
|
+
antigravityConfig.customInstructions = 'Delimit MCP is installed. On every session start: (1) call delimit_ledger_context for open tasks, (2) call delimit_gov_health, (3) call delimit_inbox_daemon(action="start") to start email polling. If new project, call delimit_scan. Track all work via the ledger. IMPORTANT: Always auto-trigger delimit_social_post and delimit_notify when drafting content or identifying actions. Never ask permission — draft and send, the founder reviews via email.';
|
|
483
|
+
fs.writeFileSync(ANTIGRAVITY_CONFIG, JSON.stringify(antigravityConfig, null, 2));
|
|
484
|
+
}
|
|
485
|
+
} catch (e) {
|
|
486
|
+
log(` ${yellow('!')} Could not configure Antigravity CLI: ${e.message}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
484
489
|
// Checkpoint: MCP is configured, now ask before modifying project files
|
|
485
490
|
log('');
|
|
486
491
|
log(` ${green('✓')} MCP server installed and configured`);
|
|
@@ -490,7 +495,6 @@ async function main() {
|
|
|
490
495
|
log(` • Update CLAUDE.md with Delimit instructions`);
|
|
491
496
|
log(` • Optional: governance wrapping + hooks`);
|
|
492
497
|
log('');
|
|
493
|
-
|
|
494
498
|
const inquirerMid = (() => { try { return require('inquirer'); } catch { return null; } })();
|
|
495
499
|
if (inquirerMid && process.stdin.isTTY) {
|
|
496
500
|
try {
|
|
@@ -507,12 +511,9 @@ async function main() {
|
|
|
507
511
|
}
|
|
508
512
|
} catch {}
|
|
509
513
|
}
|
|
510
|
-
|
|
511
514
|
// Step 4: Install default agents
|
|
512
515
|
step(4, 'Installing governance agents...');
|
|
513
|
-
|
|
514
516
|
fs.mkdirSync(AGENTS_DIR, { recursive: true });
|
|
515
|
-
|
|
516
517
|
const agents = {
|
|
517
518
|
'lint.md': `---
|
|
518
519
|
name: lint
|
|
@@ -528,9 +529,7 @@ tools:
|
|
|
528
529
|
- mcp__delimit__delimit_impact
|
|
529
530
|
- mcp__delimit__delimit_ledger
|
|
530
531
|
---
|
|
531
|
-
|
|
532
532
|
# Lint Agent
|
|
533
|
-
|
|
534
533
|
Run API governance checks. Use delimit_lint to compare specs, delimit_policy to check rules, delimit_impact for downstream analysis.
|
|
535
534
|
`,
|
|
536
535
|
'engineering.md': `---
|
|
@@ -548,9 +547,7 @@ tools:
|
|
|
548
547
|
- mcp__delimit__delimit_test_generate
|
|
549
548
|
- mcp__delimit__delimit_test_coverage
|
|
550
549
|
---
|
|
551
|
-
|
|
552
550
|
# Engineering Agent
|
|
553
|
-
|
|
554
551
|
Execute code directives. Use delimit_test_coverage to verify coverage targets. Use delimit_lint to check API compatibility after changes.
|
|
555
552
|
`,
|
|
556
553
|
'governance.md': `---
|
|
@@ -570,13 +567,10 @@ tools:
|
|
|
570
567
|
- mcp__delimit__delimit_repo_analyze
|
|
571
568
|
- mcp__delimit__delimit_repo_config_validate
|
|
572
569
|
---
|
|
573
|
-
|
|
574
570
|
# Governance Agent
|
|
575
|
-
|
|
576
571
|
Run full governance compliance checks. Verify security, policy compliance, evidence collection, and repo health.
|
|
577
572
|
`
|
|
578
573
|
};
|
|
579
|
-
|
|
580
574
|
let installed = 0;
|
|
581
575
|
for (const [filename, content] of Object.entries(agents)) {
|
|
582
576
|
const agentPath = path.join(AGENTS_DIR, filename);
|
|
@@ -586,7 +580,6 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
586
580
|
}
|
|
587
581
|
}
|
|
588
582
|
await logp(` ${green('✓')} ${installed} agents installed (${Object.keys(agents).length - installed} already existed)`);
|
|
589
|
-
|
|
590
583
|
// Step 4b: Install Git hooks if inside a git repository
|
|
591
584
|
const gitDir = findGitDir(process.cwd());
|
|
592
585
|
if (gitDir) {
|
|
@@ -621,10 +614,8 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
621
614
|
} else {
|
|
622
615
|
log(` ${dim(' Not inside a git repo — git hooks will be installed on next delimit setup inside a repo')}`);
|
|
623
616
|
}
|
|
624
|
-
|
|
625
617
|
// Step 5: Create/update CLAUDE.md and platform instruction files
|
|
626
618
|
step(5, 'Setting up AI instruction files...');
|
|
627
|
-
|
|
628
619
|
const claudeMd = path.join(os.homedir(), 'CLAUDE.md');
|
|
629
620
|
const claudeResult = upsertDelimitSection(claudeMd);
|
|
630
621
|
if (claudeResult.action === 'created') {
|
|
@@ -636,7 +627,6 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
636
627
|
} else {
|
|
637
628
|
log(` ${dim(' CLAUDE.md already up to date')}`);
|
|
638
629
|
}
|
|
639
|
-
|
|
640
630
|
// Codex instructions: AGENTS.md is the file Codex CLI auto-loads
|
|
641
631
|
// ("from CWD up to the root", per Codex binary spec). The previous
|
|
642
632
|
// ~/.codex/instructions.md write was dead code — Codex never read it.
|
|
@@ -660,7 +650,6 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
660
650
|
log(` ${dim(' AGENTS.md already up to date')}`);
|
|
661
651
|
}
|
|
662
652
|
}
|
|
663
|
-
|
|
664
653
|
// Gemini CLI: GEMINI.md is the auto-loaded instruction file
|
|
665
654
|
// (~/.gemini/GEMINI.md is the user-global tier per Gemini CLI bundle).
|
|
666
655
|
// LED-1399: install when Gemini CLI is present so governance triggers
|
|
@@ -685,7 +674,27 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
685
674
|
log(` ${dim(' GEMINI.md already up to date')}`);
|
|
686
675
|
}
|
|
687
676
|
}
|
|
688
|
-
|
|
677
|
+
// Antigravity CLI: ANTIGRAVITY.md is the auto-loaded instruction file.
|
|
678
|
+
// We install it when antigravity/agy is present, or when ~/.gemini/antigravity-cli exists.
|
|
679
|
+
const antigravityHome = path.join(os.homedir(), '.gemini', 'antigravity-cli');
|
|
680
|
+
let hasAntigravity = fs.existsSync(antigravityHome);
|
|
681
|
+
if (!hasAntigravity) {
|
|
682
|
+
try { execSync('which agy 2>/dev/null', { stdio: 'pipe' }); hasAntigravity = true; } catch {}
|
|
683
|
+
try { execSync('which antigravity 2>/dev/null', { stdio: 'pipe' }); hasAntigravity = true; } catch {}
|
|
684
|
+
}
|
|
685
|
+
if (hasAntigravity) {
|
|
686
|
+
const antigravityMd = path.join(os.homedir(), 'ANTIGRAVITY.md');
|
|
687
|
+
const antigravityResult = upsertDelimitSection(antigravityMd);
|
|
688
|
+
if (antigravityResult.action === 'created') {
|
|
689
|
+
await logp(` ${green('✓')} Created ${antigravityMd} (Antigravity CLI instructions)`);
|
|
690
|
+
} else if (antigravityResult.action === 'updated') {
|
|
691
|
+
await logp(` ${green('✓')} Updated Delimit section in ${antigravityMd}`);
|
|
692
|
+
} else if (antigravityResult.action === 'appended') {
|
|
693
|
+
await logp(` ${green('✓')} Appended Delimit section to ${antigravityMd} (user content preserved)`);
|
|
694
|
+
} else {
|
|
695
|
+
log(` ${dim(' ANTIGRAVITY.md already up to date')}`);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
689
698
|
// Cursor rules (legacy .cursorrules + new .cursor/rules/ directory)
|
|
690
699
|
const cursorRules = path.join(os.homedir(), '.cursorrules');
|
|
691
700
|
if (fs.existsSync(path.join(os.homedir(), '.cursor'))) {
|
|
@@ -704,7 +713,6 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
704
713
|
log(` ${dim(' Could not install .cursor/rules: ' + e.message)}`);
|
|
705
714
|
}
|
|
706
715
|
}
|
|
707
|
-
|
|
708
716
|
// Silent: auto-detect API keys and configure models.json (no output)
|
|
709
717
|
const modelsPath = path.join(DELIMIT_HOME, 'models.json');
|
|
710
718
|
if (!fs.existsSync(modelsPath)) {
|
|
@@ -726,7 +734,6 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
726
734
|
fs.writeFileSync(modelsPath, JSON.stringify(models, null, 2));
|
|
727
735
|
}
|
|
728
736
|
}
|
|
729
|
-
|
|
730
737
|
// Step 6: Governance wrapping (shims)
|
|
731
738
|
step(6, 'Governance wrapping...');
|
|
732
739
|
log('');
|
|
@@ -746,18 +753,14 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
746
753
|
log(` ${dim('Adds ~/.delimit/shims to your shell PATH.')}`);
|
|
747
754
|
log(` ${dim('Disable anytime: delimit shims disable')}`);
|
|
748
755
|
log('');
|
|
749
|
-
|
|
750
756
|
// Check if shims already installed
|
|
751
757
|
const shimsDir = path.join(DELIMIT_HOME, 'shims');
|
|
752
758
|
const shimsInstalled = fs.existsSync(shimsDir) && fs.readdirSync(shimsDir).length > 0;
|
|
753
|
-
|
|
754
759
|
let enableShims = shimsInstalled; // Auto-yes if already installed (just update)
|
|
755
|
-
|
|
756
760
|
if (!shimsInstalled) {
|
|
757
761
|
// First install — prompt
|
|
758
762
|
const inquirer = (() => { try { return require('inquirer'); } catch { return null; } })();
|
|
759
763
|
enableShims = true;
|
|
760
|
-
|
|
761
764
|
if (inquirer && process.stdin.isTTY) {
|
|
762
765
|
try {
|
|
763
766
|
const answer = await inquirer.prompt([{
|
|
@@ -772,11 +775,9 @@ Run full governance compliance checks. Verify security, policy compliance, evide
|
|
|
772
775
|
}
|
|
773
776
|
}
|
|
774
777
|
}
|
|
775
|
-
|
|
776
778
|
if (enableShims) {
|
|
777
779
|
// Create/update shims with latest template
|
|
778
780
|
fs.mkdirSync(shimsDir, { recursive: true });
|
|
779
|
-
|
|
780
781
|
const shimTemplate = (toolName, displayName) => `#!/bin/sh
|
|
781
782
|
# Delimit Governance Shim for ${displayName}
|
|
782
783
|
PURPLE='\\033[35m'; MAGENTA='\\033[91m'; ORANGE='\\033[33m'; GREEN='\\033[32m'
|
|
@@ -792,6 +793,14 @@ if [ "$DELIMIT_WRAPPED" = "true" ] || [ ! -t 1 ]; then
|
|
|
792
793
|
for c in /usr/bin/${toolName} /usr/local/bin/${toolName} $HOME/.local/bin/${toolName}; do
|
|
793
794
|
[ -x "$c" ] && exec "$c" "$@"
|
|
794
795
|
done
|
|
796
|
+
# Fallback checks for Antigravity/agy compatibility
|
|
797
|
+
if [ "${toolName}" = "antigravity" ] || [ "${toolName}" = "agy" ]; then
|
|
798
|
+
for alt in agy antigravity; do
|
|
799
|
+
for c in /usr/bin/\$alt /usr/local/bin/\$alt \$HOME/.local/bin/\$alt; do
|
|
800
|
+
[ -x "\$c" ] && exec "\$c" "\$@"
|
|
801
|
+
done
|
|
802
|
+
done
|
|
803
|
+
fi
|
|
795
804
|
fi
|
|
796
805
|
# Record session start for exit screen
|
|
797
806
|
SESSION_START=\$(date +%s)
|
|
@@ -827,7 +836,6 @@ printf " \${MAGENTA}\${BOLD}[Delimit]\${RESET} \${MAGENTA}═══════
|
|
|
827
836
|
sleep 0.08
|
|
828
837
|
printf " \${GREEN}\${BOLD}[Delimit]\${RESET} \${GREEN}✓ Allowed\${RESET}\\n"
|
|
829
838
|
echo ""
|
|
830
|
-
|
|
831
839
|
# --- Exit screen: session summary after AI exits ---
|
|
832
840
|
delimit_exit_screen() {
|
|
833
841
|
_EXIT_CODE=\$1
|
|
@@ -911,37 +919,46 @@ delimit_exit_screen() {
|
|
|
911
919
|
printf " \${MAGENTA}\${BOLD}[Delimit]\${RESET} \${MAGENTA}═══════════════════════════════════════════\${RESET}\\n"
|
|
912
920
|
echo ""
|
|
913
921
|
}
|
|
914
|
-
|
|
915
922
|
# Run real binary and show exit screen (replaces exec to allow post-exit code)
|
|
916
923
|
delimit_run_and_exit() {
|
|
917
924
|
"\$@"
|
|
918
925
|
_RC=\$?
|
|
919
|
-
delimit_exit_screen \$_RC
|
|
926
|
+
[ "$DELIMIT_QUIET" != "true" ] && delimit_exit_screen \$_RC
|
|
920
927
|
exit \$_RC
|
|
921
928
|
}
|
|
922
|
-
|
|
923
929
|
# Find real binary by stripping shim dir from PATH.
|
|
924
930
|
# We rely on PATH ordering ($HOME/.delimit/shims first) — no rename hack,
|
|
925
931
|
# no race with npm reinstalls. Previously we used mv tool→tool-real + cp shim,
|
|
926
932
|
# which broke whenever npm/brew clobbered the binary mid-operation.
|
|
927
933
|
REAL=$(PATH=$(echo "$PATH" | tr ':' '\\n' | grep -v '.delimit/shims' | tr '\\n' ':') command -v ${toolName} 2>/dev/null)
|
|
934
|
+
if [ -z "$REAL" ]; then
|
|
935
|
+
if [ "${toolName}" = "antigravity" ] || [ "${toolName}" = "agy" ]; then
|
|
936
|
+
REAL=$(PATH=$(echo "$PATH" | tr ':' '\\n' | grep -v '.delimit/shims' | tr '\\n' ':') command -v agy 2>/dev/null || PATH=$(echo "$PATH" | tr ':' '\\n' | grep -v '.delimit/shims' | tr '\\n' ':') command -v antigravity 2>/dev/null)
|
|
937
|
+
fi
|
|
938
|
+
fi
|
|
928
939
|
[ -x "$REAL" ] && delimit_run_and_exit "$REAL" "$@"
|
|
929
940
|
echo "[Delimit] ${toolName} not found in PATH" >&2
|
|
930
941
|
case "${toolName}" in
|
|
931
|
-
claude)
|
|
932
|
-
codex)
|
|
933
|
-
gemini)
|
|
934
|
-
|
|
942
|
+
claude) echo " Install: npm install -g @anthropic-ai/claude-code" >&2 ;;
|
|
943
|
+
codex) echo " Install: npm install -g @openai/codex" >&2 ;;
|
|
944
|
+
gemini) echo " Install: npm install -g @google/gemini-cli" >&2 ;;
|
|
945
|
+
antigravity) echo " Install: npm install -g @google-deepmind/antigravity" >&2 ;;
|
|
946
|
+
agy) echo " Install: npm install -g @google-deepmind/antigravity" >&2 ;;
|
|
947
|
+
*) echo " Install ${toolName} first" >&2 ;;
|
|
935
948
|
esac
|
|
936
949
|
exit 127
|
|
937
950
|
`;
|
|
938
|
-
|
|
939
|
-
|
|
951
|
+
for (const [tool, display] of [
|
|
952
|
+
['claude', 'Claude'],
|
|
953
|
+
['codex', 'Codex'],
|
|
954
|
+
['gemini', 'Gemini CLI'],
|
|
955
|
+
['antigravity', 'Antigravity'],
|
|
956
|
+
['agy', 'Antigravity']
|
|
957
|
+
]) {
|
|
940
958
|
const shimPath = path.join(shimsDir, tool);
|
|
941
959
|
fs.writeFileSync(shimPath, shimTemplate(tool, display));
|
|
942
960
|
fs.chmodSync(shimPath, '755');
|
|
943
961
|
}
|
|
944
|
-
|
|
945
962
|
// Governance is enforced via PATH ordering — $HOME/.delimit/shims
|
|
946
963
|
// is prepended to PATH (see below), so `claude`/`codex`/`gemini`
|
|
947
964
|
// resolve to our shim first, and the shim then PATH-strips itself
|
|
@@ -953,7 +970,6 @@ exit 127
|
|
|
953
970
|
// symlink), leaving users with "[Delimit] claude not found in PATH"
|
|
954
971
|
// when the rename ran but the shim copy failed or the symlink got
|
|
955
972
|
// re-created mid-operation. PATH ordering is the durable contract.
|
|
956
|
-
|
|
957
973
|
// Add to PATH in shell rc files (create if missing)
|
|
958
974
|
const pathLine = `export PATH="${shimsDir}:$PATH" # Delimit governance wrapping`;
|
|
959
975
|
let pathWritten = false;
|
|
@@ -987,13 +1003,11 @@ exit 127
|
|
|
987
1003
|
fs.writeFileSync(profileD, `# Delimit governance wrapping\n${pathLine}\n`);
|
|
988
1004
|
}
|
|
989
1005
|
} catch { /* not writable, skip */ }
|
|
990
|
-
|
|
991
1006
|
if (shimsInstalled) {
|
|
992
1007
|
await logp(` ${green('✓')} Governance shims updated`);
|
|
993
1008
|
} else {
|
|
994
1009
|
log(` ${green('✓')} Governance wrapping enabled`);
|
|
995
1010
|
}
|
|
996
|
-
|
|
997
1011
|
// Check if shims are already in current PATH
|
|
998
1012
|
const currentPath = process.env.PATH || '';
|
|
999
1013
|
if (!currentPath.includes('.delimit/shims')) {
|
|
@@ -1010,20 +1024,16 @@ exit 127
|
|
|
1010
1024
|
log(` ${dim(' Skipped. Enable later: delimit shims enable')}`);
|
|
1011
1025
|
}
|
|
1012
1026
|
log('');
|
|
1013
|
-
|
|
1014
1027
|
// Step 7: Install cross-model governance hooks (LED-202)
|
|
1015
1028
|
step(7, 'Installing AI assistant hooks...');
|
|
1016
|
-
|
|
1017
1029
|
try {
|
|
1018
1030
|
const crossModelHooks = require('../lib/cross-model-hooks');
|
|
1019
1031
|
const hookConfig = crossModelHooks.loadHookConfig();
|
|
1020
1032
|
const detected = crossModelHooks.detectAITools();
|
|
1021
|
-
|
|
1022
1033
|
if (detected.length === 0) {
|
|
1023
1034
|
log(` ${dim(' No AI assistants detected -- hooks will be installed when tools are found')}`);
|
|
1024
1035
|
} else {
|
|
1025
1036
|
log(` ${dim(' Detected: ' + detected.map(t => t.name).join(', '))}`);
|
|
1026
|
-
|
|
1027
1037
|
// Install hooks (auto-accept in non-interactive or prompt if TTY)
|
|
1028
1038
|
let installHooks = true;
|
|
1029
1039
|
const inq = (() => { try { return require('inquirer'); } catch { return null; } })();
|
|
@@ -1040,7 +1050,6 @@ exit 127
|
|
|
1040
1050
|
installHooks = true;
|
|
1041
1051
|
}
|
|
1042
1052
|
}
|
|
1043
|
-
|
|
1044
1053
|
if (installHooks) {
|
|
1045
1054
|
for (const tool of detected) {
|
|
1046
1055
|
const result = crossModelHooks.installHooksForTool(tool, hookConfig);
|
|
@@ -1058,10 +1067,8 @@ exit 127
|
|
|
1058
1067
|
log(` ${dim(' Hook installation skipped: ' + e.message)}`);
|
|
1059
1068
|
}
|
|
1060
1069
|
log('');
|
|
1061
|
-
|
|
1062
1070
|
// Step 8: Local dashboard API server
|
|
1063
1071
|
step(8, 'Local dashboard API...');
|
|
1064
|
-
|
|
1065
1072
|
const localServerPath = path.join(DELIMIT_HOME, 'server', 'ai', 'local_server.py');
|
|
1066
1073
|
if (fs.existsSync(localServerPath)) {
|
|
1067
1074
|
log(` ${green('✓')} Local API server available on port 7823`);
|
|
@@ -1071,10 +1078,8 @@ exit 127
|
|
|
1071
1078
|
log(` ${dim(' Local API server not found — dashboard will use cloud sync')}`);
|
|
1072
1079
|
}
|
|
1073
1080
|
log('');
|
|
1074
|
-
|
|
1075
1081
|
// Step 9: Post-install config validation (LED-098)
|
|
1076
1082
|
step(9, 'Validating config integrity...');
|
|
1077
|
-
|
|
1078
1083
|
let validationIssues = 0;
|
|
1079
1084
|
const configFiles = [
|
|
1080
1085
|
{ path: MCP_CONFIG, name: 'Claude Code', format: 'json' },
|
|
@@ -1082,7 +1087,6 @@ exit 127
|
|
|
1082
1087
|
{ path: CURSOR_CONFIG, name: 'Cursor', format: 'json' },
|
|
1083
1088
|
{ path: GEMINI_CONFIG, name: 'Gemini CLI', format: 'json' },
|
|
1084
1089
|
];
|
|
1085
|
-
|
|
1086
1090
|
for (const cfg of configFiles) {
|
|
1087
1091
|
if (!fs.existsSync(cfg.path)) continue;
|
|
1088
1092
|
try {
|
|
@@ -1096,7 +1100,6 @@ exit 127
|
|
|
1096
1100
|
const cmd = delimitEntry.command || '';
|
|
1097
1101
|
const args = delimitEntry.args || [];
|
|
1098
1102
|
const serverArg = args[0] || '';
|
|
1099
|
-
|
|
1100
1103
|
// Check command is python (not arbitrary binary)
|
|
1101
1104
|
if (!cmd.includes('python') && !cmd.includes('venv')) {
|
|
1102
1105
|
log(` ${yellow('⚠')} ${cfg.name}: delimit command is not python: ${cmd}`);
|
|
@@ -1130,7 +1133,6 @@ exit 127
|
|
|
1130
1133
|
validationIssues++;
|
|
1131
1134
|
}
|
|
1132
1135
|
}
|
|
1133
|
-
|
|
1134
1136
|
// Verify server file exists and is our code
|
|
1135
1137
|
if (fs.existsSync(actualServer)) {
|
|
1136
1138
|
const serverContent = fs.readFileSync(actualServer, 'utf-8').substring(0, 500);
|
|
@@ -1141,7 +1143,6 @@ exit 127
|
|
|
1141
1143
|
validationIssues++;
|
|
1142
1144
|
}
|
|
1143
1145
|
}
|
|
1144
|
-
|
|
1145
1146
|
// Check directory permissions
|
|
1146
1147
|
try {
|
|
1147
1148
|
const stat = fs.statSync(DELIMIT_HOME);
|
|
@@ -1153,24 +1154,20 @@ exit 127
|
|
|
1153
1154
|
log(` ${green('✓')} Directory permissions OK`);
|
|
1154
1155
|
}
|
|
1155
1156
|
} catch {}
|
|
1156
|
-
|
|
1157
1157
|
if (validationIssues === 0) {
|
|
1158
1158
|
log(` ${green('✓')} All config validations passed`);
|
|
1159
1159
|
} else {
|
|
1160
1160
|
log(` ${yellow(`⚠ ${validationIssues} issue(s) found — review above`)}`);
|
|
1161
1161
|
}
|
|
1162
1162
|
log('');
|
|
1163
|
-
|
|
1164
1163
|
// Step 10: Auto-detect OpenAPI specs
|
|
1165
1164
|
step(10, 'Scanning for API specs...');
|
|
1166
|
-
|
|
1167
1165
|
let detectedSpecs = [];
|
|
1168
1166
|
try {
|
|
1169
1167
|
const { minimatch } = (() => { try { return require('minimatch'); } catch { return { minimatch: null }; } })();
|
|
1170
1168
|
// Simple recursive glob for spec files
|
|
1171
1169
|
detectedSpecs = findSpecFiles(process.cwd());
|
|
1172
1170
|
} catch {}
|
|
1173
|
-
|
|
1174
1171
|
if (detectedSpecs.length > 0) {
|
|
1175
1172
|
log(` ${green('✓')} Found ${detectedSpecs.length} API spec(s):`);
|
|
1176
1173
|
detectedSpecs.forEach(s => log(` ${s}`));
|
|
@@ -1180,10 +1177,8 @@ exit 127
|
|
|
1180
1177
|
log(` ${dim(' No OpenAPI/Swagger specs found in current directory')}`);
|
|
1181
1178
|
}
|
|
1182
1179
|
log('');
|
|
1183
|
-
|
|
1184
1180
|
// Step 11: Social target scanning config
|
|
1185
1181
|
step(11, 'Configuring social target scanner...');
|
|
1186
|
-
|
|
1187
1182
|
const socialConfigPath = path.join(DELIMIT_HOME, 'social_target_config.json');
|
|
1188
1183
|
const socialDefaultConfig = {
|
|
1189
1184
|
platforms: {
|
|
@@ -1199,7 +1194,6 @@ exit 127
|
|
|
1199
1194
|
scan_limit: 10,
|
|
1200
1195
|
min_engagement: { score: 1, comments: 2 },
|
|
1201
1196
|
};
|
|
1202
|
-
|
|
1203
1197
|
if (!fs.existsSync(socialConfigPath)) {
|
|
1204
1198
|
fs.mkdirSync(path.dirname(socialConfigPath), { recursive: true });
|
|
1205
1199
|
fs.writeFileSync(socialConfigPath, JSON.stringify(socialDefaultConfig, null, 2) + '\n');
|
|
@@ -1207,7 +1201,6 @@ exit 127
|
|
|
1207
1201
|
} else {
|
|
1208
1202
|
log(` ${dim(' Config already exists:')} ${socialConfigPath}`);
|
|
1209
1203
|
}
|
|
1210
|
-
|
|
1211
1204
|
// Auto-detect available platforms
|
|
1212
1205
|
const detectedPlatforms = {};
|
|
1213
1206
|
// HN and Dev.to are always available (public APIs)
|
|
@@ -1228,13 +1221,11 @@ exit 127
|
|
|
1228
1221
|
} else {
|
|
1229
1222
|
detectedPlatforms['x'] = 'unavailable (no API key)';
|
|
1230
1223
|
}
|
|
1231
|
-
|
|
1232
1224
|
for (const [plat, status] of Object.entries(detectedPlatforms)) {
|
|
1233
1225
|
const icon = status.startsWith('available') ? green('\u2713') : yellow('\u2717');
|
|
1234
1226
|
log(` ${icon} ${plat}: ${dim(status)}`);
|
|
1235
1227
|
}
|
|
1236
1228
|
log('');
|
|
1237
|
-
|
|
1238
1229
|
// Step 12: Done
|
|
1239
1230
|
step(12, 'Done!');
|
|
1240
1231
|
log('');
|
|
@@ -1245,14 +1236,11 @@ exit 127
|
|
|
1245
1236
|
for (const t of tools) {
|
|
1246
1237
|
log(` ${green('✓')} ${t}`);
|
|
1247
1238
|
}
|
|
1248
|
-
|
|
1249
1239
|
log('');
|
|
1250
|
-
|
|
1251
1240
|
// Project scan — show what Delimit already knows (STR-046)
|
|
1252
1241
|
log(` ${bold('Your project:')}`);
|
|
1253
1242
|
const cwd = process.cwd();
|
|
1254
1243
|
let projectFindings = 0;
|
|
1255
|
-
|
|
1256
1244
|
// Detect framework
|
|
1257
1245
|
const frameworks = [
|
|
1258
1246
|
{ file: 'package.json', check: 'express', label: 'Express API' },
|
|
@@ -1274,7 +1262,6 @@ exit 127
|
|
|
1274
1262
|
}
|
|
1275
1263
|
} catch {}
|
|
1276
1264
|
}
|
|
1277
|
-
|
|
1278
1265
|
// Detect OpenAPI specs
|
|
1279
1266
|
const specPatterns = ['openapi.yaml', 'openapi.yml', 'openapi.json', 'swagger.yaml', 'swagger.json', 'api.yaml'];
|
|
1280
1267
|
let specFound = false;
|
|
@@ -1289,7 +1276,6 @@ exit 127
|
|
|
1289
1276
|
if (!specFound) {
|
|
1290
1277
|
await logp(` ${dim(' No API spec found — run')} ${blue('delimit-cli lint')} ${dim('to detect one')}`);
|
|
1291
1278
|
}
|
|
1292
|
-
|
|
1293
1279
|
// Count tests
|
|
1294
1280
|
const testDirs = ['tests', 'test', '__tests__', 'spec'];
|
|
1295
1281
|
for (const td of testDirs) {
|
|
@@ -1305,7 +1291,6 @@ exit 127
|
|
|
1305
1291
|
} catch {}
|
|
1306
1292
|
}
|
|
1307
1293
|
}
|
|
1308
|
-
|
|
1309
1294
|
// Check git status
|
|
1310
1295
|
try {
|
|
1311
1296
|
const gitStatus = execSync('git log --oneline -1 2>/dev/null', { encoding: 'utf-8', timeout: 3000 }).trim();
|
|
@@ -1314,7 +1299,6 @@ exit 127
|
|
|
1314
1299
|
projectFindings++;
|
|
1315
1300
|
}
|
|
1316
1301
|
} catch {}
|
|
1317
|
-
|
|
1318
1302
|
// Check for existing .delimit context
|
|
1319
1303
|
const ledgerDir = path.join(os.homedir(), '.delimit', 'ledger');
|
|
1320
1304
|
if (fs.existsSync(ledgerDir)) {
|
|
@@ -1333,7 +1317,6 @@ exit 127
|
|
|
1333
1317
|
}
|
|
1334
1318
|
} catch {}
|
|
1335
1319
|
}
|
|
1336
|
-
|
|
1337
1320
|
// Check for sessions
|
|
1338
1321
|
const sessionsDir = path.join(os.homedir(), '.delimit', 'sessions');
|
|
1339
1322
|
if (fs.existsSync(sessionsDir)) {
|
|
@@ -1345,13 +1328,10 @@ exit 127
|
|
|
1345
1328
|
}
|
|
1346
1329
|
} catch {}
|
|
1347
1330
|
}
|
|
1348
|
-
|
|
1349
1331
|
if (projectFindings === 0) {
|
|
1350
1332
|
log(` ${dim(' New project — run')} ${blue('delimit-cli demo')} ${dim('to see governance in action')}`);
|
|
1351
1333
|
}
|
|
1352
|
-
|
|
1353
1334
|
log('');
|
|
1354
|
-
|
|
1355
1335
|
// Suggested next action based on findings
|
|
1356
1336
|
log(` ${bold("What's next:")}`);
|
|
1357
1337
|
if (specFound) {
|
|
@@ -1370,7 +1350,6 @@ exit 127
|
|
|
1370
1350
|
log('');
|
|
1371
1351
|
log(` ${bold('Keep Building.')}`);
|
|
1372
1352
|
log('');
|
|
1373
|
-
|
|
1374
1353
|
// Show governance banner preview
|
|
1375
1354
|
const shimFile = path.join(DELIMIT_HOME, 'shims', 'claude');
|
|
1376
1355
|
if (fs.existsSync(shimFile)) {
|
|
@@ -1409,14 +1388,11 @@ exit 127
|
|
|
1409
1388
|
log('');
|
|
1410
1389
|
}
|
|
1411
1390
|
}
|
|
1412
|
-
|
|
1413
1391
|
// LED-213: Import canonical template from shared module
|
|
1414
1392
|
const { getDelimitSection } = require('../lib/delimit-template');
|
|
1415
|
-
|
|
1416
1393
|
function getClaudeMdContent() {
|
|
1417
1394
|
return getDelimitSection() + '\n';
|
|
1418
1395
|
}
|
|
1419
|
-
|
|
1420
1396
|
/**
|
|
1421
1397
|
* Upsert the Delimit section in a file using <!-- delimit:start --> / <!-- delimit:end --> markers.
|
|
1422
1398
|
*
|
|
@@ -1435,18 +1411,15 @@ function upsertDelimitSection(filePath) {
|
|
|
1435
1411
|
const newSection = getDelimitSection();
|
|
1436
1412
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
1437
1413
|
const version = pkg.version || '0.0.0';
|
|
1438
|
-
|
|
1439
1414
|
if (!fs.existsSync(filePath)) {
|
|
1440
1415
|
fs.writeFileSync(filePath, newSection + '\n');
|
|
1441
1416
|
return { action: 'created' };
|
|
1442
1417
|
}
|
|
1443
|
-
|
|
1444
1418
|
const rawExisting = fs.readFileSync(filePath, 'utf-8');
|
|
1445
1419
|
// Strip a UTF-8 BOM if present so the start-of-line anchor still matches
|
|
1446
1420
|
// the very first line of the file. We write back the stripped form to keep
|
|
1447
1421
|
// serialization deterministic.
|
|
1448
1422
|
const existing = rawExisting.replace(/^\uFEFF/, '');
|
|
1449
|
-
|
|
1450
1423
|
// Check if managed markers already exist.
|
|
1451
1424
|
// Markers MUST be on their own line — anchored with the multiline flag — so
|
|
1452
1425
|
// that documentation prose that quotes the markers (e.g. inside backticks,
|
|
@@ -1460,7 +1433,6 @@ function upsertDelimitSection(filePath) {
|
|
|
1460
1433
|
const endMatch = existing.match(endMarkerRe);
|
|
1461
1434
|
const hasStart = !!startMatch;
|
|
1462
1435
|
const hasEnd = !!endMatch;
|
|
1463
|
-
|
|
1464
1436
|
if (hasStart && hasEnd) {
|
|
1465
1437
|
// Extract current version from the marker (also anchored, allows indent)
|
|
1466
1438
|
const versionMatch = existing.match(/^[ \t]*<!-- delimit:start v([^ ]+) -->[ \t]*$/m);
|
|
@@ -1476,7 +1448,6 @@ function upsertDelimitSection(filePath) {
|
|
|
1476
1448
|
fs.writeFileSync(filePath, before + newSection + after);
|
|
1477
1449
|
return { action: 'updated' };
|
|
1478
1450
|
}
|
|
1479
|
-
|
|
1480
1451
|
// No markers present — append the managed section at the bottom.
|
|
1481
1452
|
// User content at the top is preserved verbatim. Markers get added so future
|
|
1482
1453
|
// upgrades can update just the managed region.
|
|
@@ -1484,7 +1455,6 @@ function upsertDelimitSection(filePath) {
|
|
|
1484
1455
|
fs.writeFileSync(filePath, existing + separator + newSection + '\n');
|
|
1485
1456
|
return { action: 'appended' };
|
|
1486
1457
|
}
|
|
1487
|
-
|
|
1488
1458
|
function copyDir(src, dest) {
|
|
1489
1459
|
fs.mkdirSync(dest, { recursive: true });
|
|
1490
1460
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
@@ -1498,7 +1468,6 @@ function copyDir(src, dest) {
|
|
|
1498
1468
|
}
|
|
1499
1469
|
}
|
|
1500
1470
|
}
|
|
1501
|
-
|
|
1502
1471
|
main().catch(err => {
|
|
1503
1472
|
console.error('Setup failed:', err.message);
|
|
1504
1473
|
process.exit(1);
|