@canonical/pragma-cli 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -47
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/defaultConfig.test.ts +33 -0
- package/src/config/defaultConfig.ts +59 -0
- package/src/config/defaults.json +17 -0
- package/src/config/findProjectConfigPath.test.ts +53 -0
- package/src/config/findProjectConfigPath.ts +36 -0
- package/src/config/index.ts +17 -2
- package/src/config/mergeConfigFileUpdate.ts +58 -0
- package/src/config/parseConfigValues.ts +217 -0
- package/src/config/readConfig.test.ts +104 -13
- package/src/config/readConfig.ts +12 -153
- package/src/config/readConfigLayers.test.ts +109 -0
- package/src/config/readConfigLayers.ts +118 -0
- package/src/config/resolveGlobalConfigPath.ts +13 -0
- package/src/config/resolveWriteConfigPath.test.ts +53 -0
- package/src/config/resolveWriteConfigPath.ts +31 -0
- package/src/config/types.ts +83 -1
- package/src/config/writeConfig.ts +25 -55
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/list.ts +5 -90
- package/src/domains/block/commands/lookup.ts +8 -167
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/mcp/specs.ts +16 -196
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
- package/src/domains/block/stories.test.ts +28 -0
- package/src/domains/block/stories.ts +260 -0
- package/src/domains/config/commands/channel.test.ts +30 -3
- package/src/domains/config/commands/channel.ts +25 -8
- package/src/domains/config/commands/configScope.ts +46 -0
- package/src/domains/config/commands/framework.test.ts +32 -1
- package/src/domains/config/commands/framework.ts +26 -8
- package/src/domains/config/commands/show.ts +5 -45
- package/src/domains/config/commands/tier.test.ts +20 -2
- package/src/domains/config/commands/tier.ts +25 -8
- package/src/domains/config/commands/trace.test.ts +19 -2
- package/src/domains/config/commands/trace.ts +6 -3
- package/src/domains/config/formatters/channel.test.ts +20 -6
- package/src/domains/config/formatters/channel.ts +14 -11
- package/src/domains/config/formatters/framework.ts +14 -11
- package/src/domains/config/formatters/show.test.ts +27 -0
- package/src/domains/config/formatters/show.ts +27 -4
- package/src/domains/config/formatters/tier.test.ts +26 -10
- package/src/domains/config/formatters/tier.ts +19 -11
- package/src/domains/config/formatters/trace.ts +7 -7
- package/src/domains/config/mcp/specs.ts +33 -53
- package/src/domains/config/operations/show.test.ts +18 -0
- package/src/domains/config/operations/show.ts +7 -1
- package/src/domains/config/operations/types.ts +7 -0
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/config/stories.ts +42 -0
- package/src/domains/config/tasks/index.ts +1 -0
- package/src/domains/config/tasks/setChannel.ts +13 -28
- package/src/domains/config/tasks/setFramework.ts +11 -30
- package/src/domains/config/tasks/setTier.ts +10 -29
- package/src/domains/config/tasks/setTrace.ts +10 -28
- package/src/domains/config/tasks/writeConfigField.ts +41 -0
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +133 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +32 -26
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
- package/src/domains/doctor/operations/checks/index.ts +1 -0
- package/src/domains/doctor/operations/checks.test.ts +160 -7
- package/src/domains/doctor/operations/runChecks.test.ts +15 -6
- package/src/domains/doctor/operations/runChecks.ts +2 -0
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
- package/src/domains/graph/helpers/resolveUri.ts +28 -21
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/build.test.ts +5 -3
- package/src/domains/graphql/commands/build.ts +8 -16
- package/src/domains/graphql/commands/check.test.ts +5 -3
- package/src/domains/graphql/commands/check.ts +7 -13
- package/src/domains/graphql/commands/index.ts +1 -0
- package/src/domains/graphql/commands/serve.test.ts +46 -0
- package/src/domains/graphql/commands/serve.ts +141 -0
- package/src/domains/graphql/constants.ts +10 -0
- package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
- package/src/domains/graphql/helpers/gatherSources.ts +58 -0
- package/src/domains/graphql/helpers/index.ts +2 -0
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
- package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
- package/src/domains/graphql/index.ts +9 -6
- package/src/domains/graphql/operations/compileSchema.test.ts +23 -47
- package/src/domains/graphql/operations/compileSchema.ts +23 -25
- package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
- package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +10 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/collectInfo.test.ts +14 -3
- package/src/domains/info/operations/collectInfo.ts +12 -15
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +24 -4
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +55 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/list.ts +5 -38
- package/src/domains/modifier/commands/lookup.ts +8 -79
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/mcp/specs.ts +15 -94
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
- package/src/domains/modifier/stories.test.ts +20 -0
- package/src/domains/modifier/stories.ts +86 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/list.ts +5 -58
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/commands/show.ts +6 -58
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +361 -64
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/mcp/specs.ts +7 -84
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.test.ts +36 -0
- package/src/domains/ontology/stories.ts +153 -0
- package/src/domains/refs/operations/updateRefs.ts +2 -38
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/commands/skills.test.ts +6 -2
- package/src/domains/setup/commands/skills.ts +6 -2
- package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
- package/src/domains/setup/helpers/runSetupTask.ts +62 -70
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/setup/operations/setupMcp.test.ts +6 -1
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +57 -18
- package/src/domains/shared/compactUri.test.ts +11 -0
- package/src/domains/shared/compactUri.ts +10 -5
- package/src/domains/shared/context.ts +2 -2
- package/src/domains/shared/contracts.ts +11 -0
- package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
- package/src/domains/shared/createInteractivePromptSession.ts +236 -0
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
- package/src/domains/shared/loaders/bundledLoader.ts +158 -12
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/index.ts +4 -1
- package/src/domains/shared/loaders/localLoader.ts +2 -4
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +82 -9
- package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
- package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
- package/src/domains/shared/lookupMany.test.ts +135 -0
- package/src/domains/shared/lookupMany.ts +50 -2
- package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
- package/src/domains/shared/mergeAndParseRefs.ts +44 -0
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/packages.ts +10 -17
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
- package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/runtime.ts +4 -63
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +34 -3
- package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
- package/src/domains/shared/stories/compileLookupCommand.ts +102 -0
- package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
- package/src/domains/shared/stories/compileLookupTool.ts +96 -0
- package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
- package/src/domains/shared/stories/compileReadCommand.ts +48 -0
- package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
- package/src/domains/shared/stories/compileReadTool.ts +38 -0
- package/src/domains/shared/stories/condense.test.ts +18 -0
- package/src/domains/shared/stories/condense.ts +15 -0
- package/src/domains/shared/stories/index.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
- package/src/domains/shared/stories/normalizeNames.ts +22 -0
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +151 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +111 -0
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +81 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +154 -0
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +406 -0
- package/src/domains/shared/stories/pack/collectPackStories.ts +158 -0
- package/src/domains/shared/stories/pack/compilePackCommands.ts +50 -0
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +751 -0
- package/src/domains/shared/stories/pack/compilePackStories.ts +724 -0
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +55 -0
- package/src/domains/shared/stories/pack/index.ts +42 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +381 -0
- package/src/domains/shared/stories/pack/types.ts +224 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +742 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +859 -0
- package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
- package/src/domains/shared/stories/projectCliParameters.ts +57 -0
- package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
- package/src/domains/shared/stories/projectToolParams.ts +32 -0
- package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
- package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
- package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
- package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
- package/src/domains/shared/stories/types.ts +179 -0
- package/src/domains/shared/suggestions/expandGlob.ts +1 -0
- package/src/domains/shared/suggestions/suggestNames.ts +43 -10
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/skill/commands/index.ts +3 -1
- package/src/domains/skill/commands/list.test.ts +6 -2
- package/src/domains/skill/commands/list.ts +5 -63
- package/src/domains/skill/commands/lookup.test.ts +118 -0
- package/src/domains/skill/commands/lookup.ts +14 -0
- package/src/domains/skill/formatters/index.ts +1 -0
- package/src/domains/skill/formatters/lookup.test.ts +46 -0
- package/src/domains/skill/formatters/lookup.ts +57 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +0 -1
- package/src/domains/skill/index.ts +7 -3
- package/src/domains/skill/mcp/specs.ts +10 -29
- package/src/domains/skill/operations/discover.test.ts +61 -30
- package/src/domains/skill/operations/discover.ts +28 -37
- package/src/domains/skill/operations/index.ts +2 -1
- package/src/domains/skill/operations/list.test.ts +15 -7
- package/src/domains/skill/operations/lookup.test.ts +91 -0
- package/src/domains/skill/operations/lookup.ts +70 -0
- package/src/domains/skill/stories.test.ts +43 -0
- package/src/domains/skill/stories.ts +103 -0
- package/src/domains/skill/types.ts +8 -0
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +18 -2
- package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/domains/trace/tracePlugin.test.ts +1 -1
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +382 -74
- package/src/mcp/registerResources.ts +5 -348
- package/src/mcp/resources/buildGraphIndex.ts +167 -0
- package/src/mcp/resources/buildResourceList.test.ts +97 -0
- package/src/mcp/resources/buildResourceList.ts +176 -0
- package/src/mcp/resources/classifyEntity.test.ts +40 -0
- package/src/mcp/resources/classifyEntity.ts +27 -0
- package/src/mcp/resources/constants.ts +105 -0
- package/src/mcp/resources/displayPredicates.ts +62 -0
- package/src/mcp/resources/pickFirstValue.test.ts +29 -0
- package/src/mcp/resources/pickFirstValue.ts +26 -0
- package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
- package/src/mcp/resources/pickPrimaryType.ts +26 -0
- package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
- package/src/mcp/resources/rankUriCompletions.ts +64 -0
- package/src/mcp/resources/readEntity.ts +169 -0
- package/src/mcp/resources/registerResources.ts +126 -0
- package/src/mcp/resources/types.ts +128 -0
- package/src/mcp/tools/index.ts +22 -5
- package/src/mcp/tools/registerTools.test.ts +167 -34
- package/src/mcp/types.ts +4 -0
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +35 -13
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +202 -25
- package/src/testing/cli.ts +30 -2
- package/src/testing/createTestMcpClient.ts +4 -1
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +7 -0
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +103 -0
- package/src/testing/integration/executor-golden.test.ts +145 -0
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +136 -82
- package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
- package/src/testing/setupXdgIsolation.ts +26 -0
- package/src/testing/standardParityFixtures.ts +35 -0
- package/src/testing/store.ts +4 -4
- package/src/testing/storyFixtures.ts +63 -0
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -90
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -99
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -244
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -32
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -59
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -38
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/pipeline/runInteractiveCommand.ts +0 -198
|
@@ -7,9 +7,13 @@ import buildSkillsCommand from "./skills.js";
|
|
|
7
7
|
|
|
8
8
|
const TMP_ROOT = join(tmpdir(), `pragma-setup-cmd-${Date.now()}`);
|
|
9
9
|
|
|
10
|
+
/** One source per skill directory, matching the loader granularity. */
|
|
10
11
|
const TEST_SOURCES = [
|
|
11
12
|
{
|
|
12
|
-
dir: join(
|
|
13
|
+
dir: join(
|
|
14
|
+
TMP_ROOT,
|
|
15
|
+
"node_modules/@canonical/design-system/skills/design-audit",
|
|
16
|
+
),
|
|
13
17
|
packageName: "@canonical/design-system",
|
|
14
18
|
},
|
|
15
19
|
];
|
|
@@ -21,7 +25,7 @@ vi.mock("../../skill/helpers/resolveSkillSources.js", () => ({
|
|
|
21
25
|
beforeAll(() => {
|
|
22
26
|
mkdirSync(TMP_ROOT, { recursive: true });
|
|
23
27
|
|
|
24
|
-
const dir =
|
|
28
|
+
const dir = TEST_SOURCES.at(0)?.dir ?? "";
|
|
25
29
|
mkdirSync(dir, { recursive: true });
|
|
26
30
|
writeFileSync(
|
|
27
31
|
join(dir, "SKILL.md"),
|
|
@@ -2,9 +2,11 @@ import {
|
|
|
2
2
|
type CommandDefinition,
|
|
3
3
|
type CommandResult,
|
|
4
4
|
createOutputResult,
|
|
5
|
+
runGeneratorTask,
|
|
5
6
|
} from "@canonical/cli-core";
|
|
6
7
|
import { detectHarnesses } from "@canonical/harnesses";
|
|
7
|
-
import { collectEffects
|
|
8
|
+
import { collectEffects } from "@canonical/task";
|
|
9
|
+
import { runTask } from "@canonical/task/node";
|
|
8
10
|
import { PragmaError } from "#error";
|
|
9
11
|
import type { PragmaContext } from "../../shared/context.js";
|
|
10
12
|
import { selectFormatter } from "../../shared/formatters.js";
|
|
@@ -94,7 +96,9 @@ export default function buildSkillsCommand(): CommandDefinition {
|
|
|
94
96
|
});
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
// Run the symlink task through the shared execution core, like the
|
|
100
|
+
// other setup commands.
|
|
101
|
+
const result = await runGeneratorTask(task);
|
|
98
102
|
|
|
99
103
|
const output: SetupSkillsOutput = { result, dryRun: false };
|
|
100
104
|
|
|
@@ -1,33 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { answerPromptWithDefaults } from "@canonical/cli-core";
|
|
2
|
+
import { pure, type Task, writeFile } from "@canonical/task";
|
|
2
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
const { createInterfaceMock, dryRunMock, runTaskMock, runUndoMock } =
|
|
5
|
-
vi.hoisted(() => ({
|
|
6
|
-
createInterfaceMock: vi.fn(),
|
|
7
|
-
dryRunMock: vi.fn(() => ({ effects: [] })),
|
|
8
|
-
runTaskMock: vi.fn(),
|
|
9
|
-
runUndoMock: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
async function loadRunSetupTask() {
|
|
13
|
-
vi.resetModules();
|
|
14
|
-
vi.doMock("node:readline", () => ({
|
|
15
|
-
createInterface: createInterfaceMock,
|
|
16
|
-
}));
|
|
17
|
-
vi.doMock("@canonical/task", async (importOriginal) => {
|
|
18
|
-
const actual = await importOriginal<typeof import("@canonical/task")>();
|
|
19
|
-
return {
|
|
20
|
-
...actual,
|
|
21
|
-
dryRun: dryRunMock,
|
|
22
|
-
runTask: runTaskMock,
|
|
23
|
-
runUndo: runUndoMock,
|
|
24
|
-
};
|
|
25
|
-
});
|
|
26
|
-
return (await import("./runSetupTask.js")).default;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
4
|
import runSetupTask from "./runSetupTask.js";
|
|
30
5
|
|
|
6
|
+
const {
|
|
7
|
+
runGeneratorTaskMock,
|
|
8
|
+
runUndoMock,
|
|
9
|
+
sessionAnswerMock,
|
|
10
|
+
sessionDisposeMock,
|
|
11
|
+
} = vi.hoisted(() => ({
|
|
12
|
+
runGeneratorTaskMock: vi.fn(),
|
|
13
|
+
runUndoMock: vi.fn(),
|
|
14
|
+
sessionAnswerMock: vi.fn(),
|
|
15
|
+
sessionDisposeMock: vi.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
// runSetupTask orchestrates: dry-run/undo use real formatting and runUndo,
|
|
19
|
+
// while production runs through the shared execution core. Mock the core,
|
|
20
|
+
// runUndo, and the interactive prompt session, so the wiring and lifecycle
|
|
21
|
+
// stay under test with real effect formatting.
|
|
22
|
+
vi.mock("@canonical/cli-core", async (importOriginal) => ({
|
|
23
|
+
...(await importOriginal<typeof import("@canonical/cli-core")>()),
|
|
24
|
+
runGeneratorTask: runGeneratorTaskMock,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
vi.mock("@canonical/task/node", async (importOriginal) => ({
|
|
28
|
+
...(await importOriginal<typeof import("@canonical/task/node")>()),
|
|
29
|
+
runUndo: runUndoMock,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock("../../shared/createInteractivePromptSession.js", () => ({
|
|
33
|
+
default: () => ({
|
|
34
|
+
answerPrompt: sessionAnswerMock,
|
|
35
|
+
wasInterrupted: () => false,
|
|
36
|
+
dispose: sessionDisposeMock,
|
|
37
|
+
}),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const succeed = async (): Promise<undefined> => undefined;
|
|
41
|
+
|
|
31
42
|
describe("runSetupTask", () => {
|
|
32
43
|
let stderrSpy: ReturnType<typeof vi.spyOn>;
|
|
33
44
|
|
|
@@ -40,13 +51,16 @@ describe("runSetupTask", () => {
|
|
|
40
51
|
|
|
41
52
|
afterEach(() => {
|
|
42
53
|
stderrSpy.mockRestore();
|
|
43
|
-
vi.doUnmock("node:readline");
|
|
44
|
-
vi.doUnmock("@canonical/task");
|
|
45
54
|
});
|
|
46
55
|
|
|
56
|
+
// ===========================================================================
|
|
57
|
+
// Dry-run mode
|
|
58
|
+
// ===========================================================================
|
|
59
|
+
|
|
47
60
|
it("returns output result in dry-run mode", async () => {
|
|
48
|
-
const
|
|
49
|
-
|
|
61
|
+
const result = await runSetupTask(writeFile("/tmp/test.txt", "hello"), {
|
|
62
|
+
dryRun: true,
|
|
63
|
+
});
|
|
50
64
|
|
|
51
65
|
expect(result.tag).toBe("output");
|
|
52
66
|
if (result.tag === "output") {
|
|
@@ -54,176 +68,152 @@ describe("runSetupTask", () => {
|
|
|
54
68
|
expect(text).toContain("Dry run");
|
|
55
69
|
expect(text).toContain("/tmp/test.txt");
|
|
56
70
|
}
|
|
71
|
+
expect(runGeneratorTaskMock).not.toHaveBeenCalled();
|
|
57
72
|
});
|
|
58
73
|
|
|
59
74
|
it("returns json output in dry-run + json mode", async () => {
|
|
60
|
-
const
|
|
61
|
-
const result = await runSetupTask(task, {
|
|
75
|
+
const result = await runSetupTask(writeFile("/tmp/test.txt", "hello"), {
|
|
62
76
|
dryRun: true,
|
|
63
77
|
format: "json",
|
|
64
78
|
});
|
|
65
79
|
|
|
66
80
|
expect(result.tag).toBe("output");
|
|
67
81
|
if (result.tag === "output") {
|
|
68
|
-
const
|
|
69
|
-
const parsed = JSON.parse(text);
|
|
82
|
+
const parsed = JSON.parse(result.render.plain(result.value));
|
|
70
83
|
expect(Array.isArray(parsed)).toBe(true);
|
|
71
84
|
expect(parsed[0].action).toBe("WriteFile");
|
|
72
85
|
}
|
|
73
86
|
});
|
|
74
87
|
|
|
75
88
|
it("returns llm markdown in dry-run + llm mode", async () => {
|
|
76
|
-
const
|
|
77
|
-
|
|
89
|
+
const result = await runSetupTask(writeFile("/tmp/test.txt", "hello"), {
|
|
90
|
+
dryRun: true,
|
|
91
|
+
llm: true,
|
|
92
|
+
});
|
|
78
93
|
|
|
79
94
|
expect(result.tag).toBe("output");
|
|
80
95
|
if (result.tag === "output") {
|
|
81
|
-
|
|
82
|
-
expect(text).toContain("## Dry Run");
|
|
83
|
-
expect(text).toContain("WriteFile");
|
|
96
|
+
expect(result.render.plain(result.value)).toContain("Dry Run");
|
|
84
97
|
}
|
|
85
98
|
});
|
|
86
99
|
|
|
87
|
-
it("
|
|
88
|
-
const
|
|
89
|
-
|
|
100
|
+
it("shows the no-visible-effects message when a dry-run collects none", async () => {
|
|
101
|
+
const result = await runSetupTask(pure(undefined) as Task<void>, {
|
|
102
|
+
dryRun: true,
|
|
103
|
+
verbose: false,
|
|
104
|
+
});
|
|
90
105
|
|
|
91
|
-
expect(result.tag).toBe("
|
|
92
|
-
if (result.tag === "
|
|
93
|
-
expect(result.
|
|
106
|
+
expect(result.tag).toBe("output");
|
|
107
|
+
if (result.tag === "output") {
|
|
108
|
+
expect(result.render.plain(result.value)).toContain("no visible effects");
|
|
94
109
|
}
|
|
95
110
|
});
|
|
96
111
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
112
|
+
// ===========================================================================
|
|
113
|
+
// Production execution — through the shared execution core
|
|
114
|
+
// ===========================================================================
|
|
115
|
+
|
|
116
|
+
it("runs production through the shared execution core and returns exit 0", async () => {
|
|
117
|
+
runGeneratorTaskMock.mockImplementation(succeed);
|
|
100
118
|
|
|
119
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), { yes: true });
|
|
120
|
+
|
|
121
|
+
expect(runGeneratorTaskMock).toHaveBeenCalledTimes(1);
|
|
101
122
|
expect(result.tag).toBe("exit");
|
|
102
|
-
if (result.tag === "exit")
|
|
103
|
-
expect(result.code).toBe(0);
|
|
104
|
-
}
|
|
123
|
+
if (result.tag === "exit") expect(result.code).toBe(0);
|
|
105
124
|
});
|
|
106
125
|
|
|
107
|
-
it("
|
|
108
|
-
|
|
109
|
-
|
|
126
|
+
it("wires the auto-confirm handler in yes mode", async () => {
|
|
127
|
+
runGeneratorTaskMock.mockImplementation(async (_task, options) => {
|
|
128
|
+
expect(options.promptHandler).toBe(answerPromptWithDefaults);
|
|
129
|
+
return succeed();
|
|
130
|
+
});
|
|
110
131
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const text = result.render.plain(result.value);
|
|
114
|
-
expect(text).toContain("no visible effects");
|
|
115
|
-
}
|
|
132
|
+
await runSetupTask(writeFile("/tmp/x", "y"), { yes: true });
|
|
133
|
+
expect(runGeneratorTaskMock).toHaveBeenCalledTimes(1);
|
|
116
134
|
});
|
|
117
135
|
|
|
118
|
-
it("
|
|
119
|
-
|
|
120
|
-
|
|
136
|
+
it("wires an interactive prompt session when not in yes mode and disposes it", async () => {
|
|
137
|
+
runGeneratorTaskMock.mockImplementation(async (_task, options) => {
|
|
138
|
+
expect(options.promptHandler).toBe(sessionAnswerMock);
|
|
139
|
+
expect(sessionDisposeMock).not.toHaveBeenCalled();
|
|
140
|
+
return succeed();
|
|
141
|
+
});
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
expect(stderrSpy).toHaveBeenCalledWith("Nothing to undo.\n");
|
|
143
|
+
await runSetupTask(writeFile("/tmp/x", "y"), {});
|
|
144
|
+
expect(runGeneratorTaskMock).toHaveBeenCalledTimes(1);
|
|
145
|
+
expect(sessionDisposeMock).toHaveBeenCalledTimes(1);
|
|
127
146
|
});
|
|
128
147
|
|
|
129
|
-
it("
|
|
130
|
-
|
|
131
|
-
expect(
|
|
132
|
-
await options.promptHandler({
|
|
133
|
-
_tag: "Prompt",
|
|
134
|
-
question: { type: "confirm", default: false, message: "Continue?" },
|
|
135
|
-
}),
|
|
136
|
-
).toBe(false);
|
|
137
|
-
expect(
|
|
138
|
-
await options.promptHandler({
|
|
139
|
-
_tag: "Prompt",
|
|
140
|
-
question: {
|
|
141
|
-
type: "select",
|
|
142
|
-
choices: [{ value: "zsh" }],
|
|
143
|
-
message: "Shell?",
|
|
144
|
-
},
|
|
145
|
-
}),
|
|
146
|
-
).toBe("zsh");
|
|
147
|
-
expect(
|
|
148
|
-
await options.promptHandler({
|
|
149
|
-
_tag: "Prompt",
|
|
150
|
-
question: { type: "multiselect", message: "Pick many" },
|
|
151
|
-
}),
|
|
152
|
-
).toEqual([]);
|
|
153
|
-
expect(
|
|
154
|
-
await options.promptHandler({
|
|
155
|
-
_tag: "Prompt",
|
|
156
|
-
question: { type: "input", message: "Name?" },
|
|
157
|
-
}),
|
|
158
|
-
).toBe("");
|
|
159
|
-
});
|
|
148
|
+
it("disposes the prompt session even when production execution fails", async () => {
|
|
149
|
+
runGeneratorTaskMock.mockRejectedValue(new Error("boom"));
|
|
160
150
|
|
|
161
|
-
const
|
|
162
|
-
const result = await runSetupTaskWithMocks({} as never, { yes: true });
|
|
151
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), {});
|
|
163
152
|
|
|
164
153
|
expect(result.tag).toBe("exit");
|
|
165
|
-
|
|
166
|
-
expect(result.code).toBe(0);
|
|
167
|
-
}
|
|
154
|
+
expect(sessionDisposeMock).toHaveBeenCalledTimes(1);
|
|
168
155
|
});
|
|
169
156
|
|
|
170
|
-
it("
|
|
171
|
-
|
|
172
|
-
question: (_prompt: string, cb: (answer: string) => void) => cb(""),
|
|
173
|
-
close: vi.fn(),
|
|
174
|
-
});
|
|
175
|
-
runTaskMock.mockImplementation(async (_task, options) => {
|
|
176
|
-
expect(
|
|
177
|
-
await options.promptHandler({
|
|
178
|
-
_tag: "Prompt",
|
|
179
|
-
question: { type: "confirm", default: true, message: "Continue?" },
|
|
180
|
-
}),
|
|
181
|
-
).toBe(true);
|
|
157
|
+
it("suppresses debug logs unless verbose during production", async () => {
|
|
158
|
+
runGeneratorTaskMock.mockImplementation(async (_task, options) => {
|
|
182
159
|
options.onLog("debug", "hidden");
|
|
183
160
|
options.onLog("info", "shown");
|
|
161
|
+
return succeed();
|
|
184
162
|
});
|
|
185
163
|
|
|
186
|
-
|
|
187
|
-
const result = await runSetupTaskWithMocks({} as never, {});
|
|
164
|
+
await runSetupTask(writeFile("/tmp/x", "y"), { yes: true });
|
|
188
165
|
|
|
189
|
-
expect(result.tag).toBe("exit");
|
|
190
166
|
expect(stderrSpy).toHaveBeenCalledWith("shown\n");
|
|
191
167
|
expect(stderrSpy).not.toHaveBeenCalledWith("hidden\n");
|
|
192
168
|
});
|
|
193
169
|
|
|
194
|
-
it("
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
170
|
+
it("emits debug logs when verbose during production", async () => {
|
|
171
|
+
runGeneratorTaskMock.mockImplementation(async (_task, options) => {
|
|
172
|
+
options.onLog("debug", "shown-debug");
|
|
173
|
+
return succeed();
|
|
198
174
|
});
|
|
199
|
-
runTaskMock.mockImplementation(async (_task, options) => {
|
|
200
|
-
expect(
|
|
201
|
-
await options.promptHandler({
|
|
202
|
-
_tag: "Prompt",
|
|
203
|
-
question: { type: "confirm", default: false, message: "Continue?" },
|
|
204
|
-
}),
|
|
205
|
-
).toBe(false);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const runSetupTaskWithMocks = await loadRunSetupTask();
|
|
209
|
-
const result = await runSetupTaskWithMocks({} as never, {});
|
|
210
175
|
|
|
211
|
-
|
|
176
|
+
await runSetupTask(writeFile("/tmp/x", "y"), { yes: true, verbose: true });
|
|
177
|
+
expect(stderrSpy).toHaveBeenCalledWith("shown-debug\n");
|
|
212
178
|
});
|
|
213
179
|
|
|
214
180
|
it("returns exit 1 when production execution fails", async () => {
|
|
215
|
-
|
|
181
|
+
runGeneratorTaskMock.mockRejectedValue(new Error("permission denied"));
|
|
216
182
|
|
|
217
|
-
const
|
|
218
|
-
const result = await runSetupTaskWithMocks({} as never, { yes: true });
|
|
183
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), { yes: true });
|
|
219
184
|
|
|
220
185
|
expect(result.tag).toBe("exit");
|
|
221
|
-
if (result.tag === "exit")
|
|
222
|
-
expect(result.code).toBe(1);
|
|
223
|
-
}
|
|
186
|
+
if (result.tag === "exit") expect(result.code).toBe(1);
|
|
224
187
|
expect(stderrSpy).toHaveBeenCalledWith("Setup failed: permission denied\n");
|
|
225
188
|
});
|
|
226
189
|
|
|
190
|
+
it("stringifies a non-Error production rejection", async () => {
|
|
191
|
+
runGeneratorTaskMock.mockRejectedValue("raw failure");
|
|
192
|
+
|
|
193
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), { yes: true });
|
|
194
|
+
|
|
195
|
+
expect(result.tag).toBe("exit");
|
|
196
|
+
if (result.tag === "exit") expect(result.code).toBe(1);
|
|
197
|
+
expect(stderrSpy).toHaveBeenCalledWith("Setup failed: raw failure\n");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ===========================================================================
|
|
201
|
+
// Undo mode
|
|
202
|
+
// ===========================================================================
|
|
203
|
+
|
|
204
|
+
it("reports nothing to undo when no undos are collected", async () => {
|
|
205
|
+
runUndoMock.mockResolvedValue({ undoCount: 0 });
|
|
206
|
+
|
|
207
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), {
|
|
208
|
+
undo: true,
|
|
209
|
+
yes: true,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
expect(result.tag).toBe("exit");
|
|
213
|
+
if (result.tag === "exit") expect(result.code).toBe(0);
|
|
214
|
+
expect(stderrSpy).toHaveBeenCalledWith("Nothing to undo.\n");
|
|
215
|
+
});
|
|
216
|
+
|
|
227
217
|
it("writes verbose undo logs and the singular completion message", async () => {
|
|
228
218
|
runUndoMock.mockImplementation(async (_task, options) => {
|
|
229
219
|
options.onLog("debug", "debug line");
|
|
@@ -231,8 +221,7 @@ describe("runSetupTask", () => {
|
|
|
231
221
|
return { undoCount: 1 };
|
|
232
222
|
});
|
|
233
223
|
|
|
234
|
-
const
|
|
235
|
-
const result = await runSetupTaskWithMocks({} as never, {
|
|
224
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), {
|
|
236
225
|
undo: true,
|
|
237
226
|
yes: true,
|
|
238
227
|
verbose: true,
|
|
@@ -249,8 +238,7 @@ describe("runSetupTask", () => {
|
|
|
249
238
|
it("writes the plural undo completion message", async () => {
|
|
250
239
|
runUndoMock.mockResolvedValue({ undoCount: 2 });
|
|
251
240
|
|
|
252
|
-
const
|
|
253
|
-
const result = await runSetupTaskWithMocks({} as never, {
|
|
241
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), {
|
|
254
242
|
undo: true,
|
|
255
243
|
yes: true,
|
|
256
244
|
});
|
|
@@ -264,16 +252,26 @@ describe("runSetupTask", () => {
|
|
|
264
252
|
it("returns exit 1 when undo fails", async () => {
|
|
265
253
|
runUndoMock.mockRejectedValue(new Error("cannot undo"));
|
|
266
254
|
|
|
267
|
-
const
|
|
268
|
-
const result = await runSetupTaskWithMocks({} as never, {
|
|
255
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), {
|
|
269
256
|
undo: true,
|
|
270
257
|
yes: true,
|
|
271
258
|
});
|
|
272
259
|
|
|
273
260
|
expect(result.tag).toBe("exit");
|
|
274
|
-
if (result.tag === "exit")
|
|
275
|
-
expect(result.code).toBe(1);
|
|
276
|
-
}
|
|
261
|
+
if (result.tag === "exit") expect(result.code).toBe(1);
|
|
277
262
|
expect(stderrSpy).toHaveBeenCalledWith("Undo failed: cannot undo\n");
|
|
278
263
|
});
|
|
264
|
+
|
|
265
|
+
it("stringifies a non-Error undo rejection", async () => {
|
|
266
|
+
runUndoMock.mockRejectedValue("raw undo failure");
|
|
267
|
+
|
|
268
|
+
const result = await runSetupTask(writeFile("/tmp/x", "y"), {
|
|
269
|
+
undo: true,
|
|
270
|
+
yes: true,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(result.tag).toBe("exit");
|
|
274
|
+
if (result.tag === "exit") expect(result.code).toBe(1);
|
|
275
|
+
expect(stderrSpy).toHaveBeenCalledWith("Undo failed: raw undo failure\n");
|
|
276
|
+
});
|
|
279
277
|
});
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { createInterface } from "node:readline";
|
|
2
1
|
import {
|
|
2
|
+
answerPromptWithDefaults,
|
|
3
3
|
type CommandResult,
|
|
4
4
|
createExitResult,
|
|
5
5
|
createOutputResult,
|
|
6
6
|
formatEffectLine,
|
|
7
7
|
isVisibleEffect,
|
|
8
|
+
runGeneratorTask,
|
|
8
9
|
} from "@canonical/cli-core";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type Task,
|
|
15
|
-
} from "@canonical/task";
|
|
10
|
+
import { dryRun, type Effect, type Task } from "@canonical/task";
|
|
11
|
+
import { runUndo } from "@canonical/task/node";
|
|
12
|
+
import createInteractivePromptSession, {
|
|
13
|
+
type InteractivePromptSession,
|
|
14
|
+
} from "../../shared/createInteractivePromptSession.js";
|
|
16
15
|
import type { LogLevel } from "../types.js";
|
|
17
16
|
|
|
18
17
|
/** Options controlling how {@link runSetupTask} executes. */
|
|
@@ -26,55 +25,41 @@ export interface SetupTaskOptions {
|
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* @returns The default value for the prompt type.
|
|
28
|
+
* Pick the prompt handler for a run: auto-confirm defaults under `--yes`, else
|
|
29
|
+
* an interactive readline session (whose lifetime the caller owns via the
|
|
30
|
+
* returned `dispose`).
|
|
33
31
|
*/
|
|
34
|
-
function
|
|
35
|
-
effect: Effect & { _tag: "Prompt" }
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
function selectPromptHandler(yes: boolean): {
|
|
33
|
+
handler: (effect: Effect & { _tag: "Prompt" }) => Promise<unknown>;
|
|
34
|
+
wasInterrupted: () => boolean;
|
|
35
|
+
dispose: () => void;
|
|
36
|
+
} {
|
|
37
|
+
if (yes) {
|
|
38
|
+
return {
|
|
39
|
+
handler: answerPromptWithDefaults,
|
|
40
|
+
wasInterrupted: () => false,
|
|
41
|
+
dispose: () => {},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const session: InteractivePromptSession = createInteractivePromptSession();
|
|
45
|
+
return {
|
|
46
|
+
handler: session.answerPrompt,
|
|
47
|
+
wasInterrupted: session.wasInterrupted,
|
|
48
|
+
dispose: session.dispose,
|
|
49
|
+
};
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* @param effect - A Prompt effect to resolve interactively.
|
|
50
|
-
* @returns The user's answer.
|
|
53
|
+
* Build a task log sink that writes to stderr, hiding debug lines unless
|
|
54
|
+
* verbose is set.
|
|
51
55
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
):
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const hint = defaultYes ? "Y/n" : "y/N";
|
|
60
|
-
const rl = createInterface({
|
|
61
|
-
input: process.stdin,
|
|
62
|
-
output: process.stderr,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const answer = await new Promise<string>((resolve) => {
|
|
66
|
-
rl.question(`${q.message} [${hint}] `, (ans) => {
|
|
67
|
-
rl.close();
|
|
68
|
-
resolve(ans.trim());
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (answer === "") return defaultYes;
|
|
73
|
-
return answer.toLowerCase().startsWith("y");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Non-confirm prompts fall back to defaults in D15
|
|
77
|
-
return q.default;
|
|
56
|
+
function createStderrLogger(
|
|
57
|
+
verbose: boolean,
|
|
58
|
+
): (level: LogLevel, message: string) => void {
|
|
59
|
+
return (level, message) => {
|
|
60
|
+
if (level === "debug" && !verbose) return;
|
|
61
|
+
process.stderr.write(`${message}\n`);
|
|
62
|
+
};
|
|
78
63
|
}
|
|
79
64
|
|
|
80
65
|
/**
|
|
@@ -150,7 +135,8 @@ function formatDryRunJson(effects: readonly Effect[]): string {
|
|
|
150
135
|
* Supports three execution modes:
|
|
151
136
|
* - `--dry-run`: collects effects without executing, formats for display.
|
|
152
137
|
* - `--undo`: walks the task tree, collects undo operations, executes in reverse.
|
|
153
|
-
* - default:
|
|
138
|
+
* - default: execution through {@link runGeneratorTask}, with readline-based
|
|
139
|
+
* prompts on stderr.
|
|
154
140
|
*
|
|
155
141
|
* @param task - The setup Task to execute.
|
|
156
142
|
* @param options - Execution options (dryRun, undo, yes, verbose, llm, format).
|
|
@@ -182,14 +168,12 @@ export default async function runSetupTask(
|
|
|
182
168
|
|
|
183
169
|
// Undo mode: walk the task tree, collect undos, execute in reverse
|
|
184
170
|
if (options.undo) {
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
process.stderr.write(`${message}\n`);
|
|
192
|
-
};
|
|
171
|
+
const {
|
|
172
|
+
handler: promptHandler,
|
|
173
|
+
wasInterrupted,
|
|
174
|
+
dispose,
|
|
175
|
+
} = selectPromptHandler(options.yes ?? false);
|
|
176
|
+
const onLog = createStderrLogger(verbose);
|
|
193
177
|
|
|
194
178
|
try {
|
|
195
179
|
const result = await runUndo(task, { promptHandler, onLog });
|
|
@@ -202,28 +186,36 @@ export default async function runSetupTask(
|
|
|
202
186
|
}
|
|
203
187
|
return createExitResult(0);
|
|
204
188
|
} catch (err) {
|
|
189
|
+
if (wasInterrupted()) {
|
|
190
|
+
return createExitResult(130);
|
|
191
|
+
}
|
|
205
192
|
const message = err instanceof Error ? err.message : String(err);
|
|
206
193
|
process.stderr.write(`Undo failed: ${message}\n`);
|
|
207
194
|
return createExitResult(1);
|
|
195
|
+
} finally {
|
|
196
|
+
dispose();
|
|
208
197
|
}
|
|
209
198
|
}
|
|
210
199
|
|
|
211
200
|
// Production execution
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
process.stderr.write(`${message}\n`);
|
|
219
|
-
};
|
|
201
|
+
const {
|
|
202
|
+
handler: promptHandler,
|
|
203
|
+
wasInterrupted,
|
|
204
|
+
dispose,
|
|
205
|
+
} = selectPromptHandler(options.yes ?? false);
|
|
206
|
+
const onLog = createStderrLogger(verbose);
|
|
220
207
|
|
|
221
208
|
try {
|
|
222
|
-
await
|
|
209
|
+
await runGeneratorTask(task, { promptHandler, onLog });
|
|
223
210
|
return createExitResult(0);
|
|
224
211
|
} catch (err) {
|
|
212
|
+
if (wasInterrupted()) {
|
|
213
|
+
return createExitResult(130);
|
|
214
|
+
}
|
|
225
215
|
const message = err instanceof Error ? err.message : String(err);
|
|
226
216
|
process.stderr.write(`Setup failed: ${message}\n`);
|
|
227
217
|
return createExitResult(1);
|
|
218
|
+
} finally {
|
|
219
|
+
dispose();
|
|
228
220
|
}
|
|
229
221
|
}
|
|
@@ -2,21 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
import type { CommandDefinition } from "@canonical/cli-core";
|
|
4
4
|
import {
|
|
5
|
-
allCommand,
|
|
6
5
|
completionsCommand,
|
|
7
6
|
lspCommand,
|
|
8
7
|
mcpCommand,
|
|
8
|
+
setupCommand,
|
|
9
9
|
skillsCommand,
|
|
10
10
|
} from "./commands/index.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Return all setup command definitions.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* The top-level `pragma setup` is a summon-generator-backed command (replacing
|
|
16
|
+
* the former `setup all`); the granular `setup <step>` verbs remain for
|
|
17
|
+
* targeted, scriptable configuration of a single concern.
|
|
18
|
+
*
|
|
19
|
+
* @returns An array of CommandDefinitions for the setup command and its verbs.
|
|
16
20
|
*/
|
|
17
21
|
export function commands(): CommandDefinition[] {
|
|
18
22
|
return [
|
|
19
|
-
|
|
23
|
+
setupCommand(),
|
|
20
24
|
lspCommand,
|
|
21
25
|
mcpCommand,
|
|
22
26
|
completionsCommand,
|