@astryxdesign/cli 0.1.0 → 0.1.1-canary.13763f6

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 (141) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +117 -75
  3. package/bin/astryx.mjs +22 -7
  4. package/docs/getting-started.doc.mjs +11 -11
  5. package/docs/icons.doc.mjs +1 -1
  6. package/docs/migration.doc.mjs +2 -2
  7. package/docs/shape.doc.mjs +1 -1
  8. package/docs/styling.doc.mjs +3 -4
  9. package/docs/theme.doc.dense.mjs +2 -2
  10. package/docs/theme.doc.mjs +14 -0
  11. package/docs/theme.doc.zh.mjs +2 -2
  12. package/docs/working-with-ai.doc.mjs +4 -4
  13. package/package.json +8 -8
  14. package/src/api/doctor.mjs +3 -3
  15. package/src/api/search.mjs +207 -13
  16. package/src/api/template.mjs +62 -11
  17. package/src/api/template.test.mjs +2 -0
  18. package/src/codemods/__tests__/registry.test.mjs +1 -0
  19. package/src/codemods/registry.mjs +1 -0
  20. package/src/codemods/runner.mjs +105 -51
  21. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-config-surfaces.test.mjs +116 -0
  22. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +51 -0
  23. package/src/codemods/transforms/v0.1.0/index.mjs +28 -0
  24. package/src/codemods/transforms/v0.1.0/migrate-xds-config-surfaces.mjs +230 -0
  25. package/src/codemods/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +84 -0
  26. package/src/commands/agent-docs.mjs +119 -66
  27. package/src/commands/agent-docs.path-safety.test.mjs +1 -1
  28. package/src/commands/agent-docs.test.mjs +87 -31
  29. package/src/commands/build-theme.import-path.test.mjs +1 -1
  30. package/src/commands/build-theme.path-safety.test.mjs +1 -1
  31. package/src/commands/build-theme.prose.test.mjs +1 -1
  32. package/src/commands/build.mjs +196 -0
  33. package/src/commands/component-package.test.mjs +1 -1
  34. package/src/commands/component.test.mjs +1 -1
  35. package/src/commands/docs.test.mjs +1 -1
  36. package/src/commands/doctor.test.mjs +1 -1
  37. package/src/commands/external-showcase.test.mjs +1 -1
  38. package/src/commands/init.mjs +10 -2
  39. package/src/commands/interactive-guard.test.mjs +1 -1
  40. package/src/commands/json-contract.test.mjs +10 -3
  41. package/src/commands/swizzle-gap-safety.test.mjs +1 -1
  42. package/src/commands/swizzle.path-safety.test.mjs +1 -1
  43. package/src/commands/template.path-safety.test.mjs +1 -1
  44. package/src/commands/template.test.mjs +1 -1
  45. package/src/commands/upgrade.mjs +353 -169
  46. package/src/commands/upgrade.test.mjs +41 -27
  47. package/src/index.mjs +1 -0
  48. package/src/lib/config.mjs +12 -0
  49. package/src/lib/config.test.mjs +42 -0
  50. package/src/lib/error-codes.mjs +3 -0
  51. package/src/types/error-codes.d.ts +1 -0
  52. package/src/utils/interactive.mjs +1 -1
  53. package/src/utils/interactive.test.mjs +2 -0
  54. package/src/utils/package-manager.mjs +1 -1
  55. package/src/utils/package-manager.test.mjs +1 -1
  56. package/src/utils/path-safety.test.mjs +1 -1
  57. package/src/utils/paths.test.mjs +8 -8
  58. package/src/utils/update-check.mjs +4 -26
  59. package/src/utils/update-check.test.mjs +2 -64
  60. package/templates/blocks/components/AppShell/AppShellContentOnly.tsx +1 -9
  61. package/templates/blocks/components/AppShell/AppShellShowcase.tsx +1 -10
  62. package/templates/blocks/components/AppShell/AppShellSideNavOnly.tsx +1 -9
  63. package/templates/blocks/components/AppShell/AppShellTopNavOnly.tsx +1 -9
  64. package/templates/blocks/components/AppShell/AppShellTopNavWithSideNav.tsx +1 -9
  65. package/templates/blocks/components/AppShell/AppShellWithBanner.tsx +1 -9
  66. package/templates/blocks/components/AspectRatio/AspectRatioShowcase.tsx +12 -19
  67. package/templates/blocks/components/Banner/BannerShowcase.tsx +1 -8
  68. package/templates/blocks/components/Blockquote/BlockquoteShowcase.tsx +1 -8
  69. package/templates/blocks/components/Carousel/CarouselShowcase.tsx +2 -12
  70. package/templates/blocks/components/ChatComposerDrawer/ChatComposerDrawerShowcase.tsx +6 -9
  71. package/templates/blocks/components/ChatLayout/ChatLayoutPanelChat.tsx +10 -12
  72. package/templates/blocks/components/ChatMessageList/ChatMessageListDensity.tsx +1 -9
  73. package/templates/blocks/components/ChatMessageList/ChatMessageListFullFeatured.tsx +1 -9
  74. package/templates/blocks/components/ChatMessageList/ChatMessageListShowcase.tsx +1 -9
  75. package/templates/blocks/components/ChatMessageMetadata/ChatMessageMetadataShowcase.tsx +1 -8
  76. package/templates/blocks/components/ChatSendButton/ChatSendButtonInComposer.tsx +1 -8
  77. package/templates/blocks/components/Citation/CitationInlineText.tsx +4 -4
  78. package/templates/blocks/components/Code/CodeInlineInParagraph.tsx +1 -8
  79. package/templates/blocks/components/CodeBlock/CodeBlockBashCommand.tsx +1 -1
  80. package/templates/blocks/components/CodeBlock/CodeBlockJSONConfig.tsx +1 -1
  81. package/templates/blocks/components/CommandPaletteItem/CommandPaletteItemShowcase.tsx +9 -12
  82. package/templates/blocks/components/ContextMenu/ContextMenuShowcase.tsx +13 -15
  83. package/templates/blocks/components/Divider/DividerShowcase.tsx +1 -8
  84. package/templates/blocks/components/Divider/DividerVertical.tsx +7 -9
  85. package/templates/blocks/components/Field/FieldShowcase.tsx +1 -8
  86. package/templates/blocks/components/FormLayout/FormLayoutHorizontal.tsx +1 -6
  87. package/templates/blocks/components/Grid/GridResponsiveAutoFit.tsx +1 -9
  88. package/templates/blocks/components/HoverCard/HoverCardInlineTextHoverCard.tsx +4 -6
  89. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.tsx +1 -6
  90. package/templates/blocks/components/HoverCard/HoverCardProfileHoverCard.tsx +2 -8
  91. package/templates/blocks/components/HoverCard/HoverCardShowcase.tsx +1 -8
  92. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.tsx +2 -12
  93. package/templates/blocks/components/OverflowList/OverflowListOverflowBadges.tsx +8 -11
  94. package/templates/blocks/components/OverflowList/OverflowListOverflowDropdownActions.tsx +9 -12
  95. package/templates/blocks/components/Overlay/OverlayBottomStrip.tsx +4 -17
  96. package/templates/blocks/components/Overlay/OverlayHoverReveal.tsx +15 -16
  97. package/templates/blocks/components/Overlay/OverlayShowcase.tsx +5 -21
  98. package/templates/blocks/components/Pagination/PaginationDotsCarousel.tsx +2 -14
  99. package/templates/blocks/components/Pagination/PaginationPageSize.tsx +12 -14
  100. package/templates/blocks/components/Pagination/PaginationVariants.tsx +1 -8
  101. package/templates/blocks/components/Pagination/PaginationWithTable.tsx +2 -14
  102. package/templates/blocks/components/Tokenizer/TokenizerClear.tsx +1 -6
  103. package/templates/blocks/components/Tokenizer/TokenizerCreatable.tsx +2 -7
  104. package/templates/blocks/components/Tokenizer/TokenizerEndContent.tsx +1 -6
  105. package/templates/blocks/components/Tokenizer/TokenizerIcon.tsx +1 -6
  106. package/templates/blocks/components/Tokenizer/TokenizerMaxEntries.tsx +1 -6
  107. package/templates/blocks/components/Tokenizer/TokenizerOverflow.tsx +2 -7
  108. package/templates/blocks/components/Tokenizer/TokenizerShowcase.tsx +1 -6
  109. package/templates/blocks/components/Tokenizer/TokenizerStates.tsx +4 -9
  110. package/templates/blocks/components/Toolbar/ToolbarCardHeader.tsx +1 -10
  111. package/templates/blocks/components/Toolbar/ToolbarSizes.tsx +1 -8
  112. package/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +1 -8
  113. package/templates/blocks/components/Toolbar/ToolbarThreeSlot.tsx +1 -10
  114. package/templates/blocks/components/Toolbar/ToolbarWithTabs.tsx +8 -11
  115. package/templates/pages/ai-chat/page.tsx +71 -64
  116. package/templates/pages/ai-chat-landing/page.tsx +8 -12
  117. package/templates/pages/centered-hero/page.tsx +13 -15
  118. package/templates/pages/classic-gallery/page.tsx +27 -34
  119. package/templates/pages/detail-page/page.tsx +18 -18
  120. package/templates/pages/documentation/page.tsx +42 -58
  121. package/templates/pages/documentation-design/page.tsx +82 -60
  122. package/templates/pages/documentation-technical/page.tsx +101 -60
  123. package/templates/pages/editor/page.tsx +42 -54
  124. package/templates/pages/file-explorer/page.tsx +13 -16
  125. package/templates/pages/form-two-column/page.tsx +13 -17
  126. package/templates/pages/gallery-hero/page.tsx +13 -15
  127. package/templates/pages/ide/page.tsx +188 -264
  128. package/templates/pages/library/page.tsx +16 -23
  129. package/templates/pages/login/page.tsx +14 -18
  130. package/templates/pages/login-card/page.tsx +14 -18
  131. package/templates/pages/login-split/page.tsx +50 -48
  132. package/templates/pages/login-sso/page.tsx +9 -13
  133. package/templates/pages/mixed-gallery/page.tsx +51 -45
  134. package/templates/pages/payment-form/page.tsx +56 -70
  135. package/templates/pages/product-detail/page.tsx +27 -33
  136. package/templates/pages/product-gallery/page.tsx +7 -13
  137. package/templates/pages/settings-dialog/page.tsx +35 -43
  138. package/templates/pages/settings-sidebar/page.tsx +39 -47
  139. package/templates/pages/side-gallery/page.tsx +6 -9
  140. package/templates/pages/table-grouped/page.tsx +11 -15
  141. package/templates/pages/theme-showcase/page.tsx +33 -37
@@ -0,0 +1,196 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file build command — the page-building assistant.
5
+ *
6
+ * Two modes:
7
+ * astryx build → the PLAYBOOK: how to build a page with Astryx
8
+ * astryx build "<what>" → a COMPOSITION KIT for what you're building:
9
+ * the closest page template (scaffold or layout
10
+ * reference), the blocks that cover parts, and
11
+ * the components to fill gaps, plus a one-line
12
+ * "Compose:" suggestion.
13
+ *
14
+ * `build` is the opinionated "assemble a page" verb. For a neutral lookup across
15
+ * the whole CLI, use `astryx search <query>` instead.
16
+ */
17
+
18
+ import {getRunPrefix} from '../utils/package-manager.mjs';
19
+ import {jsonOut, humanLog} from '../lib/json.mjs';
20
+ import {cliError} from '../lib/cli-error.mjs';
21
+ import {search as searchApi} from '../api/search.mjs';
22
+
23
+ /** A page scoring at/above this is confident enough to call a direct match. */
24
+ const PAGE_DIRECT = 95;
25
+ /** Below this a page is too weak to even offer as a layout reference. */
26
+ const PAGE_FLOOR = 50;
27
+ /** Below this a block/domain-component match is incidental noise, not surfaced. */
28
+ const DOMAIN_FLOOR = 55;
29
+
30
+ /**
31
+ * Always-surfaced primitives. Every page needs a shell + layout/typography/
32
+ * action atoms, but these never keyword-match an idea ("dashboard" != "Stack"),
33
+ * so search alone never returns them. We list them unconditionally so an agent
34
+ * composing from scratch has the whole kit (esp. off-template).
35
+ */
36
+ const FRAME = ['AppShell', 'TopNav', 'SideNav', 'Layout'];
37
+ const FOUNDATION = [
38
+ 'VStack', 'HStack', 'Grid', 'StackItem', 'Card', 'Section',
39
+ 'Text', 'Heading', 'Button', 'Icon', 'Badge', 'Divider',
40
+ ];
41
+ const ALWAYS = new Set([...FRAME, ...FOUNDATION]);
42
+
43
+ /** Print the build playbook (shown when `build` is run with no query). */
44
+ function printPlaybook(run) {
45
+ const lines = [
46
+ '',
47
+ 'How to build a page with Astryx',
48
+ '',
49
+ "1. Find a starting point for what you're building:",
50
+ ` ${run} astryx build "<what you're building>"`,
51
+ ' → returns the closest [page] template, the [block]s that cover parts,',
52
+ ' and the [component]s to fill the gaps, with a "Compose:" suggestion.',
53
+ '',
54
+ '2. If a [page] template matches → scaffold it and adapt:',
55
+ ` ${run} astryx template <name> [path]`,
56
+ '',
57
+ '3. If nothing matches exactly → compose:',
58
+ ` ${run} astryx template <name> --skeleton # study a close page's layout`,
59
+ ` ${run} astryx template <BlockName> # drop in each block from the kit`,
60
+ ` ${run} astryx component <Name> # fill remaining gaps (read props)`,
61
+ '',
62
+ '4. Rules (keep it on-system):',
63
+ ' - No <div>/raw HTML for layout — use VStack/HStack/Grid/Stack/Card etc.',
64
+ ' - No style={{}} — use component props; design tokens via `astryx docs tokens`.',
65
+ ' - Wrap the app in <Theme theme={...}> and import core reset.css + astryx.css.',
66
+ '',
67
+ `Tip: \`${run} astryx build "<idea>"\` is the fastest way in. For a neutral`,
68
+ `lookup of any component/doc/template, use \`${run} astryx search <query>\`.`,
69
+ '',
70
+ ];
71
+ for (const l of lines) humanLog(l);
72
+ }
73
+
74
+ export function registerBuild(program) {
75
+ program
76
+ .command('build [query]')
77
+ .description('Build a page: composition kit for an idea, or the workflow playbook (no args)')
78
+ .option('--type <domain>', 'Filter the kit to one domain (component|hook|template)')
79
+ .option('--limit <n>', 'Max candidates to draw from (default 60)')
80
+ .option('--detail', 'Verbose output (include import paths and match reason)')
81
+ .action(async (query, options) => {
82
+ const run = getRunPrefix();
83
+ const json = program.opts().json || false;
84
+
85
+ // No query → print the playbook (the "how to build" skill).
86
+ if (!query || !String(query).trim()) {
87
+ if (json) return jsonOut('build.help', {playbook: true});
88
+ printPlaybook(run);
89
+ return;
90
+ }
91
+
92
+ // Default to a deep pool so each role section has candidates after grouping.
93
+ let limit = 60;
94
+ if (options.limit != null) {
95
+ const parsed = Number.parseInt(options.limit, 10);
96
+ if (!Number.isFinite(parsed) || parsed <= 0) {
97
+ cliError(`Invalid --limit value "${options.limit}". Must be a positive integer.`);
98
+ return;
99
+ }
100
+ limit = parsed;
101
+ }
102
+
103
+ let result;
104
+ try {
105
+ result = await searchApi(query, {cwd: process.cwd(), type: options.type, limit});
106
+ } catch (e) {
107
+ cliError(e.message, {suggestions: e.suggestions});
108
+ return;
109
+ }
110
+
111
+ if (json) return jsonOut(result.type, result.data);
112
+
113
+ const {query: q, results} = result.data;
114
+
115
+ if (results.length === 0) {
116
+ humanLog('');
117
+ humanLog(`No matches for "${q}".`);
118
+ humanLog(`Try a broader term, or browse: ${run} astryx component --list`);
119
+ humanLog('');
120
+ return;
121
+ }
122
+
123
+ // ── Group results by role (the build kit) ──────────────────────
124
+ const pages = results
125
+ .filter(r => r.domain === 'template' && r.kind !== 'block' && r.score >= PAGE_FLOOR)
126
+ .slice(0, 3);
127
+ const blocks = results
128
+ .filter(r => r.domain === 'template' && r.kind === 'block' && r.score >= DOMAIN_FLOOR)
129
+ .slice(0, 5);
130
+ // Idea-specific atoms = matched components/hooks MINUS the always-on kit.
131
+ const domain = results
132
+ .filter(r => (r.domain === 'component' || r.domain === 'hook') && r.score >= DOMAIN_FLOOR && !ALWAYS.has(r.name))
133
+ .slice(0, 6);
134
+ const directMatch = pages.length > 0 && pages[0].score >= PAGE_DIRECT;
135
+
136
+ const printItem = (r, label) => {
137
+ const display = r.domain === 'template' && r.displayName ? r.displayName : r.name;
138
+ humanLog('');
139
+ humanLog(` [${label}] ${display}`);
140
+ if (r.description) humanLog(` ${r.description}`);
141
+ humanLog(` → ${run} ${r.command}`);
142
+ if (options.detail) {
143
+ if (r.import) humanLog(` import: ${r.import}`);
144
+ humanLog(` match: ${r.reason} (score ${r.score})`);
145
+ }
146
+ };
147
+
148
+ humanLog('');
149
+ humanLog(`Building "${q}":`);
150
+
151
+ // START — the single recommended path.
152
+ humanLog('');
153
+ if (directMatch) {
154
+ humanLog(`START → Scaffold the \`${pages[0].name}\` page template, then adapt: ${run} astryx template ${pages[0].name} ./src/App.tsx`);
155
+ } else if (pages.length) {
156
+ humanLog(`START → No exact page template. Use \`${pages[0].name}\` as a layout reference (${run} astryx template ${pages[0].name} --skeleton) and compose the pieces below.`);
157
+ } else {
158
+ humanLog(`START → No page template fits. Frame with AppShell and compose the blocks + components below.`);
159
+ }
160
+
161
+ // PAGE
162
+ if (pages.length) {
163
+ humanLog('');
164
+ humanLog(directMatch ? 'PAGE TEMPLATE — direct match:' : 'CLOSEST PAGE TEMPLATES — layout reference:');
165
+ pages.forEach(p => printItem(p, directMatch ? 'page' : 'closest'));
166
+ }
167
+
168
+ // FRAME — always (the page shell).
169
+ humanLog('');
170
+ humanLog(`FRAME — page shell (always): ${FRAME.join(', ')}`);
171
+ humanLog(` full-page → AppShell; or Layout + SideNav/TopNav. ${run} astryx component AppShell`);
172
+
173
+ // BLOCKS — idea-specific composed patterns.
174
+ if (blocks.length) {
175
+ humanLog('');
176
+ humanLog('BLOCKS — drop-in patterns that cover parts of it:');
177
+ blocks.forEach(b => printItem(b, 'block'));
178
+ }
179
+
180
+ // DOMAIN COMPONENTS — idea-specific atoms.
181
+ if (domain.length) {
182
+ humanLog('');
183
+ humanLog('DOMAIN COMPONENTS — specific to this idea:');
184
+ domain.forEach(c => printItem(c, c.domain === 'hook' ? 'hook' : 'component'));
185
+ }
186
+
187
+ // FOUNDATION — always (layout/typography/actions).
188
+ humanLog('');
189
+ humanLog(`FOUNDATION — always available (layout/text/actions): ${FOUNDATION.join(' ')}`);
190
+
191
+ // SETUP — so it renders / stays on-system.
192
+ humanLog('');
193
+ humanLog('SETUP — import "@astryxdesign/core/reset.css" + "astryx.css". No <div>/style for layout — use Stack/Grid + tokens.');
194
+ humanLog('');
195
+ });
196
+ }
@@ -65,7 +65,7 @@ export const docs = {
65
65
  }
66
66
 
67
67
  beforeEach(() => {
68
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-pkg-test-'));
68
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-pkg-test-'));
69
69
  createFixture();
70
70
  });
71
71
 
@@ -18,7 +18,7 @@ import {
18
18
  let tmpDir;
19
19
 
20
20
  beforeEach(() => {
21
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-component-test-'));
21
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-component-test-'));
22
22
  });
23
23
 
24
24
  afterEach(() => {
@@ -10,7 +10,7 @@ import {registerDocs} from './docs.mjs';
10
10
  let tmpDir;
11
11
 
12
12
  beforeEach(() => {
13
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-docs-test-'));
13
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-docs-test-'));
14
14
  vi.spyOn(console, 'log').mockImplementation(() => {});
15
15
  vi.spyOn(console, 'error').mockImplementation(() => {});
16
16
  });
@@ -26,7 +26,7 @@ let logCalls;
26
26
  let exitCode;
27
27
 
28
28
  beforeEach(() => {
29
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-doctor-test-'));
29
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-doctor-test-'));
30
30
  logCalls = [];
31
31
  exitCode = undefined;
32
32
  vi.spyOn(console, 'log').mockImplementation((...args) => {
@@ -78,7 +78,7 @@ export const doc = {
78
78
  }
79
79
 
80
80
  beforeEach(() => {
81
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-showcase-test-'));
81
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-showcase-test-'));
82
82
  createFixture();
83
83
  });
84
84
 
@@ -100,7 +100,15 @@ async function runTemplate(targetDir, {interactive = true, templateName} = {}) {
100
100
 
101
101
  if (!interactive) {
102
102
  if (!templateName) {
103
- humanLog(`✓ Available templates: ${templates.join(', ')}. Use ${run} astryx template <name> [path].`);
103
+ // Point agents at the build workflow rather than dumping page-template
104
+ // names — `build` surfaces pages AND blocks AND components for an idea,
105
+ // and `build` with no args is the full how-to-build playbook.
106
+ humanLog('✓ To build UI, use these commands:');
107
+ humanLog('');
108
+ humanLog(` ${run} astryx build "<what you're building>" build a page — kit: closest template + blocks + components`);
109
+ humanLog(` ${run} astryx build the how-to-build workflow (read this first)`);
110
+ humanLog(` ${run} astryx search <query> find anything — components, docs, templates, blocks`);
111
+ humanLog('');
104
112
  return;
105
113
  }
106
114
 
@@ -248,7 +256,7 @@ export function registerInit(program) {
248
256
  humanLog(' 2. Optionally add a theme:');
249
257
  humanLog(" import { neutralTheme } from '@astryxdesign/theme-neutral'");
250
258
  humanLog(' <Theme theme={neutralTheme}>...</Theme>');
251
- humanLog(` 3. ${run} xds --help for all commands`);
259
+ humanLog(` 3. ${run} astryx --help for all commands`);
252
260
  humanLog('');
253
261
  });
254
262
  }
@@ -35,7 +35,7 @@ function runCli(args) {
35
35
  }
36
36
 
37
37
  beforeEach(() => {
38
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-interactive-guard-'));
38
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-interactive-guard-'));
39
39
  });
40
40
 
41
41
  afterEach(() => {
@@ -53,7 +53,7 @@ function parseJson(stdout) {
53
53
  let tmpDir;
54
54
 
55
55
  beforeEach(() => {
56
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-json-contract-'));
56
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-json-contract-'));
57
57
  });
58
58
 
59
59
  afterEach(() => {
@@ -177,9 +177,16 @@ describe('--json contract: supported commands emit valid envelopes', () => {
177
177
  });
178
178
 
179
179
  it('astryx upgrade --json (already up to date) emits upgrade.status', () => {
180
- // Force a no-op range: from > to.
180
+ // Force a no-op range: from > installed target.
181
+ const coreDir = path.join(tmpDir, 'node_modules', '@astryxdesign', 'core');
182
+ fs.mkdirSync(coreDir, {recursive: true});
183
+ fs.writeFileSync(
184
+ path.join(coreDir, 'package.json'),
185
+ JSON.stringify({name: '@astryxdesign/core', version: '0.0.1'}, null, 2),
186
+ );
187
+
181
188
  const {status, stdout} = runCli(
182
- ['upgrade', '--json', '--from', '99.0.0', '--to', '0.0.1'],
189
+ ['upgrade', '--json', '--from', '99.0.0'],
183
190
  {cwd: tmpDir},
184
191
  );
185
192
  expect(status).toBe(0);
@@ -36,7 +36,7 @@ let markerFile;
36
36
  let baseEnv;
37
37
 
38
38
  beforeAll(() => {
39
- shimDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-gh-shim-'));
39
+ shimDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-gh-shim-'));
40
40
  markerFile = path.join(shimDir, 'gh-issue-create-was-called.marker');
41
41
 
42
42
  // Sabotaged `gh` shim. Records ONLY `gh issue create` invocations to the
@@ -58,7 +58,7 @@ function runCli(args, cwd) {
58
58
 
59
59
  let tmpDir;
60
60
  beforeEach(() => {
61
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-swizzle-paths-'));
61
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-swizzle-paths-'));
62
62
  });
63
63
  afterEach(() => {
64
64
  fs.rmSync(tmpDir, {recursive: true, force: true});
@@ -17,7 +17,7 @@ let tmpDir;
17
17
  let templateApi;
18
18
 
19
19
  beforeEach(async () => {
20
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-template-paths-'));
20
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-template-paths-'));
21
21
  templateApi = (await import('../api/template.mjs')).template;
22
22
  });
23
23
  afterEach(() => {
@@ -12,7 +12,7 @@ import * as os from 'node:os';
12
12
  let tmpDir;
13
13
 
14
14
  beforeEach(() => {
15
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xds-template-test-'));
15
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-template-test-'));
16
16
  });
17
17
 
18
18
  afterEach(() => {