@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,242 @@
|
|
|
1
|
+
// `gx setup` — install + repair + verify (with sandbox fallback when blocked
|
|
2
|
+
// by a protected base). Pure code-motion from src/cli/main.js.
|
|
3
|
+
const {
|
|
4
|
+
path,
|
|
5
|
+
TOOL_NAME,
|
|
6
|
+
SHORT_TOOL_NAME,
|
|
7
|
+
GLOBAL_TOOLCHAIN_PACKAGES,
|
|
8
|
+
OPTIONAL_LOCAL_COMPANION_TOOLS,
|
|
9
|
+
AGENT_WORKTREE_RELATIVE_DIRS,
|
|
10
|
+
} = require('../../context');
|
|
11
|
+
const {
|
|
12
|
+
resolveRepoRoot,
|
|
13
|
+
discoverNestedGitRepos,
|
|
14
|
+
currentBranchName,
|
|
15
|
+
printSetupRepoHints,
|
|
16
|
+
} = require('../../git');
|
|
17
|
+
const toolchainModule = require('../../toolchain');
|
|
18
|
+
const doctorModule = require('../../doctor');
|
|
19
|
+
const speckitModule = require('../../speckit');
|
|
20
|
+
const { printAutoFinishSummary } = require('../../output');
|
|
21
|
+
const { printOperations } = require('../../scaffold');
|
|
22
|
+
const { parseSetupArgs } = require('../args');
|
|
23
|
+
const {
|
|
24
|
+
runScanInternal,
|
|
25
|
+
printScanResult,
|
|
26
|
+
printWorktreePruneSummary,
|
|
27
|
+
setExitCodeFromScan,
|
|
28
|
+
} = require('../shared/scaffolding');
|
|
29
|
+
const {
|
|
30
|
+
protectedBaseWriteBlock,
|
|
31
|
+
runSetupBootstrapInternal,
|
|
32
|
+
runSetupInSandbox,
|
|
33
|
+
} = require('../shared/sandbox');
|
|
34
|
+
|
|
35
|
+
function printRequiredSystemToolStatus() {
|
|
36
|
+
const requiredSystemTools = toolchainModule.detectRequiredSystemTools();
|
|
37
|
+
const missingSystemTools = requiredSystemTools.filter((tool) => tool.status !== 'active');
|
|
38
|
+
if (missingSystemTools.length === 0) {
|
|
39
|
+
console.log(`[${TOOL_NAME}] ✅ Required system tools available (${requiredSystemTools.map((tool) => tool.name).join(', ')}).`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const names = missingSystemTools.map((tool) => tool.name).join(', ');
|
|
44
|
+
console.log(`[${TOOL_NAME}] ⚠️ Missing required system tool(s): ${names}`);
|
|
45
|
+
for (const tool of missingSystemTools) {
|
|
46
|
+
const reasonText = tool.reason ? ` (${tool.reason})` : '';
|
|
47
|
+
console.log(`[${TOOL_NAME}] Install ${tool.name}: ${tool.installHint}${reasonText}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function setup(rawArgs) {
|
|
52
|
+
const options = parseSetupArgs(rawArgs, {
|
|
53
|
+
target: process.cwd(),
|
|
54
|
+
force: false,
|
|
55
|
+
skipAgents: false,
|
|
56
|
+
skipPackageJson: false,
|
|
57
|
+
skipGitignore: false,
|
|
58
|
+
dryRun: false,
|
|
59
|
+
yesGlobalInstall: false,
|
|
60
|
+
noGlobalInstall: false,
|
|
61
|
+
allowProtectedBaseWrite: false,
|
|
62
|
+
speckit: true,
|
|
63
|
+
speckitForce: false,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const globalInstallStatus = toolchainModule.installGlobalToolchain(options);
|
|
67
|
+
if (globalInstallStatus.status === 'installed') {
|
|
68
|
+
console.log(
|
|
69
|
+
`[${TOOL_NAME}] ✅ Companion tools installed (${(globalInstallStatus.packages || []).join(', ')}).`,
|
|
70
|
+
);
|
|
71
|
+
} else if (globalInstallStatus.status === 'already-installed') {
|
|
72
|
+
console.log(`[${TOOL_NAME}] ✅ Companion tools already installed. Skipping.`);
|
|
73
|
+
} else if (globalInstallStatus.status === 'failed') {
|
|
74
|
+
const installCommands = toolchainModule.describeCompanionInstallCommands(
|
|
75
|
+
GLOBAL_TOOLCHAIN_PACKAGES,
|
|
76
|
+
OPTIONAL_LOCAL_COMPANION_TOOLS,
|
|
77
|
+
);
|
|
78
|
+
console.log(
|
|
79
|
+
`[${TOOL_NAME}] ⚠️ Global install failed: ${globalInstallStatus.reason}\n` +
|
|
80
|
+
`[${TOOL_NAME}] Continue with local safety setup. You can retry later with:\n` +
|
|
81
|
+
installCommands.map((command) => ` ${command}`).join('\n'),
|
|
82
|
+
);
|
|
83
|
+
} else if (globalInstallStatus.status === 'skipped' && globalInstallStatus.reason === 'non-interactive-default') {
|
|
84
|
+
console.log(
|
|
85
|
+
`[${TOOL_NAME}] Skipping companion installs (non-interactive mode). ` +
|
|
86
|
+
`Use --yes-global-install to force or run interactively for Y/N prompt.`,
|
|
87
|
+
);
|
|
88
|
+
} else if (globalInstallStatus.status === 'skipped') {
|
|
89
|
+
console.log(`[${TOOL_NAME}] ⚠️ Companion installs skipped by user choice.`);
|
|
90
|
+
for (const warning of toolchainModule.describeMissingGlobalDependencyWarnings(
|
|
91
|
+
globalInstallStatus.missingPackages || [],
|
|
92
|
+
)) {
|
|
93
|
+
console.log(`[${TOOL_NAME}] ⚠️ ${warning}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
printRequiredSystemToolStatus();
|
|
98
|
+
|
|
99
|
+
const topRepoRoot = resolveRepoRoot(options.target);
|
|
100
|
+
const discoveredRepos = options.recursive
|
|
101
|
+
? discoverNestedGitRepos(topRepoRoot, {
|
|
102
|
+
maxDepth: options.nestedMaxDepth,
|
|
103
|
+
extraSkip: options.nestedSkipDirs,
|
|
104
|
+
includeSubmodules: options.includeSubmodules,
|
|
105
|
+
skipRelativeDirs: AGENT_WORKTREE_RELATIVE_DIRS,
|
|
106
|
+
})
|
|
107
|
+
: [topRepoRoot];
|
|
108
|
+
|
|
109
|
+
if (discoveredRepos.length > 1) {
|
|
110
|
+
console.log(
|
|
111
|
+
`[${TOOL_NAME}] Detected ${discoveredRepos.length} git repos under ${topRepoRoot}. Installing into each (use --no-recursive or --current to limit to the top-level).`,
|
|
112
|
+
);
|
|
113
|
+
for (const repoPath of discoveredRepos) {
|
|
114
|
+
const marker = repoPath === topRepoRoot ? ' (top-level)' : '';
|
|
115
|
+
console.log(`[${TOOL_NAME}] - ${repoPath}${marker}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let aggregateErrors = 0;
|
|
120
|
+
let aggregateWarnings = 0;
|
|
121
|
+
let lastScanResult = null;
|
|
122
|
+
|
|
123
|
+
for (const repoPath of discoveredRepos) {
|
|
124
|
+
const perRepoOptions = { ...options, target: repoPath };
|
|
125
|
+
const repoLabel = discoveredRepos.length > 1 ? ` [${path.relative(topRepoRoot, repoPath) || '.'}]` : '';
|
|
126
|
+
|
|
127
|
+
if (discoveredRepos.length > 1) {
|
|
128
|
+
console.log(`[${TOOL_NAME}] ── Setup target: ${repoPath} ──`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const blocked = protectedBaseWriteBlock(perRepoOptions);
|
|
132
|
+
if (blocked) {
|
|
133
|
+
const sandboxResult = runSetupInSandbox(perRepoOptions, blocked, repoLabel);
|
|
134
|
+
aggregateErrors += sandboxResult.scanResult.errors;
|
|
135
|
+
aggregateWarnings += sandboxResult.scanResult.warnings;
|
|
136
|
+
lastScanResult = sandboxResult.scanResult;
|
|
137
|
+
const primaryBaseBranch = currentBranchName(blocked.repoRoot);
|
|
138
|
+
const prunePayload = doctorModule.pruneStaleAgentWorktrees(blocked.repoRoot, {
|
|
139
|
+
baseBranch: primaryBaseBranch,
|
|
140
|
+
dryRun: perRepoOptions.dryRun,
|
|
141
|
+
});
|
|
142
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: primaryBaseBranch });
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { installPayload, fixPayload, parentWorkspace } = runSetupBootstrapInternal(perRepoOptions);
|
|
147
|
+
printOperations(`Setup/install${repoLabel}`, installPayload, perRepoOptions.dryRun);
|
|
148
|
+
printOperations(`Setup/fix${repoLabel}`, fixPayload, perRepoOptions.dryRun);
|
|
149
|
+
|
|
150
|
+
const speckitGloballyDisabled = perRepoOptions.noGlobalInstall === true;
|
|
151
|
+
if (perRepoOptions.speckit !== false && !speckitGloballyDisabled) {
|
|
152
|
+
try {
|
|
153
|
+
speckitModule.installSpeckit({
|
|
154
|
+
target: repoPath,
|
|
155
|
+
dryRun: perRepoOptions.dryRun,
|
|
156
|
+
prune: true,
|
|
157
|
+
force: perRepoOptions.speckitForce === true,
|
|
158
|
+
silent: true,
|
|
159
|
+
});
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.log(`[${TOOL_NAME}] ⚠️ speckit install skipped: ${error.message}`);
|
|
162
|
+
}
|
|
163
|
+
} else if (speckitGloballyDisabled && perRepoOptions.speckit === true && perRepoOptions.speckitForce) {
|
|
164
|
+
// Operator explicitly forced speckit despite --no-global-install — honor that.
|
|
165
|
+
try {
|
|
166
|
+
speckitModule.installSpeckit({
|
|
167
|
+
target: repoPath,
|
|
168
|
+
dryRun: perRepoOptions.dryRun,
|
|
169
|
+
prune: true,
|
|
170
|
+
force: true,
|
|
171
|
+
silent: true,
|
|
172
|
+
});
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.log(`[${TOOL_NAME}] ⚠️ speckit install skipped: ${error.message}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (perRepoOptions.dryRun) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (parentWorkspace) {
|
|
183
|
+
console.log(`[${TOOL_NAME}] Parent workspace view: ${parentWorkspace.workspacePath}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const scanResult = runScanInternal({ target: repoPath, json: false });
|
|
187
|
+
const currentBaseBranch = currentBranchName(scanResult.repoRoot);
|
|
188
|
+
const autoFinishSummary = doctorModule.autoFinishReadyAgentBranches(scanResult.repoRoot, {
|
|
189
|
+
baseBranch: currentBaseBranch,
|
|
190
|
+
dryRun: perRepoOptions.dryRun,
|
|
191
|
+
});
|
|
192
|
+
printScanResult(scanResult, false);
|
|
193
|
+
printAutoFinishSummary(autoFinishSummary, {
|
|
194
|
+
baseBranch: currentBaseBranch,
|
|
195
|
+
});
|
|
196
|
+
const prunePayload = scanResult.guardexEnabled === false
|
|
197
|
+
? { enabled: false, ran: false, status: 'skipped', details: ['Guardex disabled for this repo.'] }
|
|
198
|
+
: doctorModule.pruneStaleAgentWorktrees(scanResult.repoRoot, {
|
|
199
|
+
baseBranch: currentBaseBranch,
|
|
200
|
+
dryRun: perRepoOptions.dryRun,
|
|
201
|
+
});
|
|
202
|
+
printWorktreePruneSummary(prunePayload, { baseBranch: currentBaseBranch });
|
|
203
|
+
printSetupRepoHints(scanResult.repoRoot, currentBaseBranch, repoLabel);
|
|
204
|
+
|
|
205
|
+
aggregateErrors += scanResult.errors;
|
|
206
|
+
aggregateWarnings += scanResult.warnings;
|
|
207
|
+
lastScanResult = scanResult;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (options.dryRun) {
|
|
211
|
+
console.log(`[${TOOL_NAME}] Dry run setup done.`);
|
|
212
|
+
process.exitCode = 0;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (aggregateErrors === 0 && aggregateWarnings === 0) {
|
|
217
|
+
const repoCount = discoveredRepos.length;
|
|
218
|
+
const suffix = repoCount > 1 ? ` (${repoCount} repos)` : '';
|
|
219
|
+
console.log(`[${TOOL_NAME}] ✅ Setup complete.${suffix}`);
|
|
220
|
+
console.log(`[${TOOL_NAME}] Copy AI setup prompt with: ${SHORT_TOOL_NAME} prompt`);
|
|
221
|
+
console.log(
|
|
222
|
+
`[${TOOL_NAME}] OpenSpec core workflow: /opsx:propose -> /opsx:apply -> /opsx:archive`,
|
|
223
|
+
);
|
|
224
|
+
console.log(
|
|
225
|
+
`[${TOOL_NAME}] Optional expanded OpenSpec profile: openspec config profile <profile-name> && openspec update`,
|
|
226
|
+
);
|
|
227
|
+
console.log(`[${TOOL_NAME}] OpenSpec guide: docs/openspec-getting-started.md`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (lastScanResult) {
|
|
231
|
+
setExitCodeFromScan({
|
|
232
|
+
...lastScanResult,
|
|
233
|
+
errors: aggregateErrors,
|
|
234
|
+
warnings: aggregateWarnings,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
module.exports = {
|
|
240
|
+
setup,
|
|
241
|
+
printRequiredSystemToolStatus,
|
|
242
|
+
};
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
// `gx status` — service/repo health summary + companion-tool prompts.
|
|
2
|
+
// Pure code-motion from src/cli/main.js — no behavior changes.
|
|
3
|
+
const {
|
|
4
|
+
path,
|
|
5
|
+
packageJson,
|
|
6
|
+
TOOL_NAME,
|
|
7
|
+
SHORT_TOOL_NAME,
|
|
8
|
+
GLOBAL_TOOLCHAIN_PACKAGES,
|
|
9
|
+
envFlagIsTruthy,
|
|
10
|
+
} = require('../../context');
|
|
11
|
+
const { isGitRepo } = require('../../git');
|
|
12
|
+
const toolchainModule = require('../../toolchain');
|
|
13
|
+
const {
|
|
14
|
+
runtimeVersion,
|
|
15
|
+
statusDot,
|
|
16
|
+
printToolLogsSummary,
|
|
17
|
+
getInvokedCliName,
|
|
18
|
+
} = require('../../output');
|
|
19
|
+
const { parseCommonArgs } = require('../args');
|
|
20
|
+
const { extractFlag } = require('../dispatch');
|
|
21
|
+
const {
|
|
22
|
+
runScanInternal,
|
|
23
|
+
countAgentWorktrees,
|
|
24
|
+
deriveNextStepHint,
|
|
25
|
+
printStatusRepairHint,
|
|
26
|
+
} = require('../shared/scaffolding');
|
|
27
|
+
const { describeGuardexRepoToggle } = require('../shared/repo-env');
|
|
28
|
+
|
|
29
|
+
function collectServicesSnapshot() {
|
|
30
|
+
const toolchain = toolchainModule.detectGlobalToolchainPackages();
|
|
31
|
+
const npmServices = GLOBAL_TOOLCHAIN_PACKAGES.map((pkg) => {
|
|
32
|
+
const service = toolchainModule.getGlobalToolchainService(pkg);
|
|
33
|
+
if (!toolchain.ok) {
|
|
34
|
+
return {
|
|
35
|
+
name: service.name,
|
|
36
|
+
displayName: service.name,
|
|
37
|
+
packageName: pkg,
|
|
38
|
+
dependencyUrl: service.dependencyUrl || null,
|
|
39
|
+
status: 'unknown',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
name: service.name,
|
|
44
|
+
displayName: service.name,
|
|
45
|
+
packageName: pkg,
|
|
46
|
+
dependencyUrl: service.dependencyUrl || null,
|
|
47
|
+
status: toolchain.installed.includes(pkg) ? 'active' : 'inactive',
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
const localCompanionServices = toolchainModule.detectOptionalLocalCompanionTools().map((tool) => ({
|
|
51
|
+
name: tool.name,
|
|
52
|
+
displayName: tool.displayName || tool.name,
|
|
53
|
+
installCommand: tool.installCommand,
|
|
54
|
+
installArgs: Array.isArray(tool.installArgs) ? [...tool.installArgs] : [],
|
|
55
|
+
status: tool.status,
|
|
56
|
+
}));
|
|
57
|
+
const requiredSystemTools = toolchainModule.detectRequiredSystemTools();
|
|
58
|
+
const services = [
|
|
59
|
+
...npmServices,
|
|
60
|
+
...localCompanionServices.map((tool) => ({
|
|
61
|
+
name: tool.name,
|
|
62
|
+
displayName: tool.displayName,
|
|
63
|
+
status: tool.status,
|
|
64
|
+
})),
|
|
65
|
+
...requiredSystemTools.map((tool) => ({
|
|
66
|
+
name: tool.name,
|
|
67
|
+
displayName: tool.displayName || tool.name,
|
|
68
|
+
command: tool.command,
|
|
69
|
+
status: tool.status,
|
|
70
|
+
})),
|
|
71
|
+
];
|
|
72
|
+
return { toolchain, npmServices, localCompanionServices, requiredSystemTools, services };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function maybePromptInstallMissingCompanions(snapshot) {
|
|
76
|
+
if (envFlagIsTruthy(process.env.GUARDEX_SKIP_COMPANION_PROMPT)) {
|
|
77
|
+
return { handled: false, installed: false };
|
|
78
|
+
}
|
|
79
|
+
const interactive = Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY);
|
|
80
|
+
const autoApproval = toolchainModule.parseAutoApproval('GUARDEX_AUTO_COMPANION_APPROVAL');
|
|
81
|
+
if (!interactive && autoApproval == null) {
|
|
82
|
+
return { handled: false, installed: false };
|
|
83
|
+
}
|
|
84
|
+
if (!snapshot.toolchain.ok) {
|
|
85
|
+
return { handled: false, installed: false };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const missingPackages = snapshot.npmServices
|
|
89
|
+
.filter((service) => service.status !== 'active')
|
|
90
|
+
.map((service) => service.packageName);
|
|
91
|
+
const missingLocalTools = snapshot.localCompanionServices.filter((tool) => tool.status !== 'active');
|
|
92
|
+
if (missingPackages.length === 0 && missingLocalTools.length === 0) {
|
|
93
|
+
return { handled: false, installed: false };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const missingNames = [
|
|
97
|
+
...missingPackages.map((pkg) => toolchainModule.formatGlobalToolchainServiceName(pkg)),
|
|
98
|
+
...missingLocalTools.map((tool) => tool.displayName || tool.name),
|
|
99
|
+
];
|
|
100
|
+
console.log(`[${TOOL_NAME}] Missing companion tools: ${missingNames.join(', ')}.`);
|
|
101
|
+
|
|
102
|
+
const promptText = toolchainModule.buildMissingCompanionInstallPrompt(missingPackages, missingLocalTools);
|
|
103
|
+
const approved = interactive
|
|
104
|
+
? toolchainModule.promptYesNoStrict(promptText)
|
|
105
|
+
: autoApproval;
|
|
106
|
+
|
|
107
|
+
if (!approved) {
|
|
108
|
+
console.log(
|
|
109
|
+
`[${TOOL_NAME}] Skipped companion install. Set GUARDEX_SKIP_COMPANION_PROMPT=1 to silence this prompt, ` +
|
|
110
|
+
`or run '${getInvokedCliName()} setup --install-only' later to install manually.`,
|
|
111
|
+
);
|
|
112
|
+
return { handled: true, installed: false };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const result = toolchainModule.performCompanionInstall(missingPackages, missingLocalTools);
|
|
116
|
+
if (result.status === 'installed') {
|
|
117
|
+
console.log(
|
|
118
|
+
`[${TOOL_NAME}] ✅ Companion tools installed (${(result.packages || []).join(', ')}).`,
|
|
119
|
+
);
|
|
120
|
+
return { handled: true, installed: true };
|
|
121
|
+
}
|
|
122
|
+
if (result.status === 'failed') {
|
|
123
|
+
console.log(
|
|
124
|
+
`[${TOOL_NAME}] ⚠️ Companion install failed: ${result.reason}. ` +
|
|
125
|
+
`Retry with '${getInvokedCliName()} setup --install-only'.`,
|
|
126
|
+
);
|
|
127
|
+
return { handled: true, installed: false };
|
|
128
|
+
}
|
|
129
|
+
return { handled: true, installed: false };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function status(rawArgs) {
|
|
133
|
+
const { found: verboseFlag, remaining: afterVerbose } = extractFlag(rawArgs, '--verbose');
|
|
134
|
+
const options = parseCommonArgs(afterVerbose, {
|
|
135
|
+
target: process.cwd(),
|
|
136
|
+
json: false,
|
|
137
|
+
});
|
|
138
|
+
const forceExpand = envFlagIsTruthy(process.env.GUARDEX_VERBOSE_STATUS) || verboseFlag;
|
|
139
|
+
const invokedBasename = getInvokedCliName();
|
|
140
|
+
|
|
141
|
+
let snapshot = collectServicesSnapshot();
|
|
142
|
+
if (!options.json) {
|
|
143
|
+
const result = maybePromptInstallMissingCompanions(snapshot);
|
|
144
|
+
if (result.installed) {
|
|
145
|
+
snapshot = collectServicesSnapshot();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
let { toolchain, npmServices, localCompanionServices, requiredSystemTools, services } = snapshot;
|
|
149
|
+
|
|
150
|
+
const targetPath = path.resolve(options.target);
|
|
151
|
+
const inGitRepo = isGitRepo(targetPath);
|
|
152
|
+
const scanResult = inGitRepo ? runScanInternal({ target: targetPath, json: false }) : null;
|
|
153
|
+
const repoServiceStatus = scanResult
|
|
154
|
+
? (scanResult.guardexEnabled === false
|
|
155
|
+
? 'disabled'
|
|
156
|
+
: (scanResult.errors === 0 && scanResult.warnings === 0 ? 'active' : 'degraded'))
|
|
157
|
+
: 'inactive';
|
|
158
|
+
|
|
159
|
+
const payload = {
|
|
160
|
+
cli: {
|
|
161
|
+
name: packageJson.name,
|
|
162
|
+
version: packageJson.version,
|
|
163
|
+
runtime: runtimeVersion(),
|
|
164
|
+
},
|
|
165
|
+
services,
|
|
166
|
+
repo: {
|
|
167
|
+
target: targetPath,
|
|
168
|
+
inGitRepo,
|
|
169
|
+
serviceStatus: repoServiceStatus,
|
|
170
|
+
guardexEnabled: scanResult ? scanResult.guardexEnabled !== false : null,
|
|
171
|
+
guardexToggle: scanResult ? scanResult.guardexToggle || null : null,
|
|
172
|
+
scan: scanResult
|
|
173
|
+
? {
|
|
174
|
+
repoRoot: scanResult.repoRoot,
|
|
175
|
+
branch: scanResult.branch,
|
|
176
|
+
errors: scanResult.errors,
|
|
177
|
+
warnings: scanResult.warnings,
|
|
178
|
+
findings: scanResult.findings.length,
|
|
179
|
+
}
|
|
180
|
+
: null,
|
|
181
|
+
},
|
|
182
|
+
detectionError: toolchain.ok ? null : toolchain.error,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
if (options.json) {
|
|
186
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
187
|
+
process.exitCode = 0;
|
|
188
|
+
return payload;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const compact = !forceExpand;
|
|
192
|
+
const activeServiceCount = services.filter((service) => service.status === 'active').length;
|
|
193
|
+
const inactiveServiceCount = services.length - activeServiceCount;
|
|
194
|
+
|
|
195
|
+
console.log(`[${TOOL_NAME}] CLI: ${payload.cli.runtime}`);
|
|
196
|
+
if (!toolchain.ok) {
|
|
197
|
+
const detectionError = compact
|
|
198
|
+
? String(toolchain.error || '').split(/\r?\n/).find(Boolean) || 'unknown error'
|
|
199
|
+
: toolchain.error;
|
|
200
|
+
console.log(`[${TOOL_NAME}] ⚠️ Could not detect global services: ${detectionError}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (compact) {
|
|
204
|
+
const serviceSummary = inactiveServiceCount === 0
|
|
205
|
+
? `${activeServiceCount}/${services.length} ${statusDot('active')} active`
|
|
206
|
+
: `${activeServiceCount}/${services.length} ${statusDot('degraded')} active (${inactiveServiceCount} inactive)`;
|
|
207
|
+
console.log(
|
|
208
|
+
`[${TOOL_NAME}] Global services: ${serviceSummary}`,
|
|
209
|
+
);
|
|
210
|
+
} else {
|
|
211
|
+
console.log(`[${TOOL_NAME}] Global services:`);
|
|
212
|
+
for (const service of services) {
|
|
213
|
+
const serviceLabel = service.displayName || service.name;
|
|
214
|
+
console.log(` - ${statusDot(service.status)} ${serviceLabel}: ${service.status}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const inactiveOptionalCompanions = [...npmServices, ...localCompanionServices]
|
|
218
|
+
.filter((service) => service.status !== 'active')
|
|
219
|
+
.map((service) => service.displayName || service.name);
|
|
220
|
+
if (inactiveOptionalCompanions.length > 0) {
|
|
221
|
+
if (compact) {
|
|
222
|
+
console.log(
|
|
223
|
+
`[${TOOL_NAME}] Optional companion tools inactive: ${inactiveOptionalCompanions.length} (run '${SHORT_TOOL_NAME} setup')`,
|
|
224
|
+
);
|
|
225
|
+
} else {
|
|
226
|
+
console.log(
|
|
227
|
+
`[${TOOL_NAME}] Optional companion tools inactive: ${inactiveOptionalCompanions.join(', ')}`,
|
|
228
|
+
);
|
|
229
|
+
for (const warning of toolchainModule.describeMissingGlobalDependencyWarnings(
|
|
230
|
+
npmServices
|
|
231
|
+
.filter((service) => service.status === 'inactive')
|
|
232
|
+
.map((service) => service.packageName),
|
|
233
|
+
)) {
|
|
234
|
+
console.log(`[${TOOL_NAME}] ${warning}`);
|
|
235
|
+
}
|
|
236
|
+
console.log(
|
|
237
|
+
`[${TOOL_NAME}] Run '${SHORT_TOOL_NAME} setup' to install missing companions with an explicit Y/N prompt.`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const missingSystemTools = requiredSystemTools.filter((tool) => tool.status !== 'active');
|
|
242
|
+
if (missingSystemTools.length > 0) {
|
|
243
|
+
const tools = missingSystemTools
|
|
244
|
+
.map((tool) => tool.displayName || tool.name)
|
|
245
|
+
.join(', ');
|
|
246
|
+
console.log(`[${TOOL_NAME}] ⚠️ Missing required system tool(s): ${tools}`);
|
|
247
|
+
if (!compact) {
|
|
248
|
+
for (const tool of missingSystemTools) {
|
|
249
|
+
const reasonText = tool.reason ? ` (${tool.reason})` : '';
|
|
250
|
+
console.log(` - install ${tool.name}: ${tool.installHint}${reasonText}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!scanResult) {
|
|
256
|
+
console.log(
|
|
257
|
+
`[${TOOL_NAME}] Repo safety service: ${statusDot('inactive')} inactive (no git repository at target).`,
|
|
258
|
+
);
|
|
259
|
+
const inactiveHint = deriveNextStepHint({
|
|
260
|
+
scanResult: null,
|
|
261
|
+
worktreeCount: 0,
|
|
262
|
+
invoked: invokedBasename,
|
|
263
|
+
inGitRepo,
|
|
264
|
+
});
|
|
265
|
+
console.log(`[${TOOL_NAME}] Next: ${inactiveHint}`);
|
|
266
|
+
printToolLogsSummary({ invokedBasename, compact });
|
|
267
|
+
process.exitCode = 0;
|
|
268
|
+
return payload;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (scanResult.guardexEnabled === false) {
|
|
272
|
+
console.log(
|
|
273
|
+
`[${TOOL_NAME}] Repo safety service: ${statusDot('disabled')} disabled (${describeGuardexRepoToggle(scanResult.guardexToggle)}).`,
|
|
274
|
+
);
|
|
275
|
+
console.log(`[${TOOL_NAME}] Repo: ${scanResult.repoRoot}`);
|
|
276
|
+
console.log(`[${TOOL_NAME}] Branch: ${scanResult.branch}`);
|
|
277
|
+
const worktreeCountDisabled = countAgentWorktrees(scanResult.repoRoot);
|
|
278
|
+
if (worktreeCountDisabled > 0) {
|
|
279
|
+
const plural = worktreeCountDisabled === 1 ? 'worktree' : 'worktrees';
|
|
280
|
+
console.log(
|
|
281
|
+
`[${TOOL_NAME}] ⚠ ${worktreeCountDisabled} active agent ${plural} under .omc/agent-worktrees or .omx/agent-worktrees.`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
const disabledHint = deriveNextStepHint({
|
|
285
|
+
scanResult,
|
|
286
|
+
worktreeCount: worktreeCountDisabled,
|
|
287
|
+
invoked: invokedBasename,
|
|
288
|
+
inGitRepo,
|
|
289
|
+
});
|
|
290
|
+
console.log(`[${TOOL_NAME}] Next: ${disabledHint}`);
|
|
291
|
+
printToolLogsSummary({ invokedBasename, compact });
|
|
292
|
+
process.exitCode = 0;
|
|
293
|
+
return payload;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (scanResult.errors === 0 && scanResult.warnings === 0) {
|
|
297
|
+
console.log(`[${TOOL_NAME}] Repo safety service: ${statusDot('active')} active.`);
|
|
298
|
+
} else if (scanResult.errors === 0) {
|
|
299
|
+
console.log(
|
|
300
|
+
`[${TOOL_NAME}] Repo safety service: ${statusDot('degraded')} degraded (${scanResult.warnings} warning(s)).`,
|
|
301
|
+
);
|
|
302
|
+
} else if (scanResult.warnings === 0) {
|
|
303
|
+
console.log(
|
|
304
|
+
`[${TOOL_NAME}] Repo safety service: ${statusDot('degraded')} degraded (${scanResult.errors} error(s)).`,
|
|
305
|
+
);
|
|
306
|
+
} else {
|
|
307
|
+
console.log(
|
|
308
|
+
`[${TOOL_NAME}] Repo safety service: ${statusDot('degraded')} degraded (${scanResult.errors} error(s), ${scanResult.warnings} warning(s)).`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
printStatusRepairHint(scanResult);
|
|
312
|
+
console.log(`[${TOOL_NAME}] Repo: ${scanResult.repoRoot}`);
|
|
313
|
+
console.log(`[${TOOL_NAME}] Branch: ${scanResult.branch}`);
|
|
314
|
+
const worktreeCountActive = countAgentWorktrees(scanResult.repoRoot);
|
|
315
|
+
if (worktreeCountActive > 0) {
|
|
316
|
+
const plural = worktreeCountActive === 1 ? 'worktree' : 'worktrees';
|
|
317
|
+
console.log(
|
|
318
|
+
`[${TOOL_NAME}] ⚠ ${worktreeCountActive} active agent ${plural} → ${invokedBasename} finish --all`,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
const activeHint = deriveNextStepHint({
|
|
322
|
+
scanResult,
|
|
323
|
+
worktreeCount: worktreeCountActive,
|
|
324
|
+
invoked: invokedBasename,
|
|
325
|
+
inGitRepo,
|
|
326
|
+
});
|
|
327
|
+
console.log(`[${TOOL_NAME}] Next: ${activeHint}`);
|
|
328
|
+
printToolLogsSummary({ invokedBasename, compact });
|
|
329
|
+
|
|
330
|
+
process.exitCode = 0;
|
|
331
|
+
return payload;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
module.exports = {
|
|
335
|
+
status,
|
|
336
|
+
collectServicesSnapshot,
|
|
337
|
+
maybePromptInstallMissingCompanions,
|
|
338
|
+
};
|