@canonical/pragma-cli 0.27.1-experimental.0 → 0.30.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 +67 -15
- package/dist/pragma +0 -0
- package/package.json +16 -17
- 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 +215 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -117
- package/src/config/readConfigLayers.test.ts +107 -0
- package/src/config/readConfigLayers.ts +115 -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 +88 -2
- package/src/config/writeConfig.ts +25 -43
- package/src/constants.ts +12 -1
- package/src/domains/block/commands/index.ts +2 -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 +55 -0
- package/src/domains/block/formatters/index.ts +2 -1
- package/src/domains/block/formatters/sample.ts +21 -0
- package/src/domains/block/index.ts +3 -3
- package/src/domains/block/mcp/specs.ts +36 -168
- package/src/domains/block/operations/index.ts +2 -1
- package/src/domains/block/operations/lookup.ts +9 -0
- package/src/domains/block/operations/sample.ts +41 -0
- package/src/domains/block/orchestration/blockEmptyError.test.ts +26 -0
- package/src/domains/block/orchestration/resolveBlockLookup.ts +11 -3
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +254 -0
- package/src/domains/config/commands/channel.test.ts +14 -3
- package/src/domains/config/commands/channel.ts +11 -7
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +105 -0
- package/src/domains/config/commands/framework.ts +91 -0
- package/src/domains/config/commands/index.ts +2 -0
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +13 -2
- package/src/domains/config/commands/tier.ts +11 -7
- package/src/domains/config/commands/trace.test.ts +165 -0
- package/src/domains/config/commands/trace.ts +83 -0
- 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 +37 -0
- package/src/domains/config/formatters/index.ts +2 -0
- 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 +28 -0
- package/src/domains/config/index.ts +15 -2
- package/src/domains/config/mcp/specs.ts +33 -53
- package/src/domains/config/operations/index.ts +1 -0
- 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/validateFramework.ts +24 -0
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +3 -0
- package/src/domains/config/tasks/setChannel.ts +13 -22
- package/src/domains/config/tasks/setFramework.ts +21 -0
- package/src/domains/config/tasks/setTier.ts +10 -23
- package/src/domains/config/tasks/setTrace.ts +20 -0
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- package/src/domains/create/commands/component.ts +5 -5
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkKeStore.ts +5 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +130 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -48
- 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 +162 -8
- package/src/domains/doctor/operations/runChecks.test.ts +85 -4
- package/src/domains/doctor/operations/runChecks.ts +50 -12
- package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graphql/commands/build.test.ts +196 -0
- package/src/domains/graphql/commands/build.ts +117 -0
- package/src/domains/graphql/commands/check.test.ts +141 -0
- package/src/domains/graphql/commands/check.ts +81 -0
- package/src/domains/graphql/commands/index.ts +5 -0
- package/src/domains/graphql/commands/serve.test.ts +40 -0
- package/src/domains/graphql/commands/serve.ts +131 -0
- package/src/domains/graphql/constants.ts +10 -0
- package/src/domains/graphql/formatters/index.ts +3 -0
- package/src/domains/graphql/formatters/report.ts +78 -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 +6 -0
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +44 -0
- package/src/domains/graphql/helpers/parsePrefixes.ts +38 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +56 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +45 -0
- 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 +24 -0
- package/src/domains/graphql/operations/compileSchema.test.ts +95 -0
- package/src/domains/graphql/operations/compileSchema.ts +107 -0
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- package/src/domains/graphql/operations/index.ts +12 -0
- package/src/domains/graphql/types.ts +29 -0
- package/src/domains/info/operations/collectInfo.test.ts +16 -5
- package/src/domains/info/operations/collectInfo.ts +16 -19
- package/src/domains/llm/capabilities.test.ts +91 -0
- package/src/domains/llm/commands/capabilities.ts +28 -0
- package/src/domains/llm/commands/index.ts +1 -0
- package/src/domains/llm/data/commandReference.ts +90 -18
- package/src/domains/llm/data/decisionTrees.ts +15 -13
- package/src/domains/llm/data/index.ts +2 -0
- package/src/domains/llm/data/toolCatalog.ts +190 -0
- package/src/domains/llm/formatters/capabilities.ts +50 -0
- package/src/domains/llm/formatters/index.ts +1 -0
- package/src/domains/llm/index.ts +3 -2
- package/src/domains/llm/mcp/index.ts +1 -1
- package/src/domains/llm/mcp/specs.ts +59 -52
- package/src/domains/llm/types.ts +40 -0
- package/src/domains/modifier/commands/index.ts +2 -1
- 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 +53 -0
- package/src/domains/modifier/formatters/index.ts +2 -1
- package/src/domains/modifier/formatters/sample.ts +15 -0
- package/src/domains/modifier/index.ts +7 -3
- package/src/domains/modifier/mcp/specs.ts +38 -70
- package/src/domains/modifier/operations/index.ts +2 -1
- package/src/domains/modifier/operations/lookup.ts +9 -0
- package/src/domains/modifier/operations/sample.ts +32 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.test.ts +26 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.ts +7 -2
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +15 -2
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +77 -0
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.ts +6 -58
- package/src/domains/ontology/formatters/show.ts +7 -1
- package/src/domains/ontology/mcp/specs.ts +7 -84
- package/src/domains/ontology/stories.test.ts +36 -0
- package/src/domains/ontology/stories.ts +108 -0
- package/src/domains/refs/operations/index.ts +1 -1
- package/src/domains/refs/operations/paths.ts +29 -1
- package/src/domains/refs/operations/updateRefs.test.ts +6 -3
- package/src/domains/refs/operations/updateRefs.ts +2 -36
- 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/operations/setupMcp.test.ts +6 -1
- package/src/domains/shared/bootStore.ts +92 -50
- 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 +12 -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 +109 -0
- package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +206 -0
- package/src/domains/shared/loaders/gitLoader.ts +31 -0
- package/src/domains/shared/loaders/index.ts +9 -0
- package/src/domains/shared/loaders/localLoader.ts +87 -0
- package/src/domains/shared/loaders/readPackageDir.ts +147 -0
- 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 +120 -0
- package/src/domains/shared/lookupMany.ts +43 -3
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/packages.ts +14 -120
- package/src/domains/shared/pickRandom.ts +22 -0
- package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/runtime.ts +7 -63
- package/src/domains/shared/sampleFormatters.ts +47 -0
- package/src/domains/shared/semanticPackage.ts +112 -0
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +100 -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 +102 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +76 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +51 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +80 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +108 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +76 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +40 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +146 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +253 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +43 -0
- package/src/domains/shared/stories/pack/index.ts +28 -0
- package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +308 -0
- package/src/domains/shared/stories/pack/types.ts +104 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +240 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +408 -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 +171 -0
- package/src/domains/shared/suggestions/detectCrossDomain.test.ts +43 -0
- package/src/domains/shared/suggestions/detectCrossDomain.ts +116 -0
- package/src/domains/shared/suggestions/expandGlob.test.ts +92 -0
- package/src/domains/shared/suggestions/expandGlob.ts +52 -0
- package/src/domains/shared/suggestions/expandLookupQueries.ts +57 -0
- package/src/domains/shared/suggestions/index.ts +7 -0
- package/src/domains/shared/suggestions/listDomainNames.ts +61 -0
- package/src/domains/shared/suggestions/suggestNames.test.ts +70 -0
- package/src/domains/shared/suggestions/suggestNames.ts +130 -0
- package/src/domains/shared/types/index.ts +1 -0
- package/src/domains/shared/types/runtime.ts +4 -1
- package/src/domains/shared/types/sample.ts +17 -0
- 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 +48 -16
- 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/standard/commands/index.ts +2 -1
- package/src/domains/standard/commands/list.ts +5 -84
- package/src/domains/standard/commands/lookup.ts +8 -93
- package/src/domains/standard/commands/sample.ts +57 -0
- package/src/domains/standard/formatters/index.ts +2 -1
- package/src/domains/standard/formatters/sample.ts +15 -0
- package/src/domains/standard/index.ts +8 -1
- package/src/domains/standard/mcp/specs.ts +44 -152
- package/src/domains/standard/operations/index.ts +2 -1
- package/src/domains/standard/operations/lookup.ts +9 -0
- package/src/domains/standard/operations/sample.ts +35 -0
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +15 -2
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +33 -0
- package/src/domains/standard/orchestration/standardEmptyError.ts +16 -6
- package/src/domains/standard/stories.test.ts +24 -0
- package/src/domains/standard/stories.ts +151 -0
- package/src/domains/tier/commands/list.ts +5 -55
- package/src/domains/tier/mcp/specs.ts +5 -31
- package/src/domains/tier/stories.test.ts +22 -0
- package/src/domains/tier/stories.ts +52 -0
- package/src/domains/token/commands/index.ts +2 -1
- package/src/domains/token/commands/sample.ts +53 -0
- package/src/domains/token/formatters/index.ts +2 -1
- package/src/domains/token/formatters/sample.ts +16 -0
- package/src/domains/token/index.ts +8 -1
- package/src/domains/token/mcp/specs.ts +52 -2
- package/src/domains/token/operations/index.ts +2 -1
- package/src/domains/token/operations/lookup.ts +9 -0
- package/src/domains/token/operations/sample.ts +32 -0
- package/src/domains/token/orchestration/resolveTokenLookup.ts +15 -2
- package/src/domains/token/orchestration/tokenEmptyError.test.ts +33 -0
- package/src/domains/token/orchestration/tokenEmptyError.ts +5 -2
- package/src/domains/trace/commands/clear.ts +71 -0
- package/src/domains/trace/commands/index.ts +5 -0
- package/src/domains/trace/commands/sessions.ts +32 -0
- package/src/domains/trace/commands/show.ts +145 -0
- package/src/domains/trace/formatters/index.ts +9 -0
- package/src/domains/trace/formatters/sessions.ts +80 -0
- package/src/domains/trace/formatters/show.ts +122 -0
- package/src/domains/trace/index.ts +17 -0
- package/src/domains/trace/operations/index.ts +8 -0
- package/src/domains/trace/operations/listSessions.test.ts +55 -0
- package/src/domains/trace/operations/listSessions.ts +52 -0
- package/src/domains/trace/operations/readTraceLog.test.ts +100 -0
- package/src/domains/trace/operations/readTraceLog.ts +104 -0
- package/src/domains/trace/tracePlugin.test.ts +171 -0
- package/src/domains/trace/tracePlugin.ts +159 -0
- package/src/domains/trace/types.ts +65 -0
- package/src/error/PragmaError.ts +15 -2
- package/src/error/types.ts +15 -1
- package/src/mcp/error/serializeErrorPayload.ts +7 -0
- package/src/mcp/error/types.ts +8 -0
- package/src/mcp/registerResources.test.ts +405 -75
- package/src/mcp/registerResources.ts +5 -299
- 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 +9 -0
- package/src/mcp/tools/registerTools.test.ts +84 -12
- package/src/mcp/types.ts +4 -0
- package/src/mcp/utils/wrapTool.test.ts +1 -0
- package/src/pipeline/collectCommands.ts +15 -2
- package/src/pipeline/renderError.ts +15 -0
- package/src/pipeline/resolveCommandKind.ts +8 -1
- package/src/pipeline/runCli.ts +51 -6
- package/src/testing/createTestMcpClient.ts +5 -1
- package/src/testing/graphqlFixtures.ts +72 -0
- package/src/testing/index.ts +17 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/executor-execution.test.ts +101 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +11 -5
- package/src/testing/integration/parity.test.ts +17 -12
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +15 -0
- package/src/testing/standardParityFixtures.ts +92 -0
- package/src/testing/storyFixtures.ts +63 -0
- package/src/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -1,126 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Default semantic graph package registry.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Sourced from the declarative built-in defaults document
|
|
5
|
+
* (`src/config/defaults.json`) — pragma core carries no hardcoded
|
|
6
|
+
* knowledge of specific design-system packages. These form the base
|
|
7
|
+
* package set whenever a config layer does not declare a non-empty
|
|
8
|
+
* `packages` list; global `refs.json` entries then merge over them by
|
|
9
|
+
* package name, and a non-empty config list replaces them entirely
|
|
10
|
+
* (see `mergeAndParseRefs`).
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
* field. When absent, the hardcoded DEFAULT_PACKAGES are used.
|
|
12
|
+
* Resolution is handled by the loader chain in `loaders/`.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { dirname } from "node:path";
|
|
16
|
-
import type { PackageRef } from "../refs/operations/parseRef.js";
|
|
17
|
-
import { gitCacheDir } from "../refs/operations/paths.js";
|
|
15
|
+
import { DEFAULT_CONFIG } from "../../config/defaultConfig.js";
|
|
16
|
+
import type { RawPackageEntry } from "../refs/operations/parseRef.js";
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Default package registry (used when config.packages is absent)
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
/** Default semantic packages loaded when no config overrides are present. */
|
|
26
|
-
export const DEFAULT_PACKAGES: readonly string[] = [
|
|
27
|
-
"@canonical/design-system",
|
|
28
|
-
"@canonical/anatomy-dsl",
|
|
29
|
-
"@canonical/code-standards",
|
|
30
|
-
"@canonical/pragma-cli",
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Types
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
export interface ResolvedPackage {
|
|
38
|
-
/** npm package name. */
|
|
39
|
-
readonly pkg: string;
|
|
40
|
-
/** Absolute path to the package root. */
|
|
41
|
-
readonly dir: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
// Resolution
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Resolve packages to filesystem directories.
|
|
50
|
-
*
|
|
51
|
-
* When `refs` is provided, each PackageRef is resolved according to its kind.
|
|
52
|
-
* When `refs` is omitted, the DEFAULT_PACKAGES are resolved via require.resolve.
|
|
53
|
-
*
|
|
54
|
-
* @param refs - Parsed package references (from config merge). Omit for defaults.
|
|
55
|
-
* @returns Array of resolved packages with absolute directory paths.
|
|
56
|
-
*/
|
|
57
|
-
export function resolvePackages(
|
|
58
|
-
refs?: ReadonlyArray<PackageRef>,
|
|
59
|
-
): ResolvedPackage[] {
|
|
60
|
-
if (!refs) {
|
|
61
|
-
return resolveNpmPackages(DEFAULT_PACKAGES);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const resolved: ResolvedPackage[] = [];
|
|
65
|
-
|
|
66
|
-
for (const ref of refs) {
|
|
67
|
-
switch (ref.kind) {
|
|
68
|
-
case "file": {
|
|
69
|
-
if (!existsSync(ref.path)) {
|
|
70
|
-
console.warn(
|
|
71
|
-
`pragma: package "${ref.pkg}" points to ${ref.path} which does not exist. Check the path or remove the file:// source.`,
|
|
72
|
-
);
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
resolved.push({ pkg: ref.pkg, dir: ref.path });
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
case "git": {
|
|
80
|
-
const cacheDir = gitCacheDir(ref.pkg, ref.ref);
|
|
81
|
-
if (existsSync(cacheDir)) {
|
|
82
|
-
resolved.push({ pkg: ref.pkg, dir: cacheDir });
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
// Cache miss — warn and fall through to npm
|
|
86
|
-
console.warn(
|
|
87
|
-
`pragma: git ref for "${ref.pkg}" not cached. Run "pragma update-refs" to fetch it.`,
|
|
88
|
-
);
|
|
89
|
-
resolveNpmFallback(ref.pkg, resolved);
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
case "npm": {
|
|
94
|
-
resolveNpmFallback(ref.pkg, resolved);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return resolved;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
// Helpers
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
|
|
107
|
-
/** Resolve a list of package names via require.resolve. */
|
|
108
|
-
function resolveNpmPackages(pkgs: readonly string[]): ResolvedPackage[] {
|
|
109
|
-
const resolved: ResolvedPackage[] = [];
|
|
110
|
-
for (const pkg of pkgs) {
|
|
111
|
-
resolveNpmFallback(pkg, resolved);
|
|
112
|
-
}
|
|
113
|
-
return resolved;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Try to resolve a single package via require.resolve, push if found. */
|
|
117
|
-
function resolveNpmFallback(pkg: string, resolved: ResolvedPackage[]): void {
|
|
118
|
-
try {
|
|
119
|
-
resolved.push({
|
|
120
|
-
pkg,
|
|
121
|
-
dir: dirname(require.resolve(`${pkg}/package.json`)),
|
|
122
|
-
});
|
|
123
|
-
} catch {
|
|
124
|
-
// package not installed — skip
|
|
125
|
-
}
|
|
126
|
-
}
|
|
18
|
+
/** Default semantic graph packages loaded when no config overrides are present. */
|
|
19
|
+
export const DEFAULT_PACKAGES: readonly RawPackageEntry[] =
|
|
20
|
+
DEFAULT_CONFIG.packages;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick `count` random items from an array without replacement.
|
|
3
|
+
*
|
|
4
|
+
* Uses Fisher-Yates partial shuffle for uniform distribution.
|
|
5
|
+
* Returns at most `arr.length` items if count exceeds the array size.
|
|
6
|
+
*/
|
|
7
|
+
export default function pickRandom<T>(
|
|
8
|
+
arr: readonly T[],
|
|
9
|
+
count: number,
|
|
10
|
+
): readonly T[] {
|
|
11
|
+
const n = Math.min(count, arr.length);
|
|
12
|
+
if (n === 0) return [];
|
|
13
|
+
|
|
14
|
+
const copy = [...arr];
|
|
15
|
+
for (let i = 0; i < n; i++) {
|
|
16
|
+
const j = i + Math.floor(Math.random() * (copy.length - i));
|
|
17
|
+
const tmp = copy[i];
|
|
18
|
+
copy[i] = copy[j] as T;
|
|
19
|
+
copy[j] = tmp as T;
|
|
20
|
+
}
|
|
21
|
+
return copy.slice(0, n);
|
|
22
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import resolveConfiguredGraphs from "./resolveConfiguredGraphs.js";
|
|
6
|
+
|
|
7
|
+
describe("resolveConfiguredGraphs", () => {
|
|
8
|
+
let dir: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
dir = mkdtempSync(join(tmpdir(), "pragma-rcg-"));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
rmSync(dir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("resolves the default semantic packages' TTL graphs", async () => {
|
|
19
|
+
// No pragma.config.json in the temp dir → mergeAndParseRefs falls back to
|
|
20
|
+
// the default packages, which resolve from node_modules in the workspace.
|
|
21
|
+
const graphs = await resolveConfiguredGraphs(dir);
|
|
22
|
+
|
|
23
|
+
expect(graphs.length).toBeGreaterThan(0);
|
|
24
|
+
expect(graphs[0]).toMatchObject({ format: "turtle" });
|
|
25
|
+
expect(typeof graphs[0]?.content).toBe("string");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve every TTL graph from the semantically-configured packages.
|
|
3
|
+
*
|
|
4
|
+
* Reads `pragma.config.json`, merges its `packages` with the global refs
|
|
5
|
+
* and the built-in defaults (a non-empty `packages` list replaces the set
|
|
6
|
+
* entirely; an empty or absent list falls back to global refs merged over
|
|
7
|
+
* the defaults), resolves each package through the loader chain
|
|
8
|
+
* (local > git > bundled), and returns the union of their definitions/ and
|
|
9
|
+
* data/ graph content — the same discovery the runtime store performs at
|
|
10
|
+
* boot, minus the store.
|
|
11
|
+
*
|
|
12
|
+
* @note Impure — reads config and package files from disk.
|
|
13
|
+
*
|
|
14
|
+
* @param cwd - Working directory used to locate `pragma.config.json`.
|
|
15
|
+
* @returns The TTL graph content of every resolved package.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readConfig } from "#config";
|
|
19
|
+
import {
|
|
20
|
+
createBundledLoader,
|
|
21
|
+
createGitLoader,
|
|
22
|
+
createLocalLoader,
|
|
23
|
+
} from "./loaders/index.js";
|
|
24
|
+
import { mergeAndParseRefs } from "./mergeAndParseRefs.js";
|
|
25
|
+
import {
|
|
26
|
+
type GraphContent,
|
|
27
|
+
resolveSemanticPackages,
|
|
28
|
+
} from "./semanticPackage.js";
|
|
29
|
+
|
|
30
|
+
export default async function resolveConfiguredGraphs(
|
|
31
|
+
cwd: string,
|
|
32
|
+
): Promise<GraphContent[]> {
|
|
33
|
+
const config = readConfig(cwd);
|
|
34
|
+
const refs = mergeAndParseRefs(config.packages);
|
|
35
|
+
const packages = await resolveSemanticPackages(refs, [
|
|
36
|
+
createLocalLoader(),
|
|
37
|
+
createGitLoader(),
|
|
38
|
+
createBundledLoader(),
|
|
39
|
+
]);
|
|
40
|
+
return packages.flatMap((pkg) => pkg.graphs);
|
|
41
|
+
}
|
|
@@ -10,14 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import type { SourceSpec } from "@canonical/ke";
|
|
12
12
|
import { readConfig } from "#config";
|
|
13
|
-
import {
|
|
14
|
-
type PackageRef,
|
|
15
|
-
parsePackageEntry,
|
|
16
|
-
type RawPackageEntry,
|
|
17
|
-
} from "../refs/operations/parseRef.js";
|
|
18
|
-
import readGlobalRefs from "../refs/operations/readGlobalRefs.js";
|
|
19
13
|
import { bootStore } from "./bootStore.js";
|
|
20
|
-
import {
|
|
14
|
+
import { mergeAndParseRefs } from "./mergeAndParseRefs.js";
|
|
21
15
|
import type { PragmaRuntime } from "./types/index.js";
|
|
22
16
|
|
|
23
17
|
export type { PragmaRuntime } from "./types/index.js";
|
|
@@ -43,75 +37,25 @@ export async function bootPragma(options?: {
|
|
|
43
37
|
const cwd = options?.cwd ?? process.cwd();
|
|
44
38
|
const config = readConfig(cwd);
|
|
45
39
|
|
|
46
|
-
// Merge package refs:
|
|
40
|
+
// Merge package refs: a non-empty config list replaces global refs and
|
|
41
|
+
// defaults; otherwise global refs merge over the defaults.
|
|
47
42
|
const refs = options?.sources
|
|
48
43
|
? undefined
|
|
49
44
|
: mergeAndParseRefs(config.packages);
|
|
50
45
|
|
|
51
|
-
const store = await bootStore({
|
|
46
|
+
const { store, packages } = await bootStore({
|
|
52
47
|
cwd,
|
|
53
48
|
sources: options?.sources,
|
|
54
49
|
refs,
|
|
50
|
+
trace: config.trace,
|
|
51
|
+
prefixes: config.prefixes,
|
|
55
52
|
});
|
|
56
53
|
|
|
57
54
|
return {
|
|
58
55
|
store,
|
|
59
56
|
config,
|
|
60
57
|
cwd,
|
|
58
|
+
packages,
|
|
61
59
|
dispose: () => store.dispose(),
|
|
62
60
|
};
|
|
63
61
|
}
|
|
64
|
-
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
// Ref merging
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Merge global refs, project config packages, and defaults into a final
|
|
71
|
-
* parsed PackageRef array.
|
|
72
|
-
*
|
|
73
|
-
* Priority: project packages > global refs > hardcoded defaults.
|
|
74
|
-
* Merging is by package name — a project entry for "@canonical/foo"
|
|
75
|
-
* overrides the global entry for the same package.
|
|
76
|
-
*/
|
|
77
|
-
function mergeAndParseRefs(
|
|
78
|
-
projectPackages?: ReadonlyArray<RawPackageEntry>,
|
|
79
|
-
): PackageRef[] {
|
|
80
|
-
const globalEntries = readGlobalRefs();
|
|
81
|
-
|
|
82
|
-
// If neither project nor global defines packages, return undefined
|
|
83
|
-
// so resolvePackages() uses hardcoded defaults.
|
|
84
|
-
if (
|
|
85
|
-
(!projectPackages || projectPackages.length === 0) &&
|
|
86
|
-
globalEntries.length === 0
|
|
87
|
-
) {
|
|
88
|
-
return DEFAULT_PACKAGES.map((pkg) => parsePackageEntry(pkg));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Build a name → entry map, global first, project overrides.
|
|
92
|
-
const merged = new Map<string, RawPackageEntry>();
|
|
93
|
-
|
|
94
|
-
// Start with defaults (lowest priority)
|
|
95
|
-
for (const pkg of DEFAULT_PACKAGES) {
|
|
96
|
-
merged.set(pkg, pkg);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Global overrides defaults
|
|
100
|
-
for (const entry of globalEntries) {
|
|
101
|
-
const name = typeof entry === "string" ? entry : entry.name;
|
|
102
|
-
merged.set(name, entry);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Project overrides global
|
|
106
|
-
if (projectPackages) {
|
|
107
|
-
// When project explicitly declares packages, it's a full replacement
|
|
108
|
-
// of the package list — clear defaults and global, use project only.
|
|
109
|
-
merged.clear();
|
|
110
|
-
for (const entry of projectPackages) {
|
|
111
|
-
const name = typeof entry === "string" ? entry : entry.name;
|
|
112
|
-
merged.set(name, entry);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return [...merged.values()].map(parsePackageEntry);
|
|
117
|
-
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for sample command formatters.
|
|
3
|
+
*
|
|
4
|
+
* Every domain's sample formatter follows the same pattern: a count
|
|
5
|
+
* header, then each item rendered via the domain's lookup formatter.
|
|
6
|
+
* This factory eliminates the duplication.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Formatters } from "./formatters.js";
|
|
10
|
+
import type { SampleOutput } from "./types/index.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a three-mode formatter for `pragma <domain> sample`.
|
|
14
|
+
*
|
|
15
|
+
* @param domain - plural domain label for the header (e.g. "standards")
|
|
16
|
+
* @param renderItem - renders one item; receives the item and the mode
|
|
17
|
+
*/
|
|
18
|
+
export default function createSampleFormatters<T>(
|
|
19
|
+
domain: string,
|
|
20
|
+
renderItem: (item: T, mode: "plain" | "llm") => string,
|
|
21
|
+
): Formatters<SampleOutput<T>> {
|
|
22
|
+
return {
|
|
23
|
+
plain(output) {
|
|
24
|
+
const header = `Showing ${output.samples.length} of ${output.totalCount} ${domain}\n`;
|
|
25
|
+
const items = output.samples.map((item) => renderItem(item, "plain"));
|
|
26
|
+
return header + items.join("\n\n");
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
llm(output) {
|
|
30
|
+
const header = `Showing ${output.samples.length} of ${output.totalCount} ${domain}\n`;
|
|
31
|
+
const items = output.samples.map((item) => renderItem(item, "llm"));
|
|
32
|
+
return header + items.join("\n\n");
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
json(output) {
|
|
36
|
+
return JSON.stringify(
|
|
37
|
+
{
|
|
38
|
+
samples: output.samples,
|
|
39
|
+
totalCount: output.totalCount,
|
|
40
|
+
nextSteps: output.nextSteps,
|
|
41
|
+
},
|
|
42
|
+
null,
|
|
43
|
+
2,
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic package contracts and resolution orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* A semantic package is a resolved graph dependency containing TTL
|
|
5
|
+
* definitions, instance data, and skills. Three loaders produce the
|
|
6
|
+
* same `SemanticPackage` shape — local (file/npm), git cache, and
|
|
7
|
+
* bundled (compiled binary). The orchestrator tries each loader in
|
|
8
|
+
* precedence order: local > git > bundled.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { PackageRef } from "../refs/operations/parseRef.js";
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Contracts
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
/** A single TTL file's content with provenance annotation. */
|
|
18
|
+
export interface GraphContent {
|
|
19
|
+
/** Original file path or virtual identifier (for provenance/debugging). */
|
|
20
|
+
readonly path: string;
|
|
21
|
+
/** TTL content string. */
|
|
22
|
+
readonly content: string;
|
|
23
|
+
/** RDF serialization format. */
|
|
24
|
+
readonly format: "turtle";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** A discovered story-pack JSON file with its content. */
|
|
28
|
+
export interface StoryFileEntry {
|
|
29
|
+
/**
|
|
30
|
+
* Original file path, or a virtual identifier for embedded sources
|
|
31
|
+
* (e.g. `(bundled)/…`) — for provenance/debugging, not guaranteed to
|
|
32
|
+
* exist on disk.
|
|
33
|
+
*/
|
|
34
|
+
readonly path: string;
|
|
35
|
+
/** The file's parsed JSON — validated by the story-pack compiler. */
|
|
36
|
+
readonly definition: unknown;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SkillEntry {
|
|
40
|
+
/** Absolute path to the skill directory (must exist on disk for symlinking). */
|
|
41
|
+
readonly dir: string;
|
|
42
|
+
/** Skill folder name (e.g. "anatomy-author"). */
|
|
43
|
+
readonly folderName: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** A resolved graph package. All loaders produce this uniform shape. */
|
|
47
|
+
export interface SemanticPackage {
|
|
48
|
+
/** npm package name (e.g. "@canonical/design-system"). */
|
|
49
|
+
readonly name: string;
|
|
50
|
+
/** Version string from package.json. */
|
|
51
|
+
readonly version: string;
|
|
52
|
+
/** How this package was resolved. */
|
|
53
|
+
readonly source: "local" | "git" | "bundled";
|
|
54
|
+
/** TTL graph content to load into the ke store. */
|
|
55
|
+
readonly graphs: GraphContent[];
|
|
56
|
+
/** Skills available in this package. */
|
|
57
|
+
readonly skills: SkillEntry[];
|
|
58
|
+
/**
|
|
59
|
+
* Raw story-pack files shipped by this package (`stories/*.json`).
|
|
60
|
+
* Parsed JSON, validated at compile time so one bad file cannot break boot.
|
|
61
|
+
*/
|
|
62
|
+
readonly stories: StoryFileEntry[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolves a package reference to a SemanticPackage.
|
|
67
|
+
* Returns `undefined` if this loader cannot resolve the given ref.
|
|
68
|
+
* May be async (e.g. BundledLoader reads blob content asynchronously).
|
|
69
|
+
*/
|
|
70
|
+
export interface PackageLoader {
|
|
71
|
+
/** Loader identity for diagnostics. */
|
|
72
|
+
readonly name: "local" | "git" | "bundled";
|
|
73
|
+
/** Attempt to resolve a package reference. */
|
|
74
|
+
resolve(
|
|
75
|
+
ref: PackageRef,
|
|
76
|
+
): SemanticPackage | undefined | Promise<SemanticPackage | undefined>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Orchestrator
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolve package references to semantic packages using a loader chain.
|
|
85
|
+
*
|
|
86
|
+
* Tries each loader in array order for each ref. The first loader that
|
|
87
|
+
* returns a result wins — subsequent loaders are skipped for that ref.
|
|
88
|
+
* Refs that no loader can resolve are silently dropped.
|
|
89
|
+
*
|
|
90
|
+
* @param refs - Parsed package references from config merge.
|
|
91
|
+
* @param loaders - Loaders in precedence order (local > git > bundled).
|
|
92
|
+
* @returns Array of resolved semantic packages.
|
|
93
|
+
*/
|
|
94
|
+
export async function resolveSemanticPackages(
|
|
95
|
+
refs: ReadonlyArray<PackageRef>,
|
|
96
|
+
loaders: ReadonlyArray<PackageLoader>,
|
|
97
|
+
): Promise<SemanticPackage[]> {
|
|
98
|
+
const packages: SemanticPackage[] = [];
|
|
99
|
+
|
|
100
|
+
for (const ref of refs) {
|
|
101
|
+
let resolved: SemanticPackage | undefined;
|
|
102
|
+
for (const loader of loaders) {
|
|
103
|
+
resolved = await loader.resolve(ref);
|
|
104
|
+
if (resolved) break;
|
|
105
|
+
}
|
|
106
|
+
if (resolved) {
|
|
107
|
+
packages.push(resolved);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return packages;
|
|
112
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { PragmaError } from "#error";
|
|
3
|
+
import type { PragmaContext } from "../context.js";
|
|
4
|
+
import type { LookupResult } from "../contracts.js";
|
|
5
|
+
import compileLookupCommand from "./compileLookupCommand.js";
|
|
6
|
+
import type { LookupStory } from "./types.js";
|
|
7
|
+
|
|
8
|
+
interface FakeEntity {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly detail: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function makeStory(
|
|
14
|
+
overrides: Partial<LookupStory<FakeEntity, string>> = {},
|
|
15
|
+
): LookupStory<FakeEntity, string> {
|
|
16
|
+
return {
|
|
17
|
+
noun: "thing",
|
|
18
|
+
description: "Look up things",
|
|
19
|
+
toolDescription: "Look up things over MCP.",
|
|
20
|
+
namesDescription: "Thing names",
|
|
21
|
+
detailedParam: { description: "Show details" },
|
|
22
|
+
examples: ["pragma thing lookup a"],
|
|
23
|
+
resolve: async (_rt, names) => {
|
|
24
|
+
const results = names
|
|
25
|
+
.filter((name) => name !== "missing")
|
|
26
|
+
.map((name) => ({ name, detail: `detail-${name}` }));
|
|
27
|
+
const errors: LookupResult<FakeEntity>["errors"] = names
|
|
28
|
+
.filter((name) => name === "missing")
|
|
29
|
+
.map((name) => ({
|
|
30
|
+
query: name,
|
|
31
|
+
code: "ENTITY_NOT_FOUND",
|
|
32
|
+
message: `thing "${name}" not found.`,
|
|
33
|
+
}));
|
|
34
|
+
return { results, errors };
|
|
35
|
+
},
|
|
36
|
+
toFmtInput: (entity, view) =>
|
|
37
|
+
view.detailed ? `${entity.name}:${entity.detail}` : entity.name,
|
|
38
|
+
formatters: {
|
|
39
|
+
plain: (text) => `plain:${text}`,
|
|
40
|
+
llm: (text) => `llm:${text}`,
|
|
41
|
+
json: (text) => JSON.stringify(text),
|
|
42
|
+
},
|
|
43
|
+
...overrides,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeCtx(overrides: Partial<PragmaContext> = {}): PragmaContext {
|
|
48
|
+
return {
|
|
49
|
+
cwd: "/tmp",
|
|
50
|
+
globalFlags: { llm: false, format: "text" as const, verbose: false },
|
|
51
|
+
...overrides,
|
|
52
|
+
} as PragmaContext;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function executeText(
|
|
56
|
+
story: LookupStory<FakeEntity, string>,
|
|
57
|
+
params: Record<string, unknown>,
|
|
58
|
+
ctx: PragmaContext,
|
|
59
|
+
): Promise<string> {
|
|
60
|
+
const cmd = compileLookupCommand(ctx, story);
|
|
61
|
+
const result = await cmd.execute(params, ctx);
|
|
62
|
+
if (result.tag !== "output") throw new Error("expected output result");
|
|
63
|
+
return result.render.plain(result.value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("compileLookupCommand", () => {
|
|
67
|
+
it("projects names, detailed, and extra parameters", () => {
|
|
68
|
+
const story = makeStory({
|
|
69
|
+
params: [
|
|
70
|
+
{ name: "extra", type: "boolean", description: "extra flag" },
|
|
71
|
+
{ name: "mcpOnly", type: "boolean", description: "x", surfaces: "mcp" },
|
|
72
|
+
],
|
|
73
|
+
parameterGroups: { Extras: ["extra"] },
|
|
74
|
+
});
|
|
75
|
+
const cmd = compileLookupCommand(makeCtx(), story);
|
|
76
|
+
expect(cmd.path).toEqual(["thing", "lookup"]);
|
|
77
|
+
expect(cmd.parameters.map((p) => p.name)).toEqual([
|
|
78
|
+
"names",
|
|
79
|
+
"detailed",
|
|
80
|
+
"extra",
|
|
81
|
+
]);
|
|
82
|
+
const names = cmd.parameters.at(0);
|
|
83
|
+
expect(names?.type).toBe("multiselect");
|
|
84
|
+
expect(names?.positional).toBe(true);
|
|
85
|
+
expect(names?.required).toBe(true);
|
|
86
|
+
expect(cmd.parameterGroups).toEqual({ Extras: ["extra"] });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("omits the detailed parameter when the story has none", () => {
|
|
90
|
+
const cmd = compileLookupCommand(
|
|
91
|
+
makeCtx(),
|
|
92
|
+
makeStory({ detailedParam: undefined }),
|
|
93
|
+
);
|
|
94
|
+
expect(cmd.parameters.map((p) => p.name)).toEqual(["names"]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("renders each result and appends an errors section", async () => {
|
|
98
|
+
const text = await executeText(
|
|
99
|
+
makeStory(),
|
|
100
|
+
{ names: ["a", "missing"] },
|
|
101
|
+
makeCtx(),
|
|
102
|
+
);
|
|
103
|
+
expect(text).toContain("plain:a");
|
|
104
|
+
expect(text).toContain("Errors:");
|
|
105
|
+
expect(text).toContain('thing "missing" not found.');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("is summary by default and detailed with the flag", async () => {
|
|
109
|
+
const summary = await executeText(makeStory(), { names: ["a"] }, makeCtx());
|
|
110
|
+
expect(summary).toBe("plain:a");
|
|
111
|
+
|
|
112
|
+
const detailed = await executeText(
|
|
113
|
+
makeStory(),
|
|
114
|
+
{ names: ["a"], detailed: true },
|
|
115
|
+
makeCtx(),
|
|
116
|
+
);
|
|
117
|
+
expect(detailed).toBe("plain:a:detail-a");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("accepts the legacy single name parameter", async () => {
|
|
121
|
+
const text = await executeText(makeStory(), { name: "a" }, makeCtx());
|
|
122
|
+
expect(text).toBe("plain:a");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("throws the story's empty-names error when no names are given", async () => {
|
|
126
|
+
const story = makeStory({
|
|
127
|
+
emptyNamesError: () => PragmaError.invalidInput("names", "(empty)"),
|
|
128
|
+
});
|
|
129
|
+
const cmd = compileLookupCommand(makeCtx(), story);
|
|
130
|
+
await expect(cmd.execute({ names: [] }, makeCtx())).rejects.toThrow(
|
|
131
|
+
PragmaError,
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("exposes an ink renderer only when the story defines one", async () => {
|
|
136
|
+
const story = makeStory({
|
|
137
|
+
renderInk: (result, view) =>
|
|
138
|
+
`ink:${result.results.length}:${view.detailed}`,
|
|
139
|
+
});
|
|
140
|
+
const cmd = compileLookupCommand(makeCtx(), story);
|
|
141
|
+
const result = await cmd.execute({ names: ["a"] }, makeCtx());
|
|
142
|
+
if (result.tag !== "output") throw new Error("expected output result");
|
|
143
|
+
expect(result.render.ink?.(result.value)).toBe("ink:1:false");
|
|
144
|
+
});
|
|
145
|
+
});
|