agent-control-plane 0.1.9 → 0.1.13

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.
Files changed (40) hide show
  1. package/hooks/heartbeat-hooks.sh +147 -8
  2. package/hooks/issue-reconcile-hooks.sh +46 -0
  3. package/npm/bin/agent-control-plane.js +89 -8
  4. package/package.json +8 -2
  5. package/references/commands.md +1 -0
  6. package/tools/bin/agent-project-cleanup-session +133 -0
  7. package/tools/bin/agent-project-publish-issue-pr +178 -62
  8. package/tools/bin/agent-project-reconcile-issue-session +171 -3
  9. package/tools/bin/agent-project-run-codex-resilient +121 -16
  10. package/tools/bin/agent-project-run-codex-session +118 -10
  11. package/tools/bin/agent-project-run-openclaw-session +82 -8
  12. package/tools/bin/branch-verification-guard.sh +15 -2
  13. package/tools/bin/cleanup-worktree.sh +4 -1
  14. package/tools/bin/dashboard-launchd-bootstrap.sh +16 -4
  15. package/tools/bin/ensure-runtime-sync.sh +182 -0
  16. package/tools/bin/flow-config-lib.sh +76 -30
  17. package/tools/bin/flow-resident-worker-lib.sh +28 -2
  18. package/tools/bin/flow-shell-lib.sh +15 -1
  19. package/tools/bin/heartbeat-safe-auto.sh +32 -0
  20. package/tools/bin/issue-publish-localization-guard.sh +142 -0
  21. package/tools/bin/project-launchd-bootstrap.sh +17 -4
  22. package/tools/bin/project-runtime-supervisor.sh +7 -1
  23. package/tools/bin/project-runtimectl.sh +78 -15
  24. package/tools/bin/reuse-issue-worktree.sh +46 -0
  25. package/tools/bin/start-issue-worker.sh +110 -30
  26. package/tools/bin/start-resident-issue-loop.sh +1 -0
  27. package/tools/bin/sync-shared-agent-home.sh +50 -10
  28. package/tools/bin/test-smoke.sh +6 -1
  29. package/tools/dashboard/app.js +71 -1
  30. package/tools/dashboard/dashboard_snapshot.py +74 -0
  31. package/tools/dashboard/styles.css +43 -0
  32. package/tools/templates/issue-prompt-template.md +20 -65
  33. package/tools/templates/legacy/issue-prompt-template-pre-slim.md +109 -0
  34. package/bin/audit-issue-routing.sh +0 -74
  35. package/tools/bin/audit-agent-worktrees.sh +0 -310
  36. package/tools/bin/audit-issue-routing.sh +0 -11
  37. package/tools/bin/audit-retained-layout.sh +0 -58
  38. package/tools/bin/audit-retained-overlap.sh +0 -135
  39. package/tools/bin/audit-retained-worktrees.sh +0 -228
  40. package/tools/bin/check-skill-contracts.sh +0 -324
@@ -1,135 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- PRIMARY_ROOT=""
5
- SECONDARY_ROOT=""
6
- PRIMARY_LABEL="primary"
7
- SECONDARY_LABEL="secondary"
8
- SUMMARY_ONLY="false"
9
-
10
- usage() {
11
- cat <<'EOF'
12
- Usage:
13
- audit-retained-overlap.sh --primary <path> --secondary <path> [options]
14
-
15
- Compares uncommitted file sets between two retained human worktrees.
16
-
17
- Options:
18
- --primary <path> Primary retained worktree
19
- --secondary <path> Secondary retained worktree
20
- --primary-label <label> Label for primary output (default: primary)
21
- --secondary-label <label> Label for secondary output (default: secondary)
22
- --summary-only Print counts and grouped summaries only
23
- --help Show this help
24
- EOF
25
- }
26
-
27
- while [[ $# -gt 0 ]]; do
28
- case "$1" in
29
- --primary) PRIMARY_ROOT="${2:-}"; shift 2 ;;
30
- --secondary) SECONDARY_ROOT="${2:-}"; shift 2 ;;
31
- --primary-label) PRIMARY_LABEL="${2:-}"; shift 2 ;;
32
- --secondary-label) SECONDARY_LABEL="${2:-}"; shift 2 ;;
33
- --summary-only) SUMMARY_ONLY="true"; shift ;;
34
- --help|-h) usage; exit 0 ;;
35
- *) echo "Unknown argument: $1" >&2; usage >&2; exit 1 ;;
36
- esac
37
- done
38
-
39
- if [[ -z "$PRIMARY_ROOT" || -z "$SECONDARY_ROOT" ]]; then
40
- usage >&2
41
- exit 1
42
- fi
43
-
44
- for repo_root in "$PRIMARY_ROOT" "$SECONDARY_ROOT"; do
45
- if [[ ! -d "$repo_root/.git" && ! -f "$repo_root/.git" ]]; then
46
- echo "not a git checkout: $repo_root" >&2
47
- exit 1
48
- fi
49
- done
50
-
51
- tmp_dir="$(mktemp -d "${TMPDIR:-/tmp}/retained-overlap.XXXXXX")"
52
- cleanup() {
53
- rm -rf "$tmp_dir"
54
- }
55
- trap cleanup EXIT
56
-
57
- collect_changed_files() {
58
- local repo_root="${1:?repo root required}"
59
- git -C "$repo_root" status --porcelain | sed -E 's/^...//' | sort -u
60
- }
61
-
62
- group_paths() {
63
- awk -F/ '
64
- NF == 0 { next }
65
- {
66
- limit = (NF < 5 ? NF : 5)
67
- key = $1
68
- for (i = 2; i <= limit; i++) {
69
- key = key "/" $i
70
- }
71
- print key
72
- }
73
- ' | sort | uniq -c | sort -nr | sed 's/^ *//'
74
- }
75
-
76
- collect_changed_files "$PRIMARY_ROOT" >"${tmp_dir}/primary.txt"
77
- collect_changed_files "$SECONDARY_ROOT" >"${tmp_dir}/secondary.txt"
78
-
79
- comm -12 "${tmp_dir}/primary.txt" "${tmp_dir}/secondary.txt" >"${tmp_dir}/overlap.txt"
80
- comm -23 "${tmp_dir}/primary.txt" "${tmp_dir}/secondary.txt" >"${tmp_dir}/primary-only.txt"
81
- comm -13 "${tmp_dir}/primary.txt" "${tmp_dir}/secondary.txt" >"${tmp_dir}/secondary-only.txt"
82
-
83
- primary_count="$(wc -l <"${tmp_dir}/primary.txt" | tr -d ' ')"
84
- secondary_count="$(wc -l <"${tmp_dir}/secondary.txt" | tr -d ' ')"
85
- overlap_count="$(wc -l <"${tmp_dir}/overlap.txt" | tr -d ' ')"
86
- primary_only_count="$(wc -l <"${tmp_dir}/primary-only.txt" | tr -d ' ')"
87
- secondary_only_count="$(wc -l <"${tmp_dir}/secondary-only.txt" | tr -d ' ')"
88
-
89
- printf '%s_ROOT=%s\n' "$(printf '%s' "$PRIMARY_LABEL" | tr '[:lower:]-' '[:upper:]_')" "$PRIMARY_ROOT"
90
- printf '%s_ROOT=%s\n' "$(printf '%s' "$SECONDARY_LABEL" | tr '[:lower:]-' '[:upper:]_')" "$SECONDARY_ROOT"
91
- printf '%s_COUNT=%s\n' "$(printf '%s' "$PRIMARY_LABEL" | tr '[:lower:]-' '[:upper:]_')" "$primary_count"
92
- printf '%s_COUNT=%s\n' "$(printf '%s' "$SECONDARY_LABEL" | tr '[:lower:]-' '[:upper:]_')" "$secondary_count"
93
- printf 'OVERLAP_COUNT=%s\n' "$overlap_count"
94
- printf '%s_ONLY_COUNT=%s\n' "$(printf '%s' "$PRIMARY_LABEL" | tr '[:lower:]-' '[:upper:]_')" "$primary_only_count"
95
- printf '%s_ONLY_COUNT=%s\n' "$(printf '%s' "$SECONDARY_LABEL" | tr '[:lower:]-' '[:upper:]_')" "$secondary_only_count"
96
- printf '\n'
97
-
98
- printf '[%s-only-groups]\n' "$PRIMARY_LABEL"
99
- if [[ -s "${tmp_dir}/primary-only.txt" ]]; then
100
- group_paths <"${tmp_dir}/primary-only.txt"
101
- else
102
- printf 'none\n'
103
- fi
104
- printf '\n'
105
-
106
- printf '[%s-only-groups]\n' "$SECONDARY_LABEL"
107
- if [[ -s "${tmp_dir}/secondary-only.txt" ]]; then
108
- group_paths <"${tmp_dir}/secondary-only.txt"
109
- else
110
- printf 'none\n'
111
- fi
112
- printf '\n'
113
-
114
- printf '[overlap-groups]\n'
115
- if [[ -s "${tmp_dir}/overlap.txt" ]]; then
116
- group_paths <"${tmp_dir}/overlap.txt"
117
- else
118
- printf 'none\n'
119
- fi
120
- printf '\n'
121
-
122
- if [[ "$SUMMARY_ONLY" == "true" ]]; then
123
- exit 0
124
- fi
125
-
126
- printf '[%s-only-files]\n' "$PRIMARY_LABEL"
127
- cat "${tmp_dir}/primary-only.txt"
128
- printf '\n'
129
-
130
- printf '[%s-only-files]\n' "$SECONDARY_LABEL"
131
- cat "${tmp_dir}/secondary-only.txt"
132
- printf '\n'
133
-
134
- printf '[overlap-files]\n'
135
- cat "${tmp_dir}/overlap.txt"
@@ -1,228 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
- # shellcheck source=/dev/null
6
- source "${SCRIPT_DIR}/flow-config-lib.sh"
7
-
8
- CONFIG_YAML="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
9
- RETAINED_ROOT="$(flow_resolve_retained_repo_root "${CONFIG_YAML}")"
10
- WORKTREE_ROOT="$(flow_resolve_worktree_root "${CONFIG_YAML}")"
11
- AGENT_ROOT="$(flow_resolve_agent_root "${CONFIG_YAML}")"
12
- RUNS_ROOT="$(flow_resolve_runs_root "${CONFIG_YAML}")"
13
- ADAPTER_ID="$(flow_resolve_adapter_id "${CONFIG_YAML}")"
14
- ADAPTER_ID_REGEX="$(flow_escape_regex "${ADAPTER_ID}")"
15
- ISSUE_SESSION_PREFIX="$(flow_resolve_issue_session_prefix "${CONFIG_YAML}")"
16
- PR_SESSION_PREFIX="$(flow_resolve_pr_session_prefix "${CONFIG_YAML}")"
17
- ISSUE_BRANCH_PREFIX="$(flow_resolve_issue_branch_prefix "${CONFIG_YAML}")"
18
- PR_WORKTREE_BRANCH_PREFIX="$(flow_resolve_pr_worktree_branch_prefix "${CONFIG_YAML}")"
19
- MANAGED_PR_BRANCH_GLOBS="$(flow_resolve_managed_pr_branch_globs "${CONFIG_YAML}")"
20
- ISSUE_BRANCH_PREFIX_REGEX="$(flow_escape_regex "${ISSUE_BRANCH_PREFIX}")"
21
- PR_WORKTREE_BRANCH_PREFIX_REGEX="$(flow_escape_regex "${PR_WORKTREE_BRANCH_PREFIX}")"
22
-
23
- cleanup="false"
24
- strict="false"
25
-
26
- usage() {
27
- cat <<'EOF'
28
- Usage:
29
- audit-retained-worktrees.sh [--cleanup] [--strict]
30
-
31
- Audits the retained human checkout for legacy automation worktrees that still
32
- attach to the retained repo's .git/worktrees metadata.
33
-
34
- Options:
35
- --cleanup Remove clean orphaned automation worktrees automatically.
36
- --strict Exit non-zero when any legacy automation worktree is found.
37
- EOF
38
- }
39
-
40
- while [[ $# -gt 0 ]]; do
41
- case "$1" in
42
- --cleanup) cleanup="true"; shift ;;
43
- --strict) strict="true"; shift ;;
44
- --help|-h) usage; exit 0 ;;
45
- *) echo "Unknown argument: $1" >&2; usage >&2; exit 1 ;;
46
- esac
47
- done
48
-
49
- if [[ ! -d "$RETAINED_ROOT/.git" && ! -f "$RETAINED_ROOT/.git" ]]; then
50
- echo "retained root is not a Git checkout: $RETAINED_ROOT" >&2
51
- exit 1
52
- fi
53
-
54
- session_for_worktree() {
55
- local worktree_path="${1:-}"
56
- local branch_ref="${2:-}"
57
- local base
58
-
59
- base="$(basename "$worktree_path")"
60
- if [[ "$base" =~ ^${ADAPTER_ID_REGEX}-pr-([0-9]+)$ ]]; then
61
- printf '%s%s\n' "${PR_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
62
- return 0
63
- fi
64
- if [[ "$base" =~ ^pr-([0-9]+)- ]]; then
65
- printf '%s%s\n' "${PR_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
66
- return 0
67
- fi
68
- if [[ "$base" =~ ^${ADAPTER_ID_REGEX}-issue-([0-9]+)$ ]]; then
69
- printf '%s%s\n' "${ISSUE_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
70
- return 0
71
- fi
72
- if [[ "$base" =~ ^issue-([0-9]+)- ]]; then
73
- printf '%s%s\n' "${ISSUE_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
74
- return 0
75
- fi
76
- if [[ "$branch_ref" =~ ^refs/heads/${PR_WORKTREE_BRANCH_PREFIX_REGEX}-([0-9]+)- ]]; then
77
- printf '%s%s\n' "${PR_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
78
- return 0
79
- fi
80
- if [[ "$branch_ref" =~ ^refs/heads/${ISSUE_BRANCH_PREFIX_REGEX}-([0-9]+)- ]]; then
81
- printf '%s%s\n' "${ISSUE_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
82
- return 0
83
- fi
84
- return 1
85
- }
86
-
87
- branch_name_is_managed() {
88
- local branch_name="${1:-}"
89
- local branch_glob=""
90
-
91
- for branch_glob in ${MANAGED_PR_BRANCH_GLOBS}; do
92
- case "$branch_name" in
93
- ${branch_glob}) return 0 ;;
94
- esac
95
- done
96
-
97
- return 1
98
- }
99
-
100
- is_legacy_automation_worktree() {
101
- local worktree_path="${1:-}"
102
- local branch_ref="${2:-}"
103
- local base
104
-
105
- base="$(basename "$worktree_path")"
106
- if [[ "$worktree_path" == "$WORKTREE_ROOT/"* ]]; then
107
- return 0
108
- fi
109
- case "$base" in
110
- ${ADAPTER_ID}-main-clean|${ADAPTER_ID}-pr-*|${ADAPTER_ID}-issue-*) return 0 ;;
111
- esac
112
- if [[ "$branch_ref" == refs/heads/* ]] && branch_name_is_managed "${branch_ref#refs/heads/}"; then
113
- return 0
114
- fi
115
- return 1
116
- }
117
-
118
- worktree_dirty() {
119
- local worktree_path="${1:-}"
120
- [[ -n "$(git -C "$worktree_path" status --short --untracked-files=normal)" ]]
121
- }
122
-
123
- worktree_has_active_owner() {
124
- local session_name="${1:-}"
125
- local status_output=""
126
- local status=""
127
-
128
- [[ -n "$session_name" ]] || return 1
129
-
130
- if tmux has-session -t "$session_name" 2>/dev/null; then
131
- return 0
132
- fi
133
-
134
- local run_dir="${RUNS_ROOT}/${session_name}"
135
- if [[ ! -d "$run_dir" ]]; then
136
- return 1
137
- fi
138
-
139
- # Completed workers still own their worktree until host-side reconcile
140
- # archives the run or writes reconciled.ok. Otherwise audit can delete the
141
- # worktree before publish/retry transitions consume it.
142
- if [[ ! -f "${run_dir}/reconciled.ok" ]]; then
143
- return 0
144
- fi
145
-
146
- status_output="$(
147
- bash "${BASH_SOURCE[0]%/*}/agent-project-worker-status" \
148
- --runs-root "$RUNS_ROOT" \
149
- --session "$session_name" 2>/dev/null || true
150
- )"
151
- status="$(awk -F= '/^STATUS=/{print $2}' <<<"$status_output" | tail -n 1)"
152
- [[ "$status" == "RUNNING" ]]
153
- }
154
-
155
- remove_legacy_worktree() {
156
- local worktree_path="${1:-}"
157
- local branch_ref="${2:-}"
158
- local branch_name=""
159
-
160
- git -C "$RETAINED_ROOT" worktree remove "$worktree_path" --force
161
-
162
- if [[ "$branch_ref" == refs/heads/* ]]; then
163
- branch_name="${branch_ref#refs/heads/}"
164
- if git -C "$RETAINED_ROOT" show-ref --verify --quiet "refs/heads/${branch_name}"; then
165
- git -C "$RETAINED_ROOT" branch -D "$branch_name" >/dev/null 2>&1 || true
166
- fi
167
- fi
168
- }
169
-
170
- issue_count=0
171
- cleaned_count=0
172
-
173
- current_worktree=""
174
- current_branch_ref=""
175
- current_head=""
176
-
177
- while IFS= read -r line || [[ -n "$line" ]]; do
178
- if [[ -z "$line" ]]; then
179
- if [[ -n "$current_worktree" ]]; then
180
- if [[ "$current_worktree" != "$RETAINED_ROOT" ]] && is_legacy_automation_worktree "$current_worktree" "$current_branch_ref"; then
181
- issue_count=$((issue_count + 1))
182
- session_name="$(session_for_worktree "$current_worktree" "$current_branch_ref" || true)"
183
- dirty="no"
184
- if worktree_dirty "$current_worktree"; then
185
- dirty="yes"
186
- fi
187
- active_owner="no"
188
- if worktree_has_active_owner "$session_name"; then
189
- active_owner="yes"
190
- fi
191
-
192
- printf 'RETAINED_WORKTREE=%s\n' "$current_worktree"
193
- printf 'BRANCH_REF=%s\n' "${current_branch_ref:-<detached>}"
194
- printf 'HEAD=%s\n' "${current_head:-<unknown>}"
195
- printf 'SESSION=%s\n' "${session_name:-<none>}"
196
- printf 'DIRTY=%s\n' "$dirty"
197
- printf 'ACTIVE_OWNER=%s\n' "$active_owner"
198
-
199
- if [[ "$cleanup" == "true" && "$dirty" == "no" && "$active_owner" == "no" ]]; then
200
- remove_legacy_worktree "$current_worktree" "$current_branch_ref"
201
- cleaned_count=$((cleaned_count + 1))
202
- printf 'CLEANUP=removed\n'
203
- else
204
- printf 'CLEANUP=skipped\n'
205
- fi
206
- printf '\n'
207
- fi
208
-
209
- current_worktree=""
210
- current_branch_ref=""
211
- current_head=""
212
- fi
213
- continue
214
- fi
215
-
216
- case "$line" in
217
- worktree\ *) current_worktree="${line#worktree }" ;;
218
- HEAD\ *) current_head="${line#HEAD }" ;;
219
- branch\ *) current_branch_ref="${line#branch }" ;;
220
- esac
221
- done < <(git -C "$RETAINED_ROOT" worktree list --porcelain; printf '\n')
222
-
223
- printf 'LEGACY_RETAINED_WORKTREE_COUNT=%s\n' "$issue_count"
224
- printf 'LEGACY_RETAINED_WORKTREE_CLEANED=%s\n' "$cleaned_count"
225
-
226
- if [[ "$strict" == "true" && "$issue_count" -gt 0 ]]; then
227
- exit 2
228
- fi