@imdeadpool/guardex 7.0.41 → 7.1.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.
Files changed (118) hide show
  1. package/README.md +94 -13
  2. package/package.json +3 -1
  3. package/skills/gitguardex/SKILL.md +13 -0
  4. package/skills/guardex-merge-skills-to-dev/SKILL.md +59 -0
  5. package/skills/gx-act/SKILL.md +82 -0
  6. package/src/agents/cleanup-sessions.js +126 -0
  7. package/src/agents/finish.js +172 -0
  8. package/src/agents/inspect.js +202 -0
  9. package/src/agents/launch.js +249 -0
  10. package/src/agents/registry.js +133 -0
  11. package/src/agents/selection-panel.js +571 -0
  12. package/src/agents/sessions.js +151 -0
  13. package/src/agents/start.js +591 -0
  14. package/src/agents/status.js +146 -0
  15. package/src/agents/terminal.js +152 -0
  16. package/src/budget/index.js +344 -0
  17. package/src/ci-init/index.js +265 -0
  18. package/src/cli/args.js +357 -3
  19. package/src/cli/commands/agents.js +364 -0
  20. package/src/cli/commands/bootstrap.js +92 -0
  21. package/src/cli/commands/branch.js +127 -0
  22. package/src/cli/commands/claude.js +674 -0
  23. package/src/cli/commands/doctor.js +268 -0
  24. package/src/cli/commands/finish.js +26 -0
  25. package/src/cli/commands/mcp.js +122 -0
  26. package/src/cli/commands/misc.js +304 -0
  27. package/src/cli/commands/pr.js +439 -0
  28. package/src/cli/commands/prompt.js +92 -0
  29. package/src/cli/commands/release.js +305 -0
  30. package/src/cli/commands/report.js +244 -0
  31. package/src/cli/commands/review.js +32 -0
  32. package/src/cli/commands/setup.js +242 -0
  33. package/src/cli/commands/status.js +338 -0
  34. package/src/cli/commands/watch.js +234 -0
  35. package/src/cli/main.js +85 -3613
  36. package/src/cli/shared/repo-env.js +161 -0
  37. package/src/cli/shared/sandbox.js +417 -0
  38. package/src/cli/shared/scaffolding.js +535 -0
  39. package/src/cli/shared/toolchain-shims.js +420 -0
  40. package/src/cockpit/action-runner.js +3 -0
  41. package/src/cockpit/actions.js +80 -0
  42. package/src/cockpit/control.js +1121 -0
  43. package/src/cockpit/index.js +426 -0
  44. package/src/cockpit/kitty-layout.js +549 -0
  45. package/src/cockpit/kitty-tree.js +144 -0
  46. package/src/cockpit/logs-reader.js +182 -0
  47. package/src/cockpit/menu.js +204 -0
  48. package/src/cockpit/pane-actions.js +597 -0
  49. package/src/cockpit/pane-menu.js +387 -0
  50. package/src/cockpit/projects-finder.js +178 -0
  51. package/src/cockpit/render.js +215 -0
  52. package/src/cockpit/settings-render.js +128 -0
  53. package/src/cockpit/settings.js +124 -0
  54. package/src/cockpit/shortcuts.js +24 -0
  55. package/src/cockpit/sidebar.js +311 -0
  56. package/src/cockpit/state.js +72 -0
  57. package/src/cockpit/theme.js +128 -0
  58. package/src/cockpit/welcome.js +266 -0
  59. package/src/context.js +304 -43
  60. package/src/core/runtime.js +6 -1
  61. package/src/doctor/index.js +45 -15
  62. package/src/finish/index.js +186 -7
  63. package/src/finish/preflight.js +177 -0
  64. package/src/finish/review-gate.js +182 -0
  65. package/src/git/index.js +511 -4
  66. package/src/hooks/index.js +0 -64
  67. package/src/kitty/command.js +101 -0
  68. package/src/kitty/runtime.js +250 -0
  69. package/src/mcp/collect.js +370 -0
  70. package/src/mcp/server.js +157 -0
  71. package/src/output/index.js +68 -2
  72. package/src/pr-review.js +264 -0
  73. package/src/pr.js +381 -0
  74. package/src/sandbox/index.js +13 -2
  75. package/src/scaffold/agent-worktree-prep.js +213 -0
  76. package/src/scaffold/index.js +127 -10
  77. package/src/speckit/index.js +226 -0
  78. package/src/submodule/index.js +288 -0
  79. package/src/terminal/index.js +45 -0
  80. package/src/terminal/kitty.js +622 -0
  81. package/src/terminal/tmux.js +125 -0
  82. package/src/tmux/command.js +27 -0
  83. package/src/tmux/session.js +89 -0
  84. package/src/toolchain/index.js +20 -0
  85. package/templates/AGENTS.monorepo-apps.md +26 -0
  86. package/templates/AGENTS.multiagent-safety.md +63 -323
  87. package/templates/AGENTS.multiagent-safety.min.md +11 -0
  88. package/templates/codex/skills/gitguardex/SKILL.md +2 -0
  89. package/templates/codex/skills/gx-act/SKILL.md +82 -0
  90. package/templates/githooks/pre-commit +44 -20
  91. package/templates/github/workflows/README.md +87 -0
  92. package/templates/github/workflows/ci-full.yml +55 -0
  93. package/templates/github/workflows/ci.yml +56 -0
  94. package/templates/github/workflows/cr.yml +20 -1
  95. package/templates/scripts/agent-branch-finish.sh +519 -23
  96. package/templates/scripts/agent-branch-merge.sh +4 -1
  97. package/templates/scripts/agent-branch-start.sh +176 -24
  98. package/templates/scripts/agent-preflight.sh +115 -0
  99. package/templates/scripts/agent-worktree-prune.sh +96 -5
  100. package/templates/scripts/codex-agent.sh +41 -97
  101. package/templates/scripts/openspec/init-plan-workspace.sh +43 -0
  102. package/templates/scripts/review-bot-watch.sh +31 -2
  103. package/templates/scripts/agent-session-state.js +0 -171
  104. package/templates/scripts/install-vscode-active-agents-extension.js +0 -135
  105. package/templates/vscode/guardex-active-agents/README.md +0 -34
  106. package/templates/vscode/guardex-active-agents/extension.js +0 -3782
  107. package/templates/vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json +0 -54
  108. package/templates/vscode/guardex-active-agents/fileicons/icons/agent.svg +0 -5
  109. package/templates/vscode/guardex-active-agents/fileicons/icons/branch.svg +0 -7
  110. package/templates/vscode/guardex-active-agents/fileicons/icons/config.svg +0 -4
  111. package/templates/vscode/guardex-active-agents/fileicons/icons/hook.svg +0 -4
  112. package/templates/vscode/guardex-active-agents/fileicons/icons/openspec.svg +0 -5
  113. package/templates/vscode/guardex-active-agents/fileicons/icons/plan.svg +0 -4
  114. package/templates/vscode/guardex-active-agents/fileicons/icons/spec.svg +0 -5
  115. package/templates/vscode/guardex-active-agents/icon.png +0 -0
  116. package/templates/vscode/guardex-active-agents/media/active-agents-hivemind.svg +0 -14
  117. package/templates/vscode/guardex-active-agents/package.json +0 -169
  118. package/templates/vscode/guardex-active-agents/session-schema.js +0 -1348
@@ -0,0 +1,265 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const cp = require('node:child_process');
6
+
7
+ const { TEMPLATE_FILES, toDestinationPath, TEMPLATE_ROOT, PACKAGE_ROOT } = require('../context');
8
+
9
+ const TOOL_NAME = 'gx';
10
+
11
+ const WORKFLOW_TEMPLATE_PREFIX = 'github/workflows/';
12
+
13
+ function listWorkflowTemplates() {
14
+ return TEMPLATE_FILES.filter((entry) => entry.startsWith(WORKFLOW_TEMPLATE_PREFIX));
15
+ }
16
+
17
+ function resolveTemplateSource(relativeTemplatePath) {
18
+ const localTemplate = path.join(TEMPLATE_ROOT, relativeTemplatePath);
19
+ if (fs.existsSync(localTemplate)) return localTemplate;
20
+ const packageTemplate = path.join(PACKAGE_ROOT, 'templates', relativeTemplatePath);
21
+ if (fs.existsSync(packageTemplate)) return packageTemplate;
22
+ return null;
23
+ }
24
+
25
+ function copyFileEnsuringDir(sourcePath, destinationAbsolute) {
26
+ fs.mkdirSync(path.dirname(destinationAbsolute), { recursive: true });
27
+ fs.copyFileSync(sourcePath, destinationAbsolute);
28
+ }
29
+
30
+ function shouldCopy(destinationAbsolute, options) {
31
+ if (!fs.existsSync(destinationAbsolute)) return { copy: true, reason: 'create' };
32
+ if (options.force) return { copy: true, reason: 'overwrite' };
33
+ return { copy: false, reason: 'exists' };
34
+ }
35
+
36
+ function planCiInitOperations(options) {
37
+ const targetRoot = path.resolve(options.target || process.cwd());
38
+ const operations = [];
39
+ for (const templateRelative of listWorkflowTemplates()) {
40
+ const destinationRelative = toDestinationPath(templateRelative);
41
+ const destinationAbsolute = path.join(targetRoot, destinationRelative);
42
+ const sourcePath = resolveTemplateSource(templateRelative);
43
+ if (!sourcePath) {
44
+ operations.push({
45
+ template: templateRelative,
46
+ destination: destinationRelative,
47
+ status: 'missing-source',
48
+ });
49
+ continue;
50
+ }
51
+ const decision = shouldCopy(destinationAbsolute, options);
52
+ operations.push({
53
+ template: templateRelative,
54
+ source: sourcePath,
55
+ destination: destinationRelative,
56
+ destinationAbsolute,
57
+ status: decision.copy ? decision.reason : 'skipped',
58
+ });
59
+ }
60
+ return { targetRoot, operations };
61
+ }
62
+
63
+ function performCiInitOperations(operations, { dryRun }) {
64
+ const summary = { copied: [], overwritten: [], skipped: [], missing: [] };
65
+ for (const op of operations) {
66
+ if (op.status === 'missing-source') {
67
+ summary.missing.push(op.template);
68
+ continue;
69
+ }
70
+ if (op.status === 'skipped') {
71
+ summary.skipped.push(op.destination);
72
+ continue;
73
+ }
74
+ if (!dryRun) {
75
+ copyFileEnsuringDir(op.source, op.destinationAbsolute);
76
+ }
77
+ if (op.status === 'overwrite') {
78
+ summary.overwritten.push(op.destination);
79
+ } else {
80
+ summary.copied.push(op.destination);
81
+ }
82
+ }
83
+ return summary;
84
+ }
85
+
86
+ function maybeStageOnAgentBranch(targetRoot, summary, options) {
87
+ if (options.dryRun || options.noStage) return null;
88
+ if (!summary.copied.length && !summary.overwritten.length) return null;
89
+ // Best-effort stage: only when the target is itself a git repo. Failures
90
+ // are non-fatal — the user can always `git add` themselves.
91
+ const isGit = cp.spawnSync('git', ['-C', targetRoot, 'rev-parse', '--is-inside-work-tree'], {
92
+ encoding: 'utf8',
93
+ });
94
+ if (isGit.status !== 0) return { staged: false, reason: 'target is not a git repo' };
95
+ const files = [...summary.copied, ...summary.overwritten];
96
+ const add = cp.spawnSync('git', ['-C', targetRoot, 'add', '--', ...files], { encoding: 'utf8' });
97
+ if (add.status !== 0) {
98
+ return { staged: false, reason: (add.stderr || add.stdout || '').trim() };
99
+ }
100
+ return { staged: true, count: files.length };
101
+ }
102
+
103
+ function formatCiInitReport({ targetRoot, summary, stageResult, dryRun }) {
104
+ const lines = [];
105
+ const mode = dryRun ? 'dry-run' : 'apply';
106
+ lines.push(`${TOOL_NAME} ci-init (${mode}) — target: ${targetRoot}`);
107
+ if (summary.copied.length) {
108
+ lines.push(` created (${summary.copied.length}):`);
109
+ for (const file of summary.copied) lines.push(` + ${file}`);
110
+ }
111
+ if (summary.overwritten.length) {
112
+ lines.push(` overwritten (${summary.overwritten.length}):`);
113
+ for (const file of summary.overwritten) lines.push(` ~ ${file}`);
114
+ }
115
+ if (summary.skipped.length) {
116
+ lines.push(` skipped (already exists, pass --force to overwrite):`);
117
+ for (const file of summary.skipped) lines.push(` = ${file}`);
118
+ }
119
+ if (summary.missing.length) {
120
+ lines.push(` missing source (${summary.missing.length}):`);
121
+ for (const file of summary.missing) lines.push(` ? ${file}`);
122
+ }
123
+ if (stageResult) {
124
+ if (stageResult.staged) {
125
+ lines.push(` staged ${stageResult.count} file(s) for commit.`);
126
+ } else {
127
+ lines.push(` not staged: ${stageResult.reason}`);
128
+ }
129
+ }
130
+ if (dryRun) {
131
+ lines.push(` (no files written; re-run without --dry-run to apply)`);
132
+ }
133
+ return lines.join('\n');
134
+ }
135
+
136
+ function parseCiInitArgs(rawArgs) {
137
+ const options = {
138
+ target: null,
139
+ dryRun: false,
140
+ force: false,
141
+ json: false,
142
+ noStage: false,
143
+ help: false,
144
+ };
145
+ const args = Array.isArray(rawArgs) ? [...rawArgs] : [];
146
+ while (args.length > 0) {
147
+ const arg = args.shift();
148
+ if (arg === '--help' || arg === '-h' || arg === 'help') {
149
+ options.help = true;
150
+ continue;
151
+ }
152
+ if (arg === '--dry-run') {
153
+ options.dryRun = true;
154
+ continue;
155
+ }
156
+ if (arg === '--force') {
157
+ options.force = true;
158
+ continue;
159
+ }
160
+ if (arg === '--json') {
161
+ options.json = true;
162
+ continue;
163
+ }
164
+ if (arg === '--no-stage') {
165
+ options.noStage = true;
166
+ continue;
167
+ }
168
+ if (arg === '--target') {
169
+ options.target = args.shift();
170
+ continue;
171
+ }
172
+ if (arg.startsWith('--target=')) {
173
+ options.target = arg.slice('--target='.length);
174
+ continue;
175
+ }
176
+ const err = new Error(`Unknown ci-init argument: ${arg}`);
177
+ err.code = 'CI_INIT_BAD_ARG';
178
+ throw err;
179
+ }
180
+ return options;
181
+ }
182
+
183
+ function renderCiInitHelp() {
184
+ return [
185
+ `${TOOL_NAME} ci-init — scaffold budget-friendly GitHub Actions workflows into a target repo.`,
186
+ '',
187
+ 'Usage:',
188
+ ` ${TOOL_NAME} ci-init [--target <path>] [--dry-run] [--force] [--no-stage] [--json]`,
189
+ '',
190
+ 'Options:',
191
+ ` --target <path> Repo to scaffold into (default: current working directory).`,
192
+ ` --dry-run Show what would be written; do not touch the filesystem.`,
193
+ ` --force Overwrite existing files instead of skipping them.`,
194
+ ` --no-stage Skip the post-copy 'git add' step.`,
195
+ ` --json Emit a structured summary instead of the text report.`,
196
+ '',
197
+ 'Files copied (from gitguardex templates/github/workflows/):',
198
+ ` - ci.yml PR-time CI with draft-skip + concurrency-cancel.`,
199
+ ` - ci-full.yml Weekly cross-runtime matrix + label opt-in.`,
200
+ ` - cr.yml AI code review with agent/* + draft skip.`,
201
+ ` - README.md Documents the budget posture and customization knobs.`,
202
+ '',
203
+ 'The command stages copied files with git add when the target is a git repo;',
204
+ 'pair with `gx branch start "<task>" "claude-code"` to land them on a new agent',
205
+ 'branch instead of the primary checkout.',
206
+ ].join('\n');
207
+ }
208
+
209
+ function runCiInitCommand(rawArgs) {
210
+ let options;
211
+ try {
212
+ options = parseCiInitArgs(rawArgs);
213
+ } catch (err) {
214
+ console.error(`[${TOOL_NAME}] ${err.message}`);
215
+ console.error(renderCiInitHelp());
216
+ process.exitCode = 1;
217
+ return;
218
+ }
219
+
220
+ if (options.help) {
221
+ console.log(renderCiInitHelp());
222
+ return;
223
+ }
224
+
225
+ const { targetRoot, operations } = planCiInitOperations(options);
226
+ const summary = performCiInitOperations(operations, { dryRun: options.dryRun });
227
+ const stageResult =
228
+ summary.copied.length || summary.overwritten.length
229
+ ? maybeStageOnAgentBranch(targetRoot, summary, options)
230
+ : null;
231
+
232
+ if (options.json) {
233
+ process.stdout.write(
234
+ `${JSON.stringify(
235
+ {
236
+ targetRoot,
237
+ dryRun: options.dryRun,
238
+ force: options.force,
239
+ summary,
240
+ stageResult,
241
+ },
242
+ null,
243
+ 2,
244
+ )}\n`,
245
+ );
246
+ } else {
247
+ console.log(formatCiInitReport({ targetRoot, summary, stageResult, dryRun: options.dryRun }));
248
+ }
249
+
250
+ if (summary.missing.length > 0) {
251
+ process.exitCode = 1;
252
+ } else {
253
+ process.exitCode = 0;
254
+ }
255
+ }
256
+
257
+ module.exports = {
258
+ runCiInitCommand,
259
+ parseCiInitArgs,
260
+ planCiInitOperations,
261
+ performCiInitOperations,
262
+ formatCiInitReport,
263
+ renderCiInitHelp,
264
+ listWorkflowTemplates,
265
+ };
package/src/cli/args.js CHANGED
@@ -108,6 +108,14 @@ function parseCommonArgs(rawArgs, defaults) {
108
108
  options.allowProtectedBaseWrite = true;
109
109
  continue;
110
110
  }
111
+ if (arg === '--contract' || arg === '--full') {
112
+ options.contract = true;
113
+ continue;
114
+ }
115
+ if (arg === '--minimal' || arg === '--no-contract') {
116
+ options.contract = false;
117
+ continue;
118
+ }
111
119
  if (Object.prototype.hasOwnProperty.call(options, 'waitForMerge') && arg === '--wait-for-merge') {
112
120
  options.waitForMerge = true;
113
121
  continue;
@@ -177,6 +185,8 @@ function parseSetupArgs(rawArgs, defaults) {
177
185
  const setupDefaults = {
178
186
  ...defaults,
179
187
  parentWorkspaceView: false,
188
+ speckit: true,
189
+ speckitForce: false,
180
190
  };
181
191
  const forwardedArgs = [];
182
192
 
@@ -190,6 +200,19 @@ function parseSetupArgs(rawArgs, defaults) {
190
200
  setupDefaults.parentWorkspaceView = false;
191
201
  continue;
192
202
  }
203
+ if (arg === '--no-speckit' || arg === '--skip-speckit') {
204
+ setupDefaults.speckit = false;
205
+ continue;
206
+ }
207
+ if (arg === '--speckit') {
208
+ setupDefaults.speckit = true;
209
+ continue;
210
+ }
211
+ if (arg === '--speckit-force' || arg === '--reinstall-speckit') {
212
+ setupDefaults.speckit = true;
213
+ setupDefaults.speckitForce = true;
214
+ continue;
215
+ }
193
216
  forwardedArgs.push(arg);
194
217
  }
195
218
 
@@ -261,6 +284,69 @@ function parseReviewArgs(rawArgs) {
261
284
  };
262
285
  }
263
286
 
287
+ function parsePrReviewArgs(rawArgs) {
288
+ const parsed = parseTargetFlag(rawArgs, process.cwd());
289
+ const options = {
290
+ target: parsed.target,
291
+ provider: 'codex',
292
+ pr: '',
293
+ post: false,
294
+ artifact: '',
295
+ timeoutMs: 10 * 60 * 1000,
296
+ };
297
+
298
+ for (let index = 0; index < parsed.args.length; index += 1) {
299
+ const arg = parsed.args[index];
300
+ if (arg === '--provider') {
301
+ const next = requireValue(parsed.args, index, '--provider');
302
+ if (!['codex', 'claude'].includes(next)) {
303
+ throw new Error(`Invalid --provider value: ${next} (expected codex|claude)`);
304
+ }
305
+ options.provider = next;
306
+ index += 1;
307
+ continue;
308
+ }
309
+ if (arg === '--pr') {
310
+ options.pr = requireValue(parsed.args, index, '--pr');
311
+ index += 1;
312
+ continue;
313
+ }
314
+ if (arg === '--post') {
315
+ options.post = true;
316
+ continue;
317
+ }
318
+ if (arg === '--no-post') {
319
+ options.post = false;
320
+ continue;
321
+ }
322
+ if (arg === '--artifact' || arg === '--output') {
323
+ options.artifact = requireValue(parsed.args, index, arg);
324
+ index += 1;
325
+ continue;
326
+ }
327
+ if (arg === '--timeout-ms') {
328
+ const raw = requireValue(parsed.args, index, '--timeout-ms');
329
+ const parsedTimeout = Number.parseInt(raw, 10);
330
+ if (!Number.isFinite(parsedTimeout) || parsedTimeout <= 0) {
331
+ throw new Error('--timeout-ms requires a positive integer');
332
+ }
333
+ options.timeoutMs = parsedTimeout;
334
+ index += 1;
335
+ continue;
336
+ }
337
+ throw new Error(`Unknown option: ${arg}`);
338
+ }
339
+
340
+ if (!options.pr) {
341
+ throw new Error('--pr requires a pull request number');
342
+ }
343
+ if (!/^\d+$/.test(String(options.pr))) {
344
+ throw new Error(`--pr must be a pull request number (received: ${options.pr})`);
345
+ }
346
+
347
+ return options;
348
+ }
349
+
264
350
  function parseAgentsArgs(rawArgs) {
265
351
  const parsed = parseTargetFlag(rawArgs, process.cwd());
266
352
  const [subcommandRaw = '', ...rest] = parsed.args;
@@ -268,14 +354,77 @@ function parseAgentsArgs(rawArgs) {
268
354
  const options = {
269
355
  target: parsed.target,
270
356
  subcommand,
357
+ task: '',
358
+ agent: '',
359
+ base: '',
360
+ claims: [],
361
+ count: 1,
362
+ agentSelectionSpecs: [],
363
+ panel: false,
364
+ terminal: process.env.GUARDEX_AGENT_TERMINAL || 'kitty',
365
+ dryRun: false,
271
366
  reviewIntervalSeconds: 30,
272
367
  cleanupIntervalSeconds: 60,
273
368
  idleMinutes: DEFAULT_SHADOW_CLEANUP_IDLE_MINUTES,
369
+ staleAgeMinutes: 24 * 60,
274
370
  pid: null,
371
+ branch: '',
372
+ json: false,
373
+ sessionId: '',
374
+ finishArgs: [],
375
+ metadata: {},
275
376
  };
377
+ let terminalProvided = false;
276
378
 
277
379
  for (let index = 0; index < rest.length; index += 1) {
278
380
  const arg = rest[index];
381
+ if (subcommand === 'finish') {
382
+ if (arg === '--json') {
383
+ options.json = true;
384
+ continue;
385
+ }
386
+ if (arg === '--session') {
387
+ const next = rest[index + 1];
388
+ if (!next) {
389
+ throw new Error('--session requires an agent session id');
390
+ }
391
+ options.sessionId = next;
392
+ index += 1;
393
+ continue;
394
+ }
395
+ if (arg === '--branch') {
396
+ const next = rest[index + 1];
397
+ if (!next) {
398
+ throw new Error('--branch requires an agent branch name');
399
+ }
400
+ options.branch = next;
401
+ index += 1;
402
+ continue;
403
+ }
404
+ options.finishArgs.push(arg);
405
+ if (
406
+ ['--base', '--commit-message', '--mode'].includes(arg) &&
407
+ rest[index + 1] &&
408
+ !rest[index + 1].startsWith('-')
409
+ ) {
410
+ options.finishArgs.push(rest[index + 1]);
411
+ index += 1;
412
+ }
413
+ continue;
414
+ }
415
+ if (arg === '--branch') {
416
+ const next = rest[index + 1];
417
+ if (!next) {
418
+ throw new Error('--branch requires an agent branch name');
419
+ }
420
+ options.branch = next;
421
+ index += 1;
422
+ continue;
423
+ }
424
+ if (arg === '--json') {
425
+ options.json = true;
426
+ continue;
427
+ }
279
428
  if (arg === '--review-interval') {
280
429
  const next = rest[index + 1];
281
430
  if (!next) {
@@ -315,6 +464,19 @@ function parseAgentsArgs(rawArgs) {
315
464
  index += 1;
316
465
  continue;
317
466
  }
467
+ if (arg === '--older-than-minutes') {
468
+ const next = rest[index + 1];
469
+ if (!next) {
470
+ throw new Error('--older-than-minutes requires an integer minutes value');
471
+ }
472
+ const parsedValue = Number.parseInt(next, 10);
473
+ if (!Number.isInteger(parsedValue) || parsedValue < 1) {
474
+ throw new Error('--older-than-minutes must be an integer >= 1');
475
+ }
476
+ options.staleAgeMinutes = parsedValue;
477
+ index += 1;
478
+ continue;
479
+ }
318
480
  if (arg === '--pid') {
319
481
  const next = rest[index + 1];
320
482
  if (!next) {
@@ -328,15 +490,170 @@ function parseAgentsArgs(rawArgs) {
328
490
  index += 1;
329
491
  continue;
330
492
  }
493
+ if (arg === '--agent') {
494
+ const next = rest[index + 1];
495
+ if (!next || next.startsWith('-')) {
496
+ throw new Error('--agent requires an agent id');
497
+ }
498
+ options.agent = next;
499
+ index += 1;
500
+ continue;
501
+ }
502
+ if (arg === '--agents') {
503
+ const next = rest[index + 1];
504
+ if (!next || next.startsWith('-')) {
505
+ throw new Error('--agents requires a comma-separated agent list');
506
+ }
507
+ options.agentSelectionSpecs.push(next);
508
+ index += 1;
509
+ continue;
510
+ }
511
+ if (arg === '--count') {
512
+ const next = rest[index + 1];
513
+ if (!next || next.startsWith('-')) {
514
+ throw new Error('--count requires a positive integer');
515
+ }
516
+ const parsedValue = Number.parseInt(next, 10);
517
+ if (!Number.isInteger(parsedValue) || parsedValue < 1) {
518
+ throw new Error('--count requires a positive integer');
519
+ }
520
+ options.count = parsedValue;
521
+ index += 1;
522
+ continue;
523
+ }
524
+ if (arg === '--codex-count' || arg === '--codex-accounts') {
525
+ const next = rest[index + 1];
526
+ if (!next || next.startsWith('-')) {
527
+ throw new Error(`${arg} requires a positive integer`);
528
+ }
529
+ const parsedValue = Number.parseInt(next, 10);
530
+ if (!Number.isInteger(parsedValue) || parsedValue < 1) {
531
+ throw new Error(`${arg} requires a positive integer`);
532
+ }
533
+ options.agent = 'codex';
534
+ options.count = parsedValue;
535
+ index += 1;
536
+ continue;
537
+ }
538
+ if (arg === '--panel') {
539
+ options.panel = true;
540
+ continue;
541
+ }
542
+ if (arg === '--terminal') {
543
+ const next = rest[index + 1];
544
+ if (!next || next.startsWith('-')) {
545
+ throw new Error('--terminal requires kitty or none');
546
+ }
547
+ options.terminal = next;
548
+ terminalProvided = true;
549
+ index += 1;
550
+ continue;
551
+ }
552
+ if (arg === '--base') {
553
+ const next = rest[index + 1];
554
+ if (!next || next.startsWith('-')) {
555
+ throw new Error('--base requires a branch name');
556
+ }
557
+ options.base = next;
558
+ index += 1;
559
+ continue;
560
+ }
561
+ if (arg === '--dry-run') {
562
+ options.dryRun = true;
563
+ continue;
564
+ }
565
+ if (arg === '--claim') {
566
+ const next = rest[index + 1];
567
+ if (!next || next.startsWith('-')) {
568
+ throw new Error('--claim requires a file path');
569
+ }
570
+ options.claims.push(next);
571
+ index += 1;
572
+ continue;
573
+ }
574
+ if (arg === '--meta') {
575
+ const next = rest[index + 1];
576
+ if (!next || next.startsWith('-')) {
577
+ throw new Error('--meta requires a key=value pair');
578
+ }
579
+ const splitIndex = next.indexOf('=');
580
+ const key = splitIndex >= 0 ? next.slice(0, splitIndex).trim() : '';
581
+ if (!key) {
582
+ throw new Error('--meta requires a non-empty key=value pair');
583
+ }
584
+ options.metadata[key] = splitIndex >= 0 ? next.slice(splitIndex + 1) : '';
585
+ index += 1;
586
+ continue;
587
+ }
588
+ if (!arg.startsWith('-') && options.subcommand === 'start' && !options.task) {
589
+ options.task = arg;
590
+ continue;
591
+ }
331
592
  throw new Error(`Unknown option: ${arg}`);
332
593
  }
333
594
 
334
- if (!['start', 'stop', 'status'].includes(options.subcommand)) {
595
+ if (!['start', 'stop', 'status', 'files', 'diff', 'locks', 'finish', 'cleanup-sessions'].includes(options.subcommand)) {
335
596
  throw new Error(`Unknown agents subcommand: ${options.subcommand}`);
336
597
  }
337
598
  if (options.pid !== null && options.subcommand !== 'stop') {
338
599
  throw new Error('--pid is only supported with `gx agents stop`');
339
600
  }
601
+ if (
602
+ (options.task || options.agent || options.base || options.claims.length > 0 || Object.keys(options.metadata).length > 0 || terminalProvided) &&
603
+ options.subcommand !== 'start'
604
+ ) {
605
+ throw new Error('--task, --agent, --agents, --count, --base, --claim, --meta, --terminal, and --panel are only supported with `gx agents start`');
606
+ }
607
+ if (
608
+ (options.agentSelectionSpecs.length > 0 || options.count !== 1 || options.panel) &&
609
+ options.subcommand !== 'start'
610
+ ) {
611
+ throw new Error('--agents, --count, and --panel are only supported with `gx agents start`');
612
+ }
613
+ if (options.dryRun && !['start', 'cleanup-sessions'].includes(options.subcommand)) {
614
+ throw new Error('--dry-run is only supported with `gx agents start|cleanup-sessions`');
615
+ }
616
+ if (options.subcommand === 'start' && options.dryRun && !options.task && !(options.panel && !options.json)) {
617
+ throw new Error('gx agents start --dry-run requires a task');
618
+ }
619
+ if (
620
+ options.subcommand === 'start' &&
621
+ !options.task &&
622
+ !options.panel &&
623
+ (options.agentSelectionSpecs.length > 0 || options.count !== 1)
624
+ ) {
625
+ throw new Error('gx agents start --agents|--count requires a task');
626
+ }
627
+ if (options.claims.length > 0 && !options.task && !options.panel) {
628
+ throw new Error('gx agents start --claim requires a task');
629
+ }
630
+ if (['files', 'diff', 'locks'].includes(options.subcommand) && !options.branch) {
631
+ throw new Error('--branch requires an agent branch name');
632
+ }
633
+ if (options.subcommand === 'finish') {
634
+ if (!options.sessionId && !options.branch) {
635
+ throw new Error('agents finish requires --session <id> or --branch <agent/...>');
636
+ }
637
+ if (options.sessionId && options.branch) {
638
+ throw new Error('agents finish accepts only one of --session or --branch');
639
+ }
640
+ }
641
+ if (options.branch && !['files', 'diff', 'locks', 'finish'].includes(options.subcommand)) {
642
+ throw new Error('--branch is only supported with `gx agents files|diff|locks|finish`');
643
+ }
644
+ if (
645
+ options.json &&
646
+ !['status', 'files', 'diff', 'locks', 'cleanup-sessions', 'finish'].includes(options.subcommand) &&
647
+ !(options.subcommand === 'start' && options.dryRun)
648
+ ) {
649
+ throw new Error('--json is only supported with `gx agents start --dry-run|status|files|diff|locks|finish|cleanup-sessions`');
650
+ }
651
+ if (options.subcommand === 'start' && options.json && !options.dryRun) {
652
+ throw new Error('gx agents start --json requires --dry-run');
653
+ }
654
+ if (options.staleAgeMinutes !== 24 * 60 && options.subcommand !== 'cleanup-sessions') {
655
+ throw new Error('--older-than-minutes is only supported with `gx agents cleanup-sessions`');
656
+ }
340
657
 
341
658
  return options;
342
659
  }
@@ -771,9 +1088,14 @@ function parseFinishArgs(rawArgs, defaults = {}) {
771
1088
  keepRemote: false,
772
1089
  noAutoCommit: false,
773
1090
  parentGitlinkCommit: defaults.parentGitlinkCommit ?? true,
1091
+ advanceSubmodules: false,
774
1092
  failFast: false,
775
1093
  commitMessage: '',
776
1094
  mergeMode: defaults.mergeMode || 'pr',
1095
+ skipPreflight: false,
1096
+ gateReview: defaults.gateReview ?? false,
1097
+ reviewProvider: defaults.reviewProvider || 'codex',
1098
+ allowNoChecks: false,
777
1099
  };
778
1100
 
779
1101
  for (let index = 0; index < rawArgs.length; index += 1) {
@@ -878,6 +1200,39 @@ function parseFinishArgs(rawArgs, defaults = {}) {
878
1200
  options.failFast = true;
879
1201
  continue;
880
1202
  }
1203
+ if (arg === '--advance-submodules') {
1204
+ options.advanceSubmodules = true;
1205
+ continue;
1206
+ }
1207
+ if (arg === '--no-advance-submodules') {
1208
+ options.advanceSubmodules = false;
1209
+ continue;
1210
+ }
1211
+ if (arg === '--skip-preflight') {
1212
+ options.skipPreflight = true;
1213
+ continue;
1214
+ }
1215
+ if (arg === '--gate-review') {
1216
+ options.gateReview = true;
1217
+ continue;
1218
+ }
1219
+ if (arg === '--no-gate-review' || arg === '--skip-review-gate') {
1220
+ options.gateReview = false;
1221
+ continue;
1222
+ }
1223
+ if (arg === '--allow-no-checks') {
1224
+ options.allowNoChecks = true;
1225
+ continue;
1226
+ }
1227
+ if (arg === '--review-provider') {
1228
+ const next = rawArgs[index + 1];
1229
+ if (!next || !['codex', 'claude'].includes(next)) {
1230
+ throw new Error('--review-provider requires a value of codex|claude');
1231
+ }
1232
+ options.reviewProvider = next;
1233
+ index += 1;
1234
+ continue;
1235
+ }
881
1236
  throw new Error(`Unknown option: ${arg}`);
882
1237
  }
883
1238
 
@@ -891,13 +1246,12 @@ function parseFinishArgs(rawArgs, defaults = {}) {
891
1246
  module.exports = {
892
1247
  requireValue,
893
1248
  normalizeManagedForcePath,
894
- collectForceManagedPaths,
895
1249
  parseCommonArgs,
896
- parseRepoTraversalArgs,
897
1250
  parseSetupArgs,
898
1251
  parseDoctorArgs,
899
1252
  parseTargetFlag,
900
1253
  parseReviewArgs,
1254
+ parsePrReviewArgs,
901
1255
  parseAgentsArgs,
902
1256
  parseReportArgs,
903
1257
  parseSyncArgs,