@imdeadpool/guardex 7.0.43 → 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 +26 -0
- package/package.json +2 -1
- package/skills/gx-act/SKILL.md +82 -0
- package/src/agents/inspect.js +17 -4
- package/src/agents/launch.js +10 -1
- package/src/agents/status.js +9 -6
- package/src/budget/index.js +2 -1
- package/src/cli/args.js +52 -2
- 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 +68 -3726
- 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/context.js +229 -11
- package/src/core/runtime.js +6 -1
- package/src/doctor/index.js +42 -13
- package/src/finish/index.js +147 -5
- package/src/finish/preflight.js +177 -0
- package/src/finish/review-gate.js +182 -0
- package/src/git/index.js +446 -4
- package/src/hooks/index.js +0 -64
- package/src/mcp/collect.js +370 -0
- package/src/mcp/server.js +157 -0
- package/src/output/index.js +67 -1
- package/src/pr-review.js +23 -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 +108 -10
- package/src/speckit/index.js +226 -0
- package/src/terminal/index.js +1 -76
- package/src/terminal/tmux.js +0 -1
- package/src/toolchain/index.js +20 -0
- package/templates/AGENTS.monorepo-apps.md +26 -0
- package/templates/AGENTS.multiagent-safety.md +61 -347
- package/templates/AGENTS.multiagent-safety.min.md +11 -0
- package/templates/codex/skills/gx-act/SKILL.md +82 -0
- package/templates/githooks/pre-commit +22 -19
- package/templates/scripts/agent-branch-finish.sh +8 -30
- package/templates/scripts/agent-branch-merge.sh +4 -1
- package/templates/scripts/agent-branch-start.sh +88 -3
- package/templates/scripts/agent-preflight.sh +31 -5
- package/templates/scripts/agent-worktree-prune.sh +1 -1
- package/templates/scripts/codex-agent.sh +0 -91
- package/src/agents/detect.js +0 -160
- package/src/cockpit/keybindings.js +0 -224
- package/src/cockpit/layout.js +0 -224
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// `gx doctor` — repair drift + verify, with sandbox fallback when blocked
|
|
2
|
+
// on a protected base. Pure code-motion from src/cli/main.js.
|
|
3
|
+
const {
|
|
4
|
+
path,
|
|
5
|
+
cp,
|
|
6
|
+
TOOL_NAME,
|
|
7
|
+
AGENT_WORKTREE_RELATIVE_DIRS,
|
|
8
|
+
} = require('../../context');
|
|
9
|
+
const {
|
|
10
|
+
resolveRepoRoot,
|
|
11
|
+
discoverNestedGitRepos,
|
|
12
|
+
currentBranchName,
|
|
13
|
+
} = require('../../git');
|
|
14
|
+
const doctorModule = require('../../doctor');
|
|
15
|
+
const {
|
|
16
|
+
colorizeDoctorOutput,
|
|
17
|
+
formatElapsedDuration,
|
|
18
|
+
printAutoFinishSummary,
|
|
19
|
+
} = require('../../output');
|
|
20
|
+
const {
|
|
21
|
+
ensureOmxScaffold,
|
|
22
|
+
configureHooks,
|
|
23
|
+
printOperations,
|
|
24
|
+
} = require('../../scaffold');
|
|
25
|
+
const { run } = require('../../core/runtime');
|
|
26
|
+
const { parseDoctorArgs } = require('../args');
|
|
27
|
+
const {
|
|
28
|
+
runFixInternal,
|
|
29
|
+
runScanInternal,
|
|
30
|
+
printScanResult,
|
|
31
|
+
setExitCodeFromScan,
|
|
32
|
+
printWorktreePruneSummary,
|
|
33
|
+
} = require('../shared/scaffolding');
|
|
34
|
+
const {
|
|
35
|
+
protectedBaseWriteBlock,
|
|
36
|
+
assertProtectedMainWriteAllowed,
|
|
37
|
+
startProtectedBaseSandbox,
|
|
38
|
+
cleanupProtectedBaseSandbox,
|
|
39
|
+
isSpawnFailure,
|
|
40
|
+
} = require('../shared/sandbox');
|
|
41
|
+
const { printRequiredSystemToolStatus } = require('./setup');
|
|
42
|
+
|
|
43
|
+
function doctor(rawArgs) {
|
|
44
|
+
const options = parseDoctorArgs(rawArgs);
|
|
45
|
+
const topRepoRoot = resolveRepoRoot(options.target);
|
|
46
|
+
const discoveredRepos = options.recursive
|
|
47
|
+
? discoverNestedGitRepos(topRepoRoot, {
|
|
48
|
+
maxDepth: options.nestedMaxDepth,
|
|
49
|
+
extraSkip: options.nestedSkipDirs,
|
|
50
|
+
includeSubmodules: options.includeSubmodules,
|
|
51
|
+
skipRelativeDirs: AGENT_WORKTREE_RELATIVE_DIRS,
|
|
52
|
+
})
|
|
53
|
+
: [topRepoRoot];
|
|
54
|
+
|
|
55
|
+
if (discoveredRepos.length > 1) {
|
|
56
|
+
if (!options.json) {
|
|
57
|
+
console.log(
|
|
58
|
+
`[${TOOL_NAME}] Detected ${discoveredRepos.length} git repos under ${topRepoRoot}. ` +
|
|
59
|
+
`Repairing each with doctor (use --single-repo or --current to limit to the target).`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const repoResults = [];
|
|
64
|
+
let aggregateExitCode = 0;
|
|
65
|
+
for (let repoIndex = 0; repoIndex < discoveredRepos.length; repoIndex += 1) {
|
|
66
|
+
const repoPath = discoveredRepos[repoIndex];
|
|
67
|
+
const progressLabel = `${repoIndex + 1}/${discoveredRepos.length}`;
|
|
68
|
+
if (!options.json) {
|
|
69
|
+
console.log(`[${TOOL_NAME}] ── Doctor target: ${repoPath} [${progressLabel}] ──`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const mainScriptPath = require.resolve('../main.js');
|
|
73
|
+
const childArgs = [
|
|
74
|
+
mainScriptPath,
|
|
75
|
+
'doctor',
|
|
76
|
+
'--single-repo',
|
|
77
|
+
'--target',
|
|
78
|
+
repoPath,
|
|
79
|
+
...(options.force ? ['--force', ...(options.forceManagedPaths || [])] : []),
|
|
80
|
+
...(options.dropStaleLocks ? [] : ['--keep-stale-locks']),
|
|
81
|
+
...(options.skipAgents ? ['--skip-agents'] : []),
|
|
82
|
+
...(options.skipPackageJson ? ['--skip-package-json'] : []),
|
|
83
|
+
...(options.skipGitignore ? ['--no-gitignore'] : []),
|
|
84
|
+
...(options.contract ? ['--contract'] : []),
|
|
85
|
+
...(options.dryRun ? ['--dry-run'] : []),
|
|
86
|
+
// Recursive child doctor runs should report pending PR state immediately instead of blocking the parent loop.
|
|
87
|
+
'--no-wait-for-merge',
|
|
88
|
+
...(options.verboseAutoFinish ? ['--verbose-auto-finish'] : []),
|
|
89
|
+
...(options.json ? ['--json'] : []),
|
|
90
|
+
...(options.allowProtectedBaseWrite ? ['--allow-protected-base-write'] : []),
|
|
91
|
+
];
|
|
92
|
+
const startedAt = Date.now();
|
|
93
|
+
const nestedResult = options.json
|
|
94
|
+
? run(process.execPath, childArgs, { cwd: topRepoRoot })
|
|
95
|
+
: cp.spawnSync(process.execPath, childArgs, {
|
|
96
|
+
cwd: topRepoRoot,
|
|
97
|
+
encoding: 'utf8',
|
|
98
|
+
stdio: 'inherit',
|
|
99
|
+
});
|
|
100
|
+
if (isSpawnFailure(nestedResult)) {
|
|
101
|
+
throw nestedResult.error;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const exitCode = typeof nestedResult.status === 'number' ? nestedResult.status : 1;
|
|
105
|
+
if (exitCode !== 0 && aggregateExitCode === 0) {
|
|
106
|
+
aggregateExitCode = exitCode;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (options.json) {
|
|
110
|
+
let parsedResult = null;
|
|
111
|
+
if (nestedResult.stdout) {
|
|
112
|
+
try {
|
|
113
|
+
parsedResult = JSON.parse(nestedResult.stdout);
|
|
114
|
+
} catch {
|
|
115
|
+
parsedResult = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
repoResults.push(
|
|
119
|
+
parsedResult
|
|
120
|
+
? { repoRoot: repoPath, exitCode, result: parsedResult }
|
|
121
|
+
: {
|
|
122
|
+
repoRoot: repoPath,
|
|
123
|
+
exitCode,
|
|
124
|
+
stdout: nestedResult.stdout || '',
|
|
125
|
+
stderr: nestedResult.stderr || '',
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
} else {
|
|
129
|
+
console.log(
|
|
130
|
+
`[${TOOL_NAME}] Doctor target complete: ${repoPath} [${progressLabel}] in ${formatElapsedDuration(Date.now() - startedAt)}.`,
|
|
131
|
+
);
|
|
132
|
+
if (repoIndex < discoveredRepos.length - 1) {
|
|
133
|
+
process.stdout.write('\n');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (options.json) {
|
|
139
|
+
process.stdout.write(
|
|
140
|
+
JSON.stringify(
|
|
141
|
+
{
|
|
142
|
+
repoRoot: topRepoRoot,
|
|
143
|
+
recursive: true,
|
|
144
|
+
repos: repoResults,
|
|
145
|
+
},
|
|
146
|
+
null,
|
|
147
|
+
2,
|
|
148
|
+
) + '\n',
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
process.exitCode = aggregateExitCode;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const singleRepoOptions = {
|
|
157
|
+
...options,
|
|
158
|
+
target: topRepoRoot,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
if (!singleRepoOptions.json) {
|
|
162
|
+
printRequiredSystemToolStatus();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const blocked = protectedBaseWriteBlock(singleRepoOptions, { requireBootstrap: false });
|
|
166
|
+
if (blocked) {
|
|
167
|
+
doctorModule.runDoctorInSandbox(singleRepoOptions, blocked, {
|
|
168
|
+
startProtectedBaseSandbox,
|
|
169
|
+
cleanupProtectedBaseSandbox,
|
|
170
|
+
ensureOmxScaffold,
|
|
171
|
+
configureHooks,
|
|
172
|
+
autoFinishReadyAgentBranches: doctorModule.autoFinishReadyAgentBranches,
|
|
173
|
+
});
|
|
174
|
+
const primaryBaseBranch = currentBranchName(blocked.repoRoot);
|
|
175
|
+
const prunePayload = doctorModule.pruneStaleAgentWorktrees(blocked.repoRoot, {
|
|
176
|
+
baseBranch: primaryBaseBranch,
|
|
177
|
+
dryRun: singleRepoOptions.dryRun,
|
|
178
|
+
});
|
|
179
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: primaryBaseBranch });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
assertProtectedMainWriteAllowed(singleRepoOptions, 'doctor');
|
|
184
|
+
const fixPayload = runFixInternal(singleRepoOptions);
|
|
185
|
+
const scanResult = runScanInternal({ target: singleRepoOptions.target, json: false });
|
|
186
|
+
const currentBaseBranch = currentBranchName(scanResult.repoRoot);
|
|
187
|
+
const autoFinishSummary = scanResult.guardexEnabled === false
|
|
188
|
+
? {
|
|
189
|
+
enabled: false,
|
|
190
|
+
attempted: 0,
|
|
191
|
+
completed: 0,
|
|
192
|
+
skipped: 0,
|
|
193
|
+
failed: 0,
|
|
194
|
+
details: [],
|
|
195
|
+
}
|
|
196
|
+
: doctorModule.autoFinishReadyAgentBranches(scanResult.repoRoot, {
|
|
197
|
+
baseBranch: currentBaseBranch,
|
|
198
|
+
dryRun: singleRepoOptions.dryRun,
|
|
199
|
+
waitForMerge: singleRepoOptions.waitForMerge,
|
|
200
|
+
});
|
|
201
|
+
const prunePayload = scanResult.guardexEnabled === false
|
|
202
|
+
? { enabled: false, ran: false, status: 'skipped', details: ['Guardex disabled for this repo.'] }
|
|
203
|
+
: doctorModule.pruneStaleAgentWorktrees(scanResult.repoRoot, {
|
|
204
|
+
baseBranch: currentBaseBranch,
|
|
205
|
+
dryRun: singleRepoOptions.dryRun,
|
|
206
|
+
});
|
|
207
|
+
const safe = scanResult.guardexEnabled === false || (scanResult.errors === 0 && scanResult.warnings === 0);
|
|
208
|
+
const musafe = safe;
|
|
209
|
+
|
|
210
|
+
if (singleRepoOptions.json) {
|
|
211
|
+
process.stdout.write(
|
|
212
|
+
JSON.stringify(
|
|
213
|
+
{
|
|
214
|
+
repoRoot: scanResult.repoRoot,
|
|
215
|
+
branch: scanResult.branch,
|
|
216
|
+
safe,
|
|
217
|
+
musafe,
|
|
218
|
+
fix: {
|
|
219
|
+
operations: fixPayload.operations,
|
|
220
|
+
hookResult: fixPayload.hookResult,
|
|
221
|
+
dryRun: Boolean(singleRepoOptions.dryRun),
|
|
222
|
+
},
|
|
223
|
+
scan: {
|
|
224
|
+
guardexEnabled: scanResult.guardexEnabled !== false,
|
|
225
|
+
guardexToggle: scanResult.guardexToggle || null,
|
|
226
|
+
errors: scanResult.errors,
|
|
227
|
+
warnings: scanResult.warnings,
|
|
228
|
+
findings: scanResult.findings,
|
|
229
|
+
},
|
|
230
|
+
autoFinish: autoFinishSummary,
|
|
231
|
+
worktreePrune: prunePayload,
|
|
232
|
+
},
|
|
233
|
+
null,
|
|
234
|
+
2,
|
|
235
|
+
) + '\n',
|
|
236
|
+
);
|
|
237
|
+
setExitCodeFromScan(scanResult);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
printOperations('Doctor/fix', fixPayload, options.dryRun);
|
|
242
|
+
printScanResult(scanResult, false);
|
|
243
|
+
if (scanResult.guardexEnabled === false) {
|
|
244
|
+
console.log(`[${TOOL_NAME}] Repo-local Guardex enforcement is intentionally disabled.`);
|
|
245
|
+
setExitCodeFromScan(scanResult);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
printAutoFinishSummary(autoFinishSummary, {
|
|
249
|
+
baseBranch: currentBaseBranch,
|
|
250
|
+
verbose: singleRepoOptions.verboseAutoFinish,
|
|
251
|
+
});
|
|
252
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: currentBaseBranch });
|
|
253
|
+
if (safe) {
|
|
254
|
+
console.log(colorizeDoctorOutput(`[${TOOL_NAME}] ✅ Repo is fully safe.`, 'safe'));
|
|
255
|
+
} else {
|
|
256
|
+
console.log(
|
|
257
|
+
colorizeDoctorOutput(
|
|
258
|
+
`[${TOOL_NAME}] ⚠️ Repo is not fully safe yet (${scanResult.errors} error(s), ${scanResult.warnings} warning(s)).`,
|
|
259
|
+
scanResult.errors > 0 ? 'unsafe' : 'warn',
|
|
260
|
+
),
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
setExitCodeFromScan(scanResult);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
module.exports = {
|
|
267
|
+
doctor,
|
|
268
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// `gx cleanup`, `gx merge`, `gx finish`, `gx sync` — thin wrappers around
|
|
2
|
+
// the finishCommands module. Pure code-motion from src/cli/main.js.
|
|
3
|
+
const finishCommands = require('../../finish');
|
|
4
|
+
|
|
5
|
+
function cleanup(rawArgs) {
|
|
6
|
+
return finishCommands.cleanup(rawArgs);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function merge(rawArgs) {
|
|
10
|
+
return finishCommands.merge(rawArgs);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function finish(rawArgs, defaults = {}) {
|
|
14
|
+
return finishCommands.finish(rawArgs, defaults);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function sync(rawArgs) {
|
|
18
|
+
return finishCommands.sync(rawArgs);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
cleanup,
|
|
23
|
+
merge,
|
|
24
|
+
finish,
|
|
25
|
+
sync,
|
|
26
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// `gx mcp` — cross-repo, read-only multi-agent observability over MCP.
|
|
4
|
+
// gx mcp serve run the stdio MCP server (for agent harnesses)
|
|
5
|
+
// gx mcp list-agents one-shot human/debug view of all agent lanes
|
|
6
|
+
// gx mcp who-owns <file> who holds the lock on a file
|
|
7
|
+
// gx mcp register print how to wire the server into Claude Code / Codex
|
|
8
|
+
|
|
9
|
+
const collect = require('../../mcp/collect');
|
|
10
|
+
const server = require('../../mcp/server');
|
|
11
|
+
const { SHORT_TOOL_NAME } = require('../../context');
|
|
12
|
+
|
|
13
|
+
function printUsage() {
|
|
14
|
+
process.stdout.write(
|
|
15
|
+
[
|
|
16
|
+
`Usage: ${SHORT_TOOL_NAME} mcp <serve|list-agents|who-owns|register>`,
|
|
17
|
+
'',
|
|
18
|
+
' serve Run the read-only MCP server over stdio.',
|
|
19
|
+
' list-agents [--json] [--no-prs]',
|
|
20
|
+
' Show every active agent lane across all repos.',
|
|
21
|
+
' who-owns <file> [--json]',
|
|
22
|
+
' Which agent/branch holds the lock on <file>.',
|
|
23
|
+
' register Print how to register the server with an agent.',
|
|
24
|
+
'',
|
|
25
|
+
].join('\n') + '\n',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function fmtAgent(a) {
|
|
30
|
+
const pr = a.prLookupError
|
|
31
|
+
? `PR? (lookup failed)`
|
|
32
|
+
: a.pr ? `PR #${a.pr.number} (${a.pr.state}${a.pr.isDraft ? ', draft' : ''})` : a.pushed ? 'pushed, no open PR' : 'local only';
|
|
33
|
+
const locks = a.locks && a.locks.length ? `${a.locks.length} lock(s)` : 'no locks';
|
|
34
|
+
const dirty = a.dirty && a.dirty.length ? `editing ${a.dirty.length} file(s)` : 'clean';
|
|
35
|
+
const when = a.lastCommit && a.lastCommit.date ? a.lastCommit.date.replace('T', ' ').replace(/\..*$/, '') : '?';
|
|
36
|
+
const warn = a.warning ? ' ⚠ ON PRIMARY CHECKOUT' : '';
|
|
37
|
+
const stale = a.stale ? ` ⚠ STALE ${a.ageDays}d` : '';
|
|
38
|
+
return [
|
|
39
|
+
`• ${a.repo} ${a.branch}${warn}${stale}`,
|
|
40
|
+
` agent=${a.agent || '?'} task=${a.task}`,
|
|
41
|
+
` ${dirty} ${locks} ${pr} last=${when}`,
|
|
42
|
+
` worktree=${a.worktree}`,
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function listAgents(rest) {
|
|
47
|
+
const includePrs = !rest.includes('--no-prs');
|
|
48
|
+
const data = collect.collectAllAgents({ includePrs });
|
|
49
|
+
if (rest.includes('--json')) {
|
|
50
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const header = `gx agents — ${data.agents.length} active lane(s) across ${data.scannedRepos} repo(s)`;
|
|
54
|
+
const body = data.agents.length ? data.agents.map(fmtAgent).join('\n') : ' (no active agent lanes found)';
|
|
55
|
+
process.stdout.write(`${header}\n\n${body}\n`);
|
|
56
|
+
if (data.errors && data.errors.length) {
|
|
57
|
+
process.stdout.write(`\n${data.errors.length} repo(s) errored during scan (run with --json for detail)\n`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function whoOwns(rest) {
|
|
62
|
+
const file = rest.find((a) => !a.startsWith('--'));
|
|
63
|
+
const result = collect.whoOwns(file, {});
|
|
64
|
+
if (rest.includes('--json')) {
|
|
65
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!result.owner) {
|
|
69
|
+
process.stdout.write(`${result.file || file}: unclaimed${result.error ? ` (${result.error})` : ''}\n`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
process.stdout.write(`${result.file}: locked by ${result.owner.agent || result.owner.branch} (branch ${result.owner.branch})\n`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function register() {
|
|
76
|
+
process.stdout.write(
|
|
77
|
+
[
|
|
78
|
+
'Register the read-only gx agent-observability MCP with your harness:',
|
|
79
|
+
'',
|
|
80
|
+
'Claude Code (user scope — available in every repo):',
|
|
81
|
+
` claude mcp add gx -s user -- ${SHORT_TOOL_NAME} mcp serve`,
|
|
82
|
+
'',
|
|
83
|
+
'Or add to a repo-root .mcp.json:',
|
|
84
|
+
' {',
|
|
85
|
+
' "mcpServers": {',
|
|
86
|
+
` "gx": { "command": "${SHORT_TOOL_NAME}", "args": ["mcp", "serve"] }`,
|
|
87
|
+
' }',
|
|
88
|
+
' }',
|
|
89
|
+
'',
|
|
90
|
+
'Codex / other MCP clients: run `gx mcp serve` as a stdio MCP server.',
|
|
91
|
+
'Tools exposed (all read-only): list_agents, repo_state, who_owns, my_context.',
|
|
92
|
+
'',
|
|
93
|
+
].join('\n') + '\n',
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function mcp(rawArgs = []) {
|
|
98
|
+
const [subcommand, ...rest] = rawArgs;
|
|
99
|
+
if (subcommand === 'serve') {
|
|
100
|
+
server.serve(); // long-running: readline keeps the process alive
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (subcommand === 'list-agents' || subcommand === 'list' || subcommand === 'agents') {
|
|
104
|
+
listAgents(rest);
|
|
105
|
+
process.exitCode = 0;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (subcommand === 'who-owns' || subcommand === 'who') {
|
|
109
|
+
whoOwns(rest);
|
|
110
|
+
process.exitCode = 0;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (subcommand === 'register' || subcommand === 'print-config') {
|
|
114
|
+
register();
|
|
115
|
+
process.exitCode = 0;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
printUsage();
|
|
119
|
+
process.exitCode = subcommand ? 1 : 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = { mcp };
|