devlog-tracker 0.34.0 → 1.1.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.
Files changed (56) hide show
  1. package/README.md +7 -5
  2. package/README.zh-TW.md +7 -5
  3. package/codex/hooks/on-interrupt.sh +1 -0
  4. package/codex/hooks/on-pre-tool.sh +9 -7
  5. package/codex/hooks/on-session-end.sh +1 -0
  6. package/codex/hooks/on-session-start.sh +1 -0
  7. package/codex/hooks/on-stop.sh +1 -0
  8. package/codex/hooks/on-subagent-start.sh +1 -0
  9. package/codex/hooks/on-user-prompt-submit.sh +1 -0
  10. package/codex/hooks/test-adapters.sh +29 -19
  11. package/commands/clean.md +2 -2
  12. package/commands/continue.md +1 -1
  13. package/commands/keep-all.md +4 -4
  14. package/commands/keep.md +4 -4
  15. package/commands/migrate.md +2 -2
  16. package/commands/span.md +4 -4
  17. package/commands/status.md +1 -1
  18. package/core/scripts/await-open.sh +4 -4
  19. package/core/scripts/clean-devlog.sh +11 -5
  20. package/core/scripts/close-open-round.sh +0 -4
  21. package/core/scripts/devlog-md.sh +89 -8
  22. package/core/scripts/devlog-path.sh +128 -10
  23. package/core/scripts/enforce-devlog.sh +10 -16
  24. package/core/scripts/keep-all.js +4 -3
  25. package/core/scripts/keep-all.sh +2 -3
  26. package/core/scripts/keep-all.test.js +31 -0
  27. package/core/scripts/keep-move.sh +13 -9
  28. package/core/scripts/migrate-handoff.sh +2 -2
  29. package/core/scripts/on-tool-failure.sh +6 -1
  30. package/core/scripts/pause-devlog.sh +5 -3
  31. package/core/scripts/round-start.sh +31 -20
  32. package/core/scripts/segment-watch-set.sh +12 -8
  33. package/core/scripts/segment-watch.sh +3 -6
  34. package/core/scripts/session-start-devlog.sh +11 -5
  35. package/core/scripts/span-close.sh +7 -3
  36. package/core/scripts/span-open.sh +4 -4
  37. package/core/scripts/status-devlog.sh +6 -6
  38. package/core/scripts/tests/test-await-open.sh +9 -0
  39. package/core/scripts/tests/test-devlog-md.sh +76 -15
  40. package/core/scripts/tests/test-devlog-path.sh +126 -0
  41. package/core/scripts/tests/test-multi-platform.sh +286 -0
  42. package/core/scripts/tests/test-segment-watch-set.sh +10 -0
  43. package/core/scripts/tests/test-session-start-devlog.sh +15 -0
  44. package/cursor/hooks/on-pre-tool.sh +1 -0
  45. package/cursor/hooks/on-session-end.sh +1 -0
  46. package/cursor/hooks/on-session-start.sh +1 -0
  47. package/cursor/hooks/on-stop.sh +1 -0
  48. package/cursor/hooks/on-submit-prompt.sh +1 -0
  49. package/cursor/hooks/on-tool-failure.sh +1 -0
  50. package/cursor/hooks/test-adapters.sh +19 -16
  51. package/package.json +1 -1
  52. package/skills/devlog-tracker/SKILL.md +23 -18
  53. package/skills/devlog-tracker/references/contract.md +2 -2
  54. package/skills/devlog-tracker/references/reply-fold.md +8 -8
  55. package/skills/devlog-tracker/references/round-segments.md +2 -2
  56. package/skills/devlog-tracker/references/span-mode.md +10 -3
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  *English | [繁體中文](README.zh-TW.md)*
4
4
 
5
- **Version** 0.34.0
5
+ **Version** 1.1.0
6
6
 
7
7
  Maintains a `.devlog/devlog.md` in your project, turning each conversation round's requests, decisions, and outcomes into a permanent record. A conversation disappears the moment you `/clear` or switch sessions; this file fills that gap so work can pause and resume. Nothing is touched until you explicitly run `/devlog-tracker:start` — installing the plugin alone doesn't create or modify any files.
8
8
 
@@ -75,9 +75,9 @@ Merges the `hooks` from `codex/hooks.json` into the project's `.codex/hooks.json
75
75
 
76
76
  - Interactive mode: Codex warns at startup when hooks need review. Use `/hooks` to inspect and trust them. A later change to hook configuration (e.g. re-running `init` and changing paths or commands) may require another review.
77
77
  - Non-interactive `codex exec` (CI, scripts): unapproved hooks are silently skipped. `--dangerously-bypass-hook-trust` lets them run, but that flag skips trust checks for *all* hooks, so it's only appropriate for automation environments where you've already vetted the hook sources yourself.
78
- - To confirm it's working: after `start`, send a message and check whether `.devlog/.round-current.md` shows this round's User Input skeleton; if not, the hook didn't run.
78
+ - To confirm it's working: after `start`, send a message and check whether `.devlog/.round-current@codex.md` shows this round's User Input skeleton; if not, the hook didn't run.
79
79
 
80
- When the silence or workspace guard blocks a tool, Codex can read the current round with a single `cat <project>/.devlog/.round-current.md` command and update it with `apply_patch`. The block message includes the project path.
80
+ When the silence or workspace guard blocks a tool, Codex can read the current round with a single `cat <project>/.devlog/.round-current@codex.md` command and update it with `apply_patch`. The block message includes the project path.
81
81
 
82
82
  Codex's `Interrupt` hook records an interrupted main-thread turn as `INTERRUPTED`. Codex has no `StopFailure` event for other abnormal endings; an open round is recovered on the next prompt or session start, or when `SessionEnd` runs. Codex's `SessionEnd` reason is currently only `other`, and it may run after an idle session rather than immediately when you switch conversations. The `Stop` hook still enforces completion of normal turns. Codex can also start from a subdirectory of an installed project; the hooks find the installation root.
83
83
 
@@ -101,6 +101,8 @@ Without `--claude`/`--codex`/`--cursor`, it interactively asks which platform(s)
101
101
 
102
102
  Copies `core/scripts/`, `claude/hooks.json`, `codex/hooks/`, `cursor/hooks/`, `skills/`, and `commands/` into the project's `.devlog-tracker/`. Re-running `npx devlog-tracker init` upgrades to the package's current version; `npx devlog-tracker status` checks whether the installed version is behind. `npx devlog-tracker report [--json] [--all-branches]` and `npx devlog-tracker timeline [--all-branches] [--out <path>]` run the same scripts as `/devlog-tracker:report` and `/devlog-tracker:timeline`, using the vendored copy when there is one.
103
103
 
104
+ Claude Code, Codex, and Cursor can run in the same worktree at the same time. They share one `devlog.md`, but each platform keeps its own open round (`.devlog/.round-current.md` for Claude Code, `.round-current@codex.md`/`.round-current@cursor.md` for the others) and its own Session Handoff; round numbers stay unique across platforms, and Codex/Cursor round headings end with ` · codex`/` · cursor`. `clean`, `keep`, and `keep-all` refuse to run while two or more platforms have a round open. Several windows of the *same* platform in one worktree are not supported — they share one slot. Upgrade every platform's install to the same devlog-tracker version together: an older version on one platform still writes the shared, un-suffixed files and breaks the per-platform split. See [`docs/design/multi-platform-concurrency.md`](docs/design/multi-platform-concurrency.md).
105
+
104
106
  `.devlog-tracker/` contains the installed program. `.devlog/` contains your project's tracking data and is created only when you run the start command. Therefore, seeing `.devlog-tracker/` immediately after `npx init` is expected; `init` alone does not start recording.
105
107
 
106
108
  `init` writes this machine's absolute paths into each platform's hooks config and into `.devlog-tracker/env.sh`. If you commit these files to git, each teammate needs to run `npx devlog-tracker init` on their own machine (paths differ per machine); alternatively, add `.devlog-tracker/` and the generated hooks config files to `.gitignore`.
@@ -214,7 +216,7 @@ See [`docs/design/summary-handoff.md`](docs/design/summary-handoff.md), [`docs/d
214
216
 
215
217
  ### Upgrading to XML Handoff
216
218
 
217
- Rounds written before this change are in the legacy `####`-headed form and are still read fine. Archive, keep and lessons files (`devlog.archive.md`, `devlog.<name>.md`, `devlog.lessons.*.md`) are never rewritten. When the Stop hook blocks on a legacy Handoff in the round it's checking, it tells the agent to run the migrate command (`/devlog-tracker:migrate` on the Claude plugin or `$devlog-migrate` on Codex; underlying script: `migrate-handoff.sh`) itself, then finish the turn again — no action needed from you in the common case. Migrate rewrites `devlog.md`, the branch devlog files, the open round (`.round-current.md`) and `handoff.md`/`handoff.<branch>.md` in place, leaving a `*.pre-migrate` backup next to each file it changes; a round it cannot map exactly is left as-is and listed on a `SKIP` line. If the agent keeps writing the legacy form instead of picking up the fix, update the plugin or rerun `npx devlog-tracker init`, then use the start command for your installation (`/devlog-tracker:start` on the Claude plugin or `$devlog-start` on Codex).
219
+ Rounds written before this change are in the legacy `####`-headed form and are still read fine. Archive, keep and lessons files (`devlog.archive.md`, `devlog.<name>.md`, `devlog.lessons.*.md`) are never rewritten. When the Stop hook blocks on a legacy Handoff in the round it's checking, it tells the agent to run the migrate command (`/devlog-tracker:migrate` on the Claude plugin or `$devlog-migrate` on Codex; underlying script: `migrate-handoff.sh`) itself, then finish the turn again — no action needed from you in the common case. Migrate rewrites `devlog.md`, the branch devlog files, every platform's open round (`.round-current.md`, `.round-current@codex.md`, `.round-current@cursor.md`) and handoff files (`handoff.md`/`handoff.<branch>.md` and their `@codex`/`@cursor` variants) in place, leaving a `*.pre-migrate` backup next to each file it changes; a round it cannot map exactly is left as-is and listed on a `SKIP` line. If the agent keeps writing the legacy form instead of picking up the fix, update the plugin or rerun `npx devlog-tracker init`, then use the start command for your installation (`/devlog-tracker:start` on the Claude plugin or `$devlog-start` on Codex).
218
220
 
219
221
  ## What the hooks do automatically
220
222
 
@@ -243,7 +245,7 @@ Non-usage API errors, SessionEnd, and a leftover `.round-open` mark an open Roun
243
245
 
244
246
  #### Segment recording
245
247
 
246
- Don't hold a long round until the very end — write `### Segment` entries as you go. If `.round-current.md` hasn't been touched for about 10 minutes within the same round, the `PreToolUse` hook blocks the next tool call; Read first, then Edit/StrReplace to append a segment (don't Write over the whole file). The threshold is adjustable with `/devlog-tracker:segment-watch <duration>`. Claude Code subagents/dynamic workflows (`PreToolUse` carrying `agent_id`) don't apply this gate from the parent round. See [`docs/design/segment-watch.md`](docs/design/segment-watch.md).
248
+ Don't hold a long round until the very end — write `### Segment` entries as you go. If the open round file (`.round-current.md`, or `.round-current@codex.md`/`.round-current@cursor.md` on Codex/Cursor) hasn't been touched for about 10 minutes within the same round, the `PreToolUse` hook blocks the next tool call; Read first, then Edit/StrReplace to append a segment (don't Write over the whole file). The threshold is adjustable with `/devlog-tracker:segment-watch <duration>`. Claude Code subagents/dynamic workflows (`PreToolUse` carrying `agent_id`) don't apply this gate from the parent round. See [`docs/design/segment-watch.md`](docs/design/segment-watch.md).
247
249
 
248
250
  #### Checkpoint Mode
249
251
 
package/README.zh-TW.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  *[English](README.md) | 繁體中文*
4
4
 
5
- **版本** 0.34.0
5
+ **版本** 1.1.0
6
6
 
7
7
  在專案中維護一份 `.devlog/devlog.md`,把每一輪對話的請求、決策與結果寫成永久紀錄。對話一 `/clear` 或換 session 就沒了;這份檔案取代那個缺口,讓工作可以中斷再接。沒下過 `/devlog-tracker:start` 時,裝著也不會動任何檔案。
8
8
 
@@ -75,9 +75,9 @@ npx devlog-tracker init --codex
75
75
 
76
76
  - 互動模式:有 hook 待審核時,Codex 啟動時會顯示警告。用 `/hooks` 檢查並信任它們。之後 hook 的設定有變動(例如重跑 `init` 讓路徑或指令改變)也可能要再審核一次。
77
77
  - 非互動的 `codex exec`(CI、腳本):未審核的 hook 會被靜默略過。`--dangerously-bypass-hook-trust` 可以讓它們跑起來,但那個旗標會略過所有 hook 的信任檢查,只適合已經自己確認過 hook 來源的自動化環境。
78
- - 想確認有沒有生效:`start` 之後送一則訊息,看 `.devlog/.round-current.md` 有沒有出現這一輪的 User Input skeleton;沒有就代表 hook 沒被執行。
78
+ - 想確認有沒有生效:`start` 之後送一則訊息,看 `.devlog/.round-current@codex.md` 有沒有出現這一輪的 User Input skeleton;沒有就代表 hook 沒被執行。
79
79
 
80
- 沉默或工作區檢查擋住工具時,Codex 可用單一 `cat <專案>/.devlog/.round-current.md` 指令讀取當輪,再用 `apply_patch` 修改;封鎖訊息會附上專案路徑。
80
+ 沉默或工作區檢查擋住工具時,Codex 可用單一 `cat <專案>/.devlog/.round-current@codex.md` 指令讀取當輪,再用 `apply_patch` 修改;封鎖訊息會附上專案路徑。
81
81
 
82
82
  Codex 的 `Interrupt` hook 會把主執行緒被中斷的輪次標成 `INTERRUPTED`。Codex 沒有處理其他異常結束的 `StopFailure` 事件;未收尾輪次會在下次訊息、下次 session start,或 `SessionEnd` 執行時補記。Codex 的 `SessionEnd` 原因目前只有 `other`,切換對話後也可能等閒置一段時間才觸發。正常輪次仍由 `Stop` hook 強制收尾。從已安裝專案的子目錄啟動 Codex 時,hook 也會找到安裝根目錄。
83
83
 
@@ -101,6 +101,8 @@ npx devlog-tracker init
101
101
 
102
102
  會把 `core/scripts/`、`claude/hooks.json`、`codex/hooks/`、`cursor/hooks/`、`skills/`、`commands/` 複製進專案的 `.devlog-tracker/`。重新執行 `npx devlog-tracker init` 可以升級到套件目前的版本;`npx devlog-tracker status` 可以查目前裝的版本是否落後。`npx devlog-tracker report [--json] [--all-branches]` 和 `npx devlog-tracker timeline [--all-branches] [--out <路徑>]` 跑的是跟 `/devlog-tracker:report`、`/devlog-tracker:timeline` 同一支腳本,有 vendored 版本就用它。
103
103
 
104
+ Claude Code、Codex、Cursor 可以在同一個工作樹同時使用。三者共用一份 `devlog.md`,但每個平台各自保有開著的輪次(Claude Code 是 `.devlog/.round-current.md`,其他平台是 `.round-current@codex.md`/`.round-current@cursor.md`)與各自的 Session Handoff;輪次號碼跨平台不重複,Codex/Cursor 的 Round 標題結尾帶 ` · codex`/` · cursor`。有兩個以上平台的輪次開著時,`clean`、`keep`、`keep-all` 會拒絕執行。同一個平台在同一個工作樹開多個視窗不支援——它們共用同一格。各平台的安裝要一起升級到同一個 devlog-tracker 版本:某個平台還停在舊版時,它仍寫共用的無後綴檔案,會破壞各平台分開的狀態。細節見 [`docs/design/multi-platform-concurrency.md`](docs/design/multi-platform-concurrency.md)。
105
+
104
106
  `.devlog-tracker/` 放的是安裝進專案的程式;`.devlog/` 放的是這個專案的紀錄資料,執行 start 指令後才會建立。因此 `npx init` 後先看到 `.devlog-tracker/` 是預期行為,單跑 `init` 不會開始記錄。
105
107
 
106
108
  `init` 會把這台機器專屬的絕對路徑寫進各平台的 hooks 設定檔與 `.devlog-tracker/env.sh`。如果你把這些檔案 commit 進 git,每位隊友都要在自己的機器上跑一次 `npx devlog-tracker init`(路徑每台機器不同);或者改成把 `.devlog-tracker/` 與產生出來的 hooks 設定檔加進 `.gitignore`。
@@ -214,7 +216,7 @@ Stop hook 會做這些事:
214
216
 
215
217
  ### 升級到 XML Handoff
216
218
 
217
- 舊輪次是 `####` 小節格式,讀取端照樣相容。archive、keep 與 lessons 檔(`devlog.archive.md`、`devlog.<name>.md`、`devlog.lessons.*.md`)永遠不會被改寫。當 Stop hook 在收尾時擋下舊格式的 Handoff,訊息會叫 agent 自己跑對應的 migrate 指令(Claude plugin 用 `/devlog-tracker:migrate`,Codex 用 `$devlog-migrate`;底層是 `migrate-handoff.sh`)再重新結束這一輪——一般情況不用你動手。migrate 會直接改寫 `devlog.md`、分支 devlog 檔、開著的 Round(`.round-current.md`)與 `handoff.md`/`handoff.<branch>.md`,每個改過的檔旁邊留一份 `*.pre-migrate` 備份;無法精確轉換的輪次保留原樣,列在 `SKIP` 行。如果 agent 一直寫舊格式、沒有照著修,請更新 plugin 或重跑 `npx devlog-tracker init`,再使用對應的 start 指令(Claude plugin 用 `/devlog-tracker:start`,Codex 用 `$devlog-start`)。
219
+ 舊輪次是 `####` 小節格式,讀取端照樣相容。archive、keep 與 lessons 檔(`devlog.archive.md`、`devlog.<name>.md`、`devlog.lessons.*.md`)永遠不會被改寫。當 Stop hook 在收尾時擋下舊格式的 Handoff,訊息會叫 agent 自己跑對應的 migrate 指令(Claude plugin 用 `/devlog-tracker:migrate`,Codex 用 `$devlog-migrate`;底層是 `migrate-handoff.sh`)再重新結束這一輪——一般情況不用你動手。migrate 會直接改寫 `devlog.md`、分支 devlog 檔、每個平台開著的 Round(`.round-current.md`、`.round-current@codex.md`、`.round-current@cursor.md`)與交接檔(`handoff.md`/`handoff.<branch>.md` 及其 `@codex`/`@cursor` 版本),每個改過的檔旁邊留一份 `*.pre-migrate` 備份;無法精確轉換的輪次保留原樣,列在 `SKIP` 行。如果 agent 一直寫舊格式、沒有照著修,請更新 plugin 或重跑 `npx devlog-tracker init`,再使用對應的 start 指令(Claude plugin 用 `/devlog-tracker:start`,Codex 用 `$devlog-start`)。
218
220
 
219
221
  ## Hook 會自動做的事
220
222
 
@@ -243,7 +245,7 @@ Stop hook 會做這些事:
243
245
 
244
246
  #### 段落記錄
245
247
 
246
- 長輪不要憋到最後,邊做邊寫 `### 段落`。同一輪連續約 10 分鐘沒改 `.round-current.md`,`PreToolUse` hook 會擋住下一個工具;先 Read 再 Edit/StrReplace 追加一段(不要 Write 覆寫整檔)。門檻可用 `/devlog-tracker:segment-watch <時間長度>` 調整。Claude Code subagent/dynamic workflow(PreToolUse 帶 `agent_id`)不套用父輪這道閥。細節見 [`docs/design/segment-watch.md`](docs/design/segment-watch.md)。
248
+ 長輪不要憋到最後,邊做邊寫 `### 段落`。同一輪連續約 10 分鐘沒改開著的 round 檔(`.round-current.md`;Codex/Cursor 是 `.round-current@codex.md`/`.round-current@cursor.md`),`PreToolUse` hook 會擋住下一個工具;先 Read 再 Edit/StrReplace 追加一段(不要 Write 覆寫整檔)。門檻可用 `/devlog-tracker:segment-watch <時間長度>` 調整。Claude Code subagent/dynamic workflow(PreToolUse 帶 `agent_id`)不套用父輪這道閥。細節見 [`docs/design/segment-watch.md`](docs/design/segment-watch.md)。
247
249
 
248
250
  #### Checkpoint Mode
249
251
 
@@ -9,5 +9,6 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" 2>/dev/null && pwd)"
9
9
  INPUT="$(cat 2>/dev/null || true)"
10
10
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
11
11
  export DEVLOG_PROJECT_DIR="$ROOT"
12
+ export DEVLOG_PLATFORM=codex
12
13
  bash "$PLUGIN_SCRIPTS/close-open-round.sh" "Interrupt:cancelled" >/dev/null 2>&1 || true
13
14
  exit 0
@@ -15,10 +15,12 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" 2>/dev/null && pwd)"
15
15
  INPUT="$(cat 2>/dev/null || true)"
16
16
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
17
17
  export DEVLOG_PROJECT_DIR="$ROOT"
18
+ export DEVLOG_PLATFORM=codex
19
+ ROUND_FILE=".devlog/.round-current@codex.md"
18
20
 
19
21
  codex_access_payload() {
20
- printf '{"tool_name":"%s","tool_input":{"file_path":".devlog/.round-current.md"},"session_id":"%s","agent_id":"%s"}' \
21
- "$1" "$(json_escape "$(json_str_field "$INPUT" session_id)")" "$(json_escape "$(json_str_field "$INPUT" agent_id)")"
22
+ printf '{"tool_name":"%s","tool_input":{"file_path":"%s"},"session_id":"%s","agent_id":"%s"}' \
23
+ "$1" "$ROUND_FILE" "$(json_escape "$(json_str_field "$INPUT" session_id)")" "$(json_escape "$(json_str_field "$INPUT" agent_id)")"
22
24
  }
23
25
 
24
26
  TOOL_NAME="$(json_str_field "$INPUT" tool_name)"
@@ -35,10 +37,10 @@ fi
35
37
  if [ "$TOOL_NAME" = Bash ]; then
36
38
  CWD="$(json_str_field "$INPUT" cwd)"
37
39
  case "$COMMAND" in
38
- "cat $ROOT/.devlog/.round-current.md"|"cat \"$ROOT/.devlog/.round-current.md\"")
40
+ "cat $ROOT/$ROUND_FILE"|"cat \"$ROOT/$ROUND_FILE\"")
39
41
  INPUT="$(codex_access_payload Read)"
40
42
  ;;
41
- 'cat .devlog/.round-current.md'|'cat ".devlog/.round-current.md"')
43
+ "cat $ROUND_FILE"|"cat \"$ROUND_FILE\"")
42
44
  [ "$CWD" != "$ROOT" ] || INPUT="$(codex_access_payload Read)"
43
45
  ;;
44
46
  esac
@@ -51,7 +53,7 @@ if [ "$TOOL_NAME" = apply_patch ]; then
51
53
  CWD="$(json_str_field "$INPUT" cwd)"
52
54
  [ -n "$CWD" ] || CWD="$ROOT"
53
55
  PATCH="$COMMAND"
54
- if printf '%s\n' "$PATCH" | awk -v cwd="$CWD" -v root="$ROOT" '
56
+ if printf '%s\n' "$PATCH" | awk -v cwd="$CWD" -v root="$ROOT" -v round_file="$ROUND_FILE" '
55
57
  function normalize(path, count, parts, stack, depth, i, result) {
56
58
  if (substr(path, 1, 1) != "/") path = cwd "/" path
57
59
  count = split(path, parts, "/")
@@ -68,7 +70,7 @@ if [ "$TOOL_NAME" = apply_patch ]; then
68
70
  NR == 1 && $0 != "*** Begin Patch" { bad = 1 }
69
71
  /^\*\*\* (Add|Update|Delete) File: / {
70
72
  path = $0; sub(/^\*\*\* (Add|Update|Delete) File: /, "", path)
71
- if ($0 ~ /^\*\*\* Delete File: / || normalize(path) != normalize(root "/.devlog/.round-current.md")) bad = 1
73
+ if ($0 ~ /^\*\*\* Delete File: / || normalize(path) != normalize(root "/" round_file)) bad = 1
72
74
  files++
73
75
  }
74
76
  /^\*\*\* Move to: / { bad = 1 }
@@ -81,7 +83,7 @@ fi
81
83
  printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/segment-watch.sh"
82
84
  RC=$?
83
85
  if [ "$RC" -eq 2 ]; then
84
- printf 'Codex CLI:先用 cat "%s/.devlog/.round-current.md" 讀取,再用 apply_patch 只修改這份檔案;完成後重試原工具。\n' "$ROOT" >&2
86
+ printf 'Codex CLI:先用 cat "%s/%s" 讀取,再用 apply_patch 只修改這份檔案;完成後重試原工具。\n' "$ROOT" "$ROUND_FILE" >&2
85
87
  exit 2
86
88
  fi
87
89
  exit 0
@@ -11,6 +11,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
11
11
  INPUT="$(cat 2>/dev/null || true)"
12
12
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
13
13
  export DEVLOG_PROJECT_DIR="$ROOT"
14
+ export DEVLOG_PLATFORM=codex
14
15
 
15
16
  REASON=""
16
17
  if command -v jq >/dev/null 2>&1; then
@@ -16,6 +16,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
16
16
  INPUT="$(cat 2>/dev/null || true)"
17
17
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
18
18
  export DEVLOG_PROJECT_DIR="$ROOT"
19
+ export DEVLOG_PLATFORM=codex
19
20
 
20
21
  SRC=""
21
22
  if command -v jq >/dev/null 2>&1; then
@@ -12,6 +12,7 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" 2>/dev/null && pwd)"
12
12
  INPUT="$(cat 2>/dev/null || true)"
13
13
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
14
14
  export DEVLOG_PROJECT_DIR="$ROOT"
15
+ export DEVLOG_PLATFORM=codex
15
16
  printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/enforce-devlog.sh" >/dev/null
16
17
  RC=$?
17
18
  [ "$RC" -eq 2 ] && exit 2
@@ -15,5 +15,6 @@ case "$SCRIPT_DIR" in
15
15
  ;;
16
16
  esac
17
17
  export DEVLOG_PROJECT_DIR="$ROOT"
18
+ export DEVLOG_PLATFORM=codex
18
19
  printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/lessons-subagent-start.sh" || true
19
20
  exit 0
@@ -11,5 +11,6 @@ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
11
11
  INPUT="$(cat 2>/dev/null || true)"
12
12
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
13
13
  export DEVLOG_PROJECT_DIR="$ROOT"
14
+ export DEVLOG_PLATFORM=codex
14
15
  printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/round-start.sh"
15
16
  exit 0
@@ -39,14 +39,15 @@ EOF
39
39
  OUT="$(printf '{"cwd":"%s","source":"startup"}' "$TMP/project" | bash "$SCRIPT_DIR/on-session-start.sh")"
40
40
  case "$OUT" in *"Round 1"*) echo "PASS: sessionStart injects context" ;; *) echo "FAIL: sessionStart [$OUT]"; FAIL=1 ;; esac
41
41
 
42
- # userPromptSubmit: opens a Round in .round-current.md
42
+ # userPromptSubmit: opens a Round in .round-current@codex.md
43
43
  SUBMIT="$TMP/submit"
44
44
  mkdir -p "$SUBMIT/.devlog" "$SUBMIT/.devlog-tracker" "$SUBMIT/src"
45
45
  touch "$SUBMIT/.devlog/.enabled"
46
+ touch "$SUBMIT/.devlog/.platform-claimed"
46
47
  bash "$SCRIPT_DIR/on-user-prompt-submit.sh" <<EOF2 >/dev/null
47
48
  {"cwd":"$SUBMIT","prompt":"hello","session_id":"codex-1"}
48
49
  EOF2
49
- if grep -q 'hello' "$SUBMIT/.devlog/.round-current.md" 2>/dev/null; then
50
+ if grep -q 'hello' "$SUBMIT/.devlog/.round-current@codex.md" 2>/dev/null; then
50
51
  echo "PASS: userPromptSubmit writes round"
51
52
  else
52
53
  echo "FAIL: userPromptSubmit round"; FAIL=1
@@ -55,8 +56,8 @@ fi
55
56
  # preTool: blocks (exit 2) once the silence window has expired, allows fresh writes
56
57
  NOW="$(date +%s)"
57
58
  OLD=$((NOW - 1000))
58
- SUM="$(cksum < "$SUBMIT/.devlog/.round-current.md" | tr -d '\n')"
59
- printf '{"last_change_epoch": %s, "last_seen_cksum": "%s", "max_silent_seconds": 900, "session_id": "codex-1"}\n' "$OLD" "$SUM" > "$SUBMIT/.devlog/.segment-state"
59
+ SUM="$(cksum < "$SUBMIT/.devlog/.round-current@codex.md" | tr -d '\n')"
60
+ printf '{"last_change_epoch": %s, "last_seen_cksum": "%s", "max_silent_seconds": 900, "session_id": "codex-1"}\n' "$OLD" "$SUM" > "$SUBMIT/.devlog/.segment-state@codex"
60
61
  set +e
61
62
  ERR="$(printf '{"cwd":"%s","tool_name":"Bash","tool_input":{},"session_id":"codex-1"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh" 2>&1 >/dev/null)"
62
63
  RC=$?
@@ -68,20 +69,20 @@ case "$ERR" in
68
69
  esac
69
70
 
70
71
  set +e
71
- printf '{"cwd":"%s","tool_name":"Write","tool_input":{"file_path":"%s/.devlog/.round-current.md"},"session_id":"codex-1"}' "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh" >/dev/null 2>/dev/null
72
+ printf '{"cwd":"%s","tool_name":"Write","tool_input":{"file_path":"%s/.devlog/.round-current@codex.md"},"session_id":"codex-1"}' "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh" >/dev/null 2>/dev/null
72
73
  RC=$?
73
74
  set -e
74
75
  if [ "$RC" -eq 0 ]; then echo "PASS: devlog write allowed (exit 0)"; else echo "FAIL: allowed-tool exit [$RC]"; FAIL=1; fi
75
76
 
76
77
  set +e
77
- printf '{"cwd":"%s","tool_name":"Bash","tool_input":{"command":"cat %s/.devlog/.round-current.md"},"session_id":"codex-1"}' \
78
+ printf '{"cwd":"%s","tool_name":"Bash","tool_input":{"command":"cat %s/.devlog/.round-current@codex.md"},"session_id":"codex-1"}' \
78
79
  "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh" >/dev/null 2>/dev/null
79
80
  RC=$?
80
81
  set -e
81
82
  if [ "$RC" -eq 0 ]; then echo "PASS: devlog cat allowed"; else echo "FAIL: devlog cat exit [$RC]"; FAIL=1; fi
82
83
 
83
84
  set +e
84
- printf '{"cwd":"%s","tool_name":"Bash","tool_input":{"command":"cat %s/.devlog/.round-current.md; echo unsafe"},"session_id":"codex-1"}' \
85
+ printf '{"cwd":"%s","tool_name":"Bash","tool_input":{"command":"cat %s/.devlog/.round-current@codex.md; echo unsafe"},"session_id":"codex-1"}' \
85
86
  "$SUBMIT" "$SUBMIT" | bash "$SCRIPT_DIR/on-pre-tool.sh" >/dev/null 2>/dev/null
86
87
  RC=$?
87
88
  set -e
@@ -89,7 +90,7 @@ if [ "$RC" -eq 2 ]; then echo "PASS: chained cat blocked"; else echo "FAIL: chai
89
90
 
90
91
  # Codex reports file edits as apply_patch with the patch in tool_input.command.
91
92
  PATCH='*** Begin Patch
92
- *** Update File: .devlog/.round-current.md
93
+ *** Update File: .devlog/.round-current@codex.md
93
94
  @@
94
95
  -before
95
96
  +after
@@ -102,7 +103,7 @@ set -e
102
103
  if [ "$RC" -eq 0 ]; then echo "PASS: devlog apply_patch allowed"; else echo "FAIL: devlog apply_patch exit [$RC]"; FAIL=1; fi
103
104
 
104
105
  PATCH='*** Begin Patch
105
- *** Update File: ../.devlog/.round-current.md
106
+ *** Update File: ../.devlog/.round-current@codex.md
106
107
  @@
107
108
  -before
108
109
  +after
@@ -115,7 +116,7 @@ set -e
115
116
  if [ "$RC" -eq 0 ]; then echo "PASS: nested cwd devlog apply_patch allowed"; else echo "FAIL: nested cwd apply_patch exit [$RC]"; FAIL=1; fi
116
117
 
117
118
  PATCH='*** Begin Patch
118
- *** Update File: .devlog/.round-current.md
119
+ *** Update File: .devlog/.round-current@codex.md
119
120
  @@
120
121
  -before
121
122
  +after
@@ -128,7 +129,7 @@ set -e
128
129
  if [ "$RC" -eq 2 ]; then echo "PASS: nested cwd unrelated apply_patch blocked"; else echo "FAIL: nested cwd unrelated patch exit [$RC]"; FAIL=1; fi
129
130
 
130
131
  PATCH='*** Begin Patch
131
- *** Update File: .devlog/.round-current.md
132
+ *** Update File: .devlog/.round-current@codex.md
132
133
  @@
133
134
  -before
134
135
  +after
@@ -144,9 +145,18 @@ RC=$?
144
145
  set -e
145
146
  if [ "$RC" -eq 2 ]; then echo "PASS: mixed apply_patch blocked"; else echo "FAIL: mixed apply_patch exit [$RC]"; FAIL=1; fi
146
147
 
148
+ # A patch to Claude's round file is not Codex's own file: still blocked.
149
+ PATCH="$(printf '*** Begin Patch\n*** Update File: .devlog/.round-current.md\n@@\n-a\n+b\n*** End Patch')"
150
+ set +e
151
+ printf '{"cwd":"%s","tool_name":"apply_patch","tool_input":{"command":%s},"session_id":"codex-1"}' \
152
+ "$SUBMIT" "$(node -p 'JSON.stringify(process.argv[1])' "$PATCH")" | bash "$SCRIPT_DIR/on-pre-tool.sh" >/dev/null 2>/dev/null
153
+ RC=$?
154
+ set -e
155
+ if [ "$RC" -eq 2 ]; then echo "PASS: Claude round-file apply_patch blocked"; else echo "FAIL: Claude round-file apply_patch exit [$RC]"; FAIL=1; fi
156
+
147
157
  # stop: blocks (exit 2) while the round is unfinished
148
- printf '{"round": 1, "opened_at": "now"}\n' > "$SUBMIT/.devlog/.round-open"
149
- cksum < "$SUBMIT/.devlog/.round-current.md" > "$SUBMIT/.devlog/.turn-start"
158
+ printf '{"round": 1, "opened_at": "now"}\n' > "$SUBMIT/.devlog/.round-open@codex"
159
+ cksum < "$SUBMIT/.devlog/.round-current@codex.md" > "$SUBMIT/.devlog/.turn-start@codex"
150
160
  set +e
151
161
  printf '{"cwd":"%s"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-stop.sh" >/dev/null 2>/dev/null
152
162
  RC=$?
@@ -154,9 +164,9 @@ set -e
154
164
  if [ "$RC" -eq 2 ]; then echo "PASS: stop blocks unfinished round (exit 2)"; else echo "FAIL: stop exit [$RC]"; FAIL=1; fi
155
165
 
156
166
  # sessionEnd: closes the open round using the documented "reason"
157
- printf '\n## Round 2 — now\n\n### Status\nIN_PROGRESS\n' > "$SUBMIT/.devlog/.round-current.md"
158
- printf '{"round": 2, "opened_at": "now"}\n' > "$SUBMIT/.devlog/.round-open"
159
- cksum < "$SUBMIT/.devlog/.round-current.md" > "$SUBMIT/.devlog/.turn-start"
167
+ printf '\n## Round 2 — now\n\n### Status\nIN_PROGRESS\n' > "$SUBMIT/.devlog/.round-current@codex.md"
168
+ printf '{"round": 2, "opened_at": "now"}\n' > "$SUBMIT/.devlog/.round-open@codex"
169
+ cksum < "$SUBMIT/.devlog/.round-current@codex.md" > "$SUBMIT/.devlog/.turn-start@codex"
160
170
  printf '{"cwd":"%s","reason":"other"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-session-end.sh" >/dev/null
161
171
  if grep -q 'SessionEnd:other' "$SUBMIT/.devlog/devlog.md" 2>/dev/null; then
162
172
  echo "PASS: sessionEnd closes round via reason"
@@ -165,9 +175,9 @@ else
165
175
  fi
166
176
 
167
177
  # Interrupt closes the active round immediately, using Codex's event.
168
- printf '\n## Round 3 — now\n\n### Status\nIN_PROGRESS\n' > "$SUBMIT/.devlog/.round-current.md"
169
- printf '{"round": 3, "opened_at": "now"}\n' > "$SUBMIT/.devlog/.round-open"
170
- cksum < "$SUBMIT/.devlog/.round-current.md" > "$SUBMIT/.devlog/.turn-start"
178
+ printf '\n## Round 3 — now\n\n### Status\nIN_PROGRESS\n' > "$SUBMIT/.devlog/.round-current@codex.md"
179
+ printf '{"round": 3, "opened_at": "now"}\n' > "$SUBMIT/.devlog/.round-open@codex"
180
+ cksum < "$SUBMIT/.devlog/.round-current@codex.md" > "$SUBMIT/.devlog/.turn-start@codex"
171
181
  printf '{"cwd":"%s","hook_event_name":"Interrupt","turn_id":"turn-3"}' "$SUBMIT" | bash "$SCRIPT_DIR/on-interrupt.sh" >/dev/null
172
182
  if grep -q 'Interrupt:cancelled' "$SUBMIT/.devlog/devlog.md" 2>/dev/null; then
173
183
  echo "PASS: interrupt closes round"
package/commands/clean.md CHANGED
@@ -34,11 +34,11 @@ description: 無條件清空 .devlog/devlog.md(含專案摘要與所有 Round
34
34
 
35
35
  ```bash
36
36
  PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
37
- DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/clean-devlog.sh" --confirmed
37
+ DEVLOG_PLATFORM="<你所在的平台:claude/codex/cursor>" DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/clean-devlog.sh" --confirmed
38
38
  ```
39
39
  ```
40
40
 
41
- `--confirmed` 是必要參數,只有在使用者明確回覆「清空」之後才可以帶這個參數執行;不要在其他情況下跑這支腳本。腳本是唯一的實作來源:有沒有開著的 Round、要重編成 Round 1 還是整份刪除、重置 `.span-open` 與 checkpoint 狀態,都不要自己動手做。exit 1 時原樣顯示 stderr,不要自行重試、不要自己動手改檔案。
41
+ `--confirmed` 是必要參數,只有在使用者明確回覆「清空」之後才可以帶這個參數執行;不要在其他情況下跑這支腳本。腳本是唯一的實作來源:有沒有開著的 Round、要重編成 Round 1 還是整份刪除、重置 `.span-open` 與 checkpoint 狀態,都不要自己動手做。exit 1 時原樣顯示 stderr,不要自行重試、不要自己動手改檔案。同一工作樹有兩個以上平台的輪次開著時,腳本會以 `其他平台還有進行中的輪次(…),等它們收尾再執行。若該平台已不再使用,可先 pause 再 start 清掉它的標記。` 拒絕,請使用者等其他平台收尾再跑。
42
42
 
43
43
  ## 4. 處理結果並收尾這一輪
44
44
 
@@ -5,7 +5,7 @@ description: 讀取 .devlog/devlog.md,核對最後一輪 Handoff 的工作區
5
5
  請執行 devlog continue(接續上一題)。這是使用者主動執行 `/devlog-tracker:continue`,或明確說「continue」「接續」「繼續上一題」時才做的事。`/clear` 之後的一般新請求不要先讀檔接舊工作。
6
6
 
7
7
  1. 讀取 `.devlog/devlog.md`。若檔案不存在,告知「目前沒有 devlog 可接續」,不要建立 `.devlog/` 或任何新檔,結束。記下 `.devlog/` 的上層目錄(也就是專案根目錄)的絕對路徑,作為本輪接下來都要用的專案根目錄——後面步驟(尤其是步驟 5.1)一律沿用這個值,不要再用 shell 的 `pwd` 重新推。Bash 工具的工作目錄會在同一段對話裡的呼叫之間持續累積,中途若因為別的原因 `cd` 過,`pwd` 就不再代表這個專案根目錄。
8
- 2. 若 `.devlog/.span-open` 存在:先告訴使用者有一個還沒關的 span(Round 編號與 `opened_at`,若讀得到),問要繼續這個自動化任務還是先關掉它。沒有明確要關就當成要繼續,不要自己刪 `.span-open`。
8
+ 2. 若你所在平台的 span 檔存在(Claude Code 是 `.devlog/.span-open`,Codex/Cursor 是 `.devlog/.span-open@codex`/`.devlog/.span-open@cursor`):先告訴使用者有一個還沒關的 span(Round 編號與 `opened_at`,若讀得到),問要繼續這個自動化任務還是先關掉它。沒有明確要關就當成要繼續,不要自己刪 span 檔。
9
9
  3. 讀最近的 Round(不夠再往前讀;有 `## Checkpoint` 就一併看最後一個)。不要讀 `devlog.archive.md` 或 `devlog.<name>.md`,除非 Handoff 下一步明確指向它們。
10
10
  4. 依**最後一個歷史 Round**(不是這一輪 continue 自己的 skeleton)的 Status 行動。`DONE`:告訴使用者上一題已經結束,等新需求。不要核對、不要自己找下一件工作。
11
11
  5. `IN_PROGRESS`、`INTERRUPTED`、`BLOCKED`:先核對,再行動。不要先問「上次做到哪」。不要改歷史 Round。
@@ -14,7 +14,7 @@ description: 整理所有 devlog:跨主檔、所有分支檔、archive 與既
14
14
 
15
15
  ```bash
16
16
  PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
17
- DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/core/scripts/keep-all.sh" --scan
17
+ DEVLOG_PLATFORM="<你所在的平台:claude/codex/cursor>" DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/core/scripts/keep-all.sh" --scan
18
18
  ```
19
19
 
20
20
  - `NO_NODE`:告知 keep-all 需要 Node.js(可改用 `/devlog-tracker:keep` 整理目前分支),結束。
@@ -76,13 +76,13 @@ DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/core/scr
76
76
  然後:
77
77
 
78
78
  ```bash
79
- DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/core/scripts/keep-all.sh" \
79
+ DEVLOG_PLATFORM="<你所在的平台:claude/codex/cursor>" DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/core/scripts/keep-all.sh" \
80
80
  --apply "<專案根目錄絕對路徑>/.devlog/.keep-all-plan.tsv" --fingerprint <fp> --count <n>
81
81
  ```
82
82
 
83
83
  `<fp>`/`<n>` 用步驟 1 的值。不要自己搬檔、刪檔或改索引——腳本是唯一的實作:驗證計畫、把所有會動到的檔備份到 `.devlog/.keep-all-backup/<時間戳>/`、組好新檔、改寫來源、刪除被整理掉的 kept 檔、清空的 archive 與搬空的分支檔、重建 `## Kept 索引`(新索引行寫在目前分支的主檔)。搬空的分支檔只在該分支不是 `active` 時才刪;目前主檔與 `devlog.md` 一律不刪。
84
84
 
85
- - exit 1:原樣顯示 stderr,不要自行重試或手動補做。驗證錯誤時不會寫任何檔;fingerprint 不符代表掃描後檔案被改過,回到步驟 1 重新掃描。
85
+ - exit 1:原樣顯示 stderr,不要自行重試或手動補做。驗證錯誤時不會寫任何檔;fingerprint 不符代表掃描後檔案被改過,回到步驟 1 重新掃描。同一工作樹有兩個以上平台的輪次開著時,腳本會以 `其他平台還有進行中的輪次(…),等它們收尾再執行。若該平台已不再使用,可先 pause 再 start 清掉它的標記。` 拒絕,請使用者等其他平台收尾再跑。
86
86
  - 成功後刪除 `.devlog/.keep-all-plan.tsv`。
87
87
 
88
88
  ## 5. 選配:摘要
@@ -93,4 +93,4 @@ DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/core/scr
93
93
 
94
94
  依 stdout 回報:每個 `KEPT=<路徑> ROUNDS=<n>`、每個 `DELETED=<路徑>`、`BACKUP=<目錄>`(出錯時可從這裡還原),以及哪幾段套用了摘要。
95
95
 
96
- 若 `.devlog/.round-open` 存在:在開著的那一輪補上 `### Summary` / `### Handoff` / `### Status` 再結束(Stop hook 仍會檢查)。
96
+ 若你所在平台的 `.round-open` 檔(Claude Code 是 `.devlog/.round-open`,Codex/Cursor 是 `.devlog/.round-open@codex`/`.devlog/.round-open@cursor`)存在:在開著的那一輪補上 `### Summary` / `### Handoff` / `### Status` 再結束(Stop hook 仍會檢查)。
package/commands/keep.md CHANGED
@@ -11,7 +11,7 @@ description: 掃描整份 devlog.md,把值得留名的主題段落一次分別
11
11
  ## 1. 讀檔、找出開著的 Round
12
12
 
13
13
  1. 讀取 `.devlog/devlog.md` 全文。若檔案不存在,告知「目前沒有東西可 keep」,不要建立 `.devlog/` 或任何新檔,結束。
14
- 2. 開著的 Round:若 `.devlog/.round-open` 存在且有 `"round"` 數字,用那個編號。若 `.round-open` 不存在(從未 start 或已 pause),表示沒有開著的 Round,檔案裡所有 `## Round` 都是歷史,不要把最後一個 Round 當成開著的 Round 來排除。
14
+ 2. 開著的 Round:若你所在平台的 `.round-open` 檔(Claude Code 是 `.devlog/.round-open`,Codex/Cursor 是 `.devlog/.round-open@codex`/`.devlog/.round-open@cursor`)存在且有 `"round"` 數字,用那個編號。若它不存在(從未 start 或已 pause),表示沒有開著的 Round,檔案裡所有 `## Round` 都是歷史,不要把最後一個 Round 當成開著的 Round 來排除。
15
15
  3. 歷史 Round = 檔案裡除了開著的 Round 以外的所有 `## Round`(判斷 `## ` 標題時,略過圍欄程式碼區塊 ``` 內的行,與 hook 腳本解析方式一致)。若沒有任何歷史 Round,告知「目前沒有東西可 keep」,不要建立新檔,結束。
16
16
 
17
17
  ## 2. 切出主題段落,過濾瑣碎段落
@@ -84,13 +84,13 @@ description: 掃描整份 devlog.md,把值得留名的主題段落一次分別
84
84
 
85
85
  ```bash
86
86
  PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
87
- DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/keep-move.sh" \
87
+ DEVLOG_PLATFORM="<你所在的平台:claude/codex/cursor>" DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/keep-move.sh" \
88
88
  --from <from> --to <to> --name "<name>" --desc "<這段在做什麼的一句話>"
89
89
  ```
90
90
 
91
91
  `<from>`、`<to>`、`<name>` 必須使用該段步驟 4 確認後的值。`<這段在做什麼的一句話>` 用步驟 2.5 記下的那句描述(純文字,不要換行、不要含反引號);腳本會把它原樣接在 `## Kept 索引` 那一行的 `kept_at` 之後。腳本是搬移、Checkpoint 歸屬、full keep 重編與 checkpoint counter reset 的唯一實作來源;它一次只認一段範圍,完全不知道這是批次的一部分,`commands/keep.md` 自己負責依序呼叫。
92
92
 
93
- 任一段的腳本 exit 1:立刻停止整批,原樣顯示那一段的 stderr,不要自行重試刪除,也不要手動補做搬移,也不要繼續跑後面還沒處理的段落。已經成功搬走的段落不要回滾。
93
+ 任一段的腳本 exit 1:立刻停止整批,原樣顯示那一段的 stderr,不要自行重試刪除,也不要手動補做搬移,也不要繼續跑後面還沒處理的段落。已經成功搬走的段落不要回滾。同一工作樹有兩個以上平台的輪次開著時,腳本會以 `其他平台還有進行中的輪次(…),等它們收尾再執行。若該平台已不再使用,可先 pause 再 start 清掉它的標記。` 拒絕,請使用者等其他平台收尾再跑。
94
94
 
95
95
  如果這批段落最終涵蓋了 `devlog.md` 目前剩下的所有歷史 Round(沒有瑣碎段落留下),跑到最後一段(Round 號碼最新的那一段)時,腳本會自動判定為 full keep:專案摘要併入那一檔、開著的 Round 重編號成 1、`.span-open` 刪除、checkpoint counter 歸零。這是預期行為,不用特別處理,也不用另外判斷「這是不是 full keep」。
96
96
 
@@ -115,4 +115,4 @@ DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>"
115
115
 
116
116
  依段落回報:每個具名檔路徑、搬走幾輪(哪些編號);再依最後一次成功執行的 `REMAINING` 回報 `devlog.md` 目前剩幾輪(這個數字含還開著的那一輪,不是只算歷史 Round)。若中途因某段腳本失敗而中止,清楚列出哪些段落已經搬走(連檔名)、哪些完全沒嘗試。不要讀寫 `devlog.archive.md`。
117
117
 
118
- 若 `.round-open` 存在:在開著的那一輪補上 `### Summary` / `### Handoff` / `### Status` 再結束(Stop hook 仍會檢查)。若沒有開著的 Round:不要改寫歷史 Round 的收尾。
118
+ 若你所在平台的 `.round-open` 檔(Claude Code 是 `.round-open`,Codex/Cursor 是 `.round-open@codex`/`.round-open@cursor`)存在:在開著的那一輪補上 `### Summary` / `### Handoff` / `### Status` 再結束(Stop hook 仍會檢查)。若沒有開著的 Round:不要改寫歷史 Round 的收尾。
@@ -13,6 +13,6 @@ description: 把 .devlog 裡舊格式(#### 小節)的 Handoff/Session Hand
13
13
  - `NO_DEVLOG`:這個專案沒有 `.devlog/`,沒有東西要轉。
14
14
  - `LOCKED <pid>`:另一個 session 正在寫 devlog,稍後再跑一次。
15
15
  - 否則用 `MIGRATED=` 與 `SKIPPED=` 回報轉了幾輪、跳過幾輪;`BACKUP=` 是轉換前的備份。
16
- 3. 有 `SKIP ... Round <N>: <原因>` 時:歷史輪次保留原樣即可(讀取端相容舊格式)。只有**這一輪**(開著的 `.round-current.md`)被跳過時,才照 `skills/devlog-tracker/SKILL.md`「每一輪的紀錄格式」的 XML 模板手動改寫這一輪的 Handoff/Session Handoff。
16
+ 3. 有 `SKIP ... Round <N>: <原因>` 時:歷史輪次保留原樣即可(讀取端相容舊格式)。只有**這一輪**(你所在平台開著的 round 檔,見 SKILL「檔案位置」)被跳過時,才照 `skills/devlog-tracker/SKILL.md`「每一輪的紀錄格式」的 XML 模板手動改寫這一輪的 Handoff/Session Handoff。
17
17
 
18
- 只轉 `.round-current.md`、`devlog.md`、branch 檔(首行是 `<!-- devlog-origin: ... -->`)與 `handoff*.md`;不轉 `devlog.archive.md`、keep 產生的具名檔、lessons 檔。
18
+ 只轉每個平台的 round 檔(`.round-current.md`、`.round-current@*.md`)、`devlog.md`、branch 檔(首行是 `<!-- devlog-origin: ... -->`)與 `handoff*.md`(含 `handoff*@*.md`);不轉 `devlog.archive.md`、keep 產生的具名檔、lessons 檔。
package/commands/span.md CHANGED
@@ -4,10 +4,10 @@ description: 開啟或關閉 Span Mode,讓自動續接的長任務定期記錄
4
4
 
5
5
  先判斷使用者要開啟或關閉:
6
6
 
7
- - 使用者明確要求關閉,或 `.devlog/.span-open` 已存在且沒有明確要求重新開啟:
8
- 先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄),設 `PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"`,再跑 `DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/span-close.sh"`,
7
+ - 使用者明確要求關閉,或你所在平台的 span 檔(Claude Code 是 `.devlog/.span-open`,Codex/Cursor 是 `.devlog/.span-open@codex`/`.devlog/.span-open@cursor`)已存在且沒有明確要求重新開啟:
8
+ 先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄),設 `PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"`,再跑 `DEVLOG_PLATFORM="<你所在的平台:claude/codex/cursor>" DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/span-close.sh"`,
9
9
  再依 SKILL 的 span 收尾規則寫一個**新的 Round**,總結整段 span。
10
10
  - 使用者要求開啟:先把目前 Round 正常寫完,`Status` 設為 `IN_PROGRESS`,再跑
11
- `DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/span-open.sh"`。
11
+ `DEVLOG_PLATFORM="<你所在的平台:claude/codex/cursor>" DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/span-open.sh"`。
12
12
 
13
- 不要手寫 `.span-open` JSON。腳本 exit 1 時顯示 stderr,停止操作。
13
+ 不要手寫 span 檔的 JSON。腳本 exit 1 時顯示 stderr,停止操作。
@@ -8,7 +8,7 @@ description: 查看這個專案 devlog 強制記錄是否開著、span / checkpo
8
8
 
9
9
  ```bash
10
10
  PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
11
- DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/status-devlog.sh"
11
+ DEVLOG_PLATFORM="<你所在的平台:claude/codex/cursor>" DEVLOG_PROJECT_DIR="<專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/status-devlog.sh"
12
12
  ```
13
13
  ```
14
14
  把 stdout 翻譯成給人看的幾行(含 `LESSONS=yes/no`:Lessons Mode 開關狀態;`LESSONS_ADVISORY=<count>/<threshold>`:Lessons Mode 開著時,機制性訊號(工作區漂移不符、或
@@ -11,13 +11,13 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
11
11
 
12
12
  devlog_resolve_paths "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
13
13
  [ -f "$DEVLOG_DIR/.enabled" ] || { echo "NOT_ENABLED"; exit 1; }
14
- [ ! -e "$DEVLOG_DIR/.awaiting-reply" ] || { echo "ALREADY_OPEN"; exit 1; }
14
+ [ ! -e "$AWAITING_FILE" ] || { echo "ALREADY_OPEN"; exit 1; }
15
15
  ROUND=""
16
- [ ! -f "$DEVLOG_DIR/.round-open" ] || ROUND="$(json_int_get "$DEVLOG_DIR/.round-open" round)"
16
+ [ ! -f "$ROUND_OPEN" ] || ROUND="$(json_int_get "$ROUND_OPEN" round)"
17
17
  if [ -z "$ROUND" ] && [ -f "$DEVLOG_FILE" ]; then
18
- ROUND="$(devlog_list_round_starts "$DEVLOG_FILE" | awk 'END { print $2 }')"
18
+ ROUND="$(devlog_list_round_starts_of "$DEVLOG_FILE" "$DEVLOG_PLATFORM" | awk 'END { print $2 }')"
19
19
  fi
20
20
  [ -n "$ROUND" ] || { echo "NO_ROUND"; exit 1; }
21
21
  OPENED_AT="$(date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S%z')"
22
- printf '{"round": %s, "opened_at": "%s"}\n' "$ROUND" "$OPENED_AT" > "$DEVLOG_DIR/.awaiting-reply" || exit 1
22
+ printf '{"round": %s, "opened_at": "%s"}\n' "$ROUND" "$OPENED_AT" > "$AWAITING_FILE" || exit 1
23
23
  printf 'OPENED=%s\n' "$ROUND"
@@ -17,8 +17,12 @@ devlog_resolve_paths "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
17
17
  devlog_lock_acquire
18
18
  trap 'devlog_lock_release' EXIT
19
19
  MAIN="$DEVLOG_FILE"
20
- ROUND_OPEN="$DEVLOG_DIR/.round-open"
21
- ROUND_CURRENT="$DEVLOG_DIR/.round-current.md"
20
+ OPEN_ROW="$(devlog_single_open_round)" || exit 1
21
+ if [ -n "$OPEN_ROW" ]; then
22
+ OPEN_PLATFORM="${OPEN_ROW%% *}"
23
+ ROUND_OPEN="$DEVLOG_DIR/$(devlog_platform_file .round-open '' "$OPEN_PLATFORM")"
24
+ ROUND_CURRENT="$DEVLOG_DIR/$(devlog_platform_file .round-current .md "$OPEN_PLATFORM")"
25
+ fi
22
26
  # Post-split, devlog.md may legitimately not exist yet (a project's very
23
27
  # first round can still be open, with real content sitting only in
24
28
  # .round-current.md) — accept either devlog.md existing or .round-current.md
@@ -55,12 +59,14 @@ if [ "$HAS_OPEN" -eq 1 ]; then
55
59
  json_int_set "$ROUND_OPEN" round 1
56
60
  KEPT_ROUND=1
57
61
  else
58
- rm -f "$MAIN" "$ROUND_CURRENT" || exit 1
62
+ rm -f "$MAIN" "$DEVLOG_DIR"/.round-current.md "$DEVLOG_DIR"/.round-current@*.md || exit 1
59
63
  KEPT_ROUND=0
60
64
  fi
61
65
 
62
- rm -f "$DEVLOG_DIR/.span-open" "$DEVLOG_DIR/.interrupted" "$DEVLOG_DIR/.awaiting-reply"
63
- rm -f "$HANDOFF_FILE"
66
+ rm -f "$DEVLOG_DIR"/.span-open "$DEVLOG_DIR"/.span-open@* \
67
+ "$DEVLOG_DIR"/.interrupted "$DEVLOG_DIR"/.interrupted@* \
68
+ "$DEVLOG_DIR"/.awaiting-reply "$DEVLOG_DIR"/.awaiting-reply@*
69
+ rm -f "$HANDOFF_STEM.md" "$HANDOFF_STEM"@*.md
64
70
  if [ -f "$DEVLOG_DIR/.checkpoint-state" ]; then
65
71
  json_int_set "$DEVLOG_DIR/.checkpoint-state" rounds_since_checkpoint 0
66
72
  json_int_set "$DEVLOG_DIR/.checkpoint-state" checkpoint_marker_count 0
@@ -35,10 +35,6 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
35
35
  PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
36
36
  devlog_resolve_paths "$PROJECT_DIR"
37
37
  ENABLED_FLAG="$DEVLOG_DIR/.enabled"
38
- ROUND_OPEN="$DEVLOG_DIR/.round-open"
39
- INTERRUPTED_FLAG="$DEVLOG_DIR/.interrupted"
40
- ROUND_CURRENT="$DEVLOG_DIR/.round-current.md"
41
- TURN_MARKER="$DEVLOG_DIR/.turn-start"
42
38
 
43
39
  drop_markers() {
44
40
  rm -f "$ROUND_OPEN" "$INTERRUPTED_FLAG" 2>/dev/null || true