devlog-tracker 1.0.0 → 1.1.1
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 +7 -5
- package/README.zh-TW.md +7 -5
- package/codex/hooks/on-interrupt.sh +1 -0
- package/codex/hooks/on-pre-tool.sh +9 -7
- package/codex/hooks/on-session-end.sh +1 -0
- package/codex/hooks/on-session-start.sh +1 -0
- package/codex/hooks/on-stop.sh +1 -0
- package/codex/hooks/on-subagent-start.sh +1 -0
- package/codex/hooks/on-user-prompt-submit.sh +1 -0
- package/codex/hooks/test-adapters.sh +29 -19
- package/commands/clean.md +2 -2
- package/commands/continue.md +1 -1
- package/commands/keep-all.md +4 -4
- package/commands/keep.md +4 -4
- package/commands/migrate.md +2 -2
- package/commands/span.md +4 -4
- package/commands/status.md +1 -1
- package/core/scripts/await-open.sh +4 -4
- package/core/scripts/clean-devlog.sh +11 -5
- package/core/scripts/close-open-round.sh +0 -4
- package/core/scripts/devlog-md.sh +89 -8
- package/core/scripts/devlog-path.sh +128 -10
- package/core/scripts/enforce-devlog.sh +35 -19
- package/core/scripts/keep-all.js +4 -3
- package/core/scripts/keep-all.sh +2 -3
- package/core/scripts/keep-all.test.js +31 -0
- package/core/scripts/keep-move.sh +13 -9
- package/core/scripts/migrate-handoff.sh +2 -2
- package/core/scripts/on-tool-failure.sh +6 -1
- package/core/scripts/pause-devlog.sh +5 -3
- package/core/scripts/round-start.sh +31 -20
- package/core/scripts/segment-watch-set.sh +12 -8
- package/core/scripts/segment-watch.sh +3 -6
- package/core/scripts/session-start-devlog.sh +11 -5
- package/core/scripts/span-close.sh +7 -3
- package/core/scripts/span-open.sh +4 -4
- package/core/scripts/status-devlog.sh +6 -6
- package/core/scripts/tests/test-await-open.sh +9 -0
- package/core/scripts/tests/test-devlog-md.sh +76 -15
- package/core/scripts/tests/test-devlog-path.sh +126 -0
- package/core/scripts/tests/test-multi-platform.sh +286 -0
- package/core/scripts/tests/test-segment-watch-set.sh +10 -0
- package/core/scripts/tests/test-session-start-devlog.sh +15 -0
- package/cursor/hooks/on-pre-tool.sh +1 -0
- package/cursor/hooks/on-session-end.sh +1 -0
- package/cursor/hooks/on-session-start.sh +1 -0
- package/cursor/hooks/on-stop.sh +1 -0
- package/cursor/hooks/on-submit-prompt.sh +1 -0
- package/cursor/hooks/on-tool-failure.sh +1 -0
- package/cursor/hooks/test-adapters.sh +19 -16
- package/package.json +1 -1
- package/skills/devlog-tracker/SKILL.md +23 -18
- package/skills/devlog-tracker/references/contract.md +2 -2
- package/skills/devlog-tracker/references/reply-fold.md +8 -8
- package/skills/devlog-tracker/references/round-segments.md +2 -2
- package/skills/devlog-tracker/references/span-mode.md +10 -3
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Two platforms in one worktree (docs/design/multi-platform-concurrency.md).
|
|
3
|
+
# Run: bash core/scripts/tests/test-multi-platform.sh
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
6
|
+
# shellcheck source=../workspace-snapshot.sh
|
|
7
|
+
. "$SCRIPT_DIR/workspace-snapshot.sh"
|
|
8
|
+
TMP_ROOT="$(mktemp -d)"
|
|
9
|
+
trap 'rm -rf "$TMP_ROOT"' EXIT
|
|
10
|
+
FAIL=0
|
|
11
|
+
pass() { echo "PASS: $1"; }
|
|
12
|
+
fail() { echo "FAIL: $1"; FAIL=1; }
|
|
13
|
+
check() { if eval "$2"; then pass "$1"; else fail "$1"; fi; }
|
|
14
|
+
|
|
15
|
+
new_project() {
|
|
16
|
+
P="$TMP_ROOT/$1"
|
|
17
|
+
mkdir -p "$P/.devlog"
|
|
18
|
+
git -C "$P" init -q
|
|
19
|
+
git -C "$P" -c user.email=t@t.t -c user.name=t commit -q --allow-empty -m init
|
|
20
|
+
git -C "$P" branch -M main
|
|
21
|
+
echo '.devlog/' > "$P/.gitignore"
|
|
22
|
+
git -C "$P" add .gitignore
|
|
23
|
+
git -C "$P" -c user.email=t@t.t -c user.name=t commit -q -m ignore
|
|
24
|
+
touch "$P/.devlog/.enabled" "$P/.devlog/.platform-claimed"
|
|
25
|
+
# shellcheck disable=SC2034 # read inside check's eval strings
|
|
26
|
+
D="$P/.devlog"
|
|
27
|
+
}
|
|
28
|
+
submit() { # platform prompt
|
|
29
|
+
printf '{"prompt":"%s","session_id":"%s-1"}' "$2" "$1" \
|
|
30
|
+
| DEVLOG_PLATFORM="$1" DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/round-start.sh"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# --- Task 3: codex writes only @codex files ---------------------------------
|
|
34
|
+
new_project refactor
|
|
35
|
+
submit codex "hello from codex" >/dev/null
|
|
36
|
+
check "codex round file" '[ -f "$D/.round-current@codex.md" ] && grep -q "hello from codex" "$D/.round-current@codex.md"'
|
|
37
|
+
check "codex marker" '[ -f "$D/.round-open@codex" ] && [ -f "$D/.turn-start@codex" ]'
|
|
38
|
+
check "claude files untouched" '[ ! -e "$D/.round-current.md" ] && [ ! -e "$D/.round-open" ] && [ ! -e "$D/.turn-start" ]'
|
|
39
|
+
|
|
40
|
+
# --- Task 4: the other platform's open round survives, numbers are unique -
|
|
41
|
+
new_project interleave
|
|
42
|
+
printf '%s\n' '{"last_change_epoch": 0, "last_seen_cksum": "", "max_silent_seconds": 600, "session_id": ""}' > "$D/.segment-state"
|
|
43
|
+
submit claude "claude task" >/dev/null
|
|
44
|
+
# shellcheck disable=SC2034 # read inside check's eval strings
|
|
45
|
+
OUT="$(submit codex "codex task")"
|
|
46
|
+
check "claude round still open" '[ -f "$D/.round-open" ] && grep -q "claude task" "$D/.round-current.md"'
|
|
47
|
+
check "claude round not INTERRUPTED" '! grep -q INTERRUPTED "$D/.round-current.md" && [ ! -s "$D/devlog.md" ]'
|
|
48
|
+
check "claude heading has no suffix" 'head -1 "$D/.round-current.md" | grep -q "^## Round 1 — " && ! head -1 "$D/.round-current.md" | grep -q " · "'
|
|
49
|
+
check "codex reserved round 2 with suffix" 'grep -q "^## Round 2 — .* · codex$" "$D/.round-current@codex.md"'
|
|
50
|
+
check "codex told its file" 'case "$OUT" in *".devlog/.round-current@codex.md"*) true ;; *) false ;; esac'
|
|
51
|
+
submit cursor "cursor task" >/dev/null
|
|
52
|
+
check "cursor reserved round 3" 'grep -q "^## Round 3 — .* · cursor$" "$D/.round-current@cursor.md"'
|
|
53
|
+
|
|
54
|
+
# Codex's own next prompt closes only codex's round.
|
|
55
|
+
submit codex "codex second" >/dev/null
|
|
56
|
+
check "codex dangling round merged as INTERRUPTED" 'grep -q "^## Round 2 — .* · codex$" "$D/devlog.md" && grep -q INTERRUPTED "$D/devlog.md"'
|
|
57
|
+
check "claude still open after codex re-submit" 'grep -q "claude task" "$D/.round-current.md"'
|
|
58
|
+
check "codex second is round 4" 'grep -q "^## Round 4 — .* · codex$" "$D/.round-current@codex.md"'
|
|
59
|
+
check "non-claude segment state seeded" '[ ! -f "$D/.segment-state" ] || [ -f "$D/.segment-state@codex" ]'
|
|
60
|
+
|
|
61
|
+
# Reply Fold finds codex's round by number even when it is not last.
|
|
62
|
+
new_project fold
|
|
63
|
+
cat > "$D/devlog.md" <<'EOF'
|
|
64
|
+
## Round 1 — 2026-09-27T10:00:00+0800 · codex
|
|
65
|
+
|
|
66
|
+
### Summary
|
|
67
|
+
asked a question
|
|
68
|
+
|
|
69
|
+
### Reply
|
|
70
|
+
q?
|
|
71
|
+
|
|
72
|
+
### Handoff
|
|
73
|
+
<handoff>
|
|
74
|
+
<state>
|
|
75
|
+
waiting
|
|
76
|
+
</state>
|
|
77
|
+
</handoff>
|
|
78
|
+
|
|
79
|
+
### Status
|
|
80
|
+
DONE
|
|
81
|
+
|
|
82
|
+
## Round 2 — 2026-09-27T10:02:00+0800
|
|
83
|
+
|
|
84
|
+
### Summary
|
|
85
|
+
claude work
|
|
86
|
+
|
|
87
|
+
### Reply
|
|
88
|
+
ok
|
|
89
|
+
|
|
90
|
+
### Handoff
|
|
91
|
+
<handoff>
|
|
92
|
+
<state>
|
|
93
|
+
done
|
|
94
|
+
</state>
|
|
95
|
+
</handoff>
|
|
96
|
+
|
|
97
|
+
### Status
|
|
98
|
+
DONE
|
|
99
|
+
EOF
|
|
100
|
+
printf '{"round": 1, "opened_at": "t"}\n' > "$D/.awaiting-reply@codex"
|
|
101
|
+
submit codex "the answer" >/dev/null
|
|
102
|
+
check "fold reopened codex round 1" 'grep -q "^## Round 1 — .* · codex$" "$D/.round-current@codex.md" && grep -q "the answer" "$D/.round-current@codex.md"'
|
|
103
|
+
check "claude round 2 stays in devlog" 'grep -q "^## Round 2 " "$D/devlog.md" && ! grep -q "^## Round 1 " "$D/devlog.md"'
|
|
104
|
+
|
|
105
|
+
# --- Task 5: both Stop in either order, handoffs coexist -------------------
|
|
106
|
+
close_round() { # platform status file
|
|
107
|
+
local f="$3"
|
|
108
|
+
cat >> "$f" <<EOF
|
|
109
|
+
|
|
110
|
+
### Summary
|
|
111
|
+
closed by $1
|
|
112
|
+
|
|
113
|
+
### Reply
|
|
114
|
+
ok
|
|
115
|
+
|
|
116
|
+
### Handoff
|
|
117
|
+
<handoff>
|
|
118
|
+
<workspace>
|
|
119
|
+
$(workspace_snapshot "$P")
|
|
120
|
+
</workspace>
|
|
121
|
+
<state>
|
|
122
|
+
$1 state
|
|
123
|
+
</state>
|
|
124
|
+
<done-when>
|
|
125
|
+
tests pass
|
|
126
|
+
</done-when>
|
|
127
|
+
<next>
|
|
128
|
+
run \`bash core/scripts/run-tests.sh\`
|
|
129
|
+
</next>
|
|
130
|
+
</handoff>
|
|
131
|
+
|
|
132
|
+
### Session Handoff
|
|
133
|
+
<session-handoff>
|
|
134
|
+
<decisions>
|
|
135
|
+
- $1 decision
|
|
136
|
+
</decisions>
|
|
137
|
+
<open-questions>
|
|
138
|
+
- (無)
|
|
139
|
+
</open-questions>
|
|
140
|
+
<failed-attempts>
|
|
141
|
+
- (無)
|
|
142
|
+
</failed-attempts>
|
|
143
|
+
</session-handoff>
|
|
144
|
+
EOF
|
|
145
|
+
# Replace the skeleton's Status with the requested one.
|
|
146
|
+
awk -v st="$2" '/^### Status$/ { print; getline; print st; next } { print }' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
|
|
147
|
+
printf '{}' | DEVLOG_PLATFORM="$1" DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/enforce-devlog.sh"
|
|
148
|
+
}
|
|
149
|
+
new_project stops_claude_first
|
|
150
|
+
submit claude "claude task" >/dev/null
|
|
151
|
+
submit codex "codex task" >/dev/null
|
|
152
|
+
close_round claude IN_PROGRESS "$D/.round-current.md" 2>"$TMP_ROOT/err0"; rc=$?
|
|
153
|
+
check "claude-first: claude Stop passes" '[ "$rc" -eq 0 ] || { cat "$TMP_ROOT/err0"; false; }'
|
|
154
|
+
check "claude-first: claude merged, codex still open" 'grep -q "closed by claude" "$D/devlog.md" && [ -f "$D/.round-open@codex" ] && [ ! -e "$D/.round-open" ]'
|
|
155
|
+
close_round codex IN_PROGRESS "$D/.round-current@codex.md" 2>"$TMP_ROOT/err0"; rc=$?
|
|
156
|
+
check "claude-first: codex Stop passes" '[ "$rc" -eq 0 ] || { cat "$TMP_ROOT/err0"; false; }'
|
|
157
|
+
check "claude-first: numbers unique" '[ "$(grep -o "^## Round [0-9]*" "$D/devlog.md" | tr "\n" ",")" = "## Round 1,## Round 2," ]'
|
|
158
|
+
check "claude-first: neither INTERRUPTED" '! grep -q INTERRUPTED "$D/devlog.md"'
|
|
159
|
+
|
|
160
|
+
new_project stops
|
|
161
|
+
submit claude "claude task" >/dev/null
|
|
162
|
+
submit codex "codex task" >/dev/null
|
|
163
|
+
close_round codex IN_PROGRESS "$D/.round-current@codex.md" 2>"$TMP_ROOT/err1"; rc=$?
|
|
164
|
+
check "codex Stop passes" '[ "$rc" -eq 0 ] || { cat "$TMP_ROOT/err1"; false; }'
|
|
165
|
+
check "codex merged, claude still open" 'grep -q "closed by codex" "$D/devlog.md" && [ -f "$D/.round-open" ] && [ ! -e "$D/.round-open@codex" ]'
|
|
166
|
+
check "codex handoff written" 'grep -q "codex decision" "$D/handoff@codex.md"'
|
|
167
|
+
close_round claude IN_PROGRESS "$D/.round-current.md" 2>"$TMP_ROOT/err2"; rc=$?
|
|
168
|
+
check "claude Stop passes" '[ "$rc" -eq 0 ] || { cat "$TMP_ROOT/err2"; false; }'
|
|
169
|
+
check "file order is close order, numbers unique" '[ "$(grep -o "^## Round [0-9]*" "$D/devlog.md" | tr "\n" ",")" = "## Round 2,## Round 1," ]'
|
|
170
|
+
check "handoffs coexist" 'grep -q "claude decision" "$D/handoff.md" && grep -q "codex decision" "$D/handoff@codex.md"'
|
|
171
|
+
check "neither INTERRUPTED" '! grep -q INTERRUPTED "$D/devlog.md"'
|
|
172
|
+
|
|
173
|
+
OUT="$(printf '{"source":"startup"}' | DEVLOG_PLATFORM=codex DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/session-start-devlog.sh")"
|
|
174
|
+
check "codex SessionStart injects own handoff" 'case "$OUT" in *"codex decision"*) true ;; *) false ;; esac'
|
|
175
|
+
check "codex SessionStart does not inject claude handoff" 'case "$OUT" in *"claude decision"*) false ;; *) true ;; esac'
|
|
176
|
+
check "codex SessionStart notes claude handoff" 'case "$OUT" in *"另一個平台(claude)有未完成的交接:.devlog/handoff.md"*) true ;; *) false ;; esac'
|
|
177
|
+
|
|
178
|
+
# Interrupt of one platform does not touch the other.
|
|
179
|
+
new_project interrupt
|
|
180
|
+
submit claude "claude task" >/dev/null
|
|
181
|
+
submit codex "codex task" >/dev/null
|
|
182
|
+
DEVLOG_PLATFORM=codex DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/close-open-round.sh" "Interrupt:cancelled"
|
|
183
|
+
check "codex interrupted round merged" 'grep -q "^## Round 2 — .* · codex$" "$D/devlog.md" && grep -q "Interrupt:cancelled" "$D/devlog.md"'
|
|
184
|
+
check "claude untouched by codex interrupt" '[ -f "$D/.round-open" ] && grep -q "claude task" "$D/.round-current.md"'
|
|
185
|
+
|
|
186
|
+
# span-open falls back to the platform's own last round.
|
|
187
|
+
new_project span
|
|
188
|
+
printf '## Round 7 — t · codex\n\n### Status\nDONE\n\n## Round 8 — t\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
189
|
+
# shellcheck disable=SC2034 # read inside check's eval strings
|
|
190
|
+
OUT="$(DEVLOG_PLATFORM=codex DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/span-open.sh")"
|
|
191
|
+
check "codex span-open uses its own last round" '[ "$OUT" = "OPENED=7" ] && grep -q "\"round\": 7" "$D/.span-open@codex" && [ ! -e "$D/.span-open" ]'
|
|
192
|
+
|
|
193
|
+
# --- Task 6: whole-devlog commands --------------------------------------
|
|
194
|
+
new_project cmds
|
|
195
|
+
submit claude "claude task" >/dev/null
|
|
196
|
+
submit codex "codex task" >/dev/null
|
|
197
|
+
DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/clean-devlog.sh" --confirmed >/dev/null 2>"$TMP_ROOT/cleanerr"; rc=$?
|
|
198
|
+
check "clean refuses with two open rounds" '[ "$rc" -ne 0 ] && grep -q "其他平台還有進行中的輪次" "$TMP_ROOT/cleanerr" && [ -f "$D/.round-current@codex.md" ]'
|
|
199
|
+
check "refusal names the pause/start escape hatch" 'grep -q "若該平台已不再使用,可先 pause 再 start 清掉它的標記。" "$TMP_ROOT/cleanerr"'
|
|
200
|
+
printf '## Round 9 — t\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
201
|
+
DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/keep-move.sh" --from 9 --to 9 --name x >/dev/null 2>"$TMP_ROOT/keeperr"; rc=$?
|
|
202
|
+
check "keep-move refuses with two open rounds" '[ "$rc" -ne 0 ] && grep -q "其他平台還有進行中的輪次(codex)" "$TMP_ROOT/keeperr" && [ ! -e "$D/devlog.x.md" ]'
|
|
203
|
+
|
|
204
|
+
DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/pause-devlog.sh" >/dev/null
|
|
205
|
+
check "pause clears every platform's markers" '[ ! -e "$D/.round-open" ] && [ ! -e "$D/.round-open@codex" ]'
|
|
206
|
+
|
|
207
|
+
new_project clean1
|
|
208
|
+
printf '## Round 1 — t\n\n### Status\nDONE\n\n## Round 2 — t\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
209
|
+
submit codex "only codex" >/dev/null
|
|
210
|
+
check "codex got round 3 before clean" 'grep -q "^## Round 3 — .* · codex$" "$D/.round-current@codex.md"'
|
|
211
|
+
echo "old claude handoff" > "$D/handoff.md"
|
|
212
|
+
echo "old codex handoff" > "$D/handoff@codex.md"
|
|
213
|
+
DEVLOG_PROJECT_DIR="$P" bash "$SCRIPT_DIR/clean-devlog.sh" --confirmed >/dev/null 2>&1
|
|
214
|
+
check "clean keeps the single open codex round as Round 1" 'grep -q "^## Round 1 — .* · codex$" "$D/.round-current@codex.md"'
|
|
215
|
+
check "clean removes every platform handoff" '[ ! -e "$D/handoff.md" ] && [ ! -e "$D/handoff@codex.md" ]'
|
|
216
|
+
|
|
217
|
+
# --- Span rounds are renumbered at merge when their number is taken -------
|
|
218
|
+
# A span-written round (no .round-open of its own) numbered by the LLM.
|
|
219
|
+
span_round() { # platform heading
|
|
220
|
+
local f="$D/.round-current@$1.md" t="$D/.turn-start@$1"
|
|
221
|
+
[ "$1" != claude ] || { f="$D/.round-current.md"; t="$D/.turn-start"; }
|
|
222
|
+
printf '%s\n\n### User Input\n```text\nspan tick\n```\n\n### Status\nIN_PROGRESS\n' "$2" > "$f"
|
|
223
|
+
echo stale > "$t"
|
|
224
|
+
close_round "$1" IN_PROGRESS "$f" 2>"$TMP_ROOT/spanerr"
|
|
225
|
+
}
|
|
226
|
+
headings() { grep -o '^## Round .*' "$D/devlog.md" | sed 's/ — [^ ]*//' | tr '\n' ','; }
|
|
227
|
+
|
|
228
|
+
new_project renum_reserved
|
|
229
|
+
printf '## Round 1 — t\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
230
|
+
submit codex "codex task" >/dev/null
|
|
231
|
+
span_round claude "## Round 2 — 2026-09-28T10:00:00+0800"
|
|
232
|
+
# shellcheck disable=SC2034 # read inside check's eval strings
|
|
233
|
+
rc=$?
|
|
234
|
+
check "span claude round passes Stop" '[ "$rc" -eq 0 ] || { cat "$TMP_ROOT/spanerr"; false; }'
|
|
235
|
+
check "claude span round 2 renumbered past codex reservation" '[ "$(headings)" = "## Round 1,## Round 3," ]'
|
|
236
|
+
check "renumbered heading keeps its timestamp" 'grep -q "^## Round 3 — 2026-09-28T10:00:00+0800$" "$D/devlog.md"'
|
|
237
|
+
close_round codex IN_PROGRESS "$D/.round-current@codex.md" 2>"$TMP_ROOT/spanerr"
|
|
238
|
+
check "codex reserved round keeps 2" '[ "$(headings)" = "## Round 1,## Round 3,## Round 2 · codex," ]'
|
|
239
|
+
|
|
240
|
+
new_project renum_existing
|
|
241
|
+
printf '## Round 1 — t\n\n### Status\nDONE\n\n## Round 2 — t · codex\n\n### Status\nDONE\n\n## Round 3 — t\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
242
|
+
span_round claude "## Round 3 — t"
|
|
243
|
+
check "span round whose number exists gets the next free one" '[ "$(headings)" = "## Round 1,## Round 2 · codex,## Round 3,## Round 4," ]'
|
|
244
|
+
|
|
245
|
+
new_project renum_suffix
|
|
246
|
+
printf '## Round 4 — t\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
247
|
+
span_round codex "## Round 5 — t"
|
|
248
|
+
check "codex span round without suffix gets it on merge" '[ "$(headings)" = "## Round 4,## Round 5 · codex," ]'
|
|
249
|
+
|
|
250
|
+
new_project renum_passthrough
|
|
251
|
+
submit claude "claude task" >/dev/null
|
|
252
|
+
submit codex "codex task" >/dev/null
|
|
253
|
+
# shellcheck disable=SC2034 # read inside check's eval strings
|
|
254
|
+
BEFORE="$(head -1 "$D/.round-current.md")"
|
|
255
|
+
close_round claude IN_PROGRESS "$D/.round-current.md" 2>/dev/null
|
|
256
|
+
check "reserved round heading passes through unchanged" '[ "$(grep "^## Round " "$D/devlog.md")" = "$BEFORE" ]'
|
|
257
|
+
|
|
258
|
+
new_project renum_own_dup
|
|
259
|
+
submit claude "claude task" >/dev/null
|
|
260
|
+
OWN_N="$(head -1 "$D/.round-current.md" | sed 's/^## Round \([0-9]*\).*/\1/')"
|
|
261
|
+
printf '\n## Round %s — dup\n\n### Status\nIN_PROGRESS\n' "$OWN_N" >> "$D/.round-current.md"
|
|
262
|
+
close_round claude IN_PROGRESS "$D/.round-current.md" 2>/dev/null
|
|
263
|
+
# shellcheck disable=SC2034 # read inside check's eval strings
|
|
264
|
+
DUP_N=$((OWN_N + 1))
|
|
265
|
+
check "second heading reusing the reserved number is renumbered" '[ "$(grep -c "^## Round $OWN_N " "$D/devlog.md")" -eq 1 ] && grep -q "^## Round $DUP_N — dup$" "$D/devlog.md"'
|
|
266
|
+
|
|
267
|
+
new_project renum_unwritable
|
|
268
|
+
printf '## Round 1 — t\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
269
|
+
printf '## Round 1 — t\n' > "$D/.round-current.md"
|
|
270
|
+
chmod a-w "$D"
|
|
271
|
+
# shellcheck disable=SC2034 # read inside check's eval strings
|
|
272
|
+
RENUM_ERR="$(DEVLOG_DIR="$D" DEVLOG_PLATFORM=claude bash -c '. "$1/devlog-path.sh"; . "$1/devlog-md.sh"; _devlog_renumber_current "$2" "$3"' _ "$SCRIPT_DIR" "$D/devlog.md" "$D/.round-current.md" 2>&1)"
|
|
273
|
+
chmod u+w "$D"
|
|
274
|
+
check "renumber stays quiet and leaves round-current alone when it cannot write" '[ -z "$RENUM_ERR" ] && [ "$(cat "$D/.round-current.md")" = "## Round 1 — t" ]'
|
|
275
|
+
|
|
276
|
+
# --- Reply Fold prefers the caller's own round when numbers repeat ---------
|
|
277
|
+
new_project fold_dup
|
|
278
|
+
printf '## Round 5 — t · codex\n\n### Summary\ncodex asked\n\n### Status\nDONE\n\n## Round 5 — t\n\n### Summary\nclaude five\n\n### Status\nDONE\n' > "$D/devlog.md"
|
|
279
|
+
printf '{"round": 5, "opened_at": "t"}\n' > "$D/.awaiting-reply@codex"
|
|
280
|
+
submit codex "the answer" >/dev/null
|
|
281
|
+
check "codex fold reopens codex's Round 5" 'grep -q "codex asked" "$D/.round-current@codex.md" && grep -q "claude five" "$D/devlog.md" && ! grep -q "codex asked" "$D/devlog.md"'
|
|
282
|
+
close_round codex IN_PROGRESS "$D/.round-current@codex.md" 2>/dev/null
|
|
283
|
+
check "folded round keeps its number on merge" '[ "$(headings)" = "## Round 5,## Round 5 · codex," ]'
|
|
284
|
+
|
|
285
|
+
if [ "$FAIL" -eq 0 ]; then echo "All checks passed."; exit 0
|
|
286
|
+
else echo "Some checks FAILED."; exit 1; fi
|
|
@@ -71,5 +71,15 @@ assert_exit "zero -> exit 1" 1 $?
|
|
|
71
71
|
bash "$SCRIPT_DIR/segment-watch-set.sh" >/dev/null 2>&1
|
|
72
72
|
assert_exit "missing arg -> exit 1" 1 $?
|
|
73
73
|
|
|
74
|
+
# --- 5: threshold applies to every platform's .segment-state ----------------
|
|
75
|
+
printf '%s\n' '{"last_change_epoch": 7, "last_seen_cksum": "", "max_silent_seconds": 600, "session_id": "c1"}' \
|
|
76
|
+
> "$TMP_ROOT/.devlog/.segment-state@codex"
|
|
77
|
+
bash "$SCRIPT_DIR/segment-watch-set.sh" 300 >/dev/null
|
|
78
|
+
assert_exit "multi-platform set -> exit 0" 0 $?
|
|
79
|
+
grep -q '"max_silent_seconds": 300' "$TMP_ROOT/.devlog/.segment-state" \
|
|
80
|
+
&& echo "PASS: claude segment-state gets 300" || { echo "FAIL: claude segment-state"; FAIL=1; }
|
|
81
|
+
grep -q '"max_silent_seconds": 300' "$TMP_ROOT/.devlog/.segment-state@codex" \
|
|
82
|
+
&& echo "PASS: codex segment-state gets 300" || { echo "FAIL: codex segment-state"; FAIL=1; }
|
|
83
|
+
|
|
74
84
|
if [ "$FAIL" -eq 0 ]; then echo "All checks passed."; exit 0
|
|
75
85
|
else echo "Some checks FAILED."; exit 1; fi
|
|
@@ -603,6 +603,21 @@ else
|
|
|
603
603
|
echo "FAIL: clear should be silent, got: $OUTPUT"; FAIL=1
|
|
604
604
|
fi
|
|
605
605
|
|
|
606
|
+
# --- other platform's handoff: noted by path, not injected ---------------
|
|
607
|
+
touch "$DEVLOG_DIR/.platform-claimed"
|
|
608
|
+
rm -f "$DEVLOG_DIR/handoff.md"
|
|
609
|
+
printf '<session-handoff>\n<decisions>\n- codex only decision\n</decisions>\n</session-handoff>\n' > "$DEVLOG_DIR/handoff@codex.md"
|
|
610
|
+
OUTPUT="$(echo '{"source":"startup"}' | bash "$SCRIPT_DIR/session-start-devlog.sh" 2>&1)"
|
|
611
|
+
assert_contains "claude notes codex handoff" "另一個平台(codex)有未完成的交接:.devlog/handoff@codex.md" "$OUTPUT"
|
|
612
|
+
assert_not_contains "claude does not inject codex handoff" "codex only decision" "$OUTPUT"
|
|
613
|
+
OUTPUT="$(echo '{"source":"clear"}' | bash "$SCRIPT_DIR/session-start-devlog.sh" 2>&1)"
|
|
614
|
+
if [ -z "$OUTPUT" ]; then
|
|
615
|
+
echo "PASS: clear stays silent with another platform's handoff"
|
|
616
|
+
else
|
|
617
|
+
echo "FAIL: clear should be silent, got: $OUTPUT"; FAIL=1
|
|
618
|
+
fi
|
|
619
|
+
rm -f "$DEVLOG_DIR/handoff@codex.md"
|
|
620
|
+
|
|
606
621
|
if [ "$FAIL" -eq 0 ]; then
|
|
607
622
|
echo "All checks passed."
|
|
608
623
|
exit 0
|
|
@@ -6,6 +6,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
|
|
|
6
6
|
INPUT="$(cat 2>/dev/null || true)"
|
|
7
7
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
8
8
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
9
|
+
export DEVLOG_PLATFORM=cursor
|
|
9
10
|
ERR_FILE="$(mktemp "${TMPDIR:-/tmp}/cursor-pre-tool.XXXXXX")" || { echo '{}'; exit 0; }
|
|
10
11
|
printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/segment-watch.sh" >/dev/null 2>"$ERR_FILE"
|
|
11
12
|
RESULT=$?
|
|
@@ -6,6 +6,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
|
|
|
6
6
|
INPUT="$(cat 2>/dev/null || true)"
|
|
7
7
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
8
8
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
9
|
+
export DEVLOG_PLATFORM=cursor
|
|
9
10
|
printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/on-session-end.sh" >/dev/null 2>&1 || true
|
|
10
11
|
echo '{}'
|
|
11
12
|
exit 0
|
|
@@ -6,6 +6,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
|
|
|
6
6
|
INPUT="$(cat 2>/dev/null || true)"
|
|
7
7
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
8
8
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
9
|
+
export DEVLOG_PLATFORM=cursor
|
|
9
10
|
EXCERPT="$(printf '%s' '{"source":"startup"}' | bash "$PLUGIN_SCRIPTS/session-start-devlog.sh" 2>/dev/null || true)"
|
|
10
11
|
if command -v jq >/dev/null 2>&1; then
|
|
11
12
|
jq -n --arg c "$EXCERPT" '{additional_context:$c}'
|
package/cursor/hooks/on-stop.sh
CHANGED
|
@@ -6,6 +6,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
|
|
|
6
6
|
INPUT="$(cat 2>/dev/null || true)"
|
|
7
7
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
8
8
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
9
|
+
export DEVLOG_PLATFORM=cursor
|
|
9
10
|
STATUS=""
|
|
10
11
|
LOOP_COUNT=0
|
|
11
12
|
if command -v jq >/dev/null 2>&1; then
|
|
@@ -6,6 +6,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
|
|
|
6
6
|
INPUT="$(cat 2>/dev/null || true)"
|
|
7
7
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
8
8
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
9
|
+
export DEVLOG_PLATFORM=cursor
|
|
9
10
|
PROMPT=""
|
|
10
11
|
SESSION_ID=""
|
|
11
12
|
if command -v jq >/dev/null 2>&1; then
|
|
@@ -6,6 +6,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
|
|
|
6
6
|
INPUT="$(cat 2>/dev/null || true)"
|
|
7
7
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
8
8
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
9
|
+
export DEVLOG_PLATFORM=cursor
|
|
9
10
|
if command -v jq >/dev/null 2>&1; then
|
|
10
11
|
IS_INTERRUPT="$(printf '%s' "$INPUT" | jq -r '.is_interrupt // .tool_result.is_interrupt // false' 2>/dev/null || echo false)"
|
|
11
12
|
else
|
|
@@ -58,17 +58,19 @@ assert_single_json "empty workspace single json" "$EMPTY"
|
|
|
58
58
|
SUBMIT="$TMP/submit"
|
|
59
59
|
mkdir -p "$SUBMIT/.devlog"
|
|
60
60
|
touch "$SUBMIT/.devlog/.enabled"
|
|
61
|
+
touch "$SUBMIT/.devlog/.platform-claimed"
|
|
61
62
|
OUT="$(printf '{"workspace_roots":["%s"],"prompt":"hello","session_id":"cursor-1"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-submit-prompt.sh")"
|
|
62
63
|
case "$OUT" in *'"continue":true'*) echo "PASS: submit continues" ;; *) echo "FAIL: submit [$OUT]"; FAIL=1 ;; esac
|
|
63
64
|
assert_single_json "submit single json" "$OUT"
|
|
64
|
-
grep -q 'hello' "$SUBMIT/.devlog/.round-current.md" && echo "PASS: submit writes round" || { echo "FAIL: submit round"; FAIL=1; }
|
|
65
|
-
ROUNDS="$(grep -c '^## Round ' "$SUBMIT/.devlog/.round-current.md" || true)"
|
|
65
|
+
grep -q 'hello' "$SUBMIT/.devlog/.round-current@cursor.md" && echo "PASS: submit writes round" || { echo "FAIL: submit round"; FAIL=1; }
|
|
66
|
+
ROUNDS="$(grep -c '^## Round ' "$SUBMIT/.devlog/.round-current@cursor.md" || true)"
|
|
66
67
|
if [ "$ROUNDS" -eq 1 ]; then echo "PASS: submit writes one round"; else echo "FAIL: submit round count [$ROUNDS]"; FAIL=1; fi
|
|
67
68
|
|
|
68
69
|
# submit prompt with stale 工作區 forwards additional_context
|
|
69
70
|
MIS="$TMP/mismatch-submit"
|
|
70
71
|
mkdir -p "$MIS/.devlog"
|
|
71
72
|
touch "$MIS/.devlog/.enabled"
|
|
73
|
+
touch "$MIS/.devlog/.platform-claimed"
|
|
72
74
|
git -C "$MIS" init -q -b main
|
|
73
75
|
git -C "$MIS" config user.email test@example.com
|
|
74
76
|
git -C "$MIS" config user.name test
|
|
@@ -76,7 +78,7 @@ echo hello > "$MIS/a.txt"
|
|
|
76
78
|
git -C "$MIS" add a.txt
|
|
77
79
|
git -C "$MIS" commit -q -m init
|
|
78
80
|
cat > "$MIS/.devlog/devlog.md" <<'EOF'
|
|
79
|
-
## Round 1 — 2026-09-11T00:00:00+08:00
|
|
81
|
+
## Round 1 — 2026-09-11T00:00:00+08:00 · cursor
|
|
80
82
|
|
|
81
83
|
### Handoff
|
|
82
84
|
#### 工作區
|
|
@@ -93,17 +95,17 @@ OUT="$(printf '{"workspace_roots":["%s"],"prompt":"keep going","session_id":"cur
|
|
|
93
95
|
case "$OUT" in *'"continue":true'*) echo "PASS: mismatch submit continues" ;; *) echo "FAIL: mismatch submit [$OUT]"; FAIL=1 ;; esac
|
|
94
96
|
case "$OUT" in *'"additional_context"'*工作區*) echo "PASS: mismatch submit additional_context" ;; *) echo "FAIL: mismatch context [$OUT]"; FAIL=1 ;; esac
|
|
95
97
|
assert_single_json "mismatch submit single json" "$OUT"
|
|
96
|
-
[ -f "$MIS/.devlog/.workspace-mismatch" ] && echo "PASS: mismatch submit writes marker" || { echo "FAIL: no cursor marker"; FAIL=1; }
|
|
98
|
+
[ -f "$MIS/.devlog/.workspace-mismatch@cursor" ] && echo "PASS: mismatch submit writes marker" || { echo "FAIL: no cursor marker"; FAIL=1; }
|
|
97
99
|
|
|
98
100
|
# preToolUse allowlists writes to the devlog and denies expired other tools
|
|
99
101
|
NOW="$(date +%s)"
|
|
100
102
|
OLD=$((NOW - 1000))
|
|
101
|
-
SUM="$(cksum < "$SUBMIT/.devlog/.round-current.md" | tr -d '\n')"
|
|
102
|
-
printf '{"last_change_epoch": %s, "last_seen_cksum": "%s", "max_silent_seconds": 900, "session_id": "cursor-1"}\n' "$OLD" "$SUM" > "$SUBMIT/.devlog/.segment-state"
|
|
103
|
-
OUT="$(printf '{"workspace_roots":["%s"],"tool_name":"Write","tool_input":{"file_path":"%s/.devlog/.round-current.md"},"session_id":"cursor-1"}' "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh")"
|
|
103
|
+
SUM="$(cksum < "$SUBMIT/.devlog/.round-current@cursor.md" | tr -d '\n')"
|
|
104
|
+
printf '{"last_change_epoch": %s, "last_seen_cksum": "%s", "max_silent_seconds": 900, "session_id": "cursor-1"}\n' "$OLD" "$SUM" > "$SUBMIT/.devlog/.segment-state@cursor"
|
|
105
|
+
OUT="$(printf '{"workspace_roots":["%s"],"tool_name":"Write","tool_input":{"file_path":"%s/.devlog/.round-current@cursor.md"},"session_id":"cursor-1"}' "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh")"
|
|
104
106
|
case "$OUT" in *'"permission":"deny"'*) echo "FAIL: devlog write denied"; FAIL=1 ;; *) echo "PASS: devlog write allowed" ;; esac
|
|
105
107
|
assert_single_json "preTool write single json" "$OUT"
|
|
106
|
-
OUT="$(printf '{"workspace_roots":["%s"],"tool_name":"StrReplace","tool_input":{"file_path":"%s/.devlog/.round-current.md"},"session_id":"cursor-1"}' "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh")"
|
|
108
|
+
OUT="$(printf '{"workspace_roots":["%s"],"tool_name":"StrReplace","tool_input":{"file_path":"%s/.devlog/.round-current@cursor.md"},"session_id":"cursor-1"}' "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh")"
|
|
107
109
|
case "$OUT" in *'"permission":"deny"'*) echo "FAIL: StrReplace devlog denied"; FAIL=1 ;; *) echo "PASS: StrReplace devlog allowed" ;; esac
|
|
108
110
|
assert_single_json "preTool StrReplace single json" "$OUT"
|
|
109
111
|
OUT="$(printf '{"workspace_roots":["%s"],"tool_name":"Bash","tool_input":{},"session_id":"cursor-1"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh")"
|
|
@@ -122,15 +124,15 @@ case "$OUT" in *followup_message*) echo "FAIL: non-completed stop enforced [$OUT
|
|
|
122
124
|
assert_single_json "non-completed stop single json" "$OUT"
|
|
123
125
|
|
|
124
126
|
# aborted stop and session end close the open round
|
|
125
|
-
printf '%s\n' '{"round": 1, "opened_at": "now"}' > "$SUBMIT/.devlog/.round-open"
|
|
127
|
+
printf '%s\n' '{"round": 1, "opened_at": "now"}' > "$SUBMIT/.devlog/.round-open@cursor"
|
|
126
128
|
OUT="$(printf '{"workspace_roots":["%s"],"status":"aborted"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-stop.sh")"
|
|
127
129
|
grep -q 'INTERRUPTED' "$SUBMIT/.devlog/devlog.md" && echo "PASS: aborted closes round" || { echo "FAIL: aborted"; FAIL=1; }
|
|
128
130
|
assert_single_json "aborted stop single json" "$OUT"
|
|
129
131
|
|
|
130
132
|
# sessionEnd delegates its reason to the existing close helper
|
|
131
|
-
printf '\n## Round 2 — now\n\n### Status\nIN_PROGRESS\n' > "$SUBMIT/.devlog/.round-current.md"
|
|
132
|
-
printf '%s\n' '{"round": 2, "opened_at": "now"}' > "$SUBMIT/.devlog/.round-open"
|
|
133
|
-
cksum < "$SUBMIT/.devlog/.round-current.md" > "$SUBMIT/.devlog/.turn-start"
|
|
133
|
+
printf '\n## Round 2 — now\n\n### Status\nIN_PROGRESS\n' > "$SUBMIT/.devlog/.round-current@cursor.md"
|
|
134
|
+
printf '%s\n' '{"round": 2, "opened_at": "now"}' > "$SUBMIT/.devlog/.round-open@cursor"
|
|
135
|
+
cksum < "$SUBMIT/.devlog/.round-current@cursor.md" > "$SUBMIT/.devlog/.turn-start@cursor"
|
|
134
136
|
OUT="$(printf '{"workspace_roots":["%s"],"reason":"windowClosed"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-session-end.sh")"
|
|
135
137
|
grep -q 'SessionEnd:windowClosed' "$SUBMIT/.devlog/devlog.md" && echo "PASS: session end reason" || { echo "FAIL: session end"; FAIL=1; }
|
|
136
138
|
assert_single_json "sessionEnd single json" "$OUT"
|
|
@@ -146,17 +148,18 @@ ROOT="$(printf '{"workspace_roots":["%s"]}' "$SUBMIT" | PATH="$PATH_NO_JQ" bash
|
|
|
146
148
|
if [ "$ROOT" = "$SUBMIT" ]; then echo "PASS: project-dir without jq"; else echo "FAIL: project-dir without jq [$ROOT]"; FAIL=1; fi
|
|
147
149
|
|
|
148
150
|
# tool interruption marker
|
|
149
|
-
rm -f "$SUBMIT/.devlog/.interrupted"
|
|
151
|
+
rm -f "$SUBMIT/.devlog/.interrupted@cursor"
|
|
150
152
|
OUT="$(printf '{"workspace_roots":["%s"],"is_interrupt":true}' "$SUBMIT" | bash "$SCRIPT_DIR/on-tool-failure.sh")"
|
|
151
|
-
[ -f "$SUBMIT/.devlog/.interrupted" ] && echo "PASS: tool interruption marked" || { echo "FAIL: interrupt marker"; FAIL=1; }
|
|
153
|
+
[ -f "$SUBMIT/.devlog/.interrupted@cursor" ] && echo "PASS: tool interruption marked" || { echo "FAIL: interrupt marker"; FAIL=1; }
|
|
152
154
|
assert_single_json "toolFailure single json" "$OUT"
|
|
153
155
|
|
|
154
156
|
# --- no-jq session_id must reach segment-state via round-start ------------
|
|
155
157
|
NOJQ_HOME="$TMP/nojq_home"
|
|
156
158
|
mkdir -p "$NOJQ_HOME/.devlog" "$TMP/nojq_path"
|
|
157
159
|
touch "$NOJQ_HOME/.devlog/.enabled"
|
|
160
|
+
touch "$NOJQ_HOME/.devlog/.platform-claimed"
|
|
158
161
|
printf '%s\n' '{"last_change_epoch": 0, "last_seen_cksum": "", "max_silent_seconds": 600, "session_id": ""}' \
|
|
159
|
-
> "$NOJQ_HOME/.devlog/.segment-state"
|
|
162
|
+
> "$NOJQ_HOME/.devlog/.segment-state@cursor"
|
|
160
163
|
CLEAN_PATH="$TMP/nojq_path"
|
|
161
164
|
# Resolve real binaries (zsh `command -v` may return aliases / builtins).
|
|
162
165
|
for cmd in bash sh cksum date grep sed cat mkdir tr mktemp rm head awk touch \
|
|
@@ -171,7 +174,7 @@ done
|
|
|
171
174
|
OUT="$(printf '{"workspace_roots":["%s"],"prompt":"nojq-hi","session_id":"cursor-nojq"}' "$NOJQ_HOME" \
|
|
172
175
|
| env PATH="$CLEAN_PATH" bash "$SCRIPT_DIR/on-submit-prompt.sh")"
|
|
173
176
|
case "$OUT" in *'"continue":true'*) echo "PASS: nojq submit continues" ;; *) echo "FAIL: nojq submit [$OUT]"; FAIL=1 ;; esac
|
|
174
|
-
SEG_SID="$(grep -o '"session_id"[[:space:]]*:[[:space:]]*"[^"]*"' "$NOJQ_HOME/.devlog/.segment-state" 2>/dev/null | head -1 | sed 's/.*"session_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || true)"
|
|
177
|
+
SEG_SID="$(grep -o '"session_id"[[:space:]]*:[[:space:]]*"[^"]*"' "$NOJQ_HOME/.devlog/.segment-state@cursor" 2>/dev/null | head -1 | sed 's/.*"session_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || true)"
|
|
175
178
|
if [ "$SEG_SID" = "cursor-nojq" ]; then
|
|
176
179
|
echo "PASS: nojq session_id stored in segment-state"
|
|
177
180
|
else
|