@astryxdesign/cli 0.6.4-canary.f0355e3 → 0.6.4

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 (331) hide show
  1. package/README.md +96 -99
  2. package/api/build/build.doc.mjs +1 -6
  3. package/api/build/build.test.mjs +0 -22
  4. package/api/build/kit/kit.mjs +5 -44
  5. package/api/component/_adapter.d.mts +0 -25
  6. package/api/component/_adapter.mjs +5 -59
  7. package/api/component/component.d.mts +3 -6
  8. package/api/component/component.doc.mjs +17 -37
  9. package/api/component/component.mjs +9 -249
  10. package/api/component/component.type.d.mts +0 -25
  11. package/api/component/component.type.mjs +0 -44
  12. package/api/discover/_adapter.d.mts +6 -114
  13. package/api/discover/_adapter.mjs +17 -372
  14. package/api/discover/detail/detail.d.mts +6 -18
  15. package/api/discover/detail/detail.mjs +13 -67
  16. package/api/discover/detail/detail.test.mjs +0 -85
  17. package/api/discover/discover.d.mts +9 -3
  18. package/api/discover/discover.doc.mjs +18 -61
  19. package/api/discover/discover.mjs +36 -220
  20. package/api/discover/discover.test.mjs +2 -11
  21. package/api/discover/discover.type.d.mts +8 -147
  22. package/api/discover/discover.type.mjs +12 -102
  23. package/api/discover/list/list.d.mts +6 -20
  24. package/api/discover/list/list.mjs +12 -45
  25. package/api/discover/list/list.test.mjs +0 -46
  26. package/api/discover/search/search.d.mts +16 -18
  27. package/api/discover/search/search.mjs +56 -102
  28. package/api/discover/search/search.test.mjs +10 -144
  29. package/api/docs/_adapter.d.mts +3 -8
  30. package/api/docs/_adapter.mjs +6 -14
  31. package/api/docs/docOverlays.test.mjs +1 -27
  32. package/api/docs/docs.doc.mjs +2 -2
  33. package/api/docs/docs.test.mjs +243 -0
  34. package/api/docs/integration-tree.test.mjs +555 -0
  35. package/api/docs/integrationDocs.test.mjs +314 -0
  36. package/api/doctor/doctor.d.mts +3 -8
  37. package/api/doctor/doctor.doc.mjs +8 -17
  38. package/api/doctor/doctor.mjs +9 -90
  39. package/api/doctor/doctor.test.mjs +10 -122
  40. package/api/doctor/doctor.type.d.mts +1 -1
  41. package/api/doctor/doctor.type.mjs +1 -1
  42. package/api/gap-report/gap-report.doc.mjs +10 -19
  43. package/api/hook/hook.doc.mjs +3 -6
  44. package/api/index.d.mts +2 -1
  45. package/api/index.mjs +5 -5
  46. package/api/init/init.doc.mjs +12 -17
  47. package/api/integration/add-helpers.d.mts +2 -5
  48. package/api/integration/add-helpers.mjs +9 -36
  49. package/api/integration/add-theme.mjs +1 -22
  50. package/api/integration/add-theme.test.mjs +0 -34
  51. package/api/integration/authoring-checks.mjs +2 -2
  52. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  53. package/api/integration/pack-check.mjs +9 -82
  54. package/api/integration/pack-check.test.mjs +0 -90
  55. package/api/integration/pack-check.type.mjs +1 -1
  56. package/api/json/assertResponse.doc.mjs +1 -1
  57. package/api/json/index.ts +1 -0
  58. package/api/json/isError.doc.mjs +1 -1
  59. package/api/layout/_adapter.d.mts +34 -0
  60. package/api/layout/_adapter.mjs +148 -0
  61. package/api/layout/check/check.d.mts +16 -0
  62. package/api/layout/check/check.mjs +40 -0
  63. package/api/layout/expand/expand.d.mts +22 -0
  64. package/api/layout/expand/expand.mjs +155 -0
  65. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  66. package/api/layout/grammar/grammar.d.mts +13 -0
  67. package/api/layout/grammar/grammar.mjs +87 -0
  68. package/api/layout/layout.d.mts +6 -0
  69. package/api/layout/layout.mjs +17 -0
  70. package/api/layout/layout.test.mjs +297 -0
  71. package/api/layout/layout.type.d.mts +89 -0
  72. package/api/layout/layout.type.mjs +103 -0
  73. package/api/layout/layoutCheck.doc.d.mts +11 -0
  74. package/api/layout/layoutCheck.doc.mjs +85 -0
  75. package/api/layout/layoutExpand.doc.d.mts +11 -0
  76. package/api/layout/layoutExpand.doc.mjs +107 -0
  77. package/api/layout/layoutGrammar.doc.d.mts +11 -0
  78. package/api/layout/layoutGrammar.doc.mjs +57 -0
  79. package/api/search/search.d.mts +1 -27
  80. package/api/search/search.doc.mjs +2 -2
  81. package/api/search/search.mjs +16 -228
  82. package/api/search/search.test.mjs +512 -0
  83. package/api/swizzle/swizzle.doc.mjs +5 -7
  84. package/api/template/copy/copy.mjs +1 -1
  85. package/api/template/copy/copy.test.mjs +0 -9
  86. package/api/template/template-integration.test.mjs +65 -1
  87. package/api/template/template.doc.mjs +1 -2
  88. package/api/template/template.mjs +1 -1
  89. package/api/theme/add/add.mjs +25 -17
  90. package/api/theme/add/add.staging.test.mjs +23 -40
  91. package/api/theme/build/build.family.test.mjs +12 -7
  92. package/api/theme/build/build.mjs +18 -8
  93. package/api/theme/generateTonalPalette.doc.mjs +2 -1
  94. package/api/theme/listThemes.doc.mjs +1 -1
  95. package/api/theme/themeAdd.doc.mjs +10 -9
  96. package/api/theme/themeBuild.doc.mjs +13 -13
  97. package/api/theme/themeList.doc.mjs +1 -1
  98. package/api/theme/themeListAvailable.doc.mjs +1 -2
  99. package/api/theme/themePaletteGenerate.doc.mjs +8 -15
  100. package/api/theme/themeTargets.doc.mjs +2 -3
  101. package/api/theme/themeTemplate.doc.mjs +1 -2
  102. package/api/upgrade/run/run.mjs +4 -6
  103. package/api/upgrade/upgrade.doc.mjs +22 -24
  104. package/api/upgrade/upgrade.type.mjs +2 -2
  105. package/assets/codemods/__tests__/runner.test.mjs +1 -3
  106. package/assets/codemods/integration-runner.mjs +3 -3
  107. package/assets/codemods/runner.mjs +4 -5
  108. package/assets/docs/README.md +2 -4
  109. package/assets/docs/browser-support.doc.mjs +11 -11
  110. package/assets/docs/color.doc.mjs +2 -8
  111. package/assets/docs/elevation.doc.mjs +4 -6
  112. package/assets/docs/getting-started.doc.mjs +16 -5
  113. package/assets/docs/icons.doc.mjs +21 -2
  114. package/assets/docs/illustrations.doc.mjs +15 -7
  115. package/assets/docs/internationalization.doc.mjs +5 -7
  116. package/assets/docs/layout.doc.dense.mjs +82 -130
  117. package/assets/docs/layout.doc.mjs +77 -133
  118. package/assets/docs/migration.doc.mjs +21 -19
  119. package/assets/docs/motion.doc.mjs +3 -16
  120. package/assets/docs/principles.doc.dense.mjs +5 -5
  121. package/assets/docs/principles.doc.mjs +0 -8
  122. package/assets/docs/principles.doc.zh.mjs +6 -6
  123. package/assets/docs/shape.doc.mjs +3 -8
  124. package/assets/docs/spacing.doc.mjs +2 -7
  125. package/assets/docs/styling-libraries.doc.mjs +2 -6
  126. package/assets/docs/styling.doc.mjs +23 -19
  127. package/assets/docs/theme.doc.dense.mjs +18 -58
  128. package/assets/docs/theme.doc.mjs +46 -56
  129. package/assets/docs/theme.doc.zh.mjs +8 -9
  130. package/assets/docs/tokens.doc.dense.mjs +2 -2
  131. package/assets/docs/tokens.doc.mjs +8 -389
  132. package/assets/docs/tokens.doc.zh.mjs +2 -2
  133. package/assets/docs/tree/integrations.doc.mjs +451 -25
  134. package/assets/docs/tree/integrations.test.mjs +62 -0
  135. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  136. package/assets/docs/typography.doc.mjs +4 -24
  137. package/assets/docs/working-with-ai.doc.mjs +22 -30
  138. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  139. package/authoring/config/config.doc.mjs +2 -10
  140. package/authoring/config/parse.d.mts +0 -2
  141. package/authoring/config/parse.mjs +0 -19
  142. package/authoring/config/parse.test.mjs +0 -8
  143. package/authoring/config/type.ts +2 -13
  144. package/authoring/doctypes/_schema.d.mts +2 -3
  145. package/authoring/doctypes/_schema.mjs +0 -6
  146. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  147. package/authoring/doctypes/base/type.ts +2 -4
  148. package/authoring/doctypes/command/command.doc.mjs +1 -1
  149. package/authoring/doctypes/command/type.ts +1 -1
  150. package/authoring/doctypes/component/component.doc.mjs +0 -6
  151. package/authoring/doctypes/component/type.ts +0 -8
  152. package/authoring/doctypes/reference/reference.doc.mjs +0 -7
  153. package/authoring/doctypes/reference/type.ts +0 -5
  154. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  155. package/authoring/doctypes/template/template.doc.mjs +1 -1
  156. package/authoring/doctypes/template/type.ts +2 -2
  157. package/authoring/index.d.mts +0 -1
  158. package/authoring/index.d.ts +0 -10
  159. package/authoring/index.mjs +0 -1
  160. package/authoring/integration/integration.doc.mjs +10 -12
  161. package/clients/cli/command-result-coverage.test.mjs +7 -7
  162. package/clients/cli/commands/component/index.mjs +55 -152
  163. package/clients/cli/commands/component-ownership.test.mjs +0 -89
  164. package/clients/cli/commands/component.doc.mjs +9 -27
  165. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  166. package/clients/cli/commands/discover.doc.mjs +9 -53
  167. package/clients/cli/commands/discover.mjs +118 -393
  168. package/clients/cli/commands/docs.doc.mjs +1 -1
  169. package/clients/cli/commands/docs.mjs +17 -60
  170. package/clients/cli/commands/docs.test.mjs +294 -0
  171. package/clients/cli/commands/doctor-integration-docs.doc.mjs +2 -3
  172. package/clients/cli/commands/doctor-integration.test.mjs +0 -53
  173. package/clients/cli/commands/doctor.doc.mjs +1 -3
  174. package/clients/cli/commands/doctor.mjs +5 -49
  175. package/clients/cli/commands/gap-report.doc.mjs +9 -10
  176. package/clients/cli/commands/init.doc.mjs +6 -9
  177. package/clients/cli/commands/integration-add.doc.mjs +9 -9
  178. package/clients/cli/commands/integration-authoring.test.mjs +10 -61
  179. package/clients/cli/commands/integration-pack.doc.mjs +9 -5
  180. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  181. package/clients/cli/commands/integration.doc.mjs +4 -4
  182. package/clients/cli/commands/integration.mjs +43 -74
  183. package/clients/cli/commands/layout-check.doc.mjs +65 -0
  184. package/clients/cli/commands/layout-expand.doc.mjs +83 -0
  185. package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
  186. package/clients/cli/commands/layout.doc.mjs +34 -0
  187. package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
  188. package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
  189. package/clients/cli/commands/layout.mjs +275 -0
  190. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  191. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  192. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  193. package/clients/cli/commands/manifest.doc.mjs +1 -1
  194. package/clients/cli/commands/search.doc.mjs +3 -10
  195. package/clients/cli/commands/search.mjs +2 -21
  196. package/clients/cli/commands/search.test.mjs +4 -21
  197. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  198. package/clients/cli/commands/template.doc.mjs +1 -1
  199. package/clients/cli/commands/text-json-parity.test.mjs +16 -5
  200. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  201. package/clients/cli/commands/theme-palette-generate.doc.mjs +2 -3
  202. package/clients/cli/commands/theme-palette.doc.mjs +2 -1
  203. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  204. package/clients/cli/commands/theme.doc.mjs +1 -2
  205. package/clients/cli/commands/upgrade.doc.mjs +3 -62
  206. package/clients/cli/index.mjs +10 -28
  207. package/clients/cli/lib/define-command.mjs +4 -28
  208. package/clients/cli/lib/define-command.test.mjs +0 -54
  209. package/clients/cli/lib/exit-codes.test.mjs +9 -18
  210. package/clients/cli/lib/json-shim.mjs +14 -24
  211. package/clients/cli/lib/json-shim.test.mjs +20 -6
  212. package/clients/cli/lib/manifest.mjs +13 -18
  213. package/clients/cli/lib/manifest.test.mjs +2 -5
  214. package/foundation/agent-docs/agent-docs.mjs +1 -1
  215. package/foundation/agent-docs/agent-docs.test.mjs +1159 -0
  216. package/foundation/discovery/authoring-self-docs.mjs +0 -1
  217. package/foundation/discovery/authoring-self-docs.test.mjs +2 -6
  218. package/foundation/discovery/cli-self-docs.mjs +2 -16
  219. package/foundation/discovery/cli-self-docs.test.mjs +0 -20
  220. package/foundation/discovery/docs-discovery.mjs +1 -5
  221. package/foundation/discovery/docs-discovery.test.mjs +0 -21
  222. package/foundation/discovery/docs-section-key.d.mts +1 -1
  223. package/foundation/discovery/docs-section-key.mjs +1 -1
  224. package/foundation/discovery/template-adapter.mjs +1 -1
  225. package/foundation/doc-compiler/doc-loads.test.mjs +14 -3
  226. package/foundation/doc-compiler/tree.d.mts +0 -4
  227. package/foundation/doc-compiler/tree.mjs +1 -6
  228. package/foundation/doc-compiler/tree.test.mjs +598 -0
  229. package/foundation/integrations/cli-requirement.d.mts +6 -26
  230. package/foundation/integrations/cli-requirement.mjs +11 -46
  231. package/foundation/integrations/cli-requirement.test.mjs +2 -7
  232. package/foundation/integrations/contribution-inventory.mjs +1 -1
  233. package/foundation/integrations/integrations.d.mts +1 -14
  234. package/foundation/integrations/integrations.mjs +1 -41
  235. package/foundation/integrations/integrations.test.mjs +0 -31
  236. package/foundation/response/error-codes.doc.mjs +8 -6
  237. package/foundation/response/error-codes.test.mjs +5 -30
  238. package/foundation/response/response-types.doc.d.mts +3 -4
  239. package/foundation/response/response-types.doc.mjs +27 -40
  240. package/foundation/response/response-types.doc.test.mjs +0 -23
  241. package/foundation/response/response.doc.mjs +10 -11
  242. package/foundation/xle/browser.d.mts +3 -3
  243. package/foundation/xle/browser.mjs +3 -3
  244. package/foundation/xle/expand.mjs +2 -2
  245. package/foundation/xle/parse.mjs +1 -1
  246. package/foundation/xle/print.mjs +2 -2
  247. package/foundation/xle/splice.mjs +1 -1
  248. package/package.json +9 -9
  249. package/api/discover/_adapter.test.mjs +0 -215
  250. package/api/discover/_catalog-view.d.mts +0 -115
  251. package/api/discover/_catalog-view.mjs +0 -203
  252. package/api/discover/_catalog-view.test.mjs +0 -128
  253. package/api/discover/detail/item/item.d.mts +0 -26
  254. package/api/discover/detail/item/item.mjs +0 -78
  255. package/api/discover/detail/item/item.test.mjs +0 -73
  256. package/api/integration/pack-check.lifecycle-output.test.mjs +0 -107
  257. package/api/theme/add/add.rollback.test.mjs +0 -158
  258. package/api/theme/build/build.rollback.test.mjs +0 -148
  259. package/api/upgrade/run/files-changed.test.mjs +0 -111
  260. package/assets/codemods/file-count.test.mjs +0 -163
  261. package/assets/docs/tree/add-a-component.doc.mjs +0 -75
  262. package/assets/docs/tree/add-a-theme.doc.mjs +0 -85
  263. package/assets/docs/tree/add-a-topic.doc.mjs +0 -144
  264. package/assets/docs/tree/agent-guidance.doc.mjs +0 -138
  265. package/assets/docs/tree/block-template.doc.mjs +0 -130
  266. package/assets/docs/tree/build-the-template.doc.mjs +0 -28
  267. package/assets/docs/tree/building-blocks.doc.mjs +0 -46
  268. package/assets/docs/tree/check-your-docs.doc.mjs +0 -137
  269. package/assets/docs/tree/checks.doc.mjs +0 -119
  270. package/assets/docs/tree/codemods.doc.mjs +0 -147
  271. package/assets/docs/tree/component-family.doc.mjs +0 -113
  272. package/assets/docs/tree/component-imports.doc.mjs +0 -69
  273. package/assets/docs/tree/component-lookups.doc.mjs +0 -149
  274. package/assets/docs/tree/components.doc.mjs +0 -23
  275. package/assets/docs/tree/configuration.doc.mjs +0 -23
  276. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +0 -182
  277. package/assets/docs/tree/define-the-theme.doc.mjs +0 -118
  278. package/assets/docs/tree/describe-the-component.doc.mjs +0 -57
  279. package/assets/docs/tree/docs.doc.mjs +0 -21
  280. package/assets/docs/tree/document-the-template.doc.mjs +0 -28
  281. package/assets/docs/tree/document-the-theme.doc.mjs +0 -68
  282. package/assets/docs/tree/export-template-assets.doc.mjs +0 -147
  283. package/assets/docs/tree/extend-or-replace.doc.mjs +0 -103
  284. package/assets/docs/tree/fonts-and-assets.doc.mjs +0 -106
  285. package/assets/docs/tree/generate-a-palette.doc.mjs +0 -66
  286. package/assets/docs/tree/grade-template-with-agent.doc.mjs +0 -105
  287. package/assets/docs/tree/help.doc.mjs +0 -16
  288. package/assets/docs/tree/links.doc.mjs +0 -98
  289. package/assets/docs/tree/package-and-test.doc.mjs +0 -32
  290. package/assets/docs/tree/page-template.doc.mjs +0 -71
  291. package/assets/docs/tree/publishing.doc.mjs +0 -111
  292. package/assets/docs/tree/quick-start.doc.mjs +0 -272
  293. package/assets/docs/tree/replace-a-core-component.doc.mjs +0 -104
  294. package/assets/docs/tree/replace-a-core-template.doc.mjs +0 -172
  295. package/assets/docs/tree/sections-and-placement.doc.mjs +0 -108
  296. package/assets/docs/tree/see-it-in-an-app.doc.mjs +0 -59
  297. package/assets/docs/tree/ship.doc.mjs +0 -16
  298. package/assets/docs/tree/short-and-findable.doc.mjs +0 -108
  299. package/assets/docs/tree/single-component.doc.mjs +0 -165
  300. package/assets/docs/tree/start-a-template.doc.mjs +0 -143
  301. package/assets/docs/tree/subcomponent.doc.mjs +0 -115
  302. package/assets/docs/tree/template-assets.doc.mjs +0 -64
  303. package/assets/docs/tree/template-doc-overview.doc.mjs +0 -109
  304. package/assets/docs/tree/template-fonts.doc.mjs +0 -102
  305. package/assets/docs/tree/template-grading-rubric.doc.mjs +0 -452
  306. package/assets/docs/tree/template-icons.doc.mjs +0 -97
  307. package/assets/docs/tree/template-images-media.doc.mjs +0 -127
  308. package/assets/docs/tree/template-styles.doc.mjs +0 -93
  309. package/assets/docs/tree/templates.doc.mjs +0 -34
  310. package/assets/docs/tree/test-in-an-app.doc.mjs +0 -115
  311. package/assets/docs/tree/test-template-in-app.doc.mjs +0 -128
  312. package/assets/docs/tree/themes.doc.mjs +0 -39
  313. package/assets/docs/tree/troubleshooting.doc.mjs +0 -149
  314. package/assets/docs/tree/upgrading.doc.mjs +0 -103
  315. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +0 -51
  316. package/assets/docs/tree/verify-packed-template.doc.mjs +0 -77
  317. package/assets/docs/tree/versioning.doc.mjs +0 -161
  318. package/assets/docs/tree/write-good-templates.doc.mjs +0 -64
  319. package/assets/docs/tree/write-the-template-file.doc.mjs +0 -154
  320. package/authoring/discover/discover.doc.d.mts +0 -13
  321. package/authoring/discover/discover.doc.mjs +0 -138
  322. package/authoring/discover/parse.d.mts +0 -24
  323. package/authoring/discover/parse.mjs +0 -128
  324. package/authoring/discover/parse.test.mjs +0 -124
  325. package/authoring/discover/type.ts +0 -87
  326. package/clients/cli/commands/component-batch.test.mjs +0 -341
  327. package/clients/cli/commands/discover.sources.test.mjs +0 -267
  328. package/clients/cli/commands/integration-verify.doc.mjs +0 -22
  329. package/clients/cli/lib/parse-error-format.test.mjs +0 -81
  330. package/foundation/response/batch.type.d.mts +0 -33
  331. package/foundation/response/batch.type.mjs +0 -34
@@ -102,7 +102,6 @@ export const doc = {
102
102
  type: 'string',
103
103
  description:
104
104
  'Directory to discover templates and resolve the target path from.',
105
- default: 'process.cwd()',
106
105
  },
107
106
  ],
108
107
  returns: [
@@ -135,7 +134,7 @@ export const doc = {
135
134
  throws: [
136
135
  {
137
136
  code: 'ERR_UNKNOWN_TEMPLATE',
138
- when: 'the named template does not exist, or options.skeleton is set without a name',
137
+ when: 'the named template does not exist, or --skeleton is run without a name',
139
138
  },
140
139
  {
141
140
  code: 'ERR_AMBIGUOUS_TEMPLATE',
@@ -8,7 +8,7 @@
8
8
  * the requested one, and routes to a leaf (list/show/skeleton/copy/cdn). The shared
9
9
  * discovery/IO + cross-command helpers live in `foundation/discovery/template-adapter.mjs` and are
10
10
  * RE-EXPORTED here so external import paths (`api/template/template.mjs`) —
11
- * used by component, search, init, discover, Doctor integration, and
11
+ * used by component, layout, search, init, discover, Doctor integration, and
12
12
  * lib/project — keep resolving unchanged.
13
13
  *
14
14
  * @position api/template — the template dispatcher + barrel; leaves live under
@@ -15,7 +15,6 @@ import {
15
15
  import {AstryxError} from '../../error.mjs';
16
16
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
17
17
  import {listAvailableThemes, findTheme} from '../_adapter.mjs';
18
- import {applyWrites} from '../../integration/add-helpers.mjs';
19
18
  // Scaffolded files must not carry our repo boilerplate into a consumer's tree.
20
19
  import {stripCopyrightHeader} from '../../../foundation/text/copyright-header.mjs';
21
20
 
@@ -137,28 +136,37 @@ export async function themeAdd(slug, options = {}) {
137
136
  }
138
137
  }
139
138
 
140
- // One transaction: every file publishes, or every replaced file gets its
141
- // previous bytes back and every created file is removed. mkdir is inside the
142
- // try so a failure (e.g. an ancestor is a file → EEXIST/ENOTDIR) surfaces as
143
- // a stable ERR_WRITE_FAILED rather than leaking a raw fs errno + absolute path.
139
+ // Stage to temp files then rename, rolling back partials on failure so a
140
+ // failed write never leaves a half-written theme. mkdir is inside the try so
141
+ // a failure (e.g. an ancestor is a file → EEXIST/ENOTDIR) surfaces as a
142
+ // stable ERR_WRITE_FAILED rather than leaking a raw fs errno + absolute path.
143
+ const staged = [];
144
144
  try {
145
145
  fs.mkdirSync(resolvedDir, {recursive: true});
146
- const plans = writes.map(w => {
147
- fs.mkdirSync(path.dirname(w.dest), {recursive: true});
148
- // Confine again once the directories exist: one may have been swapped
149
- // for a link since the destination was first checked.
146
+ for (const w of writes) {
150
147
  const dest = assertWithin(w.name, resolvedDir, {
151
148
  label: `theme destination for ${w.name}`,
152
149
  });
153
- return {
154
- path: dest,
155
- contents: scaffoldContents(fs.readFileSync(w.src)),
156
- createOnly: !overwrite,
157
- };
158
- });
159
- applyWrites(plans);
150
+ fs.mkdirSync(path.dirname(dest), {recursive: true});
151
+ // The staging file is an output path too: confine it, and never write
152
+ // through an entry that already exists under its name.
153
+ const tmp = assertWithin(`${w.name}.${process.pid}.tmp`, resolvedDir, {
154
+ label: `theme staging file for ${w.name}`,
155
+ });
156
+ fs.writeFileSync(tmp, scaffoldContents(fs.readFileSync(w.src)), {flag: 'wx'});
157
+ staged.push({tmp, dest});
158
+ }
159
+ for (const s of staged) {
160
+ fs.renameSync(s.tmp, s.dest);
161
+ }
160
162
  } catch (err) {
161
- if (err instanceof AstryxError) throw err;
163
+ for (const s of staged) {
164
+ try {
165
+ fs.rmSync(s.tmp, {force: true});
166
+ } catch {
167
+ /* best-effort */
168
+ }
169
+ }
162
170
  if (err instanceof PathSafetyError) {
163
171
  throw new AstryxError(
164
172
  err.message,
@@ -6,15 +6,14 @@ import * as os from 'node:os';
6
6
  import * as path from 'node:path';
7
7
  import {themeAdd} from './add.mjs';
8
8
  import {listThemes} from '../_adapter.mjs';
9
+ import {isErrorCode} from '../../../foundation/response/error-codes.mjs';
9
10
 
10
11
  let tmpDir;
11
12
  let outsideDir;
12
13
 
13
14
  beforeEach(() => {
14
15
  tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-themeadd-staging-'));
15
- outsideDir = fs.mkdtempSync(
16
- path.join(os.tmpdir(), 'astryx-themeadd-outside-'),
17
- );
16
+ outsideDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-themeadd-outside-'));
18
17
  });
19
18
 
20
19
  afterEach(() => {
@@ -23,61 +22,45 @@ afterEach(() => {
23
22
  });
24
23
 
25
24
  /**
26
- * Where `theme add` writes the first file of `slug`.
25
+ * Put a symlink where `theme add` stages the first file of `slug`.
27
26
  * @param {string} slug
27
+ * @param {string} target
28
28
  */
29
- function firstDestination(slug) {
29
+ function plantStagingLink(slug, target) {
30
30
  const theme = listThemes().find(entry => entry.slug === slug);
31
31
  if (!theme) throw new Error(`missing bundled theme ${slug}`);
32
- const dest = path.join(tmpDir, 'src', 'themes', slug, theme.files[0]);
32
+ const first = theme.files[0];
33
+ const dest = path.join(tmpDir, 'src', 'themes', slug, first);
33
34
  fs.mkdirSync(path.dirname(dest), {recursive: true});
35
+ fs.symlinkSync(target, `${dest}.${process.pid}.tmp`);
34
36
  return dest;
35
37
  }
36
38
 
37
- // Staging names are unpredictable and created exclusively, so an entry planted
38
- // at a predictable name beside the destination is never written through.
39
39
  describe('themeAdd staging writes stay inside the project', () => {
40
- it('never writes through a link planted at a predictable staging name', async () => {
40
+ it('refuses a staging path that links outside the project', async () => {
41
41
  const victim = path.join(outsideDir, 'victim.txt');
42
42
  fs.writeFileSync(victim, 'outside\n');
43
- const dest = firstDestination('stone');
44
- fs.symlinkSync(victim, `${dest}.${process.pid}.tmp`);
45
-
46
- await themeAdd('stone', {cwd: tmpDir});
43
+ const dest = plantStagingLink('stone', victim);
47
44
 
45
+ await expect(themeAdd('stone', {cwd: tmpDir})).rejects.toMatchObject({
46
+ code: 'ERR_PATH_TRAVERSAL',
47
+ });
48
48
  expect(fs.readFileSync(victim, 'utf-8')).toBe('outside\n');
49
- expect(fs.lstatSync(dest).isFile()).toBe(true);
49
+ expect(fs.existsSync(dest)).toBe(false);
50
50
  });
51
51
 
52
- it('never creates a file through a dangling link at a predictable staging name', async () => {
52
+ it('never creates a file through a dangling staging link', async () => {
53
53
  const victim = path.join(outsideDir, 'created.txt');
54
- fs.symlinkSync(victim, `${firstDestination('stone')}.${process.pid}.tmp`);
55
-
56
- await themeAdd('stone', {cwd: tmpDir});
57
-
58
- expect(fs.existsSync(victim)).toBe(false);
59
- });
60
-
61
- it('refuses to replace a destination that links outside the project', async () => {
62
- const victim = path.join(outsideDir, 'victim.txt');
63
- fs.writeFileSync(victim, 'outside\n');
64
- const dest = firstDestination('stone');
65
- fs.symlinkSync(victim, dest);
54
+ plantStagingLink('stone', victim);
66
55
 
67
- await expect(
68
- themeAdd('stone', {cwd: tmpDir, overwrite: true}),
69
- ).rejects.toMatchObject({code: 'ERR_PATH_TRAVERSAL'});
70
- expect(fs.readFileSync(victim, 'utf-8')).toBe('outside\n');
71
- expect(fs.lstatSync(dest).isSymbolicLink()).toBe(true);
72
- });
56
+ let error;
57
+ try {
58
+ await themeAdd('stone', {cwd: tmpDir});
59
+ } catch (caught) {
60
+ error = caught;
61
+ }
73
62
 
74
- it('never creates a file through a dangling destination link', async () => {
75
- const victim = path.join(outsideDir, 'created.txt');
76
- fs.symlinkSync(victim, firstDestination('stone'));
77
-
78
- await expect(themeAdd('stone', {cwd: tmpDir})).rejects.toMatchObject({
79
- code: 'ERR_PATH_TRAVERSAL',
80
- });
63
+ expect(isErrorCode(error?.code)).toBe(true);
81
64
  expect(fs.existsSync(victim)).toBe(false);
82
65
  });
83
66
  });
@@ -251,18 +251,23 @@ describe('themeBuildFamily()', () => {
251
251
  const dir = makeDir();
252
252
  const files = writeFamily(dir);
253
253
  const outputDir = path.join(dir, 'themes');
254
- // A directory where the JS output goes fails its staging after the CSS
255
- // output was already staged.
256
- fs.mkdirSync(path.join(outputDir, `${FAMILY_KEY}.js`));
254
+ const blockedTmp = path.join(
255
+ outputDir,
256
+ `${FAMILY_KEY}.js.${process.pid}.tmp`,
257
+ );
258
+ fs.mkdirSync(blockedTmp);
257
259
 
258
260
  await expect(build(dir, files)).rejects.toThrow(
259
261
  /Failed to write theme outputs/,
260
262
  );
261
- expect(fs.existsSync(path.join(outputDir, `${FAMILY_KEY}.css`))).toBe(false);
262
- expect(fs.existsSync(path.join(outputDir, `${FAMILY_KEY}.d.ts`))).toBe(false);
263
+ for (const output of OUTPUTS) {
264
+ expect(fs.existsSync(path.join(outputDir, output))).toBe(false);
265
+ }
263
266
  expect(
264
- fs.readdirSync(outputDir).filter(name => name.includes('.tmp-')),
265
- ).toEqual([]);
267
+ fs.existsSync(
268
+ path.join(outputDir, `${FAMILY_KEY}.css.${process.pid}.tmp`),
269
+ ),
270
+ ).toBe(false);
266
271
  });
267
272
 
268
273
  it('rejects invalid graphs and keys before touching an existing trio', async () => {
@@ -53,7 +53,6 @@ import {
53
53
  } from '../../../foundation/fs/path-safety.mjs';
54
54
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
55
55
  import {AstryxError} from '../../error.mjs';
56
- import {applyWrites} from '../../integration/add-helpers.mjs';
57
56
  import {logger} from '../../logger.mjs';
58
57
  import {loadComponentDoc} from '../../../foundation/discovery/component-loader.mjs';
59
58
  import {
@@ -225,15 +224,26 @@ function staleBuildOutputs(writes, cwd) {
225
224
  /** @param {Array<{dest: string, content: string}>} writes */
226
225
  function writeBuildOutputs(writes) {
227
226
  if (writes.length === 0) return;
227
+ /** @type {Array<{tmp: string, dest: string}>} */
228
+ const staged = [];
228
229
  try {
229
- applyWrites(
230
- writes.map(write => ({
231
- path: write.dest,
232
- contents: write.content,
233
- createOnly: false,
234
- })),
235
- );
230
+ fs.mkdirSync(path.dirname(writes[0].dest), {recursive: true});
231
+ for (const write of writes) {
232
+ const tmp = `${write.dest}.${process.pid}.tmp`;
233
+ fs.writeFileSync(tmp, write.content);
234
+ staged.push({tmp, dest: write.dest});
235
+ }
236
+ for (const stagedWrite of staged) {
237
+ fs.renameSync(stagedWrite.tmp, stagedWrite.dest);
238
+ }
236
239
  } catch (error) {
240
+ for (const stagedWrite of staged) {
241
+ try {
242
+ fs.rmSync(stagedWrite.tmp, {force: true});
243
+ } catch {
244
+ // Best effort: the command still fails and never reports success.
245
+ }
246
+ }
237
247
  const message = `Failed to write theme outputs: ${/** @type {Error} */ (error).message}`;
238
248
  throw new AstryxError(message, undefined, ERROR_CODES.ERR_WRITE_FAILED);
239
249
  }
@@ -29,7 +29,7 @@ export const doc = {
29
29
  name: 'input',
30
30
  type: 'TonalPaletteGenerationInput',
31
31
  description:
32
- 'Families and seeds plus optional modes, shared stops, anchors, vibrancy from 0 to 100 (default 50), and neutral profile.',
32
+ 'Families and seeds plus optional modes, shared stops, anchors, vibrancy from 0 to 100 (default 50), and neutral profile. Only generate an accent family when one is explicitly requested; clarify whether an ambiguous accent means one theme value or a tonal family.',
33
33
  required: true,
34
34
  },
35
35
  ],
@@ -72,5 +72,6 @@ export const doc = {
72
72
  code: "generateTonalPalette({stops: [12.5, 50], families: [{id: 'blue', seed: '#0074e2'}]});",
73
73
  },
74
74
  ],
75
+ command: 'theme palette generate',
75
76
  related: ['themePaletteGenerate'],
76
77
  };
@@ -13,7 +13,7 @@ export const doc = {
13
13
  displayName: 'listThemes()',
14
14
  summary: 'Read the CLI bundled-theme descriptors.',
15
15
  description:
16
- "Synchronously returns the themes bundled with the CLI, including each one's entry file, export name and file list. Bundled themes only; use themeListAvailable() to include themes from installed integrations.",
16
+ 'Reads the typed same-stem descriptors under templates/themes and returns normalized entries synchronously. This low-level helper keeps its historical bundled-only contract; project-aware themeList() and themeAdd() also discover source themes from installed integrations.',
17
17
  importPath: '@astryxdesign/cli/api',
18
18
  signature: 'listThemes(): BundledTheme[]',
19
19
  keywords: ['theme', 'themes', 'descriptor', 'bundled', 'adapter', 'list'],
@@ -44,7 +44,6 @@ export const doc = {
44
44
  type: 'string',
45
45
  description:
46
46
  'Project directory used for integration discovery and target paths.',
47
- default: 'process.cwd()',
48
47
  },
49
48
  {
50
49
  name: 'options.package',
@@ -58,6 +57,11 @@ export const doc = {
58
57
  description:
59
58
  'Copy receipt with slug, displayName, maintained flag, owner package, outputDir, entry, exportName, and files.',
60
59
  },
60
+ {
61
+ type: 'theme.list',
62
+ description:
63
+ 'The CLI list affordance routes a bare `astryx theme add` or `--list` to themeListAvailable() and returns every available theme with its owner.',
64
+ },
61
65
  ],
62
66
  throws: [
63
67
  {
@@ -70,10 +74,7 @@ export const doc = {
70
74
  when: 'the selected installed package has a blocking integration or theme-descriptor error',
71
75
  },
72
76
  {code: 'ERR_PATH_TRAVERSAL', when: 'the target path escapes cwd'},
73
- {
74
- code: 'ERR_NO_SOURCE',
75
- when: 'the bundled theme descriptors cannot be read, or a theme file to copy is missing',
76
- },
77
+ {code: 'ERR_NO_SOURCE', when: 'a theme file to copy is missing'},
77
78
  {
78
79
  code: 'ERR_FILE_EXISTS',
79
80
  when: 'a destination exists and overwrite is not set',
@@ -81,12 +82,12 @@ export const doc = {
81
82
  {code: 'ERR_WRITE_FAILED', when: 'writing files fails'},
82
83
  ],
83
84
  examples: [
84
- {label: 'Copy a bundled theme', code: "await themeAdd('butter');"},
85
+ {label: 'Copy a bundled theme', code: "await themeAdd('ocean');"},
85
86
  {
86
- label: 'Name the owner package and the destination',
87
- code: "await themeAdd('butter', {package: '@astryxdesign/cli', targetPath: 'src/brand-theme'});",
87
+ label: 'Copy an integration theme',
88
+ code: "await themeAdd('ocean', {package: '@acme/themes'});",
88
89
  },
89
90
  ],
90
91
  command: 'theme add',
91
- related: ['themeListAvailable', 'themeTemplate', 'listThemes'],
92
+ related: ['themeList', 'listThemes'],
92
93
  };
@@ -14,13 +14,14 @@ export const doc = {
14
14
  name: 'themeBuild',
15
15
  namespace: 'cli/api',
16
16
  displayName: 'themeBuild()',
17
- summary:
18
- 'Compile a defineTheme() file to scoped CSS, a JS module, and type declarations, or check committed outputs for drift in CI.',
17
+ summary: 'Compile a defineTheme file to CSS + JS + type declarations.',
19
18
  description:
20
- 'The compiler behind `astryx theme build`. Reads a file that calls defineTheme() and ' +
21
- 'writes a scoped CSS file, a JS module that re-exports the built theme, and a .d.ts ' +
22
- '(plus an optional .variants.d.ts when the theme adds custom prop values). It uses ' +
23
- "@astryxdesign/core's own generator, so the CSS matches what the <Theme> runtime emits. " +
19
+ 'The compiler behind `astryx theme build`. Reads a file that calls defineTheme() and, ' +
20
+ "via @astryxdesign/core's shared generator (the single source of truth, so the build " +
21
+ 'emits the exact CSS the <Theme> runtime does), writes a scoped CSS file, a JS module ' +
22
+ 'that re-exports the built theme, and a .d.ts (plus an optional .variants.d.ts when the ' +
23
+ 'theme adds custom prop values). When another build step emits the icon registry, ' +
24
+ '{iconsSpecifier} declares the fully specified module path for the generated JS import. ' +
24
25
  'With {check: true} it writes nothing and instead compares ' +
25
26
  'each output against disk, returning the drift: the CI guard for committed, generated theme CSS.',
26
27
  importPath: '@astryxdesign/cli/api',
@@ -47,7 +48,7 @@ export const doc = {
47
48
  name: 'options.out',
48
49
  type: 'string',
49
50
  description:
50
- 'Override the output CSS path. The .js, .d.ts and any .variants.d.ts are written in the same directory, named after the theme (<name>.js), not after the CSS file. A relative path must stay within cwd.',
51
+ 'Override the output CSS path; the sibling .js and .d.ts derive from it. A relative path must stay within cwd.',
51
52
  },
52
53
  {
53
54
  name: 'options.check',
@@ -60,14 +61,13 @@ export const doc = {
60
61
  name: 'options.iconsSpecifier',
61
62
  type: 'string',
62
63
  description:
63
- 'Override the import specifier of the icon registry in the generated JS module, for example ./icons.mjs. Takes effect only when the theme sets icons: to a named import; when omitted, the source specifier is kept.',
64
+ 'Override the icon-registry import specifier in the generated JS module, for example ./icons.mjs. When omitted, the source specifier is preserved.',
64
65
  },
65
66
  {
66
67
  name: 'ctx.cwd',
67
68
  type: 'string',
68
69
  description:
69
- 'Directory the theme file, a relative out path, and the returned output paths resolve against.',
70
- default: 'process.cwd()',
70
+ 'Directory the theme file and @astryxdesign/core resolve against.',
71
71
  },
72
72
  ],
73
73
  returns: [
@@ -79,7 +79,7 @@ export const doc = {
79
79
  {
80
80
  type: 'theme.build.check',
81
81
  description:
82
- 'The {check: true} receipt: theme name, an upToDate flag, the stale outputs (each {path, reason: "missing" | "outdated"}), and the full list of checked paths. Writes nothing. Resolves to null, like a normal build, when the theme produces no CSS.',
82
+ 'The {check: true} receipt: theme name, an upToDate flag, the stale outputs (each {path, reason: "missing" | "outdated"}), and the full list of checked paths. Writes nothing.',
83
83
  },
84
84
  ],
85
85
  throws: [
@@ -102,7 +102,7 @@ export const doc = {
102
102
  },
103
103
  {
104
104
  code: 'ERR_CORE_INCOMPATIBLE',
105
- when: 'the installed @astryxdesign/core does not export generateAdaptationCSS and the theme either declares ordered adaptations or has lineage whose adaptation use could not be observed (upgrade core)',
105
+ when: 'the selected theme carries ordered-adaptation intent but the installed @astryxdesign/core does not export generateAdaptationCSS (upgrade core)',
106
106
  },
107
107
  {
108
108
  code: 'ERR_WRITE_FAILED',
@@ -124,5 +124,5 @@ export const doc = {
124
124
  },
125
125
  ],
126
126
  command: 'theme build',
127
- related: ['themeTemplate', 'themeAdd', 'themeListAvailable', 'listThemes'],
127
+ related: ['themeAdd', 'themeList', 'listThemes'],
128
128
  };
@@ -13,7 +13,7 @@ export const doc = {
13
13
  displayName: 'themeList()',
14
14
  summary: 'List themes bundled with this CLI build.',
15
15
  description:
16
- 'Synchronous list of the themes bundled with the CLI. It does not include themes from installed integrations; use themeListAvailable() for the list `astryx theme list` shows.',
16
+ 'Projects the bundled typed theme descriptors into a synchronous theme.list envelope. This preserves the original programmatic API contract. The CLI command uses themeListAvailable() so installed integrations also appear.',
17
17
  importPath: '@astryxdesign/cli/api',
18
18
  signature: 'themeList(): ThemeListResponse',
19
19
  keywords: ['theme', 'list', 'themes', 'bundled', 'available'],
@@ -13,7 +13,7 @@ export const doc = {
13
13
  displayName: 'themeListAvailable()',
14
14
  summary: 'List bundled and installed integration themes.',
15
15
  description:
16
- 'Lists the bundled themes plus source themes from integrations installed in cwd, each with its owner package. If the project configuration cannot be read, it falls back to the bundled themes.',
16
+ 'Loads Project for the requested directory, combines the CLI bundle with source themes from installed integrations, and projects each entry with its owner package. An unreadable project configuration degrades to the bundled descriptors.',
17
17
  importPath: '@astryxdesign/cli/api',
18
18
  signature:
19
19
  'themeListAvailable(options?: {cwd?: string, package?: string}): Promise<ThemeListResponse>',
@@ -24,7 +24,6 @@ export const doc = {
24
24
  type: 'string',
25
25
  description:
26
26
  'Project directory whose installed integrations contribute themes.',
27
- default: 'process.cwd()',
28
27
  },
29
28
  {
30
29
  name: 'options.package',
@@ -27,34 +27,30 @@ export const doc = {
27
27
  {
28
28
  name: 'configPath',
29
29
  type: 'string',
30
- description:
31
- 'Path to a JSON file holding a TonalPaletteGenerationInput (the object generateTonalPalette() takes), resolved within cwd.',
30
+ description: 'JSON generation request, resolved within cwd.',
32
31
  required: true,
33
32
  },
34
33
  {
35
34
  name: 'options.out',
36
35
  type: 'string',
37
36
  description:
38
- 'Where to write the candidate: a path ending in .ts (a TypeScript module) or .json. A sibling <name>.receipt.json is written next to it.',
37
+ 'Optional candidate JSON destination. A sibling .receipt.json path is derived from it.',
39
38
  },
40
39
  {
41
40
  name: 'options.preview',
42
41
  type: 'string',
43
- description:
44
- 'Optional path, ending in .html, for a self-contained HTML review page.',
42
+ description: 'Optional path for a self-contained HTML review artifact.',
45
43
  },
46
44
  {
47
45
  name: 'options.overwrite',
48
46
  type: 'boolean',
49
- description:
50
- "Replace existing candidate, receipt and preview files. Without it, if any target exists, nothing is written and the result has written: false, reason: 'exists'.",
47
+ description: 'Replace existing candidate and receipt files.',
51
48
  default: 'false',
52
49
  },
53
50
  {
54
51
  name: 'ctx.cwd',
55
52
  type: 'string',
56
53
  description: 'Directory used to resolve the input and output paths.',
57
- default: 'process.cwd()',
58
54
  },
59
55
  ],
60
56
  returns: [
@@ -68,20 +64,17 @@ export const doc = {
68
64
  {code: 'ERR_FILE_NOT_FOUND', when: 'the config file does not exist'},
69
65
  {
70
66
  code: 'ERR_PALETTE_GENERATION',
71
- when: 'the config is not valid JSON; the request, seed, stop layout, mode or anchor is invalid; out does not end in .ts or .json; or preview does not end in .html',
67
+ when: 'the request, seed, stop layout, mode, or anchor constraint is invalid',
72
68
  },
73
69
  {
74
70
  code: 'ERR_PATH_TRAVERSAL',
75
71
  when: 'an input or output path escapes cwd, or output would replace input',
76
72
  },
77
- {
78
- code: 'ERR_WRITE_FAILED',
79
- when: 'the candidate, receipt or preview file cannot be written',
80
- },
73
+ {code: 'ERR_WRITE_FAILED', when: 'the candidate pair cannot be written'},
81
74
  ],
82
75
  examples: [
83
76
  {
84
- label: 'Generate a candidate without writing files',
77
+ label: 'Preview a candidate',
85
78
  code: "themePaletteGenerate('palette.config.json');",
86
79
  },
87
80
  {
@@ -90,5 +83,5 @@ export const doc = {
90
83
  },
91
84
  ],
92
85
  command: 'theme palette generate',
93
- related: ['generateTonalPalette', 'themeBuild', 'themeTemplate'],
86
+ related: ['themeBuild', 'themeTemplate'],
94
87
  };
@@ -22,7 +22,7 @@ export const doc = {
22
22
  'Same source as the Theming table `astryx component <Name>` prints ' +
23
23
  '(the component docs), so the list cannot drift from the components, and `theme build` ' +
24
24
  'validates overrides against this exact set. A filter naming a component gives that ' +
25
- "component's set; anything else is a case-insensitive substring search over each target's key, class and component.",
25
+ "component's set; anything else is a substring search over the keys.",
26
26
  importPath: '@astryxdesign/cli/api',
27
27
  signature:
28
28
  'themeTargets(filter?: string, ctx?: {cwd?: string}): Promise<ThemeTargetsResponse>',
@@ -41,14 +41,13 @@ export const doc = {
41
41
  name: 'filter',
42
42
  type: 'string',
43
43
  description:
44
- 'A component name (exact, case-insensitive), or a substring of a target key, class or component. Omit for the whole surface.',
44
+ 'A component name (exact, case-insensitive) or a substring of a target key. Omit for the whole surface.',
45
45
  },
46
46
  {
47
47
  name: 'ctx.cwd',
48
48
  type: 'string',
49
49
  description:
50
50
  "Directory the project's @astryxdesign/core is resolved from.",
51
- default: 'process.cwd()',
52
51
  },
53
52
  ],
54
53
  returns: [
@@ -43,7 +43,6 @@ export const doc = {
43
43
  name: 'options.cwd',
44
44
  type: 'string',
45
45
  description: 'Directory the target path resolves against.',
46
- default: 'process.cwd()',
47
46
  },
48
47
  ],
49
48
  returns: [
@@ -65,5 +64,5 @@ export const doc = {
65
64
  },
66
65
  ],
67
66
  command: 'theme template',
68
- related: ['themeAdd', 'themeBuild', 'themeListAvailable', 'themeTargets'],
67
+ related: ['themeAdd', 'themeBuild', 'themeList'],
69
68
  };
@@ -114,7 +114,7 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
114
114
  // Resolve the source dir against the API's cwd (not process.cwd()) so a
115
115
  // programmatic caller in another directory scans the right tree. Confine it to
116
116
  // cwd: --apply rewrites files in place, so a `..`-escaping or out-of-tree
117
- // absolute --path must be rejected (parity with template/theme/swizzle,
117
+ // absolute --path must be rejected (parity with template/theme/swizzle/layout,
118
118
  // and this is the most destructive command). allowAbsolute permits an absolute
119
119
  // path that still resolves inside cwd.
120
120
  let path_;
@@ -437,11 +437,9 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
437
437
 
438
438
  const registryResult = await reconcileCompositions();
439
439
 
440
- // A file a core codemod AND an integration codemod both changed is one file.
441
- const mergedFilesChanged = new Set([
442
- ...(coreResult?.changedFiles ?? []),
443
- ...(integrationResult?.changedFiles ?? []),
444
- ]).size;
440
+ const mergedFilesChanged =
441
+ (coreResult?.totalFilesChanged ?? 0) +
442
+ (integrationResult?.totalFilesChanged ?? 0);
445
443
  const mergedTransformsApplied =
446
444
  (coreResult?.totalTransformsApplied ?? 0) +
447
445
  (integrationResult?.totalTransformsApplied ?? 0);