@travisennis/acai 0.0.9 → 0.0.10

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 (371) hide show
  1. package/README.md +49 -762
  2. package/bin/acai +52 -0
  3. package/dist/agent/index.d.ts +12 -2
  4. package/dist/agent/index.d.ts.map +1 -1
  5. package/dist/agent/index.js +378 -199
  6. package/dist/agent/sub-agent.d.ts +23 -0
  7. package/dist/agent/sub-agent.d.ts.map +1 -0
  8. package/dist/agent/sub-agent.js +109 -0
  9. package/dist/cli/index.d.ts +26 -0
  10. package/dist/cli/index.d.ts.map +1 -0
  11. package/dist/{cli.js → cli/index.js} +84 -77
  12. package/dist/cli/stdin.d.ts.map +1 -0
  13. package/dist/{stdin.js → cli/stdin.js} +11 -0
  14. package/dist/commands/copy/index.js +2 -2
  15. package/dist/commands/copy/utils.d.ts.map +1 -1
  16. package/dist/commands/copy/utils.js +15 -13
  17. package/dist/commands/generate-rules/index.d.ts +1 -1
  18. package/dist/commands/generate-rules/index.d.ts.map +1 -1
  19. package/dist/commands/generate-rules/index.js +16 -101
  20. package/dist/commands/generate-rules/service.d.ts +21 -0
  21. package/dist/commands/generate-rules/service.d.ts.map +1 -0
  22. package/dist/commands/generate-rules/service.js +103 -0
  23. package/dist/commands/handoff/index.js +2 -2
  24. package/dist/commands/health/index.js +1 -1
  25. package/dist/commands/health/utils.d.ts.map +1 -1
  26. package/dist/commands/health/utils.js +6 -0
  27. package/dist/commands/history/index.d.ts +1 -1
  28. package/dist/commands/history/index.d.ts.map +1 -1
  29. package/dist/commands/history/index.js +17 -18
  30. package/dist/commands/history/types.d.ts +38 -0
  31. package/dist/commands/history/types.d.ts.map +1 -1
  32. package/dist/commands/history/utils.d.ts.map +1 -1
  33. package/dist/commands/history/utils.js +63 -58
  34. package/dist/commands/init/index.d.ts.map +1 -1
  35. package/dist/commands/init/index.js +3 -8
  36. package/dist/commands/init-project/index.d.ts.map +1 -1
  37. package/dist/commands/init-project/index.js +3 -3
  38. package/dist/commands/init-project/utils.d.ts.map +1 -1
  39. package/dist/commands/init-project/utils.js +10 -2
  40. package/dist/commands/list-tools/index.d.ts.map +1 -1
  41. package/dist/commands/list-tools/index.js +7 -31
  42. package/dist/commands/manager.d.ts +2 -2
  43. package/dist/commands/manager.d.ts.map +1 -1
  44. package/dist/commands/manager.js +55 -33
  45. package/dist/commands/model/index.d.ts.map +1 -1
  46. package/dist/commands/model/index.js +20 -151
  47. package/dist/commands/model/model-panel.d.ts +4 -0
  48. package/dist/commands/model/model-panel.d.ts.map +1 -0
  49. package/dist/commands/model/model-panel.js +144 -0
  50. package/dist/commands/paste/index.d.ts.map +1 -1
  51. package/dist/commands/paste/index.js +59 -62
  52. package/dist/commands/paste/utils.d.ts.map +1 -1
  53. package/dist/commands/paste/utils.js +88 -58
  54. package/dist/commands/pickup/index.d.ts.map +1 -1
  55. package/dist/commands/pickup/index.js +6 -3
  56. package/dist/commands/pickup/utils.js +3 -3
  57. package/dist/commands/resources/index.d.ts.map +1 -1
  58. package/dist/commands/resources/index.js +33 -50
  59. package/dist/commands/review/index.d.ts.map +1 -1
  60. package/dist/commands/review/index.js +3 -117
  61. package/dist/commands/review/review-panel.d.ts +3 -0
  62. package/dist/commands/review/review-panel.d.ts.map +1 -0
  63. package/dist/commands/review/review-panel.js +186 -0
  64. package/dist/commands/review/utils.d.ts +9 -0
  65. package/dist/commands/review/utils.d.ts.map +1 -1
  66. package/dist/commands/review/utils.js +127 -68
  67. package/dist/commands/session/index.d.ts +1 -1
  68. package/dist/commands/session/index.d.ts.map +1 -1
  69. package/dist/commands/session/index.js +124 -135
  70. package/dist/commands/shell/index.d.ts.map +1 -1
  71. package/dist/commands/shell/index.js +16 -1
  72. package/dist/commands/types.d.ts +2 -2
  73. package/dist/commands/types.d.ts.map +1 -1
  74. package/dist/{config.d.ts → config/index.d.ts} +20 -9
  75. package/dist/config/index.d.ts.map +1 -0
  76. package/dist/{config.js → config/index.js} +43 -42
  77. package/dist/execution/index.d.ts.map +1 -1
  78. package/dist/execution/index.js +75 -55
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +148 -141
  82. package/dist/middleware/cache.d.ts.map +1 -1
  83. package/dist/middleware/cache.js +18 -36
  84. package/dist/models/ai-config.d.ts +1 -0
  85. package/dist/models/ai-config.d.ts.map +1 -1
  86. package/dist/models/ai-config.js +4 -3
  87. package/dist/models/anthropic-provider.d.ts +2 -5
  88. package/dist/models/anthropic-provider.d.ts.map +1 -1
  89. package/dist/models/anthropic-provider.js +3 -70
  90. package/dist/models/deepseek-provider.d.ts +1 -0
  91. package/dist/models/deepseek-provider.d.ts.map +1 -1
  92. package/dist/models/google-provider.d.ts +2 -3
  93. package/dist/models/google-provider.d.ts.map +1 -1
  94. package/dist/models/google-provider.js +0 -26
  95. package/dist/models/groq-provider.d.ts +1 -0
  96. package/dist/models/groq-provider.d.ts.map +1 -1
  97. package/dist/models/manager.d.ts +13 -2
  98. package/dist/models/manager.d.ts.map +1 -1
  99. package/dist/models/manager.js +20 -8
  100. package/dist/models/openai-provider.d.ts +2 -5
  101. package/dist/models/openai-provider.d.ts.map +1 -1
  102. package/dist/models/openai-provider.js +0 -52
  103. package/dist/models/opencode-zen-provider.d.ts +7 -3
  104. package/dist/models/opencode-zen-provider.d.ts.map +1 -1
  105. package/dist/models/opencode-zen-provider.js +49 -10
  106. package/dist/models/openrouter-provider.d.ts +24 -31
  107. package/dist/models/openrouter-provider.d.ts.map +1 -1
  108. package/dist/models/openrouter-provider.js +84 -182
  109. package/dist/models/providers.d.ts +1 -1
  110. package/dist/models/providers.d.ts.map +1 -1
  111. package/dist/models/xai-provider.d.ts +4 -3
  112. package/dist/models/xai-provider.d.ts.map +1 -1
  113. package/dist/models/xai-provider.js +18 -18
  114. package/dist/modes/manager.d.ts +23 -0
  115. package/dist/modes/manager.d.ts.map +1 -0
  116. package/dist/modes/manager.js +77 -0
  117. package/dist/modes/prompts.d.ts +2 -0
  118. package/dist/modes/prompts.d.ts.map +1 -0
  119. package/dist/modes/prompts.js +143 -0
  120. package/dist/prompts/mentions.d.ts +11 -0
  121. package/dist/prompts/mentions.d.ts.map +1 -0
  122. package/dist/{mentions.js → prompts/mentions.js} +21 -80
  123. package/dist/{prompts.d.ts → prompts/system-prompt.d.ts} +7 -2
  124. package/dist/prompts/system-prompt.d.ts.map +1 -0
  125. package/dist/{prompts.js → prompts/system-prompt.js} +31 -16
  126. package/dist/repl/index.d.ts +174 -0
  127. package/dist/repl/index.d.ts.map +1 -0
  128. package/dist/{repl-new.js → repl/index.js} +389 -76
  129. package/dist/repl/project-status.d.ts +1 -0
  130. package/dist/repl/project-status.d.ts.map +1 -1
  131. package/dist/repl/project-status.js +4 -1
  132. package/dist/sessions/manager.d.ts +93 -1
  133. package/dist/sessions/manager.d.ts.map +1 -1
  134. package/dist/sessions/manager.js +262 -9
  135. package/dist/sessions/summary.d.ts +4 -0
  136. package/dist/sessions/summary.d.ts.map +1 -0
  137. package/dist/sessions/summary.js +30 -0
  138. package/dist/{skills.d.ts → skills/index.d.ts} +14 -2
  139. package/dist/skills/index.d.ts.map +1 -0
  140. package/dist/skills/index.js +294 -0
  141. package/dist/subagents/index.d.ts +15 -0
  142. package/dist/subagents/index.d.ts.map +1 -0
  143. package/dist/subagents/index.js +231 -0
  144. package/dist/terminal/control.d.ts +1 -1
  145. package/dist/terminal/control.d.ts.map +1 -1
  146. package/dist/terminal/control.js +3 -3
  147. package/dist/terminal/east-asian-width.d.ts.map +1 -1
  148. package/dist/terminal/east-asian-width.js +404 -351
  149. package/dist/terminal/keys.d.ts +17 -0
  150. package/dist/terminal/keys.d.ts.map +1 -1
  151. package/dist/terminal/keys.js +37 -0
  152. package/dist/terminal/select-prompt.d.ts.map +1 -1
  153. package/dist/terminal/select-prompt.js +24 -12
  154. package/dist/terminal/string-width.d.ts.map +1 -1
  155. package/dist/terminal/string-width.js +25 -27
  156. package/dist/terminal/style.d.ts.map +1 -1
  157. package/dist/terminal/style.js +4 -7
  158. package/dist/terminal/supports-color.d.ts.map +1 -1
  159. package/dist/terminal/supports-color.js +41 -27
  160. package/dist/terminal/table/cell.d.ts +12 -0
  161. package/dist/terminal/table/cell.d.ts.map +1 -1
  162. package/dist/terminal/table/cell.js +40 -25
  163. package/dist/terminal/table/layout-manager.d.ts.map +1 -1
  164. package/dist/terminal/table/layout-manager.js +100 -68
  165. package/dist/terminal/table/utils.d.ts.map +1 -1
  166. package/dist/terminal/table/utils.js +17 -10
  167. package/dist/terminal/wrap-ansi.d.ts.map +1 -1
  168. package/dist/terminal/wrap-ansi.js +172 -103
  169. package/dist/tokens/tracker.d.ts +1 -0
  170. package/dist/tokens/tracker.d.ts.map +1 -1
  171. package/dist/tokens/tracker.js +3 -0
  172. package/dist/tools/agent.d.ts +27 -0
  173. package/dist/tools/agent.d.ts.map +1 -0
  174. package/dist/tools/agent.js +81 -0
  175. package/dist/tools/bash.d.ts +4 -3
  176. package/dist/tools/bash.d.ts.map +1 -1
  177. package/dist/tools/bash.js +324 -137
  178. package/dist/tools/code-search.d.ts +41 -0
  179. package/dist/tools/code-search.d.ts.map +1 -0
  180. package/dist/tools/code-search.js +195 -0
  181. package/dist/tools/directory-tree.d.ts +3 -3
  182. package/dist/tools/directory-tree.d.ts.map +1 -1
  183. package/dist/tools/directory-tree.js +8 -5
  184. package/dist/tools/dynamic-tool-loader.d.ts +2 -5
  185. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  186. package/dist/tools/dynamic-tool-loader.js +20 -4
  187. package/dist/tools/edit-file.d.ts +7 -7
  188. package/dist/tools/edit-file.d.ts.map +1 -1
  189. package/dist/tools/edit-file.js +164 -66
  190. package/dist/tools/glob.d.ts +6 -6
  191. package/dist/tools/glob.d.ts.map +1 -1
  192. package/dist/tools/glob.js +95 -55
  193. package/dist/tools/grep.d.ts +15 -12
  194. package/dist/tools/grep.d.ts.map +1 -1
  195. package/dist/tools/grep.js +300 -192
  196. package/dist/tools/index.d.ts +143 -5
  197. package/dist/tools/index.d.ts.map +1 -1
  198. package/dist/tools/index.js +39 -24
  199. package/dist/tools/ls.d.ts +2 -2
  200. package/dist/tools/ls.d.ts.map +1 -1
  201. package/dist/tools/ls.js +7 -5
  202. package/dist/tools/read-file.d.ts +3 -3
  203. package/dist/tools/read-file.d.ts.map +1 -1
  204. package/dist/tools/read-file.js +74 -34
  205. package/dist/tools/save-file.d.ts +3 -3
  206. package/dist/tools/save-file.d.ts.map +1 -1
  207. package/dist/tools/save-file.js +11 -11
  208. package/dist/tools/skill.d.ts +23 -0
  209. package/dist/tools/skill.d.ts.map +1 -0
  210. package/dist/tools/skill.js +65 -0
  211. package/dist/tools/think.d.ts.map +1 -1
  212. package/dist/tools/think.js +2 -9
  213. package/dist/tools/utils.d.ts +2 -0
  214. package/dist/tools/utils.d.ts.map +1 -1
  215. package/dist/tools/utils.js +12 -0
  216. package/dist/tools/web-fetch.d.ts +62 -0
  217. package/dist/tools/web-fetch.d.ts.map +1 -0
  218. package/dist/tools/web-fetch.js +429 -0
  219. package/dist/tools/web-search.d.ts +62 -0
  220. package/dist/tools/web-search.d.ts.map +1 -0
  221. package/dist/tools/web-search.js +226 -0
  222. package/dist/tui/autocomplete/attachment-provider.d.ts +3 -6
  223. package/dist/tui/autocomplete/attachment-provider.d.ts.map +1 -1
  224. package/dist/tui/autocomplete/attachment-provider.js +25 -78
  225. package/dist/tui/autocomplete/base-provider.d.ts +1 -0
  226. package/dist/tui/autocomplete/base-provider.d.ts.map +1 -1
  227. package/dist/tui/autocomplete/combined-provider.d.ts +1 -4
  228. package/dist/tui/autocomplete/combined-provider.d.ts.map +1 -1
  229. package/dist/tui/autocomplete/combined-provider.js +3 -17
  230. package/dist/tui/autocomplete/command-provider.d.ts +1 -0
  231. package/dist/tui/autocomplete/command-provider.d.ts.map +1 -1
  232. package/dist/tui/autocomplete/command-provider.js +3 -0
  233. package/dist/tui/autocomplete/file-search-provider.d.ts +2 -1
  234. package/dist/tui/autocomplete/file-search-provider.d.ts.map +1 -1
  235. package/dist/tui/autocomplete/file-search-provider.js +36 -16
  236. package/dist/tui/autocomplete/skill-provider.d.ts +17 -0
  237. package/dist/tui/autocomplete/skill-provider.d.ts.map +1 -0
  238. package/dist/tui/autocomplete/skill-provider.js +49 -0
  239. package/dist/tui/autocomplete.d.ts +2 -2
  240. package/dist/tui/autocomplete.d.ts.map +1 -1
  241. package/dist/tui/autocomplete.js +3 -5
  242. package/dist/tui/components/assistant-message.d.ts.map +1 -1
  243. package/dist/tui/components/assistant-message.js +0 -4
  244. package/dist/tui/components/editor.d.ts +16 -2
  245. package/dist/tui/components/editor.d.ts.map +1 -1
  246. package/dist/tui/components/editor.js +211 -237
  247. package/dist/tui/components/footer.d.ts +6 -4
  248. package/dist/tui/components/footer.d.ts.map +1 -1
  249. package/dist/tui/components/footer.js +49 -25
  250. package/dist/tui/components/markdown.d.ts +8 -5
  251. package/dist/tui/components/markdown.d.ts.map +1 -1
  252. package/dist/tui/components/markdown.js +57 -39
  253. package/dist/tui/components/modal.d.ts.map +1 -1
  254. package/dist/tui/components/modal.js +35 -33
  255. package/dist/tui/components/notification.d.ts +13 -2
  256. package/dist/tui/components/notification.d.ts.map +1 -1
  257. package/dist/tui/components/notification.js +36 -2
  258. package/dist/tui/components/progress-bar.js +1 -1
  259. package/dist/tui/components/select-list.d.ts +1 -0
  260. package/dist/tui/components/select-list.d.ts.map +1 -1
  261. package/dist/tui/components/select-list.js +14 -11
  262. package/dist/tui/components/text.d.ts +16 -0
  263. package/dist/tui/components/text.d.ts.map +1 -1
  264. package/dist/tui/components/text.js +72 -57
  265. package/dist/tui/components/thinking-block.d.ts +9 -0
  266. package/dist/tui/components/thinking-block.d.ts.map +1 -1
  267. package/dist/tui/components/thinking-block.js +43 -11
  268. package/dist/tui/components/tool-execution.d.ts +5 -1
  269. package/dist/tui/components/tool-execution.d.ts.map +1 -1
  270. package/dist/tui/components/tool-execution.js +19 -10
  271. package/dist/tui/components/user-message.d.ts.map +1 -1
  272. package/dist/tui/components/user-message.js +0 -3
  273. package/dist/tui/components/welcome.js +2 -2
  274. package/dist/tui/terminal.d.ts.map +1 -1
  275. package/dist/tui/terminal.js +10 -2
  276. package/dist/tui/tui.d.ts +42 -0
  277. package/dist/tui/tui.d.ts.map +1 -1
  278. package/dist/tui/tui.js +157 -41
  279. package/dist/utils/bash/parse.d.ts +19 -0
  280. package/dist/utils/bash/parse.d.ts.map +1 -0
  281. package/dist/utils/bash/parse.js +223 -0
  282. package/dist/utils/bash/quote.d.ts +6 -0
  283. package/dist/utils/bash/quote.d.ts.map +1 -0
  284. package/dist/utils/bash/quote.js +23 -0
  285. package/dist/utils/bash.d.ts.map +1 -1
  286. package/dist/utils/bash.js +211 -126
  287. package/dist/utils/command-protection.d.ts +28 -0
  288. package/dist/utils/command-protection.d.ts.map +1 -0
  289. package/dist/utils/command-protection.js +324 -0
  290. package/dist/utils/dedent.d.ts.map +1 -0
  291. package/dist/utils/env-expand.d.ts +2 -0
  292. package/dist/utils/env-expand.d.ts.map +1 -0
  293. package/dist/utils/env-expand.js +8 -0
  294. package/dist/utils/filesystem/path-display.d.ts +11 -0
  295. package/dist/utils/filesystem/path-display.d.ts.map +1 -0
  296. package/dist/utils/filesystem/path-display.js +32 -0
  297. package/dist/utils/filesystem/security.d.ts +2 -2
  298. package/dist/utils/filesystem/security.d.ts.map +1 -1
  299. package/dist/utils/filesystem/security.js +28 -30
  300. package/dist/utils/formatting.d.ts.map +1 -0
  301. package/dist/{formatting.js → utils/formatting.js} +1 -1
  302. package/dist/utils/git.d.ts +4 -0
  303. package/dist/utils/git.d.ts.map +1 -1
  304. package/dist/utils/git.js +30 -0
  305. package/dist/utils/glob.d.ts +1 -1
  306. package/dist/utils/glob.d.ts.map +1 -1
  307. package/dist/utils/logger.d.ts.map +1 -0
  308. package/dist/{logger.js → utils/logger.js} +1 -1
  309. package/dist/utils/parsing.d.ts.map +1 -0
  310. package/dist/utils/process.d.ts.map +1 -1
  311. package/dist/utils/process.js +90 -37
  312. package/dist/utils/templates.d.ts +2 -0
  313. package/dist/utils/templates.d.ts.map +1 -0
  314. package/dist/utils/templates.js +24 -0
  315. package/dist/utils/version.d.ts.map +1 -0
  316. package/dist/{version.js → utils/version.js} +1 -1
  317. package/package.json +34 -25
  318. package/dist/cli.d.ts +0 -23
  319. package/dist/cli.d.ts.map +0 -1
  320. package/dist/commands/exit/index.d.ts +0 -10
  321. package/dist/commands/exit/index.d.ts.map +0 -1
  322. package/dist/commands/exit/index.js +0 -21
  323. package/dist/commands/exit/types.d.ts +0 -8
  324. package/dist/commands/exit/types.d.ts.map +0 -1
  325. package/dist/commands/exit/types.js +0 -1
  326. package/dist/commands/exit/utils.d.ts +0 -2
  327. package/dist/commands/exit/utils.d.ts.map +0 -1
  328. package/dist/commands/exit/utils.js +0 -13
  329. package/dist/commands/prompt/index.d.ts +0 -5
  330. package/dist/commands/prompt/index.d.ts.map +0 -1
  331. package/dist/commands/prompt/index.js +0 -122
  332. package/dist/commands/prompt/types.d.ts +0 -15
  333. package/dist/commands/prompt/types.d.ts.map +0 -1
  334. package/dist/commands/prompt/types.js +0 -1
  335. package/dist/commands/prompt/utils.d.ts +0 -12
  336. package/dist/commands/prompt/utils.d.ts.map +0 -1
  337. package/dist/commands/prompt/utils.js +0 -107
  338. package/dist/commands/reset/index.d.ts +0 -3
  339. package/dist/commands/reset/index.d.ts.map +0 -1
  340. package/dist/commands/reset/index.js +0 -25
  341. package/dist/commands/reset/types.d.ts +0 -1
  342. package/dist/commands/reset/types.d.ts.map +0 -1
  343. package/dist/commands/reset/types.js +0 -3
  344. package/dist/commands/save/index.d.ts +0 -3
  345. package/dist/commands/save/index.d.ts.map +0 -1
  346. package/dist/commands/save/index.js +0 -19
  347. package/dist/config.d.ts.map +0 -1
  348. package/dist/dedent.d.ts.map +0 -1
  349. package/dist/formatting.d.ts.map +0 -1
  350. package/dist/logger.d.ts.map +0 -1
  351. package/dist/mentions.d.ts +0 -14
  352. package/dist/mentions.d.ts.map +0 -1
  353. package/dist/parsing.d.ts.map +0 -1
  354. package/dist/prompts.d.ts.map +0 -1
  355. package/dist/repl-new.d.ts +0 -65
  356. package/dist/repl-new.d.ts.map +0 -1
  357. package/dist/skills.d.ts.map +0 -1
  358. package/dist/skills.js +0 -233
  359. package/dist/stdin.d.ts.map +0 -1
  360. package/dist/tui/autocomplete/path-provider.d.ts +0 -21
  361. package/dist/tui/autocomplete/path-provider.d.ts.map +0 -1
  362. package/dist/tui/autocomplete/path-provider.js +0 -164
  363. package/dist/version.d.ts.map +0 -1
  364. /package/dist/{stdin.d.ts → cli/stdin.d.ts} +0 -0
  365. /package/dist/{dedent.d.ts → utils/dedent.d.ts} +0 -0
  366. /package/dist/{dedent.js → utils/dedent.js} +0 -0
  367. /package/dist/{formatting.d.ts → utils/formatting.d.ts} +0 -0
  368. /package/dist/{logger.d.ts → utils/logger.d.ts} +0 -0
  369. /package/dist/{parsing.d.ts → utils/parsing.d.ts} +0 -0
  370. /package/dist/{parsing.js → utils/parsing.js} +0 -0
  371. /package/dist/{version.d.ts → utils/version.d.ts} +0 -0
@@ -2,63 +2,158 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { isPathWithinAllowedDirs } from "./filesystem/security.js";
5
- // Validate path arguments to ensure they're within the project
6
- export function validatePaths(command, allowedDirs, cwd) {
7
- // Simple tokenization - split on spaces but respect quotes
8
- const tokens = [];
9
- let current = "";
10
- let inQuotes = false;
11
- let quoteChar = "";
12
- for (let i = 0; i < command.length; i++) {
13
- const char = command[i] ?? "";
14
- if ((char === '"' || char === "'") && !inQuotes) {
15
- inQuotes = true;
16
- quoteChar = char;
17
- current += char;
5
+ function handleNormalMode(state, char, command, tokens) {
6
+ if (/\s/.test(char)) {
7
+ if (state.current) {
8
+ tokens.push(state.current);
9
+ state.current = "";
18
10
  }
19
- else if (char === quoteChar && inQuotes) {
20
- inQuotes = false;
21
- quoteChar = "";
22
- current += char;
11
+ return;
12
+ }
13
+ if (char === "'") {
14
+ state.mode = "single";
15
+ state.current += char;
16
+ return;
17
+ }
18
+ if (char === '"') {
19
+ state.mode = "double";
20
+ state.current += char;
21
+ return;
22
+ }
23
+ if (char === "\\") {
24
+ const next = command[state.i + 1];
25
+ if (next !== undefined) {
26
+ state.current += char + next;
27
+ state.i++;
28
+ return;
23
29
  }
24
- else if (char === " " && !inQuotes) {
25
- if (current) {
26
- tokens.push(current);
27
- current = "";
28
- }
30
+ }
31
+ state.current += char;
32
+ }
33
+ function handleSingleQuoteMode(state, char) {
34
+ state.current += char;
35
+ if (char === "'")
36
+ state.mode = "normal";
37
+ }
38
+ function handleDoubleQuoteMode(state, char, command) {
39
+ state.current += char;
40
+ if (char === "\\") {
41
+ const next = command[state.i + 1];
42
+ if (next !== undefined) {
43
+ state.current += next;
44
+ state.i++;
29
45
  }
30
- else {
31
- current += char;
46
+ return;
47
+ }
48
+ if (char === '"')
49
+ state.mode = "normal";
50
+ }
51
+ // Tokenize shell command respecting quotes and escapes
52
+ function tokenizeShellWords(command) {
53
+ const tokens = [];
54
+ const state = { current: "", mode: "normal", i: 0 };
55
+ for (state.i = 0; state.i < command.length; state.i++) {
56
+ const char = command[state.i] ?? "";
57
+ if (state.mode === "normal")
58
+ handleNormalMode(state, char, command, tokens);
59
+ else if (state.mode === "single")
60
+ handleSingleQuoteMode(state, char);
61
+ else
62
+ handleDoubleQuoteMode(state, char, command);
63
+ }
64
+ if (state.current)
65
+ tokens.push(state.current);
66
+ return tokens;
67
+ }
68
+ // Strip surrounding quotes from a token
69
+ function stripQuotes(token) {
70
+ if ((token.startsWith('"') && token.endsWith('"')) ||
71
+ (token.startsWith("'") && token.endsWith("'"))) {
72
+ return token.slice(1, -1);
73
+ }
74
+ return token;
75
+ }
76
+ // Check if a token is fully quoted (content should not be path-validated)
77
+ function isFullyQuoted(token) {
78
+ if (token.length < 2)
79
+ return false;
80
+ const first = token[0];
81
+ const last = token[token.length - 1];
82
+ return (first === '"' && last === '"') || (first === "'" && last === "'");
83
+ }
84
+ // Compute which tokens should be skipped from path validation
85
+ function computeSkipTokenMask(tokens) {
86
+ const skip = new Array(tokens.length).fill(false);
87
+ const bin = stripQuotes(tokens[0] ?? "");
88
+ const sub = stripQuotes(tokens[1] ?? "");
89
+ // Commands where -m/--message is a string argument (not a path)
90
+ const gitMessageSubs = new Set(["commit", "merge", "tag", "revert", "notes"]);
91
+ if (bin === "git" && gitMessageSubs.has(sub)) {
92
+ let seenDoubleDash = false;
93
+ for (let i = 2; i < tokens.length; i++) {
94
+ const t = stripQuotes(tokens[i] ?? "");
95
+ if (t === "--") {
96
+ seenDoubleDash = true;
97
+ continue;
98
+ }
99
+ if (seenDoubleDash)
100
+ continue;
101
+ // --message=<msg> - skip this entire token
102
+ if (t.startsWith("--message=")) {
103
+ skip[i] = true;
104
+ continue;
105
+ }
106
+ // -m<msg> (attached message) - skip this entire token
107
+ if (/^-m.+/.test(t)) {
108
+ skip[i] = true;
109
+ continue;
110
+ }
111
+ // -m or --message consumes next token
112
+ if (t === "-m" || t === "--message") {
113
+ if (i + 1 < tokens.length)
114
+ skip[i + 1] = true;
115
+ continue;
116
+ }
117
+ // Combined short opts containing m, e.g. -am, -avm
118
+ if (/^-[^-]+$/.test(t) && t.includes("m")) {
119
+ if (i + 1 < tokens.length)
120
+ skip[i + 1] = true;
121
+ }
32
122
  }
33
123
  }
34
- if (current)
35
- tokens.push(current);
124
+ return skip;
125
+ }
126
+ // Validate path arguments to ensure they're within the project
127
+ export function validatePaths(command, allowedDirs, cwd) {
128
+ const tokens = tokenizeShellWords(command);
129
+ const skip = computeSkipTokenMask(tokens);
36
130
  // Check each token that looks like a path
37
131
  for (let i = 1; i < tokens.length; i++) {
38
- // Skip the command itself
39
132
  const token = tokens[i];
40
133
  if (!token)
41
134
  continue;
135
+ // Skip tokens marked by command-aware logic
136
+ if (skip[i])
137
+ continue;
138
+ // Skip fully quoted tokens - they're string arguments, not paths
139
+ // (paths are typically unquoted or only quoted to handle spaces)
140
+ if (isFullyQuoted(token) && token.includes("\n")) {
141
+ continue;
142
+ }
42
143
  // Remove quotes for path checking
43
- const cleanToken = token.replace(/^['"]|['"]$/g, "");
144
+ const cleanToken = stripQuotes(token);
44
145
  // Skip if it's clearly not a path
45
146
  if (cleanToken.startsWith("-") ||
46
147
  cleanToken.includes("://") ||
47
148
  (!cleanToken.includes("/") && cleanToken !== "~")) {
48
149
  continue;
49
150
  }
50
- // Skip git commit messages and other special cases
51
- const prevToken = tokens[i - 1]?.replace(/^['"]|['"]$/g, "");
52
- if (prevToken === "-m" || prevToken === "--message") {
53
- continue;
54
- }
55
151
  try {
56
152
  // Expand ~ to home directory for proper validation
57
153
  const expandedToken = cleanToken.startsWith("~/") || cleanToken === "~"
58
154
  ? path.join(os.homedir(), cleanToken.slice(1))
59
155
  : cleanToken;
60
156
  const resolvedPath = path.resolve(cwd, expandedToken);
61
- // Allow access to explicitly allowed paths
62
157
  if (!isPathWithinAllowedDirs(resolvedPath, allowedDirs)) {
63
158
  return {
64
159
  isValid: false,
@@ -125,6 +220,87 @@ export const resolveCwd = (cwdInput, workingDir, allowedDirs) => {
125
220
  }
126
221
  return target;
127
222
  };
223
+ const mutatingBinaries = new Set([
224
+ "rm",
225
+ "mv",
226
+ "cp",
227
+ "mkdir",
228
+ "rmdir",
229
+ "touch",
230
+ "chmod",
231
+ "chown",
232
+ "ln",
233
+ "truncate",
234
+ "dd",
235
+ "tee",
236
+ ]);
237
+ const npmMutating = new Set([
238
+ "install",
239
+ "uninstall",
240
+ "update",
241
+ "ci",
242
+ "publish",
243
+ "link",
244
+ "dedupe",
245
+ "prune",
246
+ "rebuild",
247
+ "add",
248
+ ]);
249
+ const gitMutating = new Set([
250
+ "add",
251
+ "am",
252
+ "apply",
253
+ "branch",
254
+ "checkout",
255
+ "switch",
256
+ "cherry-pick",
257
+ "clean",
258
+ "commit",
259
+ "merge",
260
+ "mv",
261
+ "pull",
262
+ "push",
263
+ "rebase",
264
+ "reset",
265
+ "revert",
266
+ "stash",
267
+ "tag",
268
+ "worktree",
269
+ "submodule",
270
+ "config",
271
+ ]);
272
+ const actionMutating = new Set(["create", "update", "upgrade", "install"]);
273
+ const packageManagers = new Set(["npm", "pnpm", "yarn"]);
274
+ function isSegmentMutating(seg) {
275
+ const tokens = seg.split(/\s+/);
276
+ if (tokens.length === 0)
277
+ return false;
278
+ const bin = tokens[0];
279
+ if (!bin)
280
+ return false;
281
+ if (tokens.some((t) => actionMutating.has(t))) {
282
+ return true;
283
+ }
284
+ if (bin === "sed" && tokens.some((t) => /^-i/.test(t))) {
285
+ return true;
286
+ }
287
+ if (mutatingBinaries.has(bin)) {
288
+ return true;
289
+ }
290
+ if (bin === "git" && tokens.length > 1) {
291
+ const sub = tokens[1];
292
+ if (typeof sub === "string" && gitMutating.has(sub)) {
293
+ return true;
294
+ }
295
+ }
296
+ if (packageManagers.has(bin) && tokens.length > 1) {
297
+ const sub = tokens[1];
298
+ if (typeof sub === "string" && npmMutating.has(sub)) {
299
+ return true;
300
+ }
301
+ }
302
+ return false;
303
+ }
128
304
  export const isMutatingCommand = (rawCommand) => {
129
305
  const command = rawCommand.trim();
130
306
  // Redirections that write to disk
@@ -136,96 +312,5 @@ export const isMutatingCommand = (rawCommand) => {
136
312
  .split(/\s*(?:&&|\|\||;|\|)\s*/)
137
313
  .map((s) => s.trim())
138
314
  .filter((s) => s.length > 0);
139
- const mutatingBinaries = new Set([
140
- "rm",
141
- "mv",
142
- "cp",
143
- "mkdir",
144
- "rmdir",
145
- "touch",
146
- "chmod",
147
- "chown",
148
- "ln",
149
- "truncate",
150
- "dd",
151
- "tee",
152
- ]);
153
- const npmMutating = new Set([
154
- "install",
155
- "uninstall",
156
- "update",
157
- "ci",
158
- "publish",
159
- "link",
160
- "dedupe",
161
- "prune",
162
- "rebuild",
163
- "add",
164
- ]);
165
- const gitMutating = new Set([
166
- "add",
167
- "am",
168
- "apply",
169
- "branch",
170
- "checkout",
171
- "switch",
172
- "cherry-pick",
173
- "clean",
174
- "commit",
175
- "merge",
176
- "mv",
177
- "pull",
178
- "push",
179
- "rebase",
180
- "reset",
181
- "revert",
182
- "stash",
183
- "tag",
184
- "worktree",
185
- "submodule",
186
- "config",
187
- ]);
188
- // Generic action words that should be considered mutating when present in the command
189
- const actionMutating = new Set(["create", "update", "upgrade", "install"]);
190
- for (const seg of segments) {
191
- const tokens = seg.split(/\s+/);
192
- if (tokens.length === 0)
193
- continue;
194
- const bin = tokens[0];
195
- if (!bin)
196
- continue;
197
- // If any token is an action-like mutating word, consider mutating
198
- if (tokens.some((t) => actionMutating.has(t))) {
199
- return true;
200
- }
201
- // sed -i is mutating
202
- if (bin === "sed") {
203
- if (tokens.some((t) => /^-i/.test(t))) {
204
- return true;
205
- }
206
- // sed without -i is not mutating
207
- }
208
- if (mutatingBinaries.has(bin)) {
209
- return true;
210
- }
211
- if (bin === "git" && tokens.length > 1) {
212
- const sub = tokens[1];
213
- if (typeof sub === "string" && gitMutating.has(sub)) {
214
- return true;
215
- }
216
- }
217
- if (bin === "npm" && tokens.length > 1) {
218
- const sub = tokens[1];
219
- if (typeof sub === "string" && npmMutating.has(sub)) {
220
- return true;
221
- }
222
- }
223
- if ((bin === "pnpm" || bin === "yarn") && tokens.length > 1) {
224
- const sub = tokens[1];
225
- if (typeof sub === "string" && npmMutating.has(sub)) {
226
- return true;
227
- }
228
- }
229
- }
230
- return false;
315
+ return segments.some((seg) => isSegmentMutating(seg));
231
316
  };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Command Protection Module
3
+ * Detects and blocks destructive commands that could cause data loss
4
+ */
5
+ export interface BlockedCommandResult {
6
+ blocked: true;
7
+ reason: string;
8
+ command: string;
9
+ tip: string;
10
+ }
11
+ export interface SafeCommandResult {
12
+ blocked: false;
13
+ }
14
+ /**
15
+ * Result type for command safety check
16
+ */
17
+ export type CommandSafetyResult = BlockedCommandResult | SafeCommandResult;
18
+ /**
19
+ * Detects if a command is destructive and should be blocked
20
+ * @param command - The full command string to check
21
+ * @returns BlockedCommandResult if destructive, SafeCommandResult if safe
22
+ */
23
+ export declare function detectDestructiveCommand(command: string): CommandSafetyResult;
24
+ /**
25
+ * Generate a user-friendly blocked command message
26
+ */
27
+ export declare function formatBlockedCommandMessage(result: BlockedCommandResult): string;
28
+ //# sourceMappingURL=command-protection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-protection.d.ts","sourceRoot":"","sources":["../../source/utils/command-protection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,KAAK,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,GAAG,iBAAiB,CAAC;AAE3E;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CA4B7E;AA8UD;;GAEG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,oBAAoB,GAC3B,MAAM,CAQR"}