@oh-my-pi/pi-coding-agent 14.4.0 → 14.4.1
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/CHANGELOG.md +14 -0
- package/package.json +7 -7
- package/src/config/prompt-templates.ts +1 -1
- package/src/config/settings-schema.ts +1 -1
- package/src/edit/line-hash.ts +13 -10
- package/src/edit/modes/atom.ts +264 -29
- package/src/edit/modes/hashline.ts +17 -22
- package/src/lsp/defaults.json +142 -652
- package/src/modes/components/session-selector.ts +3 -3
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -0
- package/src/prompts/tools/atom.md +29 -38
- package/src/prompts/tools/grep.md +2 -2
- package/src/prompts/tools/read.md +1 -1
- package/src/session/session-manager.ts +4 -1
- package/src/tools/grep.ts +2 -2
- package/src/tools/match-line-format.ts +3 -3
- package/src/tools/read.ts +1 -5
- package/src/tools/write.ts +2 -2
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* if the file has changed since the caller last read it, hash mismatches are caught
|
|
9
9
|
* before any mutation occurs.
|
|
10
10
|
*
|
|
11
|
-
* Displayed format: `LINE+ID
|
|
11
|
+
* Displayed format: `LINE+ID|TEXT`
|
|
12
12
|
* Reference format: `"LINE+ID"` (e.g. `"1ab"`)
|
|
13
13
|
*
|
|
14
14
|
* In tool JSON, each edit's `content` is `string[]` (one string per logical line) or
|
|
@@ -28,7 +28,7 @@ import { resolveToCwd } from "../../tools/path-utils";
|
|
|
28
28
|
import { enforcePlanModeWrite, resolvePlanPath } from "../../tools/plan-mode-guard";
|
|
29
29
|
import { formatCodeFrameLine } from "../../tools/render-utils";
|
|
30
30
|
import { generateDiffString } from "../diff";
|
|
31
|
-
import { computeLineHash,
|
|
31
|
+
import { computeLineHash, formatHashLine, HASHLINE_BIGRAM_RE_SRC, HASHLINE_CONTENT_SEPARATOR } from "../line-hash";
|
|
32
32
|
import { detectLineEnding, normalizeToLF, restoreLineEndings, stripBom } from "../normalize";
|
|
33
33
|
import type { EditToolDetails, LspBatchRequest } from "../renderer";
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ export interface HashMismatch {
|
|
|
38
38
|
actual: string;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export type Anchor = { line: number; hash: string };
|
|
41
|
+
export type Anchor = { line: number; hash: string; contentHint?: string };
|
|
42
42
|
export type HashlineEdit =
|
|
43
43
|
| { op: "replace_line"; pos: Anchor; lines: string[] }
|
|
44
44
|
| { op: "replace_range"; pos: Anchor; end: Anchor; lines: string[] }
|
|
@@ -47,10 +47,11 @@ export type HashlineEdit =
|
|
|
47
47
|
| { op: "append_file"; lines: string[] }
|
|
48
48
|
| { op: "prepend_file"; lines: string[] };
|
|
49
49
|
|
|
50
|
-
// Tight prefix matchers for the new format `LINE+ID
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
50
|
+
// Tight prefix matchers for the new format `LINE+ID|content`. The pipe is the
|
|
51
|
+
// canonical separator; legacy reads using `:` are tolerated for back-compat.
|
|
52
|
+
// Line-number digits are mandatory.
|
|
53
|
+
// Accept both `|` (canonical) and `:` (legacy) so re-reads of older outputs still parse.
|
|
54
|
+
const HASHLINE_CONTENT_SEPARATOR_RE = "[:|]";
|
|
54
55
|
const HASHLINE_PREFIX_RE = new RegExp(
|
|
55
56
|
`^\\s*(?:>>>|>>)?\\s*(?:\\+\\s*)?\\d+${HASHLINE_BIGRAM_RE_SRC}${HASHLINE_CONTENT_SEPARATOR_RE}`,
|
|
56
57
|
);
|
|
@@ -312,8 +313,6 @@ interface ResolvedHashlineStreamOptions {
|
|
|
312
313
|
maxChunkBytes: number;
|
|
313
314
|
}
|
|
314
315
|
|
|
315
|
-
type HashlineLineFormatter = (lineNumber: number, line: string) => string;
|
|
316
|
-
|
|
317
316
|
interface HashlineChunkEmitter {
|
|
318
317
|
pushLine: (line: string) => string[];
|
|
319
318
|
flush: () => string | undefined;
|
|
@@ -329,7 +328,7 @@ function resolveHashlineStreamOptions(options: HashlineStreamOptions): ResolvedH
|
|
|
329
328
|
|
|
330
329
|
function createHashlineChunkEmitter(
|
|
331
330
|
options: ResolvedHashlineStreamOptions,
|
|
332
|
-
formatLine
|
|
331
|
+
formatLine = formatHashLine,
|
|
333
332
|
): HashlineChunkEmitter {
|
|
334
333
|
let lineNumber = options.startLine;
|
|
335
334
|
let outLines: string[] = [];
|
|
@@ -373,10 +372,6 @@ function createHashlineChunkEmitter(
|
|
|
373
372
|
return { pushLine, flush };
|
|
374
373
|
}
|
|
375
374
|
|
|
376
|
-
function formatHashlineStreamLine(lineNumber: number, line: string): string {
|
|
377
|
-
return `${formatLineHash(lineNumber, line)}${HASHLINE_CONTENT_SEPARATOR}${line}`;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
375
|
function isReadableStream(value: unknown): value is ReadableStream<Uint8Array> {
|
|
381
376
|
return (
|
|
382
377
|
typeof value === "object" &&
|
|
@@ -416,7 +411,7 @@ export async function* streamHashLinesFromUtf8(
|
|
|
416
411
|
let pending = "";
|
|
417
412
|
let sawAnyText = false;
|
|
418
413
|
let endedWithNewline = false;
|
|
419
|
-
const emitter = createHashlineChunkEmitter(resolvedOptions
|
|
414
|
+
const emitter = createHashlineChunkEmitter(resolvedOptions);
|
|
420
415
|
|
|
421
416
|
const consumeText = (text: string): string[] => {
|
|
422
417
|
if (text.length === 0) return [];
|
|
@@ -469,7 +464,7 @@ export async function* streamHashLinesFromLines(
|
|
|
469
464
|
options: HashlineStreamOptions = {},
|
|
470
465
|
): AsyncGenerator<string> {
|
|
471
466
|
const resolvedOptions = resolveHashlineStreamOptions(options);
|
|
472
|
-
const emitter = createHashlineChunkEmitter(resolvedOptions
|
|
467
|
+
const emitter = createHashlineChunkEmitter(resolvedOptions);
|
|
473
468
|
let sawAnyLine = false;
|
|
474
469
|
|
|
475
470
|
const asyncIterator = (lines as AsyncIterable<string>)[Symbol.asyncIterator];
|
|
@@ -530,7 +525,7 @@ const MISMATCH_CONTEXT = 2;
|
|
|
530
525
|
/**
|
|
531
526
|
* Error thrown when one or more hashline references have stale hashes.
|
|
532
527
|
*
|
|
533
|
-
* Displays grep-style output with
|
|
528
|
+
* Displays grep-style output with `>` separator on mismatched lines and `:` on
|
|
534
529
|
* surrounding context, showing the correct `LINE+ID` so the caller can fix all refs at once.
|
|
535
530
|
*/
|
|
536
531
|
export class HashlineMismatchError extends Error {
|
|
@@ -552,7 +547,7 @@ export class HashlineMismatchError extends Error {
|
|
|
552
547
|
/**
|
|
553
548
|
* User-visible variant of {@link formatMessage} — omits the bigram fingerprint
|
|
554
549
|
* and uses a `│` gutter so TUI rendering is clean. The model still receives
|
|
555
|
-
* the full `LINE+ID
|
|
550
|
+
* the full `LINE+ID|content` form via {@link Error.message}.
|
|
556
551
|
*/
|
|
557
552
|
get displayMessage(): string {
|
|
558
553
|
return HashlineMismatchError.formatDisplayMessage(this.mismatches, this.fileLines);
|
|
@@ -609,7 +604,7 @@ export class HashlineMismatchError extends Error {
|
|
|
609
604
|
|
|
610
605
|
lines.push(
|
|
611
606
|
`Edit rejected: ${mismatches.length} line${mismatches.length > 1 ? "s have" : " has"} changed since the last read. The edit was NOT applied.`,
|
|
612
|
-
"Use the updated anchors shown below (
|
|
607
|
+
"Use the updated anchors shown below (`>` marks changed lines, `:` marks context) and retry the edit.",
|
|
613
608
|
);
|
|
614
609
|
lines.push("");
|
|
615
610
|
|
|
@@ -626,9 +621,9 @@ export class HashlineMismatchError extends Error {
|
|
|
626
621
|
const prefix = `${lineNum}${hash}`;
|
|
627
622
|
|
|
628
623
|
if (mismatchSet.has(lineNum)) {
|
|
629
|
-
lines.push(`${prefix}
|
|
624
|
+
lines.push(`${prefix}>${text}`);
|
|
630
625
|
} else {
|
|
631
|
-
lines.push(`${prefix}
|
|
626
|
+
lines.push(`${prefix}:${text}`);
|
|
632
627
|
}
|
|
633
628
|
}
|
|
634
629
|
return lines.join("\n");
|
|
@@ -762,7 +757,7 @@ function collectBoundaryDuplicationWarning(edit: HashlineEdit, originalFileLines
|
|
|
762
757
|
const trimmedNext = nextSurvivingLine.trim();
|
|
763
758
|
const trimmedLast = lastInsertedLine.trim();
|
|
764
759
|
if (trimmedLast.length > 0 && trimmedLast === trimmedNext) {
|
|
765
|
-
const tag =
|
|
760
|
+
const tag = formatHashLine(endLine + 1, nextSurvivingLine);
|
|
766
761
|
warnings.push(
|
|
767
762
|
`Possible boundary duplication: your last replacement line \`${trimmedLast}\` is identical to the next surviving line ${tag}. ` +
|
|
768
763
|
`If you meant to replace the entire block, set \`end\` to ${tag} instead.`,
|