Skip to content

feat: add interactive questions system for plan mode#641

Open
akramcodez wants to merge 3 commits into
Nano-Collective:mainfrom
akramcodez:feature/interactive-plan-questions
Open

feat: add interactive questions system for plan mode#641
akramcodez wants to merge 3 commits into
Nano-Collective:mainfrom
akramcodez:feature/interactive-plan-questions

Conversation

@akramcodez

@akramcodez akramcodez commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Closes #96

Description

Enhances the existing Plan Mode (Shift+Tab+Tab) with an Interactive Questions System. Before generating a final implementation plan, the AI will now proactively pause and ask clarifying questions if it detects ambiguity, missing requirements, or needs to make an architectural decision. This reduces rework and ensures the generated plan perfectly aligns with user expectations.

Key updates:

  • Added usePlanClarification.ts hook for contextual AI question generation.
  • Added PlanReviewState to useAppState.tsx to handle the clarification UI lifecycle.
  • Intercepted plan mode submission flow in useAppHandlers.tsx to prompt users with interactive questions.
  • Expanded interactive test suites and fixed test environments to support the new state and logic.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Testing

Automated Tests

  • New features include passing tests in .spec.ts/tsx files
  • All existing tests pass (pnpm test:all completes successfully)
  • Tests cover both success and error scenarios

Manual Testing

  • Tested with Ollama
  • Tested with OpenRouter
  • Tested with OpenAI-compatible API
  • Tested MCP integration (if applicable)

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Documentation updated (if needed)
  • No breaking changes (or clearly documented)
  • Appropriate logging added using structured logging (see CONTRIBUTING.md)

@akramcodez akramcodez requested a review from will-lamerton as a code owner July 7, 2026 10:47
Copilot AI review requested due to automatic review settings July 7, 2026 10:47
@akramcodez akramcodez requested a review from Avtrkrb as a code owner July 7, 2026 10:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@will-lamerton

Copy link
Copy Markdown
Member

Hey @akramcodez - Really nice work on this - the code quality is high across the board. Types are clean, the new module is properly unit-tested, and the ask_user upgrade (rich options with pros/cons plus the questionType badges) is a genuinely good enhancement that I'd want to keep regardless of where the rest lands. You clearly read the existing plan-mode plumbing carefully before extending it.

I want to talk through the overall approach before we merge, because I think there's a simpler design hiding in here that would serve issue #96 better. None of this is a knock on the execution - it's a direction question.

The thing I keep coming back to: we've ended up with two question systems that don't know about each other.

There's the deterministic keyword engine (clarification-questions.tsusePlanClarificationPlanReviewPrompt), and there's the LLM-driven path (the new plan-mode prompt sections telling the model to call ask_user before exploring). Both fire in plan mode. So for a message like "add auth", the keyword engine asks "what auth approach?" up front, and then the model - following the prompt - may ask about auth again once the message reaches it. A user could get interrogated twice about the same thing, and nothing coordinates the two.

On the deterministic engine specifically, a few things worry me for the long run:

  • It can't see the codebase, so it asks things the model could answer by reading files. "add a db migration" triggers "which database?" even in a repo that's obviously already on Postgres. That's the exact behaviour asking-questions-plan.md tells the model to avoid, so the two halves of the PR are pulling in opposite directions.
  • The matching is substring-based, so it over-fires. "api" matches inside unrelated words, and "test" / "service" / "session" show up everywhere. Lots of false positives on the happy path.
  • The pros/cons cards (JWT vs session, Postgres vs Mongo) are hardcoded and will drift out of date, and the taxonomy of "what's worth asking about" will always be incomplete - it's a fixed list where the model generalises.

A couple of smaller things I noticed in the PlanReviewPrompt layer (these are what convinced me the layer is fighting the grain rather than one-off bugs):

  • The bar renders "📋 Plan ready" but it actually shows before the message reaches the AI, so no plan exists yet at that point.
  • "Proceed" is described as "switch to normal mode and implement," but the handler stays in plan mode and just appends context - it never calls setDevelopmentMode, so nothing gets implemented.
  • "Ask more" and "Proceed" take the identical branch in useAppHandlers, so "Ask more" doesn't currently ask anything more.

I don't want you to go fix those three line by line, because I think they're symptoms of the deterministic layer being the wrong shape rather than defects to patch.


Here's the approach I'd like us to take instead. The insight is to lean entirely on the model plus ask_user (which you've already made great), and map #96 onto the natural lifecycle of a plan-mode turn rather than adding a pre-flight stage in front of it. Three moments:

1. User submits → model clarifies. This is #96's ambiguity and decision questions. Pure prompt plus ask_user. The model reads just enough to know what's genuinely ambiguous, then asks up to 3 critical questions, using your rich options with pros/cons for decision-type questions. Because the model can read the codebase, it won't ask about things that are already settled. Your asking-questions-plan.md and the step-0 change already do this - that part stays.

2. Model explores and writes the plan, ending with an explicit "Assumptions I'm making" section. This is #96's assumption confirmation, expressed as plan content rather than a separate question type. One extra line in task-approach-plan.md gets us there.

3. Plan is presented -> action bar. Keep PlanReviewPrompt, but fire it when the plan-mode turn actually completes (we already have that signal via isConversationComplete while in plan mode), and make the buttons real:

  • Proceed -> actually switch to normal mode and dispatch "proceed with the plan above" so it executes with the plan in context.
  • Ask more -> stay in plan mode and dispatch "ask me any additional clarifying questions," so the model calls ask_user again (now genuinely distinct from Proceed).
  • Modify -> dismiss and let the user refine.

That covers all three of #96's question types plus the action bar from the mockups, with the model doing in context what the keyword table was approximating. Net effect: we keep the ask_user upgrade, the prompt sections, and the new question-queue types, and we can delete clarification-questions.ts, usePlanClarification.ts, types/plan.ts and their specs - roughly 1,000 of the added lines, and all three of the issues above go with them.

One honest tradeoff to flag: the deterministic path guaranteed a question appeared before any tokens were spent, whereas this leans on the model following the prompt. For strong models that's reliable; a weak local model might jump straight to a plan. If we ever want a hard guarantee, the lever is prompt strength (or a light "did you consider asking?" nudge), not a static table.

If you're up for it, I'd love for you to take a pass at the slimmed-down version - it's mostly deletion plus rewiring PlanReviewPrompt to the completion signal and making those three handlers do real work. Happy to pair on the wiring if useful. Genuinely good instincts on this feature, I just want to get the shape right before it lands.

@will-lamerton

Copy link
Copy Markdown
Member

Hey @akramcodez!

This is a big improvement - thank you for taking the earlier feedback on board. Dropping the keyword engine and leaning on the model plus ask_user is exactly the direction I was hoping for, and re-pointing PlanReviewPrompt at turn-completion with Proceed and Ask more doing real, distinct work is spot on. The overall shape is now right.

I did find two blocking issues in the new wiring and one unrelated regression before it's ready to merge. None are conceptual - just the rewiring being a bit fresh.

  • Modify and Dismiss don't actually dismiss the bar. The trigger effect in interactive-app.tsx re-runs whenever planReviewState goes back to null, and handlePlanModify only nulls planReviewState - it doesn't change the mode or reset isConversationComplete. So the effect's condition (isConversationComplete && developmentMode === 'plan' && !planReviewState) is immediately true again and the bar pops straight back. Only Proceed and Ask more can currently escape it. Suggest gating the effect to fire once per completed turn - e.g. a ref that records the message count or turn it last fired for, or a dismissed flag that Modify sets and a new submit clears.

  • While the bar is showing, the text input is still live underneath it. PlanReviewPrompt is now rendered as a sibling of ChatInput, and ChatInput no longer receives any plan-review prop, so it keeps rendering UserInput. Both register useInput, so keystrokes hit both - typing "m"/"a"/"p" triggers the bar's actions and types into the box at the same time. The earlier version had this as an exclusive branch inside the chat-input render so only one input was ever active. I'd move it back into that region (or hide UserInput while planReviewState.show is true).

  • Unrelated regression: /rename is broken on this branch. The trailing .slice(1) was removed from the commandArgs parsing in handleMessageSubmit, so the command name is no longer stripped. /rename my session now yields ['rename','my','session'] and app-util.ts joins that into the session name "rename my session". main still has the slice - looks like an accidental deletion, worth restoring.

A few smaller things while you're in here:

  • Some unrelated changes seem to have ridden along in the diff (mcp-config-loader.ts gaining a NANOCODER_IGNORE_PROJECT_CONFIG branch, file-watcher.spec.ts timeout bumped to 5000, a model-selector.spec.ts tweak). If they're not part of Feature Request: Interactive Questions System for Plan Mode #96, worth pulling them out so the PR stays focused.
  • The plan-review-prompt.spec.tsx was removed with the cleanup and nothing replaced it, so the new trigger effect and the proceed/askMore/modify handlers are untested. A couple of tests here would have caught both blockers above - worth adding.
  • Minor: after Ask more, originalMessage can end up capturing the synthetic "please ask me any additional clarifying questions" text, which then shows up in the Proceed message as "proceed with the plan above for: please ask me...". Not urgent, just a bit confusing.

Really solid turnaround on the design though - once the dismiss loop and the double-input are sorted, I think this is close.

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.

Feature Request: Interactive Questions System for Plan Mode

3 participants