VSCode Integration Plan
Target: Upstream PRs to galaxyproject/galaxy-workflows-vscode via jmchilton/galaxy-workflows-vscode
Fork checkout: /Users/jxc755/projects/repositories/galaxy-workflows-vscode
Goal: Bring tool-state-aware language services to the existing extension using this monorepo’s infrastructure.
Existing Extension Architecture (Summary)
- Two LSP servers: Format2 (YAML, gx-workflow-ls-format2) + Native (JSON, gx-workflow-ls-native)
- Schema system: Custom YAML Salad loader in Format2 server (~400 lines), stale v19.09 schemas
- DI: Inversify for service injection
- Dual build: Node + WebWorker (works on vscode.dev)
- Cleaning: Basic property stripping via
cleanablePropertiessetting - No tool-state awareness:
state:typed asAny, zero completions inside it - No ToolShed integration: Only a test-toolshed warning rule
Phases
Phase 1: JSON Schema Integration for Structural Validation
Goal: Replace stale YAML Salad schemas with JSON Schema for Format2 workflow structure, generated directly from @galaxy-tool-util/schema.
Schema source: @galaxy-tool-util/schema defines the Effect Schemas for gxformat2/native workflows. The extension adds it as an npm dependency and calls JSONSchema.make() at activation time to produce the structural JSON Schema in-process — no server, no static files, no syncing.
- Add
@galaxy-tool-util/schemaas a dependency of the Format2 language server package - At server startup, generate structural JSON Schema:
import { GxFormat2WorkflowSchema } from "@galaxy-tool-util/schema"; import * as JSONSchema from "effect/JSONSchema"; const structuralSchema = JSONSchema.make(GxFormat2WorkflowSchema); - Add a
JsonSchemaNodeResolveralongside existingSchemaNodeResolver:- Walks JSON Schema
$ref/$defsinstead of YAML Salad records - Implements same interface:
getNodeFromPath(),getCompletionItems(),getHoverInfo()
- Walks JSON Schema
- Wire into Format2 language service via Inversify binding
- Verify completion/hover/validation parity with old YAML Salad path
- Keep YAML Salad loader as fallback initially; feature-flag the switch
No server dependency. The schema is generated in-process from the npm package. Schema updates arrive via npm update @galaxy-tool-util/schema. Web extension compatibility (Phase 7) may need a server fallback if the Effect dependency is too large for the web bundle — but that’s a later concern.
Test: Run existing extension integration tests with JSON Schema path enabled. Compare completion results.
What improves: Comment types, creator types, pick_value, when field, state/tool_state field types — all now have real schemas instead of Any?.
Phase 2: Tool Registry Service ✅ COMPLETE
Goal: Extension can resolve tool definitions from local cache + direct ToolShed fetch.
Delivered (branch wf_tool_state, commits a917f85 → eb261bc → e4a1472 → 445558e → c3bbe72):
-
VSCode settings added (
package.json):galaxyWorkflows.toolCache.directory(default~/.galaxy/tool_info_cache)galaxyWorkflows.toolShed.url(defaulthttps://toolshed.g2.bx.psu.edu)
-
ToolRegistryServiceImpl— thin ~60-line Inversify injectable wrapper around@galaxy-tool-util/core’sToolInfoService. Delegates all cache/fetch logic upstream; extension owns onlyconfigure(),hasCached(),listCached(),populateCache(), andcacheSize. No duplicate types, no copied cache logic. -
LSP handlers (
server/packages/server-common/src/services/toolCacheService.ts):GET_WORKFLOW_TOOL_IDS,POPULATE_TOOL_CACHE,GET_TOOL_CACHE_STATUSrequest identifiers inshared/src/requestsDefinitions.ts- Wired via Inversify +
server.ts
-
“Populate Tool Cache” command (
client/src/commands/populateToolCache.ts) — scans open workflow docs, pre-fetches referenced tools, shows progress notification. -
Status bar item (
client/src/statusBar.ts) — shows “Tools: X/Y cached” for current workspace. -
Test suite migrated to Vitest/ESM — removed Jest/CJS workarounds (
module: "commonjs"override,transformIgnorePatterns,moduleNameMapperfor@galaxy-tool-util/core). Tests now import upstream ESM packages directly. -
ToolStateValidatoradded upstream in@galaxy-tool-util/schema(report_modelsbranch) — bridgesToolInfoService→ToolStateDiagnostic[]without exposing Effect internals. Ready for Phase 3 to call.
Deferred from original plan: Web extension support (Phase 7) — ToolInfoService uses node:fs, excluded from web build for now.
Phase 3: Tool State Completions ✅ COMPLETE
Delivered (commits bf3cd04 → 0a9f709):
toolStateTypes.ts— Shared types (ToolParamhierarchy: base, select, boolean, section, repeat, conditional) + type guards + AST helpergetStringPropertyFromStepToolStateCompletionService— detectsstate/tool_statein node path (aftersteps/<name>), navigates AST to gettool_id/tool_version, fetches cached params, generates:- Property name completions with
param_name:insertText andgx_integer → integerdetail - Select option value completions (filtered by prefix)
- Boolean
true/falsevalue completions - Navigates into sections, repeats, conditionals (all branches)
- Filters hidden params and already-declared keys
- Property name completions with
GxFormat2CompletionService: optionalToolRegistryService, asyncdoComplete(), hooks state completions before schema resolutionGxFormat2WorkflowLanguageServiceImpl: injectsToolRegistryServicevia Inversify- Not implemented: default-value insertText; conditional branch filtering deferred to Phase 5
Tests: 17 integration tests (name completions, prefix filter, existing-key exclusion, select/boolean values, section/repeat/conditional navigation, tool_state key, uncached tool, insertText format, detail type field).
Phase 4: Tool State Validation + Hover ✅ COMPLETE
Delivered (commit 4497b09):
ToolStateValidationService— for each step withtool_id+state/tool_state:- Tool not in cache →
Informationdiagnostic ontool_idvalue (“run Populate Tool Cache”) - Unknown parameter name →
Warningwith diagnostic on key node - Invalid select value →
Errorwith diagnostic on value node + list of valid options - Recurses into sections, repeats, conditionals (all branches)
- Only validates structured YAML map state (skips JSON-string
tool_state) - Diagnostic source:
"Tool State"
- Tool not in cache →
GxFormat2HoverService— extended with optionalToolRegistryService; when hovering inside state block shows**name** type, label, help, select options list, ortrue | falsehintGxFormat2WorkflowLanguageServiceImpl:doValidation()callsToolStateValidationService; hover service receivesToolRegistryService
Not implemented: debounce (handled by LSP framework); numeric range validation; conditional branch-aware validation (Phase 5).
Tests: 14 integration tests (unknown param warning, select error, valid values, section recursion, multi-step, uncached info, hover name/type/help, hover select options, hover boolean).
Phase 5: Conditional Branch Filtering + Connection Completions
Goal: Smart completions that understand conditional branches and workflow connections.
- Conditional discriminator filtering (~50 lines):
- When cursor is inside a conditional in
state:, read the test parameter value from AST - Match against
constvalues in JSON SchemaoneOfvariants - Return only the matching branch’s properties for completions
- Validation already works correctly via
oneOf+const
- When cursor is inside a conditional in
- Connection source completions:
- When completing
source:inin:blocks:- Parse workflow to build step graph (labels, outputs, types)
- Suggest
step_label/output_namefrom upstream steps - Filter by type compatibility if output type info available from tool schemas
- Show type mismatch warnings as diagnostics for existing connections
- When completing
Phase 6: Enhanced Cleaning + Workspace Features
Goal: Tool-aware cleaning and workspace-level integration.
- Upgrade cleaning commands:
- Current: strips configurable property list (dumb)
- New: use tool definitions to identify stale keys (schema-aware clean)
- Preview diff before applying (existing
previewCleanWorkflowcommand) - Add “Clean All Workflows” command for workspace
- Workspace features:
- Auto-discover workflows on activation, offer to populate tool cache
- Watch for file changes — re-validate on save
- Resolve
run:subworkflow references for navigation (Ctrl+Click onrun: subworkflow.gxwf.yml)
- Conversion commands:
- “Convert to Format2” / “Convert to Native” commands
- Preview conversion result in diff editor
Phase 7: Web Platform + gxwf-web Integration
Goal: Full functionality on vscode.dev backed by gxwf-web server.
- Detect web environment → require proxy/gxwf-web URL for tool operations
- gxwf-web as backend:
- Extension can delegate validate/lint/clean operations to gxwf-web
- Contents API for remote file management
- Settings:
galaxy.workflows.gxwfWeb.url
- Bundle common tool schemas (“schema pack”):
- Pre-cache schemas for IWC corpus tools + popular ToolShed tools
- Ship as part of extension for offline use
- IndexedDB cache for fetched schemas in web context
Implementation Strategy for Upstream PRs
Each phase should be one or a few focused PRs:
- Phase 1: Single PR — JSON Schema structural validation (no new dependencies beyond the schema file)
- Phase 2: Single PR — ToolRegistryService + settings + “Populate Cache” command
- Phase 3: Single PR — Tool state completions
- Phase 4: Can bundle with Phase 3 or separate — validation + hover
- Phase 5: Two PRs — conditional filtering, then connection completions
- Phase 6: Multiple small PRs — cleaning upgrade, workspace features, conversion commands
- Phase 7: Separate PR series — web platform, gxwf-web integration
PRs should be self-contained and independently useful. Phase 1 improves structural validation with zero new infrastructure. Phase 2 adds infrastructure. Phases 3-4 deliver the headline feature (tool state completions).
Dependencies on This Monorepo
| Extension needs | Monorepo provides |
|---|---|
| Structural JSON Schema | @galaxy-tool-util/schema as npm dep — JSONSchema.make(GxFormat2WorkflowSchema) in-process |
| Per-tool JSON Schema | @galaxy-tool-util/schema — createFieldModel() + JSONSchema.make() from cached ParsedTool |
| Tool metadata + cache | @galaxy-tool-util/core as npm dep — ToolCache, fetchFromToolShed() |
| Report model shapes | @galaxy-tool-util/schema report-models (for gxwf-web integration, Phase 7) |
The extension depends on the monorepo as npm packages for Phases 1-4. No server dependency until Phase 7 (web platform).
Unresolved Questions
Should the JSON Schema for structural validation be committed to the extension repo (static), or fetched at runtime?Resolved: Generated in-process from@galaxy-tool-util/schemanpm package viaJSONSchema.make(). No server, no static files.- The extension uses Inversify for DI — should ToolRegistryService use this, or is it simpler as a plain class? (Inversify is already entrenched, so probably use it.)
- For web compatibility: can we get CORS headers added to ToolShed 2.0’s
/api/tools/endpoints? This would eliminate the need for a proxy in the web case. - The extension’s
SchemaLoaderis deeply integrated — should Phase 1 replace it entirely, or add a parallel path and switch per-setting? - How to handle tool version resolution when steps omit
tool_version? Default to latest from ToolShed? Show a warning?