refacil-sdd-ai 5.2.3 → 5.3.0
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/NOTICE.md +46 -0
- package/README.md +209 -42
- package/agents/auditor.md +46 -0
- package/agents/debugger.md +41 -1
- package/agents/implementer.md +76 -10
- package/agents/investigator.md +36 -0
- package/agents/proposer.md +46 -2
- package/agents/tester.md +45 -8
- package/agents/validator.md +67 -13
- package/bin/cli.js +396 -84
- package/lib/bus/broker.js +121 -3
- package/lib/bus/spawn.js +189 -121
- package/lib/check-review.js +102 -0
- package/lib/codegraph-telemetry.js +135 -0
- package/lib/codegraph.js +273 -0
- package/lib/commands/autopilot.js +120 -0
- package/lib/commands/bus.js +29 -36
- package/lib/commands/compact.js +185 -46
- package/lib/commands/read-spec.js +352 -0
- package/lib/commands/sdd.js +429 -44
- package/lib/compact-guidance.js +122 -77
- package/lib/config.js +136 -0
- package/lib/global-paths.js +56 -20
- package/lib/hooks.js +26 -4
- package/lib/ide-detection.js +1 -1
- package/lib/ignore-files.js +5 -1
- package/lib/installer.js +195 -19
- package/lib/kapso.js +241 -0
- package/lib/methodology-migration-pending.js +13 -0
- package/lib/open-browser.js +32 -0
- package/lib/opencode-migrate.js +148 -0
- package/lib/opencode-plugin/index.js +84 -104
- package/lib/opencode-plugin/rules.js +236 -0
- package/lib/project-root.js +154 -0
- package/lib/repo-ide-sync.js +5 -0
- package/lib/spec-reader/lang.js +72 -0
- package/lib/spec-reader/md-parser.js +299 -0
- package/lib/spec-reader/session.js +139 -0
- package/lib/spec-reader/ui/app.js +685 -0
- package/lib/spec-reader/ui/index.html +59 -0
- package/lib/spec-reader/ui/mixed-lang.js +200 -0
- package/lib/spec-reader/ui/model-cache.js +117 -0
- package/lib/spec-reader/ui/style.css +294 -0
- package/lib/spec-reader/ui/supertonic-helper.js +565 -0
- package/lib/spec-sync.js +258 -0
- package/lib/test-scope.js +713 -0
- package/lib/testing-policy-sync.js +14 -2
- package/package.json +5 -3
- package/skills/apply/SKILL.md +39 -64
- package/skills/archive/SKILL.md +74 -48
- package/skills/ask/SKILL.md +43 -8
- package/skills/autopilot/SKILL.md +476 -0
- package/skills/bug/SKILL.md +52 -53
- package/skills/explore/SKILL.md +48 -1
- package/skills/guide/SKILL.md +31 -13
- package/skills/inbox/SKILL.md +9 -0
- package/skills/join/SKILL.md +1 -1
- package/skills/prereqs/BUS-CROSS-REPO.md +33 -16
- package/skills/prereqs/METHODOLOGY-CONTRACT.md +96 -17
- package/skills/prereqs/SKILL.md +1 -1
- package/skills/propose/SKILL.md +74 -19
- package/skills/read-spec/SKILL.md +76 -0
- package/skills/reply/SKILL.md +42 -9
- package/skills/review/SKILL.md +63 -25
- package/skills/review/checklist.md +2 -2
- package/skills/say/SKILL.md +40 -4
- package/skills/setup/SKILL.md +59 -5
- package/skills/setup/troubleshooting.md +11 -3
- package/skills/stats/SKILL.md +157 -0
- package/skills/test/SKILL.md +35 -10
- package/skills/up-code/SKILL.md +20 -13
- package/skills/update/SKILL.md +32 -1
- package/skills/verify/SKILL.md +78 -41
- package/templates/compact-guidance.md +10 -0
- package/templates/methodology-guide.md +5 -0
package/bin/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
removeSkills,
|
|
17
17
|
removeGlobalSkills,
|
|
18
18
|
removeOpenCodeArtifacts,
|
|
19
|
+
removeOpenCodeGlobalArtifacts,
|
|
19
20
|
removeCodexArtifacts,
|
|
20
21
|
removeOpenspecLegacyAssets,
|
|
21
22
|
removeProjectLevelArtifacts,
|
|
@@ -28,16 +29,22 @@ const {
|
|
|
28
29
|
getPackageVersion,
|
|
29
30
|
checkNodeVersion,
|
|
30
31
|
checkClaudeCodeVersion,
|
|
32
|
+
promptCodegraphMode,
|
|
31
33
|
} = require('../lib/installer');
|
|
32
34
|
const { installHooks, uninstallHooks, cleanLegacySettingsHooks, installOpenCodePlugin, uninstallOpenCodePlugin, removeProjectLevelHooks } = require('../lib/hooks');
|
|
33
35
|
const { globalClaudeDir, globalCursorDir, globalOpenCodeDir, globalCodexDir, readSelectedIDEs, writeSelectedIDEs } = require('../lib/global-paths');
|
|
34
36
|
const { detectInstalledIDEs } = require('../lib/ide-detection');
|
|
35
37
|
const { handleCompact } = require('../lib/commands/compact');
|
|
36
38
|
const { handleBus } = require('../lib/commands/bus');
|
|
39
|
+
const { handleReadSpec } = require('../lib/commands/read-spec');
|
|
37
40
|
const { handleSdd, autoMigrateOpenspec, findProjectRoot, cmdWriteConfig } = require('../lib/commands/sdd');
|
|
41
|
+
const { handleAutopilot } = require('../lib/commands/autopilot');
|
|
42
|
+
const { resolveWorkspaceRoot } = require('../lib/project-root');
|
|
43
|
+
const { evaluateGitPushReview } = require('../lib/check-review');
|
|
38
44
|
const { syncIgnoreFiles } = require('../lib/ignore-files');
|
|
39
45
|
const { methodologyMigrationPending } = require('../lib/methodology-migration-pending');
|
|
40
46
|
const { resolveSelectedIDEsForRepo, syncRepoIdeFiles } = require('../lib/repo-ide-sync');
|
|
47
|
+
const codegraph = require('../lib/codegraph');
|
|
41
48
|
|
|
42
49
|
const packageRoot = path.resolve(__dirname, '..');
|
|
43
50
|
const projectRoot = findProjectRoot();
|
|
@@ -102,8 +109,15 @@ function notifyUpdate() {
|
|
|
102
109
|
if (!info.shown) {
|
|
103
110
|
// First time: block so the user sees it clearly
|
|
104
111
|
try { fs.writeFileSync(flagPath, JSON.stringify({ ...info, shown: true })); } catch (_) {}
|
|
112
|
+
const isSameVersion = info.from && info.to && info.from === info.to;
|
|
113
|
+
const header = isSameVersion
|
|
114
|
+
? '[refacil-sdd-ai] Pending SDD-AI configuration detected.'
|
|
115
|
+
: `[refacil-sdd-ai] Methodology update detected (${fromLabel} → v${info.to}).`;
|
|
116
|
+
const reasonLines = mig.reasons.length
|
|
117
|
+
? '\nPending:\n' + mig.reasons.map((r) => ` • ${r}`).join('\n')
|
|
118
|
+
: '';
|
|
105
119
|
const userMsg =
|
|
106
|
-
|
|
120
|
+
`${header}${reasonLines}\n` +
|
|
107
121
|
`Your message was paused. Do you want to apply the pending migrations before continuing?\n` +
|
|
108
122
|
` • Yes: run /refacil:update\n` +
|
|
109
123
|
` • No: resend your previous message to continue without updating.`;
|
|
@@ -118,15 +132,16 @@ function notifyUpdate() {
|
|
|
118
132
|
// Second time: let through and notify the LLM (only Claude Code supports context injection)
|
|
119
133
|
try { fs.unlinkSync(flagPath); } catch (_) {}
|
|
120
134
|
if (!isCursor) {
|
|
135
|
+
const reasonSummary = mig.reasons.length ? ` Pending: ${mig.reasons.join('; ')}` : '';
|
|
121
136
|
console.log(
|
|
122
|
-
`[refacil-sdd-ai] IMPORTANT: there is a pending SDD-AI methodology update (${fromLabel} → v${info.to})
|
|
137
|
+
`[refacil-sdd-ai] IMPORTANT: there is a pending SDD-AI methodology update (${fromLabel} → v${info.to}).${reasonSummary} ` +
|
|
123
138
|
`Before responding to the user, ask them if they want to run /refacil:update now.`,
|
|
124
139
|
);
|
|
125
140
|
}
|
|
126
141
|
}
|
|
127
142
|
}
|
|
128
143
|
|
|
129
|
-
function repoIsInitialized() {
|
|
144
|
+
function repoIsInitialized(repoRoot = projectRoot) {
|
|
130
145
|
const home = os.homedir();
|
|
131
146
|
// Check global dirs first (new installation model)
|
|
132
147
|
if (
|
|
@@ -139,9 +154,9 @@ function repoIsInitialized() {
|
|
|
139
154
|
}
|
|
140
155
|
// Fallback: check legacy project-level dirs for backward compat
|
|
141
156
|
return (
|
|
142
|
-
fs.existsSync(path.join(
|
|
143
|
-
fs.existsSync(path.join(
|
|
144
|
-
fs.existsSync(path.join(
|
|
157
|
+
fs.existsSync(path.join(repoRoot, '.claude', 'skills')) ||
|
|
158
|
+
fs.existsSync(path.join(repoRoot, '.cursor', 'skills')) ||
|
|
159
|
+
fs.existsSync(path.join(repoRoot, '.opencode', 'skills'))
|
|
145
160
|
);
|
|
146
161
|
}
|
|
147
162
|
|
|
@@ -274,10 +289,10 @@ function semverGt(a, b) {
|
|
|
274
289
|
return false;
|
|
275
290
|
}
|
|
276
291
|
|
|
277
|
-
function syncRepoSkillsIfStale(globalVersion) {
|
|
278
|
-
if (!repoIsInitialized()) return null;
|
|
292
|
+
function syncRepoSkillsIfStale(globalVersion, repoRoot = projectRoot) {
|
|
293
|
+
if (!repoIsInitialized(repoRoot)) return null;
|
|
279
294
|
// Repo-level version takes priority; fall back to global store
|
|
280
|
-
const repoVersion = readRepoVersion(
|
|
295
|
+
const repoVersion = readRepoVersion(repoRoot) || readGlobalVersion(os.homedir(), repoRoot);
|
|
281
296
|
if (repoVersion === globalVersion) return null;
|
|
282
297
|
|
|
283
298
|
// Repo has newer skills than the installed package — do not downgrade
|
|
@@ -319,17 +334,126 @@ function migrationPendingCmd() {
|
|
|
319
334
|
process.exit(pending ? 1 : 0);
|
|
320
335
|
}
|
|
321
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Detect whether any active refacil flow is in progress (any .review-passed, proposal.md, etc.).
|
|
339
|
+
* Returns true if the user is mid-flow so we don't interrupt with the CodeGraph suggestion.
|
|
340
|
+
*/
|
|
341
|
+
function isActiveRefacilFlow() {
|
|
342
|
+
try {
|
|
343
|
+
const changesDir = path.join(projectRoot, 'refacil-sdd', 'changes');
|
|
344
|
+
if (!fs.existsSync(changesDir)) return false;
|
|
345
|
+
const entries = fs.readdirSync(changesDir, { withFileTypes: true });
|
|
346
|
+
return entries.some(
|
|
347
|
+
(e) => e.isDirectory() && e.name !== 'archive' &&
|
|
348
|
+
fs.existsSync(path.join(changesDir, e.name, 'proposal.md')),
|
|
349
|
+
);
|
|
350
|
+
} catch (_) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Inject a CodeGraph suggestion block if conditions are met:
|
|
357
|
+
* 1. codegraphMode is null/undefined (no preference set yet)
|
|
358
|
+
* 2. No active refacil flow detected
|
|
359
|
+
* 3. Suggestion has not been permanently suppressed (codegraph-suggest-shown: true)
|
|
360
|
+
* 4. Snooze period has not expired
|
|
361
|
+
*/
|
|
362
|
+
function maybeInjectCodegraphSuggestion() {
|
|
363
|
+
try {
|
|
364
|
+
const { readConfigFile, extractCodegraphMode, extractCodegraphSuggestState, writeConfigValue } = require('../lib/config');
|
|
365
|
+
const globalConfigPath = path.join(os.homedir(), '.refacil-sdd-ai', 'config.yaml');
|
|
366
|
+
const globalCfg = readConfigFile(globalConfigPath) || {};
|
|
367
|
+
|
|
368
|
+
const codegraphMode = extractCodegraphMode(globalCfg, 'global');
|
|
369
|
+
if (codegraphMode !== null) return; // Already answered — do not suggest again
|
|
370
|
+
|
|
371
|
+
const { shown, snoozeUntil } = extractCodegraphSuggestState(globalCfg, 'global');
|
|
372
|
+
if (shown === true) return; // Permanently suppressed
|
|
373
|
+
|
|
374
|
+
if (snoozeUntil) {
|
|
375
|
+
try {
|
|
376
|
+
const snoozeDate = new Date(snoozeUntil);
|
|
377
|
+
if (!isNaN(snoozeDate.getTime()) && new Date() < snoozeDate) return; // Still snoozed
|
|
378
|
+
} catch (_) {}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (isActiveRefacilFlow()) return; // Mid-flow — do not interrupt
|
|
382
|
+
|
|
383
|
+
const msg =
|
|
384
|
+
'[refacil-sdd-ai] CodeGraph is available — an optional tool that reduces token usage ~71% ' +
|
|
385
|
+
'in exploratory sub-agents (investigate, propose, debug) by querying the call graph instead of reading files.\n' +
|
|
386
|
+
'https://github.com/colbymchenry/codegraph (MIT, Colby McHenry)\n\n' +
|
|
387
|
+
'Would you like to enable CodeGraph integration?\n' +
|
|
388
|
+
' 1) yes-now — enable and index this repo immediately\n' +
|
|
389
|
+
' 2) yes-later — enable globally (index repos on /refacil:setup)\n' +
|
|
390
|
+
' 3) no-7d — remind me in 7 days\n' +
|
|
391
|
+
' 4) never — disable permanently\n\n' +
|
|
392
|
+
'Type a number (1-4) and press Enter, or press Enter to skip: ';
|
|
393
|
+
|
|
394
|
+
process.stdout.write(msg);
|
|
395
|
+
|
|
396
|
+
// Only read stdin when running in a real TTY — in hook context stdin is a pipe
|
|
397
|
+
// and fs.readFileSync(0) would block indefinitely waiting for data or EOF.
|
|
398
|
+
if (!process.stdin.isTTY) return;
|
|
399
|
+
|
|
400
|
+
let response = '';
|
|
401
|
+
try {
|
|
402
|
+
const raw = fs.readFileSync(0, { encoding: 'utf8', flag: 'r' });
|
|
403
|
+
response = raw.trim().toLowerCase();
|
|
404
|
+
} catch (_) {
|
|
405
|
+
return; // stdin not readable in this context — skip
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (response === '1' || response === 'yes-now') {
|
|
409
|
+
writeConfigValue('codegraphMode', 'enabled', os.homedir());
|
|
410
|
+
writeConfigValue('codegraph-suggest-shown', 'true', os.homedir());
|
|
411
|
+
const selectedIDEs = readSelectedIDEs() || ['.claude', '.cursor', '.opencode', '.codex'];
|
|
412
|
+
codegraph.registerMcp(selectedIDEs);
|
|
413
|
+
if (codegraph.isInstalled()) {
|
|
414
|
+
codegraph.init(projectRoot);
|
|
415
|
+
process.stdout.write('[refacil-sdd-ai] CodeGraph indexing started in background.\n');
|
|
416
|
+
} else {
|
|
417
|
+
process.stdout.write(
|
|
418
|
+
'[refacil-sdd-ai] CodeGraph mode set to enabled. ' +
|
|
419
|
+
'Install @colbymchenry/codegraph to activate: npm install -g @colbymchenry/codegraph\n',
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
} else if (response === '2' || response === 'yes-later') {
|
|
423
|
+
writeConfigValue('codegraphMode', 'enabled', os.homedir());
|
|
424
|
+
writeConfigValue('codegraph-suggest-shown', 'true', os.homedir());
|
|
425
|
+
process.stdout.write('[refacil-sdd-ai] CodeGraph mode set to enabled. Repos will be indexed on /refacil:setup.\n');
|
|
426
|
+
} else if (response === '3' || response === 'no-7d') {
|
|
427
|
+
const snoozeDate = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
428
|
+
writeConfigValue('codegraph-suggest-snooze', snoozeDate, os.homedir());
|
|
429
|
+
process.stdout.write('[refacil-sdd-ai] CodeGraph suggestion snoozed for 7 days.\n');
|
|
430
|
+
} else if (response === '4' || response === 'never') {
|
|
431
|
+
writeConfigValue('codegraphMode', 'disabled', os.homedir());
|
|
432
|
+
writeConfigValue('codegraph-suggest-shown', 'true', os.homedir());
|
|
433
|
+
process.stdout.write(
|
|
434
|
+
'[refacil-sdd-ai] CodeGraph disabled. Re-enable with: ' +
|
|
435
|
+
'refacil-sdd-ai sdd config --set codegraph enabled\n',
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
// Empty/unrecognised response — skip silently
|
|
439
|
+
} catch (_) {
|
|
440
|
+
// Suggestion injection must never break session startup
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
322
444
|
function checkUpdate() {
|
|
323
|
-
|
|
445
|
+
const root = resolveWorkspaceRoot();
|
|
446
|
+
|
|
447
|
+
clearStalePendingUpdateFlag(root);
|
|
324
448
|
|
|
325
449
|
try {
|
|
326
|
-
autoMigrateOpenspec(
|
|
450
|
+
autoMigrateOpenspec(root);
|
|
327
451
|
} catch (err) {
|
|
328
452
|
process.stderr.write(`[refacil-sdd-ai] Could not migrate openspec/ to refacil-sdd/: ${err.message}\n`);
|
|
329
453
|
}
|
|
330
454
|
|
|
331
455
|
try {
|
|
332
|
-
const legacyRemoved = removeOpenspecLegacyAssets(
|
|
456
|
+
const legacyRemoved = removeOpenspecLegacyAssets(root);
|
|
333
457
|
if (legacyRemoved > 0) {
|
|
334
458
|
process.stderr.write(`[refacil-sdd-ai] Removed ${legacyRemoved} legacy OpenSpec assets (openspec-* skills, opsx-* commands)\n`);
|
|
335
459
|
}
|
|
@@ -347,8 +471,8 @@ function checkUpdate() {
|
|
|
347
471
|
fs.existsSync(path.join(globalCodexDir(home), 'skills'));
|
|
348
472
|
|
|
349
473
|
if (globalActive) {
|
|
350
|
-
const cleaned = removeProjectLevelArtifacts(
|
|
351
|
-
removeProjectLevelHooks(
|
|
474
|
+
const cleaned = removeProjectLevelArtifacts(root);
|
|
475
|
+
removeProjectLevelHooks(root);
|
|
352
476
|
if (cleaned > 0) {
|
|
353
477
|
process.stdout.write(`[refacil-sdd-ai] Cleaned up ${cleaned} project-level artifact(s) — global installation is active.\n`);
|
|
354
478
|
}
|
|
@@ -361,7 +485,7 @@ function checkUpdate() {
|
|
|
361
485
|
let localVersion = getPackageVersion(packageRoot);
|
|
362
486
|
|
|
363
487
|
try {
|
|
364
|
-
const syncOut = syncRepoSessionMarkers(
|
|
488
|
+
const syncOut = syncRepoSessionMarkers(root, packageRoot);
|
|
365
489
|
if (!syncOut.ok) {
|
|
366
490
|
process.stderr.write(`[refacil-sdd-ai] session repo sync: ${syncOut.reason}\n`);
|
|
367
491
|
} else {
|
|
@@ -380,7 +504,7 @@ function checkUpdate() {
|
|
|
380
504
|
process.stderr.write(`[refacil-sdd-ai] session repo sync: ${err.message}\n`);
|
|
381
505
|
}
|
|
382
506
|
|
|
383
|
-
cleanLegacySettingsHooks(
|
|
507
|
+
cleanLegacySettingsHooks(root);
|
|
384
508
|
|
|
385
509
|
// Self-healing: if selected-ides.json exists but global skills/hooks are missing, restore them
|
|
386
510
|
// Covers the case where the Claude Code desktop app overwrites settings.json and wipes SDD hooks,
|
|
@@ -407,7 +531,7 @@ function checkUpdate() {
|
|
|
407
531
|
// Always verify hooks are present for all selected IDEs — idempotent, only writes if missing
|
|
408
532
|
for (const ide of ['.claude', '.cursor', '.opencode', '.codex']) {
|
|
409
533
|
if (sddSelectedIDEs.includes(ide)) {
|
|
410
|
-
installHooks(ide, home,
|
|
534
|
+
installHooks(ide, home, root);
|
|
411
535
|
}
|
|
412
536
|
}
|
|
413
537
|
}
|
|
@@ -439,15 +563,15 @@ function checkUpdate() {
|
|
|
439
563
|
}
|
|
440
564
|
|
|
441
565
|
// Step 2: sync repo skills with the (now updated) package
|
|
442
|
-
const syncResult = syncRepoSkillsIfStale(localVersion);
|
|
566
|
+
const syncResult = syncRepoSkillsIfStale(localVersion, root);
|
|
443
567
|
if (syncResult && !syncResult.failed) {
|
|
444
568
|
const fromLabel = syncResult.from ? `v${syncResult.from}` : 'unknown version';
|
|
445
569
|
console.log(
|
|
446
570
|
`[refacil-sdd-ai] Repo skills synced (${fromLabel} -> v${syncResult.to}). ` +
|
|
447
|
-
'Restart
|
|
571
|
+
'Restart your IDE session to pick up the changes.',
|
|
448
572
|
);
|
|
449
|
-
if (methodologyMigrationPending(
|
|
450
|
-
writePendingUpdateFlag(
|
|
573
|
+
if (methodologyMigrationPending(root).pending) {
|
|
574
|
+
writePendingUpdateFlag(root, syncResult.from, syncResult.to);
|
|
451
575
|
}
|
|
452
576
|
} else if (syncResult && syncResult.failed) {
|
|
453
577
|
console.log(
|
|
@@ -455,6 +579,40 @@ function checkUpdate() {
|
|
|
455
579
|
'but automatic sync failed. Run manually: refacil-sdd-ai update',
|
|
456
580
|
);
|
|
457
581
|
}
|
|
582
|
+
|
|
583
|
+
// Inject CodeGraph suggestion if the user has not set a preference yet
|
|
584
|
+
maybeInjectCodegraphSuggestion();
|
|
585
|
+
|
|
586
|
+
// CodeGraph auto-refresh — silent background operation, no user interaction
|
|
587
|
+
try {
|
|
588
|
+
const { loadBranchConfigWithSources } = require('../lib/config');
|
|
589
|
+
const cfgInfo = loadBranchConfigWithSources(root);
|
|
590
|
+
const cgMode = cfgInfo.codegraphMode;
|
|
591
|
+
if (cgMode === 'enabled' || cgMode === 'per-repo') {
|
|
592
|
+
if (!codegraph.isInstalled()) {
|
|
593
|
+
// CLI missing: surface via notify-update banner (blocking once)
|
|
594
|
+
writePendingUpdateFlag(root, localVersion, localVersion);
|
|
595
|
+
process.stdout.write(
|
|
596
|
+
'[refacil-sdd-ai] CodeGraph is enabled but the CLI is not installed. ' +
|
|
597
|
+
'Run /refacil:update to install and configure it.\n',
|
|
598
|
+
);
|
|
599
|
+
} else if (!codegraph.isInitialized(root)) {
|
|
600
|
+
// Not indexed: start automatically in the background, no question needed
|
|
601
|
+
codegraph.init(root);
|
|
602
|
+
process.stdout.write('[refacil-sdd-ai] CodeGraph: building index in background (~30s).\n');
|
|
603
|
+
} else if (codegraph.isStale(root)) {
|
|
604
|
+
// Index exists but new commits since last init: refresh silently
|
|
605
|
+
codegraph.init(root);
|
|
606
|
+
process.stdout.write('[refacil-sdd-ai] CodeGraph: refreshing index (new commits detected).\n');
|
|
607
|
+
} else {
|
|
608
|
+
// Initialized and up to date. If the timestamp file is absent (index was built
|
|
609
|
+
// externally, not through our tools), write it now so isStale() has a reference.
|
|
610
|
+
codegraph.touchTimestamp(root);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
} catch (_) {
|
|
614
|
+
// Tolerant — CodeGraph state check must never break session startup
|
|
615
|
+
}
|
|
458
616
|
}
|
|
459
617
|
|
|
460
618
|
// --- Check review (PreToolUse hook) ---
|
|
@@ -469,38 +627,8 @@ function checkReview() {
|
|
|
469
627
|
}
|
|
470
628
|
|
|
471
629
|
const command = (input.tool_input && input.tool_input.command) || '';
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const sddChangesDir = path.join(projectRoot, 'refacil-sdd', 'changes');
|
|
475
|
-
const legacyChangesDir = path.join(projectRoot, 'openspec', 'changes');
|
|
476
|
-
const changesDir = fs.existsSync(sddChangesDir) ? sddChangesDir : legacyChangesDir;
|
|
477
|
-
if (!fs.existsSync(changesDir)) return;
|
|
478
|
-
|
|
479
|
-
const entries = fs.readdirSync(changesDir, { withFileTypes: true });
|
|
480
|
-
const activeChanges = entries.filter(
|
|
481
|
-
(e) => e.isDirectory() && e.name !== 'archive',
|
|
482
|
-
);
|
|
483
|
-
|
|
484
|
-
if (activeChanges.length === 0) return;
|
|
485
|
-
|
|
486
|
-
const missing = activeChanges.filter(
|
|
487
|
-
(e) => !fs.existsSync(path.join(changesDir, e.name, '.review-passed')),
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
if (missing.length > 0) {
|
|
491
|
-
const names = missing.map((e) => e.name).join(', ');
|
|
492
|
-
const reason =
|
|
493
|
-
missing.length === 1
|
|
494
|
-
? `[refacil-sdd-ai] Review pending for: ${names}. ` +
|
|
495
|
-
'Stop the push and run /refacil:review on that change before pushing code. ' +
|
|
496
|
-
'If the review passes, retry the git push. ' +
|
|
497
|
-
'If the review requires corrections, report the findings to the user and DO NOT retry the push.'
|
|
498
|
-
: `[refacil-sdd-ai] Multiple changes without approved review: ${names}. ` +
|
|
499
|
-
'Stop the push and ask the user to explicitly select which change they want to push. ' +
|
|
500
|
-
'Then run /refacil:review <change-name> for that specific change and retry the push. ' +
|
|
501
|
-
'Do not run automatic review without explicit selection when there is more than one pending change.';
|
|
502
|
-
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
503
|
-
}
|
|
630
|
+
const block = evaluateGitPushReview(command, resolveWorkspaceRoot({ hookInput: input, skipStdin: true }));
|
|
631
|
+
if (block) console.log(JSON.stringify(block));
|
|
504
632
|
}
|
|
505
633
|
|
|
506
634
|
// --- Branch config prompt (used by init) ---
|
|
@@ -657,6 +785,9 @@ async function init() {
|
|
|
657
785
|
// Prompt for global branch configuration (skipped with --yes/--defaults or non-TTY)
|
|
658
786
|
await promptBranchConfig();
|
|
659
787
|
|
|
788
|
+
// Prompt for CodeGraph integration mode (skipped with --yes/--defaults or non-TTY)
|
|
789
|
+
await promptCodegraphMode(os.homedir());
|
|
790
|
+
|
|
660
791
|
if (selectedIDEs.length === 0) {
|
|
661
792
|
console.log('\n No IDEs selected. Nothing installed.\n');
|
|
662
793
|
console.log(' Re-run with: refacil-sdd-ai init --all to install for all IDEs');
|
|
@@ -895,6 +1026,14 @@ function update() {
|
|
|
895
1026
|
console.error(` Warning: session repo sync: ${err.message}`);
|
|
896
1027
|
}
|
|
897
1028
|
|
|
1029
|
+
try {
|
|
1030
|
+
const { loadBranchConfigWithSources } = require('../lib/config');
|
|
1031
|
+
const cfgInfo = loadBranchConfigWithSources(projectRoot);
|
|
1032
|
+
if (cfgInfo.codegraphMode && cfgInfo.codegraphMode !== 'disabled') {
|
|
1033
|
+
codegraph.registerMcp(selectedIDEs, homeDir);
|
|
1034
|
+
}
|
|
1035
|
+
} catch (_) {}
|
|
1036
|
+
|
|
898
1037
|
console.log('\n RESTART your IDE session to apply the changes.\n');
|
|
899
1038
|
}
|
|
900
1039
|
|
|
@@ -966,8 +1105,10 @@ function clean() {
|
|
|
966
1105
|
|
|
967
1106
|
const homeDir = os.homedir();
|
|
968
1107
|
|
|
969
|
-
const selectedIDEs =
|
|
970
|
-
const
|
|
1108
|
+
const selectedIDEs = resolveSelectedIDEsForRepo(projectRoot, homeDir);
|
|
1109
|
+
const fallbackIDEs = ['.claude', '.cursor', '.opencode', '.codex'];
|
|
1110
|
+
const ideDirsForClean = selectedIDEs.length > 0 ? selectedIDEs : fallbackIDEs;
|
|
1111
|
+
const globalCount = removeGlobalSkills(homeDir, ideDirsForClean);
|
|
971
1112
|
if (globalCount > 0) {
|
|
972
1113
|
console.log(` ${globalCount} global skills removed from IDE user directories`);
|
|
973
1114
|
}
|
|
@@ -977,33 +1118,42 @@ function clean() {
|
|
|
977
1118
|
console.log(` ${count} project-level skills removed from .claude/skills/ and .cursor/skills/`);
|
|
978
1119
|
}
|
|
979
1120
|
|
|
980
|
-
if (
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1121
|
+
if (ideDirsForClean.includes('.claude') || ideDirsForClean.includes('claude')) {
|
|
1122
|
+
if (uninstallHooks('.claude', homeDir)) {
|
|
1123
|
+
console.log(' SDD-AI hooks removed from ~/.claude/settings.json');
|
|
1124
|
+
} else {
|
|
1125
|
+
console.log(' No SDD-AI hooks found to remove in ~/.claude/settings.json.');
|
|
1126
|
+
}
|
|
984
1127
|
}
|
|
985
|
-
if (
|
|
986
|
-
|
|
1128
|
+
if (ideDirsForClean.includes('.cursor') || ideDirsForClean.includes('cursor')) {
|
|
1129
|
+
if (uninstallHooks('.cursor', homeDir)) {
|
|
1130
|
+
console.log(' SDD-AI hooks removed from ~/.cursor/hooks.json');
|
|
1131
|
+
}
|
|
987
1132
|
}
|
|
988
1133
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1134
|
+
if (ideDirsForClean.includes('.opencode') || ideDirsForClean.includes('opencode')) {
|
|
1135
|
+
try {
|
|
1136
|
+
removeOpenCodeGlobalArtifacts(homeDir);
|
|
1137
|
+
if (uninstallOpenCodePlugin(homeDir)) {
|
|
1138
|
+
console.log(' OpenCode global skills, agents, and plugin removed');
|
|
1139
|
+
} else {
|
|
1140
|
+
console.log(' OpenCode global artifacts removed from config directory');
|
|
1141
|
+
}
|
|
1142
|
+
} catch (err) {
|
|
1143
|
+
console.error(` Warning: could not remove OpenCode global artifacts: ${err.message}`);
|
|
993
1144
|
}
|
|
994
|
-
} catch (err) {
|
|
995
|
-
console.error(` Warning: could not remove OpenCode plugin: ${err.message}`);
|
|
996
1145
|
}
|
|
997
1146
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1147
|
+
if (ideDirsForClean.includes('.codex') || ideDirsForClean.includes('codex')) {
|
|
1148
|
+
try {
|
|
1149
|
+
removeCodexArtifacts(homeDir);
|
|
1150
|
+
console.log(' Codex skills and agents removed from ~/.codex/');
|
|
1151
|
+
} catch (err) {
|
|
1152
|
+
console.error(` Warning: could not remove Codex artifacts: ${err.message}`);
|
|
1153
|
+
}
|
|
1154
|
+
if (uninstallHooks('.codex', homeDir)) {
|
|
1155
|
+
console.log(' SDD-AI hooks removed from ~/.codex/config.toml');
|
|
1156
|
+
}
|
|
1007
1157
|
}
|
|
1008
1158
|
|
|
1009
1159
|
// Clean project-level OpenCode artifacts if .opencode/ directory is present
|
|
@@ -1065,10 +1215,12 @@ function help() {
|
|
|
1065
1215
|
check-review Verify that review has been completed (used by PreToolUse hook)
|
|
1066
1216
|
compact-bash Rewrite bare Bash commands to reduce tokens (used by PreToolUse hook)
|
|
1067
1217
|
compact Subcommands for the compact-bash hook:
|
|
1068
|
-
compact stats -
|
|
1218
|
+
compact stats - Stats (hook + CodeGraph) and estimated savings
|
|
1219
|
+
compact log-codegraph-event - Log CodeGraph session (--skill --has-graph --tool-calls --tokens)
|
|
1069
1220
|
compact disable - Temporarily disable rewrite
|
|
1070
1221
|
compact enable - Re-enable rewrite
|
|
1071
|
-
compact clear-log - Clear
|
|
1222
|
+
compact clear-log - Clear compact.log
|
|
1223
|
+
compact codegraph-clear-log - Clear codegraph.log
|
|
1072
1224
|
bus Subcommands for the inter-agent chat room (refacil-bus):
|
|
1073
1225
|
bus start - Start the local broker (auto-spawn detached)
|
|
1074
1226
|
bus stop - Stop the broker
|
|
@@ -1076,15 +1228,17 @@ function help() {
|
|
|
1076
1228
|
bus serve - (internal) Run the broker in foreground
|
|
1077
1229
|
bus join --room <room> [--session <s>] [--intro "..."]
|
|
1078
1230
|
bus leave [--session <s>]
|
|
1079
|
-
bus say --text "..." [--session <s>]
|
|
1080
|
-
bus ask --to <name|all|*|everyone> --text "..." [--wait N] [--session <s>]
|
|
1081
|
-
bus reply --text "..." [--correlation <id>] [--to <name>]
|
|
1231
|
+
bus say --text "..." [--from-env VAR] [--session <s>]
|
|
1232
|
+
bus ask --to <name|all|*|everyone> --text "..." [--from-env VAR] [--wait N] [--session <s>]
|
|
1233
|
+
bus reply --text "..." [--from-env VAR] [--correlation <id>] [--to <name>]
|
|
1082
1234
|
bus history [--n N] [--session <s>]
|
|
1083
1235
|
bus inbox [--session <s>]
|
|
1084
1236
|
bus rooms
|
|
1085
1237
|
bus watch <session> [--room <room>] (live panel, no tokens)
|
|
1086
1238
|
bus attend [--timeout N] (listen for directed questions)
|
|
1087
1239
|
bus view (open the web UI in the browser)
|
|
1240
|
+
read-spec Read a Markdown spec aloud in the browser (on-device TTS):
|
|
1241
|
+
read-spec --file <path> [--lang es] [--voice M3] [--speed 1]
|
|
1088
1242
|
sdd Subcommands for managing SDD-AI artifacts in refacil-sdd/:
|
|
1089
1243
|
sdd new-change <name> Create a change with proposal/design/tasks/specs scaffold
|
|
1090
1244
|
sdd archive <name> Archive a change to refacil-sdd/changes/archive/
|
|
@@ -1099,6 +1253,14 @@ function help() {
|
|
|
1099
1253
|
[--base-branch <branch>] Base branch for new changes
|
|
1100
1254
|
[--protected-branches <csv>] Protected branches (comma-separated)
|
|
1101
1255
|
[--artifact-language <lang>] Artifact language: english (default) or spanish
|
|
1256
|
+
kapso Subcommands for Kapso WhatsApp notification setup:
|
|
1257
|
+
kapso setup Interactive setup of ~/.refacil-sdd-ai/kapso.env
|
|
1258
|
+
(KAPSO_API_KEY, KAPSO_PHONE_NUMBER_ID, NOTIFY_PHONE)
|
|
1259
|
+
kapso preflight Check credentials and print 24h window notice (--json)
|
|
1260
|
+
kapso notify Send a WhatsApp notification (success|failure) with flags
|
|
1261
|
+
autopilot Subcommands for the autopilot pipeline:
|
|
1262
|
+
autopilot ready-message Generate the pre-flight ready message
|
|
1263
|
+
(--change <name> [--ide <ide>] [--lang es|en])
|
|
1102
1264
|
clean Remove SDD-AI hooks from global IDE config dirs and skills from global dirs
|
|
1103
1265
|
help Show this help
|
|
1104
1266
|
|
|
@@ -1126,7 +1288,8 @@ function help() {
|
|
|
1126
1288
|
|
|
1127
1289
|
// --- Main ---
|
|
1128
1290
|
|
|
1129
|
-
const
|
|
1291
|
+
const args = process.argv.slice(2);
|
|
1292
|
+
const command = args[0] || 'help';
|
|
1130
1293
|
|
|
1131
1294
|
if (command === '--version' || command === '-v') {
|
|
1132
1295
|
console.log(getPackageVersion(packageRoot));
|
|
@@ -1162,14 +1325,163 @@ switch (command) {
|
|
|
1162
1325
|
compactBash.run();
|
|
1163
1326
|
break;
|
|
1164
1327
|
case 'compact':
|
|
1165
|
-
handleCompact(process.argv[3]);
|
|
1328
|
+
handleCompact(process.argv[3], process.argv.slice(4));
|
|
1166
1329
|
break;
|
|
1167
1330
|
case 'bus':
|
|
1168
1331
|
handleBus(process.argv[3], process.argv.slice(4), packageRoot);
|
|
1169
1332
|
break;
|
|
1333
|
+
case 'read-spec':
|
|
1334
|
+
handleReadSpec(process.argv.slice(3), packageRoot, projectRoot).catch((err) => {
|
|
1335
|
+
console.error(` Error: ${err.message}`);
|
|
1336
|
+
process.exit(1);
|
|
1337
|
+
});
|
|
1338
|
+
break;
|
|
1170
1339
|
case 'sdd':
|
|
1171
1340
|
handleSdd(process.argv[3], process.argv.slice(4), projectRoot);
|
|
1172
1341
|
break;
|
|
1342
|
+
case 'codegraph': {
|
|
1343
|
+
const cgSub = process.argv[3];
|
|
1344
|
+
if (cgSub === 'init') {
|
|
1345
|
+
codegraph.init(projectRoot);
|
|
1346
|
+
codegraph.registerMcp(readSelectedIDEs() || ['.claude', '.cursor', '.opencode', '.codex']);
|
|
1347
|
+
console.log('[refacil-sdd-ai] CodeGraph indexing started in background. Run your next /refacil:explore when it finishes.');
|
|
1348
|
+
} else if (cgSub === 'status') {
|
|
1349
|
+
const { loadBranchConfigWithSources } = require('../lib/config');
|
|
1350
|
+
const cfgInfo = loadBranchConfigWithSources(projectRoot);
|
|
1351
|
+
const installed = codegraph.isInstalled();
|
|
1352
|
+
const initialized = codegraph.isInitialized(projectRoot);
|
|
1353
|
+
if (process.argv.includes('--json')) {
|
|
1354
|
+
console.log(JSON.stringify({
|
|
1355
|
+
installed,
|
|
1356
|
+
initialized,
|
|
1357
|
+
mode: cfgInfo.codegraphMode,
|
|
1358
|
+
modeSource: cfgInfo.sources.codegraphMode,
|
|
1359
|
+
}));
|
|
1360
|
+
} else {
|
|
1361
|
+
console.log(' CodeGraph status:');
|
|
1362
|
+
console.log(` installed: ${installed}`);
|
|
1363
|
+
console.log(` initialized: ${initialized} (${projectRoot})`);
|
|
1364
|
+
console.log(` mode: ${cfgInfo.codegraphMode || '(not set)'} [source: ${cfgInfo.sources.codegraphMode}]`);
|
|
1365
|
+
}
|
|
1366
|
+
} else if (cgSub === 'setup') {
|
|
1367
|
+
// Install CLI if missing, register MCP, then index synchronously so the caller
|
|
1368
|
+
// (skill / agent) does not continue until .codegraph/ is fully built.
|
|
1369
|
+
const { execSync } = require('child_process');
|
|
1370
|
+
const selectedIDEs = readSelectedIDEs() || ['.claude', '.cursor', '.opencode', '.codex'];
|
|
1371
|
+
if (!codegraph.isInstalled()) {
|
|
1372
|
+
process.stdout.write('[refacil-sdd-ai] Installing @colbymchenry/codegraph globally...\n');
|
|
1373
|
+
try {
|
|
1374
|
+
execSync('npm install -g @colbymchenry/codegraph', { stdio: 'inherit', timeout: 120000 });
|
|
1375
|
+
process.stdout.write('[refacil-sdd-ai] @colbymchenry/codegraph installed.\n');
|
|
1376
|
+
} catch (err) {
|
|
1377
|
+
process.stderr.write(`[refacil-sdd-ai] Failed to install codegraph: ${err.message}\n`);
|
|
1378
|
+
process.exit(1);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
codegraph.registerMcp(selectedIDEs);
|
|
1382
|
+
if (!codegraph.isInitialized(projectRoot)) {
|
|
1383
|
+
process.stdout.write(`[refacil-sdd-ai] Initializing CodeGraph at ${projectRoot} ...\n`);
|
|
1384
|
+
try {
|
|
1385
|
+
// Step 1: init creates .codegraph/ structure (fast, ~1s).
|
|
1386
|
+
execSync('codegraph init', {
|
|
1387
|
+
stdio: 'inherit',
|
|
1388
|
+
cwd: projectRoot,
|
|
1389
|
+
timeout: 30000,
|
|
1390
|
+
shell: true,
|
|
1391
|
+
});
|
|
1392
|
+
process.stdout.write('[refacil-sdd-ai] Building index (this may take ~30s) ...\n');
|
|
1393
|
+
// Step 2: index builds the actual symbol graph.
|
|
1394
|
+
// Run synchronously with inherited stdio so the agent sees all output and
|
|
1395
|
+
// waits for the graph to be fully built before continuing.
|
|
1396
|
+
execSync('codegraph index', {
|
|
1397
|
+
stdio: 'inherit',
|
|
1398
|
+
cwd: projectRoot,
|
|
1399
|
+
timeout: 300000, // 5 min ceiling for large repos
|
|
1400
|
+
shell: true,
|
|
1401
|
+
});
|
|
1402
|
+
// Record timestamp so isStale() can detect subsequent commits
|
|
1403
|
+
try {
|
|
1404
|
+
const lastInitPath = path.join(projectRoot, '.codegraph', '.refacil-last-init');
|
|
1405
|
+
fs.mkdirSync(path.dirname(lastInitPath), { recursive: true });
|
|
1406
|
+
fs.writeFileSync(lastInitPath, new Date().toISOString());
|
|
1407
|
+
} catch (_) {}
|
|
1408
|
+
process.stdout.write('[refacil-sdd-ai] CodeGraph index complete. .codegraph/ is ready.\n');
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
process.stderr.write(`[refacil-sdd-ai] codegraph index failed: ${err.message}\n`);
|
|
1411
|
+
process.exit(1);
|
|
1412
|
+
}
|
|
1413
|
+
} else {
|
|
1414
|
+
console.log('[refacil-sdd-ai] CodeGraph already initialized for this repo. MCP registration refreshed.');
|
|
1415
|
+
}
|
|
1416
|
+
} else {
|
|
1417
|
+
console.error(` Unknown codegraph subcommand: ${cgSub || '(none)'}. Usage: refacil-sdd-ai codegraph <init|setup|status>`);
|
|
1418
|
+
process.exit(1);
|
|
1419
|
+
}
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
case 'kapso': {
|
|
1423
|
+
const kapsoSub = args[1];
|
|
1424
|
+
const kapso = require('../lib/kapso');
|
|
1425
|
+
if (kapsoSub === 'setup') {
|
|
1426
|
+
kapso.setup().catch((err) => {
|
|
1427
|
+
console.error(` Error during kapso setup: ${err.message}`);
|
|
1428
|
+
process.exit(1);
|
|
1429
|
+
});
|
|
1430
|
+
} else if (kapsoSub === 'preflight') {
|
|
1431
|
+
const result = kapso.preflight();
|
|
1432
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
1433
|
+
process.exit(0);
|
|
1434
|
+
} else if (kapsoSub === 'notify') {
|
|
1435
|
+
const notifyType = args[2]; // "success" or "failure"
|
|
1436
|
+
if (!notifyType) {
|
|
1437
|
+
console.error(' Usage: refacil-sdd-ai kapso notify <success|failure> [flags]');
|
|
1438
|
+
process.exit(1);
|
|
1439
|
+
}
|
|
1440
|
+
// Parse flags from process.argv
|
|
1441
|
+
const getFlag = (flag, fallback = '') => {
|
|
1442
|
+
const idx = process.argv.indexOf(flag);
|
|
1443
|
+
return idx !== -1 && process.argv[idx + 1] ? process.argv[idx + 1] : fallback;
|
|
1444
|
+
};
|
|
1445
|
+
// Compute duration from marker startedAt (authoritative) or fall back to --duration flag.
|
|
1446
|
+
let computedDuration = getFlag('--duration', '0');
|
|
1447
|
+
try {
|
|
1448
|
+
const markerPath = path.join(findProjectRoot(), 'refacil-sdd', '.autopilot-active');
|
|
1449
|
+
if (fs.existsSync(markerPath)) {
|
|
1450
|
+
const marker = JSON.parse(fs.readFileSync(markerPath, 'utf8'));
|
|
1451
|
+
if (marker.startedAt) {
|
|
1452
|
+
const elapsed = Math.round((Date.now() - new Date(marker.startedAt).getTime()) / 60000);
|
|
1453
|
+
computedDuration = String(elapsed);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
} catch (_) { /* keep fallback */ }
|
|
1457
|
+
const opts = {
|
|
1458
|
+
repo: getFlag('--repo'),
|
|
1459
|
+
change: getFlag('--change'),
|
|
1460
|
+
branch: getFlag('--branch'),
|
|
1461
|
+
tasks: getFlag('--tasks', '0/0'),
|
|
1462
|
+
duration: computedDuration,
|
|
1463
|
+
pr: getFlag('--pr') || null,
|
|
1464
|
+
apply: getFlag('--apply', '0'),
|
|
1465
|
+
test: getFlag('--test', '0'),
|
|
1466
|
+
review: getFlag('--review', '0'),
|
|
1467
|
+
warnings: getFlag('--warnings', ''),
|
|
1468
|
+
phase: getFlag('--phase'),
|
|
1469
|
+
lastCommit: getFlag('--last-commit'),
|
|
1470
|
+
error: getFlag('--error'),
|
|
1471
|
+
};
|
|
1472
|
+
kapso.notify(notifyType, opts).catch((err) => {
|
|
1473
|
+
console.error(` Error: ${err.message}`);
|
|
1474
|
+
process.exit(1);
|
|
1475
|
+
});
|
|
1476
|
+
} else {
|
|
1477
|
+
console.error(` Unknown kapso subcommand: ${kapsoSub || '(none)'}. Usage: kapso <setup|preflight|notify>`);
|
|
1478
|
+
process.exit(1);
|
|
1479
|
+
}
|
|
1480
|
+
break;
|
|
1481
|
+
}
|
|
1482
|
+
case 'autopilot':
|
|
1483
|
+
handleAutopilot(process.argv[3], process.argv.slice(4));
|
|
1484
|
+
break;
|
|
1173
1485
|
case 'clean':
|
|
1174
1486
|
clean();
|
|
1175
1487
|
break;
|