mirror of
https://github.com/sst/opencode.git
synced 2025-08-04 13:30:52 +00:00
add back in file hierarchy in system prompt but limit to 200 items
This commit is contained in:
parent
9f3ba03965
commit
dd5736fe5f
9 changed files with 153 additions and 94 deletions
37
packages/opencode/src/cli/cmd/debug.ts
Normal file
37
packages/opencode/src/cli/cmd/debug.ts
Normal file
|
@ -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 }))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
|
@ -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())
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -5,7 +5,6 @@ import { z } from "zod"
|
||||||
import { NamedError } from "../util/error"
|
import { NamedError } from "../util/error"
|
||||||
import { lazy } from "../util/lazy"
|
import { lazy } from "../util/lazy"
|
||||||
import { Log } from "../util/log"
|
import { Log } from "../util/log"
|
||||||
import { $ } from "bun"
|
|
||||||
|
|
||||||
export namespace Fzf {
|
export namespace Fzf {
|
||||||
const log = Log.create({ service: "fzf" })
|
const log = Log.create({ service: "fzf" })
|
||||||
|
@ -115,24 +114,4 @@ export namespace Fzf {
|
||||||
const { filepath } = await state()
|
const { filepath } = await state()
|
||||||
return filepath
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -129,4 +129,104 @@ export namespace Ripgrep {
|
||||||
const result = await $`${{ raw: joined }}`.cwd(input.cwd).nothrow().text()
|
const result = await $`${{ raw: joined }}`.cwd(input.cwd).nothrow().text()
|
||||||
return result.split("\n").filter(Boolean)
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,6 @@ import yargs from "yargs"
|
||||||
import { hideBin } from "yargs/helpers"
|
import { hideBin } from "yargs/helpers"
|
||||||
import { RunCommand } from "./cli/cmd/run"
|
import { RunCommand } from "./cli/cmd/run"
|
||||||
import { GenerateCommand } from "./cli/cmd/generate"
|
import { GenerateCommand } from "./cli/cmd/generate"
|
||||||
import { ScrapCommand } from "./cli/cmd/scrap"
|
|
||||||
import { Log } from "./util/log"
|
import { Log } from "./util/log"
|
||||||
import { AuthCommand } from "./cli/cmd/auth"
|
import { AuthCommand } from "./cli/cmd/auth"
|
||||||
import { UpgradeCommand } from "./cli/cmd/upgrade"
|
import { UpgradeCommand } from "./cli/cmd/upgrade"
|
||||||
|
@ -14,6 +13,7 @@ import { NamedError } from "./util/error"
|
||||||
import { FormatError } from "./cli/error"
|
import { FormatError } from "./cli/error"
|
||||||
import { ServeCommand } from "./cli/cmd/serve"
|
import { ServeCommand } from "./cli/cmd/serve"
|
||||||
import { TuiCommand } from "./cli/cmd/tui"
|
import { TuiCommand } from "./cli/cmd/tui"
|
||||||
|
import { DebugCommand } from "./cli/cmd/debug"
|
||||||
|
|
||||||
const cancel = new AbortController()
|
const cancel = new AbortController()
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ const cli = yargs(hideBin(process.argv))
|
||||||
.command(TuiCommand)
|
.command(TuiCommand)
|
||||||
.command(RunCommand)
|
.command(RunCommand)
|
||||||
.command(GenerateCommand)
|
.command(GenerateCommand)
|
||||||
.command(ScrapCommand)
|
.command(DebugCommand)
|
||||||
.command(AuthCommand)
|
.command(AuthCommand)
|
||||||
.command(UpgradeCommand)
|
.command(UpgradeCommand)
|
||||||
.command(ServeCommand)
|
.command(ServeCommand)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { App } from "../app/app"
|
||||||
import { mapValues } from "remeda"
|
import { mapValues } from "remeda"
|
||||||
import { NamedError } from "../util/error"
|
import { NamedError } from "../util/error"
|
||||||
import { ModelsDev } from "../provider/models"
|
import { ModelsDev } from "../provider/models"
|
||||||
import { Ripgrep } from "../external/ripgrep"
|
import { Ripgrep } from "../file/ripgrep"
|
||||||
import { Config } from "../config/config"
|
import { Config } from "../config/config"
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { App } from "../app/app"
|
import { App } from "../app/app"
|
||||||
import { Ripgrep } from "../external/ripgrep"
|
import { Ripgrep } from "../file/ripgrep"
|
||||||
import { Global } from "../global"
|
import { Global } from "../global"
|
||||||
import { Filesystem } from "../util/filesystem"
|
import { Filesystem } from "../util/filesystem"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
|
@ -27,55 +27,6 @@ export namespace SystemPrompt {
|
||||||
|
|
||||||
export async function environment() {
|
export async function environment() {
|
||||||
const app = App.info()
|
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 [
|
return [
|
||||||
[
|
[
|
||||||
`Here is some useful information about the environment you are running in:`,
|
`Here is some useful information about the environment you are running in:`,
|
||||||
|
@ -85,9 +36,16 @@ export namespace SystemPrompt {
|
||||||
` Platform: ${process.platform}`,
|
` Platform: ${process.platform}`,
|
||||||
` Today's date: ${new Date().toDateString()}`,
|
` Today's date: ${new Date().toDateString()}`,
|
||||||
`</env>`,
|
`</env>`,
|
||||||
// `<project>`,
|
`<project>`,
|
||||||
// ` ${app.git ? await tree() : ""}`,
|
` ${
|
||||||
// `</project>`,
|
app.git
|
||||||
|
? await Ripgrep.tree({
|
||||||
|
cwd: app.path.cwd,
|
||||||
|
limit: 200,
|
||||||
|
})
|
||||||
|
: ""
|
||||||
|
}`,
|
||||||
|
`</project>`,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import path from "path"
|
||||||
import { Tool } from "./tool"
|
import { Tool } from "./tool"
|
||||||
import { App } from "../app/app"
|
import { App } from "../app/app"
|
||||||
import DESCRIPTION from "./glob.txt"
|
import DESCRIPTION from "./glob.txt"
|
||||||
import { Ripgrep } from "../external/ripgrep"
|
import { Ripgrep } from "../file/ripgrep"
|
||||||
|
|
||||||
export const GlobTool = Tool.define({
|
export const GlobTool = Tool.define({
|
||||||
id: "glob",
|
id: "glob",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { Tool } from "./tool"
|
import { Tool } from "./tool"
|
||||||
import { App } from "../app/app"
|
import { App } from "../app/app"
|
||||||
import { Ripgrep } from "../external/ripgrep"
|
import { Ripgrep } from "../file/ripgrep"
|
||||||
|
|
||||||
import DESCRIPTION from "./grep.txt"
|
import DESCRIPTION from "./grep.txt"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue