@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
@@ -12,25 +12,41 @@
12
12
  * context engine. Model calls go through the Vercel AI Gateway.
13
13
  */
14
14
  import { runTurn, MissingGatewayKeyError } from "../agent/pipeline.js";
15
+ import { runAgent } from "../agent/loop.js";
15
16
  import { loadProjectContext } from "../agent/project-context.js";
16
17
  import { openSessionMemory } from "../agent/memory.js";
17
18
  import { openFleetMemory } from "../agent/fleet/memory.js";
18
19
  import { openTraceStore } from "../agent/trace-store.js";
20
+ import { getAgent } from "../agents/loader.js";
21
+ import { appendVerboseLog } from "../agent/verbose-log.js";
22
+ import { formatVerboseSection } from "../agent/trace-format.js";
23
+ import { readConfig } from "../lib/config.js";
24
+ import { PermissionBroker } from "../agent/permissions.js";
19
25
  export async function runOneShot(prompt, options = {}) {
20
26
  const cwd = process.cwd();
21
27
  const projectContext = loadProjectContext(cwd);
22
28
  const memory = await openSessionMemory(cwd, `one-shot-${Date.now()}`);
23
29
  const fleetMemory = openFleetMemory(cwd);
24
30
  const traceStore = openTraceStore(cwd);
31
+ const verbose = options.verbose ?? readConfig().verbose ?? false;
32
+ // Non-interactive: build a broker only when a mode is requested. With no
33
+ // approver, `ask` denies mutations (fail closed); acceptEdits/bypass enable
34
+ // scripted edits. The model-router never under-spends on safety regardless.
35
+ const broker = options.mode && options.mode !== "readonly"
36
+ ? new PermissionBroker({ mode: options.mode, cwd })
37
+ : undefined;
38
+ const readOnly = options.readOnly || options.mode === "readonly";
25
39
  try {
26
40
  let streamed = false;
27
41
  const result = await runTurn({
28
42
  prompt,
29
43
  cwd,
30
44
  projectContext,
31
- readOnly: options.readOnly,
45
+ readOnly,
46
+ broker,
32
47
  model: options.model,
33
48
  bare: options.bare,
49
+ verbose,
34
50
  memory,
35
51
  fleetMemory,
36
52
  // Pipeline stage progress goes to stderr so stdout stays the clean answer.
@@ -53,6 +69,12 @@ export async function runOneShot(prompt, options = {}) {
53
69
  traceStore.record(result.trace);
54
70
  if (streamed)
55
71
  process.stdout.write("\n");
72
+ // Verbose: append the structured record to the JSONL stream and print the
73
+ // per-phase / per-model / cost breakdown to stderr (stdout stays the answer).
74
+ if (verbose) {
75
+ appendVerboseLog(cwd, result.trace);
76
+ process.stderr.write(formatVerboseSection(result.trace).join("\n") + "\n");
77
+ }
56
78
  }
57
79
  catch (err) {
58
80
  if (err instanceof MissingGatewayKeyError) {
@@ -67,6 +89,54 @@ export async function runOneShot(prompt, options = {}) {
67
89
  await memory?.close();
68
90
  }
69
91
  }
92
+ /**
93
+ * Run a single prompt as a named agent (its system prompt, tool allowlist, and
94
+ * model). Bypasses the eval/enhance/judge pipeline — the agent's own prompt is
95
+ * authoritative. Streams the answer to stdout; tool activity to stderr.
96
+ */
97
+ export async function runAgentOneShot(prompt, agentName, options = {}) {
98
+ const cwd = process.cwd();
99
+ const agent = getAgent(agentName, { cwd });
100
+ if (!agent) {
101
+ process.stderr.write(`Error: unknown agent "${agentName}". Run \`oxagen agent list\`.\n`);
102
+ process.exitCode = 1;
103
+ return;
104
+ }
105
+ const projectContext = loadProjectContext(cwd);
106
+ const memory = await openSessionMemory(cwd, `agent-${agentName}-${Date.now()}`);
107
+ try {
108
+ let streamed = false;
109
+ await runAgent({
110
+ prompt,
111
+ cwd,
112
+ agent,
113
+ readOnly: options.readOnly,
114
+ model: options.model,
115
+ projectContext,
116
+ memory,
117
+ onText: (delta) => {
118
+ streamed = true;
119
+ process.stdout.write(delta);
120
+ },
121
+ onToolCall: (name, input) => {
122
+ const summary = typeof input === "object" && input !== null
123
+ ? JSON.stringify(input).slice(0, 120)
124
+ : String(input);
125
+ process.stderr.write(` · ${name} ${summary}\n`);
126
+ },
127
+ onToolBlocked: (name, reason) => process.stderr.write(` ⛔ ${name}: ${reason}\n`),
128
+ });
129
+ if (streamed)
130
+ process.stdout.write("\n");
131
+ }
132
+ catch (err) {
133
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
134
+ process.exitCode = 1;
135
+ }
136
+ finally {
137
+ await memory?.close();
138
+ }
139
+ }
70
140
  export async function runFromStdin(options = {}) {
71
141
  const chunks = [];
72
142
  for await (const chunk of process.stdin) {
@@ -1 +1 @@
1
- {"version":3,"file":"one-shot.js","sourceRoot":"","sources":["../../src/repl/one-shot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"one-shot.js","sourceRoot":"","sources":["../../src/repl/one-shot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=enforce.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce.test.d.ts","sourceRoot":"","sources":["../../../src/rules/__tests__/enforce.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { renderRulesSection, guardDenyEntry, guardsToDeny } from "../enforce.js";
3
+ import { evaluateLocalPermission } from "../../settings/permissions-gate.js";
4
+ const rule = (over) => ({
5
+ description: "",
6
+ text: "do the thing",
7
+ source: "test",
8
+ ...over,
9
+ });
10
+ describe("renderRulesSection", () => {
11
+ it("returns empty string with no rules", () => {
12
+ expect(renderRulesSection([])).toBe("");
13
+ });
14
+ it("lists rules and marks enforced ones", () => {
15
+ const out = renderRulesSection([
16
+ rule({ id: "a", text: "Always read before editing." }),
17
+ rule({ id: "b", text: "Never edit applied migrations.", guard: { tool: "Edit", denyPathGlob: "m/**" } }),
18
+ ]);
19
+ expect(out).toContain("Workspace rules");
20
+ expect(out).toContain("Always read before editing.");
21
+ expect(out).toContain("Never edit applied migrations. [enforced]");
22
+ });
23
+ });
24
+ describe("guardDenyEntry", () => {
25
+ it("builds Tool(glob) for a path guard", () => {
26
+ expect(guardDenyEntry(rule({ id: "x", guard: { tool: "Edit", denyPathGlob: "m/**" } }))).toBe("Edit(m/**)");
27
+ });
28
+ it("builds Tool(glob) for a command guard", () => {
29
+ expect(guardDenyEntry(rule({ id: "x", guard: { tool: "Bash", denyCommandGlob: "rm -rf*" } }))).toBe("Bash(rm -rf*)");
30
+ });
31
+ it("builds a bare tool when no pattern", () => {
32
+ expect(guardDenyEntry(rule({ id: "x", guard: { tool: "Bash" } }))).toBe("Bash");
33
+ });
34
+ it("returns null without a guard", () => {
35
+ expect(guardDenyEntry(rule({ id: "x" }))).toBeNull();
36
+ });
37
+ });
38
+ describe("guardsToDeny", () => {
39
+ it("produces deny entries + a reason map keyed by entry", () => {
40
+ const { deny, reasons } = guardsToDeny([
41
+ rule({ id: "no-mig", text: "Add a forward migration.", guard: { tool: "Edit", denyPathGlob: "mig/*-applied/**" } }),
42
+ rule({ id: "style", text: "prompt only" }), // no guard → no deny
43
+ ]);
44
+ expect(deny).toEqual(["Edit(mig/*-applied/**)"]);
45
+ expect(reasons["Edit(mig/*-applied/**)"]).toContain('rule "no-mig"');
46
+ expect(reasons["Edit(mig/*-applied/**)"]).toContain("Add a forward migration.");
47
+ });
48
+ it("the produced deny entries actually block via the permission gate", () => {
49
+ const { deny } = guardsToDeny([
50
+ rule({ id: "no-mig", guard: { tool: "Edit", denyPathGlob: "packages/database/migrations/*-applied/**" } }),
51
+ ]);
52
+ const blocked = evaluateLocalPermission("edit_file", { path: "packages/database/migrations/0001-applied/up.sql" }, { deny });
53
+ expect(blocked.decision).toBe("deny");
54
+ const allowed = evaluateLocalPermission("edit_file", { path: "src/app.ts" }, { deny });
55
+ expect(allowed.decision).toBe("allow");
56
+ });
57
+ });
58
+ //# sourceMappingURL=enforce.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce.test.js","sourceRoot":"","sources":["../../../src/rules/__tests__/enforce.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.d.ts","sourceRoot":"","sources":["../../../src/rules/__tests__/loader.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,54 @@
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 { loadRules } from "../loader.js";
6
+ let dir;
7
+ let userDir;
8
+ function writeRule(rel, body) {
9
+ const path = join(dir, rel);
10
+ mkdirSync(join(path, ".."), { recursive: true });
11
+ writeFileSync(path, body, "utf8");
12
+ }
13
+ const load = () => loadRules({ cwd: dir, userRulesDir: userDir });
14
+ beforeEach(() => {
15
+ dir = mkdtempSync(join(tmpdir(), "oxagen-rules-"));
16
+ userDir = join(dir, "user-rules");
17
+ });
18
+ afterEach(() => rmSync(dir, { recursive: true, force: true }));
19
+ describe("loadRules", () => {
20
+ it("loads a rule with description, guard, and body text", () => {
21
+ writeRule(".oxagen/rules/no-applied-migration.md", "---\ndescription: Never edit an applied migration\nguard-tool: Edit\nguard-deny-path: packages/database/migrations/*-applied/**\n---\nAdd a new forward migration instead of editing an applied one.");
22
+ const rules = load();
23
+ expect(rules).toHaveLength(1);
24
+ const r = rules[0];
25
+ expect(r.id).toBe("no-applied-migration");
26
+ expect(r.description).toBe("Never edit an applied migration");
27
+ expect(r.guard).toEqual({ tool: "Edit", denyPathGlob: "packages/database/migrations/*-applied/**" });
28
+ expect(r.text).toContain("Add a new forward migration");
29
+ });
30
+ it("parses a bash command guard", () => {
31
+ writeRule(".oxagen/rules/no-force-push.md", "---\nguard-tool: Bash\nguard-deny-command: git push --force*\n---\nNever force-push.");
32
+ expect(load()[0]?.guard).toEqual({ tool: "Bash", denyCommandGlob: "git push --force*" });
33
+ });
34
+ it("treats a rule with no guard frontmatter as prompt-only", () => {
35
+ writeRule(".oxagen/rules/style.md", "---\ndescription: d\n---\nMatch the surrounding code style.");
36
+ expect(load()[0]?.guard).toBeUndefined();
37
+ });
38
+ it(".oxagen overrides .claude overrides user by name", () => {
39
+ mkdirSync(userDir, { recursive: true });
40
+ writeFileSync(join(userDir, "r.md"), "user text");
41
+ writeRule(".claude/rules/r.md", "claude text");
42
+ expect(load().find((x) => x.id === "r")?.text).toBe("claude text");
43
+ writeRule(".oxagen/rules/r.md", "oxagen text");
44
+ expect(load().find((x) => x.id === "r")?.text).toBe("oxagen text");
45
+ });
46
+ it("ignores files with an empty body", () => {
47
+ writeRule(".oxagen/rules/empty.md", "---\ndescription: d\n---\n");
48
+ expect(load()).toHaveLength(0);
49
+ });
50
+ it("returns an empty list when no rules exist", () => {
51
+ expect(load()).toEqual([]);
52
+ });
53
+ });
54
+ //# sourceMappingURL=loader.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.test.js","sourceRoot":"","sources":["../../../src/rules/__tests__/loader.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * enforce.ts — Turn rules into the two enforcement signals the agent loop needs.
3
+ *
4
+ * - renderRulesSection(): Tier 1 — the system-prompt block listing every rule.
5
+ * - guardsToDeny(): Tier 2 — guarded rules become permission deny entries
6
+ * (reusing the settings permission gate) plus a map from
7
+ * the deny entry back to the rule text, so a blocked tool
8
+ * call returns the human rule to the model.
9
+ */
10
+ import type { Rule } from "./types.js";
11
+ /** The system-prompt section listing active rules (Tier 1: soft adherence). */
12
+ export declare function renderRulesSection(rules: Rule[]): string;
13
+ /** The permission-gate deny entry a guard produces (e.g. `Edit(migrations/**)`). */
14
+ export declare function guardDenyEntry(rule: Rule): string | null;
15
+ export interface RuleDenies {
16
+ /** Deny entries to merge into `permissions.deny` for the gate. */
17
+ deny: string[];
18
+ /** Deny entry → human block message (rule name + text) for the gate. */
19
+ reasons: Record<string, string>;
20
+ }
21
+ /** Convert guarded rules into gate deny entries + their human reasons (Tier 2). */
22
+ export declare function guardsToDeny(rules: Rule[]): RuleDenies;
23
+ //# sourceMappingURL=enforce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce.d.ts","sourceRoot":"","sources":["../../src/rules/enforce.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAUxD;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAMxD;AAED,MAAM,WAAW,UAAU;IACzB,kEAAkE;IAClE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,UAAU,CAUtD"}
@@ -0,0 +1,36 @@
1
+ /** The system-prompt section listing active rules (Tier 1: soft adherence). */
2
+ export function renderRulesSection(rules) {
3
+ if (rules.length === 0)
4
+ return "";
5
+ const lines = [
6
+ "",
7
+ "## Workspace rules (binding — follow exactly; guarded rules are hard-blocked)",
8
+ ];
9
+ for (const r of rules) {
10
+ lines.push(`- ${r.text}${r.guard ? " [enforced]" : ""}`);
11
+ }
12
+ return lines.join("\n");
13
+ }
14
+ /** The permission-gate deny entry a guard produces (e.g. `Edit(migrations/**)`). */
15
+ export function guardDenyEntry(rule) {
16
+ const guard = rule.guard;
17
+ if (!guard)
18
+ return null;
19
+ const tool = guard.tool ?? "*";
20
+ const pattern = guard.denyPathGlob ?? guard.denyCommandGlob;
21
+ return pattern ? `${tool}(${pattern})` : tool;
22
+ }
23
+ /** Convert guarded rules into gate deny entries + their human reasons (Tier 2). */
24
+ export function guardsToDeny(rules) {
25
+ const deny = [];
26
+ const reasons = {};
27
+ for (const rule of rules) {
28
+ const entry = guardDenyEntry(rule);
29
+ if (!entry)
30
+ continue;
31
+ deny.push(entry);
32
+ reasons[entry] = `rule "${rule.id}" — ${rule.text}`;
33
+ }
34
+ return { deny, reasons };
35
+ }
36
+ //# sourceMappingURL=enforce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce.js","sourceRoot":"","sources":["../../src/rules/enforce.ts"],"names":[],"mappings":"AAWA,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG;QACZ,EAAE;QACF,+EAA+E;KAChF,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,IAAU;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,eAAe,CAAC;IAC5D,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAChD,CAAC;AASD,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,CAAC,KAAK,CAAC,GAAG,SAAS,IAAI,CAAC,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @module rules — Workspace rules the agent retrieves, is told about, and is
3
+ * hard-blocked from violating. The CLI-local foundation of the adherence loop
4
+ * in docs/specs/stella-graph-memory-sync/spec.md (§4).
5
+ */
6
+ export type { Rule, RuleGuard } from "./types.js";
7
+ export { loadRules, type LoadRulesOptions } from "./loader.js";
8
+ export { renderRulesSection, guardsToDeny, guardDenyEntry, type RuleDenies, } from "./enforce.js";
9
+ export { scaffoldRule } from "./write.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { loadRules } from "./loader.js";
2
+ export { renderRulesSection, guardsToDeny, guardDenyEntry, } from "./enforce.js";
3
+ export { scaffoldRule } from "./write.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAyB,MAAM,aAAa,CAAC;AAC/D,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,cAAc,GAEf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Rule } from "./types.js";
2
+ export interface LoadRulesOptions {
3
+ /** Project root. Defaults to process.cwd(). */
4
+ cwd?: string;
5
+ /** Override the user rules dir (testing). */
6
+ userRulesDir?: string;
7
+ }
8
+ /** Load every workspace rule visible from `cwd`, merged by name across sources. */
9
+ export declare function loadRules(opts?: LoadRulesOptions): Rule[];
10
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/rules/loader.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,IAAI,EAAa,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA+CD,mFAAmF;AACnF,wBAAgB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,IAAI,EAAE,CAQ7D"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * loader.ts — Discover and parse workspace rules.
3
+ *
4
+ * Sources, lowest → highest precedence (later overrides by name):
5
+ * 1. ~/.config/oxagen/rules/*.md (user)
6
+ * 2. <project>/.claude/rules/*.md (Claude Code interop)
7
+ * 3. <project>/.oxagen/rules/*.md (oxagen project rules)
8
+ *
9
+ * Frontmatter keys: description, guard-tool, guard-deny-path, guard-deny-command.
10
+ * The body is the rule text. Reuses the agent loader's frontmatter parser.
11
+ */
12
+ import { readdirSync, existsSync, readFileSync } from "node:fs";
13
+ import { join, basename } from "node:path";
14
+ import { homedir } from "node:os";
15
+ import { parseFrontmatter } from "../agents/loader.js";
16
+ function guardFrom(data) {
17
+ const tool = data["guard-tool"];
18
+ const denyPathGlob = data["guard-deny-path"];
19
+ const denyCommandGlob = data["guard-deny-command"];
20
+ if (!tool && !denyPathGlob && !denyCommandGlob)
21
+ return undefined;
22
+ const guard = {};
23
+ if (tool)
24
+ guard.tool = tool;
25
+ if (denyPathGlob)
26
+ guard.denyPathGlob = denyPathGlob;
27
+ if (denyCommandGlob)
28
+ guard.denyCommandGlob = denyCommandGlob;
29
+ return guard;
30
+ }
31
+ function ruleFromFile(path) {
32
+ let raw;
33
+ try {
34
+ raw = readFileSync(path, "utf8");
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ const { data, body } = parseFrontmatter(raw);
40
+ const id = data["name"] || basename(path).replace(/\.md$/, "");
41
+ if (!id || !body.trim())
42
+ return null; // a rule needs a name and a statement
43
+ return {
44
+ id,
45
+ description: data["description"] ?? "",
46
+ text: body.trim(),
47
+ guard: guardFrom(data),
48
+ source: path,
49
+ };
50
+ }
51
+ function loadDir(dir, into) {
52
+ if (!existsSync(dir))
53
+ return;
54
+ let files;
55
+ try {
56
+ files = readdirSync(dir).filter((f) => f.endsWith(".md"));
57
+ }
58
+ catch {
59
+ return;
60
+ }
61
+ for (const file of files.sort()) {
62
+ const rule = ruleFromFile(join(dir, file));
63
+ if (rule)
64
+ into.set(rule.id, rule);
65
+ }
66
+ }
67
+ /** Load every workspace rule visible from `cwd`, merged by name across sources. */
68
+ export function loadRules(opts = {}) {
69
+ const cwd = opts.cwd ?? process.cwd();
70
+ const userDir = opts.userRulesDir ?? join(homedir(), ".config", "oxagen", "rules");
71
+ const registry = new Map();
72
+ loadDir(userDir, registry);
73
+ loadDir(join(cwd, ".claude", "rules"), registry);
74
+ loadDir(join(cwd, ".oxagen", "rules"), registry);
75
+ return [...registry.values()];
76
+ }
77
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/rules/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * types.ts — Workspace rules Stella retrieves, injects, and enforces.
3
+ *
4
+ * A rule is a binding instruction for the coding agent. It is always injected
5
+ * into the system prompt (Tier 1). When it carries a `guard`, it is also
6
+ * hard-enforced at the tool boundary (Tier 2) by the same permission gate used
7
+ * for settings permissions and MCP tools — a violating tool call is blocked and
8
+ * the rule text is returned to the model so it self-corrects.
9
+ *
10
+ * Rules are authored as markdown under `.oxagen/rules/*.md` today; learned
11
+ * procedural rules (engram promotion) will flow through the same retrieval +
12
+ * enforcement path once memory sync lands (see
13
+ * docs/specs/stella-graph-memory-sync/spec.md, Phase 4).
14
+ */
15
+ /** A machine-enforceable guard that blocks a tool call that would violate the rule. */
16
+ export interface RuleGuard {
17
+ /**
18
+ * Canonical tool the guard applies to: `Bash`, `Write`, `Edit`, `Read`, or
19
+ * `*` for any. Matched against the tool's canonical permission name.
20
+ */
21
+ tool?: string;
22
+ /** Block a file tool (Write/Edit/Read) whose path matches this glob. */
23
+ denyPathGlob?: string;
24
+ /** Block a Bash command matching this glob. */
25
+ denyCommandGlob?: string;
26
+ }
27
+ export interface Rule {
28
+ /** Rule name (the filename or frontmatter `name`). */
29
+ id: string;
30
+ /** Short description shown in `rules list`. */
31
+ description: string;
32
+ /** The rule statement injected into the system prompt. */
33
+ text: string;
34
+ /** Optional hard guard (Tier 2). Absent ⇒ prompt-only (Tier 1). */
35
+ guard?: RuleGuard;
36
+ /** Where the rule came from (file path). */
37
+ source: string;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uFAAuF;AACvF,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,IAAI;IACnB,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * types.ts — Workspace rules Stella retrieves, injects, and enforces.
3
+ *
4
+ * A rule is a binding instruction for the coding agent. It is always injected
5
+ * into the system prompt (Tier 1). When it carries a `guard`, it is also
6
+ * hard-enforced at the tool boundary (Tier 2) by the same permission gate used
7
+ * for settings permissions and MCP tools — a violating tool call is blocked and
8
+ * the rule text is returned to the model so it self-corrects.
9
+ *
10
+ * Rules are authored as markdown under `.oxagen/rules/*.md` today; learned
11
+ * procedural rules (engram promotion) will flow through the same retrieval +
12
+ * enforcement path once memory sync lands (see
13
+ * docs/specs/stella-graph-memory-sync/spec.md, Phase 4).
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,10 @@
1
+ /** Write a starter rule to `.oxagen/rules/<name>.md`. */
2
+ export declare function scaffoldRule(opts: {
3
+ name: string;
4
+ cwd?: string;
5
+ dir?: string;
6
+ }): {
7
+ path: string;
8
+ created: boolean;
9
+ };
10
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/rules/write.ts"],"names":[],"mappings":"AAmBA,yDAAyD;AACzD,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB,CAOA"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * write.ts — Scaffold a new rule for `oxagen rules new`.
3
+ */
4
+ import { writeFileSync, existsSync, mkdirSync } from "node:fs";
5
+ import { join, dirname } from "node:path";
6
+ const TEMPLATE = (name) => `---
7
+ description: One-line description of what ${name} enforces.
8
+ # Optional hard guard — a violating tool call is blocked before it runs.
9
+ # guard-tool: Edit # Bash | Write | Edit | Read | *
10
+ # guard-deny-path: packages/database/migrations/*-applied/**
11
+ # guard-deny-command: rm -rf*
12
+ ---
13
+
14
+ State the rule as one clear, imperative instruction. This text is injected into
15
+ the agent's system prompt and returned to the model when a guarded violation is
16
+ blocked, so write it so the agent knows what to do instead.
17
+ `;
18
+ /** Write a starter rule to `.oxagen/rules/<name>.md`. */
19
+ export function scaffoldRule(opts) {
20
+ const dir = opts.dir ?? join(opts.cwd ?? process.cwd(), ".oxagen", "rules");
21
+ const path = join(dir, `${opts.name}.md`);
22
+ if (existsSync(path))
23
+ return { path, created: false };
24
+ mkdirSync(dirname(path), { recursive: true });
25
+ writeFileSync(path, TEMPLATE(opts.name), "utf8");
26
+ return { path, created: true };
27
+ }
28
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/rules/write.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC;4CACS,IAAI;;;;;;;;;;CAU/C,CAAC;AAEF,yDAAyD;AACzD,MAAM,UAAU,YAAY,CAAC,IAAkD;IAI7E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;IAC1C,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACtD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.test.d.ts","sourceRoot":"","sources":["../../../src/settings/__tests__/gate.test.ts"],"names":[],"mappings":""}