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