From db5122a70d7efff267683bcc929c5d227b2953e7 Mon Sep 17 00:00:00 2001 From: Suleiman Shahbari Date: Sat, 4 Jul 2026 21:13:48 +0300 Subject: [PATCH] feat(framework): default the live-run session link to claude.ai/code A live run now shows a session link to https://claude.ai/code by default (where a Claude Code session appears once Remote Control is enabled), so the dashboard points somewhere useful without --session-link. --fake gets no link (no real session); an explicit --session-link still wins, incl. the {sessionId} template filled with the real Claude session id. No per-session deep link is constructed: we drive Claude Code headless and Remote Control is opt-in + subscription-gated, so there is no session-URL slug to build. The session id is already surfaced on the dashboard + CLI narration; the README documents the Remote Control path. New exports: chooseSessionLink, CLAUDE_CODE_SESSION_LIST. Part of #209. Closes #212. --- .../default-claude-code-session-link.md | 11 +++++++ packages/framework/README.md | 12 +++++++ packages/framework/src/cli.test.ts | 22 ++++++++++++- packages/framework/src/cli.ts | 32 +++++++++++++++++-- 4 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 .changeset/default-claude-code-session-link.md diff --git a/.changeset/default-claude-code-session-link.md b/.changeset/default-claude-code-session-link.md new file mode 100644 index 0000000..cf1e4ad --- /dev/null +++ b/.changeset/default-claude-code-session-link.md @@ -0,0 +1,11 @@ +--- +'@gemstack/framework': minor +--- + +Default the live-run session link to claude.ai/code + +A live run now shows a session link to `https://claude.ai/code` by default (the page where a Claude Code session appears once Remote Control is enabled), so the dashboard points somewhere useful without needing `--session-link`. `--fake` gets no link (it has no real session), and an explicit `--session-link` still wins — including the `{sessionId}` template, which is filled in with the real Claude session id once known. + +Why not a per-session deep link: we drive Claude Code headless, and Remote Control (which powers the claude.ai/code session view) is opt-in and subscription-gated, so there is no session-URL slug to construct. The session id is already surfaced on the dashboard and in the CLI narration; the README's new "Watching the live session" section documents the Remote Control path. New exports: `chooseSessionLink`, `CLAUDE_CODE_SESSION_LIST`. + +Part of #209. Closes #212. diff --git a/packages/framework/README.md b/packages/framework/README.md index ba4bd90..2bd84db 100644 --- a/packages/framework/README.md +++ b/packages/framework/README.md @@ -76,6 +76,18 @@ framework --fake Offline demo (no CLI, no model, deterministic). The live path needs the Claude Code CLI installed (`claude` on `PATH`). The `--fake` path needs neither a CLI nor a model, so it is what CI runs. +### Watching the live session + +Every run surfaces the current Claude Code **session id** on the dashboard (and in +the CLI narration) as soon as the wrapped agent reports it. To open the live +session in a browser, enable [Remote Control](https://code.claude.com/docs/en/remote-control) +(`claude remote-control` / `--remote-control`; requires a claude.ai subscription +and Claude Code v2.1.51+) and find the session by id/name at +[claude.ai/code](https://claude.ai/code) — the dashboard's session link points +there by default. If you have a direct per-session URL scheme, pass it with +`--session-link "https://.../{sessionId}"`; `{sessionId}` is filled in with the +real Claude session id once it is known. + ## Extensions (#190) The Framework is modular: it composes **capability extensions** and **skills** diff --git a/packages/framework/src/cli.test.ts b/packages/framework/src/cli.test.ts index 94e1318..194af72 100644 --- a/packages/framework/src/cli.test.ts +++ b/packages/framework/src/cli.test.ts @@ -1,6 +1,13 @@ import { strict as assert } from 'node:assert' import { test } from 'node:test' -import { buildDeployTarget, parseArgs, runCli, type CliIO } from './cli.js' +import { + buildDeployTarget, + chooseSessionLink, + CLAUDE_CODE_SESSION_LIST, + parseArgs, + runCli, + type CliIO, +} from './cli.js' function capture(): { io: CliIO; out: string[]; err: string[] } { const out: string[] = [] @@ -28,6 +35,19 @@ test('parseArgs reads permission-mode and skip-permissions', () => { assert.equal(opts.skipPermissions, true) }) +test('chooseSessionLink defaults a live run to the claude.ai/code session list (#212)', () => { + assert.equal(chooseSessionLink({ sessionLink: undefined }, false), CLAUDE_CODE_SESSION_LIST) + assert.equal(CLAUDE_CODE_SESSION_LIST, 'https://claude.ai/code') +}) + +test('chooseSessionLink honors an explicit --session-link over the default', () => { + assert.equal(chooseSessionLink({ sessionLink: 'https://x/s/{sessionId}' }, false), 'https://x/s/{sessionId}') +}) + +test('chooseSessionLink gives no link for a fake run (no real session)', () => { + assert.equal(chooseSessionLink({ sessionLink: undefined }, true), undefined) +}) + test('runCli --help prints usage and exits 0', async () => { const { io, out } = capture() const code = await runCli(['--help'], io) diff --git a/packages/framework/src/cli.ts b/packages/framework/src/cli.ts index ad61dcf..b3912ba 100644 --- a/packages/framework/src/cli.ts +++ b/packages/framework/src/cli.ts @@ -10,6 +10,26 @@ import { FAKE_DEPLOY, FAKE_INTENT, FAKE_SIGNALS, fakeDriver } from './fake-scrip import { discoverExtensions, readProjectSignals } from './extensions.js' import { preflight } from './preflight.js' +/** + * The claude.ai/code session list — the default link shown for a live run. It is + * the page where a Claude Code session appears *when Remote Control is enabled* + * (`claude remote-control` / `--remote-control`, a claude.ai subscription, Claude + * Code v2.1.51+; https://code.claude.com/docs/en/remote-control). We drive Claude + * Code headless, so there is no per-session deep link to construct — pass + * `--session-link "...{sessionId}..."` if you have a real one. + */ +export const CLAUDE_CODE_SESSION_LIST = 'https://claude.ai/code' + +/** + * The session link to show for a run: the user's `--session-link` if given, else + * the claude.ai/code list for a live run (nothing for `--fake`, which has no real + * session). Pure, so the default is unit-testable without a live run. + */ +export function chooseSessionLink(opts: Pick, fake: boolean): string | undefined { + if (opts.sessionLink) return opts.sessionLink + return fake ? undefined : CLAUDE_CODE_SESSION_LIST +} + /** Where the CLI writes. Injectable so tests capture output. */ export interface CliIO { out: (line: string) => void @@ -57,8 +77,11 @@ Options: --no-dashboard Do not start the localhost dashboard. --skip-preflight Skip the prerequisite checks before a live run. --session-link Link to the live agent session (shown on the dashboard). - Use {sessionId} as a placeholder to template in the real - id, e.g. "https://example.com/s/{sessionId}". + Defaults to https://claude.ai/code for a live run, where + the Claude Code session appears when Remote Control is on + (see code.claude.com/docs/en/remote-control). Pass your own + URL, using {sessionId} to template in the real Claude + session id, e.g. "https://example.com/s/{sessionId}". -h, --help Show this help. -v, --version Print the version. @@ -345,7 +368,10 @@ export async function runCli(argv: string[], io: CliIO = defaultIO): Promise { + const link = chooseSessionLink(opts, fake) + return link ? { sessionLink: link } : {} + })(), } try {