@imdeadpool/guardex 7.0.39 → 7.0.43
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 +85 -16
- package/package.json +2 -1
- package/skills/gitguardex/SKILL.md +13 -0
- package/skills/guardex-merge-skills-to-dev/SKILL.md +59 -0
- package/src/agents/cleanup-sessions.js +126 -0
- package/src/agents/detect.js +160 -0
- package/src/agents/finish.js +172 -0
- package/src/agents/inspect.js +189 -0
- package/src/agents/launch.js +240 -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 +143 -0
- package/src/agents/terminal.js +152 -0
- package/src/budget/index.js +343 -0
- package/src/ci-init/index.js +265 -0
- package/src/cli/args.js +305 -1
- package/src/cli/main.js +262 -132
- 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/keybindings.js +224 -0
- package/src/cockpit/kitty-layout.js +549 -0
- package/src/cockpit/kitty-tree.js +144 -0
- package/src/cockpit/layout.js +224 -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 +78 -35
- package/src/doctor/index.js +4 -3
- package/src/finish/index.js +39 -2
- package/src/git/index.js +65 -0
- package/src/kitty/command.js +101 -0
- package/src/kitty/runtime.js +250 -0
- package/src/output/index.js +1 -1
- package/src/pr-review.js +241 -0
- package/src/scaffold/index.js +19 -0
- package/src/submodule/index.js +288 -0
- package/src/terminal/index.js +120 -0
- package/src/terminal/kitty.js +622 -0
- package/src/terminal/tmux.js +126 -0
- package/src/tmux/command.js +27 -0
- package/src/tmux/session.js +89 -0
- package/templates/AGENTS.multiagent-safety.md +421 -37
- package/templates/codex/skills/gitguardex/SKILL.md +2 -0
- package/templates/githooks/pre-commit +22 -1
- 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 +545 -27
- package/templates/scripts/agent-branch-start.sh +193 -21
- package/templates/scripts/agent-preflight.sh +89 -0
- package/templates/scripts/agent-worktree-prune.sh +96 -5
- package/templates/scripts/codex-agent.sh +41 -6
- 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
|
@@ -9,13 +9,16 @@ WORKTREE_ROOT_REL=""
|
|
|
9
9
|
WORKTREE_ROOT_EXPLICIT=0
|
|
10
10
|
NODE_BIN="${GUARDEX_NODE_BIN:-node}"
|
|
11
11
|
CLI_ENTRY="${GUARDEX_CLI_ENTRY:-}"
|
|
12
|
-
OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-
|
|
12
|
+
OPENSPEC_AUTO_INIT_RAW="${GUARDEX_OPENSPEC_AUTO_INIT:-true}"
|
|
13
13
|
OPENSPEC_PLAN_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_PLAN_SLUG:-}"
|
|
14
14
|
OPENSPEC_CHANGE_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CHANGE_SLUG:-}"
|
|
15
15
|
OPENSPEC_CAPABILITY_SLUG_OVERRIDE="${GUARDEX_OPENSPEC_CAPABILITY_SLUG:-}"
|
|
16
16
|
OPENSPEC_MASTERPLAN_LABEL_RAW="${GUARDEX_OPENSPEC_MASTERPLAN_LABEL-masterplan}"
|
|
17
17
|
OPENSPEC_TIER_RAW="${GUARDEX_OPENSPEC_TIER:-T3}"
|
|
18
18
|
REUSE_EXISTING_RAW="${GUARDEX_BRANCH_START_REUSE_EXISTING:-true}"
|
|
19
|
+
AUTO_TRANSFER_ENABLED_RAW="${GUARDEX_AUTO_TRANSFER:-true}"
|
|
20
|
+
AUTO_TRANSFER_EXCLUDE_DEFAULT='.omc/**:.omx/state/**:.dev-ports.json:apps/logs/**:.agents/settings.local.json:.codex/state/**:.claude/state/**'
|
|
21
|
+
AUTO_TRANSFER_EXCLUDE_RAW="${GUARDEX_AUTO_TRANSFER_EXCLUDE-$AUTO_TRANSFER_EXCLUDE_DEFAULT}"
|
|
19
22
|
PRINT_NAME_ONLY=0
|
|
20
23
|
POSITIONAL_ARGS=()
|
|
21
24
|
|
|
@@ -67,6 +70,18 @@ while [[ $# -gt 0 ]]; do
|
|
|
67
70
|
REUSE_EXISTING_RAW="false"
|
|
68
71
|
shift
|
|
69
72
|
;;
|
|
73
|
+
--no-transfer)
|
|
74
|
+
AUTO_TRANSFER_ENABLED_RAW="false"
|
|
75
|
+
shift
|
|
76
|
+
;;
|
|
77
|
+
--transfer)
|
|
78
|
+
AUTO_TRANSFER_ENABLED_RAW="true"
|
|
79
|
+
shift
|
|
80
|
+
;;
|
|
81
|
+
--transfer-exclude)
|
|
82
|
+
AUTO_TRANSFER_EXCLUDE_RAW="${2:-}"
|
|
83
|
+
shift 2
|
|
84
|
+
;;
|
|
70
85
|
--in-place|--allow-in-place)
|
|
71
86
|
echo "[agent-branch-start] In-place branch mode is disabled." >&2
|
|
72
87
|
echo "[agent-branch-start] This command always creates an isolated worktree to keep your active checkout unchanged." >&2
|
|
@@ -389,11 +404,27 @@ print_reused_agent_worktree() {
|
|
|
389
404
|
echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
|
|
390
405
|
echo "[agent-branch-start] OpenSpec change: existing worktree"
|
|
391
406
|
echo "[agent-branch-start] OpenSpec plan: existing worktree"
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
407
|
+
print_agent_next_steps "$branch_name" "$worktree_path" "continue work in this existing sandbox" "$BASE_BRANCH"
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
print_agent_next_steps() {
|
|
411
|
+
local branch_name="$1"
|
|
412
|
+
local worktree_path="$2"
|
|
413
|
+
local work_step="$3"
|
|
414
|
+
local base_branch="${4:-main}"
|
|
415
|
+
|
|
416
|
+
if [[ -z "$base_branch" ]]; then
|
|
417
|
+
base_branch="main"
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
echo "[agent-branch-start] Ready:"
|
|
421
|
+
echo " branch: ${branch_name}"
|
|
422
|
+
echo " worktree: ${worktree_path}"
|
|
423
|
+
echo " next:"
|
|
424
|
+
echo " cd \"${worktree_path}\""
|
|
425
|
+
echo " gx locks claim --branch \"${branch_name}\" <file...>"
|
|
426
|
+
echo " # ${work_step}"
|
|
427
|
+
echo " gx branch finish --branch \"${branch_name}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
|
|
397
428
|
}
|
|
398
429
|
|
|
399
430
|
has_local_changes() {
|
|
@@ -410,6 +441,101 @@ has_local_changes() {
|
|
|
410
441
|
return 1
|
|
411
442
|
}
|
|
412
443
|
|
|
444
|
+
meaningful_slug_tokens() {
|
|
445
|
+
local raw="$1"
|
|
446
|
+
printf '%s' "$raw" \
|
|
447
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
448
|
+
| tr '/_' '--' \
|
|
449
|
+
| tr '-' '\n' \
|
|
450
|
+
| awk '
|
|
451
|
+
length($0) < 4 { next }
|
|
452
|
+
$0 ~ /^[0-9]+$/ { next }
|
|
453
|
+
$0 ~ /^(agent|agents|branch|codex|claude|continue|dirty|existing|fix|from|implement|make|matching|openspec|reuse|start|task|that|this|update|with|worktree|worktrees)$/ { next }
|
|
454
|
+
!seen[$0]++ { print }
|
|
455
|
+
'
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
token_match_score() {
|
|
459
|
+
local task_slug="$1"
|
|
460
|
+
local branch_descriptor="$2"
|
|
461
|
+
local task_tokens branch_tokens token score
|
|
462
|
+
task_tokens="$(meaningful_slug_tokens "$task_slug")"
|
|
463
|
+
branch_tokens="$(meaningful_slug_tokens "$branch_descriptor")"
|
|
464
|
+
score=0
|
|
465
|
+
|
|
466
|
+
if [[ -z "$task_tokens" ]] || [[ -z "$branch_tokens" ]]; then
|
|
467
|
+
printf '0'
|
|
468
|
+
return 0
|
|
469
|
+
fi
|
|
470
|
+
|
|
471
|
+
while IFS= read -r token; do
|
|
472
|
+
if grep -Fxq "$token" <<<"$branch_tokens"; then
|
|
473
|
+
score=$((score + 1))
|
|
474
|
+
fi
|
|
475
|
+
done <<<"$task_tokens"
|
|
476
|
+
|
|
477
|
+
printf '%s' "$score"
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
managed_worktree_roots() {
|
|
481
|
+
local repo="$1"
|
|
482
|
+
local explicit_root="$2"
|
|
483
|
+
local root
|
|
484
|
+
local seen_roots=$'\n'
|
|
485
|
+
|
|
486
|
+
for root in \
|
|
487
|
+
"${repo}/${explicit_root}" \
|
|
488
|
+
"${repo}/.omx/agent-worktrees" \
|
|
489
|
+
"${repo}/.omc/agent-worktrees"; do
|
|
490
|
+
if [[ -n "$root" && "$seen_roots" != *$'\n'"$root"$'\n'* ]]; then
|
|
491
|
+
seen_roots+="${root}"$'\n'
|
|
492
|
+
printf '%s\n' "$root"
|
|
493
|
+
fi
|
|
494
|
+
done
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
find_matching_dirty_agent_worktree() {
|
|
498
|
+
local repo="$1"
|
|
499
|
+
local worktree_root_rel="$2"
|
|
500
|
+
local task_slug="$3"
|
|
501
|
+
local agent_slug="$4"
|
|
502
|
+
local best_score=0
|
|
503
|
+
local best_branch=""
|
|
504
|
+
local best_worktree=""
|
|
505
|
+
local best_count=0
|
|
506
|
+
local root entry branch descriptor score
|
|
507
|
+
|
|
508
|
+
while IFS= read -r root; do
|
|
509
|
+
[[ -d "$root" ]] || continue
|
|
510
|
+
while IFS= read -r entry; do
|
|
511
|
+
[[ -d "$entry" ]] || continue
|
|
512
|
+
if ! branch="$(git -C "$entry" rev-parse --abbrev-ref HEAD 2>/dev/null)"; then
|
|
513
|
+
continue
|
|
514
|
+
fi
|
|
515
|
+
[[ "$branch" == "agent/${agent_slug}/"* ]] || continue
|
|
516
|
+
has_local_changes "$entry" || continue
|
|
517
|
+
|
|
518
|
+
descriptor="${branch#agent/${agent_slug}/}"
|
|
519
|
+
score="$(token_match_score "$task_slug" "$descriptor")"
|
|
520
|
+
[[ "$score" =~ ^[0-9]+$ ]] || score=0
|
|
521
|
+
[[ "$score" -gt 0 ]] || continue
|
|
522
|
+
|
|
523
|
+
if [[ "$score" -gt "$best_score" ]]; then
|
|
524
|
+
best_score="$score"
|
|
525
|
+
best_branch="$branch"
|
|
526
|
+
best_worktree="$entry"
|
|
527
|
+
best_count=1
|
|
528
|
+
elif [[ "$score" -eq "$best_score" ]]; then
|
|
529
|
+
best_count=$((best_count + 1))
|
|
530
|
+
fi
|
|
531
|
+
done < <(find "$root" -mindepth 1 -maxdepth 1 -type d -print 2>/dev/null | sort)
|
|
532
|
+
done < <(managed_worktree_roots "$repo" "$worktree_root_rel")
|
|
533
|
+
|
|
534
|
+
if [[ "$best_score" -gt 0 && "$best_count" -eq 1 && -n "$best_branch" && -n "$best_worktree" ]]; then
|
|
535
|
+
printf '%s\t%s\n' "$best_branch" "$best_worktree"
|
|
536
|
+
fi
|
|
537
|
+
}
|
|
538
|
+
|
|
413
539
|
resolve_stash_ref_by_message() {
|
|
414
540
|
local root="$1"
|
|
415
541
|
local message="$2"
|
|
@@ -597,6 +723,16 @@ if [[ "$PRINT_NAME_ONLY" -eq 1 ]]; then
|
|
|
597
723
|
exit 0
|
|
598
724
|
fi
|
|
599
725
|
|
|
726
|
+
if [[ "$REUSE_EXISTING_WORKTREE" -eq 1 ]]; then
|
|
727
|
+
matching_dirty_worktree="$(find_matching_dirty_agent_worktree "$repo_root" "$WORKTREE_ROOT_REL" "$task_slug" "$agent_slug")"
|
|
728
|
+
if [[ -n "$matching_dirty_worktree" ]]; then
|
|
729
|
+
IFS=$'\t' read -r reused_branch reused_worktree <<<"$matching_dirty_worktree"
|
|
730
|
+
echo "[agent-branch-start] Matched dirty managed worktree for requested task."
|
|
731
|
+
print_reused_agent_worktree "$reused_branch" "$reused_worktree"
|
|
732
|
+
exit 0
|
|
733
|
+
fi
|
|
734
|
+
fi
|
|
735
|
+
|
|
600
736
|
if [[ "$BASE_BRANCH_EXPLICIT" -eq 0 ]]; then
|
|
601
737
|
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
602
738
|
protected_branches_raw="$(resolve_protected_branches "$repo_root")"
|
|
@@ -669,18 +805,54 @@ restore_auto_transfer_stash_on_failure() {
|
|
|
669
805
|
|
|
670
806
|
trap 'restore_auto_transfer_stash_on_failure "$?"' EXIT
|
|
671
807
|
|
|
808
|
+
auto_transfer_enabled_lc="$(printf '%s' "$AUTO_TRANSFER_ENABLED_RAW" | tr '[:upper:]' '[:lower:]')"
|
|
809
|
+
case "$auto_transfer_enabled_lc" in
|
|
810
|
+
0|false|no|off) AUTO_TRANSFER_ENABLED=0 ;;
|
|
811
|
+
*) AUTO_TRANSFER_ENABLED=1 ;;
|
|
812
|
+
esac
|
|
813
|
+
|
|
814
|
+
build_auto_transfer_stash_argv() {
|
|
815
|
+
# Emit NUL-separated argv: --include-untracked --message <msg> [-- :/ :(exclude,glob)PAT ...]
|
|
816
|
+
local msg="$1"
|
|
817
|
+
printf '%s\0' --include-untracked --message "$msg"
|
|
818
|
+
if [[ -z "${AUTO_TRANSFER_EXCLUDE_RAW:-}" ]]; then
|
|
819
|
+
return 0
|
|
820
|
+
fi
|
|
821
|
+
printf '%s\0' '--' ':/'
|
|
822
|
+
local -a patterns=()
|
|
823
|
+
IFS=':' read -ra patterns <<< "$AUTO_TRANSFER_EXCLUDE_RAW"
|
|
824
|
+
local pattern
|
|
825
|
+
for pattern in "${patterns[@]}"; do
|
|
826
|
+
[[ -z "$pattern" ]] && continue
|
|
827
|
+
printf '%s\0' ":(exclude,glob)${pattern}"
|
|
828
|
+
done
|
|
829
|
+
}
|
|
830
|
+
|
|
672
831
|
current_branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
673
832
|
protected_branches_raw="$(resolve_protected_branches "$repo_root")"
|
|
674
833
|
if [[ -n "$current_branch" && "$current_branch" != "HEAD" ]] && is_protected_branch_name "$current_branch" "$protected_branches_raw"; then
|
|
675
834
|
if has_local_changes "$repo_root"; then
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
835
|
+
if [[ "$AUTO_TRANSFER_ENABLED" -eq 0 ]]; then
|
|
836
|
+
echo "[agent-branch-start] Detected local changes on protected branch '${current_branch}'; --no-transfer set, leaving them in place." >&2
|
|
837
|
+
echo "[agent-branch-start] If you intended those changes for '${branch_name}', stash them manually and apply inside the worktree." >&2
|
|
838
|
+
else
|
|
839
|
+
auto_transfer_message="guardex-auto-transfer-${timestamp}-${agent_slug}-${task_slug}"
|
|
840
|
+
stash_argv=()
|
|
841
|
+
while IFS= read -r -d '' arg; do
|
|
842
|
+
stash_argv+=("$arg")
|
|
843
|
+
done < <(build_auto_transfer_stash_argv "$auto_transfer_message")
|
|
844
|
+
if git -C "$repo_root" stash push "${stash_argv[@]}" >/dev/null 2>&1; then
|
|
845
|
+
auto_transfer_stash_ref="$(resolve_stash_ref_by_message "$repo_root" "$auto_transfer_message")"
|
|
846
|
+
if [[ -n "$auto_transfer_stash_ref" ]]; then
|
|
847
|
+
auto_transfer_source_branch="$current_branch"
|
|
848
|
+
echo "[agent-branch-start] Detected local changes on protected branch '${current_branch}'. Moving them to '${branch_name}' (state-file globs excluded)..."
|
|
849
|
+
if ! maybe_fail_after_auto_transfer_stash; then
|
|
850
|
+
exit 1
|
|
851
|
+
fi
|
|
852
|
+
else
|
|
853
|
+
if has_local_changes "$repo_root"; then
|
|
854
|
+
echo "[agent-branch-start] Local changes on '${current_branch}' all match the auto-transfer exclude list; leaving them in place on '${current_branch}'." >&2
|
|
855
|
+
fi
|
|
684
856
|
fi
|
|
685
857
|
fi
|
|
686
858
|
fi
|
|
@@ -727,18 +899,18 @@ fi
|
|
|
727
899
|
echo "[agent-branch-start] Created branch: ${branch_name}"
|
|
728
900
|
echo "[agent-branch-start] Worktree: ${worktree_path}"
|
|
729
901
|
echo "[agent-branch-start] OpenSpec tier: ${OPENSPEC_TIER}"
|
|
730
|
-
if [[ "$
|
|
902
|
+
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
|
|
903
|
+
echo "[agent-branch-start] OpenSpec change: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)"
|
|
904
|
+
elif [[ "$OPENSPEC_SKIP_CHANGE" -eq 1 ]]; then
|
|
731
905
|
echo "[agent-branch-start] OpenSpec change: skipped by tier ${OPENSPEC_TIER}"
|
|
732
906
|
else
|
|
733
907
|
echo "[agent-branch-start] OpenSpec change: openspec/changes/${openspec_change_slug}"
|
|
734
908
|
fi
|
|
735
|
-
if [[ "$
|
|
909
|
+
if [[ "$OPENSPEC_AUTO_INIT" -ne 1 ]]; then
|
|
910
|
+
echo "[agent-branch-start] OpenSpec plan: skipped (GUARDEX_OPENSPEC_AUTO_INIT disabled)"
|
|
911
|
+
elif [[ "$OPENSPEC_SKIP_PLAN" -eq 1 ]]; then
|
|
736
912
|
echo "[agent-branch-start] OpenSpec plan: skipped by tier ${OPENSPEC_TIER}"
|
|
737
913
|
else
|
|
738
914
|
echo "[agent-branch-start] OpenSpec plan: openspec/plan/${openspec_plan_slug}"
|
|
739
915
|
fi
|
|
740
|
-
|
|
741
|
-
echo " cd \"${worktree_path}\""
|
|
742
|
-
echo " gx locks claim --branch \"${branch_name}\" <file...>"
|
|
743
|
-
echo " # implement + commit"
|
|
744
|
-
echo " gx branch finish --branch \"${branch_name}\" --base ${BASE_BRANCH} --via-pr --wait-for-merge"
|
|
916
|
+
print_agent_next_steps "$branch_name" "$worktree_path" "implement + commit" "$BASE_BRANCH"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Pre-flight verification gate for gitguardex-managed projects.
|
|
3
|
+
#
|
|
4
|
+
# Runs in the agent's worktree from `gx branch finish` BEFORE the push
|
|
5
|
+
# happens. Returns non-zero to refuse the push so a broken commit
|
|
6
|
+
# never reaches the PR / CI / merge funnel.
|
|
7
|
+
#
|
|
8
|
+
# Auto-detects the project's stack and runs conventional verification:
|
|
9
|
+
# - Node/pnpm: pnpm typecheck && pnpm lint && pnpm test (each only
|
|
10
|
+
# if the script exists in package.json)
|
|
11
|
+
# - Node/npm: npm test (only if defined)
|
|
12
|
+
# - Rust: cargo check
|
|
13
|
+
# - Python: ruff check (only if ruff is installed)
|
|
14
|
+
#
|
|
15
|
+
# Override per-project by replacing this file (delete the symlink under
|
|
16
|
+
# scripts/agent-preflight.sh and write your own).
|
|
17
|
+
#
|
|
18
|
+
# Skip a single run with `gx branch finish --no-preflight`.
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
23
|
+
cd "$repo_root"
|
|
24
|
+
|
|
25
|
+
ran=0
|
|
26
|
+
fail=0
|
|
27
|
+
run_step() {
|
|
28
|
+
local label="$1"
|
|
29
|
+
shift
|
|
30
|
+
echo "[agent-preflight] -> $label"
|
|
31
|
+
if "$@"; then
|
|
32
|
+
ran=$((ran + 1))
|
|
33
|
+
echo "[agent-preflight] ok"
|
|
34
|
+
else
|
|
35
|
+
echo "[agent-preflight] FAIL: $label" >&2
|
|
36
|
+
fail=1
|
|
37
|
+
fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
has_package_script() {
|
|
41
|
+
local script_name="$1"
|
|
42
|
+
[[ -f package.json ]] || return 1
|
|
43
|
+
grep -E "\"${script_name}\"\\s*:" package.json >/dev/null 2>&1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Node detection
|
|
47
|
+
if [[ -f package.json ]]; then
|
|
48
|
+
pkg_manager=""
|
|
49
|
+
if command -v pnpm >/dev/null 2>&1 && [[ -f pnpm-lock.yaml ]]; then
|
|
50
|
+
pkg_manager="pnpm"
|
|
51
|
+
elif command -v npm >/dev/null 2>&1 && [[ -f package-lock.json ]]; then
|
|
52
|
+
pkg_manager="npm"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
case "$pkg_manager" in
|
|
56
|
+
pnpm)
|
|
57
|
+
has_package_script typecheck && run_step "pnpm typecheck" pnpm typecheck
|
|
58
|
+
has_package_script lint && run_step "pnpm lint" pnpm lint
|
|
59
|
+
has_package_script test && run_step "pnpm test" pnpm test
|
|
60
|
+
;;
|
|
61
|
+
npm)
|
|
62
|
+
has_package_script test && run_step "npm test" npm test
|
|
63
|
+
;;
|
|
64
|
+
esac
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Rust detection
|
|
68
|
+
if [[ -f Cargo.toml ]] && command -v cargo >/dev/null 2>&1; then
|
|
69
|
+
run_step "cargo check" cargo check --quiet
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Python detection (ruff if available; pytest is too project-specific to default)
|
|
73
|
+
if [[ -f pyproject.toml ]] && command -v ruff >/dev/null 2>&1; then
|
|
74
|
+
run_step "ruff check" ruff check .
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if [[ "$ran" -eq 0 ]]; then
|
|
78
|
+
echo "[agent-preflight] No recognized project stack detected; skipping checks." >&2
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if [[ "$fail" -ne 0 ]]; then
|
|
83
|
+
echo "[agent-preflight] Verification failed; refusing push." >&2
|
|
84
|
+
echo "[agent-preflight] Fix the issues, or re-run with: gx branch finish --no-preflight ..." >&2
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
echo "[agent-preflight] ${ran} step(s) passed."
|
|
89
|
+
exit 0
|
|
@@ -82,7 +82,12 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
|
82
82
|
fi
|
|
83
83
|
|
|
84
84
|
repo_root="$(git rev-parse --show-toplevel)"
|
|
85
|
-
current_pwd="$(pwd -P)"
|
|
85
|
+
current_pwd="${GUARDEX_PRUNE_ACTIVE_CWD:-$(pwd -P)}"
|
|
86
|
+
if [[ -d "$current_pwd" ]]; then
|
|
87
|
+
current_pwd="$(cd "$current_pwd" && pwd -P)"
|
|
88
|
+
else
|
|
89
|
+
current_pwd=""
|
|
90
|
+
fi
|
|
86
91
|
repo_common_dir="$(
|
|
87
92
|
git -C "$repo_root" rev-parse --git-common-dir \
|
|
88
93
|
| awk -v root="$repo_root" '{ if ($0 ~ /^\//) { print $0 } else { print root "/" $0 } }'
|
|
@@ -244,11 +249,71 @@ branch_has_worktree() {
|
|
|
244
249
|
git -C "$repo_root" worktree list --porcelain | grep -q "^branch refs/heads/${branch}$"
|
|
245
250
|
}
|
|
246
251
|
|
|
252
|
+
# Globs treated as agent state, not real work. Worktrees whose only "dirty"
|
|
253
|
+
# content matches these are considered clean for prune purposes. Mirrors the
|
|
254
|
+
# auto-transfer + auto-resolve allowlist from PRs #546/#547 (state-file globs
|
|
255
|
+
# never carry authoritative content out of an agent branch).
|
|
256
|
+
WORKTREE_STATE_EXCLUDE_GLOBS_DEFAULT='.omc/**:.omx/state/**:.dev-ports.json:apps/logs/**:.agents/settings.local.json:.codex/state/**:.claude/state/**'
|
|
257
|
+
WORKTREE_STATE_EXCLUDE_GLOBS_RAW="${GUARDEX_PRUNE_STATE_EXCLUDE_GLOBS-$WORKTREE_STATE_EXCLUDE_GLOBS_DEFAULT}"
|
|
258
|
+
|
|
259
|
+
build_state_exclude_pathspec_args() {
|
|
260
|
+
# Emit one ':(exclude,glob)<pat>' arg per non-empty pattern.
|
|
261
|
+
if [[ -z "$WORKTREE_STATE_EXCLUDE_GLOBS_RAW" ]]; then
|
|
262
|
+
return 0
|
|
263
|
+
fi
|
|
264
|
+
local -a globs=()
|
|
265
|
+
IFS=':' read -ra globs <<< "$WORKTREE_STATE_EXCLUDE_GLOBS_RAW"
|
|
266
|
+
local pattern
|
|
267
|
+
for pattern in "${globs[@]}"; do
|
|
268
|
+
[[ -z "$pattern" ]] && continue
|
|
269
|
+
printf '%s\0' ":(exclude,glob)${pattern}"
|
|
270
|
+
done
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# Capture the state-exclude pathspecs once; reused by is_clean_worktree and
|
|
274
|
+
# the dirt-summary logger below.
|
|
275
|
+
STATE_EXCLUDE_PATHSPEC_ARGS=()
|
|
276
|
+
while IFS= read -r -d '' arg; do
|
|
277
|
+
STATE_EXCLUDE_PATHSPEC_ARGS+=("$arg")
|
|
278
|
+
done < <(build_state_exclude_pathspec_args)
|
|
279
|
+
|
|
247
280
|
is_clean_worktree() {
|
|
248
281
|
local wt="$1"
|
|
249
|
-
git -C "$wt" diff --quiet -- . "
|
|
250
|
-
&& git -C "$wt" diff --cached --quiet -- . "
|
|
251
|
-
&& [[ -z "$(git -C "$wt" ls-files --others --exclude-standard)" ]]
|
|
282
|
+
git -C "$wt" diff --quiet -- . "${STATE_EXCLUDE_PATHSPEC_ARGS[@]}" \
|
|
283
|
+
&& git -C "$wt" diff --cached --quiet -- . "${STATE_EXCLUDE_PATHSPEC_ARGS[@]}" \
|
|
284
|
+
&& [[ -z "$(git -C "$wt" ls-files --others --exclude-standard -- . "${STATE_EXCLUDE_PATHSPEC_ARGS[@]}")" ]]
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# Returns a short, human-readable summary of why a worktree is considered dirty:
|
|
288
|
+
# up to 3 modified-tracked paths + up to 3 untracked paths + a "(+N more)" tail.
|
|
289
|
+
# Used to surface actionable context next to "Skipping dirty worktree" log lines
|
|
290
|
+
# (previously gave no clue what was actually dirty).
|
|
291
|
+
summarize_worktree_dirt() {
|
|
292
|
+
local wt="$1"
|
|
293
|
+
local modified_paths untracked_paths
|
|
294
|
+
modified_paths="$(git -C "$wt" diff --name-only --diff-filter=AMDR -- . "${STATE_EXCLUDE_PATHSPEC_ARGS[@]}" 2>/dev/null | head -3)"
|
|
295
|
+
local modified_count
|
|
296
|
+
modified_count="$(git -C "$wt" diff --name-only --diff-filter=AMDR -- . "${STATE_EXCLUDE_PATHSPEC_ARGS[@]}" 2>/dev/null | wc -l | tr -d ' ')"
|
|
297
|
+
untracked_paths="$(git -C "$wt" ls-files --others --exclude-standard -- . "${STATE_EXCLUDE_PATHSPEC_ARGS[@]}" 2>/dev/null | head -3)"
|
|
298
|
+
local untracked_count
|
|
299
|
+
untracked_count="$(git -C "$wt" ls-files --others --exclude-standard -- . "${STATE_EXCLUDE_PATHSPEC_ARGS[@]}" 2>/dev/null | wc -l | tr -d ' ')"
|
|
300
|
+
|
|
301
|
+
if [[ -n "$modified_paths" ]]; then
|
|
302
|
+
while IFS= read -r p; do
|
|
303
|
+
[[ -n "$p" ]] && echo " modified: ${p}"
|
|
304
|
+
done <<< "$modified_paths"
|
|
305
|
+
if [[ "$modified_count" -gt 3 ]]; then
|
|
306
|
+
echo " modified: (+$((modified_count - 3)) more)"
|
|
307
|
+
fi
|
|
308
|
+
fi
|
|
309
|
+
if [[ -n "$untracked_paths" ]]; then
|
|
310
|
+
while IFS= read -r p; do
|
|
311
|
+
[[ -n "$p" ]] && echo " untracked: ${p}"
|
|
312
|
+
done <<< "$untracked_paths"
|
|
313
|
+
if [[ "$untracked_count" -gt 3 ]]; then
|
|
314
|
+
echo " untracked: (+$((untracked_count - 3)) more)"
|
|
315
|
+
fi
|
|
316
|
+
fi
|
|
252
317
|
}
|
|
253
318
|
|
|
254
319
|
resolve_worktree_common_dir() {
|
|
@@ -317,6 +382,26 @@ read_branch_activity_epoch() {
|
|
|
317
382
|
|
|
318
383
|
skipped_recent=0
|
|
319
384
|
|
|
385
|
+
has_live_process_in_worktree() {
|
|
386
|
+
local wt="$1"
|
|
387
|
+
local proc_cwd=""
|
|
388
|
+
|
|
389
|
+
[[ -d /proc ]] || return 1
|
|
390
|
+
|
|
391
|
+
for proc_cwd in /proc/[0-9]*/cwd; do
|
|
392
|
+
[[ -e "$proc_cwd" ]] || continue
|
|
393
|
+
local live_cwd=""
|
|
394
|
+
live_cwd="$(readlink "$proc_cwd" 2>/dev/null || true)"
|
|
395
|
+
[[ -n "$live_cwd" ]] || continue
|
|
396
|
+
live_cwd="${live_cwd% (deleted)}"
|
|
397
|
+
if [[ "$live_cwd" == "$wt" || "$live_cwd" == "${wt}"/* ]]; then
|
|
398
|
+
return 0
|
|
399
|
+
fi
|
|
400
|
+
done
|
|
401
|
+
|
|
402
|
+
return 1
|
|
403
|
+
}
|
|
404
|
+
|
|
320
405
|
branch_idle_gate() {
|
|
321
406
|
local branch="$1"
|
|
322
407
|
local wt="$2"
|
|
@@ -431,11 +516,16 @@ process_entry() {
|
|
|
431
516
|
return
|
|
432
517
|
fi
|
|
433
518
|
|
|
434
|
-
if [[ "$wt" == "$current_pwd" ]]; then
|
|
519
|
+
if [[ -n "$current_pwd" && ( "$wt" == "$current_pwd" || "$current_pwd" == "${wt}"/* ) ]]; then
|
|
435
520
|
skipped_active=$((skipped_active + 1))
|
|
436
521
|
echo "[agent-worktree-prune] Skipping active cwd worktree: ${wt}"
|
|
437
522
|
return
|
|
438
523
|
fi
|
|
524
|
+
if has_live_process_in_worktree "$wt"; then
|
|
525
|
+
skipped_active=$((skipped_active + 1))
|
|
526
|
+
echo "[agent-worktree-prune] Skipping live process worktree: ${wt}"
|
|
527
|
+
return
|
|
528
|
+
fi
|
|
439
529
|
|
|
440
530
|
local remove_reason=""
|
|
441
531
|
local branch_delete_mode="safe"
|
|
@@ -477,6 +567,7 @@ process_entry() {
|
|
|
477
567
|
if [[ "$FORCE_DIRTY" -ne 1 ]] && ! is_clean_worktree "$wt"; then
|
|
478
568
|
skipped_dirty=$((skipped_dirty + 1))
|
|
479
569
|
echo "[agent-worktree-prune] Skipping dirty worktree (${remove_reason}): ${wt}"
|
|
570
|
+
summarize_worktree_dirt "$wt"
|
|
480
571
|
return
|
|
481
572
|
fi
|
|
482
573
|
|
|
@@ -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
|
|
@@ -735,9 +759,14 @@ print_takeover_prompt() {
|
|
|
735
759
|
|
|
736
760
|
finish_cmd="gx branch finish --branch \"${branch}\" --base ${base_branch} --via-pr --wait-for-merge --cleanup"
|
|
737
761
|
|
|
738
|
-
echo "[codex-agent]
|
|
739
|
-
echo "
|
|
740
|
-
echo "
|
|
762
|
+
echo "[codex-agent] Resume this sandbox:"
|
|
763
|
+
echo " change: ${change_slug}"
|
|
764
|
+
echo " branch: ${branch}"
|
|
765
|
+
echo " worktree: ${wt}"
|
|
766
|
+
echo " spec: ${change_artifact}"
|
|
767
|
+
echo " routing: $(describe_task_routing) (${TASK_ROUTING_REASON})"
|
|
768
|
+
echo " rule: continue current state; do not create a new sandbox"
|
|
769
|
+
echo " finish: ${finish_cmd}"
|
|
741
770
|
}
|
|
742
771
|
|
|
743
772
|
sync_worktree_with_base() {
|
|
@@ -1034,7 +1063,8 @@ run_finish_flow() {
|
|
|
1034
1063
|
(
|
|
1035
1064
|
cd "$wt"
|
|
1036
1065
|
set +e
|
|
1037
|
-
|
|
1066
|
+
build_codex_launch_args "$review_prompt"
|
|
1067
|
+
"$CODEX_BIN" "${CODEX_LAUNCH_ARGS[@]}"
|
|
1038
1068
|
review_exit="$?"
|
|
1039
1069
|
set -e
|
|
1040
1070
|
if [[ "$review_exit" -ne 0 ]]; then
|
|
@@ -1092,10 +1122,11 @@ trap cleanup_active_session_state_on_exit EXIT INT TERM
|
|
|
1092
1122
|
echo "[codex-agent] Launching ${CODEX_BIN} in sandbox: $worktree_path"
|
|
1093
1123
|
cd "$worktree_path"
|
|
1094
1124
|
set +e
|
|
1125
|
+
build_codex_launch_args "$@"
|
|
1095
1126
|
GUARDEX_TASK_MODE="$TASK_MODE" \
|
|
1096
1127
|
GUARDEX_OPENSPEC_TIER="$OPENSPEC_TIER" \
|
|
1097
1128
|
GUARDEX_TASK_ROUTING_REASON="$TASK_ROUTING_REASON" \
|
|
1098
|
-
"$CODEX_BIN" "
|
|
1129
|
+
"$CODEX_BIN" "${CODEX_LAUNCH_ARGS[@]}"
|
|
1099
1130
|
codex_exit="$?"
|
|
1100
1131
|
set -e
|
|
1101
1132
|
|
|
@@ -1161,7 +1192,11 @@ else
|
|
|
1161
1192
|
echo "[codex-agent] Branch kept intentionally. Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
|
|
1162
1193
|
else
|
|
1163
1194
|
print_takeover_prompt "$worktree_path" "$worktree_branch"
|
|
1164
|
-
|
|
1195
|
+
finish_base_branch="$(resolve_worktree_base_branch "$worktree_path")"
|
|
1196
|
+
if [[ -z "$finish_base_branch" ]]; then
|
|
1197
|
+
finish_base_branch="dev"
|
|
1198
|
+
fi
|
|
1199
|
+
echo "[codex-agent] If finished, merge with: gx branch finish --branch \"${worktree_branch}\" --base ${finish_base_branch} --via-pr --wait-for-merge --cleanup"
|
|
1165
1200
|
echo "[codex-agent] Cleanup on demand: gx cleanup --branch \"${worktree_branch}\""
|
|
1166
1201
|
fi
|
|
1167
1202
|
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})
|