From 1a61c6b5c254b8ed7e01a3000192c8a9effb64f9 Mon Sep 17 00:00:00 2001 From: Jeffrey Tseng Date: Sat, 15 Aug 2026 16:41:22 -0700 Subject: [PATCH] docs(coding-agent): document native plan mode Document plan-mode commands, SDK controls, structured output, session records, task profiles, extension behavior, and usage examples. --- packages/coding-agent/README.md | 41 ++++++- packages/coding-agent/docs/extensions.md | 14 ++- packages/coding-agent/docs/json.md | 19 +++- packages/coding-agent/docs/sdk.md | 105 ++++++++++++++++++ packages/coding-agent/docs/session-format.md | 58 +++++++++- packages/coding-agent/docs/tui.md | 6 +- packages/coding-agent/docs/usage.md | 45 +++++++- .../examples/extensions/README.md | 2 +- .../examples/extensions/plan-mode/README.md | 15 ++- 9 files changed, 274 insertions(+), 31 deletions(-) diff --git a/packages/coding-agent/README.md b/packages/coding-agent/README.md index 9ce210850..7492b8c6b 100644 --- a/packages/coding-agent/README.md +++ b/packages/coding-agent/README.md @@ -14,7 +14,7 @@ Diffex is a minimal terminal coding harness. Adapt Diffex to your workflows, not the other way around, without having to fork and modify Diffex internals. Extend it with TypeScript [Extensions](#extensions), [Skills](#skills), [Prompt Templates](#prompt-templates), and [Themes](#themes). Put your extensions, skills, prompt templates, and themes in [Diffex Packages](#diffex-packages) and share them with others via npm or git. -Diffex ships with powerful defaults, including session-scoped sub-agents, but skips features like plan mode. You can ask Diffex to build other workflows or install a third party Diffex package that matches them. +Diffex ships with powerful defaults, including a native read-only plan mode and session-scoped sub-agents. You can build other workflows or install a third-party Diffex package that provides them. Diffex runs in four modes: interactive, print or JSON, RPC for process integration, and an SDK for embedding in your own apps. @@ -39,6 +39,7 @@ Example upstream `pi-mono` work sessions are published here: - [Quick Start](#quick-start) - [Providers & Models](#providers--models) - [Interactive Mode](#interactive-mode) + - [Plan Mode](#plan-mode) - [Editor](#editor) - [Commands](#commands) - [Keyboard Shortcuts](#keyboard-shortcuts) @@ -147,10 +148,32 @@ The interface from top to bottom: - **Startup header** - Shows shortcuts (`/hotkeys` for all), loaded AGENTS.md files, prompt templates, skills, and extensions - **Messages** - Your messages, assistant responses, tool calls and results, notifications, errors, and extension UI - **Editor** - Where you type; border color indicates thinking level -- **Footer** - Working directory, session name, total token/cache usage (`↑` input, `↓` output, `R` cache read, `W` cache write, `CH` latest cache hit rate), cost, context usage, current model. Totals include assistant responses, usage reported by tools, and summary generation. +- **Footer** - The built-in footer keeps the active profile, routing mode, and harness visible above the current model, thinking level, and context usage. The editor can be temporarily replaced by other UI, like built-in `/settings` or custom UI from extensions (e.g., a Q&A tool that lets the user answer model questions in a structured format). [Extensions](#extensions) can also replace the editor, add widgets above/below it, a status line, custom footer, or overlays. +### Plan Mode + +Run `diffex --plan` to start an interactive session in plan mode, or use `/plan [on|off]` while Diffex is idle. +Plan mode exposes `submit_plan` and the canonical subset of `read`, `grep`, `find`, and `ls` permitted by caller restrictions and the active task profile. +The agent can inspect the repository, but it cannot run commands, edit files, or call extension and SDK tools. +`submit_plan` is intrinsic to plan mode and remains available even when caller tool filters disable every inspection tool. +Automatic task-profile routing pauses for planning prompts. +In automatic mode, choosing Implement routes every accepted task before execution, even when a harness is already active. +The router may keep the active harness for a continuation, reuse a related harness, or switch to a newly selected task harness. +Diffex asks which profile should govern implementation only when the router finds a high-confidence ambiguity among specialized profiles. +The chooser shows each option's description, safeguards, and tool-policy summary. +The choice determines the implementation profile's expertise, safeguards, and tool access. +The recommended option receives initial focus but is not selected automatically. +Canceling the choice leaves the accepted implementation pending; use `/plan` to retry. +A malformed, low-confidence, or failed route also leaves the accepted implementation pending instead of silently falling back to another profile. +A pinned profile bypasses automatic routing and implements the plan in place. +Pin a profile before planning when its guardrails must apply during planning itself. + +Submitting a plan ends the planning turn and stores the plan in the session until you implement or discard it. +Pending plans and collaboration-mode changes survive session resume and tree navigation. +The `--plan` flag is supported only in interactive mode. + ### Editor | Feature | How | @@ -174,6 +197,7 @@ Type `/` in the editor to trigger commands. [Extensions](#extensions) can regist | [`/llama`](docs/llama-cpp.md) | Download, load, and unload llama.cpp router models | | `/model` | Switch models | | `/scoped-models` | Enable/disable models for Ctrl+P cycling | +| `/plan [on\|off]` | Toggle plan mode, set it explicitly, or reopen a pending plan | | `/settings` | Thinking level, theme, message delivery, transport | | `/resume` | Pick from previous sessions | | `/new` | Start a new session | @@ -384,7 +408,7 @@ The default export can also be `async`. Diffex waits for async extension factori **What's possible:** - Custom tools (or replace built-in tools entirely) -- Alternative sub-agent workflows and plan mode +- Alternative sub-agent, planning, and custom workflows - Custom compaction and summarization - Permission gates and path protection - Custom editors and UI components @@ -501,7 +525,7 @@ Diffex is aggressively extensible so it doesn't have to dictate your workflow. F **No permission popups.** Run in a container, or build your own confirmation flow with [extensions](#extensions) inline with your environment and security requirements. -**No plan mode.** The built-in `update_plan` tool is only a lightweight progress checklist. For approval workflows, read-only planning phases, or richer planning modes, write plans to files, build an [extension](#extensions), or install a package. +**Native plan mode stays read-only.** It uses a fixed planning tool set and requires explicit resolution before implementation begins. **No persistent built-in to-dos.** Use a TODO.md file, or build your own with [extensions](#extensions). @@ -544,8 +568,11 @@ diffex config # Enable/disable package resources | `-p`, `--print` | Print response and exit | | `--mode json` | Output all events as JSON lines (see [docs/json.md](docs/json.md)) | | `--mode rpc` | RPC mode for process integration (see [docs/rpc.md](docs/rpc.md)) | +| `--plan` | Start interactive mode with native plan mode active | | `--export [out]` | Export session to HTML | +Diffex rejects `--plan` with `-p`, `--mode json`, and `--mode rpc` because those hosts cannot resolve a submitted plan interactively. + In print mode, Diffex also reads piped stdin and merges it into the initial prompt: ```bash @@ -582,9 +609,10 @@ cat README.md | diffex -p "Summarize this text" | `--tools `, `-t ` | Allowlist specific tool names across built-in, extension, and custom tools | | `--exclude-tools `, `-xt ` | Disable specific tool names across built-in, extension, and custom tools | | `--no-builtin-tools`, `-nbt` | Disable built-in tools by default but keep extension/custom tools enabled | -| `--no-tools`, `-nt` | Disable all tools by default | +| `--no-tools`, `-nt` | Disable all normal-mode tools and plan-mode inspection tools by default | Available built-in tools: `read`, `bash`, `edit`, `write`, `update_plan`, `grep`, `find`, `ls` +Tool allowlists and denylists also constrain plan-mode inspection tools, but the intrinsic `submit_plan` tool remains available in plan mode. ### Resource Options @@ -631,6 +659,9 @@ diffex @code.ts @test.ts "Review these files" # Interactive with initial prompt diffex "List all .ts files in src/" +# Interactive read-only planning +diffex --plan "Design the implementation before changing anything" + # Non-interactive diffex -p "Summarize this codebase" diff --git a/packages/coding-agent/docs/extensions.md b/packages/coding-agent/docs/extensions.md index 7c4298071..73eabf227 100644 --- a/packages/coding-agent/docs/extensions.md +++ b/packages/coding-agent/docs/extensions.md @@ -1610,7 +1610,7 @@ Register a keyboard shortcut. See [keybindings.md](keybindings.md) for the short ```typescript diffex.registerShortcut("ctrl+shift+p", { - description: "Toggle plan mode", + description: "Toggle project summary", handler: async (ctx) => { ctx.ui.notify("Toggled!"); }, @@ -1622,15 +1622,15 @@ diffex.registerShortcut("ctrl+shift+p", { Register a CLI flag. ```typescript -diffex.registerFlag("plan", { - description: "Start in plan mode", +diffex.registerFlag("audit", { + description: "Enable additional audit checks", type: "boolean", default: false, }); // Check value -if (diffex.getFlag("plan")) { - // Plan mode enabled +if (diffex.getFlag("audit")) { + // Additional audit checks enabled } ``` @@ -2048,6 +2048,8 @@ diffex.registerTool({ Extensions can override built-in tools (`read`, `bash`, `edit`, `write`, `update_plan`, `grep`, `find`, `ls`) by registering a tool with the same name. Interactive mode displays a warning when this happens. +Native plan mode is an exception: while it is active, extension and SDK tools cannot override the canonical `read`, `grep`, `find`, `ls`, or `submit_plan` tool identities. + ```bash # Extension's read tool replaces built-in read diffex -e ./tool-override.ts @@ -2960,7 +2962,7 @@ All examples in [examples/extensions/](../examples/extensions/). | `timed-confirm.ts` | Dialogs with timeout | `ui.confirm` with timeout/signal | | `mac-system-theme.ts` | Auto-switch theme | `setTheme`, `exec` | | **Complex Extensions** ||| -| `plan-mode/` | Full plan mode implementation | All event types, `registerCommand`, `registerShortcut`, `registerFlag`, `setStatus`, `setWidget`, `sendMessage`, `setActiveTools` | +| `plan-mode/` | Legacy example that is incompatible with native plan mode | Historical extension APIs only; do not load it | | `preset.ts` | Saveable presets (model, tools, thinking) | `registerCommand`, `registerShortcut`, `registerFlag`, `setModel`, `setActiveTools`, `setThinkingLevel`, `appendEntry` | | `tools.ts` | Toggle tools on/off UI | `registerCommand`, `setActiveTools`, `SettingsList`, session events | | **Remote & Sandbox** ||| diff --git a/packages/coding-agent/docs/json.md b/packages/coding-agent/docs/json.md index 169b31517..816a15a9e 100644 --- a/packages/coding-agent/docs/json.md +++ b/packages/coding-agent/docs/json.md @@ -4,25 +4,34 @@ diffex --mode json "Your prompt" ``` -Outputs all session events as JSON lines to stdout. Useful for integrating Diffex into other tools or custom UIs. +Outputs the session events supported by the JSON wire protocol as JSON lines to stdout. +This mode is useful for integrating Diffex into other tools or custom UIs. ## Event Types -Wire events use `JsonAgentSessionEvent`. It matches -[`AgentSessionEvent`](https://github.com/diffexai/diffex/blob/main/packages/coding-agent/src/core/agent-session.ts) -except that streaming message updates omit cumulative snapshots: +Wire events use `JsonAgentSessionEvent`, which matches [`AgentSessionEvent`](https://github.com/diffexai/diffex/blob/main/packages/coding-agent/src/core/agent-session.ts) except for plan-only lifecycle events and cumulative streaming snapshots. ```typescript type WithoutPartial = T extends { partial: unknown } ? Omit : T; type JsonAgentSessionEvent = - | Exclude + | Exclude< + AgentSessionEvent, + { + type: + | "message_update" + | "collaboration_mode_changed" + | "plan_submitted" + | "plan_resolved"; + } + > | { type: "message_update"; assistantMessageEvent: WithoutPartial; }; ``` +The plan-only `collaboration_mode_changed`, `plan_submitted`, and `plan_resolved` events are available to direct `AgentSession` subscribers but are omitted because JSON mode cannot host plan resolution. `queue_update` emits the full pending steering and follow-up queues whenever they change. `compaction_start` and `compaction_end` cover both manual and automatic compaction. Other base events come from diff --git a/packages/coding-agent/docs/sdk.md b/packages/coding-agent/docs/sdk.md index a76f13e97..7366a4237 100644 --- a/packages/coding-agent/docs/sdk.md +++ b/packages/coding-agent/docs/sdk.md @@ -94,9 +94,17 @@ interface AgentSession { agent: Agent; model: Model | undefined; thinkingLevel: ThinkingLevel; + collaborationMode: "default" | "plan"; + pendingPlan: PlanArtifact | undefined; + pendingPlanContinuation: PlanContinuation | undefined; messages: AgentMessage[]; isStreaming: boolean; + // Native plan-mode control + setCollaborationMode(mode: "default" | "plan"): void; + resolvePlan(planId: string, resolution: PlanResolution): PlanContinuation | undefined; + promptPlanContinuation(continuation: PlanContinuation, options?: PlanContinuationPromptOptions): Promise; + // In-place tree navigation within the current session file navigateTree(targetId: string, options?: { summarize?: boolean; customInstructions?: string; replaceInstructions?: boolean; label?: string }): Promise<{ editorText?: string; cancelled: boolean }>; @@ -203,6 +211,8 @@ interface PromptOptions { It fires before `prompt()` resolves. `prompt()` still resolves only after the full accepted run finishes, including retries and coordinated child-agent batches. Failures after acceptance are reported through the normal event and message stream, not through `preflightResult(false)`. +Prompt ownership is acquired synchronously before asynchronous preflight, and `session.isStreaming` remains true from that point through run settlement. +Another prompt submitted during that interval must specify `streamingBehavior` and is queued rather than starting a concurrent preflight. The `prompt()` method handles prompt templates, extension commands, and message sending: @@ -330,6 +340,15 @@ session.subscribe((event) => { case "subagent_wait_end": console.log(`Child wait cancelled: ${event.cancelled}`); break; + case "collaboration_mode_changed": + console.log(`Mode: ${event.mode}`); + break; + case "plan_submitted": + console.log(`Plan ready: ${event.plan.title}`); + break; + case "plan_resolved": + console.log(event.planId, event.resolution); + break; case "compaction_start": case "compaction_end": case "auto_retry_start": @@ -527,6 +546,72 @@ const { session } = await createAgentSession({ resourceLoader: loader }); > See [examples/sdk/03-custom-prompt.ts](../examples/sdk/03-custom-prompt.ts) +### Native Plan Mode + +Set `collaborationMode: "plan"` when creating a session, or call `session.setCollaborationMode("plan")` while the session is idle. +Existing persistent sessions restore the collaboration mode, any pending plan, and any accepted implementation awaiting its initial turn from the active branch unless the caller supplies a compatible override. + +```typescript +import { + createAgentSession, + type PlanArtifact, +} from "@earendil-works/diffex-coding-agent"; + +const { session } = await createAgentSession({ + collaborationMode: "plan", +}); + +async function implementSubmittedPlan(plan: PlanArtifact) { + const continuation = session.resolvePlan(plan.id, "implement"); + if (continuation) { + await session.promptPlanContinuation(continuation); + } +} +``` + +Plan mode exposes `submit_plan` plus the canonical subset of `read`, `grep`, `find`, and `ls` permitted by the SDK tool policy and the active task profile. +The planning tools replace the normal, extension, and SDK tool registry for the duration of plan mode. +`submit_plan` is intrinsic to plan mode and remains available even when `tools`, `excludeTools`, or `noTools` disables every inspection tool. +`AgentSessionRuntime` pauses automatic task-profile routing for planning prompts. +In automatic mode, submit the accepted token through `runtime.promptPlanContinuation()` so the runtime routes every accepted task before execution, including when a harness is already active. +The route may stay on the active harness, reuse a related harness, or select and activate a new harness. +Only a high-confidence ambiguity among specialized profiles calls the per-request `profileSelectionResolver`. +The resolver receives 2-3 trusted, recommended-first `ProfileSelectionOption` values. +Each option includes its profile description, a bounded `safeguardSummary`, and its exact `toolPolicy` so the host can present the guardrail and tool consequences. +The resolver must return `{ profileId }` for one offered option or `{ cancelled: true }`; the runtime never selects the recommended option automatically. +Missing, canceled, invalid, or aborted selection rejects before implementation starts and preserves `session.pendingPlanContinuation` for retry. +Malformed output, low confidence, or router failure also rejects without choosing a fallback profile and preserves the continuation for retry. +A pinned profile bypasses automatic routing and implements the plan in place without calling the resolver. +Pin a profile before planning when its guardrails must apply during planning itself. + +```typescript +await runtime.promptPlanContinuation(continuation, { + profileSelectionResolver: async (proposal, signal) => { + const profileId = await chooseImplementationProfile(proposal.options, signal); + return profileId ? { profileId } : { cancelled: true }; + }, +}); +``` + +`submit_plan` creates a `PlanArtifact` with `id`, `title`, and `markdown`. +It ends the planning turn and exposes the artifact through `session.pendingPlan` and the `plan_submitted` event. +Mode changes, plan artifacts, and plan resolutions are persisted in the session tree. +Wait for the submitting agent run to settle before resolving a plan through `AgentSession` directly. + +Resolve the artifact with `session.resolvePlan(plan.id, "implement")` or `session.resolvePlan(plan.id, "discard")`. +Implement and discard persist the resolution, clear the pending plan, and return the session to default mode. +Implement returns an opaque, one-shot `PlanContinuation`, while discard returns `undefined`. +A bare `AgentSession` host may submit the token through `session.promptPlanContinuation()` to implement it with the currently loaded profile. +An `AgentSessionRuntime` host should use `runtime.promptPlanContinuation()` so automatic routing can activate an initial harness before execution. +The current token is also available from `session.pendingPlanContinuation`, including after a persistent session resumes between acceptance and implementation startup. +The session rejects stale or reused continuations and bypasses extension commands, template expansion, and input transformation for the trusted implementation handoff. +Authentication, compaction, and `before_agent_start` processing still apply, and the continuation remains retryable until the agent run is accepted. +The resolution is committed before the host starts that continuation, and the accepted implementation remains replayable until its trusted initial user message is persisted. +A later failure after that persistence boundary does not restore the resolved plan or continuation. +Diffex refuses to leave plan mode directly while a plan remains pending. + +Subscribe to `collaboration_mode_changed`, `plan_submitted`, and `plan_resolved` to keep a host UI synchronized. + ### Tools Every root session enables three child-agent coordination tools by default: @@ -562,6 +647,8 @@ Specify which tools to enable: - A `tools` allowlist must explicitly include each coordination tool that should remain available - `excludeTools` disables specific built-in, extension, custom, or coordination tool names after any `tools` allowlist is applied +These filters also constrain the plan-mode inspection subset, but they never remove the intrinsic `submit_plan` tool while plan mode is active. + The `edit` tool returns `details.diff` for Diffex's TUI display and `details.patch` as a standard unified patch for SDK consumers. The `update_plan` tool returns `details.plan` with the full ordered checklist snapshot. @@ -1241,6 +1328,9 @@ getPackageDir getReadmePath getDocsPath getExamplesPath +buildAcceptedPlanProfileRouterPrompt +parseAcceptedPlanProfileRouteDecision +routeAcceptedPlanProfile // Session management SessionManager @@ -1255,6 +1345,21 @@ createGrepTool, createFindTool, createLsTool // Types type CreateAgentSessionOptions type CreateAgentSessionResult +type CollaborationMode +type PlanArtifact +type PlanResolution +type PlanContinuation +type PlanContinuationPromptOptions +type RuntimePlanContinuationPromptOptions +type AcceptedPlanProfileRouteDecision +type AmbiguousProfileRouteDecision +type ProfileSelectionOption +type ProfileSelectionProposal +type ProfileSelectionResolution +type ProfileSelectionResolver +type CollaborationModeChangeEntry +type PlanEntry +type PlanResolutionEntry type ExtensionFactory type InlineExtension type ExtensionAPI diff --git a/packages/coding-agent/docs/session-format.md b/packages/coding-agent/docs/session-format.md index 05ede29bd..c991efe85 100644 --- a/packages/coding-agent/docs/session-format.md +++ b/packages/coding-agent/docs/session-format.md @@ -226,6 +226,47 @@ Emitted when the user changes the thinking/reasoning level. {"type":"thinking_level_change","id":"e5f6g7h8","parentId":"d4e5f6g7","timestamp":"2024-12-03T14:06:00.000Z","thinkingLevel":"high"} ``` +### CollaborationModeChangeEntry + +Records an explicit change between default and plan collaboration modes. + +```json +{"type":"collaboration_mode_change","id":"a6b7c8d9","parentId":"e5f6g7h8","timestamp":"2024-12-03T14:07:00.000Z","mode":"plan"} +``` + +The `mode` field is either `"default"` or `"plan"`. + +### PlanEntry + +Created when the planning agent submits a plan for host resolution. + +```json +{"type":"plan","id":"b7c8d9e0","parentId":"a6b7c8d9","timestamp":"2024-12-03T14:08:00.000Z","plan":{"id":"08d4f214-96b3-4ea7-9530-fdb52ef4cd8d","title":"Add account export","markdown":"## Implementation\n\n1. Add the export endpoint.\n2. Add coverage."}} +``` + +The nested plan artifact contains an opaque `id`, a concise `title`, and its complete Markdown body. + +### PlanResolutionEntry + +Created when the host chooses to implement or discard a pending plan. + +```json +{"type":"plan_resolution","id":"c8d9e0f1","parentId":"b7c8d9e0","timestamp":"2024-12-03T14:09:00.000Z","planId":"08d4f214-96b3-4ea7-9530-fdb52ef4cd8d","resolution":"implement"} +``` + +The `resolution` field is either `"implement"` or `"discard"`. +Either resolution clears the matching pending plan and returns the replayed collaboration mode to `"default"`. +An implement resolution creates a pending implementation continuation until its trusted initial user message is appended, while discard leaves no continuation. +This implicit return to default does not append a separate `collaboration_mode_change` entry. + +Collaboration mode, pending-plan state, and pending implementation continuation state are replayed from the complete active branch, including entries before a compaction checkpoint. +Branching to an implement resolution restores its pending continuation until the trusted implementation user message, branching before a resolution makes that branch's matching plan pending again, and branching before the plan removes it from replayed state. +These three entry types carry session state and never become LLM messages. + +Plan artifacts and their lifecycle are validated both when entries are appended and when a session is loaded. +A plan must follow a plan-mode entry, a resolution must match the pending plan, and a branch cannot change collaboration mode while a plan or implementation continuation remains pending. +Invalid artifacts, broken parent chains in plan-aware sessions, and inconsistent plan transitions reject the session instead of restoring mutation-capable default mode. + ### CompactionEntry Created when context is compacted. Stores a summary of earlier messages. @@ -331,13 +372,14 @@ Entries form a tree: `buildSessionContext()` builds on that entry list to produce the message list for the LLM: -1. Extracts current model and thinking level settings from the full path +1. Extracts the current model, thinking level, collaboration mode, pending plan, and pending implementation continuation from the full path 2. Converts selected entries to messages: - `message` -> stored `AgentMessage` - `compaction` -> `compactionSummary` plus `retainedTail` when present - `branch_summary` -> `branchSummary` - `custom_message` -> `CustomMessage` - `custom` -> no context message + - `collaboration_mode_change`, `plan`, and `plan_resolution` -> no context message This makes newer compactions act like self-contained checkpoints. `retainedTail` is optional only so older sessions that only store `firstKeptEntryId` continue to load correctly. @@ -379,6 +421,15 @@ for (const line of lines) { case "thinking_level_change": console.log(`[${entry.id}] Thinking: ${entry.thinkingLevel}`); break; + case "collaboration_mode_change": + console.log(`[${entry.id}] Collaboration mode: ${entry.mode}`); + break; + case "plan": + console.log(`[${entry.id}] Pending plan: ${entry.plan.title}`); + break; + case "plan_resolution": + console.log(`[${entry.id}] Plan ${entry.planId}: ${entry.resolution}`); + break; } } ``` @@ -407,6 +458,9 @@ Key methods for working with sessions programmatically. - `appendMessage(message)` - Add message - `appendThinkingLevelChange(level)` - Record thinking change - `appendModelChange(provider, modelId)` - Record model change +- `appendCollaborationModeChange(mode)` - Record a default or plan collaboration-mode change +- `appendPlan(plan)` - Record a submitted plan artifact +- `appendPlanResolution(planId, resolution)` - Record an implement or discard resolution - `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)` - Add compaction - `appendCustomEntry(customType, data?)` - Extension state (not in context) - `appendSessionInfo(name)` - Set session display name @@ -427,7 +481,7 @@ Key methods for working with sessions programmatically. ### Instance Methods - Context & Info - `buildContextEntries()` - Get active branch entries with compaction applied -- `buildSessionContext()` - Get messages, thinkingLevel, and model for LLM +- `buildSessionContext()` - Get messages, thinking level, model, collaboration mode, pending plan, and pending implementation continuation for the active branch - `getEntries()` - All entries (excluding header) - `getHeader()` - Session header metadata - `getSessionName()` - Get display name from latest session_info entry diff --git a/packages/coding-agent/docs/tui.md b/packages/coding-agent/docs/tui.md index bbb182e14..446e57273 100644 --- a/packages/coding-agent/docs/tui.md +++ b/packages/coding-agent/docs/tui.md @@ -757,7 +757,7 @@ ctx.ui.setStatus("my-ext", ctx.ui.theme.fg("accent", "● active")); ctx.ui.setStatus("my-ext", undefined); ``` -**Examples:** [status-line.ts](../examples/extensions/status-line.ts), [plan-mode/index.ts](../examples/extensions/plan-mode/index.ts), [preset.ts](../examples/extensions/preset.ts) +**Examples:** [status-line.ts](../examples/extensions/status-line.ts), [model-status.ts](../examples/extensions/model-status.ts), [preset.ts](../examples/extensions/preset.ts) ### Pattern 4b: Working Indicator Customization @@ -817,7 +817,7 @@ ctx.ui.setWidget("my-widget", (_tui, theme) => { ctx.ui.setWidget("my-widget", undefined); ``` -**Examples:** [plan-mode/index.ts](../examples/extensions/plan-mode/index.ts) +**Examples:** [widget-placement.ts](../examples/extensions/widget-placement.ts), [todo.ts](../examples/extensions/todo.ts) ### Pattern 6: Custom Footer @@ -934,7 +934,7 @@ export default function (diffex: ExtensionAPI) { - **Selection UI**: [examples/extensions/preset.ts](../examples/extensions/preset.ts) - SelectList with DynamicBorder framing - **Async with cancel**: [examples/extensions/qna.ts](../examples/extensions/qna.ts) - BorderedLoader for LLM calls - **Settings toggles**: [examples/extensions/tools.ts](../examples/extensions/tools.ts) - SettingsList for tool enable/disable -- **Status indicators**: [examples/extensions/plan-mode/index.ts](../examples/extensions/plan-mode/index.ts) - setStatus and setWidget +- **Status indicators**: [examples/extensions/status-line.ts](../examples/extensions/status-line.ts) - setStatus with session events - **Working indicator**: [examples/extensions/working-indicator.ts](../examples/extensions/working-indicator.ts) - setWorkingIndicator - **Custom footer**: [examples/extensions/custom-footer.ts](../examples/extensions/custom-footer.ts) - setFooter with stats - **Custom editor**: [examples/extensions/modal-editor.ts](../examples/extensions/modal-editor.ts) - Vim-like modal editing diff --git a/packages/coding-agent/docs/usage.md b/packages/coding-agent/docs/usage.md index f0b7f5fb2..8bf15bb82 100644 --- a/packages/coding-agent/docs/usage.md +++ b/packages/coding-agent/docs/usage.md @@ -11,7 +11,7 @@ The interface has four main areas: - **Startup header** - shortcuts, loaded context files, prompt templates, skills, and extensions - **Messages** - user messages, assistant responses, tool calls, tool results, notifications, errors, and extension UI - **Editor** - where you type; border color indicates the current thinking level -- **Footer** - working directory, session name, token/cache usage, cost, context usage, and current model. Totals include assistant responses, usage reported by tools, and summary generation. +- **Footer** - the built-in footer keeps the active profile, routing mode, and harness visible above the current model, thinking level, and context usage. The editor can be replaced temporarily by built-in UI such as `/settings` or by custom extension UI. @@ -40,6 +40,7 @@ Type `/` in the editor to open command completion. Extensions can register custo | [`/llama`](llama-cpp.md) | Download, load, and unload llama.cpp router models | | `/model` | Switch models | | `/scoped-models` | Enable/disable models for Ctrl+P cycling | +| `/plan [on\|off]` | Toggle plan mode, set it explicitly, or reopen a pending plan | | `/settings` | Thinking level, theme, message delivery, transport | | `/resume` | Pick from previous sessions | | `/new` | Start a new session | @@ -85,6 +86,34 @@ Completion notices use assistant styling, but are not conversation messages. They are not persisted or included in model context. JSON and RPC modes emit the same notices as structured session events. +## Plan Mode + +Start an interactive planning session with `diffex --plan`, toggle it with `/plan`, or set it explicitly with `/plan on` or `/plan off`. +Plan mode can be changed only while the session is idle. + +Plan mode exposes `submit_plan` and canonical `read`, `grep`, `find`, and `ls` repository inspection tools when caller restrictions and the active task profile permit them. +Extension tools, SDK custom tools, shell execution, and file mutation tools are unavailable until the plan is accepted or discarded. +`submit_plan` is intrinsic to plan mode and remains available even when caller tool filters disable every inspection tool. +Automatic task-profile routing pauses for planning prompts. +In automatic mode, choosing Implement routes every accepted task before execution, even when a harness is already active. +The router may keep the active harness for a continuation, reuse a related harness, or switch to a newly selected task harness. +Diffex asks which profile should govern implementation only when the router finds a high-confidence ambiguity among specialized profiles. +The chooser shows each option's description, safeguards, and tool-policy summary. +The choice determines the implementation profile's expertise, safeguards, and tool access. +The recommended option receives initial focus but is not selected automatically. +Canceling the choice leaves the accepted implementation pending; use `/plan` to retry. +A malformed, low-confidence, or failed route also leaves the accepted implementation pending instead of silently falling back to another profile. +A pinned profile bypasses automatic routing and implements the plan in place. +Pin a profile before planning when its guardrails must apply during planning itself. + +`submit_plan` ends the planning turn and creates a Markdown plan artifact. +The artifact remains pending in the session until you implement or discard it. +Collaboration-mode changes, submitted plans, and their resolutions are persisted in the session, so the active branch restores the correct mode, pending plan, or accepted implementation handoff after resume or tree navigation. + +The `--plan` startup flag is supported only in interactive mode. +Diffex rejects it in print, JSON, and RPC modes because those modes cannot resolve a pending plan. +The same restriction applies when a resumed session's active branch is in plan mode or has an accepted implementation waiting to start. + ## Message Queue You can submit messages while the agent is still working: @@ -200,8 +229,11 @@ See [Diffex Packages](packages.md) for package sources and security notes. | `-p`, `--print` | Print response and exit | | `--mode json` | Output all events as JSON lines; see [JSON mode](json.md) | | `--mode rpc` | RPC mode over stdin/stdout; see [RPC mode](rpc.md) | +| `--plan` | Start interactive mode with native plan mode active | | `--export [out]` | Export a session to HTML | +Do not combine `--plan` with `-p`, `--mode json`, or `--mode rpc`. + In print mode, Diffex also reads piped stdin and merges it into the initial prompt: ```bash @@ -238,9 +270,11 @@ cat README.md | diffex -p "Summarize this text" | `--tools `, `-t ` | Allowlist specific built-in, extension, and custom tools | | `--exclude-tools `, `-xt ` | Disable specific built-in, extension, and custom tools | | `--no-builtin-tools`, `-nbt` | Disable built-in tools but keep extension/custom tools enabled | -| `--no-tools`, `-nt` | Disable all tools | +| `--no-tools`, `-nt` | Disable all normal-mode tools and plan-mode inspection tools | -Built-in tools: `read`, `bash`, `edit`, `write`, `update_plan`, `grep`, `find`, `ls`. +Normal-mode built-in tools: `read`, `bash`, `edit`, `write`, `update_plan`, `grep`, `find`, `ls`. +Plan mode exposes `submit_plan` plus the permitted canonical subset of `read`, `grep`, `find`, and `ls`. +Tool allowlists and denylists constrain the inspection subset, but `submit_plan` remains available as an intrinsic plan-mode tool. ### Resource Options @@ -295,6 +329,9 @@ diffex @code.ts @test.ts "Review these files" # Interactive with initial prompt diffex "List all .ts files in src/" +# Interactive read-only planning +diffex --plan "Design the implementation before changing anything" + # Non-interactive diffex -p "Summarize this codebase" @@ -327,6 +364,6 @@ diffex --exclude-tools ask_question Diffex keeps the core small and pushes workflow-specific behavior into extensions, skills, prompt templates, and packages. -It intentionally does not include built-in MCP, permission popups, full plan mode, persistent to-dos, or background bash. The built-in `update_plan` tool is only a lightweight progress checklist. You can build or install richer workflows as extensions or packages, or use external tools such as containers and tmux. +It includes a constrained native plan mode and session-scoped sub-agents while leaving MCP, permission popups, persistent to-dos, and background bash to extensions, packages, or external tools such as containers and tmux. The built-in `update_plan` tool remains a lightweight progress checklist. For the full rationale, read the [blog post](https://mariozechner.at/posts/2025-11-30-pi-coding-agent/). diff --git a/packages/coding-agent/examples/extensions/README.md b/packages/coding-agent/examples/extensions/README.md index 404c2ac21..c93d7e3f4 100644 --- a/packages/coding-agent/examples/extensions/README.md +++ b/packages/coding-agent/examples/extensions/README.md @@ -49,7 +49,7 @@ cp permission-gate.ts ~/.diffex/agent/extensions/ | Extension | Description | |-----------|-------------| | `preset.ts` | Named presets for model, thinking level, tools, and instructions via `--preset` flag and `/preset` command | -| `plan-mode/` | Claude Code-style plan mode for read-only exploration with `/plan` command and step tracking | +| `plan-mode/` | Legacy example that is incompatible with native plan mode and retained only for historical reference; do not load it | | `tools.ts` | Interactive `/tools` command to enable/disable tools with session persistence | | `handoff.ts` | Transfer context to a new focused session via `/handoff ` | | `qna.ts` | Extracts questions from last response into editor via `ctx.ui.setEditorText()` | diff --git a/packages/coding-agent/examples/extensions/plan-mode/README.md b/packages/coding-agent/examples/extensions/plan-mode/README.md index 2568a684b..9c4d40a8d 100644 --- a/packages/coding-agent/examples/extensions/plan-mode/README.md +++ b/packages/coding-agent/examples/extensions/plan-mode/README.md @@ -1,8 +1,13 @@ -# Plan Mode Extension +# Legacy Plan Mode Extension + +This historical example predates native plan mode and is incompatible with current Diffex. +It reuses the now-reserved `--plan` and `/plan` interfaces, so do not load it. +Use the native plan mode documented in [Usage](../../../docs/usage.md#plan-mode). +The remaining sections describe the legacy extension's historical behavior only. Read-only exploration mode for safe code analysis. -## Features +## Historical Features - **Built-in write tools disabled**: Disables edit/write while preserving other active tools - **Bash allowlist**: Only read-only bash commands are allowed @@ -11,13 +16,13 @@ Read-only exploration mode for safe code analysis. - **[DONE:n] markers**: Explicit step completion tracking - **Session persistence**: State survives session resume -## Commands +## Historical Commands - `/plan` - Toggle plan mode - `/todos` - Show current plan progress - `Ctrl+Alt+P` - Toggle plan mode (shortcut) -## Usage +## Historical Usage 1. Enable plan mode with `/plan` or `--plan` flag 2. Ask the agent to analyze code and create a plan @@ -34,7 +39,7 @@ Plan: 5. During execution, the agent marks steps complete with `[DONE:n]` tags 6. Progress widget shows completion status -## How It Works +## Historical Implementation ### Plan Mode (Read-Only) - Built-in edit/write tools disabled