@oh-my-pi/pi-coding-agent 13.19.0 → 14.0.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 (202) hide show
  1. package/CHANGELOG.md +266 -1
  2. package/package.json +86 -20
  3. package/scripts/format-prompts.ts +2 -2
  4. package/src/autoresearch/apply-contract-to-state.ts +24 -0
  5. package/src/autoresearch/contract.ts +0 -44
  6. package/src/autoresearch/dashboard.ts +1 -2
  7. package/src/autoresearch/git.ts +91 -0
  8. package/src/autoresearch/helpers.ts +49 -0
  9. package/src/autoresearch/index.ts +28 -187
  10. package/src/autoresearch/prompt.md +26 -9
  11. package/src/autoresearch/state.ts +0 -6
  12. package/src/autoresearch/tools/init-experiment.ts +202 -117
  13. package/src/autoresearch/tools/log-experiment.ts +83 -125
  14. package/src/autoresearch/tools/run-experiment.ts +48 -10
  15. package/src/autoresearch/types.ts +2 -2
  16. package/src/capability/index.ts +4 -2
  17. package/src/cli/file-processor.ts +3 -3
  18. package/src/cli/grep-cli.ts +8 -8
  19. package/src/cli/grievances-cli.ts +78 -0
  20. package/src/cli/read-cli.ts +67 -0
  21. package/src/cli/setup-cli.ts +4 -4
  22. package/src/cli/update-cli.ts +3 -3
  23. package/src/cli.ts +2 -0
  24. package/src/commands/grep.ts +6 -1
  25. package/src/commands/grievances.ts +20 -0
  26. package/src/commands/read.ts +33 -0
  27. package/src/commit/agentic/agent.ts +5 -5
  28. package/src/commit/agentic/index.ts +3 -4
  29. package/src/commit/agentic/tools/analyze-file.ts +3 -3
  30. package/src/commit/agentic/validation.ts +1 -1
  31. package/src/commit/analysis/conventional.ts +4 -4
  32. package/src/commit/analysis/summary.ts +3 -3
  33. package/src/commit/changelog/generate.ts +4 -4
  34. package/src/commit/map-reduce/map-phase.ts +4 -4
  35. package/src/commit/map-reduce/reduce-phase.ts +4 -4
  36. package/src/commit/pipeline.ts +3 -4
  37. package/src/config/prompt-templates.ts +44 -226
  38. package/src/config/resolve-config-value.ts +4 -2
  39. package/src/config/settings-schema.ts +54 -2
  40. package/src/config/settings.ts +25 -26
  41. package/src/dap/client.ts +674 -0
  42. package/src/dap/config.ts +150 -0
  43. package/src/dap/defaults.json +211 -0
  44. package/src/dap/index.ts +4 -0
  45. package/src/dap/session.ts +1255 -0
  46. package/src/dap/types.ts +600 -0
  47. package/src/debug/log-viewer.ts +3 -2
  48. package/src/discovery/builtin.ts +1 -2
  49. package/src/discovery/codex.ts +2 -2
  50. package/src/discovery/github.ts +2 -1
  51. package/src/discovery/helpers.ts +2 -2
  52. package/src/discovery/opencode.ts +2 -2
  53. package/src/edit/diff.ts +818 -0
  54. package/src/edit/index.ts +309 -0
  55. package/src/edit/line-hash.ts +67 -0
  56. package/src/edit/modes/chunk.ts +454 -0
  57. package/src/{patch → edit/modes}/hashline.ts +741 -361
  58. package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
  59. package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
  60. package/src/{patch → edit}/normalize.ts +97 -76
  61. package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
  62. package/src/exec/bash-executor.ts +4 -2
  63. package/src/exec/idle-timeout-watchdog.ts +126 -0
  64. package/src/exec/non-interactive-env.ts +5 -0
  65. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
  66. package/src/extensibility/custom-commands/bundled/review/index.ts +2 -2
  67. package/src/extensibility/custom-commands/loader.ts +1 -2
  68. package/src/extensibility/custom-tools/loader.ts +34 -11
  69. package/src/extensibility/extensions/loader.ts +9 -4
  70. package/src/extensibility/extensions/runner.ts +24 -1
  71. package/src/extensibility/extensions/types.ts +1 -1
  72. package/src/extensibility/hooks/loader.ts +5 -6
  73. package/src/extensibility/hooks/types.ts +1 -1
  74. package/src/extensibility/plugins/doctor.ts +2 -1
  75. package/src/extensibility/slash-commands.ts +3 -7
  76. package/src/index.ts +2 -1
  77. package/src/internal-urls/docs-index.generated.ts +11 -11
  78. package/src/ipy/executor.ts +58 -17
  79. package/src/ipy/gateway-coordinator.ts +6 -4
  80. package/src/ipy/kernel.ts +45 -22
  81. package/src/ipy/runtime.ts +2 -2
  82. package/src/lsp/client.ts +7 -4
  83. package/src/lsp/clients/lsp-linter-client.ts +4 -4
  84. package/src/lsp/config.ts +2 -2
  85. package/src/lsp/defaults.json +688 -154
  86. package/src/lsp/index.ts +234 -45
  87. package/src/lsp/lspmux.ts +2 -2
  88. package/src/lsp/startup-events.ts +13 -0
  89. package/src/lsp/types.ts +12 -1
  90. package/src/lsp/utils.ts +8 -1
  91. package/src/main.ts +102 -46
  92. package/src/memories/index.ts +4 -5
  93. package/src/modes/acp/acp-agent.ts +563 -163
  94. package/src/modes/acp/acp-event-mapper.ts +9 -1
  95. package/src/modes/acp/acp-mode.ts +4 -2
  96. package/src/modes/components/agent-dashboard.ts +3 -4
  97. package/src/modes/components/diff.ts +6 -7
  98. package/src/modes/components/read-tool-group.ts +6 -12
  99. package/src/modes/components/settings-defs.ts +5 -0
  100. package/src/modes/components/tool-execution.ts +1 -1
  101. package/src/modes/components/welcome.ts +1 -1
  102. package/src/modes/controllers/btw-controller.ts +2 -2
  103. package/src/modes/controllers/command-controller.ts +3 -2
  104. package/src/modes/controllers/input-controller.ts +12 -8
  105. package/src/modes/index.ts +20 -2
  106. package/src/modes/interactive-mode.ts +94 -37
  107. package/src/modes/rpc/host-tools.ts +186 -0
  108. package/src/modes/rpc/rpc-client.ts +178 -13
  109. package/src/modes/rpc/rpc-mode.ts +73 -3
  110. package/src/modes/rpc/rpc-types.ts +53 -1
  111. package/src/modes/theme/theme.ts +80 -8
  112. package/src/modes/types.ts +2 -2
  113. package/src/prompts/system/system-prompt.md +2 -1
  114. package/src/prompts/tools/chunk-edit.md +219 -0
  115. package/src/prompts/tools/debug.md +43 -0
  116. package/src/prompts/tools/grep.md +3 -0
  117. package/src/prompts/tools/lsp.md +5 -5
  118. package/src/prompts/tools/read-chunk.md +17 -0
  119. package/src/prompts/tools/read.md +19 -5
  120. package/src/sdk.ts +190 -154
  121. package/src/secrets/obfuscator.ts +1 -1
  122. package/src/session/agent-session.ts +306 -256
  123. package/src/session/agent-storage.ts +12 -12
  124. package/src/session/compaction/branch-summarization.ts +3 -3
  125. package/src/session/compaction/compaction.ts +5 -6
  126. package/src/session/compaction/utils.ts +3 -3
  127. package/src/session/history-storage.ts +62 -19
  128. package/src/session/messages.ts +3 -3
  129. package/src/session/session-dump-format.ts +203 -0
  130. package/src/session/session-storage.ts +4 -2
  131. package/src/session/streaming-output.ts +1 -1
  132. package/src/session/tool-choice-queue.ts +213 -0
  133. package/src/slash-commands/builtin-registry.ts +56 -8
  134. package/src/ssh/connection-manager.ts +2 -2
  135. package/src/ssh/sshfs-mount.ts +5 -5
  136. package/src/stt/downloader.ts +4 -4
  137. package/src/stt/recorder.ts +4 -4
  138. package/src/stt/transcriber.ts +2 -2
  139. package/src/system-prompt.ts +21 -13
  140. package/src/task/agents.ts +5 -6
  141. package/src/task/commands.ts +2 -5
  142. package/src/task/executor.ts +4 -4
  143. package/src/task/index.ts +3 -4
  144. package/src/task/template.ts +2 -2
  145. package/src/task/worktree.ts +4 -4
  146. package/src/tools/ask.ts +2 -3
  147. package/src/tools/ast-edit.ts +7 -7
  148. package/src/tools/ast-grep.ts +7 -7
  149. package/src/tools/auto-generated-guard.ts +36 -41
  150. package/src/tools/await-tool.ts +2 -2
  151. package/src/tools/bash.ts +5 -23
  152. package/src/tools/browser.ts +4 -5
  153. package/src/tools/calculator.ts +2 -3
  154. package/src/tools/cancel-job.ts +2 -2
  155. package/src/tools/checkpoint.ts +3 -3
  156. package/src/tools/debug.ts +1007 -0
  157. package/src/tools/exit-plan-mode.ts +2 -3
  158. package/src/tools/fetch.ts +67 -3
  159. package/src/tools/find.ts +4 -5
  160. package/src/tools/fs-cache-invalidation.ts +5 -0
  161. package/src/tools/gemini-image.ts +13 -5
  162. package/src/tools/gh.ts +10 -11
  163. package/src/tools/grep.ts +57 -9
  164. package/src/tools/index.ts +44 -22
  165. package/src/tools/inspect-image.ts +4 -4
  166. package/src/tools/output-meta.ts +1 -1
  167. package/src/tools/python.ts +19 -6
  168. package/src/tools/read.ts +198 -67
  169. package/src/tools/render-mermaid.ts +2 -3
  170. package/src/tools/render-utils.ts +20 -6
  171. package/src/tools/renderers.ts +3 -1
  172. package/src/tools/report-tool-issue.ts +80 -0
  173. package/src/tools/resolve.ts +70 -39
  174. package/src/tools/search-tool-bm25.ts +2 -2
  175. package/src/tools/ssh.ts +2 -2
  176. package/src/tools/todo-write.ts +2 -2
  177. package/src/tools/tool-timeouts.ts +1 -0
  178. package/src/tools/write.ts +5 -6
  179. package/src/tui/tree-list.ts +3 -1
  180. package/src/utils/clipboard.ts +80 -0
  181. package/src/utils/commit-message-generator.ts +2 -3
  182. package/src/utils/edit-mode.ts +49 -0
  183. package/src/utils/file-display-mode.ts +6 -5
  184. package/src/utils/file-mentions.ts +8 -7
  185. package/src/utils/git.ts +4 -4
  186. package/src/utils/image-loading.ts +98 -0
  187. package/src/utils/title-generator.ts +2 -3
  188. package/src/utils/tools-manager.ts +6 -6
  189. package/src/web/scrapers/choosealicense.ts +1 -1
  190. package/src/web/search/index.ts +3 -3
  191. package/src/autoresearch/command-initialize.md +0 -34
  192. package/src/patch/diff.ts +0 -433
  193. package/src/patch/index.ts +0 -888
  194. package/src/patch/parser.ts +0 -532
  195. package/src/patch/types.ts +0 -292
  196. package/src/prompts/agents/oracle.md +0 -77
  197. package/src/tools/pending-action.ts +0 -49
  198. package/src/utils/child-process.ts +0 -88
  199. package/src/utils/frontmatter.ts +0 -117
  200. package/src/utils/image-input.ts +0 -274
  201. package/src/utils/mime.ts +0 -53
  202. package/src/utils/prompt-format.ts +0 -170
@@ -0,0 +1,309 @@
1
+ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
+ import { prompt } from "@oh-my-pi/pi-utils";
3
+ import {
4
+ createLspWritethrough,
5
+ type FileDiagnosticsResult,
6
+ type WritethroughCallback,
7
+ type WritethroughDeferredHandle,
8
+ writethroughNoop,
9
+ } from "../lsp";
10
+ import chunkEditDescription from "../prompts/tools/chunk-edit.md" with { type: "text" };
11
+ import hashlineDescription from "../prompts/tools/hashline.md" with { type: "text" };
12
+ import patchDescription from "../prompts/tools/patch.md" with { type: "text" };
13
+ import replaceDescription from "../prompts/tools/replace.md" with { type: "text" };
14
+ import type { ToolSession } from "../tools";
15
+ import { type EditMode, normalizeEditMode, resolveEditMode } from "../utils/edit-mode";
16
+ import {
17
+ type ChunkParams,
18
+ chunkEditParamsSchema,
19
+ executeChunkMode,
20
+ isChunkParams,
21
+ resolveAnchorStyle,
22
+ } from "./modes/chunk";
23
+ import { executeHashlineMode, type HashlineParams, hashlineEditParamsSchema, isHashlineParams } from "./modes/hashline";
24
+ import { executePatchMode, isPatchParams, type PatchParams, patchEditSchema } from "./modes/patch";
25
+ import { executeReplaceMode, isReplaceParams, type ReplaceParams, replaceEditSchema } from "./modes/replace";
26
+ import { type EditToolDetails, getLspBatchRequest, type LspBatchRequest } from "./renderer";
27
+
28
+ export { DEFAULT_EDIT_MODE, type EditMode, normalizeEditMode } from "../utils/edit-mode";
29
+ export * from "./diff";
30
+ export * from "./line-hash";
31
+ export * from "./modes/chunk";
32
+ export * from "./modes/hashline";
33
+ export * from "./modes/patch";
34
+ export * from "./modes/replace";
35
+ export * from "./normalize";
36
+ export * from "./renderer";
37
+
38
+ type TInput =
39
+ | typeof replaceEditSchema
40
+ | typeof patchEditSchema
41
+ | typeof hashlineEditParamsSchema
42
+ | typeof chunkEditParamsSchema;
43
+
44
+ type EditParams = ReplaceParams | PatchParams | HashlineParams | ChunkParams;
45
+
46
+ type ModeExecutionArgs = {
47
+ params: EditParams;
48
+ signal: AbortSignal | undefined;
49
+ batchRequest: LspBatchRequest | undefined;
50
+ };
51
+
52
+ type EditModeDefinition = {
53
+ description: (session: ToolSession) => string;
54
+ parameters: TInput;
55
+ invalidParamsMessage: string;
56
+ validate: (params: EditParams) => boolean;
57
+ execute: (tool: EditTool, args: ModeExecutionArgs) => Promise<AgentToolResult<EditToolDetails, TInput>>;
58
+ };
59
+
60
+ function resolveConfiguredEditMode(rawEditMode: string): EditMode | undefined {
61
+ if (!rawEditMode || rawEditMode === "auto") {
62
+ return undefined;
63
+ }
64
+
65
+ const editMode = normalizeEditMode(rawEditMode);
66
+ if (!editMode) {
67
+ throw new Error(`Invalid PI_EDIT_VARIANT: ${rawEditMode}`);
68
+ }
69
+
70
+ return editMode;
71
+ }
72
+
73
+ function resolveAllowFuzzy(session: ToolSession, rawValue: string): boolean {
74
+ switch (rawValue) {
75
+ case "true":
76
+ case "1":
77
+ return true;
78
+ case "false":
79
+ case "0":
80
+ return false;
81
+ case "auto":
82
+ return session.settings.get("edit.fuzzyMatch");
83
+ default:
84
+ throw new Error(`Invalid PI_EDIT_FUZZY: ${rawValue}`);
85
+ }
86
+ }
87
+
88
+ function resolveFuzzyThreshold(session: ToolSession, rawValue: string): number {
89
+ if (rawValue === "auto") {
90
+ return session.settings.get("edit.fuzzyThreshold");
91
+ }
92
+
93
+ const threshold = Number.parseFloat(rawValue);
94
+ if (Number.isNaN(threshold) || threshold < 0 || threshold > 1) {
95
+ throw new Error(`Invalid PI_EDIT_FUZZY_THRESHOLD: ${rawValue}`);
96
+ }
97
+
98
+ return threshold;
99
+ }
100
+
101
+ function createEditWritethrough(session: ToolSession): WritethroughCallback {
102
+ const enableLsp = session.enableLsp ?? true;
103
+ const enableDiagnostics = enableLsp && session.settings.get("lsp.diagnosticsOnEdit");
104
+ const enableFormat = enableLsp && session.settings.get("lsp.formatOnWrite");
105
+ return enableLsp ? createLspWritethrough(session.cwd, { enableFormat, enableDiagnostics }) : writethroughNoop;
106
+ }
107
+
108
+ export class EditTool implements AgentTool<TInput> {
109
+ readonly name = "edit";
110
+ readonly label = "Edit";
111
+ readonly nonAbortable = true;
112
+ readonly concurrency = "exclusive";
113
+ readonly strict = true;
114
+
115
+ readonly #allowFuzzy: boolean;
116
+ readonly #fuzzyThreshold: number;
117
+ readonly #writethrough: WritethroughCallback;
118
+ readonly #editMode?: EditMode;
119
+ readonly #pendingDeferredFetches = new Map<string, AbortController>();
120
+
121
+ constructor(private readonly session: ToolSession) {
122
+ const {
123
+ PI_EDIT_FUZZY: editFuzzy = "auto",
124
+ PI_EDIT_FUZZY_THRESHOLD: editFuzzyThreshold = "auto",
125
+ PI_EDIT_VARIANT: envEditVariant = "auto",
126
+ } = Bun.env;
127
+
128
+ this.#editMode = resolveConfiguredEditMode(envEditVariant);
129
+ this.#allowFuzzy = resolveAllowFuzzy(session, editFuzzy);
130
+ this.#fuzzyThreshold = resolveFuzzyThreshold(session, editFuzzyThreshold);
131
+ this.#writethrough = createEditWritethrough(session);
132
+ }
133
+
134
+ get mode(): EditMode {
135
+ if (this.#editMode) return this.#editMode;
136
+ return resolveEditMode(this.session);
137
+ }
138
+
139
+ get description(): string {
140
+ return this.#getModeDefinition().description(this.session);
141
+ }
142
+
143
+ get parameters(): TInput {
144
+ return this.#getModeDefinition().parameters;
145
+ }
146
+
147
+ async execute(
148
+ _toolCallId: string,
149
+ params: ReplaceParams,
150
+ signal?: AbortSignal,
151
+ _onUpdate?: AgentToolUpdateCallback<EditToolDetails, TInput>,
152
+ context?: AgentToolContext,
153
+ ): Promise<AgentToolResult<EditToolDetails, TInput>>;
154
+ async execute(
155
+ _toolCallId: string,
156
+ params: PatchParams,
157
+ signal?: AbortSignal,
158
+ _onUpdate?: AgentToolUpdateCallback<EditToolDetails, TInput>,
159
+ context?: AgentToolContext,
160
+ ): Promise<AgentToolResult<EditToolDetails, TInput>>;
161
+ async execute(
162
+ _toolCallId: string,
163
+ params: HashlineParams,
164
+ signal?: AbortSignal,
165
+ _onUpdate?: AgentToolUpdateCallback<EditToolDetails, TInput>,
166
+ context?: AgentToolContext,
167
+ ): Promise<AgentToolResult<EditToolDetails, TInput>>;
168
+ async execute(
169
+ _toolCallId: string,
170
+ params: ChunkParams,
171
+ signal?: AbortSignal,
172
+ _onUpdate?: AgentToolUpdateCallback<EditToolDetails, TInput>,
173
+ context?: AgentToolContext,
174
+ ): Promise<AgentToolResult<EditToolDetails, TInput>>;
175
+ async execute(
176
+ _toolCallId: string,
177
+ params: EditParams,
178
+ signal?: AbortSignal,
179
+ _onUpdate?: AgentToolUpdateCallback<EditToolDetails, TInput>,
180
+ context?: AgentToolContext,
181
+ ): Promise<AgentToolResult<EditToolDetails, TInput>> {
182
+ const modeDefinition = this.#getModeDefinition();
183
+ if (!modeDefinition.validate(params)) {
184
+ throw new Error(modeDefinition.invalidParamsMessage);
185
+ }
186
+
187
+ return modeDefinition.execute(this, {
188
+ params,
189
+ signal,
190
+ batchRequest: getLspBatchRequest(context?.toolCall),
191
+ });
192
+ }
193
+
194
+ #getModeDefinition(): EditModeDefinition {
195
+ return {
196
+ chunk: {
197
+ description: (session: ToolSession) =>
198
+ prompt.render(chunkEditDescription, {
199
+ anchorStyle: resolveAnchorStyle(session.settings),
200
+ }),
201
+ parameters: chunkEditParamsSchema,
202
+ invalidParamsMessage: "Invalid edit parameters for chunk mode.",
203
+ validate: isChunkParams,
204
+ async execute(tool: EditTool, args: ModeExecutionArgs) {
205
+ return executeChunkMode({
206
+ session: tool.session,
207
+ params: args.params as ChunkParams,
208
+ signal: args.signal,
209
+ batchRequest: args.batchRequest,
210
+ writethrough: tool.#writethrough,
211
+ beginDeferredDiagnosticsForPath: path => tool.#beginDeferredDiagnosticsForPath(path),
212
+ });
213
+ },
214
+ },
215
+ patch: {
216
+ description: () => prompt.render(patchDescription),
217
+ parameters: patchEditSchema,
218
+ invalidParamsMessage: "Invalid edit parameters for patch mode.",
219
+ validate: isPatchParams,
220
+ async execute(tool: EditTool, args: ModeExecutionArgs) {
221
+ return executePatchMode({
222
+ session: tool.session,
223
+ params: args.params as PatchParams,
224
+ signal: args.signal,
225
+ batchRequest: args.batchRequest,
226
+ allowFuzzy: tool.#allowFuzzy,
227
+ fuzzyThreshold: tool.#fuzzyThreshold,
228
+ writethrough: tool.#writethrough,
229
+ beginDeferredDiagnosticsForPath: path => tool.#beginDeferredDiagnosticsForPath(path),
230
+ });
231
+ },
232
+ },
233
+ hashline: {
234
+ description: () => prompt.render(hashlineDescription),
235
+ parameters: hashlineEditParamsSchema,
236
+ invalidParamsMessage: "Invalid edit parameters for hashline mode.",
237
+ validate: isHashlineParams,
238
+ async execute(tool: EditTool, args: ModeExecutionArgs) {
239
+ return executeHashlineMode({
240
+ session: tool.session,
241
+ params: args.params as HashlineParams,
242
+ signal: args.signal,
243
+ batchRequest: args.batchRequest,
244
+ writethrough: tool.#writethrough,
245
+ beginDeferredDiagnosticsForPath: path => tool.#beginDeferredDiagnosticsForPath(path),
246
+ });
247
+ },
248
+ },
249
+ replace: {
250
+ description: () => prompt.render(replaceDescription),
251
+ parameters: replaceEditSchema,
252
+ invalidParamsMessage: "Invalid edit parameters for replace mode.",
253
+ validate: isReplaceParams,
254
+ async execute(tool: EditTool, args: ModeExecutionArgs) {
255
+ return executeReplaceMode({
256
+ session: tool.session,
257
+ params: args.params as ReplaceParams,
258
+ signal: args.signal,
259
+ batchRequest: args.batchRequest,
260
+ allowFuzzy: tool.#allowFuzzy,
261
+ fuzzyThreshold: tool.#fuzzyThreshold,
262
+ writethrough: tool.#writethrough,
263
+ beginDeferredDiagnosticsForPath: path => tool.#beginDeferredDiagnosticsForPath(path),
264
+ });
265
+ },
266
+ },
267
+ }[this.mode];
268
+ }
269
+
270
+ #beginDeferredDiagnosticsForPath(path: string): WritethroughDeferredHandle {
271
+ const existingDeferred = this.#pendingDeferredFetches.get(path);
272
+ if (existingDeferred) {
273
+ existingDeferred.abort();
274
+ this.#pendingDeferredFetches.delete(path);
275
+ }
276
+
277
+ const deferredController = new AbortController();
278
+ return {
279
+ onDeferredDiagnostics: (lateDiagnostics: FileDiagnosticsResult) => {
280
+ this.#pendingDeferredFetches.delete(path);
281
+ this.#injectLateDiagnostics(path, lateDiagnostics);
282
+ },
283
+ signal: deferredController.signal,
284
+ finalize: (diagnostics: FileDiagnosticsResult | undefined) => {
285
+ if (!diagnostics) {
286
+ this.#pendingDeferredFetches.set(path, deferredController);
287
+ } else {
288
+ deferredController.abort();
289
+ }
290
+ },
291
+ };
292
+ }
293
+
294
+ #injectLateDiagnostics(path: string, diagnostics: FileDiagnosticsResult): void {
295
+ const summary = diagnostics.summary ?? "";
296
+ const lines = diagnostics.messages ?? [];
297
+ const body = [`Late LSP diagnostics for ${path} (arrived after the edit tool returned):`, summary, ...lines]
298
+ .filter(Boolean)
299
+ .join("\n");
300
+
301
+ this.session.queueDeferredMessage?.({
302
+ role: "custom",
303
+ customType: "lsp-late-diagnostic",
304
+ content: body,
305
+ display: false,
306
+ timestamp: Date.now(),
307
+ });
308
+ }
309
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Lightweight line-hash utilities extracted from hashline.ts to avoid
3
+ * circular dependencies (prompt-templates → hashline → tools → edit).
4
+ */
5
+
6
+ /** 16-char nibble alphabet (no digits); shared with chunk checksum suffixes. */
7
+ export const HASHLINE_NIBBLE_ALPHABET = "ZPMQVRWSNKTXJBYH";
8
+
9
+ const NIBBLE_STR = HASHLINE_NIBBLE_ALPHABET;
10
+
11
+ const DICT = Array.from({ length: 256 }, (_, i) => {
12
+ const h = i >>> 4;
13
+ const l = i & 0x0f;
14
+ return `${NIBBLE_STR[h]}${NIBBLE_STR[l]}`;
15
+ });
16
+
17
+ const RE_SIGNIFICANT = /[\p{L}\p{N}]/u;
18
+
19
+ /**
20
+ * Compute a short hexadecimal hash of a single line.
21
+ *
22
+ * Uses xxHash32 on a trailing-whitespace-trimmed, CR-stripped line, truncated to 2 chars from
23
+ * {@link NIBBLE_STR}. For lines containing no alphanumeric characters (only
24
+ * punctuation/symbols/whitespace), the line number is mixed in to reduce hash collisions.
25
+ * The line input should not include a trailing newline.
26
+ */
27
+ export function computeLineHash(idx: number, line: string): string {
28
+ line = line.replace(/\r/g, "").trimEnd();
29
+
30
+ let seed = 0;
31
+ if (!RE_SIGNIFICANT.test(line)) {
32
+ seed = idx;
33
+ }
34
+ return DICT[Bun.hash.xxHash32(line, seed) & 0xff];
35
+ }
36
+
37
+ /**
38
+ * Formats a hash given the line number and text.
39
+ */
40
+ export function formatLineHash(line: number, lines: string): string {
41
+ return `${line}#${computeLineHash(line, lines)}`;
42
+ }
43
+
44
+ /**
45
+ * Format file text with hashline prefixes for display.
46
+ *
47
+ * Each line becomes `LINENUM#HASH:TEXT` where LINENUM is 1-indexed.
48
+ *
49
+ * @param text - Raw file text string
50
+ * @param startLine - First line number (1-indexed, defaults to 1)
51
+ * @returns Formatted string with one hashline-prefixed line per input line
52
+ *
53
+ * @example
54
+ * ```
55
+ * formatHashLines("function hi() {\n return;\n}")
56
+ * // "1#HH:function hi() {\n2#HH: return;\n3#HH:}"
57
+ * ```
58
+ */
59
+ export function formatHashLines(text: string, startLine = 1): string {
60
+ const lines = text.split("\n");
61
+ return lines
62
+ .map((line, i) => {
63
+ const num = startLine + i;
64
+ return `${formatLineHash(num, line)}:${line}`;
65
+ })
66
+ .join("\n");
67
+ }