summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-08-27 15:27:49 -0500
committerAdam <[email protected]>2025-08-27 15:28:03 -0500
commit8749c0c707330550489a2928802f5e994660a424 (patch)
treec80a3dcb2457089c74f71b4d18abcce951ad0742
parent335941737804b0eacd74c0cb8b56f37f13b1ea36 (diff)
downloadopencode-8749c0c707330550489a2928802f5e994660a424.tar.gz
opencode-8749c0c707330550489a2928802f5e994660a424.zip
feat: file list api
-rw-r--r--packages/opencode/src/cli/cmd/debug/file.ts19
-rw-r--r--packages/opencode/src/file/index.ts34
-rw-r--r--packages/opencode/src/server/server.ts32
-rw-r--r--packages/sdk/js/src/gen/sdk.gen.ts14
-rw-r--r--packages/sdk/js/src/gen/types.gen.ts26
-rw-r--r--packages/sdk/stainless/stainless.yml7
6 files changed, 122 insertions, 10 deletions
diff --git a/packages/opencode/src/cli/cmd/debug/file.ts b/packages/opencode/src/cli/cmd/debug/file.ts
index f773dbd9c..74fb19361 100644
--- a/packages/opencode/src/cli/cmd/debug/file.ts
+++ b/packages/opencode/src/cli/cmd/debug/file.ts
@@ -29,8 +29,25 @@ const FileStatusCommand = cmd({
},
})
+const FileListCommand = cmd({
+ command: "list <path>",
+ builder: (yargs) =>
+ yargs.positional("path", {
+ type: "string",
+ demandOption: true,
+ description: "File path to list",
+ }),
+ async handler(args) {
+ await bootstrap({ cwd: process.cwd() }, async () => {
+ const files = await File.list(args.path)
+ console.log(JSON.stringify(files, null, 2))
+ })
+ },
+})
+
export const FileCommand = cmd({
command: "file",
- builder: (yargs) => yargs.command(FileReadCommand).command(FileStatusCommand).demandCommand(),
+ builder: (yargs) =>
+ yargs.command(FileReadCommand).command(FileStatusCommand).command(FileListCommand).demandCommand(),
async handler() {},
})
diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts
index b99f35e1f..7613e920d 100644
--- a/packages/opencode/src/file/index.ts
+++ b/packages/opencode/src/file/index.ts
@@ -24,6 +24,17 @@ export namespace File {
export type Info = z.infer<typeof Info>
+ export const Node = z
+ .object({
+ name: z.string(),
+ path: z.string(),
+ type: z.enum(["file", "directory"]),
+ })
+ .openapi({
+ ref: "FileNode",
+ })
+ export type Node = z.infer<typeof Node>
+
export const Event = {
Edited: Bus.event(
"file.edited",
@@ -120,4 +131,27 @@ export namespace File {
}
return { type: "raw", content }
}
+
+ export async function list(dir?: string) {
+ const ignore = [".git", ".DS_Store"]
+ const app = App.info()
+ const resolved = dir ? path.join(app.path.cwd, dir) : app.path.cwd
+ const nodes: Node[] = []
+ for (const entry of await fs.promises.readdir(resolved, { withFileTypes: true })) {
+ if (ignore.includes(entry.name)) continue
+ const fullPath = path.join(resolved, entry.name)
+ const relativePath = path.relative(app.path.cwd, fullPath)
+ nodes.push({
+ name: entry.name,
+ path: relativePath,
+ type: entry.isDirectory() ? "directory" : "file",
+ })
+ }
+ return nodes.sort((a, b) => {
+ if (a.type !== b.type) {
+ return a.type === "directory" ? -1 : 1
+ }
+ return a.name.localeCompare(b.name)
+ })
+ }
}
diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts
index 631c9ab4a..27d4aa921 100644
--- a/packages/opencode/src/server/server.ts
+++ b/packages/opencode/src/server/server.ts
@@ -938,6 +938,34 @@ export namespace Server {
.get(
"/file",
describeRoute({
+ description: "List files and directories",
+ operationId: "file.list",
+ responses: {
+ 200: {
+ description: "Files and directories",
+ content: {
+ "application/json": {
+ schema: resolver(File.Node.array()),
+ },
+ },
+ },
+ },
+ }),
+ zValidator(
+ "query",
+ z.object({
+ path: z.string(),
+ }),
+ ),
+ async (c) => {
+ const path = c.req.valid("query").path
+ const content = await File.list(path)
+ return c.json(content)
+ },
+ )
+ .get(
+ "/file/content",
+ describeRoute({
description: "Read a file",
operationId: "file.read",
responses: {
@@ -965,10 +993,6 @@ export namespace Server {
async (c) => {
const path = c.req.valid("query").path
const content = await File.read(path)
- log.info("read file", {
- path,
- content: content.content,
- })
return c.json(content)
},
)
diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts
index f900c24f0..a50f5e142 100644
--- a/packages/sdk/js/src/gen/sdk.gen.ts
+++ b/packages/sdk/js/src/gen/sdk.gen.ts
@@ -59,6 +59,8 @@ import type {
FindFilesResponses,
FindSymbolsData,
FindSymbolsResponses,
+ FileListData,
+ FileListResponses,
FileReadData,
FileReadResponses,
FileStatusData,
@@ -458,11 +460,21 @@ class Find extends _HeyApiClient {
class File extends _HeyApiClient {
/**
+ * List files and directories
+ */
+ public list<ThrowOnError extends boolean = false>(options: Options<FileListData, ThrowOnError>) {
+ return (options.client ?? this._client).get<FileListResponses, unknown, ThrowOnError>({
+ url: "/file",
+ ...options,
+ })
+ }
+
+ /**
* Read a file
*/
public read<ThrowOnError extends boolean = false>(options: Options<FileReadData, ThrowOnError>) {
return (options.client ?? this._client).get<FileReadResponses, unknown, ThrowOnError>({
- url: "/file",
+ url: "/file/content",
...options,
})
}
diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts
index 1c4fc5b35..5043f9688 100644
--- a/packages/sdk/js/src/gen/types.gen.ts
+++ b/packages/sdk/js/src/gen/types.gen.ts
@@ -1138,6 +1138,12 @@ export type Symbol = {
}
}
+export type FileNode = {
+ name: string
+ path: string
+ type: "file" | "directory"
+}
+
export type File = {
path: string
added: number
@@ -1804,7 +1810,7 @@ export type FindSymbolsResponses = {
export type FindSymbolsResponse = FindSymbolsResponses[keyof FindSymbolsResponses]
-export type FileReadData = {
+export type FileListData = {
body?: never
path?: never
query: {
@@ -1813,6 +1819,24 @@ export type FileReadData = {
url: "/file"
}
+export type FileListResponses = {
+ /**
+ * Files and directories
+ */
+ 200: Array<FileNode>
+}
+
+export type FileListResponse = FileListResponses[keyof FileListResponses]
+
+export type FileReadData = {
+ body?: never
+ path?: never
+ query: {
+ path: string
+ }
+ url: "/file/content"
+}
+
export type FileReadResponses = {
/**
* File content
diff --git a/packages/sdk/stainless/stainless.yml b/packages/sdk/stainless/stainless.yml
index 3dd34a413..f829e2280 100644
--- a/packages/sdk/stainless/stainless.yml
+++ b/packages/sdk/stainless/stainless.yml
@@ -48,7 +48,6 @@ resources:
app:
models:
app: App
- logLevel: LogLevel
provider: Provider
model: Model
agent: Agent
@@ -61,7 +60,6 @@ resources:
find:
models:
- match: Match
symbol: Symbol
methods:
text: get /find
@@ -71,8 +69,11 @@ resources:
file:
models:
file: File
+ fileNode: FileNode
+
methods:
- read: get /file
+ list: get /file
+ read: get /file/content
status: get /file/status
config: