@nghyane/arcane 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (738) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +12 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +109 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/custom-tools/todo/index.ts +206 -0
  7. package/examples/extensions/README.md +143 -0
  8. package/examples/extensions/api-demo.ts +89 -0
  9. package/examples/extensions/chalk-logger.ts +25 -0
  10. package/examples/extensions/hello.ts +32 -0
  11. package/examples/extensions/pirate.ts +43 -0
  12. package/examples/extensions/plan-mode.ts +550 -0
  13. package/examples/extensions/reload-runtime.ts +37 -0
  14. package/examples/extensions/todo.ts +296 -0
  15. package/examples/extensions/tools.ts +144 -0
  16. package/examples/extensions/with-deps/index.ts +35 -0
  17. package/examples/extensions/with-deps/package-lock.json +31 -0
  18. package/examples/extensions/with-deps/package.json +16 -0
  19. package/examples/hooks/README.md +56 -0
  20. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  21. package/examples/hooks/confirm-destructive.ts +58 -0
  22. package/examples/hooks/custom-compaction.ts +116 -0
  23. package/examples/hooks/dirty-repo-guard.ts +51 -0
  24. package/examples/hooks/file-trigger.ts +40 -0
  25. package/examples/hooks/git-checkpoint.ts +52 -0
  26. package/examples/hooks/handoff.ts +150 -0
  27. package/examples/hooks/permission-gate.ts +33 -0
  28. package/examples/hooks/protected-paths.ts +29 -0
  29. package/examples/hooks/qna.ts +119 -0
  30. package/examples/hooks/status-line.ts +39 -0
  31. package/examples/sdk/01-minimal.ts +21 -0
  32. package/examples/sdk/02-custom-model.ts +49 -0
  33. package/examples/sdk/03-custom-prompt.ts +43 -0
  34. package/examples/sdk/04-skills.ts +43 -0
  35. package/examples/sdk/06-extensions.ts +80 -0
  36. package/examples/sdk/06-hooks.ts +61 -0
  37. package/examples/sdk/07-context-files.ts +35 -0
  38. package/examples/sdk/08-prompt-templates.ts +36 -0
  39. package/examples/sdk/08-slash-commands.ts +41 -0
  40. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  41. package/examples/sdk/11-sessions.ts +47 -0
  42. package/examples/sdk/README.md +150 -0
  43. package/package.json +464 -0
  44. package/scripts/format-prompts.ts +184 -0
  45. package/scripts/generate-docs-index.ts +40 -0
  46. package/scripts/generate-template.ts +32 -0
  47. package/src/bun-imports.d.ts +22 -0
  48. package/src/capability/context-file.ts +39 -0
  49. package/src/capability/extension-module.ts +33 -0
  50. package/src/capability/extension.ts +47 -0
  51. package/src/capability/fs.ts +89 -0
  52. package/src/capability/hook.ts +39 -0
  53. package/src/capability/index.ts +432 -0
  54. package/src/capability/instruction.ts +36 -0
  55. package/src/capability/mcp.ts +60 -0
  56. package/src/capability/prompt.ts +34 -0
  57. package/src/capability/rule.ts +223 -0
  58. package/src/capability/settings.ts +34 -0
  59. package/src/capability/skill.ts +48 -0
  60. package/src/capability/slash-command.ts +39 -0
  61. package/src/capability/ssh.ts +41 -0
  62. package/src/capability/system-prompt.ts +34 -0
  63. package/src/capability/tool.ts +37 -0
  64. package/src/capability/types.ts +156 -0
  65. package/src/cli/args.ts +259 -0
  66. package/src/cli/config-cli.ts +357 -0
  67. package/src/cli/file-processor.ts +124 -0
  68. package/src/cli/grep-cli.ts +152 -0
  69. package/src/cli/jupyter-cli.ts +106 -0
  70. package/src/cli/list-models.ts +103 -0
  71. package/src/cli/plugin-cli.ts +661 -0
  72. package/src/cli/session-picker.ts +42 -0
  73. package/src/cli/setup-cli.ts +376 -0
  74. package/src/cli/shell-cli.ts +174 -0
  75. package/src/cli/ssh-cli.ts +179 -0
  76. package/src/cli/stats-cli.ts +197 -0
  77. package/src/cli/update-cli.ts +286 -0
  78. package/src/cli/web-search-cli.ts +143 -0
  79. package/src/cli.ts +65 -0
  80. package/src/commands/commit.ts +36 -0
  81. package/src/commands/config.ts +51 -0
  82. package/src/commands/grep.ts +41 -0
  83. package/src/commands/jupyter.ts +32 -0
  84. package/src/commands/launch.ts +139 -0
  85. package/src/commands/plugin.ts +70 -0
  86. package/src/commands/setup.ts +42 -0
  87. package/src/commands/shell.ts +29 -0
  88. package/src/commands/ssh.ts +60 -0
  89. package/src/commands/stats.ts +29 -0
  90. package/src/commands/update.ts +21 -0
  91. package/src/commands/web-search.ts +42 -0
  92. package/src/commit/agentic/agent.ts +311 -0
  93. package/src/commit/agentic/fallback.ts +96 -0
  94. package/src/commit/agentic/index.ts +359 -0
  95. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  96. package/src/commit/agentic/prompts/session-user.md +25 -0
  97. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  98. package/src/commit/agentic/prompts/system.md +38 -0
  99. package/src/commit/agentic/state.ts +69 -0
  100. package/src/commit/agentic/tools/analyze-file.ts +118 -0
  101. package/src/commit/agentic/tools/git-file-diff.ts +194 -0
  102. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  103. package/src/commit/agentic/tools/git-overview.ts +84 -0
  104. package/src/commit/agentic/tools/index.ts +56 -0
  105. package/src/commit/agentic/tools/propose-changelog.ts +128 -0
  106. package/src/commit/agentic/tools/propose-commit.ts +154 -0
  107. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  108. package/src/commit/agentic/tools/split-commit.ts +280 -0
  109. package/src/commit/agentic/topo-sort.ts +44 -0
  110. package/src/commit/agentic/trivial.ts +51 -0
  111. package/src/commit/agentic/validation.ts +200 -0
  112. package/src/commit/analysis/conventional.ts +165 -0
  113. package/src/commit/analysis/index.ts +4 -0
  114. package/src/commit/analysis/scope.ts +242 -0
  115. package/src/commit/analysis/summary.ts +112 -0
  116. package/src/commit/analysis/validation.ts +66 -0
  117. package/src/commit/changelog/detect.ts +37 -0
  118. package/src/commit/changelog/generate.ts +110 -0
  119. package/src/commit/changelog/index.ts +234 -0
  120. package/src/commit/changelog/parse.ts +44 -0
  121. package/src/commit/cli.ts +93 -0
  122. package/src/commit/git/diff.ts +148 -0
  123. package/src/commit/git/errors.ts +9 -0
  124. package/src/commit/git/index.ts +211 -0
  125. package/src/commit/git/operations.ts +54 -0
  126. package/src/commit/index.ts +5 -0
  127. package/src/commit/map-reduce/index.ts +64 -0
  128. package/src/commit/map-reduce/map-phase.ts +178 -0
  129. package/src/commit/map-reduce/reduce-phase.ts +145 -0
  130. package/src/commit/map-reduce/utils.ts +9 -0
  131. package/src/commit/message.ts +11 -0
  132. package/src/commit/model-selection.ts +69 -0
  133. package/src/commit/pipeline.ts +243 -0
  134. package/src/commit/prompts/analysis-system.md +148 -0
  135. package/src/commit/prompts/analysis-user.md +38 -0
  136. package/src/commit/prompts/changelog-system.md +50 -0
  137. package/src/commit/prompts/changelog-user.md +18 -0
  138. package/src/commit/prompts/file-observer-system.md +24 -0
  139. package/src/commit/prompts/file-observer-user.md +8 -0
  140. package/src/commit/prompts/reduce-system.md +50 -0
  141. package/src/commit/prompts/reduce-user.md +17 -0
  142. package/src/commit/prompts/summary-retry.md +3 -0
  143. package/src/commit/prompts/summary-system.md +38 -0
  144. package/src/commit/prompts/summary-user.md +13 -0
  145. package/src/commit/prompts/types-description.md +2 -0
  146. package/src/commit/types.ts +109 -0
  147. package/src/commit/utils/exclusions.ts +42 -0
  148. package/src/config/file-lock.ts +121 -0
  149. package/src/config/keybindings.ts +280 -0
  150. package/src/config/model-registry.ts +1140 -0
  151. package/src/config/model-resolver.ts +812 -0
  152. package/src/config/prompt-templates.ts +526 -0
  153. package/src/config/resolve-config-value.ts +92 -0
  154. package/src/config/settings-schema.ts +1236 -0
  155. package/src/config/settings.ts +706 -0
  156. package/src/config.ts +414 -0
  157. package/src/cursor.ts +239 -0
  158. package/src/debug/index.ts +431 -0
  159. package/src/debug/log-formatting.ts +60 -0
  160. package/src/debug/log-viewer.ts +903 -0
  161. package/src/debug/profiler.ts +158 -0
  162. package/src/debug/report-bundle.ts +366 -0
  163. package/src/debug/system-info.ts +112 -0
  164. package/src/discovery/agents-md.ts +68 -0
  165. package/src/discovery/agents.ts +199 -0
  166. package/src/discovery/builtin.ts +815 -0
  167. package/src/discovery/claude-plugins.ts +205 -0
  168. package/src/discovery/claude.ts +506 -0
  169. package/src/discovery/cline.ts +83 -0
  170. package/src/discovery/codex.ts +532 -0
  171. package/src/discovery/cursor.ts +218 -0
  172. package/src/discovery/gemini.ts +395 -0
  173. package/src/discovery/github.ts +117 -0
  174. package/src/discovery/helpers.ts +698 -0
  175. package/src/discovery/index.ts +89 -0
  176. package/src/discovery/mcp-json.ts +156 -0
  177. package/src/discovery/opencode.ts +394 -0
  178. package/src/discovery/ssh.ts +160 -0
  179. package/src/discovery/vscode.ts +103 -0
  180. package/src/discovery/windsurf.ts +145 -0
  181. package/src/exa/company.ts +57 -0
  182. package/src/exa/index.ts +62 -0
  183. package/src/exa/linkedin.ts +57 -0
  184. package/src/exa/mcp-client.ts +289 -0
  185. package/src/exa/render.ts +244 -0
  186. package/src/exa/researcher.ts +89 -0
  187. package/src/exa/search.ts +330 -0
  188. package/src/exa/types.ts +166 -0
  189. package/src/exa/websets.ts +247 -0
  190. package/src/exec/bash-executor.ts +184 -0
  191. package/src/exec/exec.ts +53 -0
  192. package/src/export/custom-share.ts +65 -0
  193. package/src/export/html/index.ts +162 -0
  194. package/src/export/html/template.css +889 -0
  195. package/src/export/html/template.generated.ts +2 -0
  196. package/src/export/html/template.html +45 -0
  197. package/src/export/html/template.js +1329 -0
  198. package/src/export/html/template.macro.ts +24 -0
  199. package/src/export/html/vendor/highlight.min.js +1213 -0
  200. package/src/export/html/vendor/marked.min.js +6 -0
  201. package/src/export/ttsr.ts +434 -0
  202. package/src/extensibility/custom-commands/bundled/review/index.ts +433 -0
  203. package/src/extensibility/custom-commands/index.ts +15 -0
  204. package/src/extensibility/custom-commands/loader.ts +231 -0
  205. package/src/extensibility/custom-commands/types.ts +111 -0
  206. package/src/extensibility/custom-tools/index.ts +22 -0
  207. package/src/extensibility/custom-tools/loader.ts +235 -0
  208. package/src/extensibility/custom-tools/types.ts +226 -0
  209. package/src/extensibility/custom-tools/wrapper.ts +45 -0
  210. package/src/extensibility/extensions/index.ts +136 -0
  211. package/src/extensibility/extensions/loader.ts +520 -0
  212. package/src/extensibility/extensions/runner.ts +774 -0
  213. package/src/extensibility/extensions/types.ts +1293 -0
  214. package/src/extensibility/extensions/wrapper.ts +188 -0
  215. package/src/extensibility/hooks/index.ts +16 -0
  216. package/src/extensibility/hooks/loader.ts +273 -0
  217. package/src/extensibility/hooks/runner.ts +441 -0
  218. package/src/extensibility/hooks/tool-wrapper.ts +106 -0
  219. package/src/extensibility/hooks/types.ts +817 -0
  220. package/src/extensibility/plugins/doctor.ts +65 -0
  221. package/src/extensibility/plugins/git-url.ts +281 -0
  222. package/src/extensibility/plugins/index.ts +33 -0
  223. package/src/extensibility/plugins/installer.ts +192 -0
  224. package/src/extensibility/plugins/loader.ts +338 -0
  225. package/src/extensibility/plugins/manager.ts +716 -0
  226. package/src/extensibility/plugins/parser.ts +105 -0
  227. package/src/extensibility/plugins/types.ts +190 -0
  228. package/src/extensibility/skills.ts +385 -0
  229. package/src/extensibility/slash-commands.ts +287 -0
  230. package/src/extensibility/tool-proxy.ts +25 -0
  231. package/src/index.ts +275 -0
  232. package/src/internal-urls/agent-protocol.ts +136 -0
  233. package/src/internal-urls/artifact-protocol.ts +97 -0
  234. package/src/internal-urls/docs-index.generated.ts +54 -0
  235. package/src/internal-urls/docs-protocol.ts +84 -0
  236. package/src/internal-urls/index.ts +31 -0
  237. package/src/internal-urls/json-query.ts +126 -0
  238. package/src/internal-urls/memory-protocol.ts +133 -0
  239. package/src/internal-urls/router.ts +70 -0
  240. package/src/internal-urls/rule-protocol.ts +55 -0
  241. package/src/internal-urls/skill-protocol.ts +111 -0
  242. package/src/internal-urls/types.ts +52 -0
  243. package/src/ipy/executor.ts +556 -0
  244. package/src/ipy/gateway-coordinator.ts +426 -0
  245. package/src/ipy/kernel.ts +892 -0
  246. package/src/ipy/modules.ts +109 -0
  247. package/src/ipy/prelude.py +831 -0
  248. package/src/ipy/prelude.ts +3 -0
  249. package/src/ipy/runtime.ts +222 -0
  250. package/src/lsp/client.ts +867 -0
  251. package/src/lsp/clients/biome-client.ts +202 -0
  252. package/src/lsp/clients/index.ts +50 -0
  253. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  254. package/src/lsp/clients/swiftlint-client.ts +120 -0
  255. package/src/lsp/config.ts +397 -0
  256. package/src/lsp/defaults.json +464 -0
  257. package/src/lsp/edits.ts +109 -0
  258. package/src/lsp/index.ts +1268 -0
  259. package/src/lsp/lspmux.ts +250 -0
  260. package/src/lsp/render.ts +689 -0
  261. package/src/lsp/types.ts +414 -0
  262. package/src/lsp/utils.ts +549 -0
  263. package/src/main.ts +773 -0
  264. package/src/mcp/client.ts +239 -0
  265. package/src/mcp/config-writer.ts +215 -0
  266. package/src/mcp/config.ts +363 -0
  267. package/src/mcp/index.ts +55 -0
  268. package/src/mcp/json-rpc.ts +84 -0
  269. package/src/mcp/loader.ts +124 -0
  270. package/src/mcp/manager.ts +490 -0
  271. package/src/mcp/oauth-discovery.ts +274 -0
  272. package/src/mcp/oauth-flow.ts +229 -0
  273. package/src/mcp/render.ts +123 -0
  274. package/src/mcp/tool-bridge.ts +372 -0
  275. package/src/mcp/tool-cache.ts +121 -0
  276. package/src/mcp/transports/http.ts +332 -0
  277. package/src/mcp/transports/index.ts +6 -0
  278. package/src/mcp/transports/stdio.ts +281 -0
  279. package/src/mcp/types.ts +248 -0
  280. package/src/memories/index.ts +1099 -0
  281. package/src/memories/storage.ts +563 -0
  282. package/src/modes/components/agent-dashboard.ts +1130 -0
  283. package/src/modes/components/assistant-message.ts +144 -0
  284. package/src/modes/components/bash-execution.ts +218 -0
  285. package/src/modes/components/bordered-loader.ts +41 -0
  286. package/src/modes/components/branch-summary-message.ts +45 -0
  287. package/src/modes/components/codemode-group.ts +369 -0
  288. package/src/modes/components/compaction-summary-message.ts +51 -0
  289. package/src/modes/components/countdown-timer.ts +46 -0
  290. package/src/modes/components/custom-editor.ts +181 -0
  291. package/src/modes/components/custom-message.ts +91 -0
  292. package/src/modes/components/diff.ts +186 -0
  293. package/src/modes/components/dynamic-border.ts +25 -0
  294. package/src/modes/components/extensions/extension-dashboard.ts +325 -0
  295. package/src/modes/components/extensions/extension-list.ts +484 -0
  296. package/src/modes/components/extensions/index.ts +9 -0
  297. package/src/modes/components/extensions/inspector-panel.ts +321 -0
  298. package/src/modes/components/extensions/state-manager.ts +586 -0
  299. package/src/modes/components/extensions/types.ts +191 -0
  300. package/src/modes/components/footer.ts +315 -0
  301. package/src/modes/components/history-search.ts +157 -0
  302. package/src/modes/components/hook-editor.ts +101 -0
  303. package/src/modes/components/hook-input.ts +72 -0
  304. package/src/modes/components/hook-message.ts +100 -0
  305. package/src/modes/components/hook-selector.ts +155 -0
  306. package/src/modes/components/index.ts +41 -0
  307. package/src/modes/components/keybinding-hints.ts +65 -0
  308. package/src/modes/components/login-dialog.ts +164 -0
  309. package/src/modes/components/mcp-add-wizard.ts +1295 -0
  310. package/src/modes/components/model-selector.ts +625 -0
  311. package/src/modes/components/oauth-selector.ts +210 -0
  312. package/src/modes/components/plugin-settings.ts +477 -0
  313. package/src/modes/components/python-execution.ts +196 -0
  314. package/src/modes/components/queue-mode-selector.ts +56 -0
  315. package/src/modes/components/read-tool-group.ts +119 -0
  316. package/src/modes/components/session-selector.ts +242 -0
  317. package/src/modes/components/settings-defs.ts +340 -0
  318. package/src/modes/components/settings-selector.ts +529 -0
  319. package/src/modes/components/show-images-selector.ts +45 -0
  320. package/src/modes/components/skill-message.ts +90 -0
  321. package/src/modes/components/status-line/index.ts +4 -0
  322. package/src/modes/components/status-line/presets.ts +94 -0
  323. package/src/modes/components/status-line/segments.ts +352 -0
  324. package/src/modes/components/status-line/separators.ts +55 -0
  325. package/src/modes/components/status-line/types.ts +75 -0
  326. package/src/modes/components/status-line-segment-editor.ts +354 -0
  327. package/src/modes/components/status-line.ts +421 -0
  328. package/src/modes/components/theme-selector.ts +63 -0
  329. package/src/modes/components/thinking-selector.ts +64 -0
  330. package/src/modes/components/todo-display.ts +115 -0
  331. package/src/modes/components/todo-reminder.ts +40 -0
  332. package/src/modes/components/tool-execution.ts +703 -0
  333. package/src/modes/components/tree-selector.ts +904 -0
  334. package/src/modes/components/ttsr-notification.ts +80 -0
  335. package/src/modes/components/user-message-selector.ts +146 -0
  336. package/src/modes/components/user-message.ts +22 -0
  337. package/src/modes/components/visual-truncate.ts +63 -0
  338. package/src/modes/components/welcome.ts +247 -0
  339. package/src/modes/controllers/command-controller.ts +1120 -0
  340. package/src/modes/controllers/event-controller.ts +479 -0
  341. package/src/modes/controllers/extension-ui-controller.ts +778 -0
  342. package/src/modes/controllers/input-controller.ts +671 -0
  343. package/src/modes/controllers/mcp-command-controller.ts +1315 -0
  344. package/src/modes/controllers/selector-controller.ts +712 -0
  345. package/src/modes/controllers/ssh-command-controller.ts +452 -0
  346. package/src/modes/index.ts +15 -0
  347. package/src/modes/interactive-mode.ts +1027 -0
  348. package/src/modes/print-mode.ts +191 -0
  349. package/src/modes/rpc/rpc-client.ts +583 -0
  350. package/src/modes/rpc/rpc-mode.ts +700 -0
  351. package/src/modes/rpc/rpc-types.ts +236 -0
  352. package/src/modes/theme/dark.json +95 -0
  353. package/src/modes/theme/defaults/alabaster.json +93 -0
  354. package/src/modes/theme/defaults/amethyst.json +96 -0
  355. package/src/modes/theme/defaults/anthracite.json +93 -0
  356. package/src/modes/theme/defaults/basalt.json +91 -0
  357. package/src/modes/theme/defaults/birch.json +95 -0
  358. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  359. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  360. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  361. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  362. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  363. package/src/modes/theme/defaults/dark-copper.json +95 -0
  364. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  365. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  366. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  367. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  368. package/src/modes/theme/defaults/dark-ember.json +95 -0
  369. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  370. package/src/modes/theme/defaults/dark-forest.json +96 -0
  371. package/src/modes/theme/defaults/dark-github.json +105 -0
  372. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  373. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  374. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  375. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  376. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  377. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  378. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  379. package/src/modes/theme/defaults/dark-nord.json +97 -0
  380. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  381. package/src/modes/theme/defaults/dark-one.json +100 -0
  382. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  383. package/src/modes/theme/defaults/dark-reef.json +91 -0
  384. package/src/modes/theme/defaults/dark-retro.json +92 -0
  385. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  386. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  387. package/src/modes/theme/defaults/dark-slate.json +95 -0
  388. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  389. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  390. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  391. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  392. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  393. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  394. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  395. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  396. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  397. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  398. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  399. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  400. package/src/modes/theme/defaults/graphite.json +92 -0
  401. package/src/modes/theme/defaults/index.ts +195 -0
  402. package/src/modes/theme/defaults/light-arctic.json +107 -0
  403. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  404. package/src/modes/theme/defaults/light-canyon.json +91 -0
  405. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  406. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  407. package/src/modes/theme/defaults/light-coral.json +95 -0
  408. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  409. package/src/modes/theme/defaults/light-dawn.json +90 -0
  410. package/src/modes/theme/defaults/light-dunes.json +91 -0
  411. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  412. package/src/modes/theme/defaults/light-forest.json +100 -0
  413. package/src/modes/theme/defaults/light-frost.json +95 -0
  414. package/src/modes/theme/defaults/light-github.json +115 -0
  415. package/src/modes/theme/defaults/light-glacier.json +91 -0
  416. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  417. package/src/modes/theme/defaults/light-haze.json +90 -0
  418. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  419. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  420. package/src/modes/theme/defaults/light-lavender.json +95 -0
  421. package/src/modes/theme/defaults/light-meadow.json +91 -0
  422. package/src/modes/theme/defaults/light-mint.json +95 -0
  423. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  424. package/src/modes/theme/defaults/light-ocean.json +99 -0
  425. package/src/modes/theme/defaults/light-one.json +99 -0
  426. package/src/modes/theme/defaults/light-opal.json +91 -0
  427. package/src/modes/theme/defaults/light-orchard.json +91 -0
  428. package/src/modes/theme/defaults/light-paper.json +95 -0
  429. package/src/modes/theme/defaults/light-prism.json +90 -0
  430. package/src/modes/theme/defaults/light-retro.json +98 -0
  431. package/src/modes/theme/defaults/light-sand.json +95 -0
  432. package/src/modes/theme/defaults/light-savanna.json +91 -0
  433. package/src/modes/theme/defaults/light-solarized.json +102 -0
  434. package/src/modes/theme/defaults/light-soleil.json +90 -0
  435. package/src/modes/theme/defaults/light-sunset.json +99 -0
  436. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  437. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  438. package/src/modes/theme/defaults/light-wetland.json +91 -0
  439. package/src/modes/theme/defaults/light-zenith.json +89 -0
  440. package/src/modes/theme/defaults/limestone.json +94 -0
  441. package/src/modes/theme/defaults/mahogany.json +97 -0
  442. package/src/modes/theme/defaults/marble.json +93 -0
  443. package/src/modes/theme/defaults/obsidian.json +91 -0
  444. package/src/modes/theme/defaults/onyx.json +91 -0
  445. package/src/modes/theme/defaults/pearl.json +93 -0
  446. package/src/modes/theme/defaults/porcelain.json +91 -0
  447. package/src/modes/theme/defaults/quartz.json +96 -0
  448. package/src/modes/theme/defaults/sandstone.json +95 -0
  449. package/src/modes/theme/defaults/titanium.json +90 -0
  450. package/src/modes/theme/light.json +93 -0
  451. package/src/modes/theme/mermaid-cache.ts +111 -0
  452. package/src/modes/theme/theme-schema.json +429 -0
  453. package/src/modes/theme/theme.ts +2333 -0
  454. package/src/modes/types.ts +216 -0
  455. package/src/modes/utils/ui-helpers.ts +529 -0
  456. package/src/patch/applicator.ts +1482 -0
  457. package/src/patch/diff.ts +425 -0
  458. package/src/patch/fuzzy.ts +784 -0
  459. package/src/patch/hashline.ts +972 -0
  460. package/src/patch/index.ts +964 -0
  461. package/src/patch/normalize.ts +397 -0
  462. package/src/patch/normative.ts +72 -0
  463. package/src/patch/parser.ts +532 -0
  464. package/src/patch/shared.ts +400 -0
  465. package/src/patch/types.ts +292 -0
  466. package/src/priority.json +35 -0
  467. package/src/prompts/agents/explore.md +48 -0
  468. package/src/prompts/agents/frontmatter.md +9 -0
  469. package/src/prompts/agents/init.md +36 -0
  470. package/src/prompts/agents/librarian.md +53 -0
  471. package/src/prompts/agents/oracle.md +51 -0
  472. package/src/prompts/agents/reviewer.md +70 -0
  473. package/src/prompts/agents/task.md +14 -0
  474. package/src/prompts/compaction/branch-summary-context.md +5 -0
  475. package/src/prompts/compaction/branch-summary-preamble.md +2 -0
  476. package/src/prompts/compaction/branch-summary.md +30 -0
  477. package/src/prompts/compaction/compaction-short-summary.md +9 -0
  478. package/src/prompts/compaction/compaction-summary-context.md +5 -0
  479. package/src/prompts/compaction/compaction-summary.md +38 -0
  480. package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
  481. package/src/prompts/compaction/compaction-update-summary.md +45 -0
  482. package/src/prompts/memories/consolidation.md +30 -0
  483. package/src/prompts/memories/read_path.md +11 -0
  484. package/src/prompts/memories/stage_one_input.md +6 -0
  485. package/src/prompts/memories/stage_one_system.md +21 -0
  486. package/src/prompts/review-request.md +64 -0
  487. package/src/prompts/system/agent-creation-architect.md +65 -0
  488. package/src/prompts/system/agent-creation-user.md +6 -0
  489. package/src/prompts/system/custom-system-prompt.md +68 -0
  490. package/src/prompts/system/file-operations.md +10 -0
  491. package/src/prompts/system/subagent-submit-reminder.md +11 -0
  492. package/src/prompts/system/subagent-system-prompt.md +31 -0
  493. package/src/prompts/system/subagent-user-prompt.md +8 -0
  494. package/src/prompts/system/summarization-system.md +3 -0
  495. package/src/prompts/system/system-prompt.md +300 -0
  496. package/src/prompts/system/title-system.md +2 -0
  497. package/src/prompts/system/ttsr-interrupt.md +7 -0
  498. package/src/prompts/system/web-search.md +28 -0
  499. package/src/prompts/tools/ask.md +44 -0
  500. package/src/prompts/tools/bash.md +24 -0
  501. package/src/prompts/tools/browser.md +33 -0
  502. package/src/prompts/tools/calculator.md +12 -0
  503. package/src/prompts/tools/explore.md +29 -0
  504. package/src/prompts/tools/fetch.md +16 -0
  505. package/src/prompts/tools/find.md +18 -0
  506. package/src/prompts/tools/gemini-image.md +23 -0
  507. package/src/prompts/tools/grep.md +28 -0
  508. package/src/prompts/tools/hashline.md +232 -0
  509. package/src/prompts/tools/librarian.md +24 -0
  510. package/src/prompts/tools/lsp.md +28 -0
  511. package/src/prompts/tools/oracle.md +26 -0
  512. package/src/prompts/tools/patch.md +74 -0
  513. package/src/prompts/tools/python.md +66 -0
  514. package/src/prompts/tools/read.md +36 -0
  515. package/src/prompts/tools/replace.md +38 -0
  516. package/src/prompts/tools/reviewer.md +41 -0
  517. package/src/prompts/tools/ssh.md +51 -0
  518. package/src/prompts/tools/task-summary.md +28 -0
  519. package/src/prompts/tools/task.md +275 -0
  520. package/src/prompts/tools/todo-write.md +65 -0
  521. package/src/prompts/tools/undo-edit.md +7 -0
  522. package/src/prompts/tools/web-search.md +19 -0
  523. package/src/prompts/tools/write.md +18 -0
  524. package/src/sdk.ts +1287 -0
  525. package/src/secrets/index.ts +116 -0
  526. package/src/secrets/obfuscator.ts +269 -0
  527. package/src/secrets/regex.ts +21 -0
  528. package/src/session/agent-session.ts +4669 -0
  529. package/src/session/agent-storage.ts +621 -0
  530. package/src/session/artifacts.ts +132 -0
  531. package/src/session/auth-storage.ts +1433 -0
  532. package/src/session/blob-store.ts +103 -0
  533. package/src/session/compaction/branch-summarization.ts +315 -0
  534. package/src/session/compaction/compaction.ts +864 -0
  535. package/src/session/compaction/index.ts +7 -0
  536. package/src/session/compaction/pruning.ts +91 -0
  537. package/src/session/compaction/utils.ts +171 -0
  538. package/src/session/history-storage.ts +170 -0
  539. package/src/session/messages.ts +317 -0
  540. package/src/session/session-manager.ts +2276 -0
  541. package/src/session/session-storage.ts +342 -0
  542. package/src/session/streaming-output.ts +565 -0
  543. package/src/slash-commands/builtin-registry.ts +439 -0
  544. package/src/ssh/config-writer.ts +183 -0
  545. package/src/ssh/connection-manager.ts +444 -0
  546. package/src/ssh/ssh-executor.ts +127 -0
  547. package/src/ssh/sshfs-mount.ts +135 -0
  548. package/src/stt/downloader.ts +71 -0
  549. package/src/stt/index.ts +3 -0
  550. package/src/stt/recorder.ts +351 -0
  551. package/src/stt/setup.ts +52 -0
  552. package/src/stt/stt-controller.ts +160 -0
  553. package/src/stt/transcribe.py +70 -0
  554. package/src/stt/transcriber.ts +91 -0
  555. package/src/system-prompt.ts +685 -0
  556. package/src/task/agents.ts +155 -0
  557. package/src/task/batch.ts +102 -0
  558. package/src/task/commands.ts +134 -0
  559. package/src/task/discovery.ts +126 -0
  560. package/src/task/executor.ts +908 -0
  561. package/src/task/index.ts +223 -0
  562. package/src/task/output-manager.ts +107 -0
  563. package/src/task/parallel.ts +84 -0
  564. package/src/task/render.ts +326 -0
  565. package/src/task/subprocess-tool-registry.ts +88 -0
  566. package/src/task/template.ts +32 -0
  567. package/src/task/types.ts +144 -0
  568. package/src/tools/ask.ts +523 -0
  569. package/src/tools/bash-interactive.ts +419 -0
  570. package/src/tools/bash-interceptor.ts +105 -0
  571. package/src/tools/bash-normalize.ts +107 -0
  572. package/src/tools/bash-skill-urls.ts +177 -0
  573. package/src/tools/bash.ts +347 -0
  574. package/src/tools/browser.ts +1374 -0
  575. package/src/tools/calculator.ts +537 -0
  576. package/src/tools/context.ts +39 -0
  577. package/src/tools/explore.ts +23 -0
  578. package/src/tools/fetch.ts +1091 -0
  579. package/src/tools/find.ts +540 -0
  580. package/src/tools/fs-cache-invalidation.ts +28 -0
  581. package/src/tools/gemini-image.ts +907 -0
  582. package/src/tools/grep.ts +489 -0
  583. package/src/tools/index.ts +337 -0
  584. package/src/tools/json-tree.ts +231 -0
  585. package/src/tools/jtd-to-json-schema.ts +247 -0
  586. package/src/tools/jtd-to-typescript.ts +198 -0
  587. package/src/tools/librarian.ts +33 -0
  588. package/src/tools/list-limit.ts +40 -0
  589. package/src/tools/notebook.ts +287 -0
  590. package/src/tools/oracle.ts +40 -0
  591. package/src/tools/output-meta.ts +459 -0
  592. package/src/tools/output-utils.ts +63 -0
  593. package/src/tools/path-utils.ts +116 -0
  594. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  595. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  596. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  597. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  598. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  599. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  600. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  601. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  602. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  603. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  604. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  605. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  606. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  607. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  608. package/src/tools/python.ts +1118 -0
  609. package/src/tools/read.ts +1193 -0
  610. package/src/tools/render-utils.ts +680 -0
  611. package/src/tools/renderers.ts +60 -0
  612. package/src/tools/reviewer-tool.ts +41 -0
  613. package/src/tools/ssh.ts +326 -0
  614. package/src/tools/subagent-tool.ts +169 -0
  615. package/src/tools/submit-result.ts +152 -0
  616. package/src/tools/todo-write.ts +255 -0
  617. package/src/tools/tool-errors.ts +92 -0
  618. package/src/tools/tool-result.ts +86 -0
  619. package/src/tools/undo-edit.ts +145 -0
  620. package/src/tools/undo-history.ts +22 -0
  621. package/src/tools/write.ts +274 -0
  622. package/src/tui/code-cell.ts +108 -0
  623. package/src/tui/file-list.ts +47 -0
  624. package/src/tui/index.ts +11 -0
  625. package/src/tui/output-block.ts +144 -0
  626. package/src/tui/status-line.ts +39 -0
  627. package/src/tui/tree-list.ts +53 -0
  628. package/src/tui/types.ts +16 -0
  629. package/src/tui/utils.ts +116 -0
  630. package/src/utils/changelog.ts +98 -0
  631. package/src/utils/event-bus.ts +33 -0
  632. package/src/utils/external-editor.ts +59 -0
  633. package/src/utils/file-display-mode.ts +36 -0
  634. package/src/utils/file-mentions.ts +384 -0
  635. package/src/utils/frontmatter.ts +101 -0
  636. package/src/utils/fuzzy.ts +108 -0
  637. package/src/utils/ignore-files.ts +119 -0
  638. package/src/utils/image-convert.ts +27 -0
  639. package/src/utils/image-resize.ts +236 -0
  640. package/src/utils/mime.ts +30 -0
  641. package/src/utils/open.ts +20 -0
  642. package/src/utils/shell-snapshot.ts +199 -0
  643. package/src/utils/timings.ts +26 -0
  644. package/src/utils/title-generator.ts +167 -0
  645. package/src/utils/tools-manager.ts +362 -0
  646. package/src/web/scrapers/artifacthub.ts +215 -0
  647. package/src/web/scrapers/arxiv.ts +88 -0
  648. package/src/web/scrapers/aur.ts +175 -0
  649. package/src/web/scrapers/biorxiv.ts +141 -0
  650. package/src/web/scrapers/bluesky.ts +284 -0
  651. package/src/web/scrapers/brew.ts +177 -0
  652. package/src/web/scrapers/cheatsh.ts +78 -0
  653. package/src/web/scrapers/chocolatey.ts +158 -0
  654. package/src/web/scrapers/choosealicense.ts +110 -0
  655. package/src/web/scrapers/cisa-kev.ts +100 -0
  656. package/src/web/scrapers/clojars.ts +180 -0
  657. package/src/web/scrapers/coingecko.ts +184 -0
  658. package/src/web/scrapers/crates-io.ts +128 -0
  659. package/src/web/scrapers/crossref.ts +149 -0
  660. package/src/web/scrapers/devto.ts +177 -0
  661. package/src/web/scrapers/discogs.ts +307 -0
  662. package/src/web/scrapers/discourse.ts +221 -0
  663. package/src/web/scrapers/dockerhub.ts +160 -0
  664. package/src/web/scrapers/fdroid.ts +158 -0
  665. package/src/web/scrapers/firefox-addons.ts +214 -0
  666. package/src/web/scrapers/flathub.ts +239 -0
  667. package/src/web/scrapers/github-gist.ts +68 -0
  668. package/src/web/scrapers/github.ts +490 -0
  669. package/src/web/scrapers/gitlab.ts +456 -0
  670. package/src/web/scrapers/go-pkg.ts +275 -0
  671. package/src/web/scrapers/hackage.ts +94 -0
  672. package/src/web/scrapers/hackernews.ts +208 -0
  673. package/src/web/scrapers/hex.ts +121 -0
  674. package/src/web/scrapers/huggingface.ts +385 -0
  675. package/src/web/scrapers/iacr.ts +86 -0
  676. package/src/web/scrapers/index.ts +249 -0
  677. package/src/web/scrapers/jetbrains-marketplace.ts +169 -0
  678. package/src/web/scrapers/lemmy.ts +220 -0
  679. package/src/web/scrapers/lobsters.ts +186 -0
  680. package/src/web/scrapers/mastodon.ts +310 -0
  681. package/src/web/scrapers/maven.ts +152 -0
  682. package/src/web/scrapers/mdn.ts +172 -0
  683. package/src/web/scrapers/metacpan.ts +253 -0
  684. package/src/web/scrapers/musicbrainz.ts +272 -0
  685. package/src/web/scrapers/npm.ts +114 -0
  686. package/src/web/scrapers/nuget.ts +205 -0
  687. package/src/web/scrapers/nvd.ts +243 -0
  688. package/src/web/scrapers/ollama.ts +265 -0
  689. package/src/web/scrapers/open-vsx.ts +119 -0
  690. package/src/web/scrapers/opencorporates.ts +275 -0
  691. package/src/web/scrapers/openlibrary.ts +319 -0
  692. package/src/web/scrapers/orcid.ts +298 -0
  693. package/src/web/scrapers/osv.ts +192 -0
  694. package/src/web/scrapers/packagist.ts +174 -0
  695. package/src/web/scrapers/pub-dev.ts +185 -0
  696. package/src/web/scrapers/pubmed.ts +177 -0
  697. package/src/web/scrapers/pypi.ts +129 -0
  698. package/src/web/scrapers/rawg.ts +124 -0
  699. package/src/web/scrapers/readthedocs.ts +125 -0
  700. package/src/web/scrapers/reddit.ts +104 -0
  701. package/src/web/scrapers/repology.ts +262 -0
  702. package/src/web/scrapers/rfc.ts +209 -0
  703. package/src/web/scrapers/rubygems.ts +117 -0
  704. package/src/web/scrapers/searchcode.ts +217 -0
  705. package/src/web/scrapers/sec-edgar.ts +274 -0
  706. package/src/web/scrapers/semantic-scholar.ts +190 -0
  707. package/src/web/scrapers/snapcraft.ts +200 -0
  708. package/src/web/scrapers/sourcegraph.ts +373 -0
  709. package/src/web/scrapers/spdx.ts +121 -0
  710. package/src/web/scrapers/spotify.ts +217 -0
  711. package/src/web/scrapers/stackoverflow.ts +124 -0
  712. package/src/web/scrapers/terraform.ts +304 -0
  713. package/src/web/scrapers/tldr.ts +51 -0
  714. package/src/web/scrapers/twitter.ts +97 -0
  715. package/src/web/scrapers/types.ts +200 -0
  716. package/src/web/scrapers/utils.ts +142 -0
  717. package/src/web/scrapers/vimeo.ts +152 -0
  718. package/src/web/scrapers/vscode-marketplace.ts +195 -0
  719. package/src/web/scrapers/w3c.ts +163 -0
  720. package/src/web/scrapers/wikidata.ts +357 -0
  721. package/src/web/scrapers/wikipedia.ts +95 -0
  722. package/src/web/scrapers/youtube.ts +312 -0
  723. package/src/web/search/auth.ts +178 -0
  724. package/src/web/search/index.ts +598 -0
  725. package/src/web/search/provider.ts +77 -0
  726. package/src/web/search/providers/anthropic.ts +284 -0
  727. package/src/web/search/providers/base.ts +22 -0
  728. package/src/web/search/providers/brave.ts +165 -0
  729. package/src/web/search/providers/codex.ts +377 -0
  730. package/src/web/search/providers/exa.ts +158 -0
  731. package/src/web/search/providers/gemini.ts +437 -0
  732. package/src/web/search/providers/jina.ts +99 -0
  733. package/src/web/search/providers/kimi.ts +196 -0
  734. package/src/web/search/providers/perplexity.ts +546 -0
  735. package/src/web/search/providers/synthetic.ts +136 -0
  736. package/src/web/search/providers/zai.ts +352 -0
  737. package/src/web/search/render.ts +299 -0
  738. package/src/web/search/types.ts +437 -0
@@ -0,0 +1,784 @@
1
+ /**
2
+ * Fuzzy matching utilities for the edit tool.
3
+ *
4
+ * Provides both character-level and line-level fuzzy matching with progressive
5
+ * fallback strategies for finding text in files.
6
+ */
7
+ import { countLeadingWhitespace, normalizeForFuzzy, normalizeUnicode } from "./normalize";
8
+ import type { ContextLineResult, FuzzyMatch, MatchOutcome, SequenceMatchStrategy, SequenceSearchResult } from "./types";
9
+
10
+ // ═══════════════════════════════════════════════════════════════════════════
11
+ // Constants
12
+ // ═══════════════════════════════════════════════════════════════════════════
13
+
14
+ /** Default similarity threshold for fuzzy matching */
15
+ export const DEFAULT_FUZZY_THRESHOLD = 0.95;
16
+
17
+ /** Threshold for sequence-based fuzzy matching */
18
+ const SEQUENCE_FUZZY_THRESHOLD = 0.92;
19
+
20
+ /** Fallback threshold for line-based matching */
21
+ const FALLBACK_THRESHOLD = 0.8;
22
+
23
+ /** Threshold for context line matching */
24
+ const CONTEXT_FUZZY_THRESHOLD = 0.8;
25
+
26
+ /** Minimum length for partial/substring matching */
27
+ const PARTIAL_MATCH_MIN_LENGTH = 6;
28
+
29
+ /** Minimum ratio of pattern to line length for substring match */
30
+ const PARTIAL_MATCH_MIN_RATIO = 0.3;
31
+
32
+ /** Context lines to show before/after an ambiguous match preview */
33
+ const OCCURRENCE_PREVIEW_CONTEXT = 5;
34
+
35
+ /** Maximum line length for ambiguous match previews */
36
+ const OCCURRENCE_PREVIEW_MAX_LEN = 80;
37
+
38
+ // ═══════════════════════════════════════════════════════════════════════════
39
+ // Core Algorithms
40
+ // ═══════════════════════════════════════════════════════════════════════════
41
+
42
+ /** Compute Levenshtein distance between two strings */
43
+ export function levenshteinDistance(a: string, b: string): number {
44
+ if (a === b) return 0;
45
+ const aLen = a.length;
46
+ const bLen = b.length;
47
+ if (aLen === 0) return bLen;
48
+ if (bLen === 0) return aLen;
49
+
50
+ let prev = new Array<number>(bLen + 1);
51
+ let curr = new Array<number>(bLen + 1);
52
+ for (let j = 0; j <= bLen; j++) {
53
+ prev[j] = j;
54
+ }
55
+
56
+ for (let i = 1; i <= aLen; i++) {
57
+ curr[0] = i;
58
+ const aCode = a.charCodeAt(i - 1);
59
+ for (let j = 1; j <= bLen; j++) {
60
+ const cost = aCode === b.charCodeAt(j - 1) ? 0 : 1;
61
+ const deletion = prev[j] + 1;
62
+ const insertion = curr[j - 1] + 1;
63
+ const substitution = prev[j - 1] + cost;
64
+ curr[j] = Math.min(deletion, insertion, substitution);
65
+ }
66
+ const tmp = prev;
67
+ prev = curr;
68
+ curr = tmp;
69
+ }
70
+
71
+ return prev[bLen];
72
+ }
73
+
74
+ /** Compute similarity score between two strings (0 to 1) */
75
+ export function similarity(a: string, b: string): number {
76
+ if (a.length === 0 && b.length === 0) return 1;
77
+ const maxLen = Math.max(a.length, b.length);
78
+ if (maxLen === 0) return 1;
79
+ const distance = levenshteinDistance(a, b);
80
+ return 1 - distance / maxLen;
81
+ }
82
+
83
+ // ═══════════════════════════════════════════════════════════════════════════
84
+ // Line-Based Utilities
85
+ // ═══════════════════════════════════════════════════════════════════════════
86
+
87
+ /** Compute relative indent depths for lines */
88
+ function computeRelativeIndentDepths(lines: string[]): number[] {
89
+ const indents = lines.map(countLeadingWhitespace);
90
+ const nonEmptyIndents: number[] = [];
91
+ for (let i = 0; i < lines.length; i++) {
92
+ if (lines[i].trim().length > 0) {
93
+ nonEmptyIndents.push(indents[i]);
94
+ }
95
+ }
96
+ const minIndent = nonEmptyIndents.length > 0 ? Math.min(...nonEmptyIndents) : 0;
97
+ const indentSteps = nonEmptyIndents.map(indent => indent - minIndent).filter(step => step > 0);
98
+ const indentUnit = indentSteps.length > 0 ? Math.min(...indentSteps) : 1;
99
+
100
+ return lines.map((line, index) => {
101
+ if (line.trim().length === 0) return 0;
102
+ if (indentUnit <= 0) return 0;
103
+ const relativeIndent = indents[index] - minIndent;
104
+ return Math.round(relativeIndent / indentUnit);
105
+ });
106
+ }
107
+
108
+ /** Normalize lines for matching, optionally including indent depth */
109
+ function normalizeLines(lines: string[], includeDepth = true): string[] {
110
+ const indentDepths = includeDepth ? computeRelativeIndentDepths(lines) : null;
111
+ return lines.map((line, index) => {
112
+ const trimmed = line.trim();
113
+ const prefix = indentDepths ? `${indentDepths[index]}|` : "|";
114
+ if (trimmed.length === 0) return prefix;
115
+ return `${prefix}${normalizeForFuzzy(trimmed)}`;
116
+ });
117
+ }
118
+
119
+ /** Compute character offsets for each line in content */
120
+ function computeLineOffsets(lines: string[]): number[] {
121
+ const offsets: number[] = [];
122
+ let offset = 0;
123
+ for (let i = 0; i < lines.length; i++) {
124
+ offsets.push(offset);
125
+ offset += lines[i].length;
126
+ if (i < lines.length - 1) offset += 1; // newline
127
+ }
128
+ return offsets;
129
+ }
130
+
131
+ // ═══════════════════════════════════════════════════════════════════════════
132
+ // Character-Level Fuzzy Match (for replace mode)
133
+ // ═══════════════════════════════════════════════════════════════════════════
134
+
135
+ interface BestFuzzyMatchResult {
136
+ best?: FuzzyMatch;
137
+ aboveThresholdCount: number;
138
+ secondBestScore: number;
139
+ }
140
+
141
+ function findBestFuzzyMatchCore(
142
+ contentLines: string[],
143
+ targetLines: string[],
144
+ offsets: number[],
145
+ threshold: number,
146
+ includeDepth: boolean,
147
+ ): BestFuzzyMatchResult {
148
+ const targetNormalized = normalizeLines(targetLines, includeDepth);
149
+
150
+ let best: FuzzyMatch | undefined;
151
+ let bestScore = -1;
152
+ let secondBestScore = -1;
153
+ let aboveThresholdCount = 0;
154
+
155
+ for (let start = 0; start <= contentLines.length - targetLines.length; start++) {
156
+ const windowLines = contentLines.slice(start, start + targetLines.length);
157
+ const windowNormalized = normalizeLines(windowLines, includeDepth);
158
+ let score = 0;
159
+ for (let i = 0; i < targetLines.length; i++) {
160
+ score += similarity(targetNormalized[i], windowNormalized[i]);
161
+ }
162
+ score = score / targetLines.length;
163
+
164
+ if (score >= threshold) {
165
+ aboveThresholdCount++;
166
+ }
167
+
168
+ if (score > bestScore) {
169
+ secondBestScore = bestScore;
170
+ bestScore = score;
171
+ best = {
172
+ actualText: windowLines.join("\n"),
173
+ startIndex: offsets[start],
174
+ startLine: start + 1,
175
+ confidence: score,
176
+ };
177
+ } else if (score > secondBestScore) {
178
+ secondBestScore = score;
179
+ }
180
+ }
181
+
182
+ return { best, aboveThresholdCount, secondBestScore };
183
+ }
184
+
185
+ function findBestFuzzyMatch(content: string, target: string, threshold: number): BestFuzzyMatchResult {
186
+ const contentLines = content.split("\n");
187
+ const targetLines = target.split("\n");
188
+
189
+ if (targetLines.length === 0 || target.length === 0) {
190
+ return { aboveThresholdCount: 0, secondBestScore: 0 };
191
+ }
192
+ if (targetLines.length > contentLines.length) {
193
+ return { aboveThresholdCount: 0, secondBestScore: 0 };
194
+ }
195
+
196
+ const offsets = computeLineOffsets(contentLines);
197
+ let result = findBestFuzzyMatchCore(contentLines, targetLines, offsets, threshold, true);
198
+
199
+ // Retry without indent depth if match is close but below threshold
200
+ if (result.best && result.best.confidence < threshold && result.best.confidence >= FALLBACK_THRESHOLD) {
201
+ const noDepthResult = findBestFuzzyMatchCore(contentLines, targetLines, offsets, threshold, false);
202
+ if (noDepthResult.best && noDepthResult.best.confidence > result.best.confidence) {
203
+ result = noDepthResult;
204
+ }
205
+ }
206
+
207
+ return result;
208
+ }
209
+
210
+ /**
211
+ * Find a match for target text within content.
212
+ * Used primarily for replace-mode edits.
213
+ */
214
+ export function findMatch(
215
+ content: string,
216
+ target: string,
217
+ options: { allowFuzzy: boolean; threshold?: number },
218
+ ): MatchOutcome {
219
+ if (target.length === 0) {
220
+ return {};
221
+ }
222
+
223
+ // Try exact match first
224
+ const exactIndex = content.indexOf(target);
225
+ if (exactIndex !== -1) {
226
+ const occurrences = content.split(target).length - 1;
227
+ if (occurrences > 1) {
228
+ // Find line numbers and previews for each occurrence (up to 5)
229
+ const contentLines = content.split("\n");
230
+ const occurrenceLines: number[] = [];
231
+ const occurrencePreviews: string[] = [];
232
+ let searchStart = 0;
233
+ for (let i = 0; i < 5; i++) {
234
+ const idx = content.indexOf(target, searchStart);
235
+ if (idx === -1) break;
236
+ const lineNumber = content.slice(0, idx).split("\n").length;
237
+ occurrenceLines.push(lineNumber);
238
+ const start = Math.max(0, lineNumber - 1 - OCCURRENCE_PREVIEW_CONTEXT);
239
+ const end = Math.min(contentLines.length, lineNumber + OCCURRENCE_PREVIEW_CONTEXT + 1);
240
+ const previewLines = contentLines.slice(start, end);
241
+ const preview = previewLines
242
+ .map((line, idx) => {
243
+ const num = start + idx + 1;
244
+ return ` ${num} | ${line.length > OCCURRENCE_PREVIEW_MAX_LEN ? `${line.slice(0, OCCURRENCE_PREVIEW_MAX_LEN - 1)}…` : line}`;
245
+ })
246
+ .join("\n");
247
+ occurrencePreviews.push(preview);
248
+ searchStart = idx + 1;
249
+ }
250
+ return { occurrences, occurrenceLines, occurrencePreviews };
251
+ }
252
+ const startLine = content.slice(0, exactIndex).split("\n").length;
253
+ return {
254
+ match: {
255
+ actualText: target,
256
+ startIndex: exactIndex,
257
+ startLine,
258
+ confidence: 1,
259
+ },
260
+ };
261
+ }
262
+
263
+ // Try fuzzy match
264
+ const threshold = options.threshold ?? DEFAULT_FUZZY_THRESHOLD;
265
+ const { best, aboveThresholdCount, secondBestScore } = findBestFuzzyMatch(content, target, threshold);
266
+
267
+ if (!best) {
268
+ return {};
269
+ }
270
+
271
+ if (options.allowFuzzy && best.confidence >= threshold) {
272
+ if (aboveThresholdCount === 1) {
273
+ return { match: best, closest: best };
274
+ }
275
+ const dominantDelta = 0.08;
276
+ const dominantMin = 0.97;
277
+ if (
278
+ aboveThresholdCount > 1 &&
279
+ best.confidence >= dominantMin &&
280
+ best.confidence - secondBestScore >= dominantDelta
281
+ ) {
282
+ return { match: best, closest: best, fuzzyMatches: aboveThresholdCount, dominantFuzzy: true };
283
+ }
284
+ }
285
+
286
+ return { closest: best, fuzzyMatches: aboveThresholdCount };
287
+ }
288
+
289
+ // ═══════════════════════════════════════════════════════════════════════════
290
+ // Line-Based Sequence Match (for patch mode)
291
+ // ═══════════════════════════════════════════════════════════════════════════
292
+
293
+ /** Check if pattern matches lines starting at index using comparison function */
294
+ function matchesAt(lines: string[], pattern: string[], i: number, compare: (a: string, b: string) => boolean): boolean {
295
+ for (let j = 0; j < pattern.length; j++) {
296
+ if (!compare(lines[i + j], pattern[j])) {
297
+ return false;
298
+ }
299
+ }
300
+ return true;
301
+ }
302
+
303
+ /** Compute average similarity score for pattern at position */
304
+ function fuzzyScoreAt(lines: string[], pattern: string[], i: number): number {
305
+ let totalScore = 0;
306
+ for (let j = 0; j < pattern.length; j++) {
307
+ const lineNorm = normalizeForFuzzy(lines[i + j]);
308
+ const patternNorm = normalizeForFuzzy(pattern[j]);
309
+ totalScore += similarity(lineNorm, patternNorm);
310
+ }
311
+ return totalScore / pattern.length;
312
+ }
313
+
314
+ /** Check if line starts with pattern (normalized) */
315
+ function lineStartsWithPattern(line: string, pattern: string): boolean {
316
+ const lineNorm = normalizeForFuzzy(line);
317
+ const patternNorm = normalizeForFuzzy(pattern);
318
+ if (patternNorm.length === 0) return lineNorm.length === 0;
319
+ return lineNorm.startsWith(patternNorm);
320
+ }
321
+
322
+ /** Check if line contains pattern as significant substring */
323
+ function lineIncludesPattern(line: string, pattern: string): boolean {
324
+ const lineNorm = normalizeForFuzzy(line);
325
+ const patternNorm = normalizeForFuzzy(pattern);
326
+ if (patternNorm.length === 0) return lineNorm.length === 0;
327
+ if (patternNorm.length < PARTIAL_MATCH_MIN_LENGTH) return false;
328
+ if (!lineNorm.includes(patternNorm)) return false;
329
+ return patternNorm.length / Math.max(1, lineNorm.length) >= PARTIAL_MATCH_MIN_RATIO;
330
+ }
331
+
332
+ function stripCommentPrefix(line: string): string {
333
+ let trimmed = line.trimStart();
334
+ if (trimmed.startsWith("/*")) {
335
+ trimmed = trimmed.slice(2);
336
+ } else if (trimmed.startsWith("*/")) {
337
+ trimmed = trimmed.slice(2);
338
+ } else if (trimmed.startsWith("//")) {
339
+ trimmed = trimmed.slice(2);
340
+ } else if (trimmed.startsWith("*")) {
341
+ trimmed = trimmed.slice(1);
342
+ } else if (trimmed.startsWith("#")) {
343
+ trimmed = trimmed.slice(1);
344
+ } else if (trimmed.startsWith(";")) {
345
+ trimmed = trimmed.slice(1);
346
+ } else if (trimmed.startsWith("/") && trimmed[1] === " ") {
347
+ trimmed = trimmed.slice(1);
348
+ }
349
+ return trimmed.trimStart();
350
+ }
351
+
352
+ /**
353
+ * Find a sequence of pattern lines within content lines.
354
+ *
355
+ * Attempts matches with decreasing strictness:
356
+ * 1. Exact match
357
+ * 2. Trailing whitespace ignored
358
+ * 3. All whitespace trimmed
359
+ * 4. Unicode punctuation normalized
360
+ * 5. Prefix match (pattern is prefix of line)
361
+ * 6. Substring match (pattern is substring of line)
362
+ * 7. Fuzzy similarity match
363
+ *
364
+ * @param lines - The lines of the file content
365
+ * @param pattern - The lines to search for
366
+ * @param start - Starting index for the search
367
+ * @param eof - If true, prefer matching at end of file first
368
+ */
369
+ export function seekSequence(
370
+ lines: string[],
371
+ pattern: string[],
372
+ start: number,
373
+ eof: boolean,
374
+ options?: { allowFuzzy?: boolean },
375
+ ): SequenceSearchResult {
376
+ const allowFuzzy = options?.allowFuzzy ?? true;
377
+ // Empty pattern matches immediately
378
+ if (pattern.length === 0) {
379
+ return { index: start, confidence: 1.0, strategy: "exact" };
380
+ }
381
+
382
+ // Pattern longer than available content cannot match
383
+ if (pattern.length > lines.length) {
384
+ return { index: undefined, confidence: 0 };
385
+ }
386
+
387
+ // Determine search start position
388
+ const searchStart = eof && lines.length >= pattern.length ? lines.length - pattern.length : start;
389
+ const maxStart = lines.length - pattern.length;
390
+
391
+ const runExactPasses = (from: number, to: number): SequenceSearchResult | undefined => {
392
+ // Pass 1: Exact match
393
+ for (let i = from; i <= to; i++) {
394
+ if (matchesAt(lines, pattern, i, (a, b) => a === b)) {
395
+ return { index: i, confidence: 1.0, strategy: "exact" };
396
+ }
397
+ }
398
+
399
+ // Pass 2: Trailing whitespace stripped
400
+ for (let i = from; i <= to; i++) {
401
+ if (matchesAt(lines, pattern, i, (a, b) => a.trimEnd() === b.trimEnd())) {
402
+ return { index: i, confidence: 0.99, strategy: "trim-trailing" };
403
+ }
404
+ }
405
+
406
+ // Pass 3: Both leading and trailing whitespace stripped
407
+ for (let i = from; i <= to; i++) {
408
+ if (matchesAt(lines, pattern, i, (a, b) => a.trim() === b.trim())) {
409
+ return { index: i, confidence: 0.98, strategy: "trim" };
410
+ }
411
+ }
412
+
413
+ // Pass 3b: Comment-prefix normalized match
414
+ for (let i = from; i <= to; i++) {
415
+ if (matchesAt(lines, pattern, i, (a, b) => stripCommentPrefix(a) === stripCommentPrefix(b))) {
416
+ return { index: i, confidence: 0.975, strategy: "comment-prefix" };
417
+ }
418
+ }
419
+
420
+ // Pass 4: Normalize unicode punctuation
421
+ for (let i = from; i <= to; i++) {
422
+ if (matchesAt(lines, pattern, i, (a, b) => normalizeUnicode(a) === normalizeUnicode(b))) {
423
+ return { index: i, confidence: 0.97, strategy: "unicode" };
424
+ }
425
+ }
426
+
427
+ if (!allowFuzzy) {
428
+ return undefined;
429
+ }
430
+
431
+ // Pass 5: Partial line prefix match (track all matches for ambiguity detection)
432
+ {
433
+ let firstMatch: number | undefined;
434
+ let matchCount = 0;
435
+ const matchIndices: number[] = [];
436
+ for (let i = from; i <= to; i++) {
437
+ if (matchesAt(lines, pattern, i, lineStartsWithPattern)) {
438
+ if (firstMatch === undefined) firstMatch = i;
439
+ matchCount++;
440
+ if (matchIndices.length < 5) matchIndices.push(i);
441
+ }
442
+ }
443
+ if (matchCount > 0) {
444
+ return { index: firstMatch, confidence: 0.965, matchCount, matchIndices, strategy: "prefix" };
445
+ }
446
+ }
447
+
448
+ // Pass 6: Partial line substring match (track all matches for ambiguity detection)
449
+ {
450
+ let firstMatch: number | undefined;
451
+ let matchCount = 0;
452
+ const matchIndices: number[] = [];
453
+ for (let i = from; i <= to; i++) {
454
+ if (matchesAt(lines, pattern, i, lineIncludesPattern)) {
455
+ if (firstMatch === undefined) firstMatch = i;
456
+ matchCount++;
457
+ if (matchIndices.length < 5) matchIndices.push(i);
458
+ }
459
+ }
460
+ if (matchCount > 0) {
461
+ return { index: firstMatch, confidence: 0.94, matchCount, matchIndices, strategy: "substring" };
462
+ }
463
+ }
464
+
465
+ return undefined;
466
+ };
467
+
468
+ const primaryPassResult = runExactPasses(searchStart, maxStart);
469
+ if (primaryPassResult) {
470
+ return primaryPassResult;
471
+ }
472
+
473
+ if (eof && searchStart > start) {
474
+ const fromStartResult = runExactPasses(start, maxStart);
475
+ if (fromStartResult) {
476
+ return fromStartResult;
477
+ }
478
+ }
479
+
480
+ if (!allowFuzzy) {
481
+ return { index: undefined, confidence: 0 };
482
+ }
483
+
484
+ // Pass 7: Fuzzy matching - find best match above threshold
485
+ let bestIndex: number | undefined;
486
+ let bestScore = 0;
487
+ let secondBestScore = 0;
488
+ let matchCount = 0;
489
+ const matchIndices: number[] = [];
490
+
491
+ for (let i = searchStart; i <= maxStart; i++) {
492
+ const score = fuzzyScoreAt(lines, pattern, i);
493
+ if (score >= SEQUENCE_FUZZY_THRESHOLD) {
494
+ matchCount++;
495
+ if (matchIndices.length < 5) matchIndices.push(i);
496
+ }
497
+ if (score > bestScore) {
498
+ secondBestScore = bestScore;
499
+ bestScore = score;
500
+ bestIndex = i;
501
+ } else if (score > secondBestScore) {
502
+ secondBestScore = score;
503
+ }
504
+ }
505
+
506
+ // Also search from start if eof mode started from end
507
+ if (eof && searchStart > start) {
508
+ for (let i = start; i < searchStart; i++) {
509
+ const score = fuzzyScoreAt(lines, pattern, i);
510
+ if (score >= SEQUENCE_FUZZY_THRESHOLD) {
511
+ matchCount++;
512
+ if (matchIndices.length < 5) matchIndices.push(i);
513
+ }
514
+ if (score > bestScore) {
515
+ secondBestScore = bestScore;
516
+ bestScore = score;
517
+ bestIndex = i;
518
+ } else if (score > secondBestScore) {
519
+ secondBestScore = score;
520
+ }
521
+ }
522
+ }
523
+
524
+ if (bestIndex !== undefined && bestScore >= SEQUENCE_FUZZY_THRESHOLD) {
525
+ const dominantDelta = 0.08;
526
+ const dominantMin = 0.97;
527
+ if (matchCount > 1 && bestScore >= dominantMin && bestScore - secondBestScore >= dominantDelta) {
528
+ return {
529
+ index: bestIndex,
530
+ confidence: bestScore,
531
+ matchCount: 1,
532
+ matchIndices,
533
+ strategy: "fuzzy-dominant",
534
+ };
535
+ }
536
+ return { index: bestIndex, confidence: bestScore, matchCount, matchIndices, strategy: "fuzzy" };
537
+ }
538
+
539
+ // Pass 8: Character-based fuzzy matching via findMatch
540
+ // This is the final fallback for when line-based matching fails
541
+ const CHARACTER_MATCH_THRESHOLD = 0.92;
542
+ const patternText = pattern.join("\n");
543
+ const contentText = lines.slice(start).join("\n");
544
+ const matchOutcome = findMatch(contentText, patternText, {
545
+ allowFuzzy: true,
546
+ threshold: CHARACTER_MATCH_THRESHOLD,
547
+ });
548
+
549
+ if (matchOutcome.match) {
550
+ // Convert character index back to line index
551
+ const matchedContent = contentText.substring(0, matchOutcome.match.startIndex);
552
+ const lineIndex = start + matchedContent.split("\n").length - 1;
553
+ const fallbackMatchCount = matchOutcome.occurrences ?? matchOutcome.fuzzyMatches ?? 1;
554
+ return {
555
+ index: lineIndex,
556
+ confidence: matchOutcome.match.confidence,
557
+ matchCount: fallbackMatchCount,
558
+ strategy: "character",
559
+ };
560
+ }
561
+
562
+ const fallbackMatchCount = matchOutcome.occurrences ?? matchOutcome.fuzzyMatches;
563
+ return { index: undefined, confidence: bestScore, matchCount: fallbackMatchCount };
564
+ }
565
+
566
+ export function findClosestSequenceMatch(
567
+ lines: string[],
568
+ pattern: string[],
569
+ options?: { start?: number; eof?: boolean },
570
+ ): { index: number | undefined; confidence: number; strategy: SequenceMatchStrategy } {
571
+ if (pattern.length === 0) {
572
+ return { index: options?.start ?? 0, confidence: 1, strategy: "exact" };
573
+ }
574
+ if (pattern.length > lines.length) {
575
+ return { index: undefined, confidence: 0, strategy: "fuzzy" };
576
+ }
577
+
578
+ const start = options?.start ?? 0;
579
+ const eof = options?.eof ?? false;
580
+ const maxStart = lines.length - pattern.length;
581
+ const searchStart = eof && lines.length >= pattern.length ? maxStart : start;
582
+
583
+ let bestIndex: number | undefined;
584
+ let bestScore = 0;
585
+
586
+ for (let i = searchStart; i <= maxStart; i++) {
587
+ const score = fuzzyScoreAt(lines, pattern, i);
588
+ if (score > bestScore) {
589
+ bestScore = score;
590
+ bestIndex = i;
591
+ }
592
+ }
593
+
594
+ if (eof && searchStart > start) {
595
+ for (let i = start; i < searchStart; i++) {
596
+ const score = fuzzyScoreAt(lines, pattern, i);
597
+ if (score > bestScore) {
598
+ bestScore = score;
599
+ bestIndex = i;
600
+ }
601
+ }
602
+ }
603
+
604
+ return { index: bestIndex, confidence: bestScore, strategy: "fuzzy" };
605
+ }
606
+
607
+ /**
608
+ * Find a context line in the file using progressive matching strategies.
609
+ *
610
+ * @param lines - The lines of the file content
611
+ * @param context - The context line to search for
612
+ * @param startFrom - Starting index for the search
613
+ */
614
+ export function findContextLine(
615
+ lines: string[],
616
+ context: string,
617
+ startFrom: number,
618
+ options?: { allowFuzzy?: boolean; skipFunctionFallback?: boolean },
619
+ ): ContextLineResult {
620
+ const allowFuzzy = options?.allowFuzzy ?? true;
621
+ const trimmedContext = context.trim();
622
+
623
+ // Pass 1: Exact line match
624
+ {
625
+ let firstMatch: number | undefined;
626
+ let matchCount = 0;
627
+ const matchIndices: number[] = [];
628
+ for (let i = startFrom; i < lines.length; i++) {
629
+ if (lines[i] === context) {
630
+ if (firstMatch === undefined) firstMatch = i;
631
+ matchCount++;
632
+ if (matchIndices.length < 5) matchIndices.push(i);
633
+ }
634
+ }
635
+ if (matchCount > 0) {
636
+ return { index: firstMatch, confidence: 1.0, matchCount, matchIndices, strategy: "exact" };
637
+ }
638
+ }
639
+
640
+ // Pass 2: Trimmed match
641
+ {
642
+ let firstMatch: number | undefined;
643
+ let matchCount = 0;
644
+ const matchIndices: number[] = [];
645
+ for (let i = startFrom; i < lines.length; i++) {
646
+ if (lines[i].trim() === trimmedContext) {
647
+ if (firstMatch === undefined) firstMatch = i;
648
+ matchCount++;
649
+ if (matchIndices.length < 5) matchIndices.push(i);
650
+ }
651
+ }
652
+ if (matchCount > 0) {
653
+ return { index: firstMatch, confidence: 0.99, matchCount, matchIndices, strategy: "trim" };
654
+ }
655
+ }
656
+
657
+ // Pass 3: Unicode normalization match
658
+ const normalizedContext = normalizeUnicode(context);
659
+ {
660
+ let firstMatch: number | undefined;
661
+ let matchCount = 0;
662
+ const matchIndices: number[] = [];
663
+ for (let i = startFrom; i < lines.length; i++) {
664
+ if (normalizeUnicode(lines[i]) === normalizedContext) {
665
+ if (firstMatch === undefined) firstMatch = i;
666
+ matchCount++;
667
+ if (matchIndices.length < 5) matchIndices.push(i);
668
+ }
669
+ }
670
+ if (matchCount > 0) {
671
+ return { index: firstMatch, confidence: 0.98, matchCount, matchIndices, strategy: "unicode" };
672
+ }
673
+ }
674
+
675
+ if (!allowFuzzy) {
676
+ return { index: undefined, confidence: 0 };
677
+ }
678
+
679
+ // Pass 4: Prefix match (file line starts with context)
680
+ const contextNorm = normalizeForFuzzy(context);
681
+ if (contextNorm.length > 0) {
682
+ let firstMatch: number | undefined;
683
+ let matchCount = 0;
684
+ const matchIndices: number[] = [];
685
+ for (let i = startFrom; i < lines.length; i++) {
686
+ const lineNorm = normalizeForFuzzy(lines[i]);
687
+ if (lineNorm.startsWith(contextNorm)) {
688
+ if (firstMatch === undefined) firstMatch = i;
689
+ matchCount++;
690
+ if (matchIndices.length < 5) matchIndices.push(i);
691
+ }
692
+ }
693
+ if (matchCount > 0) {
694
+ return { index: firstMatch, confidence: 0.96, matchCount, matchIndices, strategy: "prefix" };
695
+ }
696
+ }
697
+
698
+ // Pass 5: Substring match (file line contains context)
699
+ // First pass: find all substring matches (ignoring ratio)
700
+ // If exactly one match exists, accept it (uniqueness is sufficient)
701
+ // If multiple matches, apply ratio filter to disambiguate
702
+ if (contextNorm.length >= PARTIAL_MATCH_MIN_LENGTH) {
703
+ const allSubstringMatches: Array<{ index: number; ratio: number }> = [];
704
+ for (let i = startFrom; i < lines.length; i++) {
705
+ const lineNorm = normalizeForFuzzy(lines[i]);
706
+ if (lineNorm.includes(contextNorm)) {
707
+ const ratio = contextNorm.length / Math.max(1, lineNorm.length);
708
+ allSubstringMatches.push({ index: i, ratio });
709
+ }
710
+ }
711
+ const matchIndices = allSubstringMatches.slice(0, 5).map(match => match.index);
712
+
713
+ // If exactly one substring match, accept it regardless of ratio
714
+ if (allSubstringMatches.length === 1) {
715
+ return {
716
+ index: allSubstringMatches[0].index,
717
+ confidence: 0.94,
718
+ matchCount: 1,
719
+ matchIndices,
720
+ strategy: "substring",
721
+ };
722
+ }
723
+
724
+ // Multiple matches: filter by ratio to disambiguate
725
+ let firstMatch: number | undefined;
726
+ let matchCount = 0;
727
+ for (const match of allSubstringMatches) {
728
+ if (match.ratio >= PARTIAL_MATCH_MIN_RATIO) {
729
+ if (firstMatch === undefined) firstMatch = match.index;
730
+ matchCount++;
731
+ }
732
+ }
733
+ if (matchCount > 0) {
734
+ return { index: firstMatch, confidence: 0.94, matchCount, matchIndices, strategy: "substring" };
735
+ }
736
+
737
+ // If we had substring matches but none passed ratio filter,
738
+ // return ambiguous result so caller knows matches exist
739
+ if (allSubstringMatches.length > 1) {
740
+ return {
741
+ index: allSubstringMatches[0].index,
742
+ confidence: 0.94,
743
+ matchCount: allSubstringMatches.length,
744
+ matchIndices,
745
+ strategy: "substring",
746
+ };
747
+ }
748
+ }
749
+
750
+ // Pass 6: Fuzzy match using similarity
751
+ let bestIndex: number | undefined;
752
+ let bestScore = 0;
753
+ let matchCount = 0;
754
+ const matchIndices: number[] = [];
755
+
756
+ for (let i = startFrom; i < lines.length; i++) {
757
+ const lineNorm = normalizeForFuzzy(lines[i]);
758
+ const score = similarity(lineNorm, contextNorm);
759
+ if (score >= CONTEXT_FUZZY_THRESHOLD) {
760
+ matchCount++;
761
+ if (matchIndices.length < 5) matchIndices.push(i);
762
+ }
763
+ if (score > bestScore) {
764
+ bestScore = score;
765
+ bestIndex = i;
766
+ }
767
+ }
768
+
769
+ if (bestIndex !== undefined && bestScore >= CONTEXT_FUZZY_THRESHOLD) {
770
+ return { index: bestIndex, confidence: bestScore, matchCount, matchIndices, strategy: "fuzzy" };
771
+ }
772
+
773
+ if (!options?.skipFunctionFallback && trimmedContext.endsWith("()")) {
774
+ const withParen = trimmedContext.replace(/\(\)\s*$/u, "(");
775
+ const withoutParen = trimmedContext.replace(/\(\)\s*$/u, "");
776
+ const parenResult = findContextLine(lines, withParen, startFrom, { allowFuzzy, skipFunctionFallback: true });
777
+ if (parenResult.index !== undefined || (parenResult.matchCount ?? 0) > 0) {
778
+ return parenResult;
779
+ }
780
+ return findContextLine(lines, withoutParen, startFrom, { allowFuzzy, skipFunctionFallback: true });
781
+ }
782
+
783
+ return { index: undefined, confidence: bestScore };
784
+ }