Releases: OthmanAdi/planning-with-files
Release list
v3.10.1: Codex Unix hook JSON compatibility
What changed:
- Codex shell-backed hooks on Linux and macOS now use the shared event adapter. This fixes the invalid JSON failures reported for
SessionStartandUserPromptSubmitin issue #220 and covers the same path forPreCompact. - The tracked npm package source and sync manifest now match the 20-script tarball already published for v3.10.0, so the release tag, source tree, and package provenance are aligned.
- Installation docs now distinguish direct npm vendoring from Pi's automatically wired route. The npm listing still includes the Pi extension.
- The README now uses committed terminal-style SVGs for the
/clearcomparison and labels its statistics table. - The version bumper now reports absent gitignored ClawHub staging without failing in a fresh clone, while still updating and validating that stage whenever it exists.
Thanks: @mfehlhaber for reporting issue #220.
v3.10.0: a second session can no longer erase your plan in silence
Two sessions sharing one plan directory could silently destroy each other's work
Closes #217, reported by @dubes394.
Both agents read task_plan.md, both write it back, and the later write discards the earlier one's phases. Nothing noticed: injection emitted the clobbered file as an ordinary edit, plan-doctor reported PASS, and the Stop gate read the reverted status as current.
Attestation was the nearest existing mechanism and did not cover it. It is opt-in in legacy mode, it compares against a baseline a human approved once rather than against what the hooks last observed, it reports a collaborator's edit with the same [PLAN TAMPERED] wording as a hostile rewrite, and it is a read-side gate that cannot stop the stale write from landing.
The guard compares progress, not hashes. A hash comparison would flag a single agent's own edit on its very next fire, which is most fires. Checked items and completed phases only go up during normal work, so a decrease between two turn-start fires means work that was on disk is gone. Forward motion stays silent. Both markers are language-neutral, because every translated template deliberately keeps the literal English **Status:** complete token that check-complete.sh matches with grep -F.
On by default in every mode. PWF_PLAN_GUARD=0, or a plan-guard-off token in .mode, turns it off.
Every non-English install was a subset install
The five language variants shipped 8 of the 20 scripts the canonical skill ships. Attestation, the Stop gate, the ledger, phase status and plan-doctor had never reached them, and neither had the v3.2.0 Windows UTF-8 fix, so a non-ASCII session log still crashed session recovery for exactly the users most likely to have one. sync-ide-folders.py covered only the three hook dispatch targets from #212, which made the drift structural.
Closed additively: 60 files created, 0 overwritten. The UTF-8 fix is backported by insertion so translated prose survives, and check-complete, init-session and session-catchup are now pinned as translator-owned, because the sync manifest's claim that variant scripts are language-neutral is false and a future full sync would have replaced German and Arabic output with English.
The top of the README was unreadable on a phone
The before/after comparison used width="50%" cells, which GitHub honors directly, so the columns locked to a 50/50 split of a roughly 340px container instead of scrolling as one unit, crushing the block that shows the actual injection payload. The stats panel was a 48-column box-drawing block with right-flushed values, so a first-time mobile visitor saw five labels and no numbers. Both restructured with all content preserved.
Also
The pinned tesslio/skill-review-and-optimize SHA moves to the current release commit (#215, by @popey). The old pin predated the vendor's migration, so skill reviews had stopped running correctly, and the range it moves across closes a marker-spoofing hole in the commit this repo was pinned to.
Suite 411 to 417.
v3.9.0: plan resolution binds to a root, refusals are never silent
A thread's plan is now bound to a root, not guessed from the cwd
A Codex thread whose cwd was a shared parent injected an unrelated project's plan on every hook fire (#212, reported by @webwww123). Resolution was cwd relative with no notion of a thread, so with /workspace holding one plan and /workspace/project holding the real one, the parent's pointer was the only pointer the hook could see. The wrong plan then arrived as high priority context on every prompt and every matched tool call, competing with the user's own corrections.
PWF_PLAN_ROOTbinds resolution to a project root by absolute path.PLAN_IDis a cwd relative slug and structurally could not name a nested project's plan at all. A pin that does not resolve fails closed with a notice.- An ambiguous cwd refuses to inject. When the plan came from the shared pointer or the newest by mtime fallback and a project one directory below has its own live plan, nothing is injected and the notice names both escape hatches. An explicit pin,
PLAN_ID, or an attached session stays authoritative. - Applied to the canonical dispatcher, the
.codex/hooks.jsonroute, the Cursor hooks, and the shared resolver in both shell and PowerShell.
Three bugs found while verifying that report
PLANNING_DISABLED=1did nothing on eleven of thirteen install routes. Those SKILL.md variants still carried the v2.43 hook body inlined in their YAML scalars, so they never received the #195 opt-out, the symlink containment guard, nonce delimiters, the v3 attestation refusal, the ledger summary, orPWF_INJECT=smart, and they still wrote the SHA cache to the world writable/tmppath that moved to$XDG_CACHE_HOMEin v3.0.0. All eleven now dispatch to the versioned script, and a test fails the build if one drifts back.- The Stop hook could never find its script on six hosts. Codex, Cursor, Factory, CodeBuddy, Mastra and OpenCode inherited a discovery list naming only Claude Code paths, so completion checking silently did nothing. Discovery is also a first match wins loop now:
ls a b c | head -1returns the alphabetically first hit, so a stale marketplace copy outranked the host native one. - Eight shipped PowerShell scripts could not be parsed by Windows PowerShell 5.1. A BOM-less
.ps1is read as ANSI, and the UTF-8 bytes for an em dash end in0x94, which CP1252 maps to a closing curly quote, opening a string literal that never closes. Every dispatcher swallows the parse error. Dead on Windows: the Cursor injection hook, both.kiroasset scripts, andinit-sessionfor both Chinese variants, so those users could not create a plan at all.
Pi extension, provider errors and the status bar
agent_end never read its event (#211, reported by @killianMei), so a turn that ended with a provider error was treated as complete and got the auto continue follow up, which started another request into the same failing provider up to the limit, burying the original error. It now returns on a trailing assistant stopReason of error or aborted before the counter is touched, so an outage costs no retry budget.
The phase count is published from all four active handlers in every mode, including the all-phases-complete branch where the N/M to M/M transition reached the notification but never the bar. Bundled extension 1.2.3.
Prompt caching, answered with measurements
Injection is byte identical across fires in every context and mode, now asserted rather than assumed (#210, raised by @GlitterKill). Five routes were emitting unnormalized wall clock times because each builds its injection independently and none had received the v2.40 pass. The skill text also no longer attributes a whole-workflow token measurement to per tool call recitation.
Refusals are never silent
The session guard, the ambiguity refusal, a broken pin, and a missing script each say so once per turn and name the way out. plan-doctor reports a refusal as its own state instead of counting the notice as plan bytes and printing PASS.
Verification
Suite 311 to 411 passing, 282 to 453 subtests, 48 Pi extension tests, IDE folder sync clean. Built by Fable agents, reviewed by Opus, with a Sonnet recon fleet and a four agent adversarial pass that returned a do-not-ship verdict on the first build; four blockers, two introduced by the fix itself, were closed before release.
Full detail in CHANGELOG.md.
v3.8.2: session recovery finds dotted, spaced and emoji project paths
Fixed
Session recovery silently found nothing for any project path containing a dot, a space, or any other non-alphanumeric character (closes #209, reported by @seathatflowsinourveins).
Claude Code names ~/.claude/projects/ entries by folding every character outside [A-Za-z0-9-] to -. Three copies of session-catchup.py still used a manual replace chain that handled only /, \ and :, so they computed a directory name Claude Code never writes, no candidate matched, and main() returned at the exists() check with exit 0. Catchup after /clear produced nothing and reported nothing. Hidden directories such as ~/.dotfiles were the common case.
One of the three sits 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.
An emoji in a folder name made a project unresolvable in every copy, including the canonical one. Claude Code walks the directory name as UTF-16, so a non-BMP character costs two dashes while the sanitizer counted codepoints and produced one. Folding now counts UTF-16 code units. The rules were measured against 24 real stores whose recorded cwd could be read: one model matches all 24, and it also showed that current versions fold _ while older stores kept it, so both spellings stay in the probe chain.
The 15 copies that already folded dots keep that behavior. .kiro is untouched because it ships a different program that never reads ~/.claude/projects.
Security
Two projects whose paths fold to the same ~/.claude/projects name could read each other's transcripts. The mapping is lossy, so client.acme and client-acme share one directory. Until this release the resolver did not fold those characters and simply missed the directory; folding correctly means it now finds it, so catchup filters transcripts by the cwd they record.
A transcript is skipped only when it positively records a different project. 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 instead of used. The filter works per session rather than rejecting the whole directory, because in a collision both projects live there permanently and rejecting it would cost the project its own history. Reproduced with a planted canary before and after the fix.
Verification
Suite 305 to 311 passing, 282 subtests, sync-ide-folders.py --verify clean. The new parity suite discovers the copies with git ls-files and runs one vector table through each, so the drift that produced this issue fails the suite instead of hiding in a single file.
Thanks
seathatflowsinourveins reported the dot-folding mismatch with an exact blob reference, a working reproduction, and a correct reading of the silent return path (#209).
Full changelog: v3.8.1...v3.8.2
v3.8.1: Pi plan resolution anchored to the project, hardened to sh-resolver parity
This release closes #208 and the one gap v3.8.0 dogfooding surfaced.
Fixed
Pi extension: plan resolution no longer depends on the live shell cwd (closes #208, reported by @fd44fdg with a precise root-cause analysis). The Pi session cwd follows the shell, so an agent that changed into a subdirectory lost the project's plan entirely: resolution found nothing, the recitation went dark, and the "No task_plan.md found" warning fired on every write and edit. Resolution now anchors on the nearest ancestor directory carrying planning state, bounded by the .git repository boundary and a depth cap so a plan outside the repository can never leak into a session. Explicit PLAN_ID pins keep working from any subdirectory.
Every Pi injection now states which plan it resolved (plan: or plan: root). A stale .planning directory shadows a root task_plan.md by documented precedence; the label makes that visible instead of silent.
init-session created plans without the v3.8.0 Next Step section: the scripts write plans from an inline heredoc, not from the template files, so the section never reached real plans. All 26 heredoc copies fixed, with a regression test that asserts the created output rather than the template.
Bundled Pi extension bumped to 1.2.2. The npm package @tomxprime/planning-with-files is published by its package author; the npm side follows the repository.
Hardening shipped with the fix, found by the two-model release gate (an Opus adversarial pass plus a five-lens Sonnet reliability fleet): the Pi resolver now matches the sh resolver's slug validation and containment (a traversal PLAN_ID or a junctioned slug directory no longer resolves anything outside the project, fail-closed), every runtime consumer that takes a directory routes through the same anchor as plan resolution, and the injected plan label is sanitized.
New vitest coverage for the anchor walk and the parity hardening (35 tests green). Thanks to fd44fdg for the report and analysis.
v3.8.0: the Stop hook fires on every platform, session recovery finds the real store, smart injection
This release closes two silent failures that hit exactly the long-running story the skill exists for, then adds the injection controls that keep a long plan in the model's window.
Fixed
The Stop hook never fired on macOS or Linux, and was a silent no-op on every platform when CLAUDE_SKILL_DIR was unset. Two dispatch bugs stacked: the install path fallback used a shell default that can never substitute, and the PowerShell branch was selected everywhere because check-complete.ps1 ships on all platforms, so the dispatch died with a suppressed exit 127 wherever PowerShell is absent. Both the completion advisory and the v3 completion gate were dead in those environments. The scalar now selects targets by file existence and dispatches by platform. Windows output is unchanged. New tests execute the scalar end to end on both CI legs instead of string-matching its shape.
Session recovery searched a ~/.claude/projects directory that does not exist for macOS and Linux installs, nor for any project path containing an underscore: Claude Code keeps underscores and the leading dash of POSIX absolute paths, the mapper stripped both. Recovery after /clear silently found nothing in those cases. The mapper now probes the exact spelling first, keeps the legacy spellings as fallbacks, and settles ambiguity via the cwd recorded in the newest session file.
Also fixed: a stale SHA cache hit could report a false PLAN TAMPERED for a different project (the cache key now includes the project root), resolve-plan-dir.ps1 reached parity with the sh resolver (slug validation, task_plan.md requirement, fail-closed containment), and ledger-append.sh no longer truncates a CJK or emoji summary mid-codepoint.
Added
Opt-in structure-aware injection: PWF_INJECT=smart (or an inject-smart token in .mode) replaces the position-blind head-50 with the plan title, Goal, Next Step, Current Phase, phase counts, the full in_progress phase section, and the last three decisions, so late in a long task the active phase is always in the window. Default output stays byte-identical to v2.43.
A Next Step section in both plan templates plus a sixth reboot question (what am I about to do), tool result outcomes in the session catchup report, a macos-latest CI leg plus a BSD userland simulation harness that runs the script fleet without realpath, readlink, flock, or sha256sum, pinned line endings via .gitattributes, three problem-query docs pages, a rewritten llms.txt, and a rebuilt README with committed SVG benchmark charts that carry the internal benchmark framing inside the images.
Suite at 301 passed (from 217), 11 skipped, green on Windows, Linux, and the new macOS leg pending first run. Full details in the CHANGELOG.
v3.7.0: Agent Skills standard layout ships in-tree
v3.7.0
The Agent Skills standard layout now ships in-tree
.agents/skills/planning-with-files/ is the cross-tool Agent Skills path that Zed, Amp, Warp, Devin, Antigravity, Gemini CLI, Cursor, and the wider agentskills.io adopter list read natively. This release ships it inside the repo, so a plain git clone is discoverable by all of those tools with no per-tool setup. The mirror carries the full canonical surface: SKILL.md, references, all six templates including the autonomous plan template, and the complete script set (hook dispatchers, ledger tooling, plan-doctor).
It is wired into both maintenance systems so it cannot silently rot: sync-ide-folders.py gained a .agents manifest, and the SKILL.md joined the bump-version.py parity set plus the version-parity test (18 locked entries).
Also in this release
plan-doctor.sh(new in v3.6.0) now ships in every synced IDE skill folder, not only the two canonicalscripts/locations.docs/gemini.mdroutes new installs through the standard.agents/skills/path, which is version-locked, instead of the intentionally version-lagged.gemini/skills/variant (which stays for existing installs).AGENTS.mdand thebump-version.pydocstring now describe the actual 18-entry parity set.
Full details in the CHANGELOG.
v3.6.0: silent hook loss on native-coreutils Windows fixed, /plan-doctor self-check
v3.6.0
The headline: plan resolution and hook injection could go silently dark on Windows
A machine with a native Windows coreutils build on PATH (winget coreutils, uutils, and similar installs) gives Git Bash sessions a realpath that prints C:\-style backslash paths. The containment guard in resolve-plan-dir.sh and inject-plan.sh compares canonical paths with a forward-slash prefix pattern, so every comparison failed. The consequence: the resolver resolved nothing, injection emitted nothing, every hook exited 0, and nothing printed an error. The mechanisms this skill is built on were fully disabled on such machines with no visible symptom, and Linux CI could not reproduce it.
v3.6.0 backslash-normalizes canonical paths before comparison (pure shell, no extra process), canonicalizes the resolver root via . and candidates through their cwd-relative form (healing the 8.3 short-path mismatch that v3.2.0 fixed only in inject-plan.sh), and adds regression tests that reproduce the Windows path shape on every platform via a stub realpath, so ubuntu CI now guards this class.
Side effects of the same work: the long-standing local test_ledger.py hang on Windows is gone, and per-candidate grep/basename forks were replaced with shell builtins. One hook fire measures 289ms on the machine that measured 2.0 to 2.4s at v3.4.0.
New: /plan-doctor
Failures in this class are silent by design, so a broken install looks identical to "no plan yet". sh scripts/plan-doctor.sh (or /plan-doctor on the plugin route) reports in one pass: plan resolution, injection output, canonicalizer path shape, attestation state, install surfaces on the machine, and per-fire hook latency.
Docs
docs/installation.md now carries the install-route matrix (the plugin route ships commands/ and reliable hook registration; npx skills add and manual copies do not), the project-trust prerequisite that silently gates project-level skills, and a one-line CLAUDE.md trigger snippet: the July 2026 benchmark measured unforced skill engagement at 60 to 67% while always-loaded rules lines engaged 100%. The README gains a plan-mode handoff FAQ and updates the supported-agents answer to the 2026 .agents/skills standard landscape.
Full details in the CHANGELOG.
v3.5.1: Codex Windows WSL bash fix, Python discovery, Pi interactive-tool fix
Fixed
Codex Windows hooks kept failing after v3.4.1 and v3.5.0: root cause found. The shell resolver preferred C:\Windows\System32\bash.exe, the WSL launcher, whenever Git Bash usr\bin was not on PATH (the default Git for Windows install layout). On machines with WSL present but no distro installed, a common Docker Desktop setup, every shell hook failed silently. The resolver now skips WSL launchers (System32 and the Store WindowsApps alias) and continues to the Git for Windows probe. Extracted from PR #207 by @mahdiit, commit authorship preserved.
Windows Python discovery hardened. pwf-hook.cmd now honors PYTHON_BIN, probes standard uv and CPython install locations when Codex starts hooks with a reduced PATH, and quotes the interpreter path so installs under paths with spaces work. Also from PR #207.
Pi extension 1.2.1: recitations no longer break interactive tools. Messages queued by the tool_call hook with deliverAs: "steer" were consumed as the answer of an open AskUserQuestion dialog. All three tool_call sends now deliver as nextTurn. Issue #206, diagnosed with the exact mechanism by @jschmied.
Added
CI test workflow (PR #199 by @Yigtwxx, closes #197): pytest on ubuntu and windows runners plus the Pi vitest suite on every PR and push to master. Two test portability fixes from the first hosted run (PR #198).
Thanks
Mahdi (@mahdiit) for tracking the surviving Windows failure to the WSL launcher and contributing the fix in PR #207. jschmied for the precise #206 diagnosis. Yigtwxx for the CI workflow and test portability fixes.
v3.5.0: Codex Windows hooks, Pi nag fix, plan lifecycle docs
Fixed
- Codex hooks on Windows emitted invalid JSON and failed on Unicode (PR #205 by @yolo0731, closes #204). The Codex front door forwarded plain stdout where Codex expects
hookSpecificOutput.additionalContextor PreCompact JSON, and UTF-8 plan text broke on both decode (Windows code page) and encode (ensure_ascii=Falsethrough cmd.exe). Each event is now serialized in its supported Codex JSON shape with ASCII-safe output, shell output is decoded as UTF-8, PreToolUse plan text uses model-visibleadditionalContext, scoped plans resolve in PermissionRequest, SessionStart gainsclear|compact, and the.active_planpointer is written without a BOM. The containment resolver now fails closed. Audited clean before merge. - Closed and complete Pi plans kept nagging "Task incomplete" (#203 by @ziyu4huang).
resolveNewestPlanDirnow ranks bytask_plan.mdfile mtime instead of directory mtime,readPlanStatusparses the close marker, andagent_endand the auto-continue loop stop on a closed plan. Pi extension 1.2.0. - Four language commands referenced a skill namespace that does not exist; corrected to
planning-with-files:planning-with-files-<lang>.
Added
- Traditional Chinese
/plan-zhtcommand. - README now documents the full v3 command, hook, and mode surface (Claude Code and Pi command tables, a v3 long-running-agent features section, a hooks and modes reference, and a command-names note).
- Plan lifecycle documented in
docs/workflow.md(#202 by @kcinzgg).
Thanks
@yolo0731, @ziyu4huang, @kcinzgg.
Full changelog: https://github.com/OthmanAdi/planning-with-files/blob/master/CHANGELOG.md