@jarkkojs/readseek-api 0.9.16 → 0.10.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/contract.ts
2
- var DIGEST_FACETS = [
2
+ var INSPECT_FACETS = [
3
3
  "metadata",
4
4
  "content",
5
5
  "map",
@@ -9,18 +9,6 @@ var DIGEST_FACETS = [
9
9
  var VISION_MODES = ["none", "all", "ocr", "caption", "objects"];
10
10
  var VISION_ANALYSIS_MODES = ["all", "ocr", "caption", "objects"];
11
11
  var VISION_LEVELS = ["low", "medium", "high"];
12
- var DOCUMENT_NODE_KINDS = [
13
- "artifact",
14
- "footer",
15
- "header",
16
- "heading",
17
- "marginal_label",
18
- "page",
19
- "page_number",
20
- "paragraph",
21
- "section",
22
- "structural_section"
23
- ];
24
12
  var EDIT_VARIANTS = [
25
13
  "set_line",
26
14
  "replace_lines",
@@ -28,13 +16,13 @@ var EDIT_VARIANTS = [
28
16
  "replace",
29
17
  "replace_symbol"
30
18
  ];
31
- var GIT_SELECTION_FLAGS = ["cached", "others", "ignored"];
19
+ var GIT_SCOPES = ["tracked", "untracked", "ignored", "all"];
32
20
  var TOOL_NAMES = [
33
- "digest",
21
+ "inspect",
34
22
  "edit",
35
23
  "write",
36
24
  "grep",
37
- "view",
25
+ "extract",
38
26
  "search",
39
27
  "def",
40
28
  "refs",
@@ -56,28 +44,24 @@ var PARAM_DESCRIPTIONS = {
56
44
  line: "One-based cursor line; required with scope",
57
45
  renameLine: "One-based cursor line of the symbol to rename",
58
46
  column: "One-based cursor byte column for disambiguation",
59
- cached: "In a Git repository, search tracked/indexed files",
60
- others: "In a Git repository, search untracked files",
61
- ignored: "With others=true, include ignored untracked files",
47
+ gitScope: "In a Git repository, which files to search: tracked, untracked, ignored (untracked plus ignored), or all. Defaults to tracked plus untracked.",
62
48
  select: "Facets: metadata, content, map, diagnostics, identity (default content)",
63
49
  selectComma: "Comma-separated facets: metadata, content, map, diagnostics, identity",
64
- at: "Location: line:N[:COLUMN], hash:HASH[:COLUMN], or symbol:NAME",
50
+ selector: "Positional selector: line:N[:COLUMN], hash:H[:COLUMN], symbol:NAME, page:N, node:ID, or asset:ID. Columns require the identity facet, which in turn rejects symbol selectors",
51
+ assetSelector: "Document asset selector in the form asset:ID",
52
+ query: "KDL selection query, @path, or '-' for standard input",
53
+ stdinName: "File name hint when the target is '-'",
65
54
  end: "Last line to include",
66
55
  limit: "Maximum number of lines to include",
67
- digestDepth: "Maximum structural depth for map output; defaults to 1 in agent tools",
56
+ inspectDepth: "Maximum structural depth for map output; defaults to 1 in agent tools",
68
57
  offset: "One-based starting line",
69
58
  readLimit: "Maximum lines to return",
70
59
  symbolRead: "Symbol name to read",
71
60
  bundle: "Include related symbols from the same file; requires symbol",
72
61
  image: "Standalone image mode: none, all, ocr, caption, objects. Required for standalone images.",
73
- visionModeDigest: "Standalone image mode: none, all, caption, objects, or ocr",
74
- visionModeView: "Analyze selected assets: all, ocr, caption, or objects",
62
+ visionModeInspect: "Standalone image mode: none, all, caption, objects, or ocr",
75
63
  visionLevel: "Vision inference level: low (default), medium, or high",
76
- node: "Node ID to use as the view root",
77
- page: "One-based source page",
78
- kind: "Node kind filter",
79
- depth: "Maximum depth below selected roots",
80
- outline: "Return outline nodes only",
64
+ output: "Destination file for extracted binary data; stdout is not supported",
81
65
  edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
82
66
  editVariant: "Exactly one nested edit variant",
83
67
  setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
@@ -114,11 +98,11 @@ var PARAM_DESCRIPTIONS = {
114
98
  scopeContext: "Context lines within each symbol"
115
99
  };
116
100
  var TOOL_DESCRIPTIONS = {
117
- digest: "Digest source content and images into the native CLI envelope. Returns metadata plus requested facets unchanged from `readseek digest`. Use it before edits to obtain fresh `LINE:HASH` anchors; use shallow maps for structural overviews, diagnostics for syntax checks, and identity for cursor context.",
101
+ inspect: "Inspect source content, images, and documents through the native CLI envelope. Use it before edits to obtain fresh `LINE:HASH` anchors; use shallow maps for structural overviews, diagnostics for syntax checks, and identity for cursor context.",
118
102
  edit: 'Edit existing text files with fresh `LINE:HASH` anchors. Read or search first when no fresh anchors are available. Each `edits[]` item must use exactly one nested object: `{"set_line":{"anchor":"LINE:HASH","new_text":"..."}}`, `{"replace_lines":{"start_anchor":"LINE:HASH","end_anchor":"LINE:HASH","new_text":"..."}}`, `{"insert_after":{"anchor":"LINE:HASH","new_text":"..."}}`, `{"replace_symbol":{"symbol":"name","new_body":"..."}}`, or `{"replace":{"old_text":"...","new_text":"..."}}`.',
119
103
  write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes to an existing file.",
120
104
  grep: "Search text or regex in files and return edit-ready `LINE:HASH` anchors. Use for identifiers, strings, configuration, errors, comments, and documentation.",
121
- view: "Digest a document's structure or selected content. Start with the overview, then narrow by page, node, kind, or depth instead of reading the whole document. PDF is the first supported document format.",
105
+ extract: "Extract a selected document asset into a destination file. Use an `asset:ID` selector discovered through `inspect`; extracted binary data is never returned on stdout.",
122
106
  search: "Search code by AST structure using ast-grep-style patterns. Results include line hashes usable as `LINE:HASH` anchors. Use `grep` for identifiers, strings, comments, configuration, documentation, or other plain text and regex.",
123
107
  def: "Go to a symbol definition or declaration by name. Accepts qualified or unqualified names; use this instead of text search for functions, methods, classes, types, and other declarations.",
124
108
  refs: "Find references or usages of an identifier by name or cursor binding. Results include enclosing symbols and line hashes usable as `LINE:HASH` anchors. Without cursor scope, results are identifier-name matches; with scope, they follow one binding and exclude shadows. Use text search for literals, and use this before renaming or deleting a symbol.",
@@ -130,18 +114,11 @@ function renderToolDescription(name, aliases = {}) {
130
114
  throw new Error(`Missing TOOL_DESCRIPTIONS for ${name}`);
131
115
  return TOOL_NAMES.reduce((description, toolName) => description.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), desc);
132
116
  }
133
- var gitFlagsSchema = {
134
- cached: {
135
- type: "boolean",
136
- description: PARAM_DESCRIPTIONS.cached
137
- },
138
- others: {
139
- type: "boolean",
140
- description: PARAM_DESCRIPTIONS.others
141
- },
142
- ignored: {
143
- type: "boolean",
144
- description: PARAM_DESCRIPTIONS.ignored
117
+ var gitScopeSchema = {
118
+ gitScope: {
119
+ type: "string",
120
+ enum: GIT_SCOPES,
121
+ description: PARAM_DESCRIPTIONS.gitScope
145
122
  }
146
123
  };
147
124
  var editItemSchema = {
@@ -207,29 +184,31 @@ var editItemSchema = {
207
184
  };
208
185
  var TOOL_CONTRACTS = [
209
186
  {
210
- name: "digest",
211
- title: "Digest source",
212
- description: TOOL_DESCRIPTIONS.digest,
187
+ name: "inspect",
188
+ title: "Inspect input",
189
+ description: TOOL_DESCRIPTIONS.inspect,
213
190
  inputSchema: {
214
191
  type: "object",
215
192
  properties: {
216
193
  path: { type: "string", description: PARAM_DESCRIPTIONS.path },
194
+ selector: { type: "string", description: PARAM_DESCRIPTIONS.selector },
217
195
  select: { type: "string", description: PARAM_DESCRIPTIONS.select },
218
- at: { type: "string", description: PARAM_DESCRIPTIONS.at },
196
+ query: { type: "string", description: PARAM_DESCRIPTIONS.query },
219
197
  end: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.end },
220
198
  limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.limit },
221
- depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.digestDepth },
199
+ depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.inspectDepth },
222
200
  language: { type: "string", description: PARAM_DESCRIPTIONS.languageOverride },
223
201
  visionMode: {
224
202
  type: "string",
225
203
  enum: [...VISION_MODES],
226
- description: PARAM_DESCRIPTIONS.visionModeDigest
204
+ description: PARAM_DESCRIPTIONS.visionModeInspect
227
205
  },
228
206
  visionLevel: {
229
207
  type: "string",
230
208
  enum: [...VISION_LEVELS],
231
209
  description: PARAM_DESCRIPTIONS.visionLevel
232
- }
210
+ },
211
+ stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
233
212
  },
234
213
  required: ["path"],
235
214
  additionalProperties: false
@@ -303,34 +282,27 @@ var TOOL_CONTRACTS = [
303
282
  }
304
283
  },
305
284
  {
306
- name: "view",
307
- title: "View document",
308
- description: TOOL_DESCRIPTIONS.view,
285
+ name: "extract",
286
+ title: "Extract asset",
287
+ description: TOOL_DESCRIPTIONS.extract,
309
288
  inputSchema: {
310
289
  type: "object",
311
290
  properties: {
312
291
  path: { type: "string", description: PARAM_DESCRIPTIONS.path },
313
- node: { type: "string", description: PARAM_DESCRIPTIONS.node },
314
- page: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.page },
315
- kind: {
292
+ selector: {
316
293
  type: "string",
317
- enum: [...DOCUMENT_NODE_KINDS],
318
- description: PARAM_DESCRIPTIONS.kind
294
+ pattern: "^asset:.+",
295
+ description: PARAM_DESCRIPTIONS.assetSelector
319
296
  },
320
- depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.depth },
321
- outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline },
322
- visionMode: {
297
+ output: {
323
298
  type: "string",
324
- enum: [...VISION_ANALYSIS_MODES],
325
- description: PARAM_DESCRIPTIONS.visionModeView
299
+ minLength: 1,
300
+ pattern: "^(?!-$).+",
301
+ description: PARAM_DESCRIPTIONS.output
326
302
  },
327
- visionLevel: {
328
- type: "string",
329
- enum: [...VISION_LEVELS],
330
- description: PARAM_DESCRIPTIONS.visionLevel
331
- }
303
+ stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
332
304
  },
333
- required: ["path"],
305
+ required: ["path", "selector", "output"],
334
306
  additionalProperties: false
335
307
  }
336
308
  },
@@ -344,7 +316,8 @@ var TOOL_CONTRACTS = [
344
316
  pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
345
317
  path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
346
318
  language: { type: "string", description: PARAM_DESCRIPTIONS.language },
347
- ...gitFlagsSchema
319
+ limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
320
+ ...gitScopeSchema
348
321
  },
349
322
  required: ["pattern"],
350
323
  additionalProperties: false
@@ -360,7 +333,7 @@ var TOOL_CONTRACTS = [
360
333
  name: { type: "string", description: PARAM_DESCRIPTIONS.name },
361
334
  path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
362
335
  language: { type: "string", description: PARAM_DESCRIPTIONS.language },
363
- ...gitFlagsSchema
336
+ ...gitScopeSchema
364
337
  },
365
338
  required: ["name"],
366
339
  additionalProperties: false
@@ -379,7 +352,7 @@ var TOOL_CONTRACTS = [
379
352
  scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
380
353
  line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
381
354
  column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
382
- ...gitFlagsSchema
355
+ ...gitScopeSchema
383
356
  },
384
357
  additionalProperties: false
385
358
  }
@@ -397,7 +370,9 @@ var TOOL_CONTRACTS = [
397
370
  to: { type: "string", description: PARAM_DESCRIPTIONS.to },
398
371
  workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
399
372
  apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
400
- planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash }
373
+ planHash: { type: "string", description: PARAM_DESCRIPTIONS.planHash },
374
+ language: { type: "string", description: PARAM_DESCRIPTIONS.language },
375
+ ...gitScopeSchema
401
376
  },
402
377
  required: ["path", "line", "to"],
403
378
  additionalProperties: false
@@ -413,7 +388,7 @@ function getToolContract(name) {
413
388
  // src/policy.ts
414
389
  var REPLACEABLE_TOOLS = [
415
390
  {
416
- tool: "digest",
391
+ tool: "inspect",
417
392
  builtIn: "read",
418
393
  benefit: "it returns LINE:HASH anchors for safe edits."
419
394
  },
@@ -434,8 +409,8 @@ var REPLACEABLE_TOOLS = [
434
409
  }
435
410
  ];
436
411
  var TOOL_COMPACT_GUIDELINES = {
437
- digest: [
438
- "Use `digest` for shallow maps, diagnostics, and identity; use `def`, `refs`, or `search` for targeted symbol questions."
412
+ inspect: [
413
+ "Use `inspect` for shallow maps, diagnostics, and identity; use `def`, `refs`, or `search` for targeted symbol questions."
439
414
  ],
440
415
  edit: [
441
416
  "Prefer set_line, replace_lines, and insert_after; use replace only when anchors are impractical."
@@ -454,14 +429,14 @@ var TOOL_COMPACT_GUIDELINES = {
454
429
  ]
455
430
  };
456
431
  var BUILTIN_FALLBACKS = {
457
- read: "digest",
432
+ read: "inspect",
458
433
  edit: "edit",
459
434
  write: "write",
460
435
  grep: "grep",
461
436
  apply_patch: "edit"
462
437
  };
463
438
  var PREFERENCE_LEADS = {
464
- digest: "Preferred for reading source with anchors and facets.",
439
+ inspect: "Preferred for reading source with anchors and facets.",
465
440
  edit: "Preferred for editing existing text files with fresh LINE:HASH anchors.",
466
441
  write: "Preferred for creating or replacing a complete text file; returns LINE:HASH anchors.",
467
442
  grep: "Preferred text/regex search; returns edit-ready LINE:HASH anchors.",
@@ -469,10 +444,10 @@ var PREFERENCE_LEADS = {
469
444
  def: "Preferred for go-to-definition or declaration lookup by name.",
470
445
  refs: "Preferred for finding identifier-name matches or binding-specific references before rename or delete.",
471
446
  rename: "Preferred for renaming a symbol with binding-aware, verified edits.",
472
- view: "Preferred for digesting document structure or selected content."
447
+ extract: "Preferred for saving a selected document asset to a file."
473
448
  };
474
449
  var FALLBACK_TEMPLATES = {
475
- read: "Prefer `digest` with fresh LINE:HASH anchors.",
450
+ read: "Prefer `inspect` with fresh LINE:HASH anchors.",
476
451
  edit: "Prefer `edit` with fresh LINE:HASH anchors.",
477
452
  write: "Prefer `write` so the result returns LINE:HASH anchors.",
478
453
  grep: "Prefer `grep` for text/regex search with edit-ready anchors.",
@@ -498,7 +473,7 @@ function renderFallbackToolDescription(builtIn, aliases = {}) {
498
473
  }
499
474
  function renderToolRoutingPolicy(options = {}) {
500
475
  const aliases = options.aliases ?? {};
501
- const digest = resolveToolName("digest", aliases);
476
+ const inspect = resolveToolName("inspect", aliases);
502
477
  const edit = resolveToolName("edit", aliases);
503
478
  const write = resolveToolName("write", aliases);
504
479
  const grep = resolveToolName("grep", aliases);
@@ -506,11 +481,11 @@ function renderToolRoutingPolicy(options = {}) {
506
481
  const def = resolveToolName("def", aliases);
507
482
  const refs = resolveToolName("refs", aliases);
508
483
  const rename = resolveToolName("rename", aliases);
509
- const view = resolveToolName("view", aliases);
484
+ const extract = resolveToolName("extract", aliases);
510
485
  if (options.style === "server") {
511
486
  return [
512
- "Structural code navigation, document viewing, and hash-verified edits with readseek.",
513
- "For structural questions, prefer these tools to text Grep/Bash: targeted definitions, references, and AST search; shallow digest maps; diagnostics and identity; and document views.",
487
+ "Structural code navigation, document inspection, and hash-verified edits with readseek.",
488
+ "For structural questions, prefer these tools to text Grep/Bash: targeted definitions, references, and AST search; shallow inspect maps; diagnostics and identity; and document inspection.",
514
489
  `Use ${edit} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
515
490
  "Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
516
491
  "call MCP tools for active navigation, document questions, or anchored edits."
@@ -521,7 +496,7 @@ function renderToolRoutingPolicy(options = {}) {
521
496
  if (options.preferOverBuiltIns) {
522
497
  bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
523
498
  }
524
- bullets.push(`- Read with ${digest} first; ${edit} needs fresh LINE:HASH anchors.`, `- Use ${grep} for text/regex, ${search} for AST patterns, ${def}/${refs} for targeted symbols, and shallow ${digest} maps only for file overviews.`, `- Use ${edit} for existing files, ${write} for whole-file creation or replacement, and ${rename} for symbol renames.`, `- Use ${view} for document structure; run ${digest} with select diagnostics after source edits for a quick syntax check.`);
499
+ bullets.push(`- Read with ${inspect} first; ${edit} needs fresh LINE:HASH anchors.`, `- Use ${grep} for text/regex, ${search} for AST patterns, ${def}/${refs} for targeted symbols, and shallow ${inspect} maps only for file overviews.`, `- Use ${edit} for existing files, ${write} for whole-file creation or replacement, and ${rename} for symbol renames.`, `- Use ${inspect} for document structure; run ${inspect} with select diagnostics after source edits for a quick syntax check.`, `- Use ${extract} to save a selected document asset.`);
525
500
  if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
526
501
  bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
527
502
  }
@@ -563,12 +538,6 @@ function parseLineAnchor(anchor) {
563
538
  }
564
539
 
565
540
  // src/validation.ts
566
- function validateGitSelection(input) {
567
- if (input.ignored && !input.others) {
568
- return { ok: false, error: "ignored requires others=true" };
569
- }
570
- return { ok: true };
571
- }
572
541
  function validateRefsNameVsScope(input) {
573
542
  if (input.scope) {
574
543
  if (input.line === undefined) {
@@ -585,11 +554,8 @@ function validateRefsNameVsScope(input) {
585
554
  return { ok: true };
586
555
  }
587
556
  function validateVisionLevelVsMode(input) {
588
- if (input.outline && (input.visionMode !== undefined || input.visionLevel !== undefined)) {
589
- return { ok: false, error: "Cannot combine outline with visionMode or visionLevel" };
590
- }
591
- if (input.visionLevel !== undefined && input.visionMode === undefined) {
592
- return { ok: false, error: "visionLevel requires visionMode" };
557
+ if (input.visionLevel !== undefined && !["all", "ocr", "caption", "objects"].includes(input.visionMode ?? "")) {
558
+ return { ok: false, error: "visionLevel requires an analysis visionMode" };
593
559
  }
594
560
  return { ok: true };
595
561
  }
@@ -716,22 +682,54 @@ function coerceNonNegativeInt(value) {
716
682
  }
717
683
  return;
718
684
  }
685
+ // src/edit-plan.ts
686
+ import { Document, Node, format } from "@bgotink/kdl";
687
+ function node(name, properties, children = []) {
688
+ const result = Node.create(name);
689
+ for (const [key, value] of Object.entries(properties))
690
+ result.setProperty(key, value);
691
+ if (children.length > 0)
692
+ result.children = new Document(children);
693
+ return result;
694
+ }
695
+ function editNode(edit) {
696
+ if ("set_line" in edit) {
697
+ const value = edit.set_line;
698
+ return node("set-line", { anchor: value.anchor, text: value.new_text });
699
+ }
700
+ if ("replace_lines" in edit) {
701
+ const value = edit.replace_lines;
702
+ return node("replace-lines", { start: value.start_anchor, end: value.end_anchor, text: value.new_text });
703
+ }
704
+ if ("insert_after" in edit) {
705
+ const value = edit.insert_after;
706
+ return node("insert-after", { anchor: value.anchor, text: value.new_text });
707
+ }
708
+ if ("replace_symbol" in edit) {
709
+ const value = edit.replace_symbol;
710
+ return node("replace-symbol", { symbol: value.symbol, body: value.new_body });
711
+ }
712
+ const value = edit.replace;
713
+ return node("replace", {
714
+ old: value.old_text,
715
+ new: value.new_text,
716
+ ...value.all === undefined ? {} : { all: value.all }
717
+ });
718
+ }
719
+ function serializeEditPlan(edits) {
720
+ const validation = validateExactlyOneEditVariant(edits);
721
+ if (!validation.ok)
722
+ throw new Error(validation.error);
723
+ return format(new Document([node("plan", {}, validation.edits.map(editNode))]));
724
+ }
719
725
  // src/argv.ts
720
- var DEFAULT_DIGEST_MAP_DEPTH = 1;
726
+ var DEFAULT_INSPECT_MAP_DEPTH = 1;
721
727
  function buildLineLocation(line, column) {
722
728
  return column === undefined ? `line:${line}` : `line:${line}:${column}`;
723
729
  }
724
- function appendGitSelectionArgs(args, input) {
725
- const validated = validateGitSelection(input);
726
- if (!validated.ok) {
727
- throw new Error(validated.error);
728
- }
729
- if (input.cached)
730
- args.push("--cached");
731
- if (input.others)
732
- args.push("--others");
733
- if (input.ignored)
734
- args.push("--ignored");
730
+ function appendGitScopeArgs(args, input) {
731
+ if (input.gitScope !== undefined)
732
+ args.push("--git-scope", input.gitScope);
735
733
  return args;
736
734
  }
737
735
  function pushOptionalString(args, flag, value) {
@@ -760,33 +758,138 @@ function selectArg(select) {
760
758
  return;
761
759
  return Array.isArray(select) ? select.join(",") : select;
762
760
  }
763
- function selectsDigestMap(select) {
764
- const value = selectArg(select);
765
- return value?.split(",").some((facet) => facet.trim() === "map") ?? false;
761
+ function selectedInspectFacets(select) {
762
+ const facets = (selectArg(select) ?? "content").split(",").map((facet) => facet.trim());
763
+ for (const facet of facets) {
764
+ if (!INSPECT_FACETS.includes(facet)) {
765
+ throw new Error(`unknown inspect facet: ${facet}`);
766
+ }
767
+ }
768
+ return facets;
769
+ }
770
+ function hasInspectFacet(facets, facet) {
771
+ return facets.includes(facet);
772
+ }
773
+ function validateTarget(command, target, stdinName) {
774
+ if (target.length === 0)
775
+ throw new Error(`${command} requires a target file path or '-'`);
776
+ if (stdinName !== undefined && stdinName.length > 0 && target !== "-") {
777
+ throw new Error("stdinName requires '-' as the target");
778
+ }
779
+ }
780
+ function validatePositiveInteger(value, name) {
781
+ if (!Number.isSafeInteger(value) || value < 1) {
782
+ throw new Error(`${name} must be a positive integer`);
783
+ }
784
+ }
785
+ var SELECTOR_KINDS = ["line", "hash", "symbol", "page", "node", "asset"];
786
+ function isSelectorKind(value) {
787
+ return SELECTOR_KINDS.includes(value);
766
788
  }
767
- function buildDigestArgs(input) {
789
+ function parseSelector(text) {
790
+ const separator = text.indexOf(":");
791
+ if (separator < 0) {
792
+ throw new Error("selector must start with line:, hash:, symbol:, page:, node:, or asset:");
793
+ }
794
+ const kind = text.slice(0, separator);
795
+ const value = text.slice(separator + 1);
796
+ if (value.length === 0)
797
+ throw new Error(`${kind} selector must not be empty`);
798
+ if (!isSelectorKind(kind)) {
799
+ throw new Error(`unknown selector kind \`${kind}\`; expected line, hash, symbol, page, node, or asset`);
800
+ }
801
+ if (kind === "line" || kind === "hash") {
802
+ const parts = value.split(":");
803
+ if (parts.length > 2)
804
+ throw new Error("selector has too many components");
805
+ if (parts.length === 2) {
806
+ const column = Number(parts[1]);
807
+ if (!Number.isSafeInteger(column))
808
+ throw new Error(`invalid column \`${parts[1]}\``);
809
+ if (column < 1)
810
+ throw new Error("column must be greater than zero");
811
+ return { kind, value: parts[0], column };
812
+ }
813
+ }
814
+ if (kind === "page") {
815
+ if (value.includes(":"))
816
+ throw new Error("page selector has too many components");
817
+ const page = Number(value);
818
+ if (!Number.isSafeInteger(page))
819
+ throw new Error(`invalid page \`${value}\``);
820
+ if (page < 1)
821
+ throw new Error("page must be greater than zero");
822
+ }
823
+ return { kind, value };
824
+ }
825
+ function buildInspectArgs(input) {
826
+ validateTarget("inspect", input.path, input.stdinName);
827
+ if (input.selector !== undefined && input.selector.length === 0) {
828
+ throw new Error("selector must not be empty");
829
+ }
768
830
  const vision = validateVisionLevelVsMode({
769
831
  visionMode: input.visionMode,
770
832
  visionLevel: input.visionLevel
771
833
  });
772
834
  if (!vision.ok)
773
835
  throw new Error(vision.error);
774
- const selectsMap = selectsDigestMap(input.select);
836
+ const facets = selectedInspectFacets(input.select);
837
+ const hasContent = hasInspectFacet(facets, "content");
838
+ const hasIdentity = hasInspectFacet(facets, "identity");
839
+ const hasMap = hasInspectFacet(facets, "map");
840
+ const hasSourceFacet = hasMap || hasInspectFacet(facets, "diagnostics") || hasIdentity;
841
+ if (input.end !== undefined)
842
+ validatePositiveInteger(input.end, "end");
843
+ if (input.limit !== undefined)
844
+ validatePositiveInteger(input.limit, "limit");
845
+ if (input.end !== undefined && input.limit !== undefined) {
846
+ throw new Error("cannot combine end with limit");
847
+ }
775
848
  if (input.depth !== undefined) {
776
- if (!Number.isInteger(input.depth) || input.depth < 0) {
849
+ if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
777
850
  throw new Error("depth must be a non-negative integer");
778
851
  }
779
- if (!selectsMap)
852
+ if (!hasMap)
780
853
  throw new Error("depth requires the map facet");
781
854
  }
855
+ if (input.selector !== undefined && input.query !== undefined) {
856
+ throw new Error("cannot combine selector with query");
857
+ }
858
+ if (!hasContent && (input.end !== undefined || input.limit !== undefined || input.query !== undefined)) {
859
+ throw new Error("ranges and queries require the content facet");
860
+ }
861
+ if (hasIdentity && input.selector === undefined) {
862
+ throw new Error("the identity facet requires a selector");
863
+ }
864
+ if (input.selector !== undefined && !hasContent && !hasIdentity) {
865
+ throw new Error("selectors require the content or identity facet");
866
+ }
867
+ if (input.selector !== undefined) {
868
+ const selector = parseSelector(input.selector);
869
+ if (selector.kind === "symbol" && hasIdentity) {
870
+ throw new Error("the identity facet does not accept symbol selectors");
871
+ }
872
+ if (selector.column !== undefined && !hasIdentity) {
873
+ throw new Error("selector columns require the identity facet");
874
+ }
875
+ }
876
+ const hasVision = input.visionMode !== undefined || input.visionLevel !== undefined;
877
+ if (hasVision && !hasContent) {
878
+ throw new Error("vision options require the content facet");
879
+ }
880
+ if (hasVision && hasSourceFacet) {
881
+ throw new Error("vision options cannot be combined with source facets");
882
+ }
782
883
  const args = [];
783
884
  if (input.readseekDir !== undefined)
784
885
  args.push("--readseek-dir", input.readseekDir);
785
- args.push("digest", input.path);
886
+ args.push("inspect", input.path);
887
+ if (input.selector !== undefined)
888
+ args.push(input.selector);
786
889
  const select = selectArg(input.select);
787
890
  if (select !== undefined)
788
891
  args.push("--select", select);
789
- pushOptionalString(args, "--at", input.at);
892
+ pushOptionalString(args, "--query", input.query);
790
893
  pushOptionalNumber(args, "--end", input.end);
791
894
  pushOptionalNumber(args, "--limit", input.limit);
792
895
  pushOptionalNumber(args, "--depth", input.depth);
@@ -794,30 +897,24 @@ function buildDigestArgs(input) {
794
897
  pushOptionalString(args, "--vision-mode", input.visionMode);
795
898
  pushOptionalString(args, "--vision-level", input.visionLevel);
796
899
  pushOptionalString(args, "--stdin-name", input.stdinName);
900
+ args.push("--kdl");
797
901
  return args;
798
902
  }
799
- function buildViewArgs(input) {
800
- const vision = validateVisionLevelVsMode({
801
- visionMode: input.visionMode,
802
- visionLevel: input.visionLevel,
803
- outline: input.outline
804
- });
805
- if (!vision.ok)
806
- throw new Error(vision.error);
903
+ function buildExtractArgs(input) {
904
+ validateTarget("extract", input.path, input.stdinName);
905
+ if (input.selector.length === 0)
906
+ throw new Error("extract requires an asset selector");
907
+ if (!input.selector.startsWith("asset:") || input.selector === "asset:") {
908
+ throw new Error("extract requires an asset: selector");
909
+ }
910
+ if (input.output.length === 0 || input.output === "-") {
911
+ throw new Error("extract output must be a file path");
912
+ }
807
913
  const args = [];
808
914
  if (input.readseekDir !== undefined)
809
915
  args.push("--readseek-dir", input.readseekDir);
810
- args.push("view", input.path);
811
- if (input.format !== undefined)
812
- args.push("--format", input.format);
813
- pushOptionalString(args, "--node", input.node);
814
- pushOptionalNumber(args, "--page", input.page);
815
- pushOptionalString(args, "--kind", input.kind);
816
- pushOptionalNumber(args, "--depth", input.depth);
817
- if (input.outline)
818
- args.push("--outline");
819
- pushOptionalString(args, "--vision-mode", input.visionMode);
820
- pushOptionalString(args, "--vision-level", input.visionLevel);
916
+ args.push("--output", input.output, "extract", input.path, input.selector);
917
+ pushOptionalString(args, "--stdin-name", input.stdinName);
821
918
  return args;
822
919
  }
823
920
  function buildSearchArgs(input) {
@@ -825,15 +922,17 @@ function buildSearchArgs(input) {
825
922
  pushOptionalString(args, "--path", input.path);
826
923
  pushOptionalString(args, "--language", input.language);
827
924
  pushOptionalNumber(args, "--limit", input.limit);
828
- return appendGitSelectionArgs(args, input);
925
+ appendGitScopeArgs(args, input);
926
+ args.push("--kdl");
927
+ return args;
829
928
  }
830
929
  function buildDefArgs(input) {
831
930
  const args = ["def", input.name];
832
931
  pushOptionalString(args, "--path", input.path);
833
- if (input.format !== undefined)
834
- args.push("--format", input.format);
835
932
  pushOptionalString(args, "--language", input.language);
836
- return appendGitSelectionArgs(args, input);
933
+ appendGitScopeArgs(args, input);
934
+ args.push("--kdl");
935
+ return args;
837
936
  }
838
937
  function buildRefsArgs(input) {
839
938
  const validated = validateRefsNameVsScope(input);
@@ -848,10 +947,10 @@ function buildRefsArgs(input) {
848
947
  pushOptionalNumber(args, "--line", input.line);
849
948
  pushOptionalNumber(args, "--column", input.column);
850
949
  }
851
- if (input.format !== undefined)
852
- args.push("--format", input.format);
853
950
  pushOptionalString(args, "--language", input.language);
854
- return appendGitSelectionArgs(args, input);
951
+ appendGitScopeArgs(args, input);
952
+ args.push("--kdl");
953
+ return args;
855
954
  }
856
955
  function buildEditArgs(input) {
857
956
  const args = ["edit", input.path];
@@ -863,6 +962,7 @@ function buildEditArgs(input) {
863
962
  pushOptionalString(args, "--language", input.language);
864
963
  if (input.requestFromStdin)
865
964
  args.push("--edits", "-");
965
+ args.push("--kdl");
866
966
  return args;
867
967
  }
868
968
  function buildRenameArgs(input) {
@@ -875,7 +975,9 @@ function buildRenameArgs(input) {
875
975
  flagOrder: input.flagOrder ?? "plan-hash-first"
876
976
  });
877
977
  pushOptionalString(args, "--language", input.language);
878
- return appendGitSelectionArgs(args, input);
978
+ appendGitScopeArgs(args, input);
979
+ args.push("--kdl");
980
+ return args;
879
981
  }
880
982
  // src/platform.ts
881
983
  import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
@@ -891,6 +993,8 @@ var READSEEK_PLATFORM_PACKAGES = {
891
993
  };
892
994
  var resolveFromPackage = createRequire(import.meta.url).resolve;
893
995
  function readSeekBinaryName(platform = process.platform) {
996
+ if (platform === "linux")
997
+ return "readseek.AppImage";
894
998
  return platform === "win32" ? "readseek.exe" : "readseek";
895
999
  }
896
1000
  function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
@@ -965,7 +1069,7 @@ function resolveReadSeekCacheDir(subPath, options = {}) {
965
1069
  return pathModule.join(base, ...parts);
966
1070
  }
967
1071
  // src/path.ts
968
- import { realpathSync } from "node:fs";
1072
+ import { lstatSync, realpathSync } from "node:fs";
969
1073
  import { homedir as homedir2 } from "node:os";
970
1074
  import path2 from "node:path";
971
1075
  var UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
@@ -994,30 +1098,338 @@ function isSubPath(parent, candidate) {
994
1098
  const relative = path2.relative(parent, candidate);
995
1099
  return relative === "" || !relative.startsWith(`..${path2.sep}`) && relative !== ".." && !path2.isAbsolute(relative);
996
1100
  }
1101
+ function resolvePathWithMissingTail(filePath) {
1102
+ let ancestor = filePath;
1103
+ const missing = [];
1104
+ while (true) {
1105
+ try {
1106
+ lstatSync(ancestor);
1107
+ } catch (error) {
1108
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT")
1109
+ throw error;
1110
+ const parent = path2.dirname(ancestor);
1111
+ if (parent === ancestor)
1112
+ throw error;
1113
+ missing.push(path2.basename(ancestor));
1114
+ ancestor = parent;
1115
+ continue;
1116
+ }
1117
+ return path2.join(realpathSync.native(ancestor), ...missing.reverse());
1118
+ }
1119
+ }
997
1120
  function resolveScopedPath(root, rawPath, fallback = ".") {
998
- const canonicalRoot = canonicalPathKey(root, ".");
999
- const candidate = canonicalPathKey(canonicalRoot, rawPath ?? fallback);
1121
+ let canonicalRoot;
1122
+ let candidate;
1123
+ try {
1124
+ canonicalRoot = realpathSync.native(path2.resolve(root));
1125
+ candidate = resolvePathWithMissingTail(path2.resolve(canonicalRoot, rawPath ?? fallback));
1126
+ } catch (error) {
1127
+ const message = error instanceof Error ? error.message : String(error);
1128
+ return { ok: false, error: `cannot resolve project path (${root}): ${rawPath ?? fallback}: ${message}` };
1129
+ }
1000
1130
  if (!isSubPath(canonicalRoot, candidate)) {
1001
1131
  return { ok: false, error: `path escapes project root (${canonicalRoot}): ${rawPath ?? fallback}` };
1002
1132
  }
1003
1133
  return { ok: true, path: candidate };
1004
1134
  }
1005
- // src/json.ts
1006
- var READSEEK_USAGE_HINT = /\n\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
1135
+ // src/error.ts
1136
+ var READSEEK_USAGE_HINT = /(?:^|\n)\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
1007
1137
  function cleanReadSeekErrorText(stderr) {
1008
1138
  return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
1009
1139
  }
1010
- function parseReadSeekJson(stdout) {
1011
- try {
1012
- return JSON.parse(stdout);
1013
- } catch {
1014
- const preview = stdout.trim().slice(0, 200);
1015
- throw new Error(`readseek returned invalid JSON: ${preview}`);
1140
+ // src/kdl.ts
1141
+ import { parse } from "@bgotink/kdl";
1142
+ function convertNode(node) {
1143
+ if (node.getTag() !== null)
1144
+ throw new Error("KDL node annotations are not supported");
1145
+ const properties = new Map;
1146
+ for (const entry of node.entries) {
1147
+ const name = entry.getName();
1148
+ if (name === null)
1149
+ throw new Error("KDL positional arguments are not supported");
1150
+ if (properties.has(name))
1151
+ throw new Error(`KDL duplicate property '${name}'`);
1152
+ if (entry.getTag() !== null)
1153
+ throw new Error("KDL value annotations are not supported");
1154
+ const value = entry.getValue();
1155
+ if (value === null)
1156
+ throw new Error("KDL null values are not supported");
1157
+ if (typeof value === "number" && !Number.isFinite(value)) {
1158
+ throw new Error("KDL number out of range");
1159
+ }
1160
+ properties.set(name, value);
1161
+ }
1162
+ return {
1163
+ name: node.getName(),
1164
+ properties,
1165
+ children: node.children?.nodes.map(convertNode) ?? []
1166
+ };
1167
+ }
1168
+ function parseKdlDocument(text) {
1169
+ const document = parse(text);
1170
+ if (document.nodes.length !== 1)
1171
+ throw new Error("KDL document must contain exactly one root node");
1172
+ return convertNode(document.nodes[0]);
1173
+ }
1174
+
1175
+ // src/output-kdl.ts
1176
+ function properties(node) {
1177
+ return Object.fromEntries(node.properties);
1178
+ }
1179
+ function children(node, name) {
1180
+ return node.children.filter((child) => child.name === name);
1181
+ }
1182
+ function child(node, name) {
1183
+ return node.children.find((candidate) => candidate.name === name);
1184
+ }
1185
+ function requireProperty(node, name) {
1186
+ const value = node.properties.get(name);
1187
+ if (value === undefined)
1188
+ throw new Error(`invalid readseek KDL: ${node.name} is missing '${name}'`);
1189
+ return value;
1190
+ }
1191
+ function line(node) {
1192
+ const anchor = requireProperty(node, "anchor");
1193
+ const content = requireProperty(node, "content");
1194
+ if (typeof anchor !== "string" || typeof content !== "string") {
1195
+ throw new Error("invalid readseek KDL line");
1196
+ }
1197
+ const match = /^(\d+):([0-9a-f]+)$/.exec(anchor);
1198
+ if (!match)
1199
+ throw new Error(`invalid readseek KDL line anchor: ${anchor}`);
1200
+ return {
1201
+ line: Number(match[1]),
1202
+ hash: match[2],
1203
+ text: content
1204
+ };
1205
+ }
1206
+ function mapNode(node) {
1207
+ return {
1208
+ ...properties(node),
1209
+ children: children(node, "node").map(mapNode)
1210
+ };
1211
+ }
1212
+ function sourceContent(node) {
1213
+ const result = properties(node);
1214
+ result.hashlines = children(node, "line").map(line);
1215
+ const objects = children(node, "object");
1216
+ if (objects.length > 0) {
1217
+ result.objects = objects.map((object) => ({
1218
+ label: requireProperty(object, "label"),
1219
+ bbox: [
1220
+ requireProperty(object, "xmin"),
1221
+ requireProperty(object, "ymin"),
1222
+ requireProperty(object, "xmax"),
1223
+ requireProperty(object, "ymax")
1224
+ ]
1225
+ }));
1226
+ }
1227
+ return result;
1228
+ }
1229
+ function sourceMap(node) {
1230
+ const root = child(node, "node");
1231
+ return {
1232
+ ...properties(node),
1233
+ ...root ? { root: mapNode(root) } : {},
1234
+ symbols: children(node, "symbol").map(properties)
1235
+ };
1236
+ }
1237
+ function diagnostics(node) {
1238
+ return {
1239
+ ...properties(node),
1240
+ diagnostics: children(node, "diagnostic").map(properties)
1241
+ };
1242
+ }
1243
+ function identity(node) {
1244
+ const result = properties(node);
1245
+ const identifier = child(node, "identifier");
1246
+ const symbol = child(node, "symbol");
1247
+ if (identifier)
1248
+ result.identifier = properties(identifier);
1249
+ if (symbol)
1250
+ result.symbol = properties(symbol);
1251
+ return result;
1252
+ }
1253
+ function sourceDocument(root) {
1254
+ const result = {};
1255
+ const metadata = child(root, "metadata");
1256
+ const cache = child(root, "cache");
1257
+ const content = child(root, "content");
1258
+ const map = child(root, "map");
1259
+ const diagnosticOutput = child(root, "diagnostics");
1260
+ const identityOutput = child(root, "identity");
1261
+ if (metadata)
1262
+ result.metadata = properties(metadata);
1263
+ if (cache)
1264
+ result.cache = properties(cache);
1265
+ if (content)
1266
+ result.content = sourceContent(content);
1267
+ if (map)
1268
+ result.map = sourceMap(map);
1269
+ if (diagnosticOutput)
1270
+ result.diagnostics = diagnostics(diagnosticOutput);
1271
+ if (identityOutput)
1272
+ result.identity = identity(identityOutput);
1273
+ return result;
1274
+ }
1275
+ function hit(node) {
1276
+ return {
1277
+ ...properties(node),
1278
+ hashlines: children(node, "line").map(line),
1279
+ captures: children(node, "capture").map((capture) => ({
1280
+ ...properties(capture),
1281
+ hashlines: children(capture, "line").map(line)
1282
+ }))
1283
+ };
1284
+ }
1285
+ function search(root) {
1286
+ return {
1287
+ ...properties(root),
1288
+ results: children(root, "match").map((match) => ({
1289
+ ...properties(match),
1290
+ matches: children(match, "hit").map(hit)
1291
+ })),
1292
+ documents: children(root, "document").map((documentNode) => ({
1293
+ ...properties(documentNode),
1294
+ hits: children(documentNode, "hit").map(properties)
1295
+ }))
1296
+ };
1297
+ }
1298
+ function references(root) {
1299
+ const parsed = children(root, "reference").map((reference) => {
1300
+ const sourceLine = child(reference, "line");
1301
+ if (!sourceLine)
1302
+ throw new Error("invalid readseek KDL reference: missing line");
1303
+ const parsedLine = line(sourceLine);
1304
+ const symbol = child(reference, "symbol");
1305
+ return {
1306
+ ...properties(reference),
1307
+ line_hash: parsedLine.hash,
1308
+ text: parsedLine.text,
1309
+ ...symbol ? { symbol: properties(symbol) } : {}
1310
+ };
1311
+ });
1312
+ return {
1313
+ references: parsed,
1314
+ locations: parsed.map((reference) => {
1315
+ const { symbol, ...location } = reference;
1316
+ return symbol && typeof symbol === "object" ? { ...location, ...symbol } : location;
1317
+ })
1318
+ };
1319
+ }
1320
+ function definitions(root) {
1321
+ return {
1322
+ locations: children(root, "definition").map((definition) => {
1323
+ const symbol = child(definition, "symbol");
1324
+ const sourceLine = child(definition, "line");
1325
+ if (!symbol || !sourceLine) {
1326
+ throw new Error("invalid readseek KDL definition: missing symbol or line");
1327
+ }
1328
+ const symbolProperties = properties(symbol);
1329
+ const parsedLine = line(sourceLine);
1330
+ const definitionColumn = definition.properties.get("column");
1331
+ const nameByte = symbol.properties.get("name_byte");
1332
+ const startByte = symbol.properties.get("start_byte");
1333
+ let column = 1;
1334
+ if (typeof definitionColumn === "number") {
1335
+ column = definitionColumn;
1336
+ } else if (typeof nameByte === "number" && typeof startByte === "number") {
1337
+ column = nameByte - startByte + 1;
1338
+ }
1339
+ return {
1340
+ file: requireProperty(definition, "file"),
1341
+ line: parsedLine.line,
1342
+ column,
1343
+ line_hash: parsedLine.hash,
1344
+ text: parsedLine.text,
1345
+ kind: symbolProperties.kind,
1346
+ name: symbolProperties.name,
1347
+ qualified_name: symbolProperties.qualified_name
1348
+ };
1349
+ })
1350
+ };
1351
+ }
1352
+ function edit(root) {
1353
+ const result = properties(root);
1354
+ for (const name of ["before_content", "content"]) {
1355
+ const contentNode = child(root, name);
1356
+ if (contentNode)
1357
+ result[name] = requireProperty(contentNode, "content");
1358
+ }
1359
+ return result;
1360
+ }
1361
+ function renameEdit(node) {
1362
+ const sourceLine = child(node, "line");
1363
+ if (!sourceLine)
1364
+ throw new Error("invalid readseek KDL rename edit: missing line");
1365
+ return {
1366
+ ...properties(node),
1367
+ text: requireProperty(sourceLine, "content")
1368
+ };
1369
+ }
1370
+ function renameFile(node) {
1371
+ return {
1372
+ ...properties(node),
1373
+ edits: children(node, "edit").map(renameEdit),
1374
+ conflicts: children(node, "conflict").map(properties)
1375
+ };
1376
+ }
1377
+ function rename(root) {
1378
+ return {
1379
+ ...renameFile(root),
1380
+ others: children(root, "other").map(renameFile)
1381
+ };
1382
+ }
1383
+ function genericNode(node) {
1384
+ const result = properties(node);
1385
+ const textNode = child(node, "text");
1386
+ const bbox = child(node, "bbox");
1387
+ if (textNode)
1388
+ result.text = requireProperty(textNode, "content");
1389
+ if (bbox)
1390
+ result.bbox = properties(bbox);
1391
+ const objects = children(node, "object");
1392
+ if (objects.length > 0)
1393
+ result.objects = objects.map(genericNode);
1394
+ return result;
1395
+ }
1396
+ function pdfDocument(root) {
1397
+ return {
1398
+ ...properties(root),
1399
+ nodes: children(root, "node").map(genericNode),
1400
+ assets: children(root, "asset").map(genericNode),
1401
+ vision: children(root, "vision").map(genericNode)
1402
+ };
1403
+ }
1404
+ function parseReadSeekKdl(text) {
1405
+ const root = parseKdlDocument(text);
1406
+ switch (root.name) {
1407
+ case "document":
1408
+ return root.properties.has("format") ? pdfDocument(root) : sourceDocument(root);
1409
+ case "search":
1410
+ return search(root);
1411
+ case "check":
1412
+ return diagnostics(root);
1413
+ case "refs":
1414
+ return references(root);
1415
+ case "def":
1416
+ return definitions(root);
1417
+ case "edit":
1418
+ return edit(root);
1419
+ case "edit-batch":
1420
+ return { ...properties(root), files: children(root, "file").map(edit) };
1421
+ case "rename":
1422
+ return rename(root);
1423
+ default:
1424
+ throw new Error(`unsupported readseek KDL root: ${root.name}`);
1016
1425
  }
1017
1426
  }
1018
1427
  // src/process.ts
1019
1428
  var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
1020
1429
  var DEFAULT_MAX_OUTPUT_BYTES = 32 * 1024 * 1024;
1430
+ function readSeekProcessEnvironment() {
1431
+ return process.platform === "linux" ? { ...process.env, APPIMAGE_EXTRACT_AND_RUN: "1" } : process.env;
1432
+ }
1021
1433
  function resolveProcessLimits(options = {}) {
1022
1434
  return {
1023
1435
  timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
@@ -1048,8 +1460,8 @@ function ensureSuccessfulStdout(result) {
1048
1460
  return result.stdout;
1049
1461
  throw failureFromProcessResult(result);
1050
1462
  }
1051
- function parseSuccessfulJson(result) {
1052
- return parseReadSeekJson(ensureSuccessfulStdout(result));
1463
+ function parseSuccessfulKdl(result) {
1464
+ return parseReadSeekKdl(ensureSuccessfulStdout(result));
1053
1465
  }
1054
1466
  // src/process-node.ts
1055
1467
  import { spawn } from "node:child_process";
@@ -1116,7 +1528,7 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
1116
1528
  try {
1117
1529
  child = spawn(binaryPath, [...args], {
1118
1530
  cwd: options.cwd,
1119
- env: process.env,
1531
+ env: readSeekProcessEnvironment(),
1120
1532
  shell: false,
1121
1533
  stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
1122
1534
  windowsHide: true
@@ -1159,17 +1571,17 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
1159
1571
  async function runReadSeekNodeText(binaryPath, args, options = {}) {
1160
1572
  return ensureSuccessfulStdout(await runReadSeekNode(binaryPath, args, options));
1161
1573
  }
1162
- async function runReadSeekNodeJson(binaryPath, args, options = {}) {
1163
- return parseSuccessfulJson(await runReadSeekNode(binaryPath, args, options));
1574
+ async function runReadSeekNodeKdl(binaryPath, args, options = {}) {
1575
+ return parseSuccessfulKdl(await runReadSeekNode(binaryPath, args, options));
1164
1576
  }
1165
1577
  // src/process-bun.ts
1166
1578
  function getBunSpawn() {
1167
1579
  const bunGlobal = globalThis;
1168
- const spawn2 = bunGlobal.Bun?.spawn;
1169
- if (typeof spawn2 !== "function") {
1580
+ const spawn = bunGlobal.Bun?.spawn;
1581
+ if (typeof spawn !== "function") {
1170
1582
  throw new Error("Bun.spawn is unavailable in this runtime");
1171
1583
  }
1172
- return spawn2;
1584
+ return spawn;
1173
1585
  }
1174
1586
  async function readBoundedText(stream, streamName, maxOutputBytes, onOverflow) {
1175
1587
  const reader = stream.getReader();
@@ -1206,16 +1618,16 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
1206
1618
  if (limits.cancelable && options.signal?.aborted) {
1207
1619
  throw readSeekCancellationError(options.signal);
1208
1620
  }
1209
- const spawn2 = getBunSpawn();
1621
+ const spawn = getBunSpawn();
1210
1622
  const stdin = options.stdin === undefined ? undefined : new TextEncoder().encode(options.stdin);
1211
1623
  const spawnOptions = {
1212
1624
  cwd: options.cwd,
1213
- env: process.env,
1625
+ env: readSeekProcessEnvironment(),
1214
1626
  stderr: "pipe",
1215
1627
  stdin,
1216
1628
  stdout: "pipe"
1217
1629
  };
1218
- const child = spawn2([binaryPath, ...args], limits.cancelable && options.signal ? { ...spawnOptions, killSignal: "SIGKILL", signal: options.signal } : spawnOptions);
1630
+ const child = spawn([binaryPath, ...args], limits.cancelable && options.signal ? { ...spawnOptions, killSignal: "SIGKILL", signal: options.signal } : spawnOptions);
1219
1631
  const killForOverflow = () => {
1220
1632
  try {
1221
1633
  child.kill("SIGKILL");
@@ -1244,368 +1656,374 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
1244
1656
  } finally {
1245
1657
  if (timeout !== undefined)
1246
1658
  clearTimeout(timeout);
1659
+ pending.catch(() => {
1660
+ return;
1661
+ });
1247
1662
  }
1248
1663
  }
1249
1664
  async function runReadSeekBunText(binaryPath, args, options = {}) {
1250
1665
  return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options));
1251
1666
  }
1252
- async function runReadSeekBunJson(binaryPath, args, options = {}) {
1253
- return parseSuccessfulJson(await runReadSeekBun(binaryPath, args, options));
1667
+ async function runReadSeekBunKdl(binaryPath, args, options = {}) {
1668
+ return parseSuccessfulKdl(await runReadSeekBun(binaryPath, args, options));
1254
1669
  }
1255
- // src/client.ts
1256
- function classifyReadSeekFailure(err) {
1257
- const failure = err;
1258
- const message = String(failure?.message ?? err);
1259
- const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
1260
- if (missing) {
1261
- return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
1670
+ // src/client-output.ts
1671
+ import { Type } from "@sinclair/typebox";
1672
+ import { AssertError, Value } from "@sinclair/typebox/value";
1673
+ var OPERATIONS = ["Clone", "Clean", "Default", "Assert"];
1674
+ function parse2(schema, value, what) {
1675
+ try {
1676
+ return Value.Parse(OPERATIONS, schema, value);
1677
+ } catch (error) {
1678
+ if (!(error instanceof AssertError))
1679
+ throw error;
1680
+ const detail = error.error;
1681
+ throw new Error(`invalid readseek ${what}${detail?.path ?? ""}: ${detail?.message ?? "invalid value"}`);
1262
1682
  }
1263
- return { code: "readseek-execution-error", message };
1264
- }
1265
- function requireNumber(value, field) {
1266
- if (typeof value !== "number" || !Number.isSafeInteger(value))
1267
- throw new Error(`invalid readseek ${field}: expected safe integer`);
1268
- return value;
1269
- }
1270
- function requireString(value, field) {
1271
- if (typeof value !== "string")
1272
- throw new Error(`invalid readseek ${field}`);
1273
- return value;
1274
- }
1275
- function requireBoolean(value, field) {
1276
- if (typeof value !== "boolean")
1277
- throw new Error(`invalid readseek ${field}: expected boolean`);
1278
- return value;
1279
- }
1280
- function optionalString(value, field) {
1281
- if (value === undefined || value === null)
1282
- return;
1283
- return requireString(value, field);
1284
- }
1285
- function parseHashline(value, field) {
1286
- if (!value || typeof value !== "object")
1287
- throw new Error(`invalid readseek ${field}`);
1288
- const item = value;
1289
- return {
1290
- line: requireNumber(item.line, `${field}.line`),
1291
- hash: requireString(item.hash, `${field}.hash`),
1292
- text: requireString(item.text, `${field}.text`)
1293
- };
1294
1683
  }
1295
- function parseSearchHashlines(value, field) {
1296
- if (!Array.isArray(value))
1297
- throw new Error(`invalid readseek ${field}`);
1298
- return value.map((line) => parseHashline(line, field));
1684
+ var Int = Type.Integer({ minimum: Number.MIN_SAFE_INTEGER, maximum: Number.MAX_SAFE_INTEGER });
1685
+ var Str = Type.String();
1686
+ var Bool = Type.Boolean();
1687
+ var OptStr = Type.Optional(Str);
1688
+ var OptInt = Type.Optional(Int);
1689
+ var Hashline = Type.Object({ line: Int, hash: Str, text: Str });
1690
+ var Hashlines = Type.Array(Hashline);
1691
+ var ReadOutput = Type.Object({
1692
+ file: Str,
1693
+ language: Str,
1694
+ line_count: Int,
1695
+ file_hash: Str,
1696
+ start_line: Int,
1697
+ end_line: Int,
1698
+ hashlines: Hashlines
1699
+ });
1700
+ var SymbolOutput = Type.Object({
1701
+ kind: Str,
1702
+ name: Str,
1703
+ qualified_name: OptStr,
1704
+ start_line: Int,
1705
+ end_line: Int,
1706
+ start_hash: OptStr,
1707
+ end_hash: OptStr,
1708
+ start_anchor: OptStr,
1709
+ end_anchor: OptStr
1710
+ });
1711
+ function mapNodeShape(child) {
1712
+ return Type.Object({
1713
+ kind: Str,
1714
+ name: Str,
1715
+ start_line: Int,
1716
+ end_line: Int,
1717
+ start_anchor: Str,
1718
+ end_anchor: Str,
1719
+ children: Type.Array(child)
1720
+ });
1299
1721
  }
1722
+ function recursiveMapNode() {
1723
+ return Type.Recursive((self) => mapNodeShape(self));
1724
+ }
1725
+ var MapNode = typeof Type.Recursive === "function" ? recursiveMapNode() : Type.Cyclic({ MapNode: mapNodeShape(Type.Ref("MapNode")) }, "MapNode");
1726
+ var MapOutput = Type.Object({
1727
+ file: Str,
1728
+ language: Str,
1729
+ line_count: Int,
1730
+ file_hash: Str,
1731
+ root: Type.Optional(MapNode),
1732
+ symbols: Type.Array(SymbolOutput)
1733
+ });
1734
+ var SearchSpan = {
1735
+ start_byte: OptInt,
1736
+ end_byte: OptInt,
1737
+ text: OptStr,
1738
+ start_line: Int,
1739
+ end_line: Int,
1740
+ start_hash: Str,
1741
+ end_hash: Str,
1742
+ hashlines: Hashlines
1743
+ };
1744
+ var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
1745
+ var SearchMatch = Type.Object({
1746
+ pattern_index: Type.Integer({ default: 0 }),
1747
+ ...SearchSpan,
1748
+ captures: Type.Array(SearchCapture)
1749
+ });
1750
+ var SearchFileOutput = Type.Object({
1751
+ file: Str,
1752
+ language: Str,
1753
+ file_hash: Str,
1754
+ matches: Type.Array(SearchMatch)
1755
+ });
1756
+ var SearchEnvelope = Type.Object({
1757
+ results: Type.Array(SearchFileOutput),
1758
+ returned_matches: OptInt,
1759
+ truncated: Type.Optional(Bool),
1760
+ byte_basis: OptStr
1761
+ });
1762
+ var RefsEnvelope = Type.Object({
1763
+ references: Type.Array(Type.Object({
1764
+ file: Str,
1765
+ line: Int,
1766
+ column: Int,
1767
+ line_hash: Str,
1768
+ text: Str,
1769
+ symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
1770
+ }))
1771
+ });
1772
+ var Diagnostic = Type.Object({
1773
+ kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
1774
+ start_line: Int,
1775
+ end_line: Int
1776
+ });
1777
+ var CheckEnvelope = Type.Object({
1778
+ file_hash: OptStr,
1779
+ error_count: Int,
1780
+ missing_count: Int,
1781
+ diagnostics: Type.Array(Diagnostic)
1782
+ });
1783
+ var DetectedObject = Type.Object({
1784
+ label: Str,
1785
+ bbox: Type.Tuple([Int, Int, Int, Int])
1786
+ });
1787
+ var PreparedImage = Type.Object({
1788
+ mime: Str,
1789
+ encoding: Type.Literal("base64"),
1790
+ data: Str
1791
+ });
1792
+ var SourceDetection = Type.Object({
1793
+ kind: Type.Literal("source"),
1794
+ type: Str,
1795
+ file: Str,
1796
+ language: Str,
1797
+ engine: OptStr,
1798
+ supported: Bool,
1799
+ mime: OptStr,
1800
+ syntax: OptStr
1801
+ });
1802
+ var ImageDetection = Type.Object({
1803
+ kind: Type.Literal("image"),
1804
+ type: Str,
1805
+ file: Str,
1806
+ mime: OptStr,
1807
+ format: Str,
1808
+ width: Int,
1809
+ height: Int,
1810
+ animated: Bool,
1811
+ encoding: Type.Optional(Type.Literal("base64")),
1812
+ data: OptStr,
1813
+ ocr: OptStr,
1814
+ caption: OptStr,
1815
+ objects: Type.Optional(Type.Array(DetectedObject))
1816
+ });
1817
+ var TextDetection = Type.Object({
1818
+ kind: Type.Literal("text"),
1819
+ type: Str,
1820
+ file: Str,
1821
+ mime: OptStr
1822
+ });
1823
+ var PdfDetection = Type.Object({
1824
+ kind: Type.Literal("pdf"),
1825
+ type: Type.Literal("application/pdf"),
1826
+ file: Str,
1827
+ mime: OptStr,
1828
+ format: Type.Literal("pdf"),
1829
+ pages: Int
1830
+ });
1831
+ var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
1832
+ var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
1833
+ var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
1834
+ var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
1835
+ var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
1836
+ var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
1837
+ var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
1838
+ var DocumentCache = Type.Object({
1839
+ content_hash: Str,
1840
+ state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
1841
+ });
1842
+ var Identifier = Type.Object({
1843
+ text: Str,
1844
+ start_column: Int,
1845
+ end_column: Int,
1846
+ start_byte: Int,
1847
+ end_byte: Int
1848
+ });
1849
+ var IdentifyEnvelope = Type.Object({
1850
+ file: Str,
1851
+ language: Str,
1852
+ engine: OptStr,
1853
+ line_count: Int,
1854
+ file_hash: Str,
1855
+ line: Int,
1856
+ column: Int,
1857
+ line_hash: Str,
1858
+ identifier: Type.Optional(Identifier),
1859
+ symbol: Type.Optional(Type.Object({
1860
+ name: Str,
1861
+ kind: Str,
1862
+ qualified_name: Str,
1863
+ start_line: Int,
1864
+ end_line: Int
1865
+ }))
1866
+ });
1867
+ var EditOutputSchema = Type.Object({
1868
+ file: Str,
1869
+ before_hash: Str,
1870
+ file_hash: Str,
1871
+ plan_hash: Str,
1872
+ applied: Bool,
1873
+ changed: Bool,
1874
+ edit_count: Int,
1875
+ before_content: OptStr,
1876
+ content: OptStr
1877
+ });
1878
+ var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
1879
+ var RenameEditSchema = Type.Object({
1880
+ line: Int,
1881
+ start_column: Int,
1882
+ end_column: Int,
1883
+ start_byte: Int,
1884
+ end_byte: Int,
1885
+ occurrence: Str,
1886
+ line_hash: Str,
1887
+ text: Str
1888
+ });
1889
+ var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
1890
+ var Edits = Type.Array(RenameEditSchema, { default: [] });
1891
+ var RenameFileOutputSchema = Type.Object({
1892
+ file: Str,
1893
+ language: Str,
1894
+ engine: OptStr,
1895
+ file_hash: Str,
1896
+ conflicts: Conflicts,
1897
+ edits: Edits
1898
+ });
1899
+ var RenameOutputSchema = Type.Object({
1900
+ file: Str,
1901
+ language: Str,
1902
+ engine: OptStr,
1903
+ file_hash: Str,
1904
+ old_name: Str,
1905
+ new_name: Str,
1906
+ plan_hash: Str,
1907
+ applied: Bool,
1908
+ conflicts: Conflicts,
1909
+ edits: Edits,
1910
+ others: Type.Array(RenameFileOutputSchema, { default: [] })
1911
+ });
1912
+ var DefEnvelope = Type.Object({
1913
+ locations: Type.Array(Type.Object({
1914
+ file: Str,
1915
+ line: Int,
1916
+ column: Int,
1917
+ line_hash: Str,
1918
+ text: Str,
1919
+ kind: OptStr,
1920
+ name: OptStr,
1921
+ qualified_name: OptStr
1922
+ }))
1923
+ });
1300
1924
  function parseReadOutput(value) {
1301
- if (!value || typeof value !== "object")
1302
- throw new Error("invalid readseek read output");
1303
- const output = value;
1304
- const hashlines = output.hashlines;
1305
- if (!Array.isArray(hashlines))
1306
- throw new Error("invalid readseek hashlines");
1307
- return {
1308
- file: requireString(output.file, "file"),
1309
- language: requireString(output.language, "language"),
1310
- line_count: requireNumber(output.line_count, "line_count"),
1311
- file_hash: requireString(output.file_hash, "file_hash"),
1312
- start_line: requireNumber(output.start_line, "start_line"),
1313
- end_line: requireNumber(output.end_line, "end_line"),
1314
- hashlines: hashlines.map((line) => parseHashline(line, "hashline"))
1315
- };
1316
- }
1317
- function parseMapNode(value) {
1318
- if (!value || typeof value !== "object")
1319
- throw new Error("invalid readseek map node");
1320
- const node = value;
1321
- if (!Array.isArray(node.children))
1322
- throw new Error("invalid readseek map node children");
1323
- return {
1324
- kind: requireString(node.kind, "map node.kind"),
1325
- name: requireString(node.name, "map node.name"),
1326
- start_line: requireNumber(node.start_line, "map node.start_line"),
1327
- end_line: requireNumber(node.end_line, "map node.end_line"),
1328
- start_anchor: requireString(node.start_anchor, "map node.start_anchor"),
1329
- end_anchor: requireString(node.end_anchor, "map node.end_anchor"),
1330
- children: node.children.map(parseMapNode)
1331
- };
1925
+ return parse2(ReadOutput, value, "read output");
1332
1926
  }
1333
1927
  function parseMapOutput(value) {
1334
- if (!value || typeof value !== "object")
1335
- throw new Error("invalid readseek map output");
1336
- const output = value;
1337
- const symbols = output.symbols;
1338
- if (!Array.isArray(symbols))
1339
- throw new Error("invalid readseek symbols");
1340
- return {
1341
- file: requireString(output.file, "file"),
1342
- language: requireString(output.language, "language"),
1343
- line_count: requireNumber(output.line_count, "line_count"),
1344
- file_hash: requireString(output.file_hash, "file_hash"),
1345
- root: parseMapNode(output.root),
1346
- symbols: symbols.map((symbol) => {
1347
- if (!symbol || typeof symbol !== "object")
1348
- throw new Error("invalid readseek symbol");
1349
- const item = symbol;
1350
- return {
1351
- kind: requireString(item.kind, "symbol.kind"),
1352
- name: requireString(item.name, "symbol.name"),
1353
- qualified_name: requireString(item.qualified_name, "symbol.qualified_name"),
1354
- start_line: requireNumber(item.start_line, "symbol.start_line"),
1355
- end_line: requireNumber(item.end_line, "symbol.end_line"),
1356
- start_hash: requireString(item.start_hash, "symbol.start_hash"),
1357
- end_hash: requireString(item.end_hash, "symbol.end_hash")
1358
- };
1359
- })
1360
- };
1928
+ return parse2(MapOutput, value, "map output");
1361
1929
  }
1362
1930
  function parseSearchOutput(value) {
1363
- if (!value || typeof value !== "object")
1364
- throw new Error("invalid readseek search output");
1365
- const output = value;
1366
- if (!Array.isArray(output.results))
1367
- throw new Error("invalid readseek search results");
1368
- return output.results.map((result) => {
1369
- if (!result || typeof result !== "object")
1370
- throw new Error("invalid readseek search result");
1371
- const file = result;
1372
- if (!Array.isArray(file.matches))
1373
- throw new Error("invalid readseek search matches");
1374
- return {
1375
- file: requireString(file.file, "search.file"),
1376
- language: requireString(file.language, "search.language"),
1377
- file_hash: requireString(file.file_hash, "search.file_hash"),
1378
- matches: file.matches.map((match) => {
1379
- if (!match || typeof match !== "object")
1380
- throw new Error("invalid readseek search match");
1381
- const item = match;
1382
- if (!Array.isArray(item.captures))
1383
- throw new Error("invalid readseek search captures");
1384
- return {
1385
- pattern_index: item.pattern_index === undefined ? 0 : requireNumber(item.pattern_index, "search.match.pattern_index"),
1386
- start_line: requireNumber(item.start_line, "search.match.start_line"),
1387
- end_line: requireNumber(item.end_line, "search.match.end_line"),
1388
- start_hash: requireString(item.start_hash, "search.match.start_hash"),
1389
- end_hash: requireString(item.end_hash, "search.match.end_hash"),
1390
- hashlines: parseSearchHashlines(item.hashlines, "search.match.hashlines"),
1391
- captures: item.captures.map((capture) => {
1392
- if (!capture || typeof capture !== "object")
1393
- throw new Error("invalid readseek search capture");
1394
- const captureItem = capture;
1395
- return {
1396
- name: requireString(captureItem.name, "search.capture.name"),
1397
- start_line: requireNumber(captureItem.start_line, "search.capture.start_line"),
1398
- end_line: requireNumber(captureItem.end_line, "search.capture.end_line"),
1399
- start_hash: requireString(captureItem.start_hash, "search.capture.start_hash"),
1400
- end_hash: requireString(captureItem.end_hash, "search.capture.end_hash"),
1401
- hashlines: parseSearchHashlines(captureItem.hashlines, "search.capture.hashlines")
1402
- };
1403
- })
1404
- };
1405
- })
1406
- };
1407
- });
1931
+ const envelope = parse2(SearchEnvelope, value, "search output");
1932
+ const results = envelope.results;
1933
+ if (envelope.returned_matches !== undefined)
1934
+ results.returned_matches = envelope.returned_matches;
1935
+ if (envelope.truncated !== undefined)
1936
+ results.truncated = envelope.truncated;
1937
+ if (envelope.byte_basis !== undefined)
1938
+ results.byte_basis = envelope.byte_basis;
1939
+ return results;
1408
1940
  }
1409
1941
  function parseRefsOutput(value) {
1410
- if (!value || typeof value !== "object")
1411
- throw new Error("invalid readseek refs output");
1412
- const output = value;
1413
- if (!Array.isArray(output.references))
1414
- throw new Error("invalid readseek references");
1415
- return output.references.map((reference) => {
1416
- if (!reference || typeof reference !== "object")
1417
- throw new Error("invalid readseek reference");
1418
- const item = reference;
1419
- const symbol = item.symbol;
1420
- const enclosing = symbol && typeof symbol === "object" ? optionalString(symbol.qualified_name, "reference.symbol.qualified_name") : undefined;
1421
- return {
1422
- file: requireString(item.file, "reference.file"),
1423
- line: requireNumber(item.line, "reference.line"),
1424
- column: requireNumber(item.column, "reference.column"),
1425
- line_hash: requireString(item.line_hash, "reference.line_hash"),
1426
- text: requireString(item.text, "reference.text"),
1427
- enclosingSymbol: enclosing
1428
- };
1429
- });
1430
- }
1431
- function parseDiagnosticKind(value) {
1432
- if (value === "error" || value === "missing")
1433
- return value;
1434
- throw new Error("invalid readseek diagnostic.kind");
1942
+ return parse2(RefsEnvelope, value, "refs output").references.map(({ symbol, ...reference }) => ({
1943
+ ...reference,
1944
+ enclosingSymbol: symbol?.qualified_name
1945
+ }));
1435
1946
  }
1436
1947
  function parseCheckOutput(value) {
1437
- if (!value || typeof value !== "object")
1438
- throw new Error("invalid readseek check output");
1439
- const output = value;
1440
- if (!Array.isArray(output.diagnostics))
1441
- throw new Error("invalid readseek diagnostics");
1948
+ const output = parse2(CheckEnvelope, value, "check output");
1442
1949
  return {
1443
- file_hash: optionalString(output.file_hash, "file_hash"),
1444
- errorCount: requireNumber(output.error_count, "error_count"),
1445
- missingCount: requireNumber(output.missing_count, "missing_count"),
1446
- diagnostics: output.diagnostics.map((diagnostic) => {
1447
- if (!diagnostic || typeof diagnostic !== "object")
1448
- throw new Error("invalid readseek diagnostic");
1449
- const item = diagnostic;
1450
- return {
1451
- kind: parseDiagnosticKind(item.kind),
1452
- start_line: requireNumber(item.start_line, "diagnostic.start_line"),
1453
- end_line: requireNumber(item.end_line, "diagnostic.end_line")
1454
- };
1455
- })
1950
+ file_hash: output.file_hash,
1951
+ errorCount: output.error_count,
1952
+ missingCount: output.missing_count,
1953
+ diagnostics: output.diagnostics
1456
1954
  };
1457
1955
  }
1458
- function parseDetectedObjects(value) {
1459
- if (value === undefined || value === null)
1460
- return;
1461
- if (!Array.isArray(value))
1462
- throw new Error("invalid readseek detect objects");
1463
- return value.map((object) => {
1464
- if (!object || typeof object !== "object")
1465
- throw new Error("invalid readseek detect object");
1466
- const item = object;
1467
- const bbox = item.bbox;
1468
- if (!Array.isArray(bbox) || bbox.length !== 4)
1469
- throw new Error("invalid readseek detect object.bbox");
1470
- return {
1471
- label: requireString(item.label, "object.label"),
1472
- bbox: bbox.map((n, i) => requireNumber(n, `object.bbox[${i}]`))
1473
- };
1474
- });
1475
- }
1476
- function parseImageEncoding(value) {
1477
- if (value === "base64")
1478
- return value;
1479
- throw new Error("invalid image encoding");
1480
- }
1481
1956
  function parseDetectOutput(value) {
1482
- if (!value || typeof value !== "object")
1483
- throw new Error("invalid readseek detect output");
1484
- const output = value;
1485
- const type = requireString(output.type, "type");
1486
- const file = requireString(output.file, "file");
1487
- const mime = optionalString(output.mime, "mime");
1488
- if (type === "application/pdf") {
1489
- if (output.format !== "pdf")
1490
- throw new Error("invalid readseek PDF format");
1491
- return {
1492
- kind: "pdf",
1493
- type,
1494
- file,
1495
- mime,
1496
- format: output.format,
1497
- pages: requireNumber(output.pages, "pages")
1498
- };
1957
+ const output = value ?? {};
1958
+ if (output.type === "application/pdf") {
1959
+ return { kind: "pdf", ...parse2(PdfDetectionInput, value, "detect output") };
1499
1960
  }
1500
1961
  if (output.width !== undefined || output.height !== undefined) {
1501
- return {
1502
- kind: "image",
1503
- type,
1504
- file,
1505
- mime,
1506
- format: requireString(output.format, "format"),
1507
- width: requireNumber(output.width, "width"),
1508
- height: requireNumber(output.height, "height"),
1509
- animated: requireBoolean(output.animated, "animated"),
1510
- encoding: output.encoding === undefined ? undefined : parseImageEncoding(output.encoding),
1511
- data: output.data === undefined ? undefined : requireString(output.data, "data"),
1512
- ocr: optionalString(output.ocr, "ocr"),
1513
- caption: optionalString(output.caption, "caption"),
1514
- objects: parseDetectedObjects(output.objects)
1515
- };
1962
+ return { kind: "image", ...parse2(ImageDetectionInput, value, "detect output") };
1516
1963
  }
1517
1964
  if (output.language !== undefined) {
1518
- return {
1519
- kind: "source",
1520
- type,
1521
- file,
1522
- language: requireString(output.language, "language"),
1523
- engine: optionalString(output.engine, "engine"),
1524
- supported: requireBoolean(output.supported, "supported"),
1525
- mime,
1526
- syntax: optionalString(output.syntax, "syntax")
1527
- };
1965
+ return { kind: "source", ...parse2(SourceDetectionInput, value, "detect output") };
1528
1966
  }
1529
- return { kind: "text", type, file, mime };
1530
- }
1531
- function parseDocumentCache(value) {
1532
- if (!value || typeof value !== "object")
1533
- throw new Error("invalid readseek document cache");
1534
- const cache = value;
1535
- const state = requireString(cache.state, "cache.state");
1536
- if (state !== "built" && state !== "reused")
1537
- throw new Error("invalid readseek cache.state");
1538
- return {
1539
- content_hash: requireString(cache.content_hash, "cache.content_hash"),
1540
- state
1541
- };
1542
- }
1543
- function parseIdentifyOutput(value) {
1544
- if (!value || typeof value !== "object")
1545
- throw new Error("invalid readseek identify output");
1546
- const output = value;
1547
- const identifier = output.identifier;
1548
- const symbol = output.symbol;
1549
- return {
1550
- file: requireString(output.file, "file"),
1551
- language: requireString(output.language, "language"),
1552
- engine: optionalString(output.engine, "engine"),
1553
- line_count: requireNumber(output.line_count, "line_count"),
1554
- file_hash: requireString(output.file_hash, "file_hash"),
1555
- line: requireNumber(output.line, "line"),
1556
- column: requireNumber(output.column, "column"),
1557
- line_hash: requireString(output.line_hash, "line_hash"),
1558
- identifier: identifier && typeof identifier === "object" ? {
1559
- text: requireString(identifier.text, "identifier.text"),
1560
- start_column: requireNumber(identifier.start_column, "identifier.start_column"),
1561
- end_column: requireNumber(identifier.end_column, "identifier.end_column"),
1562
- start_byte: requireNumber(identifier.start_byte, "identifier.start_byte"),
1563
- end_byte: requireNumber(identifier.end_byte, "identifier.end_byte")
1564
- } : undefined,
1565
- symbol: symbol && typeof symbol === "object" ? {
1566
- name: requireString(symbol.name, "symbol.name"),
1567
- kind: requireString(symbol.kind, "symbol.kind"),
1568
- qualified_name: requireString(symbol.qualified_name, "symbol.qualified_name"),
1569
- start_line: requireNumber(symbol.start_line, "symbol.start_line"),
1570
- end_line: requireNumber(symbol.end_line, "symbol.end_line")
1571
- } : undefined
1572
- };
1967
+ return { kind: "text", ...parse2(TextDetectionInput, value, "detect output") };
1573
1968
  }
1574
1969
  function parseImageContent(value) {
1575
1970
  const detection = parseDetectOutput(value);
1576
1971
  if (detection.kind !== "image")
1577
- throw new Error("invalid readseek image content");
1972
+ throw new Error("invalid readseek image content: expected an image");
1578
1973
  return detection;
1579
1974
  }
1580
1975
  function parseTextContent(value) {
1976
+ const output = value ?? {};
1977
+ if (output.hashlines === undefined || output.start_line !== undefined)
1978
+ return parseReadOutput(value);
1979
+ const spanned = parse2(UnspannedContent, value, "content");
1980
+ return {
1981
+ ...spanned,
1982
+ start_line: spanned.hashlines[0]?.line ?? 1,
1983
+ end_line: spanned.hashlines[spanned.hashlines.length - 1]?.line ?? 0
1984
+ };
1985
+ }
1986
+ function parseDocumentCache(value) {
1987
+ return parse2(DocumentCache, value, "document cache");
1988
+ }
1989
+ function parseIdentifyOutput(value) {
1990
+ return parse2(IdentifyEnvelope, value, "identify output");
1991
+ }
1992
+ function parsePdfInspectMetadata(value) {
1581
1993
  if (!value || typeof value !== "object")
1582
- throw new Error("invalid readseek content");
1583
- const output = value;
1584
- if (output.hashlines !== undefined && output.start_line === undefined) {
1585
- const hashlines = parseSearchHashlines(output.hashlines, "content.hashlines");
1586
- const first = hashlines[0];
1587
- const last = hashlines[hashlines.length - 1];
1588
- return {
1589
- file: requireString(output.file, "file"),
1590
- language: requireString(output.language, "language"),
1591
- line_count: requireNumber(output.line_count, "line_count"),
1592
- file_hash: requireString(output.file_hash, "file_hash"),
1593
- start_line: first?.line ?? 1,
1594
- end_line: last?.line ?? 0,
1595
- hashlines
1596
- };
1994
+ return;
1995
+ if (value.format !== "pdf")
1996
+ return;
1997
+ const document = parse2(PdfInspectMetadata, value, "inspect metadata");
1998
+ return { kind: "pdf", type: "application/pdf", file: document.source, format: "pdf", pages: document.pages };
1999
+ }
2000
+ function parseEditOutput(value) {
2001
+ return parse2(EditOutputSchema, value, "edit output");
2002
+ }
2003
+ function parseRenameOutput(value) {
2004
+ return parse2(RenameOutputSchema, value, "rename output");
2005
+ }
2006
+ function parseDefOutput(value) {
2007
+ return parse2(DefEnvelope, value, "def output").locations;
2008
+ }
2009
+
2010
+ // src/client.ts
2011
+ function classifyReadSeekFailure(err) {
2012
+ const failure = err;
2013
+ const message = String(failure?.message ?? err);
2014
+ const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
2015
+ if (missing) {
2016
+ return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
1597
2017
  }
1598
- return parseReadOutput(value);
2018
+ return { code: "readseek-execution-error", message };
1599
2019
  }
1600
- function parseDigestEnvelope(value) {
2020
+ function parseSourceInspectEnvelope(value) {
1601
2021
  if (!value || typeof value !== "object")
1602
- throw new Error("invalid readseek digest output");
2022
+ throw new Error("invalid readseek inspect output");
1603
2023
  const output = value;
1604
- if (output.metadata === undefined)
1605
- throw new Error("invalid readseek digest metadata");
1606
- const result = {
1607
- metadata: parseDetectOutput(output.metadata)
1608
- };
2024
+ const result = {};
2025
+ if (output.metadata !== undefined)
2026
+ result.metadata = parseDetectOutput(output.metadata);
1609
2027
  if (output.cache !== undefined)
1610
2028
  result.cache = parseDocumentCache(output.cache);
1611
2029
  if (output.content !== undefined)
@@ -1618,224 +2036,104 @@ function parseDigestEnvelope(value) {
1618
2036
  result.identity = parseIdentifyOutput(output.identity);
1619
2037
  return result;
1620
2038
  }
1621
- function parseEditOutput(value) {
1622
- if (!value || typeof value !== "object")
1623
- throw new Error("invalid readseek edit output");
1624
- const output = value;
1625
- return {
1626
- file: requireString(output.file, "file"),
1627
- before_hash: requireString(output.before_hash, "before_hash"),
1628
- file_hash: requireString(output.file_hash, "file_hash"),
1629
- plan_hash: requireString(output.plan_hash, "plan_hash"),
1630
- applied: requireBoolean(output.applied, "applied"),
1631
- changed: requireBoolean(output.changed, "changed"),
1632
- edit_count: requireNumber(output.edit_count, "edit_count"),
1633
- before_content: optionalString(output.before_content, "before_content"),
1634
- content: optionalString(output.content, "content")
1635
- };
1636
- }
1637
- function parseRenameConflicts(value, field) {
1638
- if (value === undefined)
1639
- return [];
1640
- if (!Array.isArray(value))
1641
- throw new Error(`invalid readseek ${field}`);
1642
- return value.map((item) => {
1643
- if (!item || typeof item !== "object")
1644
- throw new Error(`invalid readseek ${field} entry`);
1645
- const c = item;
1646
- return {
1647
- line: requireNumber(c.line, `${field}.line`),
1648
- column: requireNumber(c.column, `${field}.column`),
1649
- reason: requireString(c.reason, `${field}.reason`)
1650
- };
1651
- });
1652
- }
1653
- function parseRenameEdits(value, field) {
1654
- if (value === undefined)
1655
- return [];
1656
- if (!Array.isArray(value))
1657
- throw new Error(`invalid readseek ${field} entry`);
1658
- return value.map((item) => {
1659
- if (!item || typeof item !== "object")
1660
- throw new Error(`invalid readseek ${field} entry`);
1661
- const e = item;
1662
- return {
1663
- line: requireNumber(e.line, `${field}.line`),
1664
- start_column: requireNumber(e.start_column, `${field}.start_column`),
1665
- end_column: requireNumber(e.end_column, `${field}.end_column`),
1666
- start_byte: requireNumber(e.start_byte, `${field}.start_byte`),
1667
- end_byte: requireNumber(e.end_byte, `${field}.end_byte`),
1668
- occurrence: requireString(e.occurrence, `${field}.occurrence`),
1669
- line_hash: requireString(e.line_hash, `${field}.line_hash`),
1670
- text: requireString(e.text, `${field}.text`)
1671
- };
1672
- });
1673
- }
1674
- function parseRenameOutput(value) {
1675
- if (!value || typeof value !== "object")
1676
- throw new Error("invalid readseek rename output");
1677
- const output = value;
1678
- const others = output.others;
1679
- if (others !== undefined && !Array.isArray(others))
1680
- throw new Error("invalid readseek others");
1681
- return {
1682
- file: requireString(output.file, "file"),
1683
- language: requireString(output.language, "language"),
1684
- engine: optionalString(output.engine, "engine"),
1685
- file_hash: requireString(output.file_hash, "file_hash"),
1686
- old_name: requireString(output.old_name, "old_name"),
1687
- new_name: requireString(output.new_name, "new_name"),
1688
- applied: requireBoolean(output.applied, "applied"),
1689
- conflicts: parseRenameConflicts(output.conflicts, "conflicts"),
1690
- edits: parseRenameEdits(output.edits, "edits"),
1691
- others: others?.map((entry) => {
1692
- if (!entry || typeof entry !== "object")
1693
- throw new Error("invalid readseek other");
1694
- const o = entry;
1695
- return {
1696
- file: requireString(o.file, "other.file"),
1697
- language: requireString(o.language, "other.language"),
1698
- engine: optionalString(o.engine, "other.engine"),
1699
- file_hash: requireString(o.file_hash, "other.file_hash"),
1700
- conflicts: parseRenameConflicts(o.conflicts, "other.conflicts"),
1701
- edits: parseRenameEdits(o.edits, "other.edits")
1702
- };
1703
- }) ?? []
1704
- };
1705
- }
1706
- function parseDefCompact(value) {
1707
- if (!value || typeof value !== "object")
1708
- throw new Error("invalid readseek def output");
1709
- const output = value;
1710
- const locations = output.locations;
1711
- if (!Array.isArray(locations))
1712
- throw new Error("invalid readseek def locations");
1713
- return locations.map((loc) => {
1714
- if (!loc || typeof loc !== "object")
1715
- throw new Error("invalid readseek def location");
1716
- const item = loc;
1717
- return {
1718
- file: requireString(item.file, "location.file"),
1719
- line: requireNumber(item.line, "location.line"),
1720
- column: requireNumber(item.column, "location.column"),
1721
- line_hash: requireString(item.line_hash, "location.line_hash"),
1722
- text: requireString(item.text, "location.text"),
1723
- kind: optionalString(item.kind, "location.kind"),
1724
- name: optionalString(item.name, "location.name"),
1725
- qualified_name: optionalString(item.qualified_name, "location.qualified_name")
1726
- };
1727
- });
1728
- }
1729
2039
  function createReadSeekClient(runner) {
1730
- function digestArgs(target, options) {
1731
- return buildDigestArgs({
2040
+ function inspectArgs(target, options) {
2041
+ return buildInspectArgs({
1732
2042
  path: target,
2043
+ selector: options.selector,
1733
2044
  select: options.select ?? "content",
1734
- at: options.at,
2045
+ query: options.query,
1735
2046
  end: options.end,
1736
2047
  limit: options.limit,
1737
2048
  depth: options.depth,
1738
2049
  language: options.language,
1739
2050
  visionMode: options.visionMode,
1740
2051
  visionLevel: options.visionLevel,
1741
- stdinName: options.stdin !== undefined && target === "-" ? options.stdinName ?? target : undefined
2052
+ stdinName: options.stdinName
1742
2053
  });
1743
2054
  }
1744
- async function runDigestRaw(target, options = {}) {
1745
- const args = digestArgs(target, options);
1746
- const runOptions = {
2055
+ function inspectRunOptions(options) {
2056
+ return {
1747
2057
  signal: options.signal,
1748
2058
  stdin: options.stdin,
1749
2059
  timeoutMs: options.timeoutMs,
1750
2060
  vision: options.vision
1751
2061
  };
1752
- return runner.json(args, runOptions);
1753
2062
  }
1754
- async function digest(target, options = {}) {
1755
- return parseDigestEnvelope(await runDigestRaw(target, options));
2063
+ async function inspect(target, options = {}) {
2064
+ return runner.kdl(inspectArgs(target, options), inspectRunOptions(options));
2065
+ }
2066
+ async function inspectText(target, options = {}) {
2067
+ return runner.text(inspectArgs(target, options), inspectRunOptions(options));
1756
2068
  }
1757
- function requireDigestContent(envelope, field) {
2069
+ async function inspectSource(target, options = {}) {
2070
+ return parseSourceInspectEnvelope(await inspect(target, options));
2071
+ }
2072
+ function requireInspectMetadata(envelope, field) {
2073
+ if (envelope.metadata === undefined)
2074
+ throw new Error(`invalid readseek ${field}: missing metadata`);
2075
+ return envelope.metadata;
2076
+ }
2077
+ function requireInspectContent(envelope, field) {
1758
2078
  if (envelope.content === undefined)
1759
2079
  throw new Error(`invalid readseek ${field}: missing content`);
1760
2080
  return envelope.content;
1761
2081
  }
1762
- async function digestContent(filePath, options = {}) {
1763
- const envelope = await digest(filePath, {
1764
- select: "content",
1765
- at: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
2082
+ async function inspectContent(filePath, options = {}) {
2083
+ const inspected = await inspect(filePath, {
2084
+ select: ["metadata", "content"],
2085
+ selector: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
1766
2086
  end: options.endLine,
1767
2087
  visionMode: options.visionMode,
1768
2088
  signal: options.signal,
1769
2089
  vision: options.vision
1770
2090
  });
1771
- if (envelope.metadata.kind === "pdf") {
1772
- return { metadata: envelope.metadata, cache: envelope.cache };
2091
+ const pdf = parsePdfInspectMetadata(inspected);
2092
+ if (pdf)
2093
+ return { metadata: pdf };
2094
+ const envelope = parseSourceInspectEnvelope(inspected);
2095
+ const metadata = requireInspectMetadata(envelope, "content");
2096
+ const content = requireInspectContent(envelope, "content");
2097
+ if (metadata.kind === "image") {
2098
+ return { metadata, image: parseImageContent(content) };
1773
2099
  }
1774
- const content = requireDigestContent(envelope, "content");
1775
- if (envelope.metadata.kind === "image") {
1776
- return { metadata: envelope.metadata, image: parseImageContent(content) };
1777
- }
1778
- return { metadata: envelope.metadata, text: parseTextContent(content) };
2100
+ return { metadata, text: parseTextContent(content) };
1779
2101
  }
1780
2102
  return {
1781
- async view(filePath, options = {}) {
1782
- const args = buildViewArgs({
1783
- path: filePath,
1784
- node: options.node,
1785
- page: options.page,
1786
- kind: options.kind,
1787
- depth: options.depth,
1788
- outline: options.outline,
1789
- visionMode: options.visionMode,
1790
- visionLevel: options.visionLevel
1791
- });
1792
- return runner.text(args, {
2103
+ inspect,
2104
+ inspectText,
2105
+ inspectContent,
2106
+ async extract(target, selector, output, options = {}) {
2107
+ await runner.text(buildExtractArgs({
2108
+ path: target,
2109
+ selector,
2110
+ output,
2111
+ stdinName: options.stdinName
2112
+ }), {
1793
2113
  signal: options.signal,
1794
- vision: options.visionMode !== undefined
2114
+ stdin: options.stdin,
2115
+ timeoutMs: options.timeoutMs,
2116
+ cancelable: options.cancelable
1795
2117
  });
1796
2118
  },
1797
- digestRaw(target, options = {}) {
1798
- return runDigestRaw(target, options);
1799
- },
1800
- digest,
1801
- digestContent,
1802
2119
  async image(filePath, modes, options = {}) {
1803
- const requestedModes = modes.length > 1 ? ["all"] : modes;
1804
- const results = await Promise.allSettled(requestedModes.map(async (mode) => {
1805
- const digested = await digestContent(filePath, {
1806
- visionMode: mode,
1807
- signal: options.signal,
1808
- vision: true
1809
- });
1810
- if (!digested.image)
1811
- throw new Error(`readseek returned no image analysis for ${filePath}`);
1812
- return digested.image;
1813
- }));
1814
- let merged;
1815
- for (const result of results) {
1816
- if (result.status !== "fulfilled")
1817
- continue;
1818
- const detection = result.value;
1819
- merged = merged === undefined ? detection : {
1820
- ...merged,
1821
- ocr: detection.ocr ?? merged.ocr,
1822
- caption: detection.caption ?? merged.caption,
1823
- objects: detection.objects ?? merged.objects
1824
- };
1825
- }
1826
- if (merged !== undefined)
1827
- return merged;
1828
- const failure = results.find((result) => result.status === "rejected");
1829
- if (failure?.status === "rejected")
1830
- throw failure.reason;
1831
- throw new Error(`readseek returned no image analysis for ${filePath}`);
2120
+ if (modes.length === 0)
2121
+ throw new Error(`readseek returned no image analysis for ${filePath}`);
2122
+ const inspected = await inspectContent(filePath, {
2123
+ visionMode: modes.length > 1 ? "all" : modes[0],
2124
+ signal: options.signal,
2125
+ vision: true
2126
+ });
2127
+ if (!inspected.image)
2128
+ throw new Error(`readseek returned no image analysis for ${filePath}`);
2129
+ return inspected.image;
1832
2130
  },
1833
2131
  async preparedImage(filePath, options = {}) {
1834
- const digested = await digestContent(filePath, {
2132
+ const inspected = await inspectContent(filePath, {
1835
2133
  visionMode: "none",
1836
2134
  signal: options.signal
1837
2135
  });
1838
- const output = digested.image;
2136
+ const output = inspected.image;
1839
2137
  if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
1840
2138
  throw new Error(`readseek returned no prepared image for ${filePath}`);
1841
2139
  }
@@ -1847,11 +2145,9 @@ function createReadSeekClient(runner) {
1847
2145
  pattern,
1848
2146
  language: options.language,
1849
2147
  limit: options.limit,
1850
- cached: options.cached,
1851
- others: options.others,
1852
- ignored: options.ignored
2148
+ gitScope: options.gitScope
1853
2149
  });
1854
- return parseSearchOutput(await runner.json(args, { signal: options.signal }));
2150
+ return parseSearchOutput(await runner.kdl(args, { signal: options.signal }));
1855
2151
  },
1856
2152
  async refs(target, options = {}) {
1857
2153
  const args = buildRefsArgs({
@@ -1861,26 +2157,21 @@ function createReadSeekClient(runner) {
1861
2157
  line: options.line,
1862
2158
  column: options.column,
1863
2159
  language: options.language,
1864
- cached: options.cached,
1865
- others: options.others,
1866
- ignored: options.ignored
2160
+ gitScope: options.gitScope
1867
2161
  });
1868
- return parseRefsOutput(await runner.json(args, { signal: options.signal }));
2162
+ return parseRefsOutput(await runner.kdl(args, { signal: options.signal }));
1869
2163
  },
1870
2164
  async def(target, options) {
1871
2165
  const args = buildDefArgs({
1872
2166
  path: target,
1873
2167
  name: options.name,
1874
- format: "plain",
1875
2168
  language: options.language,
1876
- cached: options.cached,
1877
- others: options.others,
1878
- ignored: options.ignored
2169
+ gitScope: options.gitScope
1879
2170
  });
1880
- return parseDefCompact(await runner.json(args, { signal: options.signal }));
2171
+ return parseDefOutput(await runner.kdl(args, { signal: options.signal }));
1881
2172
  },
1882
2173
  async check(filePath, content, options = {}) {
1883
- const envelope = await digest("-", {
2174
+ const envelope = await inspectSource("-", {
1884
2175
  select: "diagnostics",
1885
2176
  stdin: content,
1886
2177
  stdinName: filePath,
@@ -1890,12 +2181,12 @@ function createReadSeekClient(runner) {
1890
2181
  throw new Error("invalid readseek diagnostics: missing diagnostics facet");
1891
2182
  return envelope.diagnostics;
1892
2183
  },
1893
- async identify(filePath, content, options = {}) {
1894
- const envelope = await digest("-", {
2184
+ async identify(filePath, content, options) {
2185
+ const envelope = await inspectSource("-", {
1895
2186
  select: "identity",
1896
2187
  stdin: content,
1897
2188
  stdinName: filePath,
1898
- at: options.line !== undefined ? buildLineLocation(options.line, options.column) : undefined,
2189
+ selector: buildLineLocation(options.line, options.column),
1899
2190
  language: options.language,
1900
2191
  signal: options.signal
1901
2192
  });
@@ -1904,8 +2195,11 @@ function createReadSeekClient(runner) {
1904
2195
  return envelope.identity;
1905
2196
  },
1906
2197
  async detect(filePath, options = {}) {
1907
- const envelope = await digest(filePath, { select: "metadata", signal: options.signal });
1908
- return envelope.metadata;
2198
+ const inspected = await inspect(filePath, { select: "metadata", signal: options.signal });
2199
+ const pdf = parsePdfInspectMetadata(inspected);
2200
+ if (pdf)
2201
+ return pdf;
2202
+ return requireInspectMetadata(parseSourceInspectEnvelope(inspected), "metadata");
1909
2203
  },
1910
2204
  async edit(filePath, edits, options = {}) {
1911
2205
  const args = buildEditArgs({
@@ -1915,9 +2209,9 @@ function createReadSeekClient(runner) {
1915
2209
  language: options.language,
1916
2210
  requestFromStdin: true
1917
2211
  });
1918
- return parseEditOutput(await runner.json(args, {
2212
+ return parseEditOutput(await runner.kdl(args, {
1919
2213
  signal: options.signal,
1920
- stdin: JSON.stringify({ edits }),
2214
+ stdin: serializeEditPlan(edits),
1921
2215
  timeoutMs: options.timeoutMs,
1922
2216
  cancelable: options.cancelable
1923
2217
  }));
@@ -1930,89 +2224,88 @@ function createReadSeekClient(runner) {
1930
2224
  column: options.column,
1931
2225
  workspace: options.workspace,
1932
2226
  apply: options.apply,
2227
+ planHash: options.planHash,
1933
2228
  language: options.language,
1934
- cached: options.cached,
1935
- others: options.others,
1936
- ignored: options.ignored
2229
+ gitScope: options.gitScope
1937
2230
  });
1938
- return parseRenameOutput(await runner.json(args, { signal: options.signal }));
2231
+ return parseRenameOutput(await runner.kdl(args, { signal: options.signal }));
1939
2232
  }
1940
2233
  };
1941
2234
  }
1942
2235
  export {
1943
- validateVisionLevelVsMode,
1944
- validateRefsNameVsScope,
1945
- validateGitSelection,
1946
- validateExactlyOneEditVariant,
1947
- tryParseLineAnchor,
1948
- runReadSeekNodeText,
1949
- runReadSeekNodeJson,
1950
- runReadSeekNode,
1951
- runReadSeekBunText,
1952
- runReadSeekBunJson,
1953
- runReadSeekBun,
1954
- rewriteBacktickedToolNames,
1955
- resolveToolName,
1956
- resolveToCwd,
1957
- resolveScopedPath,
1958
- resolveReadSeekCacheDir,
1959
- resolveReadSeekBinaryOverride,
1960
- resolveReadSeekBinaryFromPackages,
1961
- resolveProcessLimits,
1962
- renderToolRoutingPolicy,
1963
- renderToolDescription,
1964
- renderPreferredToolDescription,
1965
- renderFallbackToolDescription,
1966
- renderCompactGuidelines,
1967
- readSeekPlatformKey,
1968
- readSeekBinaryName,
1969
- preferredToolDescriptionMap,
1970
- parseSuccessfulJson,
1971
- parseReadSeekJson,
1972
- parseLineAnchor,
1973
- isSubPath,
1974
- getToolContract,
1975
- fallbackToolDescriptionMap,
1976
- failureFromProcessResult,
1977
- expandUserPath,
1978
- ensureSuccessfulStdout,
1979
- createReadSeekClient,
1980
- coercePositiveInt,
1981
- coerceOptionalString,
1982
- coerceObviousBase10Int,
1983
- coerceNonNegativeInt,
1984
- coerceBoolean,
1985
- cleanReadSeekErrorText,
1986
- classifyReadSeekFailure,
1987
- canonicalPathKey,
1988
- buildViewArgs,
1989
- buildSearchArgs,
1990
- buildRenameArgs,
1991
- buildRefsArgs,
1992
- buildLineLocation,
1993
- buildEditArgs,
1994
- buildDigestArgs,
1995
- buildDefArgs,
1996
- appendGitSelectionArgs,
1997
- appendApplyAndPlanHash,
1998
- VISION_MODES,
1999
- VISION_LEVELS,
2000
- VISION_ANALYSIS_MODES,
2001
- TOOL_NAMES,
2002
- TOOL_DESCRIPTIONS,
2003
- TOOL_CONTRACTS,
2004
- TOOL_COMPACT_GUIDELINES,
2005
- REPLACEABLE_TOOLS,
2006
- READSEEK_PLATFORM_PACKAGES,
2007
- READSEEK_BINARY_ENV,
2008
- PARAM_DESCRIPTIONS,
2009
- HASH_LEN,
2010
- GIT_SELECTION_FLAGS,
2011
- EDIT_VARIANTS,
2012
- DOCUMENT_NODE_KINDS,
2013
- DIGEST_FACETS,
2014
- DEFAULT_READSEEK_TIMEOUT_MS,
2236
+ BUILTIN_FALLBACKS,
2237
+ DEFAULT_INSPECT_MAP_DEPTH,
2015
2238
  DEFAULT_MAX_OUTPUT_BYTES,
2016
- DEFAULT_DIGEST_MAP_DEPTH,
2017
- BUILTIN_FALLBACKS
2239
+ DEFAULT_READSEEK_TIMEOUT_MS,
2240
+ EDIT_VARIANTS,
2241
+ GIT_SCOPES,
2242
+ HASH_LEN,
2243
+ INSPECT_FACETS,
2244
+ PARAM_DESCRIPTIONS,
2245
+ READSEEK_BINARY_ENV,
2246
+ READSEEK_PLATFORM_PACKAGES,
2247
+ REPLACEABLE_TOOLS,
2248
+ TOOL_COMPACT_GUIDELINES,
2249
+ TOOL_CONTRACTS,
2250
+ TOOL_DESCRIPTIONS,
2251
+ TOOL_NAMES,
2252
+ VISION_ANALYSIS_MODES,
2253
+ VISION_LEVELS,
2254
+ VISION_MODES,
2255
+ appendApplyAndPlanHash,
2256
+ appendGitScopeArgs,
2257
+ buildDefArgs,
2258
+ buildEditArgs,
2259
+ buildExtractArgs,
2260
+ buildInspectArgs,
2261
+ buildLineLocation,
2262
+ buildRefsArgs,
2263
+ buildRenameArgs,
2264
+ buildSearchArgs,
2265
+ canonicalPathKey,
2266
+ classifyReadSeekFailure,
2267
+ cleanReadSeekErrorText,
2268
+ coerceBoolean,
2269
+ coerceNonNegativeInt,
2270
+ coerceObviousBase10Int,
2271
+ coerceOptionalString,
2272
+ coercePositiveInt,
2273
+ createReadSeekClient,
2274
+ ensureSuccessfulStdout,
2275
+ expandUserPath,
2276
+ failureFromProcessResult,
2277
+ fallbackToolDescriptionMap,
2278
+ getToolContract,
2279
+ isSubPath,
2280
+ parseLineAnchor,
2281
+ parseReadSeekKdl,
2282
+ parseSuccessfulKdl,
2283
+ preferredToolDescriptionMap,
2284
+ readSeekBinaryName,
2285
+ readSeekPlatformKey,
2286
+ readSeekProcessEnvironment,
2287
+ renderCompactGuidelines,
2288
+ renderFallbackToolDescription,
2289
+ renderPreferredToolDescription,
2290
+ renderToolDescription,
2291
+ renderToolRoutingPolicy,
2292
+ resolveProcessLimits,
2293
+ resolveReadSeekBinaryFromPackages,
2294
+ resolveReadSeekBinaryOverride,
2295
+ resolveReadSeekCacheDir,
2296
+ resolveScopedPath,
2297
+ resolveToCwd,
2298
+ resolveToolName,
2299
+ rewriteBacktickedToolNames,
2300
+ runReadSeekBun,
2301
+ runReadSeekBunKdl,
2302
+ runReadSeekBunText,
2303
+ runReadSeekNode,
2304
+ runReadSeekNodeKdl,
2305
+ runReadSeekNodeText,
2306
+ serializeEditPlan,
2307
+ tryParseLineAnchor,
2308
+ validateExactlyOneEditVariant,
2309
+ validateRefsNameVsScope,
2310
+ validateVisionLevelVsMode
2018
2311
  };