oh-my-dsh Architecture
oh-my-dsh is a terminal coding agent built by composing published DeepSeek Harness packages. The TUI owns terminal presentation and human interaction; Harness plugins continue to own sessions, models, tools, commands, permissions, skills, MCP integrations, and projections.
Boundaries
- Runtime:
@deepseek-ai/*packages are installed from npm at pinned versions and consumed through their published exports. - Product:
apps/omdshowns the CLI and runtime composition;packages/tui/omdsh-tuiowns the reusable terminal plugin suite. - References:
refs/deepseek-harness,refs/oh-my-pi, andrefs/piare read-only sources of architecture and interaction ideas. They never enter dependency resolution, builds, tests, or runtime execution.
The product deliberately avoids a second agent core. It adapts Harness capabilities to a local terminal without reimplementing their domain state.
Package layout
apps/omdsh/ @agi-fans/oh-my-dsh
βββ src/bin.ts CLI entry and argument handling
βββ src/boot.ts Harness tree boot
βββ src/plugin.ts `omdsh plugin` Profile installer
βββ config/cordis.yml product bundle insert
packages/tui/omdsh-tui/ @agi-fans/dsh-tui
βββ src/index.ts local provider plugin entry
βββ src/definition.ts provider-neutral TUI service
βββ src/runtime/ TTY provider, session runtime, runner, notices
βββ src/commands/ slash-command contribution plugins
βββ src/chrome/ theme, markdown, renderer, status, tool cards
βββ src/input/ keys, editor, clipboard, paste
βββ src/views/ transcript, overlays, search, copy
βββ src/session/ session controller and TUI settings
The TUI package exposes several Cordis entry points from one npm package because they share dependencies and a release cadence. A new npm package is justified only when a capability gains independent reuse, ownership, dependencies, or versioning.
Plugin ownership
βEverything is a pluginβ describes ownership rather than file count. A capability becomes a plugin when it has an independent lifecycle, configuration, dependency set, registration contract, scope, or replacement point.
- The local provider alone owns raw mode, key decoding, cursor placement, viewport state, and atomic terminal writes.
session-runtimeowns Agent creation, durable session creation and recovery, active-session replacement, model selection, Agent preset mounting, preset-derived tool exposure, projections, and cleanup.- Command plugins register metadata and handlers through
dsh-commands; the runner does not maintain a second command registry. - The Loop command owns its process-local scheduler and footer projection as a separate plugin. Repeated prompts still pass through
session-runtime; Loop state is not written into durable session history and is discarded when the active Agent changes. - Tool plugins own semantics and provider-neutral presentation intent. The TUI maps
ToolDefinition.presentCallandpresentResultinto terminal cards and retains a generic fallback. - Harness projection plugins own token, context, timing, title, and session statistics. The status line only formats their output.
session-runtimeprojects origin-classified descendant sessions into a live roster above the composer and can replace the parent viewport with one childβs transcript. Continuable children accept composer follow-ups through the subagent host queue (queueHostSubagentPromptfrom@deepseek-ai/dsh-subagent/internal), which keeps the user attribution and FIFO turn order; one-shot runs stay read-only. Child logs stay in their own sessions and are not replayed as parent transcript events.- Three delegation transports coexist and are chosen per delegation rather than configured globally. The two in-process backends (
subagent,subagent_fork) run the child Agent on the TUIβs own event loop and honor every parent-enforced start capability. The out-of-process one (subagent_isolated, overdsh-subagent-acp) spawns a whole child runtime instead, keeping a heavy delegationβs CPU off that loop; it advertises no start-time capabilities and implements no continuable run, so such a child cannot be steered after dispatch. - The human-interaction adapter connects approval and question services to terminal selectors without moving those domains into the provider.
Pure algorithms remain internal modules: ANSI parsing, display-cell width, Markdown formatting, editor movement, path matching, theme projection, frame diffing, viewport slicing, and overlay state transitions. They should not become runtime plugins until a second independently owned adapter creates a real seam.
Runtime composition
apps/omdsh/config/cordis.yml is the @agi-fans/oh-my-dsh product bundle, inserted over an empty $OMDSH_HOME/profiles/omdsh root. It composes:
- Cordis loader and timer infrastructure;
- the official DeepSeek LLM adapter, the dormant pi-ai multi-provider adapter, settings, credentials, default model, Agent preset roster, Code runtime, and Agent runtime;
- durable JSONL sessions, checkpointing, query, file and session references, title, statistics, and token projections;
- local attachment, filesystem, subprocess, sandbox, and permission providers, plus exactly one shell stack per host (
bashon POSIX,pwshon Windows) with its matching model-facing tool; - the Standard, PTC, Minimal, and Cordis Agent-plane compositions, plus Harness commands, compaction, todo, goal, plan, approval, questions, and subagents;
- filesystem skill discovery and project/user MCP server adapters;
- the local TUI provider, tool-presentation bridge, session runtime, human-interaction adapter, command contributions, startup notices, and runner.
Skills and MCP deployment details live in skills-and-mcp.md. User bundles from omdsh plugin add, the Profile cordis.patch.yml, and an optional $OMDSH_HOME/cordis.patch.yml overlay that composition at boot; omdsh --dump-config prints the result. examples/hello is the installable authoring fixture. See plugins.md.
Data and interaction flow
terminal input
β local TUI provider
β runner or command registry
β session runtime / Harness capability
β durable session events and projections
β provider-neutral transcript and status views
β differential terminal renderer
Ordinary messages enter the active Agent through session-runtime. Slash commands execute through the scoped Harness registry. The Agent preset is composed before publication and recorded through Harness session metadata and events for reconstruction; tool exposure is derived from that preset instead of a product-private session event. Model-visible composition is locked after the first prompt. Workflow and Access remain independent Harness-owned session state. Session events are the durable source for transcript replay; projection services provide derived status rather than TUI-owned counters. Tool calls and results settle into one card with distinct Input and Output sections. The composer-adjacent subagent roster shows task names and lifecycle states. Background stream chunks do not refresh it; the Agent Hub retains durable tool activity, and opening a child transcript restores its live output.
Terminal guarantees
- Layout uses terminal display cells, including ANSI sequences, CJK text, emoji, combining characters, and long unbroken content.
- The composer and two-line status footer stay anchored at the bottom while the transcript viewport scrolls independently.
- The
MainScreenRenderertreats native terminal scrollback as immutable snapshots. During stable geometry, finalized rows are painted immediately before they leave the live screen. A running turn stays mutable and ungrouped; when it ends, the live projection folds its work into one run. Production startup waits for the initial session projection. Opening or replacing a document starts a fresh row index: idle restoration replays the complete snapshot, while running restoration preserves the mutable suffix and rebuilds streaming content from buffered events. Projection refreshes, including preset and tool-catalog updates and subagent inspection, adopt only the current viewport without replaying history. Resume, session switches, forks, and/clearadd a labelled boundary;/newdoes so only after substantive transcript content. Fresh startup and projection refreshes need no boundary. Native scrollback is never erased. Direct terminals borrow the alternate screen for full-screen surfaces; multiplexers and ConPTY preserve host scrollback, and multiplexer resize bursts are coalesced before re-anchoring. The terminal provider enables 1000/1006 mouse reports whenever the main transcript overflows, in both folded and expanded views. It suspends them for full-screen surfaces and restores the reading position on return. The pinned user prompt and jump-to-latest control are viewport decorations and never enter transcript history. Requested command text results reveal the live tail; background notices and release notes preserve the reading position. Native drag selection is unavailable while those reports are enabled; keyboard copy paths remain available. The renderer owns painting and wraps each paint in one DEC 2026 synchronized write. - Settled transcript layouts are cached and the renderer emits row-level differences instead of repainting the complete screen.
- Modal selectors own input and cursor visibility until they settle, then restore the composer; a prompt displaces and restores any full-screen overlay it interrupted, so no confirmation can be accepted behind a stale surface.
- The first Ctrl-C clears or interrupts; a second Ctrl-C exits. Ctrl-D exits directly, and a durable session produces an
omdsh --resume <session-id>hint. - Pipe mode uses the same command and session semantics without claiming ownership of an interactive screen.
Public surface
The supported package exports are the provider, service definition, session runtime, human-interaction adapter, tool-presentation bridge, command groups, startup notices, and runner. Renderer, editor, Markdown, width, overlay, clipboard, and selector modules remain implementation details even when tests import them by relative path.
New contribution registries for themes, status segments, overlays, or key actions should be introduced only when at least two independently owned contributors require them. Configuration and internal state machines are preferable to speculative public seams.
Verification
- Pure rendering tests cover width, ANSI, CJK, emoji, borders, Markdown, tool cards, viewport behavior, and cursor targets.
- Runtime tests cover command registration, session creation and recovery, queueing, projections, model and permission selection, human interaction, and disposal.
pnpm smoke:happyboots the complete composition against the published Harness mock LLM path.pnpm smokeexercises the built command through a real PTY for raw input, rendering, interruption, and exit behavior.- Dependency-boundary checks require published npm packages, clean reference submodules, and no links or aliases into
refs/.
The exact commands required for a change are defined in AGENTS.md.