@reddb-io/red-skills 4.4.1 → 4.5.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/bin/build-info.mjs +87 -0
- package/bin/cli-frontdoor.mjs +14 -0
- package/bin/red-skills-brain.mjs +17 -22
- package/bin/red-skills-content.mjs +17 -0
- package/bin/red-skills-hook.mjs +45 -0
- package/bin/red-skills-memory.mjs +17 -22
- package/bin/red-skills-redskilled-mcp.mjs +2 -0
- package/bin/red-skills-resource.mjs +21 -0
- package/bin/runtime-compatibility.mjs +17 -0
- package/bin/runtime-paths.mjs +13 -0
- package/dist/code-nav.bundle.min.mjs +1 -1
- package/dist/opencode-host.bundle.min.mjs +1 -1
- package/dist/redskilled-mcp.bundle.min.mjs +36 -36
- package/dist/redskilled.bundle.min.mjs +1 -1
- package/dist/release.bundle.min.mjs +1 -1
- package/dist/rsp-core.bundle.min.mjs +1 -1
- package/dist/rsp.bundle.min.mjs +1 -1
- package/dist/statusline.bundle.min.mjs +1 -1
- package/package.json +9 -4
- package/runtime/hook-routes.json +125 -0
- package/runtime/plugins/brain/scripts/bootstrap.mjs +656 -0
- package/runtime/plugins/brain/scripts/structural-smoke.sh +7 -0
- package/runtime/plugins/dev/hooks/branch-lock-codex.sh +105 -0
- package/runtime/plugins/dev/hooks/code-nav-mcp.sh +63 -0
- package/runtime/plugins/dev/hooks/command-guard.sh +893 -0
- package/runtime/plugins/dev/hooks/ensure-codex-statusline.mjs +144 -0
- package/runtime/plugins/dev/hooks/red-fetch.mjs +38 -0
- package/runtime/plugins/dev/hooks/redskilled-mcp.sh +64 -0
- package/runtime/plugins/dev/hooks/rsp-hook.sh +343 -0
- package/runtime/plugins/dev/hooks/tests/command-guard.test.sh +378 -0
- package/runtime/plugins/dev/hooks/tests/rsp-hook.test.sh +229 -0
- package/runtime/plugins/dev/scripts/memory-bridge.sh +230 -0
- package/runtime/plugins/dev/scripts/structural-smoke.sh +7 -0
- package/runtime/plugins/dev/scripts/tests/memory-bridge.test.sh +555 -0
- package/runtime/plugins/dev/skills/engineering/afk/defaults/cargo-pre-worktree.sh +37 -0
- package/runtime/plugins/dev/skills/engineering/afk/defaults/envelope-post-attempt.sh +47 -0
- package/runtime/plugins/dev/skills/engineering/afk/defaults/gradle-pre-worktree.sh +45 -0
- package/runtime/plugins/dev/skills/engineering/afk/defaults/heartbeat-post-attempt.sh +43 -0
- package/runtime/plugins/dev/skills/engineering/afk/defaults/validation-post-merge.sh +77 -0
- package/runtime/plugins/dev/skills/engineering/afk/detectors/cargo.sh +27 -0
- package/runtime/plugins/dev/skills/engineering/afk/detectors/gradle.sh +38 -0
- package/runtime/plugins/dev/skills/engineering/afk/examples/only-mine.sh +32 -0
- package/runtime/plugins/dev/skills/engineering/afk/examples/red-afk-attempt-action.yml +53 -0
- package/runtime/plugins/dev/skills/engineering/afk/examples/rs-afk-attempt.yml +76 -0
- package/runtime/plugins/dev/skills/engineering/afk/hooks/red-cargo +38 -0
- package/runtime/plugins/dev/skills/engineering/afk/hooks/red-envelope +50 -0
- package/runtime/plugins/dev/skills/engineering/afk/hooks/red-gradle +42 -0
- package/runtime/plugins/dev/skills/engineering/afk/hooks/red-heartbeat +46 -0
- package/runtime/plugins/dev/skills/engineering/afk/hooks/red-validation +79 -0
- package/runtime/plugins/dev/skills/engineering/diagnose/scripts/hitl-loop.template.sh +41 -0
- package/runtime/plugins/dev/skills/engineering/red-setup/scripts/install-runtime-shim.sh +181 -0
- package/runtime/plugins/dev/skills/engineering/red-setup/workflows/red-issues-needs-triage.yml +36 -0
- package/runtime/plugins/dev/skills/engineering/wizard/template.sh +211 -0
- package/runtime/plugins/dev/skills/engineering/zoom-out/scripts/graph-mode-plumbing.sh +36 -0
- package/runtime/plugins/dev/skills/engineering/zoom-out/scripts/tests/contract.test.sh +168 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/branch-lock-hook.sh +113 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/branch-lock-session-start.sh +81 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/branch-lock.sh +68 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/lib/dev-config.sh +126 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/lib/git-command-classifier.sh +198 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/lib/lock-store.sh +67 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/lib/scope-resolver.sh +43 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/branch-lock-cli.test.sh +114 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/claude-plugin-hook.test.sh +117 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/codex-hook.test.sh +230 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/dev-config.test.sh +99 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/git-command-classifier.test.sh +187 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/lock-store.test.sh +90 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/scope-resolver.test.sh +63 -0
- package/runtime/plugins/dev/skills/misc/branch-lock/scripts/tests/session-start.test.sh +123 -0
- package/runtime/plugins/dev/skills/misc/git-guardrails-claude-code/scripts/block-dangerous-git.sh +231 -0
- package/runtime/plugins/dev/skills/misc/git-guardrails-claude-code/scripts/tests/block-dangerous-git.test.sh +143 -0
- package/runtime/plugins/internal/scripts/structural-smoke.sh +7 -0
- package/runtime/plugins/internal/skills/maintainer/create-plugin/scripts/create-plugin.sh +308 -0
- package/runtime/plugins/memory/scripts/bootstrap.mjs +659 -0
- package/runtime/plugins/memory/scripts/structural-smoke.sh +7 -0
- package/scripts/check-skills-boundary.mjs +32 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Tests for plugins/dev/scripts/memory-bridge.sh (issue #57, PRD #49).
|
|
3
|
+
#
|
|
4
|
+
# The bridge is dev's SOFT, one-directional hook into the memory plugin. The
|
|
5
|
+
# acceptance criteria it must satisfy:
|
|
6
|
+
# - memory installed + initialized → recall runs and its output is returned;
|
|
7
|
+
# - graph memory with neighbors/path → focused graph reads are returned;
|
|
8
|
+
# - memory absent / uninitialized → behaves exactly as before: silent no-op,
|
|
9
|
+
# exit 0, no output, no error;
|
|
10
|
+
# - detection has two gates (config opt-in AND a resolvable CLI), and the CLI
|
|
11
|
+
# resolution cascade prefers $RED_MEMORY_CLI, then a `memory` bin on PATH,
|
|
12
|
+
# then a built sibling/in-repo dist.
|
|
13
|
+
|
|
14
|
+
set -uo pipefail
|
|
15
|
+
|
|
16
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
17
|
+
SCRIPTS="$(dirname "$HERE")"
|
|
18
|
+
LIB="$SCRIPTS/memory-bridge.sh"
|
|
19
|
+
|
|
20
|
+
# shellcheck source=../memory-bridge.sh
|
|
21
|
+
source "$LIB"
|
|
22
|
+
|
|
23
|
+
pass=0
|
|
24
|
+
fail=0
|
|
25
|
+
|
|
26
|
+
expect_eq() {
|
|
27
|
+
local label="$1" expected="$2" actual="$3"
|
|
28
|
+
if [[ "$expected" == "$actual" ]]; then
|
|
29
|
+
echo "PASS $label"
|
|
30
|
+
pass=$((pass + 1))
|
|
31
|
+
else
|
|
32
|
+
printf 'FAIL %s\n expected: %q\n actual: %q\n' "$label" "$expected" "$actual"
|
|
33
|
+
fail=$((fail + 1))
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
expect_ok() {
|
|
38
|
+
local label="$1"; shift
|
|
39
|
+
if "$@"; then
|
|
40
|
+
echo "PASS $label"
|
|
41
|
+
pass=$((pass + 1))
|
|
42
|
+
else
|
|
43
|
+
echo "FAIL $label (command failed: $*)"
|
|
44
|
+
fail=$((fail + 1))
|
|
45
|
+
fi
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
expect_fail() {
|
|
49
|
+
local label="$1"; shift
|
|
50
|
+
if "$@"; then
|
|
51
|
+
echo "FAIL $label (expected non-zero, got success)"
|
|
52
|
+
fail=$((fail + 1))
|
|
53
|
+
else
|
|
54
|
+
echo "PASS $label"
|
|
55
|
+
pass=$((pass + 1))
|
|
56
|
+
fi
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# ---------- fixtures ----------
|
|
60
|
+
|
|
61
|
+
TMP="$(mktemp -d)"
|
|
62
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
63
|
+
|
|
64
|
+
# A repo that has opted into memory (config present).
|
|
65
|
+
INIT_ROOT="$TMP/init-repo"
|
|
66
|
+
mkdir -p "$INIT_ROOT/.red/memory"
|
|
67
|
+
echo '{"version":1,"mode":"markdown-only"}' > "$INIT_ROOT/.red/memory/config.json"
|
|
68
|
+
|
|
69
|
+
GRAPH_ROOT="$TMP/graph-repo"
|
|
70
|
+
mkdir -p "$GRAPH_ROOT/.red/memory"
|
|
71
|
+
echo '{"version":1,"mode":"graph","storePath":".red/memory/graph.rdb"}' > "$GRAPH_ROOT/.red/memory/config.json"
|
|
72
|
+
|
|
73
|
+
# A repo that never ran memory init (no config).
|
|
74
|
+
BARE_ROOT="$TMP/bare-repo"
|
|
75
|
+
mkdir -p "$BARE_ROOT"
|
|
76
|
+
|
|
77
|
+
# A fake `memory` CLI on PATH that echoes a recognisable block and the query it got.
|
|
78
|
+
FAKE_BIN="$TMP/bin"
|
|
79
|
+
mkdir -p "$FAKE_BIN"
|
|
80
|
+
cat > "$FAKE_BIN/memory" <<'EOF'
|
|
81
|
+
#!/usr/bin/env bash
|
|
82
|
+
case "$1" in
|
|
83
|
+
recall)
|
|
84
|
+
shift # drop the "recall" verb
|
|
85
|
+
echo "memory: 1 match for \"$*\""
|
|
86
|
+
echo " [1.0] note-1"
|
|
87
|
+
;;
|
|
88
|
+
stats)
|
|
89
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
90
|
+
;;
|
|
91
|
+
neighbors)
|
|
92
|
+
label="$2"
|
|
93
|
+
echo "memory: 2 neighbor(s) of \"$label\""
|
|
94
|
+
echo " d1 node-auth (module) auth-service"
|
|
95
|
+
echo " d1 node-cache (concept) cache-ttl"
|
|
96
|
+
;;
|
|
97
|
+
path)
|
|
98
|
+
from="$2"
|
|
99
|
+
to="$3"
|
|
100
|
+
echo "memory: path \"$from\" -> \"$to\": 2 hop(s), weight 2"
|
|
101
|
+
;;
|
|
102
|
+
structural-impact)
|
|
103
|
+
file=""
|
|
104
|
+
while [ "$#" -gt 0 ]; do
|
|
105
|
+
if [ "$1" = "--file" ]; then file="$2"; fi
|
|
106
|
+
shift || true
|
|
107
|
+
done
|
|
108
|
+
echo "memory: structural impact for file $file"
|
|
109
|
+
echo " /repo/src/app.ts imports node:path"
|
|
110
|
+
echo " /repo/src/consumer.ts imports this target through ./app.js"
|
|
111
|
+
;;
|
|
112
|
+
attempt)
|
|
113
|
+
if [[ "$2" != "record" ]]; then
|
|
114
|
+
echo "unexpected attempt command: $*" >&2
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
cat > "${FAKE_ATTEMPT_OUT:?}"
|
|
118
|
+
echo "memory: recorded attempt"
|
|
119
|
+
;;
|
|
120
|
+
*)
|
|
121
|
+
echo "unexpected command: $*" >&2
|
|
122
|
+
exit 1
|
|
123
|
+
;;
|
|
124
|
+
esac
|
|
125
|
+
EOF
|
|
126
|
+
chmod +x "$FAKE_BIN/memory"
|
|
127
|
+
|
|
128
|
+
# A clean environment helper: clear every override the bridge consults.
|
|
129
|
+
clear_env() { unset RED_MEMORY_CLI CLAUDE_PLUGIN_ROOT MEMORY_REPO_ROOT; }
|
|
130
|
+
|
|
131
|
+
# ---------- _memory_resolve_cli cascade ----------
|
|
132
|
+
|
|
133
|
+
# RED_MEMORY_CLI override (does not need node to *resolve*).
|
|
134
|
+
clear_env
|
|
135
|
+
override="$TMP/custom-cli.js"
|
|
136
|
+
echo '// fake' > "$override"
|
|
137
|
+
RED_MEMORY_CLI="$override" _memory_resolve_cli
|
|
138
|
+
expect_eq "resolve: \$RED_MEMORY_CLI -> node <path>" "node $override" "${MEMORY_CLI[*]}"
|
|
139
|
+
|
|
140
|
+
# RED_MEMORY_CLI set but file missing → no resolution.
|
|
141
|
+
clear_env
|
|
142
|
+
expect_fail "resolve: \$RED_MEMORY_CLI missing file fails" \
|
|
143
|
+
bash -c 'source "'"$LIB"'"; RED_MEMORY_CLI=/nope/cli.js _memory_resolve_cli'
|
|
144
|
+
|
|
145
|
+
# `memory` on PATH.
|
|
146
|
+
clear_env
|
|
147
|
+
PATH="$FAKE_BIN:$PATH" _memory_resolve_cli
|
|
148
|
+
expect_eq "resolve: memory on PATH -> (memory)" "memory" "${MEMORY_CLI[*]}"
|
|
149
|
+
|
|
150
|
+
# in-repo dist via MEMORY_REPO_ROOT.
|
|
151
|
+
clear_env
|
|
152
|
+
REPO="$TMP/monorepo"
|
|
153
|
+
mkdir -p "$REPO/plugins/memory/dist"
|
|
154
|
+
echo '// fake' > "$REPO/plugins/memory/dist/cli.js"
|
|
155
|
+
MEMORY_REPO_ROOT="$REPO" _memory_resolve_cli
|
|
156
|
+
expect_eq "resolve: MEMORY_REPO_ROOT in-repo dist -> node <path>" \
|
|
157
|
+
"node $REPO/plugins/memory/dist/cli.js" "${MEMORY_CLI[*]}"
|
|
158
|
+
|
|
159
|
+
# sibling dist via CLAUDE_PLUGIN_ROOT (dev plugin root; memory is its sibling).
|
|
160
|
+
clear_env
|
|
161
|
+
DEVROOT="$TMP/plugins/dev"
|
|
162
|
+
mkdir -p "$DEVROOT" "$TMP/plugins/memory/dist"
|
|
163
|
+
echo '// fake' > "$TMP/plugins/memory/dist/cli.js"
|
|
164
|
+
CLAUDE_PLUGIN_ROOT="$DEVROOT" _memory_resolve_cli
|
|
165
|
+
expect_eq "resolve: CLAUDE_PLUGIN_ROOT sibling dist -> node <path>" \
|
|
166
|
+
"node $DEVROOT/../memory/dist/cli.js" "${MEMORY_CLI[*]}"
|
|
167
|
+
|
|
168
|
+
# nothing resolvable (empty env, no PATH bin) → fails, MEMORY_CLI empty.
|
|
169
|
+
clear_env
|
|
170
|
+
expect_fail "resolve: nothing available fails" \
|
|
171
|
+
bash -c 'source "'"$LIB"'"; PATH=/usr/bin:/bin _memory_resolve_cli'
|
|
172
|
+
|
|
173
|
+
# ---------- memory_available (two gates) ----------
|
|
174
|
+
|
|
175
|
+
clear_env
|
|
176
|
+
expect_fail "available: no config -> unavailable (even with CLI on PATH)" \
|
|
177
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_available "'"$BARE_ROOT"'"'
|
|
178
|
+
|
|
179
|
+
clear_env
|
|
180
|
+
expect_fail "available: config present but no CLI -> unavailable" \
|
|
181
|
+
bash -c 'source "'"$LIB"'"; PATH=/usr/bin:/bin memory_available "'"$INIT_ROOT"'"'
|
|
182
|
+
|
|
183
|
+
clear_env
|
|
184
|
+
expect_ok "available: config present AND CLI on PATH -> available" \
|
|
185
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_available "'"$INIT_ROOT"'"'
|
|
186
|
+
|
|
187
|
+
# ---------- Memory mode + graph readiness ----------
|
|
188
|
+
|
|
189
|
+
clear_env
|
|
190
|
+
expect_eq "mode: absent config -> unavailable" "unavailable" \
|
|
191
|
+
"$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_mode "'"$BARE_ROOT"'"')"
|
|
192
|
+
|
|
193
|
+
clear_env
|
|
194
|
+
expect_eq "mode: markdown-only config -> markdown-only" "markdown-only" \
|
|
195
|
+
"$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_mode "'"$INIT_ROOT"'"')"
|
|
196
|
+
|
|
197
|
+
clear_env
|
|
198
|
+
expect_eq "mode: graph config -> graph" "graph" \
|
|
199
|
+
"$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_mode "'"$GRAPH_ROOT"'"')"
|
|
200
|
+
|
|
201
|
+
clear_env
|
|
202
|
+
expect_ok "graph-ready: graph config + stats with nodes -> ready" \
|
|
203
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_graph_ready "'"$GRAPH_ROOT"'"'
|
|
204
|
+
|
|
205
|
+
clear_env
|
|
206
|
+
expect_fail "graph-ready: absent config -> not ready" \
|
|
207
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_graph_ready "'"$BARE_ROOT"'"'
|
|
208
|
+
|
|
209
|
+
clear_env
|
|
210
|
+
expect_fail "graph-ready: markdown-only config -> not ready" \
|
|
211
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_graph_ready "'"$INIT_ROOT"'"'
|
|
212
|
+
|
|
213
|
+
clear_env
|
|
214
|
+
EMPTYBIN="$TMP/emptybin"
|
|
215
|
+
mkdir -p "$EMPTYBIN"
|
|
216
|
+
cat > "$EMPTYBIN/memory" <<'EOF'
|
|
217
|
+
#!/usr/bin/env bash
|
|
218
|
+
if [[ "$1" == "stats" ]]; then
|
|
219
|
+
echo "memory: 0 node(s), 0 edge(s)"
|
|
220
|
+
else
|
|
221
|
+
exit 1
|
|
222
|
+
fi
|
|
223
|
+
EOF
|
|
224
|
+
chmod +x "$EMPTYBIN/memory"
|
|
225
|
+
expect_fail "graph-ready: graph stats with zero nodes -> not ready" \
|
|
226
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$EMPTYBIN"':/usr/bin:/bin" memory_graph_ready "'"$GRAPH_ROOT"'"'
|
|
227
|
+
|
|
228
|
+
clear_env
|
|
229
|
+
STATERRBIN="$TMP/staterrbin"
|
|
230
|
+
mkdir -p "$STATERRBIN"
|
|
231
|
+
cat > "$STATERRBIN/memory" <<'EOF'
|
|
232
|
+
#!/usr/bin/env bash
|
|
233
|
+
if [[ "$1" == "stats" ]]; then
|
|
234
|
+
echo "stats boom" >&2
|
|
235
|
+
exit 1
|
|
236
|
+
fi
|
|
237
|
+
exit 1
|
|
238
|
+
EOF
|
|
239
|
+
chmod +x "$STATERRBIN/memory"
|
|
240
|
+
out="$(PATH="$STATERRBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_graph_ready "'"$GRAPH_ROOT"'"' 2>/dev/null)"
|
|
241
|
+
expect_eq "graph-ready: failing stats command -> no stdout" "" "$out"
|
|
242
|
+
expect_fail "graph-ready: failing stats command -> not ready" \
|
|
243
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$STATERRBIN"':/usr/bin:/bin" memory_graph_ready "'"$GRAPH_ROOT"'"'
|
|
244
|
+
|
|
245
|
+
# ---------- memory_neighbors (graph-only, graceful) ----------
|
|
246
|
+
|
|
247
|
+
clear_env
|
|
248
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_neighbors "'"$GRAPH_ROOT"'" zoom-out 1 both')"
|
|
249
|
+
expect_eq "neighbors: graph ready -> returns CLI output" \
|
|
250
|
+
$'memory: 2 neighbor(s) of "zoom-out"\n d1 node-auth (module) auth-service\n d1 node-cache (concept) cache-ttl' "$out"
|
|
251
|
+
clear_env
|
|
252
|
+
expect_ok "neighbors: graph ready -> exit 0" \
|
|
253
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_neighbors "'"$GRAPH_ROOT"'" zoom-out 1 both'
|
|
254
|
+
|
|
255
|
+
clear_env
|
|
256
|
+
out="$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_neighbors "'"$GRAPH_ROOT"'" zoom-out 1 both')"
|
|
257
|
+
expect_eq "neighbors: graph config but no CLI -> no output" "" "$out"
|
|
258
|
+
clear_env
|
|
259
|
+
expect_ok "neighbors: graph config but no CLI -> exit 0" \
|
|
260
|
+
bash -c 'source "'"$LIB"'"; PATH=/usr/bin:/bin memory_neighbors "'"$GRAPH_ROOT"'" zoom-out 1 both'
|
|
261
|
+
|
|
262
|
+
clear_env
|
|
263
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_neighbors "'"$INIT_ROOT"'" zoom-out 1 both')"
|
|
264
|
+
expect_eq "neighbors: markdown-only config -> no output" "" "$out"
|
|
265
|
+
|
|
266
|
+
clear_env
|
|
267
|
+
ZERONEIGHBORBIN="$TMP/zeroneighborbin"
|
|
268
|
+
mkdir -p "$ZERONEIGHBORBIN"
|
|
269
|
+
cat > "$ZERONEIGHBORBIN/memory" <<'EOF'
|
|
270
|
+
#!/usr/bin/env bash
|
|
271
|
+
case "$1" in
|
|
272
|
+
stats)
|
|
273
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
274
|
+
;;
|
|
275
|
+
neighbors)
|
|
276
|
+
echo "memory: 0 neighbor(s) of \"$2\""
|
|
277
|
+
;;
|
|
278
|
+
*)
|
|
279
|
+
exit 1
|
|
280
|
+
;;
|
|
281
|
+
esac
|
|
282
|
+
EOF
|
|
283
|
+
chmod +x "$ZERONEIGHBORBIN/memory"
|
|
284
|
+
out="$(PATH="$ZERONEIGHBORBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_neighbors "'"$GRAPH_ROOT"'" zoom-out 1 both')"
|
|
285
|
+
expect_eq "neighbors: zero neighbors -> no output" "" "$out"
|
|
286
|
+
|
|
287
|
+
clear_env
|
|
288
|
+
NEIGHBORERRBIN="$TMP/neighborerrbin"
|
|
289
|
+
mkdir -p "$NEIGHBORERRBIN"
|
|
290
|
+
cat > "$NEIGHBORERRBIN/memory" <<'EOF'
|
|
291
|
+
#!/usr/bin/env bash
|
|
292
|
+
case "$1" in
|
|
293
|
+
stats)
|
|
294
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
295
|
+
;;
|
|
296
|
+
neighbors)
|
|
297
|
+
echo "neighbors boom" >&2
|
|
298
|
+
exit 1
|
|
299
|
+
;;
|
|
300
|
+
*)
|
|
301
|
+
exit 1
|
|
302
|
+
;;
|
|
303
|
+
esac
|
|
304
|
+
EOF
|
|
305
|
+
chmod +x "$NEIGHBORERRBIN/memory"
|
|
306
|
+
out="$(PATH="$NEIGHBORERRBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_neighbors "'"$GRAPH_ROOT"'" zoom-out 1 both' 2>/dev/null)"
|
|
307
|
+
expect_eq "neighbors: failing command -> swallowed, no stdout" "" "$out"
|
|
308
|
+
clear_env
|
|
309
|
+
expect_ok "neighbors: failing command -> still exit 0" \
|
|
310
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$NEIGHBORERRBIN"':/usr/bin:/bin" memory_neighbors "'"$GRAPH_ROOT"'" zoom-out 1 both'
|
|
311
|
+
|
|
312
|
+
# ---------- memory_structural_impact (graph-only, graceful) ----------
|
|
313
|
+
|
|
314
|
+
clear_env
|
|
315
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_structural_impact "'"$GRAPH_ROOT"'" /repo/src/app.ts render')"
|
|
316
|
+
expect_eq "structural-impact: graph ready -> returns CLI output" \
|
|
317
|
+
$'memory: structural impact for file /repo/src/app.ts\n /repo/src/app.ts imports node:path\n /repo/src/consumer.ts imports this target through ./app.js' "$out"
|
|
318
|
+
|
|
319
|
+
clear_env
|
|
320
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_structural_impact "'"$INIT_ROOT"'" /repo/src/app.ts render')"
|
|
321
|
+
expect_eq "structural-impact: markdown-only config -> no output" "" "$out"
|
|
322
|
+
|
|
323
|
+
clear_env
|
|
324
|
+
NOIMPACTBIN="$TMP/noimpactbin"
|
|
325
|
+
mkdir -p "$NOIMPACTBIN"
|
|
326
|
+
cat > "$NOIMPACTBIN/memory" <<'EOF'
|
|
327
|
+
#!/usr/bin/env bash
|
|
328
|
+
case "$1" in
|
|
329
|
+
stats)
|
|
330
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
331
|
+
;;
|
|
332
|
+
structural-impact)
|
|
333
|
+
file=""
|
|
334
|
+
while [ "$#" -gt 0 ]; do
|
|
335
|
+
if [ "$1" = "--file" ]; then file="$2"; fi
|
|
336
|
+
shift || true
|
|
337
|
+
done
|
|
338
|
+
echo "memory: no structural impact for file $file"
|
|
339
|
+
;;
|
|
340
|
+
*)
|
|
341
|
+
exit 1
|
|
342
|
+
;;
|
|
343
|
+
esac
|
|
344
|
+
EOF
|
|
345
|
+
chmod +x "$NOIMPACTBIN/memory"
|
|
346
|
+
out="$(PATH="$NOIMPACTBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_structural_impact "'"$GRAPH_ROOT"'" /repo/src/missing.ts missing')"
|
|
347
|
+
expect_eq "structural-impact: no graph data -> no output" "" "$out"
|
|
348
|
+
|
|
349
|
+
clear_env
|
|
350
|
+
IMPACTERRBIN="$TMP/impacterrbin"
|
|
351
|
+
mkdir -p "$IMPACTERRBIN"
|
|
352
|
+
cat > "$IMPACTERRBIN/memory" <<'EOF'
|
|
353
|
+
#!/usr/bin/env bash
|
|
354
|
+
case "$1" in
|
|
355
|
+
stats)
|
|
356
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
357
|
+
;;
|
|
358
|
+
structural-impact)
|
|
359
|
+
echo "impact boom" >&2
|
|
360
|
+
exit 1
|
|
361
|
+
;;
|
|
362
|
+
*)
|
|
363
|
+
exit 1
|
|
364
|
+
;;
|
|
365
|
+
esac
|
|
366
|
+
EOF
|
|
367
|
+
chmod +x "$IMPACTERRBIN/memory"
|
|
368
|
+
out="$(PATH="$IMPACTERRBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_structural_impact "'"$GRAPH_ROOT"'" /repo/src/app.ts render' 2>/dev/null)"
|
|
369
|
+
expect_eq "structural-impact: failing command -> swallowed, no stdout" "" "$out"
|
|
370
|
+
clear_env
|
|
371
|
+
expect_ok "structural-impact: failing command -> still exit 0" \
|
|
372
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$IMPACTERRBIN"':/usr/bin:/bin" memory_structural_impact "'"$GRAPH_ROOT"'" /repo/src/app.ts render'
|
|
373
|
+
|
|
374
|
+
# ---------- memory_path (graph-only, graceful) ----------
|
|
375
|
+
|
|
376
|
+
clear_env
|
|
377
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs')"
|
|
378
|
+
expect_eq "path: graph ready -> returns CLI output" \
|
|
379
|
+
'memory: path "zoom-out" -> "memory-bridge": 2 hop(s), weight 2' "$out"
|
|
380
|
+
clear_env
|
|
381
|
+
expect_ok "path: graph ready -> exit 0" \
|
|
382
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs'
|
|
383
|
+
|
|
384
|
+
clear_env
|
|
385
|
+
out="$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs')"
|
|
386
|
+
expect_eq "path: graph config but no CLI -> no output" "" "$out"
|
|
387
|
+
clear_env
|
|
388
|
+
expect_ok "path: graph config but no CLI -> exit 0" \
|
|
389
|
+
bash -c 'source "'"$LIB"'"; PATH=/usr/bin:/bin memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs'
|
|
390
|
+
|
|
391
|
+
clear_env
|
|
392
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_path "'"$INIT_ROOT"'" zoom-out memory-bridge bfs')"
|
|
393
|
+
expect_eq "path: markdown-only config -> no output" "" "$out"
|
|
394
|
+
|
|
395
|
+
clear_env
|
|
396
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_path "'"$GRAPH_ROOT"'" zoom-out')"
|
|
397
|
+
expect_eq "path: missing destination label -> no output" "" "$out"
|
|
398
|
+
|
|
399
|
+
clear_env
|
|
400
|
+
NOPATHBIN="$TMP/nopathbin"
|
|
401
|
+
mkdir -p "$NOPATHBIN"
|
|
402
|
+
cat > "$NOPATHBIN/memory" <<'EOF'
|
|
403
|
+
#!/usr/bin/env bash
|
|
404
|
+
case "$1" in
|
|
405
|
+
stats)
|
|
406
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
407
|
+
;;
|
|
408
|
+
path)
|
|
409
|
+
echo "memory: no path from \"$2\" to \"$3\""
|
|
410
|
+
;;
|
|
411
|
+
*)
|
|
412
|
+
exit 1
|
|
413
|
+
;;
|
|
414
|
+
esac
|
|
415
|
+
EOF
|
|
416
|
+
chmod +x "$NOPATHBIN/memory"
|
|
417
|
+
out="$(PATH="$NOPATHBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs')"
|
|
418
|
+
expect_eq "path: no path result -> no output" "" "$out"
|
|
419
|
+
|
|
420
|
+
clear_env
|
|
421
|
+
EMPTYPATHBIN="$TMP/emptypathbin"
|
|
422
|
+
mkdir -p "$EMPTYPATHBIN"
|
|
423
|
+
cat > "$EMPTYPATHBIN/memory" <<'EOF'
|
|
424
|
+
#!/usr/bin/env bash
|
|
425
|
+
case "$1" in
|
|
426
|
+
stats)
|
|
427
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
428
|
+
;;
|
|
429
|
+
path)
|
|
430
|
+
exit 0
|
|
431
|
+
;;
|
|
432
|
+
*)
|
|
433
|
+
exit 1
|
|
434
|
+
;;
|
|
435
|
+
esac
|
|
436
|
+
EOF
|
|
437
|
+
chmod +x "$EMPTYPATHBIN/memory"
|
|
438
|
+
out="$(PATH="$EMPTYPATHBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs')"
|
|
439
|
+
expect_eq "path: empty command output -> no output" "" "$out"
|
|
440
|
+
|
|
441
|
+
clear_env
|
|
442
|
+
PATHERRBIN="$TMP/patherrbin"
|
|
443
|
+
mkdir -p "$PATHERRBIN"
|
|
444
|
+
cat > "$PATHERRBIN/memory" <<'EOF'
|
|
445
|
+
#!/usr/bin/env bash
|
|
446
|
+
case "$1" in
|
|
447
|
+
stats)
|
|
448
|
+
echo "memory: 17 node(s), 3 edge(s)"
|
|
449
|
+
;;
|
|
450
|
+
path)
|
|
451
|
+
echo "path boom" >&2
|
|
452
|
+
exit 1
|
|
453
|
+
;;
|
|
454
|
+
*)
|
|
455
|
+
exit 1
|
|
456
|
+
;;
|
|
457
|
+
esac
|
|
458
|
+
EOF
|
|
459
|
+
chmod +x "$PATHERRBIN/memory"
|
|
460
|
+
out="$(PATH="$PATHERRBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs' 2>/dev/null)"
|
|
461
|
+
expect_eq "path: failing command -> swallowed, no stdout" "" "$out"
|
|
462
|
+
clear_env
|
|
463
|
+
expect_ok "path: failing command -> still exit 0" \
|
|
464
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$PATHERRBIN"':/usr/bin:/bin" memory_path "'"$GRAPH_ROOT"'" zoom-out memory-bridge bfs'
|
|
465
|
+
|
|
466
|
+
# ---------- memory_recall (graceful, query passthrough) ----------
|
|
467
|
+
|
|
468
|
+
# Absent memory: silent no-op, exit 0, no output.
|
|
469
|
+
clear_env
|
|
470
|
+
out="$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_recall "'"$BARE_ROOT"'" cache ttl')"
|
|
471
|
+
expect_eq "recall: absent memory -> no output" "" "$out"
|
|
472
|
+
clear_env
|
|
473
|
+
expect_ok "recall: absent memory -> exit 0" \
|
|
474
|
+
bash -c 'source "'"$LIB"'"; PATH=/usr/bin:/bin memory_recall "'"$BARE_ROOT"'" cache ttl'
|
|
475
|
+
|
|
476
|
+
# Config present but no CLI: still silent no-op, exit 0.
|
|
477
|
+
clear_env
|
|
478
|
+
out="$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_recall "'"$INIT_ROOT"'" cache ttl')"
|
|
479
|
+
expect_eq "recall: opted-in but no CLI -> no output" "" "$out"
|
|
480
|
+
|
|
481
|
+
# No query terms: no-op even when memory is fully available.
|
|
482
|
+
clear_env
|
|
483
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_recall "'"$INIT_ROOT"'"')"
|
|
484
|
+
expect_eq "recall: no query terms -> no output" "" "$out"
|
|
485
|
+
|
|
486
|
+
# Available memory: recall runs, output returned, query passed through verbatim.
|
|
487
|
+
clear_env
|
|
488
|
+
out="$(PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_recall "'"$INIT_ROOT"'" cache ttl regression')"
|
|
489
|
+
expect_eq "recall: available -> returns CLI output" \
|
|
490
|
+
$'memory: 1 match for "cache ttl regression"\n [1.0] note-1' "$out"
|
|
491
|
+
clear_env
|
|
492
|
+
expect_ok "recall: available -> exit 0" \
|
|
493
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$FAKE_BIN"':$PATH" memory_recall "'"$INIT_ROOT"'" cache ttl'
|
|
494
|
+
|
|
495
|
+
# A CLI that errors (exit 1) must not propagate failure to the dev process.
|
|
496
|
+
clear_env
|
|
497
|
+
ERRBIN="$TMP/errbin"
|
|
498
|
+
mkdir -p "$ERRBIN"
|
|
499
|
+
cat > "$ERRBIN/memory" <<'EOF'
|
|
500
|
+
#!/usr/bin/env bash
|
|
501
|
+
echo "boom" >&2
|
|
502
|
+
exit 1
|
|
503
|
+
EOF
|
|
504
|
+
chmod +x "$ERRBIN/memory"
|
|
505
|
+
out="$(PATH="$ERRBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_recall "'"$INIT_ROOT"'" q' 2>/dev/null)"
|
|
506
|
+
expect_eq "recall: erroring CLI -> swallowed, no stdout" "" "$out"
|
|
507
|
+
clear_env
|
|
508
|
+
expect_ok "recall: erroring CLI -> still exit 0" \
|
|
509
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$ERRBIN"':/usr/bin:/bin" memory_recall "'"$INIT_ROOT"'" q'
|
|
510
|
+
|
|
511
|
+
# ---------- memory_record_attempt (graph-only write, graceful) ----------
|
|
512
|
+
|
|
513
|
+
payload="$TMP/attempt-payload.json"
|
|
514
|
+
cat > "$payload" <<'EOF'
|
|
515
|
+
{"repository":"reddb-io/red-skills","issueNumber":99,"attemptNumber":1,"status":"done"}
|
|
516
|
+
EOF
|
|
517
|
+
|
|
518
|
+
clear_env
|
|
519
|
+
FAKE_ATTEMPT_OUT="$TMP/attempt-out.json"
|
|
520
|
+
out="$(FAKE_ATTEMPT_OUT="$FAKE_ATTEMPT_OUT" PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_record_attempt "'"$GRAPH_ROOT"'" "'"$payload"'"')"
|
|
521
|
+
expect_eq "record-attempt: graph ready -> no stdout" "" "$out"
|
|
522
|
+
expect_eq "record-attempt: graph ready -> forwards JSON payload" \
|
|
523
|
+
"$(cat "$payload")" "$(cat "$FAKE_ATTEMPT_OUT")"
|
|
524
|
+
clear_env
|
|
525
|
+
expect_ok "record-attempt: graph ready -> exit 0" \
|
|
526
|
+
bash -c 'source "'"$LIB"'"; FAKE_ATTEMPT_OUT="'"$TMP/attempt-ok.json"'" PATH="'"$FAKE_BIN"':$PATH" memory_record_attempt "'"$GRAPH_ROOT"'" "'"$payload"'"'
|
|
527
|
+
|
|
528
|
+
clear_env
|
|
529
|
+
rm -f "$TMP/attempt-markdown.json"
|
|
530
|
+
out="$(FAKE_ATTEMPT_OUT="$TMP/attempt-markdown.json" PATH="$FAKE_BIN:$PATH" bash -c 'source "'"$LIB"'"; memory_record_attempt "'"$INIT_ROOT"'" "'"$payload"'"')"
|
|
531
|
+
expect_eq "record-attempt: markdown-only -> no output" "" "$out"
|
|
532
|
+
if [[ ! -e "$TMP/attempt-markdown.json" ]]; then
|
|
533
|
+
echo "PASS record-attempt: markdown-only -> does not call CLI"; pass=$((pass + 1))
|
|
534
|
+
else
|
|
535
|
+
echo "FAIL record-attempt: markdown-only -> unexpectedly called CLI"; fail=$((fail + 1))
|
|
536
|
+
fi
|
|
537
|
+
|
|
538
|
+
clear_env
|
|
539
|
+
out="$(PATH=/usr/bin:/bin bash -c 'source "'"$LIB"'"; memory_record_attempt "'"$GRAPH_ROOT"'" "'"$payload"'"')"
|
|
540
|
+
expect_eq "record-attempt: graph config but no CLI -> no output" "" "$out"
|
|
541
|
+
clear_env
|
|
542
|
+
expect_ok "record-attempt: graph config but no CLI -> exit 0" \
|
|
543
|
+
bash -c 'source "'"$LIB"'"; PATH=/usr/bin:/bin memory_record_attempt "'"$GRAPH_ROOT"'" "'"$payload"'"'
|
|
544
|
+
|
|
545
|
+
clear_env
|
|
546
|
+
out="$(PATH="$ERRBIN:/usr/bin:/bin" bash -c 'source "'"$LIB"'"; memory_record_attempt "'"$GRAPH_ROOT"'" "'"$payload"'"' 2>/dev/null)"
|
|
547
|
+
expect_eq "record-attempt: erroring CLI -> swallowed, no stdout" "" "$out"
|
|
548
|
+
clear_env
|
|
549
|
+
expect_ok "record-attempt: erroring CLI -> still exit 0" \
|
|
550
|
+
bash -c 'source "'"$LIB"'"; PATH="'"$ERRBIN"':/usr/bin:/bin" memory_record_attempt "'"$GRAPH_ROOT"'" "'"$payload"'"'
|
|
551
|
+
|
|
552
|
+
# ---------- summary ----------
|
|
553
|
+
echo
|
|
554
|
+
echo "memory-bridge: $pass passed, $fail failed"
|
|
555
|
+
[[ "$fail" -eq 0 ]]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cargo-pre-worktree.sh — built-in pre_worktree default for Rust projects.
|
|
3
|
+
#
|
|
4
|
+
# Contract (PRD #207, issue #211):
|
|
5
|
+
# stdin — JSON object: pre_worktree context (issue, target, branch, env).
|
|
6
|
+
# stdout — empty (context unchanged) when no Cargo.toml is present at
|
|
7
|
+
# $PROJECT_ROOT, OR a JSON object that merges
|
|
8
|
+
# {env: {CARGO_TARGET_DIR: <slot dir>}} into the context.
|
|
9
|
+
# exit — 0 on either branch. Never aborts the chain.
|
|
10
|
+
#
|
|
11
|
+
# Side effect: mkdir -p the slot target dir so cargo can use it immediately.
|
|
12
|
+
# CARGO_TARGET_DIR=${RED_AFK_CARGO_TARGET_BASE:-/opt/cargo-target}/slot-${RED_AFK_SLOT:-0}
|
|
13
|
+
# so each worker slot gets its own target directory and Cargo never
|
|
14
|
+
# serializes on .cargo-lock between workers.
|
|
15
|
+
#
|
|
16
|
+
# Disable with `afk.hooks.defaults.cargo: false` in `.red/config.yaml`.
|
|
17
|
+
|
|
18
|
+
set -uo pipefail
|
|
19
|
+
|
|
20
|
+
project_root="${PROJECT_ROOT:-$(pwd)}"
|
|
21
|
+
ctx="$(timeout "${RED_SKILLS_HOOK_STDIN_TIMEOUT_S:-5s}" cat 2>/dev/null || true)"
|
|
22
|
+
[[ -z "$ctx" ]] && ctx='{}'
|
|
23
|
+
|
|
24
|
+
if [[ ! -f "$project_root/Cargo.toml" ]]; then
|
|
25
|
+
# Not a Rust project — pass-through.
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
slot="${RED_AFK_SLOT:-0}"
|
|
30
|
+
base="${RED_AFK_CARGO_TARGET_BASE:-/opt/cargo-target}"
|
|
31
|
+
target_dir="${base}/slot-${slot}"
|
|
32
|
+
|
|
33
|
+
mkdir -p "$target_dir" 2>/dev/null || true
|
|
34
|
+
|
|
35
|
+
printf '%s' "$ctx" | jq -c \
|
|
36
|
+
--arg t "$target_dir" \
|
|
37
|
+
'. + {env: ((.env // {}) + {CARGO_TARGET_DIR: $t})}'
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# envelope-post-attempt.sh — built-in post_attempt default (PRD #207, issue
|
|
3
|
+
# #212; renamed from post_worker in #226).
|
|
4
|
+
#
|
|
5
|
+
# Reconciles the iteration's intermediate envelope state on the AFK state
|
|
6
|
+
# file as soon as the runner returns — before any user `post_attempt` hook
|
|
7
|
+
# runs, so a user notifier/pager integration that reads state already sees
|
|
8
|
+
# the worker's terminal result classified.
|
|
9
|
+
#
|
|
10
|
+
# Contract:
|
|
11
|
+
# stdin — JSON object: post_attempt context with `.result.status` of
|
|
12
|
+
# "success" or "fail".
|
|
13
|
+
# stdout — empty (context unchanged). Pure side-effect on the state file.
|
|
14
|
+
# exit — 0. Never aborts the chain.
|
|
15
|
+
#
|
|
16
|
+
# Env contract (set by afk.sh before dispatch):
|
|
17
|
+
# RED_AFK_STATE_FILE — path to the iteration's afk.state.json;
|
|
18
|
+
# when unset/missing, the default is a no-op.
|
|
19
|
+
# RED_AFK_RESULT_STATUS — redundant fallback for tests / runners that
|
|
20
|
+
# can't shape a ctx; if ctx.result.status is
|
|
21
|
+
# absent the env var is used instead.
|
|
22
|
+
#
|
|
23
|
+
# Disable with `afk.hooks.defaults.envelope: false` in `.red/config.yaml`.
|
|
24
|
+
|
|
25
|
+
set -uo pipefail
|
|
26
|
+
|
|
27
|
+
ctx="$(timeout "${RED_SKILLS_HOOK_STDIN_TIMEOUT_S:-5s}" cat 2>/dev/null || true)"
|
|
28
|
+
|
|
29
|
+
state_file="${RED_AFK_STATE_FILE:-}"
|
|
30
|
+
[[ -z "$state_file" || ! -f "$state_file" ]] && exit 0
|
|
31
|
+
|
|
32
|
+
status=""
|
|
33
|
+
if [[ -n "$ctx" ]]; then
|
|
34
|
+
status="$(printf '%s' "$ctx" | jq -r '.result.status // empty' 2>/dev/null || true)"
|
|
35
|
+
fi
|
|
36
|
+
[[ -z "$status" ]] && status="${RED_AFK_RESULT_STATUS:-}"
|
|
37
|
+
[[ -z "$status" ]] && exit 0
|
|
38
|
+
|
|
39
|
+
tmp="${state_file}.tmp.$$"
|
|
40
|
+
if jq --arg s "$status" '.current = ((.current // {}) + {result_status: $s})' \
|
|
41
|
+
"$state_file" > "$tmp" 2>/dev/null; then
|
|
42
|
+
mv "$tmp" "$state_file"
|
|
43
|
+
else
|
|
44
|
+
rm -f "$tmp"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
exit 0
|