Skip to content
Open
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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
name: test + typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Test
run: bun test

- name: Typecheck
run: bun run check
59 changes: 52 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
Windows Terminal integration for [oh-my-pi](https://github.com/can1357/oh-my-pi).

When you `/fork` a session, the session you just left is reopened in a new
Windows Terminal **split pane** instead of being lost — and you get commands and
a tool for opening forks/sessions in panes, tabs, or windows on demand. All via
Windows Terminal **split pane** instead of being lost. You also get commands and
tools for opening forks/sessions in panes, tabs, or windows on demand, for
**discovering** other omp terminals (even ones this extension didn't start), and
for **arranging** several sessions into an even columns/rows layout — all via
`wt.exe`; no oh-my-pi core changes.

> Requires [Windows Terminal](https://aka.ms/terminal) (`wt.exe`). Everything is
Expand All @@ -17,10 +19,13 @@ a tool for opening forks/sessions in panes, tabs, or windows on demand. All via
| Surface | Behavior |
| --- | --- |
| **Auto-pane on `/fork`** | After the built-in `/fork`, the previous session is reopened in a split pane (`omp --resume <previous>`). The native `/fork` is otherwise unchanged. |
| `/wtpane [fork\|current]` | Open this session in an adjacent split pane. |
| `/wtpane [fork\|current] [vertical\|horizontal]` | Open this session in an adjacent split pane (optional split direction). |
| `/wttab [fork\|current]` | Open this session in a new tab. |
| `/wtwindow [fork\|current]` | Open this session in a new window. |
| `spawn_session_pane` (tool) | LLM-callable: open a `fork` / `resume` / `new` session in a `pane` / `tab` / `window`. |
| `windows-terminal-control` (skill) | Teaches the agent to drive `wt.exe` for multi-pane layouts — split/tile panes, open tabs/windows, and arrange omp sessions into a grid. Activates on "arrange/split/organize my terminal" or "arrange all my omps". |
| `spawn_session_pane` (tool) | LLM-callable: open a `fork` / `resume` / `new` session in a `pane` / `tab` / `window`, with optional `title` / `profile`. |
| `list_omp_sessions` (tool) | LLM-callable: list recent omp terminals — **including ones this extension did not start** — from the core breadcrumb registry, newest first. |
| `arrange_sessions` (tool) | LLM-callable: open several sessions as even **columns** or **rows** in one window, each resumed in its recorded directory. |
| `windows-terminal-control` (skill) | Teaches the agent to drive `wt.exe` for bespoke multi-pane layouts beyond `arrange_sessions` (custom grids, focus moves). Activates on "arrange/split/organize my terminal" or "arrange all my omps". |

`/branch` is **not** hooked on purpose — branching is a lightweight "escape and
pick a message" navigation, not something that warrants a new pane.
Expand All @@ -34,6 +39,40 @@ pick a message" navigation, not something that warrants a new pane.
designed for concurrent writers, so prefer `fork` unless you specifically want
a second view and accept the risk.

## Arranging & discovering sessions

`arrange_sessions` opens several omp sessions together in one window — evenly
sized **columns** (side by side) or **rows** (stacked), each resumed in its
recorded working directory:

- Pass explicit `sessions` (ids/prefixes/paths, in order), or omit them to
auto-pick the most recent terminals.
- The sessions need **not** have been started by this extension — discovery is
agnostic to that. They come from oh-my-pi **core's** breadcrumb registry
(`~/.omp/agent/terminal-sessions/*` — two lines per terminal: cwd, then session
file). `list_omp_sessions` exposes the same list (newest first, each with its
recorded cwd) so the agent can pick.
- **Coverage is partial.** Core records a breadcrumb only for a **persisted**
session in an **identifiable terminal** — on Windows that's Windows Terminal
(`WT_SESSION` set) or a multiplexer (tmux/zellij/kitty/wezterm); a plain
cmd/PowerShell/conhost console sets no `WT_SESSION` and leaves none. Only the
**latest** session per terminal is kept. So a session run in a bare console,
non-interactively, or older than its terminal's latest won't appear here (its
`.jsonl` still lives under `~/.omp/agent/sessions/`).
- **To see the rest**, `list_omp_sessions` takes `source: "all"`, which scans the
full session store (`~/.omp/agent/sessions/`) and recovers each session's cwd
from its `.jsonl` header — covering the breadcrumb-less sessions above (at the
cost of no per-terminal "current"/recency context and a heavier walk).
`arrange_sessions` likewise resolves explicit `sessions` against the store, so
even a breadcrumb-less session arranges in its recorded directory.

This **opens a fresh arranged layout** of resumable sessions; it can't move or
reflow panes that are already running (a process is bound to its pane). Recency
is a "last seen" signal, not proof a session is still open — resuming one that's
live in another terminal means two processes appending to one session file, which
oh-my-pi's journal isn't built for. For bespoke layouts (true grids, focus moves)
the bundled `windows-terminal-control` skill drives `wt.exe` directly.

## Install

Requires oh-my-pi ≥ 16.0.0.
Expand All @@ -56,7 +95,8 @@ type-only and provided by the host at load time.
| Env var | Default | Effect |
| --- | --- | --- |
| `OMP_WT_AUTOPANE` | on | Set to `0` to disable the auto-pane-on-`/fork` behavior. |
| `OMP_BIN` | `omp` | Executable used to launch the spawned session. |
| `OMP_BIN` | `omp` | Command used to launch the spawned session. May include arguments — it is split on whitespace (e.g. `OMP_BIN="omp --some-flag"`). |
| `PI_CODING_AGENT_DIR` | `~/.omp/agent` | Where session discovery reads the breadcrumb registry. Honored from oh-my-pi core; set only if you've overridden the agent directory. |

## How it works

Expand Down Expand Up @@ -92,10 +132,15 @@ directory.

```sh
bun install
bun test # pure wt.exe argv / launcher builders
bun test # pure builders, layout chaining, and session discovery
bun run check # tsc --noEmit
```

The bundled **`windows-terminal-dev`** skill (`skills/windows-terminal-dev/`) is a
contributor guide for this codebase — the type-only host API, the
`wt.exe`-via-`cmd.exe` mechanic and how to verify `;` argv quoting, the layout
sizing math, and breadcrumb discovery.

## License

MIT
12 changes: 8 additions & 4 deletions skills/windows-terminal-control/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ layouts — in particular, to open several omp sessions arranged together.

- The user wants to **arrange / split / tile / organize** their terminal, or open
multiple omp sessions **side by side** ("arrange all my omps", "put these in a grid").
- For opening **one** session in a single pane/tab/window, prefer the
**`spawn_session_pane` tool** — it's gotcha-free. Use this skill for **multi-pane
layouts** the tool can't express.
- For opening **one** session in a pane/tab/window, prefer the **`spawn_session_pane`
tool**; for an even **columns/rows** layout of resumable sessions, prefer the
**`arrange_sessions` tool** — both are gotcha-free. Use this skill only for
**bespoke layouts those tools can't express** (true grids, focus moves, custom
per-pane sizes) or when you need raw `wt.exe` control.

## Running wt.exe — read this first

Expand Down Expand Up @@ -47,7 +49,9 @@ executable first, e.g. `omp --resume C:\path\to\session.jsonl`.
## Finding omp sessions to lay out

There is **no live-pane enumeration** — `wt` exposes no API to list panes, and the
live-session registry is future work. Use these best-effort sources:
live-session registry is future work. Prefer the **`list_omp_sessions` tool**
(recent terminals, newest first, each with its recorded cwd). Its underlying
sources, if you need them directly:

- **Recent omp terminals** — `~/.omp/agent/terminal-sessions/`: one file per
terminal, two lines: `<cwd>` then `<sessionFile>`. These are the most recent
Expand Down
76 changes: 76 additions & 0 deletions skills/windows-terminal-dev/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
name: windows-terminal-dev
description: Develop and ship changes to this oh-my-pi Windows Terminal extension (omp-windows-terminal) itself — the build/test gates, the type-only host API, the wt.exe-via-cmd.exe launch and how to verify argv quoting, the even-split layout math, and breadcrumb-based session discovery. Use when editing src/windows-terminal.ts, its tests, the README, or the bundled skills. For merely USING wt.exe to arrange sessions, see the sibling windows-terminal-control skill instead.
---

# Developing omp-windows-terminal

Contributor guide for hacking on this extension. To *use* `wt.exe` for layouts at
runtime, use the sibling **`windows-terminal-control`** skill instead.

## Layout & gates

- One module: `src/windows-terminal.ts`; tests in `test/windows-terminal.test.ts`.
- **No build step** — oh-my-pi loads the `.ts` directly on Bun.
- `@oh-my-pi/pi-coding-agent` is a **type-only** dependency, provided by the host
at load time. NEVER import its runtime functions into the extension — keep new
code on `node:fs` / `os` / `path` + `Bun.*`. Its types live at
`node_modules/@oh-my-pi/pi-coding-agent/src/extensibility/extensions/types.ts`
(`ExtensionAPI`, `ExtensionContext`, `ExtensionCommandContext`, `ToolDefinition`).
- Gates before any PR: `bun test` **and** `bun run check` (`tsc --noEmit`). CI runs
both on push/PR (`.github/workflows/ci.yml`).
- Style: don't extract one-expression / single-call-site helpers — inline them.

## wt.exe is the whole mechanic

- `wt.exe` is a Windows App Execution Alias; a direct spawn fails "Executable not
found in $PATH". Always route through `cmd.exe /d /s /c wt.exe …`
(`resolveWtLauncher`). Works on native Windows and under WSL.
- Under WSL, translate `-d` to a Windows path (`wslpath -w`) and wrap the child in
`wsl.exe -d <distro> --cd <cwd> -- …` (`adaptPaneOptionsForHost` /
`adaptLayoutPaneForHost`); native Windows is identity.
- Multi-pane layouts chain actions with a standalone `;` argv token (WT's command
delimiter). It survives the cmd.exe argv path — but **verify argv quoting
empirically** whenever you touch the chaining:
1. Round-trip probe — spawn `cmd.exe /d /s /c <prog> …args` where `<prog>` prints
`JSON.stringify(process.argv.slice(2))`; confirm `;` arrives as its own element.
2. Real E2E — run the actual builder output through the launcher with
self-closing marker panes (`cmd /c copy nul <dir>\mN.txt` — avoid `>`, the
outer cmd would eat it) in a `-w new` window, then assert every marker exists.
- `sp`/`nt` accept `-p <profile>`, `--title <t>`, `-d <cwd>`; `sp` also `-s <0..1>`
size. Even N-column sizing (`buildLayoutArgs`): at split k (1-based) give the new
pane `(n-k)/(n-k+1)` of the focused pane, so all panes end up `1/n` wide in input
order.

## Session discovery (plugin-agnostic)

oh-my-pi **core** (NOT this extension) writes a breadcrumb per terminal at
`<agentDir>/terminal-sessions/<id>` — two lines: cwd, then session `.jsonl` path —
so `discoverTerminalSessions` is agnostic to how a session launched. But core
writes one ONLY for a **persisted** session in an **identifiable terminal**: on
Windows that's Windows Terminal (`WT_SESSION` set) or a multiplexer
(tmux/zellij/kitty/wezterm) — a plain cmd/PowerShell/conhost console sets no
`WT_SESSION` and gets no breadcrumb. `/tan` named forks are suppressed, and only
the LATEST session per terminal is kept (older `.jsonl`s survive under
`<agent>/sessions/`).
The current Windows Terminal's file is `wt-$WT_SESSION`. `agentDir`
= `PI_CODING_AGENT_DIR` if set, else `<home>/<PI_CONFIG_DIR|".omp">/agent` (+
`profiles/<OMP_PROFILE>` when a profile is active). A breadcrumb is "last seen",
NOT a liveness signal — resuming a session that is live elsewhere means two writers
on one session file.

For the sessions breadcrumbs miss, `scanStoredSessions` walks
`<agentDir>/sessions/<project>/*.jsonl` and recovers `{ id, cwd, title }` from each
file's first-line header (`{ type:"session", … }`), reading only a bounded slice
(not whole files). `list_omp_sessions source:"all"` surfaces these, and
`arrange_sessions` uses it to recover the cwd of explicitly-named breadcrumb-less
sessions. Cost: no "current" flag and a full directory walk, so it's opt-in.

## Shipping

- If `git push` returns 403 because a *second* logged-in `gh` account is selected
by the credential helper, push with the active account's token by overriding the
helper for one push:
`git -c credential.helper= -c credential.helper='!f() { echo username=<repo-owner>; echo "password=$(gh auth token)"; }; f' push -u origin <branch>`.
Then `gh pr create` works (it uses the active `gh` account).
- After pushing, confirm CI is green: `gh run list --branch <branch>`.
Loading
Loading