@imdeadpool/guardex 7.0.21 → 7.0.23
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 +39 -29
- package/package.json +5 -1
- package/src/cli/args.js +89 -0
- package/src/cli/main.js +645 -2873
- package/src/context.js +195 -31
- package/src/core/stdin.js +52 -0
- package/src/core/versions.js +33 -0
- package/src/doctor/index.js +1071 -0
- package/src/finish/index.js +456 -358
- package/src/git/index.js +604 -1
- package/src/hooks/index.js +64 -0
- package/src/output/index.js +72 -5
- package/src/report/session-severity.js +213 -0
- package/src/sandbox/index.js +301 -52
- package/src/scaffold/index.js +627 -0
- package/src/toolchain/index.js +559 -179
- package/templates/AGENTS.multiagent-safety.md +25 -0
- package/templates/scripts/agent-branch-finish.sh +86 -6
- package/templates/scripts/agent-session-state.js +62 -1
- package/templates/scripts/agent-worktree-prune.sh +15 -1
- package/templates/scripts/codex-agent.sh +38 -0
- package/templates/scripts/install-vscode-active-agents-extension.js +38 -11
- package/templates/scripts/openspec/init-plan-workspace.sh +34 -3
- package/templates/vscode/guardex-active-agents/README.md +9 -6
- package/templates/vscode/guardex-active-agents/extension.js +805 -77
- package/templates/vscode/guardex-active-agents/icon.png +0 -0
- package/templates/vscode/guardex-active-agents/package.json +15 -3
- package/templates/vscode/guardex-active-agents/session-schema.js +311 -4
package/src/context.js
CHANGED
|
@@ -128,8 +128,19 @@ const TEMPLATE_FILES = [
|
|
|
128
128
|
'vscode/guardex-active-agents/extension.js',
|
|
129
129
|
'vscode/guardex-active-agents/session-schema.js',
|
|
130
130
|
'vscode/guardex-active-agents/README.md',
|
|
131
|
+
'vscode/guardex-active-agents/icon.png',
|
|
131
132
|
];
|
|
132
133
|
|
|
134
|
+
const PACKAGE_ROOT_SOURCE_OVERRIDES = new Set([
|
|
135
|
+
'scripts/agent-session-state.js',
|
|
136
|
+
'scripts/install-vscode-active-agents-extension.js',
|
|
137
|
+
'vscode/guardex-active-agents/package.json',
|
|
138
|
+
'vscode/guardex-active-agents/extension.js',
|
|
139
|
+
'vscode/guardex-active-agents/session-schema.js',
|
|
140
|
+
'vscode/guardex-active-agents/README.md',
|
|
141
|
+
'vscode/guardex-active-agents/icon.png',
|
|
142
|
+
]);
|
|
143
|
+
|
|
133
144
|
const LEGACY_WORKFLOW_SHIM_SPECS = [
|
|
134
145
|
{ relativePath: 'scripts/agent-branch-start.sh', kind: 'shell', command: ['branch', 'start'] },
|
|
135
146
|
{ relativePath: 'scripts/agent-branch-finish.sh', kind: 'shell', command: ['branch', 'finish'] },
|
|
@@ -202,6 +213,7 @@ const PACKAGE_SCRIPT_ASSETS = {
|
|
|
202
213
|
branchMerge: path.join(TEMPLATE_ROOT, 'scripts', 'agent-branch-merge.sh'),
|
|
203
214
|
codexAgent: path.join(TEMPLATE_ROOT, 'scripts', 'codex-agent.sh'),
|
|
204
215
|
reviewBot: path.join(TEMPLATE_ROOT, 'scripts', 'review-bot-watch.sh'),
|
|
216
|
+
sessionState: path.join(TEMPLATE_ROOT, 'scripts', 'agent-session-state.js'),
|
|
205
217
|
worktreePrune: path.join(TEMPLATE_ROOT, 'scripts', 'agent-worktree-prune.sh'),
|
|
206
218
|
lockTool: path.join(TEMPLATE_ROOT, 'scripts', 'agent-file-locks.py'),
|
|
207
219
|
planInit: path.join(TEMPLATE_ROOT, 'scripts', 'openspec', 'init-plan-workspace.sh'),
|
|
@@ -348,8 +360,8 @@ const CLI_COMMAND_DESCRIPTIONS = [
|
|
|
348
360
|
['cleanup', 'Prune merged/stale agent branches and worktrees'],
|
|
349
361
|
['release', 'Create or update the current GitHub release with README-generated notes'],
|
|
350
362
|
['agents', 'Start/stop repo-scoped review + cleanup bots'],
|
|
351
|
-
['prompt', 'Print AI setup checklist (--exec, --snippet)'],
|
|
352
|
-
['report', 'Security/safety reports (e.g. OpenSSF scorecard)'],
|
|
363
|
+
['prompt', 'Print AI setup checklist or named slices (--exec, --part, --list-parts, --snippet)'],
|
|
364
|
+
['report', 'Security/safety reports (e.g. OpenSSF scorecard, session severity)'],
|
|
353
365
|
['help', 'Show this help output'],
|
|
354
366
|
['version', 'Print GitGuardex version'],
|
|
355
367
|
];
|
|
@@ -369,6 +381,12 @@ const AGENT_BOT_DESCRIPTIONS = [
|
|
|
369
381
|
const DOCTOR_AUTO_FINISH_DETAIL_LIMIT = 6;
|
|
370
382
|
const DOCTOR_AUTO_FINISH_BRANCH_LABEL_MAX = 72;
|
|
371
383
|
const DOCTOR_AUTO_FINISH_MESSAGE_MAX = 160;
|
|
384
|
+
const AI_SETUP_PART_ALIASES = new Map([
|
|
385
|
+
['task', 'task-loop'],
|
|
386
|
+
['loop', 'task-loop'],
|
|
387
|
+
['reviewbot', 'review-bot'],
|
|
388
|
+
['forksync', 'fork-sync'],
|
|
389
|
+
]);
|
|
372
390
|
|
|
373
391
|
function envFlagIsTruthy(raw) {
|
|
374
392
|
const lowered = String(raw || '').trim().toLowerCase();
|
|
@@ -383,35 +401,177 @@ function defaultAgentWorktreeRelativeDir(env = process.env) {
|
|
|
383
401
|
return isClaudeCodeSession(env) ? CLAUDE_WORKTREE_RELATIVE_DIR : CODEX_WORKTREE_RELATIVE_DIR;
|
|
384
402
|
}
|
|
385
403
|
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
gx
|
|
410
|
-
gx
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
404
|
+
const AI_SETUP_PARTS = [
|
|
405
|
+
{
|
|
406
|
+
name: 'install',
|
|
407
|
+
label: 'Install',
|
|
408
|
+
promptLines: [`${GLOBAL_INSTALL_COMMAND} && gh --version`],
|
|
409
|
+
execLines: [GLOBAL_INSTALL_COMMAND, 'gh --version'],
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: 'bootstrap',
|
|
413
|
+
label: 'Bootstrap',
|
|
414
|
+
promptLines: ['gx setup'],
|
|
415
|
+
execLines: ['gx setup'],
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: 'repair',
|
|
419
|
+
label: 'Repair',
|
|
420
|
+
promptLines: ['gx doctor'],
|
|
421
|
+
execLines: ['gx doctor'],
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'task-loop',
|
|
425
|
+
label: 'Task loop',
|
|
426
|
+
promptLines: [
|
|
427
|
+
'gx branch start "<task>" "<agent>"',
|
|
428
|
+
'then gx locks claim --branch "<agent-branch>" <file...> -> inspect once -> patch once -> verify once -> gx branch finish',
|
|
429
|
+
'batch discovery, git/PR, and CI by phase; avoid repeated peeks or stdin loops',
|
|
430
|
+
],
|
|
431
|
+
execLines: [
|
|
432
|
+
'gx branch start "<task>" "<agent>"',
|
|
433
|
+
'gx locks claim --branch "<agent-branch>" <file...>',
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: 'integrate',
|
|
438
|
+
label: 'Integrate',
|
|
439
|
+
promptLines: ['gx merge --branch <agent-a> --branch <agent-b>'],
|
|
440
|
+
execLines: ['gx merge --branch <agent-a> --branch <agent-b>'],
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
name: 'finish',
|
|
444
|
+
label: 'Finish',
|
|
445
|
+
promptLines: ['gx finish --all'],
|
|
446
|
+
execLines: ['gx finish --all'],
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
name: 'cleanup',
|
|
450
|
+
label: 'Cleanup',
|
|
451
|
+
promptLines: ['gx cleanup'],
|
|
452
|
+
execLines: ['gx cleanup'],
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
name: 'openspec',
|
|
456
|
+
label: 'OpenSpec',
|
|
457
|
+
promptLines: ['/opsx:propose -> /opsx:apply -> /opsx:archive'],
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
name: 'protect',
|
|
461
|
+
label: 'Protect',
|
|
462
|
+
promptLines: ['gx protect add release staging'],
|
|
463
|
+
execLines: ['gx protect add release staging'],
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: 'sync',
|
|
467
|
+
label: 'Sync',
|
|
468
|
+
promptLines: ['gx sync --check && gx sync'],
|
|
469
|
+
execLines: ['gx sync --check && gx sync'],
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: 'review-bot',
|
|
473
|
+
label: 'Review bot',
|
|
474
|
+
promptLines: ['install https://github.com/apps/cr-gpt + set OPENAI_API_KEY'],
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: 'fork-sync',
|
|
478
|
+
label: 'Fork sync',
|
|
479
|
+
promptLines: ['install https://github.com/apps/pull + cp .github/pull.yml.example .github/pull.yml'],
|
|
480
|
+
},
|
|
481
|
+
];
|
|
482
|
+
const AI_SETUP_PARTS_BY_NAME = new Map(AI_SETUP_PARTS.map((part) => [part.name, part]));
|
|
483
|
+
const AI_SETUP_EXEC_PART_NAMES = AI_SETUP_PARTS
|
|
484
|
+
.filter((part) => Array.isArray(part.execLines))
|
|
485
|
+
.map((part) => part.name);
|
|
486
|
+
|
|
487
|
+
function normalizeAiSetupPartName(rawName) {
|
|
488
|
+
const normalized = String(rawName || '')
|
|
489
|
+
.trim()
|
|
490
|
+
.toLowerCase()
|
|
491
|
+
.replace(/_/g, '-');
|
|
492
|
+
return AI_SETUP_PART_ALIASES.get(normalized) || normalized;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function listAiSetupPartNames(options = {}) {
|
|
496
|
+
if (!options.execOnly) return AI_SETUP_PARTS.map((part) => part.name);
|
|
497
|
+
return AI_SETUP_EXEC_PART_NAMES.slice();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function parseAiSetupPartNames(rawValue) {
|
|
501
|
+
return String(rawValue || '')
|
|
502
|
+
.split(',')
|
|
503
|
+
.map((entry) => normalizeAiSetupPartName(entry))
|
|
504
|
+
.filter(Boolean);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function resolveAiSetupParts(rawPartNames, options = {}) {
|
|
508
|
+
const exec = Boolean(options.exec);
|
|
509
|
+
const requestedPartNames = Array.isArray(rawPartNames) ? rawPartNames : [];
|
|
510
|
+
const availablePartNames = listAiSetupPartNames();
|
|
511
|
+
const execCapablePartNames = listAiSetupPartNames({ execOnly: true });
|
|
512
|
+
const seen = new Set();
|
|
513
|
+
const resolved = [];
|
|
514
|
+
|
|
515
|
+
for (const rawName of requestedPartNames) {
|
|
516
|
+
const name = normalizeAiSetupPartName(rawName);
|
|
517
|
+
const part = AI_SETUP_PARTS_BY_NAME.get(name);
|
|
518
|
+
if (!part) {
|
|
519
|
+
throw new Error(
|
|
520
|
+
`Unknown prompt part: ${rawName}. Available parts: ${availablePartNames.join(', ')}`,
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (exec && !Array.isArray(part.execLines)) {
|
|
524
|
+
throw new Error(
|
|
525
|
+
`Prompt part '${name}' is not available with --exec. ` +
|
|
526
|
+
`Exec-capable parts: ${execCapablePartNames.join(', ')}`,
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
if (seen.has(name)) continue;
|
|
530
|
+
seen.add(name);
|
|
531
|
+
resolved.push(part);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return resolved;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function renderFullAiSetupPrompt() {
|
|
538
|
+
const lines = ['GitGuardex (gx) setup checklist for Codex/Claude in this repo.', ''];
|
|
539
|
+
const indentWidth = 18;
|
|
540
|
+
|
|
541
|
+
AI_SETUP_PARTS.forEach((part, index) => {
|
|
542
|
+
const [lead, ...tail] = part.promptLines;
|
|
543
|
+
const prefix = `${index + 1}) ${part.label}:`;
|
|
544
|
+
lines.push(`${prefix.padEnd(indentWidth)}${lead}`);
|
|
545
|
+
tail.forEach((line) => lines.push(`${' '.repeat(indentWidth)}${line}`));
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
return `${lines.join('\n')}\n`;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function renderPartialAiSetupPrompt(parts) {
|
|
552
|
+
return `${parts
|
|
553
|
+
.map((part) => `${part.label}:\n${part.promptLines.join('\n')}`)
|
|
554
|
+
.join('\n\n')}\n`;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function renderAiSetupCommands(parts) {
|
|
558
|
+
return `${parts.flatMap((part) => part.execLines).join('\n')}\n`;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function renderAiSetupPrompt(options = {}) {
|
|
562
|
+
const exec = Boolean(options.exec);
|
|
563
|
+
const requestedPartNames = Array.isArray(options.parts) ? options.parts : [];
|
|
564
|
+
if (requestedPartNames.length === 0) {
|
|
565
|
+
return exec
|
|
566
|
+
? renderAiSetupCommands(resolveAiSetupParts(AI_SETUP_EXEC_PART_NAMES, { exec: true }))
|
|
567
|
+
: renderFullAiSetupPrompt();
|
|
568
|
+
}
|
|
569
|
+
const parts = resolveAiSetupParts(requestedPartNames, { exec });
|
|
570
|
+
return exec ? renderAiSetupCommands(parts) : renderPartialAiSetupPrompt(parts);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const AI_SETUP_PROMPT = renderAiSetupPrompt();
|
|
574
|
+
const AI_SETUP_COMMANDS = renderAiSetupPrompt({ exec: true });
|
|
415
575
|
|
|
416
576
|
const SCORECARD_RISK_BY_CHECK = {
|
|
417
577
|
'Dangerous-Workflow': 'Critical',
|
|
@@ -472,6 +632,7 @@ module.exports = {
|
|
|
472
632
|
HOOK_NAMES,
|
|
473
633
|
toDestinationPath,
|
|
474
634
|
TEMPLATE_FILES,
|
|
635
|
+
PACKAGE_ROOT_SOURCE_OVERRIDES,
|
|
475
636
|
LEGACY_WORKFLOW_SHIM_SPECS,
|
|
476
637
|
LEGACY_WORKFLOW_SHIMS,
|
|
477
638
|
MANAGED_TEMPLATE_DESTINATIONS,
|
|
@@ -511,6 +672,9 @@ module.exports = {
|
|
|
511
672
|
envFlagIsTruthy,
|
|
512
673
|
isClaudeCodeSession,
|
|
513
674
|
defaultAgentWorktreeRelativeDir,
|
|
675
|
+
listAiSetupPartNames,
|
|
676
|
+
parseAiSetupPartNames,
|
|
677
|
+
renderAiSetupPrompt,
|
|
514
678
|
AI_SETUP_PROMPT,
|
|
515
679
|
AI_SETUP_COMMANDS,
|
|
516
680
|
SCORECARD_RISK_BY_CHECK,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
2
|
+
const { StringDecoder } = require('node:string_decoder');
|
|
3
|
+
|
|
4
|
+
const stdinWaitArray = new Int32Array(new SharedArrayBuffer(4));
|
|
5
|
+
|
|
6
|
+
function sleepSyncMs(milliseconds) {
|
|
7
|
+
Atomics.wait(stdinWaitArray, 0, 0, milliseconds);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function readSingleLineFromStdin(options = {}) {
|
|
11
|
+
const fsModule = options.fsModule || fs;
|
|
12
|
+
const input = options.input || process.stdin;
|
|
13
|
+
const sleepSync = options.sleepSync || sleepSyncMs;
|
|
14
|
+
const retryDelayMs = options.retryDelayMs == null ? 15 : options.retryDelayMs;
|
|
15
|
+
const buffer = Buffer.alloc(1);
|
|
16
|
+
const decoder = new StringDecoder('utf8');
|
|
17
|
+
let text = '';
|
|
18
|
+
|
|
19
|
+
while (true) {
|
|
20
|
+
let bytesRead = 0;
|
|
21
|
+
try {
|
|
22
|
+
bytesRead = fsModule.readSync(input.fd, buffer, 0, 1);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error && ['EAGAIN', 'EWOULDBLOCK', 'EINTR'].includes(error.code)) {
|
|
25
|
+
sleepSync(retryDelayMs);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
return text + decoder.end();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (bytesRead === 0) {
|
|
32
|
+
if (input.isTTY) {
|
|
33
|
+
sleepSync(retryDelayMs);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
return text + decoder.end();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const char = decoder.write(buffer.subarray(0, bytesRead));
|
|
40
|
+
if (!char) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (char === '\n' || char === '\r') {
|
|
44
|
+
return text;
|
|
45
|
+
}
|
|
46
|
+
text += char;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
readSingleLineFromStdin,
|
|
52
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const semver = require('semver');
|
|
2
|
+
|
|
3
|
+
function parseVersionString(version) {
|
|
4
|
+
const trimmed = String(version || '').trim();
|
|
5
|
+
if (!trimmed) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
return semver.valid(trimmed) || null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function compareParsedVersions(left, right) {
|
|
12
|
+
if (!left || !right) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
return semver.compare(left, right);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isNewerVersion(latest, current) {
|
|
19
|
+
const latestParts = parseVersionString(latest);
|
|
20
|
+
const currentParts = parseVersionString(current);
|
|
21
|
+
|
|
22
|
+
if (!latestParts || !currentParts) {
|
|
23
|
+
return String(latest || '').trim() !== String(current || '').trim();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return semver.gt(latestParts, currentParts);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
parseVersionString,
|
|
31
|
+
compareParsedVersions,
|
|
32
|
+
isNewerVersion,
|
|
33
|
+
};
|