Eleven of the thirteen hook bearing SKILL.md files still carried the v2.43
hook body inlined in their YAML scalars. The v3 refactor moved that logic into
scripts/inject-plan.sh so it would be versioned and testable instead of
duplicated, but only the canonical and .agents files were converted. The
reporter of #212 installed one of the eleven.
Those inline bodies never received: the PLANNING_DISABLED opt-out from #195,
which is why the documented workaround was inoperative on that route; the
symlink containment guard; the SHA cache key fix; nonce delimiters; the v3
attestation refusal; the ledger summary; and PWF_INJECT=smart. They also still
wrote the SHA cache to the world writable /tmp path that moved to
XDG_CACHE_HOME for security in v3.0.0.
The Stop scalar in those eleven probed only CLAUDE_SKILL_DIR and two
$HOME/.claude paths, none of which exists on Codex, Cursor, Factory,
CodeBuddy, Mastra or OpenCode, so completion checking there could never find
its script and silently did nothing.
Discovery is now a first match wins loop. The previous ls a b c | head -1
returns the alphabetically first hit, so a stale marketplace copy outranked
the host native one regardless of the order the candidates were written in.
Candidates are PWF_SCRIPT_DIR, then CLAUDE_SKILL_DIR, then the host's own
skill directory, then the two Claude paths. Repo relative candidates stay
banned: a cloned repository must not be able to plant a script the hook then
executes. PWF_SCRIPT_DIR exists for workspace installs, which are the
recommended method on several hosts and which no user level path can reach.
When nothing resolves, the once per turn hook says so rather than exiting
silently; the per tool call and precompact hooks stay quiet.
The scalars are generated from one template so the eleven cannot drift apart
again, the sync manifest ships inject-plan.sh, ledger-summary.sh and
resolve-plan-dir.sh to every variant that dispatches (without the resolver,
ledger-summary reported a false 0/0 complete), and a new test fails the build
if any hook bearing SKILL.md drifts back to an inlined body.
Plan resolution was cwd relative with no notion of a thread: PLAN_ID, then
.planning/.active_plan, then the newest plan directory by mtime, every read
taken relative to the process cwd. A Codex thread sitting at a shared parent
such as /workspace therefore saw only the parent's pointer, and the plan of an
unrelated project was injected on every prompt and every matched tool call
while the nested project's own .active_plan was never consulted (#212).
PWF_PLAN_ROOT binds resolution to a project root by absolute path, which a cwd
relative PLAN_ID slug structurally cannot express. A pin that does not resolve
fails closed with a notice rather than falling back to the ambiguous plan the
caller was escaping. Containment is checked against the pin.
When the plan was chosen by a cwd guess (the shared pointer, newest by mtime,
or a legacy root task_plan.md) and a project one directory below carries its
own live plan, nothing is injected and the notice names both escape hatches.
An explicit PLAN_ID, a pin, or an attached session stays authoritative and
skips the check. Detection is one glob at depth one, which is the whole per
fire budget. A nested pointer that is empty or names a deleted directory does
not compete, so an abandoned subproject cannot kill injection at the root.
The session attachment guard from #146 now runs on this route too. It is not
silent: on hosts that never set PWF_SESSION_ID every session is unattached by
construction, so a stale .planning/sessions/ directory would otherwise kill
injection permanently with no symptom, and .planning/ is gitignored so the
state is invisible to review. The notice is turn scoped so it cannot become
per tool call spam.
Also fixed here, both found by the new tests:
- ledger-summary.sh now takes the resolved plan directory as an argument.
Under a pin it previously re-resolved from the process cwd, so an autonomous
loop could read the parent's "1/1 complete" while its own pinned plan had an
open phase. With no plan directory determinable it says so instead of
reporting a confident 0/0.
- GNU sha256sum prefixes its output line with a backslash when the filename
needs escaping, which any Windows style pin triggers, so every attested
pinned plan reported PLAN TAMPERED on every fire.
plan-doctor reports a refusal as its own state with the remedy. It previously
counted the refusal notice as bytes of plan context and printed PASS, so the
one tool a dark user is pointed at gave a green light.
The legacy invariant holds: with no pin, no sessions directory and no nested
competing plan, output is byte identical to the previous release across every
context and mode, verified by diffing captured stdout against HEAD.
Claude Code's project-dir naming is lossy: /home/dev/client.acme and
/home/dev/client-acme both fold to -home-dev-client-acme and share one
directory of transcripts. Until this release the resolver did not fold the
same characters, so it simply missed such a directory. Now that it folds the
way Claude Code does, it finds the shared directory, and catchup has to
decide which transcripts in it belong to the project that asked.
Without that decision a catchup in one project prints the other project's
conversation into the fresh context. Reproduced against the previous commit
with a planted canary: the victim project's run emitted the donor's message
verbatim. The same run after this change emits a one line notice instead.
A transcript is skipped only when it positively records a different cwd.
Transcripts that record none are kept, because the field is not present in
every generation of the format. A directory whose transcripts all belong to
another project is reported rather than used, and mixed directories keep the
project's own transcripts, which is why the filter is per session rather
than a rejection of the whole directory: in a collision both projects live
there permanently, so rejecting the directory would cost the project its own
history for good.
Comparison goes through the same canonicalization the Codex path already
uses, case-insensitively on Windows.
tests/test_catchup_cross_project_guard.py covers the four cases at unit
level across every copy that carries the guard, plus two end to end runs of
the real script against a shared store: one asserting the canary never
appears, one asserting the project's own transcript still does.
Claude Code names ~/.claude/projects entries by folding every character
outside [A-Za-z0-9-] to '-', but three copies of session-catchup.py still
used a manual replace chain that handled only '/', '\' and ':'. Any project
path containing a dot, a space, or any other such character produced a name
Claude Code never writes, no candidate matched, and main() returned at the
exists() check with exit 0, so recovery after /clear was a silent no-op for
hidden directories such as ~/.dotfiles.
One of those copies is on a live install route. marketplace.json declares
"source": "./", so the plugin root is the repository root and the SKILL.md
restore block resolves ${CLAUDE_PLUGIN_ROOT}/scripts/session-catchup.py for
every plugin user on Linux, macOS or Git Bash. Measured against a real store
holding 89 sessions, the shipped resolver produced 0 bytes where the fixed
one produces 11336 and recovers 166 messages.
scripts/, .hermes and .mastracode now share the same normalize, sanitize and
candidate helpers as the canonical copy, keeping their existing function
names, signatures and the .mastracode Codex guard. Folding counts UTF-16
code units because Claude Code walks the name as UTF-16, so a non-BMP
character costs two dashes; that width was missing everywhere, which left
three real emoji projects unresolvable even in the canonical copy. Both
underscore spellings stay in the probe chain since stores using each are
live on disk, and the exact spelling is still probed first.
The folding rules were measured against 24 real stores whose recorded cwd
could be read; one model matches all 24. New
tests/test_catchup_store_resolution_parity.py discovers the copies with
git ls-files and runs a single vector table through each, so this drift
fails the suite instead of hiding in one file. .kiro is excluded because it
ships a different program that never reads ~/.claude/projects.
The scripts write plans from an inline heredoc, not from
templates/task_plan.md, so the section shipped in the v3.8.0 templates
never reached a created plan. All heredoc copies (sh and PowerShell,
canonical plus every mirror and the dual-shipped root twins) now emit
'## Next Step' directly after the Goal, inside every hook injection
window, and the new regression test asserts the created OUTPUT rather
than the template (test_init_session_output_contains_next_step).
Language-variant copies keep their localized heredocs and received only
line-ending normalization; init-session line endings are normalized to
LF across the fleet.
The Stop scalar never dispatched on macOS or Linux (the PowerShell branch
was always selected because check-complete.ps1 ships on every platform)
and was a silent no-op anywhere CLAUDE_SKILL_DIR was unset (the :- install
path fallback can never substitute because the probed variable is always a
non-empty string). Both the legacy completion advisory and the v3
completion gate were dead in those environments. The scalar now selects
targets by file existence and dispatches by platform, PowerShell only
under MINGW/MSYS/CYGWIN, sh elsewhere. Windows output is unchanged.
Patched in all 14 SKILL.md variants that carry the scalar.
session-catchup probed a ~/.claude/projects name that Claude Code never
writes for POSIX paths (leading dash stripped) or paths containing an
underscore (replaced with a dash), so recovery after /clear silently
found nothing there. The mapper now probes the exact spelling first,
keeps both legacy spellings for stores created by older versions, and
settles ambiguity via the cwd recorded in the newest session file.
Propagated to every shipped copy including the older-generation root,
.hermes, and .mastracode scripts.
Also: the attestation SHA cache is keyed on the absolute plan path so two
projects can no longer share a slot and report a false PLAN TAMPERED;
resolve-plan-dir.ps1 reaches parity with the sh resolver (slug filter,
task_plan.md requirement in the newest-dir scan, fail-closed containment);
ledger-append.sh no longer truncates summaries mid-codepoint (UTF-8-safe
trim via iconv with a pure-sh fallback).
New long-run features, all opt-in or additive: structure-aware injection
(PWF_INJECT=smart or an inject-smart .mode token) keeps the active phase,
phase counts, and the last three decisions in the window late in long
plans; a Next Step section in both plan templates plus a sixth reboot
question; session-catchup annotates tool results (ok or FAILED with the
first error line) instead of only listing attempts.
Infrastructure: macos-latest joins the CI matrix, a BSD-userland
simulation harness runs the script fleet without realpath, readlink,
flock, or sha256sum on the Linux leg, and .gitattributes pins LF for
scripts. SEO surfaces: llms.txt rewritten as a Q&A page, three
problem-query docs pages, plugin.json and CITATION.cff keyword hygiene.
Suite grows from 217 to 301 passed, 11 skipped, on Windows and both
existing CI legs; default hook output stays byte-identical to v2.43
(legacy invariant proven by the existing invariant tests).
Ships .agents/skills/planning-with-files/ so tools reading the
cross-tool Agent Skills path natively (Zed, Amp, Warp, Devin,
Antigravity, Gemini CLI, Cursor) discover the current skill from a
plain git clone. Full canonical surface: SKILL.md, references, six
templates, complete script set including hook dispatchers, ledger
tooling, and plan-doctor.
Wired into both maintenance systems so it cannot silently rot:
sync-ide-folders.py gains a .agents manifest (and now ships
plan-doctor.sh to every synced IDE folder), the SKILL.md joins the
bump-version.py parity set and the version-parity test (18 locked
entries). AGENTS.md and the bump-version.py docstring describe the
actual parity set. docs/gemini.md routes new installs through the
standard path instead of the intentionally lagged .gemini variant.
18-file parity bump to 3.7.0, CHANGELOG entry, README release row and
version heading.
Every failure in the resolution and injection class is silent by design
(hooks always exit 0), so a broken install looks identical to "no plan
yet". plan-doctor.sh answers in one pass: does resolution work here and
which plan wins, does injection emit plan context, what path shape does
the canonicalizer produce, is the plan attested, which install surfaces
exist, and what one hook fire costs in wall-clock.
Dual-shipped in both scripts/ locations and added to the parity test.
From the July 2026 benchmark improvement backlog (P0 trigger and
install-health items).
A native Windows coreutils build on PATH gives sh a realpath that prints
C:\-style backslash paths. The containment prefix match in
resolve-plan-dir.sh and inject-plan.sh is written with forward slashes,
so every canonical pair mismatched: the resolver resolved nothing and
inject-plan.sh emitted nothing, exit 0, no error. Plan resolution and
hook injection were fully and silently disabled on such machines.
Canonical paths are now backslash-normalized (pure shell, no extra
process) before comparison. The resolver also canonicalizes its root via
"." and checks candidates through their cwd-relative form, healing the
8.3 short-path mismatch the v3.2.0 fix only applied to inject-plan.sh.
Per-candidate grep and basename forks are replaced with case patterns
and parameter expansion; the containment root is computed once per run.
Regression tests use a PATH-prepended stub realpath that reproduces the
backslash output shape on every platform, so Linux CI guards this
Windows-only field failure. Also cures the long-standing local
test_ledger.py hang on Windows.
templates/analytics_task_plan.md and templates/analytics_findings.md existed
at the repo root (added v2.29.0 for #103) but were never copied into
skills/planning-with-files/templates/, the directory init-session.sh actually
reads inside the installed skill package. The --template analytics flag has
been silently falling back to the generic default templates for every plugin
or skill-only install since the feature shipped; only the loose repo-root
scripts and the .hermes mirror had working copies.
Copied both files into the canonical templates/ dir and added them to
sync-ide-folders.py's TEMPLATES list so future syncs carry them to every IDE
mirror automatically, then ran the sync once to backfill the seven mirrors
that share the canonical templates dir. Verified init-session.sh --template
analytics now produces the real analytics-phase task_plan.md instead of the
generic default.
This does not add the standalone analytics subagent #103 originally asked
for; the template-and-flag mode is what shipped in v2.29.0 and this fix makes
it actually work as documented.
Every Stop-hook style adapter (Codex, Cursor, GitHub Copilot's AgentStop, and
the canonical Claude Code check-complete.sh/.ps1 used by the gate and
advisory paths) checked only that task_plan.md exists, never that it contains
any "### Phase" headings. A task_plan.md that exists but isn't a structured
plan gives TOTAL=0, which fell through to the same branch as an in-progress
plan, producing "Task in progress (0/0 phases complete)" or, worse, Cursor and
GitHub Copilot's variants auto-continuing with "0/0 phases done ... continue
working on the remaining phases."
Reported against Codex specifically in #191 by @mixian939, with a correct
diagnosis and suggested fix. Auditing the same pattern found it in the
canonical scripts and two other IDE adapters too, so the guard lands
everywhere at once: a TOTAL=0 check that exits silently (or emits empty JSON
for Copilot's schema) before any status message is built.
Propagated to every check-complete.sh/.ps1 copy that shares this logic
(canonical, all sync-ide-folders.py-managed IDE mirrors via a sync run, the
five language-variant skills, and the standalone .kiro copy) so the fix is not
Claude-Code-only. inject-plan.sh, gate-stop.sh and the ledger scripts already
build on check-complete.sh's TOTAL and needed no separate change.
is_within_root() canonicalized the root via the $PWD string but canonicalized
candidates via a relative path (".", ".planning/<id>"), which resolves through
getcwd() instead. On this machine's TEMP (an 8.3 short-name path,
C:\Users\OASRVA~1\...) and under the MSYS /tmp mount alias, those two
canonicalization routes land on differently-spelled paths for the same
physical directory, so the prefix-match containment check fails and the hook
exits 0 with zero output: no plan re-injection, no tamper warning, no error.
A user would have no way to know it stopped working.
Root cause was the asymmetry, not either canonicalization call individually.
Fixed by canonicalizing the root the same way candidates already are, via the
relative "." token instead of the $PWD variable, so both sides resolve through
the same physical-cwd code path. resolve-plan-dir.sh already worked correctly
for the same reason (it builds absolute candidates), which is what pointed at
the fix.
get_project_dir_claude only replaced forward slashes, so a Windows path
(C:\Users\... or Git Bash's /c/Users/...) never sanitized to Claude's real
project directory name, and the function silently returned early every time.
Three open() calls also had no explicit encoding, so a session log with any
non-ASCII content raised UnicodeDecodeError on Windows' default cp1252 codec,
an error the bare except clauses swallowed. Together these meant the "resume
after /clear" feature did nothing on Windows, with no visible error.
Fixed by branching on whether the path looks Windows-shaped (contains ':' or
'\') before sanitizing, converting Git Bash drive-letter paths first, and
adding encoding='utf-8', errors='replace' to the three reads. Genuine Unix
absolute paths take the untouched legacy branch, so macOS/Linux behavior is
unchanged.
tests/test_path_fix.py previously reimplemented the sanitizer instead of
importing the real module ("avoids import issues" per its own comment), so
the suite stayed green while the shipped script stayed broken. It now loads
scripts/session-catchup.py via importlib and exercises the actual function,
plus a regression case for the untouched Unix branch.
The marketplace fallback resolves dispatchers from repo-root scripts/. The
review wave restored inject-plan.sh and gate-stop.sh there but not
ledger-append, ledger-summary, phase-status (sh + ps1), so autonomous and
gated mode on that route silently fell back to injecting the raw
progress.md tail instead of the structured ledger summary. Copies are
byte-identical to the skill scripts; a new location-parity test pins the
full dual-shipped set in both places.
Gate JSON neutralizes control chars 0x01-0x1F; stop_hook_active
detection anchored to its value; mixed-format plans gate via per-field
max counting; .mode token parsing fixed (autonomous gate no longer
degrades to legacy); v3 modes refuse unattested plan injection; root
scripts/ gains inject-plan.sh and gate-stop.sh so the plugin-route
fallback works on Linux; PowerShell writes BOM-less ASCII/UTF-8
sidecars so attestation hashes survive PS 5.1; nonce fallback mixes
PID; gate-stop guards unset HOME; MIGRATION notes the cache move.
resolve-plan-dir canonicalizes the resolved plan path (realpath,
readlink -f, python fallback; fail-open on minimal shells) and rejects
paths escaping the project root, closing the symlink injection vector.
attest-plan no longer leaves a stale attestation when the atomic
rename fails: fallback write is temp plus rename, then read-back
verification exits nonzero on mismatch. attest-plan --show surfaces
the plan nonce when present.
New opt-in flags write <plan-dir>/.mode (gated implies autonomous),
reset .stop_blocks, remove stale .gate_last_ledger, generate a 16-hex
nonce into .nonce for delimiter hardening, and auto-attest the fresh
plan via the sibling attest-plan script. Works in slug and legacy root
mode. Without a flag, output is byte-identical to v2.43.0.
Blocks Stop only when all hold: .mode contains gate, an in_progress
phase exists, stop_hook_active is false, block count under cap
(PWF_GATE_CAP, default 20, sidecar .stop_blocks), and the run ledger
advanced since the last block (stall detection via .gate_last_ledger).
Emits fixed-template decision:block JSON with the phase heading only,
no plan body. Without --gate the script is byte-identical to v2.43.0.
Bug fixes (items 1-5 in proposal_v2_40.md):
1. Hook resolution order inverted across UserPromptSubmit, PreToolUse, PreCompact.
Slug-mode now wins over legacy root task_plan.md when both exist. Closes the
bug where an active .planning/<slug>/ plan was silently bypassed in favor of
the root file. Resolution chain: PLAN_ID env > .planning/.active_plan >
newest .planning/<slug>/ by mtime > root fallback.
2. .active_plan target dir is validated. Dead pointers fall through cleanly.
3. .active_plan content is regex-validated as ^[A-Za-z0-9_][A-Za-z0-9._-]*$.
Whitespace-only and path-traversal corruption fall through instead of
producing weird path lookups. Tests cover whitespace, ../escape, and
leading-dot cases.
4. check-complete.sh wires into resolve-plan-dir.sh when no path arg given.
Restores slug-mode parity for any caller that invokes the script without an
explicit plan path.
5. Pi extension dangerous-command detection uses word-boundary regex. Benign
git push origin <branch> no longer triggers the warning; only --force,
--mirror, +ref, plus rm -rf, sudo, chmod 777/a+rwx, git reset --hard, git
clean -fd, fork bombs, dd to /dev/[sh]d[a-z].
Performance (items 6, 7):
6. mtime-keyed SHA-256 cache under ${TMPDIR:-/tmp}/pwf-sha cuts redundant hash
work on warm cache. Caches the result keyed by absolute plan-file path,
stores mtime + hash, skips sha256sum when mtime unchanged.
7. KV-cache hygiene on injected progress.md tail. Sub-second timestamps and
timezone-suffix forms are normalized to a stable epoch-zero form so the
model's prefix cache stays warm across turns. Manus-aligned.
Portability + races (items 19, 20):
19. resolve-plan-dir.sh portable mtime fallback chain: GNU stat -> BSD stat ->
date -r -> python3 -> python -> perl -> 0. Multi-plan workflows stop
silently degrading on non-GNU systems.
20. attest-plan.sh uses atomic temp+rename with optional flock guard.
Concurrent legacy-mode attestations stop producing truncated files that
triggered false [PLAN TAMPERED] warnings.
Tests: 130 pass / 2 pre-existing Windows exec-bit failures unchanged. +20 new
tests vs v2.39.0. See CHANGELOG for full inventory.
Version bumped to 2.40.0 across 14 SKILL.md variants + plugin.json +
marketplace.json + CITATION.cff via scripts/bump-version.py. .continue,
.gemini, .pi, .kiro lag intentionally.
PreCompact hook fires on /compact and autoCompact. When task_plan.md is
present, surfaces a reminder to flush in-context progress to progress.md
before compaction completes and prints the active Plan-SHA256 if attested.
Stays silent without a plan. Exit 0 always; never blocks compaction.
/plan-goal composes with Claude Code's /goal (v2.1.139, May 12 2026).
Derives a termination condition from the active plan so /goal stops the
agent when the plan-file is genuinely complete, not just when the
conversation looks done.
/plan-loop composes with /loop (v2.1.72+). Default 10-minute tick re-reads
planning files, runs check-complete, nudges a progress.md entry if the
last tick produced no change. templates/loop.md ships a planning-aware
default for ~/.claude/loop.md so bare /loop works grounded in the plan.
OpenCode session-catchup rewritten for the SQLite migration (sst/opencode
dev @ 2026-05-14). Reads ${XDG_DATA_HOME:-~/.local/share}/opencode/opencode.db
read-only via URI, scopes by session.directory, surfaces unsynced planning
edits. Verified against a real 162 MB OpenCode database (94 sessions, 56
unsynced parts correctly extracted from the lovable-at-home directory).
Codex gains a PermissionRequest adapter that surfaces a one-line plan
reminder when an active plan exists. Read-only; never blocks.
Tests: +19 covering PreCompact behavior, slash command file structure,
OpenCode SQLite catchup with a synthetic schema-matching DB, and Codex
PermissionRequest emit/silent behavior. 98 tests passing.
Deliberately not changed: paths glob (auto-invocation behavior risk),
wholesale !command swap (would freeze attestation hash and defeat v2.37
tamper gate), native Plan Mode panel integration (no plugin API
documented), language variant consolidation (separate breaking-change
track). PreCompact lives in the canonical SKILL.md + 5 language variants
+ clawhub-upload; other IDE mirrors keep their existing compaction
hooks until per-IDE PreCompact adapters land in a future release.
Closes#150 (hash attestation): adds an opt-in second layer of defense for
plan injection. After v2.36.1 wrapped task_plan.md content in BEGIN/END
delimiters, the model still parsed the bytes. v2.37.0 adds /plan-attest, a
slash command that locks the current task_plan.md content with a SHA-256
stored at .planning/<active-plan>/.attestation (parallel-plan mode) or
./.plan-attestation (legacy mode). UserPromptSubmit and PreToolUse hooks
recompute the hash on every fire and emit "[PLAN TAMPERED — injection
blocked]" instead of plan content on mismatch. When attestation is set, the
injected context also carries a Plan-SHA256: line for audit. New helper:
scripts/attest-plan.sh and .ps1 with --show / --clear modes, propagated to
all six IDE mirrors that ship scripts.
Closes#151 (parity-locked version bumper): names the regression class
behind v2.34.1, v2.36.0, v2.36.2, v2.36.3 (one of N parity-locked files
forgotten on bump). Fixes it surgically without overshooting into
"regenerate all SKILL.md from canonical" (per-IDE frontmatter and per-
language prose are intentionally divergent). New
scripts/bump-version.py atomically bumps the parity set in one shot. New
tests/test_skill_md_version_parity.py fails the build the moment any
parity-locked file diverges from the canonical version. .continue,
.gemini, .pi, .kiro stay on their separate schemes and are surfaced in
every bump-version run so the omission is explicit.
Other changes:
- tests/test_canonical_script_sync.py: removed leftover duplicate
CanonicalScriptSyncTests class block (lines 99 to 137); SHARED_SCRIPTS
extended to ten entries for attest-plan.{sh,ps1}.
- scripts/sync-ide-folders.py: SCRIPTS manifest extended to cover
attest-plan helpers; existing eight scripts unchanged.
- canonical SKILL.md Security Boundary section: documents the two layers
of defense and the /plan-attest workflow.
- 14 SKILL.md variants + plugin.json + marketplace.json + CITATION.cff
bumped to 2.37.0 via scripts/bump-version.py (.continue / .gemini /
.pi / .kiro left on their own schemes per release scope rule).
Tests: 78 pass, 2 pre-existing Windows exec-bit failures unchanged.
resolve-plan-dir.{sh,ps1} and set-active-plan.{sh,ps1} were added to
scripts/ in v2.36.0 but never reached skills/planning-with-files/scripts/
or the IDE mirror folders. Users installing via npx skills could not use
the parallel planning workflow because the required scripts were absent.
Adds all four scripts to the canonical skill location. Updates
sync-ide-folders.py SCRIPTS manifest to include them, and propagates to
all IDE mirrors via the sync tool (24 new files across 6 IDE folders).
Strengthens test_canonical_script_sync.py to cover all 8 user-facing
scripts (was 4), so this class of gap trips the regression test going
forward.
Documents resolve-plan-dir.sh and set-active-plan.sh in the canonical
SKILL.md Scripts section with usage descriptions and a parallel task
workflow example.
Closes#146, #147, #148.
Parallel plan isolation (#148, @shawnli1874): init-session.sh slug mode
creates .planning/YYYY-MM-DD-<slug>/ for each task. set-active-plan.sh/.ps1
switch the active plan pointer. resolve-plan-dir.sh/.ps1 provide the
resolution chain ($PLAN_ID env, .active_plan file, newest plan dir, legacy
fallback). All four Codex hooks route through the resolver. resolve_latest_dir
now requires task_plan.md to be present, preventing system dirs like sessions/
from being selected as plan dirs. short_uuid() probes each Python candidate
before trusting command -v, fixing silent failure on Windows Store aliases.
Codex session isolation (#146, @githubYiheng): sessions attach to a plan by
creating .planning/sessions/<session_id>.attached. user-prompt-submit.sh,
pre_tool_use.py, stop.py, and post_tool_use.py all gate on session attachment
before injecting context or blocking. Legacy mode preserved when
.planning/sessions/ does not exist.
Hermes integration notes (#147, @09ashishkapoor): docs/hermes.md gains an
Integration Notes section covering current support level, what is not full
parity with hook-native platforms, recommended integration pattern, and a
tradeoffs table.
34 new tests across five files. Version bumped to 2.36.0 across 14 SKILL.md
variants, plugin.json, marketplace.json, and CITATION.cff.
Use /usr/bin/env bash so scripts resolve bash through PATH instead of
assuming /bin/bash exists. This improves shebang compatibility on
systems such as NixOS.
Add domain-specific planning templates for data analytics and
exploration workflows. The existing 5-phase template assumes a
software development cycle - analytics sessions need different
phases (Data Discovery, Exploratory Analysis, Hypothesis Testing,
Synthesis) and different tracking structures (Data Sources,
Hypothesis Log, Query Results, Statistical Findings).
New files:
- templates/analytics_task_plan.md: 4-phase analytics plan
- templates/analytics_findings.md: data source and hypothesis tracking
Modified files:
- scripts/init-session.sh: add --template/-t flag
- scripts/init-session.ps1: add -Template parameter
Usage: ./init-session.sh --template analytics
Default behavior is unchanged when flag is omitted.
Relates to #103
Adds full Kiro Agent Skill support with proper .kiro/skills/ layout,
bootstrap scripts, steering integration, and planning templates.
Replaces the old .kiro/scripts/ and .kiro/steering/ approach.
Also updates docs/kiro.md and sync-ide-folders.py skip list.
Contributed by Yi Chenxi (@EListenX)
v2.24.0 — Full IDE support audit and cleanup:
New features:
- Gemini CLI: add hooks (settings.json + 5 scripts: SessionStart, BeforeTool,
AfterTool, BeforeModel, SessionEnd). BeforeModel is unique to Gemini — injects
plan awareness before every model call.
- FactoryAI Droid: add hooks to SKILL.md frontmatter (was skills-only, now has
all 4 lifecycle events) + add scripts.
- GitHub Copilot: add errorOccurred hook for automatic error logging to plan.
Bug fixes:
- Continue: fix .prompt file — was entirely in Chinese, now English
- Pi Agent: fix package.json repo URL (was ttttmr/, now OthmanAdi/)
- Mastra Code: sync drifted check-complete.sh and session-catchup.py to canonical
- CodeBuddy/Codex: remove duplicate assets/templates/ directories
Consolidation:
- Remove .adal/, .agent/ (Antigravity), .kilocode/, .openclaw/ directories.
These IDEs implement the standard Agent Skills spec with no hooks — install
via `npx skills add` which handles IDE-specific placement automatically.
- README: restructure IDE table into Enhanced (hooks) vs Standard (Agent Skills)
- sync-ide-folders.py: remove entries for consolidated IDEs
Net: -4,609 lines (removed duplication, added real value)
Fixes#106. Three root causes identified and fixed:
1. Session catchup not working after /clear: Added UserPromptSubmit hook
to all 7 IDE SKILL.md files. When task_plan.md exists, injects a
directive to read all planning files before proceeding. Fires on every
user message, ensuring context recovery after /clear or compaction.
2. Progress not updating consistently: PostToolUse hook message now leads
with "Update progress.md" instead of only mentioning task_plan.md.
Added plan existence guard to avoid noise when no plan is active.
3. Post-plan additions not tracked: check-complete scripts now output
re-entry guidance when all phases are complete. Copilot agent-stop
scripts now return continuation context instead of empty JSON.
Added Critical Rule #7 (Continue After Completion) to SKILL.md body.
Applied across: 7 IDE SKILL.md files, 16 synced check-complete scripts,
4 Copilot hook scripts, CHANGELOG, plugin.json, marketplace.json.
PowerShell's parser fails on three categories of special characters inside
double-quoted strings in check-complete.ps1:
1. UTF-8 em-dash (U+2014) in comments — misinterpreted as code tokens
2. Square brackets [planning-with-files] — parsed as array index syntax
3. Parentheses ($COMPLETE/$TOTAL) and phase(s) — parsed as subexpressions
Fix: replace double-quoted Write-Host strings with single-quoted strings
and string concatenation, and replace em-dashes with ASCII double-hyphens.
Applied to all 12 tool variant copies (adal, codebuddy, codex, continue,
gemini, kiro, mastracode, openclaw, opencode, pi, scripts, skills).
Verified on Windows 11 with PowerShell 5.1 — all scenarios pass.
Closes#87
OpenCode uses different session storage (.json at ~/.local/share/opencode/) than Claude Code (.jsonl at ~/.claude/projects/). Session catchup now detects the IDE and provides a clear workaround message instead of silently failing.
- Added IDE detection (checks env vars and directory structure)
- OpenCode users get helpful message to manually read planning files
- Claude Code functionality unchanged
- Updated OpenCode docs with known limitations section
Fixes#74
- check-complete.sh/.ps1: Always exit 0 — incomplete task is a normal
state, not an error. Uses stdout for informational status reporting.
Fixes stop hook error in Claude Code. (#68)
- README.md, docs/quickstart.md: Clarify that the AI creates planning
files (not the user manually). Align all docs on workflow. (#69)
- README.md: Add Plan Cascade by @Taoidle to Community Forks (#63)
- Updated all 21 check-complete script copies across IDE folders
- check-complete.sh/.ps1: Add fallback for [complete] inline bracket
status format in addition to existing **Status:** bold format
- Updated all 21 script copies across IDE folders
- docs/kilocode.md: Remove all references to non-existent
.kilocode/rules/ directory (migrated to skills/ in v2.10.0)
- README.md: Update Kilo Code section to reference .kilocode/skills/
The Stop hook was failing with "No such file or directory" error when
CLAUDE_PLUGIN_ROOT was not properly set in the environment, resulting in
the script path resolving to "/scripts/check-complete.sh" instead of the
full path.
Changes:
- Add SCRIPT_DIR variable with fallback to explicit path
- Use $HOME/.claude/plugins/planning-with-files/scripts as fallback when
CLAUDE_PLUGIN_ROOT is not set
- Make check-complete.sh executable (chmod +x)
- Apply fix to both Unix and Windows paths in the hook command
This ensures the Stop hook runs successfully even when CLAUDE_PLUGIN_ROOT
is not available in the shell environment.
Fixes the error: "bash: /scripts/check-complete.sh: No such file or directory"
The session-catchup.py script now properly scans across ALL sessions to find
unsynced context since the last planning file update.
Changes:
- Skip the current session (most recently modified)
- Find the most recent planning file update across all previous sessions
- Collect ALL conversation from that point forward, across all sessions
- Show session markers [Session: xxx...] when context spans multiple sessions
- Include intermediate sessions that have no planning updates but have work
- Show up to 100 messages (was 20), with note if truncated
Example: If planning was updated in session A, then work continued in sessions
B and C without updates, the catchup now shows context from A, B, and C.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Merging feature/session-catchup-recovery from @lasmarois (PR #33)
This fast-follow release adds automatic session recovery for seamless
work continuation after running /clear when context fills up.
New Features:
- Session recovery via session-catchup.py script
- Analyzes previous session JSONL files in ~/.claude/projects/
- Finds last planning file update
- Extracts unsynced conversation after that point
- Automatic recovery when invoking /planning-with-files
Enhanced Hooks:
- PreToolUse now includes Read/Glob/Grep operations
- Keeps task_plan.md in attention during research phases
- Better context management throughout workflow
Files Added:
- scripts/session-catchup.py (all 3 locations)
- Session recovery instructions in SKILL.md
- README session recovery documentation
Version Updates:
- Version bumped to 2.2.1 in all manifests
- Updated CHANGELOG.md with v2.2.1 release notes
- Added @lasmarois to CONTRIBUTORS.md
Conflicts resolved:
- CHANGELOG: Combined v2.2.1 and v2.2.0 entries
- README: Added session recovery to v2.2.1, kept v2.2.0 features
- SKILL.md: Merged session recovery with existing hooks
Contributors:
- @lasmarois (session recovery implementation)
- Ahmad Othman Ammar Adi (conflict resolution, version management)
Automatically detect and recover unsynced work from previous sessions
when context fills up and user runs /clear.
New features:
- session-catchup.py: Analyzes previous session JSONL to find unsynced context
- Recovery triggered automatically when invoking /planning-with-files
- PreToolUse hook now includes Read/Glob/Grep for better attention management
Optimal workflow:
1. Disable auto-compact in Claude Code settings
2. Start fresh session
3. Run /planning-with-files when ready for complex task
4. Work until context fills up
5. Run /clear
6. Run /planning-with-files again — automatically recovers where you left off
How recovery works:
- SKILL.md instructs Claude to first run session-catchup.py
- Script checks ~/.claude/projects/ for previous session JSONL
- Finds last planning file update, extracts conversation after that point
- Claude updates planning files based on catchup + git diff
Changes:
- SKILL.md: Added session recovery section, preserved all original content
- README: Added optimal workflow and setup instructions
- Removed SessionStart hook (doesn't work per Issue #24)
- Version bumped to 2.2.0
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This release adds comprehensive Windows compatibility and recognizes
community contributors.
Features:
- PowerShell scripts (check-complete.ps1, init-session.ps1)
- OS-aware hook execution with automatic fallback
- Cross-platform script support (Windows, Mac, Linux)
Fixes:
- Issue #32: Stop hook error on Windows PowerShell
- Issue #25: check-complete.sh not found errors
- ${CLAUDE_PLUGIN_ROOT} resolution across platforms
Documentation:
- Added CONTRIBUTORS.md recognizing community contributions
- Added COMPREHENSIVE_ISSUE_ANALYSIS.md with detailed issue research
- Updated CHANGELOG.md with v2.2.0 changes
- Updated README.md with new version info
Technical Changes:
- Created PowerShell equivalents for all bash scripts
- Updated SKILL.md Stop hook to detect OS and use appropriate script
- Scripts deployed to all three locations (root, plugin, skills)
- Graceful fallback chain: pwsh → powershell → bash
- Version bumped to 2.2.0 in all manifests
This update enables Windows users to use planning-with-files without
compatibility issues while maintaining full backwards compatibility
for Unix/Linux/Mac users.
Fixes#18 - Templates not found in cache after update
Root cause: ${CLAUDE_PLUGIN_ROOT} resolves to repo root, but templates
were only in subfolders (planning-with-files/ and skills/planning-with-files/).
Solution: Added templates/ and scripts/ at repo root level so they're
accessible regardless of how CLAUDE_PLUGIN_ROOT resolves.
Changes:
- Add templates/ directory at repo root
- Add scripts/ directory at repo root
- Bump version to 2.1.2 in all manifests
- Update CHANGELOG with fix details
- Update troubleshooting.md with cache clearing instructions
- Update README file structure diagram
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>