@cdoing/core 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 (378) hide show
  1. package/dist/agents/coordinator.d.ts +114 -0
  2. package/dist/agents/coordinator.d.ts.map +1 -0
  3. package/dist/agents/coordinator.js +158 -0
  4. package/dist/agents/coordinator.js.map +1 -0
  5. package/dist/context-providers/clipboard.d.ts +13 -0
  6. package/dist/context-providers/clipboard.d.ts.map +1 -0
  7. package/dist/context-providers/clipboard.js +53 -0
  8. package/dist/context-providers/clipboard.js.map +1 -0
  9. package/dist/context-providers/codebase.d.ts +46 -0
  10. package/dist/context-providers/codebase.d.ts.map +1 -0
  11. package/dist/context-providers/codebase.js +273 -0
  12. package/dist/context-providers/codebase.js.map +1 -0
  13. package/dist/context-providers/diff.d.ts +18 -0
  14. package/dist/context-providers/diff.d.ts.map +1 -0
  15. package/dist/context-providers/diff.js +63 -0
  16. package/dist/context-providers/diff.js.map +1 -0
  17. package/dist/context-providers/docs.d.ts +21 -0
  18. package/dist/context-providers/docs.d.ts.map +1 -0
  19. package/dist/context-providers/docs.js +180 -0
  20. package/dist/context-providers/docs.js.map +1 -0
  21. package/dist/context-providers/file-include.d.ts +13 -0
  22. package/dist/context-providers/file-include.d.ts.map +1 -0
  23. package/dist/context-providers/file-include.js +82 -0
  24. package/dist/context-providers/file-include.js.map +1 -0
  25. package/dist/context-providers/folder.d.ts +19 -0
  26. package/dist/context-providers/folder.d.ts.map +1 -0
  27. package/dist/context-providers/folder.js +130 -0
  28. package/dist/context-providers/folder.js.map +1 -0
  29. package/dist/context-providers/git.d.ts +19 -0
  30. package/dist/context-providers/git.d.ts.map +1 -0
  31. package/dist/context-providers/git.js +74 -0
  32. package/dist/context-providers/git.js.map +1 -0
  33. package/dist/context-providers/index.d.ts +26 -0
  34. package/dist/context-providers/index.d.ts.map +1 -0
  35. package/dist/context-providers/index.js +37 -0
  36. package/dist/context-providers/index.js.map +1 -0
  37. package/dist/context-providers/open-files.d.ts +25 -0
  38. package/dist/context-providers/open-files.d.ts.map +1 -0
  39. package/dist/context-providers/open-files.js +134 -0
  40. package/dist/context-providers/open-files.js.map +1 -0
  41. package/dist/context-providers/problems.d.ts +24 -0
  42. package/dist/context-providers/problems.d.ts.map +1 -0
  43. package/dist/context-providers/problems.js +97 -0
  44. package/dist/context-providers/problems.js.map +1 -0
  45. package/dist/context-providers/registry.d.ts +61 -0
  46. package/dist/context-providers/registry.d.ts.map +1 -0
  47. package/dist/context-providers/registry.js +92 -0
  48. package/dist/context-providers/registry.js.map +1 -0
  49. package/dist/context-providers/terminal.d.ts +25 -0
  50. package/dist/context-providers/terminal.d.ts.map +1 -0
  51. package/dist/context-providers/terminal.js +55 -0
  52. package/dist/context-providers/terminal.js.map +1 -0
  53. package/dist/context-providers/tree.d.ts +29 -0
  54. package/dist/context-providers/tree.d.ts.map +1 -0
  55. package/dist/context-providers/tree.js +172 -0
  56. package/dist/context-providers/tree.js.map +1 -0
  57. package/dist/context-providers/types.d.ts +72 -0
  58. package/dist/context-providers/types.d.ts.map +1 -0
  59. package/dist/context-providers/types.js +10 -0
  60. package/dist/context-providers/types.js.map +1 -0
  61. package/dist/context-providers/url.d.ts +27 -0
  62. package/dist/context-providers/url.d.ts.map +1 -0
  63. package/dist/context-providers/url.js +131 -0
  64. package/dist/context-providers/url.js.map +1 -0
  65. package/dist/effort/index.d.ts +78 -0
  66. package/dist/effort/index.d.ts.map +1 -0
  67. package/dist/effort/index.js +146 -0
  68. package/dist/effort/index.js.map +1 -0
  69. package/dist/hooks/index.d.ts +47 -0
  70. package/dist/hooks/index.d.ts.map +1 -0
  71. package/dist/hooks/index.js +151 -0
  72. package/dist/hooks/index.js.map +1 -0
  73. package/dist/index.d.ts +75 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +152 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/indexing/chunker.d.ts +25 -0
  78. package/dist/indexing/chunker.d.ts.map +1 -0
  79. package/dist/indexing/chunker.js +217 -0
  80. package/dist/indexing/chunker.js.map +1 -0
  81. package/dist/indexing/database.d.ts +49 -0
  82. package/dist/indexing/database.d.ts.map +1 -0
  83. package/dist/indexing/database.js +287 -0
  84. package/dist/indexing/database.js.map +1 -0
  85. package/dist/indexing/index.d.ts +9 -0
  86. package/dist/indexing/index.d.ts.map +1 -0
  87. package/dist/indexing/index.js +13 -0
  88. package/dist/indexing/index.js.map +1 -0
  89. package/dist/indexing/indexer.d.ts +63 -0
  90. package/dist/indexing/indexer.d.ts.map +1 -0
  91. package/dist/indexing/indexer.js +352 -0
  92. package/dist/indexing/indexer.js.map +1 -0
  93. package/dist/indexing/recent-edits-cache.d.ts +77 -0
  94. package/dist/indexing/recent-edits-cache.d.ts.map +1 -0
  95. package/dist/indexing/recent-edits-cache.js +123 -0
  96. package/dist/indexing/recent-edits-cache.js.map +1 -0
  97. package/dist/indexing/types.d.ts +39 -0
  98. package/dist/indexing/types.d.ts.map +1 -0
  99. package/dist/indexing/types.js +6 -0
  100. package/dist/indexing/types.js.map +1 -0
  101. package/dist/mcp/index.d.ts +33 -0
  102. package/dist/mcp/index.d.ts.map +1 -0
  103. package/dist/mcp/index.js +37 -0
  104. package/dist/mcp/index.js.map +1 -0
  105. package/dist/mcp/manager.d.ts +123 -0
  106. package/dist/mcp/manager.d.ts.map +1 -0
  107. package/dist/mcp/manager.js +331 -0
  108. package/dist/mcp/manager.js.map +1 -0
  109. package/dist/oauth.d.ts +33 -0
  110. package/dist/oauth.d.ts.map +1 -0
  111. package/dist/oauth.js +312 -0
  112. package/dist/oauth.js.map +1 -0
  113. package/dist/permissions/index.d.ts +216 -0
  114. package/dist/permissions/index.d.ts.map +1 -0
  115. package/dist/permissions/index.js +938 -0
  116. package/dist/permissions/index.js.map +1 -0
  117. package/dist/plan/index.d.ts +20 -0
  118. package/dist/plan/index.d.ts.map +1 -0
  119. package/dist/plan/index.js +24 -0
  120. package/dist/plan/index.js.map +1 -0
  121. package/dist/plan/manager.d.ts +101 -0
  122. package/dist/plan/manager.d.ts.map +1 -0
  123. package/dist/plan/manager.js +170 -0
  124. package/dist/plan/manager.js.map +1 -0
  125. package/dist/rules/index.d.ts +28 -0
  126. package/dist/rules/index.d.ts.map +1 -0
  127. package/dist/rules/index.js +31 -0
  128. package/dist/rules/index.js.map +1 -0
  129. package/dist/rules/manager.d.ts +77 -0
  130. package/dist/rules/manager.d.ts.map +1 -0
  131. package/dist/rules/manager.js +279 -0
  132. package/dist/rules/manager.js.map +1 -0
  133. package/dist/rules/types.d.ts +34 -0
  134. package/dist/rules/types.d.ts.map +1 -0
  135. package/dist/rules/types.js +9 -0
  136. package/dist/rules/types.js.map +1 -0
  137. package/dist/sandbox/filesystem.d.ts +20 -0
  138. package/dist/sandbox/filesystem.d.ts.map +1 -0
  139. package/dist/sandbox/filesystem.js +141 -0
  140. package/dist/sandbox/filesystem.js.map +1 -0
  141. package/dist/sandbox/index.d.ts +4 -0
  142. package/dist/sandbox/index.d.ts.map +1 -0
  143. package/dist/sandbox/index.js +8 -0
  144. package/dist/sandbox/index.js.map +1 -0
  145. package/dist/sandbox/manager.d.ts +47 -0
  146. package/dist/sandbox/manager.d.ts.map +1 -0
  147. package/dist/sandbox/manager.js +220 -0
  148. package/dist/sandbox/manager.js.map +1 -0
  149. package/dist/sandbox/network.d.ts +14 -0
  150. package/dist/sandbox/network.d.ts.map +1 -0
  151. package/dist/sandbox/network.js +87 -0
  152. package/dist/sandbox/network.js.map +1 -0
  153. package/dist/sandbox/types.d.ts +42 -0
  154. package/dist/sandbox/types.d.ts.map +1 -0
  155. package/dist/sandbox/types.js +25 -0
  156. package/dist/sandbox/types.js.map +1 -0
  157. package/dist/tools/ast-edit.d.ts +57 -0
  158. package/dist/tools/ast-edit.d.ts.map +1 -0
  159. package/dist/tools/ast-edit.js +443 -0
  160. package/dist/tools/ast-edit.js.map +1 -0
  161. package/dist/tools/code-verify.d.ts +8 -0
  162. package/dist/tools/code-verify.d.ts.map +1 -0
  163. package/dist/tools/code-verify.js +159 -0
  164. package/dist/tools/code-verify.js.map +1 -0
  165. package/dist/tools/codebase-search.d.ts +17 -0
  166. package/dist/tools/codebase-search.d.ts.map +1 -0
  167. package/dist/tools/codebase-search.js +104 -0
  168. package/dist/tools/codebase-search.js.map +1 -0
  169. package/dist/tools/file-delete.d.ts +26 -0
  170. package/dist/tools/file-delete.d.ts.map +1 -0
  171. package/dist/tools/file-delete.js +179 -0
  172. package/dist/tools/file-delete.js.map +1 -0
  173. package/dist/tools/file-edit.d.ts +10 -0
  174. package/dist/tools/file-edit.d.ts.map +1 -0
  175. package/dist/tools/file-edit.js +138 -0
  176. package/dist/tools/file-edit.js.map +1 -0
  177. package/dist/tools/file-read.d.ts +12 -0
  178. package/dist/tools/file-read.d.ts.map +1 -0
  179. package/dist/tools/file-read.js +211 -0
  180. package/dist/tools/file-read.js.map +1 -0
  181. package/dist/tools/file-run.d.ts +10 -0
  182. package/dist/tools/file-run.d.ts.map +1 -0
  183. package/dist/tools/file-run.js +179 -0
  184. package/dist/tools/file-run.js.map +1 -0
  185. package/dist/tools/file-write.d.ts +10 -0
  186. package/dist/tools/file-write.d.ts.map +1 -0
  187. package/dist/tools/file-write.js +134 -0
  188. package/dist/tools/file-write.js.map +1 -0
  189. package/dist/tools/glob-search.d.ts +8 -0
  190. package/dist/tools/glob-search.d.ts.map +1 -0
  191. package/dist/tools/glob-search.js +108 -0
  192. package/dist/tools/glob-search.js.map +1 -0
  193. package/dist/tools/grep-search.d.ts +8 -0
  194. package/dist/tools/grep-search.d.ts.map +1 -0
  195. package/dist/tools/grep-search.js +139 -0
  196. package/dist/tools/grep-search.js.map +1 -0
  197. package/dist/tools/list-dir.d.ts +16 -0
  198. package/dist/tools/list-dir.d.ts.map +1 -0
  199. package/dist/tools/list-dir.js +183 -0
  200. package/dist/tools/list-dir.js.map +1 -0
  201. package/dist/tools/multi-edit.d.ts +16 -0
  202. package/dist/tools/multi-edit.d.ts.map +1 -0
  203. package/dist/tools/multi-edit.js +163 -0
  204. package/dist/tools/multi-edit.js.map +1 -0
  205. package/dist/tools/notebook-edit.d.ts +31 -0
  206. package/dist/tools/notebook-edit.d.ts.map +1 -0
  207. package/dist/tools/notebook-edit.js +321 -0
  208. package/dist/tools/notebook-edit.js.map +1 -0
  209. package/dist/tools/registry.d.ts +16 -0
  210. package/dist/tools/registry.d.ts.map +1 -0
  211. package/dist/tools/registry.js +41 -0
  212. package/dist/tools/registry.js.map +1 -0
  213. package/dist/tools/shell-exec.d.ts +12 -0
  214. package/dist/tools/shell-exec.d.ts.map +1 -0
  215. package/dist/tools/shell-exec.js +261 -0
  216. package/dist/tools/shell-exec.js.map +1 -0
  217. package/dist/tools/sub-agent-manager.d.ts +57 -0
  218. package/dist/tools/sub-agent-manager.d.ts.map +1 -0
  219. package/dist/tools/sub-agent-manager.js +153 -0
  220. package/dist/tools/sub-agent-manager.js.map +1 -0
  221. package/dist/tools/sub-agent-status.d.ts +12 -0
  222. package/dist/tools/sub-agent-status.d.ts.map +1 -0
  223. package/dist/tools/sub-agent-status.js +59 -0
  224. package/dist/tools/sub-agent-status.js.map +1 -0
  225. package/dist/tools/sub-agent-terminate.d.ts +12 -0
  226. package/dist/tools/sub-agent-terminate.d.ts.map +1 -0
  227. package/dist/tools/sub-agent-terminate.js +55 -0
  228. package/dist/tools/sub-agent-terminate.js.map +1 -0
  229. package/dist/tools/sub-agent.d.ts +34 -0
  230. package/dist/tools/sub-agent.d.ts.map +1 -0
  231. package/dist/tools/sub-agent.js +140 -0
  232. package/dist/tools/sub-agent.js.map +1 -0
  233. package/dist/tools/system-info.d.ts +24 -0
  234. package/dist/tools/system-info.d.ts.map +1 -0
  235. package/dist/tools/system-info.js +220 -0
  236. package/dist/tools/system-info.js.map +1 -0
  237. package/dist/tools/todo.d.ts +16 -0
  238. package/dist/tools/todo.d.ts.map +1 -0
  239. package/dist/tools/todo.js +144 -0
  240. package/dist/tools/todo.js.map +1 -0
  241. package/dist/tools/types.d.ts +20 -0
  242. package/dist/tools/types.d.ts.map +1 -0
  243. package/dist/tools/types.js +3 -0
  244. package/dist/tools/types.js.map +1 -0
  245. package/dist/tools/view-diff.d.ts +11 -0
  246. package/dist/tools/view-diff.d.ts.map +1 -0
  247. package/dist/tools/view-diff.js +88 -0
  248. package/dist/tools/view-diff.js.map +1 -0
  249. package/dist/tools/view-repo-map.d.ts +18 -0
  250. package/dist/tools/view-repo-map.d.ts.map +1 -0
  251. package/dist/tools/view-repo-map.js +245 -0
  252. package/dist/tools/view-repo-map.js.map +1 -0
  253. package/dist/tools/web-fetch.d.ts +13 -0
  254. package/dist/tools/web-fetch.d.ts.map +1 -0
  255. package/dist/tools/web-fetch.js +106 -0
  256. package/dist/tools/web-fetch.js.map +1 -0
  257. package/dist/tools/web-search.d.ts +10 -0
  258. package/dist/tools/web-search.d.ts.map +1 -0
  259. package/dist/tools/web-search.js +106 -0
  260. package/dist/tools/web-search.js.map +1 -0
  261. package/dist/utils/gitignore.d.ts +10 -0
  262. package/dist/utils/gitignore.d.ts.map +1 -0
  263. package/dist/utils/gitignore.js +104 -0
  264. package/dist/utils/gitignore.js.map +1 -0
  265. package/dist/utils/lazy-apply.d.ts +45 -0
  266. package/dist/utils/lazy-apply.d.ts.map +1 -0
  267. package/dist/utils/lazy-apply.js +164 -0
  268. package/dist/utils/lazy-apply.js.map +1 -0
  269. package/dist/utils/memory.d.ts +36 -0
  270. package/dist/utils/memory.d.ts.map +1 -0
  271. package/dist/utils/memory.js +136 -0
  272. package/dist/utils/memory.js.map +1 -0
  273. package/dist/utils/path-matching.d.ts +24 -0
  274. package/dist/utils/path-matching.d.ts.map +1 -0
  275. package/dist/utils/path-matching.js +116 -0
  276. package/dist/utils/path-matching.js.map +1 -0
  277. package/dist/utils/path-safety.d.ts +13 -0
  278. package/dist/utils/path-safety.d.ts.map +1 -0
  279. package/dist/utils/path-safety.js +54 -0
  280. package/dist/utils/path-safety.js.map +1 -0
  281. package/dist/utils/project-config.d.ts +18 -0
  282. package/dist/utils/project-config.d.ts.map +1 -0
  283. package/dist/utils/project-config.js +76 -0
  284. package/dist/utils/project-config.js.map +1 -0
  285. package/dist/utils/search-match.d.ts +63 -0
  286. package/dist/utils/search-match.d.ts.map +1 -0
  287. package/dist/utils/search-match.js +426 -0
  288. package/dist/utils/search-match.js.map +1 -0
  289. package/dist/utils/shell-paths.d.ts +17 -0
  290. package/dist/utils/shell-paths.d.ts.map +1 -0
  291. package/dist/utils/shell-paths.js +107 -0
  292. package/dist/utils/shell-paths.js.map +1 -0
  293. package/dist/utils/streaming-diff.d.ts +45 -0
  294. package/dist/utils/streaming-diff.d.ts.map +1 -0
  295. package/dist/utils/streaming-diff.js +230 -0
  296. package/dist/utils/streaming-diff.js.map +1 -0
  297. package/dist/utils/todo.d.ts +47 -0
  298. package/dist/utils/todo.d.ts.map +1 -0
  299. package/dist/utils/todo.js +102 -0
  300. package/dist/utils/todo.js.map +1 -0
  301. package/package.json +23 -0
  302. package/src/agents/coordinator.ts +240 -0
  303. package/src/context-providers/clipboard.ts +48 -0
  304. package/src/context-providers/codebase.ts +274 -0
  305. package/src/context-providers/diff.ts +66 -0
  306. package/src/context-providers/docs.ts +160 -0
  307. package/src/context-providers/file-include.ts +54 -0
  308. package/src/context-providers/folder.ts +106 -0
  309. package/src/context-providers/git.ts +72 -0
  310. package/src/context-providers/index.ts +26 -0
  311. package/src/context-providers/open-files.ts +113 -0
  312. package/src/context-providers/problems.ts +100 -0
  313. package/src/context-providers/registry.ts +99 -0
  314. package/src/context-providers/terminal.ts +58 -0
  315. package/src/context-providers/tree.ts +161 -0
  316. package/src/context-providers/types.ts +84 -0
  317. package/src/context-providers/url.ts +138 -0
  318. package/src/effort/index.ts +177 -0
  319. package/src/hooks/index.ts +148 -0
  320. package/src/index.ts +114 -0
  321. package/src/indexing/README.md +267 -0
  322. package/src/indexing/chunker.ts +206 -0
  323. package/src/indexing/database.ts +299 -0
  324. package/src/indexing/index.ts +15 -0
  325. package/src/indexing/indexer.ts +383 -0
  326. package/src/indexing/recent-edits-cache.ts +150 -0
  327. package/src/indexing/types.ts +44 -0
  328. package/src/mcp/index.ts +33 -0
  329. package/src/mcp/manager.ts +385 -0
  330. package/src/oauth.ts +330 -0
  331. package/src/permissions/index.ts +1011 -0
  332. package/src/plan/index.ts +20 -0
  333. package/src/plan/manager.ts +233 -0
  334. package/src/rules/index.ts +28 -0
  335. package/src/rules/manager.ts +276 -0
  336. package/src/rules/types.ts +40 -0
  337. package/src/sandbox/filesystem.ts +135 -0
  338. package/src/sandbox/index.ts +9 -0
  339. package/src/sandbox/manager.ts +213 -0
  340. package/src/sandbox/network.ts +101 -0
  341. package/src/sandbox/types.ts +63 -0
  342. package/src/tools/ast-edit.ts +493 -0
  343. package/src/tools/code-verify.ts +143 -0
  344. package/src/tools/codebase-search.ts +117 -0
  345. package/src/tools/file-delete.ts +155 -0
  346. package/src/tools/file-edit.ts +115 -0
  347. package/src/tools/file-read.ts +195 -0
  348. package/src/tools/file-run.ts +158 -0
  349. package/src/tools/file-write.ts +104 -0
  350. package/src/tools/glob-search.ts +80 -0
  351. package/src/tools/grep-search.ts +120 -0
  352. package/src/tools/list-dir.ts +172 -0
  353. package/src/tools/multi-edit.ts +138 -0
  354. package/src/tools/notebook-edit.ts +342 -0
  355. package/src/tools/registry.ts +43 -0
  356. package/src/tools/shell-exec.ts +251 -0
  357. package/src/tools/sub-agent-manager.ts +183 -0
  358. package/src/tools/sub-agent-status.ts +67 -0
  359. package/src/tools/sub-agent-terminate.ts +62 -0
  360. package/src/tools/sub-agent.ts +162 -0
  361. package/src/tools/system-info.ts +248 -0
  362. package/src/tools/todo.ts +149 -0
  363. package/src/tools/types.ts +21 -0
  364. package/src/tools/view-diff.ts +99 -0
  365. package/src/tools/view-repo-map.ts +249 -0
  366. package/src/tools/web-fetch.ts +118 -0
  367. package/src/tools/web-search.ts +129 -0
  368. package/src/utils/gitignore.ts +73 -0
  369. package/src/utils/lazy-apply.ts +189 -0
  370. package/src/utils/memory.ts +124 -0
  371. package/src/utils/path-matching.ts +84 -0
  372. package/src/utils/path-safety.ts +19 -0
  373. package/src/utils/project-config.ts +41 -0
  374. package/src/utils/search-match.ts +495 -0
  375. package/src/utils/shell-paths.ts +79 -0
  376. package/src/utils/streaming-diff.ts +260 -0
  377. package/src/utils/todo.ts +115 -0
  378. package/tsconfig.json +18 -0
@@ -0,0 +1,938 @@
1
+ "use strict";
2
+ /**
3
+ * Permission Manager
4
+ *
5
+ * Implements the Claude Code permission system:
6
+ * - Settings-based allow/ask/deny rules loaded from .claude/settings.json files
7
+ * - Permission modes: default, acceptEdits, plan, dontAsk, bypassPermissions
8
+ * - Rule evaluation order: deny → ask → allow (deny always wins)
9
+ * - Rule syntax: Tool, Tool(*), Bash(cmd *), Read(path), WebFetch(domain:x), Agent(name)
10
+ * - Settings precedence: local project → shared project → user (~/.claude/settings.json)
11
+ *
12
+ * Also supports legacy runtime stored rules (allow once / always / project).
13
+ *
14
+ * Backward compatible: legacy ASK / AUTO_EDIT / AUTO modes still accepted.
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.PermissionManager = exports.PermissionMode = void 0;
51
+ const readline = __importStar(require("readline"));
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const os = __importStar(require("os"));
55
+ const path_matching_1 = require("../utils/path-matching");
56
+ const shell_paths_1 = require("../utils/shell-paths");
57
+ // ── Constants ──────────────────────────────────────────────────────────────────
58
+ const HOME_DIR = os.homedir();
59
+ const GLOBAL_CDOING_DIR = path.join(HOME_DIR, ".cdoing");
60
+ const GLOBAL_PERMISSIONS_FILE = path.join(GLOBAL_CDOING_DIR, "permissions.json");
61
+ // Settings files — check both .cdoing/ (our format) and .claude/ (Claude Code compat)
62
+ const USER_SETTINGS_FILE = path.join(HOME_DIR, ".cdoing", "settings.json");
63
+ const USER_SETTINGS_FILE_CLAUDE = path.join(HOME_DIR, ".claude", "settings.json");
64
+ // Managed settings — highest priority, cannot be overridden
65
+ // macOS: /Library/Application Support/cdoing/managed-settings.json
66
+ // Linux: /etc/cdoing/managed-settings.json
67
+ const MANAGED_SETTINGS_FILE = process.platform === "darwin"
68
+ ? "/Library/Application Support/cdoing/managed-settings.json"
69
+ : "/etc/cdoing/managed-settings.json";
70
+ const MANAGED_SETTINGS_FILE_CLAUDE = process.platform === "darwin"
71
+ ? "/Library/Application Support/claude-code/managed-settings.json"
72
+ : "/etc/claude-code/managed-settings.json";
73
+ /**
74
+ * Permission modes as defined in Claude Code docs.
75
+ * Legacy aliases (ask / auto-edit / auto) are kept for backward compatibility.
76
+ */
77
+ var PermissionMode;
78
+ (function (PermissionMode) {
79
+ // Canonical names
80
+ PermissionMode["DEFAULT"] = "default";
81
+ PermissionMode["ACCEPT_EDITS"] = "acceptEdits";
82
+ PermissionMode["PLAN"] = "plan";
83
+ PermissionMode["DONT_ASK"] = "dontAsk";
84
+ PermissionMode["BYPASS"] = "bypassPermissions";
85
+ // Legacy aliases (normalised in constructor)
86
+ PermissionMode["ASK"] = "ask";
87
+ PermissionMode["AUTO_EDIT"] = "auto-edit";
88
+ PermissionMode["AUTO"] = "auto";
89
+ })(PermissionMode || (exports.PermissionMode = PermissionMode = {}));
90
+ // ── Tool → Rule category mapping ───────────────────────────────────────────────
91
+ /**
92
+ * Maps internal tool names to their Claude Code rule category.
93
+ * Rule category is the prefix used in rule strings: Bash(…), Read(…), etc.
94
+ */
95
+ const TOOL_CATEGORY = {
96
+ shell_exec: "Bash",
97
+ file_run: "Bash",
98
+ file_read: "Read",
99
+ glob_search: "Read",
100
+ grep_search: "Read",
101
+ file_write: "Edit",
102
+ file_edit: "Edit",
103
+ multi_edit: "Edit",
104
+ file_delete: "Delete",
105
+ web_fetch: "WebFetch",
106
+ web_search: "WebSearch",
107
+ sub_agent: "Agent",
108
+ };
109
+ /** Write/exec tools that are blocked in Plan mode */
110
+ const PLAN_BLOCKED = new Set(["shell_exec", "file_run", "file_write", "file_edit", "multi_edit", "file_delete"]);
111
+ /** File-edit tools that are auto-allowed in acceptEdits mode */
112
+ const ACCEPT_EDITS_AUTO = new Set(["file_write", "file_edit", "multi_edit"]);
113
+ /**
114
+ * Tools whose "don't ask again" approval is stored permanently to disk
115
+ * (per project directory + command). Matches the Bash row in the tiered table.
116
+ */
117
+ const PERMANENT_APPROVAL_TOOLS = new Set(["shell_exec", "file_run"]);
118
+ function parseRule(rule) {
119
+ const parenIdx = rule.indexOf("(");
120
+ if (parenIdx === -1) {
121
+ return { category: rule.trim(), specifier: null };
122
+ }
123
+ const closeIdx = rule.lastIndexOf(")");
124
+ if (closeIdx === -1 || closeIdx < parenIdx)
125
+ return null;
126
+ const category = rule.substring(0, parenIdx).trim();
127
+ let specifier = rule.substring(parenIdx + 1, closeIdx).trim();
128
+ // Tool(*) is identical to Tool (match all)
129
+ if (specifier === "*")
130
+ return { category, specifier: null };
131
+ // Normalize deprecated ":*" suffix → " *" (e.g. "git add:*" → "git add *")
132
+ if (specifier.endsWith(":*") && category !== "WebFetch") {
133
+ specifier = specifier.slice(0, -2) + " *";
134
+ }
135
+ return { category, specifier };
136
+ }
137
+ // ── Bash pattern matching ──────────────────────────────────────────────────────
138
+ /**
139
+ * Shell operator pattern — used to block wildcard rules from matching
140
+ * compound commands like "safe-cmd && evil-cmd".
141
+ */
142
+ const SHELL_OP_RE = /(?:^|[\s;])(?:&&|\|\||;(?!;)|\|)(?:[\s]|$)/;
143
+ function escapeRegex(s) {
144
+ return s.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
145
+ }
146
+ /**
147
+ * Match a Bash command against a specifier.
148
+ *
149
+ * Rules:
150
+ * - No wildcard → exact match only.
151
+ * - "ls *" → matches "ls -la" but NOT "lsof" (space enforces word boundary).
152
+ * - "ls*" → matches both.
153
+ * - Wildcard rules do NOT match commands that contain shell operators
154
+ * (&&, ||, ;, |) to prevent bypass via compound commands.
155
+ */
156
+ function matchBash(command, specifier) {
157
+ if (!specifier.includes("*")) {
158
+ return command === specifier;
159
+ }
160
+ // Wildcard: reject compound commands
161
+ if (SHELL_OP_RE.test(command))
162
+ return false;
163
+ // Convert glob-style specifier to anchored regex
164
+ const regexStr = "^" + specifier.split("*").map(escapeRegex).join(".*") + "$";
165
+ return new RegExp(regexStr).test(command);
166
+ }
167
+ // ── WebFetch domain matching ───────────────────────────────────────────────────
168
+ /**
169
+ * Match a URL against a WebFetch specifier.
170
+ * "domain:example.com" matches example.com and *.example.com
171
+ */
172
+ function matchWebFetch(url, specifier) {
173
+ if (specifier.startsWith("domain:")) {
174
+ const domain = specifier.substring(7);
175
+ try {
176
+ const hostname = new URL(url).hostname;
177
+ return hostname === domain || hostname.endsWith("." + domain);
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ }
183
+ return url === specifier;
184
+ }
185
+ // ── MCP tool matching ─────────────────────────────────────────────────────────
186
+ /**
187
+ * Match an MCP tool name against a rule.
188
+ *
189
+ * MCP tools are named: mcp__<server>__<tool>
190
+ * Rules can be:
191
+ * mcp__puppeteer → matches all tools from "puppeteer" server
192
+ * mcp__puppeteer__* → same (wildcard matches all tools)
193
+ * mcp__puppeteer__navigate → matches only the "navigate" tool
194
+ */
195
+ function matchMcpTool(toolName, ruleCategory, _specifier) {
196
+ // Rule without specifier (parsed from "mcp__puppeteer" or "mcp__puppeteer__*")
197
+ // ruleCategory is the full rule string when no parens are used
198
+ const ruleParts = ruleCategory.split("__");
199
+ const toolParts = toolName.split("__");
200
+ // Both must start with "mcp"
201
+ if (ruleParts[0] !== "mcp" || toolParts[0] !== "mcp")
202
+ return false;
203
+ // Server must match
204
+ if (ruleParts.length < 2 || toolParts.length < 2)
205
+ return false;
206
+ if (ruleParts[1] !== toolParts[1])
207
+ return false;
208
+ // Rule is just mcp__server → matches all tools from that server
209
+ if (ruleParts.length === 2)
210
+ return true;
211
+ // Rule is mcp__server__* → matches all tools from that server
212
+ if (ruleParts[2] === "*")
213
+ return true;
214
+ // Rule is mcp__server__tool → exact tool match
215
+ if (toolParts.length < 3)
216
+ return false;
217
+ return ruleParts[2] === toolParts[2];
218
+ }
219
+ // ── Settings file loading ──────────────────────────────────────────────────────
220
+ function loadSettingsFile(filePath) {
221
+ try {
222
+ if (!fs.existsSync(filePath))
223
+ return null;
224
+ const raw = fs.readFileSync(filePath, "utf-8");
225
+ const data = JSON.parse(raw);
226
+ if (!data.permissions)
227
+ return null;
228
+ const p = data.permissions;
229
+ return {
230
+ allow: Array.isArray(p.allow) ? p.allow : [],
231
+ ask: Array.isArray(p.ask) ? p.ask : [],
232
+ deny: Array.isArray(p.deny) ? p.deny : [],
233
+ };
234
+ }
235
+ catch {
236
+ return null;
237
+ }
238
+ }
239
+ // ── PermissionManager ──────────────────────────────────────────────────────────
240
+ class PermissionManager {
241
+ mode;
242
+ projectDir = null;
243
+ cwd;
244
+ customPromptFn = null;
245
+ sandboxManager = null;
246
+ // Runtime stored rules (legacy: allow once / always / project-scoped)
247
+ globalRules = [];
248
+ projectRules = [];
249
+ /**
250
+ * Session-only approvals for file-modification tools (file_write, file_edit).
251
+ * Per the tiered permission table: "Until session end" — not written to disk.
252
+ * Key = toolName (no specifier needed; file edit approvals cover all paths).
253
+ */
254
+ sessionApprovals = new Set();
255
+ // Settings-based rules loaded from .claude/settings.json files
256
+ settingsAllow = [];
257
+ settingsAsk = [];
258
+ settingsDeny = [];
259
+ // Managed settings — highest priority, cannot be overridden by user/project
260
+ managedSettings = {};
261
+ managedAllow = [];
262
+ managedAsk = [];
263
+ managedDeny = [];
264
+ constructor(mode = PermissionMode.DEFAULT, projectDir) {
265
+ this.projectDir = projectDir || null;
266
+ this.cwd = projectDir || process.cwd();
267
+ this.loadManagedSettings();
268
+ this.loadRuntimeRules();
269
+ this.loadSettingsRules();
270
+ // defaultMode from settings files can override the constructor argument
271
+ const settingsMode = this.readDefaultModeFromSettings();
272
+ this.mode = this.normalizeMode(settingsMode ?? mode);
273
+ // Managed policy: disableBypassPermissionsMode prevents bypass mode
274
+ if (this.managedSettings.disableBypassPermissionsMode === "disable" &&
275
+ this.mode === PermissionMode.BYPASS) {
276
+ this.mode = PermissionMode.DEFAULT;
277
+ }
278
+ }
279
+ // ── Managed settings loading ───────────────────────────────────────────────
280
+ /**
281
+ * Load managed settings from system-level config.
282
+ * Managed settings have the highest priority and cannot be overridden.
283
+ *
284
+ * Paths:
285
+ * macOS: /Library/Application Support/cdoing/managed-settings.json
286
+ * Linux: /etc/cdoing/managed-settings.json
287
+ */
288
+ loadManagedSettings() {
289
+ const managed = loadSettingsFile(MANAGED_SETTINGS_FILE)
290
+ || loadSettingsFile(MANAGED_SETTINGS_FILE_CLAUDE);
291
+ if (managed) {
292
+ this.managedAllow = managed.allow;
293
+ this.managedAsk = managed.ask;
294
+ this.managedDeny = managed.deny;
295
+ }
296
+ // Load managed-only settings (disableBypassPermissionsMode, etc.)
297
+ for (const filePath of [MANAGED_SETTINGS_FILE, MANAGED_SETTINGS_FILE_CLAUDE]) {
298
+ try {
299
+ if (!fs.existsSync(filePath))
300
+ continue;
301
+ const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
302
+ this.managedSettings = {
303
+ disableBypassPermissionsMode: data.disableBypassPermissionsMode,
304
+ allowManagedPermissionRulesOnly: data.allowManagedPermissionRulesOnly,
305
+ allowManagedHooksOnly: data.allowManagedHooksOnly,
306
+ allowManagedMcpServersOnly: data.allowManagedMcpServersOnly,
307
+ };
308
+ break; // Use first found
309
+ }
310
+ catch { /* skip malformed files */ }
311
+ }
312
+ }
313
+ // ── Mode normalisation ─────────────────────────────────────────────────────
314
+ /** Collapse legacy aliases into canonical mode values. */
315
+ normalizeMode(mode) {
316
+ switch (mode) {
317
+ case "ask": return PermissionMode.DEFAULT;
318
+ case "auto-edit": return PermissionMode.ACCEPT_EDITS;
319
+ case "auto": return PermissionMode.BYPASS;
320
+ default: return mode;
321
+ }
322
+ }
323
+ /**
324
+ * Read `defaultMode` from settings files (local → shared → user).
325
+ * Higher-precedence files win. Returns null if no file sets it.
326
+ */
327
+ readDefaultModeFromSettings() {
328
+ const candidates = [
329
+ // Local project overrides (highest priority)
330
+ this.projectDir ? path.join(this.projectDir, ".cdoing", "settings.local.json") : null,
331
+ this.projectDir ? path.join(this.projectDir, ".claude", "settings.local.json") : null,
332
+ // Shared project settings
333
+ this.projectDir ? path.join(this.projectDir, ".cdoing", "settings.json") : null,
334
+ this.projectDir ? path.join(this.projectDir, ".claude", "settings.json") : null,
335
+ // Global user settings
336
+ USER_SETTINGS_FILE,
337
+ USER_SETTINGS_FILE_CLAUDE,
338
+ ];
339
+ for (const filePath of candidates) {
340
+ if (!filePath)
341
+ continue;
342
+ try {
343
+ if (!fs.existsSync(filePath))
344
+ continue;
345
+ const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
346
+ if (data.defaultMode)
347
+ return this.normalizeMode(data.defaultMode);
348
+ }
349
+ catch { /* skip malformed files */ }
350
+ }
351
+ return null;
352
+ }
353
+ // ── Public API ────────────────────────────────────────────────────────────
354
+ setPromptFn(fn) {
355
+ this.customPromptFn = fn;
356
+ }
357
+ setSandboxManager(sm) {
358
+ this.sandboxManager = sm;
359
+ }
360
+ setMode(mode) {
361
+ const normalized = this.normalizeMode(mode);
362
+ // Managed policy: prevent bypass mode if disabled by admin
363
+ if (this.managedSettings.disableBypassPermissionsMode === "disable" &&
364
+ normalized === PermissionMode.BYPASS) {
365
+ return; // Silently refuse to switch to bypass mode
366
+ }
367
+ this.mode = normalized;
368
+ }
369
+ getMode() {
370
+ return this.mode;
371
+ }
372
+ /**
373
+ * Check whether a specific file path is denied by settings rules for a given category.
374
+ * Used by shell_exec to check extracted paths against Read/Edit/Delete rules.
375
+ *
376
+ * Returns "deny" if a deny rule matches the path for the given category.
377
+ * Returns null otherwise (no deny found — the command can proceed through normal permission flow).
378
+ */
379
+ checkPathPermission(filePath, category) {
380
+ // Only check deny rules — we're not auto-allowing shell commands based on Edit/Read allow rules
381
+ for (const rule of this.settingsDeny) {
382
+ const parsed = parseRule(rule);
383
+ if (!parsed)
384
+ continue;
385
+ if (parsed.category !== category)
386
+ continue;
387
+ if (parsed.specifier === null)
388
+ return "deny"; // Deny all of this category
389
+ if ((0, path_matching_1.matchPath)(filePath, parsed.specifier, this.projectDir ?? this.cwd, this.cwd)) {
390
+ return "deny";
391
+ }
392
+ }
393
+ return null;
394
+ }
395
+ /** Check whether a file path matches any ask rule for the given category. */
396
+ pathMatchesAsk(filePath, category) {
397
+ for (const rule of this.settingsAsk) {
398
+ const parsed = parseRule(rule);
399
+ if (!parsed)
400
+ continue;
401
+ if (parsed.category !== category)
402
+ continue;
403
+ if (parsed.specifier === null)
404
+ return true;
405
+ if ((0, path_matching_1.matchPath)(filePath, parsed.specifier, this.projectDir ?? this.cwd, this.cwd)) {
406
+ return true;
407
+ }
408
+ }
409
+ return false;
410
+ }
411
+ setProjectDir(dir) {
412
+ this.projectDir = dir;
413
+ this.cwd = dir;
414
+ this.loadProjectRules();
415
+ this.loadSettingsRules();
416
+ // Re-evaluate defaultMode when project changes (local settings may differ)
417
+ const settingsMode = this.readDefaultModeFromSettings();
418
+ if (settingsMode)
419
+ this.mode = settingsMode;
420
+ }
421
+ // ── Settings rules loading ─────────────────────────────────────────────────
422
+ /**
423
+ * Load and merge permission rules from settings files.
424
+ *
425
+ * Precedence (highest to lowest, docs order):
426
+ * 1. Local project — <project>/.claude/settings.local.json (highest)
427
+ * 2. Shared project — <project>/.claude/settings.json
428
+ * 3. User — ~/.claude/settings.json (lowest)
429
+ *
430
+ * Rule evaluation uses "first match wins", so higher-precedence rules are
431
+ * stored at the front of the allow/ask arrays.
432
+ *
433
+ * Deny rules from ALL sources are merged — a deny at any level cannot be
434
+ * overridden by an allow at any other level.
435
+ */
436
+ loadSettingsRules() {
437
+ // Managed rules always apply (highest priority)
438
+ const allow = [...this.managedAllow];
439
+ const ask = [...this.managedAsk];
440
+ const deny = [...this.managedDeny];
441
+ // If allowManagedPermissionRulesOnly is set, skip user/project rules entirely
442
+ if (!this.managedSettings.allowManagedPermissionRulesOnly) {
443
+ // Load each layer (null if file doesn't exist)
444
+ // Load from .cdoing/ (our format) with .claude/ fallback (Claude Code compat)
445
+ const local = this.projectDir
446
+ ? (loadSettingsFile(path.join(this.projectDir, ".cdoing", "settings.local.json"))
447
+ || loadSettingsFile(path.join(this.projectDir, ".claude", "settings.local.json")))
448
+ : null;
449
+ const shared = this.projectDir
450
+ ? (loadSettingsFile(path.join(this.projectDir, ".cdoing", "settings.json"))
451
+ || loadSettingsFile(path.join(this.projectDir, ".claude", "settings.json")))
452
+ : null;
453
+ const user = loadSettingsFile(USER_SETTINGS_FILE)
454
+ || loadSettingsFile(USER_SETTINGS_FILE_CLAUDE);
455
+ // For allow/ask: highest precedence first so "first match wins" works correctly.
456
+ // Order: managed → local → shared → user
457
+ const orderedSources = [local, shared, user];
458
+ for (const src of orderedSources) {
459
+ if (!src)
460
+ continue;
461
+ allow.push(...src.allow);
462
+ ask.push(...src.ask);
463
+ // Deny rules are collected from every layer — any deny wins
464
+ deny.push(...src.deny);
465
+ }
466
+ }
467
+ this.settingsAllow = allow;
468
+ this.settingsAsk = ask;
469
+ this.settingsDeny = deny;
470
+ }
471
+ /** Return the currently loaded settings rules (for display / debugging). */
472
+ getSettingsRules() {
473
+ return {
474
+ allow: [...this.settingsAllow],
475
+ ask: [...this.settingsAsk],
476
+ deny: [...this.settingsDeny],
477
+ };
478
+ }
479
+ // ── Rule matching ──────────────────────────────────────────────────────────
480
+ /**
481
+ * Test whether a settings rule string covers a specific tool invocation.
482
+ */
483
+ matchesRule(rule, toolName, input) {
484
+ const parsed = parseRule(rule);
485
+ if (!parsed)
486
+ return false;
487
+ const category = TOOL_CATEGORY[toolName] || toolName;
488
+ // Rule must target this tool's category (or the raw tool name)
489
+ if (parsed.category !== category && parsed.category !== toolName)
490
+ return false;
491
+ // No specifier → match all uses of this tool
492
+ if (parsed.specifier === null)
493
+ return true;
494
+ // Specifier matching is category-specific
495
+ switch (category) {
496
+ case "Bash": {
497
+ const cmd = String(input.command || "");
498
+ return matchBash(cmd, parsed.specifier);
499
+ }
500
+ case "Read":
501
+ case "Edit":
502
+ case "Delete": {
503
+ const fp = String(input.file_path || input.path || input.pattern || "");
504
+ if (!fp)
505
+ return false;
506
+ return (0, path_matching_1.matchPath)(fp, parsed.specifier, this.projectDir ?? this.cwd, this.cwd);
507
+ }
508
+ case "WebFetch": {
509
+ const url = String(input.url || "");
510
+ return matchWebFetch(url, parsed.specifier);
511
+ }
512
+ case "Agent": {
513
+ const name = String(input.name || input.agent_name || "");
514
+ return name === parsed.specifier;
515
+ }
516
+ default: {
517
+ // MCP tools: match mcp__server or mcp__server__tool patterns
518
+ // e.g. rule "mcp__puppeteer" matches tool "mcp__puppeteer__navigate"
519
+ // rule "mcp__puppeteer__*" matches all tools from puppeteer server
520
+ // rule "mcp__puppeteer__navigate" matches exact tool
521
+ if (toolName.startsWith("mcp__") && parsed.category.startsWith("mcp__")) {
522
+ return matchMcpTool(toolName, parsed.category, parsed.specifier);
523
+ }
524
+ // Fallback: compare specifier against first input value
525
+ const first = String(Object.values(input)[0] ?? "");
526
+ return first === parsed.specifier;
527
+ }
528
+ }
529
+ }
530
+ /**
531
+ * Evaluate all settings rules for a tool call.
532
+ *
533
+ * Evaluation order per docs: deny → ask → allow.
534
+ * - deny wins over everything.
535
+ * - ask overrides allow (lets you "ask for rm *" even when "allow Bash").
536
+ * - allow auto-approves when no deny/ask rule matched.
537
+ * Returns null when no rule matches (fall back to mode behaviour).
538
+ */
539
+ evaluateRules(toolName, input) {
540
+ // 1. Deny — checked first, always wins
541
+ for (const rule of this.settingsDeny) {
542
+ if (this.matchesRule(rule, toolName, input))
543
+ return "deny";
544
+ }
545
+ // 2. Ask — overrides allow for fine-grained "still prompt" control
546
+ for (const rule of this.settingsAsk) {
547
+ if (this.matchesRule(rule, toolName, input))
548
+ return "ask";
549
+ }
550
+ // 3. Allow — auto-approve if no deny/ask matched
551
+ for (const rule of this.settingsAllow) {
552
+ if (this.matchesRule(rule, toolName, input))
553
+ return "allow";
554
+ }
555
+ return null;
556
+ }
557
+ // ── Permission request ─────────────────────────────────────────────────────
558
+ /**
559
+ * Determine whether a tool call is allowed.
560
+ *
561
+ * Decision flow:
562
+ * 1. bypassPermissions mode → allow all.
563
+ * 2. Tools that never require permission → allow.
564
+ * 3. Settings deny rule matches → deny.
565
+ * 4. Settings ask rule matches → prompt user (overrides stored/allow).
566
+ * 5. Settings allow rule matches → allow.
567
+ * 6. Session-only approval exists (file-edit tools) → allow.
568
+ * 7. Runtime stored rule matches (Bash tools, persisted) → allow.
569
+ * 8. Fall back to mode:
570
+ * - plan → deny write/exec tools.
571
+ * - dontAsk → deny anything not explicitly allowed.
572
+ * - acceptEdits → allow file edits; prompt for others.
573
+ * - default → prompt user.
574
+ */
575
+ async requestPermission(toolDef, input) {
576
+ if (this.mode === PermissionMode.BYPASS)
577
+ return true;
578
+ if (!toolDef.requiresPermission)
579
+ return true;
580
+ const toolName = toolDef.name;
581
+ const ruleResult = this.evaluateRules(toolName, input);
582
+ if (ruleResult === "deny")
583
+ return false;
584
+ // ask rule: prompt even if a stored/allow rule would otherwise bypass it
585
+ if (ruleResult === "ask") {
586
+ return this.askUser(toolName, this.describeAction(toolDef, input), input);
587
+ }
588
+ if (ruleResult === "allow")
589
+ return true;
590
+ // For Bash tools: check path-level ask rules against files extracted from the command.
591
+ // e.g. "ask": ["Edit(src/sensitive/*)"] triggers when mv/cp/> targets that path.
592
+ if (ruleResult === null && TOOL_CATEGORY[toolName] === "Bash") {
593
+ const cmd = String(input.command || "");
594
+ if (cmd) {
595
+ const paths = (0, shell_paths_1.extractShellPaths)(cmd, this.cwd);
596
+ const needsAsk = paths.read.some((p) => this.pathMatchesAsk(p, "Read")) ||
597
+ paths.write.some((p) => this.pathMatchesAsk(p, "Edit")) ||
598
+ paths.delete.some((p) => this.pathMatchesAsk(p, "Delete"));
599
+ if (needsAsk) {
600
+ return this.askUser(toolName, this.describeAction(toolDef, input), input);
601
+ }
602
+ }
603
+ }
604
+ // Session-only approval for file-modification tools ("until session end")
605
+ if (this.sessionApprovals.has(toolName))
606
+ return true;
607
+ // Persistent stored rules for Bash tools ("permanently per project + command")
608
+ if (this.hasStoredPermission(toolName, input))
609
+ return true;
610
+ if (this.mode === PermissionMode.PLAN)
611
+ return !PLAN_BLOCKED.has(toolName);
612
+ if (this.mode === PermissionMode.DONT_ASK)
613
+ return false;
614
+ if (this.mode === PermissionMode.ACCEPT_EDITS && ACCEPT_EDITS_AUTO.has(toolName))
615
+ return true;
616
+ // Sandbox auto-allow mode: if sandbox is enabled and in auto-allow mode,
617
+ // auto-approve commands that pass sandbox checks (no user prompt needed).
618
+ if (this.sandboxManager?.isEnabled() && this.sandboxManager.getMode() === "auto-allow") {
619
+ const category = TOOL_CATEGORY[toolName];
620
+ if (category === "Bash") {
621
+ const cmd = String(input.command || "");
622
+ const check = this.sandboxManager.checkShellCommand(cmd, input.dangerouslyDisableSandbox);
623
+ if (check.allowed)
624
+ return true;
625
+ }
626
+ else if (category === "Edit") {
627
+ const fp = String(input.file_path || "");
628
+ if (fp) {
629
+ const check = this.sandboxManager.checkFileWrite(fp);
630
+ if (check.allowed)
631
+ return true;
632
+ }
633
+ }
634
+ }
635
+ return this.askUser(toolName, this.describeAction(toolDef, input), input);
636
+ }
637
+ // ── Runtime stored rules (legacy) ─────────────────────────────────────────
638
+ getProjectPermissionsFile() {
639
+ if (!this.projectDir)
640
+ return null;
641
+ return path.join(this.projectDir, ".cdoing", "permissions.json");
642
+ }
643
+ loadRuntimeRules() {
644
+ this.loadGlobalRules();
645
+ this.loadProjectRules();
646
+ }
647
+ loadGlobalRules() {
648
+ try {
649
+ if (fs.existsSync(GLOBAL_PERMISSIONS_FILE)) {
650
+ const data = JSON.parse(fs.readFileSync(GLOBAL_PERMISSIONS_FILE, "utf-8"));
651
+ this.globalRules = Array.isArray(data.rules) ? data.rules : [];
652
+ }
653
+ }
654
+ catch {
655
+ this.globalRules = [];
656
+ }
657
+ }
658
+ loadProjectRules() {
659
+ this.projectRules = [];
660
+ const file = this.getProjectPermissionsFile();
661
+ if (!file)
662
+ return;
663
+ try {
664
+ if (fs.existsSync(file)) {
665
+ const data = JSON.parse(fs.readFileSync(file, "utf-8"));
666
+ this.projectRules = Array.isArray(data.rules) ? data.rules : [];
667
+ }
668
+ }
669
+ catch {
670
+ this.projectRules = [];
671
+ }
672
+ }
673
+ saveGlobalRules() {
674
+ if (!fs.existsSync(GLOBAL_CDOING_DIR)) {
675
+ fs.mkdirSync(GLOBAL_CDOING_DIR, { recursive: true });
676
+ }
677
+ fs.writeFileSync(GLOBAL_PERMISSIONS_FILE, JSON.stringify({ rules: this.globalRules }, null, 2), "utf-8");
678
+ }
679
+ saveProjectRules() {
680
+ const file = this.getProjectPermissionsFile();
681
+ if (!file)
682
+ return;
683
+ const dir = path.dirname(file);
684
+ if (!fs.existsSync(dir))
685
+ fs.mkdirSync(dir, { recursive: true });
686
+ fs.writeFileSync(file, JSON.stringify({ rules: this.projectRules }, null, 2), "utf-8");
687
+ }
688
+ hasStoredPermission(toolName, input) {
689
+ const all = [...this.globalRules, ...this.projectRules];
690
+ return all.some((rule) => {
691
+ if (rule.tool !== toolName)
692
+ return false;
693
+ if (!rule.inputMatch)
694
+ return true;
695
+ const value = String(input.file_path ?? input.command ?? input.pattern ?? Object.values(input)[0] ?? "");
696
+ return value === rule.inputMatch;
697
+ });
698
+ }
699
+ addRule(toolName, scope, inputMatch) {
700
+ // Build a settings-style rule string (e.g., "Edit", "Bash(npm test *)")
701
+ const category = TOOL_CATEGORY[toolName] || toolName;
702
+ const ruleString = inputMatch ? `${category}(${inputMatch})` : category;
703
+ // Persist to settings.json (like Claude Code does)
704
+ this.addToSettingsAllow(ruleString, scope);
705
+ // Also add session approval for immediate effect
706
+ this.sessionApprovals.add(toolName);
707
+ // Legacy: persist to permissions.json for backward compat
708
+ if (PERMANENT_APPROVAL_TOOLS.has(toolName)) {
709
+ const rules = scope === "project" ? this.projectRules : this.globalRules;
710
+ const exists = rules.some((r) => r.tool === toolName && r.inputMatch === inputMatch);
711
+ if (!exists) {
712
+ rules.push({ tool: toolName, inputMatch, createdAt: new Date().toISOString() });
713
+ scope === "project" ? this.saveProjectRules() : this.saveGlobalRules();
714
+ }
715
+ }
716
+ }
717
+ /**
718
+ * Save a deny rule to settings.json so it persists across sessions.
719
+ * Creates the .cdoing/ folder and settings.json file if they don't exist.
720
+ */
721
+ addDenyRule(toolName, scope, inputMatch) {
722
+ const category = TOOL_CATEGORY[toolName] || toolName;
723
+ const ruleString = inputMatch ? `${category}(${inputMatch})` : category;
724
+ // Persist deny to settings.json
725
+ this.addToSettingsDeny(ruleString, scope);
726
+ }
727
+ /**
728
+ * Persist a permission rule to settings.json.
729
+ *
730
+ * Writes to:
731
+ * - Project scope → <project>/.cdoing/settings.json
732
+ * - Global scope → ~/.cdoing/settings.json
733
+ */
734
+ addToSettingsAllow(rule, scope) {
735
+ this.addToSettingsField(rule, "allow", scope);
736
+ }
737
+ /**
738
+ * Persist a deny rule to settings.json.
739
+ *
740
+ * Writes to:
741
+ * - Project scope → <project>/.cdoing/settings.json
742
+ * - Global scope → ~/.cdoing/settings.json
743
+ */
744
+ addToSettingsDeny(rule, scope) {
745
+ this.addToSettingsField(rule, "deny", scope);
746
+ }
747
+ /**
748
+ * Persist a permission rule to a specific field (allow/deny/ask) in settings.json.
749
+ * Creates the directory and file if they don't exist.
750
+ */
751
+ addToSettingsField(rule, field, scope) {
752
+ const filePath = scope === "project" && this.projectDir
753
+ ? path.join(this.projectDir, ".cdoing", "settings.json")
754
+ : USER_SETTINGS_FILE;
755
+ try {
756
+ const dir = path.dirname(filePath);
757
+ if (!fs.existsSync(dir))
758
+ fs.mkdirSync(dir, { recursive: true });
759
+ let data = {};
760
+ if (fs.existsSync(filePath)) {
761
+ data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
762
+ }
763
+ if (!data.permissions)
764
+ data.permissions = {};
765
+ const perms = data.permissions;
766
+ if (!Array.isArray(perms[field]))
767
+ perms[field] = [];
768
+ // Don't add duplicates
769
+ if (!perms[field].includes(rule)) {
770
+ perms[field].push(rule);
771
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
772
+ // Reload settings so the new rule takes effect immediately
773
+ this.loadSettingsRules();
774
+ }
775
+ }
776
+ catch {
777
+ // If we can't write settings, fall back to session-only
778
+ }
779
+ }
780
+ /** Check whether bypass mode is disabled by managed settings. */
781
+ isBypassDisabled() {
782
+ return this.managedSettings.disableBypassPermissionsMode === "disable";
783
+ }
784
+ /** Return managed-only settings for external inspection. */
785
+ getManagedSettings() {
786
+ return { ...this.managedSettings };
787
+ }
788
+ /**
789
+ * Remove a permission rule from settings.json.
790
+ * Removes from the specified field (allow/deny/ask) in the given scope.
791
+ */
792
+ removeSettingsRule(rule, field, scope) {
793
+ const filePath = scope === "project" && this.projectDir
794
+ ? path.join(this.projectDir, ".cdoing", "settings.json")
795
+ : USER_SETTINGS_FILE;
796
+ try {
797
+ if (!fs.existsSync(filePath))
798
+ return;
799
+ const data = JSON.parse(fs.readFileSync(filePath, "utf-8"));
800
+ if (!data.permissions)
801
+ return;
802
+ const perms = data.permissions;
803
+ if (!Array.isArray(perms[field]))
804
+ return;
805
+ const idx = perms[field].indexOf(rule);
806
+ if (idx !== -1) {
807
+ perms[field].splice(idx, 1);
808
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
809
+ this.loadSettingsRules();
810
+ }
811
+ }
812
+ catch { /* ignore errors */ }
813
+ }
814
+ removeRule(toolName, scope) {
815
+ if (scope === "project" || !scope) {
816
+ this.projectRules = toolName
817
+ ? this.projectRules.filter((r) => r.tool !== toolName)
818
+ : [];
819
+ this.saveProjectRules();
820
+ }
821
+ if (scope === "global" || !scope) {
822
+ this.globalRules = toolName
823
+ ? this.globalRules.filter((r) => r.tool !== toolName)
824
+ : [];
825
+ this.saveGlobalRules();
826
+ }
827
+ }
828
+ getStoredRules() {
829
+ return { global: this.globalRules, project: this.projectRules };
830
+ }
831
+ // ── User prompt ────────────────────────────────────────────────────────────
832
+ describeAction(toolDef, input) {
833
+ if (toolDef.permissionMessage) {
834
+ const msg = toolDef.permissionMessage(input);
835
+ if (msg && !msg.includes("undefined"))
836
+ return msg;
837
+ }
838
+ const value = input.file_path ?? input.command ?? input.pattern ?? Object.values(input)[0];
839
+ return `${toolDef.name.replace(/_/g, " ")}: ${value ?? "(no details)"}`;
840
+ }
841
+ /**
842
+ * Ask the user whether to allow a tool call.
843
+ *
844
+ * Options:
845
+ * y / Enter → Allow once
846
+ * a → Always allow globally (~/.cdoing/permissions.json)
847
+ * p → Allow for this project (.cdoing/permissions.json)
848
+ * n → Deny
849
+ *
850
+ * Uses customPromptFn if set (e.g. VS Code UI), otherwise falls back to CLI readline.
851
+ */
852
+ async askUser(toolName, message, input) {
853
+ const hasProject = !!this.projectDir;
854
+ const label = toolName.replace(/_/g, " ");
855
+ // Build input match for persistent rules (command for Bash, path for Edit/Read/Delete)
856
+ const category = TOOL_CATEGORY[toolName] || toolName;
857
+ let inputMatch;
858
+ if (input) {
859
+ if (category === "Bash") {
860
+ inputMatch = String(input.command || "") || undefined;
861
+ }
862
+ else if (category === "Read" || category === "Edit" || category === "Delete") {
863
+ inputMatch = String(input.file_path || input.path || input.pattern || "") || undefined;
864
+ }
865
+ else if (category === "WebFetch") {
866
+ const url = String(input.url || "");
867
+ try {
868
+ inputMatch = `domain:${new URL(url).hostname}`;
869
+ }
870
+ catch {
871
+ inputMatch = url || undefined;
872
+ }
873
+ }
874
+ }
875
+ if (this.customPromptFn) {
876
+ const choice = await this.customPromptFn(toolName, message, hasProject);
877
+ if (choice === "always") {
878
+ this.addRule(toolName, "global", inputMatch);
879
+ return true;
880
+ }
881
+ if (choice === "project" && hasProject) {
882
+ this.addRule(toolName, "project", inputMatch);
883
+ return true;
884
+ }
885
+ if (choice === "deny_always") {
886
+ this.addDenyRule(toolName, "global", inputMatch);
887
+ return false;
888
+ }
889
+ if (choice === "deny_project" && hasProject) {
890
+ this.addDenyRule(toolName, "project", inputMatch);
891
+ return false;
892
+ }
893
+ if (choice === "deny")
894
+ return false;
895
+ return choice === "allow";
896
+ }
897
+ // CLI readline fallback
898
+ return new Promise((resolve) => {
899
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
900
+ const projectHint = hasProject ? ` · (p)roject allow` : "";
901
+ const denyProjectHint = hasProject ? ` · (dp) deny project` : "";
902
+ rl.question(`\n \x1b[33m⚡ Permission:\x1b[0m ${message}\n` +
903
+ ` \x1b[2m(y)es, allow once · (a)lways allow${projectHint} · (n)o, deny once · (d)eny always${denyProjectHint}\x1b[0m\n` +
904
+ ` \x1b[2mChoice [Y/a${hasProject ? "/p" : ""}/n/d${hasProject ? "/dp" : ""}]:\x1b[0m `, (answer) => {
905
+ rl.close();
906
+ const a = answer.trim().toLowerCase();
907
+ if (a === "a" || a === "always") {
908
+ this.addRule(toolName, "global", inputMatch);
909
+ console.log(` \x1b[32m✓ Permission saved globally for ${label}\x1b[0m`);
910
+ resolve(true);
911
+ }
912
+ else if ((a === "p" || a === "project") && hasProject) {
913
+ this.addRule(toolName, "project", inputMatch);
914
+ console.log(` \x1b[32m✓ Permission saved for project for ${label}\x1b[0m`);
915
+ resolve(true);
916
+ }
917
+ else if (a === "d" || a === "deny always") {
918
+ this.addDenyRule(toolName, "global", inputMatch);
919
+ console.log(` \x1b[31m✗ Deny rule saved globally for ${label}\x1b[0m`);
920
+ resolve(false);
921
+ }
922
+ else if ((a === "dp" || a === "deny project") && hasProject) {
923
+ this.addDenyRule(toolName, "project", inputMatch);
924
+ console.log(` \x1b[31m✗ Deny rule saved for project for ${label}\x1b[0m`);
925
+ resolve(false);
926
+ }
927
+ else if (a === "n" || a === "no") {
928
+ resolve(false);
929
+ }
930
+ else {
931
+ resolve(true); // y / Enter → allow once
932
+ }
933
+ });
934
+ });
935
+ }
936
+ }
937
+ exports.PermissionManager = PermissionManager;
938
+ //# sourceMappingURL=index.js.map