@astryxdesign/cli 0.7.2-canary.fe2c655 → 0.7.3-canary.02f5fe4

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 (694) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +216 -162
  3. package/api/build/_adapter.d.mts +66 -0
  4. package/api/build/_adapter.mjs +139 -4
  5. package/api/build/blocks/blocks.d.mts +92 -0
  6. package/api/build/blocks/blocks.mjs +528 -0
  7. package/api/build/blocks/blocks.test.mjs +238 -0
  8. package/api/build/blocks/blocks.weights.json +1 -0
  9. package/api/build/build.doc.mjs +4 -0
  10. package/api/build/build.mjs +25 -6
  11. package/api/build/build.type.mjs +1 -1
  12. package/api/build/help/help.mjs +1 -1
  13. package/api/build/kit/answer.d.mts +18 -0
  14. package/api/build/kit/answer.mjs +195 -0
  15. package/api/build/kit/answer.test.mjs +108 -0
  16. package/api/build/kit/kind.d.mts +25 -0
  17. package/api/build/kit/kind.mjs +170 -0
  18. package/api/build/kit/kit.d.mts +2 -4
  19. package/api/build/kit/kit.mjs +8 -406
  20. package/api/build/kit/rank.d.mts +3 -32
  21. package/api/build/kit/rank.mjs +25 -342
  22. package/api/build/kit/rank.test.mjs +2 -1
  23. package/api/build/kit/signals.d.mts +79 -0
  24. package/api/build/kit/signals.mjs +295 -0
  25. package/api/build/kit/start.d.mts +32 -0
  26. package/api/build/kit/start.mjs +163 -0
  27. package/api/build/kit/words.d.mts +72 -0
  28. package/api/build/kit/words.mjs +202 -0
  29. package/api/build/tree.d.mts +20 -0
  30. package/api/build/tree.mjs +53 -0
  31. package/api/build/tree.test.mjs +185 -0
  32. package/api/build/tree.weights.json +1 -0
  33. package/api/component/_adapter.d.mts +72 -1
  34. package/api/component/_adapter.mjs +251 -23
  35. package/api/component/component-replacement.test.mjs +1 -0
  36. package/api/component/component.doc.mjs +6 -1
  37. package/api/component/component.mjs +97 -16
  38. package/api/component/component.test.mjs +95 -0
  39. package/api/component/doc-links.test.mjs +386 -0
  40. package/api/component/integration-export-name.test.mjs +644 -0
  41. package/api/component/list/list.mjs +42 -4
  42. package/api/discover/_adapter.d.mts +131 -12
  43. package/api/discover/_adapter.mjs +520 -34
  44. package/api/discover/_adapter.test.mjs +641 -17
  45. package/api/discover/_catalog-view.d.mts +16 -5
  46. package/api/discover/_catalog-view.mjs +32 -8
  47. package/api/discover/detail/doc/doc.mjs +9 -1
  48. package/api/discover/discover.d.mts +11 -0
  49. package/api/discover/discover.doc.mjs +9 -7
  50. package/api/discover/discover.mjs +120 -19
  51. package/api/discover/discover.type.d.mts +20 -7
  52. package/api/discover/discover.type.mjs +17 -7
  53. package/api/discover/list/list.d.mts +4 -3
  54. package/api/discover/list/list.mjs +17 -10
  55. package/api/docs/_adapter.d.mts +76 -2
  56. package/api/docs/_adapter.mjs +630 -34
  57. package/api/docs/docs-homes.test.mjs +127 -0
  58. package/api/docs/docs.doc.mjs +5 -4
  59. package/api/docs/docs.mjs +2 -1
  60. package/api/docs/docs.test.mjs +39 -20
  61. package/api/docs/integration-tree.test.mjs +42 -20
  62. package/api/docs/integrationDocs.test.mjs +125 -1
  63. package/api/docs/list/list.mjs +12 -2
  64. package/api/docs/namespace-section.test.mjs +6 -6
  65. package/api/docs/reference-blocks.test.mjs +3 -5
  66. package/api/doctor/_adapter.d.mts +128 -0
  67. package/api/doctor/_adapter.mjs +253 -4
  68. package/api/doctor/doctor.d.mts +217 -0
  69. package/api/doctor/doctor.doc.mjs +3 -2
  70. package/api/doctor/doctor.mjs +691 -52
  71. package/api/doctor/doctor.test.mjs +267 -18
  72. package/api/doctor/release-checks.test.mjs +156 -0
  73. package/api/doctor/theme-checks.mjs +17 -4
  74. package/api/doctor/theme-checks.test.mjs +223 -7
  75. package/api/error.d.mts +10 -0
  76. package/api/error.mjs +25 -0
  77. package/api/gap-report/gap-report.d.mts +3 -0
  78. package/api/gap-report/gap-report.doc.mjs +11 -3
  79. package/api/gap-report/gap-report.mjs +219 -52
  80. package/api/gap-report/gap-report.test.mjs +233 -0
  81. package/api/gap-report/gap-report.type.d.mts +41 -0
  82. package/api/gap-report/gap-report.type.mjs +18 -0
  83. package/api/hook/_adapter.mjs +3 -11
  84. package/api/hook/hook.doc.mjs +4 -0
  85. package/api/hook/hook.mjs +1 -1
  86. package/api/hook/list/list.d.mts +1 -1
  87. package/api/index.d.mts +2 -0
  88. package/api/index.mjs +2 -0
  89. package/api/init/_adapter.d.mts +22 -0
  90. package/api/init/_adapter.mjs +59 -0
  91. package/api/init/init.d.mts +7 -5
  92. package/api/init/init.doc.mjs +45 -10
  93. package/api/init/init.mjs +28 -4
  94. package/api/init/init.test.mjs +18 -2
  95. package/api/init/init.type.d.mts +50 -5
  96. package/api/init/init.type.mjs +39 -5
  97. package/api/init/remove/remove.mjs +17 -4
  98. package/api/init/remove/remove.test.mjs +114 -2
  99. package/api/init/run/run.d.mts +1 -1
  100. package/api/init/run/run.mjs +86 -12
  101. package/api/init/user/user.d.mts +14 -0
  102. package/api/init/user/user.mjs +87 -0
  103. package/api/init/user/user.test.mjs +232 -0
  104. package/api/integration/_adapter.d.mts +53 -0
  105. package/api/integration/_adapter.mjs +132 -0
  106. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  107. package/api/integration/add-contribution.mjs +8 -4
  108. package/api/integration/add-contribution.test.mjs +4 -2
  109. package/api/integration/authoring-checks.mjs +192 -14
  110. package/api/integration/authoring-checks.test.mjs +1028 -0
  111. package/api/integration/authoring-checks.type.d.mts +143 -0
  112. package/api/integration/authoring-checks.type.mjs +74 -2
  113. package/api/integration/check/check.d.mts +96 -0
  114. package/api/integration/check/check.mjs +357 -0
  115. package/api/integration/check/check.test.mjs +489 -0
  116. package/api/integration/integration-check.type.d.mts +118 -0
  117. package/api/integration/integration-check.type.mjs +84 -0
  118. package/api/integration/integrationCheck.doc.d.mts +10 -0
  119. package/api/integration/integrationCheck.doc.mjs +73 -0
  120. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  121. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  122. package/api/integration/pack-check.mjs +486 -123
  123. package/api/integration/pack-check.test.mjs +817 -10
  124. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +73 -5
  126. package/api/integration/validate-integration-gaps.test.mjs +1975 -0
  127. package/api/integration/validate-integration.d.mts +23 -2
  128. package/api/integration/validate-integration.mjs +358 -45
  129. package/api/integration/validate-integration.test.mjs +88 -8
  130. package/api/integration/validate-integration.type.d.mts +105 -0
  131. package/api/integration/validate-integration.type.mjs +79 -1
  132. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  133. package/api/integration/validateIntegration.doc.mjs +19 -3
  134. package/api/search/ORDERING.md +28 -0
  135. package/api/search/_adapter.d.mts +91 -0
  136. package/api/search/_adapter.mjs +172 -0
  137. package/api/search/_adapter.test.mjs +173 -0
  138. package/api/search/_candidate-score.d.mts +40 -0
  139. package/api/search/_candidate-score.mjs +238 -0
  140. package/api/search/_cards.json +6098 -0
  141. package/api/search/_compound.d.mts +50 -0
  142. package/api/search/_compound.mjs +170 -0
  143. package/api/search/_doc-searcher.d.mts +4 -0
  144. package/api/search/_doc-searcher.mjs +23 -0
  145. package/api/search/_doc-searcher.test.mjs +36 -0
  146. package/api/search/_doc-searcher.weights.json +1 -0
  147. package/api/search/_doc-text.d.mts +30 -0
  148. package/api/search/_doc-text.mjs +87 -0
  149. package/api/search/_entries.d.mts +63 -0
  150. package/api/search/_entries.mjs +232 -0
  151. package/api/search/_entries.test.mjs +48 -0
  152. package/api/search/_features.d.mts +35 -0
  153. package/api/search/_features.mjs +155 -0
  154. package/api/search/_gather-components.d.mts +26 -0
  155. package/api/search/_gather-components.mjs +284 -0
  156. package/api/search/_gather-docs.d.mts +20 -0
  157. package/api/search/_gather-docs.mjs +254 -0
  158. package/api/search/_gather-hooks.d.mts +12 -0
  159. package/api/search/_gather-hooks.mjs +52 -0
  160. package/api/search/_gather-templates.d.mts +25 -0
  161. package/api/search/_gather-templates.mjs +107 -0
  162. package/api/search/_lexical.d.mts +135 -0
  163. package/api/search/_lexical.mjs +351 -0
  164. package/api/search/_main-searcher.d.mts +19 -0
  165. package/api/search/_main-searcher.mjs +115 -0
  166. package/api/search/_main-searcher.test.mjs +106 -0
  167. package/api/search/_main-searcher.weights.json +1 -0
  168. package/api/search/_module-doc.d.mts +32 -0
  169. package/api/search/_module-doc.mjs +78 -0
  170. package/api/search/_order.d.mts +32 -0
  171. package/api/search/_order.mjs +162 -0
  172. package/api/search/_order.test.mjs +72 -0
  173. package/api/search/_priority.d.mts +50 -0
  174. package/api/search/_priority.mjs +139 -0
  175. package/api/search/_query-words.d.mts +25 -0
  176. package/api/search/_query-words.mjs +92 -0
  177. package/api/search/_query.d.mts +65 -0
  178. package/api/search/_query.mjs +176 -0
  179. package/api/search/_result.d.mts +97 -0
  180. package/api/search/_result.mjs +150 -0
  181. package/api/search/_score.d.mts +60 -0
  182. package/api/search/_score.mjs +283 -0
  183. package/api/search/_synonyms.d.mts +21 -0
  184. package/api/search/_synonyms.mjs +67 -0
  185. package/api/search/_template-searcher.d.mts +4 -0
  186. package/api/search/_template-searcher.mjs +23 -0
  187. package/api/search/_template-searcher.test.mjs +40 -0
  188. package/api/search/_template-searcher.weights.json +1 -0
  189. package/api/search/_titles.d.mts +33 -0
  190. package/api/search/_titles.mjs +65 -0
  191. package/api/search/_words.d.mts +64 -0
  192. package/api/search/_words.mjs +157 -0
  193. package/api/search/search.d.mts +22 -236
  194. package/api/search/search.doc.mjs +14 -7
  195. package/api/search/search.domain-priority.test.mjs +108 -18
  196. package/api/search/search.integration-check.test.mjs +24 -0
  197. package/api/search/search.mjs +59 -1904
  198. package/api/search/search.test.mjs +193 -15
  199. package/api/search/search.type.d.mts +20 -2
  200. package/api/search/search.type.mjs +12 -2
  201. package/api/search/tree.d.mts +15 -0
  202. package/api/search/tree.mjs +40 -0
  203. package/api/search/tree.test.mjs +66 -0
  204. package/api/search/tree.weights.json +1 -0
  205. package/api/swizzle/_adapter.mjs +2 -2
  206. package/api/swizzle/copy/copy.mjs +29 -15
  207. package/api/template/template-integration.test.mjs +1 -1
  208. package/api/template/template.mjs +5 -1
  209. package/api/template/template.test.mjs +120 -3
  210. package/api/theme/_adapter.d.mts +16 -0
  211. package/api/theme/_adapter.mjs +32 -5
  212. package/api/theme/build/build.d.mts +2 -1
  213. package/api/theme/build/build.family.test.mjs +64 -0
  214. package/api/theme/build/build.mjs +250 -38
  215. package/api/theme/build/build.packed-old-core.test.mjs +21 -3
  216. package/api/theme/build/font-warning.mjs +5 -3
  217. package/api/theme/build/font-warning.test.mjs +2 -1
  218. package/api/theme/remove/remove.test.mjs +2 -2
  219. package/api/theme/targets/targets.mjs +3 -10
  220. package/api/theme/themeBuild.doc.mjs +4 -4
  221. package/api/theme/themeTargets.doc.mjs +4 -0
  222. package/api/theme/use/use.test.mjs +1 -1
  223. package/api/upgrade/_adapter.d.mts +97 -11
  224. package/api/upgrade/_adapter.mjs +315 -32
  225. package/api/upgrade/registry/registry.d.mts +10 -4
  226. package/api/upgrade/registry/registry.mjs +33 -16
  227. package/api/upgrade/registry/registry.test.mjs +85 -0
  228. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  229. package/api/upgrade/run/run.d.mts +4 -2
  230. package/api/upgrade/run/run.mjs +48 -4
  231. package/api/upgrade/run/run.test.mjs +417 -3
  232. package/api/upgrade/status/status.d.mts +21 -5
  233. package/api/upgrade/status/status.mjs +45 -8
  234. package/api/upgrade/upgrade.doc.mjs +16 -8
  235. package/api/upgrade/upgrade.mjs +18 -3
  236. package/api/upgrade/upgrade.test.mjs +194 -0
  237. package/api/upgrade/upgrade.type.d.mts +28 -3
  238. package/api/upgrade/upgrade.type.mjs +24 -7
  239. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  240. package/assets/codemods/registry.mjs +1 -0
  241. package/assets/codemods/runner.mjs +7 -1
  242. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  243. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  244. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  245. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  246. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  247. package/assets/discover/official-packages.json +367 -0
  248. package/assets/docs/README.md +10 -0
  249. package/assets/docs/author-a-theme.doc.mjs +5 -5
  250. package/assets/docs/browser-support.doc.mjs +1 -1
  251. package/assets/docs/getting-started.doc.mjs +26 -6
  252. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  253. package/assets/docs/styling-overview.doc.mjs +1 -1
  254. package/assets/docs/theme.doc.dense.mjs +1 -1
  255. package/assets/docs/theme.doc.mjs +2 -2
  256. package/assets/docs/theme.doc.zh.mjs +1 -1
  257. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  258. package/assets/docs/tree/add-a-theme.doc.mjs +14 -4
  259. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  260. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  261. package/assets/docs/tree/api.doc.mjs +6 -0
  262. package/assets/docs/tree/authoring.doc.mjs +61 -0
  263. package/assets/docs/tree/block-template.doc.mjs +8 -20
  264. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  265. package/assets/docs/tree/building-blocks.doc.mjs +6 -2
  266. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  267. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  268. package/assets/docs/tree/checks.doc.mjs +32 -24
  269. package/assets/docs/tree/ci-and-containers.doc.mjs +101 -0
  270. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  271. package/assets/docs/tree/cli.doc.mjs +7 -1
  272. package/assets/docs/tree/codemods.doc.mjs +17 -7
  273. package/assets/docs/tree/commands.doc.mjs +7 -1
  274. package/assets/docs/tree/component-family.doc.mjs +8 -6
  275. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  276. package/assets/docs/tree/components.doc.mjs +7 -1
  277. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  278. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  279. package/assets/docs/tree/configuration.doc.mjs +8 -4
  280. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  281. package/assets/docs/tree/define-the-theme.doc.mjs +7 -6
  282. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  283. package/assets/docs/tree/docs.doc.mjs +9 -3
  284. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  285. package/assets/docs/tree/document-the-theme.doc.mjs +4 -4
  286. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  287. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  288. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  289. package/assets/docs/tree/fonts-and-assets.doc.mjs +3 -3
  290. package/assets/docs/tree/generate-a-palette.doc.mjs +8 -6
  291. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  292. package/assets/docs/tree/global.doc.mjs +131 -0
  293. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  294. package/assets/docs/tree/help.doc.mjs +2 -2
  295. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  296. package/assets/docs/tree/install-once.doc.mjs +105 -0
  297. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  298. package/assets/docs/tree/layout.doc.mjs +6 -6
  299. package/assets/docs/tree/links.doc.mjs +7 -6
  300. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  301. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  302. package/assets/docs/tree/migration.doc.mjs +0 -1
  303. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  304. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  305. package/assets/docs/tree/page-template.doc.mjs +4 -8
  306. package/assets/docs/tree/problems.doc.mjs +205 -0
  307. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  308. package/assets/docs/tree/publishing.doc.mjs +6 -6
  309. package/assets/docs/tree/quick-start.doc.mjs +54 -28
  310. package/assets/docs/tree/reference.doc.mjs +192 -0
  311. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  312. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  313. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  314. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  315. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  316. package/assets/docs/tree/ship.doc.mjs +2 -2
  317. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  318. package/assets/docs/tree/single-component.doc.mjs +15 -6
  319. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  320. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  321. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  322. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  323. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  324. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  325. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  326. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  327. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  328. package/assets/docs/tree/templates.doc.mjs +7 -1
  329. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  330. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  331. package/assets/docs/tree/themes.doc.mjs +5 -1
  332. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  333. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  334. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  335. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  336. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  337. package/assets/docs/tree/updates.doc.mjs +92 -0
  338. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  339. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +15 -3
  340. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  341. package/assets/docs/tree/versioning.doc.mjs +14 -9
  342. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  343. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  344. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  345. package/assets/docs/use-a-theme.doc.mjs +2 -2
  346. package/assets/docs/use-integrations.doc.mjs +120 -0
  347. package/assets/docs/working-with-ai.doc.mjs +70 -13
  348. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  349. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  350. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  351. package/assets/theme.template.ts +1 -1
  352. package/authoring/codemod/codemod.doc.mjs +7 -3
  353. package/authoring/config/config.doc.mjs +9 -6
  354. package/authoring/config/type.ts +4 -2
  355. package/authoring/debug/parse.d.mts +5 -5
  356. package/authoring/discover/discover.doc.mjs +7 -1
  357. package/authoring/doctypes/_schema.d.mts +8 -3
  358. package/authoring/doctypes/_schema.mjs +1 -0
  359. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  360. package/authoring/doctypes/command/command.doc.mjs +20 -12
  361. package/authoring/doctypes/command/type.ts +14 -0
  362. package/authoring/doctypes/component/component.doc.mjs +30 -27
  363. package/authoring/doctypes/component/type.ts +21 -8
  364. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  365. package/authoring/doctypes/function/function.doc.mjs +9 -8
  366. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  367. package/authoring/doctypes/hook/type.ts +4 -0
  368. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  369. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  370. package/authoring/doctypes/schema/schema.doc.mjs +10 -10
  371. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  372. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  373. package/authoring/doctypes/template/parse.d.mts +6 -2
  374. package/authoring/doctypes/template/parse.mjs +3 -1
  375. package/authoring/doctypes/template/template.doc.mjs +3 -3
  376. package/authoring/doctypes/template/type.ts +22 -0
  377. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  378. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  379. package/authoring/identity/identity.doc.mjs +3 -3
  380. package/authoring/index.d.ts +2 -0
  381. package/authoring/integration/integration.doc.mjs +30 -6
  382. package/authoring/integration/parse.test.mjs +21 -0
  383. package/authoring/integration/schema.d.mts +16 -3
  384. package/authoring/integration/schema.mjs +34 -2
  385. package/authoring/integration/type.ts +13 -1
  386. package/clients/cli/bin/astryx.mjs +11 -65
  387. package/clients/cli/cli-command.test.mjs +166 -0
  388. package/clients/cli/command-result-coverage.test.mjs +6 -5
  389. package/clients/cli/commands/blog.doc.mjs +1 -0
  390. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  391. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  392. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  393. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  394. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  395. package/clients/cli/commands/build.doc.mjs +1 -0
  396. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  397. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  398. package/clients/cli/commands/component.doc.mjs +1 -0
  399. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  400. package/clients/cli/commands/component.test.mjs +6 -6
  401. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  402. package/clients/cli/commands/discover.doc.mjs +9 -1
  403. package/clients/cli/commands/discover.mjs +144 -93
  404. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  405. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  406. package/clients/cli/commands/docs.doc.mjs +3 -2
  407. package/clients/cli/commands/docs.mjs +50 -20
  408. package/clients/cli/commands/docs.test.mjs +16 -16
  409. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  410. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  411. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  412. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  413. package/clients/cli/commands/doctor-integration.test.mjs +936 -3
  414. package/clients/cli/commands/doctor.doc.mjs +12 -2
  415. package/clients/cli/commands/doctor.mjs +332 -7
  416. package/clients/cli/commands/doctor.test.mjs +75 -4
  417. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  418. package/clients/cli/commands/gap-report.mjs +24 -2
  419. package/clients/cli/commands/gap-report.test.mjs +79 -0
  420. package/clients/cli/commands/hook.doc.mjs +1 -0
  421. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  422. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  423. package/clients/cli/commands/init.behavior.test.mjs +235 -1
  424. package/clients/cli/commands/init.doc.mjs +55 -10
  425. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  426. package/clients/cli/commands/init.mjs +2 -2
  427. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  428. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  429. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  430. package/clients/cli/commands/integration-check.test.mjs +489 -0
  431. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  432. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  433. package/clients/cli/commands/integration.doc.mjs +6 -2
  434. package/clients/cli/commands/integration.mjs +119 -7
  435. package/clients/cli/commands/json-contract.test.mjs +11 -2
  436. package/clients/cli/commands/manifest.doc.mjs +2 -1
  437. package/clients/cli/commands/manifest.mjs +67 -0
  438. package/clients/cli/commands/outside-project.test.mjs +874 -0
  439. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  440. package/clients/cli/commands/postinstall.mjs +31 -0
  441. package/clients/cli/commands/search.doc.mjs +1 -0
  442. package/clients/cli/commands/search.mjs +16 -11
  443. package/clients/cli/commands/search.test.mjs +429 -296
  444. package/clients/cli/commands/setup-nudge.test.mjs +180 -2
  445. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  446. package/clients/cli/commands/swizzle.mjs +5 -3
  447. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  448. package/clients/cli/commands/template.doc.mjs +1 -0
  449. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  450. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  451. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  452. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  453. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  454. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  455. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  456. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  457. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  458. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  459. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  460. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  461. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +27 -14
  462. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  463. package/clients/cli/commands/upgrade.doc.mjs +26 -3
  464. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  465. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  466. package/clients/cli/commands/upgrade.mjs +9 -2
  467. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  468. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  469. package/clients/cli/index.mjs +156 -100
  470. package/clients/cli/lib/cli-error.mjs +4 -1
  471. package/clients/cli/lib/component-format.mjs +1 -1
  472. package/clients/cli/lib/component-format.test.mjs +1 -1
  473. package/clients/cli/lib/define-command.mjs +14 -9
  474. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  475. package/clients/cli/lib/json-shim.mjs +3 -1
  476. package/clients/cli/lib/manifest.d.ts +9 -0
  477. package/clients/cli/lib/manifest.mjs +17 -1
  478. package/clients/cli/lib/manifest.test.mjs +37 -0
  479. package/clients/cli/lib/welcome-box.mjs +50 -0
  480. package/clients/cli/run.mjs +108 -0
  481. package/foundation/agent-docs/agent-doc-state.d.mts +67 -13
  482. package/foundation/agent-docs/agent-doc-state.mjs +123 -14
  483. package/foundation/agent-docs/agent-docs.d.mts +104 -15
  484. package/foundation/agent-docs/agent-docs.mjs +1028 -114
  485. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  486. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  487. package/foundation/agent-docs/computer-agent-text.d.mts +170 -0
  488. package/foundation/agent-docs/computer-agent-text.mjs +750 -0
  489. package/foundation/agent-docs/computer-agent-text.test.mjs +699 -0
  490. package/foundation/config/project.d.mts +35 -3
  491. package/foundation/config/project.mjs +243 -26
  492. package/foundation/config/project.test.mjs +122 -0
  493. package/foundation/config/theme-state.mjs +5 -4
  494. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  495. package/foundation/discovery/authoring-self-docs.mjs +17 -4
  496. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  497. package/foundation/discovery/authoring-surface.d.mts +1 -1
  498. package/foundation/discovery/authoring-surface.mjs +2 -2
  499. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  500. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  501. package/foundation/discovery/cli-self-docs.mjs +22 -4
  502. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  503. package/foundation/discovery/component-discovery.d.mts +227 -10
  504. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  505. package/foundation/discovery/component-discovery.mjs +940 -57
  506. package/foundation/discovery/component-discovery.test.mjs +96 -0
  507. package/foundation/discovery/component-loader.d.mts +1 -1
  508. package/foundation/discovery/component-replacement.mjs +20 -1
  509. package/foundation/discovery/component-replacement.test.mjs +40 -0
  510. package/foundation/discovery/copied-passages.d.mts +216 -0
  511. package/foundation/discovery/copied-passages.mjs +720 -0
  512. package/foundation/discovery/copied-passages.test.mjs +124 -0
  513. package/foundation/discovery/doc-consistency.d.mts +34 -0
  514. package/foundation/discovery/doc-consistency.mjs +513 -0
  515. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  516. package/foundation/discovery/doc-fluff.d.mts +112 -0
  517. package/foundation/discovery/doc-fluff.mjs +328 -0
  518. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  519. package/foundation/discovery/doc-types.d.mts +157 -0
  520. package/foundation/discovery/doc-types.mjs +1231 -0
  521. package/foundation/discovery/doc-types.test.mjs +744 -0
  522. package/foundation/discovery/docs-discovery.d.mts +88 -0
  523. package/foundation/discovery/docs-discovery.mjs +97 -3
  524. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  525. package/foundation/discovery/integration-doc-files.mjs +231 -0
  526. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  527. package/foundation/discovery/module-exports.d.mts +68 -0
  528. package/foundation/discovery/module-exports.mjs +609 -31
  529. package/foundation/discovery/module-exports.test.mjs +172 -0
  530. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  531. package/foundation/discovery/package-tsconfig.mjs +228 -0
  532. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  533. package/foundation/discovery/project-typescript.d.mts +32 -0
  534. package/foundation/discovery/project-typescript.mjs +89 -0
  535. package/foundation/discovery/prop-drift.d.mts +297 -0
  536. package/foundation/discovery/prop-drift.mjs +2428 -0
  537. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  538. package/foundation/discovery/runner-allowlist.d.mts +38 -0
  539. package/foundation/discovery/runner-allowlist.mjs +51 -0
  540. package/foundation/discovery/runner-commands.d.mts +77 -0
  541. package/foundation/discovery/runner-commands.mjs +294 -0
  542. package/foundation/discovery/runner-commands.test.mjs +128 -0
  543. package/foundation/discovery/stray-pointers.d.mts +52 -0
  544. package/foundation/discovery/stray-pointers.mjs +293 -0
  545. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  546. package/foundation/discovery/template-adapter.mjs +92 -6
  547. package/foundation/discovery/template-needs.mjs +6 -46
  548. package/foundation/discovery/theme-discovery.mjs +147 -3
  549. package/foundation/discovery/theme-imports.mjs +8 -4
  550. package/foundation/discovery/theme-imports.test.mjs +1 -1
  551. package/foundation/discovery/type-match.d.mts +53 -0
  552. package/foundation/discovery/type-match.mjs +660 -0
  553. package/foundation/discovery/type-match.test.mjs +169 -0
  554. package/foundation/discovery/type-text.d.mts +68 -0
  555. package/foundation/discovery/type-text.mjs +658 -0
  556. package/foundation/discovery/type-text.test.mjs +196 -0
  557. package/foundation/discovery/unresolved-references.d.mts +62 -0
  558. package/foundation/discovery/unresolved-references.mjs +790 -0
  559. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  560. package/foundation/doc-compiler/doc-loads.test.mjs +84 -10
  561. package/foundation/doc-compiler/inputs.mjs +27 -1
  562. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  563. package/foundation/doc-compiler/links.d.mts +41 -5
  564. package/foundation/doc-compiler/links.mjs +167 -28
  565. package/foundation/doc-compiler/links.test.mjs +97 -4
  566. package/foundation/doc-compiler/read.d.mts +1 -1
  567. package/foundation/doc-compiler/read.mjs +59 -1
  568. package/foundation/doc-compiler/read.test.mjs +11 -2
  569. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  570. package/foundation/doc-compiler/tree.d.mts +32 -6
  571. package/foundation/doc-compiler/tree.mjs +107 -44
  572. package/foundation/doc-compiler/tree.test.mjs +14 -7
  573. package/foundation/env/cli-command.d.mts +83 -0
  574. package/foundation/env/cli-command.mjs +152 -0
  575. package/foundation/env/cli-command.test.mjs +129 -0
  576. package/foundation/env/node-version.test.mjs +15 -3
  577. package/foundation/env/package-manager.d.mts +93 -9
  578. package/foundation/env/package-manager.global.test.mjs +66 -0
  579. package/foundation/env/package-manager.mjs +184 -13
  580. package/foundation/env/package-manager.test.mjs +341 -15
  581. package/foundation/env/project-scope.d.mts +9 -0
  582. package/foundation/env/project-scope.mjs +33 -0
  583. package/foundation/env/search-path.d.mts +42 -0
  584. package/foundation/env/search-path.mjs +205 -0
  585. package/foundation/env/search-path.test.mjs +122 -0
  586. package/foundation/env/user-dirs.d.mts +39 -0
  587. package/foundation/env/user-dirs.mjs +93 -0
  588. package/foundation/env/user-dirs.test.mjs +72 -0
  589. package/foundation/env/user-settings.d.mts +46 -0
  590. package/foundation/env/user-settings.mjs +106 -0
  591. package/foundation/env/user-settings.test.mjs +64 -0
  592. package/foundation/fs/file-protection.mjs +15 -1
  593. package/foundation/fs/module-loader.d.mts +2 -1
  594. package/foundation/fs/module-loader.mjs +60 -4
  595. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  596. package/foundation/fs/module-loader.test.mjs +194 -0
  597. package/foundation/fs/path-compare.d.mts +33 -0
  598. package/foundation/fs/path-compare.mjs +73 -0
  599. package/foundation/fs/paths.d.mts +122 -4
  600. package/foundation/fs/paths.mjs +675 -29
  601. package/foundation/fs/paths.test.mjs +439 -54
  602. package/foundation/fs/read-record.d.mts +18 -0
  603. package/foundation/fs/read-record.mjs +131 -0
  604. package/foundation/fs/read-record.test.mjs +77 -0
  605. package/foundation/integrations/autolink.d.mts +2 -1
  606. package/foundation/integrations/autolink.mjs +4 -13
  607. package/foundation/integrations/cli-peer-version.d.mts +36 -0
  608. package/foundation/integrations/cli-peer-version.mjs +132 -0
  609. package/foundation/integrations/cli-peer-version.test.mjs +354 -0
  610. package/foundation/integrations/computer-integrations.d.mts +81 -0
  611. package/foundation/integrations/computer-integrations.mjs +345 -0
  612. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  613. package/foundation/integrations/contribution-inventory.d.mts +26 -20
  614. package/foundation/integrations/contribution-inventory.mjs +97 -8
  615. package/foundation/integrations/exports-subpaths.d.mts +57 -0
  616. package/foundation/integrations/exports-subpaths.mjs +113 -0
  617. package/foundation/integrations/integration-warnings.mjs +3 -2
  618. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  619. package/foundation/integrations/integrations.d.mts +51 -1
  620. package/foundation/integrations/integrations.mjs +106 -4
  621. package/foundation/integrations/issue.ts +6 -0
  622. package/foundation/integrations/public-imports.d.mts +118 -0
  623. package/foundation/integrations/public-imports.mjs +1769 -0
  624. package/foundation/integrations/public-imports.test.mjs +1793 -0
  625. package/foundation/integrations/validate-contributions.d.mts +117 -0
  626. package/foundation/integrations/validate-contributions.mjs +1209 -14
  627. package/foundation/release/agent-text-worker.d.mts +4 -0
  628. package/foundation/release/agent-text-worker.mjs +52 -0
  629. package/foundation/release/cache.d.mts +310 -0
  630. package/foundation/release/cache.mjs +1216 -0
  631. package/foundation/release/cache.test.mjs +923 -0
  632. package/foundation/release/choose.d.mts +207 -0
  633. package/foundation/release/choose.mjs +677 -0
  634. package/foundation/release/choose.test.mjs +427 -0
  635. package/foundation/release/core-worker.d.mts +4 -0
  636. package/foundation/release/core-worker.mjs +38 -0
  637. package/foundation/release/global-state.d.mts +263 -0
  638. package/foundation/release/global-state.mjs +474 -0
  639. package/foundation/release/global-state.test.mjs +381 -0
  640. package/foundation/release/handoff.d.mts +79 -0
  641. package/foundation/release/handoff.e2e.test.mjs +278 -0
  642. package/foundation/release/handoff.mjs +524 -0
  643. package/foundation/release/lock-lease.d.mts +23 -0
  644. package/foundation/release/lock-lease.mjs +80 -0
  645. package/foundation/release/notices.d.mts +307 -0
  646. package/foundation/release/notices.mjs +707 -0
  647. package/foundation/release/notices.test.mjs +586 -0
  648. package/foundation/release/npm-guard.d.mts +4 -0
  649. package/foundation/release/npm-guard.mjs +126 -0
  650. package/foundation/release/project.d.mts +141 -0
  651. package/foundation/release/project.mjs +466 -0
  652. package/foundation/release/project.test.mjs +288 -0
  653. package/foundation/release/refresh-worker.d.mts +4 -0
  654. package/foundation/release/refresh-worker.mjs +27 -0
  655. package/foundation/release/run-context.d.mts +47 -0
  656. package/foundation/release/run-context.mjs +163 -0
  657. package/foundation/release/update-notice-env.d.mts +28 -0
  658. package/foundation/release/update-notice-env.mjs +39 -0
  659. package/foundation/response/base.d.ts +26 -3
  660. package/foundation/response/error-codes.d.mts +3 -1
  661. package/foundation/response/error-codes.doc.mjs +12 -0
  662. package/foundation/response/error-codes.mjs +17 -0
  663. package/foundation/response/error-codes.test.mjs +2 -0
  664. package/foundation/response/json-contract.test.mjs +7 -2
  665. package/foundation/response/json.mjs +4 -1
  666. package/foundation/response/response-types.doc.mjs +22 -11
  667. package/foundation/response/response-types.doc.test.mjs +46 -0
  668. package/foundation/response/response.doc.mjs +3 -3
  669. package/foundation/route/route.d.mts +285 -0
  670. package/foundation/route/route.mjs +324 -0
  671. package/foundation/route/route.test.mjs +153 -0
  672. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  673. package/foundation/runtime/locked-versions.d.mts +32 -0
  674. package/foundation/runtime/locked-versions.mjs +65 -0
  675. package/foundation/runtime/package-root.d.mts +91 -0
  676. package/foundation/runtime/package-root.mjs +174 -0
  677. package/foundation/runtime/package-set.d.mts +62 -0
  678. package/foundation/runtime/package-set.mjs +130 -0
  679. package/foundation/runtime/package-set.test.mjs +432 -0
  680. package/foundation/text/string-utils.d.mts +2 -1
  681. package/foundation/text/string-utils.mjs +16 -5
  682. package/foundation/text/suggest-order.d.mts +72 -0
  683. package/foundation/text/suggest-order.mjs +363 -0
  684. package/foundation/text/suggest-order.test.mjs +259 -0
  685. package/foundation/xle/browser-imports.test.mjs +74 -0
  686. package/foundation/xle/registry.mjs +4 -0
  687. package/foundation/xle/registry.test.mjs +28 -0
  688. package/host/door-conformance.test.mjs +274 -0
  689. package/host/index.d.ts +116 -0
  690. package/host/index.mjs +295 -0
  691. package/host/index.test.mjs +302 -0
  692. package/package.json +16 -11
  693. package/scripts/postinstall.mjs +24 -5
  694. package/assets/docs/tree/integrations.doc.mjs +0 -40
@@ -0,0 +1,750 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The computer-level agent text (spec AST-081 FR8): one skill file,
5
+ * `~/.agents/skills/astryx/SKILL.md`, which most coding agents read, linked
6
+ * into the skill folder of each agent that reads only its own
7
+ * (`SKILL_LINKS`), so every agent on the computer knows Astryx is there.
8
+ *
9
+ * - One file is written. Each other folder gets a link to its folder: a
10
+ * directory symlink, or a junction on Windows, which needs no
11
+ * administrator rights. A refresh rewrites only the file; the links follow.
12
+ * - `SKILL_LINKS` is the one table of link targets: a folder relative to the
13
+ * home folder, and the folder that must already exist for it to be linked
14
+ * (`null`: always). The writer, the remover, doctor, and the docs all read it.
15
+ * - The text is version-free and names only stable entry points
16
+ * (`STABLE_COMMANDS`), so an older file never misleads. It is short: the
17
+ * skill only has to say that Astryx is here, when to use it, and where to
18
+ * start; the CLI's own docs carry the rest.
19
+ * - The file keeps the Astryx markers, which tell a file Astryx wrote from one
20
+ * a person wrote, and records inside them a hash of its text, which tells an
21
+ * edited file from an older rendering. A file without the markers, an
22
+ * edited file, and a symlinked file are never changed. A link target that
23
+ * holds anything Astryx did not create is never replaced; doctor names it.
24
+ * - The links Astryx made are recorded in the settings file, so removal
25
+ * removes exactly those.
26
+ *
27
+ * `astryx init --user` writes and removes them, and the global copy's
28
+ * background step (`foundation/release/agent-text-worker.mjs`) keeps the file
29
+ * on its release (FR7, FR8). Both honor the settings file
30
+ * (`foundation/env/user-settings.mjs`). This module names fixed paths in the
31
+ * home folder and nothing else.
32
+ *
33
+ * @input the home folder, the host's door (if any), the settings file
34
+ * @output the rendered text, its state on disk, and the write/remove receipt rows
35
+ * @position foundation/agent-docs — read by `init --user`, by the background
36
+ * step, and by `doctor`.
37
+ */
38
+
39
+ import * as fs from 'node:fs';
40
+ import * as os from 'node:os';
41
+ import * as path from 'node:path';
42
+ import {createHash} from 'node:crypto';
43
+ import {cliDoor} from '../env/cli-command.mjs';
44
+ import {astryxCommandsOnSearchPath} from '../env/search-path.mjs';
45
+ import {samePath} from '../fs/path-compare.mjs';
46
+ import {CLI_ROOT} from '../fs/paths.mjs';
47
+ import {CLI_PACKAGE} from '../env/package-manager.mjs';
48
+ import {isGlobalCopy} from '../release/choose.mjs';
49
+ import {globalUpgradeCommand} from '../release/notices.mjs';
50
+ import {
51
+ MARKER_START,
52
+ MARKER_END,
53
+ SELF_CHECK_CSS,
54
+ WORKFLOW_STEPS,
55
+ WORKFLOW_TITLE,
56
+ } from './agent-doc-state.mjs';
57
+
58
+ /** The command the computer-level text uses when no host door is set. */
59
+ export const COMPUTER_COMMAND = 'astryx';
60
+
61
+ /** The install step for the CLI copy the computer-level text relies on. */
62
+ export const COMPUTER_INSTALL_STEP = 'npm i -g @astryxdesign/cli';
63
+
64
+ /**
65
+ * What to do when the `astryx` command the text names is not found, as on a
66
+ * machine whose home folder outlives its system folders: the skill file
67
+ * survives a restart while the CLI installed for the computer does not.
68
+ * @param {string} install the install step
69
+ * @returns {string}
70
+ */
71
+ export function computerRestoreLine(install) {
72
+ return `If \`${COMPUTER_COMMAND}\` is not found, run \`${install}\` first.`;
73
+ }
74
+
75
+ /**
76
+ * The step the CLI installed for the computer names for itself: the command
77
+ * that installs it again with the same package manager into the same folder,
78
+ * as the update notice names it but with no version. Null for any other copy.
79
+ * @param {string} [cliRoot]
80
+ * @returns {string | null}
81
+ */
82
+ function ownInstallStep(cliRoot = CLI_ROOT) {
83
+ try {
84
+ if (isGlobalCopy(cliRoot)) {
85
+ return globalUpgradeCommand(cliRoot).replace(
86
+ `${CLI_PACKAGE}@latest`,
87
+ CLI_PACKAGE,
88
+ );
89
+ }
90
+ } catch {
91
+ // A copy whose install cannot be read names none of its own.
92
+ }
93
+ return null;
94
+ }
95
+
96
+ /**
97
+ * The step the skill file in `home` records, or null.
98
+ * @param {string} home
99
+ * @returns {string | null}
100
+ */
101
+ function recordedInstallStep(home) {
102
+ try {
103
+ return skillInstallStepOf(fs.readFileSync(computerSkillFile(home), 'utf8'));
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * The install step the text names. `judge`: the step a file is judged by, the
111
+ * one it records first, so every copy of one release, two global installs
112
+ * included, finds the same text current. `write`: the step a write records,
113
+ * the global copy's own first, since only the CLI installed for the computer
114
+ * chooses it. Either way npm's when there is no other.
115
+ * @param {{cliRoot?: string, home?: string, for?: 'judge' | 'write'}} [options]
116
+ * @returns {string}
117
+ */
118
+ export function computerInstallStep({
119
+ cliRoot = CLI_ROOT,
120
+ home = os.homedir(),
121
+ for: use = 'write',
122
+ } = {}) {
123
+ const own = ownInstallStep(cliRoot);
124
+ const recorded = recordedInstallStep(home);
125
+ return (
126
+ (use === 'judge' ? (recorded ?? own) : (own ?? recorded)) ??
127
+ COMPUTER_INSTALL_STEP
128
+ );
129
+ }
130
+
131
+ /**
132
+ * The install step a skill file's text records, or null when it records
133
+ * none.
134
+ * @param {string} content
135
+ * @returns {string | null}
136
+ */
137
+ export function skillInstallStepOf(content) {
138
+ return /is not found, run `(.+?)` first\./u.exec(content)?.[1] ?? null;
139
+ }
140
+
141
+ /** The skill folder, relative to the home folder, that holds the one file. */
142
+ const SKILL_FOLDER = path.join('.agents', 'skills', 'astryx');
143
+
144
+ /**
145
+ * Where the skill folder reaches agents that read only their own skills
146
+ * folder, each relative to the home folder: the agent's skills folder, the
147
+ * folder that must already exist for it to be used (`null`: always), and how:
148
+ * `link`, a folder link to the skill folder with a relative target, or `copy`,
149
+ * a managed copy of the file for an agent whose scanner skips links. Public
150
+ * agents only. Never a store another tool manages, such as `~/.codex/skills`.
151
+ * @type {ReadonlyArray<{skills: string, when: string | null, mode: 'link' | 'copy'}>}
152
+ */
153
+ export const SKILL_LINKS = Object.freeze([
154
+ {skills: path.join('.claude', 'skills'), when: null, mode: 'link'},
155
+ {skills: path.join('.kiro', 'skills'), when: '.kiro', mode: 'link'},
156
+ {
157
+ skills: path.join('.codeium', 'windsurf', 'skills'),
158
+ when: path.join('.codeium', 'windsurf'),
159
+ mode: 'link',
160
+ },
161
+ {skills: path.join('.letta', 'skills'), when: '.letta', mode: 'link'},
162
+ {
163
+ skills: path.join(
164
+ 'Library',
165
+ 'Developer',
166
+ 'Xcode',
167
+ 'CodingAssistant',
168
+ 'ClaudeAgentConfig',
169
+ 'skills',
170
+ ),
171
+ when: path.join('Library', 'Developer', 'Xcode', 'CodingAssistant'),
172
+ mode: 'link',
173
+ },
174
+ // Kimi reads only the first of `~/.config/agents/skills` and
175
+ // `~/.agents/skills` that exists.
176
+ {
177
+ skills: path.join('.config', 'agents', 'skills'),
178
+ when: path.join('.config', 'agents', 'skills'),
179
+ mode: 'link',
180
+ },
181
+ {skills: path.join('.continue', 'skills'), when: '.continue', mode: 'copy'},
182
+ ]);
183
+
184
+ /**
185
+ * The commands the text may name: stable entry points every release keeps.
186
+ * computer-agent-text.test.mjs fails on any other.
187
+ */
188
+ export const STABLE_COMMANDS = Object.freeze([
189
+ 'search',
190
+ 'docs',
191
+ 'component',
192
+ 'template',
193
+ 'build',
194
+ 'theme',
195
+ 'init',
196
+ 'doctor',
197
+ 'help',
198
+ ]);
199
+
200
+ /**
201
+ * When an agent should load the skill; the frontmatter `description`. One
202
+ * sentence.
203
+ */
204
+ const SKILL_DESCRIPTION =
205
+ 'Astryx, a design system and UI library, is available on this computer through the astryx command; use it when building or changing UI, choosing components, layout, styling or theming, working with a design system, or in any project that depends on @astryxdesign packages or mentions Astryx.';
206
+
207
+ /**
208
+ * A home-folder path as a person reads it: `~/.claude/skills/astryx`.
209
+ * @param {string} file
210
+ * @param {string} [home]
211
+ * @returns {string}
212
+ */
213
+ export function displayHomePath(file, home = os.homedir()) {
214
+ const rel = path.relative(home, file);
215
+ return rel && !rel.startsWith('..') && !path.isAbsolute(rel)
216
+ ? `~${path.sep}${rel}`
217
+ : file;
218
+ }
219
+
220
+ /**
221
+ * The skill file's absolute path.
222
+ * @param {string} [home]
223
+ * @returns {string}
224
+ */
225
+ export function computerSkillFile(home = os.homedir()) {
226
+ return path.join(home, SKILL_FOLDER, 'SKILL.md');
227
+ }
228
+
229
+ /**
230
+ * The absolute path of each link the table names, and whether it is wanted
231
+ * on this computer (its condition folder exists).
232
+ * @param {string} [home]
233
+ * @returns {Array<{path: string, wanted: boolean, mode: 'link' | 'copy'}>}
234
+ */
235
+ export function computerSkillLinks(home = os.homedir()) {
236
+ return SKILL_LINKS.map(({skills, when, mode}) => ({
237
+ path: path.join(home, skills, 'astryx'),
238
+ wanted: when == null || fs.existsSync(path.join(home, when)),
239
+ mode,
240
+ }));
241
+ }
242
+
243
+ /**
244
+ * The command the computer-level text tells agents to run: the host's door
245
+ * when one started this run, else `astryx`. An integration's `cliCommand`
246
+ * never applies: no project, so no integration, is in scope.
247
+ * @returns {string}
248
+ */
249
+ export function computerAgentCommand() {
250
+ return cliDoor() ?? COMPUTER_COMMAND;
251
+ }
252
+
253
+ /**
254
+ * The body lines, with `run` as the command and `install` as the step that
255
+ * installs it.
256
+ * @param {string} run
257
+ * @param {string} install
258
+ * @returns {string[]}
259
+ */
260
+ function bodyLines(run, install) {
261
+ return [
262
+ 'Astryx, a design system and UI library, is available on this computer.',
263
+ 'Use it when you build or change UI, pick components, or style or theme an app.',
264
+ `Its docs, components, templates, and themes are all reachable through \`${run}\`. Start with \`${run} search '<what you need>'\`.`,
265
+ `Then read \`${run} component <Name>\` or \`${run} docs <topic>\`, not the package's files.`,
266
+ "In a project, the project's version of Astryx answers automatically.",
267
+ // How to build, which discovery alone leaves to the agent's own page
268
+ // route: in a project, from the template, with the project agent docs'
269
+ // own workflow lines.
270
+ // A project that already installs Core gets the CLI at that version, as
271
+ // the CLI's own hint for that state says, never a newer CLI beside it.
272
+ `To build UI, work in a project folder (create one if there is none). Add \`@astryxdesign/core\` and \`@astryxdesign/cli\` to a new project or one without \`@astryxdesign/core\`; to a project that already has \`@astryxdesign/core\`, add \`@astryxdesign/cli\` at the same version. Run \`${run} init\` there and follow the agent docs it writes.`,
273
+ WORKFLOW_TITLE,
274
+ ...WORKFLOW_STEPS.map(step => step.replaceAll('`astryx ', `\`${run} `)),
275
+ `Before you deliver, re-read each file; ${SELF_CHECK_CSS}. Then build the project and deliver that build, not a description of it.`,
276
+ // A host's door is the host's to restore; only the global command has an
277
+ // install step to name.
278
+ ...(run === COMPUTER_COMMAND ? [computerRestoreLine(install)] : []),
279
+ ];
280
+ }
281
+
282
+ /**
283
+ * The command a skill file's text names: the one in its "Start with" line,
284
+ * or null when the text has none.
285
+ * @param {string} content
286
+ * @returns {string | null}
287
+ */
288
+ export function skillCommandOf(content) {
289
+ return /Start with `(.+?) search '/u.exec(content)?.[1] ?? null;
290
+ }
291
+
292
+ /**
293
+ * The full skill file.
294
+ * @param {{command?: string, install?: string}} [options]
295
+ * @returns {string}
296
+ */
297
+ export function renderComputerAgentText({
298
+ command = computerAgentCommand(),
299
+ install = COMPUTER_INSTALL_STEP,
300
+ } = {}) {
301
+ return withHash(
302
+ [
303
+ '---',
304
+ 'name: astryx',
305
+ // Quoted: the description holds `: ` and `;`, which strict YAML parsers
306
+ // can misread, and an agent tool that fails to parse drops the skill.
307
+ `description: ${JSON.stringify(SKILL_DESCRIPTION)}`,
308
+ '---',
309
+ '',
310
+ MARKER_START,
311
+ ...bodyLines(command, install),
312
+ MARKER_END,
313
+ '',
314
+ ].join('\n'),
315
+ );
316
+ }
317
+
318
+ /** The line, right after the start marker, that records the file's hash. */
319
+ const HASH_LINE = /^<!-- astryx-managed sha256:([0-9a-f]{64}) -->\n/mu;
320
+
321
+ /**
322
+ * The sha256 of a file's text without its hash line.
323
+ * @param {string} text
324
+ * @returns {string}
325
+ */
326
+ function hashOf(text) {
327
+ return createHash('sha256').update(text).digest('hex');
328
+ }
329
+
330
+ /**
331
+ * Insert the hash line after the start marker.
332
+ * @param {string} text a rendering without a hash line
333
+ * @returns {string}
334
+ */
335
+ function withHash(text) {
336
+ const at = text.indexOf(MARKER_START);
337
+ if (at === -1) return text;
338
+ const lineEnd = text.indexOf('\n', at);
339
+ const cut = lineEnd === -1 ? text.length : lineEnd + 1;
340
+ return `${text.slice(0, cut)}<!-- astryx-managed sha256:${hashOf(text)} -->\n${text.slice(cut)}`;
341
+ }
342
+
343
+ /**
344
+ * Whether a file with the markers is unedited: it carries a hash line, and
345
+ * the hash matches the rest of its text.
346
+ * @param {string} content
347
+ * @returns {boolean}
348
+ */
349
+ function isUnedited(content) {
350
+ const match = HASH_LINE.exec(content);
351
+ if (!match) return false;
352
+ return hashOf(content.replace(HASH_LINE, '')) === match[1];
353
+ }
354
+
355
+ /**
356
+ * What is at one path of the computer-level text.
357
+ * - `absent`: nothing there.
358
+ * - `current`: an Astryx file with exactly this release's rendering.
359
+ * - `differs`: an unedited Astryx file (its recorded hash matches) with
360
+ * another rendering, such as another release's.
361
+ * - `edited`: an Astryx file (it has the markers) whose text no longer
362
+ * matches its recorded hash, or that records none: a person changed it.
363
+ * Never changed.
364
+ * - `foreign`: a file without the Astryx markers, a symlink, or something
365
+ * other than a regular file. Never changed.
366
+ * - `linked`, `dangling` (a link only): a link to the skill folder, or one
367
+ * whose folder is missing (see linkStateOf).
368
+ * @typedef {'absent' | 'current' | 'differs' | 'edited' | 'foreign' | 'linked' | 'dangling'} ComputerAgentTextState
369
+ */
370
+
371
+ /**
372
+ * @param {string} content
373
+ * @returns {boolean}
374
+ */
375
+ function hasMarkers(content) {
376
+ const start = content.indexOf(MARKER_START);
377
+ return start !== -1 && content.indexOf(MARKER_END, start) !== -1;
378
+ }
379
+
380
+ /**
381
+ * The state of one path, read without following a symlinked file.
382
+ * @param {string} file
383
+ * @param {string} expected
384
+ * @returns {ComputerAgentTextState}
385
+ */
386
+ function stateOf(file, expected) {
387
+ /** @type {fs.Stats} */
388
+ let stat;
389
+ try {
390
+ stat = fs.lstatSync(file);
391
+ } catch {
392
+ return 'absent';
393
+ }
394
+ if (!stat.isFile()) return 'foreign';
395
+ let content;
396
+ try {
397
+ content = fs.readFileSync(file, 'utf8');
398
+ } catch {
399
+ return 'foreign';
400
+ }
401
+ if (!hasMarkers(content)) return 'foreign';
402
+ if (content === expected) return 'current';
403
+ return isUnedited(content) ? 'differs' : 'edited';
404
+ }
405
+
406
+ /**
407
+ * The state of one link target.
408
+ * - `linked`: a link to the skill folder.
409
+ * - `dangling`: a link to the skill folder, which is not there, as in a
410
+ * container that mounts only the agent's own folder.
411
+ * - `absent`: nothing there.
412
+ * - `differs`: a real folder holding only an unedited Astryx skill file, as a
413
+ * copy left it; replaced by the link.
414
+ * - `foreign`: anything else. Never changed.
415
+ * @param {string} at
416
+ * @param {string} folder the skill folder
417
+ * @returns {ComputerAgentTextState}
418
+ */
419
+ function linkStateOf(at, folder) {
420
+ /** @type {fs.Stats} */
421
+ let stat;
422
+ try {
423
+ stat = fs.lstatSync(at);
424
+ } catch {
425
+ return 'absent';
426
+ }
427
+ if (stat.isSymbolicLink()) {
428
+ const leads = pointsAt(at, folder);
429
+ if (leads == null) return 'foreign';
430
+ // A link the system cannot follow to the folder is broken, whatever its
431
+ // text says.
432
+ if (leads === 'names') return 'dangling';
433
+ return fs.existsSync(path.join(folder, 'SKILL.md')) ? 'linked' : 'dangling';
434
+ }
435
+ if (!stat.isDirectory()) return 'foreign';
436
+ return copyStateOf(at, '') === 'differs' ? 'differs' : 'foreign';
437
+ }
438
+
439
+ /**
440
+ * The state of a managed copy: a real folder at `at` holding only the skill
441
+ * file, read like the skill file itself.
442
+ * @param {string} at
443
+ * @param {string} expected
444
+ * @returns {ComputerAgentTextState}
445
+ */
446
+ function copyStateOf(at, expected) {
447
+ /** @type {fs.Stats} */
448
+ let stat;
449
+ try {
450
+ stat = fs.lstatSync(at);
451
+ } catch {
452
+ return 'absent';
453
+ }
454
+ if (!stat.isDirectory()) return 'foreign';
455
+ let names;
456
+ try {
457
+ names = fs.readdirSync(at);
458
+ } catch {
459
+ return 'foreign';
460
+ }
461
+ if (names.length === 0) return 'absent';
462
+ if (names.length !== 1 || names[0] !== 'SKILL.md') return 'foreign';
463
+ const state = stateOf(path.join(at, 'SKILL.md'), expected);
464
+ return state === 'current' && expected === '' ? 'differs' : state;
465
+ }
466
+
467
+ /**
468
+ * Where the link at `at` leads `folder`'s way, as the system follows it: a
469
+ * relative target resolves from the folder the link really sits in, which
470
+ * differs from its path when a folder above it is itself a link (such as
471
+ * `~/.claude` linked to a dotfiles folder). `reaches`: the system reaches
472
+ * `folder` (which may be missing). `names`: only the target's text, read from
473
+ * the link's path, names `folder`, as a link an earlier release made under a
474
+ * linked folder does; the system cannot follow it. Otherwise null.
475
+ * @param {string} at
476
+ * @param {string} folder
477
+ * @returns {'reaches' | 'names' | null}
478
+ */
479
+ function pointsAt(at, folder) {
480
+ try {
481
+ const text = fs.readlinkSync(at);
482
+ const followed = path.resolve(realPath(path.dirname(at)), text);
483
+ const wanted = realPath(folder);
484
+ if (samePath(followed.replace(/[\\/]+$/u, ''), wanted)) return 'reaches';
485
+ const named = path.resolve(path.dirname(at), text);
486
+ return samePath(named.replace(/[\\/]+$/u, ''), folder) ? 'names' : null;
487
+ } catch {
488
+ return null;
489
+ }
490
+ }
491
+
492
+ /**
493
+ * `p` with every link in it resolved, as far as it exists: a missing tail is
494
+ * kept as written.
495
+ * @param {string} p
496
+ * @returns {string}
497
+ */
498
+ function realPath(p) {
499
+ try {
500
+ return fs.realpathSync(p);
501
+ } catch {
502
+ const parent = path.dirname(p);
503
+ return parent === p ? p : path.join(realPath(parent), path.basename(p));
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Read-only: the state of the skill file against this release's rendering,
509
+ * then of each place the table wants on this computer, or that Astryx linked.
510
+ * @param {{home?: string, command?: string, links?: readonly string[]}} [options]
511
+ * `links`: the links Astryx made, from the settings file
512
+ * @returns {Array<{path: string, state: ComputerAgentTextState, link?: true}>}
513
+ */
514
+ export function inspectComputerAgentText({
515
+ home = os.homedir(),
516
+ command,
517
+ links = [],
518
+ } = {}) {
519
+ const expected = renderComputerAgentText({
520
+ command,
521
+ install: computerInstallStep({home, for: 'judge'}),
522
+ });
523
+ const folder = path.join(home, SKILL_FOLDER);
524
+ const made = new Set(links);
525
+ return [
526
+ {
527
+ path: computerSkillFile(home),
528
+ state: stateOf(computerSkillFile(home), expected),
529
+ },
530
+ ...computerSkillLinks(home)
531
+ .filter(place => place.wanted || made.has(place.path))
532
+ .map(place => {
533
+ const linkState = linkStateOf(place.path, folder);
534
+ const state =
535
+ place.mode === 'copy' && linkState !== 'linked'
536
+ ? copyStateOf(place.path, expected)
537
+ : linkState;
538
+ return {path: place.path, state, link: /** @type {const} */ (true)};
539
+ }),
540
+ ];
541
+ }
542
+
543
+ /**
544
+ * Write `content` to `file` without following a symlink planted at it.
545
+ * @param {string} file
546
+ * @param {string} content
547
+ */
548
+ function writeNoFollow(file, content) {
549
+ const flags =
550
+ fs.constants.O_WRONLY |
551
+ fs.constants.O_CREAT |
552
+ fs.constants.O_TRUNC |
553
+ (fs.constants.O_NOFOLLOW ?? 0);
554
+ const fd = fs.openSync(file, flags, 0o644);
555
+ try {
556
+ fs.writeFileSync(fd, content);
557
+ } finally {
558
+ fs.closeSync(fd);
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Why a path was kept: a person edited it, or Astryx did not create it.
564
+ * @param {ComputerAgentTextState} state
565
+ * @returns {'edited' | 'not-astryx'}
566
+ */
567
+ const keptReason = state => (state === 'edited' ? 'edited' : 'not-astryx');
568
+
569
+ /**
570
+ * Remove an unedited Astryx file, or a folder holding only one, and its
571
+ * emptied folder.
572
+ * @param {string} file
573
+ */
574
+ function removeFile(file) {
575
+ fs.unlinkSync(file);
576
+ try {
577
+ fs.rmdirSync(path.dirname(file));
578
+ } catch {
579
+ // The folder holds something else; leave it.
580
+ }
581
+ }
582
+
583
+ /**
584
+ * Link `at` to `folder`: a folder symlink with a relative target, which
585
+ * survives a moved or synced home folder, or a junction (absolute) on Windows.
586
+ * The target is relative to the folder the link really sits in, because the
587
+ * system resolves it from there: with `~/.claude` linked to
588
+ * `~/dotfiles/.claude`, a target worked out from `~/.claude/skills` would
589
+ * dangle. With no common base between the two, the target is absolute.
590
+ * @param {string} at
591
+ * @param {string} folder
592
+ */
593
+ function makeLink(at, folder) {
594
+ fs.mkdirSync(path.dirname(at), {recursive: true});
595
+ if (process.platform === 'win32') {
596
+ fs.symlinkSync(folder, at, 'junction');
597
+ return;
598
+ }
599
+ const realFolder = fs.realpathSync(folder);
600
+ const relative = path.relative(fs.realpathSync(path.dirname(at)), realFolder);
601
+ fs.symlinkSync(path.isAbsolute(relative) ? realFolder : relative, at, 'dir');
602
+ }
603
+
604
+ /**
605
+ * Write or refresh a managed copy of the skill file in the folder `at`.
606
+ * @param {string} at
607
+ * @param {string} expected
608
+ * @returns {import('../../api/init/init.type.mjs').InitUserFile}
609
+ */
610
+ function writeCopy(at, expected) {
611
+ const state = copyStateOf(at, expected);
612
+ if (state === 'current') return {path: at, action: 'unchanged'};
613
+ if (state === 'foreign' || state === 'edited') {
614
+ return {path: at, action: 'kept', reason: keptReason(state)};
615
+ }
616
+ try {
617
+ fs.mkdirSync(at, {recursive: true});
618
+ writeNoFollow(path.join(at, 'SKILL.md'), expected);
619
+ return {path: at, action: 'written'};
620
+ } catch {
621
+ return {path: at, action: 'kept', reason: 'not-astryx'};
622
+ }
623
+ }
624
+
625
+ /**
626
+ * Write or refresh the skill file, and link it into every folder the table
627
+ * wants on this computer. A file without the Astryx markers, an edited one, a
628
+ * symlinked one, and a link target holding anything Astryx did not create are
629
+ * kept as they are. Returns the receipt rows and the links that now point at
630
+ * the skill folder, for the settings file.
631
+ * @param {{home?: string, command?: string}} [options]
632
+ * @returns {{rows: import('../../api/init/init.type.mjs').InitUserFile[], links: string[]}}
633
+ */
634
+ export function writeComputerAgentText({home = os.homedir(), command} = {}) {
635
+ // Judged by the step the file records, so a file another copy of this
636
+ // release wrote stays as it is; a write records this copy's own step.
637
+ const judged = renderComputerAgentText({
638
+ command,
639
+ install: computerInstallStep({home, for: 'judge'}),
640
+ });
641
+ const file = computerSkillFile(home);
642
+ const folder = path.dirname(file);
643
+ /** @type {import('../../api/init/init.type.mjs').InitUserFile[]} */
644
+ const rows = [];
645
+ const state = stateOf(file, judged);
646
+ const expected =
647
+ state === 'current'
648
+ ? judged
649
+ : renderComputerAgentText({
650
+ command,
651
+ install: computerInstallStep({home, for: 'write'}),
652
+ });
653
+ if (state === 'foreign' || state === 'edited') {
654
+ rows.push({path: file, action: 'kept', reason: keptReason(state)});
655
+ } else if (state === 'current') {
656
+ rows.push({path: file, action: 'unchanged'});
657
+ } else {
658
+ fs.mkdirSync(folder, {recursive: true});
659
+ writeNoFollow(file, expected);
660
+ rows.push({path: file, action: 'written'});
661
+ }
662
+ /** @type {string[]} */
663
+ const links = [];
664
+ for (const place of computerSkillLinks(home)) {
665
+ if (!place.wanted) continue;
666
+ const at = place.path;
667
+ const linkState = linkStateOf(at, folder);
668
+ if (linkState === 'linked') {
669
+ links.push(at);
670
+ rows.push({path: at, action: 'unchanged'});
671
+ continue;
672
+ }
673
+ if (place.mode === 'copy') {
674
+ rows.push(writeCopy(at, expected));
675
+ continue;
676
+ }
677
+ if (linkState === 'foreign') {
678
+ rows.push({path: at, action: 'kept', reason: 'not-astryx'});
679
+ continue;
680
+ }
681
+ if (linkState === 'differs') removeFile(path.join(at, 'SKILL.md'));
682
+ // The skill folder exists now, so a link still dangling here is one
683
+ // whose target the system resolves from a linked folder above it: an
684
+ // Astryx link, remade so that it resolves.
685
+ if (linkState === 'dangling') fs.unlinkSync(at);
686
+ try {
687
+ makeLink(at, folder);
688
+ links.push(at);
689
+ rows.push({path: at, action: 'linked'});
690
+ } catch {
691
+ // A folder link that cannot be made (Windows without a junction, a
692
+ // file system without links) gets a managed copy instead.
693
+ rows.push(writeCopy(at, expected));
694
+ }
695
+ }
696
+ return {rows, links};
697
+ }
698
+
699
+ /**
700
+ * Remove the skill file and every link Astryx made that still points at its
701
+ * folder. A file without the Astryx markers, an edited one, or a symlinked
702
+ * one, and any link Astryx did not make, are kept.
703
+ * @param {{home?: string, links?: readonly string[]}} [options]
704
+ * @returns {import('../../api/init/init.type.mjs').InitUserFile[]}
705
+ */
706
+ export function removeComputerAgentText({
707
+ home = os.homedir(),
708
+ links = [],
709
+ } = {}) {
710
+ const file = computerSkillFile(home);
711
+ const folder = path.dirname(file);
712
+ /** @type {import('../../api/init/init.type.mjs').InitUserFile[]} */
713
+ const rows = [];
714
+ for (const at of links) {
715
+ const linkState = linkStateOf(at, folder);
716
+ if (linkState !== 'linked' && linkState !== 'dangling') continue;
717
+ try {
718
+ fs.unlinkSync(at);
719
+ rows.push({path: at, action: 'removed'});
720
+ } catch {
721
+ rows.push({path: at, action: 'kept', reason: 'not-astryx'});
722
+ }
723
+ }
724
+ // Managed copies are told apart by their markers and hash, like the file.
725
+ for (const place of computerSkillLinks(home)) {
726
+ if (copyStateOf(place.path, '') !== 'differs') continue;
727
+ removeFile(path.join(place.path, 'SKILL.md'));
728
+ rows.push({path: place.path, action: 'removed'});
729
+ }
730
+ const state = stateOf(file, '');
731
+ if (state === 'absent') {
732
+ rows.unshift({path: file, action: 'absent'});
733
+ } else if (state === 'foreign' || state === 'edited') {
734
+ rows.unshift({path: file, action: 'kept', reason: keptReason(state)});
735
+ } else {
736
+ removeFile(file);
737
+ rows.unshift({path: file, action: 'removed'});
738
+ }
739
+ return rows;
740
+ }
741
+
742
+ /**
743
+ * Whether the `astryx` a shell would run from this computer's search path is
744
+ * the Astryx CLI: the first `astryx` found is the one that runs.
745
+ * @param {{searchPath?: string, platform?: NodeJS.Platform}} [options]
746
+ * @returns {boolean}
747
+ */
748
+ export function astryxOnSearchPath(options = {}) {
749
+ return astryxCommandsOnSearchPath(options)[0]?.astryx ?? false;
750
+ }