summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-07-01 20:39:43 -0400
committerDax Raad <[email protected]>2025-07-01 20:39:43 -0400
commit7e5941e14b8746fbc68c6dc18545007013f6c9bb (patch)
tree5e0f1297c90f5ffacccc281ea264ecb516c32fa9
parentc68aeed8d96acfa28135852d620785e3557143af (diff)
downloadopencode-7e5941e14b8746fbc68c6dc18545007013f6c9bb.tar.gz
opencode-7e5941e14b8746fbc68c6dc18545007013f6c9bb.zip
ignore: add file status command
-rw-r--r--packages/opencode/src/cli/cmd/debug/file.ts24
-rw-r--r--packages/opencode/src/file/index.ts88
-rw-r--r--packages/opencode/src/file/watch.ts38
3 files changed, 122 insertions, 28 deletions
diff --git a/packages/opencode/src/cli/cmd/debug/file.ts b/packages/opencode/src/cli/cmd/debug/file.ts
index 96c4d91b5..021c49db4 100644
--- a/packages/opencode/src/cli/cmd/debug/file.ts
+++ b/packages/opencode/src/cli/cmd/debug/file.ts
@@ -2,12 +2,6 @@ import { File } from "../../../file"
import { bootstrap } from "../../bootstrap"
import { cmd } from "../cmd"
-export const FileCommand = cmd({
- command: "file",
- builder: (yargs) => yargs.command(FileReadCommand).demandCommand(),
- async handler() {},
-})
-
const FileReadCommand = cmd({
command: "read <path>",
builder: (yargs) =>
@@ -23,3 +17,21 @@ const FileReadCommand = cmd({
})
},
})
+
+const FileStatusCommand = cmd({
+ command: "status",
+ builder: (yargs) => yargs,
+ async handler() {
+ await bootstrap({ cwd: process.cwd() }, async () => {
+ const status = await File.status()
+ console.log(JSON.stringify(status, null, 2))
+ })
+ },
+})
+
+export const FileCommand = cmd({
+ command: "file",
+ builder: (yargs) =>
+ yargs.command(FileReadCommand).command(FileStatusCommand).demandCommand(),
+ async handler() {},
+})
diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts
index 4f487cabc..29331a587 100644
--- a/packages/opencode/src/file/index.ts
+++ b/packages/opencode/src/file/index.ts
@@ -3,13 +3,13 @@ import { Bus } from "../bus"
import { $ } from "bun"
import { createPatch } from "diff"
import path from "path"
-import { status } from "isomorphic-git"
+import * as git from "isomorphic-git"
import { App } from "../app/app"
import fs from "fs"
import { Log } from "../util/log"
export namespace File {
- const log = Log.create({ service: "files" })
+ const log = Log.create({ service: "file" })
export const Event = {
Edited: Bus.event(
@@ -20,6 +20,84 @@ export namespace File {
),
}
+ export async function status() {
+ const app = App.info()
+ if (!app.git) return []
+
+ // Get all changed files with line counts in one command
+ const diffOutput = await $`git diff --numstat HEAD`
+ .cwd(app.path.root)
+ .quiet()
+ .nothrow()
+ .text()
+
+ const changedFiles = []
+
+ if (diffOutput.trim()) {
+ const lines = diffOutput.trim().split("\n")
+ for (const line of lines) {
+ const [added, removed, filepath] = line.split("\t")
+ changedFiles.push({
+ file: filepath,
+ added: added === "-" ? 0 : parseInt(added, 10),
+ removed: removed === "-" ? 0 : parseInt(removed, 10),
+ status: "modified",
+ })
+ }
+ }
+
+ // Get untracked files
+ const untrackedOutput = await $`git ls-files --others --exclude-standard`
+ .cwd(app.path.root)
+ .quiet()
+ .nothrow()
+ .text()
+
+ if (untrackedOutput.trim()) {
+ const untrackedFiles = untrackedOutput.trim().split("\n")
+ for (const filepath of untrackedFiles) {
+ try {
+ const content = await Bun.file(
+ path.join(app.path.root, filepath),
+ ).text()
+ const lines = content.split("\n").length
+ changedFiles.push({
+ file: filepath,
+ added: lines,
+ removed: 0,
+ status: "added",
+ })
+ } catch {
+ continue
+ }
+ }
+ }
+
+ // Get deleted files
+ const deletedOutput = await $`git diff --name-only --diff-filter=D HEAD`
+ .cwd(app.path.root)
+ .quiet()
+ .nothrow()
+ .text()
+
+ if (deletedOutput.trim()) {
+ const deletedFiles = deletedOutput.trim().split("\n")
+ for (const filepath of deletedFiles) {
+ changedFiles.push({
+ file: filepath,
+ added: 0,
+ removed: 0, // Could get original line count but would require another git command
+ status: "deleted",
+ })
+ }
+ }
+
+ return changedFiles.map((x) => ({
+ ...x,
+ file: path.relative(app.path.cwd, path.join(app.path.root, x.file)),
+ }))
+ }
+
export async function read(file: string) {
using _ = log.time("read", { file })
const app = App.info()
@@ -27,7 +105,7 @@ export namespace File {
const content = await Bun.file(full).text()
if (app.git) {
const rel = path.relative(app.path.root, full)
- const diff = await status({
+ const diff = await git.status({
fs,
dir: app.path.root,
filepath: rel,
@@ -38,7 +116,9 @@ export namespace File {
.quiet()
.nothrow()
.text()
- const patch = createPatch(file, original, content)
+ const patch = createPatch(file, original, content, "old", "new", {
+ context: Infinity,
+ })
return patch
}
}
diff --git a/packages/opencode/src/file/watch.ts b/packages/opencode/src/file/watch.ts
index 2a702984e..8bc70cd83 100644
--- a/packages/opencode/src/file/watch.ts
+++ b/packages/opencode/src/file/watch.ts
@@ -22,28 +22,30 @@ export namespace FileWatcher {
"file.watcher",
() => {
const app = App.use()
- const watcher = fs.watch(
- app.info.path.cwd,
- { recursive: true },
- (event, file) => {
- log.info("change", { file, event })
- if (!file) return
- // for some reason async local storage is lost here
- // https://github.com/oven-sh/bun/issues/20754
- App.provideExisting(app, async () => {
- Bus.publish(Event.Updated, {
- file,
- event,
+ try {
+ const watcher = fs.watch(
+ app.info.path.cwd,
+ { recursive: true },
+ (event, file) => {
+ log.info("change", { file, event })
+ if (!file) return
+ // for some reason async local storage is lost here
+ // https://github.com/oven-sh/bun/issues/20754
+ App.provideExisting(app, async () => {
+ Bus.publish(Event.Updated, {
+ file,
+ event,
+ })
})
- })
- },
- )
- return {
- watcher,
+ },
+ )
+ return { watcher }
+ } finally {
+ return {}
}
},
async (state) => {
- state.watcher.close()
+ state.watcher?.close()
},
)()
}