ADR-VSC02: VS Code Extension Architecture¶
This ADR defines the architecture of the VS Code extension that integrates with the tnh-gen CLI to provide GenAI-powered text processing capabilities within the editor.
- Status: WIP
- Date: 2025-01-28
- Updated: 2026-01-02
- Owner: Aaron Solomon
- Author: Aaron Solomon, Claude Sonnet 4.5
Context¶
TNH Scholar users work primarily in VS Code for text editing and translation workflows. The VS Code extension needs to provide:
- Prompt Discovery: Browse available prompts without leaving the editor
- Text Processing: Execute prompts on selected text or open files
- Configuration Management: Configure prompt directories and GenAI settings
- Provenance Tracking: Show metadata about generated content
Walking Skeleton Scope¶
v0.1.0 includes:
- TypeScript extension implementation
- Unit and integration tests
- One working command: "Run Prompt on Active File"
Separate tasks (post-validation):
- Extension packaging for VS Code Marketplace
- User-facing documentation and guides
Design Constraints¶
- No Direct GenAI Integration: Extension should not directly call OpenAI/Anthropic APIs
- CLI as Contract: Extension consumes
tnh-genCLI as stable interface - JSON Protocol: Structured JSON I/O enables programmatic consumption
- Error Handling: Extension must gracefully handle CLI errors with user-friendly messages
- CF01 Compliance: Configuration resolution follows ADR-CF01 ownership-based precedence; precedence is per-setting (owner category), not a single global order.
- OS01 Compliance: Implementation follows the object-service pattern (protocols/abstractions, stateful service objects, and composed collaborators instead of procedural sprawl).
Related Work¶
- ADR-VSC01: VS Code Integration Strategy - Establishes CLI-based architecture
- ADR-TG01: CLI Architecture - CLI command structure, error codes, configuration
- ADR-TG01.1: Human-Friendly CLI Defaults -
--apiflag for machine-readable contract output - ADR-TG02: Prompt System Integration - CLI β prompt system integration patterns
- ADR-OS01: Object-Service Architecture - Layered design, protocols, separation of concerns
- ADR-CF01: Runtime Context Strategy - Configuration discovery and precedence
PatternβPrompt Migration (Pending, Non-Blocking)¶
The codebase is undergoing a PatternβPrompt terminology migration (ADR-PT04). Current state:
- Code: Still references
TNH_PATTERN_DIRand~/.config/tnh_scholar/patterns/ - CLI:
tnh-genuses prompt terminology but reads from patterns directory - Docs: Use "prompt" terminology consistently
Impact on Extension: None. The extension calls tnh-gen list/run via --api flag, which abstracts the underlying directory structure. The CLI will continue working during and after the migration.
Sequencing Strategy: PatternβPrompt migration is Priority 1 but deferred until after VS Code extension walking skeleton complete (see TODO.md). This allows:
- Extension validation without simultaneous major changes
- Dogfooding: Use working extension to test migrated prompts
- Risk minimization: Avoid breaking extension during prompt migration
See ADR-PT04: Prompt System Refactor for migration details.
Decision¶
1. Extension Architecture¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VS Code Extension β
β (TypeScript, VSCode API, UI components) β
ββββββββββββββββββ¬βββββββββββββββββββββββββ¬ββββββββββββββββββββ
β β
βββββββββΌββββββββ ββββββββΌβββββββββ
β CLI Adapter β β UI Components β
β (spawn tnh-genβ β (prompts list,β
β parse JSON) β β progress, β
β β β config) β
βββββββββ¬ββββββββ βββββββββββββββββ
β
βββββββββΌββββββββ
β tnh-gen CLI β
β (Python) β
βββββββββββββββββ
2. CLI Invocation Strategy¶
Architecture:
The extension spawns tnh-gen as a child process and communicates via JSON using the --api flag (ADR-TG01.1).
Component: TnhGenCliAdapter
Responsibilities:
- Spawn
tnh-gensubprocess with appropriate arguments - Parse JSON responses from stdout
- Handle process lifecycle (spawn, monitor, cleanup)
- Throw structured errors on non-zero exit codes
Key methods:
listPrompts(options?): Promise<PromptListResponse>- Invokestnh-gen list --apirunPrompt(request): Promise<RunPromptResponse>- Invokestnh-gen run --apigetVersion(): Promise<VersionInfo>- Invokestnh-gen version --api
Protocol contract:
- Always pass
--apiflag for machine-readable output - Parse stdout as JSON - Contains response data
- stderr - Diagnostics only (warnings, trace IDs, debug info); log to Output channel, not used for UX logic
- Exit codes - 0 (success), 1-5 (error categories per ADR-TG01)
- Success payload schemas -
ListApiPayload,RunSuccessPayload,VersionPayloadinsrc/tnh_scholar/cli_tools/tnh_gen/types.py:120 - Error parsing - On non-zero exit, attempt JSON parse for structured error; if missing/unparseable, show a generic failure and log stderr tail
3. Error Handling¶
Component: CliError
Responsibilities:
- Parse JSON error responses from CLI stdout (ADR-TG01.1 Β§3.4)
- Map exit codes to user-friendly messages (ADR-TG01 Β§5)
- Extract diagnostics and suggestions from structured errors
- Provide fallback messages when JSON parsing fails
Exit code mapping:
- 0: Success
- 1: Policy error (budget exceeded, validation failed)
- 2: Transport error (API failure, network issue)
- 3: Provider error (model unavailable, rate limit)
- 4: Format error (invalid JSON, schema validation)
- 5: Input error (invalid arguments, missing variables)
Error response format (from --api):
{
"status": "failed",
"error": "Human-readable error message",
"diagnostics": {
"suggestion": "Try X to resolve",
"trace_id": "correlation-id",
"exit_code": 2
}
}
User-facing error display:
- Show
errorfield in VS Code notification - Optionally append
diagnostics.suggestionif present - Log
stderrandtrace_idto Output channel for debugging
4. Command Flow¶
Command: "Run Prompt on Active File":
Flow of control:
- Initialize CLI adapter with configured
tnh-genpath - Read VS Code settings (user + workspace)
- Resolve effective values per CF01 ownership for each setting
- Build temp config from effective values and pass
--config <temp.json>per call - List prompts via
tnh-gen --api --config <temp.json> list - Show QuickPick with prompt metadata (name, description, tags)
- Collect required variables via input boxes (one per required variable from metadata)
- Get active document content and save to temp file
- Execute prompt via
tnh-gen --api --config <temp.json> runwith input file and variables - Parse JSON response from stdout
- Open output file in split editor pane
- Show success notification with execution metadata (model, cost, time)
Error handling at each step:
- CLI spawn failure β show error notification, check CLI installation
- User cancellation β abort silently
- CLI error (non-zero exit) β parse structured error, show user-friendly message
- JSON parse failure β show generic error, log details to Output channel
Single invocation resolution (summary):
- Read VS Code settings (user + workspace)
- Resolve effective values per CF01 ownership
- Write temp config JSON
- Call
tnh-gen --api --config <temp.json> list/run - Render results or errors
5. Configuration Management¶
Component: ConfigManager
Responsibilities:
- Discover candidate
tnh-genCLI paths - Integrate with VS Code Python extension for virtualenv detection
- Manage extension settings via VS Code configuration API
- Materialize a temp
tnh-genconfig file per invocation (no persistent mutation) - Resolve setting values per CF01 ownership precedence
Candidate discovery (for cliPath):
- Workspace setting (
tnhScholar.cliPathin.vscode/settings.json) - User setting (
tnhScholar.cliPathin global VS Code settings) - Python extension (detect active interpreter's venv, derive tnh-gen path)
- PATH (fallback to system
tnh-gen)
Configuration source: VS Code settings API only (.vscode/settings.json or global settings, JSONC). No separate tnh-scholar.json file.
Settings mapping (VS Code β tnh-gen config):
tnhScholar.promptDirectoryβprompt_catalog_dirtnhScholar.defaultModelβdefault_modeltnhScholar.maxCostUsdβmax_dollars
The adapter writes these to a temp JSON config and passes it via the global --config flag (see ADR-TG01, implemented in src/tnh_scholar/cli_tools/tnh_gen/tnh_gen.py:25).
Ownership & precedence (per ADR-CF01):
| Setting | Ownership | Effective precedence |
|---|---|---|
tnhScholar.promptDirectory |
Project-owned | Workspace β User β Built-in |
tnhScholar.defaultModel |
User-owned | User β Workspace β Built-in |
tnhScholar.maxCostUsd |
User-owned | User β Workspace β Built-in |
tnhScholar.cliPath |
Project-owned | Workspace β User β Auto-detect β PATH |
Rationale for cliPath ownership: Project-owned to keep team workspaces reproducible and avoid per-user drift in multi-user environments.
Non-duplication rule: Extension does not read TNH_* env vars or infer registry semantics; it writes a temp config and calls tnh-gen. Env merging and TNHContext resolution remain inside the CLI per ADR-CF01.
Component: CliValidator
Responsibilities:
- Validate CLI executable existence and executability
- Check minimum version compatibility
- Provide actionable error messages for misconfiguration
- Enforce minimum compatible CLI version via
tnh-gen --api version
6. Extension Configuration Schema¶
// package.json (contributes.configuration)
{
"contributes": {
"configuration": {
"title": "TNH Scholar",
"properties": {
"tnhScholar.cliPath": {
"type": "string",
"default": null,
"description": "Path to tnh-gen CLI executable (auto-detected if not set)"
},
"tnhScholar.promptDirectory": {
"type": "string",
"default": null,
"description": "Path to prompt directory (overrides TNH_PROMPT_DIR)"
},
"tnhScholar.defaultModel": {
"type": "string",
"default": "gpt-4o-mini",
"description": "Default GenAI model for prompts"
},
"tnhScholar.maxCostUsd": {
"type": "number",
"default": 0.10,
"description": "Maximum cost per request (USD)"
}
}
}
}
}
Consequences¶
Positive¶
- Stable Contract: Extension depends only on CLI JSON protocol via
--apiflag, not Python internals - Version Independence: Extension and CLI can evolve independently
- Error Transparency: CLI exit codes and structured JSON errors (ADR-TG01.1) enable rich error handling
- Full Metadata Access:
--apiprovides complete prompt metadata, provenance, usage stats - Testability: CLI can be mocked for extension unit tests
- Reusability: CLI implementation (ADR-TG01/TG01.1/TG02) serves both VS Code and command-line users
- Human-Friendly CLI: Interactive CLI users get readable output by default, while extension gets full API metadata via
--api
Negative¶
- Process Overhead: Spawning Python process for each operation introduces latency (mitigated by keeping CLI operations fast)
- Version Synchronization: Extension must validate CLI version compatibility
- Error Mapping: Extension must parse CLI JSON errors and present user-friendly messages
Risks¶
- CLI Path Discovery: Auto-detection may fail in complex Python environments (mitigated by explicit configuration)
- Breaking Changes: CLI protocol changes require coordinated extension updates (mitigated by semantic versioning)
Alternatives Considered¶
Alternative 1: Direct Python Integration (via Python Extension)¶
Approach: Extension imports TNH Scholar Python modules directly via VS Code Python extension API.
Rejected: Tight coupling to Python implementation. Extension would need to handle Python environment activation, dependency resolution, and version compatibility.
Alternative 2: Language Server Protocol (LSP)¶
Approach: Create TNH Scholar language server that VS Code extension communicates with via LSP.
Rejected: Overengineering for initial MVP. LSP is designed for language features (completion, diagnostics), not GenAI operations.
Alternative 3: REST API¶
Approach: Run TNH Scholar as HTTP server, extension makes REST calls.
Rejected: Adds complexity (server lifecycle management, port conflicts). CLI spawn model is simpler for single-user desktop usage.
Open Questions¶
- Streaming Support: How should extension handle streaming CLI output (future
--streamingflag)? - Multi-Root Workspaces: How to handle different prompt directories per workspace folder?
- Offline Mode: Should extension cache prompt list to avoid repeated CLI calls?
Next Increment (Post v0.1.0)¶
- Add an optional pre-run UI to override a small subset of user-owned settings (e.g., model, max cost) for that invocation only.
- Overrides are ephemeral: written into the temp config and never persisted to VS Code settings.
Addendum: 2026-01-02 - Session Defaults and Session Vars Files¶
Context: v0.1.0 collects prompt variables per run, but repeated prompts benefit from session-scoped defaults without requiring persistent config edits.
Decision: - Introduce session vars files as optional inputs to the extension, stored as JSONC in user or workspace context (user-controlled files). - Session vars are auto-ingested during a session and merged with per-run inputs: - Precedence: prompt defaults β session vars β run-time inputs (inline vars or input prompts). - When a user supplies values during a run, the extension can offer Save session vars (including required vars) to the chosen session vars file.
Rationale: Session defaults reduce repetitive input while keeping persistence explicit and user-controlled. JSONC keeps files readable and consistent with VS Code settings practices.
Implementation Notes (future): - Session vars file location should be explicit in extension settings (user/workspace). - The extension should never write without user confirmation; saving is opt-in and scoped to the current session file.
References¶
Related ADRs¶
- ADR-VSC01: VS Code Integration Strategy - Overall VS Code strategy
- ADR-TG01: CLI Architecture - CLI command structure, error codes, configuration
- ADR-TG01.1: Human-Friendly CLI Defaults -
--apiflag for machine-readable contract output - ADR-TG02: Prompt System Integration - CLI β prompt system integration
- ADR-AT03: AI Text Processing Refactor - ai_text_processing refactor
External Resources¶
Changelog¶
2025-12-27: Updated for ADR-TG01.1 --api flag¶
- Changed all CLI invocations to use
--apiflag (replacing earlier--verbosedesign) --apiis the machine-readable API contract mode for structured output (ADR-TG01.1)- Updated error handling to parse structured JSON errors from
--apimode - Updated version checking to use
--apiflag - Added reference to ADR-TG01.1 in Related Work and References sections
- Interactive CLI users now get human-friendly output by default
- Extension receives full API metadata via
--apiflag - This is a breaking change from the initial
--verbosedesign, done while ADR is still in proposed status
This ADR focuses on VS Code extension architecture. CLI implementation details are defined in ADR-TG01, ADR-TG01.1, and ADR-TG02.