@astryxdesign/cli 0.7.2 → 0.7.3-canary.0bf23bd
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 +104 -71
- package/api/build/_adapter.d.mts +58 -0
- package/api/build/_adapter.mjs +115 -3
- package/api/build/blocks/blocks.d.mts +42 -0
- package/api/build/blocks/blocks.mjs +169 -0
- package/api/build/blocks/blocks.test.mjs +108 -0
- package/api/build/blocks/blocks.weights.json +1 -0
- package/api/build/build.mjs +25 -6
- package/api/build/kit/answer.d.mts +14 -0
- package/api/build/kit/answer.mjs +85 -0
- package/api/build/kit/answer.test.mjs +38 -0
- package/api/build/kit/kind.d.mts +25 -0
- package/api/build/kit/kind.mjs +170 -0
- package/api/build/kit/kit.d.mts +2 -4
- package/api/build/kit/kit.mjs +8 -398
- package/api/build/kit/rank.d.mts +3 -32
- package/api/build/kit/rank.mjs +25 -342
- package/api/build/kit/rank.test.mjs +2 -1
- package/api/build/kit/signals.d.mts +60 -0
- package/api/build/kit/signals.mjs +284 -0
- package/api/build/kit/start.d.mts +32 -0
- package/api/build/kit/start.mjs +163 -0
- package/api/build/kit/words.d.mts +72 -0
- package/api/build/kit/words.mjs +202 -0
- package/api/build/tree.d.mts +20 -0
- package/api/build/tree.mjs +52 -0
- package/api/build/tree.test.mjs +177 -0
- package/api/build/tree.weights.json +1 -0
- package/api/component/component.mjs +17 -3
- package/api/component/component.test.mjs +15 -0
- package/api/component/doc-links.test.mjs +2 -2
- package/api/discover/_adapter.mjs +36 -9
- package/api/discover/_adapter.test.mjs +42 -0
- package/api/docs/_adapter.d.mts +2 -2
- package/api/docs/_adapter.mjs +78 -23
- package/api/docs/docs-homes.test.mjs +127 -0
- package/api/docs/docs.doc.mjs +3 -3
- package/api/docs/docs.test.mjs +35 -19
- package/api/docs/integration-tree.test.mjs +20 -20
- package/api/docs/integrationDocs.test.mjs +1 -1
- package/api/docs/namespace-section.test.mjs +6 -6
- package/api/docs/reference-blocks.test.mjs +2 -2
- package/api/doctor/doctor.mjs +102 -24
- package/api/doctor/doctor.test.mjs +37 -12
- package/api/hook/hook.mjs +1 -1
- package/api/init/init.doc.mjs +20 -6
- package/api/init/init.test.mjs +18 -2
- package/api/init/init.type.d.mts +3 -3
- package/api/init/init.type.mjs +3 -3
- package/api/init/remove/remove.mjs +17 -4
- package/api/init/remove/remove.test.mjs +114 -2
- package/api/init/run/run.mjs +69 -6
- package/api/integration/_adapter.d.mts +8 -0
- package/api/integration/_adapter.mjs +21 -1
- package/api/integration/authoring-checks.mjs +83 -9
- package/api/integration/authoring-checks.test.mjs +220 -0
- package/api/integration/pack-check.mjs +123 -4
- package/api/integration/pack-check.test.mjs +87 -0
- package/api/integration/validate-integration-case-disk.test.mjs +111 -0
- package/api/integration/validate-integration-gaps.test.mjs +1964 -0
- package/api/integration/validate-integration.d.mts +11 -2
- package/api/integration/validate-integration.mjs +250 -32
- package/api/integration/validate-integration.type.d.mts +59 -1
- package/api/integration/validate-integration.type.mjs +46 -2
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/search/ORDERING.md +28 -0
- package/api/search/_adapter.d.mts +82 -2
- package/api/search/_adapter.mjs +157 -10
- package/api/search/_adapter.test.mjs +173 -0
- package/api/search/_candidate-score.d.mts +40 -0
- package/api/search/_candidate-score.mjs +238 -0
- package/api/search/_cards.json +6098 -0
- package/api/search/_compound.d.mts +50 -0
- package/api/search/_compound.mjs +170 -0
- package/api/search/_doc-searcher.d.mts +4 -0
- package/api/search/_doc-searcher.mjs +23 -0
- package/api/search/_doc-searcher.test.mjs +36 -0
- package/api/search/_doc-searcher.weights.json +1 -0
- package/api/search/_doc-text.d.mts +30 -0
- package/api/search/_doc-text.mjs +87 -0
- package/api/search/_entries.d.mts +52 -0
- package/api/search/_entries.mjs +214 -0
- package/api/search/_features.d.mts +35 -0
- package/api/search/_features.mjs +155 -0
- package/api/search/_gather-components.d.mts +26 -0
- package/api/search/_gather-components.mjs +284 -0
- package/api/search/_gather-docs.d.mts +20 -0
- package/api/search/_gather-docs.mjs +254 -0
- package/api/search/_gather-hooks.d.mts +12 -0
- package/api/search/_gather-hooks.mjs +52 -0
- package/api/search/_gather-templates.d.mts +25 -0
- package/api/search/_gather-templates.mjs +107 -0
- package/api/search/_lexical.d.mts +135 -0
- package/api/search/_lexical.mjs +351 -0
- package/api/search/_main-searcher.d.mts +19 -0
- package/api/search/_main-searcher.mjs +115 -0
- package/api/search/_main-searcher.test.mjs +106 -0
- package/api/search/_main-searcher.weights.json +1 -0
- package/api/search/_module-doc.d.mts +32 -0
- package/api/search/_module-doc.mjs +78 -0
- package/api/search/_order.d.mts +32 -0
- package/api/search/_order.mjs +162 -0
- package/api/search/_order.test.mjs +72 -0
- package/api/search/_priority.d.mts +50 -0
- package/api/search/_priority.mjs +139 -0
- package/api/search/_query-words.d.mts +25 -0
- package/api/search/_query-words.mjs +92 -0
- package/api/search/_query.d.mts +65 -0
- package/api/search/_query.mjs +176 -0
- package/api/search/_result.d.mts +97 -0
- package/api/search/_result.mjs +150 -0
- package/api/search/_score.d.mts +60 -0
- package/api/search/_score.mjs +283 -0
- package/api/search/_synonyms.d.mts +21 -0
- package/api/search/_synonyms.mjs +67 -0
- package/api/search/_template-searcher.d.mts +4 -0
- package/api/search/_template-searcher.mjs +23 -0
- package/api/search/_template-searcher.test.mjs +40 -0
- package/api/search/_template-searcher.weights.json +1 -0
- package/api/search/_titles.d.mts +33 -0
- package/api/search/_titles.mjs +65 -0
- package/api/search/_words.d.mts +64 -0
- package/api/search/_words.mjs +157 -0
- package/api/search/search.d.mts +22 -305
- package/api/search/search.doc.mjs +8 -6
- package/api/search/search.mjs +56 -2482
- package/api/search/search.test.mjs +14 -14
- package/api/search/search.type.d.mts +6 -1
- package/api/search/search.type.mjs +6 -1
- package/api/search/tree.d.mts +15 -0
- package/api/search/tree.mjs +40 -0
- package/api/search/tree.test.mjs +66 -0
- package/api/search/tree.weights.json +1 -0
- package/api/theme/build/build.packed-old-core.test.mjs +1 -0
- package/api/upgrade/_adapter.d.mts +57 -9
- package/api/upgrade/_adapter.mjs +198 -28
- package/api/upgrade/registry/registry.d.mts +10 -4
- package/api/upgrade/registry/registry.mjs +33 -16
- package/api/upgrade/registry/registry.test.mjs +85 -0
- package/api/upgrade/run/run.d.mts +4 -2
- package/api/upgrade/run/run.mjs +13 -2
- package/api/upgrade/run/run.test.mjs +417 -3
- package/api/upgrade/upgrade.doc.mjs +10 -5
- package/api/upgrade/upgrade.mjs +13 -2
- package/api/upgrade/upgrade.test.mjs +194 -0
- package/api/upgrade/upgrade.type.d.mts +5 -3
- package/api/upgrade/upgrade.type.mjs +8 -6
- package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
- package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
- package/assets/docs/author-a-theme.doc.mjs +2 -2
- package/assets/docs/is-my-code-good.doc.mjs +287 -0
- package/assets/docs/tree/add-a-component.doc.mjs +1 -1
- package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
- package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
- package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +6 -0
- package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
- package/assets/docs/tree/block-template.doc.mjs +1 -1
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/building-blocks.doc.mjs +2 -2
- package/assets/docs/tree/check-the-props.doc.mjs +1 -1
- package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
- package/assets/docs/tree/checks.doc.mjs +2 -2
- package/assets/docs/tree/cli-command.doc.mjs +1 -1
- package/assets/docs/tree/cli.doc.mjs +1 -1
- package/assets/docs/tree/codemods.doc.mjs +1 -1
- package/assets/docs/tree/component-family.doc.mjs +1 -1
- package/assets/docs/tree/component-imports.doc.mjs +1 -1
- package/assets/docs/tree/components.doc.mjs +1 -1
- package/assets/docs/tree/configuration.doc.mjs +1 -1
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
- package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
- package/assets/docs/tree/docs.doc.mjs +1 -1
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
- package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
- package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
- package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
- package/assets/docs/tree/global-integrations.doc.mjs +2 -2
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
- package/assets/docs/tree/help.doc.mjs +2 -2
- package/assets/docs/tree/host-entry.doc.mjs +128 -0
- package/assets/docs/tree/links.doc.mjs +1 -1
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +1 -1
- package/assets/docs/tree/publishing.doc.mjs +1 -1
- package/assets/docs/tree/quick-start.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
- package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
- package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/ship.doc.mjs +2 -2
- package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
- package/assets/docs/tree/single-component.doc.mjs +1 -1
- package/assets/docs/tree/start-a-template.doc.mjs +1 -1
- package/assets/docs/tree/subcomponent.doc.mjs +1 -1
- package/assets/docs/tree/template-assets.doc.mjs +1 -1
- package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
- package/assets/docs/tree/template-icons.doc.mjs +1 -1
- package/assets/docs/tree/template-images-media.doc.mjs +1 -1
- package/assets/docs/tree/template-styles.doc.mjs +1 -1
- package/assets/docs/tree/templates.doc.mjs +1 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
- package/assets/docs/tree/themes.doc.mjs +1 -1
- package/assets/docs/tree/tokens-data.doc.mjs +2 -2
- package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
- package/assets/docs/tree/upgrading.doc.mjs +1 -1
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
- package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
- package/assets/docs/tree/versioning.doc.mjs +1 -1
- package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
- package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
- package/assets/docs/use-integrations.doc.mjs +120 -0
- package/assets/docs/working-with-ai.doc.mjs +6 -2
- package/assets/templates/themes/butter/butterTheme.ts +4 -2
- package/assets/templates/themes/stone/stoneTheme.ts +4 -2
- package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
- package/authoring/debug/parse.d.mts +1 -1
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/integration/integration.doc.mjs +1 -1
- package/authoring/integration/type.ts +1 -1
- package/clients/cli/bin/astryx.mjs +7 -78
- package/clients/cli/command-result-coverage.test.mjs +6 -5
- package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
- package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
- package/clients/cli/commands/component.test.mjs +6 -6
- package/clients/cli/commands/docs.doc.mjs +2 -2
- package/clients/cli/commands/docs.mjs +1 -1
- package/clients/cli/commands/docs.test.mjs +14 -14
- package/clients/cli/commands/doctor.mjs +30 -0
- package/clients/cli/commands/doctor.test.mjs +65 -0
- package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
- package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
- package/clients/cli/commands/init.behavior.test.mjs +230 -1
- package/clients/cli/commands/init.doc.mjs +28 -9
- package/clients/cli/commands/init.flag-help.test.mjs +1 -0
- package/clients/cli/commands/init.mjs +2 -2
- package/clients/cli/commands/integration-check.test.mjs +48 -0
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/manifest.mjs +67 -0
- package/clients/cli/commands/outside-project.test.mjs +2 -1
- package/clients/cli/commands/postinstall.doc.mjs +23 -0
- package/clients/cli/commands/postinstall.mjs +31 -0
- package/clients/cli/commands/setup-nudge.test.mjs +61 -0
- package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
- package/clients/cli/commands/upgrade.doc.mjs +4 -2
- package/clients/cli/commands/upgrade.mjs +9 -2
- package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
- package/clients/cli/index.mjs +29 -86
- package/clients/cli/lib/component-format.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +12 -7
- package/clients/cli/lib/exit-codes.test.mjs +5 -0
- package/clients/cli/lib/manifest.test.mjs +12 -0
- package/clients/cli/lib/welcome-box.mjs +50 -0
- package/clients/cli/run.mjs +108 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
- package/foundation/agent-docs/agent-doc-state.mjs +35 -8
- package/foundation/agent-docs/agent-docs.d.mts +100 -12
- package/foundation/agent-docs/agent-docs.mjs +1012 -100
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
- package/foundation/agent-docs/agent-docs.test.mjs +683 -39
- package/foundation/config/project.mjs +20 -22
- package/foundation/config/project.test.mjs +56 -0
- package/foundation/discovery/authoring-self-docs.d.mts +7 -0
- package/foundation/discovery/authoring-self-docs.mjs +16 -4
- package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
- package/foundation/discovery/authoring-surface.d.mts +1 -1
- package/foundation/discovery/authoring-surface.mjs +2 -2
- package/foundation/discovery/authoring-surface.test.mjs +1 -1
- package/foundation/discovery/cli-self-docs.d.mts +2 -1
- package/foundation/discovery/cli-self-docs.mjs +4 -3
- package/foundation/discovery/cli-self-docs.test.mjs +2 -2
- package/foundation/discovery/component-discovery.d.mts +37 -0
- package/foundation/discovery/component-discovery.mjs +80 -5
- package/foundation/discovery/component-loader.d.mts +1 -1
- package/foundation/discovery/docs-discovery.mjs +14 -1
- package/foundation/discovery/module-exports.d.mts +41 -0
- package/foundation/discovery/module-exports.mjs +253 -0
- package/foundation/discovery/runner-allowlist.d.mts +2 -4
- package/foundation/discovery/runner-allowlist.mjs +2 -4
- package/foundation/discovery/runner-commands.d.mts +77 -0
- package/foundation/discovery/runner-commands.mjs +294 -0
- package/foundation/discovery/runner-commands.test.mjs +128 -0
- package/foundation/discovery/stray-pointers.test.mjs +1 -1
- package/foundation/discovery/template-adapter.mjs +18 -1
- package/foundation/discovery/template-needs.mjs +1 -44
- package/foundation/discovery/theme-discovery.mjs +147 -3
- package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
- package/foundation/doc-compiler/read.mjs +59 -1
- package/foundation/doc-compiler/read.test.mjs +11 -2
- package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
- package/foundation/doc-compiler/tree.d.mts +32 -6
- package/foundation/doc-compiler/tree.mjs +77 -43
- package/foundation/doc-compiler/tree.test.mjs +7 -7
- package/foundation/fs/paths.d.mts +70 -8
- package/foundation/fs/paths.mjs +556 -38
- package/foundation/fs/paths.test.mjs +430 -54
- package/foundation/fs/read-record.d.mts +18 -0
- package/foundation/fs/read-record.mjs +131 -0
- package/foundation/fs/read-record.test.mjs +77 -0
- package/foundation/integrations/autolink.d.mts +2 -1
- package/foundation/integrations/autolink.mjs +4 -13
- package/foundation/integrations/computer-integrations.d.mts +6 -3
- package/foundation/integrations/computer-integrations.mjs +38 -9
- package/foundation/integrations/contribution-inventory.d.mts +15 -20
- package/foundation/integrations/contribution-inventory.mjs +41 -5
- package/foundation/integrations/integrations.d.mts +27 -1
- package/foundation/integrations/integrations.mjs +61 -2
- package/foundation/integrations/validate-contributions.d.mts +115 -0
- package/foundation/integrations/validate-contributions.mjs +1168 -13
- package/foundation/release/handoff.d.mts +23 -4
- package/foundation/release/handoff.mjs +175 -8
- package/foundation/release/run-context.mjs +14 -0
- package/foundation/response/response-types.doc.mjs +1 -1
- package/foundation/route/route.d.mts +285 -0
- package/foundation/route/route.mjs +324 -0
- package/foundation/route/route.test.mjs +153 -0
- package/foundation/runtime/hosted-runtime.test.mjs +403 -0
- package/foundation/runtime/locked-versions.d.mts +32 -0
- package/foundation/runtime/locked-versions.mjs +65 -0
- package/foundation/runtime/package-root.d.mts +91 -0
- package/foundation/runtime/package-root.mjs +174 -0
- package/foundation/runtime/package-set.d.mts +62 -0
- package/foundation/runtime/package-set.mjs +130 -0
- package/foundation/runtime/package-set.test.mjs +432 -0
- package/host/door-conformance.test.mjs +274 -0
- package/host/index.d.ts +116 -0
- package/host/index.mjs +295 -0
- package/host/index.test.mjs +302 -0
- package/package.json +14 -9
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
findComponentSource,
|
|
13
13
|
levenshteinDistance,
|
|
14
14
|
findClosestComponents,
|
|
15
|
-
} from './component
|
|
15
|
+
} from './component.mjs';
|
|
16
16
|
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
17
17
|
|
|
18
18
|
const REPO_ROOT = path.resolve(import.meta.dirname, '../../../../..');
|
|
@@ -528,7 +528,7 @@ describe('findExternalComponentDoc', () => {
|
|
|
528
528
|
describe('searchComponents', () => {
|
|
529
529
|
it('finds Collapsible when searching "accordion"', async () => {
|
|
530
530
|
const {searchComponents, discoverComponents} =
|
|
531
|
-
await import('./component
|
|
531
|
+
await import('./component.mjs');
|
|
532
532
|
const components = discoverComponents('packages/core');
|
|
533
533
|
const results = await searchComponents(
|
|
534
534
|
'accordion',
|
|
@@ -542,7 +542,7 @@ describe('searchComponents', () => {
|
|
|
542
542
|
|
|
543
543
|
it('finds Dialog when searching "modal"', async () => {
|
|
544
544
|
const {searchComponents, discoverComponents} =
|
|
545
|
-
await import('./component
|
|
545
|
+
await import('./component.mjs');
|
|
546
546
|
const components = discoverComponents('packages/core');
|
|
547
547
|
const results = await searchComponents(
|
|
548
548
|
'modal',
|
|
@@ -556,7 +556,7 @@ describe('searchComponents', () => {
|
|
|
556
556
|
|
|
557
557
|
it('returns multiple matches for ambiguous terms like "select"', async () => {
|
|
558
558
|
const {searchComponents, discoverComponents} =
|
|
559
|
-
await import('./component
|
|
559
|
+
await import('./component.mjs');
|
|
560
560
|
const components = discoverComponents('packages/core');
|
|
561
561
|
const results = await searchComponents(
|
|
562
562
|
'select',
|
|
@@ -571,7 +571,7 @@ describe('searchComponents', () => {
|
|
|
571
571
|
|
|
572
572
|
it('finds exact name matches with score 100', async () => {
|
|
573
573
|
const {searchComponents, discoverComponents} =
|
|
574
|
-
await import('./component
|
|
574
|
+
await import('./component.mjs');
|
|
575
575
|
const components = discoverComponents('packages/core');
|
|
576
576
|
const results = await searchComponents(
|
|
577
577
|
'Button',
|
|
@@ -584,7 +584,7 @@ describe('searchComponents', () => {
|
|
|
584
584
|
|
|
585
585
|
it('returns empty array for complete gibberish', async () => {
|
|
586
586
|
const {searchComponents, discoverComponents} =
|
|
587
|
-
await import('./component
|
|
587
|
+
await import('./component.mjs');
|
|
588
588
|
const components = discoverComponents('packages/core');
|
|
589
589
|
const results = await searchComponents(
|
|
590
590
|
'zzzzzzzzzzz',
|
|
@@ -58,10 +58,10 @@ export const doc = {
|
|
|
58
58
|
{label: 'Every CLI doc, one line each', cli: 'astryx docs cli --depth all'},
|
|
59
59
|
{
|
|
60
60
|
label: 'The integration guides as one read',
|
|
61
|
-
cli: 'astryx docs
|
|
61
|
+
cli: 'astryx docs authoring --depth all --detail full',
|
|
62
62
|
},
|
|
63
63
|
{label: 'One API function', cli: 'astryx docs cli/api/functions/search'},
|
|
64
|
-
{label: 'A whole guide', cli: 'astryx docs
|
|
64
|
+
{label: 'A whole guide', cli: 'astryx docs authoring/quick-start --full'},
|
|
65
65
|
],
|
|
66
66
|
outsideProject: 'same',
|
|
67
67
|
exitCodes: [
|
|
@@ -535,7 +535,7 @@ function emitNode(node, detail, run) {
|
|
|
535
535
|
text(
|
|
536
536
|
[
|
|
537
537
|
// A child's route is its parent's route and its name, except a flat
|
|
538
|
-
// topic in
|
|
538
|
+
// topic in a topic level, which keeps its own name.
|
|
539
539
|
node.slots.every(slot =>
|
|
540
540
|
slot.children.every(child => child.route.startsWith(`${node.route}/`)),
|
|
541
541
|
)
|
|
@@ -227,7 +227,7 @@ describe('the docs tree, one level at a time', () => {
|
|
|
227
227
|
it('wraps namespace summaries without discarding searchable words', async () => {
|
|
228
228
|
const {status, stdout} = await runCli([
|
|
229
229
|
'docs',
|
|
230
|
-
'
|
|
230
|
+
'authoring/building-blocks/templates',
|
|
231
231
|
]);
|
|
232
232
|
expect(status).toBe(0);
|
|
233
233
|
expect(stdout).toMatch(/^start-a-template +Help others build apps faster/m);
|
|
@@ -274,8 +274,8 @@ describe('the docs tree, one level at a time', () => {
|
|
|
274
274
|
}, SLOW);
|
|
275
275
|
|
|
276
276
|
it('reads the integration guides by their routes, and not by the old name', async () => {
|
|
277
|
-
//
|
|
278
|
-
const level = await runCli(['docs', '
|
|
277
|
+
// authoring is a namespace: one level, its guides by slot.
|
|
278
|
+
const level = await runCli(['docs', 'authoring']);
|
|
279
279
|
expect(level.status).toBe(0);
|
|
280
280
|
expect(level.stdout).toMatch(
|
|
281
281
|
/^quick-start +Create a new integration package/m,
|
|
@@ -283,42 +283,42 @@ describe('the docs tree, one level at a time', () => {
|
|
|
283
283
|
expect(level.stdout).toMatch(/^building-blocks +/m);
|
|
284
284
|
const guide = await runCli([
|
|
285
285
|
'docs',
|
|
286
|
-
'
|
|
286
|
+
'authoring/building-blocks/codemods',
|
|
287
287
|
'--index',
|
|
288
288
|
]);
|
|
289
289
|
expect(guide.status).toBe(0);
|
|
290
290
|
expect(guide.stdout).toMatch(
|
|
291
|
-
/Read one section: .*docs
|
|
291
|
+
/Read one section: .*docs authoring\/building-blocks\/codemods <section>/,
|
|
292
292
|
);
|
|
293
293
|
// A bare read is one level too: the sections, and how to read it all.
|
|
294
294
|
const bare = await runCli([
|
|
295
295
|
'docs',
|
|
296
|
-
'
|
|
296
|
+
'authoring/building-blocks/codemods',
|
|
297
297
|
]);
|
|
298
298
|
expect(bare.status).toBe(0);
|
|
299
299
|
expect(bare.stdout).toBe(guide.stdout);
|
|
300
300
|
expect(bare.stdout).toMatch(
|
|
301
|
-
/Read everything: +.*docs
|
|
301
|
+
/Read everything: +.*docs authoring\/building-blocks\/codemods --full$/m,
|
|
302
302
|
);
|
|
303
303
|
const one = await runCli([
|
|
304
304
|
'docs',
|
|
305
|
-
'
|
|
305
|
+
'authoring/building-blocks/codemods',
|
|
306
306
|
'which-codemods-run',
|
|
307
307
|
]);
|
|
308
308
|
expect(one.status).toBe(0);
|
|
309
309
|
expect(one.stdout).toMatch(/^## Choose when a codemod runs$/m);
|
|
310
310
|
expect(one.stdout).toMatch(
|
|
311
|
-
/^Up: .*docs
|
|
311
|
+
/^Up: .*docs authoring\/building-blocks\/codemods --index$/m,
|
|
312
312
|
);
|
|
313
313
|
expect(one.stdout).toMatch(
|
|
314
|
-
/^Previous: .*docs
|
|
314
|
+
/^Previous: .*docs authoring\/building-blocks\/codemods write-the-transform$/m,
|
|
315
315
|
);
|
|
316
316
|
expect(one.stdout).toMatch(
|
|
317
|
-
/^Next: .*docs
|
|
317
|
+
/^Next: .*docs authoring\/building-blocks\/codemods run-codemods-in-an-app$/m,
|
|
318
318
|
);
|
|
319
319
|
const full = await runCli([
|
|
320
320
|
'docs',
|
|
321
|
-
'
|
|
321
|
+
'authoring/building-blocks/codemods',
|
|
322
322
|
'--full',
|
|
323
323
|
]);
|
|
324
324
|
expect(full.status).toBe(0);
|
|
@@ -343,11 +343,11 @@ describe('the docs tree, one level at a time', () => {
|
|
|
343
343
|
type: 'docs.detail.section',
|
|
344
344
|
data: {id: 'side-panels', title: 'Side panels'},
|
|
345
345
|
});
|
|
346
|
-
const missing = await runCli(['docs', '
|
|
346
|
+
const missing = await runCli(['docs', 'authoring', 'zzzz-nope', '--json']);
|
|
347
347
|
expect(missing.status).toBe(1);
|
|
348
348
|
const error = JSON.parse(missing.stdout);
|
|
349
349
|
expect(error).toMatchObject({code: 'ERR_UNKNOWN_SECTION'});
|
|
350
|
-
expect(error.suggestions.map(s => s.name)).toContain('
|
|
350
|
+
expect(error.suggestions.map(s => s.name)).toContain('authoring/quick-start');
|
|
351
351
|
}, SLOW);
|
|
352
352
|
});
|
|
353
353
|
|
|
@@ -179,6 +179,34 @@ function docTypesBlocks(report, failed) {
|
|
|
179
179
|
return blocks;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
/**
|
|
183
|
+
* The component doc names that could not be judged against their sources.
|
|
184
|
+
* @param {import('../../../api/integration/validate-integration.type.mjs').DocNamesReport | undefined} report
|
|
185
|
+
*/
|
|
186
|
+
function docNamesBlocks(report) {
|
|
187
|
+
if (!report || report.unchecked.length === 0) return [];
|
|
188
|
+
return [
|
|
189
|
+
text(
|
|
190
|
+
`[info] Component names not checked: ${report.unchecked.length} doc(s) whose source exports this check cannot read:`,
|
|
191
|
+
),
|
|
192
|
+
records(report.unchecked, {fields: ['file', 'name', 'reason']}),
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The block component names this check could not judge.
|
|
198
|
+
* @param {import('../../../api/integration/validate-integration.type.mjs').BlockComponentsReport | undefined} report
|
|
199
|
+
*/
|
|
200
|
+
function blockComponentsBlocks(report) {
|
|
201
|
+
if (!report || report.unchecked.length === 0) return [];
|
|
202
|
+
return [
|
|
203
|
+
text(
|
|
204
|
+
`[info] Block component names not checked: ${report.unchecked.length} name(s) this check could not judge:`,
|
|
205
|
+
),
|
|
206
|
+
records(report.unchecked, {fields: ['file', 'name', 'reason']}),
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
|
|
182
210
|
/**
|
|
183
211
|
* @param {import('../../../api/integration/validate-integration.type.mjs').ValidateIntegrationResponse['data']} data
|
|
184
212
|
*/
|
|
@@ -204,6 +232,8 @@ function printIntegrationValidation(data) {
|
|
|
204
232
|
`${warnings} warning${warnings === 1 ? '' : 's'}`,
|
|
205
233
|
),
|
|
206
234
|
...docTypesBlocks(data.docTypes, errors > 0),
|
|
235
|
+
...docNamesBlocks(data.docNames),
|
|
236
|
+
...blockComponentsBlocks(data.blockComponents),
|
|
207
237
|
);
|
|
208
238
|
}
|
|
209
239
|
|
|
@@ -191,6 +191,31 @@ describe('doctor — individual checks', () => {
|
|
|
191
191
|
expect(res.fix).toContain('`npx astryx init --features agents`');
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
+
it('agent-docs: at the top of a home settings folder, the fix moves the project or edits by hand, never init', () => {
|
|
195
|
+
const saved = {HOME: process.env.HOME, USERPROFILE: process.env.USERPROFILE};
|
|
196
|
+
process.env.HOME = tmpDir;
|
|
197
|
+
process.env.USERPROFILE = tmpDir;
|
|
198
|
+
try {
|
|
199
|
+
const project = path.join(tmpDir, '.dotfiles');
|
|
200
|
+
fs.mkdirSync(project);
|
|
201
|
+
fs.writeFileSync(path.join(project, 'package.json'), '{"name":"dots"}');
|
|
202
|
+
for (const content of [null, '# Agents\nno markers here\n']) {
|
|
203
|
+
if (content != null) fs.writeFileSync(path.join(project, 'AGENTS.md'), content);
|
|
204
|
+
const res = checkAgentDocs({cwd: project});
|
|
205
|
+
expect(res.fix).toContain('Agent docs are not written into the home folder');
|
|
206
|
+
expect(res.fix).toContain(
|
|
207
|
+
'Move the project into a folder of its own, or edit the Astryx block in its agent file by hand.',
|
|
208
|
+
);
|
|
209
|
+
expect(res.fix).not.toContain('init --features agents');
|
|
210
|
+
}
|
|
211
|
+
} finally {
|
|
212
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
213
|
+
if (value === undefined) delete process.env[key];
|
|
214
|
+
else process.env[key] = value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
194
219
|
it('agent-docs: WARN when docs exist without XDS markers', () => {
|
|
195
220
|
fs.writeFileSync(path.join(tmpDir, 'AGENTS.md'), '# AGENTS\nno markers here');
|
|
196
221
|
const res = checkAgentDocs({cwd: tmpDir});
|
|
@@ -221,6 +246,25 @@ describe('doctor — individual checks', () => {
|
|
|
221
246
|
);
|
|
222
247
|
});
|
|
223
248
|
|
|
249
|
+
it('agent-docs: PASS from a nested package, reading the root that declares Astryx', () => {
|
|
250
|
+
fs.writeFileSync(
|
|
251
|
+
path.join(tmpDir, 'package.json'),
|
|
252
|
+
JSON.stringify({dependencies: {'@astryxdesign/core': '0.7.0'}}),
|
|
253
|
+
);
|
|
254
|
+
fs.writeFileSync(
|
|
255
|
+
path.join(tmpDir, 'AGENTS.md'),
|
|
256
|
+
'<!-- ASTRYX:START -->\nstuff\n<!-- ASTRYX:END -->\n',
|
|
257
|
+
);
|
|
258
|
+
const functions = path.join(tmpDir, 'functions');
|
|
259
|
+
fs.mkdirSync(path.join(functions, 'src'), {recursive: true});
|
|
260
|
+
fs.writeFileSync(path.join(functions, 'package.json'), '{"name":"functions"}');
|
|
261
|
+
|
|
262
|
+
expect(checkAgentDocs({cwd: path.join(functions, 'src')})).toMatchObject({
|
|
263
|
+
status: 'pass',
|
|
264
|
+
message: `Astryx agent docs section present in ${path.join('..', '..', 'AGENTS.md')}.`,
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
224
268
|
it('agent-docs: reads every file init can write, Hermes included', () => {
|
|
225
269
|
fs.writeFileSync(
|
|
226
270
|
path.join(tmpDir, 'HERMES.md'),
|
|
@@ -232,6 +276,27 @@ describe('doctor — individual checks', () => {
|
|
|
232
276
|
});
|
|
233
277
|
});
|
|
234
278
|
|
|
279
|
+
it('agent-docs: FAIL when GEMINI.md imports an AGENTS.md that holds no block', () => {
|
|
280
|
+
const geminiImport = '<!-- ASTRYX:START -->\n@./AGENTS.md\n<!-- ASTRYX:END -->\n';
|
|
281
|
+
fs.writeFileSync(path.join(tmpDir, 'GEMINI.md'), geminiImport);
|
|
282
|
+
expect(checkAgentDocs({cwd: tmpDir})).toMatchObject({
|
|
283
|
+
status: 'fail',
|
|
284
|
+
message: expect.stringMatching(/^GEMINI\.md imports AGENTS\.md, which holds no Astryx block/),
|
|
285
|
+
fix: expect.stringMatching(/init --features agents/),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// A copy elsewhere does not mend it: Codex and Gemini CLI still get none.
|
|
289
|
+
fs.writeFileSync(path.join(tmpDir, 'CLAUDE.md'), '<!-- ASTRYX:START -->\nstuff\n<!-- ASTRYX:END -->\n');
|
|
290
|
+
fs.writeFileSync(path.join(tmpDir, 'AGENTS.md'), '# Agents\n');
|
|
291
|
+
expect(checkAgentDocs({cwd: tmpDir}).status).toBe('fail');
|
|
292
|
+
|
|
293
|
+
fs.writeFileSync(path.join(tmpDir, 'AGENTS.md'), '<!-- ASTRYX:START -->\nstuff\n<!-- ASTRYX:END -->\n');
|
|
294
|
+
expect(checkAgentDocs({cwd: tmpDir})).toMatchObject({
|
|
295
|
+
status: 'pass',
|
|
296
|
+
message: 'Astryx agent docs section present in AGENTS.md, CLAUDE.md.',
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
235
300
|
it('agent-docs: INFO names what it looked for and where', () => {
|
|
236
301
|
const res = checkAgentDocs({cwd: tmpDir});
|
|
237
302
|
expect(res.status).toBe('info');
|
|
@@ -11,17 +11,17 @@ import {
|
|
|
11
11
|
formatHookCompact,
|
|
12
12
|
formatHookBrief,
|
|
13
13
|
formatHookParams,
|
|
14
|
-
} from '
|
|
15
|
-
import {getCliInvocation} from '
|
|
16
|
-
import {jsonOut} from '
|
|
17
|
-
import {emit, section, text, list, records, code, record} from '
|
|
18
|
-
import {cliError} from '
|
|
19
|
-
import {defineCommand} from '
|
|
20
|
-
import {resultSet} from '
|
|
21
|
-
import {ERROR_CODES} from '
|
|
22
|
-
import {hook as hookApi} from '
|
|
23
|
-
import {doc as hookCommand} from '
|
|
24
|
-
import {doc as hookFn} from '
|
|
14
|
+
} from '../lib/hook-format.mjs';
|
|
15
|
+
import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
|
|
16
|
+
import {jsonOut} from '../../../foundation/response/json.mjs';
|
|
17
|
+
import {emit, section, text, list, records, code, record} from '../formatters/index.mjs';
|
|
18
|
+
import {cliError} from '../lib/cli-error.mjs';
|
|
19
|
+
import {defineCommand} from '../lib/define-command.mjs';
|
|
20
|
+
import {resultSet} from '../../../foundation/debug/index.mjs';
|
|
21
|
+
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
22
|
+
import {hook as hookApi} from '../../../api/hook/hook.mjs';
|
|
23
|
+
import {doc as hookCommand} from './hook.doc.mjs';
|
|
24
|
+
import {doc as hookFn} from '../../../api/hook/hook.doc.mjs';
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* The api layer's hook() widens its return to `{type: string, data: unknown}`,
|
|
@@ -29,9 +29,9 @@ import {doc as hookFn} from '../../../../api/hook/hook.doc.mjs';
|
|
|
29
29
|
* the colocated api/hook/hook.type.mjs to get narrowing + typed data.
|
|
30
30
|
*
|
|
31
31
|
* @typedef {(
|
|
32
|
-
* | import('
|
|
33
|
-
* | import('
|
|
34
|
-
* | import('
|
|
32
|
+
* | import('../../../api/hook/hook.type.mjs').HookListResponse
|
|
33
|
+
* | import('../../../api/hook/hook.type.mjs').HookDetailResponse
|
|
34
|
+
* | import('../../../api/hook/hook.type.mjs').HookDetailParamsResponse
|
|
35
35
|
* )} HookResult
|
|
36
36
|
*/
|
|
37
37
|
|
|
@@ -40,7 +40,7 @@ import {doc as hookFn} from '../../../../api/hook/hook.doc.mjs';
|
|
|
40
40
|
* count is every hook across the groups — names or docs, depending on detail.
|
|
41
41
|
*
|
|
42
42
|
* @param {HookResult} result
|
|
43
|
-
* @returns {import('
|
|
43
|
+
* @returns {import('../../../foundation/debug/command-result.mjs').CommandResult}
|
|
44
44
|
*/
|
|
45
45
|
function summarize(result) {
|
|
46
46
|
if (result.type === 'hook.list') {
|
|
@@ -90,7 +90,7 @@ export function registerHook(program) {
|
|
|
90
90
|
lang, zh,
|
|
91
91
|
}));
|
|
92
92
|
} catch (e) {
|
|
93
|
-
const err = /** @type {import('
|
|
93
|
+
const err = /** @type {import('../../../api/error.mjs').AstryxError} */ (e);
|
|
94
94
|
return cliError(err.message, {suggestions: err.suggestions, code: err.code});
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -112,7 +112,7 @@ export function registerHook(program) {
|
|
|
112
112
|
// The whole view is one markdown document: a `## <category>` heading
|
|
113
113
|
// over each category's concatenated hook docs.
|
|
114
114
|
const groups = result.data.components;
|
|
115
|
-
/** @type {import('
|
|
115
|
+
/** @type {import('../formatters/index.mjs').Block[]} */
|
|
116
116
|
const out = [];
|
|
117
117
|
for (const [cat, items] of Object.entries(groups)) {
|
|
118
118
|
const body = items
|
|
@@ -130,7 +130,7 @@ export function registerHook(program) {
|
|
|
130
130
|
// --detail compact — one record (name + description) per hook,
|
|
131
131
|
// grouped by category.
|
|
132
132
|
const groups = result.data.components;
|
|
133
|
-
/** @type {import('
|
|
133
|
+
/** @type {import('../formatters/index.mjs').Block[]} */
|
|
134
134
|
const out = [];
|
|
135
135
|
for (const [cat, items] of Object.entries(groups)) {
|
|
136
136
|
out.push(section(cat), records(items, {fields: ['name', 'description']}));
|
|
@@ -147,7 +147,7 @@ export function registerHook(program) {
|
|
|
147
147
|
const [cat, hookNames] = Object.entries(groups)[0];
|
|
148
148
|
emit(section(`${cat}:`), list(hookNames));
|
|
149
149
|
} else {
|
|
150
|
-
/** @type {import('
|
|
150
|
+
/** @type {import('../formatters/index.mjs').Block[]} */
|
|
151
151
|
const out = [];
|
|
152
152
|
for (const [category, hookNames] of Object.entries(groups)) {
|
|
153
153
|
out.push(section(category), list(hookNames));
|
|
@@ -197,12 +197,12 @@ export function registerHook(program) {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
// Re-export lib functions for external consumers
|
|
200
|
-
export {discoverHooks, findHookDoc, getAllHookNames} from '
|
|
201
|
-
export {loadDocs} from '
|
|
200
|
+
export {discoverHooks, findHookDoc, getAllHookNames} from '../../../foundation/discovery/hook-discovery.mjs';
|
|
201
|
+
export {loadDocs} from '../../../foundation/discovery/component-loader.mjs';
|
|
202
202
|
export {
|
|
203
203
|
formatHookFull,
|
|
204
204
|
formatHookCompact,
|
|
205
205
|
formatHookBrief,
|
|
206
206
|
formatHookBriefAll,
|
|
207
207
|
formatHookParams,
|
|
208
|
-
} from '
|
|
208
|
+
} from '../lib/hook-format.mjs';
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
resolveImportPath,
|
|
22
22
|
discoverComponents,
|
|
23
23
|
findComponentSource,
|
|
24
|
-
} from './component
|
|
24
|
+
} from './component.mjs';
|
|
25
25
|
import {findCoreDir} from '../../../foundation/fs/paths.mjs';
|
|
26
26
|
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
27
27
|
|
|
@@ -70,10 +70,12 @@ describe('astryx init — default (no flags)', () => {
|
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
describe('astryx init --features', () => {
|
|
73
|
-
it('--features agents writes AGENTS.md
|
|
73
|
+
it('--features agents writes AGENTS.md and the GEMINI.md import of it', async () => {
|
|
74
74
|
const {status} = await runCli(['init', '--features', 'agents'], {cwd: tmpDir});
|
|
75
75
|
expect(status).toBe(0);
|
|
76
76
|
expect(exists('AGENTS.md')).toBe(true);
|
|
77
|
+
expect(read('GEMINI.md')).toBe(`${MARKER_START}\n@./AGENTS.md\n<!-- ASTRYX:END -->\n`);
|
|
78
|
+
expect(exists('theme.template.ts')).toBe(false);
|
|
77
79
|
});
|
|
78
80
|
|
|
79
81
|
it('--features theme writes the annotated theme template', async () => {
|
|
@@ -126,7 +128,234 @@ describe('astryx init --agent <preset>', () => {
|
|
|
126
128
|
});
|
|
127
129
|
});
|
|
128
130
|
|
|
131
|
+
describe('astryx init from a subfolder', () => {
|
|
132
|
+
/** @param {string} dir */
|
|
133
|
+
const astryxProject = dir =>
|
|
134
|
+
fs.writeFileSync(
|
|
135
|
+
path.join(dir, 'package.json'),
|
|
136
|
+
JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
it('writes the agent docs at the project root and reports them from where it ran', async () => {
|
|
140
|
+
astryxProject(tmpDir);
|
|
141
|
+
const sub = path.join(tmpDir, 'src', 'app');
|
|
142
|
+
fs.mkdirSync(sub, {recursive: true});
|
|
143
|
+
|
|
144
|
+
const json = await runCli(['init', '--json'], {cwd: sub});
|
|
145
|
+
expect(json.status).toBe(0);
|
|
146
|
+
expect(JSON.parse(json.stdout).data.docsWritten).toEqual([
|
|
147
|
+
'../../AGENTS.md',
|
|
148
|
+
'../../GEMINI.md',
|
|
149
|
+
]);
|
|
150
|
+
expect(read('AGENTS.md')).toContain(MARKER_START);
|
|
151
|
+
expect(fs.readdirSync(sub)).toEqual([]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('keeps --agent-docs-path relative to where it ran', async () => {
|
|
155
|
+
astryxProject(tmpDir);
|
|
156
|
+
const sub = path.join(tmpDir, 'docs');
|
|
157
|
+
fs.mkdirSync(sub);
|
|
158
|
+
|
|
159
|
+
const json = await runCli(['init', '--agent-docs-path', 'AI.md', '--json'], {cwd: sub});
|
|
160
|
+
expect(JSON.parse(json.stdout).data.docsWritten).toEqual(['AI.md']);
|
|
161
|
+
expect(fs.readFileSync(path.join(sub, 'AI.md'), 'utf8')).toContain(MARKER_START);
|
|
162
|
+
expect(exists('AGENTS.md')).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('astryx init in the home folder', () => {
|
|
167
|
+
const saved = {HOME: process.env.HOME, USERPROFILE: process.env.USERPROFILE};
|
|
168
|
+
const personal = '# My notes\n\nAlways use tabs.\n';
|
|
169
|
+
|
|
170
|
+
beforeEach(() => {
|
|
171
|
+
process.env.HOME = tmpDir;
|
|
172
|
+
process.env.USERPROFILE = tmpDir;
|
|
173
|
+
fs.mkdirSync(path.join(tmpDir, '.claude'), {recursive: true});
|
|
174
|
+
fs.writeFileSync(path.join(tmpDir, '.claude', 'CLAUDE.md'), personal);
|
|
175
|
+
});
|
|
176
|
+
afterEach(() => {
|
|
177
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
178
|
+
if (value === undefined) delete process.env[key];
|
|
179
|
+
else process.env[key] = value;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('writes no agent docs, says why, and keeps the soft-failure exit code', async () => {
|
|
184
|
+
const json = await runCli(['init', '--json'], {cwd: tmpDir});
|
|
185
|
+
expect(json.status).toBe(0);
|
|
186
|
+
const env = JSON.parse(json.stdout);
|
|
187
|
+
expect(env.data.docsWritten).toEqual([]);
|
|
188
|
+
expect(env.data.docsError).toEqual({
|
|
189
|
+
kind: 'install-failed',
|
|
190
|
+
message: expect.stringMatching(/not written into the home folder/),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const text = await runCli(['init'], {cwd: tmpDir});
|
|
194
|
+
expect(text.status).toBe(0);
|
|
195
|
+
expect(text.stderr).toMatch(/not written into the home folder/);
|
|
196
|
+
expect(text.stderr).not.toMatch(/Error:/);
|
|
197
|
+
|
|
198
|
+
expect(read('.claude/CLAUDE.md')).toBe(personal);
|
|
199
|
+
expect(fs.readdirSync(tmpDir)).toEqual(['.claude']);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('does not let a package.json in the home folder pull the docs there', async () => {
|
|
203
|
+
fs.writeFileSync(
|
|
204
|
+
path.join(tmpDir, 'package.json'),
|
|
205
|
+
JSON.stringify({dependencies: {'@astryxdesign/core': '0.7.0'}}),
|
|
206
|
+
);
|
|
207
|
+
const scratch = path.join(tmpDir, 'scratch');
|
|
208
|
+
fs.mkdirSync(scratch);
|
|
209
|
+
|
|
210
|
+
const json = await runCli(['init', '--json'], {cwd: scratch});
|
|
211
|
+
expect(json.status).toBe(0);
|
|
212
|
+
expect(JSON.parse(json.stdout).data.docsWritten).toEqual(['AGENTS.md', 'GEMINI.md']);
|
|
213
|
+
expect(fs.existsSync(path.join(scratch, 'AGENTS.md'))).toBe(true);
|
|
214
|
+
expect(exists('AGENTS.md')).toBe(false);
|
|
215
|
+
expect(read('.claude/CLAUDE.md')).toBe(personal);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('--remove-agents in a folder in home never reaches the home folder', async () => {
|
|
219
|
+
fs.writeFileSync(
|
|
220
|
+
path.join(tmpDir, '.claude', 'CLAUDE.md'),
|
|
221
|
+
`${personal}\n<!-- ASTRYX:START -->\nAstryx v0.7.0\n<!-- ASTRYX:END -->\n`,
|
|
222
|
+
);
|
|
223
|
+
fs.writeFileSync(
|
|
224
|
+
path.join(tmpDir, 'package.json'),
|
|
225
|
+
JSON.stringify({dependencies: {'@astryxdesign/core': '0.7.0'}}),
|
|
226
|
+
);
|
|
227
|
+
const scratch = path.join(tmpDir, 'scratch');
|
|
228
|
+
fs.mkdirSync(scratch);
|
|
229
|
+
|
|
230
|
+
const json = await runCli(['init', '--remove-agents', '--json'], {cwd: scratch});
|
|
231
|
+
expect(JSON.parse(json.stdout).data.removed).toBe(false);
|
|
232
|
+
expect(read('.claude/CLAUDE.md')).toContain('<!-- ASTRYX:START -->');
|
|
233
|
+
|
|
234
|
+
// Run in the home folder itself, it still removes the block there.
|
|
235
|
+
const home = await runCli(['init', '--remove-agents', '--json'], {cwd: tmpDir});
|
|
236
|
+
expect(JSON.parse(home.stdout).data.removed).toBe(true);
|
|
237
|
+
expect(read('.claude/CLAUDE.md')).toBe(personal);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it.each(['.gemini', '.claude'])('refuses the settings folder ~/%s', async folder => {
|
|
241
|
+
const dir = path.join(tmpDir, folder);
|
|
242
|
+
fs.mkdirSync(dir, {recursive: true});
|
|
243
|
+
const before = fs.readdirSync(dir);
|
|
244
|
+
|
|
245
|
+
const json = await runCli(['init', '--json'], {cwd: dir});
|
|
246
|
+
expect(json.status).toBe(0);
|
|
247
|
+
const env = JSON.parse(json.stdout);
|
|
248
|
+
expect(env.data.docsWritten).toEqual([]);
|
|
249
|
+
expect(env.data.docsError.message).toMatch(/settings folder in it outside a project/);
|
|
250
|
+
expect(fs.readdirSync(dir)).toEqual(before);
|
|
251
|
+
expect(read('.claude/CLAUDE.md')).toBe(personal);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe('astryx init in an agent worktree inside a home settings folder', () => {
|
|
256
|
+
const saved = {HOME: process.env.HOME, USERPROFILE: process.env.USERPROFILE};
|
|
257
|
+
|
|
258
|
+
beforeEach(() => {
|
|
259
|
+
process.env.HOME = tmpDir;
|
|
260
|
+
process.env.USERPROFILE = tmpDir;
|
|
261
|
+
});
|
|
262
|
+
afterEach(() => {
|
|
263
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
264
|
+
if (value === undefined) delete process.env[key];
|
|
265
|
+
else process.env[key] = value;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it.each([
|
|
270
|
+
['Codex', path.join('.codex', 'worktrees', 'a1b2c3', 'web')],
|
|
271
|
+
['Cursor', path.join('.cursor', 'worktrees', 'web', 'x9y8')],
|
|
272
|
+
])('writes at the project root of a %s worktree', async (_tool, rel) => {
|
|
273
|
+
const project = path.join(tmpDir, rel);
|
|
274
|
+
fs.mkdirSync(path.join(project, 'src'), {recursive: true});
|
|
275
|
+
fs.writeFileSync(path.join(project, '.git'), 'gitdir: /elsewhere/.git/worktrees/web\n');
|
|
276
|
+
fs.writeFileSync(
|
|
277
|
+
path.join(project, 'package.json'),
|
|
278
|
+
JSON.stringify({name: 'web', dependencies: {'@astryxdesign/core': '0.7.0'}}),
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const json = await runCli(['init', '--json'], {cwd: path.join(project, 'src')});
|
|
282
|
+
expect(json.status).toBe(0);
|
|
283
|
+
const env = JSON.parse(json.stdout);
|
|
284
|
+
expect(env.data.docsWritten).toEqual(['../AGENTS.md', '../GEMINI.md']);
|
|
285
|
+
expect(env.data.docsError).toBeNull();
|
|
286
|
+
|
|
287
|
+
const explicit = await runCli(['init', '--agent-docs-path', 'AI.md', '--json'], {
|
|
288
|
+
cwd: path.join(project, 'src'),
|
|
289
|
+
});
|
|
290
|
+
expect(JSON.parse(explicit.stdout).data.docsWritten).toEqual(['AI.md']);
|
|
291
|
+
|
|
292
|
+
// Nothing reaches the settings folder itself.
|
|
293
|
+
const settings = path.join(tmpDir, rel.split(path.sep)[0]);
|
|
294
|
+
expect(fs.readdirSync(settings)).toEqual(['worktrees']);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe('astryx init and the project root', () => {
|
|
299
|
+
it('writes where it runs when no folder above has a package.json, as before', async () => {
|
|
300
|
+
const notes = path.join(tmpDir, 'notes');
|
|
301
|
+
fs.mkdirSync(notes);
|
|
302
|
+
|
|
303
|
+
const json = await runCli(['init', '--json'], {cwd: notes});
|
|
304
|
+
expect(json.status).toBe(0);
|
|
305
|
+
const env = JSON.parse(json.stdout);
|
|
306
|
+
expect(env.data.docsWritten).toEqual(['AGENTS.md', 'GEMINI.md']);
|
|
307
|
+
expect(env.data.docsError).toBeNull();
|
|
308
|
+
expect(fs.readdirSync(notes).sort()).toEqual(['AGENTS.md', 'GEMINI.md']);
|
|
309
|
+
expect(fs.readdirSync(tmpDir)).toEqual(['notes']);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('writes at the project root that declares Astryx, past a nested package that does not', async () => {
|
|
313
|
+
fs.writeFileSync(
|
|
314
|
+
path.join(tmpDir, 'package.json'),
|
|
315
|
+
JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}),
|
|
316
|
+
);
|
|
317
|
+
const functions = path.join(tmpDir, 'functions');
|
|
318
|
+
fs.mkdirSync(path.join(functions, 'src'), {recursive: true});
|
|
319
|
+
fs.writeFileSync(path.join(functions, 'package.json'), '{"name":"functions"}\n');
|
|
320
|
+
|
|
321
|
+
const json = await runCli(['init', '--json'], {cwd: path.join(functions, 'src')});
|
|
322
|
+
expect(JSON.parse(json.stdout).data.docsWritten).toEqual([
|
|
323
|
+
'../../AGENTS.md',
|
|
324
|
+
'../../GEMINI.md',
|
|
325
|
+
]);
|
|
326
|
+
expect(fs.readdirSync(functions).sort()).toEqual(['package.json', 'src']);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
129
330
|
describe('astryx init --remove-agents', () => {
|
|
331
|
+
it('run in a subfolder, removes what init wrote at the project root, with the same JSON shape', async () => {
|
|
332
|
+
fs.writeFileSync(
|
|
333
|
+
path.join(tmpDir, 'package.json'),
|
|
334
|
+
JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}),
|
|
335
|
+
);
|
|
336
|
+
const sub = path.join(tmpDir, 'src', 'app');
|
|
337
|
+
fs.mkdirSync(sub, {recursive: true});
|
|
338
|
+
await runCli(['init'], {cwd: sub});
|
|
339
|
+
expect(exists('AGENTS.md')).toBe(true);
|
|
340
|
+
|
|
341
|
+
const text = await runCli(['init', '--remove-agents'], {cwd: sub});
|
|
342
|
+
expect(text.status).toBe(0);
|
|
343
|
+
expect(text.stdout).toMatch(/Removed empty \.\.\/\.\.\/AGENTS\.md/);
|
|
344
|
+
expect(exists('AGENTS.md')).toBe(false);
|
|
345
|
+
expect(exists('GEMINI.md')).toBe(false);
|
|
346
|
+
|
|
347
|
+
await runCli(['init'], {cwd: sub});
|
|
348
|
+
const json = await runCli(['init', '--remove-agents', '--json'], {cwd: sub});
|
|
349
|
+
expect(json.status).toBe(0);
|
|
350
|
+
// meta.answeredBy names the release that answered (spec AST-081).
|
|
351
|
+
expect(JSON.parse(json.stdout)).toEqual({
|
|
352
|
+
apiVersion: 1,
|
|
353
|
+
type: 'init.remove',
|
|
354
|
+
data: {removed: true},
|
|
355
|
+
meta: {answeredBy: expect.any(Object)},
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
130
359
|
it('removes the Astryx section it previously installed', async () => {
|
|
131
360
|
await runCli(['init', '--features', 'agents'], {cwd: tmpDir});
|
|
132
361
|
expect(exists('AGENTS.md')).toBe(true);
|