@canonical/pragma-cli 0.18.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 +377 -0
- package/package.json +78 -0
- package/src/bin.test.ts +52 -0
- package/src/bin.ts +19 -0
- package/src/compile-validation.runner.ts +137 -0
- package/src/compile-validation.ts +192 -0
- package/src/completions/computeSocketPath.test.ts +26 -0
- package/src/completions/computeSocketPath.ts +16 -0
- package/src/completions/constants.ts +18 -0
- package/src/completions/handleQuery.test.ts +93 -0
- package/src/completions/handleQuery.ts +30 -0
- package/src/completions/index.ts +14 -0
- package/src/completions/queryCompletions.test.ts +100 -0
- package/src/completions/queryCompletions.ts +61 -0
- package/src/completions/querySocket.test.ts +70 -0
- package/src/completions/querySocket.ts +44 -0
- package/src/completions/startCompletionsServer.test.ts +74 -0
- package/src/completions/startCompletionsServer.ts +75 -0
- package/src/completions/startCompletionsServer.unit.test.ts +131 -0
- package/src/completions/waitForSocket.test.ts +27 -0
- package/src/completions/waitForSocket.ts +27 -0
- package/src/config/configExists.ts +14 -0
- package/src/config/index.ts +6 -0
- package/src/config/readConfig.test.ts +152 -0
- package/src/config/readConfig.ts +64 -0
- package/src/config/resolveConfigPath.test.ts +20 -0
- package/src/config/resolveConfigPath.ts +11 -0
- package/src/config/types.ts +26 -0
- package/src/config/writeConfig.ts +49 -0
- package/src/constants.ts +78 -0
- package/src/domains/block/blockConfig.ts +53 -0
- package/src/domains/block/commands/index.ts +4 -0
- package/src/domains/block/commands/list.test.ts +111 -0
- package/src/domains/block/commands/list.ts +69 -0
- package/src/domains/block/commands/lookup.test.ts +187 -0
- package/src/domains/block/commands/lookup.ts +199 -0
- package/src/domains/block/formatters/index.ts +4 -0
- package/src/domains/block/formatters/list.test.ts +107 -0
- package/src/domains/block/formatters/list.ts +64 -0
- package/src/domains/block/formatters/lookup.test.ts +317 -0
- package/src/domains/block/formatters/lookup.ts +354 -0
- package/src/domains/block/formatters/types.ts +16 -0
- package/src/domains/block/helpers/index.ts +4 -0
- package/src/domains/block/helpers/resolveAspects.test.ts +64 -0
- package/src/domains/block/helpers/resolveAspects.ts +35 -0
- package/src/domains/block/index.ts +25 -0
- package/src/domains/block/mcp/index.ts +2 -0
- package/src/domains/block/mcp/specs.ts +199 -0
- package/src/domains/block/operations/index.ts +4 -0
- package/src/domains/block/operations/list.test.ts +80 -0
- package/src/domains/block/operations/list.ts +72 -0
- package/src/domains/block/operations/lookup.test.ts +108 -0
- package/src/domains/block/operations/lookup.ts +359 -0
- package/src/domains/block/orchestration/blockEmptyError.ts +19 -0
- package/src/domains/block/orchestration/buildBlockFilters.ts +12 -0
- package/src/domains/block/orchestration/enrichBlocks.ts +37 -0
- package/src/domains/block/orchestration/index.ts +5 -0
- package/src/domains/block/orchestration/resolveBlockList.ts +39 -0
- package/src/domains/block/orchestration/resolveBlockLookup.ts +21 -0
- package/src/domains/block/types.ts +30 -0
- package/src/domains/config/commands/channel.test.ts +101 -0
- package/src/domains/config/commands/channel.ts +80 -0
- package/src/domains/config/commands/index.ts +4 -0
- package/src/domains/config/commands/show.test.ts +61 -0
- package/src/domains/config/commands/show.ts +51 -0
- package/src/domains/config/commands/tier.test.ts +84 -0
- package/src/domains/config/commands/tier.ts +78 -0
- package/src/domains/config/formatters/channel.test.ts +56 -0
- package/src/domains/config/formatters/channel.ts +34 -0
- package/src/domains/config/formatters/index.ts +4 -0
- package/src/domains/config/formatters/show.test.ts +126 -0
- package/src/domains/config/formatters/show.ts +66 -0
- package/src/domains/config/formatters/tier.test.ts +70 -0
- package/src/domains/config/formatters/tier.ts +40 -0
- package/src/domains/config/index.ts +23 -0
- package/src/domains/config/mcp/index.ts +2 -0
- package/src/domains/config/mcp/specs.ts +108 -0
- package/src/domains/config/operations/index.ts +5 -0
- package/src/domains/config/operations/show.test.ts +60 -0
- package/src/domains/config/operations/show.ts +42 -0
- package/src/domains/config/operations/types.ts +13 -0
- package/src/domains/config/operations/validateChannel.test.ts +36 -0
- package/src/domains/config/operations/validateChannel.ts +24 -0
- package/src/domains/config/operations/validateTier.test.ts +56 -0
- package/src/domains/config/operations/validateTier.ts +35 -0
- package/src/domains/create/commands/component.test.ts +166 -0
- package/src/domains/create/commands/component.ts +129 -0
- package/src/domains/create/commands/index.ts +3 -0
- package/src/domains/create/commands/package.test.ts +35 -0
- package/src/domains/create/commands/package.ts +21 -0
- package/src/domains/create/index.ts +15 -0
- package/src/domains/create/mcp/index.ts +2 -0
- package/src/domains/create/mcp/specs.ts +174 -0
- package/src/domains/doctor/commands/doctor.test.ts +75 -0
- package/src/domains/doctor/commands/doctor.ts +43 -0
- package/src/domains/doctor/commands/index.ts +2 -0
- package/src/domains/doctor/formatters/doctor.test.ts +117 -0
- package/src/domains/doctor/formatters/doctor.ts +84 -0
- package/src/domains/doctor/formatters/index.ts +2 -0
- package/src/domains/doctor/index.ts +9 -0
- package/src/domains/doctor/mcp/index.ts +2 -0
- package/src/domains/doctor/mcp/specs.ts +88 -0
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +28 -0
- package/src/domains/doctor/operations/checks/checkKeStore.ts +38 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +65 -0
- package/src/domains/doctor/operations/checks/checkNodeVersion.ts +25 -0
- package/src/domains/doctor/operations/checks/checkPragmaVersion.ts +20 -0
- package/src/domains/doctor/operations/checks/checkShellCompletions.ts +42 -0
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +64 -0
- package/src/domains/doctor/operations/checks/index.ts +8 -0
- package/src/domains/doctor/operations/checks.test.ts +151 -0
- package/src/domains/doctor/operations/index.ts +3 -0
- package/src/domains/doctor/operations/runChecks.test.ts +89 -0
- package/src/domains/doctor/operations/runChecks.ts +38 -0
- package/src/domains/doctor/operations/types.ts +20 -0
- package/src/domains/graph/commands/index.ts +4 -0
- package/src/domains/graph/commands/inspect.test.ts +94 -0
- package/src/domains/graph/commands/inspect.ts +63 -0
- package/src/domains/graph/commands/query.test.ts +92 -0
- package/src/domains/graph/commands/query.ts +60 -0
- package/src/domains/graph/formatters/index.ts +4 -0
- package/src/domains/graph/formatters/inspect.ts +45 -0
- package/src/domains/graph/formatters/query.ts +37 -0
- package/src/domains/graph/helpers/index.ts +3 -0
- package/src/domains/graph/helpers/resolveUri.ts +70 -0
- package/src/domains/graph/index.ts +22 -0
- package/src/domains/graph/mcp/index.ts +2 -0
- package/src/domains/graph/mcp/specs.ts +82 -0
- package/src/domains/graph/operations/executeQuery.test.ts +68 -0
- package/src/domains/graph/operations/executeQuery.ts +32 -0
- package/src/domains/graph/operations/index.ts +4 -0
- package/src/domains/graph/operations/inspectUri.test.ts +101 -0
- package/src/domains/graph/operations/inspectUri.ts +61 -0
- package/src/domains/info/commands/index.ts +4 -0
- package/src/domains/info/commands/info.test.ts +88 -0
- package/src/domains/info/commands/info.ts +47 -0
- package/src/domains/info/commands/upgrade.test.ts +183 -0
- package/src/domains/info/commands/upgrade.ts +150 -0
- package/src/domains/info/constants.test.ts +22 -0
- package/src/domains/info/constants.ts +15 -0
- package/src/domains/info/formatters/index.ts +8 -0
- package/src/domains/info/formatters/info.test.ts +173 -0
- package/src/domains/info/formatters/info.ts +116 -0
- package/src/domains/info/formatters/upgrade.test.ts +133 -0
- package/src/domains/info/formatters/upgrade.ts +68 -0
- package/src/domains/info/index.ts +7 -0
- package/src/domains/info/operations/checkRegistryVersion.test.ts +124 -0
- package/src/domains/info/operations/checkRegistryVersion.ts +45 -0
- package/src/domains/info/operations/collectInfo.test.ts +111 -0
- package/src/domains/info/operations/collectInfo.ts +68 -0
- package/src/domains/info/operations/collectStoreSummary.test.ts +61 -0
- package/src/domains/info/operations/collectStoreSummary.ts +42 -0
- package/src/domains/info/operations/index.ts +5 -0
- package/src/domains/info/types.test-d.ts +35 -0
- package/src/domains/info/types.ts +46 -0
- package/src/domains/llm/commands/index.ts +3 -0
- package/src/domains/llm/commands/llm.ts +37 -0
- package/src/domains/llm/data/commandReference.ts +25 -0
- package/src/domains/llm/data/decisionTrees.ts +46 -0
- package/src/domains/llm/data/index.ts +4 -0
- package/src/domains/llm/formatters/index.ts +3 -0
- package/src/domains/llm/formatters/orientation.ts +53 -0
- package/src/domains/llm/index.ts +8 -0
- package/src/domains/llm/llmCommand.test.ts +93 -0
- package/src/domains/llm/mcp/index.ts +2 -0
- package/src/domains/llm/mcp/specs.ts +87 -0
- package/src/domains/llm/operations/collectContext.ts +47 -0
- package/src/domains/llm/operations/index.ts +3 -0
- package/src/domains/llm/types.ts +32 -0
- package/src/domains/modifier/commands/index.ts +4 -0
- package/src/domains/modifier/commands/list.test.ts +57 -0
- package/src/domains/modifier/commands/list.ts +33 -0
- package/src/domains/modifier/commands/lookup.test.ts +91 -0
- package/src/domains/modifier/commands/lookup.ts +105 -0
- package/src/domains/modifier/formatters/index.ts +4 -0
- package/src/domains/modifier/formatters/list.test.ts +38 -0
- package/src/domains/modifier/formatters/list.ts +35 -0
- package/src/domains/modifier/formatters/lookup.test.ts +33 -0
- package/src/domains/modifier/formatters/lookup.ts +38 -0
- package/src/domains/modifier/index.ts +22 -0
- package/src/domains/modifier/mcp/index.ts +2 -0
- package/src/domains/modifier/mcp/specs.ts +97 -0
- package/src/domains/modifier/operations/index.ts +4 -0
- package/src/domains/modifier/operations/list.test.ts +39 -0
- package/src/domains/modifier/operations/list.ts +41 -0
- package/src/domains/modifier/operations/lookup.test.ts +37 -0
- package/src/domains/modifier/operations/lookup.ts +57 -0
- package/src/domains/modifier/orchestration/index.ts +3 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.ts +10 -0
- package/src/domains/modifier/orchestration/resolveModifierList.ts +16 -0
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +20 -0
- package/src/domains/modifier/types.ts +5 -0
- package/src/domains/ontology/commands/index.ts +4 -0
- package/src/domains/ontology/commands/list.test.ts +82 -0
- package/src/domains/ontology/commands/list.ts +46 -0
- package/src/domains/ontology/commands/show.test.ts +103 -0
- package/src/domains/ontology/commands/show.ts +66 -0
- package/src/domains/ontology/formatters/index.ts +4 -0
- package/src/domains/ontology/formatters/list.ts +37 -0
- package/src/domains/ontology/formatters/show.ts +74 -0
- package/src/domains/ontology/helpers/findNamespace.ts +23 -0
- package/src/domains/ontology/helpers/index.ts +7 -0
- package/src/domains/ontology/helpers/queryClasses.ts +50 -0
- package/src/domains/ontology/helpers/queryProperties.ts +57 -0
- package/src/domains/ontology/helpers/resolvePrefix.ts +45 -0
- package/src/domains/ontology/index.ts +26 -0
- package/src/domains/ontology/mcp/index.ts +2 -0
- package/src/domains/ontology/mcp/specs.ts +93 -0
- package/src/domains/ontology/operations/index.ts +5 -0
- package/src/domains/ontology/operations/listOntologies.test.ts +45 -0
- package/src/domains/ontology/operations/listOntologies.ts +101 -0
- package/src/domains/ontology/operations/showOntology.test.ts +65 -0
- package/src/domains/ontology/operations/showOntology.ts +41 -0
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +39 -0
- package/src/domains/ontology/operations/showOntologyRaw.ts +50 -0
- package/src/domains/setup/commands/all.ts +58 -0
- package/src/domains/setup/commands/completions.ts +85 -0
- package/src/domains/setup/commands/entrypoints.test.ts +170 -0
- package/src/domains/setup/commands/index.ts +6 -0
- package/src/domains/setup/commands/lsp.ts +49 -0
- package/src/domains/setup/commands/mcp.ts +119 -0
- package/src/domains/setup/commands/skills.test.ts +64 -0
- package/src/domains/setup/commands/skills.ts +106 -0
- package/src/domains/setup/formatters/index.ts +2 -0
- package/src/domains/setup/formatters/skills.test.ts +103 -0
- package/src/domains/setup/formatters/skills.ts +112 -0
- package/src/domains/setup/formatters/types.ts +7 -0
- package/src/domains/setup/helpers/completionScripts.test.ts +58 -0
- package/src/domains/setup/helpers/completionScripts.ts +92 -0
- package/src/domains/setup/helpers/constants.ts +2 -0
- package/src/domains/setup/helpers/detectShell.test.ts +48 -0
- package/src/domains/setup/helpers/detectShell.ts +20 -0
- package/src/domains/setup/helpers/index.ts +12 -0
- package/src/domains/setup/helpers/runSetupTask.test.ts +279 -0
- package/src/domains/setup/helpers/runSetupTask.ts +229 -0
- package/src/domains/setup/index.ts +33 -0
- package/src/domains/setup/operations/index.ts +6 -0
- package/src/domains/setup/operations/setupAll.test.ts +48 -0
- package/src/domains/setup/operations/setupAll.ts +41 -0
- package/src/domains/setup/operations/setupCompletions.test.ts +94 -0
- package/src/domains/setup/operations/setupCompletions.ts +54 -0
- package/src/domains/setup/operations/setupLsp.test.ts +29 -0
- package/src/domains/setup/operations/setupLsp.ts +17 -0
- package/src/domains/setup/operations/setupMcp.test.ts +115 -0
- package/src/domains/setup/operations/setupMcp.ts +108 -0
- package/src/domains/setup/operations/setupSkills.test.ts +191 -0
- package/src/domains/setup/operations/setupSkills.ts +160 -0
- package/src/domains/setup/types.ts +18 -0
- package/src/domains/shared/ToolSpec.ts +49 -0
- package/src/domains/shared/bootStore.ts +77 -0
- package/src/domains/shared/buildQuery.ts +18 -0
- package/src/domains/shared/compactUri.test.ts +21 -0
- package/src/domains/shared/compactUri.ts +16 -0
- package/src/domains/shared/context.ts +15 -0
- package/src/domains/shared/contracts.ts +113 -0
- package/src/domains/shared/extractLocalName.test.ts +21 -0
- package/src/domains/shared/extractLocalName.ts +14 -0
- package/src/domains/shared/filters/buildChannelFilter.test.ts +52 -0
- package/src/domains/shared/filters/buildChannelFilter.ts +41 -0
- package/src/domains/shared/filters/buildFilterConfig.ts +16 -0
- package/src/domains/shared/filters/buildFilters.test.ts +33 -0
- package/src/domains/shared/filters/buildFilters.ts +30 -0
- package/src/domains/shared/filters/buildTierFilter.test.ts +78 -0
- package/src/domains/shared/filters/buildTierFilter.ts +69 -0
- package/src/domains/shared/filters/describeFilters.ts +14 -0
- package/src/domains/shared/filters/index.ts +10 -0
- package/src/domains/shared/formatters.ts +35 -0
- package/src/domains/shared/lookupMany.ts +45 -0
- package/src/domains/shared/namespaces.ts +18 -0
- package/src/domains/shared/packages.ts +76 -0
- package/src/domains/shared/prefixes.ts +37 -0
- package/src/domains/shared/runtime.ts +62 -0
- package/src/domains/shared/types.test-d.ts +127 -0
- package/src/domains/shared/types.ts +348 -0
- package/src/domains/skill/commands/index.ts +2 -0
- package/src/domains/skill/commands/list.test.ts +111 -0
- package/src/domains/skill/commands/list.ts +69 -0
- package/src/domains/skill/formatters/index.ts +2 -0
- package/src/domains/skill/formatters/list.test.ts +140 -0
- package/src/domains/skill/formatters/list.ts +119 -0
- package/src/domains/skill/formatters/types.ts +8 -0
- package/src/domains/skill/helpers/index.ts +6 -0
- package/src/domains/skill/helpers/parseFrontmatter.test.ts +122 -0
- package/src/domains/skill/helpers/parseFrontmatter.ts +112 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +34 -0
- package/src/domains/skill/index.ts +19 -0
- package/src/domains/skill/mcp/index.ts +2 -0
- package/src/domains/skill/mcp/specs.ts +38 -0
- package/src/domains/skill/operations/discover.test.ts +145 -0
- package/src/domains/skill/operations/discover.ts +68 -0
- package/src/domains/skill/operations/index.ts +4 -0
- package/src/domains/skill/operations/list.test.ts +68 -0
- package/src/domains/skill/operations/list.ts +47 -0
- package/src/domains/skill/types.ts +25 -0
- package/src/domains/standard/commands/categories.test.ts +70 -0
- package/src/domains/standard/commands/categories.ts +48 -0
- package/src/domains/standard/commands/index.ts +5 -0
- package/src/domains/standard/commands/list.test.ts +105 -0
- package/src/domains/standard/commands/list.ts +81 -0
- package/src/domains/standard/commands/lookup.test.ts +135 -0
- package/src/domains/standard/commands/lookup.ts +127 -0
- package/src/domains/standard/formatters/categories.test.ts +44 -0
- package/src/domains/standard/formatters/categories.ts +38 -0
- package/src/domains/standard/formatters/index.ts +6 -0
- package/src/domains/standard/formatters/list.test.ts +166 -0
- package/src/domains/standard/formatters/list.ts +156 -0
- package/src/domains/standard/formatters/lookup.test.ts +95 -0
- package/src/domains/standard/formatters/lookup.ts +86 -0
- package/src/domains/standard/formatters/types.ts +31 -0
- package/src/domains/standard/index.ts +32 -0
- package/src/domains/standard/mcp/index.ts +2 -0
- package/src/domains/standard/mcp/specs.ts +197 -0
- package/src/domains/standard/operations/categories.test.ts +61 -0
- package/src/domains/standard/operations/categories.ts +38 -0
- package/src/domains/standard/operations/index.ts +5 -0
- package/src/domains/standard/operations/list.test.ts +75 -0
- package/src/domains/standard/operations/list.ts +64 -0
- package/src/domains/standard/operations/lookup.test.ts +58 -0
- package/src/domains/standard/operations/lookup.ts +142 -0
- package/src/domains/standard/orchestration/buildStandardFilters.ts +11 -0
- package/src/domains/standard/orchestration/index.ts +4 -0
- package/src/domains/standard/orchestration/resolveStandardList.ts +42 -0
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +20 -0
- package/src/domains/standard/orchestration/standardEmptyError.ts +19 -0
- package/src/domains/standard/types.ts +15 -0
- package/src/domains/tier/commands/index.ts +3 -0
- package/src/domains/tier/commands/list.test.ts +67 -0
- package/src/domains/tier/commands/list.ts +43 -0
- package/src/domains/tier/formatters/index.ts +3 -0
- package/src/domains/tier/formatters/list.test.ts +33 -0
- package/src/domains/tier/formatters/list.ts +40 -0
- package/src/domains/tier/index.ts +17 -0
- package/src/domains/tier/mcp/index.ts +2 -0
- package/src/domains/tier/mcp/specs.ts +38 -0
- package/src/domains/tier/operations/index.ts +3 -0
- package/src/domains/tier/operations/list.test.ts +32 -0
- package/src/domains/tier/operations/list.ts +33 -0
- package/src/domains/token/commands/add-config.test.ts +68 -0
- package/src/domains/token/commands/add-config.ts +62 -0
- package/src/domains/token/commands/index.ts +5 -0
- package/src/domains/token/commands/list.test.ts +82 -0
- package/src/domains/token/commands/list.ts +45 -0
- package/src/domains/token/commands/lookup.test.ts +125 -0
- package/src/domains/token/commands/lookup.ts +121 -0
- package/src/domains/token/formatters/index.ts +4 -0
- package/src/domains/token/formatters/list.test.ts +37 -0
- package/src/domains/token/formatters/list.ts +37 -0
- package/src/domains/token/formatters/lookup.test.ts +61 -0
- package/src/domains/token/formatters/lookup.ts +72 -0
- package/src/domains/token/index.ts +32 -0
- package/src/domains/token/mcp/index.ts +2 -0
- package/src/domains/token/mcp/specs.ts +145 -0
- package/src/domains/token/operations/add-config.test.ts +68 -0
- package/src/domains/token/operations/add-config.ts +61 -0
- package/src/domains/token/operations/index.ts +7 -0
- package/src/domains/token/operations/list.test.ts +50 -0
- package/src/domains/token/operations/list.ts +52 -0
- package/src/domains/token/operations/lookup.test.ts +42 -0
- package/src/domains/token/operations/lookup.ts +66 -0
- package/src/domains/token/orchestration/index.ts +3 -0
- package/src/domains/token/orchestration/resolveTokenList.ts +17 -0
- package/src/domains/token/orchestration/resolveTokenLookup.ts +20 -0
- package/src/domains/token/orchestration/tokenEmptyError.ts +17 -0
- package/src/domains/token/types.ts +6 -0
- package/src/error/PragmaError.test.ts +121 -0
- package/src/error/PragmaError.ts +164 -0
- package/src/error/constants.ts +12 -0
- package/src/error/index.ts +4 -0
- package/src/error/types.ts +41 -0
- package/src/index.ts +141 -0
- package/src/mcp/createMcpServer.test.ts +41 -0
- package/src/mcp/createMcpServer.ts +53 -0
- package/src/mcp/error/buildRecovery.test.ts +37 -0
- package/src/mcp/error/buildRecovery.ts +17 -0
- package/src/mcp/error/index.ts +12 -0
- package/src/mcp/error/serializeError.test.ts +121 -0
- package/src/mcp/error/serializeError.ts +31 -0
- package/src/mcp/error/serializeErrorPayload.test.ts +38 -0
- package/src/mcp/error/serializeErrorPayload.ts +27 -0
- package/src/mcp/error/types.ts +51 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/registerResources.test.ts +236 -0
- package/src/mcp/registerResources.ts +303 -0
- package/src/mcp/runMcpServer.test.ts +66 -0
- package/src/mcp/runMcpServer.ts +24 -0
- package/src/mcp/tools/index.ts +55 -0
- package/src/mcp/tools/registerFromSpec.ts +97 -0
- package/src/mcp/tools/registerTools.test.ts +1124 -0
- package/src/mcp/types.ts +62 -0
- package/src/mcp/utils/estimateTokens.test.ts +14 -0
- package/src/mcp/utils/estimateTokens.ts +9 -0
- package/src/mcp/utils/index.ts +11 -0
- package/src/mcp/utils/wrapTool.test.ts +108 -0
- package/src/mcp/utils/wrapTool.ts +81 -0
- package/src/package-manager/constants.ts +29 -0
- package/src/package-manager/detectInstallSource.test.ts +23 -0
- package/src/package-manager/detectInstallSource.ts +111 -0
- package/src/package-manager/detectLocalInstall.test.ts +16 -0
- package/src/package-manager/detectLocalInstall.ts +43 -0
- package/src/package-manager/detectPackageManager.test.ts +81 -0
- package/src/package-manager/detectPackageManager.ts +30 -0
- package/src/package-manager/index.ts +6 -0
- package/src/package-manager/types.ts +12 -0
- package/src/pipeline/collectCommands.test.ts +37 -0
- package/src/pipeline/collectCommands.ts +47 -0
- package/src/pipeline/constants.ts +14 -0
- package/src/pipeline/createProgram.test.ts +112 -0
- package/src/pipeline/createProgram.ts +70 -0
- package/src/pipeline/formatTerminal.test.ts +43 -0
- package/src/pipeline/formatTerminal.ts +53 -0
- package/src/pipeline/index.ts +19 -0
- package/src/pipeline/mapExitCode.test.ts +41 -0
- package/src/pipeline/mapExitCode.ts +12 -0
- package/src/pipeline/parseGlobalFlags.ts +21 -0
- package/src/pipeline/renderError.test.ts +151 -0
- package/src/pipeline/renderError.ts +114 -0
- package/src/pipeline/resolveCommandKind.test.ts +68 -0
- package/src/pipeline/resolveCommandKind.ts +71 -0
- package/src/pipeline/runCli.orchestration.test.ts +418 -0
- package/src/pipeline/runCli.test.ts +70 -0
- package/src/pipeline/runCli.ts +215 -0
- package/src/pipeline/types.ts +17 -0
- package/src/testing/cli.ts +43 -0
- package/src/testing/createTestMcpClient.ts +64 -0
- package/src/testing/dsFixtures.ts +506 -0
- package/src/testing/fixtures/canonical-config.json +4 -0
- package/src/testing/fixtures/canonical.ttl +437 -0
- package/src/testing/fixtures/filtered-config.json +4 -0
- package/src/testing/fixtures.ts +34 -0
- package/src/testing/helpers/assertParity.ts +28 -0
- package/src/testing/helpers/createTestMcpClient.ts +40 -0
- package/src/testing/helpers/createTestRuntime.ts +67 -0
- package/src/testing/index.ts +8 -0
- package/src/testing/integration/agent-session.test.ts +148 -0
- package/src/testing/integration/boot.test.ts +67 -0
- package/src/testing/integration/mcp-surface.test.ts +574 -0
- package/src/testing/integration/parity.test.ts +192 -0
- package/src/testing/store.ts +39 -0
- package/src/testing/types.ts +20 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { ConfigShowData } from "../operations/types.js";
|
|
3
|
+
import formatters from "./show.js";
|
|
4
|
+
|
|
5
|
+
const WITH_TIER: ConfigShowData = {
|
|
6
|
+
tier: "apps/lxd",
|
|
7
|
+
tierChain: ["global", "apps", "apps/lxd"],
|
|
8
|
+
channel: "experimental",
|
|
9
|
+
includedReleases: ["stable", "experimental"],
|
|
10
|
+
packageManager: "bun",
|
|
11
|
+
installSource: "bun (global)",
|
|
12
|
+
configFilePath: "/project/pragma.config.json",
|
|
13
|
+
configFileExists: true,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const WITHOUT_TIER: ConfigShowData = {
|
|
17
|
+
tier: undefined,
|
|
18
|
+
tierChain: [],
|
|
19
|
+
channel: "normal",
|
|
20
|
+
includedReleases: ["stable"],
|
|
21
|
+
packageManager: "npm",
|
|
22
|
+
installSource: "bun (local)",
|
|
23
|
+
configFilePath: "/project/pragma.config.json",
|
|
24
|
+
configFileExists: false,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const PRERELEASE: ConfigShowData = {
|
|
28
|
+
tier: "global",
|
|
29
|
+
tierChain: ["global"],
|
|
30
|
+
channel: "prerelease",
|
|
31
|
+
includedReleases: ["stable", "experimental", "alpha", "beta"],
|
|
32
|
+
packageManager: "pnpm",
|
|
33
|
+
installSource: "pnpm (global)",
|
|
34
|
+
configFilePath: "/project/pragma.config.json",
|
|
35
|
+
configFileExists: true,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
describe("formatters.plain", () => {
|
|
39
|
+
it("renders full config with tier", () => {
|
|
40
|
+
const output = formatters.plain(WITH_TIER);
|
|
41
|
+
expect(output).toContain("tier: apps/lxd (global → apps → apps/lxd)");
|
|
42
|
+
expect(output).toContain("channel: experimental (stable + experimental)");
|
|
43
|
+
expect(output).toContain("installed via: bun (global)");
|
|
44
|
+
expect(output).toContain("config file: /project/pragma.config.json");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("renders config without tier", () => {
|
|
48
|
+
const output = formatters.plain(WITHOUT_TIER);
|
|
49
|
+
expect(output).toContain("tier: (none — all tiers visible)");
|
|
50
|
+
expect(output).toContain("channel: normal (stable)");
|
|
51
|
+
expect(output).toContain("installed via: bun (local)");
|
|
52
|
+
expect(output).toContain("config file: (not found)");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("renders prerelease channel with all release levels", () => {
|
|
56
|
+
const output = formatters.plain(PRERELEASE);
|
|
57
|
+
expect(output).toContain(
|
|
58
|
+
"channel: prerelease (stable + experimental + alpha + beta)",
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("formatters.llm", () => {
|
|
64
|
+
it("renders markdown heading", () => {
|
|
65
|
+
const output = formatters.llm(WITH_TIER);
|
|
66
|
+
expect(output).toContain("## Configuration");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("renders tier info as markdown", () => {
|
|
70
|
+
const output = formatters.llm(WITH_TIER);
|
|
71
|
+
expect(output).toContain("**Tier:** apps/lxd (global > apps > apps/lxd)");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("renders channel info as markdown", () => {
|
|
75
|
+
const output = formatters.llm(WITH_TIER);
|
|
76
|
+
expect(output).toContain(
|
|
77
|
+
"**Channel:** experimental (stable, experimental)",
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("renders config file path in backticks", () => {
|
|
82
|
+
const output = formatters.llm(WITH_TIER);
|
|
83
|
+
expect(output).toContain("`/project/pragma.config.json`");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("renders no tier message", () => {
|
|
87
|
+
const output = formatters.llm(WITHOUT_TIER);
|
|
88
|
+
expect(output).toContain("**Tier:** none (all tiers visible)");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("renders missing config file", () => {
|
|
92
|
+
const output = formatters.llm(WITHOUT_TIER);
|
|
93
|
+
expect(output).toContain("**Config file:** not found");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("formatters.json", () => {
|
|
98
|
+
it("returns valid JSON", () => {
|
|
99
|
+
const text = formatters.json(WITH_TIER);
|
|
100
|
+
const parsed = JSON.parse(text);
|
|
101
|
+
expect(parsed.tier).toBe("apps/lxd");
|
|
102
|
+
expect(parsed.channel).toBe("experimental");
|
|
103
|
+
expect(parsed.tierChain).toEqual(["global", "apps", "apps/lxd"]);
|
|
104
|
+
expect(parsed.includedReleases).toEqual(["stable", "experimental"]);
|
|
105
|
+
expect(parsed.packageManager).toBe("bun");
|
|
106
|
+
expect(parsed.configFileExists).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("returns JSON without tier when undefined", () => {
|
|
110
|
+
const text = formatters.json(WITHOUT_TIER);
|
|
111
|
+
const parsed = JSON.parse(text);
|
|
112
|
+
expect(parsed.tier).toBeUndefined();
|
|
113
|
+
expect(parsed.tierChain).toEqual([]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("preserves all fields", () => {
|
|
117
|
+
const text = formatters.json(PRERELEASE);
|
|
118
|
+
const parsed = JSON.parse(text);
|
|
119
|
+
expect(parsed.includedReleases).toEqual([
|
|
120
|
+
"stable",
|
|
121
|
+
"experimental",
|
|
122
|
+
"alpha",
|
|
123
|
+
"beta",
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Formatters } from "../../shared/formatters.js";
|
|
2
|
+
import type { ConfigShowData } from "../operations/types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Formatters for `pragma config show` output.
|
|
6
|
+
*
|
|
7
|
+
* - **plain** renders tier chain, channel, package manager, and config path.
|
|
8
|
+
* - **llm** renders a markdown section with bullet points.
|
|
9
|
+
* - **json** serializes the raw ConfigShowData.
|
|
10
|
+
*/
|
|
11
|
+
const formatters: Formatters<ConfigShowData> = {
|
|
12
|
+
plain(data) {
|
|
13
|
+
const lines: string[] = [];
|
|
14
|
+
|
|
15
|
+
if (data.tier !== undefined) {
|
|
16
|
+
const chain = data.tierChain.join(" → ");
|
|
17
|
+
lines.push(`tier: ${data.tier} (${chain})`);
|
|
18
|
+
} else {
|
|
19
|
+
lines.push("tier: (none — all tiers visible)");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const releases = data.includedReleases.join(" + ");
|
|
23
|
+
lines.push(`channel: ${data.channel} (${releases})`);
|
|
24
|
+
lines.push(`installed via: ${data.installSource}`);
|
|
25
|
+
|
|
26
|
+
if (data.configFileExists) {
|
|
27
|
+
lines.push(`config file: ${data.configFilePath}`);
|
|
28
|
+
} else {
|
|
29
|
+
lines.push("config file: (not found)");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return lines.join("\n");
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
llm(data) {
|
|
36
|
+
const lines: string[] = [];
|
|
37
|
+
|
|
38
|
+
lines.push("## Configuration");
|
|
39
|
+
lines.push("");
|
|
40
|
+
|
|
41
|
+
if (data.tier !== undefined) {
|
|
42
|
+
const chain = data.tierChain.join(" > ");
|
|
43
|
+
lines.push(`- **Tier:** ${data.tier} (${chain})`);
|
|
44
|
+
} else {
|
|
45
|
+
lines.push("- **Tier:** none (all tiers visible)");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const releases = data.includedReleases.join(", ");
|
|
49
|
+
lines.push(`- **Channel:** ${data.channel} (${releases})`);
|
|
50
|
+
lines.push(`- **Installed via:** ${data.installSource}`);
|
|
51
|
+
|
|
52
|
+
if (data.configFileExists) {
|
|
53
|
+
lines.push(`- **Config file:** \`${data.configFilePath}\``);
|
|
54
|
+
} else {
|
|
55
|
+
lines.push("- **Config file:** not found");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
json(data) {
|
|
62
|
+
return JSON.stringify(data, null, 2);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default formatters;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import formatters from "./tier.js";
|
|
3
|
+
|
|
4
|
+
describe("tierFormatters.set", () => {
|
|
5
|
+
const data = { field: "tier", value: "apps/lxd" };
|
|
6
|
+
|
|
7
|
+
it("plain renders set message", () => {
|
|
8
|
+
expect(formatters.set.plain(data)).toBe('Set tier to "apps/lxd".');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("llm renders set message", () => {
|
|
12
|
+
expect(formatters.set.llm(data)).toBe('Set tier to "apps/lxd".');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("json returns valid JSON", () => {
|
|
16
|
+
const parsed = JSON.parse(formatters.set.json(data));
|
|
17
|
+
expect(parsed.field).toBe("tier");
|
|
18
|
+
expect(parsed.value).toBe("apps/lxd");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("tierFormatters.reset", () => {
|
|
23
|
+
it("plain renders reset message", () => {
|
|
24
|
+
expect(formatters.reset.plain("")).toBe("Reset tier to default.");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("llm renders reset message", () => {
|
|
28
|
+
expect(formatters.reset.llm("")).toBe("Reset tier to default.");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("json returns valid JSON", () => {
|
|
32
|
+
const parsed = JSON.parse(formatters.reset.json(""));
|
|
33
|
+
expect(parsed.field).toBe("tier");
|
|
34
|
+
expect(parsed.reset).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("tierFormatters.query", () => {
|
|
39
|
+
it("plain shows current tier when set", () => {
|
|
40
|
+
expect(formatters.query.plain("apps")).toBe("Current tier: apps");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("plain shows no tier message when undefined", () => {
|
|
44
|
+
expect(formatters.query.plain(undefined)).toBe(
|
|
45
|
+
"No tier set (all tiers visible).",
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("llm shows current tier when set", () => {
|
|
50
|
+
expect(formatters.query.llm("apps")).toBe("Current tier: apps");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("llm shows no tier message when undefined", () => {
|
|
54
|
+
expect(formatters.query.llm(undefined)).toBe(
|
|
55
|
+
"No tier set (all tiers visible).",
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("json returns value when set", () => {
|
|
60
|
+
const parsed = JSON.parse(formatters.query.json("apps"));
|
|
61
|
+
expect(parsed.field).toBe("tier");
|
|
62
|
+
expect(parsed.value).toBe("apps");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("json returns null value when undefined", () => {
|
|
66
|
+
const parsed = JSON.parse(formatters.query.json(undefined));
|
|
67
|
+
expect(parsed.field).toBe("tier");
|
|
68
|
+
expect(parsed.value).toBeNull();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Formatters } from "../../shared/formatters.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Formatter sets for `pragma config tier` output, grouped by branch:
|
|
5
|
+
*
|
|
6
|
+
* - **set** formats the confirmation after setting a tier (plain/llm/json).
|
|
7
|
+
* - **reset** formats the confirmation after resetting to default (plain/llm/json).
|
|
8
|
+
* - **query** formats the current tier value (plain/llm/json).
|
|
9
|
+
*/
|
|
10
|
+
const tierFormatters = {
|
|
11
|
+
set: {
|
|
12
|
+
plain: (d: { field: string; value: string }) =>
|
|
13
|
+
`Set ${d.field} to "${d.value}".`,
|
|
14
|
+
llm: (d: { field: string; value: string }) =>
|
|
15
|
+
`Set ${d.field} to "${d.value}".`,
|
|
16
|
+
json: (d: { field: string; value: string }) =>
|
|
17
|
+
JSON.stringify({ field: d.field, value: d.value }),
|
|
18
|
+
} satisfies Formatters<{ field: string; value: string }>,
|
|
19
|
+
|
|
20
|
+
reset: {
|
|
21
|
+
plain: () => "Reset tier to default.",
|
|
22
|
+
llm: () => "Reset tier to default.",
|
|
23
|
+
json: () => JSON.stringify({ field: "tier", reset: true }),
|
|
24
|
+
} satisfies Formatters<string>,
|
|
25
|
+
|
|
26
|
+
query: {
|
|
27
|
+
plain: (tier: string | undefined) =>
|
|
28
|
+
tier !== undefined
|
|
29
|
+
? `Current tier: ${tier}`
|
|
30
|
+
: "No tier set (all tiers visible).",
|
|
31
|
+
llm: (tier: string | undefined) =>
|
|
32
|
+
tier !== undefined
|
|
33
|
+
? `Current tier: ${tier}`
|
|
34
|
+
: "No tier set (all tiers visible).",
|
|
35
|
+
json: (tier: string | undefined) =>
|
|
36
|
+
JSON.stringify({ field: "tier", value: tier ?? null }),
|
|
37
|
+
} satisfies Formatters<string | undefined>,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default tierFormatters;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** @module Config domain -- tier, channel, and show commands for `pragma config`. */
|
|
2
|
+
|
|
3
|
+
import type { CommandDefinition } from "@canonical/cli-core";
|
|
4
|
+
import type { PragmaContext } from "../shared/context.js";
|
|
5
|
+
import { channelCommand, showCommand, tierCommand } from "./commands/index.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Return all config command definitions.
|
|
9
|
+
*
|
|
10
|
+
* @param ctx - Pragma context providing cwd, store, and formatter selection.
|
|
11
|
+
* @returns An array of CommandDefinitions for the config subcommands.
|
|
12
|
+
*/
|
|
13
|
+
export function commands(ctx: PragmaContext): CommandDefinition[] {
|
|
14
|
+
return [tierCommand(ctx), channelCommand(ctx), showCommand(ctx)];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { specs as mcpSpecs } from "./mcp/index.js";
|
|
18
|
+
export type { ConfigShowData } from "./operations/index.js";
|
|
19
|
+
export {
|
|
20
|
+
resolveConfigShow,
|
|
21
|
+
validateChannel,
|
|
22
|
+
validateTier,
|
|
23
|
+
} from "./operations/index.js";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool specs for the config domain.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readConfig, writeConfig } from "#config";
|
|
6
|
+
import type { ToolSpec } from "../../shared/ToolSpec.js";
|
|
7
|
+
import { validateChannel, validateTier } from "../operations/index.js";
|
|
8
|
+
|
|
9
|
+
const specs: readonly ToolSpec[] = [
|
|
10
|
+
{
|
|
11
|
+
name: "config_show",
|
|
12
|
+
description:
|
|
13
|
+
"Show current pragma configuration (tier and channel settings).",
|
|
14
|
+
params: {
|
|
15
|
+
condensed: {
|
|
16
|
+
type: "boolean",
|
|
17
|
+
description: "Token-optimized output",
|
|
18
|
+
optional: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
readOnly: true,
|
|
22
|
+
async execute(rt, { condensed }) {
|
|
23
|
+
const data = {
|
|
24
|
+
tier: rt.config.tier ?? null,
|
|
25
|
+
channel: rt.config.channel,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (condensed) {
|
|
29
|
+
const text = `Config: tier=${data.tier ?? "(none)"} channel=${data.channel}`;
|
|
30
|
+
return {
|
|
31
|
+
condensed: true,
|
|
32
|
+
text,
|
|
33
|
+
tokens: `~${Math.ceil(text.length / 4)}`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { data };
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "config_tier",
|
|
42
|
+
description:
|
|
43
|
+
"Set or reset the active tier. Pass a tier path to set, or reset=true to clear.",
|
|
44
|
+
params: {
|
|
45
|
+
path: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Tier path (e.g. 'global', 'apps/lxd')",
|
|
48
|
+
optional: true,
|
|
49
|
+
},
|
|
50
|
+
reset: {
|
|
51
|
+
type: "boolean",
|
|
52
|
+
description: "Reset tier to default",
|
|
53
|
+
optional: true,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
readOnly: false,
|
|
57
|
+
async execute(rt, { path, reset }) {
|
|
58
|
+
if (reset) {
|
|
59
|
+
writeConfig(rt.cwd, { tier: undefined });
|
|
60
|
+
return { data: { tier: null, action: "reset" } };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (path) {
|
|
64
|
+
await validateTier(rt.store, path as string);
|
|
65
|
+
writeConfig(rt.cwd, { tier: path as string });
|
|
66
|
+
return { data: { tier: path, action: "set" } };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const config = readConfig(rt.cwd);
|
|
70
|
+
return { data: { tier: config.tier ?? null, action: "query" } };
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "config_channel",
|
|
75
|
+
description:
|
|
76
|
+
"Set or reset the release channel. Pass a value to set, or reset=true to clear.",
|
|
77
|
+
params: {
|
|
78
|
+
value: {
|
|
79
|
+
type: "string",
|
|
80
|
+
description: "Channel (normal, experimental, prerelease)",
|
|
81
|
+
optional: true,
|
|
82
|
+
},
|
|
83
|
+
reset: {
|
|
84
|
+
type: "boolean",
|
|
85
|
+
description: "Reset channel to normal",
|
|
86
|
+
optional: true,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
readOnly: false,
|
|
90
|
+
async execute(rt, { value, reset }) {
|
|
91
|
+
if (reset) {
|
|
92
|
+
writeConfig(rt.cwd, { channel: undefined });
|
|
93
|
+
return { data: { channel: "normal", action: "reset" } };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (value) {
|
|
97
|
+
const channel = validateChannel(value as string);
|
|
98
|
+
writeConfig(rt.cwd, { channel });
|
|
99
|
+
return { data: { channel, action: "set" } };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const config = readConfig(rt.cwd);
|
|
103
|
+
return { data: { channel: config.channel, action: "query" } };
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
export default specs;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** @module Config operations: show, validate tier, validate channel. */
|
|
2
|
+
export { default as resolveConfigShow } from "./show.js";
|
|
3
|
+
export type { ConfigShowData } from "./types.js";
|
|
4
|
+
export { default as validateChannel } from "./validateChannel.js";
|
|
5
|
+
export { default as validateTier } from "./validateTier.js";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import resolveConfigShow from "./show.js";
|
|
3
|
+
|
|
4
|
+
describe("resolveConfigShow", () => {
|
|
5
|
+
it("resolves tier chain and included releases", () => {
|
|
6
|
+
const data = resolveConfigShow(
|
|
7
|
+
{ tier: "apps/lxd", channel: "experimental" },
|
|
8
|
+
{
|
|
9
|
+
packageManager: "bun",
|
|
10
|
+
installSource: "bun (global)",
|
|
11
|
+
configFilePath: "/tmp/pragma.config.json",
|
|
12
|
+
configFileExists: true,
|
|
13
|
+
},
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
expect(data.tier).toBe("apps/lxd");
|
|
17
|
+
expect(data.tierChain).toEqual(["global", "apps", "apps/lxd"]);
|
|
18
|
+
expect(data.channel).toBe("experimental");
|
|
19
|
+
expect(data.includedReleases).toEqual(["stable", "experimental"]);
|
|
20
|
+
expect(data.packageManager).toBe("bun");
|
|
21
|
+
expect(data.installSource).toBe("bun (global)");
|
|
22
|
+
expect(data.configFileExists).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("returns empty tier chain when no tier is set", () => {
|
|
26
|
+
const data = resolveConfigShow(
|
|
27
|
+
{ tier: undefined, channel: "normal" },
|
|
28
|
+
{
|
|
29
|
+
packageManager: "npm",
|
|
30
|
+
installSource: "bun (local)",
|
|
31
|
+
configFilePath: "/tmp/pragma.config.json",
|
|
32
|
+
configFileExists: false,
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(data.tier).toBeUndefined();
|
|
37
|
+
expect(data.tierChain).toEqual([]);
|
|
38
|
+
expect(data.includedReleases).toEqual(["stable"]);
|
|
39
|
+
expect(data.configFileExists).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("includes all release levels for prerelease channel", () => {
|
|
43
|
+
const data = resolveConfigShow(
|
|
44
|
+
{ tier: undefined, channel: "prerelease" },
|
|
45
|
+
{
|
|
46
|
+
packageManager: "pnpm",
|
|
47
|
+
installSource: "pnpm (global)",
|
|
48
|
+
configFilePath: "/tmp/pragma.config.json",
|
|
49
|
+
configFileExists: true,
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(data.includedReleases).toEqual([
|
|
54
|
+
"stable",
|
|
55
|
+
"experimental",
|
|
56
|
+
"alpha",
|
|
57
|
+
"beta",
|
|
58
|
+
]);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { PragmaConfig } from "#config";
|
|
2
|
+
import { CHANNEL_RELEASES } from "../../shared/filters/buildChannelFilter.js";
|
|
3
|
+
import { resolveTierChain } from "../../shared/filters/buildTierFilter.js";
|
|
4
|
+
import type { ConfigShowData } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Assemble the data needed for `pragma config show` output.
|
|
8
|
+
*
|
|
9
|
+
* Pure data assembly -- the tier chain is resolved from the path
|
|
10
|
+
* string, not from the ontology.
|
|
11
|
+
*
|
|
12
|
+
* @param config - The resolved pragma config.
|
|
13
|
+
* @param opts - Additional context: package manager, config file path, and existence flag.
|
|
14
|
+
* @returns The fully resolved ConfigShowData.
|
|
15
|
+
*/
|
|
16
|
+
export default function resolveConfigShow(
|
|
17
|
+
config: PragmaConfig,
|
|
18
|
+
opts: {
|
|
19
|
+
packageManager: string;
|
|
20
|
+
installSource: string;
|
|
21
|
+
configFilePath: string;
|
|
22
|
+
configFileExists: boolean;
|
|
23
|
+
},
|
|
24
|
+
): ConfigShowData {
|
|
25
|
+
// Don't throw for stale config — show always displays what's configured.
|
|
26
|
+
// resolveTierChain is pure string manipulation, doesn't need ontology.
|
|
27
|
+
const tierChain =
|
|
28
|
+
config.tier !== undefined ? resolveTierChain(config.tier) : [];
|
|
29
|
+
|
|
30
|
+
const includedReleases = CHANNEL_RELEASES[config.channel];
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
tier: config.tier,
|
|
34
|
+
tierChain,
|
|
35
|
+
channel: config.channel,
|
|
36
|
+
includedReleases,
|
|
37
|
+
packageManager: opts.packageManager,
|
|
38
|
+
installSource: opts.installSource,
|
|
39
|
+
configFilePath: opts.configFilePath,
|
|
40
|
+
configFileExists: opts.configFileExists,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Channel } from "#constants";
|
|
2
|
+
|
|
3
|
+
/** Data returned by `pragma config show`, including resolved tier chain and channel releases. */
|
|
4
|
+
export interface ConfigShowData {
|
|
5
|
+
readonly tier: string | undefined;
|
|
6
|
+
readonly tierChain: readonly string[];
|
|
7
|
+
readonly channel: Channel;
|
|
8
|
+
readonly includedReleases: readonly string[];
|
|
9
|
+
readonly packageManager: string;
|
|
10
|
+
readonly installSource: string;
|
|
11
|
+
readonly configFilePath: string;
|
|
12
|
+
readonly configFileExists: boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import validateChannel from "./validateChannel.js";
|
|
3
|
+
|
|
4
|
+
describe("validateChannel", () => {
|
|
5
|
+
it("returns the channel for valid values", () => {
|
|
6
|
+
expect(validateChannel("normal")).toBe("normal");
|
|
7
|
+
expect(validateChannel("experimental")).toBe("experimental");
|
|
8
|
+
expect(validateChannel("prerelease")).toBe("prerelease");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("throws INVALID_INPUT for an invalid channel", () => {
|
|
12
|
+
expect(() => validateChannel("aggressive")).toThrow(
|
|
13
|
+
'Invalid channel "aggressive"',
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("includes valid channels in error validOptions", () => {
|
|
18
|
+
try {
|
|
19
|
+
validateChannel("nope");
|
|
20
|
+
expect.unreachable("should have thrown");
|
|
21
|
+
} catch (err: unknown) {
|
|
22
|
+
const pragmaErr = err as {
|
|
23
|
+
code: string;
|
|
24
|
+
validOptions: string[];
|
|
25
|
+
recovery?: { cli?: string };
|
|
26
|
+
};
|
|
27
|
+
expect(pragmaErr.code).toBe("INVALID_INPUT");
|
|
28
|
+
expect(pragmaErr.validOptions).toEqual([
|
|
29
|
+
"normal",
|
|
30
|
+
"experimental",
|
|
31
|
+
"prerelease",
|
|
32
|
+
]);
|
|
33
|
+
expect(pragmaErr.recovery?.cli).toBe("pragma config channel --reset");
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Channel, VALID_CHANNELS } from "#constants";
|
|
2
|
+
import { PragmaError } from "#error";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validate that a string is one of the accepted channel values.
|
|
6
|
+
*
|
|
7
|
+
* @param value - The channel string to validate.
|
|
8
|
+
* @returns The validated Channel value.
|
|
9
|
+
* @throws PragmaError.invalidInput if the value is not a valid channel.
|
|
10
|
+
*/
|
|
11
|
+
export default function validateChannel(value: string): Channel {
|
|
12
|
+
if (VALID_CHANNELS.includes(value as Channel)) {
|
|
13
|
+
return value as Channel;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
throw PragmaError.invalidInput("channel", value, {
|
|
17
|
+
validOptions: [...VALID_CHANNELS],
|
|
18
|
+
recovery: {
|
|
19
|
+
message: "Reset channel configuration.",
|
|
20
|
+
cli: "pragma config channel --reset",
|
|
21
|
+
mcp: { tool: "config_channel", params: { reset: true } },
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Store } from "@canonical/ke";
|
|
2
|
+
import type { TestStoreResult } from "@canonical/ke/testing";
|
|
3
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
4
|
+
import { createTestStore, DS_ONTOLOGY_TTL, DS_TIERS_TTL } from "#testing";
|
|
5
|
+
import validateTier from "./validateTier.js";
|
|
6
|
+
|
|
7
|
+
const TIERS_TTL = [DS_ONTOLOGY_TTL, DS_TIERS_TTL].join("\n");
|
|
8
|
+
|
|
9
|
+
describe("validateTier", () => {
|
|
10
|
+
let store: Store;
|
|
11
|
+
let cleanup: TestStoreResult["cleanup"];
|
|
12
|
+
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
const result = await createTestStore({ ttl: TIERS_TTL });
|
|
15
|
+
store = result.store;
|
|
16
|
+
cleanup = result.cleanup;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(async () => {
|
|
20
|
+
await cleanup();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("returns the matching TierEntry for a valid tier", async () => {
|
|
24
|
+
const entry = await validateTier(store, "apps/lxd");
|
|
25
|
+
expect(entry.path).toBe("apps/lxd");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("returns the global tier", async () => {
|
|
29
|
+
const entry = await validateTier(store, "global");
|
|
30
|
+
expect(entry.path).toBe("global");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("throws INVALID_INPUT for a nonexistent tier", async () => {
|
|
34
|
+
await expect(validateTier(store, "apps/nonexistent")).rejects.toThrow(
|
|
35
|
+
'Invalid tier "apps/nonexistent"',
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("includes valid tier paths in error validOptions", async () => {
|
|
40
|
+
try {
|
|
41
|
+
await validateTier(store, "nope");
|
|
42
|
+
expect.unreachable("should have thrown");
|
|
43
|
+
} catch (err: unknown) {
|
|
44
|
+
const pragmaErr = err as {
|
|
45
|
+
code: string;
|
|
46
|
+
validOptions: string[];
|
|
47
|
+
recovery: { message: string; cli: string };
|
|
48
|
+
};
|
|
49
|
+
expect(pragmaErr.code).toBe("INVALID_INPUT");
|
|
50
|
+
expect(pragmaErr.validOptions).toContain("global");
|
|
51
|
+
expect(pragmaErr.validOptions).toContain("apps");
|
|
52
|
+
expect(pragmaErr.validOptions).toContain("apps/lxd");
|
|
53
|
+
expect(pragmaErr.recovery.cli).toBe("pragma config tier --reset");
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|