agent-control-plane 0.1.9 → 0.1.12

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 (37) hide show
  1. package/hooks/heartbeat-hooks.sh +97 -8
  2. package/package.json +8 -2
  3. package/references/commands.md +1 -0
  4. package/tools/bin/agent-project-cleanup-session +133 -0
  5. package/tools/bin/agent-project-publish-issue-pr +178 -62
  6. package/tools/bin/agent-project-reconcile-issue-session +171 -3
  7. package/tools/bin/agent-project-run-codex-resilient +121 -16
  8. package/tools/bin/agent-project-run-codex-session +60 -10
  9. package/tools/bin/agent-project-run-openclaw-session +82 -8
  10. package/tools/bin/cleanup-worktree.sh +4 -1
  11. package/tools/bin/dashboard-launchd-bootstrap.sh +16 -4
  12. package/tools/bin/ensure-runtime-sync.sh +182 -0
  13. package/tools/bin/flow-config-lib.sh +76 -30
  14. package/tools/bin/flow-resident-worker-lib.sh +28 -2
  15. package/tools/bin/flow-shell-lib.sh +15 -1
  16. package/tools/bin/heartbeat-safe-auto.sh +32 -0
  17. package/tools/bin/issue-publish-localization-guard.sh +142 -0
  18. package/tools/bin/project-launchd-bootstrap.sh +17 -4
  19. package/tools/bin/project-runtime-supervisor.sh +7 -1
  20. package/tools/bin/project-runtimectl.sh +78 -15
  21. package/tools/bin/reuse-issue-worktree.sh +46 -0
  22. package/tools/bin/start-issue-worker.sh +76 -6
  23. package/tools/bin/start-resident-issue-loop.sh +1 -0
  24. package/tools/bin/sync-shared-agent-home.sh +26 -0
  25. package/tools/bin/test-smoke.sh +6 -1
  26. package/tools/dashboard/app.js +71 -1
  27. package/tools/dashboard/dashboard_snapshot.py +74 -0
  28. package/tools/dashboard/styles.css +43 -0
  29. package/tools/templates/issue-prompt-template.md +18 -66
  30. package/tools/templates/legacy/issue-prompt-template-pre-slim.md +109 -0
  31. package/bin/audit-issue-routing.sh +0 -74
  32. package/tools/bin/audit-agent-worktrees.sh +0 -310
  33. package/tools/bin/audit-issue-routing.sh +0 -11
  34. package/tools/bin/audit-retained-layout.sh +0 -58
  35. package/tools/bin/audit-retained-overlap.sh +0 -135
  36. package/tools/bin/audit-retained-worktrees.sh +0 -228
  37. package/tools/bin/check-skill-contracts.sh +0 -324
@@ -1,310 +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
- WORKER_STATUS_TOOL="${BASH_SOURCE[0]%/*}/agent-project-worker-status"
9
- WORKTREE_CLEANUP_TOOL="${BASH_SOURCE[0]%/*}/agent-cleanup-worktree"
10
- CONFIG_YAML="$(resolve_flow_config_yaml "${BASH_SOURCE[0]}")"
11
- AGENT_REPO_ROOT="$(flow_resolve_agent_repo_root "${CONFIG_YAML}")"
12
- WORKTREE_ROOT="$(flow_resolve_worktree_root "${CONFIG_YAML}")"
13
- AGENT_ROOT="$(flow_resolve_agent_root "${CONFIG_YAML}")"
14
- RUNS_ROOT="$(flow_resolve_runs_root "${CONFIG_YAML}")"
15
- PENDING_LAUNCH_DIR="${ACP_PENDING_LAUNCH_DIR:-${F_LOSNING_PENDING_LAUNCH_DIR:-$(flow_resolve_state_root "${CONFIG_YAML}")/pending-launches}}"
16
- ISSUE_SESSION_PREFIX="$(flow_resolve_issue_session_prefix "${CONFIG_YAML}")"
17
- PR_SESSION_PREFIX="$(flow_resolve_pr_session_prefix "${CONFIG_YAML}")"
18
- ISSUE_BRANCH_PREFIX="$(flow_resolve_issue_branch_prefix "${CONFIG_YAML}")"
19
- PR_WORKTREE_BRANCH_PREFIX="$(flow_resolve_pr_worktree_branch_prefix "${CONFIG_YAML}")"
20
- MANAGED_PR_BRANCH_GLOBS="$(flow_resolve_managed_pr_branch_globs "${CONFIG_YAML}")"
21
- ISSUE_BRANCH_PREFIX_REGEX="$(flow_escape_regex "${ISSUE_BRANCH_PREFIX}")"
22
- PR_WORKTREE_BRANCH_PREFIX_REGEX="$(flow_escape_regex "${PR_WORKTREE_BRANCH_PREFIX}")"
23
-
24
- cleanup="false"
25
- strict="false"
26
-
27
- usage() {
28
- cat <<'EOF'
29
- Usage:
30
- audit-agent-worktrees.sh [--cleanup] [--strict]
31
-
32
- Audits agent-managed worktrees attached to the anchor repo and removes stale
33
- ones when they no longer have a running owner and only contain generated
34
- artifacts such as node_modules symlinks or .openclaw-artifacts.
35
-
36
- Options:
37
- --cleanup Remove clean/orphaned agent worktrees automatically.
38
- --strict Exit non-zero when any stale agent worktree is found.
39
- EOF
40
- }
41
-
42
- while [[ $# -gt 0 ]]; do
43
- case "$1" in
44
- --cleanup) cleanup="true"; shift ;;
45
- --strict) strict="true"; shift ;;
46
- --help|-h) usage; exit 0 ;;
47
- *) echo "Unknown argument: $1" >&2; usage >&2; exit 1 ;;
48
- esac
49
- done
50
-
51
- if [[ ! -d "$AGENT_REPO_ROOT/.git" && ! -f "$AGENT_REPO_ROOT/.git" ]]; then
52
- echo "agent repo root is not a Git checkout: $AGENT_REPO_ROOT" >&2
53
- exit 1
54
- fi
55
-
56
- session_for_worktree() {
57
- local worktree_path="${1:-}"
58
- local branch_ref="${2:-}"
59
- local base
60
-
61
- base="$(basename "$worktree_path")"
62
- if [[ "$base" =~ ^pr-([0-9]+)- ]]; then
63
- printf '%s%s\n' "${PR_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
64
- return 0
65
- fi
66
- if [[ "$base" =~ ^issue-([0-9]+)- ]]; then
67
- printf '%s%s\n' "${ISSUE_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
68
- return 0
69
- fi
70
- if [[ "$branch_ref" =~ ^refs/heads/${PR_WORKTREE_BRANCH_PREFIX_REGEX}-([0-9]+)- ]]; then
71
- printf '%s%s\n' "${PR_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
72
- return 0
73
- fi
74
- if [[ "$branch_ref" =~ ^refs/heads/${ISSUE_BRANCH_PREFIX_REGEX}-([0-9]+)- ]]; then
75
- printf '%s%s\n' "${ISSUE_SESSION_PREFIX}" "${BASH_REMATCH[1]}"
76
- return 0
77
- fi
78
- return 1
79
- }
80
-
81
- branch_name_is_managed() {
82
- local branch_name="${1:-}"
83
- local branch_glob=""
84
-
85
- for branch_glob in ${MANAGED_PR_BRANCH_GLOBS}; do
86
- case "$branch_name" in
87
- ${branch_glob}) return 0 ;;
88
- esac
89
- done
90
-
91
- return 1
92
- }
93
-
94
- is_agent_managed_worktree() {
95
- local worktree_path="${1:-}"
96
- local branch_ref="${2:-}"
97
- local branch_name=""
98
-
99
- [[ "$worktree_path" == "$WORKTREE_ROOT/"* ]] || return 1
100
- if [[ "$branch_ref" == refs/heads/* ]]; then
101
- branch_name="${branch_ref#refs/heads/}"
102
- if branch_name_is_managed "$branch_name"; then
103
- return 0
104
- fi
105
- fi
106
- case "$(basename "$worktree_path")" in
107
- pr-*|issue-*|shared-*) return 0 ;;
108
- esac
109
- return 1
110
- }
111
-
112
- worktree_effectively_dirty() {
113
- local worktree_path="${1:?worktree path required}"
114
- local filtered=""
115
-
116
- if ! git -C "$worktree_path" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
117
- return 1
118
- fi
119
-
120
- filtered="$(
121
- git -C "$worktree_path" status --short --untracked-files=normal \
122
- | awk '
123
- /^\?\? node_modules$/ {next}
124
- /^\?\? .+\/node_modules$/ {next}
125
- /^\?\? \.openclaw-artifacts$/ {next}
126
- /^\?\? \.openclaw-artifacts\// {next}
127
- {print}
128
- '
129
- )"
130
- [[ -n "$filtered" ]]
131
- }
132
-
133
- worktree_is_broken() {
134
- local worktree_path="${1:?worktree path required}"
135
- [[ -d "$worktree_path" ]] || return 0
136
- git -C "$worktree_path" rev-parse --is-inside-work-tree >/dev/null 2>&1 && return 1
137
- return 0
138
- }
139
-
140
- worktree_has_active_owner() {
141
- local session_name="${1:-}"
142
- local run_dir=""
143
- local status_output=""
144
- local status=""
145
-
146
- [[ -n "$session_name" ]] || return 1
147
-
148
- if tmux has-session -t "$session_name" 2>/dev/null; then
149
- return 0
150
- fi
151
-
152
- run_dir="${RUNS_ROOT}/${session_name}"
153
- if [[ ! -d "$run_dir" ]]; then
154
- return 1
155
- fi
156
-
157
- # Completed workers still own their worktree until host-side reconcile
158
- # archives the run or writes reconciled.ok. Otherwise audit can delete the
159
- # worktree before publish/retry transitions consume it.
160
- if [[ ! -f "${run_dir}/reconciled.ok" ]]; then
161
- return 0
162
- fi
163
-
164
- status_output="$(
165
- "$WORKER_STATUS_TOOL" \
166
- --runs-root "$RUNS_ROOT" \
167
- --session "$session_name" 2>/dev/null || true
168
- )"
169
- status="$(awk -F= '/^STATUS=/{print $2}' <<<"$status_output" | tail -n 1)"
170
- [[ "$status" == "RUNNING" ]]
171
- }
172
-
173
- worktree_has_active_launch() {
174
- local session_name="${1:-}"
175
- local pending_file=""
176
- local pending_pid=""
177
-
178
- [[ -n "$session_name" ]] || return 1
179
-
180
- case "$session_name" in
181
- "${ISSUE_SESSION_PREFIX}"*)
182
- pending_file="${PENDING_LAUNCH_DIR}/issue-${session_name#${ISSUE_SESSION_PREFIX}}.pid"
183
- ;;
184
- "${PR_SESSION_PREFIX}"*)
185
- pending_file="${PENDING_LAUNCH_DIR}/pr-${session_name#${PR_SESSION_PREFIX}}.pid"
186
- ;;
187
- *)
188
- return 1
189
- ;;
190
- esac
191
-
192
- [[ -f "$pending_file" ]] || return 1
193
- pending_pid="$(tr -d '[:space:]' <"$pending_file" 2>/dev/null || true)"
194
- [[ -n "$pending_pid" ]] || return 1
195
- kill -0 "$pending_pid" 2>/dev/null
196
- }
197
-
198
- remove_agent_worktree() {
199
- local worktree_path="${1:-}"
200
- local branch_ref="${2:-}"
201
- local branch_name=""
202
- local cleanup_args=()
203
- local cleanup_failed="false"
204
-
205
- if [[ "$branch_ref" == refs/heads/* ]]; then
206
- branch_name="${branch_ref#refs/heads/}"
207
- cleanup_args=(--branch "$branch_name" --path "$worktree_path" --allow-unmerged)
208
- if [[ "$branch_name" == "${ISSUE_BRANCH_PREFIX}"-* ]]; then
209
- cleanup_args+=(--keep-remote)
210
- fi
211
- if ! (
212
- cd "$AGENT_REPO_ROOT"
213
- "$WORKTREE_CLEANUP_TOOL" "${cleanup_args[@]}"
214
- ) >/dev/null 2>&1; then
215
- cleanup_failed="true"
216
- fi
217
- else
218
- git -C "$AGENT_REPO_ROOT" worktree remove "$worktree_path" --force || true
219
- git -C "$AGENT_REPO_ROOT" worktree prune
220
- return 0
221
- fi
222
-
223
- if [[ "$cleanup_failed" == "true" ]]; then
224
- rm -rf "$worktree_path"
225
- if [[ -n "$branch_name" ]]; then
226
- git -C "$AGENT_REPO_ROOT" branch -D "$branch_name" >/dev/null 2>&1 || true
227
- fi
228
- fi
229
-
230
- git -C "$AGENT_REPO_ROOT" worktree prune >/dev/null 2>&1 || true
231
- }
232
-
233
- issue_count=0
234
- cleaned_count=0
235
-
236
- current_worktree=""
237
- current_branch_ref=""
238
- current_head=""
239
-
240
- while IFS= read -r line || [[ -n "$line" ]]; do
241
- if [[ -z "$line" ]]; then
242
- if [[ -n "$current_worktree" ]]; then
243
- if [[ "$current_worktree" != "$AGENT_REPO_ROOT" ]] && is_agent_managed_worktree "$current_worktree" "$current_branch_ref"; then
244
- session_name="$(session_for_worktree "$current_worktree" "$current_branch_ref" || true)"
245
- active_owner="no"
246
- if worktree_has_active_owner "$session_name"; then
247
- active_owner="yes"
248
- fi
249
- active_launch="no"
250
- if worktree_has_active_launch "$session_name"; then
251
- active_launch="yes"
252
- fi
253
-
254
- if [[ "$active_owner" == "yes" || "$active_launch" == "yes" ]]; then
255
- current_worktree=""
256
- current_branch_ref=""
257
- current_head=""
258
- continue
259
- fi
260
-
261
- broken_worktree="no"
262
- if worktree_is_broken "$current_worktree"; then
263
- broken_worktree="yes"
264
- fi
265
- dirty="no"
266
- if [[ "$broken_worktree" == "no" ]] && worktree_effectively_dirty "$current_worktree"; then
267
- dirty="yes"
268
- fi
269
-
270
- issue_count=$((issue_count + 1))
271
-
272
- printf 'AGENT_WORKTREE=%s\n' "$current_worktree"
273
- printf 'BRANCH_REF=%s\n' "${current_branch_ref:-<detached>}"
274
- printf 'HEAD=%s\n' "${current_head:-<unknown>}"
275
- printf 'SESSION=%s\n' "${session_name:-<none>}"
276
- printf 'BROKEN_WORKTREE=%s\n' "$broken_worktree"
277
- printf 'DIRTY=%s\n' "$dirty"
278
- printf 'ACTIVE_OWNER=%s\n' "$active_owner"
279
- printf 'ACTIVE_LAUNCH=%s\n' "$active_launch"
280
-
281
- if [[ "$cleanup" == "true" && "$dirty" == "no" && "$active_owner" == "no" && "$active_launch" == "no" ]]; then
282
- remove_agent_worktree "$current_worktree" "$current_branch_ref"
283
- cleaned_count=$((cleaned_count + 1))
284
- printf 'CLEANUP=removed\n'
285
- else
286
- printf 'CLEANUP=skipped\n'
287
- fi
288
- printf '\n'
289
- fi
290
-
291
- current_worktree=""
292
- current_branch_ref=""
293
- current_head=""
294
- fi
295
- continue
296
- fi
297
-
298
- case "$line" in
299
- worktree\ *) current_worktree="${line#worktree }" ;;
300
- HEAD\ *) current_head="${line#HEAD }" ;;
301
- branch\ *) current_branch_ref="${line#branch }" ;;
302
- esac
303
- done < <(git -C "$AGENT_REPO_ROOT" worktree list --porcelain; printf '\n')
304
-
305
- printf 'LEGACY_AGENT_WORKTREE_COUNT=%s\n' "$issue_count"
306
- printf 'LEGACY_AGENT_WORKTREE_CLEANED=%s\n' "$cleaned_count"
307
-
308
- if [[ "$strict" == "true" && "$issue_count" -gt 0 ]]; then
309
- exit 2
310
- fi
@@ -1,11 +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-shell-lib.sh"
7
-
8
- FLOW_SKILL_DIR="$(resolve_flow_skill_dir "${BASH_SOURCE[0]}")"
9
- ADAPTER_BIN_DIR="${FLOW_SKILL_DIR}/bin"
10
-
11
- exec "${ADAPTER_BIN_DIR}/audit-issue-routing.sh" "$@"
@@ -1,58 +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
- ADAPTER_ID="$(flow_resolve_adapter_id "${CONFIG_YAML}")"
11
-
12
- classify_worktree() {
13
- local path="${1:-}"
14
- case "$path" in
15
- */${ADAPTER_ID}-pr-*|*/${ADAPTER_ID}-issue-*|*/${ADAPTER_ID}-main-clean)
16
- printf 'legacy-automation\n'
17
- ;;
18
- "$RETAINED_ROOT")
19
- printf 'retained-main\n'
20
- ;;
21
- *)
22
- printf 'retained-manual\n'
23
- ;;
24
- esac
25
- }
26
-
27
- count_dirty_paths() {
28
- local path="${1:?path required}"
29
- git -C "$path" status --porcelain 2>/dev/null | wc -l | tr -d ' '
30
- }
31
-
32
- current_branch() {
33
- local path="${1:?path required}"
34
- git -C "$path" rev-parse --abbrev-ref HEAD 2>/dev/null || printf 'unknown\n'
35
- }
36
-
37
- current_head() {
38
- local path="${1:?path required}"
39
- git -C "$path" rev-parse --short HEAD 2>/dev/null || printf 'unknown\n'
40
- }
41
-
42
- worktree_list="$(
43
- git -C "$RETAINED_ROOT" worktree list --porcelain |
44
- awk '/^worktree /{print substr($0,10)}'
45
- )"
46
-
47
- printf 'RETAINED_ROOT=%s\n' "$RETAINED_ROOT"
48
- printf 'RETAINED_WORKTREE_COUNT=%s\n' "$(printf '%s\n' "$worktree_list" | sed '/^$/d' | wc -l | tr -d ' ')"
49
-
50
- while IFS= read -r worktree; do
51
- [[ -n "$worktree" ]] || continue
52
- printf '\n[worktree]\n'
53
- printf 'path=%s\n' "$worktree"
54
- printf 'class=%s\n' "$(classify_worktree "$worktree")"
55
- printf 'branch=%s\n' "$(current_branch "$worktree")"
56
- printf 'head=%s\n' "$(current_head "$worktree")"
57
- printf 'dirty_paths=%s\n' "$(count_dirty_paths "$worktree")"
58
- done <<<"$worktree_list"
@@ -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"