@canonical/pragma-cli 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -47
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/defaultConfig.test.ts +33 -0
- package/src/config/defaultConfig.ts +59 -0
- package/src/config/defaults.json +17 -0
- package/src/config/findProjectConfigPath.test.ts +53 -0
- package/src/config/findProjectConfigPath.ts +36 -0
- package/src/config/index.ts +17 -2
- package/src/config/mergeConfigFileUpdate.ts +58 -0
- package/src/config/parseConfigValues.ts +217 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -153
- package/src/config/readConfigLayers.test.ts +109 -0
- package/src/config/readConfigLayers.ts +118 -0
- package/src/config/resolveGlobalConfigPath.ts +13 -0
- package/src/config/resolveWriteConfigPath.test.ts +53 -0
- package/src/config/resolveWriteConfigPath.ts +31 -0
- package/src/config/types.ts +83 -1
- package/src/config/writeConfig.ts +25 -55
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/list.ts +5 -90
- package/src/domains/block/commands/lookup.ts +8 -167
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/mcp/specs.ts +16 -196
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +260 -0
- package/src/domains/config/commands/channel.test.ts +30 -3
- package/src/domains/config/commands/channel.ts +25 -8
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +32 -1
- package/src/domains/config/commands/framework.ts +26 -8
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +20 -2
- package/src/domains/config/commands/tier.ts +25 -8
- package/src/domains/config/commands/trace.test.ts +19 -2
- package/src/domains/config/commands/trace.ts +6 -3
- package/src/domains/config/formatters/channel.test.ts +20 -6
- package/src/domains/config/formatters/channel.ts +14 -11
- package/src/domains/config/formatters/framework.ts +14 -11
- package/src/domains/config/formatters/show.test.ts +27 -0
- package/src/domains/config/formatters/show.ts +27 -4
- package/src/domains/config/formatters/tier.test.ts +26 -10
- package/src/domains/config/formatters/tier.ts +19 -11
- package/src/domains/config/formatters/trace.ts +7 -7
- package/src/domains/config/mcp/specs.ts +33 -53
- package/src/domains/config/operations/show.test.ts +18 -0
- package/src/domains/config/operations/show.ts +7 -1
- package/src/domains/config/operations/types.ts +7 -0
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +1 -0
- package/src/domains/config/tasks/setChannel.ts +13 -28
- package/src/domains/config/tasks/setFramework.ts +11 -30
- package/src/domains/config/tasks/setTier.ts +10 -29
- package/src/domains/config/tasks/setTrace.ts +10 -28
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +133 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +32 -26
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
- package/src/domains/doctor/operations/checks/index.ts +1 -0
- package/src/domains/doctor/operations/checks.test.ts +160 -7
- package/src/domains/doctor/operations/runChecks.test.ts +15 -6
- package/src/domains/doctor/operations/runChecks.ts +2 -0
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/build.test.ts +5 -3
- package/src/domains/graphql/commands/build.ts +8 -16
- package/src/domains/graphql/commands/check.test.ts +5 -3
- package/src/domains/graphql/commands/check.ts +7 -13
- package/src/domains/graphql/commands/index.ts +1 -0
- package/src/domains/graphql/commands/serve.test.ts +46 -0
- package/src/domains/graphql/commands/serve.ts +141 -0
- package/src/domains/graphql/constants.ts +10 -0
- package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
- package/src/domains/graphql/helpers/gatherSources.ts +58 -0
- package/src/domains/graphql/helpers/index.ts +2 -0
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
- package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
- package/src/domains/graphql/index.ts +9 -6
- package/src/domains/graphql/operations/compileSchema.test.ts +23 -47
- package/src/domains/graphql/operations/compileSchema.ts +23 -25
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +10 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/collectInfo.test.ts +14 -3
- package/src/domains/info/operations/collectInfo.ts +12 -15
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +24 -4
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +55 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/list.ts +5 -38
- package/src/domains/modifier/commands/lookup.ts +8 -79
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/mcp/specs.ts +15 -94
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +86 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/commands/show.ts +6 -58
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +361 -64
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/mcp/specs.ts +7 -84
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.test.ts +36 -0
- package/src/domains/ontology/stories.ts +153 -0
- package/src/domains/refs/operations/updateRefs.ts +2 -38
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/commands/skills.test.ts +6 -2
- package/src/domains/setup/commands/skills.ts +6 -2
- package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
- package/src/domains/setup/helpers/runSetupTask.ts +62 -70
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/setup/operations/setupMcp.test.ts +6 -1
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +57 -18
- package/src/domains/shared/compactUri.test.ts +11 -0
- package/src/domains/shared/compactUri.ts +10 -5
- package/src/domains/shared/context.ts +2 -2
- package/src/domains/shared/contracts.ts +11 -0
- package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
- package/src/domains/shared/createInteractivePromptSession.ts +236 -0
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +158 -12
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/index.ts +4 -1
- package/src/domains/shared/loaders/localLoader.ts +2 -4
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +82 -9
- package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
- package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
- package/src/domains/shared/lookupMany.test.ts +135 -0
- package/src/domains/shared/lookupMany.ts +50 -2
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/packages.ts +10 -17
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/runtime.ts +4 -63
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +34 -3
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +102 -0
- package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
- package/src/domains/shared/stories/compileLookupTool.ts +96 -0
- package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
- package/src/domains/shared/stories/compileReadCommand.ts +48 -0
- package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
- package/src/domains/shared/stories/compileReadTool.ts +38 -0
- package/src/domains/shared/stories/condense.test.ts +18 -0
- package/src/domains/shared/stories/condense.ts +15 -0
- package/src/domains/shared/stories/index.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.ts +22 -0
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +151 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +111 -0
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +81 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +154 -0
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +406 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +158 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +50 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +751 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +724 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +55 -0
- package/src/domains/shared/stories/pack/index.ts +42 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +381 -0
- package/src/domains/shared/stories/pack/types.ts +224 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +742 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +859 -0
- package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
- package/src/domains/shared/stories/projectCliParameters.ts +57 -0
- package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
- package/src/domains/shared/stories/projectToolParams.ts +32 -0
- package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
- package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
- package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
- package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
- package/src/domains/shared/stories/types.ts +179 -0
- package/src/domains/shared/suggestions/expandGlob.ts +1 -0
- package/src/domains/shared/suggestions/suggestNames.ts +43 -10
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/skill/commands/index.ts +3 -1
- package/src/domains/skill/commands/list.test.ts +6 -2
- package/src/domains/skill/commands/list.ts +5 -63
- package/src/domains/skill/commands/lookup.test.ts +118 -0
- package/src/domains/skill/commands/lookup.ts +14 -0
- package/src/domains/skill/formatters/index.ts +1 -0
- package/src/domains/skill/formatters/lookup.test.ts +46 -0
- package/src/domains/skill/formatters/lookup.ts +57 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +0 -1
- package/src/domains/skill/index.ts +7 -3
- package/src/domains/skill/mcp/specs.ts +10 -29
- package/src/domains/skill/operations/discover.test.ts +61 -30
- package/src/domains/skill/operations/discover.ts +28 -37
- package/src/domains/skill/operations/index.ts +2 -1
- package/src/domains/skill/operations/list.test.ts +15 -7
- package/src/domains/skill/operations/lookup.test.ts +91 -0
- package/src/domains/skill/operations/lookup.ts +70 -0
- package/src/domains/skill/stories.test.ts +43 -0
- package/src/domains/skill/stories.ts +103 -0
- package/src/domains/skill/types.ts +8 -0
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +18 -2
- package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/domains/trace/tracePlugin.test.ts +1 -1
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +382 -74
- package/src/mcp/registerResources.ts +5 -348
- package/src/mcp/resources/buildGraphIndex.ts +167 -0
- package/src/mcp/resources/buildResourceList.test.ts +97 -0
- package/src/mcp/resources/buildResourceList.ts +176 -0
- package/src/mcp/resources/classifyEntity.test.ts +40 -0
- package/src/mcp/resources/classifyEntity.ts +27 -0
- package/src/mcp/resources/constants.ts +105 -0
- package/src/mcp/resources/displayPredicates.ts +62 -0
- package/src/mcp/resources/pickFirstValue.test.ts +29 -0
- package/src/mcp/resources/pickFirstValue.ts +26 -0
- package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
- package/src/mcp/resources/pickPrimaryType.ts +26 -0
- package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
- package/src/mcp/resources/rankUriCompletions.ts +64 -0
- package/src/mcp/resources/readEntity.ts +169 -0
- package/src/mcp/resources/registerResources.ts +126 -0
- package/src/mcp/resources/types.ts +128 -0
- package/src/mcp/tools/index.ts +22 -5
- package/src/mcp/tools/registerTools.test.ts +167 -34
- package/src/mcp/types.ts +4 -0
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +35 -13
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +202 -25
- package/src/testing/cli.ts +30 -2
- package/src/testing/createTestMcpClient.ts +4 -1
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +7 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +103 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +136 -82
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +26 -0
- package/src/testing/standardParityFixtures.ts +35 -0
- package/src/testing/store.ts +4 -4
- package/src/testing/storyFixtures.ts +63 -0
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -90
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -99
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -244
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -32
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -59
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -38
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -7,38 +7,72 @@ import discoverSkills from "./discover.js";
|
|
|
7
7
|
|
|
8
8
|
const TMP_ROOT = join(tmpdir(), `pragma-skill-discover-${Date.now()}`);
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Test skill sources that mirror the real loader granularity: each source
|
|
12
|
+
* is one skill directory (`<package>/skills/<name>`) containing SKILL.md,
|
|
13
|
+
* as produced by `readPackageDir`.
|
|
14
|
+
*/
|
|
11
15
|
const TEST_SOURCES: SkillSource[] = [
|
|
12
16
|
{
|
|
13
|
-
dir: join(
|
|
17
|
+
dir: join(
|
|
18
|
+
TMP_ROOT,
|
|
19
|
+
"node_modules/@canonical/design-system/skills/design-audit",
|
|
20
|
+
),
|
|
14
21
|
packageName: "@canonical/design-system",
|
|
15
22
|
},
|
|
16
23
|
{
|
|
17
|
-
dir: join(
|
|
24
|
+
dir: join(
|
|
25
|
+
TMP_ROOT,
|
|
26
|
+
"node_modules/@canonical/design-system/skills/ds-review",
|
|
27
|
+
),
|
|
28
|
+
packageName: "@canonical/design-system",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
dir: join(
|
|
32
|
+
TMP_ROOT,
|
|
33
|
+
"node_modules/@canonical/anatomy-dsl/skills/anatomy-author",
|
|
34
|
+
),
|
|
18
35
|
packageName: "@canonical/anatomy-dsl",
|
|
19
36
|
},
|
|
20
37
|
{
|
|
21
|
-
dir: join(
|
|
38
|
+
dir: join(
|
|
39
|
+
TMP_ROOT,
|
|
40
|
+
"node_modules/@canonical/pragma-cli/skills/scaffold-story",
|
|
41
|
+
),
|
|
42
|
+
packageName: "@canonical/pragma-cli",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
dir: join(
|
|
46
|
+
TMP_ROOT,
|
|
47
|
+
"node_modules/@canonical/pragma-cli/skills/broken-skill",
|
|
48
|
+
),
|
|
22
49
|
packageName: "@canonical/pragma-cli",
|
|
23
50
|
},
|
|
51
|
+
{
|
|
52
|
+
dir: join(
|
|
53
|
+
TMP_ROOT,
|
|
54
|
+
"node_modules/@canonical/design-system/skills/no-skill-md",
|
|
55
|
+
),
|
|
56
|
+
packageName: "@canonical/design-system",
|
|
57
|
+
},
|
|
24
58
|
];
|
|
25
59
|
|
|
26
|
-
function writeSkillMd(
|
|
27
|
-
source:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
60
|
+
function writeSkillMd(source: SkillSource, content: string): void {
|
|
61
|
+
mkdirSync(source.dir, { recursive: true });
|
|
62
|
+
writeFileSync(join(source.dir, "SKILL.md"), content);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function sourceAt(index: number): SkillSource {
|
|
66
|
+
const source = TEST_SOURCES.at(index);
|
|
67
|
+
if (!source) throw new Error(`no test source at index ${index}`);
|
|
68
|
+
return source;
|
|
34
69
|
}
|
|
35
70
|
|
|
36
71
|
beforeAll(() => {
|
|
37
72
|
mkdirSync(TMP_ROOT, { recursive: true });
|
|
38
73
|
|
|
39
74
|
writeSkillMd(
|
|
40
|
-
|
|
41
|
-
"design-audit",
|
|
75
|
+
sourceAt(0),
|
|
42
76
|
`---
|
|
43
77
|
name: design-audit
|
|
44
78
|
description: Audit a component against DS specs
|
|
@@ -51,8 +85,7 @@ metadata:
|
|
|
51
85
|
);
|
|
52
86
|
|
|
53
87
|
writeSkillMd(
|
|
54
|
-
|
|
55
|
-
"ds-review",
|
|
88
|
+
sourceAt(1),
|
|
56
89
|
`---
|
|
57
90
|
name: ds-review
|
|
58
91
|
description: Review a PR for DS compliance
|
|
@@ -61,8 +94,7 @@ description: Review a PR for DS compliance
|
|
|
61
94
|
);
|
|
62
95
|
|
|
63
96
|
writeSkillMd(
|
|
64
|
-
|
|
65
|
-
"scaffold-story",
|
|
97
|
+
sourceAt(3),
|
|
66
98
|
`---
|
|
67
99
|
name: scaffold-story
|
|
68
100
|
description: Generate a Storybook story from anatomy
|
|
@@ -70,20 +102,19 @@ description: Generate a Storybook story from anatomy
|
|
|
70
102
|
`,
|
|
71
103
|
);
|
|
72
104
|
|
|
73
|
-
// Invalid frontmatter — should be skipped with warning
|
|
105
|
+
// Invalid frontmatter (missing description) — should be skipped with warning
|
|
74
106
|
writeSkillMd(
|
|
75
|
-
|
|
76
|
-
"broken-skill",
|
|
107
|
+
sourceAt(4),
|
|
77
108
|
`---
|
|
78
109
|
name: broken-skill
|
|
79
110
|
---
|
|
80
111
|
`,
|
|
81
112
|
);
|
|
82
113
|
|
|
83
|
-
//
|
|
84
|
-
const
|
|
85
|
-
mkdirSync(
|
|
86
|
-
writeFileSync(join(
|
|
114
|
+
// Source directory without a SKILL.md — should be skipped
|
|
115
|
+
const noSkillMd = sourceAt(5);
|
|
116
|
+
mkdirSync(noSkillMd.dir, { recursive: true });
|
|
117
|
+
writeFileSync(join(noSkillMd.dir, "README.md"), "Not a skill");
|
|
87
118
|
});
|
|
88
119
|
|
|
89
120
|
afterAll(() => {
|
|
@@ -100,15 +131,15 @@ describe("discoverSkills", () => {
|
|
|
100
131
|
});
|
|
101
132
|
|
|
102
133
|
it("skips missing source directories", async () => {
|
|
103
|
-
// TEST_SOURCES[
|
|
134
|
+
// TEST_SOURCES[2] (anatomy-dsl) was never created on disk
|
|
104
135
|
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
105
136
|
const packages = [...new Set(skills.map((s) => s.sourcePackage))];
|
|
106
137
|
expect(packages).not.toContain("@canonical/anatomy-dsl");
|
|
107
138
|
});
|
|
108
139
|
|
|
109
|
-
it("skips
|
|
140
|
+
it("skips source directories without SKILL.md", async () => {
|
|
110
141
|
const skills = await discoverSkills(TMP_ROOT, TEST_SOURCES);
|
|
111
|
-
const names = skills.map((s) => s.
|
|
142
|
+
const names = skills.map((s) => s.folderName);
|
|
112
143
|
expect(names).not.toContain("no-skill-md");
|
|
113
144
|
});
|
|
114
145
|
|
|
@@ -123,7 +154,7 @@ describe("discoverSkills", () => {
|
|
|
123
154
|
const audit = skills.find((s) => s.name === "design-audit");
|
|
124
155
|
expect(audit?.sourcePackage).toBe("@canonical/design-system");
|
|
125
156
|
expect(audit?.folderName).toBe("design-audit");
|
|
126
|
-
expect(audit?.sourcePath).toBe(
|
|
157
|
+
expect(audit?.sourcePath).toBe(sourceAt(0).dir);
|
|
127
158
|
});
|
|
128
159
|
|
|
129
160
|
it("preserves frontmatter metadata", async () => {
|
|
@@ -135,7 +166,7 @@ describe("discoverSkills", () => {
|
|
|
135
166
|
it("returns empty array when no sources available", async () => {
|
|
136
167
|
const emptySources: SkillSource[] = [
|
|
137
168
|
{
|
|
138
|
-
dir: "/nonexistent/path/skills",
|
|
169
|
+
dir: "/nonexistent/path/skills/some-skill",
|
|
139
170
|
packageName: "@canonical/nope",
|
|
140
171
|
},
|
|
141
172
|
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolve } from "node:path";
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { basename, resolve } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
parseFrontmatter,
|
|
5
5
|
resolveSkillSources,
|
|
@@ -8,14 +8,18 @@ import {
|
|
|
8
8
|
import type { DiscoveredSkill } from "../types.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Discover skills
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Discover skills from resolved skill source directories.
|
|
12
|
+
*
|
|
13
|
+
* Each source directory is one skill folder (`<package>/skills/<name>`)
|
|
14
|
+
* containing a `SKILL.md` with YAML frontmatter — the granularity
|
|
15
|
+
* produced by the package loaders' `readPackageDir`. Missing files and
|
|
16
|
+
* invalid frontmatter are skipped gracefully (invalid frontmatter is
|
|
17
|
+
* reported on stderr).
|
|
14
18
|
*
|
|
15
19
|
* @param _cwd - Working directory (unused, sources are resolved globally).
|
|
16
20
|
* @param overrideSources - Optional override for skill source definitions.
|
|
17
21
|
* @returns Array of discovered skills with parsed frontmatter.
|
|
18
|
-
* @note Impure
|
|
22
|
+
* @note Impure — reads SKILL.md files from the filesystem.
|
|
19
23
|
*/
|
|
20
24
|
export default async function discoverSkills(
|
|
21
25
|
_cwd: string,
|
|
@@ -25,43 +29,30 @@ export default async function discoverSkills(
|
|
|
25
29
|
const sources = overrideSources ?? resolveSkillSources();
|
|
26
30
|
|
|
27
31
|
for (const source of sources) {
|
|
28
|
-
|
|
32
|
+
const skillMdPath = resolve(source.dir, "SKILL.md");
|
|
33
|
+
let content: string;
|
|
29
34
|
try {
|
|
30
|
-
|
|
35
|
+
content = await readFile(skillMdPath, "utf-8");
|
|
31
36
|
} catch {
|
|
32
37
|
continue;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
});
|
|
40
|
+
const frontmatter = parseFrontmatter(content);
|
|
41
|
+
if (!frontmatter) {
|
|
42
|
+
process.stderr.write(
|
|
43
|
+
`Warning: invalid SKILL.md frontmatter in ${skillMdPath}\n`,
|
|
44
|
+
);
|
|
45
|
+
continue;
|
|
64
46
|
}
|
|
47
|
+
|
|
48
|
+
skills.push({
|
|
49
|
+
name: frontmatter.name,
|
|
50
|
+
description: frontmatter.description,
|
|
51
|
+
sourcePath: source.dir,
|
|
52
|
+
sourcePackage: source.packageName,
|
|
53
|
+
folderName: basename(source.dir),
|
|
54
|
+
frontmatter,
|
|
55
|
+
});
|
|
65
56
|
}
|
|
66
57
|
|
|
67
58
|
return skills;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/** @module Skill operations: discovery
|
|
1
|
+
/** @module Skill operations: discovery, listing, and content lookup. */
|
|
2
2
|
export { default as discoverSkills } from "./discover.js";
|
|
3
3
|
export type { SkillListResult } from "./list.js";
|
|
4
4
|
export { default as listSkills } from "./list.js";
|
|
5
|
+
export { default as lookupSkill } from "./lookup.js";
|
|
@@ -7,25 +7,33 @@ import listSkills from "./list.js";
|
|
|
7
7
|
|
|
8
8
|
const TMP_ROOT = join(tmpdir(), `pragma-skill-list-${Date.now()}`);
|
|
9
9
|
|
|
10
|
+
/** Each source is one skill directory, matching the loader granularity. */
|
|
10
11
|
const TEST_SOURCES: SkillSource[] = [
|
|
11
12
|
{
|
|
12
|
-
dir: join(
|
|
13
|
+
dir: join(
|
|
14
|
+
TMP_ROOT,
|
|
15
|
+
"node_modules/@canonical/design-system/skills/design-audit",
|
|
16
|
+
),
|
|
13
17
|
packageName: "@canonical/design-system",
|
|
14
18
|
},
|
|
15
19
|
{
|
|
16
|
-
dir: join(
|
|
20
|
+
dir: join(
|
|
21
|
+
TMP_ROOT,
|
|
22
|
+
"node_modules/@canonical/anatomy-dsl/skills/anatomy-author",
|
|
23
|
+
),
|
|
17
24
|
packageName: "@canonical/anatomy-dsl",
|
|
18
25
|
},
|
|
19
26
|
{
|
|
20
|
-
dir: join(
|
|
27
|
+
dir: join(
|
|
28
|
+
TMP_ROOT,
|
|
29
|
+
"node_modules/@canonical/pragma-cli/skills/scaffold-story",
|
|
30
|
+
),
|
|
21
31
|
packageName: "@canonical/pragma-cli",
|
|
22
32
|
},
|
|
23
33
|
];
|
|
24
34
|
|
|
25
35
|
beforeAll(() => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const skillDir = join(TEST_SOURCES[0].dir, "design-audit");
|
|
36
|
+
const skillDir = TEST_SOURCES.at(0)?.dir ?? "";
|
|
29
37
|
mkdirSync(skillDir, { recursive: true });
|
|
30
38
|
writeFileSync(
|
|
31
39
|
join(skillDir, "SKILL.md"),
|
|
@@ -45,7 +53,7 @@ describe("listSkills", () => {
|
|
|
45
53
|
it("returns discovered skills", async () => {
|
|
46
54
|
const { skills } = await listSkills(TMP_ROOT, TEST_SOURCES);
|
|
47
55
|
expect(skills).toHaveLength(1);
|
|
48
|
-
expect(skills
|
|
56
|
+
expect(skills.at(0)?.name).toBe("design-audit");
|
|
49
57
|
});
|
|
50
58
|
|
|
51
59
|
it("reports source availability", async () => {
|
|
@@ -0,0 +1,91 @@
|
|
|
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 { PragmaError } from "#error";
|
|
6
|
+
import type { SkillSource } from "../helpers/index.js";
|
|
7
|
+
import lookupSkill from "./lookup.js";
|
|
8
|
+
|
|
9
|
+
const TMP_ROOT = join(tmpdir(), `pragma-skill-lookup-${Date.now()}`);
|
|
10
|
+
|
|
11
|
+
/** Each source is one skill directory, matching the loader granularity. */
|
|
12
|
+
const TEST_SOURCES: SkillSource[] = [
|
|
13
|
+
{
|
|
14
|
+
dir: join(
|
|
15
|
+
TMP_ROOT,
|
|
16
|
+
"node_modules/@canonical/design-system/skills/design-auditor",
|
|
17
|
+
),
|
|
18
|
+
packageName: "@canonical/design-system",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
dir: join(
|
|
22
|
+
TMP_ROOT,
|
|
23
|
+
"node_modules/@canonical/code-standards/skills/add-standard",
|
|
24
|
+
),
|
|
25
|
+
packageName: "@canonical/code-standards",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const AUDITOR_CONTENT = `---
|
|
30
|
+
name: design-auditor
|
|
31
|
+
description: Audit design system coverage
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# Design Auditor
|
|
35
|
+
|
|
36
|
+
Full instructions here.
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
beforeAll(() => {
|
|
40
|
+
const auditor = TEST_SOURCES.at(0)?.dir ?? "";
|
|
41
|
+
mkdirSync(auditor, { recursive: true });
|
|
42
|
+
writeFileSync(join(auditor, "SKILL.md"), AUDITOR_CONTENT);
|
|
43
|
+
writeFileSync(join(auditor, "AUDIT_SPEC.md"), "# Spec");
|
|
44
|
+
|
|
45
|
+
const addStandard = TEST_SOURCES.at(1)?.dir ?? "";
|
|
46
|
+
mkdirSync(addStandard, { recursive: true });
|
|
47
|
+
writeFileSync(
|
|
48
|
+
join(addStandard, "SKILL.md"),
|
|
49
|
+
`---
|
|
50
|
+
name: add-standard
|
|
51
|
+
description: Create a new code standard
|
|
52
|
+
---
|
|
53
|
+
`,
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
afterAll(() => {
|
|
58
|
+
rmSync(TMP_ROOT, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("lookupSkill", () => {
|
|
62
|
+
it("returns the skill with full content and companion files", async () => {
|
|
63
|
+
const skill = await lookupSkill(TMP_ROOT, "design-auditor", TEST_SOURCES);
|
|
64
|
+
expect(skill.name).toBe("design-auditor");
|
|
65
|
+
expect(skill.content).toBe(AUDITOR_CONTENT);
|
|
66
|
+
expect(skill.files).toEqual(["AUDIT_SPEC.md"]);
|
|
67
|
+
expect(skill.sourcePackage).toBe("@canonical/design-system");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("matches case-insensitively", async () => {
|
|
71
|
+
const skill = await lookupSkill(TMP_ROOT, "Design-Auditor", TEST_SOURCES);
|
|
72
|
+
expect(skill.name).toBe("design-auditor");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("reports no companion files when SKILL.md is alone", async () => {
|
|
76
|
+
const skill = await lookupSkill(TMP_ROOT, "add-standard", TEST_SOURCES);
|
|
77
|
+
expect(skill.files).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("throws not-found with suggestions for unknown names", async () => {
|
|
81
|
+
await expect(
|
|
82
|
+
lookupSkill(TMP_ROOT, "design-audit", TEST_SOURCES),
|
|
83
|
+
).rejects.toMatchObject({
|
|
84
|
+
code: "ENTITY_NOT_FOUND",
|
|
85
|
+
suggestions: ["design-auditor"],
|
|
86
|
+
});
|
|
87
|
+
await expect(
|
|
88
|
+
lookupSkill(TMP_ROOT, "nope", TEST_SOURCES),
|
|
89
|
+
).rejects.toBeInstanceOf(PragmaError);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { PragmaError } from "#error";
|
|
4
|
+
import { suggestNames } from "../../shared/suggestions/index.js";
|
|
5
|
+
import type { SkillSource } from "../helpers/index.js";
|
|
6
|
+
import type { SkillDetailed } from "../types.js";
|
|
7
|
+
import discoverSkills from "./discover.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Look up one skill by name or folder name and serve its content.
|
|
11
|
+
*
|
|
12
|
+
* Matches case-insensitively against the frontmatter name and the skill's
|
|
13
|
+
* folder name, then reads the full SKILL.md text plus the names of any
|
|
14
|
+
* companion files shipped alongside it (e.g. spec documents).
|
|
15
|
+
*
|
|
16
|
+
* @param cwd - Working directory.
|
|
17
|
+
* @param query - Skill name or folder name to look up.
|
|
18
|
+
* @param overrideSources - Optional override for skill source definitions.
|
|
19
|
+
* @returns The matched skill with content and companion file names.
|
|
20
|
+
* @throws PragmaError with code ENTITY_NOT_FOUND and ranked suggestions
|
|
21
|
+
* when no skill matches.
|
|
22
|
+
* @note Impure — reads SKILL.md and the skill directory listing.
|
|
23
|
+
*/
|
|
24
|
+
export default async function lookupSkill(
|
|
25
|
+
cwd: string,
|
|
26
|
+
query: string,
|
|
27
|
+
overrideSources?: SkillSource[],
|
|
28
|
+
): Promise<SkillDetailed> {
|
|
29
|
+
const skills = await discoverSkills(cwd, overrideSources);
|
|
30
|
+
const queryLower = query.toLowerCase();
|
|
31
|
+
const match = skills.find(
|
|
32
|
+
(skill) =>
|
|
33
|
+
skill.name.toLowerCase() === queryLower ||
|
|
34
|
+
skill.folderName.toLowerCase() === queryLower,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (!match) {
|
|
38
|
+
throw PragmaError.notFound("skill", query, {
|
|
39
|
+
suggestions: suggestNames(
|
|
40
|
+
query,
|
|
41
|
+
skills.map((skill) => skill.name),
|
|
42
|
+
),
|
|
43
|
+
recovery: {
|
|
44
|
+
message: "List available skills.",
|
|
45
|
+
cli: "pragma skill list",
|
|
46
|
+
mcp: { tool: "skill_list" },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const content = await readFile(
|
|
52
|
+
resolve(match.sourcePath, "SKILL.md"),
|
|
53
|
+
"utf-8",
|
|
54
|
+
);
|
|
55
|
+
const files = await listCompanionFiles(match.sourcePath);
|
|
56
|
+
|
|
57
|
+
return { ...match, content, files };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function listCompanionFiles(dir: string): Promise<readonly string[]> {
|
|
61
|
+
try {
|
|
62
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
63
|
+
return entries
|
|
64
|
+
.filter((entry) => entry.isFile() && entry.name !== "SKILL.md")
|
|
65
|
+
.map((entry) => entry.name)
|
|
66
|
+
.sort();
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { skillListStory } from "./stories.js";
|
|
3
|
+
import type { SkillSource } from "./types.js";
|
|
4
|
+
|
|
5
|
+
const availableSource: SkillSource = {
|
|
6
|
+
path: "/pkgs/design-system/skills/design-auditor",
|
|
7
|
+
packageName: "@canonical/design-system",
|
|
8
|
+
available: true,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const unavailableSource: SkillSource = {
|
|
12
|
+
path: "/pkgs/missing/skills/gone",
|
|
13
|
+
packageName: "@canonical/missing",
|
|
14
|
+
available: false,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
describe("skillListStory", () => {
|
|
18
|
+
it("passes the CLI detailed flag into the formatter input", () => {
|
|
19
|
+
const data = { skills: [], sources: [] };
|
|
20
|
+
expect(skillListStory.toOutput(data, { detailed: true }).detailed).toBe(
|
|
21
|
+
true,
|
|
22
|
+
);
|
|
23
|
+
expect(skillListStory.toOutput(data, {}).detailed).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("suggests installing packages when no source is available", () => {
|
|
27
|
+
const error = skillListStory.emptyError?.(
|
|
28
|
+
{ skills: [], sources: [unavailableSource] },
|
|
29
|
+
{},
|
|
30
|
+
);
|
|
31
|
+
expect(error?.recovery?.message).toBe("Install @canonical packages first");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("reports missing SKILL.md files when sources are available", () => {
|
|
35
|
+
const error = skillListStory.emptyError?.(
|
|
36
|
+
{ skills: [], sources: [availableSource] },
|
|
37
|
+
{},
|
|
38
|
+
);
|
|
39
|
+
expect(error?.recovery?.message).toBe(
|
|
40
|
+
"No SKILL.md files found in source packages",
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill read stories — the single declaration of `skill list` and
|
|
3
|
+
* `skill lookup` for both surfaces.
|
|
4
|
+
*
|
|
5
|
+
* `skill lookup` serves the full SKILL.md content, making skills directly
|
|
6
|
+
* consumable by agents over MCP (previously only metadata was listed).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { PragmaError } from "#error";
|
|
10
|
+
import lookupMany from "../shared/lookupMany.js";
|
|
11
|
+
import {
|
|
12
|
+
type LookupStory,
|
|
13
|
+
type ReadStory,
|
|
14
|
+
requirePragmaContext,
|
|
15
|
+
} from "../shared/stories/index.js";
|
|
16
|
+
import { listFormatters, lookupFormatters } from "./formatters/index.js";
|
|
17
|
+
import type { SkillListInput } from "./formatters/types.js";
|
|
18
|
+
import {
|
|
19
|
+
discoverSkills,
|
|
20
|
+
listSkills,
|
|
21
|
+
lookupSkill,
|
|
22
|
+
type SkillListResult,
|
|
23
|
+
} from "./operations/index.js";
|
|
24
|
+
import type { SkillDetailed } from "./types.js";
|
|
25
|
+
|
|
26
|
+
/** The `skill list` / `skill_list` read story. */
|
|
27
|
+
export const skillListStory: ReadStory<SkillListResult, SkillListInput> = {
|
|
28
|
+
noun: "skill",
|
|
29
|
+
verb: "list",
|
|
30
|
+
description: "List available agent skills from design system packages",
|
|
31
|
+
toolDescription: "List available agent skills from design system packages.",
|
|
32
|
+
params: [
|
|
33
|
+
{
|
|
34
|
+
name: "detailed",
|
|
35
|
+
type: "boolean",
|
|
36
|
+
description: "Show full metadata for each skill",
|
|
37
|
+
default: false,
|
|
38
|
+
surfaces: "cli",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
examples: [
|
|
42
|
+
"pragma skill list",
|
|
43
|
+
"pragma skill list --detailed",
|
|
44
|
+
"pragma skill list --llm",
|
|
45
|
+
"pragma skill list --format json",
|
|
46
|
+
],
|
|
47
|
+
resolve: (rt) => listSkills(rt.cwd),
|
|
48
|
+
toOutput: ({ skills, sources }, params) => ({
|
|
49
|
+
skills,
|
|
50
|
+
sources,
|
|
51
|
+
detailed: params.detailed === true,
|
|
52
|
+
}),
|
|
53
|
+
formatters: listFormatters,
|
|
54
|
+
toEnvelope: ({ skills, sources }) => ({
|
|
55
|
+
data: { skills, sources },
|
|
56
|
+
meta: { count: skills.length },
|
|
57
|
+
}),
|
|
58
|
+
emptyError: ({ skills, sources }) => {
|
|
59
|
+
if (skills.length > 0) return undefined;
|
|
60
|
+
const allUnavailable = sources.every((source) => !source.available);
|
|
61
|
+
return PragmaError.emptyResults("skill", {
|
|
62
|
+
recovery: {
|
|
63
|
+
message: allUnavailable
|
|
64
|
+
? "Install @canonical packages first"
|
|
65
|
+
: "No SKILL.md files found in source packages",
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** The `skill lookup` / `skill_lookup` read story. */
|
|
72
|
+
export const skillLookupStory: LookupStory<SkillDetailed, SkillDetailed> = {
|
|
73
|
+
noun: "skill",
|
|
74
|
+
description: "Show full instructions for a skill by name",
|
|
75
|
+
toolDescription:
|
|
76
|
+
"Get full skill instructions (SKILL.md content) for one or more agent skills by name.",
|
|
77
|
+
namesDescription: "Skill names",
|
|
78
|
+
namesToolDescription: "Skill names to look up (e.g. ['design-auditor'])",
|
|
79
|
+
complete: async (partial, cmdCtx) => {
|
|
80
|
+
const ctx = requirePragmaContext(cmdCtx);
|
|
81
|
+
const skills = await discoverSkills(ctx.cwd);
|
|
82
|
+
return skills
|
|
83
|
+
.map((skill) => skill.name)
|
|
84
|
+
.filter((name) => name.toLowerCase().startsWith(partial.toLowerCase()));
|
|
85
|
+
},
|
|
86
|
+
examples: [
|
|
87
|
+
"pragma skill lookup design-auditor",
|
|
88
|
+
"pragma skill lookup design-auditor add-standard",
|
|
89
|
+
"pragma skill lookup design-auditor --llm",
|
|
90
|
+
],
|
|
91
|
+
resolve: (rt, names) =>
|
|
92
|
+
lookupMany(names, (query) => lookupSkill(rt.cwd, query)),
|
|
93
|
+
toFmtInput: (skill) => skill,
|
|
94
|
+
formatters: lookupFormatters,
|
|
95
|
+
emptyNamesError: () =>
|
|
96
|
+
PragmaError.invalidInput("names", "(empty)", {
|
|
97
|
+
recovery: {
|
|
98
|
+
message: "List available skills.",
|
|
99
|
+
cli: "pragma skill list",
|
|
100
|
+
mcp: { tool: "skill_list" },
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
};
|
|
@@ -23,3 +23,11 @@ export interface SkillSource {
|
|
|
23
23
|
readonly packageName: string;
|
|
24
24
|
readonly available: boolean;
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/** A discovered skill enriched with its full SKILL.md content and companion files. */
|
|
28
|
+
export interface SkillDetailed extends DiscoveredSkill {
|
|
29
|
+
/** Full SKILL.md text, frontmatter included. */
|
|
30
|
+
readonly content: string;
|
|
31
|
+
/** Companion file names shipped next to SKILL.md (e.g. spec documents). */
|
|
32
|
+
readonly files: readonly string[];
|
|
33
|
+
}
|
|
@@ -122,4 +122,12 @@ describe("token lookup command", () => {
|
|
|
122
122
|
expect(text).toContain("color.primary");
|
|
123
123
|
expect(text).toContain("spacing.sm");
|
|
124
124
|
});
|
|
125
|
+
|
|
126
|
+
it("rejects an empty names list with INVALID_INPUT", async () => {
|
|
127
|
+
const ctx = makeCtx();
|
|
128
|
+
const cmd = lookupCommand(ctx);
|
|
129
|
+
await expect(cmd.execute({ names: [] }, ctx)).rejects.toMatchObject({
|
|
130
|
+
code: "INVALID_INPUT",
|
|
131
|
+
});
|
|
132
|
+
});
|
|
125
133
|
});
|