@theglitchking/persistent-planning 3.1.0 → 3.4.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 (63) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.documentation/INDEX.md +15 -7
  4. package/.documentation/REGISTRY.md +2 -2
  5. package/.documentation/agents/INDEX.md +2 -2
  6. package/.documentation/agents/REGISTRY.md +2 -2
  7. package/.documentation/api/INDEX.md +2 -2
  8. package/.documentation/api/REGISTRY.md +2 -2
  9. package/.documentation/architecture/INDEX.md +5 -3
  10. package/.documentation/architecture/REGISTRY.md +8 -4
  11. package/.documentation/architecture/lg-mode.md +16 -0
  12. package/.documentation/architecture/lg-plan-artifact-lifecycle.md +104 -0
  13. package/.documentation/architecture/skill-linking-and-reclaim.md +84 -0
  14. package/.documentation/backups/INDEX.md +2 -2
  15. package/.documentation/backups/REGISTRY.md +2 -2
  16. package/.documentation/database/INDEX.md +2 -2
  17. package/.documentation/database/REGISTRY.md +2 -2
  18. package/.documentation/devops/INDEX.md +2 -2
  19. package/.documentation/devops/REGISTRY.md +2 -2
  20. package/.documentation/features/INDEX.md +2 -2
  21. package/.documentation/features/REGISTRY.md +2 -2
  22. package/.documentation/plans/INDEX.md +2 -2
  23. package/.documentation/plans/REGISTRY.md +2 -2
  24. package/.documentation/procedures/INDEX.md +4 -3
  25. package/.documentation/procedures/REGISTRY.md +6 -4
  26. package/.documentation/procedures/plan-completion-and-archive.md +34 -0
  27. package/.documentation/procedures/plugin-update-delivery.md +99 -0
  28. package/.documentation/quickstart/INDEX.md +2 -2
  29. package/.documentation/quickstart/REGISTRY.md +2 -2
  30. package/.documentation/reference/INDEX.md +10 -3
  31. package/.documentation/reference/REGISTRY.md +8 -4
  32. package/.documentation/reference/mandatory-frontmatter.md +71 -0
  33. package/.documentation/reference/skill-version-marker.md +70 -0
  34. package/.documentation/security/INDEX.md +2 -2
  35. package/.documentation/security/REGISTRY.md +2 -2
  36. package/.documentation/standards/INDEX.md +2 -2
  37. package/.documentation/standards/REGISTRY.md +2 -2
  38. package/.documentation/standards/mandatory-closing-phases.md +21 -1
  39. package/.documentation/testing/INDEX.md +2 -2
  40. package/.documentation/testing/REGISTRY.md +2 -2
  41. package/.documentation/testing/test-suite.md +38 -0
  42. package/.documentation/troubleshooting/INDEX.md +8 -8
  43. package/.documentation/troubleshooting/REGISTRY.md +20 -5
  44. package/.documentation/troubleshooting/plan-never-reads-complete.md +109 -0
  45. package/.documentation/troubleshooting/stale-skill-dir-drift.md +115 -0
  46. package/.documentation/workflows/INDEX.md +2 -2
  47. package/.documentation/workflows/REGISTRY.md +2 -2
  48. package/CHANGELOG.md +149 -0
  49. package/bin/persistent-planning.js +56 -6
  50. package/hooks/session-start.js +115 -3
  51. package/package.json +1 -1
  52. package/scripts/archive-plan.sh +3 -6
  53. package/scripts/init-atom.sh +7 -0
  54. package/scripts/init-phase.sh +58 -6
  55. package/scripts/init-planning.sh +18 -6
  56. package/scripts/init-task.sh +7 -0
  57. package/scripts/lib/planning.sh +122 -0
  58. package/scripts/lib/skill-link.js +177 -0
  59. package/scripts/link-skills.js +38 -0
  60. package/scripts/plan-status.sh +85 -10
  61. package/skills/persistent-planning/SKILL.md +5 -2
  62. package/templates/lg/phase.md +7 -4
  63. package/templates/lg/task.md +3 -2
@@ -1,12 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
  // Postinstall — delegates to @theglitchking/claude-plugin-runtime.
3
3
  // See https://github.com/TheGlitchKing/claude-plugin-runtime/blob/main/docs/PLUGIN_AUTHORING_SCAFFOLD.md
4
+ //
5
+ // One thing runs before the delegation: reclaimStaleSkillDirs(). The runtime's linker
6
+ // skips a destination that is a real directory rather than a symlink, and skips it
7
+ // forever — so v1 leftovers never heal. Renaming the real dir aside first leaves a
8
+ // clean destination the runtime then links normally.
4
9
 
5
10
  import { runPostinstall } from "@theglitchking/claude-plugin-runtime";
6
11
  import { dirname, resolve } from "node:path";
7
12
  import { fileURLToPath } from "node:url";
13
+ import { reclaimStaleSkillDirs, writeVersionMarkers, ENV_PREFIX } from "./lib/skill-link.js";
14
+ import { createRequire } from "node:module";
8
15
 
9
16
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
17
+ const consumerRoot = process.env.INIT_CWD || process.cwd();
18
+
19
+ try {
20
+ // Skip when the runtime itself would skip: dev-in-place, or linking disabled.
21
+ const devInPlace = resolve(consumerRoot) === resolve(packageRoot);
22
+ if (!devInPlace && process.env[`${ENV_PREFIX}_SKIP_LINK`] !== "1") {
23
+ const r = reclaimStaleSkillDirs(consumerRoot, packageRoot, {
24
+ skillsSubdir: "skills",
25
+ log: (m) => console.log(m),
26
+ });
27
+ if (r.reclaimed.length) {
28
+ console.log(
29
+ `[persistent-planning] reclaimed ${r.reclaimed.length} stale skill dir(s); ` +
30
+ `originals kept alongside as .bak-* — delete them once you are satisfied.`
31
+ );
32
+ }
33
+ }
34
+ } catch (err) {
35
+ // Reclaim is best-effort. A failure here must never block the install.
36
+ console.warn(`[persistent-planning] skill reclaim skipped: ${err?.message || err}`);
37
+ }
10
38
 
11
39
  try {
12
40
  runPostinstall({
@@ -21,3 +49,13 @@ try {
21
49
  } catch (err) {
22
50
  console.warn(`[persistent-planning] postinstall failed: ${err?.message || err}`);
23
51
  }
52
+
53
+ try {
54
+ // Stamp the artifact that actually executes, so drift is detectable at all.
55
+ // Only healthy symlinks are stamped — never a real dir, which would mark stale
56
+ // code as current.
57
+ const { version } = createRequire(import.meta.url)("../package.json");
58
+ writeVersionMarkers(consumerRoot, packageRoot, version, { skillsSubdir: "skills" });
59
+ } catch {
60
+ // Marker is a diagnostic aid, not a dependency. Absent is a valid state.
61
+ }
@@ -45,22 +45,91 @@ plan_root_artifact() {
45
45
  done
46
46
  }
47
47
 
48
+ # frontmatter_field <file> <key> -> value of `<key>:` inside the leading --- block
49
+ #
50
+ # Scoped to the frontmatter on purpose. Grepping a whole file for a frontmatter field
51
+ # matches it in prose, tables and fenced examples too — a notes.md documenting the
52
+ # contract then reads as an artifact that declares it (issue #14).
53
+ frontmatter_field() {
54
+ [[ -f "$1" ]] || return
55
+ awk -v key="$2" '
56
+ NR==1 && $0 != "---" { exit }
57
+ NR>1 && $0 == "---" { exit }
58
+ index($0, key ":") == 1 {
59
+ sub("^" key ":[[:space:]]*", "")
60
+ gsub(/["\047]/, "")
61
+ sub(/[[:space:]]+$/, "")
62
+ print; exit
63
+ }' "$1"
64
+ }
65
+
48
66
  # frontmatter_status <file> -> value of `status:` inside the leading --- block
49
67
  frontmatter_status() {
50
- [[ -f "$1" ]] || return
51
- awk 'NR==1 && $0 != "---" { exit }
52
- NR>1 && $0 == "---" { exit }
53
- /^status:[[:space:]]*/ { sub(/^status:[[:space:]]*/, ""); gsub(/["\047]/, ""); print; exit }' "$1"
68
+ frontmatter_field "$1" "status"
54
69
  }
55
70
 
56
71
  # Emits "<total> <checked>" for every markdown file under a plan directory.
72
+ #
73
+ # Checkboxes inside fenced code blocks are examples, not work. A plan whose notes.md
74
+ # quotes markdown — which is what notes.md is for — used to have those quoted boxes
75
+ # counted against it, and a quoted `- [ ]` inflates only the denominator, so the plan
76
+ # became permanently uncompletable with no visible reason (issue #14).
77
+ #
78
+ # An unterminated fence swallows the rest of the file. That is the safe direction:
79
+ # ambiguous content is not counted as outstanding work.
57
80
  count_boxes() {
58
- local total checked
59
- total=$(grep -rhoE '^[[:space:]]*- \[[ xX]\] ' --include='*.md' "$1" 2>/dev/null | wc -l | tr -d ' ')
60
- checked=$(grep -rhoE '^[[:space:]]*- \[[xX]\] ' --include='*.md' "$1" 2>/dev/null | wc -l | tr -d ' ')
81
+ local total=0 checked=0 f out
82
+ while IFS= read -r f; do
83
+ [[ -n "$f" ]] || continue
84
+ out=$(awk '
85
+ # Fence open/close: ``` or ~~~, optionally indented, optional info string.
86
+ # A fence closes only on the same character.
87
+ /^[[:space:]]*(```|~~~)/ {
88
+ line = $0
89
+ sub(/^[[:space:]]*/, "", line)
90
+ ch = substr(line, 1, 1)
91
+ if (!infence) { infence = 1; fencechar = ch }
92
+ else if (ch == fencechar) { infence = 0 }
93
+ next
94
+ }
95
+ infence { next }
96
+ /^[[:space:]]*- \[[ xX]\] / { t++ }
97
+ /^[[:space:]]*- \[[xX]\] / { c++ }
98
+ END { print t + 0, c + 0 }
99
+ ' "$f" 2>/dev/null) || continue
100
+ total=$((total + ${out%% *}))
101
+ checked=$((checked + ${out##* }))
102
+ done < <(find "$1" -type f -name '*.md' 2>/dev/null)
61
103
  echo "$total $checked"
62
104
  }
63
105
 
106
+ # Files under a plan whose FRONTMATTER declares <key>: <value>.
107
+ frontmatter_files_with() {
108
+ local dir="$1" key="$2" want="$3" f
109
+ while IFS= read -r f; do
110
+ [[ -n "$f" ]] || continue
111
+ [[ "$(frontmatter_field "$f" "$key")" == "$want" ]] && echo "$f"
112
+ done < <(find "$dir" -type f -name '*.md' 2>/dev/null)
113
+ }
114
+
115
+ # Counts mandatory tasks under a plan that are not yet done.
116
+ #
117
+ # The two closing tasks carry `mandatory: true`. A plan cannot be COMPLETE while one
118
+ # of them is unfinished, however the boxes happen to add up — that gate was stated in
119
+ # the templates and enforced nowhere (issue #12).
120
+ #
121
+ # Additive by design: a plan with no `mandatory: true` artifacts behaves exactly as it
122
+ # did before. Retroactively reopening finished plans would be worse than the bug.
123
+ unfinished_mandatory() {
124
+ local n=0 f st
125
+ while IFS= read -r f; do
126
+ [[ -n "$f" ]] || continue
127
+ st=$(frontmatter_status "$f")
128
+ [[ "$st" == "done" || "$st" == "archived" ]] || n=$((n + 1))
129
+ done < <(frontmatter_files_with "$1" "mandatory" "true")
130
+ echo "$n"
131
+ }
132
+
64
133
  COMPLETE=()
65
134
  ROWS=()
66
135
 
@@ -74,13 +143,19 @@ for plan_path in "$PLANNING"/*/; do
74
143
 
75
144
  read -r total checked <<<"$(count_boxes "${plan_path%/}")"
76
145
  status=$(frontmatter_status "$root")
77
- blocked=$(grep -rl '^status: blocked' --include='*.md' "${plan_path%/}" 2>/dev/null | wc -l | tr -d ' ')
146
+ blocked=$(frontmatter_files_with "${plan_path%/}" "status" "blocked" | wc -l | tr -d ' ')
147
+ pending_mandatory=$(unfinished_mandatory "${plan_path%/}")
78
148
 
79
149
  if [[ "$status" == "archived" ]]; then
80
150
  verdict="archived"
81
151
  elif [[ "$status" == "done" ]] || { [[ "$total" -gt 0 ]] && [[ "$checked" -eq "$total" ]]; }; then
82
- verdict="COMPLETE"
83
- COMPLETE+=("$slug")
152
+ if [[ "$pending_mandatory" -gt 0 ]]; then
153
+ # Every box ticked, but a mandatory closer is not done. Not complete.
154
+ verdict="in progress"
155
+ else
156
+ verdict="COMPLETE"
157
+ COMPLETE+=("$slug")
158
+ fi
84
159
  elif [[ "$blocked" -gt 0 ]]; then
85
160
  verdict="blocked"
86
161
  elif [[ "$total" -eq 0 ]]; then
@@ -161,7 +161,7 @@ Create this file FIRST for any complex task:
161
161
  - [ ] Phase 3: Execute/build
162
162
  - [ ] Phase 4: Review and deliver
163
163
  - [ ] Phase 5: Validate success through comprehensive testing (MANDATORY)
164
- - [ ] Phase 6: Documentation pass -- create/update/deprecate docs (MANDATORY)
164
+ - [ ] Phase 6: Documentation pass -- create/update/deprecate as many docs as needed to capture what was done, where it lives, how to troubleshoot it, etc. etc.. etc.. (MANDATORY)
165
165
 
166
166
  > Phases 5 and 6 are mandatory and MUST stay the last two phases of this plan.
167
167
  > Insert new work above them; never after. Renumber them if you add phases.
@@ -169,7 +169,10 @@ Create this file FIRST for any complex task:
169
169
  > - **Validate success**: prove the work with tests that fail if the change breaks.
170
170
  > - **Documentation pass**: create/update/deprecate every doc this change touches --
171
171
  > what it is, where it lives, how to fix it, how to operate it, why it matters.
172
- > If hit-em-with-the-docs is installed, use it for this pass.
172
+ > If hit-em-with-the-docs is installed, you MUST use it for this pass --
173
+ > `npx hewtd integrate <file> -a` to create, `npx hewtd archive <file>` to retire,
174
+ > `npx hewtd maintain --quick` to regenerate indexes. Never hand-edit INDEX.md or
175
+ > REGISTRY.md; they are generated.
173
176
 
174
177
  ## Key Questions
175
178
  1. [Question to answer]
@@ -20,9 +20,9 @@ Each task is a bounded deliverable. Tasks default to dependencies-first scheduli
20
20
  Tasks with no inter-dependencies can be marked `parallelizable: true` in their frontmatter
21
21
  so subagent teams can pick them up concurrently.]
22
22
 
23
- - [ ] (no tasks yet — run `/start-task "Task name" --parent PHASE_SLUG_PLACEHOLDER`)
24
- - [ ] **Validate success through comprehensive testing** (MANDATORY — second-to-last)
25
- - [ ] **Documentation pass — create/update/deprecate docs** (MANDATORY — last)
23
+ _(no tasks yet — run `/start-task "Task name" --parent PHASE_SLUG_PLACEHOLDER`)_
24
+ - [ ] **Validate success through comprehensive testing** (`validate-success-through-comprehensive-testing`) (MANDATORY — second-to-last)
25
+ - [ ] **Documentation pass — create/update/deprecate as many docs as needed to capture what was done, where it lives, how to troubleshoot it, etc. etc.. etc..** (`documentation-pass-create-update-deprecate-docs`) (MANDATORY — last)
26
26
 
27
27
  > The two mandatory closing tasks above MUST remain the last two tasks of this phase.
28
28
  > Add new tasks above them; never after. A phase cannot be marked `done` until both are
@@ -31,7 +31,10 @@ so subagent teams can pick them up concurrently.]
31
31
  > - **Validate success**: prove the phase's work with tests that fail if it breaks.
32
32
  > - **Documentation pass**: create/update/deprecate every doc this phase touches —
33
33
  > what it is, where it lives, how to fix it, how to operate it, why it matters.
34
- > If hit-em-with-the-docs is installed, use it for this pass.
34
+ > If hit-em-with-the-docs is installed, you MUST use it for this pass --
35
+ > `npx hewtd integrate <file> -a` to create, `npx hewtd archive <file>` to retire,
36
+ > `npx hewtd maintain --quick` to regenerate indexes. Never hand-edit INDEX.md or
37
+ > REGISTRY.md; they are generated.
35
38
 
36
39
  ## Decisions Made
37
40
  [Phase-level decisions that affect multiple tasks. Format: `**Decision**: rationale`.]
@@ -9,6 +9,7 @@ plan_kind: task
9
9
  parent: PHASE_SLUG_PLACEHOLDER
10
10
  depends_on: []
11
11
  parallelizable: false
12
+ mandatory: false
12
13
  ---
13
14
 
14
15
  # Task: TASK_TITLE_PLACEHOLDER
@@ -29,8 +30,8 @@ under `atoms/` that subagents read via the planning MCP.
29
30
  Inline checkbox vs. standalone atom file — see anti-pattern doc:
30
31
  .documentation/standards/atom-granularity.md.]
31
32
 
32
- - [ ] (no atoms yet — add inline checkboxes here for simple atoms,
33
- or run `/start-atom "Atom name" --parent TASK_SLUG_PLACEHOLDER` for complex ones)
33
+ _(no atoms yet — add inline checkboxes here for simple atoms,
34
+ or run `/start-atom "Atom name" --parent TASK_SLUG_PLACEHOLDER` for complex ones)_
34
35
 
35
36
  ## Decisions Made
36
37
  [Task-level decisions. Format: `**Decision**: rationale`.]