@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.
- package/README.md +94 -13
- package/package.json +3 -1
- package/skills/gitguardex/SKILL.md +13 -0
- package/skills/guardex-merge-skills-to-dev/SKILL.md +59 -0
- package/skills/gx-act/SKILL.md +82 -0
- package/src/agents/cleanup-sessions.js +126 -0
- package/src/agents/finish.js +172 -0
- package/src/agents/inspect.js +202 -0
- package/src/agents/launch.js +249 -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 +146 -0
- package/src/agents/terminal.js +152 -0
- package/src/budget/index.js +344 -0
- package/src/ci-init/index.js +265 -0
- package/src/cli/args.js +357 -3
- package/src/cli/commands/agents.js +364 -0
- package/src/cli/commands/bootstrap.js +92 -0
- package/src/cli/commands/branch.js +127 -0
- package/src/cli/commands/claude.js +674 -0
- package/src/cli/commands/doctor.js +268 -0
- package/src/cli/commands/finish.js +26 -0
- package/src/cli/commands/mcp.js +122 -0
- package/src/cli/commands/misc.js +304 -0
- package/src/cli/commands/pr.js +439 -0
- package/src/cli/commands/prompt.js +92 -0
- package/src/cli/commands/release.js +305 -0
- package/src/cli/commands/report.js +244 -0
- package/src/cli/commands/review.js +32 -0
- package/src/cli/commands/setup.js +242 -0
- package/src/cli/commands/status.js +338 -0
- package/src/cli/commands/watch.js +234 -0
- package/src/cli/main.js +85 -3613
- package/src/cli/shared/repo-env.js +161 -0
- package/src/cli/shared/sandbox.js +417 -0
- package/src/cli/shared/scaffolding.js +535 -0
- package/src/cli/shared/toolchain-shims.js +420 -0
- 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/kitty-layout.js +549 -0
- package/src/cockpit/kitty-tree.js +144 -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 +304 -43
- package/src/core/runtime.js +6 -1
- package/src/doctor/index.js +45 -15
- package/src/finish/index.js +186 -7
- package/src/finish/preflight.js +177 -0
- package/src/finish/review-gate.js +182 -0
- package/src/git/index.js +511 -4
- package/src/hooks/index.js +0 -64
- package/src/kitty/command.js +101 -0
- package/src/kitty/runtime.js +250 -0
- package/src/mcp/collect.js +370 -0
- package/src/mcp/server.js +157 -0
- package/src/output/index.js +68 -2
- package/src/pr-review.js +264 -0
- package/src/pr.js +381 -0
- package/src/sandbox/index.js +13 -2
- package/src/scaffold/agent-worktree-prep.js +213 -0
- package/src/scaffold/index.js +127 -10
- package/src/speckit/index.js +226 -0
- package/src/submodule/index.js +288 -0
- package/src/terminal/index.js +45 -0
- package/src/terminal/kitty.js +622 -0
- package/src/terminal/tmux.js +125 -0
- package/src/tmux/command.js +27 -0
- package/src/tmux/session.js +89 -0
- package/src/toolchain/index.js +20 -0
- package/templates/AGENTS.monorepo-apps.md +26 -0
- package/templates/AGENTS.multiagent-safety.md +63 -323
- package/templates/AGENTS.multiagent-safety.min.md +11 -0
- package/templates/codex/skills/gitguardex/SKILL.md +2 -0
- package/templates/codex/skills/gx-act/SKILL.md +82 -0
- package/templates/githooks/pre-commit +44 -20
- 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 +519 -23
- package/templates/scripts/agent-branch-merge.sh +4 -1
- package/templates/scripts/agent-branch-start.sh +176 -24
- package/templates/scripts/agent-preflight.sh +115 -0
- package/templates/scripts/agent-worktree-prune.sh +96 -5
- package/templates/scripts/codex-agent.sh +41 -97
- 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
|
@@ -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,
|