@jarkkojs/readseek-api 0.11.4 → 0.12.1
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/README.md +8 -10
- package/dist/argv.d.ts +42 -33
- package/dist/client-output.d.ts +115 -8
- package/dist/client.d.ts +29 -29
- package/dist/contract.d.ts +22 -22
- package/dist/grep-presentation.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +484 -241
- package/dist/show-presentation.d.ts +1 -1
- package/dist/validation.d.ts +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,22 +9,22 @@ var EDIT_VARIANTS = [
|
|
|
9
9
|
];
|
|
10
10
|
var GIT_SCOPES = ["tracked", "untracked", "ignored", "all"];
|
|
11
11
|
var TOOL_NAMES = [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
12
|
+
"show",
|
|
13
|
+
"tree",
|
|
14
|
+
"meta",
|
|
15
|
+
"assets",
|
|
16
|
+
"syntax",
|
|
17
|
+
"check",
|
|
18
|
+
"caption",
|
|
19
|
+
"objects",
|
|
20
|
+
"ocr",
|
|
21
|
+
"search",
|
|
22
|
+
"grep",
|
|
23
|
+
"def",
|
|
24
|
+
"refs",
|
|
25
|
+
"edit",
|
|
26
|
+
"rename",
|
|
27
|
+
"write"
|
|
28
28
|
];
|
|
29
29
|
var PARAM_DESCRIPTIONS = {
|
|
30
30
|
path: "Absolute or relative file path",
|
|
@@ -36,12 +36,12 @@ var PARAM_DESCRIPTIONS = {
|
|
|
36
36
|
pattern: "AST pattern (ast-grep style), such as console.log($$$ARGS)",
|
|
37
37
|
patternContext: "Surrounding code context for sub-statement patterns",
|
|
38
38
|
selector: "Target AST node kind to extract from pattern context",
|
|
39
|
-
grepPattern: "
|
|
39
|
+
grepPattern: "Native regex pattern; set literal for exact text. Empty regex matches every line; empty literal is invalid.",
|
|
40
40
|
name: "Qualified or unqualified symbol name",
|
|
41
41
|
refsName: "Identifier to find references for",
|
|
42
42
|
gitScope: "In a Git repository, which files to search: tracked, untracked, ignored (untracked plus ignored), or all. Defaults to tracked plus untracked.",
|
|
43
|
-
at: "Location selector: line:N[:COLUMN], hash:H[:COLUMN], symbol:NAME, page:N, node:ID, or asset:ID",
|
|
44
|
-
assetAt: "Document selector: page:N, node:ID, or asset:ID",
|
|
43
|
+
at: "Location selector: N[:COLUMN], START-END, H[:COLUMN], NAME, line:N[:COLUMN], hash:H[:COLUMN], symbol:NAME, page:N, node:ID, or asset:ID",
|
|
44
|
+
assetAt: "Document selector: N, page:N, node:ID, or asset:ID",
|
|
45
45
|
rule: "KDL node selection rule, @path, or '-' for standard input",
|
|
46
46
|
rules: "KDL structural checks, @path, or '-' for standard input",
|
|
47
47
|
stdinName: "File name hint when the target is '-'",
|
|
@@ -53,7 +53,6 @@ var PARAM_DESCRIPTIONS = {
|
|
|
53
53
|
kind: "Restrict structure to this node kind",
|
|
54
54
|
outline: "Show outline of document hierarchy or source symbols",
|
|
55
55
|
noAnchors: "Omit source line anchors",
|
|
56
|
-
text: "Literal native text query; adapter find_text also supports regex via pattern",
|
|
57
56
|
edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
|
|
58
57
|
editVariant: "Exactly one nested edit variant",
|
|
59
58
|
setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
|
|
@@ -85,27 +84,28 @@ var PARAM_DESCRIPTIONS = {
|
|
|
85
84
|
literal: "Treat pattern literally",
|
|
86
85
|
context: "Surrounding lines for each match",
|
|
87
86
|
matchLimit: "Maximum matches to return",
|
|
87
|
+
grepLimit: "Maximum matching lines to return; positive integer, defaults to 100",
|
|
88
88
|
summary: "Return per-file counts",
|
|
89
89
|
grepScope: "Group matches by enclosing symbol",
|
|
90
|
-
scopeContext: "
|
|
90
|
+
scopeContext: "Non-negative context lines within each symbol; requires scope: symbol"
|
|
91
91
|
};
|
|
92
92
|
var TOOL_DESCRIPTIONS = {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
93
|
+
show: "Read source, document content, or image descriptors without inference. Obtain fresh `LINE:HASH` anchors before edits; use at with a column for cursor context. Document results include typed nodes and cached asset paths.",
|
|
94
|
+
tree: "Show parsed source or document hierarchy. Use shallow depth for file overviews.",
|
|
95
|
+
meta: "Show format, source identity, language, dimensions, or page count without decoding image content or inference.",
|
|
96
|
+
assets: "Return selected document asset descriptors with usable cached paths. Adapters attach images from those paths.",
|
|
97
|
+
syntax: "Check parser syntax diagnostics. Exit 0 is clean, 1 has findings, 2 is failure; this is not type checking.",
|
|
98
|
+
check: "Evaluate KDL structural assertions. Exit 0 is clean, 1 has findings, 2 is failure.",
|
|
99
|
+
caption: "Explicitly infer a caption using an installed model. Include text and objects to request OCR and detection too; never downloads models automatically.",
|
|
100
|
+
objects: "Explicitly infer object labels and bounding boxes using an installed model.",
|
|
101
|
+
ocr: "Explicitly recognize text from pixels using an installed model; reading never silently invokes OCR.",
|
|
102
|
+
search: "Find AST nodes by pattern or KDL selection rule. Returns edit-ready anchors. Use `grep` for text and regex.",
|
|
103
|
+
grep: "Search with native regex or literal matching. Returns matching-line counts, source LINE:HASH anchors, and document node-local locations. Native glob, context, summary, limits, Git scope, and symbol grouping apply; default limit is 100 matching lines.",
|
|
104
|
+
def: "Find a symbol definition or declaration by qualified or unqualified name.",
|
|
105
|
+
refs: "Find identifier-name matches with name, or binding-scoped references with at. Use before renaming or deleting a symbol.",
|
|
106
|
+
edit: "Edit existing files with fresh `LINE:HASH` anchors. Each edits item contains exactly one set_line, replace_lines, insert_after, replace_symbol, or replace object. Preview before applying a verified plan.",
|
|
107
|
+
rename: "Rename a symbol at a line or hash cursor with binding-aware edits and stale-plan protection.",
|
|
108
|
+
write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes."
|
|
109
109
|
};
|
|
110
110
|
function rewriteBacktickedToolNames(text, aliases = {}) {
|
|
111
111
|
return TOOL_NAMES.reduce((rewritten, toolName) => rewritten.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), text);
|
|
@@ -198,22 +198,22 @@ function tool(name, title, properties, required) {
|
|
|
198
198
|
return { name, title, description: TOOL_DESCRIPTIONS[name], inputSchema: { type: "object", properties, required, additionalProperties: false } };
|
|
199
199
|
}
|
|
200
200
|
var TOOL_CONTRACTS = [
|
|
201
|
-
tool("
|
|
202
|
-
tool("
|
|
203
|
-
tool("
|
|
204
|
-
tool("
|
|
205
|
-
tool("
|
|
206
|
-
tool("
|
|
207
|
-
tool("
|
|
208
|
-
tool("
|
|
209
|
-
tool("
|
|
210
|
-
tool("
|
|
211
|
-
tool("
|
|
212
|
-
tool("
|
|
213
|
-
tool("
|
|
214
|
-
tool("
|
|
215
|
-
tool("
|
|
216
|
-
tool("
|
|
201
|
+
tool("show", "Show content", { ...path, ...at, ...lang, ...stdin, ...noAnchors, end: integer(PARAM_DESCRIPTIONS.end), limit: integer(PARAM_DESCRIPTIONS.limit) }, ["path"]),
|
|
202
|
+
tool("tree", "Show structure", { ...path, ...at, ...lang, ...stdin, depth: integer(PARAM_DESCRIPTIONS.depth, 0), kind: string(PARAM_DESCRIPTIONS.kind), outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline } }, ["path"]),
|
|
203
|
+
tool("meta", "Show metadata", { ...path, ...lang, ...stdin }, ["path"]),
|
|
204
|
+
tool("assets", "Show assets", { ...path, at: string(PARAM_DESCRIPTIONS.assetAt), ...stdin }, ["path"]),
|
|
205
|
+
tool("syntax", "Check syntax", { ...path, ...lang, ...stdin }, ["path"]),
|
|
206
|
+
tool("check", "Check rules", { path: string(PARAM_DESCRIPTIONS.searchPath), ...gitScopeSchema, rules: string(PARAM_DESCRIPTIONS.rules) }, ["rules"]),
|
|
207
|
+
tool("caption", "Generate caption", { ...path, ...at, ...stdin, ...level, include: { type: "array", items: { type: "string", enum: ["text", "objects"] }, minItems: 1, uniqueItems: true, description: PARAM_DESCRIPTIONS.include } }, ["path"]),
|
|
208
|
+
tool("objects", "Detect objects", { ...path, ...at, ...stdin, ...level }, ["path"]),
|
|
209
|
+
tool("ocr", "Recognize text", { ...path, ...at, ...stdin, ...level }, ["path"]),
|
|
210
|
+
tool("search", "Search AST nodes", { ...path, ...lang, ...stdin, ...limit, ...gitScopeSchema, ...noAnchors, pattern: string(PARAM_DESCRIPTIONS.pattern), context: string(PARAM_DESCRIPTIONS.patternContext), selector: string(PARAM_DESCRIPTIONS.selector), rule: string(PARAM_DESCRIPTIONS.rule) }, []),
|
|
211
|
+
tool("grep", "Search text", { path: string(PARAM_DESCRIPTIONS.searchPath), limit: { ...integer(PARAM_DESCRIPTIONS.grepLimit), default: 100 }, ...gitScopeSchema, pattern: string(PARAM_DESCRIPTIONS.grepPattern), glob: string(PARAM_DESCRIPTIONS.glob), ignoreCase: { type: "boolean", description: PARAM_DESCRIPTIONS.ignoreCase }, literal: { type: "boolean", description: PARAM_DESCRIPTIONS.literal }, context: integer(PARAM_DESCRIPTIONS.context, 0), summary: { type: "boolean", description: PARAM_DESCRIPTIONS.summary }, scope: { type: "string", enum: ["symbol"], description: PARAM_DESCRIPTIONS.grepScope }, scopeContext: integer(PARAM_DESCRIPTIONS.scopeContext, 0) }, ["pattern"]),
|
|
212
|
+
tool("def", "Find definitions", { ...path, ...lang, ...gitScopeSchema, ...noAnchors, name: string(PARAM_DESCRIPTIONS.name) }, ["name"]),
|
|
213
|
+
tool("refs", "Find references", { ...path, ...at, ...lang, ...gitScopeSchema, ...noAnchors, name: string(PARAM_DESCRIPTIONS.refsName) }, []),
|
|
214
|
+
tool("edit", "Hash-verified edit", { ...path, ...lang, ...gitScopeSchema, ...apply, edits: { type: "array", minItems: 1, items: editItemSchema, description: PARAM_DESCRIPTIONS.edits }, postEditVerify: { type: "boolean", description: PARAM_DESCRIPTIONS.postEditVerify } }, ["path", "edits"]),
|
|
215
|
+
tool("rename", "Rename symbol", { ...path, ...at, ...lang, ...gitScopeSchema, ...apply, to: string(PARAM_DESCRIPTIONS.to), workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace } }, ["path", "at", "to"]),
|
|
216
|
+
tool("write", "Write file", { ...path, content: string(PARAM_DESCRIPTIONS.content) }, ["path", "content"])
|
|
217
217
|
];
|
|
218
218
|
function getToolContract(name) {
|
|
219
219
|
const contract = TOOL_CONTRACTS.find((entry) => entry.name === name);
|
|
@@ -224,70 +224,70 @@ function getToolContract(name) {
|
|
|
224
224
|
// src/policy.ts
|
|
225
225
|
var REPLACEABLE_TOOLS = [
|
|
226
226
|
{
|
|
227
|
-
tool: "
|
|
227
|
+
tool: "show",
|
|
228
228
|
builtIn: "read",
|
|
229
229
|
benefit: "it returns LINE:HASH anchors for safe edits."
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
|
-
tool: "
|
|
232
|
+
tool: "edit",
|
|
233
233
|
builtIn: "edit",
|
|
234
234
|
benefit: "it verifies fresh LINE:HASH anchors."
|
|
235
235
|
},
|
|
236
236
|
{
|
|
237
|
-
tool: "
|
|
237
|
+
tool: "grep",
|
|
238
238
|
builtIn: "grep",
|
|
239
|
-
benefit: "it returns LINE:HASH anchors."
|
|
239
|
+
benefit: "it runs native regex/literal matching and returns source LINE:HASH anchors or document locations."
|
|
240
240
|
},
|
|
241
241
|
{
|
|
242
|
-
tool: "
|
|
242
|
+
tool: "write",
|
|
243
243
|
builtIn: "write",
|
|
244
244
|
benefit: "it returns LINE:HASH anchors."
|
|
245
245
|
}
|
|
246
246
|
];
|
|
247
247
|
var TOOL_COMPACT_GUIDELINES = {
|
|
248
|
-
|
|
249
|
-
"Use `
|
|
248
|
+
show: [
|
|
249
|
+
"Use `tree` for shallow overviews and `syntax` for diagnostics; use `def`, `refs`, or `search` for targeted symbol questions."
|
|
250
250
|
],
|
|
251
|
-
|
|
251
|
+
edit: [
|
|
252
252
|
"Prefer set_line, replace_lines, and insert_after; use replace only when anchors are impractical."
|
|
253
253
|
],
|
|
254
|
-
|
|
255
|
-
"Use `
|
|
254
|
+
grep: [
|
|
255
|
+
"Use `grep` with summary first for broad searches, then narrow by path, glob, or pattern."
|
|
256
256
|
],
|
|
257
|
-
|
|
258
|
-
"Use anchored edits rather than `
|
|
257
|
+
write: [
|
|
258
|
+
"Use anchored edits rather than `write` for small changes or appends."
|
|
259
259
|
],
|
|
260
|
-
|
|
261
|
-
"Use `
|
|
260
|
+
search: [
|
|
261
|
+
"Use `search` for AST patterns; use `grep` for plain text."
|
|
262
262
|
],
|
|
263
|
-
|
|
264
|
-
"Use `
|
|
263
|
+
refs: [
|
|
264
|
+
"Use `refs` before changing a symbol; use at with line:N:C to follow one binding."
|
|
265
265
|
]
|
|
266
266
|
};
|
|
267
267
|
var BUILTIN_FALLBACKS = {
|
|
268
|
-
read: "
|
|
269
|
-
edit: "
|
|
270
|
-
write: "
|
|
271
|
-
grep: "
|
|
272
|
-
apply_patch: "
|
|
268
|
+
read: "show",
|
|
269
|
+
edit: "edit",
|
|
270
|
+
write: "write",
|
|
271
|
+
grep: "grep",
|
|
272
|
+
apply_patch: "edit"
|
|
273
273
|
};
|
|
274
274
|
var PREFERENCE_LEADS = {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
275
|
+
show: "Preferred for reading source with anchors.",
|
|
276
|
+
edit: "Preferred for editing existing text files with fresh LINE:HASH anchors.",
|
|
277
|
+
write: "Preferred for creating or replacing a complete text file; returns LINE:HASH anchors.",
|
|
278
|
+
grep: "Preferred native text/regex search; returns edit-ready source LINE:HASH anchors and document node-local locations.",
|
|
279
|
+
search: "Preferred for AST or structural code search with ast-grep-style patterns.",
|
|
280
|
+
def: "Preferred for go-to-definition or declaration lookup by name.",
|
|
281
|
+
refs: "Preferred for finding identifier-name matches or binding-specific references before `rename` or delete.",
|
|
282
|
+
rename: "Preferred for renaming a symbol with binding-aware, verified edits.",
|
|
283
|
+
assets: "Preferred for materializing selected document assets with usable cached paths."
|
|
284
284
|
};
|
|
285
285
|
var FALLBACK_TEMPLATES = {
|
|
286
|
-
read: "Prefer `
|
|
287
|
-
edit: "Prefer `
|
|
288
|
-
write: "Prefer `
|
|
289
|
-
grep: "Prefer `
|
|
290
|
-
apply_patch: "Prefer `
|
|
286
|
+
read: "Prefer `show` with fresh LINE:HASH anchors.",
|
|
287
|
+
edit: "Prefer `edit` with fresh LINE:HASH anchors.",
|
|
288
|
+
write: "Prefer `write` so the result returns LINE:HASH anchors.",
|
|
289
|
+
grep: "Prefer `grep` for native text/regex search with source anchors and document locations.",
|
|
290
|
+
apply_patch: "Prefer `edit` for existing text files."
|
|
291
291
|
};
|
|
292
292
|
function resolveToolName(name, aliases = {}) {
|
|
293
293
|
return aliases[name] ?? name;
|
|
@@ -306,21 +306,21 @@ function renderFallbackToolDescription(builtIn, aliases = {}) {
|
|
|
306
306
|
}
|
|
307
307
|
function renderToolRoutingPolicy(options = {}) {
|
|
308
308
|
const aliases = options.aliases ?? {};
|
|
309
|
-
const
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
const
|
|
309
|
+
const show = resolveToolName("show", aliases);
|
|
310
|
+
const edit = resolveToolName("edit", aliases);
|
|
311
|
+
const write = resolveToolName("write", aliases);
|
|
312
|
+
const grep = resolveToolName("grep", aliases);
|
|
313
|
+
const search = resolveToolName("search", aliases);
|
|
314
|
+
const def = resolveToolName("def", aliases);
|
|
315
|
+
const refs = resolveToolName("refs", aliases);
|
|
316
|
+
const rename = resolveToolName("rename", aliases);
|
|
317
|
+
const assets = resolveToolName("assets", aliases);
|
|
318
318
|
const title = options.title ?? "ReadSeek tool policy:";
|
|
319
319
|
const bullets = [];
|
|
320
320
|
if (options.preferOverBuiltIns) {
|
|
321
321
|
bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
|
|
322
322
|
}
|
|
323
|
-
bullets.push(`- Read with ${
|
|
323
|
+
bullets.push(`- Read with ${show} first; ${edit} needs fresh LINE:HASH anchors.`, `- Use ${grep} for text/regex, ${search} for AST patterns, ${def}/${refs} for targeted symbols, and shallow ${resolveToolName("tree", aliases)} structures only for file overviews.`, `- Use ${edit} for existing files, ${write} for whole-file creation or replacement, and ${rename} for symbol renames.`, `- Use ${resolveToolName("tree", aliases)} for document structure; run ${resolveToolName("syntax", aliases)} after source edits for a quick syntax check.`, `- Use ${assets} to materialize selected document assets and read their returned paths.`);
|
|
324
324
|
if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
|
|
325
325
|
bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
|
|
326
326
|
}
|
|
@@ -367,6 +367,59 @@ function validateReferencesSelection(input) {
|
|
|
367
367
|
return { ok: false, error: "exactly one of name or at is required" };
|
|
368
368
|
return { ok: true };
|
|
369
369
|
}
|
|
370
|
+
function validateGrepParams(input) {
|
|
371
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
372
|
+
return { ok: false, error: "grep params must be an object" };
|
|
373
|
+
}
|
|
374
|
+
const params = input;
|
|
375
|
+
const allowed = [
|
|
376
|
+
"pattern",
|
|
377
|
+
"path",
|
|
378
|
+
"glob",
|
|
379
|
+
"ignoreCase",
|
|
380
|
+
"literal",
|
|
381
|
+
"context",
|
|
382
|
+
"limit",
|
|
383
|
+
"summary",
|
|
384
|
+
"scope",
|
|
385
|
+
"scopeContext",
|
|
386
|
+
"gitScope",
|
|
387
|
+
"signal",
|
|
388
|
+
"stdin",
|
|
389
|
+
"timeoutMs",
|
|
390
|
+
"cancelable"
|
|
391
|
+
];
|
|
392
|
+
if (Object.keys(params).some((key) => !allowed.includes(key)))
|
|
393
|
+
return { ok: false, error: "unknown grep parameter" };
|
|
394
|
+
if (typeof params.pattern !== "string")
|
|
395
|
+
return { ok: false, error: "grep requires a pattern string" };
|
|
396
|
+
if (params.literal === true && params.pattern.length === 0)
|
|
397
|
+
return { ok: false, error: "literal pattern must not be empty" };
|
|
398
|
+
for (const key of ["path", "glob"]) {
|
|
399
|
+
if (params[key] !== undefined && (typeof params[key] !== "string" || params[key].length === 0)) {
|
|
400
|
+
return { ok: false, error: `${key} must be a non-empty string` };
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
for (const key of ["ignoreCase", "literal", "summary"]) {
|
|
404
|
+
if (params[key] !== undefined && typeof params[key] !== "boolean")
|
|
405
|
+
return { ok: false, error: `${key} must be a boolean` };
|
|
406
|
+
}
|
|
407
|
+
for (const key of ["context", "limit", "scopeContext"]) {
|
|
408
|
+
const value = params[key];
|
|
409
|
+
const minimum = key === "limit" ? 1 : 0;
|
|
410
|
+
if (value !== undefined && (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum)) {
|
|
411
|
+
return { ok: false, error: `${key} must be a ${minimum === 1 ? "positive" : "non-negative"} integer` };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (params.scope !== undefined && params.scope !== "symbol")
|
|
415
|
+
return { ok: false, error: "scope must be symbol" };
|
|
416
|
+
if (params.scopeContext !== undefined && params.scope !== "symbol")
|
|
417
|
+
return { ok: false, error: "scopeContext requires scope" };
|
|
418
|
+
if (params.gitScope !== undefined && !GIT_SCOPES.includes(params.gitScope)) {
|
|
419
|
+
return { ok: false, error: "invalid gitScope" };
|
|
420
|
+
}
|
|
421
|
+
return { ok: true };
|
|
422
|
+
}
|
|
370
423
|
var VARIANT_FIELDS = {
|
|
371
424
|
set_line: ["anchor", "new_text"],
|
|
372
425
|
replace_lines: ["start_anchor", "end_anchor", "new_text"],
|
|
@@ -530,50 +583,111 @@ var SELECTOR_KINDS = ["line", "hash", "symbol", "page", "node", "asset"];
|
|
|
530
583
|
function isSelectorKind(value) {
|
|
531
584
|
return SELECTOR_KINDS.includes(value);
|
|
532
585
|
}
|
|
586
|
+
function parsePositiveSelectorInt(raw, name) {
|
|
587
|
+
if (!/^-?\d+$/.test(raw))
|
|
588
|
+
throw new Error(`invalid ${name} \`${raw}\``);
|
|
589
|
+
const parsed = Number(raw);
|
|
590
|
+
if (!Number.isSafeInteger(parsed))
|
|
591
|
+
throw new Error(`invalid ${name} \`${raw}\``);
|
|
592
|
+
if (parsed < 1)
|
|
593
|
+
throw new Error(`${name} must be greater than zero`);
|
|
594
|
+
return parsed;
|
|
595
|
+
}
|
|
596
|
+
function parseLineSelector(value, explicitKind) {
|
|
597
|
+
const parts = value.split(":");
|
|
598
|
+
if (parts.length > 2)
|
|
599
|
+
throw new Error("selector has too many components");
|
|
600
|
+
const head = parts[0];
|
|
601
|
+
if (parts.length === 2) {
|
|
602
|
+
if (head.slice(1).includes("-"))
|
|
603
|
+
throw new Error("line range cannot include a column");
|
|
604
|
+
const line = parsePositiveSelectorInt(head, "line");
|
|
605
|
+
const column = parsePositiveSelectorInt(parts[1], "column");
|
|
606
|
+
return { kind: "line", value: String(line), column, explicitKind };
|
|
607
|
+
}
|
|
608
|
+
const dash = head.indexOf("-", 1);
|
|
609
|
+
if (dash > 0) {
|
|
610
|
+
const start = parsePositiveSelectorInt(head.slice(0, dash), "line");
|
|
611
|
+
const endLine = parsePositiveSelectorInt(head.slice(dash + 1), "end line");
|
|
612
|
+
if (endLine < start)
|
|
613
|
+
throw new Error(`end line ${endLine} precedes start line ${start}`);
|
|
614
|
+
return { kind: "line", value: String(start), endLine, explicitKind };
|
|
615
|
+
}
|
|
616
|
+
const line = parsePositiveSelectorInt(head, "line");
|
|
617
|
+
return { kind: "line", value: String(line), explicitKind };
|
|
618
|
+
}
|
|
619
|
+
function parseHashSelector(value, explicitKind) {
|
|
620
|
+
const parts = value.split(":");
|
|
621
|
+
if (parts.length > 2)
|
|
622
|
+
throw new Error("selector has too many components");
|
|
623
|
+
const head = parts[0];
|
|
624
|
+
if (!/^[0-9a-fA-F]{6}$/.test(head))
|
|
625
|
+
throw new Error(`invalid line hash \`${head}\``);
|
|
626
|
+
if (parts.length === 2) {
|
|
627
|
+
const column = parsePositiveSelectorInt(parts[1], "column");
|
|
628
|
+
return { kind: "hash", value: head, column, explicitKind };
|
|
629
|
+
}
|
|
630
|
+
return { kind: "hash", value: head, explicitKind };
|
|
631
|
+
}
|
|
632
|
+
function isDocumentSelector(parsed) {
|
|
633
|
+
return ["page", "node", "asset"].includes(parsed.kind) || parsed.kind === "line" && !parsed.explicitKind && parsed.column === undefined && parsed.endLine === undefined;
|
|
634
|
+
}
|
|
635
|
+
function isBindingSelector(parsed) {
|
|
636
|
+
if (parsed.endLine !== undefined)
|
|
637
|
+
return false;
|
|
638
|
+
if (parsed.kind === "line" || parsed.kind === "hash")
|
|
639
|
+
return true;
|
|
640
|
+
return parsed.kind === "symbol" && !parsed.explicitKind && /^[0-9a-fA-F]{6}$/.test(parsed.value);
|
|
641
|
+
}
|
|
533
642
|
function parseSelector(text) {
|
|
643
|
+
if (text.length === 0)
|
|
644
|
+
throw new Error("selector must not be empty");
|
|
534
645
|
const separator = text.indexOf(":");
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
646
|
+
const hasSingleColon = separator >= 0 && text[separator + 1] !== ":";
|
|
647
|
+
if (hasSingleColon) {
|
|
648
|
+
const kind = text.slice(0, separator);
|
|
649
|
+
const value = text.slice(separator + 1);
|
|
650
|
+
if (isSelectorKind(kind)) {
|
|
651
|
+
if (value.length === 0)
|
|
652
|
+
throw new Error(`${kind} selector must not be empty`);
|
|
653
|
+
if (kind === "line")
|
|
654
|
+
return parseLineSelector(value, true);
|
|
655
|
+
if (kind === "hash")
|
|
656
|
+
return parseHashSelector(value, true);
|
|
657
|
+
if (kind === "page") {
|
|
658
|
+
if (value.includes(":"))
|
|
659
|
+
throw new Error("page selector has too many components");
|
|
660
|
+
parsePositiveSelectorInt(value, "page");
|
|
661
|
+
}
|
|
662
|
+
return { kind, value, explicitKind: true };
|
|
663
|
+
}
|
|
664
|
+
if (/^\d+$/.test(kind))
|
|
665
|
+
return parseLineSelector(text, false);
|
|
666
|
+
if (/^[0-9a-fA-F]{6}$/.test(kind) && /^\d+$/.test(value))
|
|
667
|
+
return parseHashSelector(text, false);
|
|
543
668
|
throw new Error(`unknown selector kind \`${kind}\`; expected line, hash, symbol, page, node, or asset`);
|
|
544
669
|
}
|
|
545
|
-
if (
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
throw new Error("selector has too many components");
|
|
549
|
-
if (parts.length === 2) {
|
|
550
|
-
const column = Number(parts[1]);
|
|
551
|
-
if (!Number.isSafeInteger(column))
|
|
552
|
-
throw new Error(`invalid column \`${parts[1]}\``);
|
|
553
|
-
if (column < 1)
|
|
554
|
-
throw new Error("column must be greater than zero");
|
|
555
|
-
return { kind, value: parts[0], column };
|
|
670
|
+
if (/^[0-9-]/.test(text)) {
|
|
671
|
+
if (/^[0-9a-fA-F]{6}$/.test(text) && /[a-fA-F]/.test(text)) {
|
|
672
|
+
return parseHashSelector(text, false);
|
|
556
673
|
}
|
|
674
|
+
return parseLineSelector(text, false);
|
|
557
675
|
}
|
|
558
|
-
if (
|
|
559
|
-
|
|
560
|
-
throw new Error("page selector has too many components");
|
|
561
|
-
const page = Number(value);
|
|
562
|
-
if (!Number.isSafeInteger(page))
|
|
563
|
-
throw new Error(`invalid page \`${value}\``);
|
|
564
|
-
if (page < 1)
|
|
565
|
-
throw new Error("page must be greater than zero");
|
|
676
|
+
if (/^[0-9a-fA-F]{6}$/.test(text) && /\d/.test(text)) {
|
|
677
|
+
return parseHashSelector(text, false);
|
|
566
678
|
}
|
|
567
|
-
|
|
679
|
+
if (/\s/.test(text))
|
|
680
|
+
throw new Error(`invalid selector \`${text}\``);
|
|
681
|
+
return { kind: "symbol", value: text, explicitKind: false };
|
|
568
682
|
}
|
|
569
|
-
function start(
|
|
683
|
+
function start(command, input) {
|
|
570
684
|
if (!input.path)
|
|
571
|
-
throw new Error(`${
|
|
685
|
+
throw new Error(`${command} requires a path`);
|
|
572
686
|
if (input.stdinName !== undefined && input.path !== "-")
|
|
573
687
|
throw new Error("stdinName requires '-' as the target");
|
|
574
688
|
const args = [];
|
|
575
689
|
pushOptionalString(args, "--readseek-dir", input.readseekDir);
|
|
576
|
-
args.push(
|
|
690
|
+
args.push(command, input.path);
|
|
577
691
|
pushOptionalString(args, "--stdin-name", input.stdinName);
|
|
578
692
|
return args;
|
|
579
693
|
}
|
|
@@ -599,12 +713,12 @@ function anchors(args, input) {
|
|
|
599
713
|
if (input.noAnchors)
|
|
600
714
|
args.push("--no-anchors");
|
|
601
715
|
}
|
|
602
|
-
function
|
|
716
|
+
function buildShowArgs(input) {
|
|
603
717
|
if (input.end !== undefined)
|
|
604
718
|
validatePositiveInteger(input.end, "end");
|
|
605
719
|
if (input.end !== undefined && input.limit !== undefined)
|
|
606
720
|
throw new Error("cannot combine end with limit");
|
|
607
|
-
const args = start("show",
|
|
721
|
+
const args = start("show", input);
|
|
608
722
|
at2(args, input.at);
|
|
609
723
|
pushOptionalNumber(args, "--end", input.end);
|
|
610
724
|
limit2(args, input.limit);
|
|
@@ -612,10 +726,10 @@ function buildShowContentArgs(input) {
|
|
|
612
726
|
anchors(args, input);
|
|
613
727
|
return finish(args);
|
|
614
728
|
}
|
|
615
|
-
function
|
|
729
|
+
function buildTreeArgs(input) {
|
|
616
730
|
if (input.depth !== undefined && (!Number.isSafeInteger(input.depth) || input.depth < 0))
|
|
617
731
|
throw new Error("depth must be a non-negative integer");
|
|
618
|
-
const args = start("
|
|
732
|
+
const args = start("tree", input);
|
|
619
733
|
at2(args, input.at);
|
|
620
734
|
pushOptionalNumber(args, "--depth", input.depth);
|
|
621
735
|
pushOptionalString(args, "--kind", input.kind);
|
|
@@ -624,35 +738,35 @@ function buildShowStructureArgs(input) {
|
|
|
624
738
|
args.push("--outline");
|
|
625
739
|
return finish(args);
|
|
626
740
|
}
|
|
627
|
-
function
|
|
628
|
-
const args = start("
|
|
741
|
+
function buildMetaArgs(input) {
|
|
742
|
+
const args = start("meta", input);
|
|
629
743
|
language(args, input);
|
|
630
744
|
return finish(args);
|
|
631
745
|
}
|
|
632
|
-
function
|
|
633
|
-
if (input.at !== undefined && !
|
|
746
|
+
function buildAssetsArgs(input) {
|
|
747
|
+
if (input.at !== undefined && !isDocumentSelector(parseSelector(input.at)))
|
|
634
748
|
throw new Error("assets requires a page, node, or asset selector");
|
|
635
|
-
const args = start("
|
|
749
|
+
const args = start("assets", input);
|
|
636
750
|
at2(args, input.at);
|
|
637
751
|
return finish(args);
|
|
638
752
|
}
|
|
639
|
-
function
|
|
640
|
-
const args = start("
|
|
753
|
+
function buildSyntaxArgs(input) {
|
|
754
|
+
const args = start("syntax", input);
|
|
641
755
|
language(args, input);
|
|
642
756
|
return finish(args);
|
|
643
757
|
}
|
|
644
|
-
function visionArgs(
|
|
758
|
+
function visionArgs(command, input) {
|
|
645
759
|
if (input.level !== undefined && !VISION_LEVELS.includes(input.level))
|
|
646
760
|
throw new Error("invalid vision level");
|
|
647
|
-
if (input.at !== undefined && !
|
|
761
|
+
if (input.at !== undefined && !isDocumentSelector(parseSelector(input.at)))
|
|
648
762
|
throw new Error("vision requires a document selector");
|
|
649
|
-
const args = start(
|
|
763
|
+
const args = start(command, input);
|
|
650
764
|
at2(args, input.at);
|
|
651
765
|
pushOptionalString(args, "--level", input.level);
|
|
652
766
|
return args;
|
|
653
767
|
}
|
|
654
|
-
function
|
|
655
|
-
const args = visionArgs("
|
|
768
|
+
function buildCaptionArgs(input) {
|
|
769
|
+
const args = visionArgs("caption", input);
|
|
656
770
|
if (input.include !== undefined) {
|
|
657
771
|
if (input.include.length === 0 || input.include.some((task) => task !== "text" && task !== "objects"))
|
|
658
772
|
throw new Error("include requires text or objects");
|
|
@@ -660,13 +774,13 @@ function buildGenerateCaptionArgs(input) {
|
|
|
660
774
|
}
|
|
661
775
|
return finish(args);
|
|
662
776
|
}
|
|
663
|
-
function
|
|
664
|
-
return finish(visionArgs("
|
|
777
|
+
function buildObjectsArgs(input) {
|
|
778
|
+
return finish(visionArgs("objects", input));
|
|
665
779
|
}
|
|
666
|
-
function
|
|
667
|
-
return finish(visionArgs("
|
|
780
|
+
function buildOcrArgs(input) {
|
|
781
|
+
return finish(visionArgs("ocr", input));
|
|
668
782
|
}
|
|
669
|
-
function
|
|
783
|
+
function buildSearchArgs(input) {
|
|
670
784
|
const hasPattern = Boolean(input.pattern);
|
|
671
785
|
const hasRule = Boolean(input.rule);
|
|
672
786
|
const hasContext = Boolean(input.context);
|
|
@@ -674,7 +788,7 @@ function buildFindNodesArgs(input) {
|
|
|
674
788
|
if (hasPattern || hasContext || input.selector)
|
|
675
789
|
throw new Error("rule cannot be combined with pattern, context, or selector");
|
|
676
790
|
} else if (!hasPattern && !hasContext) {
|
|
677
|
-
throw new Error("
|
|
791
|
+
throw new Error("search requires pattern, context, or rule");
|
|
678
792
|
}
|
|
679
793
|
if (hasContext && !input.selector) {
|
|
680
794
|
throw new Error("context requires selector");
|
|
@@ -682,7 +796,7 @@ function buildFindNodesArgs(input) {
|
|
|
682
796
|
if (hasPattern && hasContext) {
|
|
683
797
|
throw new Error("pattern and context cannot be combined");
|
|
684
798
|
}
|
|
685
|
-
const args = start("
|
|
799
|
+
const args = start("search", input);
|
|
686
800
|
pushOptionalString(args, "--pattern", input.pattern);
|
|
687
801
|
pushOptionalString(args, "--context", input.context);
|
|
688
802
|
pushOptionalString(args, "--selector", input.selector);
|
|
@@ -693,28 +807,46 @@ function buildFindNodesArgs(input) {
|
|
|
693
807
|
anchors(args, input);
|
|
694
808
|
return finish(args);
|
|
695
809
|
}
|
|
696
|
-
function
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
810
|
+
function buildGrepArgs(input) {
|
|
811
|
+
const validated = validateGrepParams(input);
|
|
812
|
+
if (!validated.ok)
|
|
813
|
+
throw new Error(validated.error);
|
|
814
|
+
const args = ["grep", "--kdl", `--pattern=${input.pattern}`];
|
|
815
|
+
const needsPathTerminator = input.path !== undefined && input.path.startsWith("-") && input.path !== "-";
|
|
816
|
+
if (input.path !== undefined && !needsPathTerminator)
|
|
817
|
+
args.push(input.path);
|
|
818
|
+
if (input.literal)
|
|
819
|
+
args.push("--literal");
|
|
820
|
+
if (input.ignoreCase)
|
|
821
|
+
args.push("--ignore-case");
|
|
822
|
+
if (input.glob !== undefined)
|
|
823
|
+
args.push(`--glob=${input.glob}`);
|
|
824
|
+
pushOptionalNumber(args, "--context", input.context);
|
|
825
|
+
limit2(args, input.limit ?? 100);
|
|
826
|
+
if (input.summary)
|
|
827
|
+
args.push("--summary");
|
|
828
|
+
pushOptionalString(args, "--scope", input.scope);
|
|
829
|
+
pushOptionalNumber(args, "--scope-context", input.scopeContext);
|
|
700
830
|
appendGitScopeArgs(args, input);
|
|
701
|
-
|
|
831
|
+
if (input.path !== undefined && needsPathTerminator)
|
|
832
|
+
args.push("--", input.path);
|
|
833
|
+
return args;
|
|
702
834
|
}
|
|
703
|
-
function
|
|
704
|
-
const args = start("
|
|
835
|
+
function buildDefArgs(input) {
|
|
836
|
+
const args = start("def", input);
|
|
705
837
|
args.push("--name", input.name);
|
|
706
838
|
language(args, input);
|
|
707
839
|
appendGitScopeArgs(args, input);
|
|
708
840
|
anchors(args, input);
|
|
709
841
|
return finish(args);
|
|
710
842
|
}
|
|
711
|
-
function
|
|
843
|
+
function buildRefsArgs(input) {
|
|
712
844
|
const validated = validateReferencesSelection(input);
|
|
713
845
|
if (!validated.ok)
|
|
714
846
|
throw new Error(validated.error);
|
|
715
|
-
if (input.at !== undefined && !
|
|
847
|
+
if (input.at !== undefined && !isBindingSelector(parseSelector(input.at)))
|
|
716
848
|
throw new Error("references requires a line or hash selector");
|
|
717
|
-
const args = start("
|
|
849
|
+
const args = start("refs", input);
|
|
718
850
|
pushOptionalString(args, "--name", input.name);
|
|
719
851
|
at2(args, input.at);
|
|
720
852
|
language(args, input);
|
|
@@ -722,14 +854,14 @@ function buildFindReferencesArgs(input) {
|
|
|
722
854
|
anchors(args, input);
|
|
723
855
|
return finish(args);
|
|
724
856
|
}
|
|
725
|
-
function
|
|
726
|
-
const args = start("check",
|
|
857
|
+
function buildCheckArgs(input) {
|
|
858
|
+
const args = start("check", input);
|
|
727
859
|
args.push("--rules", input.rules);
|
|
728
860
|
appendGitScopeArgs(args, input);
|
|
729
861
|
return finish(args);
|
|
730
862
|
}
|
|
731
|
-
function
|
|
732
|
-
const args = start("edit",
|
|
863
|
+
function buildEditArgs(input) {
|
|
864
|
+
const args = start("edit", input);
|
|
733
865
|
appendApplyAndPlanHash(args, input);
|
|
734
866
|
language(args, input);
|
|
735
867
|
appendGitScopeArgs(args, input);
|
|
@@ -737,10 +869,10 @@ function buildEditTextArgs(input) {
|
|
|
737
869
|
args.push("--edits", "-");
|
|
738
870
|
return finish(args);
|
|
739
871
|
}
|
|
740
|
-
function
|
|
741
|
-
if (!
|
|
872
|
+
function buildRenameArgs(input) {
|
|
873
|
+
if (!isBindingSelector(parseSelector(input.at)))
|
|
742
874
|
throw new Error("rename requires a line or hash selector");
|
|
743
|
-
const args = start("rename",
|
|
875
|
+
const args = start("rename", input);
|
|
744
876
|
at2(args, input.at);
|
|
745
877
|
args.push("--to", input.to);
|
|
746
878
|
if (input.workspace !== undefined)
|
|
@@ -1130,25 +1262,41 @@ var DocumentOutput = Type.Object({
|
|
|
1130
1262
|
assets: Type.Array(DocumentAsset, { default: [] }),
|
|
1131
1263
|
vision: Type.Array(DocumentVision, { default: [] })
|
|
1132
1264
|
});
|
|
1133
|
-
var
|
|
1134
|
-
|
|
1135
|
-
|
|
1265
|
+
var GrepCount = Type.Integer({ minimum: 0, maximum: Number.MAX_SAFE_INTEGER });
|
|
1266
|
+
var GrepPosition = Type.Integer({ minimum: 1, maximum: Number.MAX_SAFE_INTEGER });
|
|
1267
|
+
var GrepAnchor = Type.String({ pattern: "^[1-9][0-9]*:[0-9a-f]{6}$" });
|
|
1268
|
+
var GrepLineSchema = Type.Object({
|
|
1269
|
+
line: GrepPosition,
|
|
1270
|
+
hash: Type.String({ pattern: "^[0-9a-f]{6}$" }),
|
|
1136
1271
|
text: Str,
|
|
1137
|
-
|
|
1138
|
-
end_line: Int,
|
|
1139
|
-
start_byte: Int,
|
|
1140
|
-
end_byte: Int,
|
|
1141
|
-
hashlines: Hashlines
|
|
1272
|
+
kind: Type.Union([Type.Literal("match"), Type.Literal("context")])
|
|
1142
1273
|
});
|
|
1143
|
-
var
|
|
1144
|
-
var
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1274
|
+
var GrepHitSchema = Type.Object({ line: GrepPosition, column: GrepPosition, start_byte: GrepCount, end_byte: GrepCount, text: Str, anchor: GrepAnchor });
|
|
1275
|
+
var GrepSymbolSchema = Type.Object({ name: Str, kind: Str, start_line: GrepPosition, end_line: GrepPosition, lines: Type.Array(GrepLineSchema) });
|
|
1276
|
+
var GrepSourceResultSchema = Type.Object({
|
|
1277
|
+
file: Str,
|
|
1278
|
+
file_hash: Str,
|
|
1279
|
+
language: Str,
|
|
1280
|
+
total_matches: GrepCount,
|
|
1281
|
+
returned_matches: GrepCount,
|
|
1282
|
+
hits: Type.Array(GrepHitSchema),
|
|
1283
|
+
lines: Type.Array(GrepLineSchema),
|
|
1284
|
+
symbols: Type.Array(GrepSymbolSchema)
|
|
1285
|
+
});
|
|
1286
|
+
var GrepDocumentHitSchema = Type.Object({ node_id: Str, kind: Str, snippet: Str, line: GrepPosition, page: Type.Optional(GrepPosition) });
|
|
1287
|
+
var GrepDocumentResultSchema = Type.Object({ file: Str, content_hash: Str, total_matches: GrepCount, returned_matches: GrepCount, hits: Type.Array(GrepDocumentHitSchema) });
|
|
1288
|
+
var GrepOutputSchema = Type.Object({
|
|
1289
|
+
pattern: Str,
|
|
1290
|
+
mode: Type.Union([Type.Literal("regex"), Type.Literal("literal")]),
|
|
1291
|
+
ignore_case: Bool,
|
|
1292
|
+
summary: Bool,
|
|
1293
|
+
total_matches: GrepCount,
|
|
1294
|
+
returned_matches: GrepCount,
|
|
1149
1295
|
truncated: Bool,
|
|
1150
|
-
|
|
1151
|
-
|
|
1296
|
+
count_unit: Type.Literal("lines"),
|
|
1297
|
+
scope: Type.Optional(Type.Literal("symbol")),
|
|
1298
|
+
results: Type.Array(GrepSourceResultSchema),
|
|
1299
|
+
documents: Type.Array(GrepDocumentResultSchema)
|
|
1152
1300
|
});
|
|
1153
1301
|
var RuleFinding = Type.Object({
|
|
1154
1302
|
name: Str,
|
|
@@ -1193,8 +1341,35 @@ function parseStructureEnvelope(value) {
|
|
|
1193
1341
|
throw new Error("invalid readseek structure: missing structure");
|
|
1194
1342
|
return { ...output.metadata !== undefined ? { metadata: parseDetectOutput(output.metadata) } : {}, structure: parseStructureOutput(output.structure) };
|
|
1195
1343
|
}
|
|
1196
|
-
function
|
|
1197
|
-
|
|
1344
|
+
function parseGrepOutput(value) {
|
|
1345
|
+
const output = parse(GrepOutputSchema, value, "grep output");
|
|
1346
|
+
const invalid = (message) => {
|
|
1347
|
+
throw new Error(`invalid readseek grep output: ${message}`);
|
|
1348
|
+
};
|
|
1349
|
+
const groups = [...output.results, ...output.documents];
|
|
1350
|
+
if (output.returned_matches > output.total_matches || groups.reduce((sum, group) => sum + group.total_matches, 0) !== output.total_matches || groups.reduce((sum, group) => sum + group.returned_matches, 0) !== output.returned_matches)
|
|
1351
|
+
invalid("inconsistent counts");
|
|
1352
|
+
for (const group of groups) {
|
|
1353
|
+
if (group.total_matches === 0 || group.returned_matches > group.total_matches)
|
|
1354
|
+
invalid("inconsistent file counts");
|
|
1355
|
+
if (output.summary ? group.hits.length !== 0 : group.hits.length !== group.returned_matches)
|
|
1356
|
+
invalid("inconsistent hit count");
|
|
1357
|
+
}
|
|
1358
|
+
for (const result of output.results) {
|
|
1359
|
+
if (output.summary && (result.lines.length > 0 || result.symbols.length > 0))
|
|
1360
|
+
invalid("summary contains display entries");
|
|
1361
|
+
if (output.scope !== "symbol" && result.symbols.length > 0)
|
|
1362
|
+
invalid("symbols require symbol scope");
|
|
1363
|
+
for (const hit of result.hits) {
|
|
1364
|
+
if (hit.end_byte < hit.start_byte || Number(hit.anchor.split(":")[0]) !== hit.line)
|
|
1365
|
+
invalid("invalid hit span or anchor");
|
|
1366
|
+
}
|
|
1367
|
+
for (const symbol of result.symbols) {
|
|
1368
|
+
if (symbol.end_line < symbol.start_line || symbol.lines.some((line) => line.line < symbol.start_line || line.line > symbol.end_line))
|
|
1369
|
+
invalid("invalid symbol range");
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return output;
|
|
1198
1373
|
}
|
|
1199
1374
|
function parseRulesOutput(value) {
|
|
1200
1375
|
return parse(RulesOutput, value, "rules output");
|
|
@@ -1271,71 +1446,71 @@ function runOptions(options, vision = false) {
|
|
|
1271
1446
|
}
|
|
1272
1447
|
function createReadSeekClient(runner) {
|
|
1273
1448
|
return {
|
|
1274
|
-
async
|
|
1275
|
-
const value = await runner.kdl(
|
|
1449
|
+
async show(path, options = {}) {
|
|
1450
|
+
const value = await runner.kdl(buildShowArgs({ ...options, path }), runOptions(options));
|
|
1276
1451
|
return parseContentOutput(value);
|
|
1277
1452
|
},
|
|
1278
|
-
async
|
|
1279
|
-
const value = await runner.kdl(
|
|
1453
|
+
async tree(path, options = {}) {
|
|
1454
|
+
const value = await runner.kdl(buildTreeArgs({ ...options, path }), runOptions(options));
|
|
1280
1455
|
return parseStructureEnvelope(value);
|
|
1281
1456
|
},
|
|
1282
|
-
async
|
|
1283
|
-
const value = await runner.kdl(
|
|
1457
|
+
async meta(path, options = {}) {
|
|
1458
|
+
const value = await runner.kdl(buildMetaArgs({ ...options, path }), runOptions(options));
|
|
1284
1459
|
return parsePdfMetadata(value) ?? parseDetectOutput(value?.metadata);
|
|
1285
1460
|
},
|
|
1286
|
-
async
|
|
1287
|
-
const value = await runner.kdl(
|
|
1461
|
+
async assets(path, options = {}) {
|
|
1462
|
+
const value = await runner.kdl(buildAssetsArgs({ ...options, path }), runOptions(options));
|
|
1288
1463
|
return parseAssetsOutput(value);
|
|
1289
1464
|
},
|
|
1290
|
-
async
|
|
1291
|
-
const value = await runner.kdl(
|
|
1465
|
+
async syntax(path, options = {}) {
|
|
1466
|
+
const value = await runner.kdl(buildSyntaxArgs({ ...options, path }), runOptions(options));
|
|
1292
1467
|
return parseCheckOutput(value?.diagnostics ?? value);
|
|
1293
1468
|
},
|
|
1294
|
-
async
|
|
1295
|
-
const value = await runner.kdl(
|
|
1469
|
+
async check(path, options) {
|
|
1470
|
+
const value = await runner.kdl(buildCheckArgs({ ...options, path }), runOptions(options));
|
|
1296
1471
|
return parseRulesOutput(value);
|
|
1297
1472
|
},
|
|
1298
|
-
async
|
|
1299
|
-
const value = await runner.kdl(
|
|
1473
|
+
async caption(path, options = {}) {
|
|
1474
|
+
const value = await runner.kdl(buildCaptionArgs({ ...options, path }), runOptions(options, true));
|
|
1300
1475
|
return parseContentOutput(value);
|
|
1301
1476
|
},
|
|
1302
|
-
async
|
|
1303
|
-
const value = await runner.kdl(
|
|
1477
|
+
async objects(path, options = {}) {
|
|
1478
|
+
const value = await runner.kdl(buildObjectsArgs({ ...options, path }), runOptions(options, true));
|
|
1304
1479
|
return parseContentOutput(value);
|
|
1305
1480
|
},
|
|
1306
|
-
async
|
|
1307
|
-
const value = await runner.kdl(
|
|
1481
|
+
async ocr(path, options = {}) {
|
|
1482
|
+
const value = await runner.kdl(buildOcrArgs({ ...options, path }), runOptions(options, true));
|
|
1308
1483
|
return parseContentOutput(value);
|
|
1309
1484
|
},
|
|
1310
|
-
async
|
|
1311
|
-
const value = await runner.kdl(
|
|
1485
|
+
async search(path, options) {
|
|
1486
|
+
const value = await runner.kdl(buildSearchArgs({ ...options, path }), runOptions(options));
|
|
1312
1487
|
return parseSearchOutput(value);
|
|
1313
1488
|
},
|
|
1314
|
-
async
|
|
1315
|
-
const value = await runner.kdl(
|
|
1316
|
-
return
|
|
1489
|
+
async grep(params) {
|
|
1490
|
+
const value = await runner.kdl(buildGrepArgs(params), runOptions(params));
|
|
1491
|
+
return parseGrepOutput(value);
|
|
1317
1492
|
},
|
|
1318
|
-
async
|
|
1319
|
-
const value = await runner.kdl(
|
|
1493
|
+
async def(path, options) {
|
|
1494
|
+
const value = await runner.kdl(buildDefArgs({ ...options, path }), runOptions(options));
|
|
1320
1495
|
return parseDefOutput(value);
|
|
1321
1496
|
},
|
|
1322
|
-
async
|
|
1323
|
-
const value = await runner.kdl(
|
|
1497
|
+
async refs(path, options) {
|
|
1498
|
+
const value = await runner.kdl(buildRefsArgs({ ...options, path }), runOptions(options));
|
|
1324
1499
|
return parseRefsOutput(value);
|
|
1325
1500
|
},
|
|
1326
|
-
async
|
|
1501
|
+
async edit(path, edits, options = {}) {
|
|
1327
1502
|
const stdin = serializeEditPlan(edits);
|
|
1328
|
-
return parseEditOutput(await runner.kdl(
|
|
1503
|
+
return parseEditOutput(await runner.kdl(buildEditArgs({ ...options, path, requestFromStdin: true }), runOptions({ ...options, stdin })));
|
|
1329
1504
|
},
|
|
1330
|
-
async
|
|
1331
|
-
const value = await runner.kdl(
|
|
1505
|
+
async rename(path, options) {
|
|
1506
|
+
const value = await runner.kdl(buildRenameArgs({ ...options, path }), runOptions(options));
|
|
1332
1507
|
return parseRenameOutput(value);
|
|
1333
1508
|
}
|
|
1334
1509
|
};
|
|
1335
1510
|
}
|
|
1336
1511
|
// src/show-presentation.ts
|
|
1337
1512
|
function isShowTool(name) {
|
|
1338
|
-
return name === "
|
|
1513
|
+
return name === "show" || name === "tree" || name === "meta" || name === "assets";
|
|
1339
1514
|
}
|
|
1340
1515
|
function record(value) {
|
|
1341
1516
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -1450,7 +1625,7 @@ function presentShowResult(tool, value, options = {}) {
|
|
|
1450
1625
|
const path = inline(options.path || output.source || output.file || content.file || structure.file || metadata.file || cursor.file) || "(unknown path)";
|
|
1451
1626
|
const summary = [];
|
|
1452
1627
|
const result = { summary: "", body: "", notices: [], metadata: {} };
|
|
1453
|
-
if (tool === "
|
|
1628
|
+
if (tool === "meta") {
|
|
1454
1629
|
const data = Object.keys(metadata).length > 0 ? metadata : output;
|
|
1455
1630
|
if (text(data.format || data.language || data.type))
|
|
1456
1631
|
summary.push(inline(data.format || data.language || data.type));
|
|
@@ -1459,7 +1634,7 @@ function presentShowResult(tool, value, options = {}) {
|
|
|
1459
1634
|
summary.push(plural(result.metadata.pages, "page"));
|
|
1460
1635
|
}
|
|
1461
1636
|
result.body = fields(data) || "No metadata available.";
|
|
1462
|
-
} else if (tool === "
|
|
1637
|
+
} else if (tool === "assets") {
|
|
1463
1638
|
const assets = records(output.assets);
|
|
1464
1639
|
result.metadata.assets = assets.length;
|
|
1465
1640
|
summary.push(plural(assets.length, "asset"));
|
|
@@ -1473,7 +1648,7 @@ function presentShowResult(tool, value, options = {}) {
|
|
|
1473
1648
|
summary.push(plural(nodes.length, "node"));
|
|
1474
1649
|
if (result.metadata.pages !== undefined)
|
|
1475
1650
|
summary.push(plural(result.metadata.pages, "page"));
|
|
1476
|
-
const sections = [text(output.title) ? safe(text(output.title)) : "", documentNodes(nodes, tool === "
|
|
1651
|
+
const sections = [text(output.title) ? safe(text(output.title)) : "", documentNodes(nodes, tool === "show")];
|
|
1477
1652
|
if (assets.length > 0) {
|
|
1478
1653
|
result.metadata.assets = assets.length;
|
|
1479
1654
|
sections.push(`Assets
|
|
@@ -1482,7 +1657,7 @@ ${assetList(assets)}`);
|
|
|
1482
1657
|
result.body = sections.filter(Boolean).join(`
|
|
1483
1658
|
|
|
1484
1659
|
`) || "No content in this selection.";
|
|
1485
|
-
} else if (tool === "
|
|
1660
|
+
} else if (tool === "tree") {
|
|
1486
1661
|
const symbols = records(structure.symbols);
|
|
1487
1662
|
result.metadata.symbols = symbols.length;
|
|
1488
1663
|
summary.push(plural(symbols.length, "symbol"));
|
|
@@ -1536,7 +1711,7 @@ ${assetList(assets)}`);
|
|
|
1536
1711
|
const description = Object.keys(content).length > 0 ? content : output;
|
|
1537
1712
|
result.body = fields(description) || "No content in this selection.";
|
|
1538
1713
|
}
|
|
1539
|
-
if (tool === "
|
|
1714
|
+
if (tool === "tree")
|
|
1540
1715
|
result.notices.push("Structure is limited to the requested depth, kind, and outline. Use a deeper depth or focus with at to inspect further.");
|
|
1541
1716
|
if (text(output.warning))
|
|
1542
1717
|
result.notices.push(safe(text(output.warning)));
|
|
@@ -1548,6 +1723,31 @@ function formatShowResult(presentation) {
|
|
|
1548
1723
|
|
|
1549
1724
|
`);
|
|
1550
1725
|
}
|
|
1726
|
+
// src/grep-presentation.ts
|
|
1727
|
+
function renderGrepOutput(output) {
|
|
1728
|
+
const rendered = [`${output.returned_matches} of ${output.total_matches} matching lines${output.truncated ? " (truncated)" : ""}`];
|
|
1729
|
+
const displayLine = (line) => `${line.line}:${line.hash}|${line.kind === "match" ? ">>" : " "}${line.text}`;
|
|
1730
|
+
for (const result of output.results) {
|
|
1731
|
+
rendered.push(`${result.file}: ${result.total_matches} matching lines (${result.returned_matches} returned)`);
|
|
1732
|
+
if (output.summary)
|
|
1733
|
+
continue;
|
|
1734
|
+
rendered.push(...result.lines.map(displayLine));
|
|
1735
|
+
for (const symbol of result.symbols) {
|
|
1736
|
+
rendered.push(` ${symbol.kind} ${symbol.name} (${symbol.start_line}-${symbol.end_line})`);
|
|
1737
|
+
rendered.push(...symbol.lines.map(displayLine));
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
for (const document of output.documents) {
|
|
1741
|
+
rendered.push(`${document.file}: ${document.total_matches} matching lines (${document.returned_matches} returned)`);
|
|
1742
|
+
if (output.summary)
|
|
1743
|
+
continue;
|
|
1744
|
+
for (const hit of document.hits) {
|
|
1745
|
+
rendered.push(` node:${hit.node_id}${hit.page === undefined ? "" : ` page:${hit.page}`} line:${hit.line} (${hit.kind}): ${hit.snippet}`);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
return rendered.join(`
|
|
1749
|
+
`);
|
|
1750
|
+
}
|
|
1551
1751
|
// src/platform.ts
|
|
1552
1752
|
import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
|
|
1553
1753
|
import { createRequire } from "node:module";
|
|
@@ -1869,6 +2069,46 @@ function search(root) {
|
|
|
1869
2069
|
}))
|
|
1870
2070
|
};
|
|
1871
2071
|
}
|
|
2072
|
+
function grepChildren(node, allowed) {
|
|
2073
|
+
if (node.children.some((child) => !allowed.includes(child.name))) {
|
|
2074
|
+
throw new Error(`invalid readseek KDL grep: unexpected child of ${node.name}`);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
function grepLine(node) {
|
|
2078
|
+
grepChildren(node, []);
|
|
2079
|
+
const parsed = line(node);
|
|
2080
|
+
if (requireProperty(node, "number") !== parsed.line)
|
|
2081
|
+
throw new Error("invalid readseek KDL grep line number");
|
|
2082
|
+
return { ...parsed, kind: requireProperty(node, "kind") };
|
|
2083
|
+
}
|
|
2084
|
+
function grep(root) {
|
|
2085
|
+
grepChildren(root, ["match", "document"]);
|
|
2086
|
+
return parseGrepOutput({
|
|
2087
|
+
...properties(root),
|
|
2088
|
+
results: children(root, "match").map((match) => {
|
|
2089
|
+
grepChildren(match, ["hit", "line", "symbol"]);
|
|
2090
|
+
return {
|
|
2091
|
+
...properties(match),
|
|
2092
|
+
hits: children(match, "hit").map((hit) => {
|
|
2093
|
+
grepChildren(hit, []);
|
|
2094
|
+
return properties(hit);
|
|
2095
|
+
}),
|
|
2096
|
+
lines: children(match, "line").map(grepLine),
|
|
2097
|
+
symbols: children(match, "symbol").map((symbol) => {
|
|
2098
|
+
grepChildren(symbol, ["line"]);
|
|
2099
|
+
return { ...properties(symbol), lines: children(symbol, "line").map(grepLine) };
|
|
2100
|
+
})
|
|
2101
|
+
};
|
|
2102
|
+
}),
|
|
2103
|
+
documents: children(root, "document").map((document) => {
|
|
2104
|
+
grepChildren(document, ["hit"]);
|
|
2105
|
+
return { ...properties(document), hits: children(document, "hit").map((hit) => {
|
|
2106
|
+
grepChildren(hit, []);
|
|
2107
|
+
return properties(hit);
|
|
2108
|
+
}) };
|
|
2109
|
+
})
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
1872
2112
|
function references(root) {
|
|
1873
2113
|
const parsed = children(root, "reference").map((reference) => {
|
|
1874
2114
|
const sourceLine = child(reference, "line");
|
|
@@ -1984,7 +2224,7 @@ function parseReadSeekKdl(text) {
|
|
|
1984
2224
|
case "document":
|
|
1985
2225
|
return root.properties.has("format") ? pdfDocument(root) : sourceDocument(root);
|
|
1986
2226
|
case "search":
|
|
1987
|
-
return search(root);
|
|
2227
|
+
return ["mode", "ignore_case", "summary", "count_unit"].some((key) => root.properties.has(key)) ? grep(root) : search(root);
|
|
1988
2228
|
case "check":
|
|
1989
2229
|
return diagnostics(root);
|
|
1990
2230
|
case "refs":
|
|
@@ -2049,7 +2289,7 @@ function acceptsReadSeekExitCode(args, exitCode) {
|
|
|
2049
2289
|
index++;
|
|
2050
2290
|
continue;
|
|
2051
2291
|
}
|
|
2052
|
-
return arg === "
|
|
2292
|
+
return arg === "grep" || arg === "search" || arg === "def" || arg === "refs" || arg === "syntax" || arg === "check";
|
|
2053
2293
|
}
|
|
2054
2294
|
return false;
|
|
2055
2295
|
}
|
|
@@ -2285,22 +2525,22 @@ export {
|
|
|
2285
2525
|
acceptsReadSeekExitCode,
|
|
2286
2526
|
appendApplyAndPlanHash,
|
|
2287
2527
|
appendGitScopeArgs,
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
buildFindReferencesArgs,
|
|
2295
|
-
buildFindTextArgs,
|
|
2296
|
-
buildGenerateCaptionArgs,
|
|
2528
|
+
buildAssetsArgs,
|
|
2529
|
+
buildCaptionArgs,
|
|
2530
|
+
buildCheckArgs,
|
|
2531
|
+
buildDefArgs,
|
|
2532
|
+
buildEditArgs,
|
|
2533
|
+
buildGrepArgs,
|
|
2297
2534
|
buildLineLocation,
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2535
|
+
buildMetaArgs,
|
|
2536
|
+
buildObjectsArgs,
|
|
2537
|
+
buildOcrArgs,
|
|
2538
|
+
buildRefsArgs,
|
|
2539
|
+
buildRenameArgs,
|
|
2540
|
+
buildSearchArgs,
|
|
2541
|
+
buildShowArgs,
|
|
2542
|
+
buildSyntaxArgs,
|
|
2543
|
+
buildTreeArgs,
|
|
2304
2544
|
canonicalPathKey,
|
|
2305
2545
|
classifyReadSeekFailure,
|
|
2306
2546
|
cleanReadSeekErrorText,
|
|
@@ -2318,6 +2558,7 @@ export {
|
|
|
2318
2558
|
getToolContract,
|
|
2319
2559
|
isShowTool,
|
|
2320
2560
|
isSubPath,
|
|
2561
|
+
parseGrepOutput,
|
|
2321
2562
|
parseLineAnchor,
|
|
2322
2563
|
parseReadSeekKdl,
|
|
2323
2564
|
parseSelector,
|
|
@@ -2329,6 +2570,7 @@ export {
|
|
|
2329
2570
|
readSeekProcessEnvironment,
|
|
2330
2571
|
renderCompactGuidelines,
|
|
2331
2572
|
renderFallbackToolDescription,
|
|
2573
|
+
renderGrepOutput,
|
|
2332
2574
|
renderPreferredToolDescription,
|
|
2333
2575
|
renderToolDescription,
|
|
2334
2576
|
renderToolRoutingPolicy,
|
|
@@ -2349,5 +2591,6 @@ export {
|
|
|
2349
2591
|
serializeEditPlan,
|
|
2350
2592
|
tryParseLineAnchor,
|
|
2351
2593
|
validateExactlyOneEditVariant,
|
|
2594
|
+
validateGrepParams,
|
|
2352
2595
|
validateReferencesSelection
|
|
2353
2596
|
};
|