diff --git a/README.md b/README.md index 246448d..da5da08 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ The conversation, provider configuration, and local state are stored in: ### Session history -A session is one saved conversation, belonging to the project it happened in. It is written after every turn using an atomic write, so an interrupted session does not leave a half-written transcript behind. Restarting Woopcode in the same repository resumes the newest one; starting it somewhere else does not, because sessions live under `sessions//` rather than in one file shared by every repository. +A session is one saved conversation, belonging to the project it happened in. It is written after every turn using an atomic write, so an interrupted session does not leave a half-written transcript behind. Restarting Woopcode opens a fresh session rather than reopening the last one, because a conversation restored without being asked for is state nothing on screen accounts for. Sessions live under `sessions//` rather than in one file shared by every repository, so a resume only ever offers you that repository's own. `/new` starts a fresh session and keeps the old one — `/resume` goes back to it, `/rename` gives it a name, `/branch` copies it to try a second approach, and `woopcode --continue` / `--resume` do the same from the command line. Sessions are deleted 30 days after their last turn; `retentionDays` changes that and `0` keeps them forever. diff --git a/commands/agent.tsx b/commands/agent.tsx index 84876b7..8661963 100644 --- a/commands/agent.tsx +++ b/commands/agent.tsx @@ -45,11 +45,13 @@ export interface RunAgentOptions { /** * Turns the session flags into what `AgentController.initialize` takes. * - * The two entry points differ in one default and it matters: the TUI continues - * where you left off, because that is what Woopcode has always done and what - * the documentation promises, while `-p` starts clean. A headless run used to - * inherit whatever the interactive session had been doing, which is a surprise - * for a scripted caller and impossible to opt out of. + * Both entry points start clean, and resuming is something you ask for. The TUI + * used to continue the newest session in the project on a bare launch, which is + * invisible state: nothing on screen says a conversation was restored, and the + * home screen — which renders only on an empty timeline — silently stopped + * appearing from the second launch onwards. `-p` already worked this way, + * because inheriting whatever the interactive session had been doing is a + * surprise a scripted caller cannot see coming. */ export function sessionOptionsFrom( options: RunAgentOptions, @@ -66,10 +68,7 @@ export function sessionOptionsFrom( return { ...(resumeRef ? { sessionRef: resumeRef } : {}), - continueLatest: - !options.new && - !resumeRef && - (options.continue === true || mode === "interactive"), + continueLatest: !options.new && !resumeRef && options.continue === true, fork: options.forkSession === true, ...(options.name ? { name: options.name } : {}), persist: options.sessionPersistence !== false, @@ -85,7 +84,7 @@ export function addSessionOptions(command: Command): Command { return command .option("-c, --continue", "resume the newest session in this project") .option("--resume [session]", "resume a session by name or id") - .option("--new", "start a fresh session instead of continuing") + .option("--new", "start a fresh session (the interactive default)") .option("--fork-session", "with --continue or --resume, branch instead of writing into it") .option("-n, --name ", "name a new session so it can be resumed by name") .option("--no-session-persistence", "with --prompt, do not save the session"); @@ -409,8 +408,10 @@ async function runInteractive( store.hydrateTimeline(resumed.messages); } - // A bare `--resume` asks to choose. The newest session is loaded above so - // there is something behind the dialog and something to fall back to on Esc. + // A bare `--resume` asks to choose. Nothing is loaded behind it — a launch no + // longer continues anything on its own — so the dialog opens over the home + // screen and Esc leaves you in the fresh session rather than in whichever one + // happened to be newest. if (session.openPicker) store.openSessionPicker(); const homeScreen = await buildHomeScreen(provider); diff --git a/docs/getting-started/first-session.md b/docs/getting-started/first-session.md index bf3ea55..c5f6328 100644 --- a/docs/getting-started/first-session.md +++ b/docs/getting-started/first-session.md @@ -99,14 +99,13 @@ A few commands are worth knowing on day one: | `/new` | Clear the conversation and start fresh | | `/workspace` | Show the repository Woopcode thinks it is in | -Your conversation is saved after every turn, so quitting and restarting resumes -where you left off. `/new` is how you deliberately drop that history. - -:::warning -There is one history file, not one per repository. Starting Woopcode in a -different project resumes the same conversation, so run `/new` when you switch -— otherwise the agent begins with context from somewhere else entirely. -::: +Your conversation is saved after every turn, so nothing is lost when you quit. +Starting Woopcode again opens a fresh session rather than reopening it — +`woopcode --continue` goes back to the newest one, and `/resume` picks from a +list. `/new` starts a fresh one without leaving the session you are in. + +Sessions belong to the project they happened in, so a repository only ever shows +you its own. See [Sessions & history](/docs/guides/sessions-and-history). ## When it does not work diff --git a/docs/guides/sessions-and-history.md b/docs/guides/sessions-and-history.md index 936078a..c97343f 100644 --- a/docs/guides/sessions-and-history.md +++ b/docs/guides/sessions-and-history.md @@ -13,18 +13,19 @@ since: 0.6.0 # Sessions & history A session is one saved conversation, belonging to the project it happened in. -It is written after every turn, so quitting and coming back picks up where you -left off — in that repository, and not in any other. +It is written after every turn, so nothing is lost when you quit — but coming +back to it is something you ask for. ## Resuming -Starting Woopcode in a project reopens the newest session there. The rest: +Starting Woopcode opens a fresh session. Going back to an old one: | Command | What it does | | --- | --- | | `woopcode --continue` | Reopen the newest session in this project | | `woopcode --resume ` | Reopen a particular one | -| `woopcode --new` | Start fresh instead of continuing | +| `woopcode --resume` | Pick from a list before starting | +| `woopcode --new` | Start fresh — what a bare launch already does | | `/resume` | Pick from a list, without leaving the session you are in | | `/resume ` | Switch straight to one | | `/sessions` | List what is saved in this project | @@ -121,9 +122,9 @@ Sessions are deleted 30 days after their last turn. Change it in ## Two windows on one conversation -Open Woopcode twice in the same repository and both continue the newest session. -Each turn writes the whole record, so the second window would overwrite the -first's work. +Two windows land on the same conversation when both resume it — `--continue` in +each, or `/resume` onto one the other already has open. Each turn writes the +whole record, so the second window would overwrite the first's work. It does not: a session that changed underneath a window is detected, and that window's turn is kept as a branch with its own id, leaving the other window's diff --git a/docs/introduction/how-a-turn-works.md b/docs/introduction/how-a-turn-works.md index cfe8342..b76e65b 100644 --- a/docs/introduction/how-a-turn-works.md +++ b/docs/introduction/how-a-turn-works.md @@ -81,8 +81,8 @@ approval dialog. See [Keyboard](/docs/reference/keyboard). ## Afterwards -The conversation is saved, globally rather than per repository — restarting -Woopcode anywhere resumes from it. See +The conversation is saved, to the project it happened in. Restarting Woopcode +opens a fresh session; `--continue` or `/resume` goes back to this one. See [Sessions & history](/docs/guides/sessions-and-history). ## Next diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 2a96446..2435dd8 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -27,7 +27,7 @@ directory. | `-m, --model ` | Model id for this run only; the saved selection is left alone | | `-c, --continue` | Reopen the newest session in this project | | `--resume ` | Reopen a session by name, id or id prefix | -| `--new` | Start a fresh session instead of continuing | +| `--new` | Start a fresh session — what a bare launch already does | | `--fork-session` | With `--continue` or `--resume`, branch instead of writing into it | | `-n, --name ` | Name a new session so it can be resumed by name | | `--no-session-persistence` | With `--prompt`, do not save the session | @@ -57,14 +57,15 @@ woopcode ### Sessions -A bare launch reopens the newest session in that project. The flags pick a -different one: +A bare launch starts a fresh session, keeping whatever was saved before. The +flags go back to one: ```bash -woopcode --continue # explicit about the default +woopcode --continue # the newest session in this project woopcode --resume auth-refactor # by name woopcode --resume 3f9c1a2b # by id prefix -woopcode --new # start fresh, keeping the old one +woopcode --resume # pick from a list +woopcode --new # explicit about the default woopcode --continue --fork-session # branch rather than continue in place woopcode -n auth-refactor # name the session as it starts ``` diff --git a/packages/tests/config/sessionFlags.test.ts b/packages/tests/config/sessionFlags.test.ts index 0cd217d..06a4175 100644 --- a/packages/tests/config/sessionFlags.test.ts +++ b/packages/tests/config/sessionFlags.test.ts @@ -4,13 +4,15 @@ import { sessionOptionsFrom } from "../../../commands/agent"; /** * The mapping from command-line flags to what the controller resolves. * - * Pure and worth testing on its own: the two entry points differ in one default - * and getting it backwards is invisible until someone loses a conversation. + * Pure and worth testing on its own: getting a default backwards here is + * invisible until someone loses a conversation, or until a restored one starts + * hiding the home screen. */ describe("sessionOptionsFrom", () => { - test("a bare interactive launch continues where you left off", () => { - // The documented promise, now scoped to the project you are in. - expect(sessionOptionsFrom({}, "interactive").continueLatest).toBe(true); + test("a bare interactive launch starts a fresh session", () => { + // Continuing on a bare launch is invisible state — nothing says a + // conversation was restored, and the home screen quietly stops appearing. + expect(sessionOptionsFrom({}, "interactive").continueLatest).toBe(false); }); test("a bare headless run starts its own session", () => { @@ -19,12 +21,17 @@ describe("sessionOptionsFrom", () => { expect(sessionOptionsFrom({}, "headless").continueLatest).toBe(false); }); - test("--continue asks for it explicitly, headlessly too", () => { + test("--continue asks for it explicitly, in both modes", () => { + expect(sessionOptionsFrom({ continue: true }, "interactive").continueLatest).toBe(true); expect(sessionOptionsFrom({ continue: true }, "headless").continueLatest).toBe(true); }); - test("--new overrides the interactive default", () => { - expect(sessionOptionsFrom({ new: true }, "interactive").continueLatest).toBe(false); + test("--new beats --continue rather than being ignored beside it", () => { + // --new is now the default rather than an override, but a script that + // passes both is asking for a fresh session and has to get one. + expect( + sessionOptionsFrom({ new: true, continue: true }, "headless").continueLatest, + ).toBe(false); }); test("--resume resolves a reference instead of continuing", () => {