@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
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled transitional `standard` pack.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the hand-written `standard` domain (~1,300 LOC of command/
|
|
5
|
+
* operation/formatter/orchestration/MCP wiring) with a declarative pack
|
|
6
|
+
* definition. `standard list|lookup|categories|sample` and the
|
|
7
|
+
* `standard_list|_lookup|_categories|_sample` MCP tools compile from this
|
|
8
|
+
* definition through the same kernel as every other pack, so the CLI core
|
|
9
|
+
* stops carrying standard-specific TypeScript.
|
|
10
|
+
*
|
|
11
|
+
* @remarks transitional — this ships inside pragma today; in P4 it moves
|
|
12
|
+
* into the `@canonical/code-standards` package as `stories/standard.json`
|
|
13
|
+
* alongside that package's prefix declaration, at which point the core
|
|
14
|
+
* carries no standard at all.
|
|
15
|
+
*
|
|
16
|
+
* Scope notes (semantic parity, not byte parity — see standardParity.test):
|
|
17
|
+
* - Rows/entities are the uniform pack shapes: the list SELECT binds the
|
|
18
|
+
* standard IRI to `uri` and renames `categoryName` → `category`, so JSON
|
|
19
|
+
* carries the same keys as the old summary items.
|
|
20
|
+
* - The old domain's `--digest`/`--detailed` LIST enrichment is not
|
|
21
|
+
* reproduced (pack lists have a single level); the same data is reachable
|
|
22
|
+
* through `standard lookup --detail …` and `standard sample`.
|
|
23
|
+
* - Lookup disclosure keeps three levels. The old digest's bespoke
|
|
24
|
+
* "first do-example truncated to 120 chars" rendering is not expressible
|
|
25
|
+
* generically; here `digest` gates the full `dos` expand (richer than the
|
|
26
|
+
* old digest) and `detailed` adds `donts`.
|
|
27
|
+
* - `extends` stays the raw IRI in JSON (renderers compact it at display
|
|
28
|
+
* time); the old domain compacted it in resolved data.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import type { StoryPackDefinition } from "../types.js";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The `standard` read stories as data.
|
|
35
|
+
*
|
|
36
|
+
* The list SELECT mirrors the query the old `listStandards` operation sent
|
|
37
|
+
* the store — `cs:name` and the category path stay OPTIONAL so a standard
|
|
38
|
+
* without a display name or category still appears — while `ORDER BY ?name`
|
|
39
|
+
* sorts by the human-facing name. The BIND reproduces the old
|
|
40
|
+
* `deriveStandardName` fallback declaratively: a standard without `cs:name`
|
|
41
|
+
* displays its IRI fragment with dots rendered as slashes
|
|
42
|
+
* (`…#react.component.props` → `react/component/props`). Category filtering
|
|
43
|
+
* is value-free (the category set lives in the graph) and search covers
|
|
44
|
+
* name + description; both are post-query row predicates, so user input
|
|
45
|
+
* never touches this query text.
|
|
46
|
+
*/
|
|
47
|
+
export const standardPack: StoryPackDefinition = {
|
|
48
|
+
noun: "standard",
|
|
49
|
+
description: "List all code standards",
|
|
50
|
+
toolDescription:
|
|
51
|
+
"List code standards. Optionally filter by category or search term.",
|
|
52
|
+
list: {
|
|
53
|
+
query: [
|
|
54
|
+
"SELECT ?uri ?name ?category ?description",
|
|
55
|
+
"WHERE {",
|
|
56
|
+
" ?uri a cs:CodeStandard ;",
|
|
57
|
+
" cs:description ?description .",
|
|
58
|
+
" OPTIONAL { ?uri cs:name ?n . }",
|
|
59
|
+
' BIND(COALESCE(?n, REPLACE(STRAFTER(STR(?uri), "#"), "\\\\.", "/")) AS ?name)',
|
|
60
|
+
" OPTIONAL {",
|
|
61
|
+
" ?uri cs:hasCategory ?cat .",
|
|
62
|
+
" ?cat cs:slug ?category .",
|
|
63
|
+
" }",
|
|
64
|
+
"}",
|
|
65
|
+
"ORDER BY ?name",
|
|
66
|
+
].join("\n"),
|
|
67
|
+
columns: [
|
|
68
|
+
{ field: "uri", label: "IRI" },
|
|
69
|
+
{ field: "name", label: "Name" },
|
|
70
|
+
{ field: "category", label: "Category" },
|
|
71
|
+
{ field: "description", label: "Description" },
|
|
72
|
+
],
|
|
73
|
+
filters: [
|
|
74
|
+
{
|
|
75
|
+
param: "category",
|
|
76
|
+
variable: "category",
|
|
77
|
+
description: "Filter by category name",
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
search: {
|
|
81
|
+
variables: ["name", "description"],
|
|
82
|
+
description: "Search in name and description",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
verbs: [
|
|
86
|
+
{
|
|
87
|
+
verb: "categories",
|
|
88
|
+
description: "List all standard categories with counts",
|
|
89
|
+
toolDescription: "List all code standard categories.",
|
|
90
|
+
// OPTIONAL keeps zero-count categories in the result (a category with
|
|
91
|
+
// no standards still lists, with count 0), mirroring the old
|
|
92
|
+
// listCategories operation.
|
|
93
|
+
query: [
|
|
94
|
+
"SELECT ?name (COUNT(?standard) AS ?count)",
|
|
95
|
+
"WHERE {",
|
|
96
|
+
" ?cat a cs:Category ;",
|
|
97
|
+
" cs:slug ?name .",
|
|
98
|
+
" OPTIONAL {",
|
|
99
|
+
" ?standard a cs:CodeStandard ;",
|
|
100
|
+
" cs:hasCategory ?cat .",
|
|
101
|
+
" }",
|
|
102
|
+
"}",
|
|
103
|
+
"GROUP BY ?name",
|
|
104
|
+
"ORDER BY ?name",
|
|
105
|
+
].join("\n"),
|
|
106
|
+
columns: [
|
|
107
|
+
{ field: "name", label: "Category" },
|
|
108
|
+
{ field: "count", label: "Standards" },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
lookup: {
|
|
113
|
+
by: "cs:name",
|
|
114
|
+
type: "cs:CodeStandard",
|
|
115
|
+
description:
|
|
116
|
+
"Look up detailed information for a standard by name, IRI, or glob",
|
|
117
|
+
toolDescription:
|
|
118
|
+
"Get detailed information about one or more code standards including dos and donts with code examples. Address standards by name, prefixed name (cs:…), absolute IRI, or glob pattern (react/component/*).",
|
|
119
|
+
fields: [
|
|
120
|
+
{
|
|
121
|
+
name: "category",
|
|
122
|
+
property: "cs:hasCategory/cs:slug",
|
|
123
|
+
label: "Category",
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "description",
|
|
127
|
+
property: "cs:description",
|
|
128
|
+
label: "Description",
|
|
129
|
+
},
|
|
130
|
+
{ name: "extends", property: "cs:extends", label: "Extends" },
|
|
131
|
+
],
|
|
132
|
+
expand: [
|
|
133
|
+
{
|
|
134
|
+
name: "dos",
|
|
135
|
+
heading: "Do",
|
|
136
|
+
relation: "cs:do",
|
|
137
|
+
select: [
|
|
138
|
+
{ name: "caption", property: "cs:description" },
|
|
139
|
+
{ name: "language", property: "cs:language" },
|
|
140
|
+
{ name: "code", property: "cs:code" },
|
|
141
|
+
],
|
|
142
|
+
level: "digest",
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: "donts",
|
|
146
|
+
heading: "Don't",
|
|
147
|
+
relation: "cs:dont",
|
|
148
|
+
select: [
|
|
149
|
+
{ name: "caption", property: "cs:description" },
|
|
150
|
+
{ name: "language", property: "cs:language" },
|
|
151
|
+
{ name: "code", property: "cs:code" },
|
|
152
|
+
],
|
|
153
|
+
level: "detailed",
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
disclosure: { levels: ["summary", "digest", "detailed"] },
|
|
157
|
+
sample: {
|
|
158
|
+
description:
|
|
159
|
+
"Return randomly selected complete standard instances as exemplars for shape discovery",
|
|
160
|
+
toolDescription:
|
|
161
|
+
"Return 1–5 randomly selected complete code standard instances as exemplars. Use BEFORE writing queries to see actual data shapes, property names, and value formats. Each call returns different instances.",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled transitional `tier` pack.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the hand-written `tier` domain (~179 LOC of command/operation/
|
|
5
|
+
* formatter/MCP wiring) with a declarative list story. `tier list` and the
|
|
6
|
+
* `tier_list` MCP tool are compiled from this definition through the same
|
|
7
|
+
* kernel as every other pack, so the CLI core stops carrying tier-specific
|
|
8
|
+
* TypeScript.
|
|
9
|
+
*
|
|
10
|
+
* @remarks transitional — this ships inside pragma today; in P4 it moves into
|
|
11
|
+
* the `@canonical/design-system` package as `stories/tier.json` alongside that
|
|
12
|
+
* package's prefix declaration, at which point the core carries no tier at all.
|
|
13
|
+
*
|
|
14
|
+
* Scope note: the old domain's `TierEntry.depth`/`parent` fields were always
|
|
15
|
+
* `0`/absent — tier hierarchy is encoded in the slash-separated *path string*
|
|
16
|
+
* (`apps/lxd`), not in graph edges, and the ordered-inheritance logic lives in
|
|
17
|
+
* `buildTierFilter`/`resolveTierChain` (a filter primitive other domains use).
|
|
18
|
+
* `tier list` is therefore a flat, name-ordered list, which this pack
|
|
19
|
+
* reproduces faithfully; there is no graph tree to project.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { StoryPackDefinition } from "../types.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The `tier` list story as data.
|
|
26
|
+
*
|
|
27
|
+
* The SELECT binds the tier IRI to `uri` and its `ds:name` to `name` (the
|
|
28
|
+
* honest mapping of the `ds:name` property), so `--format json` is the uniform
|
|
29
|
+
* `{uri, name}` pack shape and the generic renderer emits `- ` + "`ds:apps`" +
|
|
30
|
+
* ` — **Apps**` in llm mode. The store injects PREFIX declarations, so `ds:`
|
|
31
|
+
* resolves against the merged prefix map. The old domain's always-zero
|
|
32
|
+
* `depth` field is intentionally dropped.
|
|
33
|
+
*/
|
|
34
|
+
export const tierPack: StoryPackDefinition = {
|
|
35
|
+
noun: "tier",
|
|
36
|
+
description: "List all tiers in the design system ontology",
|
|
37
|
+
toolDescription: "List all tiers in the design-system ontology.",
|
|
38
|
+
list: {
|
|
39
|
+
query: [
|
|
40
|
+
"SELECT ?uri ?name WHERE {",
|
|
41
|
+
" ?uri a ds:Tier ;",
|
|
42
|
+
" ds:name ?name .",
|
|
43
|
+
"} ORDER BY ?name",
|
|
44
|
+
].join("\n"),
|
|
45
|
+
columns: [
|
|
46
|
+
{ field: "uri", label: "IRI" },
|
|
47
|
+
{ field: "name", label: "Name" },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { PragmaConfig } from "#config";
|
|
3
|
+
import { PragmaError } from "#error";
|
|
4
|
+
import { RECIPE_STORY } from "#testing";
|
|
5
|
+
import type { SemanticPackage } from "../../semanticPackage.js";
|
|
6
|
+
import { BUNDLED_PACKS } from "./bundled/index.js";
|
|
7
|
+
import realCollectPackStories from "./collectPackStories.js";
|
|
8
|
+
import {
|
|
9
|
+
buildReservedVerbs,
|
|
10
|
+
deriveReservedVerbs,
|
|
11
|
+
isReserved,
|
|
12
|
+
type ReservedVerbs,
|
|
13
|
+
} from "./reservedVerbs.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Test wrapper that isolates the always-present bundled packs: unit tests
|
|
17
|
+
* asserting exact entries pass `[]` so the default bundled `tier` pack does not
|
|
18
|
+
* pollute their expectations. Bundled behavior is covered separately below.
|
|
19
|
+
*/
|
|
20
|
+
function collectPackStories(
|
|
21
|
+
config: PragmaConfig,
|
|
22
|
+
packages: readonly SemanticPackage[],
|
|
23
|
+
reserved: ReservedVerbs,
|
|
24
|
+
): ReturnType<typeof realCollectPackStories> {
|
|
25
|
+
return realCollectPackStories(config, packages, reserved, []);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function makePackage(stories: SemanticPackage["stories"]): SemanticPackage {
|
|
29
|
+
return {
|
|
30
|
+
name: "@canonical/example",
|
|
31
|
+
version: "1.0.0",
|
|
32
|
+
source: "local",
|
|
33
|
+
graphs: [],
|
|
34
|
+
skills: [],
|
|
35
|
+
stories,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const CONFIG = { tier: undefined, channel: "normal" as const };
|
|
40
|
+
|
|
41
|
+
// A representative slice of the real built-in surface: leaf read nouns that
|
|
42
|
+
// own `list`/`lookup`, plus operational nouns (graph, config, create, graphql,
|
|
43
|
+
// setup, tokens) that own neither and so must be reserved wholesale.
|
|
44
|
+
const BUILTIN_PAIRS: readonly (readonly [string, string | undefined])[] = [
|
|
45
|
+
["standard", "list"],
|
|
46
|
+
["standard", "lookup"],
|
|
47
|
+
["standard", "categories"],
|
|
48
|
+
["standard", "sample"],
|
|
49
|
+
["block", "list"],
|
|
50
|
+
["block", "lookup"],
|
|
51
|
+
["token", "list"],
|
|
52
|
+
["token", "lookup"],
|
|
53
|
+
["tier", "list"],
|
|
54
|
+
["config", "show"],
|
|
55
|
+
["config", "tier"],
|
|
56
|
+
["config", "channel"],
|
|
57
|
+
["graph", "query"],
|
|
58
|
+
["graph", "inspect"],
|
|
59
|
+
["create", "component"],
|
|
60
|
+
["create", "package"],
|
|
61
|
+
["graphql", "build"],
|
|
62
|
+
["graphql", "check"],
|
|
63
|
+
["graphql", "serve"],
|
|
64
|
+
["setup", "all"],
|
|
65
|
+
["setup", "lsp"],
|
|
66
|
+
["tokens", "add-config"],
|
|
67
|
+
["info", undefined],
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
/** Run a thunk and return the error it throws (or `undefined`). */
|
|
71
|
+
function caught(run: () => unknown): unknown {
|
|
72
|
+
try {
|
|
73
|
+
run();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return error;
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
describe("collectPackStories", () => {
|
|
81
|
+
let warn: ReturnType<typeof vi.spyOn>;
|
|
82
|
+
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
warn = vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
afterEach(() => {
|
|
88
|
+
warn.mockRestore();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("collects config stories and validated package stories", () => {
|
|
92
|
+
const entries = collectPackStories(
|
|
93
|
+
{ ...CONFIG, stories: [RECIPE_STORY] },
|
|
94
|
+
[
|
|
95
|
+
makePackage([
|
|
96
|
+
{
|
|
97
|
+
path: "/pkg/stories/other.json",
|
|
98
|
+
definition: { ...RECIPE_STORY, noun: "other" },
|
|
99
|
+
},
|
|
100
|
+
]),
|
|
101
|
+
],
|
|
102
|
+
buildReservedVerbs([["block", undefined]]),
|
|
103
|
+
);
|
|
104
|
+
expect(entries.map((entry) => entry.definition.noun)).toEqual([
|
|
105
|
+
"recipe",
|
|
106
|
+
"other",
|
|
107
|
+
]);
|
|
108
|
+
expect(entries.at(0)?.source).toBe("config");
|
|
109
|
+
expect(entries.at(1)?.source).toBe("/pkg/stories/other.json");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("lets config stories win noun collisions against packages", () => {
|
|
113
|
+
const entries = collectPackStories(
|
|
114
|
+
{ ...CONFIG, stories: [RECIPE_STORY] },
|
|
115
|
+
[
|
|
116
|
+
makePackage([
|
|
117
|
+
{ path: "/pkg/stories/recipe.json", definition: RECIPE_STORY },
|
|
118
|
+
]),
|
|
119
|
+
],
|
|
120
|
+
buildReservedVerbs([]),
|
|
121
|
+
);
|
|
122
|
+
expect(entries).toHaveLength(1);
|
|
123
|
+
expect(entries.at(0)?.source).toBe("config");
|
|
124
|
+
expect(warn).toHaveBeenCalledWith(
|
|
125
|
+
expect.stringContaining("already provided"),
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("skips invalid package stories with a warning", () => {
|
|
130
|
+
const entries = collectPackStories(
|
|
131
|
+
CONFIG,
|
|
132
|
+
[
|
|
133
|
+
makePackage([
|
|
134
|
+
{ path: "/pkg/stories/broken.json", definition: { nope: true } },
|
|
135
|
+
]),
|
|
136
|
+
],
|
|
137
|
+
buildReservedVerbs([]),
|
|
138
|
+
);
|
|
139
|
+
expect(entries).toHaveLength(0);
|
|
140
|
+
expect(warn).toHaveBeenCalledWith(
|
|
141
|
+
expect.stringContaining("skipping story"),
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("throws when a config story shadows a built-in noun", () => {
|
|
146
|
+
expect(() =>
|
|
147
|
+
collectPackStories(
|
|
148
|
+
{ ...CONFIG, stories: [{ ...RECIPE_STORY, noun: "block" }] },
|
|
149
|
+
[],
|
|
150
|
+
buildReservedVerbs([["block", undefined]]),
|
|
151
|
+
),
|
|
152
|
+
).toThrow(PragmaError);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("skips package stories that shadow built-in nouns", () => {
|
|
156
|
+
const entries = collectPackStories(
|
|
157
|
+
CONFIG,
|
|
158
|
+
[
|
|
159
|
+
makePackage([
|
|
160
|
+
{
|
|
161
|
+
path: "/pkg/stories/block.json",
|
|
162
|
+
definition: { ...RECIPE_STORY, noun: "block" },
|
|
163
|
+
},
|
|
164
|
+
]),
|
|
165
|
+
],
|
|
166
|
+
buildReservedVerbs([["block", undefined]]),
|
|
167
|
+
);
|
|
168
|
+
expect(entries).toHaveLength(0);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("admits a pack once its noun's verbs leave the reserved map", () => {
|
|
172
|
+
// Post-migration simulation: `standard`'s list/lookup wrappers are gone,
|
|
173
|
+
// so only categories/sample remain reserved — a `standard` pack (which
|
|
174
|
+
// emits list + lookup) is now admissible. `info` is a bare (whole-noun)
|
|
175
|
+
// built-in, so an `info` pack stays blocked. This is the keystone: the
|
|
176
|
+
// per-verb guard makes the leaf cutover incremental.
|
|
177
|
+
const reserved = buildReservedVerbs([
|
|
178
|
+
["standard", "categories"],
|
|
179
|
+
["standard", "sample"],
|
|
180
|
+
["info", undefined],
|
|
181
|
+
]);
|
|
182
|
+
|
|
183
|
+
const entries = collectPackStories(
|
|
184
|
+
CONFIG,
|
|
185
|
+
[
|
|
186
|
+
makePackage([
|
|
187
|
+
{
|
|
188
|
+
path: "/pkg/stories/standard.json",
|
|
189
|
+
definition: { ...RECIPE_STORY, noun: "standard" },
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
path: "/pkg/stories/info.json",
|
|
193
|
+
definition: { ...RECIPE_STORY, noun: "info" },
|
|
194
|
+
},
|
|
195
|
+
]),
|
|
196
|
+
],
|
|
197
|
+
reserved,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
expect(entries.map((entry) => entry.definition.noun)).toEqual(["standard"]);
|
|
201
|
+
expect(warn).toHaveBeenCalledWith(
|
|
202
|
+
expect.stringContaining("shadows built-in command"),
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Regression restoration (FIX 1): operational nouns own no list/lookup, so
|
|
207
|
+
// deriveReservedVerbs promotes them to a whole-noun reservation. Since a
|
|
208
|
+
// pack always emits `list`, a pack targeting an operational namespace must
|
|
209
|
+
// stay BLOCKED — this is the load-bearing proof that the per-verb flip did
|
|
210
|
+
// not silently open pragma's core operational namespaces to packs.
|
|
211
|
+
describe("operational nouns stay reserved wholesale", () => {
|
|
212
|
+
const reserved = deriveReservedVerbs(BUILTIN_PAIRS);
|
|
213
|
+
|
|
214
|
+
it("promotes every operational noun to a whole-noun reservation", () => {
|
|
215
|
+
for (const noun of ["config", "graph", "create", "graphql", "setup"]) {
|
|
216
|
+
expect(isReserved(reserved, noun, "list")).toBe(true);
|
|
217
|
+
}
|
|
218
|
+
// `tokens` (the plural add-config noun) owns only `add-config`.
|
|
219
|
+
expect(isReserved(reserved, "tokens", "list")).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("throws a config error when a config story shadows an operational noun", () => {
|
|
223
|
+
const error = caught(() =>
|
|
224
|
+
collectPackStories(
|
|
225
|
+
{ ...CONFIG, stories: [{ ...RECIPE_STORY, noun: "graph" }] },
|
|
226
|
+
[],
|
|
227
|
+
reserved,
|
|
228
|
+
),
|
|
229
|
+
);
|
|
230
|
+
expect(error).toBeInstanceOf(PragmaError);
|
|
231
|
+
expect((error as PragmaError).code).toBe("CONFIG_ERROR");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("skips a package story that shadows an operational noun", () => {
|
|
235
|
+
const entries = collectPackStories(
|
|
236
|
+
CONFIG,
|
|
237
|
+
[
|
|
238
|
+
makePackage([
|
|
239
|
+
{
|
|
240
|
+
path: "/pkg/stories/graph.json",
|
|
241
|
+
definition: { ...RECIPE_STORY, noun: "graph" },
|
|
242
|
+
},
|
|
243
|
+
]),
|
|
244
|
+
],
|
|
245
|
+
reserved,
|
|
246
|
+
);
|
|
247
|
+
expect(entries).toHaveLength(0);
|
|
248
|
+
expect(warn).toHaveBeenCalledWith(
|
|
249
|
+
expect.stringContaining("shadows built-in command"),
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Keystone incremental window (FIX 3): `standard`'s `list` wrapper is
|
|
255
|
+
// deleted but `lookup` is still built-in (alongside categories/sample).
|
|
256
|
+
describe("incremental leaf cutover window", () => {
|
|
257
|
+
const reserved = deriveReservedVerbs([
|
|
258
|
+
["standard", "lookup"],
|
|
259
|
+
["standard", "categories"],
|
|
260
|
+
["standard", "sample"],
|
|
261
|
+
["info", undefined],
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
it("keeps `standard` per-verb because it still owns lookup (not promoted)", () => {
|
|
265
|
+
// Disjointness check: the noun retains a read verb, so it must NOT be
|
|
266
|
+
// wholesale-promoted — otherwise `list` could never be freed.
|
|
267
|
+
expect(isReserved(reserved, "standard", "list")).toBe(false);
|
|
268
|
+
expect(isReserved(reserved, "standard", "lookup")).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("admits a standard pack that declares only list", () => {
|
|
272
|
+
const listOnly = { ...RECIPE_STORY, noun: "standard", lookup: undefined };
|
|
273
|
+
const entries = collectPackStories(
|
|
274
|
+
CONFIG,
|
|
275
|
+
[
|
|
276
|
+
makePackage([
|
|
277
|
+
{ path: "/pkg/stories/standard.json", definition: listOnly },
|
|
278
|
+
]),
|
|
279
|
+
],
|
|
280
|
+
reserved,
|
|
281
|
+
);
|
|
282
|
+
expect(entries.map((entry) => entry.definition.noun)).toEqual([
|
|
283
|
+
"standard",
|
|
284
|
+
]);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("rejects a standard pack that also declares lookup (config throws)", () => {
|
|
288
|
+
const error = caught(() =>
|
|
289
|
+
collectPackStories(
|
|
290
|
+
{ ...CONFIG, stories: [{ ...RECIPE_STORY, noun: "standard" }] },
|
|
291
|
+
[],
|
|
292
|
+
reserved,
|
|
293
|
+
),
|
|
294
|
+
);
|
|
295
|
+
expect(error).toBeInstanceOf(PragmaError);
|
|
296
|
+
expect((error as PragmaError).code).toBe("CONFIG_ERROR");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("rejects a standard pack that also declares lookup (package skips)", () => {
|
|
300
|
+
const entries = collectPackStories(
|
|
301
|
+
CONFIG,
|
|
302
|
+
[
|
|
303
|
+
makePackage([
|
|
304
|
+
{
|
|
305
|
+
path: "/pkg/stories/standard.json",
|
|
306
|
+
definition: { ...RECIPE_STORY, noun: "standard" },
|
|
307
|
+
},
|
|
308
|
+
]),
|
|
309
|
+
],
|
|
310
|
+
reserved,
|
|
311
|
+
);
|
|
312
|
+
expect(entries).toHaveLength(0);
|
|
313
|
+
expect(warn).toHaveBeenCalledWith(
|
|
314
|
+
expect.stringContaining("shadows built-in command"),
|
|
315
|
+
);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("rejects a list-only standard pack whose extra verb is still built-in", () => {
|
|
319
|
+
// Pack v1 emissions beyond list/lookup are guarded too: `categories`
|
|
320
|
+
// here collides with the still-registered built-in wrapper.
|
|
321
|
+
const withCategories = {
|
|
322
|
+
...RECIPE_STORY,
|
|
323
|
+
noun: "standard",
|
|
324
|
+
lookup: undefined,
|
|
325
|
+
verbs: [
|
|
326
|
+
{
|
|
327
|
+
verb: "categories",
|
|
328
|
+
query: RECIPE_STORY.list.query,
|
|
329
|
+
columns: RECIPE_STORY.list.columns,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
};
|
|
333
|
+
const error = caught(() =>
|
|
334
|
+
collectPackStories(
|
|
335
|
+
{ ...CONFIG, stories: [withCategories] },
|
|
336
|
+
[],
|
|
337
|
+
reserved,
|
|
338
|
+
),
|
|
339
|
+
);
|
|
340
|
+
expect(error).toBeInstanceOf(PragmaError);
|
|
341
|
+
expect((error as PragmaError).message).toContain("standard categories");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("rejects a pack whose sample capability is still built-in", () => {
|
|
345
|
+
const withSample = {
|
|
346
|
+
...RECIPE_STORY,
|
|
347
|
+
noun: "standard",
|
|
348
|
+
lookup: {
|
|
349
|
+
// biome-ignore lint/style/noNonNullAssertion: fixture always declares lookup
|
|
350
|
+
...RECIPE_STORY.lookup!,
|
|
351
|
+
sample: true as const,
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
const error = caught(() =>
|
|
355
|
+
collectPackStories({ ...CONFIG, stories: [withSample] }, [], reserved),
|
|
356
|
+
);
|
|
357
|
+
expect(error).toBeInstanceOf(PragmaError);
|
|
358
|
+
expect((error as PragmaError).message).toContain("standard sample");
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Bundled transitional packs use the REAL collect (not the isolating wrapper).
|
|
363
|
+
describe("bundled packs", () => {
|
|
364
|
+
it("includes bundled packs by default, lowest precedence", () => {
|
|
365
|
+
const entries = realCollectPackStories(
|
|
366
|
+
CONFIG,
|
|
367
|
+
[],
|
|
368
|
+
buildReservedVerbs([]),
|
|
369
|
+
);
|
|
370
|
+
const nouns = entries.map((entry) => entry.definition.noun);
|
|
371
|
+
// Every bundled pack is present, tagged with a `bundled:` source.
|
|
372
|
+
for (const pack of BUNDLED_PACKS) {
|
|
373
|
+
expect(nouns).toContain(pack.noun);
|
|
374
|
+
const entry = entries.find((e) => e.definition.noun === pack.noun);
|
|
375
|
+
expect(entry?.source).toBe(`bundled:${pack.noun}`);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("lets a config story override a bundled noun (config wins)", () => {
|
|
380
|
+
const bundledNoun = BUNDLED_PACKS[0]?.noun ?? "tier";
|
|
381
|
+
const entries = realCollectPackStories(
|
|
382
|
+
{ ...CONFIG, stories: [{ ...RECIPE_STORY, noun: bundledNoun }] },
|
|
383
|
+
[],
|
|
384
|
+
buildReservedVerbs([]),
|
|
385
|
+
);
|
|
386
|
+
const entry = entries.find((e) => e.definition.noun === bundledNoun);
|
|
387
|
+
// The config-declared story wins; the bundled one yields silently.
|
|
388
|
+
expect(entry?.source).toBe("config");
|
|
389
|
+
expect(
|
|
390
|
+
entries.filter((e) => e.definition.noun === bundledNoun),
|
|
391
|
+
).toHaveLength(1);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("yields a bundled noun to a still-reserved built-in", () => {
|
|
395
|
+
// If a bundled noun is still a built-in (not yet cut over), the bundled
|
|
396
|
+
// pack must not shadow it.
|
|
397
|
+
const bundledNoun = BUNDLED_PACKS[0]?.noun ?? "tier";
|
|
398
|
+
const entries = realCollectPackStories(
|
|
399
|
+
CONFIG,
|
|
400
|
+
[],
|
|
401
|
+
buildReservedVerbs([[bundledNoun, undefined]]),
|
|
402
|
+
);
|
|
403
|
+
expect(entries.map((e) => e.definition.noun)).not.toContain(bundledNoun);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
});
|