@intutic/cli 2.0.0 → 2.2.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 (225) hide show
  1. package/dist/cli.js +131 -130
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/attenuate.d.ts.map +1 -1
  4. package/dist/commands/attenuate.js +4 -2
  5. package/dist/commands/attenuate.js.map +1 -1
  6. package/dist/commands/budget.d.ts.map +1 -1
  7. package/dist/commands/budget.js +2 -2
  8. package/dist/commands/budget.js.map +1 -1
  9. package/dist/commands/connect.d.ts.map +1 -1
  10. package/dist/commands/connect.js +138 -49
  11. package/dist/commands/connect.js.map +1 -1
  12. package/dist/commands/disconnect.d.ts +25 -0
  13. package/dist/commands/disconnect.d.ts.map +1 -0
  14. package/dist/commands/disconnect.js +252 -0
  15. package/dist/commands/disconnect.js.map +1 -0
  16. package/dist/commands/doctor.d.ts +19 -1
  17. package/dist/commands/doctor.d.ts.map +1 -1
  18. package/dist/commands/doctor.js +33 -41
  19. package/dist/commands/doctor.js.map +1 -1
  20. package/dist/commands/enforce.d.ts +10 -0
  21. package/dist/commands/enforce.d.ts.map +1 -1
  22. package/dist/commands/enforce.js +36 -0
  23. package/dist/commands/enforce.js.map +1 -1
  24. package/dist/commands/env.d.ts +1 -1
  25. package/dist/commands/env.d.ts.map +1 -1
  26. package/dist/commands/env.js +2 -7
  27. package/dist/commands/env.js.map +1 -1
  28. package/dist/commands/exec.d.ts +7 -8
  29. package/dist/commands/exec.d.ts.map +1 -1
  30. package/dist/commands/exec.js +59 -60
  31. package/dist/commands/exec.js.map +1 -1
  32. package/dist/commands/gateway.d.ts +2 -0
  33. package/dist/commands/gateway.d.ts.map +1 -1
  34. package/dist/commands/gateway.js +16 -3
  35. package/dist/commands/gateway.js.map +1 -1
  36. package/dist/commands/guardrails.d.ts.map +1 -1
  37. package/dist/commands/guardrails.js +18 -1
  38. package/dist/commands/guardrails.js.map +1 -1
  39. package/dist/commands/init.d.ts +5 -2
  40. package/dist/commands/init.d.ts.map +1 -1
  41. package/dist/commands/init.js +38 -22
  42. package/dist/commands/init.js.map +1 -1
  43. package/dist/commands/install-daemon.d.ts +1 -1
  44. package/dist/commands/install-daemon.d.ts.map +1 -1
  45. package/dist/commands/install-daemon.js +5 -3
  46. package/dist/commands/install-daemon.js.map +1 -1
  47. package/dist/commands/integrity.d.ts +31 -2
  48. package/dist/commands/integrity.d.ts.map +1 -1
  49. package/dist/commands/integrity.js +116 -3
  50. package/dist/commands/integrity.js.map +1 -1
  51. package/dist/commands/judge.d.ts +8 -4
  52. package/dist/commands/judge.d.ts.map +1 -1
  53. package/dist/commands/judge.js +41 -21
  54. package/dist/commands/judge.js.map +1 -1
  55. package/dist/commands/login.d.ts +1 -0
  56. package/dist/commands/login.d.ts.map +1 -1
  57. package/dist/commands/login.js +16 -1
  58. package/dist/commands/login.js.map +1 -1
  59. package/dist/commands/predict-cost.d.ts +1 -2
  60. package/dist/commands/predict-cost.d.ts.map +1 -1
  61. package/dist/commands/predict-cost.js +1 -4
  62. package/dist/commands/predict-cost.js.map +1 -1
  63. package/dist/commands/setup.d.ts +1 -2
  64. package/dist/commands/setup.d.ts.map +1 -1
  65. package/dist/commands/setup.js +11 -68
  66. package/dist/commands/setup.js.map +1 -1
  67. package/dist/commands/skill.d.ts +1 -0
  68. package/dist/commands/skill.d.ts.map +1 -1
  69. package/dist/commands/skill.js +17 -3
  70. package/dist/commands/skill.js.map +1 -1
  71. package/dist/commands/start.d.ts.map +1 -1
  72. package/dist/commands/start.js +5 -1
  73. package/dist/commands/start.js.map +1 -1
  74. package/dist/commands/syncContext.d.ts.map +1 -1
  75. package/dist/commands/syncContext.js +15 -5
  76. package/dist/commands/syncContext.js.map +1 -1
  77. package/dist/config/paths.d.ts +25 -3
  78. package/dist/config/paths.d.ts.map +1 -1
  79. package/dist/config/paths.js +47 -7
  80. package/dist/config/paths.js.map +1 -1
  81. package/dist/harness/agentFramework.d.ts +25 -0
  82. package/dist/harness/agentFramework.d.ts.map +1 -0
  83. package/dist/harness/agentFramework.js +37 -0
  84. package/dist/harness/agentFramework.js.map +1 -0
  85. package/dist/harness/agentcore.d.ts +6 -3
  86. package/dist/harness/agentcore.d.ts.map +1 -1
  87. package/dist/harness/agentcore.js +7 -4
  88. package/dist/harness/agentcore.js.map +1 -1
  89. package/dist/harness/agenticOrchestrator.d.ts +3 -5
  90. package/dist/harness/agenticOrchestrator.d.ts.map +1 -1
  91. package/dist/harness/agenticOrchestrator.js +3 -5
  92. package/dist/harness/agenticOrchestrator.js.map +1 -1
  93. package/dist/harness/aiSdkHarness.d.ts +1 -1
  94. package/dist/harness/aiSdkHarness.js +1 -1
  95. package/dist/harness/aiSdkWorkflow.d.ts +1 -1
  96. package/dist/harness/aiSdkWorkflow.d.ts.map +1 -1
  97. package/dist/harness/aiSdkWorkflow.js +5 -3
  98. package/dist/harness/aiSdkWorkflow.js.map +1 -1
  99. package/dist/harness/aider.d.ts +4 -3
  100. package/dist/harness/aider.d.ts.map +1 -1
  101. package/dist/harness/aider.js +7 -6
  102. package/dist/harness/aider.js.map +1 -1
  103. package/dist/harness/antigravity.d.ts +4 -2
  104. package/dist/harness/antigravity.d.ts.map +1 -1
  105. package/dist/harness/antigravity.js +15 -12
  106. package/dist/harness/antigravity.js.map +1 -1
  107. package/dist/harness/base.d.ts +5 -1
  108. package/dist/harness/base.d.ts.map +1 -1
  109. package/dist/harness/base.js +9 -8
  110. package/dist/harness/base.js.map +1 -1
  111. package/dist/harness/cline.d.ts +10 -4
  112. package/dist/harness/cline.d.ts.map +1 -1
  113. package/dist/harness/cline.js +40 -32
  114. package/dist/harness/cline.js.map +1 -1
  115. package/dist/harness/codex.d.ts +10 -4
  116. package/dist/harness/codex.d.ts.map +1 -1
  117. package/dist/harness/codex.js +30 -32
  118. package/dist/harness/codex.js.map +1 -1
  119. package/dist/harness/continue.d.ts +8 -5
  120. package/dist/harness/continue.d.ts.map +1 -1
  121. package/dist/harness/continue.js +27 -63
  122. package/dist/harness/continue.js.map +1 -1
  123. package/dist/harness/cursor.d.ts.map +1 -1
  124. package/dist/harness/cursor.js +2 -6
  125. package/dist/harness/cursor.js.map +1 -1
  126. package/dist/harness/detector.d.ts +1 -1
  127. package/dist/harness/detector.d.ts.map +1 -1
  128. package/dist/harness/detector.js +3 -1
  129. package/dist/harness/detector.js.map +1 -1
  130. package/dist/harness/dsh.d.ts +2 -2
  131. package/dist/harness/dsh.js +3 -3
  132. package/dist/harness/dsh.js.map +1 -1
  133. package/dist/harness/eve.d.ts +1 -1
  134. package/dist/harness/eve.js +1 -1
  135. package/dist/harness/githubCopilot.d.ts +4 -2
  136. package/dist/harness/githubCopilot.d.ts.map +1 -1
  137. package/dist/harness/githubCopilot.js +13 -8
  138. package/dist/harness/githubCopilot.js.map +1 -1
  139. package/dist/harness/grok.d.ts.map +1 -1
  140. package/dist/harness/grok.js +4 -6
  141. package/dist/harness/grok.js.map +1 -1
  142. package/dist/harness/jsSdkGatedAdapter.d.ts.map +1 -1
  143. package/dist/harness/jsSdkGatedAdapter.js +8 -9
  144. package/dist/harness/jsSdkGatedAdapter.js.map +1 -1
  145. package/dist/harness/langgraph.d.ts.map +1 -1
  146. package/dist/harness/langgraph.js +8 -10
  147. package/dist/harness/langgraph.js.map +1 -1
  148. package/dist/harness/muse.d.ts.map +1 -1
  149. package/dist/harness/muse.js +4 -6
  150. package/dist/harness/muse.js.map +1 -1
  151. package/dist/harness/n8n.d.ts +12 -2
  152. package/dist/harness/n8n.d.ts.map +1 -1
  153. package/dist/harness/n8n.js +66 -3
  154. package/dist/harness/n8n.js.map +1 -1
  155. package/dist/harness/openWebUI.d.ts +7 -2
  156. package/dist/harness/openWebUI.d.ts.map +1 -1
  157. package/dist/harness/openWebUI.js +15 -4
  158. package/dist/harness/openWebUI.js.map +1 -1
  159. package/dist/harness/openaiAgents.d.ts.map +1 -1
  160. package/dist/harness/openaiAgents.js +4 -1
  161. package/dist/harness/openaiAgents.js.map +1 -1
  162. package/dist/harness/opencode.d.ts +3 -2
  163. package/dist/harness/opencode.d.ts.map +1 -1
  164. package/dist/harness/opencode.js +7 -8
  165. package/dist/harness/opencode.js.map +1 -1
  166. package/dist/harness/openhands.d.ts +3 -2
  167. package/dist/harness/openhands.d.ts.map +1 -1
  168. package/dist/harness/openhands.js +40 -27
  169. package/dist/harness/openhands.js.map +1 -1
  170. package/dist/harness/rooCode.d.ts +6 -5
  171. package/dist/harness/rooCode.d.ts.map +1 -1
  172. package/dist/harness/rooCode.js +13 -18
  173. package/dist/harness/rooCode.js.map +1 -1
  174. package/dist/harness/sdkGatedAdapter.d.ts.map +1 -1
  175. package/dist/harness/sdkGatedAdapter.js +8 -9
  176. package/dist/harness/sdkGatedAdapter.js.map +1 -1
  177. package/dist/harness/trueforge.js +2 -2
  178. package/dist/harness/trueforge.js.map +1 -1
  179. package/dist/harness/types.d.ts.map +1 -1
  180. package/dist/harness/types.js +4 -1
  181. package/dist/harness/types.js.map +1 -1
  182. package/dist/harness/windsurf.d.ts +4 -2
  183. package/dist/harness/windsurf.d.ts.map +1 -1
  184. package/dist/harness/windsurf.js +14 -11
  185. package/dist/harness/windsurf.js.map +1 -1
  186. package/dist/harness/xirp.d.ts +5 -7
  187. package/dist/harness/xirp.d.ts.map +1 -1
  188. package/dist/harness/xirp.js +5 -7
  189. package/dist/harness/xirp.js.map +1 -1
  190. package/dist/lib/envInjector.d.ts +16 -6
  191. package/dist/lib/envInjector.d.ts.map +1 -1
  192. package/dist/lib/envInjector.js +99 -88
  193. package/dist/lib/envInjector.js.map +1 -1
  194. package/dist/lib/localProxy.d.ts +19 -0
  195. package/dist/lib/localProxy.d.ts.map +1 -0
  196. package/dist/lib/localProxy.js +33 -0
  197. package/dist/lib/localProxy.js.map +1 -0
  198. package/dist/lib/onboarding.d.ts +1 -1
  199. package/dist/lib/onboarding.d.ts.map +1 -1
  200. package/dist/lib/onboarding.js +21 -27
  201. package/dist/lib/onboarding.js.map +1 -1
  202. package/dist/lib/otelPropagation.d.ts +1 -1
  203. package/dist/lib/otelPropagation.js +1 -1
  204. package/dist/lib/policySnapshot.d.ts +2 -1
  205. package/dist/lib/policySnapshot.d.ts.map +1 -1
  206. package/dist/lib/policySnapshot.js +11 -4
  207. package/dist/lib/policySnapshot.js.map +1 -1
  208. package/dist/lib/sandbox/image.d.ts +27 -0
  209. package/dist/lib/sandbox/image.d.ts.map +1 -0
  210. package/dist/lib/sandbox/image.js +54 -0
  211. package/dist/lib/sandbox/image.js.map +1 -0
  212. package/dist/lib/sandbox/oci.d.ts +1 -0
  213. package/dist/lib/sandbox/oci.d.ts.map +1 -1
  214. package/dist/lib/sandbox/oci.js +4 -0
  215. package/dist/lib/sandbox/oci.js.map +1 -1
  216. package/dist/lib/sandbox/types.d.ts +5 -0
  217. package/dist/lib/sandbox/types.d.ts.map +1 -1
  218. package/package.json +6 -5
  219. package/resources/sandbox/Dockerfile +30 -0
  220. package/resources/sandbox/entrypoint.sh +108 -0
  221. package/resources/sandbox/firecracker-launch.sh +120 -0
  222. package/dist/harness/vscodeSettingsWriter.d.ts +0 -40
  223. package/dist/harness/vscodeSettingsWriter.d.ts.map +0 -1
  224. package/dist/harness/vscodeSettingsWriter.js +0 -116
  225. package/dist/harness/vscodeSettingsWriter.js.map +0 -1
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import { Command } from 'commander';
14
14
  import { createRequire } from 'node:module';
15
+ import { DEFAULT_SANDBOX_IMAGE } from './lib/sandbox/image.js';
15
16
  // Read the version from package.json rather than repeating it here. The literal
16
17
  // that used to live below said 1.6.0 for three releases running, so
17
18
  // `intutic --version` reported a version the user did not have — and any check
@@ -20,14 +21,22 @@ import { createRequire } from 'node:module';
20
21
  // but JSON imports still need an assertion on older runtimes.
21
22
  const { version } = createRequire(import.meta.url)('../package.json');
22
23
  const program = new Command();
24
+ // Positional options: `--version` and `--help` belong to the program only when
25
+ // they come before a subcommand. Without this, commander matches program
26
+ // options anywhere on the line, so `intutic policy rollback <id> --version 2`
27
+ // printed the CLI version and exited before `policy rollback` ever saw its own
28
+ // required `--version`.
23
29
  program
24
30
  .name('intutic')
25
31
  .description('Intutic CLI — AI governance control plane for developer workspaces')
26
- .version(version);
32
+ .version(version)
33
+ .enablePositionalOptions();
27
34
  program
28
35
  .command('init')
29
- .description('Initialize workspace — detect harnesses, configure sync')
36
+ .description('Initialize workspace — detect harnesses and record them in ~/.intutic/config.json')
30
37
  .option('--dev', 'Use local control plane (http://localhost:3001)')
38
+ .option('--git-hooks', 'Install the Intutic Git hooks without asking')
39
+ .option('--no-git-hooks', 'Skip the Git hooks without asking (the default when stdin is not a terminal)')
31
40
  .action(async (opts) => {
32
41
  const { runInit } = await import('./commands/init.js');
33
42
  await runInit(opts);
@@ -35,7 +44,7 @@ program
35
44
  // ── Cohort setup wizard (LLD #70) ────────────────────────────────────────
36
45
  program
37
46
  .command('setup')
38
- .description('Guided setup: detect harnesses, configure a provider credential, verify it, and (optionally) a judge model')
47
+ .description('Guided setup: detect harnesses, configure a provider credential, and verify it')
39
48
  .option('--dev', 'Use local control plane (http://localhost:3001)')
40
49
  .action(async (opts) => {
41
50
  const { runSetup } = await import('./commands/setup.js');
@@ -46,7 +55,7 @@ const judgeCmd = program
46
55
  .description('Configure the LLM-as-judge path');
47
56
  judgeCmd
48
57
  .command('configure')
49
- .description('Generate local artifacts (litellm_config.yaml, env, Helm values) for an on-prem judge — never a remote API call')
58
+ .description('Generate local artifacts (litellm_config.yaml, env, Helm values) for an on-prem judge on a self-hosted model — never a remote API call')
50
59
  .option('--out <path>', 'Where to write litellm_config.yaml', './litellm_config.yaml')
51
60
  .action(async (opts) => {
52
61
  const { runJudgeConfigure } = await import('./commands/judge.js');
@@ -56,11 +65,22 @@ program
56
65
  .command('login')
57
66
  .description('Authenticate with the Intutic control plane')
58
67
  .option('--api-key <key>', 'Authenticate with an API key (vk_*)')
68
+ .option('--control-plane-url <url>', 'Control plane to log in to (e.g. a self-hosted one); saved for every later command')
59
69
  .option('--dev', 'Use local control plane (http://localhost:3001)')
60
70
  .action(async (opts) => {
61
71
  const { runLogin } = await import('./commands/login.js');
62
72
  await runLogin(opts);
63
73
  });
74
+ program
75
+ .command('disconnect')
76
+ .description('Undo intutic connect: restore every harness config it changed, remove its services, and log out')
77
+ .option('--harness <id>', 'Disconnect one harness only (credentials and services stay)')
78
+ .option('--dry-run', 'Print exactly what would change, and change nothing')
79
+ .option('--keep-login', 'Keep the stored credentials')
80
+ .action(async (opts) => {
81
+ const { runDisconnect } = await import('./commands/disconnect.js');
82
+ await runDisconnect({ harness: opts.harness, dryRun: opts.dryRun, keepLogin: opts.keepLogin });
83
+ });
64
84
  program
65
85
  .command('logout')
66
86
  .description('Clear stored credentials')
@@ -108,11 +128,10 @@ program
108
128
  });
109
129
  program
110
130
  .command('predict-cost')
111
- .description('Pre-flight cost estimate for a prompt/task before it runs (POST /api/v1/predict-cost). ' +
112
- 'Estimates output tokens from the workspace baseline and prices with the same rate table ' +
113
- 'every other cost figure in the control plane uses.')
131
+ .description('Pre-flight cost estimate for a prompt/task before it runs (requires `intutic login`). ' +
132
+ "Estimates output tokens from the workspace's recorded usage and prices them with the same " +
133
+ 'rate table as every other cost figure the control plane reports.')
114
134
  .requiredOption('--model <model>', 'Model to estimate against (e.g. claude-sonnet-4-5)')
115
- .option('--task-type <type>', 'Task type used to pick the baseline bucket', 'coding')
116
135
  .option('--tokens <n>', 'Input token count (mutually exclusive with --file)')
117
136
  .option('--file <path>', 'File whose contents size the input (mutually exclusive with --tokens)')
118
137
  .option('--json', 'Output as JSON instead of a report')
@@ -128,7 +147,7 @@ sopsCmd
128
147
  .command('push <name>')
129
148
  .description('Push a local offline SOP folder to the central workspace')
130
149
  .option('--dev', 'Use local control plane (http://localhost:3001)')
131
- .option('--org', "Push as an org-wide floor (LLD #65 follow-up) instead of a workspace SOP -- applies to every workspace under the caller's org, in addition to that workspace's own SOPs")
150
+ .option('--org', "Push as an org-wide floor instead of a workspace SOP -- applies to every workspace under the caller's org, in addition to that workspace's own SOPs")
132
151
  .action(async (name, opts) => {
133
152
  const { runSopsPush } = await import('./commands/sops.js');
134
153
  await runSopsPush(name, opts);
@@ -152,7 +171,7 @@ sopsCmd
152
171
  });
153
172
  sopsCmd
154
173
  .command('org-list')
155
- .description("List org-wide SOP floors for the caller's own org (LLD #65 follow-up)")
174
+ .description("List org-wide SOP floors for the caller's own org")
156
175
  .option('--dev', 'Use local control plane (http://localhost:3001)')
157
176
  .action(async (opts) => {
158
177
  const { runSopsOrgList } = await import('./commands/sops.js');
@@ -349,7 +368,7 @@ guardrailsCmd
349
368
  .command('list')
350
369
  .description('List guardrails with their status and shadow evidence')
351
370
  .option('--status <status>', 'PROPOSED | SHADOW | ENFORCING | REJECTED | RETIRED')
352
- .option('--target <target>', 'hook_rule | sop_front_matter | wasm_rule')
371
+ .option('--target <target>', 'hook_rule | sop_front_matter | wasm_rule | workspace_setting')
353
372
  .option('--doc <docId>', 'Only guardrails cited from this document')
354
373
  .option('--limit <n>', 'Max rows (default 50, capped at 200)')
355
374
  .option('--json', 'Output as JSON')
@@ -378,8 +397,8 @@ guardrailsCmd
378
397
  });
379
398
  guardrailsCmd
380
399
  .command('promote <guardrailId>')
381
- .description('Promote a shadow guardrail to enforcing once the server says it is ready')
382
- .option('--acknowledge-no-traffic', 'Promote a rule that no observed traffic exercised')
400
+ .description('Promote a shadow guardrail to enforcing once the server says it is ready (an egress allow list is applied from proposed)')
401
+ .option('--acknowledge-no-traffic', 'Promote a rule that no observed traffic exercised, or apply an egress allow list, which has no shadow evidence')
383
402
  .option('--json', 'Output as JSON')
384
403
  .option('--dev', 'Use local control plane (http://localhost:3001)')
385
404
  .action(async (guardrailId, opts) => {
@@ -475,7 +494,7 @@ program
475
494
  program
476
495
  .command('start')
477
496
  .description('Start the proxy standalone — no account or control plane needed')
478
- .option('--port <port>', 'Proxy port', '4000')
497
+ .option('--port <port>', 'Proxy port (default: the port of $INTUTIC_PROXY_URL, else 4000)')
479
498
  .option('--valkey-port <port>', 'Valkey port', '6379')
480
499
  .option('--upstream-url <url>', 'Upstream LLM provider base URL')
481
500
  .action(async (opts) => {
@@ -531,7 +550,7 @@ enterpriseCmd
531
550
  .option('--mdm-output-dir <dir>', 'Directory to write generated MDM manifests to', './intutic-mdm')
532
551
  .option('--skip-ca', 'Skip system-wide CA trust installation')
533
552
  .option('--skip-hooks', 'Skip system-level Cursor hooks installation')
534
- .option('--dev', 'Use local control plane (http://localhost:3001) when resolving workspace context')
553
+ .option('--dev', 'Send the device enforcement report to the local control plane (http://localhost:3001)')
535
554
  .option('--cli-binary-path <path>', 'Absolute path to the intutic CLI binary on TARGET machines, embedded in the firewall-deployment manifests (default: /usr/local/bin/intutic — override if your fleet installs elsewhere)')
536
555
  .action(async (opts) => {
537
556
  const { runEnterpriseInstall } = await import('./commands/enterprise.js');
@@ -542,7 +561,7 @@ const envCmd = program
542
561
  .description('Persist or clear the proxy base-URL environment variables');
543
562
  envCmd
544
563
  .command('persist')
545
- .description('Write ANTHROPIC_BASE_URL / OPENAI_BASE_URL at the OS level so they survive a restart.\n' +
564
+ .description('Write ANTHROPIC_BASE_URL / OPENAI_BASE_URL at the OS level so new shells and apps pick them up.\n' +
546
565
  ' macOS: launchctl setenv Linux: ~/.bashrc Windows: setx\n' +
547
566
  '\n' +
548
567
  ' Opt-in on purpose: on macOS this reaches every GUI app you launch afterwards.\n' +
@@ -573,8 +592,8 @@ program
573
592
  });
574
593
  program
575
594
  .command('sync-context')
576
- .description('Sync Git context metadata to the local daemon')
577
- .option('--git', 'Sync Git branch and commit information')
595
+ .description('Record the Git branch and commit in .intutic/git-context.json, which a running `intutic connect` reports')
596
+ .option('--git', 'Read the branch and commit from the repository (for any not given below)')
578
597
  .option('--branch <name>', 'Current Git branch name')
579
598
  .option('--commit <hash>', 'Current Git commit SHA')
580
599
  .action(async (opts) => {
@@ -585,7 +604,7 @@ program
585
604
  .command('decisions-log-refresh')
586
605
  .description('One-shot refresh of the governed decisions log (.intutic/DECISIONS.md + the claude-code ' +
587
606
  'harness config section) — no-ops if decisionsLogEnabled is off. Invoked by the optional ' +
588
- 'post-merge hook (see gitHooks.ts); safe to run manually.')
607
+ 'post-merge Git hook `intutic init` installs; safe to run manually.')
589
608
  .action(async () => {
590
609
  const { refreshDecisionsLog } = await import('./lib/decisionsLogRefresh.js');
591
610
  const result = await refreshDecisionsLog(process.cwd());
@@ -602,7 +621,7 @@ program
602
621
  // (LLD #63 §6): cap-drop, no-new-privileges, read-only rootfs, resource caps,
603
622
  // and a default-deny egress firewall the agent cannot undo.
604
623
  .option('--sandbox [kind]', 'Run the agent in an isolated sandbox (kind: oci | firecracker; default oci)')
605
- .option('--sandbox-image <image>', 'Sandbox image (must contain the agent + nftables + capsh)', 'intutic/sandbox:latest')
624
+ .option('--sandbox-image <image>', 'Sandbox image (must contain the agent + nftables + capsh); the default is built locally on first use', DEFAULT_SANDBOX_IMAGE)
606
625
  .option('--sandbox-memory <size>', 'Sandbox memory cap (e.g. 2g)', '2g')
607
626
  .option('--sandbox-cpus <n>', 'Sandbox CPU cap', '2')
608
627
  .option('--sandbox-pids <n>', 'Sandbox max process count', '512')
@@ -658,7 +677,7 @@ const findings = program
658
677
  findings
659
678
  .command('list')
660
679
  .description('List detector findings for the workspace')
661
- .option('--unadjudicated', "Only show findings nobody has ruled on yet (the route's own default is to show all)")
680
+ .option('--unadjudicated', 'Only show findings nobody has ruled on yet (default: all findings)')
662
681
  .option('--detector <id>', 'Filter by detector_id (e.g. response_injection:override-instructions)')
663
682
  .option('--limit <n>', 'Max rows to return (default: 100, max: 500)')
664
683
  .option('--json', 'Output as JSON instead of table')
@@ -690,8 +709,8 @@ findings
690
709
  findings
691
710
  .command('echo-report')
692
711
  .description('Response-injection echo measurement report — per-pattern false-positive rate')
693
- .option('--since <date>', 'ISO date — window start (route default: trailing 7 days)')
694
- .option('--until <date>', 'ISO date — window end (route default: now)')
712
+ .option('--since <date>', 'ISO date — window start (default: 7 days before --until)')
713
+ .option('--until <date>', 'ISO date — window end (default: now)')
695
714
  .option('--json', 'Output as JSON instead of table')
696
715
  .option('--dev', 'Use local control plane (http://localhost:3001)')
697
716
  .action(async (opts) => {
@@ -715,6 +734,7 @@ integrity
715
734
  integrity
716
735
  .command('verify <root_id>')
717
736
  .description('Re-derive a root from the live traces and check its signature (exit 1 on mismatch)')
737
+ .option('--against <file>', 'Also compare with the copy of this root mirrored to your own bucket (BYOC)')
718
738
  .option('--json', 'Output as JSON instead of a report')
719
739
  .option('--dev', 'Use local control plane (http://localhost:3001)')
720
740
  .action(async (rootId, opts) => {
@@ -747,7 +767,7 @@ const routing = program
747
767
  routing
748
768
  .command('adoption-report')
749
769
  .description('Mirror-test adoption report for one candidate model — win/loss/tie, fault-rate delta, ' +
750
- 'cost delta, latency delta (Phase 7b: GET /api/v1/routing/mirror-adoption-report). ' +
770
+ 'cost delta, latency delta. ' +
751
771
  'A reported signal for human review, not an automatic gate.')
752
772
  .requiredOption('--candidate-model <model>', 'The mirror-tested candidate model to report on')
753
773
  .option('--json', 'Output as JSON instead of a report')
@@ -760,80 +780,93 @@ routing
760
780
  const daemon = program
761
781
  .command('daemon')
762
782
  .description('Manage the Intutic sync-daemon, MCP daemon and standalone proxy system services (LaunchAgent / systemd)');
763
- daemon
764
- .command('install')
765
- .alias('install-daemon') // top-level alias for discoverability
766
- .description('Install sync-daemon as a system service (auto-starts on login, restarts on any exit).\n' +
767
- ' macOS: ~/Library/LaunchAgents/ai.intutic.sync-daemon.plist (KeepAlive: true)\n' +
768
- ' Linux: ~/.config/systemd/user/intutic-sync-daemon.service (Restart=always)\n' +
769
- '\n' +
770
- ' --proxy installs the standalone intutic-proxy binary instead (no workspace or key needed):\n' +
771
- ' macOS: ~/Library/LaunchAgents/ai.intutic.proxy.plist\n' +
772
- ' Linux: ~/.config/systemd/user/intutic-proxy.service\n' +
773
- '\n' +
774
- ' NOTE: To stop the daemon you MUST use \'intutic daemon uninstall\' or \'launchctl unload\'.\n' +
775
- ' \'intutic disconnect\' alone will NOT stop a daemon-installed service. (TD-154)')
776
- .option('--workspace-id <id>', 'Workspace ID (e.g. wk_xxxx) — required unless --proxy')
777
- .option('--api-key <key>', 'Workspace API key (e.g. vk_xxxx) — required unless --proxy')
778
- .option('--control-plane-url <url>', 'Control plane URL', 'https://api.intutic.ai')
779
- .option('--binary-path <path>', 'Path to intutic CLI binary (defaults to current process); with --proxy, absolute path to intutic-proxy')
780
- .option('--dry-run', 'Print what would be done without writing files')
781
- .option('--system', 'Install as a system-level service (LaunchDaemon on macOS, systemd system unit on Linux)')
782
- .option('--mcp', 'Install the MCP proxy daemon instead of the sync-daemon')
783
- .option('--proxy', 'Install the standalone intutic-proxy binary as a service (TD-465)')
784
- .option('--port <port>', 'With --proxy: proxy listen port', '4000')
785
- .option('--valkey-url <url>', 'With --proxy: Valkey URL to attach to; omit to run standalone (INTUTIC_STANDALONE=1)')
786
- .option('--upstream-url <url>', 'With --proxy: upstream LLM provider base URL')
787
- .action(async (opts, cmd) => {
788
- if (opts.proxy) {
789
- // The standalone proxy has no control plane, so no workspace or key.
790
- // Its unit carries the same environment `intutic start` sets, fixed at
791
- // install time rather than probed at launch (TD-465).
792
- const { installProxyService } = await import('./commands/install-daemon.js');
793
- await installProxyService({
794
- port: opts.port,
795
- valkeyUrl: opts.valkeyUrl,
796
- upstreamUrl: opts.upstreamUrl,
783
+ // `install` and `uninstall` are also reachable as the top-level shortcuts
784
+ // `intutic install-daemon` / `intutic uninstall-daemon`. A commander alias only
785
+ // renames a command within its own parent, so each verb is defined once here
786
+ // and registered in both places. The shortcuts used to be separate copies, and
787
+ // they drifted: `install-daemon` had no --mcp, --proxy or proxy options and
788
+ // demanded a workspace and key even for the standalone proxy, while the docs
789
+ // called it a shortcut for `daemon install`.
790
+ function defineDaemonInstall(cmd) {
791
+ return cmd
792
+ .description('Install sync-daemon as a system service (auto-starts on login, restarts on any exit).\n' +
793
+ ' macOS: ~/Library/LaunchAgents/ai.intutic.sync-daemon.plist (KeepAlive: true)\n' +
794
+ ' Linux: ~/.config/systemd/user/intutic-sync-daemon.service (Restart=always)\n' +
795
+ '\n' +
796
+ ' --proxy installs the standalone intutic-proxy binary instead (no workspace or key needed):\n' +
797
+ ' macOS: ~/Library/LaunchAgents/ai.intutic.proxy.plist\n' +
798
+ ' Linux: ~/.config/systemd/user/intutic-proxy.service\n' +
799
+ '\n' +
800
+ ' The service restarts whenever its process exits, so killing the process does not stop it.\n' +
801
+ ' Use \'intutic daemon stop\' or \'intutic daemon uninstall\'.')
802
+ .option('--workspace-id <id>', 'Workspace ID (e.g. wk_xxxx) — required unless --proxy')
803
+ .option('--api-key <key>', 'Workspace API key (e.g. vk_xxxx) — required unless --proxy')
804
+ .option('--control-plane-url <url>', 'Control plane the daemon connects to (default: $INTUTIC_CONTROL_PLANE_URL, then the URL saved by `intutic login`, then https://api.intutic.ai)')
805
+ .option('--binary-path <path>', 'Path to intutic CLI binary (defaults to current process); with --proxy, absolute path to intutic-proxy')
806
+ .option('--dry-run', 'Print what would be done without writing files')
807
+ .option('--system', 'Install as a system-level service (LaunchDaemon on macOS, systemd system unit on Linux)')
808
+ .option('--mcp', 'Install the MCP proxy daemon instead of the sync-daemon')
809
+ .option('--proxy', 'Install the standalone intutic-proxy binary as a service')
810
+ .option('--port <port>', 'With --proxy: proxy listen port (default: the port of $INTUTIC_PROXY_URL, else 4000)')
811
+ .option('--valkey-url <url>', 'With --proxy: Valkey URL to attach to; omit to run standalone (INTUTIC_STANDALONE=1)')
812
+ .option('--upstream-url <url>', 'With --proxy: upstream LLM provider base URL')
813
+ .action(async (opts, cmd) => {
814
+ if (opts.proxy) {
815
+ // The standalone proxy has no control plane, so no workspace or key.
816
+ // Its unit carries the same environment `intutic start` sets, fixed at
817
+ // install time rather than probed at launch.
818
+ const { installProxyService } = await import('./commands/install-daemon.js');
819
+ await installProxyService({
820
+ port: opts.port,
821
+ valkeyUrl: opts.valkeyUrl,
822
+ upstreamUrl: opts.upstreamUrl,
823
+ binaryPath: opts.binaryPath,
824
+ dryRun: opts.dryRun,
825
+ system: opts.system,
826
+ });
827
+ return;
828
+ }
829
+ // Required for the two daemons; commander's `requiredOption` cannot express
830
+ // "unless --proxy", so the check lives here with the same wording.
831
+ if (!opts.workspaceId)
832
+ cmd.error("error: required option '--workspace-id <id>' not specified");
833
+ if (!opts.apiKey)
834
+ cmd.error("error: required option '--api-key <key>' not specified");
835
+ // `installMcpDaemon` and `buildMcpPlist` were written, tested and exported,
836
+ // and then nothing called them: every route into install-daemon.ts landed
837
+ // on `installDaemon`, so the MCP proxy daemon could not be installed by any
838
+ // command the CLI offered. This flag is that missing route.
839
+ const { installDaemon, installMcpDaemon } = await import('./commands/install-daemon.js');
840
+ const install = opts.mcp ? installMcpDaemon : installDaemon;
841
+ await install({
842
+ workspaceId: opts.workspaceId,
843
+ apiKey: opts.apiKey,
844
+ controlPlaneUrl: opts.controlPlaneUrl,
797
845
  binaryPath: opts.binaryPath,
798
846
  dryRun: opts.dryRun,
799
847
  system: opts.system,
800
848
  });
801
- return;
802
- }
803
- // Required for the two daemons; commander's `requiredOption` cannot express
804
- // "unless --proxy", so the check lives here with the same wording.
805
- if (!opts.workspaceId)
806
- cmd.error("error: required option '--workspace-id <id>' not specified");
807
- if (!opts.apiKey)
808
- cmd.error("error: required option '--api-key <key>' not specified");
809
- // `installMcpDaemon` and `buildMcpPlist` were written, tested and exported,
810
- // and then nothing called them: every route into install-daemon.ts landed
811
- // on `installDaemon`, so the MCP proxy daemon could not be installed by any
812
- // command the CLI offered (TD-153). This flag is that missing route.
813
- const { installDaemon, installMcpDaemon } = await import('./commands/install-daemon.js');
814
- const install = opts.mcp ? installMcpDaemon : installDaemon;
815
- await install({
816
- workspaceId: opts.workspaceId,
817
- apiKey: opts.apiKey,
818
- controlPlaneUrl: opts.controlPlaneUrl,
819
- binaryPath: opts.binaryPath,
820
- dryRun: opts.dryRun,
821
- system: opts.system,
822
849
  });
823
- });
824
- daemon
825
- .command('uninstall')
826
- .alias('uninstall-daemon')
827
- .description('Remove the sync-daemon system service and stop it permanently.')
828
- .option('--dry-run', 'Print what would be done without writing files')
829
- .option('--system', 'Uninstall the system-level service')
830
- .option('--mcp', 'Uninstall the MCP proxy daemon instead of the sync-daemon')
831
- .option('--proxy', 'Uninstall the standalone intutic-proxy service instead of the sync-daemon')
832
- .action(async (opts) => {
833
- const { uninstallDaemon, uninstallMcpDaemon, uninstallProxyService } = await import('./commands/install-daemon.js');
834
- const uninstall = opts.proxy ? uninstallProxyService : opts.mcp ? uninstallMcpDaemon : uninstallDaemon;
835
- await uninstall({ dryRun: opts.dryRun, system: opts.system });
836
- });
850
+ }
851
+ function defineDaemonUninstall(cmd) {
852
+ return cmd
853
+ .description('Remove the sync-daemon system service and stop it permanently.')
854
+ .option('--dry-run', 'Print what would be done without writing files')
855
+ .option('--system', 'Uninstall the system-level service')
856
+ .option('--mcp', 'Uninstall the MCP proxy daemon instead of the sync-daemon')
857
+ .option('--proxy', 'Uninstall the standalone intutic-proxy service instead of the sync-daemon')
858
+ .action(async (opts) => {
859
+ const { uninstallDaemon, uninstallMcpDaemon, uninstallProxyService } = await import('./commands/install-daemon.js');
860
+ const uninstall = opts.proxy ? uninstallProxyService : opts.mcp ? uninstallMcpDaemon : uninstallDaemon;
861
+ await uninstall({ dryRun: opts.dryRun, system: opts.system });
862
+ });
863
+ }
864
+ defineDaemonInstall(daemon.command('install'));
865
+ defineDaemonUninstall(daemon.command('uninstall'));
866
+ defineDaemonInstall(program.command('install-daemon'))
867
+ .summary("Shortcut for 'intutic daemon install'");
868
+ defineDaemonUninstall(program.command('uninstall-daemon'))
869
+ .summary("Shortcut for 'intutic daemon uninstall'");
837
870
  daemon
838
871
  .command('status')
839
872
  .description('Show sync-daemon system service status (or the standalone proxy service with --proxy).')
@@ -870,39 +903,6 @@ daemon
870
903
  const { daemonStart, proxyServiceStart, mcpDaemonStart } = await import('./commands/install-daemon.js');
871
904
  await (opts.proxy ? proxyServiceStart() : opts.mcp ? mcpDaemonStart() : daemonStart());
872
905
  });
873
- // Top-level shortcuts (for discoverability)
874
- program
875
- .command('install-daemon', { hidden: false })
876
- .description('Shortcut for \'intutic daemon install\' — install sync-daemon as system service')
877
- .requiredOption('--workspace-id <id>', 'Workspace ID')
878
- .requiredOption('--api-key <key>', 'Workspace API key')
879
- .option('--control-plane-url <url>', 'Control plane URL', 'https://api.intutic.ai')
880
- .option('--binary-path <path>', 'Path to intutic CLI binary')
881
- .option('--dry-run', 'Print what would be done without writing files')
882
- .option('--system', 'Install as a system-level service')
883
- .action(async (opts) => {
884
- const { installDaemon } = await import('./commands/install-daemon.js');
885
- await installDaemon({
886
- workspaceId: opts.workspaceId,
887
- apiKey: opts.apiKey,
888
- controlPlaneUrl: opts.controlPlaneUrl,
889
- binaryPath: opts.binaryPath,
890
- dryRun: opts.dryRun,
891
- system: opts.system,
892
- });
893
- });
894
- program
895
- .command('uninstall-daemon', { hidden: false })
896
- .description('Shortcut for \'intutic daemon uninstall\'')
897
- .option('--dry-run', 'Print what would be done without writing files')
898
- .option('--system', 'Uninstall the system-level service')
899
- .option('--mcp', 'Uninstall the MCP proxy daemon instead of the sync-daemon')
900
- .option('--proxy', 'Uninstall the standalone intutic-proxy service instead of the sync-daemon')
901
- .action(async (opts) => {
902
- const { uninstallDaemon, uninstallMcpDaemon, uninstallProxyService } = await import('./commands/install-daemon.js');
903
- const uninstall = opts.proxy ? uninstallProxyService : opts.mcp ? uninstallMcpDaemon : uninstallDaemon;
904
- await uninstall({ dryRun: opts.dryRun, system: opts.system });
905
- });
906
906
  // ── Skill commands ─────────────────────────────────────────────────────────
907
907
  const skillCmd = program
908
908
  .command('skill')
@@ -920,9 +920,10 @@ skillCmd
920
920
  .option('--sarif', 'Output findings as a SARIF 2.1.0 document (for GitHub Code Scanning / CI interop) instead of the human-readable report')
921
921
  .option('--engine <engine>', "Scanning engine: 'native' (default, always runs) or 'cisco' to also run the opt-in Cisco " +
922
922
  "skill-scanner integration (requires the 'skill-scanner' binary on PATH — pipx install cisco-ai-skill-scanner)", 'native')
923
+ .option('--exit-zero', 'Exit 0 even when there are findings (by default findings exit 1)')
923
924
  .action(async (opts) => {
924
925
  const { runSkillAudit } = await import('./commands/skill.js');
925
- await runSkillAudit({ sarif: opts.sarif, engine: opts.engine });
926
+ await runSkillAudit({ sarif: opts.sarif, engine: opts.engine, exitZero: opts.exitZero });
926
927
  });
927
928
  skillCmd
928
929
  .command('scan-staged')
@@ -969,7 +970,7 @@ loopCmd
969
970
  .option('--approve', 'Release the hold; the run resumes')
970
971
  .option('--reject', 'Refuse the hold; the run is killed')
971
972
  .option('--note <note>', 'Why, recorded against the run')
972
- .option('--dev', 'Target the local control plane')
973
+ .option('--dev', 'Use local control plane (http://localhost:3001)')
973
974
  .action(async (loopRunId, opts) => {
974
975
  const { runLoopReview } = await import('./commands/skill.js');
975
976
  await runLoopReview(loopRunId, opts);
@@ -1028,7 +1029,7 @@ decisionCmd
1028
1029
  // see commands/gateway.ts's module doc.
1029
1030
  const gatewayCmd = program
1030
1031
  .command('gateway')
1031
- .description('Manage self-hosted gateway registrations (Docker / Kubernetes / bare-metal)');
1032
+ .description('Manage self-hosted gateway registrations (Docker / Kubernetes / bare metal)');
1032
1033
  gatewayCmd
1033
1034
  .command('register')
1034
1035
  .description('Register a new self-hosted gateway and print its one-time gwk_ token')
@@ -1154,7 +1155,7 @@ const attenuateCmd = program
1154
1155
  .description('Attenuate an API key to a narrower child key (capability subset + optional TTL)')
1155
1156
  .option('--parent-key <keyId>', 'Parent API key ID to attenuate')
1156
1157
  .option('--caps <caps>', 'Comma-separated capability subset to grant the child key')
1157
- .option('--ttl <seconds>', 'Child key TTL in seconds (server default 14400, max 86400)')
1158
+ .option('--ttl <seconds>', 'Child key TTL in whole seconds, 60 to 86400 (default 14400)')
1158
1159
  .option('--dev', 'Use local control plane (http://localhost:3001)')
1159
1160
  .action(async (opts) => {
1160
1161
  const { runAttenuate } = await import('./commands/attenuate.js');