@imdeadpool/guardex 6.1.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -39
- package/bin/multiagent-safety.js +372 -296
- package/package.json +3 -5
- package/templates/AGENTS.multiagent-safety.md +9 -82
- package/templates/claude/commands/guardex.md +6 -12
- package/templates/codex/skills/guardex/SKILL.md +18 -64
- package/templates/githooks/post-merge +39 -3
- package/templates/githooks/pre-commit +27 -193
- package/templates/githooks/pre-push +0 -0
- package/templates/scripts/agent-branch-finish.sh +70 -702
- package/templates/scripts/agent-branch-start.sh +76 -877
- package/templates/scripts/agent-worktree-prune.sh +65 -353
- package/templates/scripts/codex-agent.sh +626 -238
- package/templates/scripts/install-agent-git-hooks.sh +4 -27
- package/templates/scripts/openspec/init-change-workspace.sh +4 -50
- package/templates/scripts/openspec/init-plan-workspace.sh +48 -495
- package/templates/scripts/review-bot-watch.sh +11 -11
package/bin/multiagent-safety.js
CHANGED
|
@@ -167,158 +167,84 @@ const COMMAND_TYPO_ALIASES = new Map([
|
|
|
167
167
|
const SUGGESTIBLE_COMMANDS = [
|
|
168
168
|
'status',
|
|
169
169
|
'setup',
|
|
170
|
-
'init',
|
|
171
170
|
'doctor',
|
|
172
|
-
'review',
|
|
173
171
|
'agents',
|
|
174
172
|
'finish',
|
|
175
173
|
'report',
|
|
176
|
-
'copy-prompt',
|
|
177
|
-
'copy-commands',
|
|
178
174
|
'protect',
|
|
179
175
|
'sync',
|
|
180
176
|
'cleanup',
|
|
181
|
-
'
|
|
177
|
+
'prompt',
|
|
178
|
+
'help',
|
|
179
|
+
'version',
|
|
180
|
+
// deprecated aliases still routable with a warning
|
|
181
|
+
'init',
|
|
182
182
|
'install',
|
|
183
183
|
'fix',
|
|
184
184
|
'scan',
|
|
185
|
+
'review',
|
|
186
|
+
'copy-prompt',
|
|
187
|
+
'copy-commands',
|
|
185
188
|
'print-agents-snippet',
|
|
186
|
-
'
|
|
187
|
-
'version',
|
|
189
|
+
'release',
|
|
188
190
|
];
|
|
189
191
|
const CLI_COMMAND_DESCRIPTIONS = [
|
|
190
192
|
['status', 'Show GuardeX CLI + service health without modifying files'],
|
|
191
|
-
['setup', 'Install
|
|
192
|
-
['
|
|
193
|
-
['doctor', 'Repair safety setup drift, then verify repo safety'],
|
|
194
|
-
['report', 'Generate security/safety reports (for example: OpenSSF scorecard)'],
|
|
195
|
-
['finish', 'Auto-commit completed agent branches, then run PR finish flow'],
|
|
196
|
-
['copy-prompt', 'Print the AI-ready setup checklist'],
|
|
197
|
-
['copy-commands', 'Print setup checklist as executable commands only'],
|
|
193
|
+
['setup', 'Install, repair, and verify guardrails (flags: --repair, --install-only, --target)'],
|
|
194
|
+
['doctor', 'Repair drift + verify (auto-sandboxes on protected main)'],
|
|
198
195
|
['protect', 'Manage protected branches (list/add/remove/set/reset)'],
|
|
199
|
-
['sync', '
|
|
200
|
-
['
|
|
196
|
+
['sync', 'Sync agent branches with origin/<base>'],
|
|
197
|
+
['finish', 'Commit + PR + merge completed agent branches (--all, --branch)'],
|
|
198
|
+
['cleanup', 'Prune merged/stale agent branches and worktrees'],
|
|
201
199
|
['agents', 'Start/stop repo-scoped review + cleanup bots'],
|
|
202
|
-
['
|
|
203
|
-
['
|
|
204
|
-
['scan', 'Report safety issues and exit non-zero on findings'],
|
|
205
|
-
['print-agents-snippet', 'Print the AGENTS.md snippet template'],
|
|
206
|
-
['release', 'Publish GuardeX from maintainer release repo'],
|
|
200
|
+
['prompt', 'Print AI setup checklist (--exec, --snippet)'],
|
|
201
|
+
['report', 'Security/safety reports (e.g. OpenSSF scorecard)'],
|
|
207
202
|
['help', 'Show this help output'],
|
|
208
203
|
['version', 'Print GuardeX version'],
|
|
209
204
|
];
|
|
210
|
-
const
|
|
211
|
-
'setup',
|
|
212
|
-
'
|
|
213
|
-
'
|
|
214
|
-
'
|
|
215
|
-
'
|
|
216
|
-
'
|
|
217
|
-
'
|
|
205
|
+
const DEPRECATED_COMMAND_ALIASES = new Map([
|
|
206
|
+
['init', { target: 'setup', hint: 'gx setup' }],
|
|
207
|
+
['install', { target: 'setup', hint: 'gx setup --install-only' }],
|
|
208
|
+
['fix', { target: 'setup', hint: 'gx setup --repair' }],
|
|
209
|
+
['scan', { target: 'status', hint: 'gx status --strict' }],
|
|
210
|
+
['copy-prompt', { target: 'prompt', hint: 'gx prompt' }],
|
|
211
|
+
['copy-commands', { target: 'prompt', hint: 'gx prompt --exec' }],
|
|
212
|
+
['print-agents-snippet', { target: 'prompt', hint: 'gx prompt --snippet' }],
|
|
213
|
+
['review', { target: 'agents', hint: 'gx agents start (runs review + cleanup)' }],
|
|
218
214
|
]);
|
|
219
215
|
const AGENT_BOT_DESCRIPTIONS = [
|
|
220
|
-
['
|
|
221
|
-
['agents', 'Start/stop both review and cleanup bots for this repo'],
|
|
216
|
+
['agents', 'Start/stop review + cleanup bots for this repo'],
|
|
222
217
|
];
|
|
223
218
|
|
|
224
|
-
const AI_SETUP_PROMPT = `
|
|
225
|
-
|
|
226
|
-
1) Install
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
4) Optional: start continuous PR monitor from this repo:
|
|
244
|
-
gx review --interval 30
|
|
245
|
-
|
|
246
|
-
5) Confirm next safe agent workflow commands:
|
|
247
|
-
bash scripts/codex-agent.sh "task" "agent-name"
|
|
248
|
-
bash scripts/agent-branch-start.sh "task" "agent-name"
|
|
249
|
-
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
|
|
250
|
-
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base dev --via-pr --wait-for-merge
|
|
251
|
-
- For every new user message/task, repeat the same cycle:
|
|
252
|
-
start isolated agent branch/worktree -> claim file locks -> implement/verify ->
|
|
253
|
-
finish via PR/merge cleanup into dev with scripts/agent-branch-finish.sh.
|
|
254
|
-
- Finished branches stay available by default for audit/follow-up.
|
|
255
|
-
Remove them explicitly when done:
|
|
256
|
-
gx cleanup --branch "$(git rev-parse --abbrev-ref HEAD)"
|
|
257
|
-
- To finalize all completed agent branches in one pass:
|
|
258
|
-
gx finish --all
|
|
259
|
-
|
|
260
|
-
6) OpenSpec default change flow (core profile):
|
|
261
|
-
/opsx:propose <change-name>
|
|
262
|
-
/opsx:apply
|
|
263
|
-
/opsx:archive
|
|
264
|
-
- Full guide: docs/openspec-getting-started.md
|
|
265
|
-
|
|
266
|
-
7) Optional: enable expanded OpenSpec workflow commands:
|
|
267
|
-
openspec config profile <profile-name>
|
|
268
|
-
openspec update
|
|
269
|
-
- Expanded path: /opsx:new -> /opsx:ff or /opsx:continue -> /opsx:apply -> /opsx:verify -> /opsx:archive
|
|
270
|
-
|
|
271
|
-
8) Optional: create OpenSpec planning workspace:
|
|
272
|
-
bash scripts/openspec/init-plan-workspace.sh "<plan-slug>"
|
|
273
|
-
|
|
274
|
-
9) Optional: protect extra branches:
|
|
275
|
-
gx protect add release staging
|
|
276
|
-
|
|
277
|
-
10) Optional: sync your current agent branch with latest base branch:
|
|
278
|
-
gx sync --check
|
|
279
|
-
gx sync
|
|
280
|
-
|
|
281
|
-
11) Optional (GitHub remote cleanup): enable:
|
|
282
|
-
Settings -> General -> Pull Requests -> Automatically delete head branches
|
|
283
|
-
|
|
284
|
-
12) Optional (fork sync with Pull app):
|
|
285
|
-
cp .github/pull.yml.example .github/pull.yml
|
|
286
|
-
# then edit .github/pull.yml:
|
|
287
|
-
# - set rules[].base to your fork branch (main/master/dev)
|
|
288
|
-
# - set rules[].upstream to upstream-owner:branch
|
|
289
|
-
# install app: https://github.com/apps/pull
|
|
290
|
-
# validate config: https://pull.git.ci/check/<owner>/<repo>
|
|
291
|
-
|
|
292
|
-
13) Optional (PR review bot with cr-gpt GitHub App):
|
|
293
|
-
- install app: https://github.com/apps/cr-gpt
|
|
294
|
-
- in GitHub repo Settings -> Secrets and variables -> Actions -> Variables:
|
|
295
|
-
add OPENAI_API_KEY (your API key)
|
|
296
|
-
- the app reviews new/updated pull requests automatically
|
|
297
|
-
|
|
298
|
-
14) Optional: test PR review action workflow
|
|
299
|
-
- gx setup installs .github/workflows/cr.yml
|
|
300
|
-
- open or update a PR
|
|
301
|
-
- check Actions -> "Code Review" run logs + PR timeline comments
|
|
219
|
+
const AI_SETUP_PROMPT = `GuardeX (gx) setup checklist for Codex/Claude in this repo.
|
|
220
|
+
|
|
221
|
+
1) Install: npm i -g @imdeadpool/guardex && gh --version
|
|
222
|
+
2) Bootstrap: gx setup # installs hooks/templates + verifies; prompts Y/N for global OMX/OpenSpec/codex-auth
|
|
223
|
+
3) If degraded: gx doctor # repair + re-verify
|
|
224
|
+
4) Per task: bash scripts/codex-agent.sh "<task>" "<agent>"
|
|
225
|
+
# or manual:
|
|
226
|
+
# bash scripts/agent-branch-start.sh "<task>" "<agent>"
|
|
227
|
+
# python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
|
|
228
|
+
# bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --via-pr --wait-for-merge
|
|
229
|
+
5) Finalize all: gx finish --all
|
|
230
|
+
6) Cleanup: gx cleanup
|
|
231
|
+
7) OpenSpec: /opsx:propose -> /opsx:apply -> /opsx:archive (see docs/openspec-getting-started.md)
|
|
232
|
+
8) Protect: gx protect add release staging (optional)
|
|
233
|
+
9) Sync: gx sync --check && gx sync (optional; rebase onto base)
|
|
234
|
+
10) Fork sync: cp .github/pull.yml.example .github/pull.yml (optional; install https://github.com/apps/pull)
|
|
235
|
+
11) PR review bot: install https://github.com/apps/cr-gpt + set OPENAI_API_KEY in Actions variables (uses .github/workflows/cr.yml)
|
|
236
|
+
12) GitHub repo: enable Settings -> PRs -> Automatically delete head branches
|
|
302
237
|
`;
|
|
303
238
|
|
|
304
239
|
const AI_SETUP_COMMANDS = `npm i -g @imdeadpool/guardex
|
|
305
240
|
gh --version
|
|
306
241
|
gx setup
|
|
307
242
|
gx doctor
|
|
308
|
-
|
|
309
|
-
bash scripts/codex-agent.sh "task" "agent-name"
|
|
310
|
-
bash scripts/agent-branch-start.sh "task" "agent-name"
|
|
311
|
-
python3 scripts/agent-file-locks.py claim --branch "$(git rev-parse --abbrev-ref HEAD)" <file...>
|
|
312
|
-
bash scripts/agent-branch-finish.sh --branch "$(git rev-parse --abbrev-ref HEAD)" --base dev --via-pr --wait-for-merge
|
|
243
|
+
bash scripts/codex-agent.sh "<task>" "<agent>"
|
|
313
244
|
gx finish --all
|
|
314
|
-
gx cleanup
|
|
315
|
-
bash scripts/openspec/init-plan-workspace.sh "<plan-slug>"
|
|
316
|
-
openspec config profile <profile-name>
|
|
317
|
-
openspec update
|
|
245
|
+
gx cleanup
|
|
318
246
|
gx protect add release staging
|
|
319
|
-
gx sync --check
|
|
320
247
|
gx sync
|
|
321
|
-
cp .github/pull.yml.example .github/pull.yml
|
|
322
248
|
`;
|
|
323
249
|
|
|
324
250
|
const SCORECARD_RISK_BY_CHECK = {
|
|
@@ -364,15 +290,12 @@ function statusDot(status) {
|
|
|
364
290
|
return colorize('●', '33'); // yellow for degraded/unknown
|
|
365
291
|
}
|
|
366
292
|
|
|
367
|
-
function commandCatalogLines(indent = ' '
|
|
368
|
-
const
|
|
369
|
-
? CLI_COMMAND_DESCRIPTIONS.filter(([name]) => CORE_COMMAND_NAMES.has(name))
|
|
370
|
-
: CLI_COMMAND_DESCRIPTIONS;
|
|
371
|
-
const maxCommandLength = entries.reduce(
|
|
293
|
+
function commandCatalogLines(indent = ' ') {
|
|
294
|
+
const maxCommandLength = CLI_COMMAND_DESCRIPTIONS.reduce(
|
|
372
295
|
(max, [command]) => Math.max(max, command.length),
|
|
373
296
|
0,
|
|
374
297
|
);
|
|
375
|
-
return
|
|
298
|
+
return CLI_COMMAND_DESCRIPTIONS.map(
|
|
376
299
|
([command, description]) => `${indent}${command.padEnd(maxCommandLength + 2)}${description}`,
|
|
377
300
|
);
|
|
378
301
|
}
|
|
@@ -389,7 +312,7 @@ function agentBotCatalogLines(indent = ' ') {
|
|
|
389
312
|
|
|
390
313
|
function printToolLogsSummary() {
|
|
391
314
|
const usageLine = ` $ ${SHORT_TOOL_NAME} <command> [options]`;
|
|
392
|
-
const commandDetails = commandCatalogLines(' '
|
|
315
|
+
const commandDetails = commandCatalogLines(' ');
|
|
393
316
|
const agentBotDetails = agentBotCatalogLines(' ');
|
|
394
317
|
|
|
395
318
|
if (!supportsAnsiColors()) {
|
|
@@ -434,7 +357,7 @@ function printToolLogsSummary() {
|
|
|
434
357
|
}
|
|
435
358
|
console.log(` ${pipe}${line.slice(2)}`);
|
|
436
359
|
}
|
|
437
|
-
console.log(` ${corner}─ ${colorize(`Try '${
|
|
360
|
+
console.log(` ${corner}─ ${colorize(`Try '${TOOL_NAME} doctor' for one-step repair + verification.`, '2')}`);
|
|
438
361
|
}
|
|
439
362
|
|
|
440
363
|
function usage(options = {}) {
|
|
@@ -455,19 +378,12 @@ AGENT BOT
|
|
|
455
378
|
${agentBotCatalogLines().join('\n')}
|
|
456
379
|
|
|
457
380
|
NOTES
|
|
458
|
-
-
|
|
459
|
-
-
|
|
460
|
-
-
|
|
461
|
-
-
|
|
462
|
-
- ${
|
|
463
|
-
-
|
|
464
|
-
- Optional parent-folder Source Control view: ${SHORT_TOOL_NAME} setup --target <repo-path> --parent-workspace-view
|
|
465
|
-
- In initialized repos, setup/install/fix block in-place writes on protected main by default
|
|
466
|
-
- setup/doctor auto-finish clean pending agent/* branches via PR flow into the current local base branch
|
|
467
|
-
- doctor auto-runs in a sandbox agent branch/worktree on protected main and tries auto-finish PR flow
|
|
468
|
-
- agent-branch-finish merges by default and keeps agent branches/worktrees until explicit cleanup
|
|
469
|
-
- use '${SHORT_TOOL_NAME} cleanup' to remove merged agent branches/worktrees (optionally remote refs too)
|
|
470
|
-
- Legacy command aliases are still supported: ${LEGACY_NAMES.join(', ')}`);
|
|
381
|
+
- No command = ${SHORT_TOOL_NAME} status. ${SHORT_TOOL_NAME} init is an alias of ${SHORT_TOOL_NAME} setup.
|
|
382
|
+
- Global installs need Y/N approval; GitHub CLI (gh) is required for PR automation.
|
|
383
|
+
- Target another repo: ${SHORT_TOOL_NAME} <cmd> --target <repo-path>.
|
|
384
|
+
- On protected main, setup/install/fix/doctor auto-sandbox via agent branch + PR flow.
|
|
385
|
+
- Run '${SHORT_TOOL_NAME} cleanup' to prune merged agent branches/worktrees.
|
|
386
|
+
- Legacy aliases: ${LEGACY_NAMES.join(', ')}.`);
|
|
471
387
|
|
|
472
388
|
if (outsideGitRepo) {
|
|
473
389
|
console.log(`
|
|
@@ -513,6 +429,90 @@ function isGitRepo(targetPath) {
|
|
|
513
429
|
return result.status === 0;
|
|
514
430
|
}
|
|
515
431
|
|
|
432
|
+
const NESTED_REPO_DEFAULT_MAX_DEPTH = 6;
|
|
433
|
+
const NESTED_REPO_DEFAULT_SKIP_DIRS = new Set([
|
|
434
|
+
'node_modules',
|
|
435
|
+
'.git',
|
|
436
|
+
'dist',
|
|
437
|
+
'build',
|
|
438
|
+
'.next',
|
|
439
|
+
'.cache',
|
|
440
|
+
'target',
|
|
441
|
+
'vendor',
|
|
442
|
+
'.venv',
|
|
443
|
+
'.pnpm-store',
|
|
444
|
+
]);
|
|
445
|
+
const NESTED_REPO_WORKTREE_RELATIVE_DIR = path.join('.omx', 'agent-worktrees');
|
|
446
|
+
|
|
447
|
+
function discoverNestedGitRepos(rootPath, opts = {}) {
|
|
448
|
+
const maxDepth = Number.isFinite(opts.maxDepth) ? Math.max(1, opts.maxDepth) : NESTED_REPO_DEFAULT_MAX_DEPTH;
|
|
449
|
+
const extraSkip = new Set(Array.isArray(opts.extraSkip) ? opts.extraSkip : []);
|
|
450
|
+
const includeSubmodules = Boolean(opts.includeSubmodules);
|
|
451
|
+
const resolvedRoot = path.resolve(rootPath);
|
|
452
|
+
|
|
453
|
+
const rootCommonDir = (() => {
|
|
454
|
+
const result = run('git', ['-C', resolvedRoot, 'rev-parse', '--git-common-dir'], { cwd: resolvedRoot });
|
|
455
|
+
if (result.status !== 0) return null;
|
|
456
|
+
const raw = result.stdout.trim();
|
|
457
|
+
if (!raw) return null;
|
|
458
|
+
return path.resolve(resolvedRoot, raw);
|
|
459
|
+
})();
|
|
460
|
+
|
|
461
|
+
const workreeSkipAbsolute = path.join(resolvedRoot, NESTED_REPO_WORKTREE_RELATIVE_DIR);
|
|
462
|
+
const found = new Set();
|
|
463
|
+
found.add(resolvedRoot);
|
|
464
|
+
|
|
465
|
+
function shouldSkipDir(dirName) {
|
|
466
|
+
return NESTED_REPO_DEFAULT_SKIP_DIRS.has(dirName) || extraSkip.has(dirName);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function walk(currentPath, depth) {
|
|
470
|
+
if (depth > maxDepth) return;
|
|
471
|
+
let entries;
|
|
472
|
+
try {
|
|
473
|
+
entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
474
|
+
} catch {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
for (const entry of entries) {
|
|
479
|
+
const entryPath = path.join(currentPath, entry.name);
|
|
480
|
+
|
|
481
|
+
if (entry.name === '.git') {
|
|
482
|
+
if (entry.isDirectory()) {
|
|
483
|
+
if (entryPath === path.join(resolvedRoot, '.git')) continue;
|
|
484
|
+
found.add(path.dirname(entryPath));
|
|
485
|
+
} else if (includeSubmodules && entry.isFile()) {
|
|
486
|
+
found.add(path.dirname(entryPath));
|
|
487
|
+
}
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
492
|
+
if (shouldSkipDir(entry.name)) continue;
|
|
493
|
+
if (entryPath === workreeSkipAbsolute) continue;
|
|
494
|
+
walk(entryPath, depth + 1);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
walk(resolvedRoot, 0);
|
|
499
|
+
|
|
500
|
+
const filtered = Array.from(found).filter((repoPath) => {
|
|
501
|
+
if (repoPath === resolvedRoot) return true;
|
|
502
|
+
if (!rootCommonDir) return true;
|
|
503
|
+
const childResult = run('git', ['-C', repoPath, 'rev-parse', '--git-common-dir'], { cwd: repoPath });
|
|
504
|
+
if (childResult.status !== 0) return true;
|
|
505
|
+
const childCommonDirRaw = childResult.stdout.trim();
|
|
506
|
+
if (!childCommonDirRaw) return true;
|
|
507
|
+
const childCommonDir = path.resolve(repoPath, childCommonDirRaw);
|
|
508
|
+
return childCommonDir !== rootCommonDir;
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
const [root, ...rest] = filtered;
|
|
512
|
+
rest.sort((a, b) => a.localeCompare(b));
|
|
513
|
+
return [root, ...rest];
|
|
514
|
+
}
|
|
515
|
+
|
|
516
516
|
function toDestinationPath(relativeTemplatePath) {
|
|
517
517
|
if (relativeTemplatePath.startsWith('scripts/')) {
|
|
518
518
|
return relativeTemplatePath;
|
|
@@ -709,7 +709,8 @@ function writeLockState(repoRoot, payload, dryRun) {
|
|
|
709
709
|
fs.writeFileSync(lockPath, JSON.stringify(payload, null, 2) + '\n', 'utf8');
|
|
710
710
|
}
|
|
711
711
|
|
|
712
|
-
function ensurePackageScripts(repoRoot, dryRun) {
|
|
712
|
+
function ensurePackageScripts(repoRoot, dryRun, options = {}) {
|
|
713
|
+
const force = Boolean(options.force);
|
|
713
714
|
const packagePath = path.join(repoRoot, 'package.json');
|
|
714
715
|
if (!fs.existsSync(packagePath)) {
|
|
715
716
|
return { status: 'skipped', file: 'package.json', note: 'package.json not found' };
|
|
@@ -722,30 +723,15 @@ function ensurePackageScripts(repoRoot, dryRun) {
|
|
|
722
723
|
throw new Error(`Unable to parse package.json in target repo: ${error.message}`);
|
|
723
724
|
}
|
|
724
725
|
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
'
|
|
731
|
-
|
|
732
|
-
'agent:hooks:install': 'bash ./scripts/install-agent-git-hooks.sh',
|
|
733
|
-
'agent:locks:claim': 'python3 ./scripts/agent-file-locks.py claim',
|
|
734
|
-
'agent:locks:allow-delete': 'python3 ./scripts/agent-file-locks.py allow-delete',
|
|
735
|
-
'agent:locks:release': 'python3 ./scripts/agent-file-locks.py release',
|
|
736
|
-
'agent:locks:status': 'python3 ./scripts/agent-file-locks.py status',
|
|
737
|
-
'agent:plan:init': 'bash ./scripts/openspec/init-plan-workspace.sh',
|
|
738
|
-
'agent:change:init': 'bash ./scripts/openspec/init-change-workspace.sh',
|
|
739
|
-
'agent:protect:list': `${SHORT_TOOL_NAME} protect list`,
|
|
740
|
-
'agent:branch:sync': `${SHORT_TOOL_NAME} sync`,
|
|
741
|
-
'agent:branch:sync:check': `${SHORT_TOOL_NAME} sync --check`,
|
|
742
|
-
'agent:safety:setup': `${SHORT_TOOL_NAME} setup`,
|
|
743
|
-
'agent:safety:scan': `${SHORT_TOOL_NAME} scan`,
|
|
744
|
-
'agent:safety:fix': `${SHORT_TOOL_NAME} fix`,
|
|
745
|
-
'agent:safety:doctor': `${SHORT_TOOL_NAME} doctor`,
|
|
746
|
-
};
|
|
726
|
+
const existingScripts = pkg.scripts && typeof pkg.scripts === 'object'
|
|
727
|
+
? pkg.scripts
|
|
728
|
+
: {};
|
|
729
|
+
const hasExistingAgentScripts = Object.keys(existingScripts).some((key) => key.startsWith('agent:'));
|
|
730
|
+
if (hasExistingAgentScripts && !force) {
|
|
731
|
+
return { status: 'unchanged', file: 'package.json', note: 'preserved existing agent:* scripts' };
|
|
732
|
+
}
|
|
747
733
|
|
|
748
|
-
pkg.scripts =
|
|
734
|
+
pkg.scripts = existingScripts;
|
|
749
735
|
let changed = false;
|
|
750
736
|
for (const [key, value] of Object.entries(REQUIRED_PACKAGE_SCRIPTS)) {
|
|
751
737
|
if (pkg.scripts[key] !== value) {
|
|
@@ -765,7 +751,8 @@ function ensurePackageScripts(repoRoot, dryRun) {
|
|
|
765
751
|
return { status: 'updated', file: 'package.json' };
|
|
766
752
|
}
|
|
767
753
|
|
|
768
|
-
function ensureAgentsSnippet(repoRoot, dryRun) {
|
|
754
|
+
function ensureAgentsSnippet(repoRoot, dryRun, options = {}) {
|
|
755
|
+
const force = Boolean(options.force);
|
|
769
756
|
const agentsPath = path.join(repoRoot, 'AGENTS.md');
|
|
770
757
|
const snippet = fs.readFileSync(path.join(TEMPLATE_ROOT, 'AGENTS.multiagent-safety.md'), 'utf8').trimEnd();
|
|
771
758
|
const managedRegex = new RegExp(
|
|
@@ -782,6 +769,9 @@ function ensureAgentsSnippet(repoRoot, dryRun) {
|
|
|
782
769
|
|
|
783
770
|
const existing = fs.readFileSync(agentsPath, 'utf8');
|
|
784
771
|
if (managedRegex.test(existing)) {
|
|
772
|
+
if (!force) {
|
|
773
|
+
return { status: 'unchanged', file: 'AGENTS.md', note: 'preserved existing guardex-managed block' };
|
|
774
|
+
}
|
|
785
775
|
const next = existing.replace(managedRegex, snippet);
|
|
786
776
|
if (next === existing) {
|
|
787
777
|
return { status: 'unchanged', file: 'AGENTS.md' };
|
|
@@ -925,10 +915,18 @@ function parseCommonArgs(rawArgs, defaults) {
|
|
|
925
915
|
}
|
|
926
916
|
|
|
927
917
|
function parseSetupArgs(rawArgs, defaults) {
|
|
928
|
-
const setupDefaults = {
|
|
918
|
+
const setupDefaults = {
|
|
919
|
+
...defaults,
|
|
920
|
+
parentWorkspaceView: false,
|
|
921
|
+
recursive: true,
|
|
922
|
+
nestedMaxDepth: NESTED_REPO_DEFAULT_MAX_DEPTH,
|
|
923
|
+
nestedSkipDirs: [],
|
|
924
|
+
includeSubmodules: false,
|
|
925
|
+
};
|
|
929
926
|
const forwardedArgs = [];
|
|
930
927
|
|
|
931
|
-
for (
|
|
928
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
929
|
+
const arg = rawArgs[index];
|
|
932
930
|
if (arg === '--parent-workspace-view') {
|
|
933
931
|
setupDefaults.parentWorkspaceView = true;
|
|
934
932
|
continue;
|
|
@@ -937,6 +935,34 @@ function parseSetupArgs(rawArgs, defaults) {
|
|
|
937
935
|
setupDefaults.parentWorkspaceView = false;
|
|
938
936
|
continue;
|
|
939
937
|
}
|
|
938
|
+
if (arg === '--no-recursive' || arg === '--no-nested' || arg === '--single-repo') {
|
|
939
|
+
setupDefaults.recursive = false;
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
if (arg === '--recursive' || arg === '--nested') {
|
|
943
|
+
setupDefaults.recursive = true;
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
if (arg === '--max-depth') {
|
|
947
|
+
const raw = requireValue(rawArgs, index, '--max-depth');
|
|
948
|
+
const parsed = Number.parseInt(raw, 10);
|
|
949
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
950
|
+
throw new Error('--max-depth requires a positive integer');
|
|
951
|
+
}
|
|
952
|
+
setupDefaults.nestedMaxDepth = parsed;
|
|
953
|
+
index += 1;
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
if (arg === '--skip-nested') {
|
|
957
|
+
const raw = requireValue(rawArgs, index, '--skip-nested');
|
|
958
|
+
setupDefaults.nestedSkipDirs.push(raw);
|
|
959
|
+
index += 1;
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
if (arg === '--include-submodules') {
|
|
963
|
+
setupDefaults.includeSubmodules = true;
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
940
966
|
forwardedArgs.push(arg);
|
|
941
967
|
}
|
|
942
968
|
|
|
@@ -1092,6 +1118,7 @@ function resolveSandboxTarget(repoRoot, worktreePath, targetPath) {
|
|
|
1092
1118
|
function buildSandboxDoctorArgs(options, sandboxTarget) {
|
|
1093
1119
|
const args = ['doctor', '--target', sandboxTarget];
|
|
1094
1120
|
if (options.dryRun) args.push('--dry-run');
|
|
1121
|
+
if (options.force) args.push('--force');
|
|
1095
1122
|
if (options.skipAgents) args.push('--skip-agents');
|
|
1096
1123
|
if (options.skipPackageJson) args.push('--skip-package-json');
|
|
1097
1124
|
if (options.skipGitignore) args.push('--no-gitignore');
|
|
@@ -3528,11 +3555,11 @@ function runInstallInternal(options) {
|
|
|
3528
3555
|
}
|
|
3529
3556
|
|
|
3530
3557
|
if (!options.skipPackageJson) {
|
|
3531
|
-
operations.push(ensurePackageScripts(repoRoot, Boolean(options.dryRun)));
|
|
3558
|
+
operations.push(ensurePackageScripts(repoRoot, Boolean(options.dryRun), { force: Boolean(options.force) }));
|
|
3532
3559
|
}
|
|
3533
3560
|
|
|
3534
3561
|
if (!options.skipAgents) {
|
|
3535
|
-
operations.push(ensureAgentsSnippet(repoRoot, Boolean(options.dryRun)));
|
|
3562
|
+
operations.push(ensureAgentsSnippet(repoRoot, Boolean(options.dryRun), { force: Boolean(options.force) }));
|
|
3536
3563
|
}
|
|
3537
3564
|
|
|
3538
3565
|
const hookResult = configureHooks(repoRoot, Boolean(options.dryRun));
|
|
@@ -3582,11 +3609,11 @@ function runFixInternal(options) {
|
|
|
3582
3609
|
}
|
|
3583
3610
|
|
|
3584
3611
|
if (!options.skipPackageJson) {
|
|
3585
|
-
operations.push(ensurePackageScripts(repoRoot, Boolean(options.dryRun)));
|
|
3612
|
+
operations.push(ensurePackageScripts(repoRoot, Boolean(options.dryRun), { force: Boolean(options.force) }));
|
|
3586
3613
|
}
|
|
3587
3614
|
|
|
3588
3615
|
if (!options.skipAgents) {
|
|
3589
|
-
operations.push(ensureAgentsSnippet(repoRoot, Boolean(options.dryRun)));
|
|
3616
|
+
operations.push(ensureAgentsSnippet(repoRoot, Boolean(options.dryRun), { force: Boolean(options.force) }));
|
|
3590
3617
|
}
|
|
3591
3618
|
|
|
3592
3619
|
const hookResult = configureHooks(repoRoot, Boolean(options.dryRun));
|
|
@@ -4453,23 +4480,87 @@ function setup(rawArgs) {
|
|
|
4453
4480
|
}
|
|
4454
4481
|
}
|
|
4455
4482
|
|
|
4456
|
-
|
|
4457
|
-
const
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4483
|
+
const topRepoRoot = resolveRepoRoot(options.target);
|
|
4484
|
+
const discoveredRepos = options.recursive
|
|
4485
|
+
? discoverNestedGitRepos(topRepoRoot, {
|
|
4486
|
+
maxDepth: options.nestedMaxDepth,
|
|
4487
|
+
extraSkip: options.nestedSkipDirs,
|
|
4488
|
+
includeSubmodules: options.includeSubmodules,
|
|
4489
|
+
})
|
|
4490
|
+
: [topRepoRoot];
|
|
4491
|
+
|
|
4492
|
+
if (discoveredRepos.length > 1) {
|
|
4493
|
+
console.log(
|
|
4494
|
+
`[${TOOL_NAME}] Detected ${discoveredRepos.length} git repos under ${topRepoRoot}. Installing into each (use --no-recursive to limit to the top-level).`,
|
|
4495
|
+
);
|
|
4496
|
+
for (const repoPath of discoveredRepos) {
|
|
4497
|
+
const marker = repoPath === topRepoRoot ? ' (top-level)' : '';
|
|
4498
|
+
console.log(`[${TOOL_NAME}] - ${repoPath}${marker}`);
|
|
4499
|
+
}
|
|
4461
4500
|
}
|
|
4462
|
-
printOperations('Setup/install', installPayload, options.dryRun);
|
|
4463
4501
|
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4502
|
+
let aggregateErrors = 0;
|
|
4503
|
+
let aggregateWarnings = 0;
|
|
4504
|
+
let lastScanResult = null;
|
|
4505
|
+
|
|
4506
|
+
for (const repoPath of discoveredRepos) {
|
|
4507
|
+
const perRepoOptions = { ...options, target: repoPath };
|
|
4508
|
+
const repoLabel = discoveredRepos.length > 1 ? ` [${path.relative(topRepoRoot, repoPath) || '.'}]` : '';
|
|
4509
|
+
|
|
4510
|
+
if (discoveredRepos.length > 1) {
|
|
4511
|
+
console.log(`[${TOOL_NAME}] ── Setup target: ${repoPath} ──`);
|
|
4512
|
+
}
|
|
4513
|
+
|
|
4514
|
+
assertProtectedMainWriteAllowed(perRepoOptions, 'setup');
|
|
4515
|
+
const installPayload = runInstallInternal(perRepoOptions);
|
|
4516
|
+
installPayload.operations.push(ensureSetupProtectedBranches(installPayload.repoRoot, Boolean(perRepoOptions.dryRun)));
|
|
4517
|
+
if (perRepoOptions.parentWorkspaceView) {
|
|
4518
|
+
installPayload.operations.push(ensureParentWorkspaceView(installPayload.repoRoot, Boolean(perRepoOptions.dryRun)));
|
|
4519
|
+
}
|
|
4520
|
+
printOperations(`Setup/install${repoLabel}`, installPayload, perRepoOptions.dryRun);
|
|
4521
|
+
|
|
4522
|
+
const fixPayload = runFixInternal({
|
|
4523
|
+
target: repoPath,
|
|
4524
|
+
dryRun: perRepoOptions.dryRun,
|
|
4525
|
+
force: perRepoOptions.force,
|
|
4526
|
+
dropStaleLocks: true,
|
|
4527
|
+
skipAgents: perRepoOptions.skipAgents,
|
|
4528
|
+
skipPackageJson: perRepoOptions.skipPackageJson,
|
|
4529
|
+
skipGitignore: perRepoOptions.skipGitignore,
|
|
4530
|
+
});
|
|
4531
|
+
printOperations(`Setup/fix${repoLabel}`, fixPayload, perRepoOptions.dryRun);
|
|
4532
|
+
|
|
4533
|
+
if (perRepoOptions.dryRun) {
|
|
4534
|
+
continue;
|
|
4535
|
+
}
|
|
4536
|
+
|
|
4537
|
+
if (perRepoOptions.parentWorkspaceView) {
|
|
4538
|
+
const parentWorkspace = buildParentWorkspaceView(installPayload.repoRoot);
|
|
4539
|
+
console.log(`[${TOOL_NAME}] Parent workspace view: ${parentWorkspace.workspacePath}`);
|
|
4540
|
+
}
|
|
4541
|
+
|
|
4542
|
+
const scanResult = runScanInternal({ target: repoPath, json: false });
|
|
4543
|
+
const currentBaseBranch = currentBranchName(scanResult.repoRoot);
|
|
4544
|
+
const autoFinishSummary = autoFinishReadyAgentBranches(scanResult.repoRoot, {
|
|
4545
|
+
baseBranch: currentBaseBranch,
|
|
4546
|
+
dryRun: perRepoOptions.dryRun,
|
|
4547
|
+
});
|
|
4548
|
+
printScanResult(scanResult, false);
|
|
4549
|
+
if (autoFinishSummary.enabled) {
|
|
4550
|
+
console.log(
|
|
4551
|
+
`[${TOOL_NAME}] Auto-finish sweep (base=${currentBaseBranch}): attempted=${autoFinishSummary.attempted}, completed=${autoFinishSummary.completed}, skipped=${autoFinishSummary.skipped}, failed=${autoFinishSummary.failed}`,
|
|
4552
|
+
);
|
|
4553
|
+
for (const detail of autoFinishSummary.details) {
|
|
4554
|
+
console.log(`[${TOOL_NAME}] ${detail}`);
|
|
4555
|
+
}
|
|
4556
|
+
} else if (autoFinishSummary.details.length > 0) {
|
|
4557
|
+
console.log(`[${TOOL_NAME}] ${autoFinishSummary.details[0]}`);
|
|
4558
|
+
}
|
|
4559
|
+
|
|
4560
|
+
aggregateErrors += scanResult.errors;
|
|
4561
|
+
aggregateWarnings += scanResult.warnings;
|
|
4562
|
+
lastScanResult = scanResult;
|
|
4563
|
+
}
|
|
4473
4564
|
|
|
4474
4565
|
if (options.dryRun) {
|
|
4475
4566
|
console.log(`[${TOOL_NAME}] Dry run setup done.`);
|
|
@@ -4477,32 +4568,11 @@ function setup(rawArgs) {
|
|
|
4477
4568
|
return;
|
|
4478
4569
|
}
|
|
4479
4570
|
|
|
4480
|
-
if (
|
|
4481
|
-
const
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
const scanResult = runScanInternal({ target: options.target, json: false });
|
|
4486
|
-
const currentBaseBranch = currentBranchName(scanResult.repoRoot);
|
|
4487
|
-
const autoFinishSummary = autoFinishReadyAgentBranches(scanResult.repoRoot, {
|
|
4488
|
-
baseBranch: currentBaseBranch,
|
|
4489
|
-
dryRun: options.dryRun,
|
|
4490
|
-
});
|
|
4491
|
-
printScanResult(scanResult, false);
|
|
4492
|
-
if (autoFinishSummary.enabled) {
|
|
4493
|
-
console.log(
|
|
4494
|
-
`[${TOOL_NAME}] Auto-finish sweep (base=${currentBaseBranch}): attempted=${autoFinishSummary.attempted}, completed=${autoFinishSummary.completed}, skipped=${autoFinishSummary.skipped}, failed=${autoFinishSummary.failed}`,
|
|
4495
|
-
);
|
|
4496
|
-
for (const detail of autoFinishSummary.details) {
|
|
4497
|
-
console.log(`[${TOOL_NAME}] ${detail}`);
|
|
4498
|
-
}
|
|
4499
|
-
} else if (autoFinishSummary.details.length > 0) {
|
|
4500
|
-
console.log(`[${TOOL_NAME}] ${autoFinishSummary.details[0]}`);
|
|
4501
|
-
}
|
|
4502
|
-
|
|
4503
|
-
if (scanResult.errors === 0 && scanResult.warnings === 0) {
|
|
4504
|
-
console.log(`[${TOOL_NAME}] ✅ Setup complete.`);
|
|
4505
|
-
console.log(`[${TOOL_NAME}] Copy AI setup prompt with: ${SHORT_TOOL_NAME} copy-prompt`);
|
|
4571
|
+
if (aggregateErrors === 0 && aggregateWarnings === 0) {
|
|
4572
|
+
const repoCount = discoveredRepos.length;
|
|
4573
|
+
const suffix = repoCount > 1 ? ` (${repoCount} repos)` : '';
|
|
4574
|
+
console.log(`[${TOOL_NAME}] ✅ Setup complete.${suffix}`);
|
|
4575
|
+
console.log(`[${TOOL_NAME}] Copy AI setup prompt with: ${SHORT_TOOL_NAME} prompt`);
|
|
4506
4576
|
console.log(
|
|
4507
4577
|
`[${TOOL_NAME}] OpenSpec core workflow: /opsx:propose -> /opsx:apply -> /opsx:archive`,
|
|
4508
4578
|
);
|
|
@@ -4512,7 +4582,13 @@ function setup(rawArgs) {
|
|
|
4512
4582
|
console.log(`[${TOOL_NAME}] OpenSpec guide: docs/openspec-getting-started.md`);
|
|
4513
4583
|
}
|
|
4514
4584
|
|
|
4515
|
-
|
|
4585
|
+
if (lastScanResult) {
|
|
4586
|
+
setExitCodeFromScan({
|
|
4587
|
+
...lastScanResult,
|
|
4588
|
+
errors: aggregateErrors,
|
|
4589
|
+
warnings: aggregateWarnings,
|
|
4590
|
+
});
|
|
4591
|
+
}
|
|
4516
4592
|
}
|
|
4517
4593
|
|
|
4518
4594
|
function ensureMainBranch(repoRoot) {
|
|
@@ -4811,6 +4887,31 @@ function copyCommands() {
|
|
|
4811
4887
|
process.exitCode = 0;
|
|
4812
4888
|
}
|
|
4813
4889
|
|
|
4890
|
+
function prompt(rawArgs) {
|
|
4891
|
+
const args = Array.isArray(rawArgs) ? rawArgs : [];
|
|
4892
|
+
let variant = 'prompt';
|
|
4893
|
+
for (const arg of args) {
|
|
4894
|
+
if (arg === '--exec' || arg === '--commands') variant = 'exec';
|
|
4895
|
+
else if (arg === '--snippet' || arg === '--agents') variant = 'snippet';
|
|
4896
|
+
else if (arg === '--prompt' || arg === '--full') variant = 'prompt';
|
|
4897
|
+
else if (arg === '-h' || arg === '--help') variant = 'help';
|
|
4898
|
+
else throw new Error(`Unknown option: ${arg}`);
|
|
4899
|
+
}
|
|
4900
|
+
if (variant === 'help') {
|
|
4901
|
+
console.log(
|
|
4902
|
+
`${SHORT_TOOL_NAME} prompt commands:\n` +
|
|
4903
|
+
` ${SHORT_TOOL_NAME} prompt Print AI setup checklist\n` +
|
|
4904
|
+
` ${SHORT_TOOL_NAME} prompt --exec Print setup commands only (shell-ready)\n` +
|
|
4905
|
+
` ${SHORT_TOOL_NAME} prompt --snippet Print the AGENTS.md managed-block template`,
|
|
4906
|
+
);
|
|
4907
|
+
process.exitCode = 0;
|
|
4908
|
+
return;
|
|
4909
|
+
}
|
|
4910
|
+
if (variant === 'exec') return copyCommands();
|
|
4911
|
+
if (variant === 'snippet') return printAgentsSnippet();
|
|
4912
|
+
return copyPrompt();
|
|
4913
|
+
}
|
|
4914
|
+
|
|
4814
4915
|
function cleanup(rawArgs) {
|
|
4815
4916
|
const options = parseCleanupArgs(rawArgs);
|
|
4816
4917
|
const repoRoot = resolveRepoRoot(options.target);
|
|
@@ -5289,6 +5390,29 @@ function normalizeCommandOrThrow(command) {
|
|
|
5289
5390
|
return command;
|
|
5290
5391
|
}
|
|
5291
5392
|
|
|
5393
|
+
function warnDeprecatedAlias(aliasName) {
|
|
5394
|
+
const entry = DEPRECATED_COMMAND_ALIASES.get(aliasName);
|
|
5395
|
+
if (!entry) return;
|
|
5396
|
+
console.error(
|
|
5397
|
+
`[${TOOL_NAME}] '${aliasName}' is deprecated and will be removed in a future major release. ` +
|
|
5398
|
+
`Use: ${entry.hint}`,
|
|
5399
|
+
);
|
|
5400
|
+
}
|
|
5401
|
+
|
|
5402
|
+
function extractFlag(args, ...names) {
|
|
5403
|
+
const flagSet = new Set(names);
|
|
5404
|
+
let found = false;
|
|
5405
|
+
const remaining = [];
|
|
5406
|
+
for (const arg of args) {
|
|
5407
|
+
if (flagSet.has(arg)) {
|
|
5408
|
+
found = true;
|
|
5409
|
+
} else {
|
|
5410
|
+
remaining.push(arg);
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
return { found, remaining };
|
|
5414
|
+
}
|
|
5415
|
+
|
|
5292
5416
|
function main() {
|
|
5293
5417
|
const args = process.argv.slice(2);
|
|
5294
5418
|
|
|
@@ -5312,90 +5436,42 @@ function main() {
|
|
|
5312
5436
|
return;
|
|
5313
5437
|
}
|
|
5314
5438
|
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
return;
|
|
5439
|
+
// Deprecated direct aliases — route to new surface and warn once.
|
|
5440
|
+
if (DEPRECATED_COMMAND_ALIASES.has(command)) {
|
|
5441
|
+
warnDeprecatedAlias(command);
|
|
5442
|
+
if (command === 'init') return setup(rest);
|
|
5443
|
+
if (command === 'install') return install(rest);
|
|
5444
|
+
if (command === 'fix') return fix(rest);
|
|
5445
|
+
if (command === 'scan') return scan(rest);
|
|
5446
|
+
if (command === 'copy-prompt') return copyPrompt();
|
|
5447
|
+
if (command === 'copy-commands') return copyCommands();
|
|
5448
|
+
if (command === 'print-agents-snippet') return printAgentsSnippet();
|
|
5449
|
+
if (command === 'review') return review(rest);
|
|
5323
5450
|
}
|
|
5324
5451
|
|
|
5325
|
-
if (command === '
|
|
5326
|
-
|
|
5327
|
-
return;
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
if (command === '
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
if (command === '
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
}
|
|
5349
|
-
|
|
5350
|
-
if (command === 'copy-prompt') {
|
|
5351
|
-
copyPrompt();
|
|
5352
|
-
return;
|
|
5353
|
-
}
|
|
5354
|
-
|
|
5355
|
-
if (command === 'copy-commands') {
|
|
5356
|
-
copyCommands();
|
|
5357
|
-
return;
|
|
5358
|
-
}
|
|
5359
|
-
|
|
5360
|
-
if (command === 'protect') {
|
|
5361
|
-
protect(rest);
|
|
5362
|
-
return;
|
|
5363
|
-
}
|
|
5364
|
-
|
|
5365
|
-
if (command === 'sync') {
|
|
5366
|
-
sync(rest);
|
|
5367
|
-
return;
|
|
5368
|
-
}
|
|
5369
|
-
|
|
5370
|
-
if (command === 'cleanup') {
|
|
5371
|
-
cleanup(rest);
|
|
5372
|
-
return;
|
|
5373
|
-
}
|
|
5374
|
-
|
|
5375
|
-
if (command === 'release') {
|
|
5376
|
-
release(rest);
|
|
5377
|
-
return;
|
|
5378
|
-
}
|
|
5379
|
-
|
|
5380
|
-
if (command === 'install') {
|
|
5381
|
-
install(rest);
|
|
5382
|
-
return;
|
|
5383
|
-
}
|
|
5384
|
-
|
|
5385
|
-
if (command === 'fix') {
|
|
5386
|
-
fix(rest);
|
|
5387
|
-
return;
|
|
5388
|
-
}
|
|
5389
|
-
|
|
5390
|
-
if (command === 'scan') {
|
|
5391
|
-
scan(rest);
|
|
5392
|
-
return;
|
|
5393
|
-
}
|
|
5394
|
-
|
|
5395
|
-
if (command === 'print-agents-snippet') {
|
|
5396
|
-
printAgentsSnippet();
|
|
5397
|
-
return;
|
|
5398
|
-
}
|
|
5452
|
+
if (command === 'status') {
|
|
5453
|
+
const { found: strict, remaining } = extractFlag(rest, '--strict');
|
|
5454
|
+
if (strict) return scan(remaining);
|
|
5455
|
+
return status(remaining);
|
|
5456
|
+
}
|
|
5457
|
+
|
|
5458
|
+
if (command === 'setup') {
|
|
5459
|
+
const installOnly = extractFlag(rest, '--install-only', '--only-install');
|
|
5460
|
+
if (installOnly.found) return install(installOnly.remaining);
|
|
5461
|
+
const repairOnly = extractFlag(installOnly.remaining, '--repair', '--fix-only');
|
|
5462
|
+
if (repairOnly.found) return fix(repairOnly.remaining);
|
|
5463
|
+
return setup(repairOnly.remaining);
|
|
5464
|
+
}
|
|
5465
|
+
|
|
5466
|
+
if (command === 'prompt') return prompt(rest);
|
|
5467
|
+
if (command === 'doctor') return doctor(rest);
|
|
5468
|
+
if (command === 'agents') return agents(rest);
|
|
5469
|
+
if (command === 'finish') return finish(rest);
|
|
5470
|
+
if (command === 'report') return report(rest);
|
|
5471
|
+
if (command === 'protect') return protect(rest);
|
|
5472
|
+
if (command === 'sync') return sync(rest);
|
|
5473
|
+
if (command === 'cleanup') return cleanup(rest);
|
|
5474
|
+
if (command === 'release') return release(rest);
|
|
5399
5475
|
|
|
5400
5476
|
const suggestion = maybeSuggestCommand(command);
|
|
5401
5477
|
if (suggestion) {
|