@bastani/atomic 0.9.2-alpha.1 → 0.9.3-alpha.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.
Files changed (455) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +2 -2
  3. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  4. package/dist/builtin/cursor/package.json +2 -2
  5. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  6. package/dist/builtin/intercom/package.json +1 -1
  7. package/dist/builtin/mcp/CHANGELOG.md +12 -0
  8. package/dist/builtin/mcp/direct-tools.ts +4 -2
  9. package/dist/builtin/mcp/package.json +1 -1
  10. package/dist/builtin/mcp/proxy-call.ts +3 -1
  11. package/dist/builtin/mcp/utils.ts +18 -7
  12. package/dist/builtin/subagents/CHANGELOG.md +17 -0
  13. package/dist/builtin/subagents/README.md +6 -6
  14. package/dist/builtin/subagents/agents/code-simplifier.md +7 -6
  15. package/dist/builtin/subagents/agents/codebase-analyzer.md +5 -4
  16. package/dist/builtin/subagents/agents/codebase-locator.md +3 -3
  17. package/dist/builtin/subagents/agents/codebase-online-researcher.md +10 -10
  18. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +4 -4
  19. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +3 -3
  20. package/dist/builtin/subagents/agents/codebase-research-locator.md +4 -4
  21. package/dist/builtin/subagents/agents/debugger.md +5 -5
  22. package/dist/builtin/subagents/agents/worker.md +56 -0
  23. package/dist/builtin/subagents/package.json +1 -1
  24. package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
  25. package/dist/builtin/subagents/src/agents/agent-loaders.ts +3 -5
  26. package/dist/builtin/subagents/src/agents/agent-management-helpers.ts +3 -3
  27. package/dist/builtin/subagents/src/extension/schemas.ts +2 -2
  28. package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -3
  29. package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +1 -1
  30. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +2 -2
  31. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -1
  32. package/dist/builtin/subagents/src/shared/types-depth.ts +5 -5
  33. package/dist/builtin/subagents/src/shared/types-runtime.ts +2 -1
  34. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -2
  35. package/dist/builtin/web-access/CHANGELOG.md +6 -0
  36. package/dist/builtin/web-access/package.json +1 -1
  37. package/dist/builtin/workflows/CHANGELOG.md +21 -0
  38. package/dist/builtin/workflows/README.md +2 -2
  39. package/dist/builtin/workflows/builtin/goal-artifacts.ts +11 -6
  40. package/dist/builtin/workflows/builtin/goal-ledger.ts +33 -1
  41. package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -28
  42. package/dist/builtin/workflows/builtin/goal-reducer.ts +2 -2
  43. package/dist/builtin/workflows/builtin/goal-reports.ts +2 -5
  44. package/dist/builtin/workflows/builtin/goal-review.ts +1 -1
  45. package/dist/builtin/workflows/builtin/goal-runner.ts +10 -17
  46. package/dist/builtin/workflows/builtin/open-claude-design-feedback.ts +3 -3
  47. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +1 -3
  48. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +1 -1
  49. package/dist/builtin/workflows/builtin/ralph-core.ts +7 -17
  50. package/dist/builtin/workflows/builtin/ralph-runner.ts +11 -18
  51. package/dist/builtin/workflows/builtin/shared-prompts.ts +1 -1
  52. package/dist/builtin/workflows/package.json +1 -1
  53. package/dist/builtin/workflows/src/extension/config-loader.ts +35 -15
  54. package/dist/builtin/workflows/src/extension/discovery.ts +20 -8
  55. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +1 -2
  56. package/dist/builtin/workflows/src/extension/wiring.ts +1 -1
  57. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +11 -10
  58. package/dist/cli/args.d.ts.map +1 -1
  59. package/dist/cli/args.js +9 -9
  60. package/dist/cli/args.js.map +1 -1
  61. package/dist/config-self-update.d.ts.map +1 -1
  62. package/dist/config-self-update.js +3 -4
  63. package/dist/config-self-update.js.map +1 -1
  64. package/dist/config.d.ts.map +1 -1
  65. package/dist/config.js +4 -5
  66. package/dist/config.js.map +1 -1
  67. package/dist/core/agent-session-bash.d.ts +1 -0
  68. package/dist/core/agent-session-bash.d.ts.map +1 -1
  69. package/dist/core/agent-session-bash.js +1 -0
  70. package/dist/core/agent-session-bash.js.map +1 -1
  71. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  72. package/dist/core/agent-session-tool-registry.js +23 -0
  73. package/dist/core/agent-session-tool-registry.js.map +1 -1
  74. package/dist/core/bash-executor.d.ts +2 -0
  75. package/dist/core/bash-executor.d.ts.map +1 -1
  76. package/dist/core/bash-executor.js +1 -0
  77. package/dist/core/bash-executor.js.map +1 -1
  78. package/dist/core/compaction/compaction.d.ts +29 -0
  79. package/dist/core/compaction/compaction.d.ts.map +1 -1
  80. package/dist/core/compaction/compaction.js +36 -1
  81. package/dist/core/compaction/compaction.js.map +1 -1
  82. package/dist/core/compaction/context-compaction-metrics.d.ts +14 -2
  83. package/dist/core/compaction/context-compaction-metrics.d.ts.map +1 -1
  84. package/dist/core/compaction/context-compaction-metrics.js +50 -1
  85. package/dist/core/compaction/context-compaction-metrics.js.map +1 -1
  86. package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
  87. package/dist/core/compaction/context-compaction-prompt.js +2 -0
  88. package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
  89. package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
  90. package/dist/core/compaction/context-compaction-runner.js +1 -1
  91. package/dist/core/compaction/context-compaction-runner.js.map +1 -1
  92. package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
  93. package/dist/core/compaction/context-deletion-application.js +5 -5
  94. package/dist/core/compaction/context-deletion-application.js.map +1 -1
  95. package/dist/core/compaction/context-deletion-targets.d.ts +2 -0
  96. package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
  97. package/dist/core/compaction/context-deletion-targets.js +23 -3
  98. package/dist/core/compaction/context-deletion-targets.js.map +1 -1
  99. package/dist/core/compaction/context-deletion-tool-definitions.d.ts +6 -0
  100. package/dist/core/compaction/context-deletion-tool-definitions.d.ts.map +1 -1
  101. package/dist/core/compaction/context-deletion-tool-definitions.js.map +1 -1
  102. package/dist/core/compaction/context-deletion-tools.d.ts.map +1 -1
  103. package/dist/core/compaction/context-deletion-tools.js +18 -10
  104. package/dist/core/compaction/context-deletion-tools.js.map +1 -1
  105. package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
  106. package/dist/core/compaction/context-transcript-analysis.js +2 -4
  107. package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
  108. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
  109. package/dist/core/copilot-gemini-tool-arguments.js +2 -60
  110. package/dist/core/copilot-gemini-tool-arguments.js.map +1 -1
  111. package/dist/core/extensions/context-types.d.ts +2 -0
  112. package/dist/core/extensions/context-types.d.ts.map +1 -1
  113. package/dist/core/extensions/context-types.js.map +1 -1
  114. package/dist/core/extensions/index.d.ts +2 -2
  115. package/dist/core/extensions/index.d.ts.map +1 -1
  116. package/dist/core/extensions/index.js +1 -1
  117. package/dist/core/extensions/index.js.map +1 -1
  118. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  119. package/dist/core/extensions/loader-virtual-modules.js +11 -3
  120. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  121. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  122. package/dist/core/extensions/runner-context.js +11 -0
  123. package/dist/core/extensions/runner-context.js.map +1 -1
  124. package/dist/core/extensions/tool-events.d.ts +13 -13
  125. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  126. package/dist/core/extensions/tool-events.js +3 -3
  127. package/dist/core/extensions/tool-events.js.map +1 -1
  128. package/dist/core/extensions/types.d.ts +1 -1
  129. package/dist/core/extensions/types.d.ts.map +1 -1
  130. package/dist/core/extensions/types.js +1 -1
  131. package/dist/core/extensions/types.js.map +1 -1
  132. package/dist/core/flattened-tool-arguments.d.ts +18 -0
  133. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  134. package/dist/core/flattened-tool-arguments.js +104 -0
  135. package/dist/core/flattened-tool-arguments.js.map +1 -1
  136. package/dist/core/sdk-exports.d.ts +1 -1
  137. package/dist/core/sdk-exports.d.ts.map +1 -1
  138. package/dist/core/sdk-exports.js +1 -1
  139. package/dist/core/sdk-exports.js.map +1 -1
  140. package/dist/core/sdk-types.d.ts +2 -2
  141. package/dist/core/sdk-types.d.ts.map +1 -1
  142. package/dist/core/sdk-types.js.map +1 -1
  143. package/dist/core/settings-manager-basic-accessors.d.ts +4 -0
  144. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  145. package/dist/core/settings-manager-basic-accessors.js +18 -0
  146. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  147. package/dist/core/settings-manager-resource-accessors.d.ts +4 -0
  148. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  149. package/dist/core/settings-manager-resource-accessors.js +15 -0
  150. package/dist/core/settings-manager-resource-accessors.js.map +1 -1
  151. package/dist/core/settings-types.d.ts +11 -0
  152. package/dist/core/settings-types.d.ts.map +1 -1
  153. package/dist/core/settings-types.js.map +1 -1
  154. package/dist/core/system-prompt.d.ts +1 -1
  155. package/dist/core/system-prompt.d.ts.map +1 -1
  156. package/dist/core/system-prompt.js +3 -2
  157. package/dist/core/system-prompt.js.map +1 -1
  158. package/dist/core/tools/artifact-protocol.d.ts +11 -0
  159. package/dist/core/tools/artifact-protocol.d.ts.map +1 -0
  160. package/dist/core/tools/artifact-protocol.js +76 -0
  161. package/dist/core/tools/artifact-protocol.js.map +1 -0
  162. package/dist/core/tools/artifacts.d.ts +18 -0
  163. package/dist/core/tools/artifacts.d.ts.map +1 -0
  164. package/dist/core/tools/artifacts.js +90 -0
  165. package/dist/core/tools/artifacts.js.map +1 -0
  166. package/dist/core/tools/bash-async-jobs.d.ts +20 -0
  167. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -0
  168. package/dist/core/tools/bash-async-jobs.js +59 -0
  169. package/dist/core/tools/bash-async-jobs.js.map +1 -0
  170. package/dist/core/tools/bash-async-output.d.ts +10 -0
  171. package/dist/core/tools/bash-async-output.d.ts.map +1 -0
  172. package/dist/core/tools/bash-async-output.js +80 -0
  173. package/dist/core/tools/bash-async-output.js.map +1 -0
  174. package/dist/core/tools/bash-interceptor.d.ts +10 -0
  175. package/dist/core/tools/bash-interceptor.d.ts.map +1 -0
  176. package/dist/core/tools/bash-interceptor.js +39 -0
  177. package/dist/core/tools/bash-interceptor.js.map +1 -0
  178. package/dist/core/tools/bash-leading-cd.d.ts +7 -0
  179. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -0
  180. package/dist/core/tools/bash-leading-cd.js +59 -0
  181. package/dist/core/tools/bash-leading-cd.js.map +1 -0
  182. package/dist/core/tools/bash-pty-native.d.ts +14 -0
  183. package/dist/core/tools/bash-pty-native.d.ts.map +1 -0
  184. package/dist/core/tools/bash-pty-native.js +71 -0
  185. package/dist/core/tools/bash-pty-native.js.map +1 -0
  186. package/dist/core/tools/bash.d.ts +28 -17
  187. package/dist/core/tools/bash.d.ts.map +1 -1
  188. package/dist/core/tools/bash.js +152 -35
  189. package/dist/core/tools/bash.js.map +1 -1
  190. package/dist/core/tools/block-resolver.d.ts +16 -0
  191. package/dist/core/tools/block-resolver.d.ts.map +1 -0
  192. package/dist/core/tools/block-resolver.js +74 -0
  193. package/dist/core/tools/block-resolver.js.map +1 -0
  194. package/dist/core/tools/conflict-registry.d.ts +16 -0
  195. package/dist/core/tools/conflict-registry.d.ts.map +1 -0
  196. package/dist/core/tools/conflict-registry.js +44 -0
  197. package/dist/core/tools/conflict-registry.js.map +1 -0
  198. package/dist/core/tools/directory-tree.d.ts +13 -0
  199. package/dist/core/tools/directory-tree.d.ts.map +1 -0
  200. package/dist/core/tools/directory-tree.js +81 -0
  201. package/dist/core/tools/directory-tree.js.map +1 -0
  202. package/dist/core/tools/edit.d.ts +4 -29
  203. package/dist/core/tools/edit.d.ts.map +1 -1
  204. package/dist/core/tools/edit.js +136 -228
  205. package/dist/core/tools/edit.js.map +1 -1
  206. package/dist/core/tools/fetch-url.d.ts +74 -0
  207. package/dist/core/tools/fetch-url.d.ts.map +1 -0
  208. package/dist/core/tools/fetch-url.js +518 -0
  209. package/dist/core/tools/fetch-url.js.map +1 -0
  210. package/dist/core/tools/find.d.ts +27 -9
  211. package/dist/core/tools/find.d.ts.map +1 -1
  212. package/dist/core/tools/find.js +400 -176
  213. package/dist/core/tools/find.js.map +1 -1
  214. package/dist/core/tools/glob-path-utils.d.ts +8 -0
  215. package/dist/core/tools/glob-path-utils.d.ts.map +1 -0
  216. package/dist/core/tools/glob-path-utils.js +26 -0
  217. package/dist/core/tools/glob-path-utils.js.map +1 -0
  218. package/dist/core/tools/grep.d.ts +12 -0
  219. package/dist/core/tools/grep.d.ts.map +1 -1
  220. package/dist/core/tools/grep.js +141 -17
  221. package/dist/core/tools/grep.js.map +1 -1
  222. package/dist/core/tools/hashline-engine/apply.d.ts +11 -0
  223. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -0
  224. package/dist/core/tools/hashline-engine/apply.js +752 -0
  225. package/dist/core/tools/hashline-engine/apply.js.map +1 -0
  226. package/dist/core/tools/hashline-engine/block.d.ts +40 -0
  227. package/dist/core/tools/hashline-engine/block.d.ts.map +1 -0
  228. package/dist/core/tools/hashline-engine/block.js +117 -0
  229. package/dist/core/tools/hashline-engine/block.js.map +1 -0
  230. package/dist/core/tools/hashline-engine/diff-preview.d.ts +15 -0
  231. package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -0
  232. package/dist/core/tools/hashline-engine/diff-preview.js +98 -0
  233. package/dist/core/tools/hashline-engine/diff-preview.js.map +1 -0
  234. package/dist/core/tools/hashline-engine/format.d.ts +71 -0
  235. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -0
  236. package/dist/core/tools/hashline-engine/format.js +178 -0
  237. package/dist/core/tools/hashline-engine/format.js.map +1 -0
  238. package/dist/core/tools/hashline-engine/fs.d.ts +81 -0
  239. package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -0
  240. package/dist/core/tools/hashline-engine/fs.js +143 -0
  241. package/dist/core/tools/hashline-engine/fs.js.map +1 -0
  242. package/dist/core/tools/hashline-engine/index.d.ts +18 -0
  243. package/dist/core/tools/hashline-engine/index.d.ts.map +1 -0
  244. package/dist/core/tools/hashline-engine/index.js +20 -0
  245. package/dist/core/tools/hashline-engine/index.js.map +1 -0
  246. package/dist/core/tools/hashline-engine/input.d.ts +101 -0
  247. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -0
  248. package/dist/core/tools/hashline-engine/input.js +398 -0
  249. package/dist/core/tools/hashline-engine/input.js.map +1 -0
  250. package/dist/core/tools/hashline-engine/messages.d.ts +99 -0
  251. package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -0
  252. package/dist/core/tools/hashline-engine/messages.js +144 -0
  253. package/dist/core/tools/hashline-engine/messages.js.map +1 -0
  254. package/dist/core/tools/hashline-engine/mismatch.d.ts +45 -0
  255. package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -0
  256. package/dist/core/tools/hashline-engine/mismatch.js +90 -0
  257. package/dist/core/tools/hashline-engine/mismatch.js.map +1 -0
  258. package/dist/core/tools/hashline-engine/normalize.d.ts +21 -0
  259. package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -0
  260. package/dist/core/tools/hashline-engine/normalize.js +33 -0
  261. package/dist/core/tools/hashline-engine/normalize.js.map +1 -0
  262. package/dist/core/tools/hashline-engine/parser.d.ts +24 -0
  263. package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -0
  264. package/dist/core/tools/hashline-engine/parser.js +381 -0
  265. package/dist/core/tools/hashline-engine/parser.js.map +1 -0
  266. package/dist/core/tools/hashline-engine/patcher.d.ts +118 -0
  267. package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -0
  268. package/dist/core/tools/hashline-engine/patcher.js +341 -0
  269. package/dist/core/tools/hashline-engine/patcher.js.map +1 -0
  270. package/dist/core/tools/hashline-engine/prefixes.d.ts +43 -0
  271. package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -0
  272. package/dist/core/tools/hashline-engine/prefixes.js +135 -0
  273. package/dist/core/tools/hashline-engine/prefixes.js.map +1 -0
  274. package/dist/core/tools/hashline-engine/recovery.d.ts +41 -0
  275. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -0
  276. package/dist/core/tools/hashline-engine/recovery.js +168 -0
  277. package/dist/core/tools/hashline-engine/recovery.js.map +1 -0
  278. package/dist/core/tools/hashline-engine/snapshots.d.ts +65 -0
  279. package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -0
  280. package/dist/core/tools/hashline-engine/snapshots.js +108 -0
  281. package/dist/core/tools/hashline-engine/snapshots.js.map +1 -0
  282. package/dist/core/tools/hashline-engine/stream.d.ts +3 -0
  283. package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -0
  284. package/dist/core/tools/hashline-engine/stream.js +111 -0
  285. package/dist/core/tools/hashline-engine/stream.js.map +1 -0
  286. package/dist/core/tools/hashline-engine/tokenizer.d.ts +69 -0
  287. package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -0
  288. package/dist/core/tools/hashline-engine/tokenizer.js +430 -0
  289. package/dist/core/tools/hashline-engine/tokenizer.js.map +1 -0
  290. package/dist/core/tools/hashline-engine/types.d.ts +166 -0
  291. package/dist/core/tools/hashline-engine/types.d.ts.map +1 -0
  292. package/dist/core/tools/hashline-engine/types.js +9 -0
  293. package/dist/core/tools/hashline-engine/types.js.map +1 -0
  294. package/dist/core/tools/hashline.d.ts +29 -0
  295. package/dist/core/tools/hashline.d.ts.map +1 -0
  296. package/dist/core/tools/hashline.js +110 -0
  297. package/dist/core/tools/hashline.js.map +1 -0
  298. package/dist/core/tools/index.d.ts +6 -4
  299. package/dist/core/tools/index.d.ts.map +1 -1
  300. package/dist/core/tools/index.js +52 -35
  301. package/dist/core/tools/index.js.map +1 -1
  302. package/dist/core/tools/notebook.d.ts +38 -0
  303. package/dist/core/tools/notebook.d.ts.map +1 -0
  304. package/dist/core/tools/notebook.js +125 -0
  305. package/dist/core/tools/notebook.js.map +1 -0
  306. package/dist/core/tools/read-document-extract.d.ts +9 -0
  307. package/dist/core/tools/read-document-extract.d.ts.map +1 -0
  308. package/dist/core/tools/read-document-extract.js +212 -0
  309. package/dist/core/tools/read-document-extract.js.map +1 -0
  310. package/dist/core/tools/read-selectors.d.ts +24 -0
  311. package/dist/core/tools/read-selectors.d.ts.map +1 -0
  312. package/dist/core/tools/read-selectors.js +277 -0
  313. package/dist/core/tools/read-selectors.js.map +1 -0
  314. package/dist/core/tools/read-url.d.ts +37 -0
  315. package/dist/core/tools/read-url.d.ts.map +1 -0
  316. package/dist/core/tools/read-url.js +39 -0
  317. package/dist/core/tools/read-url.js.map +1 -0
  318. package/dist/core/tools/read.d.ts +11 -11
  319. package/dist/core/tools/read.d.ts.map +1 -1
  320. package/dist/core/tools/read.js +224 -94
  321. package/dist/core/tools/read.js.map +1 -1
  322. package/dist/core/tools/resource-selectors.d.ts +44 -0
  323. package/dist/core/tools/resource-selectors.d.ts.map +1 -0
  324. package/dist/core/tools/resource-selectors.js +808 -0
  325. package/dist/core/tools/resource-selectors.js.map +1 -0
  326. package/dist/core/tools/search-details.d.ts +26 -0
  327. package/dist/core/tools/search-details.d.ts.map +1 -0
  328. package/dist/core/tools/search-details.js +24 -0
  329. package/dist/core/tools/search-details.js.map +1 -0
  330. package/dist/core/tools/search-line-ranges.d.ts +11 -0
  331. package/dist/core/tools/search-line-ranges.d.ts.map +1 -0
  332. package/dist/core/tools/search-line-ranges.js +65 -0
  333. package/dist/core/tools/search-line-ranges.js.map +1 -0
  334. package/dist/core/tools/search-native.d.ts +97 -0
  335. package/dist/core/tools/search-native.d.ts.map +1 -0
  336. package/dist/core/tools/search-native.js +27 -0
  337. package/dist/core/tools/search-native.js.map +1 -0
  338. package/dist/core/tools/search.d.ts +24 -0
  339. package/dist/core/tools/search.d.ts.map +1 -0
  340. package/dist/core/tools/search.js +573 -0
  341. package/dist/core/tools/search.js.map +1 -0
  342. package/dist/core/tools/truncate.d.ts +4 -4
  343. package/dist/core/tools/truncate.d.ts.map +1 -1
  344. package/dist/core/tools/truncate.js +3 -3
  345. package/dist/core/tools/truncate.js.map +1 -1
  346. package/dist/core/tools/url-ip-guards.d.ts +4 -0
  347. package/dist/core/tools/url-ip-guards.d.ts.map +1 -0
  348. package/dist/core/tools/url-ip-guards.js +126 -0
  349. package/dist/core/tools/url-ip-guards.js.map +1 -0
  350. package/dist/core/tools/write.d.ts +12 -2
  351. package/dist/core/tools/write.d.ts.map +1 -1
  352. package/dist/core/tools/write.js +166 -14
  353. package/dist/core/tools/write.js.map +1 -1
  354. package/dist/core/trust-manager.d.ts.map +1 -1
  355. package/dist/core/trust-manager.js +2 -3
  356. package/dist/core/trust-manager.js.map +1 -1
  357. package/dist/index-extensions.d.ts +2 -2
  358. package/dist/index-extensions.d.ts.map +1 -1
  359. package/dist/index-extensions.js +1 -1
  360. package/dist/index-extensions.js.map +1 -1
  361. package/dist/index.d.ts +3 -3
  362. package/dist/index.d.ts.map +1 -1
  363. package/dist/index.js +3 -3
  364. package/dist/index.js.map +1 -1
  365. package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
  366. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  367. package/dist/modes/interactive/components/custom-editor.js +9 -2
  368. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  369. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  370. package/dist/modes/interactive/components/settings-selector-handlers.js +3 -0
  371. package/dist/modes/interactive/components/settings-selector-handlers.js.map +1 -1
  372. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  373. package/dist/modes/interactive/components/settings-selector-items.js +7 -0
  374. package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
  375. package/dist/modes/interactive/components/settings-selector-types.d.ts +2 -0
  376. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  377. package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
  378. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  379. package/dist/modes/interactive/components/tree-selector-content.js +0 -5
  380. package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
  381. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  382. package/dist/modes/interactive/interactive-auth-login.js +1 -0
  383. package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
  384. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  385. package/dist/modes/interactive/interactive-autocomplete.js +80 -2
  386. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  387. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  388. package/dist/modes/interactive/interactive-hotkeys-debug.js +3 -0
  389. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  390. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  391. package/dist/modes/interactive/interactive-input-handling.js +51 -0
  392. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  393. package/dist/modes/interactive/interactive-mode-base.d.ts +5 -0
  394. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  395. package/dist/modes/interactive/interactive-mode-base.js +5 -0
  396. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  397. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  398. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  399. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  400. package/dist/modes/interactive/interactive-mode-surface.d.ts +12 -0
  401. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  402. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  403. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  404. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  405. package/dist/modes/interactive/interactive-mode.js +1 -0
  406. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  407. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  408. package/dist/modes/interactive/interactive-model-routing.js +4 -1
  409. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  410. package/dist/modes/interactive/interactive-onboarding.d.ts +11 -0
  411. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -0
  412. package/dist/modes/interactive/interactive-onboarding.js +220 -0
  413. package/dist/modes/interactive/interactive-onboarding.js.map +1 -0
  414. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  415. package/dist/modes/interactive/interactive-selectors.js +4 -0
  416. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  417. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  418. package/dist/modes/interactive/interactive-session-routing.js +6 -0
  419. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  420. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  421. package/dist/modes/interactive/interactive-slash-commands.js +9 -4
  422. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  423. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  424. package/dist/modes/interactive/interactive-startup.js +28 -0
  425. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  426. package/dist/utils/child-process.d.ts.map +1 -1
  427. package/dist/utils/child-process.js +21 -1
  428. package/dist/utils/child-process.js.map +1 -1
  429. package/dist/utils/markit.d.ts +8 -0
  430. package/dist/utils/markit.d.ts.map +1 -0
  431. package/dist/utils/markit.js +53 -0
  432. package/dist/utils/markit.js.map +1 -0
  433. package/dist/utils/paths.d.ts +2 -1
  434. package/dist/utils/paths.d.ts.map +1 -1
  435. package/dist/utils/paths.js +14 -1
  436. package/dist/utils/paths.js.map +1 -1
  437. package/docs/compaction.md +16 -1
  438. package/docs/containerization.md +1 -1
  439. package/docs/docs.json +1 -0
  440. package/docs/extensions.md +25 -36
  441. package/docs/quickstart.md +11 -6
  442. package/docs/sdk.md +5 -5
  443. package/docs/settings.md +7 -0
  444. package/docs/subagents.md +3 -2
  445. package/docs/tools.md +49 -0
  446. package/docs/usage.md +3 -3
  447. package/docs/workflows.md +7 -5
  448. package/examples/extensions/subagent/README.md +5 -5
  449. package/examples/extensions/subagent/agents/planner.md +1 -1
  450. package/examples/extensions/subagent/agents/reviewer.md +1 -1
  451. package/examples/extensions/subagent/agents/scout.md +2 -2
  452. package/examples/extensions/subagent/display.ts +3 -3
  453. package/examples/sdk/05-tools.ts +3 -3
  454. package/examples/sdk/README.md +1 -1
  455. package/package.json +3 -2
@@ -0,0 +1,144 @@
1
+ // @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.
2
+ // Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).
3
+ /** Centralized error/warning text for the hashline parser, applier, and patcher. */
4
+ import { formatNumberedLine, HL_FILE_HASH_SEP, HL_FILE_PREFIX, HL_FILE_SUFFIX } from "./format.js";
5
+ /** Lines of context shown either side of a hash mismatch. */
6
+ export const MISMATCH_CONTEXT = 2;
7
+ /**
8
+ * Numbered `LINE:TEXT` rows around `anchorLines` (±{@link MISMATCH_CONTEXT}),
9
+ * `*`-marking anchors, `...` between non-adjacent runs. Out-of-range anchors
10
+ * contribute no rows.
11
+ */
12
+ export function formatAnchoredContext(anchorLines, fileLines) {
13
+ const displayLines = new Set();
14
+ for (const line of anchorLines) {
15
+ if (line < 1 || line > fileLines.length)
16
+ continue;
17
+ const lo = Math.max(1, line - MISMATCH_CONTEXT);
18
+ const hi = Math.min(fileLines.length, line + MISMATCH_CONTEXT);
19
+ for (let lineNum = lo; lineNum <= hi; lineNum++)
20
+ displayLines.add(lineNum);
21
+ }
22
+ const anchorSet = new Set(anchorLines);
23
+ const rows = [];
24
+ let previous = -1;
25
+ for (const lineNum of [...displayLines].sort((a, b) => a - b)) {
26
+ if (previous !== -1 && lineNum > previous + 1)
27
+ rows.push("...");
28
+ previous = lineNum;
29
+ const marker = anchorSet.has(lineNum) ? "*" : " ";
30
+ rows.push(`${marker}${formatNumberedLine(lineNum, fileLines[lineNum - 1] ?? "")}`);
31
+ }
32
+ return rows;
33
+ }
34
+ /** Optional patch envelope start marker; silently consumed. */
35
+ export const BEGIN_PATCH_MARKER = "*** Begin Patch";
36
+ /** Optional patch envelope end marker; terminates parsing. */
37
+ export const END_PATCH_MARKER = "*** End Patch";
38
+ /**
39
+ * Truncation sentinel emitted by an agent loop mid-call. Ends parsing like
40
+ * {@link END_PATCH_MARKER}, without a warning.
41
+ */
42
+ export const ABORT_MARKER = "*** Abort";
43
+ /** Two consecutive hunks targeted the exact same concrete range. */
44
+ export const REPLACE_PAIR_COALESCED_WARNING = "Two hunks targeted the same range; kept only the second. One `replace N..M:` hunk per range — the body is the final content, never old+new.";
45
+ /** Bare bodyless hunk followed by an overlapping concrete hunk. */
46
+ export const REPLACE_PAIR_COALESCED_OVERLAP_WARNING = "Dropped a bare hunk overlapped by the concrete hunk after it. One `replace N..M:` hunk per range — the body is the final content, never old+new.";
47
+ /** Bare body rows auto-converted to literal `+` rows. */
48
+ export const BARE_BODY_AUTO_PIPED_WARNING = "Auto-prefixed bare body row(s) with `+`. Body rows must be `+TEXT` literal lines.";
49
+ /** Unified-diff-style `-` row in a hunk body. */
50
+ export const MINUS_ROW_REJECTED = "`-` rows are not valid; the range already names the lines being changed. For a literal `-` line, write `+-…`.";
51
+ /** Replace hunk with no body. */
52
+ export const EMPTY_REPLACE = "`replace N..M:` needs at least one `+TEXT` body row. To delete lines, use `delete N..M`.";
53
+ /** `replace block N:` hunk with no body. */
54
+ export const EMPTY_BLOCK = "`replace block N:` needs at least one `+TEXT` body row. To delete a block, use `delete block N`.";
55
+ /**
56
+ * Block-anchored replace/delete could not resolve to a syntactic block
57
+ * (unsupported language, blank/out-of-range line, no node beginning on N, or
58
+ * parse error). Appends a {@link formatAnchoredContext} preview when
59
+ * `fileLines` is given. `insert after block N:` never reaches this — it is
60
+ * lowered to plain `insert after N:` instead (see
61
+ * {@link insertAfterBlockUnresolvedLoweredWarning}).
62
+ */
63
+ export function blockUnresolvedMessage(line, op = "replace", fileLines) {
64
+ const phrase = op === "delete" ? `delete block ${line}` : `replace block ${line}:`;
65
+ const fallback = op === "delete" ? `delete ${line}..M` : `replace ${line}..M:`;
66
+ let message = `\`${phrase}\` could not resolve a syntactic block beginning on line ${line} ` +
67
+ `(unsupported language, blank/closer line, or parse error). Use \`${fallback}\` with explicit lines.`;
68
+ if (fileLines) {
69
+ const context = formatAnchoredContext([line], fileLines);
70
+ if (context.length > 0)
71
+ message += `\n\n${context.join("\n")}`;
72
+ }
73
+ return message;
74
+ }
75
+ /** Block-anchored edit reached a path with no {@link BlockResolver} wired in — a host-configuration bug. */
76
+ export const BLOCK_RESOLVER_UNAVAILABLE = "`replace block`/`delete block`/`insert after block` are not available here (no block resolver configured). Use a concrete line range.";
77
+ /**
78
+ * `insert after block N:` anchored on a closing-delimiter line, lowered to
79
+ * plain `insert after N:` — the closer ends a block, and inserting after it
80
+ * is exactly what the plain form does.
81
+ */
82
+ export function insertAfterBlockCloserLoweredWarning(line) {
83
+ return `\`insert after block ${line}:\` anchors on a closing delimiter, so it was applied as plain \`insert after ${line}:\`. Anchor on the line that OPENS the construct.`;
84
+ }
85
+ /**
86
+ * `insert after block N:` anchor unresolvable (unsupported language, blank
87
+ * line, parse error, or no resolver), lowered to plain `insert after N:` —
88
+ * applying with a warning beats failing the patch.
89
+ */
90
+ export function insertAfterBlockUnresolvedLoweredWarning(line) {
91
+ return `\`insert after block ${line}:\` could not resolve a syntactic block on line ${line}, so it was applied as plain \`insert after ${line}:\`. Verify the landing line; anchor on a line that OPENS a construct.`;
92
+ }
93
+ /**
94
+ * Internal invariant: `applyEdits` received an unresolved `replace block N:`
95
+ * edit; `resolveBlockEdits` must run first. Wiring bug, not authored input.
96
+ */
97
+ export const UNRESOLVED_BLOCK_INTERNAL = "internal error: unresolved `replace block` edit reached the applier (resolveBlockEdits was not run).";
98
+ /** Delete hunk received a body row. */
99
+ export const DELETE_TAKES_NO_BODY = "`delete N..M` does not take body rows. Remove the body, or use `replace N..M:`.";
100
+ /** `delete block N` hunk received a body row. */
101
+ export const DELETE_BLOCK_TAKES_NO_BODY = "`delete block N` does not take body rows. Remove the body, or use `replace block N:`.";
102
+ /** Insert hunk with no body. */
103
+ export const EMPTY_INSERT = "`insert` needs at least one `+TEXT` body row.";
104
+ /**
105
+ * `insert after` body indented shallower than the anchor: the landing slid
106
+ * forward past trailing closer lines — the common "anchored on the last line
107
+ * I read instead of after the block" mistake.
108
+ */
109
+ export function afterInsertLandingShiftWarning(anchorLine, landingLine, crossed) {
110
+ return `insert after ${anchorLine}: body indented shallower than the anchor, so the landing moved past ${crossed} closing line${crossed === 1 ? "" : "s"} to after line ${landingLine}. For the deeper position inside the block, re-issue with the body indented to match.`;
111
+ }
112
+ /**
113
+ * `insert after block N:` body indented deeper than the block's closer: the
114
+ * landing was pulled inside the block — a deeper body almost always means
115
+ * "append inside the block's body".
116
+ */
117
+ export function blockInsertLandingShiftWarning(blockStart, closerLine, landingLine) {
118
+ return `insert after block ${blockStart}: body indented deeper than closing line ${closerLine}, so it was placed inside the block, after line ${landingLine}. \`insert after block\` lands AFTER the block at sibling depth — if inside was intended, use plain \`insert after ${closerLine}:\`.`;
119
+ }
120
+ /** `Recovery`: an external write matched a cached snapshot. */
121
+ export const RECOVERY_EXTERNAL_WARNING = "Recovered from a stale file hash using a previous read snapshot (file changed externally between read and edit).";
122
+ /** `Recovery`: a prior in-session edit advanced the hash. */
123
+ export const RECOVERY_SESSION_CHAIN_WARNING = "Recovered from a stale file hash using an earlier in-session snapshot (a prior edit in this session advanced the hash).";
124
+ /**
125
+ * `Recovery`: session-chain replay fast-path. Less certain than
126
+ * {@link RECOVERY_SESSION_CHAIN_WARNING} — the 3-way merge refused, the
127
+ * anchor-content gate passed, but a coincidental insert+delete earlier in
128
+ * the chain could still misplace an anchor — hence the verify hedge.
129
+ */
130
+ export const RECOVERY_SESSION_REPLAY_WARNING = "Recovered by replaying your edits onto the current file content (a prior in-session edit changed the lines you re-targeted with a stale hash). Verify the diff matches your intent.";
131
+ /**
132
+ * `insert head:`/`insert tail:` applied despite a stale snapshot tag.
133
+ * Head/tail position is content-independent, so drift is non-fatal: apply
134
+ * onto live content and warn instead of hard-failing.
135
+ */
136
+ export const HEADTAIL_DRIFT_WARNING = "Applied the `insert head:`/`insert tail:` edit despite a stale snapshot tag (file changed since your read) — head/tail position is content-independent. Re-read if the drift was unexpected.";
137
+ /**
138
+ * Section omitted the mandatory snapshot tag. Shared by the apply
139
+ * ({@link Patcher.prepare}) and preview/diff paths so both stay in lockstep.
140
+ */
141
+ export function missingSnapshotTagMessage(sectionPath) {
142
+ return `Missing hashline snapshot tag for ${sectionPath}; use \`${HL_FILE_PREFIX}${sectionPath}${HL_FILE_HASH_SEP}tag${HL_FILE_SUFFIX}\` from your latest read/search output. To create a new file, use the write tool.`;
143
+ }
144
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/messages.ts"],"names":[],"mappings":"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","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/** Centralized error/warning text for the hashline parser, applier, and patcher. */\n\nimport { formatNumberedLine, HL_FILE_HASH_SEP, HL_FILE_PREFIX, HL_FILE_SUFFIX } from \"./format.js\";\n\n/** Lines of context shown either side of a hash mismatch. */\nexport const MISMATCH_CONTEXT = 2;\n\n/**\n * Numbered `LINE:TEXT` rows around `anchorLines` (±{@link MISMATCH_CONTEXT}),\n * `*`-marking anchors, `...` between non-adjacent runs. Out-of-range anchors\n * contribute no rows.\n */\nexport function formatAnchoredContext(anchorLines: readonly number[], fileLines: readonly string[]): string[] {\n\tconst displayLines = new Set<number>();\n\tfor (const line of anchorLines) {\n\t\tif (line < 1 || line > fileLines.length) continue;\n\t\tconst lo = Math.max(1, line - MISMATCH_CONTEXT);\n\t\tconst hi = Math.min(fileLines.length, line + MISMATCH_CONTEXT);\n\t\tfor (let lineNum = lo; lineNum <= hi; lineNum++) displayLines.add(lineNum);\n\t}\n\tconst anchorSet = new Set(anchorLines);\n\tconst rows: string[] = [];\n\tlet previous = -1;\n\tfor (const lineNum of [...displayLines].sort((a, b) => a - b)) {\n\t\tif (previous !== -1 && lineNum > previous + 1) rows.push(\"...\");\n\t\tprevious = lineNum;\n\t\tconst marker = anchorSet.has(lineNum) ? \"*\" : \" \";\n\t\trows.push(`${marker}${formatNumberedLine(lineNum, fileLines[lineNum - 1] ?? \"\")}`);\n\t}\n\treturn rows;\n}\n\n/** Optional patch envelope start marker; silently consumed. */\nexport const BEGIN_PATCH_MARKER = \"*** Begin Patch\";\n\n/** Optional patch envelope end marker; terminates parsing. */\nexport const END_PATCH_MARKER = \"*** End Patch\";\n\n/**\n * Truncation sentinel emitted by an agent loop mid-call. Ends parsing like\n * {@link END_PATCH_MARKER}, without a warning.\n */\nexport const ABORT_MARKER = \"*** Abort\";\n\n/** Two consecutive hunks targeted the exact same concrete range. */\nexport const REPLACE_PAIR_COALESCED_WARNING =\n\t\"Two hunks targeted the same range; kept only the second. One `replace N..M:` hunk per range — the body is the final content, never old+new.\";\n\n/** Bare bodyless hunk followed by an overlapping concrete hunk. */\nexport const REPLACE_PAIR_COALESCED_OVERLAP_WARNING =\n\t\"Dropped a bare hunk overlapped by the concrete hunk after it. One `replace N..M:` hunk per range — the body is the final content, never old+new.\";\n\n/** Bare body rows auto-converted to literal `+` rows. */\nexport const BARE_BODY_AUTO_PIPED_WARNING =\n\t\"Auto-prefixed bare body row(s) with `+`. Body rows must be `+TEXT` literal lines.\";\n\n/** Unified-diff-style `-` row in a hunk body. */\nexport const MINUS_ROW_REJECTED =\n\t\"`-` rows are not valid; the range already names the lines being changed. For a literal `-` line, write `+-…`.\";\n\n/** Replace hunk with no body. */\nexport const EMPTY_REPLACE = \"`replace N..M:` needs at least one `+TEXT` body row. To delete lines, use `delete N..M`.\";\n\n/** `replace block N:` hunk with no body. */\nexport const EMPTY_BLOCK =\n\t\"`replace block N:` needs at least one `+TEXT` body row. To delete a block, use `delete block N`.\";\n\n/**\n * Block-anchored replace/delete could not resolve to a syntactic block\n * (unsupported language, blank/out-of-range line, no node beginning on N, or\n * parse error). Appends a {@link formatAnchoredContext} preview when\n * `fileLines` is given. `insert after block N:` never reaches this — it is\n * lowered to plain `insert after N:` instead (see\n * {@link insertAfterBlockUnresolvedLoweredWarning}).\n */\nexport function blockUnresolvedMessage(\n\tline: number,\n\top: \"replace\" | \"delete\" = \"replace\",\n\tfileLines?: readonly string[],\n): string {\n\tconst phrase = op === \"delete\" ? `delete block ${line}` : `replace block ${line}:`;\n\tconst fallback = op === \"delete\" ? `delete ${line}..M` : `replace ${line}..M:`;\n\tlet message =\n\t\t`\\`${phrase}\\` could not resolve a syntactic block beginning on line ${line} ` +\n\t\t`(unsupported language, blank/closer line, or parse error). Use \\`${fallback}\\` with explicit lines.`;\n\tif (fileLines) {\n\t\tconst context = formatAnchoredContext([line], fileLines);\n\t\tif (context.length > 0) message += `\\n\\n${context.join(\"\\n\")}`;\n\t}\n\treturn message;\n}\n\n/** Block-anchored edit reached a path with no {@link BlockResolver} wired in — a host-configuration bug. */\nexport const BLOCK_RESOLVER_UNAVAILABLE =\n\t\"`replace block`/`delete block`/`insert after block` are not available here (no block resolver configured). Use a concrete line range.\";\n\n/**\n * `insert after block N:` anchored on a closing-delimiter line, lowered to\n * plain `insert after N:` — the closer ends a block, and inserting after it\n * is exactly what the plain form does.\n */\nexport function insertAfterBlockCloserLoweredWarning(line: number): string {\n\treturn `\\`insert after block ${line}:\\` anchors on a closing delimiter, so it was applied as plain \\`insert after ${line}:\\`. Anchor on the line that OPENS the construct.`;\n}\n\n/**\n * `insert after block N:` anchor unresolvable (unsupported language, blank\n * line, parse error, or no resolver), lowered to plain `insert after N:` —\n * applying with a warning beats failing the patch.\n */\nexport function insertAfterBlockUnresolvedLoweredWarning(line: number): string {\n\treturn `\\`insert after block ${line}:\\` could not resolve a syntactic block on line ${line}, so it was applied as plain \\`insert after ${line}:\\`. Verify the landing line; anchor on a line that OPENS a construct.`;\n}\n\n/**\n * Internal invariant: `applyEdits` received an unresolved `replace block N:`\n * edit; `resolveBlockEdits` must run first. Wiring bug, not authored input.\n */\nexport const UNRESOLVED_BLOCK_INTERNAL =\n\t\"internal error: unresolved `replace block` edit reached the applier (resolveBlockEdits was not run).\";\n\n/** Delete hunk received a body row. */\nexport const DELETE_TAKES_NO_BODY = \"`delete N..M` does not take body rows. Remove the body, or use `replace N..M:`.\";\n\n/** `delete block N` hunk received a body row. */\nexport const DELETE_BLOCK_TAKES_NO_BODY =\n\t\"`delete block N` does not take body rows. Remove the body, or use `replace block N:`.\";\n\n/** Insert hunk with no body. */\nexport const EMPTY_INSERT = \"`insert` needs at least one `+TEXT` body row.\";\n\n/**\n * `insert after` body indented shallower than the anchor: the landing slid\n * forward past trailing closer lines — the common \"anchored on the last line\n * I read instead of after the block\" mistake.\n */\nexport function afterInsertLandingShiftWarning(anchorLine: number, landingLine: number, crossed: number): string {\n\treturn `insert after ${anchorLine}: body indented shallower than the anchor, so the landing moved past ${crossed} closing line${crossed === 1 ? \"\" : \"s\"} to after line ${landingLine}. For the deeper position inside the block, re-issue with the body indented to match.`;\n}\n\n/**\n * `insert after block N:` body indented deeper than the block's closer: the\n * landing was pulled inside the block — a deeper body almost always means\n * \"append inside the block's body\".\n */\nexport function blockInsertLandingShiftWarning(blockStart: number, closerLine: number, landingLine: number): string {\n\treturn `insert after block ${blockStart}: body indented deeper than closing line ${closerLine}, so it was placed inside the block, after line ${landingLine}. \\`insert after block\\` lands AFTER the block at sibling depth — if inside was intended, use plain \\`insert after ${closerLine}:\\`.`;\n}\n\n/** `Recovery`: an external write matched a cached snapshot. */\nexport const RECOVERY_EXTERNAL_WARNING =\n\t\"Recovered from a stale file hash using a previous read snapshot (file changed externally between read and edit).\";\n\n/** `Recovery`: a prior in-session edit advanced the hash. */\nexport const RECOVERY_SESSION_CHAIN_WARNING =\n\t\"Recovered from a stale file hash using an earlier in-session snapshot (a prior edit in this session advanced the hash).\";\n\n/**\n * `Recovery`: session-chain replay fast-path. Less certain than\n * {@link RECOVERY_SESSION_CHAIN_WARNING} — the 3-way merge refused, the\n * anchor-content gate passed, but a coincidental insert+delete earlier in\n * the chain could still misplace an anchor — hence the verify hedge.\n */\nexport const RECOVERY_SESSION_REPLAY_WARNING =\n\t\"Recovered by replaying your edits onto the current file content (a prior in-session edit changed the lines you re-targeted with a stale hash). Verify the diff matches your intent.\";\n\n/**\n * `insert head:`/`insert tail:` applied despite a stale snapshot tag.\n * Head/tail position is content-independent, so drift is non-fatal: apply\n * onto live content and warn instead of hard-failing.\n */\nexport const HEADTAIL_DRIFT_WARNING =\n\t\"Applied the `insert head:`/`insert tail:` edit despite a stale snapshot tag (file changed since your read) — head/tail position is content-independent. Re-read if the drift was unexpected.\";\n\n/**\n * Section omitted the mandatory snapshot tag. Shared by the apply\n * ({@link Patcher.prepare}) and preview/diff paths so both stay in lockstep.\n */\nexport function missingSnapshotTagMessage(sectionPath: string): string {\n\treturn `Missing hashline snapshot tag for ${sectionPath}; use \\`${HL_FILE_PREFIX}${sectionPath}${HL_FILE_HASH_SEP}tag${HL_FILE_SUFFIX}\\` from your latest read/search output. To create a new file, use the write tool.`;\n}\n"]}
@@ -0,0 +1,45 @@
1
+ /** Format the required-shape diagnostic shown when a line reference is malformed. */
2
+ export declare function formatFullAnchorRequirement(raw?: string): string;
3
+ /** Parse a decorated bare line-number anchor like `42`, `*42:foo`, ` > 7`. */
4
+ export declare function parseTag(ref: string): {
5
+ line: number;
6
+ };
7
+ export interface MismatchDetails {
8
+ path?: string;
9
+ expectedFileHash: string;
10
+ actualFileHash: string;
11
+ fileLines: string[];
12
+ anchorLines?: readonly number[];
13
+ /**
14
+ * `true` when the section's expected hash resolved to a recorded snapshot
15
+ * (file content drifted since that snapshot), `false` when no snapshot
16
+ * was ever recorded for the hash (likely fabricated or carried over from
17
+ * a prior session). Drives a more actionable rejection message; defaults
18
+ * to `true` for backward compatibility with direct callers.
19
+ */
20
+ hashRecognized?: boolean;
21
+ }
22
+ /**
23
+ * Raised when a hashline section's snapshot tag doesn't match the live file's
24
+ * content (and recovery, if configured, declined the merge). Carries the
25
+ * file lines plus anchored lines so renderers can produce a richer
26
+ * diagnostic via {@link MismatchError.displayMessage}.
27
+ */
28
+ export declare class MismatchError extends Error {
29
+ readonly path: string | undefined;
30
+ readonly expectedFileHash: string;
31
+ readonly actualFileHash: string;
32
+ readonly fileLines: string[];
33
+ readonly anchorLines: readonly number[];
34
+ readonly hashRecognized: boolean;
35
+ constructor(details: MismatchDetails);
36
+ get displayMessage(): string;
37
+ static rejectionHeader(details: MismatchDetails): string[];
38
+ static formatDisplayMessage(details: MismatchDetails): string;
39
+ static formatMessage(details: MismatchDetails): string;
40
+ }
41
+ /** Throws when the line reference is out of bounds for the given file. */
42
+ export declare function validateLineRef(ref: {
43
+ line: number;
44
+ }, fileLines: string[]): void;
45
+ //# sourceMappingURL=mismatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mismatch.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/mismatch.ts"],"names":[],"mappings":"AAcA,qFAAqF;AACrF,wBAAgB,2BAA2B,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAQtD;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;GAKG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAEjC,YAAY,OAAO,EAAE,eAAe,EASnC;IAED,IAAI,cAAc,IAAI,MAAM,CAS3B;IAED,MAAM,CAAC,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,EAAE,CAazD;IAED,MAAM,CAAC,oBAAoB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAE5D;IAED,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAMrD;CACD;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAIhF","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Error type raised when a section's snapshot tag does not match the live file\n * content and recovery is unavailable / has failed.\n *\n * Carries enough context to render a useful diagnostic: the anchored lines\n * plus a couple of lines of surrounding context. The {@link MismatchError}\n * formats this into a message at construction time.\n */\nimport { HL_FILE_HASH_EXAMPLES, HL_FILE_HASH_SEP, HL_FILE_PREFIX, HL_FILE_SUFFIX } from \"./format.js\";\nimport { formatAnchoredContext } from \"./messages.js\";\n\nconst LINE_REF_RE = /^\\s*[>+\\-*]*\\s*(\\d+)(?::.*)?\\s*$/;\n/** Format the required-shape diagnostic shown when a line reference is malformed. */\nexport function formatFullAnchorRequirement(raw?: string): string {\n\tconst received = raw === undefined ? \"\" : ` Received ${JSON.stringify(raw)}.`;\n\treturn (\n\t\t`a bare line number from read/search output plus the section header content-hash tag ` +\n\t\t`(for example ${HL_FILE_PREFIX}src/foo.ts${HL_FILE_HASH_SEP}${HL_FILE_HASH_EXAMPLES[0]}${HL_FILE_SUFFIX} and line \"160\")${received}`\n\t);\n}\n\n/** Parse a decorated bare line-number anchor like `42`, `*42:foo`, ` > 7`. */\nexport function parseTag(ref: string): { line: number } {\n\tconst match = ref.match(LINE_REF_RE);\n\tif (!match) {\n\t\tthrow new Error(`Invalid line reference. Expected ${formatFullAnchorRequirement(ref)}.`);\n\t}\n\tconst line = Number.parseInt(match[1], 10);\n\tif (line < 1) throw new Error(`Line number must be >= 1, got ${line} in \"${ref}\".`);\n\treturn { line };\n}\n\nexport interface MismatchDetails {\n\tpath?: string;\n\texpectedFileHash: string;\n\tactualFileHash: string;\n\tfileLines: string[];\n\tanchorLines?: readonly number[];\n\t/**\n\t * `true` when the section's expected hash resolved to a recorded snapshot\n\t * (file content drifted since that snapshot), `false` when no snapshot\n\t * was ever recorded for the hash (likely fabricated or carried over from\n\t * a prior session). Drives a more actionable rejection message; defaults\n\t * to `true` for backward compatibility with direct callers.\n\t */\n\thashRecognized?: boolean;\n}\n\n/**\n * Raised when a hashline section's snapshot tag doesn't match the live file's\n * content (and recovery, if configured, declined the merge). Carries the\n * file lines plus anchored lines so renderers can produce a richer\n * diagnostic via {@link MismatchError.displayMessage}.\n */\nexport class MismatchError extends Error {\n\treadonly path: string | undefined;\n\treadonly expectedFileHash: string;\n\treadonly actualFileHash: string;\n\treadonly fileLines: string[];\n\treadonly anchorLines: readonly number[];\n\treadonly hashRecognized: boolean;\n\n\tconstructor(details: MismatchDetails) {\n\t\tsuper(MismatchError.formatMessage(details));\n\t\tthis.name = \"MismatchError\";\n\t\tthis.path = details.path;\n\t\tthis.expectedFileHash = details.expectedFileHash;\n\t\tthis.actualFileHash = details.actualFileHash;\n\t\tthis.fileLines = details.fileLines;\n\t\tthis.anchorLines = details.anchorLines ?? [];\n\t\tthis.hashRecognized = details.hashRecognized ?? true;\n\t}\n\n\tget displayMessage(): string {\n\t\treturn MismatchError.formatDisplayMessage({\n\t\t\tpath: this.path,\n\t\t\texpectedFileHash: this.expectedFileHash,\n\t\t\tactualFileHash: this.actualFileHash,\n\t\t\tfileLines: this.fileLines,\n\t\t\tanchorLines: this.anchorLines,\n\t\t\thashRecognized: this.hashRecognized,\n\t\t});\n\t}\n\n\tstatic rejectionHeader(details: MismatchDetails): string[] {\n\t\tconst pathText = details.path ? ` for ${details.path}` : \"\";\n\t\tconst hashRecognized = details.hashRecognized ?? true;\n\t\tif (!hashRecognized) {\n\t\t\treturn [\n\t\t\t\t`Edit rejected${pathText}: hash ${HL_FILE_HASH_SEP}${details.expectedFileHash} is not from this session.`,\n\t\t\t\t`The current file hashes to ${HL_FILE_HASH_SEP}${details.actualFileHash}. Re-read the file with \\`read\\` to copy a current ${HL_FILE_PREFIX}path${HL_FILE_HASH_SEP}tag${HL_FILE_SUFFIX} header — never invent the tag and never reuse one from a prior session.`,\n\t\t\t];\n\t\t}\n\t\treturn [\n\t\t\t`Edit rejected${pathText}: file changed between read and edit.`,\n\t\t\t`Section is bound to ${HL_FILE_HASH_SEP}${details.expectedFileHash}, but the current file hashes to ${HL_FILE_HASH_SEP}${details.actualFileHash}. If a prior edit in this session modified this file, copy the ${HL_FILE_PREFIX}path${HL_FILE_HASH_SEP}newhash${HL_FILE_SUFFIX} header from that edit's response; otherwise re-read the file with \\`read\\` to refresh the tag before retrying.`,\n\t\t];\n\t}\n\n\tstatic formatDisplayMessage(details: MismatchDetails): string {\n\t\treturn MismatchError.formatMessage(details);\n\t}\n\n\tstatic formatMessage(details: MismatchDetails): string {\n\t\tconst lines = MismatchError.rejectionHeader(details);\n\t\tconst context = formatAnchoredContext(details.anchorLines ?? [], details.fileLines);\n\t\tif (context.length === 0) return lines.join(\"\\n\");\n\t\tlines.push(\"\", ...context);\n\t\treturn lines.join(\"\\n\");\n\t}\n}\n\n/** Throws when the line reference is out of bounds for the given file. */\nexport function validateLineRef(ref: { line: number }, fileLines: string[]): void {\n\tif (ref.line < 1 || ref.line > fileLines.length) {\n\t\tthrow new Error(`Line ${ref.line} does not exist (file has ${fileLines.length} lines)`);\n\t}\n}\n"]}
@@ -0,0 +1,90 @@
1
+ // @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.
2
+ // Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).
3
+ /**
4
+ * Error type raised when a section's snapshot tag does not match the live file
5
+ * content and recovery is unavailable / has failed.
6
+ *
7
+ * Carries enough context to render a useful diagnostic: the anchored lines
8
+ * plus a couple of lines of surrounding context. The {@link MismatchError}
9
+ * formats this into a message at construction time.
10
+ */
11
+ import { HL_FILE_HASH_EXAMPLES, HL_FILE_HASH_SEP, HL_FILE_PREFIX, HL_FILE_SUFFIX } from "./format.js";
12
+ import { formatAnchoredContext } from "./messages.js";
13
+ const LINE_REF_RE = /^\s*[>+\-*]*\s*(\d+)(?::.*)?\s*$/;
14
+ /** Format the required-shape diagnostic shown when a line reference is malformed. */
15
+ export function formatFullAnchorRequirement(raw) {
16
+ const received = raw === undefined ? "" : ` Received ${JSON.stringify(raw)}.`;
17
+ return (`a bare line number from read/search output plus the section header content-hash tag ` +
18
+ `(for example ${HL_FILE_PREFIX}src/foo.ts${HL_FILE_HASH_SEP}${HL_FILE_HASH_EXAMPLES[0]}${HL_FILE_SUFFIX} and line "160")${received}`);
19
+ }
20
+ /** Parse a decorated bare line-number anchor like `42`, `*42:foo`, ` > 7`. */
21
+ export function parseTag(ref) {
22
+ const match = ref.match(LINE_REF_RE);
23
+ if (!match) {
24
+ throw new Error(`Invalid line reference. Expected ${formatFullAnchorRequirement(ref)}.`);
25
+ }
26
+ const line = Number.parseInt(match[1], 10);
27
+ if (line < 1)
28
+ throw new Error(`Line number must be >= 1, got ${line} in "${ref}".`);
29
+ return { line };
30
+ }
31
+ /**
32
+ * Raised when a hashline section's snapshot tag doesn't match the live file's
33
+ * content (and recovery, if configured, declined the merge). Carries the
34
+ * file lines plus anchored lines so renderers can produce a richer
35
+ * diagnostic via {@link MismatchError.displayMessage}.
36
+ */
37
+ export class MismatchError extends Error {
38
+ constructor(details) {
39
+ super(MismatchError.formatMessage(details));
40
+ this.name = "MismatchError";
41
+ this.path = details.path;
42
+ this.expectedFileHash = details.expectedFileHash;
43
+ this.actualFileHash = details.actualFileHash;
44
+ this.fileLines = details.fileLines;
45
+ this.anchorLines = details.anchorLines ?? [];
46
+ this.hashRecognized = details.hashRecognized ?? true;
47
+ }
48
+ get displayMessage() {
49
+ return MismatchError.formatDisplayMessage({
50
+ path: this.path,
51
+ expectedFileHash: this.expectedFileHash,
52
+ actualFileHash: this.actualFileHash,
53
+ fileLines: this.fileLines,
54
+ anchorLines: this.anchorLines,
55
+ hashRecognized: this.hashRecognized,
56
+ });
57
+ }
58
+ static rejectionHeader(details) {
59
+ const pathText = details.path ? ` for ${details.path}` : "";
60
+ const hashRecognized = details.hashRecognized ?? true;
61
+ if (!hashRecognized) {
62
+ return [
63
+ `Edit rejected${pathText}: hash ${HL_FILE_HASH_SEP}${details.expectedFileHash} is not from this session.`,
64
+ `The current file hashes to ${HL_FILE_HASH_SEP}${details.actualFileHash}. Re-read the file with \`read\` to copy a current ${HL_FILE_PREFIX}path${HL_FILE_HASH_SEP}tag${HL_FILE_SUFFIX} header — never invent the tag and never reuse one from a prior session.`,
65
+ ];
66
+ }
67
+ return [
68
+ `Edit rejected${pathText}: file changed between read and edit.`,
69
+ `Section is bound to ${HL_FILE_HASH_SEP}${details.expectedFileHash}, but the current file hashes to ${HL_FILE_HASH_SEP}${details.actualFileHash}. If a prior edit in this session modified this file, copy the ${HL_FILE_PREFIX}path${HL_FILE_HASH_SEP}newhash${HL_FILE_SUFFIX} header from that edit's response; otherwise re-read the file with \`read\` to refresh the tag before retrying.`,
70
+ ];
71
+ }
72
+ static formatDisplayMessage(details) {
73
+ return MismatchError.formatMessage(details);
74
+ }
75
+ static formatMessage(details) {
76
+ const lines = MismatchError.rejectionHeader(details);
77
+ const context = formatAnchoredContext(details.anchorLines ?? [], details.fileLines);
78
+ if (context.length === 0)
79
+ return lines.join("\n");
80
+ lines.push("", ...context);
81
+ return lines.join("\n");
82
+ }
83
+ }
84
+ /** Throws when the line reference is out of bounds for the given file. */
85
+ export function validateLineRef(ref, fileLines) {
86
+ if (ref.line < 1 || ref.line > fileLines.length) {
87
+ throw new Error(`Line ${ref.line} does not exist (file has ${fileLines.length} lines)`);
88
+ }
89
+ }
90
+ //# sourceMappingURL=mismatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mismatch.js","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/mismatch.ts"],"names":[],"mappings":"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","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Error type raised when a section's snapshot tag does not match the live file\n * content and recovery is unavailable / has failed.\n *\n * Carries enough context to render a useful diagnostic: the anchored lines\n * plus a couple of lines of surrounding context. The {@link MismatchError}\n * formats this into a message at construction time.\n */\nimport { HL_FILE_HASH_EXAMPLES, HL_FILE_HASH_SEP, HL_FILE_PREFIX, HL_FILE_SUFFIX } from \"./format.js\";\nimport { formatAnchoredContext } from \"./messages.js\";\n\nconst LINE_REF_RE = /^\\s*[>+\\-*]*\\s*(\\d+)(?::.*)?\\s*$/;\n/** Format the required-shape diagnostic shown when a line reference is malformed. */\nexport function formatFullAnchorRequirement(raw?: string): string {\n\tconst received = raw === undefined ? \"\" : ` Received ${JSON.stringify(raw)}.`;\n\treturn (\n\t\t`a bare line number from read/search output plus the section header content-hash tag ` +\n\t\t`(for example ${HL_FILE_PREFIX}src/foo.ts${HL_FILE_HASH_SEP}${HL_FILE_HASH_EXAMPLES[0]}${HL_FILE_SUFFIX} and line \"160\")${received}`\n\t);\n}\n\n/** Parse a decorated bare line-number anchor like `42`, `*42:foo`, ` > 7`. */\nexport function parseTag(ref: string): { line: number } {\n\tconst match = ref.match(LINE_REF_RE);\n\tif (!match) {\n\t\tthrow new Error(`Invalid line reference. Expected ${formatFullAnchorRequirement(ref)}.`);\n\t}\n\tconst line = Number.parseInt(match[1], 10);\n\tif (line < 1) throw new Error(`Line number must be >= 1, got ${line} in \"${ref}\".`);\n\treturn { line };\n}\n\nexport interface MismatchDetails {\n\tpath?: string;\n\texpectedFileHash: string;\n\tactualFileHash: string;\n\tfileLines: string[];\n\tanchorLines?: readonly number[];\n\t/**\n\t * `true` when the section's expected hash resolved to a recorded snapshot\n\t * (file content drifted since that snapshot), `false` when no snapshot\n\t * was ever recorded for the hash (likely fabricated or carried over from\n\t * a prior session). Drives a more actionable rejection message; defaults\n\t * to `true` for backward compatibility with direct callers.\n\t */\n\thashRecognized?: boolean;\n}\n\n/**\n * Raised when a hashline section's snapshot tag doesn't match the live file's\n * content (and recovery, if configured, declined the merge). Carries the\n * file lines plus anchored lines so renderers can produce a richer\n * diagnostic via {@link MismatchError.displayMessage}.\n */\nexport class MismatchError extends Error {\n\treadonly path: string | undefined;\n\treadonly expectedFileHash: string;\n\treadonly actualFileHash: string;\n\treadonly fileLines: string[];\n\treadonly anchorLines: readonly number[];\n\treadonly hashRecognized: boolean;\n\n\tconstructor(details: MismatchDetails) {\n\t\tsuper(MismatchError.formatMessage(details));\n\t\tthis.name = \"MismatchError\";\n\t\tthis.path = details.path;\n\t\tthis.expectedFileHash = details.expectedFileHash;\n\t\tthis.actualFileHash = details.actualFileHash;\n\t\tthis.fileLines = details.fileLines;\n\t\tthis.anchorLines = details.anchorLines ?? [];\n\t\tthis.hashRecognized = details.hashRecognized ?? true;\n\t}\n\n\tget displayMessage(): string {\n\t\treturn MismatchError.formatDisplayMessage({\n\t\t\tpath: this.path,\n\t\t\texpectedFileHash: this.expectedFileHash,\n\t\t\tactualFileHash: this.actualFileHash,\n\t\t\tfileLines: this.fileLines,\n\t\t\tanchorLines: this.anchorLines,\n\t\t\thashRecognized: this.hashRecognized,\n\t\t});\n\t}\n\n\tstatic rejectionHeader(details: MismatchDetails): string[] {\n\t\tconst pathText = details.path ? ` for ${details.path}` : \"\";\n\t\tconst hashRecognized = details.hashRecognized ?? true;\n\t\tif (!hashRecognized) {\n\t\t\treturn [\n\t\t\t\t`Edit rejected${pathText}: hash ${HL_FILE_HASH_SEP}${details.expectedFileHash} is not from this session.`,\n\t\t\t\t`The current file hashes to ${HL_FILE_HASH_SEP}${details.actualFileHash}. Re-read the file with \\`read\\` to copy a current ${HL_FILE_PREFIX}path${HL_FILE_HASH_SEP}tag${HL_FILE_SUFFIX} header — never invent the tag and never reuse one from a prior session.`,\n\t\t\t];\n\t\t}\n\t\treturn [\n\t\t\t`Edit rejected${pathText}: file changed between read and edit.`,\n\t\t\t`Section is bound to ${HL_FILE_HASH_SEP}${details.expectedFileHash}, but the current file hashes to ${HL_FILE_HASH_SEP}${details.actualFileHash}. If a prior edit in this session modified this file, copy the ${HL_FILE_PREFIX}path${HL_FILE_HASH_SEP}newhash${HL_FILE_SUFFIX} header from that edit's response; otherwise re-read the file with \\`read\\` to refresh the tag before retrying.`,\n\t\t];\n\t}\n\n\tstatic formatDisplayMessage(details: MismatchDetails): string {\n\t\treturn MismatchError.formatMessage(details);\n\t}\n\n\tstatic formatMessage(details: MismatchDetails): string {\n\t\tconst lines = MismatchError.rejectionHeader(details);\n\t\tconst context = formatAnchoredContext(details.anchorLines ?? [], details.fileLines);\n\t\tif (context.length === 0) return lines.join(\"\\n\");\n\t\tlines.push(\"\", ...context);\n\t\treturn lines.join(\"\\n\");\n\t}\n}\n\n/** Throws when the line reference is out of bounds for the given file. */\nexport function validateLineRef(ref: { line: number }, fileLines: string[]): void {\n\tif (ref.line < 1 || ref.line > fileLines.length) {\n\t\tthrow new Error(`Line ${ref.line} does not exist (file has ${fileLines.length} lines)`);\n\t}\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Minimal text-shape normalization: line-ending detection / round-trip and
3
+ * BOM stripping. The patcher uses these to canonicalize text to LF before
4
+ * applying edits and to restore the original shape on write-back.
5
+ */
6
+ export type LineEnding = "\r\n" | "\n" | "\r";
7
+ /** Detect the first line ending style in `content`. Defaults to LF when neither is present. */
8
+ export declare function detectLineEnding(content: string): LineEnding;
9
+ /** Normalize every line ending to LF. */
10
+ export declare function normalizeToLF(text: string): string;
11
+ /** Re-encode LF text with the requested line ending. */
12
+ export declare function restoreLineEndings(text: string, ending: LineEnding): string;
13
+ export interface BomResult {
14
+ /** Either the empty string or the BOM sequence (currently UTF-8 BOM). */
15
+ bom: string;
16
+ /** Text with any leading BOM removed. */
17
+ text: string;
18
+ }
19
+ /** Strip a UTF-8 BOM if present and return both the BOM and the trailing text. */
20
+ export declare function stripBom(content: string): BomResult;
21
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/normalize.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,+FAA+F;AAC/F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAO5D;AAED,yCAAyC;AACzC,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAG3E;AAED,MAAM,WAAW,SAAS;IACzB,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;CACb;AAED,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAEnD","sourcesContent":["// @generated vendored from oh-my-pi packages/hashline @ 15b5c1397fc with Atomic parity adaptations -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted for Atomic's Node runtime plus CR-only line-ending round trips.\n/**\n * Minimal text-shape normalization: line-ending detection / round-trip and\n * BOM stripping. The patcher uses these to canonicalize text to LF before\n * applying edits and to restore the original shape on write-back.\n */\n\nexport type LineEnding = \"\\r\\n\" | \"\\n\" | \"\\r\";\n\n/** Detect the first line ending style in `content`. Defaults to LF when neither is present. */\nexport function detectLineEnding(content: string): LineEnding {\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst ch = content[i];\n\t\tif (ch === \"\\n\") return \"\\n\";\n\t\tif (ch === \"\\r\") return content[i + 1] === \"\\n\" ? \"\\r\\n\" : \"\\r\";\n\t}\n\treturn \"\\n\";\n}\n\n/** Normalize every line ending to LF. */\nexport function normalizeToLF(text: string): string {\n\treturn text.replace(/\\r\\n?/g, \"\\n\");\n}\n\n/** Re-encode LF text with the requested line ending. */\nexport function restoreLineEndings(text: string, ending: LineEnding): string {\n\tif (ending === \"\\r\\n\") return text.replace(/\\n/g, \"\\r\\n\");\n\treturn ending === \"\\r\" ? text.replace(/\\n/g, \"\\r\") : text;\n}\n\nexport interface BomResult {\n\t/** Either the empty string or the BOM sequence (currently UTF-8 BOM). */\n\tbom: string;\n\t/** Text with any leading BOM removed. */\n\ttext: string;\n}\n\n/** Strip a UTF-8 BOM if present and return both the BOM and the trailing text. */\nexport function stripBom(content: string): BomResult {\n\treturn content.startsWith(\"\\uFEFF\") ? { bom: \"\\uFEFF\", text: content.slice(1) } : { bom: \"\", text: content };\n}\n"]}
@@ -0,0 +1,33 @@
1
+ // @generated vendored from oh-my-pi packages/hashline @ 15b5c1397fc with Atomic parity adaptations -- DO NOT EDIT.
2
+ // Parity source for the Atomic hashline edit engine (issue #1483); adapted for Atomic's Node runtime plus CR-only line-ending round trips.
3
+ /**
4
+ * Minimal text-shape normalization: line-ending detection / round-trip and
5
+ * BOM stripping. The patcher uses these to canonicalize text to LF before
6
+ * applying edits and to restore the original shape on write-back.
7
+ */
8
+ /** Detect the first line ending style in `content`. Defaults to LF when neither is present. */
9
+ export function detectLineEnding(content) {
10
+ for (let i = 0; i < content.length; i++) {
11
+ const ch = content[i];
12
+ if (ch === "\n")
13
+ return "\n";
14
+ if (ch === "\r")
15
+ return content[i + 1] === "\n" ? "\r\n" : "\r";
16
+ }
17
+ return "\n";
18
+ }
19
+ /** Normalize every line ending to LF. */
20
+ export function normalizeToLF(text) {
21
+ return text.replace(/\r\n?/g, "\n");
22
+ }
23
+ /** Re-encode LF text with the requested line ending. */
24
+ export function restoreLineEndings(text, ending) {
25
+ if (ending === "\r\n")
26
+ return text.replace(/\n/g, "\r\n");
27
+ return ending === "\r" ? text.replace(/\n/g, "\r") : text;
28
+ }
29
+ /** Strip a UTF-8 BOM if present and return both the BOM and the trailing text. */
30
+ export function stripBom(content) {
31
+ return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
32
+ }
33
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/normalize.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,2IAA2I;AAC3I;;;;GAIG;AAIH,+FAA+F;AAC/F,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,EAAE,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC7B,IAAI,EAAE,KAAK,IAAI;YAAE,OAAO,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,MAAkB;IAClE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1D,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC3D,CAAC;AASD,kFAAkF;AAClF,MAAM,UAAU,QAAQ,CAAC,OAAe;IACvC,OAAO,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC9G,CAAC","sourcesContent":["// @generated vendored from oh-my-pi packages/hashline @ 15b5c1397fc with Atomic parity adaptations -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted for Atomic's Node runtime plus CR-only line-ending round trips.\n/**\n * Minimal text-shape normalization: line-ending detection / round-trip and\n * BOM stripping. The patcher uses these to canonicalize text to LF before\n * applying edits and to restore the original shape on write-back.\n */\n\nexport type LineEnding = \"\\r\\n\" | \"\\n\" | \"\\r\";\n\n/** Detect the first line ending style in `content`. Defaults to LF when neither is present. */\nexport function detectLineEnding(content: string): LineEnding {\n\tfor (let i = 0; i < content.length; i++) {\n\t\tconst ch = content[i];\n\t\tif (ch === \"\\n\") return \"\\n\";\n\t\tif (ch === \"\\r\") return content[i + 1] === \"\\n\" ? \"\\r\\n\" : \"\\r\";\n\t}\n\treturn \"\\n\";\n}\n\n/** Normalize every line ending to LF. */\nexport function normalizeToLF(text: string): string {\n\treturn text.replace(/\\r\\n?/g, \"\\n\");\n}\n\n/** Re-encode LF text with the requested line ending. */\nexport function restoreLineEndings(text: string, ending: LineEnding): string {\n\tif (ending === \"\\r\\n\") return text.replace(/\\n/g, \"\\r\\n\");\n\treturn ending === \"\\r\" ? text.replace(/\\n/g, \"\\r\") : text;\n}\n\nexport interface BomResult {\n\t/** Either the empty string or the BOM sequence (currently UTF-8 BOM). */\n\tbom: string;\n\t/** Text with any leading BOM removed. */\n\ttext: string;\n}\n\n/** Strip a UTF-8 BOM if present and return both the BOM and the trailing text. */\nexport function stripBom(content: string): BomResult {\n\treturn content.startsWith(\"\\uFEFF\") ? { bom: \"\\uFEFF\", text: content.slice(1) } : { bom: \"\", text: content };\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { type Token } from "./tokenizer.js";
2
+ import type { Edit } from "./types.js";
3
+ export declare class Executor {
4
+ #private;
5
+ feed(token: Token): void;
6
+ end(): {
7
+ edits: Edit[];
8
+ warnings: string[];
9
+ };
10
+ endStreaming(): {
11
+ edits: Edit[];
12
+ warnings: string[];
13
+ };
14
+ reset(): void;
15
+ }
16
+ export declare function parsePatch(diff: string): {
17
+ edits: Edit[];
18
+ warnings: string[];
19
+ };
20
+ export declare function parsePatchStreaming(diff: string): {
21
+ edits: Edit[];
22
+ warnings: string[];
23
+ };
24
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../../src/core/tools/hashline-engine/parser.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAmD,KAAK,KAAK,EAAa,MAAM,gBAAgB,CAAC;AACxG,OAAO,KAAK,EAAkB,IAAI,EAAE,MAAM,YAAY,CAAC;AAyFvD,qBAAa,QAAQ;;IAkBpB,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CA0CvB;IAED,GAAG,IAAI;QAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAK3C;IAED,YAAY,IAAI;QAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAQpD;IAED,KAAK,IAAI,IAAI,CAOZ;CAqMD;AAOD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAK9E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAMvF","sourcesContent":["// @generated vendored verbatim from oh-my-pi packages/hashline @ 15b5c1397fc -- DO NOT EDIT.\n// Parity source for the Atomic hashline edit engine (issue #1483); adapted only for Atomic's Node runtime (relative imports, Bun->Node host calls, erasable constructor syntax).\n/**\n * Token-driven state machine that turns a stream of {@link Token}s into a\n * flat list of {@link Edit}s. Sits between the {@link Tokenizer} and the\n * applier.\n */\nimport { HL_PAYLOAD_REPLACE } from \"./format.js\";\nimport {\n\tBARE_BODY_AUTO_PIPED_WARNING,\n\tDELETE_BLOCK_TAKES_NO_BODY,\n\tDELETE_TAKES_NO_BODY,\n\tEMPTY_BLOCK,\n\tEMPTY_INSERT,\n\tMINUS_ROW_REJECTED,\n} from \"./messages.js\";\nimport { stripOneLeadingHashlinePrefix } from \"./prefixes.js\";\nimport { type BlockTarget, cloneCursor, type ParsedRange, type Token, Tokenizer } from \"./tokenizer.js\";\nimport type { Anchor, Cursor, Edit } from \"./types.js\";\n\nfunction validateRangeOrder(range: ParsedRange, lineNum: number): void {\n\tif (range.end.line < range.start.line) {\n\t\tthrow new Error(`line ${lineNum}: range ${range.start.line}..${range.end.line} ends before it starts.`);\n\t}\n}\n\nfunction expandRange(range: ParsedRange): Anchor[] {\n\tconst anchors: Anchor[] = [];\n\tfor (let line = range.start.line; line <= range.end.line; line++) anchors.push({ line });\n\treturn anchors;\n}\n\nfunction isSkippableCommentLine(line: string): boolean {\n\treturn line.trimStart().startsWith(\"#\");\n}\n\n/**\n * Stripped remainder of a bare `N: <value>` row that is a lone quoted or\n * numeric literal (optionally comma-terminated) — the shape of a numeric-keyed\n * dict/YAML body rather than read-output paste.\n */\nconst BARE_LITERAL_VALUE_RE = /^\\s*(?:\"[^\"]*\"|'[^']*'|[-+]?\\d+(?:\\.\\d+)?)\\s*,?\\s*$/;\n\nfunction detectApplyPatchContamination(text: string, _hasPending: boolean): string | null {\n\tconst trimmed = text.trimStart();\n\tif (trimmed.length === 0) return null;\n\tif (\n\t\ttrimmed.startsWith(\"*** Update File:\") ||\n\t\ttrimmed.startsWith(\"*** Add File:\") ||\n\t\ttrimmed.startsWith(\"*** Delete File:\") ||\n\t\ttrimmed.startsWith(\"*** Move to:\")\n\t) {\n\t\tconst preview = trimmed.length > 48 ? `${trimmed.slice(0, 48)}…` : trimmed;\n\t\treturn (\n\t\t\t`apply_patch sentinel ${JSON.stringify(preview)} is not valid in hashline. ` +\n\t\t\t\"File sections start with `[path#HASH]` (no `Update File:` / `Add File:` keyword). \" +\n\t\t\t\"Use `replace N..M:`, `delete N..M`, or `insert before|after|head|tail:` ops.\"\n\t\t);\n\t}\n\tif (/^@@\\s+[-+]?\\d+,\\d+\\s+[-+]?\\d+,\\d+\\s+@@/.test(trimmed)) {\n\t\treturn (\n\t\t\t\"unified-diff hunk header (`@@ -N,M +N,M @@`) is not valid in hashline. \" +\n\t\t\t\"Use `replace N..M:`, `delete N..M`, or `insert before|after|head|tail:` ops.\"\n\t\t);\n\t}\n\tif (trimmed.startsWith(\"@@\")) {\n\t\tconst preview = trimmed.length > 48 ? `${trimmed.slice(0, 48)}…` : trimmed;\n\t\treturn (\n\t\t\t`\\`@@\\`-bracketed hunk header ${JSON.stringify(preview)} is not valid in hashline. ` +\n\t\t\t\"Drop the `@@ ... @@` brackets and write a verb header such as `replace N..M:`.\"\n\t\t);\n\t}\n\tif (/^delete\\s+[1-9]\\d*(?:\\s*(?:\\.\\.|-|…|\\s)\\s*[1-9]\\d*)?\\s*:/.test(trimmed)) {\n\t\treturn \"`delete N..M` has no colon and no body. Remove the colon and body rows.\";\n\t}\n\tif (/^[1-9]\\d*\\s*$/.test(trimmed)) {\n\t\treturn `hunk headers need a verb. Use \\`replace ${trimmed}..${trimmed}:\\` to replace, or \\`delete ${trimmed}\\` to delete.`;\n\t}\n\tconst bareRange = /^([1-9]\\d*)\\s*[-. …]+\\s*([1-9]\\d*)\\s*:?$/.exec(trimmed);\n\tif (bareRange !== null) {\n\t\treturn (\n\t\t\t`bare range hunk header ${JSON.stringify(trimmed)} is not valid. ` +\n\t\t\t`Hunk headers need a verb: write \\`replace ${bareRange[1]}..${bareRange[2]}:\\` or \\`delete ${bareRange[1]}..${bareRange[2]}\\`.`\n\t\t);\n\t}\n\treturn null;\n}\n\ninterface PendingComment {\n\tlineNum: number;\n\ttext: string;\n}\n\ntype PayloadRow = { kind: \"literal\"; text: string; lineNum: number; bare?: boolean };\n\ninterface Pending {\n\ttarget: BlockTarget;\n\tlineNum: number;\n\tpayloads: PayloadRow[];\n\t/**\n\t * Blank rows seen after the body started. Interior blanks are committed to\n\t * the payload when the next non-blank row arrives; trailing blanks before\n\t * the next header/op are layout separators and are discarded on flush.\n\t */\n\tdeferredBlanks: PayloadRow[];\n}\n\nexport class Executor {\n\t#edits: Edit[] = [];\n\t#warnings: string[] = [];\n\t#editIndex = 0;\n\t#pending: Pending | undefined;\n\t#terminated = false;\n\t#skippableComments: PendingComment[] = [];\n\n\t#discardPendingSkippableComments(): void {\n\t\tthis.#skippableComments = [];\n\t}\n\n\t#consumePendingSkippableComments(): void {\n\t\tif (this.#skippableComments.length === 0) return;\n\t\tfor (const comment of this.#skippableComments) this.#handleRaw(comment.text, comment.lineNum);\n\t\tthis.#skippableComments = [];\n\t}\n\n\tfeed(token: Token): void {\n\t\tif (this.#terminated) return;\n\t\tswitch (token.kind) {\n\t\t\tcase \"envelope-begin\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\treturn;\n\t\t\tcase \"envelope-end\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#terminated = true;\n\t\t\t\treturn;\n\t\t\tcase \"abort\":\n\t\t\t\tthis.#terminated = true;\n\t\t\t\treturn;\n\t\t\tcase \"header\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#flushPending();\n\t\t\t\treturn;\n\t\t\tcase \"blank\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#handleBlank(\"\", token.lineNum);\n\t\t\t\treturn;\n\t\t\tcase \"payload-literal\":\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#handleLiteralPayload(token.text, token.lineNum);\n\t\t\t\treturn;\n\t\t\tcase \"raw\":\n\t\t\t\tif (this.#pending === undefined && isSkippableCommentLine(token.text)) {\n\t\t\t\t\tthis.#skippableComments.push({ text: token.text, lineNum: token.lineNum });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis.#consumePendingSkippableComments();\n\t\t\t\tthis.#handleRaw(token.text, token.lineNum);\n\t\t\t\treturn;\n\t\t\tcase \"op-block\":\n\t\t\t\tthis.#discardPendingSkippableComments();\n\t\t\t\tif (token.target.kind === \"replace\" || token.target.kind === \"delete\") {\n\t\t\t\t\tvalidateRangeOrder(token.target.range, token.lineNum);\n\t\t\t\t}\n\t\t\t\tthis.#flushPending();\n\t\t\t\tthis.#pending = { target: token.target, lineNum: token.lineNum, payloads: [], deferredBlanks: [] };\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\tend(): { edits: Edit[]; warnings: string[] } {\n\t\tthis.#consumePendingSkippableComments();\n\t\tthis.#flushPending();\n\t\tthis.#validateNoOverlappingDeletes();\n\t\treturn { edits: this.#edits, warnings: this.#warnings };\n\t}\n\n\tendStreaming(): { edits: Edit[]; warnings: string[] } {\n\t\tthis.#consumePendingSkippableComments();\n\t\tif (this.#pending && this.#pending.payloads.length > 0) this.#flushPending();\n\t\telse if (this.#pending?.target.kind === \"delete\" || this.#pending?.target.kind === \"delete_block\")\n\t\t\tthis.#flushPending();\n\t\telse this.#pending = undefined;\n\t\tthis.#validateNoOverlappingDeletes();\n\t\treturn { edits: this.#edits, warnings: this.#warnings };\n\t}\n\n\treset(): void {\n\t\tthis.#edits = [];\n\t\tthis.#warnings = [];\n\t\tthis.#editIndex = 0;\n\t\tthis.#pending = undefined;\n\t\tthis.#skippableComments = [];\n\t\tthis.#terminated = false;\n\t}\n\n\t#validateNoOverlappingDeletes(): void {\n\t\tconst sourceLinesByAnchor = new Map<number, number[]>();\n\t\tfor (const edit of this.#edits) {\n\t\t\tif (edit.kind !== \"delete\") continue;\n\t\t\tlet sourceLines = sourceLinesByAnchor.get(edit.anchor.line);\n\t\t\tif (sourceLines === undefined) {\n\t\t\t\tsourceLines = [];\n\t\t\t\tsourceLinesByAnchor.set(edit.anchor.line, sourceLines);\n\t\t\t}\n\t\t\tif (!sourceLines.includes(edit.lineNum)) sourceLines.push(edit.lineNum);\n\t\t}\n\t\tfor (const [anchorLine, sourceLines] of sourceLinesByAnchor) {\n\t\t\tif (sourceLines.length < 2) continue;\n\t\t\tconst [firstBlock, secondBlock] = [...sourceLines].sort((a, b) => a - b);\n\t\t\tthrow new Error(\n\t\t\t\t`line ${secondBlock}: anchor line ${anchorLine} is already targeted by another hunk on line ${firstBlock}. ` +\n\t\t\t\t\t\"Issue ONE hunk per range; payload is only the final desired content, never a before/after pair.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t#handleLiteralPayload(text: string, lineNum: number): void {\n\t\tconst pending = this.#pending;\n\t\tif (!pending) {\n\t\t\tthrow new Error(\n\t\t\t\t`line ${lineNum}: payload line has no preceding hunk header. ` +\n\t\t\t\t\t`Got ${JSON.stringify(`${HL_PAYLOAD_REPLACE}${text}`)}.`,\n\t\t\t);\n\t\t}\n\t\tif (pending.target.kind === \"delete\") throw new Error(`line ${lineNum}: ${DELETE_TAKES_NO_BODY}`);\n\t\tif (pending.target.kind === \"delete_block\") throw new Error(`line ${lineNum}: ${DELETE_BLOCK_TAKES_NO_BODY}`);\n\t\tthis.#commitDeferredBlanks(pending);\n\t\tpending.payloads.push({ kind: \"literal\", text, lineNum });\n\t}\n\n\t#handleRaw(text: string, lineNum: number): void {\n\t\tconst contamination = detectApplyPatchContamination(text, this.#pending !== undefined);\n\t\tif (contamination !== null) throw new Error(`line ${lineNum}: ${contamination}`);\n\t\tif (this.#pending) {\n\t\t\tif (text.trim().length === 0) {\n\t\t\t\tthis.#handleBlank(text, lineNum);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (this.#pending.target.kind === \"delete\") throw new Error(`line ${lineNum}: ${DELETE_TAKES_NO_BODY}`);\n\t\t\tif (this.#pending.target.kind === \"delete_block\")\n\t\t\t\tthrow new Error(`line ${lineNum}: ${DELETE_BLOCK_TAKES_NO_BODY}`);\n\t\t\tif (text.trimStart().charCodeAt(0) === 45 /* - */) throw new Error(`line ${lineNum}: ${MINUS_ROW_REJECTED}`);\n\t\t\tif (!this.#warnings.includes(BARE_BODY_AUTO_PIPED_WARNING)) this.#warnings.push(BARE_BODY_AUTO_PIPED_WARNING);\n\t\t\tthis.#commitDeferredBlanks(this.#pending);\n\t\t\t// Defer read-output line-number stripping to #flushPending: a bare\n\t\t\t// \"N:text\" row is only a copy-paste artifact from snapshot output\n\t\t\t// when *every* bare row in the hunk carries that prefix. Stripping a\n\t\t\t// row in isolation would corrupt a genuine body that merely starts\n\t\t\t// with \"digits:\" (YAML ports \"42:hello\", timestamps \"12:30\") when it\n\t\t\t// sits next to an unprefixed sibling. Rows with an explicit \"+\" go\n\t\t\t// through #handleLiteralPayload and are never bare, never stripped.\n\t\t\tthis.#pending.payloads.push({ kind: \"literal\", text, lineNum, bare: true });\n\t\t\treturn;\n\t\t}\n\t\tif (text.trim().length === 0) return;\n\t\tthrow new Error(\n\t\t\t`line ${lineNum}: payload line has no preceding hunk header. ` +\n\t\t\t\t`Use \\`replace N..M:\\`, \\`delete N..M\\`, or \\`insert before|after|head|tail:\\` above the body. Got ${JSON.stringify(text)}.`,\n\t\t);\n\t}\n\n\t/**\n\t * A blank row inside a hunk body is ambiguous: interior blanks are body\n\t * content (a bare-pasted body legitimately contains empty lines), while\n\t * blanks before the body starts or trailing into the next op are layout.\n\t * Defer them; {@link #commitDeferredBlanks} folds them in only when a later\n\t * non-blank row proves they were interior.\n\t */\n\t#handleBlank(text: string, lineNum: number): void {\n\t\tconst pending = this.#pending;\n\t\tif (!pending) return;\n\t\tif (pending.target.kind === \"delete\" || pending.target.kind === \"delete_block\") return;\n\t\tif (pending.payloads.length === 0) return;\n\t\tpending.deferredBlanks.push({ kind: \"literal\", text, lineNum, bare: true });\n\t}\n\n\t#commitDeferredBlanks(pending: Pending): void {\n\t\tif (pending.deferredBlanks.length === 0) return;\n\t\tif (!this.#warnings.includes(BARE_BODY_AUTO_PIPED_WARNING)) this.#warnings.push(BARE_BODY_AUTO_PIPED_WARNING);\n\t\tpending.payloads.push(...pending.deferredBlanks);\n\t\tpending.deferredBlanks = [];\n\t}\n\n\t/**\n\t * Strip a single read-output line-number prefix (`N:`) from every bare body\n\t * row, but only when *all* bare rows carry one. A uniform set of prefixes is\n\t * the signature of content pasted straight from `read`/`search` output; a\n\t * mixed set means the `N:` is genuine payload content and must stay. Rows\n\t * authored with an explicit `+` are not bare and are never touched.\n\t */\n\t#stripBarePrefixesIfUniform(payloads: PayloadRow[]): void {\n\t\tlet sawBare = false;\n\t\tlet allLiteralValues = true;\n\t\tfor (const row of payloads) {\n\t\t\tif (!row.bare || row.text.trim().length === 0) continue;\n\t\t\tsawBare = true;\n\t\t\tconst stripped = stripOneLeadingHashlinePrefix(row.text);\n\t\t\tif (stripped === row.text) return;\n\t\t\tallLiteralValues &&= BARE_LITERAL_VALUE_RE.test(stripped);\n\t\t}\n\t\tif (!sawBare) return;\n\t\t// A body where every stripped remainder is a lone quoted/numeric literal\n\t\t// (optionally comma-terminated) is the shape of a numeric-keyed dict or\n\t\t// YAML mapping (`1: \"one\",`), not read-output paste; stripping the \"N:\"\n\t\t// keys would mangle every line. Leave such bodies untouched.\n\t\tif (allLiteralValues) return;\n\t\tfor (const row of payloads) {\n\t\t\tif (row.bare && row.text.trim().length > 0) row.text = stripOneLeadingHashlinePrefix(row.text);\n\t\t}\n\t}\n\n\t#pushInsert(cursor: Cursor, text: string, lineNum: number, mode?: \"replacement\"): void {\n\t\tthis.#edits.push({\n\t\t\tkind: \"insert\",\n\t\t\tcursor: cloneCursor(cursor),\n\t\t\ttext,\n\t\t\tlineNum,\n\t\t\tindex: this.#editIndex++,\n\t\t\t...(mode === undefined ? {} : { mode }),\n\t\t});\n\t}\n\n\t#pushDelete(anchor: Anchor, lineNum: number): void {\n\t\tthis.#edits.push({ kind: \"delete\", anchor: { ...anchor }, lineNum, index: this.#editIndex++ });\n\t}\n\n\t#pushBlock(anchor: Anchor, payloads: readonly PayloadRow[], lineNum: number, mode?: \"insert_after\"): void {\n\t\tthis.#edits.push({\n\t\t\tkind: \"block\",\n\t\t\tanchor: { ...anchor },\n\t\t\tpayloads: payloads.map(payload => payload.text),\n\t\t\t...(mode === undefined ? {} : { mode }),\n\t\t\tlineNum,\n\t\t\tindex: this.#editIndex++,\n\t\t});\n\t}\n\n\t#emitPayloadRows(cursor: Cursor, payloads: readonly PayloadRow[], lineNum: number, mode?: \"replacement\"): void {\n\t\tfor (const payload of payloads) this.#pushInsert(cursor, payload.text, lineNum, mode);\n\t}\n\n\t#flushPending(): void {\n\t\tconst pending = this.#pending;\n\t\tif (!pending) return;\n\t\tconst { target, lineNum, payloads } = pending;\n\t\tthis.#stripBarePrefixesIfUniform(payloads);\n\t\tthis.#pending = undefined;\n\t\tif (target.kind === \"delete\") {\n\t\t\tfor (const anchor of expandRange(target.range)) this.#pushDelete(anchor, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"delete_block\") {\n\t\t\t// A block edit with no payloads resolves to a pure block deletion.\n\t\t\tthis.#pushBlock(target.anchor, [], lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"block\") {\n\t\t\tif (payloads.length === 0) throw new Error(`line ${lineNum}: ${EMPTY_BLOCK}`);\n\t\t\tthis.#pushBlock(target.anchor, payloads, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"insert_after_block\") {\n\t\t\tif (payloads.length === 0) throw new Error(`line ${lineNum}: ${EMPTY_INSERT}`);\n\t\t\tthis.#pushBlock(target.anchor, payloads, lineNum, \"insert_after\");\n\t\t\treturn;\n\t\t}\n\t\tif (payloads.length === 0) {\n\t\t\tif (target.kind === \"replace\") {\n\t\t\t\tfor (const anchor of expandRange(target.range)) this.#pushDelete(anchor, lineNum);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new Error(`line ${lineNum}: ${EMPTY_INSERT}`);\n\t\t}\n\t\tif (target.kind === \"replace\") {\n\t\t\tconst cursor: Cursor = { kind: \"before_anchor\", anchor: { ...target.range.start } };\n\t\t\tthis.#emitPayloadRows(cursor, payloads, lineNum, \"replacement\");\n\t\t\tfor (const anchor of expandRange(target.range)) this.#pushDelete(anchor, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"insert_before\") {\n\t\t\tthis.#emitPayloadRows({ kind: \"before_anchor\", anchor: { ...target.anchor } }, payloads, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tif (target.kind === \"insert_after\") {\n\t\t\tthis.#emitPayloadRows({ kind: \"after_anchor\", anchor: { ...target.anchor } }, payloads, lineNum);\n\t\t\treturn;\n\t\t}\n\t\tconst cursor: Cursor = target.kind === \"bof\" ? { kind: \"bof\" } : { kind: \"eof\" };\n\t\tthis.#emitPayloadRows(cursor, payloads, lineNum);\n\t}\n}\n\nfunction drain(executor: Executor, tokenizer: Tokenizer): { edits: Edit[]; warnings: string[] } {\n\tfor (const token of tokenizer.end()) executor.feed(token);\n\treturn executor.end();\n}\n\nexport function parsePatch(diff: string): { edits: Edit[]; warnings: string[] } {\n\tconst tokenizer = new Tokenizer();\n\tconst executor = new Executor();\n\tfor (const token of tokenizer.feed(diff)) executor.feed(token);\n\treturn drain(executor, tokenizer);\n}\n\nexport function parsePatchStreaming(diff: string): { edits: Edit[]; warnings: string[] } {\n\tconst tokenizer = new Tokenizer();\n\tconst executor = new Executor();\n\tfor (const token of tokenizer.feed(diff)) executor.feed(token);\n\tfor (const token of tokenizer.end()) executor.feed(token);\n\treturn executor.endStreaming();\n}\n"]}