@synap-core/cli 1.8.0 → 1.10.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 (332) hide show
  1. package/dist/commands/agent-chat.d.ts +47 -0
  2. package/dist/commands/agent-chat.js +250 -0
  3. package/dist/commands/agent-chat.js.map +1 -0
  4. package/dist/commands/agent-run.js +3 -4
  5. package/dist/commands/agent-run.js.map +1 -1
  6. package/dist/commands/agents.js +85 -46
  7. package/dist/commands/agents.js.map +1 -1
  8. package/dist/commands/automation.js +2 -1
  9. package/dist/commands/automation.js.map +1 -1
  10. package/dist/commands/bridge-setup.d.ts +66 -0
  11. package/dist/commands/bridge-setup.js +1112 -0
  12. package/dist/commands/bridge-setup.js.map +1 -0
  13. package/dist/commands/cap-connections.d.ts +28 -0
  14. package/dist/commands/cap-connections.js +224 -0
  15. package/dist/commands/cap-connections.js.map +1 -0
  16. package/dist/commands/capability.d.ts +89 -0
  17. package/dist/commands/capability.js +1523 -0
  18. package/dist/commands/capability.js.map +1 -0
  19. package/dist/commands/centrality.d.ts +20 -0
  20. package/dist/commands/centrality.js +94 -0
  21. package/dist/commands/centrality.js.map +1 -0
  22. package/dist/commands/connect.d.ts +2 -0
  23. package/dist/commands/connect.js +16 -35
  24. package/dist/commands/connect.js.map +1 -1
  25. package/dist/commands/context.js +4 -6
  26. package/dist/commands/context.js.map +1 -1
  27. package/dist/commands/data-extra.d.ts +1 -0
  28. package/dist/commands/data-extra.js +34 -10
  29. package/dist/commands/data-extra.js.map +1 -1
  30. package/dist/commands/data.d.ts +4 -4
  31. package/dist/commands/data.js +241 -184
  32. package/dist/commands/data.js.map +1 -1
  33. package/dist/commands/diagnose.d.ts +20 -0
  34. package/dist/commands/diagnose.js +143 -0
  35. package/dist/commands/diagnose.js.map +1 -0
  36. package/dist/commands/digest.d.ts +17 -0
  37. package/dist/commands/digest.js +121 -0
  38. package/dist/commands/digest.js.map +1 -0
  39. package/dist/commands/discover.d.ts +2 -0
  40. package/dist/commands/discover.js +25 -8
  41. package/dist/commands/discover.js.map +1 -1
  42. package/dist/commands/events.js +2 -1
  43. package/dist/commands/events.js.map +1 -1
  44. package/dist/commands/explain.d.ts +1 -1
  45. package/dist/commands/explain.js +18 -15
  46. package/dist/commands/explain.js.map +1 -1
  47. package/dist/commands/facet.d.ts +29 -0
  48. package/dist/commands/facet.js +113 -0
  49. package/dist/commands/facet.js.map +1 -0
  50. package/dist/commands/graph.js +2 -1
  51. package/dist/commands/graph.js.map +1 -1
  52. package/dist/commands/import.d.ts +38 -0
  53. package/dist/commands/import.js +440 -0
  54. package/dist/commands/import.js.map +1 -0
  55. package/dist/commands/init.js +35 -16
  56. package/dist/commands/init.js.map +1 -1
  57. package/dist/commands/keys.js +3 -4
  58. package/dist/commands/keys.js.map +1 -1
  59. package/dist/commands/knowledge.d.ts +2 -0
  60. package/dist/commands/knowledge.js +61 -9
  61. package/dist/commands/knowledge.js.map +1 -1
  62. package/dist/commands/launch.d.ts +50 -0
  63. package/dist/commands/launch.js +790 -0
  64. package/dist/commands/launch.js.map +1 -0
  65. package/dist/commands/lens.d.ts +45 -0
  66. package/dist/commands/lens.js +386 -0
  67. package/dist/commands/lens.js.map +1 -0
  68. package/dist/commands/market.d.ts +209 -0
  69. package/dist/commands/market.js +1368 -0
  70. package/dist/commands/market.js.map +1 -0
  71. package/dist/commands/mcp.d.ts +42 -0
  72. package/dist/commands/mcp.js +184 -0
  73. package/dist/commands/mcp.js.map +1 -0
  74. package/dist/commands/observe.js +14 -11
  75. package/dist/commands/observe.js.map +1 -1
  76. package/dist/commands/open.d.ts +21 -4
  77. package/dist/commands/open.js +45 -4
  78. package/dist/commands/open.js.map +1 -1
  79. package/dist/commands/pod.d.ts +100 -0
  80. package/dist/commands/pod.js +359 -0
  81. package/dist/commands/pod.js.map +1 -0
  82. package/dist/commands/pods.d.ts +1 -0
  83. package/dist/commands/pods.js +77 -15
  84. package/dist/commands/pods.js.map +1 -1
  85. package/dist/commands/proposals-actions.js +40 -3
  86. package/dist/commands/proposals-actions.js.map +1 -1
  87. package/dist/commands/purge.d.ts +23 -0
  88. package/dist/commands/purge.js +148 -0
  89. package/dist/commands/purge.js.map +1 -0
  90. package/dist/commands/raycast.d.ts +15 -0
  91. package/dist/commands/raycast.js +153 -0
  92. package/dist/commands/raycast.js.map +1 -0
  93. package/dist/commands/sessions.js +45 -21
  94. package/dist/commands/sessions.js.map +1 -1
  95. package/dist/commands/skill-manage.d.ts +21 -0
  96. package/dist/commands/skill-manage.js +195 -0
  97. package/dist/commands/skill-manage.js.map +1 -0
  98. package/dist/commands/skills-commands.js +2 -1
  99. package/dist/commands/skills-commands.js.map +1 -1
  100. package/dist/commands/skills.js +4 -3
  101. package/dist/commands/skills.js.map +1 -1
  102. package/dist/commands/status.d.ts +3 -1
  103. package/dist/commands/status.js +166 -14
  104. package/dist/commands/status.js.map +1 -1
  105. package/dist/commands/statusline.d.ts +24 -0
  106. package/dist/commands/statusline.js +450 -0
  107. package/dist/commands/statusline.js.map +1 -0
  108. package/dist/commands/subscribe.js +2 -2
  109. package/dist/commands/subscribe.js.map +1 -1
  110. package/dist/commands/tools.d.ts +30 -0
  111. package/dist/commands/tools.js +315 -0
  112. package/dist/commands/tools.js.map +1 -0
  113. package/dist/commands/versions.d.ts +27 -0
  114. package/dist/commands/versions.js +277 -0
  115. package/dist/commands/versions.js.map +1 -0
  116. package/dist/commands/view.js +3 -2
  117. package/dist/commands/view.js.map +1 -1
  118. package/dist/commands/whoami.d.ts +21 -0
  119. package/dist/commands/whoami.js +129 -0
  120. package/dist/commands/whoami.js.map +1 -0
  121. package/dist/index.js +776 -60
  122. package/dist/index.js.map +1 -1
  123. package/dist/lib/approval-poll.d.ts +42 -0
  124. package/dist/lib/approval-poll.js +52 -0
  125. package/dist/lib/approval-poll.js.map +1 -0
  126. package/dist/lib/auth.d.ts +29 -6
  127. package/dist/lib/auth.js +105 -94
  128. package/dist/lib/auth.js.map +1 -1
  129. package/dist/lib/bundle-version.d.ts +11 -0
  130. package/dist/lib/bundle-version.js +33 -0
  131. package/dist/lib/bundle-version.js.map +1 -0
  132. package/dist/lib/capture-lane.d.ts +30 -12
  133. package/dist/lib/capture-lane.js +71 -14
  134. package/dist/lib/capture-lane.js.map +1 -1
  135. package/dist/lib/cp-packages.d.ts +121 -0
  136. package/dist/lib/cp-packages.js +218 -0
  137. package/dist/lib/cp-packages.js.map +1 -0
  138. package/dist/lib/describe-lens.d.ts +46 -0
  139. package/dist/lib/describe-lens.js +33 -0
  140. package/dist/lib/describe-lens.js.map +1 -0
  141. package/dist/lib/hub-client.d.ts +78 -1
  142. package/dist/lib/hub-client.js +474 -22
  143. package/dist/lib/hub-client.js.map +1 -1
  144. package/dist/lib/installed.d.ts +51 -0
  145. package/dist/lib/installed.js +70 -0
  146. package/dist/lib/installed.js.map +1 -0
  147. package/dist/lib/next-steps.d.ts +61 -0
  148. package/dist/lib/next-steps.js +146 -0
  149. package/dist/lib/next-steps.js.map +1 -0
  150. package/dist/lib/pod.d.ts +91 -19
  151. package/dist/lib/pod.js +222 -56
  152. package/dist/lib/pod.js.map +1 -1
  153. package/dist/lib/project-ref.d.ts +95 -0
  154. package/dist/lib/project-ref.js +144 -0
  155. package/dist/lib/project-ref.js.map +1 -0
  156. package/dist/lib/project.d.ts +35 -0
  157. package/dist/lib/project.js +39 -0
  158. package/dist/lib/project.js.map +1 -0
  159. package/dist/lib/session-lens.d.ts +33 -0
  160. package/dist/lib/session-lens.js +76 -0
  161. package/dist/lib/session-lens.js.map +1 -0
  162. package/dist/lib/skill-import.d.ts +40 -0
  163. package/dist/lib/skill-import.js +203 -0
  164. package/dist/lib/skill-import.js.map +1 -0
  165. package/dist/lib/skills-installer.d.ts +17 -6
  166. package/dist/lib/skills-installer.js +133 -19
  167. package/dist/lib/skills-installer.js.map +1 -1
  168. package/dist/lib/suite.d.ts +28 -0
  169. package/dist/lib/suite.js +29 -0
  170. package/dist/lib/suite.js.map +1 -0
  171. package/dist/lib/superwhisper-import.d.ts +40 -0
  172. package/dist/lib/superwhisper-import.js +389 -0
  173. package/dist/lib/superwhisper-import.js.map +1 -0
  174. package/dist/lib/targets.d.ts +49 -1
  175. package/dist/lib/targets.js +420 -288
  176. package/dist/lib/targets.js.map +1 -1
  177. package/dist/lib/unwrapList.d.ts +1 -0
  178. package/dist/lib/unwrapList.js +20 -0
  179. package/dist/lib/unwrapList.js.map +1 -0
  180. package/dist/utils/logger.d.ts +2 -0
  181. package/dist/utils/logger.js +10 -2
  182. package/dist/utils/logger.js.map +1 -1
  183. package/package.json +9 -4
  184. package/skills/agent-os/SKILL.md +212 -0
  185. package/skills/manifest.json +5 -0
  186. package/skills/onboard/SKILL.md +186 -0
  187. package/skills/synap/SKILL.md +1182 -37
  188. package/skills/synap/_order.txt +37 -0
  189. package/skills/synap/_skill.yaml +24 -0
  190. package/skills/synap/authentication.md +10 -0
  191. package/skills/synap/automations.md +264 -0
  192. package/skills/synap/capabilities.md +107 -0
  193. package/skills/synap/capture.md +10 -0
  194. package/skills/synap/cli-operations.md +107 -0
  195. package/skills/synap/common-mistakes.md +12 -0
  196. package/skills/synap/creative-loop.md +43 -0
  197. package/skills/synap/crm.md +117 -0
  198. package/skills/synap/diagnostics.md +35 -0
  199. package/skills/synap/digesting-a-channel.md +38 -0
  200. package/skills/synap/document-embeds.md +123 -0
  201. package/skills/synap/escalation-ladder.md +71 -0
  202. package/skills/synap/focus-sessions.md +47 -0
  203. package/skills/synap/governance-rules.md +64 -0
  204. package/skills/synap/governance.md +6 -6
  205. package/skills/synap/graph-gardening.md +21 -0
  206. package/skills/synap/inline-patterns.md +38 -0
  207. package/skills/synap/lenses.md +38 -0
  208. package/skills/synap/linking-principle.md +39 -0
  209. package/skills/synap/linking.md +1 -0
  210. package/skills/synap/mental-model.md +45 -0
  211. package/skills/synap/more.md +7 -0
  212. package/skills/synap/multi-entity-capture.md +10 -0
  213. package/skills/synap/next-action.md +37 -0
  214. package/skills/synap/north-star.md +17 -0
  215. package/skills/synap/operating-mode-rules.md +45 -0
  216. package/skills/synap/operating-mode.md +9 -0
  217. package/skills/synap/quick-reference.md +67 -0
  218. package/skills/synap/reading.md +43 -0
  219. package/skills/synap/recap.md +21 -0
  220. package/skills/synap/reflexes.md +46 -0
  221. package/skills/synap/response-style.md +21 -0
  222. package/skills/synap/scope.md +13 -0
  223. package/skills/synap/showing.md +17 -0
  224. package/skills/synap/user-model.md +25 -0
  225. package/skills/synap/viewframe-cells.md +290 -0
  226. package/skills/synap/work-flow.md +113 -0
  227. package/skills/synap/worked-examples.md +53 -0
  228. package/skills/synap/workspace-design.md +45 -0
  229. package/skills/synap/workspace-edges.md +49 -0
  230. package/skills/synap/writes.md +156 -0
  231. package/skills/synap-schema/SKILL.md +97 -6
  232. package/skills/synap-schema/_order.txt +13 -0
  233. package/skills/synap-schema/_skill.yaml +22 -0
  234. package/skills/synap-schema/before-you-touch.md +14 -0
  235. package/skills/synap-schema/common-mistakes.md +9 -0
  236. package/skills/synap-schema/creating-profiles.md +17 -0
  237. package/skills/synap-schema/discover-profiles.md +24 -0
  238. package/skills/synap-schema/entity-scope.md +8 -0
  239. package/skills/synap-schema/extend-vs-create.md +31 -0
  240. package/skills/synap-schema/more.md +6 -0
  241. package/skills/synap-schema/overlay-properties.md +22 -0
  242. package/skills/synap-schema/profiles.md +3 -0
  243. package/skills/synap-schema/property-defs.md +29 -0
  244. package/skills/synap-schema/read-before-write.md +50 -0
  245. package/skills/synap-schema/relations.md +18 -0
  246. package/skills/synap-schema/worked-example.md +30 -0
  247. package/skills/synap-ui/SKILL.md +26 -2
  248. package/skills/synap-ui/_order.txt +13 -0
  249. package/skills/synap-ui/_skill.yaml +23 -0
  250. package/skills/synap-ui/ai-companion.md +27 -0
  251. package/skills/synap-ui/arranging-bento.md +8 -0
  252. package/skills/synap-ui/before-you-build.md +20 -0
  253. package/skills/synap-ui/bento-layouts.md +35 -0
  254. package/skills/synap-ui/common-mistakes.md +10 -0
  255. package/skills/synap-ui/creating-views.md +26 -0
  256. package/skills/synap-ui/four-builds.md +24 -0
  257. package/skills/synap-ui/more.md +7 -0
  258. package/skills/synap-ui/proposing-vs-extending.md +11 -0
  259. package/skills/synap-ui/view-list.md +24 -0
  260. package/skills/synap-ui/views.md +3 -0
  261. package/skills/synap-ui/worked-example.md +18 -0
  262. package/skills/synap-ui/workspaces.md +47 -0
  263. package/templates/automations/bookmark-enrichment.automation.json +44 -0
  264. package/templates/automations/mail-feed.automation.json +86 -0
  265. package/templates/automations/url-bookmark-capture.automation.json +70 -0
  266. package/templates/skills/README.md +21 -0
  267. package/templates/skills/authoring-automations/SKILL.md +95 -0
  268. package/templates/skills/authoring-automations/reference/node-matrix.md +49 -0
  269. package/templates/skills/authoring-capabilities/SKILL.md +93 -0
  270. package/templates/skills/authoring-capabilities/reference/template-anatomy.md +70 -0
  271. package/templates/skills/authoring-playbooks/SKILL.md +102 -0
  272. package/templates/skills/authoring-playbooks/reference/stages-contract.md +35 -0
  273. package/templates/skills/authoring-playbooks/reference/worked-example.md +64 -0
  274. package/templates/skills/bookmark-enrichment/SKILL.md +40 -0
  275. package/templates/skills/coordinating-agents/SKILL.md +32 -0
  276. package/templates/skills/coordinating-branches/SKILL.md +27 -0
  277. package/templates/skills/mapping-the-server/SKILL.md +54 -0
  278. package/templates/skills/reading-client-progress/SKILL.md +65 -0
  279. package/templates/skills/routing-to-channels/SKILL.md +54 -0
  280. package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-CHEATSHEET.md +133 -0
  281. package/templates/skills/stellar-scf-grants-advisor/ASSOCIATE-ONBOARDING-GUIDE.md +761 -0
  282. package/templates/skills/stellar-scf-grants-advisor/CLIENT-FAQ.md +181 -0
  283. package/templates/skills/stellar-scf-grants-advisor/PARTNER-HANDOVER.md +130 -0
  284. package/templates/skills/stellar-scf-grants-advisor/SKILL.md +102 -0
  285. package/templates/skills/stellar-scf-grants-advisor/examples/README.md +43 -0
  286. package/templates/skills/stellar-scf-grants-advisor/examples/chat-summary-templates.md +158 -0
  287. package/templates/skills/stellar-scf-grants-advisor/examples/corpus-comparable-rows.md +136 -0
  288. package/templates/skills/stellar-scf-grants-advisor/examples/q10-team-rewrites.md +109 -0
  289. package/templates/skills/stellar-scf-grants-advisor/examples/q2-description-rewrites.md +88 -0
  290. package/templates/skills/stellar-scf-grants-advisor/examples/q6-integration-rewrites.md +107 -0
  291. package/templates/skills/stellar-scf-grants-advisor/examples/tranche-structures.md +176 -0
  292. package/templates/skills/stellar-scf-grants-advisor/reference/01-methodology.md +96 -0
  293. package/templates/skills/stellar-scf-grants-advisor/reference/02-scoring-framework.md +199 -0
  294. package/templates/skills/stellar-scf-grants-advisor/reference/03-rejection-patterns.md +241 -0
  295. package/templates/skills/stellar-scf-grants-advisor/reference/04-winner-patterns.md +120 -0
  296. package/templates/skills/stellar-scf-grants-advisor/reference/05-comeback-patterns.md +92 -0
  297. package/templates/skills/stellar-scf-grants-advisor/reference/06-budget-benchmarks.md +125 -0
  298. package/templates/skills/stellar-scf-grants-advisor/reference/07-integration-list.md +147 -0
  299. package/templates/skills/stellar-scf-grants-advisor/reference/08-abstract-fields.md +258 -0
  300. package/templates/skills/stellar-scf-grants-advisor/reference/09-build-fields.md +259 -0
  301. package/templates/skills/stellar-scf-grants-advisor/reference/10-corpus-winners.md +135 -0
  302. package/templates/skills/stellar-scf-grants-advisor/reference/11-corpus-rejects.md +267 -0
  303. package/templates/skills/stellar-scf-grants-advisor/reference/12-client-communication.md +131 -0
  304. package/templates/skills/stellar-scf-grants-advisor/reference/13-excel-output-spec.md +199 -0
  305. package/templates/skills/stellar-scf-grants-advisor/reference/14-worked-examples.md +160 -0
  306. package/templates/skills/stellar-scf-grants-advisor/templates/abstract-review-builder.py +253 -0
  307. package/templates/skills/stellar-scf-grants-advisor/templates/build-review-builder.py +213 -0
  308. package/dist/commands/connectors.d.ts +0 -31
  309. package/dist/commands/connectors.js +0 -276
  310. package/dist/commands/connectors.js.map +0 -1
  311. package/dist/commands/finish.d.ts +0 -25
  312. package/dist/commands/finish.js +0 -274
  313. package/dist/commands/finish.js.map +0 -1
  314. package/dist/commands/openclaw.d.ts +0 -40
  315. package/dist/commands/openclaw.js +0 -1148
  316. package/dist/commands/openclaw.js.map +0 -1
  317. package/dist/commands/security-audit.d.ts +0 -12
  318. package/dist/commands/security-audit.js +0 -100
  319. package/dist/commands/security-audit.js.map +0 -1
  320. package/dist/lib/hardening.d.ts +0 -18
  321. package/dist/lib/hardening.js +0 -214
  322. package/dist/lib/hardening.js.map +0 -1
  323. package/node_modules/@synap/hub-rest-client/dist/index.cjs +0 -881
  324. package/node_modules/@synap/hub-rest-client/dist/index.d.cts +0 -907
  325. package/node_modules/@synap/hub-rest-client/dist/index.d.ts +0 -907
  326. package/node_modules/@synap/hub-rest-client/dist/index.js +0 -851
  327. package/node_modules/@synap/hub-rest-client/package.json +0 -45
  328. package/node_modules/@synap/hub-rest-client/src/client.ts +0 -1143
  329. package/node_modules/@synap/hub-rest-client/src/errors.ts +0 -30
  330. package/node_modules/@synap/hub-rest-client/src/index.ts +0 -111
  331. package/node_modules/@synap/hub-rest-client/src/setup.ts +0 -77
  332. package/node_modules/@synap/hub-rest-client/src/types.ts +0 -639
@@ -2,6 +2,7 @@ export interface BaseOpts {
2
2
  json?: boolean;
3
3
  podUrl?: string;
4
4
  apiKey?: string;
5
+ details?: boolean;
5
6
  }
6
7
  export declare const parseLimit: (s: string | undefined, def: number) => number;
7
8
  export declare function formatHit(hit: Record<string, unknown>): {
@@ -16,18 +17,17 @@ export declare function listEntities(opts: BaseOpts & {
16
17
  workspace?: string;
17
18
  profile?: string;
18
19
  limit?: string;
20
+ role?: string;
19
21
  }): Promise<void>;
20
22
  export declare function getEntity(id: string, opts: BaseOpts & {
21
23
  workspace?: string;
22
24
  }): Promise<void>;
23
- export declare function noteData(text: string, opts: BaseOpts & {
24
- context?: string;
25
- json?: boolean;
26
- }): Promise<void>;
27
25
  export declare function askKnowledge(query: string, opts: BaseOpts & {
28
26
  workspace?: string;
29
27
  limit?: string;
30
28
  json?: boolean;
29
+ session?: boolean;
30
+ compare?: boolean;
31
31
  }): Promise<void>;
32
32
  export declare function createEntity(opts: BaseOpts & {
33
33
  profile: string;
@@ -1,7 +1,9 @@
1
1
  import chalk from "chalk";
2
2
  import { log } from "../utils/logger.js";
3
- import { resolveHubConfig, resolveUserId, hubGet, hubPost, hubPatch } from "../lib/hub-client.js";
4
- import { resolveWorkspaceName, formatLaneLine, laneJsonFields, } from "../lib/capture-lane.js";
3
+ import { resolveHubConfig, resolveUserId, hubGet, hubPost, hubPatch, readActiveSessionId, renderHubError } from "../lib/hub-client.js";
4
+ import { unwrapList } from "../lib/unwrapList.js";
5
+ import { reportWrite } from "../lib/capture-lane.js";
6
+ import { renderNextSteps, FLOW } from "../lib/next-steps.js";
5
7
  export const parseLimit = (s, def) => s ? parseInt(s, 10) : def;
6
8
  export function formatHit(hit) {
7
9
  const doc = (hit.document ?? hit);
@@ -13,112 +15,114 @@ export function formatHit(hit) {
13
15
  id: String(doc.id ?? hit.id ?? "").slice(0, 8),
14
16
  };
15
17
  }
16
- // ─── orient ───────────────────────────────────────────────────────────────────
17
18
  export async function orient(opts) {
18
19
  try {
19
20
  const cfg = await resolveHubConfig(opts);
20
- const [workspacesRes, meRes] = await Promise.all([
21
- hubGet("/workspaces", {}, cfg),
22
- hubGet("/users/me", {}, cfg),
23
- ]);
24
- const workspaces = workspacesRes.workspaces ?? workspacesRes;
25
- const me = meRes;
26
- const userId = String(me.id ?? cfg.userId);
27
- const wsList = Array.isArray(workspaces) ? workspaces : [];
28
- // For each workspace, fetch profiles + entity count in parallel.
29
- // Newer pods return `entityCount` directly on GET /workspaces; for older
30
- // pods fall back to counting a real page of entities (GET /entities
31
- // returns a bare array — there is no `total` field on the wire).
32
- const FALLBACK_PAGE = 100;
33
- const wsDetails = await Promise.all(wsList.map(async (ws) => {
34
- const wsId = String(ws.id ?? "");
35
- const declaredCount = typeof ws.entityCount === "number" ? ws.entityCount : undefined;
36
- const [profilesRes, entitiesRes] = await Promise.allSettled([
37
- hubGet("/profiles", { userId, workspaceId: wsId }, cfg),
38
- declaredCount === undefined
39
- ? hubGet("/entities", { userId, workspaceId: wsId, limit: FALLBACK_PAGE }, cfg)
40
- : Promise.resolve(null),
41
- ]);
42
- const profiles = profilesRes.status === "fulfilled"
43
- ? (profilesRes.value.profiles ?? profilesRes.value)
44
- : [];
45
- const profileList = Array.isArray(profiles) ? profiles : [];
46
- let entityTotal = declaredCount ?? 0;
47
- let countIsLowerBound = false;
48
- if (declaredCount === undefined && entitiesRes.status === "fulfilled" && entitiesRes.value) {
49
- const v = entitiesRes.value;
50
- const arr = Array.isArray(v) ? v : (v.entities ?? []);
51
- entityTotal = Array.isArray(arr) ? arr.length : 0;
52
- countIsLowerBound = entityTotal === FALLBACK_PAGE;
53
- }
54
- return { ws, profileList, entityTotal, countIsLowerBound };
55
- }));
21
+ // ONE canonical shape: the backend `discover()` service (via GET /orient)
22
+ // assembles the lens map — workspaces + projects + profile sample +
23
+ // disclosure (onboarding→goal, entity counts, per-ws profiles under full).
24
+ // The CLI only RENDERS it; `--details` maps to detail:'full'. No client-side
25
+ // assembly, no N+1 fanout — the server does the shaping.
26
+ const res = (await hubGet("/orient", opts.details ? { detail: "full" } : {}, cfg));
27
+ const userId = String(res.me?.userId ?? cfg.userId);
28
+ const projList = res.projects ?? [];
29
+ const wsList = res.workspaces ?? [];
56
30
  if (opts.json) {
57
- const capabilities = [
58
- "synap graph --entity <id> --depth 2",
59
- "synap events --entity <id>",
60
- "synap subscribe --event entity.*",
61
- "synap context",
62
- "synap automation list",
63
- "synap list views --type whiteboard",
64
- "synap view create --type kanban --profile task",
65
- "synap proposals list",
66
- "synap explain",
67
- ];
68
- console.log(JSON.stringify({ userId, podUrl: cfg.podUrl, workspaceId: cfg.workspaceId, workspaces: wsDetails, me, capabilities }, null, 2));
31
+ console.log(JSON.stringify({ ...res, podUrl: cfg.podUrl, workspaceId: cfg.workspaceId, nextSteps: FLOW.afterOrient() }, null, 2));
69
32
  return;
70
33
  }
34
+ // Render content rows with console.log (default terminal fg) rather than
35
+ // log.info — log.info wraps every line in chalk.blue, and that blue fg
36
+ // persists through inner bold/dim (they reset weight, not color), flattening
37
+ // the hierarchy. We keep only targeted accents: bold=name, dim=metadata,
38
+ // green ▶ = active.
71
39
  log.heading("Pod Orientation");
72
- log.info(`User : ${String(me.email ?? me.name ?? userId)}`);
73
- log.info(`Pod URL : ${cfg.podUrl}`);
74
- if (cfg.workspaceId)
75
- log.info(`Workspace: ${cfg.workspaceId} ${chalk.dim("(active — use 'synap use <id>' to change)")}`);
40
+ console.log(` User : ${userId}`);
41
+ console.log(` Pod URL : ${cfg.podUrl}`);
42
+ // The per-Claude-session lens (project + workspace + session) — "where am I".
43
+ // Full ids so they can be pasted into 'synap project use' / 'synap use'.
44
+ const { getClaudeSessionId, resolveActiveLens } = await import("../lib/session-lens.js");
45
+ let activeProjectId;
46
+ if (getClaudeSessionId()) {
47
+ const lens = resolveActiveLens();
48
+ activeProjectId = lens?.projectId ?? undefined;
49
+ const proj = lens?.projectId ?? chalk.dim("none");
50
+ const ws = lens?.workspaceId ?? cfg.workspaceId ?? chalk.dim("pod-wide");
51
+ const sess = lens?.focusSessionId ?? chalk.dim("none");
52
+ console.log(` Lens : ${chalk.dim("project")} ${proj} · ${chalk.dim("ws")} ${ws} · ${chalk.dim("session")} ${sess}`);
53
+ }
54
+ else if (cfg.workspaceId) {
55
+ console.log(` Workspace: ${cfg.workspaceId} ${chalk.dim("(active — use 'synap use <id>' to change)")}`);
56
+ }
57
+ // ── Projects (companies / initiatives) — the primary lens ───────────────
58
+ log.blank();
59
+ log.heading("Projects");
60
+ if (projList.length === 0) {
61
+ log.dim("No projects yet — a project is a company/initiative that ties workspaces together.");
62
+ }
63
+ else {
64
+ for (const p of projList) {
65
+ const isActive = p.id === activeProjectId;
66
+ const marker = isActive ? chalk.green("▶ ") : " ";
67
+ const name = chalk.bold(String(p.name ?? p.id));
68
+ const id = chalk.dim(String(p.id ?? ""));
69
+ const status = p.status ? chalk.dim(String(p.status)) : "";
70
+ const homePart = p.homeWorkspace ? chalk.dim(`· ${p.homeWorkspace}`) : "";
71
+ console.log(` ${marker}${name} ${id} ${status}${homePart ? " " + homePart : ""}`);
72
+ }
73
+ }
74
+ // ── Workspaces (operational domains) ────────────────────────────────────
76
75
  log.blank();
77
76
  log.heading("Workspaces");
78
- if (wsDetails.length === 0) {
77
+ if (wsList.length === 0) {
79
78
  log.dim("No workspaces found.");
80
79
  }
81
- else {
82
- for (const { ws, profileList, entityTotal, countIsLowerBound } of wsDetails) {
80
+ else if (opts.details) {
81
+ for (const ws of wsList) {
83
82
  const isActive = ws.id === cfg.workspaceId;
84
83
  const marker = isActive ? chalk.green("▶ ") : " ";
85
84
  const name = chalk.bold(String(ws.name ?? ws.id));
86
- const id = chalk.dim(String(ws.id ?? "").slice(0, 8) + "…");
87
- const count = entityTotal > 0
88
- ? chalk.dim(`${entityTotal}${countIsLowerBound ? "+" : ""} entities`)
85
+ const id = chalk.dim(String(ws.id ?? ""));
86
+ const count = ws.entityCount > 0
87
+ ? chalk.dim(`${ws.entityCount} entities`)
89
88
  : chalk.dim("empty");
90
- log.info(`${marker}${name} ${id} ${count}`);
89
+ console.log(` ${marker}${name} ${id} ${count}`);
91
90
  if (ws.description)
92
91
  log.dim(` ${String(ws.description)}`);
93
- if (profileList.length > 0) {
94
- const slugs = profileList
95
- .map((p) => String(p.slug ?? p.name ?? ""))
92
+ if (ws.onboarding?.goal && ws.entityCount === 0) {
93
+ log.dim(` ${chalk.cyan("→ onboard:")} ${String(ws.onboarding.goal)}`);
94
+ }
95
+ const profiles = ws.profiles ?? [];
96
+ if (profiles.length > 0) {
97
+ const slugs = profiles
98
+ .map((p) => p.slug)
96
99
  .filter(Boolean)
97
100
  .slice(0, 8)
98
101
  .join(chalk.dim(", "));
99
- log.dim(` profiles: ${slugs}${profileList.length > 8 ? chalk.dim(" …") : ""}`);
102
+ log.dim(` profiles: ${slugs}${profiles.length > 8 ? chalk.dim(" …") : ""}`);
100
103
  }
101
104
  }
102
- if (!cfg.workspaceId) {
103
- log.blank();
104
- log.dim("Tip: run 'synap use <workspaceId>' to set a default workspace.");
105
+ }
106
+ else {
107
+ // Light: name + id + domain (operational-domain label = workspaceSubtype
108
+ // falling back to workspaceType, resolved server-side).
109
+ for (const ws of wsList) {
110
+ const isActive = ws.id === cfg.workspaceId;
111
+ const marker = isActive ? chalk.green("▶ ") : " ";
112
+ const name = chalk.bold(String(ws.name ?? ws.id));
113
+ const id = chalk.dim(String(ws.id ?? ""));
114
+ const domain = ws.domain ? chalk.dim(String(ws.domain)) : "";
115
+ console.log(` ${marker}${name} ${id} ${domain}`);
105
116
  }
117
+ log.blank();
118
+ log.dim("Workspaces are operational domains; run 'synap orient --details' for profiles + entity counts.");
106
119
  }
107
- // ── Capabilities block ─────────────────────────────────────────────────
108
- log.blank();
109
- log.heading("Capabilities");
110
- console.log(` ${chalk.cyan("Graph traversal")} synap graph --entity <id> --depth 2`);
111
- console.log(` ${chalk.cyan("Event chain")} synap events --entity <id>`);
112
- console.log(` ${chalk.cyan("Subscribe")} synap subscribe --event entity.*`);
113
- console.log(` ${chalk.cyan("Session context")} synap context`);
114
- console.log(` ${chalk.cyan("Automations")} synap automation list`);
115
- console.log(` ${chalk.cyan("Views")} synap list views --type whiteboard · synap view create --type kanban --profile task`);
116
- console.log(` ${chalk.cyan("Governance")} synap proposals list`);
117
120
  log.blank();
118
- log.dim("For full capability map: synap explain");
121
+ log.dim("Lenses compose — a project spans workspaces; a workspace spans projects. Omit them to stay pod-wide.");
122
+ renderNextSteps(FLOW.afterOrient());
119
123
  }
120
124
  catch (e) {
121
- console.error(chalk.red("Error: " + e.message));
125
+ renderHubError(e);
122
126
  process.exit(1);
123
127
  }
124
128
  }
@@ -129,8 +133,7 @@ export async function useWorkspace(workspaceId, opts) {
129
133
  const userId = await resolveUserId(cfg);
130
134
  // Verify the workspace exists and is accessible
131
135
  const res = await hubGet("/workspaces", {}, cfg);
132
- const workspaces = res.workspaces ?? res;
133
- const list = Array.isArray(workspaces) ? workspaces : [];
136
+ const list = unwrapList(res, ["workspaces"]);
134
137
  const match = list.find((w) => w.id === workspaceId || String(w.name).toLowerCase() === workspaceId.toLowerCase());
135
138
  if (!match) {
136
139
  const names = list.map((w) => `${String(w.name ?? w.id)}`).join(", ");
@@ -139,16 +142,26 @@ export async function useWorkspace(workspaceId, opts) {
139
142
  log.dim(`Available: ${names}`);
140
143
  process.exit(1);
141
144
  }
142
- const { setActiveWorkspaceId } = await import("../lib/pod.js");
143
- setActiveWorkspaceId(String(match.id));
145
+ // In a Claude Code session, scope to THIS session's lens (so concurrent
146
+ // sessions can be on different workspaces). Otherwise set the global default.
147
+ const { getClaudeSessionId, writeLens } = await import("../lib/session-lens.js");
148
+ const claudeSession = getClaudeSessionId();
149
+ if (claudeSession) {
150
+ writeLens(claudeSession, { workspaceId: String(match.id) });
151
+ }
152
+ else {
153
+ const { setActiveWorkspaceId } = await import("../lib/pod.js");
154
+ setActiveWorkspaceId(String(match.id));
155
+ }
144
156
  if (opts.json) {
145
- console.log(JSON.stringify({ workspaceId: match.id, name: match.name }, null, 2));
157
+ console.log(JSON.stringify({ workspaceId: match.id, name: match.name, scope: claudeSession ? "session" : "global" }, null, 2));
146
158
  return;
147
159
  }
148
- log.success(`Now in workspace: ${chalk.bold(String(match.name ?? match.id))} ${chalk.dim(String(match.id ?? ""))}`);
160
+ const scopeNote = claudeSession ? chalk.dim(" (this session)") : "";
161
+ log.success(`Now in workspace: ${chalk.bold(String(match.name ?? match.id))} ${chalk.dim(String(match.id ?? ""))}${scopeNote}`);
149
162
  }
150
163
  catch (e) {
151
- console.error(chalk.red("Error: " + e.message));
164
+ renderHubError(e);
152
165
  process.exit(1);
153
166
  }
154
167
  }
@@ -157,8 +170,7 @@ export async function listWorkspaces(opts) {
157
170
  try {
158
171
  const cfg = await resolveHubConfig(opts);
159
172
  const res = await hubGet("/workspaces", {}, cfg);
160
- const workspaces = res.workspaces ?? res;
161
- const list = Array.isArray(workspaces) ? workspaces : [];
173
+ const list = unwrapList(res, ["workspaces"]);
162
174
  if (opts.json) {
163
175
  console.log(JSON.stringify({ workspaces: list }, null, 2));
164
176
  return;
@@ -173,7 +185,7 @@ export async function listWorkspaces(opts) {
173
185
  }
174
186
  }
175
187
  catch (e) {
176
- console.error(chalk.red("Error: " + e.message));
188
+ renderHubError(e);
177
189
  process.exit(1);
178
190
  }
179
191
  }
@@ -183,14 +195,23 @@ export async function listEntities(opts) {
183
195
  const cfg = await resolveHubConfig(opts);
184
196
  const userId = await resolveUserId(cfg);
185
197
  const limit = parseLimit(opts.limit, 20);
198
+ const workspaceId = opts.workspace ?? cfg.workspaceId;
199
+ // --role needs a workspace to resolve facet scope (see browseEntities);
200
+ // without one the filter is silently dropped and "no entities" would lie.
201
+ if (opts.role && !workspaceId) {
202
+ throw new Error("--role needs an active workspace to resolve the role. Run 'synap use <workspace>' or pass --workspace <id>.");
203
+ }
186
204
  const res = await hubGet("/entities", {
187
205
  userId,
188
- workspaceId: opts.workspace ?? cfg.workspaceId,
206
+ workspaceId,
189
207
  profileSlug: opts.profile,
208
+ // Browse-by-role: the list route resolves facet scope from the
209
+ // `workspaceId` QUERY PARAM (already threaded above), not the
210
+ // X-Workspace-Id header — verified against the live pod.
211
+ facetSlug: opts.role,
190
212
  limit,
191
213
  }, cfg);
192
- const entities = res.entities ?? res;
193
- const list = Array.isArray(entities) ? entities : [];
214
+ const list = unwrapList(res, ["entities"]);
194
215
  if (opts.json) {
195
216
  console.log(JSON.stringify({ entities: list, count: list.length, limit, truncated: list.length === limit }, null, 2));
196
217
  return;
@@ -206,8 +227,7 @@ export async function listEntities(opts) {
206
227
  workspaceId: opts.workspace ?? cfg.workspaceId,
207
228
  type: opts.profile,
208
229
  }, cfg);
209
- const views = viewsRes.views
210
- ?? (Array.isArray(viewsRes) ? viewsRes : []);
230
+ const views = unwrapList(viewsRes, ["views"]);
211
231
  if (views.length > 0) {
212
232
  log.blank();
213
233
  log.info(`'${opts.profile}' is a ${chalk.bold("view type")}, not an entity profile — found ${views.length} view${views.length !== 1 ? "s" : ""}.`);
@@ -235,7 +255,7 @@ export async function listEntities(opts) {
235
255
  }
236
256
  }
237
257
  catch (e) {
238
- console.error(chalk.red("Error: " + e.message));
258
+ renderHubError(e);
239
259
  process.exit(1);
240
260
  }
241
261
  }
@@ -263,64 +283,7 @@ export async function getEntity(id, opts) {
263
283
  }
264
284
  }
265
285
  catch (e) {
266
- console.error(chalk.red("Error: " + e.message));
267
- process.exit(1);
268
- }
269
- }
270
- // ─── noteData ─────────────────────────────────────────────────────────────────
271
- export async function noteData(text, opts) {
272
- try {
273
- const cfg = await resolveHubConfig(opts);
274
- const userId = await resolveUserId(cfg);
275
- // A `note` is the human raw-inbox primitive ("dump now, structure later") —
276
- // it lands in the active workspace, NOT a private agent-memory ws (that lane
277
- // was removed). The AI should never write a note: structuring is its job, so
278
- // it uses `synap capture` (structured) instead.
279
- const workspaceId = cfg.workspaceId;
280
- if (!workspaceId) {
281
- console.error(chalk.red("No workspace set. Run: synap use <workspace-id>"));
282
- process.exit(1);
283
- }
284
- const res = await hubPost("/entities", {
285
- userId,
286
- profileSlug: "note",
287
- title: text.slice(0, 200),
288
- workspaceId,
289
- // The `note` profile requires a `content` property — carry the full text
290
- // (title is truncated). Without this the hub rejects the write (HTTP 500).
291
- properties: {
292
- content: text,
293
- ...(opts.context ? { sourceEntityId: opts.context } : {}),
294
- },
295
- }, cfg);
296
- const isProposed = res.status === "proposed" || Boolean(res.proposalId);
297
- const report = {
298
- lane: "work",
299
- workspaceId,
300
- workspaceName: await resolveWorkspaceName(workspaceId, cfg),
301
- governance: isProposed ? "proposed" : "auto",
302
- };
303
- if (opts.json) {
304
- if (isProposed) {
305
- console.log(JSON.stringify({ proposed: true, proposalId: res.proposalId ?? res.id, ...laneJsonFields(report) }, null, 2));
306
- }
307
- else {
308
- console.log(JSON.stringify({ id: res.id, stored: true, ...laneJsonFields(report) }, null, 2));
309
- }
310
- return;
311
- }
312
- if (isProposed) {
313
- log.warn(`Queued for approval (proposal: ${String(res.proposalId ?? "")})`);
314
- if (res.reviewUrl)
315
- log.dim(` Review: ${String(res.reviewUrl)}`);
316
- }
317
- else {
318
- log.success(`Note saved: "${text.slice(0, 80)}" ${chalk.dim(String(res.id ?? "").slice(0, 8))}`);
319
- }
320
- console.log(" " + formatLaneLine(report));
321
- }
322
- catch (e) {
323
- console.error(chalk.red("Error: " + e.message));
286
+ renderHubError(e);
324
287
  process.exit(1);
325
288
  }
326
289
  }
@@ -358,16 +321,71 @@ function formatAskItem(substrate, item) {
358
321
  : "";
359
322
  return `${title} ${chalk.dim(`[${type}]`)} ${chalk.dim(id)}${snippet}`;
360
323
  }
324
+ /**
325
+ * Render the A/B ranker diagnostic: baseline vs Horizon top-N side-by-side,
326
+ * then the diff. Comparison is by RANK POSITION (not score magnitude) — the two
327
+ * strategies score on different scales, so only the ordering is comparable.
328
+ */
329
+ function renderComparison(query, comparison) {
330
+ if (!comparison) {
331
+ log.dim(`"${query}" → no ranker comparison returned (semantic pool empty?).`);
332
+ return;
333
+ }
334
+ const { baseline, horizon, diff } = comparison;
335
+ const n = Math.max(baseline.length, horizon.length);
336
+ log.dim(`"${query}" → A/B ranker comparison (by RANK, not score magnitude)`);
337
+ log.blank();
338
+ // Side-by-side: rank · title for each column.
339
+ const trunc = (s, w) => (s.length > w ? s.slice(0, w - 1) + "…" : s).padEnd(w, " ");
340
+ const COL = 38;
341
+ log.info(" " + chalk.bold("BASELINE".padEnd(COL + 5)) + chalk.bold("HORIZON"));
342
+ for (let i = 0; i < n; i++) {
343
+ const b = baseline[i];
344
+ const h = horizon[i];
345
+ const bCell = b ? `${chalk.dim(`#${b.rank}`)} ${trunc(b.title, COL)}` : "".padEnd(COL + 4);
346
+ const hCell = h ? `${chalk.dim(`#${h.rank}`)} ${trunc(h.title, COL)}` : "";
347
+ log.info(" " + bCell + " " + hCell);
348
+ }
349
+ log.blank();
350
+ // Diff — overlap@N + items that moved rank between the two strategies.
351
+ const topN = Math.max(baseline.length, horizon.length);
352
+ log.info(chalk.bold("DIFF") +
353
+ chalk.dim(` · overlap@${topN}: ${diff.overlapAtN}/${topN} shared ids`));
354
+ if (diff.moved.length === 0) {
355
+ log.dim(" No shared item changed rank between the two rankers.");
356
+ }
357
+ else {
358
+ for (const m of diff.moved) {
359
+ const dir = m.horizonRank < m.baselineRank ? chalk.green("↑") : chalk.red("↓");
360
+ log.info(` ${dir} ${m.title} ${chalk.dim(`(baseline #${m.baselineRank} → horizon #${m.horizonRank})`)}`);
361
+ }
362
+ }
363
+ log.blank();
364
+ }
361
365
  export async function askKnowledge(query, opts) {
362
366
  try {
363
367
  const cfg = await resolveHubConfig(opts);
364
368
  const limit = parseLimit(opts.limit, 10);
365
369
  const workspaceId = opts.workspace ?? cfg.workspaceId;
366
- const res = (await hubPost("/knowledge/ask", { query, ...(workspaceId ? { workspaceId } : {}), limit }, cfg));
370
+ const sessionId = opts.session ? readActiveSessionId() : undefined;
371
+ const res = (await hubPost("/knowledge/ask", {
372
+ query,
373
+ ...(workspaceId ? { workspaceId } : {}),
374
+ ...(sessionId ? { sessionId } : {}),
375
+ limit,
376
+ ...(opts.compare ? { compare: true } : {}),
377
+ }, cfg));
367
378
  if (opts.json) {
368
379
  console.log(JSON.stringify(res, null, 2));
369
380
  return;
370
381
  }
382
+ // A/B ranker diagnostic — render the comparison and STOP. This is a
383
+ // ranking diff (baseline vs Horizon on the same candidate pool), not the
384
+ // normal answer, so it replaces the substrate listing when requested.
385
+ if (opts.compare) {
386
+ renderComparison(query, res.comparison);
387
+ return;
388
+ }
371
389
  const routed = (res.routedTo ?? []).join(", ");
372
390
  const verdict = res.verdict ? chalk.dim(` · ${res.verdict}`) : "";
373
391
  // Honest routing: if the query's cue suggested one substrate but another
@@ -405,12 +423,40 @@ export async function askKnowledge(query, opts) {
405
423
  }
406
424
  log.blank();
407
425
  }
426
+ // res.degraded was only ever checked as a boolean (anyErrored) above —
427
+ // surface which substrate(s) actually degraded, verbatim, so the answer's
428
+ // incompleteness is visible rather than just its existence.
429
+ if (res.degraded && res.degraded.length > 0) {
430
+ log.dim(`Degraded: ${res.degraded.join(", ")}`);
431
+ }
408
432
  }
409
433
  catch (e) {
410
- console.error(chalk.red("Error: " + e.message));
434
+ renderHubError(e);
411
435
  process.exit(1);
412
436
  }
413
437
  }
438
+ const refLabel = (r) => `${String(r.name ?? r.id ?? "?")}${r.profileSlug ? ` [${r.profileSlug}]` : ""}`;
439
+ /** Print 1-3 dim graph-impact lines for a create response. No-op if absent. */
440
+ function reportResolution(res) {
441
+ const resolution = res.resolution;
442
+ if (!resolution)
443
+ return;
444
+ const existing = resolution.existingSameProfile;
445
+ if (existing) {
446
+ log.dim(`⚠ an entity named "${String(existing.name ?? "?")}" already exists as profile ${String(existing.profileSlug ?? "?")} (${String(existing.id ?? "?").slice(0, 8)}) — consider updating it instead of duplicating`);
447
+ }
448
+ const autoConnected = resolution.autoConnected ?? [];
449
+ if (autoConnected.length > 0) {
450
+ const names = autoConnected.map(refLabel).join(", ");
451
+ log.dim(`→ auto-linked to ${autoConnected.length} existing same-name entit${autoConnected.length === 1 ? "y" : "ies"}: ${names}`);
452
+ }
453
+ // suggestions beyond what was already auto-connected
454
+ const autoIds = new Set(autoConnected.map((r) => r.id).filter(Boolean));
455
+ const suggestions = (resolution.suggestions ?? []).filter((s) => !autoIds.has(s.id));
456
+ if (suggestions.length > 0) {
457
+ log.dim(`· you may want to link: ${suggestions.map(refLabel).join(", ")}`);
458
+ }
459
+ }
414
460
  // ─── createEntity ─────────────────────────────────────────────────────────────
415
461
  export async function createEntity(opts) {
416
462
  try {
@@ -425,24 +471,20 @@ export async function createEntity(opts) {
425
471
  properties,
426
472
  }, cfg);
427
473
  const entity = res;
428
- if (opts.json) {
429
- console.log(JSON.stringify(entity, null, 2));
430
- return;
431
- }
432
- if (entity.status === "proposed") {
433
- log.warn(`Queued for approval — not yet created.`);
434
- log.dim(` proposal: ${String(entity.proposalId ?? entity.id ?? "")}`);
435
- if (entity.reviewUrl)
436
- log.dim(` Review: ${String(entity.reviewUrl)}`);
437
- else
438
- log.dim(` Open Synap to approve the pending proposal.`);
439
- }
440
- else {
441
- log.success(`Created: ${opts.name} ${chalk.dim(String(entity.id ?? "").slice(0, 8))}`);
442
- }
474
+ await reportWrite(entity, {
475
+ label: `Created: ${opts.name}`,
476
+ lane: "work",
477
+ workspaceId: opts.workspace ?? cfg.workspaceId,
478
+ cfg,
479
+ json: opts.json,
480
+ });
481
+ // Graph-impact feedback — human output only (the `--json` path already
482
+ // passed the raw response, including `resolution`, through reportWrite).
483
+ if (!opts.json)
484
+ reportResolution(entity);
443
485
  }
444
486
  catch (e) {
445
- console.error(chalk.red("Error: " + e.message));
487
+ renderHubError(e);
446
488
  process.exit(1);
447
489
  }
448
490
  }
@@ -456,8 +498,8 @@ export async function listProfiles(opts) {
456
498
  console.error(chalk.red("No workspace set. Run: synap use <workspace-id>"));
457
499
  process.exit(1);
458
500
  }
459
- const res = await hubGet("/profiles", { userId, workspaceId }, cfg);
460
- const profiles = (res.profiles ?? (Array.isArray(res) ? res : []));
501
+ const res = await hubGet("/profiles", { userId, workspaceId, ...(opts.json ? { detail: "full" } : {}) }, cfg);
502
+ const profiles = unwrapList(res, ["profiles"]);
461
503
  if (opts.json) {
462
504
  console.log(JSON.stringify(profiles, null, 2));
463
505
  return;
@@ -470,11 +512,18 @@ export async function listProfiles(opts) {
470
512
  for (const p of profiles) {
471
513
  const scope = String(p.entityScope ?? "workspace");
472
514
  const desc = p.description ? chalk.dim(` — ${String(p.description).slice(0, 60)}`) : "";
473
- log.info(`${chalk.bold(String(p.slug ?? ""))} ${chalk.dim(scope)}${desc}`);
515
+ // A role-profile (client, partner, …) is a hat an existing entity wears
516
+ // via `synap facet attach` — never created as its own entity. Label it
517
+ // distinctly so it's never confused with a primary kind.
518
+ const applicableKinds = p.applicableKinds;
519
+ const kindLabel = p.profileKind === "role"
520
+ ? chalk.magenta(`[Role${applicableKinds?.length ? ` of ${applicableKinds.join("/")}` : ""}]`)
521
+ : chalk.green("[Kind]");
522
+ log.info(`${chalk.bold(String(p.slug ?? ""))} ${kindLabel} ${chalk.dim(scope)}${desc}`);
474
523
  }
475
524
  }
476
525
  catch (e) {
477
- console.error(chalk.red("Error: " + e.message));
526
+ renderHubError(e);
478
527
  process.exit(1);
479
528
  }
480
529
  }
@@ -491,14 +540,16 @@ export async function createRelation(opts) {
491
540
  type: opts.type,
492
541
  }, cfg);
493
542
  const relation = res;
494
- if (opts.json) {
495
- console.log(JSON.stringify(relation, null, 2));
496
- return;
497
- }
498
- log.success(`Created relation: ${opts.type} (${opts.source} → ${opts.target})`);
543
+ await reportWrite(relation, {
544
+ label: `Relation: ${opts.type} (${opts.source} → ${opts.target})`,
545
+ lane: "work",
546
+ workspaceId: opts.workspace ?? cfg.workspaceId,
547
+ cfg,
548
+ json: opts.json,
549
+ });
499
550
  }
500
551
  catch (e) {
501
- console.error(chalk.red("Error: " + e.message));
552
+ renderHubError(e);
502
553
  process.exit(1);
503
554
  }
504
555
  }
@@ -521,9 +572,15 @@ export async function updateEntity(id, opts) {
521
572
  return;
522
573
  }
523
574
  log.success(`Updated: ${id}`);
575
+ // Graph-impact feedback — shallow immediate neighbors the edit may touch.
576
+ // Additive + graceful: absent on older backends, so output is unchanged.
577
+ const impact = entity.impact;
578
+ if (Array.isArray(impact) && impact.length > 0) {
579
+ log.dim(`→ ${impact.length} connected entit${impact.length === 1 ? "y" : "ies"} may be affected: ${impact.map(refLabel).join(", ")}`);
580
+ }
524
581
  }
525
582
  catch (e) {
526
- console.error(chalk.red("Error: " + e.message));
583
+ renderHubError(e);
527
584
  process.exit(1);
528
585
  }
529
586
  }