@imdeadpool/guardex 7.0.18 → 7.0.20
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 +34 -19
- package/bin/multiagent-safety.js +2 -7784
- package/package.json +2 -1
- package/src/cli/args.js +7 -0
- package/src/cli/dispatch.js +86 -0
- package/src/cli/main.js +7719 -0
- package/src/context.js +503 -0
- package/src/core/runtime.js +119 -0
- package/src/finish/index.js +425 -0
- package/src/git/index.js +112 -0
- package/src/hooks/index.js +74 -0
- package/src/output/index.js +398 -0
- package/src/sandbox/index.js +68 -0
- package/src/scaffold/index.js +169 -0
- package/src/toolchain/index.js +223 -0
- package/templates/AGENTS.multiagent-safety.md +3 -0
- package/templates/codex/skills/gitguardex/SKILL.md +1 -1
- package/templates/codex/skills/guardex-merge-skills-to-dev/SKILL.md +3 -3
- package/templates/githooks/pre-commit +21 -2
- package/templates/scripts/agent-branch-finish.sh +32 -19
- package/templates/scripts/agent-branch-merge.sh +24 -5
- package/templates/scripts/agent-branch-start.sh +74 -36
- package/templates/scripts/agent-file-locks.py +11 -11
- package/templates/scripts/codex-agent.sh +179 -61
- package/templates/scripts/review-bot-watch.sh +30 -7
- package/templates/vscode/guardex-active-agents/README.md +16 -10
- package/templates/vscode/guardex-active-agents/extension.js +901 -49
- package/templates/vscode/guardex-active-agents/package.json +61 -1
- package/templates/vscode/guardex-active-agents/session-schema.js +211 -17
package/src/context.js
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const os = require('node:os');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const cp = require('node:child_process');
|
|
5
|
+
|
|
6
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
7
|
+
const CLI_ENTRY_PATH = path.join(PACKAGE_ROOT, 'bin', 'multiagent-safety.js');
|
|
8
|
+
const packageJsonPath = path.join(PACKAGE_ROOT, 'package.json');
|
|
9
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
10
|
+
|
|
11
|
+
const TOOL_NAME = 'gitguardex';
|
|
12
|
+
const SHORT_TOOL_NAME = 'gx';
|
|
13
|
+
if (!process.env.GUARDEX_CLI_ENTRY) {
|
|
14
|
+
process.env.GUARDEX_CLI_ENTRY = CLI_ENTRY_PATH;
|
|
15
|
+
}
|
|
16
|
+
if (!process.env.GUARDEX_NODE_BIN) {
|
|
17
|
+
process.env.GUARDEX_NODE_BIN = process.execPath;
|
|
18
|
+
}
|
|
19
|
+
const LEGACY_NAMES = ['guardex', 'multiagent-safety'];
|
|
20
|
+
const GLOBAL_INSTALL_COMMAND = `npm i -g ${packageJson.name}`;
|
|
21
|
+
const OPENSPEC_PACKAGE = '@fission-ai/openspec';
|
|
22
|
+
const OMC_PACKAGE = 'oh-my-claude-sisyphus';
|
|
23
|
+
const OMC_REPO_URL = 'https://github.com/Yeachan-Heo/oh-my-claudecode';
|
|
24
|
+
const CAVEMEM_PACKAGE = 'cavemem';
|
|
25
|
+
const NPX_BIN = process.env.GUARDEX_NPX_BIN || 'npx';
|
|
26
|
+
const GUARDEX_HOME_DIR = path.resolve(process.env.GUARDEX_HOME_DIR || os.homedir());
|
|
27
|
+
const GLOBAL_TOOLCHAIN_SERVICES = [
|
|
28
|
+
{ name: 'oh-my-codex', packageName: 'oh-my-codex' },
|
|
29
|
+
{
|
|
30
|
+
name: 'oh-my-claudecode',
|
|
31
|
+
packageName: OMC_PACKAGE,
|
|
32
|
+
dependencyUrl: OMC_REPO_URL,
|
|
33
|
+
},
|
|
34
|
+
{ name: OPENSPEC_PACKAGE, packageName: OPENSPEC_PACKAGE },
|
|
35
|
+
{ name: CAVEMEM_PACKAGE, packageName: CAVEMEM_PACKAGE },
|
|
36
|
+
{
|
|
37
|
+
name: '@imdeadpool/codex-account-switcher',
|
|
38
|
+
packageName: '@imdeadpool/codex-account-switcher',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
const GLOBAL_TOOLCHAIN_PACKAGES = [
|
|
42
|
+
...GLOBAL_TOOLCHAIN_SERVICES.map((service) => service.packageName),
|
|
43
|
+
];
|
|
44
|
+
const OPTIONAL_LOCAL_COMPANION_TOOLS = [
|
|
45
|
+
{
|
|
46
|
+
name: 'cavekit',
|
|
47
|
+
candidatePaths: [
|
|
48
|
+
'.cavekit/plugin.json',
|
|
49
|
+
'.codex/local-marketplaces/cavekit/.agents/plugins/marketplace.json',
|
|
50
|
+
],
|
|
51
|
+
installCommand: `${NPX_BIN} skills add JuliusBrussee/cavekit`,
|
|
52
|
+
installArgs: ['skills', 'add', 'JuliusBrussee/cavekit'],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'caveman',
|
|
56
|
+
candidatePaths: [
|
|
57
|
+
'.config/caveman/config.json',
|
|
58
|
+
'.cavekit/skills/caveman/SKILL.md',
|
|
59
|
+
],
|
|
60
|
+
installCommand: `${NPX_BIN} skills add JuliusBrussee/caveman`,
|
|
61
|
+
installArgs: ['skills', 'add', 'JuliusBrussee/caveman'],
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
const GH_BIN = process.env.GUARDEX_GH_BIN || 'gh';
|
|
65
|
+
const REQUIRED_SYSTEM_TOOLS = [
|
|
66
|
+
{
|
|
67
|
+
name: 'gh',
|
|
68
|
+
displayName: 'GitHub (gh)',
|
|
69
|
+
command: GH_BIN,
|
|
70
|
+
installHint: 'https://cli.github.com/',
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
const MAINTAINER_RELEASE_REPO = path.resolve(
|
|
74
|
+
process.env.GUARDEX_RELEASE_REPO || path.resolve(PACKAGE_ROOT),
|
|
75
|
+
);
|
|
76
|
+
const NPM_BIN = process.env.GUARDEX_NPM_BIN || 'npm';
|
|
77
|
+
const OPENSPEC_BIN = process.env.GUARDEX_OPENSPEC_BIN || 'openspec';
|
|
78
|
+
const SCORECARD_BIN = process.env.GUARDEX_SCORECARD_BIN || 'scorecard';
|
|
79
|
+
const GIT_PROTECTED_BRANCHES_KEY = 'multiagent.protectedBranches';
|
|
80
|
+
const GIT_BASE_BRANCH_KEY = 'multiagent.baseBranch';
|
|
81
|
+
const GIT_SYNC_STRATEGY_KEY = 'multiagent.sync.strategy';
|
|
82
|
+
const GUARDEX_REPO_TOGGLE_ENV = 'GUARDEX_ON';
|
|
83
|
+
const DEFAULT_PROTECTED_BRANCHES = ['dev', 'main', 'master'];
|
|
84
|
+
const DEFAULT_BASE_BRANCH = 'dev';
|
|
85
|
+
const DEFAULT_SYNC_STRATEGY = 'rebase';
|
|
86
|
+
const DEFAULT_SHADOW_CLEANUP_IDLE_MINUTES = 60;
|
|
87
|
+
const COMPOSE_HINT_FILES = [
|
|
88
|
+
'docker-compose.yml',
|
|
89
|
+
'docker-compose.yaml',
|
|
90
|
+
'compose.yml',
|
|
91
|
+
'compose.yaml',
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const TEMPLATE_ROOT = path.join(PACKAGE_ROOT, 'templates');
|
|
95
|
+
|
|
96
|
+
const HOOK_NAMES = ['pre-commit', 'pre-push', 'post-merge', 'post-checkout'];
|
|
97
|
+
|
|
98
|
+
function toDestinationPath(relativeTemplatePath) {
|
|
99
|
+
if (relativeTemplatePath.startsWith('scripts/')) {
|
|
100
|
+
return relativeTemplatePath;
|
|
101
|
+
}
|
|
102
|
+
if (relativeTemplatePath.startsWith('githooks/')) {
|
|
103
|
+
return `.${relativeTemplatePath}`;
|
|
104
|
+
}
|
|
105
|
+
if (relativeTemplatePath.startsWith('codex/')) {
|
|
106
|
+
return `.${relativeTemplatePath}`;
|
|
107
|
+
}
|
|
108
|
+
if (relativeTemplatePath.startsWith('claude/')) {
|
|
109
|
+
return `.${relativeTemplatePath}`;
|
|
110
|
+
}
|
|
111
|
+
if (relativeTemplatePath.startsWith('github/')) {
|
|
112
|
+
return `.${relativeTemplatePath}`;
|
|
113
|
+
}
|
|
114
|
+
if (relativeTemplatePath.startsWith('vscode/')) {
|
|
115
|
+
return relativeTemplatePath;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Unsupported template path: ${relativeTemplatePath}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const TEMPLATE_FILES = [
|
|
121
|
+
'scripts/agent-session-state.js',
|
|
122
|
+
'scripts/guardex-docker-loader.sh',
|
|
123
|
+
'scripts/guardex-env.sh',
|
|
124
|
+
'scripts/install-vscode-active-agents-extension.js',
|
|
125
|
+
'github/pull.yml.example',
|
|
126
|
+
'github/workflows/cr.yml',
|
|
127
|
+
'vscode/guardex-active-agents/package.json',
|
|
128
|
+
'vscode/guardex-active-agents/extension.js',
|
|
129
|
+
'vscode/guardex-active-agents/session-schema.js',
|
|
130
|
+
'vscode/guardex-active-agents/README.md',
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const LEGACY_WORKFLOW_SHIM_SPECS = [
|
|
134
|
+
{ relativePath: 'scripts/agent-branch-start.sh', kind: 'shell', command: ['branch', 'start'] },
|
|
135
|
+
{ relativePath: 'scripts/agent-branch-finish.sh', kind: 'shell', command: ['branch', 'finish'] },
|
|
136
|
+
{ relativePath: 'scripts/agent-branch-merge.sh', kind: 'shell', command: ['branch', 'merge'] },
|
|
137
|
+
{ relativePath: 'scripts/codex-agent.sh', kind: 'shell', command: ['internal', 'run-shell', 'codexAgent'] },
|
|
138
|
+
{ relativePath: 'scripts/review-bot-watch.sh', kind: 'shell', command: ['internal', 'run-shell', 'reviewBot'] },
|
|
139
|
+
{ relativePath: 'scripts/agent-worktree-prune.sh', kind: 'shell', command: ['worktree', 'prune'] },
|
|
140
|
+
{ relativePath: 'scripts/agent-file-locks.py', kind: 'python', command: ['locks'] },
|
|
141
|
+
{ relativePath: 'scripts/openspec/init-plan-workspace.sh', kind: 'shell', command: ['internal', 'run-shell', 'planInit'] },
|
|
142
|
+
{ relativePath: 'scripts/openspec/init-change-workspace.sh', kind: 'shell', command: ['internal', 'run-shell', 'changeInit'] },
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const LEGACY_WORKFLOW_SHIMS = LEGACY_WORKFLOW_SHIM_SPECS.map((entry) => entry.relativePath);
|
|
146
|
+
|
|
147
|
+
const MANAGED_TEMPLATE_DESTINATIONS = TEMPLATE_FILES.map((entry) => toDestinationPath(entry));
|
|
148
|
+
const MANAGED_TEMPLATE_SCRIPT_FILES = MANAGED_TEMPLATE_DESTINATIONS.filter((entry) =>
|
|
149
|
+
entry.startsWith('scripts/'),
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const LEGACY_MANAGED_REPO_FILES = [
|
|
153
|
+
...LEGACY_WORKFLOW_SHIMS,
|
|
154
|
+
'scripts/agent-session-state.js',
|
|
155
|
+
'scripts/guardex-docker-loader.sh',
|
|
156
|
+
'scripts/install-vscode-active-agents-extension.js',
|
|
157
|
+
'scripts/guardex-env.sh',
|
|
158
|
+
'scripts/install-agent-git-hooks.sh',
|
|
159
|
+
'.githooks/pre-commit',
|
|
160
|
+
'.githooks/pre-push',
|
|
161
|
+
'.githooks/post-merge',
|
|
162
|
+
'.githooks/post-checkout',
|
|
163
|
+
'.codex/skills/gitguardex/SKILL.md',
|
|
164
|
+
'.codex/skills/guardex-merge-skills-to-dev/SKILL.md',
|
|
165
|
+
'.claude/commands/gitguardex.md',
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const REQUIRED_MANAGED_REPO_FILES = [
|
|
169
|
+
...MANAGED_TEMPLATE_DESTINATIONS,
|
|
170
|
+
...HOOK_NAMES.map((entry) => path.posix.join('.githooks', entry)),
|
|
171
|
+
'.omx/state/agent-file-locks.json',
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const LEGACY_MANAGED_PACKAGE_SCRIPTS = {
|
|
175
|
+
'agent:codex': 'bash ./scripts/codex-agent.sh',
|
|
176
|
+
'agent:branch:start': 'bash ./scripts/agent-branch-start.sh',
|
|
177
|
+
'agent:branch:finish': 'bash ./scripts/agent-branch-finish.sh',
|
|
178
|
+
'agent:branch:merge': 'bash ./scripts/agent-branch-merge.sh',
|
|
179
|
+
'agent:cleanup': 'gx cleanup',
|
|
180
|
+
'agent:hooks:install': 'bash ./scripts/install-agent-git-hooks.sh',
|
|
181
|
+
'agent:locks:claim': 'python3 ./scripts/agent-file-locks.py claim',
|
|
182
|
+
'agent:locks:allow-delete': 'python3 ./scripts/agent-file-locks.py allow-delete',
|
|
183
|
+
'agent:locks:release': 'python3 ./scripts/agent-file-locks.py release',
|
|
184
|
+
'agent:locks:status': 'python3 ./scripts/agent-file-locks.py status',
|
|
185
|
+
'agent:plan:init': 'bash ./scripts/openspec/init-plan-workspace.sh',
|
|
186
|
+
'agent:change:init': 'bash ./scripts/openspec/init-change-workspace.sh',
|
|
187
|
+
'agent:protect:list': 'gx protect list',
|
|
188
|
+
'agent:branch:sync': 'gx sync',
|
|
189
|
+
'agent:branch:sync:check': 'gx sync --check',
|
|
190
|
+
'agent:safety:setup': 'gx setup',
|
|
191
|
+
'agent:safety:scan': 'gx status --strict',
|
|
192
|
+
'agent:safety:fix': 'gx setup --repair',
|
|
193
|
+
'agent:safety:doctor': 'gx doctor',
|
|
194
|
+
'agent:docker:load': 'bash ./scripts/guardex-docker-loader.sh',
|
|
195
|
+
'agent:review:watch': 'bash ./scripts/review-bot-watch.sh',
|
|
196
|
+
'agent:finish': 'gx finish --all',
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const PACKAGE_SCRIPT_ASSETS = {
|
|
200
|
+
branchStart: path.join(TEMPLATE_ROOT, 'scripts', 'agent-branch-start.sh'),
|
|
201
|
+
branchFinish: path.join(TEMPLATE_ROOT, 'scripts', 'agent-branch-finish.sh'),
|
|
202
|
+
branchMerge: path.join(TEMPLATE_ROOT, 'scripts', 'agent-branch-merge.sh'),
|
|
203
|
+
codexAgent: path.join(TEMPLATE_ROOT, 'scripts', 'codex-agent.sh'),
|
|
204
|
+
reviewBot: path.join(TEMPLATE_ROOT, 'scripts', 'review-bot-watch.sh'),
|
|
205
|
+
worktreePrune: path.join(TEMPLATE_ROOT, 'scripts', 'agent-worktree-prune.sh'),
|
|
206
|
+
lockTool: path.join(TEMPLATE_ROOT, 'scripts', 'agent-file-locks.py'),
|
|
207
|
+
planInit: path.join(TEMPLATE_ROOT, 'scripts', 'openspec', 'init-plan-workspace.sh'),
|
|
208
|
+
changeInit: path.join(TEMPLATE_ROOT, 'scripts', 'openspec', 'init-change-workspace.sh'),
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const USER_LEVEL_SKILL_ASSETS = [
|
|
212
|
+
{
|
|
213
|
+
source: path.join(TEMPLATE_ROOT, 'codex', 'skills', 'gitguardex', 'SKILL.md'),
|
|
214
|
+
destination: path.join('.codex', 'skills', 'gitguardex', 'SKILL.md'),
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
source: path.join(TEMPLATE_ROOT, 'codex', 'skills', 'guardex-merge-skills-to-dev', 'SKILL.md'),
|
|
218
|
+
destination: path.join('.codex', 'skills', 'guardex-merge-skills-to-dev', 'SKILL.md'),
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
source: path.join(TEMPLATE_ROOT, 'claude', 'commands', 'gitguardex.md'),
|
|
222
|
+
destination: path.join('.claude', 'commands', 'gitguardex.md'),
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
const EXECUTABLE_RELATIVE_PATHS = new Set([
|
|
227
|
+
...MANAGED_TEMPLATE_SCRIPT_FILES,
|
|
228
|
+
...HOOK_NAMES.map((entry) => path.posix.join('.githooks', entry)),
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
const CRITICAL_GUARDRAIL_PATHS = new Set([
|
|
232
|
+
'AGENTS.md',
|
|
233
|
+
...HOOK_NAMES.map((entry) => path.posix.join('.githooks', entry)),
|
|
234
|
+
'scripts/guardex-env.sh',
|
|
235
|
+
]);
|
|
236
|
+
|
|
237
|
+
const LOCK_FILE_RELATIVE = '.omx/state/agent-file-locks.json';
|
|
238
|
+
const AGENTS_BOTS_STATE_RELATIVE = '.omx/state/agents-bots.json';
|
|
239
|
+
const AGENTS_MARKER_START = '<!-- multiagent-safety:START -->';
|
|
240
|
+
const AGENTS_MARKER_END = '<!-- multiagent-safety:END -->';
|
|
241
|
+
const GITIGNORE_MARKER_START = '# multiagent-safety:START';
|
|
242
|
+
const GITIGNORE_MARKER_END = '# multiagent-safety:END';
|
|
243
|
+
const CODEX_WORKTREE_RELATIVE_DIR = path.join('.omx', 'agent-worktrees');
|
|
244
|
+
const CLAUDE_WORKTREE_RELATIVE_DIR = path.join('.omc', 'agent-worktrees');
|
|
245
|
+
const AGENT_WORKTREE_RELATIVE_DIRS = [
|
|
246
|
+
CODEX_WORKTREE_RELATIVE_DIR,
|
|
247
|
+
CLAUDE_WORKTREE_RELATIVE_DIR,
|
|
248
|
+
];
|
|
249
|
+
const MANAGED_GITIGNORE_PATHS = [
|
|
250
|
+
'.omx/',
|
|
251
|
+
'.omc/',
|
|
252
|
+
'scripts/agent-session-state.js',
|
|
253
|
+
'scripts/guardex-docker-loader.sh',
|
|
254
|
+
'scripts/guardex-env.sh',
|
|
255
|
+
'scripts/install-vscode-active-agents-extension.js',
|
|
256
|
+
'.githooks',
|
|
257
|
+
'oh-my-codex/',
|
|
258
|
+
LOCK_FILE_RELATIVE,
|
|
259
|
+
];
|
|
260
|
+
const REPO_SCAFFOLD_DIRECTORIES = ['bin'];
|
|
261
|
+
const OMX_SCAFFOLD_DIRECTORIES = [
|
|
262
|
+
'.omx',
|
|
263
|
+
'.omx/state',
|
|
264
|
+
'.omx/logs',
|
|
265
|
+
'.omx/plans',
|
|
266
|
+
CODEX_WORKTREE_RELATIVE_DIR,
|
|
267
|
+
'.omc',
|
|
268
|
+
CLAUDE_WORKTREE_RELATIVE_DIR,
|
|
269
|
+
];
|
|
270
|
+
const OMX_SCAFFOLD_FILES = new Map([
|
|
271
|
+
['.omx/notepad.md', '\n\n## WORKING MEMORY\n'],
|
|
272
|
+
['.omx/project-memory.json', '{}\n'],
|
|
273
|
+
]);
|
|
274
|
+
const TARGETED_FORCEABLE_MANAGED_PATHS = new Set([
|
|
275
|
+
'AGENTS.md',
|
|
276
|
+
'.gitignore',
|
|
277
|
+
...Array.from(OMX_SCAFFOLD_FILES.keys()),
|
|
278
|
+
...REQUIRED_MANAGED_REPO_FILES,
|
|
279
|
+
...LEGACY_WORKFLOW_SHIMS,
|
|
280
|
+
]);
|
|
281
|
+
const COMMAND_TYPO_ALIASES = new Map([
|
|
282
|
+
['relaese', 'release'],
|
|
283
|
+
['realaese', 'release'],
|
|
284
|
+
['relase', 'release'],
|
|
285
|
+
['setpu', 'setup'],
|
|
286
|
+
['inti', 'init'],
|
|
287
|
+
['intsall', 'install'],
|
|
288
|
+
['docter', 'doctor'],
|
|
289
|
+
['doctro', 'doctor'],
|
|
290
|
+
['cleunup', 'cleanup'],
|
|
291
|
+
['scna', 'scan'],
|
|
292
|
+
]);
|
|
293
|
+
const SUGGESTIBLE_COMMANDS = [
|
|
294
|
+
'status',
|
|
295
|
+
'setup',
|
|
296
|
+
'doctor',
|
|
297
|
+
'branch',
|
|
298
|
+
'locks',
|
|
299
|
+
'worktree',
|
|
300
|
+
'hook',
|
|
301
|
+
'migrate',
|
|
302
|
+
'install-agent-skills',
|
|
303
|
+
'agents',
|
|
304
|
+
'merge',
|
|
305
|
+
'finish',
|
|
306
|
+
'report',
|
|
307
|
+
'protect',
|
|
308
|
+
'sync',
|
|
309
|
+
'cleanup',
|
|
310
|
+
'prompt',
|
|
311
|
+
'help',
|
|
312
|
+
'version',
|
|
313
|
+
'init',
|
|
314
|
+
'install',
|
|
315
|
+
'fix',
|
|
316
|
+
'scan',
|
|
317
|
+
'review',
|
|
318
|
+
'copy-prompt',
|
|
319
|
+
'copy-commands',
|
|
320
|
+
'print-agents-snippet',
|
|
321
|
+
'release',
|
|
322
|
+
];
|
|
323
|
+
const CLI_COMMAND_DESCRIPTIONS = [
|
|
324
|
+
['status', 'Show GitGuardex CLI + service health without modifying files'],
|
|
325
|
+
['setup', 'Install, repair, and verify guardrails (flags: --repair, --install-only, --target)'],
|
|
326
|
+
['doctor', 'Repair drift + verify (auto-sandboxes on protected main)'],
|
|
327
|
+
['branch', 'CLI-owned branch workflow surface (start/finish/merge)'],
|
|
328
|
+
['locks', 'CLI-owned file lock surface (claim/allow-delete/release/status/validate)'],
|
|
329
|
+
['worktree', 'CLI-owned worktree cleanup surface (prune)'],
|
|
330
|
+
['hook', 'Hook dispatch/install surface used by managed shims'],
|
|
331
|
+
['migrate', 'Convert legacy repo-local installs to the zero-copy CLI-owned surface'],
|
|
332
|
+
['install-agent-skills', 'Install Guardex Codex/Claude skills into the user home'],
|
|
333
|
+
['protect', 'Manage protected branches (list/add/remove/set/reset)'],
|
|
334
|
+
['merge', 'Create/reuse an integration lane and merge overlapping agent branches'],
|
|
335
|
+
['sync', 'Sync agent branches with origin/<base>'],
|
|
336
|
+
['finish', 'Commit + PR + merge completed agent branches (--all, --branch)'],
|
|
337
|
+
['cleanup', 'Prune merged/stale agent branches and worktrees'],
|
|
338
|
+
['release', 'Create or update the current GitHub release with README-generated notes'],
|
|
339
|
+
['agents', 'Start/stop repo-scoped review + cleanup bots'],
|
|
340
|
+
['prompt', 'Print AI setup checklist (--exec, --snippet)'],
|
|
341
|
+
['report', 'Security/safety reports (e.g. OpenSSF scorecard)'],
|
|
342
|
+
['help', 'Show this help output'],
|
|
343
|
+
['version', 'Print GitGuardex version'],
|
|
344
|
+
];
|
|
345
|
+
const DEPRECATED_COMMAND_ALIASES = new Map([
|
|
346
|
+
['init', { target: 'setup', hint: 'gx setup' }],
|
|
347
|
+
['install', { target: 'setup', hint: 'gx setup --install-only' }],
|
|
348
|
+
['fix', { target: 'setup', hint: 'gx setup --repair' }],
|
|
349
|
+
['scan', { target: 'status', hint: 'gx status --strict' }],
|
|
350
|
+
['copy-prompt', { target: 'prompt', hint: 'gx prompt' }],
|
|
351
|
+
['copy-commands', { target: 'prompt', hint: 'gx prompt --exec' }],
|
|
352
|
+
['print-agents-snippet', { target: 'prompt', hint: 'gx prompt --snippet' }],
|
|
353
|
+
['review', { target: 'agents', hint: 'gx agents start (runs review + cleanup)' }],
|
|
354
|
+
]);
|
|
355
|
+
const AGENT_BOT_DESCRIPTIONS = [
|
|
356
|
+
['agents', 'Start/stop review + cleanup bots for this repo'],
|
|
357
|
+
];
|
|
358
|
+
const DOCTOR_AUTO_FINISH_DETAIL_LIMIT = 6;
|
|
359
|
+
const DOCTOR_AUTO_FINISH_BRANCH_LABEL_MAX = 72;
|
|
360
|
+
const DOCTOR_AUTO_FINISH_MESSAGE_MAX = 160;
|
|
361
|
+
|
|
362
|
+
function envFlagIsTruthy(raw) {
|
|
363
|
+
const lowered = String(raw || '').trim().toLowerCase();
|
|
364
|
+
return lowered === '1' || lowered === 'true' || lowered === 'yes' || lowered === 'on';
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function isClaudeCodeSession(env = process.env) {
|
|
368
|
+
return envFlagIsTruthy(env.CLAUDECODE) || Boolean(env.CLAUDE_CODE_SESSION_ID);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function defaultAgentWorktreeRelativeDir(env = process.env) {
|
|
372
|
+
return isClaudeCodeSession(env) ? CLAUDE_WORKTREE_RELATIVE_DIR : CODEX_WORKTREE_RELATIVE_DIR;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const AI_SETUP_PROMPT = `GitGuardex (gx) setup checklist for Codex/Claude in this repo.
|
|
376
|
+
|
|
377
|
+
1) Install: ${GLOBAL_INSTALL_COMMAND} && gh --version
|
|
378
|
+
2) Bootstrap: gx setup
|
|
379
|
+
3) Repair: gx doctor
|
|
380
|
+
4) Task loop: gx branch start "<task>" "<agent>"
|
|
381
|
+
then gx locks claim --branch "<agent-branch>" <file...> -> gx branch finish
|
|
382
|
+
5) Integrate: gx merge --branch <agent-a> --branch <agent-b>
|
|
383
|
+
6) Finish: gx finish --all
|
|
384
|
+
7) Cleanup: gx cleanup
|
|
385
|
+
8) OpenSpec: /opsx:propose -> /opsx:apply -> /opsx:archive
|
|
386
|
+
9) Optional: gx protect add release staging
|
|
387
|
+
10) Optional: gx sync --check && gx sync
|
|
388
|
+
11) Review bot: install https://github.com/apps/cr-gpt + set OPENAI_API_KEY
|
|
389
|
+
12) Fork sync: install https://github.com/apps/pull + cp .github/pull.yml.example .github/pull.yml
|
|
390
|
+
`;
|
|
391
|
+
|
|
392
|
+
const AI_SETUP_COMMANDS = `${GLOBAL_INSTALL_COMMAND}
|
|
393
|
+
gh --version
|
|
394
|
+
gx setup
|
|
395
|
+
gx doctor
|
|
396
|
+
gx branch start "<task>" "<agent>"
|
|
397
|
+
gx locks claim --branch "<agent-branch>" <file...>
|
|
398
|
+
gx merge --branch "<agent-a>" --branch "<agent-b>"
|
|
399
|
+
gx finish --all
|
|
400
|
+
gx cleanup
|
|
401
|
+
gx protect add release staging
|
|
402
|
+
gx sync --check && gx sync
|
|
403
|
+
`;
|
|
404
|
+
|
|
405
|
+
const SCORECARD_RISK_BY_CHECK = {
|
|
406
|
+
'Dangerous-Workflow': 'Critical',
|
|
407
|
+
'Code-Review': 'High',
|
|
408
|
+
Maintained: 'High',
|
|
409
|
+
'Binary-Artifacts': 'High',
|
|
410
|
+
'Dependency-Update-Tool': 'High',
|
|
411
|
+
'Token-Permissions': 'High',
|
|
412
|
+
Vulnerabilities: 'High',
|
|
413
|
+
'Branch-Protection': 'High',
|
|
414
|
+
Fuzzing: 'Medium',
|
|
415
|
+
'Pinned-Dependencies': 'Medium',
|
|
416
|
+
SAST: 'Medium',
|
|
417
|
+
'Security-Policy': 'Medium',
|
|
418
|
+
'CII-Best-Practices': 'Low',
|
|
419
|
+
Contributors: 'Low',
|
|
420
|
+
License: 'Low',
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
module.exports = {
|
|
424
|
+
fs,
|
|
425
|
+
os,
|
|
426
|
+
path,
|
|
427
|
+
cp,
|
|
428
|
+
PACKAGE_ROOT,
|
|
429
|
+
CLI_ENTRY_PATH,
|
|
430
|
+
packageJsonPath,
|
|
431
|
+
packageJson,
|
|
432
|
+
TOOL_NAME,
|
|
433
|
+
SHORT_TOOL_NAME,
|
|
434
|
+
LEGACY_NAMES,
|
|
435
|
+
GLOBAL_INSTALL_COMMAND,
|
|
436
|
+
OPENSPEC_PACKAGE,
|
|
437
|
+
OMC_PACKAGE,
|
|
438
|
+
OMC_REPO_URL,
|
|
439
|
+
CAVEMEM_PACKAGE,
|
|
440
|
+
NPX_BIN,
|
|
441
|
+
GUARDEX_HOME_DIR,
|
|
442
|
+
GLOBAL_TOOLCHAIN_SERVICES,
|
|
443
|
+
GLOBAL_TOOLCHAIN_PACKAGES,
|
|
444
|
+
OPTIONAL_LOCAL_COMPANION_TOOLS,
|
|
445
|
+
GH_BIN,
|
|
446
|
+
REQUIRED_SYSTEM_TOOLS,
|
|
447
|
+
MAINTAINER_RELEASE_REPO,
|
|
448
|
+
NPM_BIN,
|
|
449
|
+
OPENSPEC_BIN,
|
|
450
|
+
SCORECARD_BIN,
|
|
451
|
+
GIT_PROTECTED_BRANCHES_KEY,
|
|
452
|
+
GIT_BASE_BRANCH_KEY,
|
|
453
|
+
GIT_SYNC_STRATEGY_KEY,
|
|
454
|
+
GUARDEX_REPO_TOGGLE_ENV,
|
|
455
|
+
DEFAULT_PROTECTED_BRANCHES,
|
|
456
|
+
DEFAULT_BASE_BRANCH,
|
|
457
|
+
DEFAULT_SYNC_STRATEGY,
|
|
458
|
+
DEFAULT_SHADOW_CLEANUP_IDLE_MINUTES,
|
|
459
|
+
COMPOSE_HINT_FILES,
|
|
460
|
+
TEMPLATE_ROOT,
|
|
461
|
+
HOOK_NAMES,
|
|
462
|
+
toDestinationPath,
|
|
463
|
+
TEMPLATE_FILES,
|
|
464
|
+
LEGACY_WORKFLOW_SHIM_SPECS,
|
|
465
|
+
LEGACY_WORKFLOW_SHIMS,
|
|
466
|
+
MANAGED_TEMPLATE_DESTINATIONS,
|
|
467
|
+
MANAGED_TEMPLATE_SCRIPT_FILES,
|
|
468
|
+
LEGACY_MANAGED_REPO_FILES,
|
|
469
|
+
REQUIRED_MANAGED_REPO_FILES,
|
|
470
|
+
LEGACY_MANAGED_PACKAGE_SCRIPTS,
|
|
471
|
+
PACKAGE_SCRIPT_ASSETS,
|
|
472
|
+
USER_LEVEL_SKILL_ASSETS,
|
|
473
|
+
EXECUTABLE_RELATIVE_PATHS,
|
|
474
|
+
CRITICAL_GUARDRAIL_PATHS,
|
|
475
|
+
LOCK_FILE_RELATIVE,
|
|
476
|
+
AGENTS_BOTS_STATE_RELATIVE,
|
|
477
|
+
AGENTS_MARKER_START,
|
|
478
|
+
AGENTS_MARKER_END,
|
|
479
|
+
GITIGNORE_MARKER_START,
|
|
480
|
+
GITIGNORE_MARKER_END,
|
|
481
|
+
CODEX_WORKTREE_RELATIVE_DIR,
|
|
482
|
+
CLAUDE_WORKTREE_RELATIVE_DIR,
|
|
483
|
+
AGENT_WORKTREE_RELATIVE_DIRS,
|
|
484
|
+
MANAGED_GITIGNORE_PATHS,
|
|
485
|
+
REPO_SCAFFOLD_DIRECTORIES,
|
|
486
|
+
OMX_SCAFFOLD_DIRECTORIES,
|
|
487
|
+
OMX_SCAFFOLD_FILES,
|
|
488
|
+
TARGETED_FORCEABLE_MANAGED_PATHS,
|
|
489
|
+
COMMAND_TYPO_ALIASES,
|
|
490
|
+
SUGGESTIBLE_COMMANDS,
|
|
491
|
+
CLI_COMMAND_DESCRIPTIONS,
|
|
492
|
+
DEPRECATED_COMMAND_ALIASES,
|
|
493
|
+
AGENT_BOT_DESCRIPTIONS,
|
|
494
|
+
DOCTOR_AUTO_FINISH_DETAIL_LIMIT,
|
|
495
|
+
DOCTOR_AUTO_FINISH_BRANCH_LABEL_MAX,
|
|
496
|
+
DOCTOR_AUTO_FINISH_MESSAGE_MAX,
|
|
497
|
+
envFlagIsTruthy,
|
|
498
|
+
isClaudeCodeSession,
|
|
499
|
+
defaultAgentWorktreeRelativeDir,
|
|
500
|
+
AI_SETUP_PROMPT,
|
|
501
|
+
AI_SETUP_COMMANDS,
|
|
502
|
+
SCORECARD_RISK_BY_CHECK,
|
|
503
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const {
|
|
2
|
+
fs,
|
|
3
|
+
path,
|
|
4
|
+
CLI_ENTRY_PATH,
|
|
5
|
+
PACKAGE_SCRIPT_ASSETS,
|
|
6
|
+
} = require('../context');
|
|
7
|
+
|
|
8
|
+
function requireValue(rawArgs, index, flagName) {
|
|
9
|
+
const value = rawArgs[index + 1];
|
|
10
|
+
if (!value || value.startsWith('-')) {
|
|
11
|
+
throw new Error(`${flagName} requires a value`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function run(cmd, args, options = {}) {
|
|
17
|
+
return require('node:child_process').spawnSync(cmd, args, {
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
stdio: options.stdio || 'pipe',
|
|
20
|
+
cwd: options.cwd,
|
|
21
|
+
env: options.env ? { ...process.env, ...options.env } : process.env,
|
|
22
|
+
timeout: options.timeout,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function extractTargetedArgs(rawArgs, defaultTarget = process.cwd()) {
|
|
27
|
+
const passthrough = [];
|
|
28
|
+
let target = defaultTarget;
|
|
29
|
+
|
|
30
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
31
|
+
const arg = rawArgs[index];
|
|
32
|
+
if (arg === '--target' || arg === '-t') {
|
|
33
|
+
target = requireValue(rawArgs, index, '--target');
|
|
34
|
+
index += 1;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
passthrough.push(arg);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { target, passthrough };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function packageAssetEnv(extraEnv = {}) {
|
|
44
|
+
return {
|
|
45
|
+
GUARDEX_CLI_ENTRY: CLI_ENTRY_PATH,
|
|
46
|
+
GUARDEX_NODE_BIN: process.execPath,
|
|
47
|
+
...extraEnv,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function packageAssetPath(assetKey) {
|
|
52
|
+
const assetPath = PACKAGE_SCRIPT_ASSETS[assetKey];
|
|
53
|
+
if (!assetPath) {
|
|
54
|
+
throw new Error(`Unknown package asset: ${assetKey}`);
|
|
55
|
+
}
|
|
56
|
+
if (!fs.existsSync(assetPath)) {
|
|
57
|
+
throw new Error(`Missing package asset: ${assetPath}`);
|
|
58
|
+
}
|
|
59
|
+
return assetPath;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function runPackageAsset(assetKey, rawArgs, options = {}) {
|
|
63
|
+
const assetPath = packageAssetPath(assetKey);
|
|
64
|
+
let cmd = 'bash';
|
|
65
|
+
if (assetPath.endsWith('.py')) {
|
|
66
|
+
cmd = 'python3';
|
|
67
|
+
} else if (assetPath.endsWith('.js')) {
|
|
68
|
+
cmd = process.execPath;
|
|
69
|
+
}
|
|
70
|
+
return run(cmd, [assetPath, ...rawArgs], {
|
|
71
|
+
cwd: options.cwd || process.cwd(),
|
|
72
|
+
stdio: options.stdio || 'pipe',
|
|
73
|
+
timeout: options.timeout,
|
|
74
|
+
env: packageAssetEnv(options.env),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function repoLocalLegacyScriptPath(repoRoot, relativePath) {
|
|
79
|
+
const assetPath = path.join(repoRoot, relativePath);
|
|
80
|
+
return fs.existsSync(assetPath) ? assetPath : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function runReviewBotCommand(repoRoot, rawArgs, options = {}) {
|
|
84
|
+
const legacyScript = repoLocalLegacyScriptPath(repoRoot, 'scripts/review-bot-watch.sh');
|
|
85
|
+
if (legacyScript) {
|
|
86
|
+
return run('bash', [legacyScript, ...rawArgs], {
|
|
87
|
+
cwd: repoRoot,
|
|
88
|
+
stdio: options.stdio || 'pipe',
|
|
89
|
+
timeout: options.timeout,
|
|
90
|
+
env: packageAssetEnv(options.env),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return runPackageAsset('reviewBot', rawArgs, {
|
|
94
|
+
...options,
|
|
95
|
+
cwd: repoRoot,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function invokePackageAsset(assetKey, rawArgs, options = {}) {
|
|
100
|
+
const result = runPackageAsset(assetKey, rawArgs, options);
|
|
101
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
102
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
103
|
+
if (result.status !== 0) {
|
|
104
|
+
throw new Error(`${assetKey} command failed with status ${result.status}`);
|
|
105
|
+
}
|
|
106
|
+
process.exitCode = 0;
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
run,
|
|
112
|
+
extractTargetedArgs,
|
|
113
|
+
packageAssetEnv,
|
|
114
|
+
packageAssetPath,
|
|
115
|
+
runPackageAsset,
|
|
116
|
+
repoLocalLegacyScriptPath,
|
|
117
|
+
runReviewBotCommand,
|
|
118
|
+
invokePackageAsset,
|
|
119
|
+
};
|