Skip to content

Latest commit

 

History

History
233 lines (188 loc) · 9.14 KB

File metadata and controls

233 lines (188 loc) · 9.14 KB

Calva Backseat Driver - AI Agent Guide

ClojureScript VS Code extension providing REPL access to AI agents via Language Model API and MCP. Enables Interactive Programming where AI evaluates code in the user's running environment rather than guessing.

Essential Architecture

Action/Effect System ("Ex")

Functional core with unidirectional flow:

dispatch! → handle-actions → [enrich → route] → {:ex/db, :ex/fxs, :ex/dxs} → update state → execute effects

Actions (*/axs.cljs): Pure functions returning state changes and effects Effects (*/fxs.cljs): Side effect handlers with dispatch access State: Single app-db atom in src/calva_backseat_driver/app/db.cljs

Runtime State Structure

The app-db atom contains:

{:vscode/extension-context    ; VS Code extension API access
 :app/log-file-uri            ; Logging location
 :app/min-log-level           ; :debug/:info/:warn/:error
 :mcp/wrapper-config-path     ; ~/.config/calva/backseat-driver
 :calva/output-line-counter   ; Monotonic line counter for output entities
 :calva/history-storage-uri   ; Workspace-scoped Transit file for persistent history
 :extension/disposables       ; VS Code subscriptions
 :extension/when-contexts}    ; Context keys for enablement

Datascript Connections

  • !output-conn — Session-scoped, schemaless. All REPL output categories. Cap: 1000 entities.
  • !history-conn — Persistent, :output/line unique identity schema. evaluatedCode only. Cap: 10000 entities. Persisted to eval-history.transit.json under context.storageUri.

Calva API Integration

Available at calva-backseat-driver.integrations.calva.api/calva-api:

{:repl     {:evaluateCode, :currentSessionKey, :onOutputLogged}
 :ranges   {:currentTopLevelForm, :currentEnclosingForm, ...}
 :editor   {:replace}
 :document {:getNamespace, :getNamespaceAndNsForm}
 :info     {:getSymbolInfo, :getClojureDocsDotOrg}}

Development Workflow

REPL Setup (ClojureScript)

npm run watch           # shadow-cljs + nREPL, auto-runs tests
# Connect Calva: Ctrl+Alt+C Ctrl+Alt+C
# Launch Extension Host: F5

Critical: This is a ClojureScript project running in VS Code Extension Host (Node.js). Use cljs REPL session, not clj.

Interactive Development Pattern

;; Explore runtime state
(in-ns 'calva-backseat-driver.app.db)
@!app-db

;; Test Calva API
(in-ns 'calva-backseat-driver.integrations.calva.api)
(keys (:repl calva-api))

;; Test utilities
(in-ns 'calva-backseat-driver.integrations.calva.editor)
(require '[calva-backseat-driver.integrations.calva.editor-util :as util])
(util/get-context-lines sample-text 3 10)

Testing Commands

bb run-e2e-tests-ws     # Full E2E
bb run-mcp-inspector    # Test MCP tools interactively
bb package-pre-release  # Package pre-release VSIX

Note: When running bb run-e2e-tests-ws: Detailed output goes to .tmp/e2e-output.log — read that file for details, if needed. The command outputs a very brief summary. Don't pipe or redirect.

E2E Testing

Load the e2e-testing skill (.github/skills/e2e-testing/SKILL.md) when writing, modifying, or debugging e2e tests. It covers test runner mechanics, shared helpers, session lifecycle patterns, and data shape through MCP.

Key rule: always use wait-for+ polling instead of p/delay/setTimeout for async conditions.

Key Implementation Patterns

Enrichment System

Actions use keyword placeholders enriched at runtime:

;; In action:
[:vscode/fx.show-input-box {:title "Name" :ex/then [[:handler :ex/action-args]]}]

;; Effect enriches and dispatches:
(dispatch! context (ax/enrich-with-args then-actions result))

;; Placeholders: :ex/action-args (entire result), :ex/action-args%1 (first element), etc.

File Context Feature

When structural editing fails, returns diagnostic context:

;; 21 lines around target with line numbers and marker
" 13 | (defn subtract-numbers
  14 |   \"Subtracts b from a\"
  15 |   [a b]
  16 |   (- a b))
→ 17 |
  18 | (defn add-numbers
  ..."

Implemented in integrations/calva/editor-util.cljs:

(util/get-context-lines doc-text line-number context-size)
;; Returns formatted string with line numbers and → marker at target

Naming Conventions

  • Side effects: function-name!
  • Promises: function-name+
  • Inline debugging: (def var var) inside REPL comment blocks
  • Test names: kebab-case describing area/thing (file-context-formatting)

Structural Editing Tools

Text Targeting

The clojure_edit_files tool uses targetLineText (first line of target form) + line number ±2:

;; clojure_edit_files batch with replace/insert edits
{:edits [{:type "replace"
          :filePath "/absolute/path.clj"
          :line 23
          :targetLineText "(defn multiply-numbers"
          :newForm "(defn multiply-numbers\n  [x y]\n  (* x y))"}]}

Automatic Features

  • Bracket balancing: Parinfer-powered (integrations/parinfer.cljs)
  • Rich Comment support: Forms inside (comment ...) treated as top-level
  • Diagnostic context: On failure, returns 21-line context + remedy message

Critical: Bottom-to-Top Editing

When making multiple edits, work from highest line number to lowest (line numbers shift down as you edit above).

MCP Server Architecture

Socket Server Pattern

  • Backend server runs in Extension Host (TCP socket)
  • Port file: ${workspaceFolder}/.calva/mcp-server/port
  • stdio wrapper (dist/calva-mcp-server.js) relays stdio ↔ socket
  • One server per workspace folder

Skills and Instructions as Bundled Assets

  • assets/skills/ and assets/instructions/ are the canonical source for content bundled with the extension. When updating skill or instruction content, edit these files — not the installed extension copies under ~/.vscode*/extensions/.
  • Skills declared in package.json under contributes.chatSkills are exposed as MCP resources
  • Discovery: resources/list returns skill name, description, and URI
  • Reading: resources/read at /skills/{name}/SKILL.md returns full skill content
  • Dynamic instructions: the initialize response includes instructions composed from available tools and skills
  • Implementation: src/calva_backseat_driver/mcp/requests.cljsskill-manifests, get-skills, compose-instructions

Configuration Access

Settings read via enrichment:

;; In action/effect context:
:vscode/config.enableMcpReplEvaluation  ; boolean
:vscode/config.mcpSocketServerPort      ; number (default 1664, 0=random)
:vscode/config.autoStartMCPServer       ; boolean
:vscode/config.provideBdSkill           ; boolean (default true) — enable/disable Backseat Driver skill
:vscode/config.provideEditSkill         ; boolean (default true) — enable/disable structural editing skill

Adding New Features

New Skill

  1. Create assets/skills/{name}/SKILL.md with YAML frontmatter (name, description)
  2. Add entry to package.jsoncontributes.chatSkills: {"path": "./assets/skills/{name}/SKILL.md"}
  3. The MCP server picks it up automatically — no code changes needed

New Tool Implementation

  1. Tool manifest: package.jsonlanguageModelTools
  2. MCP handler: src/calva_backseat_driver/mcp/requests.cljs
  3. VS Code tool: src/calva_backseat_driver/integrations/vscode/tools.cljs
  4. Test: bb run-mcp-inspector

Action/Effect Example

;; Action (pure, in */axs.cljs)
[:app/ax.register-command command-id actions]
{:ex/fxs [[:app/fx.register-command command-id actions]]}

;; Effect (side effect, in */fxs.cljs)
[:app/fx.register-command command-id actions]
(vscode/commands.registerCommand command-id
  (fn [] (dispatch! context actions)))

Common Pitfalls

  • DON'T access @!app-db directly from helpers (pass data explicitly)
  • DON'T perform side effects in action handlers (return effects instead)
  • DON'T forget namespace reloads after file edits: (require 'ns :reload)
  • DO evaluate in REPL before file edits
  • DO use cljs session (this is ClojureScript, not Clojure)
  • DO check :calva/output-buffer in app-db to see REPL activity
  • DO work bottom-to-top when multiple structural edits

Quick Reference

Key Namespaces:

  • calva-backseat-driver.ex.ex - Dispatcher
  • calva-backseat-driver.app.{axs,fxs,db} - Application layer
  • calva-backseat-driver.mcp.{server,requests,axs,fxs} - MCP server
  • calva-backseat-driver.integrations.calva.{api,editor,editor-util} - Calva integration
  • calva-backseat-driver.integrations.vscode.tools - VS Code Language Model tools

REPL Inspection Commands:

@calva-backseat-driver.app.db/!app-db                    ; See full state
(keys calva-backseat-driver.integrations.calva.api/calva-api)  ; Available Calva APIs
(:calva/output-buffer @calva-backseat-driver.app.db/!app-db)   ; Recent REPL output

Build Artifacts:

  • out/extension.js - Main extension bundle
  • dist/calva-mcp-server.js - MCP stdio wrapper
  • out/extension-tests.js - Unit tests

For deeper architecture details, see:

  • dev/EX_ARCHITECTURE.md - Action/effect system
  • dev/MCP_OVERVIEW.md - MCP protocol specifics
  • PROJECT_SUMMARY.md - Complete project overview