diff --git a/packages/opencode/src/cli/bootstrap.ts b/packages/opencode/src/cli/bootstrap.ts index 4419773b4..3af9809bc 100644 --- a/packages/opencode/src/cli/bootstrap.ts +++ b/packages/opencode/src/cli/bootstrap.ts @@ -3,6 +3,7 @@ import { ConfigHooks } from "../config/hooks" import { Format } from "../format" import { LSP } from "../lsp" import { Share } from "../share/share" +import { Snapshot } from "../snapshot" export async function bootstrap(input: App.Input, cb: (app: App.Info) => Promise) { return App.provide(input, async (app) => { @@ -10,6 +11,7 @@ export async function bootstrap(input: App.Input, cb: (app: App.Info) => Prom Format.init() ConfigHooks.init() LSP.init() + Snapshot.init() return cb(app) }) diff --git a/packages/opencode/src/cli/cmd/debug/snapshot.ts b/packages/opencode/src/cli/cmd/debug/snapshot.ts index 7a86f2b88..36f89f337 100644 --- a/packages/opencode/src/cli/cmd/debug/snapshot.ts +++ b/packages/opencode/src/cli/cmd/debug/snapshot.ts @@ -14,7 +14,7 @@ const CreateCommand = cmd({ command: "create", async handler() { await bootstrap({ cwd: process.cwd() }, async () => { - const result = await Snapshot.create("test") + const result = await Snapshot.create() console.log(result) }) }, @@ -30,7 +30,7 @@ const RestoreCommand = cmd({ }), async handler(args) { await bootstrap({ cwd: process.cwd() }, async () => { - await Snapshot.restore("test", args.commit) + await Snapshot.restore(args.commit) console.log("restored") }) }, @@ -47,7 +47,7 @@ export const DiffCommand = cmd({ }), async handler(args) { await bootstrap({ cwd: process.cwd() }, async () => { - const diff = await Snapshot.diff("test", args.commit) + const diff = await Snapshot.diff(args.commit) console.log(diff) }) }, diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 01c3e2dbe..8d7bb299c 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -819,7 +819,7 @@ export namespace Session { }) switch (value.type) { case "start": - const snapshot = await Snapshot.create(assistantMsg.sessionID, true) + const snapshot = await Snapshot.create() if (snapshot) await updatePart({ id: Identifier.ascending("part"), @@ -883,7 +883,7 @@ export namespace Session { }, }) delete toolCalls[value.toolCallId] - const snapshot = await Snapshot.create(assistantMsg.sessionID) + const snapshot = await Snapshot.create() if (snapshot) await updatePart({ id: Identifier.ascending("part"), @@ -912,7 +912,7 @@ export namespace Session { }, }) delete toolCalls[value.toolCallId] - const snapshot = await Snapshot.create(assistantMsg.sessionID) + const snapshot = await Snapshot.create() if (snapshot) await updatePart({ id: Identifier.ascending("part"), @@ -1075,9 +1075,9 @@ export namespace Session { if ((msg.info.id === input.messageID && !input.partID) || part.id === input.partID) { // if no useful parts left in message, same as reverting whole message const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) ? input.partID : undefined - const snapshot = session.revert?.snapshot ?? (await Snapshot.create(input.sessionID, true)) + const snapshot = session.revert?.snapshot ?? (await Snapshot.create(true)) log.info("revert snapshot", { snapshot }) - if (lastSnapshot) await Snapshot.restore(input.sessionID, lastSnapshot.snapshot) + if (lastSnapshot) await Snapshot.restore(lastSnapshot.snapshot) const next = await update(input.sessionID, (draft) => { draft.revert = { // if not part id jump to the last user message @@ -1097,7 +1097,7 @@ export namespace Session { log.info("unreverting", input) const session = await get(input.sessionID) if (!session.revert) return session - if (session.revert.snapshot) await Snapshot.restore(input.sessionID, session.revert.snapshot) + if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot) const next = await update(input.sessionID, (draft) => { draft.revert = undefined }) diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 68d9d5469..e147ba129 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -4,11 +4,26 @@ import path from "path" import fs from "fs/promises" import { Ripgrep } from "../file/ripgrep" import { Log } from "../util/log" +import { Global } from "../global" export namespace Snapshot { const log = Log.create({ service: "snapshot" }) - export async function create(sessionID: string, force?: boolean) { + export function init() { + Array.fromAsync( + new Bun.Glob("**/snapshot").scan({ + absolute: true, + onlyFiles: false, + cwd: Global.Path.data, + }), + ).then((files) => { + for (const file of files) { + fs.rmdir(file, { recursive: true }) + } + }) + } + + export async function create(force?: boolean) { log.info("creating snapshot") const app = App.info() @@ -23,7 +38,7 @@ export namespace Snapshot { if (files.length >= 1000) return } - const git = gitdir(sessionID) + const git = gitdir() if (await fs.mkdir(git, { recursive: true })) { await $`git init` .env({ @@ -50,22 +65,22 @@ export namespace Snapshot { return match![1] } - export async function restore(sessionID: string, snapshot: string) { + export async function restore(snapshot: string) { log.info("restore", { commit: snapshot }) const app = App.info() - const git = gitdir(sessionID) + const git = gitdir() await $`git --git-dir=${git} reset --hard ${snapshot}`.quiet().cwd(app.path.root) } - export async function diff(sessionID: string, commit: string) { - const git = gitdir(sessionID) + export async function diff(commit: string) { + const git = gitdir() const result = await $`git --git-dir=${git} diff -R ${commit}`.quiet().cwd(App.info().path.root) const text = result.stdout.toString("utf8") return text } - function gitdir(sessionID: string) { + function gitdir() { const app = App.info() - return path.join(app.path.data, "snapshot", sessionID) + return path.join(app.path.data, "snapshots") } }