@jarkkojs/readseek-api 0.9.15 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,27 +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",
56
+ inspectDepth: "Maximum structural depth for map output; defaults to 1 in agent tools",
67
57
  offset: "One-based starting line",
68
58
  readLimit: "Maximum lines to return",
69
59
  symbolRead: "Symbol name to read",
70
60
  bundle: "Include related symbols from the same file; requires symbol",
71
61
  image: "Standalone image mode: none, all, ocr, caption, objects. Required for standalone images.",
72
- visionModeDigest: "Standalone image mode: none, all, caption, objects, or ocr",
73
- visionModeView: "Analyze selected assets: all, ocr, caption, or objects",
62
+ visionModeInspect: "Standalone image mode: none, all, caption, objects, or ocr",
74
63
  visionLevel: "Vision inference level: low (default), medium, or high",
75
- node: "Node ID to use as the view root",
76
- page: "One-based source page",
77
- kind: "Node kind filter",
78
- depth: "Maximum depth below selected roots",
79
- outline: "Return outline nodes only",
64
+ output: "Destination file for extracted binary data; stdout is not supported",
80
65
  edits: "Use set_line, replace_lines, insert_after, replace_symbol, or replace",
81
66
  editVariant: "Exactly one nested edit variant",
82
67
  setLine: 'Replace one line: {"set_line":{"anchor":"LINE:HASH","new_text":"..."}}',
@@ -113,11 +98,11 @@ var PARAM_DESCRIPTIONS = {
113
98
  scopeContext: "Context lines within each symbol"
114
99
  };
115
100
  var TOOL_DESCRIPTIONS = {
116
- 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 for structural maps, diagnostics, and cursor identity.",
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.",
117
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":"..."}}`.',
118
103
  write: "Create or replace a complete text file and return `LINE:HASH` anchors. Use `edit` for small changes to an existing file.",
119
104
  grep: "Search text or regex in files and return edit-ready `LINE:HASH` anchors. Use for identifiers, strings, configuration, errors, comments, and documentation.",
120
- 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.",
121
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.",
122
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.",
123
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.",
@@ -129,18 +114,11 @@ function renderToolDescription(name, aliases = {}) {
129
114
  throw new Error(`Missing TOOL_DESCRIPTIONS for ${name}`);
130
115
  return TOOL_NAMES.reduce((description, toolName) => description.replaceAll(`\`${toolName}\``, `\`${aliases[toolName] ?? toolName}\``), desc);
131
116
  }
132
- var gitFlagsSchema = {
133
- cached: {
134
- type: "boolean",
135
- description: PARAM_DESCRIPTIONS.cached
136
- },
137
- others: {
138
- type: "boolean",
139
- description: PARAM_DESCRIPTIONS.others
140
- },
141
- ignored: {
142
- type: "boolean",
143
- description: PARAM_DESCRIPTIONS.ignored
117
+ var gitScopeSchema = {
118
+ gitScope: {
119
+ type: "string",
120
+ enum: GIT_SCOPES,
121
+ description: PARAM_DESCRIPTIONS.gitScope
144
122
  }
145
123
  };
146
124
  var editItemSchema = {
@@ -206,28 +184,31 @@ var editItemSchema = {
206
184
  };
207
185
  var TOOL_CONTRACTS = [
208
186
  {
209
- name: "digest",
210
- title: "Digest source",
211
- description: TOOL_DESCRIPTIONS.digest,
187
+ name: "inspect",
188
+ title: "Inspect input",
189
+ description: TOOL_DESCRIPTIONS.inspect,
212
190
  inputSchema: {
213
191
  type: "object",
214
192
  properties: {
215
193
  path: { type: "string", description: PARAM_DESCRIPTIONS.path },
194
+ selector: { type: "string", description: PARAM_DESCRIPTIONS.selector },
216
195
  select: { type: "string", description: PARAM_DESCRIPTIONS.select },
217
- at: { type: "string", description: PARAM_DESCRIPTIONS.at },
196
+ query: { type: "string", description: PARAM_DESCRIPTIONS.query },
218
197
  end: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.end },
219
198
  limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.limit },
199
+ depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.inspectDepth },
220
200
  language: { type: "string", description: PARAM_DESCRIPTIONS.languageOverride },
221
201
  visionMode: {
222
202
  type: "string",
223
203
  enum: [...VISION_MODES],
224
- description: PARAM_DESCRIPTIONS.visionModeDigest
204
+ description: PARAM_DESCRIPTIONS.visionModeInspect
225
205
  },
226
206
  visionLevel: {
227
207
  type: "string",
228
208
  enum: [...VISION_LEVELS],
229
209
  description: PARAM_DESCRIPTIONS.visionLevel
230
- }
210
+ },
211
+ stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
231
212
  },
232
213
  required: ["path"],
233
214
  additionalProperties: false
@@ -301,34 +282,27 @@ var TOOL_CONTRACTS = [
301
282
  }
302
283
  },
303
284
  {
304
- name: "view",
305
- title: "View document",
306
- description: TOOL_DESCRIPTIONS.view,
285
+ name: "extract",
286
+ title: "Extract asset",
287
+ description: TOOL_DESCRIPTIONS.extract,
307
288
  inputSchema: {
308
289
  type: "object",
309
290
  properties: {
310
291
  path: { type: "string", description: PARAM_DESCRIPTIONS.path },
311
- node: { type: "string", description: PARAM_DESCRIPTIONS.node },
312
- page: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.page },
313
- kind: {
292
+ selector: {
314
293
  type: "string",
315
- enum: [...DOCUMENT_NODE_KINDS],
316
- description: PARAM_DESCRIPTIONS.kind
294
+ pattern: "^asset:.+",
295
+ description: PARAM_DESCRIPTIONS.assetSelector
317
296
  },
318
- depth: { type: "integer", minimum: 0, description: PARAM_DESCRIPTIONS.depth },
319
- outline: { type: "boolean", description: PARAM_DESCRIPTIONS.outline },
320
- visionMode: {
297
+ output: {
321
298
  type: "string",
322
- enum: [...VISION_ANALYSIS_MODES],
323
- description: PARAM_DESCRIPTIONS.visionModeView
299
+ minLength: 1,
300
+ pattern: "^(?!-$).+",
301
+ description: PARAM_DESCRIPTIONS.output
324
302
  },
325
- visionLevel: {
326
- type: "string",
327
- enum: [...VISION_LEVELS],
328
- description: PARAM_DESCRIPTIONS.visionLevel
329
- }
303
+ stdinName: { type: "string", description: PARAM_DESCRIPTIONS.stdinName }
330
304
  },
331
- required: ["path"],
305
+ required: ["path", "selector", "output"],
332
306
  additionalProperties: false
333
307
  }
334
308
  },
@@ -342,7 +316,8 @@ var TOOL_CONTRACTS = [
342
316
  pattern: { type: "string", description: PARAM_DESCRIPTIONS.pattern },
343
317
  path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
344
318
  language: { type: "string", description: PARAM_DESCRIPTIONS.language },
345
- ...gitFlagsSchema
319
+ limit: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.matchLimit },
320
+ ...gitScopeSchema
346
321
  },
347
322
  required: ["pattern"],
348
323
  additionalProperties: false
@@ -358,7 +333,7 @@ var TOOL_CONTRACTS = [
358
333
  name: { type: "string", description: PARAM_DESCRIPTIONS.name },
359
334
  path: { type: "string", description: PARAM_DESCRIPTIONS.searchPath },
360
335
  language: { type: "string", description: PARAM_DESCRIPTIONS.language },
361
- ...gitFlagsSchema
336
+ ...gitScopeSchema
362
337
  },
363
338
  required: ["name"],
364
339
  additionalProperties: false
@@ -377,7 +352,7 @@ var TOOL_CONTRACTS = [
377
352
  scope: { type: "boolean", description: PARAM_DESCRIPTIONS.scope },
378
353
  line: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.line },
379
354
  column: { type: "integer", minimum: 1, description: PARAM_DESCRIPTIONS.column },
380
- ...gitFlagsSchema
355
+ ...gitScopeSchema
381
356
  },
382
357
  additionalProperties: false
383
358
  }
@@ -395,7 +370,9 @@ var TOOL_CONTRACTS = [
395
370
  to: { type: "string", description: PARAM_DESCRIPTIONS.to },
396
371
  workspace: { type: "boolean", description: PARAM_DESCRIPTIONS.workspace },
397
372
  apply: { type: "boolean", description: PARAM_DESCRIPTIONS.apply },
398
- 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
399
376
  },
400
377
  required: ["path", "line", "to"],
401
378
  additionalProperties: false
@@ -411,7 +388,7 @@ function getToolContract(name) {
411
388
  // src/policy.ts
412
389
  var REPLACEABLE_TOOLS = [
413
390
  {
414
- tool: "digest",
391
+ tool: "inspect",
415
392
  builtIn: "read",
416
393
  benefit: "it returns LINE:HASH anchors for safe edits."
417
394
  },
@@ -432,8 +409,8 @@ var REPLACEABLE_TOOLS = [
432
409
  }
433
410
  ];
434
411
  var TOOL_COMPACT_GUIDELINES = {
435
- digest: [
436
- "Use `digest` for map, diagnostics, and identity facets instead of separate tools."
412
+ inspect: [
413
+ "Use `inspect` for shallow maps, diagnostics, and identity; use `def`, `refs`, or `search` for targeted symbol questions."
437
414
  ],
438
415
  edit: [
439
416
  "Prefer set_line, replace_lines, and insert_after; use replace only when anchors are impractical."
@@ -452,14 +429,14 @@ var TOOL_COMPACT_GUIDELINES = {
452
429
  ]
453
430
  };
454
431
  var BUILTIN_FALLBACKS = {
455
- read: "digest",
432
+ read: "inspect",
456
433
  edit: "edit",
457
434
  write: "write",
458
435
  grep: "grep",
459
436
  apply_patch: "edit"
460
437
  };
461
438
  var PREFERENCE_LEADS = {
462
- digest: "Preferred for reading source with anchors and facets.",
439
+ inspect: "Preferred for reading source with anchors and facets.",
463
440
  edit: "Preferred for editing existing text files with fresh LINE:HASH anchors.",
464
441
  write: "Preferred for creating or replacing a complete text file; returns LINE:HASH anchors.",
465
442
  grep: "Preferred text/regex search; returns edit-ready LINE:HASH anchors.",
@@ -467,10 +444,10 @@ var PREFERENCE_LEADS = {
467
444
  def: "Preferred for go-to-definition or declaration lookup by name.",
468
445
  refs: "Preferred for finding identifier-name matches or binding-specific references before rename or delete.",
469
446
  rename: "Preferred for renaming a symbol with binding-aware, verified edits.",
470
- view: "Preferred for digesting document structure or selected content."
447
+ extract: "Preferred for saving a selected document asset to a file."
471
448
  };
472
449
  var FALLBACK_TEMPLATES = {
473
- read: "Prefer `digest` with fresh LINE:HASH anchors.",
450
+ read: "Prefer `inspect` with fresh LINE:HASH anchors.",
474
451
  edit: "Prefer `edit` with fresh LINE:HASH anchors.",
475
452
  write: "Prefer `write` so the result returns LINE:HASH anchors.",
476
453
  grep: "Prefer `grep` for text/regex search with edit-ready anchors.",
@@ -496,7 +473,7 @@ function renderFallbackToolDescription(builtIn, aliases = {}) {
496
473
  }
497
474
  function renderToolRoutingPolicy(options = {}) {
498
475
  const aliases = options.aliases ?? {};
499
- const digest = resolveToolName("digest", aliases);
476
+ const inspect = resolveToolName("inspect", aliases);
500
477
  const edit = resolveToolName("edit", aliases);
501
478
  const write = resolveToolName("write", aliases);
502
479
  const grep = resolveToolName("grep", aliases);
@@ -504,11 +481,11 @@ function renderToolRoutingPolicy(options = {}) {
504
481
  const def = resolveToolName("def", aliases);
505
482
  const refs = resolveToolName("refs", aliases);
506
483
  const rename = resolveToolName("rename", aliases);
507
- const view = resolveToolName("view", aliases);
484
+ const extract = resolveToolName("extract", aliases);
508
485
  if (options.style === "server") {
509
486
  return [
510
- "Structural code navigation, document viewing, and hash-verified edits with readseek.",
511
- "For structural questions, prefer these tools to text Grep/Bash: definitions, references, AST search, multi-facet digest (content/map/diagnostics/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.",
512
489
  `Use ${edit} for LINE:HASH-anchored mutations (preview with apply=false; apply defaults to true and re-checks plan_hash).`,
513
490
  "Paths resolve against CLAUDE_PROJECT_DIR. Native Read/Grep/Edit hooks provide passive context;",
514
491
  "call MCP tools for active navigation, document questions, or anchored edits."
@@ -519,7 +496,7 @@ function renderToolRoutingPolicy(options = {}) {
519
496
  if (options.preferOverBuiltIns) {
520
497
  bullets.push("- Prefer ReadSeek tools over built-ins when they can do the job.");
521
498
  }
522
- bullets.push(`- Read with ${digest} first; ${edit} needs fresh LINE:HASH anchors.`, `- Use ${grep} for text/regex, ${search} for AST patterns, and ${digest}/${def}/${refs} for symbol navigation.`, `- 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.`);
523
500
  if (options.demoteBuiltIns && options.demoteBuiltIns.length > 0) {
524
501
  bullets.push(`- Do not use built-in ${options.demoteBuiltIns.join(", ")} when a ReadSeek tool can do the job.`);
525
502
  }
@@ -561,12 +538,6 @@ function parseLineAnchor(anchor) {
561
538
  }
562
539
 
563
540
  // src/validation.ts
564
- function validateGitSelection(input) {
565
- if (input.ignored && !input.others) {
566
- return { ok: false, error: "ignored requires others=true" };
567
- }
568
- return { ok: true };
569
- }
570
541
  function validateRefsNameVsScope(input) {
571
542
  if (input.scope) {
572
543
  if (input.line === undefined) {
@@ -583,11 +554,8 @@ function validateRefsNameVsScope(input) {
583
554
  return { ok: true };
584
555
  }
585
556
  function validateVisionLevelVsMode(input) {
586
- if (input.outline && (input.visionMode !== undefined || input.visionLevel !== undefined)) {
587
- return { ok: false, error: "Cannot combine outline with visionMode or visionLevel" };
588
- }
589
- if (input.visionLevel !== undefined && input.visionMode === undefined) {
590
- 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" };
591
559
  }
592
560
  return { ok: true };
593
561
  }
@@ -714,21 +682,54 @@ function coerceNonNegativeInt(value) {
714
682
  }
715
683
  return;
716
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
+ }
717
725
  // src/argv.ts
726
+ var DEFAULT_INSPECT_MAP_DEPTH = 1;
718
727
  function buildLineLocation(line, column) {
719
728
  return column === undefined ? `line:${line}` : `line:${line}:${column}`;
720
729
  }
721
- function appendGitSelectionArgs(args, input) {
722
- const validated = validateGitSelection(input);
723
- if (!validated.ok) {
724
- throw new Error(validated.error);
725
- }
726
- if (input.cached)
727
- args.push("--cached");
728
- if (input.others)
729
- args.push("--others");
730
- if (input.ignored)
731
- args.push("--ignored");
730
+ function appendGitScopeArgs(args, input) {
731
+ if (input.gitScope !== undefined)
732
+ args.push("--git-scope", input.gitScope);
732
733
  return args;
733
734
  }
734
735
  function pushOptionalString(args, flag, value) {
@@ -757,51 +758,163 @@ function selectArg(select) {
757
758
  return;
758
759
  return Array.isArray(select) ? select.join(",") : select;
759
760
  }
760
- function buildDigestArgs(input) {
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);
788
+ }
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
+ }
761
830
  const vision = validateVisionLevelVsMode({
762
831
  visionMode: input.visionMode,
763
832
  visionLevel: input.visionLevel
764
833
  });
765
834
  if (!vision.ok)
766
835
  throw new Error(vision.error);
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
+ }
848
+ if (input.depth !== undefined) {
849
+ if (!Number.isSafeInteger(input.depth) || input.depth < 0) {
850
+ throw new Error("depth must be a non-negative integer");
851
+ }
852
+ if (!hasMap)
853
+ throw new Error("depth requires the map facet");
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
+ }
767
883
  const args = [];
768
884
  if (input.readseekDir !== undefined)
769
885
  args.push("--readseek-dir", input.readseekDir);
770
- args.push("digest", input.path);
886
+ args.push("inspect", input.path);
887
+ if (input.selector !== undefined)
888
+ args.push(input.selector);
771
889
  const select = selectArg(input.select);
772
890
  if (select !== undefined)
773
891
  args.push("--select", select);
774
- pushOptionalString(args, "--at", input.at);
892
+ pushOptionalString(args, "--query", input.query);
775
893
  pushOptionalNumber(args, "--end", input.end);
776
894
  pushOptionalNumber(args, "--limit", input.limit);
895
+ pushOptionalNumber(args, "--depth", input.depth);
777
896
  pushOptionalString(args, "--language", input.language);
778
897
  pushOptionalString(args, "--vision-mode", input.visionMode);
779
898
  pushOptionalString(args, "--vision-level", input.visionLevel);
780
899
  pushOptionalString(args, "--stdin-name", input.stdinName);
900
+ args.push("--kdl");
781
901
  return args;
782
902
  }
783
- function buildViewArgs(input) {
784
- const vision = validateVisionLevelVsMode({
785
- visionMode: input.visionMode,
786
- visionLevel: input.visionLevel,
787
- outline: input.outline
788
- });
789
- if (!vision.ok)
790
- 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
+ }
791
913
  const args = [];
792
914
  if (input.readseekDir !== undefined)
793
915
  args.push("--readseek-dir", input.readseekDir);
794
- args.push("view", input.path);
795
- if (input.format !== undefined)
796
- args.push("--format", input.format);
797
- pushOptionalString(args, "--node", input.node);
798
- pushOptionalNumber(args, "--page", input.page);
799
- pushOptionalString(args, "--kind", input.kind);
800
- pushOptionalNumber(args, "--depth", input.depth);
801
- if (input.outline)
802
- args.push("--outline");
803
- pushOptionalString(args, "--vision-mode", input.visionMode);
804
- pushOptionalString(args, "--vision-level", input.visionLevel);
916
+ args.push("--output", input.output, "extract", input.path, input.selector);
917
+ pushOptionalString(args, "--stdin-name", input.stdinName);
805
918
  return args;
806
919
  }
807
920
  function buildSearchArgs(input) {
@@ -809,15 +922,17 @@ function buildSearchArgs(input) {
809
922
  pushOptionalString(args, "--path", input.path);
810
923
  pushOptionalString(args, "--language", input.language);
811
924
  pushOptionalNumber(args, "--limit", input.limit);
812
- return appendGitSelectionArgs(args, input);
925
+ appendGitScopeArgs(args, input);
926
+ args.push("--kdl");
927
+ return args;
813
928
  }
814
929
  function buildDefArgs(input) {
815
930
  const args = ["def", input.name];
816
931
  pushOptionalString(args, "--path", input.path);
817
- if (input.format !== undefined)
818
- args.push("--format", input.format);
819
932
  pushOptionalString(args, "--language", input.language);
820
- return appendGitSelectionArgs(args, input);
933
+ appendGitScopeArgs(args, input);
934
+ args.push("--kdl");
935
+ return args;
821
936
  }
822
937
  function buildRefsArgs(input) {
823
938
  const validated = validateRefsNameVsScope(input);
@@ -832,10 +947,10 @@ function buildRefsArgs(input) {
832
947
  pushOptionalNumber(args, "--line", input.line);
833
948
  pushOptionalNumber(args, "--column", input.column);
834
949
  }
835
- if (input.format !== undefined)
836
- args.push("--format", input.format);
837
950
  pushOptionalString(args, "--language", input.language);
838
- return appendGitSelectionArgs(args, input);
951
+ appendGitScopeArgs(args, input);
952
+ args.push("--kdl");
953
+ return args;
839
954
  }
840
955
  function buildEditArgs(input) {
841
956
  const args = ["edit", input.path];
@@ -847,6 +962,7 @@ function buildEditArgs(input) {
847
962
  pushOptionalString(args, "--language", input.language);
848
963
  if (input.requestFromStdin)
849
964
  args.push("--edits", "-");
965
+ args.push("--kdl");
850
966
  return args;
851
967
  }
852
968
  function buildRenameArgs(input) {
@@ -859,7 +975,9 @@ function buildRenameArgs(input) {
859
975
  flagOrder: input.flagOrder ?? "plan-hash-first"
860
976
  });
861
977
  pushOptionalString(args, "--language", input.language);
862
- return appendGitSelectionArgs(args, input);
978
+ appendGitScopeArgs(args, input);
979
+ args.push("--kdl");
980
+ return args;
863
981
  }
864
982
  // src/platform.ts
865
983
  import { accessSync, constants as fsConstants, existsSync, statSync } from "node:fs";
@@ -875,6 +993,8 @@ var READSEEK_PLATFORM_PACKAGES = {
875
993
  };
876
994
  var resolveFromPackage = createRequire(import.meta.url).resolve;
877
995
  function readSeekBinaryName(platform = process.platform) {
996
+ if (platform === "linux")
997
+ return "readseek.AppImage";
878
998
  return platform === "win32" ? "readseek.exe" : "readseek";
879
999
  }
880
1000
  function readSeekPlatformKey(platform = process.platform, arch = process.arch) {
@@ -906,9 +1026,9 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
906
1026
  const platformPackage = READSEEK_PLATFORM_PACKAGES[key];
907
1027
  if (!platformPackage) {
908
1028
  const supported = Object.keys(READSEEK_PLATFORM_PACKAGES).join(", ");
909
- throw new Error(`@jarkkojs/readseek ships no binary for ${key}; it supports ${supported}`);
1029
+ throw new Error(`@jarkkojs/readseek-api ships no binary for ${key}; it supports ${supported}`);
910
1030
  }
911
- const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
1031
+ const metaPackage = options.metaPackage ?? "@jarkkojs/readseek-api";
912
1032
  const metaPackageJson = resolve(`${metaPackage}/package.json`);
913
1033
  const metaPackageDir = path.dirname(metaPackageJson);
914
1034
  let packageJsonPath;
@@ -949,7 +1069,7 @@ function resolveReadSeekCacheDir(subPath, options = {}) {
949
1069
  return pathModule.join(base, ...parts);
950
1070
  }
951
1071
  // src/path.ts
952
- import { realpathSync } from "node:fs";
1072
+ import { lstatSync, realpathSync } from "node:fs";
953
1073
  import { homedir as homedir2 } from "node:os";
954
1074
  import path2 from "node:path";
955
1075
  var UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
@@ -978,30 +1098,338 @@ function isSubPath(parent, candidate) {
978
1098
  const relative = path2.relative(parent, candidate);
979
1099
  return relative === "" || !relative.startsWith(`..${path2.sep}`) && relative !== ".." && !path2.isAbsolute(relative);
980
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
+ }
981
1120
  function resolveScopedPath(root, rawPath, fallback = ".") {
982
- const canonicalRoot = canonicalPathKey(root, ".");
983
- 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
+ }
984
1130
  if (!isSubPath(canonicalRoot, candidate)) {
985
1131
  return { ok: false, error: `path escapes project root (${canonicalRoot}): ${rawPath ?? fallback}` };
986
1132
  }
987
1133
  return { ok: true, path: candidate };
988
1134
  }
989
- // src/json.ts
990
- 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*$/;
991
1137
  function cleanReadSeekErrorText(stderr) {
992
1138
  return stderr.replace(READSEEK_USAGE_HINT, "").replace(/^error:\s*/i, "").replace(/\s*\n\s*/g, " ").trim();
993
1139
  }
994
- function parseReadSeekJson(stdout) {
995
- try {
996
- return JSON.parse(stdout);
997
- } catch {
998
- const preview = stdout.trim().slice(0, 200);
999
- 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}`);
1000
1425
  }
1001
1426
  }
1002
1427
  // src/process.ts
1003
1428
  var DEFAULT_READSEEK_TIMEOUT_MS = 120000;
1004
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
+ }
1005
1433
  function resolveProcessLimits(options = {}) {
1006
1434
  return {
1007
1435
  timeoutMs: options.timeoutMs ?? DEFAULT_READSEEK_TIMEOUT_MS,
@@ -1032,8 +1460,8 @@ function ensureSuccessfulStdout(result) {
1032
1460
  return result.stdout;
1033
1461
  throw failureFromProcessResult(result);
1034
1462
  }
1035
- function parseSuccessfulJson(result) {
1036
- return parseReadSeekJson(ensureSuccessfulStdout(result));
1463
+ function parseSuccessfulKdl(result) {
1464
+ return parseReadSeekKdl(ensureSuccessfulStdout(result));
1037
1465
  }
1038
1466
  // src/process-node.ts
1039
1467
  import { spawn } from "node:child_process";
@@ -1100,7 +1528,7 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
1100
1528
  try {
1101
1529
  child = spawn(binaryPath, [...args], {
1102
1530
  cwd: options.cwd,
1103
- env: process.env,
1531
+ env: readSeekProcessEnvironment(),
1104
1532
  shell: false,
1105
1533
  stdio: [options.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
1106
1534
  windowsHide: true
@@ -1143,17 +1571,17 @@ async function runReadSeekNode(binaryPath, args, options = {}) {
1143
1571
  async function runReadSeekNodeText(binaryPath, args, options = {}) {
1144
1572
  return ensureSuccessfulStdout(await runReadSeekNode(binaryPath, args, options));
1145
1573
  }
1146
- async function runReadSeekNodeJson(binaryPath, args, options = {}) {
1147
- return parseSuccessfulJson(await runReadSeekNode(binaryPath, args, options));
1574
+ async function runReadSeekNodeKdl(binaryPath, args, options = {}) {
1575
+ return parseSuccessfulKdl(await runReadSeekNode(binaryPath, args, options));
1148
1576
  }
1149
1577
  // src/process-bun.ts
1150
1578
  function getBunSpawn() {
1151
1579
  const bunGlobal = globalThis;
1152
- const spawn2 = bunGlobal.Bun?.spawn;
1153
- if (typeof spawn2 !== "function") {
1580
+ const spawn = bunGlobal.Bun?.spawn;
1581
+ if (typeof spawn !== "function") {
1154
1582
  throw new Error("Bun.spawn is unavailable in this runtime");
1155
1583
  }
1156
- return spawn2;
1584
+ return spawn;
1157
1585
  }
1158
1586
  async function readBoundedText(stream, streamName, maxOutputBytes, onOverflow) {
1159
1587
  const reader = stream.getReader();
@@ -1190,16 +1618,16 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
1190
1618
  if (limits.cancelable && options.signal?.aborted) {
1191
1619
  throw readSeekCancellationError(options.signal);
1192
1620
  }
1193
- const spawn2 = getBunSpawn();
1621
+ const spawn = getBunSpawn();
1194
1622
  const stdin = options.stdin === undefined ? undefined : new TextEncoder().encode(options.stdin);
1195
1623
  const spawnOptions = {
1196
1624
  cwd: options.cwd,
1197
- env: process.env,
1625
+ env: readSeekProcessEnvironment(),
1198
1626
  stderr: "pipe",
1199
1627
  stdin,
1200
1628
  stdout: "pipe"
1201
1629
  };
1202
- 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);
1203
1631
  const killForOverflow = () => {
1204
1632
  try {
1205
1633
  child.kill("SIGKILL");
@@ -1228,351 +1656,368 @@ async function runReadSeekBun(binaryPath, args, options = {}) {
1228
1656
  } finally {
1229
1657
  if (timeout !== undefined)
1230
1658
  clearTimeout(timeout);
1659
+ pending.catch(() => {
1660
+ return;
1661
+ });
1231
1662
  }
1232
1663
  }
1233
1664
  async function runReadSeekBunText(binaryPath, args, options = {}) {
1234
1665
  return ensureSuccessfulStdout(await runReadSeekBun(binaryPath, args, options));
1235
1666
  }
1236
- async function runReadSeekBunJson(binaryPath, args, options = {}) {
1237
- return parseSuccessfulJson(await runReadSeekBun(binaryPath, args, options));
1667
+ async function runReadSeekBunKdl(binaryPath, args, options = {}) {
1668
+ return parseSuccessfulKdl(await runReadSeekBun(binaryPath, args, options));
1238
1669
  }
1239
- // src/client.ts
1240
- function classifyReadSeekFailure(err) {
1241
- const failure = err;
1242
- const message = String(failure?.message ?? err);
1243
- const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
1244
- if (missing) {
1245
- 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"}`);
1246
1682
  }
1247
- return { code: "readseek-execution-error", message };
1248
- }
1249
- function requireNumber(value, field) {
1250
- if (typeof value !== "number" || !Number.isSafeInteger(value))
1251
- throw new Error(`invalid readseek ${field}: expected safe integer`);
1252
- return value;
1253
- }
1254
- function requireString(value, field) {
1255
- if (typeof value !== "string")
1256
- throw new Error(`invalid readseek ${field}`);
1257
- return value;
1258
- }
1259
- function requireBoolean(value, field) {
1260
- if (typeof value !== "boolean")
1261
- throw new Error(`invalid readseek ${field}: expected boolean`);
1262
- return value;
1263
- }
1264
- function optionalString(value, field) {
1265
- if (value === undefined || value === null)
1266
- return;
1267
- return requireString(value, field);
1268
- }
1269
- function parseHashline(value, field) {
1270
- if (!value || typeof value !== "object")
1271
- throw new Error(`invalid readseek ${field}`);
1272
- const item = value;
1273
- return {
1274
- line: requireNumber(item.line, `${field}.line`),
1275
- hash: requireString(item.hash, `${field}.hash`),
1276
- text: requireString(item.text, `${field}.text`)
1277
- };
1278
- }
1279
- function parseSearchHashlines(value, field) {
1280
- if (!Array.isArray(value))
1281
- throw new Error(`invalid readseek ${field}`);
1282
- return value.map((line) => parseHashline(line, field));
1283
1683
  }
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
+ var MapNode = Type.Recursive((Self) => Type.Object({
1712
+ kind: Str,
1713
+ name: Str,
1714
+ start_line: Int,
1715
+ end_line: Int,
1716
+ start_anchor: Str,
1717
+ end_anchor: Str,
1718
+ children: Type.Array(Self)
1719
+ }));
1720
+ var MapOutput = Type.Object({
1721
+ file: Str,
1722
+ language: Str,
1723
+ line_count: Int,
1724
+ file_hash: Str,
1725
+ root: Type.Optional(MapNode),
1726
+ symbols: Type.Array(SymbolOutput)
1727
+ });
1728
+ var SearchSpan = {
1729
+ start_byte: OptInt,
1730
+ end_byte: OptInt,
1731
+ text: OptStr,
1732
+ start_line: Int,
1733
+ end_line: Int,
1734
+ start_hash: Str,
1735
+ end_hash: Str,
1736
+ hashlines: Hashlines
1737
+ };
1738
+ var SearchCapture = Type.Object({ name: Str, ...SearchSpan });
1739
+ var SearchMatch = Type.Object({
1740
+ pattern_index: Type.Integer({ default: 0 }),
1741
+ ...SearchSpan,
1742
+ captures: Type.Array(SearchCapture)
1743
+ });
1744
+ var SearchFileOutput = Type.Object({
1745
+ file: Str,
1746
+ language: Str,
1747
+ file_hash: Str,
1748
+ matches: Type.Array(SearchMatch)
1749
+ });
1750
+ var SearchEnvelope = Type.Object({
1751
+ results: Type.Array(SearchFileOutput),
1752
+ returned_matches: OptInt,
1753
+ truncated: Type.Optional(Bool),
1754
+ byte_basis: OptStr
1755
+ });
1756
+ var RefsEnvelope = Type.Object({
1757
+ references: Type.Array(Type.Object({
1758
+ file: Str,
1759
+ line: Int,
1760
+ column: Int,
1761
+ line_hash: Str,
1762
+ text: Str,
1763
+ symbol: Type.Optional(Type.Object({ qualified_name: OptStr }))
1764
+ }))
1765
+ });
1766
+ var Diagnostic = Type.Object({
1767
+ kind: Type.Union([Type.Literal("error"), Type.Literal("missing")]),
1768
+ start_line: Int,
1769
+ end_line: Int
1770
+ });
1771
+ var CheckEnvelope = Type.Object({
1772
+ file_hash: OptStr,
1773
+ error_count: Int,
1774
+ missing_count: Int,
1775
+ diagnostics: Type.Array(Diagnostic)
1776
+ });
1777
+ var DetectedObject = Type.Object({
1778
+ label: Str,
1779
+ bbox: Type.Tuple([Int, Int, Int, Int])
1780
+ });
1781
+ var PreparedImage = Type.Object({
1782
+ mime: Str,
1783
+ encoding: Type.Literal("base64"),
1784
+ data: Str
1785
+ });
1786
+ var SourceDetection = Type.Object({
1787
+ kind: Type.Literal("source"),
1788
+ type: Str,
1789
+ file: Str,
1790
+ language: Str,
1791
+ engine: OptStr,
1792
+ supported: Bool,
1793
+ mime: OptStr,
1794
+ syntax: OptStr
1795
+ });
1796
+ var ImageDetection = Type.Object({
1797
+ kind: Type.Literal("image"),
1798
+ type: Str,
1799
+ file: Str,
1800
+ mime: OptStr,
1801
+ format: Str,
1802
+ width: Int,
1803
+ height: Int,
1804
+ animated: Bool,
1805
+ encoding: Type.Optional(Type.Literal("base64")),
1806
+ data: OptStr,
1807
+ ocr: OptStr,
1808
+ caption: OptStr,
1809
+ objects: Type.Optional(Type.Array(DetectedObject))
1810
+ });
1811
+ var TextDetection = Type.Object({
1812
+ kind: Type.Literal("text"),
1813
+ type: Str,
1814
+ file: Str,
1815
+ mime: OptStr
1816
+ });
1817
+ var PdfDetection = Type.Object({
1818
+ kind: Type.Literal("pdf"),
1819
+ type: Type.Literal("application/pdf"),
1820
+ file: Str,
1821
+ mime: OptStr,
1822
+ format: Type.Literal("pdf"),
1823
+ pages: Int
1824
+ });
1825
+ var Detection = Type.Union([SourceDetection, ImageDetection, TextDetection, PdfDetection]);
1826
+ var SourceDetectionInput = Type.Omit(SourceDetection, ["kind"]);
1827
+ var ImageDetectionInput = Type.Omit(ImageDetection, ["kind"]);
1828
+ var TextDetectionInput = Type.Omit(TextDetection, ["kind"]);
1829
+ var PdfDetectionInput = Type.Omit(PdfDetection, ["kind"]);
1830
+ var UnspannedContent = Type.Omit(ReadOutput, ["start_line", "end_line"]);
1831
+ var PdfInspectMetadata = Type.Object({ source: Str, format: Type.Literal("pdf"), pages: Int });
1832
+ var DocumentCache = Type.Object({
1833
+ content_hash: Str,
1834
+ state: Type.Union([Type.Literal("built"), Type.Literal("reused")])
1835
+ });
1836
+ var Identifier = Type.Object({
1837
+ text: Str,
1838
+ start_column: Int,
1839
+ end_column: Int,
1840
+ start_byte: Int,
1841
+ end_byte: Int
1842
+ });
1843
+ var IdentifyEnvelope = Type.Object({
1844
+ file: Str,
1845
+ language: Str,
1846
+ engine: OptStr,
1847
+ line_count: Int,
1848
+ file_hash: Str,
1849
+ line: Int,
1850
+ column: Int,
1851
+ line_hash: Str,
1852
+ identifier: Type.Optional(Identifier),
1853
+ symbol: Type.Optional(Type.Object({
1854
+ name: Str,
1855
+ kind: Str,
1856
+ qualified_name: Str,
1857
+ start_line: Int,
1858
+ end_line: Int
1859
+ }))
1860
+ });
1861
+ var EditOutputSchema = Type.Object({
1862
+ file: Str,
1863
+ before_hash: Str,
1864
+ file_hash: Str,
1865
+ plan_hash: Str,
1866
+ applied: Bool,
1867
+ changed: Bool,
1868
+ edit_count: Int,
1869
+ before_content: OptStr,
1870
+ content: OptStr
1871
+ });
1872
+ var RenameConflictSchema = Type.Object({ line: Int, column: Int, reason: Str });
1873
+ var RenameEditSchema = Type.Object({
1874
+ line: Int,
1875
+ start_column: Int,
1876
+ end_column: Int,
1877
+ start_byte: Int,
1878
+ end_byte: Int,
1879
+ occurrence: Str,
1880
+ line_hash: Str,
1881
+ text: Str
1882
+ });
1883
+ var Conflicts = Type.Array(RenameConflictSchema, { default: [] });
1884
+ var Edits = Type.Array(RenameEditSchema, { default: [] });
1885
+ var RenameFileOutputSchema = Type.Object({
1886
+ file: Str,
1887
+ language: Str,
1888
+ engine: OptStr,
1889
+ file_hash: Str,
1890
+ conflicts: Conflicts,
1891
+ edits: Edits
1892
+ });
1893
+ var RenameOutputSchema = Type.Object({
1894
+ file: Str,
1895
+ language: Str,
1896
+ engine: OptStr,
1897
+ file_hash: Str,
1898
+ old_name: Str,
1899
+ new_name: Str,
1900
+ plan_hash: Str,
1901
+ applied: Bool,
1902
+ conflicts: Conflicts,
1903
+ edits: Edits,
1904
+ others: Type.Array(RenameFileOutputSchema, { default: [] })
1905
+ });
1906
+ var DefEnvelope = Type.Object({
1907
+ locations: Type.Array(Type.Object({
1908
+ file: Str,
1909
+ line: Int,
1910
+ column: Int,
1911
+ line_hash: Str,
1912
+ text: Str,
1913
+ kind: OptStr,
1914
+ name: OptStr,
1915
+ qualified_name: OptStr
1916
+ }))
1917
+ });
1284
1918
  function parseReadOutput(value) {
1285
- if (!value || typeof value !== "object")
1286
- throw new Error("invalid readseek read output");
1287
- const output = value;
1288
- const hashlines = output.hashlines;
1289
- if (!Array.isArray(hashlines))
1290
- throw new Error("invalid readseek hashlines");
1291
- return {
1292
- file: requireString(output.file, "file"),
1293
- language: requireString(output.language, "language"),
1294
- line_count: requireNumber(output.line_count, "line_count"),
1295
- file_hash: requireString(output.file_hash, "file_hash"),
1296
- start_line: requireNumber(output.start_line, "start_line"),
1297
- end_line: requireNumber(output.end_line, "end_line"),
1298
- hashlines: hashlines.map((line) => parseHashline(line, "hashline"))
1299
- };
1919
+ return parse2(ReadOutput, value, "read output");
1300
1920
  }
1301
1921
  function parseMapOutput(value) {
1302
- if (!value || typeof value !== "object")
1303
- throw new Error("invalid readseek map output");
1304
- const output = value;
1305
- const symbols = output.symbols;
1306
- if (!Array.isArray(symbols))
1307
- throw new Error("invalid readseek symbols");
1308
- return {
1309
- file: requireString(output.file, "file"),
1310
- language: requireString(output.language, "language"),
1311
- line_count: requireNumber(output.line_count, "line_count"),
1312
- file_hash: requireString(output.file_hash, "file_hash"),
1313
- symbols: symbols.map((symbol) => {
1314
- if (!symbol || typeof symbol !== "object")
1315
- throw new Error("invalid readseek symbol");
1316
- const item = symbol;
1317
- return {
1318
- kind: requireString(item.kind, "symbol.kind"),
1319
- name: requireString(item.name, "symbol.name"),
1320
- qualified_name: requireString(item.qualified_name, "symbol.qualified_name"),
1321
- start_line: requireNumber(item.start_line, "symbol.start_line"),
1322
- end_line: requireNumber(item.end_line, "symbol.end_line"),
1323
- start_hash: requireString(item.start_hash, "symbol.start_hash"),
1324
- end_hash: requireString(item.end_hash, "symbol.end_hash")
1325
- };
1326
- })
1327
- };
1922
+ return parse2(MapOutput, value, "map output");
1328
1923
  }
1329
1924
  function parseSearchOutput(value) {
1330
- if (!value || typeof value !== "object")
1331
- throw new Error("invalid readseek search output");
1332
- const output = value;
1333
- if (!Array.isArray(output.results))
1334
- throw new Error("invalid readseek search results");
1335
- return output.results.map((result) => {
1336
- if (!result || typeof result !== "object")
1337
- throw new Error("invalid readseek search result");
1338
- const file = result;
1339
- if (!Array.isArray(file.matches))
1340
- throw new Error("invalid readseek search matches");
1341
- return {
1342
- file: requireString(file.file, "search.file"),
1343
- language: requireString(file.language, "search.language"),
1344
- file_hash: requireString(file.file_hash, "search.file_hash"),
1345
- matches: file.matches.map((match) => {
1346
- if (!match || typeof match !== "object")
1347
- throw new Error("invalid readseek search match");
1348
- const item = match;
1349
- if (!Array.isArray(item.captures))
1350
- throw new Error("invalid readseek search captures");
1351
- return {
1352
- pattern_index: item.pattern_index === undefined ? 0 : requireNumber(item.pattern_index, "search.match.pattern_index"),
1353
- start_line: requireNumber(item.start_line, "search.match.start_line"),
1354
- end_line: requireNumber(item.end_line, "search.match.end_line"),
1355
- start_hash: requireString(item.start_hash, "search.match.start_hash"),
1356
- end_hash: requireString(item.end_hash, "search.match.end_hash"),
1357
- hashlines: parseSearchHashlines(item.hashlines, "search.match.hashlines"),
1358
- captures: item.captures.map((capture) => {
1359
- if (!capture || typeof capture !== "object")
1360
- throw new Error("invalid readseek search capture");
1361
- const captureItem = capture;
1362
- return {
1363
- name: requireString(captureItem.name, "search.capture.name"),
1364
- start_line: requireNumber(captureItem.start_line, "search.capture.start_line"),
1365
- end_line: requireNumber(captureItem.end_line, "search.capture.end_line"),
1366
- start_hash: requireString(captureItem.start_hash, "search.capture.start_hash"),
1367
- end_hash: requireString(captureItem.end_hash, "search.capture.end_hash"),
1368
- hashlines: parseSearchHashlines(captureItem.hashlines, "search.capture.hashlines")
1369
- };
1370
- })
1371
- };
1372
- })
1373
- };
1374
- });
1925
+ const envelope = parse2(SearchEnvelope, value, "search output");
1926
+ const results = envelope.results;
1927
+ if (envelope.returned_matches !== undefined)
1928
+ results.returned_matches = envelope.returned_matches;
1929
+ if (envelope.truncated !== undefined)
1930
+ results.truncated = envelope.truncated;
1931
+ if (envelope.byte_basis !== undefined)
1932
+ results.byte_basis = envelope.byte_basis;
1933
+ return results;
1375
1934
  }
1376
1935
  function parseRefsOutput(value) {
1377
- if (!value || typeof value !== "object")
1378
- throw new Error("invalid readseek refs output");
1379
- const output = value;
1380
- if (!Array.isArray(output.references))
1381
- throw new Error("invalid readseek references");
1382
- return output.references.map((reference) => {
1383
- if (!reference || typeof reference !== "object")
1384
- throw new Error("invalid readseek reference");
1385
- const item = reference;
1386
- const symbol = item.symbol;
1387
- const enclosing = symbol && typeof symbol === "object" ? optionalString(symbol.qualified_name, "reference.symbol.qualified_name") : undefined;
1388
- return {
1389
- file: requireString(item.file, "reference.file"),
1390
- line: requireNumber(item.line, "reference.line"),
1391
- column: requireNumber(item.column, "reference.column"),
1392
- line_hash: requireString(item.line_hash, "reference.line_hash"),
1393
- text: requireString(item.text, "reference.text"),
1394
- enclosingSymbol: enclosing
1395
- };
1396
- });
1397
- }
1398
- function parseDiagnosticKind(value) {
1399
- if (value === "error" || value === "missing")
1400
- return value;
1401
- throw new Error("invalid readseek diagnostic.kind");
1936
+ return parse2(RefsEnvelope, value, "refs output").references.map(({ symbol, ...reference }) => ({
1937
+ ...reference,
1938
+ enclosingSymbol: symbol?.qualified_name
1939
+ }));
1402
1940
  }
1403
1941
  function parseCheckOutput(value) {
1404
- if (!value || typeof value !== "object")
1405
- throw new Error("invalid readseek check output");
1406
- const output = value;
1407
- if (!Array.isArray(output.diagnostics))
1408
- throw new Error("invalid readseek diagnostics");
1942
+ const output = parse2(CheckEnvelope, value, "check output");
1409
1943
  return {
1410
- file_hash: optionalString(output.file_hash, "file_hash"),
1411
- errorCount: requireNumber(output.error_count, "error_count"),
1412
- missingCount: requireNumber(output.missing_count, "missing_count"),
1413
- diagnostics: output.diagnostics.map((diagnostic) => {
1414
- if (!diagnostic || typeof diagnostic !== "object")
1415
- throw new Error("invalid readseek diagnostic");
1416
- const item = diagnostic;
1417
- return {
1418
- kind: parseDiagnosticKind(item.kind),
1419
- start_line: requireNumber(item.start_line, "diagnostic.start_line"),
1420
- end_line: requireNumber(item.end_line, "diagnostic.end_line")
1421
- };
1422
- })
1944
+ file_hash: output.file_hash,
1945
+ errorCount: output.error_count,
1946
+ missingCount: output.missing_count,
1947
+ diagnostics: output.diagnostics
1423
1948
  };
1424
1949
  }
1425
- function parseDetectedObjects(value) {
1426
- if (value === undefined || value === null)
1427
- return;
1428
- if (!Array.isArray(value))
1429
- throw new Error("invalid readseek detect objects");
1430
- return value.map((object) => {
1431
- if (!object || typeof object !== "object")
1432
- throw new Error("invalid readseek detect object");
1433
- const item = object;
1434
- const bbox = item.bbox;
1435
- if (!Array.isArray(bbox) || bbox.length !== 4)
1436
- throw new Error("invalid readseek detect object.bbox");
1437
- return {
1438
- label: requireString(item.label, "object.label"),
1439
- bbox: bbox.map((n, i) => requireNumber(n, `object.bbox[${i}]`))
1440
- };
1441
- });
1442
- }
1443
- function parseImageEncoding(value) {
1444
- if (value === "base64")
1445
- return value;
1446
- throw new Error("invalid image encoding");
1447
- }
1448
1950
  function parseDetectOutput(value) {
1449
- if (!value || typeof value !== "object")
1450
- throw new Error("invalid readseek detect output");
1451
- const output = value;
1452
- const type = requireString(output.type, "type");
1453
- const file = requireString(output.file, "file");
1454
- const mime = optionalString(output.mime, "mime");
1455
- if (type === "application/pdf") {
1456
- if (output.format !== "pdf")
1457
- throw new Error("invalid readseek PDF format");
1458
- return {
1459
- kind: "pdf",
1460
- type,
1461
- file,
1462
- mime,
1463
- format: output.format,
1464
- pages: requireNumber(output.pages, "pages")
1465
- };
1951
+ const output = value ?? {};
1952
+ if (output.type === "application/pdf") {
1953
+ return { kind: "pdf", ...parse2(PdfDetectionInput, value, "detect output") };
1466
1954
  }
1467
1955
  if (output.width !== undefined || output.height !== undefined) {
1468
- return {
1469
- kind: "image",
1470
- type,
1471
- file,
1472
- mime,
1473
- format: requireString(output.format, "format"),
1474
- width: requireNumber(output.width, "width"),
1475
- height: requireNumber(output.height, "height"),
1476
- animated: requireBoolean(output.animated, "animated"),
1477
- encoding: output.encoding === undefined ? undefined : parseImageEncoding(output.encoding),
1478
- data: output.data === undefined ? undefined : requireString(output.data, "data"),
1479
- ocr: optionalString(output.ocr, "ocr"),
1480
- caption: optionalString(output.caption, "caption"),
1481
- objects: parseDetectedObjects(output.objects)
1482
- };
1956
+ return { kind: "image", ...parse2(ImageDetectionInput, value, "detect output") };
1483
1957
  }
1484
1958
  if (output.language !== undefined) {
1485
- return {
1486
- kind: "source",
1487
- type,
1488
- file,
1489
- language: requireString(output.language, "language"),
1490
- engine: optionalString(output.engine, "engine"),
1491
- supported: requireBoolean(output.supported, "supported"),
1492
- mime,
1493
- syntax: optionalString(output.syntax, "syntax")
1494
- };
1959
+ return { kind: "source", ...parse2(SourceDetectionInput, value, "detect output") };
1495
1960
  }
1496
- return { kind: "text", type, file, mime };
1497
- }
1498
- function parseDocumentCache(value) {
1499
- if (!value || typeof value !== "object")
1500
- throw new Error("invalid readseek document cache");
1501
- const cache = value;
1502
- const state = requireString(cache.state, "cache.state");
1503
- if (state !== "built" && state !== "reused")
1504
- throw new Error("invalid readseek cache.state");
1505
- return {
1506
- content_hash: requireString(cache.content_hash, "cache.content_hash"),
1507
- state
1508
- };
1509
- }
1510
- function parseIdentifyOutput(value) {
1511
- if (!value || typeof value !== "object")
1512
- throw new Error("invalid readseek identify output");
1513
- const output = value;
1514
- const identifier = output.identifier;
1515
- const symbol = output.symbol;
1516
- return {
1517
- file: requireString(output.file, "file"),
1518
- language: requireString(output.language, "language"),
1519
- engine: optionalString(output.engine, "engine"),
1520
- line_count: requireNumber(output.line_count, "line_count"),
1521
- file_hash: requireString(output.file_hash, "file_hash"),
1522
- line: requireNumber(output.line, "line"),
1523
- column: requireNumber(output.column, "column"),
1524
- line_hash: requireString(output.line_hash, "line_hash"),
1525
- identifier: identifier && typeof identifier === "object" ? {
1526
- text: requireString(identifier.text, "identifier.text"),
1527
- start_column: requireNumber(identifier.start_column, "identifier.start_column"),
1528
- end_column: requireNumber(identifier.end_column, "identifier.end_column"),
1529
- start_byte: requireNumber(identifier.start_byte, "identifier.start_byte"),
1530
- end_byte: requireNumber(identifier.end_byte, "identifier.end_byte")
1531
- } : undefined,
1532
- symbol: symbol && typeof symbol === "object" ? {
1533
- name: requireString(symbol.name, "symbol.name"),
1534
- kind: requireString(symbol.kind, "symbol.kind"),
1535
- qualified_name: requireString(symbol.qualified_name, "symbol.qualified_name"),
1536
- start_line: requireNumber(symbol.start_line, "symbol.start_line"),
1537
- end_line: requireNumber(symbol.end_line, "symbol.end_line")
1538
- } : undefined
1539
- };
1961
+ return { kind: "text", ...parse2(TextDetectionInput, value, "detect output") };
1540
1962
  }
1541
1963
  function parseImageContent(value) {
1542
1964
  const detection = parseDetectOutput(value);
1543
1965
  if (detection.kind !== "image")
1544
- throw new Error("invalid readseek image content");
1966
+ throw new Error("invalid readseek image content: expected an image");
1545
1967
  return detection;
1546
1968
  }
1547
1969
  function parseTextContent(value) {
1970
+ const output = value ?? {};
1971
+ if (output.hashlines === undefined || output.start_line !== undefined)
1972
+ return parseReadOutput(value);
1973
+ const spanned = parse2(UnspannedContent, value, "content");
1974
+ return {
1975
+ ...spanned,
1976
+ start_line: spanned.hashlines[0]?.line ?? 1,
1977
+ end_line: spanned.hashlines[spanned.hashlines.length - 1]?.line ?? 0
1978
+ };
1979
+ }
1980
+ function parseDocumentCache(value) {
1981
+ return parse2(DocumentCache, value, "document cache");
1982
+ }
1983
+ function parseIdentifyOutput(value) {
1984
+ return parse2(IdentifyEnvelope, value, "identify output");
1985
+ }
1986
+ function parsePdfInspectMetadata(value) {
1548
1987
  if (!value || typeof value !== "object")
1549
- throw new Error("invalid readseek content");
1550
- const output = value;
1551
- if (output.hashlines !== undefined && output.start_line === undefined) {
1552
- const hashlines = parseSearchHashlines(output.hashlines, "content.hashlines");
1553
- const first = hashlines[0];
1554
- const last = hashlines[hashlines.length - 1];
1555
- return {
1556
- file: requireString(output.file, "file"),
1557
- language: requireString(output.language, "language"),
1558
- line_count: requireNumber(output.line_count, "line_count"),
1559
- file_hash: requireString(output.file_hash, "file_hash"),
1560
- start_line: first?.line ?? 1,
1561
- end_line: last?.line ?? 0,
1562
- hashlines
1563
- };
1988
+ return;
1989
+ if (value.format !== "pdf")
1990
+ return;
1991
+ const document = parse2(PdfInspectMetadata, value, "inspect metadata");
1992
+ return { kind: "pdf", type: "application/pdf", file: document.source, format: "pdf", pages: document.pages };
1993
+ }
1994
+ function parseEditOutput(value) {
1995
+ return parse2(EditOutputSchema, value, "edit output");
1996
+ }
1997
+ function parseRenameOutput(value) {
1998
+ return parse2(RenameOutputSchema, value, "rename output");
1999
+ }
2000
+ function parseDefOutput(value) {
2001
+ return parse2(DefEnvelope, value, "def output").locations;
2002
+ }
2003
+
2004
+ // src/client.ts
2005
+ function classifyReadSeekFailure(err) {
2006
+ const failure = err;
2007
+ const message = String(failure?.message ?? err);
2008
+ const missing = failure?.code === "ENOENT" || /Cannot find package|Cannot find module|no such file/i.test(message);
2009
+ if (missing) {
2010
+ return { code: "readseek-not-installed", message, hint: "Run npm install to install @jarkkojs/readseek." };
1564
2011
  }
1565
- return parseReadOutput(value);
2012
+ return { code: "readseek-execution-error", message };
1566
2013
  }
1567
- function parseDigestEnvelope(value) {
2014
+ function parseSourceInspectEnvelope(value) {
1568
2015
  if (!value || typeof value !== "object")
1569
- throw new Error("invalid readseek digest output");
2016
+ throw new Error("invalid readseek inspect output");
1570
2017
  const output = value;
1571
- if (output.metadata === undefined)
1572
- throw new Error("invalid readseek digest metadata");
1573
- const result = {
1574
- metadata: parseDetectOutput(output.metadata)
1575
- };
2018
+ const result = {};
2019
+ if (output.metadata !== undefined)
2020
+ result.metadata = parseDetectOutput(output.metadata);
1576
2021
  if (output.cache !== undefined)
1577
2022
  result.cache = parseDocumentCache(output.cache);
1578
2023
  if (output.content !== undefined)
@@ -1585,223 +2030,104 @@ function parseDigestEnvelope(value) {
1585
2030
  result.identity = parseIdentifyOutput(output.identity);
1586
2031
  return result;
1587
2032
  }
1588
- function parseEditOutput(value) {
1589
- if (!value || typeof value !== "object")
1590
- throw new Error("invalid readseek edit output");
1591
- const output = value;
1592
- return {
1593
- file: requireString(output.file, "file"),
1594
- before_hash: requireString(output.before_hash, "before_hash"),
1595
- file_hash: requireString(output.file_hash, "file_hash"),
1596
- plan_hash: requireString(output.plan_hash, "plan_hash"),
1597
- applied: requireBoolean(output.applied, "applied"),
1598
- changed: requireBoolean(output.changed, "changed"),
1599
- edit_count: requireNumber(output.edit_count, "edit_count"),
1600
- before_content: optionalString(output.before_content, "before_content"),
1601
- content: optionalString(output.content, "content")
1602
- };
1603
- }
1604
- function parseRenameConflicts(value, field) {
1605
- if (value === undefined)
1606
- return [];
1607
- if (!Array.isArray(value))
1608
- throw new Error(`invalid readseek ${field}`);
1609
- return value.map((item) => {
1610
- if (!item || typeof item !== "object")
1611
- throw new Error(`invalid readseek ${field} entry`);
1612
- const c = item;
1613
- return {
1614
- line: requireNumber(c.line, `${field}.line`),
1615
- column: requireNumber(c.column, `${field}.column`),
1616
- reason: requireString(c.reason, `${field}.reason`)
1617
- };
1618
- });
1619
- }
1620
- function parseRenameEdits(value, field) {
1621
- if (value === undefined)
1622
- return [];
1623
- if (!Array.isArray(value))
1624
- throw new Error(`invalid readseek ${field} entry`);
1625
- return value.map((item) => {
1626
- if (!item || typeof item !== "object")
1627
- throw new Error(`invalid readseek ${field} entry`);
1628
- const e = item;
1629
- return {
1630
- line: requireNumber(e.line, `${field}.line`),
1631
- start_column: requireNumber(e.start_column, `${field}.start_column`),
1632
- end_column: requireNumber(e.end_column, `${field}.end_column`),
1633
- start_byte: requireNumber(e.start_byte, `${field}.start_byte`),
1634
- end_byte: requireNumber(e.end_byte, `${field}.end_byte`),
1635
- occurrence: requireString(e.occurrence, `${field}.occurrence`),
1636
- line_hash: requireString(e.line_hash, `${field}.line_hash`),
1637
- text: requireString(e.text, `${field}.text`)
1638
- };
1639
- });
1640
- }
1641
- function parseRenameOutput(value) {
1642
- if (!value || typeof value !== "object")
1643
- throw new Error("invalid readseek rename output");
1644
- const output = value;
1645
- const others = output.others;
1646
- if (others !== undefined && !Array.isArray(others))
1647
- throw new Error("invalid readseek others");
1648
- return {
1649
- file: requireString(output.file, "file"),
1650
- language: requireString(output.language, "language"),
1651
- engine: optionalString(output.engine, "engine"),
1652
- file_hash: requireString(output.file_hash, "file_hash"),
1653
- old_name: requireString(output.old_name, "old_name"),
1654
- new_name: requireString(output.new_name, "new_name"),
1655
- applied: requireBoolean(output.applied, "applied"),
1656
- conflicts: parseRenameConflicts(output.conflicts, "conflicts"),
1657
- edits: parseRenameEdits(output.edits, "edits"),
1658
- others: others?.map((entry) => {
1659
- if (!entry || typeof entry !== "object")
1660
- throw new Error("invalid readseek other");
1661
- const o = entry;
1662
- return {
1663
- file: requireString(o.file, "other.file"),
1664
- language: requireString(o.language, "other.language"),
1665
- engine: optionalString(o.engine, "other.engine"),
1666
- file_hash: requireString(o.file_hash, "other.file_hash"),
1667
- conflicts: parseRenameConflicts(o.conflicts, "other.conflicts"),
1668
- edits: parseRenameEdits(o.edits, "other.edits")
1669
- };
1670
- }) ?? []
1671
- };
1672
- }
1673
- function parseDefCompact(value) {
1674
- if (!value || typeof value !== "object")
1675
- throw new Error("invalid readseek def output");
1676
- const output = value;
1677
- const locations = output.locations;
1678
- if (!Array.isArray(locations))
1679
- throw new Error("invalid readseek def locations");
1680
- return locations.map((loc) => {
1681
- if (!loc || typeof loc !== "object")
1682
- throw new Error("invalid readseek def location");
1683
- const item = loc;
1684
- return {
1685
- file: requireString(item.file, "location.file"),
1686
- line: requireNumber(item.line, "location.line"),
1687
- column: requireNumber(item.column, "location.column"),
1688
- line_hash: requireString(item.line_hash, "location.line_hash"),
1689
- text: requireString(item.text, "location.text"),
1690
- kind: optionalString(item.kind, "location.kind"),
1691
- name: optionalString(item.name, "location.name"),
1692
- qualified_name: optionalString(item.qualified_name, "location.qualified_name")
1693
- };
1694
- });
1695
- }
1696
2033
  function createReadSeekClient(runner) {
1697
- function digestArgs(target, options) {
1698
- return buildDigestArgs({
2034
+ function inspectArgs(target, options) {
2035
+ return buildInspectArgs({
1699
2036
  path: target,
2037
+ selector: options.selector,
1700
2038
  select: options.select ?? "content",
1701
- at: options.at,
2039
+ query: options.query,
1702
2040
  end: options.end,
1703
2041
  limit: options.limit,
2042
+ depth: options.depth,
1704
2043
  language: options.language,
1705
2044
  visionMode: options.visionMode,
1706
2045
  visionLevel: options.visionLevel,
1707
- stdinName: options.stdin !== undefined && target === "-" ? options.stdinName ?? target : undefined
2046
+ stdinName: options.stdinName
1708
2047
  });
1709
2048
  }
1710
- async function runDigestRaw(target, options = {}) {
1711
- const args = digestArgs(target, options);
1712
- const runOptions = {
2049
+ function inspectRunOptions(options) {
2050
+ return {
1713
2051
  signal: options.signal,
1714
2052
  stdin: options.stdin,
1715
2053
  timeoutMs: options.timeoutMs,
1716
2054
  vision: options.vision
1717
2055
  };
1718
- return runner.json(args, runOptions);
1719
2056
  }
1720
- async function digest(target, options = {}) {
1721
- return parseDigestEnvelope(await runDigestRaw(target, options));
2057
+ async function inspect(target, options = {}) {
2058
+ return runner.kdl(inspectArgs(target, options), inspectRunOptions(options));
2059
+ }
2060
+ async function inspectText(target, options = {}) {
2061
+ return runner.text(inspectArgs(target, options), inspectRunOptions(options));
1722
2062
  }
1723
- function requireDigestContent(envelope, field) {
2063
+ async function inspectSource(target, options = {}) {
2064
+ return parseSourceInspectEnvelope(await inspect(target, options));
2065
+ }
2066
+ function requireInspectMetadata(envelope, field) {
2067
+ if (envelope.metadata === undefined)
2068
+ throw new Error(`invalid readseek ${field}: missing metadata`);
2069
+ return envelope.metadata;
2070
+ }
2071
+ function requireInspectContent(envelope, field) {
1724
2072
  if (envelope.content === undefined)
1725
2073
  throw new Error(`invalid readseek ${field}: missing content`);
1726
2074
  return envelope.content;
1727
2075
  }
1728
- async function digestContent(filePath, options = {}) {
1729
- const envelope = await digest(filePath, {
1730
- select: "content",
1731
- at: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
2076
+ async function inspectContent(filePath, options = {}) {
2077
+ const inspected = await inspect(filePath, {
2078
+ select: ["metadata", "content"],
2079
+ selector: options.startLine !== undefined ? buildLineLocation(options.startLine) : undefined,
1732
2080
  end: options.endLine,
1733
2081
  visionMode: options.visionMode,
1734
2082
  signal: options.signal,
1735
2083
  vision: options.vision
1736
2084
  });
1737
- if (envelope.metadata.kind === "pdf") {
1738
- return { metadata: envelope.metadata, cache: envelope.cache };
2085
+ const pdf = parsePdfInspectMetadata(inspected);
2086
+ if (pdf)
2087
+ return { metadata: pdf };
2088
+ const envelope = parseSourceInspectEnvelope(inspected);
2089
+ const metadata = requireInspectMetadata(envelope, "content");
2090
+ const content = requireInspectContent(envelope, "content");
2091
+ if (metadata.kind === "image") {
2092
+ return { metadata, image: parseImageContent(content) };
1739
2093
  }
1740
- const content = requireDigestContent(envelope, "content");
1741
- if (envelope.metadata.kind === "image") {
1742
- return { metadata: envelope.metadata, image: parseImageContent(content) };
1743
- }
1744
- return { metadata: envelope.metadata, text: parseTextContent(content) };
2094
+ return { metadata, text: parseTextContent(content) };
1745
2095
  }
1746
2096
  return {
1747
- async view(filePath, options = {}) {
1748
- const args = buildViewArgs({
1749
- path: filePath,
1750
- node: options.node,
1751
- page: options.page,
1752
- kind: options.kind,
1753
- depth: options.depth,
1754
- outline: options.outline,
1755
- visionMode: options.visionMode,
1756
- visionLevel: options.visionLevel
1757
- });
1758
- return runner.text(args, {
2097
+ inspect,
2098
+ inspectText,
2099
+ inspectContent,
2100
+ async extract(target, selector, output, options = {}) {
2101
+ await runner.text(buildExtractArgs({
2102
+ path: target,
2103
+ selector,
2104
+ output,
2105
+ stdinName: options.stdinName
2106
+ }), {
1759
2107
  signal: options.signal,
1760
- vision: options.visionMode !== undefined
2108
+ stdin: options.stdin,
2109
+ timeoutMs: options.timeoutMs,
2110
+ cancelable: options.cancelable
1761
2111
  });
1762
2112
  },
1763
- digestRaw(target, options = {}) {
1764
- return runDigestRaw(target, options);
1765
- },
1766
- digest,
1767
- digestContent,
1768
2113
  async image(filePath, modes, options = {}) {
1769
- const requestedModes = modes.length > 1 ? ["all"] : modes;
1770
- const results = await Promise.allSettled(requestedModes.map(async (mode) => {
1771
- const digested = await digestContent(filePath, {
1772
- visionMode: mode,
1773
- signal: options.signal,
1774
- vision: true
1775
- });
1776
- if (!digested.image)
1777
- throw new Error(`readseek returned no image analysis for ${filePath}`);
1778
- return digested.image;
1779
- }));
1780
- let merged;
1781
- for (const result of results) {
1782
- if (result.status !== "fulfilled")
1783
- continue;
1784
- const detection = result.value;
1785
- merged = merged === undefined ? detection : {
1786
- ...merged,
1787
- ocr: detection.ocr ?? merged.ocr,
1788
- caption: detection.caption ?? merged.caption,
1789
- objects: detection.objects ?? merged.objects
1790
- };
1791
- }
1792
- if (merged !== undefined)
1793
- return merged;
1794
- const failure = results.find((result) => result.status === "rejected");
1795
- if (failure?.status === "rejected")
1796
- throw failure.reason;
1797
- throw new Error(`readseek returned no image analysis for ${filePath}`);
2114
+ if (modes.length === 0)
2115
+ throw new Error(`readseek returned no image analysis for ${filePath}`);
2116
+ const inspected = await inspectContent(filePath, {
2117
+ visionMode: modes.length > 1 ? "all" : modes[0],
2118
+ signal: options.signal,
2119
+ vision: true
2120
+ });
2121
+ if (!inspected.image)
2122
+ throw new Error(`readseek returned no image analysis for ${filePath}`);
2123
+ return inspected.image;
1798
2124
  },
1799
2125
  async preparedImage(filePath, options = {}) {
1800
- const digested = await digestContent(filePath, {
2126
+ const inspected = await inspectContent(filePath, {
1801
2127
  visionMode: "none",
1802
2128
  signal: options.signal
1803
2129
  });
1804
- const output = digested.image;
2130
+ const output = inspected.image;
1805
2131
  if (!output || output.encoding !== "base64" || output.data === undefined || output.mime === undefined) {
1806
2132
  throw new Error(`readseek returned no prepared image for ${filePath}`);
1807
2133
  }
@@ -1813,11 +2139,9 @@ function createReadSeekClient(runner) {
1813
2139
  pattern,
1814
2140
  language: options.language,
1815
2141
  limit: options.limit,
1816
- cached: options.cached,
1817
- others: options.others,
1818
- ignored: options.ignored
2142
+ gitScope: options.gitScope
1819
2143
  });
1820
- return parseSearchOutput(await runner.json(args, { signal: options.signal }));
2144
+ return parseSearchOutput(await runner.kdl(args, { signal: options.signal }));
1821
2145
  },
1822
2146
  async refs(target, options = {}) {
1823
2147
  const args = buildRefsArgs({
@@ -1827,26 +2151,21 @@ function createReadSeekClient(runner) {
1827
2151
  line: options.line,
1828
2152
  column: options.column,
1829
2153
  language: options.language,
1830
- cached: options.cached,
1831
- others: options.others,
1832
- ignored: options.ignored
2154
+ gitScope: options.gitScope
1833
2155
  });
1834
- return parseRefsOutput(await runner.json(args, { signal: options.signal }));
2156
+ return parseRefsOutput(await runner.kdl(args, { signal: options.signal }));
1835
2157
  },
1836
2158
  async def(target, options) {
1837
2159
  const args = buildDefArgs({
1838
2160
  path: target,
1839
2161
  name: options.name,
1840
- format: "plain",
1841
2162
  language: options.language,
1842
- cached: options.cached,
1843
- others: options.others,
1844
- ignored: options.ignored
2163
+ gitScope: options.gitScope
1845
2164
  });
1846
- return parseDefCompact(await runner.json(args, { signal: options.signal }));
2165
+ return parseDefOutput(await runner.kdl(args, { signal: options.signal }));
1847
2166
  },
1848
2167
  async check(filePath, content, options = {}) {
1849
- const envelope = await digest("-", {
2168
+ const envelope = await inspectSource("-", {
1850
2169
  select: "diagnostics",
1851
2170
  stdin: content,
1852
2171
  stdinName: filePath,
@@ -1856,12 +2175,12 @@ function createReadSeekClient(runner) {
1856
2175
  throw new Error("invalid readseek diagnostics: missing diagnostics facet");
1857
2176
  return envelope.diagnostics;
1858
2177
  },
1859
- async identify(filePath, content, options = {}) {
1860
- const envelope = await digest("-", {
2178
+ async identify(filePath, content, options) {
2179
+ const envelope = await inspectSource("-", {
1861
2180
  select: "identity",
1862
2181
  stdin: content,
1863
2182
  stdinName: filePath,
1864
- at: options.line !== undefined ? buildLineLocation(options.line, options.column) : undefined,
2183
+ selector: buildLineLocation(options.line, options.column),
1865
2184
  language: options.language,
1866
2185
  signal: options.signal
1867
2186
  });
@@ -1870,8 +2189,11 @@ function createReadSeekClient(runner) {
1870
2189
  return envelope.identity;
1871
2190
  },
1872
2191
  async detect(filePath, options = {}) {
1873
- const envelope = await digest(filePath, { select: "metadata", signal: options.signal });
1874
- return envelope.metadata;
2192
+ const inspected = await inspect(filePath, { select: "metadata", signal: options.signal });
2193
+ const pdf = parsePdfInspectMetadata(inspected);
2194
+ if (pdf)
2195
+ return pdf;
2196
+ return requireInspectMetadata(parseSourceInspectEnvelope(inspected), "metadata");
1875
2197
  },
1876
2198
  async edit(filePath, edits, options = {}) {
1877
2199
  const args = buildEditArgs({
@@ -1881,9 +2203,9 @@ function createReadSeekClient(runner) {
1881
2203
  language: options.language,
1882
2204
  requestFromStdin: true
1883
2205
  });
1884
- return parseEditOutput(await runner.json(args, {
2206
+ return parseEditOutput(await runner.kdl(args, {
1885
2207
  signal: options.signal,
1886
- stdin: JSON.stringify({ edits }),
2208
+ stdin: serializeEditPlan(edits),
1887
2209
  timeoutMs: options.timeoutMs,
1888
2210
  cancelable: options.cancelable
1889
2211
  }));
@@ -1896,88 +2218,88 @@ function createReadSeekClient(runner) {
1896
2218
  column: options.column,
1897
2219
  workspace: options.workspace,
1898
2220
  apply: options.apply,
2221
+ planHash: options.planHash,
1899
2222
  language: options.language,
1900
- cached: options.cached,
1901
- others: options.others,
1902
- ignored: options.ignored
2223
+ gitScope: options.gitScope
1903
2224
  });
1904
- return parseRenameOutput(await runner.json(args, { signal: options.signal }));
2225
+ return parseRenameOutput(await runner.kdl(args, { signal: options.signal }));
1905
2226
  }
1906
2227
  };
1907
2228
  }
1908
2229
  export {
1909
- validateVisionLevelVsMode,
1910
- validateRefsNameVsScope,
1911
- validateGitSelection,
1912
- validateExactlyOneEditVariant,
1913
- tryParseLineAnchor,
1914
- runReadSeekNodeText,
1915
- runReadSeekNodeJson,
1916
- runReadSeekNode,
1917
- runReadSeekBunText,
1918
- runReadSeekBunJson,
1919
- runReadSeekBun,
1920
- rewriteBacktickedToolNames,
1921
- resolveToolName,
1922
- resolveToCwd,
1923
- resolveScopedPath,
1924
- resolveReadSeekCacheDir,
1925
- resolveReadSeekBinaryOverride,
1926
- resolveReadSeekBinaryFromPackages,
1927
- resolveProcessLimits,
1928
- renderToolRoutingPolicy,
1929
- renderToolDescription,
1930
- renderPreferredToolDescription,
1931
- renderFallbackToolDescription,
1932
- renderCompactGuidelines,
1933
- readSeekPlatformKey,
1934
- readSeekBinaryName,
1935
- preferredToolDescriptionMap,
1936
- parseSuccessfulJson,
1937
- parseReadSeekJson,
1938
- parseLineAnchor,
1939
- isSubPath,
1940
- getToolContract,
1941
- fallbackToolDescriptionMap,
1942
- failureFromProcessResult,
1943
- expandUserPath,
1944
- ensureSuccessfulStdout,
1945
- createReadSeekClient,
1946
- coercePositiveInt,
1947
- coerceOptionalString,
1948
- coerceObviousBase10Int,
1949
- coerceNonNegativeInt,
1950
- coerceBoolean,
1951
- cleanReadSeekErrorText,
1952
- classifyReadSeekFailure,
1953
- canonicalPathKey,
1954
- buildViewArgs,
1955
- buildSearchArgs,
1956
- buildRenameArgs,
1957
- buildRefsArgs,
1958
- buildLineLocation,
1959
- buildEditArgs,
1960
- buildDigestArgs,
1961
- buildDefArgs,
1962
- appendGitSelectionArgs,
1963
- appendApplyAndPlanHash,
1964
- VISION_MODES,
1965
- VISION_LEVELS,
1966
- VISION_ANALYSIS_MODES,
1967
- TOOL_NAMES,
1968
- TOOL_DESCRIPTIONS,
1969
- TOOL_CONTRACTS,
1970
- TOOL_COMPACT_GUIDELINES,
1971
- REPLACEABLE_TOOLS,
1972
- READSEEK_PLATFORM_PACKAGES,
1973
- READSEEK_BINARY_ENV,
1974
- PARAM_DESCRIPTIONS,
1975
- HASH_LEN,
1976
- GIT_SELECTION_FLAGS,
1977
- EDIT_VARIANTS,
1978
- DOCUMENT_NODE_KINDS,
1979
- DIGEST_FACETS,
1980
- DEFAULT_READSEEK_TIMEOUT_MS,
2230
+ BUILTIN_FALLBACKS,
2231
+ DEFAULT_INSPECT_MAP_DEPTH,
1981
2232
  DEFAULT_MAX_OUTPUT_BYTES,
1982
- BUILTIN_FALLBACKS
2233
+ DEFAULT_READSEEK_TIMEOUT_MS,
2234
+ EDIT_VARIANTS,
2235
+ GIT_SCOPES,
2236
+ HASH_LEN,
2237
+ INSPECT_FACETS,
2238
+ PARAM_DESCRIPTIONS,
2239
+ READSEEK_BINARY_ENV,
2240
+ READSEEK_PLATFORM_PACKAGES,
2241
+ REPLACEABLE_TOOLS,
2242
+ TOOL_COMPACT_GUIDELINES,
2243
+ TOOL_CONTRACTS,
2244
+ TOOL_DESCRIPTIONS,
2245
+ TOOL_NAMES,
2246
+ VISION_ANALYSIS_MODES,
2247
+ VISION_LEVELS,
2248
+ VISION_MODES,
2249
+ appendApplyAndPlanHash,
2250
+ appendGitScopeArgs,
2251
+ buildDefArgs,
2252
+ buildEditArgs,
2253
+ buildExtractArgs,
2254
+ buildInspectArgs,
2255
+ buildLineLocation,
2256
+ buildRefsArgs,
2257
+ buildRenameArgs,
2258
+ buildSearchArgs,
2259
+ canonicalPathKey,
2260
+ classifyReadSeekFailure,
2261
+ cleanReadSeekErrorText,
2262
+ coerceBoolean,
2263
+ coerceNonNegativeInt,
2264
+ coerceObviousBase10Int,
2265
+ coerceOptionalString,
2266
+ coercePositiveInt,
2267
+ createReadSeekClient,
2268
+ ensureSuccessfulStdout,
2269
+ expandUserPath,
2270
+ failureFromProcessResult,
2271
+ fallbackToolDescriptionMap,
2272
+ getToolContract,
2273
+ isSubPath,
2274
+ parseLineAnchor,
2275
+ parseReadSeekKdl,
2276
+ parseSuccessfulKdl,
2277
+ preferredToolDescriptionMap,
2278
+ readSeekBinaryName,
2279
+ readSeekPlatformKey,
2280
+ readSeekProcessEnvironment,
2281
+ renderCompactGuidelines,
2282
+ renderFallbackToolDescription,
2283
+ renderPreferredToolDescription,
2284
+ renderToolDescription,
2285
+ renderToolRoutingPolicy,
2286
+ resolveProcessLimits,
2287
+ resolveReadSeekBinaryFromPackages,
2288
+ resolveReadSeekBinaryOverride,
2289
+ resolveReadSeekCacheDir,
2290
+ resolveScopedPath,
2291
+ resolveToCwd,
2292
+ resolveToolName,
2293
+ rewriteBacktickedToolNames,
2294
+ runReadSeekBun,
2295
+ runReadSeekBunKdl,
2296
+ runReadSeekBunText,
2297
+ runReadSeekNode,
2298
+ runReadSeekNodeKdl,
2299
+ runReadSeekNodeText,
2300
+ serializeEditPlan,
2301
+ tryParseLineAnchor,
2302
+ validateExactlyOneEditVariant,
2303
+ validateRefsNameVsScope,
2304
+ validateVisionLevelVsMode
1983
2305
  };