devlog-tracker 0.29.2 → 0.30.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/claude/hooks.json +21 -0
- package/core/scripts/lessons-subagent-done.sh +36 -0
- package/core/scripts/lessons-subagent-start.sh +36 -0
- package/core/scripts/round-start.sh +14 -0
- package/core/scripts/tests/test-lessons-subagent-hooks.sh +95 -0
- package/core/scripts/tests/test-round-start.sh +49 -0
- package/package.json +1 -1
- package/skills/devlog-tracker/SKILL.md +4 -2
- package/skills/devlog-tracker/references/lessons-mode.md +10 -4
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.30.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
|
|
|
@@ -220,7 +220,7 @@ Switching branches within the same working directory automatically splits the ma
|
|
|
220
220
|
|
|
221
221
|
#### Lessons Mode (off by default, not automatic)
|
|
222
222
|
|
|
223
|
-
When enabled, a development-lesson entry is only considered when a `BLOCKED` status resolves, an obvious detour happens, or workspace drift accumulates to a threshold (default 3, adjustable via `/devlog-tracker:lessons-drift <count>`); it's stored per-topic as `devlog.lessons.<topic>.md`, with `devlog.md` keeping only a heading index.
|
|
223
|
+
When enabled, a development-lesson entry is only considered when a `BLOCKED` status resolves, an obvious detour happens, or workspace drift accumulates to a threshold (default 3, adjustable via `/devlog-tracker:lessons-drift <count>`); it's stored per-topic as `devlog.lessons.<topic>.md`, with `devlog.md` keeping only a heading index. On Claude Code, subagents and Workflow agents get the `lessons-append.sh` usage (with absolute paths, so worktree isolation is safe) injected at `SubagentStart` and may record an entry themselves; when a subagent returns or a background task-notification arrives, the main session sees a `[Lessons Mode 提示]` advisory (`failed`/`killed` also count toward the shared counter). None of these signals enforce writing an entry, and this isn't a knowledge base (architectural decisions still live in `docs/design/*.md`). See [`docs/design/lessons-mode.md`](docs/design/lessons-mode.md).
|
|
224
224
|
|
|
225
225
|
## Tests
|
|
226
226
|
|
package/README.zh-TW.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
*[English](README.md) | 繁體中文*
|
|
4
4
|
|
|
5
|
-
**版本** 0.
|
|
5
|
+
**版本** 0.30.0
|
|
6
6
|
|
|
7
7
|
在專案中維護一份 `.devlog/devlog.md`,把每一輪對話的請求、決策與結果寫成永久紀錄。對話一 `/clear` 或換 session 就沒了;這份檔案取代那個缺口,讓工作可以中斷再接。沒下過 `/devlog-tracker:start` 時,裝著也不會動任何檔案。
|
|
8
8
|
|
|
@@ -220,7 +220,7 @@ Claude 用純文字結尾提出問題、下一則訊息才拿到答案時,不
|
|
|
220
220
|
|
|
221
221
|
#### Lessons Mode(預設關閉,不自動)
|
|
222
222
|
|
|
223
|
-
開著時,Status 從 `BLOCKED` 解開、明顯繞路,或工作區漂移累積達門檻(預設 3 次,`/devlog-tracker:lessons-drift <次數>` 可調)才考慮記一筆開發歷程教訓,per-topic 存成 `devlog.lessons.<topic>.md`,`devlog.md`
|
|
223
|
+
開著時,Status 從 `BLOCKED` 解開、明顯繞路,或工作區漂移累積達門檻(預設 3 次,`/devlog-tracker:lessons-drift <次數>` 可調)才考慮記一筆開發歷程教訓,per-topic 存成 `devlog.lessons.<topic>.md`,`devlog.md` 只留標題索引。Claude Code 上,sub agent/Workflow agent 開始時(`SubagentStart`)會被注入 `lessons-append.sh` 的用法(絕對路徑,worktree isolation 也不會寫錯地方),可以自己記一筆;sub agent 回來或背景任務通知到達時,主 session 會看到一句 `[Lessons Mode 提示]`(`failed`/`killed` 另算進共用計數器)。這些訊號都完全不 hook 強制寫入本身、不是知識庫(架構決策仍在 `docs/design/*.md`)。細節見 [`docs/design/lessons-mode.md`](docs/design/lessons-mode.md)。
|
|
224
224
|
|
|
225
225
|
## 測試
|
|
226
226
|
|
package/claude/hooks.json
CHANGED
|
@@ -31,6 +31,27 @@
|
|
|
31
31
|
]
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
|
+
"SubagentStart": [
|
|
35
|
+
{
|
|
36
|
+
"hooks": [
|
|
37
|
+
{
|
|
38
|
+
"type": "command",
|
|
39
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/core/scripts/lessons-subagent-start.sh\""
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"PostToolUse": [
|
|
45
|
+
{
|
|
46
|
+
"matcher": "Agent|Task",
|
|
47
|
+
"hooks": [
|
|
48
|
+
{
|
|
49
|
+
"type": "command",
|
|
50
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/core/scripts/lessons-subagent-done.sh\""
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
],
|
|
34
55
|
"PostToolUseFailure": [
|
|
35
56
|
{
|
|
36
57
|
"hooks": [
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# PostToolUse hook(Claude Code,matcher Agent|Task):前景 sub agent 跑完、
|
|
3
|
+
# 結果回到主 session 時,Lessons Mode 開著就用 additionalContext 提示主
|
|
4
|
+
# session 檢查 sub agent 訊號(docs/design/lessons-mode.md「sub agent/
|
|
5
|
+
# workflow 情境」)。背景 agent 在這裡只是剛啟動(tool_response.status 為
|
|
6
|
+
# async_launched),完成時改由 round-start.sh 的 task-notification 分支提示,
|
|
7
|
+
# 這裡略過;sub agent 自己再派 agent(payload 帶 agent_id)也略過。
|
|
8
|
+
# fail-open:任何問題都 exit 0、不輸出。
|
|
9
|
+
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
|
|
12
|
+
_src="${BASH_SOURCE[0]}"
|
|
13
|
+
HOOKS_DIR="$(cd "${_src%/*}" && pwd)"
|
|
14
|
+
# shellcheck source=json-field.sh
|
|
15
|
+
. "$HOOKS_DIR/json-field.sh"
|
|
16
|
+
PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
|
|
17
|
+
[ -f "$PROJECT_DIR/.devlog/.enabled" ] || exit 0
|
|
18
|
+
[ -f "$PROJECT_DIR/.devlog/.lessons-enabled" ] || exit 0
|
|
19
|
+
|
|
20
|
+
INPUT="$(cat 2>/dev/null || true)"
|
|
21
|
+
[ -n "$(json_str_field "$INPUT" agent_id)" ] && exit 0
|
|
22
|
+
|
|
23
|
+
ASYNC=0
|
|
24
|
+
if command -v jq >/dev/null 2>&1; then
|
|
25
|
+
if printf '%s' "$INPUT" | jq -e '(.tool_response.status? == "async_launched") or (.tool_response.isAsync? == true)' >/dev/null 2>&1; then
|
|
26
|
+
ASYNC=1
|
|
27
|
+
fi
|
|
28
|
+
else
|
|
29
|
+
case "$INPUT" in
|
|
30
|
+
*'"status":"async_launched"'*|*'"isAsync":true'*) ASYNC=1 ;;
|
|
31
|
+
esac
|
|
32
|
+
fi
|
|
33
|
+
[ "$ASYNC" -eq 1 ] && exit 0
|
|
34
|
+
|
|
35
|
+
printf '%s\n' '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"[Lessons Mode 提示] sub agent 完成。讀完回報後檢查:verify 推翻先前的 fix/claim、sub agent 自陳繞路、多個 agent 卡在類似問題、成果被打回票——有的話可考慮用 lessons-append.sh 記一筆(sub agent 若已自己記過就不用重複),非強制。"}}'
|
|
36
|
+
exit 0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SubagentStart hook(Claude Code):Lessons Mode 開著時,把「可以自己呼叫
|
|
3
|
+
# lessons-append.sh」的說明注入 sub agent/Workflow agent 的 context
|
|
4
|
+
# (docs/design/lessons-mode.md「sub agent/workflow 情境」)。
|
|
5
|
+
# sub agent 的 Bash 裡沒有 CLAUDE_PROJECT_DIR,isolation: "worktree" 時 cwd
|
|
6
|
+
# 還是 worktree(沒有 .devlog/),所以專案目錄跟腳本路徑都在這裡寫死成
|
|
7
|
+
# 主專案的絕對路徑。fail-open:任何問題都 exit 0、不輸出。
|
|
8
|
+
|
|
9
|
+
set -uo pipefail
|
|
10
|
+
|
|
11
|
+
_src="${BASH_SOURCE[0]}"
|
|
12
|
+
HOOKS_DIR="$(cd "${_src%/*}" && pwd)"
|
|
13
|
+
PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
|
|
14
|
+
[ -f "$PROJECT_DIR/.devlog/.enabled" ] || exit 0
|
|
15
|
+
[ -f "$PROJECT_DIR/.devlog/.lessons-enabled" ] || exit 0
|
|
16
|
+
PROJECT_DIR="$(cd "$PROJECT_DIR" 2>/dev/null && pwd)" || exit 0
|
|
17
|
+
cat >/dev/null 2>&1 || true
|
|
18
|
+
|
|
19
|
+
MSG="[devlog-tracker Lessons Mode] 這個專案開著 Lessons Mode:記錄開發**過程**踩過的坑(不是架構知識)。如果這次任務中你繞了一大圈才找到對的做法、先前的 fix/claim 被驗證推翻、或卡在某個值得後人避開的坑,可以直接記一筆(非強制,沒有就不用記):
|
|
20
|
+
|
|
21
|
+
DEVLOG_PROJECT_DIR='${PROJECT_DIR}' bash '${HOOKS_DIR}/lessons-append.sh' --topic '<kebab-case 主題,2–4 段>' --text '<一段自由散文:卡在哪、怎麼解開、下次怎麼避免>'
|
|
22
|
+
|
|
23
|
+
路徑都是絕對路徑,在 worktree 裡也照原樣用,不要改成相對路徑。有記的話,在最終回報裡用一句話說明記了哪個主題;腳本回報 NEW_TOPIC 時可改用它列出的既有主題重跑。"
|
|
24
|
+
|
|
25
|
+
json_escape() {
|
|
26
|
+
printf '%s' "$1" | awk '
|
|
27
|
+
BEGIN { ORS = "" }
|
|
28
|
+
{
|
|
29
|
+
gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); gsub(/\t/, "\\t"); gsub(/\r/, "\\r")
|
|
30
|
+
if (NR > 1) print "\\n"
|
|
31
|
+
print
|
|
32
|
+
}'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
printf '{"hookSpecificOutput":{"hookEventName":"SubagentStart","additionalContext":"%s"}}\n' "$(json_escape "$MSG")"
|
|
36
|
+
exit 0
|
|
@@ -123,6 +123,20 @@ if [ "$TASK_NOTIF" -eq 1 ]; then
|
|
|
123
123
|
else
|
|
124
124
|
PROMPT="$NOTIF_SUMMARY"
|
|
125
125
|
fi
|
|
126
|
+
|
|
127
|
+
# Lessons Mode:背景 sub agent/Workflow 完成時機械提示主 session 檢查
|
|
128
|
+
# sub agent 訊號(docs/design/lessons-mode.md「sub agent/workflow 情境」);
|
|
129
|
+
# failed/killed 另外算進共用計數器。span 開著也照印——長任務正是會派
|
|
130
|
+
# 背景 agent 的場景。
|
|
131
|
+
if [ -f "$DEVLOG_DIR/.lessons-enabled" ]; then
|
|
132
|
+
printf '\n[Lessons Mode 提示] 背景任務完成(status=%s)。讀完回報後檢查:verify 推翻先前的 fix/claim、sub agent 自陳繞路、多個 agent 卡在類似問題、成果被打回票——有的話可考慮用 lessons-append.sh 記一筆(sub agent 若已自己記過就不用重複),非強制。\n' "${NOTIF_STATUS:-unknown}"
|
|
133
|
+
case "$NOTIF_STATUS" in
|
|
134
|
+
failed|killed)
|
|
135
|
+
lessons_advisory_migrate "$DEVLOG_DIR"
|
|
136
|
+
lessons_advisory_bump "$ADVISORY_FILE"
|
|
137
|
+
;;
|
|
138
|
+
esac
|
|
139
|
+
fi
|
|
126
140
|
fi
|
|
127
141
|
|
|
128
142
|
if [ "$SPAN_SKIP" -eq 1 ]; then
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Self-check for lessons-subagent-start.sh (SubagentStart) and
|
|
3
|
+
# lessons-subagent-done.sh (PostToolUse Agent|Task). Run:
|
|
4
|
+
# bash core/scripts/tests/test-lessons-subagent-hooks.sh
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
8
|
+
TMP_ROOT="$(mktemp -d)"
|
|
9
|
+
trap 'rm -rf "$TMP_ROOT"' EXIT
|
|
10
|
+
|
|
11
|
+
export CLAUDE_PROJECT_DIR="$TMP_ROOT"
|
|
12
|
+
DEVLOG_DIR="$TMP_ROOT/.devlog"
|
|
13
|
+
mkdir -p "$DEVLOG_DIR"
|
|
14
|
+
|
|
15
|
+
FAIL=0
|
|
16
|
+
assert_contains() {
|
|
17
|
+
local desc="$1" needle="$2" haystack="$3"
|
|
18
|
+
case "$haystack" in
|
|
19
|
+
*"$needle"*) echo "PASS: $desc" ;;
|
|
20
|
+
*) echo "FAIL: $desc (missing: $needle)"; FAIL=1 ;;
|
|
21
|
+
esac
|
|
22
|
+
}
|
|
23
|
+
assert_empty() {
|
|
24
|
+
local desc="$1" out="$2"
|
|
25
|
+
if [ -z "$out" ]; then echo "PASS: $desc"; else echo "FAIL: $desc (got: $out)"; FAIL=1; fi
|
|
26
|
+
}
|
|
27
|
+
assert_valid_json() {
|
|
28
|
+
local desc="$1" out="$2"
|
|
29
|
+
if ! command -v jq >/dev/null 2>&1; then echo "SKIP: $desc (no jq)"; return 0; fi
|
|
30
|
+
if printf '%s' "$out" | jq -e . >/dev/null 2>&1; then echo "PASS: $desc"; else echo "FAIL: $desc (invalid JSON: $out)"; FAIL=1; fi
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
START_IN='{"session_id":"s1","cwd":"/elsewhere/.claude/worktrees/agent-x","agent_id":"a1","agent_type":"general-purpose","hook_event_name":"SubagentStart"}'
|
|
34
|
+
SYNC_DONE='{"session_id":"s1","hook_event_name":"PostToolUse","tool_name":"Agent","tool_input":{"prompt":"p"},"tool_response":{"status":"completed","content":[{"type":"text","text":"done"}]}}'
|
|
35
|
+
ASYNC_DONE='{"session_id":"s1","hook_event_name":"PostToolUse","tool_name":"Agent","tool_input":{"prompt":"p"},"tool_response":{"isAsync":true,"status":"async_launched","agentId":"a1"}}'
|
|
36
|
+
NESTED_DONE='{"session_id":"s1","agent_id":"a1","hook_event_name":"PostToolUse","tool_name":"Agent","tool_input":{"prompt":"p"},"tool_response":{"status":"completed"}}'
|
|
37
|
+
|
|
38
|
+
# --- disabled: both hooks silent -------------------------------------------
|
|
39
|
+
OUT="$(printf '%s' "$START_IN" | bash "$SCRIPT_DIR/lessons-subagent-start.sh")"
|
|
40
|
+
assert_empty "start: .enabled absent -> silent" "$OUT"
|
|
41
|
+
OUT="$(printf '%s' "$SYNC_DONE" | bash "$SCRIPT_DIR/lessons-subagent-done.sh")"
|
|
42
|
+
assert_empty "done: .enabled absent -> silent" "$OUT"
|
|
43
|
+
|
|
44
|
+
touch "$DEVLOG_DIR/.enabled"
|
|
45
|
+
OUT="$(printf '%s' "$START_IN" | bash "$SCRIPT_DIR/lessons-subagent-start.sh")"
|
|
46
|
+
assert_empty "start: lessons off -> silent" "$OUT"
|
|
47
|
+
OUT="$(printf '%s' "$SYNC_DONE" | bash "$SCRIPT_DIR/lessons-subagent-done.sh")"
|
|
48
|
+
assert_empty "done: lessons off -> silent" "$OUT"
|
|
49
|
+
|
|
50
|
+
touch "$DEVLOG_DIR/.lessons-enabled"
|
|
51
|
+
|
|
52
|
+
# --- SubagentStart: inject lessons-append.sh instructions with absolute paths
|
|
53
|
+
OUT="$(printf '%s' "$START_IN" | bash "$SCRIPT_DIR/lessons-subagent-start.sh")"
|
|
54
|
+
assert_valid_json "start: output is valid JSON" "$OUT"
|
|
55
|
+
assert_contains "start: hookEventName SubagentStart" '"hookEventName":"SubagentStart"' "$OUT"
|
|
56
|
+
assert_contains "start: additionalContext present" '"additionalContext":' "$OUT"
|
|
57
|
+
assert_contains "start: pins the main project dir, not the agent cwd" "DEVLOG_PROJECT_DIR='$TMP_ROOT'" "$OUT"
|
|
58
|
+
assert_contains "start: absolute lessons-append.sh path" "$SCRIPT_DIR/lessons-append.sh" "$OUT"
|
|
59
|
+
assert_contains "start: asks to mention it in the final report" "最終回報" "$OUT"
|
|
60
|
+
|
|
61
|
+
# relative CLAUDE_PROJECT_DIR still resolves to an absolute path
|
|
62
|
+
(cd "$TMP_ROOT" && OUT="$(printf '%s' "$START_IN" | CLAUDE_PROJECT_DIR=. bash "$SCRIPT_DIR/lessons-subagent-start.sh")"
|
|
63
|
+
case "$OUT" in *"DEVLOG_PROJECT_DIR='$TMP_ROOT'"*|*"DEVLOG_PROJECT_DIR='$(pwd -P)'"*) echo "PASS: start: relative project dir made absolute" ;;
|
|
64
|
+
*) echo "FAIL: start: relative project dir not made absolute ($OUT)"; exit 1 ;; esac) || FAIL=1
|
|
65
|
+
|
|
66
|
+
# path needing JSON escaping stays valid JSON
|
|
67
|
+
QDIR="$TMP_ROOT/we\"ird"
|
|
68
|
+
mkdir -p "$QDIR/.devlog"
|
|
69
|
+
touch "$QDIR/.devlog/.enabled" "$QDIR/.devlog/.lessons-enabled"
|
|
70
|
+
OUT="$(printf '%s' "$START_IN" | CLAUDE_PROJECT_DIR="$QDIR" bash "$SCRIPT_DIR/lessons-subagent-start.sh")"
|
|
71
|
+
assert_valid_json "start: quote in project path still valid JSON" "$OUT"
|
|
72
|
+
|
|
73
|
+
# --- PostToolUse(Agent): foreground completion only -------------------------
|
|
74
|
+
OUT="$(printf '%s' "$SYNC_DONE" | bash "$SCRIPT_DIR/lessons-subagent-done.sh")"
|
|
75
|
+
assert_valid_json "done: output is valid JSON" "$OUT"
|
|
76
|
+
assert_contains "done: hookEventName PostToolUse" '"hookEventName":"PostToolUse"' "$OUT"
|
|
77
|
+
assert_contains "done: sub agent advisory" "[Lessons Mode 提示] sub agent 完成" "$OUT"
|
|
78
|
+
|
|
79
|
+
OUT="$(printf '%s' "$ASYNC_DONE" | bash "$SCRIPT_DIR/lessons-subagent-done.sh")"
|
|
80
|
+
assert_empty "done: async launch -> silent (task-notification covers it)" "$OUT"
|
|
81
|
+
|
|
82
|
+
OUT="$(printf '%s' "$NESTED_DONE" | bash "$SCRIPT_DIR/lessons-subagent-done.sh")"
|
|
83
|
+
assert_empty "done: nested (agent_id set) -> silent" "$OUT"
|
|
84
|
+
|
|
85
|
+
OUT="$(printf '%s' 'not json' | bash "$SCRIPT_DIR/lessons-subagent-done.sh")"
|
|
86
|
+
RC=$?
|
|
87
|
+
[ "$RC" -eq 0 ] && echo "PASS: done: garbage stdin fails open" || { echo "FAIL: done: garbage stdin exit $RC"; FAIL=1; }
|
|
88
|
+
|
|
89
|
+
if [ "$FAIL" -eq 0 ]; then
|
|
90
|
+
echo "All checks passed."
|
|
91
|
+
exit 0
|
|
92
|
+
else
|
|
93
|
+
echo "Some checks FAILED."
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
@@ -1050,6 +1050,55 @@ assert_not_contains "lessons off: no BLOCKED-accumulation advisory" "[Lessons Mo
|
|
|
1050
1050
|
rm -rf "$NOLES_DIR"
|
|
1051
1051
|
export CLAUDE_PROJECT_DIR="$TMP_ROOT"
|
|
1052
1052
|
|
|
1053
|
+
# --- lessons: background agent / workflow task-notification advisory ------
|
|
1054
|
+
TN_DIR="$(mktemp -d)"
|
|
1055
|
+
mkdir -p "$TN_DIR/.devlog"
|
|
1056
|
+
touch "$TN_DIR/.devlog/.enabled" "$TN_DIR/.devlog/.lessons-enabled"
|
|
1057
|
+
export CLAUDE_PROJECT_DIR="$TN_DIR"
|
|
1058
|
+
tn_notif() {
|
|
1059
|
+
printf '{"prompt":"<task-notification><task-id>t9</task-id><status>%s</status><summary>Agent \\"x\\" finished</summary></task-notification>"}' "$1"
|
|
1060
|
+
}
|
|
1061
|
+
tn_reset() {
|
|
1062
|
+
rm -f "$TN_DIR/.devlog/.round-open" "$TN_DIR/.devlog/.round-current.md"
|
|
1063
|
+
printf '## Round 1 — 2026-09-23T00:00:00+0800\n\n### User Input\n```text\nx\n```\n\n### Summary\ns\n\n### Status\nDONE\n' > "$TN_DIR/.devlog/devlog.md"
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
tn_reset
|
|
1067
|
+
printf '%s\n' '{"count": 0, "threshold": 3}' > "$TN_DIR/.devlog/.lessons-advisory-state"
|
|
1068
|
+
OUT="$(tn_notif completed | bash "$SCRIPT_DIR/round-start.sh" 2>/dev/null)"
|
|
1069
|
+
assert_contains "task-notif completed: sub agent advisory printed" "[Lessons Mode 提示] 背景任務完成(status=completed)" "$OUT"
|
|
1070
|
+
grep -q '"count": 0' "$TN_DIR/.devlog/.lessons-advisory-state" && echo "PASS: task-notif completed does not bump counter" || { echo "FAIL: completed bumped counter"; FAIL=1; }
|
|
1071
|
+
|
|
1072
|
+
tn_reset
|
|
1073
|
+
OUT="$(tn_notif failed | bash "$SCRIPT_DIR/round-start.sh" 2>/dev/null)"
|
|
1074
|
+
assert_contains "task-notif failed: advisory printed" "背景任務完成(status=failed)" "$OUT"
|
|
1075
|
+
grep -q '"count": 1' "$TN_DIR/.devlog/.lessons-advisory-state" && echo "PASS: task-notif failed bumps counter" || { echo "FAIL: failed did not bump counter"; FAIL=1; }
|
|
1076
|
+
|
|
1077
|
+
tn_reset
|
|
1078
|
+
printf '%s\n' '{"count": 2, "threshold": 3}' > "$TN_DIR/.devlog/.lessons-advisory-state"
|
|
1079
|
+
OUT="$(tn_notif killed | bash "$SCRIPT_DIR/round-start.sh" 2>/dev/null)"
|
|
1080
|
+
assert_contains "task-notif killed at threshold: shared nudge printed" "流程訊號已累積出現 3 次" "$OUT"
|
|
1081
|
+
grep -q '"count": 0' "$TN_DIR/.devlog/.lessons-advisory-state" && echo "PASS: task-notif killed resets counter at threshold" || { echo "FAIL: killed did not reset counter"; FAIL=1; }
|
|
1082
|
+
|
|
1083
|
+
tn_reset
|
|
1084
|
+
printf '%s\n' '{"round": 1, "ticks_since_checkin": 0, "max_silent_ticks": 5}' > "$TN_DIR/.devlog/.span-open"
|
|
1085
|
+
OUT="$(tn_notif completed | bash "$SCRIPT_DIR/round-start.sh" 2>/dev/null)"
|
|
1086
|
+
assert_contains "task-notif during span: advisory still printed" "背景任務完成(status=completed)" "$OUT"
|
|
1087
|
+
rm -f "$TN_DIR/.devlog/.span-open"
|
|
1088
|
+
|
|
1089
|
+
tn_reset
|
|
1090
|
+
rm -f "$TN_DIR/.devlog/.lessons-enabled" "$TN_DIR/.devlog/.lessons-advisory-state"
|
|
1091
|
+
OUT="$(tn_notif failed | bash "$SCRIPT_DIR/round-start.sh" 2>/dev/null)"
|
|
1092
|
+
assert_not_contains "task-notif with lessons off: no advisory" "[Lessons Mode 提示]" "$OUT"
|
|
1093
|
+
[ ! -f "$TN_DIR/.devlog/.lessons-advisory-state" ] && echo "PASS: task-notif with lessons off leaves no advisory-state" || { echo "FAIL: advisory-state created while lessons off"; FAIL=1; }
|
|
1094
|
+
|
|
1095
|
+
touch "$TN_DIR/.devlog/.lessons-enabled"
|
|
1096
|
+
tn_reset
|
|
1097
|
+
OUT="$(printf '%s' '{"prompt":"plain message"}' | bash "$SCRIPT_DIR/round-start.sh" 2>/dev/null)"
|
|
1098
|
+
assert_not_contains "plain prompt: no background-task advisory" "背景任務完成" "$OUT"
|
|
1099
|
+
rm -rf "$TN_DIR"
|
|
1100
|
+
export CLAUDE_PROJECT_DIR="$TMP_ROOT"
|
|
1101
|
+
|
|
1053
1102
|
if [ "$FAIL" -eq 0 ]; then
|
|
1054
1103
|
echo "All checks passed."
|
|
1055
1104
|
exit 0
|
package/package.json
CHANGED
|
@@ -467,8 +467,10 @@ Handoff。核對用 `commands/continue.md` 步驟 5.1–5.2(不要跟著做 5.
|
|
|
467
467
|
若這輪任務是透過 Agent 工具派 sub agent,或用 Workflow 工具跑多階段 pipeline,一樣可能
|
|
468
468
|
踩到值得記的坑,只是沒有 Round/Status 可比對訊號;讀完 sub agent/workflow 的最終回報後
|
|
469
469
|
自我判斷(例如 verify 推翻了它先前的 fix、它自陳繞了一圈、多個 agent 重複卡在同一種問題、
|
|
470
|
-
或成果被打回票要求重做),值得的話一樣呼叫 `lessons-append.sh
|
|
471
|
-
|
|
470
|
+
或成果被打回票要求重做),值得的話一樣呼叫 `lessons-append.sh`。Claude Code 上 hook 會自動
|
|
471
|
+
觸發:sub agent/Workflow agent 開始時被注入 `lessons-append.sh` 的絕對路徑用法、可以自己記;
|
|
472
|
+
它回來或背景任務通知到達時,你會看到一句 `[Lessons Mode 提示]`(`failed`/`killed` 另算進
|
|
473
|
+
共用計數器)。sub agent 已記過的不用重複記。
|
|
472
474
|
|
|
473
475
|
寫法、per-topic 存檔規則、索引重建、機制性訊號細節,見
|
|
474
476
|
`${CLAUDE_PLUGIN_ROOT}/skills/devlog-tracker/references/lessons-mode.md`(完整
|
|
@@ -49,8 +49,14 @@ verify 階段推翻了 sub agent 先前的 fix/claim、sub agent 自陳繞了
|
|
|
49
49
|
裡多個 agent 各自卡在類似問題(彙整成一筆更有代表性的)、sub agent 的成果被使用者或
|
|
50
50
|
reviewer 打回票要求重做。
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
Claude Code 上 hook 會自動觸發(只在 Lessons Mode 開著時):
|
|
53
|
+
- sub agent/Workflow agent 開始時(`SubagentStart`),hook 會把 `lessons-append.sh` 的用法
|
|
54
|
+
連同**主專案絕對路徑**注入它的 context,它覺得值得就自己記一筆,並在最終回報裡說一聲。
|
|
55
|
+
worktree isolation 下也照那條絕對路徑指令跑,不會寫錯地方。
|
|
56
|
+
- 前景 sub agent 回來時(`PostToolUse`),或背景 sub agent/Workflow 的 task-notification
|
|
57
|
+
到達時(`round-start.sh`),你會看到一句 `[Lessons Mode 提示]`,提醒你檢查上面四種訊號;
|
|
58
|
+
背景任務 `status` 是 `failed`/`killed` 時還會算進共用計數器。
|
|
59
|
+
|
|
60
|
+
看到提示後:sub agent 已說明記過的主題不要重複記;多個 agent 卡在同一種問題時,由你彙整成
|
|
61
|
+
一筆更有代表性的。一樣全部非強制。Codex/Cursor 沒有對應 hook,仍是你自己判斷。詳見
|
|
56
62
|
`docs/design/lessons-mode.md`「sub agent/workflow 情境的自我判斷訊號」。
|