pi-readseek 0.9.1 → 0.9.3
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 +124 -174
- package/package.json +2 -2
package/dist/index.ts
CHANGED
|
@@ -3386,159 +3386,6 @@ function resolveSyntaxValidateMode(opts) {
|
|
|
3386
3386
|
return opts.syntaxValidate ?? resolveReadSeekSyntaxValidation() ?? DEFAULT;
|
|
3387
3387
|
}
|
|
3388
3388
|
|
|
3389
|
-
// src/pending-diff-preview.ts
|
|
3390
|
-
import { existsSync as existsSync2, readFileSync as readFileSync4, realpathSync, statSync as statSync3 } from "node:fs";
|
|
3391
|
-
import { dirname, isAbsolute as isAbsolute2, relative as relative2, resolve, sep } from "node:path";
|
|
3392
|
-
var PENDING_DIFF_MAX_BYTES = 1024 * 1024;
|
|
3393
|
-
function skip(reason) {
|
|
3394
|
-
return { type: "skip", reason };
|
|
3395
|
-
}
|
|
3396
|
-
function isInsidePath(parent, child) {
|
|
3397
|
-
const rel = relative2(parent, child);
|
|
3398
|
-
return rel === "" || rel !== "" && !rel.startsWith("..") && !rel.startsWith(sep) && !resolve(rel).startsWith(".." + sep);
|
|
3399
|
-
}
|
|
3400
|
-
function resolveWorkspacePreviewPath(rawPath, cwd, allowMissing) {
|
|
3401
|
-
if (typeof rawPath !== "string" || rawPath.trim() === "")
|
|
3402
|
-
return { type: "skip", reason: "missing path" };
|
|
3403
|
-
const workspace = realpathSync(cwd);
|
|
3404
|
-
const normalizedPath = rawPath.replace(/^@/, "");
|
|
3405
|
-
const explicitAbsolute = isAbsolute2(normalizedPath);
|
|
3406
|
-
const requested = resolve(workspace, normalizedPath);
|
|
3407
|
-
if (existsSync2(requested)) {
|
|
3408
|
-
const realTarget = realpathSync(requested);
|
|
3409
|
-
if (!explicitAbsolute && !isInsidePath(workspace, realTarget))
|
|
3410
|
-
return { type: "skip", reason: "path outside workspace" };
|
|
3411
|
-
return { type: "ok", path: realTarget, existed: true };
|
|
3412
|
-
}
|
|
3413
|
-
if (!explicitAbsolute && !isInsidePath(workspace, requested))
|
|
3414
|
-
return { type: "skip", reason: "path outside workspace" };
|
|
3415
|
-
if (!allowMissing)
|
|
3416
|
-
return { type: "skip", reason: "file not found" };
|
|
3417
|
-
const parent = dirname(requested);
|
|
3418
|
-
if (!existsSync2(parent))
|
|
3419
|
-
return { type: "skip", reason: "parent directory not found" };
|
|
3420
|
-
return { type: "ok", path: requested, existed: false };
|
|
3421
|
-
}
|
|
3422
|
-
function readUtf8File(filePath) {
|
|
3423
|
-
const stat2 = statSync3(filePath);
|
|
3424
|
-
if (!stat2.isFile())
|
|
3425
|
-
return { type: "skip", reason: "not a file" };
|
|
3426
|
-
if (stat2.size > PENDING_DIFF_MAX_BYTES)
|
|
3427
|
-
return { type: "skip", reason: "file too large" };
|
|
3428
|
-
const content = readFileSync4(filePath, "utf-8");
|
|
3429
|
-
if (content.includes("\x00"))
|
|
3430
|
-
return { type: "skip", reason: "binary file" };
|
|
3431
|
-
return { type: "ok", content };
|
|
3432
|
-
}
|
|
3433
|
-
function buildData(filePath, previousContent, nextContent, existed, headerLabel) {
|
|
3434
|
-
const diff = generateDiffString(normalizeToLF(previousContent), normalizeToLF(nextContent)).diff;
|
|
3435
|
-
return {
|
|
3436
|
-
type: "ok",
|
|
3437
|
-
data: {
|
|
3438
|
-
filePath,
|
|
3439
|
-
previousContent,
|
|
3440
|
-
nextContent,
|
|
3441
|
-
fileExistedBeforeWrite: existed,
|
|
3442
|
-
headerLabel,
|
|
3443
|
-
diff
|
|
3444
|
-
}
|
|
3445
|
-
};
|
|
3446
|
-
}
|
|
3447
|
-
function getEdits(input) {
|
|
3448
|
-
return Array.isArray(input.edits) ? input.edits : [];
|
|
3449
|
-
}
|
|
3450
|
-
function buildPendingWritePreviewData(input, cwd) {
|
|
3451
|
-
if (typeof input.content !== "string")
|
|
3452
|
-
return skip("missing content");
|
|
3453
|
-
if (Buffer.byteLength(input.content, "utf8") > PENDING_DIFF_MAX_BYTES)
|
|
3454
|
-
return skip("content too large");
|
|
3455
|
-
const resolved = resolveWorkspacePreviewPath(input.path, cwd, true);
|
|
3456
|
-
if (resolved.type === "skip")
|
|
3457
|
-
return resolved;
|
|
3458
|
-
const previous = resolved.existed ? readUtf8File(resolved.path) : { type: "ok", content: "" };
|
|
3459
|
-
if (previous.type === "skip")
|
|
3460
|
-
return previous;
|
|
3461
|
-
return buildData(resolved.path, previous.content, input.content, resolved.existed, resolved.existed ? "pending overwrite" : "pending create");
|
|
3462
|
-
}
|
|
3463
|
-
async function buildPendingEditPreviewData(input, cwd) {
|
|
3464
|
-
const edits = getEdits(input);
|
|
3465
|
-
if (edits.length === 0)
|
|
3466
|
-
return skip("missing edits");
|
|
3467
|
-
const resolved = resolveWorkspacePreviewPath(input.path, cwd, false);
|
|
3468
|
-
if (resolved.type === "skip")
|
|
3469
|
-
return resolved;
|
|
3470
|
-
const previous = readUtf8File(resolved.path);
|
|
3471
|
-
if (previous.type === "skip")
|
|
3472
|
-
return previous;
|
|
3473
|
-
try {
|
|
3474
|
-
const plan = await readSeekEdit(resolved.path, edits, {
|
|
3475
|
-
language: typeof input.language === "string" ? input.language : undefined
|
|
3476
|
-
});
|
|
3477
|
-
if (typeof plan.content !== "string")
|
|
3478
|
-
return skip("edit plan did not include content");
|
|
3479
|
-
return buildData(resolved.path, previous.content, plan.content, true, "pending edit");
|
|
3480
|
-
} catch (err) {
|
|
3481
|
-
return skip(`edit projection failed: ${err?.message ?? String(err)}`);
|
|
3482
|
-
}
|
|
3483
|
-
}
|
|
3484
|
-
function buildEditPreviewKey(input) {
|
|
3485
|
-
if (typeof input.path !== "string")
|
|
3486
|
-
return;
|
|
3487
|
-
const edits = getEdits(input);
|
|
3488
|
-
if (edits.length === 0)
|
|
3489
|
-
return;
|
|
3490
|
-
return JSON.stringify({ path: input.path, edits, language: input.language });
|
|
3491
|
-
}
|
|
3492
|
-
function buildWritePreviewKey(input) {
|
|
3493
|
-
if (typeof input.path !== "string" || typeof input.content !== "string")
|
|
3494
|
-
return;
|
|
3495
|
-
return JSON.stringify({ path: input.path, content: input.content });
|
|
3496
|
-
}
|
|
3497
|
-
function resolvePendingDiffPreview(context, stateKey, previewKey, compute) {
|
|
3498
|
-
if (!previewKey)
|
|
3499
|
-
return;
|
|
3500
|
-
const root = context?.state;
|
|
3501
|
-
if (!root)
|
|
3502
|
-
return;
|
|
3503
|
-
const slot = root[stateKey] ??= {};
|
|
3504
|
-
if (slot.key !== previewKey) {
|
|
3505
|
-
slot.key = previewKey;
|
|
3506
|
-
slot.data = undefined;
|
|
3507
|
-
slot.pending = false;
|
|
3508
|
-
}
|
|
3509
|
-
if (slot.data !== undefined)
|
|
3510
|
-
return slot.data;
|
|
3511
|
-
if (slot.pending)
|
|
3512
|
-
return;
|
|
3513
|
-
let value;
|
|
3514
|
-
try {
|
|
3515
|
-
value = compute();
|
|
3516
|
-
} catch (err) {
|
|
3517
|
-
const skipped = { type: "skip", reason: `projection failed: ${err?.message ?? String(err)}` };
|
|
3518
|
-
slot.data = skipped;
|
|
3519
|
-
return skipped;
|
|
3520
|
-
}
|
|
3521
|
-
if (value && typeof value.then === "function") {
|
|
3522
|
-
slot.pending = true;
|
|
3523
|
-
value.then((resolved) => {
|
|
3524
|
-
if (slot.key !== previewKey)
|
|
3525
|
-
return;
|
|
3526
|
-
slot.data = resolved;
|
|
3527
|
-
slot.pending = false;
|
|
3528
|
-
context?.invalidate?.();
|
|
3529
|
-
}).catch((err) => {
|
|
3530
|
-
if (slot.key !== previewKey)
|
|
3531
|
-
return;
|
|
3532
|
-
slot.data = { type: "skip", reason: `projection failed: ${err?.message ?? String(err)}` };
|
|
3533
|
-
slot.pending = false;
|
|
3534
|
-
context?.invalidate?.();
|
|
3535
|
-
});
|
|
3536
|
-
return;
|
|
3537
|
-
}
|
|
3538
|
-
slot.data = value;
|
|
3539
|
-
return slot.data;
|
|
3540
|
-
}
|
|
3541
|
-
|
|
3542
3389
|
// src/diff-data.ts
|
|
3543
3390
|
var MAX_INLINE_DIFF_LINE_LENGTH = 4096;
|
|
3544
3391
|
var MAX_INLINE_DIFF_TOKENS = 512;
|
|
@@ -3957,15 +3804,6 @@ function upsertTextComponent(lastComponent, text) {
|
|
|
3957
3804
|
}
|
|
3958
3805
|
|
|
3959
3806
|
// src/edit.ts
|
|
3960
|
-
var EDIT_PENDING_PREVIEW_STATE_KEY = "hashline-edit-pending-preview";
|
|
3961
|
-
function pendingPreviewLines(summary, preview, expanded) {
|
|
3962
|
-
if (!preview || preview.type !== "ok")
|
|
3963
|
-
return { lines: summary.split(`
|
|
3964
|
-
`) };
|
|
3965
|
-
const diffData = buildDiffData({ path: preview.data.filePath, oldContent: preview.data.previousContent, newContent: preview.data.nextContent, diff: preview.data.diff });
|
|
3966
|
-
const headerLine = summaryLine(preview.data.headerLabel, { hidden: !expanded });
|
|
3967
|
-
return { lines: [summary, headerLine], diffData: expanded ? diffData : undefined, headerLabel: preview.data.headerLabel };
|
|
3968
|
-
}
|
|
3969
3807
|
var hashlineEditItemSchema = Type3.Object({
|
|
3970
3808
|
set_line: Type3.Optional(Type3.Object({
|
|
3971
3809
|
anchor: Type3.String({ description: "Fresh LINE:HASH anchor for the line to replace" }),
|
|
@@ -4201,18 +4039,7 @@ function registerEditTool(pi, options = {}) {
|
|
|
4201
4039
|
text += ` ${theme.fg("dim", suffix)}`;
|
|
4202
4040
|
}
|
|
4203
4041
|
text = clampLineToWidth(text, context.width);
|
|
4204
|
-
|
|
4205
|
-
return upsertTextComponent(context.lastComponent, text);
|
|
4206
|
-
}
|
|
4207
|
-
const previewKey = buildEditPreviewKey(args ?? {});
|
|
4208
|
-
const preview = resolvePendingDiffPreview(context, EDIT_PENDING_PREVIEW_STATE_KEY, previewKey, () => buildPendingEditPreviewData(args ?? {}, context.cwd ?? process.cwd()));
|
|
4209
|
-
const expanded = !!context.expanded;
|
|
4210
|
-
const preview2 = pendingPreviewLines(text, preview, expanded);
|
|
4211
|
-
if (preview2.diffData) {
|
|
4212
|
-
return upsertDiffComponent(context.lastComponent, { prefixLines: preview2.lines, diffData: preview2.diffData, theme, expanded: true });
|
|
4213
|
-
}
|
|
4214
|
-
return upsertTextComponent(context.lastComponent, clampLinesToWidth(preview2.lines, context.width).join(`
|
|
4215
|
-
`));
|
|
4042
|
+
return upsertTextComponent(context.lastComponent, text);
|
|
4216
4043
|
},
|
|
4217
4044
|
renderResult(result, options2, theme, ...rest) {
|
|
4218
4045
|
const { isPartial, isError, expanded, width, context } = resolveRenderResultContext(options2, rest, resolveReadSeekToolDisplayMode("edit") === "expanded");
|
|
@@ -5566,6 +5393,129 @@ import { dirname as dirname2, relative as relative3 } from "node:path";
|
|
|
5566
5393
|
import { withFileMutationQueue as withFileMutationQueue2, truncateHead as truncateHead4 } from "@earendil-works/pi-coding-agent";
|
|
5567
5394
|
import { Text as Text7 } from "@earendil-works/pi-tui";
|
|
5568
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
|
|
5569
5519
|
var WRITE_PENDING_PREVIEW_STATE_KEY = "hashline-write-pending-preview";
|
|
5570
5520
|
var CONTENT_PREVIEW_MAX_LINES = 200;
|
|
5571
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.3",
|
|
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.3",
|
|
44
44
|
"diff": "^9.0.0",
|
|
45
45
|
"xxhash-wasm": "^1.1.0"
|
|
46
46
|
},
|