Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3806a40
add --fork flag to duplicate sessions before continuing
ariane-emory Jan 30, 2026
f9366af
rename --fork to --fork-session to match issue requirements
ariane-emory Jan 30, 2026
c25420c
add --fork-session support to TUI command
ariane-emory Jan 30, 2026
47a1b03
add short -f alias for --fork-session in TUI command, remove --fork a…
ariane-emory Jan 30, 2026
805d819
refactor: use const instead of let in run.ts for fork-session
ariane-emory Jan 30, 2026
a4928c3
feat: add error toast when fork-session fails
ariane-emory Jan 30, 2026
19c179a
Fix --session + --fork race condition causing session to disappear
ariane-emory Jan 30, 2026
69f4c91
refactor: reorganize fork-session option properties and update descri…
ariane-emory Jan 30, 2026
1357e27
feat: use -F alias for --fork-session in both commands for consistency
ariane-emory Jan 30, 2026
235ff55
Merge branch 'dev' into feat/fork-session-take-2
ariane-emory Jan 30, 2026
0958761
adjust shorthands
ariane-emory Jan 31, 2026
f535193
Merge branch 'dev' into feat/fork-session-take-2
ariane-emory Feb 1, 2026
4410d8b
Merge branch 'dev' into feat/fork-session-take-2
ariane-emory Feb 2, 2026
8e3988b
Rename --fork-session to --fork
ariane-emory Feb 2, 2026
60aaacc
Merge branch 'dev' into feat/fork-session-take-2
ariane-emory Feb 2, 2026
6aa0f5d
Merge dev into feat/fork-session-take-2
ariane-emory Feb 3, 2026
c7cea89
Merge branch 'dev' into feat/fork-session-take-2
ariane-emory Feb 3, 2026
9a1f05a
Merge dev into feat/fork-session-take-2
ariane-emory Feb 5, 2026
2625c81
Merge dev into feat/fork-session-take-2
ariane-emory Feb 5, 2026
2264141
Merge dev into feat/fork-session-take-2
ariane-emory Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions packages/opencode/src/cli/cmd/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ export const RunCommand = cmd({
describe: "session id to continue",
type: "string",
})
.option("fork", {
describe: "fork the session before continuing (requires --continue or --session)",
type: "boolean",
})
.option("share", {
type: "boolean",
describe: "share the session",
Expand Down Expand Up @@ -324,6 +328,11 @@ export const RunCommand = cmd({
process.exit(1)
}

if (args.fork && !args.continue && !args.session) {
UI.error("--fork requires --continue or --session")
process.exit(1)
}

const rules: PermissionNext.Ruleset = [
{
permission: "question",
Expand All @@ -349,11 +358,17 @@ export const RunCommand = cmd({
}

async function session(sdk: OpencodeClient) {
if (args.continue) {
const result = await sdk.session.list()
return result.data?.find((s) => !s.parentID)?.id
const baseID = args.continue
? (await sdk.session.list()).data?.find((s) => !s.parentID)?.id
: args.session

if (baseID && args.fork) {
const forked = await sdk.session.fork({ sessionID: baseID })
return forked.data?.id
}
if (args.session) return args.session

if (baseID) return baseID

const name = title()
const result = await sdk.session.create({ title: name, permission: rules })
return result.data?.id
Expand Down
31 changes: 29 additions & 2 deletions packages/opencode/src/cli/cmd/tui/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ function App() {
})
local.model.set({ providerID, modelID }, { recent: true })
}
if (args.sessionID) {
// Handle --session without --fork immediately (fork is handled in createEffect below)
if (args.sessionID && !args.fork) {
route.navigate({
type: "session",
sessionID: args.sessionID,
Expand All @@ -268,10 +269,36 @@ function App() {
.find((x) => x.parentID === undefined)?.id
if (match) {
continued = true
route.navigate({ type: "session", sessionID: match })
if (args.fork) {
sdk.client.session.fork({ sessionID: match }).then((result) => {
if (result.data?.id) {
route.navigate({ type: "session", sessionID: result.data.id })
} else {
toast.show({ message: "Failed to fork session", variant: "error" })
}
})
} else {
route.navigate({ type: "session", sessionID: match })
}
}
})

// Handle --session with --fork: wait for sync to be fully complete before forking
// (session list loads in non-blocking phase for --session, so we must wait for "complete"
// to avoid a race where reconcile overwrites the newly forked session)
let forked = false
createEffect(() => {
if (forked || sync.status !== "complete" || !args.sessionID || !args.fork) return
forked = true
sdk.client.session.fork({ sessionID: args.sessionID }).then((result) => {
if (result.data?.id) {
route.navigate({ type: "session", sessionID: result.data.id })
} else {
toast.show({ message: "Failed to fork session", variant: "error" })
}
})
})

createEffect(
on(
() => sync.status === "complete" && sync.data.provider.length === 0,
Expand Down
1 change: 1 addition & 0 deletions packages/opencode/src/cli/cmd/tui/context/args.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Args {
prompt?: string
continue?: boolean
sessionID?: string
fork?: boolean
}

export const { use: useArgs, provider: ArgsProvider } = createSimpleContext({
Expand Down
10 changes: 10 additions & 0 deletions packages/opencode/src/cli/cmd/tui/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export const TuiThreadCommand = cmd({
type: "string",
describe: "session id to continue",
})
.option("fork", {
type: "boolean",
describe: "fork the session when continuing (use with --continue or --session)",
})
.option("prompt", {
type: "string",
describe: "prompt to use",
Expand All @@ -73,6 +77,11 @@ export const TuiThreadCommand = cmd({
describe: "agent to use",
}),
handler: async (args) => {
if (args.fork && !args.continue && !args.session) {
UI.error("--fork requires --continue or --session")
process.exit(1)
}

// Resolve relative paths against PWD to preserve behavior when using --cwd flag
const baseCwd = process.env.PWD ?? process.cwd()
const cwd = args.project ? path.resolve(baseCwd, args.project) : process.cwd()
Expand Down Expand Up @@ -150,6 +159,7 @@ export const TuiThreadCommand = cmd({
agent: args.agent,
model: args.model,
prompt,
fork: args.fork,
},
onExit: async () => {
await client.call("shutdown", undefined)
Expand Down
Loading