pi-hashline-edit-pro 4.1.0 → 4.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/src/replace.ts CHANGED
@@ -22,7 +22,6 @@ import { applyEdit,
22
22
  type HEdit,
23
23
  type NEdit,
24
24
  } from "./hashline";
25
- import { commitEdit } from "./commit";
26
25
  import { withDedupRows, type RMetrics } from "./replace-response";
27
26
  import {
28
27
  type RPreview,
@@ -33,7 +32,9 @@ import { adoptAnchors, servedForPath } from "./anchor-registry";
33
32
  import { resolveTarget } from "./fs-write";
34
33
  import { toCwd } from "./paths";
35
34
  import { noopPayloadKey, markBoundaryNoop, consumeBoundaryBypass, clearBoundaryBypass } from "./boundary-bypass";
36
- import { queuedEdit, editToolBase, editRenderCallWrapper, editRenderResultWrapper, resolveEditTargetWithRequirement, throwIfStrictInput, isBoundaryDedupEnabled, withReplacePrompts, DEFAULT_EDIT_FLAGS, type EditToolFlags } from "./edit-common";
35
+ import { queuedEdit, editToolBase, editRenderCallWrapper, editRenderResultWrapper, resolveEditTargetWithRequirement, throwIfStrictInput, getBoundaryDedupMode, withReplacePrompts, DEFAULT_EDIT_FLAGS, type EditToolFlags } from "./edit-common";
36
+ import { commitEdit } from "./commit";
37
+ import { batchMemberFor, executeBatchMember, noteBatchFailure } from "./batch";
37
38
 
38
39
  export { editToolSchema, type ReqParams, assertReq };
39
40
 
@@ -47,6 +48,7 @@ export type ReplaceDetails = {
47
48
  metrics?: RMetrics;
48
49
  diffLineNumbers?: (number|undefined)[];
49
50
  warnings?: string[];
51
+ batch?: { id: number; size: number; last: boolean; total: number };
50
52
  };
51
53
 
52
54
  export interface PipelineResult {
@@ -112,13 +114,7 @@ function countLineChanges(
112
114
  };
113
115
  }
114
116
 
115
- export async function execPipeline(
116
- targetPath: string,
117
- params: ReqParams,
118
- cwd: string,
119
- options?: ExecPipelineOptions,
120
- ): Promise<PipelineResult> {
121
-
117
+ export function buildReplaceHEdit(params: ReqParams): { edit: HEdit; warnings: string[] } {
122
118
  const editWarnings: string[] = [];
123
119
  let replacementLines = params.replacement_lines;
124
120
  const expandedReplacement = decodeStringArray(replacementLines);
@@ -134,7 +130,17 @@ export async function execPipeline(
134
130
  },
135
131
  editWarnings,
136
132
  );
133
+ return { edit, warnings: editWarnings };
134
+ }
137
135
 
136
+ export async function execPipeline(
137
+ targetPath: string,
138
+ params: ReqParams,
139
+ cwd: string,
140
+ options?: ExecPipelineOptions,
141
+ ): Promise<PipelineResult> {
142
+
143
+ const { edit, warnings: editWarnings } = buildReplaceHEdit(params);
138
144
  const hashStore = options?.store ?? await loadHashStore();
139
145
  const preResolvedPath = await resolveTarget(toCwd(targetPath, cwd));
140
146
  const served = servedForPath(preResolvedPath);
@@ -143,8 +149,9 @@ export async function execPipeline(
143
149
  );
144
150
  const displayPath = relative(cwd, absolutePath).replace(/\\/g, "/") || targetPath;
145
151
 
146
- const dedupEnabled = await isBoundaryDedupEnabled();
147
- const effectiveSkipBoundaryDedup = options?.skipBoundaryDedup === true || !dedupEnabled;
152
+ const dedupMode = await getBoundaryDedupMode();
153
+ const effectiveSkipBoundaryDedup = options?.skipBoundaryDedup === true || dedupMode === "off";
154
+ const strictBoundaryDedup = options?.skipBoundaryDedup !== true && dedupMode === "strict";
148
155
  let anchorResult: ReturnType<typeof applyEdit>;
149
156
  try {
150
157
  anchorResult = applyEdit(
@@ -155,6 +162,7 @@ export async function execPipeline(
155
162
  displayPath,
156
163
  served,
157
164
  effectiveSkipBoundaryDedup,
165
+ strictBoundaryDedup,
158
166
  );
159
167
  } catch (error) {
160
168
  await noteAnchorError(absolutePath, error, originalHashes, options?.noPersist);
@@ -275,29 +283,61 @@ export function buildToolDef(flags: EditToolFlags = DEFAULT_EDIT_FLAGS): ToolDef
275
283
  removeTo: normalizedParams.remove_to,
276
284
  providedPath: normalizedParams.path,
277
285
  cwd: ctx.cwd,
286
+ }).catch((error: unknown) => {
287
+ const member = batchMemberFor(_toolCallId);
288
+ if (member) noteBatchFailure(member, error);
289
+ throw error;
278
290
  });
279
291
  return queuedEdit(targetPath, ctx.cwd, signal, async (absolutePath, mutationTargetPath) => {
280
- const dedupOn = await isBoundaryDedupEnabled();
292
+ const dedupMode = await getBoundaryDedupMode();
293
+ const dedupOn = dedupMode !== "off";
281
294
  const noopPayload = noopPayloadKey(mutationTargetPath, normalizedParams.remove_from, normalizedParams.remove_to, normalizedParams.replacement_lines);
282
295
  const boundaryBypass = dedupOn ? consumeBoundaryBypass(mutationTargetPath, noopPayload) : false;
283
- const pipe = await execPipeline(
284
- targetPath,
285
- normalizedParams,
286
- ctx.cwd,
287
- { accessMode: constants.R_OK | constants.W_OK, signal, skipBoundaryDedup: boundaryBypass },
288
- );
296
+ const strictBoundaryDedup = dedupMode === "strict" && !boundaryBypass;
297
+ const member = batchMemberFor(_toolCallId);
298
+ if (!member) {
299
+ const pipe = await execPipeline(
300
+ targetPath,
301
+ normalizedParams,
302
+ ctx.cwd,
303
+ { accessMode: constants.R_OK | constants.W_OK, signal, skipBoundaryDedup: boundaryBypass },
304
+ );
305
+ const appliedWarnings = boundaryBypass
306
+ ? ["[W_BOUNDARY_BYPASS] Boundary dedup was off for this call and is back on."]
307
+ : [];
308
+ return commitEdit(pipe, {
309
+ path: pipe.path,
310
+ absolutePath,
311
+ mutationTargetPath,
312
+ editAnchors: [normalizedParams.remove_from, normalizedParams.remove_to],
313
+ signal,
314
+ appliedWarnings,
315
+ onApplied: () => { if (dedupOn) clearBoundaryBypass(mutationTargetPath); },
316
+ onNoopDedup: dedupOn ? () => markBoundaryNoop(mutationTargetPath, noopPayload) : undefined,
317
+ });
318
+ }
319
+ let built: { edit: HEdit; warnings: string[] };
320
+ try {
321
+ built = buildReplaceHEdit(normalizedParams);
322
+ } catch (error) {
323
+ noteBatchFailure(member, error);
324
+ throw error;
325
+ }
289
326
  const appliedWarnings = boundaryBypass
290
327
  ? ["[W_BOUNDARY_BYPASS] Boundary dedup was off for this call and is back on."]
291
328
  : [];
292
- return commitEdit(pipe, {
293
- path: pipe.path,
294
- absolutePath,
329
+ return executeBatchMember({
330
+ kind: "replace",
331
+ member,
332
+ targetPath,
295
333
  mutationTargetPath,
296
- editAnchors: [normalizedParams.remove_from, normalizedParams.remove_to],
334
+ cwd: ctx.cwd,
297
335
  signal,
298
- appliedWarnings,
299
- onApplied: () => { if (dedupOn) clearBoundaryBypass(mutationTargetPath); },
300
- onNoopDedup: dedupOn ? () => markBoundaryNoop(mutationTargetPath, noopPayload) : undefined,
336
+ hedit: built.edit,
337
+ extraWarnings: [...built.warnings, ...appliedWarnings],
338
+ skipBoundaryDedup: boundaryBypass,
339
+ strictBoundaryDedup,
340
+ noopPayload,
301
341
  });
302
342
  });
303
343
  },