Simplify directory tree output for prompts (#11731)
This commit is contained in:
@@ -275,100 +275,56 @@ export namespace Ripgrep {
|
|||||||
log.info("tree", input)
|
log.info("tree", input)
|
||||||
const files = await Array.fromAsync(Ripgrep.files({ cwd: input.cwd, signal: input.signal }))
|
const files = await Array.fromAsync(Ripgrep.files({ cwd: input.cwd, signal: input.signal }))
|
||||||
interface Node {
|
interface Node {
|
||||||
path: string[]
|
name: string
|
||||||
children: Node[]
|
children: Map<string, Node>
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPath(node: Node, parts: string[], create: boolean) {
|
function dir(node: Node, name: string) {
|
||||||
if (parts.length === 0) return node
|
const existing = node.children.get(name)
|
||||||
let current = node
|
if (existing) return existing
|
||||||
for (const part of parts) {
|
const next = { name, children: new Map() }
|
||||||
let existing = current.children.find((x) => x.path.at(-1) === part)
|
node.children.set(name, next)
|
||||||
if (!existing) {
|
return next
|
||||||
if (!create) return
|
|
||||||
existing = {
|
|
||||||
path: current.path.concat(part),
|
|
||||||
children: [],
|
|
||||||
}
|
|
||||||
current.children.push(existing)
|
|
||||||
}
|
|
||||||
current = existing
|
|
||||||
}
|
|
||||||
return current
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const root: Node = {
|
const root: Node = { name: "", children: new Map() }
|
||||||
path: [],
|
|
||||||
children: [],
|
|
||||||
}
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (file.includes(".opencode")) continue
|
if (file.includes(".opencode")) continue
|
||||||
const parts = file.split(path.sep)
|
const parts = file.split(path.sep)
|
||||||
getPath(root, parts, true)
|
if (parts.length < 2) continue
|
||||||
}
|
let node = root
|
||||||
|
for (const part of parts.slice(0, -1)) {
|
||||||
function sort(node: Node) {
|
node = dir(node, part)
|
||||||
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]
|
function count(node: Node): number {
|
||||||
const result: Node = {
|
let total = 0
|
||||||
path: [],
|
for (const child of node.children.values()) {
|
||||||
children: [],
|
total += 1 + count(child)
|
||||||
}
|
}
|
||||||
|
return total
|
||||||
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 total = count(root)
|
||||||
|
const limit = input.limit ?? total
|
||||||
const lines: string[] = []
|
const lines: string[] = []
|
||||||
|
const queue: { node: Node; path: string }[] = []
|
||||||
|
for (const child of Array.from(root.children.values()).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||||
|
queue.push({ node: child, path: child.name })
|
||||||
|
}
|
||||||
|
|
||||||
function render(node: Node, depth: number) {
|
let used = 0
|
||||||
const indent = "\t".repeat(depth)
|
for (let i = 0; i < queue.length && used < limit; i++) {
|
||||||
lines.push(indent + node.path.at(-1) + (node.children.length ? "/" : ""))
|
const { node, path } = queue[i]
|
||||||
for (const child of node.children) {
|
lines.push(path)
|
||||||
render(child, depth + 1)
|
used++
|
||||||
|
for (const child of Array.from(node.children.values()).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||||
|
queue.push({ node: child, path: `${path}/${child.name}` })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.children.map((x) => render(x, 0))
|
|
||||||
|
if (total > used) lines.push(`[${total - used} truncated]`)
|
||||||
|
|
||||||
return lines.join("\n")
|
return lines.join("\n")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,16 +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>`,
|
||||||
`<files>`,
|
`<directories>`,
|
||||||
` ${
|
` ${
|
||||||
project.vcs === "git" && false
|
project.vcs === "git" && false
|
||||||
? await Ripgrep.tree({
|
? await Ripgrep.tree({
|
||||||
cwd: Instance.directory,
|
cwd: Instance.directory,
|
||||||
limit: 200,
|
limit: 50,
|
||||||
})
|
})
|
||||||
: ""
|
: ""
|
||||||
}`,
|
}`,
|
||||||
`</files>`,
|
`</directories>`,
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user