@astryxdesign/cli 0.7.2 → 0.7.3-canary.06872b4

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 (212) hide show
  1. package/README.md +99 -66
  2. package/api/build/_adapter.d.mts +58 -0
  3. package/api/build/_adapter.mjs +115 -3
  4. package/api/build/blocks/blocks.d.mts +42 -0
  5. package/api/build/blocks/blocks.mjs +169 -0
  6. package/api/build/blocks/blocks.test.mjs +108 -0
  7. package/api/build/blocks/blocks.weights.json +1 -0
  8. package/api/build/build.mjs +25 -6
  9. package/api/build/kit/answer.d.mts +14 -0
  10. package/api/build/kit/answer.mjs +85 -0
  11. package/api/build/kit/answer.test.mjs +38 -0
  12. package/api/build/kit/kind.d.mts +25 -0
  13. package/api/build/kit/kind.mjs +170 -0
  14. package/api/build/kit/kit.d.mts +2 -4
  15. package/api/build/kit/kit.mjs +8 -398
  16. package/api/build/kit/rank.d.mts +3 -32
  17. package/api/build/kit/rank.mjs +25 -342
  18. package/api/build/kit/rank.test.mjs +2 -1
  19. package/api/build/kit/signals.d.mts +60 -0
  20. package/api/build/kit/signals.mjs +284 -0
  21. package/api/build/kit/start.d.mts +32 -0
  22. package/api/build/kit/start.mjs +163 -0
  23. package/api/build/kit/words.d.mts +72 -0
  24. package/api/build/kit/words.mjs +202 -0
  25. package/api/build/tree.d.mts +20 -0
  26. package/api/build/tree.mjs +52 -0
  27. package/api/build/tree.test.mjs +177 -0
  28. package/api/build/tree.weights.json +1 -0
  29. package/api/component/component.mjs +16 -2
  30. package/api/component/component.test.mjs +15 -0
  31. package/api/discover/_adapter.mjs +36 -9
  32. package/api/discover/_adapter.test.mjs +42 -0
  33. package/api/docs/_adapter.mjs +17 -5
  34. package/api/docs/docs.test.mjs +7 -1
  35. package/api/doctor/doctor.mjs +86 -12
  36. package/api/doctor/doctor.test.mjs +28 -3
  37. package/api/init/init.doc.mjs +20 -6
  38. package/api/init/init.test.mjs +18 -2
  39. package/api/init/init.type.d.mts +3 -3
  40. package/api/init/init.type.mjs +3 -3
  41. package/api/init/remove/remove.mjs +17 -4
  42. package/api/init/remove/remove.test.mjs +114 -2
  43. package/api/init/run/run.mjs +69 -6
  44. package/api/integration/authoring-checks.mjs +83 -9
  45. package/api/integration/pack-check.mjs +123 -4
  46. package/api/integration/pack-check.test.mjs +87 -0
  47. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  48. package/api/integration/validate-integration-gaps.test.mjs +1964 -0
  49. package/api/integration/validate-integration.d.mts +11 -2
  50. package/api/integration/validate-integration.mjs +226 -32
  51. package/api/integration/validate-integration.type.d.mts +58 -1
  52. package/api/integration/validate-integration.type.mjs +45 -2
  53. package/api/integration/validateIntegration.doc.mjs +1 -1
  54. package/api/search/ORDERING.md +28 -0
  55. package/api/search/_adapter.d.mts +66 -2
  56. package/api/search/_adapter.mjs +118 -10
  57. package/api/search/_candidate-score.d.mts +40 -0
  58. package/api/search/_candidate-score.mjs +238 -0
  59. package/api/search/_cards.json +6098 -0
  60. package/api/search/_compound.d.mts +50 -0
  61. package/api/search/_compound.mjs +170 -0
  62. package/api/search/_doc-searcher.d.mts +4 -0
  63. package/api/search/_doc-searcher.mjs +23 -0
  64. package/api/search/_doc-searcher.test.mjs +36 -0
  65. package/api/search/_doc-searcher.weights.json +1 -0
  66. package/api/search/_doc-text.d.mts +30 -0
  67. package/api/search/_doc-text.mjs +87 -0
  68. package/api/search/_entries.d.mts +52 -0
  69. package/api/search/_entries.mjs +158 -0
  70. package/api/search/_features.d.mts +35 -0
  71. package/api/search/_features.mjs +155 -0
  72. package/api/search/_gather-components.d.mts +26 -0
  73. package/api/search/_gather-components.mjs +284 -0
  74. package/api/search/_gather-docs.d.mts +20 -0
  75. package/api/search/_gather-docs.mjs +254 -0
  76. package/api/search/_gather-hooks.d.mts +12 -0
  77. package/api/search/_gather-hooks.mjs +52 -0
  78. package/api/search/_gather-templates.d.mts +25 -0
  79. package/api/search/_gather-templates.mjs +107 -0
  80. package/api/search/_lexical.d.mts +135 -0
  81. package/api/search/_lexical.mjs +351 -0
  82. package/api/search/_main-searcher.d.mts +19 -0
  83. package/api/search/_main-searcher.mjs +115 -0
  84. package/api/search/_main-searcher.test.mjs +106 -0
  85. package/api/search/_main-searcher.weights.json +1 -0
  86. package/api/search/_module-doc.d.mts +32 -0
  87. package/api/search/_module-doc.mjs +78 -0
  88. package/api/search/_order.d.mts +32 -0
  89. package/api/search/_order.mjs +162 -0
  90. package/api/search/_order.test.mjs +72 -0
  91. package/api/search/_priority.d.mts +50 -0
  92. package/api/search/_priority.mjs +139 -0
  93. package/api/search/_query-words.d.mts +25 -0
  94. package/api/search/_query-words.mjs +92 -0
  95. package/api/search/_query.d.mts +65 -0
  96. package/api/search/_query.mjs +176 -0
  97. package/api/search/_result.d.mts +97 -0
  98. package/api/search/_result.mjs +150 -0
  99. package/api/search/_score.d.mts +60 -0
  100. package/api/search/_score.mjs +283 -0
  101. package/api/search/_synonyms.d.mts +21 -0
  102. package/api/search/_synonyms.mjs +67 -0
  103. package/api/search/_template-searcher.d.mts +4 -0
  104. package/api/search/_template-searcher.mjs +23 -0
  105. package/api/search/_template-searcher.test.mjs +40 -0
  106. package/api/search/_template-searcher.weights.json +1 -0
  107. package/api/search/_titles.d.mts +33 -0
  108. package/api/search/_titles.mjs +65 -0
  109. package/api/search/_words.d.mts +64 -0
  110. package/api/search/_words.mjs +157 -0
  111. package/api/search/search.d.mts +22 -305
  112. package/api/search/search.doc.mjs +7 -5
  113. package/api/search/search.mjs +53 -2483
  114. package/api/search/search.type.d.mts +6 -1
  115. package/api/search/search.type.mjs +6 -1
  116. package/api/search/tree.d.mts +15 -0
  117. package/api/search/tree.mjs +40 -0
  118. package/api/search/tree.test.mjs +66 -0
  119. package/api/search/tree.weights.json +1 -0
  120. package/api/theme/build/build.packed-old-core.test.mjs +1 -0
  121. package/api/upgrade/_adapter.d.mts +57 -9
  122. package/api/upgrade/_adapter.mjs +198 -28
  123. package/api/upgrade/registry/registry.d.mts +10 -4
  124. package/api/upgrade/registry/registry.mjs +33 -16
  125. package/api/upgrade/registry/registry.test.mjs +85 -0
  126. package/api/upgrade/run/run.d.mts +4 -2
  127. package/api/upgrade/run/run.mjs +13 -2
  128. package/api/upgrade/run/run.test.mjs +417 -3
  129. package/api/upgrade/upgrade.doc.mjs +10 -5
  130. package/api/upgrade/upgrade.mjs +13 -2
  131. package/api/upgrade/upgrade.test.mjs +194 -0
  132. package/api/upgrade/upgrade.type.d.mts +5 -3
  133. package/api/upgrade/upgrade.type.mjs +8 -6
  134. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  135. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  136. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  137. package/assets/docs/tree/api.doc.mjs +6 -0
  138. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  139. package/assets/docs/tree/template-grading-rubric.doc.mjs +16 -37
  140. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  141. package/assets/docs/working-with-ai.doc.mjs +6 -2
  142. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  143. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  144. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  145. package/authoring/debug/parse.d.mts +1 -1
  146. package/clients/cli/bin/astryx.mjs +7 -78
  147. package/clients/cli/commands/doctor.mjs +30 -0
  148. package/clients/cli/commands/doctor.test.mjs +65 -0
  149. package/clients/cli/commands/init.behavior.test.mjs +230 -1
  150. package/clients/cli/commands/init.doc.mjs +28 -9
  151. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  152. package/clients/cli/commands/init.mjs +2 -2
  153. package/clients/cli/commands/outside-project.test.mjs +2 -1
  154. package/clients/cli/commands/setup-nudge.test.mjs +61 -0
  155. package/clients/cli/commands/upgrade.doc.mjs +4 -2
  156. package/clients/cli/commands/upgrade.mjs +9 -2
  157. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  158. package/clients/cli/index.mjs +19 -1
  159. package/clients/cli/run.mjs +108 -0
  160. package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
  161. package/foundation/agent-docs/agent-doc-state.mjs +35 -8
  162. package/foundation/agent-docs/agent-docs.d.mts +100 -12
  163. package/foundation/agent-docs/agent-docs.mjs +1010 -99
  164. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  165. package/foundation/agent-docs/agent-docs.test.mjs +678 -35
  166. package/foundation/config/project.mjs +20 -22
  167. package/foundation/config/project.test.mjs +56 -0
  168. package/foundation/discovery/component-discovery.d.mts +37 -0
  169. package/foundation/discovery/component-discovery.mjs +80 -5
  170. package/foundation/discovery/component-loader.d.mts +1 -1
  171. package/foundation/discovery/docs-discovery.mjs +14 -1
  172. package/foundation/discovery/module-exports.d.mts +41 -0
  173. package/foundation/discovery/module-exports.mjs +253 -0
  174. package/foundation/discovery/template-adapter.mjs +18 -1
  175. package/foundation/discovery/template-needs.mjs +1 -44
  176. package/foundation/discovery/theme-discovery.mjs +147 -3
  177. package/foundation/doc-compiler/doc-loads.test.mjs +8 -0
  178. package/foundation/doc-compiler/read.mjs +59 -1
  179. package/foundation/doc-compiler/read.test.mjs +11 -2
  180. package/foundation/fs/paths.d.mts +70 -8
  181. package/foundation/fs/paths.mjs +556 -38
  182. package/foundation/fs/paths.test.mjs +430 -54
  183. package/foundation/integrations/autolink.d.mts +2 -1
  184. package/foundation/integrations/autolink.mjs +4 -13
  185. package/foundation/integrations/computer-integrations.d.mts +6 -3
  186. package/foundation/integrations/computer-integrations.mjs +38 -9
  187. package/foundation/integrations/contribution-inventory.d.mts +15 -20
  188. package/foundation/integrations/contribution-inventory.mjs +41 -5
  189. package/foundation/integrations/integrations.d.mts +27 -1
  190. package/foundation/integrations/integrations.mjs +61 -2
  191. package/foundation/integrations/validate-contributions.d.mts +115 -0
  192. package/foundation/integrations/validate-contributions.mjs +1168 -13
  193. package/foundation/release/handoff.d.mts +23 -4
  194. package/foundation/release/handoff.mjs +175 -8
  195. package/foundation/release/run-context.mjs +14 -0
  196. package/foundation/response/response-types.doc.mjs +1 -1
  197. package/foundation/route/route.d.mts +285 -0
  198. package/foundation/route/route.mjs +324 -0
  199. package/foundation/route/route.test.mjs +153 -0
  200. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  201. package/foundation/runtime/locked-versions.d.mts +32 -0
  202. package/foundation/runtime/locked-versions.mjs +65 -0
  203. package/foundation/runtime/package-root.d.mts +91 -0
  204. package/foundation/runtime/package-root.mjs +174 -0
  205. package/foundation/runtime/package-set.d.mts +62 -0
  206. package/foundation/runtime/package-set.mjs +130 -0
  207. package/foundation/runtime/package-set.test.mjs +432 -0
  208. package/host/door-conformance.test.mjs +274 -0
  209. package/host/index.d.ts +116 -0
  210. package/host/index.mjs +295 -0
  211. package/host/index.test.mjs +302 -0
  212. package/package.json +14 -9
@@ -24,6 +24,7 @@
24
24
  import {list} from './list/list.mjs';
25
25
  import {registryUpgrade} from './registry/registry.mjs';
26
26
  import {run} from './run/run.mjs';
27
+ import {relativeToOrigin, scopeToProject} from './_adapter.mjs';
27
28
  import {isValidSemver} from '../../foundation/env/semver.mjs';
28
29
  import {getCliInvocation} from '../../foundation/env/package-manager.mjs';
29
30
  import {isOutsideProject} from '../../foundation/env/project-scope.mjs';
@@ -88,7 +89,11 @@ export async function upgrade(options = {}, {cwd = process.cwd()} = {}) {
88
89
  logger.log('Aborted\n');
89
90
  throw new AstryxError(msg, undefined, ERROR_CODES.ERR_INVALID_ARGUMENT);
90
91
  }
91
- return registryUpgrade(options, {cwd});
92
+ const scoped = scopeToProject(options, cwd);
93
+ return registryUpgrade(scoped.options, {
94
+ cwd: scoped.cwd,
95
+ origin: scoped.origin,
96
+ });
92
97
  }
93
98
 
94
99
  if (!options.list && !options.from) {
@@ -112,5 +117,11 @@ export async function upgrade(options = {}, {cwd = process.cwd()} = {}) {
112
117
  return list();
113
118
  }
114
119
 
115
- return run(options, {cwd});
120
+ // A run from a subfolder upgrades the project the folder belongs to, and
121
+ // names files relative to the folder it ran in.
122
+ const scoped = scopeToProject(options, cwd);
123
+ return relativeToOrigin(
124
+ await run(scoped.options, {cwd: scoped.cwd, origin: scoped.origin}),
125
+ scoped,
126
+ );
116
127
  }
@@ -246,6 +246,200 @@ describe('upgrade() — receipts', () => {
246
246
  expect(content).not.toMatch(/Astryx v0\.0\.1 ·/);
247
247
  });
248
248
 
249
+ it('leaves the block of a project at the top of a home settings folder, saying why and not pointing at init', async () => {
250
+ const saved = {
251
+ HOME: process.env.HOME,
252
+ USERPROFILE: process.env.USERPROFILE,
253
+ };
254
+ process.env.HOME = tmpDir;
255
+ process.env.USERPROFILE = tmpDir;
256
+ const warned = [];
257
+ const warn = vi
258
+ .spyOn(console, 'error')
259
+ .mockImplementation((...a) => warned.push(a.join(' ')));
260
+ logger.setSilent(false);
261
+ try {
262
+ const project = path.join(tmpDir, '.dotfiles');
263
+ fs.mkdirSync(project, {recursive: true});
264
+ writePkg(project, {'@astryxdesign/core': '0.0.15'});
265
+ writeInstalledCore(project, '0.0.15');
266
+ writeAgentBlock(project, 'AGENTS.md', '0.0.1');
267
+
268
+ const res = await upgrade({from: '0.0.15', apply: true}, {cwd: project});
269
+
270
+ expect(res.data.agentDocs.action).toBe('error');
271
+ expect(res.data.agentDocs.refreshed).toBe(false);
272
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8')).toMatch(
273
+ /Astryx v0\.0\.1 ·/,
274
+ );
275
+ const text = warned.join('\n');
276
+ expect(text).toContain(
277
+ `Agent docs not refreshed: ${fs.realpathSync(project)} is the home folder or a settings folder in it, which every project's agents read. To keep agent docs, move the project into a folder of its own; otherwise update or remove the Astryx block there by hand.`,
278
+ );
279
+ expect(text).not.toContain('init --features agents');
280
+ } finally {
281
+ logger.setSilent(true);
282
+ warn.mockRestore();
283
+ for (const [key, value] of Object.entries(saved)) {
284
+ if (value === undefined) delete process.env[key];
285
+ else process.env[key] = value;
286
+ }
287
+ }
288
+ });
289
+
290
+ it('with no block, in a project at the top of a home settings folder, says why instead of pointing at init', async () => {
291
+ const saved = {
292
+ HOME: process.env.HOME,
293
+ USERPROFILE: process.env.USERPROFILE,
294
+ };
295
+ process.env.HOME = tmpDir;
296
+ process.env.USERPROFILE = tmpDir;
297
+ const warned = [];
298
+ const warn = vi
299
+ .spyOn(console, 'error')
300
+ .mockImplementation((...a) => warned.push(a.join(' ')));
301
+ logger.setSilent(false);
302
+ try {
303
+ const project = path.join(tmpDir, '.dotfiles');
304
+ fs.mkdirSync(project, {recursive: true});
305
+ writePkg(project, {'@astryxdesign/core': '0.0.15'});
306
+ writeInstalledCore(project, '0.0.15');
307
+
308
+ const res = await upgrade({from: '0.0.15'}, {cwd: project});
309
+
310
+ expect(res.data.agentDocs.action).toBe('error');
311
+ const text = warned.join('\n');
312
+ expect(text).toContain(
313
+ `Agent docs not installed: ${fs.realpathSync(project)} is the home folder or a settings folder in it, which every project's agents read. To have agent docs, move the project into a folder of its own, or add the Astryx block to its agent file by hand.`,
314
+ );
315
+ expect(text).not.toContain('init --features agents');
316
+ } finally {
317
+ logger.setSilent(true);
318
+ warn.mockRestore();
319
+ for (const [key, value] of Object.entries(saved)) {
320
+ if (value === undefined) delete process.env[key];
321
+ else process.env[key] = value;
322
+ }
323
+ }
324
+ });
325
+
326
+ it('a dry run in a project at the top of a home settings folder reports the refusal, not a refresh to apply', async () => {
327
+ const saved = {
328
+ HOME: process.env.HOME,
329
+ USERPROFILE: process.env.USERPROFILE,
330
+ };
331
+ process.env.HOME = tmpDir;
332
+ process.env.USERPROFILE = tmpDir;
333
+ const warned = [];
334
+ const warn = vi
335
+ .spyOn(console, 'error')
336
+ .mockImplementation((...a) => warned.push(a.join(' ')));
337
+ logger.setSilent(false);
338
+ try {
339
+ const project = path.join(tmpDir, '.dotfiles');
340
+ fs.mkdirSync(project, {recursive: true});
341
+ writePkg(project, {'@astryxdesign/core': '0.0.15'});
342
+ writeInstalledCore(project, '0.0.15');
343
+ writeAgentBlock(project, 'AGENTS.md', '0.0.1');
344
+
345
+ const res = await upgrade({from: '0.0.15'}, {cwd: project});
346
+
347
+ expect(res.data.agentDocs.action).toBe('error');
348
+ expect(res.data.agentDocs.refreshed).toBe(false);
349
+ const text = warned.join('\n');
350
+ expect(text).toContain(
351
+ `Agent docs not refreshed: ${fs.realpathSync(project)} is the home folder or a settings folder in it`,
352
+ );
353
+ expect(text).not.toContain('--apply to refresh');
354
+ expect(text).not.toContain('init --features agents');
355
+ } finally {
356
+ logger.setSilent(true);
357
+ warn.mockRestore();
358
+ for (const [key, value] of Object.entries(saved)) {
359
+ if (value === undefined) delete process.env[key];
360
+ else process.env[key] = value;
361
+ }
362
+ }
363
+ });
364
+
365
+ it('refreshes the agent docs of a project in an agent worktree inside a home settings folder', async () => {
366
+ const saved = {
367
+ HOME: process.env.HOME,
368
+ USERPROFILE: process.env.USERPROFILE,
369
+ };
370
+ process.env.HOME = tmpDir;
371
+ process.env.USERPROFILE = tmpDir;
372
+ try {
373
+ const project = path.join(tmpDir, '.codex', 'worktrees', 'a1b2c3', 'web');
374
+ fs.mkdirSync(project, {recursive: true});
375
+ writePkg(project, {'@astryxdesign/core': '0.0.15'});
376
+ writeInstalledCore(project, '0.0.15');
377
+ writeAgentBlock(project, 'AGENTS.md', '0.0.1');
378
+
379
+ const res = await upgrade({from: '0.0.15', apply: true}, {cwd: project});
380
+
381
+ expect(res.data.agentDocs.action).toBe('refreshed');
382
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8')).toMatch(
383
+ /Astryx v0\.0\.15 ·/,
384
+ );
385
+ } finally {
386
+ for (const [key, value] of Object.entries(saved)) {
387
+ if (value === undefined) delete process.env[key];
388
+ else process.env[key] = value;
389
+ }
390
+ }
391
+ });
392
+
393
+ it('refreshes the managed block in an ignored .claude/CLAUDE.md and keeps the text around it (INV31)', async () => {
394
+ writePkg(tmpDir);
395
+ writeInstalledCore(tmpDir, '0.0.15');
396
+ writeAgentBlock(tmpDir, 'AGENTS.md', '0.0.1');
397
+ fs.mkdirSync(path.join(tmpDir, '.claude'));
398
+ writeAgentBlock(tmpDir, '.claude/CLAUDE.md', '0.0.1');
399
+ const claude = path.join(tmpDir, '.claude', 'CLAUDE.md');
400
+ fs.writeFileSync(
401
+ claude,
402
+ `# Team notes\n\n${fs.readFileSync(claude, 'utf-8')}\nMore notes.\n`,
403
+ );
404
+ fs.writeFileSync(path.join(tmpDir, '.gitignore'), '.claude/\n');
405
+
406
+ const res = await upgrade({from: '0.0.15', apply: true}, {cwd: tmpDir});
407
+
408
+ expect(res.data.agentDocs.action).toBe('refreshed');
409
+ expect(res.data.agentDocs.files).toEqual([
410
+ 'AGENTS.md',
411
+ '.claude/CLAUDE.md',
412
+ ]);
413
+ expect(res.data.agentDocs).not.toHaveProperty('protectedFiles');
414
+ const content = fs.readFileSync(claude, 'utf-8');
415
+ expect(content).toMatch(/^# Team notes\n/u);
416
+ expect(content).toMatch(/Astryx v0\.0\.15 ·/u);
417
+ expect(content).toMatch(/More notes\.\n$/u);
418
+ });
419
+
420
+ it('refreshes a committed .claude/CLAUDE.md that imports AGENTS.md and keeps a copy beside an ignored root CLAUDE.md', async () => {
421
+ writePkg(tmpDir);
422
+ writeInstalledCore(tmpDir, '0.0.15');
423
+ writeAgentBlock(tmpDir, 'AGENTS.md', '0.0.1');
424
+ writeAgentBlock(tmpDir, 'CLAUDE.md', '0.0.1');
425
+ fs.writeFileSync(path.join(tmpDir, '.gitignore'), '/CLAUDE.md\n');
426
+ fs.mkdirSync(path.join(tmpDir, '.claude'));
427
+ const copy = path.join(tmpDir, '.claude', 'CLAUDE.md');
428
+ fs.writeFileSync(
429
+ copy,
430
+ `@../AGENTS.md\n\n${generateCompressedIndex('0.0.1')}\n`,
431
+ );
432
+
433
+ for (let round = 0; round < 2; round++) {
434
+ await upgrade({from: '0.0.15', apply: true}, {cwd: tmpDir});
435
+ const content = fs.readFileSync(copy, 'utf-8');
436
+ expect(content, `round ${round}`).toMatch(/^@\.\.\/AGENTS\.md\n/u);
437
+ expect(content, `round ${round}`).toMatch(/Astryx v0\.0\.15 ·/u);
438
+ }
439
+ const dry = await upgrade({from: '0.0.15'}, {cwd: tmpDir});
440
+ expect(dry.data.agentDocs.status).toBe('current');
441
+ });
442
+
249
443
  it('reports would-refresh on a dry run without writing (honors cwd)', async () => {
250
444
  writePkg(tmpDir);
251
445
  writeInstalledCore(tmpDir, '0.0.15');
@@ -44,7 +44,7 @@ export type AgentDocsSummary = {
44
44
  */
45
45
  fromVersions: string[];
46
46
  /**
47
- * Files rewritten (apply) or that would be rewritten (dry-run).
47
+ * Files rewritten (apply) or that would be rewritten (dry-run), relative to the folder the command ran in.
48
48
  */
49
49
  files: string[];
50
50
  /**
@@ -73,7 +73,9 @@ export type RegistryCompositionItemSummary = {
73
73
  files: RegistryCompositionFileSummary[];
74
74
  };
75
75
  /**
76
- * Aggregate receipt for ShadCN-copied composition reconciliation.
76
+ * Aggregate receipt for ShadCN-copied composition reconciliation. File paths
77
+ * are relative to the scanned source directory, or, when the upgrade ran in a
78
+ * folder below the project root, to that folder.
77
79
  */
78
80
  export type RegistryCompositionSummary = {
79
81
  applied: boolean;
@@ -140,7 +142,7 @@ export type UpgradeRegistryResponse = {
140
142
  */
141
143
  export type ProtectedCodemodFile = {
142
144
  /**
143
- * Project-relative path.
145
+ * Path relative to the folder the command ran in.
144
146
  */
145
147
  file: string;
146
148
  /**
@@ -45,7 +45,7 @@
45
45
  * @property {'missing' | 'stale' | 'current'} status
46
46
  * @property {string} installedVersion Installed core version the block should reflect.
47
47
  * @property {string[]} fromVersions Distinct stale block versions found (the "from" side of the refresh).
48
- * @property {string[]} files Files rewritten (apply) or that would be rewritten (dry-run).
48
+ * @property {string[]} files Files rewritten (apply) or that would be rewritten (dry-run), relative to the folder the command ran in.
49
49
  * @property {boolean} refreshed True only when a block was actually rewritten (apply mode).
50
50
  * @property {'refreshed' | 'would-refresh' | 'nudge-init' | 'error' | 'none'} action
51
51
  */
@@ -70,7 +70,9 @@
70
70
  */
71
71
 
72
72
  /**
73
- * Aggregate receipt for ShadCN-copied composition reconciliation.
73
+ * Aggregate receipt for ShadCN-copied composition reconciliation. File paths
74
+ * are relative to the scanned source directory, or, when the upgrade ran in a
75
+ * folder below the project root, to that folder.
74
76
  * @typedef {object} RegistryCompositionSummary
75
77
  * @property {boolean} applied
76
78
  * @property {boolean} ok False when conflicts, missing files, invalid receipts, or failures remain.
@@ -99,7 +101,7 @@
99
101
  /**
100
102
  * A required codemod change blocked by effective working-tree protection.
101
103
  * @typedef {object} ProtectedCodemodFile
102
- * @property {string} file Project-relative path.
104
+ * @property {string} file Path relative to the folder the command ran in.
103
105
  * @property {string[]} codemods Codemods that still require a change.
104
106
  * @property {string[]} reasons Protection categories (generated, vendored, ignored, and hard boundaries).
105
107
  * @property {string[]} declarations Effective declarations that protect the file.
@@ -132,11 +134,11 @@
132
134
  * @property {boolean} [data.complete] False when protected required changes remain.
133
135
  * @property {'ERR_CODEMOD_PROTECTED'} [data.errorCode] Stable incomplete-result code when complete is false.
134
136
  * @property {number} [data.filesChanged] Distinct files changed across core + integration codemods (apply mode). One file that four codemods each changed counts once.
135
- * @property {string[]} [data.modifiedFiles] Project-relative files changed or previewed.
137
+ * @property {string[]} [data.modifiedFiles] Files changed or previewed, relative to the folder the command ran in.
136
138
  * @property {ProtectedCodemodFile[]} [data.protectedFiles] Protected files that still require a codemod change after regeneration.
137
- * @property {Array<{file: string, location?: string, reason: string}>} [data.declinedCandidates] Candidates left unchanged because proof was insufficient.
139
+ * @property {Array<{file: string, location?: string, reason: string}>} [data.declinedCandidates] Candidates left unchanged because proof was insufficient. Each `file` is relative to the folder the command ran in.
138
140
  * @property {number} [data.transformsApplied] Total codemod changes. A code or config codemod counts once for each file it changed, so one file changed by four of them counts four times; a project codemod counts once, however many files it writes.
139
- * @property {Array<{file: string, codemod: string, error: string}>} [data.errors] Per-codemod errors, when any codemod failed.
141
+ * @property {Array<{file: string, codemod: string, error: string}>} [data.errors] Per-codemod errors, when any codemod failed. Each `file` is relative to the folder the command ran in.
140
142
  * @property {CliBehindCore} [data.cliBehindCore] Present only when the installed Core is newer than this CLI: codemods for the releases after `cliBehindCore.cliVersion` did not run.
141
143
  */
142
144
 
@@ -29,6 +29,17 @@ describe('validate-data-token-ownership', () => {
29
29
  }
30
30
  });
31
31
 
32
+ it('reports prebuilt CSS availability without requiring a source compile step', async () => {
33
+ await expect(
34
+ run('const fill = "var(--color-data-categorical-blue)";'),
35
+ ).rejects.toThrow(
36
+ /in prebuilt mode, loads @astryxdesign\/core\/astryx\.css/,
37
+ );
38
+ await expect(
39
+ run('.bar { fill: var(--color-data-categorical-blue); }', 'chart.css'),
40
+ ).rejects.toThrow(/load @astryxdesign\/core\/astryx\.css in prebuilt mode/);
41
+ });
42
+
32
43
  it('leaves fallbacks, dataVars access, and comments unchanged', async () => {
33
44
  const source = `
34
45
  // var(--color-data-categorical-green)
@@ -56,7 +56,7 @@ export default function validateDataTokenOwnership(file, api) {
56
56
  ROOT_DATA_DECLARATION.test(file.source)
57
57
  ) {
58
58
  throw new Error(
59
- 'This generated theme CSS still emits canonical data defaults at :root. Rebuild it with `astryx theme build`; defaults now come from compiled dataVars consumers.',
59
+ 'This generated theme CSS still emits canonical data defaults at :root. Rebuild it with `astryx theme build`; defaults come from source-compiled dataVars consumers or @astryxdesign/core/astryx.css in prebuilt mode.',
60
60
  );
61
61
  }
62
62
  const uncommented = file.source.replace(CSS_COMMENT, comment =>
@@ -66,7 +66,7 @@ export default function validateDataTokenOwnership(file, api) {
66
66
  if (cssMatch) {
67
67
  const line = uncommented.slice(0, cssMatch.index).split('\n').length;
68
68
  throw new Error(
69
- `Raw var(${cssMatch[1]}) without a fallback at line ${line} needs manual migration: compiled dataVars output is not guaranteed in this stylesheet, so add an explicit fallback or style the element from code that imports dataVars from @astryxdesign/core/theme/dataTokens.stylex.`,
69
+ `Raw var(${cssMatch[1]}) without a fallback at line ${line} needs an availability check: load @astryxdesign/core/astryx.css in prebuilt mode, ensure a source-compiled dataVars consumer retains the group, or add an explicit fallback. CSS-capable code imports dataVars from @astryxdesign/core/theme/dataTokens.stylex.`,
70
70
  );
71
71
  }
72
72
  return undefined;
@@ -95,7 +95,7 @@ export default function validateDataTokenOwnership(file, api) {
95
95
  if (found) {
96
96
  const {token, line} = found;
97
97
  throw new Error(
98
- `Direct var(${token})${line == null ? '' : ` at line ${line}`} needs manual migration: CSS-capable code imports dataVars from @astryxdesign/core/theme/dataTokens.stylex; Canvas and other non-CSS APIs use resolveThemeToken() or useTheme().token().`,
98
+ `Direct var(${token})${line == null ? '' : ` at line ${line}`} needs manual migration: CSS-capable code imports dataVars from @astryxdesign/core/theme/dataTokens.stylex and, in prebuilt mode, loads @astryxdesign/core/astryx.css; Canvas and other non-CSS APIs use resolveThemeToken() or useTheme().token().`,
99
99
  );
100
100
  }
101
101
  return undefined;
@@ -0,0 +1,287 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `astryx docs is-my-code-good`: one evidence-first guide for checking
5
+ * whether a product surface uses Astryx well and improving it with an agent.
6
+ */
7
+
8
+ /** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */
9
+ export const docs = {
10
+ type: 'generic',
11
+ name: 'is-my-code-good',
12
+ title: 'Is my Astryx code good?',
13
+ category: 'guide',
14
+ keywords: [
15
+ 'is my code good',
16
+ 'review my code',
17
+ 'code review',
18
+ 'check my code',
19
+ 'evaluate my code',
20
+ 'review astryx code',
21
+ 'rubric',
22
+ 'astryx quality rubric',
23
+ 'custom code audit',
24
+ ],
25
+ description:
26
+ 'Check whether a product surface fits its app and uses Astryx well, require evidence for every claim, and improve it with a repeatable agent review.',
27
+ sections: [
28
+ {
29
+ id: 'understand-the-app',
30
+ title: 'Understand the app',
31
+ content: [
32
+ {
33
+ type: 'prose',
34
+ text: 'Do not judge a file or surface in isolation. First learn the app context that makes a pattern correct, inconsistent, or incomplete.',
35
+ },
36
+ {
37
+ type: 'list',
38
+ style: 'ordered',
39
+ items: [
40
+ 'Name what the app is for, who uses it, and the task this surface helps them complete.',
41
+ 'Trace where the surface sits: its route, entry point, navigation path, parent shell, and the page or block pattern it is meant to use.',
42
+ 'Inspect sibling surfaces that do the same or adjacent jobs. Record their shared headers, actions, layout primitives, spacing, density, states, and narrow-screen behavior.',
43
+ 'Read project guidance, including the root and relevant nested `AGENTS.md` files. Record app-owned wrapper components and intentional app conventions.',
44
+ 'Record the installed `@astryxdesign/core`, `@astryxdesign/cli`, and integration-package versions from the project manifest and lockfile. Run the installed invocation of `astryx component --list` to see core and integration components, then inspect app-owned wrappers that do not appear in that catalog.',
45
+ 'List the real data states, roles and permissions, supported locales and RTL behavior, and mobile or narrow-width needs that affect the task.',
46
+ ],
47
+ },
48
+ {
49
+ type: 'prose',
50
+ text: 'Keep this context with the review. Mark anything you could not learn as `not inspected`. Do not flag a documented, intentional app convention merely because it differs from a generic pattern; cite the convention and verify that the user task and accessibility requirements still work.',
51
+ },
52
+ ],
53
+ },
54
+ {
55
+ id: 'choose-the-review',
56
+ title: 'Choose the review',
57
+ content: [
58
+ {
59
+ type: 'prose',
60
+ text: 'Review one exact revision and one named product surface, route, or component in its app context. Pick the smallest review that can answer your question.',
61
+ },
62
+ {
63
+ type: 'table',
64
+ headers: ['Review', 'Use it for', 'What it can prove'],
65
+ rows: [
66
+ [
67
+ 'Source check',
68
+ 'A fast check for raw elements, direct icons, custom styles, and unsupported imports',
69
+ 'Source findings plus app context visible in the project. It cannot call the rendered product good.',
70
+ ],
71
+ [
72
+ 'Full product review',
73
+ 'Calling a surface done',
74
+ 'App fit and task completion plus source quality, rendered layout, states, assets, project checks, and accessibility evidence.',
75
+ ],
76
+ [
77
+ 'Template publication grade',
78
+ 'An integration template that people copy',
79
+ 'The shared criteria plus versioned scoring, metadata, packaging, copy, and clean-app checks in {@link generic:grade-template-with-agent}.',
80
+ ],
81
+ ],
82
+ },
83
+ {
84
+ type: 'prose',
85
+ text: 'If all you have is one source file, run the source check anyway and use any project context that is available. Report clear source blockers; list missing app purpose, route, sibling patterns, shell, theme and density, installed components and versions, real states and roles, locale and mobile needs, revision, runnable app, rendered states, and project checks; and do not call the full product good.',
86
+ },
87
+ {
88
+ type: 'prose',
89
+ text: "`astryx` in this guide means the project's installed CLI invocation, such as its local binary or existing package script. If the project has no installed invocation, report that evidence gap instead of downloading a different CLI version for the review.",
90
+ },
91
+ {
92
+ type: 'prose',
93
+ text: 'A screenshot can prove a visible result, but it cannot prove which imports, components, or styles produced it. Source alone cannot prove responsive behavior, interaction, color modes, or whether an asset loads. Mark anything outside the supplied evidence as `not inspected`.',
94
+ },
95
+ ],
96
+ },
97
+ {
98
+ id: 'apply-the-rubric',
99
+ title: 'Apply the shared rubric',
100
+ content: [
101
+ {
102
+ type: 'prose',
103
+ text: 'Mark each category `good`, `needs work`, or `not inspected`. Cite the route, sibling surface, project guidance, theme, component catalog, or user requirement behind every context-dependent result; cite a file and line for every source result; and name the rendered state for every visual result. Never turn missing evidence into a pass. A blocker is a finding that prevents the surface from building, rendering, operating, letting the target user complete the task, or meeting an accessibility requirement. Other departures from the rubric are `needs work`.',
104
+ },
105
+ {
106
+ type: 'table',
107
+ headers: ['Category', 'What good means'],
108
+ rows: [
109
+ [
110
+ 'App fit and task completion',
111
+ "The surface uses the app's established shell, page pattern, theme, density, wrappers, integrations, and interaction conventions unless a deviation is intentional and evidenced. The target user can complete the surface's job across the real states and permissions in scope.",
112
+ ],
113
+ [
114
+ 'Astryx components',
115
+ 'Astryx owns each visible or interactive pattern for which it has a suitable component. Raw elements remain only when their native semantics are necessary or Astryx has no equivalent.',
116
+ ],
117
+ [
118
+ 'Icons',
119
+ 'Icons render through `Icon`, `IconButton`, or the receiving component icon prop, not as raw SVG or a library component used directly.',
120
+ ],
121
+ [
122
+ 'Styling',
123
+ 'Component props and design tokens do the work before custom StyleX declarations, inline styles, or classes. Each custom rule has a reason because no supported Astryx surface fits.',
124
+ ],
125
+ [
126
+ 'Layout',
127
+ 'Astryx layout primitives express the visible structure, hierarchy, spacing, and responsive behavior without rebuilding the same patterns in raw containers or CSS.',
128
+ ],
129
+ [
130
+ 'Images and assets',
131
+ 'Every image, font, and other asset is intentional, accessible, stable, and working from the product build path.',
132
+ ],
133
+ [
134
+ 'Code health',
135
+ 'Public imports resolve, client boundaries are necessary, reachable states are handled, and no dead code or wrapper hides a finding.',
136
+ ],
137
+ ],
138
+ },
139
+ {
140
+ type: 'prose',
141
+ text: "Before calling a pattern custom or wrong, compare sibling surfaces and the installed component catalog. Reuse an app-owned wrapper or integration component when it already owns the job. A documented app convention is not a finding merely because it differs from a generic pattern; cite the convention and verify the user's task and accessibility requirements.",
142
+ },
143
+ {type: 'heading', level: 3, text: 'Common component replacements'},
144
+ {
145
+ type: 'table',
146
+ headers: ['Raw pattern', 'Common Astryx owner'],
147
+ rows: [
148
+ [
149
+ 'Layout `div`',
150
+ '`VStack`, `HStack`, `Grid`, `Card`, `Section`, or `Center`',
151
+ ],
152
+ ['`span`, `p`, or `h1` through `h6`', '`Text` or `Heading`'],
153
+ ['`button` or linked action', '`Button`, `IconButton`, or `Link`'],
154
+ ['In-page `nav` or `aside`', '`LayoutPanel`'],
155
+ [
156
+ '`header` or `main`',
157
+ '`LayoutHeader` or `LayoutContent` inside `Layout`',
158
+ ],
159
+ ['`ul`, `ol`, or `li`', '`List` and `ListItem`'],
160
+ [
161
+ '`input`, `textarea`, or `select`',
162
+ 'The matching Astryx form control',
163
+ ],
164
+ ['`table`, `tr`, or `td`', '`Table`'],
165
+ [
166
+ '`hr`, `dialog`, `details`, or `summary`',
167
+ '`Divider`, `Dialog`, or `Collapsible`',
168
+ ],
169
+ ],
170
+ },
171
+ {
172
+ type: 'prose',
173
+ text: 'Keep a native element when its semantics are necessary and Astryx has no equivalent. Common examples are a `form` that provides native submission, `input type="hidden"` for native form state, and an `img` when no general Astryx image component fits.',
174
+ },
175
+ {
176
+ type: 'list',
177
+ style: 'unordered',
178
+ items: [
179
+ "Compare the route with sibling surfaces that do the same job. Identify the app's shared header, actions, shell, page pattern, spacing, and density before judging the target.",
180
+ 'Inspect every JSX opening tag. Moving raw HTML behind a PascalCase helper does not make the underlying pattern an Astryx component.',
181
+ 'Count every raw SVG element used as an icon, including `svg`, `path`, `circle`, `rect`, `line`, `polyline`, `polygon`, `ellipse`, and `g`. For an inline SVG helper, count its raw SVG elements once in the helper definition, not again at each render. Count each icon-library component rendered directly outside an Astryx icon surface.',
182
+ 'Look up a possible replacement with `astryx component <Name> --dense`. Do not invent a component or prop that the installed version does not provide.',
183
+ 'Run the installed `astryx component --list` and inspect app-owned wrappers before rebuilding a pattern. Prefer the component the app or one of its installed integrations already uses for the job.',
184
+ 'Inspect every `stylex.create` declaration, inline style, `className`, and `stylex.props` use. Look for a component prop or token first.',
185
+ "When replacing a literal spacing value, read `astryx docs spacing --full` for the matching step or token and the target component doc for the prop's accepted scale. Do not guess a pixel-to-step mapping.",
186
+ 'Choose the root that matches the surface. Use `Layout` for a page, `Center` for centered content, and `AppShell` only when the surface owns global application chrome.',
187
+ 'Render realistic empty, loading, error, long-content, and interactive states. Check narrow and wide widths plus each supported color mode.',
188
+ 'Verify the target roles and permissions, supported locales and RTL behavior, and mobile needs. Follow the real task from entry to completion instead of judging only the static page.',
189
+ 'Run the product build, type check, tests, and accessibility checks. Keep a missing or failed check visible instead of calling the result good.',
190
+ ],
191
+ },
192
+ {
193
+ type: 'prose',
194
+ text: 'This shared rubric has no numeric score. It answers whether ordinary product code uses Astryx well without pretending that an inapplicable template field or missing browser state earned points. Integration templates add a versioned score in {@link generic:template-grading-rubric}.',
195
+ },
196
+ ],
197
+ },
198
+ {
199
+ id: 'collect-the-evidence',
200
+ title: 'Collect the evidence',
201
+ content: [
202
+ {
203
+ type: 'list',
204
+ style: 'ordered',
205
+ items: [
206
+ 'Record the exact revision, named surface, route, and source files in scope. If the files are outside version control, record a content hash for each file and say that no project revision was available.',
207
+ "Write down the app's purpose, target users, surface task, entry point, navigation path, parent shell, expected page pattern, and sibling surfaces used for comparison.",
208
+ 'Record project guidance, theme, density, installed Astryx and integration versions, the relevant output of `astryx component --list`, and app-owned wrappers or conventions.',
209
+ 'List the states and context that matter, including real data shapes, roles and permissions, empty, loading, error, long content, interaction, narrow width, wide width, supported color modes, locales, and RTL behavior when they apply.',
210
+ 'Run the product and capture each named rendered state. Use screenshots or a live browser result for visible claims, and follow the target user task from entry to completion.',
211
+ 'Run the build, type check, tests, and accessibility checks. Keep their commands and results with the review.',
212
+ 'For each Astryx component whose replacement, prop, or composition is in question, read `astryx component <Name> --dense`.',
213
+ ],
214
+ },
215
+ ],
216
+ },
217
+ {
218
+ id: 'ask-an-agent',
219
+ title: 'Ask an agent',
220
+ content: [
221
+ {
222
+ type: 'prose',
223
+ text: 'Start with a read-only pass so the original findings stay visible. Replace every angle-bracket value in this prompt.',
224
+ },
225
+ {
226
+ type: 'code',
227
+ lang: 'text',
228
+ label: 'Agent evaluation prompt',
229
+ code: `Evaluate how well <surface> at <revision> fits <app> and uses Astryx. Do not edit files during this first pass.
230
+
231
+ \`astryx\` below means the project's installed CLI invocation, such as its local binary or existing package script. If none is installed, report that evidence gap instead of downloading a different version.
232
+
233
+ Before evaluating:
234
+ 1. Read \`astryx docs is-my-code-good --full\`.
235
+ 2. Read the root and relevant nested \`AGENTS.md\` files plus other project guidance. State what the app is for, who uses it, and the task this surface supports.
236
+ 3. Trace the surface's route, entry point, navigation path, parent shell, and expected page or block pattern.
237
+ 4. Inspect at least two sibling surfaces that do the same or adjacent jobs. Record their shared headers, actions, layout, spacing, density, states, and narrow-screen behavior.
238
+ 5. Record the app's theme, density, app-owned wrappers, and documented intentional conventions.
239
+ 6. Read the project manifest and lockfile for the installed Astryx, CLI, and integration versions. Run \`astryx component --list\`, then identify relevant integration components and app-owned wrappers.
240
+ 7. List the real data states, roles and permissions, supported locales and RTL behavior, and mobile or narrow-width needs.
241
+ 8. Inspect every source file in scope and its public imports.
242
+ 9. Run \`astryx component <Name> --dense\` for each Astryx component whose replacement, prop, or composition you question.
243
+ 10. Read \`astryx docs spacing --full\` and the target component's docs before mapping a literal spacing value to a prop or token.
244
+ 11. Render every supplied state at narrow and wide widths and in each supported color mode.
245
+ 12. Read the build, type-check, test, and accessibility-check results.
246
+
247
+ If only source is supplied, continue with a source check. Report blockers visible in the source, list every missing app-context and product-evidence item, and do not call the full product good. Do not leave the blocker list empty only because no checks were supplied.
248
+
249
+ Judge the code against the app as well as Astryx. Ask whether it matches sibling surfaces, uses the right shell and page pattern, reuses app or integration components instead of rebuilding them, preserves intentional app conventions, and lets the target user complete the task.
250
+
251
+ For each rubric category, return \`good\`, \`needs work\`, or \`not inspected\`. Every context-dependent finding must cite the route, sibling file, project guidance, theme, component catalog, wrapper, or user requirement it relies on. Cite file and line evidence for source findings and name the rendered state for visual findings. Mark context you could not learn as \`not inspected\`. Do not flag a documented intentional app convention merely because it differs from a generic pattern. Never turn missing evidence into a pass. Do not hide raw HTML or custom styles behind a helper. Do not calculate a template score for ordinary product code.
252
+
253
+ Return:
254
+ - app purpose, target users, surface job, route, and entry path
255
+ - app context inspected and app context missing
256
+ - product evidence inspected and product evidence missing
257
+ - one row per rubric category with result, evidence, and fix
258
+ - task-completion, functional, or accessibility blockers visible in the source or found by the supplied checks
259
+ - the top three fixes in order
260
+ - every claim that still needs a person to verify`,
261
+ },
262
+ ],
263
+ },
264
+ {
265
+ id: 'verify-and-repeat',
266
+ title: 'Verify, improve, and repeat',
267
+ content: [
268
+ {
269
+ type: 'list',
270
+ style: 'ordered',
271
+ items: [
272
+ 'Reject a `good` result without app-context, source, and rendered evidence. Open every cited route, sibling file, guidance file, and source location, and reproduce every named visible state.',
273
+ 'Check that documented intentional app conventions were not flagged merely for differing from a generic pattern, and that context the reviewer could not learn stayed `not inspected`.',
274
+ 'Fix task-completion, build, functional, and accessibility blockers first.',
275
+ 'Reuse the app shell, page pattern, wrappers, and installed integration components before replacing unnecessary raw elements, direct icons, and custom styles.',
276
+ 'Follow the target user task again, render the full state set, and rerun the project checks.',
277
+ 'Repeat the read-only review on the new revision. Keep the first report so the improvement and every remaining tradeoff stay visible.',
278
+ ],
279
+ },
280
+ {
281
+ type: 'prose',
282
+ text: 'For an integration template, finish with the scored package and copy workflow in {@link generic:grade-template-with-agent}. Ordinary product code stops with the evidence-backed category report and does not receive a template publication grade.',
283
+ },
284
+ ],
285
+ },
286
+ ],
287
+ };
@@ -20,6 +20,12 @@ export const docs = {
20
20
  slots: {
21
21
  kinds: {title: 'Reference', accepts: {kinds: ['namespace']}},
22
22
  },
23
+ blocks: [
24
+ {
25
+ type: 'prose',
26
+ text: 'To run the whole CLI from your own command-line tool, with its arguments, folder, and command name, use the host entry: {@link generic:host-entry}.',
27
+ },
28
+ ],
23
29
  adopts: [
24
30
  {
25
31
  source: {group: 'cli/api', kinds: ['function', 'schema', 'enum']},