@astrale-os/cli 1.0.0-beta.3 → 1.0.0-beta.30

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 (343) hide show
  1. package/README.md +80 -6
  2. package/dist/astrale.js +55471 -58674
  3. package/dist/public/connect-core.js +2158 -30706
  4. package/dist/public/keys/index.js +1016 -29541
  5. package/dist/public/paths/index.js +370 -28654
  6. package/dist/types/admin/contract.d.ts +26 -0
  7. package/dist/types/admin/instance/client.d.ts +20 -0
  8. package/dist/types/admin/instance/index.d.ts +2 -0
  9. package/dist/types/admin/instance/model.d.ts +6 -10
  10. package/dist/types/connection/auth.d.ts +3 -0
  11. package/dist/types/connection/call.d.ts +3 -0
  12. package/dist/types/connection/command.d.ts +27 -0
  13. package/dist/types/connection/credential.d.ts +14 -0
  14. package/dist/types/connection/errors.d.ts +1 -0
  15. package/dist/types/connection/exchange.d.ts +9 -0
  16. package/dist/types/connection/failure/classify.d.ts +2 -0
  17. package/dist/types/connection/failure/index.d.ts +5 -0
  18. package/dist/types/connection/failure/model.d.ts +25 -0
  19. package/dist/types/connection/failure/render.d.ts +3 -0
  20. package/dist/types/connection/index.d.ts +8 -0
  21. package/dist/types/connection/reasons.d.ts +37 -0
  22. package/dist/types/connection/self.d.ts +37 -0
  23. package/dist/types/connection/session.d.ts +31 -0
  24. package/dist/types/connection/target.d.ts +38 -0
  25. package/dist/types/errors.d.ts +1 -1
  26. package/dist/types/lib/admin-instance.d.ts +17 -0
  27. package/dist/types/lib/admin-target.d.ts +11 -36
  28. package/dist/types/lib/config.d.ts +13 -59
  29. package/dist/types/lib/failure-debug.d.ts +1 -0
  30. package/dist/types/lib/idp.d.ts +25 -173
  31. package/dist/types/lib/instance-target.d.ts +1 -1
  32. package/dist/types/lib/instance.d.ts +30 -88
  33. package/dist/types/lib/log.d.ts +5 -3
  34. package/dist/types/lib/output.d.ts +1 -1
  35. package/dist/types/lib/proc.d.ts +43 -0
  36. package/dist/types/lib/self.d.ts +9 -0
  37. package/dist/types/lib/update.d.ts +109 -0
  38. package/dist/types/lib/validation.d.ts +4 -1
  39. package/dist/types/state/exchange-credentials.d.ts +6 -2
  40. package/dist/types/state/files.d.ts +2 -0
  41. package/dist/types/state/index.d.ts +3 -2
  42. package/dist/types/state/paths.d.ts +2 -0
  43. package/dist/types/state/session-routes.d.ts +10 -0
  44. package/package.json +16 -19
  45. package/src/__tests__/fixtures/publication.ts +20 -0
  46. package/src/admin/.spec/architecture.md +12 -5
  47. package/src/admin/__tests__/fixture.ts +25 -0
  48. package/src/admin/catalog/.spec/api.d.ts +1 -3
  49. package/src/admin/catalog/.spec/architecture.md +5 -4
  50. package/src/admin/catalog/__tests__/client.test.ts +143 -69
  51. package/src/admin/catalog/client.ts +40 -54
  52. package/src/admin/catalog/model.ts +3 -4
  53. package/src/admin/contract.ts +47 -0
  54. package/src/admin/graph/.spec/api.d.ts +4 -10
  55. package/src/admin/graph/nodes.ts +1 -1
  56. package/src/admin/instance/.spec/api.d.ts +5 -9
  57. package/src/admin/instance/.spec/architecture.md +7 -7
  58. package/src/admin/instance/__tests__/client.test.ts +157 -175
  59. package/src/admin/instance/client.ts +39 -128
  60. package/src/admin/instance/index.ts +1 -2
  61. package/src/admin/instance/model.ts +12 -18
  62. package/src/commands/__tests__/admin-instance.test.ts +11 -8
  63. package/src/commands/__tests__/call-describe.test.ts +46 -33
  64. package/src/commands/__tests__/call.test.ts +34 -0
  65. package/src/commands/__tests__/domain-install-operation.test.ts +30 -22
  66. package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
  67. package/src/commands/__tests__/domain-list.test.ts +5 -25
  68. package/src/commands/__tests__/install-direct.test.ts +115 -8
  69. package/src/commands/__tests__/install-identity-override.test.ts +6 -17
  70. package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
  71. package/src/commands/__tests__/instance-status.test.ts +139 -0
  72. package/src/commands/__tests__/introspect-parse.test.ts +80 -1
  73. package/src/commands/__tests__/logs.test.ts +250 -2
  74. package/src/commands/__tests__/read-commands.test.ts +118 -10
  75. package/src/commands/__tests__/token-ttl.test.ts +49 -4
  76. package/src/commands/__tests__/update.test.ts +25 -3
  77. package/src/commands/__tests__/view.test.ts +50 -2
  78. package/src/commands/auth/logout.ts +2 -1
  79. package/src/commands/browser.ts +29 -0
  80. package/src/commands/call-describe.ts +60 -68
  81. package/src/commands/call.ts +71 -59
  82. package/src/commands/domain/install.ts +73 -46
  83. package/src/commands/domain/list.ts +1 -1
  84. package/src/commands/domain/publish.ts +14 -11
  85. package/src/commands/domain/uninstall.ts +2 -2
  86. package/src/commands/get.ts +15 -16
  87. package/src/commands/identity/__tests__/register.test.ts +3 -3
  88. package/src/commands/identity/register.ts +8 -8
  89. package/src/commands/instance/create.ts +8 -16
  90. package/src/commands/instance/delete.ts +12 -9
  91. package/src/commands/instance/list.ts +2 -2
  92. package/src/commands/instance/status.ts +66 -20
  93. package/src/commands/instance/use.ts +16 -12
  94. package/src/commands/introspect.ts +53 -41
  95. package/src/commands/logs.ts +123 -36
  96. package/src/commands/mutate.ts +6 -5
  97. package/src/commands/query.ts +35 -32
  98. package/src/commands/session/analyze.ts +26 -4
  99. package/src/commands/token.ts +42 -27
  100. package/src/commands/ui/__tests__/commands.test.ts +152 -0
  101. package/src/commands/ui/add.ts +51 -0
  102. package/src/commands/ui/doctor.ts +21 -0
  103. package/src/commands/ui/init.ts +38 -0
  104. package/src/commands/ui/list.ts +26 -0
  105. package/src/commands/ui/preset-apply.ts +19 -0
  106. package/src/commands/ui/preset-list.ts +12 -0
  107. package/src/commands/ui/shared.ts +25 -0
  108. package/src/commands/update.ts +89 -48
  109. package/src/commands/view.ts +82 -37
  110. package/src/connection/.spec/api.d.ts +7 -8
  111. package/src/connection/.spec/architecture.md +14 -5
  112. package/src/connection/.spec/flows/session.ts +2 -2
  113. package/src/connection/.spec/laws/connection.ts +6 -2
  114. package/src/connection/.spec/layout.ts +1 -0
  115. package/src/connection/__tests__/auth.test.ts +1 -0
  116. package/src/connection/__tests__/credential.test.ts +21 -1
  117. package/src/connection/__tests__/errors.test.ts +27 -20
  118. package/src/connection/__tests__/exchange.test.ts +64 -11
  119. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  120. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  121. package/src/connection/__tests__/self.test.ts +31 -11
  122. package/src/connection/__tests__/session.test.ts +8 -4
  123. package/src/connection/__tests__/target.test.ts +2 -0
  124. package/src/connection/auth.ts +13 -0
  125. package/src/connection/call.ts +1 -1
  126. package/src/connection/credential.ts +2 -2
  127. package/src/connection/errors.ts +1 -355
  128. package/src/connection/exchange.ts +110 -61
  129. package/src/connection/failure/classify.ts +110 -0
  130. package/src/connection/failure/index.ts +18 -0
  131. package/src/connection/failure/model.ts +28 -0
  132. package/src/connection/failure/render.ts +116 -0
  133. package/src/connection/reasons.ts +16 -0
  134. package/src/connection/self.ts +39 -21
  135. package/src/connection/session.ts +27 -9
  136. package/src/errors.ts +3 -2
  137. package/src/graph/.spec/api.d.ts +12 -5
  138. package/src/graph/.spec/layout.ts +9 -1
  139. package/src/graph/__tests__/mutation.test.ts +2 -2
  140. package/src/graph/__tests__/query.test.ts +25 -6
  141. package/src/graph/class.ts +30 -0
  142. package/src/graph/index.ts +1 -0
  143. package/src/graph/query.ts +21 -32
  144. package/src/identity/.spec/api.d.ts +2 -2
  145. package/src/identity/__tests__/fixtures/registry-journey.ts +13 -1
  146. package/src/identity/__tests__/registration.test.ts +1 -1
  147. package/src/identity/__tests__/registry.test.ts +4 -0
  148. package/src/identity/registration.ts +3 -3
  149. package/src/identity/registry.ts +2 -0
  150. package/src/keys/__tests__/keys.test.ts +4 -0
  151. package/src/keys/credential.ts +1 -0
  152. package/src/lib/__tests__/admin-target.test.ts +12 -0
  153. package/src/lib/__tests__/binary.test.ts +16 -1
  154. package/src/lib/__tests__/browser-retention.test.ts +212 -0
  155. package/src/lib/__tests__/command-dx.test.ts +1 -1
  156. package/src/lib/__tests__/config.test.ts +27 -0
  157. package/src/lib/__tests__/domain-publication.test.ts +10 -16
  158. package/src/lib/__tests__/idp.test.ts +34 -4
  159. package/src/lib/__tests__/instance-candidates.test.ts +1 -3
  160. package/src/lib/__tests__/instance-target.test.ts +7 -39
  161. package/src/lib/__tests__/log-errors.test.ts +64 -0
  162. package/src/lib/__tests__/skills.test.ts +809 -0
  163. package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
  164. package/src/lib/__tests__/update.test.ts +264 -11
  165. package/src/lib/__tests__/view-assets.test.ts +25 -0
  166. package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
  167. package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
  168. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  169. package/src/lib/__tests__/view-server.test.ts +26 -3
  170. package/src/lib/__tests__/view-session.test.ts +2 -1
  171. package/src/lib/admin-instance.ts +14 -8
  172. package/src/lib/admin-target.ts +21 -2
  173. package/src/lib/binary.ts +22 -5
  174. package/src/lib/browser-retention.ts +210 -0
  175. package/src/lib/command-dx.ts +9 -9
  176. package/src/lib/config.ts +12 -1
  177. package/src/lib/domain-publication.ts +9 -7
  178. package/src/lib/failure-debug.ts +26 -0
  179. package/src/lib/idp.ts +34 -3
  180. package/src/lib/instance-candidates.ts +2 -2
  181. package/src/lib/instance-target.ts +3 -14
  182. package/src/lib/instance.ts +16 -11
  183. package/src/lib/log.ts +16 -9
  184. package/src/lib/output.ts +1 -1
  185. package/src/lib/proc.ts +7 -2
  186. package/src/lib/provision-instance.ts +34 -57
  187. package/src/lib/skills/lock.ts +117 -0
  188. package/src/lib/skills/sync.ts +814 -0
  189. package/src/lib/skills.ts +1 -52
  190. package/src/lib/update.ts +298 -45
  191. package/src/lib/validation.ts +2 -2
  192. package/src/lib/view/assets.ts +11 -2
  193. package/src/lib/view/external-open-intent.ts +41 -0
  194. package/src/lib/view/external-open-origins.ts +37 -0
  195. package/src/lib/view/host-capabilities.ts +17 -0
  196. package/src/lib/view/resolve.ts +1 -1
  197. package/src/lib/view/server.ts +18 -3
  198. package/src/lib/view/session.ts +2 -0
  199. package/src/program/.spec/api.d.ts +1 -0
  200. package/src/program/__tests__/program.test.ts +163 -6
  201. package/src/program/argv.ts +14 -0
  202. package/src/program/build.ts +26 -4
  203. package/src/program/command.ts +1 -0
  204. package/src/program/index.ts +1 -0
  205. package/src/program/registry.ts +2 -1
  206. package/src/setup/__tests__/instance-step.test.ts +27 -10
  207. package/src/setup/steps/instance.ts +22 -15
  208. package/src/setup/steps/skills.ts +40 -38
  209. package/src/state/.spec/api.d.ts +21 -2
  210. package/src/state/.spec/architecture.md +18 -4
  211. package/src/state/.spec/layout.ts +1 -0
  212. package/src/state/__tests__/exchange-credentials.test.ts +88 -42
  213. package/src/state/__tests__/files.test.ts +24 -1
  214. package/src/state/__tests__/fixtures/session-route-process.ts +93 -0
  215. package/src/state/__tests__/paths.test.ts +1 -0
  216. package/src/state/__tests__/session-routes.test.ts +138 -0
  217. package/src/state/exchange-credentials.ts +22 -9
  218. package/src/state/files.ts +41 -0
  219. package/src/state/index.ts +3 -1
  220. package/src/state/paths.ts +3 -0
  221. package/src/state/session-routes.ts +34 -0
  222. package/src/telemetry/__tests__/analyze-log.test.ts +38 -0
  223. package/src/telemetry/__tests__/recorder.test.ts +1 -0
  224. package/src/telemetry/__tests__/retention.test.ts +267 -0
  225. package/src/telemetry/__tests__/settings.test.ts +102 -0
  226. package/src/telemetry/__tests__/store-scan.test.ts +128 -0
  227. package/src/telemetry/__tests__/trigger.test.ts +33 -4
  228. package/src/telemetry/analyze.ts +24 -2
  229. package/src/telemetry/recorder.ts +15 -4
  230. package/src/telemetry/retention.ts +176 -0
  231. package/src/telemetry/session.ts +18 -12
  232. package/src/telemetry/settings.ts +64 -11
  233. package/src/telemetry/store.ts +92 -9
  234. package/src/telemetry/trigger.ts +16 -28
  235. package/src/ui/.spec/api.d.ts +14 -0
  236. package/src/ui/.spec/architecture.md +14 -0
  237. package/src/ui/.spec/laws.ts +50 -0
  238. package/src/ui/.spec/layout.ts +16 -0
  239. package/src/ui/__tests__/ui.test.ts +1742 -0
  240. package/src/ui/index.ts +13 -0
  241. package/src/ui/lock.ts +89 -0
  242. package/src/ui/model.ts +83 -0
  243. package/src/ui/operations.ts +1044 -0
  244. package/src/ui/project.ts +333 -0
  245. package/src/ui/release.ts +398 -0
  246. package/src/ui/runner.ts +18 -0
  247. package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
  248. package/studio/client/dist/assets/index-BFVFs_8x.js +81 -0
  249. package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
  250. package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  251. package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
  252. package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
  253. package/studio/client/dist/index.html +4 -3
  254. package/studio/package.json +8 -14
  255. package/studio/server/agent/ask.test.ts +1 -1
  256. package/studio/server/agent/bridge/grant.test.ts +1 -1
  257. package/studio/server/agent/bridge/routes.test.ts +1 -1
  258. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  259. package/studio/server/agent/harness/gateway/token.test.ts +1 -1
  260. package/studio/server/agent/harness/gateway/token.ts +3 -3
  261. package/studio/server/agent/prompts/anchors.test.ts +41 -32
  262. package/studio/server/agent/prompts/anchors.ts +23 -51
  263. package/studio/server/agent/prompts/system.test.ts +4 -4
  264. package/studio/server/agent/prompts/system.ts +5 -5
  265. package/studio/server/agent/routes.test.ts +1 -1
  266. package/studio/server/agent/run/coordinator.test.ts +1 -1
  267. package/studio/server/agent/run/preparation.ts +1 -1
  268. package/studio/server/api/canvas.ts +0 -1
  269. package/studio/server/api/context.ts +1 -1
  270. package/studio/server/api/deployment.ts +1 -1
  271. package/studio/server/api/views.ts +2 -2
  272. package/studio/server/api/workspace.ts +1 -1
  273. package/studio/server/api-agent-loadout.test.ts +1 -1
  274. package/studio/server/api.test.ts +1 -1
  275. package/studio/server/cache.test.ts +24 -18
  276. package/studio/server/cache.ts +10 -54
  277. package/studio/server/cli-consumers.test.ts +31 -3
  278. package/studio/server/domain.test.ts +50 -68
  279. package/studio/server/domain.ts +82 -63
  280. package/studio/server/handoff/copy.ts +2 -2
  281. package/studio/server/index.ts +1 -1
  282. package/studio/server/instances/probe.test.ts +1 -1
  283. package/studio/server/introspect/anatomy/views/routes.ts +46 -37
  284. package/studio/server/introspect/anatomy/views.ts +10 -16
  285. package/studio/server/introspect/anatomy-extras.test.ts +29 -56
  286. package/studio/server/introspect/anatomy.test.ts +22 -14
  287. package/studio/server/introspect/anatomy.ts +6 -9
  288. package/studio/server/introspect/bundle.ts +0 -3
  289. package/studio/server/introspect/canonical-schema.test.ts +155 -424
  290. package/studio/server/introspect/canonical-schema.ts +248 -601
  291. package/studio/server/introspect/core.ts +14 -22
  292. package/studio/server/introspect/diff.test.ts +21 -9
  293. package/studio/server/introspect/diff.ts +19 -64
  294. package/studio/server/introspect/extractor.ts +23 -122
  295. package/studio/server/introspect/overlay-tsmorph.test.ts +116 -241
  296. package/studio/server/introspect/overlay-tsmorph.ts +0 -1
  297. package/studio/server/introspect/overlay.ts +3 -51
  298. package/studio/server/introspect/revision.test.ts +24 -28
  299. package/studio/server/introspect/revision.ts +11 -22
  300. package/studio/server/introspect/runtime.test.ts +77 -195
  301. package/studio/server/introspect/runtime.ts +5 -67
  302. package/studio/server/introspect/schema-ir-json.test.ts +81 -0
  303. package/studio/server/introspect/schema-ir-json.ts +39 -69
  304. package/studio/server/introspect/schema-refs.test.ts +43 -88
  305. package/studio/server/introspect/schema-refs.ts +14 -49
  306. package/studio/server/introspect/source-overlay/handlers.ts +116 -636
  307. package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
  308. package/studio/server/introspect/source-overlay/spans.ts +15 -45
  309. package/studio/server/lifecycle.ts +4 -1
  310. package/studio/server/state/baseline.test.ts +14 -9
  311. package/studio/server/state/baseline.ts +3 -5
  312. package/studio/server/state/documents.test.ts +69 -0
  313. package/studio/server/state/documents.ts +57 -10
  314. package/studio/server/state/visibility.ts +1 -5
  315. package/studio/server/views/model.ts +3 -3
  316. package/studio/server/views/target.test.ts +18 -76
  317. package/studio/server/views/target.ts +24 -50
  318. package/studio/server/watch.test.ts +11 -11
  319. package/studio/server/watch.ts +8 -4
  320. package/studio/server/workspace/updates.ts +23 -1
  321. package/studio/server/workspace-watch.ts +2 -2
  322. package/studio/shared/contracts/runtime.ts +4 -0
  323. package/studio/shared/contracts/schema.ts +44 -131
  324. package/studio/shared/contracts/surface.test.ts +15 -17
  325. package/studio/shared/contracts/workspace.ts +5 -11
  326. package/studio/shared/schema/identity.test.ts +20 -44
  327. package/studio/shared/schema/identity.ts +9 -24
  328. package/studio/tsconfig.json +2 -1
  329. package/tsconfig.json +1 -1
  330. package/viewer/dist/index.html +21 -0
  331. package/viewer/dist/main.js +41 -62
  332. package/.check-workspace.cjs +0 -40
  333. package/src/admin/binding.ts +0 -168
  334. package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
  335. package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
  336. package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
  337. package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
  338. package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
  339. package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
  340. package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
  341. package/studio/server/introspect/core-extractor.ts +0 -139
  342. package/studio/server/introspect/overlay.test.ts +0 -72
  343. package/studio/server/introspect/source-overlay/annotations.ts +0 -14
@@ -5,7 +5,7 @@ import { createHash } from 'node:crypto'
5
5
  import { existsSync, readFileSync } from 'node:fs'
6
6
  import { join } from 'node:path'
7
7
 
8
- import { buildProgram } from '../index'
8
+ import { buildProgram, normalizeRootVersionArgv } from '../index'
9
9
 
10
10
  // Help output is the public CLI contract: version, spec anchors, and skill mirror stay in sync.
11
11
 
@@ -181,13 +181,21 @@ describe('program composition', () => {
181
181
  'status',
182
182
  'studio',
183
183
  'token',
184
+ 'ui',
185
+ 'ui add',
186
+ 'ui doctor',
187
+ 'ui init',
188
+ 'ui list',
189
+ 'ui preset',
190
+ 'ui preset apply',
191
+ 'ui preset list',
184
192
  'update',
185
193
  'use',
186
194
  'view',
187
195
  'whoami',
188
196
  ])
189
197
  expect(createHash('sha256').update(JSON.stringify(surface)).digest('hex')).toBe(
190
- '44f78959867171661197043f5b47eb6d6890274090370e071d10f11b4204850d',
198
+ '092b8f4f737740105c8baed9d01a81604711b5b2719dfcb834eeeac414732c61',
191
199
  )
192
200
  })
193
201
 
@@ -213,6 +221,19 @@ describe('help contract — no internal SPEC anchors leak to users', () => {
213
221
 
214
222
  expect(offenders).toEqual([])
215
223
  })
224
+
225
+ test('Kernel and Instance vocabulary does not expose retired Host or Interface concepts', async () => {
226
+ const program = await buildProgram()
227
+ const help = allCommands(program)
228
+ .map((command) => command.helpInformation())
229
+ .join('\n')
230
+
231
+ expect(help).not.toContain('host.astrale.ai')
232
+ expect(help).not.toContain(':interface.')
233
+ expect(help).not.toContain('Class or Interface')
234
+ expect(help).not.toContain('emulated host')
235
+ expect(help).not.toContain('Host shell')
236
+ })
216
237
  })
217
238
 
218
239
  describe('help contract — IdP/auth surface is registered', () => {
@@ -251,21 +272,24 @@ describe('help contract — admin target surface is registered', () => {
251
272
  expect(adminUse?.helpInformation()).not.toContain('--issuer <url>')
252
273
  })
253
274
 
254
- test('instance create is the alphaCreate flow, not legacy Instance.init DX', async () => {
275
+ test('instance create delegates infrastructure placement to Admin', async () => {
255
276
  const program = await buildProgram()
256
277
  const instanceCreate = allCommands(program).find((command) => command.name() === 'create')
257
278
  const help = instanceCreate?.helpInformation() ?? ''
258
279
 
259
- expect(help).toContain('Instance.alphaCreate')
280
+ expect(help).toContain('Provision an instance through the Admin control plane')
281
+ expect(help).not.toContain('Host')
282
+ expect(help).not.toContain('Fleet')
283
+ expect(help).not.toContain('--host-id')
260
284
  expect(help).not.toContain('--no-use')
261
- expect(help).not.toContain('Instance.init requires --host-id')
285
+ expect(help).not.toContain('Instance.init')
262
286
  })
263
287
  })
264
288
 
265
289
  describe('help contract — connect-only command surface', () => {
266
290
  test('runtime management commands are not registered', async () => {
267
291
  const program = await buildProgram()
268
- const names = allCommands(program).map((command) => command.name())
292
+ const names = program.commands.map((command) => command.name())
269
293
 
270
294
  // `logs` and `domain` have managed/admin meanings, so only retired verbs are absent.
271
295
  for (const removed of [
@@ -287,6 +311,139 @@ describe('help contract — connect-only command surface', () => {
287
311
  })
288
312
  })
289
313
 
314
+ describe('help contract — UI is project tooling', () => {
315
+ test('add recovery guidance uses only registered UI commands', async () => {
316
+ const program = await buildProgram()
317
+ const uiAdd = program.commands
318
+ .find((command) => command.name() === 'ui')
319
+ ?.commands.find((command) => command.name() === 'add')
320
+ let help = ''
321
+ uiAdd?.configureOutput({
322
+ writeOut: (chunk) => {
323
+ help += chunk ?? ''
324
+ },
325
+ })
326
+ uiAdd?.outputHelp()
327
+
328
+ expect(help).toContain('astrale ui doctor')
329
+ expect(help).toContain('--overwrite --yes')
330
+ expect(help).not.toMatch(/\b(?:astrale ui )?diff\b/u)
331
+ })
332
+
333
+ test('keeps the documented root version alias operational', async () => {
334
+ const program = await buildProgram()
335
+ let stdout = ''
336
+ program.exitOverride().configureOutput({
337
+ writeOut: (chunk) => {
338
+ stdout += chunk ?? ''
339
+ },
340
+ })
341
+
342
+ await expect(
343
+ program.parseAsync(normalizeRootVersionArgv(['node', 'astrale', '--version'])),
344
+ ).rejects.toMatchObject({ code: 'commander.version' })
345
+ expect(stdout.trim()).toBe(program.version() ?? '')
346
+ expect(program.helpInformation()).toMatch(/root alias:\s+--version/u)
347
+ })
348
+
349
+ test('routes root and subcommand version flags to their exact owners', async () => {
350
+ const program = await buildProgram()
351
+ const uiList = program.commands
352
+ .find((command) => command.name() === 'ui')
353
+ ?.commands.find((command) => command.name() === 'list')
354
+ let observedVersion: unknown
355
+ uiList?.action((_query, options) => {
356
+ observedVersion = options.version
357
+ })
358
+
359
+ const uiArgv = ['node', 'astrale', 'ui', 'list', 'chart', '--version', '0.3.0-beta.1']
360
+ expect(normalizeRootVersionArgv(uiArgv)).toEqual(uiArgv)
361
+ expect(normalizeRootVersionArgv(['node', 'astrale', '--version'])).toEqual([
362
+ 'node',
363
+ 'astrale',
364
+ '--cli-version',
365
+ ])
366
+ expect(normalizeRootVersionArgv(['node', 'astrale', '--ci', '--version'])).toEqual([
367
+ 'node',
368
+ 'astrale',
369
+ '--ci',
370
+ '--cli-version',
371
+ ])
372
+
373
+ await program.parseAsync(uiArgv)
374
+
375
+ expect(observedVersion).toBe('0.3.0-beta.1')
376
+ })
377
+
378
+ test('routes explicit versions with positional inputs for UI init and update', async () => {
379
+ const program = await buildProgram()
380
+ const uiInit = program.commands
381
+ .find((command) => command.name() === 'ui')
382
+ ?.commands.find((command) => command.name() === 'init')
383
+ const update = program.commands.find((command) => command.name() === 'update')
384
+ let initializedPath: unknown
385
+ let initializedVersion: unknown
386
+ let updateVersion: unknown
387
+ uiInit?.action((projectPath, options) => {
388
+ initializedPath = projectPath
389
+ initializedVersion = options.version
390
+ })
391
+ update?.action((options) => {
392
+ updateVersion = options.version
393
+ })
394
+
395
+ await program.parseAsync([
396
+ 'node',
397
+ 'astrale',
398
+ 'ui',
399
+ 'init',
400
+ './app',
401
+ '--version',
402
+ '0.3.0-beta.1',
403
+ ])
404
+ expect(initializedPath).toBe('./app')
405
+ expect(initializedVersion).toBe('0.3.0-beta.1')
406
+
407
+ await program.parseAsync(['node', 'astrale', 'update', '--version', '1.0.0-beta.13'])
408
+ expect(updateVersion).toBe('1.0.0-beta.13')
409
+ })
410
+
411
+ test('continues to admit global machine flags after a subcommand', async () => {
412
+ const program = await buildProgram()
413
+ const uiList = program.commands
414
+ .find((command) => command.name() === 'ui')
415
+ ?.commands.find((command) => command.name() === 'list')
416
+ let invoked = false
417
+ uiList?.action(() => {
418
+ invoked = true
419
+ })
420
+
421
+ await program.parseAsync(['node', 'astrale', 'ui', 'list', '--ci', '--no-prompt'])
422
+
423
+ expect(invoked).toBe(true)
424
+ })
425
+
426
+ test('UI commands are local-only and add accepts zero or more canonical addresses', async () => {
427
+ const program = await buildProgram()
428
+ const ui = program.commands.find((command) => command.name() === 'ui')
429
+ const add = ui?.commands.find((command) => command.name() === 'add')
430
+
431
+ expect(ui?.commands.map((command) => command.name())).toEqual([
432
+ 'init',
433
+ 'list',
434
+ 'add',
435
+ 'doctor',
436
+ 'preset',
437
+ ])
438
+ expect(add?.helpInformation()).toContain('[items...]')
439
+ for (const command of ui?.commands ?? []) {
440
+ const help = command.helpInformation()
441
+ expect(help).not.toContain('--url <url>')
442
+ expect(help).not.toContain('--anonymous')
443
+ }
444
+ })
445
+ })
446
+
290
447
  describe('help contract — read command split', () => {
291
448
  test('get is point-read only and query owns structured read flags', async () => {
292
449
  const program = await buildProgram()
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Keep `astrale --version` as the familiar root spelling without claiming the
3
+ * same flag after a subcommand. Commands such as `ui list` and `update` own
4
+ * their release `--version` value.
5
+ */
6
+ export function normalizeRootVersionArgv(argv: readonly string[]): string[] {
7
+ const normalized = [...argv]
8
+ const commandIndex = normalized.findIndex((token, index) => index >= 2 && !token.startsWith('-'))
9
+ const versionIndex = normalized.indexOf('--version', 2)
10
+ if (versionIndex !== -1 && (commandIndex === -1 || versionIndex < commandIndex)) {
11
+ normalized[versionIndex] = '--cli-version'
12
+ }
13
+ return normalized
14
+ }
@@ -19,7 +19,7 @@ export async function buildProgram(): Promise<Command> {
19
19
  .description('Astrale CLI — connect to existing Astrale kernels')
20
20
  // Single source of truth = package.json (bumped by release-please together
21
21
  // with .release-please-manifest.json). Never hand-write a version literal.
22
- .version(pkg.version)
22
+ .version(pkg.version, '-V, --cli-version', 'output the CLI version (root alias: --version)')
23
23
  .showSuggestionAfterError(true)
24
24
  .addOption(new Option('--ci', 'Machine mode: no prompts, structured errors on stderr'))
25
25
  .addOption(new Option('--no-prompt', 'Disable interactive prompts'))
@@ -63,6 +63,27 @@ export async function buildProgram(): Promise<Command> {
63
63
  registerCommand(program, (await import('../commands/view-serve')).default)
64
64
  registerCommand(program, (await import('../commands/studio')).default)
65
65
 
66
+ registerGroup(program, {
67
+ name: 'ui',
68
+ description: 'Initialize and install Astrale UI in local applications',
69
+ commands: [
70
+ (await import('../commands/ui/init')).default,
71
+ (await import('../commands/ui/list')).default,
72
+ (await import('../commands/ui/add')).default,
73
+ (await import('../commands/ui/doctor')).default,
74
+ ],
75
+ subgroups: [
76
+ {
77
+ name: 'preset',
78
+ description: 'List and apply qualified Astrale UI presets',
79
+ commands: [
80
+ (await import('../commands/ui/preset-list')).default,
81
+ (await import('../commands/ui/preset-apply')).default,
82
+ ],
83
+ },
84
+ ],
85
+ })
86
+
66
87
  registerGroup(program, {
67
88
  name: 'instance',
68
89
  description: 'Manage admin-provisioned instances and local bookmarks',
@@ -154,12 +175,13 @@ Command groups:
154
175
  Getting started setup (sign in, pick an instance, equip your workspace)
155
176
  Kernel get, mutate, call, query, introspect, logs, view, token
156
177
  Management admin, instance, domain, identity, auth, idp, update
178
+ Application ui (initialize, inspect, and install Astrale UI source)
157
179
  Agent browser (drive the GUI via agent-browser)
158
180
  Studio studio (launch the local Domain Studio GUI for a workspace)
159
181
 
160
182
  Path syntax:
161
183
  /:origin Domain root
162
- /:origin:class.Name Class node (or /:origin:interface.Name)
184
+ /:origin:class.Name Class node
163
185
  /:origin:class.Name:method Static callable
164
186
  <nodePath>::method Instance method dispatch — double colon ::
165
187
  @nodeId Reference a node by its UID
@@ -172,9 +194,9 @@ Examples:
172
194
  $ astrale instance bookmark staging --url https://kernel.example.com
173
195
  $ astrale instance create my-app
174
196
  $ astrale instance status staging
175
- $ astrale token --audience shell.astrale.ai --ttl 3600
197
+ $ astrale token --audience shell.astrale.ai
176
198
  $ astrale query /:notes.example.dev:class.Note --limit 50
177
- $ astrale introspect /:host.astrale.ai:class.Manager:createInstance
199
+ $ astrale introspect /:kernel.astrale.ai:class.Identity:whois
178
200
  $ astrale query --file query.v6.json --cursor "$CURSOR"
179
201
  `,
180
202
  )
@@ -2,6 +2,7 @@ export interface CommandArgument {
2
2
  readonly name: string
3
3
  readonly description: string
4
4
  readonly required?: boolean
5
+ readonly variadic?: boolean
5
6
  }
6
7
 
7
8
  export interface CommandOption {
@@ -1,2 +1,3 @@
1
1
  export { buildProgram } from './build.js'
2
+ export { normalizeRootVersionArgv } from './argv.js'
2
3
  export type { CommandArgument, CommandDefinition, CommandGroup, CommandOption } from './command.js'
@@ -18,7 +18,8 @@ export function registerCommand(parent: Command, def: CommandDefinition): void {
18
18
 
19
19
  if (def.arguments) {
20
20
  for (const arg of def.arguments) {
21
- const bracket = arg.required !== false ? `<${arg.name}>` : `[${arg.name}]`
21
+ const name = arg.variadic ? `${arg.name}...` : arg.name
22
+ const bracket = arg.required !== false ? `<${name}>` : `[${name}]`
22
23
  cmd.argument(bracket, arg.description)
23
24
  }
24
25
  }
@@ -27,11 +27,15 @@ function instance(slug: string, state: OwnedInstanceInfo['state'] = 'ready'): Ow
27
27
  }
28
28
  }
29
29
 
30
+ function inventory(instances: OwnedInstanceInfo[], identity?: string) {
31
+ return { instances, ...(identity ? { identity } : {}) }
32
+ }
33
+
30
34
  function dependencies(
31
35
  overrides: Partial<InstanceSetupDependencies> = {},
32
36
  ): InstanceSetupDependencies {
33
37
  return {
34
- fetchOwned: mock(async () => []),
38
+ fetchOwned: mock(async () => inventory([])),
35
39
  adopt: mock(async () => {}),
36
40
  selectReady: mock(async (instances) => instances[0] ?? null),
37
41
  confirmCreate: mock(async () => true),
@@ -48,12 +52,14 @@ describe('setup owned-instance reconciliation', () => {
48
52
  test('silently adopts the sole owned ready instance', async () => {
49
53
  const ready = instance('only')
50
54
  const failed = instance('old-attempt', 'failed')
51
- const deps = dependencies({ fetchOwned: mock(async () => [failed, ready]) })
55
+ const deps = dependencies({
56
+ fetchOwned: mock(async () => inventory([failed, ready], 'manager')),
57
+ })
52
58
 
53
59
  await expect(ensureOwnedInstance(ctx, deps)).resolves.toBe('fixed')
54
60
 
55
61
  expect(deps.adopt).toHaveBeenCalledTimes(1)
56
- expect(deps.adopt).toHaveBeenCalledWith(ready)
62
+ expect(deps.adopt).toHaveBeenCalledWith(ready, 'manager')
57
63
  expect(deps.selectReady).not.toHaveBeenCalled()
58
64
  expect(deps.confirmCreate).not.toHaveBeenCalled()
59
65
  expect(deps.provision).not.toHaveBeenCalled()
@@ -64,14 +70,14 @@ describe('setup owned-instance reconciliation', () => {
64
70
  const second = instance('second')
65
71
  const pending = instance('pending', 'provisioning')
66
72
  const deps = dependencies({
67
- fetchOwned: mock(async () => [first, pending, second]),
73
+ fetchOwned: mock(async () => inventory([first, pending, second], 'manager')),
68
74
  selectReady: mock(async (instances) => instances[1] ?? null),
69
75
  })
70
76
 
71
77
  await expect(ensureOwnedInstance(ctx, deps)).resolves.toBe('fixed')
72
78
 
73
79
  expect(deps.selectReady).toHaveBeenCalledWith([first, second])
74
- expect(deps.adopt).toHaveBeenCalledWith(second)
80
+ expect(deps.adopt).toHaveBeenCalledWith(second, 'manager')
75
81
  expect(deps.confirmCreate).not.toHaveBeenCalled()
76
82
  expect(deps.provision).not.toHaveBeenCalled()
77
83
  })
@@ -80,7 +86,7 @@ describe('setup owned-instance reconciliation', () => {
80
86
  const first = instance('first')
81
87
  const second = instance('second')
82
88
  const deps = dependencies({
83
- fetchOwned: mock(async () => [first, second]),
89
+ fetchOwned: mock(async () => inventory([first, second])),
84
90
  selectReady: mock(async () => null),
85
91
  })
86
92
 
@@ -165,7 +171,9 @@ describe('setup owned-instance reconciliation', () => {
165
171
  phase: 'installing:default-domains',
166
172
  }
167
173
  const failed = { ...instance('broken', 'failed'), error: 'postInstall failed' }
168
- const deps = dependencies({ fetchOwned: mock(async () => [provisioning, failed]) })
174
+ const deps = dependencies({
175
+ fetchOwned: mock(async () => inventory([provisioning, failed])),
176
+ })
169
177
  const logged: string[] = []
170
178
  const original = console.log
171
179
  console.log = mock((...args: unknown[]) => {
@@ -218,10 +226,10 @@ describe('owned-instance adoption', () => {
218
226
  console.log = mock(() => {})
219
227
 
220
228
  try {
221
- await adoptOwnedInstance(owned, {
229
+ await adoptOwnedInstance(owned, 'manager', {
222
230
  upsert: mock(async (...args) => {
223
231
  calls.push(['upsert', ...args])
224
- return {}
232
+ return { entry: {} }
225
233
  }),
226
234
  activate: mock(async (...args) => {
227
235
  calls.push(['activate', ...args])
@@ -232,7 +240,16 @@ describe('owned-instance adoption', () => {
232
240
  }
233
241
 
234
242
  expect(calls).toEqual([
235
- ['upsert', 'existing', 'existing', 'https://existing.eu.astrale.ai', 'org_existing'],
243
+ [
244
+ 'upsert',
245
+ {
246
+ key: 'existing',
247
+ slug: 'existing',
248
+ url: 'https://existing.eu.astrale.ai',
249
+ organizationId: 'org_existing',
250
+ defaultIdentity: 'manager',
251
+ },
252
+ ],
236
253
  ['activate', 'existing'],
237
254
  ])
238
255
  })
@@ -4,7 +4,7 @@ import type { OwnedInstanceInfo } from '../../lib/admin-instance'
4
4
  import type { SetupContext, SetupStep } from '../types'
5
5
 
6
6
  import { AstraleError } from '../../errors'
7
- import { listOwnedInstances } from '../../lib/admin-instance'
7
+ import { listOwnedInstancesWithIdentity } from '../../lib/admin-instance'
8
8
  import { normalizeInstanceKernelUrl, setActive, upsertManagedBookmark } from '../../lib/instance'
9
9
  import { readLocalStatus } from '../../lib/local-status'
10
10
  import { log, withSpinner } from '../../lib/log'
@@ -14,8 +14,11 @@ import { provisionInstance, type ProvisionResult } from '../../lib/provision-ins
14
14
  import { guiOrigin, slugError } from '../util'
15
15
 
16
16
  export type InstanceSetupDependencies = {
17
- fetchOwned: (ctx: SetupContext) => Promise<OwnedInstanceInfo[]>
18
- adopt: (info: OwnedInstanceInfo) => Promise<void>
17
+ fetchOwned: (ctx: SetupContext) => Promise<{
18
+ readonly instances: OwnedInstanceInfo[]
19
+ readonly identity?: string
20
+ }>
21
+ adopt: (info: OwnedInstanceInfo, identity?: string) => Promise<void>
19
22
  selectReady: (instances: OwnedInstanceInfo[]) => Promise<OwnedInstanceInfo | null>
20
23
  confirmCreate: () => Promise<boolean>
21
24
  promptSlug: () => Promise<string | undefined>
@@ -23,12 +26,7 @@ export type InstanceSetupDependencies = {
23
26
  }
24
27
 
25
28
  export type OwnedInstanceAdoptionDependencies = {
26
- upsert: (
27
- key: string,
28
- slug: string,
29
- url: string,
30
- organizationId?: string,
31
- ) => Promise<{ repointedFrom?: string }>
29
+ upsert: typeof upsertManagedBookmark
32
30
  activate: (slug: string) => Promise<unknown>
33
31
  }
34
32
 
@@ -41,12 +39,19 @@ function hero(slug: string, kernelUrl: string): void {
41
39
 
42
40
  export async function adoptOwnedInstance(
43
41
  info: OwnedInstanceInfo,
42
+ defaultIdentity?: string,
44
43
  deps: OwnedInstanceAdoptionDependencies = {
45
44
  upsert: upsertManagedBookmark,
46
45
  activate: setActive,
47
46
  },
48
47
  ): Promise<void> {
49
- const { repointedFrom } = await deps.upsert(info.slug, info.slug, info.url, info.organizationId)
48
+ const { repointedFrom } = await deps.upsert({
49
+ key: info.slug,
50
+ slug: info.slug,
51
+ url: info.url,
52
+ ...(info.organizationId ? { organizationId: info.organizationId } : {}),
53
+ ...(defaultIdentity ? { defaultIdentity } : {}),
54
+ })
50
55
  await deps.activate(info.slug)
51
56
  if (repointedFrom) {
52
57
  log.warn(
@@ -61,7 +66,7 @@ function defaultDependencies(ctx: SetupContext): InstanceSetupDependencies {
61
66
  return {
62
67
  fetchOwned: (setupCtx) =>
63
68
  withSpinner('Checking for existing instances', !setupCtx.machine, () =>
64
- listOwnedInstances(setupCtx.opts),
69
+ listOwnedInstancesWithIdentity(setupCtx.opts),
65
70
  ),
66
71
  adopt: adoptOwnedInstance,
67
72
  selectReady: (instances) =>
@@ -90,9 +95,9 @@ export async function ensureOwnedInstance(
90
95
  ctx: SetupContext,
91
96
  deps: InstanceSetupDependencies = defaultDependencies(ctx),
92
97
  ): Promise<'fixed' | 'skipped'> {
93
- let owned: OwnedInstanceInfo[]
98
+ let inventory: Awaited<ReturnType<InstanceSetupDependencies['fetchOwned']>>
94
99
  try {
95
- owned = await deps.fetchOwned(ctx)
100
+ inventory = await deps.fetchOwned(ctx)
96
101
  } catch (cause) {
97
102
  const detail = cause instanceof Error ? cause.message : String(cause)
98
103
  throw new AstraleError(
@@ -102,9 +107,11 @@ export async function ensureOwnedInstance(
102
107
  )
103
108
  }
104
109
 
110
+ const owned = inventory.instances
111
+
105
112
  const ready = owned.filter((info) => info.state === 'ready')
106
113
  if (ready.length === 1) {
107
- await deps.adopt(ready[0]!)
114
+ await deps.adopt(ready[0]!, inventory.identity)
108
115
  return 'fixed'
109
116
  }
110
117
 
@@ -114,7 +121,7 @@ export async function ensureOwnedInstance(
114
121
  log.dim(' Skipped — no active instance set.')
115
122
  return 'skipped'
116
123
  }
117
- await deps.adopt(choice)
124
+ await deps.adopt(choice, inventory.identity)
118
125
  return 'fixed'
119
126
  }
120
127
 
@@ -1,59 +1,61 @@
1
1
  import type { SetupStep } from '../types'
2
2
 
3
3
  import { log } from '../../lib/log'
4
- import {
5
- ASTRALE_CLI_SKILL,
6
- ASTRALE_DOMAIN_SKILL,
7
- detectSkill,
8
- installSkills,
9
- SKILL_INSTALL_HINT,
10
- } from '../../lib/skills'
4
+ import { checkAstraleSkills, SKILL_INSTALL_HINT, syncAstraleSkills } from '../../lib/skills'
11
5
 
12
6
  const FIX = SKILL_INSTALL_HINT
13
7
 
14
8
  /**
15
- * Equip — the astrale agent skills. Both the astrale-cli (ops) and astrale-domain
16
- * (authoring) skills publish from the one public `astrale-os/cli` repo, so a
17
- * single `npx skills add astrale-os/cli -g` installs both, globally. We don't
18
- * reimplement skill installation; we detect presence and delegate to `npx skills
19
- * add` (the skill package manager). A global (user-level) install equips every
20
- * project on the machine, so a freshly scaffolded domain has the skills without
21
- * shipping its own copy.
9
+ * Equip — reconcile every top-level Astrale-owned skill from astrale-os/cli main.
10
+ * Setup and update deliberately share one owner, so an existence-only setup check
11
+ * can never bless an incomplete or stale global cohort.
22
12
  */
23
- const bothInstalled = (): boolean =>
24
- detectSkill(ASTRALE_CLI_SKILL).installed && detectSkill(ASTRALE_DOMAIN_SKILL).installed
25
-
26
13
  export const skillsStep: SetupStep = {
27
14
  id: 'skills',
28
- title: 'astrale agent skills (cli + domain)',
15
+ title: 'Astrale agent skills',
29
16
  group: 'equip',
30
17
 
31
18
  async detect() {
32
- if (bothInstalled()) {
33
- return { state: 'satisfied', summary: 'astrale-cli + astrale-domain skills installed' }
19
+ const result = await checkAstraleSkills()
20
+ if (result.status === 'current') {
21
+ return { state: 'satisfied', summary: 'Astrale skills are installed and up to date' }
22
+ }
23
+ if (result.status === 'unavailable') {
24
+ return {
25
+ state: 'broken',
26
+ summary: 'Astrale skills could not be verified',
27
+ detail: result.error,
28
+ fixHint: FIX,
29
+ }
30
+ }
31
+ return {
32
+ state: 'gap',
33
+ summary:
34
+ result.status === 'repair-needed'
35
+ ? 'Astrale skills need repair'
36
+ : 'Astrale skills need installation or update',
37
+ fixHint: FIX,
34
38
  }
35
- const have = detectSkill(ASTRALE_CLI_SKILL).installed
36
- ? 'astrale-domain'
37
- : 'astrale agent skills'
38
- return { state: 'gap', summary: `${have} not installed`, fixHint: FIX }
39
39
  },
40
40
 
41
41
  async ensure() {
42
- if (bothInstalled()) {
43
- log.success('astrale-cli + astrale-domain skills already installed')
44
- return 'unchanged'
45
- }
46
-
47
- log.step(`Installing the astrale agent skills — ${FIX}`)
48
- if (!(await installSkills())) {
49
- log.warn(`Skill install did not complete — run it later: ${FIX}`)
42
+ log.step('Ensuring Astrale agent skills are current and healthy')
43
+ try {
44
+ const result = await syncAstraleSkills()
45
+ if (result.status === 'unchanged') {
46
+ log.success('Astrale skills already up to date')
47
+ return 'unchanged'
48
+ }
49
+ if (result.status === 'installed') log.success('Astrale skills installed')
50
+ else if (result.status === 'updated') log.success('Astrale skills updated')
51
+ else if (result.status === 'repaired') log.success('Astrale skills repaired and updated')
52
+ return 'fixed'
53
+ } catch (error) {
54
+ log.warn(
55
+ `Astrale skills could not be installed safely${error instanceof Error ? `: ${error.message}` : ''}`,
56
+ )
57
+ log.dim(` Retry: ${FIX}`)
50
58
  return 'failed'
51
59
  }
52
- if (bothInstalled()) {
53
- log.success('astrale-cli + astrale-domain skills installed')
54
- } else {
55
- log.success('astrale-cli skill installed')
56
- }
57
- return 'fixed'
58
60
  },
59
61
  }