@pellux/goodvibes-agent 0.1.45 → 0.1.46

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  All notable changes to GoodVibes Agent will be recorded here.
4
4
 
5
+ ## 0.1.46 - 2026-05-31
6
+
7
+ - e0addfe Add full daemon capability inventory
8
+
5
9
  ## 0.1.45 - 2026-05-31
6
10
 
7
11
  - 0aa4b3e Add daemon route risk approval review
package/README.md CHANGED
@@ -19,6 +19,7 @@ goodvibes-agent capabilities
19
19
  goodvibes-agent capabilities daemon
20
20
  goodvibes-agent capabilities daemon gaps
21
21
  goodvibes-agent capabilities daemon risk
22
+ goodvibes-agent capabilities daemon inventory
22
23
  ```
23
24
 
24
25
  If Bun reports untrusted lifecycle dependencies, trust only the package and dependencies required by this package:
@@ -47,7 +48,7 @@ bun run publish:check
47
48
 
48
49
  Inside the Agent TUI, use `/agent`, `/home`, or `/operator` to open the operator workspace. It is the Agent-first fullscreen surface for setup, status, live daemon capability coverage, knowledge, local memory/skills, work-plan/approval review, automation observability, and explicit build delegation to GoodVibes TUI.
49
50
 
50
- Use `goodvibes-agent capabilities` or `/capabilities` to inspect the OpenClaw/Hermes benchmark, current Agent posture, configuration commands, usage paths, and remaining gaps. Use `goodvibes-agent capabilities daemon` or `/capabilities daemon` for a live read-only audit of the GoodVibes daemon method catalog, route risk posture, and isolated Agent Knowledge route coverage. Use `goodvibes-agent capabilities daemon gaps` or `/capabilities daemon gaps` to turn that live daemon audit into a prioritized gap plan that separates missing daemon routes from Agent UX work. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for route-risk-aware approval posture without mutating approval state.
51
+ Use `goodvibes-agent capabilities` or `/capabilities` to inspect the OpenClaw/Hermes benchmark, current Agent posture, configuration commands, usage paths, and remaining gaps. Use `goodvibes-agent capabilities daemon` or `/capabilities daemon` for a live read-only audit of the GoodVibes daemon method catalog, route risk posture, and isolated Agent Knowledge route coverage. Use `goodvibes-agent capabilities daemon inventory` for the full public daemon method inventory grouped by category, access, HTTP posture, and dangerous flags. Use `goodvibes-agent capabilities daemon gaps` or `/capabilities daemon gaps` to turn that live daemon audit into a prioritized gap plan that separates missing daemon routes from Agent UX work. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for route-risk-aware approval posture without mutating approval state.
51
52
 
52
53
  Inside the workspace, use `/agent-profile guide` to author custom profile starters without leaving the Agent TUI. The guided flow lists starters, exports starter JSON, imports edited local starters, and creates isolated runtime profiles from them.
53
54
 
@@ -91,9 +92,10 @@ goodvibes-agent capabilities daemon
91
92
  goodvibes-agent capabilities daemon --json
92
93
  goodvibes-agent capabilities daemon gaps
93
94
  goodvibes-agent capabilities daemon risk
95
+ goodvibes-agent capabilities daemon inventory
94
96
  ```
95
97
 
96
- This audit checks `/api/control-plane/methods` and `/api/goodvibes-agent/knowledge/status`. The gap plan and route-risk review are derived from the same read-only calls. None of these commands query default Knowledge/Wiki or HomeGraph.
98
+ This audit checks `/api/control-plane/methods` and `/api/goodvibes-agent/knowledge/status`. The gap plan, route-risk review, and full method inventory are derived from read-only daemon calls. None of these commands query default Knowledge/Wiki or HomeGraph.
97
99
 
98
100
  Agent intentionally blocks daemon lifecycle commands:
99
101
 
@@ -19,6 +19,7 @@ goodvibes-agent capabilities daemon
19
19
  goodvibes-agent capabilities daemon --json
20
20
  goodvibes-agent capabilities daemon gaps
21
21
  goodvibes-agent capabilities daemon risk
22
+ goodvibes-agent capabilities daemon inventory
22
23
  goodvibes-agent capabilities daemon knowledge
23
24
  ```
24
25
 
@@ -30,6 +31,7 @@ Inside the TUI:
30
31
  /capabilities knowledge
31
32
  /capabilities daemon
32
33
  /capabilities daemon gaps
34
+ /capabilities daemon inventory
33
35
  /approval risk
34
36
  ```
35
37
 
@@ -60,7 +62,7 @@ The benchmark measures two different GoodVibes layers:
60
62
 
61
63
  If the daemon already has a route but Agent lacks a good setup/workspace/CLI surface, the gap is treated as an Agent product gap rather than a missing platform capability.
62
64
 
63
- Use `goodvibes-agent capabilities daemon` for the live read-only daemon audit. It checks the public control-plane method catalog, route risk posture, and the isolated Agent Knowledge status route. Use `goodvibes-agent capabilities daemon gaps` to convert that daemon-measured audit into a prioritized gap plan with `version_mismatch`, `agent_route_missing`, `required_method_missing`, `route_risk_review`, and `agent_ux_gap` rows. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for a route-risk-aware approval-center view over read-only, mutating, dangerous, and authenticated route metadata. These commands intentionally avoid default `/api/knowledge/*`, HomeGraph, and Home Assistant routes.
65
+ Use `goodvibes-agent capabilities daemon` for the live read-only daemon audit. It checks the public control-plane method catalog, route risk posture, and the isolated Agent Knowledge status route. Use `goodvibes-agent capabilities daemon inventory` for the full public daemon method inventory grouped by category, access, HTTP method, and dangerous flag. Use `goodvibes-agent capabilities daemon gaps` to convert that daemon-measured audit into a prioritized gap plan with `version_mismatch`, `agent_route_missing`, `required_method_missing`, `route_risk_review`, and `agent_ux_gap` rows. Use `goodvibes-agent capabilities daemon risk` or `/approval risk` for a route-risk-aware approval-center view over read-only, mutating, dangerous, and authenticated route metadata. These commands intentionally avoid default `/api/knowledge/*`, HomeGraph, and Home Assistant routes.
64
66
 
65
67
  ## Capability Targets
66
68
 
@@ -82,7 +84,7 @@ Use `goodvibes-agent capabilities daemon` for the live read-only daemon audit. I
82
84
 
83
85
  GoodVibes Agent should exceed OpenClaw/Hermes by making these properties true from day one:
84
86
 
85
- - Capability surfaces are discoverable through `goodvibes-agent capabilities`, `goodvibes-agent capabilities daemon`, `goodvibes-agent capabilities daemon gaps`, `goodvibes-agent capabilities daemon risk`, `/capabilities`, `/capabilities daemon`, `/approval risk`, onboarding, and the operator workspace.
87
+ - Capability surfaces are discoverable through `goodvibes-agent capabilities`, `goodvibes-agent capabilities daemon`, `goodvibes-agent capabilities daemon inventory`, `goodvibes-agent capabilities daemon gaps`, `goodvibes-agent capabilities daemon risk`, `/capabilities`, `/capabilities daemon`, `/approval risk`, onboarding, and the operator workspace.
86
88
  - Agent Knowledge isolation is a release gate, not a convention.
87
89
  - Routine-to-schedule promotion preserves Agent Knowledge isolation, uses only public external daemon schedule routes, supports explicit delivery targets, and stores redacted receipts.
88
90
  - Model-visible tools are policy-gated for serial, non-secret, non-destructive use.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-agent",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "private": false,
5
5
  "description": "Near-fork GoodVibes operator assistant with the GoodVibes TUI shell, renderer, input, fullscreen workspace, and daemon-connected Agent product brain.",
6
6
  "type": "module",
@@ -9,18 +9,21 @@ import {
9
9
  buildDaemonCapabilityGapReport,
10
10
  buildDaemonCapabilityRouteRiskReport,
11
11
  fetchLiveDaemonCapabilityAudit,
12
+ fetchLiveDaemonCapabilityInventory,
12
13
  filterDaemonCapabilityAuditAreas,
13
14
  filterDaemonCapabilityGaps,
15
+ filterDaemonCapabilityInventoryGroups,
14
16
  filterDaemonCapabilityRouteRiskAreas,
15
17
  renderDaemonCapabilityAudit,
16
18
  renderDaemonCapabilityFailure,
17
19
  renderDaemonCapabilityGaps,
20
+ renderDaemonCapabilityInventory,
18
21
  renderDaemonCapabilityRouteRisk,
19
22
  } from '../operator/daemon-capability-audit.ts';
20
23
  import { resolveAgentDaemonConnection } from '../agent/routine-schedule-promotion.ts';
21
24
 
22
25
  interface CapabilityCommandArgs {
23
- readonly mode: 'benchmark' | 'daemon' | 'daemon-gaps' | 'daemon-risk';
26
+ readonly mode: 'benchmark' | 'daemon' | 'daemon-gaps' | 'daemon-risk' | 'daemon-inventory';
24
27
  readonly query: string | undefined;
25
28
  }
26
29
 
@@ -39,6 +42,10 @@ function readCapabilityArgs(args: readonly string[]): CapabilityCommandArgs {
39
42
  const query = values.slice(2).join(' ').trim();
40
43
  return { mode: 'daemon-risk', query: query.length > 0 ? query : undefined };
41
44
  }
45
+ if (values[1] === 'inventory' || values[1] === 'methods' || values[1] === 'routes') {
46
+ const query = values.slice(2).join(' ').trim();
47
+ return { mode: 'daemon-inventory', query: query.length > 0 ? query : undefined };
48
+ }
42
49
  const query = values.slice(1).join(' ').trim();
43
50
  return { mode: 'daemon', query: query.length > 0 ? query : undefined };
44
51
  }
@@ -106,6 +113,25 @@ export async function handleCapabilitiesCommand(runtime: CliCommandRuntime): Pro
106
113
  exitCode: 0,
107
114
  };
108
115
  }
116
+ if (args.mode === 'daemon-inventory') {
117
+ const connection = resolveAgentDaemonConnection(runtime.configManager, runtime.homeDirectory);
118
+ const inventory = await fetchLiveDaemonCapabilityInventory(connection);
119
+ if (!inventory.ok) {
120
+ return {
121
+ output: runtime.cli.flags.outputFormat === 'json'
122
+ ? JSON.stringify(inventory, null, 2)
123
+ : renderDaemonCapabilityFailure(inventory),
124
+ exitCode: inventory.kind === 'auth_required' || inventory.kind === 'daemon_unavailable' ? 1 : 2,
125
+ };
126
+ }
127
+ const groups = filterDaemonCapabilityInventoryGroups(inventory.groups, args.query);
128
+ return {
129
+ output: runtime.cli.flags.outputFormat === 'json'
130
+ ? JSON.stringify({ ...inventory, matchedGroupCount: groups.length, groups }, null, 2)
131
+ : renderDaemonCapabilityInventory(inventory, groups),
132
+ exitCode: 0,
133
+ };
134
+ }
109
135
  const report = buildOperatorCapabilityBenchmarkReport();
110
136
  const capabilities = filterOperatorCapabilities(report.capabilities, args.query);
111
137
  if (runtime.cli.flags.outputFormat === 'json') {
package/src/cli/help.ts CHANGED
@@ -105,6 +105,7 @@ export function renderGoodVibesHelp(binary = 'goodvibes-agent'): string {
105
105
  ` ${binary} capabilities daemon`,
106
106
  ` ${binary} capabilities daemon gaps`,
107
107
  ` ${binary} capabilities daemon risk`,
108
+ ` ${binary} capabilities daemon inventory`,
108
109
  ` ${binary} knowledge status`,
109
110
  ` ${binary} knowledge ask "What is GoodVibes Agent?"`,
110
111
  ` ${binary} ask "What is GoodVibes Agent?"`,
@@ -199,9 +200,9 @@ const COMMAND_HELP: Record<string, CommandHelp> = {
199
200
  examples: ['compat', 'compat --json'],
200
201
  },
201
202
  capabilities: {
202
- usage: ['capabilities [openclaw|hermes|query]', 'capabilities daemon [query]', 'capabilities daemon gaps [query]', 'capabilities daemon risk [query]', 'capabilities --json'],
203
- summary: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness, live GoodVibes daemon method coverage, and daemon-measured product gaps.',
204
- examples: ['capabilities', 'capabilities hermes', 'capabilities daemon', 'capabilities daemon gaps', 'capabilities daemon risk --json', 'capabilities daemon knowledge --json'],
203
+ usage: ['capabilities [openclaw|hermes|query]', 'capabilities daemon [query]', 'capabilities daemon gaps [query]', 'capabilities daemon risk [query]', 'capabilities daemon inventory [query]', 'capabilities --json'],
204
+ summary: 'Show the OpenClaw/Hermes capability benchmark, Agent readiness, live GoodVibes daemon method coverage, route risk, and daemon-measured product gaps.',
205
+ examples: ['capabilities', 'capabilities hermes', 'capabilities daemon', 'capabilities daemon gaps', 'capabilities daemon risk --json', 'capabilities daemon inventory channels --json'],
205
206
  },
206
207
  knowledge: {
207
208
  usage: [
@@ -491,6 +491,7 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
491
491
  { id: 'capabilities-daemon', label: 'Live daemon audit', detail: 'Inspect the public daemon method catalog plus isolated Agent Knowledge route coverage. Does not query default Knowledge/Wiki or HomeGraph.', command: '/capabilities daemon', kind: 'command', safety: 'read-only' },
492
492
  { id: 'capabilities-daemon-gaps', label: 'Daemon gap plan', detail: 'Classify live daemon coverage into platform gaps, Agent route gaps, route-risk reviews, and Agent UX work without querying default Knowledge/Wiki or HomeGraph.', command: '/capabilities daemon gaps', kind: 'command', safety: 'read-only' },
493
493
  { id: 'capabilities-daemon-risk', label: 'Route risk review', detail: 'Inspect daemon read-only, mutating, dangerous, and authenticated route metadata for approval-center planning.', command: '/capabilities daemon risk', kind: 'command', safety: 'read-only' },
494
+ { id: 'capabilities-daemon-inventory', label: 'Full method inventory', detail: 'List every public daemon method by category, HTTP posture, access, and dangerous flag without querying default Knowledge/Wiki or HomeGraph.', command: '/capabilities daemon inventory', kind: 'command', safety: 'read-only' },
494
495
  { id: 'capabilities-daemon-knowledge', label: 'Knowledge coverage', detail: 'Filter the live daemon audit to the isolated Agent Knowledge segment.', command: '/capabilities daemon knowledge', kind: 'command', safety: 'read-only' },
495
496
  { id: 'capabilities-daemon-channels', label: 'Channel coverage', detail: 'Filter the live daemon audit to channel and delivery gateway route coverage.', command: '/capabilities daemon channels', kind: 'command', safety: 'read-only' },
496
497
  { id: 'capabilities-daemon-automation', label: 'Automation coverage', detail: 'Filter the live daemon audit to automation, schedule, run, and capacity route coverage.', command: '/capabilities daemon automation', kind: 'command', safety: 'read-only' },
@@ -8,12 +8,15 @@ import {
8
8
  buildDaemonCapabilityGapReport,
9
9
  buildDaemonCapabilityRouteRiskReport,
10
10
  fetchLiveDaemonCapabilityAudit,
11
+ fetchLiveDaemonCapabilityInventory,
11
12
  filterDaemonCapabilityAuditAreas,
12
13
  filterDaemonCapabilityGaps,
14
+ filterDaemonCapabilityInventoryGroups,
13
15
  filterDaemonCapabilityRouteRiskAreas,
14
16
  renderDaemonCapabilityAudit,
15
17
  renderDaemonCapabilityFailure,
16
18
  renderDaemonCapabilityGaps,
19
+ renderDaemonCapabilityInventory,
17
20
  renderDaemonCapabilityRouteRisk,
18
21
  } from '../../operator/daemon-capability-audit.ts';
19
22
  import { resolveAgentDaemonConnection } from '../../agent/routine-schedule-promotion.ts';
@@ -47,6 +50,17 @@ export function registerCapabilitiesRuntimeCommands(registry: CommandRegistry):
47
50
  ctx.print(renderDaemonCapabilityRouteRisk(report, areas));
48
51
  return;
49
52
  }
53
+ if (args[1] === 'inventory' || args[1] === 'methods' || args[1] === 'routes') {
54
+ const inventory = await fetchLiveDaemonCapabilityInventory(connection);
55
+ if (!inventory.ok) {
56
+ ctx.print(renderDaemonCapabilityFailure(inventory));
57
+ return;
58
+ }
59
+ const query = args.slice(2).join(' ').trim() || undefined;
60
+ const groups = filterDaemonCapabilityInventoryGroups(inventory.groups, query);
61
+ ctx.print(renderDaemonCapabilityInventory(inventory, groups));
62
+ return;
63
+ }
50
64
  const query = args.slice(1).join(' ').trim() || undefined;
51
65
  const areas = filterDaemonCapabilityAuditAreas(audit.areas, query);
52
66
  ctx.print(renderDaemonCapabilityAudit(audit, areas));
@@ -135,6 +135,53 @@ export interface DaemonCapabilityRouteRiskReport {
135
135
  readonly areas: readonly DaemonCapabilityRouteRiskArea[];
136
136
  }
137
137
 
138
+ export interface DaemonCapabilityInventoryMethod {
139
+ readonly id: string;
140
+ readonly title?: string;
141
+ readonly category: string;
142
+ readonly access: string;
143
+ readonly invokable: boolean | null;
144
+ readonly dangerous: boolean;
145
+ readonly httpMethod: string;
146
+ readonly path?: string;
147
+ readonly readOnly: boolean;
148
+ readonly mutating: boolean;
149
+ }
150
+
151
+ export interface DaemonCapabilityInventoryGroup {
152
+ readonly category: string;
153
+ readonly methodCount: number;
154
+ readonly readOnlyMethodCount: number;
155
+ readonly mutatingMethodCount: number;
156
+ readonly authenticatedMethodCount: number;
157
+ readonly dangerousMethodCount: number;
158
+ readonly methods: readonly DaemonCapabilityInventoryMethod[];
159
+ }
160
+
161
+ export interface DaemonCapabilityInventoryReport {
162
+ readonly ok: true;
163
+ readonly kind: 'daemon.capabilities.inventory';
164
+ readonly baseUrl: string;
165
+ readonly daemonVersion: string;
166
+ readonly expectedSdkVersion: string;
167
+ readonly daemonCompatible: boolean;
168
+ readonly methodCatalogRoute: typeof DAEMON_METHOD_CATALOG_ROUTE;
169
+ readonly methodCount: number;
170
+ readonly agentKnowledgeRoute: typeof AGENT_KNOWLEDGE_STATUS_ROUTE;
171
+ readonly agentKnowledgeRouteReady: boolean;
172
+ readonly defaultKnowledgeFallback: false;
173
+ readonly homeGraphFallback: false;
174
+ readonly readOnlyMethodCount: number;
175
+ readonly mutatingMethodCount: number;
176
+ readonly authenticatedMethodCount: number;
177
+ readonly dangerousMethodCount: number;
178
+ readonly accessCounts: readonly {
179
+ readonly access: string;
180
+ readonly count: number;
181
+ }[];
182
+ readonly groups: readonly DaemonCapabilityInventoryGroup[];
183
+ }
184
+
138
185
  export interface DaemonCapabilityAuditFailure {
139
186
  readonly ok: false;
140
187
  readonly kind: DaemonCapabilityAuditFailureKind;
@@ -380,6 +427,36 @@ function readMethodSummaries(body: unknown): readonly DaemonMethodSummary[] {
380
427
  });
381
428
  }
382
429
 
430
+ function normalizeMethodCategory(method: DaemonMethodSummary): string {
431
+ const category = method.category?.trim();
432
+ if (category) return category;
433
+ const [prefix] = method.id.split('.');
434
+ return prefix || 'uncategorized';
435
+ }
436
+
437
+ function normalizeAccess(method: DaemonMethodSummary): string {
438
+ const access = method.access?.trim();
439
+ return access || 'unknown';
440
+ }
441
+
442
+ function normalizeHttpMethod(method: DaemonMethodSummary): string {
443
+ return method.http?.method?.trim().toUpperCase() || 'UNKNOWN';
444
+ }
445
+
446
+ function isReadOnlyHttpMethod(httpMethod: string): boolean {
447
+ return httpMethod === 'GET' || httpMethod === 'HEAD';
448
+ }
449
+
450
+ function compareInventoryMethods(left: DaemonCapabilityInventoryMethod, right: DaemonCapabilityInventoryMethod): number {
451
+ return left.id.localeCompare(right.id);
452
+ }
453
+
454
+ function compareInventoryGroups(left: DaemonCapabilityInventoryGroup, right: DaemonCapabilityInventoryGroup): number {
455
+ const countDelta = right.methodCount - left.methodCount;
456
+ if (countDelta !== 0) return countDelta;
457
+ return left.category.localeCompare(right.category);
458
+ }
459
+
383
460
  function daemonVersionFromStatus(body: unknown): string {
384
461
  if (!isRecord(body)) return 'unknown';
385
462
  return readString(body, 'version')
@@ -585,6 +662,116 @@ export async function fetchLiveDaemonCapabilityAudit(
585
662
  }
586
663
  }
587
664
 
665
+ export function buildDaemonCapabilityInventoryReport(
666
+ connection: AgentDaemonConnection,
667
+ daemonVersion: string,
668
+ agentKnowledgeRouteReady: boolean,
669
+ methodSummaries: readonly DaemonMethodSummary[],
670
+ ): DaemonCapabilityInventoryReport {
671
+ const methods = methodSummaries.map((method): DaemonCapabilityInventoryMethod => {
672
+ const httpMethod = normalizeHttpMethod(method);
673
+ const readOnly = isReadOnlyHttpMethod(httpMethod);
674
+ return {
675
+ id: method.id,
676
+ title: method.title,
677
+ category: normalizeMethodCategory(method),
678
+ access: normalizeAccess(method),
679
+ invokable: typeof method.invokable === 'boolean' ? method.invokable : null,
680
+ dangerous: method.dangerous === true,
681
+ httpMethod,
682
+ path: method.http?.path,
683
+ readOnly,
684
+ mutating: httpMethod !== 'UNKNOWN' && !readOnly,
685
+ };
686
+ }).sort(compareInventoryMethods);
687
+
688
+ const groupsByCategory = new Map<string, DaemonCapabilityInventoryMethod[]>();
689
+ for (const method of methods) {
690
+ const existing = groupsByCategory.get(method.category) ?? [];
691
+ existing.push(method);
692
+ groupsByCategory.set(method.category, existing);
693
+ }
694
+
695
+ const groups = [...groupsByCategory.entries()].map(([category, categoryMethods]): DaemonCapabilityInventoryGroup => ({
696
+ category,
697
+ methodCount: categoryMethods.length,
698
+ readOnlyMethodCount: categoryMethods.filter((method) => method.readOnly).length,
699
+ mutatingMethodCount: categoryMethods.filter((method) => method.mutating).length,
700
+ authenticatedMethodCount: categoryMethods.filter((method) => method.access === 'authenticated').length,
701
+ dangerousMethodCount: categoryMethods.filter((method) => method.dangerous).length,
702
+ methods: categoryMethods,
703
+ })).sort(compareInventoryGroups);
704
+
705
+ const accessEntries = new Map<string, number>();
706
+ for (const method of methods) {
707
+ accessEntries.set(method.access, (accessEntries.get(method.access) ?? 0) + 1);
708
+ }
709
+ const accessCounts = [...accessEntries.entries()]
710
+ .map(([access, count]) => ({ access, count }))
711
+ .sort((left, right) => {
712
+ const countDelta = right.count - left.count;
713
+ if (countDelta !== 0) return countDelta;
714
+ return left.access.localeCompare(right.access);
715
+ });
716
+
717
+ return {
718
+ ok: true,
719
+ kind: 'daemon.capabilities.inventory',
720
+ baseUrl: connection.baseUrl,
721
+ daemonVersion,
722
+ expectedSdkVersion: SDK_VERSION,
723
+ daemonCompatible: daemonVersion === SDK_VERSION,
724
+ methodCatalogRoute: DAEMON_METHOD_CATALOG_ROUTE,
725
+ methodCount: methods.length,
726
+ agentKnowledgeRoute: AGENT_KNOWLEDGE_STATUS_ROUTE,
727
+ agentKnowledgeRouteReady,
728
+ defaultKnowledgeFallback: false,
729
+ homeGraphFallback: false,
730
+ readOnlyMethodCount: methods.filter((method) => method.readOnly).length,
731
+ mutatingMethodCount: methods.filter((method) => method.mutating).length,
732
+ authenticatedMethodCount: methods.filter((method) => method.access === 'authenticated').length,
733
+ dangerousMethodCount: methods.filter((method) => method.dangerous).length,
734
+ accessCounts,
735
+ groups,
736
+ };
737
+ }
738
+
739
+ export type DaemonCapabilityInventoryResult =
740
+ | DaemonCapabilityInventoryReport
741
+ | DaemonCapabilityAuditFailure;
742
+
743
+ export async function fetchLiveDaemonCapabilityInventory(
744
+ connection: AgentDaemonConnection,
745
+ ): Promise<DaemonCapabilityInventoryResult> {
746
+ let daemonVersion = 'unknown';
747
+ try {
748
+ const status = await fetchJson(connection, DAEMON_STATUS_ROUTE);
749
+ if (status.ok) {
750
+ daemonVersion = daemonVersionFromStatus(status.body);
751
+ } else if (status.status === 401 || status.status === 403) {
752
+ return failureFromResponse(status, connection, DAEMON_STATUS_ROUTE, daemonVersion);
753
+ }
754
+
755
+ const methods = await fetchJson(connection, DAEMON_METHOD_CATALOG_ROUTE);
756
+ if (!methods.ok) return failureFromResponse(methods, connection, DAEMON_METHOD_CATALOG_ROUTE, daemonVersion);
757
+
758
+ const agentKnowledge = await fetchJson(connection, AGENT_KNOWLEDGE_STATUS_ROUTE);
759
+ if (!agentKnowledge.ok) {
760
+ const failure = failureFromResponse(agentKnowledge, connection, AGENT_KNOWLEDGE_STATUS_ROUTE, daemonVersion);
761
+ if (failure.kind === 'auth_required') return failure;
762
+ }
763
+
764
+ return buildDaemonCapabilityInventoryReport(
765
+ connection,
766
+ daemonVersion,
767
+ agentKnowledge.ok,
768
+ readMethodSummaries(methods.body),
769
+ );
770
+ } catch (error) {
771
+ return failureFromThrown(error, connection, daemonVersion === 'unknown' ? DAEMON_STATUS_ROUTE : DAEMON_METHOD_CATALOG_ROUTE);
772
+ }
773
+ }
774
+
588
775
  export function filterDaemonCapabilityAuditAreas(
589
776
  areas: readonly DaemonCapabilityAuditArea[],
590
777
  query: string | undefined,
@@ -857,6 +1044,82 @@ export function renderDaemonCapabilityRouteRisk(
857
1044
  return lines.join('\n').trimEnd();
858
1045
  }
859
1046
 
1047
+ export function filterDaemonCapabilityInventoryGroups(
1048
+ groups: readonly DaemonCapabilityInventoryGroup[],
1049
+ query: string | undefined,
1050
+ ): readonly DaemonCapabilityInventoryGroup[] {
1051
+ const normalized = query?.trim().toLowerCase();
1052
+ if (!normalized) return groups;
1053
+ return groups.flatMap((group): DaemonCapabilityInventoryGroup[] => {
1054
+ const categoryMatches = group.category.toLowerCase().includes(normalized);
1055
+ const methods = categoryMatches
1056
+ ? group.methods
1057
+ : group.methods.filter((method) => {
1058
+ return method.id.toLowerCase().includes(normalized)
1059
+ || method.title?.toLowerCase().includes(normalized) === true
1060
+ || method.access.toLowerCase().includes(normalized)
1061
+ || method.httpMethod.toLowerCase().includes(normalized)
1062
+ || method.path?.toLowerCase().includes(normalized) === true;
1063
+ });
1064
+ if (methods.length === 0) return [];
1065
+ return [{
1066
+ category: group.category,
1067
+ methodCount: methods.length,
1068
+ readOnlyMethodCount: methods.filter((method) => method.readOnly).length,
1069
+ mutatingMethodCount: methods.filter((method) => method.mutating).length,
1070
+ authenticatedMethodCount: methods.filter((method) => method.access === 'authenticated').length,
1071
+ dangerousMethodCount: methods.filter((method) => method.dangerous).length,
1072
+ methods,
1073
+ }];
1074
+ });
1075
+ }
1076
+
1077
+ function renderInventoryMethod(method: DaemonCapabilityInventoryMethod): string {
1078
+ const risk = method.dangerous
1079
+ ? ' dangerous'
1080
+ : method.mutating
1081
+ ? ' mutating'
1082
+ : ' read-only';
1083
+ const route = method.path ? ` ${method.httpMethod} ${method.path}` : ` ${method.httpMethod}`;
1084
+ return ` ${method.id} [${method.access};${risk}]${route}`;
1085
+ }
1086
+
1087
+ export function renderDaemonCapabilityInventory(
1088
+ report: DaemonCapabilityInventoryReport,
1089
+ groups: readonly DaemonCapabilityInventoryGroup[] = report.groups,
1090
+ ): string {
1091
+ const lines: string[] = [
1092
+ 'GoodVibes daemon method inventory',
1093
+ ` daemon: ${report.baseUrl}`,
1094
+ ` SDK: Agent expects ${report.expectedSdkVersion}; daemon reports ${report.daemonVersion}`,
1095
+ ` compatibility: ${report.daemonCompatible ? 'matched' : 'mismatch'}`,
1096
+ ` method catalog: ${report.methodCount} methods from ${report.methodCatalogRoute}`,
1097
+ ` Agent Knowledge: ${report.agentKnowledgeRouteReady ? 'ready' : 'missing'} ${report.agentKnowledgeRoute}`,
1098
+ ' isolation: default Knowledge/Wiki fallback no; HomeGraph fallback no',
1099
+ ` totals: ${report.readOnlyMethodCount} read-only; ${report.mutatingMethodCount} mutating; ${report.dangerousMethodCount} dangerous; ${report.authenticatedMethodCount} authenticated`,
1100
+ ` access: ${report.accessCounts.map((entry) => `${entry.access} ${entry.count}`).join('; ') || 'none'}`,
1101
+ '',
1102
+ ];
1103
+
1104
+ if (groups.length === 0) {
1105
+ lines.push('No daemon methods matched this query.');
1106
+ return lines.join('\n');
1107
+ }
1108
+
1109
+ for (const group of groups) {
1110
+ lines.push(`${group.category} (${group.methodCount})`);
1111
+ lines.push(` ${group.readOnlyMethodCount} read-only; ${group.mutatingMethodCount} mutating; ${group.dangerousMethodCount} dangerous; ${group.authenticatedMethodCount} authenticated`);
1112
+ const visibleMethods = group.methods.slice(0, 12);
1113
+ for (const method of visibleMethods) lines.push(renderInventoryMethod(method));
1114
+ if (group.methods.length > visibleMethods.length) {
1115
+ lines.push(` ... ${group.methods.length - visibleMethods.length} more; use --json or a narrower query for the full list.`);
1116
+ }
1117
+ lines.push('');
1118
+ }
1119
+
1120
+ return lines.join('\n').trimEnd();
1121
+ }
1122
+
860
1123
  export function renderDaemonCapabilityAudit(
861
1124
  audit: DaemonCapabilityAuditSuccess,
862
1125
  areas: readonly DaemonCapabilityAuditArea[] = audit.areas,
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.45';
9
+ let _version = '0.1.46';
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 {