Skip to content

Latest commit

 

History

History
226 lines (166 loc) · 12.7 KB

File metadata and controls

226 lines (166 loc) · 12.7 KB

Lowest-Coupling Project Framework

Goal

Design v1 as an independent OpenCode plugin that emits Windows Terminal OSC 9;4 progress sequences without modifying OpenCode core/TUI or oh-my-openagent internals. After T1/T5 discovery, the current verified v1 path is a local .js or .ts named event-hook plugin, WindowsTerminalProgressPlugin, returning { event }. This path is best-effort because it cannot fully prove the current visible main session. A public TUI plugin entrypoint or bridge remains future or experimental until it reliably proves visible-session ownership.

The plugin should fail soft: if hooks, stdout, /dev/tty, tmux passthrough, or terminal support are unavailable, OpenCode itself must keep working and the progress indicator may simply be disabled or degraded.

Non-blocking behavior is a hard invariant. No code path in this plugin may block, delay, crash, or otherwise interfere with OpenCode's main runtime; terminal progress is always best-effort.

Coupling Boundaries

  • Depend only on OpenCode's public plugin loading and hook/event surface.
  • Do not patch OpenCode core, TUI state management, provider logic, tool execution, or agent configuration for v1.
  • Use the local named event-hook plugin as the current verified v1 runtime path. Treat TUI entrypoints as future or experimental until TUI-visible session state is verified. Server hooks may exist only as limited/best-effort support and must not claim product completeness.
  • Do not import from or modify oh-my-openagent internals. Treat oh-my-openagent as another OpenCode plugin that may be loaded beside this one.
  • Do not listen to oh-my-openagent child-agent/task internals as separate progress sources. Terminal progress represents the current visible OpenCode main session/main agent only.
  • Keep all OpenCode-specific hook names and payload parsing in one adapter module so OpenCode API changes have a small blast radius.
  • Keep terminal progress generation independent from OpenCode so it can be unit-tested and reused from another entrypoint if plugin hooks prove insufficient.
  • Never perform long-running, synchronous, retry-loop, network, package-install, or filesystem-heavy work inside OpenCode hook handlers. Hook handlers should compute the next progress state and attempt a bounded best-effort write only.

Current Layout

The implementation follows this low-coupling layout.

.
├── RPD.md
├── FRAMEWORK.md
├── AGENTS.md
├── README.md
├── package.json
├── src/
│   ├── plugin/
│   │   ├── event.ts
│   │   ├── index.ts
│   │   ├── server.ts
│   │   └── tui.ts
│   ├── opencode/
│   │   ├── tui-state-adapter.ts
│   │   └── state-adapter.ts
│   ├── core/
│   │   ├── config.ts
│   │   ├── progress-state.ts
│   │   └── state-machine.ts
│   └── terminal/
│       ├── osc94.ts
│       ├── tmux.ts
│       ├── tty-target.ts
│       └── writer.ts
└── test/
    ├── config.test.ts
    ├── event-plugin.test.ts
    ├── integration-harness.test.ts
    ├── osc94.test.ts
    ├── scaffold.test.ts
    ├── state-adapter.test.ts
    ├── state-machine.test.ts
    ├── tmux.test.ts
    ├── tui-state-adapter.test.ts
    └── writer.test.ts

Use only executable commands from package.json: npm run typecheck, npm run build, npm test, npm run qa:smoke:osc, and npm run qa:fixtures. Use npm pack --dry-run for package contents checks without publishing.

Module Responsibilities

src/plugin/index.ts

OpenCode package entrypoint. It keeps the package default export available for the future TUI path, but current docs must not present that default as the proven local runtime path. Initialization failures must disable this plugin rather than fail OpenCode startup.

src/plugin/event.ts

Current verified OpenCode local runtime entrypoint. It exports the named WindowsTerminalProgressPlugin function for .js or .ts local plugin auto-scan and explicit plugin config loading. It returns { event }, reads only verified event-hook payloads, and maps session.status, session.idle, session.error, and permission.updated to internal progress events. It must document question/input and cancellation as unsupported on this path unless future public contract and real observation evidence changes that.

src/plugin/tui.ts

Public TUI plugin entrypoint. It should read only verified public TUI-visible state such as current route/session/status/permission/question, convert that state to internal semantic events, and write progress best-effort. If the current visible session cannot be proven, it must skip output.

This path is future or experimental for v1. Do not describe it as product-complete until reliable visible-session proof exists.

src/plugin/server.ts

Limited server plugin entrypoint. It must not emit product-complete progress unless a future verified bridge proves event ownership for the current visible main session. For now it may be no-op or explicitly limited to documented diagnostics/tests.

src/opencode/tui-state-adapter.ts

The only module that knows TUI-specific public state shapes. If OpenCode changes TUI state fields, adapt here first.

src/opencode/state-adapter.ts

Converts verified OpenCode event-hook or TUI state into internal semantic events. If OpenCode does not expose a distinct question/input or cancellation state through a released public surface, document that limitation instead of guessing from unrelated signals.

The adapter emits internal semantic events:

  • busy
  • permissionNeeded
  • questionNeeded
  • idle
  • error
  • cancelled

When oh-my-openagent spawns sub-agents or background tasks, do not emit progress transitions for each child completion. Child-agent work is already part of the main OpenCode session from the terminal user's perspective; only main-session visible states should drive tab/taskbar progress.

Current event-hook v1 filters child/background sessions best-effort through client.session.get({ path: { id } }), parentID, and recognizable subagent titles. If lookup fails or metadata is missing, the plugin must fail soft and avoid blocking OpenCode.

src/core/progress-state.ts

Defines the fixed v1 mapping from internal state to Windows Terminal progress state:

Internal State OSC 9;4 State Progress
busy 3 0
permissionNeeded 4 100
questionNeeded 4 100
idle 0 0
error 2 100
cancelled 0 0 by default

Do not introduce determinate percentages unless OpenCode exposes reliable numeric progress.

src/core/state-machine.ts

Owns transition behavior and duplicate suppression. It should ensure idle and cancellation clear the indicator by default, while error remains visible until the next activity or explicit clear.

src/core/config.ts

Defines defaults and validates a small config object. It should not depend on oh-my-openagent config loading or mutate global OpenCode config.

Suggested shape:

{
  "terminalProgress": {
    "enabled": true,
    "mode": "windows-terminal",
    "clearOnIdle": true,
    "showWarningOnPermission": true,
    "showWarningOnQuestion": true,
    "showError": true,
    "tmuxPassthrough": "auto",
    "outputTarget": "auto",
    "clearOnCancellation": true
  }
}

src/terminal/osc94.ts

Pure OSC sequence encoder. It should have no OpenCode, filesystem, process, or tmux dependency.

src/terminal/tty-target.ts

Chooses the active terminal output target. Prefer process.stdout only when it reaches the active OpenCode TUI terminal; use /dev/tty when plugin stdout is captured or detached.

src/terminal/tmux.ts

Detects TMUX and wraps OSC bytes with tmux passthrough when configured. Documentation must still mention that user tmux config may need allow-passthrough.

src/terminal/writer.ts

Single write boundary for OSC output. It should isolate write failures so the progress plugin cannot break OpenCode sessions. Writes must be bounded and best-effort; if the target blocks or errors, disable or skip future writes rather than retrying in the hook path.

Non-Blocking Invariants

  • Plugin initialization failure must not prevent OpenCode from starting.
  • Hook handler failure must not fail or delay the OpenCode action that triggered the hook.
  • TTY detection and opening must happen lazily or during safe initialization, not repeatedly in hot hook paths.
  • Terminal writes must not wait on unbounded I/O, retries, timers, network calls, package installs, or child processes.
  • Configuration parsing errors should fall back to safe defaults or disable the plugin, never throw into OpenCode.
  • Logging, if added, must be best-effort and must not block hook handling.
  • Tests must include failure cases for config errors, missing /dev/tty, write errors, and repeated hook events.

Loading Strategy

  • Prefer project-local plugin loading during development so this feature can be tested without changing global OpenCode behavior.
  • For user installation, load it as a separate OpenCode plugin entry beside oh-my-openagent, not inside oh-my-openagent.
  • Keep the package name distinct from oh-my-openagent and avoid shared config file names.
  • Account for the oh-my-opencode to oh-my-openagent rename transition by never using either basename for this plugin's package, config, logs, or docs examples.
  • Avoid assuming plugin ordering semantics unless OpenCode documents them. This plugin should observe session state and write terminal progress only; it must not mutate hook payloads that other plugins may rely on.
  • If oh-my-openagent launches many child agents, this plugin should still only reflect the main OpenCode session's user-visible busy/waiting/error/idle state. Do not attempt to summarize child-agent counts, child completions, or background-task progress in v1.

Example final shape, once the plugin is packaged:

{
  "plugin": [
    "existing-user-plugin",
    "opencode-windows-terminal-progress"
  ]
}

This is illustrative only. Preserve any existing user plugin entries, append this package as its own entry, and don't assume plugin ordering.

Upgrade Risk Mitigations

  • Pin all OpenCode-hook-specific logic to src/plugin/opencode-events.ts and cover it with focused tests.
  • Treat OpenCode event hooks as an integration boundary, not a durability guarantee. Do not rely on asynchronous cleanup work completing after a fire-and-forget idle/error event unless verified against the current OpenCode version.
  • Keep terminal modules pure or near-pure so OpenCode upgrades cannot affect OSC encoding behavior.
  • Do not require oh-my-openagent to be present; support running with or without it.
  • After OpenCode upgrades, validate plugin loading, hook firing, and @opencode-ai/plugin dependency freshness if OpenCode's upgrade path does not update plugin dependencies automatically.
  • After oh-my-openagent upgrades, verify both plugins still appear in the OpenCode plugin list and that this plugin does not suppress, reorder, or depend on oh-my-openagent behavior.
  • When troubleshooting oh-my-openagent coexistence, check for legacy oh-my-opencode entries or config files before blaming this plugin.
  • Verify that oh-my-openagent sub-agent completion does not clear the progress indicator while the main agent is still waiting, synthesizing, or otherwise active.

Validation Gates

Before calling v1 complete, verify:

  • Direct Windows Terminal smoke sequence from RPD.md works.
  • Same smoke sequence works over SSH from Windows Terminal.
  • The plugin emits running, warning, error, and clear states for normal completion, long-running generation, permission request, question/input prompt, error, and user cancellation.
  • OpenCode works normally when terminal progress is disabled.
  • OpenCode works normally if the plugin cannot open /dev/tty.
  • OpenCode startup and hook execution continue normally if plugin initialization, config parsing, TTY detection, or terminal writes fail.
  • oh-my-openagent still loads and its agents/tools remain available when both plugins are configured.
  • oh-my-openagent child-agent/background-task completion does not independently trigger warning, error, or clear states.
  • tmux behavior is either implemented with passthrough or clearly documented as requiring user configuration.

Explicit Non-Goals For v1

  • No OpenCode core/TUI patch unless plugin event coverage or active TTY access proves unreliable.
  • No oh-my-openagent fork, patch, internal import, hook composition, or config mutation.
  • No desktop notifications, audio alerts, GUI, dashboard, terminal multiplexer, or generic notification framework.
  • No broad cross-terminal abstraction beyond Windows Terminal-compatible OSC 9;4 behavior.