@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
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type { Store } from "@canonical/ke";
|
|
2
|
+
import { PragmaError } from "#error";
|
|
3
|
+
import type {
|
|
4
|
+
ColumnDef,
|
|
5
|
+
LookupField,
|
|
6
|
+
RenderListOptions,
|
|
7
|
+
RenderLookupOptions,
|
|
8
|
+
SectionDef,
|
|
9
|
+
} from "../../contracts.js";
|
|
10
|
+
import type { Formatters } from "../../formatters.js";
|
|
11
|
+
import lookupMany from "../../lookupMany.js";
|
|
12
|
+
import {
|
|
13
|
+
renderListLlm,
|
|
14
|
+
renderListPlain,
|
|
15
|
+
renderLookupLlm,
|
|
16
|
+
renderLookupPlain,
|
|
17
|
+
} from "../../renderers.js";
|
|
18
|
+
import { suggestNames } from "../../suggestions/index.js";
|
|
19
|
+
import requirePragmaContext from "../requirePragmaContext.js";
|
|
20
|
+
import type { LookupStory, ReadStory, StoryParam } from "../types.js";
|
|
21
|
+
import applyPackFilters from "./applyPackFilters.js";
|
|
22
|
+
import buildLookupQuery, { buildLookupNamesQuery } from "./buildLookupQuery.js";
|
|
23
|
+
import runSelectQuery from "./runSelectQuery.js";
|
|
24
|
+
import type {
|
|
25
|
+
StoryPackDefinition,
|
|
26
|
+
StoryPackFilter,
|
|
27
|
+
StoryPackLookup,
|
|
28
|
+
} from "./types.js";
|
|
29
|
+
|
|
30
|
+
/** A pack entity/row: SELECT variable name → string value. */
|
|
31
|
+
type PackRow = Record<string, string>;
|
|
32
|
+
|
|
33
|
+
/** The read stories a pack definition compiles to. */
|
|
34
|
+
export interface CompiledPackStories {
|
|
35
|
+
readonly list: ReadStory<PackRow[], PackRow[]>;
|
|
36
|
+
readonly lookup?: LookupStory<PackRow, PackRow>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compile one validated story-pack definition into kernel read stories.
|
|
41
|
+
*
|
|
42
|
+
* The list story runs the pack's preferred SELECT verbatim; the lookup
|
|
43
|
+
* story runs the generated, injection-safe per-name query. Both render
|
|
44
|
+
* through the shared generic renderers with the runtime's merged prefix
|
|
45
|
+
* map, so foreign namespaces display compacted.
|
|
46
|
+
*
|
|
47
|
+
* @param definition - A validated pack definition.
|
|
48
|
+
* @param source - Where the definition came from, for diagnostics.
|
|
49
|
+
* @param prefixes - The merged prefix map used for display.
|
|
50
|
+
* @returns The compiled list story and, when declared, the lookup story.
|
|
51
|
+
*/
|
|
52
|
+
export default function compilePackStories(
|
|
53
|
+
definition: StoryPackDefinition,
|
|
54
|
+
source: string,
|
|
55
|
+
prefixes: Readonly<Record<string, string>>,
|
|
56
|
+
): CompiledPackStories {
|
|
57
|
+
const { noun } = definition;
|
|
58
|
+
const heading = capitalize(noun);
|
|
59
|
+
const description = definition.description ?? `List ${noun} entries`;
|
|
60
|
+
|
|
61
|
+
const listColumns: ColumnDef<PackRow>[] = definition.list.columns.map(
|
|
62
|
+
(column) => ({
|
|
63
|
+
key: column.field,
|
|
64
|
+
label: column.label ?? column.field,
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
const listOptions: RenderListOptions<PackRow> = {
|
|
68
|
+
heading,
|
|
69
|
+
columns: listColumns,
|
|
70
|
+
prefixes,
|
|
71
|
+
};
|
|
72
|
+
const listFormatters: Formatters<PackRow[]> = {
|
|
73
|
+
plain: (rows) => renderListPlain(rows, listOptions),
|
|
74
|
+
llm: (rows) => renderListLlm(rows, listOptions),
|
|
75
|
+
json: (rows) => JSON.stringify(rows, null, 2),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const filters = definition.list.filters;
|
|
79
|
+
const filterExample = filters?.at(0);
|
|
80
|
+
const list: ReadStory<PackRow[], PackRow[]> = {
|
|
81
|
+
noun,
|
|
82
|
+
verb: "list",
|
|
83
|
+
description,
|
|
84
|
+
toolDescription:
|
|
85
|
+
definition.toolDescription ?? `${description} (story pack: ${source}).`,
|
|
86
|
+
params: projectFilterParams(filters),
|
|
87
|
+
examples: [
|
|
88
|
+
`pragma ${noun} list`,
|
|
89
|
+
...(filterExample
|
|
90
|
+
? [
|
|
91
|
+
`pragma ${noun} list --${filterExample.param} ${quoteExampleValue(filterExample.values.at(0) ?? "")}`,
|
|
92
|
+
]
|
|
93
|
+
: []),
|
|
94
|
+
`pragma ${noun} list --llm`,
|
|
95
|
+
],
|
|
96
|
+
resolve: async (rt, params) =>
|
|
97
|
+
applyPackFilters(
|
|
98
|
+
await runSelectQuery(rt.store, definition.list.query, source),
|
|
99
|
+
filters,
|
|
100
|
+
params,
|
|
101
|
+
),
|
|
102
|
+
toOutput: (rows) => rows,
|
|
103
|
+
formatters: listFormatters,
|
|
104
|
+
toEnvelope: (rows) => ({ data: rows, meta: { count: rows.length } }),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const lookup = definition.lookup
|
|
108
|
+
? compileLookup(definition.lookup, noun, source, prefixes)
|
|
109
|
+
: undefined;
|
|
110
|
+
|
|
111
|
+
return { list, ...(lookup ? { lookup } : {}) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function compileLookup(
|
|
115
|
+
lookup: StoryPackLookup,
|
|
116
|
+
noun: string,
|
|
117
|
+
source: string,
|
|
118
|
+
prefixes: Readonly<Record<string, string>>,
|
|
119
|
+
): LookupStory<PackRow, PackRow> {
|
|
120
|
+
const fields: LookupField<PackRow>[] = (lookup.fields ?? []).map((field) => ({
|
|
121
|
+
label: field.label ?? field.name,
|
|
122
|
+
value: (entity) => entity[field.name],
|
|
123
|
+
}));
|
|
124
|
+
const sections: SectionDef<PackRow>[] = (lookup.sections ?? []).map(
|
|
125
|
+
(section) => ({
|
|
126
|
+
key: section.name,
|
|
127
|
+
heading: section.label ?? section.name,
|
|
128
|
+
kind: section.kind ?? "field",
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
const lookupOptions: RenderLookupOptions<PackRow> = {
|
|
132
|
+
title: (entity) => entity.name ?? entity.uri ?? "(unnamed)",
|
|
133
|
+
fields,
|
|
134
|
+
sections,
|
|
135
|
+
prefixes,
|
|
136
|
+
};
|
|
137
|
+
const lookupFormatters: Formatters<PackRow> = {
|
|
138
|
+
plain: (entity) => renderLookupPlain(entity, lookupOptions),
|
|
139
|
+
llm: (entity) => renderLookupLlm(entity, lookupOptions),
|
|
140
|
+
json: (entity) => JSON.stringify(entity, null, 2),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
noun,
|
|
145
|
+
description: `Look up ${noun} details by name`,
|
|
146
|
+
toolDescription: `Get details for one or more ${noun} entries by name (story pack: ${source}).`,
|
|
147
|
+
namesDescription: `${capitalize(noun)} names`,
|
|
148
|
+
complete: async (partial, cmdCtx) => {
|
|
149
|
+
const ctx = requirePragmaContext(cmdCtx);
|
|
150
|
+
const names = await listEntityNames(ctx.store, lookup, source);
|
|
151
|
+
return names.filter((name) =>
|
|
152
|
+
name.toLowerCase().startsWith(partial.toLowerCase()),
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
examples: [`pragma ${noun} lookup <name>`],
|
|
156
|
+
resolve: (rt, names) =>
|
|
157
|
+
lookupMany(names, (query) =>
|
|
158
|
+
lookupPackEntity(rt.store, lookup, noun, query, source),
|
|
159
|
+
),
|
|
160
|
+
toFmtInput: (entity) => entity,
|
|
161
|
+
formatters: lookupFormatters,
|
|
162
|
+
emptyNamesError: () =>
|
|
163
|
+
PragmaError.invalidInput("names", "(empty)", {
|
|
164
|
+
recovery: {
|
|
165
|
+
message: `List available ${noun} entries.`,
|
|
166
|
+
cli: `pragma ${noun} list`,
|
|
167
|
+
mcp: { tool: `${noun}_list` },
|
|
168
|
+
},
|
|
169
|
+
}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Look up one pack entity by name via the generated query.
|
|
175
|
+
*
|
|
176
|
+
* @throws PragmaError with code `ENTITY_NOT_FOUND` and ranked suggestions
|
|
177
|
+
* when no entity matches.
|
|
178
|
+
* @note Impure — queries the ke store.
|
|
179
|
+
*/
|
|
180
|
+
async function lookupPackEntity(
|
|
181
|
+
store: Store,
|
|
182
|
+
lookup: StoryPackLookup,
|
|
183
|
+
noun: string,
|
|
184
|
+
query: string,
|
|
185
|
+
source: string,
|
|
186
|
+
): Promise<PackRow> {
|
|
187
|
+
const rows = await runSelectQuery(
|
|
188
|
+
store,
|
|
189
|
+
buildLookupQuery(lookup, query),
|
|
190
|
+
source,
|
|
191
|
+
);
|
|
192
|
+
const entity = rows.at(0);
|
|
193
|
+
if (entity) {
|
|
194
|
+
return entity;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const candidates = await listEntityNames(store, lookup, source);
|
|
198
|
+
throw PragmaError.notFound(noun, query, {
|
|
199
|
+
suggestions: suggestNames(query, candidates),
|
|
200
|
+
recovery: {
|
|
201
|
+
message: `List available ${noun} entries.`,
|
|
202
|
+
cli: `pragma ${noun} list`,
|
|
203
|
+
mcp: { tool: `${noun}_list` },
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function listEntityNames(
|
|
209
|
+
store: Store,
|
|
210
|
+
lookup: StoryPackLookup,
|
|
211
|
+
source: string,
|
|
212
|
+
): Promise<string[]> {
|
|
213
|
+
const rows = await runSelectQuery(
|
|
214
|
+
store,
|
|
215
|
+
buildLookupNamesQuery(lookup),
|
|
216
|
+
source,
|
|
217
|
+
);
|
|
218
|
+
return rows.map((row) => row.name ?? "").filter((name) => name !== "");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Quote a filter value for a shell help example when it is not a bare word.
|
|
223
|
+
*
|
|
224
|
+
* Values with whitespace or shell-sensitive characters are double-quoted
|
|
225
|
+
* so the generated `pragma … list --flag value` example stays valid to
|
|
226
|
+
* copy-paste; simple tokens render unquoted.
|
|
227
|
+
*/
|
|
228
|
+
function quoteExampleValue(value: string): string {
|
|
229
|
+
return /^[\w.-]+$/.test(value) ? value : JSON.stringify(value);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Project declared pack filters onto kernel story parameters.
|
|
234
|
+
*
|
|
235
|
+
* The declared value set becomes the parameter's enum, which the kernel
|
|
236
|
+
* projects to CLI select choices and an MCP enum schema.
|
|
237
|
+
*/
|
|
238
|
+
function projectFilterParams(
|
|
239
|
+
filters: readonly StoryPackFilter[] | undefined,
|
|
240
|
+
): StoryParam[] {
|
|
241
|
+
return (filters ?? []).map((filter) => ({
|
|
242
|
+
name: filter.param,
|
|
243
|
+
type: "string",
|
|
244
|
+
description:
|
|
245
|
+
filter.description ??
|
|
246
|
+
`Filter by ${filter.variable} (${filter.values.join(", ")})`,
|
|
247
|
+
enum: filter.values,
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function capitalize(value: string): string {
|
|
252
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
253
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { PREFIX_MAP } from "../../prefixes.js";
|
|
2
|
+
import type { ToolSpec } from "../../ToolSpec.js";
|
|
3
|
+
import type { PragmaRuntime } from "../../types/index.js";
|
|
4
|
+
import compileLookupTool from "../compileLookupTool.js";
|
|
5
|
+
import compileReadTool from "../compileReadTool.js";
|
|
6
|
+
import collectPackStories from "./collectPackStories.js";
|
|
7
|
+
import compilePackStories from "./compilePackStories.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Compile every active story pack into MCP tool specs.
|
|
11
|
+
*
|
|
12
|
+
* Mirrors `compilePackCommands` on the MCP surface: each pack yields
|
|
13
|
+
* `<noun>_list` and, when declared, `<noun>_lookup`, registered alongside
|
|
14
|
+
* the built-in tools.
|
|
15
|
+
*
|
|
16
|
+
* @param runtime - Pragma runtime carrying config, packages, and the store.
|
|
17
|
+
* @param reservedNouns - Built-in nouns packs must not shadow.
|
|
18
|
+
* @returns Tool specs for every pack story.
|
|
19
|
+
*/
|
|
20
|
+
export default function compilePackToolSpecs(
|
|
21
|
+
runtime: PragmaRuntime,
|
|
22
|
+
reservedNouns: ReadonlySet<string>,
|
|
23
|
+
): ToolSpec[] {
|
|
24
|
+
const prefixes = { ...PREFIX_MAP, ...runtime.config.prefixes };
|
|
25
|
+
const entries = collectPackStories(
|
|
26
|
+
runtime.config,
|
|
27
|
+
runtime.packages,
|
|
28
|
+
reservedNouns,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return entries.flatMap((entry) => {
|
|
32
|
+
const compiled = compilePackStories(
|
|
33
|
+
entry.definition,
|
|
34
|
+
entry.source,
|
|
35
|
+
prefixes,
|
|
36
|
+
);
|
|
37
|
+
const specs = [compileReadTool(compiled.list)];
|
|
38
|
+
if (compiled.lookup) {
|
|
39
|
+
specs.push(compileLookupTool(compiled.lookup));
|
|
40
|
+
}
|
|
41
|
+
return specs;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story packs — declarative read stories compiled through the kernel.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
buildLookupNamesQuery,
|
|
8
|
+
default as buildLookupQuery,
|
|
9
|
+
escapeSparqlString,
|
|
10
|
+
formatTerm,
|
|
11
|
+
} from "./buildLookupQuery.js";
|
|
12
|
+
export type { PackStoryEntry } from "./collectPackStories.js";
|
|
13
|
+
export { default as collectPackStories } from "./collectPackStories.js";
|
|
14
|
+
export { default as compilePackCommands } from "./compilePackCommands.js";
|
|
15
|
+
export type { CompiledPackStories } from "./compilePackStories.js";
|
|
16
|
+
export { default as compilePackStories } from "./compilePackStories.js";
|
|
17
|
+
export { default as compilePackToolSpecs } from "./compilePackToolSpecs.js";
|
|
18
|
+
export { default as runSelectQuery } from "./runSelectQuery.js";
|
|
19
|
+
export type {
|
|
20
|
+
StoryPackColumn,
|
|
21
|
+
StoryPackDefinition,
|
|
22
|
+
StoryPackField,
|
|
23
|
+
StoryPackList,
|
|
24
|
+
StoryPackLookup,
|
|
25
|
+
StoryPackSection,
|
|
26
|
+
StoryPackSource,
|
|
27
|
+
} from "./types.js";
|
|
28
|
+
export { default as validateStoryPackDefinition } from "./validateStoryPackDefinition.js";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Store } from "@canonical/ke";
|
|
2
|
+
import { PragmaError } from "#error";
|
|
3
|
+
import { buildQuery } from "../../buildQuery.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Run a pack story's SELECT and return its binding rows.
|
|
7
|
+
*
|
|
8
|
+
* The store rejects non-read queries at the engine level; this guards the
|
|
9
|
+
* result shape so a pack whose query is not a SELECT fails with a
|
|
10
|
+
* recoverable config error instead of a shape mismatch downstream.
|
|
11
|
+
*
|
|
12
|
+
* @param store - The ke store to query.
|
|
13
|
+
* @param query - SPARQL SELECT text (the store injects PREFIX declarations).
|
|
14
|
+
* @param source - The pack source, for error attribution.
|
|
15
|
+
* @returns One record per row, keyed by SELECT variable name.
|
|
16
|
+
* @throws PragmaError with code `CONFIG_ERROR` when the query is not a SELECT.
|
|
17
|
+
* @note Impure — queries the ke store.
|
|
18
|
+
*/
|
|
19
|
+
export default async function runSelectQuery(
|
|
20
|
+
store: Store,
|
|
21
|
+
query: string,
|
|
22
|
+
source: string,
|
|
23
|
+
): Promise<Record<string, string>[]> {
|
|
24
|
+
const result = await store.query(buildQuery(query));
|
|
25
|
+
if (result.type !== "select") {
|
|
26
|
+
throw PragmaError.configError(
|
|
27
|
+
`Story query in ${source} must be a SELECT (got ${result.type}).`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return result.bindings;
|
|
31
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard-noun parity pilot — the built-in `standard` read stories vs
|
|
3
|
+
* the declarative pack definition in `standardParityFixtures.ts`.
|
|
4
|
+
*
|
|
5
|
+
* Both paths run against the real @canonical/code-standards graphs,
|
|
6
|
+
* resolved from node_modules through the production loader chain — no
|
|
7
|
+
* fixture TTL. Assertions demand byte parity wherever the v0 pack
|
|
8
|
+
* format reaches; every remaining divergence is pinned exactly and
|
|
9
|
+
* must be named in PARITY_GAPS. Nothing is cut over: the built-in
|
|
10
|
+
* stories stay untouched and the pack is compiled directly.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { Store } from "@canonical/ke";
|
|
14
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
15
|
+
import { PARITY_GAPS, STANDARD_PACK_STORY } from "#testing";
|
|
16
|
+
import { parsePackageEntry } from "../../../refs/operations/parseRef.js";
|
|
17
|
+
import type { StandardListOutput } from "../../../standard/formatters/index.js";
|
|
18
|
+
import {
|
|
19
|
+
standardListStory,
|
|
20
|
+
standardLookupStory,
|
|
21
|
+
} from "../../../standard/stories.js";
|
|
22
|
+
import { bootStore } from "../../bootStore.js";
|
|
23
|
+
import compactUri from "../../compactUri.js";
|
|
24
|
+
import { PREFIX_MAP } from "../../prefixes.js";
|
|
25
|
+
import type { PragmaRuntime, StandardDetailed } from "../../types/index.js";
|
|
26
|
+
import type { LookupStoryView } from "../types.js";
|
|
27
|
+
import compilePackStories, {
|
|
28
|
+
type CompiledPackStories,
|
|
29
|
+
} from "./compilePackStories.js";
|
|
30
|
+
import validateStoryPackDefinition from "./validateStoryPackDefinition.js";
|
|
31
|
+
|
|
32
|
+
const PACK_SOURCE = "standardParityFixtures";
|
|
33
|
+
|
|
34
|
+
/** The built-in CLI default view: summary lookup, no extra params. */
|
|
35
|
+
const SUMMARY_VIEW: LookupStoryView = {
|
|
36
|
+
surface: "cli",
|
|
37
|
+
detailed: false,
|
|
38
|
+
params: {},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** Real standard names without cs:extends — full byte parity expected. */
|
|
42
|
+
const PARITY_LOOKUP_NAMES = [
|
|
43
|
+
"code/function/purity",
|
|
44
|
+
"react/component/props",
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
47
|
+
/** A real standard with cs:extends — pins the JSON compaction gap. */
|
|
48
|
+
const EXTENDS_LOOKUP_NAME = "react/component/structure/context";
|
|
49
|
+
|
|
50
|
+
/** A near-miss query — both paths must produce identical suggestions. */
|
|
51
|
+
const NEAR_MISS_NAME = "code/function/puriti";
|
|
52
|
+
|
|
53
|
+
let store: Store;
|
|
54
|
+
let rt: PragmaRuntime;
|
|
55
|
+
let packList: CompiledPackStories["list"];
|
|
56
|
+
let packLookup: NonNullable<CompiledPackStories["lookup"]>;
|
|
57
|
+
|
|
58
|
+
beforeAll(async () => {
|
|
59
|
+
const boot = await bootStore({
|
|
60
|
+
refs: [parsePackageEntry("@canonical/code-standards")],
|
|
61
|
+
});
|
|
62
|
+
store = boot.store;
|
|
63
|
+
rt = { store } as PragmaRuntime;
|
|
64
|
+
|
|
65
|
+
const compiled = compilePackStories(STANDARD_PACK_STORY, PACK_SOURCE, {
|
|
66
|
+
...PREFIX_MAP,
|
|
67
|
+
});
|
|
68
|
+
packList = compiled.list;
|
|
69
|
+
const lookup = compiled.lookup;
|
|
70
|
+
if (!lookup) {
|
|
71
|
+
throw new Error("STANDARD_PACK_STORY must declare a lookup story");
|
|
72
|
+
}
|
|
73
|
+
packLookup = lookup;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
afterAll(() => store?.dispose());
|
|
77
|
+
|
|
78
|
+
/** Resolve one name through the built-in lookup story. */
|
|
79
|
+
async function lookupBuiltin(name: string): Promise<StandardDetailed> {
|
|
80
|
+
const result = await standardLookupStory.resolve(rt, [name], {});
|
|
81
|
+
expect(result.errors).toEqual([]);
|
|
82
|
+
const entity = result.results.at(0);
|
|
83
|
+
if (entity === undefined) {
|
|
84
|
+
throw new Error(`built-in lookup found no result for "${name}"`);
|
|
85
|
+
}
|
|
86
|
+
return entity;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Resolve one name through the compiled pack lookup story. */
|
|
90
|
+
async function lookupPack(name: string): Promise<Record<string, string>> {
|
|
91
|
+
const result = await packLookup.resolve(rt, [name], {});
|
|
92
|
+
expect(result.errors).toEqual([]);
|
|
93
|
+
const entity = result.results.at(0);
|
|
94
|
+
if (entity === undefined) {
|
|
95
|
+
throw new Error(`pack lookup found no result for "${name}"`);
|
|
96
|
+
}
|
|
97
|
+
return entity;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Remove the built-in's URI field line — the one v0 lookup rendering
|
|
102
|
+
* gap (see PARITY_GAPS: "lookup uri field"). Asserts the line occurs
|
|
103
|
+
* exactly once so the transformation cannot mask other divergence.
|
|
104
|
+
*/
|
|
105
|
+
function dropUriField(text: string, marker: " URI: " | "- URI: "): string {
|
|
106
|
+
const lines = text.split("\n");
|
|
107
|
+
const remaining = lines.filter((line) => !line.startsWith(marker));
|
|
108
|
+
expect(lines.length - remaining.length).toBe(1);
|
|
109
|
+
return remaining.join("\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Assert every name appears in the text, in order. */
|
|
113
|
+
function expectNamesInOrder(text: string, names: readonly string[]): void {
|
|
114
|
+
let cursor = 0;
|
|
115
|
+
for (const name of names) {
|
|
116
|
+
const index = text.indexOf(name, cursor);
|
|
117
|
+
expect(
|
|
118
|
+
index,
|
|
119
|
+
`expected "${name}" after position ${cursor}`,
|
|
120
|
+
).toBeGreaterThanOrEqual(0);
|
|
121
|
+
cursor = index + name.length;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
describe("standard pack definition", () => {
|
|
126
|
+
it("round-trips as declarative JSON and validates as a v0 pack", () => {
|
|
127
|
+
const raw: unknown = JSON.parse(JSON.stringify(STANDARD_PACK_STORY));
|
|
128
|
+
expect(validateStoryPackDefinition(raw, PACK_SOURCE)).toEqual(
|
|
129
|
+
STANDARD_PACK_STORY,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("records every known gap as a distinct capability entry", () => {
|
|
134
|
+
expect(PARITY_GAPS.length).toBeGreaterThan(0);
|
|
135
|
+
expect(new Set(PARITY_GAPS).size).toBe(PARITY_GAPS.length);
|
|
136
|
+
for (const gap of PARITY_GAPS) {
|
|
137
|
+
expect(gap.trim().length).toBeGreaterThan(0);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("standard list parity", () => {
|
|
143
|
+
let builtinResolution: Awaited<ReturnType<typeof standardListStory.resolve>>;
|
|
144
|
+
let builtinOutput: StandardListOutput;
|
|
145
|
+
let packRows: Record<string, string>[];
|
|
146
|
+
|
|
147
|
+
beforeAll(async () => {
|
|
148
|
+
builtinResolution = await standardListStory.resolve(rt, {});
|
|
149
|
+
builtinOutput = standardListStory.toOutput(builtinResolution, {});
|
|
150
|
+
packRows = await packList.resolve(rt, {});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("resolves the same rows as the built-in resolver", () => {
|
|
154
|
+
expect(packRows.length).toBeGreaterThan(0);
|
|
155
|
+
expect(packRows).toEqual(builtinOutput.items);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("json output is byte-identical", () => {
|
|
159
|
+
const builtinJson = standardListStory.formatters.json(builtinOutput);
|
|
160
|
+
const packJson = packList.formatters.json(packList.toOutput(packRows, {}));
|
|
161
|
+
expect(packJson).toBe(builtinJson);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("MCP envelope matches the built-in summary envelope", () => {
|
|
165
|
+
expect(packList.toEnvelope(packRows)).toEqual(
|
|
166
|
+
standardListStory.toEnvelope(builtinResolution),
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("plain output carries every standard in order — layout is a recorded gap", () => {
|
|
171
|
+
const names = builtinOutput.items.map((item) => item.name);
|
|
172
|
+
const builtinPlain = standardListStory.formatters.plain(builtinOutput);
|
|
173
|
+
const packPlain = packList.formatters.plain(
|
|
174
|
+
packList.toOutput(packRows, {}),
|
|
175
|
+
);
|
|
176
|
+
expectNamesInOrder(builtinPlain, names);
|
|
177
|
+
expectNamesInOrder(packPlain, names);
|
|
178
|
+
// Layout divergence pinned — see PARITY_GAPS "list plain template".
|
|
179
|
+
expect(packPlain).not.toBe(builtinPlain);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("llm output carries every standard in order — templates are a recorded gap", () => {
|
|
183
|
+
const names = builtinOutput.items.map((item) => item.name);
|
|
184
|
+
const builtinLlm = standardListStory.formatters.llm(builtinOutput);
|
|
185
|
+
const packLlm = packList.formatters.llm(packList.toOutput(packRows, {}));
|
|
186
|
+
expectNamesInOrder(builtinLlm, names);
|
|
187
|
+
expectNamesInOrder(packLlm, names);
|
|
188
|
+
// Heading divergence pinned — see PARITY_GAPS "list llm template".
|
|
189
|
+
expect(builtinLlm.startsWith("## Standards\n")).toBe(true);
|
|
190
|
+
expect(packLlm.startsWith(`## Standard (${packRows.length})\n`)).toBe(true);
|
|
191
|
+
expect(packLlm).not.toBe(builtinLlm);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe("standard lookup parity (summary view)", () => {
|
|
196
|
+
for (const name of PARITY_LOOKUP_NAMES) {
|
|
197
|
+
it(`plain output for "${name}" matches modulo the URI field gap`, async () => {
|
|
198
|
+
const builtinPlain = standardLookupStory.formatters.plain(
|
|
199
|
+
standardLookupStory.toFmtInput(await lookupBuiltin(name), SUMMARY_VIEW),
|
|
200
|
+
);
|
|
201
|
+
const packPlain = packLookup.formatters.plain(
|
|
202
|
+
packLookup.toFmtInput(await lookupPack(name), SUMMARY_VIEW),
|
|
203
|
+
);
|
|
204
|
+
expect(packPlain).toBe(dropUriField(builtinPlain, " URI: "));
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it(`llm output for "${name}" matches modulo the URI field gap`, async () => {
|
|
208
|
+
const builtinLlm = standardLookupStory.formatters.llm(
|
|
209
|
+
standardLookupStory.toFmtInput(await lookupBuiltin(name), SUMMARY_VIEW),
|
|
210
|
+
);
|
|
211
|
+
const packLlm = packLookup.formatters.llm(
|
|
212
|
+
packLookup.toFmtInput(await lookupPack(name), SUMMARY_VIEW),
|
|
213
|
+
);
|
|
214
|
+
expect(packLlm).toBe(dropUriField(builtinLlm, "- URI: "));
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it(`json output for "${name}" is byte-identical`, async () => {
|
|
218
|
+
const builtinJson = standardLookupStory.formatters.json(
|
|
219
|
+
standardLookupStory.toFmtInput(await lookupBuiltin(name), SUMMARY_VIEW),
|
|
220
|
+
);
|
|
221
|
+
const packJson = packLookup.formatters.json(
|
|
222
|
+
packLookup.toFmtInput(await lookupPack(name), SUMMARY_VIEW),
|
|
223
|
+
);
|
|
224
|
+
expect(packJson).toBe(builtinJson);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe("standard lookup parity — cs:extends divergence", () => {
|
|
230
|
+
it("plain and llm stay byte-identical modulo the URI field", async () => {
|
|
231
|
+
const builtinInput = standardLookupStory.toFmtInput(
|
|
232
|
+
await lookupBuiltin(EXTENDS_LOOKUP_NAME),
|
|
233
|
+
SUMMARY_VIEW,
|
|
234
|
+
);
|
|
235
|
+
const packInput = packLookup.toFmtInput(
|
|
236
|
+
await lookupPack(EXTENDS_LOOKUP_NAME),
|
|
237
|
+
SUMMARY_VIEW,
|
|
238
|
+
);
|
|
239
|
+
expect(packLookup.formatters.plain(packInput)).toBe(
|
|
240
|
+
dropUriField(
|
|
241
|
+
standardLookupStory.formatters.plain(builtinInput),
|
|
242
|
+
" URI: ",
|
|
243
|
+
),
|
|
244
|
+
);
|
|
245
|
+
expect(packLookup.formatters.llm(packInput)).toBe(
|
|
246
|
+
dropUriField(standardLookupStory.formatters.llm(builtinInput), "- URI: "),
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("json diverges only on extends compaction — a recorded gap", async () => {
|
|
251
|
+
const builtinJson = standardLookupStory.formatters.json(
|
|
252
|
+
standardLookupStory.toFmtInput(
|
|
253
|
+
await lookupBuiltin(EXTENDS_LOOKUP_NAME),
|
|
254
|
+
SUMMARY_VIEW,
|
|
255
|
+
),
|
|
256
|
+
);
|
|
257
|
+
const packJson = packLookup.formatters.json(
|
|
258
|
+
packLookup.toFmtInput(
|
|
259
|
+
await lookupPack(EXTENDS_LOOKUP_NAME),
|
|
260
|
+
SUMMARY_VIEW,
|
|
261
|
+
),
|
|
262
|
+
);
|
|
263
|
+
// Divergence pinned — see PARITY_GAPS "lookup data compaction".
|
|
264
|
+
expect(packJson).not.toBe(builtinJson);
|
|
265
|
+
|
|
266
|
+
const builtinParsed = JSON.parse(builtinJson) as Record<string, string>;
|
|
267
|
+
const packParsed = JSON.parse(packJson) as Record<string, string>;
|
|
268
|
+
const packExtends = packParsed.extends;
|
|
269
|
+
if (packExtends === undefined) {
|
|
270
|
+
throw new Error(`expected cs:extends on "${EXTENDS_LOOKUP_NAME}"`);
|
|
271
|
+
}
|
|
272
|
+
// The built-in compacts extends in resolved data; the pack keeps the
|
|
273
|
+
// raw IRI. Compacting the pack value restores byte-level agreement.
|
|
274
|
+
expect({
|
|
275
|
+
...packParsed,
|
|
276
|
+
extends: compactUri(packExtends, PREFIX_MAP),
|
|
277
|
+
}).toEqual(builtinParsed);
|
|
278
|
+
expect(
|
|
279
|
+
JSON.stringify(
|
|
280
|
+
{ ...packParsed, extends: compactUri(packExtends, PREFIX_MAP) },
|
|
281
|
+
null,
|
|
282
|
+
2,
|
|
283
|
+
),
|
|
284
|
+
).toBe(builtinJson);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("standard lookup miss parity", () => {
|
|
289
|
+
it("collects identical not-found entries with ranked suggestions", async () => {
|
|
290
|
+
const builtinResult = await standardLookupStory.resolve(
|
|
291
|
+
rt,
|
|
292
|
+
[NEAR_MISS_NAME],
|
|
293
|
+
{},
|
|
294
|
+
);
|
|
295
|
+
const packResult = await packLookup.resolve(rt, [NEAR_MISS_NAME], {});
|
|
296
|
+
expect(builtinResult.results).toEqual([]);
|
|
297
|
+
expect(packResult.results).toEqual([]);
|
|
298
|
+
|
|
299
|
+
const builtinError = builtinResult.errors.at(0);
|
|
300
|
+
const packError = packResult.errors.at(0);
|
|
301
|
+
if (builtinError === undefined || packError === undefined) {
|
|
302
|
+
throw new Error("both paths must report a not-found error");
|
|
303
|
+
}
|
|
304
|
+
expect(packError).toEqual(builtinError);
|
|
305
|
+
expect(builtinError.code).toBe("ENTITY_NOT_FOUND");
|
|
306
|
+
expect(builtinError.suggestions).toContain("code/function/purity");
|
|
307
|
+
});
|
|
308
|
+
});
|