@astryxdesign/cli 0.6.3-canary.7ada42e → 0.6.3-canary.7ec7be0
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.
- package/README.md +21 -19
- package/api/build/_adapter.d.mts +50 -0
- package/api/build/_adapter.mjs +60 -0
- package/api/build/build.doc.mjs +13 -7
- package/api/build/build.test.mjs +184 -6
- package/api/build/build.type.d.mts +57 -2
- package/api/build/build.type.mjs +30 -7
- package/api/build/help/help.d.mts +4 -0
- package/api/build/help/help.mjs +24 -11
- package/api/build/kit/kit.d.mts +4 -1
- package/api/build/kit/kit.mjs +165 -49
- package/api/build/kit/rank.d.mts +44 -0
- package/api/build/kit/rank.mjs +432 -0
- package/api/build/kit/rank.test.mjs +196 -0
- package/api/component/_adapter.d.mts +6 -12
- package/api/component/_adapter.mjs +20 -10
- package/api/component/component.mjs +60 -12
- package/api/component/list/list.mjs +3 -2
- package/api/docs/_adapter.d.mts +251 -28
- package/api/docs/_adapter.mjs +918 -35
- package/api/docs/detail/detail.mjs +11 -3
- package/api/docs/detail/section/section.mjs +24 -13
- package/api/docs/detail/section/section.test.mjs +15 -6
- package/api/docs/docs.d.mts +5 -3
- package/api/docs/docs.doc.mjs +38 -17
- package/api/docs/docs.mjs +44 -8
- package/api/docs/docs.test.mjs +158 -4
- package/api/docs/docs.type.d.mts +181 -2
- package/api/docs/docs.type.mjs +117 -3
- package/api/docs/index/index.mjs +11 -3
- package/api/docs/index/index.test.mjs +1 -1
- package/api/docs/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +14 -14
- package/api/docs/list/list.mjs +25 -5
- package/api/docs/node/node.d.mts +43 -0
- package/api/docs/node/node.mjs +192 -0
- package/api/docs/reference-blocks.test.mjs +406 -0
- package/api/doctor/doctor.d.mts +30 -2
- package/api/doctor/doctor.mjs +151 -8
- package/api/doctor/doctor.test.mjs +95 -7
- package/api/index.d.mts +1 -1
- package/api/index.mjs +1 -0
- package/api/init/init.doc.mjs +1 -1
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +107 -5
- package/api/integration/add-contribution.test.mjs +137 -0
- package/api/integration/authoring-checks.mjs +138 -28
- package/api/integration/authoring-checks.test.mjs +171 -3
- package/api/integration/authoring-checks.type.mjs +6 -1
- package/api/integration/integration-authoring.type.d.mts +2 -0
- package/api/integration/integration-authoring.type.mjs +2 -0
- package/api/integration/integrationAdd.doc.mjs +6 -0
- package/api/integration/integrationAddDoc.doc.mjs +7 -1
- package/api/integration/integrationDocConflicts.doc.mjs +1 -1
- package/api/integration/integrationPackCheck.doc.mjs +1 -1
- package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
- package/api/integration/integrationTemplateConflicts.doc.mjs +3 -0
- package/api/integration/pack-check.mjs +34 -0
- package/api/integration/pack-check.test.mjs +121 -0
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +14 -1
- package/api/integration/template-conflict-compatibility.test.mjs +73 -0
- package/api/integration/validate-integration.test.mjs +3 -3
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/json/index.ts +2 -0
- package/api/layout/_adapter.mjs +20 -5
- package/api/search/search.d.mts +59 -1
- package/api/search/search.doc.mjs +4 -3
- package/api/search/search.mjs +442 -71
- package/api/search/search.test.mjs +91 -2
- package/api/search/search.type.d.mts +13 -1
- package/api/search/search.type.mjs +4 -1
- package/api/template/list/list.mjs +1 -0
- package/api/template/table-floating-bulk-actions.test.mjs +66 -0
- package/api/template/template-integration.test.mjs +1072 -3
- package/api/template/template.doc.mjs +27 -7
- package/api/template/template.mjs +45 -8
- package/api/template/template.type.d.mts +6 -8
- package/api/template/template.type.mjs +3 -2
- package/api/theme/template/template.test.mjs +5 -0
- package/api/theme/themeTemplate.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +2 -2
- package/assets/docs/layout.doc.dense.mjs +2 -2
- package/assets/docs/layout.doc.mjs +1 -1
- package/assets/docs/principles.doc.mjs +6 -6
- package/assets/docs/styling-libraries.doc.mjs +3 -3
- package/assets/docs/styling.doc.mjs +4 -4
- package/assets/docs/theme.doc.mjs +4 -4
- package/assets/docs/tokens.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +30 -0
- package/assets/docs/tree/cli.doc.mjs +23 -0
- package/assets/docs/tree/commands.doc.mjs +25 -0
- package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +37 -9
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/working-with-ai.doc.mjs +3 -3
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
- package/authoring/debug/parse.d.mts +3 -3
- package/authoring/doctypes/_schema.d.mts +119 -117
- package/authoring/doctypes/_schema.mjs +54 -3
- package/authoring/doctypes/base/graph-fields.doc.mjs +7 -5
- package/authoring/doctypes/base/type.ts +6 -5
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +2 -2
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +1 -1
- package/authoring/doctypes/function/function.doc.mjs +3 -2
- package/authoring/doctypes/function/type.ts +3 -2
- package/authoring/doctypes/load-contract.test.mjs +26 -1
- package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
- package/authoring/doctypes/namespace/parse.test.mjs +23 -25
- package/authoring/doctypes/namespace/type.ts +5 -2
- package/authoring/doctypes/parse.mjs +3 -2
- package/authoring/doctypes/reference/reference.doc.mjs +34 -5
- package/authoring/doctypes/reference/type.ts +28 -11
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +1 -1
- package/authoring/doctypes/template/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +4 -0
- package/authoring/doctypes/template/parse.test.mjs +18 -0
- package/authoring/doctypes/template/template.doc.mjs +6 -0
- package/authoring/doctypes/template/type.ts +8 -0
- package/authoring/identity/identity.doc.mjs +2 -2
- package/authoring/index.d.ts +1 -0
- package/authoring/integration/type.ts +1 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.mjs +1 -1
- package/clients/cli/commands/build.doc.mjs +11 -6
- package/clients/cli/commands/build.mjs +105 -67
- package/clients/cli/commands/build.text-fields.test.mjs +41 -0
- package/clients/cli/commands/component/index.mjs +1 -1
- package/clients/cli/commands/component-ownership.test.mjs +3 -3
- package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
- package/clients/cli/commands/discover.mjs +1 -1
- package/clients/cli/commands/docs.doc.mjs +20 -8
- package/clients/cli/commands/docs.mjs +183 -69
- package/clients/cli/commands/docs.test.mjs +120 -16
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +2 -2
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +14 -7
- package/clients/cli/commands/doctor-integration.test.mjs +75 -1
- package/clients/cli/commands/doctor.mjs +53 -16
- package/clients/cli/commands/integration-add.doc.mjs +10 -0
- package/clients/cli/commands/integration.mjs +1 -0
- package/clients/cli/commands/search.mjs +11 -2
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/template.doc.mjs +23 -6
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/index.mjs +8 -0
- package/clients/cli/lib/manifest.mjs +9 -2
- package/foundation/agent-docs/agent-docs.d.mts +3 -2
- package/foundation/agent-docs/agent-docs.mjs +13 -9
- package/foundation/agent-docs/agent-docs.test.mjs +19 -1
- package/foundation/config/project.d.mts +12 -11
- package/foundation/config/project.mjs +73 -59
- package/foundation/config/project.test.mjs +141 -5
- package/foundation/discovery/authoring-self-docs.d.mts +12 -0
- package/foundation/discovery/authoring-self-docs.mjs +20 -8
- package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
- package/foundation/discovery/cli-self-docs.d.mts +40 -34
- package/foundation/discovery/cli-self-docs.mjs +91 -115
- package/foundation/discovery/cli-self-docs.test.mjs +104 -166
- package/foundation/discovery/component-discovery.d.mts +38 -0
- package/foundation/discovery/component-discovery.mjs +48 -0
- package/foundation/discovery/docs-discovery.d.mts +109 -9
- package/foundation/discovery/docs-discovery.mjs +223 -24
- package/foundation/discovery/docs-discovery.test.mjs +103 -27
- package/foundation/discovery/docs-output-budget.d.mts +2 -2
- package/foundation/discovery/docs-output-budget.mjs +1 -1
- package/foundation/discovery/docs-section-key.d.mts +28 -10
- package/foundation/discovery/docs-section-key.mjs +134 -33
- package/foundation/discovery/docs-section-key.test.mjs +41 -19
- package/foundation/discovery/template-adapter.d.mts +92 -6
- package/foundation/discovery/template-adapter.mjs +453 -17
- package/foundation/discovery/template-adapter.test.mjs +42 -0
- package/foundation/discovery/template-conflict-release.d.mts +13 -0
- package/foundation/discovery/template-conflict-release.mjs +40 -0
- package/foundation/discovery/template-conflict-release.test.mjs +40 -0
- package/foundation/doc-compiler/bundle.mjs +61 -1
- package/foundation/doc-compiler/bundle.test.mjs +21 -10
- package/foundation/doc-compiler/compile.d.mts +113 -132
- package/foundation/doc-compiler/compile.mjs +57 -10
- package/foundation/doc-compiler/diagnostics.mjs +28 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +2 -2
- package/foundation/doc-compiler/inputs.d.mts +2 -2
- package/foundation/doc-compiler/inputs.mjs +6 -1
- package/foundation/doc-compiler/ir.mjs +7 -0
- package/foundation/doc-compiler/lenses.d.mts +8 -5
- package/foundation/doc-compiler/lenses.mjs +50 -4
- package/foundation/doc-compiler/links.d.mts +162 -0
- package/foundation/doc-compiler/links.mjs +294 -0
- package/foundation/doc-compiler/links.test.mjs +192 -0
- package/foundation/doc-compiler/lower-doc.test.mjs +5 -2
- package/foundation/doc-compiler/read.d.mts +3 -2
- package/foundation/doc-compiler/read.mjs +6 -1
- package/foundation/doc-compiler/tree.d.mts +288 -0
- package/foundation/doc-compiler/tree.mjs +876 -0
- package/foundation/doc-compiler/tree.test.mjs +598 -0
- package/foundation/integrations/autolink.test.mjs +1 -1
- package/foundation/integrations/cli-requirement.d.mts +45 -0
- package/foundation/integrations/cli-requirement.mjs +154 -0
- package/foundation/integrations/cli-requirement.test.mjs +84 -0
- package/foundation/integrations/contribution-inventory.d.mts +2 -1
- package/foundation/integrations/contribution-inventory.mjs +10 -4
- package/foundation/integrations/contribution-inventory.test.mjs +19 -0
- package/foundation/integrations/integration-warnings.d.mts +9 -2
- package/foundation/integrations/integration-warnings.mjs +51 -26
- package/foundation/integrations/integration-warnings.test.mjs +74 -1
- package/foundation/integrations/validate-contributions.mjs +25 -27
- package/foundation/response/response-types.doc.d.mts +5 -1
- package/foundation/response/response-types.doc.mjs +19 -10
- package/foundation/text/string-utils.d.mts +8 -0
- package/foundation/text/string-utils.mjs +18 -0
- package/foundation/xle/expand.mjs +1 -1
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +9 -9
- package/assets/docs/cli.doc.mjs +0 -15
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @file Command-level coverage for `astryx doctor integration` authoring checks.
|
|
5
|
+
* @input Integration fixtures and the post-0.7 conflict projection.
|
|
6
|
+
* @output Assertions for human and typed command responses.
|
|
7
|
+
* @position CLI adapter coverage; 0.6.x API shape is verified separately.
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
|
11
|
+
|
|
12
|
+
vi.mock('../../../foundation/discovery/template-conflict-release.mjs', () => ({
|
|
13
|
+
expandedTemplateConflictSchemaActive: () => true,
|
|
14
|
+
}));
|
|
8
15
|
import * as fs from 'node:fs';
|
|
9
16
|
import * as path from 'node:path';
|
|
10
17
|
import {Command} from 'commander';
|
|
@@ -31,6 +38,8 @@ function writeIntegration({
|
|
|
31
38
|
id,
|
|
32
39
|
name = 'Integration template',
|
|
33
40
|
missingRoot = false,
|
|
41
|
+
replacements,
|
|
42
|
+
type = 'page',
|
|
34
43
|
}) {
|
|
35
44
|
fs.writeFileSync(
|
|
36
45
|
path.join(tmpDir, 'package.json'),
|
|
@@ -45,7 +54,7 @@ function writeIntegration({
|
|
|
45
54
|
fs.mkdirSync(path.dirname(stem), {recursive: true});
|
|
46
55
|
fs.writeFileSync(
|
|
47
56
|
`${stem}.doc.mjs`,
|
|
48
|
-
`export default {type: '
|
|
57
|
+
`export default {type: '${type}', name: ${JSON.stringify(name)}, description: 'fixture'${replacements?.[id] == null ? '' : `, replaces: ${JSON.stringify(replacements[id])}`}};\n`,
|
|
49
58
|
);
|
|
50
59
|
fs.writeFileSync(
|
|
51
60
|
`${stem}.tsx`,
|
|
@@ -249,6 +258,71 @@ describe('doctor integration — command', () => {
|
|
|
249
258
|
expect(process.exitCode).toBeUndefined();
|
|
250
259
|
});
|
|
251
260
|
|
|
261
|
+
it('templates reports an intentional replacement and the Core-original command', async () => {
|
|
262
|
+
const core = (await discoverCoreTemplates()).find(
|
|
263
|
+
template => template.type === 'page',
|
|
264
|
+
);
|
|
265
|
+
expect(core).toBeDefined();
|
|
266
|
+
writeIntegration({
|
|
267
|
+
id: 'acme-app-shell',
|
|
268
|
+
type: core.type,
|
|
269
|
+
replacements: {'acme-app-shell': core.dirName},
|
|
270
|
+
});
|
|
271
|
+
process.chdir(tmpDir);
|
|
272
|
+
|
|
273
|
+
await createProgram().parseAsync([
|
|
274
|
+
'node',
|
|
275
|
+
'astryx',
|
|
276
|
+
'--json',
|
|
277
|
+
'doctor',
|
|
278
|
+
'integration',
|
|
279
|
+
'templates',
|
|
280
|
+
]);
|
|
281
|
+
|
|
282
|
+
const parsed = JSON.parse(logCalls.join('\n'));
|
|
283
|
+
expect(parsed.data.issues).toEqual([]);
|
|
284
|
+
expect(parsed.data.conflicts).toEqual([
|
|
285
|
+
expect.objectContaining({
|
|
286
|
+
id: 'acme-app-shell',
|
|
287
|
+
relationship: 'replaces',
|
|
288
|
+
replaces: core.dirName,
|
|
289
|
+
severity: 'info',
|
|
290
|
+
}),
|
|
291
|
+
]);
|
|
292
|
+
expect(parsed.data.conflicts[0].command).toContain(
|
|
293
|
+
`--package @astryxdesign/core`,
|
|
294
|
+
);
|
|
295
|
+
expect(process.exitCode).toBeUndefined();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('templates exits 1 for a missing replacement target', async () => {
|
|
299
|
+
writeIntegration({
|
|
300
|
+
id: 'acme-app-shell',
|
|
301
|
+
replacements: {'acme-app-shell': 'missing-core-shell'},
|
|
302
|
+
});
|
|
303
|
+
process.chdir(tmpDir);
|
|
304
|
+
|
|
305
|
+
await createProgram().parseAsync([
|
|
306
|
+
'node',
|
|
307
|
+
'astryx',
|
|
308
|
+
'--json',
|
|
309
|
+
'doctor',
|
|
310
|
+
'integration',
|
|
311
|
+
'templates',
|
|
312
|
+
]);
|
|
313
|
+
|
|
314
|
+
const parsed = JSON.parse(logCalls.join('\n'));
|
|
315
|
+
expect(parsed.data.issues).toEqual(
|
|
316
|
+
expect.arrayContaining([
|
|
317
|
+
expect.objectContaining({
|
|
318
|
+
code: 'missing_template_replacement_target',
|
|
319
|
+
severity: 'error',
|
|
320
|
+
}),
|
|
321
|
+
]),
|
|
322
|
+
);
|
|
323
|
+
expect(process.exitCode).toBe(1);
|
|
324
|
+
});
|
|
325
|
+
|
|
252
326
|
it('templates exits 1 when structural errors prevent a trustworthy check', async () => {
|
|
253
327
|
writeIntegration({missingRoot: true});
|
|
254
328
|
process.chdir(tmpDir);
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file `astryx doctor` project health and integration-authoring diagnostics.
|
|
5
5
|
* Human output uses plain stable tokens; every leaf also has a typed JSON
|
|
6
|
-
* response.
|
|
6
|
+
* response. Template-conflict fields follow the package release boundary.
|
|
7
|
+
* Exit 1 means a check found an error, while warnings remain exit 0.
|
|
8
|
+
*
|
|
9
|
+
* @input Typed Doctor and integration-authoring API responses.
|
|
10
|
+
* @output Human-readable records or pass-through typed JSON.
|
|
11
|
+
* @position CLI presentation adapter for project and integration health.
|
|
7
12
|
*/
|
|
8
13
|
|
|
9
14
|
import {runChecks} from '../../../api/doctor/doctor.mjs';
|
|
@@ -125,25 +130,41 @@ function printTemplateConflicts(data) {
|
|
|
125
130
|
),
|
|
126
131
|
...issueBlocks(data.issues),
|
|
127
132
|
];
|
|
128
|
-
if (data.conflicts.length === 0) {
|
|
133
|
+
if (data.conflicts.length === 0 && data.issues.length === 0) {
|
|
129
134
|
output.push(text('[ok] No template ids conflict with Core.'));
|
|
130
|
-
} else {
|
|
135
|
+
} else if (data.conflicts.length > 0) {
|
|
136
|
+
const expanded = data.conflicts.some(
|
|
137
|
+
conflict => 'relationship' in conflict,
|
|
138
|
+
);
|
|
131
139
|
output.push(
|
|
132
140
|
records(data.conflicts, {
|
|
133
|
-
fields:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
fields: expanded
|
|
142
|
+
? [
|
|
143
|
+
'severity',
|
|
144
|
+
'relationship',
|
|
145
|
+
'id',
|
|
146
|
+
'replaces',
|
|
147
|
+
'integrationPackage',
|
|
148
|
+
'integrationType',
|
|
149
|
+
'integrationName',
|
|
150
|
+
'message',
|
|
151
|
+
'command',
|
|
152
|
+
]
|
|
153
|
+
: [
|
|
154
|
+
'severity',
|
|
155
|
+
'id',
|
|
156
|
+
'integrationPackage',
|
|
157
|
+
'integrationType',
|
|
158
|
+
'integrationName',
|
|
159
|
+
'message',
|
|
160
|
+
'command',
|
|
161
|
+
],
|
|
142
162
|
format: {severity: statusToken},
|
|
143
163
|
}),
|
|
144
164
|
text(
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
expanded
|
|
166
|
+
? `${data.conflicts.length} Core template relationship(s).`
|
|
167
|
+
: `${data.conflicts.length} Core template conflict(s). Renaming is recommended but optional; keep the package-qualified command if the overlap is intentional.`,
|
|
147
168
|
),
|
|
148
169
|
);
|
|
149
170
|
}
|
|
@@ -165,7 +186,13 @@ function printComponentConflicts(data) {
|
|
|
165
186
|
} else {
|
|
166
187
|
output.push(
|
|
167
188
|
records(data.conflicts, {
|
|
168
|
-
fields: [
|
|
189
|
+
fields: [
|
|
190
|
+
'severity',
|
|
191
|
+
'name',
|
|
192
|
+
'integrationPackage',
|
|
193
|
+
'message',
|
|
194
|
+
'command',
|
|
195
|
+
],
|
|
169
196
|
format: {severity: statusToken},
|
|
170
197
|
}),
|
|
171
198
|
text(
|
|
@@ -187,6 +214,15 @@ function printDocConflicts(data) {
|
|
|
187
214
|
),
|
|
188
215
|
...issueBlocks(data.issues),
|
|
189
216
|
];
|
|
217
|
+
if (
|
|
218
|
+
!data.issues.some(
|
|
219
|
+
issue => issue.severity === 'error' || issue.code === 'invalid_doc_graph',
|
|
220
|
+
)
|
|
221
|
+
) {
|
|
222
|
+
output.push(
|
|
223
|
+
text('[ok] The docs tree and every link in these docs check out.'),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
190
226
|
if (data.findings.length === 0) {
|
|
191
227
|
output.push(text('[ok] No doc topics overlap with Core.'));
|
|
192
228
|
} else {
|
|
@@ -272,7 +308,8 @@ async function runAuthoringCheck(program, pkg, kind) {
|
|
|
272
308
|
const structuralErrors = summarizeIssues(result.data.issues).errors;
|
|
273
309
|
const docErrors =
|
|
274
310
|
result.type === 'integration.doc-conflicts'
|
|
275
|
-
? result.data.findings.filter(finding => finding.severity === 'error')
|
|
311
|
+
? result.data.findings.filter(finding => finding.severity === 'error')
|
|
312
|
+
.length
|
|
276
313
|
: 0;
|
|
277
314
|
if (structuralErrors > 0 || docErrors > 0) process.exitCode = 1;
|
|
278
315
|
return NO_RESULT_SET;
|
|
@@ -50,6 +50,12 @@ export const doc = {
|
|
|
50
50
|
description:
|
|
51
51
|
'Existing doc topic this one extends (letters, digits, _ and -); only valid for doc and not with --replaces',
|
|
52
52
|
},
|
|
53
|
+
{
|
|
54
|
+
flag: '--parent <namespace>',
|
|
55
|
+
param: 'options.parent',
|
|
56
|
+
description:
|
|
57
|
+
"Namespace of this package to place the doc in, as a guide in its `guides` slot; writes the namespace doc when it is missing, and declares the CLI that reads it as an optional `@astryxdesign/cli` peer. Only valid for doc, and not with --replaces or --extends",
|
|
58
|
+
},
|
|
53
59
|
{
|
|
54
60
|
flag: '--to <version>',
|
|
55
61
|
param: 'options.to',
|
|
@@ -79,6 +85,10 @@ export const doc = {
|
|
|
79
85
|
label: 'Extend a doc topic',
|
|
80
86
|
cli: 'astryx integration add doc acme-theming --extends theme',
|
|
81
87
|
},
|
|
88
|
+
{
|
|
89
|
+
label: 'Add a guide to the package\'s own docs section',
|
|
90
|
+
cli: 'astryx integration add doc deploying --parent acme',
|
|
91
|
+
},
|
|
82
92
|
{
|
|
83
93
|
label: 'Add a codemod',
|
|
84
94
|
cli: 'astryx integration add codemod rename-widget --to 1.2.0',
|
|
@@ -49,7 +49,7 @@ export function registerSearch(program) {
|
|
|
49
49
|
|
|
50
50
|
try {
|
|
51
51
|
const project = await Project.load(process.cwd());
|
|
52
|
-
await warnOnIntegrationIssues(project
|
|
52
|
+
await warnOnIntegrationIssues(project, {json});
|
|
53
53
|
} catch {
|
|
54
54
|
// Never let the nudge break the command.
|
|
55
55
|
}
|
|
@@ -115,7 +115,9 @@ export function registerSearch(program) {
|
|
|
115
115
|
const fields = options.verbose
|
|
116
116
|
? [
|
|
117
117
|
'name',
|
|
118
|
+
'section',
|
|
118
119
|
'domain',
|
|
120
|
+
'package',
|
|
119
121
|
'title',
|
|
120
122
|
'displayName',
|
|
121
123
|
'kind',
|
|
@@ -124,16 +126,20 @@ export function registerSearch(program) {
|
|
|
124
126
|
'import',
|
|
125
127
|
'description',
|
|
126
128
|
'command',
|
|
129
|
+
'parent',
|
|
127
130
|
]
|
|
128
131
|
: [
|
|
129
132
|
'name',
|
|
133
|
+
'section',
|
|
130
134
|
'domain',
|
|
135
|
+
'package',
|
|
131
136
|
'title',
|
|
132
137
|
'displayName',
|
|
133
138
|
'kind',
|
|
134
139
|
'import',
|
|
135
140
|
'description',
|
|
136
141
|
'command',
|
|
142
|
+
'parent',
|
|
137
143
|
];
|
|
138
144
|
|
|
139
145
|
// The heading mirrors the JSON: `matchCount` is what matched, and the
|
|
@@ -145,7 +151,10 @@ export function registerSearch(program) {
|
|
|
145
151
|
? `Results for "${q}" (${results.length} of ${matchCount})`
|
|
146
152
|
: `Results for "${q}" (${results.length})`,
|
|
147
153
|
),
|
|
148
|
-
records(results, {
|
|
154
|
+
records(results, {
|
|
155
|
+
fields,
|
|
156
|
+
format: {command: formatCliCommand, parent: formatCliCommand},
|
|
157
|
+
}),
|
|
149
158
|
);
|
|
150
159
|
return answered;
|
|
151
160
|
},
|
|
@@ -89,6 +89,12 @@ describe('enforcement layer 3 — per-command setup nudge', () => {
|
|
|
89
89
|
expect((await runCli(['docs', 'tokens'], tmp)).stderr).not.toMatch(NUDGE);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
it('is quiet in an integration package, which is not an app', async () => {
|
|
93
|
+
asProject();
|
|
94
|
+
write('astryx.integration.mjs', 'export default {};\n');
|
|
95
|
+
expect((await runCli(['docs', 'tokens'], tmp)).stderr).not.toMatch(NUDGE);
|
|
96
|
+
});
|
|
97
|
+
|
|
92
98
|
it('does not nudge for the installer command itself', async () => {
|
|
93
99
|
asProject();
|
|
94
100
|
expect((await runCli(['init'], tmp)).stderr).not.toMatch(NUDGE);
|
|
@@ -18,9 +18,11 @@ export const doc = {
|
|
|
18
18
|
description:
|
|
19
19
|
'One entry point for the template family: with no name it lists the discovered ' +
|
|
20
20
|
'templates; with a name it shows the source or a layout skeleton, or scaffolds it ' +
|
|
21
|
-
'into the project at a target path.
|
|
22
|
-
'
|
|
23
|
-
'
|
|
21
|
+
'into the project at a target path. A configured integration replacement is the ' +
|
|
22
|
+
'default for its Core id; use --package @astryxdesign/core for the original. ' +
|
|
23
|
+
'Narrow other ambiguous names with --type and/or --package. --cdn writes the ' +
|
|
24
|
+
'no-build-step CDN starter page, which ships as an asset rather than as a ' +
|
|
25
|
+
'discovered template.',
|
|
24
26
|
fn: 'template',
|
|
25
27
|
args: [
|
|
26
28
|
{
|
|
@@ -55,7 +57,8 @@ export const doc = {
|
|
|
55
57
|
{
|
|
56
58
|
flag: '--package <pkg>',
|
|
57
59
|
param: 'options.package',
|
|
58
|
-
description:
|
|
60
|
+
description:
|
|
61
|
+
'Narrow to templates from a specific package. Use @astryxdesign/core to select an original hidden by an integration replacement.',
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
64
|
flag: '--skeleton',
|
|
@@ -81,15 +84,29 @@ export const doc = {
|
|
|
81
84
|
],
|
|
82
85
|
examples: [
|
|
83
86
|
{label: 'List templates', cli: 'astryx template --json'},
|
|
87
|
+
{
|
|
88
|
+
label: 'List exact Core ids',
|
|
89
|
+
cli: 'astryx --json template --list --package @astryxdesign/core',
|
|
90
|
+
},
|
|
84
91
|
{
|
|
85
92
|
label: 'Scaffold into the app',
|
|
86
93
|
cli: 'astryx template dashboard ./src/app',
|
|
87
94
|
},
|
|
95
|
+
{
|
|
96
|
+
label: 'Select a replaced Core original',
|
|
97
|
+
cli: 'astryx template shell-side-nav ./src/app --package @astryxdesign/core',
|
|
98
|
+
},
|
|
88
99
|
{label: 'CDN starter page', cli: 'astryx template --cdn'},
|
|
89
|
-
{
|
|
100
|
+
{
|
|
101
|
+
label: 'CDN starter page, elsewhere',
|
|
102
|
+
cli: 'astryx template --cdn public/demo.html',
|
|
103
|
+
},
|
|
90
104
|
],
|
|
91
105
|
exitCodes: [
|
|
92
|
-
{
|
|
106
|
+
{
|
|
107
|
+
code: 0,
|
|
108
|
+
when: 'success, including a CDN page left untouched because it already exists',
|
|
109
|
+
},
|
|
93
110
|
{
|
|
94
111
|
code: 1,
|
|
95
112
|
when: 'unknown or ambiguous template, no source, a path escape, or an existing target without --overwrite',
|
|
@@ -4,12 +4,9 @@
|
|
|
4
4
|
* @file template command — thin CLI wrapper around api/template.mjs
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import * as path from 'node:path';
|
|
8
|
-
import * as fs from 'node:fs';
|
|
9
7
|
import {jsonOut} from '../../../foundation/response/json.mjs';
|
|
10
8
|
import {emit, section, text, records, code} from '../formatters/index.mjs';
|
|
11
9
|
import {cliError} from '../lib/cli-error.mjs';
|
|
12
|
-
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
13
10
|
import {template as templateApi} from '../../../api/template/template.mjs';
|
|
14
11
|
import {Project} from '../../../foundation/config/project.mjs';
|
|
15
12
|
import {warnOnIntegrationIssues} from '../../../foundation/integrations/integration-warnings.mjs';
|
|
@@ -21,18 +18,6 @@ import {doc as templateFn} from '../../../api/template/template.doc.mjs';
|
|
|
21
18
|
|
|
22
19
|
export {discoverTemplates, listTemplates} from '../../../api/template/template.mjs';
|
|
23
20
|
|
|
24
|
-
/**
|
|
25
|
-
* A template entry as produced by the discover* helpers in api/template.mjs.
|
|
26
|
-
* The api layer returns these as `object[]`; this local shape captures the
|
|
27
|
-
* fields the command consumes. Remove once api/template.mjs exports a precise
|
|
28
|
-
* type for its discovered templates.
|
|
29
|
-
* @typedef {object} DiscoveredTemplate
|
|
30
|
-
* @property {'page' | 'block'} type
|
|
31
|
-
* @property {string} dirName
|
|
32
|
-
* @property {string} name
|
|
33
|
-
* @property {string} filePath
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
21
|
/**
|
|
37
22
|
* The discriminated response returned by the template API. The api layer
|
|
38
23
|
* currently declares `{type: string, data: unknown}`; narrow it here to the
|
|
@@ -89,36 +74,11 @@ export function registerTemplate(program) {
|
|
|
89
74
|
// doctor integration validate. Best-effort; suppressed in --json mode.
|
|
90
75
|
try {
|
|
91
76
|
const project = await Project.load(process.cwd());
|
|
92
|
-
await warnOnIntegrationIssues(
|
|
93
|
-
/** @type {Array<import('../../../foundation/integrations/integrations.mjs').LoadedIntegration>} */ (
|
|
94
|
-
project.loadedIntegrations
|
|
95
|
-
),
|
|
96
|
-
{json},
|
|
97
|
-
);
|
|
77
|
+
await warnOnIntegrationIssues(project, {json});
|
|
98
78
|
} catch {
|
|
99
79
|
// Never let the nudge break the command.
|
|
100
80
|
}
|
|
101
81
|
|
|
102
|
-
// Pre-flight overwrite check (only when we'd actually copy a file).
|
|
103
|
-
// The API resolves the destination; we need to mirror its logic
|
|
104
|
-
// narrowly enough to detect collisions before invoking it.
|
|
105
|
-
if (
|
|
106
|
-
name &&
|
|
107
|
-
targetPath &&
|
|
108
|
-
!options.list &&
|
|
109
|
-
!options.skeleton &&
|
|
110
|
-
!options.cdn
|
|
111
|
-
) {
|
|
112
|
-
const collision = await detectTemplateCollision(name, targetPath);
|
|
113
|
-
if (collision && !options.overwrite) {
|
|
114
|
-
const rel = path.relative(process.cwd(), collision) || collision;
|
|
115
|
-
const msg =
|
|
116
|
-
`Refusing to overwrite existing file ${rel}. ` +
|
|
117
|
-
`Re-run with --overwrite (or -f) to replace it.`;
|
|
118
|
-
return cliError(msg, {code: ERROR_CODES.ERR_FILE_EXISTS});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
82
|
/** @type {TemplateResponse} */
|
|
123
83
|
let result;
|
|
124
84
|
try {
|
|
@@ -156,12 +116,14 @@ export function registerTemplate(program) {
|
|
|
156
116
|
// isn't the built-in core package.
|
|
157
117
|
/** @param {import('../../../api/template/template.type.mjs').TemplateListEntry} t */
|
|
158
118
|
const toRow = t => ({
|
|
119
|
+
id: t.id,
|
|
159
120
|
name: t.isReady ? t.name : `${t.name} (WIP)`,
|
|
160
121
|
description: t.description,
|
|
161
122
|
package:
|
|
162
123
|
t.package && t.package !== '@astryxdesign/core' ? t.package : '',
|
|
124
|
+
replaces: t.replaces ?? '',
|
|
163
125
|
});
|
|
164
|
-
const fields = ['name', 'description', 'package'];
|
|
126
|
+
const fields = ['id', 'name', 'description', 'package', 'replaces'];
|
|
165
127
|
emit(
|
|
166
128
|
pages.length > 0 && section('Page Templates'),
|
|
167
129
|
pages.length > 0 && records(pages.map(toRow), {fields}),
|
|
@@ -229,52 +191,3 @@ export function registerTemplate(program) {
|
|
|
229
191
|
},
|
|
230
192
|
});
|
|
231
193
|
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Mirror the destination-resolution logic in api/template.mjs so we can
|
|
235
|
-
* detect a clobber before invoking the API.
|
|
236
|
-
*
|
|
237
|
-
* Returns the absolute destination file path if it already exists, or
|
|
238
|
-
* `null` otherwise (template missing, no collision, or list/skeleton mode).
|
|
239
|
-
*
|
|
240
|
-
* Path-safety enforcement happens inside the API; here we only need
|
|
241
|
-
* enough precision to check existence — a false negative just means the
|
|
242
|
-
* API will catch the issue and abort.
|
|
243
|
-
*
|
|
244
|
-
* @param {string} name
|
|
245
|
-
* @param {string} targetPath
|
|
246
|
-
* @returns {Promise<string | null>}
|
|
247
|
-
*/
|
|
248
|
-
async function detectTemplateCollision(name, targetPath) {
|
|
249
|
-
const {discoverTemplates} = await import('../../../api/template/template.mjs');
|
|
250
|
-
/** @type {DiscoveredTemplate[]} */
|
|
251
|
-
let templates;
|
|
252
|
-
try {
|
|
253
|
-
templates = /** @type {DiscoveredTemplate[]} */ (
|
|
254
|
-
await discoverTemplates(process.cwd())
|
|
255
|
-
);
|
|
256
|
-
} catch {
|
|
257
|
-
return null;
|
|
258
|
-
}
|
|
259
|
-
const match = templates.find(t => t.dirName === name);
|
|
260
|
-
if (!match) return null;
|
|
261
|
-
|
|
262
|
-
// Resolve target as the API will. We can't call assertWithin here
|
|
263
|
-
// because the API does it itself; we just need to know "is there a
|
|
264
|
-
// file at the destination?".
|
|
265
|
-
const resolved = path.resolve(process.cwd(), targetPath);
|
|
266
|
-
|
|
267
|
-
// File-arg branch: targetPath looks like `./foo.tsx`.
|
|
268
|
-
const {isFilePathArg} = await import('../../../foundation/fs/path-safety.mjs');
|
|
269
|
-
let dest;
|
|
270
|
-
if (isFilePathArg(targetPath)) {
|
|
271
|
-
dest = resolved;
|
|
272
|
-
} else {
|
|
273
|
-
const fileName = match.type === 'block'
|
|
274
|
-
? path.basename(match.filePath)
|
|
275
|
-
: 'page.tsx';
|
|
276
|
-
dest = path.join(resolved, fileName);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return fs.existsSync(dest) ? dest : null;
|
|
280
|
-
}
|
package/clients/cli/index.mjs
CHANGED
|
@@ -276,6 +276,12 @@ const commands = [
|
|
|
276
276
|
];
|
|
277
277
|
|
|
278
278
|
const SETUP_NUDGE_EXEMPT = new Set(['init', 'agent-docs']);
|
|
279
|
+
/** An integration package's manifest: the package is not an app to set up. */
|
|
280
|
+
const INTEGRATION_MANIFEST_FILES = [
|
|
281
|
+
'astryx.integration.ts',
|
|
282
|
+
'astryx.integration.mjs',
|
|
283
|
+
'astryx.integration.js',
|
|
284
|
+
];
|
|
279
285
|
|
|
280
286
|
/**
|
|
281
287
|
* Build a fresh, fully-wired Astryx CLI program (root options, hooks, all
|
|
@@ -519,6 +525,8 @@ export async function createProgram() {
|
|
|
519
525
|
if (SETUP_NUDGE_EXEMPT.has(actionCommand.name())) return;
|
|
520
526
|
const cwd = process.cwd();
|
|
521
527
|
if (!fs.existsSync(path.join(cwd, 'package.json'))) return; // not a project
|
|
528
|
+
// An integration package is not an app: `init` is not its next step.
|
|
529
|
+
if (INTEGRATION_MANIFEST_FILES.some(file => fs.existsSync(path.join(cwd, file)))) return;
|
|
522
530
|
if (isAstryxInitialized(cwd)) return; // already set up — stay quiet
|
|
523
531
|
// Same wording as the core/cli postinstall nudges. #4151's getCliInvocation()
|
|
524
532
|
// renders the correct form for THIS project — scoped `npx @astryxdesign/cli`
|
|
@@ -57,7 +57,13 @@ export const RESPONSE_TYPES = {
|
|
|
57
57
|
'component.detail.showcase',
|
|
58
58
|
'component.detail.blocks',
|
|
59
59
|
],
|
|
60
|
-
docs: [
|
|
60
|
+
docs: [
|
|
61
|
+
'docs.list',
|
|
62
|
+
'docs.index',
|
|
63
|
+
'docs.detail',
|
|
64
|
+
'docs.detail.section',
|
|
65
|
+
'docs.node',
|
|
66
|
+
],
|
|
61
67
|
blog: ['blog.list', 'blog.detail'],
|
|
62
68
|
discover: [
|
|
63
69
|
'discover.list',
|
|
@@ -110,8 +116,9 @@ const EXAMPLES = {
|
|
|
110
116
|
docs: [
|
|
111
117
|
'astryx docs',
|
|
112
118
|
'astryx docs spacing --json',
|
|
113
|
-
'astryx docs theme
|
|
119
|
+
'astryx docs theme',
|
|
114
120
|
'astryx docs theme quick-start',
|
|
121
|
+
'astryx docs cli/integrations --full',
|
|
115
122
|
],
|
|
116
123
|
discover: ['astryx discover --json'],
|
|
117
124
|
search: [
|
|
@@ -78,8 +78,9 @@ export function detectStylingSystem(targetDir: string): "stylex" | "tailwind" |
|
|
|
78
78
|
* Generate the agent cheat sheet from live CLI metadata.
|
|
79
79
|
*
|
|
80
80
|
* Structured as: workflow (behavioral) → rules (error prevention) → CLI reference.
|
|
81
|
-
* Templates
|
|
82
|
-
*
|
|
81
|
+
* Templates lead the workflow: every page starts from a scaffolded template,
|
|
82
|
+
* because the template already carries the frame and spacing that an agent
|
|
83
|
+
* composing from components would have to re-derive, and usually gets wrong.
|
|
83
84
|
*
|
|
84
85
|
* `stylingSystem` tailors the custom-styling guidance to what the project has
|
|
85
86
|
* configured (see {@link detectStylingSystem}) so the agent never reaches for a
|
|
@@ -354,8 +354,9 @@ export function detectStylingSystem(targetDir) {
|
|
|
354
354
|
* Generate the agent cheat sheet from live CLI metadata.
|
|
355
355
|
*
|
|
356
356
|
* Structured as: workflow (behavioral) → rules (error prevention) → CLI reference.
|
|
357
|
-
* Templates
|
|
358
|
-
*
|
|
357
|
+
* Templates lead the workflow: every page starts from a scaffolded template,
|
|
358
|
+
* because the template already carries the frame and spacing that an agent
|
|
359
|
+
* composing from components would have to re-derive, and usually gets wrong.
|
|
359
360
|
*
|
|
360
361
|
* `stylingSystem` tailors the custom-styling guidance to what the project has
|
|
361
362
|
* configured (see {@link detectStylingSystem}) so the agent never reaches for a
|
|
@@ -428,17 +429,19 @@ export function generateCompressedIndex(
|
|
|
428
429
|
lines.push(' import "@astryxdesign/core/astryx.css";');
|
|
429
430
|
lines.push('');
|
|
430
431
|
|
|
431
|
-
// Workflow — `build` is the front door
|
|
432
|
-
|
|
433
|
-
lines.push('
|
|
434
|
-
lines.push('
|
|
435
|
-
lines.push('
|
|
432
|
+
// Workflow — `build` is the front door, and every page starts from the
|
|
433
|
+
// template it names.
|
|
434
|
+
lines.push('WORKFLOW — start every page from a template. Never lay out a page from scratch:');
|
|
435
|
+
lines.push('1. `astryx build "<idea>"` — START HERE: names the [page] template to start from (always one: the closest match, or the app shell), two other templates, and the [block]s + [component]s for parts it lacks. No args = full playbook.');
|
|
436
|
+
lines.push('2. `astryx template <name> <path>` — scaffold that template into your project. Keep its frame, gap and padding; replace its data, copy and sections; delete sections you do not need.');
|
|
437
|
+
lines.push('3. `astryx template <Block>` for a part the template lacks; `astryx component <Name>` for props + examples before you use or change a component.');
|
|
438
|
+
lines.push('Changing a page you already have? Keep it: skip step 2 and add blocks and components inside its sections.');
|
|
436
439
|
lines.push('');
|
|
437
440
|
|
|
438
441
|
// Rules — the top error-preventers.
|
|
439
442
|
lines.push('RULES:');
|
|
440
443
|
lines.push('- No <div> — components do all layout/spacing, page frame included.');
|
|
441
|
-
lines.push('- Frame first:
|
|
444
|
+
lines.push('- Frame first: the template you scaffold sets the page frame. Read `astryx docs layout` before you change it — region widths, breakpoint behavior.');
|
|
442
445
|
lines.push('- Dense data = rows (Table, List/Item), never Card-wrapped list items; Card is for standalone widgets. Status = StatusDot/Token; Badge = counts only.');
|
|
443
446
|
// Styling guidance tailored to the project's configured system — never
|
|
444
447
|
// recommend a path that isn't compiled here (xstyle needs the StyleX compiler;
|
|
@@ -463,7 +466,7 @@ export function generateCompressedIndex(
|
|
|
463
466
|
css: 'replace any raw <div>/<span> layout, imported .css/@apply, or hardcoded value (#hex, 16px) with the component or a token (var(--color-*|--spacing-*|…))',
|
|
464
467
|
};
|
|
465
468
|
lines.push(
|
|
466
|
-
`- SELF-CHECK before you finish: re-read the file and ${selfCheckFix[stylingSystem] ?? selfCheckFix.css}. If unsure a component/prop exists, run \`astryx component <Name>\` / \`astryx search "<thing>"\`; don't hand-roll CSS.`,
|
|
469
|
+
`- SELF-CHECK before you finish: re-read the file and ${selfCheckFix[stylingSystem] ?? selfCheckFix.css}. Confirm the page kept its template's frame, gap and padding. If unsure a component/prop exists, run \`astryx component <Name>\` / \`astryx search "<thing>"\`; don't hand-roll CSS.`,
|
|
467
470
|
);
|
|
468
471
|
lines.push('');
|
|
469
472
|
|
|
@@ -486,6 +489,7 @@ export function generateCompressedIndex(
|
|
|
486
489
|
if (resolvedTopics.length > 0) {
|
|
487
490
|
lines.push(` docs <topic> ${resolvedTopics.join(', ')}`);
|
|
488
491
|
}
|
|
492
|
+
lines.push(' docs cli commands, API reference, integration authoring (one level at a time)');
|
|
489
493
|
lines.push(' swizzle <Name> eject component source for deep customization');
|
|
490
494
|
lines.push(' upgrade --apply run after any Astryx or integration dependency bump');
|
|
491
495
|
const appendCount = agentDocs.reduce(
|