Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,9 @@ jobs:
${{ runner.temp }}/vimcode-e2e-${{ matrix.host }}/multiline-undo/*.txt
${{ runner.temp }}/vimcode-e2e-${{ matrix.host }}/multiline-undo/failure.json
${{ runner.temp }}/vimcode-e2e-${{ matrix.host }}/multiline-undo/failure.termctrl
${{ runner.temp }}/vimcode-e2e-${{ matrix.host }}/answer-paste/*.txt
${{ runner.temp }}/vimcode-e2e-${{ matrix.host }}/answer-paste/failure.json
${{ runner.temp }}/vimcode-e2e-${{ matrix.host }}/answer-paste/failure.termctrl
${{ runner.temp }}/vimcode-e2e-${{ matrix.host }}/*/failure-status.json
if-no-files-found: warn
retention-days: 7
13 changes: 9 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ This API surface makes text objects (`ciw`, `di"`), direct cursor manipulation,

```
src/
index.ts (477 lines) Dual v1 tui/v2 setup entry: intercept registration, action application
index.ts (707 lines) Dual v1 tui/v2 setup entry: intercepts, answer ownership, action application
editor.ts (28 lines) Host-coordinate horizontal selection bounds, preserving native anchor
v2.ts (246 lines) Experimental v2 TUI facade (host input, commands, state, events)
editing.ts (28 lines) Editor-local Vim contexts and shared ANSWER focus trait check
v2.ts (250 lines) Experimental v2 TUI facade (host input, commands, state, events)
vim/ Pure vim engine (thin barrel re-exports the public surface):
index.ts (7 lines) Barrel — public surface only. No export *, no internals.
types.ts (57 lines) Action union, VimState, Mode, Operator, Pending, Range, KeyEvent, HandlerResult, PromptAccess
Expand Down Expand Up @@ -90,9 +91,11 @@ test/
editor.test.ts (118) Host-coordinate boundaries and opaque selection-color forwarding
v2.test.ts (428) Experimental v2 facade contract and lifecycle tests
child-session-navigation.test.ts (267) #79: isolated intercept and real OpenTUI keymap navigation regressions
question-answer.test.ts (621) #78: common v1/v2 answer editing and host-key ownership regressions
question-answer-support.ts (464) Typed focused editor fixture with real OpenTUI keymap dispatch
leader.test.ts (125 lines) Unit tests for leader key matching functions
compat/ Optional real-host Python driver and test-only TUI fixture (not packaged)
e2e/ Terminal Control guards: shared harness, *.integration.ts and *.e2e.ts explicit entries (not unit-discovered or packaged)
e2e/ Terminal Control guards: shared harness, v1/v2 question setup fixtures, *.integration.ts and *.e2e.ts explicit entries (not unit-discovered or packaged)
```

**Data flow:**
Expand Down Expand Up @@ -172,7 +175,9 @@ just compat-unit # Pure checks for the optional host harness
just compat v1 /absolute/opencode 1.18.33 /canonical/empty/output # Isolated installed-artifact check
```

The `dev-tui.json` config is picked up only by `just dev`. Running `opencode` normally in this directory does not load the plugin. `just dev2` runs `scripts/dev2.ts` with the tested v2 package (or an explicit binary), loads local source through the root `tui.ts` shim, and isolates on-disk settings/history under `.dev2/`. The shim/launcher are not distributed; package installs still resolve `exports["./tui"]`. Both dev commands retain arrow navigation and add `ctrl+x j` to open children/picker; v1 uses `h`/`l` to cycle and `k` to return, while v2 uses `h`/`k` and `j`/`l` in the Composer. Visual mode captures the focused editor on entry and re-anchors at the new editor's cursor on the next eligible key after a prompt switch; overlay keys do not change ownership. Horizontal normalization probes `editBuffer.getTextRange()` in host display-cell coordinates and uses lower `editorView` selection calls to retain the renderer's native anchor. The v2 disabled setting is per activation: external storage reconciliation applies on reload, while local `/vim` updates both controller and form guard immediately. `just compat-unit` runs in CI; the Python/tmux live-host checks remain manual. Separate PR jobs run `just test-int` and `just test-e2e` on pinned v1/v2 hosts; harness parser checks are units, driver lifecycle checks are SDK/process integration, and real-host checks are product E2E. These entries use non-discovered `.unit.ts`/`.integration.ts`/`.e2e.ts` suffixes, so ordinary `bun test` and `just check` remain fast and do not launch the guards. `scripts/test-e2e.ts` infers canonical fresh OS-temp outputs, prints receipts, and privately bootstraps exact pins only when no matching PATH/override binary exists. Optional ignored `.env` recognizes only `VIMCODE_E2E_V1_BIN` / `VIMCODE_E2E_V2_BIN` paths (relative to repo root); process values win, CI ignores the file, and unknown/provider keys are never forwarded. Use `bun --env-file=/dev/null run test:e2e`, not bare `bun run`, on Bun 1.3.2 to prevent outer dotenv autoload; synthetic launcher integration tests verify suppression. The advanced `just test-e2e HOST BINARY VERSION OUTPUT` form and CI artifact paths remain supported. See `docs/test-e2e.md` for network/bootstrap limits (local v2 registry access blocked), Linux pending status, and the unchanged private SDK 1.2.1 cleanup boundary.
The `dev-tui.json` config is picked up only by `just dev`. Running `opencode` normally in this directory does not load the plugin. `just dev2` runs `scripts/dev2.ts` with the tested v2 package (or an explicit binary), loads local source through the root `tui.ts` shim, and isolates on-disk settings/history under `.dev2/`. The shim/launcher are not distributed; package installs still resolve `exports["./tui"]`. Both dev commands retain arrow navigation and add `ctrl+x j` to open children/picker; v1 uses `h`/`l` to cycle and `k` to return, while v2 uses `h`/`k` and `j`/`l` in the Composer. Visual mode captures the focused editor on entry and re-anchors at the new editor's cursor on the next eligible key after a prompt switch; overlay keys do not change ownership. Horizontal normalization probes `editBuffer.getTextRange()` in host display-cell coordinates and uses lower `editorView` selection calls to retain the renderer's native anchor. The v2 disabled setting is per activation: external storage reconciliation applies on reload, while local `/vim` updates both controller and form guard immediately. `just compat-unit` runs in CI; the Python/tmux live-host checks remain manual. Separate PR jobs run `just test-int` and `just test-e2e` on pinned v1/v2 hosts; harness parser checks are units, driver lifecycle checks are SDK/process integration, and real-host checks are product E2E. These entries use non-discovered `.unit.ts`/`.integration.ts`/`.e2e.ts` suffixes, so ordinary `bun test` and `just check` remain fast and do not launch the guards. `scripts/test-e2e.ts` infers canonical fresh OS-temp outputs, prints receipts, and privately bootstraps exact pins only when no matching PATH/override binary exists. Optional ignored `.env` recognizes only `VIMCODE_E2E_V1_BIN` / `VIMCODE_E2E_V2_BIN` paths (relative to repo root); process values win, CI ignores the file, and unknown/provider keys are never forwarded. Use `bun --env-file=/dev/null run test:e2e`, not bare `bun run`, on Bun 1.3.2 to prevent outer dotenv autoload; synthetic launcher integration tests verify suppression. The advanced `just test-e2e HOST BINARY VERSION OUTPUT` form and CI artifact paths remain supported. See `docs/test-e2e.md` for network/bootstrap limits (local v2 registry access blocked), Linux CI/startup diagnostics and the unchanged private SDK 1.2.1 cleanup boundary.

Question answer editing uses the shared controller on both hosts: require the focused editor's `traits.status === "ANSWER"` plus active question/form ownership, never a renderable id or an arbitrary textarea. Each answer editor has its own Vim state, snapshots, visual owner and leader sequence; the main prompt's context is suspended intact. Enter/Ctrl+Enter/Tab remain host-owned. Deferred editor actions validate focus, route, question ownership and disposal. Pending answer actions, including `:` palette transitions, drain in input order before the next key or native paste is handed to the host. Global palette actions still survive editor focus changes. During asynchronous palette mounting, keys and paste wait for the dialog's focused editor and are reconstructed through `keyInput.processParsedKey()`/`processPaste()`, one per turn; focus loss, closure or disposal drops remaining buffered input. Disabling Vim restores the owned editor's line cursor without styling unrelated overlays. v2 still protects printable leaders in non-answer textual forms without interpreting their other keys as Vim commands.

The experimental v2 adapter uses a raw renderer key listener because v2's public keymap does not expose intercepts; see `docs/opencode-v2-poc.md` for configuration and `docs/opencode-v2-strategy.md` for tested alternatives and remaining compatibility gaps.

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version

- Added separate PR CI guards via `just test-int` for harness integration and `just test-e2e` for cache-installed OpenCode v1/v2 E2E editing, with exact prompt checks and no-plugin controls; local runs remain on demand and outside the unit suite.
- Added no-argument `just test-e2e` defaults for both pinned hosts, optional single-host selection, fresh temp receipts, private pinned bootstrap, and ignored path-only `.env` binary overrides; existing explicit CI paths remain supported.
- Added v1/v2 Terminal Control regressions for answer append followed by native paste in one PTY write, with synthetic question setup fixtures and exact visible-answer checks.

### Fixed

- Custom question answers now support Vim editing on both OpenCode v1 and v2, starting in insert mode with isolated mode/undo state and preserving native answer submission, choice navigation and permissions ([#78](https://github.com/oribarilan/vimcode/issues/78)).
- Native paste in question answers now waits for queued Vim motions, preventing insertion at the previous cursor position.
- E2E startup failures retain the native exit status and current-scenario host error log instead of only reporting a closed readiness stream.

## [0.19.1] — 2026-10-04

Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,15 @@ In normal mode, keys are vim commands. Unrecognized keys get swallowed so you do

### Overlay passthrough

When OpenCode shows its own UI (command palette, `/sessions`, the `@` file picker, question prompts, permission prompts) vimcode steps aside. All keys pass through to the overlay until it closes.
When OpenCode shows its own UI (command palette, `/sessions`, the `@` file picker, question choices, permission prompts) vimcode steps aside. Those controls keep their native keybindings.

### Question answers

On both OpenCode v1 and v2, a question's **Type your own answer** editor starts in insert mode with a line cursor. Escape switches to normal without closing the editor; motions, operators, text objects, visual mode and undo work on the answer. `i` returns to insert. A further Escape in normal mode reaches OpenCode's cancel handler, which can discard an unsubmitted draft on v1.

Enter, Ctrl+Enter and Tab keep OpenCode's native question behavior, rather than vimcode's main-prompt submit/newline bindings. Each answer editor has its own Vim mode, pending commands, yank register and undo snapshots; returning to the main prompt restores its previous state. This also applies to child-session questions. `p` pastes the answer's own yank register, and empty-answer `j`/`k` never loads prompt history.

`:` still opens the command palette, which owns its keys until it closes. Timeline and conversation-navigation Vim shortcuts are inactive while editing an answer. Question choices and confirmation screens remain host-controlled.

### Subagent navigation

Expand Down
Loading
Loading