pi-readseek 0.9.0 → 0.9.2
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.ts +130 -182
- package/package.json +2 -2
package/dist/index.ts
CHANGED
|
@@ -833,14 +833,12 @@ async function spawnReadSeekRaw(args, options = {}) {
|
|
|
833
833
|
stdio,
|
|
834
834
|
signal: cancelable ? options.signal : undefined
|
|
835
835
|
});
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
timeout.unref?.();
|
|
843
|
-
}
|
|
836
|
+
const timeoutMs = options.timeoutMs ?? readSeekTimeoutMs();
|
|
837
|
+
timeout = setTimeout(() => {
|
|
838
|
+
child.kill("SIGKILL");
|
|
839
|
+
fail(new Error(`readseek timed out after ${timeoutMs} ms`));
|
|
840
|
+
}, timeoutMs);
|
|
841
|
+
timeout.unref?.();
|
|
844
842
|
const childStdout = child.stdout;
|
|
845
843
|
const childStderr = child.stderr;
|
|
846
844
|
const childStdin = child.stdin;
|
|
@@ -3388,159 +3386,6 @@ function resolveSyntaxValidateMode(opts) {
|
|
|
3388
3386
|
return opts.syntaxValidate ?? resolveReadSeekSyntaxValidation() ?? DEFAULT;
|
|
3389
3387
|
}
|
|
3390
3388
|
|
|
3391
|
-
// src/pending-diff-preview.ts
|
|
3392
|
-
import { existsSync as existsSync2, readFileSync as readFileSync4, realpathSync, statSync as statSync3 } from "node:fs";
|
|
3393
|
-
import { dirname, isAbsolute as isAbsolute2, relative as relative2, resolve, sep } from "node:path";
|
|
3394
|
-
var PENDING_DIFF_MAX_BYTES = 1024 * 1024;
|
|
3395
|
-
function skip(reason) {
|
|
3396
|
-
return { type: "skip", reason };
|
|
3397
|
-
}
|
|
3398
|
-
function isInsidePath(parent, child) {
|
|
3399
|
-
const rel = relative2(parent, child);
|
|
3400
|
-
return rel === "" || rel !== "" && !rel.startsWith("..") && !rel.startsWith(sep) && !resolve(rel).startsWith(".." + sep);
|
|
3401
|
-
}
|
|
3402
|
-
function resolveWorkspacePreviewPath(rawPath, cwd, allowMissing) {
|
|
3403
|
-
if (typeof rawPath !== "string" || rawPath.trim() === "")
|
|
3404
|
-
return { type: "skip", reason: "missing path" };
|
|
3405
|
-
const workspace = realpathSync(cwd);
|
|
3406
|
-
const normalizedPath = rawPath.replace(/^@/, "");
|
|
3407
|
-
const explicitAbsolute = isAbsolute2(normalizedPath);
|
|
3408
|
-
const requested = resolve(workspace, normalizedPath);
|
|
3409
|
-
if (existsSync2(requested)) {
|
|
3410
|
-
const realTarget = realpathSync(requested);
|
|
3411
|
-
if (!explicitAbsolute && !isInsidePath(workspace, realTarget))
|
|
3412
|
-
return { type: "skip", reason: "path outside workspace" };
|
|
3413
|
-
return { type: "ok", path: realTarget, existed: true };
|
|
3414
|
-
}
|
|
3415
|
-
if (!explicitAbsolute && !isInsidePath(workspace, requested))
|
|
3416
|
-
return { type: "skip", reason: "path outside workspace" };
|
|
3417
|
-
if (!allowMissing)
|
|
3418
|
-
return { type: "skip", reason: "file not found" };
|
|
3419
|
-
const parent = dirname(requested);
|
|
3420
|
-
if (!existsSync2(parent))
|
|
3421
|
-
return { type: "skip", reason: "parent directory not found" };
|
|
3422
|
-
return { type: "ok", path: requested, existed: false };
|
|
3423
|
-
}
|
|
3424
|
-
function readUtf8File(filePath) {
|
|
3425
|
-
const stat2 = statSync3(filePath);
|
|
3426
|
-
if (!stat2.isFile())
|
|
3427
|
-
return { type: "skip", reason: "not a file" };
|
|
3428
|
-
if (stat2.size > PENDING_DIFF_MAX_BYTES)
|
|
3429
|
-
return { type: "skip", reason: "file too large" };
|
|
3430
|
-
const content = readFileSync4(filePath, "utf-8");
|
|
3431
|
-
if (content.includes("\x00"))
|
|
3432
|
-
return { type: "skip", reason: "binary file" };
|
|
3433
|
-
return { type: "ok", content };
|
|
3434
|
-
}
|
|
3435
|
-
function buildData(filePath, previousContent, nextContent, existed, headerLabel) {
|
|
3436
|
-
const diff = generateDiffString(normalizeToLF(previousContent), normalizeToLF(nextContent)).diff;
|
|
3437
|
-
return {
|
|
3438
|
-
type: "ok",
|
|
3439
|
-
data: {
|
|
3440
|
-
filePath,
|
|
3441
|
-
previousContent,
|
|
3442
|
-
nextContent,
|
|
3443
|
-
fileExistedBeforeWrite: existed,
|
|
3444
|
-
headerLabel,
|
|
3445
|
-
diff
|
|
3446
|
-
}
|
|
3447
|
-
};
|
|
3448
|
-
}
|
|
3449
|
-
function getEdits(input) {
|
|
3450
|
-
return Array.isArray(input.edits) ? input.edits : [];
|
|
3451
|
-
}
|
|
3452
|
-
function buildPendingWritePreviewData(input, cwd) {
|
|
3453
|
-
if (typeof input.content !== "string")
|
|
3454
|
-
return skip("missing content");
|
|
3455
|
-
if (Buffer.byteLength(input.content, "utf8") > PENDING_DIFF_MAX_BYTES)
|
|
3456
|
-
return skip("content too large");
|
|
3457
|
-
const resolved = resolveWorkspacePreviewPath(input.path, cwd, true);
|
|
3458
|
-
if (resolved.type === "skip")
|
|
3459
|
-
return resolved;
|
|
3460
|
-
const previous = resolved.existed ? readUtf8File(resolved.path) : { type: "ok", content: "" };
|
|
3461
|
-
if (previous.type === "skip")
|
|
3462
|
-
return previous;
|
|
3463
|
-
return buildData(resolved.path, previous.content, input.content, resolved.existed, resolved.existed ? "pending overwrite" : "pending create");
|
|
3464
|
-
}
|
|
3465
|
-
async function buildPendingEditPreviewData(input, cwd) {
|
|
3466
|
-
const edits = getEdits(input);
|
|
3467
|
-
if (edits.length === 0)
|
|
3468
|
-
return skip("missing edits");
|
|
3469
|
-
const resolved = resolveWorkspacePreviewPath(input.path, cwd, false);
|
|
3470
|
-
if (resolved.type === "skip")
|
|
3471
|
-
return resolved;
|
|
3472
|
-
const previous = readUtf8File(resolved.path);
|
|
3473
|
-
if (previous.type === "skip")
|
|
3474
|
-
return previous;
|
|
3475
|
-
try {
|
|
3476
|
-
const plan = await readSeekEdit(resolved.path, edits, {
|
|
3477
|
-
language: typeof input.language === "string" ? input.language : undefined
|
|
3478
|
-
});
|
|
3479
|
-
if (typeof plan.content !== "string")
|
|
3480
|
-
return skip("edit plan did not include content");
|
|
3481
|
-
return buildData(resolved.path, previous.content, plan.content, true, "pending edit");
|
|
3482
|
-
} catch (err) {
|
|
3483
|
-
return skip(`edit projection failed: ${err?.message ?? String(err)}`);
|
|
3484
|
-
}
|
|
3485
|
-
}
|
|
3486
|
-
function buildEditPreviewKey(input) {
|
|
3487
|
-
if (typeof input.path !== "string")
|
|
3488
|
-
return;
|
|
3489
|
-
const edits = getEdits(input);
|
|
3490
|
-
if (edits.length === 0)
|
|
3491
|
-
return;
|
|
3492
|
-
return JSON.stringify({ path: input.path, edits, language: input.language });
|
|
3493
|
-
}
|
|
3494
|
-
function buildWritePreviewKey(input) {
|
|
3495
|
-
if (typeof input.path !== "string" || typeof input.content !== "string")
|
|
3496
|
-
return;
|
|
3497
|
-
return JSON.stringify({ path: input.path, content: input.content });
|
|
3498
|
-
}
|
|
3499
|
-
function resolvePendingDiffPreview(context, stateKey, previewKey, compute) {
|
|
3500
|
-
if (!previewKey)
|
|
3501
|
-
return;
|
|
3502
|
-
const root = context?.state;
|
|
3503
|
-
if (!root)
|
|
3504
|
-
return;
|
|
3505
|
-
const slot = root[stateKey] ??= {};
|
|
3506
|
-
if (slot.key !== previewKey) {
|
|
3507
|
-
slot.key = previewKey;
|
|
3508
|
-
slot.data = undefined;
|
|
3509
|
-
slot.pending = false;
|
|
3510
|
-
}
|
|
3511
|
-
if (slot.data !== undefined)
|
|
3512
|
-
return slot.data;
|
|
3513
|
-
if (slot.pending)
|
|
3514
|
-
return;
|
|
3515
|
-
let value;
|
|
3516
|
-
try {
|
|
3517
|
-
value = compute();
|
|
3518
|
-
} catch (err) {
|
|
3519
|
-
const skipped = { type: "skip", reason: `projection failed: ${err?.message ?? String(err)}` };
|
|
3520
|
-
slot.data = skipped;
|
|
3521
|
-
return skipped;
|
|
3522
|
-
}
|
|
3523
|
-
if (value && typeof value.then === "function") {
|
|
3524
|
-
slot.pending = true;
|
|
3525
|
-
value.then((resolved) => {
|
|
3526
|
-
if (slot.key !== previewKey)
|
|
3527
|
-
return;
|
|
3528
|
-
slot.data = resolved;
|
|
3529
|
-
slot.pending = false;
|
|
3530
|
-
context?.invalidate?.();
|
|
3531
|
-
}).catch((err) => {
|
|
3532
|
-
if (slot.key !== previewKey)
|
|
3533
|
-
return;
|
|
3534
|
-
slot.data = { type: "skip", reason: `projection failed: ${err?.message ?? String(err)}` };
|
|
3535
|
-
slot.pending = false;
|
|
3536
|
-
context?.invalidate?.();
|
|
3537
|
-
});
|
|
3538
|
-
return;
|
|
3539
|
-
}
|
|
3540
|
-
slot.data = value;
|
|
3541
|
-
return slot.data;
|
|
3542
|
-
}
|
|
3543
|
-
|
|
3544
3389
|
// src/diff-data.ts
|
|
3545
3390
|
var MAX_INLINE_DIFF_LINE_LENGTH = 4096;
|
|
3546
3391
|
var MAX_INLINE_DIFF_TOKENS = 512;
|
|
@@ -3959,15 +3804,6 @@ function upsertTextComponent(lastComponent, text) {
|
|
|
3959
3804
|
}
|
|
3960
3805
|
|
|
3961
3806
|
// src/edit.ts
|
|
3962
|
-
var EDIT_PENDING_PREVIEW_STATE_KEY = "hashline-edit-pending-preview";
|
|
3963
|
-
function pendingPreviewLines(summary, preview, expanded) {
|
|
3964
|
-
if (!preview || preview.type !== "ok")
|
|
3965
|
-
return { lines: summary.split(`
|
|
3966
|
-
`) };
|
|
3967
|
-
const diffData = buildDiffData({ path: preview.data.filePath, oldContent: preview.data.previousContent, newContent: preview.data.nextContent, diff: preview.data.diff });
|
|
3968
|
-
const headerLine = summaryLine(preview.data.headerLabel, { hidden: !expanded });
|
|
3969
|
-
return { lines: [summary, headerLine], diffData: expanded ? diffData : undefined, headerLabel: preview.data.headerLabel };
|
|
3970
|
-
}
|
|
3971
3807
|
var hashlineEditItemSchema = Type3.Object({
|
|
3972
3808
|
set_line: Type3.Optional(Type3.Object({
|
|
3973
3809
|
anchor: Type3.String({ description: "Fresh LINE:HASH anchor for the line to replace" }),
|
|
@@ -4203,18 +4039,7 @@ function registerEditTool(pi, options = {}) {
|
|
|
4203
4039
|
text += ` ${theme.fg("dim", suffix)}`;
|
|
4204
4040
|
}
|
|
4205
4041
|
text = clampLineToWidth(text, context.width);
|
|
4206
|
-
|
|
4207
|
-
return upsertTextComponent(context.lastComponent, text);
|
|
4208
|
-
}
|
|
4209
|
-
const previewKey = buildEditPreviewKey(args ?? {});
|
|
4210
|
-
const preview = resolvePendingDiffPreview(context, EDIT_PENDING_PREVIEW_STATE_KEY, previewKey, () => buildPendingEditPreviewData(args ?? {}, context.cwd ?? process.cwd()));
|
|
4211
|
-
const expanded = !!context.expanded;
|
|
4212
|
-
const preview2 = pendingPreviewLines(text, preview, expanded);
|
|
4213
|
-
if (preview2.diffData) {
|
|
4214
|
-
return upsertDiffComponent(context.lastComponent, { prefixLines: preview2.lines, diffData: preview2.diffData, theme, expanded: true });
|
|
4215
|
-
}
|
|
4216
|
-
return upsertTextComponent(context.lastComponent, clampLinesToWidth(preview2.lines, context.width).join(`
|
|
4217
|
-
`));
|
|
4042
|
+
return upsertTextComponent(context.lastComponent, text);
|
|
4218
4043
|
},
|
|
4219
4044
|
renderResult(result, options2, theme, ...rest) {
|
|
4220
4045
|
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options2, rest, resolveReadSeekToolDisplayMode("edit") === "expanded");
|
|
@@ -5568,6 +5393,129 @@ import { dirname as dirname2, relative as relative3 } from "node:path";
|
|
|
5568
5393
|
import { withFileMutationQueue as withFileMutationQueue2, truncateHead as truncateHead4 } from "@earendil-works/pi-coding-agent";
|
|
5569
5394
|
import { Text as Text7 } from "@earendil-works/pi-tui";
|
|
5570
5395
|
import { Type as Type9 } from "@sinclair/typebox";
|
|
5396
|
+
|
|
5397
|
+
// src/pending-diff-preview.ts
|
|
5398
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4, realpathSync, statSync as statSync3 } from "node:fs";
|
|
5399
|
+
import { dirname, isAbsolute as isAbsolute2, relative as relative2, resolve, sep } from "node:path";
|
|
5400
|
+
var PENDING_DIFF_MAX_BYTES = 1024 * 1024;
|
|
5401
|
+
function skip(reason) {
|
|
5402
|
+
return { type: "skip", reason };
|
|
5403
|
+
}
|
|
5404
|
+
function isInsidePath(parent, child) {
|
|
5405
|
+
const rel = relative2(parent, child);
|
|
5406
|
+
return rel === "" || rel !== "" && !rel.startsWith("..") && !rel.startsWith(sep) && !resolve(rel).startsWith(".." + sep);
|
|
5407
|
+
}
|
|
5408
|
+
function resolveWorkspacePreviewPath(rawPath, cwd, allowMissing) {
|
|
5409
|
+
if (typeof rawPath !== "string" || rawPath.trim() === "")
|
|
5410
|
+
return { type: "skip", reason: "missing path" };
|
|
5411
|
+
const workspace = realpathSync(cwd);
|
|
5412
|
+
const normalizedPath = rawPath.replace(/^@/, "");
|
|
5413
|
+
const explicitAbsolute = isAbsolute2(normalizedPath);
|
|
5414
|
+
const requested = resolve(workspace, normalizedPath);
|
|
5415
|
+
if (existsSync2(requested)) {
|
|
5416
|
+
const realTarget = realpathSync(requested);
|
|
5417
|
+
if (!explicitAbsolute && !isInsidePath(workspace, realTarget))
|
|
5418
|
+
return { type: "skip", reason: "path outside workspace" };
|
|
5419
|
+
return { type: "ok", path: realTarget, existed: true };
|
|
5420
|
+
}
|
|
5421
|
+
if (!explicitAbsolute && !isInsidePath(workspace, requested))
|
|
5422
|
+
return { type: "skip", reason: "path outside workspace" };
|
|
5423
|
+
if (!allowMissing)
|
|
5424
|
+
return { type: "skip", reason: "file not found" };
|
|
5425
|
+
const parent = dirname(requested);
|
|
5426
|
+
if (!existsSync2(parent))
|
|
5427
|
+
return { type: "skip", reason: "parent directory not found" };
|
|
5428
|
+
return { type: "ok", path: requested, existed: false };
|
|
5429
|
+
}
|
|
5430
|
+
function readUtf8File(filePath) {
|
|
5431
|
+
const stat2 = statSync3(filePath);
|
|
5432
|
+
if (!stat2.isFile())
|
|
5433
|
+
return { type: "skip", reason: "not a file" };
|
|
5434
|
+
if (stat2.size > PENDING_DIFF_MAX_BYTES)
|
|
5435
|
+
return { type: "skip", reason: "file too large" };
|
|
5436
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
5437
|
+
if (content.includes("\x00"))
|
|
5438
|
+
return { type: "skip", reason: "binary file" };
|
|
5439
|
+
return { type: "ok", content };
|
|
5440
|
+
}
|
|
5441
|
+
function buildData(filePath, previousContent, nextContent, existed, headerLabel) {
|
|
5442
|
+
const diff = generateDiffString(normalizeToLF(previousContent), normalizeToLF(nextContent)).diff;
|
|
5443
|
+
return {
|
|
5444
|
+
type: "ok",
|
|
5445
|
+
data: {
|
|
5446
|
+
filePath,
|
|
5447
|
+
previousContent,
|
|
5448
|
+
nextContent,
|
|
5449
|
+
fileExistedBeforeWrite: existed,
|
|
5450
|
+
headerLabel,
|
|
5451
|
+
diff
|
|
5452
|
+
}
|
|
5453
|
+
};
|
|
5454
|
+
}
|
|
5455
|
+
function buildPendingWritePreviewData(input, cwd) {
|
|
5456
|
+
if (typeof input.content !== "string")
|
|
5457
|
+
return skip("missing content");
|
|
5458
|
+
if (Buffer.byteLength(input.content, "utf8") > PENDING_DIFF_MAX_BYTES)
|
|
5459
|
+
return skip("content too large");
|
|
5460
|
+
const resolved = resolveWorkspacePreviewPath(input.path, cwd, true);
|
|
5461
|
+
if (resolved.type === "skip")
|
|
5462
|
+
return resolved;
|
|
5463
|
+
const previous = resolved.existed ? readUtf8File(resolved.path) : { type: "ok", content: "" };
|
|
5464
|
+
if (previous.type === "skip")
|
|
5465
|
+
return previous;
|
|
5466
|
+
return buildData(resolved.path, previous.content, input.content, resolved.existed, resolved.existed ? "pending overwrite" : "pending create");
|
|
5467
|
+
}
|
|
5468
|
+
function buildWritePreviewKey(input) {
|
|
5469
|
+
if (typeof input.path !== "string" || typeof input.content !== "string")
|
|
5470
|
+
return;
|
|
5471
|
+
return JSON.stringify({ path: input.path, content: input.content });
|
|
5472
|
+
}
|
|
5473
|
+
function resolvePendingDiffPreview(context, stateKey, previewKey, compute) {
|
|
5474
|
+
if (!previewKey)
|
|
5475
|
+
return;
|
|
5476
|
+
const root = context?.state;
|
|
5477
|
+
if (!root)
|
|
5478
|
+
return;
|
|
5479
|
+
const slot = root[stateKey] ??= {};
|
|
5480
|
+
if (slot.key !== previewKey) {
|
|
5481
|
+
slot.key = previewKey;
|
|
5482
|
+
slot.data = undefined;
|
|
5483
|
+
slot.pending = false;
|
|
5484
|
+
}
|
|
5485
|
+
if (slot.data !== undefined)
|
|
5486
|
+
return slot.data;
|
|
5487
|
+
if (slot.pending)
|
|
5488
|
+
return;
|
|
5489
|
+
let value;
|
|
5490
|
+
try {
|
|
5491
|
+
value = compute();
|
|
5492
|
+
} catch (err) {
|
|
5493
|
+
const skipped = { type: "skip", reason: `projection failed: ${err?.message ?? String(err)}` };
|
|
5494
|
+
slot.data = skipped;
|
|
5495
|
+
return skipped;
|
|
5496
|
+
}
|
|
5497
|
+
if (value && typeof value.then === "function") {
|
|
5498
|
+
slot.pending = true;
|
|
5499
|
+
value.then((resolved) => {
|
|
5500
|
+
if (slot.key !== previewKey)
|
|
5501
|
+
return;
|
|
5502
|
+
slot.data = resolved;
|
|
5503
|
+
slot.pending = false;
|
|
5504
|
+
context?.invalidate?.();
|
|
5505
|
+
}).catch((err) => {
|
|
5506
|
+
if (slot.key !== previewKey)
|
|
5507
|
+
return;
|
|
5508
|
+
slot.data = { type: "skip", reason: `projection failed: ${err?.message ?? String(err)}` };
|
|
5509
|
+
slot.pending = false;
|
|
5510
|
+
context?.invalidate?.();
|
|
5511
|
+
});
|
|
5512
|
+
return;
|
|
5513
|
+
}
|
|
5514
|
+
slot.data = value;
|
|
5515
|
+
return slot.data;
|
|
5516
|
+
}
|
|
5517
|
+
|
|
5518
|
+
// src/write.ts
|
|
5571
5519
|
var WRITE_PENDING_PREVIEW_STATE_KEY = "hashline-write-pending-preview";
|
|
5572
5520
|
var CONTENT_PREVIEW_MAX_LINES = 200;
|
|
5573
5521
|
function formatContentPreviewLines(content, theme) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-readseek",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
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.2",
|
|
44
44
|
"diff": "^9.0.0",
|
|
45
45
|
"xxhash-wasm": "^1.1.0"
|
|
46
46
|
},
|