@deep-assistant/agent
Version:
A minimal, public domain AI CLI agent compatible with OpenCode's JSON interface. Bun-only runtime.
106 lines (101 loc) • 2.76 kB
text/typescript
import z from "zod"
import { Filesystem } from "../util/filesystem"
import path from "path"
import { $ } from "bun"
import { Storage } from "../storage/storage"
import { Log } from "../util/log"
import { Flag } from "../flag/flag"
export namespace Project {
const log = Log.create({ service: "project" })
export const Info = z
.object({
id: z.string(),
worktree: z.string(),
vcs: z.literal("git").optional(),
time: z.object({
created: z.number(),
initialized: z.number().optional(),
}),
})
.meta({
ref: "Project",
})
export type Info = z.infer<typeof Info>
export async function fromDirectory(directory: string) {
log.info("fromDirectory", { directory })
const matches = Filesystem.up({ targets: [".git"], start: directory })
const git = await matches.next().then((x) => x.value)
await matches.return()
if (!git) {
const project: Info = {
id: "global",
worktree: "/",
vcs: "none", // No VCS
time: {
created: Date.now(),
},
}
await Storage.write<Info>(["project", "global"], project)
return project
}
let worktree = path.dirname(git)
const timer = log.time("git.rev-parse")
let id = await Bun.file(path.join(git, "opencode"))
.text()
.then((x) => x.trim())
.catch(() => {})
if (!id) {
const roots = await $`git rev-list --max-parents=0 --all`
.quiet()
.nothrow()
.cwd(worktree)
.text()
.then((x) =>
x
.split("\n")
.filter(Boolean)
.map((x) => x.trim())
.toSorted(),
)
id = roots[0]
if (id) Bun.file(path.join(git, "opencode")).write(id)
}
timer.stop()
if (!id) {
const project: Info = {
id: "global",
worktree: "/",
time: {
created: Date.now(),
},
}
await Storage.write<Info>(["project", "global"], project)
return project
}
worktree = await $`git rev-parse --path-format=absolute --show-toplevel`
.quiet()
.nothrow()
.cwd(worktree)
.text()
.then((x) => x.trim())
const project: Info = {
id,
worktree,
vcs: "git",
time: {
created: Date.now(),
},
}
await Storage.write<Info>(["project", id], project)
return project
}
export async function setInitialized(projectID: string) {
await Storage.update<Info>(["project", projectID], (draft) => {
draft.time.initialized = Date.now()
})
}
export async function list() {
const keys = await Storage.list(["project"])
return await Promise.all(keys.map((x) => Storage.read<Info>(x)))
}
}