devlog-tracker 0.25.1 → 0.27.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 (117) hide show
  1. package/README.md +43 -72
  2. package/claude/hooks.json +75 -0
  3. package/cli/agents-md.js +14 -8
  4. package/cli/agents-md.test.js +21 -1
  5. package/cli/init.js +6 -4
  6. package/cli/init.test.js +5 -4
  7. package/cli/merge-hooks.js +5 -2
  8. package/cli/merge-hooks.test.js +52 -0
  9. package/cli/platforms/claude.js +55 -0
  10. package/cli/platforms/claude.test.js +178 -0
  11. package/cli/platforms/codex.js +6 -18
  12. package/cli/skills-from-commands.js +32 -0
  13. package/cli/skills-from-commands.test.js +51 -0
  14. package/cli/vendor.js +8 -1
  15. package/cli/vendor.test.js +24 -5
  16. package/codex/hooks/on-pre-tool.sh +3 -3
  17. package/codex/hooks/on-session-end.sh +2 -2
  18. package/codex/hooks/on-session-start.sh +2 -2
  19. package/codex/hooks/on-stop.sh +3 -3
  20. package/codex/hooks/on-user-prompt-submit.sh +2 -2
  21. package/codex/hooks/test-adapters.sh +2 -2
  22. package/commands/checkpoint.md +2 -2
  23. package/commands/clean.md +3 -3
  24. package/commands/compact.md +3 -3
  25. package/commands/continue.md +4 -4
  26. package/commands/keep.md +3 -3
  27. package/commands/lessons-drift.md +2 -2
  28. package/commands/lessons-off.md +3 -3
  29. package/commands/lessons-on.md +3 -3
  30. package/commands/lessons.md +3 -3
  31. package/commands/overview.md +3 -3
  32. package/commands/pause.md +3 -3
  33. package/commands/resume.md +3 -3
  34. package/commands/segment-watch.md +3 -3
  35. package/commands/span.md +2 -2
  36. package/commands/start.md +3 -3
  37. package/commands/status.md +3 -3
  38. package/{hooks → core}/scripts/await-open.sh +1 -1
  39. package/{hooks → core}/scripts/checkpoint-set.sh +1 -1
  40. package/{hooks → core}/scripts/clean-devlog.sh +1 -1
  41. package/{hooks → core}/scripts/close-open-round.sh +1 -1
  42. package/{hooks → core}/scripts/compact-devlog.sh +1 -1
  43. package/{hooks → core}/scripts/enforce-devlog.sh +1 -1
  44. package/{hooks → core}/scripts/keep-move.sh +1 -1
  45. package/{hooks → core}/scripts/kept-list.sh +1 -1
  46. package/{hooks → core}/scripts/lessons-append.sh +2 -2
  47. package/{hooks → core}/scripts/lessons-drift-set.sh +1 -1
  48. package/{hooks → core}/scripts/lessons-off.sh +1 -1
  49. package/{hooks → core}/scripts/lessons-on.sh +1 -1
  50. package/{hooks → core}/scripts/lessons-read.sh +1 -1
  51. package/{hooks → core}/scripts/on-tool-failure.sh +1 -1
  52. package/{hooks → core}/scripts/pause-devlog.sh +1 -1
  53. package/core/scripts/project-dir.sh +19 -0
  54. package/{hooks → core}/scripts/resume-devlog.sh +1 -1
  55. package/{hooks → core}/scripts/round-start.sh +1 -1
  56. package/{hooks → core}/scripts/run-tests.sh +1 -1
  57. package/{hooks → core}/scripts/segment-watch-set.sh +1 -1
  58. package/{hooks → core}/scripts/segment-watch.sh +1 -1
  59. package/{hooks → core}/scripts/session-start-devlog.sh +1 -1
  60. package/{hooks → core}/scripts/span-close.sh +1 -1
  61. package/{hooks → core}/scripts/span-open.sh +1 -1
  62. package/{hooks → core}/scripts/start-devlog.sh +1 -1
  63. package/{hooks → core}/scripts/status-devlog.sh +1 -1
  64. package/core/scripts/tests/test-cli-init-e2e.sh +68 -0
  65. package/core/scripts/tests/test-project-dir.sh +30 -0
  66. package/{hooks → core}/scripts/tests/test-session-start-devlog.sh +2 -2
  67. package/{hooks → core}/scripts/workspace-snapshot.sh +2 -2
  68. package/cursor/hooks/on-pre-tool.sh +2 -2
  69. package/cursor/hooks/on-session-end.sh +2 -2
  70. package/cursor/hooks/on-session-start.sh +2 -2
  71. package/cursor/hooks/on-stop.sh +2 -2
  72. package/cursor/hooks/on-submit-prompt.sh +2 -2
  73. package/cursor/hooks/on-tool-failure.sh +2 -2
  74. package/package.json +4 -3
  75. package/skills/devlog-tracker/SKILL.md +9 -9
  76. package/skills/devlog-tracker/references/lessons-mode.md +1 -1
  77. package/skills/devlog-tracker/references/reply-fold.md +1 -1
  78. package/hooks/scripts/tests/test-cli-init-e2e.sh +0 -38
  79. /package/{hooks → core}/scripts/detect-pending-question.sh +0 -0
  80. /package/{hooks → core}/scripts/devlog-lock.sh +0 -0
  81. /package/{hooks → core}/scripts/devlog-md.sh +0 -0
  82. /package/{hooks → core}/scripts/devlog-path.sh +0 -0
  83. /package/{hooks → core}/scripts/files-snapshot.sh +0 -0
  84. /package/{hooks → core}/scripts/json-field.sh +0 -0
  85. /package/{hooks → core}/scripts/on-session-end.sh +0 -0
  86. /package/{hooks → core}/scripts/on-stop-failure.sh +0 -0
  87. /package/{hooks → core}/scripts/redact-prompt.sh +0 -0
  88. /package/{hooks → core}/scripts/tests/test-await-open.sh +0 -0
  89. /package/{hooks → core}/scripts/tests/test-branch-scoped-integration.sh +0 -0
  90. /package/{hooks → core}/scripts/tests/test-checkpoint-set.sh +0 -0
  91. /package/{hooks → core}/scripts/tests/test-clean-devlog.sh +0 -0
  92. /package/{hooks → core}/scripts/tests/test-close-open-round.sh +0 -0
  93. /package/{hooks → core}/scripts/tests/test-compact-devlog.sh +0 -0
  94. /package/{hooks → core}/scripts/tests/test-devlog-lock.sh +0 -0
  95. /package/{hooks → core}/scripts/tests/test-devlog-md.sh +0 -0
  96. /package/{hooks → core}/scripts/tests/test-devlog-path.sh +0 -0
  97. /package/{hooks → core}/scripts/tests/test-enforce-devlog-files.sh +0 -0
  98. /package/{hooks → core}/scripts/tests/test-enforce-devlog-handoff-order.sh +0 -0
  99. /package/{hooks → core}/scripts/tests/test-enforce-devlog-workspace.sh +0 -0
  100. /package/{hooks → core}/scripts/tests/test-enforce-devlog.sh +0 -0
  101. /package/{hooks → core}/scripts/tests/test-files-snapshot.sh +0 -0
  102. /package/{hooks → core}/scripts/tests/test-json-field.sh +0 -0
  103. /package/{hooks → core}/scripts/tests/test-keep-move.sh +0 -0
  104. /package/{hooks → core}/scripts/tests/test-kept-list.sh +0 -0
  105. /package/{hooks → core}/scripts/tests/test-lessons-append.sh +0 -0
  106. /package/{hooks → core}/scripts/tests/test-lessons-drift-set.sh +0 -0
  107. /package/{hooks → core}/scripts/tests/test-lessons-on-off.sh +0 -0
  108. /package/{hooks → core}/scripts/tests/test-lessons-read.sh +0 -0
  109. /package/{hooks → core}/scripts/tests/test-on-interrupt.sh +0 -0
  110. /package/{hooks → core}/scripts/tests/test-redact-prompt.sh +0 -0
  111. /package/{hooks → core}/scripts/tests/test-resume-devlog.sh +0 -0
  112. /package/{hooks → core}/scripts/tests/test-round-start.sh +0 -0
  113. /package/{hooks → core}/scripts/tests/test-segment-watch-set.sh +0 -0
  114. /package/{hooks → core}/scripts/tests/test-segment-watch.sh +0 -0
  115. /package/{hooks → core}/scripts/tests/test-start-pause-devlog.sh +0 -0
  116. /package/{hooks → core}/scripts/tests/test-status-span.sh +0 -0
  117. /package/{hooks → core}/scripts/tests/test-workspace-snapshot.sh +0 -0
@@ -8,11 +8,11 @@ description: 把已 keep 的具名檔(devlog.<name>.md)整合成一份跨主
8
8
 
9
9
  ## 1. 取得已 keep 的檔案清單
10
10
 
11
- 記下你目前已經確認的專案根目錄絕對路徑(後面步驟都要用這個值,不要用 `$(pwd)` 重新推——理由同 `commands/continue.md` 步驟 1)。先決定 plugin 根目錄(有 `CLAUDE_PLUGIN_ROOT` 用它;否則用 `DEVLOG_TRACKER_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄),再跑:
11
+ 記下你目前已經確認的專案根目錄絕對路徑(後面步驟都要用這個值,不要用 `$(pwd)` 重新推——理由同 `commands/continue.md` 步驟 1)。先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄),再跑:
12
12
 
13
13
  ```bash
14
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${DEVLOG_TRACKER_ROOT:-}}"
15
- CLAUDE_PROJECT_DIR="<剛才記下的專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/hooks/scripts/kept-list.sh"
14
+ PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
15
+ DEVLOG_PROJECT_DIR="<剛才記下的專案根目錄絕對路徑>" bash "${PLUGIN_ROOT}/core/scripts/kept-list.sh"
16
16
  ```
17
17
 
18
18
  - `NO_INDEX`:告知「目前沒有已 keep 的舊檔」,結束。
package/commands/pause.md CHANGED
@@ -6,11 +6,11 @@ description: 暫停這個專案的 devlog 強制記錄機制。不會刪除任
6
6
 
7
7
  1. 跑:
8
8
  ```bash
9
- 先決定 plugin 根目錄(有 `CLAUDE_PLUGIN_ROOT` 用它;否則用 `DEVLOG_TRACKER_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
9
+ 先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
10
10
 
11
11
  ```bash
12
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${DEVLOG_TRACKER_ROOT:-}}"
13
- CLAUDE_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/hooks/scripts/pause-devlog.sh"
12
+ PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
13
+ DEVLOG_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/core/scripts/pause-devlog.sh"
14
14
  ```
15
15
  ```
16
16
  不要自己刪 `.enabled`。
@@ -2,11 +2,11 @@
2
2
  description: 讀取具名保存的 devlog,核對最後一輪 Handoff 工作區後再接續該段工作。
3
3
  ---
4
4
 
5
- 取得使用者提供的 `<name>`;沒有名稱時先詢問。記下你目前已經確認的專案根目錄絕對路徑(後面步驟都要用這個值,不要用 `$(pwd)` 重新推——理由同 `commands/continue.md` 步驟 1:Bash 工具的工作目錄會在對話裡持續累積前面呼叫的 `cd`,用當下的 `pwd` 可能已經不是這個專案根目錄)。先決定 plugin 根目錄(有 `CLAUDE_PLUGIN_ROOT` 用它;否則用 `DEVLOG_TRACKER_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄),再跑:
5
+ 取得使用者提供的 `<name>`;沒有名稱時先詢問。記下你目前已經確認的專案根目錄絕對路徑(後面步驟都要用這個值,不要用 `$(pwd)` 重新推——理由同 `commands/continue.md` 步驟 1:Bash 工具的工作目錄會在對話裡持續累積前面呼叫的 `cd`,用當下的 `pwd` 可能已經不是這個專案根目錄)。先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄),再跑:
6
6
 
7
7
  ```bash
8
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${DEVLOG_TRACKER_ROOT:-}}"
9
- CLAUDE_PROJECT_DIR="<剛才記下的專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/hooks/scripts/resume-devlog.sh" --name "<name>"
8
+ PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
9
+ DEVLOG_PROJECT_DIR="<剛才記下的專案根目錄絕對路徑,不要用 $(pwd) 重新推>" bash "${PLUGIN_ROOT}/core/scripts/resume-devlog.sh" --name "<name>"
10
10
  ```
11
11
 
12
12
  若回傳 `MISSING`,列出 `CANDIDATES` 讓使用者選,不要自動執行工作。
@@ -6,11 +6,11 @@ description: 調整 Segment Watch 的沉默門檻——同一輪連續多久沒
6
6
  換算成整數秒數 `<seconds>`,跑:
7
7
 
8
8
  ```bash
9
- 先決定 plugin 根目錄(有 `CLAUDE_PLUGIN_ROOT` 用它;否則用 `DEVLOG_TRACKER_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
9
+ 先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
10
10
 
11
11
  ```bash
12
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${DEVLOG_TRACKER_ROOT:-}}"
13
- CLAUDE_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/hooks/scripts/segment-watch-set.sh" <seconds>
12
+ PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
13
+ DEVLOG_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/core/scripts/segment-watch-set.sh" <seconds>
14
14
  ```
15
15
  ```
16
16
 
package/commands/span.md CHANGED
@@ -5,9 +5,9 @@ description: 開啟或關閉 Span Mode,讓自動續接的長任務定期記錄
5
5
  先判斷使用者要開啟或關閉:
6
6
 
7
7
  - 使用者明確要求關閉,或 `.devlog/.span-open` 已存在且沒有明確要求重新開啟:
8
- 先決定 plugin 根目錄(有 `CLAUDE_PLUGIN_ROOT` 用它;否則用 `DEVLOG_TRACKER_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄),設 `PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${DEVLOG_TRACKER_ROOT:-}}"`,再跑 `CLAUDE_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/hooks/scripts/span-close.sh"`,
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"`,
9
9
  再依 SKILL 的 span 收尾規則寫一個**新的 Round**,總結整段 span。
10
10
  - 使用者要求開啟:先把目前 Round 正常寫完,`Status` 設為 `IN_PROGRESS`,再跑
11
- `CLAUDE_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/hooks/scripts/span-open.sh"`。
11
+ `DEVLOG_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/core/scripts/span-open.sh"`。
12
12
 
13
13
  不要手寫 `.span-open` JSON。腳本 exit 1 時顯示 stderr,停止操作。
package/commands/start.md CHANGED
@@ -4,10 +4,10 @@ description: 啟動這個專案的 devlog 強制記錄機制。之後每一輪
4
4
 
5
5
  請執行以下步驟:
6
6
 
7
- 1. 先決定 plugin 根目錄(有 `CLAUDE_PLUGIN_ROOT` 用它;否則用 `DEVLOG_TRACKER_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
7
+ 1. 先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
8
8
  ```bash
9
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${DEVLOG_TRACKER_ROOT:-}}"
10
- CLAUDE_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/hooks/scripts/start-devlog.sh"
9
+ PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
10
+ DEVLOG_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/core/scripts/start-devlog.sh"
11
11
  ```
12
12
  不要自己用手建 `.enabled` / `.checkpoint-state` / `.segment-state`。
13
13
  2. 若 stdout 有 `GITIGNORE_DEVLOG=no`:鄭重提醒——`.devlog/` 會寫入使用者原文(遮罩只覆蓋常見 token 前綴,不是通用掃密)。**強烈建議**把 `.devlog/` 加進專案 `.gitignore`。問要不要現在加。只有使用者明確說要,才在 `.gitignore` 末尾追加一行 `.devlog/`(檔案不存在就建立)。不要改其他行。若使用者拒絕,再警告一次「之後若不小心 commit,prompt/殘留密鑰可能進版控」,然後繼續步驟 3。
@@ -4,11 +4,11 @@ description: 查看這個專案 devlog 強制記錄是否開著、span / checkpo
4
4
 
5
5
  跑:
6
6
  ```bash
7
- 先決定 plugin 根目錄(有 `CLAUDE_PLUGIN_ROOT` 用它;否則用 `DEVLOG_TRACKER_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
7
+ 先決定 plugin 根目錄(有 `DEVLOG_TRACKER_ROOT` 用它;否則用 `CLAUDE_PLUGIN_ROOT`;兩者都空就用含 `.claude-plugin/plugin.json` 的本 plugin 根目錄):
8
8
 
9
9
  ```bash
10
- PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${DEVLOG_TRACKER_ROOT:-}}"
11
- CLAUDE_PROJECT_DIR="$(pwd)" bash "${PLUGIN_ROOT}/hooks/scripts/status-devlog.sh"
10
+ PLUGIN_ROOT="${DEVLOG_TRACKER_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
11
+ 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_DRIFT=<count>/<threshold>`:Lessons Mode 開著時,工作區漂移不符的累積次數/門檻)。不要改任何檔。`NOT_STARTED` 就說還沒 `/devlog-tracker:start`。
@@ -9,7 +9,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
9
9
  # shellcheck source=devlog-path.sh
10
10
  . "$SCRIPT_DIR/devlog-path.sh"
11
11
 
12
- devlog_resolve_paths "${CLAUDE_PROJECT_DIR:-.}"
12
+ devlog_resolve_paths "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
13
13
  [ -f "$DEVLOG_DIR/.enabled" ] || { echo "NOT_ENABLED"; exit 1; }
14
14
  [ ! -e "$DEVLOG_DIR/.awaiting-reply" ] || { echo "ALREADY_OPEN"; exit 1; }
15
15
  ROUND=""
@@ -8,7 +8,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
8
8
  # shellcheck source=json-field.sh
9
9
  . "$SCRIPT_DIR/json-field.sh"
10
10
 
11
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
11
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
12
12
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
13
13
 
14
14
  if [ ! -f "$DEVLOG_DIR/.enabled" ]; then
@@ -13,7 +13,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
13
13
  # replacement for that confirmation.
14
14
  [ "${1:-}" = "--confirmed" ] || { echo "需要 --confirmed(使用者尚未確認,不要呼叫這支腳本)" >&2; exit 1; }
15
15
 
16
- devlog_resolve_paths "${CLAUDE_PROJECT_DIR:-.}"
16
+ 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"
@@ -32,7 +32,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
32
32
  # shellcheck source=devlog-md.sh
33
33
  . "$SCRIPT_DIR/devlog-md.sh"
34
34
 
35
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
35
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
36
36
  devlog_resolve_paths "$PROJECT_DIR"
37
37
  ENABLED_FLAG="$DEVLOG_DIR/.enabled"
38
38
  ROUND_OPEN="$DEVLOG_DIR/.round-open"
@@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
7
7
  . "$SCRIPT_DIR/devlog-lock.sh"
8
8
  . "$SCRIPT_DIR/devlog-path.sh"
9
9
 
10
- devlog_resolve_paths "${CLAUDE_PROJECT_DIR:-.}"
10
+ devlog_resolve_paths "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
11
11
  devlog_lock_acquire
12
12
  trap 'devlog_lock_release' EXIT
13
13
  MAIN="$DEVLOG_FILE"
@@ -37,7 +37,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
37
37
  # 足以涵蓋 Claude Code 實際送出的 stop_hook_active 欄位形狀),兩種環境都要生效。
38
38
  INPUT="$(cat 2>/dev/null || true)"
39
39
 
40
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
40
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
41
41
  # 沒下過 /devlog-tracker:start,代表這個專案沒啟動強制記錄,直接放行。
42
42
  # 這是唯一的判斷依據——不猜這輪是否呼叫了某個 skill,也不解析 transcript。
43
43
  # 這個 gate 放在路徑解析之前,沒啟用時就不必付 git rev-parse 的成本;
@@ -33,7 +33,7 @@ NAME="$(slugify "$NAME")"
33
33
  || { echo "檔名無效" >&2; exit 1; }
34
34
  [ "${#NAME}" -le 64 ] || { echo "檔名超過 64 字元" >&2; exit 1; }
35
35
 
36
- devlog_resolve_paths "${CLAUDE_PROJECT_DIR:-.}"
36
+ devlog_resolve_paths "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
37
37
  devlog_lock_acquire
38
38
  trap 'devlog_lock_release' EXIT
39
39
  MAIN="$DEVLOG_FILE"
@@ -13,7 +13,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
13
13
  # shellcheck source=devlog-path.sh
14
14
  . "$SCRIPT_DIR/devlog-path.sh"
15
15
 
16
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
16
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
17
17
  devlog_resolve_paths "$PROJECT_DIR"
18
18
  MAIN="$DEVLOG_FILE"
19
19
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # Claude-invoked only (docs/design/lessons-mode.md), never wired into
3
- # hooks/hooks.json — same posture as keep-move.sh. Appends one free-prose
3
+ # claude/hooks.json — same posture as keep-move.sh. Appends one free-prose
4
4
  # entry to .devlog/devlog.lessons.<topic>.md (creating it if new), then
5
5
  # rebuilds the trailing "## Lessons 索引" block in devlog.md.
6
6
  set -uo pipefail
@@ -26,7 +26,7 @@ while [ "$#" -gt 0 ]; do
26
26
  esac
27
27
  done
28
28
 
29
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
29
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
30
30
  devlog_resolve_paths "$PROJECT_DIR"
31
31
  MAIN="$DEVLOG_FILE"
32
32
 
@@ -11,7 +11,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
11
11
  # shellcheck source=json-field.sh
12
12
  . "$SCRIPT_DIR/json-field.sh"
13
13
 
14
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
14
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
15
15
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
16
16
 
17
17
  if [ ! -f "$DEVLOG_DIR/.enabled" ]; then
@@ -4,7 +4,7 @@
4
4
  # or the "## Lessons 索引" block (docs/design/lessons-mode.md), same
5
5
  # non-destructive posture as pause-devlog.sh toward devlog.md.
6
6
  set -uo pipefail
7
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
7
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
8
8
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
9
9
  if [ ! -f "$DEVLOG_DIR/.lessons-enabled" ]; then
10
10
  echo "NOT_ENABLED"
@@ -4,7 +4,7 @@
4
4
  # disable」): refuses if .enabled is absent, since there is no Round/Status
5
5
  # history to detect a BLOCKED->resolved transition against.
6
6
  set -uo pipefail
7
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
7
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
8
8
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
9
9
  if [ ! -f "$DEVLOG_DIR/.enabled" ]; then
10
10
  echo "NOT_ENABLED"
@@ -14,7 +14,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
14
14
  # shellcheck source=devlog-path.sh
15
15
  . "$SCRIPT_DIR/devlog-path.sh"
16
16
 
17
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
17
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
18
18
  devlog_resolve_paths "$PROJECT_DIR"
19
19
  MAIN="$DEVLOG_FILE"
20
20
 
@@ -2,7 +2,7 @@
2
2
  # PostToolUseFailure: remember user Esc so Stop can stamp without blocking.
3
3
  set -uo pipefail
4
4
 
5
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
5
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
6
6
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
7
7
  ENABLED_FLAG="$DEVLOG_DIR/.enabled"
8
8
  [ -f "$ENABLED_FLAG" ] || exit 0
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  set -uo pipefail
3
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
3
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
4
4
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
5
5
  if [ ! -f "$DEVLOG_DIR/.enabled" ]; then
6
6
  echo "NOT_ENABLED"
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Sourced helper: the single documented definition of "which project
3
+ # directory is a hook/script operating in". Precedence:
4
+ # 1. DEVLOG_PROJECT_DIR — neutral name, harness-agnostic
5
+ # 2. CLAUDE_PROJECT_DIR — Claude Code's own variable, kept as a
6
+ # compatibility fallback so anything that already exports it
7
+ # (Claude Code itself, or an external script) keeps working
8
+ # unmodified
9
+ # 3. "." — current directory, when neither is set
10
+ #
11
+ # Most core/scripts/*.sh scripts inline the equivalent expression
12
+ # (`${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}`) directly rather than
13
+ # sourcing this file, to keep each entry-point script a single self-
14
+ # contained line. This file is the canonical statement of that precedence
15
+ # and is exercised by core/scripts/tests/test-project-dir.sh; source it
16
+ # instead of re-deriving the fallback chain in new code.
17
+ devlog_project_dir() {
18
+ printf '%s\n' "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
19
+ }
@@ -20,7 +20,7 @@ NAME="$(slugify "$NAME")"
20
20
  [ -n "$NAME" ] && [ "$NAME" != "archive" ] && [ "${#NAME}" -le 64 ] \
21
21
  || { echo "INVALID_NAME" >&2; exit 1; }
22
22
 
23
- DEVLOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.devlog"
23
+ DEVLOG_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}/.devlog"
24
24
  FILE="$DEVLOG_DIR/devlog.$NAME.md"
25
25
  if [ ! -f "$FILE" ]; then
26
26
  candidates=""
@@ -24,7 +24,7 @@ HOOKS_DIR="$(cd "${_src%/*}" && pwd)"
24
24
  . "$HOOKS_DIR/detect-pending-question.sh"
25
25
  # shellcheck source=devlog-path.sh
26
26
  . "$HOOKS_DIR/devlog-path.sh"
27
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
27
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
28
28
  [ -f "$PROJECT_DIR/.devlog/.enabled" ] || exit 0
29
29
  devlog_resolve_paths "$PROJECT_DIR"
30
30
  ROUND_CURRENT="$DEVLOG_DIR/.round-current.md"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Run every hook self-check. Usage: bash hooks/scripts/run-tests.sh
2
+ # Run every hook self-check. Usage: bash core/scripts/run-tests.sh
3
3
  set -uo pipefail
4
4
  DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  FAIL=0
@@ -8,7 +8,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
8
8
  # shellcheck source=json-field.sh
9
9
  . "$SCRIPT_DIR/json-field.sh"
10
10
 
11
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
11
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
12
12
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
13
13
 
14
14
  if [ ! -f "$DEVLOG_DIR/.enabled" ]; then
@@ -13,7 +13,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
13
13
  # shellcheck source=devlog-path.sh
14
14
  . "$SCRIPT_DIR/devlog-path.sh"
15
15
 
16
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
16
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
17
17
  [ -f "$PROJECT_DIR/.devlog/.enabled" ] || exit 0
18
18
  devlog_resolve_paths "$PROJECT_DIR"
19
19
  ROUND_CURRENT="$DEVLOG_DIR/.round-current.md"
@@ -33,7 +33,7 @@ HOOKS_DIR="$(cd "${_src%/*}" && pwd)"
33
33
  # shellcheck source=devlog-path.sh
34
34
  . "$HOOKS_DIR/devlog-path.sh"
35
35
 
36
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
36
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
37
37
  devlog_resolve_paths "$PROJECT_DIR"
38
38
  ROUND_CURRENT="$DEVLOG_DIR/.round-current.md"
39
39
  SPAN_FILE="$DEVLOG_DIR/.span-open"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  set -uo pipefail
3
- DEVLOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.devlog"
3
+ DEVLOG_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}/.devlog"
4
4
  if [ ! -e "$DEVLOG_DIR/.span-open" ]; then
5
5
  echo "NOT_OPEN"
6
6
  exit 0
@@ -6,7 +6,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
6
6
  . "$SCRIPT_DIR/devlog-md.sh"
7
7
  . "$SCRIPT_DIR/devlog-path.sh"
8
8
 
9
- devlog_resolve_paths "${CLAUDE_PROJECT_DIR:-.}"
9
+ devlog_resolve_paths "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
10
10
  [ -f "$DEVLOG_DIR/.enabled" ] || { echo "NOT_ENABLED" >&2; exit 1; }
11
11
  [ ! -e "$DEVLOG_DIR/.span-open" ] || { echo "ALREADY_OPEN" >&2; exit 1; }
12
12
  ROUND=""
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  # User-invoked: /devlog-tracker:start filesystem side.
3
3
  set -uo pipefail
4
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
4
+ PROJECT_DIR="${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
5
5
  DEVLOG_DIR="$PROJECT_DIR/.devlog"
6
6
  mkdir -p "$DEVLOG_DIR" || exit 1
7
7
  if [ ! -f "$DEVLOG_DIR/.enabled" ]; then
@@ -6,7 +6,7 @@ SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
6
6
  . "$SCRIPT_DIR/devlog-md.sh"
7
7
  . "$SCRIPT_DIR/devlog-path.sh"
8
8
 
9
- devlog_resolve_paths "${CLAUDE_PROJECT_DIR:-.}"
9
+ devlog_resolve_paths "${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}"
10
10
  [ -d "$DEVLOG_DIR" ] || { echo "NOT_STARTED"; exit 0; }
11
11
 
12
12
  [ -f "$DEVLOG_DIR/.enabled" ] && echo "ENABLED=yes" || echo "ENABLED=no"
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
5
+ TMP="$(mktemp -d)"
6
+ trap 'rm -rf "$TMP"' EXIT
7
+ FAIL=0
8
+
9
+ command -v node >/dev/null 2>&1 || { echo "SKIP: node not installed"; exit 0; }
10
+
11
+ PROJECT="$TMP/project"
12
+ mkdir -p "$PROJECT"
13
+
14
+ cd "$PROJECT" || exit 1
15
+ NODE_BIN="$(command -v node)"
16
+ "$NODE_BIN" "$REPO_ROOT/bin/devlog-tracker.js" init --claude --codex --cursor >/dev/null 2>"$TMP/stderr" \
17
+ || { echo "FAIL: init exited non-zero"; cat "$TMP/stderr"; FAIL=1; }
18
+
19
+ [ -f "$PROJECT/.devlog-tracker/VERSION" ] && echo "PASS: vendored VERSION exists" || { echo "FAIL: no VERSION"; FAIL=1; }
20
+ [ -f "$PROJECT/.devlog-tracker/env.sh" ] && echo "PASS: vendored env.sh exists" || { echo "FAIL: no env.sh"; FAIL=1; }
21
+ [ -f "$PROJECT/.claude/settings.local.json" ] && echo "PASS: .claude/settings.local.json written" || { echo "FAIL: no .claude/settings.local.json"; FAIL=1; }
22
+ [ -f "$PROJECT/.claude/skills/devlog-start/SKILL.md" ] && echo "PASS: Claude start skill written" || { echo "FAIL: no Claude start skill"; FAIL=1; }
23
+ [ -f "$PROJECT/CLAUDE.md" ] && grep -q 'devlog-tracker:begin' "$PROJECT/CLAUDE.md" \
24
+ && echo "PASS: CLAUDE.md fallback block written" || { echo "FAIL: no devlog-tracker block in CLAUDE.md"; FAIL=1; }
25
+ [ -f "$PROJECT/.codex/hooks.json" ] && echo "PASS: .codex/hooks.json written" || { echo "FAIL: no .codex/hooks.json"; FAIL=1; }
26
+ [ -f "$PROJECT/.agents/skills/devlog-start/SKILL.md" ] && echo "PASS: Codex start skill written" || { echo "FAIL: no Codex start skill"; FAIL=1; }
27
+ [ -f "$PROJECT/.cursor/hooks.json" ] && echo "PASS: .cursor/hooks.json written" || { echo "FAIL: no .cursor/hooks.json"; FAIL=1; }
28
+
29
+ # Controller addition 1a: Check vendored core scripts exist
30
+ [ -f "$PROJECT/.devlog-tracker/core/scripts/enforce-devlog.sh" ] && echo "PASS: vendored core/scripts/enforce-devlog.sh exists" || { echo "FAIL: no core/scripts/enforce-devlog.sh"; FAIL=1; }
31
+ [ -f "$PROJECT/.devlog-tracker/claude/hooks.json" ] && echo "PASS: vendored claude/hooks.json exists" || { echo "FAIL: no claude/hooks.json"; FAIL=1; }
32
+
33
+ # Controller addition 1b: Check merged Claude hook commands are resolved (no placeholders)
34
+ if grep -q '\${CLAUDE_PLUGIN_ROOT\|${DEVLOG_TRACKER_ROOT' "$PROJECT/.claude/settings.local.json"; then
35
+ echo "FAIL: unresolved placeholders in .claude/settings.local.json"; FAIL=1
36
+ else
37
+ echo "PASS: no unresolved placeholders in .claude/settings.local.json"
38
+ fi
39
+
40
+ # Verify that Stop hook command references .devlog-tracker/core/scripts/
41
+ STOP_CMD=$("$NODE_BIN" -e "console.log(JSON.parse(require('fs').readFileSync('$PROJECT/.claude/settings.local.json')).hooks.Stop[0].hooks[0].command)")
42
+ if echo "$STOP_CMD" | grep -q '\.devlog-tracker/core/scripts/enforce-devlog\.sh'; then
43
+ echo "PASS: Claude Stop hook references .devlog-tracker/core/scripts/enforce-devlog.sh"
44
+ else
45
+ echo "FAIL: Claude Stop hook does not reference .devlog-tracker/core/scripts/enforce-devlog.sh"; FAIL=1
46
+ fi
47
+
48
+ # re-running must not duplicate entries
49
+ COUNT_BEFORE_CODEX=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$PROJECT/.codex/hooks.json')).hooks.Stop.length)")
50
+ COUNT_BEFORE_CLAUDE=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$PROJECT/.claude/settings.local.json')).hooks.Stop.length)")
51
+ "$NODE_BIN" "$REPO_ROOT/bin/devlog-tracker.js" init --claude --codex --cursor >/dev/null 2>"$TMP/stderr" \
52
+ || { echo "FAIL: second init exited non-zero"; cat "$TMP/stderr"; FAIL=1; }
53
+ COUNT_AFTER_CODEX=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$PROJECT/.codex/hooks.json')).hooks.Stop.length)")
54
+ COUNT_AFTER_CLAUDE=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$PROJECT/.claude/settings.local.json')).hooks.Stop.length)")
55
+ if [ "$COUNT_BEFORE_CODEX" = "$COUNT_AFTER_CODEX" ]; then
56
+ echo "PASS: re-running init does not duplicate Codex hook entries"
57
+ else
58
+ echo "FAIL: Codex hook entries duplicated ($COUNT_BEFORE_CODEX -> $COUNT_AFTER_CODEX)"; FAIL=1
59
+ fi
60
+ if [ "$COUNT_BEFORE_CLAUDE" = "$COUNT_AFTER_CLAUDE" ]; then
61
+ echo "PASS: re-running init does not duplicate Claude hook entries"
62
+ else
63
+ echo "FAIL: Claude hook entries duplicated ($COUNT_BEFORE_CLAUDE -> $COUNT_AFTER_CLAUDE)"; FAIL=1
64
+ fi
65
+
66
+ if [ "$FAIL" -eq 0 ]; then echo "All checks passed."; exit 0
67
+ else echo "Some checks FAILED."; exit 1
68
+ fi
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
4
+ BASH_BIN="$(command -v bash)"
5
+ FAIL=0
6
+
7
+ run_case() {
8
+ local desc="$1" expected="$2"
9
+ shift 2
10
+ local actual
11
+ actual="$(env -i PATH="$PATH" "$@" "$BASH_BIN" -c '. "'"$DIR"'/project-dir.sh"; devlog_project_dir')"
12
+ if [ "$actual" = "$expected" ]; then
13
+ echo "PASS: $desc"
14
+ else
15
+ echo "FAIL: $desc (expected [$expected], got [$actual])"
16
+ FAIL=1
17
+ fi
18
+ }
19
+
20
+ run_case "neither var set falls back to ." "."
21
+ run_case "CLAUDE_PROJECT_DIR alone is honored" "/legacy" \
22
+ CLAUDE_PROJECT_DIR=/legacy
23
+ run_case "DEVLOG_PROJECT_DIR alone is honored" "/neutral" \
24
+ DEVLOG_PROJECT_DIR=/neutral
25
+ run_case "DEVLOG_PROJECT_DIR wins over CLAUDE_PROJECT_DIR" "/neutral" \
26
+ DEVLOG_PROJECT_DIR=/neutral CLAUDE_PROJECT_DIR=/legacy
27
+
28
+ if [ "$FAIL" -eq 0 ]; then echo "All checks passed."; exit 0
29
+ else echo "Some checks FAILED."; exit 1
30
+ fi
@@ -2,7 +2,7 @@
2
2
  # Self-check for session-start-devlog.sh, including the Span Mode
3
3
  # resume-context note. No framework — plain assert-and-exit, matching this
4
4
  # repo's existing style. Run directly:
5
- # bash hooks/scripts/test-session-start-devlog.sh
5
+ # bash core/scripts/tests/test-session-start-devlog.sh
6
6
  set -uo pipefail
7
7
 
8
8
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -181,7 +181,7 @@ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
181
181
  assert_exit "after compact skip-heal, Stop still enforces (hash equal) -> exit 2" 2 $?
182
182
 
183
183
  # --- Scenario 7: hooks.json SessionStart matcher includes fork -------------
184
- HOOKS_JSON="$SCRIPT_DIR/../hooks.json"
184
+ HOOKS_JSON="$SCRIPT_DIR/../../claude/hooks.json"
185
185
  if grep -q '"matcher": "startup|resume|clear|compact|fork"' "$HOOKS_JSON"; then
186
186
  echo "PASS: SessionStart matcher includes fork"
187
187
  else
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # Usage (sourced): workspace_snapshot "$PROJECT_DIR"
9
9
  # Usage (executed): bash workspace-snapshot.sh [dir]
10
- # dir defaults to ${CLAUDE_PROJECT_DIR:-.}
10
+ # dir defaults to ${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}
11
11
  # Echoes 1 line (clean / non-git / detached-clean / unborn-clean) or 2 lines
12
12
  # (dirty / detached-dirty / unborn-dirty). Never fails: any git command error
13
13
  # degrades toward "非 git 工作區", matching this hook suite's fail-open design.
@@ -65,5 +65,5 @@ workspace_snapshot() {
65
65
  }
66
66
 
67
67
  if [ "${BASH_SOURCE[0]}" = "$0" ]; then
68
- workspace_snapshot "${1:-${CLAUDE_PROJECT_DIR:-.}}"
68
+ workspace_snapshot "${1:-${DEVLOG_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-.}}}"
69
69
  fi
@@ -2,10 +2,10 @@
2
2
  set -uo pipefail
3
3
  _src="${BASH_SOURCE[0]}"
4
4
  SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
5
- PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../hooks/scripts" && pwd)"
5
+ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
6
6
  INPUT="$(cat 2>/dev/null || true)"
7
7
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
8
- export CLAUDE_PROJECT_DIR="$ROOT"
8
+ export DEVLOG_PROJECT_DIR="$ROOT"
9
9
  ERR_FILE="$(mktemp "${TMPDIR:-/tmp}/cursor-pre-tool.XXXXXX")" || { echo '{}'; exit 0; }
10
10
  printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/segment-watch.sh" >/dev/null 2>"$ERR_FILE"
11
11
  RESULT=$?
@@ -2,10 +2,10 @@
2
2
  set -uo pipefail
3
3
  _src="${BASH_SOURCE[0]}"
4
4
  SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
5
- PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../hooks/scripts" && pwd)"
5
+ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
6
6
  INPUT="$(cat 2>/dev/null || true)"
7
7
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
8
- export CLAUDE_PROJECT_DIR="$ROOT"
8
+ export DEVLOG_PROJECT_DIR="$ROOT"
9
9
  printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/on-session-end.sh" >/dev/null 2>&1 || true
10
10
  echo '{}'
11
11
  exit 0
@@ -2,10 +2,10 @@
2
2
  set -uo pipefail
3
3
  _src="${BASH_SOURCE[0]}"
4
4
  SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
5
- PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../hooks/scripts" && pwd)"
5
+ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
6
6
  INPUT="$(cat 2>/dev/null || true)"
7
7
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
8
- export CLAUDE_PROJECT_DIR="$ROOT"
8
+ export DEVLOG_PROJECT_DIR="$ROOT"
9
9
  EXCERPT="$(printf '%s' '{"source":"startup"}' | bash "$PLUGIN_SCRIPTS/session-start-devlog.sh" 2>/dev/null || true)"
10
10
  if command -v jq >/dev/null 2>&1; then
11
11
  jq -n --arg c "$EXCERPT" '{additional_context:$c}'
@@ -2,10 +2,10 @@
2
2
  set -uo pipefail
3
3
  _src="${BASH_SOURCE[0]}"
4
4
  SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
5
- PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../hooks/scripts" && pwd)"
5
+ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
6
6
  INPUT="$(cat 2>/dev/null || true)"
7
7
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
8
- export CLAUDE_PROJECT_DIR="$ROOT"
8
+ export DEVLOG_PROJECT_DIR="$ROOT"
9
9
  STATUS=""
10
10
  LOOP_COUNT=0
11
11
  if command -v jq >/dev/null 2>&1; then
@@ -2,10 +2,10 @@
2
2
  set -uo pipefail
3
3
  _src="${BASH_SOURCE[0]}"
4
4
  SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
5
- PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../hooks/scripts" && pwd)"
5
+ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
6
6
  INPUT="$(cat 2>/dev/null || true)"
7
7
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
8
- export CLAUDE_PROJECT_DIR="$ROOT"
8
+ export DEVLOG_PROJECT_DIR="$ROOT"
9
9
  PROMPT=""
10
10
  SESSION_ID=""
11
11
  if command -v jq >/dev/null 2>&1; then
@@ -2,10 +2,10 @@
2
2
  set -uo pipefail
3
3
  _src="${BASH_SOURCE[0]}"
4
4
  SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
5
- PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../hooks/scripts" && pwd)"
5
+ PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" && pwd)"
6
6
  INPUT="$(cat 2>/dev/null || true)"
7
7
  ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
8
- export CLAUDE_PROJECT_DIR="$ROOT"
8
+ export DEVLOG_PROJECT_DIR="$ROOT"
9
9
  if command -v jq >/dev/null 2>&1; then
10
10
  IS_INTERRUPT="$(printf '%s' "$INPUT" | jq -r '.is_interrupt // .tool_result.is_interrupt // false' 2>/dev/null || echo false)"
11
11
  else