dsh-hashline-edit 0.1.0 → 0.2.0
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 +72 -70
- package/cordis.patch.yml +1 -1
- package/lib/index.js +4494 -4813
- package/lib/types/dsh/contracts.d.ts +69 -6
- package/lib/types/dsh/fs.d.ts +38 -3
- package/lib/types/dsh/guidance.d.ts +5 -5
- package/lib/types/dsh/hide.d.ts +78 -0
- package/lib/types/dsh/host-compat.d.ts +7 -78
- package/lib/types/dsh/index.d.ts +17 -7
- package/lib/types/dsh/metrics/events.d.ts +8 -1
- package/lib/types/dsh/metrics/instrument.d.ts +20 -14
- package/lib/types/dsh/metrics/observer.d.ts +28 -0
- package/lib/types/dsh/sandbox.d.ts +2 -2
- package/lib/types/dsh/shell-hook.d.ts +17 -8
- package/lib/types/dsh/shell-view.d.ts +65 -3
- package/lib/types/dsh/tools/apply-patch.d.ts +38 -0
- package/lib/types/dsh/tools/context.d.ts +19 -1
- package/lib/types/dsh/tools/output.d.ts +32 -4
- package/lib/types/dsh/tools/read.d.ts +12 -15
- package/lib/types/dsh/tools/undo.d.ts +5 -0
- package/lib/types/dsh/write-hook.d.ts +8 -22
- package/lib/types/src/apply-patch/commit.d.ts +49 -0
- package/lib/types/src/apply-patch/grammar.d.ts +29 -0
- package/lib/types/src/apply-patch/lifecycle.d.ts +32 -0
- package/lib/types/src/apply-patch/plan.d.ts +50 -0
- package/lib/types/src/apply-patch/render.d.ts +51 -0
- package/lib/types/src/apply-patch/resolve.d.ts +129 -0
- package/lib/types/src/apply-patch/tool.d.ts +54 -0
- package/lib/types/src/apply-patch/types.d.ts +362 -0
- package/lib/types/src/bash-view-banner.d.ts +20 -0
- package/lib/types/src/domain-errors.d.ts +16 -2
- package/lib/types/src/edit-undo.d.ts +11 -2
- package/lib/types/src/file-content/detection.d.ts +17 -0
- package/lib/types/src/file-content/index.d.ts +2 -2
- package/lib/types/src/file-content/preview.d.ts +1 -1
- package/lib/types/src/fs-lifecycle.d.ts +44 -0
- package/lib/types/src/hashline/resolve.d.ts +6 -0
- package/lib/types/src/host-utils.d.ts +63 -0
- package/lib/types/src/mutation-engine/batch-span-gate.d.ts +6 -0
- package/lib/types/src/mutation-engine/edit-request.d.ts +34 -0
- package/lib/types/src/mutation-engine/pipeline.d.ts +18 -3
- package/lib/types/src/mutation-engine/types.d.ts +50 -58
- package/lib/types/src/read-expansion/grammar-manifest.d.ts +4 -2
- package/lib/types/src/read-expansion/java-decoration.d.ts +21 -0
- package/lib/types/src/read-expansion/span-expansion.d.ts +6 -2
- package/lib/types/src/read-expansion/span-kind.d.ts +3 -2
- package/lib/types/src/read-expansion/vue-decoration.d.ts +14 -0
- package/lib/types/src/served-session/serve-render.d.ts +9 -3
- package/lib/types/src/undo-store.d.ts +17 -1
- package/package.json +10 -22
- package/prompts/apply-patch-guidelines.md +10 -0
- package/prompts/apply-patch.md +1 -0
- package/prompts/read-guidelines.md +5 -5
- package/prompts/read.md +1 -1
- package/prompts/undo-last-edit-guidelines.md +2 -2
- package/prompts/undo-last-edit-snippet.md +1 -1
- package/prompts/undo-last-edit.md +1 -1
- package/src/apply-patch/commit.ts +190 -0
- package/src/apply-patch/grammar.ts +620 -0
- package/src/apply-patch/index.ts +17 -0
- package/src/apply-patch/lifecycle.ts +195 -0
- package/src/apply-patch/plan.ts +800 -0
- package/src/apply-patch/render.ts +137 -0
- package/src/apply-patch/resolve.ts +470 -0
- package/src/apply-patch/tool.ts +257 -0
- package/src/apply-patch/types.ts +387 -0
- package/src/cut-repair.ts +77 -11
- package/src/domain-errors.ts +58 -10
- package/src/edit-undo.ts +379 -25
- package/src/file-content/detection.ts +34 -0
- package/src/file-content/index.ts +2 -2
- package/src/file-content/preview.ts +1 -1
- package/src/file-content/served-header.ts +1 -1
- package/src/fs-lifecycle.ts +201 -0
- package/src/hash-store.ts +18 -1
- package/src/hashline/lease-resolve.ts +14 -8
- package/src/hashline/resolve.ts +30 -27
- package/src/host-utils.ts +224 -0
- package/src/mutation-engine/batch-span-gate.ts +17 -4
- package/src/mutation-engine/edit-request.ts +39 -0
- package/src/mutation-engine/pipeline.ts +535 -63
- package/src/mutation-engine/types.ts +49 -65
- package/src/mutation-queue.ts +1 -1
- package/src/prompts.ts +0 -28
- package/src/read-expansion/ast-provider.ts +4 -0
- package/src/read-expansion/grammar-manifest.ts +21 -2
- package/src/read-expansion/java-decoration.ts +34 -0
- package/src/read-expansion/span-expansion.ts +6 -2
- package/src/read-expansion/span-kind.ts +3 -1
- package/src/read-expansion/vue-decoration.ts +43 -0
- package/src/served-session/serve-render.ts +5 -5
- package/src/undo-store.ts +74 -5
- package/lib/types/dsh/encoding-bridge.d.ts +0 -25
- package/lib/types/dsh/encoding.d.ts +0 -144
- package/lib/types/dsh/tools/edit.d.ts +0 -10
- package/lib/types/src/edit-tool.d.ts +0 -41
- package/lib/types/src/mutation-engine/engine.d.ts +0 -31
- package/lib/types/src/payload-contract.d.ts +0 -124
- package/prompts/edit-guidelines.md +0 -10
- package/prompts/edit-snippet.md +0 -1
- package/prompts/edit.md +0 -1
- package/src/bash-classifier.ts +0 -1626
- package/src/bash-conversion/decide.ts +0 -812
- package/src/bash-display-transform.ts +0 -50
- package/src/bash-multifile-config.ts +0 -239
- package/src/bash-view-config.ts +0 -158
- package/src/edit-normalize.ts +0 -8
- package/src/edit-render.ts +0 -161
- package/src/edit-tool.ts +0 -116
- package/src/edit.ts +0 -136
- package/src/integrations/pi-lens/command.ts +0 -712
- package/src/integrations/pi-lens/config.ts +0 -148
- package/src/integrations/pi-lens/io-bridge-adapter.ts +0 -234
- package/src/integrations/pi-lens/io-bridge.ts +0 -112
- package/src/integrations/pi-lens/settings-overlay.ts +0 -272
- package/src/lifecycle-hooks/index.ts +0 -1992
- package/src/lifecycle-hooks/types.ts +0 -64
- package/src/mutated-files.ts +0 -75
- package/src/mutation-engine/engine.ts +0 -173
- package/src/mutation-engine/index.ts +0 -17
- package/src/payload-contract.ts +0 -637
- package/src/preview-controller.ts +0 -84
- package/src/read.ts +0 -865
- package/src/search-hook.ts +0 -41
- package/src/store-config.ts +0 -73
- package/src/store-janitor-warning.ts +0 -169
- package/src/store-janitor.ts +0 -539
- package/src/store-kernel/settings.ts +0 -233
- package/src/store.ts +0 -154
- package/src/tui-presenter.ts +0 -134
- package/src/write-hook.ts +0 -130
package/src/cut-repair.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { restoreEndings, type LineEnding } from "./edit-diff.js";
|
|
2
2
|
import { readBytes, writeAtomic } from "./fs-write.js";
|
|
3
|
+
import { pathKind, removeFile, renameFile } from "./fs-lifecycle.js";
|
|
3
4
|
import { withSortedMutationQueues } from "./mutation-queue.js";
|
|
4
5
|
import { errCode } from "./utils.js";
|
|
5
6
|
import { loadHashStore, type HashStore } from "./hash-store.js";
|
|
@@ -73,20 +74,87 @@ function candidateFor(row: MemberRow, which: "pre" | "post"): Buffer | undefined
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
async function stateOf(row: MemberRow): Promise<MemberState> {
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
bytes = await readBytes(row.path);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
if (errCode(error) === "ENOENT") return "other";
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
77
|
+
const lifecycle = row.lifecycle ?? "edit";
|
|
83
78
|
const post = candidateFor(row, "post");
|
|
84
|
-
if (post !== undefined && bytes.equals(post)) return "post";
|
|
85
79
|
const pre = candidateFor(row, "pre");
|
|
80
|
+
const readOrUndefined = async (path: string | null | undefined): Promise<Buffer | undefined> => {
|
|
81
|
+
if (path === null || path === undefined) return undefined;
|
|
82
|
+
try {
|
|
83
|
+
return await readBytes(path);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (errCode(error) === "ENOENT") return undefined;
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
if (lifecycle === "add") {
|
|
90
|
+
const bytes = await readOrUndefined(row.path);
|
|
91
|
+
if (bytes === undefined) return "pre";
|
|
92
|
+
return post !== undefined && bytes.equals(post) ? "post" : "other";
|
|
93
|
+
}
|
|
94
|
+
if (lifecycle === "delete") {
|
|
95
|
+
const bytes = await readOrUndefined(row.path);
|
|
96
|
+
if (bytes === undefined) return "post";
|
|
97
|
+
return pre !== undefined && bytes.equals(pre) ? "pre" : "other";
|
|
98
|
+
}
|
|
99
|
+
if (lifecycle === "move") {
|
|
100
|
+
const to = await readOrUndefined(row.path);
|
|
101
|
+
const from = await readOrUndefined(row.fromPath);
|
|
102
|
+
if (to !== undefined && post !== undefined && to.equals(post)) return "post";
|
|
103
|
+
if (from !== undefined && pre !== undefined && from.equals(pre)) return "pre";
|
|
104
|
+
// WHY: crash after the rename but before the content rewrite — the destination holds the raw
|
|
105
|
+
// WHY: pre bytes and the source is gone. That is the PRE state of an as-yet-unapplied move.
|
|
106
|
+
if (to !== undefined && pre !== undefined && to.equals(pre) && from === undefined) return "pre";
|
|
107
|
+
return "other";
|
|
108
|
+
}
|
|
109
|
+
const bytes = await readOrUndefined(row.path);
|
|
110
|
+
if (bytes === undefined) return "other";
|
|
111
|
+
if (post !== undefined && bytes.equals(post)) return "post";
|
|
86
112
|
if (pre !== undefined && bytes.equals(pre)) return "pre";
|
|
87
113
|
return "other";
|
|
88
114
|
}
|
|
89
115
|
|
|
116
|
+
/** Write one lifecycle row to its named state; returns false when the row cannot express it. */
|
|
117
|
+
async function writeState(row: MemberRow, which: "pre" | "post"): Promise<boolean> {
|
|
118
|
+
const lifecycle = row.lifecycle ?? "edit";
|
|
119
|
+
const bytes = candidateFor(row, which);
|
|
120
|
+
if (lifecycle === "add") {
|
|
121
|
+
if (which === "post") {
|
|
122
|
+
if (bytes === undefined) return false;
|
|
123
|
+
await writeAtomic(row.path, bytes);
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if ((await pathKind(row.path)) !== "absent") await removeFile(row.path);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
if (lifecycle === "delete") {
|
|
130
|
+
if (which === "post") {
|
|
131
|
+
if ((await pathKind(row.path)) !== "absent") await removeFile(row.path);
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
if (bytes === undefined) return false;
|
|
135
|
+
await writeAtomic(row.path, bytes);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
if (lifecycle === "move") {
|
|
139
|
+
const source = row.fromPath;
|
|
140
|
+
if (source === null || source === undefined) return false;
|
|
141
|
+
if (which === "post") {
|
|
142
|
+
if ((await pathKind(row.path)) === "absent") {
|
|
143
|
+
if ((await pathKind(source)) === "absent") return false;
|
|
144
|
+
await renameFile(source, row.path);
|
|
145
|
+
}
|
|
146
|
+
if (bytes !== undefined) await writeAtomic(row.path, bytes);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
if ((await pathKind(row.path)) !== "absent") await renameFile(row.path, source);
|
|
150
|
+
if (bytes !== undefined) await writeAtomic(source, bytes);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (bytes === undefined) return false;
|
|
154
|
+
await writeAtomic(row.path, bytes);
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
90
158
|
/** Returns true when the resolution completed (bytes written or nothing to write) and the intent retired. */
|
|
91
159
|
async function applyResolution(
|
|
92
160
|
store: HashStore,
|
|
@@ -106,9 +174,7 @@ async function applyResolution(
|
|
|
106
174
|
if (now !== expected) return false;
|
|
107
175
|
}
|
|
108
176
|
for (const row of toWrite) {
|
|
109
|
-
|
|
110
|
-
if (bytes === undefined) return false;
|
|
111
|
-
await writeAtomic(row.path, bytes);
|
|
177
|
+
if (!(await writeState(row, which))) return false;
|
|
112
178
|
}
|
|
113
179
|
deleteCutIntent(store, txnId);
|
|
114
180
|
return true;
|
package/src/domain-errors.ts
CHANGED
|
@@ -54,19 +54,27 @@ export type DomainErrorCode =
|
|
|
54
54
|
| "E_UNDO_REVERT_FAILED"
|
|
55
55
|
| "E_UNKNOWN"
|
|
56
56
|
| "E_LARGE_FILE"
|
|
57
|
-
| "E_INTERNAL_INVARIANT"
|
|
57
|
+
| "E_INTERNAL_INVARIANT"
|
|
58
|
+
// WHY: apply_patch's two genuinely new codes (E_STALE_ANCHOR already exists above).
|
|
59
|
+
| "E_PATCH_SYNTAX"
|
|
60
|
+
| "E_UNIQUE_MATCH";
|
|
58
61
|
|
|
59
62
|
/**
|
|
60
63
|
* The registered tool whose call produced an `E_BAD_PAYLOAD`. WHY the registered name: the model
|
|
61
64
|
* reads the tool list, so `undo_last_edit` names the call it must repair and a short form would
|
|
62
65
|
* name nothing. A tool belongs here only after a live path is measured, never by assumption.
|
|
63
66
|
*/
|
|
64
|
-
export type PayloadSubject = "read" | "edit" | "undo_last_edit";
|
|
67
|
+
export type PayloadSubject = "read" | "edit" | "apply_patch" | "undo_last_edit";
|
|
65
68
|
|
|
66
69
|
// WHY: this is the runtime companion of the `PayloadSubject` union, like `isDomainErrorCode`.
|
|
67
70
|
// WHY: Only a member may name a tool.
|
|
68
71
|
// WHY: A non-member renders the neutral wording instead.
|
|
69
|
-
const PAYLOAD_SUBJECTS: readonly PayloadSubject[] = [
|
|
72
|
+
const PAYLOAD_SUBJECTS: readonly PayloadSubject[] = [
|
|
73
|
+
"read",
|
|
74
|
+
"edit",
|
|
75
|
+
"apply_patch",
|
|
76
|
+
"undo_last_edit",
|
|
77
|
+
];
|
|
70
78
|
|
|
71
79
|
export function isPayloadSubject(value: unknown): value is PayloadSubject {
|
|
72
80
|
return typeof value === "string" && (PAYLOAD_SUBJECTS as readonly string[]).includes(value);
|
|
@@ -252,6 +260,20 @@ export interface ErrorPayloadMap {
|
|
|
252
260
|
*/
|
|
253
261
|
subject?: PayloadSubject;
|
|
254
262
|
};
|
|
263
|
+
E_PATCH_SYNTAX: {
|
|
264
|
+
message: string;
|
|
265
|
+
path?: string;
|
|
266
|
+
patchLine?: number;
|
|
267
|
+
servedRows?: ServedRow[];
|
|
268
|
+
servedBlock?: string;
|
|
269
|
+
};
|
|
270
|
+
E_UNIQUE_MATCH: {
|
|
271
|
+
message: string;
|
|
272
|
+
path?: string;
|
|
273
|
+
patchLine?: number;
|
|
274
|
+
servedRows?: ServedRow[];
|
|
275
|
+
servedBlock?: string;
|
|
276
|
+
};
|
|
255
277
|
}
|
|
256
278
|
|
|
257
279
|
export interface CodeSpec<P> {
|
|
@@ -334,13 +356,29 @@ function largeFileFormat(payload: ErrorPayloadMap["E_LARGE_FILE"]): string {
|
|
|
334
356
|
return `${where} has ${observed} lines, exceeding the ${payload.limit}-line edit limit.`;
|
|
335
357
|
}
|
|
336
358
|
|
|
359
|
+
/** SAFETY: one copy of each distinct anchor, in submission order. */
|
|
360
|
+
function distinctAnchors(anchors: readonly string[]): string[] {
|
|
361
|
+
return [...new Set(anchors)];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* SAFETY: render the served evidence section at most once. A producer may hand a headline that
|
|
366
|
+
* already carries the pre-rendered section (a forwarded rejection); appending the block again would
|
|
367
|
+
* make the model pay for the same rows twice in the retry round. The `heading` + block pair is the
|
|
368
|
+
* machine-checkable section, so a bare row echoed inside a headline is not mistreated as one.
|
|
369
|
+
*/
|
|
370
|
+
function appendServedBlock(headline: string, heading: string, servedBlock: string): string {
|
|
371
|
+
if (servedBlock === "") return headline;
|
|
372
|
+
if (headline.includes(`${heading}\n${servedBlock}`)) return headline;
|
|
373
|
+
return `${headline}\n${heading}\n${servedBlock}`;
|
|
374
|
+
}
|
|
375
|
+
|
|
337
376
|
function staleAnchorFormat(payload: ErrorPayloadMap["E_STALE_ANCHOR"]): string {
|
|
338
|
-
|
|
339
|
-
return `${payload.headline}\nCurrent range:\n${payload.servedBlock}`;
|
|
377
|
+
return appendServedBlock(payload.headline, "Current range:", payload.servedBlock ?? "");
|
|
340
378
|
}
|
|
341
379
|
|
|
342
380
|
function unknownAnchorFormat(payload: ErrorPayloadMap["E_UNKNOWN_ANCHOR"]): string {
|
|
343
|
-
const anchors = payload.anchors;
|
|
381
|
+
const anchors = distinctAnchors(payload.anchors);
|
|
344
382
|
if (anchors.length === 1) {
|
|
345
383
|
return `${payload.path} has not served the anchor "${anchors[0]}".`;
|
|
346
384
|
}
|
|
@@ -356,13 +394,13 @@ function foreignHomesDisplay(homes: string[]): string {
|
|
|
356
394
|
}
|
|
357
395
|
|
|
358
396
|
function foreignAnchorFormat(payload: ErrorPayloadMap["E_FOREIGN_ANCHOR"]): string {
|
|
359
|
-
const anchors = payload.anchors;
|
|
397
|
+
const anchors = distinctAnchors(payload.anchors);
|
|
360
398
|
const noun =
|
|
361
399
|
anchors.length === 1
|
|
362
400
|
? `the anchor "${anchors[0]}"`
|
|
363
401
|
: `the anchors ${anchors.map((a) => `"${a}"`).join(", ")}`;
|
|
364
402
|
const verb = anchors.length === 1 ? "was" : "were";
|
|
365
|
-
const homes = foreignHomesDisplay(payload.homes);
|
|
403
|
+
const homes = foreignHomesDisplay([...new Set(payload.homes)]);
|
|
366
404
|
if (homes.length === 0) {
|
|
367
405
|
return `${noun} ${verb} not served for ${payload.path}.`;
|
|
368
406
|
}
|
|
@@ -423,7 +461,7 @@ export const ERROR_REGISTRY: { [K in DomainErrorCode]: CodeSpec<ErrorPayloadMap[
|
|
|
423
461
|
if (servedBlock === "" && headline === "") {
|
|
424
462
|
throw new TypeError("E_STALE_RANGE: headline must be non-empty when servedBlock is empty");
|
|
425
463
|
}
|
|
426
|
-
return
|
|
464
|
+
return appendServedBlock(headline, FRESH_READ_HEADING, servedBlock);
|
|
427
465
|
},
|
|
428
466
|
},
|
|
429
467
|
E_TARGET_LOST: {
|
|
@@ -445,7 +483,7 @@ export const ERROR_REGISTRY: { [K in DomainErrorCode]: CodeSpec<ErrorPayloadMap[
|
|
|
445
483
|
"E_UNVERIFIED_RANGE: headline must be non-empty when servedBlock is empty",
|
|
446
484
|
);
|
|
447
485
|
}
|
|
448
|
-
return
|
|
486
|
+
return appendServedBlock(resolved, FRESH_READ_HEADING, servedBlock);
|
|
449
487
|
},
|
|
450
488
|
},
|
|
451
489
|
E_MALFORMED_ANCHOR: {
|
|
@@ -562,6 +600,16 @@ export const ERROR_REGISTRY: { [K in DomainErrorCode]: CodeSpec<ErrorPayloadMap[
|
|
|
562
600
|
// WHY: no options — read needs `mode: "verbatim"` and edit needs a split, but the payload
|
|
563
601
|
// WHY: names neither tool. Add `subject` before serving advice (ADR-0047).
|
|
564
602
|
},
|
|
603
|
+
E_PATCH_SYNTAX: {
|
|
604
|
+
audience: "MODEL",
|
|
605
|
+
// WHY: the parser already rendered every problem with its patch line; the envelope carries
|
|
606
|
+
// WHY: that aggregate so one resubmission can fix every syntax problem.
|
|
607
|
+
format: ({ message }) => message,
|
|
608
|
+
},
|
|
609
|
+
E_UNIQUE_MATCH: {
|
|
610
|
+
audience: "MODEL",
|
|
611
|
+
format: ({ message }) => message,
|
|
612
|
+
},
|
|
565
613
|
};
|
|
566
614
|
|
|
567
615
|
export interface WarningPayloadMap {
|