@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,27 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import resolveConfiguredGraphs from "./resolveConfiguredGraphs.js";
|
|
6
|
+
|
|
7
|
+
describe("resolveConfiguredGraphs", () => {
|
|
8
|
+
let dir: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
dir = mkdtempSync(join(tmpdir(), "pragma-rcg-"));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
rmSync(dir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("resolves the default semantic packages' TTL graphs", async () => {
|
|
19
|
+
// No pragma.config.json in the temp dir → mergeAndParseRefs falls back to
|
|
20
|
+
// the default packages, which resolve from node_modules in the workspace.
|
|
21
|
+
const graphs = await resolveConfiguredGraphs(dir);
|
|
22
|
+
|
|
23
|
+
expect(graphs.length).toBeGreaterThan(0);
|
|
24
|
+
expect(graphs[0]).toMatchObject({ format: "turtle" });
|
|
25
|
+
expect(typeof graphs[0]?.content).toBe("string");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve every TTL graph from the semantically-configured packages.
|
|
3
|
+
*
|
|
4
|
+
* Reads `pragma.config.json`, merges its `packages` with the global refs
|
|
5
|
+
* and the built-in defaults (a non-empty `packages` list replaces the set
|
|
6
|
+
* entirely; an empty or absent list falls back to global refs merged over
|
|
7
|
+
* the defaults), resolves each package through the loader chain
|
|
8
|
+
* (local > git > bundled), and returns the union of their definitions/ and
|
|
9
|
+
* data/ graph content — the same discovery the runtime store performs at
|
|
10
|
+
* boot, minus the store.
|
|
11
|
+
*
|
|
12
|
+
* @note Impure — reads config and package files from disk.
|
|
13
|
+
*
|
|
14
|
+
* @param cwd - Working directory used to locate `pragma.config.json`.
|
|
15
|
+
* @returns The TTL graph content of every resolved package.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readConfig } from "#config";
|
|
19
|
+
import {
|
|
20
|
+
createBundledLoader,
|
|
21
|
+
createGitLoader,
|
|
22
|
+
createLocalLoader,
|
|
23
|
+
} from "./loaders/index.js";
|
|
24
|
+
import { mergeAndParseRefs } from "./mergeAndParseRefs.js";
|
|
25
|
+
import {
|
|
26
|
+
type GraphContent,
|
|
27
|
+
resolveSemanticPackages,
|
|
28
|
+
} from "./semanticPackage.js";
|
|
29
|
+
|
|
30
|
+
export default async function resolveConfiguredGraphs(
|
|
31
|
+
cwd: string,
|
|
32
|
+
): Promise<GraphContent[]> {
|
|
33
|
+
const config = readConfig(cwd);
|
|
34
|
+
const refs = mergeAndParseRefs(config.packages);
|
|
35
|
+
const packages = await resolveSemanticPackages(refs, [
|
|
36
|
+
createLocalLoader(),
|
|
37
|
+
createGitLoader(),
|
|
38
|
+
createBundledLoader(),
|
|
39
|
+
]);
|
|
40
|
+
return packages.flatMap((pkg) => pkg.graphs);
|
|
41
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { LookupResult } from "./contracts.js";
|
|
3
|
+
import resolveLookupExitCode from "./resolveLookupExitCode.js";
|
|
4
|
+
|
|
5
|
+
function makeResult<T>(
|
|
6
|
+
results: T[],
|
|
7
|
+
errors: LookupResult<T>["errors"],
|
|
8
|
+
): LookupResult<T> {
|
|
9
|
+
return { results, errors };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("resolveLookupExitCode", () => {
|
|
13
|
+
it("returns 0 when at least one result was found", () => {
|
|
14
|
+
const result = makeResult(
|
|
15
|
+
[{ name: "ok" }],
|
|
16
|
+
[{ query: "miss", code: "ENTITY_NOT_FOUND", message: "not found" }],
|
|
17
|
+
);
|
|
18
|
+
expect(resolveLookupExitCode(result)).toBe(0);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("returns 0 when there are no errors", () => {
|
|
22
|
+
expect(resolveLookupExitCode(makeResult([{ name: "ok" }], []))).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("maps an all-not-found lookup to exit 1", () => {
|
|
26
|
+
const result = makeResult(
|
|
27
|
+
[],
|
|
28
|
+
[{ query: "x", code: "ENTITY_NOT_FOUND", message: "not found" }],
|
|
29
|
+
);
|
|
30
|
+
expect(resolveLookupExitCode(result)).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("maps an all-invalid-input lookup to exit 3", () => {
|
|
34
|
+
const result = makeResult(
|
|
35
|
+
[],
|
|
36
|
+
[{ query: "x", code: "INVALID_INPUT", message: "bad" }],
|
|
37
|
+
);
|
|
38
|
+
expect(resolveLookupExitCode(result)).toBe(3);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("falls back to exit 1 for an unrecognised error code", () => {
|
|
42
|
+
const result = makeResult(
|
|
43
|
+
[],
|
|
44
|
+
[{ query: "x", code: "NOT_A_REAL_CODE", message: "?" }],
|
|
45
|
+
);
|
|
46
|
+
expect(resolveLookupExitCode(result)).toBe(1);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the process exit code for a lookup result.
|
|
3
|
+
*
|
|
4
|
+
* A lookup that produced at least one result — even alongside some failures —
|
|
5
|
+
* is a partial success and exits 0. A lookup where every query failed exits
|
|
6
|
+
* non-zero, mapping the first error's code to its exit code so callers and
|
|
7
|
+
* scripts can detect the failure. The errors are still rendered inline for the
|
|
8
|
+
* human reader.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { ERROR_CODES, type ErrorCode } from "#error";
|
|
12
|
+
// mapExitCode is imported by its direct path, not the #pipeline barrel: the
|
|
13
|
+
// barrel re-exports runCli/createProgram, which would form an import cycle
|
|
14
|
+
// through this shared utility and break module initialization.
|
|
15
|
+
import mapExitCode from "../../pipeline/mapExitCode.js";
|
|
16
|
+
import type { LookupResult } from "./contracts.js";
|
|
17
|
+
|
|
18
|
+
/** Default non-zero exit when a lookup error carries an unrecognised code. */
|
|
19
|
+
const FALLBACK_LOOKUP_EXIT_CODE = 1;
|
|
20
|
+
|
|
21
|
+
/** Narrow an arbitrary error-code string to a known {@link ErrorCode}. */
|
|
22
|
+
function isErrorCode(code: string | undefined): code is ErrorCode {
|
|
23
|
+
return (
|
|
24
|
+
code !== undefined && (ERROR_CODES as readonly string[]).includes(code)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the exit code a lookup command should report.
|
|
30
|
+
*
|
|
31
|
+
* @param result - The resolved lookup result.
|
|
32
|
+
* @returns 0 on success/partial success, else the mapped non-zero exit code.
|
|
33
|
+
*/
|
|
34
|
+
export default function resolveLookupExitCode<T>(
|
|
35
|
+
result: LookupResult<T>,
|
|
36
|
+
): number {
|
|
37
|
+
if (result.results.length > 0 || result.errors.length === 0) return 0;
|
|
38
|
+
const firstCode = result.errors.at(0)?.code;
|
|
39
|
+
return isErrorCode(firstCode)
|
|
40
|
+
? mapExitCode(firstCode)
|
|
41
|
+
: FALLBACK_LOOKUP_EXIT_CODE;
|
|
42
|
+
}
|
|
@@ -10,14 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import type { SourceSpec } from "@canonical/ke";
|
|
12
12
|
import { readConfig } from "#config";
|
|
13
|
-
import {
|
|
14
|
-
type PackageRef,
|
|
15
|
-
parsePackageEntry,
|
|
16
|
-
type RawPackageEntry,
|
|
17
|
-
} from "../refs/operations/parseRef.js";
|
|
18
|
-
import readGlobalRefs from "../refs/operations/readGlobalRefs.js";
|
|
19
13
|
import { bootStore } from "./bootStore.js";
|
|
20
|
-
import {
|
|
14
|
+
import { mergeAndParseRefs } from "./mergeAndParseRefs.js";
|
|
21
15
|
import type { PragmaRuntime } from "./types/index.js";
|
|
22
16
|
|
|
23
17
|
export type { PragmaRuntime } from "./types/index.js";
|
|
@@ -43,7 +37,8 @@ export async function bootPragma(options?: {
|
|
|
43
37
|
const cwd = options?.cwd ?? process.cwd();
|
|
44
38
|
const config = readConfig(cwd);
|
|
45
39
|
|
|
46
|
-
// Merge package refs:
|
|
40
|
+
// Merge package refs: a non-empty config list replaces global refs and
|
|
41
|
+
// defaults; otherwise global refs merge over the defaults.
|
|
47
42
|
const refs = options?.sources
|
|
48
43
|
? undefined
|
|
49
44
|
: mergeAndParseRefs(config.packages);
|
|
@@ -53,6 +48,7 @@ export async function bootPragma(options?: {
|
|
|
53
48
|
sources: options?.sources,
|
|
54
49
|
refs,
|
|
55
50
|
trace: config.trace,
|
|
51
|
+
prefixes: config.prefixes,
|
|
56
52
|
});
|
|
57
53
|
|
|
58
54
|
return {
|
|
@@ -63,58 +59,3 @@ export async function bootPragma(options?: {
|
|
|
63
59
|
dispose: () => store.dispose(),
|
|
64
60
|
};
|
|
65
61
|
}
|
|
66
|
-
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
// Ref merging
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Merge global refs, project config packages, and defaults into a final
|
|
73
|
-
* parsed PackageRef array.
|
|
74
|
-
*
|
|
75
|
-
* Priority: project packages > global refs > hardcoded defaults.
|
|
76
|
-
* Merging is by package name — a project entry for "@canonical/foo"
|
|
77
|
-
* overrides the global entry for the same package.
|
|
78
|
-
*/
|
|
79
|
-
function mergeAndParseRefs(
|
|
80
|
-
projectPackages?: ReadonlyArray<RawPackageEntry>,
|
|
81
|
-
): PackageRef[] {
|
|
82
|
-
const globalEntries = readGlobalRefs();
|
|
83
|
-
|
|
84
|
-
// If neither project nor global defines packages, return undefined
|
|
85
|
-
// so resolvePackages() uses hardcoded defaults.
|
|
86
|
-
if (
|
|
87
|
-
(!projectPackages || projectPackages.length === 0) &&
|
|
88
|
-
globalEntries.length === 0
|
|
89
|
-
) {
|
|
90
|
-
return DEFAULT_PACKAGES.map((pkg) => parsePackageEntry(pkg));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Build a name → entry map, global first, project overrides.
|
|
94
|
-
const merged = new Map<string, RawPackageEntry>();
|
|
95
|
-
|
|
96
|
-
// Start with defaults (lowest priority)
|
|
97
|
-
for (const entry of DEFAULT_PACKAGES) {
|
|
98
|
-
const name = typeof entry === "string" ? entry : entry.name;
|
|
99
|
-
merged.set(name, entry);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Global overrides defaults
|
|
103
|
-
for (const entry of globalEntries) {
|
|
104
|
-
const name = typeof entry === "string" ? entry : entry.name;
|
|
105
|
-
merged.set(name, entry);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Project overrides global
|
|
109
|
-
if (projectPackages) {
|
|
110
|
-
// When project explicitly declares packages, it's a full replacement
|
|
111
|
-
// of the package list — clear defaults and global, use project only.
|
|
112
|
-
merged.clear();
|
|
113
|
-
for (const entry of projectPackages) {
|
|
114
|
-
const name = typeof entry === "string" ? entry : entry.name;
|
|
115
|
-
merged.set(name, entry);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return [...merged.values()].map(parsePackageEntry);
|
|
120
|
-
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { PackageRef } from "../refs/operations/parseRef.js";
|
|
3
|
+
import type { PackageLoader, SemanticPackage } from "./semanticPackage.js";
|
|
4
|
+
import { resolveSemanticPackages } from "./semanticPackage.js";
|
|
5
|
+
|
|
6
|
+
function makeRef(pkg: string): PackageRef {
|
|
7
|
+
return { kind: "npm", pkg };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function makePackage(name: string): SemanticPackage {
|
|
11
|
+
return {
|
|
12
|
+
name,
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
source: "local",
|
|
15
|
+
graphs: [{ path: `${name}/data.ttl`, content: "", format: "turtle" }],
|
|
16
|
+
skills: [],
|
|
17
|
+
stories: [],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("resolveSemanticPackages", () => {
|
|
22
|
+
it("resolves each ref through the loader chain", async () => {
|
|
23
|
+
const loader: PackageLoader = {
|
|
24
|
+
name: "local",
|
|
25
|
+
resolve: (ref) => makePackage(ref.pkg),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const packages = await resolveSemanticPackages(
|
|
29
|
+
[makeRef("a"), makeRef("b")],
|
|
30
|
+
[loader],
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(packages.map((pkg) => pkg.name)).toEqual(["a", "b"]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("drops refs no loader can resolve", async () => {
|
|
37
|
+
const loader: PackageLoader = {
|
|
38
|
+
name: "local",
|
|
39
|
+
resolve: (ref) => (ref.pkg === "a" ? makePackage("a") : undefined),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const packages = await resolveSemanticPackages(
|
|
43
|
+
[makeRef("a"), makeRef("b")],
|
|
44
|
+
[loader],
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(packages.map((pkg) => pkg.name)).toEqual(["a"]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Regression: the bundled loader returns ONE cached aggregate package for
|
|
51
|
+
// every ref it serves. Including it once per ref loaded its graphs into
|
|
52
|
+
// the store multiple times, re-minting blank nodes on each parse — which
|
|
53
|
+
// triplicated blank-node subgraphs (standard do/don't examples) whenever
|
|
54
|
+
// all three default refs fell back to the bundled data.
|
|
55
|
+
it("includes a package instance resolved for multiple refs only once", async () => {
|
|
56
|
+
const aggregate = makePackage("(bundled)");
|
|
57
|
+
const loader: PackageLoader = {
|
|
58
|
+
name: "bundled",
|
|
59
|
+
resolve: () => aggregate,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const packages = await resolveSemanticPackages(
|
|
63
|
+
[makeRef("a"), makeRef("b"), makeRef("c")],
|
|
64
|
+
[loader],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(packages).toHaveLength(1);
|
|
68
|
+
expect(packages[0]).toBe(aggregate);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("keeps distinct packages from a mixed loader chain", async () => {
|
|
72
|
+
const aggregate = makePackage("(bundled)");
|
|
73
|
+
const git: PackageLoader = {
|
|
74
|
+
name: "git",
|
|
75
|
+
resolve: (ref) => (ref.pkg === "a" ? makePackage("a") : undefined),
|
|
76
|
+
};
|
|
77
|
+
const bundled: PackageLoader = {
|
|
78
|
+
name: "bundled",
|
|
79
|
+
resolve: () => aggregate,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const packages = await resolveSemanticPackages(
|
|
83
|
+
[makeRef("a"), makeRef("b"), makeRef("c")],
|
|
84
|
+
[git, bundled],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(packages.map((pkg) => pkg.name)).toEqual(["a", "(bundled)"]);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -24,7 +24,18 @@ export interface GraphContent {
|
|
|
24
24
|
readonly format: "turtle";
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/** A discovered
|
|
27
|
+
/** A discovered story-pack JSON file with its content. */
|
|
28
|
+
export interface StoryFileEntry {
|
|
29
|
+
/**
|
|
30
|
+
* Original file path, or a virtual identifier for embedded sources
|
|
31
|
+
* (e.g. `(bundled)/…`) — for provenance/debugging, not guaranteed to
|
|
32
|
+
* exist on disk.
|
|
33
|
+
*/
|
|
34
|
+
readonly path: string;
|
|
35
|
+
/** The file's parsed JSON — validated by the story-pack compiler. */
|
|
36
|
+
readonly definition: unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
export interface SkillEntry {
|
|
29
40
|
/** Absolute path to the skill directory (must exist on disk for symlinking). */
|
|
30
41
|
readonly dir: string;
|
|
@@ -42,8 +53,20 @@ export interface SemanticPackage {
|
|
|
42
53
|
readonly source: "local" | "git" | "bundled";
|
|
43
54
|
/** TTL graph content to load into the ke store. */
|
|
44
55
|
readonly graphs: GraphContent[];
|
|
56
|
+
/**
|
|
57
|
+
* Namespace prefixes this package declares, merged over the core prefix
|
|
58
|
+
* map at boot (see {@link resolvePrefixes}). Read from the package's
|
|
59
|
+
* `package.json` `pragma.prefixes` field. Absent when the package
|
|
60
|
+
* declares none.
|
|
61
|
+
*/
|
|
62
|
+
readonly prefixes?: Readonly<Record<string, string>>;
|
|
45
63
|
/** Skills available in this package. */
|
|
46
64
|
readonly skills: SkillEntry[];
|
|
65
|
+
/**
|
|
66
|
+
* Raw story-pack files shipped by this package (`stories/*.json`).
|
|
67
|
+
* Parsed JSON, validated at compile time so one bad file cannot break boot.
|
|
68
|
+
*/
|
|
69
|
+
readonly stories: StoryFileEntry[];
|
|
47
70
|
}
|
|
48
71
|
|
|
49
72
|
/**
|
|
@@ -71,15 +94,22 @@ export interface PackageLoader {
|
|
|
71
94
|
* returns a result wins — subsequent loaders are skipped for that ref.
|
|
72
95
|
* Refs that no loader can resolve are silently dropped.
|
|
73
96
|
*
|
|
97
|
+
* A package instance that resolves for more than one ref (the bundled
|
|
98
|
+
* loader returns one cached aggregate for every ref it serves) is
|
|
99
|
+
* included only once: loading the same graphs repeatedly re-mints their
|
|
100
|
+
* blank nodes on each parse, duplicating blank-node subgraphs (e.g.
|
|
101
|
+
* standard do/don't examples) in the store.
|
|
102
|
+
*
|
|
74
103
|
* @param refs - Parsed package references from config merge.
|
|
75
104
|
* @param loaders - Loaders in precedence order (local > git > bundled).
|
|
76
|
-
* @returns Array of resolved semantic packages.
|
|
105
|
+
* @returns Array of resolved semantic packages, deduplicated by identity.
|
|
77
106
|
*/
|
|
78
107
|
export async function resolveSemanticPackages(
|
|
79
108
|
refs: ReadonlyArray<PackageRef>,
|
|
80
109
|
loaders: ReadonlyArray<PackageLoader>,
|
|
81
110
|
): Promise<SemanticPackage[]> {
|
|
82
111
|
const packages: SemanticPackage[] = [];
|
|
112
|
+
const seen = new Set<SemanticPackage>();
|
|
83
113
|
|
|
84
114
|
for (const ref of refs) {
|
|
85
115
|
let resolved: SemanticPackage | undefined;
|
|
@@ -87,7 +117,8 @@ export async function resolveSemanticPackages(
|
|
|
87
117
|
resolved = await loader.resolve(ref);
|
|
88
118
|
if (resolved) break;
|
|
89
119
|
}
|
|
90
|
-
if (resolved) {
|
|
120
|
+
if (resolved && !seen.has(resolved)) {
|
|
121
|
+
seen.add(resolved);
|
|
91
122
|
packages.push(resolved);
|
|
92
123
|
}
|
|
93
124
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { PragmaError } from "#error";
|
|
3
|
+
import type { PragmaContext } from "../context.js";
|
|
4
|
+
import type { LookupResult } from "../contracts.js";
|
|
5
|
+
import compileLookupCommand from "./compileLookupCommand.js";
|
|
6
|
+
import type { LookupStory } from "./types.js";
|
|
7
|
+
|
|
8
|
+
interface FakeEntity {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly detail: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function makeStory(
|
|
14
|
+
overrides: Partial<LookupStory<FakeEntity, string>> = {},
|
|
15
|
+
): LookupStory<FakeEntity, string> {
|
|
16
|
+
return {
|
|
17
|
+
noun: "thing",
|
|
18
|
+
description: "Look up things",
|
|
19
|
+
toolDescription: "Look up things over MCP.",
|
|
20
|
+
namesDescription: "Thing names",
|
|
21
|
+
detailedParam: { description: "Show details" },
|
|
22
|
+
examples: ["pragma thing lookup a"],
|
|
23
|
+
resolve: async (_rt, names) => {
|
|
24
|
+
const results = names
|
|
25
|
+
.filter((name) => name !== "missing")
|
|
26
|
+
.map((name) => ({ name, detail: `detail-${name}` }));
|
|
27
|
+
const errors: LookupResult<FakeEntity>["errors"] = names
|
|
28
|
+
.filter((name) => name === "missing")
|
|
29
|
+
.map((name) => ({
|
|
30
|
+
query: name,
|
|
31
|
+
code: "ENTITY_NOT_FOUND",
|
|
32
|
+
message: `thing "${name}" not found.`,
|
|
33
|
+
}));
|
|
34
|
+
return { results, errors };
|
|
35
|
+
},
|
|
36
|
+
toFmtInput: (entity, view) =>
|
|
37
|
+
view.detailed ? `${entity.name}:${entity.detail}` : entity.name,
|
|
38
|
+
formatters: {
|
|
39
|
+
plain: (text) => `plain:${text}`,
|
|
40
|
+
llm: (text) => `llm:${text}`,
|
|
41
|
+
json: (text) => JSON.stringify(text),
|
|
42
|
+
},
|
|
43
|
+
...overrides,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
|
|
48
|
+
return {
|
|
49
|
+
cwd: "/tmp",
|
|
50
|
+
globalFlags: { llm: false, format: "text" as const, verbose: false },
|
|
51
|
+
...overrides,
|
|
52
|
+
} as PragmaContext;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function executeText(
|
|
56
|
+
story: LookupStory<FakeEntity, string>,
|
|
57
|
+
params: Record<string, unknown>,
|
|
58
|
+
ctx: PragmaContext,
|
|
59
|
+
): Promise<string> {
|
|
60
|
+
const cmd = compileLookupCommand(ctx, story);
|
|
61
|
+
const result = await cmd.execute(params, ctx);
|
|
62
|
+
if (result.tag !== "output") throw new Error("expected output result");
|
|
63
|
+
return result.render.plain(result.value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("compileLookupCommand", () => {
|
|
67
|
+
it("projects names, detailed, and extra parameters", () => {
|
|
68
|
+
const story = makeStory({
|
|
69
|
+
params: [
|
|
70
|
+
{ name: "extra", type: "boolean", description: "extra flag" },
|
|
71
|
+
{ name: "mcpOnly", type: "boolean", description: "x", surfaces: "mcp" },
|
|
72
|
+
],
|
|
73
|
+
parameterGroups: { Extras: ["extra"] },
|
|
74
|
+
});
|
|
75
|
+
const cmd = compileLookupCommand(makeCtx(), story);
|
|
76
|
+
expect(cmd.path).toEqual(["thing", "lookup"]);
|
|
77
|
+
expect(cmd.parameters.map((p) => p.name)).toEqual([
|
|
78
|
+
"names",
|
|
79
|
+
"detailed",
|
|
80
|
+
"extra",
|
|
81
|
+
]);
|
|
82
|
+
const names = cmd.parameters.at(0);
|
|
83
|
+
expect(names?.type).toBe("multiselect");
|
|
84
|
+
expect(names?.positional).toBe(true);
|
|
85
|
+
expect(names?.required).toBe(true);
|
|
86
|
+
expect(cmd.parameterGroups).toEqual({ Extras: ["extra"] });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("omits the detailed parameter when the story has none", () => {
|
|
90
|
+
const cmd = compileLookupCommand(
|
|
91
|
+
makeCtx(),
|
|
92
|
+
makeStory({ detailedParam: undefined }),
|
|
93
|
+
);
|
|
94
|
+
expect(cmd.parameters.map((p) => p.name)).toEqual(["names"]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("renders each result and appends an errors section", async () => {
|
|
98
|
+
const text = await executeText(
|
|
99
|
+
makeStory(),
|
|
100
|
+
{ names: ["a", "missing"] },
|
|
101
|
+
makeCtx(),
|
|
102
|
+
);
|
|
103
|
+
expect(text).toContain("plain:a");
|
|
104
|
+
expect(text).toContain("Errors:");
|
|
105
|
+
expect(text).toContain('thing "missing" not found.');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("is summary by default and detailed with the flag", async () => {
|
|
109
|
+
const summary = await executeText(makeStory(), { names: ["a"] }, makeCtx());
|
|
110
|
+
expect(summary).toBe("plain:a");
|
|
111
|
+
|
|
112
|
+
const detailed = await executeText(
|
|
113
|
+
makeStory(),
|
|
114
|
+
{ names: ["a"], detailed: true },
|
|
115
|
+
makeCtx(),
|
|
116
|
+
);
|
|
117
|
+
expect(detailed).toBe("plain:a:detail-a");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("accepts the legacy single name parameter", async () => {
|
|
121
|
+
const text = await executeText(makeStory(), { name: "a" }, makeCtx());
|
|
122
|
+
expect(text).toBe("plain:a");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("throws the story's empty-names error when no names are given", async () => {
|
|
126
|
+
const story = makeStory({
|
|
127
|
+
emptyNamesError: () => PragmaError.invalidInput("names", "(empty)"),
|
|
128
|
+
});
|
|
129
|
+
const cmd = compileLookupCommand(makeCtx(), story);
|
|
130
|
+
await expect(cmd.execute({ names: [] }, makeCtx())).rejects.toThrow(
|
|
131
|
+
PragmaError,
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("exposes an ink renderer only when the story defines one", async () => {
|
|
136
|
+
const story = makeStory({
|
|
137
|
+
renderInk: (result, view) =>
|
|
138
|
+
`ink:${result.results.length}:${view.detailed}`,
|
|
139
|
+
});
|
|
140
|
+
const cmd = compileLookupCommand(makeCtx(), story);
|
|
141
|
+
const result = await cmd.execute({ names: ["a"] }, makeCtx());
|
|
142
|
+
if (result.tag !== "output") throw new Error("expected output result");
|
|
143
|
+
expect(result.render.ink?.(result.value)).toBe("ink:1:false");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CommandDefinition,
|
|
3
|
+
type CommandResult,
|
|
4
|
+
createOutputResult,
|
|
5
|
+
type ParameterDefinition,
|
|
6
|
+
} from "@canonical/cli-core";
|
|
7
|
+
import type { PragmaContext } from "../context.js";
|
|
8
|
+
import type { LookupResult } from "../contracts.js";
|
|
9
|
+
import { renderLookupResults } from "../formatters.js";
|
|
10
|
+
import resolveLookupExitCode from "../resolveLookupExitCode.js";
|
|
11
|
+
import normalizeNames from "./normalizeNames.js";
|
|
12
|
+
import projectCliParameters from "./projectCliParameters.js";
|
|
13
|
+
import resolveLookupDetailed from "./resolveLookupDetailed.js";
|
|
14
|
+
import type { LookupStory, LookupStoryView } from "./types.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Compile a lookup story into its CLI `CommandDefinition` projection.
|
|
18
|
+
*
|
|
19
|
+
* Owns the shared execute skeleton: normalize the positional `names`,
|
|
20
|
+
* reject empty input when the story defines an error, resolve the batch,
|
|
21
|
+
* then render each result through the story's formatters with the shared
|
|
22
|
+
* multi-result renderer (which appends the "Errors:" section).
|
|
23
|
+
*/
|
|
24
|
+
export default function compileLookupCommand<TDetailed, TFmtInput>(
|
|
25
|
+
ctx: PragmaContext,
|
|
26
|
+
story: LookupStory<TDetailed, TFmtInput>,
|
|
27
|
+
): CommandDefinition {
|
|
28
|
+
interface LookupOutput {
|
|
29
|
+
readonly result: LookupResult<TDetailed>;
|
|
30
|
+
readonly view: LookupStoryView;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
path: [story.noun, "lookup"],
|
|
35
|
+
description: story.description,
|
|
36
|
+
parameters: buildParameters(story),
|
|
37
|
+
...(story.parameterGroups && { parameterGroups: story.parameterGroups }),
|
|
38
|
+
meta: {
|
|
39
|
+
examples: story.examples,
|
|
40
|
+
},
|
|
41
|
+
execute: async (
|
|
42
|
+
params: Record<string, unknown>,
|
|
43
|
+
): Promise<CommandResult> => {
|
|
44
|
+
const names = normalizeNames(params.names, params.name);
|
|
45
|
+
if (names.length === 0 && story.emptyNamesError) {
|
|
46
|
+
throw story.emptyNamesError();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const view: LookupStoryView = {
|
|
50
|
+
surface: "cli",
|
|
51
|
+
detailed: resolveLookupDetailed(story, "cli", params),
|
|
52
|
+
params,
|
|
53
|
+
};
|
|
54
|
+
const result = await story.resolve(ctx, names, params, view);
|
|
55
|
+
const renderInk = story.renderInk;
|
|
56
|
+
|
|
57
|
+
return createOutputResult<LookupOutput>(
|
|
58
|
+
{ result, view },
|
|
59
|
+
{
|
|
60
|
+
plain: (output) =>
|
|
61
|
+
renderLookupResults({
|
|
62
|
+
ctx,
|
|
63
|
+
result: output.result,
|
|
64
|
+
formatters: story.formatters,
|
|
65
|
+
mapResult: (entity) => story.toFmtInput(entity, output.view),
|
|
66
|
+
}),
|
|
67
|
+
...(renderInk && {
|
|
68
|
+
ink: (output: LookupOutput) =>
|
|
69
|
+
renderInk(output.result, output.view),
|
|
70
|
+
}),
|
|
71
|
+
},
|
|
72
|
+
resolveLookupExitCode(result),
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildParameters<TDetailed, TFmtInput>(
|
|
79
|
+
story: LookupStory<TDetailed, TFmtInput>,
|
|
80
|
+
): ParameterDefinition[] {
|
|
81
|
+
const names: ParameterDefinition = {
|
|
82
|
+
name: "names",
|
|
83
|
+
description: story.namesDescription,
|
|
84
|
+
type: "multiselect",
|
|
85
|
+
positional: true,
|
|
86
|
+
required: true,
|
|
87
|
+
...(story.complete && { complete: story.complete }),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const detailed: ParameterDefinition[] = story.detailedParam
|
|
91
|
+
? [
|
|
92
|
+
{
|
|
93
|
+
name: "detailed",
|
|
94
|
+
description: story.detailedParam.description,
|
|
95
|
+
type: "boolean",
|
|
96
|
+
default: false,
|
|
97
|
+
},
|
|
98
|
+
]
|
|
99
|
+
: [];
|
|
100
|
+
|
|
101
|
+
return [names, ...detailed, ...projectCliParameters(story.params ?? [])];
|
|
102
|
+
}
|