improve snapshot system

This commit is contained in:
Dax Raad 2025-07-22 19:23:13 -04:00
parent ae1f3be109
commit 9b0935d14c
4 changed files with 34 additions and 17 deletions

View file

@ -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<T>(input: App.Input, cb: (app: App.Info) => Promise<T>) {
return App.provide(input, async (app) => {
@ -10,6 +11,7 @@ export async function bootstrap<T>(input: App.Input, cb: (app: App.Info) => Prom
Format.init()
ConfigHooks.init()
LSP.init()
Snapshot.init()
return cb(app)
})

View file

@ -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)
})
},

View file

@ -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
})

View file

@ -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")
}
}