jev-lens 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/classifier.d.ts +78 -0
- package/dist/classifier.js +67 -0
- package/dist/config.d.ts +96 -0
- package/dist/config.js +119 -0
- package/dist/health.d.ts +11 -0
- package/dist/health.js +52 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/lens.d.ts +69 -0
- package/dist/lens.js +58 -0
- package/dist/presend.d.ts +151 -0
- package/dist/presend.js +172 -0
- package/dist/recall.d.ts +31 -0
- package/dist/recall.js +58 -0
- package/dist/shell-display.d.ts +2 -0
- package/dist/shell-display.js +135 -0
- package/dist/text.d.ts +17 -0
- package/dist/text.js +66 -0
- package/dist/treesitter.d.ts +10 -0
- package/dist/treesitter.js +263 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.js +1 -0
- package/dist/views.d.ts +129 -0
- package/dist/views.js +687 -0
- package/package.json +52 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter backed structure for code views: exact top-level blocks (functions, classes,
|
|
3
|
+
* methods, top-level assignments) with signature lines, for the languages that ship in
|
|
4
|
+
* tree-sitter-wasms. Falls back to undefined when the language is unknown or parsing fails,
|
|
5
|
+
* in which case views.ts uses its regex heuristics.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
|
+
import { dirname, extname, join } from "node:path";
|
|
10
|
+
const require = createRequire(import.meta.url);
|
|
11
|
+
/** Grammars shipped by @vscode/tree-sitter-wasm (ABI-compatible with web-tree-sitter 0.27). */
|
|
12
|
+
const LANG_BY_EXT = {
|
|
13
|
+
".js": "javascript", ".mjs": "javascript", ".cjs": "javascript", ".jsx": "javascript",
|
|
14
|
+
".ts": "typescript", ".mts": "typescript", ".cts": "typescript", ".tsx": "tsx",
|
|
15
|
+
".py": "python", ".pyx": "python", ".go": "go", ".rs": "rust", ".java": "java", ".rb": "ruby",
|
|
16
|
+
".c": "c", ".h": "c", ".cc": "cpp", ".cpp": "cpp", ".hpp": "cpp", ".cs": "c-sharp", ".php": "php",
|
|
17
|
+
".sh": "bash", ".bash": "bash", ".css": "css",
|
|
18
|
+
".kt": "kotlin", ".kts": "kotlin",
|
|
19
|
+
};
|
|
20
|
+
/** Extra grammar packages: language → wasm path resolver (the VS Code bundle has no Kotlin). */
|
|
21
|
+
const EXTRA_WASM = {
|
|
22
|
+
kotlin: () => {
|
|
23
|
+
try {
|
|
24
|
+
const dir = dirname(require.resolve("@binclusive/tree-sitter-kotlin-wasm/package.json"));
|
|
25
|
+
const { readdirSync } = require("node:fs");
|
|
26
|
+
const walk = (d) => { for (const f of readdirSync(d, { withFileTypes: true })) {
|
|
27
|
+
const p = join(d, f.name);
|
|
28
|
+
if (f.isDirectory() && f.name !== "node_modules") {
|
|
29
|
+
const r = walk(p);
|
|
30
|
+
if (r)
|
|
31
|
+
return r;
|
|
32
|
+
}
|
|
33
|
+
else if (f.name.endsWith(".wasm"))
|
|
34
|
+
return p;
|
|
35
|
+
} return undefined; };
|
|
36
|
+
return walk(dir);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
/** Node types that count as top-level blocks, per language family. */
|
|
44
|
+
const BLOCK_TYPES = new Set([
|
|
45
|
+
"function_declaration", "function_definition", "generator_function_declaration", "class_declaration", "class_definition",
|
|
46
|
+
"method_definition", "method_declaration", "abstract_class_declaration", "interface_declaration", "type_alias_declaration",
|
|
47
|
+
"enum_declaration", "module", "internal_module", "lexical_declaration", "variable_declaration", "export_statement",
|
|
48
|
+
"decorated_definition", "function_item", "impl_item", "struct_item", "enum_item", "trait_item", "mod_item", "const_item", "static_item",
|
|
49
|
+
"type_item", "func_literal", "method_declaration", "type_declaration", "var_declaration", "const_declaration",
|
|
50
|
+
"class_specifier", "struct_specifier", "namespace_definition", "template_declaration", "preproc_function_def",
|
|
51
|
+
"function_signature", "singleton_method", "module", "class", "method", "object_declaration", "property_declaration",
|
|
52
|
+
"companion_object", "constructor_declaration", "record_declaration", "annotation_type_declaration", "macro_definition", "extern_crate_declaration",
|
|
53
|
+
]);
|
|
54
|
+
const HEADER_TYPES = new Set(["import_statement", "import_declaration", "import_from_statement", "package_clause", "package_declaration", "use_declaration", "preproc_include", "require_call", "using_directive", "comment", "expression_statement", "attribute_item", "mod_item"]);
|
|
55
|
+
let ts;
|
|
56
|
+
let inited;
|
|
57
|
+
const languages = new Map();
|
|
58
|
+
function wasmDir() {
|
|
59
|
+
try {
|
|
60
|
+
return join(dirname(require.resolve("@vscode/tree-sitter-wasm/package.json")), "wasm");
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function init() {
|
|
67
|
+
if (ts)
|
|
68
|
+
return ts;
|
|
69
|
+
if (!inited) {
|
|
70
|
+
inited = (async () => {
|
|
71
|
+
try {
|
|
72
|
+
const mod = (await import("web-tree-sitter"));
|
|
73
|
+
await mod.Parser.init();
|
|
74
|
+
ts = mod;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
ts = undefined;
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
}
|
|
81
|
+
await inited;
|
|
82
|
+
return ts;
|
|
83
|
+
}
|
|
84
|
+
async function language(name) {
|
|
85
|
+
if (!languages.has(name)) {
|
|
86
|
+
languages.set(name, (async () => {
|
|
87
|
+
const mod = await init();
|
|
88
|
+
const dir = wasmDir();
|
|
89
|
+
if (!mod || !dir)
|
|
90
|
+
return undefined;
|
|
91
|
+
const file = EXTRA_WASM[name]?.() ?? join(dir, `tree-sitter-${name}.wasm`);
|
|
92
|
+
if (!file || !existsSync(file))
|
|
93
|
+
return undefined;
|
|
94
|
+
try {
|
|
95
|
+
return await mod.Language.load(file);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
})());
|
|
101
|
+
}
|
|
102
|
+
return languages.get(name);
|
|
103
|
+
}
|
|
104
|
+
export function languageForPath(path) {
|
|
105
|
+
return LANG_BY_EXT[extname(path).toLowerCase()];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Top-level blocks from the syntax tree: each named child of the root that is a declaration
|
|
109
|
+
* becomes a block spanning its full line range (including a directly preceding comment).
|
|
110
|
+
* Leading imports and other non-block statements are folded into a header block.
|
|
111
|
+
*/
|
|
112
|
+
export async function treeSitterBlocks(path, text, maxBlocks = 48) {
|
|
113
|
+
const lang = languageForPath(path);
|
|
114
|
+
if (!lang)
|
|
115
|
+
return undefined;
|
|
116
|
+
const mod = await init();
|
|
117
|
+
const L = await language(lang);
|
|
118
|
+
if (!mod || !L)
|
|
119
|
+
return undefined;
|
|
120
|
+
const parser = new mod.Parser();
|
|
121
|
+
parser.setLanguage(L);
|
|
122
|
+
const tree = parser.parse(text);
|
|
123
|
+
if (!tree)
|
|
124
|
+
return undefined;
|
|
125
|
+
const lines = text.split("\n");
|
|
126
|
+
const root = tree.rootNode;
|
|
127
|
+
// Python and Ruby put everything under "module"/"program"; unwrap one level when the root has a single block child.
|
|
128
|
+
let children = root.namedChildren;
|
|
129
|
+
if (children.length === 1 && (children[0].type === "module" || children[0].type === "program"))
|
|
130
|
+
children = children[0].namedChildren;
|
|
131
|
+
const blocks = [];
|
|
132
|
+
let pendingComment;
|
|
133
|
+
let headerEnd = 0;
|
|
134
|
+
for (const c of children) {
|
|
135
|
+
if (!c)
|
|
136
|
+
continue;
|
|
137
|
+
const startLine = c.startPosition.row;
|
|
138
|
+
const endLine = c.endPosition.row;
|
|
139
|
+
if (c.type === "comment") {
|
|
140
|
+
if (pendingComment === undefined)
|
|
141
|
+
pendingComment = startLine;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
let node = c;
|
|
145
|
+
// export const x = ...; export default class ...; decorated defs
|
|
146
|
+
if ((c.type === "export_statement" || c.type === "decorated_definition") && c.namedChildren.length) {
|
|
147
|
+
const inner = c.namedChildren.find((n) => n && BLOCK_TYPES.has(n.type));
|
|
148
|
+
if (inner)
|
|
149
|
+
node = inner;
|
|
150
|
+
}
|
|
151
|
+
// Multi-line top-level assignments (config dicts, tables, constants) are blocks too.
|
|
152
|
+
const isBigAssignment = (node.type === "expression_statement" || node.type === "assignment") && endLine - startLine >= 3;
|
|
153
|
+
// one-line declarations (type aliases, Kotlin data classes, Rust consts) are blocks too: they belong in the outline
|
|
154
|
+
const isBlock = BLOCK_TYPES.has(node.type) || isBigAssignment;
|
|
155
|
+
if (!isBlock) {
|
|
156
|
+
pendingComment = undefined;
|
|
157
|
+
if (blocks.length === 0)
|
|
158
|
+
headerEnd = endLine;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const from = (pendingComment ?? startLine) + 1;
|
|
162
|
+
pendingComment = undefined;
|
|
163
|
+
const sig = lines[startLine].trim().slice(0, 120);
|
|
164
|
+
// Large classes: expose their methods as blocks so the second step can pick individual bodies.
|
|
165
|
+
const body = node.namedChildren.find((n) => n && (n.type === "class_body" || n.type === "block" || n.type === "declaration_list" || n.type === "field_declaration_list"));
|
|
166
|
+
const methods = body ? body.namedChildren.filter((n) => n && (n.type === "method_definition" || n.type === "function_definition" || n.type === "method_declaration" || n.type === "constructor_declaration" || n.type === "decorated_definition" || n.type === "function_item" || n.type === "function_declaration" || n.type === "companion_object" || n.type === "property_declaration")) : [];
|
|
167
|
+
if (endLine - startLine > 40 && methods.length >= 2) {
|
|
168
|
+
blocks.push({ name: sig, from, to: methods[0].startPosition.row });
|
|
169
|
+
for (let k = 0; k < methods.length; k++) {
|
|
170
|
+
const mm = methods[k];
|
|
171
|
+
const mEnd = k + 1 < methods.length ? methods[k + 1].startPosition.row : endLine + 1;
|
|
172
|
+
blocks.push({ name: `${sig.replace(/[{:]\s*$/, "")} › ${lines[mm.startPosition.row].trim().slice(0, 80)}`, from: mm.startPosition.row + 1, to: mEnd });
|
|
173
|
+
}
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
blocks.push({ name: sig, from, to: endLine + 1 });
|
|
177
|
+
}
|
|
178
|
+
tree.delete();
|
|
179
|
+
parser.delete();
|
|
180
|
+
if (blocks.length < 2)
|
|
181
|
+
return blocks.length === 0 ? [] : undefined;
|
|
182
|
+
// fill gaps so the block list partitions the file
|
|
183
|
+
const out = [];
|
|
184
|
+
if (blocks[0].from > 1)
|
|
185
|
+
out.push({ name: "(header: imports, constants)", from: 1, to: blocks[0].from - 1 });
|
|
186
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
187
|
+
const b = { ...blocks[i] };
|
|
188
|
+
const next = blocks[i + 1];
|
|
189
|
+
if (next && next.from > b.to + 1)
|
|
190
|
+
b.to = next.from - 1;
|
|
191
|
+
if (!next && b.to < lines.length)
|
|
192
|
+
b.to = lines.length;
|
|
193
|
+
out.push(b);
|
|
194
|
+
}
|
|
195
|
+
void headerEnd;
|
|
196
|
+
return out.slice(0, maxBlocks);
|
|
197
|
+
}
|
|
198
|
+
const SIGNATURE_TYPES = new Set(["function_declaration", "function_definition", "method_definition", "method_declaration", "constructor_declaration", "function_item", "decorated_definition", "class_declaration", "class_definition", "interface_declaration", "struct_item", "enum_item", "trait_item", "impl_item", "object_declaration", "companion_object", "type_alias_declaration", "type_item", "record_declaration", "enum_declaration", "abstract_class_declaration", "singleton_method", "method", "class", "module", "func_literal", "generator_function_declaration", "lexical_declaration", "property_declaration"]);
|
|
199
|
+
/** 0-based rows of every declaration signature in the tree, at any nesting depth (methods in small classes, nested functions). */
|
|
200
|
+
async function signatureRows(path, text) {
|
|
201
|
+
const lang = languageForPath(path);
|
|
202
|
+
if (!lang)
|
|
203
|
+
return undefined;
|
|
204
|
+
const mod = await init();
|
|
205
|
+
const L = await language(lang);
|
|
206
|
+
if (!mod || !L)
|
|
207
|
+
return undefined;
|
|
208
|
+
const parser = new mod.Parser();
|
|
209
|
+
parser.setLanguage(L);
|
|
210
|
+
const tree = parser.parse(text);
|
|
211
|
+
if (!tree)
|
|
212
|
+
return undefined;
|
|
213
|
+
const rows = new Set();
|
|
214
|
+
const walk = (n, depth) => {
|
|
215
|
+
if (depth > 6)
|
|
216
|
+
return;
|
|
217
|
+
for (const c of n.namedChildren) {
|
|
218
|
+
if (!c)
|
|
219
|
+
continue;
|
|
220
|
+
if (SIGNATURE_TYPES.has(c.type)) {
|
|
221
|
+
// property/lexical declarations only when they hold a function (arrow functions, lambdas) or are top-level
|
|
222
|
+
if ((c.type === "lexical_declaration" || c.type === "property_declaration") && depth > 0 && !/=>|lambda|fun\b|function\b/.test(text.split("\n")[c.startPosition.row]))
|
|
223
|
+
continue;
|
|
224
|
+
rows.add(c.startPosition.row);
|
|
225
|
+
}
|
|
226
|
+
walk(c, depth + 1);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
walk(tree.rootNode, 0);
|
|
230
|
+
tree.delete();
|
|
231
|
+
parser.delete();
|
|
232
|
+
return [...rows].sort((a, b) => a - b);
|
|
233
|
+
}
|
|
234
|
+
/** Signature lines (block starts) as an outline index list, 0-based. */
|
|
235
|
+
export async function treeSitterOutline(path, text) {
|
|
236
|
+
const sigs = await signatureRows(path, text);
|
|
237
|
+
if (!sigs)
|
|
238
|
+
return undefined;
|
|
239
|
+
const blocks = (await treeSitterBlocks(path, text)) ?? [];
|
|
240
|
+
const idx = [...sigs];
|
|
241
|
+
const lines = text.split("\n");
|
|
242
|
+
if (blocks.length === 0)
|
|
243
|
+
for (let i = 0; i < Math.min(lines.length, 60); i++)
|
|
244
|
+
if (/^\s*(import|from|require|use|package|#include|using)\b/.test(lines[i]))
|
|
245
|
+
idx.push(i);
|
|
246
|
+
for (const b of blocks) {
|
|
247
|
+
if (b.name.startsWith("(header")) {
|
|
248
|
+
// imports plus any one-line declarations (Kotlin data classes, type aliases, constants) that were too short to be blocks
|
|
249
|
+
for (let i = b.from - 1; i < b.to; i++)
|
|
250
|
+
if (/^\s*(import|from|require|use|package|#include|using)\b/.test(lines[i]) || /^(export\s+)?(const|let|var|val|type|typealias|data class|sealed class|enum class|class|interface|object|fun|def|pub|static|final)\b/.test(lines[i]))
|
|
251
|
+
idx.push(i);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
// the signature line is the first non-comment line of the block
|
|
255
|
+
for (let i = b.from - 1; i < b.to; i++) {
|
|
256
|
+
if (!/^\s*(\/\/|\/\*|\*|#|"""|''')/.test(lines[i]) && lines[i].trim()) {
|
|
257
|
+
idx.push(i);
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return idx;
|
|
263
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type Bucket = "keep" | "trim" | "forget";
|
|
2
|
+
export interface Probabilities {
|
|
3
|
+
needed: number;
|
|
4
|
+
outcomeOnly: number;
|
|
5
|
+
}
|
|
6
|
+
export interface Decision {
|
|
7
|
+
/** toolCallId of the tool result this decision is about. */
|
|
8
|
+
id: string;
|
|
9
|
+
toolName: string;
|
|
10
|
+
bucket: Bucket;
|
|
11
|
+
p: Probabilities;
|
|
12
|
+
/** One-line description used in the stub, fixed at decision time so the stub never changes. */
|
|
13
|
+
summary: string;
|
|
14
|
+
tokensBefore: number;
|
|
15
|
+
decidedAt: number;
|
|
16
|
+
/** "pending" until first applied in a context call; then frozen forever. */
|
|
17
|
+
status: "pending" | "applied";
|
|
18
|
+
appliedAtCall?: number;
|
|
19
|
+
/** Why the decision was applied (rolling, cold-cache, compaction, forced). */
|
|
20
|
+
appliedReason?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface CallStats {
|
|
23
|
+
call: number;
|
|
24
|
+
at: number;
|
|
25
|
+
messages: number;
|
|
26
|
+
tokensOriginal: number;
|
|
27
|
+
tokensSent: number;
|
|
28
|
+
tokensPruned: number;
|
|
29
|
+
appliedNow: number;
|
|
30
|
+
frozen: number;
|
|
31
|
+
pendingHeld: number;
|
|
32
|
+
coldCache: boolean;
|
|
33
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/views.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Candidate views of a tool result. Every view is a deterministic subset of the original
|
|
3
|
+
* text (never generated), with line numbers so the agent can ask for exact ranges later.
|
|
4
|
+
*/
|
|
5
|
+
export type ViewKind = "full" | "outline" | "relevant" | "focus" | "signals" | "testlog" | "tree" | "matches" | "log" | "sample" | "head_tail" | "sections";
|
|
6
|
+
export interface View {
|
|
7
|
+
kind: ViewKind;
|
|
8
|
+
text: string;
|
|
9
|
+
lines: number;
|
|
10
|
+
chars: number;
|
|
11
|
+
/** 1-based line numbers of the original that are included. */
|
|
12
|
+
included: number[];
|
|
13
|
+
}
|
|
14
|
+
export type ContentKind = "code" | "data" | "prose" | "command" | "listing";
|
|
15
|
+
export { displayedFiles } from "./shell-display.ts";
|
|
16
|
+
/** Mixed or unknown file types retain the ordinary command policy. */
|
|
17
|
+
export declare function kindOfFiles(files: string[]): ContentKind | undefined;
|
|
18
|
+
/** Guess what kind of content this is from the tool, its arguments and the text itself. */
|
|
19
|
+
export declare function detectKind(toolName: string, args: unknown, text: string): ContentKind;
|
|
20
|
+
/** Mostly lines that are file paths → a directory listing or find output. */
|
|
21
|
+
export declare function looksLikePathList(text: string): boolean;
|
|
22
|
+
/** Many lines with the same delimiter count → tabular or log-like data. */
|
|
23
|
+
export declare function looksRepetitive(text: string): boolean;
|
|
24
|
+
/** Collapse decorative runs (=====, -----, ......) and very long lines so views stay small. */
|
|
25
|
+
export declare function tidyLine(l: string): string;
|
|
26
|
+
export declare function fullView(text: string): View;
|
|
27
|
+
export declare function headTailView(text: string, head?: number, tail?: number, tidy?: boolean): View;
|
|
28
|
+
/** Code and prose structure: signatures, exports, imports, doc comments, headings. */
|
|
29
|
+
export declare function outlineView(text: string, kind: ContentKind): View;
|
|
30
|
+
/** Lines mentioning any of the given terms, with context. */
|
|
31
|
+
export declare function focusView(text: string, terms: string[], ctx?: number, tidy?: boolean): View | undefined;
|
|
32
|
+
/** Command output: error/warning/summary lines with context, plus the tail. */
|
|
33
|
+
export declare function signalsView(text: string, ctx?: number, tail?: number, tidy?: boolean): View;
|
|
34
|
+
/** Is this command output a test run? */
|
|
35
|
+
export declare function looksLikeTestLog(text: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Test run output reduced to what the agent acts on: the session header, every failing test's
|
|
38
|
+
* section (test id, assertion, the last frames of its traceback), the short summary, and the
|
|
39
|
+
* final counts. Passing tests, dots and decorative bars are dropped.
|
|
40
|
+
*/
|
|
41
|
+
export declare function testlogView(text: string, ctx?: number, maxFailLines?: number, maxIds?: number): View | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Directory listings and find output: group paths by directory, keep the first entries of each
|
|
44
|
+
* directory and say how many more there are. Directories with many files (tests, fixtures) collapse.
|
|
45
|
+
*/
|
|
46
|
+
export declare function treeView(text: string, perDir?: number, terms?: string[], tidy?: boolean): View | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* grep / rg / git grep output (path:line:content): keep the first matches of every file and say how
|
|
49
|
+
* many more each file has. Files are what the agent navigates by; the tail of a long match list rarely matters.
|
|
50
|
+
*/
|
|
51
|
+
export declare function matchesView(text: string, perFile?: number, tidy?: boolean): View | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Log-like output (scripts, servers, repeated progress lines): keep the first two and the last
|
|
54
|
+
* occurrence of every line template, so repeated lines collapse while the story stays readable.
|
|
55
|
+
*/
|
|
56
|
+
export declare function logView(text: string, tidy?: boolean): View | undefined;
|
|
57
|
+
/** Data files: header plus a sample of rows and the count. */
|
|
58
|
+
export declare function sampleView(text: string, rows?: number, tidy?: boolean): View;
|
|
59
|
+
/** Pull identifier-like terms out of task text and tool arguments to drive the focus view. */
|
|
60
|
+
export declare function extractTerms(...texts: string[]): string[];
|
|
61
|
+
export interface ViewParams {
|
|
62
|
+
headLines: number;
|
|
63
|
+
tailLines: number;
|
|
64
|
+
focusCtx: number;
|
|
65
|
+
sampleRows: number;
|
|
66
|
+
signalsCtx: number;
|
|
67
|
+
signalsTail: number;
|
|
68
|
+
/** How many passing test ids the testlog view keeps as an index (0 = none). */
|
|
69
|
+
testIds: number;
|
|
70
|
+
/** Max lines kept per failure section in testlog. */
|
|
71
|
+
testFailLines: number;
|
|
72
|
+
/** grep-style output: matches kept per file in the "matches" view. */
|
|
73
|
+
matchesPerFile: number;
|
|
74
|
+
/** Log-like output: offer the "log" view that collapses repeated line templates. */
|
|
75
|
+
logView: boolean;
|
|
76
|
+
/** Command output: offer the "sections" view (first line of every section; bodies expanded in a second step). */
|
|
77
|
+
sectionsView: boolean;
|
|
78
|
+
/** Sections shorter than this are merged into their predecessor. */
|
|
79
|
+
sectionMinLines: number;
|
|
80
|
+
/** At most this many sections (adjacent ones are merged beyond it). */
|
|
81
|
+
sectionMaxBlocks: number;
|
|
82
|
+
/** Output without any section structure is cut into chunks of this many lines (0 = no sections then). */
|
|
83
|
+
sectionChunkLines: number;
|
|
84
|
+
minShrink: number;
|
|
85
|
+
}
|
|
86
|
+
export declare const DEFAULT_VIEW_PARAMS: ViewParams;
|
|
87
|
+
export interface Candidates {
|
|
88
|
+
kind: ContentKind;
|
|
89
|
+
views: View[];
|
|
90
|
+
}
|
|
91
|
+
/** Build the candidate views for a tool result. Full is always first. Views that do not shrink the text enough are dropped. */
|
|
92
|
+
export declare function buildCandidates(toolName: string, args: unknown, text: string, terms: string[], params?: Partial<ViewParams>): Candidates;
|
|
93
|
+
export interface FooterOptions {
|
|
94
|
+
/** How the host names the recall tool in the note (default: pi's `recall(id: "...")` phrasing). */
|
|
95
|
+
recall?: (toolCallId: string) => string;
|
|
96
|
+
/** An extra sentence, for hosts that add their own line numbers in front of the view's. */
|
|
97
|
+
note?: string;
|
|
98
|
+
}
|
|
99
|
+
export declare function footer(view: View, toolCallId: string, total: number, opts?: FooterOptions): string;
|
|
100
|
+
export interface Block {
|
|
101
|
+
/** The signature line (trimmed). */
|
|
102
|
+
name: string;
|
|
103
|
+
/** 1-based inclusive line range. */
|
|
104
|
+
from: number;
|
|
105
|
+
to: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Split code into top-level blocks: each block starts at a signature line with no indentation
|
|
109
|
+
* (or the least indentation seen) and runs until the next one. Leading imports form one block.
|
|
110
|
+
*/
|
|
111
|
+
export declare function splitBlocks(text: string, maxBlocks?: number): Block[];
|
|
112
|
+
/**
|
|
113
|
+
* Split command output into sections: grep context groups (separated by `--` or a change of file), the
|
|
114
|
+
* top-level keys or items of a JSON document, markdown headings, marker lines (COMMAND:, URL:, ALL-CAPS labels,
|
|
115
|
+
* ===== bars, tracebacks) and paragraphs separated by blank lines. Small sections are merged into their
|
|
116
|
+
* predecessor; at most maxBlocks sections are kept. Output with no structure at all falls back to fixed chunks.
|
|
117
|
+
*/
|
|
118
|
+
export declare function splitSections(text: string, minLines?: number, maxBlocks?: number, chunkLines?: number): Block[];
|
|
119
|
+
/** The first non-empty line of every section, line-numbered, with omission markers between. */
|
|
120
|
+
export declare function sectionsView(text: string, blocks: Block[], tidy?: boolean): View | undefined;
|
|
121
|
+
/** Outline plus the full bodies of the chosen blocks. */
|
|
122
|
+
export declare function relevantView(text: string, kind: ContentKind, blocks: Block[], expand: Set<number>, outlineIncluded?: number[]): View;
|
|
123
|
+
/**
|
|
124
|
+
* Async variant of buildCandidates that uses tree-sitter for the outline of code files when the
|
|
125
|
+
* grammar is available, and returns the blocks so the second step can reuse them.
|
|
126
|
+
*/
|
|
127
|
+
export declare function buildCandidatesAsync(toolName: string, args: unknown, text: string, terms: string[], params?: Partial<ViewParams>): Promise<Candidates & {
|
|
128
|
+
blocks?: Block[];
|
|
129
|
+
}>;
|