@canonical/pragma-cli 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -47
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/defaultConfig.test.ts +33 -0
- package/src/config/defaultConfig.ts +59 -0
- package/src/config/defaults.json +17 -0
- package/src/config/findProjectConfigPath.test.ts +53 -0
- package/src/config/findProjectConfigPath.ts +36 -0
- package/src/config/index.ts +17 -2
- package/src/config/mergeConfigFileUpdate.ts +58 -0
- package/src/config/parseConfigValues.ts +217 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -153
- package/src/config/readConfigLayers.test.ts +109 -0
- package/src/config/readConfigLayers.ts +118 -0
- package/src/config/resolveGlobalConfigPath.ts +13 -0
- package/src/config/resolveWriteConfigPath.test.ts +53 -0
- package/src/config/resolveWriteConfigPath.ts +31 -0
- package/src/config/types.ts +83 -1
- package/src/config/writeConfig.ts +25 -55
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/list.ts +5 -90
- package/src/domains/block/commands/lookup.ts +8 -167
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/mcp/specs.ts +16 -196
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +260 -0
- package/src/domains/config/commands/channel.test.ts +30 -3
- package/src/domains/config/commands/channel.ts +25 -8
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +32 -1
- package/src/domains/config/commands/framework.ts +26 -8
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +20 -2
- package/src/domains/config/commands/tier.ts +25 -8
- package/src/domains/config/commands/trace.test.ts +19 -2
- package/src/domains/config/commands/trace.ts +6 -3
- package/src/domains/config/formatters/channel.test.ts +20 -6
- package/src/domains/config/formatters/channel.ts +14 -11
- package/src/domains/config/formatters/framework.ts +14 -11
- package/src/domains/config/formatters/show.test.ts +27 -0
- package/src/domains/config/formatters/show.ts +27 -4
- package/src/domains/config/formatters/tier.test.ts +26 -10
- package/src/domains/config/formatters/tier.ts +19 -11
- package/src/domains/config/formatters/trace.ts +7 -7
- package/src/domains/config/mcp/specs.ts +33 -53
- package/src/domains/config/operations/show.test.ts +18 -0
- package/src/domains/config/operations/show.ts +7 -1
- package/src/domains/config/operations/types.ts +7 -0
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +1 -0
- package/src/domains/config/tasks/setChannel.ts +13 -28
- package/src/domains/config/tasks/setFramework.ts +11 -30
- package/src/domains/config/tasks/setTier.ts +10 -29
- package/src/domains/config/tasks/setTrace.ts +10 -28
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +133 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +32 -26
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
- package/src/domains/doctor/operations/checks/index.ts +1 -0
- package/src/domains/doctor/operations/checks.test.ts +160 -7
- package/src/domains/doctor/operations/runChecks.test.ts +15 -6
- package/src/domains/doctor/operations/runChecks.ts +2 -0
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/build.test.ts +5 -3
- package/src/domains/graphql/commands/build.ts +8 -16
- package/src/domains/graphql/commands/check.test.ts +5 -3
- package/src/domains/graphql/commands/check.ts +7 -13
- package/src/domains/graphql/commands/index.ts +1 -0
- package/src/domains/graphql/commands/serve.test.ts +46 -0
- package/src/domains/graphql/commands/serve.ts +141 -0
- package/src/domains/graphql/constants.ts +10 -0
- package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
- package/src/domains/graphql/helpers/gatherSources.ts +58 -0
- package/src/domains/graphql/helpers/index.ts +2 -0
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
- package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
- package/src/domains/graphql/index.ts +9 -6
- package/src/domains/graphql/operations/compileSchema.test.ts +23 -47
- package/src/domains/graphql/operations/compileSchema.ts +23 -25
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +10 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/collectInfo.test.ts +14 -3
- package/src/domains/info/operations/collectInfo.ts +12 -15
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +24 -4
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +55 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/list.ts +5 -38
- package/src/domains/modifier/commands/lookup.ts +8 -79
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/mcp/specs.ts +15 -94
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +86 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/commands/show.ts +6 -58
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +361 -64
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/mcp/specs.ts +7 -84
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.test.ts +36 -0
- package/src/domains/ontology/stories.ts +153 -0
- package/src/domains/refs/operations/updateRefs.ts +2 -38
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/commands/skills.test.ts +6 -2
- package/src/domains/setup/commands/skills.ts +6 -2
- package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
- package/src/domains/setup/helpers/runSetupTask.ts +62 -70
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/setup/operations/setupMcp.test.ts +6 -1
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +57 -18
- package/src/domains/shared/compactUri.test.ts +11 -0
- package/src/domains/shared/compactUri.ts +10 -5
- package/src/domains/shared/context.ts +2 -2
- package/src/domains/shared/contracts.ts +11 -0
- package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
- package/src/domains/shared/createInteractivePromptSession.ts +236 -0
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +158 -12
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/index.ts +4 -1
- package/src/domains/shared/loaders/localLoader.ts +2 -4
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +82 -9
- package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
- package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
- package/src/domains/shared/lookupMany.test.ts +135 -0
- package/src/domains/shared/lookupMany.ts +50 -2
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/packages.ts +10 -17
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/runtime.ts +4 -63
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +34 -3
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +102 -0
- package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
- package/src/domains/shared/stories/compileLookupTool.ts +96 -0
- package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
- package/src/domains/shared/stories/compileReadCommand.ts +48 -0
- package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
- package/src/domains/shared/stories/compileReadTool.ts +38 -0
- package/src/domains/shared/stories/condense.test.ts +18 -0
- package/src/domains/shared/stories/condense.ts +15 -0
- package/src/domains/shared/stories/index.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.ts +22 -0
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +151 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +111 -0
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +81 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +154 -0
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +406 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +158 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +50 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +751 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +724 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +55 -0
- package/src/domains/shared/stories/pack/index.ts +42 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +381 -0
- package/src/domains/shared/stories/pack/types.ts +224 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +742 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +859 -0
- package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
- package/src/domains/shared/stories/projectCliParameters.ts +57 -0
- package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
- package/src/domains/shared/stories/projectToolParams.ts +32 -0
- package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
- package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
- package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
- package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
- package/src/domains/shared/stories/types.ts +179 -0
- package/src/domains/shared/suggestions/expandGlob.ts +1 -0
- package/src/domains/shared/suggestions/suggestNames.ts +43 -10
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/skill/commands/index.ts +3 -1
- package/src/domains/skill/commands/list.test.ts +6 -2
- package/src/domains/skill/commands/list.ts +5 -63
- package/src/domains/skill/commands/lookup.test.ts +118 -0
- package/src/domains/skill/commands/lookup.ts +14 -0
- package/src/domains/skill/formatters/index.ts +1 -0
- package/src/domains/skill/formatters/lookup.test.ts +46 -0
- package/src/domains/skill/formatters/lookup.ts +57 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +0 -1
- package/src/domains/skill/index.ts +7 -3
- package/src/domains/skill/mcp/specs.ts +10 -29
- package/src/domains/skill/operations/discover.test.ts +61 -30
- package/src/domains/skill/operations/discover.ts +28 -37
- package/src/domains/skill/operations/index.ts +2 -1
- package/src/domains/skill/operations/list.test.ts +15 -7
- package/src/domains/skill/operations/lookup.test.ts +91 -0
- package/src/domains/skill/operations/lookup.ts +70 -0
- package/src/domains/skill/stories.test.ts +43 -0
- package/src/domains/skill/stories.ts +103 -0
- package/src/domains/skill/types.ts +8 -0
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +18 -2
- package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/domains/trace/tracePlugin.test.ts +1 -1
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +382 -74
- package/src/mcp/registerResources.ts +5 -348
- package/src/mcp/resources/buildGraphIndex.ts +167 -0
- package/src/mcp/resources/buildResourceList.test.ts +97 -0
- package/src/mcp/resources/buildResourceList.ts +176 -0
- package/src/mcp/resources/classifyEntity.test.ts +40 -0
- package/src/mcp/resources/classifyEntity.ts +27 -0
- package/src/mcp/resources/constants.ts +105 -0
- package/src/mcp/resources/displayPredicates.ts +62 -0
- package/src/mcp/resources/pickFirstValue.test.ts +29 -0
- package/src/mcp/resources/pickFirstValue.ts +26 -0
- package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
- package/src/mcp/resources/pickPrimaryType.ts +26 -0
- package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
- package/src/mcp/resources/rankUriCompletions.ts +64 -0
- package/src/mcp/resources/readEntity.ts +169 -0
- package/src/mcp/resources/registerResources.ts +126 -0
- package/src/mcp/resources/types.ts +128 -0
- package/src/mcp/tools/index.ts +22 -5
- package/src/mcp/tools/registerTools.test.ts +167 -34
- package/src/mcp/types.ts +4 -0
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +35 -13
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +202 -25
- package/src/testing/cli.ts +30 -2
- package/src/testing/createTestMcpClient.ts +4 -1
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +7 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +103 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +136 -82
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +26 -0
- package/src/testing/standardParityFixtures.ts +35 -0
- package/src/testing/store.ts +4 -4
- package/src/testing/storyFixtures.ts +63 -0
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -90
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -99
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -244
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -32
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -59
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -38
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -1,39 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import type { Task } from "@canonical/task";
|
|
2
|
+
import type { ConfigScope } from "../../../config/index.js";
|
|
3
|
+
import writeConfigField from "./writeConfigField.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Build a config-write task for setting or resetting the active tier.
|
|
7
7
|
*
|
|
8
|
-
* @param cwd - Working directory
|
|
8
|
+
* @param cwd - Working directory the target config layer is resolved from.
|
|
9
9
|
* @param tier - Tier path to persist, or `undefined` to reset.
|
|
10
|
-
* @
|
|
11
|
-
* @
|
|
10
|
+
* @param scope - Optional explicit target layer (`"global"` | `"local"`).
|
|
11
|
+
* @returns Task yielding the persisted field/value payload and target path.
|
|
12
|
+
* @note - Impure — reads and writes the target config file.
|
|
12
13
|
*/
|
|
13
14
|
export default function setTier(
|
|
14
15
|
cwd: string,
|
|
15
16
|
tier: string | undefined,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}> {
|
|
20
|
-
return gen(function* () {
|
|
21
|
-
// Merge onto the full existing config so other fields (e.g. `packages`,
|
|
22
|
-
// `trace`) survive. `readConfig` normalises defaults (tier: undefined,
|
|
23
|
-
// channel: "normal"); strip those to keep the file minimal, then apply
|
|
24
|
-
// the tier change (`undefined` resets it).
|
|
25
|
-
const current = readConfig(cwd);
|
|
26
|
-
const { tier: _current, channel, ...rest } = current;
|
|
27
|
-
const next = {
|
|
28
|
-
...rest,
|
|
29
|
-
...(tier !== undefined ? { tier } : {}),
|
|
30
|
-
...(channel !== "normal" ? { channel } : {}),
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
yield* $(
|
|
34
|
-
writeFile(resolveConfigPath(cwd), `${JSON.stringify(next, null, 2)}\n`),
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
return { field: "tier", value: tier };
|
|
38
|
-
});
|
|
17
|
+
scope?: ConfigScope,
|
|
18
|
+
): Task<{ field: "tier"; value: string | undefined; path: string }> {
|
|
19
|
+
return writeConfigField(cwd, "tier", tier, scope);
|
|
39
20
|
}
|
|
@@ -1,38 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import type { Task } from "@canonical/task";
|
|
2
|
+
import type { ConfigScope } from "../../../config/index.js";
|
|
3
|
+
import writeConfigField from "./writeConfigField.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Build a config-write task for enabling or disabling query tracing.
|
|
7
7
|
*
|
|
8
|
-
* @param cwd - Working directory
|
|
8
|
+
* @param cwd - Working directory the target config layer is resolved from.
|
|
9
9
|
* @param trace - `true` to enable, `false` to disable.
|
|
10
|
-
* @
|
|
10
|
+
* @param scope - Optional explicit target layer (`"global"` | `"local"`).
|
|
11
|
+
* @returns Task yielding the persisted field/value payload and target path.
|
|
12
|
+
* @note - Impure — reads and writes the target config file.
|
|
11
13
|
*/
|
|
12
14
|
export default function setTrace(
|
|
13
15
|
cwd: string,
|
|
14
16
|
trace: boolean,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// survive — rebuilding from a fixed field list silently drops everything
|
|
19
|
-
// not listed, and `packages` is runtime-consumed, so dropping it is data
|
|
20
|
-
// loss. `readConfig` normalises defaults (tier: undefined, channel:
|
|
21
|
-
// "normal"); strip those so we keep the file minimal, matching the
|
|
22
|
-
// existing convention of not persisting unset defaults.
|
|
23
|
-
const current = readConfig(cwd);
|
|
24
|
-
const { tier, channel, ...rest } = current;
|
|
25
|
-
const next = {
|
|
26
|
-
...rest,
|
|
27
|
-
...(tier !== undefined ? { tier } : {}),
|
|
28
|
-
...(channel !== "normal" ? { channel } : {}),
|
|
29
|
-
trace,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
yield* $(
|
|
33
|
-
writeFile(resolveConfigPath(cwd), `${JSON.stringify(next, null, 2)}\n`),
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
return { field: "trace", value: trace };
|
|
37
|
-
});
|
|
17
|
+
scope?: ConfigScope,
|
|
18
|
+
): Task<{ field: "trace"; value: boolean | undefined; path: string }> {
|
|
19
|
+
return writeConfigField(cwd, "trace", trace, scope);
|
|
38
20
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { $, gen, type Task, writeFile } from "@canonical/task";
|
|
2
|
+
import {
|
|
3
|
+
type ConfigScope,
|
|
4
|
+
type ConfigUpdate,
|
|
5
|
+
mergeConfigFileUpdate,
|
|
6
|
+
resolveWriteConfigPath,
|
|
7
|
+
} from "../../../config/index.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Build a config-write task for one field.
|
|
11
|
+
*
|
|
12
|
+
* Resolves the target layer with the global-first write rule (explicit
|
|
13
|
+
* scope wins, else the nearest existing project file, else the global XDG
|
|
14
|
+
* file), merges the update into that file's own contents — never copying
|
|
15
|
+
* values from another layer — and writes through the tracked `writeFile`
|
|
16
|
+
* effect so the change participates in dry-run and undo. `undefined`
|
|
17
|
+
* removes the field from the target file.
|
|
18
|
+
*
|
|
19
|
+
* @param cwd - Working directory the project layer is resolved from.
|
|
20
|
+
* @param field - Config field to set or remove.
|
|
21
|
+
* @param value - New value, or `undefined` to remove the field.
|
|
22
|
+
* @param scope - Optional explicit target layer.
|
|
23
|
+
* @returns Task yielding the persisted field/value payload and the path written.
|
|
24
|
+
* @note Impure — resolves and reads the target file, writes via effect.
|
|
25
|
+
*/
|
|
26
|
+
export default function writeConfigField<F extends keyof ConfigUpdate>(
|
|
27
|
+
cwd: string,
|
|
28
|
+
field: F,
|
|
29
|
+
value: ConfigUpdate[F],
|
|
30
|
+
scope?: ConfigScope,
|
|
31
|
+
): Task<{ field: F; value: ConfigUpdate[F]; path: string }> {
|
|
32
|
+
return gen(function* () {
|
|
33
|
+
const path = resolveWriteConfigPath(cwd, scope);
|
|
34
|
+
const update = { [field]: value } as ConfigUpdate;
|
|
35
|
+
const content = mergeConfigFileUpdate(path, update);
|
|
36
|
+
|
|
37
|
+
yield* $(writeFile(path, content));
|
|
38
|
+
|
|
39
|
+
return { field, value, path };
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled generator packs.
|
|
3
|
+
*
|
|
4
|
+
* The generator sets pragma ships by default, statically imported so the
|
|
5
|
+
* single-file build embeds them. Each package's full generator set is exposed —
|
|
6
|
+
* every generator, always — grouped into `create <noun>` commands and
|
|
7
|
+
* `create_<noun>` MCP tools by {@link compileGeneratorPack}.
|
|
8
|
+
*
|
|
9
|
+
* This is the write-side counterpart of the bundled story packs. In a later
|
|
10
|
+
* slice these move into the packages themselves (declared, dynamically
|
|
11
|
+
* resolved) so the core ships no generator wiring at all; today they are
|
|
12
|
+
* bundled transitional entries, like the story packs.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { generators as applicationGenerators } from "@canonical/summon-application";
|
|
16
|
+
import { generators as componentGenerators } from "@canonical/summon-component";
|
|
17
|
+
import { generators as packageGenerators } from "@canonical/summon-package";
|
|
18
|
+
import compileGeneratorPack, {
|
|
19
|
+
type CompiledGeneratorPack,
|
|
20
|
+
type GeneratorSet,
|
|
21
|
+
} from "./compileGeneratorPack.js";
|
|
22
|
+
|
|
23
|
+
/** Every generator set pragma bundles, in surface order. */
|
|
24
|
+
const BUNDLED_GENERATOR_SETS: readonly GeneratorSet[] = [
|
|
25
|
+
componentGenerators,
|
|
26
|
+
packageGenerators,
|
|
27
|
+
applicationGenerators,
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
let cached: CompiledGeneratorPack | undefined;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Compile every bundled generator pack into its CLI commands and MCP specs.
|
|
34
|
+
*
|
|
35
|
+
* Memoized — the generator sets are static, and both the CLI and MCP
|
|
36
|
+
* registration paths call this.
|
|
37
|
+
*
|
|
38
|
+
* @returns The merged commands and specs across all bundled packs.
|
|
39
|
+
*/
|
|
40
|
+
export default function bundledGeneratorPacks(): CompiledGeneratorPack {
|
|
41
|
+
cached ??= BUNDLED_GENERATOR_SETS.map(compileGeneratorPack).reduce(
|
|
42
|
+
(merged, pack) => ({
|
|
43
|
+
commands: [...merged.commands, ...pack.commands],
|
|
44
|
+
specs: [...merged.specs, ...pack.specs],
|
|
45
|
+
}),
|
|
46
|
+
{ commands: [], specs: [] },
|
|
47
|
+
);
|
|
48
|
+
return cached;
|
|
49
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { AnyGenerator } from "@canonical/summon-core";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import type { PragmaRuntime } from "../../shared/types/index.js";
|
|
4
|
+
import bundledGeneratorPacks from "./bundled.js";
|
|
5
|
+
import compileGeneratorPack from "./compileGeneratorPack.js";
|
|
6
|
+
|
|
7
|
+
describe("compileGeneratorPack (bundled)", () => {
|
|
8
|
+
const { commands, specs } = bundledGeneratorPacks();
|
|
9
|
+
const cliPaths = commands.map((c) => c.path.join(" "));
|
|
10
|
+
const toolNames = specs.map((s) => s.name);
|
|
11
|
+
|
|
12
|
+
it("exposes every generator from every bundled package as a create command", () => {
|
|
13
|
+
// component (react/svelte/lit → one noun), package, and the four
|
|
14
|
+
// application-package generators.
|
|
15
|
+
expect(cliPaths).toEqual([
|
|
16
|
+
"create component",
|
|
17
|
+
"create package",
|
|
18
|
+
"create application",
|
|
19
|
+
"create domain",
|
|
20
|
+
"create route",
|
|
21
|
+
"create wrapper",
|
|
22
|
+
]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("emits a matching MCP tool per noun", () => {
|
|
26
|
+
expect(toolNames).toEqual([
|
|
27
|
+
"create_component",
|
|
28
|
+
"create_package",
|
|
29
|
+
"create_application",
|
|
30
|
+
"create_domain",
|
|
31
|
+
"create_route",
|
|
32
|
+
"create_wrapper",
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("dispatches a multi-variant noun via a `variant` positional select", () => {
|
|
37
|
+
const component = commands.find(
|
|
38
|
+
(c) => c.path.join(" ") === "create component",
|
|
39
|
+
);
|
|
40
|
+
const variant = component?.parameters.find((p) => p.name === "variant");
|
|
41
|
+
expect(variant?.type).toBe("select");
|
|
42
|
+
expect(variant?.positional).toBe(true);
|
|
43
|
+
expect(variant?.choices?.map((ch) => ch.value)).toEqual([
|
|
44
|
+
"react",
|
|
45
|
+
"svelte",
|
|
46
|
+
"lit",
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("gives the multi-variant MCP tool a `variant` enum parameter", () => {
|
|
51
|
+
const component = specs.find((s) => s.name === "create_component");
|
|
52
|
+
expect(component?.params?.variant?.enum).toEqual([
|
|
53
|
+
"react",
|
|
54
|
+
"svelte",
|
|
55
|
+
"lit",
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("collapses a single-variant noun to a plain `create <noun>` (no variant arg)", () => {
|
|
60
|
+
const application = commands.find(
|
|
61
|
+
(c) => c.path.join(" ") === "create application",
|
|
62
|
+
);
|
|
63
|
+
expect(application?.parameters.some((p) => p.name === "variant")).toBe(
|
|
64
|
+
false,
|
|
65
|
+
);
|
|
66
|
+
// Its prompts still project — appPath comes from the generator.
|
|
67
|
+
expect(application?.parameters.some((p) => p.name === "appPath")).toBe(
|
|
68
|
+
true,
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("derives single-generator MCP params from the generator's prompts", () => {
|
|
73
|
+
const pkg = specs.find((s) => s.name === "create_package");
|
|
74
|
+
expect(pkg?.params).toBeDefined();
|
|
75
|
+
// The package generator prompts for a package name.
|
|
76
|
+
expect(Object.keys(pkg?.params ?? {}).length).toBeGreaterThan(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("never advertises params the runner pins (runInstall is forced off)", () => {
|
|
80
|
+
// The runner always executes a dry-run plan with runInstall: false, so
|
|
81
|
+
// no create tool may advertise the parameter it would ignore.
|
|
82
|
+
for (const spec of specs) {
|
|
83
|
+
expect(spec.params).not.toHaveProperty("runInstall");
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/** Minimal synthetic generator for edge-case tests. */
|
|
89
|
+
function makeGenerator(name: string, generate?: () => never): AnyGenerator {
|
|
90
|
+
return {
|
|
91
|
+
meta: {
|
|
92
|
+
name,
|
|
93
|
+
displayName: name,
|
|
94
|
+
description: `Test generator ${name}`,
|
|
95
|
+
version: "0.0.0",
|
|
96
|
+
},
|
|
97
|
+
prompts: [],
|
|
98
|
+
generate:
|
|
99
|
+
generate ??
|
|
100
|
+
(() => {
|
|
101
|
+
throw new Error("not exercised");
|
|
102
|
+
}),
|
|
103
|
+
} as unknown as AnyGenerator;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
describe("compileGeneratorPack — edge cases", () => {
|
|
107
|
+
let warnings: string[];
|
|
108
|
+
let warn: ReturnType<typeof vi.spyOn>;
|
|
109
|
+
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
warnings = [];
|
|
112
|
+
warn = vi
|
|
113
|
+
.spyOn(process.stderr, "write")
|
|
114
|
+
.mockImplementation((chunk: string | Uint8Array) => {
|
|
115
|
+
warnings.push(String(chunk));
|
|
116
|
+
return true;
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
afterEach(() => {
|
|
121
|
+
warn.mockRestore();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("warns and exposes only the first generator of a mixed bare/variant noun", () => {
|
|
125
|
+
const { commands, specs } = compileGeneratorPack({
|
|
126
|
+
thing: makeGenerator("thing"),
|
|
127
|
+
"thing/extra": makeGenerator("thing/extra"),
|
|
128
|
+
});
|
|
129
|
+
expect(commands.map((c) => c.path.join(" "))).toEqual(["create thing"]);
|
|
130
|
+
expect(specs.map((s) => s.name)).toEqual(["create_thing"]);
|
|
131
|
+
expect(warnings.join("")).toContain("mixes bare and variant keys");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("maps a generator that throws to INVALID_INPUT with its message as recovery", async () => {
|
|
135
|
+
const throwing = makeGenerator("boom", () => {
|
|
136
|
+
throw new Error("boom requires --both flags");
|
|
137
|
+
});
|
|
138
|
+
const { specs } = compileGeneratorPack({ boom: throwing });
|
|
139
|
+
const spec = specs.at(0);
|
|
140
|
+
const rt = { cwd: "/tmp" } as PragmaRuntime;
|
|
141
|
+
await expect(spec?.execute(rt, {})).rejects.toMatchObject({
|
|
142
|
+
code: "INVALID_INPUT",
|
|
143
|
+
recovery: { message: "boom requires --both flags" },
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("rejects an unknown variant with INVALID_INPUT and the valid options", async () => {
|
|
148
|
+
const { specs } = compileGeneratorPack({
|
|
149
|
+
"widget/a": makeGenerator("widget/a"),
|
|
150
|
+
"widget/b": makeGenerator("widget/b"),
|
|
151
|
+
});
|
|
152
|
+
const spec = specs.at(0);
|
|
153
|
+
const rt = { cwd: "/tmp" } as PragmaRuntime;
|
|
154
|
+
await expect(spec?.execute(rt, { variant: "c" })).rejects.toMatchObject({
|
|
155
|
+
code: "INVALID_INPUT",
|
|
156
|
+
validOptions: ["a", "b"],
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator packs — the write-side analog of story packs.
|
|
3
|
+
*
|
|
4
|
+
* A generator pack is a set of summon generators (a package's `generators`
|
|
5
|
+
* export). This compiler enumerates them, groups by noun, and emits `create
|
|
6
|
+
* <noun>` CLI commands and `create_<noun>` MCP tools — the same data-driven
|
|
7
|
+
* surface story packs give read commands. The core stops hardcoding which
|
|
8
|
+
* generators exist; a package's full set is exposed automatically.
|
|
9
|
+
*
|
|
10
|
+
* Key convention (summon's own): `"<noun>/<variant>"` (e.g. `component/react`)
|
|
11
|
+
* groups under `<noun>` with a `<variant>` discriminator; a bare `"<noun>"`
|
|
12
|
+
* (e.g. `package`) is a single generator. A noun with one variant collapses to
|
|
13
|
+
* `create <noun>`; a noun with several becomes `create <noun> <variant>`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
type CommandDefinition,
|
|
18
|
+
executeGenerator,
|
|
19
|
+
generatorToCommand,
|
|
20
|
+
type ParameterDefinition,
|
|
21
|
+
} from "@canonical/cli-core";
|
|
22
|
+
import type { AnyGenerator, PromptDefinition } from "@canonical/summon-core";
|
|
23
|
+
import { PragmaError } from "#error";
|
|
24
|
+
import type { ToolParamDef, ToolSpec } from "../../shared/ToolSpec.js";
|
|
25
|
+
import renderGeneratorUi from "../renderGeneratorUi.js";
|
|
26
|
+
|
|
27
|
+
/** A package's generator set: generator key → generator. */
|
|
28
|
+
export type GeneratorSet = Readonly<Record<string, AnyGenerator>>;
|
|
29
|
+
|
|
30
|
+
/** Compiled surfaces for one generator pack. */
|
|
31
|
+
export interface CompiledGeneratorPack {
|
|
32
|
+
readonly commands: readonly CommandDefinition[];
|
|
33
|
+
readonly specs: readonly ToolSpec[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A generator grouped under a noun, with its optional variant. */
|
|
37
|
+
interface GroupedGenerator {
|
|
38
|
+
readonly variant?: string;
|
|
39
|
+
readonly gen: AnyGenerator;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Split a generator key into `{ noun, variant? }` on the first slash. */
|
|
43
|
+
function parseGeneratorKey(key: string): { noun: string; variant?: string } {
|
|
44
|
+
const slash = key.indexOf("/");
|
|
45
|
+
if (slash === -1) return { noun: key };
|
|
46
|
+
return { noun: key.slice(0, slash), variant: key.slice(slash + 1) };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Group a generator set by noun, preserving declaration order. */
|
|
50
|
+
function groupByNoun(
|
|
51
|
+
generators: GeneratorSet,
|
|
52
|
+
): Map<string, GroupedGenerator[]> {
|
|
53
|
+
const groups = new Map<string, GroupedGenerator[]>();
|
|
54
|
+
for (const [key, gen] of Object.entries(generators)) {
|
|
55
|
+
const { noun, variant } = parseGeneratorKey(key);
|
|
56
|
+
const group = groups.get(noun) ?? [];
|
|
57
|
+
group.push(variant !== undefined ? { variant, gen } : { gen });
|
|
58
|
+
groups.set(noun, group);
|
|
59
|
+
}
|
|
60
|
+
return groups;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Prompt type → tool param type, mirroring cli-core's `promptToParameter`. */
|
|
64
|
+
const PROMPT_TYPE_MAP: Record<PromptDefinition["type"], ToolParamDef["type"]> =
|
|
65
|
+
{
|
|
66
|
+
text: "string",
|
|
67
|
+
confirm: "boolean",
|
|
68
|
+
select: "string",
|
|
69
|
+
multiselect: "string[]",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Map a summon prompt to a neutral MCP tool parameter. */
|
|
73
|
+
function promptToToolParam(prompt: PromptDefinition): ToolParamDef {
|
|
74
|
+
const enumValues =
|
|
75
|
+
prompt.type === "confirm"
|
|
76
|
+
? undefined
|
|
77
|
+
: prompt.choices?.map((choice) => choice.value);
|
|
78
|
+
return {
|
|
79
|
+
type: PROMPT_TYPE_MAP[prompt.type],
|
|
80
|
+
description: prompt.message,
|
|
81
|
+
// A prompt with a default or a `when` condition is answerable without
|
|
82
|
+
// input; only an unconditional, defaultless prompt is required.
|
|
83
|
+
optional: prompt.default !== undefined || prompt.when !== undefined,
|
|
84
|
+
...(enumValues ? { enum: enumValues } : {}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generator answers the MCP tool runner pins to fixed values.
|
|
90
|
+
*
|
|
91
|
+
* MCP create tools always produce a dry-run plan, so `runInstall` is forced
|
|
92
|
+
* off regardless of input. This one map drives BOTH the schema (pinned
|
|
93
|
+
* answers are excluded from the advertised params — a tool must not
|
|
94
|
+
* advertise a parameter it ignores) and the runtime override in
|
|
95
|
+
* {@link runGeneratorTool}, so the two can never drift.
|
|
96
|
+
*/
|
|
97
|
+
const PINNED_TOOL_ANSWERS: Readonly<Record<string, unknown>> = {
|
|
98
|
+
runInstall: false,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Build the MCP param map for a generator from its prompts, omitting
|
|
103
|
+
* prompts whose answers are pinned by {@link PINNED_TOOL_ANSWERS}.
|
|
104
|
+
*/
|
|
105
|
+
function toolParamsFor(gen: AnyGenerator): Record<string, ToolParamDef> {
|
|
106
|
+
return Object.fromEntries(
|
|
107
|
+
gen.prompts
|
|
108
|
+
.filter((prompt) => !(prompt.name in PINNED_TOOL_ANSWERS))
|
|
109
|
+
.map((prompt) => [prompt.name, promptToToolParam(prompt)]),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Run one generator as an MCP tool call: json-mode dry-run plan.
|
|
115
|
+
*
|
|
116
|
+
* A generator that throws (e.g. an unmet hard requirement such as
|
|
117
|
+
* application's ssr/router pairing) is mapped to a typed INVALID_INPUT
|
|
118
|
+
* carrying the generator's own message as the recovery hint — the generic
|
|
119
|
+
* replacement for the per-generator validation the hand-written specs did.
|
|
120
|
+
*/
|
|
121
|
+
async function runGeneratorTool(
|
|
122
|
+
gen: AnyGenerator,
|
|
123
|
+
rt: { cwd: string },
|
|
124
|
+
params: Record<string, unknown>,
|
|
125
|
+
discriminator?: string,
|
|
126
|
+
): Promise<{ data: unknown }> {
|
|
127
|
+
const batchCtx = {
|
|
128
|
+
cwd: rt.cwd,
|
|
129
|
+
globalFlags: { llm: false, format: "json" as const, verbose: false },
|
|
130
|
+
};
|
|
131
|
+
// Apply the pinned answers (never install during a dry-run plan); the
|
|
132
|
+
// discriminator is dispatch metadata, not a generator answer.
|
|
133
|
+
const genParams: Record<string, unknown> = {
|
|
134
|
+
...params,
|
|
135
|
+
...PINNED_TOOL_ANSWERS,
|
|
136
|
+
};
|
|
137
|
+
if (discriminator) delete genParams[discriminator];
|
|
138
|
+
try {
|
|
139
|
+
const result = await executeGenerator(gen, genParams, batchCtx);
|
|
140
|
+
if (result.tag === "output") {
|
|
141
|
+
// json mode: result.value is already the structured plan.
|
|
142
|
+
return { data: result.value };
|
|
143
|
+
}
|
|
144
|
+
throw PragmaError.invalidInput("parameters", "(no plan produced)", {
|
|
145
|
+
recovery: { message: `The ${gen.meta.name} generator rejected them.` },
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error instanceof PragmaError) throw error;
|
|
149
|
+
throw PragmaError.invalidInput("parameters", "(rejected)", {
|
|
150
|
+
recovery: {
|
|
151
|
+
message: error instanceof Error ? error.message : String(error),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Build the variant → generator resolver shared by the CLI and MCP dispatch.
|
|
159
|
+
*
|
|
160
|
+
* @returns A function resolving a raw `variant` param to its generator.
|
|
161
|
+
* @throws PragmaError with code INVALID_INPUT for a missing/unknown variant.
|
|
162
|
+
*/
|
|
163
|
+
function buildVariantResolver(
|
|
164
|
+
group: readonly GroupedGenerator[],
|
|
165
|
+
variants: readonly string[],
|
|
166
|
+
): (variant: unknown) => AnyGenerator {
|
|
167
|
+
const byVariant = new Map(group.map((g) => [g.variant, g.gen]));
|
|
168
|
+
return (variant) => {
|
|
169
|
+
const gen =
|
|
170
|
+
typeof variant === "string" ? byVariant.get(variant) : undefined;
|
|
171
|
+
if (!gen) {
|
|
172
|
+
throw PragmaError.invalidInput(
|
|
173
|
+
"variant",
|
|
174
|
+
typeof variant === "string" ? variant : "(missing)",
|
|
175
|
+
{ validOptions: [...variants] },
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return gen;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Compile a package's generator set into `create <noun>` commands and
|
|
184
|
+
* `create_<noun>` MCP tools.
|
|
185
|
+
*
|
|
186
|
+
* @note Impure — warns on stderr when a degenerate group (duplicate noun
|
|
187
|
+
* with mixed bare/variant keys) forces generators to be skipped.
|
|
188
|
+
*/
|
|
189
|
+
export default function compileGeneratorPack(
|
|
190
|
+
generators: GeneratorSet,
|
|
191
|
+
): CompiledGeneratorPack {
|
|
192
|
+
const commands: CommandDefinition[] = [];
|
|
193
|
+
const specs: ToolSpec[] = [];
|
|
194
|
+
|
|
195
|
+
for (const [noun, group] of groupByNoun(generators)) {
|
|
196
|
+
const [first, ...rest] = group;
|
|
197
|
+
if (!first) continue; // groupByNoun never emits empty groups
|
|
198
|
+
|
|
199
|
+
const variants = group
|
|
200
|
+
.map((g) => g.variant)
|
|
201
|
+
.filter((v): v is string => v !== undefined);
|
|
202
|
+
|
|
203
|
+
if (rest.length > 0 && variants.length === group.length) {
|
|
204
|
+
commands.push(buildVariantCommand(noun, first.gen, group, variants));
|
|
205
|
+
specs.push(buildVariantSpec(noun, first.gen, group, variants));
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (rest.length > 0) {
|
|
210
|
+
// Mixed bare/variant keys under one noun is a malformed set — expose
|
|
211
|
+
// the first generator rather than none, but never drop the rest
|
|
212
|
+
// silently.
|
|
213
|
+
process.stderr.write(
|
|
214
|
+
`Warning: generator noun "${noun}" mixes bare and variant keys; ` +
|
|
215
|
+
`only "${first.gen.meta.name}" is exposed.\n`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
commands.push(buildSingleCommand(noun, first.gen));
|
|
219
|
+
specs.push(buildSingleSpec(noun, first.gen));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { commands, specs };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** `create <noun>` for a single generator, with summon's rich Ink UI. */
|
|
226
|
+
function buildSingleCommand(
|
|
227
|
+
noun: string,
|
|
228
|
+
gen: AnyGenerator,
|
|
229
|
+
): CommandDefinition {
|
|
230
|
+
const base = generatorToCommand(["create", noun], gen);
|
|
231
|
+
return {
|
|
232
|
+
...base,
|
|
233
|
+
execute: (params, ctx) => renderGeneratorUi(gen, params, ctx),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* `create <noun> <variant>` dispatch over several generators.
|
|
239
|
+
*
|
|
240
|
+
* Help and parameters project from the reference (first-declared) generator —
|
|
241
|
+
* variants of one noun are assumed prompt-compatible, the same assumption the
|
|
242
|
+
* hand-written component command made.
|
|
243
|
+
*/
|
|
244
|
+
function buildVariantCommand(
|
|
245
|
+
noun: string,
|
|
246
|
+
reference: AnyGenerator,
|
|
247
|
+
group: readonly GroupedGenerator[],
|
|
248
|
+
variants: readonly string[],
|
|
249
|
+
): CommandDefinition {
|
|
250
|
+
const resolve = buildVariantResolver(group, variants);
|
|
251
|
+
const base = generatorToCommand(["create", noun], reference);
|
|
252
|
+
const variantParam: ParameterDefinition = {
|
|
253
|
+
name: "variant",
|
|
254
|
+
description: `Which ${noun} to scaffold (${variants.join(", ")})`,
|
|
255
|
+
type: "select",
|
|
256
|
+
choices: variants.map((v) => ({ label: v, value: v })),
|
|
257
|
+
positional: true,
|
|
258
|
+
required: true,
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
...base,
|
|
262
|
+
parameters: [variantParam, ...base.parameters],
|
|
263
|
+
// async so an unknown variant rejects instead of throwing synchronously.
|
|
264
|
+
execute: async (params, ctx) =>
|
|
265
|
+
renderGeneratorUi(resolve(params.variant), params, ctx),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** `create_<noun>` MCP tool for a single generator. */
|
|
270
|
+
function buildSingleSpec(noun: string, gen: AnyGenerator): ToolSpec {
|
|
271
|
+
return {
|
|
272
|
+
name: `create_${noun}`,
|
|
273
|
+
description: `${gen.meta.description} Returns a JSON generation plan (dry-run).`,
|
|
274
|
+
params: toolParamsFor(gen),
|
|
275
|
+
readOnly: false,
|
|
276
|
+
destructive: false,
|
|
277
|
+
execute: (rt, params) => runGeneratorTool(gen, rt, params),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* `create_<noun>` MCP tool with a `variant` discriminator.
|
|
283
|
+
*
|
|
284
|
+
* The param schema projects from the reference generator's prompts (see
|
|
285
|
+
* {@link buildVariantCommand} for the compatibility assumption).
|
|
286
|
+
*/
|
|
287
|
+
function buildVariantSpec(
|
|
288
|
+
noun: string,
|
|
289
|
+
reference: AnyGenerator,
|
|
290
|
+
group: readonly GroupedGenerator[],
|
|
291
|
+
variants: readonly string[],
|
|
292
|
+
): ToolSpec {
|
|
293
|
+
const resolve = buildVariantResolver(group, variants);
|
|
294
|
+
return {
|
|
295
|
+
name: `create_${noun}`,
|
|
296
|
+
description: `${reference.meta.description} Returns a JSON generation plan (dry-run).`,
|
|
297
|
+
params: {
|
|
298
|
+
variant: {
|
|
299
|
+
type: "string",
|
|
300
|
+
description: `Which ${noun} to scaffold`,
|
|
301
|
+
optional: false,
|
|
302
|
+
enum: [...variants],
|
|
303
|
+
},
|
|
304
|
+
...toolParamsFor(reference),
|
|
305
|
+
},
|
|
306
|
+
readOnly: false,
|
|
307
|
+
destructive: false,
|
|
308
|
+
// async so an unknown variant rejects instead of throwing synchronously.
|
|
309
|
+
execute: async (rt, params) =>
|
|
310
|
+
runGeneratorTool(resolve(params.variant), rt, params, "variant"),
|
|
311
|
+
};
|
|
312
|
+
}
|