@hank-warren/pi-plan-mode 0.1.0 β†’ 1.0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # @hank-warren/pi-plan-mode
2
+
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - cf12677: ship CHANGELOG.md in the published tarball
8
+
9
+ ## 1.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - 88572fe: Rewrite Plan mode around a durable plan file and stop managing tool permissions.
14
+
15
+ Plan mode is now a mode of intent rather than a permission system. It blocks exactly `edit`, `write`, and `update_plan` while planning and leaves every other tool as configured, so Bash and extension-tool safety stays with your permission layer (for example `@hank-warren/pi-auto-permissions`).
16
+
17
+ **Plan mode no longer calls `setActiveTools`.** It previously rewrote the session's active tool set on every turn, which stripped tools other extensions had registered β€” lazily connected MCP tools and on-demand channels such as pi-subagents' supervisor tool were removed mid-session and never reactivated, and extensions that manage their own tools on `before_agent_start` fought Plan mode for control. None of that can happen now.
18
+
19
+ Completed plans are written to `<agent dir>/plans/<session-id>.md`. The plan file is the plan: session state stores only its path, the file can be hand-edited, and both implementation paths read it from disk. While a plan is active the model receives a one-line pointer to the file instead of the plan body, so plans survive compaction at negligible context cost regardless of size, replacing the previous reinjection of up to 50,000 characters.
20
+
21
+ Breaking changes:
22
+
23
+ - Removed settings `defaultPlanTools`, `bashPolicy`, `safeSubcommands`, and `implementationPlanRetention`. They are now ignored rather than rejected, so existing settings files keep loading. `thinkingLevel` and `defaultPlanExportPath` are unchanged.
24
+ - Removed the pre-start tool selector and `/plan tools`.
25
+ - Removed the separate saved-plan state and `/plan save`; there is one plan per session, and `/plan exit` deletes it.
26
+ - Removed the legacy `<proposed_plan>` XML completion path; use `plan_mode_complete`.
27
+ - The implementation handoff and fresh-session transfer now reference the plan file instead of inlining the plan text.
package/NOTICE.md CHANGED
@@ -5,3 +5,7 @@ This package is a fork of [`@narumitw/pi-plan-mode`](https://github.com/narumiru
5
5
  The original work is Copyright (c) 2026 narumiruna and is used under the MIT License included in [`LICENSE`](LICENSE).
6
6
 
7
7
  Fork-specific changes are Copyright (c) 2026 Hank Warren and are released under the same MIT License.
8
+
9
+ ## Divergence from upstream
10
+
11
+ As of 1.0 this package has diverged substantially from upstream and no longer tracks it. Plan mode was rewritten around a durable plan file and no longer manages tool permissions: the tool selector, `defaultPlanTools`, the Bash inspection allowlist, `safeSubcommands`, the Auto Permissions Bash policy, the saved-plan state, and the plan-retention/context-reinjection machinery were all removed. Upstream changes are no longer merged.
package/README.md CHANGED
@@ -1,34 +1,25 @@
1
- # 🧭 pi-plan-mode β€” Codex-like Plan Mode for Pi
1
+ # 🧭 pi-plan-mode β€” Plan mode for Pi
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@hank-warren/pi-plan-mode)](https://www.npmjs.com/package/@hank-warren/pi-plan-mode) [![Pi extension](https://img.shields.io/badge/Pi-extension-blue)](https://pi.dev) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
4
4
 
5
- `@hank-warren/pi-plan-mode` adds a Codex-like `/plan` collaboration mode to Pi. Plan mode is for read-only exploration, clarifying questions, and a structured implementation-ready plan before any code mutation happens.
5
+ `@hank-warren/pi-plan-mode` adds a `/plan` mode to Pi for research and design. You gather information, ask questions, and land on a plan β€” then implement it, either in the same conversation or in a fresh one.
6
6
 
7
- This is a fork of `@narumitw/pi-plan-mode` 0.49.3. It preserves the upstream Plan workflow and adds an opt-in, fail-closed integration with `@ogulcancelik/pi-auto-permissions` so guarded planning-time information gathering can run without a second Plan-mode veto. See [NOTICE.md](NOTICE.md) for attribution.
7
+ **Plan mode is a mode of intent, not a permission system.** It blocks `edit`, `write`, and `update_plan` while planning and leaves every other tool exactly as you configured it. Command safety stays with your permission extension (for example [`@hank-warren/pi-auto-permissions`](../pi-auto-permissions)), which already reviews Bash. Plan mode never touches the session's active tool set, so it cannot break other extensions.
8
8
 
9
- Pi core intentionally does not ship a built-in plan mode; this package provides one as an independently installable extension.
9
+ The plan is written to a **durable file** that survives compaction, survives resume, and can be hand-edited.
10
10
 
11
11
  ## ✨ Features
12
12
 
13
- - Adds a state-aware `/plan` launch and management menu, plus `/plan start` for direct activation.
14
- - Adds `--plan` to start a session in Plan mode.
15
- - Enables built-in read-only tools by default while Plan mode is active.
16
- - Disables extension and custom tools by default, with persistent pre-start Settings and a staged `/plan tools` compatibility shortcut for explicit user-risk opt-in.
17
- - Blocks `update_plan`, mutating built-in tools, andβ€”by defaultβ€”unsafe `bash` forms such as writes, substitutions, background jobs, dependency installs, and mutating Git commands.
18
- - Optionally delegates only Bash commands covered by active Auto Permissions guarded rules, while keeping `edit`, `write`, and `update_plan` blocked and preserving the model-level non-mutation instructions.
19
- - Injects Codex-like Plan mode instructions: explore first, ask decision questions for high-impact ambiguity, do not mutate files, and finalize only when decision-complete.
20
- - Adds required `plan_mode_question` and `plan_mode_complete` tools for structured questions and completion.
21
- - Presents the complete plan and lets you implement with the planning conversation or start a fresh linked session carrying only the approved plan, as well as export, save, stay, or discard.
22
- - Exports ready, saved, or active implementation plans with `/plan export [path]` without overwriting existing paths; the omitted-path destination is configurable, and exporting a ready plan completes and exits Plan mode.
23
- - Lets each accepted plan remain active, serve only as the first implementation handoff, or clear after the first implementation run; the current retained-plan behavior remains the default.
24
- - Keeps legacy `<proposed_plan>` responses compatible without advertising XML as the primary workflow.
25
- - Shows Plan mode state in Pi's statusline as `plan active`, `plan ready`, `plan saved`, or `plan implementing`; `@narumitw/pi-statusline` adds the default `πŸ“` icon unless configured otherwise (this repo's `@hank-warren/pi-statusline` does not render extension statuses).
26
- - Persists Plan mode, one session-local saved plan, and active implementation state so resume and compaction retain the exact accepted plan.
13
+ - `/plan` mode with a planning system prompt: explore first, ask decision questions, do not implement.
14
+ - `plan_mode_question` for structured 1-3 question decision points with options and a free-form path.
15
+ - `plan_mode_complete({ plan })` writes the plan to `<agent dir>/plans/<session-id>.md`.
16
+ - **Pointer, not payload.** An active plan adds one line to the system prompt naming the file. The plan body is never injected into context, so a 50-page plan costs the same as a one-liner and survives compaction for free.
17
+ - Two ways to implement: continue in this conversation, or open a fresh session that reads the same file.
18
+ - `/plan export [path]` copies the plan anywhere, never overwriting an existing target.
19
+ - Hand-edit the plan file at any time; every command and both implementation paths read from disk.
27
20
 
28
21
  ## πŸ“¦ Install
29
22
 
30
- This release requires Pi 0.80.6 or newer.
31
-
32
23
  ```bash
33
24
  pi install npm:@hank-warren/pi-plan-mode
34
25
  ```
@@ -39,244 +30,80 @@ Try without installing permanently:
39
30
  pi -e npm:@hank-warren/pi-plan-mode
40
31
  ```
41
32
 
42
- Try this package locally from the repository root:
43
-
44
- ```bash
45
- pi -e ./packages/pi-plan-mode
46
- ```
47
-
48
33
  ## πŸš€ Usage
49
34
 
50
35
  ```text
51
- /plan
52
- /plan start
53
- /plan <prompt>
54
- /plan tools
55
- /plan show
56
- /plan finalize
57
- /plan implement
58
- /plan save
59
- /plan export [path]
60
- /plan exit
36
+ /plan open the menu for the current state
37
+ /plan start enter Plan mode without sending a prompt
38
+ /plan <prompt> enter Plan mode and start planning <prompt>
39
+ /plan show display the stored plan
40
+ /plan finalize ask the agent to complete the plan now
41
+ /plan implement implement the completed plan here
42
+ /plan export [path] copy the plan to a Markdown file
43
+ /plan exit leave Plan mode and delete the plan file
61
44
  ```
62
45
 
63
- In TUI and RPC, use bare `/plan` to open the menu for the current Plan state. When Plan mode is off
64
- and no plan is stored, the launch menu shows the effective next-start tools and offers **Start Plan
65
- mode**, **Choose tools, then start…**, **Settings**, and **How Plan mode works**. Settings edits the
66
- persistent defaults for later workflows. Launch-menu tool changes remain a draft until **Done β€” start
67
- Plan mode** is selected; Back, Escape, Ctrl+C, disposal, session replacement, and shutdown discard
68
- the draft without changing Plan state, active tools, thinking, or the stored selection.
69
-
70
- Use `/plan start` when you want to enter Plan mode directly without sending a model message. Use
71
- `/plan <prompt>` to enter Plan mode and immediately submit `<prompt>` as the first Plan-mode user
72
- message. The exact argument `start` is reserved for direct activation; longer text such as `/plan
73
- start a migration` remains an inline planning prompt. `--plan` also remains a direct activation path.
74
-
75
- Use **Choose tools, then start…** or the `/plan tools` compatibility shortcut to choose a
76
- session-specific override before Planning starts. Both routes use the same draft selector: **Done β€”
77
- start Plan mode** stores the selection and starts the workflow, while cancellation leaves Plan mode
78
- off and changes nothing. The bounded multi-select shows 10 rows at a time, supports viewport paging,
79
- descriptions, and explicit unavailable rows for blocked tools. In TUI mode, type to fuzzy-search tool
80
- names, descriptions, policy, and source metadata; RPC keeps the complete unfiltered list. Once Plan
81
- mode is active, tools are locked: `/plan` no longer offers tool or Settings actions, and `/plan tools`
82
- rejects the request. Exit and start a new workflow if a different tool set is required. The
83
- `plan_mode_question` tool keeps its one-off question/choice dialog because it is a model-requested
84
- planning interaction, not command-menu navigation. `/plan show` displays the stored
85
- plan without starting a model turn, including the accepted plan while implementation is active.
86
- `/plan finalize` explicitly asks the agent to complete the plan or ask one remaining material
87
- question, `/plan save` stores a completed ready plan for later and leaves Plan mode, and `/plan
88
- export [path]` writes a ready, saved, or active implementation plan to Markdown. Completed and saved
89
- plan menus offer **Implement here**, which continues with the planning conversation, and **Start
90
- fresh and implement**, which opens a new session and transfers only the approved plan. The direct
91
- `/plan implement` compatibility route remains equivalent to **Implement here** and never opens a
92
- selector. A successful ready-plan export also leaves Plan mode; saved and active implementation
93
- exports retain their existing state. `show`, `save`, `export`, and `implement` fail closed when no
94
- applicable plan is stored; `finalize` requires active Plan mode.
95
-
96
- `/plan export` uses the configured **Export destination**, which defaults to `PLAN.md`. Supply a path
97
- to override that default for one export. Relative paths resolve from the command's current `ctx.cwd`
98
- at export time, absolute paths remain absolute, a leading `@` is accepted for Pi path compatibility,
99
- and missing parent directories are created. Explicit `/plan export <path>` input always wins over the
100
- setting. Export never overwrites an existing file, directory, or symbolic
101
- link: choose another path or remove the existing target first. A successful export adds one trailing
102
- newline but otherwise preserves the accepted Markdown exactly. After a ready plan is written, Plan
103
- mode ends, its tools and thinking level are restored, and the ready state is cleared without starting
104
- a model turn. Exporting a saved or active implementation plan leaves that state unchanged. Failed or
105
- cancelled exports leave every Plan-mode state unchanged. The resulting file is available to the agent
106
- through its normal file-reading tools. Export is an explicit user-requested file mutation;
107
- model-initiated Plan-mode writes remain blocked.
108
-
109
- In TUI and RPC, **Export plan…** opens a single-line path input from every ready, saved, or active
110
- plan menu. The input shows both the configured value and its currently resolved path. Submit an empty
111
- value to use the configured destination, or enter a relative or absolute one-off path. A failed TUI
112
- export retains the draft for correction; RPC reopens its input dialog. Escape returns to
113
- the owning menu without writing a file. A successful ready-plan export closes the menu and ends Plan
114
- mode; saved and active implementation menus close without changing their stored state.
115
-
116
- When Plan mode is active, ask the agent to design the change. The agent may inspect files and run read-only commands, but it should not edit files or execute the implementation. It should explore first, then use structured questions when your preference or a tradeoff materially changes the plan. Configure persistent defaults or a one-workflow tool override before activation; Planning and ready menus deliberately keep those controls locked.
117
-
118
- By default, Plan mode manages only Pi's built-in tools: `read`, limited `bash`, available read-only built-ins such as `grep`, `find`, and `ls`, plus the required `plan_mode_question` and `plan_mode_complete` tools. Built-in `edit` and `write` are blocked. `update_plan` is also blocked because it tracks execution progress rather than conversational planning. Extension and custom tools are disabled by default because Pi tools do not expose standardized mutability metadata; enable them before starting from Settings or the staged workflow selector only when you accept the risk. For example, you can opt into `firecrawl_scrape`, `firecrawl_search`, or `lsp_diagnostics` if those extensions are loaded and you want to use them during planning.
119
-
120
- With the default `limited` Bash policy, Plan mode uses a fail-closed inspection allowlist, including when an extension overrides the canonical `bash` tool name. It accepts common inspection commands, read-only Git and npm queries, pipelines and command lists composed entirely of accepted commands, plus selected checks such as `npm test`, `npm run typecheck`, and `cargo test`. It rejects output/input redirects, shell expansion, substitutions, subshells, background jobs, mutating flags, dependency changes, editors, and unknown commands. A rejected parsed command list or pipeline identifies its first blocked command segment; malformed or unsupported shell syntax reports the complete submitted input instead. Tests and builds may still write ignored caches or build artifacts and may execute project-defined hooks; enable or invoke them only when the repository is trusted. This is extension-level risk reduction, not an OS sandbox.
121
-
122
- Set `bashPolicy` to `auto-permissions` to integrate with `@ogulcancelik/pi-auto-permissions` 0.1.x. Plan mode first applies its normal inspection allowlist. For a command that allowlist would block, it stands down only when the Auto Permissions extension is loaded and its current configuration contains a matching, non-trusted `guarded` rule. Missing, disabled, malformed, unmatched, convention-only, or trusted-group-bypassed configurations keep the Plan-mode block. Auto Permissions then performs its normal guardian review or user approval flow. Plan mode still blocks built-in `edit`, `write`, and `update_plan`, and its model instructions still prohibit implementation and mutation.
123
-
124
- `plan_mode_question` follows Codex's `request_user_input` pattern: the agent can ask 1-3 concise questions, each with meaningful options and a free-form Other path. If you cancel or no interactive UI is available, the agent should ask a concise plain-text question or proceed only with a clearly stated low-risk assumption instead of prematurely producing a final plan.
125
-
126
- Pi activates tools by tool name. The pre-start selector stores accepted session selections by name
127
- and shows each effective tool's source from Pi metadata, such as `built-in`, a user extension path,
128
- or a project extension path. If an extension overrides a built-in tool with the same name, Pi exposes the effective tool for that name and the selector shows that source.
129
-
130
- A complete Plan mode answer should appear only after the agent has resolved discoverable facts and high-impact user decisions. The agent must call `plan_mode_complete({ plan })` alone as its final action, passing the complete Markdown plan. The tool rejects empty or whitespace-only plans and plans longer than 50,000 JavaScript characters; it does not truncate. Its visible result contains the full plan, and versioned result details let the extension restore it safely from the active session branch.
131
-
132
- `plan_mode_complete` uses Pi's `terminate: true` hint. Termination is best effort: if a model puts it in a parallel tool batch, Pi terminates the batch early only when every finalized sibling tool also terminates. The prompt therefore requires the completion call to be standalone and last. The extension deliberately does not infer completion from phrases such as β€œI will present the plan,” and it does not automatically retry a turn with no plan because research and clarification turns may legitimately remain unfinished. If a turn ends without a plan, Plan mode stays active; use `/plan finalize` for explicit recovery.
133
-
134
- Legacy sessions and models may still submit one non-empty `<proposed_plan>` block with tags on their own lines. That compatibility path remains accepted, but it is not the primary workflow. Empty, malformed, unclosed, or multiple legacy blocks keep Plan mode active and produce a warning.
135
-
136
- After completion, `/plan` opens the ready actions when interactive UI is available. The same flat menu shows **Implement here** and **Start fresh and implement**, explains which conversation context each choice uses, and previews how long the approved plan will remain active. **Implement here**β€”and the compatibility route `/plan implement`β€”disables Plan mode, restores full tool access, captures the current **After Implement** policy, and starts implementation in the current session with its planning conversation. **Start fresh and implement** waits for the source session to become idle, verifies the selected model and authentication, creates a new session linked to the persisted source as its parent, and transfers the exact approved plan without copying planning messages, tool results, or compaction/branch summaries. The destination still loads its normal `AGENTS.md`, skills, project resources, and extensions. Choosing **Export plan…** asks for a destination, writes the plan, restores normal tools and thinking, and leaves Plan mode without starting a model turn. Choosing **Save for later**β€”or running `/plan save`β€”instead stores one plan in the current Pi session before leaving Plan mode.
137
-
138
- When a workflow was started only with `--plan` and no `/plan` command has run in that session, the automatic menu cannot obtain Pi's command-only session replacement capability; choosing fresh asks you to reopen `/plan`, where the same action is available. A successful fresh handoff does not delete or consume the source planning session. Resume it later to inspect or hand off the ready/saved plan again; this deliberate duplication is the recovery path if the destination work is abandoned. In-memory sessions create an unlinked fresh session because no parent file exists. Escape, Ctrl+C, menu disposal, source replacement/shutdown, model/auth failure, or cancellation by another extension before replacement leaves the source plan unchanged. Once replacement succeeds, the destination persists active-plan state before kickoff. If persistence fails, the complete request is placed in the destination editor and the source remains resumable. If kickoff fails, the destination retains the active plan; send a message to continue, use `/plan exit` to clear it, or resume the parent planning session.
139
-
140
- A saved plan appears as `plan saved` and remains available after reload, resume, branch-local fork, and compaction in that session. It does not expire automatically, cross into a new session, or participate in ordinary model context. Open `/plan` to Show, Implement here, Start fresh and implement, Export, open Settings, or Clear it; `/plan show`, `/plan implement`, `/plan export [path]`, and `/plan exit`/`off` retain their direct routes in TUI and RPC. Fresh implementation checks idle state, the selected model, and authentication before session replacement; Implement here keeps its established preflight behavior. Starting another workflow with `/plan start`, `/plan <prompt>`, or `/plan tools` is blocked until the saved plan is implemented or cleared, so the single saved slot is never silently overwritten. Resuming that session with `--plan` moves the saved plan back to ready Plan mode. Cancellation or failed implementation preflight leaves it unchanged.
141
-
142
- Text print and JSON modes cannot display the bare `/plan` menu and reject that route before changing
143
- state; use `/plan start` for direct no-prompt activation or `/plan <prompt>` to start planning with a
144
- prompt. `/plan tools` also rejects before changing state because its staged selector requires TUI or
145
- RPC. These modes can export any stored plan with `/plan export [path]`, save a ready plan with
146
- `/plan save`, and clear it with `/plan exit` or `/plan off`. Successful export is observable through
147
- the created file; exporting a ready plan also leaves Plan mode, while saved and active implementation
148
- state remains unchanged. An existing target or missing plan fails the command without changing state.
149
- These modes reject saved-plan display and implementation before changing state because Pi provides
150
- neither printable custom-message output nor acknowledged extension-triggered turns; resume the
151
- session in TUI or RPC to show or implement it.
152
-
153
- Both implementation paths apply the current **After Implement** policy in their destination; the fresh-session choice does not change retention semantics. With the default **Keep plan active** policy, the exact accepted plan remains active across later turns, session resume, and manual or automatic compaction without depending on the compaction summary. Plan mode avoids a duplicate context block while the original implementation handoff remains available and injects one hidden canonical copy after that handoff is compacted away. This can consume up to the existing 50,000-character plan limit in model context when reinjection is needed. **Use plan for handoff only** clears retained state immediately after the first implementation request receives the complete plan. **Clear after first implementation run** keeps the plan through that run, including retries, compaction retries, and queued continuation, then clears it at `agent_settled`. Cleanup is bound to the matching implementation, so an older run settling cannot clear a newer handoff.
154
-
155
- While implementation is active, `/plan show` displays the accepted plan. Interactive `/plan` offers Show, Export plan…, Settings, Start a new plan, and Clear; `/plan exit` and `/plan off` are the direct clear routes. Settings changes never alter the policy already captured by the active implementation. Automatic cleanup has the same observable result as clearing: its active status and future injected plan context disappear, while the implementation request that triggered cleanup still receives the complete plan. Starting a new Plan-mode workflow or implementing a replacement plan supersedes the previous active plan. The extension deliberately does not infer completion from assistant prose or agent settlement, so clear the active plan when the implementation no longer applies. Choosing Stay before implementation keeps the plan ready. Revision feedback starts another Plan-mode turn and clears the previous implementable plan until an updated completion arrives. For clarification-only follow-ups, the agent answers and resubmits the complete unchanged plan so it remains implementable. Before saving or implementation, exit/off discards the ready plan and removes its completion result from later non-Plan model context.
156
-
157
- While Plan mode is enabled, the extension also publishes a compact status for Pi statuslines. With a statusline that renders extension statuses (for example `@narumitw/pi-statusline`; this repo's `@hank-warren/pi-statusline` does not), this appears as:
158
-
159
- - `plan active`: Plan mode is enabled and still gathering context or drafting a plan.
160
- - `plan ready`: A completed plan is stored until you implement it, export it, save it, continue planning, or exit Plan mode.
161
- - `plan saved`: One completed plan is stored outside model context in the current session until you implement or clear it.
162
- - `plan implementing`: The exact accepted plan is currently retained under its captured **After Implement** policy.
163
-
164
- You can also exit directly. Before implementation, direct exit discards the latest proposed plan; while a plan is saved, it clears that saved plan. During implementation, it removes both the original implementation handoff and the extension's canonical active-plan block from later model calls; an earlier Pi-generated compaction summary may still describe prior work:
165
-
166
- ```text
167
- /plan exit
168
- ```
169
-
170
- ## βš™οΈ Settings
171
-
172
- Open **Settings** from an inactive `/plan` menu to edit one flat group of five workflow choices: **Plan thinking**, **Plan tools**, **Bash policy**, **After Implement**, and **Export destination**. You can also edit `$PI_CODING_AGENT_DIR/pi-plan-mode.json` (normally `~/.pi/agent/pi-plan-mode.json`) manually; `safeSubcommands` remains JSON-only. The file is optional, is read at session start, and is created only after an explicit Settings save or manual edit.
46
+ `--plan` starts a session directly in Plan mode.
173
47
 
174
- ```json
175
- {
176
- "thinkingLevel": "inherit",
177
- "defaultPlanTools": ["read", "bash", "grep", "find", "ls", "mcp"],
178
- "bashPolicy": "auto-permissions",
179
- "implementationPlanRetention": "keep",
180
- "defaultPlanExportPath": "PLAN.md",
181
- "safeSubcommands": {
182
- "git": ["status", "log", "rev-parse", "blame"],
183
- "gh": ["pr view", "pr list", "issue view", "issue list"]
184
- }
185
- }
186
- ```
48
+ While Plan mode is active, ask the agent to design the change. It can read, search, and run commands, but `edit`, `write`, and `update_plan` are blocked. When the plan is decision-complete, the agent calls `plan_mode_complete` and the plan is written to disk.
187
49
 
188
- ### Default Plan tools
50
+ From a completed plan you can:
189
51
 
190
- `defaultPlanTools` defines the initial tool selection when a session has no stored pre-start selection. Omit itβ€”or choose **Use automatic safe built-ins**β€”to keep the available safe built-ins as the default. An explicit empty array appears as **Required tools only** and enables only `plan_mode_question` and `plan_mode_complete`.
52
+ - **Implement here** β€” Plan mode turns off and implementation continues in this conversation.
53
+ - **Start fresh and implement** β€” a new linked session opens, pointed at the same plan file, without carrying the planning conversation.
54
+ - **Export plan…** β€” write the plan to a path of your choice.
55
+ - **Stay in Plan mode** β€” keep refining. The next planning turn supersedes the previous plan.
191
56
 
192
- Tool names must be non-empty strings; duplicates are removed in first-seen order. Unknown, unavailable, and Plan-mode-blocked names are ignored when tools are activated. Settings retains configured unavailable names and shows them as unavailable; resetting to automatic removes the entire override. A tool registered after Plan mode is already active is not added automatically; exit and start a new Plan workflow to apply another set. Non-built-in tools named in this global setting are an explicit user-risk opt-in, just like selecting them in the pre-start workflow selector. Plan mode does not interpret their arguments or actions: enabling one trusts the whole effective tool. Pi resolves tools by name, so if an extension overrides a built-in name, the effective extension tool is selected instead. An effective tool named `bash` remains subject to the limited-shell policy regardless of its source metadata.
57
+ Print and JSON modes cannot show the interactive menu; use `/plan start`, `/plan <prompt>`, `/plan show`, `/plan export`, and `/plan exit` there.
193
58
 
194
- A selection accepted through **Choose tools, then start…** or `/plan tools` is stored in that Pi session and takes precedence over `defaultPlanTools` when the session resumes. The global setting remains the baseline for fresh sessions and sessions without an explicit selection. Settings saves immediately, but the saved tools and thinking level apply only when a later Plan workflow starts; they never mutate a workflow already in progress.
59
+ ## πŸ“„ The plan file
195
60
 
196
- ### Bash policy
61
+ The plan lives at `<agent dir>/plans/<session-id>.md` β€” normally `~/.pi/agent/plans/<session-id>.md`.
197
62
 
198
- `bashPolicy` controls which extension makes Bash authorization decisions while Plan mode is active:
63
+ - **It is the plan.** Session state stores only the path.
64
+ - **Hand-edit it freely.** Everything reads from disk, so your edits are what the agent implements.
65
+ - **It survives compaction** because the model only ever sees a one-line pointer to it, and re-reads the file when needed.
66
+ - **A fresh implementation session points at the same file.** The plan is never copied, so both sessions see the same content.
67
+ - `/plan exit` deletes it. Export first if you want to keep a copy.
199
68
 
200
- - Omit it or use `limited` for the upstream-compatible, fail-closed Plan-mode inspection allowlist.
201
- - Use `auto-permissions` to retain the Plan allowlist while selectively deferring blocked commands that match an active Auto Permissions guarded rule.
69
+ Writes are atomic (temp file plus rename), so a reader never sees a partial plan.
202
70
 
203
- Delegation does not activate tools. Keep `bash` in `defaultPlanTools` or select it before starting the workflow. It also does not activate custom tools: add names such as `mcp`, `source_check`, or `ask_user_question` to `defaultPlanTools`, or select them at launch. Plan mode treats every selected non-built-in tool as a whole-tool user-risk opt-in because Pi does not publish standardized mutability metadata.
71
+ ## βš™οΈ Settings
204
72
 
205
- An Auto Permissions approval is permission to execute the exact call, not permission to implement the plan. The Plan prompt continues to require non-mutating information gathering. The compatibility preflight is deliberately fail-closed and currently mirrors the Auto Permissions 0.1.x configuration contract, including `PI_AUTO_PERMISSIONS_CONFIG` and trusted project groups. If that contract changes in a future Auto Permissions release, unmatched or unreadable policy falls back to the Plan-mode block.
73
+ Open **Settings** from the `/plan` menu, or edit `$PI_CODING_AGENT_DIR/pi-plan-mode.json` (normally `~/.pi/agent/pi-plan-mode.json`). The file is optional.
206
74
 
207
- ### After Implement
75
+ ```json
76
+ {
77
+ "thinkingLevel": "inherit",
78
+ "defaultPlanExportPath": "PLAN.md"
79
+ }
80
+ ```
208
81
 
209
- `implementationPlanRetention` controls the result of the next Implement action. Omit it or use `keep` for **Keep plan active**, the backward-compatible behavior that retains and reinjects the exact plan until `/plan exit` or supersession. Use `clear-on-start` for **Use plan for handoff only**: the first matching implementation request receives the complete plan, then retained state clears before later requests. Use `clear-after-first-run` to retain the plan until that implementation's first fully settled run ends. A resumed cleanup policy re-arms against the first context in the replacement session. Failed handoff delivery restores the ready or saved plan and does not run automatic cleanup.
82
+ ### Plan thinking
210
83
 
211
- Changing this setting applies to the next Implement action only. Each active implementation stores its effective policy, so a later Settings save cannot shorten or extend an implementation already in progress. `/plan exit` remains available under every policy.
84
+ `thinkingLevel` requests a fixed thinking level while Plan mode is active. Supported values are `inherit`, `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. The extension restores your previous level on exit unless you changed it manually during Plan mode. Saving applies to the next Plan workflow, never one already running.
212
85
 
213
86
  ### Export destination
214
87
 
215
- `defaultPlanExportPath` controls only exports that omit a path. Omit itβ€”or submit an empty value in Settingsβ€”to use `PLAN.md`. The value must be a non-empty string of at most 4,096 characters without terminal control characters or NUL. Relative values are resolved against the current working directory at export time; the Settings detail and every export input preview the concrete resolved destination. An explicit `/plan export <path>` is a one-off override and does not edit Settings. Saving a new destination affects the next export immediately, including export of a currently active implementation.
88
+ `defaultPlanExportPath` controls only exports that omit a path, and defaults to `PLAN.md`. Relative values resolve against the current working directory at export time. An explicit `/plan export <path>` always wins. Export never overwrites an existing file, directory, or symbolic link.
216
89
 
217
- The existing no-overwrite, cancellation, and atomic Plan-state behavior is unchanged. A failed save rolls the row back to its previous value; a failed or cancelled export preserves the plan and target. Long previews wrap or truncate to the available terminal width without changing the raw path used by the action.
90
+ Unknown keys are preserved. Settings removed in 1.0 (`defaultPlanTools`, `bashPolicy`, `safeSubcommands`, `implementationPlanRetention`) are ignored rather than treated as errors, so an existing settings file keeps working.
218
91
 
219
- ### Safe shell subcommands
92
+ ## πŸ” What Plan mode does and does not enforce
220
93
 
221
- `safeSubcommands` adds reviewed command validators to limited `bash`; it is not a raw shell allowlist. Only the following exact values are accepted:
94
+ Plan mode blocks exactly three tools while planning: `edit`, `write`, and `update_plan`. That is the whole enforcement surface.
222
95
 
223
- - `git`: `status`, `log`, `diff`, `show`, `branch`, `remote`, `ls-files`, `grep`, `rev-parse`, `blame`, `describe`, `merge-base`, `ls-tree`, and `cat-file`.
224
- - `gh`: `pr view`, `pr list`, `issue view`, and `issue list`.
96
+ It deliberately does **not** police Bash, subagents, MCP tools, or any other extension tool. Those decisions belong to your permission layer, which can see the whole session and judge each call. Pair Plan mode with a permission extension such as `@hank-warren/pi-auto-permissions` if you want command review during planning.
225
97
 
226
- The first eight Git validators are built in and remain enabled when omitted, so listing them is valid but redundant. The other six Git validators and every `gh` path require an explicit opt-in. Git entries select one exact subcommand; `gh` entries select one exact two-word path, so `"pr view"` never enables `pr merge`, `pr close`, or `pr edit`. Omitted `safeSubcommands`, an empty object, and empty arrays preserve the default policy. Duplicate values are removed in first-seen order.
98
+ Plan mode also never calls `setActiveTools`. Extensions that register tools lazily (MCP connections, subagent supervision channels) keep working normally, and nothing needs to be restored when Plan mode exits.
227
99
 
228
- With the example configuration above, commands such as these are accepted:
100
+ ## πŸ“Š Statusline
229
101
 
230
- ```bash
231
- git rev-parse --show-toplevel
232
- git blame -- src/plan-mode.ts
233
- git diff --cached
234
- git show --stat --oneline HEAD
235
- git log -p -1 HEAD -- src/plan-mode.ts
236
- gh pr view 218 --json number,title,state
237
- gh issue list --state open --json number,title,state
238
- ```
239
-
240
- The command-specific validators still reject unsafe forms, including:
241
-
242
- ```bash
243
- git blame --textconv -- src/plan-mode.ts
244
- git cat-file --filters HEAD
245
- git diff --ext-diff
246
- git log --show-signature -1
247
- git remote show origin
248
- git show --textconv HEAD
249
- gh pr merge 218
250
- gh pr view 218
251
- gh pr view 218 --web
252
- gh pr view 218 > pr.txt
253
- gh pr list --json number,title && gh pr merge 218
254
- ```
255
-
256
- Redirects, shell expansion and substitution, explicit pager or browser requests, explicit external diff/textconv/filter/signature helpers, output flags, malformed command layouts, and any chain containing an unsafe segment fail closed. Read-dominant Git validators accept ordinary inspection flags without requiring `--no-textconv` or `--no-ext-diff`; Git may therefore invoke a helper configured by the user or trusted repository even when the command does not request one explicitly. Use the negative flags when you want to suppress those configured helpers. Mixed read/write surfaces remain narrower: use `git remote show -n` to avoid invoking a transport helper, while mutating `branch` and `remote` forms remain blocked. GitHub CLI read paths require `--json <fields>` output so Plan mode does not rely on `GH_PAGER`, `PAGER`, or gh pager configuration. Unknown `safeSubcommands` keys or values, non-array values, and non-string entries invalidate the entire settings file and trigger the normal warning/default fallback on session start.
257
-
258
- Read-only does not mean private: Git inspection can expose repository history and tracked secrets, while `gh` queries can expose remote repository, pull request, and issue data available to your authenticated account. The policy reduces accidental mutation and explicit helper execution; it is not a sandbox or a confidentiality boundary.
259
-
260
- ### Thinking level
261
-
262
- Plan mode inherits Pi's current thinking level by default. Set `thinkingLevel` to request a fixed level only while Plan mode is active. Supported values are `inherit`, `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. The extension snapshots the prior level and restores it on exit only if the level still matches the value it applied; a manual change made during Plan mode is preserved. A Settings save does not change Pi's current or default thinking level and takes effect only when the next Plan workflow starts.
263
-
264
- Settings saves are serialized in invocation order inside one Pi process. Each save re-reads the latest valid document, preserves unknown top-level fields and unedited `safeSubcommands`, then publishes through a same-directory temporary file and rename. A missing file stays absent until an explicit save. Invalid JSON, invalid values, oversized content, non-regular files, and read failures make Settings read-only; the existing bytes and previous effective settings remain. This in-process queue is not a cross-process lock, so concurrent separate Pi processes can still race.
102
+ Plan mode publishes a compact status for statuslines that render extension statuses:
265
103
 
266
- Invalid settings produce a warning and fall back to inherited thinking, available safe-built-in tool defaults, the `limited` Bash policy, `keep`, and `PLAN.md`. Compatibility: a valid legacy `plan-mode.json` remains readable with a warning and is never modified automatically. If Settings is explicitly saved while only that legacy file exists, the extension creates canonical `pi-plan-mode.json` from the complete legacy document, applies the selected change, preserves unknown fields, and leaves the legacy file untouched. If both files exist, the canonical filename takes precedence.
267
-
268
- ## 🧠 Codex-like behavior
269
-
270
- This extension maps Codex's `ModeKind::Plan` behavior onto Pi's extension API:
271
-
272
- - Plan mode is a conversational collaboration mode, not TODO/progress tracking.
273
- - `/plan <prompt>` follows Codex behavior by switching to Plan mode before submitting the inline prompt.
274
- - The agent should use `plan_mode_question` for important non-discoverable preferences or tradeoffs before finalizing.
275
- - The agent completes with a standalone `plan_mode_complete` tool call instead of relying on semantic prose detection.
276
- - `update_plan` checklist use is blocked while Plan mode is active.
277
- - The implementation boundary is explicit: Plan mode restores tools before saving or starting implementation, saving keeps the plan outside ordinary model context, choosing implementation immediately triggers a normal agent turn with full tool access, and the accepted plan follows the captured `keep`, `clear-on-start`, or `clear-after-first-run` lifecycle.
278
- - Pi extension safety is approximated with tool classification and fail-closed filtering for every effective tool named `bash`; the optional Auto Permissions integration delegates only exact commands covered by active guarded rules. Other non-built-in tools remain user-selected at user risk because Pi does not expose standardized tool mutability metadata.
279
- - Unlike native Codex, this extension uses a terminating Pi tool plus an `agent_settled` ready flow; Pi cannot provide sandbox-level enforcement.
104
+ - `plan active` β€” planning is under way.
105
+ - `plan ready` β€” a completed plan is waiting for your choice.
106
+ - `plan implementing` β€” a plan file is active and guiding implementation.
280
107
 
281
108
  ## πŸ—‚οΈ Package layout
282
109
 
@@ -284,9 +111,10 @@ This extension maps Codex's `ModeKind::Plan` behavior onto Pi's extension API:
284
111
  packages/pi-plan-mode/
285
112
  β”œβ”€β”€ index.ts # Pi package entrypoint
286
113
  β”œβ”€β”€ src/
287
- β”‚ β”œβ”€β”€ plan-mode.ts # Extension registration, mode state, and UI loading boundary
288
- β”‚ β”œβ”€β”€ interactive-ui.ts # Lazily loaded interactive menu surface
289
- β”‚ └── *.ts # Package-local prompt, policy, question, and message modules
114
+ β”‚ β”œβ”€β”€ plan-mode.ts # Extension registration, mode state, hooks
115
+ β”‚ β”œβ”€β”€ plan-file.ts # Durable plan file read/write/delete
116
+ β”‚ β”œβ”€β”€ interactive-ui.ts # Lazily loaded interactive menus
117
+ β”‚ └── *.ts # Prompt, question, export, settings modules
290
118
  β”œβ”€β”€ test/
291
119
  β”œβ”€β”€ README.md
292
120
  β”œβ”€β”€ NOTICE.md
@@ -294,20 +122,6 @@ packages/pi-plan-mode/
294
122
  └── package.json
295
123
  ```
296
124
 
297
- The root `index.ts` forwards to the internal source entrypoint. The package exposes its Pi extension through `package.json`:
298
-
299
- ```json
300
- {
301
- "pi": {
302
- "extensions": ["./index.ts"]
303
- }
304
- }
305
- ```
306
-
307
- ## πŸ”Ž Keywords
308
-
309
- Pi extension, Pi coding agent, plan mode, Codex-like plan mode, AI coding workflow, read-only planning, implementation plan.
310
-
311
125
  ## πŸ“„ License
312
126
 
313
127
  MIT. See [`LICENSE`](./LICENSE).
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "@hank-warren/pi-plan-mode",
3
- "version": "0.1.0",
4
- "description": "Codex-like Plan mode for Pi with fail-closed integration for Auto Permissions Bash review.",
3
+ "version": "1.0.1",
4
+ "description": "Plan mode for Pi: research and design with a durable plan file that survives compaction.",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "pi-package",
8
8
  "pi-extension",
9
9
  "pi",
10
10
  "plan-mode",
11
- "planning",
12
- "permissions"
11
+ "planning"
13
12
  ],
14
13
  "author": "Hank Warren",
15
14
  "license": "MIT",
@@ -35,7 +34,8 @@
35
34
  "src",
36
35
  "README.md",
37
36
  "NOTICE.md",
38
- "LICENSE"
37
+ "LICENSE",
38
+ "CHANGELOG.md"
39
39
  ],
40
40
  "peerDependencies": {
41
41
  "@earendil-works/pi-coding-agent": "*",
@@ -4,10 +4,11 @@ import { type PlanExportDestinationProvider, planExportInputScreen } from "./pla
4
4
 
5
5
  interface ActiveImplementationMenuOptions {
6
6
  statusText: string;
7
+ planPathLine?: string;
7
8
  getExportDestination: PlanExportDestinationProvider;
8
9
  signal: AbortSignal;
9
10
  isCurrent(): boolean;
10
- show(): void;
11
+ show(): void | Promise<void>;
11
12
  exportPlan(path: string, signal: AbortSignal): Promise<boolean>;
12
13
  settings(signal: AbortSignal): Promise<boolean>;
13
14
  startNew(): void;
@@ -26,7 +27,7 @@ export async function showActiveImplementationMenu(
26
27
  active: () => ({
27
28
  kind: "actions",
28
29
  title: "Active implementation plan",
29
- lines: [options.statusText],
30
+ lines: [options.statusText, ...(options.planPathLine ? [options.planPathLine] : [])],
30
31
  items: [
31
32
  { id: "show", label: "Show active implementation plan", action: "show" },
32
33
  { id: "export", label: "Export plan…", to: "export" },
@@ -40,7 +41,7 @@ export async function showActiveImplementationMenu(
40
41
  },
41
42
  actions: {
42
43
  show: async () => {
43
- options.show();
44
+ await options.show();
44
45
  return { kind: "close" };
45
46
  },
46
47
  export: async ({ value, signal }) =>
package/src/command.ts CHANGED
@@ -6,18 +6,12 @@ export interface CommandArgumentCompletion {
6
6
 
7
7
  const PLAN_COMMAND_COMPLETIONS: readonly CommandArgumentCompletion[] = [
8
8
  { value: "start", label: "start", description: "Start Plan mode without sending a prompt" },
9
- { value: "show", label: "show", description: "Show the ready, saved, or active plan" },
9
+ { value: "show", label: "show", description: "Show the stored plan" },
10
10
  { value: "finalize", label: "finalize", description: "Request a completed plan" },
11
- { value: "implement", label: "implement", description: "Implement the completed or saved plan" },
12
- { value: "save", label: "save", description: "Save the completed plan for later" },
11
+ { value: "implement", label: "implement", description: "Implement the completed plan" },
13
12
  { value: "export", label: "export", description: "Export the stored plan to a Markdown file" },
14
- { value: "exit", label: "exit", description: "Leave Plan mode or clear a saved/active plan" },
15
- { value: "off", label: "off", description: "Leave Plan mode or clear a saved/active plan" },
16
- {
17
- value: "tools",
18
- label: "tools",
19
- description: "Choose tools before starting this Plan workflow",
20
- },
13
+ { value: "exit", label: "exit", description: "Leave Plan mode and clear the plan" },
14
+ { value: "off", label: "off", description: "Leave Plan mode and clear the plan" },
21
15
  ];
22
16
 
23
17
  export function completePlanArguments(argumentPrefix: string): CommandArgumentCompletion[] | null {
@@ -9,6 +9,7 @@ export type PlanModeCompletionDetails = {
9
9
  version: typeof PLAN_MODE_COMPLETE_VERSION;
10
10
  source: typeof PLAN_MODE_COMPLETE_TOOL_NAME;
11
11
  plan: string;
12
+ planPath?: string;
12
13
  };
13
14
 
14
15
  export const PLAN_MODE_COMPLETE_PARAMS = {
@@ -54,13 +55,14 @@ export function planFromCompletionDetails(value: unknown) {
54
55
  return normalized.ok ? normalized.plan : undefined;
55
56
  }
56
57
 
57
- export function planModeCompleted(plan: string) {
58
+ export function planModeCompleted(plan: string, planPath?: string) {
58
59
  return {
59
60
  content: [{ type: "text" as const, text: `**Proposed Plan**\n\n${plan}` }],
60
61
  details: {
61
62
  version: PLAN_MODE_COMPLETE_VERSION,
62
63
  source: PLAN_MODE_COMPLETE_TOOL_NAME,
63
64
  plan,
65
+ ...(planPath ? { planPath } : {}),
64
66
  } satisfies PlanModeCompletionDetails,
65
67
  terminate: true,
66
68
  };