@theglitchking/persistent-planning 3.4.0 → 3.4.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.
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Official marketplace for persistent-planning - Persistent markdown-based planning for Claude Code. Context engineering pattern with task directories, progress tracking, and working memory that persists across sessions.",
|
|
9
|
-
"version": "3.4.
|
|
9
|
+
"version": "3.4.1"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "persistent-planning",
|
|
14
14
|
"description": "Persistent markdown-based planning for Claude Code. Uses on-disk markdown files as working memory for planning, progress tracking, and knowledge storage. Plans persist across sessions and support multiple concurrent tasks.",
|
|
15
|
-
"version": "3.4.
|
|
15
|
+
"version": "3.4.1",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "TheGlitchKing"
|
|
18
18
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "persistent-planning",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"description": "Persistent markdown-based planning with task directories, progress tracking, and context engineering for multi-step Claude Code workflows",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TheGlitchKing"
|
|
@@ -68,6 +68,13 @@ its own.
|
|
|
68
68
|
failure is contained.
|
|
69
69
|
- **Fails open.** Every path is wrapped. A broken migration must never cost someone
|
|
70
70
|
their session start.
|
|
71
|
+
- **Never let a dependency be fatal.** Load the runtime with `await import()` in a
|
|
72
|
+
try/catch. A static import makes an unresolvable dependency fatal for the whole hook,
|
|
73
|
+
including the repair — the delivery path must survive what it is delivering a fix for
|
|
74
|
+
(#18). Only the update check genuinely needs the runtime.
|
|
75
|
+
- **Stamp on success, never on failure.** Marking a version done after a failed attempt
|
|
76
|
+
turns a transient problem into a permanent one: the repair never runs again, even once
|
|
77
|
+
the cause is gone. Retrying costs one spawn per session while genuinely drifted.
|
|
71
78
|
- **One stdout write.** The runtime owns the single `SessionStart` JSON response and
|
|
72
79
|
exposes no append hook; a second response line is invalid. Merge into the existing
|
|
73
80
|
intercept.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [3.4.1] - 2026-09-04
|
|
6
|
+
|
|
7
|
+
### Fixed — the SessionStart hook could not survive a missing runtime (#18)
|
|
8
|
+
|
|
9
|
+
`hooks/session-start.js` imported `@theglitchking/claude-plugin-runtime` statically. That
|
|
10
|
+
package resolves out of the shared `~/.claude/plugins/npm-cache/`, which a marketplace
|
|
11
|
+
plugin cache does not carry itself, so when it could not resolve the **entire hook died** —
|
|
12
|
+
`ERR_MODULE_NOT_FOUND` before any of its work ran.
|
|
13
|
+
|
|
14
|
+
That took down the drift warning and `autoRepair` along with the update check: the two
|
|
15
|
+
things #10 built precisely so a fix could reach an install that is already broken. The
|
|
16
|
+
delivery path had a single point of failure the payload was meant to survive.
|
|
17
|
+
|
|
18
|
+
- **The runtime is loaded lazily now**, in a try/catch returning `null`. A resolution
|
|
19
|
+
failure costs only the update check; the completion nudge, drift warning and repair all
|
|
20
|
+
still run, and the hook emits its own valid `SessionStart` payload since nothing else
|
|
21
|
+
will.
|
|
22
|
+
- **A failed repair no longer stamps the version.** `autoRepair` previously stamped
|
|
23
|
+
`repairedSkillsForVersion` regardless of outcome, to avoid retrying every session — which
|
|
24
|
+
turned a *transient* failure into a permanent one. Reproduced: one session without a
|
|
25
|
+
resolvable runtime marked 3.4.0 done, and the repair was then skipped forever even once
|
|
26
|
+
the runtime returned. It stamps on success only; a failure costs one spawn per session
|
|
27
|
+
while genuinely drifted, and the drift warning fires alongside it naming the manual fix.
|
|
28
|
+
|
|
29
|
+
Both defects came from the #10 work and shipped in 3.4.0. Test suite grows to 125
|
|
30
|
+
assertions, including a fixture with no `node_modules` at all.
|
|
31
|
+
|
|
5
32
|
## [3.4.0] - 2026-09-04
|
|
6
33
|
|
|
7
34
|
### Changed — only completed plans are gitignored (#15)
|
package/hooks/session-start.js
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Three jobs:
|
|
5
5
|
// 1. The update check, delegated entirely to @theglitchking/claude-plugin-runtime.
|
|
6
|
+
// That import is dynamic on purpose: the runtime resolves out of the shared
|
|
7
|
+
// ~/.claude/plugins/npm-cache/, which is not guaranteed to be populated. A
|
|
8
|
+
// static import made an unresolvable runtime fatal for the WHOLE hook, taking
|
|
9
|
+
// jobs 2-4 down with it — including the repair that is the only way a fix
|
|
10
|
+
// reaches an already-broken install. Jobs 2-4 need no runtime at all.
|
|
6
11
|
// 2. A completion nudge: if any plan under .planning/ has every box checked
|
|
7
12
|
// but is still sitting in the active tree, say so, so the agent archives it
|
|
8
13
|
// instead of rediscovering a finished plan next session.
|
|
@@ -14,13 +19,23 @@
|
|
|
14
19
|
// node_modules in the plugin cache and never runs npm postinstall -- so it is
|
|
15
20
|
// the only delivery path a fix can reach existing broken installs through.
|
|
16
21
|
|
|
17
|
-
import { runSessionStart } from "@theglitchking/claude-plugin-runtime";
|
|
18
22
|
import { spawnSync } from "node:child_process";
|
|
19
23
|
import { dirname, join, resolve } from "node:path";
|
|
20
24
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
21
25
|
import { fileURLToPath } from "node:url";
|
|
22
26
|
import { driftedSkills, readSkillVersion } from "../scripts/lib/skill-link.js";
|
|
23
27
|
|
|
28
|
+
// Resolve the runtime lazily. Returns null when it cannot be loaded, which costs
|
|
29
|
+
// only the update check — never the drift warning or the repair.
|
|
30
|
+
async function loadRunSessionStart() {
|
|
31
|
+
try {
|
|
32
|
+
const mod = await import("@theglitchking/claude-plugin-runtime");
|
|
33
|
+
return typeof mod?.runSessionStart === "function" ? mod.runSessionStart : null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
25
40
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
26
41
|
|
|
@@ -96,14 +111,18 @@ function autoRepair() {
|
|
|
96
111
|
timeout: 10000,
|
|
97
112
|
});
|
|
98
113
|
|
|
99
|
-
// Stamp
|
|
100
|
-
//
|
|
114
|
+
// Stamp only on success. Stamping a failure turns a transient problem — an
|
|
115
|
+
// unresolvable runtime, a locked file — into a permanent one: the version is
|
|
116
|
+
// marked done and the repair never runs again, even once the cause is gone.
|
|
117
|
+
// A failure costs one spawn per session while genuinely drifted, and the drift
|
|
118
|
+
// warning fires alongside it naming the manual fix, so nobody is stuck silently.
|
|
119
|
+
const remaining = driftedSkills(projectRoot, packageRoot, "skills");
|
|
120
|
+
if (remaining.length || r.status !== 0) return ""; // let driftNudge() report it
|
|
121
|
+
|
|
101
122
|
try {
|
|
102
123
|
writeFileSync(CONFIG, JSON.stringify({ ...cfg, repairedSkillsForVersion: version }, null, 2) + "\n", "utf8");
|
|
103
|
-
} catch { /* config unwritable —
|
|
124
|
+
} catch { /* config unwritable — worst case the repair is attempted again */ }
|
|
104
125
|
|
|
105
|
-
const remaining = driftedSkills(projectRoot, packageRoot, "skills");
|
|
106
|
-
if (remaining.length || r.status !== 0) return ""; // let driftNudge() report it
|
|
107
126
|
return "[persistent-planning] repaired a stale skill directory (originals kept as .bak-*).";
|
|
108
127
|
} catch {
|
|
109
128
|
return "";
|
|
@@ -147,12 +166,27 @@ function driftNudge() {
|
|
|
147
166
|
const repaired = autoRepair();
|
|
148
167
|
const notice = [planNudge(), repaired, driftNudge()].filter(Boolean).join("\n\n");
|
|
149
168
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
const runSessionStart = await loadRunSessionStart();
|
|
170
|
+
const RUNTIME_OPTS = {
|
|
171
|
+
packageName: "@theglitchking/persistent-planning",
|
|
172
|
+
pluginName: "persistent-planning",
|
|
173
|
+
configFile: "persistent-planning.json",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
function emitNoticeOnly() {
|
|
177
|
+
// No runtime, so nobody else will emit the response. Say our piece and stop.
|
|
178
|
+
if (!notice) return;
|
|
179
|
+
process.stdout.write(
|
|
180
|
+
JSON.stringify({
|
|
181
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: notice },
|
|
182
|
+
}) + "\n"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!runSessionStart) {
|
|
187
|
+
emitNoticeOnly();
|
|
188
|
+
} else if (!notice) {
|
|
189
|
+
await runSessionStart(RUNTIME_OPTS);
|
|
156
190
|
} else {
|
|
157
191
|
// ponytail: the runtime owns the one SessionStart JSON response and exposes no
|
|
158
192
|
// hook for appending to it, and a second response line on stdout is invalid.
|
|
@@ -179,11 +213,7 @@ if (!notice) {
|
|
|
179
213
|
return realWrite(chunk, ...rest);
|
|
180
214
|
};
|
|
181
215
|
|
|
182
|
-
await runSessionStart(
|
|
183
|
-
packageName: "@theglitchking/persistent-planning",
|
|
184
|
-
pluginName: "persistent-planning",
|
|
185
|
-
configFile: "persistent-planning.json",
|
|
186
|
-
});
|
|
216
|
+
await runSessionStart(RUNTIME_OPTS);
|
|
187
217
|
|
|
188
218
|
// The runtime can return without emitting (e.g. it chdir-failed). Say our piece.
|
|
189
219
|
if (!merged) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theglitchking/persistent-planning",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"description": "Persistent markdown-based planning with task directories, progress tracking, and context engineering for multi-step Claude Code workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|