/** * 侧栏问一个路径时发给模型的话:写明是文件还是文件夹、相对工作区的路径(工作区外的库文件写绝对路径)。 * 文件附上开头若干行;文件夹只列这一层的名字,不把里面的代码贴进去。 */ /** 文件开头最多贴这么多行,剩下的让模型自己去读。 */ export const PATH_ASK_HEAD_LINES = 80 export interface PathChild { name: string kind: 'file' | 'folder' } /** 文件开头:贴得下的行,或者为什么没附上。 */ export type PathHead = | { lines: string[], truncated: boolean } | { unavailable: string } /** 问的这条路径在哪:工作区里的 [path] 是相对所在工作区文件夹的路径;[outside] 时在工作区外(比如库源码),[path] 是绝对路径 */ interface PathAskWhere { path: string outside?: true } export interface PathAskFile extends PathAskWhere { kind: 'file' head: PathHead } export interface PathAskFolder extends PathAskWhere { kind: 'folder' /** 这一层的子文件和子文件夹,调用方排好序;列不出来时是原因 */ children: PathChild[] | { unavailable: string } } export type PathAskSubject = PathAskFile | PathAskFolder const LEAD = '我在看项目里的一个路径,想问它。请用中文、大白话、简洁地回答,不绕弯子。需要时可以自己去读、搜项目里的文件,但只读,不要改任何东西。提到文件时写相对项目根目录的路径。' /** 一次提问的全文。不写进某一块代码的问答记录。 */ export function pathAskPrompt(subject: PathAskSubject, question: string): string { const noun = subject.kind === 'folder' ? '文件夹' : '文件' const where = subject.outside ? `这是项目外的一个${noun}(比如依赖库的源码),绝对路径:${subject.path}` : `这是一个${noun},相对路径:${subject.path}` const detail = subject.kind === 'file' ? fileHead(subject.head) : folderList(subject.children) return [LEAD, where, detail, `问题:${question}`].join('\n\n') } /** 这条路径已经问过、接着上一次会话问时发的话:只说还是这个文件或文件夹(`path` 同 [PathAskSubject] 的),材料在会话里。 */ export function pathAskFollowUp(path: string, kind: 'file' | 'folder', question: string): string { const noun = kind === 'folder' ? '文件夹' : '文件' return `接着问(还是 ${path} 这个${noun},需要时重新读):${question}` } function fileHead(head: PathHead): string { if ('unavailable' in head) return `文件开头没附上:${head.unavailable}` if (head.lines.length === 0) return head.truncated ? '文件开头一行都没读全,需要时自己读这个文件。' : '文件是空的。' const width = String(head.lines.length).length const body = head.lines.map((line, index) => `${String(index + 1).padStart(width)} | ${line}`).join('\n') const rest = head.truncated ? `\n(上面只贴了开头 ${head.lines.length} 行,剩下的需要时自己读这个文件)` : '' return `文件开头:\n${body}${rest}` } function folderList(children: PathAskFolder['children']): string { if (!Array.isArray(children)) return `这一层没列出来:${children.unavailable}` if (children.length === 0) return '里面这一层是空的。' const lines = children.map(child => `- ${child.kind === 'folder' ? '文件夹' : '文件'} ${child.name}`) return `里面这一层有:\n${lines.join('\n')}\n只列了这一层的名字,没有把里面的代码贴进来。侧栏里不显示的名字这里也没列。需要时自己去读这些文件。` }