devlog-tracker 0.33.5 → 0.34.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 +41 -10
- package/README.zh-TW.md +41 -10
- package/cli/agents-md.js +1 -0
- package/cli/agents-md.test.js +10 -0
- package/cli/platforms/claude.js +1 -0
- package/codex/hooks/on-interrupt.sh +13 -0
- package/codex/hooks/on-pre-tool.sh +69 -1
- package/codex/hooks/on-session-end.sh +5 -5
- package/codex/hooks/on-session-start.sh +5 -6
- package/codex/hooks/on-stop.sh +3 -2
- package/codex/hooks/on-subagent-start.sh +19 -0
- package/codex/hooks/project-dir.sh +16 -1
- package/codex/hooks/test-adapters.sh +127 -9
- package/codex/hooks.json +21 -0
- package/commands/continue.md +3 -3
- package/commands/keep-all.md +1 -1
- package/commands/keep.md +2 -2
- package/commands/lessons-on.md +1 -1
- package/commands/migrate.md +18 -0
- package/commands/pr.md +3 -3
- package/commands/resume.md +1 -1
- package/core/scripts/close-open-round.sh +8 -2
- package/core/scripts/devlog-md.sh +6 -11
- package/core/scripts/enforce-devlog.sh +79 -85
- package/core/scripts/handoff-convert.sh +187 -0
- package/core/scripts/handoff-fields.sh +219 -0
- package/core/scripts/handoff-file.sh +19 -88
- package/core/scripts/lessons-subagent-start.sh +1 -1
- package/core/scripts/migrate-handoff.sh +91 -0
- package/core/scripts/round-start.sh +13 -2
- package/core/scripts/segment-watch.sh +1 -1
- package/core/scripts/tests/lib/xml-fixture.sh +10 -0
- package/core/scripts/tests/test-close-open-round.sh +2 -1
- package/core/scripts/tests/test-devlog-md.sh +21 -0
- package/core/scripts/tests/test-enforce-devlog-files.sh +4 -1
- package/core/scripts/tests/test-enforce-devlog-handoff-order.sh +149 -81
- package/core/scripts/tests/test-enforce-devlog-session-handoff.sh +92 -11
- package/core/scripts/tests/test-enforce-devlog-workspace.sh +42 -26
- package/core/scripts/tests/test-enforce-devlog.sh +70 -0
- package/core/scripts/tests/test-handoff-fields.sh +171 -0
- package/core/scripts/tests/test-handoff-file.sh +67 -45
- package/core/scripts/tests/test-migrate-handoff.sh +278 -0
- package/core/scripts/tests/test-round-start.sh +54 -1
- package/core/scripts/tests/test-session-start-devlog.sh +45 -0
- package/core/scripts/timeline-render.js +26 -2
- package/core/scripts/timeline-render.test.js +23 -1
- package/package.json +3 -3
- package/skills/devlog-tracker/SKILL.md +80 -55
- package/skills/devlog-tracker/references/contract.md +4 -3
- package/skills/devlog-tracker/references/lessons-mode.md +7 -7
- package/skills/devlog-tracker/references/round-segments.md +11 -5
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.34.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
|
|
|
@@ -45,19 +45,43 @@ Merges the hooks into `.claude/settings.local.json` (not `settings.json` — the
|
|
|
45
45
|
|
|
46
46
|
### Codex
|
|
47
47
|
|
|
48
|
+
**Option 1: plugin marketplace**
|
|
49
|
+
|
|
50
|
+
To try an unpublished checkout, run these commands **from this repository's root**:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
codex plugin marketplace add .
|
|
54
|
+
codex plugin add devlog-tracker@devlog-tracker
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
After the branch is merged and pushed, install from GitHub instead:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
codex plugin marketplace add gogogohuang/devlog-tracker
|
|
61
|
+
codex plugin add devlog-tracker@devlog-tracker
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Start a new Codex session in the project you want to track, review and trust the bundled hooks with `/hooks`, then use `$devlog-start` (or pick it from `/skills`). The plugin keeps its scripts in Codex's plugin cache; it does not create a `.devlog-tracker/` directory in each project. Tracking data is created under `.devlog/` only after `$devlog-start`. If this project already uses `npx devlog-tracker init --codex`, remove the devlog-tracker entries from its project-level `.codex/hooks.json` before enabling the plugin to avoid running each hook twice.
|
|
65
|
+
|
|
66
|
+
**Option 2: npx (vendored into the project, version pinnable)**
|
|
67
|
+
|
|
48
68
|
```bash
|
|
49
69
|
npx devlog-tracker init --codex
|
|
50
70
|
```
|
|
51
71
|
|
|
52
72
|
Merges the `hooks` from `codex/hooks.json` into the project's `.codex/hooks.json`, and generates `.agents/skills/devlog-<name>/SKILL.md` from `commands/*.md`, invoked with `$devlog-<name>` (or picked from `/skills`); it also adds the same kind of fallback block to `AGENTS.md`. Files the older 0.25.0 version wrote to `.codex/prompts/` are cleared out on the next `init` run — Codex doesn't read project-level custom prompts.
|
|
53
73
|
|
|
54
|
-
**Hooks require
|
|
74
|
+
**Hooks require trust review before they run.** Codex skips new or changed hooks until you trust their current definitions. For npx installs, the project `.codex/` layer must also be trusted to load project hooks. Use `/hooks` to review the active definitions.
|
|
55
75
|
|
|
56
|
-
- Interactive mode:
|
|
76
|
+
- Interactive mode: Codex warns at startup when hooks need review. Use `/hooks` to inspect and trust them. A later change to hook configuration (e.g. re-running `init` and changing paths or commands) may require another review.
|
|
57
77
|
- Non-interactive `codex exec` (CI, scripts): unapproved hooks are silently skipped. `--dangerously-bypass-hook-trust` lets them run, but that flag skips trust checks for *all* hooks, so it's only appropriate for automation environments where you've already vetted the hook sources yourself.
|
|
58
78
|
- To confirm it's working: after `start`, send a message and check whether `.devlog/.round-current.md` shows this round's User Input skeleton; if not, the hook didn't run.
|
|
59
79
|
|
|
60
|
-
|
|
80
|
+
When the silence or workspace guard blocks a tool, Codex can read the current round with a single `cat <project>/.devlog/.round-current.md` command and update it with `apply_patch`. The block message includes the project path.
|
|
81
|
+
|
|
82
|
+
Codex's `Interrupt` hook records an interrupted main-thread turn as `INTERRUPTED`. Codex has no `StopFailure` event for other abnormal endings; an open round is recovered on the next prompt or session start, or when `SessionEnd` runs. Codex's `SessionEnd` reason is currently only `other`, and it may run after an idle session rather than immediately when you switch conversations. The `Stop` hook still enforces completion of normal turns. Codex can also start from a subdirectory of an installed project; the hooks find the installation root.
|
|
83
|
+
|
|
84
|
+
With Lessons Mode enabled, Codex's `SubagentStart` hook gives subagents the recording guidance and project path. For npx installs, it preserves the main project's path when a subagent runs in a separate worktree; plugin installs use that worktree's path.
|
|
61
85
|
|
|
62
86
|
### Cursor
|
|
63
87
|
|
|
@@ -77,6 +101,8 @@ Without `--claude`/`--codex`/`--cursor`, it interactively asks which platform(s)
|
|
|
77
101
|
|
|
78
102
|
Copies `core/scripts/`, `claude/hooks.json`, `codex/hooks/`, `cursor/hooks/`, `skills/`, and `commands/` into the project's `.devlog-tracker/`. Re-running `npx devlog-tracker init` upgrades to the package's current version; `npx devlog-tracker status` checks whether the installed version is behind. `npx devlog-tracker report [--json] [--all-branches]` and `npx devlog-tracker timeline [--all-branches] [--out <path>]` run the same scripts as `/devlog-tracker:report` and `/devlog-tracker:timeline`, using the vendored copy when there is one.
|
|
79
103
|
|
|
104
|
+
`.devlog-tracker/` contains the installed program. `.devlog/` contains your project's tracking data and is created only when you run the start command. Therefore, seeing `.devlog-tracker/` immediately after `npx init` is expected; `init` alone does not start recording.
|
|
105
|
+
|
|
80
106
|
`init` writes this machine's absolute paths into each platform's hooks config and into `.devlog-tracker/env.sh`. If you commit these files to git, each teammate needs to run `npx devlog-tracker init` on their own machine (paths differ per machine); alternatively, add `.devlog-tracker/` and the generated hooks config files to `.gitignore`.
|
|
81
107
|
|
|
82
108
|
To install manually (without npx), set `DEVLOG_TRACKER_ROOT` to this plugin's absolute path, and merge the `hooks` from the matching platform's `hooks.json` into the project config; `commands/*.md` prefers `DEVLOG_TRACKER_ROOT`, falling back to `CLAUDE_PLUGIN_ROOT`:
|
|
@@ -100,19 +126,20 @@ Run once in your project (pick whichever matches your install method):
|
|
|
100
126
|
```
|
|
101
127
|
/devlog-tracker:start # Claude Code plugin
|
|
102
128
|
/devlog-start # npx init --claude
|
|
103
|
-
$devlog-start # npx init --codex
|
|
129
|
+
$devlog-start # Codex plugin or npx init --codex
|
|
104
130
|
```
|
|
105
131
|
|
|
106
132
|
After that, just converse normally — every round is enforced-checked and `.devlog/devlog.md` gets updated before it can end. After `/clear`, context is empty; to pick up prior work, run the matching `continue` command. See the command table below for pause, archive, named export, status, and span.
|
|
107
133
|
|
|
108
134
|
## Commands
|
|
109
135
|
|
|
110
|
-
The table below uses the plugin's `/devlog-tracker:*` namespace; `npx init --claude` installs `/devlog-<name>`,
|
|
136
|
+
The table below uses the Claude plugin's `/devlog-tracker:*` namespace; `npx init --claude` installs `/devlog-<name>`, while either Codex installation method uses `$devlog-<name>` — the command content is the same.
|
|
111
137
|
|
|
112
138
|
| Command | What it does |
|
|
113
139
|
|---|---|
|
|
114
140
|
| `/devlog-tracker:start` | Runs a script that creates `.devlog/.enabled` (missing state files are backfilled; existing thresholds aren't reset). Reads the file to check progress; doesn't auto-start work. `.devlog/` contains a prompt suggesting adding it to `.gitignore`, and only edits it with your consent. |
|
|
115
141
|
| `/devlog-tracker:continue` | Reads `.devlog/devlog.md`, checks the last round's Handoff "Workspace" section, then continues per its next step. Use this after `/clear` to resume. See [`docs/design/continue.md`](docs/design/continue.md). |
|
|
142
|
+
| `/devlog-tracker:migrate` | Converts legacy `####` Handoff/Session Handoff in `devlog.md`, branch files, the open round and `handoff.md` to XML (backups as `*.pre-migrate`). The Stop hook tells the agent to run it when it blocks a legacy Handoff. |
|
|
116
143
|
| `/devlog-tracker:pause` | Pauses enforced recording; history files are untouched, and you can `start` again later. |
|
|
117
144
|
| `/devlog-tracker:compact` | A script moves older `DONE` rounds into `devlog.archive.md` (Checkpoints and unfinished rounds stay in the main file). |
|
|
118
145
|
| `/devlog-tracker:keep` | Scans the current branch's main file (to reorganize every devlog at once, use `keep-all`), groups it by topic, lists suggestions at once, then — after confirmation — moves each section out into its own `devlog.<name>.md` (leaving a `## Kept index` pointer line with a one-sentence topic description in the main file); can also extract a single section or merge everything into one history file. Not the same as compact. See [`docs/design/keep.md`](docs/design/keep.md). |
|
|
@@ -170,20 +197,24 @@ Every round has these fixed sections:
|
|
|
170
197
|
- **`User Input`** — the raw submitted text takes priority (written by the hook; Claude shouldn't rewrite it), common tokens are masked
|
|
171
198
|
- **`Summary`** — a conclusion a human can scan
|
|
172
199
|
- **`Reply`** — what was said/promised to the user this round
|
|
173
|
-
- **`Handoff`** — for the next round to pick up (decisions / files / workspace / current state / completion criteria / next steps)
|
|
200
|
+
- **`Handoff`** — for the next round to pick up (decisions / files / workspace / current state / completion criteria / next steps). `Handoff` and `Session Handoff` are written as line-based XML tags (`<handoff>` … `<next>` …) because only the next agent and the Stop hook read them; `Summary`/`Reply` stay Markdown for humans. See [`docs/design/handoff-xml.md`](docs/design/handoff-xml.md).
|
|
174
201
|
- **`Status`** — one of `DONE` / `IN_PROGRESS` / `BLOCKED` / `INTERRUPTED`
|
|
175
202
|
|
|
176
203
|
"Workspace" is a git snapshot taken at wrap-up time; required whenever in progress or blocked. `DONE` also requires it if "Files" has content (claiming files were touched/committed). "Completion criteria" is required whenever in progress or blocked.
|
|
177
204
|
|
|
178
205
|
The Stop hook does the following:
|
|
179
206
|
|
|
180
|
-
1. Confirms `Summary`/`Reply`/`Handoff` headings have content underneath, and Status is one of the four values above; Handoff
|
|
207
|
+
1. Confirms `Summary`/`Reply`/`Handoff` headings have content underneath, and Status is one of the four values above; Handoff must use the XML tag form, with tags in the order `decisions` → `files` → `workspace` → `state` → `done-when` → `next`
|
|
181
208
|
2. In-progress/blocked rounds must have "Completion criteria" and "Next steps"; "Next steps" can't be pure blacklisted filler (e.g. a section that just says "continue finishing up" — string matching, not semantic scoring; see [`docs/design/next-step-blacklist.md`](docs/design/next-step-blacklist.md)); in-progress rounds get an additional lightweight actionability check; blocked rounds require "Current state" or "Next steps" to contain a missing-piece phrasing
|
|
182
209
|
3. Machine-verifies that "Workspace" matches the actual git state at wrap-up time, verbatim (always checked when in progress/blocked; only checked for `DONE` when "Files" is non-empty) — this catches claims like "already committed" that don't actually match reality
|
|
183
210
|
|
|
184
|
-
When
|
|
211
|
+
When `<files>` is non-empty, it's likewise machine-verified: the commit section must match that commit's actual content verbatim; for uncommitted sections, it only requires that the claimed paths actually have changes (not full coverage, so leftovers from a previous round aren't counted as missing from this one).
|
|
212
|
+
|
|
213
|
+
See [`docs/design/summary-handoff.md`](docs/design/summary-handoff.md), [`docs/design/devlog-as-ssot-assessment.md`](docs/design/devlog-as-ssot-assessment.md), [`docs/design/files-verify.md`](docs/design/files-verify.md), [`docs/design/handoff-xml.md`](docs/design/handoff-xml.md), and SKILL.md for details.
|
|
214
|
+
|
|
215
|
+
### Upgrading to XML Handoff
|
|
185
216
|
|
|
186
|
-
|
|
217
|
+
Rounds written before this change are in the legacy `####`-headed form and are still read fine. Archive, keep and lessons files (`devlog.archive.md`, `devlog.<name>.md`, `devlog.lessons.*.md`) are never rewritten. When the Stop hook blocks on a legacy Handoff in the round it's checking, it tells the agent to run the migrate command (`/devlog-tracker:migrate` on the Claude plugin or `$devlog-migrate` on Codex; underlying script: `migrate-handoff.sh`) itself, then finish the turn again — no action needed from you in the common case. Migrate rewrites `devlog.md`, the branch devlog files, the open round (`.round-current.md`) and `handoff.md`/`handoff.<branch>.md` in place, leaving a `*.pre-migrate` backup next to each file it changes; a round it cannot map exactly is left as-is and listed on a `SKIP` line. If the agent keeps writing the legacy form instead of picking up the fix, update the plugin or rerun `npx devlog-tracker init`, then use the start command for your installation (`/devlog-tracker:start` on the Claude plugin or `$devlog-start` on Codex).
|
|
187
218
|
|
|
188
219
|
## What the hooks do automatically
|
|
189
220
|
|
package/README.zh-TW.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
*[English](README.md) | 繁體中文*
|
|
4
4
|
|
|
5
|
-
**版本** 0.
|
|
5
|
+
**版本** 0.34.0
|
|
6
6
|
|
|
7
7
|
在專案中維護一份 `.devlog/devlog.md`,把每一輪對話的請求、決策與結果寫成永久紀錄。對話一 `/clear` 或換 session 就沒了;這份檔案取代那個缺口,讓工作可以中斷再接。沒下過 `/devlog-tracker:start` 時,裝著也不會動任何檔案。
|
|
8
8
|
|
|
@@ -45,19 +45,43 @@ npx devlog-tracker init --claude
|
|
|
45
45
|
|
|
46
46
|
### Codex
|
|
47
47
|
|
|
48
|
+
**方式一:plugin marketplace**
|
|
49
|
+
|
|
50
|
+
要測試尚未發布的 checkout,先在**這個 repo 的根目錄**用本機版本安裝:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
codex plugin marketplace add .
|
|
54
|
+
codex plugin add devlog-tracker@devlog-tracker
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
合併並推送後,才改用 GitHub 來源安裝:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
codex plugin marketplace add gogogohuang/devlog-tracker
|
|
61
|
+
codex plugin add devlog-tracker@devlog-tracker
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
在要追蹤的專案開新 Codex session,用 `/hooks` 審核並信任 plugin 附帶的 hooks,再執行 `$devlog-start`(或從 `/skills` 選)。plugin 腳本放在 Codex 的快取中,不會在每個專案建立 `.devlog-tracker/`;執行 `$devlog-start` 後才會在專案建立 `.devlog/` 紀錄資料。若專案之前跑過 `npx devlog-tracker init --codex`,啟用 plugin 前先從專案的 `.codex/hooks.json` 移除 devlog-tracker 的 hook 項目,避免同一個 hook 執行兩次。
|
|
65
|
+
|
|
66
|
+
**方式二:npx(vendor 進專案,版本可鎖定)**
|
|
67
|
+
|
|
48
68
|
```bash
|
|
49
69
|
npx devlog-tracker init --codex
|
|
50
70
|
```
|
|
51
71
|
|
|
52
72
|
會把 `codex/hooks.json` 的 `hooks` 合併進專案 `.codex/hooks.json`,並從 `commands/*.md` 產生 `.agents/skills/devlog-<名稱>/SKILL.md`,用 `$devlog-<名稱>`(或 `/skills` 選)執行;同時在 `AGENTS.md` 加上同一種 fallback 說明區塊。舊版(0.25.0)寫到 `.codex/prompts/` 的檔案會在重跑 `init` 時清掉——Codex 不讀專案層級的 custom prompts。
|
|
53
73
|
|
|
54
|
-
**hook
|
|
74
|
+
**hook 需要信任審核才會執行。** Codex 會略過尚未信任的新 hook 或變更過的 hook。若使用 npx 安裝,專案的 `.codex/` 設定層也須先被信任,專案 hook 才會載入;之後可用 `/hooks` 查看哪些 hook 定義仍待審核。
|
|
55
75
|
|
|
56
|
-
-
|
|
76
|
+
- 互動模式:有 hook 待審核時,Codex 啟動時會顯示警告。用 `/hooks` 檢查並信任它們。之後 hook 的設定有變動(例如重跑 `init` 讓路徑或指令改變)也可能要再審核一次。
|
|
57
77
|
- 非互動的 `codex exec`(CI、腳本):未審核的 hook 會被靜默略過。`--dangerously-bypass-hook-trust` 可以讓它們跑起來,但那個旗標會略過所有 hook 的信任檢查,只適合已經自己確認過 hook 來源的自動化環境。
|
|
58
78
|
- 想確認有沒有生效:`start` 之後送一則訊息,看 `.devlog/.round-current.md` 有沒有出現這一輪的 User Input skeleton;沒有就代表 hook 沒被執行。
|
|
59
79
|
|
|
60
|
-
Codex
|
|
80
|
+
沉默或工作區檢查擋住工具時,Codex 可用單一 `cat <專案>/.devlog/.round-current.md` 指令讀取當輪,再用 `apply_patch` 修改;封鎖訊息會附上專案路徑。
|
|
81
|
+
|
|
82
|
+
Codex 的 `Interrupt` hook 會把主執行緒被中斷的輪次標成 `INTERRUPTED`。Codex 沒有處理其他異常結束的 `StopFailure` 事件;未收尾輪次會在下次訊息、下次 session start,或 `SessionEnd` 執行時補記。Codex 的 `SessionEnd` 原因目前只有 `other`,切換對話後也可能等閒置一段時間才觸發。正常輪次仍由 `Stop` hook 強制收尾。從已安裝專案的子目錄啟動 Codex 時,hook 也會找到安裝根目錄。
|
|
83
|
+
|
|
84
|
+
Lessons Mode 開啟時,Codex 的 `SubagentStart` hook 會把記錄指引與專案路徑交給子代理。npx 安裝在子代理使用獨立 worktree 時仍會傳主專案路徑;plugin 安裝則使用該 worktree 的路徑。
|
|
61
85
|
|
|
62
86
|
### Cursor
|
|
63
87
|
|
|
@@ -77,6 +101,8 @@ npx devlog-tracker init
|
|
|
77
101
|
|
|
78
102
|
會把 `core/scripts/`、`claude/hooks.json`、`codex/hooks/`、`cursor/hooks/`、`skills/`、`commands/` 複製進專案的 `.devlog-tracker/`。重新執行 `npx devlog-tracker init` 可以升級到套件目前的版本;`npx devlog-tracker status` 可以查目前裝的版本是否落後。`npx devlog-tracker report [--json] [--all-branches]` 和 `npx devlog-tracker timeline [--all-branches] [--out <路徑>]` 跑的是跟 `/devlog-tracker:report`、`/devlog-tracker:timeline` 同一支腳本,有 vendored 版本就用它。
|
|
79
103
|
|
|
104
|
+
`.devlog-tracker/` 放的是安裝進專案的程式;`.devlog/` 放的是這個專案的紀錄資料,執行 start 指令後才會建立。因此 `npx init` 後先看到 `.devlog-tracker/` 是預期行為,單跑 `init` 不會開始記錄。
|
|
105
|
+
|
|
80
106
|
`init` 會把這台機器專屬的絕對路徑寫進各平台的 hooks 設定檔與 `.devlog-tracker/env.sh`。如果你把這些檔案 commit 進 git,每位隊友都要在自己的機器上跑一次 `npx devlog-tracker init`(路徑每台機器不同);或者改成把 `.devlog-tracker/` 與產生出來的 hooks 設定檔加進 `.gitignore`。
|
|
81
107
|
|
|
82
108
|
若要手動裝(不透過 npx),設定 `DEVLOG_TRACKER_ROOT` 為本 plugin 的絕對路徑,把對應平台的 `hooks.json` 的 `hooks` 合併進專案設定;`commands/*.md` 會優先讀 `DEVLOG_TRACKER_ROOT`,否則讀 `CLAUDE_PLUGIN_ROOT`:
|
|
@@ -100,19 +126,20 @@ bash "$DEVLOG_TRACKER_ROOT/core/scripts/timeline-devlog.sh"
|
|
|
100
126
|
```
|
|
101
127
|
/devlog-tracker:start # Claude Code plugin
|
|
102
128
|
/devlog-start # npx init --claude
|
|
103
|
-
$devlog-start # npx init --codex
|
|
129
|
+
$devlog-start # Codex plugin 或 npx init --codex
|
|
104
130
|
```
|
|
105
131
|
|
|
106
132
|
之後正常對話即可,每一輪結束前都會被強制檢查、補上 `.devlog/devlog.md` 的紀錄。`/clear` 之後 context 是空的;要接著做上一題,下對應的 `continue` 指令。暫停、歸檔、具名搬走、狀態與 span 見下方指令表。
|
|
107
133
|
|
|
108
134
|
## 指令
|
|
109
135
|
|
|
110
|
-
下表以 plugin 的 `/devlog-tracker:*` namespace 表示;`npx init --claude` 裝的是 `/devlog
|
|
136
|
+
下表以 Claude plugin 的 `/devlog-tracker:*` namespace 表示;`npx init --claude` 裝的是 `/devlog-<名稱>`,Codex 兩種安裝方式都用 `$devlog-<名稱>`,指令內容相同。
|
|
111
137
|
|
|
112
138
|
| 指令 | 做什麼 |
|
|
113
139
|
|---|---|
|
|
114
140
|
| `/devlog-tracker:start` | 跑腳本建立 `.devlog/.enabled`(缺的 state 檔會補上,已有門檻不重置)。讀檔對進度,不自動開工。`.devlog/` 含 prompt,會建議加進 `.gitignore`,要你同意才改。 |
|
|
115
141
|
| `/devlog-tracker:continue` | 讀 `.devlog/devlog.md`,核對最後一輪 Handoff「工作區」後再依下一步接著做。`/clear` 之後要接續用這個。細節見 [`docs/design/continue.md`](docs/design/continue.md)。 |
|
|
142
|
+
| `/devlog-tracker:migrate` | 把 `devlog.md`、分支檔、開著的那一輪與 `handoff.md` 裡舊格式 `####` 的 Handoff/Session Handoff 轉成 XML(備份成 `*.pre-migrate`)。Stop hook 擋下舊格式的 Handoff 時,會叫 agent 自己跑這個指令。 |
|
|
116
143
|
| `/devlog-tracker:pause` | 暫停強制記錄,歷史檔不動,之後可再 `start`。 |
|
|
117
144
|
| `/devlog-tracker:compact` | 腳本把較舊的 `DONE` 輪次搬到 `devlog.archive.md`(Checkpoint 與未完成輪留在主檔)。 |
|
|
118
145
|
| `/devlog-tracker:keep` | 掃目前分支的主檔分主題(要一次整理所有 devlog 用 `keep-all`),一次列出建議,確認後把各段各自搬走成 `devlog.<name>.md`(並在主檔留一個 `## Kept 索引` 指標行,含一句主題描述);也可抽出一段或合併成全部歷史一檔。不是 compact。細節見 [`docs/design/keep.md`](docs/design/keep.md)。 |
|
|
@@ -170,20 +197,24 @@ sequenceDiagram
|
|
|
170
197
|
- **`User Input`** — 送出原文優先(hook 寫入;Claude 不要改寫),常見 token 會遮罩
|
|
171
198
|
- **`Summary`** — 給人掃的結論
|
|
172
199
|
- **`Reply`** — 這輪對使用者說過/答應過的話
|
|
173
|
-
- **`Handoff`** —
|
|
200
|
+
- **`Handoff`** — 給下一輪接手(決策/檔案/工作區/現況/完成條件/下一步)。`Handoff` 與 `Session Handoff` 用以行為單位的 XML 標籤寫(`<handoff>` … `<next>` …),因為讀者只有下一輪的 agent 與 Stop hook;`Summary`/`Reply` 仍是給人看的 Markdown。細節見 [`docs/design/handoff-xml.md`](docs/design/handoff-xml.md)。
|
|
174
201
|
- **`Status`** — `DONE` / `IN_PROGRESS` / `BLOCKED` / `INTERRUPTED` 四選一
|
|
175
202
|
|
|
176
203
|
其中「工作區」是收尾時的 git 快照,進行中/卡住必寫;`DONE` 若「檔案」有內容(宣稱動過/commit 過檔案)也必寫。「完成條件」在進行中/卡住必寫。
|
|
177
204
|
|
|
178
205
|
Stop hook 會做這些事:
|
|
179
206
|
|
|
180
|
-
1. 確認 `Summary`/`Reply`/`Handoff` 標題底下有內容、Status 是上述四值之一;Handoff
|
|
207
|
+
1. 確認 `Summary`/`Reply`/`Handoff` 標題底下有內容、Status 是上述四值之一;Handoff 必須是 XML 標籤格式,標籤順序為 `decisions` → `files` → `workspace` → `state` → `done-when` → `next`
|
|
181
208
|
2. 進行中/卡住時有「完成條件」與「下一步」;「下一步」不是純黑名單空話(例如整節只寫「繼續完成」;字串比對,非語意評分,細節見 [`docs/design/next-step-blacklist.md`](docs/design/next-step-blacklist.md));進行中另做輕量可執行檢查;卡住時「現況」或「下一步」須含缺件句式
|
|
182
209
|
3. 機器核對「工作區」是否跟收尾當下的 git 狀態逐字相符(進行中/卡住一律核對,`DONE` 只在「檔案」非空時核對),避免「已 commit 完成」卻其實沒 commit 這類宣稱跟實際不符
|
|
183
210
|
|
|
184
|
-
|
|
211
|
+
`<files>` 非空時同樣機器核對:commit 區塊要跟該次 commit 的實際內容逐字相符,未 commit 的區塊只要求宣稱的路徑真的存在變更(不要求涵蓋全部,避免把跨輪殘留算成這輪漏列)。
|
|
212
|
+
|
|
213
|
+
細節見 [`docs/design/summary-handoff.md`](docs/design/summary-handoff.md)、[`docs/design/devlog-as-ssot-assessment.md`](docs/design/devlog-as-ssot-assessment.md)、[`docs/design/files-verify.md`](docs/design/files-verify.md)、[`docs/design/handoff-xml.md`](docs/design/handoff-xml.md) 和 SKILL.md。
|
|
214
|
+
|
|
215
|
+
### 升級到 XML Handoff
|
|
185
216
|
|
|
186
|
-
|
|
217
|
+
舊輪次是 `####` 小節格式,讀取端照樣相容。archive、keep 與 lessons 檔(`devlog.archive.md`、`devlog.<name>.md`、`devlog.lessons.*.md`)永遠不會被改寫。當 Stop hook 在收尾時擋下舊格式的 Handoff,訊息會叫 agent 自己跑對應的 migrate 指令(Claude plugin 用 `/devlog-tracker:migrate`,Codex 用 `$devlog-migrate`;底層是 `migrate-handoff.sh`)再重新結束這一輪——一般情況不用你動手。migrate 會直接改寫 `devlog.md`、分支 devlog 檔、開著的 Round(`.round-current.md`)與 `handoff.md`/`handoff.<branch>.md`,每個改過的檔旁邊留一份 `*.pre-migrate` 備份;無法精確轉換的輪次保留原樣,列在 `SKIP` 行。如果 agent 一直寫舊格式、沒有照著修,請更新 plugin 或重跑 `npx devlog-tracker init`,再使用對應的 start 指令(Claude plugin 用 `/devlog-tracker:start`,Codex 用 `$devlog-start`)。
|
|
187
218
|
|
|
188
219
|
## Hook 會自動做的事
|
|
189
220
|
|
package/cli/agents-md.js
CHANGED
|
@@ -22,6 +22,7 @@ function codexBlock() {
|
|
|
22
22
|
| 接續上一題 / continue(換過工具或 \`/clear\` 之後) | \`commands/continue.md\` |
|
|
23
23
|
| 歸檔 / compact | \`commands/compact.md\` |
|
|
24
24
|
| 清空重編 / clean(不可復原,先問使用者確認) | \`commands/clean.md\` |
|
|
25
|
+
| 舊格式 Handoff 轉 XML / migrate(Stop hook 擋下舊格式時直接跑) | \`commands/migrate.md\` |
|
|
25
26
|
| 保存主題 / keep、接續具名檔 / resume、跨主題總覽 / overview | \`commands/keep.md\`、\`commands/resume.md\`、\`commands/overview.md\` |
|
|
26
27
|
| 整理所有 devlog(跨分支檔、archive、既有 keep 檔重新分主題) / keep-all | \`commands/keep-all.md\` |
|
|
27
28
|
| 沉澱規範寫進 CLAUDE.md/AGENTS.md / promote | \`commands/promote.md\` |
|
package/cli/agents-md.test.js
CHANGED
|
@@ -92,6 +92,16 @@ test('init --codex writes AGENTS.md and project skills; --cursor alone does not'
|
|
|
92
92
|
assert.match(skill, new RegExp(`^---\\nname: ${name}\\ndescription: ".+"\\n---\\n`));
|
|
93
93
|
}
|
|
94
94
|
assert.ok(!fs.existsSync(path.join(withCodex, '.codex', 'prompts')));
|
|
95
|
+
const installedHooks = JSON.parse(fs.readFileSync(path.join(withCodex, '.codex', 'hooks.json'), 'utf8'));
|
|
96
|
+
assert.equal(installedHooks.hooks.Interrupt[0].hooks[0].timeout, 3);
|
|
97
|
+
assert.equal(
|
|
98
|
+
installedHooks.hooks.Interrupt[0].hooks[0].command,
|
|
99
|
+
`bash "${path.join(withCodex, '.devlog-tracker', 'codex', 'hooks', 'on-interrupt.sh')}"`
|
|
100
|
+
);
|
|
101
|
+
assert.equal(
|
|
102
|
+
installedHooks.hooks.SubagentStart[0].hooks[0].command,
|
|
103
|
+
`bash "${path.join(withCodex, '.devlog-tracker', 'codex', 'hooks', 'on-subagent-start.sh')}"`
|
|
104
|
+
);
|
|
95
105
|
|
|
96
106
|
const upgraded = tmp();
|
|
97
107
|
const legacy = path.join(upgraded, '.codex', 'prompts');
|
package/cli/platforms/claude.js
CHANGED
|
@@ -21,6 +21,7 @@ function claudeBlock() {
|
|
|
21
21
|
| 接續上一題 / continue(換過工具或 \`/clear\` 之後) | \`commands/continue.md\` |
|
|
22
22
|
| 歸檔 / compact | \`commands/compact.md\` |
|
|
23
23
|
| 清空重編 / clean(不可復原,先問使用者確認) | \`commands/clean.md\` |
|
|
24
|
+
| 舊格式 Handoff 轉 XML / migrate(Stop hook 擋下舊格式時直接跑) | \`commands/migrate.md\` |
|
|
24
25
|
| 保存主題 / keep、接續具名檔 / resume、跨主題總覽 / overview | \`commands/keep.md\`、\`commands/resume.md\`、\`commands/overview.md\` |
|
|
25
26
|
| 整理所有 devlog(跨分支檔、archive、既有 keep 檔重新分主題) / keep-all | \`commands/keep-all.md\` |
|
|
26
27
|
| 沉澱規範寫進 CLAUDE.md/AGENTS.md / promote | \`commands/promote.md\` |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Codex Interrupt: close the active round as interrupted. The event is
|
|
3
|
+
# advisory and has a short timeout, so failures must not affect Codex.
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
_src="${BASH_SOURCE[0]}"
|
|
6
|
+
SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
|
|
7
|
+
PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" 2>/dev/null && pwd)"
|
|
8
|
+
[ -d "$PLUGIN_SCRIPTS" ] || exit 0
|
|
9
|
+
INPUT="$(cat 2>/dev/null || true)"
|
|
10
|
+
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
11
|
+
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
12
|
+
bash "$PLUGIN_SCRIPTS/close-open-round.sh" "Interrupt:cancelled" >/dev/null 2>&1 || true
|
|
13
|
+
exit 0
|
|
@@ -10,10 +10,78 @@ _src="${BASH_SOURCE[0]}"
|
|
|
10
10
|
SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
|
|
11
11
|
PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" 2>/dev/null && pwd)"
|
|
12
12
|
[ -d "$PLUGIN_SCRIPTS" ] || exit 0
|
|
13
|
+
# shellcheck source=../../core/scripts/json-field.sh
|
|
14
|
+
. "$PLUGIN_SCRIPTS/json-field.sh"
|
|
13
15
|
INPUT="$(cat 2>/dev/null || true)"
|
|
14
16
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
15
17
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
18
|
+
|
|
19
|
+
codex_access_payload() {
|
|
20
|
+
printf '{"tool_name":"%s","tool_input":{"file_path":".devlog/.round-current.md"},"session_id":"%s","agent_id":"%s"}' \
|
|
21
|
+
"$1" "$(json_escape "$(json_str_field "$INPUT" session_id)")" "$(json_escape "$(json_str_field "$INPUT" agent_id)")"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
TOOL_NAME="$(json_str_field "$INPUT" tool_name)"
|
|
25
|
+
if [ "$TOOL_NAME" = Bash ] || [ "$TOOL_NAME" = apply_patch ]; then
|
|
26
|
+
if command -v jq >/dev/null 2>&1; then
|
|
27
|
+
COMMAND="$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || true)"
|
|
28
|
+
else
|
|
29
|
+
COMMAND="$(json_str_field "$INPUT" command)"
|
|
30
|
+
fi
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Codex CLI reads files through Bash. Permit a single cat of the current
|
|
34
|
+
# devlog while the core valve is blocking other tools.
|
|
35
|
+
if [ "$TOOL_NAME" = Bash ]; then
|
|
36
|
+
CWD="$(json_str_field "$INPUT" cwd)"
|
|
37
|
+
case "$COMMAND" in
|
|
38
|
+
"cat $ROOT/.devlog/.round-current.md"|"cat \"$ROOT/.devlog/.round-current.md\"")
|
|
39
|
+
INPUT="$(codex_access_payload Read)"
|
|
40
|
+
;;
|
|
41
|
+
'cat .devlog/.round-current.md'|'cat ".devlog/.round-current.md"')
|
|
42
|
+
[ "$CWD" != "$ROOT" ] || INPUT="$(codex_access_payload Read)"
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# During a blocked segment, the core permits edits to the current devlog.
|
|
48
|
+
# Codex's file editor is apply_patch, whose only file path is inside the patch
|
|
49
|
+
# string. Normalize only patches that exclusively add/update that file.
|
|
50
|
+
if [ "$TOOL_NAME" = apply_patch ]; then
|
|
51
|
+
CWD="$(json_str_field "$INPUT" cwd)"
|
|
52
|
+
[ -n "$CWD" ] || CWD="$ROOT"
|
|
53
|
+
PATCH="$COMMAND"
|
|
54
|
+
if printf '%s\n' "$PATCH" | awk -v cwd="$CWD" -v root="$ROOT" '
|
|
55
|
+
function normalize(path, count, parts, stack, depth, i, result) {
|
|
56
|
+
if (substr(path, 1, 1) != "/") path = cwd "/" path
|
|
57
|
+
count = split(path, parts, "/")
|
|
58
|
+
depth = 0
|
|
59
|
+
for (i = 1; i <= count; i++) {
|
|
60
|
+
if (parts[i] == "" || parts[i] == ".") continue
|
|
61
|
+
if (parts[i] == "..") { if (depth > 0) depth--; continue }
|
|
62
|
+
stack[++depth] = parts[i]
|
|
63
|
+
}
|
|
64
|
+
result = ""
|
|
65
|
+
for (i = 1; i <= depth; i++) result = result "/" stack[i]
|
|
66
|
+
return result == "" ? "/" : result
|
|
67
|
+
}
|
|
68
|
+
NR == 1 && $0 != "*** Begin Patch" { bad = 1 }
|
|
69
|
+
/^\*\*\* (Add|Update|Delete) File: / {
|
|
70
|
+
path = $0; sub(/^\*\*\* (Add|Update|Delete) File: /, "", path)
|
|
71
|
+
if ($0 ~ /^\*\*\* Delete File: / || normalize(path) != normalize(root "/.devlog/.round-current.md")) bad = 1
|
|
72
|
+
files++
|
|
73
|
+
}
|
|
74
|
+
/^\*\*\* Move to: / { bad = 1 }
|
|
75
|
+
$0 == "*** End Patch" { ended = 1; next }
|
|
76
|
+
END { exit !(files > 0 && ended && !bad) }
|
|
77
|
+
'; then
|
|
78
|
+
INPUT="$(codex_access_payload Write)"
|
|
79
|
+
fi
|
|
80
|
+
fi
|
|
16
81
|
printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/segment-watch.sh"
|
|
17
82
|
RC=$?
|
|
18
|
-
[ "$RC" -eq 2 ]
|
|
83
|
+
if [ "$RC" -eq 2 ]; then
|
|
84
|
+
printf 'Codex CLI:先用 cat "%s/.devlog/.round-current.md" 讀取,再用 apply_patch 只修改這份檔案;完成後重試原工具。\n' "$ROOT" >&2
|
|
85
|
+
exit 2
|
|
86
|
+
fi
|
|
19
87
|
exit 0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Codex SessionEnd wrapper. on-session-end.sh (shared) reads a "reason"
|
|
3
|
-
# field. Codex
|
|
4
|
-
#
|
|
3
|
+
# field. Codex documents `reason` (currently only `other`); accept older
|
|
4
|
+
# `why` payloads too and default to
|
|
5
5
|
# "unknown" rather than guessing further, and rebuild a synthetic payload
|
|
6
6
|
# so the shared script's json_str_field lookup always finds "reason".
|
|
7
7
|
set -uo pipefail
|
|
@@ -14,10 +14,10 @@ export DEVLOG_PROJECT_DIR="$ROOT"
|
|
|
14
14
|
|
|
15
15
|
REASON=""
|
|
16
16
|
if command -v jq >/dev/null 2>&1; then
|
|
17
|
-
REASON="$(printf '%s' "$INPUT" | jq -r '.
|
|
17
|
+
REASON="$(printf '%s' "$INPUT" | jq -r '.reason // .why // empty' 2>/dev/null || true)"
|
|
18
18
|
else
|
|
19
|
-
REASON="$(printf '%s' "$INPUT" | grep -o '"
|
|
20
|
-
[ -n "$REASON" ] || REASON="$(printf '%s' "$INPUT" | grep -o '"
|
|
19
|
+
REASON="$(printf '%s' "$INPUT" | grep -o '"reason"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"reason"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || true)"
|
|
20
|
+
[ -n "$REASON" ] || REASON="$(printf '%s' "$INPUT" | grep -o '"why"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"why"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || true)"
|
|
21
21
|
fi
|
|
22
22
|
[ -n "$REASON" ] || REASON="unknown"
|
|
23
23
|
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
# JSON-in shape as Claude Code, so we assume the same is true for stdout-out
|
|
6
6
|
# here and skip any envelope too.
|
|
7
7
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# We try both and default to "startup" on purpose: session-start-devlog.sh
|
|
8
|
+
# Codex's documented SessionStart field is `source`. Accept older `how`
|
|
9
|
+
# payloads too, and default to "startup" on purpose: session-start-devlog.sh
|
|
11
10
|
# SKIPS the dangling-round heal when source is missing/unrecognized (same as
|
|
12
11
|
# `compact`); the wrapper forces SRC="startup" so healing runs.
|
|
13
12
|
set -uo pipefail
|
|
@@ -20,10 +19,10 @@ export DEVLOG_PROJECT_DIR="$ROOT"
|
|
|
20
19
|
|
|
21
20
|
SRC=""
|
|
22
21
|
if command -v jq >/dev/null 2>&1; then
|
|
23
|
-
SRC="$(printf '%s' "$INPUT" | jq -r '.
|
|
22
|
+
SRC="$(printf '%s' "$INPUT" | jq -r '.source // .how // empty' 2>/dev/null || true)"
|
|
24
23
|
else
|
|
25
|
-
SRC="$(printf '%s' "$INPUT" | grep -o '"
|
|
26
|
-
[ -n "$SRC" ] || SRC="$(printf '%s' "$INPUT" | grep -o '"
|
|
24
|
+
SRC="$(printf '%s' "$INPUT" | grep -o '"source"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"source"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || true)"
|
|
25
|
+
[ -n "$SRC" ] || SRC="$(printf '%s' "$INPUT" | grep -o '"how"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"how"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || true)"
|
|
27
26
|
fi
|
|
28
27
|
[ -n "$SRC" ] || SRC="startup"
|
|
29
28
|
|
package/codex/hooks/on-stop.sh
CHANGED
|
@@ -8,11 +8,12 @@ set -uo pipefail
|
|
|
8
8
|
_src="${BASH_SOURCE[0]}"
|
|
9
9
|
SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
|
|
10
10
|
PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" 2>/dev/null && pwd)"
|
|
11
|
-
[ -d "$PLUGIN_SCRIPTS" ] || exit 0
|
|
11
|
+
[ -d "$PLUGIN_SCRIPTS" ] || { printf '{}\n'; exit 0; }
|
|
12
12
|
INPUT="$(cat 2>/dev/null || true)"
|
|
13
13
|
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
14
14
|
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
15
|
-
printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/enforce-devlog.sh"
|
|
15
|
+
printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/enforce-devlog.sh" >/dev/null
|
|
16
16
|
RC=$?
|
|
17
17
|
[ "$RC" -eq 2 ] && exit 2
|
|
18
|
+
printf '{}\n'
|
|
18
19
|
exit 0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Codex SubagentStart: give the child the shared Lessons Mode guidance.
|
|
3
|
+
set -uo pipefail
|
|
4
|
+
_src="${BASH_SOURCE[0]}"
|
|
5
|
+
SCRIPT_DIR="$(cd "${_src%/*}" && pwd)"
|
|
6
|
+
PLUGIN_SCRIPTS="$(cd "$SCRIPT_DIR/../../core/scripts" 2>/dev/null && pwd)"
|
|
7
|
+
[ -d "$PLUGIN_SCRIPTS" ] || exit 0
|
|
8
|
+
INPUT="$(cat 2>/dev/null || true)"
|
|
9
|
+
ROOT="$(printf '%s' "$INPUT" | bash "$SCRIPT_DIR/project-dir.sh")"
|
|
10
|
+
# A subagent may run in a separate worktree. Installed hook scripts live under
|
|
11
|
+
# <project>/.devlog-tracker/codex/hooks, so their path identifies the parent.
|
|
12
|
+
case "$SCRIPT_DIR" in
|
|
13
|
+
*/.devlog-tracker/codex/hooks)
|
|
14
|
+
ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
15
|
+
;;
|
|
16
|
+
esac
|
|
17
|
+
export DEVLOG_PROJECT_DIR="$ROOT"
|
|
18
|
+
printf '%s' "$INPUT" | bash "$PLUGIN_SCRIPTS/lessons-subagent-start.sh" || true
|
|
19
|
+
exit 0
|
|
@@ -10,4 +10,19 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
10
10
|
else
|
|
11
11
|
ROOT="$(printf '%s' "$INPUT" | grep -o '"cwd"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | sed 's/.*"cwd"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || true)"
|
|
12
12
|
fi
|
|
13
|
-
|
|
13
|
+
[ -n "$ROOT" ] || ROOT=.
|
|
14
|
+
# Codex can start from a subdirectory while loading the repo's hooks.json.
|
|
15
|
+
# npx uses the vendored marker; a Codex plugin has no project-local vendor
|
|
16
|
+
# directory, so use the nearest tracked project or existing .devlog root.
|
|
17
|
+
SEARCH="$ROOT"
|
|
18
|
+
while [ -d "$SEARCH" ]; do
|
|
19
|
+
if [ -d "$SEARCH/.devlog-tracker" ] || [ -d "$SEARCH/.devlog" ] || [ -e "$SEARCH/.git" ]; then
|
|
20
|
+
printf '%s\n' "$SEARCH"
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
PARENT="${SEARCH%/*}"
|
|
24
|
+
[ -n "$PARENT" ] || PARENT=/
|
|
25
|
+
[ "$PARENT" != "$SEARCH" ] || break
|
|
26
|
+
SEARCH="$PARENT"
|
|
27
|
+
done
|
|
28
|
+
printf '%s\n' "$ROOT"
|