@pellux/goodvibes-agent 0.1.116 → 1.0.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 (285) hide show
  1. package/CHANGELOG.md +48 -661
  2. package/README.md +64 -57
  3. package/bin/goodvibes-agent.ts +16 -2
  4. package/dist/package/main.js +199180 -192185
  5. package/docs/README.md +15 -8
  6. package/docs/channels-remote-and-api.md +50 -0
  7. package/docs/connected-host.md +59 -0
  8. package/docs/getting-started.md +61 -50
  9. package/docs/knowledge-artifacts-and-multimodal.md +91 -0
  10. package/docs/project-planning.md +79 -0
  11. package/docs/providers-and-routing.md +46 -0
  12. package/docs/release-and-publishing.md +48 -11
  13. package/docs/tools-and-commands.md +123 -0
  14. package/docs/voice-and-live-tts.md +51 -0
  15. package/package.json +3 -6
  16. package/src/agent/behavior-discovery-summary.ts +1 -3
  17. package/src/agent/channel-delivery.ts +201 -0
  18. package/src/agent/media-generation.ts +159 -0
  19. package/src/agent/memory-prompt.ts +0 -1
  20. package/src/agent/note-registry.ts +329 -0
  21. package/src/agent/operator-actions.ts +343 -0
  22. package/src/agent/persona-discovery.ts +1 -3
  23. package/src/agent/persona-registry.ts +19 -16
  24. package/src/agent/record-labels.ts +107 -0
  25. package/src/agent/reminder-schedule-format.ts +33 -24
  26. package/src/agent/reminder-schedule.ts +27 -26
  27. package/src/agent/routine-registry.ts +54 -14
  28. package/src/agent/routine-schedule-args.ts +2 -1
  29. package/src/agent/routine-schedule-format.ts +77 -53
  30. package/src/agent/routine-schedule-promotion.ts +35 -33
  31. package/src/agent/routine-schedule-receipts.ts +29 -27
  32. package/src/agent/runtime-profile-starters.ts +5 -5
  33. package/src/agent/runtime-profile.ts +91 -12
  34. package/src/agent/skill-registry.ts +210 -32
  35. package/src/cli/agent-knowledge-args.ts +5 -1
  36. package/src/cli/agent-knowledge-command.ts +40 -34
  37. package/src/cli/agent-knowledge-format.ts +80 -67
  38. package/src/cli/agent-knowledge-methods.ts +1 -1
  39. package/src/cli/agent-knowledge-runtime.ts +32 -26
  40. package/src/cli/bundle-command.ts +14 -9
  41. package/src/cli/config-overrides.ts +37 -36
  42. package/src/cli/entrypoint.ts +16 -7
  43. package/src/cli/external-runtime.ts +10 -4
  44. package/src/cli/help.ts +54 -29
  45. package/src/cli/local-library-command.ts +203 -83
  46. package/src/cli/management-commands.ts +105 -68
  47. package/src/cli/management.ts +55 -74
  48. package/src/cli/memory-command.ts +66 -32
  49. package/src/cli/parser.ts +37 -3
  50. package/src/cli/profiles-command.ts +166 -31
  51. package/src/cli/provider-auth-routes.ts +2 -1
  52. package/src/cli/routines-command.ts +176 -40
  53. package/src/cli/service-posture.ts +16 -16
  54. package/src/cli/status.ts +56 -135
  55. package/src/core/conversation-message-snapshot.ts +131 -0
  56. package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
  57. package/src/input/agent-workspace-access-command-editors.ts +170 -0
  58. package/src/input/agent-workspace-activation.ts +35 -8
  59. package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
  60. package/src/input/agent-workspace-basic-command-editors.ts +502 -9
  61. package/src/input/agent-workspace-categories.ts +423 -51
  62. package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
  63. package/src/input/agent-workspace-channel-command-editors.ts +65 -0
  64. package/src/input/agent-workspace-channels.ts +35 -2
  65. package/src/input/agent-workspace-command-editor.ts +41 -3
  66. package/src/input/agent-workspace-config-reader.ts +16 -0
  67. package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
  68. package/src/input/agent-workspace-editors.ts +166 -1
  69. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
  70. package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
  71. package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
  72. package/src/input/agent-workspace-learned-behavior.ts +67 -0
  73. package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
  74. package/src/input/agent-workspace-library-command-editors.ts +35 -0
  75. package/src/input/agent-workspace-local-operations.ts +218 -0
  76. package/src/input/agent-workspace-local-selection.ts +75 -0
  77. package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
  78. package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
  79. package/src/input/agent-workspace-media-command-editors.ts +27 -0
  80. package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
  81. package/src/input/agent-workspace-memory-command-editors.ts +180 -0
  82. package/src/input/agent-workspace-memory-editor.ts +2 -2
  83. package/src/input/agent-workspace-navigation.ts +81 -0
  84. package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
  85. package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
  86. package/src/input/agent-workspace-operations-command-editors.ts +196 -0
  87. package/src/input/agent-workspace-panel-route.ts +43 -0
  88. package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
  89. package/src/input/agent-workspace-provider-command-editors.ts +93 -0
  90. package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
  91. package/src/input/agent-workspace-requirements.ts +11 -0
  92. package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
  93. package/src/input/agent-workspace-search.ts +169 -0
  94. package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
  95. package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
  96. package/src/input/agent-workspace-session-command-editors.ts +249 -0
  97. package/src/input/agent-workspace-setup.ts +37 -16
  98. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
  99. package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
  100. package/src/input/agent-workspace-snapshot.ts +162 -21
  101. package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
  102. package/src/input/agent-workspace-task-command-editors.ts +47 -0
  103. package/src/input/agent-workspace-token.ts +18 -2
  104. package/src/input/agent-workspace-types.ts +212 -4
  105. package/src/input/agent-workspace-voice-media.ts +4 -7
  106. package/src/input/agent-workspace-web-research-editor.ts +104 -0
  107. package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
  108. package/src/input/agent-workspace.ts +178 -240
  109. package/src/input/command-registry.ts +5 -2
  110. package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
  111. package/src/input/commands/agent-skills-runtime.ts +149 -72
  112. package/src/input/commands/agent-workspace-runtime.ts +23 -6
  113. package/src/input/commands/brief-runtime.ts +55 -25
  114. package/src/input/commands/channels-runtime.ts +147 -33
  115. package/src/input/commands/compat-runtime.ts +32 -0
  116. package/src/input/commands/delegation-runtime.ts +29 -23
  117. package/src/input/commands/experience-runtime.ts +6 -4
  118. package/src/input/commands/guidance-runtime.ts +4 -4
  119. package/src/input/commands/health-runtime.ts +149 -126
  120. package/src/input/commands/knowledge.ts +82 -60
  121. package/src/input/commands/local-provider-runtime.ts +43 -21
  122. package/src/input/commands/local-runtime.ts +138 -16
  123. package/src/input/commands/local-setup-review.ts +10 -20
  124. package/src/input/commands/mcp-runtime.ts +63 -46
  125. package/src/input/commands/notify-runtime.ts +38 -9
  126. package/src/input/commands/operator-actions-runtime.ts +138 -0
  127. package/src/input/commands/operator-runtime.ts +6 -76
  128. package/src/input/commands/personas-runtime.ts +46 -31
  129. package/src/input/commands/planning-runtime.ts +1 -1
  130. package/src/input/commands/platform-access-runtime.ts +35 -98
  131. package/src/input/commands/product-runtime.ts +2 -2
  132. package/src/input/commands/provider-accounts-runtime.ts +60 -39
  133. package/src/input/commands/qrcode-runtime.ts +50 -9
  134. package/src/input/commands/recall-bundle.ts +11 -11
  135. package/src/input/commands/recall-capture.ts +13 -11
  136. package/src/input/commands/recall-query.ts +29 -21
  137. package/src/input/commands/recall-review.ts +2 -1
  138. package/src/input/commands/routines-runtime.ts +99 -45
  139. package/src/input/commands/runtime-services.ts +1 -14
  140. package/src/input/commands/schedule-runtime.ts +33 -20
  141. package/src/input/commands/security-runtime.ts +6 -11
  142. package/src/input/commands/session-content.ts +80 -78
  143. package/src/input/commands/session-workflow.ts +134 -104
  144. package/src/input/commands/session.ts +3 -174
  145. package/src/input/commands/shell-core.ts +38 -17
  146. package/src/input/commands/subscription-runtime.ts +54 -185
  147. package/src/input/commands/support-bundle-runtime.ts +53 -0
  148. package/src/input/commands/tasks-runtime.ts +21 -21
  149. package/src/input/commands/work-plan-runtime.ts +33 -8
  150. package/src/input/commands.ts +6 -2
  151. package/src/input/feed-context-factory.ts +2 -2
  152. package/src/input/file-picker.ts +3 -2
  153. package/src/input/handler-command-route.ts +4 -7
  154. package/src/input/handler-content-actions.ts +89 -26
  155. package/src/input/handler-feed-routes.ts +19 -12
  156. package/src/input/handler-feed.ts +6 -5
  157. package/src/input/handler-interactions.ts +9 -36
  158. package/src/input/handler-modal-stack.ts +3 -3
  159. package/src/input/handler-onboarding.ts +52 -91
  160. package/src/input/handler-shortcuts.ts +16 -7
  161. package/src/input/handler.ts +50 -23
  162. package/src/input/input-history.ts +5 -6
  163. package/src/input/keybindings.ts +27 -16
  164. package/src/input/mcp-workspace.ts +90 -26
  165. package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
  166. package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
  167. package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
  168. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  169. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
  170. package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
  171. package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
  172. package/src/input/onboarding/onboarding-wizard.ts +87 -2
  173. package/src/input/profile-picker-modal.ts +31 -12
  174. package/src/input/session-picker-modal.ts +21 -4
  175. package/src/input/settings-modal-agent-policy.ts +6 -6
  176. package/src/input/settings-modal-behavior.ts +0 -3
  177. package/src/input/settings-modal-subscriptions.ts +3 -3
  178. package/src/input/settings-modal-types.ts +2 -13
  179. package/src/input/settings-modal.ts +20 -66
  180. package/src/input/submission-intent.ts +0 -1
  181. package/src/input/submission-router.ts +3 -3
  182. package/src/main.ts +18 -10
  183. package/src/panels/approval-panel.ts +8 -8
  184. package/src/panels/automation-control-panel.ts +4 -4
  185. package/src/panels/base-panel.ts +1 -1
  186. package/src/panels/builtin/agent.ts +1 -1
  187. package/src/panels/builtin/operations.ts +3 -12
  188. package/src/panels/builtin/session.ts +32 -24
  189. package/src/panels/builtin/shared.ts +7 -7
  190. package/src/panels/cost-tracker-panel.ts +1 -1
  191. package/src/panels/docs-panel.ts +5 -3
  192. package/src/panels/index.ts +0 -1
  193. package/src/panels/knowledge-panel.ts +6 -5
  194. package/src/panels/memory-panel.ts +7 -6
  195. package/src/panels/panel-list-panel.ts +36 -80
  196. package/src/panels/project-planning-panel.ts +19 -12
  197. package/src/panels/provider-account-snapshot.ts +51 -25
  198. package/src/panels/provider-accounts-panel.ts +33 -18
  199. package/src/panels/provider-health-domains.ts +48 -7
  200. package/src/panels/provider-health-panel.ts +5 -4
  201. package/src/panels/qr-panel.ts +44 -20
  202. package/src/panels/schedule-panel.ts +9 -17
  203. package/src/panels/security-panel.ts +8 -8
  204. package/src/panels/session-browser-panel.ts +10 -10
  205. package/src/panels/subscription-panel.ts +6 -6
  206. package/src/panels/system-messages-panel.ts +3 -3
  207. package/src/panels/tasks-panel.ts +21 -14
  208. package/src/panels/tool-inspector-panel.ts +19 -12
  209. package/src/panels/work-plan-panel.ts +5 -5
  210. package/src/planning/project-planning-coordinator.ts +3 -3
  211. package/src/provider-auth-route-display.ts +9 -0
  212. package/src/renderer/agent-workspace-style.ts +34 -0
  213. package/src/renderer/agent-workspace.ts +254 -57
  214. package/src/renderer/autocomplete-overlay.ts +25 -6
  215. package/src/renderer/block-actions.ts +1 -3
  216. package/src/renderer/bookmark-modal.ts +19 -4
  217. package/src/renderer/buffer.ts +4 -2
  218. package/src/renderer/context-inspector.ts +50 -13
  219. package/src/renderer/diff.ts +1 -1
  220. package/src/renderer/file-picker-overlay.ts +19 -6
  221. package/src/renderer/help-overlay.ts +112 -34
  222. package/src/renderer/history-search-overlay.ts +19 -4
  223. package/src/renderer/live-tail-modal.ts +27 -5
  224. package/src/renderer/mcp-workspace.ts +176 -43
  225. package/src/renderer/model-picker-overlay.ts +58 -3
  226. package/src/renderer/model-workspace.ts +104 -22
  227. package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
  228. package/src/renderer/process-modal.ts +27 -6
  229. package/src/renderer/profile-picker-modal.ts +21 -5
  230. package/src/renderer/search-overlay.ts +25 -5
  231. package/src/renderer/selection-modal-overlay.ts +46 -14
  232. package/src/renderer/session-picker-modal.ts +18 -1
  233. package/src/renderer/settings-modal-helpers.ts +2 -40
  234. package/src/renderer/settings-modal.ts +88 -55
  235. package/src/renderer/system-message.ts +1 -1
  236. package/src/renderer/tool-call.ts +20 -11
  237. package/src/runtime/agent-runtime-events.ts +129 -0
  238. package/src/runtime/bootstrap-command-context.ts +7 -1
  239. package/src/runtime/bootstrap-command-parts.ts +11 -8
  240. package/src/runtime/bootstrap-core.ts +23 -65
  241. package/src/runtime/bootstrap-hook-bridge.ts +7 -18
  242. package/src/runtime/bootstrap-shell.ts +46 -8
  243. package/src/runtime/bootstrap.ts +36 -46
  244. package/src/runtime/connected-host-auth.ts +47 -0
  245. package/src/runtime/diagnostics/panels/index.ts +2 -2
  246. package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
  247. package/src/runtime/diagnostics/panels/policy.ts +7 -7
  248. package/src/runtime/index.ts +2 -5
  249. package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
  250. package/src/runtime/onboarding/apply.ts +288 -74
  251. package/src/runtime/onboarding/derivation.ts +10 -13
  252. package/src/runtime/onboarding/snapshot.ts +2 -17
  253. package/src/runtime/onboarding/types.ts +34 -20
  254. package/src/runtime/onboarding/verify.ts +105 -4
  255. package/src/runtime/operator-token-cleanup.ts +3 -3
  256. package/src/runtime/services.ts +95 -11
  257. package/src/runtime/store/selectors/index.ts +3 -3
  258. package/src/runtime/store/state.ts +1 -4
  259. package/src/runtime/surface-feature-flags.ts +41 -6
  260. package/src/runtime/ui-read-models.ts +3 -4
  261. package/src/runtime/ui-services.ts +6 -6
  262. package/src/shell/blocking-input.ts +2 -1
  263. package/src/shell/service-settings-sync.ts +7 -7
  264. package/src/shell/ui-openers.ts +17 -38
  265. package/src/tools/agent-analysis-registry-policy.ts +0 -1
  266. package/src/tools/agent-channel-send-tool.ts +133 -0
  267. package/src/tools/agent-context-policy.ts +1 -1
  268. package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
  269. package/src/tools/agent-knowledge-tool.ts +170 -0
  270. package/src/tools/agent-local-registry-tool.ts +279 -73
  271. package/src/tools/agent-media-generate-tool.ts +133 -0
  272. package/src/tools/agent-notify-tool.ts +143 -0
  273. package/src/tools/agent-operator-action-tool.ts +137 -0
  274. package/src/tools/agent-operator-briefing-tool.ts +217 -0
  275. package/src/tools/agent-reminder-schedule-tool.ts +237 -0
  276. package/src/tools/agent-tool-policy-guard.ts +13 -15
  277. package/src/tools/agent-work-plan-tool.ts +256 -0
  278. package/src/version.ts +1 -1
  279. package/docs/connected-services.md +0 -51
  280. package/src/cli/package-verification.ts +0 -274
  281. package/src/input/commands/policy-dispatch.ts +0 -339
  282. package/src/input/commands/policy.ts +0 -17
  283. package/src/panels/panel-picker.ts +0 -105
  284. package/src/panels/policy-panel.ts +0 -308
  285. package/src/renderer/panel-picker-overlay.ts +0 -202
@@ -1,274 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import { existsSync, readFileSync, statSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { CommandRegistry } from '../input/command-registry.ts';
5
- import { registerBuiltinCommands } from '../input/commands.ts';
6
-
7
- export interface PackageCliBinVerification {
8
- readonly command: 'goodvibes-agent';
9
- readonly target: string;
10
- readonly exists: boolean;
11
- readonly executable: boolean;
12
- readonly usesBunShebang: boolean;
13
- readonly hasSourceEntrypoint: boolean;
14
- }
15
-
16
- export interface PackageCliVerificationReport {
17
- readonly packageName: string;
18
- readonly version: string;
19
- readonly bins: readonly PackageCliBinVerification[];
20
- readonly tarball: {
21
- readonly entryCount: number;
22
- readonly unpackedSize: number;
23
- readonly requiredPathsPresent: readonly string[];
24
- readonly forbiddenPaths: readonly string[];
25
- };
26
- readonly packageFacingText: {
27
- readonly checkedPaths: readonly string[];
28
- readonly failures: readonly string[];
29
- };
30
- readonly issues: readonly string[];
31
- }
32
-
33
- const REQUIRED_BIN_COMMANDS = ['goodvibes-agent'] as const;
34
- const REQUIRED_TARBALL_PATHS = [
35
- 'README.md',
36
- 'CHANGELOG.md',
37
- 'LICENSE',
38
- 'package.json',
39
- 'src/main.ts',
40
- 'dist/package/main.js',
41
- 'bin/goodvibes-agent.ts',
42
- 'tsconfig.json',
43
- 'docs/README.md',
44
- 'docs/getting-started.md',
45
- 'docs/connected-services.md',
46
- 'docs/release-and-publishing.md',
47
- ] as const;
48
- const FORBIDDEN_TARBALL_PREFIXES = ['.github/', 'src/test/', 'src/.test/', '.goodvibes/', 'vendor/'] as const;
49
- const FORBIDDEN_TARBALL_DOCS = [
50
- ['docs/cloud', 'flare-batch.md'].join(''),
51
- ['docs/home', 'assistant-surface.md'].join(''),
52
- 'docs/wrfc/',
53
- ] as const;
54
- const FORBIDDEN_TARBALL_FILES = new Set([
55
- 'src/panels/agent-inspector-panel.ts',
56
- 'src/panels/agent-inspector-shared.ts',
57
- 'src/panels/agent-logs-panel.ts',
58
- 'src/panels/agent-logs-shared.ts',
59
- 'src/tools/wrfc-agent-guard.ts',
60
- 'src/renderer/agent-detail-modal.ts',
61
- 'src/renderer/process-summary.ts',
62
- ]);
63
- const PACKAGE_FACING_TEXT_PATHS = [
64
- 'README.md',
65
- 'CHANGELOG.md',
66
- 'docs/README.md',
67
- 'docs/getting-started.md',
68
- 'docs/connected-services.md',
69
- 'docs/release-and-publishing.md',
70
- ] as const;
71
- const PACKAGE_FACING_FORBIDDEN_TEXT = [
72
- ['/api/', 'knowledge'].join(''),
73
- ['/api/home', 'assistant'].join(''),
74
- ['home', 'assistant.home', 'Graph'].join(''),
75
- ['include', 'AllSpaces'].join(''),
76
- ['knowledge', 'SpaceId'].join(''),
77
- ['@pellux/goodvibes-', 'tui'].join(''),
78
- ['@pellux/goodvibes-', 'daemon'].join(''),
79
- ['goodvibes-', 'daemon'].join(''),
80
- ['~/.goodvibes/', 'tui'].join(''),
81
- ['Home', ' Assistant'].join(''),
82
- ['Home', 'Graph'].join(''),
83
- ['Cloud', 'flare'].join(''),
84
- ['Open', 'Claw'].join(''),
85
- ['Her', 'mes'].join(''),
86
- ['cap', 'abilities audit'].join(''),
87
- ['cap', 'abilities command'].join(''),
88
- ['near', '-fork'].join(''),
89
- ['Optional ', 'Browser Access'].join(''),
90
- ['Optional ', 'Other-Device Access'].join(''),
91
- ['Optional ', 'Incoming Events'].join(''),
92
- ['Service ', '& Network'].join(''),
93
- ['Surfaces ', '& Integrations'].join(''),
94
- ['runtime', '-isolation'].join(''),
95
- ['goodvibes-agent', 'serve'].join(' '),
96
- ['goodvibes-agent', 'service'].join(' '),
97
- ['goodvibes-agent', 'services'].join(' '),
98
- ['goodvibes-agent', 'surfaces'].join(' '),
99
- ['goodvibes-agent', 'surface'].join(' '),
100
- ['goodvibes-agent', 'listener'].join(' '),
101
- ['goodvibes-agent', 'control-plane'].join(' '),
102
- ['goodvibes-agent', 'remote'].join(' '),
103
- ['goodvibes-agent', 'bridge'].join(' '),
104
- ['goodvibes-agent', 'web'].join(' '),
105
- ['goodvibes-agent', 'launch'].join(' '),
106
- ['goodvibes-agent', 'start'].join(' '),
107
- ['tui ', '[path]'].join(''),
108
- ['tui', '|launch'].join(''),
109
- ['tui', '|launch|start'].join(''),
110
- 'Every plan must have a multi-agent execution strategy',
111
- 'NEVER skip WRFC',
112
- 'ALWAYS work in parallel when implementing a plan',
113
- 'PRIMARY GOAL: Fully complete and functional code',
114
- 'You are a code reviewer for the WRFC',
115
- 'ReviewerReport',
116
- '"wrfcId"',
117
- ] as const;
118
- const PACKAGE_FACING_REQUIRED_TEXT: readonly {
119
- readonly path: typeof PACKAGE_FACING_TEXT_PATHS[number];
120
- readonly required: readonly string[];
121
- }[] = [
122
- { path: 'README.md', required: ['/api/goodvibes-agent/knowledge', 'bun add -g @pellux/goodvibes-agent'] },
123
- { path: 'docs/README.md', required: ['/api/goodvibes-agent/knowledge'] },
124
- { path: 'docs/getting-started.md', required: ['/api/goodvibes-agent/knowledge', 'bun add -g @pellux/goodvibes-agent'] },
125
- { path: 'docs/connected-services.md', required: ['/api/goodvibes-agent/knowledge'] },
126
- { path: 'docs/release-and-publishing.md', required: ['/api/goodvibes-agent/knowledge', 'bun add -g @pellux/goodvibes-agent'] },
127
- ];
128
- const NON_COMMAND_ROUTE_ROOTS = new Set(['api', 'status']);
129
-
130
- function readPackageJson(root: string): Record<string, unknown> {
131
- return JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')) as Record<string, unknown>;
132
- }
133
-
134
- function buildRegisteredSlashCommandNames(): ReadonlySet<string> {
135
- const registry = new CommandRegistry();
136
- registerBuiltinCommands(registry);
137
- const names = new Set<string>();
138
- for (const command of registry.list()) {
139
- names.add(command.name);
140
- for (const alias of command.aliases ?? []) names.add(alias);
141
- }
142
- return names;
143
- }
144
-
145
- function verifyPackageFacingSlashCommands(path: string, content: string, registeredCommands: ReadonlySet<string>): readonly string[] {
146
- const failures: string[] = [];
147
- const commandPattern = /(^|[\s`([])\/([a-z][a-z0-9_-]*)(?=$|[\s`.,;:)\]])/g;
148
- const lines = content.split(/\r?\n/);
149
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
150
- const line = lines[lineIndex] ?? '';
151
- commandPattern.lastIndex = 0;
152
- for (let match = commandPattern.exec(line); match !== null; match = commandPattern.exec(line)) {
153
- const root = match[2] ?? '';
154
- if (NON_COMMAND_ROUTE_ROOTS.has(root)) continue;
155
- if (registeredCommands.has(root)) continue;
156
- failures.push(`package-facing text ${path}:${lineIndex + 1} references unknown Agent slash command: /${root}`);
157
- }
158
- }
159
- return failures;
160
- }
161
-
162
- function hasExecutableBit(path: string): boolean {
163
- return existsSync(path) && (statSync(path).mode & 0o111) !== 0;
164
- }
165
-
166
- function verifyBin(root: string, command: typeof REQUIRED_BIN_COMMANDS[number], target: string | undefined): PackageCliBinVerification {
167
- const binPath = target ? join(root, target) : '';
168
- const source = target && existsSync(binPath) ? readFileSync(binPath, 'utf-8') : '';
169
- return {
170
- command,
171
- target: target ?? '',
172
- exists: Boolean(target) && existsSync(binPath),
173
- executable: Boolean(target) && hasExecutableBit(binPath),
174
- usesBunShebang: source.startsWith('#!/usr/bin/env bun'),
175
- hasSourceEntrypoint: source.includes('dist') && source.includes('package') && source.includes('main.js'),
176
- };
177
- }
178
-
179
- function registryPackDryRun(root: string): { readonly files: readonly string[]; readonly entryCount: number; readonly unpackedSize: number } {
180
- execSync('bun run build:package-runtime', {
181
- cwd: root,
182
- stdio: 'inherit',
183
- });
184
- const raw = execSync('npm pack --json --dry-run', {
185
- cwd: root,
186
- encoding: 'utf-8',
187
- stdio: ['ignore', 'pipe', 'inherit'],
188
- });
189
- const [packResult] = JSON.parse(raw) as Array<{ files?: Array<{ path?: string }>; entryCount?: number; unpackedSize?: number }>;
190
- return {
191
- files: Array.isArray(packResult?.files) ? packResult.files.map((entry) => String(entry.path ?? '')) : [],
192
- entryCount: Number(packResult?.entryCount ?? 0),
193
- unpackedSize: Number(packResult?.unpackedSize ?? 0),
194
- };
195
- }
196
-
197
- export function verifyPackageFacingText(root: string): { readonly checkedPaths: readonly string[]; readonly failures: readonly string[] } {
198
- const failures: string[] = [];
199
- const registeredCommands = buildRegisteredSlashCommandNames();
200
- for (const path of PACKAGE_FACING_TEXT_PATHS) {
201
- const absolutePath = join(root, path);
202
- if (!existsSync(absolutePath)) {
203
- failures.push(`package-facing text is missing: ${path}`);
204
- continue;
205
- }
206
- const content = readFileSync(absolutePath, 'utf-8');
207
- if (path !== 'CHANGELOG.md') {
208
- failures.push(...verifyPackageFacingSlashCommands(path, content, registeredCommands));
209
- }
210
- for (const forbidden of PACKAGE_FACING_FORBIDDEN_TEXT) {
211
- if (content.includes(forbidden)) {
212
- failures.push(`package-facing text ${path} contains forbidden default/TUI route or policy: ${forbidden}`);
213
- }
214
- }
215
- const requirement = PACKAGE_FACING_REQUIRED_TEXT.find((entry) => entry.path === path);
216
- if (requirement) {
217
- for (const required of requirement.required) {
218
- if (!content.includes(required)) {
219
- failures.push(`package-facing text ${path} is missing required Agent route/policy text: ${required}`);
220
- }
221
- }
222
- }
223
- }
224
- return {
225
- checkedPaths: PACKAGE_FACING_TEXT_PATHS,
226
- failures,
227
- };
228
- }
229
-
230
- export function verifyPackageCliInstall(root: string): PackageCliVerificationReport {
231
- const pkg = readPackageJson(root);
232
- const bin = pkg.bin && typeof pkg.bin === 'object' ? pkg.bin as Record<string, string | undefined> : {};
233
- const bins = REQUIRED_BIN_COMMANDS.map((command) => verifyBin(root, command, bin[command]));
234
- const pack = registryPackDryRun(root);
235
- const requiredPathsPresent = REQUIRED_TARBALL_PATHS.filter((path) => pack.files.includes(path));
236
- const forbiddenPaths = pack.files.filter((path) => {
237
- if (FORBIDDEN_TARBALL_PREFIXES.some((prefix) => path.startsWith(prefix))) return true;
238
- if (FORBIDDEN_TARBALL_FILES.has(path)) return true;
239
- return FORBIDDEN_TARBALL_DOCS.some((docPath) => path === docPath || path.startsWith(docPath));
240
- });
241
- const issues: string[] = [];
242
- const packageFacingText = verifyPackageFacingText(root);
243
-
244
- for (const item of bins) {
245
- if (!item.target) issues.push(`package.json bin is missing ${item.command}.`);
246
- if (!item.exists) issues.push(`bin target does not exist: ${item.command} -> ${item.target}`);
247
- if (!item.executable) issues.push(`bin target is not executable: ${item.command} -> ${item.target}`);
248
- if (!item.usesBunShebang) issues.push(`bin target does not use Bun shebang: ${item.command} -> ${item.target}`);
249
- if (!item.hasSourceEntrypoint) issues.push(`bin target does not load the packaged Agent runtime: ${item.command}`);
250
- }
251
- for (const path of REQUIRED_TARBALL_PATHS) {
252
- if (!pack.files.includes(path)) issues.push(`registry tarball missing required path: ${path}`);
253
- }
254
- for (const path of forbiddenPaths) {
255
- issues.push(`registry tarball includes forbidden path: ${path}`);
256
- }
257
- for (const failure of packageFacingText.failures) {
258
- issues.push(failure);
259
- }
260
-
261
- return {
262
- packageName: String(pkg.name ?? ''),
263
- version: String(pkg.version ?? ''),
264
- bins,
265
- tarball: {
266
- entryCount: pack.entryCount,
267
- unpackedSize: pack.unpackedSize,
268
- requiredPathsPresent,
269
- forbiddenPaths,
270
- },
271
- packageFacingText,
272
- issues,
273
- };
274
- }
@@ -1,339 +0,0 @@
1
- import type { CommandContext } from '../command-registry.ts';
2
- import {
3
- buildPolicyPreflightReview,
4
- createPermissionSimulator,
5
- lintPolicyConfig,
6
- runPolicySimulationScenarios,
7
- } from '@/runtime/index.ts';
8
- import { DivergenceDashboard } from '@/runtime/index.ts';
9
- import type { PolicyRuntimeState } from '@/runtime/index.ts';
10
- import { createUnsignedBundle } from '@/runtime/index.ts';
11
- import type { PolicyBundlePayload } from '@/runtime/index.ts';
12
- import type { PolicyRule, PermissionsConfig, DivergenceStats } from '@/runtime/index.ts';
13
- import { requireShellPaths } from './runtime-services.ts';
14
-
15
- function getPolicyState(ctx?: CommandContext): PolicyRuntimeState {
16
- const policyRuntimeState = ctx?.extensions.policyRuntimeState;
17
- if (!policyRuntimeState) {
18
- throw new Error('Policy state is not available in this Agent session.');
19
- }
20
- return policyRuntimeState;
21
- }
22
-
23
- function getRegistry(ctx?: CommandContext) {
24
- return ctx?.extensions.policyRegistry ?? getPolicyState(ctx).getRegistry();
25
- }
26
-
27
- function fmtRate(rate: number): string {
28
- return `${(rate * 100).toFixed(1)}%`;
29
- }
30
-
31
- function bundleSummary(
32
- label: string,
33
- version: { bundle: { bundleId: string; issuedAt: string; issuer?: string }; state: string; rules: PolicyRule[] },
34
- ): string {
35
- const issuer = version.bundle.issuer ? ` issuer=${version.bundle.issuer}` : '';
36
- const ts = version.bundle.issuedAt.replace('T', ' ').slice(0, 19);
37
- return `${label}: ${version.bundle.bundleId} (${ts}${issuer}) [${version.rules.length} rules, ${version.state}]`;
38
- }
39
-
40
- async function handleLoad(args: string[], context: CommandContext): Promise<void> {
41
- const registry = getRegistry(context);
42
- const policyState = getPolicyState(context);
43
- const bundleId = args[0] ?? `policy-candidate-${Date.now()}`;
44
- const ruleCount = Math.max(0, parseInt(args[1] ?? '0', 10));
45
- const rules: PolicyRule[] = [];
46
- for (let i = 0; i < ruleCount; i++) {
47
- rules.push({
48
- type: 'prefix',
49
- id: `${bundleId}-rule-${i}`,
50
- description: `Demo rule ${i}`,
51
- origin: 'user',
52
- effect: 'allow',
53
- toolPattern: '*',
54
- commandPrefixes: [],
55
- });
56
- }
57
- const payload: PolicyBundlePayload = {
58
- version: 1,
59
- rules,
60
- description: `Loaded via /policy load at ${new Date().toISOString()}`,
61
- };
62
- const bundle = createUnsignedBundle(bundleId, payload);
63
- const result = registry.loadCandidate(bundle);
64
- if (!result.ok) {
65
- context.print(`[policy] Load failed: ${result.error ?? 'unknown error'}`);
66
- return;
67
- }
68
- const candidate = registry.getCandidate();
69
- if (candidate) {
70
- policyState.notify();
71
- context.print(bundleSummary('[policy] Candidate loaded', candidate));
72
- context.print('[policy] Next: run `/policy simulate` to collect divergence evidence before promoting.');
73
- }
74
- }
75
-
76
- async function handleSimulate(args: string[], context: CommandContext): Promise<void> {
77
- const registry = getRegistry(context);
78
- const policyState = getPolicyState(context);
79
- const projectRoot = requireShellPaths(context).workingDirectory;
80
- const candidate = registry.getCandidate();
81
- const current = registry.getCurrent();
82
- if (!candidate) {
83
- context.print('[policy] No candidate bundle loaded. Run `/policy load <bundle-id>` first.');
84
- return;
85
- }
86
- if (!registry.markSimulating()) {
87
- context.print(`[policy] Cannot start simulation: candidate is in "${candidate.state}" state. Only a freshly loaded ("loaded") candidate can be simulated.`);
88
- return;
89
- }
90
- const currentConfig: PermissionsConfig = {
91
- mode: 'default',
92
- projectRoot,
93
- rules: current?.rules ?? [],
94
- defaultEffect: 'allow',
95
- };
96
- const candidateForSim = registry.getCandidate();
97
- if (!candidateForSim) {
98
- throw new Error('Invariant: candidate disappeared after markSimulating()');
99
- }
100
- const candidateConfig: PermissionsConfig = {
101
- mode: 'default',
102
- projectRoot,
103
- rules: candidateForSim.rules,
104
- defaultEffect: 'allow',
105
- };
106
- const modeArg = args[0];
107
- const simulationMode = modeArg === 'enforce' ? 'enforce' : modeArg === 'silent' ? 'simulation-only' : 'warn-on-divergence';
108
- const simulator = createPermissionSimulator(currentConfig, candidateConfig, simulationMode, {});
109
- const dashboard = new DivergenceDashboard(simulator, simulationMode, { threshold: 0.05 });
110
- policyState.setDashboard(dashboard);
111
- context.print(`[policy] Simulation started in "${simulationMode}" mode. Evaluations will be tracked against candidate bundle "${candidate.bundle.bundleId}".`);
112
- context.print('[policy] Use `/policy diff` to compare rules. When ready, run `/policy promote` to enforce (requires gate passing).');
113
- const report = simulator.getDivergenceReport();
114
- const gateResult = dashboard.checkEnforceGate();
115
- const scenarioSummary = runPolicySimulationScenarios(simulator);
116
- registry.attachSimulationReport(report, gateResult);
117
- policyState.recordSimulationSummary(scenarioSummary);
118
- const candidate2 = registry.getCandidate();
119
- policyState.notify();
120
- context.print(`[policy] Baseline divergence: ${fmtRate(report.overall.divergenceRate)} (${report.overall.total} divergences / ${report.overall.totalEvaluations} evaluations). Gate: ${gateResult.status}.`);
121
- context.print(`[policy] Scenario run: ${scenarioSummary.totalScenarios} samples, ${scenarioSummary.divergentScenarios} diverged, ${scenarioSummary.allowedByActual}/${scenarioSummary.allowedBySimulated} allowed (actual/candidate).`);
122
- if (candidate2) context.print(bundleSummary('[policy] Candidate state', candidate2));
123
- }
124
-
125
- async function handleDiff(_args: string[], context: CommandContext): Promise<void> {
126
- const registry = getRegistry(context);
127
- const diff = registry.diff();
128
- if (!diff) {
129
- const current = registry.getCurrent();
130
- const candidate = registry.getCandidate();
131
- if (!current) context.print('[policy] No active bundle. Load a bundle with `/policy load`.');
132
- else if (!candidate) context.print('[policy] No candidate bundle. Load a candidate with `/policy load`.');
133
- else context.print('[policy] Diff unavailable.');
134
- return;
135
- }
136
- context.print(`[policy] Diff: ${diff.fromBundleId} → ${diff.toBundleId} (${diff.totalChanges} change${diff.totalChanges !== 1 ? 's' : ''})`);
137
- context.print(` Added: ${diff.added.length} rule${diff.added.length !== 1 ? 's' : ''}`);
138
- context.print(` Removed: ${diff.removed.length} rule${diff.removed.length !== 1 ? 's' : ''}`);
139
- context.print(` Changed: ${diff.changed.length} rule${diff.changed.length !== 1 ? 's' : ''}`);
140
- context.print(` Unchanged: ${diff.unchanged.length} rule${diff.unchanged.length !== 1 ? 's' : ''}`);
141
- if (diff.added.length > 0) {
142
- context.print(' [+] Added rules:');
143
- for (const r of diff.added) context.print(` + ${r.id} (${r.type}, effect=${r.effect})`);
144
- }
145
- if (diff.removed.length > 0) {
146
- context.print(' [-] Removed rules:');
147
- for (const r of diff.removed) context.print(` - ${r.id} (${r.type}, effect=${r.effect})`);
148
- }
149
- if (diff.changed.length > 0) {
150
- context.print(' [~] Changed rules:');
151
- for (const c of diff.changed) context.print(` ~ ${c.ruleId}`);
152
- }
153
- const dashboard = getPolicyState(context).getDashboard();
154
- if (dashboard) {
155
- const snap = dashboard.getSnapshot();
156
- const report = snap.report;
157
- const prefixEntries = Object.entries(report.byCommandPrefix);
158
- if (prefixEntries.length > 0) {
159
- context.print(' [divergence by command prefix]');
160
- for (const [prefix, stats] of prefixEntries) {
161
- context.print(` ${prefix}: ${fmtRate(stats.divergenceRate)} (${stats.total}/${stats.totalEvaluations})`);
162
- }
163
- }
164
- const classEntries = Object.entries(report.byToolClass) as Array<[string, DivergenceStats]>;
165
- if (classEntries.length > 0) {
166
- context.print(' [divergence by tool class]');
167
- for (const [cls, stats] of classEntries) {
168
- if (!stats) continue;
169
- context.print(` ${cls}: ${fmtRate(stats.divergenceRate)} (${stats.total}/${stats.totalEvaluations})`);
170
- }
171
- }
172
- }
173
- }
174
-
175
- async function handleLint(_args: string[], context: CommandContext): Promise<void> {
176
- const registry = getRegistry(context);
177
- const current = registry.getCurrent();
178
- const candidate = registry.getCandidate();
179
- if (!current && !candidate) {
180
- context.print('[policy] No policy bundles loaded. Use `/policy load` to begin.');
181
- return;
182
- }
183
- const findings = [
184
- ...(current ? lintPolicyConfig({ mode: 'custom', rules: current.rules }).map((finding) => ({ scope: 'current', ...finding })) : []),
185
- ...(candidate ? lintPolicyConfig({ mode: 'custom', rules: candidate.rules }).map((finding) => ({ scope: 'candidate', ...finding })) : []),
186
- ];
187
- if (findings.length === 0) {
188
- context.print('[policy] No lint findings for the active or candidate bundles.');
189
- return;
190
- }
191
- context.print(`[policy] Lint findings (${findings.length}):`);
192
- for (const finding of findings) {
193
- context.print(` [${finding.scope}] ${finding.severity.toUpperCase()} ${finding.ruleId ? `${finding.ruleId}: ` : ''}${finding.message}`);
194
- }
195
- }
196
-
197
- async function handlePreflight(_args: string[], context: CommandContext): Promise<void> {
198
- const registry = getRegistry(context);
199
- const policyState = getPolicyState(context);
200
- const current = registry.getCurrent();
201
- const candidate = registry.getCandidate();
202
- const lintFindings = [
203
- ...(current ? lintPolicyConfig({ mode: 'custom', rules: current.rules }) : []),
204
- ...(candidate ? lintPolicyConfig({ mode: 'custom', rules: candidate.rules }) : []),
205
- ];
206
- const review = buildPolicyPreflightReview({
207
- config: context.platform.config,
208
- lintFindings,
209
- mcpServers: context.extensions.mcpRegistry.listServerSecurity().map((server) => ({
210
- serverName: server.name,
211
- trustMode: server.trustMode,
212
- role: server.role,
213
- allowedPaths: server.allowedPaths,
214
- allowedHosts: server.allowedHosts,
215
- })),
216
- });
217
- policyState.recordPreflightReview(review);
218
- context.print(`[policy] Preflight review: ${review.status.toUpperCase()} (${review.issueCount} issue${review.issueCount === 1 ? '' : 's'})`);
219
- context.print(`[policy] ${review.summary}`);
220
- for (const issue of review.issues.slice(0, 8)) {
221
- const subject = issue.serverName ? ` ${issue.serverName}` : '';
222
- const detail = issue.detail ? ` — ${issue.detail}` : '';
223
- context.print(` [${issue.severity.toUpperCase()}] ${issue.source}${subject}: ${issue.message}${detail}`);
224
- }
225
- }
226
-
227
- async function handlePromote(args: string[], context: CommandContext): Promise<void> {
228
- const registry = getRegistry(context);
229
- const policyState = getPolicyState(context);
230
- const force = args.includes('--force');
231
- if (force) {
232
- context.print('[policy] WARNING: --force bypasses the divergence gate. This is not safe for production use.');
233
- }
234
- const result = registry.promote(force);
235
- if (!result.ok) {
236
- context.print(`[policy] Promotion blocked: ${result.error}`);
237
- if (result.gate) {
238
- context.print(`[policy] Gate: ${result.gate.status} — divergence rate ${result.gate.divergenceRate !== undefined ? fmtRate(result.gate.divergenceRate) : 'unknown'} (threshold ${fmtRate(result.gate.threshold)}, ${result.gate.totalEvaluations} evaluations).`);
239
- }
240
- return;
241
- }
242
- const current = registry.getCurrent();
243
- context.print(`[policy] Promoted: "${result.bundleId}" is now the active enforcement policy.`);
244
- if (result.gate) {
245
- context.print(`[policy] Gate at promotion: ${result.gate.status} — divergence rate ${result.gate.divergenceRate !== undefined ? fmtRate(result.gate.divergenceRate) : 'unknown'} (threshold ${fmtRate(result.gate.threshold)}).`);
246
- }
247
- if (current) context.print(bundleSummary('[policy] Active bundle', current));
248
- policyState.notify();
249
- }
250
-
251
- async function handleRollback(_args: string[], context: CommandContext): Promise<void> {
252
- const registry = getRegistry(context);
253
- const policyState = getPolicyState(context);
254
- const result = registry.rollback();
255
- if (!result.ok) {
256
- context.print(`[policy] Rollback failed: ${result.error}`);
257
- return;
258
- }
259
- const current = registry.getCurrent();
260
- context.print(`[policy] Rolled back to bundle "${result.restoredBundleId}".`);
261
- if (current) context.print(bundleSummary('[policy] Active bundle', current));
262
- policyState.setDashboard(null);
263
- policyState.notify();
264
- context.print('[policy] Simulation dashboard cleared. Run `/policy simulate` for the restored bundle.');
265
- }
266
-
267
- async function handleStatus(_args: string[], context: CommandContext): Promise<void> {
268
- const registry = getRegistry(context);
269
- const policyState = getPolicyState(context);
270
- const current = registry.getCurrent();
271
- const candidate = registry.getCandidate();
272
- const history = registry.getHistory();
273
- if (!current && !candidate) {
274
- context.print('[policy] No policy bundles loaded. Use `/policy load` to begin.');
275
- return;
276
- }
277
- if (current) context.print(bundleSummary('[policy] Current (enforced)', current));
278
- if (candidate) context.print(bundleSummary('[policy] Candidate', candidate));
279
- context.print(`[policy] History: ${history.length} previous bundle(s).`);
280
- const dashboard = policyState.getDashboard();
281
- if (dashboard) {
282
- const snap = dashboard.getSnapshot();
283
- context.print(`[policy] Divergence gate: ${snap.gate.status} — ${snap.gate.divergenceRate !== undefined ? fmtRate(snap.gate.divergenceRate) : fmtRate(snap.report.overall.divergenceRate)} (${snap.report.overall.totalEvaluations} evaluations).`);
284
- } else {
285
- context.print('[policy] No active simulation dashboard.');
286
- }
287
- }
288
-
289
- export function renderPolicyUsage(): string {
290
- return [
291
- 'Usage: /policy <subcommand>',
292
- ' /policy — open the policy/governance panel',
293
- ' load <bundle-id> [rule-count] — Load a candidate bundle',
294
- ' simulate [mode] — Run simulation (silent|warn|enforce)',
295
- ' diff — Show rule diff (current vs candidate)',
296
- ' lint — Lint active and candidate bundles',
297
- ' preflight — Review proactive policy and MCP risk state',
298
- ' promote [--force] — Promote candidate to enforcement',
299
- ' rollback — Restore the previous active bundle',
300
- ' status — Show current policy state',
301
- ].join('\n');
302
- }
303
-
304
- export async function dispatchPolicyCommand(args: string[], context: CommandContext): Promise<void> {
305
- const [sub, ...rest] = args;
306
- switch (sub) {
307
- case 'load':
308
- await handleLoad(rest, context);
309
- break;
310
- case 'simulate':
311
- case 'sim':
312
- await handleSimulate(rest, context);
313
- break;
314
- case 'diff':
315
- await handleDiff(rest, context);
316
- break;
317
- case 'lint':
318
- await handleLint(rest, context);
319
- break;
320
- case 'preflight':
321
- case 'pf':
322
- await handlePreflight(rest, context);
323
- break;
324
- case 'promote':
325
- await handlePromote(rest, context);
326
- break;
327
- case 'rollback':
328
- case 'rb':
329
- await handleRollback(rest, context);
330
- break;
331
- case 'status':
332
- case 'st':
333
- await handleStatus(rest, context);
334
- break;
335
- default:
336
- context.print(renderPolicyUsage());
337
- break;
338
- }
339
- }
@@ -1,17 +0,0 @@
1
- import type { SlashCommand, CommandContext } from '../command-registry.ts';
2
- import { dispatchPolicyCommand } from './policy-dispatch.ts';
3
-
4
- export const policyCommand: SlashCommand = {
5
- name: 'policy',
6
- aliases: ['pol'],
7
- description: 'Open the policy panel or manage versioned policy bundles (load, simulate, diff, promote, rollback).',
8
- usage: '<subcommand> [args]',
9
- argsHint: 'load|simulate|diff|lint|preflight|promote|rollback|status',
10
- handler: async (args: string[], context: CommandContext): Promise<void> => {
11
- if (args.length === 0 && context.openPolicyPanel) {
12
- context.openPolicyPanel();
13
- return;
14
- }
15
- await dispatchPolicyCommand(args, context);
16
- },
17
- };