@synap-core/cli 1.6.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/commands/agent-run.d.ts +47 -0
  2. package/dist/commands/agent-run.js +302 -0
  3. package/dist/commands/agent-run.js.map +1 -0
  4. package/dist/commands/artifact.d.ts +26 -0
  5. package/dist/commands/artifact.js +76 -0
  6. package/dist/commands/artifact.js.map +1 -0
  7. package/dist/commands/automation.d.ts +47 -0
  8. package/dist/commands/automation.js +600 -0
  9. package/dist/commands/automation.js.map +1 -0
  10. package/dist/commands/cell.d.ts +55 -0
  11. package/dist/commands/cell.js +277 -0
  12. package/dist/commands/cell.js.map +1 -0
  13. package/dist/commands/connect.js +41 -7
  14. package/dist/commands/connect.js.map +1 -1
  15. package/dist/commands/connections.js +0 -16
  16. package/dist/commands/connections.js.map +1 -1
  17. package/dist/commands/connectors.d.ts +31 -0
  18. package/dist/commands/connectors.js +276 -0
  19. package/dist/commands/connectors.js.map +1 -0
  20. package/dist/commands/context.d.ts +22 -0
  21. package/dist/commands/context.js +151 -0
  22. package/dist/commands/context.js.map +1 -0
  23. package/dist/commands/data-extra.d.ts +13 -0
  24. package/dist/commands/data-extra.js +161 -0
  25. package/dist/commands/data-extra.js.map +1 -0
  26. package/dist/commands/data.d.ts +13 -9
  27. package/dist/commands/data.js +220 -109
  28. package/dist/commands/data.js.map +1 -1
  29. package/dist/commands/discover.d.ts +18 -0
  30. package/dist/commands/discover.js +71 -0
  31. package/dist/commands/discover.js.map +1 -0
  32. package/dist/commands/doc.d.ts +39 -0
  33. package/dist/commands/doc.js +130 -0
  34. package/dist/commands/doc.js.map +1 -0
  35. package/dist/commands/events.d.ts +26 -0
  36. package/dist/commands/events.js +68 -0
  37. package/dist/commands/events.js.map +1 -0
  38. package/dist/commands/explain.d.ts +21 -0
  39. package/dist/commands/explain.js +248 -0
  40. package/dist/commands/explain.js.map +1 -0
  41. package/dist/commands/graph.d.ts +18 -0
  42. package/dist/commands/graph.js +73 -0
  43. package/dist/commands/graph.js.map +1 -0
  44. package/dist/commands/home.d.ts +12 -0
  45. package/dist/commands/home.js +54 -0
  46. package/dist/commands/home.js.map +1 -0
  47. package/dist/commands/init.d.ts +0 -2
  48. package/dist/commands/init.js +62 -102
  49. package/dist/commands/init.js.map +1 -1
  50. package/dist/commands/keys.d.ts +9 -0
  51. package/dist/commands/keys.js +79 -0
  52. package/dist/commands/keys.js.map +1 -0
  53. package/dist/commands/knowledge.d.ts +32 -18
  54. package/dist/commands/knowledge.js +303 -53
  55. package/dist/commands/knowledge.js.map +1 -1
  56. package/dist/commands/observe.d.ts +9 -0
  57. package/dist/commands/observe.js +90 -0
  58. package/dist/commands/observe.js.map +1 -0
  59. package/dist/commands/open.d.ts +15 -0
  60. package/dist/commands/open.js +44 -0
  61. package/dist/commands/open.js.map +1 -0
  62. package/dist/commands/pods.d.ts +5 -0
  63. package/dist/commands/pods.js +85 -37
  64. package/dist/commands/pods.js.map +1 -1
  65. package/dist/commands/proposals-actions.d.ts +7 -0
  66. package/dist/commands/proposals-actions.js +40 -0
  67. package/dist/commands/proposals-actions.js.map +1 -0
  68. package/dist/commands/providers.d.ts +38 -0
  69. package/dist/commands/providers.js +170 -0
  70. package/dist/commands/providers.js.map +1 -0
  71. package/dist/commands/sessions.d.ts +31 -0
  72. package/dist/commands/sessions.js +224 -0
  73. package/dist/commands/sessions.js.map +1 -0
  74. package/dist/commands/skills-commands.d.ts +26 -0
  75. package/dist/commands/skills-commands.js +186 -0
  76. package/dist/commands/skills-commands.js.map +1 -0
  77. package/dist/commands/skills.d.ts +26 -0
  78. package/dist/commands/skills.js +195 -0
  79. package/dist/commands/skills.js.map +1 -0
  80. package/dist/commands/status.js +68 -30
  81. package/dist/commands/status.js.map +1 -1
  82. package/dist/commands/subscribe.d.ts +19 -0
  83. package/dist/commands/subscribe.js +91 -0
  84. package/dist/commands/subscribe.js.map +1 -0
  85. package/dist/commands/view.d.ts +57 -0
  86. package/dist/commands/view.js +194 -0
  87. package/dist/commands/view.js.map +1 -0
  88. package/dist/index.d.ts +3 -4
  89. package/dist/index.js +823 -206
  90. package/dist/index.js.map +1 -1
  91. package/dist/lib/agent-templates.d.ts +25 -0
  92. package/dist/lib/agent-templates.js +148 -0
  93. package/dist/lib/agent-templates.js.map +1 -0
  94. package/dist/lib/agents-config.d.ts +4 -1
  95. package/dist/lib/agents-config.js +19 -2
  96. package/dist/lib/agents-config.js.map +1 -1
  97. package/dist/lib/capture-lane.d.ts +49 -0
  98. package/dist/lib/capture-lane.js +79 -0
  99. package/dist/lib/capture-lane.js.map +1 -0
  100. package/dist/lib/hub-client.d.ts +14 -0
  101. package/dist/lib/hub-client.js +45 -3
  102. package/dist/lib/hub-client.js.map +1 -1
  103. package/dist/lib/pod.d.ts +22 -1
  104. package/dist/lib/pod.js +32 -2
  105. package/dist/lib/pod.js.map +1 -1
  106. package/dist/lib/skills-installer.d.ts +1 -1
  107. package/dist/lib/skills-installer.js +1 -1
  108. package/dist/lib/skills-installer.js.map +1 -1
  109. package/dist/lib/targets.d.ts +17 -1
  110. package/dist/lib/targets.js +744 -70
  111. package/dist/lib/targets.js.map +1 -1
  112. package/node_modules/@synap/hub-rest-client/dist/index.cjs +881 -0
  113. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +907 -0
  114. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +907 -0
  115. package/node_modules/@synap/hub-rest-client/dist/index.js +851 -0
  116. package/node_modules/@synap/hub-rest-client/package.json +45 -0
  117. package/node_modules/@synap/hub-rest-client/src/client.ts +1143 -0
  118. package/node_modules/@synap/hub-rest-client/src/errors.ts +30 -0
  119. package/node_modules/@synap/hub-rest-client/src/index.ts +111 -0
  120. package/node_modules/@synap/hub-rest-client/src/setup.ts +77 -0
  121. package/node_modules/@synap/hub-rest-client/src/types.ts +639 -0
  122. package/package.json +9 -2
  123. package/skills/synap/SKILL.md +374 -100
  124. package/skills/synap-ui/SKILL.md +12 -9
  125. package/dist/lib/templates.d.ts +0 -11
  126. package/dist/lib/templates.js +0 -13
  127. package/dist/lib/templates.js.map +0 -1
  128. package/dist/templates/agent-os.json +0 -3090
package/dist/index.js CHANGED
@@ -1,12 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @synap/cli — Connect OpenClaw to sovereign knowledge infrastructure
3
+ * @synap/cli — Connect AI agents to your Synap data pod
4
4
  *
5
5
  * Usage:
6
6
  * npx @synap/cli init Full setup: detect, harden, connect, install
7
- * npx @synap/cli connect Connect OpenClaw to an existing Synap pod
8
- * npx @synap/cli security-audit Check OpenClaw for known vulnerabilities
9
- * npx @synap/cli status Show pod + OpenClaw health
7
+ * npx @synap/cli connect Connect an AI surface to a Synap pod
8
+ * npx @synap/cli status Show pod health and API status
10
9
  * npx @synap/cli update Update skill + check for CLI updates
11
10
  *
12
11
  * Or install globally:
@@ -30,33 +29,23 @@ catch {
30
29
  const program = new Command();
31
30
  program
32
31
  .name("synap")
33
- .description("Synap CLI — connect OpenClaw to sovereign knowledge infrastructure")
34
- .version(version);
32
+ .description("Synap CLI — connect AI agents to your data pod")
33
+ .version(version)
34
+ .showSuggestionAfterError(true);
35
35
  program
36
36
  .command("init")
37
- .description("Full setup: detect OpenClaw, harden security, connect to Synap pod, install skill")
37
+ .description("Full setup: connect to a Synap pod and install the skill")
38
38
  .option("--pod-url <url>", "Synap pod URL (skip pod choice prompt)")
39
39
  .option("--api-key <key>", "Hub Protocol API key (skip key generation)")
40
- .option("--skip-security", "Skip security hardening step")
41
40
  .option("--skip-is", "Skip Intelligence Service provider setup")
42
41
  .action(async (opts) => {
43
42
  const { init } = await import("./commands/init.js");
44
43
  await init(opts);
45
44
  });
46
- program
47
- .command("finish")
48
- .description("One-shot post-install: skill, AI key, public domain, IS")
49
- .option("--skip-is", "Skip Intelligence Service provider setup")
50
- .option("--skip-ai-key", "Skip AI provider key setup")
51
- .option("--skip-domain", "Skip public domain exposure")
52
- .action(async (opts) => {
53
- const { finish } = await import("./commands/finish.js");
54
- await finish(opts);
55
- });
56
45
  program
57
46
  .command("connect")
58
- .description("Connect an AI surface (Claude Code, Claude Desktop, Cursor, Raycast, OpenClaw) to a Synap pod")
59
- .option("--target <name>", "AI surface: claude-code | claude-desktop | cursor | raycast | openclaw")
47
+ .description("Connect an AI surface (Claude Code, Claude Desktop, Cursor, Raycast) to a Synap pod")
48
+ .option("--target <name>", "AI surface: claude-code | claude-desktop | cursor | raycast | custom")
60
49
  .option("--pod-url <url>", "Synap pod URL")
61
50
  .option("--api-key <key>", "Hub Protocol API key")
62
51
  .option("--name <name>", "Pod profile name to save credentials under (default: 'default')")
@@ -87,7 +76,7 @@ pods
87
76
  pods
88
77
  .command("use <name>")
89
78
  .description("Switch active pod and propagate credentials to all agent surfaces")
90
- .option("--surface <surface>", "Only switch this surface (raycast, claude-code, claude-desktop, cursor, openclaw)")
79
+ .option("--surface <surface>", "Only switch this surface (raycast, claude-code, claude-desktop, cursor)")
91
80
  .action(async (name, opts) => {
92
81
  const { podsUse } = await import("./commands/pods.js");
93
82
  await podsUse(name, { surface: opts.surface });
@@ -100,19 +89,16 @@ pods
100
89
  const { podsRemove } = await import("./commands/pods.js");
101
90
  await podsRemove(name);
102
91
  });
103
- program
104
- .command("security-audit")
105
- .alias("audit")
106
- .description("Check OpenClaw configuration for known vulnerabilities")
107
- .option("--fix", "Auto-fix issues where possible")
108
- .option("--json", "Output results as JSON")
109
- .action(async (opts) => {
110
- const { securityAudit } = await import("./commands/security-audit.js");
111
- await securityAudit(opts);
92
+ pods
93
+ .command("reconnect [name]")
94
+ .description("Re-authenticate a saved pod (refresh API key without re-entering the URL)")
95
+ .action(async (name) => {
96
+ const { podsReconnect } = await import("./commands/pods.js");
97
+ await podsReconnect(name);
112
98
  });
113
99
  program
114
100
  .command("status")
115
- .description("Show Synap pod and OpenClaw health status")
101
+ .description("Show Synap pod health and API status")
116
102
  .action(async () => {
117
103
  const { status } = await import("./commands/status.js");
118
104
  await status();
@@ -126,101 +112,6 @@ program
126
112
  const { update } = await import("./commands/update.js");
127
113
  await update(opts);
128
114
  });
129
- const oc = program
130
- .command("openclaw")
131
- .alias("oc")
132
- .description("Access, configure, and connect OpenClaw");
133
- oc
134
- .command("status", { isDefault: true })
135
- .description("Overview: gateway status, AI key, skill, how to connect")
136
- .action(async () => {
137
- const { openclawOverview } = await import("./commands/openclaw.js");
138
- await openclawOverview();
139
- });
140
- oc
141
- .command("dashboard")
142
- .alias("dash")
143
- .description("Open the OpenClaw web UI (or print SSH tunnel instructions for remote servers)")
144
- .action(async () => {
145
- const { openclawDashboard } = await import("./commands/openclaw.js");
146
- openclawDashboard();
147
- });
148
- oc
149
- .command("connect")
150
- .description("Show MCP client configs for Claude Desktop, Cursor, Windsurf")
151
- .option("--client <name>", "Specific client: claude, cursor, windsurf")
152
- .action(async (opts) => {
153
- const { openclawConnect } = await import("./commands/openclaw.js");
154
- await openclawConnect(opts);
155
- });
156
- oc
157
- .command("configure")
158
- .alias("config")
159
- .description("Set AI provider API key via OpenClaw's own config system")
160
- .option("-i, --interactive", "Run OpenClaw's own interactive wizard")
161
- .option("--provider <name>", "Provider: anthropic | openai | google")
162
- .option("--key <key>", "API key (non-interactive)")
163
- .option("--model <id>", "Model id (e.g. anthropic/claude-sonnet-4-6)")
164
- .option("--show", "Print the current config")
165
- .action(async (opts) => {
166
- const { openclawConfigure } = await import("./commands/openclaw.js");
167
- await openclawConfigure(opts);
168
- });
169
- oc
170
- .command("token")
171
- .description("Print the OpenClaw gateway token (for MCP clients)")
172
- .option("--copy", "Copy to clipboard instead of printing")
173
- .option("--for <client>", "Print a pre-filled MCP config (claude, cursor, windsurf)")
174
- .action(async (opts) => {
175
- const { openclawToken } = await import("./commands/openclaw.js");
176
- openclawToken(opts);
177
- });
178
- oc
179
- .command("doctor")
180
- .description("Run OpenClaw's diagnostic (openclaw doctor)")
181
- .option("--fix", "Auto-fix detected issues")
182
- .action(async (opts) => {
183
- const { openclawDoctor } = await import("./commands/openclaw.js");
184
- openclawDoctor(opts);
185
- });
186
- oc
187
- .command("logs")
188
- .description("Tail OpenClaw container logs")
189
- .option("-n, --lines <n>", "Number of log lines", "50")
190
- .option("-f, --follow", "Follow log output")
191
- .action(async (opts) => {
192
- const { openclawLogs } = await import("./commands/openclaw.js");
193
- openclawLogs({ lines: opts.lines ? parseInt(opts.lines, 10) : 50, follow: opts.follow });
194
- });
195
- oc
196
- .command("restart")
197
- .description("Restart the OpenClaw container")
198
- .action(async () => {
199
- const { openclawRestart } = await import("./commands/openclaw.js");
200
- await openclawRestart();
201
- });
202
- oc
203
- .command("setup-domain")
204
- .description("Expose the OpenClaw dashboard via a public HTTPS subdomain (Caddy)")
205
- .action(async () => {
206
- const { openclawSetupDomain } = await import("./commands/openclaw.js");
207
- openclawSetupDomain();
208
- });
209
- oc
210
- .command("connections")
211
- .alias("conn")
212
- .description("Unified view: AI providers, skills, channels, MCP clients")
213
- .action(async () => {
214
- const { openclawConnections } = await import("./commands/openclaw.js");
215
- openclawConnections();
216
- });
217
- oc
218
- .command("open [section]")
219
- .description("Open the OpenClaw dashboard (optionally at a section: channels, skills, config, chat, sessions, logs)")
220
- .action(async (section) => {
221
- const { openclawOpen } = await import("./commands/openclaw.js");
222
- openclawOpen(section);
223
- });
224
115
  // ─── infra ────────────────────────────────────────────────────────────────────
225
116
  const infra = program
226
117
  .command("infra")
@@ -278,60 +169,66 @@ program
278
169
  program
279
170
  .command("connections")
280
171
  .alias("conn")
281
- .description("Show which pod each agent surface (Claude Code, Desktop, Cursor, OpenClaw) is connected to")
172
+ .description("Show which pod each agent surface (Claude Code, Desktop, Cursor, Raycast) is connected to")
282
173
  .action(async () => {
283
174
  const { connections } = await import("./commands/connections.js");
284
175
  await connections();
285
176
  });
286
177
  program
287
178
  .command("login")
288
- .description("Sign in to your Synap account via browser")
289
- .option("--token <token>", "Provide a Synap API token directly (for headless/server use)")
179
+ .description("Connect to a Synap pod (self-hosted or managed)")
180
+ .option("--reconnect [name]", "Re-authenticate a saved pod without re-entering its URL")
290
181
  .action(async (opts) => {
291
182
  const chalk = (await import("chalk")).default;
292
- const ora = (await import("ora")).default;
293
- const { login, loginWithToken, isLoggedIn } = await import("./lib/auth.js");
294
- // Headless path: token provided directly
295
- if (opts.token) {
296
- const spinner = ora("Validating token...").start();
297
- const creds = await loginWithToken(opts.token);
298
- if (creds) {
299
- spinner.succeed(`Authenticated as ${creds.email}`);
300
- }
301
- else {
302
- spinner.fail("Token is invalid or expired. Get a token at https://synap.live/account/tokens");
303
- }
183
+ const { listPodProfiles } = await import("./lib/pod.js");
184
+ // --reconnect: delegate to reconnect flow
185
+ if (opts.reconnect !== undefined) {
186
+ const name = typeof opts.reconnect === "string" ? opts.reconnect : undefined;
187
+ const { podsReconnect } = await import("./commands/pods.js");
188
+ await podsReconnect(name);
304
189
  return;
305
190
  }
306
- const status = await isLoggedIn();
307
- if (status.valid) {
308
- console.log(chalk.green(` Already logged in as ${status.email}`));
191
+ const profiles = listPodProfiles();
192
+ if (profiles.length === 0) {
193
+ // No pod saved — guide to pods add
194
+ console.log(chalk.dim(" No pod configured yet."));
195
+ console.log(chalk.blue(" Adding a new pod...\n"));
196
+ const { podsAdd } = await import("./commands/pods.js");
197
+ await podsAdd();
309
198
  return;
310
199
  }
311
- console.log(chalk.blue(" Opening browser to sign in..."));
312
- const spinner = ora("Waiting for authentication...").start();
313
- const creds = await login();
314
- if (creds) {
315
- spinner.succeed(`Authenticated as ${creds.email}`);
316
- }
317
- else {
318
- spinner.fail("Authentication timed out. Try: synap login --token <token>");
319
- console.log(chalk.dim(" Get a token at: https://synap.live/account/tokens"));
200
+ // Already have pods — show status and offer reconnect if any are unreachable
201
+ const { checkPodHealth } = await import("./lib/pod.js");
202
+ const ora = (await import("ora")).default;
203
+ const spinner = ora("Checking pod health...").start();
204
+ const checks = await Promise.all(profiles.map(async (p) => {
205
+ const h = await checkPodHealth(p.config.podUrl).catch(() => ({ healthy: false }));
206
+ return { ...p, healthy: h.healthy };
207
+ }));
208
+ spinner.stop();
209
+ console.log("\n Configured pods:\n");
210
+ for (const p of checks) {
211
+ const active = p.active ? chalk.green(" ● ") : " ";
212
+ const status = p.healthy ? chalk.green("ok") : chalk.red("unreachable");
213
+ console.log(` ${active}${chalk.bold(p.name.padEnd(14))} ${status} ${chalk.dim(p.config.podUrl)}`);
320
214
  }
321
- });
322
- program
323
- .command("logout")
324
- .description("Sign out and remove stored credentials")
325
- .action(async () => {
326
- const chalk = (await import("chalk")).default;
327
- const { logout, getStoredToken } = await import("./lib/auth.js");
328
- const creds = getStoredToken();
329
- logout();
330
- if (creds) {
331
- console.log(chalk.green(` Logged out (was: ${creds.email})`));
215
+ console.log();
216
+ const unreachable = checks.filter((p) => !p.healthy);
217
+ if (unreachable.length > 0) {
218
+ console.log(chalk.yellow(` ${unreachable.length} pod(s) unreachable. To refresh credentials:`));
219
+ for (const p of unreachable) {
220
+ console.log(chalk.dim(` synap login --reconnect ${p.name}`));
221
+ }
222
+ console.log();
332
223
  }
333
224
  else {
334
- console.log(chalk.dim(" Not logged in"));
225
+ console.log(chalk.green(" All pods healthy."));
226
+ const active = checks.find((p) => p.active);
227
+ if (active)
228
+ console.log(chalk.dim(` Active: ${active.name} (${active.config.podUrl})`));
229
+ console.log();
230
+ console.log(chalk.dim(" synap pods use <name> — switch active pod"));
231
+ console.log(chalk.dim(" synap pods add — connect another pod"));
335
232
  }
336
233
  });
337
234
  // ─── orient ───────────────────────────────────────────────────────────────────
@@ -356,68 +253,103 @@ program
356
253
  const { useWorkspace } = await import("./commands/data.js");
357
254
  await useWorkspace(workspace, opts);
358
255
  });
359
- // ─── search ───────────────────────────────────────────────────────────────────
360
- program
361
- .command("search <query>")
362
- .description("Search entities, documents, and views (Typesense-powered)")
363
- .option("--workspace <id>", "Scope to a specific workspace")
364
- .option("--type <type>", "Filter: entity | doc | view")
365
- .option("--limit <n>", "Max results", "20")
366
- .option("--json", "Output as JSON")
367
- .option("--pod-url <url>", "Pod URL override")
368
- .option("--api-key <key>", "API key override")
369
- .action(async (query, opts) => {
370
- const { searchData } = await import("./commands/data.js");
371
- await searchData(query, opts);
372
- });
373
- // ─── remember / recall ────────────────────────────────────────────────────────
256
+ // ─── note ─────────────────────────────────────────────────────────────────────
257
+ // Quick `note` entity. The one canonical READ verb is `ask`; the one canonical
258
+ // structured-WRITE verb is `capture`. (search / recall / remember removed — they
259
+ // were redundant doors `ask`/`capture`/`note` already cover.)
374
260
  program
375
- .command("remember <text>")
376
- .description("Store a memory on the pod")
377
- .option("--context <entity-id>", "Link memory to an entity")
261
+ .command("note <text>")
262
+ .description("Quick freeform note → note entity (for structured learnings use `synap capture`)")
263
+ .option("--context <entity-id>", "Link this note to an entity")
378
264
  .option("--json", "Output as JSON")
379
265
  .option("--pod-url <url>", "Pod URL override")
380
266
  .option("--api-key <key>", "API key override")
381
267
  .action(async (text, opts) => {
382
- const { rememberData } = await import("./commands/data.js");
383
- await rememberData(text, opts);
268
+ const { noteData } = await import("./commands/data.js");
269
+ await noteData(text, opts);
384
270
  });
271
+ // ─── ask (unified knowledge access) ─────────────────────────────────────────────
272
+ // The ONE read verb. Routes to the right substrate(s) — semantic (entities),
273
+ // procedural (how-to docs), episodic (captures) — and returns a glass-box answer.
385
274
  program
386
- .command("recall <query>")
387
- .description("Retrieve memories from the pod (add --structured for knowledge entities)")
388
- .option("--structured", "Search structured knowledge entities instead of episodic memory")
389
- .option("--type <type>", "Filter by entry type: gotcha|lesson|decision|reference (--structured only)")
390
- .option("--tags <csv>", "Filter by tags e.g. repo:synap-backend (--structured only)")
391
- .option("--workspace <id>", "Workspace to search (--structured only, defaults to active)")
392
- .option("--limit <n>", "Max results", "10")
275
+ .command("ask <query>")
276
+ .description("Ask your pod anything — routes to the right knowledge substrate(s) and shows which answered")
277
+ .option("--workspace <id>", "Scope to a specific workspace (omit for pod-wide)")
278
+ .option("--limit <n>", "Max results per substrate", "10")
393
279
  .option("--json", "Output as JSON")
394
280
  .option("--pod-url <url>", "Pod URL override")
395
281
  .option("--api-key <key>", "API key override")
396
282
  .action(async (query, opts) => {
397
- if (opts.structured) {
398
- const { recallStructured } = await import("./commands/knowledge.js");
399
- await recallStructured(query, opts);
400
- }
401
- else {
402
- const { recallData } = await import("./commands/data.js");
403
- await recallData(query, opts);
404
- }
283
+ const { askKnowledge } = await import("./commands/data.js");
284
+ await askKnowledge(query, opts);
405
285
  });
406
286
  // ─── capture ──────────────────────────────────────────────────────────────────
407
287
  // Uses the active pod + workspace from config (set via `synap pods use` /
408
288
  // `synap use` / `synap workspace provision-agent`). No auth flags needed.
409
289
  program
410
- .command("capture")
411
- .description("Capture a structured knowledge entry into the active agent workspace")
412
- .requiredOption("--type <type>", "Entry type: gotcha|lesson|decision|reference")
413
- .requiredOption("--claim <text>", "One-line assertion")
290
+ .command("capture [text]")
291
+ .description("Capture knowledge: smart `capture \"<free text>\"` (AI structures it) OR typed `capture --type gotcha --claim \"…\"`")
292
+ .option("--type <type>", "Typed mode: gotcha|lesson|decision|reference")
293
+ .option("--claim <text>", "Typed mode: one-line assertion (required with --type)")
414
294
  .option("--why <text>", "Reasoning or context")
415
295
  .option("--evidence <text>", "Supporting file path, URL, or code snippet")
416
296
  .option("--tags <csv>", "Comma-separated tags e.g. repo:synap-backend,layer:migrations")
297
+ .option("--global", "GLOBAL lane: pod-wide cross-cutting runbook (→ knowledge_keys), visible in every workspace")
298
+ .option("--key <ns:slug>", "Stable key for a --global runbook (derived from --type + --claim if omitted)")
299
+ .option("--team", "Write to the first product workspace instead of the active workspace")
300
+ .option("--workspace <id>", "Explicit workspace override")
301
+ .option("--yes", "Skip the y/N confirmation prompt (smart mode)")
417
302
  .option("--json", "Output as JSON")
418
- .action(async (opts) => {
303
+ .action(async (text, opts) => {
419
304
  const { captureKnowledge } = await import("./commands/knowledge.js");
420
- await captureKnowledge(opts);
305
+ await captureKnowledge({ ...opts, text });
306
+ });
307
+ program
308
+ .command("subscribe")
309
+ .description("Stream pod events as NDJSON (long-poll)")
310
+ .option("--event <pattern>", "Filter by event type pattern (e.g. proposal.*)")
311
+ .option("--limit <n>", "Events per poll", "20")
312
+ .option("--json", "NDJSON output")
313
+ .option("--pod-url <url>")
314
+ .option("--api-key <key>")
315
+ .action(async (opts) => {
316
+ const { subscribeEvents } = await import("./commands/subscribe.js");
317
+ await subscribeEvents(opts);
318
+ });
319
+ program
320
+ .command("home")
321
+ .description("Show the active workspace's home bento layout (what cells are on screen)")
322
+ .option("--workspace <id>", "Workspace to read (defaults to active workspace)")
323
+ .option("--json", "Output as JSON")
324
+ .option("--pod-url <url>", "Pod URL override")
325
+ .option("--api-key <key>", "API key override")
326
+ .action(async (opts) => {
327
+ const { showHome } = await import("./commands/home.js");
328
+ await showHome(opts);
329
+ });
330
+ program
331
+ .command("context")
332
+ .description("Session-start context: knowledge, proposals, tasks, active sessions")
333
+ .option("--repo <name>", "Filter knowledge by repo tag")
334
+ .option("--limit <n>", "Entries per section", "5")
335
+ .option("--json", "NDJSON output")
336
+ .option("--workspace <id>", "Override workspace")
337
+ .option("--pod-url <url>")
338
+ .option("--api-key <key>")
339
+ .action(async (opts) => {
340
+ const { contextSummary } = await import("./commands/context.js");
341
+ await contextSummary(opts);
342
+ });
343
+ program
344
+ .command("discover")
345
+ .description("Runtime discovery: entity profiles with property schemas + CLI command tree")
346
+ .option("--json", "Output as JSON (default: human-readable)")
347
+ .option("--profiles", "Show only profiles (use with --json for machine-readable schema)")
348
+ .option("--commands", "Show only the command tree")
349
+ .option("--workspace <id>", "Workspace to query (defaults to active workspace)")
350
+ .action(async (opts) => {
351
+ const { discover } = await import("./commands/discover.js");
352
+ await discover(opts);
421
353
  });
422
354
  // ─── workspace ────────────────────────────────────────────────────────────────
423
355
  // Context setup commands. Run once to configure the session — all subsequent
@@ -459,6 +391,18 @@ list
459
391
  const { listProfiles } = await import("./commands/data.js");
460
392
  await listProfiles(opts);
461
393
  });
394
+ list
395
+ .command("views")
396
+ .description("List views in a workspace (whiteboard, kanban, bento, …)")
397
+ .option("--type <type>", "Filter by view type (e.g. whiteboard, kanban, calendar)")
398
+ .option("--workspace <id>", "Workspace (defaults to active)")
399
+ .option("--json", "Output as JSON")
400
+ .option("--pod-url <url>", "Pod URL override")
401
+ .option("--api-key <key>", "API key override")
402
+ .action(async (opts) => {
403
+ const { viewList } = await import("./commands/view.js");
404
+ await viewList(opts);
405
+ });
462
406
  list
463
407
  .command("entities")
464
408
  .description("List entities on the pod")
@@ -472,6 +416,259 @@ list
472
416
  const { listEntities } = await import("./commands/data.js");
473
417
  await listEntities(opts);
474
418
  });
419
+ // ─── show ─────────────────────────────────────────────────────────────────────
420
+ // Entity detail + its relations — one command to understand any node in the graph.
421
+ program
422
+ .command("show <id>")
423
+ .description("Show an entity with its properties and all linked relations")
424
+ .option("--json", "Output as JSON")
425
+ .option("--pod-url <url>", "Pod URL override")
426
+ .option("--api-key <key>", "API key override")
427
+ .action(async (id, opts) => {
428
+ const { showEntity } = await import("./commands/data-extra.js");
429
+ await showEntity(id, opts);
430
+ });
431
+ // ─── proposals ────────────────────────────────────────────────────────────────
432
+ // Governance inbox — pending proposals attributed to this agent.
433
+ const proposals = program
434
+ .command("proposals")
435
+ .description("Review and act on governance proposals");
436
+ proposals
437
+ .command("list", { isDefault: true })
438
+ .description("List pending governance proposals for this agent")
439
+ .option("--workspace <id>", "Scope to a specific workspace")
440
+ .option("--limit <n>", "Max results", "20")
441
+ .option("--json", "Output as JSON")
442
+ .option("--pod-url <url>", "Pod URL override")
443
+ .option("--api-key <key>", "API key override")
444
+ .action(async (opts) => {
445
+ const { listProposals } = await import("./commands/data-extra.js");
446
+ await listProposals(opts);
447
+ });
448
+ proposals
449
+ .command("approve <id>")
450
+ .description("Approve a pending proposal")
451
+ .option("--reason <text>", "Optional approval note")
452
+ .option("--json", "Output as JSON")
453
+ .option("--pod-url <url>", "Pod URL override")
454
+ .option("--api-key <key>", "API key override")
455
+ .action(async (id, opts) => {
456
+ const { approveProposal } = await import("./commands/proposals-actions.js");
457
+ await approveProposal(id, opts);
458
+ });
459
+ proposals
460
+ .command("reject <id>")
461
+ .description("Reject a pending proposal")
462
+ .option("--reason <text>", "Rejection reason")
463
+ .option("--json", "Output as JSON")
464
+ .option("--pod-url <url>", "Pod URL override")
465
+ .option("--api-key <key>", "API key override")
466
+ .action(async (id, opts) => {
467
+ const { rejectProposal } = await import("./commands/proposals-actions.js");
468
+ await rejectProposal(id, opts);
469
+ });
470
+ // ─── session ──────────────────────────────────────────────────────────────────
471
+ // Focus sessions — goal-bound work rooms tracked on the pod.
472
+ // AI agents: use start_session to begin, update_session to report progress,
473
+ // update_session({status:"closed"}) to finish. Sessions link goals, context,
474
+ // plans, execution logs, and verification reports together.
475
+ const session = program
476
+ .command("session")
477
+ .description("Manage focus sessions (goal-bound AI work rooms)");
478
+ session
479
+ .command("start")
480
+ .description("Start a new focus session and return its ID")
481
+ .requiredOption("--goal <text>", "What this session is working toward")
482
+ .option("--workspace <id>", "Workspace that owns the session")
483
+ .option("--task-id <id>", "Link session to an existing task entity")
484
+ .option("--json", "Output as JSON")
485
+ .option("--pod-url <url>", "Pod URL override")
486
+ .option("--api-key <key>", "API key override")
487
+ .action(async (opts) => {
488
+ const { startSession } = await import("./commands/sessions.js");
489
+ await startSession(opts);
490
+ });
491
+ session
492
+ .command("list", { isDefault: true })
493
+ .description("List focus sessions in the active workspace")
494
+ .option("--workspace <id>", "Scope to a specific workspace")
495
+ .option("--status <status>", "Filter by status: active | paused | closed")
496
+ .option("--limit <n>", "Max results", "20")
497
+ .option("--json", "Output as JSON")
498
+ .option("--pod-url <url>", "Pod URL override")
499
+ .option("--api-key <key>", "API key override")
500
+ .action(async (opts) => {
501
+ const { listSessions } = await import("./commands/sessions.js");
502
+ await listSessions(opts);
503
+ });
504
+ session
505
+ .command("get <id>")
506
+ .description("Show details for a specific focus session")
507
+ .option("--workspace <id>", "Workspace that owns the session (required for scoped fetch)")
508
+ .option("--json", "Output as JSON")
509
+ .option("--pod-url <url>", "Pod URL override")
510
+ .option("--api-key <key>", "API key override")
511
+ .action(async (id, opts) => {
512
+ const { getSession } = await import("./commands/sessions.js");
513
+ await getSession(id, opts);
514
+ });
515
+ session
516
+ .command("update <id>")
517
+ .description("Update progress or status of a focus session")
518
+ .requiredOption("--workspace <id>", "Workspace that owns the session")
519
+ .option("--progress <0-100>", "Progress percentage")
520
+ .option("--status <status>", "New status: active | paused")
521
+ .option("--goal <text>", "Revised goal")
522
+ .option("--json", "Output as JSON")
523
+ .option("--pod-url <url>", "Pod URL override")
524
+ .option("--api-key <key>", "API key override")
525
+ .action(async (id, opts) => {
526
+ const { updateSession } = await import("./commands/sessions.js");
527
+ await updateSession(id, opts);
528
+ });
529
+ session
530
+ .command("close <id>")
531
+ .description("Close a focus session and optionally attach a recap")
532
+ .requiredOption("--workspace <id>", "Workspace that owns the session")
533
+ .option("--recap <text>", "Short summary of what was accomplished")
534
+ .option("--json", "Output as JSON")
535
+ .option("--pod-url <url>", "Pod URL override")
536
+ .option("--api-key <key>", "API key override")
537
+ .action(async (id, opts) => {
538
+ const { closeSession } = await import("./commands/sessions.js");
539
+ await closeSession(id, opts);
540
+ });
541
+ session
542
+ .command("attach <id>")
543
+ .description("Attach a session to this terminal — all hub calls tag X-Session-Id until detached")
544
+ .option("--json", "Output as JSON")
545
+ .option("--pod-url <url>", "Pod URL override")
546
+ .option("--api-key <key>", "API key override")
547
+ .action(async (id, opts) => {
548
+ const { attachSession } = await import("./commands/sessions.js");
549
+ await attachSession(id, opts);
550
+ });
551
+ session
552
+ .command("detach")
553
+ .description("Remove the active session from this terminal")
554
+ .option("--json", "Output as JSON")
555
+ .action(async (opts) => {
556
+ const { detachSession } = await import("./commands/sessions.js");
557
+ detachSession(opts);
558
+ });
559
+ session
560
+ .command("current")
561
+ .description("Show the active session for this terminal (from .synap-session or SYNAP_SESSION_ID)")
562
+ .option("--json", "Output as JSON")
563
+ .action(async (opts) => {
564
+ const { sessionStatus } = await import("./commands/sessions.js");
565
+ sessionStatus(opts);
566
+ });
567
+ // ─── skill ────────────────────────────────────────────────────────────────────
568
+ // Autonomous workspace session skills — context gathering, task planning, etc.
569
+ const skillCmd = program
570
+ .command("skill")
571
+ .description("Execute a skill in an autonomous workspace session")
572
+ .action(() => {
573
+ skillCmd.help();
574
+ });
575
+ skillCmd
576
+ .command("gather-context")
577
+ .description("Gather pod KG + local context (CLAUDE.md, .claude/kg-verify) for a session")
578
+ .option("--session <id>", "Focus session ID (required)")
579
+ .option("--workspace <id>", "Workspace ID (defaults to active workspace)")
580
+ .option("--json", "Output as JSON")
581
+ .option("--pod-url <url>", "Pod URL override")
582
+ .option("--api-key <key>", "API key override")
583
+ .allowUnknownOption()
584
+ .action(async (opts) => {
585
+ const { gatherContext } = await import("./commands/skills.js");
586
+ await gatherContext(opts);
587
+ });
588
+ skillCmd
589
+ .command("suggest <topic>")
590
+ .description("Search the skill knowledge base by topic or keyword")
591
+ .option("--pod-url <url>", "Pod URL override")
592
+ .option("--api-key <key>", "API key override")
593
+ .action(async (topic, opts) => {
594
+ const { suggestSkills } = await import("./commands/skills-commands.js");
595
+ await suggestSkills(topic, opts);
596
+ });
597
+ skillCmd
598
+ .command("get <slug>")
599
+ .description("Fetch a skill from the knowledge base by slug and print its body")
600
+ .option("--pod-url <url>", "Pod URL override")
601
+ .option("--api-key <key>", "API key override")
602
+ .action(async (slug, opts) => {
603
+ const { getSkill } = await import("./commands/skills-commands.js");
604
+ await getSkill(slug, opts);
605
+ });
606
+ skillCmd
607
+ .command("sync")
608
+ .description("Bulk-import local skills from ~/.claude/skills/ into the pod")
609
+ .option("--from <path>", "Skills directory path (default: ~/.claude/skills)")
610
+ .option("--pod-url <url>", "Pod URL override")
611
+ .option("--api-key <key>", "API key override")
612
+ .action(async (opts) => {
613
+ const { syncSkills } = await import("./commands/skills-commands.js");
614
+ await syncSkills(opts);
615
+ });
616
+ skillCmd
617
+ .command("verify-code")
618
+ .description("Run the project's type-check/test gate and store the result on the session")
619
+ .requiredOption("--session <id>", "Focus session ID")
620
+ .requiredOption("--workspace <id>", "Workspace that owns the session")
621
+ .requiredOption("--cmd <command>", "Gate command to run (e.g. 'pnpm type-check')")
622
+ .option("--json", "Output as JSON")
623
+ .option("--pod-url <url>", "Pod URL override")
624
+ .option("--api-key <key>", "API key override")
625
+ .action(async (opts) => {
626
+ const { verifyCode } = await import("./commands/skills.js");
627
+ await verifyCode(opts);
628
+ });
629
+ // ─── browse ───────────────────────────────────────────────────────────────────
630
+ // Paginated entity list — cleaner than `list entities`.
631
+ program
632
+ .command("browse [profile]")
633
+ .description("Browse entities in the active workspace, optionally filtered by profile type")
634
+ .option("--workspace <id>", "Scope to a specific workspace")
635
+ .option("--limit <n>", "Max results", "20")
636
+ .option("--json", "Output as JSON")
637
+ .option("--pod-url <url>", "Pod URL override")
638
+ .option("--api-key <key>", "API key override")
639
+ .action(async (profile, opts) => {
640
+ const { browseEntities } = await import("./commands/data-extra.js");
641
+ await browseEntities({ ...opts, profile });
642
+ });
643
+ // ─── observe ──────────────────────────────────────────────────────────────────
644
+ // AI-maintained user model — write and query pod-scoped user observations.
645
+ const observe = program
646
+ .command("observe")
647
+ .description("Write and recall pod-wide user observations (AI-maintained user model)");
648
+ observe
649
+ .command("write <text>", { isDefault: true })
650
+ .description("Record an observation about the user (pod-scoped, AI-maintained)")
651
+ .option("--category <cat>", "working_style | communication | focus | technical | preference", "preference")
652
+ .option("--confidence <n>", "Confidence score 0-1", "0.5")
653
+ .option("--json", "Output as JSON")
654
+ .option("--pod-url <url>", "Pod URL override")
655
+ .option("--api-key <key>", "API key override")
656
+ .action(async (text, opts) => {
657
+ const { observeWrite } = await import("./commands/observe.js");
658
+ await observeWrite(text, opts);
659
+ });
660
+ observe
661
+ .command("recall <query>")
662
+ .description("Search recorded user observations")
663
+ .option("--category <cat>", "Filter by category")
664
+ .option("--limit <n>", "Max results", "10")
665
+ .option("--json", "Output as JSON")
666
+ .option("--pod-url <url>", "Pod URL override")
667
+ .option("--api-key <key>", "API key override")
668
+ .action(async (query, opts) => {
669
+ const { observeRecall } = await import("./commands/observe.js");
670
+ await observeRecall(query, opts);
671
+ });
475
672
  // ─── get ──────────────────────────────────────────────────────────────────────
476
673
  const get = program
477
674
  .command("get")
@@ -527,6 +724,7 @@ set
527
724
  .command("entity <id>")
528
725
  .description("Update entity properties")
529
726
  .requiredOption("--props <json>", "Properties as JSON string")
727
+ .option("--workspace <id>", "Workspace ID (sent in PATCH body so workspace-scoped automations fire)")
530
728
  .option("--json", "Output as JSON")
531
729
  .option("--pod-url <url>", "Pod URL override")
532
730
  .option("--api-key <key>", "API key override")
@@ -593,5 +791,424 @@ agents
593
791
  const { agentsInfo } = await import("./commands/agents.js");
594
792
  agentsInfo(name);
595
793
  });
794
+ // ─── agent (autonomous runner) ───────────────────────────────────────────────
795
+ const agent = program
796
+ .command("agent")
797
+ .description("Run autonomous agent loops against the IS, or schedule recurring goals");
798
+ agent
799
+ .command("run")
800
+ .description("Run an autonomous agent toward a goal using the Intelligence Service")
801
+ .requiredOption("--goal <text>", "What the agent should accomplish")
802
+ .option("--persona <type>", "researcher | assistant | developer (default: researcher)")
803
+ .option("--model <model>", "IS model alias, e.g. synap/advanced (default: synap/advanced)")
804
+ .option("--workspace <id>", "Workspace to store results in (uses active workspace if omitted)")
805
+ .option("--max-steps <n>", "Max reasoning steps before auto-saving (default: 12)", parseInt)
806
+ .option("--dry-run", "Print config without running")
807
+ .option("--pod-url <url>", "Override pod URL")
808
+ .option("--api-key <key>", "Override API key")
809
+ .action(async (opts) => {
810
+ const { agentRun } = await import("./commands/agent-run.js");
811
+ await agentRun(opts);
812
+ });
813
+ agent
814
+ .command("schedule")
815
+ .description("Add, list, or remove recurring agent schedules")
816
+ .option("--goal <text>", "Goal for the scheduled agent")
817
+ .option("--name <label>", "Name for this schedule (required when adding)")
818
+ .option("--every <interval>", "hourly | daily | weekly")
819
+ .option("--persona <type>", "researcher | assistant | developer (default: researcher)")
820
+ .option("--model <model>", "IS model alias (default: synap/advanced)")
821
+ .option("--workspace <id>", "Workspace to store results in")
822
+ .option("--run-now", "Also run immediately after creating the schedule")
823
+ .option("--list", "List all configured schedules")
824
+ .option("--remove <name>", "Remove a schedule by name")
825
+ .option("--pod-url <url>", "Override pod URL")
826
+ .option("--api-key <key>", "Override API key")
827
+ .action(async (opts) => {
828
+ const { agentSchedule } = await import("./commands/agent-run.js");
829
+ await agentSchedule(opts);
830
+ });
831
+ agent
832
+ .command("tick")
833
+ .description("Check due schedules and run them — wire this to crontab: `0 * * * * synap agent tick`")
834
+ .option("--dry-run", "Show what would run without executing")
835
+ .option("--pod-url <url>", "Override pod URL")
836
+ .option("--api-key <key>", "Override API key")
837
+ .action(async (opts) => {
838
+ const { agentTick } = await import("./commands/agent-run.js");
839
+ await agentTick(opts);
840
+ });
841
+ // ─── automation ──────────────────────────────────────────────────────────────
842
+ function collect(val, prev) { return [...prev, val]; }
843
+ const automation = program
844
+ .command("automation")
845
+ .description("Create, manage, and inspect pod automations");
846
+ automation
847
+ .command("list")
848
+ .description("List automations on the pod")
849
+ .option("--workspace <id>", "Filter by workspace")
850
+ .option("--status <status>", "Filter by status (active|draft|paused|error)")
851
+ .option("--json", "Output raw JSON")
852
+ .action(async (opts) => {
853
+ const { automationList } = await import("./commands/automation.js");
854
+ await automationList(opts);
855
+ });
856
+ automation
857
+ .command("describe <id>")
858
+ .description("Show full details of an automation")
859
+ .option("--workspace <id>")
860
+ .option("--json")
861
+ .action(async (id, opts) => {
862
+ const { automationDescribe } = await import("./commands/automation.js");
863
+ await automationDescribe(id, opts);
864
+ });
865
+ automation
866
+ .command("create")
867
+ .description("Create an automation (quick flags or --from file.yaml)")
868
+ .option("--name <name>", "Automation name (required in quick mode)")
869
+ .option("--trigger <spec>", "event:<pattern> | cron:<expr> | webhook | manual")
870
+ .option("--filter <kv>", "key=value filter (event triggers, repeatable)", collect, [])
871
+ .option("--action <type>", "notify | channel-message | none (default: none)")
872
+ .option("--message <text>", "Message content for notify/channel-message")
873
+ .option("--channel <id>", "Channel ID for channel-message action")
874
+ .option("--status <status>", "draft (default) | active")
875
+ .option("--description <text>", "Description")
876
+ .option("--workspace <id>", "Workspace ID")
877
+ .option("--from <file>", "Create from YAML/JSON file instead of flags")
878
+ .action(async (opts) => {
879
+ const { automationCreate } = await import("./commands/automation.js");
880
+ await automationCreate(opts);
881
+ });
882
+ automation
883
+ .command("enable <id>")
884
+ .description("Activate an automation")
885
+ .option("--workspace <id>")
886
+ .action(async (id, opts) => {
887
+ const { automationEnable } = await import("./commands/automation.js");
888
+ await automationEnable(id, opts);
889
+ });
890
+ automation
891
+ .command("disable <id>")
892
+ .description("Pause an automation")
893
+ .option("--workspace <id>")
894
+ .action(async (id, opts) => {
895
+ const { automationDisable } = await import("./commands/automation.js");
896
+ await automationDisable(id, opts);
897
+ });
898
+ automation
899
+ .command("delete <id>")
900
+ .description("Delete an automation")
901
+ .option("--workspace <id>")
902
+ .option("--force", "Skip confirmation prompt")
903
+ .action(async (id, opts) => {
904
+ const { automationDelete } = await import("./commands/automation.js");
905
+ await automationDelete(id, opts);
906
+ });
907
+ automation
908
+ .command("schema")
909
+ .description("Fetch the automation DSL schema from the pod (AI context)")
910
+ .option("--write-context [path]", "Write markdown context file for Claude Code")
911
+ .option("--json", "Output raw JSON instead of formatted markdown")
912
+ .action(async (opts) => {
913
+ const { automationSchema } = await import("./commands/automation.js");
914
+ await automationSchema(opts);
915
+ });
916
+ // ─── graph ────────────────────────────────────────────────────────────────────
917
+ program
918
+ .command("graph")
919
+ .description("Low-level BFS graph traversal from an entity (prefer `synap ask` — graph is one of its signals)")
920
+ .requiredOption("--entity <id>", "Start entity ID")
921
+ .option("--depth <n>", "Traversal depth (max 3)", "2")
922
+ .option("--json", "Raw JSON output")
923
+ .option("--pod-url <url>", "Pod URL override")
924
+ .option("--api-key <key>", "API key override")
925
+ .action(async (opts) => {
926
+ const { graphTraverse } = await import("./commands/graph.js");
927
+ await graphTraverse(opts);
928
+ });
929
+ // ─── events ───────────────────────────────────────────────────────────────────
930
+ program
931
+ .command("events")
932
+ .description("Show event chain for an entity or recent pod events")
933
+ .option("--entity <id>", "Filter by entity ID (subjectId)")
934
+ .option("--limit <n>", "Number of events", "20")
935
+ .option("--json", "Raw JSON output")
936
+ .option("--pod-url <url>", "Pod URL override")
937
+ .option("--api-key <key>", "API key override")
938
+ .action(async (opts) => {
939
+ const { listEvents } = await import("./commands/events.js");
940
+ await listEvents(opts);
941
+ });
942
+ // ─── view ─────────────────────────────────────────────────────────────────────
943
+ const view = program
944
+ .command("view")
945
+ .description("Manage views");
946
+ view
947
+ .command("create")
948
+ .description("Create a view (table, kanban, bento, …)")
949
+ .requiredOption("--type <type>", "View type: table|kanban|bento|list|grid|gallery|calendar|masonry|flow|matrix|branch_tree|whiteboard")
950
+ .option("--profile <slug>", "Entity profile to display (passed as profileId)")
951
+ .option("--name <name>", "View name")
952
+ .option("--workspace <id>", "Workspace ID")
953
+ .option("--json", "JSON output")
954
+ .option("--pod-url <url>", "Pod URL override")
955
+ .option("--api-key <key>", "API key override")
956
+ .action(async (opts) => {
957
+ const { viewCreate } = await import("./commands/view.js");
958
+ await viewCreate(opts);
959
+ });
960
+ view
961
+ .command("list", { isDefault: true })
962
+ .description("List views in the active workspace")
963
+ .option("--type <type>", "Filter by view type (e.g. whiteboard, kanban, calendar)")
964
+ .option("--workspace <id>", "Workspace ID")
965
+ .option("--json", "JSON output")
966
+ .option("--pod-url <url>", "Pod URL override")
967
+ .option("--api-key <key>", "API key override")
968
+ .action(async (opts) => {
969
+ const { viewList } = await import("./commands/view.js");
970
+ await viewList(opts);
971
+ });
972
+ view
973
+ .command("arrange <viewId>")
974
+ .description("Replace the widget arrangement of a bento view")
975
+ .option("--file <path>", "JSON file containing widget layout (array or { widgets: [...] })")
976
+ .option("--workspace <id>", "Workspace ID")
977
+ .option("--json", "JSON output")
978
+ .option("--pod-url <url>", "Pod URL override")
979
+ .option("--api-key <key>", "API key override")
980
+ .addHelpText("after", `
981
+ Examples:
982
+ synap view arrange <viewId> --file layout.json
983
+ echo '[{"id":"w1","kind":"widget","x":0,"y":0,"w":6,"h":4}]' | synap view arrange <viewId>
984
+
985
+ Layout format (array of BentoWidget):
986
+ [{ "id": "<widgetId>", "kind": "widget|view|entity", "x": 0, "y": 0, "w": 6, "h": 4, "config": {} }]
987
+ `)
988
+ .action(async (viewId, opts) => {
989
+ const { viewArrange } = await import("./commands/view.js");
990
+ await viewArrange(viewId, opts);
991
+ });
992
+ // ─── explain ──────────────────────────────────────────────────────────────────
993
+ program
994
+ .command("explain [topic]")
995
+ .description("Full capability map — what Synap can do and how (no network required)")
996
+ .action(async (topic) => {
997
+ const { explain } = await import("./commands/explain.js");
998
+ await explain({ topic });
999
+ });
1000
+ // ─── open ─────────────────────────────────────────────────────────────────────
1001
+ program
1002
+ .command("open <kind> <id>")
1003
+ .description("Open an entity, view, cell, or document in the Synap desktop app")
1004
+ .action(async (kind, id) => {
1005
+ if (kind !== "entity" && kind !== "view" && kind !== "cell" && kind !== "document") {
1006
+ console.error(`Unknown kind: "${kind}". Use: entity | view | cell | document`);
1007
+ process.exit(1);
1008
+ }
1009
+ const { openInBrowser } = await import("./commands/open.js");
1010
+ await openInBrowser({ kind: kind, id });
1011
+ });
1012
+ // ─── doc ──────────────────────────────────────────────────────────────────────
1013
+ const doc = program
1014
+ .command("doc")
1015
+ .description("Create and update markdown documents on the pod");
1016
+ doc
1017
+ .command("create")
1018
+ .description("Create a new markdown document")
1019
+ .requiredOption("--title <title>", "Document title")
1020
+ .option("--content <text>", "Markdown content (inline)")
1021
+ .option("--file <path>", "Read content from a file")
1022
+ .option("--workspace <id>", "Workspace to create the document in")
1023
+ .option("--open", "Open in the Synap desktop app after creation")
1024
+ .option("--json", "JSON output")
1025
+ .option("--pod-url <url>", "Pod URL override")
1026
+ .option("--api-key <key>", "API key override")
1027
+ .addHelpText("after", `
1028
+ Examples:
1029
+ synap doc create --title "My Notes" --content "# Hello"
1030
+ synap doc create --title "My Notes" --file ./notes.md --open
1031
+ echo "# Draft" | synap doc create --title "Draft"
1032
+ `)
1033
+ .action(async (opts) => {
1034
+ const { docCreate } = await import("./commands/doc.js");
1035
+ await docCreate(opts);
1036
+ });
1037
+ doc
1038
+ .command("update <documentId>")
1039
+ .description("Update a document's content (full replacement, proposal-gated)")
1040
+ .option("--content <text>", "New markdown content (inline)")
1041
+ .option("--file <path>", "Read new content from a file")
1042
+ .option("--title <title>", "Update document title (requires --content too)")
1043
+ .option("--open", "Open in the Synap desktop app after update")
1044
+ .option("--json", "JSON output")
1045
+ .option("--pod-url <url>", "Pod URL override")
1046
+ .option("--api-key <key>", "API key override")
1047
+ .addHelpText("after", `
1048
+ Examples:
1049
+ synap doc update <id> --content "# Updated"
1050
+ synap doc update <id> --file ./notes.md
1051
+ cat notes.md | synap doc update <id>
1052
+ `)
1053
+ .action(async (documentId, opts) => {
1054
+ const { docUpdate } = await import("./commands/doc.js");
1055
+ await docUpdate(documentId, opts);
1056
+ });
1057
+ // ─── cell ─────────────────────────────────────────────────────────────────────
1058
+ const cell = program
1059
+ .command("cell")
1060
+ .description("Define and bundle frame cells for the Synap runtime");
1061
+ cell
1062
+ .command("define")
1063
+ .description("Create or update a frame cell on the pod from ESM source")
1064
+ .requiredOption("--name <name>", "Cell display name")
1065
+ .option("--file <path>", "ESM source file (or pipe stdin)")
1066
+ .option("--type-key <key>", "Explicit typeKey (default: generated:<slug>)")
1067
+ .option("--deps <json>", 'Dependency map JSON, e.g. \'{"recharts":"2.12.0"}\' (sent in payload; backend support pending)')
1068
+ .option("--workspace <id>", "Workspace to scope the cell to (omit for pod-global)")
1069
+ .option("--description <text>", "Short description")
1070
+ .option("--open", "Open in the Synap desktop app after define")
1071
+ .option("--json", "JSON output")
1072
+ .option("--pod-url <url>", "Pod URL override")
1073
+ .option("--api-key <key>", "API key override")
1074
+ .addHelpText("after", `
1075
+ Examples:
1076
+ synap cell define --name "Revenue Chart" --file ./chart.js
1077
+ synap cell define --name "Revenue Chart" --file ./chart.js --deps '{"recharts":"2.12.0"}'
1078
+ cat chart.js | synap cell define --name "Revenue Chart"
1079
+ `)
1080
+ .action(async (opts) => {
1081
+ const { cellDefine } = await import("./commands/cell.js");
1082
+ await cellDefine(opts);
1083
+ });
1084
+ cell
1085
+ .command("build <entry>")
1086
+ .description("Bundle a multi-file cell/app into a single ESM module the runtime expects")
1087
+ .option("--out <file>", "Output file path (default: <entry>.bundle.js)")
1088
+ .option("--define", "Chain into cell define after bundling (requires --name)")
1089
+ .option("--name <name>", "Cell name (required with --define)")
1090
+ .option("--type-key <key>", "Explicit typeKey (used with --define)")
1091
+ .option("--deps <json>", "Override dependency versions as JSON, e.g. '{\"react\":\"18.3.0\"}'")
1092
+ .option("--workspace <id>", "Workspace (used with --define)")
1093
+ .option("--description <text>", "Description (used with --define)")
1094
+ .option("--open", "Open in browser after define (used with --define)")
1095
+ .option("--json", "JSON output")
1096
+ .option("--pod-url <url>", "Pod URL override")
1097
+ .option("--api-key <key>", "API key override")
1098
+ .addHelpText("after", `
1099
+ Output: a single ESM file. Bare imports (react, recharts, …) are externalized
1100
+ and emitted as a deps map. At runtime Synap resolves them via esm.sh importmap.
1101
+
1102
+ Examples:
1103
+ synap cell build ./src/chart.tsx --out ./dist/chart.js
1104
+ synap cell build ./src/chart.tsx --out ./dist/chart.js --define --name "Revenue Chart"
1105
+ `)
1106
+ .action(async (entry, opts) => {
1107
+ const { cellBuild } = await import("./commands/cell.js");
1108
+ await cellBuild(entry, opts);
1109
+ });
1110
+ // ─── artifact ─────────────────────────────────────────────────────────────────
1111
+ const artifact = program
1112
+ .command("artifact")
1113
+ .description("Create and manage HTML artifacts as Synap documents");
1114
+ artifact
1115
+ .command("create")
1116
+ .description("Create an HTML artifact from a local file")
1117
+ .requiredOption("--html <file>", "HTML file to upload")
1118
+ .option("--name <name>", "Document title (default: filename)")
1119
+ .option("--workspace <id>", "Workspace context")
1120
+ .option("--open", "Open in the Synap desktop app after creation")
1121
+ .option("--json", "JSON output")
1122
+ .option("--pod-url <url>", "Pod URL override")
1123
+ .option("--api-key <key>", "API key override")
1124
+ .action(async (opts) => {
1125
+ const { artifactCreate } = await import("./commands/artifact.js");
1126
+ await artifactCreate(opts);
1127
+ });
1128
+ // ─── connect (top-level shorthand) ───────────────────────────────────────────
1129
+ program
1130
+ .command("connect-service [service]")
1131
+ .description("Connect an external service (Google, GitHub, Notion, Linear, Slack, …)")
1132
+ .option("--workspace <id>", "Workspace context")
1133
+ .option("--pod-url <url>", "Pod URL override")
1134
+ .option("--api-key <key>", "API key override")
1135
+ .action(async (service, opts) => {
1136
+ const { connectorsConnect } = await import("./commands/connectors.js");
1137
+ await connectorsConnect(service, opts);
1138
+ });
1139
+ // ─── connectors ──────────────────────────────────────────────────────────────
1140
+ const connectors = program
1141
+ .command("connectors")
1142
+ .description("Manage pod connector integrations (Nango-powered)");
1143
+ connectors
1144
+ .command("list")
1145
+ .description("List available connectors and connection status")
1146
+ .option("--json", "Output as JSON")
1147
+ .option("--workspace <id>", "Workspace context")
1148
+ .action(async (opts) => {
1149
+ const { connectorsList } = await import("./commands/connectors.js");
1150
+ await connectorsList(opts);
1151
+ });
1152
+ connectors
1153
+ .command("sync <provider>")
1154
+ .description("Trigger a manual sync for a connected provider")
1155
+ .option("--workspace <id>", "Workspace context")
1156
+ .action(async (provider, opts) => {
1157
+ const { connectorsSync } = await import("./commands/connectors.js");
1158
+ await connectorsSync(provider, opts);
1159
+ });
1160
+ connectors
1161
+ .command("disconnect <provider>")
1162
+ .description("Revoke a connector connection")
1163
+ .option("--workspace <id>", "Workspace context")
1164
+ .option("--force", "Skip confirmation")
1165
+ .action(async (provider, opts) => {
1166
+ const { connectorsDisconnect } = await import("./commands/connectors.js");
1167
+ await connectorsDisconnect(provider, opts);
1168
+ });
1169
+ connectors
1170
+ .command("schema")
1171
+ .description("Fetch connector schema and supported providers (AI context)")
1172
+ .option("--write-context [path]", "Write to .claude/CONNECTOR_CONTEXT.md")
1173
+ .option("--json", "Output raw JSON instead of formatted markdown")
1174
+ .action(async (opts) => {
1175
+ const { connectorsSchema } = await import("./commands/connectors.js");
1176
+ await connectorsSchema(opts);
1177
+ });
1178
+ // ─── providers ───────────────────────────────────────────────────────────────
1179
+ const providers = program
1180
+ .command("providers")
1181
+ .description("Discover AI providers from the pod and configure local tools");
1182
+ providers
1183
+ .command("list", { isDefault: true })
1184
+ .description("Show enabled AI providers and their models")
1185
+ .option("--json", "Output as JSON")
1186
+ .option("--pod-url <url>", "Pod URL override")
1187
+ .option("--api-key <key>", "API key override")
1188
+ .action(async (opts) => {
1189
+ const { providersList } = await import("./commands/providers.js");
1190
+ await providersList(opts);
1191
+ });
1192
+ providers
1193
+ .command("pull")
1194
+ .description("Write pod provider config to a local AI tool (opencode, aider)")
1195
+ .option("--target <name>", "Target tool: opencode | aider")
1196
+ .option("--pod-url <url>", "Pod URL override")
1197
+ .option("--api-key <key>", "API key override")
1198
+ .action(async (opts) => {
1199
+ const { providersPull } = await import("./commands/providers.js");
1200
+ await providersPull(opts);
1201
+ });
1202
+ // ─── keys ────────────────────────────────────────────────────────────────────
1203
+ const keys = program
1204
+ .command("keys")
1205
+ .description("Manage pod API keys");
1206
+ keys
1207
+ .command("rotate")
1208
+ .description("Rotate your CLI key to pick up the latest scope set")
1209
+ .action(async () => {
1210
+ const { keysRotate } = await import("./commands/keys.js");
1211
+ await keysRotate({});
1212
+ });
596
1213
  program.parse();
597
1214
  //# sourceMappingURL=index.js.map