@oxagen/cli 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/dist/agent/__tests__/judge.test.js +11 -6
  2. package/dist/agent/__tests__/judge.test.js.map +1 -1
  3. package/dist/agent/__tests__/loop-errors.test.js +32 -1
  4. package/dist/agent/__tests__/loop-errors.test.js.map +1 -1
  5. package/dist/agent/__tests__/loop-gating.test.d.ts +2 -0
  6. package/dist/agent/__tests__/loop-gating.test.d.ts.map +1 -0
  7. package/dist/agent/__tests__/loop-gating.test.js +94 -0
  8. package/dist/agent/__tests__/loop-gating.test.js.map +1 -0
  9. package/dist/agent/__tests__/model-router.test.js +16 -0
  10. package/dist/agent/__tests__/model-router.test.js.map +1 -1
  11. package/dist/agent/__tests__/permissions.test.d.ts +2 -0
  12. package/dist/agent/__tests__/permissions.test.d.ts.map +1 -0
  13. package/dist/agent/__tests__/permissions.test.js +206 -0
  14. package/dist/agent/__tests__/permissions.test.js.map +1 -0
  15. package/dist/agent/__tests__/pipeline-telemetry.test.d.ts +2 -0
  16. package/dist/agent/__tests__/pipeline-telemetry.test.d.ts.map +1 -0
  17. package/dist/agent/__tests__/pipeline-telemetry.test.js +158 -0
  18. package/dist/agent/__tests__/pipeline-telemetry.test.js.map +1 -0
  19. package/dist/agent/__tests__/pipeline.test.js +12 -3
  20. package/dist/agent/__tests__/pipeline.test.js.map +1 -1
  21. package/dist/agent/__tests__/rate-card.test.d.ts +2 -0
  22. package/dist/agent/__tests__/rate-card.test.d.ts.map +1 -0
  23. package/dist/agent/__tests__/rate-card.test.js +69 -0
  24. package/dist/agent/__tests__/rate-card.test.js.map +1 -0
  25. package/dist/agent/__tests__/system-prompt.test.d.ts +2 -0
  26. package/dist/agent/__tests__/system-prompt.test.d.ts.map +1 -0
  27. package/dist/agent/__tests__/system-prompt.test.js +29 -0
  28. package/dist/agent/__tests__/system-prompt.test.js.map +1 -0
  29. package/dist/agent/__tests__/trace-format-verbose.test.d.ts +2 -0
  30. package/dist/agent/__tests__/trace-format-verbose.test.d.ts.map +1 -0
  31. package/dist/agent/__tests__/trace-format-verbose.test.js +93 -0
  32. package/dist/agent/__tests__/trace-format-verbose.test.js.map +1 -0
  33. package/dist/agent/__tests__/verbose-log.test.d.ts +2 -0
  34. package/dist/agent/__tests__/verbose-log.test.d.ts.map +1 -0
  35. package/dist/agent/__tests__/verbose-log.test.js +69 -0
  36. package/dist/agent/__tests__/verbose-log.test.js.map +1 -0
  37. package/dist/agent/evaluator.d.ts.map +1 -1
  38. package/dist/agent/evaluator.js +8 -4
  39. package/dist/agent/evaluator.js.map +1 -1
  40. package/dist/agent/fleet/orchestrator.d.ts +5 -0
  41. package/dist/agent/fleet/orchestrator.d.ts.map +1 -1
  42. package/dist/agent/fleet/orchestrator.js +3 -0
  43. package/dist/agent/fleet/orchestrator.js.map +1 -1
  44. package/dist/agent/fleet/types.d.ts +2 -0
  45. package/dist/agent/fleet/types.d.ts.map +1 -1
  46. package/dist/agent/judge.d.ts +11 -3
  47. package/dist/agent/judge.d.ts.map +1 -1
  48. package/dist/agent/judge.js +15 -3
  49. package/dist/agent/judge.js.map +1 -1
  50. package/dist/agent/loop.d.ts +35 -0
  51. package/dist/agent/loop.d.ts.map +1 -1
  52. package/dist/agent/loop.js +147 -48
  53. package/dist/agent/loop.js.map +1 -1
  54. package/dist/agent/model-router.d.ts +5 -19
  55. package/dist/agent/model-router.d.ts.map +1 -1
  56. package/dist/agent/model-router.js +19 -42
  57. package/dist/agent/model-router.js.map +1 -1
  58. package/dist/agent/permissions.d.ts +103 -0
  59. package/dist/agent/permissions.d.ts.map +1 -0
  60. package/dist/agent/permissions.js +245 -0
  61. package/dist/agent/permissions.js.map +1 -0
  62. package/dist/agent/pipeline.d.ts +13 -3
  63. package/dist/agent/pipeline.d.ts.map +1 -1
  64. package/dist/agent/pipeline.js +66 -15
  65. package/dist/agent/pipeline.js.map +1 -1
  66. package/dist/agent/planner.d.ts +3 -0
  67. package/dist/agent/planner.d.ts.map +1 -1
  68. package/dist/agent/planner.js +14 -1
  69. package/dist/agent/planner.js.map +1 -1
  70. package/dist/agent/prompt-enhancer.d.ts +9 -0
  71. package/dist/agent/prompt-enhancer.d.ts.map +1 -1
  72. package/dist/agent/prompt-enhancer.js +23 -1
  73. package/dist/agent/prompt-enhancer.js.map +1 -1
  74. package/dist/agent/rate-card.d.ts +77 -0
  75. package/dist/agent/rate-card.d.ts.map +1 -0
  76. package/dist/agent/rate-card.js +99 -0
  77. package/dist/agent/rate-card.js.map +1 -0
  78. package/dist/agent/system-prompt.d.ts +5 -0
  79. package/dist/agent/system-prompt.d.ts.map +1 -1
  80. package/dist/agent/system-prompt.js +13 -4
  81. package/dist/agent/system-prompt.js.map +1 -1
  82. package/dist/agent/tools.d.ts +8 -4
  83. package/dist/agent/tools.d.ts.map +1 -1
  84. package/dist/agent/tools.js +35 -4
  85. package/dist/agent/tools.js.map +1 -1
  86. package/dist/agent/trace-format.d.ts +7 -0
  87. package/dist/agent/trace-format.d.ts.map +1 -1
  88. package/dist/agent/trace-format.js +114 -0
  89. package/dist/agent/trace-format.js.map +1 -1
  90. package/dist/agent/trace.d.ts +59 -0
  91. package/dist/agent/trace.d.ts.map +1 -1
  92. package/dist/agent/verbose-log.d.ts +8 -0
  93. package/dist/agent/verbose-log.d.ts.map +1 -0
  94. package/dist/agent/verbose-log.js +53 -0
  95. package/dist/agent/verbose-log.js.map +1 -0
  96. package/dist/agents/__tests__/loader.test.d.ts +2 -0
  97. package/dist/agents/__tests__/loader.test.d.ts.map +1 -0
  98. package/dist/agents/__tests__/loader.test.js +88 -0
  99. package/dist/agents/__tests__/loader.test.js.map +1 -0
  100. package/dist/agents/__tests__/tools.test.d.ts +2 -0
  101. package/dist/agents/__tests__/tools.test.d.ts.map +1 -0
  102. package/dist/agents/__tests__/tools.test.js +40 -0
  103. package/dist/agents/__tests__/tools.test.js.map +1 -0
  104. package/dist/agents/index.d.ts +12 -0
  105. package/dist/agents/index.d.ts.map +1 -0
  106. package/dist/agents/index.js +4 -0
  107. package/dist/agents/index.js.map +1 -0
  108. package/dist/agents/loader.d.ts +25 -0
  109. package/dist/agents/loader.d.ts.map +1 -0
  110. package/dist/agents/loader.js +133 -0
  111. package/dist/agents/loader.js.map +1 -0
  112. package/dist/agents/tools.d.ts +14 -0
  113. package/dist/agents/tools.d.ts.map +1 -0
  114. package/dist/agents/tools.js +21 -0
  115. package/dist/agents/tools.js.map +1 -0
  116. package/dist/agents/types.d.ts +27 -0
  117. package/dist/agents/types.d.ts.map +1 -0
  118. package/dist/agents/types.js +11 -0
  119. package/dist/agents/types.js.map +1 -0
  120. package/dist/agents/write.d.ts +10 -0
  121. package/dist/agents/write.d.ts.map +1 -0
  122. package/dist/agents/write.js +28 -0
  123. package/dist/agents/write.js.map +1 -0
  124. package/dist/commands/__tests__/agent.test.d.ts +2 -0
  125. package/dist/commands/__tests__/agent.test.d.ts.map +1 -0
  126. package/dist/commands/__tests__/agent.test.js +82 -0
  127. package/dist/commands/__tests__/agent.test.js.map +1 -0
  128. package/dist/commands/__tests__/command.test.d.ts +2 -0
  129. package/dist/commands/__tests__/command.test.d.ts.map +1 -0
  130. package/dist/commands/__tests__/command.test.js +73 -0
  131. package/dist/commands/__tests__/command.test.js.map +1 -0
  132. package/dist/commands/__tests__/cost.test.d.ts +2 -0
  133. package/dist/commands/__tests__/cost.test.d.ts.map +1 -0
  134. package/dist/commands/__tests__/cost.test.js +139 -0
  135. package/dist/commands/__tests__/cost.test.js.map +1 -0
  136. package/dist/commands/__tests__/graph.pull.test.d.ts +2 -0
  137. package/dist/commands/__tests__/graph.pull.test.d.ts.map +1 -0
  138. package/dist/commands/__tests__/graph.pull.test.js +259 -0
  139. package/dist/commands/__tests__/graph.pull.test.js.map +1 -0
  140. package/dist/commands/__tests__/mcp.test.d.ts +2 -0
  141. package/dist/commands/__tests__/mcp.test.d.ts.map +1 -0
  142. package/dist/commands/__tests__/mcp.test.js +88 -0
  143. package/dist/commands/__tests__/mcp.test.js.map +1 -0
  144. package/dist/commands/__tests__/rules.test.d.ts +2 -0
  145. package/dist/commands/__tests__/rules.test.d.ts.map +1 -0
  146. package/dist/commands/__tests__/rules.test.js +95 -0
  147. package/dist/commands/__tests__/rules.test.js.map +1 -0
  148. package/dist/commands/__tests__/settings.test.d.ts +2 -0
  149. package/dist/commands/__tests__/settings.test.d.ts.map +1 -0
  150. package/dist/commands/__tests__/settings.test.js +83 -0
  151. package/dist/commands/__tests__/settings.test.js.map +1 -0
  152. package/dist/commands/agent.d.ts +15 -0
  153. package/dist/commands/agent.d.ts.map +1 -0
  154. package/dist/commands/agent.js +58 -0
  155. package/dist/commands/agent.js.map +1 -0
  156. package/dist/commands/command.d.ts +17 -0
  157. package/dist/commands/command.d.ts.map +1 -0
  158. package/dist/commands/command.js +74 -0
  159. package/dist/commands/command.js.map +1 -0
  160. package/dist/commands/config.js.map +1 -1
  161. package/dist/commands/cost.d.ts +10 -0
  162. package/dist/commands/cost.d.ts.map +1 -0
  163. package/dist/commands/cost.js +140 -0
  164. package/dist/commands/cost.js.map +1 -0
  165. package/dist/commands/graph.pull.d.ts +15 -0
  166. package/dist/commands/graph.pull.d.ts.map +1 -0
  167. package/dist/commands/graph.pull.js +137 -0
  168. package/dist/commands/graph.pull.js.map +1 -0
  169. package/dist/commands/graph.status.d.ts +10 -0
  170. package/dist/commands/graph.status.d.ts.map +1 -0
  171. package/dist/commands/graph.status.js +75 -0
  172. package/dist/commands/graph.status.js.map +1 -0
  173. package/dist/commands/mcp.d.ts +18 -0
  174. package/dist/commands/mcp.d.ts.map +1 -0
  175. package/dist/commands/mcp.js +183 -0
  176. package/dist/commands/mcp.js.map +1 -0
  177. package/dist/commands/rules.d.ts +19 -0
  178. package/dist/commands/rules.d.ts.map +1 -0
  179. package/dist/commands/rules.js +96 -0
  180. package/dist/commands/rules.js.map +1 -0
  181. package/dist/commands/settings.d.ts +10 -0
  182. package/dist/commands/settings.d.ts.map +1 -0
  183. package/dist/commands/settings.js +117 -0
  184. package/dist/commands/settings.js.map +1 -0
  185. package/dist/index.js +297 -1
  186. package/dist/index.js.map +1 -1
  187. package/dist/lib/config.d.ts +2 -0
  188. package/dist/lib/config.d.ts.map +1 -1
  189. package/dist/lib/config.js.map +1 -1
  190. package/dist/mcp/__tests__/client.test.d.ts +2 -0
  191. package/dist/mcp/__tests__/client.test.d.ts.map +1 -0
  192. package/dist/mcp/__tests__/client.test.js +101 -0
  193. package/dist/mcp/__tests__/client.test.js.map +1 -0
  194. package/dist/mcp/client.d.ts +89 -0
  195. package/dist/mcp/client.d.ts.map +1 -0
  196. package/dist/mcp/client.js +201 -0
  197. package/dist/mcp/client.js.map +1 -0
  198. package/dist/repl/__tests__/components.test.js +50 -1
  199. package/dist/repl/__tests__/components.test.js.map +1 -1
  200. package/dist/repl/components.d.ts +18 -1
  201. package/dist/repl/components.d.ts.map +1 -1
  202. package/dist/repl/components.js +32 -2
  203. package/dist/repl/components.js.map +1 -1
  204. package/dist/repl/interactive.d.ts +5 -0
  205. package/dist/repl/interactive.d.ts.map +1 -1
  206. package/dist/repl/interactive.js +124 -11
  207. package/dist/repl/interactive.js.map +1 -1
  208. package/dist/repl/one-shot.d.ts +16 -0
  209. package/dist/repl/one-shot.d.ts.map +1 -1
  210. package/dist/repl/one-shot.js +71 -1
  211. package/dist/repl/one-shot.js.map +1 -1
  212. package/dist/rules/__tests__/enforce.test.d.ts +2 -0
  213. package/dist/rules/__tests__/enforce.test.d.ts.map +1 -0
  214. package/dist/rules/__tests__/enforce.test.js +58 -0
  215. package/dist/rules/__tests__/enforce.test.js.map +1 -0
  216. package/dist/rules/__tests__/loader.test.d.ts +2 -0
  217. package/dist/rules/__tests__/loader.test.d.ts.map +1 -0
  218. package/dist/rules/__tests__/loader.test.js +54 -0
  219. package/dist/rules/__tests__/loader.test.js.map +1 -0
  220. package/dist/rules/enforce.d.ts +23 -0
  221. package/dist/rules/enforce.d.ts.map +1 -0
  222. package/dist/rules/enforce.js +36 -0
  223. package/dist/rules/enforce.js.map +1 -0
  224. package/dist/rules/index.d.ts +10 -0
  225. package/dist/rules/index.d.ts.map +1 -0
  226. package/dist/rules/index.js +4 -0
  227. package/dist/rules/index.js.map +1 -0
  228. package/dist/rules/loader.d.ts +10 -0
  229. package/dist/rules/loader.d.ts.map +1 -0
  230. package/dist/rules/loader.js +77 -0
  231. package/dist/rules/loader.js.map +1 -0
  232. package/dist/rules/types.d.ts +39 -0
  233. package/dist/rules/types.d.ts.map +1 -0
  234. package/dist/rules/types.js +16 -0
  235. package/dist/rules/types.js.map +1 -0
  236. package/dist/rules/write.d.ts +10 -0
  237. package/dist/rules/write.d.ts.map +1 -0
  238. package/dist/rules/write.js +28 -0
  239. package/dist/rules/write.js.map +1 -0
  240. package/dist/settings/__tests__/gate.test.d.ts +2 -0
  241. package/dist/settings/__tests__/gate.test.d.ts.map +1 -0
  242. package/dist/settings/__tests__/gate.test.js +137 -0
  243. package/dist/settings/__tests__/gate.test.js.map +1 -0
  244. package/dist/settings/__tests__/hooks.test.d.ts +2 -0
  245. package/dist/settings/__tests__/hooks.test.d.ts.map +1 -0
  246. package/dist/settings/__tests__/hooks.test.js +103 -0
  247. package/dist/settings/__tests__/hooks.test.js.map +1 -0
  248. package/dist/settings/__tests__/mcp-write.test.d.ts +2 -0
  249. package/dist/settings/__tests__/mcp-write.test.d.ts.map +1 -0
  250. package/dist/settings/__tests__/mcp-write.test.js +77 -0
  251. package/dist/settings/__tests__/mcp-write.test.js.map +1 -0
  252. package/dist/settings/__tests__/permissions-gate.test.d.ts +2 -0
  253. package/dist/settings/__tests__/permissions-gate.test.d.ts.map +1 -0
  254. package/dist/settings/__tests__/permissions-gate.test.js +75 -0
  255. package/dist/settings/__tests__/permissions-gate.test.js.map +1 -0
  256. package/dist/settings/__tests__/resolve.test.d.ts +2 -0
  257. package/dist/settings/__tests__/resolve.test.d.ts.map +1 -0
  258. package/dist/settings/__tests__/resolve.test.js +109 -0
  259. package/dist/settings/__tests__/resolve.test.js.map +1 -0
  260. package/dist/settings/__tests__/runtime.test.d.ts +2 -0
  261. package/dist/settings/__tests__/runtime.test.d.ts.map +1 -0
  262. package/dist/settings/__tests__/runtime.test.js +72 -0
  263. package/dist/settings/__tests__/runtime.test.js.map +1 -0
  264. package/dist/settings/__tests__/schema.test.d.ts +2 -0
  265. package/dist/settings/__tests__/schema.test.d.ts.map +1 -0
  266. package/dist/settings/__tests__/schema.test.js +55 -0
  267. package/dist/settings/__tests__/schema.test.js.map +1 -0
  268. package/dist/settings/__tests__/write.test.d.ts +2 -0
  269. package/dist/settings/__tests__/write.test.d.ts.map +1 -0
  270. package/dist/settings/__tests__/write.test.js +61 -0
  271. package/dist/settings/__tests__/write.test.js.map +1 -0
  272. package/dist/settings/gate.d.ts +34 -0
  273. package/dist/settings/gate.d.ts.map +1 -0
  274. package/dist/settings/gate.js +50 -0
  275. package/dist/settings/gate.js.map +1 -0
  276. package/dist/settings/hooks.d.ts +26 -0
  277. package/dist/settings/hooks.d.ts.map +1 -0
  278. package/dist/settings/hooks.js +115 -0
  279. package/dist/settings/hooks.js.map +1 -0
  280. package/dist/settings/index.d.ts +23 -0
  281. package/dist/settings/index.d.ts.map +1 -0
  282. package/dist/settings/index.js +23 -0
  283. package/dist/settings/index.js.map +1 -0
  284. package/dist/settings/mcp-write.d.ts +41 -0
  285. package/dist/settings/mcp-write.d.ts.map +1 -0
  286. package/dist/settings/mcp-write.js +61 -0
  287. package/dist/settings/mcp-write.js.map +1 -0
  288. package/dist/settings/permissions-gate.d.ts +19 -0
  289. package/dist/settings/permissions-gate.d.ts.map +1 -0
  290. package/dist/settings/permissions-gate.js +103 -0
  291. package/dist/settings/permissions-gate.js.map +1 -0
  292. package/dist/settings/resolve.d.ts +37 -0
  293. package/dist/settings/resolve.d.ts.map +1 -0
  294. package/dist/settings/resolve.js +169 -0
  295. package/dist/settings/resolve.js.map +1 -0
  296. package/dist/settings/runtime.d.ts +28 -0
  297. package/dist/settings/runtime.d.ts.map +1 -0
  298. package/dist/settings/runtime.js +45 -0
  299. package/dist/settings/runtime.js.map +1 -0
  300. package/dist/settings/schema.d.ts +1387 -0
  301. package/dist/settings/schema.d.ts.map +1 -0
  302. package/dist/settings/schema.js +146 -0
  303. package/dist/settings/schema.js.map +1 -0
  304. package/dist/settings/write.d.ts +37 -0
  305. package/dist/settings/write.d.ts.map +1 -0
  306. package/dist/settings/write.js +85 -0
  307. package/dist/settings/write.js.map +1 -0
  308. package/dist/slash/__tests__/expand.test.d.ts +2 -0
  309. package/dist/slash/__tests__/expand.test.d.ts.map +1 -0
  310. package/dist/slash/__tests__/expand.test.js +44 -0
  311. package/dist/slash/__tests__/expand.test.js.map +1 -0
  312. package/dist/slash/__tests__/loader.test.d.ts +2 -0
  313. package/dist/slash/__tests__/loader.test.d.ts.map +1 -0
  314. package/dist/slash/__tests__/loader.test.js +48 -0
  315. package/dist/slash/__tests__/loader.test.js.map +1 -0
  316. package/dist/slash/expand.d.ts +32 -0
  317. package/dist/slash/expand.d.ts.map +1 -0
  318. package/dist/slash/expand.js +48 -0
  319. package/dist/slash/expand.js.map +1 -0
  320. package/dist/slash/index.d.ts +11 -0
  321. package/dist/slash/index.d.ts.map +1 -0
  322. package/dist/slash/index.js +4 -0
  323. package/dist/slash/index.js.map +1 -0
  324. package/dist/slash/loader.d.ts +10 -0
  325. package/dist/slash/loader.d.ts.map +1 -0
  326. package/dist/slash/loader.js +64 -0
  327. package/dist/slash/loader.js.map +1 -0
  328. package/dist/slash/types.d.ts +24 -0
  329. package/dist/slash/types.d.ts.map +1 -0
  330. package/dist/slash/types.js +11 -0
  331. package/dist/slash/types.js.map +1 -0
  332. package/dist/slash/write.d.ts +10 -0
  333. package/dist/slash/write.d.ts.map +1 -0
  334. package/dist/slash/write.js +26 -0
  335. package/dist/slash/write.js.map +1 -0
  336. package/dist/tui/fleet-view/index.d.ts.map +1 -1
  337. package/dist/tui/fleet-view/index.js +5 -1
  338. package/dist/tui/fleet-view/index.js.map +1 -1
  339. package/package.json +5 -1
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { applySettingsToEnv } from "../runtime.js";
6
+ import { getScopePaths, clearSettingsCache } from "../resolve.js";
7
+ let dir;
8
+ let userPath;
9
+ const TOUCHED = ["OXAGEN_MODEL", "OXAGEN_API_URL", "OXAGEN_RT_TEST_A", "OXAGEN_RT_TEST_B"];
10
+ const saved = {};
11
+ function writeProject(body) {
12
+ const path = getScopePaths({ cwd: dir, userSettingsPath: userPath }).project;
13
+ mkdirSync(join(path, ".."), { recursive: true });
14
+ writeFileSync(path, JSON.stringify(body), "utf8");
15
+ }
16
+ function apply() {
17
+ return applySettingsToEnv({ cwd: dir, userSettingsPath: userPath, noCache: true });
18
+ }
19
+ beforeEach(() => {
20
+ dir = mkdtempSync(join(tmpdir(), "oxagen-runtime-"));
21
+ userPath = join(dir, "user-settings.json");
22
+ for (const k of TOUCHED) {
23
+ saved[k] = process.env[k];
24
+ delete process.env[k];
25
+ }
26
+ clearSettingsCache();
27
+ });
28
+ afterEach(() => {
29
+ rmSync(dir, { recursive: true, force: true });
30
+ for (const k of TOUCHED) {
31
+ if (saved[k] === undefined)
32
+ delete process.env[k];
33
+ else
34
+ process.env[k] = saved[k];
35
+ }
36
+ clearSettingsCache();
37
+ });
38
+ describe("applySettingsToEnv", () => {
39
+ it("fills unset env vars from settings.env", () => {
40
+ writeProject({ env: { OXAGEN_RT_TEST_A: "from-settings" } });
41
+ const applied = apply();
42
+ expect(process.env["OXAGEN_RT_TEST_A"]).toBe("from-settings");
43
+ expect(applied.envKeys).toContain("OXAGEN_RT_TEST_A");
44
+ });
45
+ it("does not override an env var the shell already set", () => {
46
+ process.env["OXAGEN_RT_TEST_B"] = "from-shell";
47
+ writeProject({ env: { OXAGEN_RT_TEST_B: "from-settings" } });
48
+ const applied = apply();
49
+ expect(process.env["OXAGEN_RT_TEST_B"]).toBe("from-shell");
50
+ expect(applied.envKeys).not.toContain("OXAGEN_RT_TEST_B");
51
+ });
52
+ it("projects apiUrl and model into OXAGEN_API_URL / OXAGEN_MODEL when unset", () => {
53
+ writeProject({ model: "vendor/model", apiUrl: "https://api.example.com" });
54
+ const applied = apply();
55
+ expect(process.env["OXAGEN_MODEL"]).toBe("vendor/model");
56
+ expect(process.env["OXAGEN_API_URL"]).toBe("https://api.example.com");
57
+ expect(applied.model).toBe(true);
58
+ expect(applied.apiUrl).toBe(true);
59
+ });
60
+ it("leaves a shell-set OXAGEN_MODEL untouched", () => {
61
+ process.env["OXAGEN_MODEL"] = "shell/model";
62
+ writeProject({ model: "settings/model" });
63
+ const applied = apply();
64
+ expect(process.env["OXAGEN_MODEL"]).toBe("shell/model");
65
+ expect(applied.model).toBe(false);
66
+ });
67
+ it("is a no-op when there are no settings", () => {
68
+ const applied = apply();
69
+ expect(applied).toEqual({ envKeys: [], apiUrl: false, model: false });
70
+ });
71
+ });
72
+ //# sourceMappingURL=runtime.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.test.js","sourceRoot":"","sources":["../../../src/settings/__tests__/runtime.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.test.d.ts","sourceRoot":"","sources":["../../../src/settings/__tests__/schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { oxagenSettingsSchema, permissionsSchema, hooksSchema } from "../schema.js";
3
+ describe("oxagenSettingsSchema", () => {
4
+ it("parses a full settings document", () => {
5
+ const parsed = oxagenSettingsSchema.parse({
6
+ model: "anthropic/claude-sonnet-4.6",
7
+ apiUrl: "https://api.oxagen.sh",
8
+ env: { FOO: "bar" },
9
+ permissions: { defaultMode: "default", deny: ["Bash(rm -rf*)"], allow: ["Bash(git*)"] },
10
+ hooks: { PreToolUse: [{ matcher: "bash", hooks: [{ type: "command", command: "echo hi" }] }] },
11
+ mcpServers: {
12
+ github: { transport: "stdio", command: "npx", args: ["-y", "server-github"] },
13
+ },
14
+ toolVisibility: { github: { include: ["create_*"] } },
15
+ });
16
+ expect(parsed.model).toBe("anthropic/claude-sonnet-4.6");
17
+ expect(parsed.permissions?.deny).toContain("Bash(rm -rf*)");
18
+ expect(parsed.mcpServers?.github?.transport).toBe("stdio");
19
+ });
20
+ it("defaults a hook action's type to 'command'", () => {
21
+ const parsed = hooksSchema.parse({
22
+ PreToolUse: [{ hooks: [{ command: "echo hi" }] }],
23
+ });
24
+ expect(parsed.PreToolUse?.[0]?.hooks[0]?.type).toBe("command");
25
+ });
26
+ it("rejects an invalid apiUrl", () => {
27
+ expect(() => oxagenSettingsSchema.parse({ apiUrl: "not-a-url" })).toThrow();
28
+ });
29
+ it("rejects an unknown permission mode", () => {
30
+ expect(() => permissionsSchema.parse({ defaultMode: "yolo" })).toThrow();
31
+ });
32
+ it("rejects a hook matcher with no actions", () => {
33
+ expect(() => hooksSchema.parse({ PreToolUse: [{ matcher: "*", hooks: [] }] })).toThrow();
34
+ });
35
+ it("passes through forward-compat sections (commands/skills)", () => {
36
+ const parsed = oxagenSettingsSchema.parse({
37
+ model: "x/y",
38
+ commands: ["./.oxagen/commands"],
39
+ skills: ["./.oxagen/skills"],
40
+ });
41
+ expect(parsed["commands"]).toEqual(["./.oxagen/commands"]);
42
+ expect(parsed["skills"]).toEqual(["./.oxagen/skills"]);
43
+ });
44
+ it("validates inline agent definitions (prompt required)", () => {
45
+ const parsed = oxagenSettingsSchema.parse({
46
+ agents: { reviewer: { description: "d", prompt: "You review.", tools: ["Read"], model: "z" } },
47
+ });
48
+ expect(parsed.agents?.reviewer?.prompt).toBe("You review.");
49
+ expect(() => oxagenSettingsSchema.parse({ agents: { bad: { model: "z" } } })).toThrow();
50
+ });
51
+ it("accepts an empty document", () => {
52
+ expect(oxagenSettingsSchema.parse({})).toEqual({});
53
+ });
54
+ });
55
+ //# sourceMappingURL=schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../../src/settings/__tests__/schema.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=write.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.test.d.ts","sourceRoot":"","sources":["../../../src/settings/__tests__/write.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, rmSync, readFileSync, existsSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { writeSettingsValue, writeStarterSettings } from "../write.js";
6
+ import { loadSettings, getScopePaths, clearSettingsCache } from "../resolve.js";
7
+ let dir;
8
+ let userPath;
9
+ beforeEach(() => {
10
+ dir = mkdtempSync(join(tmpdir(), "oxagen-write-"));
11
+ userPath = join(dir, "user-settings.json");
12
+ clearSettingsCache();
13
+ });
14
+ afterEach(() => {
15
+ rmSync(dir, { recursive: true, force: true });
16
+ clearSettingsCache();
17
+ });
18
+ describe("writeSettingsValue", () => {
19
+ it("writes a scalar into the project scope and round-trips through loadSettings", () => {
20
+ const path = writeSettingsValue({ scope: "project", key: "model", value: "a/b", cwd: dir });
21
+ expect(existsSync(path)).toBe(true);
22
+ expect(path).toBe(getScopePaths({ cwd: dir }).project);
23
+ const reread = loadSettings({ cwd: dir, userSettingsPath: userPath, noCache: true }).settings;
24
+ expect(reread.model).toBe("a/b");
25
+ });
26
+ it("writes an env var via the env.NAME form", () => {
27
+ writeSettingsValue({ scope: "local", key: "env.MY_TOKEN", value: "sek", cwd: dir });
28
+ const reread = loadSettings({ cwd: dir, userSettingsPath: userPath, noCache: true }).settings;
29
+ expect(reread.env?.MY_TOKEN).toBe("sek");
30
+ });
31
+ it("merges into an existing file rather than clobbering it", () => {
32
+ writeSettingsValue({ scope: "project", key: "model", value: "a/b", cwd: dir });
33
+ writeSettingsValue({ scope: "project", key: "env.K", value: "v", cwd: dir });
34
+ const reread = loadSettings({ cwd: dir, userSettingsPath: userPath, noCache: true }).settings;
35
+ expect(reread.model).toBe("a/b");
36
+ expect(reread.env?.K).toBe("v");
37
+ });
38
+ it("rejects an unsupported key", () => {
39
+ expect(() => writeSettingsValue({ scope: "project", key: "permissions", value: "x", cwd: dir })).toThrow(/cannot set/);
40
+ });
41
+ it("rejects an empty env name", () => {
42
+ expect(() => writeSettingsValue({ scope: "project", key: "env.", value: "x", cwd: dir })).toThrow();
43
+ });
44
+ });
45
+ describe("writeStarterSettings", () => {
46
+ it("creates a valid starter file and is idempotent", () => {
47
+ const first = writeStarterSettings({ scope: "project", cwd: dir });
48
+ expect(first.created).toBe(true);
49
+ const doc = JSON.parse(readFileSync(first.path, "utf8"));
50
+ expect(doc["$schema"]).toBeTruthy();
51
+ expect(doc["permissions"]).toBeTruthy();
52
+ const second = writeStarterSettings({ scope: "project", cwd: dir });
53
+ expect(second.created).toBe(false);
54
+ });
55
+ it("starter file passes validation via loadSettings", () => {
56
+ writeStarterSettings({ scope: "project", cwd: dir });
57
+ const resolved = loadSettings({ cwd: dir, userSettingsPath: userPath, noCache: true });
58
+ expect(resolved.scopes.find((s) => s.scope === "project")?.error).toBeUndefined();
59
+ });
60
+ });
61
+ //# sourceMappingURL=write.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.test.js","sourceRoot":"","sources":["../../../src/settings/__tests__/write.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * gate.ts — Wraps the agent's local tools with the settings-driven permission
3
+ * gate and PreToolUse/PostToolUse hooks.
4
+ *
5
+ * This is the single chokepoint where `settings.json` becomes load-bearing for
6
+ * tool execution: every tool's `execute` is intercepted to (1) evaluate the
7
+ * permission rules and (2) fire hooks. A denied call or a blocking PreToolUse
8
+ * hook returns a plain string the model reads instead of running the tool — the
9
+ * loop continues, the side effect never happens.
10
+ */
11
+ import type { ToolSet } from "ai";
12
+ import type { Hooks, Permissions } from "./schema.js";
13
+ export interface ToolGateContext {
14
+ cwd: string;
15
+ permissions?: Permissions;
16
+ hooks?: Hooks;
17
+ /** Aborts in-flight hook commands when the turn is cancelled. */
18
+ signal?: AbortSignal;
19
+ /** Notified whenever a tool call is blocked (for surfacing in the UI/logs). */
20
+ onBlocked?: (toolName: string, reason: string) => void;
21
+ /**
22
+ * Map from a permission deny entry (e.g. `Edit(migrations/**)`) to a human
23
+ * message. When a workspace rule's guard produces the deny, the agent sees the
24
+ * rule text instead of the raw glob, so it self-corrects. (See the rules
25
+ * module / adherence loop.)
26
+ */
27
+ denyReasons?: Record<string, string>;
28
+ }
29
+ /**
30
+ * Return a copy of `tools` whose executes are gated. Tools without an `execute`
31
+ * (none today, but the type allows it) pass through untouched.
32
+ */
33
+ export declare function wrapToolsWithGate(tools: ToolSet, ctx: ToolGateContext): ToolSet;
34
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/settings/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAGlC,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iEAAiE;IACjE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAwD/E"}
@@ -0,0 +1,50 @@
1
+ import { runHooks } from "./hooks.js";
2
+ import { evaluateLocalPermission } from "./permissions-gate.js";
3
+ /**
4
+ * Return a copy of `tools` whose executes are gated. Tools without an `execute`
5
+ * (none today, but the type allows it) pass through untouched.
6
+ */
7
+ export function wrapToolsWithGate(tools, ctx) {
8
+ // Nothing to enforce → hand back the original set (zero overhead).
9
+ if (!ctx.permissions && !ctx.hooks)
10
+ return tools;
11
+ const out = {};
12
+ for (const [name, original] of Object.entries(tools)) {
13
+ const exec = original.execute;
14
+ if (!exec) {
15
+ out[name] = original;
16
+ continue;
17
+ }
18
+ const gated = async (input, options) => {
19
+ const perm = evaluateLocalPermission(name, input, ctx.permissions);
20
+ if (perm.decision === "deny") {
21
+ const ruleMessage = perm.rule ? ctx.denyReasons?.[perm.rule] : undefined;
22
+ const reason = ruleMessage
23
+ ? `Blocked by ${ruleMessage}`
24
+ : `Permission denied: \`${name}\` ${perm.reason}.`;
25
+ ctx.onBlocked?.(name, ruleMessage ?? perm.reason);
26
+ return `⛔ ${reason}`;
27
+ }
28
+ const pre = await runHooks(ctx.hooks, { event: "PreToolUse", cwd: ctx.cwd, tool: { name, input } }, ctx.signal);
29
+ if (pre.blocked) {
30
+ const reason = pre.reason ?? "blocked by PreToolUse hook";
31
+ ctx.onBlocked?.(name, reason);
32
+ return `⛔ Blocked by PreToolUse hook: ${reason}`;
33
+ }
34
+ // The AI SDK types a generic tool's output as `any`; narrow to `unknown`
35
+ // so we handle it safely (and never re-leak `any` to callers).
36
+ const result = await exec(input, options);
37
+ // PostToolUse is observational — its exit code never blocks.
38
+ await runHooks(ctx.hooks, {
39
+ event: "PostToolUse",
40
+ cwd: ctx.cwd,
41
+ tool: { name, input },
42
+ toolResult: typeof result === "string" ? result.slice(0, 2000) : undefined,
43
+ }, ctx.signal);
44
+ return result;
45
+ };
46
+ out[name] = { ...original, execute: gated };
47
+ }
48
+ return out;
49
+ }
50
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/settings/gate.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAsBhE;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc,EAAE,GAAoB;IACpE,mEAAmE;IACnE,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,GAAG,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IAEjD,MAAM,GAAG,GAAY,EAAE,CAAC;IACxB,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAkC,CAAC;QACzD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;YACrB,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAgB,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YAClD,MAAM,IAAI,GAAG,uBAAuB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;YACnE,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACzE,MAAM,MAAM,GAAG,WAAW;oBACxB,CAAC,CAAC,cAAc,WAAW,EAAE;oBAC7B,CAAC,CAAC,wBAAwB,IAAI,MAAM,IAAI,CAAC,MAAM,GAAG,CAAC;gBACrD,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;gBAClD,OAAO,KAAK,MAAM,EAAE,CAAC;YACvB,CAAC;YAED,MAAM,GAAG,GAAG,MAAM,QAAQ,CACxB,GAAG,CAAC,KAAK,EACT,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAC5D,GAAG,CAAC,MAAM,CACX,CAAC;YACF,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;gBAChB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,4BAA4B,CAAC;gBAC1D,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC9B,OAAO,iCAAiC,MAAM,EAAE,CAAC;YACnD,CAAC;YAED,yEAAyE;YACzE,+DAA+D;YAC/D,MAAM,MAAM,GAAY,MAAM,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAEnD,6DAA6D;YAC7D,MAAM,QAAQ,CACZ,GAAG,CAAC,KAAK,EACT;gBACE,KAAK,EAAE,aAAa;gBACpB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;gBACrB,UAAU,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aAC3E,EACD,GAAG,CAAC,MAAM,CACX,CAAC;YAEF,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC;QAEF,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { HookEvent, Hooks } from "./schema.js";
2
+ export interface HookPayload {
3
+ event: HookEvent;
4
+ cwd: string;
5
+ /** Present for PreToolUse / PostToolUse. */
6
+ tool?: {
7
+ name: string;
8
+ input: unknown;
9
+ };
10
+ /** Present for PostToolUse: the (clipped) result string the tool returned. */
11
+ toolResult?: string;
12
+ }
13
+ export interface HookRunOutcome {
14
+ /** PreToolUse only: a hook blocked the tool (non-zero exit). */
15
+ blocked: boolean;
16
+ /** Human-readable reason when blocked (hook stderr/stdout). */
17
+ reason?: string;
18
+ /** Concatenated stdout of all hooks that ran (used for SessionStart context). */
19
+ output: string;
20
+ }
21
+ /**
22
+ * Run all hooks registered for an event. Returns whether the action should be
23
+ * blocked (PreToolUse) and any captured stdout (SessionStart context).
24
+ */
25
+ export declare function runHooks(hooks: Hooks | undefined, payload: HookPayload, signal?: AbortSignal): Promise<HookRunOutcome>;
26
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/settings/hooks.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAc,SAAS,EAAe,KAAK,EAAE,MAAM,aAAa,CAAC;AAI7E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IACxC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;CAChB;AA4ED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,KAAK,GAAG,SAAS,EACxB,OAAO,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CA6BzB"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * hooks.ts — The lifecycle hook engine for the CLI agent loop.
3
+ *
4
+ * Hooks are shell commands declared in `settings.json` that fire on agent
5
+ * lifecycle events. They receive the event payload as JSON on stdin (Claude Code
6
+ * parity), so a hook can inspect what the agent is about to do.
7
+ *
8
+ * Three events are wired today, each with distinct, load-bearing behavior:
9
+ *
10
+ * - SessionStart runs once before the turn. Anything a hook prints to stdout
11
+ * is appended to the system prompt as additional context
12
+ * (e.g. inject the current sprint, on-call notes, a changelog).
13
+ * - PreToolUse runs before a tool executes. A non-zero exit BLOCKS the tool
14
+ * — the model receives the hook's message instead of running it
15
+ * (e.g. forbid `git push`, gate writes to protected paths).
16
+ * - PostToolUse runs after a tool executes (side effects only — auto-format a
17
+ * file after write_file, emit a notification). Never blocks.
18
+ *
19
+ * Matchers are globs over the tool name (PreToolUse / PostToolUse). SessionStart
20
+ * ignores the matcher and runs every action.
21
+ */
22
+ import { spawn } from "node:child_process";
23
+ import { matchGlob } from "@oxagen/mcp-config/permissions";
24
+ const DEFAULT_HOOK_TIMEOUT_MS = 60_000;
25
+ /** Run one hook command, feeding the payload JSON on stdin. Never throws. */
26
+ function execHook(action, payloadJson, cwd, signal) {
27
+ return new Promise((resolvePromise) => {
28
+ const child = spawn("/bin/bash", ["-c", action.command], {
29
+ cwd,
30
+ env: process.env,
31
+ stdio: ["pipe", "pipe", "pipe"],
32
+ });
33
+ let stdout = "";
34
+ let stderr = "";
35
+ let settled = false;
36
+ const finish = (result) => {
37
+ if (settled)
38
+ return;
39
+ settled = true;
40
+ clearTimeout(timer);
41
+ resolvePromise(result);
42
+ };
43
+ const timeoutMs = Math.min(action.timeoutMs ?? DEFAULT_HOOK_TIMEOUT_MS, 600_000);
44
+ const timer = setTimeout(() => {
45
+ child.kill("SIGKILL");
46
+ finish({ code: 124, stdout, stderr: stderr + `\n[hook timed out after ${timeoutMs}ms]` });
47
+ }, timeoutMs);
48
+ const onAbort = () => {
49
+ child.kill("SIGKILL");
50
+ finish({ code: 130, stdout, stderr: stderr + "\n[hook aborted]" });
51
+ };
52
+ if (signal) {
53
+ if (signal.aborted)
54
+ onAbort();
55
+ else
56
+ signal.addEventListener("abort", onAbort, { once: true });
57
+ }
58
+ child.stdout.on("data", (d) => (stdout += d.toString()));
59
+ child.stderr.on("data", (d) => (stderr += d.toString()));
60
+ child.on("error", (err) => finish({ code: 1, stdout, stderr: stderr + String(err) }));
61
+ child.on("close", (code) => finish({ code: code ?? 0, stdout, stderr }));
62
+ // A hook that exits before reading stdin makes the write below emit EPIPE
63
+ // asynchronously on the stdin stream. Swallow it — the exit code is what
64
+ // matters, and an unhandled stream error would otherwise crash the process.
65
+ child.stdin.on("error", () => { });
66
+ try {
67
+ child.stdin.write(payloadJson);
68
+ child.stdin.end();
69
+ }
70
+ catch {
71
+ /* stdin may already be closed if the command exits immediately */
72
+ }
73
+ });
74
+ }
75
+ /** Which matchers apply for this event + tool. SessionStart ignores the matcher. */
76
+ function selectMatchers(event, matchers, toolName) {
77
+ if (event === "SessionStart")
78
+ return matchers;
79
+ if (!toolName)
80
+ return [];
81
+ return matchers.filter((m) => matchGlob(m.matcher ?? "*", toolName));
82
+ }
83
+ /**
84
+ * Run all hooks registered for an event. Returns whether the action should be
85
+ * blocked (PreToolUse) and any captured stdout (SessionStart context).
86
+ */
87
+ export async function runHooks(hooks, payload, signal) {
88
+ const none = { blocked: false, output: "" };
89
+ if (!hooks)
90
+ return none;
91
+ const matchers = hooks[payload.event];
92
+ if (!matchers || matchers.length === 0)
93
+ return none;
94
+ const selected = selectMatchers(payload.event, matchers, payload.tool?.name);
95
+ if (selected.length === 0)
96
+ return none;
97
+ const payloadJson = JSON.stringify(payload);
98
+ const outputs = [];
99
+ for (const matcher of selected) {
100
+ for (const action of matcher.hooks) {
101
+ const result = await execHook(action, payloadJson, payload.cwd, signal);
102
+ if (result.stdout.trim())
103
+ outputs.push(result.stdout.trim());
104
+ // PreToolUse is the only blocking event: a non-zero exit vetoes the tool.
105
+ if (payload.event === "PreToolUse" && result.code !== 0) {
106
+ const reason = result.stderr.trim() ||
107
+ result.stdout.trim() ||
108
+ `hook \`${action.command}\` exited ${result.code}`;
109
+ return { blocked: true, reason, output: outputs.join("\n") };
110
+ }
111
+ }
112
+ }
113
+ return { blocked: false, output: outputs.join("\n") };
114
+ }
115
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/settings/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @module settings — The unified `settings.json` driver for the Oxagen CLI.
3
+ *
4
+ * One hierarchical settings file (user → project → local) configures the CLI's
5
+ * model, environment, platform URL, tool permissions, and lifecycle hooks —
6
+ * the spine the MCP-client, agents, commands, and skills subsystems plug into.
7
+ *
8
+ * Public surface:
9
+ * - loadSettings / getScopePaths / clearSettingsCache — resolution
10
+ * - applySettingsToEnv — startup projection
11
+ * - wrapToolsWithGate — permission + hook gate
12
+ * - runHooks — lifecycle hook engine
13
+ * - writeSettingsValue / writeStarterSettings — `oxagen settings` writes
14
+ */
15
+ export { oxagenSettingsSchema, permissionsSchema, hooksSchema, HOOK_EVENTS, type OxagenSettings, type Permissions, type PermissionMode, type Hooks, type HookMatcher, type HookAction, type HookEvent, } from "./schema.js";
16
+ export { loadSettings, getScopePaths, clearSettingsCache, type ResolvedSettings, type ResolvedScopeFile, type ResolveSettingsOptions, type SettingsScope, } from "./resolve.js";
17
+ export { applySettingsToEnv, type AppliedSettingsEnv } from "./runtime.js";
18
+ export { wrapToolsWithGate, type ToolGateContext } from "./gate.js";
19
+ export { runHooks, type HookPayload, type HookRunOutcome } from "./hooks.js";
20
+ export { evaluateLocalPermission, parseRule, type LocalPermissionResult, } from "./permissions-gate.js";
21
+ export { writeSettingsValue, writeStarterSettings, readScopeDoc, writeScopeDoc, SETTABLE_KEYS, type WriteValueOptions, type InitOptions, } from "./write.js";
22
+ export { writeMcpServer, removeMcpServer, setMcpServerDisabled, findServerScope, type WriteMcpServerOptions, type McpMutateOptions, } from "./mcp-write.js";
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/settings/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,KAAK,EACV,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,GACf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7E,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @module settings — The unified `settings.json` driver for the Oxagen CLI.
3
+ *
4
+ * One hierarchical settings file (user → project → local) configures the CLI's
5
+ * model, environment, platform URL, tool permissions, and lifecycle hooks —
6
+ * the spine the MCP-client, agents, commands, and skills subsystems plug into.
7
+ *
8
+ * Public surface:
9
+ * - loadSettings / getScopePaths / clearSettingsCache — resolution
10
+ * - applySettingsToEnv — startup projection
11
+ * - wrapToolsWithGate — permission + hook gate
12
+ * - runHooks — lifecycle hook engine
13
+ * - writeSettingsValue / writeStarterSettings — `oxagen settings` writes
14
+ */
15
+ export { oxagenSettingsSchema, permissionsSchema, hooksSchema, HOOK_EVENTS, } from "./schema.js";
16
+ export { loadSettings, getScopePaths, clearSettingsCache, } from "./resolve.js";
17
+ export { applySettingsToEnv } from "./runtime.js";
18
+ export { wrapToolsWithGate } from "./gate.js";
19
+ export { runHooks } from "./hooks.js";
20
+ export { evaluateLocalPermission, parseRule, } from "./permissions-gate.js";
21
+ export { writeSettingsValue, writeStarterSettings, readScopeDoc, writeScopeDoc, SETTABLE_KEYS, } from "./write.js";
22
+ export { writeMcpServer, removeMcpServer, setMcpServerDisabled, findServerScope, } from "./mcp-write.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/settings/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,WAAW,EACX,WAAW,GAQZ,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,kBAAkB,GAKnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,kBAAkB,EAA2B,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAwB,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,QAAQ,EAAyC,MAAM,YAAY,CAAC;AAE7E,OAAO,EACL,uBAAuB,EACvB,SAAS,GAEV,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,aAAa,EACb,aAAa,GAGd,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,GAGhB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * mcp-write.ts — Writers for `oxagen mcp add/remove/enable/disable`.
3
+ *
4
+ * MCP servers live in the `mcpServers` map of a single scope file. `add` writes
5
+ * to a chosen scope (default project); `remove`/`enable`/`disable` auto-detect
6
+ * which scope defines the server (highest precedence first) unless a scope is
7
+ * given. Every write validates the whole document against the schema first.
8
+ */
9
+ import type { McpServerConfig } from "@oxagen/mcp-config/schema";
10
+ import { type SettingsScope, type ResolveSettingsOptions } from "./resolve.js";
11
+ type Ctx = Pick<ResolveSettingsOptions, "cwd" | "userSettingsPath" | "projectDirName">;
12
+ /** Find the highest-precedence scope that defines a server, if any. */
13
+ export declare function findServerScope(name: string, ctx?: Ctx): {
14
+ scope: SettingsScope;
15
+ path: string;
16
+ } | null;
17
+ export interface WriteMcpServerOptions extends Ctx {
18
+ scope?: SettingsScope;
19
+ name: string;
20
+ config: McpServerConfig;
21
+ }
22
+ /** Add or replace an MCP server in a scope file (default: project). */
23
+ export declare function writeMcpServer(opts: WriteMcpServerOptions): string;
24
+ export interface McpMutateOptions extends Ctx {
25
+ scope?: SettingsScope;
26
+ name: string;
27
+ }
28
+ /** Remove a server. Returns the path touched and whether it existed. */
29
+ export declare function removeMcpServer(opts: McpMutateOptions): {
30
+ path: string;
31
+ found: boolean;
32
+ };
33
+ /** Toggle a server's `disabled` flag. Returns the path and whether it existed. */
34
+ export declare function setMcpServerDisabled(opts: McpMutateOptions & {
35
+ disabled: boolean;
36
+ }): {
37
+ path: string;
38
+ found: boolean;
39
+ };
40
+ export {};
41
+ //# sourceMappingURL=mcp-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-write.d.ts","sourceRoot":"","sources":["../../src/settings/mcp-write.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAI9F,KAAK,GAAG,GAAG,IAAI,CAAC,sBAAsB,EAAE,KAAK,GAAG,kBAAkB,GAAG,gBAAgB,CAAC,CAAC;AAQvF,uEAAuE;AACvE,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,GAAQ,GACZ;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO/C;AAED,MAAM,WAAW,qBAAsB,SAAQ,GAAG;IAChD,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CASlE;AAED,MAAM,WAAW,gBAAiB,SAAQ,GAAG;IAC3C,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAYxF;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAalC"}