@astryxdesign/cli 0.7.2-canary.fe2c655 → 0.7.2
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/CHANGELOG.md +119 -0
- package/README.md +175 -154
- package/api/build/_adapter.d.mts +8 -0
- package/api/build/_adapter.mjs +12 -1
- package/api/build/build.doc.mjs +4 -0
- package/api/build/help/help.mjs +1 -1
- package/api/build/kit/kit.mjs +3 -11
- package/api/component/_adapter.d.mts +72 -1
- package/api/component/_adapter.mjs +212 -18
- package/api/component/component.doc.mjs +6 -1
- package/api/component/component.mjs +82 -15
- package/api/component/component.test.mjs +80 -0
- package/api/component/doc-links.test.mjs +386 -0
- package/api/component/integration-export-name.test.mjs +644 -0
- package/api/component/list/list.mjs +42 -4
- package/api/discover/_adapter.d.mts +132 -12
- package/api/discover/_adapter.mjs +488 -27
- package/api/discover/_adapter.test.mjs +599 -17
- package/api/discover/_catalog-view.d.mts +16 -5
- package/api/discover/_catalog-view.mjs +32 -8
- package/api/discover/detail/doc/doc.mjs +9 -1
- package/api/discover/discover.d.mts +11 -0
- package/api/discover/discover.doc.mjs +9 -7
- package/api/discover/discover.mjs +121 -19
- package/api/discover/discover.type.d.mts +20 -7
- package/api/discover/discover.type.mjs +17 -7
- package/api/discover/list/list.d.mts +4 -3
- package/api/discover/list/list.mjs +17 -10
- package/api/docs/_adapter.d.mts +74 -0
- package/api/docs/_adapter.mjs +553 -16
- package/api/docs/docs.doc.mjs +2 -1
- package/api/docs/docs.mjs +2 -1
- package/api/docs/docs.test.mjs +5 -2
- package/api/docs/integration-tree.test.mjs +22 -0
- package/api/docs/integrationDocs.test.mjs +124 -0
- package/api/docs/list/list.mjs +12 -2
- package/api/docs/reference-blocks.test.mjs +1 -3
- package/api/doctor/_adapter.d.mts +108 -0
- package/api/doctor/_adapter.mjs +223 -4
- package/api/doctor/doctor.d.mts +199 -0
- package/api/doctor/doctor.doc.mjs +3 -2
- package/api/doctor/doctor.mjs +530 -34
- package/api/doctor/doctor.test.mjs +180 -14
- package/api/doctor/release-checks.test.mjs +156 -0
- package/api/doctor/theme-checks.mjs +17 -4
- package/api/doctor/theme-checks.test.mjs +223 -7
- package/api/error.d.mts +10 -0
- package/api/error.mjs +25 -0
- package/api/gap-report/gap-report.d.mts +3 -0
- package/api/gap-report/gap-report.doc.mjs +11 -3
- package/api/gap-report/gap-report.mjs +219 -52
- package/api/gap-report/gap-report.test.mjs +233 -0
- package/api/gap-report/gap-report.type.d.mts +41 -0
- package/api/gap-report/gap-report.type.mjs +18 -0
- package/api/hook/_adapter.mjs +3 -11
- package/api/hook/hook.doc.mjs +4 -0
- package/api/hook/list/list.d.mts +1 -1
- package/api/index.d.mts +2 -0
- package/api/index.mjs +2 -0
- package/api/init/_adapter.d.mts +22 -0
- package/api/init/_adapter.mjs +59 -0
- package/api/init/init.d.mts +7 -5
- package/api/init/init.doc.mjs +28 -7
- package/api/init/init.mjs +28 -4
- package/api/init/init.type.d.mts +48 -3
- package/api/init/init.type.mjs +37 -3
- package/api/init/run/run.d.mts +1 -1
- package/api/init/run/run.mjs +17 -6
- package/api/init/user/user.d.mts +14 -0
- package/api/init/user/user.mjs +87 -0
- package/api/init/user/user.test.mjs +232 -0
- package/api/integration/_adapter.d.mts +45 -0
- package/api/integration/_adapter.mjs +112 -0
- package/api/integration/add-contribution.component-names.test.mjs +66 -0
- package/api/integration/add-contribution.mjs +8 -4
- package/api/integration/add-contribution.test.mjs +4 -2
- package/api/integration/authoring-checks.mjs +110 -6
- package/api/integration/authoring-checks.test.mjs +808 -0
- package/api/integration/authoring-checks.type.d.mts +143 -0
- package/api/integration/authoring-checks.type.mjs +74 -2
- package/api/integration/check/check.d.mts +96 -0
- package/api/integration/check/check.mjs +357 -0
- package/api/integration/check/check.test.mjs +489 -0
- package/api/integration/integration-check.type.d.mts +118 -0
- package/api/integration/integration-check.type.mjs +84 -0
- package/api/integration/integrationCheck.doc.d.mts +10 -0
- package/api/integration/integrationCheck.doc.mjs +73 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
- package/api/integration/integrationDocConflicts.doc.mjs +22 -5
- package/api/integration/pack-check.mjs +364 -120
- package/api/integration/pack-check.test.mjs +730 -10
- package/api/integration/validate-integration-fixes.test.mjs +67 -0
- package/api/integration/validate-integration.d.mts +8 -0
- package/api/integration/validate-integration.mjs +63 -9
- package/api/integration/validate-integration.test.mjs +88 -8
- package/api/integration/validate-integration.type.d.mts +16 -0
- package/api/integration/validate-integration.type.mjs +14 -1
- package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
- package/api/integration/validateIntegration.doc.mjs +19 -3
- package/api/search/_adapter.d.mts +11 -0
- package/api/search/_adapter.mjs +25 -0
- package/api/search/search.d.mts +82 -13
- package/api/search/search.doc.mjs +8 -3
- package/api/search/search.domain-priority.test.mjs +108 -18
- package/api/search/search.integration-check.test.mjs +24 -0
- package/api/search/search.mjs +645 -64
- package/api/search/search.test.mjs +179 -1
- package/api/search/search.type.d.mts +14 -1
- package/api/search/search.type.mjs +6 -1
- package/api/swizzle/_adapter.mjs +2 -2
- package/api/swizzle/copy/copy.mjs +29 -15
- package/api/template/template-integration.test.mjs +1 -1
- package/api/template/template.test.mjs +120 -3
- package/api/theme/_adapter.d.mts +16 -0
- package/api/theme/_adapter.mjs +32 -5
- package/api/theme/build/build.d.mts +2 -1
- package/api/theme/build/build.family.test.mjs +64 -0
- package/api/theme/build/build.mjs +250 -38
- package/api/theme/build/build.packed-old-core.test.mjs +20 -3
- package/api/theme/build/font-warning.mjs +5 -3
- package/api/theme/build/font-warning.test.mjs +2 -1
- package/api/theme/remove/remove.test.mjs +2 -2
- package/api/theme/targets/targets.mjs +3 -10
- package/api/theme/themeBuild.doc.mjs +4 -4
- package/api/theme/themeTargets.doc.mjs +4 -0
- package/api/theme/use/use.test.mjs +1 -1
- package/api/upgrade/_adapter.d.mts +40 -2
- package/api/upgrade/_adapter.mjs +118 -5
- package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
- package/api/upgrade/run/run.mjs +35 -2
- package/api/upgrade/status/status.d.mts +21 -5
- package/api/upgrade/status/status.mjs +45 -8
- package/api/upgrade/upgrade.doc.mjs +6 -3
- package/api/upgrade/upgrade.mjs +5 -1
- package/api/upgrade/upgrade.type.d.mts +23 -0
- package/api/upgrade/upgrade.type.mjs +16 -1
- package/assets/codemods/__tests__/registry.test.mjs +1 -0
- package/assets/codemods/registry.mjs +1 -0
- package/assets/codemods/runner.mjs +7 -1
- package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
- package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
- package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
- package/assets/discover/official-packages.json +367 -0
- package/assets/docs/README.md +10 -0
- package/assets/docs/author-a-theme.doc.mjs +3 -3
- package/assets/docs/browser-support.doc.mjs +1 -1
- package/assets/docs/getting-started.doc.mjs +26 -6
- package/assets/docs/styling-overview.doc.mjs +1 -1
- package/assets/docs/theme.doc.dense.mjs +1 -1
- package/assets/docs/theme.doc.mjs +2 -2
- package/assets/docs/theme.doc.zh.mjs +1 -1
- package/assets/docs/tree/add-a-component.doc.mjs +22 -2
- package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
- package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
- package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
- package/assets/docs/tree/block-template.doc.mjs +8 -20
- package/assets/docs/tree/build-the-template.doc.mjs +1 -1
- package/assets/docs/tree/building-blocks.doc.mjs +4 -0
- package/assets/docs/tree/check-the-props.doc.mjs +201 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
- package/assets/docs/tree/checks.doc.mjs +32 -24
- package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
- package/assets/docs/tree/cli-command.doc.mjs +142 -0
- package/assets/docs/tree/cli.doc.mjs +6 -0
- package/assets/docs/tree/codemods.doc.mjs +17 -7
- package/assets/docs/tree/commands.doc.mjs +7 -1
- package/assets/docs/tree/component-family.doc.mjs +8 -6
- package/assets/docs/tree/component-imports.doc.mjs +21 -3
- package/assets/docs/tree/components.doc.mjs +7 -1
- package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
- package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
- package/assets/docs/tree/configuration.doc.mjs +7 -3
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
- package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
- package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
- package/assets/docs/tree/docs.doc.mjs +9 -3
- package/assets/docs/tree/document-the-template.doc.mjs +1 -1
- package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
- package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
- package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
- package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
- package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
- package/assets/docs/tree/global-integrations.doc.mjs +124 -0
- package/assets/docs/tree/global.doc.mjs +131 -0
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
- package/assets/docs/tree/install-once.doc.mjs +105 -0
- package/assets/docs/tree/integrations.doc.mjs +13 -1
- package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
- package/assets/docs/tree/layout.doc.mjs +6 -6
- package/assets/docs/tree/links.doc.mjs +7 -6
- package/assets/docs/tree/migration-setup.doc.mjs +3 -2
- package/assets/docs/tree/migration-verification.doc.mjs +0 -1
- package/assets/docs/tree/migration.doc.mjs +0 -1
- package/assets/docs/tree/monorepos.doc.mjs +84 -0
- package/assets/docs/tree/package-and-test.doc.mjs +1 -1
- package/assets/docs/tree/page-template.doc.mjs +4 -8
- package/assets/docs/tree/problems.doc.mjs +205 -0
- package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
- package/assets/docs/tree/publishing.doc.mjs +6 -6
- package/assets/docs/tree/quick-start.doc.mjs +53 -27
- package/assets/docs/tree/reference.doc.mjs +192 -0
- package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
- package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
- package/assets/docs/tree/scaffold.doc.mjs +1 -1
- package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
- package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
- package/assets/docs/tree/single-component.doc.mjs +15 -6
- package/assets/docs/tree/start-a-template.doc.mjs +18 -8
- package/assets/docs/tree/subcomponent.doc.mjs +6 -6
- package/assets/docs/tree/template-assets.doc.mjs +2 -2
- package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
- package/assets/docs/tree/template-fonts.doc.mjs +1 -1
- 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 +2 -2
- package/assets/docs/tree/templates.doc.mjs +7 -1
- package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
- package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
- package/assets/docs/tree/themes.doc.mjs +4 -0
- package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
- package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
- package/assets/docs/tree/type-scale.doc.mjs +3 -3
- package/assets/docs/tree/uninstall.doc.mjs +109 -0
- package/assets/docs/tree/updates.doc.mjs +92 -0
- package/assets/docs/tree/upgrading.doc.mjs +6 -6
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
- package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
- package/assets/docs/tree/versioning.doc.mjs +10 -9
- package/assets/docs/tree/who-answers.doc.mjs +119 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
- package/assets/docs/use-a-theme.doc.mjs +2 -2
- package/assets/docs/working-with-ai.doc.mjs +66 -13
- package/assets/theme.template.ts +1 -1
- package/authoring/codemod/codemod.doc.mjs +7 -3
- package/authoring/config/config.doc.mjs +9 -6
- package/authoring/config/type.ts +4 -2
- package/authoring/debug/parse.d.mts +5 -5
- package/authoring/discover/discover.doc.mjs +7 -1
- package/authoring/doctypes/_schema.d.mts +8 -3
- package/authoring/doctypes/_schema.mjs +1 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
- package/authoring/doctypes/command/command.doc.mjs +20 -12
- package/authoring/doctypes/command/type.ts +14 -0
- package/authoring/doctypes/component/component.doc.mjs +30 -27
- package/authoring/doctypes/component/type.ts +21 -8
- package/authoring/doctypes/enum/enum.doc.mjs +7 -4
- package/authoring/doctypes/function/function.doc.mjs +9 -8
- package/authoring/doctypes/hook/hook.doc.mjs +5 -4
- package/authoring/doctypes/hook/type.ts +4 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
- package/authoring/doctypes/reference/reference.doc.mjs +12 -11
- package/authoring/doctypes/schema/schema.doc.mjs +9 -9
- package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
- package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
- package/authoring/doctypes/template/parse.d.mts +6 -2
- package/authoring/doctypes/template/parse.mjs +3 -1
- package/authoring/doctypes/template/template.doc.mjs +3 -3
- package/authoring/doctypes/template/type.ts +22 -0
- package/authoring/doctypes/theme/theme.doc.mjs +2 -2
- package/authoring/gap-report/gap-report.doc.mjs +2 -2
- package/authoring/identity/identity.doc.mjs +3 -3
- package/authoring/index.d.ts +2 -0
- package/authoring/integration/integration.doc.mjs +30 -6
- package/authoring/integration/parse.test.mjs +21 -0
- package/authoring/integration/schema.d.mts +16 -3
- package/authoring/integration/schema.mjs +34 -2
- package/authoring/integration/type.ts +12 -0
- package/clients/cli/bin/astryx.mjs +20 -3
- package/clients/cli/cli-command.test.mjs +166 -0
- package/clients/cli/commands/blog.doc.mjs +1 -0
- package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
- package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
- package/clients/cli/commands/build-theme.mjs +26 -13
- package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
- package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
- package/clients/cli/commands/build.doc.mjs +1 -0
- package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
- package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
- package/clients/cli/commands/component.doc.mjs +1 -0
- package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
- package/clients/cli/commands/discover.doc.mjs +9 -1
- package/clients/cli/commands/discover.mjs +144 -93
- package/clients/cli/commands/discover.no-source.test.mjs +448 -33
- package/clients/cli/commands/discover.sources.test.mjs +20 -1
- package/clients/cli/commands/docs.doc.mjs +1 -0
- package/clients/cli/commands/docs.mjs +49 -19
- package/clients/cli/commands/docs.test.mjs +2 -2
- package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
- package/clients/cli/commands/doctor-integration.test.mjs +888 -3
- package/clients/cli/commands/doctor.doc.mjs +12 -2
- package/clients/cli/commands/doctor.mjs +292 -7
- package/clients/cli/commands/doctor.test.mjs +10 -4
- package/clients/cli/commands/gap-report.doc.mjs +15 -2
- package/clients/cli/commands/gap-report.mjs +24 -2
- package/clients/cli/commands/gap-report.test.mjs +79 -0
- package/clients/cli/commands/hook.doc.mjs +1 -0
- package/clients/cli/commands/init.behavior.test.mjs +5 -0
- package/clients/cli/commands/init.doc.mjs +29 -3
- package/clients/cli/commands/integration-add.doc.mjs +7 -0
- package/clients/cli/commands/integration-authoring.test.mjs +49 -3
- package/clients/cli/commands/integration-check.doc.mjs +93 -0
- package/clients/cli/commands/integration-check.test.mjs +441 -0
- package/clients/cli/commands/integration-pack.doc.mjs +8 -1
- package/clients/cli/commands/integration-verify.doc.mjs +8 -1
- package/clients/cli/commands/integration.doc.mjs +6 -2
- package/clients/cli/commands/integration.mjs +119 -7
- package/clients/cli/commands/json-contract.test.mjs +11 -2
- package/clients/cli/commands/manifest.doc.mjs +1 -0
- package/clients/cli/commands/outside-project.test.mjs +774 -0
- package/clients/cli/commands/search.doc.mjs +1 -0
- package/clients/cli/commands/search.mjs +16 -11
- package/clients/cli/commands/search.test.mjs +429 -296
- package/clients/cli/commands/setup-nudge.test.mjs +102 -2
- package/clients/cli/commands/swizzle.doc.mjs +7 -0
- package/clients/cli/commands/swizzle.mjs +5 -3
- package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
- package/clients/cli/commands/template.doc.mjs +1 -0
- package/clients/cli/commands/text-json-parity.test.mjs +5 -0
- package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
- package/clients/cli/commands/theme-add.doc.mjs +8 -1
- package/clients/cli/commands/theme-build.doc.mjs +3 -2
- package/clients/cli/commands/theme-eject.doc.mjs +1 -0
- package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
- package/clients/cli/commands/theme-list.doc.mjs +1 -0
- package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
- package/clients/cli/commands/theme-remove.doc.mjs +7 -0
- package/clients/cli/commands/theme-targets.doc.mjs +1 -0
- package/clients/cli/commands/theme-template.doc.mjs +1 -0
- package/clients/cli/commands/theme-use.doc.mjs +7 -0
- package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
- package/clients/cli/commands/upgrade.doc.mjs +22 -1
- package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
- package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
- package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
- package/clients/cli/index.mjs +112 -16
- package/clients/cli/lib/cli-error.mjs +4 -1
- package/clients/cli/lib/component-format.test.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +2 -2
- package/clients/cli/lib/json-shim.mjs +3 -1
- package/clients/cli/lib/manifest.d.ts +9 -0
- package/clients/cli/lib/manifest.mjs +17 -1
- package/clients/cli/lib/manifest.test.mjs +25 -0
- package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
- package/foundation/agent-docs/agent-doc-state.mjs +66 -6
- package/foundation/agent-docs/agent-docs.d.mts +4 -3
- package/foundation/agent-docs/agent-docs.mjs +11 -9
- package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
- package/foundation/agent-docs/computer-agent-text.mjs +623 -0
- package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
- package/foundation/config/project.d.mts +35 -3
- package/foundation/config/project.mjs +226 -7
- package/foundation/config/project.test.mjs +66 -0
- package/foundation/config/theme-state.mjs +5 -4
- package/foundation/discovery/authoring-self-docs.mjs +2 -1
- package/foundation/discovery/cli-self-docs.mjs +20 -3
- package/foundation/discovery/cli-self-docs.test.mjs +3 -2
- package/foundation/discovery/component-discovery.d.mts +190 -10
- package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
- package/foundation/discovery/component-discovery.mjs +864 -56
- package/foundation/discovery/component-discovery.test.mjs +96 -0
- package/foundation/discovery/component-replacement.mjs +20 -1
- package/foundation/discovery/component-replacement.test.mjs +40 -0
- package/foundation/discovery/copied-passages.d.mts +216 -0
- package/foundation/discovery/copied-passages.mjs +720 -0
- package/foundation/discovery/copied-passages.test.mjs +124 -0
- package/foundation/discovery/doc-consistency.d.mts +34 -0
- package/foundation/discovery/doc-consistency.mjs +513 -0
- package/foundation/discovery/doc-consistency.test.mjs +347 -0
- package/foundation/discovery/doc-fluff.d.mts +112 -0
- package/foundation/discovery/doc-fluff.mjs +328 -0
- package/foundation/discovery/doc-fluff.test.mjs +142 -0
- package/foundation/discovery/doc-types.d.mts +157 -0
- package/foundation/discovery/doc-types.mjs +1231 -0
- package/foundation/discovery/doc-types.test.mjs +744 -0
- package/foundation/discovery/docs-discovery.d.mts +88 -0
- package/foundation/discovery/docs-discovery.mjs +83 -2
- package/foundation/discovery/integration-doc-files.d.mts +22 -0
- package/foundation/discovery/integration-doc-files.mjs +231 -0
- package/foundation/discovery/integration-doc-files.test.mjs +246 -0
- package/foundation/discovery/module-exports.d.mts +27 -0
- package/foundation/discovery/module-exports.mjs +356 -31
- package/foundation/discovery/module-exports.test.mjs +172 -0
- package/foundation/discovery/package-tsconfig.d.mts +14 -0
- package/foundation/discovery/package-tsconfig.mjs +228 -0
- package/foundation/discovery/package-tsconfig.test.mjs +210 -0
- package/foundation/discovery/project-typescript.d.mts +32 -0
- package/foundation/discovery/project-typescript.mjs +89 -0
- package/foundation/discovery/prop-drift.d.mts +297 -0
- package/foundation/discovery/prop-drift.mjs +2428 -0
- package/foundation/discovery/prop-drift.test.mjs +2337 -0
- package/foundation/discovery/runner-allowlist.d.mts +40 -0
- package/foundation/discovery/runner-allowlist.mjs +53 -0
- package/foundation/discovery/stray-pointers.d.mts +52 -0
- package/foundation/discovery/stray-pointers.mjs +293 -0
- package/foundation/discovery/stray-pointers.test.mjs +86 -0
- package/foundation/discovery/template-adapter.mjs +74 -5
- package/foundation/discovery/template-needs.mjs +5 -2
- package/foundation/discovery/theme-imports.mjs +8 -4
- package/foundation/discovery/theme-imports.test.mjs +1 -1
- package/foundation/discovery/type-match.d.mts +53 -0
- package/foundation/discovery/type-match.mjs +660 -0
- package/foundation/discovery/type-match.test.mjs +169 -0
- package/foundation/discovery/type-text.d.mts +68 -0
- package/foundation/discovery/type-text.mjs +658 -0
- package/foundation/discovery/type-text.test.mjs +196 -0
- package/foundation/discovery/unresolved-references.d.mts +62 -0
- package/foundation/discovery/unresolved-references.mjs +790 -0
- package/foundation/discovery/unresolved-references.test.mjs +567 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +51 -1
- package/foundation/doc-compiler/inputs.mjs +27 -1
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/links.d.mts +41 -5
- package/foundation/doc-compiler/links.mjs +167 -28
- package/foundation/doc-compiler/links.test.mjs +97 -4
- package/foundation/doc-compiler/read.d.mts +1 -1
- package/foundation/doc-compiler/tree.mjs +30 -1
- package/foundation/doc-compiler/tree.test.mjs +8 -1
- package/foundation/env/cli-command.d.mts +83 -0
- package/foundation/env/cli-command.mjs +152 -0
- package/foundation/env/cli-command.test.mjs +129 -0
- package/foundation/env/node-version.test.mjs +15 -3
- package/foundation/env/package-manager.d.mts +93 -9
- package/foundation/env/package-manager.global.test.mjs +66 -0
- package/foundation/env/package-manager.mjs +184 -13
- package/foundation/env/package-manager.test.mjs +341 -15
- package/foundation/env/project-scope.d.mts +9 -0
- package/foundation/env/project-scope.mjs +33 -0
- package/foundation/env/search-path.d.mts +42 -0
- package/foundation/env/search-path.mjs +205 -0
- package/foundation/env/search-path.test.mjs +122 -0
- package/foundation/env/user-dirs.d.mts +39 -0
- package/foundation/env/user-dirs.mjs +93 -0
- package/foundation/env/user-dirs.test.mjs +72 -0
- package/foundation/env/user-settings.d.mts +46 -0
- package/foundation/env/user-settings.mjs +106 -0
- package/foundation/env/user-settings.test.mjs +64 -0
- package/foundation/fs/file-protection.mjs +15 -1
- package/foundation/fs/module-loader.d.mts +2 -1
- package/foundation/fs/module-loader.mjs +60 -4
- package/foundation/fs/module-loader.stdout.test.mjs +3 -2
- package/foundation/fs/module-loader.test.mjs +194 -0
- package/foundation/fs/path-compare.d.mts +33 -0
- package/foundation/fs/path-compare.mjs +73 -0
- package/foundation/fs/paths.d.mts +57 -1
- package/foundation/fs/paths.mjs +131 -3
- package/foundation/fs/paths.test.mjs +10 -1
- package/foundation/integrations/computer-integrations.d.mts +78 -0
- package/foundation/integrations/computer-integrations.mjs +316 -0
- package/foundation/integrations/computer-integrations.test.mjs +292 -0
- package/foundation/integrations/contribution-inventory.d.mts +11 -0
- package/foundation/integrations/contribution-inventory.mjs +56 -3
- package/foundation/integrations/integration-warnings.mjs +3 -2
- package/foundation/integrations/integration-warnings.test.mjs +8 -9
- package/foundation/integrations/integrations.d.mts +25 -1
- package/foundation/integrations/integrations.mjs +46 -3
- package/foundation/integrations/issue.ts +6 -0
- package/foundation/integrations/validate-contributions.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +41 -1
- package/foundation/release/agent-text-worker.d.mts +4 -0
- package/foundation/release/agent-text-worker.mjs +52 -0
- package/foundation/release/cache.d.mts +310 -0
- package/foundation/release/cache.mjs +1216 -0
- package/foundation/release/cache.test.mjs +920 -0
- package/foundation/release/choose.d.mts +207 -0
- package/foundation/release/choose.mjs +677 -0
- package/foundation/release/choose.test.mjs +427 -0
- package/foundation/release/core-worker.d.mts +4 -0
- package/foundation/release/core-worker.mjs +38 -0
- package/foundation/release/global-state.d.mts +259 -0
- package/foundation/release/global-state.mjs +472 -0
- package/foundation/release/global-state.test.mjs +381 -0
- package/foundation/release/handoff.d.mts +60 -0
- package/foundation/release/handoff.e2e.test.mjs +278 -0
- package/foundation/release/handoff.mjs +357 -0
- package/foundation/release/lock-lease.d.mts +23 -0
- package/foundation/release/lock-lease.mjs +80 -0
- package/foundation/release/notices.d.mts +253 -0
- package/foundation/release/notices.mjs +596 -0
- package/foundation/release/notices.test.mjs +565 -0
- package/foundation/release/npm-guard.d.mts +4 -0
- package/foundation/release/npm-guard.mjs +126 -0
- package/foundation/release/project.d.mts +141 -0
- package/foundation/release/project.mjs +466 -0
- package/foundation/release/project.test.mjs +288 -0
- package/foundation/release/refresh-worker.d.mts +4 -0
- package/foundation/release/refresh-worker.mjs +27 -0
- package/foundation/release/run-context.d.mts +47 -0
- package/foundation/release/run-context.mjs +149 -0
- package/foundation/release/update-notice-env.d.mts +28 -0
- package/foundation/release/update-notice-env.mjs +39 -0
- package/foundation/response/base.d.ts +26 -3
- package/foundation/response/error-codes.d.mts +3 -1
- package/foundation/response/error-codes.doc.mjs +12 -0
- package/foundation/response/error-codes.mjs +17 -0
- package/foundation/response/error-codes.test.mjs +2 -0
- package/foundation/response/json-contract.test.mjs +7 -2
- package/foundation/response/json.mjs +4 -1
- package/foundation/response/response-types.doc.mjs +22 -11
- package/foundation/response/response-types.doc.test.mjs +46 -0
- package/foundation/response/response.doc.mjs +3 -3
- package/foundation/xle/browser-imports.test.mjs +74 -0
- package/foundation/xle/registry.mjs +4 -0
- package/foundation/xle/registry.test.mjs +28 -0
- package/package.json +11 -11
- package/scripts/postinstall.mjs +24 -5
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file What every command does outside a project (spec AST-081 FR3, FR4).
|
|
5
|
+
*
|
|
6
|
+
* 1. Declarations: every command and subcommand doc declares `outsideProject`
|
|
7
|
+
* (`same`, `refuse`, or `own`), a group that only holds subcommands
|
|
8
|
+
* declares nothing, and `astryx manifest` publishes each value.
|
|
9
|
+
* 2. Behavior: each command's examples run in an empty folder, outside any
|
|
10
|
+
* project, where the repository's Core is the Core this release carries.
|
|
11
|
+
* - `same`: the run matches the same example in a new project that
|
|
12
|
+
* installs only this release's Core: exit status, envelope type or error
|
|
13
|
+
* code, and the files written.
|
|
14
|
+
* - `refuse`: the run writes nothing and fails with ERR_PROJECT_REQUIRED, or
|
|
15
|
+
* with the code its released versions refuse with (listed below).
|
|
16
|
+
* - `own`: each has a test of its own in this file.
|
|
17
|
+
* 3. Advice: outside a project, no output names a step that only works inside
|
|
18
|
+
* one.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
import * as os from 'node:os';
|
|
23
|
+
import * as path from 'node:path';
|
|
24
|
+
import {fileURLToPath, pathToFileURL} from 'node:url';
|
|
25
|
+
import {afterAll, afterEach, beforeAll, describe, expect, it, vi} from 'vitest';
|
|
26
|
+
import {program, JSON_SUPPORTED} from '../index.mjs';
|
|
27
|
+
import {buildManifest} from '../lib/manifest.mjs';
|
|
28
|
+
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
29
|
+
import {
|
|
30
|
+
checkAnsweringRelease,
|
|
31
|
+
checkComputerAgentText,
|
|
32
|
+
checkNewestRelease,
|
|
33
|
+
} from '../../../api/doctor/doctor.mjs';
|
|
34
|
+
|
|
35
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
const CLI_ROOT = path.resolve(HERE, '../../..');
|
|
37
|
+
const CORE_ROOT = path.resolve(CLI_ROOT, '../core');
|
|
38
|
+
const VERSION = JSON.parse(
|
|
39
|
+
fs.readFileSync(path.join(CLI_ROOT, 'package.json'), 'utf-8'),
|
|
40
|
+
).version;
|
|
41
|
+
const SLOW = 60_000;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The code each `refuse` command's released versions refuse with outside a
|
|
45
|
+
* project. Every other `refuse` command fails with ERR_PROJECT_REQUIRED.
|
|
46
|
+
* @type {Record<string, string>}
|
|
47
|
+
*/
|
|
48
|
+
const RELEASED_REFUSAL_CODES = {
|
|
49
|
+
swizzle: 'ERR_CORE_NOT_FOUND',
|
|
50
|
+
'integration add': 'ERR_FILE_NOT_FOUND',
|
|
51
|
+
'theme use': 'ERR_THEME_INVALID',
|
|
52
|
+
'theme remove': 'ERR_THEME_INVALID',
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** Every `own` command; each has a describe block below. */
|
|
56
|
+
const OWN_TESTED = new Set([
|
|
57
|
+
'doctor',
|
|
58
|
+
'doctor integration validate',
|
|
59
|
+
'init',
|
|
60
|
+
'integration check',
|
|
61
|
+
'integration verify',
|
|
62
|
+
'integration pack',
|
|
63
|
+
'theme add',
|
|
64
|
+
'upgrade',
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
/** @returns {Promise<any[]>} every command doc the CLI ships */
|
|
68
|
+
async function loadCommandDocs() {
|
|
69
|
+
const docs = [];
|
|
70
|
+
for (const name of fs.readdirSync(HERE)) {
|
|
71
|
+
if (!name.endsWith('.doc.mjs')) continue;
|
|
72
|
+
const {doc} = await import(pathToFileURL(path.join(HERE, name)).href);
|
|
73
|
+
if (doc?.type === 'command') docs.push(doc);
|
|
74
|
+
}
|
|
75
|
+
return docs.sort((a, b) => a.name.localeCompare(b.name));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const commandDocs = await loadCommandDocs();
|
|
79
|
+
const byName = new Map(commandDocs.map(doc => [doc.name, doc]));
|
|
80
|
+
|
|
81
|
+
/** A doc that only groups subcommands: it has subcommands and wraps no function. */
|
|
82
|
+
function isGroup(doc) {
|
|
83
|
+
return (doc.subcommands?.length ?? 0) > 0 && doc.fn == null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Split an example's command line the way a shell would, quotes included. */
|
|
87
|
+
function splitArgs(line) {
|
|
88
|
+
const out = [];
|
|
89
|
+
let current = '';
|
|
90
|
+
let quote = null;
|
|
91
|
+
let started = false;
|
|
92
|
+
for (const ch of line) {
|
|
93
|
+
if (quote) {
|
|
94
|
+
if (ch === quote) quote = null;
|
|
95
|
+
else current += ch;
|
|
96
|
+
} else if (ch === '"' || ch === "'") {
|
|
97
|
+
quote = ch;
|
|
98
|
+
started = true;
|
|
99
|
+
} else if (/\s/.test(ch)) {
|
|
100
|
+
if (current || started) out.push(current);
|
|
101
|
+
current = '';
|
|
102
|
+
started = false;
|
|
103
|
+
} else {
|
|
104
|
+
current += ch;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (current || started) out.push(current);
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The command an argument list runs: its longest name prefix with a doc. */
|
|
112
|
+
function invokedCommand(args) {
|
|
113
|
+
const words = args.filter(arg => !arg.startsWith('-'));
|
|
114
|
+
for (let n = words.length; n > 0; n--) {
|
|
115
|
+
const name = words.slice(0, n).join(' ');
|
|
116
|
+
if (byName.has(name) && args.join(' ').includes(name))
|
|
117
|
+
return byName.get(name);
|
|
118
|
+
}
|
|
119
|
+
return byName.get('manifest');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Every example in every doc, keyed by the command it runs. */
|
|
123
|
+
const examples = commandDocs.flatMap(doc =>
|
|
124
|
+
(doc.examples ?? []).map(example => {
|
|
125
|
+
const args = splitArgs(example.cli).slice(1);
|
|
126
|
+
return {
|
|
127
|
+
cli: example.cli,
|
|
128
|
+
args: args.includes('--json') ? args : ['--json', ...args],
|
|
129
|
+
command: invokedCommand(args),
|
|
130
|
+
};
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
/** @type {string[]} */
|
|
135
|
+
const tmpDirs = [];
|
|
136
|
+
function tmpDir(label) {
|
|
137
|
+
const dir = fs.mkdtempSync(
|
|
138
|
+
path.join(os.tmpdir(), `astryx-outside-${label}-`),
|
|
139
|
+
);
|
|
140
|
+
tmpDirs.push(dir);
|
|
141
|
+
return dir;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** A new project that installs only this release's Core. */
|
|
145
|
+
function newProject() {
|
|
146
|
+
const dir = tmpDir('project');
|
|
147
|
+
fs.writeFileSync(
|
|
148
|
+
path.join(dir, 'package.json'),
|
|
149
|
+
JSON.stringify({
|
|
150
|
+
name: 'new-app',
|
|
151
|
+
private: true,
|
|
152
|
+
dependencies: {'@astryxdesign/core': VERSION},
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
fs.mkdirSync(path.join(dir, 'node_modules', '@astryxdesign'), {
|
|
156
|
+
recursive: true,
|
|
157
|
+
});
|
|
158
|
+
fs.symlinkSync(
|
|
159
|
+
CORE_ROOT,
|
|
160
|
+
path.join(dir, 'node_modules', '@astryxdesign', 'core'),
|
|
161
|
+
'dir',
|
|
162
|
+
);
|
|
163
|
+
return dir;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Files under `dir`, relative, skipping node_modules. */
|
|
167
|
+
function filesIn(dir, root = dir) {
|
|
168
|
+
/** @type {string[]} */
|
|
169
|
+
const out = [];
|
|
170
|
+
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
|
|
171
|
+
const file = path.join(dir, entry.name);
|
|
172
|
+
if (entry.isDirectory() && entry.name !== 'node_modules') {
|
|
173
|
+
out.push(...filesIn(file, root));
|
|
174
|
+
} else if (entry.isFile()) {
|
|
175
|
+
out.push(path.relative(root, file));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return out.sort();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** What a run answered: exit status, envelope type or error code, files written. */
|
|
182
|
+
async function answer(args, dir) {
|
|
183
|
+
const before = new Set(filesIn(dir));
|
|
184
|
+
const r = await runCli(args, dir);
|
|
185
|
+
let envelope = null;
|
|
186
|
+
try {
|
|
187
|
+
envelope = JSON.parse(r.stdout);
|
|
188
|
+
} catch {
|
|
189
|
+
// Compared as unparsed below.
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
status: r.status,
|
|
193
|
+
kind: envelope ? (envelope.code ?? envelope.type ?? null) : 'not json',
|
|
194
|
+
written: filesIn(dir).filter(file => !before.has(file)),
|
|
195
|
+
envelope,
|
|
196
|
+
stdout: r.stdout,
|
|
197
|
+
stderr: r.stderr,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// No run reaches the network: a fetch fails the same way in both folders.
|
|
202
|
+
const realFetch = globalThis.fetch;
|
|
203
|
+
beforeAll(() => {
|
|
204
|
+
globalThis.fetch = async () => {
|
|
205
|
+
throw new TypeError('fetch failed');
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
afterAll(() => {
|
|
209
|
+
globalThis.fetch = realFetch;
|
|
210
|
+
});
|
|
211
|
+
afterEach(() => {
|
|
212
|
+
while (tmpDirs.length) {
|
|
213
|
+
fs.rmSync(/** @type {string} */ (tmpDirs.pop()), {
|
|
214
|
+
recursive: true,
|
|
215
|
+
force: true,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('outsideProject declarations', () => {
|
|
221
|
+
it.each(commandDocs.map(doc => [doc.name, doc]))(
|
|
222
|
+
'%s declares a value, unless it only groups subcommands',
|
|
223
|
+
(_name, doc) => {
|
|
224
|
+
if (isGroup(doc)) expect(doc.outsideProject).toBeUndefined();
|
|
225
|
+
else expect(['same', 'refuse', 'own']).toContain(doc.outsideProject);
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
it('astryx manifest publishes each command doc value', () => {
|
|
230
|
+
const manifest = buildManifest(program, {jsonSupported: JSON_SUPPORTED});
|
|
231
|
+
const flat = [];
|
|
232
|
+
const walk = cmds => {
|
|
233
|
+
for (const cmd of cmds) {
|
|
234
|
+
flat.push(cmd);
|
|
235
|
+
walk(cmd.subcommands ?? []);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
walk(manifest.commands);
|
|
239
|
+
expect(flat.length).toBeGreaterThan(0);
|
|
240
|
+
for (const cmd of flat) {
|
|
241
|
+
const doc = byName.get(cmd.name);
|
|
242
|
+
expect(doc, `${cmd.name} has a command doc`).toBeDefined();
|
|
243
|
+
expect(cmd.outsideProject, cmd.name).toBe(doc.outsideProject);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('names every refusal code a released version uses only for a refuse command', () => {
|
|
248
|
+
for (const name of Object.keys(RELEASED_REFUSAL_CODES)) {
|
|
249
|
+
expect(byName.get(name)?.outsideProject, name).toBe('refuse');
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('gives every own command a test of its own', () => {
|
|
254
|
+
const own = commandDocs
|
|
255
|
+
.filter(doc => doc.outsideProject === 'own')
|
|
256
|
+
.map(doc => doc.name);
|
|
257
|
+
expect(new Set(own)).toEqual(OWN_TESTED);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('every example in an empty folder matches its declaration', () => {
|
|
262
|
+
const cases = examples.filter(e => e.command.outsideProject !== 'own');
|
|
263
|
+
|
|
264
|
+
it.each(cases.map(e => [e.cli, e]))(
|
|
265
|
+
'%s',
|
|
266
|
+
async (_cli, example) => {
|
|
267
|
+
const {command, args} = example;
|
|
268
|
+
const empty = await answer(args, tmpDir('empty'));
|
|
269
|
+
if (command.outsideProject === 'refuse') {
|
|
270
|
+
expect(empty.status).not.toBe(0);
|
|
271
|
+
expect(empty.written).toEqual([]);
|
|
272
|
+
expect(empty.kind).toBe(
|
|
273
|
+
RELEASED_REFUSAL_CODES[command.name] ?? 'ERR_PROJECT_REQUIRED',
|
|
274
|
+
);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const project = await answer(args, newProject());
|
|
278
|
+
expect(
|
|
279
|
+
{status: empty.status, kind: empty.kind, written: empty.written},
|
|
280
|
+
`${example.cli}\n${empty.stdout}`,
|
|
281
|
+
).toEqual({
|
|
282
|
+
status: project.status,
|
|
283
|
+
kind: project.kind,
|
|
284
|
+
written: project.written,
|
|
285
|
+
});
|
|
286
|
+
},
|
|
287
|
+
SLOW,
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
describe('doctor outside a project (own)', () => {
|
|
292
|
+
it(
|
|
293
|
+
'checks the computer and never fails because the folder has no project',
|
|
294
|
+
async () => {
|
|
295
|
+
const dir = tmpDir('doctor');
|
|
296
|
+
const r = await answer(['--json', 'doctor'], dir);
|
|
297
|
+
expect(r.status).toBe(0);
|
|
298
|
+
expect(r.written).toEqual([]);
|
|
299
|
+
const checks = r.envelope.data.checks;
|
|
300
|
+
expect(checks.map(c => c.id)).toEqual([
|
|
301
|
+
'node-version',
|
|
302
|
+
'answering-release',
|
|
303
|
+
'newest-release',
|
|
304
|
+
'cli-on-path',
|
|
305
|
+
'computer-integrations',
|
|
306
|
+
'computer-agent-text',
|
|
307
|
+
]);
|
|
308
|
+
expect(checks.every(c => c.status !== 'fail')).toBe(true);
|
|
309
|
+
expect(checks[1].message).toContain(`CLI ${VERSION}`);
|
|
310
|
+
},
|
|
311
|
+
SLOW,
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
it(
|
|
315
|
+
'treats a package.json that declares no Astryx package as outside a project',
|
|
316
|
+
async () => {
|
|
317
|
+
const dir = tmpDir('doctor-pkg');
|
|
318
|
+
fs.writeFileSync(path.join(dir, 'package.json'), '{"name":"x"}');
|
|
319
|
+
const r = await answer(['--json', 'doctor'], dir);
|
|
320
|
+
expect(r.status).toBe(0);
|
|
321
|
+
expect(r.envelope.data.checks.map(c => c.id)).not.toContain(
|
|
322
|
+
'core-installed',
|
|
323
|
+
);
|
|
324
|
+
},
|
|
325
|
+
SLOW,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
it(
|
|
329
|
+
'runs the project checks inside a project, unchanged',
|
|
330
|
+
async () => {
|
|
331
|
+
const r = await answer(['--json', 'doctor'], newProject());
|
|
332
|
+
const ids = r.envelope.data.checks.map(c => c.id);
|
|
333
|
+
expect(ids).toContain('core-installed');
|
|
334
|
+
expect(ids).not.toContain('answering-release');
|
|
335
|
+
expect(ids).not.toContain('computer-agent-text');
|
|
336
|
+
const newest = r.envelope.data.checks.find(
|
|
337
|
+
c => c.id === 'newest-release',
|
|
338
|
+
);
|
|
339
|
+
expect(newest.status).toBe('info');
|
|
340
|
+
},
|
|
341
|
+
SLOW,
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
it('names the release that answered and the Core it reads', () => {
|
|
345
|
+
expect(
|
|
346
|
+
checkAnsweringRelease({
|
|
347
|
+
nodeVersion: '22.13.0',
|
|
348
|
+
answeredBy: {cli: '1.2.0', core: '1.2.0', copy: 'carried'},
|
|
349
|
+
latest: null,
|
|
350
|
+
}),
|
|
351
|
+
).toMatchObject({
|
|
352
|
+
status: 'pass',
|
|
353
|
+
message: expect.stringContaining('CLI 1.2.0 with Core 1.2.0'),
|
|
354
|
+
});
|
|
355
|
+
expect(
|
|
356
|
+
checkAnsweringRelease({
|
|
357
|
+
nodeVersion: '22.13.0',
|
|
358
|
+
answeredBy: {cli: '1.2.0', core: null, copy: 'carried'},
|
|
359
|
+
latest: null,
|
|
360
|
+
}),
|
|
361
|
+
).toMatchObject({status: 'info'});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('reports the newest release it knows of, as info only', () => {
|
|
365
|
+
const at = latest =>
|
|
366
|
+
checkNewestRelease({
|
|
367
|
+
nodeVersion: '22.13.0',
|
|
368
|
+
answeredBy: {cli: '1.2.0', core: '1.2.0', copy: 'carried'},
|
|
369
|
+
latest,
|
|
370
|
+
});
|
|
371
|
+
expect(at(null).message).toBe(
|
|
372
|
+
'Update check: not run yet, so the newest release is not known here; it runs in the background once a day, after a command.',
|
|
373
|
+
);
|
|
374
|
+
expect(at('1.3.0').message).toContain('1.3.0 is newer than 1.2.0');
|
|
375
|
+
expect(at('1.2.0').message).toContain('No newer release is known');
|
|
376
|
+
for (const latest of [null, '1.3.0', '1.2.0', '1.1.0']) {
|
|
377
|
+
expect(at(latest).status).toBe('info');
|
|
378
|
+
expect(at(latest).fix).toBeUndefined();
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('doctor: the computer-level agent text', () => {
|
|
384
|
+
const at = agentText =>
|
|
385
|
+
checkComputerAgentText({
|
|
386
|
+
nodeVersion: '22.13.0',
|
|
387
|
+
answeredBy: {cli: '1.2.0', core: '1.2.0', copy: 'carried'},
|
|
388
|
+
latest: null,
|
|
389
|
+
agentText,
|
|
390
|
+
cwd: os.tmpdir(),
|
|
391
|
+
});
|
|
392
|
+
const A = '/home/me/.agents/skills/astryx/SKILL.md';
|
|
393
|
+
const C = '/home/me/.claude/skills/astryx';
|
|
394
|
+
|
|
395
|
+
it('present and current, linked: no fix', () => {
|
|
396
|
+
const check = at([
|
|
397
|
+
{path: A, state: 'current'},
|
|
398
|
+
{path: C, state: 'linked', link: true},
|
|
399
|
+
]);
|
|
400
|
+
expect(check).toMatchObject({id: 'computer-agent-text', status: 'info'});
|
|
401
|
+
expect(check.message).toContain('matches this release');
|
|
402
|
+
expect(check.message).toContain(`Linked into: ${C}`);
|
|
403
|
+
expect(check.fix).toBeUndefined();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('a different file, or a link not made yet: fix with init --user', () => {
|
|
407
|
+
for (const [file, link] of [
|
|
408
|
+
['differs', 'linked'],
|
|
409
|
+
['current', 'absent'],
|
|
410
|
+
]) {
|
|
411
|
+
const check = at([
|
|
412
|
+
{path: A, state: file},
|
|
413
|
+
{path: C, state: link, link: true},
|
|
414
|
+
]);
|
|
415
|
+
expect(check.status).toBe('info');
|
|
416
|
+
expect(check.fix).toMatch(/init --user`\.$/);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('names a link target it skipped because Astryx did not create what is there', () => {
|
|
421
|
+
const check = at([
|
|
422
|
+
{path: A, state: 'current'},
|
|
423
|
+
{path: C, state: 'foreign', link: true},
|
|
424
|
+
]);
|
|
425
|
+
expect(check.message).toContain(
|
|
426
|
+
`Not linked, because something Astryx did not create is there: ${C}`,
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('flags a link whose skill folder is missing, with the fix', () => {
|
|
431
|
+
const check = at([
|
|
432
|
+
{path: A, state: 'current'},
|
|
433
|
+
{path: C, state: 'dangling', link: true},
|
|
434
|
+
]);
|
|
435
|
+
expect(check.message).toContain(
|
|
436
|
+
`the skill folder the link points to is not there`,
|
|
437
|
+
);
|
|
438
|
+
expect(check.message).toContain(C);
|
|
439
|
+
expect(check.fix).toMatch(/init --user`\.$/);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('absent: fix with init --user; a file Astryx did not write: left as it is', () => {
|
|
443
|
+
const check = at([
|
|
444
|
+
{path: A, state: 'absent'},
|
|
445
|
+
{path: C, state: 'absent', link: true},
|
|
446
|
+
]);
|
|
447
|
+
expect(check.message).toContain('No computer-level agent text');
|
|
448
|
+
expect(check.fix).toMatch(/init --user`\.$/);
|
|
449
|
+
expect(
|
|
450
|
+
at([
|
|
451
|
+
{path: A, state: 'foreign'},
|
|
452
|
+
{path: C, state: 'absent', link: true},
|
|
453
|
+
]).message,
|
|
454
|
+
).toContain(`${A} was not written by Astryx, so it is left as it is`);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it(
|
|
458
|
+
'reads the home folder: absent, then current after init --user',
|
|
459
|
+
async () => {
|
|
460
|
+
const home = tmpDir('home');
|
|
461
|
+
vi.stubEnv('HOME', home);
|
|
462
|
+
vi.stubEnv('USERPROFILE', home);
|
|
463
|
+
try {
|
|
464
|
+
const find = r =>
|
|
465
|
+
r.envelope.data.checks.find(c => c.id === 'computer-agent-text');
|
|
466
|
+
const before = find(await answer(['--json', 'doctor'], tmpDir('doc')));
|
|
467
|
+
expect(before.message).toContain('No computer-level agent text');
|
|
468
|
+
expect(before.status).toBe('info');
|
|
469
|
+
await runCli(['--json', 'init', '--user'], tmpDir('user'));
|
|
470
|
+
const after = find(await answer(['--json', 'doctor'], tmpDir('doc2')));
|
|
471
|
+
expect(after.message).toContain('matches this release');
|
|
472
|
+
expect(after.fix).toBeUndefined();
|
|
473
|
+
} finally {
|
|
474
|
+
vi.unstubAllEnvs();
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
SLOW,
|
|
478
|
+
);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
describe('doctor integration validate outside a package (own)', () => {
|
|
482
|
+
it(
|
|
483
|
+
'writes nothing and reports one no_package issue',
|
|
484
|
+
async () => {
|
|
485
|
+
const r = await answer(
|
|
486
|
+
['--json', 'doctor', 'integration', 'validate'],
|
|
487
|
+
tmpDir('validate'),
|
|
488
|
+
);
|
|
489
|
+
expect(r.status).toBe(1);
|
|
490
|
+
expect(r.written).toEqual([]);
|
|
491
|
+
expect(r.envelope.data.issues.map(i => i.code)).toEqual(['no_package']);
|
|
492
|
+
},
|
|
493
|
+
SLOW,
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
it(
|
|
497
|
+
'looks a package name up as in a project',
|
|
498
|
+
async () => {
|
|
499
|
+
const empty = await answer(
|
|
500
|
+
['--json', 'doctor', 'integration', 'validate', '@acme/widgets'],
|
|
501
|
+
tmpDir('validate-pkg'),
|
|
502
|
+
);
|
|
503
|
+
const project = await answer(
|
|
504
|
+
['--json', 'doctor', 'integration', 'validate', '@acme/widgets'],
|
|
505
|
+
newProject(),
|
|
506
|
+
);
|
|
507
|
+
expect(empty.status).toBe(project.status);
|
|
508
|
+
expect(empty.envelope.data.issues.map(i => i.code)).toEqual(
|
|
509
|
+
project.envelope.data.issues.map(i => i.code),
|
|
510
|
+
);
|
|
511
|
+
},
|
|
512
|
+
SLOW,
|
|
513
|
+
);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
describe('integration check outside a package (own)', () => {
|
|
517
|
+
it(
|
|
518
|
+
'writes nothing, reports one no_package issue from validate, and exits 1',
|
|
519
|
+
async () => {
|
|
520
|
+
const r = await answer(
|
|
521
|
+
['--json', 'integration', 'check'],
|
|
522
|
+
tmpDir('check'),
|
|
523
|
+
);
|
|
524
|
+
expect(r.status).toBe(1);
|
|
525
|
+
expect(r.written).toEqual([]);
|
|
526
|
+
expect(r.envelope.type).toBe('integration.check');
|
|
527
|
+
expect(r.envelope.data.failed).toBe(true);
|
|
528
|
+
const validate = r.envelope.data.checks.find(c => c.check === 'validate');
|
|
529
|
+
expect(validate.response.data.issues.map(i => i.code)).toEqual([
|
|
530
|
+
'no_package',
|
|
531
|
+
]);
|
|
532
|
+
},
|
|
533
|
+
SLOW,
|
|
534
|
+
);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
describe('init outside a project (own)', () => {
|
|
538
|
+
it(
|
|
539
|
+
'writes the agent docs and prints no getting-started steps',
|
|
540
|
+
async () => {
|
|
541
|
+
const dir = tmpDir('init');
|
|
542
|
+
const json = await answer(['--json', 'init'], dir);
|
|
543
|
+
expect(json.status).toBe(0);
|
|
544
|
+
expect(json.written).toEqual(['AGENTS.md']);
|
|
545
|
+
expect(json.envelope.data.nextSteps).toBe(false);
|
|
546
|
+
const text = await runCli(['init'], tmpDir('init-text'));
|
|
547
|
+
expect(text.stdout).not.toContain('Next steps');
|
|
548
|
+
expect(text.stdout).not.toContain('npm install');
|
|
549
|
+
},
|
|
550
|
+
SLOW,
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
it(
|
|
554
|
+
'names no step that adds a theme to an app',
|
|
555
|
+
async () => {
|
|
556
|
+
const r = await runCli(['init', '--all'], tmpDir('init-all'));
|
|
557
|
+
expect(r.status).toBe(0);
|
|
558
|
+
expect(r.stdout).not.toContain('theme add');
|
|
559
|
+
expect(r.stdout).toContain('theme eject');
|
|
560
|
+
},
|
|
561
|
+
SLOW,
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
it(
|
|
565
|
+
'--user writes only the computer-level agent text and its link, in the home folder',
|
|
566
|
+
async () => {
|
|
567
|
+
const home = tmpDir('home');
|
|
568
|
+
vi.stubEnv('HOME', home);
|
|
569
|
+
vi.stubEnv('USERPROFILE', home);
|
|
570
|
+
try {
|
|
571
|
+
const r = await answer(
|
|
572
|
+
['--json', 'init', '--user'],
|
|
573
|
+
tmpDir('init-user'),
|
|
574
|
+
);
|
|
575
|
+
expect(r.status).toBe(0);
|
|
576
|
+
expect(r.written).toEqual([]);
|
|
577
|
+
expect(r.envelope.type).toBe('init.user');
|
|
578
|
+
// The per-user cache and settings may also live under this home
|
|
579
|
+
// folder.
|
|
580
|
+
expect(
|
|
581
|
+
filesIn(home).filter(
|
|
582
|
+
f => !f.startsWith('.cache') && !f.startsWith('.config'),
|
|
583
|
+
),
|
|
584
|
+
).toEqual([path.join('.agents', 'skills', 'astryx', 'SKILL.md')]);
|
|
585
|
+
// Claude Code's folder is a link to it.
|
|
586
|
+
expect(
|
|
587
|
+
fs
|
|
588
|
+
.lstatSync(path.join(home, '.claude', 'skills', 'astryx'))
|
|
589
|
+
.isSymbolicLink(),
|
|
590
|
+
).toBe(true);
|
|
591
|
+
} finally {
|
|
592
|
+
vi.unstubAllEnvs();
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
SLOW,
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
it(
|
|
599
|
+
'prints the getting-started steps inside a project',
|
|
600
|
+
async () => {
|
|
601
|
+
const r = await answer(['--json', 'init'], newProject());
|
|
602
|
+
expect(r.envelope.data.nextSteps).toBe(true);
|
|
603
|
+
},
|
|
604
|
+
SLOW,
|
|
605
|
+
);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
describe.each([
|
|
609
|
+
['integration verify', ['integration', 'verify']],
|
|
610
|
+
['integration pack', ['integration', 'pack', '--check']],
|
|
611
|
+
])('%s outside a package (own)', (_name, args) => {
|
|
612
|
+
it(
|
|
613
|
+
'writes nothing and reports one failed check, no_package',
|
|
614
|
+
async () => {
|
|
615
|
+
const r = await answer(['--json', ...args], tmpDir('verify'));
|
|
616
|
+
expect(r.status).toBe(1);
|
|
617
|
+
expect(r.written).toEqual([]);
|
|
618
|
+
expect(r.envelope.type).toBe('integration.pack-check');
|
|
619
|
+
expect(r.envelope.data.issues.map(i => i.code)).toEqual(['no_package']);
|
|
620
|
+
},
|
|
621
|
+
SLOW,
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
describe('theme add outside a project (own)', () => {
|
|
626
|
+
it(
|
|
627
|
+
'lists the available themes with no slug, as in a new project',
|
|
628
|
+
async () => {
|
|
629
|
+
const empty = await answer(
|
|
630
|
+
['--json', 'theme', 'add'],
|
|
631
|
+
tmpDir('theme-add'),
|
|
632
|
+
);
|
|
633
|
+
const project = await answer(['--json', 'theme', 'add'], newProject());
|
|
634
|
+
expect(empty.status).toBe(0);
|
|
635
|
+
expect(empty.envelope.type).toBe('theme.list');
|
|
636
|
+
expect(empty.envelope.data.map(t => t.slug)).toEqual(
|
|
637
|
+
project.envelope.data.map(t => t.slug),
|
|
638
|
+
);
|
|
639
|
+
},
|
|
640
|
+
SLOW,
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
it(
|
|
644
|
+
'writes nothing and names the app project it needs, given a slug',
|
|
645
|
+
async () => {
|
|
646
|
+
const r = await answer(
|
|
647
|
+
['--json', 'theme', 'add', 'butter'],
|
|
648
|
+
tmpDir('theme-add-slug'),
|
|
649
|
+
);
|
|
650
|
+
expect(r.status).toBe(1);
|
|
651
|
+
expect(r.written).toEqual([]);
|
|
652
|
+
expect(r.kind).toBe('ERR_THEME_INVALID');
|
|
653
|
+
expect(r.envelope.error).toContain('app project');
|
|
654
|
+
},
|
|
655
|
+
SLOW,
|
|
656
|
+
);
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
describe('upgrade outside a project (own)', () => {
|
|
660
|
+
it(
|
|
661
|
+
'lists the codemods this release ships',
|
|
662
|
+
async () => {
|
|
663
|
+
const r = await answer(
|
|
664
|
+
['--json', 'upgrade', '--list'],
|
|
665
|
+
tmpDir('upgrade-list'),
|
|
666
|
+
);
|
|
667
|
+
expect(r.status).toBe(0);
|
|
668
|
+
expect(r.envelope.type).toBe('upgrade.list');
|
|
669
|
+
},
|
|
670
|
+
SLOW,
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
it(
|
|
674
|
+
'finds no copied compositions to update',
|
|
675
|
+
async () => {
|
|
676
|
+
const r = await answer(
|
|
677
|
+
['--json', 'upgrade', '--registry', '--apply'],
|
|
678
|
+
tmpDir('upgrade-reg'),
|
|
679
|
+
);
|
|
680
|
+
expect(r.status).toBe(0);
|
|
681
|
+
expect(r.written).toEqual([]);
|
|
682
|
+
},
|
|
683
|
+
SLOW,
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
it(
|
|
687
|
+
'writes nothing and fails a migration with ERR_VERSION_DETECT, naming no install step',
|
|
688
|
+
async () => {
|
|
689
|
+
const r = await answer(
|
|
690
|
+
['--json', 'upgrade', '--from', '0.1.0', '--apply'],
|
|
691
|
+
tmpDir('upgrade-from'),
|
|
692
|
+
);
|
|
693
|
+
expect(r.status).toBe(1);
|
|
694
|
+
expect(r.written).toEqual([]);
|
|
695
|
+
expect(r.kind).toBe('ERR_VERSION_DETECT');
|
|
696
|
+
expect(r.envelope.error).toContain('outside a project');
|
|
697
|
+
expect(r.envelope.error).not.toMatch(/install the target/i);
|
|
698
|
+
},
|
|
699
|
+
SLOW,
|
|
700
|
+
);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
describe('no advice that works only inside a project', () => {
|
|
704
|
+
it(
|
|
705
|
+
'prints no setup reminder in a package.json that declares no Astryx package',
|
|
706
|
+
async () => {
|
|
707
|
+
const dir = tmpDir('nudge');
|
|
708
|
+
fs.writeFileSync(path.join(dir, 'package.json'), '{"name":"x"}');
|
|
709
|
+
const r = await runCli(['docs', 'tokens'], dir);
|
|
710
|
+
expect(r.stderr).not.toMatch(/finish setup/);
|
|
711
|
+
},
|
|
712
|
+
SLOW,
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
it(
|
|
716
|
+
'theme list names no step that adds a theme to an app',
|
|
717
|
+
async () => {
|
|
718
|
+
const r = await runCli(['theme', 'list'], tmpDir('theme-list'));
|
|
719
|
+
expect(r.status).toBe(0);
|
|
720
|
+
expect(r.stdout).not.toContain('theme add');
|
|
721
|
+
expect(r.stdout).toContain('theme eject');
|
|
722
|
+
},
|
|
723
|
+
SLOW,
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
it(
|
|
727
|
+
'a theme search result forks the source instead of adding the theme',
|
|
728
|
+
async () => {
|
|
729
|
+
const r = await answer(
|
|
730
|
+
['--json', 'search', 'warm', '--type', 'theme'],
|
|
731
|
+
tmpDir('search'),
|
|
732
|
+
);
|
|
733
|
+
const themes = r.envelope.data.results.filter(x => x.domain === 'theme');
|
|
734
|
+
expect(themes.length).toBeGreaterThan(0);
|
|
735
|
+
for (const theme of themes) {
|
|
736
|
+
expect(theme.command).toBe(`astryx theme eject ${theme.name}`);
|
|
737
|
+
}
|
|
738
|
+
const inside = await answer(
|
|
739
|
+
['--json', 'search', 'warm', '--type', 'theme'],
|
|
740
|
+
newProject(),
|
|
741
|
+
);
|
|
742
|
+
for (const theme of inside.envelope.data.results.filter(
|
|
743
|
+
x => x.domain === 'theme',
|
|
744
|
+
)) {
|
|
745
|
+
expect(theme.command).toBe(`astryx theme add ${theme.name}`);
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
SLOW,
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
it(
|
|
752
|
+
'discover names no config step',
|
|
753
|
+
async () => {
|
|
754
|
+
const r = await runCli(['discover'], tmpDir('discover'));
|
|
755
|
+
expect(r.stdout).not.toContain('astryx.config');
|
|
756
|
+
},
|
|
757
|
+
SLOW,
|
|
758
|
+
);
|
|
759
|
+
|
|
760
|
+
it(
|
|
761
|
+
'the integration checks name no installed-integration step',
|
|
762
|
+
async () => {
|
|
763
|
+
for (const kind of ['templates', 'components', 'docs']) {
|
|
764
|
+
const r = await runCli(
|
|
765
|
+
['doctor', 'integration', kind],
|
|
766
|
+
tmpDir('authoring'),
|
|
767
|
+
);
|
|
768
|
+
expect(r.stdout).toContain('Nothing was checked');
|
|
769
|
+
expect(r.stdout).not.toContain('To check an installed integration');
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
SLOW,
|
|
773
|
+
);
|
|
774
|
+
});
|