فهرست منبع

add back in file hierarchy in system prompt but limit to 200 items

Dax Raad 7 ماه پیش
والد
کامیت
dd5736fe5f

+ 37 - 0
packages/opencode/src/cli/cmd/debug.ts

@@ -0,0 +1,37 @@
+import { App } from "../../app/app"
+import { Ripgrep } from "../../file/ripgrep"
+import { LSP } from "../../lsp"
+import { cmd } from "./cmd"
+
+export const DebugCommand = cmd({
+  command: "debug",
+  builder: (yargs) =>
+    yargs.command(DiagnosticsCommand).command(TreeCommand).demandCommand(),
+  async handler() {},
+})
+
+const DiagnosticsCommand = cmd({
+  command: "diagnostics <file>",
+  builder: (yargs) =>
+    yargs.positional("file", { type: "string", demandOption: true }),
+  async handler(args) {
+    await App.provide({ cwd: process.cwd() }, async () => {
+      await LSP.touchFile(args.file, true)
+      console.log(await LSP.diagnostics())
+    })
+  },
+})
+
+const TreeCommand = cmd({
+  command: "tree",
+  builder: (yargs) =>
+    yargs.option("limit", {
+      type: "number",
+    }),
+  async handler(args) {
+    await App.provide({ cwd: process.cwd() }, async () => {
+      const app = App.info()
+      console.log(await Ripgrep.tree({ cwd: app.path.cwd, limit: args.limit }))
+    })
+  },
+})

+ 0 - 15
packages/opencode/src/cli/cmd/scrap.ts

@@ -1,15 +0,0 @@
-import { App } from "../../app/app"
-import { LSP } from "../../lsp"
-import { cmd } from "./cmd"
-
-export const ScrapCommand = cmd({
-  command: "scrap <file>",
-  builder: (yargs) =>
-    yargs.positional("file", { type: "string", demandOption: true }),
-  async handler(args) {
-    await App.provide({ cwd: process.cwd() }, async () => {
-      await LSP.touchFile(args.file, true)
-      console.log(await LSP.diagnostics())
-    })
-  },
-})

+ 0 - 21
packages/opencode/src/external/fzf.ts → packages/opencode/src/file/fzf.ts

@@ -5,7 +5,6 @@ import { z } from "zod"
 import { NamedError } from "../util/error"
 import { lazy } from "../util/lazy"
 import { Log } from "../util/log"
-import { $ } from "bun"
 
 export namespace Fzf {
   const log = Log.create({ service: "fzf" })
@@ -115,24 +114,4 @@ export namespace Fzf {
     const { filepath } = await state()
     return filepath
   }
-
-  export async function search(input: {
-    cwd: string
-    query: string
-    limit?: number
-  }) {
-    const results = await $`${await filepath()} --filter=${input.query}`
-      .quiet()
-      .throws(false)
-      .cwd(input.cwd)
-      .text()
-    const split = results
-      .trim()
-      .split("\n")
-      .filter((line) => line.length > 0)
-    log.info("results", {
-      count: split.length,
-    })
-    return split
-  }
 }

+ 100 - 0
packages/opencode/src/external/ripgrep.ts → packages/opencode/src/file/ripgrep.ts

@@ -129,4 +129,104 @@ export namespace Ripgrep {
     const result = await $`${{ raw: joined }}`.cwd(input.cwd).nothrow().text()
     return result.split("\n").filter(Boolean)
   }
+
+  export async function tree(input: { cwd: string; limit?: number }) {
+    const files = await Ripgrep.files({ cwd: input.cwd })
+    interface Node {
+      path: string[]
+      children: Node[]
+    }
+
+    function getPath(node: Node, parts: string[], create: boolean) {
+      if (parts.length === 0) return node
+      let current = node
+      for (const part of parts) {
+        let existing = current.children.find((x) => x.path.at(-1) === part)
+        if (!existing) {
+          if (!create) return
+          existing = {
+            path: current.path.concat(part),
+            children: [],
+          }
+          current.children.push(existing)
+        }
+        current = existing
+      }
+      return current
+    }
+
+    const root: Node = {
+      path: [],
+      children: [],
+    }
+    for (const file of files) {
+      const parts = file.split(path.sep)
+      getPath(root, parts, true)
+    }
+
+    function sort(node: Node) {
+      node.children.sort((a, b) => {
+        if (!a.children.length && b.children.length) return 1
+        if (!b.children.length && a.children.length) return -1
+        return a.path.at(-1)!.localeCompare(b.path.at(-1)!)
+      })
+      for (const child of node.children) {
+        sort(child)
+      }
+    }
+    sort(root)
+
+    let current = [root]
+    const result: Node = {
+      path: [],
+      children: [],
+    }
+
+    let processed = 0
+    const limit = input.limit ?? 50
+    while (current.length > 0) {
+      const next = []
+      for (const node of current) {
+        if (node.children.length) next.push(...node.children)
+      }
+      const max = Math.max(...current.map((x) => x.children.length))
+      for (let i = 0; i < max && processed < limit; i++) {
+        for (const node of current) {
+          const child = node.children[i]
+          if (!child) continue
+          getPath(result, child.path, true)
+          processed++
+          if (processed >= limit) break
+        }
+      }
+      if (processed >= limit) {
+        for (const node of [...current, ...next]) {
+          const compare = getPath(result, node.path, false)
+          if (!compare) continue
+          if (compare?.children.length !== node.children.length) {
+            const diff = node.children.length - compare.children.length
+            compare.children.push({
+              path: compare.path.concat(`[${diff} truncated]`),
+              children: [],
+            })
+          }
+        }
+        break
+      }
+      current = next
+    }
+
+    const lines: string[] = []
+
+    function render(node: Node, depth: number) {
+      const indent = "\t".repeat(depth)
+      lines.push(indent + node.path.at(-1) + (node.children.length ? "/" : ""))
+      for (const child of node.children) {
+        render(child, depth + 1)
+      }
+    }
+    result.children.map((x) => render(x, 0))
+
+    return lines.join("\n")
+  }
 }

+ 2 - 2
packages/opencode/src/index.ts

@@ -3,7 +3,6 @@ import yargs from "yargs"
 import { hideBin } from "yargs/helpers"
 import { RunCommand } from "./cli/cmd/run"
 import { GenerateCommand } from "./cli/cmd/generate"
-import { ScrapCommand } from "./cli/cmd/scrap"
 import { Log } from "./util/log"
 import { AuthCommand } from "./cli/cmd/auth"
 import { UpgradeCommand } from "./cli/cmd/upgrade"
@@ -14,6 +13,7 @@ import { NamedError } from "./util/error"
 import { FormatError } from "./cli/error"
 import { ServeCommand } from "./cli/cmd/serve"
 import { TuiCommand } from "./cli/cmd/tui"
+import { DebugCommand } from "./cli/cmd/debug"
 
 const cancel = new AbortController()
 
@@ -49,7 +49,7 @@ const cli = yargs(hideBin(process.argv))
   .command(TuiCommand)
   .command(RunCommand)
   .command(GenerateCommand)
-  .command(ScrapCommand)
+  .command(DebugCommand)
   .command(AuthCommand)
   .command(UpgradeCommand)
   .command(ServeCommand)

+ 1 - 1
packages/opencode/src/server/server.ts

@@ -12,7 +12,7 @@ import { App } from "../app/app"
 import { mapValues } from "remeda"
 import { NamedError } from "../util/error"
 import { ModelsDev } from "../provider/models"
-import { Ripgrep } from "../external/ripgrep"
+import { Ripgrep } from "../file/ripgrep"
 import { Config } from "../config/config"
 
 const ERRORS = {

+ 11 - 53
packages/opencode/src/session/system.ts

@@ -1,5 +1,5 @@
 import { App } from "../app/app"
-import { Ripgrep } from "../external/ripgrep"
+import { Ripgrep } from "../file/ripgrep"
 import { Global } from "../global"
 import { Filesystem } from "../util/filesystem"
 import path from "path"
@@ -27,55 +27,6 @@ export namespace SystemPrompt {
 
   export async function environment() {
     const app = App.info()
-
-    ;async () => {
-      const files = await Ripgrep.files({
-        cwd: app.path.cwd,
-      })
-      type Node = {
-        children: Record<string, Node>
-      }
-      const root: Node = {
-        children: {},
-      }
-      for (const file of files) {
-        const parts = file.split("/")
-        let node = root
-        for (const part of parts) {
-          const existing = node.children[part]
-          if (existing) {
-            node = existing
-            continue
-          }
-          node.children[part] = {
-            children: {},
-          }
-          node = node.children[part]
-        }
-      }
-
-      function render(path: string[], node: Node): string {
-        // if (path.length === 3) return "\t".repeat(path.length) + "..."
-        const lines: string[] = []
-        const entries = Object.entries(node.children).sort(([a], [b]) =>
-          a.localeCompare(b),
-        )
-
-        for (const [name, child] of entries) {
-          const currentPath = [...path, name]
-          const indent = "\t".repeat(path.length)
-          const hasChildren = Object.keys(child.children).length > 0
-          lines.push(`${indent}${name}` + (hasChildren ? "/" : ""))
-
-          if (hasChildren) lines.push(render(currentPath, child))
-        }
-
-        return lines.join("\n")
-      }
-      const result = render([], root)
-      return result
-    }
-
     return [
       [
         `Here is some useful information about the environment you are running in:`,
@@ -85,9 +36,16 @@ export namespace SystemPrompt {
         `  Platform: ${process.platform}`,
         `  Today's date: ${new Date().toDateString()}`,
         `</env>`,
-        // `<project>`,
-        // `  ${app.git ? await tree() : ""}`,
-        // `</project>`,
+        `<project>`,
+        `  ${
+          app.git
+            ? await Ripgrep.tree({
+                cwd: app.path.cwd,
+                limit: 200,
+              })
+            : ""
+        }`,
+        `</project>`,
       ].join("\n"),
     ]
   }

+ 1 - 1
packages/opencode/src/tool/glob.ts

@@ -3,7 +3,7 @@ import path from "path"
 import { Tool } from "./tool"
 import { App } from "../app/app"
 import DESCRIPTION from "./glob.txt"
-import { Ripgrep } from "../external/ripgrep"
+import { Ripgrep } from "../file/ripgrep"
 
 export const GlobTool = Tool.define({
   id: "glob",

+ 1 - 1
packages/opencode/src/tool/grep.ts

@@ -1,7 +1,7 @@
 import { z } from "zod"
 import { Tool } from "./tool"
 import { App } from "../app/app"
-import { Ripgrep } from "../external/ripgrep"
+import { Ripgrep } from "../file/ripgrep"
 
 import DESCRIPTION from "./grep.txt"