devlog-tracker 0.25.0 → 0.25.1
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 +7 -6
- package/cli/agents-md.js +4 -4
- package/cli/agents-md.test.js +16 -8
- package/cli/platforms/codex.js +29 -10
- package/hooks/scripts/tests/test-cli-init-e2e.sh +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# devlog-tracker
|
|
2
2
|
|
|
3
|
-
**版本** 0.25.
|
|
3
|
+
**版本** 0.25.1
|
|
4
4
|
|
|
5
5
|
在專案中維護一份 `.devlog/devlog.md`,把每一輪對話的請求、決策與結果寫成永久紀錄。對話一 `/clear` 或換 session 就沒了;這份檔案取代那個缺口,讓工作可以中斷再接。沒下過 `/devlog-tracker:start` 時,裝著也不會動任何檔案。
|
|
6
6
|
|
|
@@ -46,8 +46,10 @@ npx devlog-tracker init --codex --cursor
|
|
|
46
46
|
其他工具已設定的 hook)。重新執行 `npx devlog-tracker init` 可以升級到套件目前的
|
|
47
47
|
版本;`npx devlog-tracker status` 可以查目前裝的版本是否落後。
|
|
48
48
|
|
|
49
|
-
裝 Codex 時,`init` 會從 `.devlog-tracker/commands/*.md`
|
|
50
|
-
`.
|
|
49
|
+
裝 Codex 時,`init` 會從 `.devlog-tracker/commands/*.md` 產生對應的專案 skill 到
|
|
50
|
+
`.agents/skills/devlog-<名稱>/SKILL.md`,例如在 Codex 打 `$devlog-start`(或用 `/skills`
|
|
51
|
+
選)啟動追蹤。舊版(0.25.0)寫到 `.codex/prompts/` 的檔案會在重跑 `init` 時清掉——
|
|
52
|
+
Codex 不讀專案層級的 custom prompts。也會在專案根目錄的
|
|
51
53
|
`AGENTS.md` 加上(或更新)一段以 `<!-- devlog-tracker:begin/end -->` 包住的 fallback
|
|
52
54
|
說明。區塊外的內容不會動,重跑 `init` 只會換掉區塊本身;區塊裡只有相對路徑,可以 commit。
|
|
53
55
|
|
|
@@ -106,9 +108,8 @@ Codex 目前沒有對應「使用者中斷」(Claude Code 的 `PostToolUseFail
|
|
|
106
108
|
Codex 上不會被標記成 `INTERRUPTED`;核心強制記錄機制(`Stop` 事件擋住未寫完的
|
|
107
109
|
輪次)不受影響。
|
|
108
110
|
|
|
109
|
-
Codex 使用
|
|
110
|
-
|
|
111
|
-
使用以下腳本(`commands/*.md` 會優先讀 `CLAUDE_PLUGIN_ROOT`,否則讀
|
|
111
|
+
Codex 使用 `$devlog-<名稱>`(例如 `$devlog-start`)執行指令;它們會讀與 Claude
|
|
112
|
+
commands 相同的內容。若使用的 Codex 環境沒有載入專案 skills,仍可直接使用以下腳本(`commands/*.md` 會優先讀 `CLAUDE_PLUGIN_ROOT`,否則讀
|
|
112
113
|
`DEVLOG_TRACKER_ROOT`):
|
|
113
114
|
|
|
114
115
|
```bash
|
package/cli/agents-md.js
CHANGED
|
@@ -8,16 +8,16 @@ const END = '<!-- devlog-tracker:end -->';
|
|
|
8
8
|
const BLOCK = `${BEGIN}
|
|
9
9
|
## devlog-tracker
|
|
10
10
|
|
|
11
|
-
這個專案用 devlog-tracker 在 \`.devlog/devlog.md\` 維護逐輪紀錄(Claude Code 與 Codex 共用同一份)。Codex
|
|
11
|
+
這個專案用 devlog-tracker 在 \`.devlog/devlog.md\` 維護逐輪紀錄(Claude Code 與 Codex 共用同一份)。Codex 指令是 \`.agents/skills/devlog-<名稱>/\` 底下的 skill,用 \`/skills\` 選或打 \`$devlog-<名稱>\` 執行;若 skill 不可用,請照下面對照做:
|
|
12
12
|
|
|
13
13
|
1. 先 \`source .devlog-tracker/env.sh\`(設定 \`DEVLOG_TRACKER_ROOT\`)。
|
|
14
14
|
2. 依使用者意圖讀對應的 \`.devlog-tracker/commands/<名稱>.md\`,照裡面的步驟做(腳本在 \`.devlog-tracker/hooks/scripts/\`,執行時 \`CLAUDE_PROJECT_DIR\` 設成專案根目錄)。
|
|
15
15
|
|
|
16
16
|
| 使用者說 | 讀這份 |
|
|
17
17
|
|---|---|
|
|
18
|
-
| 開始追蹤 / start |
|
|
19
|
-
| 暫停 / pause |
|
|
20
|
-
| 狀態 / status |
|
|
18
|
+
| 開始追蹤 / start | \`$devlog-start\`;\`commands/start.md\` |
|
|
19
|
+
| 暫停 / pause | \`$devlog-pause\`;\`commands/pause.md\` |
|
|
20
|
+
| 狀態 / status | \`$devlog-status\`;\`commands/status.md\` |
|
|
21
21
|
| 接續上一題 / continue(換過工具或 \`/clear\` 之後) | \`commands/continue.md\` |
|
|
22
22
|
| 歸檔 / compact | \`commands/compact.md\` |
|
|
23
23
|
| 清空重編 / clean(不可復原,先問使用者確認) | \`commands/clean.md\` |
|
package/cli/agents-md.test.js
CHANGED
|
@@ -40,27 +40,35 @@ test('is idempotent and replaces the block in place', () => {
|
|
|
40
40
|
assert.equal(once.split(BEGIN).length, 2);
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
test('block mentions every command doc and Codex
|
|
43
|
+
test('block mentions every command doc and Codex skill naming convention', () => {
|
|
44
44
|
const commandsDir = path.join(__dirname, '..', 'commands');
|
|
45
45
|
const text = fs.readFileSync(upsertAgentsMd(tmp()), 'utf8');
|
|
46
46
|
for (const file of fs.readdirSync(commandsDir).filter((f) => f.endsWith('.md'))) {
|
|
47
47
|
assert.ok(text.includes(`commands/${file}`), `AGENTS.md block is missing commands/${file}`);
|
|
48
48
|
}
|
|
49
|
-
assert.ok(text.includes('
|
|
49
|
+
assert.ok(text.includes('$devlog-start'));
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
test('init --codex writes AGENTS.md and
|
|
52
|
+
test('init --codex writes AGENTS.md and project skills; --cursor alone does not', async () => {
|
|
53
53
|
const repoRoot = path.join(__dirname, '..');
|
|
54
54
|
const withCodex = tmp();
|
|
55
55
|
await run(['--codex'], { repoRoot, targetDir: withCodex, version: '0.0.0' });
|
|
56
56
|
assert.ok(fs.existsSync(path.join(withCodex, 'AGENTS.md')));
|
|
57
57
|
for (const file of fs.readdirSync(path.join(repoRoot, 'commands')).filter((file) => file.endsWith('.md'))) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
assert.match(prompt, /\$ARGUMENTS/);
|
|
58
|
+
const name = `devlog-${path.basename(file, '.md')}`;
|
|
59
|
+
const skill = fs.readFileSync(path.join(withCodex, '.agents', 'skills', name, 'SKILL.md'), 'utf8');
|
|
60
|
+
assert.match(skill, new RegExp(`^---\\nname: ${name}\\ndescription: ".+"\\n---\\n`));
|
|
63
61
|
}
|
|
62
|
+
assert.ok(!fs.existsSync(path.join(withCodex, '.codex', 'prompts')));
|
|
63
|
+
|
|
64
|
+
const upgraded = tmp();
|
|
65
|
+
const legacy = path.join(upgraded, '.codex', 'prompts');
|
|
66
|
+
fs.mkdirSync(legacy, { recursive: true });
|
|
67
|
+
fs.writeFileSync(path.join(legacy, 'devlog-start.md'), 'old');
|
|
68
|
+
fs.writeFileSync(path.join(legacy, 'mine.md'), 'keep');
|
|
69
|
+
await run(['--codex'], { repoRoot, targetDir: upgraded, version: '0.0.0' });
|
|
70
|
+
assert.ok(!fs.existsSync(path.join(legacy, 'devlog-start.md')));
|
|
71
|
+
assert.ok(fs.existsSync(path.join(legacy, 'mine.md')));
|
|
64
72
|
|
|
65
73
|
const cursorOnly = tmp();
|
|
66
74
|
await run(['--cursor'], { repoRoot, targetDir: cursorOnly, version: '0.0.0' });
|
package/cli/platforms/codex.js
CHANGED
|
@@ -4,20 +4,39 @@ const path = require('path');
|
|
|
4
4
|
const { mergeHooksTemplate } = require('../merge-hooks');
|
|
5
5
|
const { upsertAgentsMd } = require('../agents-md');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// Codex 只從 ~/.codex/prompts/ 讀 custom prompts(且已棄用),不會讀專案內的
|
|
8
|
+
// .codex/prompts/;專案層級的指令要做成 .agents/skills/<name>/SKILL.md,以 $<name> 叫用。
|
|
9
|
+
function parseCommand(text) {
|
|
10
|
+
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(text);
|
|
11
|
+
if (!match) return { description: '', body: text };
|
|
12
|
+
const description = /^description:\s*(.*)$/m.exec(match[1]);
|
|
13
|
+
return { description: description ? description[1].trim() : '', body: match[2] };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 0.25.0 曾把 prompts 寫到 .codex/prompts/devlog-*.md,Codex 讀不到;升級時清掉。
|
|
17
|
+
function removeLegacyPrompts(targetDir) {
|
|
9
18
|
const promptsDir = path.join(targetDir, '.codex', 'prompts');
|
|
10
|
-
fs.
|
|
19
|
+
if (!fs.existsSync(promptsDir)) return;
|
|
20
|
+
for (const file of fs.readdirSync(promptsDir)) {
|
|
21
|
+
if (/^devlog-.*\.md$/.test(file)) fs.rmSync(path.join(promptsDir, file));
|
|
22
|
+
}
|
|
23
|
+
if (fs.readdirSync(promptsDir).length === 0) fs.rmdirSync(promptsDir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function installSkills({ targetDir, vendorRoot }) {
|
|
27
|
+
const commandsDir = path.join(vendorRoot, 'commands');
|
|
11
28
|
for (const file of fs.readdirSync(commandsDir)) {
|
|
12
29
|
if (!file.endsWith('.md')) continue;
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
30
|
+
const name = `devlog-${path.basename(file, '.md')}`;
|
|
31
|
+
const { description, body } = parseCommand(fs.readFileSync(path.join(commandsDir, file), 'utf8'));
|
|
32
|
+
const skillDir = path.join(targetDir, '.agents', 'skills', name);
|
|
33
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
16
34
|
fs.writeFileSync(
|
|
17
|
-
|
|
18
|
-
|
|
35
|
+
path.join(skillDir, 'SKILL.md'),
|
|
36
|
+
`---\nname: ${name}\ndescription: ${JSON.stringify(description)}\n---\n\n${body.trim()}\n\n使用者提供的額外參數:請看觸發這個 skill 的使用者訊息。\n`
|
|
19
37
|
);
|
|
20
38
|
}
|
|
39
|
+
removeLegacyPrompts(targetDir);
|
|
21
40
|
}
|
|
22
41
|
|
|
23
42
|
function install({ repoRoot, targetDir, vendorRoot }) {
|
|
@@ -26,8 +45,8 @@ function install({ repoRoot, targetDir, vendorRoot }) {
|
|
|
26
45
|
targetPath: path.join(targetDir, '.codex', 'hooks.json'),
|
|
27
46
|
vendorRoot,
|
|
28
47
|
});
|
|
29
|
-
|
|
48
|
+
installSkills({ targetDir, vendorRoot });
|
|
30
49
|
upsertAgentsMd(targetDir);
|
|
31
50
|
}
|
|
32
51
|
|
|
33
|
-
module.exports = { install,
|
|
52
|
+
module.exports = { install, installSkills };
|
|
@@ -19,7 +19,7 @@ NODE_BIN="$(command -v node)"
|
|
|
19
19
|
[ -f "$PROJECT/.devlog-tracker/VERSION" ] && echo "PASS: vendored VERSION exists" || { echo "FAIL: no VERSION"; FAIL=1; }
|
|
20
20
|
[ -f "$PROJECT/.devlog-tracker/env.sh" ] && echo "PASS: vendored env.sh exists" || { echo "FAIL: no env.sh"; FAIL=1; }
|
|
21
21
|
[ -f "$PROJECT/.codex/hooks.json" ] && echo "PASS: .codex/hooks.json written" || { echo "FAIL: no .codex/hooks.json"; FAIL=1; }
|
|
22
|
-
[ -f "$PROJECT/.
|
|
22
|
+
[ -f "$PROJECT/.agents/skills/devlog-start/SKILL.md" ] && echo "PASS: Codex start skill written" || { echo "FAIL: no Codex start skill"; FAIL=1; }
|
|
23
23
|
[ -f "$PROJECT/.cursor/hooks.json" ] && echo "PASS: .cursor/hooks.json written" || { echo "FAIL: no .cursor/hooks.json"; FAIL=1; }
|
|
24
24
|
|
|
25
25
|
# re-running must not duplicate entries
|