From b120f7f012f4bd471f2e07fb02683ea8f6c5671f Mon Sep 17 00:00:00 2001 From: Wojtek Date: Tue, 15 Sep 2026 17:50:15 -0400 Subject: [PATCH 1/2] Preserve native terminal selection in chat by default --- CHANGELOG.md | 4 ++++ README.md | 4 ++-- src/cli/chat-view.ts | 2 +- src/cli/chat.ts | 4 ++-- src/cli/parser.ts | 1 + src/cli/registry.ts | 2 +- tests/chat.test.ts | 15 +++++++++++++++ tests/parser.test.ts | 2 +- 8 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92d236a..6fdca5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ changes will be called out under **Breaking changes**. ## Unreleased +### Fixed + +- **Native text selection in chat.** Mouse capture is off by default, restoring terminal drag selection, double-click word selection, and normal copying. Use `--mouse` to opt into chat wheel scrolling; `--no-mouse` retains native selection. + ## [0.18.0] — 2026-09-15 Full notes: [`docs/releases/0.18.0.md`](docs/releases/0.18.0.md). diff --git a/README.md b/README.md index ff55435..385a62e 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,7 @@ tt state [path] [--all] # compact room state; - tt health [path] [--verbose|--all] # concise safety/action check; verbose shows diagnostics tt status [path] [--verbose|--all] # alias for health tt events [path] [--all] [--after N] [--limit N] [--wait|--follow] [--event TYPE[,TYPE]] [--target self|any|agent] # audit/debug event log; --wait/--follow lower-level streams -tt chat [path] [--history N] [--events] [--no-mouse] # operator chat console for the room +tt chat [path] [--history N] [--events] [--mouse|--no-mouse] # operator chat console for the room tt msg send [--interrupt] [--stdin] [--path DIR] # send an OOB message tt msg recv [--wait|--follow] [--from agent] [--after N] [--target self|any|agent] [--path DIR] # receive OOB messages tt kick [path] [--reason TEXT] [--force] # remove a member (live ones need --force) @@ -310,7 +310,7 @@ claude → you 12:05 3 members │ codex holding 12m · claude idle 3m ``` -Scroll with the mouse wheel, Page Up/Page Down, or Shift+Up/Down. The input stays fixed and editable. New messages do not pull you away from older history; a count appears in the footer. Ctrl+End or `/bottom` returns to live messages. The in-memory buffer retains up to 2,000 message/notice blocks and rewraps on resize. Use `--no-mouse` for keyboard-only scrolling and native text selection; otherwise hold your terminal's selection modifier (usually Shift) when dragging. +Scroll with Page Up/Page Down or Shift+Up/Down. The input stays fixed and editable. New messages do not pull you away from older history; a count appears in the footer. Ctrl+End or `/bottom` returns to live messages. The in-memory buffer retains up to 2,000 message/notice blocks and rewraps on resize. Mouse capture is off by default: drag to select text, double-click to select a word, and copy using your terminal's usual shortcut or menu. For wheel scrolling inside the conversation, opt in with `tt chat --mouse`; this captures mouse gestures, so native selection then requires your terminal's selection modifier (often Shift). `--no-mouse` explicitly restores the default and wins if both flags are supplied. Typing `/`, `@`, or `!@` opens a suggestion list drawn over the bottom of the conversation, so nothing moves while you type. Up/Down choose, Tab or Enter accept, and Enter still sends once the word is complete (an exact `/quit` still quits). Escape closes the list first and clears the draft on a second press; Ctrl+C clears the draft. Neither quits. Alt+Enter (or Shift+Enter where the terminal supports it) adds a new line, and with the list closed Up/Down move through a multi-line draft at the same column. Pasted multiline text stays in the draft until Enter. On exit, the console restores the original terminal screen. diff --git a/src/cli/chat-view.ts b/src/cli/chat-view.ts index a58b39d..2e8f13a 100644 --- a/src/cli/chat-view.ts +++ b/src/cli/chat-view.ts @@ -67,7 +67,7 @@ export function formatChatHelp(width: number, color: boolean, keys = false): str ["Esc", "Dismiss suggestions; press again to clear"], ["Ctrl+C", "Clear the draft"], ["PgUp / PgDn", "Scroll the conversation"], - ["Shift+↑ / ↓ · wheel", "Scroll a few lines"], + ["Shift+↑ / ↓", "Scroll a few lines (wheel with --mouse)"], ["Ctrl+End", "Return to the latest messages"], ["Ctrl+D", "Quit when the draft is empty"] ] : CHAT_COMMANDS.map((command) => [ diff --git a/src/cli/chat.ts b/src/cli/chat.ts index e06a893..63383bb 100644 --- a/src/cli/chat.ts +++ b/src/cli/chat.ts @@ -102,7 +102,7 @@ export async function handleChatCommand( color: terminal && !process.env.NO_COLOR, history: parseOptionalInteger(parsed, "history") ?? DEFAULT_HISTORY, show_turn_events: hasOption(parsed, "events"), - mouse: !hasOption(parsed, "no-mouse") + mouse: hasOption(parsed, "mouse") && !hasOption(parsed, "no-mouse") }); } @@ -553,7 +553,7 @@ export async function runChatSession( screenActive = true; output.write( "\u001b[?1049h\u001b[?2004h" + - (options.mouse !== false ? "\u001b[?1000h\u001b[?1006h" : "") + (options.mouse === true ? "\u001b[?1000h\u001b[?1006h" : "") ); editor = new ChatInputController({ input: options.input, diff --git a/src/cli/parser.ts b/src/cli/parser.ts index bf5d05d..74726f5 100644 --- a/src/cli/parser.ts +++ b/src/cli/parser.ts @@ -19,6 +19,7 @@ const BOOLEAN_FLAGS = new Set([ "link", "no-guard", "no-mouse", + "mouse", "operator-requested", "park", "print", diff --git a/src/cli/registry.ts b/src/cli/registry.ts index 51bdf53..4051098 100644 --- a/src/cli/registry.ts +++ b/src/cli/registry.ts @@ -267,7 +267,7 @@ export const COMMAND_REGISTRY: CommandEntry[] = [ needsRuntime: true, startupMaintenance: true, internal: false, - usage: "tt chat [path] [--history N] [--events] [--no-mouse]", + usage: "tt chat [path] [--history N] [--events] [--mouse|--no-mouse]", description: "Open an operator chat console for a room's agents.", handler: ({ runtime, parsed }) => handleChatCommand(requireRuntime(runtime), parsed) }, diff --git a/tests/chat.test.ts b/tests/chat.test.ts index 6d50394..d0806f0 100644 --- a/tests/chat.test.ts +++ b/tests/chat.test.ts @@ -1289,3 +1289,18 @@ test("chat kicks a persistently ended member without force and protects live mem await until(() => transcript.includes("In the room: codex")); } finally { input.write("/quit\n"); await session; } }); + +test.each([undefined, false, true])("chat enables terminal mouse capture only when requested (mouse=%s)", async (mouse) => { + const { root, service } = setupService(); + const input = new PassThrough(); const output = new PassThrough(); let captured = ""; + output.on("data", (chunk) => { captured += chunk.toString(); }); + const session = runChatSession({ runtime: { commands: new TalkingStickCommands(service), close() {} }, + identity: observerIdentity(), context_path: root, input, output, terminal: true, color: false, history: 0, + show_turn_events: false, poll_ms: 5, mouse }); + try { + await until(() => captured.includes("Room · ")); + expect(captured.includes("\u001b[?1000h")).toBe(mouse === true); + expect(captured.includes("\u001b[?1006h")).toBe(mouse === true); + } finally { input.write("/quit\r"); await session; } + expect(captured).toContain("\u001b[?1000l\u001b[?1006l"); +}); diff --git a/tests/parser.test.ts b/tests/parser.test.ts index a62a06f..4751c6f 100644 --- a/tests/parser.test.ts +++ b/tests/parser.test.ts @@ -6,7 +6,7 @@ import { } from "../src/cli/parser.js"; describe("parseCommand", () => { - test.each(["json", "no-mouse"])( + test.each(["json", "no-mouse", "mouse"])( "boolean --%s preserves the following path", (flag) => { const parsed = parseCommand(["chat", `--${flag}`, "/repo"]); From c0cc83fbc0ca9af7f5058214badd61f4ede9b8e1 Mon Sep 17 00:00:00 2001 From: Wojtek Date: Tue, 15 Sep 2026 17:52:17 -0400 Subject: [PATCH 2/2] Prepare 0.18.1 release --- CHANGELOG.md | 5 +++++ docs/releases/0.18.1.md | 18 ++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 docs/releases/0.18.1.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fdca5c..e2399d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ changes will be called out under **Breaking changes**. ## Unreleased +## [0.18.1] — 2026-09-15 + +Full notes: [`docs/releases/0.18.1.md`](docs/releases/0.18.1.md). + ### Fixed - **Native text selection in chat.** Mouse capture is off by default, restoring terminal drag selection, double-click word selection, and normal copying. Use `--mouse` to opt into chat wheel scrolling; `--no-mouse` retains native selection. @@ -558,6 +562,7 @@ Initial alpha. Core room protocol, SQLite-backed persistence, multi-process contention coverage, MCP smoke coverage, human guardian flow, harness installers, and the portable `talking-stick` skill. +[0.18.1]: https://github.com/mostlydev/talking-stick/releases/tag/v0.18.1 [0.18.0]: https://github.com/mostlydev/talking-stick/releases/tag/v0.18.0 [0.17.0]: https://github.com/mostlydev/talking-stick/releases/tag/v0.17.0 [0.16.0]: https://github.com/mostlydev/talking-stick/releases/tag/v0.16.0 diff --git a/docs/releases/0.18.1.md b/docs/releases/0.18.1.md new file mode 100644 index 0000000..a9fd3a7 --- /dev/null +++ b/docs/releases/0.18.1.md @@ -0,0 +1,18 @@ +# Talking Stick 0.18.1 + +Date: 2026-09-15 + +## Fixed + +- **Native text selection in chat.** Mouse capture is off by default, restoring terminal drag selection, double-click word selection, and normal copying. Use `--mouse` to opt into chat wheel scrolling; `--no-mouse` retains native selection. + +## Verification + +```bash +npm run typecheck +npm test +npm run build +node dist/cli.js --help +git diff --check +npm pack --dry-run +``` diff --git a/package-lock.json b/package-lock.json index 0d8e07d..102ec98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "talking-stick", - "version": "0.18.0", + "version": "0.18.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "talking-stick", - "version": "0.18.0", + "version": "0.18.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 7b0507e..456a364 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talking-stick", - "version": "0.18.0", + "version": "0.18.1", "description": "CLI coordination tool for path-scoped agent handoffs.", "type": "module", "bin": {