facult 1.1.0 → 1.2.1

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/src/index.ts CHANGED
@@ -2,14 +2,28 @@
2
2
 
3
3
  import { join } from "node:path";
4
4
  import { getAllAdapters } from "./adapters";
5
+ import { aiCommand } from "./ai";
5
6
  import { auditCommand } from "./audit";
6
7
  import { autosyncCommand } from "./autosync";
8
+ import {
9
+ type CapabilityScopeMode,
10
+ parseCliContextArgs,
11
+ resolveCliContextRoot,
12
+ } from "./cli-context";
7
13
  import { consolidateCommand } from "./consolidate";
8
14
  import { doctorCommand } from "./doctor";
9
15
  import { disableCommand, enableCommand } from "./enable-disable";
16
+ import type { AssetScope, AssetSourceKind } from "./graph";
17
+ import {
18
+ graphDependencies,
19
+ graphDependents,
20
+ loadGraph,
21
+ resolveGraphNode,
22
+ } from "./graph-query";
10
23
  import type {
11
24
  AgentEntry,
12
25
  FacultIndex,
26
+ InstructionEntry,
13
27
  McpEntry,
14
28
  SkillEntry,
15
29
  SnippetEntry,
@@ -25,9 +39,11 @@ import { migrateCommand } from "./migrate";
25
39
  import type { QueryFilters } from "./query";
26
40
  import {
27
41
  filterAgents,
42
+ filterInstructions,
28
43
  filterMcp,
29
44
  filterSkills,
30
45
  filterSnippets,
46
+ findCapabilities,
31
47
  loadIndex,
32
48
  } from "./query";
33
49
  import {
@@ -44,9 +60,15 @@ import { snippetsCommand } from "./snippets-cli";
44
60
  import { trustCommand, untrustCommand } from "./trust";
45
61
  import { parseJsonLenient } from "./util/json";
46
62
 
47
- type ListKind = "skills" | "mcp" | "agents" | "snippets";
63
+ type ListKind = "skills" | "mcp" | "agents" | "snippets" | "instructions";
48
64
 
49
- const LIST_KINDS: ListKind[] = ["skills", "mcp", "agents", "snippets"];
65
+ const LIST_KINDS: ListKind[] = [
66
+ "skills",
67
+ "mcp",
68
+ "agents",
69
+ "snippets",
70
+ "instructions",
71
+ ];
50
72
 
51
73
  export interface ListCommandOptions {
52
74
  kind: ListKind;
@@ -54,6 +76,25 @@ export interface ListCommandOptions {
54
76
  json: boolean;
55
77
  }
56
78
 
79
+ export interface FindCommandOptions {
80
+ text: string;
81
+ json: boolean;
82
+ }
83
+
84
+ type GraphCommandKind = "show" | "deps" | "dependents";
85
+
86
+ interface ContextualCommandOptions {
87
+ rootArg?: string;
88
+ scopeMode: CapabilityScopeMode;
89
+ sourceKind?: AssetSourceKind;
90
+ }
91
+
92
+ interface GraphCommandOptions {
93
+ kind: GraphCommandKind;
94
+ target: string;
95
+ json: boolean;
96
+ }
97
+
57
98
  function printHelp() {
58
99
  console.log(`facult — inspect local agent configs for skills + MCP servers
59
100
 
@@ -66,9 +107,13 @@ Usage:
66
107
  facult doctor [--repair]
67
108
  facult consolidate [--force] [--auto <mode>] [scan options]
68
109
  facult index [--force]
69
- facult list [skills|mcp|agents|snippets] [--enabled-for TOOL] [--untrusted] [--flagged] [--pending] [--json]
110
+ facult list [skills|mcp|agents|snippets|instructions] [--enabled-for TOOL] [--untrusted] [--flagged] [--pending] [--json]
70
111
  facult show <name>
71
112
  facult show mcp:<name> [--show-secrets]
113
+ facult show instruction:<name>
114
+ facult find <query> [--json]
115
+ facult graph <show|deps|dependents> <asset> [--json]
116
+ facult ai <writeback|evolve> [args...]
72
117
  facult adapters
73
118
  facult trust <name> [moreNames...]
74
119
  facult untrust <name> [moreNames...]
@@ -97,8 +142,11 @@ Commands:
97
142
  doctor Inspect and repair local facult state
98
143
  consolidate Deduplicate and copy skills + MCP configs (interactive or --auto)
99
144
  index Build a queryable index from the canonical store (see FACULT_ROOT_DIR)
100
- list List indexed skills, MCP servers, agents, or snippets
145
+ list List indexed skills, MCP servers, agents, snippets, or instructions
101
146
  show Show a single indexed entry, including file contents
147
+ find Search local indexed capabilities across asset types
148
+ graph Inspect explicit capability graph nodes and dependencies
149
+ ai Record, group, and evolve AI writeback into reviewable proposals
102
150
  adapters List registered tool adapters
103
151
  trust Mark a skill or MCP server as trusted (annotation only)
104
152
  untrust Remove trusted annotation
@@ -146,6 +194,11 @@ Options:
146
194
  --self (update) run self-update flow instead of remote item updates
147
195
  --strict-source-trust Enforce trust-only remote install/update actions
148
196
  --show-secrets (show) Print raw secret values (unsafe)
197
+ --root Select a canonical .ai root explicitly
198
+ --global Force the global canonical root
199
+ --project Force the nearest repo-local .ai root
200
+ --scope Capability view scope: merged|global|project
201
+ --source Filter to builtin|global|project asset provenance
149
202
  `);
150
203
  }
151
204
 
@@ -153,13 +206,18 @@ function printListHelp() {
153
206
  console.log(`facult list — list indexed entries from the canonical store
154
207
 
155
208
  Usage:
156
- facult list [skills|mcp|agents|snippets] [options]
209
+ facult list [skills|mcp|agents|snippets|instructions] [options]
157
210
 
158
211
  Options:
159
212
  --enabled-for TOOL Only include entries enabled for a tool
160
213
  --untrusted Only include entries that are not trusted
161
214
  --flagged Only include entries flagged by audit
162
215
  --pending Only include entries pending audit
216
+ --root PATH Select a canonical .ai root explicitly
217
+ --global Force the global canonical root
218
+ --project Force the nearest repo-local .ai root
219
+ --scope SCOPE merged|global|project (default: merged)
220
+ --source KIND builtin|global|project
163
221
  --json Print JSON array
164
222
  `);
165
223
  }
@@ -170,9 +228,49 @@ function printShowHelp() {
170
228
  Usage:
171
229
  facult show <name>
172
230
  facult show mcp:<name> [--show-secrets]
231
+ facult show instruction:<name>
173
232
 
174
233
  Options:
175
234
  --show-secrets (mcp) Print raw secret values (unsafe)
235
+ --root PATH Select a canonical .ai root explicitly
236
+ --global Force the global canonical root
237
+ --project Force the nearest repo-local .ai root
238
+ --scope SCOPE merged|global|project (default: merged)
239
+ --source KIND builtin|global|project
240
+ `);
241
+ }
242
+
243
+ function printFindHelp() {
244
+ console.log(`facult find — search local indexed capabilities across asset types
245
+
246
+ Usage:
247
+ facult find <query> [--json]
248
+
249
+ Options:
250
+ --root PATH Select a canonical .ai root explicitly
251
+ --global Force the global canonical root
252
+ --project Force the nearest repo-local .ai root
253
+ --scope SCOPE merged|global|project (default: merged)
254
+ --source KIND builtin|global|project
255
+ --json Print JSON array
256
+ `);
257
+ }
258
+
259
+ function printGraphHelp() {
260
+ console.log(`facult graph — inspect explicit capability graph nodes and relations
261
+
262
+ Usage:
263
+ facult graph show <asset> [--json]
264
+ facult graph deps <asset> [--json]
265
+ facult graph dependents <asset> [--json]
266
+
267
+ Options:
268
+ --root PATH Select a canonical .ai root explicitly
269
+ --global Force the global canonical root
270
+ --project Force the nearest repo-local .ai root
271
+ --scope SCOPE merged|global|project (default: merged)
272
+ --source KIND builtin|global|project
273
+ --json Print JSON
176
274
  `);
177
275
  }
178
276
 
@@ -247,15 +345,111 @@ export function parseListArgs(argv: string[]): ListCommandOptions {
247
345
  return { kind, filters, json };
248
346
  }
249
347
 
348
+ export function parseFindArgs(argv: string[]): FindCommandOptions {
349
+ let json = false;
350
+ const terms: string[] = [];
351
+
352
+ for (const arg of argv) {
353
+ if (!arg) {
354
+ continue;
355
+ }
356
+ if (arg === "--json") {
357
+ json = true;
358
+ continue;
359
+ }
360
+ if (arg.startsWith("-")) {
361
+ throw new Error(`Unknown option: ${arg}`);
362
+ }
363
+ terms.push(arg);
364
+ }
365
+
366
+ const text = terms.join(" ").trim();
367
+ if (!text) {
368
+ throw new Error("find requires a query");
369
+ }
370
+
371
+ return { text, json };
372
+ }
373
+
374
+ function parseGraphArgs(argv: string[]): GraphCommandOptions {
375
+ const [kind, ...rest] = argv;
376
+ if (!(kind === "show" || kind === "deps" || kind === "dependents")) {
377
+ throw new Error(`Unknown graph command: ${kind ?? "<missing>"}`);
378
+ }
379
+
380
+ let json = false;
381
+ let target: string | null = null;
382
+ for (const arg of rest) {
383
+ if (!arg) {
384
+ continue;
385
+ }
386
+ if (arg === "--json") {
387
+ json = true;
388
+ continue;
389
+ }
390
+ if (arg.startsWith("-")) {
391
+ throw new Error(`Unknown option: ${arg}`);
392
+ }
393
+ if (target) {
394
+ throw new Error(`graph ${kind} accepts a single asset selector`);
395
+ }
396
+ target = arg;
397
+ }
398
+
399
+ if (!target) {
400
+ throw new Error(`graph ${kind} requires an asset selector`);
401
+ }
402
+
403
+ return { kind, target, json };
404
+ }
405
+
406
+ function scopeFilterForMode(
407
+ scopeMode: CapabilityScopeMode
408
+ ): AssetScope | undefined {
409
+ return scopeMode === "project" ? "project" : undefined;
410
+ }
411
+
412
+ function resolveContextualOptions(
413
+ argv: string[],
414
+ opts?: { allowSource?: boolean }
415
+ ): { argv: string[]; context: ContextualCommandOptions } {
416
+ const parsed = parseCliContextArgs(argv, {
417
+ allowSource: opts?.allowSource,
418
+ });
419
+ return {
420
+ argv: parsed.argv,
421
+ context: {
422
+ rootArg: parsed.rootArg,
423
+ scopeMode: parsed.scope,
424
+ sourceKind: parsed.sourceKind,
425
+ },
426
+ };
427
+ }
428
+
250
429
  async function listCommand(argv: string[]) {
251
- if (argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
430
+ const { argv: contextualArgv, context } = resolveContextualOptions(argv, {
431
+ allowSource: true,
432
+ });
433
+
434
+ if (
435
+ contextualArgv.includes("--help") ||
436
+ contextualArgv.includes("-h") ||
437
+ contextualArgv[0] === "help"
438
+ ) {
252
439
  printListHelp();
253
440
  return;
254
441
  }
255
442
 
256
443
  let opts: ListCommandOptions;
257
444
  try {
258
- opts = parseListArgs(argv);
445
+ opts = parseListArgs(contextualArgv);
446
+ if (context.sourceKind) {
447
+ opts.filters.sourceKind = context.sourceKind;
448
+ }
449
+ const scopeFilter = scopeFilterForMode(context.scopeMode);
450
+ if (scopeFilter) {
451
+ opts.filters.scope = scopeFilter;
452
+ }
259
453
  } catch (err) {
260
454
  console.error(err instanceof Error ? err.message : String(err));
261
455
  process.exitCode = 1;
@@ -264,14 +458,25 @@ async function listCommand(argv: string[]) {
264
458
 
265
459
  let index: FacultIndex;
266
460
  try {
267
- index = await loadIndex();
461
+ index = await loadIndex({
462
+ rootDir: resolveCliContextRoot({
463
+ rootArg: context.rootArg,
464
+ scope: context.scopeMode,
465
+ cwd: process.cwd(),
466
+ }),
467
+ });
268
468
  } catch (err) {
269
469
  console.error(err instanceof Error ? err.message : String(err));
270
470
  process.exitCode = 1;
271
471
  return;
272
472
  }
273
473
 
274
- let entries: SkillEntry[] | McpEntry[] | AgentEntry[] | SnippetEntry[] = [];
474
+ let entries:
475
+ | SkillEntry[]
476
+ | McpEntry[]
477
+ | AgentEntry[]
478
+ | SnippetEntry[]
479
+ | InstructionEntry[] = [];
275
480
 
276
481
  switch (opts.kind) {
277
482
  case "skills":
@@ -286,6 +491,9 @@ async function listCommand(argv: string[]) {
286
491
  case "snippets":
287
492
  entries = filterSnippets(index.snippets ?? {}, opts.filters);
288
493
  break;
494
+ case "instructions":
495
+ entries = filterInstructions(index.instructions ?? {}, opts.filters);
496
+ break;
289
497
  default:
290
498
  entries = [];
291
499
  break;
@@ -307,7 +515,7 @@ async function listCommand(argv: string[]) {
307
515
  const trustedLabel = meta.trusted === true ? "trusted" : "untrusted";
308
516
  const auditLabel = (meta.auditStatus ?? "pending").trim().toLowerCase();
309
517
  console.log(
310
- `${skill.name}${desc}\t[${trustedLabel}; audit=${auditLabel}]`
518
+ `${skill.name}${desc}\t[${trustedLabel}; audit=${auditLabel}]${formatSourceMeta(skill)}`
311
519
  );
312
520
  } else if (opts.kind === "mcp") {
313
521
  const meta = entry as McpEntry & {
@@ -316,9 +524,11 @@ async function listCommand(argv: string[]) {
316
524
  };
317
525
  const trustedLabel = meta.trusted === true ? "trusted" : "untrusted";
318
526
  const auditLabel = (meta.auditStatus ?? "pending").trim().toLowerCase();
319
- console.log(`${entry.name}\t[${trustedLabel}; audit=${auditLabel}]`);
527
+ console.log(
528
+ `${entry.name}\t[${trustedLabel}; audit=${auditLabel}]${formatSourceMeta(entry)}`
529
+ );
320
530
  } else {
321
- console.log(entry.name);
531
+ console.log(`${entry.name}${formatSourceMeta(entry)}`);
322
532
  }
323
533
  }
324
534
  }
@@ -331,6 +541,60 @@ async function readEntryContents(entryPath: string): Promise<string> {
331
541
  return file.text();
332
542
  }
333
543
 
544
+ async function findCommand(argv: string[]) {
545
+ const { argv: contextualArgv, context } = resolveContextualOptions(argv, {
546
+ allowSource: true,
547
+ });
548
+
549
+ if (
550
+ contextualArgv.includes("--help") ||
551
+ contextualArgv.includes("-h") ||
552
+ contextualArgv[0] === "help"
553
+ ) {
554
+ printFindHelp();
555
+ return;
556
+ }
557
+
558
+ let opts: FindCommandOptions;
559
+ try {
560
+ opts = parseFindArgs(contextualArgv);
561
+ } catch (err) {
562
+ console.error(err instanceof Error ? err.message : String(err));
563
+ process.exitCode = 1;
564
+ return;
565
+ }
566
+
567
+ let index: FacultIndex;
568
+ try {
569
+ index = await loadIndex({
570
+ rootDir: resolveCliContextRoot({
571
+ rootArg: context.rootArg,
572
+ scope: context.scopeMode,
573
+ cwd: process.cwd(),
574
+ }),
575
+ });
576
+ } catch (err) {
577
+ console.error(err instanceof Error ? err.message : String(err));
578
+ process.exitCode = 1;
579
+ return;
580
+ }
581
+
582
+ const matches = findCapabilities(index, {
583
+ text: opts.text,
584
+ sourceKind: context.sourceKind,
585
+ scope: scopeFilterForMode(context.scopeMode),
586
+ });
587
+ if (opts.json) {
588
+ console.log(`${JSON.stringify(matches, null, 2)}`);
589
+ return;
590
+ }
591
+
592
+ for (const entry of matches) {
593
+ const desc = entry.description ? `\t${entry.description}` : "";
594
+ console.log(`${entry.kind}:${entry.name}${desc}${formatSourceMeta(entry)}`);
595
+ }
596
+ }
597
+
334
598
  const SECRET_KEY_RE = /(TOKEN|KEY|SECRET|PASSWORD|PASS|BEARER)/i;
335
599
  const SECRETY_STRING_RE =
336
600
  /\b(sk-[A-Za-z0-9]{10,}|ghp_[A-Za-z0-9]{10,}|github_pat_[A-Za-z0-9_]{10,})\b/g;
@@ -339,6 +603,21 @@ function redactPossibleSecrets(value: string): string {
339
603
  return value.replace(SECRETY_STRING_RE, "<redacted>");
340
604
  }
341
605
 
606
+ function formatSourceMeta(entry: {
607
+ sourceKind?: string;
608
+ scope?: string;
609
+ }): string {
610
+ const source = entry.sourceKind?.trim();
611
+ const scope = entry.scope?.trim();
612
+ if (!(source || scope)) {
613
+ return "";
614
+ }
615
+ if (source && scope) {
616
+ return `\t[${source}/${scope}]`;
617
+ }
618
+ return `\t[${source ?? scope}]`;
619
+ }
620
+
342
621
  function sanitizeForDisplay(value: unknown): unknown {
343
622
  if (typeof value === "string") {
344
623
  return redactPossibleSecrets(value);
@@ -361,14 +640,22 @@ function sanitizeForDisplay(value: unknown): unknown {
361
640
  }
362
641
 
363
642
  async function showCommand(argv: string[]) {
364
- if (argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
643
+ const { argv: contextualArgv, context } = resolveContextualOptions(argv, {
644
+ allowSource: true,
645
+ });
646
+
647
+ if (
648
+ contextualArgv.includes("--help") ||
649
+ contextualArgv.includes("-h") ||
650
+ contextualArgv[0] === "help"
651
+ ) {
365
652
  printShowHelp();
366
653
  return;
367
654
  }
368
655
 
369
656
  let showSecrets = false;
370
657
  let raw: string | null = null;
371
- for (const arg of argv) {
658
+ for (const arg of contextualArgv) {
372
659
  if (!arg) {
373
660
  continue;
374
661
  }
@@ -396,7 +683,13 @@ async function showCommand(argv: string[]) {
396
683
 
397
684
  let index: FacultIndex;
398
685
  try {
399
- index = await loadIndex();
686
+ index = await loadIndex({
687
+ rootDir: resolveCliContextRoot({
688
+ rootArg: context.rootArg,
689
+ scope: context.scopeMode,
690
+ cwd: process.cwd(),
691
+ }),
692
+ });
400
693
  } catch (err) {
401
694
  console.error(err instanceof Error ? err.message : String(err));
402
695
  process.exitCode = 1;
@@ -409,25 +702,60 @@ async function showCommand(argv: string[]) {
409
702
  if (raw.startsWith("mcp:")) {
410
703
  kind = "mcp";
411
704
  name = raw.slice("mcp:".length);
412
- }
413
-
414
- let entry: SkillEntry | McpEntry | AgentEntry | SnippetEntry | null = null;
705
+ } else if (raw.startsWith("instruction:")) {
706
+ kind = "instructions";
707
+ name = raw.slice("instruction:".length);
708
+ } else if (raw.startsWith("instructions:")) {
709
+ kind = "instructions";
710
+ name = raw.slice("instructions:".length);
711
+ }
712
+
713
+ let entry:
714
+ | SkillEntry
715
+ | McpEntry
716
+ | AgentEntry
717
+ | SnippetEntry
718
+ | InstructionEntry
719
+ | null = null;
415
720
  const skill = index.skills[name];
416
721
  const mcpServer = index.mcp?.servers?.[name];
417
722
  const agent = index.agents?.[name];
418
723
  const snippet = index.snippets?.[name];
724
+ const instruction = index.instructions?.[name];
725
+ const matchesContext = (candidate: {
726
+ sourceKind?: string;
727
+ scope?: string;
728
+ }): boolean => {
729
+ if (context.sourceKind && candidate.sourceKind !== context.sourceKind) {
730
+ return false;
731
+ }
732
+ const scopeFilter = scopeFilterForMode(context.scopeMode);
733
+ if (scopeFilter && candidate.scope !== scopeFilter) {
734
+ return false;
735
+ }
736
+ return true;
737
+ };
419
738
 
420
- if (kind === "skills" && skill) {
739
+ if (kind === "skills" && skill && matchesContext(skill)) {
421
740
  entry = skill;
422
- } else if (kind === "mcp" && mcpServer) {
741
+ } else if (kind === "mcp" && mcpServer && matchesContext(mcpServer)) {
423
742
  entry = mcpServer;
424
- } else if (kind === "skills" && agent) {
743
+ } else if (kind === "skills" && agent && matchesContext(agent)) {
425
744
  kind = "agents";
426
745
  entry = agent;
427
- } else if (kind === "skills" && snippet) {
746
+ } else if (kind === "skills" && snippet && matchesContext(snippet)) {
428
747
  kind = "snippets";
429
748
  entry = snippet;
430
- } else if (kind === "skills" && mcpServer) {
749
+ } else if (
750
+ kind === "instructions" &&
751
+ instruction &&
752
+ matchesContext(instruction)
753
+ ) {
754
+ entry = instruction;
755
+ } else if (kind === "skills" && instruction && matchesContext(instruction)) {
756
+ kind = "instructions";
757
+ entry = instruction;
758
+ } else if (kind === "skills" && mcpServer && matchesContext(mcpServer)) {
431
759
  kind = "mcp";
432
760
  entry = mcpServer;
433
761
  }
@@ -474,6 +802,91 @@ async function showCommand(argv: string[]) {
474
802
  console.log(displayContents);
475
803
  }
476
804
 
805
+ async function graphCommand(argv: string[]) {
806
+ const { argv: contextualArgv, context } = resolveContextualOptions(argv, {
807
+ allowSource: true,
808
+ });
809
+
810
+ if (
811
+ contextualArgv.includes("--help") ||
812
+ contextualArgv.includes("-h") ||
813
+ contextualArgv[0] === "help"
814
+ ) {
815
+ printGraphHelp();
816
+ return;
817
+ }
818
+
819
+ let opts: GraphCommandOptions;
820
+ try {
821
+ opts = parseGraphArgs(contextualArgv);
822
+ } catch (err) {
823
+ console.error(err instanceof Error ? err.message : String(err));
824
+ process.exitCode = 1;
825
+ return;
826
+ }
827
+
828
+ try {
829
+ const graph = await loadGraph({
830
+ rootDir: resolveCliContextRoot({
831
+ rootArg: context.rootArg,
832
+ scope: context.scopeMode,
833
+ cwd: process.cwd(),
834
+ }),
835
+ });
836
+ const node = resolveGraphNode(graph, opts.target, {
837
+ sourceKind: context.sourceKind,
838
+ scope: scopeFilterForMode(context.scopeMode),
839
+ });
840
+ if (!node) {
841
+ throw new Error(`Graph node not found: ${opts.target}`);
842
+ }
843
+
844
+ const deps = graphDependencies(graph, node.id);
845
+ const dependents = graphDependents(graph, node.id);
846
+
847
+ if (opts.json) {
848
+ if (opts.kind === "show") {
849
+ console.log(
850
+ JSON.stringify({ node, dependencies: deps, dependents }, null, 2)
851
+ );
852
+ } else if (opts.kind === "deps") {
853
+ console.log(JSON.stringify(deps, null, 2));
854
+ } else {
855
+ console.log(JSON.stringify(dependents, null, 2));
856
+ }
857
+ return;
858
+ }
859
+
860
+ if (opts.kind === "show") {
861
+ console.log(node.id);
862
+ console.log(JSON.stringify(node, null, 2));
863
+ console.log("\nDependencies:");
864
+ for (const dep of deps) {
865
+ console.log(
866
+ `- ${dep.edge.kind}\t${dep.node.id}\t(${dep.edge.locator})`
867
+ );
868
+ }
869
+ console.log("\nDependents:");
870
+ for (const dependent of dependents) {
871
+ console.log(
872
+ `- ${dependent.edge.kind}\t${dependent.node.id}\t(${dependent.edge.locator})`
873
+ );
874
+ }
875
+ return;
876
+ }
877
+
878
+ const relations = opts.kind === "deps" ? deps : dependents;
879
+ for (const relation of relations) {
880
+ console.log(
881
+ `${relation.edge.kind}\t${relation.node.id}\t(${relation.edge.locator})`
882
+ );
883
+ }
884
+ } catch (err) {
885
+ console.error(err instanceof Error ? err.message : String(err));
886
+ process.exitCode = 1;
887
+ }
888
+ }
889
+
477
890
  function adaptersCommand(argv: string[]) {
478
891
  if (argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
479
892
  console.log(
@@ -530,6 +943,15 @@ async function main(argv: string[]) {
530
943
  case "show":
531
944
  await showCommand(rest);
532
945
  return;
946
+ case "find":
947
+ await findCommand(rest);
948
+ return;
949
+ case "graph":
950
+ await graphCommand(rest);
951
+ return;
952
+ case "ai":
953
+ await aiCommand(rest);
954
+ return;
533
955
  case "adapters":
534
956
  await adaptersCommand(rest);
535
957
  return;