@versatly/workgraph 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,19 +1,28 @@
1
1
  import {
2
2
  bases_exports,
3
+ board_exports,
3
4
  command_center_exports,
5
+ dispatch_exports,
6
+ graph_exports,
4
7
  ledger_exports,
8
+ onboard_exports,
9
+ orientation_exports,
10
+ policy_exports,
11
+ query_exports,
5
12
  registry_exports,
13
+ search_qmd_adapter_exports,
6
14
  skill_exports,
7
15
  store_exports,
8
16
  thread_exports,
17
+ trigger_exports,
9
18
  workspace_exports
10
- } from "./chunk-CRQXDCPR.js";
19
+ } from "./chunk-XUMA4O2Z.js";
11
20
 
12
21
  // src/cli.ts
13
22
  import fs from "fs";
14
23
  import path from "path";
15
24
  import { Command } from "commander";
16
- var DEFAULT_ACTOR = process.env.WORKGRAPH_AGENT || process.env.CLAWVAULT_AGENT || process.env.USER || "anonymous";
25
+ var DEFAULT_ACTOR = process.env.WORKGRAPH_AGENT || process.env.USER || "anonymous";
17
26
  var CLI_VERSION = (() => {
18
27
  try {
19
28
  const pkgUrl = new URL("../package.json", import.meta.url);
@@ -271,7 +280,7 @@ addWorkspaceOption(
271
280
  );
272
281
  var basesCmd = program.command("bases").description("Generate Obsidian .base files from primitive-registry.yaml");
273
282
  addWorkspaceOption(
274
- basesCmd.command("sync-registry").description("Sync .clawvault/primitive-registry.yaml from active registry").option("--json", "Emit structured JSON output")
283
+ basesCmd.command("sync-registry").description("Sync .workgraph/primitive-registry.yaml from active registry").option("--json", "Emit structured JSON output")
275
284
  ).action(
276
285
  (opts) => runCommand(
277
286
  opts,
@@ -280,7 +289,7 @@ addWorkspaceOption(
280
289
  const manifest = bases_exports.syncPrimitiveRegistryManifest(workspacePath);
281
290
  return {
282
291
  primitiveCount: manifest.primitives.length,
283
- manifestPath: ".clawvault/primitive-registry.yaml"
292
+ manifestPath: ".workgraph/primitive-registry.yaml"
284
293
  };
285
294
  },
286
295
  (result) => [
@@ -290,7 +299,7 @@ addWorkspaceOption(
290
299
  )
291
300
  );
292
301
  addWorkspaceOption(
293
- basesCmd.command("generate").description("Generate .base files by reading primitive-registry.yaml").option("--all", "Include non-canonical primitives").option("--refresh-registry", "Refresh primitive-registry.yaml before generation").option("--output-dir <path>", "Output directory for .base files (default: .clawvault/bases)").option("--json", "Emit structured JSON output")
302
+ basesCmd.command("generate").description("Generate .base files by reading primitive-registry.yaml").option("--all", "Include non-canonical primitives").option("--refresh-registry", "Refresh primitive-registry.yaml before generation").option("--output-dir <path>", "Output directory for .base files (default: .workgraph/bases)").option("--json", "Emit structured JSON output")
294
303
  ).action(
295
304
  (opts) => runCommand(
296
305
  opts,
@@ -330,17 +339,7 @@ addWorkspaceOption(
330
339
  opts,
331
340
  () => {
332
341
  const workspacePath = resolveWorkspacePath(opts);
333
- const fields = { title };
334
- for (const pair of opts.set ?? []) {
335
- const eqIdx = String(pair).indexOf("=");
336
- if (eqIdx === -1) continue;
337
- const key = String(pair).slice(0, eqIdx).trim();
338
- let value = String(pair).slice(eqIdx + 1).trim();
339
- if (typeof value === "string" && value.includes(",")) {
340
- value = value.split(",").map((v) => v.trim());
341
- }
342
- fields[key] = value;
343
- }
342
+ const fields = { title, ...parseSetPairs(opts.set ?? []) };
344
343
  return {
345
344
  instance: store_exports.create(workspacePath, type, fields, opts.body, opts.actor)
346
345
  };
@@ -348,9 +347,28 @@ addWorkspaceOption(
348
347
  (result) => [`Created ${result.instance.type}: ${result.instance.path}`]
349
348
  )
350
349
  );
350
+ addWorkspaceOption(
351
+ primitiveCmd.command("update <path>").description("Update an existing primitive instance").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--set <fields...>", 'Set fields as "key=value"').option("--body <text>", "Replace markdown body content").option("--body-file <path>", "Read markdown body content from file").option("--json", "Emit structured JSON output")
352
+ ).action(
353
+ (targetPath, opts) => runCommand(
354
+ opts,
355
+ () => {
356
+ const workspacePath = resolveWorkspacePath(opts);
357
+ const updates = parseSetPairs(opts.set ?? []);
358
+ let body = opts.body;
359
+ if (opts.bodyFile) {
360
+ body = fs.readFileSync(path.resolve(opts.bodyFile), "utf-8");
361
+ }
362
+ return {
363
+ instance: store_exports.update(workspacePath, targetPath, updates, body, opts.actor)
364
+ };
365
+ },
366
+ (result) => [`Updated ${result.instance.type}: ${result.instance.path}`]
367
+ )
368
+ );
351
369
  var skillCmd = program.command("skill").description("Manage native skill primitives in shared workgraph vaults");
352
370
  addWorkspaceOption(
353
- skillCmd.command("write <title>").description("Create or update a skill primitive").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--owner <name>", "Skill owner").option("--version <semver>", "Skill version").option("--status <status>", "draft | proposed | active | deprecated | archived").option("--distribution <mode>", "Distribution mode", "tailscale-shared-vault").option("--tailscale-path <path>", "Shared Tailscale workspace path").option("--reviewers <list>", "Comma-separated reviewer names").option("--tags <list>", "Comma-separated tags").option("--body <text>", "Skill markdown content").option("--body-file <path>", "Read markdown content from file").option("--json", "Emit structured JSON output")
371
+ skillCmd.command("write <title>").description("Create or update a skill primitive").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--owner <name>", "Skill owner").option("--skill-version <semver>", "Skill version").option("--status <status>", "draft | proposed | active | deprecated | archived").option("--distribution <mode>", "Distribution mode", "tailscale-shared-vault").option("--tailscale-path <path>", "Shared Tailscale workspace path").option("--reviewers <list>", "Comma-separated reviewer names").option("--depends-on <list>", "Comma-separated skill dependencies (slug/path)").option("--expected-updated-at <iso>", "Optimistic concurrency guard for updates").option("--tags <list>", "Comma-separated tags").option("--body <text>", "Skill markdown content").option("--body-file <path>", "Read markdown content from file").option("--json", "Emit structured JSON output")
354
372
  ).action(
355
373
  (title, opts) => runCommand(
356
374
  opts,
@@ -368,11 +386,13 @@ addWorkspaceOption(
368
386
  opts.actor,
369
387
  {
370
388
  owner: opts.owner,
371
- version: opts.version,
389
+ version: opts.skillVersion,
372
390
  status: opts.status,
373
391
  distribution: opts.distribution,
374
392
  tailscalePath: opts.tailscalePath,
375
393
  reviewers: csv(opts.reviewers),
394
+ dependsOn: csv(opts.dependsOn),
395
+ expectedUpdatedAt: opts.expectedUpdatedAt,
376
396
  tags: csv(opts.tags)
377
397
  }
378
398
  );
@@ -403,18 +423,54 @@ addWorkspaceOption(
403
423
  )
404
424
  );
405
425
  addWorkspaceOption(
406
- skillCmd.command("list").description("List skills").option("--status <status>", "Filter by status").option("--json", "Emit structured JSON output")
426
+ skillCmd.command("list").description("List skills").option("--status <status>", "Filter by status").option("--updated-since <iso>", "Filter by updated timestamp (ISO-8601)").option("--json", "Emit structured JSON output")
407
427
  ).action(
408
428
  (opts) => runCommand(
409
429
  opts,
410
430
  () => {
411
431
  const workspacePath = resolveWorkspacePath(opts);
412
- const skills = skill_exports.listSkills(workspacePath, { status: opts.status });
432
+ const skills = skill_exports.listSkills(workspacePath, {
433
+ status: opts.status,
434
+ updatedSince: opts.updatedSince
435
+ });
413
436
  return { skills, count: skills.length };
414
437
  },
415
438
  (result) => result.skills.map((skill) => `${String(skill.fields.title)} [${String(skill.fields.status)}] -> ${skill.path}`)
416
439
  )
417
440
  );
441
+ addWorkspaceOption(
442
+ skillCmd.command("history <skillRef>").description("Show ledger history entries for one skill").option("--limit <n>", "Limit number of returned entries").option("--json", "Emit structured JSON output")
443
+ ).action(
444
+ (skillRef, opts) => runCommand(
445
+ opts,
446
+ () => {
447
+ const workspacePath = resolveWorkspacePath(opts);
448
+ return {
449
+ entries: skill_exports.skillHistory(workspacePath, skillRef, {
450
+ limit: opts.limit ? Number.parseInt(String(opts.limit), 10) : void 0
451
+ })
452
+ };
453
+ },
454
+ (result) => result.entries.map((entry) => `${entry.ts} ${entry.op} ${entry.actor}`)
455
+ )
456
+ );
457
+ addWorkspaceOption(
458
+ skillCmd.command("diff <skillRef>").description("Show latest field-change summary for one skill").option("--json", "Emit structured JSON output")
459
+ ).action(
460
+ (skillRef, opts) => runCommand(
461
+ opts,
462
+ () => {
463
+ const workspacePath = resolveWorkspacePath(opts);
464
+ return skill_exports.skillDiff(workspacePath, skillRef);
465
+ },
466
+ (result) => [
467
+ `Skill: ${result.path}`,
468
+ `Latest: ${result.latestEntryTs ?? "none"}`,
469
+ `Previous: ${result.previousEntryTs ?? "none"}`,
470
+ `Changed fields: ${result.changedFields.join(", ") || "none"}`
471
+ ]
472
+ )
473
+ );
418
474
  addWorkspaceOption(
419
475
  skillCmd.command("propose <skillRef>").description("Move a skill into proposed state and open review thread").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--proposal-thread <path>", "Explicit proposal thread path").option("--no-create-thread", "Do not create a proposal thread automatically").option("--space <spaceRef>", "Space for created proposal thread").option("--reviewers <list>", "Comma-separated reviewers").option("--json", "Emit structured JSON output")
420
476
  ).action(
@@ -438,7 +494,7 @@ addWorkspaceOption(
438
494
  )
439
495
  );
440
496
  addWorkspaceOption(
441
- skillCmd.command("promote <skillRef>").description("Promote a proposed/draft skill to active").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--version <semver>", "Explicit promoted version").option("--json", "Emit structured JSON output")
497
+ skillCmd.command("promote <skillRef>").description("Promote a proposed/draft skill to active").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--skill-version <semver>", "Explicit promoted version").option("--json", "Emit structured JSON output")
442
498
  ).action(
443
499
  (skillRef, opts) => runCommand(
444
500
  opts,
@@ -446,7 +502,7 @@ addWorkspaceOption(
446
502
  const workspacePath = resolveWorkspacePath(opts);
447
503
  return {
448
504
  skill: skill_exports.promoteSkill(workspacePath, skillRef, opts.actor, {
449
- version: opts.version
505
+ version: opts.skillVersion
450
506
  })
451
507
  };
452
508
  },
@@ -601,6 +657,456 @@ addWorkspaceOption(
601
657
  ]
602
658
  )
603
659
  );
660
+ addWorkspaceOption(
661
+ program.command("status").description("Show workspace situational status snapshot").option("--json", "Emit structured JSON output")
662
+ ).action(
663
+ (opts) => runCommand(
664
+ opts,
665
+ () => {
666
+ const workspacePath = resolveWorkspacePath(opts);
667
+ return orientation_exports.statusSnapshot(workspacePath);
668
+ },
669
+ (result) => [
670
+ `Threads: total=${result.threads.total} open=${result.threads.open} active=${result.threads.active} blocked=${result.threads.blocked} done=${result.threads.done}`,
671
+ `Ready threads: ${result.threads.ready} Active claims: ${result.claims.active}`,
672
+ `Primitive types: ${Object.keys(result.primitives.byType).length}`
673
+ ]
674
+ )
675
+ );
676
+ addWorkspaceOption(
677
+ program.command("brief").description("Show actor-centric operational brief").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--recent <count>", "Recent activity count", "12").option("--next <count>", "Next ready threads to include", "5").option("--json", "Emit structured JSON output")
678
+ ).action(
679
+ (opts) => runCommand(
680
+ opts,
681
+ () => {
682
+ const workspacePath = resolveWorkspacePath(opts);
683
+ return orientation_exports.brief(workspacePath, opts.actor, {
684
+ recentCount: Number.parseInt(String(opts.recent), 10),
685
+ nextCount: Number.parseInt(String(opts.next), 10)
686
+ });
687
+ },
688
+ (result) => [
689
+ `Brief for ${result.actor}`,
690
+ `My claims: ${result.myClaims.length}`,
691
+ `Blocked threads: ${result.blockedThreads.length}`,
692
+ `Next ready: ${result.nextReadyThreads.map((item) => item.path).join(", ") || "none"}`
693
+ ]
694
+ )
695
+ );
696
+ addWorkspaceOption(
697
+ program.command("checkpoint <summary>").description("Create a checkpoint primitive for hand-off continuity").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--next <items>", "Comma-separated next actions").option("--blocked <items>", "Comma-separated blockers").option("--tags <items>", "Comma-separated tags").option("--json", "Emit structured JSON output")
698
+ ).action(
699
+ (summary, opts) => runCommand(
700
+ opts,
701
+ () => {
702
+ const workspacePath = resolveWorkspacePath(opts);
703
+ return {
704
+ checkpoint: orientation_exports.checkpoint(workspacePath, opts.actor, summary, {
705
+ next: csv(opts.next),
706
+ blocked: csv(opts.blocked),
707
+ tags: csv(opts.tags)
708
+ })
709
+ };
710
+ },
711
+ (result) => [`Created checkpoint: ${result.checkpoint.path}`]
712
+ )
713
+ );
714
+ addWorkspaceOption(
715
+ program.command("intake <observation>").description("Capture intake observation as lightweight checkpoint note").option("-a, --actor <name>", "Agent name", DEFAULT_ACTOR).option("--tags <items>", "Comma-separated tags").option("--json", "Emit structured JSON output")
716
+ ).action(
717
+ (observation, opts) => runCommand(
718
+ opts,
719
+ () => {
720
+ const workspacePath = resolveWorkspacePath(opts);
721
+ return {
722
+ intake: orientation_exports.intake(workspacePath, opts.actor, observation, {
723
+ tags: csv(opts.tags)
724
+ })
725
+ };
726
+ },
727
+ (result) => [`Captured intake: ${result.intake.path}`]
728
+ )
729
+ );
730
+ addWorkspaceOption(
731
+ program.command("query").description("Query primitive instances with multi-field filters").option("--type <type>", "Primitive type").option("--status <status>", "Status value").option("--owner <owner>", "Owner/actor value").option("--tag <tag>", "Tag filter").option("--text <text>", "Full-text contains filter").option("--path-includes <text>", "Path substring filter").option("--updated-after <iso>", "Updated at or after").option("--updated-before <iso>", "Updated at or before").option("--created-after <iso>", "Created at or after").option("--created-before <iso>", "Created at or before").option("--limit <n>", "Result limit").option("--offset <n>", "Result offset").option("--json", "Emit structured JSON output")
732
+ ).action(
733
+ (opts) => runCommand(
734
+ opts,
735
+ () => {
736
+ const workspacePath = resolveWorkspacePath(opts);
737
+ const results = query_exports.queryPrimitives(workspacePath, {
738
+ type: opts.type,
739
+ status: opts.status,
740
+ owner: opts.owner,
741
+ tag: opts.tag,
742
+ text: opts.text,
743
+ pathIncludes: opts.pathIncludes,
744
+ updatedAfter: opts.updatedAfter,
745
+ updatedBefore: opts.updatedBefore,
746
+ createdAfter: opts.createdAfter,
747
+ createdBefore: opts.createdBefore,
748
+ limit: opts.limit ? Number.parseInt(String(opts.limit), 10) : void 0,
749
+ offset: opts.offset ? Number.parseInt(String(opts.offset), 10) : void 0
750
+ });
751
+ return { results, count: results.length };
752
+ },
753
+ (result) => result.results.map((item) => `${item.type} ${item.path}`)
754
+ )
755
+ );
756
+ addWorkspaceOption(
757
+ program.command("search <text>").description("Keyword search across markdown body/frontmatter with optional QMD-compatible mode").option("--type <type>", "Limit to primitive type").option("--mode <mode>", "auto | core | qmd", "auto").option("--limit <n>", "Result limit").option("--json", "Emit structured JSON output")
758
+ ).action(
759
+ (text, opts) => runCommand(
760
+ opts,
761
+ () => {
762
+ const workspacePath = resolveWorkspacePath(opts);
763
+ const result = search_qmd_adapter_exports.search(workspacePath, text, {
764
+ mode: opts.mode,
765
+ type: opts.type,
766
+ limit: opts.limit ? Number.parseInt(String(opts.limit), 10) : void 0
767
+ });
768
+ return {
769
+ ...result,
770
+ count: result.results.length
771
+ };
772
+ },
773
+ (result) => [
774
+ `Mode: ${result.mode}`,
775
+ ...result.fallbackReason ? [`Note: ${result.fallbackReason}`] : [],
776
+ ...result.results.map((item) => `${item.type} ${item.path}`)
777
+ ]
778
+ )
779
+ );
780
+ var boardCmd = program.command("board").description("Generate and sync Obsidian Kanban board views");
781
+ addWorkspaceOption(
782
+ boardCmd.command("generate").description("Generate Obsidian Kanban board markdown from thread states").option("-o, --output <path>", "Output board path", "ops/Workgraph Board.md").option("--include-cancelled", "Include cancelled lane").option("--json", "Emit structured JSON output")
783
+ ).action(
784
+ (opts) => runCommand(
785
+ opts,
786
+ () => {
787
+ const workspacePath = resolveWorkspacePath(opts);
788
+ return board_exports.generateKanbanBoard(workspacePath, {
789
+ outputPath: opts.output,
790
+ includeCancelled: !!opts.includeCancelled
791
+ });
792
+ },
793
+ (result) => [
794
+ `Generated board: ${result.outputPath}`,
795
+ `Backlog=${result.counts.backlog} InProgress=${result.counts.inProgress} Blocked=${result.counts.blocked} Done=${result.counts.done}`
796
+ ]
797
+ )
798
+ );
799
+ addWorkspaceOption(
800
+ boardCmd.command("sync").description("Sync existing board markdown from current thread states").option("-o, --output <path>", "Output board path", "ops/Workgraph Board.md").option("--include-cancelled", "Include cancelled lane").option("--json", "Emit structured JSON output")
801
+ ).action(
802
+ (opts) => runCommand(
803
+ opts,
804
+ () => {
805
+ const workspacePath = resolveWorkspacePath(opts);
806
+ return board_exports.syncKanbanBoard(workspacePath, {
807
+ outputPath: opts.output,
808
+ includeCancelled: !!opts.includeCancelled
809
+ });
810
+ },
811
+ (result) => [
812
+ `Synced board: ${result.outputPath}`,
813
+ `Backlog=${result.counts.backlog} InProgress=${result.counts.inProgress} Blocked=${result.counts.blocked} Done=${result.counts.done}`
814
+ ]
815
+ )
816
+ );
817
+ var graphCmd = program.command("graph").description("Wiki-link graph indexing and hygiene");
818
+ addWorkspaceOption(
819
+ graphCmd.command("index").description("Build wiki-link graph index").option("--json", "Emit structured JSON output")
820
+ ).action(
821
+ (opts) => runCommand(
822
+ opts,
823
+ () => {
824
+ const workspacePath = resolveWorkspacePath(opts);
825
+ return graph_exports.refreshWikiLinkGraphIndex(workspacePath);
826
+ },
827
+ (result) => [
828
+ `Nodes: ${result.nodes.length}`,
829
+ `Edges: ${result.edges.length}`,
830
+ `Broken links: ${result.brokenLinks.length}`
831
+ ]
832
+ )
833
+ );
834
+ addWorkspaceOption(
835
+ graphCmd.command("hygiene").description("Generate graph hygiene report (orphans, broken links, hubs)").option("--json", "Emit structured JSON output")
836
+ ).action(
837
+ (opts) => runCommand(
838
+ opts,
839
+ () => {
840
+ const workspacePath = resolveWorkspacePath(opts);
841
+ return graph_exports.graphHygieneReport(workspacePath);
842
+ },
843
+ (result) => [
844
+ `Nodes=${result.nodeCount} Edges=${result.edgeCount}`,
845
+ `Orphans=${result.orphanCount} BrokenLinks=${result.brokenLinkCount}`,
846
+ `Top hub: ${result.hubs[0]?.node ?? "none"}`
847
+ ]
848
+ )
849
+ );
850
+ addWorkspaceOption(
851
+ graphCmd.command("neighbors <nodePath>").description("Query incoming/outgoing wiki-link neighbors for one node").option("--refresh", "Refresh graph index before querying").option("--json", "Emit structured JSON output")
852
+ ).action(
853
+ (nodePath, opts) => runCommand(
854
+ opts,
855
+ () => {
856
+ const workspacePath = resolveWorkspacePath(opts);
857
+ return graph_exports.graphNeighborhood(workspacePath, nodePath, {
858
+ refresh: !!opts.refresh
859
+ });
860
+ },
861
+ (result) => [
862
+ `Node: ${result.node} (${result.exists ? "exists" : "missing"})`,
863
+ `Outgoing: ${result.outgoing.length}`,
864
+ `Incoming: ${result.incoming.length}`
865
+ ]
866
+ )
867
+ );
868
+ var policyCmd = program.command("policy").description("Manage policy parties and capabilities");
869
+ var policyPartyCmd = policyCmd.command("party").description("Manage registered policy parties");
870
+ addWorkspaceOption(
871
+ policyPartyCmd.command("upsert <id>").description("Create or update a policy party").option("--roles <roles>", "Comma-separated roles").option("--capabilities <caps>", "Comma-separated capabilities").option("--json", "Emit structured JSON output")
872
+ ).action(
873
+ (id, opts) => runCommand(
874
+ opts,
875
+ () => {
876
+ const workspacePath = resolveWorkspacePath(opts);
877
+ return {
878
+ party: policy_exports.upsertParty(workspacePath, id, {
879
+ roles: csv(opts.roles),
880
+ capabilities: csv(opts.capabilities)
881
+ })
882
+ };
883
+ },
884
+ (result) => [`Upserted policy party: ${result.party.id}`]
885
+ )
886
+ );
887
+ addWorkspaceOption(
888
+ policyPartyCmd.command("get <id>").description("Get one policy party").option("--json", "Emit structured JSON output")
889
+ ).action(
890
+ (id, opts) => runCommand(
891
+ opts,
892
+ () => {
893
+ const workspacePath = resolveWorkspacePath(opts);
894
+ const party = policy_exports.getParty(workspacePath, id);
895
+ if (!party) throw new Error(`Policy party not found: ${id}`);
896
+ return { party };
897
+ },
898
+ (result) => [`${result.party.id} roles=${result.party.roles.join(",")}`]
899
+ )
900
+ );
901
+ addWorkspaceOption(
902
+ policyPartyCmd.command("list").description("List policy parties").option("--json", "Emit structured JSON output")
903
+ ).action(
904
+ (opts) => runCommand(
905
+ opts,
906
+ () => {
907
+ const workspacePath = resolveWorkspacePath(opts);
908
+ const registry = policy_exports.loadPolicyRegistry(workspacePath);
909
+ return {
910
+ parties: Object.values(registry.parties)
911
+ };
912
+ },
913
+ (result) => result.parties.map((party) => `${party.id} [${party.roles.join(", ")}]`)
914
+ )
915
+ );
916
+ var dispatchCmd = program.command("dispatch").description("Programmatic runtime dispatch contract");
917
+ addWorkspaceOption(
918
+ dispatchCmd.command("create <objective>").description("Create a new run dispatch request").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--adapter <name>", "Adapter name", "cursor-cloud").option("--idempotency-key <key>", "Idempotency key").option("--json", "Emit structured JSON output")
919
+ ).action(
920
+ (objective, opts) => runCommand(
921
+ opts,
922
+ () => {
923
+ const workspacePath = resolveWorkspacePath(opts);
924
+ return {
925
+ run: dispatch_exports.createRun(workspacePath, {
926
+ actor: opts.actor,
927
+ adapter: opts.adapter,
928
+ objective,
929
+ idempotencyKey: opts.idempotencyKey
930
+ })
931
+ };
932
+ },
933
+ (result) => [`Run created: ${result.run.id} [${result.run.status}]`]
934
+ )
935
+ );
936
+ addWorkspaceOption(
937
+ dispatchCmd.command("list").description("List runs").option("--status <status>", "queued|running|succeeded|failed|cancelled").option("--limit <n>", "Result limit").option("--json", "Emit structured JSON output")
938
+ ).action(
939
+ (opts) => runCommand(
940
+ opts,
941
+ () => {
942
+ const workspacePath = resolveWorkspacePath(opts);
943
+ return {
944
+ runs: dispatch_exports.listRuns(workspacePath, {
945
+ status: opts.status,
946
+ limit: opts.limit ? Number.parseInt(String(opts.limit), 10) : void 0
947
+ })
948
+ };
949
+ },
950
+ (result) => result.runs.map((run) => `${run.id} [${run.status}] ${run.objective}`)
951
+ )
952
+ );
953
+ addWorkspaceOption(
954
+ dispatchCmd.command("status <runId>").description("Get run status by ID").option("--json", "Emit structured JSON output")
955
+ ).action(
956
+ (runId, opts) => runCommand(
957
+ opts,
958
+ () => {
959
+ const workspacePath = resolveWorkspacePath(opts);
960
+ return {
961
+ run: dispatch_exports.status(workspacePath, runId)
962
+ };
963
+ },
964
+ (result) => [`${result.run.id} [${result.run.status}]`]
965
+ )
966
+ );
967
+ addWorkspaceOption(
968
+ dispatchCmd.command("followup <runId> <input>").description("Send follow-up input to a run").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
969
+ ).action(
970
+ (runId, input, opts) => runCommand(
971
+ opts,
972
+ () => {
973
+ const workspacePath = resolveWorkspacePath(opts);
974
+ return {
975
+ run: dispatch_exports.followup(workspacePath, runId, opts.actor, input)
976
+ };
977
+ },
978
+ (result) => [`Follow-up recorded: ${result.run.id} [${result.run.status}]`]
979
+ )
980
+ );
981
+ addWorkspaceOption(
982
+ dispatchCmd.command("stop <runId>").description("Cancel a run").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
983
+ ).action(
984
+ (runId, opts) => runCommand(
985
+ opts,
986
+ () => {
987
+ const workspacePath = resolveWorkspacePath(opts);
988
+ return {
989
+ run: dispatch_exports.stop(workspacePath, runId, opts.actor)
990
+ };
991
+ },
992
+ (result) => [`Stopped run: ${result.run.id} [${result.run.status}]`]
993
+ )
994
+ );
995
+ addWorkspaceOption(
996
+ dispatchCmd.command("mark <runId>").description("Set run status transition explicitly").requiredOption("--status <status>", "running|succeeded|failed|cancelled").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--output <text>", "Optional output payload").option("--error <text>", "Optional error payload").option("--json", "Emit structured JSON output")
997
+ ).action(
998
+ (runId, opts) => runCommand(
999
+ opts,
1000
+ () => {
1001
+ const workspacePath = resolveWorkspacePath(opts);
1002
+ const status = normalizeRunStatus(opts.status);
1003
+ return {
1004
+ run: dispatch_exports.markRun(workspacePath, runId, opts.actor, status, {
1005
+ output: opts.output,
1006
+ error: opts.error
1007
+ })
1008
+ };
1009
+ },
1010
+ (result) => [`Marked run: ${result.run.id} [${result.run.status}]`]
1011
+ )
1012
+ );
1013
+ addWorkspaceOption(
1014
+ dispatchCmd.command("logs <runId>").description("Read logs from a run").option("--json", "Emit structured JSON output")
1015
+ ).action(
1016
+ (runId, opts) => runCommand(
1017
+ opts,
1018
+ () => {
1019
+ const workspacePath = resolveWorkspacePath(opts);
1020
+ return {
1021
+ runId,
1022
+ logs: dispatch_exports.logs(workspacePath, runId)
1023
+ };
1024
+ },
1025
+ (result) => result.logs.map((entry) => `${entry.ts} [${entry.level}] ${entry.message}`)
1026
+ )
1027
+ );
1028
+ var triggerCmd = program.command("trigger").description("Trigger primitives and run dispatch lifecycle");
1029
+ addWorkspaceOption(
1030
+ triggerCmd.command("fire <triggerPath>").description("Fire an approved/active trigger and dispatch a run").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--event-key <key>", "Deterministic event key for idempotency").option("--objective <text>", "Override run objective").option("--json", "Emit structured JSON output")
1031
+ ).action(
1032
+ (triggerPath, opts) => runCommand(
1033
+ opts,
1034
+ () => {
1035
+ const workspacePath = resolveWorkspacePath(opts);
1036
+ return trigger_exports.fireTrigger(workspacePath, triggerPath, {
1037
+ actor: opts.actor,
1038
+ eventKey: opts.eventKey,
1039
+ objective: opts.objective
1040
+ });
1041
+ },
1042
+ (result) => [
1043
+ `Fired trigger: ${result.triggerPath}`,
1044
+ `Run: ${result.run.id} [${result.run.status}]`
1045
+ ]
1046
+ )
1047
+ );
1048
+ addWorkspaceOption(
1049
+ program.command("onboard").description("Guided agent-first workspace setup and starter artifacts").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--spaces <list>", "Comma-separated space names").option("--no-demo-threads", "Skip starter onboarding threads").option("--json", "Emit structured JSON output")
1050
+ ).action(
1051
+ (opts) => runCommand(
1052
+ opts,
1053
+ () => {
1054
+ const workspacePath = resolveWorkspacePath(opts);
1055
+ return onboard_exports.onboardWorkspace(workspacePath, {
1056
+ actor: opts.actor,
1057
+ spaces: csv(opts.spaces),
1058
+ createDemoThreads: opts.demoThreads
1059
+ });
1060
+ },
1061
+ (result) => [
1062
+ `Onboarded actor: ${result.actor}`,
1063
+ `Spaces created: ${result.spacesCreated.length}`,
1064
+ `Threads created: ${result.threadsCreated.length}`,
1065
+ `Board: ${result.boardPath}`,
1066
+ `Command center: ${result.commandCenterPath}`,
1067
+ `Onboarding primitive: ${result.onboardingPath}`
1068
+ ]
1069
+ )
1070
+ );
1071
+ var onboardingCmd = program.command("onboarding").description("Manage onboarding primitive lifecycle");
1072
+ addWorkspaceOption(
1073
+ onboardingCmd.command("show <onboardingPath>").description("Show one onboarding primitive").option("--json", "Emit structured JSON output")
1074
+ ).action(
1075
+ (onboardingPath, opts) => runCommand(
1076
+ opts,
1077
+ () => {
1078
+ const workspacePath = resolveWorkspacePath(opts);
1079
+ const onboarding = store_exports.read(workspacePath, onboardingPath);
1080
+ if (!onboarding) throw new Error(`Onboarding primitive not found: ${onboardingPath}`);
1081
+ if (onboarding.type !== "onboarding") throw new Error(`Target is not onboarding primitive: ${onboardingPath}`);
1082
+ return { onboarding };
1083
+ },
1084
+ (result) => [
1085
+ `Onboarding: ${result.onboarding.path}`,
1086
+ `Status: ${String(result.onboarding.fields.status)}`,
1087
+ `Actor: ${String(result.onboarding.fields.actor)}`
1088
+ ]
1089
+ )
1090
+ );
1091
+ addWorkspaceOption(
1092
+ onboardingCmd.command("update <onboardingPath>").description("Update onboarding lifecycle status").requiredOption("--status <status>", "active|paused|completed").option("-a, --actor <name>", "Actor", DEFAULT_ACTOR).option("--json", "Emit structured JSON output")
1093
+ ).action(
1094
+ (onboardingPath, opts) => runCommand(
1095
+ opts,
1096
+ () => {
1097
+ const workspacePath = resolveWorkspacePath(opts);
1098
+ return {
1099
+ onboarding: onboard_exports.updateOnboardingStatus(
1100
+ workspacePath,
1101
+ onboardingPath,
1102
+ normalizeOnboardingStatus(opts.status),
1103
+ opts.actor
1104
+ )
1105
+ };
1106
+ },
1107
+ (result) => [`Updated onboarding: ${result.onboarding.path} [${String(result.onboarding.fields.status)}]`]
1108
+ )
1109
+ );
604
1110
  program.parse();
605
1111
  function addWorkspaceOption(command) {
606
1112
  return command.option("-w, --workspace <path>", "Workgraph workspace path").option("--vault <path>", "Alias for --workspace").option("--shared-vault <path>", "Shared vault path (e.g. mounted via Tailscale)");
@@ -610,13 +1116,58 @@ function resolveWorkspacePath(opts) {
610
1116
  if (explicit) return path.resolve(explicit);
611
1117
  if (process.env.WORKGRAPH_SHARED_VAULT) return path.resolve(process.env.WORKGRAPH_SHARED_VAULT);
612
1118
  if (process.env.WORKGRAPH_PATH) return path.resolve(process.env.WORKGRAPH_PATH);
613
- if (process.env.CLAWVAULT_PATH) return path.resolve(process.env.CLAWVAULT_PATH);
614
1119
  return process.cwd();
615
1120
  }
1121
+ function parseSetPairs(pairs) {
1122
+ const fields = {};
1123
+ for (const pair of pairs) {
1124
+ const eqIdx = String(pair).indexOf("=");
1125
+ if (eqIdx === -1) continue;
1126
+ const key = String(pair).slice(0, eqIdx).trim();
1127
+ const raw = String(pair).slice(eqIdx + 1).trim();
1128
+ if (!key) continue;
1129
+ fields[key] = parseScalar(raw);
1130
+ }
1131
+ return fields;
1132
+ }
616
1133
  function csv(value) {
617
1134
  if (!value) return void 0;
618
1135
  return String(value).split(",").map((s) => s.trim()).filter(Boolean);
619
1136
  }
1137
+ function parseScalar(value) {
1138
+ if (value === "true") return true;
1139
+ if (value === "false") return false;
1140
+ if (value === "null") return null;
1141
+ if (value === "") return "";
1142
+ if (/^-?\d+(\.\d+)?$/.test(value)) return Number(value);
1143
+ if (value.startsWith("[") && value.endsWith("]")) {
1144
+ const inner = value.slice(1, -1).trim();
1145
+ if (!inner) return [];
1146
+ return inner.split(",").map((item) => parseScalar(item.trim()));
1147
+ }
1148
+ if (value.includes(",")) {
1149
+ return value.split(",").map((item) => parseScalar(item.trim()));
1150
+ }
1151
+ try {
1152
+ return JSON.parse(value);
1153
+ } catch {
1154
+ return value;
1155
+ }
1156
+ }
1157
+ function normalizeRunStatus(status) {
1158
+ const normalized = String(status).toLowerCase();
1159
+ if (normalized === "running" || normalized === "succeeded" || normalized === "failed" || normalized === "cancelled") {
1160
+ return normalized;
1161
+ }
1162
+ throw new Error(`Invalid run status "${status}". Expected running|succeeded|failed|cancelled.`);
1163
+ }
1164
+ function normalizeOnboardingStatus(status) {
1165
+ const normalized = String(status).toLowerCase();
1166
+ if (normalized === "active" || normalized === "paused" || normalized === "completed") {
1167
+ return normalized;
1168
+ }
1169
+ throw new Error(`Invalid onboarding status "${status}". Expected active|paused|completed.`);
1170
+ }
620
1171
  function wantsJson(opts) {
621
1172
  if (opts.json) return true;
622
1173
  if (process.env.WORKGRAPH_JSON === "1") return true;