@astryxdesign/cli 0.7.2 → 0.7.3-canary.0bf23bd

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 (337) hide show
  1. package/README.md +104 -71
  2. package/api/build/_adapter.d.mts +58 -0
  3. package/api/build/_adapter.mjs +115 -3
  4. package/api/build/blocks/blocks.d.mts +42 -0
  5. package/api/build/blocks/blocks.mjs +169 -0
  6. package/api/build/blocks/blocks.test.mjs +108 -0
  7. package/api/build/blocks/blocks.weights.json +1 -0
  8. package/api/build/build.mjs +25 -6
  9. package/api/build/kit/answer.d.mts +14 -0
  10. package/api/build/kit/answer.mjs +85 -0
  11. package/api/build/kit/answer.test.mjs +38 -0
  12. package/api/build/kit/kind.d.mts +25 -0
  13. package/api/build/kit/kind.mjs +170 -0
  14. package/api/build/kit/kit.d.mts +2 -4
  15. package/api/build/kit/kit.mjs +8 -398
  16. package/api/build/kit/rank.d.mts +3 -32
  17. package/api/build/kit/rank.mjs +25 -342
  18. package/api/build/kit/rank.test.mjs +2 -1
  19. package/api/build/kit/signals.d.mts +60 -0
  20. package/api/build/kit/signals.mjs +284 -0
  21. package/api/build/kit/start.d.mts +32 -0
  22. package/api/build/kit/start.mjs +163 -0
  23. package/api/build/kit/words.d.mts +72 -0
  24. package/api/build/kit/words.mjs +202 -0
  25. package/api/build/tree.d.mts +20 -0
  26. package/api/build/tree.mjs +52 -0
  27. package/api/build/tree.test.mjs +177 -0
  28. package/api/build/tree.weights.json +1 -0
  29. package/api/component/component.mjs +17 -3
  30. package/api/component/component.test.mjs +15 -0
  31. package/api/component/doc-links.test.mjs +2 -2
  32. package/api/discover/_adapter.mjs +36 -9
  33. package/api/discover/_adapter.test.mjs +42 -0
  34. package/api/docs/_adapter.d.mts +2 -2
  35. package/api/docs/_adapter.mjs +78 -23
  36. package/api/docs/docs-homes.test.mjs +127 -0
  37. package/api/docs/docs.doc.mjs +3 -3
  38. package/api/docs/docs.test.mjs +35 -19
  39. package/api/docs/integration-tree.test.mjs +20 -20
  40. package/api/docs/integrationDocs.test.mjs +1 -1
  41. package/api/docs/namespace-section.test.mjs +6 -6
  42. package/api/docs/reference-blocks.test.mjs +2 -2
  43. package/api/doctor/doctor.mjs +102 -24
  44. package/api/doctor/doctor.test.mjs +37 -12
  45. package/api/hook/hook.mjs +1 -1
  46. package/api/init/init.doc.mjs +20 -6
  47. package/api/init/init.test.mjs +18 -2
  48. package/api/init/init.type.d.mts +3 -3
  49. package/api/init/init.type.mjs +3 -3
  50. package/api/init/remove/remove.mjs +17 -4
  51. package/api/init/remove/remove.test.mjs +114 -2
  52. package/api/init/run/run.mjs +69 -6
  53. package/api/integration/_adapter.d.mts +8 -0
  54. package/api/integration/_adapter.mjs +21 -1
  55. package/api/integration/authoring-checks.mjs +83 -9
  56. package/api/integration/authoring-checks.test.mjs +220 -0
  57. package/api/integration/pack-check.mjs +123 -4
  58. package/api/integration/pack-check.test.mjs +87 -0
  59. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  60. package/api/integration/validate-integration-gaps.test.mjs +1964 -0
  61. package/api/integration/validate-integration.d.mts +11 -2
  62. package/api/integration/validate-integration.mjs +250 -32
  63. package/api/integration/validate-integration.type.d.mts +59 -1
  64. package/api/integration/validate-integration.type.mjs +46 -2
  65. package/api/integration/validateIntegration.doc.mjs +1 -1
  66. package/api/search/ORDERING.md +28 -0
  67. package/api/search/_adapter.d.mts +82 -2
  68. package/api/search/_adapter.mjs +157 -10
  69. package/api/search/_adapter.test.mjs +173 -0
  70. package/api/search/_candidate-score.d.mts +40 -0
  71. package/api/search/_candidate-score.mjs +238 -0
  72. package/api/search/_cards.json +6098 -0
  73. package/api/search/_compound.d.mts +50 -0
  74. package/api/search/_compound.mjs +170 -0
  75. package/api/search/_doc-searcher.d.mts +4 -0
  76. package/api/search/_doc-searcher.mjs +23 -0
  77. package/api/search/_doc-searcher.test.mjs +36 -0
  78. package/api/search/_doc-searcher.weights.json +1 -0
  79. package/api/search/_doc-text.d.mts +30 -0
  80. package/api/search/_doc-text.mjs +87 -0
  81. package/api/search/_entries.d.mts +52 -0
  82. package/api/search/_entries.mjs +214 -0
  83. package/api/search/_features.d.mts +35 -0
  84. package/api/search/_features.mjs +155 -0
  85. package/api/search/_gather-components.d.mts +26 -0
  86. package/api/search/_gather-components.mjs +284 -0
  87. package/api/search/_gather-docs.d.mts +20 -0
  88. package/api/search/_gather-docs.mjs +254 -0
  89. package/api/search/_gather-hooks.d.mts +12 -0
  90. package/api/search/_gather-hooks.mjs +52 -0
  91. package/api/search/_gather-templates.d.mts +25 -0
  92. package/api/search/_gather-templates.mjs +107 -0
  93. package/api/search/_lexical.d.mts +135 -0
  94. package/api/search/_lexical.mjs +351 -0
  95. package/api/search/_main-searcher.d.mts +19 -0
  96. package/api/search/_main-searcher.mjs +115 -0
  97. package/api/search/_main-searcher.test.mjs +106 -0
  98. package/api/search/_main-searcher.weights.json +1 -0
  99. package/api/search/_module-doc.d.mts +32 -0
  100. package/api/search/_module-doc.mjs +78 -0
  101. package/api/search/_order.d.mts +32 -0
  102. package/api/search/_order.mjs +162 -0
  103. package/api/search/_order.test.mjs +72 -0
  104. package/api/search/_priority.d.mts +50 -0
  105. package/api/search/_priority.mjs +139 -0
  106. package/api/search/_query-words.d.mts +25 -0
  107. package/api/search/_query-words.mjs +92 -0
  108. package/api/search/_query.d.mts +65 -0
  109. package/api/search/_query.mjs +176 -0
  110. package/api/search/_result.d.mts +97 -0
  111. package/api/search/_result.mjs +150 -0
  112. package/api/search/_score.d.mts +60 -0
  113. package/api/search/_score.mjs +283 -0
  114. package/api/search/_synonyms.d.mts +21 -0
  115. package/api/search/_synonyms.mjs +67 -0
  116. package/api/search/_template-searcher.d.mts +4 -0
  117. package/api/search/_template-searcher.mjs +23 -0
  118. package/api/search/_template-searcher.test.mjs +40 -0
  119. package/api/search/_template-searcher.weights.json +1 -0
  120. package/api/search/_titles.d.mts +33 -0
  121. package/api/search/_titles.mjs +65 -0
  122. package/api/search/_words.d.mts +64 -0
  123. package/api/search/_words.mjs +157 -0
  124. package/api/search/search.d.mts +22 -305
  125. package/api/search/search.doc.mjs +8 -6
  126. package/api/search/search.mjs +56 -2482
  127. package/api/search/search.test.mjs +14 -14
  128. package/api/search/search.type.d.mts +6 -1
  129. package/api/search/search.type.mjs +6 -1
  130. package/api/search/tree.d.mts +15 -0
  131. package/api/search/tree.mjs +40 -0
  132. package/api/search/tree.test.mjs +66 -0
  133. package/api/search/tree.weights.json +1 -0
  134. package/api/theme/build/build.packed-old-core.test.mjs +1 -0
  135. package/api/upgrade/_adapter.d.mts +57 -9
  136. package/api/upgrade/_adapter.mjs +198 -28
  137. package/api/upgrade/registry/registry.d.mts +10 -4
  138. package/api/upgrade/registry/registry.mjs +33 -16
  139. package/api/upgrade/registry/registry.test.mjs +85 -0
  140. package/api/upgrade/run/run.d.mts +4 -2
  141. package/api/upgrade/run/run.mjs +13 -2
  142. package/api/upgrade/run/run.test.mjs +417 -3
  143. package/api/upgrade/upgrade.doc.mjs +10 -5
  144. package/api/upgrade/upgrade.mjs +13 -2
  145. package/api/upgrade/upgrade.test.mjs +194 -0
  146. package/api/upgrade/upgrade.type.d.mts +5 -3
  147. package/api/upgrade/upgrade.type.mjs +8 -6
  148. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  149. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  150. package/assets/docs/author-a-theme.doc.mjs +2 -2
  151. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  152. package/assets/docs/tree/add-a-component.doc.mjs +1 -1
  153. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  154. package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
  155. package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
  156. package/assets/docs/tree/api.doc.mjs +6 -0
  157. package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
  158. package/assets/docs/tree/block-template.doc.mjs +1 -1
  159. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  160. package/assets/docs/tree/building-blocks.doc.mjs +2 -2
  161. package/assets/docs/tree/check-the-props.doc.mjs +1 -1
  162. package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
  163. package/assets/docs/tree/checks.doc.mjs +2 -2
  164. package/assets/docs/tree/cli-command.doc.mjs +1 -1
  165. package/assets/docs/tree/cli.doc.mjs +1 -1
  166. package/assets/docs/tree/codemods.doc.mjs +1 -1
  167. package/assets/docs/tree/component-family.doc.mjs +1 -1
  168. package/assets/docs/tree/component-imports.doc.mjs +1 -1
  169. package/assets/docs/tree/components.doc.mjs +1 -1
  170. package/assets/docs/tree/configuration.doc.mjs +1 -1
  171. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
  172. package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
  173. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  174. package/assets/docs/tree/docs.doc.mjs +1 -1
  175. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  176. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  177. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  178. package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
  179. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  180. package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
  181. package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
  182. package/assets/docs/tree/global-integrations.doc.mjs +2 -2
  183. package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
  184. package/assets/docs/tree/help.doc.mjs +2 -2
  185. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  186. package/assets/docs/tree/links.doc.mjs +1 -1
  187. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  188. package/assets/docs/tree/page-template.doc.mjs +1 -1
  189. package/assets/docs/tree/publishing.doc.mjs +1 -1
  190. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  191. package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
  192. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  193. package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
  194. package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
  195. package/assets/docs/tree/ship.doc.mjs +2 -2
  196. package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
  197. package/assets/docs/tree/single-component.doc.mjs +1 -1
  198. package/assets/docs/tree/start-a-template.doc.mjs +1 -1
  199. package/assets/docs/tree/subcomponent.doc.mjs +1 -1
  200. package/assets/docs/tree/template-assets.doc.mjs +1 -1
  201. package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
  202. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  203. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  204. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  205. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  206. package/assets/docs/tree/template-styles.doc.mjs +1 -1
  207. package/assets/docs/tree/templates.doc.mjs +1 -1
  208. package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
  209. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  210. package/assets/docs/tree/themes.doc.mjs +1 -1
  211. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  212. package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
  213. package/assets/docs/tree/upgrading.doc.mjs +1 -1
  214. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
  215. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  216. package/assets/docs/tree/versioning.doc.mjs +1 -1
  217. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  218. package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
  219. package/assets/docs/use-integrations.doc.mjs +120 -0
  220. package/assets/docs/working-with-ai.doc.mjs +6 -2
  221. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  222. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  223. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  224. package/authoring/debug/parse.d.mts +1 -1
  225. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  226. package/authoring/integration/integration.doc.mjs +1 -1
  227. package/authoring/integration/type.ts +1 -1
  228. package/clients/cli/bin/astryx.mjs +7 -78
  229. package/clients/cli/command-result-coverage.test.mjs +6 -5
  230. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  231. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  232. package/clients/cli/commands/component.test.mjs +6 -6
  233. package/clients/cli/commands/docs.doc.mjs +2 -2
  234. package/clients/cli/commands/docs.mjs +1 -1
  235. package/clients/cli/commands/docs.test.mjs +14 -14
  236. package/clients/cli/commands/doctor.mjs +30 -0
  237. package/clients/cli/commands/doctor.test.mjs +65 -0
  238. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  239. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  240. package/clients/cli/commands/init.behavior.test.mjs +230 -1
  241. package/clients/cli/commands/init.doc.mjs +28 -9
  242. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  243. package/clients/cli/commands/init.mjs +2 -2
  244. package/clients/cli/commands/integration-check.test.mjs +48 -0
  245. package/clients/cli/commands/integration.doc.mjs +1 -1
  246. package/clients/cli/commands/manifest.doc.mjs +1 -1
  247. package/clients/cli/commands/manifest.mjs +67 -0
  248. package/clients/cli/commands/outside-project.test.mjs +2 -1
  249. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  250. package/clients/cli/commands/postinstall.mjs +31 -0
  251. package/clients/cli/commands/setup-nudge.test.mjs +61 -0
  252. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
  253. package/clients/cli/commands/upgrade.doc.mjs +4 -2
  254. package/clients/cli/commands/upgrade.mjs +9 -2
  255. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  256. package/clients/cli/index.mjs +29 -86
  257. package/clients/cli/lib/component-format.mjs +1 -1
  258. package/clients/cli/lib/define-command.mjs +12 -7
  259. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  260. package/clients/cli/lib/manifest.test.mjs +12 -0
  261. package/clients/cli/lib/welcome-box.mjs +50 -0
  262. package/clients/cli/run.mjs +108 -0
  263. package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
  264. package/foundation/agent-docs/agent-doc-state.mjs +35 -8
  265. package/foundation/agent-docs/agent-docs.d.mts +100 -12
  266. package/foundation/agent-docs/agent-docs.mjs +1012 -100
  267. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  268. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  269. package/foundation/config/project.mjs +20 -22
  270. package/foundation/config/project.test.mjs +56 -0
  271. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  272. package/foundation/discovery/authoring-self-docs.mjs +16 -4
  273. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  274. package/foundation/discovery/authoring-surface.d.mts +1 -1
  275. package/foundation/discovery/authoring-surface.mjs +2 -2
  276. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  277. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  278. package/foundation/discovery/cli-self-docs.mjs +4 -3
  279. package/foundation/discovery/cli-self-docs.test.mjs +2 -2
  280. package/foundation/discovery/component-discovery.d.mts +37 -0
  281. package/foundation/discovery/component-discovery.mjs +80 -5
  282. package/foundation/discovery/component-loader.d.mts +1 -1
  283. package/foundation/discovery/docs-discovery.mjs +14 -1
  284. package/foundation/discovery/module-exports.d.mts +41 -0
  285. package/foundation/discovery/module-exports.mjs +253 -0
  286. package/foundation/discovery/runner-allowlist.d.mts +2 -4
  287. package/foundation/discovery/runner-allowlist.mjs +2 -4
  288. package/foundation/discovery/runner-commands.d.mts +77 -0
  289. package/foundation/discovery/runner-commands.mjs +294 -0
  290. package/foundation/discovery/runner-commands.test.mjs +128 -0
  291. package/foundation/discovery/stray-pointers.test.mjs +1 -1
  292. package/foundation/discovery/template-adapter.mjs +18 -1
  293. package/foundation/discovery/template-needs.mjs +1 -44
  294. package/foundation/discovery/theme-discovery.mjs +147 -3
  295. package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
  296. package/foundation/doc-compiler/read.mjs +59 -1
  297. package/foundation/doc-compiler/read.test.mjs +11 -2
  298. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  299. package/foundation/doc-compiler/tree.d.mts +32 -6
  300. package/foundation/doc-compiler/tree.mjs +77 -43
  301. package/foundation/doc-compiler/tree.test.mjs +7 -7
  302. package/foundation/fs/paths.d.mts +70 -8
  303. package/foundation/fs/paths.mjs +556 -38
  304. package/foundation/fs/paths.test.mjs +430 -54
  305. package/foundation/fs/read-record.d.mts +18 -0
  306. package/foundation/fs/read-record.mjs +131 -0
  307. package/foundation/fs/read-record.test.mjs +77 -0
  308. package/foundation/integrations/autolink.d.mts +2 -1
  309. package/foundation/integrations/autolink.mjs +4 -13
  310. package/foundation/integrations/computer-integrations.d.mts +6 -3
  311. package/foundation/integrations/computer-integrations.mjs +38 -9
  312. package/foundation/integrations/contribution-inventory.d.mts +15 -20
  313. package/foundation/integrations/contribution-inventory.mjs +41 -5
  314. package/foundation/integrations/integrations.d.mts +27 -1
  315. package/foundation/integrations/integrations.mjs +61 -2
  316. package/foundation/integrations/validate-contributions.d.mts +115 -0
  317. package/foundation/integrations/validate-contributions.mjs +1168 -13
  318. package/foundation/release/handoff.d.mts +23 -4
  319. package/foundation/release/handoff.mjs +175 -8
  320. package/foundation/release/run-context.mjs +14 -0
  321. package/foundation/response/response-types.doc.mjs +1 -1
  322. package/foundation/route/route.d.mts +285 -0
  323. package/foundation/route/route.mjs +324 -0
  324. package/foundation/route/route.test.mjs +153 -0
  325. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  326. package/foundation/runtime/locked-versions.d.mts +32 -0
  327. package/foundation/runtime/locked-versions.mjs +65 -0
  328. package/foundation/runtime/package-root.d.mts +91 -0
  329. package/foundation/runtime/package-root.mjs +174 -0
  330. package/foundation/runtime/package-set.d.mts +62 -0
  331. package/foundation/runtime/package-set.mjs +130 -0
  332. package/foundation/runtime/package-set.test.mjs +432 -0
  333. package/host/door-conformance.test.mjs +274 -0
  334. package/host/index.d.ts +116 -0
  335. package/host/index.mjs +295 -0
  336. package/host/index.test.mjs +302 -0
  337. package/package.json +14 -9
@@ -26,13 +26,17 @@ import {projectRootOf} from '../../foundation/release/choose.mjs';
26
26
  import {readReleaseFacts} from '../../foundation/release/project.mjs';
27
27
  import {buildManifest} from './lib/manifest.mjs';
28
28
  import {cliError} from './lib/cli-error.mjs';
29
- import {emit, section, text, records} from './formatters/index.mjs';
29
+ import {section, text, records} from './formatters/index.mjs';
30
30
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
31
31
  import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
32
32
  import {installJsonShim} from './lib/json-shim.mjs';
33
- import {addDocHelp, markReportsResult} from './lib/define-command.mjs';
34
- import {doc as manifestDoc} from './commands/manifest.doc.mjs';
35
- import {isAstryxInitialized, isAstryxPromptInReach} from '../../foundation/agent-docs/agent-docs.mjs';
33
+ import {markReportsResult} from './lib/define-command.mjs';
34
+ import {
35
+ agentDocsTargetDir,
36
+ assertAgentDocsTarget,
37
+ isAstryxInitialized,
38
+ isAstryxPromptInReach,
39
+ } from '../../foundation/agent-docs/agent-docs.mjs';
36
40
  import {CLI_ROOT} from '../../foundation/fs/paths.mjs';
37
41
  import {setupNudge} from '../../foundation/agent-docs/agent-doc-state.mjs';
38
42
  import * as debug from '../../foundation/debug/index.mjs';
@@ -292,7 +296,7 @@ function captureInvocation(actionCommand, root) {
292
296
  */
293
297
  const commands = [
294
298
  {name: 'init', path: './commands/init.mjs', register: 'registerInit'},
295
- {name: 'component', path: './commands/component/index.mjs', register: 'registerComponent'},
299
+ {name: 'component', path: './commands/component.mjs', register: 'registerComponent'},
296
300
  {name: 'docs', path: './commands/docs.mjs', register: 'registerDocs'},
297
301
  {name: 'blog', path: './commands/blog.mjs', register: 'registerBlog'},
298
302
  {name: 'swizzle', path: './commands/swizzle.mjs', register: 'registerSwizzle'},
@@ -300,13 +304,17 @@ const commands = [
300
304
  // agent-docs folded into init — functions still importable from agent-docs.mjs
301
305
  {name: 'template', path: './commands/template.mjs', register: 'registerTemplate'},
302
306
  {name: 'upgrade', path: './commands/upgrade.mjs', register: 'registerUpgrade'},
303
- {name: 'theme', path: './commands/build-theme.mjs', register: 'registerTheme'},
307
+ {name: 'theme', path: './commands/theme.mjs', register: 'registerTheme'},
304
308
  {name: 'integration', path: './commands/integration.mjs', register: 'registerIntegration'},
305
- {name: 'hook', path: './commands/hook/index.mjs', register: 'registerHook'},
309
+ {name: 'hook', path: './commands/hook.mjs', register: 'registerHook'},
306
310
  {name: 'discover', path: './commands/discover.mjs', register: 'registerDiscover'},
307
311
  {name: 'search', path: './commands/search.mjs', register: 'registerSearch'},
308
312
  {name: 'build', path: './commands/build.mjs', register: 'registerBuild'},
309
313
  {name: 'doctor', path: './commands/doctor.mjs', register: 'registerDoctor'},
314
+ // Registered after every other command: the manifest lists the commands in
315
+ // registration order.
316
+ {name: 'manifest', path: './commands/manifest.mjs', register: 'registerManifest'},
317
+ {name: 'postinstall', path: './commands/postinstall.mjs', register: 'registerPostinstall'},
310
318
  ];
311
319
 
312
320
  const SETUP_NUDGE_EXEMPT = new Set(['init', 'agent-docs']);
@@ -635,6 +643,19 @@ export async function createProgram() {
635
643
  if (INTEGRATION_MANIFEST_FILES.some(file => fs.existsSync(path.join(cwd, file)))) return;
636
644
  if (inOwnSourceCheckout(cwd)) return; // developing Astryx itself, not an app
637
645
  if (isAstryxPromptInReach(cwd)) return; // already set up — stay quiet
646
+ // Where init writes the docs, by init's own rule: the project root, which
647
+ // can sit above a nested package such as functions/. Inside a git
648
+ // repository the walk above already reaches it (the root never lies
649
+ // above the repository root), so this changes nothing there; outside a
650
+ // repository the walk checks only cwd, and this finds the root.
651
+ if (isAstryxInitialized(agentDocsTargetDir(cwd))) return;
652
+ // In the home folder, or at the top of a settings folder in it, init
653
+ // writes no agent docs, so naming it would repeat on every command.
654
+ try {
655
+ assertAgentDocsTarget(agentDocsTargetDir(cwd));
656
+ } catch {
657
+ return;
658
+ }
638
659
  // Same wording as the core/cli postinstall nudges. getCliInvocation()
639
660
  // renders the correct form for THIS project — an integration's cliCommand,
640
661
  // scoped `npx @astryxdesign/cli` one-off, or `<pm> astryx` when installed —
@@ -659,7 +680,7 @@ export async function createProgram() {
659
680
  for (const cmd of commands) {
660
681
  try {
661
682
  const mod = await import(cmd.path);
662
- mod[cmd.register](program);
683
+ mod[cmd.register](program, {jsonSupported: JSON_SUPPORTED, version: pkg.version});
663
684
  } catch (e) {
664
685
  // Command fails to load but CLI still works
665
686
  const reason = /** @type {any} */ (e).message;
@@ -679,84 +700,6 @@ export async function createProgram() {
679
700
  }
680
701
  }
681
702
 
682
- // Capability manifest — a single, self-describing view of the whole CLI so
683
- // agents can discover every command, argument, flag, and response type without
684
- // scraping `--help`. `astryx manifest --json` is the dedicated surface; the bare
685
- // `xds --json` embeds the same payload under data.manifest for convenience.
686
- // Intentionally CLI-special — no `api/manifest`. It introspects the live
687
- // Commander `program`, so extracting it to `api/` would create the `api → cli`
688
- // cycle from #4302. `buildManifest(program)` lives in lib/; see its header.
689
- const manifestCommand = program
690
- .command('manifest')
691
- .description('Print the full CLI capability manifest (use with --json)')
692
- .action(() => {
693
- const manifest = buildManifest(program, {
694
- jsonSupported: JSON_SUPPORTED,
695
- version: pkg.version,
696
- });
697
- // The manifest IS a result set: one entry per command the CLI ships.
698
- debug.recordCommandResult(
699
- debug.resultSet({
700
- count: manifest.commands.length,
701
- resultKind: 'command',
702
- }),
703
- );
704
- if (program.opts().json) {
705
- process.__xdsJsonHandled = true;
706
- console.log(JSON.stringify({apiVersion: API_VERSION, type: 'manifest', data: manifest, meta: {answeredBy: answeredBy()}}, null, 2));
707
- return;
708
- }
709
- // Human-readable summary as greppable records (agents should use --json).
710
- // One record per command: name, whether it supports --json, and the
711
- // description. Field names are the manifest entry's own keys.
712
- emit(
713
- section(`${manifest.name} v${manifest.version} (${manifest.commands.length} commands)`),
714
- records(
715
- manifest.commands.map(c => ({
716
- name: c.name,
717
- json: c.json ? 'yes' : '',
718
- description: c.description || '',
719
- })),
720
- {fields: ['name', 'json', 'description']},
721
- ),
722
- text(`Run \`${getCliInvocation()} manifest --json\` for the full structured manifest.`),
723
- );
724
- });
725
- addDocHelp(manifestCommand, manifestDoc);
726
- markReportsResult(manifestCommand);
727
-
728
- // Hidden command used by package.json postinstall scripts
729
- const postinstallCommand = program
730
- .command('postinstall', {hidden: true})
731
- .action(() => {
732
- // Prints the welcome box. Nothing is looked up.
733
- debug.recordCommandResult(debug.NO_RESULT_SET);
734
- const r = getCliInvocation();
735
- const pad = (/** @type {string} */ s, /** @type {number} */ len) => s + ' '.repeat(Math.max(0, len - s.length));
736
- const W = 49; // inner width of the box
737
- const line = (/** @type {string} */ s) => ` │ ${pad(s, W)}│`;
738
- console.log(`
739
- ╭${'─'.repeat(W + 2)}╮
740
- ${line('')}
741
- ${line(' Design system installed!')}
742
- ${line('')}
743
- ${line(' Get started:')}
744
- ${line(` ${r} init Setup + AI agent docs`)}
745
- ${line(` ${r} --help See all commands`)}
746
- ${line('')}
747
- ${line(' Or run directly:')}
748
- ${line(` ${r} init Setup + AI agent docs`)}
749
- ${line(` ${r} component Browse component docs`)}
750
- ${line(` ${r} hook Browse hook docs`)}
751
- ${line(` ${r} docs Design system reference`)}
752
- ${line(` ${r} swizzle Customize a component`)}
753
- ${line(` ${r} template Add a page template`)}
754
- ${line('')}
755
- ╰${'─'.repeat(W + 2)}╯
756
- `);
757
- });
758
- markReportsResult(postinstallCommand);
759
-
760
703
  // Install the JSON shim AFTER all commands are registered so we can
761
704
  // patch outputHelp on every command (root + subcommands). The shim
762
705
  // extends the --json contract to cover Commander's parse-time short
@@ -21,7 +21,7 @@ import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
21
21
  * (→ `.astryx-table-cell`), and `astryx-button` → key `button`.
22
22
  *
23
23
  * Keep the `astryx-` literal in sync with packages/core/src/naming.ts
24
- * (NAMESPACE / classPrefix), the same way build-theme.mjs mirrors it.
24
+ * (NAMESPACE / classPrefix), the same way commands/theme.mjs mirrors it.
25
25
  * <!-- SYNC: packages/core/src/naming.ts (namespace prefix source of truth) -->
26
26
  * @param {import('@astryxdesign/cli/authoring').ComponentThemingTarget} target
27
27
  * @returns {string}
@@ -63,10 +63,10 @@ export function reportsResultVia(cmd) {
63
63
  /**
64
64
  * Mark a hand-registered command as reporting its result.
65
65
  *
66
- * For the handful of commands that cannot come from a CommandDoc — the root
67
- * program, `manifest` (it introspects the live program, so it cannot be
68
- * declared by one), `postinstall`, and the stub left behind when a command
69
- * module fails to load. They call {@link recordCommandResult} themselves; this
66
+ * For what does not come from a CommandDoc: the root program, and the stub
67
+ * left behind when a command module fails to load. Every command, `manifest`
68
+ * and the hidden `postinstall` hook included, registers through
69
+ * {@link defineCommand}. These call {@link recordCommandResult} themselves; this
70
70
  * says so, and the coverage test pins the set of commands allowed to, so the
71
71
  * mark cannot become a quiet way around the contract.
72
72
  *
@@ -105,10 +105,12 @@ export function commandDocsOf(cmd) {
105
105
  * fn?: import('@astryxdesign/cli/authoring').FunctionDoc,
106
106
  * action?: (...args: any[]) => import('../../../foundation/debug/command-result.mjs').CommandResult
107
107
  * | Promise<import('../../../foundation/debug/command-result.mjs').CommandResult>,
108
- * }} [impl]
108
+ * hidden?: boolean,
109
+ * }} [impl] `hidden` keeps the command out of help and the manifest (a
110
+ * package-script hook, not a command for people or agents).
109
111
  * @returns {import('commander').Command} the created command.
110
112
  */
111
- export function defineCommand(parent, doc, {fn, action} = {}) {
113
+ export function defineCommand(parent, doc, {fn, action, hidden} = {}) {
112
114
  // The command token is the last path segment ("theme build" -> "build"),
113
115
  // since subcommands are added to their group command, not the program.
114
116
  const token = doc.name.split(' ').pop() ?? doc.name;
@@ -119,7 +121,10 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
119
121
  })
120
122
  .join(' ');
121
123
 
122
- const cmd = parent.command(argSpec ? `${token} ${argSpec}` : token);
124
+ const cmd = parent.command(
125
+ argSpec ? `${token} ${argSpec}` : token,
126
+ hidden ? {hidden: true} : {},
127
+ );
123
128
  Object.defineProperty(cmd, COMMAND_DOCS, {value: {doc, fn}, configurable: true});
124
129
  if (doc.summary) cmd.description(doc.summary);
125
130
  if (doc.deprecated) {
@@ -24,6 +24,10 @@ for (const file of fs.readdirSync(COMMANDS).sort()) {
24
24
  }
25
25
 
26
26
  const manifest = buildManifest(program, {jsonSupported: JSON_SUPPORTED, version: '0.0.0-test'});
27
+ /** Hidden commands (a package-script hook): documented, never in the manifest. */
28
+ const HIDDEN = new Set(
29
+ program.commands.filter((c) => /** @type {any} */ (c)._hidden).map((c) => c.name()),
30
+ );
27
31
  /** @type {Map<string, any>} */
28
32
  const entries = new Map();
29
33
  const walk = (/** @type {any} */ c) => {
@@ -98,6 +102,7 @@ describe('command exit codes', () => {
98
102
 
99
103
  it('the manifest carries every command exit code', () => {
100
104
  for (const doc of commandDocs) {
105
+ if (HIDDEN.has(doc.name)) continue;
101
106
  expect(entries.get(doc.name)?.exitCodes, doc.name).toEqual(
102
107
  doc.exitCodes.map(({code, when}) => ({code, when})),
103
108
  );
@@ -55,6 +55,10 @@ const CLI_LAYER_TYPES = {
55
55
  };
56
56
 
57
57
  const manifest = buildManifest(program, {jsonSupported: JSON_SUPPORTED, version: '0.0.0-test'});
58
+ /** Hidden commands (a package-script hook): documented, never in the manifest. */
59
+ const HIDDEN = new Set(
60
+ program.commands.filter((c) => /** @type {any} */ (c)._hidden).map((c) => c.name()),
61
+ );
58
62
 
59
63
  /** Flatten the manifest command tree into fully-qualified names. */
60
64
  function flatten(cmds, out = []) {
@@ -116,12 +120,19 @@ describe('manifest: drift guards', () => {
116
120
 
117
121
  it('takes every command example from its CommandDoc', () => {
118
122
  for (const [name, doc] of commandDocs) {
123
+ if (HIDDEN.has(name)) continue;
119
124
  const entry = allEntries.find((c) => c.name === name);
120
125
  expect(entry, `CommandDoc "${name}" has no manifest entry`).toBeDefined();
121
126
  expect(entry.examples ?? [], name).toEqual((doc.examples ?? []).map((e) => e.cli));
122
127
  }
123
128
  });
124
129
 
130
+ it('documents the hidden postinstall hook and keeps it out of the manifest', () => {
131
+ expect([...HIDDEN]).toEqual(['postinstall']);
132
+ expect(commandDocs.has('postinstall')).toBe(true);
133
+ expect(allNames.has('postinstall')).toBe(false);
134
+ });
135
+
125
136
  it('lists every response type the wrapped API function returns', () => {
126
137
  for (const [name, doc] of commandDocs) {
127
138
  if (!doc.fn) continue;
@@ -152,6 +163,7 @@ describe('manifest: drift guards', () => {
152
163
 
153
164
  it('takes response types from the wrapped FunctionDoc, plus CLI-layer envelopes', () => {
154
165
  for (const [name, doc] of commandDocs) {
166
+ if (HIDDEN.has(name)) continue;
155
167
  const entry = allEntries.find((c) => c.name === name);
156
168
  const returns = doc.fn ? functionDocs.get(doc.fn).returns.map((r) => r.type) : [];
157
169
  const expected = [...returns, ...(CLI_LAYER_TYPES[name] ?? [])];
@@ -0,0 +1,50 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The welcome box `astryx postinstall` prints after the package installs.
5
+ *
6
+ * A fixed banner, not tabular output, so it is drawn here rather than with the
7
+ * formatter kit; the command prints it verbatim through `emit(code(...))`.
8
+ * @input The CLI invocation for this project (e.g. `pnpm exec astryx`).
9
+ * @output {@link welcomeBox}
10
+ * @position clients/cli/lib — presentation helper for commands/postinstall.mjs
11
+ */
12
+
13
+ /** Inner width of the box. */
14
+ const W = 49;
15
+
16
+ /**
17
+ * @param {string} s
18
+ * @param {number} len
19
+ */
20
+ const pad = (s, len) => s + ' '.repeat(Math.max(0, len - s.length));
21
+
22
+ /** @param {string} s */
23
+ const line = s => ` │ ${pad(s, W)}│`;
24
+
25
+ /**
26
+ * The welcome box, with a blank line before and after it.
27
+ * @param {string} r the CLI invocation, e.g. `pnpm exec astryx`
28
+ * @returns {string}
29
+ */
30
+ export function welcomeBox(r) {
31
+ return `
32
+ ╭${'─'.repeat(W + 2)}╮
33
+ ${line('')}
34
+ ${line(' Design system installed!')}
35
+ ${line('')}
36
+ ${line(' Get started:')}
37
+ ${line(` ${r} init Setup + AI agent docs`)}
38
+ ${line(` ${r} --help See all commands`)}
39
+ ${line('')}
40
+ ${line(' Or run directly:')}
41
+ ${line(` ${r} init Setup + AI agent docs`)}
42
+ ${line(` ${r} component Browse component docs`)}
43
+ ${line(` ${r} hook Browse hook docs`)}
44
+ ${line(` ${r} docs Design system reference`)}
45
+ ${line(` ${r} swizzle Customize a component`)}
46
+ ${line(` ${r} template Add a page template`)}
47
+ ${line('')}
48
+ ╰${'─'.repeat(W + 2)}╯
49
+ `;
50
+ }
@@ -0,0 +1,108 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file One run of the CLI, after the Node version gate: choose who answers
5
+ * the folder, hand the run off when that is another release (spec AST-081
6
+ * FR2, FR9), then dispatch the command.
7
+ *
8
+ * @input The arguments after `astryx`, read from `process.argv`; the CLI
9
+ * package root; and, for a run a programmatic host started, the reason the
10
+ * host declined the project's own CLI.
11
+ * @output The command's effects. The run owns the process: its exit status is
12
+ * `process.exitCode`, unless a hand-off or a failure exits the process.
13
+ * @position clients/cli — called by `bin/astryx.mjs` and by the host entry
14
+ * (`host/index.mjs`).
15
+ */
16
+
17
+ /**
18
+ * Run the CLI once in this process.
19
+ *
20
+ * Every import here is dynamic: modules that load `styleText` must load only
21
+ * after the bin's Node version gate, and the release choice must run before
22
+ * the command modules load, so a run handed to another release loads none of
23
+ * them.
24
+ *
25
+ * @param {{cliRoot: string, argv: string[], declinedProjectCli?: string}} input
26
+ * @returns {Promise<import('../../host/index.js').HostedCliResult | undefined>}
27
+ * resolves when the command has finished; with another release's result
28
+ * when, in a hosted run, that release's host entry answered
29
+ */
30
+ export async function runCli({cliRoot, argv, declinedProjectCli}) {
31
+ const {startRun} = await import('../../foundation/release/handoff.mjs');
32
+ const answered = await startRun({cliRoot, argv, declinedProjectCli});
33
+ if (answered) return answered;
34
+
35
+ const {
36
+ program,
37
+ loadProjectDebugHandler,
38
+ loadProjectForHelp,
39
+ handleVersionJsonPreflight,
40
+ } = await import('./index.mjs');
41
+ const {isJsonMode, toErrorEnvelope} =
42
+ await import('../../foundation/response/json.mjs');
43
+ const {handleCommanderError} = await import('./lib/json-shim.mjs');
44
+ const {setOutcome} = await import('../../foundation/debug/index.mjs');
45
+
46
+ /**
47
+ * Top-level error boundary (contract guarantee #4): an uncaught throw must
48
+ * never reach a --json consumer as a raw stack trace. If we're in JSON mode,
49
+ * convert it to the contract's error envelope on stdout and exit 1;
50
+ * otherwise preserve the normal human-facing behavior.
51
+ *
52
+ * We detect JSON mode two ways: the global flag (set once root options are
53
+ * parsed) and a raw argv check, since a throw can occur during
54
+ * parsing/command load — before the preAction hook engages the flag.
55
+ */
56
+ function inJsonMode() {
57
+ return isJsonMode() || process.argv.slice(2).includes('--json');
58
+ }
59
+
60
+ /** @param {unknown} err */
61
+ function handleFatal(err) {
62
+ // CommanderError (parse errors, --help, unknown command) routes
63
+ // through the JSON shim so that a --json consumer always gets a
64
+ // valid envelope and the right exit code. handleCommanderError
65
+ // calls process.exit when it owns the error.
66
+ if (handleCommanderError(err)) return;
67
+
68
+ // Anything still here is an uncaught throw or an unhandled rejection —
69
+ // a bug rather than a user error, and the class of failure most worth
70
+ // having a record of.
71
+ setOutcome('fatal', {exitCode: 1, error: err});
72
+
73
+ if (inJsonMode()) {
74
+ // Only emit if a command didn't already produce an envelope.
75
+ if (!process.__xdsJsonHandled) {
76
+ process.__xdsJsonHandled = true;
77
+ console.log(JSON.stringify(toErrorEnvelope(err), null, 2));
78
+ }
79
+ process.exit(1);
80
+ }
81
+ // Human mode: preserve Commander/Node default-ish behavior.
82
+ console.error(
83
+ err instanceof Error ? err.stack || err.message : String(err),
84
+ );
85
+ process.exit(1);
86
+ }
87
+
88
+ process.on('unhandledRejection', handleFatal);
89
+ process.on('uncaughtException', handleFatal);
90
+
91
+ // Pick up the project's `debug` function before parsing, so parse errors
92
+ // and `--help` — which short-circuit before any hook runs — are reported
93
+ // too.
94
+ await loadProjectDebugHandler();
95
+ // Help prints its commands while Commander parses, so a help run reads the
96
+ // project's declared `cliCommand` here.
97
+ await loadProjectForHelp();
98
+
99
+ // `--version --json` answers and exits without Commander, so it runs here —
100
+ // after the handler exists, or the run would go unrecorded.
101
+ handleVersionJsonPreflight();
102
+
103
+ try {
104
+ await program.parseAsync(process.argv);
105
+ } catch (err) {
106
+ handleFatal(err);
107
+ }
108
+ }
@@ -4,15 +4,25 @@
4
4
  /**
5
5
  * Find all existing agent doc files in a directory, across EVERY location any
6
6
  * preset can write (see {@link AGENT_DOC_PATHS}: AGENTS.md, CLAUDE.md,
7
- * .claude/CLAUDE.md, .cursorrules, .hermes.md, HERMES.md).
7
+ * .claude/CLAUDE.md, GEMINI.md, .cursorrules, .hermes.md, HERMES.md).
8
8
  * @param {string} targetDir
9
9
  * @returns {string[]} Relative paths of existing agent doc files
10
10
  */
11
11
  export function discoverAgentDocs(targetDir: string): string[];
12
+ /**
13
+ * Whether `content` holds an Astryx block. A managed block that only imports
14
+ * another file (the one-line `@./AGENTS.md` that init writes to GEMINI.md) does
15
+ * not count: it holds nothing once that file has lost its block. A malformed
16
+ * block still counts, so a half-written file reads as set up.
17
+ *
18
+ * @param {string} content
19
+ * @returns {boolean}
20
+ */
21
+ export function holdsAstryxBlock(content: string): boolean;
12
22
  /**
13
23
  * Single source of truth for "is Astryx set up in this project?" — true when any
14
- * agent-doc file already carries the Astryx marker, i.e. `init` / `agent-docs`
15
- * has run. Reused by the init & upgrade commands, the per-command setup nudge
24
+ * agent-doc file already holds an Astryx block, i.e. `init` / `agent-docs` has
25
+ * run. Reused by the init & upgrade commands, the per-command setup nudge
16
26
  * (enforcement layer 3), and the cli postinstall nudge (layer 2). Core's
17
27
  * postinstall (separate package, layer 1) mirrors the same marker contract.
18
28
  *
@@ -68,6 +78,7 @@ export function shouldNudge({ scriptPath, npmCommand, isSetUp }?: {
68
78
  export const AGENTS_MD: "AGENTS.md";
69
79
  export const CLAUDE_MD: "CLAUDE.md";
70
80
  export const CLAUDE_DIR_MD: ".claude/CLAUDE.md";
81
+ export const GEMINI_MD: "GEMINI.md";
71
82
  export const CURSOR_RULES: ".cursorrules";
72
83
  export const HERMES_DOT_MD: ".hermes.md";
73
84
  export const HERMES_MD: "HERMES.md";
@@ -77,10 +88,12 @@ export const LEGACY_MARKER_START: "<!-- XDS:START -->";
77
88
  export const LEGACY_MARKER_END: "<!-- XDS:END -->";
78
89
  /**
79
90
  * The canonical set of EVERY location an --agent preset (or the default) can
80
- * write the Astryx block. SINGLE SOURCE OF TRUTH: discovery, removal, and the
81
- * `isAstryxInitialized` predicate all derive from this list, so "where init
82
- * writes" and "where we look" can never drift. (Explicit --agent-docs-path
83
- * targets are user-chosen and not enumerable here.)
91
+ * write a managed Astryx block — the full block, or the one-line import of
92
+ * AGENTS.md that the default writes to GEMINI.md. SINGLE
93
+ * SOURCE OF TRUTH: discovery, removal, and the `isAstryxInitialized` predicate
94
+ * all derive from this list, so "where init writes" and "where we look" can
95
+ * never drift. (Explicit --agent-docs-path targets are user-chosen and not
96
+ * enumerable here.)
84
97
  */
85
98
  export const AGENT_DOC_PATHS: string[];
86
99
  /**
@@ -29,6 +29,7 @@ import * as path from 'node:path';
29
29
  export const AGENTS_MD = 'AGENTS.md';
30
30
  export const CLAUDE_MD = 'CLAUDE.md';
31
31
  export const CLAUDE_DIR_MD = '.claude/CLAUDE.md'; // cross-platform literal
32
+ export const GEMINI_MD = 'GEMINI.md';
32
33
  export const CURSOR_RULES = '.cursorrules';
33
34
  export const HERMES_DOT_MD = '.hermes.md';
34
35
  export const HERMES_MD = 'HERMES.md';
@@ -41,15 +42,18 @@ export const LEGACY_MARKER_END = '<!-- XDS:END -->';
41
42
 
42
43
  /**
43
44
  * The canonical set of EVERY location an --agent preset (or the default) can
44
- * write the Astryx block. SINGLE SOURCE OF TRUTH: discovery, removal, and the
45
- * `isAstryxInitialized` predicate all derive from this list, so "where init
46
- * writes" and "where we look" can never drift. (Explicit --agent-docs-path
47
- * targets are user-chosen and not enumerable here.)
45
+ * write a managed Astryx block — the full block, or the one-line import of
46
+ * AGENTS.md that the default writes to GEMINI.md. SINGLE
47
+ * SOURCE OF TRUTH: discovery, removal, and the `isAstryxInitialized` predicate
48
+ * all derive from this list, so "where init writes" and "where we look" can
49
+ * never drift. (Explicit --agent-docs-path targets are user-chosen and not
50
+ * enumerable here.)
48
51
  */
49
52
  export const AGENT_DOC_PATHS = [
50
53
  AGENTS_MD, // Codex / ChatGPT / Muse / generic
51
54
  CLAUDE_MD, // Claude Code (root)
52
55
  CLAUDE_DIR_MD, // Claude Code (.claude/CLAUDE.md)
56
+ GEMINI_MD, // Gemini CLI
53
57
  CURSOR_RULES, // Cursor
54
58
  HERMES_DOT_MD, // Hermes
55
59
  HERMES_MD, // Hermes
@@ -64,7 +68,7 @@ export const INIT_MARKERS = [MARKER_START, LEGACY_MARKER_START];
64
68
  /**
65
69
  * Find all existing agent doc files in a directory, across EVERY location any
66
70
  * preset can write (see {@link AGENT_DOC_PATHS}: AGENTS.md, CLAUDE.md,
67
- * .claude/CLAUDE.md, .cursorrules, .hermes.md, HERMES.md).
71
+ * .claude/CLAUDE.md, GEMINI.md, .cursorrules, .hermes.md, HERMES.md).
68
72
  * @param {string} targetDir
69
73
  * @returns {string[]} Relative paths of existing agent doc files
70
74
  */
@@ -72,10 +76,33 @@ export function discoverAgentDocs(targetDir) {
72
76
  return AGENT_DOC_PATHS.filter(p => fs.existsSync(path.join(targetDir, p)));
73
77
  }
74
78
 
79
+ /**
80
+ * Whether `content` holds an Astryx block. A managed block that only imports
81
+ * another file (the one-line `@./AGENTS.md` that init writes to GEMINI.md) does
82
+ * not count: it holds nothing once that file has lost its block. A malformed
83
+ * block still counts, so a half-written file reads as set up.
84
+ *
85
+ * @param {string} content
86
+ * @returns {boolean}
87
+ */
88
+ export function holdsAstryxBlock(content) {
89
+ for (const marker of INIT_MARKERS) {
90
+ let at = content.indexOf(marker);
91
+ while (at !== -1) {
92
+ const from = at + marker.length;
93
+ const end = content.indexOf('<!--', from);
94
+ const inner = (end === -1 ? content.slice(from) : content.slice(from, end)).trim();
95
+ if (!/^@\S+$/.test(inner)) return true;
96
+ at = content.indexOf(marker, from);
97
+ }
98
+ }
99
+ return false;
100
+ }
101
+
75
102
  /**
76
103
  * Single source of truth for "is Astryx set up in this project?" — true when any
77
- * agent-doc file already carries the Astryx marker, i.e. `init` / `agent-docs`
78
- * has run. Reused by the init & upgrade commands, the per-command setup nudge
104
+ * agent-doc file already holds an Astryx block, i.e. `init` / `agent-docs` has
105
+ * run. Reused by the init & upgrade commands, the per-command setup nudge
79
106
  * (enforcement layer 3), and the cli postinstall nudge (layer 2). Core's
80
107
  * postinstall (separate package, layer 1) mirrors the same marker contract.
81
108
  *
@@ -86,7 +113,7 @@ export function isAstryxInitialized(targetDir = process.cwd()) {
86
113
  for (const rel of discoverAgentDocs(targetDir)) {
87
114
  try {
88
115
  const content = fs.readFileSync(path.join(targetDir, rel), 'utf-8');
89
- if (INIT_MARKERS.some(m => content.includes(m))) {
116
+ if (holdsAstryxBlock(content)) {
90
117
  return true;
91
118
  }
92
119
  } catch {