@imdeadpool/guardex 7.0.23 → 7.0.24
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
CHANGED
|
@@ -672,6 +672,11 @@ npm pack --dry-run
|
|
|
672
672
|
<details>
|
|
673
673
|
<summary><strong>v7.x</strong></summary>
|
|
674
674
|
|
|
675
|
+
### v7.0.24
|
|
676
|
+
- Bumped `@imdeadpool/guardex` from `7.0.23` to `7.0.24` so GitHub Releases and the npm publish retry can advance together after `v7.0.23` landed on GitHub but not on npm.
|
|
677
|
+
- Release verification no longer loses its base ref on tag-triggered runs, so the publish workflow keeps the history it needs before packing and publish checks.
|
|
678
|
+
- Keep the release scoped to version and release automation metadata only; the packaged Guardex CLI payload stays aligned with the already-verified `main` branch contents.
|
|
679
|
+
|
|
675
680
|
### v7.0.23
|
|
676
681
|
- Bumped `@imdeadpool/guardex` from `7.0.22` to `7.0.23` so GitHub release and npm can advance together after `7.0.22` reached npm without a matching published GitHub release.
|
|
677
682
|
- Active Agents stays easier to scan and more truthful: the package repo remains the canonical source, inspect/install paths stay loadable across VS Code churn, and session rows group under worktrees with clearer merged-cleanup truth.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/guardex",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.24",
|
|
4
4
|
"description": "Guardian T-Rex for your multi-agent repo. Isolated worktrees, file locks, and PR-only merges stop parallel Codex & Claude agents from overwriting each other's work. Auto-wires Oh My Codex, Oh My Claude, OpenSpec, and Caveman.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"preferGlobal": true,
|
package/src/cli/main.js
CHANGED
|
@@ -2431,15 +2431,20 @@ function report(rawArgs) {
|
|
|
2431
2431
|
const options = parseReportArgs(rawArgs);
|
|
2432
2432
|
const subcommand = options.subcommand || 'help';
|
|
2433
2433
|
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
|
|
2434
|
+
const sessionSeverityHelpDetails = sessionSeverityReport.renderSessionSeverityHelpDetails()
|
|
2435
|
+
.split('\n')
|
|
2436
|
+
.map((line) => ` ${line}`)
|
|
2437
|
+
.join('\n');
|
|
2434
2438
|
console.log(
|
|
2435
2439
|
`${TOOL_NAME} report commands:\n` +
|
|
2436
2440
|
` ${TOOL_NAME} report scorecard [--target <path>] [--repo github.com/<owner>/<repo>] [--scorecard-json <file>] [--output-dir <path>] [--date YYYY-MM-DD] [--dry-run] [--json]\n` +
|
|
2437
|
-
` ${TOOL_NAME}
|
|
2441
|
+
` ${sessionSeverityReport.renderSessionSeverityCommand(TOOL_NAME)}\n` +
|
|
2442
|
+
`${sessionSeverityHelpDetails}\n` +
|
|
2438
2443
|
`\n` +
|
|
2439
2444
|
`Examples:\n` +
|
|
2440
2445
|
` ${TOOL_NAME} report scorecard --repo github.com/recodeecom/multiagent-safety\n` +
|
|
2441
2446
|
` ${TOOL_NAME} report scorecard --scorecard-json ./scorecard.json --date 2026-04-10\n` +
|
|
2442
|
-
` ${TOOL_NAME}
|
|
2447
|
+
` ${sessionSeverityReport.renderSessionSeverityExample(TOOL_NAME)}`,
|
|
2443
2448
|
);
|
|
2444
2449
|
process.exitCode = 0;
|
|
2445
2450
|
return;
|
|
@@ -4,7 +4,8 @@ const TASK_SIZE_UPPER_BOUNDS = {
|
|
|
4
4
|
'large-change': 8_000_000,
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
const TASK_SIZE_VALUES =
|
|
7
|
+
const TASK_SIZE_VALUES = Object.keys(TASK_SIZE_UPPER_BOUNDS);
|
|
8
|
+
const TASK_SIZE_SET = new Set(TASK_SIZE_VALUES);
|
|
8
9
|
const FRAGMENTATION_PRESET_SCORES = {
|
|
9
10
|
clean: 0,
|
|
10
11
|
'few-extra-checks': 5,
|
|
@@ -32,6 +33,50 @@ const DRIVER_LABELS = {
|
|
|
32
33
|
finishPath: 'finish-path discipline',
|
|
33
34
|
postProof: 'post-proof drift',
|
|
34
35
|
};
|
|
36
|
+
const LABEL_BANDS = [
|
|
37
|
+
{ max: 15, label: 'Healthy' },
|
|
38
|
+
{ max: 30, label: 'Mildly fragmented' },
|
|
39
|
+
{ max: 50, label: 'Inefficient' },
|
|
40
|
+
{ max: 75, label: 'Runaway' },
|
|
41
|
+
{ max: 100, label: 'Catastrophic' },
|
|
42
|
+
];
|
|
43
|
+
const SESSION_SEVERITY_SUBCOMMAND = 'session-severity';
|
|
44
|
+
const SESSION_SEVERITY_USAGE_ARGS = [
|
|
45
|
+
`--task-size <${TASK_SIZE_VALUES.join('|')}>`,
|
|
46
|
+
'--tokens <count>',
|
|
47
|
+
'--exec-count <count>',
|
|
48
|
+
'--write-stdin-count <count>',
|
|
49
|
+
'--completion-before-tail <yes|no>',
|
|
50
|
+
'[--expected-bound <count>]',
|
|
51
|
+
`[--fragmentation <${Object.keys(FRAGMENTATION_PRESET_SCORES).join('|')}|0-25>]`,
|
|
52
|
+
`[--finish-path <${Object.keys(FINISH_PATH_PRESET_SCORES).join('|')}|0-15>]`,
|
|
53
|
+
`[--post-proof <${Object.keys(POST_PROOF_PRESET_SCORES).join('|')}|0-15>]`,
|
|
54
|
+
'[--json]',
|
|
55
|
+
].join(' ');
|
|
56
|
+
const SESSION_SEVERITY_COMMAND_TAIL = `${SESSION_SEVERITY_SUBCOMMAND} ${SESSION_SEVERITY_USAGE_ARGS}`;
|
|
57
|
+
const SESSION_SEVERITY_EXAMPLE_ARGS = [
|
|
58
|
+
'--task-size',
|
|
59
|
+
'narrow-patch',
|
|
60
|
+
'--tokens',
|
|
61
|
+
'3850000',
|
|
62
|
+
'--exec-count',
|
|
63
|
+
'18',
|
|
64
|
+
'--write-stdin-count',
|
|
65
|
+
'6',
|
|
66
|
+
'--completion-before-tail',
|
|
67
|
+
'yes',
|
|
68
|
+
'--fragmentation',
|
|
69
|
+
'14',
|
|
70
|
+
'--finish-path',
|
|
71
|
+
'6',
|
|
72
|
+
'--post-proof',
|
|
73
|
+
'4',
|
|
74
|
+
];
|
|
75
|
+
const SESSION_SEVERITY_EXAMPLE_TAIL = `${SESSION_SEVERITY_SUBCOMMAND} ${SESSION_SEVERITY_EXAMPLE_ARGS.join(' ')}`;
|
|
76
|
+
|
|
77
|
+
function formatInteger(value) {
|
|
78
|
+
return Number(value).toLocaleString('en-US');
|
|
79
|
+
}
|
|
35
80
|
|
|
36
81
|
function parseRequiredPositiveInteger(name, rawValue, { allowZero = true } = {}) {
|
|
37
82
|
const parsed = Number.parseInt(String(rawValue || ''), 10);
|
|
@@ -58,8 +103,8 @@ function clampScore(value, min, max) {
|
|
|
58
103
|
|
|
59
104
|
function parseTaskSize(rawTaskSize) {
|
|
60
105
|
const normalized = String(rawTaskSize || '').trim();
|
|
61
|
-
if (!
|
|
62
|
-
throw new Error(`--task-size must be one of: ${
|
|
106
|
+
if (!TASK_SIZE_SET.has(normalized)) {
|
|
107
|
+
throw new Error(`--task-size must be one of: ${TASK_SIZE_VALUES.join(', ')}`);
|
|
63
108
|
}
|
|
64
109
|
return normalized;
|
|
65
110
|
}
|
|
@@ -123,11 +168,7 @@ function scorePostProof(completionBeforeTail, override) {
|
|
|
123
168
|
}
|
|
124
169
|
|
|
125
170
|
function labelForTotal(total) {
|
|
126
|
-
|
|
127
|
-
if (total <= 30) return 'Mildly fragmented';
|
|
128
|
-
if (total <= 50) return 'Inefficient';
|
|
129
|
-
if (total <= 75) return 'Runaway';
|
|
130
|
-
return 'Catastrophic';
|
|
171
|
+
return LABEL_BANDS.find((band) => total <= band.max)?.label || LABEL_BANDS[LABEL_BANDS.length - 1].label;
|
|
131
172
|
}
|
|
132
173
|
|
|
133
174
|
function buildSessionSeverityReport(options) {
|
|
@@ -205,9 +246,37 @@ function renderSessionSeverityReport(report) {
|
|
|
205
246
|
].join('\n');
|
|
206
247
|
}
|
|
207
248
|
|
|
249
|
+
function renderSessionSeverityHelpDetails() {
|
|
250
|
+
const taskSizeDefaults = TASK_SIZE_VALUES
|
|
251
|
+
.map((taskSize) => `${taskSize}=${formatInteger(TASK_SIZE_UPPER_BOUNDS[taskSize])}`)
|
|
252
|
+
.join(', ');
|
|
253
|
+
const labelBands = LABEL_BANDS
|
|
254
|
+
.map((band, index) => {
|
|
255
|
+
const min = index === 0 ? 0 : LABEL_BANDS[index - 1].max + 1;
|
|
256
|
+
return `${band.label}=${min}-${band.max}`;
|
|
257
|
+
})
|
|
258
|
+
.join(', ');
|
|
259
|
+
return [`Task-size defaults: ${taskSizeDefaults}`, `Label bands: ${labelBands}`].join('\n');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function renderSessionSeverityCommand(toolName) {
|
|
263
|
+
return `${toolName} report ${SESSION_SEVERITY_COMMAND_TAIL}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function renderSessionSeverityExample(toolName) {
|
|
267
|
+
return `${toolName} report ${SESSION_SEVERITY_EXAMPLE_TAIL}`;
|
|
268
|
+
}
|
|
269
|
+
|
|
208
270
|
module.exports = {
|
|
271
|
+
LABEL_BANDS,
|
|
272
|
+
SESSION_SEVERITY_COMMAND_TAIL,
|
|
273
|
+
SESSION_SEVERITY_EXAMPLE_ARGS,
|
|
274
|
+
SESSION_SEVERITY_EXAMPLE_TAIL,
|
|
209
275
|
TASK_SIZE_UPPER_BOUNDS,
|
|
210
276
|
buildSessionSeverityReport,
|
|
211
277
|
renderSessionSeverityReport,
|
|
278
|
+
renderSessionSeverityHelpDetails,
|
|
279
|
+
renderSessionSeverityCommand,
|
|
280
|
+
renderSessionSeverityExample,
|
|
212
281
|
labelForTotal,
|
|
213
282
|
};
|
|
@@ -455,6 +455,44 @@ read_pr_state() {
|
|
|
455
455
|
return 0
|
|
456
456
|
}
|
|
457
457
|
|
|
458
|
+
read_merged_pr_for_head() {
|
|
459
|
+
local head_sha="${1:-}"
|
|
460
|
+
local state_line=""
|
|
461
|
+
local parsed_state=""
|
|
462
|
+
local parsed_merged_at=""
|
|
463
|
+
local parsed_url=""
|
|
464
|
+
|
|
465
|
+
if [[ -z "$head_sha" ]]; then
|
|
466
|
+
return 1
|
|
467
|
+
fi
|
|
468
|
+
|
|
469
|
+
state_line="$("$GH_BIN" pr list \
|
|
470
|
+
--state merged \
|
|
471
|
+
--head "$SOURCE_BRANCH" \
|
|
472
|
+
--base "$BASE_BRANCH" \
|
|
473
|
+
--json state,mergedAt,url,headRefOid \
|
|
474
|
+
--jq "map(select(.headRefOid == \"$head_sha\")) | sort_by(.mergedAt // \"\") | reverse | (.[0] // {}) | [(.state // \"\"), (.mergedAt // \"\"), (.url // \"\")] | join(\"\u001f\")" \
|
|
475
|
+
2>/dev/null || true)"
|
|
476
|
+
if [[ -z "$state_line" ]]; then
|
|
477
|
+
return 1
|
|
478
|
+
fi
|
|
479
|
+
|
|
480
|
+
IFS=$'\x1f' read -r parsed_state parsed_merged_at parsed_url <<< "$state_line"
|
|
481
|
+
if [[ -z "$parsed_state" && -z "$parsed_merged_at" && -z "$parsed_url" ]]; then
|
|
482
|
+
return 1
|
|
483
|
+
fi
|
|
484
|
+
if [[ "$parsed_state" != "MERGED" && -z "$parsed_merged_at" ]]; then
|
|
485
|
+
return 1
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
PR_STATE="$parsed_state"
|
|
489
|
+
PR_MERGED_AT="$parsed_merged_at"
|
|
490
|
+
if [[ -n "$parsed_url" ]]; then
|
|
491
|
+
pr_url="$parsed_url"
|
|
492
|
+
fi
|
|
493
|
+
return 0
|
|
494
|
+
}
|
|
495
|
+
|
|
458
496
|
wait_for_pr_merge() {
|
|
459
497
|
local deadline
|
|
460
498
|
deadline=$(( $(date +%s) + WAIT_TIMEOUT_SECONDS ))
|
|
@@ -509,11 +547,22 @@ wait_for_pr_merge() {
|
|
|
509
547
|
}
|
|
510
548
|
|
|
511
549
|
run_pr_flow() {
|
|
550
|
+
local source_head_sha=""
|
|
551
|
+
|
|
512
552
|
if ! command -v "$GH_BIN" >/dev/null 2>&1; then
|
|
513
553
|
echo "[agent-branch-finish] PR fallback requested but GitHub CLI not found: ${GH_BIN}" >&2
|
|
514
554
|
return 1
|
|
515
555
|
fi
|
|
516
556
|
|
|
557
|
+
source_head_sha="$(git -C "$repo_root" rev-parse "$SOURCE_BRANCH" 2>/dev/null || true)"
|
|
558
|
+
if read_merged_pr_for_head "$source_head_sha"; then
|
|
559
|
+
echo "[agent-branch-finish] Source branch head already landed in a merged PR; skipping new PR creation and continuing cleanup." >&2
|
|
560
|
+
if [[ -n "$pr_url" ]]; then
|
|
561
|
+
echo "[agent-branch-finish] Merged PR: ${pr_url}" >&2
|
|
562
|
+
fi
|
|
563
|
+
return 0
|
|
564
|
+
fi
|
|
565
|
+
|
|
517
566
|
git -C "$source_worktree" push -u origin "$SOURCE_BRANCH"
|
|
518
567
|
|
|
519
568
|
pr_title="$(git -C "$repo_root" log -1 --pretty=%s "$SOURCE_BRANCH" 2>/dev/null || true)"
|
|
@@ -155,6 +155,15 @@ env_flag_truthy() {
|
|
|
155
155
|
esac
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
maybe_fail_after_auto_transfer_stash() {
|
|
159
|
+
if env_flag_truthy "${GUARDEX_TEST_FAIL_AFTER_AUTO_TRANSFER_STASH:-}"; then
|
|
160
|
+
echo "[agent-branch-start] Simulated failure after capturing auto-transfer stash." >&2
|
|
161
|
+
return 1
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
return 0
|
|
165
|
+
}
|
|
166
|
+
|
|
158
167
|
default_worktree_root_rel() {
|
|
159
168
|
local raw_agent="$1"
|
|
160
169
|
local override="${GUARDEX_AGENT_TYPE:-}"
|
|
@@ -580,6 +589,27 @@ fi
|
|
|
580
589
|
auto_transfer_stash_ref=""
|
|
581
590
|
auto_transfer_message=""
|
|
582
591
|
auto_transfer_source_branch=""
|
|
592
|
+
auto_transfer_completed=0
|
|
593
|
+
|
|
594
|
+
restore_auto_transfer_stash_on_failure() {
|
|
595
|
+
local exit_code="${1:-0}"
|
|
596
|
+
if [[ "$exit_code" -eq 0 ]] || [[ -z "$auto_transfer_stash_ref" ]] || [[ "$auto_transfer_completed" -eq 1 ]]; then
|
|
597
|
+
return 0
|
|
598
|
+
fi
|
|
599
|
+
|
|
600
|
+
local transfer_label="${auto_transfer_source_branch:-$BASE_BRANCH}"
|
|
601
|
+
if git -C "$repo_root" stash apply "$auto_transfer_stash_ref" >/dev/null 2>&1; then
|
|
602
|
+
git -C "$repo_root" stash drop "$auto_transfer_stash_ref" >/dev/null 2>&1 || true
|
|
603
|
+
auto_transfer_stash_ref=""
|
|
604
|
+
echo "[agent-branch-start] Restored moved changes back to '${transfer_label}' after startup failure." >&2
|
|
605
|
+
else
|
|
606
|
+
echo "[agent-branch-start] Startup failed and auto-restore also failed." >&2
|
|
607
|
+
echo "[agent-branch-start] Changes are preserved in ${auto_transfer_stash_ref} on ${transfer_label}." >&2
|
|
608
|
+
fi
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
trap 'restore_auto_transfer_stash_on_failure "$?"' EXIT
|
|
612
|
+
|
|
583
613
|
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
584
614
|
protected_branches_raw="$(resolve_protected_branches "$repo_root")"
|
|
585
615
|
if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]] && is_protected_branch_name "$current_branch" "$protected_branches_raw"; then
|
|
@@ -593,6 +623,9 @@ if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]] && is_protected_bra
|
|
|
593
623
|
if [[ -n "$auto_transfer_stash_ref" ]]; then
|
|
594
624
|
auto_transfer_source_branch="$current_branch"
|
|
595
625
|
echo "[agent-branch-start] Detected local changes on protected branch '${current_branch}'. Moving them to '${branch_name}'..."
|
|
626
|
+
if ! maybe_fail_after_auto_transfer_stash; then
|
|
627
|
+
exit 1
|
|
628
|
+
fi
|
|
596
629
|
fi
|
|
597
630
|
fi
|
|
598
631
|
fi
|
|
@@ -610,7 +643,9 @@ git -C "$worktree_path" branch --unset-upstream "$branch_name" >/dev/null 2>&1 |
|
|
|
610
643
|
|
|
611
644
|
if [[ -n "$auto_transfer_stash_ref" ]]; then
|
|
612
645
|
if git -C "$worktree_path" stash apply "$auto_transfer_stash_ref" >/dev/null 2>&1; then
|
|
646
|
+
auto_transfer_completed=1
|
|
613
647
|
git -C "$repo_root" stash drop "$auto_transfer_stash_ref" >/dev/null 2>&1 || true
|
|
648
|
+
auto_transfer_stash_ref=""
|
|
614
649
|
transfer_label="${auto_transfer_source_branch:-$BASE_BRANCH}"
|
|
615
650
|
echo "[agent-branch-start] Moved local changes from '${transfer_label}' into '${branch_name}'."
|
|
616
651
|
else
|