summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/opencode/src/cli/cmd/run.ts23
-rw-r--r--packages/opencode/src/cli/cmd/tui/app.tsx31
-rw-r--r--packages/opencode/src/cli/cmd/tui/context/args.tsx1
-rw-r--r--packages/opencode/src/cli/cmd/tui/thread.ts10
4 files changed, 59 insertions, 6 deletions
diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts
index 6960ffd55..bf71fbe30 100644
--- a/packages/opencode/src/cli/cmd/run.ts
+++ b/packages/opencode/src/cli/cmd/run.ts
@@ -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",
@@ -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",
@@ -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
diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx
index 744203760..0d5aefe7b 100644
--- a/packages/opencode/src/cli/cmd/tui/app.tsx
+++ b/packages/opencode/src/cli/cmd/tui/app.tsx
@@ -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,
@@ -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,
diff --git a/packages/opencode/src/cli/cmd/tui/context/args.tsx b/packages/opencode/src/cli/cmd/tui/context/args.tsx
index ffd43009a..8a229ffab 100644
--- a/packages/opencode/src/cli/cmd/tui/context/args.tsx
+++ b/packages/opencode/src/cli/cmd/tui/context/args.tsx
@@ -6,6 +6,7 @@ export interface Args {
prompt?: string
continue?: boolean
sessionID?: string
+ fork?: boolean
}
export const { use: useArgs, provider: ArgsProvider } = createSimpleContext({
diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts
index 057142685..2ea49ff6b 100644
--- a/packages/opencode/src/cli/cmd/tui/thread.ts
+++ b/packages/opencode/src/cli/cmd/tui/thread.ts
@@ -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",
@@ -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()
@@ -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)