@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,108 @@
|
|
|
1
|
+
import type { McpServerConfig } from "@canonical/harnesses";
|
|
2
|
+
import {
|
|
3
|
+
detectHarnesses,
|
|
4
|
+
findHarnessById,
|
|
5
|
+
writeMcpConfig,
|
|
6
|
+
} from "@canonical/harnesses";
|
|
7
|
+
import {
|
|
8
|
+
$,
|
|
9
|
+
gen,
|
|
10
|
+
info,
|
|
11
|
+
promptConfirm,
|
|
12
|
+
type Task,
|
|
13
|
+
traverse_,
|
|
14
|
+
warn,
|
|
15
|
+
when,
|
|
16
|
+
} from "@canonical/task";
|
|
17
|
+
import { MCP_SERVER_NAME } from "../helpers/constants.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build the pragma MCP server config object for a project root.
|
|
21
|
+
*
|
|
22
|
+
* @param root - Project root directory.
|
|
23
|
+
* @returns The MCP server configuration pointing to `pragma mcp`.
|
|
24
|
+
*/
|
|
25
|
+
function pragmaMcpConfig(root: string): McpServerConfig {
|
|
26
|
+
return { command: "pragma", args: ["mcp"], cwd: root };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compose a Task that configures the pragma MCP server for detected
|
|
31
|
+
* (or forced) AI harnesses. In detection mode, prompts for each
|
|
32
|
+
* discovered harness before writing.
|
|
33
|
+
*
|
|
34
|
+
* @param root - Project root directory.
|
|
35
|
+
* @param forceHarnessId - If set, bypass detection and configure this harness only.
|
|
36
|
+
* @returns A Task that yields void on completion.
|
|
37
|
+
* @note Impure
|
|
38
|
+
*/
|
|
39
|
+
export default function setupMcp(
|
|
40
|
+
root: string,
|
|
41
|
+
forceHarnessId?: string,
|
|
42
|
+
): Task<void> {
|
|
43
|
+
return gen(function* () {
|
|
44
|
+
// Force mode: bypass detection
|
|
45
|
+
if (forceHarnessId) {
|
|
46
|
+
const harness = findHarnessById(forceHarnessId);
|
|
47
|
+
if (!harness) {
|
|
48
|
+
yield* $(warn(`Unknown harness: ${forceHarnessId}`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
yield* $(
|
|
52
|
+
writeMcpConfig(harness, root, MCP_SERVER_NAME, pragmaMcpConfig(root)),
|
|
53
|
+
);
|
|
54
|
+
yield* $(
|
|
55
|
+
info(`✓ pragma MCP server added to ${harness.configPath(root)}`),
|
|
56
|
+
);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Detection mode
|
|
61
|
+
const detected = yield* $(detectHarnesses(root));
|
|
62
|
+
|
|
63
|
+
if (detected.length === 0) {
|
|
64
|
+
yield* $(
|
|
65
|
+
warn(
|
|
66
|
+
"No AI harnesses detected. Use --claude-code, --cursor, or --windsurf to configure manually.",
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
yield* $(
|
|
73
|
+
info(
|
|
74
|
+
`Detected harness${detected.length > 1 ? "es" : ""}: ${detected.map((d) => d.harness.name).join(", ")}`,
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
yield* $(
|
|
79
|
+
traverse_(detected, (d) =>
|
|
80
|
+
gen(function* () {
|
|
81
|
+
const confirmed = yield* $(
|
|
82
|
+
promptConfirm(
|
|
83
|
+
`setup-mcp-${d.harness.id}`,
|
|
84
|
+
`Configure pragma MCP for ${d.harness.name}?`,
|
|
85
|
+
true,
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
yield* $(
|
|
89
|
+
when(
|
|
90
|
+
confirmed,
|
|
91
|
+
gen(function* () {
|
|
92
|
+
yield* $(
|
|
93
|
+
writeMcpConfig(
|
|
94
|
+
d.harness,
|
|
95
|
+
root,
|
|
96
|
+
MCP_SERVER_NAME,
|
|
97
|
+
pragmaMcpConfig(root),
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
yield* $(info(`✓ pragma MCP server added to ${d.configPath}`));
|
|
101
|
+
}),
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
}),
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { DetectedHarness, HarnessDefinition } from "@canonical/harnesses";
|
|
2
|
+
import { dryRunWith, type Effect, filterEffects } from "@canonical/task";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import type { DiscoveredSkill } from "../../skill/types.js";
|
|
5
|
+
import type { SetupSkillsResult } from "../types.js";
|
|
6
|
+
import setupSkills from "./setupSkills.js";
|
|
7
|
+
|
|
8
|
+
const MOCK_SKILLS: readonly DiscoveredSkill[] = [
|
|
9
|
+
{
|
|
10
|
+
name: "design-audit",
|
|
11
|
+
description: "Audit a component against DS specs",
|
|
12
|
+
sourcePath:
|
|
13
|
+
"/project/node_modules/@canonical/ds-global/skills/design-audit",
|
|
14
|
+
sourcePackage: "@canonical/ds-global",
|
|
15
|
+
folderName: "design-audit",
|
|
16
|
+
frontmatter: {
|
|
17
|
+
name: "design-audit",
|
|
18
|
+
description: "Audit a component against DS specs",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "scaffold-story",
|
|
23
|
+
description: "Generate a Storybook story from anatomy",
|
|
24
|
+
sourcePath:
|
|
25
|
+
"/project/node_modules/@canonical/pragma-cli/skills/scaffold-story",
|
|
26
|
+
sourcePackage: "@canonical/pragma-cli",
|
|
27
|
+
folderName: "scaffold-story",
|
|
28
|
+
frontmatter: {
|
|
29
|
+
name: "scaffold-story",
|
|
30
|
+
description: "Generate a Storybook story from anatomy",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function makeHarness(
|
|
36
|
+
id: string,
|
|
37
|
+
name: string,
|
|
38
|
+
skillsDir: string,
|
|
39
|
+
): DetectedHarness {
|
|
40
|
+
return {
|
|
41
|
+
harness: {
|
|
42
|
+
id,
|
|
43
|
+
name,
|
|
44
|
+
version: "*",
|
|
45
|
+
detect: [],
|
|
46
|
+
configPath: () => "",
|
|
47
|
+
configFormat: "json",
|
|
48
|
+
mcpKey: "mcpServers",
|
|
49
|
+
skillsPath: (root: string) => `${root}/${skillsDir}`,
|
|
50
|
+
} as HarnessDefinition,
|
|
51
|
+
confidence: "high",
|
|
52
|
+
configExists: false,
|
|
53
|
+
configPath: "",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const MOCK_HARNESSES: readonly DetectedHarness[] = [
|
|
58
|
+
makeHarness("claude-code", "Claude Code", ".claude/skills"),
|
|
59
|
+
makeHarness("cursor", "Cursor", ".cursor/skills"),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
describe("setupSkills", () => {
|
|
63
|
+
it("creates symlinks for each skill in each harness", () => {
|
|
64
|
+
const task = setupSkills(MOCK_SKILLS, MOCK_HARNESSES, "/project");
|
|
65
|
+
|
|
66
|
+
const mocks = new Map<string, (e: Effect) => unknown>([
|
|
67
|
+
["Exists", () => false],
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
const { value, effects } = dryRunWith(task, mocks);
|
|
71
|
+
const result = value as SetupSkillsResult;
|
|
72
|
+
|
|
73
|
+
const created = result.actions.filter((a) => a.action === "created");
|
|
74
|
+
// 2 skills × 3 targets (claude, cursor, .agents/skills) = 6
|
|
75
|
+
expect(created).toHaveLength(6);
|
|
76
|
+
expect(result.harnessCount).toBe(2);
|
|
77
|
+
expect(result.skillCount).toBe(2);
|
|
78
|
+
|
|
79
|
+
const symlinkEffects = filterEffects(effects, "Symlink");
|
|
80
|
+
expect(symlinkEffects).toHaveLength(6);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("skips existing correct symlinks", () => {
|
|
84
|
+
const task = setupSkills(MOCK_SKILLS, MOCK_HARNESSES, "/project");
|
|
85
|
+
|
|
86
|
+
const mocks = new Map<string, (e: Effect) => unknown>([
|
|
87
|
+
["Exists", () => true],
|
|
88
|
+
[
|
|
89
|
+
"Exec",
|
|
90
|
+
(e: Effect) => {
|
|
91
|
+
if (e._tag === "Exec" && e.command === "readlink") {
|
|
92
|
+
const linkPath = e.args[0];
|
|
93
|
+
if (linkPath.includes("design-audit")) {
|
|
94
|
+
return {
|
|
95
|
+
stdout:
|
|
96
|
+
"/project/node_modules/@canonical/ds-global/skills/design-audit",
|
|
97
|
+
stderr: "",
|
|
98
|
+
exitCode: 0,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (linkPath.includes("scaffold-story")) {
|
|
102
|
+
return {
|
|
103
|
+
stdout:
|
|
104
|
+
"/project/node_modules/@canonical/pragma-cli/skills/scaffold-story",
|
|
105
|
+
stderr: "",
|
|
106
|
+
exitCode: 0,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { stdout: "", stderr: "", exitCode: 0 };
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
const { value } = dryRunWith(task, mocks);
|
|
116
|
+
const result = value as SetupSkillsResult;
|
|
117
|
+
|
|
118
|
+
const skipped = result.actions.filter((a) => a.action === "skipped");
|
|
119
|
+
expect(skipped).toHaveLength(6);
|
|
120
|
+
expect(result.warnings).toHaveLength(0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("replaces wrong symlinks with warning", () => {
|
|
124
|
+
const task = setupSkills([MOCK_SKILLS[0]], [MOCK_HARNESSES[0]], "/project");
|
|
125
|
+
|
|
126
|
+
const mocks = new Map<string, (e: Effect) => unknown>([
|
|
127
|
+
["Exists", () => true],
|
|
128
|
+
[
|
|
129
|
+
"Exec",
|
|
130
|
+
() => ({
|
|
131
|
+
stdout: "/wrong/target",
|
|
132
|
+
stderr: "",
|
|
133
|
+
exitCode: 0,
|
|
134
|
+
}),
|
|
135
|
+
],
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const { value } = dryRunWith(task, mocks);
|
|
139
|
+
const result = value as SetupSkillsResult;
|
|
140
|
+
|
|
141
|
+
const replaced = result.actions.filter((a) => a.action === "replaced");
|
|
142
|
+
expect(replaced.length).toBeGreaterThan(0);
|
|
143
|
+
expect(result.warnings.length).toBeGreaterThan(0);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("deduplicates cross-client .agents/skills path", () => {
|
|
147
|
+
const harnesses = [
|
|
148
|
+
makeHarness("cline", "Cline", ".agents/skills"),
|
|
149
|
+
makeHarness("opencode", "OpenCode", ".agents/skills"),
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
const task = setupSkills(MOCK_SKILLS, harnesses, "/project");
|
|
153
|
+
|
|
154
|
+
const mocks = new Map<string, (e: Effect) => unknown>([
|
|
155
|
+
["Exists", () => false],
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
const { value } = dryRunWith(task, mocks);
|
|
159
|
+
const result = value as SetupSkillsResult;
|
|
160
|
+
|
|
161
|
+
// Both harnesses use .agents/skills, so only one set of symlinks
|
|
162
|
+
// plus no separate cross-client dir (already covered)
|
|
163
|
+
const created = result.actions.filter((a) => a.action === "created");
|
|
164
|
+
expect(created).toHaveLength(2); // 2 skills × 1 unique dir
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("handles empty skills list", () => {
|
|
168
|
+
const task = setupSkills([], MOCK_HARNESSES, "/project");
|
|
169
|
+
|
|
170
|
+
const { value } = dryRunWith(task, new Map());
|
|
171
|
+
const result = value as SetupSkillsResult;
|
|
172
|
+
|
|
173
|
+
expect(result.actions).toHaveLength(0);
|
|
174
|
+
expect(result.skillCount).toBe(0);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("handles empty harnesses list", () => {
|
|
178
|
+
const task = setupSkills(MOCK_SKILLS, [], "/project");
|
|
179
|
+
|
|
180
|
+
const mocks = new Map<string, (e: Effect) => unknown>([
|
|
181
|
+
["Exists", () => false],
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
const { value } = dryRunWith(task, mocks);
|
|
185
|
+
const result = value as SetupSkillsResult;
|
|
186
|
+
|
|
187
|
+
// Still creates cross-client .agents/skills
|
|
188
|
+
const created = result.actions.filter((a) => a.action === "created");
|
|
189
|
+
expect(created).toHaveLength(2); // 2 skills × 1 (.agents/skills)
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import type { DetectedHarness } from "@canonical/harnesses";
|
|
3
|
+
import {
|
|
4
|
+
deleteFile,
|
|
5
|
+
exec,
|
|
6
|
+
exists,
|
|
7
|
+
flatMap,
|
|
8
|
+
map,
|
|
9
|
+
mkdir,
|
|
10
|
+
pure,
|
|
11
|
+
sequence,
|
|
12
|
+
symlink,
|
|
13
|
+
type Task,
|
|
14
|
+
} from "@canonical/task";
|
|
15
|
+
import type { DiscoveredSkill } from "../../skill/types.js";
|
|
16
|
+
import type { SetupSkillsResult, SymlinkAction } from "../types.js";
|
|
17
|
+
|
|
18
|
+
/** Cross-client skill directory, shared across all harnesses. */
|
|
19
|
+
const CROSS_CLIENT_DIR = ".agents/skills";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create or replace a single skill symlink, returning the action taken.
|
|
23
|
+
*
|
|
24
|
+
* @param skill - The discovered skill to symlink.
|
|
25
|
+
* @param targetDir - Directory where the symlink will be created.
|
|
26
|
+
* @param projectRoot - Project root for resolving absolute paths.
|
|
27
|
+
* @param harnessName - Display name of the target harness.
|
|
28
|
+
* @returns A Task yielding the SymlinkAction describing what happened.
|
|
29
|
+
*/
|
|
30
|
+
function symlinkOneSkill(
|
|
31
|
+
skill: DiscoveredSkill,
|
|
32
|
+
targetDir: string,
|
|
33
|
+
harnessName: string,
|
|
34
|
+
): Task<SymlinkAction> {
|
|
35
|
+
const target = skill.sourcePath;
|
|
36
|
+
const linkPath = resolve(targetDir, skill.folderName);
|
|
37
|
+
|
|
38
|
+
return flatMap(exists(linkPath), (linkExists) => {
|
|
39
|
+
if (!linkExists) {
|
|
40
|
+
return map(
|
|
41
|
+
symlink(target, linkPath),
|
|
42
|
+
(): SymlinkAction => ({
|
|
43
|
+
skillName: skill.name,
|
|
44
|
+
target,
|
|
45
|
+
linkPath,
|
|
46
|
+
action: "created",
|
|
47
|
+
harnessName,
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return flatMap(
|
|
53
|
+
exec("readlink", [linkPath]),
|
|
54
|
+
(readlinkResult): Task<SymlinkAction> => {
|
|
55
|
+
const currentTarget = readlinkResult.stdout.trim();
|
|
56
|
+
if (currentTarget === target) {
|
|
57
|
+
return pure({
|
|
58
|
+
skillName: skill.name,
|
|
59
|
+
target,
|
|
60
|
+
linkPath,
|
|
61
|
+
action: "skipped",
|
|
62
|
+
harnessName,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return map(
|
|
67
|
+
flatMap(deleteFile(linkPath), () => symlink(target, linkPath)),
|
|
68
|
+
(): SymlinkAction => ({
|
|
69
|
+
skillName: skill.name,
|
|
70
|
+
target,
|
|
71
|
+
linkPath,
|
|
72
|
+
action: "replaced",
|
|
73
|
+
harnessName,
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Symlink all skills into a single target directory, creating the
|
|
83
|
+
* directory if it does not exist.
|
|
84
|
+
*
|
|
85
|
+
* @param skills - Skills to symlink.
|
|
86
|
+
* @param targetDir - Target directory path.
|
|
87
|
+
* @param projectRoot - Project root for resolving absolute paths.
|
|
88
|
+
* @param harnessName - Display name of the target harness.
|
|
89
|
+
* @returns A Task yielding an array of SymlinkActions.
|
|
90
|
+
*/
|
|
91
|
+
function symlinkForTarget(
|
|
92
|
+
skills: readonly DiscoveredSkill[],
|
|
93
|
+
targetDir: string,
|
|
94
|
+
harnessName: string,
|
|
95
|
+
): Task<SymlinkAction[]> {
|
|
96
|
+
return flatMap(mkdir(targetDir), () =>
|
|
97
|
+
map(
|
|
98
|
+
sequence(
|
|
99
|
+
skills.map((skill) => symlinkOneSkill(skill, targetDir, harnessName)),
|
|
100
|
+
),
|
|
101
|
+
(actions) => [...actions],
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Symlink discovered skills into each harness skill directory and the
|
|
108
|
+
* cross-client `.agents/skills` directory. Idempotent: existing correct
|
|
109
|
+
* symlinks are skipped, stale symlinks are replaced.
|
|
110
|
+
*
|
|
111
|
+
* @param skills - Skills discovered from installed packages.
|
|
112
|
+
* @param harnesses - Detected AI harnesses to target.
|
|
113
|
+
* @param projectRoot - Project root directory.
|
|
114
|
+
* @returns A Task yielding a SetupSkillsResult summary.
|
|
115
|
+
* @note Impure
|
|
116
|
+
*/
|
|
117
|
+
export default function setupSkills(
|
|
118
|
+
skills: readonly DiscoveredSkill[],
|
|
119
|
+
harnesses: readonly DetectedHarness[],
|
|
120
|
+
projectRoot: string,
|
|
121
|
+
): Task<SetupSkillsResult> {
|
|
122
|
+
const processedDirs = new Set<string>();
|
|
123
|
+
const targets: { dir: string; name: string }[] = [];
|
|
124
|
+
|
|
125
|
+
for (const detected of harnesses) {
|
|
126
|
+
const dir = detected.harness.skillsPath(projectRoot);
|
|
127
|
+
if (!processedDirs.has(dir)) {
|
|
128
|
+
processedDirs.add(dir);
|
|
129
|
+
targets.push({ dir, name: detected.harness.name });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const crossClientDir = resolve(projectRoot, CROSS_CLIENT_DIR);
|
|
134
|
+
if (!processedDirs.has(crossClientDir)) {
|
|
135
|
+
processedDirs.add(crossClientDir);
|
|
136
|
+
targets.push({ dir: crossClientDir, name: ".agents/skills" });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return map(
|
|
140
|
+
sequence(
|
|
141
|
+
targets.map(({ dir, name }) => symlinkForTarget(skills, dir, name)),
|
|
142
|
+
),
|
|
143
|
+
(actionGroups): SetupSkillsResult => {
|
|
144
|
+
const allActions = actionGroups.flat();
|
|
145
|
+
const warnings = allActions
|
|
146
|
+
.filter((a) => a.action === "replaced")
|
|
147
|
+
.map(
|
|
148
|
+
(a) =>
|
|
149
|
+
`Replaced existing symlink for ${a.skillName} in ${a.harnessName}`,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
actions: allActions,
|
|
154
|
+
harnessCount: harnesses.length,
|
|
155
|
+
skillCount: skills.length,
|
|
156
|
+
warnings,
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Describes a single symlink create/skip/replace action performed during skill setup. */
|
|
2
|
+
export interface SymlinkAction {
|
|
3
|
+
readonly skillName: string;
|
|
4
|
+
readonly target: string;
|
|
5
|
+
readonly linkPath: string;
|
|
6
|
+
readonly action: "created" | "skipped" | "replaced";
|
|
7
|
+
readonly harnessName: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Aggregate result of the `pragma setup skills` operation. */
|
|
11
|
+
export interface SetupSkillsResult {
|
|
12
|
+
readonly actions: readonly SymlinkAction[];
|
|
13
|
+
readonly harnessCount: number;
|
|
14
|
+
readonly skillCount: number;
|
|
15
|
+
readonly warnings: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declarative MCP tool specification — no MCP imports.
|
|
3
|
+
*
|
|
4
|
+
* Domains export `ToolSpec[]` from `mcp/specs.ts`. The `mcp/` adapter layer
|
|
5
|
+
* consumes specs via `registerFromSpec()` to wire zod schemas, wrapTool
|
|
6
|
+
* envelopes, and `server.registerTool()` calls.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { PragmaRuntime } from "./runtime.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parameter definition for a tool — neutral format.
|
|
13
|
+
*
|
|
14
|
+
* The MCP adapter generates zod schemas from this. A future CLI adapter
|
|
15
|
+
* could generate `ParameterDefinition[]`.
|
|
16
|
+
*/
|
|
17
|
+
export interface ToolParamDef {
|
|
18
|
+
readonly type: "string" | "boolean" | "string[]";
|
|
19
|
+
readonly description: string;
|
|
20
|
+
readonly optional?: boolean;
|
|
21
|
+
readonly enum?: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Return value from a tool spec's execute function.
|
|
26
|
+
*
|
|
27
|
+
* Structurally compatible with `ToolPayload` from `mcp/types.ts`.
|
|
28
|
+
*/
|
|
29
|
+
export type ToolResult =
|
|
30
|
+
| { data: unknown; meta?: Record<string, unknown> }
|
|
31
|
+
| { condensed: true; text: string; tokens: string };
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Declarative tool specification — no MCP imports.
|
|
35
|
+
*
|
|
36
|
+
* Domains export arrays of these. The `mcp/tools/registerFromSpec.ts`
|
|
37
|
+
* adapter converts each spec into a registered MCP tool.
|
|
38
|
+
*/
|
|
39
|
+
export interface ToolSpec {
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly description: string;
|
|
42
|
+
readonly params?: Readonly<Record<string, ToolParamDef>>;
|
|
43
|
+
readonly readOnly: boolean;
|
|
44
|
+
readonly destructive?: boolean;
|
|
45
|
+
readonly execute: (
|
|
46
|
+
rt: PragmaRuntime,
|
|
47
|
+
params: Record<string, unknown>,
|
|
48
|
+
) => Promise<ToolResult>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot a ke store from pragma.config.json sources.
|
|
3
|
+
*
|
|
4
|
+
* Reads configuration, resolves sources (defaults or overridden),
|
|
5
|
+
* creates the store with registered prefixes, and wraps errors.
|
|
6
|
+
*
|
|
7
|
+
* @note Impure — reads filesystem, creates ke store.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import type { SourceSpec, Store } from "@canonical/ke";
|
|
12
|
+
import { createStore } from "@canonical/ke";
|
|
13
|
+
import type { PragmaConfig } from "#config";
|
|
14
|
+
import { PragmaError } from "../../error/index.js";
|
|
15
|
+
import { PACKAGES, resolvePackages } from "./packages.js";
|
|
16
|
+
import { PREFIX_MAP } from "./prefixes.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolve default TTL sources from the package registry.
|
|
20
|
+
* Package-manager agnostic — works with bun, npm, pnpm, and yarn.
|
|
21
|
+
*/
|
|
22
|
+
export function defaultSources(): SourceSpec[] {
|
|
23
|
+
const sources: SourceSpec[] = [];
|
|
24
|
+
const resolved = resolvePackages();
|
|
25
|
+
|
|
26
|
+
for (const { pkg, dir } of resolved) {
|
|
27
|
+
const def = PACKAGES.find((p) => p.pkg === pkg);
|
|
28
|
+
if (!def) continue;
|
|
29
|
+
for (const glob of def.ttl) {
|
|
30
|
+
sources.push(join(dir, glob));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return sources;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BootStoreOptions {
|
|
38
|
+
/** Override config (skip reading from disk). */
|
|
39
|
+
config?: PragmaConfig;
|
|
40
|
+
/** Override sources (skip config sources field). */
|
|
41
|
+
sources?: SourceSpec[];
|
|
42
|
+
/** Working directory for resolving relative paths. */
|
|
43
|
+
cwd?: string;
|
|
44
|
+
/** Cache path for serialized store. */
|
|
45
|
+
cache?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Boot a ke store from configuration.
|
|
50
|
+
*
|
|
51
|
+
* @throws PragmaError with code STORE_ERROR on failure.
|
|
52
|
+
*/
|
|
53
|
+
export async function bootStore(
|
|
54
|
+
options: BootStoreOptions = {},
|
|
55
|
+
): Promise<Store> {
|
|
56
|
+
const sources = options.sources ?? defaultSources();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const store = await createStore({
|
|
60
|
+
sources,
|
|
61
|
+
prefixes: PREFIX_MAP,
|
|
62
|
+
cache: options.cache,
|
|
63
|
+
cwd: options.cwd,
|
|
64
|
+
});
|
|
65
|
+
return store;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw PragmaError.storeError(
|
|
68
|
+
error instanceof Error ? error.message : String(error),
|
|
69
|
+
{
|
|
70
|
+
recovery: {
|
|
71
|
+
message:
|
|
72
|
+
"Ensure design system packages are installed: bun add -D @canonical/design-system @canonical/code-standards @canonical/anatomy-dsl",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand an internally-constructed query string as SPARQL.
|
|
3
|
+
*
|
|
4
|
+
* The `sparql` tagged template cannot interpolate raw SPARQL fragments
|
|
5
|
+
* (it would escape them as string literals). Operations that include
|
|
6
|
+
* dynamic filter clauses construct the query as a plain string and
|
|
7
|
+
* brand it via this helper.
|
|
8
|
+
*
|
|
9
|
+
* Safe because filter content is generated internally, never from
|
|
10
|
+
* user input. User-supplied values (e.g., component names) are
|
|
11
|
+
* escaped via `escapeSparqlValue()` before interpolation.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { SPARQL } from "@canonical/ke";
|
|
15
|
+
|
|
16
|
+
export function buildQuery(query: string): SPARQL<string> {
|
|
17
|
+
return query as SPARQL<string>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import compactUri from "./compactUri.js";
|
|
3
|
+
|
|
4
|
+
describe("compactUri", () => {
|
|
5
|
+
const prefixes = {
|
|
6
|
+
ds: "https://ds.canonical.com/",
|
|
7
|
+
cs: "http://pragma.canonical.com/codestandards#",
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
it("compacts a URI when a matching prefix exists", () => {
|
|
11
|
+
expect(
|
|
12
|
+
compactUri("https://ds.canonical.com/global.component.button", prefixes),
|
|
13
|
+
).toBe("ds:global.component.button");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns the original URI when no prefix matches", () => {
|
|
17
|
+
expect(compactUri("https://example.com/thing", prefixes)).toBe(
|
|
18
|
+
"https://example.com/thing",
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a full URI to its compact prefixed form when a matching prefix exists.
|
|
3
|
+
* Falls back to the original URI when no namespace matches.
|
|
4
|
+
*/
|
|
5
|
+
export default function compactUri(
|
|
6
|
+
fullUri: string,
|
|
7
|
+
prefixes: Readonly<Record<string, string>>,
|
|
8
|
+
): string {
|
|
9
|
+
for (const [prefix, namespace] of Object.entries(prefixes)) {
|
|
10
|
+
if (fullUri.startsWith(namespace)) {
|
|
11
|
+
return `${prefix}:${fullUri.slice(namespace.length)}`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return fullUri;
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended command context for pragma CLI commands.
|
|
3
|
+
*
|
|
4
|
+
* Extends `PragmaRuntime` with CLI-specific global flags so commands
|
|
5
|
+
* access store, config, cwd, and output preferences uniformly via `ctx`.
|
|
6
|
+
*
|
|
7
|
+
* cli-core remains unaware of Store or FilterConfig.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { GlobalFlags } from "@canonical/cli-core";
|
|
11
|
+
import type { PragmaRuntime } from "./runtime.js";
|
|
12
|
+
|
|
13
|
+
export interface PragmaContext extends PragmaRuntime {
|
|
14
|
+
readonly globalFlags: GlobalFlags;
|
|
15
|
+
}
|