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 +2 -2
- package/README.zh-TW.md +2 -2
- package/core/scripts/devlog-path.sh +78 -7
- package/core/scripts/tests/test-devlog-path.sh +107 -36
- package/package.json +1 -1
- package/skills/devlog-tracker/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
*English | [繁體中文](README.zh-TW.md)*
|
|
4
4
|
|
|
5
|
-
**Version** 0.
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
25
|
-
# devlog.md
|
|
26
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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="$
|
|
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
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
&& [
|
|
83
|
-
&&
|
|
84
|
-
|
|
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:
|
|
91
|
-
echo "## Round
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
@@ -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
|
|
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`;
|