pi-readseek 0.9.10 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -9
- package/dist/index.ts +122 -47
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
`pi-readseek` adds ReadSeek's anchored file operations, structural search,
|
|
4
4
|
symbol navigation, and document tools to Pi. Built-ins remain available unless
|
|
5
|
-
`overrideTools` maps `edit`, `write`, or `grep` to a ReadSeek-backed
|
|
6
|
-
implementation.
|
|
5
|
+
`overrideTools` maps `read`, `edit`, `write`, or `grep` to a ReadSeek-backed
|
|
6
|
+
implementation. The `read` override runs ReadSeek's `digest` command; it does not
|
|
7
|
+
add a separate `readSeek_read` tool.
|
|
7
8
|
|
|
8
9
|
## Install
|
|
9
10
|
|
|
@@ -12,8 +13,9 @@ pi install npm:pi-readseek
|
|
|
12
13
|
```
|
|
13
14
|
|
|
14
15
|
The package depends on `@jarkkojs/readseek`, which supplies the native binary on
|
|
15
|
-
supported platforms.
|
|
16
|
-
|
|
16
|
+
supported platforms. The extension uses that packaged binary exclusively; an
|
|
17
|
+
unresolved binary indicates a broken installation rather than falling back to an
|
|
18
|
+
unrelated `readseek` executable on `PATH`.
|
|
17
19
|
|
|
18
20
|
## Tools
|
|
19
21
|
|
|
@@ -58,11 +60,13 @@ Defaults:
|
|
|
58
60
|
}
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
- `overrideTools`: any of `"edit"`, `"write"`, and `"grep"`.
|
|
62
|
-
- `imageMode`:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
- `overrideTools`: any of `"read"`, `"edit"`, `"write"`, and `"grep"`.
|
|
64
|
+
- `imageMode`: controls standalone images when `digest` overrides `read`.
|
|
65
|
+
`"auto"` passes the prepared image to image-capable Pi models and otherwise
|
|
66
|
+
runs local analysis, `"on"` always runs local analysis, and `"off"` skips the
|
|
67
|
+
image. Local analysis defaults to `all` and also supports `ocr`, `caption`, and
|
|
68
|
+
`objects` through `visionMode`. The namespaced `readSeek_digest` tool applies
|
|
69
|
+
`visionMode` directly, including `none`. Documents use `readSeek_view` with
|
|
66
70
|
optional `visionMode` and `visionLevel`. Local analysis uses
|
|
67
71
|
[Qwen3-VL-2B-Instruct](../../docs/models/qwen3-vl-2b-instruct.md).
|
|
68
72
|
- `syntaxValidation`: `"warn"` (write and warn), `"block"` (abort), or `"off"`.
|
package/dist/index.ts
CHANGED
|
@@ -403,6 +403,11 @@ var TOOL_CONTRACTS = [
|
|
|
403
403
|
];
|
|
404
404
|
// ../readseek-api/src/policy.ts
|
|
405
405
|
var REPLACEABLE_TOOLS = [
|
|
406
|
+
{
|
|
407
|
+
tool: "digest",
|
|
408
|
+
builtIn: "read",
|
|
409
|
+
benefit: "it returns LINE:HASH anchors for safe edits."
|
|
410
|
+
},
|
|
406
411
|
{
|
|
407
412
|
tool: "edit",
|
|
408
413
|
builtIn: "edit",
|
|
@@ -685,23 +690,17 @@ function resolveReadSeekBinaryFromPackages(resolver = resolveFromPackage, option
|
|
|
685
690
|
const metaPackage = options.metaPackage ?? "@jarkkojs/readseek";
|
|
686
691
|
const metaPackageJson = resolve(`${metaPackage}/package.json`);
|
|
687
692
|
const metaPackageDir = path.dirname(metaPackageJson);
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
for (const dir of (pathEnv ?? "").split(path.delimiter)) {
|
|
696
|
-
if (!dir)
|
|
697
|
-
continue;
|
|
698
|
-
const candidate = path.join(dir, name);
|
|
699
|
-
try {
|
|
700
|
-
accessSync(candidate, fsConstants.X_OK);
|
|
701
|
-
return candidate;
|
|
702
|
-
} catch {}
|
|
693
|
+
let packageJsonPath;
|
|
694
|
+
try {
|
|
695
|
+
packageJsonPath = resolve(`${platformPackage}/package.json`);
|
|
696
|
+
} catch {
|
|
697
|
+
packageJsonPath = resolve(`${platformPackage}/package.json`, {
|
|
698
|
+
paths: [metaPackageDir]
|
|
699
|
+
});
|
|
703
700
|
}
|
|
704
|
-
|
|
701
|
+
const binaryPath = path.join(path.dirname(packageJsonPath), "bin", readSeekBinaryName(platform));
|
|
702
|
+
accessSync(binaryPath, fsConstants.X_OK);
|
|
703
|
+
return binaryPath;
|
|
705
704
|
}
|
|
706
705
|
// ../readseek-api/src/json.ts
|
|
707
706
|
var READSEEK_USAGE_HINT = /\n\s*Run readseek(?: [\w-]+)* --help for more information\.?\s*$/;
|
|
@@ -2150,6 +2149,7 @@ import { readFileSync, statSync } from "node:fs";
|
|
|
2150
2149
|
import { homedir as homedir2 } from "node:os";
|
|
2151
2150
|
import { join } from "node:path";
|
|
2152
2151
|
var READSEEK_TOOL_REPLACEMENTS = {
|
|
2152
|
+
read: "readSeek_digest",
|
|
2153
2153
|
edit: "readSeek_edit",
|
|
2154
2154
|
grep: "readSeek_grep",
|
|
2155
2155
|
write: "readSeek_write"
|
|
@@ -2346,6 +2346,9 @@ function resolveReadSeekJsonSettings() {
|
|
|
2346
2346
|
warnings: [...globalResult.warnings, ...projectResult.warnings]
|
|
2347
2347
|
};
|
|
2348
2348
|
}
|
|
2349
|
+
function resolveReadSeekImageMode() {
|
|
2350
|
+
return resolveReadSeekJsonSettings().settings.imageMode ?? "auto";
|
|
2351
|
+
}
|
|
2349
2352
|
function resolveReadSeekSyntaxValidation() {
|
|
2350
2353
|
return resolveReadSeekJsonSettings().settings.syntaxValidation;
|
|
2351
2354
|
}
|
|
@@ -2359,7 +2362,7 @@ function resolveReadSeekToolDisplayMode(tool) {
|
|
|
2359
2362
|
// src/readseek-client.ts
|
|
2360
2363
|
var resolveFromHost = createRequire2(import.meta.url).resolve;
|
|
2361
2364
|
function readSeekBinaryPath() {
|
|
2362
|
-
return
|
|
2365
|
+
return resolveReadSeekBinaryFromPackages(resolveFromHost);
|
|
2363
2366
|
}
|
|
2364
2367
|
function readSeekBinaryAvailability() {
|
|
2365
2368
|
try {
|
|
@@ -5460,6 +5463,36 @@ function summarizeEnvelope(envelope, facets) {
|
|
|
5460
5463
|
parts.unshift("metadata");
|
|
5461
5464
|
return `digested ${parts.join(",") || "metadata"}`;
|
|
5462
5465
|
}
|
|
5466
|
+
function parseImageEnvelope(envelope) {
|
|
5467
|
+
if (!envelope || typeof envelope !== "object")
|
|
5468
|
+
return;
|
|
5469
|
+
const record = envelope;
|
|
5470
|
+
if (!record.metadata || typeof record.metadata !== "object")
|
|
5471
|
+
return;
|
|
5472
|
+
const metadata = record.metadata;
|
|
5473
|
+
if (typeof metadata.type !== "string" || !metadata.type.startsWith("image/"))
|
|
5474
|
+
return;
|
|
5475
|
+
if (!record.content || typeof record.content !== "object")
|
|
5476
|
+
return { metadata };
|
|
5477
|
+
const content = record.content;
|
|
5478
|
+
const mime = content.mime ?? content.type ?? metadata.mime ?? metadata.type;
|
|
5479
|
+
const prepared = content.encoding === "base64" && typeof content.data === "string" && typeof mime === "string" ? { data: content.data, mimeType: mime } : undefined;
|
|
5480
|
+
return { metadata, prepared };
|
|
5481
|
+
}
|
|
5482
|
+
function buildDigestSuccess(filePath, facets, envelope, content = [{ type: "text", text: JSON.stringify(envelope) }]) {
|
|
5483
|
+
return {
|
|
5484
|
+
content,
|
|
5485
|
+
details: {
|
|
5486
|
+
readSeekValue: {
|
|
5487
|
+
tool: "digest",
|
|
5488
|
+
ok: true,
|
|
5489
|
+
path: filePath,
|
|
5490
|
+
select: facets,
|
|
5491
|
+
envelope
|
|
5492
|
+
}
|
|
5493
|
+
}
|
|
5494
|
+
};
|
|
5495
|
+
}
|
|
5463
5496
|
async function executeDigest(opts) {
|
|
5464
5497
|
const { params, signal, cwd, onFileAnchored } = opts;
|
|
5465
5498
|
const p = params;
|
|
@@ -5471,6 +5504,8 @@ async function executeDigest(opts) {
|
|
|
5471
5504
|
path: p.path
|
|
5472
5505
|
});
|
|
5473
5506
|
}
|
|
5507
|
+
const facets = selectedFacets(select);
|
|
5508
|
+
const appliesImagePolicy = opts.imageMode !== undefined && facets.includes("content");
|
|
5474
5509
|
const end = coerceObviousBase10Int(p.end, "end");
|
|
5475
5510
|
if (!end.ok || end.value !== undefined && end.value < 1) {
|
|
5476
5511
|
const message = end.ok ? `Invalid end: expected a positive integer, received ${end.value}.` : end.message;
|
|
@@ -5487,41 +5522,67 @@ async function executeDigest(opts) {
|
|
|
5487
5522
|
path: p.path
|
|
5488
5523
|
});
|
|
5489
5524
|
}
|
|
5490
|
-
if (p.visionLevel !== undefined && p.visionMode === undefined) {
|
|
5525
|
+
if (p.visionLevel !== undefined && p.visionMode === undefined && !appliesImagePolicy) {
|
|
5491
5526
|
return buildToolErrorResult("digest", "invalid-params-combo", "visionLevel requires visionMode.", {
|
|
5492
5527
|
path: p.path
|
|
5493
5528
|
});
|
|
5494
5529
|
}
|
|
5495
5530
|
const filePath = resolveToCwd(p.path, cwd);
|
|
5496
|
-
const
|
|
5497
|
-
const
|
|
5531
|
+
const commonOptions = { select, signal };
|
|
5532
|
+
const textOptions = {
|
|
5533
|
+
...commonOptions,
|
|
5534
|
+
at,
|
|
5535
|
+
end: end.value,
|
|
5536
|
+
limit: limit.value,
|
|
5537
|
+
language: p.language
|
|
5538
|
+
};
|
|
5539
|
+
const hasTextOptions = at !== undefined || end.value !== undefined || limit.value !== undefined || p.language !== undefined;
|
|
5540
|
+
const hasVisionOptions = p.visionMode !== undefined || p.visionLevel !== undefined;
|
|
5541
|
+
const initialVisionMode = appliesImagePolicy ? undefined : p.visionMode;
|
|
5542
|
+
const initialVisionLevel = appliesImagePolicy ? undefined : p.visionLevel;
|
|
5498
5543
|
try {
|
|
5499
5544
|
const envelope = await readSeekDigestNative(filePath, {
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5545
|
+
...appliesImagePolicy ? commonOptions : textOptions,
|
|
5546
|
+
visionMode: initialVisionMode,
|
|
5547
|
+
visionLevel: initialVisionLevel,
|
|
5548
|
+
vision: initialVisionMode !== undefined && initialVisionMode !== "none"
|
|
5549
|
+
});
|
|
5550
|
+
const image = appliesImagePolicy ? parseImageEnvelope(envelope) : undefined;
|
|
5551
|
+
if (image) {
|
|
5552
|
+
const imageDetails = { metadata: image.metadata };
|
|
5553
|
+
if (opts.imageMode === "off") {
|
|
5554
|
+
return buildDigestSuccess(filePath, facets, imageDetails, [
|
|
5555
|
+
{ type: "text", text: `[Skipped standalone image: ${p.path}; readseek.imageMode is off]` }
|
|
5556
|
+
]);
|
|
5557
|
+
}
|
|
5558
|
+
if (opts.imageMode === "auto" && opts.modelSupportsImages && image.prepared) {
|
|
5559
|
+
return buildDigestSuccess(filePath, facets, imageDetails, [
|
|
5560
|
+
{ type: "image", data: image.prepared.data, mimeType: image.prepared.mimeType }
|
|
5561
|
+
]);
|
|
5562
|
+
}
|
|
5563
|
+
const visionMode = p.visionMode && p.visionMode !== "none" ? p.visionMode : "all";
|
|
5564
|
+
const analyzedEnvelope = await readSeekDigestNative(filePath, {
|
|
5565
|
+
...commonOptions,
|
|
5566
|
+
visionMode,
|
|
5567
|
+
visionLevel: p.visionLevel,
|
|
5568
|
+
vision: true
|
|
5569
|
+
});
|
|
5570
|
+
return buildDigestSuccess(filePath, facets, analyzedEnvelope);
|
|
5571
|
+
}
|
|
5572
|
+
if (p.visionLevel !== undefined && p.visionMode === undefined) {
|
|
5573
|
+
return buildToolErrorResult("digest", "invalid-params-combo", "visionLevel requires visionMode.", {
|
|
5574
|
+
path: p.path
|
|
5575
|
+
});
|
|
5576
|
+
}
|
|
5577
|
+
const textEnvelope = appliesImagePolicy && (hasTextOptions || hasVisionOptions) ? await readSeekDigestNative(filePath, {
|
|
5578
|
+
...textOptions,
|
|
5505
5579
|
visionMode: p.visionMode,
|
|
5506
5580
|
visionLevel: p.visionLevel,
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
if (hasDigestAnchors(envelope))
|
|
5581
|
+
vision: p.visionMode !== undefined && p.visionMode !== "none"
|
|
5582
|
+
}) : envelope;
|
|
5583
|
+
if (hasDigestAnchors(textEnvelope))
|
|
5511
5584
|
onFileAnchored?.(filePath);
|
|
5512
|
-
|
|
5513
|
-
return {
|
|
5514
|
-
content: [{ type: "text", text }],
|
|
5515
|
-
details: {
|
|
5516
|
-
readSeekValue: {
|
|
5517
|
-
tool: "digest",
|
|
5518
|
-
ok: true,
|
|
5519
|
-
path: filePath,
|
|
5520
|
-
select: facets,
|
|
5521
|
-
envelope
|
|
5522
|
-
}
|
|
5523
|
-
}
|
|
5524
|
-
};
|
|
5585
|
+
return buildDigestSuccess(filePath, facets, textEnvelope);
|
|
5525
5586
|
} catch (err) {
|
|
5526
5587
|
const failure = classifyReadSeekFailure(err);
|
|
5527
5588
|
return buildToolErrorResult("digest", failure.code, failure.message, failure.hint ? { hint: failure.hint } : {});
|
|
@@ -5543,7 +5604,14 @@ function registerDigestTool(pi, options = {}) {
|
|
|
5543
5604
|
promptGuidelines: promptMetadata.promptGuidelines,
|
|
5544
5605
|
parameters: digestSchema,
|
|
5545
5606
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
5546
|
-
return executeDigest({
|
|
5607
|
+
return executeDigest({
|
|
5608
|
+
params,
|
|
5609
|
+
signal,
|
|
5610
|
+
cwd: ctx.cwd,
|
|
5611
|
+
onFileAnchored: options.onFileAnchored,
|
|
5612
|
+
imageMode: options.imageMode,
|
|
5613
|
+
modelSupportsImages: ctx.model?.input.includes("image") ?? false
|
|
5614
|
+
});
|
|
5547
5615
|
},
|
|
5548
5616
|
renderCall(args, theme, ...rest) {
|
|
5549
5617
|
const context = rest[0] ?? {};
|
|
@@ -5736,7 +5804,7 @@ var READSEEK_TOOL_ENTRIES = [
|
|
|
5736
5804
|
{ builtIn: null, readSeekName: "readSeek_rename" },
|
|
5737
5805
|
{ builtIn: "write", readSeekName: "readSeek_write" },
|
|
5738
5806
|
{ builtIn: null, readSeekName: "readSeek_def" },
|
|
5739
|
-
{ builtIn:
|
|
5807
|
+
{ builtIn: "read", readSeekName: "readSeek_digest" },
|
|
5740
5808
|
{ builtIn: null, readSeekName: "readSeek_view" }
|
|
5741
5809
|
];
|
|
5742
5810
|
function formatSettingsWarning(warning) {
|
|
@@ -5756,11 +5824,13 @@ function piReadSeekExtension(pi) {
|
|
|
5756
5824
|
const { settings } = resolveReadSeekJsonSettings();
|
|
5757
5825
|
const replacedBuiltIns = new Set(settings.overrideTools ?? []);
|
|
5758
5826
|
const binaryAvailable = readSeekBinaryAvailability().available;
|
|
5759
|
-
const swap = (builtIn, readSeekName) =>
|
|
5827
|
+
const swap = (builtIn, readSeekName) => replacedBuiltIns.has(builtIn) ? builtIn : readSeekName;
|
|
5828
|
+
const readName = swap("read", "readSeek_digest");
|
|
5760
5829
|
const editName = swap("edit", "readSeek_edit");
|
|
5761
5830
|
const writeName = swap("write", "readSeek_write");
|
|
5762
5831
|
const grepName = swap("grep", "readSeek_grep");
|
|
5763
5832
|
const toolAliases = {
|
|
5833
|
+
readSeek_digest: readName,
|
|
5764
5834
|
readSeek_edit: editName,
|
|
5765
5835
|
readSeek_grep: grepName,
|
|
5766
5836
|
readSeek_write: writeName
|
|
@@ -5775,7 +5845,12 @@ function piReadSeekExtension(pi) {
|
|
|
5775
5845
|
registerRefsTool(pi, { onFileAnchored: markAnchored });
|
|
5776
5846
|
registerRenameTool(pi, { onFileMutated: forgetAnchors });
|
|
5777
5847
|
registerDefTool(pi, { onFileAnchored: markAnchored });
|
|
5778
|
-
registerDigestTool(pi, {
|
|
5848
|
+
registerDigestTool(pi, {
|
|
5849
|
+
onFileAnchored: markAnchored,
|
|
5850
|
+
name: readName,
|
|
5851
|
+
toolAliases,
|
|
5852
|
+
imageMode: replacedBuiltIns.has("read") ? resolveReadSeekImageMode() : undefined
|
|
5853
|
+
});
|
|
5779
5854
|
registerViewTool(pi);
|
|
5780
5855
|
registerWriteTool(pi, { onFileAnchored: markAnchored, name: writeName });
|
|
5781
5856
|
pi.on("before_agent_start", (event) => {
|
|
@@ -5791,7 +5866,7 @@ ${toolRoutingPolicy(policyAliases)}` };
|
|
|
5791
5866
|
const problems = warnings.map(formatSettingsWarning);
|
|
5792
5867
|
const availability = readSeekBinaryAvailability();
|
|
5793
5868
|
if (!availability.available) {
|
|
5794
|
-
problems.push(`readseek
|
|
5869
|
+
problems.push(`readseek installation is broken: ${availability.reason}`);
|
|
5795
5870
|
}
|
|
5796
5871
|
for (const problem of problems) {
|
|
5797
5872
|
if (ctx.hasUI)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-readseek",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.12",
|
|
4
4
|
"description": "Pi extension for LINE:HASH-anchored file operations and structural code navigation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"node": ">=20.0.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@jarkkojs/readseek": "^0.9.
|
|
43
|
+
"@jarkkojs/readseek": "^0.9.12",
|
|
44
44
|
"diff": "^9.0.0",
|
|
45
45
|
"xxhash-wasm": "^1.1.0"
|
|
46
46
|
},
|