devlog-tracker 0.31.1 → 0.32.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  *English | [繁體中文](README.zh-TW.md)*
4
4
 
5
- **Version** 0.31.1
5
+ **Version** 0.32.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
 
@@ -233,7 +233,7 @@ When Claude ends a turn with a plain-text question and the next message is the a
233
233
 
234
234
  #### Per-branch devlog files
235
235
 
236
- Switching branches within the same working directory automatically splits the main file by the checked-out branch: `main`/`master` keeps using `.devlog/devlog.md`, while other branches each use `.devlog/devlog.<branch>.md` (slashes converted to `-`). A separate `git worktree` (a different directory) already has its own independent `.devlog/` and is unaffected by this mechanism. The first time a branch is detected without its own file while `devlog.md` already has content, it gets renamed (not copied) into that branch's file. See [`docs/design/branch-scoped-devlog.md`](docs/design/branch-scoped-devlog.md).
236
+ Switching branches within the same working directory automatically splits the main file by the checked-out branch: `main`/`master` keeps using `.devlog/devlog.md`, while other branches each use `.devlog/devlog.<branch>.md` (slashes converted to `-`). A separate `git worktree` (a different directory) already has its own independent `.devlog/` and is unaffected by this mechanism. The first time a branch is detected without its own file, only the unfinished tail of `devlog.md` (the Rounds after the last `DONE`, plus `handoff.md`) is cut into that branch's file; `main`'s own history, project summary, Checkpoints, and Kept/Lessons indexes stay in `devlog.md`. If the last Round is already `DONE`, or the branch checked out doesn't contain the current `main` tip (an older branch), nothing moves and the branch starts with an empty file. See [`docs/design/branch-scoped-devlog.md`](docs/design/branch-scoped-devlog.md).
237
237
 
238
238
  #### Lessons Mode (off by default, not automatic)
239
239
 
package/README.zh-TW.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  *[English](README.md) | 繁體中文*
4
4
 
5
- **版本** 0.31.1
5
+ **版本** 0.32.0
6
6
 
7
7
  在專案中維護一份 `.devlog/devlog.md`,把每一輪對話的請求、決策與結果寫成永久紀錄。對話一 `/clear` 或換 session 就沒了;這份檔案取代那個缺口,讓工作可以中斷再接。沒下過 `/devlog-tracker:start` 時,裝著也不會動任何檔案。
8
8
 
@@ -233,7 +233,7 @@ Claude 用純文字結尾提出問題、下一則訊息才拿到答案時,不
233
233
 
234
234
  #### 分支各自的 devlog 檔
235
235
 
236
- 同一個工作目錄裡切換分支時,主檔會依目前 checkout 的分支自動分開:`main`/`master` 繼續用 `.devlog/devlog.md`,其他分支各自用 `.devlog/devlog.<branch>.md`(斜線轉成 `-`)。另開一個 `git worktree`(不同目錄)本來就有自己獨立的 `.devlog/`,不受這個機制影響。第一次在某分支偵測到還沒有專屬檔案、且 `devlog.md` 已有內容時,會把它改名(非複製)成該分支的檔案。細節見 [`docs/design/branch-scoped-devlog.md`](docs/design/branch-scoped-devlog.md)。
236
+ 同一個工作目錄裡切換分支時,主檔會依目前 checkout 的分支自動分開:`main`/`master` 繼續用 `.devlog/devlog.md`,其他分支各自用 `.devlog/devlog.<branch>.md`(斜線轉成 `-`)。另開一個 `git worktree`(不同目錄)本來就有自己獨立的 `.devlog/`,不受這個機制影響。第一次在某分支偵測到還沒有專屬檔案時,只會把 `devlog.md` 裡還沒完成的尾巴(最後一個 `DONE` 之後的 Round,連同 `handoff.md`)剪到該分支的檔案;`main` 自己的歷史、專案摘要、Checkpoint、Kept/Lessons 索引都留在 `devlog.md`。最後一輪已經是 `DONE`,或切到的是不含目前 `main` 最新 commit 的舊分支時,什麼都不搬,新分支從空檔開始。細節見 [`docs/design/branch-scoped-devlog.md`](docs/design/branch-scoped-devlog.md)。
237
237
 
238
238
  #### Lessons Mode(預設關閉,不自動)
239
239
 
@@ -6,6 +6,8 @@
6
6
  _DEVLOG_PATH_DIR="$(cd "${BASH_SOURCE[0]%/*}" && pwd)"
7
7
  # shellcheck source=devlog-lock.sh
8
8
  . "$_DEVLOG_PATH_DIR/devlog-lock.sh"
9
+ # shellcheck source=devlog-md.sh
10
+ . "$_DEVLOG_PATH_DIR/devlog-md.sh"
9
11
 
10
12
  # Turns an arbitrary branch/worktree name into a safe devlog.<name>.md
11
13
  # filename segment: anything outside [A-Za-z0-9._-] becomes '-', repeats
@@ -14,6 +16,74 @@ _devlog_sanitize_name() {
14
16
  printf '%s' "$1" | sed -E 's/[^A-Za-z0-9._-]/-/g; s/-+/-/g; s/^-+//; s/-+$//'
15
17
  }
16
18
 
19
+ # Cuts the unfinished tail of $2 (the shared devlog.md) into $3 (a branch
20
+ # file that doesn't exist yet): every "## Round" block after the last one
21
+ # whose Status is DONE. Nothing moves when the last round is DONE, when
22
+ # there are no rounds, or when HEAD doesn't contain the local main/master
23
+ # tip (an older branch being checked out, not one just forked from the
24
+ # work in progress). The project header, Checkpoints, and Kept/Lessons
25
+ # indexes always stay in $2. When rounds move, $4 (handoff.md) moves to
26
+ # $5 too, since it snapshots that same unfinished work. Caller holds the
27
+ # devlog lock.
28
+ _devlog_migrate_unfinished_tail() {
29
+ local dir="$1" src="$2" dst="$3" src_handoff="$4" dst_handoff="$5"
30
+ [ -s "$src" ] || return 0
31
+
32
+ # Cheap early exit first: until the branch file exists, every hook
33
+ # re-resolves, so the common "main ended DONE" case must not scan every
34
+ # round each time.
35
+ local start end status
36
+ start="$(devlog_list_round_starts "$src" | awk 'END { print $1 }')"
37
+ [ -n "$start" ] || return 0
38
+ end="$(devlog_block_end "$src" "$start")"
39
+ status="$(devlog_round_status "$src" "$start" "$end" | tr -d '[:space:]')"
40
+ [ "$status" != DONE ] || return 0
41
+
42
+ local base
43
+ base="$(git -C "$dir" for-each-ref --format='%(refname:short)' refs/heads/ 2>/dev/null \
44
+ | grep -ixE 'main|master' | head -1)"
45
+ [ -n "$base" ] || return 0
46
+ git -C "$dir" merge-base --is-ancestor "$base" HEAD 2>/dev/null || return 0
47
+
48
+ local ranges=""
49
+ for start in $(devlog_list_round_starts "$src" | awk '{ print $1 }'); do
50
+ end="$(devlog_block_end "$src" "$start")"
51
+ status="$(devlog_round_status "$src" "$start" "$end" | tr -d '[:space:]')"
52
+ if [ "$status" = DONE ]; then ranges=""; else ranges="$ranges $start:$end"; fi
53
+ done
54
+ [ -n "$ranges" ] || return 0
55
+
56
+ # Split in one pass: lines inside a moved range go to $dst, the rest stay.
57
+ # Trailing blank lines are dropped from both so later appends keep the
58
+ # usual single-blank-line separator.
59
+ local drop_trailing_blanks='
60
+ /^[ \t]*$/ { pending = pending $0 ORS; next }
61
+ { printf "%s", pending; pending = ""; print }
62
+ '
63
+ awk -v ranges="$ranges" '
64
+ BEGIN {
65
+ n = split(ranges, r, " ")
66
+ for (i = 1; i <= n; i++) { split(r[i], se, ":"); s[i] = se[1]; e[i] = se[2] }
67
+ }
68
+ {
69
+ moved = 0
70
+ for (i = 1; i <= n; i++) if (NR >= s[i] && NR <= e[i]) { moved = 1; break }
71
+ print moved ? "M" $0 : "K" $0
72
+ }
73
+ ' "$src" > "$src.split" 2>/dev/null || { rm -f "$src.split"; return 0; }
74
+ sed -n 's/^M//p' "$src.split" | awk "$drop_trailing_blanks" > "$dst.tmp" 2>/dev/null
75
+ sed -n 's/^K//p' "$src.split" | awk "$drop_trailing_blanks" > "$src.tmp" 2>/dev/null
76
+ rm -f "$src.split"
77
+
78
+ # Branch file first: if the second mv fails the tail is duplicated, never lost.
79
+ if mv "$dst.tmp" "$dst" 2>/dev/null && mv "$src.tmp" "$src" 2>/dev/null; then
80
+ if [ -f "$src_handoff" ] && [ ! -f "$dst_handoff" ]; then
81
+ mv "$src_handoff" "$dst_handoff" 2>/dev/null || true
82
+ fi
83
+ fi
84
+ rm -f "$dst.tmp" "$src.tmp" 2>/dev/null || true
85
+ }
86
+
17
87
  # Sets DEVLOG_DIR, DEVLOG_FILE, and HANDOFF_FILE for the branch currently
18
88
  # checked out in $1 (defaults to "."). main/master (any case) and anything
19
89
  # that isn't a git repo or has no resolvable branch keep the shared
@@ -21,10 +91,9 @@ _devlog_sanitize_name() {
21
91
  # devlog.<sanitized-branch>.md and handoff.<sanitized-branch>.md; a
22
92
  # detached HEAD (or an unborn branch, which git also reports as "HEAD"
23
93
  # here) falls back to the working directory's own basename. The first
24
- # time a branch resolves to a file that doesn't exist yet while
25
- # devlog.md already has content, the existing devlog.md is renamed (not
26
- # copied) into that branch's file. handoff.md is never renamed on first
27
- # resolve (current-state snapshot, not history).
94
+ # time a branch resolves to a file that doesn't exist yet, only
95
+ # devlog.md's unfinished tail is cut into it (see
96
+ # _devlog_migrate_unfinished_tail); main's own history stays in devlog.md.
28
97
  devlog_resolve_paths() {
29
98
  local dir="${1:-.}"
30
99
  DEVLOG_DIR="$dir/.devlog"
@@ -66,12 +135,14 @@ devlog_resolve_paths() {
66
135
  esac
67
136
 
68
137
  local resolved="$DEVLOG_DIR/devlog.$name.md"
69
- if [ -f "$DEVLOG_DIR/.enabled" ] && [ ! -f "$resolved" ] && [ -f "$DEVLOG_FILE" ]; then
138
+ local resolved_handoff="$DEVLOG_DIR/handoff.$name.md"
139
+ if [ -f "$DEVLOG_DIR/.enabled" ] && [ ! -f "$resolved" ] && [ -s "$DEVLOG_FILE" ]; then
70
140
  devlog_lock_acquire
71
- mv "$DEVLOG_FILE" "$resolved" 2>/dev/null || true
141
+ [ -f "$resolved" ] || _devlog_migrate_unfinished_tail \
142
+ "$dir" "$DEVLOG_FILE" "$resolved" "$HANDOFF_FILE" "$resolved_handoff"
72
143
  devlog_lock_release
73
144
  fi
74
145
  DEVLOG_FILE="$resolved"
75
146
  # shellcheck disable=SC2034 # consumed by callers (e.g. enforce-devlog.sh), not used in this file
76
- HANDOFF_FILE="$DEVLOG_DIR/handoff.$name.md"
147
+ HANDOFF_FILE="$resolved_handoff"
77
148
  }
@@ -69,35 +69,124 @@ devlog_resolve_paths "$DETACHEDREPO"
69
69
  && echo "PASS: detached HEAD falls back to worktree dirname" \
70
70
  || { echo "FAIL: detached got $DEVLOG_FILE"; FAIL=1; }
71
71
 
72
- # --- migration: existing devlog.md content moves to branch file ----------
73
- MIGREPO="$TMP/migrepo"
74
- mkdir -p "$MIGREPO/.devlog"
75
- : > "$MIGREPO/.devlog/.enabled"
76
- git_setup "$MIGREPO"
77
- git -C "$MIGREPO" checkout -q -b feature-y
78
- echo "## Round 1 existing content" > "$MIGREPO/.devlog/devlog.md"
79
- devlog_resolve_paths "$MIGREPO"
80
- if [ "$DEVLOG_FILE" = "$MIGREPO/.devlog/devlog.feature-y.md" ] \
72
+ # Writes a devlog.md with a project header, the given rounds (as
73
+ # "<n>:<STATUS>" pairs), a Checkpoint between rounds 1 and 2, and a
74
+ # trailing Kept 索引.
75
+ write_main_devlog() {
76
+ local file="$1"; shift
77
+ {
78
+ printf '# 專案摘要\n\nheader line\n'
79
+ local pair n st
80
+ for pair in "$@"; do
81
+ n="${pair%%:*}"; st="${pair#*:}"
82
+ printf '\n## Round %s — t\n\n### Summary\nround %s body\n\n### Status\n%s\n' "$n" "$n" "$st"
83
+ [ "$n" = 1 ] && printf '\n## Checkpoint — t\n\ncheckpoint body\n'
84
+ done
85
+ printf '\n## Kept 索引\n\n- devlog.old.md — kept topic\n'
86
+ } > "$file"
87
+ }
88
+
89
+ # New repo on main with .enabled; leaves the caller on main.
90
+ mig_repo() {
91
+ mkdir -p "$1/.devlog"
92
+ : > "$1/.devlog/.enabled"
93
+ git_setup "$1"
94
+ git -C "$1" branch -M main
95
+ }
96
+
97
+ # --- main's last round DONE: new branch starts empty, devlog.md untouched --
98
+ DONEREPO="$TMP/donerepo"
99
+ mig_repo "$DONEREPO"
100
+ write_main_devlog "$DONEREPO/.devlog/devlog.md" 1:DONE 2:DONE
101
+ cp "$DONEREPO/.devlog/devlog.md" "$TMP/done-before.md"
102
+ echo "main handoff" > "$DONEREPO/.devlog/handoff.md"
103
+ git -C "$DONEREPO" checkout -q -b feature-done
104
+ devlog_resolve_paths "$DONEREPO"
105
+ if [ "$DEVLOG_FILE" = "$DONEREPO/.devlog/devlog.feature-done.md" ] \
106
+ && [ ! -f "$DEVLOG_FILE" ] \
107
+ && cmp -s "$DONEREPO/.devlog/devlog.md" "$TMP/done-before.md" \
108
+ && [ -f "$DONEREPO/.devlog/handoff.md" ] \
109
+ && [ ! -f "$DONEREPO/.devlog/handoff.feature-done.md" ]; then
110
+ echo "PASS: main ending DONE leaves devlog.md alone, branch starts empty"
111
+ else
112
+ echo "FAIL: DONE-tail migration moved something (file=$DEVLOG_FILE)"
113
+ FAIL=1
114
+ fi
115
+
116
+ # --- unfinished tail: rounds after the last DONE are cut into branch file --
117
+ TAILREPO="$TMP/tailrepo"
118
+ mig_repo "$TAILREPO"
119
+ write_main_devlog "$TAILREPO/.devlog/devlog.md" 1:DONE 2:DONE 3:BLOCKED 4:IN_PROGRESS
120
+ echo "main handoff" > "$TAILREPO/.devlog/handoff.md"
121
+ git -C "$TAILREPO" checkout -q -b feature-y
122
+ devlog_resolve_paths "$TAILREPO"
123
+ MAINF="$TAILREPO/.devlog/devlog.md"
124
+ if [ "$DEVLOG_FILE" = "$TAILREPO/.devlog/devlog.feature-y.md" ] \
81
125
  && [ -f "$DEVLOG_FILE" ] \
82
- && [ ! -f "$MIGREPO/.devlog/devlog.md" ] \
83
- && grep -q "existing content" "$DEVLOG_FILE"; then
84
- echo "PASS: migration renames devlog.md into branch file"
126
+ && [ "$(devlog_list_round_starts "$DEVLOG_FILE" | awk '{print $2}' | tr '\n' ' ')" = "3 4 " ] \
127
+ && [ "$(devlog_list_round_starts "$MAINF" | awk '{print $2}' | tr '\n' ' ')" = "1 2 " ] \
128
+ && ! grep -q "round 3 body\|round 4 body" "$MAINF" \
129
+ && grep -q "^# 專案摘要" "$MAINF" && ! grep -q "^# 專案摘要" "$DEVLOG_FILE" \
130
+ && grep -q "^## Checkpoint" "$MAINF" && ! grep -q "^## Checkpoint" "$DEVLOG_FILE" \
131
+ && grep -q "^## Kept 索引" "$MAINF" && ! grep -q "^## Kept 索引" "$DEVLOG_FILE" \
132
+ && [ ! -f "$TAILREPO/.devlog/handoff.md" ] \
133
+ && grep -q "main handoff" "$TAILREPO/.devlog/handoff.feature-y.md"; then
134
+ echo "PASS: unfinished tail (rounds after last DONE) cut into branch file with handoff"
85
135
  else
86
- echo "FAIL: migration file=$DEVLOG_FILE"
136
+ echo "FAIL: tail migration (file=$DEVLOG_FILE)"
137
+ echo "--- devlog.md"; cat "$MAINF"; echo "--- branch"; cat "$DEVLOG_FILE" 2>/dev/null
87
138
  FAIL=1
88
139
  fi
89
140
 
90
- # --- migration is idempotent: second call doesn't re-move or error -------
91
- echo "## Round 2 written after migration" >> "$MIGREPO/.devlog/devlog.feature-y.md"
92
- devlog_resolve_paths "$MIGREPO"
93
- if [ "$DEVLOG_FILE" = "$MIGREPO/.devlog/devlog.feature-y.md" ] \
94
- && grep -q "Round 2 written after migration" "$DEVLOG_FILE"; then
141
+ # --- tail migration is idempotent: branch file exists, nothing re-moves ----
142
+ echo "## Round 5 written after migration" >> "$TAILREPO/.devlog/devlog.feature-y.md"
143
+ cp "$MAINF" "$TMP/tail-main-after.md"
144
+ devlog_resolve_paths "$TAILREPO"
145
+ if [ "$DEVLOG_FILE" = "$TAILREPO/.devlog/devlog.feature-y.md" ] \
146
+ && grep -q "Round 5 written after migration" "$DEVLOG_FILE" \
147
+ && cmp -s "$MAINF" "$TMP/tail-main-after.md"; then
95
148
  echo "PASS: second resolve is a no-op, content preserved"
96
149
  else
97
150
  echo "FAIL: second resolve altered state, got $DEVLOG_FILE"
98
151
  FAIL=1
99
152
  fi
100
153
 
154
+ # --- no DONE round at all: every round moves, header/index stay ----------
155
+ NODONEREPO="$TMP/nodonerepo"
156
+ mig_repo "$NODONEREPO"
157
+ write_main_devlog "$NODONEREPO/.devlog/devlog.md" 1:INTERRUPTED 2:IN_PROGRESS
158
+ git -C "$NODONEREPO" checkout -q -b feature-n
159
+ devlog_resolve_paths "$NODONEREPO"
160
+ if [ "$(devlog_list_round_starts "$DEVLOG_FILE" 2>/dev/null | awk '{print $2}' | tr '\n' ' ')" = "1 2 " ] \
161
+ && [ -z "$(devlog_list_round_starts "$NODONEREPO/.devlog/devlog.md")" ] \
162
+ && grep -q "^## Kept 索引" "$NODONEREPO/.devlog/devlog.md" \
163
+ && grep -q "^## Checkpoint" "$NODONEREPO/.devlog/devlog.md"; then
164
+ echo "PASS: with no DONE round, all rounds move and non-round sections stay"
165
+ else
166
+ echo "FAIL: no-DONE migration (file=$DEVLOG_FILE)"
167
+ FAIL=1
168
+ fi
169
+
170
+ # --- stale branch (doesn't contain main's tip): nothing moves -------------
171
+ STALEREPO="$TMP/stalerepo"
172
+ mig_repo "$STALEREPO"
173
+ git -C "$STALEREPO" branch old-branch
174
+ git -C "$STALEREPO" -c user.email=t@t.t -c user.name=t commit -q --allow-empty -m "main moves on"
175
+ write_main_devlog "$STALEREPO/.devlog/devlog.md" 1:DONE 2:IN_PROGRESS
176
+ cp "$STALEREPO/.devlog/devlog.md" "$TMP/stale-before.md"
177
+ echo "main handoff" > "$STALEREPO/.devlog/handoff.md"
178
+ git -C "$STALEREPO" checkout -q old-branch
179
+ devlog_resolve_paths "$STALEREPO"
180
+ if [ "$DEVLOG_FILE" = "$STALEREPO/.devlog/devlog.old-branch.md" ] \
181
+ && [ ! -f "$DEVLOG_FILE" ] \
182
+ && cmp -s "$STALEREPO/.devlog/devlog.md" "$TMP/stale-before.md" \
183
+ && [ -f "$STALEREPO/.devlog/handoff.md" ]; then
184
+ echo "PASS: checking out a branch that lacks main's tip moves nothing"
185
+ else
186
+ echo "FAIL: stale branch took main's tail (file=$DEVLOG_FILE)"
187
+ FAIL=1
188
+ fi
189
+
101
190
  # --- migration does NOT fire when .devlog/.enabled is absent (Fix 6) -----
102
191
  NOENABLEDREPO="$TMP/noenabledrepo"
103
192
  mkdir -p "$NOENABLEDREPO/.devlog"
@@ -140,23 +229,5 @@ devlog_resolve_paths "$DETACHEDREPO"
140
229
  && echo "PASS: detached HANDOFF_FILE uses worktree dirname" \
141
230
  || { echo "FAIL: detached HANDOFF_FILE=$HANDOFF_FILE"; FAIL=1; }
142
231
 
143
- # migration must NOT move handoff.md
144
- MIGHO="$TMP/mighandoff"
145
- mkdir -p "$MIGHO/.devlog"
146
- : > "$MIGHO/.devlog/.enabled"
147
- git_setup "$MIGHO"
148
- git -C "$MIGHO" checkout -q -b feature-h
149
- echo "old handoff" > "$MIGHO/.devlog/handoff.md"
150
- echo "## Round 1" > "$MIGHO/.devlog/devlog.md"
151
- devlog_resolve_paths "$MIGHO"
152
- if [ -f "$MIGHO/.devlog/handoff.md" ] \
153
- && [ ! -f "$MIGHO/.devlog/handoff.feature-h.md" ] \
154
- && [ "$HANDOFF_FILE" = "$MIGHO/.devlog/handoff.feature-h.md" ]; then
155
- echo "PASS: handoff.md is not renamed on first branch resolve"
156
- else
157
- echo "FAIL: handoff migration diverged (HANDOFF_FILE=$HANDOFF_FILE)"
158
- FAIL=1
159
- fi
160
-
161
232
  if [ "$FAIL" -eq 0 ]; then echo "All checks passed."; exit 0
162
233
  else echo "Some checks FAILED."; exit 1; fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devlog-tracker",
3
- "version": "0.31.1",
3
+ "version": "0.32.0",
4
4
  "description": "npx installer for devlog-tracker's Claude Code, Cursor and Codex adapters.",
5
5
  "bin": {
6
6
  "devlog-tracker": "bin/devlog-tracker.js"
@@ -70,7 +70,7 @@ Summary/Handoff」——後面這句要整句刪掉,不是縮短。
70
70
  ## 檔案位置
71
71
 
72
72
  - 主檔:`.devlog/devlog.md`——在 `main`/`master` 分支上工作時使用
73
- - 分支主檔:`.devlog/devlog.<branch>.md`——在同一個 worktree 裡切換到其他分支時,主檔會依目前 checkout 的分支自動分開(斜線轉成 `-`);detached HEAD 退回用 worktree 目錄名。另開一個 `git worktree`(不同目錄)本來就有自己獨立的 `.devlog/`,不受這個機制影響。第一次在某分支偵測到還沒有專屬檔案、且 `.devlog/devlog.md` 已有內容時,會把它改名(非複製)成該分支的檔案。細節見 `docs/design/branch-scoped-devlog.md`。
73
+ - 分支主檔:`.devlog/devlog.<branch>.md`——在同一個 worktree 裡切換到其他分支時,主檔會依目前 checkout 的分支自動分開(斜線轉成 `-`);detached HEAD 退回用 worktree 目錄名。另開一個 `git worktree`(不同目錄)本來就有自己獨立的 `.devlog/`,不受這個機制影響。第一次在某分支偵測到還沒有專屬檔案時,只會把 `devlog.md` 裡還沒完成的尾巴(最後一個 `DONE` 之後的 Round,連同 `handoff.md`)剪到該分支的檔案;`main` 自己的歷史、專案摘要、Checkpoint、Kept/Lessons 索引都留在 `devlog.md`。最後一輪已經是 `DONE`,或切到的是不含目前 `main` 最新 commit 的舊分支時,什麼都不搬,新分支從空檔開始。細節見 `docs/design/branch-scoped-devlog.md`。
74
74
  - 歸檔:`.devlog/devlog.archive.md`
75
75
  - 具名保存:`.devlog/devlog.<name>.md`(`/devlog-tracker:keep` 搬走的主題檔;SessionStart 不讀這些檔)
76
76
  - 當輪暫存:`.devlog/.round-current.md`(目前開著的那一輪,Claude 該讀寫的是這個檔,不是 `devlog.md`;