@canonical/pragma-cli 0.29.0 → 0.31.0
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 +128 -47
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/defaultConfig.test.ts +33 -0
- package/src/config/defaultConfig.ts +59 -0
- package/src/config/defaults.json +17 -0
- package/src/config/findProjectConfigPath.test.ts +53 -0
- package/src/config/findProjectConfigPath.ts +36 -0
- package/src/config/index.ts +17 -2
- package/src/config/mergeConfigFileUpdate.ts +58 -0
- package/src/config/parseConfigValues.ts +217 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -153
- package/src/config/readConfigLayers.test.ts +109 -0
- package/src/config/readConfigLayers.ts +118 -0
- package/src/config/resolveGlobalConfigPath.ts +13 -0
- package/src/config/resolveWriteConfigPath.test.ts +53 -0
- package/src/config/resolveWriteConfigPath.ts +31 -0
- package/src/config/types.ts +83 -1
- package/src/config/writeConfig.ts +25 -55
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/list.ts +5 -90
- package/src/domains/block/commands/lookup.ts +8 -167
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/mcp/specs.ts +16 -196
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +260 -0
- package/src/domains/config/commands/channel.test.ts +30 -3
- package/src/domains/config/commands/channel.ts +25 -8
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +32 -1
- package/src/domains/config/commands/framework.ts +26 -8
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +20 -2
- package/src/domains/config/commands/tier.ts +25 -8
- package/src/domains/config/commands/trace.test.ts +19 -2
- package/src/domains/config/commands/trace.ts +6 -3
- package/src/domains/config/formatters/channel.test.ts +20 -6
- package/src/domains/config/formatters/channel.ts +14 -11
- package/src/domains/config/formatters/framework.ts +14 -11
- package/src/domains/config/formatters/show.test.ts +27 -0
- package/src/domains/config/formatters/show.ts +27 -4
- package/src/domains/config/formatters/tier.test.ts +26 -10
- package/src/domains/config/formatters/tier.ts +19 -11
- package/src/domains/config/formatters/trace.ts +7 -7
- package/src/domains/config/mcp/specs.ts +33 -53
- package/src/domains/config/operations/show.test.ts +18 -0
- package/src/domains/config/operations/show.ts +7 -1
- package/src/domains/config/operations/types.ts +7 -0
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +1 -0
- package/src/domains/config/tasks/setChannel.ts +13 -28
- package/src/domains/config/tasks/setFramework.ts +11 -30
- package/src/domains/config/tasks/setTier.ts +10 -29
- package/src/domains/config/tasks/setTrace.ts +10 -28
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +133 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +32 -26
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
- package/src/domains/doctor/operations/checks/index.ts +1 -0
- package/src/domains/doctor/operations/checks.test.ts +160 -7
- package/src/domains/doctor/operations/runChecks.test.ts +15 -6
- package/src/domains/doctor/operations/runChecks.ts +2 -0
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/build.test.ts +5 -3
- package/src/domains/graphql/commands/build.ts +8 -16
- package/src/domains/graphql/commands/check.test.ts +5 -3
- package/src/domains/graphql/commands/check.ts +7 -13
- package/src/domains/graphql/commands/index.ts +1 -0
- package/src/domains/graphql/commands/serve.test.ts +46 -0
- package/src/domains/graphql/commands/serve.ts +141 -0
- package/src/domains/graphql/constants.ts +10 -0
- package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
- package/src/domains/graphql/helpers/gatherSources.ts +58 -0
- package/src/domains/graphql/helpers/index.ts +2 -0
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
- package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
- package/src/domains/graphql/index.ts +9 -6
- package/src/domains/graphql/operations/compileSchema.test.ts +23 -47
- package/src/domains/graphql/operations/compileSchema.ts +23 -25
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +10 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/collectInfo.test.ts +14 -3
- package/src/domains/info/operations/collectInfo.ts +12 -15
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +24 -4
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +55 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/list.ts +5 -38
- package/src/domains/modifier/commands/lookup.ts +8 -79
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/mcp/specs.ts +15 -94
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +86 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/commands/show.ts +6 -58
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +361 -64
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/mcp/specs.ts +7 -84
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.test.ts +36 -0
- package/src/domains/ontology/stories.ts +153 -0
- package/src/domains/refs/operations/updateRefs.ts +2 -38
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/commands/skills.test.ts +6 -2
- package/src/domains/setup/commands/skills.ts +6 -2
- package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
- package/src/domains/setup/helpers/runSetupTask.ts +62 -70
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/setup/operations/setupMcp.test.ts +6 -1
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +57 -18
- package/src/domains/shared/compactUri.test.ts +11 -0
- package/src/domains/shared/compactUri.ts +10 -5
- package/src/domains/shared/context.ts +2 -2
- package/src/domains/shared/contracts.ts +11 -0
- package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
- package/src/domains/shared/createInteractivePromptSession.ts +236 -0
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +158 -12
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/index.ts +4 -1
- package/src/domains/shared/loaders/localLoader.ts +2 -4
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +82 -9
- package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
- package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
- package/src/domains/shared/lookupMany.test.ts +135 -0
- package/src/domains/shared/lookupMany.ts +50 -2
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/packages.ts +10 -17
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/runtime.ts +4 -63
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +34 -3
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +102 -0
- package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
- package/src/domains/shared/stories/compileLookupTool.ts +96 -0
- package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
- package/src/domains/shared/stories/compileReadCommand.ts +48 -0
- package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
- package/src/domains/shared/stories/compileReadTool.ts +38 -0
- package/src/domains/shared/stories/condense.test.ts +18 -0
- package/src/domains/shared/stories/condense.ts +15 -0
- package/src/domains/shared/stories/index.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.ts +22 -0
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +151 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +111 -0
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +81 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +154 -0
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +406 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +158 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +50 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +751 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +724 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +55 -0
- package/src/domains/shared/stories/pack/index.ts +42 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +381 -0
- package/src/domains/shared/stories/pack/types.ts +224 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +742 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +859 -0
- package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
- package/src/domains/shared/stories/projectCliParameters.ts +57 -0
- package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
- package/src/domains/shared/stories/projectToolParams.ts +32 -0
- package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
- package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
- package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
- package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
- package/src/domains/shared/stories/types.ts +179 -0
- package/src/domains/shared/suggestions/expandGlob.ts +1 -0
- package/src/domains/shared/suggestions/suggestNames.ts +43 -10
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/skill/commands/index.ts +3 -1
- package/src/domains/skill/commands/list.test.ts +6 -2
- package/src/domains/skill/commands/list.ts +5 -63
- package/src/domains/skill/commands/lookup.test.ts +118 -0
- package/src/domains/skill/commands/lookup.ts +14 -0
- package/src/domains/skill/formatters/index.ts +1 -0
- package/src/domains/skill/formatters/lookup.test.ts +46 -0
- package/src/domains/skill/formatters/lookup.ts +57 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +0 -1
- package/src/domains/skill/index.ts +7 -3
- package/src/domains/skill/mcp/specs.ts +10 -29
- package/src/domains/skill/operations/discover.test.ts +61 -30
- package/src/domains/skill/operations/discover.ts +28 -37
- package/src/domains/skill/operations/index.ts +2 -1
- package/src/domains/skill/operations/list.test.ts +15 -7
- package/src/domains/skill/operations/lookup.test.ts +91 -0
- package/src/domains/skill/operations/lookup.ts +70 -0
- package/src/domains/skill/stories.test.ts +43 -0
- package/src/domains/skill/stories.ts +103 -0
- package/src/domains/skill/types.ts +8 -0
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +18 -2
- package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/domains/trace/tracePlugin.test.ts +1 -1
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +382 -74
- package/src/mcp/registerResources.ts +5 -348
- package/src/mcp/resources/buildGraphIndex.ts +167 -0
- package/src/mcp/resources/buildResourceList.test.ts +97 -0
- package/src/mcp/resources/buildResourceList.ts +176 -0
- package/src/mcp/resources/classifyEntity.test.ts +40 -0
- package/src/mcp/resources/classifyEntity.ts +27 -0
- package/src/mcp/resources/constants.ts +105 -0
- package/src/mcp/resources/displayPredicates.ts +62 -0
- package/src/mcp/resources/pickFirstValue.test.ts +29 -0
- package/src/mcp/resources/pickFirstValue.ts +26 -0
- package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
- package/src/mcp/resources/pickPrimaryType.ts +26 -0
- package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
- package/src/mcp/resources/rankUriCompletions.ts +64 -0
- package/src/mcp/resources/readEntity.ts +169 -0
- package/src/mcp/resources/registerResources.ts +126 -0
- package/src/mcp/resources/types.ts +128 -0
- package/src/mcp/tools/index.ts +22 -5
- package/src/mcp/tools/registerTools.test.ts +167 -34
- package/src/mcp/types.ts +4 -0
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +35 -13
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +202 -25
- package/src/testing/cli.ts +30 -2
- package/src/testing/createTestMcpClient.ts +4 -1
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +7 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +103 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +136 -82
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +26 -0
- package/src/testing/standardParityFixtures.ts +35 -0
- package/src/testing/store.ts +4 -4
- package/src/testing/storyFixtures.ts +63 -0
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -90
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -99
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -244
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -32
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -59
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -38
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -52,6 +52,30 @@ function parseJson<T>(result: CommandResult): T {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
describe("CLI user stories", () => {
|
|
55
|
+
it("story: a first-time user is greeted once and gets a global config", () => {
|
|
56
|
+
const workspace = createWorkspace();
|
|
57
|
+
const freshXdg = mkdtempSync(join(tmpdir(), "pragma-firstrun-e2e-"));
|
|
58
|
+
tempDirs.add(freshXdg);
|
|
59
|
+
const env = { XDG_CONFIG_HOME: freshXdg };
|
|
60
|
+
|
|
61
|
+
// First invocation: greeting on stderr, config created, stdout untouched.
|
|
62
|
+
const first = runCommand(["info", "--format", "json"], workspace, env);
|
|
63
|
+
expect(first.exitCode).toBe(0);
|
|
64
|
+
expect(first.stderr).toContain("pre-release pragma CLI");
|
|
65
|
+
expect(first.stderr).toContain(join(freshXdg, "pragma", "config.json"));
|
|
66
|
+
expect(() => JSON.parse(first.stdout)).not.toThrow();
|
|
67
|
+
expect(
|
|
68
|
+
JSON.parse(
|
|
69
|
+
readFileSync(join(freshXdg, "pragma", "config.json"), "utf-8"),
|
|
70
|
+
),
|
|
71
|
+
).toEqual({});
|
|
72
|
+
|
|
73
|
+
// Second invocation: no greeting — first-run happens once.
|
|
74
|
+
const second = runCommand(["info", "--format", "json"], workspace, env);
|
|
75
|
+
expect(second.exitCode).toBe(0);
|
|
76
|
+
expect(second.stderr.trim()).toBe("");
|
|
77
|
+
}, 40_000);
|
|
78
|
+
|
|
55
79
|
it("story: a new contributor verifies the local installation with info", () => {
|
|
56
80
|
const workspace = createWorkspace();
|
|
57
81
|
|
|
@@ -195,4 +219,43 @@ describe("CLI user stories", () => {
|
|
|
195
219
|
expect(existsSync(indexFile)).toBe(true);
|
|
196
220
|
expect(readFileSync(indexFile, "utf-8")).toContain("./MySth");
|
|
197
221
|
}, 20_000);
|
|
222
|
+
|
|
223
|
+
it("story: --version works at the root, noun, and verb levels", () => {
|
|
224
|
+
const workspace = createWorkspace();
|
|
225
|
+
const root = runCommand(["--version"], workspace);
|
|
226
|
+
const verb = runCommand(["block", "list", "--version"], workspace);
|
|
227
|
+
|
|
228
|
+
expectSuccess(root);
|
|
229
|
+
expectSuccess(verb);
|
|
230
|
+
expect(root.stdout.trim()).toBe(verb.stdout.trim());
|
|
231
|
+
expect(root.stdout.trim()).toMatch(/^\d+\.\d+\.\d+/);
|
|
232
|
+
}, 20_000);
|
|
233
|
+
|
|
234
|
+
it("story: an unknown verb under a known noun lists that noun's verbs", () => {
|
|
235
|
+
const workspace = createWorkspace();
|
|
236
|
+
|
|
237
|
+
const result = runCommand(["standard", "frobnicate"], workspace);
|
|
238
|
+
|
|
239
|
+
expect(result.exitCode).not.toBe(0);
|
|
240
|
+
// Errors, then defers to the category that exists.
|
|
241
|
+
expect(result.stderr).toContain("unknown command 'frobnicate'");
|
|
242
|
+
expect(result.stderr).toContain("pragma standard <verb>");
|
|
243
|
+
expect(result.stderr).toContain("list");
|
|
244
|
+
expect(result.stderr).toContain("lookup");
|
|
245
|
+
}, 20_000);
|
|
246
|
+
|
|
247
|
+
it("story: a global flag before the noun still lists that noun's verbs", () => {
|
|
248
|
+
const workspace = createWorkspace();
|
|
249
|
+
|
|
250
|
+
// `--format json` sits before the noun; the flag *value* (`json`) must not
|
|
251
|
+
// be mistaken for the noun, or no verbs would be suggested.
|
|
252
|
+
const result = runCommand(
|
|
253
|
+
["--format", "json", "standard", "frobnicate"],
|
|
254
|
+
workspace,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
expect(result.exitCode).not.toBe(0);
|
|
258
|
+
expect(result.stderr).toContain("pragma standard <verb>");
|
|
259
|
+
expect(result.stderr).toContain("lookup");
|
|
260
|
+
}, 20_000);
|
|
198
261
|
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The P.05 byte-equality gate: `pragma create X` and `summon X` must write
|
|
3
|
+
* identical bytes for the same generator invocation.
|
|
4
|
+
*
|
|
5
|
+
* Both binaries reduce to compositions over the shared execution core:
|
|
6
|
+
* pragma's batch path is `executeGenerator` (which stamps via
|
|
7
|
+
* `createStampOnEffectStart`), and summon's Ink executor runs
|
|
8
|
+
* `runGeneratorTask` with the same stamp transform and the defaults prompt
|
|
9
|
+
* handler. This test runs both compositions over the real generators into
|
|
10
|
+
* separate directories and asserts the trees are byte-identical — stamps
|
|
11
|
+
* included.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
mkdirSync,
|
|
16
|
+
mkdtempSync,
|
|
17
|
+
readdirSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
rmSync,
|
|
20
|
+
} from "node:fs";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import { join, relative } from "node:path";
|
|
23
|
+
import {
|
|
24
|
+
answerPromptWithDefaults,
|
|
25
|
+
createGeneratorStamp,
|
|
26
|
+
createStampOnEffectStart,
|
|
27
|
+
executeGenerator,
|
|
28
|
+
runGeneratorTask,
|
|
29
|
+
} from "@canonical/cli-core";
|
|
30
|
+
import { generators as componentGenerators } from "@canonical/summon-component";
|
|
31
|
+
import type { GeneratorDefinition } from "@canonical/summon-core";
|
|
32
|
+
import { generators as packageGenerators } from "@canonical/summon-package";
|
|
33
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
34
|
+
|
|
35
|
+
interface Case {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly gen: GeneratorDefinition;
|
|
38
|
+
readonly answers: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const cases: Case[] = [
|
|
42
|
+
{
|
|
43
|
+
name: "component/react",
|
|
44
|
+
gen: componentGenerators["component/react"] as GeneratorDefinition,
|
|
45
|
+
answers: { componentPath: "src/components/Button" },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "package/library",
|
|
49
|
+
gen: packageGenerators.package as GeneratorDefinition,
|
|
50
|
+
answers: { name: "@canonical/example", type: "library" },
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
/** Collect every file under root as relative path → raw content bytes. */
|
|
55
|
+
const readTree = (root: string, dir = root): Map<string, Buffer> => {
|
|
56
|
+
const files = new Map<string, Buffer>();
|
|
57
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
58
|
+
const full = join(dir, entry.name);
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
for (const [k, v] of readTree(root, full)) files.set(k, v);
|
|
61
|
+
} else {
|
|
62
|
+
files.set(relative(root, full), readFileSync(full));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return files;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
describe("cross-binary byte equality — pragma create ≡ summon", () => {
|
|
69
|
+
let dir: string;
|
|
70
|
+
let originalCwd: string;
|
|
71
|
+
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
originalCwd = process.cwd();
|
|
74
|
+
dir = mkdtempSync(join(tmpdir(), "byte-equality-"));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
process.chdir(originalCwd);
|
|
79
|
+
rmSync(dir, { recursive: true, force: true });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
for (const testCase of cases) {
|
|
83
|
+
it(`${testCase.name} — both binaries write identical bytes, stamps included`, async () => {
|
|
84
|
+
const pragmaDir = join(dir, "pragma");
|
|
85
|
+
const summonDir = join(dir, "summon");
|
|
86
|
+
mkdirSync(pragmaDir);
|
|
87
|
+
mkdirSync(summonDir);
|
|
88
|
+
|
|
89
|
+
// pragma path: the create command's batch execution (stamps by default).
|
|
90
|
+
const result = await executeGenerator(
|
|
91
|
+
testCase.gen,
|
|
92
|
+
{ ...testCase.answers, yes: true },
|
|
93
|
+
{
|
|
94
|
+
cwd: pragmaDir,
|
|
95
|
+
globalFlags: { llm: false, format: "text", verbose: false },
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
expect(result.tag).toBe("output");
|
|
99
|
+
|
|
100
|
+
// summon path: the Ink executor's exact composition over the shared
|
|
101
|
+
// core. Both binaries fold prompt defaults into the answers before
|
|
102
|
+
// generating; do the same here.
|
|
103
|
+
const answers = { ...testCase.answers };
|
|
104
|
+
for (const prompt of testCase.gen.prompts) {
|
|
105
|
+
if (!(prompt.name in answers) && prompt.default !== undefined) {
|
|
106
|
+
answers[prompt.name] = prompt.default;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
await runGeneratorTask(testCase.gen.generate(answers), {
|
|
110
|
+
cwd: summonDir,
|
|
111
|
+
promptHandler: answerPromptWithDefaults,
|
|
112
|
+
onEffectStart: createStampOnEffectStart(
|
|
113
|
+
createGeneratorStamp(testCase.gen),
|
|
114
|
+
),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const pragmaTree = readTree(pragmaDir);
|
|
118
|
+
const summonTree = readTree(summonDir);
|
|
119
|
+
|
|
120
|
+
expect([...pragmaTree.keys()].sort()).toEqual(
|
|
121
|
+
[...summonTree.keys()].sort(),
|
|
122
|
+
);
|
|
123
|
+
for (const [path, content] of pragmaTree) {
|
|
124
|
+
expect(summonTree.get(path)?.equals(content), path).toBe(true);
|
|
125
|
+
}
|
|
126
|
+
expect(pragmaTree.size).toBeGreaterThan(0);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
11
11
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
12
12
|
import type { PragmaRuntime } from "../../domains/shared/runtime.js";
|
|
13
|
+
import { TOKEN_READ_SURFACE_ENABLED } from "../../domains/token/featureFlag.js";
|
|
13
14
|
import createTestMcpClient from "../helpers/createTestMcpClient.js";
|
|
14
15
|
import createTestRuntime from "../helpers/createTestRuntime.js";
|
|
15
16
|
|
|
@@ -122,21 +123,24 @@ describe("all-fail batch lookups", () => {
|
|
|
122
123
|
expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
|
|
123
124
|
});
|
|
124
125
|
|
|
125
|
-
it(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
126
|
+
it.skipIf(!TOKEN_READ_SURFACE_ENABLED)(
|
|
127
|
+
"token_lookup: unknown token → error",
|
|
128
|
+
async () => {
|
|
129
|
+
const res = await client.callTool({
|
|
130
|
+
name: "token_lookup",
|
|
131
|
+
arguments: { names: ["color.nonexistent"] },
|
|
132
|
+
});
|
|
133
|
+
const body = parseEnvelope(res);
|
|
134
|
+
expect(body.ok).toBe(true);
|
|
135
|
+
const data = body.data as {
|
|
136
|
+
results: unknown[];
|
|
137
|
+
errors: { query: string; code: string }[];
|
|
138
|
+
};
|
|
139
|
+
expect(data.results).toHaveLength(0);
|
|
140
|
+
expect(data.errors).toHaveLength(1);
|
|
141
|
+
expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
|
|
142
|
+
},
|
|
143
|
+
);
|
|
140
144
|
});
|
|
141
145
|
|
|
142
146
|
// ---------------------------------------------------------------------------
|
|
@@ -144,33 +148,38 @@ describe("all-fail batch lookups", () => {
|
|
|
144
148
|
// ---------------------------------------------------------------------------
|
|
145
149
|
|
|
146
150
|
describe("empty result sets", () => {
|
|
147
|
-
it("standard_list with nonexistent category →
|
|
151
|
+
it("standard_list with nonexistent category → empty rows", async () => {
|
|
148
152
|
const res = await client.callTool({
|
|
149
153
|
name: "standard_list",
|
|
150
154
|
arguments: { category: "nonexistent_category" },
|
|
151
155
|
});
|
|
152
156
|
const body = parseEnvelope(res);
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
expect(
|
|
157
|
+
// The bundled pack's value-free category filter returns zero rows for
|
|
158
|
+
// an unmatched value; the old EMPTY_RESULTS error is a pinned
|
|
159
|
+
// PARITY_GAP of the standard cutover (packs have no emptyError hook).
|
|
160
|
+
expect(body.ok).toBe(true);
|
|
161
|
+
expect(body.data).toEqual([]);
|
|
162
|
+
expect((body.meta as { count: number }).count).toBe(0);
|
|
157
163
|
});
|
|
158
164
|
|
|
159
|
-
it(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
165
|
+
it.skipIf(!TOKEN_READ_SURFACE_ENABLED)(
|
|
166
|
+
"token_list with nonexistent category → empty or error",
|
|
167
|
+
async () => {
|
|
168
|
+
const res = await client.callTool({
|
|
169
|
+
name: "token_list",
|
|
170
|
+
arguments: { category: "NonexistentType" },
|
|
171
|
+
});
|
|
172
|
+
const body = parseEnvelope(res);
|
|
173
|
+
if (body.ok) {
|
|
174
|
+
const data = body.data as unknown[];
|
|
175
|
+
expect(data).toHaveLength(0);
|
|
176
|
+
} else {
|
|
177
|
+
// Token list may throw on empty results
|
|
178
|
+
const error = body.error as { code: string };
|
|
179
|
+
expect(error.code).toBeDefined();
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
);
|
|
174
183
|
});
|
|
175
184
|
|
|
176
185
|
// ---------------------------------------------------------------------------
|
|
@@ -245,19 +254,22 @@ describe("condensed mode error rendering", () => {
|
|
|
245
254
|
expect(text).toContain("nonexistent");
|
|
246
255
|
});
|
|
247
256
|
|
|
248
|
-
it(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
257
|
+
it.skipIf(!TOKEN_READ_SURFACE_ENABLED)(
|
|
258
|
+
"token_lookup condensed: includes error for unknown token",
|
|
259
|
+
async () => {
|
|
260
|
+
const res = await client.callTool({
|
|
261
|
+
name: "token_lookup",
|
|
262
|
+
arguments: {
|
|
263
|
+
names: ["color.primary", "color.nonexistent"],
|
|
264
|
+
condensed: true,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
const body = parseEnvelope(res);
|
|
268
|
+
expect(body.condensed).toBe(true);
|
|
269
|
+
const text = body.text as string;
|
|
270
|
+
expect(text).toContain("color.primary");
|
|
271
|
+
expect(text).toContain("Errors");
|
|
272
|
+
expect(text).toContain("color.nonexistent");
|
|
273
|
+
},
|
|
274
|
+
);
|
|
263
275
|
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end characterization of the real-execution path the executor merge
|
|
3
|
+
* (4a) rewired: every generator the CLI actually dispatches, and a real setup
|
|
4
|
+
* task, run through the shared UI-free core ({@link runGeneratorTask}) and
|
|
5
|
+
* produce their files on disk.
|
|
6
|
+
*
|
|
7
|
+
* The golden snapshots (executor-golden.test.ts) lock the *preview* modes
|
|
8
|
+
* (dry-run/json/llm), which never execute. This locks the *real* run: the
|
|
9
|
+
* merged core executes the genuine task trees — not test doubles — with the
|
|
10
|
+
* correct working-directory semantics.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
existsSync,
|
|
15
|
+
mkdirSync,
|
|
16
|
+
mkdtempSync,
|
|
17
|
+
readdirSync,
|
|
18
|
+
rmSync,
|
|
19
|
+
} from "node:fs";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { runGeneratorTask } from "@canonical/cli-core";
|
|
23
|
+
import { generators as componentGenerators } from "@canonical/summon-component";
|
|
24
|
+
import { generators as packageGenerators } from "@canonical/summon-package";
|
|
25
|
+
import type { Task } from "@canonical/task";
|
|
26
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
27
|
+
import setupMcp from "../../domains/setup/operations/setupMcp.js";
|
|
28
|
+
|
|
29
|
+
interface Case {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly build: () => Task<unknown>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const componentParams = { componentPath: "src/components/Button" };
|
|
35
|
+
const packageParams = { name: "@canonical/example", type: "library" };
|
|
36
|
+
|
|
37
|
+
const cases: Case[] = [
|
|
38
|
+
{
|
|
39
|
+
name: "component/react",
|
|
40
|
+
build: () =>
|
|
41
|
+
componentGenerators["component/react"].generate(componentParams),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "component/svelte",
|
|
45
|
+
build: () =>
|
|
46
|
+
componentGenerators["component/svelte"].generate(componentParams),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "component/lit",
|
|
50
|
+
build: () => componentGenerators["component/lit"].generate(componentParams),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "package/library",
|
|
54
|
+
build: () => packageGenerators.package.generate(packageParams),
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const countFiles = (root: string): number => {
|
|
59
|
+
let total = 0;
|
|
60
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
61
|
+
total += entry.isDirectory() ? countFiles(join(root, entry.name)) : 1;
|
|
62
|
+
}
|
|
63
|
+
return total;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
describe("shared executor — real execution over the dispatched tasks", () => {
|
|
67
|
+
let dir: string;
|
|
68
|
+
let originalCwd: string;
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
originalCwd = process.cwd();
|
|
72
|
+
dir = mkdtempSync(join(tmpdir(), "executor-execution-"));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
process.chdir(originalCwd);
|
|
77
|
+
rmSync(dir, { recursive: true, force: true });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
for (const testCase of cases) {
|
|
81
|
+
it(`${testCase.name} — executes for real in the target cwd`, async () => {
|
|
82
|
+
const targetDir = join(dir, "target");
|
|
83
|
+
mkdirSync(targetDir);
|
|
84
|
+
|
|
85
|
+
await runGeneratorTask(testCase.build(), { cwd: targetDir });
|
|
86
|
+
|
|
87
|
+
expect(countFiles(targetDir)).toBeGreaterThan(0);
|
|
88
|
+
expect(process.cwd()).toBe(originalCwd);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// A real setup task on the rewired production surface. setupMcp in force
|
|
93
|
+
// mode writes root-relative config (no prompts, no home-dir writes), so it
|
|
94
|
+
// is safe to execute in a sandbox.
|
|
95
|
+
it("setup/mcp (force) — writes the harness config for real", async () => {
|
|
96
|
+
const root = join(dir, "project");
|
|
97
|
+
mkdirSync(root);
|
|
98
|
+
|
|
99
|
+
await runGeneratorTask(setupMcp(root, "cursor"));
|
|
100
|
+
|
|
101
|
+
expect(existsSync(join(root, ".cursor", "mcp.json"))).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Characterization (golden) snapshots of the two current executors —
|
|
3
|
+
* `executeGenerator` (cli-core, drives `pragma create`) and `runSetupTask`
|
|
4
|
+
* (drives `pragma setup`) — across the real generators and setup tasks.
|
|
5
|
+
*
|
|
6
|
+
* These lock the *observable output* of each executor for representative,
|
|
7
|
+
* fully-answered inputs in every non-interactive mode. Both executors now run
|
|
8
|
+
* real execution through the shared UI-free core (`runGeneratorTask`), and the
|
|
9
|
+
* merged path must keep reproducing these byte-for-byte; the execution
|
|
10
|
+
* characterization test alongside asserts the real-run path. Interactive
|
|
11
|
+
* behavior is characterized separately, since it runs through the injected
|
|
12
|
+
* `promptHandler` seam.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { dirname, resolve } from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import type { CommandContext, CommandResult } from "@canonical/cli-core";
|
|
18
|
+
import { executeGenerator } from "@canonical/cli-core";
|
|
19
|
+
import { generators as componentGenerators } from "@canonical/summon-component";
|
|
20
|
+
import { generators as packageGenerators } from "@canonical/summon-package";
|
|
21
|
+
import { describe, expect, it } from "vitest";
|
|
22
|
+
import runSetupTask from "../../domains/setup/helpers/runSetupTask.js";
|
|
23
|
+
import setupCompletions from "../../domains/setup/operations/setupCompletions.js";
|
|
24
|
+
import setupLsp from "../../domains/setup/operations/setupLsp.js";
|
|
25
|
+
|
|
26
|
+
const ROOT = "/tmp/characterization-project";
|
|
27
|
+
|
|
28
|
+
const ctx = (format: "text" | "json", llm = false): CommandContext => ({
|
|
29
|
+
cwd: ROOT,
|
|
30
|
+
globalFlags: { llm, format, verbose: false },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Render an executor result to the plain string a user would see, with
|
|
35
|
+
* environment-specific bytes normalised so a committed baseline compares
|
|
36
|
+
* equal on every machine and in CI: ANSI color codes are stripped (chalk
|
|
37
|
+
* enables them under nx's FORCE_COLOR in CI but not in a plain local run),
|
|
38
|
+
* then the repo checkout root (package-generator plans embed absolute
|
|
39
|
+
* template source paths) and the home directory (shell-completion install
|
|
40
|
+
* targets embed `$HOME`) become stable tokens. The repo root is replaced
|
|
41
|
+
* first — on a dev machine it usually lives under `$HOME`.
|
|
42
|
+
*/
|
|
43
|
+
const plain = (result: CommandResult): string => {
|
|
44
|
+
let text =
|
|
45
|
+
result.tag === "output"
|
|
46
|
+
? result.render.plain(result.value)
|
|
47
|
+
: `[${result.tag}]`;
|
|
48
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching the ANSI escape byte is the point
|
|
49
|
+
text = text.replace(/\u001b\[[0-9;]*m/g, "");
|
|
50
|
+
const repoRoot = resolve(
|
|
51
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
52
|
+
"../../../../../..",
|
|
53
|
+
);
|
|
54
|
+
text = text.split(repoRoot).join("<repo>");
|
|
55
|
+
const home = process.env.HOME ?? "";
|
|
56
|
+
return home === "" ? text : text.split(home).join("<home>");
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// create — executeGenerator across the real component + package generators
|
|
61
|
+
// =============================================================================
|
|
62
|
+
|
|
63
|
+
const componentParams = { componentPath: "src/components/Button" };
|
|
64
|
+
const packageParams = { name: "@canonical/example", type: "library" };
|
|
65
|
+
|
|
66
|
+
describe("golden: executeGenerator — components", () => {
|
|
67
|
+
for (const framework of ["react", "svelte", "lit"] as const) {
|
|
68
|
+
const gen = componentGenerators[`component/${framework}`];
|
|
69
|
+
|
|
70
|
+
it(`${framework} — dry-run plan`, async () => {
|
|
71
|
+
const result = await executeGenerator(
|
|
72
|
+
gen,
|
|
73
|
+
{ ...componentParams, dryRun: true },
|
|
74
|
+
ctx("text"),
|
|
75
|
+
);
|
|
76
|
+
expect(plain(result)).toMatchSnapshot();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it(`${framework} — json plan`, async () => {
|
|
80
|
+
const result = await executeGenerator(
|
|
81
|
+
gen,
|
|
82
|
+
{ ...componentParams },
|
|
83
|
+
ctx("json"),
|
|
84
|
+
);
|
|
85
|
+
expect(plain(result)).toMatchSnapshot();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it(`${framework} — llm plan`, async () => {
|
|
89
|
+
const result = await executeGenerator(
|
|
90
|
+
gen,
|
|
91
|
+
{ ...componentParams },
|
|
92
|
+
ctx("text", true),
|
|
93
|
+
);
|
|
94
|
+
expect(plain(result)).toMatchSnapshot();
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("golden: executeGenerator — package", () => {
|
|
100
|
+
const gen = packageGenerators.package;
|
|
101
|
+
|
|
102
|
+
it("library — dry-run plan", async () => {
|
|
103
|
+
const result = await executeGenerator(
|
|
104
|
+
gen,
|
|
105
|
+
{ ...packageParams, dryRun: true },
|
|
106
|
+
ctx("text"),
|
|
107
|
+
);
|
|
108
|
+
expect(plain(result)).toMatchSnapshot();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("library — json plan", async () => {
|
|
112
|
+
const result = await executeGenerator(
|
|
113
|
+
gen,
|
|
114
|
+
{ ...packageParams },
|
|
115
|
+
ctx("json"),
|
|
116
|
+
);
|
|
117
|
+
expect(plain(result)).toMatchSnapshot();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// setup — runSetupTask across representative deterministic setup tasks
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
describe("golden: runSetupTask — setups", () => {
|
|
126
|
+
it("lsp — dry-run", async () => {
|
|
127
|
+
const result = await runSetupTask(setupLsp(ROOT), { dryRun: true });
|
|
128
|
+
expect(plain(result)).toMatchSnapshot();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("completions (bash) — dry-run", async () => {
|
|
132
|
+
const result = await runSetupTask(setupCompletions("bash"), {
|
|
133
|
+
dryRun: true,
|
|
134
|
+
});
|
|
135
|
+
expect(plain(result)).toMatchSnapshot();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("lsp — dry-run json", async () => {
|
|
139
|
+
const result = await runSetupTask(setupLsp(ROOT), {
|
|
140
|
+
dryRun: true,
|
|
141
|
+
format: "json",
|
|
142
|
+
});
|
|
143
|
+
expect(plain(result)).toMatchSnapshot();
|
|
144
|
+
});
|
|
145
|
+
});
|