Skip to main content
← Back to list
01Issue
FeatureOpenSwamp Club
AssigneesNone

Port remaining CLI commands to libswamp + renderer pattern

Opened by swampadmin · 3/19/2026

The swamp CLI is being refactored to separate domain orchestration from presentation logic per design/libswamp.md and design/rendering.md. Every operation becomes an async function* generator in src/libswamp/ yielding typed events, consumed by mode-specific Renderer<E> classes via consumeStream().

3 commands are already ported: auth whoami, model method run, workflow run. ~55 commands remain with inline presentation logic in src/presentation/output/.

Top constraint: Output must not change (same log lines, same JSON shapes). Domain logic must not change. Commands migrate incrementally.

Architectural Decisions

Ink search commands become full Renderer<E> implementations with a distinct InkXxxRenderer (or TuiXxxRenderer) class — not a log renderer that happens to use Ink. The Ink renderer's async completed handler launches the interactive TUI and stores the user's selection. The renderer extends the base interface (e.g., selectedItem(): T | undefined), matching the existing WorkflowRunRenderer.workflowFailed() pattern. The factory selects the Ink renderer when in log mode (TTY). .tsx components move to src/presentation/renderers/.

Confirmation prompts (delete, gc, overwrite) live in the CLI layer before calling the generator. Complex "what will be affected" info uses a two-phase pattern: preview generator (read-only) -> CLI prompt -> execute generator.

Editor launch is injected via deps.openEditor(path), making generators testable.

Auth login browser flow modeled as a generator with I/O injected through deps.

Per-Command Migration Checklist

For every command:

  1. Define event type union (kind discriminant, completed + error terminals)
  2. Define XxxDeps interface + createXxxDeps() factory
  3. Implement async function* generator (zero presentation logic, ctx.logger debug/trace only)
  4. Implement LogXxxRenderer + JsonXxxRenderer (+ InkXxxRenderer for search commands). Verify output parity.
  5. Update CLI handler to pure orchestration: wire deps, create renderer, consumeStream()
  6. Export from src/libswamp/mod.ts, delete old src/presentation/output/xxx_output.ts
  7. Generator unit tests with fake deps; existing tests pass unchanged

Batches

  • Batch 0: Infrastructure Prep

    Add shared error factories to src/libswamp/errors.ts:

    • notFound(entityType, idOrName)
    • alreadyExists(entityType, name)
    • validationFailed(message, details?)

    Create libswamp directories: data/, vaults/, extensions/, repo/

  • Batch 1: Simple Read-Only Gets (~10 commands)

    Pattern: resolve entity by ID/name -> not found = error -> found = completed with data. Reuse existing data structures from *_output.ts as event payloads.

    • model getmodel_get.ts + model_get_output.ts
    • workflow getworkflow_get.ts + workflow_get_output.ts
    • vault getvault_get.ts + vault_get_output.ts
    • data getdata_get.ts + data_get_output.ts (two paths: model-scoped vs workflow-scoped, handle via input)
    • model output getmodel_output_get.ts + model_output_get_output.ts (partial-ID matching is domain logic)
    • workflow history getworkflow_history_get.ts
    • model method history getmodel_method_history_get.ts
    • model method describemodel_method_describe.ts + model_method_describe_output.ts
    • type describetype_describe.ts + type_describe_output.ts
    • vault describevault_describe_output.ts
  • Batch 2: List/Versions/Validate/Stats (~12 commands)

    Pattern: collection results, possibly multi-step aggregation. Validate commands need validation_result event kind. Log commands inject LogFileReader through deps.

    • data listdata_list.ts + data_list_output.ts
    • data versionsdata_versions.ts + data_versions_output.ts
    • vault list-keysvault_list_keys.ts + vault_list_keys_output.ts
    • extension listextension_list.ts + extension_list_output.ts
    • model validatemodel_validate.ts + model_validate_output.ts
    • workflow validateworkflow_validate.ts + workflow_validate_output.ts
    • workflow schemaworkflow_schema.ts + workflow_schema_output.ts
    • workflow history logsworkflow_history_logs.ts
    • model method history logsmodel_method_history_logs.ts
    • model output logsmodel_output_logs.ts
    • model output datamodel_output_data.ts
    • telemetry statstelemetry_stats.ts + telemetry_stats_output.ts
  • Batch 3: Simple Mutations (~7 commands)

    Pattern: validate -> create/mutate -> save -> yield completed. model create injects resolveModelType as dep. vault create resolves provider config in generator.

    • model createmodel_create.ts + model_create_output.ts
    • workflow createworkflow_create.ts + workflow_create_output.ts
    • vault createvault_create.ts + vault_create_output.ts
    • data renamedata_rename.ts + data_rename_output.ts
    • auth logoutauth_logout.ts
    • repo initrepo_init.ts + repo_output.ts
    • versionversion.ts
  • Batch 4: Search Commands (~11 commands)

    Pattern: generator fetches + filters, yields completed with results. Three renderer classes per command: JsonXxxRenderer (serializes), InkXxxRenderer / TuiXxxRenderer (interactive TUI — its own class, not a log renderer), and optionally a plain LogXxxRenderer for non-TTY log output. The Ink renderer's async completed handler launches the TUI and stores the selection. Extended interface: selectedItem(): T | undefined. Factory selects Ink renderer when in log mode with TTY. CLI chains follow-up generator if needed. .tsx components move to src/presentation/renderers/.

    • model searchmodel_search.ts + model_search_output.tsx
    • vault searchvault_search.ts + vault_search_output.tsx
    • data searchdata_search.ts + data_search_output.tsx
    • workflow searchworkflow_search.ts + workflow_search_output.tsx (follow-up: run workflow)
    • extension searchextension_search.ts + extension_search_output.tsx (follow-up: install)
    • type searchtype_search.ts + type_search_output.tsx
    • vault type searchvault_type_search.ts + vault_type_search_output.tsx
    • model method history searchmodel_method_history_search.ts + model_output_search_output.tsx
    • model output searchmodel_output_search.ts + model_output_search_output.tsx
    • workflow history searchworkflow_history_search.ts + workflow_history_search_output.tsx
    • workflow run searchworkflow_run_search.ts
  • Batch 5: Confirmation-Guarded Mutations (~6 commands)

    Pattern: two-phase (preview generator -> CLI prompt -> execute generator) or single-phase with pre-check. extension pull has recursive deps + conflict detection (most complex).

    • model deletemodel_delete.ts + model_delete_output.ts
    • workflow deleteworkflow_delete.ts + workflow_delete_output.ts
    • vault putvault_put.ts + vault_put_output.ts
    • data gcdata_gc.ts + data_gc_output.ts
    • extension pullextension_pull.ts + extension_pull_output.ts
    • extension rmextension_rm.ts + extension_rm_output.ts
  • Batch 6: Edit Commands (~3 commands)

    Pattern: generator resolves entity + handles stdin/editor via deps.openEditor(path). Search-first mode (no arg): chain search renderer -> selectedItem() -> edit generator.

    • model editmodel_edit.ts + model_edit_output.ts
    • workflow editworkflow_edit.ts + workflow_edit_output.ts
    • vault editvault_edit.ts + vault_edit_output.ts
  • Batch 7: Complex Evaluation (~2 commands)

    Pattern: generator takes single/all mode in input. CEL evaluation + definition caching in generator. Locking injected through deps. Most domain-logic-heavy generators.

    • model evaluatemodel_evaluate.ts + model_evaluate_output.ts
    • workflow evaluateworkflow_evaluate.ts + workflow_evaluate_output.ts
  • Batch 8: Remaining Commands (~15 commands)

    Mixed bag: auth login (browser flow via deps), audit/summarise (aggregation in generator), extensions (API calls via deps), datastore (S3 via deps), source, issue, update.

    • auth loginauth_login.ts + auth_login_output.ts
    • auditaudit.ts + audit_output.ts
    • summarisesummarise.ts + summarise_output.ts
    • extension pushextension_push.ts + extension_push_output.ts
    • extension updateextension_update.ts + extension_update_output.ts
    • extension yankextension_yank.ts + extension_yank_output.ts
    • extension fmtextension_fmt.ts + extension_fmt_output.ts
    • datastore setupdatastore_setup.ts + datastore_output.ts
    • datastore statusdatastore_status.ts + datastore_output.ts
    • datastore syncdatastore_sync.ts + datastore_output.ts
    • datastore lockdatastore_lock.ts + datastore_output.ts
    • source path / fetch / cleansource_*.ts + source_output.ts
    • issue bug / featureissue_*.ts + issue_output.ts
    • updateupdate.ts + update_output.ts

Reference Implementations

Command Generator Renderer
auth whoami src/libswamp/auth/whoami.ts src/presentation/renderers/auth_whoami.ts
model method run src/libswamp/models/run.ts src/presentation/renderers/model_method_run.ts
workflow run src/libswamp/workflows/run.ts src/presentation/renderers/workflow_run.ts

Key Infrastructure

  • src/libswamp/context.tsLibSwampContext, createLibSwampContext()
  • src/libswamp/stream.tsconsumeStream, EventHandlers, result, withDefaults
  • src/libswamp/errors.tsSwampError + error factories
  • src/libswamp/testing.tscollect, assertCompletes, assertErrors
  • src/presentation/renderer.tsRenderer<E> interface

Design Docs

  • design/libswamp.md — Event stream architecture, generator pattern, deps injection
  • design/rendering.md — Renderer interface, factory pattern, logging boundaries
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

3/19/2026, 5:48:32 PM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.