diff --git a/docs/collab.md b/docs/collab.md index f69e9fc9a..ec3a50a92 100644 --- a/docs/collab.md +++ b/docs/collab.md @@ -20,7 +20,7 @@ Collab session started! The browser line is click-to-join (an OSC 8 hyperlink to the full `https://` deep link): the relay serves the web guest client at `/`, and the room id + key ride in the URL fragment. From another omp (any directory, any machine), either form works: -Run `/collab qrcode` to start or re-print hosting with a QR code for the full-control browser link; use `/collab qrcode-view` for a read-only QR code (`/collab qr-view` is a short alias). +Running `/collab` or `/collab view` starts or displays the active hosting session, rendering both the terminal/browser join links and their corresponding QR codes. ``` /join my.omp.sh/#mgAYTZwEnpRQtca0CTgn-Q.gdJU… @@ -32,11 +32,9 @@ The guest's previous session is restored on `/leave` (or when the host stops). | Command | Effect | |---|---| -| `/collab` | Start sharing (or re-print the link when already hosting) | +| `/collab` | Start sharing full-control (or re-print the link/QR when already hosting) | | `/collab ` | Start sharing through a specific relay (`relay.example.com`, `ws://localhost:7475`) | -| `/collab view` | Print a read-only (view-only) link (starts sharing first if needed) | -| `/collab qrcode` | Print a QR code for a full-control browser link (starts sharing first if needed) | -| `/collab qrcode-view` | Print a QR code for a read-only browser link (starts sharing first if needed); `/collab qr-view` is an alias | +| `/collab view` | Start sharing read-only (or re-print the link/QR when already hosting) | | `/collab status` | Show link + participants | | `/collab stop` | Stop sharing | | `/join ` | Join a shared session as a guest | diff --git a/packages/ai/test/dialect-thinking.test.ts b/packages/ai/test/dialect-thinking.test.ts index 1696ce811..84f2f32c3 100644 --- a/packages/ai/test/dialect-thinking.test.ts +++ b/packages/ai/test/dialect-thinking.test.ts @@ -164,26 +164,26 @@ describe("kimi think tags (…)", () => { }); }); -describe("pi native thinking channel (…)", () => { - it("routes to thinking, keeping it out of the reply and calls", () => { - const events = scan("pi", "reasoning"); +describe("pi native thinking channel (¤…¤)", () => { + it("routes ¤ to thinking, keeping it out of the reply and calls", () => { + const events = scan("pi", "¤reasoning¤§foo x=1\n"); expect(thinkingText(events)).toBe("reasoning"); expect(callNames(events)).toEqual([{ name: "foo", arguments: { x: 1 } }]); - expect(visibleText(events)).not.toContain(""); + expect(visibleText(events)).not.toContain("¤"); }); it("round-trips renderThinking through the scanner", () => { const rendered = getDialectDefinition("pi").renderThinking("reasoning"); - expect(rendered).toBe("\nreasoning\n"); + expect(rendered).toBe("¤\nreasoning\n¤"); const events = scan("pi", `${rendered}visible`); expect(thinkingText(events).trim()).toBe("reasoning"); expect(visibleText(events)).toBe("visible"); }); - it("treats as plain text when parseThinking is disabled", () => { - const events = scan("pi", "xanswer", { options: { parseThinking: false } }); + it("treats ¤ as plain text when parseThinking is disabled", () => { + const events = scan("pi", "¤x¤answer", { options: { parseThinking: false } }); expect(thinkingBoundaries(events)).toBe(0); - expect(visibleText(events)).toContain("x"); + expect(visibleText(events)).toContain("¤x¤"); }); }); diff --git a/packages/ai/test/tool-examples.test.ts b/packages/ai/test/tool-examples.test.ts index d1ae732e6..d0e0cfa03 100644 --- a/packages/ai/test/tool-examples.test.ts +++ b/packages/ai/test/tool-examples.test.ts @@ -50,9 +50,7 @@ describe("renderToolExamples", () => { }; const rendered = renderToolExamples(tool, "pi"); - expect(rendered).toContain(""); - expect(rendered).toContain(""); - expect(rendered).toContain("src/**/*.ts"); + expect(rendered).toContain('§find paths=["src/**/*.ts"]'); }); it("renders call example in hermes format", () => { diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 1a15a8577..0733df528 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -5,7 +5,7 @@ ### Added - Updated the `pi` option of the `tools.format` setting to use the new compact sigil-delimited owned tool-calling dialect (`§`/`«…»`/`¤`/`‡‡`) that uses ~46% fewer tokens than the legacy format on typical calls. -- Added `/collab qrcode` and `/collab qrcode-view` one-shot terminal QR output for browser join links and a `collab.webUrl` setting for separately hosted collab web clients. +- Integrated terminal QR codes directly into `/collab` and `/collab view` to display both deep links and scannable codes, and added a `collab.webUrl` setting for separately hosted collab web clients. ### Changed diff --git a/packages/coding-agent/src/slash-commands/builtin-registry.ts b/packages/coding-agent/src/slash-commands/builtin-registry.ts index 0c9aa5a25..aef4f2287 100644 --- a/packages/coding-agent/src/slash-commands/builtin-registry.ts +++ b/packages/coding-agent/src/slash-commands/builtin-registry.ts @@ -108,15 +108,9 @@ function showCollabQrCode(ctx: InteractiveModeContext, webLink: string): void { } } -function showCollabLink( - ctx: InteractiveModeContext, - host: CollabHost, - heading: string, - view = false, - options?: { forceQr?: boolean }, -): void { +function showCollabLink(ctx: InteractiveModeContext, host: CollabHost, heading: string, view = false): void { ctx.showStatus(collabLinkHint(host, heading, view), { dim: false }); - if (options?.forceQr) showCollabQrCode(ctx, view ? host.webViewLink : host.webLink); + showCollabQrCode(ctx, view ? host.webViewLink : host.webLink); } function formatFreshSessionResult(result: FreshSessionResult): string { @@ -598,13 +592,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray = [ { name: "collab", description: "Share this session live via a relay", - inlineHint: "[start|view|stop|status|qrcode|qrcode-view] [relayUrl]", + inlineHint: "[start|view|stop|status] [relayUrl]", subcommands: [ { name: "view", description: "Share a read-only link (guests can watch, not prompt)" }, { name: "status", description: "Show link + participants" }, { name: "stop", description: "Stop sharing" }, - { name: "qrcode", description: "Print a QR code for a full-control browser link" }, - { name: "qrcode-view", description: "Print a QR code for a read-only browser link" }, ], allowArgs: true, handleTui: async (command, runtime) => { @@ -642,23 +634,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray = [ ctx.showError("Already in a collab session as a guest (/leave first)"); return; } - const knownStartVerb = - verb === "start" || verb === "view" || verb === "qrcode" || verb === "qrcode-view" || verb === "qr-view"; - const view = verb === "view" || verb === "qrcode-view" || verb === "qr-view"; - const forceQr = verb === "qrcode" || verb === "qrcode-view" || verb === "qr-view"; + const knownStartVerb = verb === "start" || verb === "view"; + const view = verb === "view"; if (ctx.collabHost) { showCollabLink( ctx, ctx.collabHost, - forceQr - ? view - ? "Read-only collab QR code" - : "Collab QR code" - : view - ? "Read-only collab link" - : "Collab session active", + view ? "Read-only collab session active" : "Collab session active", view, - { forceQr }, ); return; } @@ -681,7 +664,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray = [ return; } ctx.collabHost = host; - showCollabLink(ctx, host, "Collab session started!", view, { forceQr }); + showCollabLink(ctx, host, "Collab session started!", view); }, }, { diff --git a/packages/coding-agent/test/slash-commands/collab-qrcode.test.ts b/packages/coding-agent/test/slash-commands/collab-qrcode.test.ts index 2ae87f621..8a63482d0 100644 --- a/packages/coding-agent/test/slash-commands/collab-qrcode.test.ts +++ b/packages/coding-agent/test/slash-commands/collab-qrcode.test.ts @@ -78,12 +78,12 @@ function mockStartedHostLinks() { }); } -describe("/collab qrcode slash command", () => { +describe("/collab slash command QR code rendering", () => { it("starts hosting and prints a one-shot full-control QR", async () => { const startSpy = mockStartedHostLinks(); const harness = createRuntimeHarness(); - const handled = await executeBuiltinSlashCommand("/collab qrcode", harness.runtime); + const handled = await executeBuiltinSlashCommand("/collab", harness.runtime); expect(handled).toBe(true); expect(harness.setText).toHaveBeenCalledWith(""); @@ -103,7 +103,7 @@ describe("/collab qrcode slash command", () => { const startSpy = mockStartedHostLinks(); const harness = createRuntimeHarness(); - const handled = await executeBuiltinSlashCommand("/collab qrcode-view", harness.runtime); + const handled = await executeBuiltinSlashCommand("/collab view", harness.runtime); expect(handled).toBe(true); expect(startSpy).toHaveBeenCalledWith("wss://relay.example.com", ""); @@ -121,7 +121,7 @@ describe("/collab qrcode slash command", () => { it("prints the active full-control browser QR when hosting", async () => { const harness = createRuntimeHarness({ collabHost: fakeHost() }); - const handled = await executeBuiltinSlashCommand("/collab qrcode", harness.runtime); + const handled = await executeBuiltinSlashCommand("/collab", harness.runtime); expect(handled).toBe(true); const statusText = harness.showStatus.mock.calls[0]?.[0] as string; @@ -138,7 +138,7 @@ describe("/collab qrcode slash command", () => { const webViewLink = "https://my.omp.sh/#read-only"; const harness = createRuntimeHarness({ collabHost: fakeHost({ webLink, webViewLink }) }); - const handled = await executeBuiltinSlashCommand("/collab qrcode-view", harness.runtime); + const handled = await executeBuiltinSlashCommand("/collab view", harness.runtime); expect(handled).toBe(true); const statusText = harness.showStatus.mock.calls[0]?.[0] as string; @@ -151,17 +151,4 @@ describe("/collab qrcode slash command", () => { expect(component.url).toBe(webViewLink); expect(component.render(10).join("\n")).toContain("QR code hidden"); }); - - it("accepts qr-view as an alias for qrcode-view", async () => { - const webLink = "https://my.omp.sh/#full-control"; - const webViewLink = "https://my.omp.sh/#read-only"; - const harness = createRuntimeHarness({ collabHost: fakeHost({ webLink, webViewLink }) }); - - const handled = await executeBuiltinSlashCommand("/collab qr-view", harness.runtime); - - expect(handled).toBe(true); - const presented = harness.present.mock.calls[0]?.[0] as readonly unknown[]; - expect(presented[1]).toBeInstanceOf(CollabQrCodeComponent); - expect((presented[1] as CollabQrCodeComponent).url).toBe(webViewLink); - }); });