plugsuits 1.0.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 (560) hide show
  1. package/cli.js +3 -0
  2. package/dist/agent-reasoning-default.test.d.ts +2 -0
  3. package/dist/agent-reasoning-default.test.d.ts.map +1 -0
  4. package/dist/agent-reasoning-default.test.js +38 -0
  5. package/dist/agent-reasoning-default.test.js.map +1 -0
  6. package/dist/agent.d.ts +61 -0
  7. package/dist/agent.d.ts.map +1 -0
  8. package/dist/agent.js +308 -0
  9. package/dist/agent.js.map +1 -0
  10. package/dist/agent.test.d.ts +2 -0
  11. package/dist/agent.test.d.ts.map +1 -0
  12. package/dist/agent.test.js +38 -0
  13. package/dist/agent.test.js.map +1 -0
  14. package/dist/cli-args.d.ts +15 -0
  15. package/dist/cli-args.d.ts.map +1 -0
  16. package/dist/cli-args.js +63 -0
  17. package/dist/cli-args.js.map +1 -0
  18. package/dist/cli-args.test.d.ts +2 -0
  19. package/dist/cli-args.test.d.ts.map +1 -0
  20. package/dist/cli-args.test.js +105 -0
  21. package/dist/cli-args.test.js.map +1 -0
  22. package/dist/commands/aliases-and-tool-fallback.test.d.ts +2 -0
  23. package/dist/commands/aliases-and-tool-fallback.test.d.ts.map +1 -0
  24. package/dist/commands/aliases-and-tool-fallback.test.js +132 -0
  25. package/dist/commands/aliases-and-tool-fallback.test.js.map +1 -0
  26. package/dist/commands/clear.d.ts +3 -0
  27. package/dist/commands/clear.d.ts.map +1 -0
  28. package/dist/commands/clear.js +12 -0
  29. package/dist/commands/clear.js.map +1 -0
  30. package/dist/commands/factories/create-toggle-command.d.ts +13 -0
  31. package/dist/commands/factories/create-toggle-command.d.ts.map +1 -0
  32. package/dist/commands/factories/create-toggle-command.js +38 -0
  33. package/dist/commands/factories/create-toggle-command.js.map +1 -0
  34. package/dist/commands/help.d.ts +3 -0
  35. package/dist/commands/help.d.ts.map +1 -0
  36. package/dist/commands/help.js +23 -0
  37. package/dist/commands/help.js.map +1 -0
  38. package/dist/commands/index.d.ts +18 -0
  39. package/dist/commands/index.d.ts.map +1 -0
  40. package/dist/commands/index.js +82 -0
  41. package/dist/commands/index.js.map +1 -0
  42. package/dist/commands/model.d.ts +15 -0
  43. package/dist/commands/model.d.ts.map +1 -0
  44. package/dist/commands/model.js +100 -0
  45. package/dist/commands/model.js.map +1 -0
  46. package/dist/commands/reasoning-mode.d.ts +3 -0
  47. package/dist/commands/reasoning-mode.d.ts.map +1 -0
  48. package/dist/commands/reasoning-mode.js +47 -0
  49. package/dist/commands/reasoning-mode.js.map +1 -0
  50. package/dist/commands/render.d.ts +19 -0
  51. package/dist/commands/render.d.ts.map +1 -0
  52. package/dist/commands/render.js +140 -0
  53. package/dist/commands/render.js.map +1 -0
  54. package/dist/commands/render.test.d.ts +2 -0
  55. package/dist/commands/render.test.d.ts.map +1 -0
  56. package/dist/commands/render.test.js +36 -0
  57. package/dist/commands/render.test.js.map +1 -0
  58. package/dist/commands/tool-fallback.d.ts +3 -0
  59. package/dist/commands/tool-fallback.d.ts.map +1 -0
  60. package/dist/commands/tool-fallback.js +38 -0
  61. package/dist/commands/tool-fallback.js.map +1 -0
  62. package/dist/commands/translate.d.ts +3 -0
  63. package/dist/commands/translate.d.ts.map +1 -0
  64. package/dist/commands/translate.js +12 -0
  65. package/dist/commands/translate.js.map +1 -0
  66. package/dist/commands/translate.test.d.ts +2 -0
  67. package/dist/commands/translate.test.d.ts.map +1 -0
  68. package/dist/commands/translate.test.js +49 -0
  69. package/dist/commands/translate.test.js.map +1 -0
  70. package/dist/commands/types.d.ts +17 -0
  71. package/dist/commands/types.d.ts.map +1 -0
  72. package/dist/commands/types.js +2 -0
  73. package/dist/commands/types.js.map +1 -0
  74. package/dist/context/environment-context.d.ts +2 -0
  75. package/dist/context/environment-context.d.ts.map +1 -0
  76. package/dist/context/environment-context.js +11 -0
  77. package/dist/context/environment-context.js.map +1 -0
  78. package/dist/context/paths.d.ts +3 -0
  79. package/dist/context/paths.d.ts.map +1 -0
  80. package/dist/context/paths.js +3 -0
  81. package/dist/context/paths.js.map +1 -0
  82. package/dist/context/session.d.ts +4 -0
  83. package/dist/context/session.d.ts.map +1 -0
  84. package/dist/context/session.js +16 -0
  85. package/dist/context/session.js.map +1 -0
  86. package/dist/context/skill-command-prefix.d.ts +4 -0
  87. package/dist/context/skill-command-prefix.d.ts.map +1 -0
  88. package/dist/context/skill-command-prefix.js +15 -0
  89. package/dist/context/skill-command-prefix.js.map +1 -0
  90. package/dist/context/skills-integration.test.d.ts +2 -0
  91. package/dist/context/skills-integration.test.d.ts.map +1 -0
  92. package/dist/context/skills-integration.test.js +87 -0
  93. package/dist/context/skills-integration.test.js.map +1 -0
  94. package/dist/context/skills.d.ts +18 -0
  95. package/dist/context/skills.d.ts.map +1 -0
  96. package/dist/context/skills.js +431 -0
  97. package/dist/context/skills.js.map +1 -0
  98. package/dist/context/skills.test.d.ts +2 -0
  99. package/dist/context/skills.test.d.ts.map +1 -0
  100. package/dist/context/skills.test.js +20 -0
  101. package/dist/context/skills.test.js.map +1 -0
  102. package/dist/context/system-prompt.d.ts +2 -0
  103. package/dist/context/system-prompt.d.ts.map +1 -0
  104. package/dist/context/system-prompt.js +100 -0
  105. package/dist/context/system-prompt.js.map +1 -0
  106. package/dist/context/translation-integration.test.d.ts +2 -0
  107. package/dist/context/translation-integration.test.d.ts.map +1 -0
  108. package/dist/context/translation-integration.test.js +138 -0
  109. package/dist/context/translation-integration.test.js.map +1 -0
  110. package/dist/context/translation.d.ts +21 -0
  111. package/dist/context/translation.d.ts.map +1 -0
  112. package/dist/context/translation.js +82 -0
  113. package/dist/context/translation.js.map +1 -0
  114. package/dist/context/translation.test.d.ts +2 -0
  115. package/dist/context/translation.test.d.ts.map +1 -0
  116. package/dist/context/translation.test.js +129 -0
  117. package/dist/context/translation.test.js.map +1 -0
  118. package/dist/entrypoints/cli-input-rendering.test.d.ts +2 -0
  119. package/dist/entrypoints/cli-input-rendering.test.d.ts.map +1 -0
  120. package/dist/entrypoints/cli-input-rendering.test.js +192 -0
  121. package/dist/entrypoints/cli-input-rendering.test.js.map +1 -0
  122. package/dist/entrypoints/cli.d.ts +3 -0
  123. package/dist/entrypoints/cli.d.ts.map +1 -0
  124. package/dist/entrypoints/cli.js +1268 -0
  125. package/dist/entrypoints/cli.js.map +1 -0
  126. package/dist/entrypoints/headless-agent-config.d.ts +21 -0
  127. package/dist/entrypoints/headless-agent-config.d.ts.map +1 -0
  128. package/dist/entrypoints/headless-agent-config.js +15 -0
  129. package/dist/entrypoints/headless-agent-config.js.map +1 -0
  130. package/dist/entrypoints/headless-agent-config.test.d.ts +2 -0
  131. package/dist/entrypoints/headless-agent-config.test.d.ts.map +1 -0
  132. package/dist/entrypoints/headless-agent-config.test.js +63 -0
  133. package/dist/entrypoints/headless-agent-config.test.js.map +1 -0
  134. package/dist/entrypoints/headless.d.ts +3 -0
  135. package/dist/entrypoints/headless.d.ts.map +1 -0
  136. package/dist/entrypoints/headless.js +396 -0
  137. package/dist/entrypoints/headless.js.map +1 -0
  138. package/dist/env.d.ts +8 -0
  139. package/dist/env.d.ts.map +1 -0
  140. package/dist/env.js +14 -0
  141. package/dist/env.js.map +1 -0
  142. package/dist/friendli-models.d.ts +21 -0
  143. package/dist/friendli-models.d.ts.map +1 -0
  144. package/dist/friendli-models.js +57 -0
  145. package/dist/friendli-models.js.map +1 -0
  146. package/dist/friendli-reasoning.d.ts +10 -0
  147. package/dist/friendli-reasoning.d.ts.map +1 -0
  148. package/dist/friendli-reasoning.js +181 -0
  149. package/dist/friendli-reasoning.js.map +1 -0
  150. package/dist/friendli-reasoning.test.d.ts +2 -0
  151. package/dist/friendli-reasoning.test.d.ts.map +1 -0
  152. package/dist/friendli-reasoning.test.js +77 -0
  153. package/dist/friendli-reasoning.test.js.map +1 -0
  154. package/dist/index.d.ts +3 -0
  155. package/dist/index.d.ts.map +1 -0
  156. package/dist/index.js +3 -0
  157. package/dist/index.js.map +1 -0
  158. package/dist/interaction/colors.d.ts +22 -0
  159. package/dist/interaction/colors.d.ts.map +1 -0
  160. package/dist/interaction/colors.js +24 -0
  161. package/dist/interaction/colors.js.map +1 -0
  162. package/dist/interaction/pi-tui-stream-renderer.d.ts +19 -0
  163. package/dist/interaction/pi-tui-stream-renderer.d.ts.map +1 -0
  164. package/dist/interaction/pi-tui-stream-renderer.js +1509 -0
  165. package/dist/interaction/pi-tui-stream-renderer.js.map +1 -0
  166. package/dist/interaction/pi-tui-stream-renderer.test.d.ts +2 -0
  167. package/dist/interaction/pi-tui-stream-renderer.test.d.ts.map +1 -0
  168. package/dist/interaction/pi-tui-stream-renderer.test.js +1314 -0
  169. package/dist/interaction/pi-tui-stream-renderer.test.js.map +1 -0
  170. package/dist/interaction/spinner.d.ts +13 -0
  171. package/dist/interaction/spinner.d.ts.map +1 -0
  172. package/dist/interaction/spinner.js +51 -0
  173. package/dist/interaction/spinner.js.map +1 -0
  174. package/dist/middleware/index.d.ts +7 -0
  175. package/dist/middleware/index.d.ts.map +1 -0
  176. package/dist/middleware/index.js +15 -0
  177. package/dist/middleware/index.js.map +1 -0
  178. package/dist/middleware/todo-continuation.d.ts +11 -0
  179. package/dist/middleware/todo-continuation.d.ts.map +1 -0
  180. package/dist/middleware/todo-continuation.js +103 -0
  181. package/dist/middleware/todo-continuation.js.map +1 -0
  182. package/dist/middleware/trim-leading-newlines.d.ts +3 -0
  183. package/dist/middleware/trim-leading-newlines.d.ts.map +1 -0
  184. package/dist/middleware/trim-leading-newlines.js +49 -0
  185. package/dist/middleware/trim-leading-newlines.js.map +1 -0
  186. package/dist/reasoning-mode.d.ts +5 -0
  187. package/dist/reasoning-mode.d.ts.map +1 -0
  188. package/dist/reasoning-mode.js +30 -0
  189. package/dist/reasoning-mode.js.map +1 -0
  190. package/dist/reasoning-mode.test.d.ts +2 -0
  191. package/dist/reasoning-mode.test.d.ts.map +1 -0
  192. package/dist/reasoning-mode.test.js +22 -0
  193. package/dist/reasoning-mode.test.js.map +1 -0
  194. package/dist/tool-fallback-mode.d.ts +6 -0
  195. package/dist/tool-fallback-mode.d.ts.map +1 -0
  196. package/dist/tool-fallback-mode.js +25 -0
  197. package/dist/tool-fallback-mode.js.map +1 -0
  198. package/dist/tools/execute/shell-execute.d.ts +17 -0
  199. package/dist/tools/execute/shell-execute.d.ts.map +1 -0
  200. package/dist/tools/execute/shell-execute.js +55 -0
  201. package/dist/tools/execute/shell-execute.js.map +1 -0
  202. package/dist/tools/execute/shell-execute.test.d.ts +2 -0
  203. package/dist/tools/execute/shell-execute.test.d.ts.map +1 -0
  204. package/dist/tools/execute/shell-execute.test.js +86 -0
  205. package/dist/tools/execute/shell-execute.test.js.map +1 -0
  206. package/dist/tools/execute/shell-interact.d.ts +10 -0
  207. package/dist/tools/execute/shell-interact.d.ts.map +1 -0
  208. package/dist/tools/execute/shell-interact.js +122 -0
  209. package/dist/tools/execute/shell-interact.js.map +1 -0
  210. package/dist/tools/execute/shell-interact.test.d.ts +2 -0
  211. package/dist/tools/execute/shell-interact.test.d.ts.map +1 -0
  212. package/dist/tools/execute/shell-interact.test.js +175 -0
  213. package/dist/tools/execute/shell-interact.test.js.map +1 -0
  214. package/dist/tools/explore/glob.d.ts +15 -0
  215. package/dist/tools/explore/glob.d.ts.map +1 -0
  216. package/dist/tools/explore/glob.js +107 -0
  217. package/dist/tools/explore/glob.js.map +1 -0
  218. package/dist/tools/explore/glob.test.d.ts +2 -0
  219. package/dist/tools/explore/glob.test.d.ts.map +1 -0
  220. package/dist/tools/explore/glob.test.js +183 -0
  221. package/dist/tools/explore/glob.test.js.map +1 -0
  222. package/dist/tools/explore/grep.d.ts +27 -0
  223. package/dist/tools/explore/grep.d.ts.map +1 -0
  224. package/dist/tools/explore/grep.js +203 -0
  225. package/dist/tools/explore/grep.js.map +1 -0
  226. package/dist/tools/explore/grep.test.d.ts +2 -0
  227. package/dist/tools/explore/grep.test.d.ts.map +1 -0
  228. package/dist/tools/explore/grep.test.js +132 -0
  229. package/dist/tools/explore/grep.test.js.map +1 -0
  230. package/dist/tools/explore/read-file.d.ts +23 -0
  231. package/dist/tools/explore/read-file.d.ts.map +1 -0
  232. package/dist/tools/explore/read-file.js +84 -0
  233. package/dist/tools/explore/read-file.js.map +1 -0
  234. package/dist/tools/explore/read-file.test.d.ts +2 -0
  235. package/dist/tools/explore/read-file.test.d.ts.map +1 -0
  236. package/dist/tools/explore/read-file.test.js +278 -0
  237. package/dist/tools/explore/read-file.test.js.map +1 -0
  238. package/dist/tools/index.d.ts +71 -0
  239. package/dist/tools/index.d.ts.map +1 -0
  240. package/dist/tools/index.js +26 -0
  241. package/dist/tools/index.js.map +1 -0
  242. package/dist/tools/modify/delete-file.d.ts +19 -0
  243. package/dist/tools/modify/delete-file.d.ts.map +1 -0
  244. package/dist/tools/modify/delete-file.js +71 -0
  245. package/dist/tools/modify/delete-file.js.map +1 -0
  246. package/dist/tools/modify/delete-file.test.d.ts +2 -0
  247. package/dist/tools/modify/delete-file.test.d.ts.map +1 -0
  248. package/dist/tools/modify/delete-file.test.js +136 -0
  249. package/dist/tools/modify/delete-file.test.js.map +1 -0
  250. package/dist/tools/modify/edit-file-diagnostics.d.ts +17 -0
  251. package/dist/tools/modify/edit-file-diagnostics.d.ts.map +1 -0
  252. package/dist/tools/modify/edit-file-diagnostics.js +157 -0
  253. package/dist/tools/modify/edit-file-diagnostics.js.map +1 -0
  254. package/dist/tools/modify/edit-file-repair.d.ts +13 -0
  255. package/dist/tools/modify/edit-file-repair.d.ts.map +1 -0
  256. package/dist/tools/modify/edit-file-repair.js +135 -0
  257. package/dist/tools/modify/edit-file-repair.js.map +1 -0
  258. package/dist/tools/modify/edit-file-stress.test.d.ts +2 -0
  259. package/dist/tools/modify/edit-file-stress.test.d.ts.map +1 -0
  260. package/dist/tools/modify/edit-file-stress.test.js +163 -0
  261. package/dist/tools/modify/edit-file-stress.test.js.map +1 -0
  262. package/dist/tools/modify/edit-file-validation.d.ts +9 -0
  263. package/dist/tools/modify/edit-file-validation.d.ts.map +1 -0
  264. package/dist/tools/modify/edit-file-validation.js +86 -0
  265. package/dist/tools/modify/edit-file-validation.js.map +1 -0
  266. package/dist/tools/modify/edit-file-whitespace.test.d.ts +2 -0
  267. package/dist/tools/modify/edit-file-whitespace.test.d.ts.map +1 -0
  268. package/dist/tools/modify/edit-file-whitespace.test.js +90 -0
  269. package/dist/tools/modify/edit-file-whitespace.test.js.map +1 -0
  270. package/dist/tools/modify/edit-file.d.ts +33 -0
  271. package/dist/tools/modify/edit-file.d.ts.map +1 -0
  272. package/dist/tools/modify/edit-file.js +177 -0
  273. package/dist/tools/modify/edit-file.js.map +1 -0
  274. package/dist/tools/modify/edit-file.test.d.ts +2 -0
  275. package/dist/tools/modify/edit-file.test.d.ts.map +1 -0
  276. package/dist/tools/modify/edit-file.test.js +948 -0
  277. package/dist/tools/modify/edit-file.test.js.map +1 -0
  278. package/dist/tools/modify/write-file.d.ts +17 -0
  279. package/dist/tools/modify/write-file.d.ts.map +1 -0
  280. package/dist/tools/modify/write-file.js +39 -0
  281. package/dist/tools/modify/write-file.js.map +1 -0
  282. package/dist/tools/modify/write-file.test.d.ts +2 -0
  283. package/dist/tools/modify/write-file.test.d.ts.map +1 -0
  284. package/dist/tools/modify/write-file.test.js +168 -0
  285. package/dist/tools/modify/write-file.test.js.map +1 -0
  286. package/dist/tools/planning/load-skill.d.ts +13 -0
  287. package/dist/tools/planning/load-skill.d.ts.map +1 -0
  288. package/dist/tools/planning/load-skill.js +101 -0
  289. package/dist/tools/planning/load-skill.js.map +1 -0
  290. package/dist/tools/planning/load-skill.test.d.ts +2 -0
  291. package/dist/tools/planning/load-skill.test.d.ts.map +1 -0
  292. package/dist/tools/planning/load-skill.test.js +37 -0
  293. package/dist/tools/planning/load-skill.test.js.map +1 -0
  294. package/dist/tools/planning/todo-write.d.ts +49 -0
  295. package/dist/tools/planning/todo-write.d.ts.map +1 -0
  296. package/dist/tools/planning/todo-write.js +118 -0
  297. package/dist/tools/planning/todo-write.js.map +1 -0
  298. package/dist/tools/planning/todo-write.test.d.ts +2 -0
  299. package/dist/tools/planning/todo-write.test.d.ts.map +1 -0
  300. package/dist/tools/planning/todo-write.test.js +82 -0
  301. package/dist/tools/planning/todo-write.test.js.map +1 -0
  302. package/dist/tools/utils/execute/format-utils.d.ts +4 -0
  303. package/dist/tools/utils/execute/format-utils.d.ts.map +1 -0
  304. package/dist/tools/utils/execute/format-utils.js +31 -0
  305. package/dist/tools/utils/execute/format-utils.js.map +1 -0
  306. package/dist/tools/utils/execute/format-utils.test.d.ts +2 -0
  307. package/dist/tools/utils/execute/format-utils.test.d.ts.map +1 -0
  308. package/dist/tools/utils/execute/format-utils.test.js +40 -0
  309. package/dist/tools/utils/execute/format-utils.test.js.map +1 -0
  310. package/dist/tools/utils/execute/noninteractive-wrapper.d.ts +12 -0
  311. package/dist/tools/utils/execute/noninteractive-wrapper.d.ts.map +1 -0
  312. package/dist/tools/utils/execute/noninteractive-wrapper.js +269 -0
  313. package/dist/tools/utils/execute/noninteractive-wrapper.js.map +1 -0
  314. package/dist/tools/utils/execute/noninteractive-wrapper.test.d.ts +2 -0
  315. package/dist/tools/utils/execute/noninteractive-wrapper.test.d.ts.map +1 -0
  316. package/dist/tools/utils/execute/noninteractive-wrapper.test.js +233 -0
  317. package/dist/tools/utils/execute/noninteractive-wrapper.test.js.map +1 -0
  318. package/dist/tools/utils/execute/output-handler.d.ts +14 -0
  319. package/dist/tools/utils/execute/output-handler.d.ts.map +1 -0
  320. package/dist/tools/utils/execute/output-handler.js +71 -0
  321. package/dist/tools/utils/execute/output-handler.js.map +1 -0
  322. package/dist/tools/utils/execute/output-handler.test.d.ts +2 -0
  323. package/dist/tools/utils/execute/output-handler.test.d.ts.map +1 -0
  324. package/dist/tools/utils/execute/output-handler.test.js +58 -0
  325. package/dist/tools/utils/execute/output-handler.test.js.map +1 -0
  326. package/dist/tools/utils/execute/process-manager.d.ts +17 -0
  327. package/dist/tools/utils/execute/process-manager.d.ts.map +1 -0
  328. package/dist/tools/utils/execute/process-manager.js +229 -0
  329. package/dist/tools/utils/execute/process-manager.js.map +1 -0
  330. package/dist/tools/utils/execute/process-manager.test.d.ts +2 -0
  331. package/dist/tools/utils/execute/process-manager.test.d.ts.map +1 -0
  332. package/dist/tools/utils/execute/process-manager.test.js +139 -0
  333. package/dist/tools/utils/execute/process-manager.test.js.map +1 -0
  334. package/dist/tools/utils/execute/shell-detection.d.ts +3 -0
  335. package/dist/tools/utils/execute/shell-detection.d.ts.map +1 -0
  336. package/dist/tools/utils/execute/shell-detection.js +56 -0
  337. package/dist/tools/utils/execute/shell-detection.js.map +1 -0
  338. package/dist/tools/utils/execute/shell-detection.test.d.ts +2 -0
  339. package/dist/tools/utils/execute/shell-detection.test.d.ts.map +1 -0
  340. package/dist/tools/utils/execute/shell-detection.test.js +86 -0
  341. package/dist/tools/utils/execute/shell-detection.test.js.map +1 -0
  342. package/dist/tools/utils/hashline/autocorrect-replacement-lines.d.ts +5 -0
  343. package/dist/tools/utils/hashline/autocorrect-replacement-lines.d.ts.map +1 -0
  344. package/dist/tools/utils/hashline/autocorrect-replacement-lines.js +113 -0
  345. package/dist/tools/utils/hashline/autocorrect-replacement-lines.js.map +1 -0
  346. package/dist/tools/utils/hashline/constants.d.ts +5 -0
  347. package/dist/tools/utils/hashline/constants.d.ts.map +1 -0
  348. package/dist/tools/utils/hashline/constants.js +11 -0
  349. package/dist/tools/utils/hashline/constants.js.map +1 -0
  350. package/dist/tools/utils/hashline/diff-utils.d.ts +7 -0
  351. package/dist/tools/utils/hashline/diff-utils.d.ts.map +1 -0
  352. package/dist/tools/utils/hashline/diff-utils.js +50 -0
  353. package/dist/tools/utils/hashline/diff-utils.js.map +1 -0
  354. package/dist/tools/utils/hashline/diff-utils.test.d.ts +2 -0
  355. package/dist/tools/utils/hashline/diff-utils.test.d.ts.map +1 -0
  356. package/dist/tools/utils/hashline/diff-utils.test.js +46 -0
  357. package/dist/tools/utils/hashline/diff-utils.test.js.map +1 -0
  358. package/dist/tools/utils/hashline/edit-deduplication.d.ts +6 -0
  359. package/dist/tools/utils/hashline/edit-deduplication.d.ts.map +1 -0
  360. package/dist/tools/utils/hashline/edit-deduplication.js +32 -0
  361. package/dist/tools/utils/hashline/edit-deduplication.js.map +1 -0
  362. package/dist/tools/utils/hashline/edit-operation-primitives.d.ts +11 -0
  363. package/dist/tools/utils/hashline/edit-operation-primitives.d.ts.map +1 -0
  364. package/dist/tools/utils/hashline/edit-operation-primitives.js +93 -0
  365. package/dist/tools/utils/hashline/edit-operation-primitives.js.map +1 -0
  366. package/dist/tools/utils/hashline/edit-operations.d.ts +9 -0
  367. package/dist/tools/utils/hashline/edit-operations.d.ts.map +1 -0
  368. package/dist/tools/utils/hashline/edit-operations.js +101 -0
  369. package/dist/tools/utils/hashline/edit-operations.js.map +1 -0
  370. package/dist/tools/utils/hashline/edit-operations.test.d.ts +2 -0
  371. package/dist/tools/utils/hashline/edit-operations.test.d.ts.map +1 -0
  372. package/dist/tools/utils/hashline/edit-operations.test.js +135 -0
  373. package/dist/tools/utils/hashline/edit-operations.test.js.map +1 -0
  374. package/dist/tools/utils/hashline/edit-ordering.d.ts +5 -0
  375. package/dist/tools/utils/hashline/edit-ordering.d.ts.map +1 -0
  376. package/dist/tools/utils/hashline/edit-ordering.js +54 -0
  377. package/dist/tools/utils/hashline/edit-ordering.js.map +1 -0
  378. package/dist/tools/utils/hashline/edit-text-normalization.d.ts +9 -0
  379. package/dist/tools/utils/hashline/edit-text-normalization.d.ts.map +1 -0
  380. package/dist/tools/utils/hashline/edit-text-normalization.js +135 -0
  381. package/dist/tools/utils/hashline/edit-text-normalization.js.map +1 -0
  382. package/dist/tools/utils/hashline/file-text-canonicalization.d.ts +8 -0
  383. package/dist/tools/utils/hashline/file-text-canonicalization.d.ts.map +1 -0
  384. package/dist/tools/utils/hashline/file-text-canonicalization.js +42 -0
  385. package/dist/tools/utils/hashline/file-text-canonicalization.js.map +1 -0
  386. package/dist/tools/utils/hashline/hash-computation.d.ts +14 -0
  387. package/dist/tools/utils/hashline/hash-computation.d.ts.map +1 -0
  388. package/dist/tools/utils/hashline/hash-computation.js +158 -0
  389. package/dist/tools/utils/hashline/hash-computation.js.map +1 -0
  390. package/dist/tools/utils/hashline/hash-computation.test.d.ts +2 -0
  391. package/dist/tools/utils/hashline/hash-computation.test.d.ts.map +1 -0
  392. package/dist/tools/utils/hashline/hash-computation.test.js +63 -0
  393. package/dist/tools/utils/hashline/hash-computation.test.js.map +1 -0
  394. package/dist/tools/utils/hashline/hashline-chunk-formatter.d.ts +11 -0
  395. package/dist/tools/utils/hashline/hashline-chunk-formatter.d.ts.map +1 -0
  396. package/dist/tools/utils/hashline/hashline-chunk-formatter.js +41 -0
  397. package/dist/tools/utils/hashline/hashline-chunk-formatter.js.map +1 -0
  398. package/dist/tools/utils/hashline/hashline-edit-diff.d.ts +2 -0
  399. package/dist/tools/utils/hashline/hashline-edit-diff.d.ts.map +1 -0
  400. package/dist/tools/utils/hashline/hashline-edit-diff.js +27 -0
  401. package/dist/tools/utils/hashline/hashline-edit-diff.js.map +1 -0
  402. package/dist/tools/utils/hashline/index.d.ts +14 -0
  403. package/dist/tools/utils/hashline/index.d.ts.map +1 -0
  404. package/dist/tools/utils/hashline/index.js +11 -0
  405. package/dist/tools/utils/hashline/index.js.map +1 -0
  406. package/dist/tools/utils/hashline/merge-expansion.d.ts +4 -0
  407. package/dist/tools/utils/hashline/merge-expansion.d.ts.map +1 -0
  408. package/dist/tools/utils/hashline/merge-expansion.js +105 -0
  409. package/dist/tools/utils/hashline/merge-expansion.js.map +1 -0
  410. package/dist/tools/utils/hashline/normalize-edits.d.ts +11 -0
  411. package/dist/tools/utils/hashline/normalize-edits.d.ts.map +1 -0
  412. package/dist/tools/utils/hashline/normalize-edits.js +81 -0
  413. package/dist/tools/utils/hashline/normalize-edits.js.map +1 -0
  414. package/dist/tools/utils/hashline/types.d.ts +18 -0
  415. package/dist/tools/utils/hashline/types.d.ts.map +1 -0
  416. package/dist/tools/utils/hashline/types.js +2 -0
  417. package/dist/tools/utils/hashline/types.js.map +1 -0
  418. package/dist/tools/utils/hashline/validation.d.ts +19 -0
  419. package/dist/tools/utils/hashline/validation.d.ts.map +1 -0
  420. package/dist/tools/utils/hashline/validation.js +161 -0
  421. package/dist/tools/utils/hashline/validation.js.map +1 -0
  422. package/dist/tools/utils/hashline/validation.test.d.ts +2 -0
  423. package/dist/tools/utils/hashline/validation.test.d.ts.map +1 -0
  424. package/dist/tools/utils/hashline/validation.test.js +86 -0
  425. package/dist/tools/utils/hashline/validation.test.js.map +1 -0
  426. package/dist/tools/utils/safety-utils.d.ts +66 -0
  427. package/dist/tools/utils/safety-utils.d.ts.map +1 -0
  428. package/dist/tools/utils/safety-utils.js +681 -0
  429. package/dist/tools/utils/safety-utils.js.map +1 -0
  430. package/dist/utils/tools-manager.d.ts +16 -0
  431. package/dist/utils/tools-manager.d.ts.map +1 -0
  432. package/dist/utils/tools-manager.js +257 -0
  433. package/dist/utils/tools-manager.js.map +1 -0
  434. package/package.json +49 -0
  435. package/src/AGENTS.md +52 -0
  436. package/src/agent-reasoning-default.test.ts +48 -0
  437. package/src/agent.test.ts +49 -0
  438. package/src/agent.ts +455 -0
  439. package/src/cli-args.test.ts +152 -0
  440. package/src/cli-args.ts +90 -0
  441. package/src/commands/aliases-and-tool-fallback.test.ts +172 -0
  442. package/src/commands/clear.ts +14 -0
  443. package/src/commands/factories/create-toggle-command.ts +68 -0
  444. package/src/commands/help.ts +30 -0
  445. package/src/commands/index.ts +125 -0
  446. package/src/commands/model.ts +146 -0
  447. package/src/commands/reasoning-mode.ts +55 -0
  448. package/src/commands/render.test.ts +47 -0
  449. package/src/commands/render.ts +205 -0
  450. package/src/commands/tool-fallback.ts +47 -0
  451. package/src/commands/translate.test.ts +62 -0
  452. package/src/commands/translate.ts +14 -0
  453. package/src/commands/types.ts +18 -0
  454. package/src/context/environment-context.ts +11 -0
  455. package/src/context/paths.ts +2 -0
  456. package/src/context/session.ts +18 -0
  457. package/src/context/skill-command-prefix.ts +18 -0
  458. package/src/context/skills-integration.test.ts +113 -0
  459. package/src/context/skills.test.ts +25 -0
  460. package/src/context/skills.ts +566 -0
  461. package/src/context/system-prompt.ts +100 -0
  462. package/src/context/translation-integration.test.ts +194 -0
  463. package/src/context/translation.test.ts +186 -0
  464. package/src/context/translation.ts +122 -0
  465. package/src/entrypoints/AGENTS.md +33 -0
  466. package/src/entrypoints/cli-input-rendering.test.ts +236 -0
  467. package/src/entrypoints/cli.ts +1845 -0
  468. package/src/entrypoints/headless-agent-config.test.ts +82 -0
  469. package/src/entrypoints/headless-agent-config.ts +42 -0
  470. package/src/entrypoints/headless.ts +622 -0
  471. package/src/env.ts +14 -0
  472. package/src/friendli-models.ts +81 -0
  473. package/src/friendli-reasoning.test.ts +147 -0
  474. package/src/friendli-reasoning.ts +280 -0
  475. package/src/index.ts +3 -0
  476. package/src/interaction/colors.ts +24 -0
  477. package/src/interaction/pi-tui-stream-renderer.test.ts +1471 -0
  478. package/src/interaction/pi-tui-stream-renderer.ts +2150 -0
  479. package/src/interaction/spinner.ts +61 -0
  480. package/src/middleware/index.ts +32 -0
  481. package/src/middleware/todo-continuation.ts +128 -0
  482. package/src/middleware/trim-leading-newlines.ts +66 -0
  483. package/src/reasoning-mode.test.ts +24 -0
  484. package/src/reasoning-mode.ts +40 -0
  485. package/src/skills/example/SKILL.md +44 -0
  486. package/src/skills/example/references/api.md +37 -0
  487. package/src/skills/example/scripts/setup.sh +13 -0
  488. package/src/skills/git-workflow.md +405 -0
  489. package/src/tool-fallback-mode.ts +34 -0
  490. package/src/tools/AGENTS.md +44 -0
  491. package/src/tools/execute/shell-execute.test.ts +114 -0
  492. package/src/tools/execute/shell-execute.ts +74 -0
  493. package/src/tools/execute/shell-execute.txt +27 -0
  494. package/src/tools/execute/shell-interact.test.ts +236 -0
  495. package/src/tools/execute/shell-interact.ts +151 -0
  496. package/src/tools/execute/shell-interact.txt +15 -0
  497. package/src/tools/explore/glob-files.txt +8 -0
  498. package/src/tools/explore/glob.test.ts +217 -0
  499. package/src/tools/explore/glob.ts +137 -0
  500. package/src/tools/explore/grep-files.txt +12 -0
  501. package/src/tools/explore/grep.test.ts +183 -0
  502. package/src/tools/explore/grep.ts +266 -0
  503. package/src/tools/explore/read-file.test.ts +355 -0
  504. package/src/tools/explore/read-file.ts +102 -0
  505. package/src/tools/explore/read-file.txt +24 -0
  506. package/src/tools/index.ts +29 -0
  507. package/src/tools/modify/AGENTS.md +38 -0
  508. package/src/tools/modify/delete-file.test.ts +200 -0
  509. package/src/tools/modify/delete-file.ts +95 -0
  510. package/src/tools/modify/delete-file.txt +9 -0
  511. package/src/tools/modify/edit-file-diagnostics.ts +210 -0
  512. package/src/tools/modify/edit-file-repair.ts +183 -0
  513. package/src/tools/modify/edit-file-stress.test.ts +200 -0
  514. package/src/tools/modify/edit-file-validation.ts +134 -0
  515. package/src/tools/modify/edit-file-whitespace.test.ts +117 -0
  516. package/src/tools/modify/edit-file.test.ts +1231 -0
  517. package/src/tools/modify/edit-file.ts +252 -0
  518. package/src/tools/modify/edit-file.txt +73 -0
  519. package/src/tools/modify/write-file.test.ts +240 -0
  520. package/src/tools/modify/write-file.ts +56 -0
  521. package/src/tools/modify/write-file.txt +9 -0
  522. package/src/tools/planning/load-skill.test.ts +48 -0
  523. package/src/tools/planning/load-skill.ts +136 -0
  524. package/src/tools/planning/load-skill.txt +6 -0
  525. package/src/tools/planning/todo-write.test.ts +91 -0
  526. package/src/tools/planning/todo-write.ts +141 -0
  527. package/src/tools/planning/todo-write.txt +7 -0
  528. package/src/tools/utils/execute/format-utils.test.ts +53 -0
  529. package/src/tools/utils/execute/format-utils.ts +37 -0
  530. package/src/tools/utils/execute/noninteractive-wrapper.test.ts +306 -0
  531. package/src/tools/utils/execute/noninteractive-wrapper.ts +314 -0
  532. package/src/tools/utils/execute/output-handler.test.ts +72 -0
  533. package/src/tools/utils/execute/output-handler.ts +101 -0
  534. package/src/tools/utils/execute/process-manager.test.ts +175 -0
  535. package/src/tools/utils/execute/process-manager.ts +310 -0
  536. package/src/tools/utils/execute/shell-detection.test.ts +112 -0
  537. package/src/tools/utils/execute/shell-detection.ts +72 -0
  538. package/src/tools/utils/hashline/autocorrect-replacement-lines.ts +159 -0
  539. package/src/tools/utils/hashline/constants.ts +13 -0
  540. package/src/tools/utils/hashline/diff-utils.test.ts +61 -0
  541. package/src/tools/utils/hashline/diff-utils.ts +64 -0
  542. package/src/tools/utils/hashline/edit-deduplication.ts +40 -0
  543. package/src/tools/utils/hashline/edit-operation-primitives.ts +149 -0
  544. package/src/tools/utils/hashline/edit-operations.test.ts +154 -0
  545. package/src/tools/utils/hashline/edit-operations.ts +132 -0
  546. package/src/tools/utils/hashline/edit-ordering.ts +60 -0
  547. package/src/tools/utils/hashline/edit-text-normalization.ts +180 -0
  548. package/src/tools/utils/hashline/file-text-canonicalization.ts +58 -0
  549. package/src/tools/utils/hashline/hash-computation.test.ts +82 -0
  550. package/src/tools/utils/hashline/hash-computation.ts +199 -0
  551. package/src/tools/utils/hashline/hashline-chunk-formatter.ts +61 -0
  552. package/src/tools/utils/hashline/hashline-edit-diff.ts +35 -0
  553. package/src/tools/utils/hashline/index.ts +55 -0
  554. package/src/tools/utils/hashline/merge-expansion.ts +120 -0
  555. package/src/tools/utils/hashline/normalize-edits.ts +127 -0
  556. package/src/tools/utils/hashline/types.ts +20 -0
  557. package/src/tools/utils/hashline/validation.test.ts +109 -0
  558. package/src/tools/utils/hashline/validation.ts +212 -0
  559. package/src/tools/utils/safety-utils.ts +938 -0
  560. package/src/utils/tools-manager.ts +353 -0
@@ -0,0 +1,200 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from "bun:test";
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ mkdtempSync,
6
+ readFileSync,
7
+ rmSync,
8
+ symlinkSync,
9
+ writeFileSync,
10
+ } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { executeDeleteFile } from "./delete-file";
14
+
15
+ describe("executeDeleteFile", () => {
16
+ let tempDir: string;
17
+
18
+ beforeAll(() => {
19
+ tempDir = mkdtempSync(join(tmpdir(), "delete-file-test-"));
20
+ });
21
+
22
+ afterAll(() => {
23
+ if (existsSync(tempDir)) {
24
+ rmSync(tempDir, { recursive: true });
25
+ }
26
+ });
27
+
28
+ describe("file deletion", () => {
29
+ it("deletes file and returns metadata", async () => {
30
+ const testFile = join(tempDir, "to-delete.txt");
31
+ writeFileSync(testFile, "content to delete");
32
+
33
+ const result = await executeDeleteFile({ path: testFile }, { rootDir: tempDir });
34
+
35
+ expect(result).toContain("OK - deleted file: to-delete.txt");
36
+ expect(result).toContain(`path: ${testFile}`);
37
+ expect(result).toContain("bytes:");
38
+ expect(result).toContain("last_modified:");
39
+ expect(existsSync(testFile)).toBe(false);
40
+ });
41
+
42
+ it("includes correct byte size in response", async () => {
43
+ const testFile = join(tempDir, "sized.txt");
44
+ const content = "12345";
45
+ writeFileSync(testFile, content);
46
+
47
+ const result = await executeDeleteFile({ path: testFile }, { rootDir: tempDir });
48
+
49
+ expect(result).toContain("bytes: 5");
50
+ });
51
+ });
52
+
53
+ describe("directory deletion", () => {
54
+ it("throws error for directory without recursive flag", async () => {
55
+ const testDir = join(tempDir, "dir-no-recursive");
56
+ mkdirSync(testDir);
57
+
58
+ await expect(
59
+ executeDeleteFile({ path: testDir }, { rootDir: tempDir })
60
+ ).rejects.toThrow("recursive: true");
61
+
62
+ expect(existsSync(testDir)).toBe(true);
63
+ });
64
+
65
+ it("deletes empty directory with recursive flag", async () => {
66
+ const testDir = join(tempDir, "empty-dir");
67
+ mkdirSync(testDir);
68
+
69
+ const result = await executeDeleteFile(
70
+ { path: testDir, recursive: true },
71
+ { rootDir: tempDir }
72
+ );
73
+
74
+ expect(result).toContain("OK - deleted directory: empty-dir");
75
+ expect(result).toContain(`path: ${testDir}`);
76
+ expect(result).toContain("last_modified:");
77
+ expect(existsSync(testDir)).toBe(false);
78
+ });
79
+
80
+ it("deletes non-empty directory with recursive flag", async () => {
81
+ const testDir = join(tempDir, "non-empty-dir");
82
+ mkdirSync(testDir);
83
+ writeFileSync(join(testDir, "file1.txt"), "content1");
84
+ writeFileSync(join(testDir, "file2.txt"), "content2");
85
+ mkdirSync(join(testDir, "subdir"));
86
+ writeFileSync(join(testDir, "subdir", "nested.txt"), "nested");
87
+
88
+ const result = await executeDeleteFile(
89
+ { path: testDir, recursive: true },
90
+ { rootDir: tempDir }
91
+ );
92
+
93
+ expect(result).toContain("OK - deleted directory: non-empty-dir");
94
+ expect(existsSync(testDir)).toBe(false);
95
+ });
96
+ });
97
+
98
+ describe("ignore_missing option", () => {
99
+ it("throws error for non-existent file by default", async () => {
100
+ const nonExistent = join(tempDir, "does-not-exist.txt");
101
+
102
+ await expect(executeDeleteFile({ path: nonExistent }, { rootDir: tempDir })).rejects.toThrow();
103
+ });
104
+
105
+ it("returns skip message when ignore_missing is true", async () => {
106
+ const nonExistent = join(tempDir, "also-does-not-exist.txt");
107
+
108
+ const result = await executeDeleteFile(
109
+ { path: nonExistent, ignore_missing: true },
110
+ { rootDir: tempDir }
111
+ );
112
+
113
+ expect(result).toContain("SKIPPED");
114
+ expect(result).toContain("file does not exist");
115
+ expect(result).toContain(nonExistent);
116
+ });
117
+ });
118
+
119
+ describe("edge cases", () => {
120
+ it("handles file with special characters in name", async () => {
121
+ const testFile = join(tempDir, "file with spaces.txt");
122
+ writeFileSync(testFile, "content");
123
+
124
+ const result = await executeDeleteFile({ path: testFile }, { rootDir: tempDir });
125
+
126
+ expect(result).toContain("OK - deleted file: file with spaces.txt");
127
+ expect(existsSync(testFile)).toBe(false);
128
+ });
129
+
130
+ it("handles empty file", async () => {
131
+ const testFile = join(tempDir, "empty-file.txt");
132
+ writeFileSync(testFile, "");
133
+
134
+ const result = await executeDeleteFile({ path: testFile }, { rootDir: tempDir });
135
+
136
+ expect(result).toContain("OK - deleted file: empty-file.txt");
137
+ expect(result).toContain("bytes: 0");
138
+ expect(existsSync(testFile)).toBe(false);
139
+ });
140
+ });
141
+
142
+ describe("file safety (C-1, C-2)", () => {
143
+ it("C-1: blocks path traversal via .. segments", async () => {
144
+ const traversalPath = join(tempDir, "..", "..", "etc", "passwd");
145
+ await expect(
146
+ executeDeleteFile({ path: traversalPath }, { rootDir: tempDir })
147
+ ).rejects.toThrow(/[Pp]ath traversal blocked/);
148
+ });
149
+
150
+ it("C-1: blocks absolute paths outside project root", async () => {
151
+ await expect(
152
+ executeDeleteFile({ path: "/tmp/outside-project.txt" }, { rootDir: tempDir })
153
+ ).rejects.toThrow(/[Pp]ath traversal blocked|outside/);
154
+ });
155
+
156
+ it("C-2: blocks deletion of symlinks", async () => {
157
+ const realFile = join(tempDir, "real-delete-target.txt");
158
+ writeFileSync(realFile, "real content");
159
+ const symlinkPath = join(tempDir, "symlink-to-delete.txt");
160
+ symlinkSync(realFile, symlinkPath);
161
+
162
+ await expect(
163
+ executeDeleteFile({ path: symlinkPath }, { rootDir: tempDir })
164
+ ).rejects.toThrow(/symlink/i);
165
+
166
+ // Both the symlink and the real file should still exist
167
+ expect(existsSync(symlinkPath)).toBe(true);
168
+ expect(existsSync(realFile)).toBe(true);
169
+ expect(readFileSync(realFile, "utf-8")).toBe("real content");
170
+ });
171
+
172
+ it("C-2: blocks deletion of symlinks pointing outside root", async () => {
173
+ const outsideDir = mkdtempSync(join(tmpdir(), "outside-delete-"));
174
+ const outsideFile = join(outsideDir, "secret.txt");
175
+ writeFileSync(outsideFile, "secret data");
176
+ const symlinkPath = join(tempDir, "escape-delete-link.txt");
177
+ symlinkSync(outsideFile, symlinkPath);
178
+
179
+ try {
180
+ await expect(
181
+ executeDeleteFile({ path: symlinkPath }, { rootDir: tempDir })
182
+ ).rejects.toThrow(/symlink/i);
183
+ expect(existsSync(outsideFile)).toBe(true);
184
+ expect(readFileSync(outsideFile, "utf-8")).toBe("secret data");
185
+ } finally {
186
+ rmSync(outsideDir, { recursive: true });
187
+ }
188
+ });
189
+
190
+ it("allows deletion within project root (normal operation)", async () => {
191
+ const safeFile = join(tempDir, "safe-to-delete.txt");
192
+ writeFileSync(safeFile, "deletable content");
193
+
194
+ const result = await executeDeleteFile({ path: safeFile }, { rootDir: tempDir });
195
+
196
+ expect(result).toContain("OK - deleted file: safe-to-delete.txt");
197
+ expect(existsSync(safeFile)).toBe(false);
198
+ });
199
+ });
200
+ });
@@ -0,0 +1,95 @@
1
+ import { lstat, rm } from "node:fs/promises";
2
+ import { basename } from "node:path";
3
+ import { tool } from "ai";
4
+ import { z } from "zod";
5
+ import { assertWriteSafety } from "../utils/safety-utils";
6
+ import DELETE_FILE_DESCRIPTION from "./delete-file.txt";
7
+
8
+ const inputSchema = z.object({
9
+ path: z.string().describe("Path to delete"),
10
+ recursive: z
11
+ .boolean()
12
+ .optional()
13
+ .default(false)
14
+ .describe("Delete directories recursively (default: false)"),
15
+ ignore_missing: z
16
+ .boolean()
17
+ .optional()
18
+ .default(false)
19
+ .describe("Don't error if file doesn't exist (default: false)"),
20
+ });
21
+
22
+ export type DeleteFileInput = z.input<typeof inputSchema>;
23
+
24
+ export interface DeleteFileOptions {
25
+ /** Override project root for safety checks (defaults to process.cwd()). */
26
+ rootDir?: string;
27
+ }
28
+
29
+ export async function executeDeleteFile(
30
+ { path, recursive = false, ignore_missing = false }: DeleteFileInput,
31
+ options?: DeleteFileOptions
32
+ ): Promise<string> {
33
+ // C-1 + C-2: Path traversal and symlink safety checks
34
+ const safePath = await assertWriteSafety(path, options?.rootDir);
35
+
36
+ // C-2: Use lstat (not stat) — lstat does NOT follow symlinks,
37
+ // so we see the symlink entry itself rather than its target.
38
+ let stats: Awaited<ReturnType<typeof lstat>>;
39
+ try {
40
+ stats = await lstat(safePath);
41
+ } catch (error) {
42
+ if (
43
+ ignore_missing &&
44
+ error instanceof Error &&
45
+ "code" in error &&
46
+ (error as NodeJS.ErrnoException).code === "ENOENT"
47
+ ) {
48
+ return `SKIPPED - file does not exist: ${path}`;
49
+ }
50
+ throw error;
51
+ }
52
+
53
+ // Reject symlink deletion — force explicit resolution
54
+ if (stats.isSymbolicLink()) {
55
+ throw new Error(
56
+ `Refusing to delete symlink: '${path}'. ` +
57
+ "Resolve the symlink first or operate on the real file directly."
58
+ );
59
+ }
60
+
61
+ const isDirectory = stats.isDirectory();
62
+ const fileName = basename(path);
63
+ const byteSize = isDirectory ? null : stats.size;
64
+ const mtime = stats.mtime.toISOString();
65
+
66
+ if (isDirectory && !recursive) {
67
+ throw new Error(
68
+ `Cannot delete directory '${path}' without recursive: true. ` +
69
+ "Set recursive: true to delete directories."
70
+ );
71
+ }
72
+
73
+ await rm(safePath, { recursive, force: false });
74
+
75
+ const output: string[] = [];
76
+
77
+ if (isDirectory) {
78
+ output.push(`OK - deleted directory: ${fileName}`);
79
+ output.push(`path: ${path}`);
80
+ output.push(`last_modified: ${mtime}`);
81
+ } else {
82
+ output.push(`OK - deleted file: ${fileName}`);
83
+ output.push(`path: ${path}`);
84
+ output.push(`bytes: ${byteSize}`);
85
+ output.push(`last_modified: ${mtime}`);
86
+ }
87
+
88
+ return output.join("\n");
89
+ }
90
+
91
+ export const deleteFileTool = tool({
92
+ description: DELETE_FILE_DESCRIPTION,
93
+ inputSchema,
94
+ execute: (input) => executeDeleteFile(input),
95
+ });
@@ -0,0 +1,9 @@
1
+ ### delete_file
2
+ **Purpose**: Permanently delete a file or directory
3
+
4
+ **When to use**:
5
+ - Removing obsolete files
6
+ - Cleaning temporary artifacts
7
+ - Deleting directories with explicit recursive intent
8
+
9
+ **DO NOT use shell**: Prefer this over `rm` / `rm -rf`
@@ -0,0 +1,210 @@
1
+ import { computeLineHash, tryParseLineTag } from "../utils/hashline";
2
+
3
+ const ANCHOR_HASH_EXTRACT_REGEX = /#([ZPMQVRWSNKTXJBYH]{2})/i;
4
+ const NUMERIC_LINE_PREFIX_REGEX = /^\d+$/;
5
+ const HASHLINE_ALPHABET_PREFIX_REGEX = /^[ZPMQVRWSNKTXJBYH]{2}$/i;
6
+ const ANCHOR_PREFIX_REGEX = /^\s*(\d+\s*#\s*[ZPMQVRWSNKTXJBYH]{2})/i;
7
+ const HASH_SPACE_REGEX = /\s*#\s*/;
8
+ const KEY_VALUE_IN_POS_REGEX = /['"]lines['"\s]*:/;
9
+ const XML_MARKUP_IN_POS_REGEX = /<\/?parameter/;
10
+ const POS_DISPLAY_MAX_LENGTH = 80;
11
+
12
+ const missingLinesFailures = new Map<string, number>();
13
+ const missingLinesFileFailures = new Map<string, number>();
14
+ export const ESCALATION_THRESHOLD = 2;
15
+ export const ESCALATION_EXAMPLE_THRESHOLD = 4;
16
+ export const ESCALATION_BAIL_THRESHOLD = 6;
17
+ export const FILE_BAIL_THRESHOLD = 10;
18
+
19
+ export function trackMissingLinesFailure(
20
+ pos: string | undefined,
21
+ filePath?: string
22
+ ): number {
23
+ if (filePath) {
24
+ const fileCount = (missingLinesFileFailures.get(filePath) ?? 0) + 1;
25
+ missingLinesFileFailures.set(filePath, fileCount);
26
+ }
27
+ if (!pos) {
28
+ return 1;
29
+ }
30
+ const key = pos.trim();
31
+ const count = (missingLinesFailures.get(key) ?? 0) + 1;
32
+ missingLinesFailures.set(key, count);
33
+ return count;
34
+ }
35
+
36
+ export function resetMissingLinesFailures(): void {
37
+ missingLinesFailures.clear();
38
+ missingLinesFileFailures.clear();
39
+ }
40
+
41
+ export function getMissingLinesFailureCount(pos: string): number {
42
+ return missingLinesFailures.get(pos) ?? 0;
43
+ }
44
+
45
+ export function getFileFailureCount(filePath: string): number {
46
+ return missingLinesFileFailures.get(filePath) ?? 0;
47
+ }
48
+
49
+ export function suggestLineForHash(
50
+ raw: string,
51
+ fileLines: string[]
52
+ ): string | null {
53
+ const hashMatch = raw.match(ANCHOR_HASH_EXTRACT_REGEX);
54
+ if (!hashMatch) {
55
+ return null;
56
+ }
57
+ const hash = hashMatch[1].toUpperCase();
58
+ for (let i = 0; i < fileLines.length; i += 1) {
59
+ if (computeLineHash(i + 1, fileLines[i] ?? "") === hash) {
60
+ return `Did you mean "${i + 1}#${hash}"?`;
61
+ }
62
+ }
63
+ return null;
64
+ }
65
+
66
+ export function diagnoseAnchorFailure(
67
+ raw: string,
68
+ fieldName: string,
69
+ fileLines?: string[]
70
+ ): string {
71
+ const hashIdx = raw.indexOf("#");
72
+ let message: string;
73
+
74
+ if (hashIdx > 0) {
75
+ const prefix = raw.slice(0, hashIdx).trim();
76
+ const suffix = raw.slice(hashIdx + 1).trim();
77
+
78
+ if (!NUMERIC_LINE_PREFIX_REGEX.test(prefix)) {
79
+ message = `${fieldName} "${raw}": "${prefix}" is not a line number. Use the actual line number from read_file output.`;
80
+ } else if (HASHLINE_ALPHABET_PREFIX_REGEX.test(suffix)) {
81
+ message = `${fieldName} "${raw}" is not a valid {line_number}#{hash_id} anchor.`;
82
+ } else {
83
+ message = `${fieldName} "${raw}": hash portion "${suffix}" is not valid. Hash must be 2 characters from the hashline alphabet.`;
84
+ }
85
+ } else {
86
+ message = `${fieldName} "${raw}" is not a valid {line_number}#{hash_id} anchor — missing # separator.`;
87
+ }
88
+
89
+ if (fileLines && fileLines.length > 0) {
90
+ const hint = suggestLineForHash(raw, fileLines);
91
+ if (hint) {
92
+ message += ` ${hint}`;
93
+ }
94
+ }
95
+
96
+ return message;
97
+ }
98
+
99
+ export function diagnoseMissingLines(pos: string | undefined): string {
100
+ const base = "replace requires explicit 'lines' field.";
101
+
102
+ if (!pos || pos.trim().length === 0) {
103
+ return base;
104
+ }
105
+
106
+ const truncated =
107
+ pos.length > POS_DISPLAY_MAX_LENGTH
108
+ ? `${pos.slice(0, POS_DISPLAY_MAX_LENGTH - 3)}...`
109
+ : pos;
110
+
111
+ if (KEY_VALUE_IN_POS_REGEX.test(pos)) {
112
+ return `${base} Detected key-value syntax in pos — 'pos' and 'lines' must be separate JSON fields.`;
113
+ }
114
+
115
+ if (XML_MARKUP_IN_POS_REGEX.test(pos)) {
116
+ return `${base} Detected XML markup in pos — use JSON fields: pos for anchor, lines for content.`;
117
+ }
118
+
119
+ const anchorMatch = pos.match(ANCHOR_PREFIX_REGEX);
120
+ if (anchorMatch) {
121
+ const anchorText = anchorMatch[1].replace(HASH_SPACE_REGEX, "#");
122
+ const rest = pos.slice(anchorMatch[0].length);
123
+ if (rest.trim().length > 0) {
124
+ return `${base} pos contains content after anchor '${anchorText}' — move replacement text to 'lines'.`;
125
+ }
126
+ return `${base} Add 'lines' with the replacement content for anchor '${anchorText}'.`;
127
+ }
128
+
129
+ return `${base} pos should contain only a {line_number}#{hash_id} anchor, not content. Got: "${truncated}".`;
130
+ }
131
+
132
+ export function getEscalatedHint(
133
+ pos: string | undefined,
134
+ fileLines: string[],
135
+ failureCount: number
136
+ ): string | null {
137
+ if (!pos || fileLines.length === 0) {
138
+ return null;
139
+ }
140
+ const parsed = tryParseLineTag(pos);
141
+ if (!parsed) {
142
+ return null;
143
+ }
144
+ const lineIndex = parsed.line - 1;
145
+ if (lineIndex < 0 || lineIndex >= fileLines.length) {
146
+ return null;
147
+ }
148
+ const content = fileLines[lineIndex];
149
+ const anchor = `${parsed.line}#${parsed.hash}`;
150
+
151
+ if (failureCount >= ESCALATION_EXAMPLE_THRESHOLD) {
152
+ return `STOP REPEATING (attempt ${failureCount}). You MUST include 'lines' as a separate JSON field. Correct format: {"op":"replace","pos":"${anchor}","lines":["your replacement text"]}. Current line ${parsed.line} contains: '${content}'.`;
153
+ }
154
+
155
+ return `Line ${parsed.line} currently contains '${content}'. Set lines to the replacement content.`;
156
+ }
157
+
158
+ export function buildEscalationBailMessage(
159
+ edits: Array<{ pos?: string; end?: string }>,
160
+ filePath: string,
161
+ fileLines: string[]
162
+ ): string | null {
163
+ const anchorBail = edits.some((e) => {
164
+ const pos = e.pos?.trim();
165
+ return pos && getMissingLinesFailureCount(pos) >= ESCALATION_BAIL_THRESHOLD;
166
+ });
167
+ const fileBail = getFileFailureCount(filePath) >= FILE_BAIL_THRESHOLD;
168
+ if (!(anchorBail || fileBail)) {
169
+ return null;
170
+ }
171
+
172
+ const failingEdit =
173
+ edits.find((e) => {
174
+ const pos = e.pos?.trim();
175
+ return (
176
+ pos && getMissingLinesFailureCount(pos) >= ESCALATION_BAIL_THRESHOLD
177
+ );
178
+ }) ?? edits.find((e) => e.pos?.trim());
179
+ const anchor = failingEdit?.pos?.trim() ?? "";
180
+ const parsedTag = tryParseLineTag(anchor);
181
+ const lineContent = parsedTag && fileLines[parsedTag.line - 1];
182
+ const anchorFailCount = getMissingLinesFailureCount(anchor);
183
+ const fileFailCount = getFileFailureCount(filePath);
184
+ const failCount = Math.max(anchorFailCount, fileFailCount);
185
+
186
+ const parts: string[] = [
187
+ `⚠️ edit_file: NOT APPLIED (attempt ${failCount}) — 'lines' field is missing.`,
188
+ "",
189
+ `Your input: {"op": "replace", "pos": "${anchor}"}`,
190
+ `The file ${filePath} was NOT changed.`,
191
+ "",
192
+ `To replace this line, you MUST include 'lines' as a separate JSON field:`,
193
+ ` {"op": "replace", "pos": "${anchor}", "lines": ["your new content here"]}`,
194
+ ];
195
+
196
+ if (lineContent !== undefined && lineContent !== null) {
197
+ parts.push(
198
+ "",
199
+ `Line ${parsedTag?.line} currently contains: "${lineContent}"`
200
+ );
201
+ }
202
+
203
+ parts.push(
204
+ "",
205
+ `To delete this line: {"op": "replace", "pos": "${anchor}", "lines": []}`,
206
+ "Alternatively, use write_file to replace the entire file content."
207
+ );
208
+
209
+ return parts.join("\n");
210
+ }
@@ -0,0 +1,183 @@
1
+ const SINGLE_LINE_SPLIT_REGEX = /\r?\n/;
2
+ const ANCHOR_PREFIX_REGEX = /^\s*(\d+\s*#\s*[ZPMQVRWSNKTXJBYH]{2})/i;
3
+ const EMBEDDED_END_REGEX =
4
+ /['"']?end['"']?\s*[:=]\s*['"']?(\d+\s*#\s*[ZPMQVRWSNKTXJBYH]{2})['"']?/i;
5
+ const NULL_VALUE_REGEX = /^null\s*[})]?\s*$/;
6
+ const ARRAY_VALUE_REGEX = /^\[([^\]]*)\]/;
7
+ const COMMA_SPLIT_REGEX = /,\s*/;
8
+ const QUOTE_STRIP_REGEX = /^['"]|['"]$/g;
9
+ const STRING_VALUE_REGEX = /^['"](.*)['"]/;
10
+ const KV_LINES_REGEX = /['"]?lines['"]?\s*[:=]\s*(.+?)\s*[})]?\s*$/;
11
+ const QS_LINES_REGEX = /[&?]\s*lines\s*[:=]\s*(.+?)\s*$/;
12
+ const HASH_NORMALIZE_REGEX = /\s*#\s*/;
13
+ const LEADING_SEPARATOR_REGEX = /^[|=%:,;]\s*/;
14
+ const GARBAGE_CONTENT_REGEX = /^['"]?[\]})>]|<\/|<\w+>|^\s*}/;
15
+
16
+ export interface HashlineToolEdit {
17
+ end?: string;
18
+ lines?: string[] | string | null;
19
+ op: "append" | "prepend" | "replace";
20
+ pos?: string;
21
+ }
22
+
23
+ export interface RepairResult {
24
+ edit: HashlineToolEdit;
25
+ warnings: string[];
26
+ }
27
+
28
+ export function assertSingleLineAnchor(
29
+ anchor: string | undefined,
30
+ fieldName: "pos" | "end"
31
+ ): void {
32
+ if (!anchor) {
33
+ return;
34
+ }
35
+ if (anchor.includes("\n") || anchor.includes("\r")) {
36
+ const lineCount = anchor.split(SINGLE_LINE_SPLIT_REGEX).length;
37
+ throw new Error(
38
+ `${fieldName} contains ${lineCount} lines — must be a single-line {line_number}#{hash_id} anchor.`
39
+ );
40
+ }
41
+ }
42
+
43
+ function tryParseEmbeddedValue(raw: string): string[] | null | undefined {
44
+ const trimmed = raw.trim();
45
+
46
+ if (NULL_VALUE_REGEX.test(trimmed)) {
47
+ return null;
48
+ }
49
+
50
+ const arrayMatch = trimmed.match(ARRAY_VALUE_REGEX);
51
+ if (arrayMatch) {
52
+ const inner = arrayMatch[1].trim();
53
+ if (inner.length === 0) {
54
+ return [];
55
+ }
56
+ const elements = inner.split(COMMA_SPLIT_REGEX).map((el) => {
57
+ const s = el.trim();
58
+ const unquoted = s.replace(QUOTE_STRIP_REGEX, "");
59
+ return unquoted;
60
+ });
61
+ return elements;
62
+ }
63
+
64
+ const stringMatch = trimmed.match(STRING_VALUE_REGEX);
65
+ if (stringMatch) {
66
+ return [stringMatch[1]];
67
+ }
68
+
69
+ return undefined;
70
+ }
71
+
72
+ function tryExtractEmbeddedLines(
73
+ posAfterAnchor: string
74
+ ): string[] | null | undefined {
75
+ const kvMatch = posAfterAnchor.match(KV_LINES_REGEX);
76
+ if (kvMatch) {
77
+ return tryParseEmbeddedValue(kvMatch[1]);
78
+ }
79
+
80
+ const qsMatch = posAfterAnchor.match(QS_LINES_REGEX);
81
+ if (qsMatch) {
82
+ return tryParseEmbeddedValue(qsMatch[1]);
83
+ }
84
+
85
+ return undefined;
86
+ }
87
+
88
+ function tryExtractEmbeddedEnd(rest: string): string | undefined {
89
+ const endMatch = rest.match(EMBEDDED_END_REGEX);
90
+ if (!endMatch) {
91
+ return undefined;
92
+ }
93
+ return endMatch[1].replace(HASH_NORMALIZE_REGEX, "#");
94
+ }
95
+
96
+ function repairAnchorField(
97
+ raw: string | undefined
98
+ ): { cleanAnchor: string; rest: string } | null {
99
+ if (!raw) {
100
+ return null;
101
+ }
102
+ if (raw.includes("\n") || raw.includes("\r")) {
103
+ return null;
104
+ }
105
+ const anchorMatch = raw.match(ANCHOR_PREFIX_REGEX);
106
+ if (!anchorMatch) {
107
+ return null;
108
+ }
109
+ const cleanAnchor = anchorMatch[1].replace(HASH_NORMALIZE_REGEX, "#");
110
+ const rest = raw.slice(anchorMatch[0].length);
111
+ if (rest.trim().length === 0) {
112
+ return null;
113
+ }
114
+ return { cleanAnchor, rest };
115
+ }
116
+
117
+ export function repairMalformedEdit(edit: HashlineToolEdit): RepairResult {
118
+ const warnings: string[] = [];
119
+ let repairedPos = edit.pos;
120
+ let repairedEnd = edit.end;
121
+ let repairedLines = edit.lines;
122
+
123
+ const posRepair = repairAnchorField(edit.pos);
124
+ if (posRepair) {
125
+ repairedPos = posRepair.cleanAnchor;
126
+ warnings.push(
127
+ `Auto-repaired pos: extracted anchor '${posRepair.cleanAnchor}' from malformed value.`
128
+ );
129
+
130
+ if (edit.lines === undefined) {
131
+ const extractedLines = tryExtractEmbeddedLines(posRepair.rest);
132
+ if (extractedLines !== undefined) {
133
+ repairedLines = extractedLines;
134
+ warnings.push(
135
+ "Auto-repaired lines: extracted embedded content from pos field."
136
+ );
137
+ } else {
138
+ const plainContent = posRepair.rest
139
+ .replace(LEADING_SEPARATOR_REGEX, "")
140
+ .trim();
141
+ const looksLikeGarbage = GARBAGE_CONTENT_REGEX.test(plainContent);
142
+ if (plainContent.length > 0 && !looksLikeGarbage) {
143
+ repairedLines = plainContent.split("\n");
144
+ warnings.push(
145
+ `Auto-repaired lines: extracted trailing content '${plainContent.slice(0, 60)}' from pos as replacement text.`
146
+ );
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ const endRepair = repairAnchorField(edit.end);
153
+ if (endRepair) {
154
+ repairedEnd = endRepair.cleanAnchor;
155
+ warnings.push(
156
+ `Auto-repaired end: extracted anchor '${endRepair.cleanAnchor}' from malformed value.`
157
+ );
158
+ }
159
+
160
+ if (repairedEnd === undefined && posRepair) {
161
+ const extractedEnd = tryExtractEmbeddedEnd(posRepair.rest);
162
+ if (extractedEnd) {
163
+ repairedEnd = extractedEnd;
164
+ warnings.push(
165
+ `Auto-repaired end: extracted anchor '${extractedEnd}' from embedded content in pos.`
166
+ );
167
+ }
168
+ }
169
+
170
+ if (warnings.length === 0) {
171
+ return { edit, warnings };
172
+ }
173
+
174
+ return {
175
+ edit: {
176
+ ...edit,
177
+ pos: repairedPos,
178
+ end: repairedEnd,
179
+ lines: repairedLines,
180
+ },
181
+ warnings,
182
+ };
183
+ }