@namzu/cli 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/dist/__tests__/permission-rules-reach-the-turn.test.d.ts +31 -0
  2. package/dist/__tests__/permission-rules-reach-the-turn.test.d.ts.map +1 -0
  3. package/dist/__tests__/permission-rules-reach-the-turn.test.js +132 -0
  4. package/dist/__tests__/permission-rules-reach-the-turn.test.js.map +1 -0
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +16 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/cli.test.js +7 -1
  9. package/dist/cli.test.js.map +1 -1
  10. package/dist/commands/__tests__/resume.test.d.ts +15 -0
  11. package/dist/commands/__tests__/resume.test.d.ts.map +1 -0
  12. package/dist/commands/__tests__/resume.test.js +116 -0
  13. package/dist/commands/__tests__/resume.test.js.map +1 -0
  14. package/dist/commands/__tests__/run-flags.test.d.ts +11 -0
  15. package/dist/commands/__tests__/run-flags.test.d.ts.map +1 -0
  16. package/dist/commands/__tests__/run-flags.test.js +189 -0
  17. package/dist/commands/__tests__/run-flags.test.js.map +1 -0
  18. package/dist/commands/__tests__/run-stream-flags.test.js +160 -11
  19. package/dist/commands/__tests__/run-stream-flags.test.js.map +1 -1
  20. package/dist/commands/doctor.js.map +1 -1
  21. package/dist/commands/resume.d.ts +60 -0
  22. package/dist/commands/resume.d.ts.map +1 -0
  23. package/dist/commands/resume.js +107 -0
  24. package/dist/commands/resume.js.map +1 -0
  25. package/dist/commands/run-flags.d.ts +76 -0
  26. package/dist/commands/run-flags.d.ts.map +1 -0
  27. package/dist/commands/run-flags.js +163 -0
  28. package/dist/commands/run-flags.js.map +1 -0
  29. package/dist/commands/run-stream.d.ts +3 -39
  30. package/dist/commands/run-stream.d.ts.map +1 -1
  31. package/dist/commands/run-stream.js +74 -101
  32. package/dist/commands/run-stream.js.map +1 -1
  33. package/dist/commands/run.d.ts +25 -0
  34. package/dist/commands/run.d.ts.map +1 -1
  35. package/dist/commands/run.js +189 -16
  36. package/dist/commands/run.js.map +1 -1
  37. package/dist/commands/stubs.d.ts +10 -0
  38. package/dist/commands/stubs.d.ts.map +1 -1
  39. package/dist/commands/stubs.js +12 -2
  40. package/dist/commands/stubs.js.map +1 -1
  41. package/dist/config/load.d.ts.map +1 -1
  42. package/dist/config/load.js +20 -4
  43. package/dist/config/load.js.map +1 -1
  44. package/dist/config/schema.d.ts +9 -12
  45. package/dist/config/schema.d.ts.map +1 -1
  46. package/dist/config/schema.js.map +1 -1
  47. package/dist/doctor/checks/credentials.d.ts +20 -0
  48. package/dist/doctor/checks/credentials.d.ts.map +1 -0
  49. package/dist/doctor/checks/credentials.js +58 -0
  50. package/dist/doctor/checks/credentials.js.map +1 -0
  51. package/dist/doctor/checks/credentials.test.d.ts +10 -0
  52. package/dist/doctor/checks/credentials.test.d.ts.map +1 -0
  53. package/dist/doctor/checks/credentials.test.js +47 -0
  54. package/dist/doctor/checks/credentials.test.js.map +1 -0
  55. package/dist/doctor/checks/index.d.ts +2 -1
  56. package/dist/doctor/checks/index.d.ts.map +1 -1
  57. package/dist/doctor/checks/index.js +3 -1
  58. package/dist/doctor/checks/index.js.map +1 -1
  59. package/dist/index.d.ts +1 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/integrations/providers/discover.d.ts +3 -8
  64. package/dist/integrations/providers/discover.d.ts.map +1 -1
  65. package/dist/integrations/providers/discover.js +2 -12
  66. package/dist/integrations/providers/discover.js.map +1 -1
  67. package/dist/integrations/providers/discover.test.js +1 -28
  68. package/dist/integrations/providers/discover.test.js.map +1 -1
  69. package/dist/integrations/providers/index.d.ts +0 -1
  70. package/dist/integrations/providers/index.d.ts.map +1 -1
  71. package/dist/integrations/providers/index.js +0 -1
  72. package/dist/integrations/providers/index.js.map +1 -1
  73. package/dist/integrations/providers/oauth.d.ts +1 -1
  74. package/dist/integrations/providers/oauth.js +1 -1
  75. package/dist/integrations/providers/preferences.d.ts +2 -2
  76. package/dist/integrations/providers/preferences.js +2 -2
  77. package/dist/integrations/sessions/store.js +1 -1
  78. package/dist/integrations/sessions/store.js.map +1 -1
  79. package/dist/integrations/updates.d.ts +4 -12
  80. package/dist/integrations/updates.d.ts.map +1 -1
  81. package/dist/integrations/updates.js +6 -53
  82. package/dist/integrations/updates.js.map +1 -1
  83. package/dist/permissions/__tests__/mode.test.d.ts +10 -0
  84. package/dist/permissions/__tests__/mode.test.d.ts.map +1 -0
  85. package/dist/permissions/__tests__/mode.test.js +115 -0
  86. package/dist/permissions/__tests__/mode.test.js.map +1 -0
  87. package/dist/permissions/__tests__/rules.test.d.ts +10 -0
  88. package/dist/permissions/__tests__/rules.test.d.ts.map +1 -0
  89. package/dist/permissions/__tests__/rules.test.js +196 -0
  90. package/dist/permissions/__tests__/rules.test.js.map +1 -0
  91. package/dist/permissions/mode.d.ts +64 -0
  92. package/dist/permissions/mode.d.ts.map +1 -0
  93. package/dist/permissions/mode.js +32 -0
  94. package/dist/permissions/mode.js.map +1 -0
  95. package/dist/permissions/rules.d.ts +105 -0
  96. package/dist/permissions/rules.d.ts.map +1 -0
  97. package/dist/permissions/rules.js +163 -0
  98. package/dist/permissions/rules.js.map +1 -0
  99. package/dist/tui/App.d.ts.map +1 -1
  100. package/dist/tui/App.js +29 -133
  101. package/dist/tui/App.js.map +1 -1
  102. package/dist/tui/Picker.d.ts +4 -4
  103. package/dist/tui/Picker.d.ts.map +1 -1
  104. package/dist/tui/Picker.js +6 -8
  105. package/dist/tui/Picker.js.map +1 -1
  106. package/dist/tui/__tests__/deferred-tool-discovery.test.d.ts +24 -0
  107. package/dist/tui/__tests__/deferred-tool-discovery.test.d.ts.map +1 -0
  108. package/dist/tui/__tests__/deferred-tool-discovery.test.js +111 -0
  109. package/dist/tui/__tests__/deferred-tool-discovery.test.js.map +1 -0
  110. package/dist/tui/__tests__/to-agent-event.test.js +88 -0
  111. package/dist/tui/__tests__/to-agent-event.test.js.map +1 -1
  112. package/dist/tui/__tests__/working-directory.test.d.ts +13 -0
  113. package/dist/tui/__tests__/working-directory.test.d.ts.map +1 -0
  114. package/dist/tui/__tests__/working-directory.test.js +109 -0
  115. package/dist/tui/__tests__/working-directory.test.js.map +1 -0
  116. package/dist/tui/agent.d.ts +46 -5
  117. package/dist/tui/agent.d.ts.map +1 -1
  118. package/dist/tui/agent.js +140 -43
  119. package/dist/tui/agent.js.map +1 -1
  120. package/dist/tui/slashCommands.d.ts +0 -6
  121. package/dist/tui/slashCommands.d.ts.map +1 -1
  122. package/dist/tui/slashCommands.js +1 -21
  123. package/dist/tui/slashCommands.js.map +1 -1
  124. package/dist/tui/types.d.ts +12 -0
  125. package/dist/tui/types.d.ts.map +1 -1
  126. package/package.json +4 -5
  127. package/dist/commands/tools.d.ts +0 -16
  128. package/dist/commands/tools.d.ts.map +0 -1
  129. package/dist/commands/tools.js +0 -161
  130. package/dist/commands/tools.js.map +0 -1
  131. package/dist/integrations/clawtool/agents.d.ts +0 -31
  132. package/dist/integrations/clawtool/agents.d.ts.map +0 -1
  133. package/dist/integrations/clawtool/agents.js +0 -30
  134. package/dist/integrations/clawtool/agents.js.map +0 -1
  135. package/dist/integrations/clawtool/agents.test.d.ts +0 -2
  136. package/dist/integrations/clawtool/agents.test.d.ts.map +0 -1
  137. package/dist/integrations/clawtool/agents.test.js +0 -79
  138. package/dist/integrations/clawtool/agents.test.js.map +0 -1
  139. package/dist/integrations/clawtool/auth.d.ts +0 -26
  140. package/dist/integrations/clawtool/auth.d.ts.map +0 -1
  141. package/dist/integrations/clawtool/auth.js +0 -56
  142. package/dist/integrations/clawtool/auth.js.map +0 -1
  143. package/dist/integrations/clawtool/auth.test.d.ts +0 -2
  144. package/dist/integrations/clawtool/auth.test.d.ts.map +0 -1
  145. package/dist/integrations/clawtool/auth.test.js +0 -40
  146. package/dist/integrations/clawtool/auth.test.js.map +0 -1
  147. package/dist/integrations/clawtool/binary.d.ts +0 -25
  148. package/dist/integrations/clawtool/binary.d.ts.map +0 -1
  149. package/dist/integrations/clawtool/binary.js +0 -50
  150. package/dist/integrations/clawtool/binary.js.map +0 -1
  151. package/dist/integrations/clawtool/binary.test.d.ts +0 -2
  152. package/dist/integrations/clawtool/binary.test.d.ts.map +0 -1
  153. package/dist/integrations/clawtool/binary.test.js +0 -50
  154. package/dist/integrations/clawtool/binary.test.js.map +0 -1
  155. package/dist/integrations/clawtool/daemon.d.ts +0 -37
  156. package/dist/integrations/clawtool/daemon.d.ts.map +0 -1
  157. package/dist/integrations/clawtool/daemon.js +0 -109
  158. package/dist/integrations/clawtool/daemon.js.map +0 -1
  159. package/dist/integrations/clawtool/daemon.test.d.ts +0 -11
  160. package/dist/integrations/clawtool/daemon.test.d.ts.map +0 -1
  161. package/dist/integrations/clawtool/daemon.test.js +0 -118
  162. package/dist/integrations/clawtool/daemon.test.js.map +0 -1
  163. package/dist/integrations/clawtool/dispatch.d.ts +0 -33
  164. package/dist/integrations/clawtool/dispatch.d.ts.map +0 -1
  165. package/dist/integrations/clawtool/dispatch.js +0 -155
  166. package/dist/integrations/clawtool/dispatch.js.map +0 -1
  167. package/dist/integrations/clawtool/dispatch.test.d.ts +0 -2
  168. package/dist/integrations/clawtool/dispatch.test.d.ts.map +0 -1
  169. package/dist/integrations/clawtool/dispatch.test.js +0 -138
  170. package/dist/integrations/clawtool/dispatch.test.js.map +0 -1
  171. package/dist/integrations/clawtool/index.d.ts +0 -11
  172. package/dist/integrations/clawtool/index.d.ts.map +0 -1
  173. package/dist/integrations/clawtool/index.js +0 -10
  174. package/dist/integrations/clawtool/index.js.map +0 -1
  175. package/dist/integrations/clawtool/mcp.d.ts +0 -51
  176. package/dist/integrations/clawtool/mcp.d.ts.map +0 -1
  177. package/dist/integrations/clawtool/mcp.js +0 -174
  178. package/dist/integrations/clawtool/mcp.js.map +0 -1
  179. package/dist/integrations/clawtool/mcp.test.d.ts +0 -2
  180. package/dist/integrations/clawtool/mcp.test.d.ts.map +0 -1
  181. package/dist/integrations/clawtool/mcp.test.js +0 -126
  182. package/dist/integrations/clawtool/mcp.test.js.map +0 -1
  183. package/dist/integrations/clawtool/paths.d.ts +0 -8
  184. package/dist/integrations/clawtool/paths.d.ts.map +0 -1
  185. package/dist/integrations/clawtool/paths.js +0 -19
  186. package/dist/integrations/clawtool/paths.js.map +0 -1
  187. package/dist/integrations/clawtool/peers.d.ts +0 -67
  188. package/dist/integrations/clawtool/peers.d.ts.map +0 -1
  189. package/dist/integrations/clawtool/peers.js +0 -150
  190. package/dist/integrations/clawtool/peers.js.map +0 -1
  191. package/dist/integrations/clawtool/plugin.d.ts +0 -42
  192. package/dist/integrations/clawtool/plugin.d.ts.map +0 -1
  193. package/dist/integrations/clawtool/plugin.js +0 -38
  194. package/dist/integrations/clawtool/plugin.js.map +0 -1
  195. package/dist/integrations/clawtool/state.d.ts +0 -11
  196. package/dist/integrations/clawtool/state.d.ts.map +0 -1
  197. package/dist/integrations/clawtool/state.js +0 -41
  198. package/dist/integrations/clawtool/state.js.map +0 -1
  199. package/dist/integrations/clawtool/state.test.d.ts +0 -2
  200. package/dist/integrations/clawtool/state.test.d.ts.map +0 -1
  201. package/dist/integrations/clawtool/state.test.js +0 -38
  202. package/dist/integrations/clawtool/state.test.js.map +0 -1
  203. package/dist/integrations/clawtool/tooling.d.ts +0 -52
  204. package/dist/integrations/clawtool/tooling.d.ts.map +0 -1
  205. package/dist/integrations/clawtool/tooling.js +0 -91
  206. package/dist/integrations/clawtool/tooling.js.map +0 -1
  207. package/dist/integrations/clawtool/tooling.test.d.ts +0 -2
  208. package/dist/integrations/clawtool/tooling.test.d.ts.map +0 -1
  209. package/dist/integrations/clawtool/tooling.test.js +0 -58
  210. package/dist/integrations/clawtool/tooling.test.js.map +0 -1
  211. package/dist/integrations/clawtool/types.d.ts +0 -41
  212. package/dist/integrations/clawtool/types.d.ts.map +0 -1
  213. package/dist/integrations/clawtool/types.js +0 -9
  214. package/dist/integrations/clawtool/types.js.map +0 -1
  215. package/dist/integrations/providers/secrets.d.ts +0 -37
  216. package/dist/integrations/providers/secrets.d.ts.map +0 -1
  217. package/dist/integrations/providers/secrets.js +0 -69
  218. package/dist/integrations/providers/secrets.js.map +0 -1
@@ -0,0 +1,64 @@
1
+ /**
2
+ * How a run resolves the calls no rule decided.
3
+ *
4
+ * The `[permissions]` table says what a tool may do. This says what happens to
5
+ * everything it did not cover. The two axes are separate on purpose: a rule is
6
+ * a durable statement an operator reviewed, and a mode is a property of ONE
7
+ * invocation — the difference between "we never force-push" and "this run is
8
+ * unattended".
9
+ *
10
+ * ## Precedence between a flag and the config file
11
+ *
12
+ * A mode only governs calls the gate routed to REVIEW. A rule that denied a
13
+ * call already stopped it, and a rule that allowed one never asked, so neither
14
+ * reaches the mode at all. **A mode can therefore never reopen what a rule
15
+ * closed, and `--permission-mode` cannot widen a `deny`.**
16
+ *
17
+ * That direction is deliberate. The config file is written once, read by
18
+ * whoever reviews the repository, and changed on purpose; a flag is typed in a
19
+ * hurry by someone who wants to get on with it. Letting the hurried thing
20
+ * overrule the considered one would make every `deny` in the file advisory, and
21
+ * a prohibition that a flag can lift is not a prohibition. The dangerous-pattern
22
+ * floor sits above both and no mode reaches it either.
23
+ */
24
+ export type PermissionMode =
25
+ /** Ask a human. The default when a terminal is attached. */
26
+ 'prompt'
27
+ /**
28
+ * Approve it. The default with no terminal, and what every headless run has
29
+ * always done — named here rather than left implicit.
30
+ */
31
+ | 'auto'
32
+ /**
33
+ * Refuse it. Nothing runs unless a rule allowed it by name or pattern.
34
+ *
35
+ * The mode that did not exist before: an unattended run could previously
36
+ * only be `auto`, so a CI job either trusted the agent with everything or
37
+ * could not use it. This makes the allowlist the whole permission surface,
38
+ * which is the only form an unattended run can actually be reasoned about.
39
+ */
40
+ | 'strict';
41
+ export declare const PERMISSION_MODES: readonly PermissionMode[];
42
+ export declare function isPermissionMode(value: unknown): value is PermissionMode;
43
+ /**
44
+ * The mode for a run, from the flag, the bypass alias, and whether anyone is
45
+ * there to answer.
46
+ *
47
+ * `--yolo` / `--dangerously-skip-permissions` map to `auto`. They were accepted
48
+ * and documented as doing nothing, which was true and unsatisfying; now they
49
+ * say the same thing the mode vocabulary says, and in the TUI they do what
50
+ * their name has always implied. Neither reaches a `deny` rule or the
51
+ * dangerous-pattern floor, so the name still overstates what it can do —
52
+ * deliberately, because it should read as more dangerous than it is rather than
53
+ * less.
54
+ */
55
+ export declare function resolvePermissionMode(opts: {
56
+ readonly flag?: string | null;
57
+ readonly skipPermissions?: boolean;
58
+ readonly interactive: boolean;
59
+ }): {
60
+ mode: PermissionMode;
61
+ } | {
62
+ error: string;
63
+ };
64
+ //# sourceMappingURL=mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.d.ts","sourceRoot":"","sources":["../../src/permissions/mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,cAAc;AACzB,4DAA4D;AAC1D,QAAQ;AACV;;;GAGG;GACD,MAAM;AACR;;;;;;;GAOG;GACD,QAAQ,CAAA;AAEX,eAAO,MAAM,gBAAgB,EAAE,SAAS,cAAc,EAAiC,CAAA;AAEvF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAExE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;CAC7B,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAa/C"}
@@ -0,0 +1,32 @@
1
+ export const PERMISSION_MODES = ['prompt', 'auto', 'strict'];
2
+ export function isPermissionMode(value) {
3
+ return typeof value === 'string' && PERMISSION_MODES.includes(value);
4
+ }
5
+ /**
6
+ * The mode for a run, from the flag, the bypass alias, and whether anyone is
7
+ * there to answer.
8
+ *
9
+ * `--yolo` / `--dangerously-skip-permissions` map to `auto`. They were accepted
10
+ * and documented as doing nothing, which was true and unsatisfying; now they
11
+ * say the same thing the mode vocabulary says, and in the TUI they do what
12
+ * their name has always implied. Neither reaches a `deny` rule or the
13
+ * dangerous-pattern floor, so the name still overstates what it can do —
14
+ * deliberately, because it should read as more dangerous than it is rather than
15
+ * less.
16
+ */
17
+ export function resolvePermissionMode(opts) {
18
+ if (opts.flag != null && opts.flag !== '') {
19
+ if (!isPermissionMode(opts.flag)) {
20
+ return {
21
+ error: `--permission-mode must be one of: ${PERMISSION_MODES.join(', ')} (got "${opts.flag}")`,
22
+ };
23
+ }
24
+ return { mode: opts.flag };
25
+ }
26
+ if (opts.skipPermissions)
27
+ return { mode: 'auto' };
28
+ // No terminal means nobody to ask, so `prompt` would silently become `auto`
29
+ // anyway. Saying so here keeps the resolved mode honest in a log.
30
+ return { mode: opts.interactive ? 'prompt' : 'auto' };
31
+ }
32
+ //# sourceMappingURL=mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mode.js","sourceRoot":"","sources":["../../src/permissions/mode.ts"],"names":[],"mappings":"AAyCA,MAAM,CAAC,MAAM,gBAAgB,GAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEvF,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC9C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,gBAAsC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC5F,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAIrC;IACA,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC;QAC3C,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,OAAO;gBACN,KAAK,EAAE,qCAAqC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,IAAI;aAC9F,CAAA;QACF,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;IAC3B,CAAC;IACD,IAAI,IAAI,CAAC,eAAe;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACjD,4EAA4E;IAC5E,kEAAkE;IAClE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;AACtD,CAAC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Operator-authored tool permissions, compiled to the kernel's rule vocabulary.
3
+ *
4
+ * The kernel already has the engine — `VerificationRule[]` with an
5
+ * allow/deny/review decision, evaluated first-match-wins. What was missing was
6
+ * any way for a person to author those rules: the CLI passed `rules: []`, so a
7
+ * gate with seven rule types ran with none of them and every decision fell back
8
+ * to "ask a human". This turns a `[permissions]` table into that array.
9
+ *
10
+ * The whole surface is pure: config in, rules out. The gate stays the only
11
+ * thing that decides anything, which is what keeps this testable without a run.
12
+ */
13
+ import type { VerificationRule } from '@namzu/sdk';
14
+ /** What the operator wants to happen when a rule matches. */
15
+ export type PermissionEffect = 'allow' | 'ask' | 'deny';
16
+ export declare function isPermissionEffect(value: unknown): value is PermissionEffect;
17
+ /**
18
+ * One tool's policy: a single effect for every call, or per-argument patterns.
19
+ *
20
+ * `bash = "ask"` and
21
+ * `bash = { "git status*" = "allow", "*" = "ask" }`
22
+ * are both meant to be readable by someone who has never seen this file before,
23
+ * which is the whole point of putting it in config rather than in code.
24
+ */
25
+ export type ToolPermission = PermissionEffect | Readonly<Record<string, PermissionEffect>>;
26
+ export type PermissionsConfig = Readonly<Record<string, ToolPermission>>;
27
+ export interface CompileDiagnostic {
28
+ readonly tool: string;
29
+ readonly pattern?: string;
30
+ readonly message: string;
31
+ }
32
+ export interface CompiledPermissions {
33
+ readonly rules: readonly VerificationRule[];
34
+ /**
35
+ * What was wrong with the config, for the caller to print.
36
+ *
37
+ * Collected rather than thrown: one unreadable line should cost that line,
38
+ * not the other nine rules an operator wrote. But it must be SAID — a
39
+ * permission someone believes is in force and which was silently dropped is
40
+ * the worst outcome available here, worse than refusing to start.
41
+ */
42
+ readonly diagnostics: readonly CompileDiagnostic[];
43
+ }
44
+ /**
45
+ * Turn a glob-ish pattern into an anchored regex source.
46
+ *
47
+ * `*` matches any run of characters, `?` exactly one; everything else is
48
+ * literal. Backslashes become forward slashes on both sides so one rule works
49
+ * on every platform — an operator writing `src/*` should not have to write it
50
+ * twice.
51
+ *
52
+ * A pattern ending in `<space>*` also matches the bare command: `git push *`
53
+ * covers `git push`. Without that, the obvious rule silently misses the exact
54
+ * invocation it was written to catch, which is the failure this whole file
55
+ * exists to stop.
56
+ */
57
+ export declare function patternToRegExpSource(pattern: string): string;
58
+ /**
59
+ * A pattern that matches this tool, and only this tool, doing this thing.
60
+ *
61
+ * The kernel's `custom_pattern` has no tool scope: `target: 'args'` tests
62
+ * `JSON.stringify(input)` for ANY tool, so a rule written under `bash` would
63
+ * also decide an `edit` call whose arguments happened to match. `target:
64
+ * 'both'` tests `` `${toolName} ${JSON.stringify(input)}` ``, so the tool name
65
+ * can be pinned by writing it into the pattern — which is what this does.
66
+ *
67
+ * The argument half is deliberately unanchored on both sides, because the
68
+ * operator writes `git push*` while the gate sees
69
+ * `bash {"command":"git push origin main"}`. An anchored argument pattern would
70
+ * match nothing at all — a rule that reads like a prohibition and silently
71
+ * permits everything, which is worse than having no rule.
72
+ *
73
+ * The looseness is real and worth naming: a pattern can match text in any
74
+ * argument field, not only the one the operator had in mind. Narrowing that
75
+ * needs a rule type that names the argument, which is the kernel's to add.
76
+ */
77
+ export declare function toolScopedPattern(tool: string, pattern: string): string;
78
+ /** Whether `input` satisfies `pattern`, with the matching above. */
79
+ export declare function matchesPattern(input: string, pattern: string): boolean;
80
+ /**
81
+ * Order patterns so the most specific is tried first.
82
+ *
83
+ * The kernel evaluates first-match-wins, so `bash = { "*" = "ask", "git push*"
84
+ * = "deny" }` would otherwise resolve to `ask` and the deny would be dead —
85
+ * config that reads like a prohibition and is not one. Sorting here means an
86
+ * operator does not have to know the evaluation order to write what they mean.
87
+ *
88
+ * "Specific" is: a bare `*` is always last, then longer literal prefixes before
89
+ * shorter ones, then alphabetical so the result is stable to read and to test.
90
+ */
91
+ export declare function bySpecificity(a: string, b: string): number;
92
+ /**
93
+ * Compile a `[permissions]` table into kernel rules.
94
+ *
95
+ * `ask` emits nothing. That is not an omission: the gate's fallback for an
96
+ * unmatched call is already `review`, so the rule that says "ask" and the
97
+ * absence of any rule have to mean the same thing or the config would be
98
+ * lying about one of them. It also keeps the emitted array small enough for a
99
+ * person to read in a log.
100
+ *
101
+ * A tool nobody wrote a rule for is therefore asked about, and there is no way
102
+ * to spell "allow everything by omission" — the only way to widen is to say so.
103
+ */
104
+ export declare function compilePermissions(config: PermissionsConfig | undefined): CompiledPermissions;
105
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/permissions/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,6DAA6D;AAC7D,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAA;AAIvD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAA;AAE1F,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;AAExE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC3C;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAClD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIvE;AAMD,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAKtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAU1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,mBAAmB,CAgD7F"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Operator-authored tool permissions, compiled to the kernel's rule vocabulary.
3
+ *
4
+ * The kernel already has the engine — `VerificationRule[]` with an
5
+ * allow/deny/review decision, evaluated first-match-wins. What was missing was
6
+ * any way for a person to author those rules: the CLI passed `rules: []`, so a
7
+ * gate with seven rule types ran with none of them and every decision fell back
8
+ * to "ask a human". This turns a `[permissions]` table into that array.
9
+ *
10
+ * The whole surface is pure: config in, rules out. The gate stays the only
11
+ * thing that decides anything, which is what keeps this testable without a run.
12
+ */
13
+ const EFFECTS = new Set(['allow', 'ask', 'deny']);
14
+ export function isPermissionEffect(value) {
15
+ return typeof value === 'string' && EFFECTS.has(value);
16
+ }
17
+ /**
18
+ * Turn a glob-ish pattern into an anchored regex source.
19
+ *
20
+ * `*` matches any run of characters, `?` exactly one; everything else is
21
+ * literal. Backslashes become forward slashes on both sides so one rule works
22
+ * on every platform — an operator writing `src/*` should not have to write it
23
+ * twice.
24
+ *
25
+ * A pattern ending in `<space>*` also matches the bare command: `git push *`
26
+ * covers `git push`. Without that, the obvious rule silently misses the exact
27
+ * invocation it was written to catch, which is the failure this whole file
28
+ * exists to stop.
29
+ */
30
+ export function patternToRegExpSource(pattern) {
31
+ const normalized = pattern.replaceAll('\\', '/');
32
+ const escaped = normalized
33
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
34
+ .replace(/\*/g, '.*')
35
+ .replace(/\?/g, '.');
36
+ const trailingSpaceStar = escaped.endsWith(' .*') ? `${escaped.slice(0, -3)}( .*)?` : escaped;
37
+ return `^${trailingSpaceStar}$`;
38
+ }
39
+ /**
40
+ * A pattern that matches this tool, and only this tool, doing this thing.
41
+ *
42
+ * The kernel's `custom_pattern` has no tool scope: `target: 'args'` tests
43
+ * `JSON.stringify(input)` for ANY tool, so a rule written under `bash` would
44
+ * also decide an `edit` call whose arguments happened to match. `target:
45
+ * 'both'` tests `` `${toolName} ${JSON.stringify(input)}` ``, so the tool name
46
+ * can be pinned by writing it into the pattern — which is what this does.
47
+ *
48
+ * The argument half is deliberately unanchored on both sides, because the
49
+ * operator writes `git push*` while the gate sees
50
+ * `bash {"command":"git push origin main"}`. An anchored argument pattern would
51
+ * match nothing at all — a rule that reads like a prohibition and silently
52
+ * permits everything, which is worse than having no rule.
53
+ *
54
+ * The looseness is real and worth naming: a pattern can match text in any
55
+ * argument field, not only the one the operator had in mind. Narrowing that
56
+ * needs a rule type that names the argument, which is the kernel's to add.
57
+ */
58
+ export function toolScopedPattern(tool, pattern) {
59
+ const toolPart = escapeRegExp(tool);
60
+ const argPart = patternToRegExpSource(pattern).slice(1, -1);
61
+ return `^${toolPart} .*${argPart}.*$`;
62
+ }
63
+ function escapeRegExp(value) {
64
+ return value.replace(/[.*+^${}()|[\]\\?]/g, '\\$&');
65
+ }
66
+ /** Whether `input` satisfies `pattern`, with the matching above. */
67
+ export function matchesPattern(input, pattern) {
68
+ // Case-insensitive on Windows, where paths and commands are, and
69
+ // case-sensitive everywhere else, where they are not.
70
+ const flags = process.platform === 'win32' ? 'si' : 's';
71
+ return new RegExp(patternToRegExpSource(pattern), flags).test(input.replaceAll('\\', '/'));
72
+ }
73
+ /**
74
+ * Order patterns so the most specific is tried first.
75
+ *
76
+ * The kernel evaluates first-match-wins, so `bash = { "*" = "ask", "git push*"
77
+ * = "deny" }` would otherwise resolve to `ask` and the deny would be dead —
78
+ * config that reads like a prohibition and is not one. Sorting here means an
79
+ * operator does not have to know the evaluation order to write what they mean.
80
+ *
81
+ * "Specific" is: a bare `*` is always last, then longer literal prefixes before
82
+ * shorter ones, then alphabetical so the result is stable to read and to test.
83
+ */
84
+ export function bySpecificity(a, b) {
85
+ if (a === '*' && b !== '*')
86
+ return 1;
87
+ if (b === '*' && a !== '*')
88
+ return -1;
89
+ const literal = (p) => {
90
+ const star = p.search(/[*?]/);
91
+ return star === -1 ? p.length : star;
92
+ };
93
+ const diff = literal(b) - literal(a);
94
+ if (diff !== 0)
95
+ return diff;
96
+ return a < b ? -1 : a > b ? 1 : 0;
97
+ }
98
+ /**
99
+ * Compile a `[permissions]` table into kernel rules.
100
+ *
101
+ * `ask` emits nothing. That is not an omission: the gate's fallback for an
102
+ * unmatched call is already `review`, so the rule that says "ask" and the
103
+ * absence of any rule have to mean the same thing or the config would be
104
+ * lying about one of them. It also keeps the emitted array small enough for a
105
+ * person to read in a log.
106
+ *
107
+ * A tool nobody wrote a rule for is therefore asked about, and there is no way
108
+ * to spell "allow everything by omission" — the only way to widen is to say so.
109
+ */
110
+ export function compilePermissions(config) {
111
+ const rules = [];
112
+ const diagnostics = [];
113
+ if (!config)
114
+ return { rules, diagnostics };
115
+ for (const [tool, permission] of Object.entries(config)) {
116
+ if (isPermissionEffect(permission)) {
117
+ if (permission === 'allow')
118
+ rules.push({ type: 'allow_by_name', toolNames: [tool] });
119
+ if (permission === 'deny')
120
+ rules.push({ type: 'deny_by_name', toolNames: [tool] });
121
+ continue;
122
+ }
123
+ if (typeof permission !== 'object' || permission === null) {
124
+ diagnostics.push({
125
+ tool,
126
+ message: `expected "allow", "ask", "deny", or a table of patterns; got ${typeof permission}`,
127
+ });
128
+ continue;
129
+ }
130
+ for (const pattern of Object.keys(permission).sort(bySpecificity)) {
131
+ const effect = permission[pattern];
132
+ if (!isPermissionEffect(effect)) {
133
+ diagnostics.push({
134
+ tool,
135
+ pattern,
136
+ message: `expected "allow", "ask" or "deny"; got ${JSON.stringify(effect)}`,
137
+ });
138
+ continue;
139
+ }
140
+ if (effect === 'ask')
141
+ continue;
142
+ try {
143
+ new RegExp(patternToRegExpSource(pattern));
144
+ }
145
+ catch (err) {
146
+ diagnostics.push({
147
+ tool,
148
+ pattern,
149
+ message: `pattern is not usable: ${err instanceof Error ? err.message : String(err)}`,
150
+ });
151
+ continue;
152
+ }
153
+ rules.push({
154
+ type: 'custom_pattern',
155
+ pattern: toolScopedPattern(tool, pattern),
156
+ target: 'both',
157
+ decision: effect,
158
+ });
159
+ }
160
+ }
161
+ return { rules, diagnostics };
162
+ }
163
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/permissions/rules.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA6DH,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,YAAY,CAAA;AAE/D,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CACxB;AAWD,wBAAgB,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,QAAQ,2CA60BpC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqDH,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,YAAY,CAAA;AAE/D,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CACxB;AAWD,wBAAgB,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,QAAQ,2CAytBpC"}