@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
|
@@ -6,6 +6,7 @@ AGENT_NAME="${GUARDEX_AGENT_NAME:-agent}"
|
|
|
6
6
|
BASE_BRANCH="${GUARDEX_BASE_BRANCH:-}"
|
|
7
7
|
BASE_BRANCH_EXPLICIT=0
|
|
8
8
|
CODEX_BIN="${GUARDEX_CODEX_BIN:-codex}"
|
|
9
|
+
CODEX_APPROVAL_POLICY="${GUARDEX_CODEX_APPROVAL_POLICY-never}"
|
|
9
10
|
NODE_BIN="${GUARDEX_NODE_BIN:-node}"
|
|
10
11
|
CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
|
|
11
12
|
AUTO_FINISH_RAW="${GUARDEX_CODEX_AUTO_FINISH:-true}"
|
|
@@ -67,6 +68,29 @@ normalize_tier() {
|
|
|
67
68
|
esac
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
codex_args_include_approval_policy() {
|
|
72
|
+
local arg
|
|
73
|
+
for arg in "$@"; do
|
|
74
|
+
case "$arg" in
|
|
75
|
+
-a|--ask-for-approval|--approval-policy|--dangerously-bypass-approvals-and-sandbox)
|
|
76
|
+
return 0
|
|
77
|
+
;;
|
|
78
|
+
--ask-for-approval=*|--approval-policy=*)
|
|
79
|
+
return 0
|
|
80
|
+
;;
|
|
81
|
+
esac
|
|
82
|
+
done
|
|
83
|
+
return 1
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
build_codex_launch_args() {
|
|
87
|
+
CODEX_LAUNCH_ARGS=()
|
|
88
|
+
if [[ -n "$CODEX_APPROVAL_POLICY" ]] && ! codex_args_include_approval_policy "$@"; then
|
|
89
|
+
CODEX_LAUNCH_ARGS+=("-a" "$CODEX_APPROVAL_POLICY")
|
|
90
|
+
fi
|
|
91
|
+
CODEX_LAUNCH_ARGS+=("$@")
|
|
92
|
+
}
|
|
93
|
+
|
|
70
94
|
string_contains_any() {
|
|
71
95
|
local haystack="$1"
|
|
72
96
|
shift
|
|
@@ -300,7 +324,6 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
|
300
324
|
exit 1
|
|
301
325
|
fi
|
|
302
326
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
303
|
-
active_session_state_script="${repo_root}/scripts/agent-session-state.js"
|
|
304
327
|
|
|
305
328
|
guardex_env_helper="${repo_root}/scripts/guardex-env.sh"
|
|
306
329
|
if [[ -f "$guardex_env_helper" ]]; then
|
|
@@ -614,78 +637,6 @@ has_origin_remote() {
|
|
|
614
637
|
git -C "$repo_root" remote get-url origin >/dev/null 2>&1
|
|
615
638
|
}
|
|
616
639
|
|
|
617
|
-
run_active_session_state() {
|
|
618
|
-
local action="$1"
|
|
619
|
-
shift
|
|
620
|
-
|
|
621
|
-
if [[ ! -f "$active_session_state_script" ]]; then
|
|
622
|
-
return 0
|
|
623
|
-
fi
|
|
624
|
-
if ! command -v "$NODE_BIN" >/dev/null 2>&1; then
|
|
625
|
-
return 0
|
|
626
|
-
fi
|
|
627
|
-
|
|
628
|
-
"$NODE_BIN" "$active_session_state_script" "$action" "$@" >/dev/null 2>&1 || true
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
record_active_session_state() {
|
|
632
|
-
local wt="$1"
|
|
633
|
-
local branch="$2"
|
|
634
|
-
|
|
635
|
-
run_active_session_state \
|
|
636
|
-
start \
|
|
637
|
-
--repo "$repo_root" \
|
|
638
|
-
--branch "$branch" \
|
|
639
|
-
--task "$TASK_NAME" \
|
|
640
|
-
--agent "$AGENT_NAME" \
|
|
641
|
-
--worktree "$wt" \
|
|
642
|
-
--pid "$$" \
|
|
643
|
-
--cli "$CODEX_BIN" \
|
|
644
|
-
--task-mode "$TASK_MODE" \
|
|
645
|
-
--openspec-tier "$OPENSPEC_TIER" \
|
|
646
|
-
--routing-reason "$TASK_ROUTING_REASON"
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
clear_active_session_state() {
|
|
650
|
-
local branch="$1"
|
|
651
|
-
run_active_session_state stop --repo "$repo_root" --branch "$branch"
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
heartbeat_active_session_state() {
|
|
655
|
-
local branch="$1"
|
|
656
|
-
run_active_session_state heartbeat --repo "$repo_root" --branch "$branch" --state working
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
normalize_heartbeat_interval_seconds() {
|
|
660
|
-
local raw="${GUARDEX_ACTIVE_SESSION_HEARTBEAT_INTERVAL_SECONDS:-15}"
|
|
661
|
-
if [[ "$raw" =~ ^[0-9]+$ ]] && [[ "$raw" -ge 1 ]]; then
|
|
662
|
-
printf '%s' "$raw"
|
|
663
|
-
return 0
|
|
664
|
-
fi
|
|
665
|
-
printf '15'
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
start_active_session_heartbeat() {
|
|
669
|
-
local branch="$1"
|
|
670
|
-
local interval
|
|
671
|
-
interval="$(normalize_heartbeat_interval_seconds)"
|
|
672
|
-
(
|
|
673
|
-
while true; do
|
|
674
|
-
sleep "$interval" || break
|
|
675
|
-
heartbeat_active_session_state "$branch"
|
|
676
|
-
done
|
|
677
|
-
) &
|
|
678
|
-
active_session_heartbeat_pid="$!"
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
stop_active_session_heartbeat() {
|
|
682
|
-
if [[ -n "${active_session_heartbeat_pid:-}" ]]; then
|
|
683
|
-
kill "$active_session_heartbeat_pid" >/dev/null 2>&1 || true
|
|
684
|
-
wait "$active_session_heartbeat_pid" >/dev/null 2>&1 || true
|
|
685
|
-
active_session_heartbeat_pid=""
|
|
686
|
-
fi
|
|
687
|
-
}
|
|
688
|
-
|
|
689
640
|
origin_remote_supports_pr_finish() {
|
|
690
641
|
local origin_url
|
|
691
642
|
origin_url="$(git -C "$repo_root" remote get-url origin 2>/dev/null || true)"
|
|
@@ -735,9 +686,14 @@ print_takeover_prompt() {
|
|
|
735
686
|
|
|
736
687
|
finish_cmd="gx branch finish --branch \"${branch}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
|
|
737
688
|
|
|
738
|
-
echo "[codex-agent]
|
|
739
|
-
echo "
|
|
740
|
-
echo "
|
|
689
|
+
echo "[codex-agent] Resume this sandbox:"
|
|
690
|
+
echo " change: ${change_slug}"
|
|
691
|
+
echo " branch: ${branch}"
|
|
692
|
+
echo " worktree: ${wt}"
|
|
693
|
+
echo " spec: ${change_artifact}"
|
|
694
|
+
echo " routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
|
|
695
|
+
echo " rule: continue current state; do not create a new sandbox"
|
|
696
|
+
echo " finish: ${finish_cmd}"
|
|
741
697
|
}
|
|
742
698
|
|
|
743
699
|
sync_worktree_with_base() {
|
|
@@ -1034,7 +990,8 @@ run_finish_flow() {
|
|
|
1034
990
|
(
|
|
1035
991
|
cd "$wt"
|
|
1036
992
|
set +e
|
|
1037
|
-
|
|
993
|
+
build_codex_launch_args "$review_prompt"
|
|
994
|
+
"$CODEX_BIN" "${CODEX_LAUNCH_ARGS[@]}"
|
|
1038
995
|
review_exit="$?"
|
|
1039
996
|
set -e
|
|
1040
997
|
if [[ "$review_exit" -ne 0 ]]; then
|
|
@@ -1073,35 +1030,18 @@ fi
|
|
|
1073
1030
|
|
|
1074
1031
|
echo "[codex-agent] Task routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
|
|
1075
1032
|
|
|
1076
|
-
active_session_recorded=0
|
|
1077
|
-
active_session_heartbeat_pid=""
|
|
1078
|
-
cleanup_active_session_state_on_exit() {
|
|
1079
|
-
set +e
|
|
1080
|
-
if [[ "${active_session_recorded:-0}" -eq 1 && -n "${worktree_branch:-}" && "${worktree_branch:-}" != "HEAD" ]]; then
|
|
1081
|
-
stop_active_session_heartbeat
|
|
1082
|
-
clear_active_session_state "$worktree_branch"
|
|
1083
|
-
active_session_recorded=0
|
|
1084
|
-
fi
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
record_active_session_state "$worktree_path" "$worktree_branch"
|
|
1088
|
-
active_session_recorded=1
|
|
1089
|
-
start_active_session_heartbeat "$worktree_branch"
|
|
1090
|
-
trap cleanup_active_session_state_on_exit EXIT INT TERM
|
|
1091
|
-
|
|
1092
1033
|
echo "[codex-agent] Launching ${CODEX_BIN} in sandbox: $worktree_path"
|
|
1093
1034
|
cd "$worktree_path"
|
|
1094
1035
|
set +e
|
|
1036
|
+
build_codex_launch_args "$@"
|
|
1095
1037
|
GUARDEX_TASK_MODE="$TASK_MODE" \
|
|
1096
1038
|
GUARDEX_OPENSPEC_TIER="$OPENSPEC_TIER" \
|
|
1097
1039
|
GUARDEX_TASK_ROUTING_REASON="$TASK_ROUTING_REASON" \
|
|
1098
|
-
"$CODEX_BIN" "
|
|
1040
|
+
"$CODEX_BIN" "${CODEX_LAUNCH_ARGS[@]}"
|
|
1099
1041
|
codex_exit="$?"
|
|
1100
1042
|
set -e
|
|
1101
1043
|
|
|
1102
1044
|
cd "$repo_root"
|
|
1103
|
-
cleanup_active_session_state_on_exit
|
|
1104
|
-
trap - EXIT INT TERM
|
|
1105
1045
|
final_exit="$codex_exit"
|
|
1106
1046
|
auto_finish_completed=0
|
|
1107
1047
|
|
|
@@ -1161,7 +1101,11 @@ else
|
|
|
1161
1101
|
echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
|
|
1162
1102
|
else
|
|
1163
1103
|
print_takeover_prompt "$worktree_path" "$worktree_branch"
|
|
1164
|
-
|
|
1104
|
+
finish_base_branch="$(resolve_worktree_base_branch "$worktree_path")"
|
|
1105
|
+
if [[ -z "$finish_base_branch" ]]; then
|
|
1106
|
+
finish_base_branch="dev"
|
|
1107
|
+
fi
|
|
1108
|
+
echo "[codex-agent] If finished, merge with: gx branch finish --branch \"${worktree_branch}\" --base ${finish_base_branch} --via-pr --wait-for-merge --cleanup"
|
|
1165
1109
|
echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
|
|
1166
1110
|
fi
|
|
1167
1111
|
fi
|
|
@@ -84,6 +84,7 @@ if [[ ! -f "$PLAN_DIR/README.md" ]]; then
|
|
|
84
84
|
echo
|
|
85
85
|
echo "Each role folder contains OpenSpec-style artifacts:"
|
|
86
86
|
echo "- \`.openspec.yaml\`"
|
|
87
|
+
echo "- \`prompt.md\` (copy/paste role prompt)"
|
|
87
88
|
echo "- \`proposal.md\`"
|
|
88
89
|
echo "- \`tasks.md\` (Spec / Tests / Implementation / Checkpoints checklists)"
|
|
89
90
|
echo "- \`specs/<role>/spec.md\`"
|
|
@@ -108,6 +109,7 @@ Drive this plan from draft to execution-ready status with strict checkpoint disc
|
|
|
108
109
|
- \`openspec/plan/${PLAN_SLUG}/checkpoints.md\`
|
|
109
110
|
- \`openspec/plan/${PLAN_SLUG}/open-questions.md\`
|
|
110
111
|
- \`openspec/plan/${PLAN_SLUG}/planner/plan.md\`
|
|
112
|
+
- role \`prompt.md\` files for copy/paste helper startup
|
|
111
113
|
- role \`tasks.md\` files for planner/architect/critic/executor/writer/verifier
|
|
112
114
|
|
|
113
115
|
## Coordinator responsibilities
|
|
@@ -282,6 +284,7 @@ Role workspace for \`${role}\`.
|
|
|
282
284
|
|
|
283
285
|
Default artifacts:
|
|
284
286
|
- \`.openspec.yaml\`
|
|
287
|
+
- \`prompt.md\`
|
|
285
288
|
- \`proposal.md\`
|
|
286
289
|
- \`tasks.md\`
|
|
287
290
|
- \`specs/<role>/spec.md\`
|
|
@@ -302,12 +305,52 @@ plan: ${PLAN_SLUG}
|
|
|
302
305
|
role: ${role}
|
|
303
306
|
status: draft
|
|
304
307
|
artifacts:
|
|
308
|
+
prompt: prompt.md
|
|
305
309
|
proposal: proposal.md
|
|
306
310
|
tasks: tasks.md
|
|
307
311
|
spec: specs/${ROLE_SPEC_SLUG}/spec.md
|
|
308
312
|
ROLEYAMLEOF
|
|
309
313
|
fi
|
|
310
314
|
|
|
315
|
+
if [[ ! -f "$ROLE_DIR/prompt.md" ]]; then
|
|
316
|
+
cat > "$ROLE_DIR/prompt.md" <<ROLEPROMPTEOF
|
|
317
|
+
# ${role} Prompt
|
|
318
|
+
|
|
319
|
+
You are the \`${role}\` role for OpenSpec plan \`${PLAN_SLUG}\`.
|
|
320
|
+
|
|
321
|
+
## Objective
|
|
322
|
+
|
|
323
|
+
Complete only this role's assigned checklist and leave compact evidence for the coordinator.
|
|
324
|
+
|
|
325
|
+
## Source of truth
|
|
326
|
+
|
|
327
|
+
- \`openspec/plan/${PLAN_SLUG}/summary.md\`
|
|
328
|
+
- \`openspec/plan/${PLAN_SLUG}/checkpoints.md\`
|
|
329
|
+
- \`openspec/plan/${PLAN_SLUG}/open-questions.md\`
|
|
330
|
+
- \`openspec/plan/${PLAN_SLUG}/${role}/tasks.md\`
|
|
331
|
+
- \`openspec/plan/${PLAN_SLUG}/${role}/proposal.md\`
|
|
332
|
+
|
|
333
|
+
## Before edits
|
|
334
|
+
|
|
335
|
+
1. Confirm branch/worktree with \`git status --short --branch\`.
|
|
336
|
+
2. Claim every touched file before editing:
|
|
337
|
+
- Prefer Colony \`task_claim_file\` when an active task exists.
|
|
338
|
+
- Otherwise run \`gx locks claim --branch <agent-branch> <file...>\`.
|
|
339
|
+
3. Stay inside assigned files/modules; coordinate before touching shared paths.
|
|
340
|
+
|
|
341
|
+
## Working rules
|
|
342
|
+
|
|
343
|
+
- Update \`${role}/tasks.md\` as each item completes.
|
|
344
|
+
- Record durable unresolved questions in \`open-questions.md\`.
|
|
345
|
+
- Keep handoffs short: files changed, behavior touched, verification, risks.
|
|
346
|
+
- Do not revert another agent's edits.
|
|
347
|
+
|
|
348
|
+
## Cleanup
|
|
349
|
+
|
|
350
|
+
Only the owner/finalizer lane runs \`gx branch finish --branch <agent-branch> --base dev --via-pr --wait-for-merge --cleanup\`. If blocked, append \`BLOCKED:\` with branch, task, blocker, next, evidence.
|
|
351
|
+
ROLEPROMPTEOF
|
|
352
|
+
fi
|
|
353
|
+
|
|
311
354
|
if [[ ! -f "$ROLE_DIR/proposal.md" ]]; then
|
|
312
355
|
cat > "$ROLE_DIR/proposal.md" <<ROLEPROPOSALEOF
|
|
313
356
|
# Proposal: ${role} (${PLAN_SLUG})
|
|
@@ -64,6 +64,36 @@ normalize_bool() {
|
|
|
64
64
|
esac
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
gh_api_probe_looks_like_network_failure() {
|
|
68
|
+
local probe_output="$1"
|
|
69
|
+
[[ "$probe_output" =~ error[[:space:]]connecting[[:space:]]to[[:space:]]api\.github\.com|Could[[:space:]]not[[:space:]]resolve[[:space:]]host|could[[:space:]]not[[:space:]]resolve[[:space:]]host|Failed[[:space:]]to[[:space:]]connect|failed[[:space:]]to[[:space:]]connect|Network[[:space:]]is[[:space:]]unreachable|network[[:space:]]is[[:space:]]unreachable|Connection[[:space:]]timed[[:space:]]out|connection[[:space:]]timed[[:space:]]out|Temporary[[:space:]]failure[[:space:]]in[[:space:]]name[[:space:]]resolution|temporary[[:space:]]failure[[:space:]]in[[:space:]]name[[:space:]]resolution ]]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
ensure_gh_auth_or_explain() {
|
|
73
|
+
local auth_output probe_output
|
|
74
|
+
if auth_output="$(gh auth status 2>&1)"; then
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if probe_output="$(gh api user --jq .login 2>&1)"; then
|
|
79
|
+
echo "[review-bot-watch] gh auth status failed, but gh api user succeeded; continuing with usable GitHub auth." >&2
|
|
80
|
+
return 0
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if gh_api_probe_looks_like_network_failure "$probe_output"; then
|
|
84
|
+
echo "[review-bot-watch] GitHub API is unreachable, so gh cannot validate the stored token." >&2
|
|
85
|
+
echo "[review-bot-watch] This is a network or Codex sandbox connectivity problem, not proof that the token is invalid." >&2
|
|
86
|
+
echo "$probe_output" >&2
|
|
87
|
+
return 1
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
echo "[review-bot-watch] gh is not authenticated. Run: gh auth login" >&2
|
|
91
|
+
if [[ -n "$auth_output" ]]; then
|
|
92
|
+
echo "$auth_output" >&2
|
|
93
|
+
fi
|
|
94
|
+
return 1
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
ONCE=0
|
|
68
98
|
|
|
69
99
|
while [[ $# -gt 0 ]]; do
|
|
@@ -153,8 +183,7 @@ if ! command -v codex >/dev/null 2>&1; then
|
|
|
153
183
|
exit 127
|
|
154
184
|
fi
|
|
155
185
|
|
|
156
|
-
if !
|
|
157
|
-
echo "[review-bot-watch] gh is not authenticated. Run: gh auth login" >&2
|
|
186
|
+
if ! ensure_gh_auth_or_explain; then
|
|
158
187
|
exit 1
|
|
159
188
|
fi
|
|
160
189
|
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
|
|
6
|
-
function resolveSessionSchemaModule() {
|
|
7
|
-
const candidates = [
|
|
8
|
-
path.resolve(__dirname, '..', 'vscode', 'guardex-active-agents', 'session-schema.js'),
|
|
9
|
-
path.resolve(__dirname, '..', 'templates', 'vscode', 'guardex-active-agents', 'session-schema.js'),
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
for (const candidate of candidates) {
|
|
13
|
-
if (fs.existsSync(candidate)) {
|
|
14
|
-
return require(candidate);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
throw new Error('Could not resolve Guardex active-agent session schema module.');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const sessionSchema = resolveSessionSchemaModule();
|
|
22
|
-
|
|
23
|
-
function usage() {
|
|
24
|
-
return (
|
|
25
|
-
'Usage:\n' +
|
|
26
|
-
' node scripts/agent-session-state.js start --repo <path> --branch <name> --task <task> --agent <agent> --worktree <path> --pid <pid> --cli <name> [--task-mode <caveman|omx>] [--openspec-tier <T0|T1|T2|T3>] [--routing-reason <text>] [--state <working|thinking|idle>]\n' +
|
|
27
|
-
' node scripts/agent-session-state.js heartbeat --repo <path> --branch <name> [--state <working|thinking|idle>]\n' +
|
|
28
|
-
' node scripts/agent-session-state.js terminate --repo <path> --branch <name>\n' +
|
|
29
|
-
' node scripts/agent-session-state.js stop --repo <path> --branch <name>\n'
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function parseOptions(argv) {
|
|
34
|
-
const options = {};
|
|
35
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
36
|
-
const token = argv[index];
|
|
37
|
-
if (!token.startsWith('--')) {
|
|
38
|
-
throw new Error(`Unexpected argument: ${token}`);
|
|
39
|
-
}
|
|
40
|
-
const key = token.slice(2);
|
|
41
|
-
const value = argv[index + 1];
|
|
42
|
-
if (!value || value.startsWith('--')) {
|
|
43
|
-
throw new Error(`Missing value for --${key}`);
|
|
44
|
-
}
|
|
45
|
-
options[key] = value;
|
|
46
|
-
index += 1;
|
|
47
|
-
}
|
|
48
|
-
return options;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function requireOption(options, key) {
|
|
52
|
-
const value = options[key];
|
|
53
|
-
if (!value) {
|
|
54
|
-
throw new Error(`Missing required option --${key}`);
|
|
55
|
-
}
|
|
56
|
-
return value;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function writeSessionRecord(options) {
|
|
60
|
-
const repoRoot = requireOption(options, 'repo');
|
|
61
|
-
const branch = requireOption(options, 'branch');
|
|
62
|
-
const record = sessionSchema.buildSessionRecord({
|
|
63
|
-
repoRoot,
|
|
64
|
-
branch,
|
|
65
|
-
taskName: requireOption(options, 'task'),
|
|
66
|
-
agentName: requireOption(options, 'agent'),
|
|
67
|
-
worktreePath: requireOption(options, 'worktree'),
|
|
68
|
-
pid: requireOption(options, 'pid'),
|
|
69
|
-
cliName: requireOption(options, 'cli'),
|
|
70
|
-
taskMode: options['task-mode'],
|
|
71
|
-
openspecTier: options['openspec-tier'],
|
|
72
|
-
taskRoutingReason: options['routing-reason'],
|
|
73
|
-
state: options.state,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
|
|
77
|
-
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
78
|
-
fs.writeFileSync(targetPath, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function refreshSessionRecord(options) {
|
|
82
|
-
const repoRoot = requireOption(options, 'repo');
|
|
83
|
-
const branch = requireOption(options, 'branch');
|
|
84
|
-
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
|
|
85
|
-
if (!fs.existsSync(targetPath)) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const parsed = JSON.parse(fs.readFileSync(targetPath, 'utf8'));
|
|
90
|
-
const nextRecord = {
|
|
91
|
-
...parsed,
|
|
92
|
-
lastHeartbeatAt: new Date().toISOString(),
|
|
93
|
-
};
|
|
94
|
-
if (options.state) {
|
|
95
|
-
nextRecord.state = options.state;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
fs.writeFileSync(targetPath, `${JSON.stringify(nextRecord, null, 2)}\n`, 'utf8');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function readSessionRecord(options) {
|
|
102
|
-
const repoRoot = requireOption(options, 'repo');
|
|
103
|
-
const branch = requireOption(options, 'branch');
|
|
104
|
-
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
|
|
105
|
-
if (!fs.existsSync(targetPath)) {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
return JSON.parse(fs.readFileSync(targetPath, 'utf8'));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function terminateSessionProcess(options) {
|
|
112
|
-
const record = readSessionRecord(options);
|
|
113
|
-
const pid = Number(record?.pid);
|
|
114
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
115
|
-
throw new Error('No live pid recorded for branch.');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
try {
|
|
119
|
-
process.kill(pid, 'SIGTERM');
|
|
120
|
-
} catch (error) {
|
|
121
|
-
if (error?.code === 'ESRCH') {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function removeSessionRecord(options) {
|
|
129
|
-
const repoRoot = requireOption(options, 'repo');
|
|
130
|
-
const branch = requireOption(options, 'branch');
|
|
131
|
-
const targetPath = sessionSchema.sessionFilePathForBranch(repoRoot, branch);
|
|
132
|
-
if (fs.existsSync(targetPath)) {
|
|
133
|
-
fs.unlinkSync(targetPath);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function main() {
|
|
138
|
-
const [command, ...rest] = process.argv.slice(2);
|
|
139
|
-
if (!command || ['-h', '--help', 'help'].includes(command)) {
|
|
140
|
-
process.stdout.write(usage());
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const options = parseOptions(rest);
|
|
145
|
-
if (command === 'start') {
|
|
146
|
-
writeSessionRecord(options);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (command === 'heartbeat') {
|
|
150
|
-
refreshSessionRecord(options);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (command === 'terminate') {
|
|
154
|
-
terminateSessionProcess(options);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
if (command === 'stop') {
|
|
158
|
-
removeSessionRecord(options);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
throw new Error(`Unknown subcommand: ${command}`);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
main();
|
|
167
|
-
} catch (error) {
|
|
168
|
-
process.stderr.write(`[guardex-active-session] ${error.message}\n`);
|
|
169
|
-
process.stderr.write(usage());
|
|
170
|
-
process.exitCode = 1;
|
|
171
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const os = require('node:os');
|
|
5
|
-
const path = require('node:path');
|
|
6
|
-
|
|
7
|
-
const PATCH_COMPATIBILITY_WINDOW = 20;
|
|
8
|
-
const RETIRED_EXTENSION_IDS = [
|
|
9
|
-
'recodeee.gitguardex-active-agents',
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
function parseOptions(argv) {
|
|
13
|
-
const options = {};
|
|
14
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
15
|
-
const token = argv[index];
|
|
16
|
-
if (!token.startsWith('--')) {
|
|
17
|
-
throw new Error(`Unexpected argument: ${token}`);
|
|
18
|
-
}
|
|
19
|
-
const key = token.slice(2);
|
|
20
|
-
const value = argv[index + 1];
|
|
21
|
-
if (!value || value.startsWith('--')) {
|
|
22
|
-
throw new Error(`Missing value for --${key}`);
|
|
23
|
-
}
|
|
24
|
-
options[key] = value;
|
|
25
|
-
index += 1;
|
|
26
|
-
}
|
|
27
|
-
return options;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function resolveExtensionSource(repoRoot) {
|
|
31
|
-
const candidates = [
|
|
32
|
-
path.join(repoRoot, 'vscode', 'guardex-active-agents'),
|
|
33
|
-
path.join(repoRoot, 'templates', 'vscode', 'guardex-active-agents'),
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
for (const candidate of candidates) {
|
|
37
|
-
if (fs.existsSync(path.join(candidate, 'package.json'))) {
|
|
38
|
-
return candidate;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
throw new Error('Could not find the Guardex VS Code companion sources.');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function removeIfExists(targetPath) {
|
|
46
|
-
if (fs.existsSync(targetPath)) {
|
|
47
|
-
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function parseSimpleSemver(version) {
|
|
52
|
-
const parts = String(version || '').trim().split('.').map((part) => Number.parseInt(part, 10));
|
|
53
|
-
if (parts.length !== 3 || parts.some((part) => Number.isNaN(part))) {
|
|
54
|
-
throw new Error(`Expected simple semver for the Active Agents companion, received "${version}".`);
|
|
55
|
-
}
|
|
56
|
-
return parts;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function buildInstallTargets(extensionId, version, extensionsDir) {
|
|
60
|
-
const [major, minor, patch] = parseSimpleSemver(version);
|
|
61
|
-
const firstCompatiblePatch = Math.max(0, patch - PATCH_COMPATIBILITY_WINDOW);
|
|
62
|
-
const targets = [path.join(extensionsDir, extensionId)];
|
|
63
|
-
|
|
64
|
-
for (let compatiblePatch = firstCompatiblePatch; compatiblePatch <= patch; compatiblePatch += 1) {
|
|
65
|
-
targets.push(path.join(extensionsDir, `${extensionId}-${major}.${minor}.${compatiblePatch}`));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return targets;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function isRetiredExtensionInstall(entryName, currentExtensionId) {
|
|
72
|
-
return RETIRED_EXTENSION_IDS
|
|
73
|
-
.filter((extensionId) => extensionId !== currentExtensionId)
|
|
74
|
-
.some((extensionId) => entryName === extensionId || entryName.startsWith(`${extensionId}-`));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function main() {
|
|
78
|
-
const repoRoot = path.resolve(__dirname, '..');
|
|
79
|
-
const options = parseOptions(process.argv.slice(2));
|
|
80
|
-
const sourceDir = resolveExtensionSource(repoRoot);
|
|
81
|
-
const manifest = JSON.parse(fs.readFileSync(path.join(sourceDir, 'package.json'), 'utf8'));
|
|
82
|
-
const extensionId = `${manifest.publisher}.${manifest.name}`;
|
|
83
|
-
const extensionsDir = path.resolve(
|
|
84
|
-
options['extensions-dir'] ||
|
|
85
|
-
process.env.GUARDEX_VSCODE_EXTENSIONS_DIR ||
|
|
86
|
-
process.env.VSCODE_EXTENSIONS_DIR ||
|
|
87
|
-
path.join(os.homedir(), '.vscode', 'extensions'),
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
fs.mkdirSync(extensionsDir, { recursive: true });
|
|
91
|
-
const targetDirs = buildInstallTargets(extensionId, manifest.version, extensionsDir);
|
|
92
|
-
const canonicalTargetDir = targetDirs[0];
|
|
93
|
-
const keepDirNames = new Set(targetDirs.map((targetDir) => path.basename(targetDir)));
|
|
94
|
-
let retiredInstallCount = 0;
|
|
95
|
-
|
|
96
|
-
for (const entry of fs.readdirSync(extensionsDir, { withFileTypes: true })) {
|
|
97
|
-
if (!entry.isDirectory()) {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
if (isRetiredExtensionInstall(entry.name, extensionId)) {
|
|
101
|
-
removeIfExists(path.join(extensionsDir, entry.name));
|
|
102
|
-
retiredInstallCount += 1;
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
if (keepDirNames.has(entry.name)) {
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
if (entry.name === extensionId || entry.name.startsWith(`${extensionId}-`)) {
|
|
109
|
-
removeIfExists(path.join(extensionsDir, entry.name));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
for (const targetDir of targetDirs) {
|
|
114
|
-
removeIfExists(targetDir);
|
|
115
|
-
fs.cpSync(sourceDir, targetDir, {
|
|
116
|
-
recursive: true,
|
|
117
|
-
force: true,
|
|
118
|
-
preserveTimestamps: true,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
process.stdout.write(
|
|
123
|
-
`[guardex-active-agents] Installed ${extensionId}@${manifest.version} to ${canonicalTargetDir}\n` +
|
|
124
|
-
`[guardex-active-agents] Refreshed ${targetDirs.length - 1} recent patch compatibility path(s) for already-open windows.\n` +
|
|
125
|
-
`[guardex-active-agents] Removed ${retiredInstallCount} retired extension install path(s).\n` +
|
|
126
|
-
'[guardex-active-agents] Reload each already-open VS Code window to activate the newest Source Control companion.\n',
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
main();
|
|
132
|
-
} catch (error) {
|
|
133
|
-
process.stderr.write(`[guardex-active-agents] ${error.message}\n`);
|
|
134
|
-
process.exitCode = 1;
|
|
135
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# GitGuardex Active Agents
|
|
2
|
-
|
|
3
|
-
Local VS Code companion for Guardex-managed repos.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
Use the dedicated Active Agents sidebar icon to create or inspect Guardex sandboxes quickly.
|
|
8
|
-
|
|
9
|
-
1. Install from a Guardex-wired repo:
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
node scripts/install-vscode-active-agents-extension.js
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
2. Reload the VS Code window.
|
|
16
|
-
3. In the Activity Bar, open the dedicated `Active Agents` hive icon. Use `Start agent` to enter a task + agent name and launch the repo Guardex agent runner. The companion prefers `bash scripts/codex-agent.sh` when present, falls back to `npm run agent:codex --`, and only uses `gx branch start` as a last resort.
|
|
17
|
-
|
|
18
|
-
What it does:
|
|
19
|
-
|
|
20
|
-
- Bundles a local GitGuardex icon so repo installs show branded extension metadata inside VS Code.
|
|
21
|
-
- Bundles the optional `GitGuardex File Icons` theme for OpenSpec, agent worktree, and hook files in Explorer.
|
|
22
|
-
- Adds a dedicated `Active Agents` Activity Bar container with a hive icon and live badge count for active sessions.
|
|
23
|
-
- Renders one repo node per live Guardex workspace with grouped `ACTIVE AGENTS` and `CHANGES` sections.
|
|
24
|
-
- Splits live sessions inside `ACTIVE AGENTS` into `BLOCKED`, `WORKING NOW`, `THINKING`, `STALLED`, and `DEAD` groups so stuck, active, and inactive lanes stand out immediately.
|
|
25
|
-
- Mirrors the same live state in the VS Code status bar so the selected session or active-agent count stays visible outside the tree.
|
|
26
|
-
- Keeps the built-in Source Control view focused on real Git repositories; the Active Agents commit command prompts for a message from its own toolbar action.
|
|
27
|
-
- Shows one row per live Guardex sandbox session inside those activity groups, with changed-file rows nested under sessions that are touching files.
|
|
28
|
-
- Labels session rows with provider identity and snapshot context; snapshot-backed rows use a one-letter snapshot badge such as `N` for `nagyviktor@edixa.com`.
|
|
29
|
-
- Shows raw agent branch groups with the `git-branch` icon instead of the generic folder icon.
|
|
30
|
-
- Shows repo-root git changes in a sibling `CHANGES` section when the guarded repo itself is dirty.
|
|
31
|
-
- Derives session state from dirty worktree status, git conflict markers, heartbeat freshness, PID liveness, and recent file mtimes, surfaces working/dead/conflict counts in the repo/header summary, and shows changed-file counts for active edits.
|
|
32
|
-
- Uses distinct VS Code codicons for each session state, including animated `loading~spin` for `WORKING NOW`.
|
|
33
|
-
- Reads repo-local presence files from `.omx/state/active-sessions/`, expects `lastHeartbeatAt` freshness, and falls back to managed worktree-root `AGENT.lock` telemetry when the launcher session file is absent.
|
|
34
|
-
- Publishes `guardex.hasAgents` and `guardex.hasConflicts` context keys for other VS Code contributions.
|