@imdeadpool/guardex 7.0.41 → 7.0.43
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 +68 -13
- package/package.json +2 -1
- package/skills/gitguardex/SKILL.md +13 -0
- package/skills/guardex-merge-skills-to-dev/SKILL.md +59 -0
- package/src/agents/cleanup-sessions.js +126 -0
- package/src/agents/detect.js +160 -0
- package/src/agents/finish.js +172 -0
- package/src/agents/inspect.js +189 -0
- package/src/agents/launch.js +240 -0
- package/src/agents/registry.js +133 -0
- package/src/agents/selection-panel.js +571 -0
- package/src/agents/sessions.js +151 -0
- package/src/agents/start.js +591 -0
- package/src/agents/status.js +143 -0
- package/src/agents/terminal.js +152 -0
- package/src/budget/index.js +343 -0
- package/src/ci-init/index.js +265 -0
- package/src/cli/args.js +305 -1
- package/src/cli/main.js +262 -132
- package/src/cockpit/action-runner.js +3 -0
- package/src/cockpit/actions.js +80 -0
- package/src/cockpit/control.js +1121 -0
- package/src/cockpit/index.js +426 -0
- package/src/cockpit/keybindings.js +224 -0
- package/src/cockpit/kitty-layout.js +549 -0
- package/src/cockpit/kitty-tree.js +144 -0
- package/src/cockpit/layout.js +224 -0
- package/src/cockpit/logs-reader.js +182 -0
- package/src/cockpit/menu.js +204 -0
- package/src/cockpit/pane-actions.js +597 -0
- package/src/cockpit/pane-menu.js +387 -0
- package/src/cockpit/projects-finder.js +178 -0
- package/src/cockpit/render.js +215 -0
- package/src/cockpit/settings-render.js +128 -0
- package/src/cockpit/settings.js +124 -0
- package/src/cockpit/shortcuts.js +24 -0
- package/src/cockpit/sidebar.js +311 -0
- package/src/cockpit/state.js +72 -0
- package/src/cockpit/theme.js +128 -0
- package/src/cockpit/welcome.js +266 -0
- package/src/context.js +76 -33
- package/src/doctor/index.js +3 -2
- package/src/finish/index.js +39 -2
- package/src/git/index.js +65 -0
- package/src/kitty/command.js +101 -0
- package/src/kitty/runtime.js +250 -0
- package/src/output/index.js +1 -1
- package/src/pr-review.js +241 -0
- package/src/scaffold/index.js +19 -0
- package/src/submodule/index.js +288 -0
- package/src/terminal/index.js +120 -0
- package/src/terminal/kitty.js +622 -0
- package/src/terminal/tmux.js +126 -0
- package/src/tmux/command.js +27 -0
- package/src/tmux/session.js +89 -0
- package/templates/AGENTS.multiagent-safety.md +27 -1
- package/templates/codex/skills/gitguardex/SKILL.md +2 -0
- package/templates/githooks/pre-commit +22 -1
- package/templates/github/workflows/README.md +87 -0
- package/templates/github/workflows/ci-full.yml +55 -0
- package/templates/github/workflows/ci.yml +56 -0
- package/templates/github/workflows/cr.yml +20 -1
- package/templates/scripts/agent-branch-finish.sh +544 -26
- package/templates/scripts/agent-branch-start.sh +89 -22
- package/templates/scripts/agent-preflight.sh +89 -0
- package/templates/scripts/agent-worktree-prune.sh +96 -5
- package/templates/scripts/codex-agent.sh +41 -6
- package/templates/scripts/openspec/init-plan-workspace.sh +43 -0
- package/templates/scripts/review-bot-watch.sh +31 -2
- package/templates/scripts/agent-session-state.js +0 -171
- package/templates/scripts/install-vscode-active-agents-extension.js +0 -135
- package/templates/vscode/guardex-active-agents/README.md +0 -34
- package/templates/vscode/guardex-active-agents/extension.js +0 -3782
- package/templates/vscode/guardex-active-agents/fileicons/gitguardex-fileicons.json +0 -54
- package/templates/vscode/guardex-active-agents/fileicons/icons/agent.svg +0 -5
- package/templates/vscode/guardex-active-agents/fileicons/icons/branch.svg +0 -7
- package/templates/vscode/guardex-active-agents/fileicons/icons/config.svg +0 -4
- package/templates/vscode/guardex-active-agents/fileicons/icons/hook.svg +0 -4
- package/templates/vscode/guardex-active-agents/fileicons/icons/openspec.svg +0 -5
- package/templates/vscode/guardex-active-agents/fileicons/icons/plan.svg +0 -4
- package/templates/vscode/guardex-active-agents/fileicons/icons/spec.svg +0 -5
- package/templates/vscode/guardex-active-agents/icon.png +0 -0
- package/templates/vscode/guardex-active-agents/media/active-agents-hivemind.svg +0 -14
- package/templates/vscode/guardex-active-agents/package.json +0 -169
- 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
|
@@ -261,6 +261,69 @@ function parseReviewArgs(rawArgs) {
|
|
|
261
261
|
};
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
function parsePrReviewArgs(rawArgs) {
|
|
265
|
+
const parsed = parseTargetFlag(rawArgs, process.cwd());
|
|
266
|
+
const options = {
|
|
267
|
+
target: parsed.target,
|
|
268
|
+
provider: 'codex',
|
|
269
|
+
pr: '',
|
|
270
|
+
post: false,
|
|
271
|
+
artifact: '',
|
|
272
|
+
timeoutMs: 10 * 60 * 1000,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
for (let index = 0; index < parsed.args.length; index += 1) {
|
|
276
|
+
const arg = parsed.args[index];
|
|
277
|
+
if (arg === '--provider') {
|
|
278
|
+
const next = requireValue(parsed.args, index, '--provider');
|
|
279
|
+
if (!['codex', 'claude'].includes(next)) {
|
|
280
|
+
throw new Error(`Invalid --provider value: ${next} (expected codex|claude)`);
|
|
281
|
+
}
|
|
282
|
+
options.provider = next;
|
|
283
|
+
index += 1;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (arg === '--pr') {
|
|
287
|
+
options.pr = requireValue(parsed.args, index, '--pr');
|
|
288
|
+
index += 1;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (arg === '--post') {
|
|
292
|
+
options.post = true;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (arg === '--no-post') {
|
|
296
|
+
options.post = false;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
if (arg === '--artifact' || arg === '--output') {
|
|
300
|
+
options.artifact = requireValue(parsed.args, index, arg);
|
|
301
|
+
index += 1;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (arg === '--timeout-ms') {
|
|
305
|
+
const raw = requireValue(parsed.args, index, '--timeout-ms');
|
|
306
|
+
const parsedTimeout = Number.parseInt(raw, 10);
|
|
307
|
+
if (!Number.isFinite(parsedTimeout) || parsedTimeout <= 0) {
|
|
308
|
+
throw new Error('--timeout-ms requires a positive integer');
|
|
309
|
+
}
|
|
310
|
+
options.timeoutMs = parsedTimeout;
|
|
311
|
+
index += 1;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!options.pr) {
|
|
318
|
+
throw new Error('--pr requires a pull request number');
|
|
319
|
+
}
|
|
320
|
+
if (!/^\d+$/.test(String(options.pr))) {
|
|
321
|
+
throw new Error(`--pr must be a pull request number (received: ${options.pr})`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return options;
|
|
325
|
+
}
|
|
326
|
+
|
|
264
327
|
function parseAgentsArgs(rawArgs) {
|
|
265
328
|
const parsed = parseTargetFlag(rawArgs, process.cwd());
|
|
266
329
|
const [subcommandRaw = '', ...rest] = parsed.args;
|
|
@@ -268,14 +331,77 @@ function parseAgentsArgs(rawArgs) {
|
|
|
268
331
|
const options = {
|
|
269
332
|
target: parsed.target,
|
|
270
333
|
subcommand,
|
|
334
|
+
task: '',
|
|
335
|
+
agent: '',
|
|
336
|
+
base: '',
|
|
337
|
+
claims: [],
|
|
338
|
+
count: 1,
|
|
339
|
+
agentSelectionSpecs: [],
|
|
340
|
+
panel: false,
|
|
341
|
+
terminal: process.env.GUARDEX_AGENT_TERMINAL || 'kitty',
|
|
342
|
+
dryRun: false,
|
|
271
343
|
reviewIntervalSeconds: 30,
|
|
272
344
|
cleanupIntervalSeconds: 60,
|
|
273
345
|
idleMinutes: DEFAULT_SHADOW_CLEANUP_IDLE_MINUTES,
|
|
346
|
+
staleAgeMinutes: 24 * 60,
|
|
274
347
|
pid: null,
|
|
348
|
+
branch: '',
|
|
349
|
+
json: false,
|
|
350
|
+
sessionId: '',
|
|
351
|
+
finishArgs: [],
|
|
352
|
+
metadata: {},
|
|
275
353
|
};
|
|
354
|
+
let terminalProvided = false;
|
|
276
355
|
|
|
277
356
|
for (let index = 0; index < rest.length; index += 1) {
|
|
278
357
|
const arg = rest[index];
|
|
358
|
+
if (subcommand === 'finish') {
|
|
359
|
+
if (arg === '--json') {
|
|
360
|
+
options.json = true;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (arg === '--session') {
|
|
364
|
+
const next = rest[index + 1];
|
|
365
|
+
if (!next) {
|
|
366
|
+
throw new Error('--session requires an agent session id');
|
|
367
|
+
}
|
|
368
|
+
options.sessionId = next;
|
|
369
|
+
index += 1;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
if (arg === '--branch') {
|
|
373
|
+
const next = rest[index + 1];
|
|
374
|
+
if (!next) {
|
|
375
|
+
throw new Error('--branch requires an agent branch name');
|
|
376
|
+
}
|
|
377
|
+
options.branch = next;
|
|
378
|
+
index += 1;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
options.finishArgs.push(arg);
|
|
382
|
+
if (
|
|
383
|
+
['--base', '--commit-message', '--mode'].includes(arg) &&
|
|
384
|
+
rest[index + 1] &&
|
|
385
|
+
!rest[index + 1].startsWith('-')
|
|
386
|
+
) {
|
|
387
|
+
options.finishArgs.push(rest[index + 1]);
|
|
388
|
+
index += 1;
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (arg === '--branch') {
|
|
393
|
+
const next = rest[index + 1];
|
|
394
|
+
if (!next) {
|
|
395
|
+
throw new Error('--branch requires an agent branch name');
|
|
396
|
+
}
|
|
397
|
+
options.branch = next;
|
|
398
|
+
index += 1;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (arg === '--json') {
|
|
402
|
+
options.json = true;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
279
405
|
if (arg === '--review-interval') {
|
|
280
406
|
const next = rest[index + 1];
|
|
281
407
|
if (!next) {
|
|
@@ -315,6 +441,19 @@ function parseAgentsArgs(rawArgs) {
|
|
|
315
441
|
index += 1;
|
|
316
442
|
continue;
|
|
317
443
|
}
|
|
444
|
+
if (arg === '--older-than-minutes') {
|
|
445
|
+
const next = rest[index + 1];
|
|
446
|
+
if (!next) {
|
|
447
|
+
throw new Error('--older-than-minutes requires an integer minutes value');
|
|
448
|
+
}
|
|
449
|
+
const parsedValue = Number.parseInt(next, 10);
|
|
450
|
+
if (!Number.isInteger(parsedValue) || parsedValue < 1) {
|
|
451
|
+
throw new Error('--older-than-minutes must be an integer >= 1');
|
|
452
|
+
}
|
|
453
|
+
options.staleAgeMinutes = parsedValue;
|
|
454
|
+
index += 1;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
318
457
|
if (arg === '--pid') {
|
|
319
458
|
const next = rest[index + 1];
|
|
320
459
|
if (!next) {
|
|
@@ -328,15 +467,170 @@ function parseAgentsArgs(rawArgs) {
|
|
|
328
467
|
index += 1;
|
|
329
468
|
continue;
|
|
330
469
|
}
|
|
470
|
+
if (arg === '--agent') {
|
|
471
|
+
const next = rest[index + 1];
|
|
472
|
+
if (!next || next.startsWith('-')) {
|
|
473
|
+
throw new Error('--agent requires an agent id');
|
|
474
|
+
}
|
|
475
|
+
options.agent = next;
|
|
476
|
+
index += 1;
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
if (arg === '--agents') {
|
|
480
|
+
const next = rest[index + 1];
|
|
481
|
+
if (!next || next.startsWith('-')) {
|
|
482
|
+
throw new Error('--agents requires a comma-separated agent list');
|
|
483
|
+
}
|
|
484
|
+
options.agentSelectionSpecs.push(next);
|
|
485
|
+
index += 1;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (arg === '--count') {
|
|
489
|
+
const next = rest[index + 1];
|
|
490
|
+
if (!next || next.startsWith('-')) {
|
|
491
|
+
throw new Error('--count requires a positive integer');
|
|
492
|
+
}
|
|
493
|
+
const parsedValue = Number.parseInt(next, 10);
|
|
494
|
+
if (!Number.isInteger(parsedValue) || parsedValue < 1) {
|
|
495
|
+
throw new Error('--count requires a positive integer');
|
|
496
|
+
}
|
|
497
|
+
options.count = parsedValue;
|
|
498
|
+
index += 1;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
if (arg === '--codex-count' || arg === '--codex-accounts') {
|
|
502
|
+
const next = rest[index + 1];
|
|
503
|
+
if (!next || next.startsWith('-')) {
|
|
504
|
+
throw new Error(`${arg} requires a positive integer`);
|
|
505
|
+
}
|
|
506
|
+
const parsedValue = Number.parseInt(next, 10);
|
|
507
|
+
if (!Number.isInteger(parsedValue) || parsedValue < 1) {
|
|
508
|
+
throw new Error(`${arg} requires a positive integer`);
|
|
509
|
+
}
|
|
510
|
+
options.agent = 'codex';
|
|
511
|
+
options.count = parsedValue;
|
|
512
|
+
index += 1;
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (arg === '--panel') {
|
|
516
|
+
options.panel = true;
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (arg === '--terminal') {
|
|
520
|
+
const next = rest[index + 1];
|
|
521
|
+
if (!next || next.startsWith('-')) {
|
|
522
|
+
throw new Error('--terminal requires kitty or none');
|
|
523
|
+
}
|
|
524
|
+
options.terminal = next;
|
|
525
|
+
terminalProvided = true;
|
|
526
|
+
index += 1;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
if (arg === '--base') {
|
|
530
|
+
const next = rest[index + 1];
|
|
531
|
+
if (!next || next.startsWith('-')) {
|
|
532
|
+
throw new Error('--base requires a branch name');
|
|
533
|
+
}
|
|
534
|
+
options.base = next;
|
|
535
|
+
index += 1;
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
if (arg === '--dry-run') {
|
|
539
|
+
options.dryRun = true;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (arg === '--claim') {
|
|
543
|
+
const next = rest[index + 1];
|
|
544
|
+
if (!next || next.startsWith('-')) {
|
|
545
|
+
throw new Error('--claim requires a file path');
|
|
546
|
+
}
|
|
547
|
+
options.claims.push(next);
|
|
548
|
+
index += 1;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
if (arg === '--meta') {
|
|
552
|
+
const next = rest[index + 1];
|
|
553
|
+
if (!next || next.startsWith('-')) {
|
|
554
|
+
throw new Error('--meta requires a key=value pair');
|
|
555
|
+
}
|
|
556
|
+
const splitIndex = next.indexOf('=');
|
|
557
|
+
const key = splitIndex >= 0 ? next.slice(0, splitIndex).trim() : '';
|
|
558
|
+
if (!key) {
|
|
559
|
+
throw new Error('--meta requires a non-empty key=value pair');
|
|
560
|
+
}
|
|
561
|
+
options.metadata[key] = splitIndex >= 0 ? next.slice(splitIndex + 1) : '';
|
|
562
|
+
index += 1;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (!arg.startsWith('-') && options.subcommand === 'start' && !options.task) {
|
|
566
|
+
options.task = arg;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
331
569
|
throw new Error(`Unknown option: ${arg}`);
|
|
332
570
|
}
|
|
333
571
|
|
|
334
|
-
if (!['start', 'stop', 'status'].includes(options.subcommand)) {
|
|
572
|
+
if (!['start', 'stop', 'status', 'files', 'diff', 'locks', 'finish', 'cleanup-sessions'].includes(options.subcommand)) {
|
|
335
573
|
throw new Error(`Unknown agents subcommand: ${options.subcommand}`);
|
|
336
574
|
}
|
|
337
575
|
if (options.pid !== null && options.subcommand !== 'stop') {
|
|
338
576
|
throw new Error('--pid is only supported with `gx agents stop`');
|
|
339
577
|
}
|
|
578
|
+
if (
|
|
579
|
+
(options.task || options.agent || options.base || options.claims.length > 0 || Object.keys(options.metadata).length > 0 || terminalProvided) &&
|
|
580
|
+
options.subcommand !== 'start'
|
|
581
|
+
) {
|
|
582
|
+
throw new Error('--task, --agent, --agents, --count, --base, --claim, --meta, --terminal, and --panel are only supported with `gx agents start`');
|
|
583
|
+
}
|
|
584
|
+
if (
|
|
585
|
+
(options.agentSelectionSpecs.length > 0 || options.count !== 1 || options.panel) &&
|
|
586
|
+
options.subcommand !== 'start'
|
|
587
|
+
) {
|
|
588
|
+
throw new Error('--agents, --count, and --panel are only supported with `gx agents start`');
|
|
589
|
+
}
|
|
590
|
+
if (options.dryRun && !['start', 'cleanup-sessions'].includes(options.subcommand)) {
|
|
591
|
+
throw new Error('--dry-run is only supported with `gx agents start|cleanup-sessions`');
|
|
592
|
+
}
|
|
593
|
+
if (options.subcommand === 'start' && options.dryRun && !options.task && !(options.panel && !options.json)) {
|
|
594
|
+
throw new Error('gx agents start --dry-run requires a task');
|
|
595
|
+
}
|
|
596
|
+
if (
|
|
597
|
+
options.subcommand === 'start' &&
|
|
598
|
+
!options.task &&
|
|
599
|
+
!options.panel &&
|
|
600
|
+
(options.agentSelectionSpecs.length > 0 || options.count !== 1)
|
|
601
|
+
) {
|
|
602
|
+
throw new Error('gx agents start --agents|--count requires a task');
|
|
603
|
+
}
|
|
604
|
+
if (options.claims.length > 0 && !options.task && !options.panel) {
|
|
605
|
+
throw new Error('gx agents start --claim requires a task');
|
|
606
|
+
}
|
|
607
|
+
if (['files', 'diff', 'locks'].includes(options.subcommand) && !options.branch) {
|
|
608
|
+
throw new Error('--branch requires an agent branch name');
|
|
609
|
+
}
|
|
610
|
+
if (options.subcommand === 'finish') {
|
|
611
|
+
if (!options.sessionId && !options.branch) {
|
|
612
|
+
throw new Error('agents finish requires --session <id> or --branch <agent/...>');
|
|
613
|
+
}
|
|
614
|
+
if (options.sessionId && options.branch) {
|
|
615
|
+
throw new Error('agents finish accepts only one of --session or --branch');
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (options.branch && !['files', 'diff', 'locks', 'finish'].includes(options.subcommand)) {
|
|
619
|
+
throw new Error('--branch is only supported with `gx agents files|diff|locks|finish`');
|
|
620
|
+
}
|
|
621
|
+
if (
|
|
622
|
+
options.json &&
|
|
623
|
+
!['status', 'files', 'diff', 'locks', 'cleanup-sessions', 'finish'].includes(options.subcommand) &&
|
|
624
|
+
!(options.subcommand === 'start' && options.dryRun)
|
|
625
|
+
) {
|
|
626
|
+
throw new Error('--json is only supported with `gx agents start --dry-run|status|files|diff|locks|finish|cleanup-sessions`');
|
|
627
|
+
}
|
|
628
|
+
if (options.subcommand === 'start' && options.json && !options.dryRun) {
|
|
629
|
+
throw new Error('gx agents start --json requires --dry-run');
|
|
630
|
+
}
|
|
631
|
+
if (options.staleAgeMinutes !== 24 * 60 && options.subcommand !== 'cleanup-sessions') {
|
|
632
|
+
throw new Error('--older-than-minutes is only supported with `gx agents cleanup-sessions`');
|
|
633
|
+
}
|
|
340
634
|
|
|
341
635
|
return options;
|
|
342
636
|
}
|
|
@@ -771,6 +1065,7 @@ function parseFinishArgs(rawArgs, defaults = {}) {
|
|
|
771
1065
|
keepRemote: false,
|
|
772
1066
|
noAutoCommit: false,
|
|
773
1067
|
parentGitlinkCommit: defaults.parentGitlinkCommit ?? true,
|
|
1068
|
+
advanceSubmodules: false,
|
|
774
1069
|
failFast: false,
|
|
775
1070
|
commitMessage: '',
|
|
776
1071
|
mergeMode: defaults.mergeMode || 'pr',
|
|
@@ -878,6 +1173,14 @@ function parseFinishArgs(rawArgs, defaults = {}) {
|
|
|
878
1173
|
options.failFast = true;
|
|
879
1174
|
continue;
|
|
880
1175
|
}
|
|
1176
|
+
if (arg === '--advance-submodules') {
|
|
1177
|
+
options.advanceSubmodules = true;
|
|
1178
|
+
continue;
|
|
1179
|
+
}
|
|
1180
|
+
if (arg === '--no-advance-submodules') {
|
|
1181
|
+
options.advanceSubmodules = false;
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
881
1184
|
throw new Error(`Unknown option: ${arg}`);
|
|
882
1185
|
}
|
|
883
1186
|
|
|
@@ -898,6 +1201,7 @@ module.exports = {
|
|
|
898
1201
|
parseDoctorArgs,
|
|
899
1202
|
parseTargetFlag,
|
|
900
1203
|
parseReviewArgs,
|
|
1204
|
+
parsePrReviewArgs,
|
|
901
1205
|
parseAgentsArgs,
|
|
902
1206
|
parseReportArgs,
|
|
903
1207
|
parseSyncArgs,
|