diff --git a/CHANGELOG.md b/CHANGELOG.md index 983ef56..6a8f971 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.11.1] — 2026-05-07 + +### Improvements + +- README, GitHub Pages site, and architecture doc now lead with + AI session management — discover every past Copilot CLI / Claude + Code session, resume any of them in one click, close active ones + from the sidebar, delete history from disk, and have every session + tab auto-restore exactly where you left it the next time you open + the app. +- Demo animation now showcases the headline workflow: opening the + Sessions panel, resuming a past session in one click, and chatting + with the resumed Copilot session. + ## [0.11.0] — 2026-05-07 ### Features @@ -342,7 +356,8 @@ AI-assisted development. - Eight built-in themes (dark and light variants). - Keyboard shortcuts for tabs, splits, sidebar, and settings. -[Unreleased]: https://github.com/Ron537/DPlex/compare/v0.11.0...HEAD +[Unreleased]: https://github.com/Ron537/DPlex/compare/v0.11.1...HEAD +[0.11.1]: https://github.com/Ron537/DPlex/compare/v0.11.0...v0.11.1 [0.11.0]: https://github.com/Ron537/DPlex/compare/v0.10.0...v0.11.0 [0.10.0]: https://github.com/Ron537/DPlex/compare/v0.9.2...v0.10.0 [0.9.2]: https://github.com/Ron537/DPlex/compare/v0.9.1...v0.9.2 diff --git a/README.md b/README.md index ffa1967..1f25529 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ # DPlex -**A terminal multiplexer built for AI-assisted development.** +**A desktop workspace for the AI CLI sessions you run every day.** -DPlex is a desktop terminal app that manages multiple AI CLI tool sessions alongside regular terminals — all in one window. Think of it as a purpose-built workspace for developers who run **Copilot CLI**, **Claude Code**, or other AI coding agents daily across many projects. +DPlex **discovers** every **Copilot CLI** / **Claude Code** session you've ever started, lets you **resume** any of them in one click, and **auto-restores every open session tab** the next time you open the app — splits, order, working directory, and resume command preserved. On top of that: the multiplexer essentials — split panes, tabs, projects, worktrees, and a built-in VSCode-style Source Control view. [![Tests](https://github.com/Ron537/DPlex/actions/workflows/tests.yml/badge.svg)](https://github.com/Ron537/DPlex/actions/workflows/tests.yml) [![CodeQL](https://github.com/Ron537/DPlex/actions/workflows/codeql.yml/badge.svg)](https://github.com/Ron537/DPlex/actions/workflows/codeql.yml) @@ -20,7 +20,7 @@ DPlex is a desktop terminal app that manages multiple AI CLI tool sessions along

- DPlex demo: switching between Projects, Sessions, and Source Control views with a live AI session running in the editor pane + DPlex demo: switching between Projects and Source Control, then opening the Sessions panel, resuming a past AI session in one click, and chatting with the resumed Copilot session

> **🚧 Pre-1.0.** DPlex is functional and used daily by its author, but @@ -32,11 +32,14 @@ DPlex is a desktop terminal app that manages multiple AI CLI tool sessions along ## Why DPlex? -When working with AI CLI tools across multiple projects, you end up with a mess of terminal windows — a Copilot session here, a Claude session there, plus regular shells scattered everywhere. DPlex gives you one home for all of it: +When working with AI CLI tools across multiple projects, you end up with a mess of terminal windows — a Copilot session here, a Claude session there, plus regular shells scattered everywhere, and not a single one of them survives a reboot. DPlex gives you one home for all of it, with **AI session management as the headline feature**: -- **One window** with split panes, tabs, and an activity bar (Projects · Sessions · Source Control). -- **Automatic session discovery** — sees active and past AI sessions across providers without manual tracking. -- **Session persistence** — close the app, reopen it, your AI sessions resume where you left off. +- **🗂️ Discover every past session.** DPlex reads each provider's data directory and surfaces every Copilot CLI / Claude Code session you've ever started — searchable by name, ID, summary, or workspace. +- **▶️ Resume in one click.** Click any past session and it opens in a new tab with the correct resume command and original CWD. No copy-pasting session IDs from `~/.copilot/...`. +- **⏹️ Close active sessions from the sidebar.** Stop running AI sessions without hunting for their terminal — closing a tab fully terminates the underlying process. +- **🗑️ Delete from disk.** Remove a session's stored data when you're done, with confirmation. +- **♻️ Auto-restore session tabs across restarts.** Quit the app, reopen it tomorrow — every AI session tab snaps back exactly where it was, with the right resume command, CWD, splits, and tab order preserved. +- **One window** for everything: split panes, tabs, and an activity bar (Projects · Sessions · Source Control). - **Project-aware workflow** — group sessions by project, start a new AI session in any folder with one click. - **Worktree-friendly** — first-class Git worktree support so concurrent feature work doesn't pollute your main checkout. - **Built-in Source Control** — VSCode-style changes view scoped to whichever project (or worktree) you pick. @@ -134,7 +137,7 @@ CI logs and SBOMs are attached to every release. Sessions panel with active and historical AI sessions -
Sessions panel. Every Copilot CLI / Claude Code session you've ever started, searchable. +
Sessions panel. Every Copilot CLI / Claude Code session you've ever started, searchable. Click to resume, right-click to delete from disk. @@ -171,6 +174,60 @@ CI logs and SBOMs are attached to every release. ## Features +### AI session management + +> The headline feature. DPlex gives you full lifecycle control over the +> Copilot CLI / Claude Code sessions you actually work with daily. + +- **🗂️ Past-session list.** Every session you've ever started, surfaced + in the **Sessions** activity-bar panel — searchable by name, ID, + summary, or workspace, grouped by recency or by project. +- **▶️ One-click resume.** Click any past session to reopen it in a new + tab with the correct resume command and original CWD pre-filled. The + re-spawned PTY is matched back to its provider session ID so the + active-session indicator lights up automatically once the AI tool + writes its lock file. +- **🟢 Live active-session indicators.** Active sessions are detected + via provider lock files (`inuse..lock` for Copilot, pidfiles for + Claude) with PID liveness checks, so the sidebar always reflects what + is actually running. +- **⏹️ Close active sessions from the sidebar.** Stop a running AI + session without finding its terminal — closing the tab fully + terminates the underlying process. +- **🗑️ Delete sessions from disk.** Remove a session's stored history + when you're done with it (with confirmation). +- **🧠 Prompt-history viewer.** Browse and search the prompts you've + sent in any past session — useful for re-running, copy-pasting, or + remembering what you asked yesterday. +- **📌 Recent sessions per project.** Each expanded project (and + worktree) lists its last few idle sessions inline, so you can resume + them without leaving the projects panel. +- **🧩 Provider-agnostic.** Same start / resume / close / delete flow + whether the underlying tool is Copilot CLI, Claude Code, or one you + add yourself — see [docs/providers.md](./docs/providers.md). + +### Workspace persistence (auto-restore on restart) + +> **Close the app today, open it tomorrow — every AI session tab is +> back exactly where you left it.** This is the feature DPlex was built +> around. + +- **Every AI session tab is restored.** Tabs are serialized to + `sessions.json` in the Electron `userData` directory on every natural + lifecycle event and synchronously on quit, so a SIGTERM doesn't lose + state. +- **Splits and tab order survive too.** Horizontal/vertical splits, tab + order within each pane, the active group, and the active tab are all + rebuilt on launch. +- **Resume command + CWD preserved.** Each restored tab is recreated + with its original resume command and working directory. A short retry + loop re-resolves the underlying provider session ID once the AI tool + writes its lock file, so active-session indicators light up + automatically. +- **History is independent of workspace state.** Even after a hard kill + (SIGKILL/OOM) the session *history* is untouched — every previous + session is still discoverable and resumable from the Sessions panel. + ### Activity bar — Projects · Sessions · Source Control - **VSCode-style activity bar** on the far left. One click jumps between projects, sessions, and git changes. @@ -189,8 +246,8 @@ CI logs and SBOMs are attached to every release. - **Split panes** — horizontal and vertical splits with resizable dividers. - **Tabbed interface** — multiple terminals per pane, drag tabs between panes. - **Tab reordering** — drag and drop within and across groups. +- **Drag a tab onto another pane's edge** to create a new split right there — handy for putting a freshly-resumed AI session next to the one you already had open. - **Shell selector** — pick from auto-detected system shells (bash, zsh, fish, PowerShell, etc.) when opening a new terminal. -- **Workspace persistence** — AI session tabs are saved and restored across app restarts. ### Project management @@ -208,16 +265,6 @@ CI logs and SBOMs are attached to every release. - **Single-click preview** + **double-click promotes to a permanent diff tab** (VSCode behavior). - **Live updates** via filesystem watchers — changes appear as you save. -### Sessions - -- **Session discovery** — automatically discovers past sessions from each provider's data directory. -- **Search & filter** — by name, ID, or summary. -- **Resume** — click to resume any past session in a new terminal tab. -- **Close active sessions** — stop running AI sessions from the sidebar; closing a tab fully terminates the underlying process. -- **Delete sessions** — remove session data from disk. -- **Time and workspace grouping** — group by recency or by workspace. -- **Prompt history viewer** — browse and search the prompts you've sent in any past session. - ### Attention inbox - **Notification bell** — aggregated inbox surfaces sessions that need you, with an unread badge in the title bar. @@ -262,7 +309,7 @@ There are great tools in adjacent niches. DPlex isn't trying to replace any of t | **Wave Terminal** | AI-focused terminal | Adjacent vision; broader scope, more opinionated UI. DPlex is narrower and Electron-portable today. | | **Zed / Cursor** | AI-native editors | They embed the AI in the editor; DPlex orchestrates the AI you already use from the terminal. | -If your goal is "I want to run a Claude session in repo A, a Copilot session in repo B, and a regular shell in repo C, all visible at once, and yesterday's sessions findable tomorrow" — that's DPlex. +If your goal is "I want to start a Claude session in repo A today, a Copilot session in repo B tomorrow, find yesterday's sessions whenever I need them, resume any of them in one click, and reopen the app next week with every tab right where I left it" — that's DPlex. ## FAQ / Troubleshooting diff --git a/docs/architecture.md b/docs/architecture.md index 89d9c59..558f733 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -92,15 +92,25 @@ A few notable consequences: ## Workspace persistence -AI session tabs are serialized to `sessions.json` in the Electron -`userData` directory: - -- On quit, `saveWorkspaceSync` ensures a synchronous save so a SIGTERM +Auto-restoring AI session tabs across app restarts is one of DPlex's +flagship user-facing guarantees. The implementation: + +- AI session tabs are serialized to `sessions.json` in the Electron + `userData` directory. The recursive split layout, tab order within + each pane, the active group, and the active tab id are all part of + this snapshot. +- Saves happen on every natural lifecycle event (tab open/close, + resume, split, group activation, app blur). On quit, + `saveWorkspaceSync` performs a synchronous write so a SIGTERM doesn't lose state. -- On restore, session tabs are recreated with their original command - and CWD; session IDs are re-resolved from PID after PTY creation +- On restore, each tab is recreated with its original resume command + and CWD; session IDs are then re-resolved from the new PTY's PID with retry logic (some AI tools take a beat to write their lock file). +- Session *history* is independent of workspace state — past sessions + are discoverable from the providers' data directories regardless of + what the workspace snapshot says, so even a hard kill never loses + history. ## Provider system diff --git a/docs/assets/demo.gif b/docs/assets/demo.gif index e88e737..23fef0f 100644 Binary files a/docs/assets/demo.gif and b/docs/assets/demo.gif differ diff --git a/package-lock.json b/package-lock.json index fdeabf3..41fa3d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dplex", - "version": "0.10.0", + "version": "0.11.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dplex", - "version": "0.10.0", + "version": "0.11.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 46ee26d..221c178 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dplex", - "version": "0.11.0", + "version": "0.11.1", "description": "A terminal multiplexer built for AI-assisted development. Manage multiple AI CLI sessions (Copilot, Claude, and more) alongside regular terminals in one window.", "main": "./out/main/index.js", "author": { diff --git a/scripts/demo-gif.ts b/scripts/demo-gif.ts index fc206be..ab9b9e9 100644 --- a/scripts/demo-gif.ts +++ b/scripts/demo-gif.ts @@ -5,7 +5,9 @@ * 1. Watching a live AI session * 2. Switching to Source Control via the activity bar * 3. Picking a different project from the dropdown - * 4. Splitting the editor and typing into two terminals concurrently + * 4. Opening the Sessions panel, resuming a past session in one click + * 5. Typing a follow-up prompt into the resumed session and watching + * Copilot respond — proof the resumed session is live * * Includes an injected SVG "cursor" that smoothly travels between * targets so the GIF reads as a guided tour rather than random clicks. @@ -472,37 +474,94 @@ async function main(): Promise { await sleep(1200) await captureFrames(window, 18, 'g-api-changes') - // ── 5. Back to Projects ─────────────────────────────────────────────── - const projBox = await window.getByTestId('activity-bar-projects').boundingBox() - if (projBox) { - const tx = projBox.x + projBox.width / 2 - const ty = projBox.y + projBox.height / 2 + // ── 5. Open the Sessions panel ──────────────────────────────────────── + // Tells the user "this is where every past AI session lives, even + // ones from yesterday." We move the cursor to the activity-bar + // Sessions icon and click; the SessionList already has fakeSessions + // seeded into it, so the panel populates immediately. + const sessionsBox = await window.getByTestId('activity-bar-sessions').boundingBox() + if (sessionsBox) { + const tx = sessionsBox.x + sessionsBox.width / 2 + const ty = sessionsBox.y + sessionsBox.height / 2 await moveCursorTo(window, tx, ty, 0) - await captureFrames(window, 5, 'h-cursor-to-projects') + await captureFrames(window, 5, 'h-cursor-to-sessions') + await clickRipple(window, tx, ty) + await window.getByTestId('activity-bar-sessions').click() + } + await captureFrames(window, 14, 'i-sessions-panel') + + // ── 6. Hover a past session and "click" to resume ───────────────────── + // The fake providers don't ship real resume commands, so a bare click + // wouldn't open a tab. We move the cursor to the row (with ripple) for + // narrative, then programmatically create the resumed tab via + // terminalStore + paint AI-session content into it. This faithfully + // mirrors what handleResume does in production. + const RESUMED_SUMMARY = 'Wire OAuth flow with PKCE and refresh-token rotation' + const sessionRow = window.locator(`text=${RESUMED_SUMMARY}`).first() + const rowBox = await sessionRow.boundingBox().catch(() => null) + if (rowBox) { + const tx = rowBox.x + rowBox.width / 2 + const ty = rowBox.y + rowBox.height / 2 + await moveCursorTo(window, tx, ty, 0) + await captureFrames(window, 5, 'j-cursor-to-session-row') await clickRipple(window, tx, ty) - await window.getByTestId('activity-bar-projects').click() } - await captureFrames(window, 8, 'i-projects-back') - // ── 6. Vertical split — show two terminals side-by-side ─────────────── - await window.evaluate(() => { + const resumedTabId = (await window.evaluate((title) => { // @ts-expect-error injected const ts = window.__dplex.terminalStore const id = ts.getState().activeGroupId - if (id) ts.getState().splitGroup(id, 'vertical') - }) + if (!id) return null + return ts.getState().createTerminal(id, title, undefined, undefined, undefined, 'copilot-cli') + }, `↻ ${RESUMED_SUMMARY}`)) as string | null await sleep(450) - await captureFrames(window, 12, 'j-after-split') - // ── 7. Type a command into the new (right) terminal ─────────────────── + // Paint a "session resumed" banner into the new tab so viewers + // immediately see this is a different session than the one on the left. + if (resumedTabId) { + await window.evaluate((tabId) => { + // @ts-expect-error injected + const entry = window.__dplex.terminalRegistry.getTerminalEntry(tabId) + if (!entry?.term) return + const ESC = '\x1b' + const RESET = `${ESC}[0m` + const DIM = `${ESC}[2m` + const BOLD = `${ESC}[1m` + const CYAN = `${ESC}[36m` + const GREEN = `${ESC}[32m` + const MAGENTA = `${ESC}[35m` + entry.term.write( + [ + `${ESC}[3J${ESC}[2J${ESC}[H`, + `${DIM}~/code/api-server ${CYAN}main${RESET}`, + `${DIM}$${RESET} copilot --resume=4f1e9c`, + ``, + `${BOLD}${MAGENTA}● ${BOLD}Wire OAuth flow with PKCE${RESET}`, + `${DIM} Resumed · 17 messages · 4 tool calls${RESET}`, + ``, + `${GREEN}${BOLD}● Copilot${RESET}`, + ` Session restored. Where were we?`, + ``, + `${DIM} Last edit: src/handlers/auth.ts (+34 −12)${RESET}`, + `${DIM} Pending: rotate refresh tokens on /token endpoint${RESET}`, + `` + ].join('\r\n') + ) + }, resumedTabId) + } + await captureFrames(window, 12, 'k-resumed-tab') + + // ── 7. Talk with the resumed session — type a prompt + stream reply ─── + // Demonstrates the resumed session is alive and interactive, not a + // static screenshot. We type into the now-active resumed tab and + // stream a short Copilot response. await typeIntoActiveTerminal( window, - 'copilot -p "audit the auth handler for token leaks"\r\n', - 'k-typing', - 50 + 'rotate the refresh token on /token and add a unit test\r\n', + 'm-typing', + 45 ) - await sleep(120) - // Stream a fake response. + await sleep(140) await window.evaluate(() => { // @ts-expect-error injected const groups = window.__dplex.terminalStore.getState().groups @@ -518,23 +577,26 @@ async function main(): Promise { const DIM = `${ESC}[2m` const BOLD = `${ESC}[1m` const GREEN = `${ESC}[32m` - const MAGENTA = `${ESC}[35m` const YELLOW = `${ESC}[33m` entry.term.write( [ - ``, - `${BOLD}${MAGENTA}● ${BOLD}Audit auth handler for token leaks${RESET}`, ``, `${GREEN}${BOLD}● Copilot${RESET}`, - ` Reading the auth handler...`, + ` On it — adding rotation + a unit test.`, + ``, + ` ${YELLOW}▸${RESET} ${DIM}edit${RESET} src/handlers/refresh.ts`, + ` ${GREEN}✓${RESET} ${DIM}+22 −4${RESET}`, + ``, + ` ${YELLOW}▸${RESET} ${DIM}create${RESET} src/handlers/refresh.test.ts`, + ` ${GREEN}✓${RESET} ${DIM}38 lines${RESET}`, ``, - ` ${YELLOW}▸${RESET} ${DIM}readFile${RESET} src/handlers/auth.ts`, - ` ${GREEN}✓${RESET} ${DIM}204 lines${RESET}`, + ` ${YELLOW}▸${RESET} ${DIM}run${RESET} npm test -- refresh`, + ` ${GREEN}✓${RESET} ${DIM}5 passed${RESET}`, `` ].join('\r\n') ) }) - await captureFrames(window, 14, 'l-response') + await captureFrames(window, 18, 'n-chat-response') // ── 8. End frame — let it breathe ───────────────────────────────────── await captureFrames(window, 10, 'z-end') diff --git a/site/index.html b/site/index.html index fa985a8..0b1b290 100644 --- a/site/index.html +++ b/site/index.html @@ -3,10 +3,10 @@ -DPlex — Terminal multiplexer for AI-assisted development - - - +DPlex — One window for every AI coding session you run + + + @@ -357,7 +357,7 @@

One window for every AI coding session you run.

- DPlex is a desktop terminal multiplexer that manages your Copilot CLI, Claude Code, and regular shells across projects — with session discovery, persistence, and worktrees built in. + DPlex discovers every Copilot CLI and Claude Code session you've ever started, lets you resume any of them in one click, and auto-restores every session tab the next time you open the app — splits, order, and resume command preserved.

macOS · Windows · Linux · No telemetry · Built-in Copilot CLI + Claude Code support
- DPlex demo: switching between Projects, Sessions, and Source Control views with a live AI session running in the editor pane + DPlex demo: switching between Projects and Source Control, then opening the Sessions panel, resuming a past AI session in one click, and chatting with the resumed Copilot session
-

Built for developers running many AI sessions at once.

-

Every feature targets the same problem: keeping a dozen AI sessions across many projects under control without losing context.

+

Built around AI session management.

+

DPlex's headline capability is full lifecycle control over your AI coding sessions — discover them, resume them, close them, delete them, and never lose your place across restarts.

- +
-

Multi-provider

-

Copilot CLI and Claude Code work out of the box. Add a provider with one TypeScript interface — pluggable, open architecture.

+

Discover every past session

+

Auto-reads each provider's data directory and lists every Copilot CLI / Claude Code session you've ever started — searchable by name, ID, summary, or workspace.

- + +
+

Resume in one click

+

Click any past session and it opens in a new tab with the right resume command and original CWD pre-filled. No copy-pasting session IDs.

+
+
+
+ +
+

Auto-restore on restart

+

Quit the app, reopen it tomorrow — every AI session tab is back exactly where you left it. Splits, tab order, resume command, and CWD all preserved.

+
+
+
+ +
+

Close & delete from the sidebar

+

Stop a running AI session without finding its terminal — closing the tab terminates the process. Right-click a past session to delete its data from disk.

+
+
+
+
-

Session discovery

-

Auto-discovers active and past sessions across providers. No copy-pasting session IDs, no manual tracking.

+

Multi-provider

+

Copilot CLI and Claude Code work out of the box. Add a provider with one TypeScript interface — pluggable, open architecture.

@@ -411,14 +432,7 @@

First-class worktrees

Splits & tabs

-

Horizontal + vertical splits, draggable tabs, drag-between-panes — the multiplexer essentials, polished.

-
-
-
- -
-

Session persistence

-

Close the app, reopen it — your AI sessions are exactly where you left them, including resumable command + cwd.

+

Horizontal + vertical splits, draggable tabs, drag-between-panes — drop a freshly-resumed session next to the one you already had open.