@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,145 @@
|
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
5
|
+
import type { SkillSource } from "../helpers/index.js";
|
|
6
|
+
import discoverSkills from "./discover.js";
|
|
7
|
+
|
|
8
|
+
const TMP_ROOT = join(tmpdir(), `pragma-skill-discover-${Date.now()}`);
|
|
9
|
+
|
|
10
|
+
/** Test skill sources that mirror the real package layout. */
|
|
11
|
+
const TEST_SOURCES: SkillSource[] = [
|
|
12
|
+
{
|
|
13
|
+
dir: join(TMP_ROOT, "node_modules/@canonical/design-system/skills"),
|
|
14
|
+
packageName: "@canonical/design-system",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
dir: join(TMP_ROOT, "node_modules/@canonical/anatomy-dsl/skills"),
|
|
18
|
+
packageName: "@canonical/anatomy-dsl",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
dir: join(TMP_ROOT, "node_modules/@canonical/pragma-cli/skills"),
|
|
22
|
+
packageName: "@canonical/pragma-cli",
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function writeSkillMd(
|
|
27
|
+
source: SkillSource,
|
|
28
|
+
skillName: string,
|
|
29
|
+
content: string,
|
|
30
|
+
): void {
|
|
31
|
+
const dir = join(source.dir, skillName);
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
writeFileSync(join(dir, "SKILL.md"), content);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
beforeAll(() => {
|
|
37
|
+
mkdirSync(TMP_ROOT, { recursive: true });
|
|
38
|
+
|
|
39
|
+
writeSkillMd(
|
|
40
|
+
TEST_SOURCES[0],
|
|
41
|
+
"design-audit",
|
|
42
|
+
`---
|
|
43
|
+
name: design-audit
|
|
44
|
+
description: Audit a component against DS specs
|
|
45
|
+
metadata:
|
|
46
|
+
author: canonical
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# Design Audit
|
|
50
|
+
`,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
writeSkillMd(
|
|
54
|
+
TEST_SOURCES[0],
|
|
55
|
+
"ds-review",
|
|
56
|
+
`---
|
|
57
|
+
name: ds-review
|
|
58
|
+
description: Review a PR for DS compliance
|
|
59
|
+
---
|
|
60
|
+
`,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
writeSkillMd(
|
|
64
|
+
TEST_SOURCES[2],
|
|
65
|
+
"scaffold-story",
|
|
66
|
+
`---
|
|
67
|
+
name: scaffold-story
|
|
68
|
+
description: Generate a Storybook story from anatomy
|
|
69
|
+
---
|
|
70
|
+
`,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// Invalid frontmatter — should be skipped with warning
|
|
74
|
+
writeSkillMd(
|
|
75
|
+
TEST_SOURCES[2],
|
|
76
|
+
"broken-skill",
|
|
77
|
+
`---
|
|
78
|
+
name: broken-skill
|
|
79
|
+
---
|
|
80
|
+
`,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// File without SKILL.md — should be skipped
|
|
84
|
+
const noSkillDir = join(TEST_SOURCES[0].dir, "no-skill-md");
|
|
85
|
+
mkdirSync(noSkillDir, { recursive: true });
|
|
86
|
+
writeFileSync(join(noSkillDir, "README.md"), "Not a skill");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
afterAll(() => {
|
|
90
|
+
rmSync(TMP_ROOT, { recursive: true, force: true });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("discoverSkills", () => {
|
|
94
|
+
it("discovers skills from available sources", async () => {
|
|
95
|
+
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
96
|
+
const names = skills.map((s) => s.name);
|
|
97
|
+
expect(names).toContain("design-audit");
|
|
98
|
+
expect(names).toContain("ds-review");
|
|
99
|
+
expect(names).toContain("scaffold-story");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("skips missing source directories", async () => {
|
|
103
|
+
// TEST_SOURCES[1] (anatomy-dsl) was not created
|
|
104
|
+
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
105
|
+
const packages = [...new Set(skills.map((s) => s.sourcePackage))];
|
|
106
|
+
expect(packages).not.toContain("@canonical/anatomy-dsl");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("skips folders without SKILL.md", async () => {
|
|
110
|
+
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
111
|
+
const names = skills.map((s) => s.name);
|
|
112
|
+
expect(names).not.toContain("no-skill-md");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("skips skills with invalid frontmatter", async () => {
|
|
116
|
+
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
117
|
+
const names = skills.map((s) => s.name);
|
|
118
|
+
expect(names).not.toContain("broken-skill");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("populates source metadata correctly", async () => {
|
|
122
|
+
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
123
|
+
const audit = skills.find((s) => s.name === "design-audit");
|
|
124
|
+
expect(audit?.sourcePackage).toBe("@canonical/design-system");
|
|
125
|
+
expect(audit?.folderName).toBe("design-audit");
|
|
126
|
+
expect(audit?.sourcePath).toBe(join(TEST_SOURCES[0].dir, "design-audit"));
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("preserves frontmatter metadata", async () => {
|
|
130
|
+
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
131
|
+
const audit = skills.find((s) => s.name === "design-audit");
|
|
132
|
+
expect(audit?.frontmatter.metadata).toEqual({ author: "canonical" });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("returns empty array when no sources available", async () => {
|
|
136
|
+
const emptySources: SkillSource[] = [
|
|
137
|
+
{
|
|
138
|
+
dir: "/nonexistent/path/skills",
|
|
139
|
+
packageName: "@canonical/nope",
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
const skills = await discoverSkills("/nonexistent", emptySources);
|
|
143
|
+
expect(skills).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
parseFrontmatter,
|
|
5
|
+
resolveSkillSources,
|
|
6
|
+
type SkillSource,
|
|
7
|
+
} from "../helpers/index.js";
|
|
8
|
+
import type { DiscoveredSkill } from "../types.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Discover skills by scanning source package directories for subdirectories
|
|
12
|
+
* containing a valid `SKILL.md` file. Missing source paths and invalid
|
|
13
|
+
* frontmatter are skipped gracefully.
|
|
14
|
+
*
|
|
15
|
+
* @param _cwd - Working directory (unused, sources are resolved globally).
|
|
16
|
+
* @param overrideSources - Optional override for skill source definitions.
|
|
17
|
+
* @returns Array of discovered skills with parsed frontmatter.
|
|
18
|
+
* @note Impure
|
|
19
|
+
*/
|
|
20
|
+
export default async function discoverSkills(
|
|
21
|
+
_cwd: string,
|
|
22
|
+
overrideSources?: SkillSource[],
|
|
23
|
+
): Promise<DiscoveredSkill[]> {
|
|
24
|
+
const skills: DiscoveredSkill[] = [];
|
|
25
|
+
const sources = overrideSources ?? resolveSkillSources();
|
|
26
|
+
|
|
27
|
+
for (const source of sources) {
|
|
28
|
+
let entries: string[];
|
|
29
|
+
try {
|
|
30
|
+
entries = await readdir(source.dir);
|
|
31
|
+
} catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
const entryPath = resolve(source.dir, entry);
|
|
37
|
+
const entryStat = await stat(entryPath).catch(() => null);
|
|
38
|
+
if (!entryStat?.isDirectory()) continue;
|
|
39
|
+
|
|
40
|
+
const skillMdPath = resolve(entryPath, "SKILL.md");
|
|
41
|
+
let content: string;
|
|
42
|
+
try {
|
|
43
|
+
content = await readFile(skillMdPath, "utf-8");
|
|
44
|
+
} catch {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const frontmatter = parseFrontmatter(content);
|
|
49
|
+
if (!frontmatter) {
|
|
50
|
+
process.stderr.write(
|
|
51
|
+
`Warning: invalid SKILL.md frontmatter in ${skillMdPath}\n`,
|
|
52
|
+
);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
skills.push({
|
|
57
|
+
name: frontmatter.name,
|
|
58
|
+
description: frontmatter.description,
|
|
59
|
+
sourcePath: entryPath,
|
|
60
|
+
sourcePackage: source.packageName,
|
|
61
|
+
folderName: entry,
|
|
62
|
+
frontmatter,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return skills;
|
|
68
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
5
|
+
import type { SkillSource } from "../helpers/index.js";
|
|
6
|
+
import listSkills from "./list.js";
|
|
7
|
+
|
|
8
|
+
const TMP_ROOT = join(tmpdir(), `pragma-skill-list-${Date.now()}`);
|
|
9
|
+
|
|
10
|
+
const TEST_SOURCES: SkillSource[] = [
|
|
11
|
+
{
|
|
12
|
+
dir: join(TMP_ROOT, "node_modules/@canonical/design-system/skills"),
|
|
13
|
+
packageName: "@canonical/design-system",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
dir: join(TMP_ROOT, "node_modules/@canonical/anatomy-dsl/skills"),
|
|
17
|
+
packageName: "@canonical/anatomy-dsl",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
dir: join(TMP_ROOT, "node_modules/@canonical/pragma-cli/skills"),
|
|
21
|
+
packageName: "@canonical/pragma-cli",
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
beforeAll(() => {
|
|
26
|
+
mkdirSync(TMP_ROOT, { recursive: true });
|
|
27
|
+
|
|
28
|
+
const skillDir = join(TEST_SOURCES[0].dir, "design-audit");
|
|
29
|
+
mkdirSync(skillDir, { recursive: true });
|
|
30
|
+
writeFileSync(
|
|
31
|
+
join(skillDir, "SKILL.md"),
|
|
32
|
+
`---
|
|
33
|
+
name: design-audit
|
|
34
|
+
description: Audit a component against DS specs
|
|
35
|
+
---
|
|
36
|
+
`,
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterAll(() => {
|
|
41
|
+
rmSync(TMP_ROOT, { recursive: true, force: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("listSkills", () => {
|
|
45
|
+
it("returns discovered skills", async () => {
|
|
46
|
+
const { skills } = await listSkills(TMP_ROOT, TEST_SOURCES);
|
|
47
|
+
expect(skills).toHaveLength(1);
|
|
48
|
+
expect(skills[0].name).toBe("design-audit");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("reports source availability", async () => {
|
|
52
|
+
const { sources } = await listSkills(TMP_ROOT, TEST_SOURCES);
|
|
53
|
+
const ds = sources.find(
|
|
54
|
+
(s) => s.packageName === "@canonical/design-system",
|
|
55
|
+
);
|
|
56
|
+
expect(ds?.available).toBe(true);
|
|
57
|
+
|
|
58
|
+
const anatomyDsl = sources.find(
|
|
59
|
+
(s) => s.packageName === "@canonical/anatomy-dsl",
|
|
60
|
+
);
|
|
61
|
+
expect(anatomyDsl?.available).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("includes all three sources in result", async () => {
|
|
65
|
+
const { sources } = await listSkills(TMP_ROOT, TEST_SOURCES);
|
|
66
|
+
expect(sources).toHaveLength(3);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import {
|
|
3
|
+
resolveSkillSources,
|
|
4
|
+
type SkillSource as SkillSourceDef,
|
|
5
|
+
} from "../helpers/index.js";
|
|
6
|
+
import type { DiscoveredSkill, SkillSource } from "../types.js";
|
|
7
|
+
import discoverSkills from "./discover.js";
|
|
8
|
+
|
|
9
|
+
/** Result of listing skills, including source availability metadata. */
|
|
10
|
+
export interface SkillListResult {
|
|
11
|
+
readonly skills: readonly DiscoveredSkill[];
|
|
12
|
+
readonly sources: readonly SkillSource[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* List all discovered skills along with source availability metadata.
|
|
17
|
+
*
|
|
18
|
+
* Wraps {@link discoverSkills} and enriches the result with per-source
|
|
19
|
+
* availability flags by checking the filesystem.
|
|
20
|
+
*
|
|
21
|
+
* @param cwd - Working directory.
|
|
22
|
+
* @param overrideSources - Optional override for skill source definitions.
|
|
23
|
+
* @returns Skills and source availability information.
|
|
24
|
+
* @note Impure
|
|
25
|
+
*/
|
|
26
|
+
export default async function listSkills(
|
|
27
|
+
cwd: string,
|
|
28
|
+
overrideSources?: SkillSourceDef[],
|
|
29
|
+
): Promise<SkillListResult> {
|
|
30
|
+
const resolved = overrideSources ?? resolveSkillSources();
|
|
31
|
+
const skills = await discoverSkills(cwd, resolved);
|
|
32
|
+
|
|
33
|
+
const sources: SkillSource[] = await Promise.all(
|
|
34
|
+
resolved.map(async (source) => {
|
|
35
|
+
const available = await stat(source.dir)
|
|
36
|
+
.then((s) => s.isDirectory())
|
|
37
|
+
.catch(() => false);
|
|
38
|
+
return {
|
|
39
|
+
path: source.dir,
|
|
40
|
+
packageName: source.packageName,
|
|
41
|
+
available,
|
|
42
|
+
};
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return { skills, sources };
|
|
47
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** YAML frontmatter extracted from a SKILL.md file per the agentskills.io spec. */
|
|
2
|
+
export interface SkillFrontmatter {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly description: string;
|
|
5
|
+
readonly license?: string;
|
|
6
|
+
readonly compatibility?: readonly string[];
|
|
7
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** A skill discovered from a hardcoded source path. */
|
|
11
|
+
export interface DiscoveredSkill {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly description: string;
|
|
14
|
+
readonly sourcePath: string;
|
|
15
|
+
readonly sourcePackage: string;
|
|
16
|
+
readonly folderName: string;
|
|
17
|
+
readonly frontmatter: SkillFrontmatter;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Availability metadata about a skill source entry. */
|
|
21
|
+
export interface SkillSource {
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly packageName: string;
|
|
24
|
+
readonly available: boolean;
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CommandDefinition } from "@canonical/cli-core";
|
|
2
|
+
import type { Store } from "@canonical/ke";
|
|
3
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
4
|
+
import { createTestStore, DS_ALL_TTL } from "#testing";
|
|
5
|
+
import type { PragmaContext } from "../../shared/context.js";
|
|
6
|
+
import buildCategoriesCommand from "./categories.js";
|
|
7
|
+
|
|
8
|
+
let store: Store;
|
|
9
|
+
let cleanup: () => void;
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
const result = await createTestStore({ ttl: DS_ALL_TTL });
|
|
13
|
+
store = result.store;
|
|
14
|
+
cleanup = result.cleanup;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(() => cleanup());
|
|
18
|
+
|
|
19
|
+
function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
|
|
20
|
+
return {
|
|
21
|
+
cwd: "/tmp",
|
|
22
|
+
globalFlags: { llm: false, format: "text" as const, verbose: false },
|
|
23
|
+
store,
|
|
24
|
+
config: { tier: undefined, channel: "prerelease" },
|
|
25
|
+
...overrides,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function executeOutput(
|
|
30
|
+
cmd: CommandDefinition,
|
|
31
|
+
params: Record<string, unknown>,
|
|
32
|
+
ctx: PragmaContext,
|
|
33
|
+
): Promise<{ value: unknown; text: string }> {
|
|
34
|
+
const result = await cmd.execute(params, ctx);
|
|
35
|
+
expect(result.tag).toBe("output");
|
|
36
|
+
if (result.tag !== "output") throw new Error("Expected output result");
|
|
37
|
+
const text = result.render.plain(result.value);
|
|
38
|
+
return { value: result.value, text };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("buildCategoriesCommand", () => {
|
|
42
|
+
it("returns categories with counts", async () => {
|
|
43
|
+
const ctx = makeCtx();
|
|
44
|
+
const cmd = buildCategoriesCommand(ctx);
|
|
45
|
+
const { text } = await executeOutput(cmd, {}, ctx);
|
|
46
|
+
expect(text).toContain("react (2 standards)");
|
|
47
|
+
expect(text).toContain("code (1 standard)");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("renders LLM format with --llm", async () => {
|
|
51
|
+
const ctx = makeCtx({
|
|
52
|
+
globalFlags: { llm: true, format: "text" as const, verbose: false },
|
|
53
|
+
});
|
|
54
|
+
const cmd = buildCategoriesCommand(ctx);
|
|
55
|
+
const { text } = await executeOutput(cmd, {}, ctx);
|
|
56
|
+
expect(text).toContain("## Standard Categories");
|
|
57
|
+
expect(text).toContain("**react**");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("renders JSON output", async () => {
|
|
61
|
+
const ctx = makeCtx({
|
|
62
|
+
globalFlags: { llm: false, format: "json" as const, verbose: false },
|
|
63
|
+
});
|
|
64
|
+
const cmd = buildCategoriesCommand(ctx);
|
|
65
|
+
const { text } = await executeOutput(cmd, {}, ctx);
|
|
66
|
+
const parsed = JSON.parse(text);
|
|
67
|
+
expect(Array.isArray(parsed)).toBe(true);
|
|
68
|
+
expect(parsed.some((c: { name: string }) => c.name === "react")).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires the `pragma standard categories` CLI command.
|
|
3
|
+
*
|
|
4
|
+
* Lists all standard categories with their standard counts. Throws a
|
|
5
|
+
* structured recovery error when no categories are found.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type CommandDefinition,
|
|
10
|
+
type CommandResult,
|
|
11
|
+
createOutputResult,
|
|
12
|
+
} from "@canonical/cli-core";
|
|
13
|
+
import { PragmaError } from "#error";
|
|
14
|
+
import type { PragmaContext } from "../../shared/context.js";
|
|
15
|
+
import { selectFormatter } from "../../shared/formatters.js";
|
|
16
|
+
import { categoriesFormatters } from "../formatters/index.js";
|
|
17
|
+
import { listCategories } from "../operations/index.js";
|
|
18
|
+
|
|
19
|
+
export default function buildCategoriesCommand(
|
|
20
|
+
ctx: PragmaContext,
|
|
21
|
+
): CommandDefinition {
|
|
22
|
+
return {
|
|
23
|
+
path: ["standard", "categories"],
|
|
24
|
+
description: "List all standard categories with counts",
|
|
25
|
+
parameters: [],
|
|
26
|
+
meta: {
|
|
27
|
+
examples: ["pragma standard categories"],
|
|
28
|
+
},
|
|
29
|
+
execute: async (
|
|
30
|
+
_params: Record<string, unknown>,
|
|
31
|
+
): Promise<CommandResult> => {
|
|
32
|
+
const categories = await listCategories(ctx.store);
|
|
33
|
+
|
|
34
|
+
if (categories.length === 0) {
|
|
35
|
+
throw PragmaError.emptyResults("categories", {
|
|
36
|
+
recovery: {
|
|
37
|
+
message:
|
|
38
|
+
"Ensure code standards packages are installed: bun add -D @canonical/code-standards",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return createOutputResult(categories, {
|
|
44
|
+
plain: selectFormatter(ctx, categoriesFormatters),
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { CommandDefinition } from "@canonical/cli-core";
|
|
2
|
+
import type { Store } from "@canonical/ke";
|
|
3
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
4
|
+
import { PragmaError } from "#error";
|
|
5
|
+
import { createTestStore, DS_ALL_TTL } from "#testing";
|
|
6
|
+
import type { PragmaContext } from "../../shared/context.js";
|
|
7
|
+
import buildListCommand from "./list.js";
|
|
8
|
+
|
|
9
|
+
let store: Store;
|
|
10
|
+
let cleanup: () => void;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const result = await createTestStore({ ttl: DS_ALL_TTL });
|
|
14
|
+
store = result.store;
|
|
15
|
+
cleanup = result.cleanup;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterAll(() => cleanup());
|
|
19
|
+
|
|
20
|
+
function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
|
|
21
|
+
return {
|
|
22
|
+
cwd: "/tmp",
|
|
23
|
+
globalFlags: { llm: false, format: "text" as const, verbose: false },
|
|
24
|
+
store,
|
|
25
|
+
config: { tier: undefined, channel: "prerelease" },
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function executeOutput(
|
|
31
|
+
cmd: CommandDefinition,
|
|
32
|
+
params: Record<string, unknown>,
|
|
33
|
+
ctx: PragmaContext,
|
|
34
|
+
): Promise<{ value: unknown; text: string }> {
|
|
35
|
+
const result = await cmd.execute(params, ctx);
|
|
36
|
+
expect(result.tag).toBe("output");
|
|
37
|
+
if (result.tag !== "output") throw new Error("Expected output result");
|
|
38
|
+
const text = result.render.plain(result.value);
|
|
39
|
+
return { value: result.value, text };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("buildListCommand", () => {
|
|
43
|
+
it("returns output result with all standards", async () => {
|
|
44
|
+
const ctx = makeCtx();
|
|
45
|
+
const cmd = buildListCommand(ctx);
|
|
46
|
+
const { text } = await executeOutput(cmd, {}, ctx);
|
|
47
|
+
expect(text).toContain("react/component/folder-structure");
|
|
48
|
+
expect(text).toContain("react/component/props");
|
|
49
|
+
expect(text).toContain("code/function/purity");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("filters by --category", async () => {
|
|
53
|
+
const ctx = makeCtx();
|
|
54
|
+
const cmd = buildListCommand(ctx);
|
|
55
|
+
const { text } = await executeOutput(cmd, { category: "react" }, ctx);
|
|
56
|
+
expect(text).toContain("react/component/folder-structure");
|
|
57
|
+
expect(text).toContain("react/component/props");
|
|
58
|
+
expect(text).not.toContain("code/function/purity");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("filters by --search", async () => {
|
|
62
|
+
const ctx = makeCtx();
|
|
63
|
+
const cmd = buildListCommand(ctx);
|
|
64
|
+
const { text } = await executeOutput(cmd, { search: "folder" }, ctx);
|
|
65
|
+
expect(text).toContain("react/component/folder-structure");
|
|
66
|
+
expect(text).not.toContain("code/function/purity");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("throws EMPTY_RESULTS for non-matching filter", async () => {
|
|
70
|
+
const ctx = makeCtx();
|
|
71
|
+
const cmd = buildListCommand(ctx);
|
|
72
|
+
try {
|
|
73
|
+
await cmd.execute({ category: "nonexistent" }, ctx);
|
|
74
|
+
expect.fail("Should have thrown");
|
|
75
|
+
} catch (e) {
|
|
76
|
+
expect(e).toBeInstanceOf(PragmaError);
|
|
77
|
+
expect((e as PragmaError).code).toBe("EMPTY_RESULTS");
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("renders LLM format with --llm", async () => {
|
|
82
|
+
const ctx = makeCtx({
|
|
83
|
+
globalFlags: { llm: true, format: "text" as const, verbose: false },
|
|
84
|
+
});
|
|
85
|
+
const cmd = buildListCommand(ctx);
|
|
86
|
+
const { text } = await executeOutput(cmd, {}, ctx);
|
|
87
|
+
expect(text).toContain("## Standards");
|
|
88
|
+
expect(text).toContain("**react/component/folder-structure**");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("renders JSON output", async () => {
|
|
92
|
+
const ctx = makeCtx({
|
|
93
|
+
globalFlags: { llm: false, format: "json" as const, verbose: false },
|
|
94
|
+
});
|
|
95
|
+
const cmd = buildListCommand(ctx);
|
|
96
|
+
const { text } = await executeOutput(cmd, {}, ctx);
|
|
97
|
+
const parsed = JSON.parse(text);
|
|
98
|
+
expect(Array.isArray(parsed)).toBe(true);
|
|
99
|
+
expect(
|
|
100
|
+
parsed.some(
|
|
101
|
+
(s: { name: string }) => s.name === "react/component/folder-structure",
|
|
102
|
+
),
|
|
103
|
+
).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wires the `pragma standard list` CLI command.
|
|
3
|
+
*
|
|
4
|
+
* Lists all code standards with optional category and search filters.
|
|
5
|
+
* Supports progressive disclosure via `--digest` (summary + first example)
|
|
6
|
+
* and `--detailed` (full dos/donts) flags.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
type CommandDefinition,
|
|
11
|
+
type CommandResult,
|
|
12
|
+
createOutputResult,
|
|
13
|
+
} from "@canonical/cli-core";
|
|
14
|
+
import type { PragmaContext } from "../../shared/context.js";
|
|
15
|
+
import { selectFormatter } from "../../shared/formatters.js";
|
|
16
|
+
import { listFormatters } from "../formatters/index.js";
|
|
17
|
+
import type { StandardListOutput } from "../formatters/types.js";
|
|
18
|
+
import { resolveStandardList } from "../orchestration/index.js";
|
|
19
|
+
|
|
20
|
+
export default function buildListCommand(
|
|
21
|
+
ctx: PragmaContext,
|
|
22
|
+
): CommandDefinition {
|
|
23
|
+
return {
|
|
24
|
+
path: ["standard", "list"],
|
|
25
|
+
description: "List all code standards",
|
|
26
|
+
parameters: [
|
|
27
|
+
{
|
|
28
|
+
name: "category",
|
|
29
|
+
description: "Filter by category",
|
|
30
|
+
type: "string",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "search",
|
|
34
|
+
description: "Search in name and description",
|
|
35
|
+
type: "string",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "digest",
|
|
39
|
+
description: "Show description and first example for each standard",
|
|
40
|
+
type: "boolean",
|
|
41
|
+
default: false,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "detailed",
|
|
45
|
+
description: "Show full dos/donts for each standard",
|
|
46
|
+
type: "boolean",
|
|
47
|
+
default: false,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
meta: {
|
|
51
|
+
examples: [
|
|
52
|
+
"pragma standard list",
|
|
53
|
+
"pragma standard list --category react",
|
|
54
|
+
'pragma standard list --search "folder"',
|
|
55
|
+
"pragma standard list --digest",
|
|
56
|
+
"pragma standard list --detailed",
|
|
57
|
+
"pragma standard list --llm",
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
execute: async (
|
|
61
|
+
params: Record<string, unknown>,
|
|
62
|
+
): Promise<CommandResult> => {
|
|
63
|
+
const contract = await resolveStandardList(ctx, {
|
|
64
|
+
category: params.category as string | undefined,
|
|
65
|
+
search: params.search as string | undefined,
|
|
66
|
+
digest: params.digest === true,
|
|
67
|
+
detailed: params.detailed === true,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const output: StandardListOutput = {
|
|
71
|
+
items: contract.items,
|
|
72
|
+
details: contract.details,
|
|
73
|
+
disclosure: contract.disclosure,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return createOutputResult(output, {
|
|
77
|
+
plain: selectFormatter(ctx, listFormatters),
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|