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.
Files changed (2) hide show
  1. package/dist/index.ts +130 -182
  2. 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
- if (cancelable) {
837
- const timeoutMs = options.timeoutMs ?? readSeekTimeoutMs();
838
- timeout = setTimeout(() => {
839
- child.kill("SIGKILL");
840
- fail(new Error(`readseek timed out after ${timeoutMs} ms`));
841
- }, timeoutMs);
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
- if (context.executionStarted) {
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.0",
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.0",
43
+ "@jarkkojs/readseek": "^0.9.2",
44
44
  "diff": "^9.0.0",
45
45
  "xxhash-wasm": "^1.1.0"
46
46
  },