Skip to content

feat: model switching and plan mode (Steps 7-8)#4

Merged
pertrai1 merged 7 commits into
mainfrom
feature/model-switching
Apr 18, 2026
Merged

feat: model switching and plan mode (Steps 7-8)#4
pertrai1 merged 7 commits into
mainfrom
feature/model-switching

Conversation

@pertrai1

@pertrai1 pertrai1 commented Apr 18, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the remaining Step 7 and all Step 8 items from ROADMAP.md:

Model Switching (Step 7)

  • /model <id> command to switch models mid-session
  • /model with no args shows current model
  • Configurable default model via .ai-agent/config.json

Plan Mode (Step 8)

  • /plan activates read-only plan mode — mutating tools are denied via isToolDenied callback
  • /plan off deactivates — full tool access restored
  • [plan] > prompt indicator when active
  • Planner system prompt instructs model to produce structured plans
  • Post-response approval flow: approve, reject, or modify the plan
  • All 7 Step 8 ROADMAP items marked complete

Changes

  • src/repl.ts — plan mode state, dynamic prompt, tool denial, approval flow
  • src/agent.tsisToolDenied callback in AgentLoopOptions
  • src/repl/commands.ts/model and /plan command handlers
  • Tests: 25 new tests across 4 test files (408 total passing)
  • OpenSpec: full artifact workflow (proposal → design → specs → tasks → verify → archive)
  • Docs: Socratic Journal, FOR-Rob-Simpson, MANUAL_TESTING, README

Summary by cubic

Adds model switching and a new Plan Mode to the REPL. You can switch models mid-session and safely plan changes with mutating tools disabled; plan approval now executes immediately, and rejection/edits re-run planning right away.

  • New Features

    • Model switching: /model <id> to switch; /model shows current; default via .ai-agent/config.json.
    • Plan Mode: /plan on, /plan off off; prompt shows [plan] >; /status displays mode.
    • Tool safety in Plan Mode: denies write_file, edit_file, and bash via new isToolDenied callback.
    • Planner prompt: system prompt appends a planning appendix while in Plan Mode.
    • Plan approval: approve with y (exits Plan Mode and executes immediately), reject with n, or type edits to revise; rejection/edits re-run planning immediately.
    • Agent API: added isToolDenied to AgentLoopOptions, checked before permission flow.
    • Specs/tests/docs updated: added plan-mode spec, repl/tool-permissions deltas, ROADMAP updates.
  • Bug Fixes

    • Approval flow now re-runs the agent loop immediately after approval, rejection, or feedback (no extra user step required).

Written for commit 52753ac. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced "Plan Mode" — toggle via /plan and /plan off slash commands to enable read-only planning without code mutations.
    • Agent generates ordered, step-by-step plans in plan mode while denying mutating tool calls.
    • Interactive approval workflow: approve plans to proceed with execution, or reject/modify to revise.
    • Plan mode status now displayed in /status output.
  • Documentation

    • Added comprehensive plan-mode specifications, manual testing guidance, and architectural design docs.

pertrai1 and others added 6 commits April 18, 2026 11:21
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Add plan mode toggle (/plan, /plan off) that denies mutating tools
(write_file, edit_file, bash) via isToolDenied callback, appends a
planner-oriented system prompt, and prompts the user to approve,
reject, or modify plans before execution. Updates /status to show
plan mode state and changes prompt to [plan] > when active.
- Sync repl-chat-loop delta: add plan mode toggle commands and scenarios
- Sync tool-permissions delta: add isToolDenied callback requirement
- Add plan-mode as new main spec
- Archive step-8-plan-mode change to openspec/changes/archive/
@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR implements "plan mode," a new REPL operational state where the agent acts as an architect to generate ordered action plans before execution. The agent loop gains tool-denial capability via an isToolDenied callback evaluated before existing permissions, the REPL manages plan mode state with /plan slash commands, and user approval is required to transition from planning to execution.

Changes

Cohort / File(s) Summary
Roadmap & Status
ROADMAP.md
Marks seven Step 8 plan-mode tasks as completed (tool denial switch, planner prompt, approval flow, plan execution, manual tests).
Documentation
docs/FOR-Rob-Simpson.md, docs/MANUAL_TESTING.md, docs/SOCRATIC_JOURNAL.md
Adds plan-mode design narrative, slash command reference updates, and Q&A entries describing the tool denial callback, REPL state separation, and approval workflow.
OpenSpec Archive & Spec Files
openspec/changes/archive/2026-04-18-step-8-plan-mode/*, openspec/specs/plan-mode/spec.md, openspec/specs/repl-chat-loop/spec.md, openspec/specs/tool-permissions/spec.md
Defines plan-mode specification with slash-command behavior, tool-denial callback contract, system-prompt augmentation, approval flow, and REPL state requirements; archives design rationale and proposal documents.
Agent Loop Tool Denial
src/agent.ts
Adds optional isToolDenied callback to AgentLoopOptions and checks it before existing permission logic; denies tools with a plan-mode error message when callback returns true.
REPL Plan Mode State & Workflow
src/repl.ts
Introduces planMode boolean state, dynamic prompt switching ([plan] > in plan mode), conditional system-prompt augmentation with PLAN_MODE_PROMPT, and approval workflow (approve exits plan mode; reject/modify stays in plan mode with user feedback appended).
Slash Command Extensions
src/repl/commands.ts
Extends HandleSlashCommandOptions with getPlanMode() and setPlanMode(), adds /plan and /plan off commands, and updates /status to display plan-mode state.
Test Coverage
src/__tests__/agent-plan-mode.test.ts, src/__tests__/commands-plan.test.ts, src/__tests__/model-switching.test.ts, src/__tests__/step7-spec-anchors.test.ts
New tests verify isToolDenied callback precedence, plan-mode slash command behavior, and status output; existing test helpers updated to include plan-mode properties.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant REPL
    participant AgentLoop as Agent Loop
    participant ToolExecutor as Tool Executor
    
    User->>REPL: /plan (or message in plan mode)
    REPL->>REPL: Set planMode = true<br/>Augment system prompt<br/>Set up isToolDenied callback
    REPL->>AgentLoop: runAgentLoop(options with isToolDenied)
    
    Note over AgentLoop: Model generates plan<br/>or attempts tool call
    
    alt Tool called in plan mode
        AgentLoop->>AgentLoop: isToolDenied(toolName) → true
        AgentLoop->>ToolExecutor: ✗ Deny (plan mode)
        ToolExecutor-->>AgentLoop: Error result
    else Read-only tool
        AgentLoop->>AgentLoop: isToolDenied(toolName) → false
        AgentLoop->>ToolExecutor: Execute read-only tool
        ToolExecutor-->>AgentLoop: Tool result
    end
    
    AgentLoop-->>REPL: Text response (plan)
    REPL->>User: Display plan<br/>Prompt for approval
    
    alt User approves (y)
        User->>REPL: y
        REPL->>REPL: planMode = false<br/>Clear tool denial<br/>Append execution instruction
        REPL->>AgentLoop: runAgentLoop(normal mode)
        AgentLoop->>ToolExecutor: Execute approved plan
        ToolExecutor-->>AgentLoop: Results
        AgentLoop-->>REPL: Completion
    else User rejects/modifies
        User->>REPL: n or feedback text
        REPL->>REPL: planMode = true (stay)<br/>Append feedback as user msg
        REPL->>AgentLoop: runAgentLoop(plan mode)<br/>with revised context
        AgentLoop-->>REPL: Revised plan
        REPL->>User: Display revised plan
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

Poem

🐰 A plan mode blooms, dear architect's delight,
No hammers swinging till the user says "right!"
Deny those mutations, approve before change,
We hop through the loop in a thoughtful exchange.
With [plan] > gleaming, we prance through the code. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: model switching and plan mode (Steps 7-8)' directly reflects the two main features implemented in the changeset: model switching and plan mode, corresponding to Steps 7 and 8 from the ROADMAP.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/model-switching

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 21 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/repl.ts">

<violation number="1" location="src/repl.ts:222">
P2: Approving a plan does not continue execution immediately; it only queues a message and waits for another user input before implementation starts.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/repl.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c881bdc25b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/repl.ts
Comment on lines +223 to +227
messages.push({
role: "user",
content: [{ type: "text", text: "Plan approved. Please proceed with implementation." }],
});
tokenTracker.addMessage();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Re-run agent immediately after plan approval input

In startRepl, the plan-mode approval handler only appends a synthetic user message (messages.push(...)) and then falls back to the outer REPL loop without calling runAgentLoop again. This means y does not actually trigger execution of the approved plan, and free-text feedback does not trigger an immediate revised plan; both require an extra user prompt, which breaks the intended approval/revision flow.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements ROADMAP Step 7–8 features for the interactive REPL: switching LLM models mid-session and introducing a read-only “plan mode” that blocks mutating tools and adds a post-response approval step.

Changes:

  • Add /model command support (show current / switch to new model) and expose model state via REPL callbacks.
  • Add plan mode (/plan, /plan off) with prompt indicator, planner system-prompt appendix, and mutating-tool denial via a new isToolDenied agent-loop option.
  • Add unit tests plus OpenSpec/docs/roadmap updates to capture the new workflows.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/repl/commands.ts Adds /plan + /plan off handlers and extends /status output with plan-mode state.
src/repl.ts Introduces planMode state, dynamic prompt, planner system-prompt appendix, tool-denial wiring, and plan approval prompt.
src/agent.ts Adds isToolDenied callback to override tool permissions before normal permission flow.
src/tests/step7-spec-anchors.test.ts Updates command option mocks to include plan-mode callbacks.
src/tests/model-switching.test.ts Updates command option mocks to include plan-mode callbacks.
src/tests/commands-plan.test.ts Adds tests for /plan, /plan off, and /status plan-mode display behavior.
src/tests/agent-plan-mode.test.ts Adds tests verifying isToolDenied blocks tools and runs before permission checks.
openspec/specs/tool-permissions/spec.md Specifies isToolDenied override semantics and ordering.
openspec/specs/repl-chat-loop/spec.md Extends REPL spec to include plan-mode commands, prompt indicator, and status output.
openspec/specs/plan-mode/spec.md Adds the full plan-mode behavior spec (toggle, denial, prompt appendix, approval flow).
openspec/changes/archive/2026-04-18-step-8-plan-mode/tasks.md Archives the step’s task checklist.
openspec/changes/archive/2026-04-18-step-8-plan-mode/specs/tool-permissions/spec.md Archived spec delta for tool-permissions changes.
openspec/changes/archive/2026-04-18-step-8-plan-mode/specs/repl-chat-loop/spec.md Archived spec delta for repl-chat-loop changes.
openspec/changes/archive/2026-04-18-step-8-plan-mode/specs/plan-mode/spec.md Archived plan-mode spec addition.
openspec/changes/archive/2026-04-18-step-8-plan-mode/proposal.md Archives the plan-mode proposal rationale/scope.
openspec/changes/archive/2026-04-18-step-8-plan-mode/design.md Archives the design decisions and integration points.
openspec/changes/archive/2026-04-18-step-8-plan-mode/.openspec.yaml Adds OpenSpec archive metadata for the change set.
docs/SOCRATIC_JOURNAL.md Documents architectural reasoning for plan mode and isToolDenied.
docs/MANUAL_TESTING.md Adds manual test procedures and updates REPL command list.
docs/FOR-Rob-Simpson.md Adds narrative/architecture write-up for Step 8 plan mode.
ROADMAP.md Marks Step 8 plan-mode items complete.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/repl.ts
Comment on lines +215 to +243
if (planMode) {
const approval = await rl.question(
chalk.yellow("\n📋 Approve this plan? (y to approve / n to reject / or type modifications): "),
);
const approvalTrimmed = approval.trim().toLowerCase();

if (approvalTrimmed === "y") {
planMode = false;
messages.push({
role: "user",
content: [{ type: "text", text: "Plan approved. Please proceed with implementation." }],
});
tokenTracker.addMessage();
shouldPersistSession = true;
} else if (approvalTrimmed === "n") {
messages.push({
role: "user",
content: [{ type: "text", text: "Plan rejected. Please revise the plan." }],
});
tokenTracker.addMessage();
shouldPersistSession = true;
} else {
messages.push({
role: "user",
content: [{ type: "text", text: `Plan feedback: ${approval.trim()}` }],
});
tokenTracker.addMessage();
shouldPersistSession = true;
}

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the plan approval prompt, the code only appends a user message (e.g. "Plan approved. Please proceed with implementation.") but does not re-enter runAgentLoop automatically. This means approving a plan won't actually execute anything until the user types another input line, which contradicts the spec/docs that say implementation should begin immediately after y. Consider looping back into runAgentLoop right after recording the approval (or treating the approval as the next user input and skipping the next rl.question).

Copilot uses AI. Check for mistakes.
Comment thread src/repl.ts
promptForApproval,
tokenTracker,
isToolDenied: planMode
? (toolName: string) => MUTATING_TOOLS.has(toolName)

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plan mode currently denies only direct tool calls to write_file, edit_file, and bash. Because subagent is still allowed and subagents run runAgentLoop without promptForApproval (and without an isToolDenied hook), approving a subagent tool call during plan mode can still mutate files or run bash, bypassing the read-only guarantee. Consider propagating the plan-mode denial callback into subagent execution (or denying subagent while plan mode is active) so plan mode cannot be circumvented via subagents.

Suggested change
? (toolName: string) => MUTATING_TOOLS.has(toolName)
? (toolName: string) => toolName === "subagent" || MUTATING_TOOLS.has(toolName)

Copilot uses AI. Check for mistakes.
…back

Approval now triggers execution right away instead of waiting for
the next user input. Rejection and feedback re-run in plan mode to
produce a revised plan immediately.
@pertrai1

Copy link
Copy Markdown
Owner Author

Thanks for the reviews! Fixed in 52753ac — plan approval (y), rejection (n), and feedback all now re-enter runAgentLoop immediately instead of waiting for the next user input.

Re: the subagent bypass suggestion — not applicable to this codebase. There is no subagent tool; the project has exactly 6 tools (read_file, write_file, edit_file, glob, grep, bash). That's something to keep in mind if/when subagents are added later.

@pertrai1
pertrai1 merged commit 3e50054 into main Apr 18, 2026
3 of 4 checks passed
@pertrai1
pertrai1 deleted the feature/model-switching branch April 18, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants