Skip to content

Step 8: Interactive Shell — Complete (WI-0 through WI-5)#561

Merged
tnaum-ms merged 28 commits intofeature/shell-integrationfrom
dev/tnaum/interactive-shell
Apr 9, 2026
Merged

Step 8: Interactive Shell — Complete (WI-0 through WI-5)#561
tnaum-ms merged 28 commits intofeature/shell-integrationfrom
dev/tnaum/interactive-shell

Conversation

@tnaum-ms
Copy link
Copy Markdown
Collaborator

@tnaum-ms tnaum-ms commented Apr 8, 2026

Interactive Shell Implementation (Step 8) ✅

Implements the Interactive Shell feature — a REPL experience within a VS Code terminal, integrated with the extension's connection management and shell-runtime eval engine.

Full plan: docs/plan/08-interactive-shell.md

Work Items

  • WI-0: Legacy Shell Removal & Settings Migration
  • WI-1: Worker Infrastructure Refactoring
  • WI-2: Shell UI — Terminal Pseudoterminal
  • WI-3: CommandInterceptor Expansion
  • WI-4: Connection & Launch
  • WI-4.5: Shell UX Polish
  • WI-5: Terminal Links — Open in Collection View

What's New

Interactive Shell (Pseudoterminal)

  • REPL experience in a VS Code terminal tab with prompt, line editing, command history (Up/Down)
  • Persistent eval context — variables, cursor state, and db reference survive across commands
  • Shell commands: help, exit/quit, cls/clear, show dbs, show collections, use <db>, it
  • Dynamic prompt showing current database name (updates on use <db>)
  • ANSI-colored JSON output with configurable documentDB.shell.display.colorOutput
  • Connection banner showing host, auth method, username (SCRAM), emulator status
  • Dynamic terminal tab name: DocumentDB: user@cluster/db

Worker Infrastructure

  • Extracted WorkerSessionManager from PlaygroundEvaluator — shared by playground (fresh context) and shell (persistent context)
  • Each shell session gets its own dedicated worker thread with its own connection
  • persistent: true mode enables @mongosh state persistence across evaluations

Terminal Links

  • After query results with a known namespace, appends clickable 📊 Open collection [db.collection] in Collection View action line
  • ShellTerminalLinkProvider matches the action line pattern and opens Collection View on click

CommandInterceptor

  • exit/quit → signal shell close
  • cls/clear → clear terminal screen
  • Surface-aware help text (separate help for shell vs. playground)

Launch Points

  • Right-click cluster → Open Interactive Shell (defaults to test database)
  • Right-click database → Open Interactive Shell (uses that database)
  • Right-click collection → Open Interactive Shell (uses that collection's database)

Removed

  • Legacy launchShell command (external process launcher)
  • documentDB.mongoShell.path, documentDB.mongoShell.args, documentDB.mongoShell.timeout settings
  • Replaced mongoShell with shellCommand in AI service types and prompt templates

New Settings

Setting Type Default Description
documentDB.shell.display.colorOutput boolean true ANSI-colored JSON output
documentDB.shell.display.autocompletion boolean true Reserved for future TerminalCompletionProvider
documentDB.shell.timeout number 120 Shell eval timeout (seconds)

Test Coverage

  • ShellInputHandler: 37 tests — line editing, cursor movement, history, word navigation
  • ShellOutputFormatter: 17 tests — EJSON formatting, ANSI coloring, cursor hints
  • DocumentDBShellPty: 22 tests — lifecycle, eval, special results, use <db>, action line
  • ShellTerminalLinkProvider: 10 tests — pattern matching, special characters, registry
  • CommandInterceptor: Tests for exit, quit, cls, clear patterns
  • openInteractiveShell: 9 tests — node extraction, credential checks

Part of umbrella PR #508.

…references

- Remove src/commands/launchShell/ directory (legacy external mongosh launcher)
- Replace launchShell command registration with openInteractiveShell stub in ClustersExtension.ts
- Remove documentDB.mongoShell.* settings from package.json and extensionVariables.ts
- Add documentDB.shell.display.colorOutput and documentDB.shell.display.autocompletion settings
- Update all package.json menu contributions from launchShell to openInteractiveShell
- Rename mongoShell -> shellCommand across AI service types, query insights, and webview types
- Mark WI-0 as in-progress in plan file
@tnaum-ms tnaum-ms mentioned this pull request Apr 8, 2026
17 tasks
tnaum-ms added 11 commits April 8, 2026 22:31
…eout migration

- Rename mongoShell -> shellCommand in promptTemplates.ts (LLM JSON schema)
- Rename mongoShell local vars/comments in transformations.ts
- Migrate documentDB.mongoShell.timeout -> documentDB.shell.timeout (used by PlaygroundEvaluator)
- Remove mongosh prerequisite from README.md
- Regenerate l10n bundle (removed stale mongoShell entries, added new strings)
- All builds, lint, prettier, l10n, and tests pass (98/98)
…r, add persistent mode

- Extract core worker lifecycle/IPC logic from PlaygroundEvaluator into
  WorkerSessionManager (spawn, init, eval, shutdown, timeout, token relay)
- PlaygroundEvaluator now delegates to WorkerSessionManager (persistent: false)
- Add persistent: boolean to ShellRuntimeOptions and workerTypes InitMessage
- DocumentDBShellRuntime supports persistent mode: reuses ShellInstanceState,
  ShellEvaluator, and vm.Context across evaluate() calls
- Add ShellSessionManager (persistent: true) for interactive shell sessions
- Add tests for DocumentDBShellRuntime persistent/non-persistent modes
- Add tests for WorkerSessionManager initial state and dispose
- All 82 existing + new tests pass, no regressions
Implement the interactive shell REPL UI using VS Code's Pseudoterminal API.

New files:
- DocumentDBShellPty.ts: Pseudoterminal implementation (REPL loop,
  prompt rendering, eval dispatch, special result handling, Ctrl+C)
- ShellInputHandler.ts: Line editing, cursor movement, command history,
  word navigation, Ctrl+U/K/W editing shortcuts
- ShellOutputFormatter.ts: EJSON deserialization, ANSI color-coded JSON
  output, CursorIterationResult unwrapping, help/error/system formatting

Tests: 69 new tests (37 + 17 + 15) covering all three components.
Add two new command patterns to the shell-runtime CommandInterceptor:
- exit/quit: returns { type: 'exit' } result to signal shell close
- cls/clear: returns { type: 'clear' } result to signal screen clear

Both patterns support optional trailing semicolons and whitespace.
Negative tests ensure function calls, property access, and substrings
are not intercepted.

Also update HelpProvider to include shell commands section:
help, exit/quit, cls/clear, it, use <db>.

Tests: 36 CommandInterceptor tests (was 12), all HelpProvider tests pass.
Implement the openInteractiveShell command that opens a DocumentDB
interactive shell terminal from the tree view context menu.

New files:
- src/commands/openInteractiveShell/openInteractiveShell.ts
  Handles cluster, database, and collection tree nodes. Extracts
  connection info using clusterId (stable, not treeId), verifies
  credentials, creates a DocumentDBShellPty, and opens a terminal.
  Cluster-level invocations use 'test' as default database name.

- src/commands/openInteractiveShell/openInteractiveShell.test.ts
  9 tests covering all node types, no-node case, and missing credentials.

Modified:
- ClustersExtension.ts: Replace stub registration with real command
  using withTreeNodeCommandCorrelation for telemetry.
- l10n/bundle.l10n.json: Updated with new user-facing strings.
Split HelpProvider content by surface ('playground' | 'shell'):

- Playground: keyboard shortcuts, block tips, console output panel
- Shell: exit/quit, cls/clear, it, use <db>, session persistence tips
- Shared: collection access, query/write/index/cursor/database/BSON

The surface is derived from the persistent option in
DocumentDBShellRuntime: persistent=true → shell, false → playground.

HelpProvider constructor now takes an optional HelpSurface parameter
(defaults to 'playground' for backward compatibility).

Tests updated: 20 HelpProvider tests (was 14), covering both surfaces.
4.5.1: Suppress null after print() / side-effect-only expressions
  - Add printableIsUndefined flag to SerializableExecutionResult
  - Worker detects result.printable === undefined before EJSON serialization
    (EJSON.stringify(undefined) produces 'null', losing the distinction)
  - ShellOutputFormatter returns empty string when flag is set

4.5.2: Connection banner with authentication details
  - ShellSessionManager.initialize() now returns ShellConnectionMetadata
    (host, authMechanism, isEmulator)
  - Host extracted safely from connection string (no credential exposure)
  - PTY shows: Connected to host | Authentication: SCRAM/Entra ID | Database

4.5.3: Fix prompt not updating after 'use <db>'
  - Bug: updateDatabaseFromResult checked result.type === 'string', but
    @MongoSH returns type: null for all primitives including the 'switched
    to db <name>' string. Removed the incorrect type guard.

72 tests pass (3 new).
…ery results

After a find/findOne query with a known namespace, the shell appends a
clickable action line: '📊 Open collection [db.coll] in Collection View'.

- ShellTerminalLinkProvider: TerminalLinkProvider matching the action line
  pattern, opens Collection View via internal command on click
- DocumentDBShellPty: writes action line after Cursor/Document results
  with source.namespace; tracks active action line state
- Terminal registry: maps Terminal → PTY info for the link provider
- 16 tests (10 link provider + 6 PTY action line tests)
- Query pre-fill deferred to future work (§3.1 in terminal enhancements)
- Show username in banner for SCRAM auth (User: admin | Authentication: SCRAM | Database: mydb)
- For Entra ID, show auth method without username (as before)
- Update terminal tab name dynamically after connection to include user: 'DocumentDB: user@cluster/db'
- Add username field to ShellConnectionMetadata, sourced from CredentialCache
- Add onDidChangeName to PTY for dynamic terminal name updates
@tnaum-ms tnaum-ms changed the title Step 8: Interactive Shell — WI-0 through WI-5 Step 8: Interactive Shell — Complete (WI-0 through WI-5) Apr 9, 2026
- Add setTerminal, getTerminalInfo, onDidChangeName to DocumentDBShellPty mock
- Mock registerShellTerminal in openInteractiveShell tests
- Fix pre-existing test: 'Authenticating and connecting' → 'Connecting and authenticating'
@tnaum-ms tnaum-ms marked this pull request as ready for review April 9, 2026 12:12
@tnaum-ms tnaum-ms requested a review from a team as a code owner April 9, 2026 12:12
@tnaum-ms tnaum-ms requested a review from Copilot April 9, 2026 12:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements Step 8 “Interactive Shell” as an in-extension REPL hosted in a VS Code pseudoterminal, backed by the existing worker-thread eval pipeline with a new persistent runtime mode. This also removes the legacy external mongosh launcher/settings and updates Query Insights/AI payload fields from mongoShellshellCommand.

Changes:

  • Added an interactive shell PTY with line editing/history, formatted output, and session lifecycle management.
  • Refactored worker infrastructure into a reusable WorkerSessionManager, and added persistent eval mode to the shell runtime.
  • Replaced the legacy launchShell command/settings and updated Query Insights + prompt templates to use shellCommand.

Reviewed changes

Copilot reviewed 37 out of 37 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/webviews/documentdb/collectionView/types/queryInsights.ts Renames query insight payload fields to shellCommand.
src/services/ai/types.ts Renames AI index recommendation field to shellCommand.
src/services/ai/QueryInsightsAIService.ts Updates modify-index payload validation/parsing to use shellCommand.
src/extensionVariables.ts Removes legacy mongo shell settings keys; adds documentDB.shell.timeout key constant.
src/documentdb/shell/ShellTerminalLinkProvider.ts Adds terminal link provider + registry for “Open collection …” action lines.
src/documentdb/shell/ShellTerminalLinkProvider.test.ts Unit tests for terminal link detection and command execution.
src/documentdb/shell/ShellSessionManager.ts Adds shell-specific session wrapper around worker manager (persistent context).
src/documentdb/shell/ShellOutputFormatter.ts Adds output formatting (EJSON, optional ANSI coloring, cursor “it” hint).
src/documentdb/shell/ShellOutputFormatter.test.ts Tests for formatter behavior and color/no-color modes.
src/documentdb/shell/ShellInputHandler.ts Adds line editing, cursor movement, history navigation, word ops for PTY input.
src/documentdb/shell/ShellInputHandler.test.ts Tests for input handling and editing operations.
src/documentdb/shell/DocumentDBShellPty.ts Implements PTY REPL loop, prompt handling, interrupts, action line emission.
src/documentdb/shell/DocumentDBShellPty.test.ts Tests for PTY lifecycle, eval flow, special results, action line output.
src/documentdb/queryInsights/transformations.ts Wires shellCommand into ImprovementCards and action payloads.
src/documentdb/playground/workerTypes.ts Adds printableIsUndefined + persistent init option to worker protocol.
src/documentdb/playground/WorkerSessionManager.ts New reusable worker lifecycle/IPC manager extracted from playground evaluator.
src/documentdb/playground/WorkerSessionManager.test.ts Basic unit tests for worker manager initial state/dispose.
src/documentdb/playground/playgroundWorker.ts Supports persistent runtime option + preserves undefined via flag.
src/documentdb/playground/PlaygroundEvaluator.ts Refactors to use WorkerSessionManager and keeps playground semantics.
src/documentdb/ClustersExtension.ts Registers new openInteractiveShell command and terminal link provider.
src/commands/openInteractiveShell/openInteractiveShell.ts Adds command to open PTY shell from cluster/db/collection nodes.
src/commands/openInteractiveShell/openInteractiveShell.test.ts Tests command behavior, telemetry props, and credential gating.
src/commands/llmEnhancedCommands/promptTemplates.ts Updates prompt schema/rules from mongoShellshellCommand.
src/commands/launchShell/launchShell.ts Removes legacy external mongosh launcher implementation.
README.md Removes external mongosh prerequisite note (legacy launcher removed).
packages/documentdb-shell-runtime/src/types.ts Adds persistent option to runtime API.
packages/documentdb-shell-runtime/src/index.ts Exposes HelpSurface type.
packages/documentdb-shell-runtime/src/HelpProvider.ts Adds surface-aware help content (playground vs shell).
packages/documentdb-shell-runtime/src/HelpProvider.test.ts Adds tests for both help surfaces + getHelpResult behavior.
packages/documentdb-shell-runtime/src/DocumentDBShellRuntime.ts Implements persistent evaluation mode and surface-aware interceptor help.
packages/documentdb-shell-runtime/src/DocumentDBShellRuntime.test.ts Tests persistent vs fresh context behavior and disposal.
packages/documentdb-shell-runtime/src/CommandInterceptor.ts Adds interception for exit/quit and cls/clear (plus help).
packages/documentdb-shell-runtime/src/CommandInterceptor.test.ts Adds tests for new interception patterns.
packages/documentdb-shell-runtime/README.md Documents runtime modes and usage.
packages/documentdb-shell-runtime/package.json Updates package description to match new scope.
package.json Contributes new command, menus, and new shell settings; removes legacy shell settings.
l10n/bundle.l10n.json Adds/removes localized strings for interactive shell UX and Query Insights updates.

tnaum-ms added 10 commits April 9, 2026 15:30
…Ctrl+C during eval

- Add _terminatingIntentionally flag to WorkerSessionManager to suppress
  onWorkerExit callback during intentional kills (Ctrl+C, timeout, dispose)
- Allow Ctrl+C to bypass the _enabled guard in ShellInputHandler so users
  can cancel running evaluations
- Reset evaluating state and re-enable input after Ctrl+C kill
- Fixes: shell no longer shows 'ended unexpectedly' on Ctrl+C or timeout
- Fixes: Ctrl+C now cancels long-running queries instead of being swallowed

Addresses review findings #2 and #14.
…es Ctrl+C/timeout)

- Add _activeDatabase field to ShellSessionManager, initialized from connectionInfo
- Update _activeDatabase when PTY detects a 'use <db>' result
- Use _activeDatabase (not original connectionInfo.databaseName) for both
  buildInitMessage() and evaluate(), so worker re-init uses the correct database
- Add setActiveDatabase mock to PTY test suite

Addresses review finding #1.
- Extend EXIT_PATTERN regex to match exit(), quit(), exit();, quit();
- Previously these bypassed the interceptor and went to @MongoSH, which
  called process.exit() in the worker, producing a crash-like exit
- Update tests to verify the new function-call forms are intercepted

Addresses review finding #7.
- Set _inputHandler.setEnabled(false) at the start of open()
- Re-enable input after successful init (before showing prompt)
- Re-enable input on init failure (before closing terminal)
- Prevents user input from triggering a concurrent initialize() call

Addresses review finding #8.
…ll sessions

- Replace hardcoded expiresInSeconds: 0 with actual expiration from the JWT
- Parse the exp claim from the access token payload
- Fall back to 3500 seconds (~1 hour) if JWT parsing fails
- Eliminates redundant token acquisition on every database operation in
  persistent shell sessions (previously every command triggered a full
  IPC + auth API round-trip)

Addresses review finding #9.
… handling

- Replace 📊 emoji with 🔗 (link icon) to suggest clickability
- Make action line format locale-independent: '🔗 [db.collection]'
  (no English text to translate — tooltip remains localized)
- Regex in ShellTerminalLinkProvider now matches the sentinel prefix
  instead of hard-coded English text
- Fix Unicode input handling: use for...of iteration instead of data[i]
  to correctly handle surrogate pairs (emoji, CJK Extension B)

Addresses review findings #3 and #10.
Remove mockInitialize.mockResolvedValue(undefined) overrides so tests
exercise the connected-shell code path with real metadata instead of
falling into the error/failure path.

Addresses review finding #5.
- Cache username from initialization metadata in _username field
- Extract updateTerminalTitle() helper called from both init and
  updateDatabaseFromResult()
- Terminal tabs now reflect the active database after switching

Addresses review finding #6.
Change regex from /\S+/ to /.+/ to capture database names containing
spaces (e.g., 'my db').

Addresses review finding #11.
tnaum-ms added 3 commits April 9, 2026 15:41
The eraseActionLine method was a no-op (only reset a flag). Remove the
method, the _hasActiveActionLine field, and the call site. Action lines
in scroll-back remain clickable, which is the intended behavior.

Addresses PR thread item B (r3057731102, r3057760296).
…r message

- Change getShellTimeoutMs() local fallback from 120 to 30 (matches
  the contributed setting default in package.json)
- Timeout error message now tells users the timeout is configurable
  in Settings > DocumentDB > Shell > Timeout

Addresses PR thread item D (r3057731255, r3057764942).
@tnaum-ms
Copy link
Copy Markdown
Collaborator Author

tnaum-ms commented Apr 9, 2026

Review findings addressed

All findings from the code review have been addressed in the latest commits. Summary:

High priority (correctness)

  • #1 use <db> lost on worker restart → Fixed in 9a482a9 (_activeDatabase tracked in ShellSessionManager)
  • #2/#14 Ctrl+C swallowed during eval + timeout closes shell → Fixed in 83512e4 (_terminatingIntentionally flag + Ctrl+C bypasses _enabled guard)

Medium priority (robustness)

  • #7 exit()/quit() bypass interceptor → Fixed in 3919753 (regex extended)
  • #8 Race condition during async init → Fixed in 25c7192 (input disabled during init)
  • #9 expiresInSeconds: 0 in OIDC callback → Fixed in 3ba3008 (JWT exp claim parsed)
  • #3/#10 Terminal links break in localized builds + Unicode input → Fixed in 4f26228 (locale-independent action line format 🔗 [db.collection], for...of iteration)
  • #5 Test stubs with undefined metadata → Fixed in 1145d97

Low priority (polish)

  • #6 Stale terminal tab title after use <db> → Fixed in 1067713 (re-emit on db change)
  • #11 Database name regex \S+.+ → Fixed in 86e14fe
  • #12 EventEmitter disposal → Fixed in ae17c8a
  • #13 JSON colorizer escaped quotes → Fixed in 0eb7e76
  • B Dead eraseActionLine() code → Fixed in e4ec828
  • D Timeout fallback + UX message → Fixed in c99d4ea

Acknowledged / deferred

  • #4 Discovery node credential gap → Consistent extension-wide pattern, deferred to future work
  • A Auth label localization → Product names kept as-is
  • C Emulator detection heuristic → Low-priority future cleanup

tnaum-ms added 2 commits April 9, 2026 15:47
… Reconnecting message

Three user-facing issues fixed:

1. print() output now gets a newline before the next prompt.
   Track _lastOutputHadTrailingNewline from console output and emit
   \r\n before the prompt if the last output didn't end with one.

2. Ctrl+C no longer shows 'Worker terminated' error.
   Set _interrupted flag in handleInterrupt() so evaluateInput()'s
   catch and handleLineInput()'s finally both skip their output/prompt
   when the interruption was intentional.

3. After Ctrl+C, show 'Reconnecting...' while the worker respawns.
   Add onReconnecting callback to ShellSessionCallbacks, fired from
   evaluate() when re-initialization is needed after a worker kill.
@tnaum-ms tnaum-ms merged commit db36868 into feature/shell-integration Apr 9, 2026
5 checks passed
@tnaum-ms tnaum-ms deleted the dev/tnaum/interactive-shell branch April 9, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants