Skip to content

feat: upstream backports — MCP reconnect, compress fixes, hooks cleanup, follow-up suggestions#19

Merged
mabry1985 merged 16 commits into
mainfrom
feat/upstream-backports
Apr 3, 2026
Merged

feat: upstream backports — MCP reconnect, compress fixes, hooks cleanup, follow-up suggestions#19
mabry1985 merged 16 commits into
mainfrom
feat/upstream-backports

Conversation

@mabry1985

@mabry1985 mabry1985 commented Apr 3, 2026

Copy link
Copy Markdown

Summary

Backports 10 high-value upstream items from QwenLM/qwen-code into protoCLI.

What's included

Item Why
MCP auto-reconnect Agents using MCP tools no longer die on transient server flaps; auto-retry x3
Compress: orphaned funcCall fix /compress no longer corrupts history when agent was interrupted mid-tool-call
Compress: tool-heavy split point Better split point selection when history is full of tool calls
PTY FD leak @lydell/node-pty 1.2.0-beta.10 fixes file descriptor leak in long shell sessions
Kitty IME timeout 200ms buffer prevents stalled keyboard sequences from mangling input
Hooks cleanup Remove --experimental-hooks flag (hooks are stable); fix abort listener leak
npm extension install proto /extension install npm:package-name
Web fetch in plan mode WebFetch now shows approval dialog in plan mode instead of silent block
Follow-up suggestions Post-response suggested prompts in CLI

Skipped

  • channels/ (Telegram, WeChat, DingTalk) — Qwen-specific
  • VSCode ACP reconnect — we removed VSCode
  • Qwen3.6-Plus model support

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Follow-up suggestions: ghost-text next-input predictions (accept with Tab/Enter/Right, dismiss by typing); background speculation to pre-run suggestions.
    • CLI: new mcp reconnect command; npm-based extension install with --registry.
    • Fast-model option to pick a dedicated model for background tasks.
  • Improvements

    • Global "disableAllHooks" toggle with user-facing disabled-hooks UI and messaging.
    • Task updates show compact before→after diffs.
  • Documentation

    • New docs for follow-up suggestions, speculation, npm extension publishing, settings, and commands.

LaZzyMan and others added 12 commits April 3, 2026 12:59
…ions

When conversation history is near the context window limit and dominated by
tool call/response cycles, findCompressSplitPoint would return a near-zero
split point because it only considered non-functionResponse user messages as
valid split points. This caused /compress to send almost no history to the
compression API (e.g. 29 tokens), producing a useless summary that inflated
token count instead of reducing it.

Changes:
- Track tool completion boundaries (positions after functionResponse) as
  fallback split points in findCompressSplitPoint
- Add user-with-functionResponse to the compress-everything safety check
- Use Math.max of primary and fallback split points for better coverage
- Add minimum content guard (5% threshold) to prevent futile API calls
- Add 4 new test cases covering tool-heavy conversation scenarios

Fixes QwenLM#2647
…heavy conversations

- Strip trailing orphaned funcCall (force=true) before split point calculation,
  so normal compression logic runs cleanly on the remaining history instead of
  requiring ad-hoc special-casing
- Remove redundant lastToolCompletionSplitPoint machinery: after fixing the
  i+2 index bug, lastSplitPoint already subsumes it, making Math.max redundant
- Add MIN_COMPRESSION_FRACTION constant (0.05) to guard against futile API
  calls when historyToCompress is too small relative to total history
- Add tests for orphaned funcCall handling (force=true compresses, force=false NOOP)
- Add test for MIN_COMPRESSION_FRACTION guard

Fixes QwenLM#2647
The previous version (1.1.0) has a native-level bug on macOS where each
PTY spawn leaks one /dev/ptmx file descriptor that is never closed. Over
a long session with hundreds of shell commands, this exhausts the
system-wide PTY pool (kern.tty.ptmx_max = 511), breaking other programs
like tmux and new terminal windows.

Root cause: microsoft/node-pty#882

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The error handler in hookRunner cleared the timeout but did not remove
the abort signal listener, unlike the close handler. When spawn fails
(e.g. executable not found), only the error event fires — the close
event is not guaranteed — so the abort listener leaked on the signal.
- Add new npm extension installation channel via scoped packages (@scope/name)
- Implement npm.ts module with registry resolution, authentication, and download logic
- Support version pinning, dist-tags (latest, beta), and custom registries
- Handle private registry auth via NPM_TOKEN env var and .npmrc _authToken entries
- Update CLI install command with --registry flag for npm extensions
- Add comprehensive tests for npm package parsing and registry operations
- Update documentation for releasing and installing from npm registries
- Integrate npm updates into extension manager and update checking flow

This enables teams using npm for package distribution to publish Qwen Code extensions through their existing infrastructure, with full support for private registries and access control.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
- Restore lsp field to CliArgs interface (removed by hooks cleanup cherry-pick)
- Add lsp: undefined to auth handler and gemini test fixtures
- Resolve client.ts conflict: keep both endTurnSpan and cache-safe param saving
- Fix speculationToolGate: ToolNames.TODO_WRITE → ToolNames.TASK_LIST
- mcp-tool: replace getStringifiedResultForDisplay with getDisplayFromParts,
  remove unused allowlist field and truncateTextParts method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a follow-up suggestion and speculative execution subsystem (generation, cache-aware forked queries, filtering, overlay FS, tool-gating, speculation lifecycle), CLI/Web UI integration (ghost-text UX, fast-model), npm extension support, hooks opt-out semantics, MCP reconnect command and retries, telemetry events, many tests, and documentation.

Changes

Cohort / File(s) Summary
Design & User Docs
docs/design/prompt-suggestion/*.md, docs/superpowers/*, docs/users/features/*, docs/users/configuration/settings.md, docs/users/extension/*, docs/users/overview.md
Added design, implementation, and user-facing docs for followup suggestions, speculation, new settings (followup/cache/speculation/fastModel), npm extension publishing/install, and task-update diff UI.
Followup Core (new subsystem)
packages/core/src/followup/*, packages/core/src/followup/{followupState.ts,suggestionGenerator.ts,forkedQuery.ts,overlayFs.ts,speculation.ts,speculationToolGate.ts,index.ts}
New followup subsystem: followup controller/state, NES suggestion generation and filters, cache-safe forked queries, OverlayFs, speculation engine (start/accept/abort), tool gate and helpers, and entry exports.
Followup Tests & Smoke
packages/core/src/followup/*.{test.ts,smoke.test.ts}
Comprehensive unit and integration tests for followup controller, filters, forked queries, overlay FS, speculation, tool gating, and integration smoke scenarios.
Telemetry & Types
packages/core/src/telemetry/{constants.ts,loggers.ts,types.ts}, packages/core/src/index.ts
Added telemetry constants, event types and loggers for prompt suggestions and speculation; exported telemetry and followup APIs from core entrypoint.
CLI Integration & UI wiring
packages/cli/src/ui/*, packages/cli/src/ui/components/{InputPrompt.tsx,Composer.tsx,DialogManager.tsx,ModelDialog.tsx}, packages/cli/src/ui/hooks/{useFollowupSuggestions.tsx,useModelCommand.ts,slashCommandProcessor.ts}, packages/cli/src/ui/commands/modelCommand.ts, packages/cli/src/ui/hooks/useFollowupSuggestions.tsx, packages/cli/src/ui/contexts/UIStateContext.tsx, packages/cli/src/ui/AppContainer.tsx
Wired followup lifecycle into CLI UI/state: promptSuggestion state, accept/dismiss controls (Tab/Enter/Right), timing (300ms), telemetry hooks, fast-model dialog flow, and speculation start/accept/abort integration.
CLI Input Components & Tests
packages/cli/src/ui/components/{InputPrompt.tsx,InputPrompt.test.tsx,Composer.tsx}, packages/cli/src/ui/components/hooks/*, packages/cli/src/ui/components/hooks/*.{test.tsx}
InputPrompt now supports promptSuggestion props, accept/dismiss keyboard behavior, paste/typing dismissal; added tests and HooksDisabled UI/tests.
Web UI Integration
packages/webui/src/hooks/useFollowupSuggestions.ts, packages/webui/src/followup.ts, packages/webui/src/components/layout/InputForm.tsx, packages/webui/src/styles/components.css, packages/webui/vite.config.followup.ts, packages/webui/package.json
Added Web UI followup hook, InputForm followup props/behavior, CSS for ghost-text, dedicated followup build and package subpath export to avoid forcing core dependency.
Settings & Config Schema
packages/cli/src/config/settingsSchema.ts, packages/cli/src/config/config.ts, docs/users/configuration/settings.md
Added settings: ui.enableFollowupSuggestions, ui.enableCacheSharing, ui.enableSpeculation, top-level fastModel; replaced hooksConfig with disableAllHooks opt-out.
Hook System & UI Changes
packages/core/src/*, packages/cli/src/*, packages/cli/src/ui/components/hooks/*, packages/core/src/hooks/*
Inverted hook gating to use disableAllHooks (negated) and removed per-hook disabled-list filtering; always include hooks command; added HooksDisabledStep UI, updated registry/runner behavior, and tests.
Extensions: npm support
packages/core/src/extension/{npm.ts,marketplace.ts,github.ts,index.ts}, packages/core/src/extension/*.test.ts, packages/cli/src/commands/extensions/install.ts
Added npm registry extension support: scoped package parsing, registry resolution/auth precedence, tarball download/extract, update checking, CLI install wiring (registry option), tests, and ExtensionInstallMetadata.registryUrl.
MCP reconnect & Tool retry
packages/cli/src/commands/mcp/reconnect.ts, packages/cli/src/commands/mcp/*, packages/core/src/tools/mcp-tool.ts, tests
Added mcp reconnect command and MCP tool auto-reconnect/retry handling with tests; adjusted tool invocation retry logic and output truncation behavior.
Chat Compression & Tools
packages/core/src/services/chatCompressionService.ts, packages/core/src/tools/tools.ts, docs/superpowers/specs/*
Adjusted compression to handle orphaned function-calls, added minimum-compressible fraction, and introduced TaskUpdateDiffDisplay result type (spec/docs).
I18n & System Info
packages/cli/src/i18n/locales/*, packages/cli/src/utils/systemInfo.ts, packages/cli/src/utils/systemInfoFields.*
Added translations for Fast Model and hooks-disabled messaging; surfaced fastModel in system info and tests.
Config/API Surface
packages/core/src/config/config.ts, packages/cli/src/config/config.ts, packages/core/src/index.ts
Config changes: added disableAllHooks and registryUrl support for npm installs; removed enableHooks/hooksConfig; exported followup APIs and telemetry loggers from core.
Build & Packaging
packages/webui/package.json, packages/webui/vite.config.followup.ts, packages/webui/vite.config.ts
Added followup subpath export, followup-specific Vite build config, and peerDependency on core for the followup bundle.
Misc Tests Updated
integration-tests/*, packages/cli/src/**/*test.ts*, packages/core/src/**/*test.ts*
Updated many tests to reflect disableAllHooks semantics, followup behavior, MCP reconnect, npm utilities, hook UI flows, and added new test coverage across subsystems.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User as User
participant UI as CLI/Web UI
participant Core as Core (followup)
participant Gemini as GeminiChat
participant Overlay as OverlayFs
participant Tool as ToolExecutor
participant Telemetry as Telemetry

User->>UI: Model turn completes
UI->>Core: generatePromptSuggestion(history, abortSignal)
Core->>Gemini: runForkedQuery / generateContent
Gemini-->>Core: suggestion (text / JSON)
Core->>Core: shouldFilterSuggestion -> accept/reject
Core-->>UI: setSuggestion (visible after 300ms)
UI->>User: render ghost-text
alt user accepts suggestion
User->>UI: Tab/Enter/Right
UI->>Core: followup.accept(method)
Core-->>UI: insert/submit suggestion
Core->>Telemetry: logPromptSuggestion(accepted)
else speculation enabled (background)
UI->>Core: startSpeculation(suggestion)
Core->>Gemini: speculative chat turns (forked)
Gemini-->>Core: model text + functionCalls
Core->>Overlay: redirectWrite / resolveReadPath
Core->>Tool: execute allowed tools (reads/writes redirected)
Tool-->>Core: tool results
Core->>Overlay: apply speculative writes on accept
Core->>Telemetry: logSpeculation(result)
end

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

✨ 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 feat/upstream-backports

Comment thread packages/core/src/extension/npm.ts Fixed
Comment thread packages/core/src/extension/npm.ts Fixed

@coderabbitai coderabbitai 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.

Actionable comments posted: 18

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/core/src/services/chatCompressionService.ts (1)

366-430: ⚠️ Potential issue | 🟠 Major

Stripping the orphaned tail is lost on NOOP/failure paths.

historyForSplit only changes the summary input. If force=true and the stripped history later hits splitPoint === 0, the new 5% guard, or any failed-summary path, this method still returns newHistory: null, so the caller keeps the original curatedHistory with the dangling functionCall intact. A minimal repro is [user, model(functionCall)]: the tail is "stripped" locally, then the early NOOP puts it right back. The sanitized history needs to survive those early returns too—either by persisting the cleanup before deciding whether to summarize, or by returning a distinct sanitized result.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/services/chatCompressionService.ts` around lines 366 - 430,
The current logic only strips the orphaned model functionCall into
historyForSplit but then returns newHistory: null on NOOP/failure paths, which
restores the dangling tail; update the early-return branches (the
historyToCompress.length === 0 branch and the MIN_COMPRESSION_FRACTION guard
branch) to return a sanitized newHistory that reflects the stripped history
(i.e., use historyForSplit or reconstructed curatedHistory without the last
orphaned message when force && hasOrphanedFuncCall) instead of null so the
caller receives the cleaned-up history; locate references to curatedHistory,
lastMessage, hasOrphanedFuncCall, historyForSplit, historyToCompress and the
findCompressSplitPoint call to implement the change.
packages/core/src/telemetry/types.ts (1)

898-932: ⚠️ Potential issue | 🟠 Major

New telemetry events are not included in the TelemetryEvent union type.

PromptSuggestionEvent and SpeculationEvent (lines 1066-1137) are missing from the TelemetryEvent union. This will cause type mismatches when these events are passed to telemetry logging functions that expect TelemetryEvent.

🐛 Proposed fix
 export type TelemetryEvent =
   | StartSessionEvent
   | EndSessionEvent
   | UserPromptEvent
   | ToolCallEvent
   | ApiRequestEvent
   | ApiErrorEvent
   | ApiCancelEvent
   | ApiResponseEvent
   | FlashFallbackEvent
   | LoopDetectedEvent
   | LoopDetectionDisabledEvent
   | NextSpeakerCheckEvent
   | KittySequenceOverflowEvent
   | MalformedJsonResponseEvent
   | IdeConnectionEvent
   | ConversationFinishedEvent
   | SlashCommandEvent
   | FileOperationEvent
   | InvalidChunkEvent
   | ContentRetryEvent
   | ContentRetryFailureEvent
   | SubagentExecutionEvent
   | ExtensionEnableEvent
   | ExtensionInstallEvent
   | ExtensionUninstallEvent
   | ToolOutputTruncatedEvent
   | ModelSlashCommandEvent
   | AuthEvent
   | HookCallEvent
   | SkillLaunchEvent
   | UserFeedbackEvent
   | ArenaSessionStartedEvent
   | ArenaAgentCompletedEvent
-  | ArenaSessionEndedEvent;
+  | ArenaSessionEndedEvent
+  | PromptSuggestionEvent
+  | SpeculationEvent;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/telemetry/types.ts` around lines 898 - 932, The
TelemetryEvent union type is missing the new event types, causing type errors
when logging PromptSuggestionEvent and SpeculationEvent; update the
TelemetryEvent union (the exported type TelemetryEvent) to include
PromptSuggestionEvent and SpeculationEvent so they are recognized as valid
telemetry events for functions that accept TelemetryEvent.
packages/core/src/extension/marketplace.ts (1)

207-258: ⚠️ Potential issue | 🟠 Major

npm: sources still miss the new npm branch.

At Line 211, parseSourceAndPluginName() splits a raw source like npm:package-name into repo === 'npm' and pluginName === 'package-name'. That means the new check at Lines 252-258 never runs, so raw npm: installs still fall through to Install source not found. Please special-case the npm: scheme before plugin-name parsing, and use the npm package parser for unscoped specs too.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/extension/marketplace.ts` around lines 207 - 258, In
parseInstallSource, special-case the "npm:" scheme before calling
parseSourceAndPluginName: if source startsWith "npm:" (or matches the npm
scheme), treat it as an npm install, parse the rest of the spec with the npm
package parser (not parseSourceAndPluginName) to obtain package name and
optional version, set installMetadata.type = 'npm' and pluginName appropriately,
and skip the isScopedNpmPackage branch; keep parseSourceAndPluginName for
non-scheme inputs and retain isScopedNpmPackage for scoped-package detection.
Ensure you update repo/repoSource/pluginName assignments so raw
"npm:package-name" no longer yields repo === 'npm' and falls through.
packages/cli/src/ui/components/ModelDialog.tsx (1)

148-206: ⚠️ Potential issue | 🟠 Major

Fast-model mode can't round-trip every selectable option.

Fast mode still renders runtime models and models from every auth provider, but it only persists fastModel = modelId and later reconstructs the selected key with the current authType while activeRuntimeSnapshot is disabled. Picking a runtime model, or a model from a different provider, therefore won't restore reliably and can resolve to the wrong target on reopen/use. Either hide unsupported entries in fast mode, or persist enough metadata (authType / snapshot id) alongside fastModel.

Also applies to: 250-265, 301-323

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/ModelDialog.tsx` around lines 148 - 206, The
current fast-model flow can't reliably restore selections because only modelId
is persisted; update the code so fast-mode selections either are limited to
reconstructable entries or persist full metadata: when building
availableModelEntries (used by the selector) detect fast mode (fastModel flag)
and filter out entries that cannot be round-tripped (e.g., skip runtime models
if activeRuntimeSnapshot is false and skip registry models whose authType cannot
be reselected), and/or change the fastModel storage to an object { modelId,
authType, snapshotId } and update the selection serialization/deserialization
logic to read/write these fields so reconstruction uses authType and snapshotId
(adjust any code that reads fastModel to prefer the expanded shape); make
changes to the selector build (availableModelEntries) and the fastModel
read/write code paths so they stay consistent.
🟡 Minor comments (18)
docs/users/extension/extension-releasing.md-138-146 (1)

138-146: ⚠️ Potential issue | 🟡 Minor

Add language identifiers to fenced code blocks.

Several changed fenced blocks are missing a language tag (markdownlint MD040). Please use ```text for directory trees and ```bash for commands.

Suggested doc fix
-```
+```text
 my-extension/
 ├── package.json
 ├── qwen-extension.json
 ├── QWEN.md              # optional context file
 ├── commands/             # optional custom commands
 ├── skills/               # optional custom skills
 └── agents/               # optional custom subagents

- +bash

Install latest version

qwen extensions install @your-org/my-extension
...


-```
+```bash
# Publish a beta release
npm publish --tag beta
...

Also applies to: 166-175, 196-202

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/users/extension/extension-releasing.md` around lines 138 - 146, Update
the fenced code blocks in docs/users/extension/extension-releasing.md to include
language identifiers: change the directory tree block (the block showing
"my-extension/ ├── package.json ...") to use ```text, and change the
bash/command blocks (the install and publish examples shown in the diff) to use
```bash; apply the same fixes to the other similar blocks referenced around
lines 166-175 and 196-202 so all directory trees use text and all shell examples
use bash.
docs/users/extension/extension-releasing.md-9-9 (1)

9-9: ⚠️ Potential issue | 🟡 Minor

Use hyphenated compound adjective on Line 9.

platform specific should be platform-specific in both occurrences for correct grammar and consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/users/extension/extension-releasing.md` at line 9, Replace the two
instances of the unhyphenated phrase "platform specific" in the sentence
starting "GitHub releases may also contain platform specific archives..." with
the hyphenated compound adjective "platform-specific" for correct grammar and
consistency; search for the exact phrase "platform specific archives" in the doc
and update both occurrences to "platform-specific archives".
docs/users/extension/introduction.md-19-19 (1)

19-19: ⚠️ Potential issue | 🟡 Minor

Fix command description text on Line 19.

There’s a small user-facing wording issue: page(Gemini or ClaudeCode) is missing a space after page, and ClaudeCode should be consistently written as Claude Code if that’s the product name used elsewhere.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/users/extension/introduction.md` at line 19, Update the table cell
description for the `/extensions explore [source]` command so it reads "Open
extensions source page (Gemini or Claude Code) in your browser" — add the
missing space after "page" and change "ClaudeCode" to "Claude Code" to match the
project's product naming; update the cell string that currently contains "|
`/extensions explore [source]`        | Open extensions source page(Gemini or
ClaudeCode) in your browser            |".
docs/users/overview.md-59-59 (1)

59-59: ⚠️ Potential issue | 🟡 Minor

Consider hyphenating "Follow-up" for consistency with standard English conventions.

"Follow-up" is typically hyphenated when used as a compound adjective. However, if "Followup" is the established product/feature naming convention across the codebase, this can be ignored.

📝 Proposed fix
-- **[Followup suggestions](./features/followup-suggestions)**: Qwen Code predicts what you want to type next and shows it as ghost text. Press Tab to accept, or just keep typing to dismiss.
+- **[Follow-up suggestions](./features/followup-suggestions)**: Qwen Code predicts what you want to type next and shows it as ghost text. Press Tab to accept, or just keep typing to dismiss.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/users/overview.md` at line 59, Change the link text "**[Followup
suggestions](./features/followup-suggestions)**" to use the hyphenated form
"**Follow-up suggestions**" (i.e., update the markdown link label to "[Follow-up
suggestions](./features/followup-suggestions)"), unless the project-wide
product/feature naming convention intentionally uses "Followup"—in that case
leave it as-is after confirming the established naming.
packages/cli/src/acp-integration/session/Session.ts-926-926 (1)

926-926: ⚠️ Potential issue | 🟡 Minor

Remove unnecessary optional chaining on Config method calls.

Line 926 uses optional chaining on getDisableAllHooks?.() and getMessageBus?.(), but config is a required non-optional property and both methods are required on the Config type (always defined and non-optional). This pattern diverges from all other callsites in the codebase, which call these methods directly. Remove the ?. operators to align with the rest of the codebase and clarify that these methods always exist.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/acp-integration/session/Session.ts` at line 926, Remove the
unnecessary optional chaining when calling config methods: replace uses of
this.config.getDisableAllHooks?.() and this.config.getMessageBus?.() with direct
calls this.config.getDisableAllHooks() and this.config.getMessageBus() (these
methods are required on Config); update those callsites in Session.ts (and any
other occurrences in the file) so they match the rest of the codebase and no
longer use the ?. operator.
integration-tests/hook-integration/hooks.test.ts-47-47 (1)

47-47: ⚠️ Potential issue | 🟡 Minor

Remove the non-functional enabled: true pattern from hook configs or ensure tests verify actual hook execution.

Line 47 correctly uses disableAllHooks: false, but the file also contains many instances of hooks: { enabled: true, ... } (e.g., line 1687+). The enabled field at the hooks-object level is explicitly filtered out and ignored by HookRegistry during processing—it's listed in HOOKS_CONFIG_FIELDS and skipped via if (HOOKS_CONFIG_FIELDS.includes(eventName)) { continue; }.

This means tests using hooks: { enabled: true, SessionStart: [...] } are relying on a non-functional flag. While the hook definitions (SessionStart, etc.) are still processed and the tests likely pass, the enabled: true has no effect and provides false assurance of control. Standardize on the working pattern: either remove enabled: true or migrate all instances to use disableAllHooks: false at the settings level for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integration-tests/hook-integration/hooks.test.ts` at line 47, Tests are using
a non-functional "enabled: true" key inside hooks configs which HookRegistry
ignores (see HOOKS_CONFIG_FIELDS and the filtering logic in HookRegistry), so
remove all occurrences of the top-level "enabled" property from hook config
objects or replace them with the functional pattern (set disableAllHooks: false
in the settings used by the tests); search for "hooks: { enabled:" and update
those instances, ensuring HookRegistry still receives actual hook event keys
like "SessionStart" so the tests verify real hook execution.
packages/cli/src/i18n/locales/de.js-627-628 (1)

627-628: ⚠️ Potential issue | 🟡 Minor

Fix German grammar in disabled-hooks sentence.

Line 627–628 is missing a noun after {{count}}, so the sentence reads incomplete in UI.

Suggested wording
-  'All hooks are currently disabled. You have {{count}} that are not running.':
-    'Alle Hooks sind derzeit deaktiviert. Sie haben {{count}} die nicht ausgeführt werden.',
+  'All hooks are currently disabled. You have {{count}} that are not running.':
+    'Alle Hooks sind derzeit deaktiviert. Sie haben {{count}} Hooks, die nicht ausgeführt werden.',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/i18n/locales/de.js` around lines 627 - 628, The German
translation for the key 'All hooks are currently disabled. You have {{count}}
that are not running.' is missing a noun after {{count}}; update the translation
string (currently 'Alle Hooks sind derzeit deaktiviert. Sie haben {{count}} die
nicht ausgeführt werden.') to include the noun and correct grammar, e.g. change
it to 'Alle Hooks sind derzeit deaktiviert. Sie haben {{count}} Hooks, die nicht
ausgeführt werden.' so the sentence reads complete and grammatically correct.
packages/cli/src/ui/components/hooks/HooksDisabledStep.test.tsx-13-29 (1)

13-29: ⚠️ Potential issue | 🟡 Minor

Zero-count handling in the mock is still falsy-sensitive.

The guards here use options?.count, so a numeric 0 skips the interpolation branches. If HooksDisabledStep forwards configuredHooksCount as a number, the zero-hooks case stops exercising the translated path and falls back to the raw key instead.

🛠️ Suggested fix
-  t: vi.fn((key: string, options?: { count?: string }) => {
+  t: vi.fn((key: string, options?: { count?: number | string }) => {
     // Handle pluralization
-    if (key === '{{count}} configured hook' && options?.count) {
+    if (
+      key === '{{count}} configured hook' &&
+      options?.count !== undefined
+    ) {
       return `${options.count} configured hook`;
     }
-    if (key === '{{count}} configured hooks' && options?.count) {
+    if (
+      key === '{{count}} configured hooks' &&
+      options?.count !== undefined
+    ) {
       return `${options.count} configured hooks`;
     }
     // Handle interpolation for main message
     if (
       key ===
         'All hooks are currently disabled. You have {{count}} that are not running.' &&
-      options?.count
+      options?.count !== undefined
     ) {
       return `All hooks are currently disabled. You have ${options.count} that are not running.`;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/hooks/HooksDisabledStep.test.tsx` around lines
13 - 29, The mock translator function t in HooksDisabledStep.test.tsx currently
checks options?.count which treats 0 as falsy and skips interpolation; update
the guards to explicitly detect numeric zero (e.g., check options?.count !==
undefined and options?.count !== null or use typeof options?.count === 'number'
or Number.isFinite(options.count')) so the branches for '{{count}} configured
hook(s)' and the long message string interpolate when count is 0 and the test
exercises the translated path.
packages/core/src/core/client.ts-994-1013 (1)

994-1013: ⚠️ Potential issue | 🟡 Minor

Persist the cache-safe snapshot on every successful return.

This block only runs on the final fallthrough. When model.skipNextSpeakerCheck is enabled, Line 945 returns before it, so the follow-up cache never refreshes and suggestions stay stale/empty for that mode. Pull this into a helper and call it from each non-aborted success exit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/core/client.ts` around lines 994 - 1013, The cache-save
logic that calls saveCacheSafeParams using this.getChat and chat.getHistory only
runs on the final fallthrough, so when model.skipNextSpeakerCheck causes an
early return the cache snapshot isn't refreshed; extract that block into a
helper method (e.g., persistCacheSafeSnapshot or similar) which accepts the
signal check and uses this.getChat(), chat.getGenerationConfig(), and
this.config.getModel() to compute cachedHistory and call saveCacheSafeParams,
ensure it swallows errors like the original, and invoke this helper from every
non-aborted success exit path (including the branch where
model.skipNextSpeakerCheck triggers an early return) so the cache is persisted
on every successful return.
packages/cli/src/ui/components/hooks/HooksManagementDialog.tsx-361-364 (1)

361-364: ⚠️ Potential issue | 🟡 Minor

Don't bypass load/error handling for the disabled step.

Because this branch returns before the isLoading and loadError checks, a settings parse/load failure while hooks are globally disabled renders a normal disabled view with a misleading count instead of surfacing the failure. Either move the disabled branch below the error handling, or let HooksDisabledStep render explicit loading/error states.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/hooks/HooksManagementDialog.tsx` around lines
361 - 364, The early return for the disabled state in HooksManagementDialog
(checking currentStep === HOOKS_MANAGEMENT_STEPS.HOOKS_DISABLED and returning
HooksDisabledStep) bypasses isLoading/loadError handling; move that
disabled-branch check below the existing isLoading and loadError checks so the
component first handles loading and error states before rendering
HooksDisabledStep (passing configuredHooksCount as before), or alternatively
update HooksDisabledStep to accept isLoading and loadError and render explicit
loading/error UIs instead—choose one approach and apply consistently so failures
are surfaced when hooks are disabled.
docs/users/configuration/settings.md-191-195 (1)

191-195: ⚠️ Potential issue | 🟡 Minor

Clarify the JSON path for fastModel.

This section introduces fastModel as a bare setting, but Line 76 says settings live inside top-level category objects. Right now it's unclear whether users should set { "fastModel": ... } or nest it under model. Please make the path explicit and mirror it in the example config.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/users/configuration/settings.md` around lines 191 - 195, The
documentation currently treats fastModel as a top-level key but settings are
stored under category objects; update the wording and example to explicitly
state that fastModel lives under the model category (i.e., model.fastModel) and
mirror that in the example configuration, update the table/description to show
the JSON path as model.fastModel, and note that the CLI command /model --fast
maps to model.fastModel so users know where to set it.
docs/users/features/followup-suggestions.md-105-105 (1)

105-105: ⚠️ Potential issue | 🟡 Minor

Capitalize "Markdown" as a proper noun.

"Markdown" is a proper noun (named after its creator John Gruber) and should be capitalized.

Proposed fix
-- Cannot be multiple sentences or contain formatting (markdown, newlines)
+- Cannot be multiple sentences or contain formatting (Markdown, newlines)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/users/features/followup-suggestions.md` at line 105, Update the text
fragment "Cannot be multiple sentences or contain formatting (markdown,
newlines)" so that the word "markdown" is capitalized as "Markdown" (i.e.,
change the string to "Cannot be multiple sentences or contain formatting
(Markdown, newlines)"); ensure any other occurrences of the lowercase "markdown"
in this document are similarly capitalized to "Markdown".
docs/users/features/followup-suggestions.md-11-13 (1)

11-13: ⚠️ Potential issue | 🟡 Minor

Add language specifier to fenced code block.

The code block lacks a language specifier. For user input examples, consider using text or plaintext.

Proposed fix
-```
+```text
 > run the tests

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/users/features/followup-suggestions.md around lines 11 - 13, The fenced
code block containing the example line "> run the tests" is missing a language
specifier; update that fenced block in
docs/users/features/followup-suggestions.md to add a language token (e.g.,
text or plaintext) immediately after the opening backticks so the block
becomes a plaintext example and renders correctly.


</details>

</blockquote></details>
<details>
<summary>docs/users/features/followup-suggestions.md-51-53 (1)</summary><blockquote>

`51-53`: _⚠️ Potential issue_ | _🟡 Minor_

**Add language specifier to fenced code block.**

Same issue as above - the code block should have a language specifier for consistent markdown linting.


<details>
<summary>Proposed fix</summary>

```diff
-```
+```text
 /model --fast qwen3.5-flash
 ```
```

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/users/features/followup-suggestions.md around lines 51 - 53, The fenced
code block containing "/model --fast qwen3.5-flash" is missing a language
specifier; update the triple-backtick fence to include a language (e.g., change
totext) so the block follows markdown lint rules and renders
consistently—locate the fenced block around the literal "/model --fast
qwen3.5-flash" and add the language token to the opening fence.


</details>

</blockquote></details>
<details>
<summary>packages/cli/src/ui/components/hooks/HooksDisabledStep.tsx-7-17 (1)</summary><blockquote>

`7-17`: _⚠️ Potential issue_ | _🟡 Minor_

**Missing `React` import for type annotation at line 17.**

The return type `React.JSX.Element` references the `React` namespace, but `React` is not imported. While React 19's JSX transform allows JSX syntax without importing React, type annotations using `React.JSX.Element` still require the import. This pattern is inconsistent across the codebase—22 other components that use `React.JSX.Element` do import React.

<details>
<summary>Proposed fix</summary>

```diff
+import React from 'react';
 import { Box, Text } from 'ink';
 import { theme } from '../../semantic-colors.js';
 import { t } from '../../../i18n/index.js';
```

Alternatively, use `JSX.Element` directly without the `React.` prefix.

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/hooks/HooksDisabledStep.tsx` around lines 7 -
17, The component HooksDisabledStep declares its return type as
React.JSX.Element but React is not imported; either add an import for React (so
the React namespace is available) or change the return type to JSX.Element to
avoid needing the React import; update the HooksDisabledStep function signature
accordingly and ensure consistency with other components using React.JSX.Element
or JSX.Element.
```

</details>

</blockquote></details>
<details>
<summary>docs/design/prompt-suggestion/prompt-suggestion-design.md-9-10 (1)</summary><blockquote>

`9-10`: _⚠️ Potential issue_ | _🟡 Minor_

**The word-count rule conflicts with the filter table.**

The overview and prompt both say suggestions are `2-12 words`, but `too_few_words` explicitly allows one-word outputs like `commit` and `push`. That ambiguity will leak into implementation and tests; please pick one rule and use it consistently throughout the spec.



Also applies to: 75-95

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@docs/design/prompt-suggestion/prompt-suggestion-design.md` around lines 9 -
10, The doc currently conflicts: the overview/spec for "prompt suggestion"
states suggestions must be 2–12 words while the filter `too_few_words` allows
one-word outputs; pick and apply a single rule consistently by either (A)
changing the overview and example prompts to state the allowed range as 1–12
words, or (B) tightening the `too_few_words` filter to reject single-word
suggestions and require a minimum of 2 words; update every occurrence of the
phrase/rule (including the "prompt suggestion" overview and the `too_few_words`
filter and the repeated text around the sections referenced) so the spec,
examples, and filter table all use the same word-count rule.
```

</details>

</blockquote></details>
<details>
<summary>packages/core/src/followup/overlayFs.test.ts-107-119 (1)</summary><blockquote>

`107-119`: _⚠️ Potential issue_ | _🟡 Minor_

**This case never exercises a relative path.**

The value passed to `resolveReadPath()` is still the absolute file path under `testDir`, so this rechecks the written-file branch instead of covering relative resolution. Pass a path relative to `realCwd` here.

<details>
<summary>🧪 Minimal fix</summary>

```diff
-      const resolved = overlay.resolveReadPath(join(testDir, 'src', 'app.ts'));
+      const resolved = overlay.resolveReadPath(join('src', 'app.ts'));
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/followup/overlayFs.test.ts` around lines 107 - 119, The
test is passing an absolute path to overlay.resolveReadPath so it never
exercises the relative-path branch; change the argument to a path relative to
overlay.realCwd (or realCwd used in the test) so resolveReadPath receives a
relative path — e.g. compute the relative path from realCwd to realFile (or
directly pass join('src','app.ts') if realCwd is testDir) after calling
overlay.redirectWrite(realFile) so the test asserts the relative-resolution
behavior of overlay.resolveReadPath.
```

</details>

</blockquote></details>
<details>
<summary>packages/cli/src/ui/components/hooks/HooksManagementDialog.test.tsx-126-133 (1)</summary><blockquote>

`126-133`: _⚠️ Potential issue_ | _🟡 Minor_

**Reset the `useConfig` implementation between tests.**

Tests override `useConfig()` to return `getDisableAllHooks() === true` (at lines 241–246 and 261–265), but `beforeEach` only calls `vi.clearAllMocks()`. In Vitest, this clears call history only—custom mock implementations set via `mockReturnValue()` persist across tests. If test execution order changes (e.g., due to randomization), later tests could inherit the overridden state. Either restore the default `useConfig` mock in `beforeEach`, or switch to `vi.resetAllMocks()` and re-register the shared mocks.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/hooks/HooksManagementDialog.test.tsx` around
lines 126 - 133, The tests override useConfig() in individual test cases but
beforeEach only calls vi.clearAllMocks(), so mock implementations persist across
tests; update the beforeEach in HooksManagementDialog.test.tsx to reset the
useConfig mock between tests — either call vi.resetAllMocks() and then
re-register shared mocks (e.g., mockedUseKeypress mockImplementation) or
explicitly restore the default mockedUseConfig implementation (undoing
test-specific mockReturnValue for getDisableAllHooks) so functions like
useConfig and getDisableAllHooks do not leak state across tests.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (10)</summary><blockquote>

<details>
<summary>docs/superpowers/plans/2026-04-02-task-update-diff-display.md (2)</summary><blockquote>

`13-13`: **Fix heading level increment.**

The task headings (lines 13, 122, 393, 509) use h3 (`###`) directly after an h2 (`---` on line 11), skipping the proper hierarchy. These should be h2 (`##`) headings to maintain consistent document structure.



<details>
<summary>📝 Proposed fix</summary>

```diff
 ---

-### Task 1: Add `TaskUpdateDiffDisplay` type to core
+## Task 1: Add `TaskUpdateDiffDisplay` type to core

 **Files:**
```

Apply the same change to Task 2 (line 122), Task 3 (line 393), and Task 4 (line 509).

</details>

As per coding guidelines: Format code with Prettier for consistent styling applies to `**/*.md` files.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@docs/superpowers/plans/2026-04-02-task-update-diff-display.md` at line 13,
Headings for the task sections use h3 (###) immediately after the h2 document
divider (---), breaking hierarchy; update the task section headings "Task 1: Add
`TaskUpdateDiffDisplay` type to core", "Task 2", "Task 3", and "Task 4" to h2
(##) instead of h3, and reformat the markdown file with Prettier (apply to
**/*.md) to ensure consistent styling.
```

</details>

---

`608-612`: **Add language specifier to example output block.**

The example output block is missing a language specifier. Adding `text` or `plaintext` improves markdown rendering consistency.



<details>
<summary>📝 Proposed fix</summary>

```diff
 Expected output should show a diff block:

-```
+```text
   ✓ Update task <id>: status=in_progress
     test task
     status      pending → in_progress
 ```
```

</details>

As per coding guidelines: Format code with Prettier for consistent styling applies to `**/*.md` files.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/superpowers/plans/2026-04-02-task-update-diff-display.md around lines
608 - 612, The fenced code block showing the task update example currently has
no language tag; update the opening fence from totext (or plaintext) so the snippet becomes "text" and then run Prettier (or format markdown) to
ensure consistent styling for **/*.md files; target the example block containing
"✓ Update task : status=in_progress" and ensure the closing fence remains
unchanged.


</details>

</blockquote></details>
<details>
<summary>docs/superpowers/specs/2026-04-02-task-update-diff-display.md (1)</summary><blockquote>

`21-41`: **Add language specifiers to fenced code blocks.**

The example output blocks on lines 21, 27, and 37 are missing language specifiers. Adding `text` or `plaintext` to these code fences improves markdown rendering and readability.



<details>
<summary>📝 Proposed fix</summary>

```diff
-```
+```text
   ✓ Update task 5e24f34e: status=in_progress
     Explore project context for brainstorming
     status      pending → in_progress
 ```

-```
+```text
   ✓ Update task 5e24f34e: status=completed, title=...
     Research auth patterns
     title       "Research auth" → "Research auth patterns"
     status      in_progress → completed
     priority    medium → high
 ```

-```
+```text
   ✓ Update task 5e24f34e
     Explore project context
     no changes
 ```
```

</details>

As per coding guidelines: Format code with Prettier for consistent styling applies to `**/*.md` files.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/superpowers/specs/2026-04-02-task-update-diff-display.md around lines
21 - 41, The three example fenced code blocks showing "✓ Update task 5e24f34e:
status=in_progress", "✓ Update task 5e24f34e: status=completed, title=...", and
the "✓ Update task 5e24f34e" no-op block are missing language specifiers; update
each opening triple-backtick to include a language (e.g., text) so the blocks read text, and then reformat the markdown with Prettier to ensure consistent
styling across the file.


</details>

</blockquote></details>
<details>
<summary>packages/core/src/followup/forkedQuery.ts (2)</summary><blockquote>

`71-83`: **`JSON.stringify` comparison is fragile for object equality.**

Using `JSON.stringify` to compare `systemInstruction` and `tools` objects can produce false positives (treating equal objects as different) when key ordering differs, or false negatives when semantically equivalent values serialize identically despite differences. Consider using a deep equality utility for more reliable comparison.


<details>
<summary>♻️ Alternative approaches</summary>

Option 1: Use a deep-equal library like `fast-deep-equal`:
```diff
+import equal from 'fast-deep-equal';

 const sysChanged =
   !prevConfig ||
-  JSON.stringify(prevConfig.systemInstruction) !==
-    JSON.stringify(generationConfig.systemInstruction);
+  !equal(prevConfig.systemInstruction, generationConfig.systemInstruction);
 const toolsChanged =
   !prevConfig ||
-  JSON.stringify(prevConfig.tools) !== JSON.stringify(generationConfig.tools);
+  !equal(prevConfig.tools, generationConfig.tools);
```

Option 2: If the current approach works in practice, add a comment explaining the trade-off.
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/followup/forkedQuery.ts` around lines 71 - 83, The
JSON.stringify comparisons for prevConfig.systemInstruction and prevConfig.tools
are fragile; replace them with a proper deep-equality check (e.g., import
deepEqual from "fast-deep-equal" or use lodash's isEqual) and change the
sysChanged/toolsChanged checks to use deepEqual(prevConfig.systemInstruction,
generationConfig.systemInstruction) and deepEqual(prevConfig.tools,
generationConfig.tools); update imports accordingly and ensure currentVersion is
incremented only when deepEqual reports inequality. If you intentionally accept
stringify behavior, add a short comment on why JSON.stringify is sufficient and
note the trade-offs instead of changing logic.
```

</details>

---

`55-60`: **Global mutable state may cause issues in multi-session scenarios.**

The module-level `currentCacheSafeParams` and `currentVersion` variables assume a single active session. If the codebase ever supports multiple concurrent sessions, this design would need refactoring to per-session state management.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/followup/forkedQuery.ts` around lines 55 - 60, The
module-level mutable globals currentCacheSafeParams and currentVersion create
shared state across all sessions which will break in multi-session/concurrent
use; refactor to move these into a per-session or per-instance store (e.g.,
attach them to a Session/Context object or create a ForkedQueryManager class)
and update all usages in this module to read/write from that per-session object
instead of the globals (replace direct references to
currentCacheSafeParams/currentVersion with getters/setters on the
session/context or instance fields on the new ForkedQueryManager).
```

</details>

</blockquote></details>
<details>
<summary>packages/cli/src/utils/systemInfoFields.test.ts (1)</summary><blockquote>

`34-46`: **LGTM!**

The test correctly verifies that "Fast Model" appears in the expected position. Since the test input lacks an explicit `fastModel` property, this validates the fallback behavior where `modelVersion` is displayed.

Consider adding a test case with an explicit `fastModel` value to verify both code paths.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/utils/systemInfoFields.test.ts` around lines 34 - 46, Add a
new unit test in packages/cli/src/utils/systemInfoFields.test.ts that covers the
explicit fastModel code path by supplying an input object with a fastModel
property (e.g., alongside modelVersion) and asserting that the systemInfoFields
(or the test's labels/values) include "Fast Model" and display the provided
fastModel value instead of falling back to modelVersion; locate the existing
test that checks labels (the one asserting the array with 'Fast Model') and
duplicate/extend it to verify both the fallback behavior and the explicit
fastModel behavior for functions/variables used in that file (e.g.,
systemInfoFields generator or the test helpers).
```

</details>

</blockquote></details>
<details>
<summary>packages/webui/src/styles/components.css (1)</summary><blockquote>

`452-458`: **Add keyboard-focus affordance to match hover behavior.**

Line 452 currently improves discoverability only on hover. Consider mirroring this style on focus/focus-visible for keyboard users.

<details>
<summary>Suggested tweak</summary>

```diff
 .composer-input[data-has-suggestion='true']:hover:empty::before,
 .composer-input[data-has-suggestion='true']:hover[data-empty='true']::before {
   opacity: 0.9;
   text-decoration: underline;
   text-decoration-style: dotted;
   text-underline-offset: 2px;
 }
+
+.composer-input[data-has-suggestion='true']:focus:empty::before,
+.composer-input[data-has-suggestion='true']:focus[data-empty='true']::before,
+.composer-input[data-has-suggestion='true']:focus-visible:empty::before,
+.composer-input[data-has-suggestion='true']:focus-visible[data-empty='true']::before {
+  opacity: 0.9;
+  text-decoration: underline;
+  text-decoration-style: dotted;
+  text-underline-offset: 2px;
+}
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/webui/src/styles/components.css` around lines 452 - 458, The
hover-only affordance for suggestion discoverability should also apply on
keyboard focus; update the selector(s) targeting
.composer-input[data-has-suggestion='true']:hover:empty::before and
.composer-input[data-has-suggestion='true']:hover[data-empty='true']::before to
include :focus and/or :focus-visible (e.g., add :focus:empty::before and
:focus-visible[data-empty='true']::before) so the same opacity, dotted
underline, and text-underline-offset styles are applied for keyboard users using
the .composer-input element.
```

</details>

</blockquote></details>
<details>
<summary>packages/cli/src/ui/components/InputPrompt.test.tsx (1)</summary><blockquote>

`239-302`: **Prefer fake timers over fixed sleeps here.**

These tests add four 350ms wall-clock waits and couple the assertions to the current suggestion-delay implementation, which makes the suite slower and more brittle in CI. `vi.useFakeTimers()` plus `vi.advanceTimersByTimeAsync()` would make this path deterministic.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/InputPrompt.test.tsx` around lines 239 - 302,
Replace the real-time sleeps in the InputPrompt prompt-suggestions tests with
fake timers: call vi.useFakeTimers() in the describe or beforeEach and
vi.useRealTimers() in afterEach, then replace each await wait(350) with await
vi.advanceTimersByTimeAsync(350) (and any subsequent await wait() with
advanceTimersByTimeAsync(0) or a small advance to let microtasks run) so the
suggestion-delay logic in the InputPrompt tests (the `describe('prompt
suggestions')` block and the tests that call renderWithProviders, stdin.write,
mockBuffer.insert, props.onSubmit, mockCommandCompletion.handleAutocomplete)
becomes deterministic and fast. Ensure timers are restored after the tests.
```

</details>

</blockquote></details>
<details>
<summary>packages/cli/src/ui/components/hooks/HooksDisabledStep.tsx (1)</summary><blockquote>

`19-24`: **Consider using i18n pluralization rules instead of manual branching.**

The manual `configuredHooksCount === 1` check works but doesn't scale well for languages with complex pluralization rules (e.g., Russian, Arabic). Most i18n libraries support built-in pluralization.


<details>
<summary>Suggestion using i18n pluralization</summary>

If the i18n library supports ICU plural format or count-based keys:

```diff
-  const hooksText =
-    configuredHooksCount === 1
-      ? t('{{count}} configured hook', { count: String(configuredHooksCount) })
-      : t('{{count}} configured hooks', {
-          count: String(configuredHooksCount),
-        });
+  const hooksText = t('configured_hooks', { count: configuredHooksCount });
```

With translation key:
```json
{
  "configured_hooks_one": "{{count}} configured hook",
  "configured_hooks_other": "{{count}} configured hooks"
}
```

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/hooks/HooksDisabledStep.tsx` around lines 19 -
24, The current manual branching on configuredHooksCount (in the hooksText
constant) bypasses the i18n library's pluralization support and won't handle
complex plural forms; replace the conditional logic with a single t(...) call
that leverages the i18n pluralization/count feature (pass configuredHooksCount
as the count/value and use pluralized translation keys or ICU plural syntax in
your translation files) so hooksText is produced by the i18n layer rather than
by configuredHooksCount === 1 checks.
```

</details>

</blockquote></details>
<details>
<summary>packages/cli/src/ui/commands/modelCommand.ts (1)</summary><blockquote>

`48-77`: **Consider more robust argument parsing for `--fast` flag.**

The current parsing with `.replace('--fast', '').trim()` works for typical usage but doesn't handle edge cases like `/model --fastmodel` (no space after flag). Using a more explicit parsing approach would be safer.


<details>
<summary>More robust parsing suggestion</summary>

```diff
     // Handle --fast flag: /model --fast <modelName>
     const args = context.invocation?.args?.trim() ?? '';
-    if (args.startsWith('--fast')) {
-      const modelName = args.replace('--fast', '').trim();
+    const fastMatch = args.match(/^--fast(?:\s+(.+))?$/);
+    if (fastMatch) {
+      const modelName = fastMatch[1]?.trim() ?? '';
       if (!modelName) {
         // Open model dialog in fast-model mode
```

This ensures `--fast` is either standalone or followed by whitespace and a model name.

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/commands/modelCommand.ts` around lines 48 - 77, The
current args parsing around context.invocation?.args in the model command
incorrectly treats any occurrence of "--fast" (e.g., "--fastmodel") as the flag;
update the parsing so "--fast" is matched as a discrete token (either alone or
followed by whitespace + model name), extract the optional modelName safely, and
only treat the remainder as modelName when separated by whitespace; then
continue to call getPersistScopeForModelSelection(...) and
settings.setValue(...) as before and return the same dialog/message flows.
```

</details>

</blockquote></details>

</blockquote></details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: Path: .coderabbit.yaml

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `c100091a-655f-4ba7-8ce5-e821e5f9d93f`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 63cef9d3b22c82a7c38fa5643140c4cf8effbee6 and db370e385d2b6a61726c0c693e10c7f5ad2aa2b8.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (106)</summary>

* `docs/design/prompt-suggestion/prompt-suggestion-design.md`
* `docs/design/prompt-suggestion/prompt-suggestion-implementation.md`
* `docs/design/prompt-suggestion/speculation-design.md`
* `docs/superpowers/plans/2026-04-02-task-update-diff-display.md`
* `docs/superpowers/specs/2026-04-02-task-update-diff-display.md`
* `docs/users/configuration/settings.md`
* `docs/users/extension/extension-releasing.md`
* `docs/users/extension/introduction.md`
* `docs/users/features/_meta.ts`
* `docs/users/features/commands.md`
* `docs/users/features/followup-suggestions.md`
* `docs/users/features/hooks.md`
* `docs/users/overview.md`
* `integration-tests/hook-integration/hooks.test.ts`
* `packages/cli/src/acp-integration/session/Session.test.ts`
* `packages/cli/src/acp-integration/session/Session.ts`
* `packages/cli/src/commands/auth/handler.ts`
* `packages/cli/src/commands/extensions/install.ts`
* `packages/cli/src/commands/mcp.test.ts`
* `packages/cli/src/commands/mcp.ts`
* `packages/cli/src/commands/mcp/reconnect.test.ts`
* `packages/cli/src/commands/mcp/reconnect.ts`
* `packages/cli/src/config/config.ts`
* `packages/cli/src/config/settingsSchema.ts`
* `packages/cli/src/gemini.test.tsx`
* `packages/cli/src/i18n/locales/de.js`
* `packages/cli/src/i18n/locales/en.js`
* `packages/cli/src/i18n/locales/ja.js`
* `packages/cli/src/i18n/locales/pt.js`
* `packages/cli/src/i18n/locales/ru.js`
* `packages/cli/src/i18n/locales/zh.js`
* `packages/cli/src/services/BuiltinCommandLoader.test.ts`
* `packages/cli/src/services/BuiltinCommandLoader.ts`
* `packages/cli/src/ui/AppContainer.tsx`
* `packages/cli/src/ui/commands/bugCommand.test.ts`
* `packages/cli/src/ui/commands/modelCommand.ts`
* `packages/cli/src/ui/commands/types.ts`
* `packages/cli/src/ui/components/Composer.tsx`
* `packages/cli/src/ui/components/DialogManager.tsx`
* `packages/cli/src/ui/components/InputPrompt.test.tsx`
* `packages/cli/src/ui/components/InputPrompt.tsx`
* `packages/cli/src/ui/components/ModelDialog.tsx`
* `packages/cli/src/ui/components/hooks/HooksDisabledStep.test.tsx`
* `packages/cli/src/ui/components/hooks/HooksDisabledStep.tsx`
* `packages/cli/src/ui/components/hooks/HooksManagementDialog.test.tsx`
* `packages/cli/src/ui/components/hooks/HooksManagementDialog.tsx`
* `packages/cli/src/ui/components/hooks/constants.ts`
* `packages/cli/src/ui/components/hooks/index.ts`
* `packages/cli/src/ui/components/hooks/types.ts`
* `packages/cli/src/ui/contexts/KeypressContext.tsx`
* `packages/cli/src/ui/contexts/UIStateContext.tsx`
* `packages/cli/src/ui/hooks/slashCommandProcessor.ts`
* `packages/cli/src/ui/hooks/useAttentionNotifications.ts`
* `packages/cli/src/ui/hooks/useFollowupSuggestions.tsx`
* `packages/cli/src/ui/hooks/useModelCommand.ts`
* `packages/cli/src/ui/hooks/useToolScheduler.test.ts`
* `packages/cli/src/utils/systemInfo.ts`
* `packages/cli/src/utils/systemInfoFields.test.ts`
* `packages/cli/src/utils/systemInfoFields.ts`
* `packages/cli/tsconfig.json`
* `packages/core/src/config/config.test.ts`
* `packages/core/src/config/config.ts`
* `packages/core/src/core/client.test.ts`
* `packages/core/src/core/client.ts`
* `packages/core/src/core/coreToolScheduler.ts`
* `packages/core/src/core/geminiChat.ts`
* `packages/core/src/extension/extensionManager.ts`
* `packages/core/src/extension/github.ts`
* `packages/core/src/extension/index.ts`
* `packages/core/src/extension/marketplace.test.ts`
* `packages/core/src/extension/marketplace.ts`
* `packages/core/src/extension/npm.test.ts`
* `packages/core/src/extension/npm.ts`
* `packages/core/src/followup/followupState.test.ts`
* `packages/core/src/followup/followupState.ts`
* `packages/core/src/followup/forkedQuery.test.ts`
* `packages/core/src/followup/forkedQuery.ts`
* `packages/core/src/followup/index.ts`
* `packages/core/src/followup/overlayFs.test.ts`
* `packages/core/src/followup/overlayFs.ts`
* `packages/core/src/followup/smoke.test.ts`
* `packages/core/src/followup/speculation.test.ts`
* `packages/core/src/followup/speculation.ts`
* `packages/core/src/followup/speculationToolGate.test.ts`
* `packages/core/src/followup/speculationToolGate.ts`
* `packages/core/src/followup/suggestionGenerator.test.ts`
* `packages/core/src/followup/suggestionGenerator.ts`
* `packages/core/src/hooks/hookRegistry.test.ts`
* `packages/core/src/hooks/hookRegistry.ts`
* `packages/core/src/hooks/hookRunner.ts`
* `packages/core/src/index.ts`
* `packages/core/src/services/chatCompressionService.test.ts`
* `packages/core/src/services/chatCompressionService.ts`
* `packages/core/src/telemetry/constants.ts`
* `packages/core/src/telemetry/loggers.ts`
* `packages/core/src/telemetry/types.ts`
* `packages/core/src/tools/mcp-tool.test.ts`
* `packages/core/src/tools/mcp-tool.ts`
* `packages/webui/package.json`
* `packages/webui/src/components/layout/InputForm.tsx`
* `packages/webui/src/followup.ts`
* `packages/webui/src/hooks/useFollowupSuggestions.ts`
* `packages/webui/src/index.ts`
* `packages/webui/src/styles/components.css`
* `packages/webui/vite.config.followup.ts`
* `packages/webui/vite.config.ts`

</details>

<details>
<summary>💤 Files with no reviewable changes (2)</summary>

* packages/cli/src/commands/auth/handler.ts
* packages/cli/src/gemini.test.tsx

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +96 to +107
try {
const config = await createMinimalConfig();
const toolRegistry = config.getToolRegistry();
await toolRegistry.discoverToolsForServer(serverName);
writeStdoutLine(`Successfully reconnected to server "${serverName}".`);
await config.shutdown();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw createReconnectError(
`Failed to reconnect to server "${serverName}": ${message}`,
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Always shut Config down on the error path.

If discoverToolsForServer() throws, this function rethrows a ReconnectError without ever calling config.shutdown(). That leaks whatever state/resources initialize() created for the reconnect session.

Suggested fix
 async function reconnectMcpServer(serverName: string): Promise<void> {
   const mcpServers = await getMcpServersFromConfig();
@@
-  try {
-    const config = await createMinimalConfig();
+  let config: Config | undefined;
+  try {
+    config = await createMinimalConfig();
     const toolRegistry = config.getToolRegistry();
     await toolRegistry.discoverToolsForServer(serverName);
     writeStdoutLine(`Successfully reconnected to server "${serverName}".`);
-    await config.shutdown();
   } catch (error) {
     const message = error instanceof Error ? error.message : String(error);
     throw createReconnectError(
       `Failed to reconnect to server "${serverName}": ${message}`,
     );
+  } finally {
+    if (config) {
+      await config.shutdown();
+    }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const config = await createMinimalConfig();
const toolRegistry = config.getToolRegistry();
await toolRegistry.discoverToolsForServer(serverName);
writeStdoutLine(`Successfully reconnected to server "${serverName}".`);
await config.shutdown();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw createReconnectError(
`Failed to reconnect to server "${serverName}": ${message}`,
);
}
let config: Config | undefined;
try {
config = await createMinimalConfig();
const toolRegistry = config.getToolRegistry();
await toolRegistry.discoverToolsForServer(serverName);
writeStdoutLine(`Successfully reconnected to server "${serverName}".`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw createReconnectError(
`Failed to reconnect to server "${serverName}": ${message}`,
);
} finally {
if (config) {
await config.shutdown();
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/commands/mcp/reconnect.ts` around lines 96 - 107, The
try/catch path can leak resources because createMinimalConfig()/config may never
be shut down if discoverToolsForServer() throws; ensure the Config returned by
createMinimalConfig() is always shut down by moving config declaration outside
the try and calling await config.shutdown() in a finally block (or add a finally
that checks config truthiness and awaits config.shutdown()); keep the existing
createReconnectError throw in the catch but still perform shutdown before
rethrowing, referencing createMinimalConfig, config.shutdown,
discoverToolsForServer, and createReconnectError to locate the fix.

Comment on lines +133 to +146
for (const serverName of serverNames) {
try {
await toolRegistry.discoverToolsForServer(serverName);
writeStdoutLine(`✓ ${serverName}: Reconnected successfully`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
writeStdoutLine(`✗ ${serverName}: Failed - ${message}`);
}
}
} finally {
if (config) {
await config.shutdown();
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Return a non-zero status when any --all reconnect fails.

The loop logs ✗ ... Failed lines but then falls through as success, so mcp reconnect --all exits 0 even if one or more servers could not reconnect. That makes the new command unreliable for automation.

Suggested fix
   let config: Config | undefined;
+  let hadFailures = false;
   try {
     config = await createMinimalConfig();
     const toolRegistry = config.getToolRegistry();

     for (const serverName of serverNames) {
       try {
         await toolRegistry.discoverToolsForServer(serverName);
         writeStdoutLine(`✓ ${serverName}: Reconnected successfully`);
       } catch (error) {
+        hadFailures = true;
         const message = error instanceof Error ? error.message : String(error);
         writeStdoutLine(`✗ ${serverName}: Failed - ${message}`);
       }
     }
+
+    if (hadFailures) {
+      throw createReconnectError(
+        'One or more MCP servers failed to reconnect.',
+      );
+    }
   } finally {
     if (config) {
       await config.shutdown();
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/commands/mcp/reconnect.ts` around lines 133 - 146, The
reconnect loop currently logs failures but always exits 0; modify the code
around the serverNames loop (the try/catch using
toolRegistry.discoverToolsForServer and writeStdoutLine) to record failures
(e.g., a boolean like hadFailure or an integer failureCount set in the catch
block) and, after the loop completes (but before/after the finally that calls
config.shutdown), set process.exitCode = 1 if any failures occurred (or throw a
new Error if the command framework expects thrown errors) so `mcp reconnect
--all` returns a non-zero status when one or more servers failed to reconnect.

Comment on lines +953 to +955
if (result.nextSuggestion) {
setPromptSuggestion(result.nextSuggestion);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clear the accepted suggestion when there's no pipelined replacement.

This path never transitions back through Responding, so leaving promptSuggestion untouched here keeps the just-executed ghost prompt visible and lets the user submit it again on the next Enter.

Suggested fix
-            if (result.nextSuggestion) {
-              setPromptSuggestion(result.nextSuggestion);
-            }
+            setPromptSuggestion(result.nextSuggestion ?? null);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (result.nextSuggestion) {
setPromptSuggestion(result.nextSuggestion);
}
setPromptSuggestion(result.nextSuggestion ?? null);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/AppContainer.tsx` around lines 953 - 955, When handling
the result where you currently only setPromptSuggestion(result.nextSuggestion)
if truthy, add an explicit else branch to clear the accepted suggestion (e.g.,
call setPromptSuggestion(undefined) or setPromptSuggestion('')) so the previous
ghost prompt is removed when there's no pipelined replacement; update the block
that checks result.nextSuggestion in AppContainer.tsx (the setPromptSuggestion
call) to clear the state in the else path because this code path never
transitions back through the Responding state.

Comment on lines +1175 to +1192
if (
followupSuggestionsEnabled &&
config.isInteractive() &&
!config.getSdkMode() &&
prevStreamingStateRef.current === StreamingState.Responding &&
streamingState === StreamingState.Idle &&
// Check both committed history and pending items for errors
// (API errors go to pendingGeminiHistoryItems, not historyManager.history)
historyManager.history[historyManager.history.length - 1]?.type !==
'error' &&
!pendingGeminiHistoryItems.some((item) => item.type === 'error') &&
!shellConfirmationRequest &&
!confirmationRequest &&
!loopDetectionConfirmationRequest &&
!isPermissionsDialogOpen &&
settingInputRequests.length === 0 &&
config.getApprovalMode() !== ApprovalMode.PLAN
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard follow-up generation behind the full dialog/modal state.

This condition only blocks a subset of interactive overlays. Theme/model/auth/update/feedback/plugin dialogs can still satisfy it, which means we'll launch another LLM request—and possibly speculation—behind an open modal. Reuse the existing aggregate dialog visibility guard here instead of hand-picking a few flags.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/AppContainer.tsx` around lines 1175 - 1192, The long
conditional that gates followup suggestion generation (the if starting with
followupSuggestionsEnabled && config.isInteractive() ...) manually re-checks
many individual flags (shellConfirmationRequest, confirmationRequest,
loopDetectionConfirmationRequest, isPermissionsDialogOpen, settingInputRequests,
and others) and can still allow followups while other modal/dialog overlays are
open; replace those hand-picked checks with the existing aggregate dialog/modal
visibility guard used elsewhere in the app (the single boolean/function that
indicates any dialog/modal is open — e.g., isAnyDialogOpen/isAnyModalOpen) so
the if condition uses that guard (negated) instead of the individual flags,
keeping the other checks (followupSuggestionsEnabled, config.isInteractive(),
!config.getSdkMode(), prevStreamingStateRef/current StreamingState checks,
history/pending error checks, and config.getApprovalMode()) unchanged.

Comment on lines +243 to +249
const followup = useFollowupSuggestionsCLI({
onAccept: (suggestion) => {
buffer.insert(suggestion);
},
config,
isFocused: isShellFocused,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't reinsert the accepted text after Enter submits it.

followup.accept('enter') queues onAccept, and onAccept always does buffer.insert(suggestion). On the Enter path you then call handleSubmitAndClear(text) immediately, so the queued insert runs after the submit/clear and leaves the just-submitted suggestion sitting back in the prompt. The submit path needs to bypass that insert or move the submit into the accept callback.

Also applies to: 977-986

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/components/InputPrompt.tsx` around lines 243 - 249, The
enter-submit workflow reinserts accepted followups because
useFollowupSuggestionsCLI's onAccept always calls buffer.insert(suggestion) and
followup.accept('enter') queues that before the submit; fix by making the Enter
submission bypass or coordinate with the accept handler: either (a) detect the
Enter path in followup.accept('enter') and call handleSubmitAndClear(text) from
the accept callback instead of queueing buffer.insert, or (b) change onAccept in
the useFollowupSuggestionsCLI call to conditionally insert only for non-Enter
acceptances (e.g., expose an accept reason or flag) so that the Enter case does
not call buffer.insert(suggestion) before handleSubmitAndClear; modify the
followup.accept('enter') usage and the onAccept callback in InputPrompt to
implement one of these options (references: followup.accept('enter'),
useFollowupSuggestionsCLI, onAccept, buffer.insert, handleSubmitAndClear).

Comment on lines +210 to +212
const newTool = toolRegistry.getTool(
`mcp__${this.serverName}__${this.serverToolName}`,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Look the rediscovered tool up by its normalized name.

DiscoveredMCPTool registers under generateValidName(...), but this lookup uses the raw mcp__${server}__${tool} string. Any server/tool name that needed sanitizing or truncation will never be found after rediscovery, so auto-reconnect silently falls back to the original failure.

Suggested fix
-      const newTool = toolRegistry.getTool(
-        `mcp__${this.serverName}__${this.serverToolName}`,
-      );
+      const newTool = toolRegistry.getTool(
+        generateValidName(`mcp__${this.serverName}__${this.serverToolName}`),
+      );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/tools/mcp-tool.ts` around lines 210 - 212, The lookup uses
the raw `mcp__${this.serverName}__${this.serverToolName}` string but
`DiscoveredMCPTool` registers under the normalized name from
`generateValidName(...)`, so replace the raw key with the normalized name when
calling `toolRegistry.getTool(...)`: compute the generated name via
`generateValidName(this.serverName, this.serverToolName)` (or call the same
normalization function used by `DiscoveredMCPTool`) and use that value to get
the tool (the `newTool` lookup) so rediscovered tools with sanitized/truncated
names are found.

Comment on lines +228 to +265
private async handleReconnectOnError(
error: unknown,
signal: AbortSignal,
updateOutput?: (output: ToolResultDisplay) => void,
): Promise<ToolResult> {
debugLogger.error(`MCP server error '${this.serverName}': ${error}`);

if (this.retryCount < DiscoveredMCPToolInvocation.MAX_RECONNECT_RETRIES) {
debugLogger.info(
`Reconnection attempt ${this.retryCount + 1}/${DiscoveredMCPToolInvocation.MAX_RECONNECT_RETRIES} for MCP server '${this.serverName}'`,
);
const newTool = await this.attemptReconnect();
if (newTool) {
const newInvocation = new DiscoveredMCPToolInvocation(
newTool['mcpTool'],
this.serverName,
this.serverToolName,
this.displayName,
this.trust,
this.params,
this.cliConfig,
newTool['mcpClient'],
this.mcpTimeout,
this.annotations,
this.retryCount + 1,
);
return newInvocation.execute(signal, updateOutput);
}
} else if (
this.retryCount >= DiscoveredMCPToolInvocation.MAX_RECONNECT_RETRIES
) {
debugLogger.error(
`Max reconnection attempts (${DiscoveredMCPToolInvocation.MAX_RECONNECT_RETRIES}) reached for MCP server '${this.serverName}'`,
);
}

throw error;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don't auto-retry every failed MCP invocation.

This path reconnects and re-executes the tool for any thrown error, including aborts and ordinary tool/runtime failures, and it does so without checking whether the tool is safe to replay. If the first call reached the server and only the transport failed on the way back, retrying a non-idempotent MCP tool can duplicate side effects. Restrict reconnect/retry to verified connection-loss cases and only auto-reexecute tools that are explicitly read-only or idempotent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/tools/mcp-tool.ts` around lines 228 - 265, The handler
handleReconnectOnError currently retries for any thrown error; change it to only
attempt reconnect/re-execution when the error is a verified connection-loss
(e.g., network/transport errors, timeouts, ECONNRESET) and the tool is known
safe to replay (check a read-only/idempotent flag on the MCP tool or
annotations, e.g., this.mcpTool.isReadOnly or this.annotations?.idempotent).
Also immediately rethrow when signal.aborted or when the error is not a
connection-loss, or when the tool is not marked safe to replay, so
non-idempotent or abort scenarios do not get retried; keep attemptReconnect and
the DiscoveredMCPToolInvocation re-execution only under the verified conditions.

Comment thread packages/core/src/tools/mcp-tool.ts Outdated
Comment on lines 47 to +55
"peerDependencies": {
"@qwen-code/qwen-code-core": ">=0.13.1",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@qwen-code/qwen-code-core": {
"optional": true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- package versions ---'
find packages -name package.json -print0 | xargs -0 -n1 jq -r '.name + "@" + .version'

echo
echo '--- core imports used by the followup entrypoint ---'
rg -n '@qwen-code/qwen-code-core' \
  packages/webui/src/followup.ts \
  packages/webui/src/hooks/useFollowupSuggestions.ts \
  packages/webui/vite.config.followup.ts

Repository: protoLabsAI/protoCLI

Length of output: 997


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== useFollowupSuggestions.ts imports ===" 
sed -n '1,30p' packages/webui/src/hooks/useFollowupSuggestions.ts

echo
echo "=== followup.ts imports ===" 
sed -n '1,30p' packages/webui/src/followup.ts

echo
echo "=== Checking if followup module has guards/optional logic ===" 
rg -A 5 -B 5 'try.*@qwen-code/qwen-code-core|optional.*@qwen-code/qwen-code-core|catch.*@qwen-code/qwen-code-core' packages/webui/src/

Repository: protoLabsAI/protoCLI

Length of output: 1619


Remove optional: true from the core peer dependency or refactor the hard imports to be conditional.

The /followup entrypoint has hard runtime imports from @qwen-code/qwen-code-core (specifically INITIAL_FOLLOWUP_STATE and createFollowupController in useFollowupSuggestions.ts). Setting optional: true suppresses npm install warnings despite these dependencies being required at runtime, contradicting the file's stated intent to avoid forcing installation. Additionally, the version constraint >=0.13.1 is loose for coordinated 0.x packages; with both @qwen-code/webui and @qwen-code/qwen-code-core at 0.14.0, use a tighter constraint like ^0.14.0 or >=0.14.0.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/webui/package.json` around lines 47 - 55, Remove "optional": true
for the `@qwen-code/qwen-code-core` peer dependency in package.json or make its
runtime imports conditional: either delete the peerDependenciesMeta entry so the
core peer dep is required and tighten the version constraint from ">=0.13.1" to
a coordinated range such as "^0.14.0" (or ">=0.14.0"), or if you truly want it
optional, refactor the hard imports in useFollowupSuggestions.ts (the symbols
INITIAL_FOLLOWUP_STATE and createFollowupController) to be dynamically
imported/guarded so the code only requires the module when present.

Comment on lines +247 to +285
// Tab to accept prompt suggestion (only when callback is wired)
if (
e.key === 'Tab' &&
hasFollowup &&
onAcceptFollowup &&
!inputText &&
!completionActive
) {
e.preventDefault();
e.stopPropagation();
onAcceptFollowup('tab');
return;
}
// Right arrow to accept prompt suggestion (only when callback is wired)
if (
e.key === 'ArrowRight' &&
hasFollowup &&
onAcceptFollowup &&
!inputText &&
!completionActive
) {
e.preventDefault();
onAcceptFollowup?.('right');
return;
}
// If composing (Chinese IME input), don't process Enter key
if (e.key === 'Enter' && !e.shiftKey && !isComposing) {
// If CompletionMenu is open, let it handle Enter key
if (completionActive) {
return;
}
// Accept and submit prompt suggestion on Enter when input is empty
if (hasFollowup && !inputText && followupSuggestion) {
e.preventDefault();
onAcceptFollowup?.('enter');
// Pass suggestion text explicitly — onInputChange is async (React setState)
// so onSubmit cannot rely on reading inputText from the closure.
onSubmit(e, followupSuggestion);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make follow-up acceptance available to non-keyboard submit paths.

This only wires Tab/ArrowRight/Enter. A visible follow-up still isn't submitted through the normal form path, and the send button remains tied to real draft text, so mouse/touch users can't accept the suggestion at all. Treat hasFollowup + empty input as submittable content and pass followupSuggestion through the regular submit handler too.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/webui/src/components/layout/InputForm.tsx` around lines 247 - 285,
The current code only accepts follow-up suggestions via keyboard handlers;
update the normal submit path so mouse/touch submissions accept a follow-up when
inputText is empty and hasFollowup is true: in the form submit handler
(onSubmit) and the send-button click handler, detect if hasFollowup &&
!inputText && followupSuggestion, then call onAcceptFollowup('submit' or
similar) and pass followupSuggestion into the existing onSubmit flow (i.e.,
invoke the same submit logic with followupSuggestion instead of relying on
inputText). Also ensure the send button enabled/disabled logic treats
hasFollowup+empty-as-submittable so the button is clickable for accepting
suggestions. Reference symbols: hasFollowup, followupSuggestion, onSubmit,
onAcceptFollowup, inputText, completionActive.

- ideCommand.test: remove install subcommand tests (no longer in ideCommand)
- constants.test: update DISPLAY_HOOK_EVENTS length from 12→15 (3 new hook events)
- Footer.test: mock VoiceMicButton to avoid UIActionsProvider dep, add voice
  state fields to mock UIState, update snapshots
- client.test: add getDisableAllHooks mock for hooks cleanup change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/cli/src/ui/commands/ideCommand.test.ts (1)

13-22: Remove unused getIdeInstaller mock.

The getIdeInstaller mock at line 17 is no longer used after the install subcommand tests were removed. This is dead code that should be cleaned up for clarity.

♻️ Proposed cleanup
 vi.mock('@qwen-code/qwen-code-core', async (importOriginal) => {
   const original = await importOriginal<typeof core>();
   return {
     ...original,
-    getIdeInstaller: vi.fn(original.getIdeInstaller),
     IdeClient: {
       getInstance: vi.fn(),
     },
   };
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/ui/commands/ideCommand.test.ts` around lines 13 - 22, The
getIdeInstaller mock is unused and should be removed from the vi.mock block: in
the mocked module return object remove the getIdeInstaller property (leaving the
spread of original and the mocked IdeClient.getInstance) so the mock only
defines what the tests actually use (e.g., IdeClient.getInstance) and avoid
introducing dead code; update the vi.mock callback to no longer reference
getIdeInstaller.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/cli/src/ui/components/hooks/constants.test.ts`:
- Around line 179-180: Update the test to remove the stale hardcoded count and
assert the DISPLAY_HOOK_EVENTS array is in sync with the HookEventName enum:
change the case description from "should have 12 events" to something like
"should match HookEventName count" and replace
expect(DISPLAY_HOOK_EVENTS).toHaveLength(15) with
expect(DISPLAY_HOOK_EVENTS).toHaveLength(Object.values(HookEventName).length),
referencing DISPLAY_HOOK_EVENTS and HookEventName to ensure the test stays
correct when the enum changes.

---

Nitpick comments:
In `@packages/cli/src/ui/commands/ideCommand.test.ts`:
- Around line 13-22: The getIdeInstaller mock is unused and should be removed
from the vi.mock block: in the mocked module return object remove the
getIdeInstaller property (leaving the spread of original and the mocked
IdeClient.getInstance) so the mock only defines what the tests actually use
(e.g., IdeClient.getInstance) and avoid introducing dead code; update the
vi.mock callback to no longer reference getIdeInstaller.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d67dedc5-1952-43c5-bed9-a7b3e03a0f0d

📥 Commits

Reviewing files that changed from the base of the PR and between db370e3 and 08d0453.

⛔ Files ignored due to path filters (1)
  • packages/cli/src/ui/components/__snapshots__/Footer.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • packages/cli/src/ui/commands/ideCommand.test.ts
  • packages/cli/src/ui/components/Footer.test.tsx
  • packages/cli/src/ui/components/hooks/constants.test.ts
  • packages/core/src/core/client.test.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/core/src/core/client.test.ts

Comment thread packages/cli/src/ui/components/hooks/constants.test.ts Outdated
@github-actions

github-actions Bot commented Apr 3, 2026

Copy link
Copy Markdown

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 55.23% 55.23% 67% 79.05%
Core 72.17% 72.17% 75.69% 80.99%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   55.23 |    79.05 |      67 |   55.23 |                   
 src               |   66.51 |     64.8 |   73.68 |   66.51 |                   
  gemini.tsx       |   60.91 |    63.15 |      75 |   60.91 | ...03,511-514,523 
  ...ractiveCli.ts |   58.33 |    58.06 |      50 |   58.33 | ...20,435,439-562 
  ...liCommands.ts |   83.77 |    69.76 |     100 |   83.77 | ...12,247,249,377 
  ...ActiveAuth.ts |   94.11 |    82.35 |     100 |   94.11 | 27-30             
 ...cp-integration |    5.61 |    33.33 |    12.5 |    5.61 |                   
  acpAgent.ts      |    2.86 |        0 |       0 |    2.86 | 71-125,128-625    
  authMethods.ts   |    11.9 |      100 |       0 |    11.9 | 11-32,35-39,42-51 
  errorCodes.ts    |       0 |        0 |       0 |       0 | 1-22              
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
 ...ration/service |   68.65 |    83.33 |   66.66 |   68.65 |                   
  filesystem.ts    |   68.65 |    83.33 |   66.66 |   68.65 | ...32,77-94,97-98 
 ...ration/session |   63.95 |    60.26 |    74.5 |   63.95 |                   
  ...ryReplayer.ts |      76 |    79.41 |      90 |      76 | ...19-220,228-229 
  Session.ts       |   56.15 |    47.76 |    62.5 |   56.15 | ...1416,1422-1425 
  ...entTracker.ts |   90.85 |    84.84 |      90 |   90.85 | ...35,199,251-260 
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   74.59 |    73.07 |     100 |   74.59 | ...73-189,204-206 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ssion/emitters |   92.48 |    87.12 |      92 |   92.48 |                   
  BaseEmitter.ts   |    82.6 |       75 |      80 |    82.6 | 23-24,50-51       
  ...ageEmitter.ts |     100 |    83.33 |     100 |     100 | 84-86             
  PlanEmitter.ts   |     100 |      100 |     100 |     100 |                   
  ...allEmitter.ts |   91.37 |    86.79 |     100 |   91.37 | ...28-229,318,326 
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
 src/commands      |   68.59 |      100 |   16.66 |   68.59 |                   
  auth.ts          |   46.55 |      100 |       0 |   46.55 | ...58,67-72,75-76 
  extensions.tsx   |   96.55 |      100 |      50 |   96.55 | 37                
  hooks.tsx        |   66.66 |      100 |       0 |   66.66 | 20-24             
  mcp.ts           |   94.73 |      100 |      50 |   94.73 | 28                
 src/commands/auth |    42.6 |    95.83 |      60 |    42.6 |                   
  handler.ts       |   27.37 |    94.44 |   14.28 |   27.37 | 55-394            
  ...veSelector.ts |     100 |    96.66 |     100 |     100 | 58                
 ...nds/extensions |   84.53 |    88.95 |   81.81 |   84.53 |                   
  consent.ts       |   71.65 |    89.28 |   42.85 |   71.65 | ...85-141,156-162 
  disable.ts       |     100 |      100 |     100 |     100 |                   
  enable.ts        |     100 |      100 |     100 |     100 |                   
  install.ts       |    75.6 |    66.66 |   66.66 |    75.6 | ...39-142,145-153 
  link.ts          |     100 |      100 |     100 |     100 |                   
  list.ts          |     100 |      100 |     100 |     100 |                   
  new.ts           |     100 |      100 |     100 |     100 |                   
  settings.ts      |   99.15 |      100 |   83.33 |   99.15 | 151               
  uninstall.ts     |    37.5 |      100 |   33.33 |    37.5 | 23-45,57-64,67-70 
  update.ts        |   96.32 |      100 |     100 |   96.32 | 101-105           
  utils.ts         |   60.24 |    28.57 |     100 |   60.24 | ...81,83-87,89-93 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 src/commands/mcp  |   91.28 |    82.97 |   88.88 |   91.28 |                   
  add.ts           |     100 |    96.66 |     100 |     100 | 213               
  list.ts          |   91.22 |    80.76 |      80 |   91.22 | ...19-121,146-147 
  reconnect.ts     |   76.72 |    71.42 |   85.71 |   76.72 | 35-48,153-175     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 src/config        |   90.86 |    80.26 |   84.12 |   90.86 |                   
  auth.ts          |   86.92 |    78.84 |     100 |   86.92 | ...98-199,215-216 
  config.ts        |   86.77 |    81.61 |   72.22 |   86.77 | ...1164,1186-1187 
  keyBindings.ts   |   95.87 |       50 |     100 |   95.87 | 159-162           
  ...idersScope.ts |      92 |       90 |     100 |      92 | 11-12             
  sandboxConfig.ts |   54.16 |    23.07 |   66.66 |   54.16 | ...44,54-68,73-89 
  settings.ts      |   80.69 |    82.05 |   81.81 |   80.69 | ...98-699,775-792 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...tedFolders.ts |   96.29 |       94 |     100 |   96.29 | ...88-190,205-206 
  webSearch.ts     |    40.9 |    22.22 |     100 |    40.9 | ...95-102,105-121 
 ...nfig/migration |   94.56 |    78.94 |   83.33 |   94.56 |                   
  index.ts         |   93.93 |    88.88 |     100 |   93.93 | 85-86             
  scheduler.ts     |   96.55 |    77.77 |     100 |   96.55 | 19-20             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ation/versions |   93.63 |     94.5 |     100 |   93.63 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |    90.19 |     100 |   81.75 | ...28-229,231-247 
  v2-to-v3.ts      |     100 |      100 |     100 |     100 |                   
 src/constants     |   93.02 |    94.11 |   66.66 |   93.02 |                   
  ...dardApiKey.ts |     100 |      100 |     100 |     100 |                   
  codingPlan.ts    |   92.74 |    94.11 |   66.66 |   92.74 | ...00-301,309-321 
 src/core          |   51.51 |    33.33 |   33.33 |   51.51 |                   
  auth.ts          |    9.52 |      100 |       0 |    9.52 | 21-48             
  initializer.ts   |   84.37 |    33.33 |     100 |   84.37 | 41,55-58          
  theme.ts         |   38.46 |      100 |       0 |   38.46 | 17-24             
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 src/i18n          |   43.69 |    76.19 |   38.88 |   43.69 |                   
  index.ts         |   26.11 |    76.92 |   26.66 |   26.11 | ...35-236,246-257 
  languages.ts     |   98.27 |       75 |     100 |   98.27 | 88                
 src/i18n/locales  |       0 |        0 |       0 |       0 |                   
  de.js            |       0 |        0 |       0 |       0 | 1-1965            
  en.js            |       0 |        0 |       0 |       0 | 1-2006            
  ja.js            |       0 |        0 |       0 |       0 | 1-1459            
  pt.js            |       0 |        0 |       0 |       0 | 1-1957            
  ru.js            |       0 |        0 |       0 |       0 | 1-1964            
  zh.js            |       0 |        0 |       0 |       0 | 1-1811            
 ...nonInteractive |   68.34 |    71.68 |   68.88 |   68.34 |                   
  session.ts       |    73.1 |    69.52 |   81.81 |    73.1 | ...03-604,612-622 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...74-575,578-579 
 ...active/control |   77.48 |       88 |      80 |   77.48 |                   
  ...rolContext.ts |    7.69 |        0 |       0 |    7.69 | 47-79             
  ...Dispatcher.ts |   91.63 |    91.66 |   88.88 |   91.63 | ...54-372,387,390 
  ...rolService.ts |       8 |        0 |       0 |       8 | 46-179            
 ...ol/controllers |    7.32 |       80 |   13.79 |    7.32 |                   
  ...Controller.ts |   19.32 |      100 |      60 |   19.32 | 81-118,127-210    
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |    3.96 |      100 |   11.11 |    3.96 | ...61-379,389-494 
  ...Controller.ts |   14.06 |      100 |       0 |   14.06 | ...82-117,130-133 
  ...Controller.ts |    5.72 |      100 |       0 |    5.72 | ...72-384,393-418 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |   97.57 |    92.91 |   96.15 |   97.57 |                   
  ...putAdapter.ts |    97.3 |    91.75 |     100 |    97.3 | ...1272,1297-1298 
  ...putAdapter.ts |      96 |    91.66 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.23 |      100 |   89.47 |   98.23 | 70-71,111-112     
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/services      |   89.39 |    87.61 |   96.87 |   89.39 |                   
  ...mandLoader.ts |     100 |      100 |     100 |     100 |                   
  ...killLoader.ts |     100 |      100 |     100 |     100 |                   
  ...andService.ts |     100 |      100 |     100 |     100 |                   
  ...mandLoader.ts |   86.61 |    82.14 |     100 |   86.61 | ...29-334,339-344 
  ...omptLoader.ts |    75.1 |    80.64 |   83.33 |    75.1 | ...03-204,270-271 
  audioCapture.ts  |    98.5 |    93.75 |     100 |    98.5 | 76                
  ...nd-factory.ts |    91.2 |    93.33 |     100 |    91.2 | 119-126           
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  ...and-parser.ts |   89.74 |    85.71 |     100 |   89.74 | 59-62             
  sttClient.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |   85.95 |    86.42 |   90.47 |   85.95 |                   
  DataProcessor.ts |   85.68 |    86.46 |   92.85 |   85.68 | ...1110,1114-1121 
  ...tGenerator.ts |   98.21 |    85.71 |     100 |   98.21 | 46                
  ...teRenderer.ts |   45.45 |      100 |       0 |   45.45 | 13-51             
 .../insight/types |       0 |       50 |      50 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 | 1                 
 ...mpt-processors |   97.27 |    94.04 |     100 |   97.27 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...eProcessor.ts |   94.52 |    84.21 |     100 |   94.52 | 46-47,93-94       
  ...tionParser.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.41 |    95.65 |     100 |   97.41 | 95-98             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/test-utils    |   93.63 |    83.33 |      80 |   93.63 |                   
  ...omMatchers.ts |   69.69 |       50 |      50 |   69.69 | 32-35,37-39,45-47 
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   60.69 |    62.75 |   45.94 |   60.69 |                   
  App.tsx          |     100 |      100 |     100 |     100 |                   
  AppContainer.tsx |   63.38 |    53.64 |   42.85 |   63.38 | ...1684,2069-2075 
  ...tionNudge.tsx |    9.58 |      100 |       0 |    9.58 | 24-94             
  ...ackDialog.tsx |   29.23 |      100 |       0 |   29.23 | 25-75             
  ...tionNudge.tsx |    7.69 |      100 |       0 |    7.69 | 25-103            
  colors.ts        |   52.72 |      100 |   23.52 |   52.72 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   91.83 |    88.46 |     100 |   91.83 | 25-26,54-55       
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   29.96 |       50 |   26.08 |   29.96 |                   
  AuthDialog.tsx   |   52.67 |    51.16 |   28.57 |   52.67 | ...66,685,687,689 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  useAuth.ts       |    2.47 |      100 |       0 |    2.47 | 48-612            
 src/ui/commands   |   54.64 |    79.67 |    45.5 |   54.64 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  agentsCommand.ts |    64.7 |      100 |       0 |    64.7 | ...30,35-36,39-41 
  ...odeCommand.ts |     100 |      100 |     100 |     100 |                   
  arenaCommand.ts  |   32.65 |    67.64 |    37.5 |   32.65 | ...52-557,636-641 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |     100 |    90.32 |     100 |     100 | 25,199,213        
  bugCommand.ts    |   76.47 |    66.66 |      50 |   76.47 | 21-22,57-66       
  clearCommand.ts  |   89.65 |    55.55 |      50 |   89.65 | 23-24,49-50,68-69 
  ...essCommand.ts |   63.15 |       50 |      50 |   63.15 | ...47-148,162-165 
  ...extCommand.ts |    4.65 |      100 |       0 |    4.65 | ...8,93-96,99-375 
  copyCommand.ts   |   96.22 |      100 |      50 |   96.22 | 15-16             
  ...ryCommand.tsx |   59.19 |    73.07 |    37.5 |   59.19 | ...15-216,224-232 
  docsCommand.ts   |   95.23 |       80 |      50 |   95.23 | 20-21             
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |   55.97 |    91.66 |   33.33 |   55.97 | ...48-349,356-357 
  ...onsCommand.ts |   44.09 |    85.71 |   27.27 |   44.09 | ...35-236,244-245 
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |   42.72 |       60 |   33.33 |   42.72 | ...86-116,123-124 
  ideCommand.ts    |   41.91 |    69.23 |   21.42 |   41.91 | ...08-209,212-226 
  initCommand.ts   |    81.7 |       70 |      50 |    81.7 | ...67,81-86,88-93 
  ...ghtCommand.ts |   69.23 |       40 |   66.66 |   69.23 | ...97-111,116-129 
  ...ageCommand.ts |   89.24 |    82.35 |   76.92 |   89.24 | ...20-323,345-346 
  mcpCommand.ts    |   85.71 |      100 |      50 |   85.71 | 14-15             
  memoryCommand.ts |   57.42 |    84.61 |      30 |   57.42 | ...99,404-405,423 
  modelCommand.ts  |   57.31 |    81.81 |   66.66 |   57.31 | 24-33,51-77       
  ...onsCommand.ts |     100 |      100 |     100 |     100 |                   
  quitCommand.ts   |   93.75 |      100 |      50 |   93.75 | 15-16             
  ...oreCommand.ts |   92.24 |     87.5 |     100 |   92.24 | ...,83-88,129-130 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   80.12 |    63.63 |      60 |   80.12 | ...69-172,175-178 
  skillsCommand.ts |   12.28 |      100 |       0 |   12.28 | ...91-109,112-139 
  statsCommand.ts  |   76.92 |       75 |      50 |   76.92 | ...36,50-51,65-66 
  ...aryCommand.ts |    4.61 |      100 |       0 |    4.61 | 21-24,27-322      
  teamCommand.ts   |   17.79 |      100 |       0 |   17.79 | ...29-230,233-257 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  toolsCommand.ts  |   95.12 |      100 |      50 |   95.12 | 18-19             
  trustCommand.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  vimCommand.ts    |   42.85 |      100 |       0 |   42.85 | 14-15,18-28       
  voiceCommand.ts  |   37.28 |      100 |       0 |   37.28 | 32-54,58-76       
 src/ui/components |   61.98 |    72.97 |   63.35 |   61.98 |                   
  AboutBox.tsx     |     100 |      100 |     100 |     100 |                   
  AnsiOutput.tsx   |     100 |      100 |     100 |     100 |                   
  ApiKeyInput.tsx  |   18.91 |      100 |       0 |   18.91 | 30-95             
  AppHeader.tsx    |   87.03 |    42.85 |     100 |   87.03 | 33-39,41          
  ...odeDialog.tsx |     9.7 |      100 |       0 |     9.7 | 35-47,50-182      
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   13.95 |      100 |       0 |   13.95 | 18-58             
  ...TextInput.tsx |   63.22 |    70.27 |      50 |   63.22 | ...12,220-222,240 
  Composer.tsx     |   89.89 |    58.33 |      50 |   89.89 | ...-55,73,112,125 
  ...itDisplay.tsx |   55.81 |      100 |      50 |   55.81 | 22-38,42-43       
  ...entPrompt.tsx |     100 |      100 |     100 |     100 |                   
  ...ryDisplay.tsx |   75.89 |    62.06 |     100 |   75.89 | ...,88,93-108,113 
  ...geDisplay.tsx |   90.47 |       75 |     100 |   90.47 | 20-21             
  ...ification.tsx |   28.57 |      100 |       0 |   28.57 | 16-36             
  ...gProfiler.tsx |       0 |        0 |       0 |       0 | 1-36              
  ...ogManager.tsx |   12.69 |      100 |       0 |   12.69 | 54-379            
  ...ngsDialog.tsx |    8.44 |      100 |       0 |    8.44 | 37-195            
  ExitWarning.tsx  |     100 |      100 |     100 |     100 |                   
  ...ustDialog.tsx |     100 |      100 |     100 |     100 |                   
  Footer.tsx       |   74.31 |       35 |     100 |   74.31 | ...74,83-87,89-93 
  ...ngSpinner.tsx |   54.28 |       50 |      50 |   54.28 | 31-48,61          
  Header.tsx       |   94.28 |    76.92 |     100 |   94.28 | 96,98,103-106     
  Help.tsx         |    98.7 |    68.75 |     100 |    98.7 | 74,129            
  ...emDisplay.tsx |    68.9 |     42.1 |     100 |    68.9 | ...19-225,228,231 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   79.17 |    75.08 |     100 |   79.17 | ...1305-1321,1363 
  ...Shortcuts.tsx |   21.11 |      100 |       0 |   21.11 | ...5,48-50,66-124 
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  ...firmation.tsx |   91.42 |      100 |      50 |   91.42 | 26-31             
  MainContent.tsx  |   18.46 |      100 |       0 |   18.46 | 24-80             
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   73.26 |    43.07 |     100 |   73.26 | ...75-484,490-494 
  ...tsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...fications.tsx |   18.18 |      100 |       0 |   18.18 | 15-58             
  ...onsDialog.tsx |    2.18 |      100 |       0 |    2.18 | 62-133,148-986    
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...icePrompt.tsx |   88.14 |    83.87 |     100 |   88.14 | ...01-105,133-138 
  PrepareLabel.tsx |   91.66 |    76.19 |     100 |   91.66 | 73-75,77-79,110   
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |   21.42 |      100 |       0 |   21.42 | 13-39             
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...ionPicker.tsx |   94.18 |    92.85 |     100 |   94.18 | 79,194-202        
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...putPrompt.tsx |   72.56 |       80 |      40 |   72.56 | ...06-109,114-117 
  ...ngsDialog.tsx |   66.75 |    72.66 |     100 |   66.75 | ...72-780,786-787 
  ...ionDialog.tsx |    87.8 |      100 |   33.33 |    87.8 | 36-39,44-51       
  ...putPrompt.tsx |    15.9 |      100 |       0 |    15.9 | 20-63             
  ...Indicator.tsx |   44.44 |      100 |       0 |   44.44 | 12-17             
  ...MoreLines.tsx |      28 |      100 |       0 |      28 | 18-40             
  ...ionPicker.tsx |    7.86 |      100 |       0 |    7.86 | 21-131            
  StatsDisplay.tsx |   98.66 |    93.33 |     100 |   98.66 | 199-201           
  ...nsDisplay.tsx |   84.09 |    57.14 |     100 |   84.09 | ...16-118,125-127 
  ...ffDisplay.tsx |   18.91 |      100 |       0 |   18.91 | 16-18,25-57       
  ThemeDialog.tsx  |   90.95 |    44.44 |      75 |   90.95 | ...16-117,159-161 
  Tips.tsx         |      75 |       60 |      75 |      75 | 23,48-49,52-62    
  TodoDisplay.tsx  |     100 |      100 |     100 |     100 |                   
  ...tsDisplay.tsx |     100 |     87.5 |     100 |     100 | 31-32             
  TrustDialog.tsx  |     100 |    81.81 |     100 |     100 | 71-86             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...MicButton.tsx |   10.52 |      100 |       0 |   10.52 | 28-96             
  ...ackDialog.tsx |    7.84 |      100 |       0 |    7.84 | 24-134            
 ...nts/agent-view |   25.14 |    89.36 |    12.5 |   25.14 |                   
  ...tChatView.tsx |    8.42 |      100 |       0 |    8.42 | 53-272            
  ...tComposer.tsx |    9.95 |      100 |       0 |    9.95 | 57-308            
  AgentFooter.tsx  |   17.07 |      100 |       0 |   17.07 | 28-66             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |    8.25 |      100 |       0 |    8.25 | 35-55,60-167      
  ...oryAdapter.ts |     100 |     91.3 |     100 |     100 | 102,108-109,137   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |    5.92 |      100 |       0 |    5.92 |                   
  ArenaCards.tsx   |       4 |      100 |       0 |       4 | 24-129,134-290    
  ...ectDialog.tsx |    5.28 |      100 |       0 |    5.28 | 32-260            
  ...artDialog.tsx |   10.15 |      100 |       0 |   10.15 | 27-161            
  ...tusDialog.tsx |    5.63 |      100 |       0 |    5.63 | 33-75,80-288      
  ...topDialog.tsx |    6.17 |      100 |       0 |    6.17 | 33-213            
 ...nts/extensions |   45.28 |    33.33 |      60 |   45.28 |                   
  ...gerDialog.tsx |   44.31 |    34.14 |      75 |   44.31 | ...71-480,483-488 
  index.ts         |       0 |        0 |       0 |       0 | 1-9               
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...tensions/steps |   54.77 |    94.23 |   66.66 |   54.77 |                   
  ...ctionStep.tsx |   95.12 |    92.85 |   85.71 |   95.12 | 84-86,89          
  ...etailStep.tsx |    6.18 |      100 |       0 |    6.18 | 17-128            
  ...nListStep.tsx |   88.35 |    94.73 |      80 |   88.35 | 51-52,58-71,105   
  ...electStep.tsx |   13.46 |      100 |       0 |   13.46 | 20-70             
  ...nfirmStep.tsx |   19.56 |      100 |       0 |   19.56 | 23-65             
  index.ts         |     100 |      100 |     100 |     100 |                   
 ...mponents/hooks |   78.71 |    70.53 |      85 |   78.71 |                   
  ...etailStep.tsx |   96.52 |       75 |     100 |   96.52 | 33,37,50,59       
  ...etailStep.tsx |   98.13 |    82.35 |     100 |   98.13 | 41-42             
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   48.83 |    52.83 |   66.66 |   48.83 | ...06,410-423,427 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   18.82 |    84.37 |   77.77 |   18.82 |                   
  ...entDialog.tsx |    3.64 |      100 |       0 |    3.64 | 41-717            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-30              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   96.42 |    87.09 |     100 |   96.42 | 21,96-97          
 ...ents/mcp/steps |    7.31 |      100 |       0 |    7.31 |                   
  ...icateStep.tsx |    7.58 |      100 |       0 |    7.58 | 27-197            
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |    5.26 |      100 |       0 |    5.26 | 31-247            
  ...rListStep.tsx |    5.88 |      100 |       0 |    5.88 | 20-176            
  ...etailStep.tsx |   10.41 |      100 |       0 |   10.41 | ...1,67-79,82-139 
  ToolListStep.tsx |    7.14 |      100 |       0 |    7.14 | 16-146            
 ...nents/messages |   73.63 |    75.88 |   59.18 |   73.63 |                   
  ...ionDialog.tsx |   65.11 |       73 |   42.85 |   65.11 | ...20,538,556-558 
  BtwMessage.tsx   |   76.31 |       50 |     100 |   76.31 | 34-42             
  ...onMessage.tsx |   91.93 |    82.35 |     100 |   91.93 | 57-59,61,63       
  ...nMessages.tsx |   77.35 |      100 |      70 |   77.35 | ...31-244,248-260 
  DiffRenderer.tsx |   93.19 |    86.17 |     100 |   93.19 | ...09,237-238,304 
  ...ssMessage.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...sMessages.tsx |   16.17 |      100 |       0 |   16.17 | ...1,85-95,99-104 
  ...ryMessage.tsx |   12.82 |      100 |       0 |   12.82 | 22-59             
  ...onMessage.tsx |   73.07 |    55.81 |   33.33 |   73.07 | ...34-436,443-445 
  ...upMessage.tsx |   95.09 |     87.5 |     100 |   95.09 | 40-43,55          
  ToolMessage.tsx  |   82.07 |    71.95 |      90 |   82.07 | ...13-418,492-494 
 ...ponents/shared |   77.03 |    76.86 |   90.56 |   77.03 |                   
  ...ctionList.tsx |   99.03 |    95.65 |     100 |   99.03 | 85                
  ...tonSelect.tsx |   97.14 |    66.66 |     100 |   97.14 | 72                
  EnumSelector.tsx |     100 |    96.42 |     100 |     100 | 58                
  MaxSizedBox.tsx  |   81.13 |    81.96 |   88.88 |   81.13 | ...12-513,618-619 
  MultiSelect.tsx  |    5.59 |      100 |       0 |    5.59 | 34-41,44-193      
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  TextInput.tsx    |    7.14 |      100 |       0 |    7.14 | 39-222            
  text-buffer.ts   |   82.07 |    75.59 |   96.87 |   82.07 | ...1897,1924,1986 
  ...er-actions.ts |   86.71 |    67.79 |     100 |   86.71 | ...07-608,809-811 
 ...ents/subagents |    32.1 |      100 |       0 |    32.1 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  reducers.tsx     |    12.1 |      100 |       0 |    12.1 | 33-190            
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   10.95 |      100 |       0 |   10.95 | ...1,56-57,60-102 
 ...bagents/create |    9.13 |      100 |       0 |    9.13 |                   
  ...ionWizard.tsx |    7.28 |      100 |       0 |    7.28 | 34-299            
  ...rSelector.tsx |   14.75 |      100 |       0 |   14.75 | 26-85             
  ...onSummary.tsx |    4.26 |      100 |       0 |    4.26 | 27-331            
  ...tionInput.tsx |    8.63 |      100 |       0 |    8.63 | 23-177            
  ...dSelector.tsx |   33.33 |      100 |       0 |   33.33 | 20-21,26-27,36-63 
  ...nSelector.tsx |    37.5 |      100 |       0 |    37.5 | 20-21,26-27,36-58 
  ...EntryStep.tsx |   12.76 |      100 |       0 |   12.76 | 34-78             
  ToolSelector.tsx |    4.16 |      100 |       0 |    4.16 | 31-253            
 ...bagents/manage |    8.43 |      100 |       0 |    8.43 |                   
  ...ctionStep.tsx |   10.25 |      100 |       0 |   10.25 | 21-103            
  ...eleteStep.tsx |   20.93 |      100 |       0 |   20.93 | 23-62             
  ...tEditStep.tsx |   25.53 |      100 |       0 |   25.53 | ...2,37-38,51-124 
  ...ctionStep.tsx |    2.29 |      100 |       0 |    2.29 | 28-449            
  ...iewerStep.tsx |   15.21 |      100 |       0 |   15.21 | 18-66             
  ...gerDialog.tsx |    6.74 |      100 |       0 |    6.74 | 35-341            
 ...agents/runtime |    7.83 |      100 |       0 |    7.83 |                   
  ...onDisplay.tsx |    7.83 |      100 |       0 |    7.83 | ...72-502,511-549 
 ...mponents/views |   48.59 |    69.23 |      30 |   48.59 |                   
  ContextUsage.tsx |    5.07 |      100 |       0 |    5.07 | ...30-145,148-424 
  ...sionsList.tsx |   87.69 |    73.68 |     100 |   87.69 | 65-72             
  McpStatus.tsx    |   89.53 |    60.52 |     100 |   89.53 | ...72,175-177,262 
  SkillsList.tsx   |   27.27 |      100 |       0 |   27.27 | 18-35             
  ToolsList.tsx    |     100 |      100 |     100 |     100 |                   
 src/ui/contexts   |   77.16 |    80.45 |    87.5 |   77.16 |                   
  ...ewContext.tsx |   65.77 |      100 |      75 |   65.77 | ...22-225,231-241 
  AppContext.tsx   |      40 |      100 |       0 |      40 | 17-22             
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   85.05 |    83.75 |     100 |   85.05 | ...1033,1038-1040 
  ...owContext.tsx |   89.28 |       80 |   66.66 |   89.28 | 34,47-48,60-62    
  ...onContext.tsx |   47.02 |     62.5 |   71.42 |   47.02 | ...36-239,243-246 
  ...gsContext.tsx |   83.33 |       50 |     100 |   83.33 | 17-18             
  ...usContext.tsx |     100 |      100 |     100 |     100 |                   
  ...ngContext.tsx |   71.42 |       50 |     100 |   71.42 | 17-20             
  ...nsContext.tsx |   89.47 |       50 |     100 |   89.47 | 114-115           
  ...teContext.tsx |   85.71 |       50 |     100 |   85.71 | 161-162           
  ...deContext.tsx |   76.08 |    72.72 |     100 |   76.08 | 47-48,52-59,77-78 
 src/ui/editors    |   93.33 |    85.71 |   66.66 |   93.33 |                   
  ...ngsManager.ts |   93.33 |    85.71 |   66.66 |   93.33 | 49,63-64          
 src/ui/hooks      |   79.39 |    81.09 |   83.01 |   79.39 |                   
  ...dProcessor.ts |   83.02 |     81.9 |     100 |   83.02 | ...86-387,406-433 
  keyToAnsi.ts     |    3.92 |      100 |       0 |    3.92 | 19-77             
  ...dProcessor.ts |   94.77 |    70.58 |     100 |   94.77 | ...75-276,281-282 
  ...dProcessor.ts |   76.16 |    58.87 |   66.66 |   76.16 | ...81,705,724-728 
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-158            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...odeCommand.ts |   58.82 |      100 |     100 |   58.82 | 28,33-48          
  ...enaCommand.ts |      85 |      100 |     100 |      85 | 23-24,29          
  ...aInProcess.ts |   19.81 |    66.66 |      25 |   19.81 | 57-175            
  ...Completion.ts |   92.77 |    89.09 |     100 |   92.77 | ...86-187,220-223 
  ...ifications.ts |   88.05 |    94.73 |     100 |   88.05 | 84-93             
  ...tIndicator.ts |     100 |    93.75 |     100 |     100 | 63                
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-38             
  ...lanUpdates.ts |     100 |       92 |     100 |     100 | 59,158            
  ...ompletion.tsx |   94.87 |    80.55 |     100 |   94.87 | ...02-203,205-206 
  ...dMigration.ts |   90.62 |       75 |     100 |   90.62 | 38-40             
  useCompletion.ts |    92.4 |     87.5 |     100 |    92.4 | 68-69,93-94,98-99 
  ...ialogClose.ts |   22.22 |      100 |     100 |   22.22 | 67-109            
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...ionUpdates.ts |   93.45 |     92.3 |     100 |   93.45 | ...83-287,300-306 
  ...agerDialog.ts |   88.88 |      100 |     100 |   88.88 | 21,25             
  ...backDialog.ts |   50.37 |    77.77 |   33.33 |   50.37 | ...58-174,195-196 
  useFocus.ts      |     100 |      100 |     100 |     100 |                   
  ...olderTrust.ts |     100 |      100 |     100 |     100 |                   
  ...ggestions.tsx |   89.15 |     64.7 |     100 |   89.15 | ...19-121,146-147 
  ...miniStream.ts |   72.88 |    71.42 |      80 |   72.88 | ...1773,1782-1784 
  ...BranchName.ts |    90.9 |     92.3 |     100 |    90.9 | 19-20,55-58       
  ...oryManager.ts |   98.41 |    93.33 |     100 |   98.41 | 43                
  ...ooksDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...stListener.ts |     100 |      100 |     100 |     100 |                   
  ...nAuthError.ts |   76.19 |       50 |     100 |   76.19 | 39-40,43-45       
  ...putHistory.ts |   92.59 |    85.71 |     100 |   92.59 | 63-64,72,94-96    
  ...storyStore.ts |     100 |    94.11 |     100 |     100 | 69                
  useKeypress.ts   |     100 |      100 |     100 |     100 |                   
  ...rdProtocol.ts |   36.36 |      100 |       0 |   36.36 | 24-31             
  ...unchEditor.ts |    8.97 |      100 |       0 |    8.97 | 20-67,74-125      
  ...gIndicator.ts |     100 |      100 |     100 |     100 |                   
  useLogger.ts     |   21.05 |      100 |       0 |   21.05 | 15-37             
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...delCommand.ts |     100 |       75 |     100 |     100 | 22                
  ...raseCycler.ts |   84.48 |    76.47 |     100 |   84.48 | ...47,50-51,67-69 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |   85.06 |    94.73 |     100 |   85.06 | ...05-208,295-305 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...umeCommand.ts |   94.54 |    68.75 |     100 |   94.54 | 60-61,79          
  ...ompletion.tsx |   90.59 |    83.33 |     100 |   90.59 | ...01,104,137-140 
  ...ectionList.ts |   96.59 |    94.62 |     100 |   96.59 | ...82-183,237-240 
  ...sionPicker.ts |   91.66 |    72.34 |     100 |   91.66 | ...45-246,250-251 
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |   91.74 |    79.41 |     100 |   91.74 | ...74,122-123,133 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-73              
  ...Completion.ts |   80.97 |     84.4 |   91.66 |   80.97 | ...69-471,479-487 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...rminalSize.ts |   76.19 |      100 |      50 |   76.19 | 21-25             
  ...emeCommand.ts |    8.04 |      100 |       0 |    8.04 | 25-112            
  useTimer.ts      |   88.09 |    85.71 |     100 |   88.09 | 44-45,51-53       
  ...lMigration.ts |       0 |        0 |       0 |       0 |                   
  ...rustModify.ts |     100 |      100 |     100 |     100 |                   
  useVoice.ts      |     100 |    81.25 |     100 |     100 | 30,39,62          
  ...elcomeBack.ts |   69.44 |    54.54 |     100 |   69.44 | ...85,89-90,96-98 
  vim.ts           |   83.54 |     79.5 |     100 |   83.54 | ...44,748-756,765 
 src/ui/layouts    |   77.31 |    38.46 |     100 |   77.31 |                   
  ...AppLayout.tsx |   79.36 |       40 |     100 |   79.36 | 38-40,46-53,71-73 
  ...AppLayout.tsx |   73.52 |    33.33 |     100 |   73.52 | 30-35,37-39       
 src/ui/models     |   80.24 |    79.16 |   71.42 |   80.24 |                   
  ...ableModels.ts |   80.24 |    79.16 |   71.42 |   80.24 | ...,61-71,123-125 
 ...noninteractive |     100 |      100 |    7.69 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |    7.69 |     100 |                   
 src/ui/state      |   94.91 |    81.81 |     100 |   94.91 |                   
  extensions.ts    |   94.91 |    81.81 |     100 |   94.91 | 68-69,88          
 src/ui/themes     |      99 |    58.66 |     100 |      99 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  color-utils.ts   |     100 |      100 |     100 |     100 |                   
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  qwen-dark.ts     |     100 |      100 |     100 |     100 |                   
  qwen-light.ts    |     100 |      100 |     100 |     100 |                   
  ...tic-tokens.ts |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-manager.ts |   87.08 |    79.68 |     100 |   87.08 | ...03-312,317-318 
  theme.ts         |     100 |    28.98 |     100 |     100 | 272-461           
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   67.95 |    83.56 |   78.16 |   67.95 |                   
  ...Colorizer.tsx |   82.78 |    88.23 |     100 |   82.78 | ...10-111,197-223 
  ...nRenderer.tsx |   59.31 |    38.23 |     100 |   59.31 | ...33-139,149-151 
  ...wnDisplay.tsx |   85.75 |    88.05 |     100 |   85.75 | ...76-284,317-342 
  QueryGuard.ts    |   85.71 |      100 |   66.66 |   85.71 | 61-62,65-66       
  ...eRenderer.tsx |   77.88 |    76.19 |     100 |   77.88 | 54-82             
  ...boardUtils.ts |   59.61 |    58.82 |     100 |   59.61 | ...,86-88,107-149 
  commandUtils.ts  |   93.54 |    88.63 |     100 |   93.54 | ...43,147,149-150 
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  displayUtils.ts  |   60.46 |      100 |      50 |   60.46 | 19-35             
  formatters.ts    |    94.8 |    98.07 |     100 |    94.8 | 101-104           
  highlight.ts     |   98.63 |       95 |     100 |   98.63 | 93                
  isNarrowWidth.ts |     100 |      100 |     100 |     100 |                   
  ...olDetector.ts |    7.22 |      100 |       0 |    7.22 | ...24-125,128-129 
  layoutUtils.ts   |     100 |      100 |     100 |     100 |                   
  ...nUtilities.ts |   69.84 |    85.71 |     100 |   69.84 | 75-91,100-101     
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  ...storyUtils.ts |   59.68 |    67.69 |      90 |   59.68 | ...40,388,393-415 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   96.52 |    94.44 |    87.5 |   96.52 | 19-20,148-149     
  updateCheck.ts   |     100 |    80.95 |     100 |     100 | 30-42             
 ...i/utils/export |    2.38 |        0 |       0 |    2.38 |                   
  collect.ts       |    0.87 |      100 |       0 |    0.87 | 40-394,401-697    
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |    1.23 |      100 |       0 |    1.23 | 17-336            
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |      40 |      100 |       0 |      40 | 11-13             
 ...ort/formatters |    3.38 |      100 |       0 |    3.38 |                   
  html.ts          |    9.61 |      100 |       0 |    9.61 | ...28,34-76,82-84 
  json.ts          |      50 |      100 |       0 |      50 | 14-15             
  jsonl.ts         |     3.5 |      100 |       0 |     3.5 | 14-76             
  markdown.ts      |    0.94 |      100 |       0 |    0.94 | 13-295            
 src/utils         |   68.76 |    89.64 |    93.7 |   68.76 |                   
  acpModelUtils.ts |     100 |      100 |     100 |     100 |                   
  ...tification.ts |   92.59 |    71.42 |     100 |   92.59 | 36-37             
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  cleanup.ts       |   65.38 |      100 |   66.66 |   65.38 | 28-37             
  commands.ts      |     100 |      100 |     100 |     100 |                   
  commentJson.ts   |     100 |    93.75 |     100 |     100 | 30                
  deepMerge.ts     |     100 |       90 |     100 |     100 | 41-43,49          
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  ...arResolver.ts |   94.28 |    88.46 |     100 |   94.28 | 28-29,125-126     
  errors.ts        |   98.27 |       95 |     100 |   98.27 | 44-45             
  events.ts        |     100 |      100 |     100 |     100 |                   
  gitUtils.ts      |   91.91 |    84.61 |     100 |   91.91 | 78-81,124-127     
  ...AutoUpdate.ts |    51.2 |       90 |      50 |    51.2 | 87-152            
  ...lationInfo.ts |     100 |      100 |     100 |     100 |                   
  languageUtils.ts |   97.87 |    96.42 |     100 |   97.87 | 132-133           
  math.ts          |       0 |        0 |       0 |       0 | 1-15              
  ...onfigUtils.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   96.84 |    93.28 |     100 |   96.84 | ...87-488,586,599 
  package.ts       |   88.88 |       80 |     100 |   88.88 | 33-34             
  processUtils.ts  |     100 |      100 |     100 |     100 |                   
  readStdin.ts     |   79.62 |       90 |      80 |   79.62 | 33-40,52-54       
  relaunch.ts      |   98.07 |    76.92 |     100 |   98.07 | 70                
  resolvePath.ts   |   66.66 |       25 |     100 |   66.66 | 12-13,16,18-19    
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-984             
  settingsUtils.ts |   86.32 |    90.59 |   94.44 |   86.32 | ...38,569,632-644 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       60 |     100 |     100 | 23,32             
  systemInfo.ts    |      99 |     90.9 |     100 |      99 | 172               
  ...InfoFields.ts |   86.91 |    65.78 |     100 |   86.91 | ...16-117,138-139 
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |   91.17 |    82.35 |     100 |   91.17 | 67-68,73-74,77-78 
  version.ts       |     100 |       50 |     100 |     100 | 11                
  windowTitle.ts   |     100 |      100 |     100 |     100 |                   
  ...WithBackup.ts |    62.1 |    77.77 |     100 |    62.1 | 93,107,118-157    
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   72.17 |    80.99 |   75.69 |   72.17 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |       0 |        0 |       0 |       0 |                   
  promises.ts      |       0 |        0 |       0 |       0 | 1-48              
 src/agents        |   27.89 |     90.9 |   35.71 |   27.89 |                   
  ...ound-store.ts |   97.95 |     90.9 |     100 |   97.95 | 61                
  index.ts         |     100 |      100 |     100 |     100 |                   
  mailbox.ts       |    5.71 |      100 |       0 |    5.71 | 38-145            
  team-config.ts   |    6.48 |      100 |       0 |    6.48 | 43-176            
 src/agents/arena  |   64.56 |    66.66 |   68.49 |   64.56 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |    61.9 |    63.09 |   67.27 |    61.9 | ...1611,1620-1630 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   76.77 |    85.23 |    72.6 |   76.77 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |   92.81 |     85.5 |   82.85 |   92.81 | ...15-235,294,390 
  TmuxBackend.ts   |    90.7 |    76.55 |   97.36 |    90.7 | ...87,697,743-747 
  detect.ts        |   31.25 |      100 |       0 |   31.25 | 34-88             
  index.ts         |     100 |      100 |     100 |     100 |                   
  iterm-it2.ts     |     100 |     92.1 |     100 |     100 | 37-38,106         
  tmux-commands.ts |    6.64 |      100 |    3.03 |    6.64 | ...93-363,386-503 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...agents/runtime |   77.65 |     74.1 |   74.25 |   77.65 |                   
  agent-core.ts    |   65.18 |    62.58 |   56.52 |   65.18 | ...1182,1209-1255 
  agent-events.ts  |    87.5 |      100 |      75 |    87.5 | 259-263           
  ...t-headless.ts |   79.52 |       75 |      55 |   79.52 | ...54-355,358-359 
  ...nteractive.ts |   85.71 |    83.33 |   80.64 |   85.71 | ...18,720,722,725 
  ...statistics.ts |   98.19 |    82.35 |     100 |   98.19 | 127,151,192,225   
  agent-types.ts   |     100 |      100 |     100 |     100 |                   
  compaction.ts    |   88.76 |    66.66 |     100 |   88.76 | ...24-128,135-136 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/config        |   70.44 |    72.02 |      60 |   70.44 |                   
  config.ts        |   68.52 |    67.57 |   54.93 |   68.52 | ...2366,2370-2373 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   81.61 |    92.72 |   81.57 |   81.61 | ...76-280,291-292 
 ...nfirmation-bus |   74.35 |    52.17 |      90 |   74.35 |                   
  message-bus.ts   |   72.22 |       50 |      90 |   72.22 | ...18-120,123-124 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |   79.86 |    80.44 |   89.86 |   79.86 |                   
  baseLlmClient.ts |     100 |    96.42 |     100 |     100 | 115               
  client.ts        |   72.72 |    75.74 |   86.95 |   72.72 | ...1032,1058-1074 
  ...tGenerator.ts |    72.1 |    61.11 |     100 |    72.1 | ...42,344,351-354 
  ...lScheduler.ts |   70.39 |    77.45 |   89.65 |   70.39 | ...1718,1775-1779 
  geminiChat.ts    |   84.49 |    84.96 |   86.66 |   84.49 | ...87-698,732-735 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  logger.ts        |   82.25 |    81.81 |     100 |   82.25 | ...57-361,407-421 
  ...tyDefaults.ts |     100 |      100 |     100 |     100 |                   
  ...olExecutor.ts |   92.59 |       75 |      50 |   92.59 | 41-42             
  ...on-helpers.ts |   74.74 |    46.15 |     100 |   74.74 | ...86,192,197-206 
  prompts.ts       |    90.4 |    87.05 |      75 |    90.4 | ...1018,1221-1222 
  tokenLimits.ts   |     100 |    88.23 |     100 |     100 | 43-44             
  ...okTriggers.ts |   99.31 |     90.9 |     100 |   99.31 | 124,135           
  turn.ts          |   96.15 |    88.46 |     100 |   96.15 | ...49,362-363,411 
 ...ntentGenerator |   87.91 |    69.28 |   91.17 |   87.91 |                   
  ...tGenerator.ts |   87.25 |    64.66 |    87.5 |   87.25 | ...45-652,656-657 
  converter.ts     |   90.35 |    73.97 |     100 |   90.35 | ...99,542-557,580 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
 ...ntentGenerator |   75.87 |    61.84 |   94.11 |   75.87 |                   
  ...tGenerator.ts |   73.49 |    61.64 |   93.75 |   73.49 | ...57-463,481-482 
  index.ts         |     100 |    66.66 |     100 |     100 | 45                
 ...ntentGenerator |   90.34 |    71.57 |      85 |   90.34 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   90.29 |    71.57 |      85 |   90.29 | ...62,472-473,501 
 ...ntentGenerator |   74.57 |       82 |   91.42 |   74.57 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   70.98 |    78.77 |   88.88 |   70.98 | ...1321,1342-1351 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-94              
  ...tGenerator.ts |   48.78 |    91.66 |   77.77 |   48.78 | ...10-163,166-167 
  pipeline.ts      |   83.75 |    78.78 |     100 |   83.75 | ...40-641,643-654 
  ...CallParser.ts |   90.66 |    88.57 |     100 |   90.66 | ...15-319,349-350 
 ...rator/provider |   96.09 |    88.07 |   93.75 |   96.09 |                   
  dashscope.ts     |   96.33 |    87.69 |   93.33 |   96.33 | ...12-213,289-290 
  deepseek.ts      |   90.76 |       75 |     100 |   90.76 | 40-41,45-46,59-60 
  default.ts       |   97.56 |      100 |   85.71 |   97.56 | 85-86             
  index.ts         |     100 |      100 |     100 |     100 |                   
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
 src/extension     |   60.36 |    79.75 |   78.22 |   60.36 |                   
  ...-converter.ts |   63.79 |    47.82 |      90 |   63.79 | ...86-787,796-828 
  ...ionManager.ts |    44.7 |    84.12 |   65.11 |    44.7 | ...1320,1341-1360 
  ...onSettings.ts |   93.46 |    93.05 |     100 |   93.46 | ...17-221,228-232 
  ...-converter.ts |   54.88 |    94.44 |      60 |   54.88 | ...35-146,158-192 
  github.ts        |   44.94 |    88.52 |      60 |   44.94 | ...53-359,398-451 
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   97.29 |    93.75 |     100 |   97.29 | ...64,184-185,274 
  npm.ts           |   49.82 |    76.08 |      75 |   49.82 | ...05-407,414-418 
  override.ts      |   94.11 |    88.88 |     100 |   94.11 | 63-64,81-82       
  settings.ts      |   66.26 |      100 |      50 |   66.26 | 81-108,143-149    
  storage.ts       |   94.73 |       90 |     100 |   94.73 | 41-42             
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   88.75 |    83.33 |     100 |   88.75 | ...28-231,234-237 
 src/followup      |   44.88 |    92.71 |   68.42 |   44.88 |                   
  followupState.ts |   95.83 |    89.18 |     100 |   95.83 | 153-155,210-211   
  forkedQuery.ts   |      30 |      100 |      50 |      30 | 122-249           
  index.ts         |     100 |      100 |     100 |     100 |                   
  overlayFs.ts     |   95.06 |       84 |     100 |   95.06 | 78,108,122,133    
  speculation.ts   |    13.4 |      100 |   16.66 |    13.4 | 88-458,518-563    
  ...onToolGate.ts |     100 |    96.29 |     100 |     100 | 93                
  ...nGenerator.ts |   38.27 |    95.12 |   33.33 |   38.27 | ...00-302,337-367 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/hooks         |   77.44 |     84.8 |   85.24 |   77.44 |                   
  ...on-checker.ts |     100 |      100 |     100 |     100 |                   
  ...Aggregator.ts |   96.17 |       90 |     100 |   96.17 | ...74,276-277,350 
  ...entHandler.ts |   86.07 |       84 |   80.76 |   86.07 | ...88,641-642,652 
  hookPlanner.ts   |   78.18 |       72 |    90.9 |   78.18 | ...18-236,244-245 
  hookRegistry.ts  |   79.74 |    74.28 |     100 |   79.74 | ...59,361,363,365 
  hookRunner.ts    |   57.83 |    73.33 |   66.66 |   57.83 | ...47-557,610-611 
  hookSystem.ts    |   83.22 |      100 |   86.95 |   83.22 | ...46-362,368-384 
  index.ts         |     100 |      100 |     100 |     100 |                   
  trustedHooks.ts  |     9.3 |      100 |       0 |     9.3 | 24-122            
  types.ts         |   89.61 |    94.66 |   85.18 |   89.61 | ...45-346,406-410 
 src/ide           |   72.67 |    83.92 |   75.92 |   72.67 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |    64.2 |    81.48 |   66.66 |    64.2 | ...9-970,999-1007 
  ideContext.ts    |     100 |      100 |     100 |     100 |                   
  process-utils.ts |   84.84 |    71.79 |     100 |   84.84 | ...37,151,193-194 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/lsp           |   33.39 |    43.75 |   44.91 |   33.39 |                   
  ...nfigLoader.ts |   70.27 |    35.89 |   94.73 |   70.27 | ...20-422,426-432 
  ...ionFactory.ts |    4.29 |      100 |       0 |    4.29 | ...20-371,377-394 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   10.47 |       75 |      25 |   10.47 | ...56-675,681-711 
  ...eLspClient.ts |   17.89 |      100 |       0 |   17.89 | ...37-244,254-258 
  ...LspService.ts |   45.87 |    62.13 |   66.66 |   45.87 | ...1282,1299-1309 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |   78.74 |    75.68 |   75.92 |   78.74 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   86.95 |      100 |   33.33 |   86.95 | ...,93,97,101-102 
  ...h-provider.ts |   73.74 |    54.45 |     100 |   73.74 | ...64-871,878-880 
  ...en-storage.ts |   98.62 |    97.72 |     100 |   98.62 | 87-88             
  oauth-utils.ts   |   70.58 |    85.29 |    90.9 |   70.58 | ...70-290,315-344 
  ...n-provider.ts |   89.83 |    95.83 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   79.48 |    86.66 |   86.36 |   79.48 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   82.75 |    82.35 |   92.85 |   82.75 | ...62-172,180-181 
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   68.14 |    82.35 |   64.28 |   68.14 | ...81-295,298-314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/memory        |   14.91 |       75 |    9.09 |   14.91 |                   
  ...tterParser.ts |   13.04 |      100 |       0 |   13.04 | 20-51,57-72       
  index.ts         |     100 |      100 |     100 |     100 |                   
  memoryAge.ts     |      25 |      100 |       0 |      25 | 13-23,29-30,36-38 
  ...yExtractor.ts |   12.76 |      100 |       0 |   12.76 | 38-188            
  memoryPrompt.ts  |     100 |      100 |     100 |     100 |                   
  memoryScan.ts    |   21.05 |      100 |       0 |   21.05 | 19-24,31-40       
  memoryStore.ts   |    7.45 |       75 |   18.18 |    7.45 | 51-361            
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   88.34 |    83.09 |   87.69 |   88.34 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nfigErrors.ts |   74.22 |    47.82 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |    97.5 |    86.44 |     100 |    97.5 | ...95,301,316-317 
  modelRegistry.ts |     100 |    98.21 |     100 |     100 | 182               
  modelsConfig.ts  |   83.61 |    81.37 |   83.78 |   83.61 | ...1157,1186-1187 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/output        |     100 |      100 |     100 |     100 |                   
  ...-formatter.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/permissions   |   68.39 |    87.29 |   48.27 |   68.39 |                   
  ...classifier.ts |   30.88 |    57.14 |   33.33 |   30.88 | ...07-230,234-235 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   78.62 |       79 |      84 |   78.62 | ...65-772,792-801 
  rule-parser.ts   |   97.97 |     95.7 |     100 |   97.97 | ...23-824,968-970 
  ...-semantics.ts |   58.28 |    85.27 |    30.2 |   58.28 | ...1604-1614,1643 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/prompts       |   29.09 |      100 |      25 |   29.09 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |   31.81 |      100 |   28.57 |   31.81 | ...45,51-58,71-76 
 src/qwen          |   85.87 |    79.93 |   97.18 |   85.87 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   84.74 |    75.78 |   93.33 |   84.74 | ...4,963-979,1009 
  ...kenManager.ts |   83.67 |    76.03 |     100 |   83.67 | ...59-764,785-790 
 src/services      |   67.32 |    81.37 |   81.76 |   67.32 |                   
  baselineCheck.ts |    3.57 |      100 |       0 |    3.57 | 25-93,100-133     
  ...ionService.ts |   96.49 |    91.12 |     100 |   96.49 | ...59,471,473-477 
  ...ingService.ts |   68.39 |    48.38 |   85.71 |   68.39 | ...25-437,453-454 
  cronScheduler.ts |   97.56 |    92.98 |     100 |   97.56 | 62-63,77,155      
  ...eryService.ts |   80.43 |    95.45 |      75 |   80.43 | ...19-134,140-141 
  ...temService.ts |   89.76 |     85.1 |   88.88 |   89.76 | ...89,191,266-273 
  fileTracker.ts   |   98.21 |     90.9 |     100 |   98.21 | 43                
  gitService.ts    |   66.29 |     90.9 |   55.55 |   66.29 | ...03-113,116-120 
  ...reeService.ts |   68.75 |    67.04 |   86.95 |   68.75 | ...88-789,805,821 
  ...ionService.ts |   98.98 |     98.3 |     100 |   98.98 | 260-261           
  ...solidation.ts |     5.9 |      100 |       0 |     5.9 | 65-339            
  ...EditVerify.ts |   10.25 |      100 |       0 |   10.25 | 18-68             
  sessionNotes.ts  |   16.94 |      100 |      20 |   16.94 | 58-131            
  ...ionService.ts |   79.23 |    73.19 |   88.88 |   79.23 | ...53-674,682-706 
  ...ionService.ts |   83.46 |    78.53 |   83.33 |   83.46 | ...1017,1023-1028 
  task-store.ts    |     8.2 |      100 |       0 |     8.2 | 106-586           
 src/skills        |   76.46 |    80.14 |   77.77 |   76.46 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  skill-load.ts    |   90.83 |    77.77 |     100 |   90.83 | ...28,148,160-162 
  skill-manager.ts |   71.68 |    80.76 |   73.91 |   71.68 | ...95-703,706-715 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/subagents     |   80.29 |    82.53 |    92.1 |   80.29 |                   
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |    71.6 |    73.37 |      88 |    71.6 | ...-932,1008-1009 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.34 |    96.51 |     100 |   92.34 | 50-55,59-64,68-73 
 src/telemetry     |   67.12 |    86.36 |      72 |   67.12 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...-exporters.ts |   36.76 |      100 |   22.22 |   36.76 | ...84,87-88,91-92 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-111             
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |    49.4 |     61.4 |   52.38 |    49.4 | ...1123,1126-1150 
  metrics.ts       |   75.93 |    86.74 |   76.92 |   75.93 | ...09-846,849-878 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  sdk.ts           |    87.5 |    70.83 |     100 |    87.5 | ...11,217-218,224 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  ...panContext.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |   77.31 |    94.17 |   81.81 |   77.31 | ...1105,1108-1137 
  uiTelemetry.ts   |   91.87 |    96.15 |   78.57 |   91.87 | ...67-168,174-181 
 ...ry/qwen-logger |   68.18 |    80.21 |   64.91 |   68.18 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   68.18 |       80 |   64.28 |   68.18 | ...1040,1078-1079 
 src/test-utils    |   92.85 |    94.28 |   77.41 |   92.85 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  mock-tool.ts     |   91.02 |    93.54 |   75.86 |   91.02 | ...31,195-196,209 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   72.49 |    79.67 |   74.23 |   72.49 |                   
  agent.ts         |   75.65 |    87.03 |      75 |   75.65 | ...34,890,929-934 
  ...erQuestion.ts |   87.89 |     73.8 |    90.9 |   87.89 | ...44-345,349-350 
  cron-create.ts   |   97.61 |    88.88 |   83.33 |   97.61 | 30-31             
  cron-delete.ts   |   96.55 |      100 |   83.33 |   96.55 | 26-27             
  cron-list.ts     |   96.36 |      100 |   83.33 |   96.36 | 25-26             
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  edit.ts          |   80.03 |    83.83 |      75 |   80.03 | ...49-550,633-683 
  exitPlanMode.ts  |   85.81 |    86.95 |     100 |   85.81 | ...24-129,157-169 
  glob.ts          |   91.57 |    88.33 |   84.61 |   91.57 | ...20,163,293,296 
  grep.ts          |    71.3 |    87.34 |   68.42 |    71.3 | ...90,530,538-545 
  ls.ts            |   96.72 |    90.14 |     100 |   96.72 | 169-174,205,209   
  lsp.ts           |   72.58 |    60.29 |   90.32 |   72.58 | ...1202,1204-1205 
  ...nt-manager.ts |   47.47 |       60 |   44.44 |   47.47 | ...73-491,494-531 
  mcp-client.ts    |   29.24 |    69.44 |   46.87 |   29.24 | ...1416,1420-1423 
  mcp-tool.ts      |   90.46 |     87.5 |   96.55 |   90.46 | ...11-612,662-663 
  memoryTool.ts    |   74.68 |     80.7 |   90.47 |   74.68 | ...41-350,463-547 
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 102,109           
  read-file.ts     |   94.47 |    88.63 |      80 |   94.47 | ...74,80-83,86-87 
  ripGrep.ts       |   96.24 |    90.76 |     100 |   96.24 | ...73,276,354-355 
  ...-transport.ts |    6.34 |      100 |       0 |    6.34 | 47-145            
  shell.ts         |   85.31 |    78.12 |   85.71 |   85.31 | ...66-470,664-665 
  skill.ts         |   94.08 |    88.88 |   84.61 |   94.08 | ...16,255-258,262 
  task-create.ts   |   59.03 |      100 |   33.33 |   59.03 | ...,36-59,105-108 
  task-get.ts      |      50 |      100 |   33.33 |      50 | ...29,32-57,88-91 
  task-list.ts     |   47.82 |      100 |   33.33 |   47.82 | ...,40-72,113-116 
  task-output.ts   |    59.7 |      100 |   33.33 |    59.7 | ...30,33-50,87-90 
  task-ready.ts    |   39.44 |      100 |   33.33 |   39.44 | ...,35-93,132-135 
  task-stop.ts     |      50 |      100 |   33.33 |      50 | ...30,33-61,96-99 
  task-update.ts   |   86.46 |      100 |      75 |   86.46 | 33-38,155-166     
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   62.79 |    65.38 |   59.37 |   62.79 | ...34-543,550-566 
  tools.ts         |   83.11 |    89.58 |   77.77 |   83.11 | ...35-436,452-458 
  web-fetch.ts     |   85.41 |    58.33 |   91.66 |   85.41 | ...58-259,261-262 
  write-file.ts    |   79.24 |    79.36 |   69.23 |   79.24 | ...46-449,461-496 
 ...ols/web-search |   79.93 |    74.13 |   78.94 |   79.93 |                   
  base-provider.ts |    72.5 |    57.14 |     100 |    72.5 | 44-48,63-66,75-76 
  index.ts         |   82.94 |    80.43 |   85.71 |   82.94 | ...80-188,196-200 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |      60 |       50 |      50 |      60 | 35-42             
 ...arch/providers |   47.86 |    68.42 |   72.72 |   47.86 |                   
  ...e-provider.ts |    7.69 |      100 |       0 |    7.69 | 68-83,89-203      
  ...e-provider.ts |   83.33 |    55.55 |     100 |   83.33 | 57-58,61-62,76-80 
  ...y-provider.ts |   90.56 |       80 |     100 |   90.56 | 66-70             
 src/utils         |   85.24 |    87.09 |   89.43 |   85.24 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   76.08 |    44.44 |     100 |   76.08 | 61-70,72          
  browser.ts       |    7.69 |      100 |       0 |    7.69 | 17-56             
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  cronDisplay.ts   |   42.85 |    23.07 |     100 |   42.85 | 26-31,33-45,47-54 
  cronParser.ts    |   89.74 |    85.71 |     100 |   89.74 | ...,63-64,183-186 
  debugLogger.ts   |   96.12 |    93.75 |   93.75 |   96.12 | 164-168           
  editHelper.ts    |   92.67 |    82.14 |     100 |   92.67 | ...52-454,463-464 
  editor.ts        |   96.98 |    93.87 |     100 |   96.98 | ...93-194,196-197 
  ...arResolver.ts |   94.28 |    88.88 |     100 |   94.28 | 28-29,125-126     
  ...entContext.ts |     100 |       95 |     100 |     100 | 83                
  errorParsing.ts  |   96.92 |       95 |     100 |   96.92 | 36-37             
  ...rReporting.ts |   88.46 |       90 |     100 |   88.46 | 69-74             
  errors.ts        |    68.7 |    77.27 |   53.33 |    68.7 | ...86-202,206-212 
  fetch.ts         |   71.97 |    71.42 |   71.42 |   71.97 | ...38,144,157,182 
  fileUtils.ts     |   87.96 |     81.4 |   94.73 |   87.96 | ...85-791,805-811 
  formatters.ts    |   54.54 |       50 |     100 |   54.54 | 12-16             
  ...eUtilities.ts |   89.21 |    86.66 |     100 |   89.21 | 16-17,49-55,65-66 
  ...rStructure.ts |   94.36 |    94.28 |     100 |   94.36 | ...17-120,330-335 
  getPty.ts        |    12.5 |      100 |       0 |    12.5 | 21-34             
  ...noreParser.ts |    92.3 |    89.36 |     100 |    92.3 | ...15-116,186-187 
  gitUtils.ts      |   36.66 |    76.92 |      50 |   36.66 | ...4,88-89,97-148 
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  ...ionManager.ts |     100 |       90 |     100 |     100 | 26                
  jsonl-utils.ts   |    8.87 |      100 |       0 |    8.87 | ...51-184,190-196 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...yDiscovery.ts |   81.99 |    72.72 |     100 |   81.99 | ...67-370,391-392 
  ...tProcessor.ts |   93.63 |       90 |     100 |   93.63 | ...96-302,384-385 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  ...kerChecker.ts |   84.04 |    78.94 |     100 |   84.04 | 68-69,79-84,92-98 
  openaiLogger.ts  |   86.27 |    82.14 |     100 |   86.27 | ...05-107,130-135 
  partUtils.ts     |     100 |      100 |     100 |     100 |                   
  pathReader.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |   95.69 |    94.52 |     100 |   95.69 | ...,71-72,104-105 
  ...ectSummary.ts |    3.75 |      100 |       0 |    3.75 | 27-119            
  ...tIdContext.ts |     100 |      100 |     100 |     100 |                   
  ...rDetection.ts |   58.57 |       76 |     100 |   58.57 | ...4,88-89,95-100 
  ...noreParser.ts |   85.45 |    81.48 |     100 |   85.45 | ...59,65-66,72-73 
  rateLimit.ts     |      90 |    84.37 |     100 |      90 | 68,79-81          
  readManyFiles.ts |   85.95 |    85.71 |     100 |   85.95 | ...80-182,198-209 
  retry.ts         |   70.14 |    76.92 |     100 |   70.14 | ...88,206,213-214 
  ripgrepUtils.ts  |   46.53 |    83.33 |   66.66 |   46.53 | ...32-233,245-322 
  ...tchOptions.ts |   55.88 |       50 |      75 |   55.88 | ...29-130,151-152 
  safeJsonParse.ts |   74.07 |    83.33 |     100 |   74.07 | 40-46             
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  ...aConverter.ts |   90.78 |    87.87 |     100 |   90.78 | ...41-42,93,95-96 
  ...aValidator.ts |     100 |    82.85 |     100 |     100 | 15-39,93-94,125   
  ...r-launcher.ts |   76.52 |     87.5 |   66.66 |   76.52 | ...33,135,153-191 
  shell-utils.ts   |   84.23 |    90.84 |     100 |   84.23 | ...1035,1042-1046 
  ...lAstParser.ts |   92.34 |    86.47 |    92.3 |   92.34 | ...1139-1141,1151 
  ...nlyChecker.ts |   95.75 |    92.39 |     100 |   95.75 | ...00-301,313-314 
  ...tGenerator.ts |     100 |     90.9 |     100 |     100 | 129               
  symlink.ts       |   77.77 |       50 |     100 |   77.77 | 44,54-59          
  ...emEncoding.ts |   96.36 |    91.17 |     100 |   96.36 | 59-60,124-125     
  ...Serializer.ts |   99.07 |    91.22 |     100 |   99.07 | 90,156-158        
  testUtils.ts     |   53.33 |      100 |   33.33 |   53.33 | ...53,59-64,70-72 
  textUtils.ts     |      60 |      100 |   66.66 |      60 | 36-55             
  thoughtUtils.ts  |     100 |    92.85 |     100 |     100 | 71                
  ...-converter.ts |   94.59 |    85.71 |     100 |   94.59 | 35-36             
  tool-utils.ts    |    93.6 |     91.3 |     100 |    93.6 | ...58-159,162-163 
  truncation.ts    |     100 |     90.9 |     100 |     100 | 52,71             
  ...aceContext.ts |   96.22 |       92 |   93.33 |   96.22 | ...15-116,133,160 
  yaml-parser.ts   |      92 |    83.67 |     100 |      92 | 49-53,65-69       
 ...ils/filesearch |   96.17 |     91.4 |     100 |   96.17 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |   96.22 |     92.3 |     100 |   96.22 | 66-67             
  fileSearch.ts    |   93.22 |    87.14 |     100 |   93.22 | ...30-231,233-234 
  ignore.ts        |     100 |      100 |     100 |     100 |                   
  result-cache.ts  |     100 |     92.3 |     100 |     100 | 46                
 ...uest-tokenizer |   56.63 |    74.52 |   74.19 |   56.63 |                   
  ...eTokenizer.ts |   41.86 |    76.47 |   69.23 |   41.86 | ...70-443,453-507 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tTokenizer.ts |   68.39 |    69.49 |    90.9 |   68.39 | ...24-325,327-328 
  ...ageFormats.ts |      76 |      100 |   33.33 |      76 | 45-48,55-56       
  textTokenizer.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
-------------------|---------|----------|---------|---------|-------------------

For detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run.

mabry1985 and others added 2 commits April 3, 2026 14:16
- constants.test: replace hardcoded count with Object.values(HookEventName).length
  so the test stays correct when the enum grows
- ideCommand.test: remove unused getIdeInstaller mock from vi.mock block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- coreToolScheduler.test + nonInteractiveToolExecutor.test: add
  getDisableAllHooks mock to all mock configs (hooks cleanup change)
- mcp-tool.ts: restore output truncation via truncateParts helper using
  existing truncateToolOutput utility
- mcp-tool.ts: gate auto-reconnect on isConnectionError check so generic
  errors (Invalid parameters) don't trigger reconnect
- extension/npm.ts: fix CodeQL alerts — use replaceAll for '/' encoding
  in scoped package names

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/core/src/extension/npm.ts (2)

285-286: ⚠️ Potential issue | 🟡 Minor

Inconsistent encoding: use replaceAll here to match line 374.

This line uses replace('/', '%2f') which only replaces the first occurrence, while checkNpmUpdate at line 374 correctly uses replaceAll. The commit message indicates this was intended to be fixed. While scoped packages typically have only one /, using replaceAll is safer and maintains consistency.

🔧 Proposed fix
-  const encodedName = name.replace('/', '%2f');
+  const encodedName = name.replaceAll('/', '%2f');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/extension/npm.ts` around lines 285 - 286, The encoding of
package names is inconsistent: change the creation of encodedName (used to build
metadataUrl) to use name.replaceAll('/', '%2f') instead of name.replace('/',
'%2f') so all slashes are encoded (matching the checkNpmUpdate implementation)
and ensuring scoped or multi-slash names are handled consistently.

195-201: ⚠️ Potential issue | 🔴 Critical

Auth token leakage on cross-origin redirects remains unaddressed.

The redirect handling still passes authToken to any redirect target verbatim. A malicious or misconfigured registry could redirect to a different host (or downgrade to http://), leaking private registry credentials. Relative Location headers are also not resolved against the current URL.

🔒️ Recommended fix for secure redirect handling
         if (res.statusCode === 301 || res.statusCode === 302) {
           if (res.headers.location) {
-            fetchNpmJson<T>(res.headers.location, authToken)
+            const nextUrl = new URL(res.headers.location, url).toString();
+            const next = new URL(nextUrl);
+            const current = new URL(url);
+            const forwardedToken =
+              next.protocol === 'https:' && next.origin === current.origin
+                ? authToken
+                : undefined;
+            fetchNpmJson<T>(nextUrl, forwardedToken)
               .then(resolve)
               .catch(reject);
             return;
           }
         }
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/extension/npm.ts`:
- Around line 257-262: The file write stream created as file =
fs.createWriteStream(dest) lacks an error handler, so if the stream errors the
surrounding Promise (the resolve/reject used with res.pipe) never settles; add a
file.on('error', ...) handler that closes/destroys the stream and calls
reject(err) to propagate the failure (mirror the existing file.on('finish', ...)
behavior) so writes failing during res.pipe(...) properly reject the Promise.

---

Duplicate comments:
In `@packages/core/src/extension/npm.ts`:
- Around line 285-286: The encoding of package names is inconsistent: change the
creation of encodedName (used to build metadataUrl) to use name.replaceAll('/',
'%2f') instead of name.replace('/', '%2f') so all slashes are encoded (matching
the checkNpmUpdate implementation) and ensuring scoped or multi-slash names are
handled consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3e48ba9d-3487-487a-adf0-93c3cbb730a8

📥 Commits

Reviewing files that changed from the base of the PR and between 893c61d and 8657b4d.

📒 Files selected for processing (4)
  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/nonInteractiveToolExecutor.test.ts
  • packages/core/src/extension/npm.ts
  • packages/core/src/tools/mcp-tool.ts
✅ Files skipped from review due to trivial changes (2)
  • packages/core/src/core/nonInteractiveToolExecutor.test.ts
  • packages/core/src/core/coreToolScheduler.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/tools/mcp-tool.ts

Comment thread packages/core/src/extension/npm.ts
- Add file.on('error') handler to write stream in downloadNpmTarball so
  stream errors during res.pipe() properly reject the Promise instead of
  hanging forever
- Fix name.replace('/', '%2f') → replaceAll in downloadFromNpmRegistry
  (line 285 was missed in previous commit; line 374 already correct)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/core/src/extension/npm.ts (2)

42-62: ⚠️ Potential issue | 🟠 Major

Unscoped npm packages are still rejected.

parseNpmPackageSource() still hard-requires @scope/name[...], and the helper below is scoped-only too. That means the unscoped npm install flow described in this PR still gets rejected before any registry metadata is fetched. When you relax this parser, make the later scope extraction conditional as follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/extension/npm.ts` around lines 42 - 62, The parser
currently rejects unscoped packages; update parseNpmPackageSource to accept both
scoped (`@scope/name`[`@version`]) and unscoped (name[`@version`]) forms by using a
regex that captures either full package name (with optional scope) and an
optional version, return {name, version} for both, and replace the current
strict scoped error with a generic "Invalid npm package source" message; keep
isScopedNpmPackage focused on detecting scoped packages only (e.g., test for
leading @) so any later scope extraction logic can be made conditional based on
that helper.

195-199: ⚠️ Potential issue | 🔴 Critical

Redirects are still followed unsafely.

These recursive calls reuse Location verbatim and forward the bearer token unchanged. Relative redirects break, and cross-origin or protocol-downgraded redirects can leak private registry credentials.

Safer redirect handling
-            fetchNpmJson<T>(res.headers.location, authToken)
+            const nextUrl = new URL(res.headers.location, url).toString();
+            const current = new URL(url);
+            const next = new URL(nextUrl);
+            const forwardedToken =
+              next.protocol === 'https:' && next.origin === current.origin
+                ? authToken
+                : undefined;
+            res.resume();
+            fetchNpmJson<T>(nextUrl, forwardedToken)
               .then(resolve)
               .catch(reject);
-            downloadNpmFile(res.headers.location, dest, authToken)
+            const nextUrl = new URL(res.headers.location, url).toString();
+            const current = new URL(url);
+            const next = new URL(nextUrl);
+            const forwardedToken =
+              next.protocol === 'https:' && next.origin === current.origin
+                ? authToken
+                : undefined;
+            res.resume();
+            downloadNpmFile(nextUrl, dest, forwardedToken)
               .then(resolve)
               .catch(reject);

Also applies to: 242-246

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/extension/npm.ts` around lines 195 - 199, The redirect
handling in fetchNpmJson is unsafe: it forwards the Location header verbatim and
always re-sends the bearer token, which breaks relative redirects and can leak
credentials on cross-origin or protocol-downgrade redirects. Fix fetchNpmJson by
resolving relative Location headers against the original request URL (use a URL
constructor with the previous request as base), introduce a redirect limit
parameter to avoid infinite recursion, and strip the Authorization header when
the redirect target has a different origin or a downgraded protocol (e.g., https
-> http); only forward the token for same-origin, same-protocol redirects.
Update the recursive call sites in fetchNpmJson to pass the resolved absolute
URL, the adjusted authToken (or null), and the remaining redirect count.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/extension/npm.ts`:
- Around line 145-168: The code currently returns the first .npmrc token match
while iterating lines, which lets a host-wide token mask a more specific
path-scoped token; instead, when scanning npmrcPaths (and inside the loop over
lines) collect all matching tokens (capture entryPrefix and token from match)
into a list, and after reading all files select the token whose entryPrefix
appears earliest in registryPrefixes (registryPrefixes is built from
most-specific to least-specific) — i.e., choose the match with the lowest index
in registryPrefixes — then return that token; update the loop that uses
match/entryPrefix to push candidates rather than immediately return.

---

Duplicate comments:
In `@packages/core/src/extension/npm.ts`:
- Around line 42-62: The parser currently rejects unscoped packages; update
parseNpmPackageSource to accept both scoped (`@scope/name`[`@version`]) and unscoped
(name[`@version`]) forms by using a regex that captures either full package name
(with optional scope) and an optional version, return {name, version} for both,
and replace the current strict scoped error with a generic "Invalid npm package
source" message; keep isScopedNpmPackage focused on detecting scoped packages
only (e.g., test for leading @) so any later scope extraction logic can be made
conditional based on that helper.
- Around line 195-199: The redirect handling in fetchNpmJson is unsafe: it
forwards the Location header verbatim and always re-sends the bearer token,
which breaks relative redirects and can leak credentials on cross-origin or
protocol-downgrade redirects. Fix fetchNpmJson by resolving relative Location
headers against the original request URL (use a URL constructor with the
previous request as base), introduce a redirect limit parameter to avoid
infinite recursion, and strip the Authorization header when the redirect target
has a different origin or a downgraded protocol (e.g., https -> http); only
forward the token for same-origin, same-protocol redirects. Update the recursive
call sites in fetchNpmJson to pass the resolved absolute URL, the adjusted
authToken (or null), and the remaining redirect count.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0ca40341-3e6a-4213-8451-5a0f545734ee

📥 Commits

Reviewing files that changed from the base of the PR and between 8657b4d and c4b9250.

📒 Files selected for processing (1)
  • packages/core/src/extension/npm.ts

Comment on lines +145 to +168
const parsed = new URL(registryUrl);
const registryPrefixes: string[] = [];
const pathSegments = parsed.pathname
.replace(/\/$/, '')
.split('/')
.filter(Boolean);
for (let i = pathSegments.length; i >= 0; i--) {
const prefix = pathSegments.slice(0, i).join('/');
registryPrefixes.push(`${parsed.host}${prefix ? `/${prefix}` : ''}`);
}

for (const npmrcPath of npmrcPaths) {
try {
const content = fs.readFileSync(npmrcPath, 'utf-8');
const lines = content.split('\n');
for (const line of lines) {
const trimmed = line.trim();
// Format: //host[/path]/:_authToken=TOKEN
const match = trimmed.match(/^\/\/(.+?)\/:_authToken\s*=\s*(.+)/);
if (match) {
const entryPrefix = match[1].replace(/\/$/, '');
if (registryPrefixes.includes(entryPrefix)) {
return match[2].trim();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Choose the most specific matching _authToken.

registryPrefixes is built from most-specific to least-specific, but this loop returns the first matching .npmrc line. A host-wide token can therefore mask a path-scoped token on the same registry host, which breaks feeds like Azure Artifacts or Artifactory when both entries exist.

Possible fix
   for (const npmrcPath of npmrcPaths) {
     try {
       const content = fs.readFileSync(npmrcPath, 'utf-8');
       const lines = content.split('\n');
+      const tokens = new Map<string, string>();
       for (const line of lines) {
         const trimmed = line.trim();
         // Format: //host[/path]/:_authToken=TOKEN
         const match = trimmed.match(/^\/\/(.+?)\/:_authToken\s*=\s*(.+)/);
         if (match) {
           const entryPrefix = match[1].replace(/\/$/, '');
-          if (registryPrefixes.includes(entryPrefix)) {
-            return match[2].trim();
-          }
+          tokens.set(entryPrefix, match[2].trim());
         }
       }
+      for (const prefix of registryPrefixes) {
+        const token = tokens.get(prefix);
+        if (token) return token;
+      }
     } catch {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/extension/npm.ts` around lines 145 - 168, The code
currently returns the first .npmrc token match while iterating lines, which lets
a host-wide token mask a more specific path-scoped token; instead, when scanning
npmrcPaths (and inside the loop over lines) collect all matching tokens (capture
entryPrefix and token from match) into a list, and after reading all files
select the token whose entryPrefix appears earliest in registryPrefixes
(registryPrefixes is built from most-specific to least-specific) — i.e., choose
the match with the lowest index in registryPrefixes — then return that token;
update the loop that uses match/entryPrefix to push candidates rather than
immediately return.

@mabry1985 mabry1985 merged commit d6ec21f into main Apr 3, 2026
7 checks passed
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.

7 participants