@pellux/goodvibes-agent 0.1.114 → 0.1.117

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 (168) hide show
  1. package/CHANGELOG.md +41 -650
  2. package/README.md +40 -50
  3. package/dist/package/main.js +38224 -35673
  4. package/docs/README.md +5 -4
  5. package/docs/{connected-services.md → connected-host.md} +18 -10
  6. package/docs/getting-started.md +43 -42
  7. package/docs/release-and-publishing.md +6 -4
  8. package/package.json +3 -3
  9. package/src/agent/behavior-discovery-summary.ts +165 -0
  10. package/src/agent/persona-discovery.ts +1 -3
  11. package/src/agent/persona-registry.ts +5 -3
  12. package/src/agent/reminder-schedule-format.ts +6 -6
  13. package/src/agent/reminder-schedule.ts +1 -1
  14. package/src/agent/routine-registry.ts +43 -4
  15. package/src/agent/routine-schedule-format.ts +6 -6
  16. package/src/agent/routine-schedule-promotion.ts +1 -1
  17. package/src/agent/routine-schedule-receipts.ts +1 -1
  18. package/src/agent/runtime-profile-starters.ts +331 -0
  19. package/src/agent/runtime-profile.ts +228 -330
  20. package/src/agent/skill-registry.ts +190 -13
  21. package/src/cli/agent-knowledge-command.ts +2 -2
  22. package/src/cli/agent-knowledge-format.ts +2 -2
  23. package/src/cli/agent-knowledge-runtime.ts +1 -1
  24. package/src/cli/bundle-command.ts +1 -1
  25. package/src/cli/entrypoint.ts +16 -7
  26. package/src/cli/help.ts +26 -19
  27. package/src/cli/local-library-command.ts +75 -21
  28. package/src/cli/management-commands.ts +7 -6
  29. package/src/cli/management.ts +6 -51
  30. package/src/cli/parser.ts +23 -2
  31. package/src/cli/profiles-command.ts +247 -1
  32. package/src/cli/routines-command.ts +139 -11
  33. package/src/cli/service-posture.ts +11 -11
  34. package/src/cli/status.ts +21 -25
  35. package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
  36. package/src/input/agent-workspace-access-command-editors.ts +170 -0
  37. package/src/input/agent-workspace-activation.ts +2 -1
  38. package/src/input/agent-workspace-basic-command-editor-submission.ts +787 -0
  39. package/src/input/agent-workspace-basic-command-editors.ts +524 -459
  40. package/src/input/agent-workspace-categories.ts +177 -14
  41. package/src/input/agent-workspace-channel-command-editor-submission.ts +49 -0
  42. package/src/input/agent-workspace-channel-command-editors.ts +47 -0
  43. package/src/input/agent-workspace-command-editor.ts +25 -1
  44. package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
  45. package/src/input/agent-workspace-editors.ts +27 -0
  46. package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
  47. package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
  48. package/src/input/agent-workspace-learned-behavior.ts +67 -0
  49. package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
  50. package/src/input/agent-workspace-library-command-editors.ts +35 -0
  51. package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
  52. package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
  53. package/src/input/agent-workspace-memory-command-editors.ts +180 -0
  54. package/src/input/agent-workspace-navigation.ts +45 -0
  55. package/src/input/agent-workspace-notify-editor-submission.ts +107 -0
  56. package/src/input/agent-workspace-operations-command-editor-submission.ts +146 -0
  57. package/src/input/agent-workspace-operations-command-editors.ts +119 -0
  58. package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
  59. package/src/input/agent-workspace-provider-command-editors.ts +93 -0
  60. package/src/input/agent-workspace-requirements.ts +11 -0
  61. package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
  62. package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
  63. package/src/input/agent-workspace-session-command-editors.ts +249 -0
  64. package/src/input/agent-workspace-setup.ts +49 -19
  65. package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
  66. package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
  67. package/src/input/agent-workspace-snapshot.ts +56 -8
  68. package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
  69. package/src/input/agent-workspace-task-command-editors.ts +47 -0
  70. package/src/input/agent-workspace-types.ts +124 -0
  71. package/src/input/agent-workspace-voice-media.ts +1 -1
  72. package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
  73. package/src/input/agent-workspace.ts +47 -37
  74. package/src/input/command-registry.ts +1 -1
  75. package/src/input/commands/agent-runtime-profile-runtime.ts +160 -6
  76. package/src/input/commands/agent-skills-runtime.ts +76 -12
  77. package/src/input/commands/agent-workspace-runtime.ts +4 -3
  78. package/src/input/commands/brief-runtime.ts +38 -9
  79. package/src/input/commands/channels-runtime.ts +3 -3
  80. package/src/input/commands/compat-runtime.ts +32 -0
  81. package/src/input/commands/health-runtime.ts +22 -24
  82. package/src/input/commands/knowledge.ts +26 -5
  83. package/src/input/commands/local-provider-runtime.ts +7 -3
  84. package/src/input/commands/local-setup-review.ts +4 -14
  85. package/src/input/commands/mcp-runtime.ts +8 -12
  86. package/src/input/commands/operator-runtime.ts +8 -67
  87. package/src/input/commands/personas-runtime.ts +1 -1
  88. package/src/input/commands/platform-access-runtime.ts +7 -70
  89. package/src/input/commands/policy-dispatch.ts +1 -1
  90. package/src/input/commands/policy.ts +1 -5
  91. package/src/input/commands/product-runtime.ts +2 -2
  92. package/src/input/commands/qrcode-runtime.ts +6 -4
  93. package/src/input/commands/routines-runtime.ts +44 -6
  94. package/src/input/commands/runtime-services.ts +1 -14
  95. package/src/input/commands/security-runtime.ts +3 -8
  96. package/src/input/commands/session-content.ts +1 -1
  97. package/src/input/commands/session-workflow.ts +5 -14
  98. package/src/input/commands/shell-core.ts +8 -2
  99. package/src/input/commands/subscription-runtime.ts +1 -6
  100. package/src/input/commands/support-bundle-runtime.ts +53 -0
  101. package/src/input/commands/tasks-runtime.ts +2 -2
  102. package/src/input/commands.ts +4 -0
  103. package/src/input/feed-context-factory.ts +0 -1
  104. package/src/input/handler-content-actions.ts +0 -25
  105. package/src/input/handler-feed.ts +0 -2
  106. package/src/input/handler-interactions.ts +2 -31
  107. package/src/input/handler-onboarding.ts +29 -12
  108. package/src/input/handler-shortcuts.ts +1 -3
  109. package/src/input/handler.ts +4 -7
  110. package/src/input/keybindings.ts +9 -9
  111. package/src/input/mcp-workspace.ts +90 -26
  112. package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
  113. package/src/input/onboarding/onboarding-wizard-apply.ts +49 -0
  114. package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
  115. package/src/input/onboarding/onboarding-wizard-operator-steps.ts +126 -11
  116. package/src/input/onboarding/onboarding-wizard-steps.ts +15 -8
  117. package/src/input/onboarding/onboarding-wizard-types.ts +1 -0
  118. package/src/input/onboarding/onboarding-wizard.ts +19 -2
  119. package/src/input/profile-picker-modal.ts +2 -2
  120. package/src/input/settings-modal-agent-policy.ts +6 -6
  121. package/src/input/settings-modal-subscriptions.ts +3 -3
  122. package/src/input/settings-modal.ts +14 -14
  123. package/src/main.ts +0 -2
  124. package/src/panels/automation-control-panel.ts +3 -3
  125. package/src/panels/builtin/agent.ts +1 -1
  126. package/src/panels/builtin/operations.ts +2 -2
  127. package/src/panels/builtin/session.ts +26 -18
  128. package/src/panels/builtin/shared.ts +6 -6
  129. package/src/panels/project-planning-panel.ts +1 -1
  130. package/src/panels/provider-health-domains.ts +3 -3
  131. package/src/panels/qr-panel.ts +44 -20
  132. package/src/panels/subscription-panel.ts +3 -3
  133. package/src/panels/system-messages-panel.ts +2 -2
  134. package/src/panels/tasks-panel.ts +4 -4
  135. package/src/planning/project-planning-coordinator.ts +2 -2
  136. package/src/renderer/agent-workspace.ts +52 -5
  137. package/src/renderer/block-actions.ts +1 -3
  138. package/src/renderer/help-overlay.ts +13 -8
  139. package/src/renderer/mcp-workspace.ts +32 -13
  140. package/src/renderer/model-picker-overlay.ts +0 -1
  141. package/src/renderer/model-workspace.ts +0 -2
  142. package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
  143. package/src/renderer/panel-picker-overlay.ts +1 -1
  144. package/src/renderer/profile-picker-modal.ts +2 -2
  145. package/src/renderer/settings-modal-helpers.ts +23 -23
  146. package/src/renderer/settings-modal.ts +18 -18
  147. package/src/renderer/system-message.ts +1 -1
  148. package/src/runtime/agent-runtime-events.ts +129 -0
  149. package/src/runtime/bootstrap-command-parts.ts +3 -2
  150. package/src/runtime/bootstrap-core.ts +4 -64
  151. package/src/runtime/bootstrap-shell.ts +42 -4
  152. package/src/runtime/bootstrap.ts +8 -27
  153. package/src/runtime/connected-host-auth.ts +42 -0
  154. package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
  155. package/src/runtime/index.ts +1 -5
  156. package/src/runtime/onboarding/apply.ts +213 -0
  157. package/src/runtime/onboarding/derivation.ts +25 -6
  158. package/src/runtime/onboarding/snapshot.ts +4 -2
  159. package/src/runtime/onboarding/types.ts +28 -1
  160. package/src/runtime/onboarding/verify.ts +81 -2
  161. package/src/runtime/operator-token-cleanup.ts +2 -2
  162. package/src/runtime/services.ts +27 -12
  163. package/src/shell/service-settings-sync.ts +7 -7
  164. package/src/shell/ui-openers.ts +27 -38
  165. package/src/tools/agent-local-registry-tool.ts +10 -4
  166. package/src/tools/agent-tool-policy-guard.ts +5 -7
  167. package/src/version.ts +1 -1
  168. package/src/cli/package-verification.ts +0 -274
@@ -1,7 +1,7 @@
1
1
  import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config';
2
2
  import type { CliServiceRuntime } from '../cli/service-posture.ts';
3
3
 
4
- export const AGENT_EXTERNAL_DAEMON_SERVICE_MESSAGE = 'GoodVibes Agent uses connected GoodVibes services and does not install, start, stop, restart, or uninstall them. Manage service lifecycle outside Agent.';
4
+ export const AGENT_EXTERNAL_HOST_SERVICE_MESSAGE = 'GoodVibes Agent uses a connected GoodVibes host and does not install, start, stop, restart, or uninstall it. Manage host lifecycle outside Agent.';
5
5
 
6
6
  export interface ServiceSettingsSyncChange {
7
7
  readonly key: ConfigKey;
@@ -11,13 +11,13 @@ export interface ServiceSettingsSyncChange {
11
11
 
12
12
  export interface ServiceSettingsSyncResult {
13
13
  readonly handled: boolean;
14
- readonly action?: 'external-daemon-blocked' | 'unchanged';
14
+ readonly action?: 'connected-host-blocked' | 'unchanged';
15
15
  readonly message?: string;
16
16
  readonly error?: string;
17
17
  }
18
18
 
19
19
  export interface ServiceSettingsSyncOptions {
20
- readonly allowExternalDaemonMutation?: false;
20
+ readonly allowExternalHostMutation?: false;
21
21
  }
22
22
 
23
23
  export function syncServiceSettingToPlatform(
@@ -30,15 +30,15 @@ export function syncServiceSettingToPlatform(
30
30
  return {
31
31
  handled: true,
32
32
  action: 'unchanged',
33
- message: 'External daemon service setting unchanged',
33
+ message: 'Connected-host setting unchanged',
34
34
  };
35
35
  }
36
36
 
37
37
  runtime.configManager.setDynamic(change.key, change.previousValue);
38
38
  return {
39
39
  handled: true,
40
- action: 'external-daemon-blocked',
41
- message: AGENT_EXTERNAL_DAEMON_SERVICE_MESSAGE,
42
- error: 'daemon_lifecycle_external',
40
+ action: 'connected-host-blocked',
41
+ message: AGENT_EXTERNAL_HOST_SERVICE_MESSAGE,
42
+ error: 'connected_host_lifecycle_external',
43
43
  };
44
44
  }
@@ -78,6 +78,17 @@ function buildConfiguredViaMap(
78
78
  return map;
79
79
  }
80
80
 
81
+ function agentWorkspaceCategoryForPanel(panelId: string): string {
82
+ if (panelId === 'knowledge') return 'knowledge';
83
+ if (panelId === 'memory') return 'memory';
84
+ if (panelId === 'work-plan' || panelId === 'tasks' || panelId === 'approval') return 'work';
85
+ if (panelId === 'automation' || panelId === 'schedule') return 'automation';
86
+ if (panelId === 'provider-health' || panelId === 'providers' || panelId === 'accounts' || panelId === 'subscription') return 'setup';
87
+ if (panelId === 'security' || panelId === 'policy') return 'tools';
88
+ if (panelId === 'qr-code') return 'channels';
89
+ return 'home';
90
+ }
91
+
81
92
  export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
82
93
  const {
83
94
  commandContext,
@@ -284,8 +295,8 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
284
295
  render();
285
296
  };
286
297
 
287
- commandContext.openAgentWorkspace = () => {
288
- input.openAgentWorkspace(commandContext);
298
+ commandContext.openAgentWorkspace = (categoryId?: string) => {
299
+ input.openAgentWorkspace(commandContext, categoryId);
289
300
  render();
290
301
  };
291
302
 
@@ -296,42 +307,18 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
296
307
  };
297
308
 
298
309
  commandContext.openPanelPicker = () => {
299
- if (!panelManager.isVisible()) {
300
- if (panelManager.getAllOpen().length === 0) {
301
- try {
302
- panelManager.open('panel-list');
303
- } catch {
304
- // non-fatal
305
- }
306
- }
307
- panelManager.show();
308
- input.panelFocused = true;
309
- conversation.setSplashSuppressed(true);
310
- conversation.rebuildHistory();
311
- } else if (!input.panelFocused) {
312
- if (panelManager.getAllOpen().length === 0) {
313
- try {
314
- panelManager.open('panel-list');
315
- } catch {
316
- // non-fatal
317
- }
318
- }
319
- panelManager.show();
320
- input.panelFocused = true;
321
- conversation.setSplashSuppressed(true);
322
- conversation.rebuildHistory();
323
- } else {
324
- panelManager.hide();
325
- input.panelFocused = false;
326
- conversation.setSplashSuppressed(false);
327
- conversation.rebuildHistory();
328
- }
310
+ panelManager.hide();
311
+ input.panelFocused = false;
312
+ conversation.setSplashSuppressed(false);
313
+ conversation.log('Panel picker is deferred in GoodVibes Agent. Opening the Agent operator workspace instead.', { fg: '214' });
314
+ input.openAgentWorkspace(commandContext, 'home');
315
+ conversation.rebuildHistory();
329
316
  render();
330
317
  };
331
318
 
332
319
  commandContext.focusPanels = () => {
333
- if (!panelManager.isVisible() || panelManager.getAllOpen().length === 0) return;
334
- input.panelFocused = true;
320
+ input.panelFocused = false;
321
+ input.openAgentWorkspace(commandContext, 'home');
335
322
  render();
336
323
  };
337
324
 
@@ -342,10 +329,12 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
342
329
  };
343
330
 
344
331
  commandContext.showPanel = (panelId, pane) => {
345
- panelManager.open(panelId, pane);
346
- panelManager.show();
347
- input.panelFocused = true;
348
- conversation.setSplashSuppressed(true);
332
+ void pane;
333
+ panelManager.hide();
334
+ input.panelFocused = false;
335
+ conversation.setSplashSuppressed(false);
336
+ conversation.log(`Panel "${panelId}" is not part of the Agent front door yet. Opening the matching Agent workspace.`, { fg: '214' });
337
+ input.openAgentWorkspace(commandContext, agentWorkspaceCategoryForPanel(panelId));
349
338
  conversation.rebuildHistory();
350
339
  render();
351
340
  };
@@ -137,6 +137,12 @@ function requireDescription(args: AgentLocalRegistryToolArgs): string {
137
137
  return description;
138
138
  }
139
139
 
140
+ function requireTextField(value: unknown, fieldName: string): string {
141
+ const text = readString(value);
142
+ if (!text) throw new Error(`${fieldName} is required.`);
143
+ return text;
144
+ }
145
+
140
146
  function requireSummary(args: AgentLocalRegistryToolArgs): string {
141
147
  const summary = readString(args.summary || args.description);
142
148
  if (!summary) throw new Error('summary is required.');
@@ -299,7 +305,7 @@ function handlePersona(shellPaths: ShellPathService, action: AgentLocalRegistryA
299
305
  const persona = registry.create({
300
306
  name: requireName(args),
301
307
  description: requireDescription(args),
302
- body: readString(args.body),
308
+ body: requireTextField(args.body, 'body'),
303
309
  tags: readStringList(args.tags),
304
310
  triggers: readStringList(args.triggers),
305
311
  source: 'agent',
@@ -350,7 +356,7 @@ function handleSkill(shellPaths: ShellPathService, action: AgentLocalRegistryAct
350
356
  const skill = registry.create({
351
357
  name: requireName(args),
352
358
  description: requireDescription(args),
353
- procedure: readString(args.procedure),
359
+ procedure: requireTextField(args.procedure, 'procedure'),
354
360
  triggers: readStringList(args.triggers),
355
361
  tags: readStringList(args.tags),
356
362
  enabled: args.enabled === true,
@@ -441,7 +447,7 @@ function handleRoutine(shellPaths: ShellPathService, action: AgentLocalRegistryA
441
447
  const routine = registry.create({
442
448
  name: requireName(args),
443
449
  description: requireDescription(args),
444
- steps: readString(args.steps),
450
+ steps: requireTextField(args.steps, 'steps'),
445
451
  triggers: readStringList(args.triggers),
446
452
  tags: readStringList(args.tags),
447
453
  enabled: args.enabled === true,
@@ -469,7 +475,7 @@ function handleRoutine(shellPaths: ShellPathService, action: AgentLocalRegistryA
469
475
  const routine = registry.markStarted(requireId(args));
470
476
  return [
471
477
  `Started Agent-local routine ${routine.id}: ${routine.name}`,
472
- 'Policy: same main conversation; no hidden background job, daemon mutation, or external side effect was started.',
478
+ 'Policy: same main conversation; no hidden background job, connected-host mutation, or external side effect was started.',
473
479
  '',
474
480
  routine.steps,
475
481
  ].join('\n');
@@ -83,8 +83,6 @@ const READ_ONLY_AGENT_TOOL_MODES = [
83
83
  'templates',
84
84
  'get',
85
85
  'budget',
86
- 'wrfc-chains',
87
- 'wrfc-history',
88
86
  'cohort-status',
89
87
  'cohort-report',
90
88
  ] as const;
@@ -174,7 +172,7 @@ const STATE_MUTATION_DENIAL = [
174
172
  const SETTINGS_MUTATION_DENIAL = [
175
173
  'GoodVibes Agent does not mutate configuration through model tools in the main conversation.',
176
174
  'Use explicit Agent CLI/slash settings commands for intentional config changes.',
177
- 'Secrets, tokens, passwords, daemon lifecycle settings, and service exposure settings require explicit user action outside the model tool surface.',
175
+ 'Secrets, tokens, passwords, connected-host lifecycle settings, and connected-host exposure settings require explicit user action outside the model tool surface.',
178
176
  ].join(' ');
179
177
 
180
178
  const INSPECT_WRITE_DENIAL = [
@@ -191,8 +189,8 @@ const DURABLE_WORKFLOW_MUTATION_DENIAL = [
191
189
 
192
190
  const CONTROL_MUTATION_DENIAL = [
193
191
  'GoodVibes Agent only inspects runtime-owned product-control surfaces from the main conversation.',
194
- 'Product-control mutation, daemon lifecycle, and service posture changes are disabled here.',
195
- 'Use explicit Agent CLI/slash commands for Agent-owned changes, and keep daemon lifecycle external.',
192
+ 'Product-control mutation, connected-host lifecycle, and connected-host posture changes are disabled here.',
193
+ 'Use explicit Agent CLI/slash commands for Agent-owned changes, and keep connected-host lifecycle external.',
196
194
  ].join(' ');
197
195
 
198
196
  export function installAgentToolPolicyGuard(registry: ToolRegistry, options: AgentToolPolicyGuardOptions = {}): void {
@@ -250,7 +248,7 @@ export function installAgentToolPolicyGuard(registry: ToolRegistry, options: Age
250
248
  modeSet: READ_ONLY_CONTROL_TOOL_MODE_SET,
251
249
  description: [
252
250
  'Read-only product-control inspection for GoodVibes Agent.',
253
- 'Command, panel, and subscription catalogs can be inspected, but product-control mutation and daemon lifecycle are external.',
251
+ 'Command, panel, and subscription catalogs can be inspected, but product-control mutation and connected-host lifecycle are external.',
254
252
  ].join(' '),
255
253
  denial: CONTROL_MUTATION_DENIAL,
256
254
  });
@@ -363,7 +361,7 @@ export function wrapBlockedSettingsToolForAgentPolicy(tool: Tool): void {
363
361
  tool.definition.description = [
364
362
  'Blocked in GoodVibes Agent main conversation: configuration mutation.',
365
363
  'Use explicit Agent CLI/slash settings commands for intentional config changes.',
366
- 'Daemon lifecycle and service exposure remain externally managed by GoodVibes TUI/daemon.',
364
+ 'Connected-host lifecycle and service exposure remain externally managed outside GoodVibes Agent.',
367
365
  ].join(' ');
368
366
  tool.definition.sideEffects = [];
369
367
  tool.definition.parameters = {
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '0.1.114';
9
+ let _version = '0.1.117';
10
10
  let _sdkVersion = '0.33.35';
11
11
  try {
12
12
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
@@ -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
- }