devlog-tracker 0.22.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 (120) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +234 -0
  3. package/bin/devlog-tracker.js +51 -0
  4. package/cli/init.js +55 -0
  5. package/cli/init.test.js +37 -0
  6. package/cli/merge-hooks.js +60 -0
  7. package/cli/merge-hooks.test.js +187 -0
  8. package/cli/platforms/codex.js +13 -0
  9. package/cli/platforms/cursor.js +13 -0
  10. package/cli/status.js +14 -0
  11. package/cli/status.test.js +33 -0
  12. package/cli/vendor.js +37 -0
  13. package/cli/vendor.test.js +84 -0
  14. package/codex/hooks/on-pre-tool.sh +19 -0
  15. package/codex/hooks/on-session-end.sh +31 -0
  16. package/codex/hooks/on-session-start.sh +37 -0
  17. package/codex/hooks/on-stop.sh +18 -0
  18. package/codex/hooks/on-user-prompt-submit.sh +15 -0
  19. package/codex/hooks/project-dir.sh +13 -0
  20. package/codex/hooks/test-adapters.sh +98 -0
  21. package/codex/hooks.json +54 -0
  22. package/commands/checkpoint.md +17 -0
  23. package/commands/clean.md +50 -0
  24. package/commands/compact.md +24 -0
  25. package/commands/continue.md +30 -0
  26. package/commands/keep.md +100 -0
  27. package/commands/lessons-drift.md +19 -0
  28. package/commands/lessons-off.md +16 -0
  29. package/commands/lessons-on.md +20 -0
  30. package/commands/lessons.md +20 -0
  31. package/commands/overview.md +32 -0
  32. package/commands/pause.md +20 -0
  33. package/commands/resume.md +18 -0
  34. package/commands/segment-watch.md +21 -0
  35. package/commands/span.md +13 -0
  36. package/commands/start.md +19 -0
  37. package/commands/status.md +14 -0
  38. package/cursor/hooks/on-pre-tool.sh +24 -0
  39. package/cursor/hooks/on-session-end.sh +11 -0
  40. package/cursor/hooks/on-session-start.sh +15 -0
  41. package/cursor/hooks/on-stop.sh +44 -0
  42. package/cursor/hooks/on-submit-prompt.sh +28 -0
  43. package/cursor/hooks/on-tool-failure.sh +16 -0
  44. package/cursor/hooks/project-dir.sh +10 -0
  45. package/cursor/hooks/test-adapters.sh +201 -0
  46. package/cursor/hooks.json +26 -0
  47. package/hooks/scripts/await-open.sh +23 -0
  48. package/hooks/scripts/checkpoint-set.sh +44 -0
  49. package/hooks/scripts/clean-devlog.sh +69 -0
  50. package/hooks/scripts/close-open-round.sh +186 -0
  51. package/hooks/scripts/compact-devlog.sh +72 -0
  52. package/hooks/scripts/detect-pending-question.sh +31 -0
  53. package/hooks/scripts/devlog-lock.sh +38 -0
  54. package/hooks/scripts/devlog-md.sh +222 -0
  55. package/hooks/scripts/devlog-path.sh +70 -0
  56. package/hooks/scripts/enforce-devlog.sh +629 -0
  57. package/hooks/scripts/files-snapshot.sh +70 -0
  58. package/hooks/scripts/json-field.sh +76 -0
  59. package/hooks/scripts/keep-move.sh +155 -0
  60. package/hooks/scripts/kept-list.sh +40 -0
  61. package/hooks/scripts/lessons-append.sh +93 -0
  62. package/hooks/scripts/lessons-drift-set.sh +42 -0
  63. package/hooks/scripts/lessons-off.sh +15 -0
  64. package/hooks/scripts/lessons-on.sh +21 -0
  65. package/hooks/scripts/lessons-read.sh +60 -0
  66. package/hooks/scripts/on-session-end.sh +14 -0
  67. package/hooks/scripts/on-stop-failure.sh +18 -0
  68. package/hooks/scripts/on-tool-failure.sh +25 -0
  69. package/hooks/scripts/pause-devlog.sh +13 -0
  70. package/hooks/scripts/redact-prompt.sh +19 -0
  71. package/hooks/scripts/resume-devlog.sh +41 -0
  72. package/hooks/scripts/round-start.sh +333 -0
  73. package/hooks/scripts/run-tests.sh +26 -0
  74. package/hooks/scripts/segment-watch-set.sh +47 -0
  75. package/hooks/scripts/segment-watch.sh +197 -0
  76. package/hooks/scripts/session-start-devlog.sh +157 -0
  77. package/hooks/scripts/span-close.sh +9 -0
  78. package/hooks/scripts/span-open.sh +21 -0
  79. package/hooks/scripts/start-devlog.sh +24 -0
  80. package/hooks/scripts/status-devlog.sh +43 -0
  81. package/hooks/scripts/tests/test-await-open.sh +112 -0
  82. package/hooks/scripts/tests/test-branch-scoped-integration.sh +108 -0
  83. package/hooks/scripts/tests/test-checkpoint-set.sh +36 -0
  84. package/hooks/scripts/tests/test-clean-devlog.sh +160 -0
  85. package/hooks/scripts/tests/test-cli-init-e2e.sh +37 -0
  86. package/hooks/scripts/tests/test-close-open-round.sh +325 -0
  87. package/hooks/scripts/tests/test-compact-devlog.sh +62 -0
  88. package/hooks/scripts/tests/test-devlog-lock.sh +37 -0
  89. package/hooks/scripts/tests/test-devlog-md.sh +530 -0
  90. package/hooks/scripts/tests/test-devlog-path.sh +118 -0
  91. package/hooks/scripts/tests/test-enforce-devlog-files.sh +233 -0
  92. package/hooks/scripts/tests/test-enforce-devlog-handoff-order.sh +223 -0
  93. package/hooks/scripts/tests/test-enforce-devlog-workspace.sh +333 -0
  94. package/hooks/scripts/tests/test-enforce-devlog.sh +1290 -0
  95. package/hooks/scripts/tests/test-files-snapshot.sh +142 -0
  96. package/hooks/scripts/tests/test-json-field.sh +57 -0
  97. package/hooks/scripts/tests/test-keep-move.sh +127 -0
  98. package/hooks/scripts/tests/test-kept-list.sh +80 -0
  99. package/hooks/scripts/tests/test-lessons-append.sh +138 -0
  100. package/hooks/scripts/tests/test-lessons-drift-set.sh +36 -0
  101. package/hooks/scripts/tests/test-lessons-on-off.sh +75 -0
  102. package/hooks/scripts/tests/test-lessons-read.sh +97 -0
  103. package/hooks/scripts/tests/test-on-interrupt.sh +186 -0
  104. package/hooks/scripts/tests/test-redact-prompt.sh +27 -0
  105. package/hooks/scripts/tests/test-resume-devlog.sh +35 -0
  106. package/hooks/scripts/tests/test-round-start.sh +825 -0
  107. package/hooks/scripts/tests/test-segment-watch-set.sh +75 -0
  108. package/hooks/scripts/tests/test-segment-watch.sh +468 -0
  109. package/hooks/scripts/tests/test-session-start-devlog.sh +508 -0
  110. package/hooks/scripts/tests/test-start-pause-devlog.sh +84 -0
  111. package/hooks/scripts/tests/test-status-span.sh +50 -0
  112. package/hooks/scripts/tests/test-workspace-snapshot.sh +130 -0
  113. package/hooks/scripts/workspace-snapshot.sh +69 -0
  114. package/package.json +37 -0
  115. package/skills/devlog-tracker/SKILL.md +411 -0
  116. package/skills/devlog-tracker/references/checkpoint-mode.md +42 -0
  117. package/skills/devlog-tracker/references/lessons-mode.md +32 -0
  118. package/skills/devlog-tracker/references/reply-fold.md +129 -0
  119. package/skills/devlog-tracker/references/round-segments.md +55 -0
  120. package/skills/devlog-tracker/references/span-mode.md +65 -0
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env bash
2
+ # Self-check for workspace-snapshot.sh's workspace_snapshot(), covering the
3
+ # seven canonical `#### 工作區` formats from skills/devlog-tracker/SKILL.md.
4
+ # No framework — plain assert-and-exit, matching this repo's existing style.
5
+ set -uo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
8
+ . "$SCRIPT_DIR/workspace-snapshot.sh"
9
+ . "$SCRIPT_DIR/devlog-md.sh"
10
+
11
+ TMP_ROOT="$(mktemp -d)"
12
+ trap 'rm -rf "$TMP_ROOT"' EXIT
13
+
14
+ FAIL=0
15
+ assert_eq() {
16
+ local desc="$1" expected="$2" actual="$3"
17
+ if [ "$actual" = "$expected" ]; then
18
+ echo "PASS: $desc"
19
+ else
20
+ echo "FAIL: $desc"
21
+ printf ' expected: %s\n' "$expected"
22
+ printf ' actual: %s\n' "$actual"
23
+ FAIL=1
24
+ fi
25
+ }
26
+
27
+ # --- non-git directory ----------------------------------------------------
28
+ NONGIT="$TMP_ROOT/nongit"
29
+ mkdir -p "$NONGIT"
30
+ OUT="$(workspace_snapshot "$NONGIT")"
31
+ assert_eq "non-git directory" "非 git 工作區" "$OUT"
32
+
33
+ # --- clean branch -----------------------------------------------------------
34
+ REPO="$TMP_ROOT/repo"
35
+ mkdir -p "$REPO"
36
+ git -C "$REPO" init -q -b main
37
+ git -C "$REPO" config user.email test@example.com
38
+ git -C "$REPO" config user.name test
39
+ echo hello > "$REPO/a.txt"
40
+ git -C "$REPO" add a.txt
41
+ git -C "$REPO" commit -q -m init
42
+ HASH="$(git -C "$REPO" rev-parse --short HEAD)"
43
+ OUT="$(workspace_snapshot "$REPO")"
44
+ assert_eq "clean branch" "main @ ${HASH},工作樹乾淨" "$OUT"
45
+
46
+ # --- dirty branch, two files -------------------------------------------------
47
+ echo change >> "$REPO/a.txt"
48
+ echo new > "$REPO/b.txt"
49
+ OUT="$(workspace_snapshot "$REPO")"
50
+ EXPECTED="main @ ${HASH}
51
+ 未提交:a.txt, b.txt"
52
+ assert_eq "dirty branch, two files" "$EXPECTED" "$OUT"
53
+
54
+ # --- detached, clean ----------------------------------------------------------
55
+ git -C "$REPO" reset --hard -q "$HASH"
56
+ git -C "$REPO" clean -fdq
57
+ git -C "$REPO" checkout -q "$HASH" 2>/dev/null
58
+ OUT="$(workspace_snapshot "$REPO")"
59
+ assert_eq "detached, clean" "HEAD detached @ ${HASH}" "$OUT"
60
+
61
+ # --- detached, dirty -----------------------------------------------------------
62
+ echo dirty >> "$REPO/a.txt"
63
+ OUT="$(workspace_snapshot "$REPO")"
64
+ EXPECTED="HEAD detached @ ${HASH}
65
+ 未提交:a.txt"
66
+ assert_eq "detached, dirty" "$EXPECTED" "$OUT"
67
+
68
+ # --- unborn branch (git init, zero commits), clean --------------------------
69
+ UNBORN="$TMP_ROOT/unborn"
70
+ mkdir -p "$UNBORN"
71
+ git -C "$UNBORN" init -q -b main
72
+ OUT="$(workspace_snapshot "$UNBORN")"
73
+ assert_eq "unborn branch, clean" "main @ (尚無 commit),工作樹乾淨" "$OUT"
74
+
75
+ # --- unborn branch, dirty (untracked file, still no commits) ----------------
76
+ echo new > "$UNBORN/c.txt"
77
+ OUT="$(workspace_snapshot "$UNBORN")"
78
+ EXPECTED_UNBORN="main @ (尚無 commit)
79
+ 未提交:c.txt"
80
+ assert_eq "unborn branch, dirty" "$EXPECTED_UNBORN" "$OUT"
81
+
82
+ CLI_UNBORN="$(bash "$SCRIPT_DIR/workspace-snapshot.sh" "$UNBORN")"
83
+ FUNC_UNBORN="$(workspace_snapshot "$UNBORN")"
84
+ assert_eq "cli matches function (unborn dirty)" "$FUNC_UNBORN" "$CLI_UNBORN"
85
+
86
+ # --- executable entry matches the sourced function --------------------------
87
+ CLI_NONGIT="$(bash "$SCRIPT_DIR/workspace-snapshot.sh" "$NONGIT")"
88
+ assert_eq "cli non-git directory" "非 git 工作區" "$CLI_NONGIT"
89
+
90
+ CLI_DETACHED="$(bash "$SCRIPT_DIR/workspace-snapshot.sh" "$REPO")"
91
+ FUNC_DETACHED="$(workspace_snapshot "$REPO")"
92
+ assert_eq "cli matches function (detached dirty)" "$FUNC_DETACHED" "$CLI_DETACHED"
93
+
94
+ DEFAULT_DIR="$TMP_ROOT/default-dir"
95
+ mkdir -p "$DEFAULT_DIR"
96
+ CLI_DEFAULT="$(CLAUDE_PROJECT_DIR="$DEFAULT_DIR" bash "$SCRIPT_DIR/workspace-snapshot.sh")"
97
+ assert_eq "cli default dir is CLAUDE_PROJECT_DIR" "非 git 工作區" "$CLI_DEFAULT"
98
+
99
+ # --- claim state vs live snapshot ------------------------------------------
100
+ LIVE_NOW="$(workspace_snapshot "$REPO")"
101
+ cat > "$TMP_ROOT/round.md" <<EOF
102
+ ## Round 1 — 2026-09-11T00:00:00+08:00
103
+
104
+ ### Handoff
105
+ #### 工作區
106
+ ${LIVE_NOW}
107
+
108
+ ### Status
109
+ IN_PROGRESS
110
+ EOF
111
+ assert_eq "claim MATCH on live snapshot" "MATCH" "$(workspace_claim_state "$REPO" "$TMP_ROOT/round.md")"
112
+ cat > "$TMP_ROOT/stale.md" <<'EOF'
113
+ ## Round 1 — 2026-09-11T00:00:00+08:00
114
+
115
+ ### Handoff
116
+ #### 工作區
117
+ main @ deadbeef,工作樹乾淨
118
+
119
+ ### Status
120
+ IN_PROGRESS
121
+ EOF
122
+ assert_eq "claim MISMATCH on stale hash" "MISMATCH" "$(workspace_claim_state "$REPO" "$TMP_ROOT/stale.md")"
123
+
124
+ if [ "$FAIL" -eq 0 ]; then
125
+ echo "All checks passed."
126
+ exit 0
127
+ else
128
+ echo "Some checks FAILED."
129
+ exit 1
130
+ fi
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck shell=bash
3
+ # Sourced by enforce-devlog.sh (write-time Stop check) and executed by
4
+ # continue / resume (read-time verify). Computes the canonical `#### 工作區`
5
+ # text using the seven output formats defined in skills/devlog-tracker/SKILL.md.
6
+ # This function is the only machine producer — do not re-encode in command docs.
7
+ #
8
+ # Usage (sourced): workspace_snapshot "$PROJECT_DIR"
9
+ # Usage (executed): bash workspace-snapshot.sh [dir]
10
+ # dir defaults to ${CLAUDE_PROJECT_DIR:-.}
11
+ # Echoes 1 line (clean / non-git / detached-clean / unborn-clean) or 2 lines
12
+ # (dirty / detached-dirty / unborn-dirty). Never fails: any git command error
13
+ # degrades toward "非 git 工作區", matching this hook suite's fail-open design.
14
+ #
15
+ # Known limitation: a dirty file path containing a space is not exactly
16
+ # reversible from `git status --short`'s last-field extraction below — same
17
+ # limitation the hand-written 工作區 convention already has.
18
+ workspace_snapshot() {
19
+ local dir="${1:-.}"
20
+ if ! git -C "$dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
21
+ printf '非 git 工作區\n'
22
+ return 0
23
+ fi
24
+ local branch hash label status_out dirty_files
25
+ branch="$(git -C "$dir" rev-parse --abbrev-ref HEAD 2>/dev/null || echo '')"
26
+ hash="$(git -C "$dir" rev-parse --short HEAD 2>/dev/null || echo '')"
27
+ if [ -z "$hash" ]; then
28
+ # No commits yet (unborn branch, e.g. right after `git init`) is a valid
29
+ # git workspace, not "非 git 工作區" — reserve that message for a
30
+ # genuinely broken/non-git state. An unborn branch still resolves a
31
+ # symbolic HEAD (`git symbolic-ref` succeeds, unlike `rev-parse
32
+ # --abbrev-ref HEAD` which fails on it too); if symbolic-ref also fails,
33
+ # fall through to the original fail-open message.
34
+ local unborn_branch
35
+ unborn_branch="$(git -C "$dir" symbolic-ref -q --short HEAD 2>/dev/null || echo '')"
36
+ if [ -z "$unborn_branch" ]; then
37
+ printf '非 git 工作區\n'
38
+ return 0
39
+ fi
40
+ status_out="$(git -C "$dir" status --short 2>/dev/null || echo '')"
41
+ if [ -z "$status_out" ]; then
42
+ printf '%s @ (尚無 commit),工作樹乾淨\n' "$unborn_branch"
43
+ else
44
+ dirty_files="$(printf '%s\n' "$status_out" | awk '{printf "%s%s", (NR>1?", ":""), $NF}')"
45
+ printf '%s @ (尚無 commit)\n未提交:%s\n' "$unborn_branch" "$dirty_files"
46
+ fi
47
+ return 0
48
+ fi
49
+ if [ "$branch" = "HEAD" ] || [ -z "$branch" ]; then
50
+ label="HEAD detached"
51
+ else
52
+ label="$branch"
53
+ fi
54
+ status_out="$(git -C "$dir" status --short 2>/dev/null || echo '')"
55
+ if [ -z "$status_out" ]; then
56
+ if [ "$label" = "HEAD detached" ]; then
57
+ printf '%s @ %s\n' "$label" "$hash"
58
+ else
59
+ printf '%s @ %s,工作樹乾淨\n' "$label" "$hash"
60
+ fi
61
+ else
62
+ dirty_files="$(printf '%s\n' "$status_out" | awk '{printf "%s%s", (NR>1?", ":""), $NF}')"
63
+ printf '%s @ %s\n未提交:%s\n' "$label" "$hash" "$dirty_files"
64
+ fi
65
+ }
66
+
67
+ if [ "${BASH_SOURCE[0]}" = "$0" ]; then
68
+ workspace_snapshot "${1:-${CLAUDE_PROJECT_DIR:-.}}"
69
+ fi
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "devlog-tracker",
3
+ "version": "0.22.0",
4
+ "description": "npx installer for the devlog-tracker Claude Code plugin's Cursor and Codex adapters.",
5
+ "bin": {
6
+ "devlog-tracker": "bin/devlog-tracker.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "cli",
11
+ "hooks/scripts",
12
+ "codex/hooks",
13
+ "cursor/hooks",
14
+ "skills",
15
+ "commands",
16
+ "codex/hooks.json",
17
+ "cursor/hooks.json"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "scripts": {
23
+ "test": "node --test cli/*.test.js scripts/*.test.js",
24
+ "version": "node scripts/sync-version.js && git add .claude-plugin/plugin.json .claude-plugin/marketplace.json README.md"
25
+ },
26
+ "license": "Apache-2.0",
27
+ "author": {
28
+ "name": "gogogohuang"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/gogogohuang/devlog-tracker.git"
33
+ },
34
+ "keywords": [
35
+ "devlog"
36
+ ]
37
+ }