diff options
| author | Dax Raad <[email protected]> | 2025-07-01 12:06:38 -0400 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-07-01 12:28:34 -0400 |
| commit | 11d042be25ee0509db323dc117724b0ac9e4610a (patch) | |
| tree | f9b457eb5c8ea7aca8c293fd0208ce1f30310629 /packages | |
| parent | 33b5fe236a204a3d1d935a94e1d1d5f3a6f312a8 (diff) | |
| download | opencode-11d042be25ee0509db323dc117724b0ac9e4610a.tar.gz opencode-11d042be25ee0509db323dc117724b0ac9e4610a.zip | |
snapshot functionality
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/opencode/package.json | 1 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/debug/file.ts | 26 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/debug/index.ts | 17 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/debug/lsp.ts | 37 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/debug/ripgrep.ts (renamed from packages/opencode/src/cli/cmd/debug.ts) | 69 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/debug/snapshot.ts | 39 | ||||
| -rw-r--r-- | packages/opencode/src/snapshot/index.ts | 85 |
7 files changed, 210 insertions, 64 deletions
diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 6ccf2f969..fae2e3425 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -37,6 +37,7 @@ "env-paths": "3.0.0", "hono": "4.7.10", "hono-openapi": "0.4.8", + "isomorphic-git": "1.32.1", "open": "10.1.2", "remeda": "2.22.3", "ts-lsp-client": "1.0.3", diff --git a/packages/opencode/src/cli/cmd/debug/file.ts b/packages/opencode/src/cli/cmd/debug/file.ts new file mode 100644 index 000000000..1d23ed851 --- /dev/null +++ b/packages/opencode/src/cli/cmd/debug/file.ts @@ -0,0 +1,26 @@ +import { File } from "../../../file" +import { bootstrap } from "../../bootstrap" +import { cmd } from "../cmd" +import path from "path" + +export const FileCommand = cmd({ + command: "file", + builder: (yargs) => yargs.command(FileReadCommand).demandCommand(), + async handler() {}, +}) + +const FileReadCommand = cmd({ + command: "read <path>", + builder: (yargs) => + yargs.positional("path", { + type: "string", + demandOption: true, + description: "File path to read", + }), + async handler(args) { + await bootstrap({ cwd: process.cwd() }, async () => { + const content = await File.read(path.resolve(args.path)) + console.log(content) + }) + }, +}) diff --git a/packages/opencode/src/cli/cmd/debug/index.ts b/packages/opencode/src/cli/cmd/debug/index.ts new file mode 100644 index 000000000..5a765997f --- /dev/null +++ b/packages/opencode/src/cli/cmd/debug/index.ts @@ -0,0 +1,17 @@ +import { cmd } from "../cmd" +import { FileCommand } from "./file" +import { LSPCommand } from "./lsp" +import { RipgrepCommand } from "./ripgrep" +import { SnapshotCommand } from "./snapshot" + +export const DebugCommand = cmd({ + command: "debug", + builder: (yargs) => + yargs + .command(LSPCommand) + .command(RipgrepCommand) + .command(FileCommand) + .command(SnapshotCommand) + .demandCommand(), + async handler() {}, +}) diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts new file mode 100644 index 000000000..d596bf6c5 --- /dev/null +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -0,0 +1,37 @@ +import { LSP } from "../../../lsp" +import { bootstrap } from "../../bootstrap" +import { cmd } from "../cmd" +import { Log } from "../../../util/log" + +export const LSPCommand = cmd({ + command: "lsp", + builder: (yargs) => + yargs.command(DiagnosticsCommand).command(SymbolsCommand).demandCommand(), + async handler() {}, +}) + +const DiagnosticsCommand = cmd({ + command: "diagnostics <file>", + builder: (yargs) => + yargs.positional("file", { type: "string", demandOption: true }), + async handler(args) { + await bootstrap({ cwd: process.cwd() }, async () => { + await LSP.touchFile(args.file, true) + console.log(await LSP.diagnostics()) + }) + }, +}) + +export const SymbolsCommand = cmd({ + command: "symbols <query>", + builder: (yargs) => + yargs.positional("query", { type: "string", demandOption: true }), + async handler(args) { + await bootstrap({ cwd: process.cwd() }, async () => { + await LSP.touchFile("./src/index.ts", true) + using _ = Log.Default.time("symbols") + const results = await LSP.workspaceSymbol(args.query) + console.log(JSON.stringify(results, null, 2)) + }) + }, +}) diff --git a/packages/opencode/src/cli/cmd/debug.ts b/packages/opencode/src/cli/cmd/debug/ripgrep.ts index 6d218733b..c71368671 100644 --- a/packages/opencode/src/cli/cmd/debug.ts +++ b/packages/opencode/src/cli/cmd/debug/ripgrep.ts @@ -1,52 +1,9 @@ -import { App } from "../../app/app" -import { Ripgrep } from "../../file/ripgrep" -import { File } from "../../file" -import { LSP } from "../../lsp" -import { Log } from "../../util/log" -import { bootstrap } from "../bootstrap" -import { cmd } from "./cmd" -import path from "path" +import { App } from "../../../app/app" +import { Ripgrep } from "../../../file/ripgrep" +import { bootstrap } from "../../bootstrap" +import { cmd } from "../cmd" -export const DebugCommand = cmd({ - command: "debug", - builder: (yargs) => - yargs - .command(DiagnosticsCommand) - .command(RipgrepCommand) - .command(SymbolsCommand) - .command(FileReadCommand) - .demandCommand(), - async handler() {}, -}) - -const DiagnosticsCommand = cmd({ - command: "diagnostics <file>", - builder: (yargs) => - yargs.positional("file", { type: "string", demandOption: true }), - async handler(args) { - await bootstrap({ cwd: process.cwd() }, async () => { - await LSP.touchFile(args.file, true) - await LSP.touchFile(args.file, true) - console.log(await LSP.diagnostics()) - }) - }, -}) - -const SymbolsCommand = cmd({ - command: "symbols <query>", - builder: (yargs) => - yargs.positional("query", { type: "string", demandOption: true }), - async handler(args) { - await bootstrap({ cwd: process.cwd() }, async () => { - await LSP.touchFile("./src/index.ts", true) - using _ = Log.Default.time("symbols") - const results = await LSP.workspaceSymbol(args.query) - console.log(JSON.stringify(results, null, 2)) - }) - }, -}) - -const RipgrepCommand = cmd({ +export const RipgrepCommand = cmd({ command: "rg", builder: (yargs) => yargs @@ -128,19 +85,3 @@ const SearchCommand = cmd({ console.log(JSON.stringify(results, null, 2)) }, }) - -const FileReadCommand = cmd({ - command: "file-read <path>", - builder: (yargs) => - yargs.positional("path", { - type: "string", - demandOption: true, - description: "File path to read", - }), - async handler(args) { - await bootstrap({ cwd: process.cwd() }, async () => { - const content = await File.read(path.resolve(args.path)) - console.log(content) - }) - }, -}) diff --git a/packages/opencode/src/cli/cmd/debug/snapshot.ts b/packages/opencode/src/cli/cmd/debug/snapshot.ts new file mode 100644 index 000000000..a6d129d5a --- /dev/null +++ b/packages/opencode/src/cli/cmd/debug/snapshot.ts @@ -0,0 +1,39 @@ +import { Snapshot } from "../../../snapshot" +import { bootstrap } from "../../bootstrap" +import { cmd } from "../cmd" + +export const SnapshotCommand = cmd({ + command: "snapshot", + builder: (yargs) => + yargs + .command(SnapshotCreateCommand) + .command(SnapshotRestoreCommand) + .demandCommand(), + async handler() {}, +}) + +export const SnapshotCreateCommand = cmd({ + command: "create", + async handler() { + await bootstrap({ cwd: process.cwd() }, async () => { + const result = await Snapshot.create("test") + console.log(result) + }) + }, +}) + +export const SnapshotRestoreCommand = cmd({ + command: "restore <commit>", + builder: (yargs) => + yargs.positional("commit", { + type: "string", + description: "commit", + demandOption: true, + }), + async handler(args) { + await bootstrap({ cwd: process.cwd() }, async () => { + await Snapshot.restore("test", args.commit) + console.log("restored") + }) + }, +}) diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts new file mode 100644 index 000000000..bf8ea05f6 --- /dev/null +++ b/packages/opencode/src/snapshot/index.ts @@ -0,0 +1,85 @@ +import { App } from "../app/app" +import { + add, + commit, + init, + checkout, + statusMatrix, + remove, +} from "isomorphic-git" +import path from "path" +import fs from "fs" +import { Ripgrep } from "../file/ripgrep" +import { Log } from "../util/log" + +export namespace Snapshot { + const log = Log.create({ service: "snapshot" }) + + export async function create(sessionID: string) { + const app = App.info() + const git = gitdir(sessionID) + const files = await Ripgrep.files({ + cwd: app.path.cwd, + limit: app.git ? undefined : 1000, + }) + // not a git repo and too big to snapshot + if (!app.git && files.length === 1000) return + await init({ + dir: app.path.cwd, + gitdir: git, + fs, + }) + const status = await statusMatrix({ + fs, + gitdir: git, + dir: app.path.cwd, + }) + await add({ + fs, + gitdir: git, + parallel: true, + dir: app.path.cwd, + filepath: files, + }) + for (const [file, _head, workdir, stage] of status) { + if (workdir === 0 && stage === 1) { + log.info("remove", { file }) + await remove({ + fs, + gitdir: git, + dir: app.path.cwd, + filepath: file, + }) + } + } + const result = await commit({ + fs, + gitdir: git, + dir: app.path.cwd, + message: "snapshot", + author: { + name: "opencode", + email: "[email protected]", + }, + }) + log.info("commit", { result }) + return result + } + + export async function restore(sessionID: string, commit: string) { + log.info("restore", { commit }) + const app = App.info() + await checkout({ + fs, + gitdir: gitdir(sessionID), + dir: app.path.cwd, + ref: commit, + force: true, + }) + } + + function gitdir(sessionID: string) { + const app = App.info() + return path.join(app.path.data, "snapshot", sessionID) + } +} |
