@canonical/pragma-cli 0.18.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 +377 -0
- package/package.json +78 -0
- package/src/bin.test.ts +52 -0
- package/src/bin.ts +19 -0
- package/src/compile-validation.runner.ts +137 -0
- package/src/compile-validation.ts +192 -0
- package/src/completions/computeSocketPath.test.ts +26 -0
- package/src/completions/computeSocketPath.ts +16 -0
- package/src/completions/constants.ts +18 -0
- package/src/completions/handleQuery.test.ts +93 -0
- package/src/completions/handleQuery.ts +30 -0
- package/src/completions/index.ts +14 -0
- package/src/completions/queryCompletions.test.ts +100 -0
- package/src/completions/queryCompletions.ts +61 -0
- package/src/completions/querySocket.test.ts +70 -0
- package/src/completions/querySocket.ts +44 -0
- package/src/completions/startCompletionsServer.test.ts +74 -0
- package/src/completions/startCompletionsServer.ts +75 -0
- package/src/completions/startCompletionsServer.unit.test.ts +131 -0
- package/src/completions/waitForSocket.test.ts +27 -0
- package/src/completions/waitForSocket.ts +27 -0
- package/src/config/configExists.ts +14 -0
- package/src/config/index.ts +6 -0
- package/src/config/readConfig.test.ts +152 -0
- package/src/config/readConfig.ts +64 -0
- package/src/config/resolveConfigPath.test.ts +20 -0
- package/src/config/resolveConfigPath.ts +11 -0
- package/src/config/types.ts +26 -0
- package/src/config/writeConfig.ts +49 -0
- package/src/constants.ts +78 -0
- package/src/domains/block/blockConfig.ts +53 -0
- package/src/domains/block/commands/index.ts +4 -0
- package/src/domains/block/commands/list.test.ts +111 -0
- package/src/domains/block/commands/list.ts +69 -0
- package/src/domains/block/commands/lookup.test.ts +187 -0
- package/src/domains/block/commands/lookup.ts +199 -0
- package/src/domains/block/formatters/index.ts +4 -0
- package/src/domains/block/formatters/list.test.ts +107 -0
- package/src/domains/block/formatters/list.ts +64 -0
- package/src/domains/block/formatters/lookup.test.ts +317 -0
- package/src/domains/block/formatters/lookup.ts +354 -0
- package/src/domains/block/formatters/types.ts +16 -0
- package/src/domains/block/helpers/index.ts +4 -0
- package/src/domains/block/helpers/resolveAspects.test.ts +64 -0
- package/src/domains/block/helpers/resolveAspects.ts +35 -0
- package/src/domains/block/index.ts +25 -0
- package/src/domains/block/mcp/index.ts +2 -0
- package/src/domains/block/mcp/specs.ts +199 -0
- package/src/domains/block/operations/index.ts +4 -0
- package/src/domains/block/operations/list.test.ts +80 -0
- package/src/domains/block/operations/list.ts +72 -0
- package/src/domains/block/operations/lookup.test.ts +108 -0
- package/src/domains/block/operations/lookup.ts +359 -0
- package/src/domains/block/orchestration/blockEmptyError.ts +19 -0
- package/src/domains/block/orchestration/buildBlockFilters.ts +12 -0
- package/src/domains/block/orchestration/enrichBlocks.ts +37 -0
- package/src/domains/block/orchestration/index.ts +5 -0
- package/src/domains/block/orchestration/resolveBlockList.ts +39 -0
- package/src/domains/block/orchestration/resolveBlockLookup.ts +21 -0
- package/src/domains/block/types.ts +30 -0
- package/src/domains/config/commands/channel.test.ts +101 -0
- package/src/domains/config/commands/channel.ts +80 -0
- package/src/domains/config/commands/index.ts +4 -0
- package/src/domains/config/commands/show.test.ts +61 -0
- package/src/domains/config/commands/show.ts +51 -0
- package/src/domains/config/commands/tier.test.ts +84 -0
- package/src/domains/config/commands/tier.ts +78 -0
- package/src/domains/config/formatters/channel.test.ts +56 -0
- package/src/domains/config/formatters/channel.ts +34 -0
- package/src/domains/config/formatters/index.ts +4 -0
- package/src/domains/config/formatters/show.test.ts +126 -0
- package/src/domains/config/formatters/show.ts +66 -0
- package/src/domains/config/formatters/tier.test.ts +70 -0
- package/src/domains/config/formatters/tier.ts +40 -0
- package/src/domains/config/index.ts +23 -0
- package/src/domains/config/mcp/index.ts +2 -0
- package/src/domains/config/mcp/specs.ts +108 -0
- package/src/domains/config/operations/index.ts +5 -0
- package/src/domains/config/operations/show.test.ts +60 -0
- package/src/domains/config/operations/show.ts +42 -0
- package/src/domains/config/operations/types.ts +13 -0
- package/src/domains/config/operations/validateChannel.test.ts +36 -0
- package/src/domains/config/operations/validateChannel.ts +24 -0
- package/src/domains/config/operations/validateTier.test.ts +56 -0
- package/src/domains/config/operations/validateTier.ts +35 -0
- package/src/domains/create/commands/component.test.ts +166 -0
- package/src/domains/create/commands/component.ts +129 -0
- package/src/domains/create/commands/index.ts +3 -0
- package/src/domains/create/commands/package.test.ts +35 -0
- package/src/domains/create/commands/package.ts +21 -0
- package/src/domains/create/index.ts +15 -0
- package/src/domains/create/mcp/index.ts +2 -0
- package/src/domains/create/mcp/specs.ts +174 -0
- package/src/domains/doctor/commands/doctor.test.ts +75 -0
- package/src/domains/doctor/commands/doctor.ts +43 -0
- package/src/domains/doctor/commands/index.ts +2 -0
- package/src/domains/doctor/formatters/doctor.test.ts +117 -0
- package/src/domains/doctor/formatters/doctor.ts +84 -0
- package/src/domains/doctor/formatters/index.ts +2 -0
- package/src/domains/doctor/index.ts +9 -0
- package/src/domains/doctor/mcp/index.ts +2 -0
- package/src/domains/doctor/mcp/specs.ts +88 -0
- package/src/domains/doctor/operations/checks/checkConfigFile.ts +28 -0
- package/src/domains/doctor/operations/checks/checkKeStore.ts +38 -0
- package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +65 -0
- package/src/domains/doctor/operations/checks/checkNodeVersion.ts +25 -0
- package/src/domains/doctor/operations/checks/checkPragmaVersion.ts +20 -0
- package/src/domains/doctor/operations/checks/checkShellCompletions.ts +42 -0
- package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +64 -0
- package/src/domains/doctor/operations/checks/index.ts +8 -0
- package/src/domains/doctor/operations/checks.test.ts +151 -0
- package/src/domains/doctor/operations/index.ts +3 -0
- package/src/domains/doctor/operations/runChecks.test.ts +89 -0
- package/src/domains/doctor/operations/runChecks.ts +38 -0
- package/src/domains/doctor/operations/types.ts +20 -0
- package/src/domains/graph/commands/index.ts +4 -0
- package/src/domains/graph/commands/inspect.test.ts +94 -0
- package/src/domains/graph/commands/inspect.ts +63 -0
- package/src/domains/graph/commands/query.test.ts +92 -0
- package/src/domains/graph/commands/query.ts +60 -0
- package/src/domains/graph/formatters/index.ts +4 -0
- package/src/domains/graph/formatters/inspect.ts +45 -0
- package/src/domains/graph/formatters/query.ts +37 -0
- package/src/domains/graph/helpers/index.ts +3 -0
- package/src/domains/graph/helpers/resolveUri.ts +70 -0
- package/src/domains/graph/index.ts +22 -0
- package/src/domains/graph/mcp/index.ts +2 -0
- package/src/domains/graph/mcp/specs.ts +82 -0
- package/src/domains/graph/operations/executeQuery.test.ts +68 -0
- package/src/domains/graph/operations/executeQuery.ts +32 -0
- package/src/domains/graph/operations/index.ts +4 -0
- package/src/domains/graph/operations/inspectUri.test.ts +101 -0
- package/src/domains/graph/operations/inspectUri.ts +61 -0
- package/src/domains/info/commands/index.ts +4 -0
- package/src/domains/info/commands/info.test.ts +88 -0
- package/src/domains/info/commands/info.ts +47 -0
- package/src/domains/info/commands/upgrade.test.ts +183 -0
- package/src/domains/info/commands/upgrade.ts +150 -0
- package/src/domains/info/constants.test.ts +22 -0
- package/src/domains/info/constants.ts +15 -0
- package/src/domains/info/formatters/index.ts +8 -0
- package/src/domains/info/formatters/info.test.ts +173 -0
- package/src/domains/info/formatters/info.ts +116 -0
- package/src/domains/info/formatters/upgrade.test.ts +133 -0
- package/src/domains/info/formatters/upgrade.ts +68 -0
- package/src/domains/info/index.ts +7 -0
- package/src/domains/info/operations/checkRegistryVersion.test.ts +124 -0
- package/src/domains/info/operations/checkRegistryVersion.ts +45 -0
- package/src/domains/info/operations/collectInfo.test.ts +111 -0
- package/src/domains/info/operations/collectInfo.ts +68 -0
- package/src/domains/info/operations/collectStoreSummary.test.ts +61 -0
- package/src/domains/info/operations/collectStoreSummary.ts +42 -0
- package/src/domains/info/operations/index.ts +5 -0
- package/src/domains/info/types.test-d.ts +35 -0
- package/src/domains/info/types.ts +46 -0
- package/src/domains/llm/commands/index.ts +3 -0
- package/src/domains/llm/commands/llm.ts +37 -0
- package/src/domains/llm/data/commandReference.ts +25 -0
- package/src/domains/llm/data/decisionTrees.ts +46 -0
- package/src/domains/llm/data/index.ts +4 -0
- package/src/domains/llm/formatters/index.ts +3 -0
- package/src/domains/llm/formatters/orientation.ts +53 -0
- package/src/domains/llm/index.ts +8 -0
- package/src/domains/llm/llmCommand.test.ts +93 -0
- package/src/domains/llm/mcp/index.ts +2 -0
- package/src/domains/llm/mcp/specs.ts +87 -0
- package/src/domains/llm/operations/collectContext.ts +47 -0
- package/src/domains/llm/operations/index.ts +3 -0
- package/src/domains/llm/types.ts +32 -0
- package/src/domains/modifier/commands/index.ts +4 -0
- package/src/domains/modifier/commands/list.test.ts +57 -0
- package/src/domains/modifier/commands/list.ts +33 -0
- package/src/domains/modifier/commands/lookup.test.ts +91 -0
- package/src/domains/modifier/commands/lookup.ts +105 -0
- package/src/domains/modifier/formatters/index.ts +4 -0
- package/src/domains/modifier/formatters/list.test.ts +38 -0
- package/src/domains/modifier/formatters/list.ts +35 -0
- package/src/domains/modifier/formatters/lookup.test.ts +33 -0
- package/src/domains/modifier/formatters/lookup.ts +38 -0
- package/src/domains/modifier/index.ts +22 -0
- package/src/domains/modifier/mcp/index.ts +2 -0
- package/src/domains/modifier/mcp/specs.ts +97 -0
- package/src/domains/modifier/operations/index.ts +4 -0
- package/src/domains/modifier/operations/list.test.ts +39 -0
- package/src/domains/modifier/operations/list.ts +41 -0
- package/src/domains/modifier/operations/lookup.test.ts +37 -0
- package/src/domains/modifier/operations/lookup.ts +57 -0
- package/src/domains/modifier/orchestration/index.ts +3 -0
- package/src/domains/modifier/orchestration/modifierEmptyError.ts +10 -0
- package/src/domains/modifier/orchestration/resolveModifierList.ts +16 -0
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +20 -0
- package/src/domains/modifier/types.ts +5 -0
- package/src/domains/ontology/commands/index.ts +4 -0
- package/src/domains/ontology/commands/list.test.ts +82 -0
- package/src/domains/ontology/commands/list.ts +46 -0
- package/src/domains/ontology/commands/show.test.ts +103 -0
- package/src/domains/ontology/commands/show.ts +66 -0
- package/src/domains/ontology/formatters/index.ts +4 -0
- package/src/domains/ontology/formatters/list.ts +37 -0
- package/src/domains/ontology/formatters/show.ts +74 -0
- package/src/domains/ontology/helpers/findNamespace.ts +23 -0
- package/src/domains/ontology/helpers/index.ts +7 -0
- package/src/domains/ontology/helpers/queryClasses.ts +50 -0
- package/src/domains/ontology/helpers/queryProperties.ts +57 -0
- package/src/domains/ontology/helpers/resolvePrefix.ts +45 -0
- package/src/domains/ontology/index.ts +26 -0
- package/src/domains/ontology/mcp/index.ts +2 -0
- package/src/domains/ontology/mcp/specs.ts +93 -0
- package/src/domains/ontology/operations/index.ts +5 -0
- package/src/domains/ontology/operations/listOntologies.test.ts +45 -0
- package/src/domains/ontology/operations/listOntologies.ts +101 -0
- package/src/domains/ontology/operations/showOntology.test.ts +65 -0
- package/src/domains/ontology/operations/showOntology.ts +41 -0
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +39 -0
- package/src/domains/ontology/operations/showOntologyRaw.ts +50 -0
- package/src/domains/setup/commands/all.ts +58 -0
- package/src/domains/setup/commands/completions.ts +85 -0
- package/src/domains/setup/commands/entrypoints.test.ts +170 -0
- package/src/domains/setup/commands/index.ts +6 -0
- package/src/domains/setup/commands/lsp.ts +49 -0
- package/src/domains/setup/commands/mcp.ts +119 -0
- package/src/domains/setup/commands/skills.test.ts +64 -0
- package/src/domains/setup/commands/skills.ts +106 -0
- package/src/domains/setup/formatters/index.ts +2 -0
- package/src/domains/setup/formatters/skills.test.ts +103 -0
- package/src/domains/setup/formatters/skills.ts +112 -0
- package/src/domains/setup/formatters/types.ts +7 -0
- package/src/domains/setup/helpers/completionScripts.test.ts +58 -0
- package/src/domains/setup/helpers/completionScripts.ts +92 -0
- package/src/domains/setup/helpers/constants.ts +2 -0
- package/src/domains/setup/helpers/detectShell.test.ts +48 -0
- package/src/domains/setup/helpers/detectShell.ts +20 -0
- package/src/domains/setup/helpers/index.ts +12 -0
- package/src/domains/setup/helpers/runSetupTask.test.ts +279 -0
- package/src/domains/setup/helpers/runSetupTask.ts +229 -0
- package/src/domains/setup/index.ts +33 -0
- package/src/domains/setup/operations/index.ts +6 -0
- package/src/domains/setup/operations/setupAll.test.ts +48 -0
- package/src/domains/setup/operations/setupAll.ts +41 -0
- package/src/domains/setup/operations/setupCompletions.test.ts +94 -0
- package/src/domains/setup/operations/setupCompletions.ts +54 -0
- package/src/domains/setup/operations/setupLsp.test.ts +29 -0
- package/src/domains/setup/operations/setupLsp.ts +17 -0
- package/src/domains/setup/operations/setupMcp.test.ts +115 -0
- package/src/domains/setup/operations/setupMcp.ts +108 -0
- package/src/domains/setup/operations/setupSkills.test.ts +191 -0
- package/src/domains/setup/operations/setupSkills.ts +160 -0
- package/src/domains/setup/types.ts +18 -0
- package/src/domains/shared/ToolSpec.ts +49 -0
- package/src/domains/shared/bootStore.ts +77 -0
- package/src/domains/shared/buildQuery.ts +18 -0
- package/src/domains/shared/compactUri.test.ts +21 -0
- package/src/domains/shared/compactUri.ts +16 -0
- package/src/domains/shared/context.ts +15 -0
- package/src/domains/shared/contracts.ts +113 -0
- package/src/domains/shared/extractLocalName.test.ts +21 -0
- package/src/domains/shared/extractLocalName.ts +14 -0
- package/src/domains/shared/filters/buildChannelFilter.test.ts +52 -0
- package/src/domains/shared/filters/buildChannelFilter.ts +41 -0
- package/src/domains/shared/filters/buildFilterConfig.ts +16 -0
- package/src/domains/shared/filters/buildFilters.test.ts +33 -0
- package/src/domains/shared/filters/buildFilters.ts +30 -0
- package/src/domains/shared/filters/buildTierFilter.test.ts +78 -0
- package/src/domains/shared/filters/buildTierFilter.ts +69 -0
- package/src/domains/shared/filters/describeFilters.ts +14 -0
- package/src/domains/shared/filters/index.ts +10 -0
- package/src/domains/shared/formatters.ts +35 -0
- package/src/domains/shared/lookupMany.ts +45 -0
- package/src/domains/shared/namespaces.ts +18 -0
- package/src/domains/shared/packages.ts +76 -0
- package/src/domains/shared/prefixes.ts +37 -0
- package/src/domains/shared/runtime.ts +62 -0
- package/src/domains/shared/types.test-d.ts +127 -0
- package/src/domains/shared/types.ts +348 -0
- package/src/domains/skill/commands/index.ts +2 -0
- package/src/domains/skill/commands/list.test.ts +111 -0
- package/src/domains/skill/commands/list.ts +69 -0
- package/src/domains/skill/formatters/index.ts +2 -0
- package/src/domains/skill/formatters/list.test.ts +140 -0
- package/src/domains/skill/formatters/list.ts +119 -0
- package/src/domains/skill/formatters/types.ts +8 -0
- package/src/domains/skill/helpers/index.ts +6 -0
- package/src/domains/skill/helpers/parseFrontmatter.test.ts +122 -0
- package/src/domains/skill/helpers/parseFrontmatter.ts +112 -0
- package/src/domains/skill/helpers/resolveSkillSources.ts +34 -0
- package/src/domains/skill/index.ts +19 -0
- package/src/domains/skill/mcp/index.ts +2 -0
- package/src/domains/skill/mcp/specs.ts +38 -0
- package/src/domains/skill/operations/discover.test.ts +145 -0
- package/src/domains/skill/operations/discover.ts +68 -0
- package/src/domains/skill/operations/index.ts +4 -0
- package/src/domains/skill/operations/list.test.ts +68 -0
- package/src/domains/skill/operations/list.ts +47 -0
- package/src/domains/skill/types.ts +25 -0
- package/src/domains/standard/commands/categories.test.ts +70 -0
- package/src/domains/standard/commands/categories.ts +48 -0
- package/src/domains/standard/commands/index.ts +5 -0
- package/src/domains/standard/commands/list.test.ts +105 -0
- package/src/domains/standard/commands/list.ts +81 -0
- package/src/domains/standard/commands/lookup.test.ts +135 -0
- package/src/domains/standard/commands/lookup.ts +127 -0
- package/src/domains/standard/formatters/categories.test.ts +44 -0
- package/src/domains/standard/formatters/categories.ts +38 -0
- package/src/domains/standard/formatters/index.ts +6 -0
- package/src/domains/standard/formatters/list.test.ts +166 -0
- package/src/domains/standard/formatters/list.ts +156 -0
- package/src/domains/standard/formatters/lookup.test.ts +95 -0
- package/src/domains/standard/formatters/lookup.ts +86 -0
- package/src/domains/standard/formatters/types.ts +31 -0
- package/src/domains/standard/index.ts +32 -0
- package/src/domains/standard/mcp/index.ts +2 -0
- package/src/domains/standard/mcp/specs.ts +197 -0
- package/src/domains/standard/operations/categories.test.ts +61 -0
- package/src/domains/standard/operations/categories.ts +38 -0
- package/src/domains/standard/operations/index.ts +5 -0
- package/src/domains/standard/operations/list.test.ts +75 -0
- package/src/domains/standard/operations/list.ts +64 -0
- package/src/domains/standard/operations/lookup.test.ts +58 -0
- package/src/domains/standard/operations/lookup.ts +142 -0
- package/src/domains/standard/orchestration/buildStandardFilters.ts +11 -0
- package/src/domains/standard/orchestration/index.ts +4 -0
- package/src/domains/standard/orchestration/resolveStandardList.ts +42 -0
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +20 -0
- package/src/domains/standard/orchestration/standardEmptyError.ts +19 -0
- package/src/domains/standard/types.ts +15 -0
- package/src/domains/tier/commands/index.ts +3 -0
- package/src/domains/tier/commands/list.test.ts +67 -0
- package/src/domains/tier/commands/list.ts +43 -0
- package/src/domains/tier/formatters/index.ts +3 -0
- package/src/domains/tier/formatters/list.test.ts +33 -0
- package/src/domains/tier/formatters/list.ts +40 -0
- package/src/domains/tier/index.ts +17 -0
- package/src/domains/tier/mcp/index.ts +2 -0
- package/src/domains/tier/mcp/specs.ts +38 -0
- package/src/domains/tier/operations/index.ts +3 -0
- package/src/domains/tier/operations/list.test.ts +32 -0
- package/src/domains/tier/operations/list.ts +33 -0
- package/src/domains/token/commands/add-config.test.ts +68 -0
- package/src/domains/token/commands/add-config.ts +62 -0
- package/src/domains/token/commands/index.ts +5 -0
- package/src/domains/token/commands/list.test.ts +82 -0
- package/src/domains/token/commands/list.ts +45 -0
- package/src/domains/token/commands/lookup.test.ts +125 -0
- package/src/domains/token/commands/lookup.ts +121 -0
- package/src/domains/token/formatters/index.ts +4 -0
- package/src/domains/token/formatters/list.test.ts +37 -0
- package/src/domains/token/formatters/list.ts +37 -0
- package/src/domains/token/formatters/lookup.test.ts +61 -0
- package/src/domains/token/formatters/lookup.ts +72 -0
- package/src/domains/token/index.ts +32 -0
- package/src/domains/token/mcp/index.ts +2 -0
- package/src/domains/token/mcp/specs.ts +145 -0
- package/src/domains/token/operations/add-config.test.ts +68 -0
- package/src/domains/token/operations/add-config.ts +61 -0
- package/src/domains/token/operations/index.ts +7 -0
- package/src/domains/token/operations/list.test.ts +50 -0
- package/src/domains/token/operations/list.ts +52 -0
- package/src/domains/token/operations/lookup.test.ts +42 -0
- package/src/domains/token/operations/lookup.ts +66 -0
- package/src/domains/token/orchestration/index.ts +3 -0
- package/src/domains/token/orchestration/resolveTokenList.ts +17 -0
- package/src/domains/token/orchestration/resolveTokenLookup.ts +20 -0
- package/src/domains/token/orchestration/tokenEmptyError.ts +17 -0
- package/src/domains/token/types.ts +6 -0
- package/src/error/PragmaError.test.ts +121 -0
- package/src/error/PragmaError.ts +164 -0
- package/src/error/constants.ts +12 -0
- package/src/error/index.ts +4 -0
- package/src/error/types.ts +41 -0
- package/src/index.ts +141 -0
- package/src/mcp/createMcpServer.test.ts +41 -0
- package/src/mcp/createMcpServer.ts +53 -0
- package/src/mcp/error/buildRecovery.test.ts +37 -0
- package/src/mcp/error/buildRecovery.ts +17 -0
- package/src/mcp/error/index.ts +12 -0
- package/src/mcp/error/serializeError.test.ts +121 -0
- package/src/mcp/error/serializeError.ts +31 -0
- package/src/mcp/error/serializeErrorPayload.test.ts +38 -0
- package/src/mcp/error/serializeErrorPayload.ts +27 -0
- package/src/mcp/error/types.ts +51 -0
- package/src/mcp/index.ts +15 -0
- package/src/mcp/registerResources.test.ts +236 -0
- package/src/mcp/registerResources.ts +303 -0
- package/src/mcp/runMcpServer.test.ts +66 -0
- package/src/mcp/runMcpServer.ts +24 -0
- package/src/mcp/tools/index.ts +55 -0
- package/src/mcp/tools/registerFromSpec.ts +97 -0
- package/src/mcp/tools/registerTools.test.ts +1124 -0
- package/src/mcp/types.ts +62 -0
- package/src/mcp/utils/estimateTokens.test.ts +14 -0
- package/src/mcp/utils/estimateTokens.ts +9 -0
- package/src/mcp/utils/index.ts +11 -0
- package/src/mcp/utils/wrapTool.test.ts +108 -0
- package/src/mcp/utils/wrapTool.ts +81 -0
- package/src/package-manager/constants.ts +29 -0
- package/src/package-manager/detectInstallSource.test.ts +23 -0
- package/src/package-manager/detectInstallSource.ts +111 -0
- package/src/package-manager/detectLocalInstall.test.ts +16 -0
- package/src/package-manager/detectLocalInstall.ts +43 -0
- package/src/package-manager/detectPackageManager.test.ts +81 -0
- package/src/package-manager/detectPackageManager.ts +30 -0
- package/src/package-manager/index.ts +6 -0
- package/src/package-manager/types.ts +12 -0
- package/src/pipeline/collectCommands.test.ts +37 -0
- package/src/pipeline/collectCommands.ts +47 -0
- package/src/pipeline/constants.ts +14 -0
- package/src/pipeline/createProgram.test.ts +112 -0
- package/src/pipeline/createProgram.ts +70 -0
- package/src/pipeline/formatTerminal.test.ts +43 -0
- package/src/pipeline/formatTerminal.ts +53 -0
- package/src/pipeline/index.ts +19 -0
- package/src/pipeline/mapExitCode.test.ts +41 -0
- package/src/pipeline/mapExitCode.ts +12 -0
- package/src/pipeline/parseGlobalFlags.ts +21 -0
- package/src/pipeline/renderError.test.ts +151 -0
- package/src/pipeline/renderError.ts +114 -0
- package/src/pipeline/resolveCommandKind.test.ts +68 -0
- package/src/pipeline/resolveCommandKind.ts +71 -0
- package/src/pipeline/runCli.orchestration.test.ts +418 -0
- package/src/pipeline/runCli.test.ts +70 -0
- package/src/pipeline/runCli.ts +215 -0
- package/src/pipeline/types.ts +17 -0
- package/src/testing/cli.ts +43 -0
- package/src/testing/createTestMcpClient.ts +64 -0
- package/src/testing/dsFixtures.ts +506 -0
- package/src/testing/fixtures/canonical-config.json +4 -0
- package/src/testing/fixtures/canonical.ttl +437 -0
- package/src/testing/fixtures/filtered-config.json +4 -0
- package/src/testing/fixtures.ts +34 -0
- package/src/testing/helpers/assertParity.ts +28 -0
- package/src/testing/helpers/createTestMcpClient.ts +40 -0
- package/src/testing/helpers/createTestRuntime.ts +67 -0
- package/src/testing/index.ts +8 -0
- package/src/testing/integration/agent-session.test.ts +148 -0
- package/src/testing/integration/boot.test.ts +67 -0
- package/src/testing/integration/mcp-surface.test.ts +574 -0
- package/src/testing/integration/parity.test.ts +192 -0
- package/src/testing/store.ts +39 -0
- package/src/testing/types.ts +20 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { Formatters } from "../../shared/formatters.js";
|
|
3
|
+
import type { SetupSkillsOutput } from "./types.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Formatters for `pragma setup skills` output.
|
|
7
|
+
*
|
|
8
|
+
* - **plain** renders symlink actions grouped by harness with chalk styling.
|
|
9
|
+
* - **llm** renders a markdown summary with bullet lists.
|
|
10
|
+
* - **json** serializes the raw SetupSkillsResult.
|
|
11
|
+
*/
|
|
12
|
+
const formatters: Formatters<SetupSkillsOutput> = {
|
|
13
|
+
plain({ result, dryRun }) {
|
|
14
|
+
const lines: string[] = [];
|
|
15
|
+
const prefix = dryRun ? "Would create" : "Created";
|
|
16
|
+
|
|
17
|
+
const created = result.actions.filter((a) => a.action === "created");
|
|
18
|
+
const skipped = result.actions.filter((a) => a.action === "skipped");
|
|
19
|
+
const replaced = result.actions.filter((a) => a.action === "replaced");
|
|
20
|
+
|
|
21
|
+
if (created.length > 0) {
|
|
22
|
+
const byHarness = groupByHarness(created.map((a) => a));
|
|
23
|
+
for (const [harness, actions] of byHarness) {
|
|
24
|
+
const count = actions.length;
|
|
25
|
+
lines.push(
|
|
26
|
+
`${chalk.green("✓")} ${prefix} ${count} symlink${count === 1 ? "" : "s"} for ${harness}`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (skipped.length > 0) {
|
|
32
|
+
lines.push(
|
|
33
|
+
chalk.dim(
|
|
34
|
+
` ${skipped.length} existing symlink${skipped.length === 1 ? "" : "s"} unchanged`,
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (replaced.length > 0) {
|
|
40
|
+
for (const action of replaced) {
|
|
41
|
+
lines.push(
|
|
42
|
+
chalk.yellow(
|
|
43
|
+
` ⚠ Replaced symlink for ${action.skillName} in ${action.harnessName}`,
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const warning of result.warnings) {
|
|
50
|
+
lines.push(chalk.yellow(` ⚠ ${warning}`));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return lines.join("\n");
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
llm({ result, dryRun }) {
|
|
57
|
+
const lines: string[] = [];
|
|
58
|
+
const verb = dryRun ? "Would symlink" : "Symlinked";
|
|
59
|
+
|
|
60
|
+
lines.push(`## Setup Skills`);
|
|
61
|
+
lines.push("");
|
|
62
|
+
|
|
63
|
+
const created = result.actions.filter((a) => a.action === "created");
|
|
64
|
+
const skipped = result.actions.filter((a) => a.action === "skipped");
|
|
65
|
+
|
|
66
|
+
if (created.length > 0) {
|
|
67
|
+
lines.push(`${verb} ${created.length} skill(s):`);
|
|
68
|
+
for (const action of created) {
|
|
69
|
+
lines.push(`- **${action.skillName}** → ${action.harnessName}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (skipped.length > 0) {
|
|
74
|
+
lines.push("");
|
|
75
|
+
lines.push(`${skipped.length} already up to date.`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (result.warnings.length > 0) {
|
|
79
|
+
lines.push("");
|
|
80
|
+
lines.push("### Warnings");
|
|
81
|
+
for (const w of result.warnings) {
|
|
82
|
+
lines.push(`- ${w}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return lines.join("\n");
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
json({ result }) {
|
|
90
|
+
return JSON.stringify(result, null, 2);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Group actions by their harness name for display.
|
|
96
|
+
*
|
|
97
|
+
* @param actions - Symlink actions to group.
|
|
98
|
+
* @returns A Map keyed by harness name.
|
|
99
|
+
*/
|
|
100
|
+
function groupByHarness(
|
|
101
|
+
actions: readonly { harnessName: string }[],
|
|
102
|
+
): Map<string, typeof actions> {
|
|
103
|
+
const groups = new Map<string, (typeof actions)[number][]>();
|
|
104
|
+
for (const action of actions) {
|
|
105
|
+
const group = groups.get(action.harnessName) ?? [];
|
|
106
|
+
group.push(action);
|
|
107
|
+
groups.set(action.harnessName, group);
|
|
108
|
+
}
|
|
109
|
+
return groups;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default formatters;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
completionScriptContent,
|
|
4
|
+
completionScriptPath,
|
|
5
|
+
postInstallHint,
|
|
6
|
+
} from "./completionScripts.js";
|
|
7
|
+
|
|
8
|
+
describe("completionScriptContent", () => {
|
|
9
|
+
it("produces zsh completion with compdef", () => {
|
|
10
|
+
const script = completionScriptContent("zsh");
|
|
11
|
+
expect(script).toContain("#compdef pragma");
|
|
12
|
+
expect(script).toContain("pragma completions zsh");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("produces bash completion with complete -F", () => {
|
|
16
|
+
const script = completionScriptContent("bash");
|
|
17
|
+
expect(script).toContain("complete -F _pragma pragma");
|
|
18
|
+
expect(script).toContain("pragma completions bash");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("produces fish completion with complete -c", () => {
|
|
22
|
+
const script = completionScriptContent("fish");
|
|
23
|
+
expect(script).toContain("complete -c pragma");
|
|
24
|
+
expect(script).toContain("pragma completions fish");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("completionScriptPath", () => {
|
|
29
|
+
it("returns zsh path under ~/.zfunc", () => {
|
|
30
|
+
expect(completionScriptPath("zsh")).toContain(".zfunc/_pragma");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("returns bash path under ~/.local/share/bash-completion", () => {
|
|
34
|
+
expect(completionScriptPath("bash")).toContain(
|
|
35
|
+
"bash-completion/completions/pragma",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("returns fish path under ~/.config/fish/completions", () => {
|
|
40
|
+
expect(completionScriptPath("fish")).toContain(
|
|
41
|
+
"fish/completions/pragma.fish",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("postInstallHint", () => {
|
|
47
|
+
it("hints source for zsh", () => {
|
|
48
|
+
expect(postInstallHint("zsh")).toContain("source ~/.zshrc");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("hints source for bash", () => {
|
|
52
|
+
expect(postInstallHint("bash")).toContain("source ~/.bashrc");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("hints new shell for fish", () => {
|
|
56
|
+
expect(postInstallHint("fish")).toContain("new shell");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { ShellId } from "./detectShell.js";
|
|
2
|
+
|
|
3
|
+
const HOME = process.env.HOME ?? "";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate the shell-specific completion script content.
|
|
7
|
+
*
|
|
8
|
+
* Each script delegates to `pragma completions <shell>` at runtime
|
|
9
|
+
* so the completion logic stays in the CLI, not in the shell script.
|
|
10
|
+
*
|
|
11
|
+
* @param shell - Target shell identifier.
|
|
12
|
+
* @returns The completion script content as a string.
|
|
13
|
+
*/
|
|
14
|
+
export function completionScriptContent(shell: ShellId): string {
|
|
15
|
+
switch (shell) {
|
|
16
|
+
case "zsh":
|
|
17
|
+
return [
|
|
18
|
+
"#compdef pragma",
|
|
19
|
+
"# Generated by `pragma setup completions`",
|
|
20
|
+
"",
|
|
21
|
+
"_pragma() {",
|
|
22
|
+
" local -a completions",
|
|
23
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell syntax, not JS template
|
|
24
|
+
' completions=("${(@f)$(pragma completions zsh -- "${words[@]}" 2>/dev/null)}")',
|
|
25
|
+
" compadd -- $completions",
|
|
26
|
+
"}",
|
|
27
|
+
"",
|
|
28
|
+
"compdef _pragma pragma",
|
|
29
|
+
"",
|
|
30
|
+
].join("\n");
|
|
31
|
+
|
|
32
|
+
case "bash":
|
|
33
|
+
return [
|
|
34
|
+
"# Generated by `pragma setup completions`",
|
|
35
|
+
"",
|
|
36
|
+
"_pragma() {",
|
|
37
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell syntax, not JS template
|
|
38
|
+
' local cur="${COMP_WORDS[COMP_CWORD]}"',
|
|
39
|
+
" local completions",
|
|
40
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: shell syntax, not JS template
|
|
41
|
+
' completions=$(pragma completions bash -- "${COMP_WORDS[@]}" 2>/dev/null)',
|
|
42
|
+
" # shellcheck disable=SC2207",
|
|
43
|
+
' COMPREPLY=($(compgen -W "$completions" -- "$cur"))',
|
|
44
|
+
"}",
|
|
45
|
+
"",
|
|
46
|
+
"complete -F _pragma pragma",
|
|
47
|
+
"",
|
|
48
|
+
].join("\n");
|
|
49
|
+
|
|
50
|
+
case "fish":
|
|
51
|
+
return [
|
|
52
|
+
"# Generated by `pragma setup completions`",
|
|
53
|
+
"",
|
|
54
|
+
"complete -c pragma -f -a '(pragma completions fish -- (commandline -cop) 2>/dev/null)'",
|
|
55
|
+
"",
|
|
56
|
+
].join("\n");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Return the standard filesystem path where the completion script should be installed.
|
|
62
|
+
*
|
|
63
|
+
* @param shell - Target shell identifier.
|
|
64
|
+
* @returns Absolute path to the completion script location.
|
|
65
|
+
*/
|
|
66
|
+
export function completionScriptPath(shell: ShellId): string {
|
|
67
|
+
switch (shell) {
|
|
68
|
+
case "zsh":
|
|
69
|
+
return `${HOME}/.zfunc/_pragma`;
|
|
70
|
+
case "bash":
|
|
71
|
+
return `${HOME}/.local/share/bash-completion/completions/pragma`;
|
|
72
|
+
case "fish":
|
|
73
|
+
return `${HOME}/.config/fish/completions/pragma.fish`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Return a post-install hint instructing the user how to activate completions.
|
|
79
|
+
*
|
|
80
|
+
* @param shell - Target shell identifier.
|
|
81
|
+
* @returns A human-readable activation hint.
|
|
82
|
+
*/
|
|
83
|
+
export function postInstallHint(shell: ShellId): string {
|
|
84
|
+
switch (shell) {
|
|
85
|
+
case "zsh":
|
|
86
|
+
return "Run `source ~/.zshrc` or start a new shell to activate.";
|
|
87
|
+
case "bash":
|
|
88
|
+
return "Run `source ~/.bashrc` or start a new shell to activate.";
|
|
89
|
+
case "fish":
|
|
90
|
+
return "Start a new shell to activate.";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import detectShell from "./detectShell.js";
|
|
3
|
+
|
|
4
|
+
describe("detectShell", () => {
|
|
5
|
+
let original: string | undefined;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
original = process.env.SHELL;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
if (original !== undefined) {
|
|
13
|
+
process.env.SHELL = original;
|
|
14
|
+
} else {
|
|
15
|
+
delete process.env.SHELL;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("detects zsh from /bin/zsh", () => {
|
|
20
|
+
process.env.SHELL = "/bin/zsh";
|
|
21
|
+
expect(detectShell()).toBe("zsh");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("detects zsh from /usr/bin/zsh", () => {
|
|
25
|
+
process.env.SHELL = "/usr/bin/zsh";
|
|
26
|
+
expect(detectShell()).toBe("zsh");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("detects bash from /bin/bash", () => {
|
|
30
|
+
process.env.SHELL = "/bin/bash";
|
|
31
|
+
expect(detectShell()).toBe("bash");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("detects fish from /usr/bin/fish", () => {
|
|
35
|
+
process.env.SHELL = "/usr/bin/fish";
|
|
36
|
+
expect(detectShell()).toBe("fish");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("returns null for unknown shell", () => {
|
|
40
|
+
process.env.SHELL = "/bin/tcsh";
|
|
41
|
+
expect(detectShell()).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("returns null when SHELL is unset", () => {
|
|
45
|
+
delete process.env.SHELL;
|
|
46
|
+
expect(detectShell()).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Union of supported shell identifiers. */
|
|
2
|
+
export type ShellId = "zsh" | "bash" | "fish";
|
|
3
|
+
|
|
4
|
+
const SHELL_MAP: Record<string, ShellId> = {
|
|
5
|
+
zsh: "zsh",
|
|
6
|
+
bash: "bash",
|
|
7
|
+
fish: "fish",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect the user's shell from the `$SHELL` environment variable.
|
|
12
|
+
*
|
|
13
|
+
* @returns The detected ShellId, or null if `$SHELL` is unset or unrecognized.
|
|
14
|
+
* @note Impure
|
|
15
|
+
*/
|
|
16
|
+
export default function detectShell(): ShellId | null {
|
|
17
|
+
const shell = process.env.SHELL ?? "";
|
|
18
|
+
const basename = shell.split("/").pop() ?? "";
|
|
19
|
+
return SHELL_MAP[basename] ?? null;
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** @module Shared helpers for setup commands: shell detection, completion scripts, and task execution. */
|
|
2
|
+
export {
|
|
3
|
+
completionScriptContent,
|
|
4
|
+
completionScriptPath,
|
|
5
|
+
postInstallHint,
|
|
6
|
+
} from "./completionScripts.js";
|
|
7
|
+
export { MCP_SERVER_NAME } from "./constants.js";
|
|
8
|
+
export { default as detectShell, type ShellId } from "./detectShell.js";
|
|
9
|
+
export {
|
|
10
|
+
default as runSetupTask,
|
|
11
|
+
type SetupTaskOptions,
|
|
12
|
+
} from "./runSetupTask.js";
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { info, pure, writeFile } from "@canonical/task";
|
|
2
|
+
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
|
+
import runSetupTask from "./runSetupTask.js";
|
|
30
|
+
|
|
31
|
+
describe("runSetupTask", () => {
|
|
32
|
+
let stderrSpy: ReturnType<typeof vi.spyOn>;
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
vi.clearAllMocks();
|
|
36
|
+
stderrSpy = vi
|
|
37
|
+
.spyOn(process.stderr, "write")
|
|
38
|
+
.mockImplementation(() => true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
stderrSpy.mockRestore();
|
|
43
|
+
vi.doUnmock("node:readline");
|
|
44
|
+
vi.doUnmock("@canonical/task");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns output result in dry-run mode", async () => {
|
|
48
|
+
const task = writeFile("/tmp/test.txt", "hello");
|
|
49
|
+
const result = await runSetupTask(task, { dryRun: true });
|
|
50
|
+
|
|
51
|
+
expect(result.tag).toBe("output");
|
|
52
|
+
if (result.tag === "output") {
|
|
53
|
+
const text = result.render.plain(result.value);
|
|
54
|
+
expect(text).toContain("Dry run");
|
|
55
|
+
expect(text).toContain("/tmp/test.txt");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns json output in dry-run + json mode", async () => {
|
|
60
|
+
const task = writeFile("/tmp/test.txt", "hello");
|
|
61
|
+
const result = await runSetupTask(task, {
|
|
62
|
+
dryRun: true,
|
|
63
|
+
format: "json",
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(result.tag).toBe("output");
|
|
67
|
+
if (result.tag === "output") {
|
|
68
|
+
const text = result.render.plain(result.value);
|
|
69
|
+
const parsed = JSON.parse(text);
|
|
70
|
+
expect(Array.isArray(parsed)).toBe(true);
|
|
71
|
+
expect(parsed[0].action).toBe("WriteFile");
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("returns llm markdown in dry-run + llm mode", async () => {
|
|
76
|
+
const task = writeFile("/tmp/test.txt", "hello");
|
|
77
|
+
const result = await runSetupTask(task, { dryRun: true, llm: true });
|
|
78
|
+
|
|
79
|
+
expect(result.tag).toBe("output");
|
|
80
|
+
if (result.tag === "output") {
|
|
81
|
+
const text = result.render.plain(result.value);
|
|
82
|
+
expect(text).toContain("## Dry Run");
|
|
83
|
+
expect(text).toContain("WriteFile");
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("returns exit 0 on successful execution with --yes", async () => {
|
|
88
|
+
const task = info("test message");
|
|
89
|
+
const result = await runSetupTask(task, { yes: true });
|
|
90
|
+
|
|
91
|
+
expect(result.tag).toBe("exit");
|
|
92
|
+
if (result.tag === "exit") {
|
|
93
|
+
expect(result.code).toBe(0);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("returns exit 0 for pure task", async () => {
|
|
98
|
+
const task = pure(undefined);
|
|
99
|
+
const result = await runSetupTask(task, { yes: true });
|
|
100
|
+
|
|
101
|
+
expect(result.tag).toBe("exit");
|
|
102
|
+
if (result.tag === "exit") {
|
|
103
|
+
expect(result.code).toBe(0);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("shows no visible effects message for pure dry-run", async () => {
|
|
108
|
+
const task = pure(undefined);
|
|
109
|
+
const result = await runSetupTask(task, { dryRun: true });
|
|
110
|
+
|
|
111
|
+
expect(result.tag).toBe("output");
|
|
112
|
+
if (result.tag === "output") {
|
|
113
|
+
const text = result.render.plain(result.value);
|
|
114
|
+
expect(text).toContain("no visible effects");
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("returns exit 0 in undo mode for a pure task", async () => {
|
|
119
|
+
const task = pure(undefined);
|
|
120
|
+
const result = await runSetupTask(task, { undo: true, yes: true });
|
|
121
|
+
|
|
122
|
+
expect(result.tag).toBe("exit");
|
|
123
|
+
if (result.tag === "exit") {
|
|
124
|
+
expect(result.code).toBe(0);
|
|
125
|
+
}
|
|
126
|
+
expect(stderrSpy).toHaveBeenCalledWith("Nothing to undo.\n");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("auto-confirms select, multiselect, confirm, and text prompts in yes mode", async () => {
|
|
130
|
+
runTaskMock.mockImplementation(async (_task, options) => {
|
|
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
|
+
});
|
|
160
|
+
|
|
161
|
+
const runSetupTaskWithMocks = await loadRunSetupTask();
|
|
162
|
+
const result = await runSetupTaskWithMocks({} as never, { yes: true });
|
|
163
|
+
|
|
164
|
+
expect(result.tag).toBe("exit");
|
|
165
|
+
if (result.tag === "exit") {
|
|
166
|
+
expect(result.code).toBe(0);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("uses interactive confirm defaults and suppresses debug logs unless verbose", async () => {
|
|
171
|
+
createInterfaceMock.mockReturnValue({
|
|
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);
|
|
182
|
+
options.onLog("debug", "hidden");
|
|
183
|
+
options.onLog("info", "shown");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const runSetupTaskWithMocks = await loadRunSetupTask();
|
|
187
|
+
const result = await runSetupTaskWithMocks({} as never, {});
|
|
188
|
+
|
|
189
|
+
expect(result.tag).toBe("exit");
|
|
190
|
+
expect(stderrSpy).toHaveBeenCalledWith("shown\n");
|
|
191
|
+
expect(stderrSpy).not.toHaveBeenCalledWith("hidden\n");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("returns false for explicit negative interactive confirmation", async () => {
|
|
195
|
+
createInterfaceMock.mockReturnValue({
|
|
196
|
+
question: (_prompt: string, cb: (answer: string) => void) => cb("n"),
|
|
197
|
+
close: vi.fn(),
|
|
198
|
+
});
|
|
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
|
+
|
|
211
|
+
expect(result.tag).toBe("exit");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("returns exit 1 when production execution fails", async () => {
|
|
215
|
+
runTaskMock.mockRejectedValue(new Error("permission denied"));
|
|
216
|
+
|
|
217
|
+
const runSetupTaskWithMocks = await loadRunSetupTask();
|
|
218
|
+
const result = await runSetupTaskWithMocks({} as never, { yes: true });
|
|
219
|
+
|
|
220
|
+
expect(result.tag).toBe("exit");
|
|
221
|
+
if (result.tag === "exit") {
|
|
222
|
+
expect(result.code).toBe(1);
|
|
223
|
+
}
|
|
224
|
+
expect(stderrSpy).toHaveBeenCalledWith("Setup failed: permission denied\n");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("writes verbose undo logs and the singular completion message", async () => {
|
|
228
|
+
runUndoMock.mockImplementation(async (_task, options) => {
|
|
229
|
+
options.onLog("debug", "debug line");
|
|
230
|
+
options.onLog("info", "undoing");
|
|
231
|
+
return { undoCount: 1 };
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const runSetupTaskWithMocks = await loadRunSetupTask();
|
|
235
|
+
const result = await runSetupTaskWithMocks({} as never, {
|
|
236
|
+
undo: true,
|
|
237
|
+
yes: true,
|
|
238
|
+
verbose: true,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
expect(result.tag).toBe("exit");
|
|
242
|
+
expect(stderrSpy).toHaveBeenCalledWith("debug line\n");
|
|
243
|
+
expect(stderrSpy).toHaveBeenCalledWith("undoing\n");
|
|
244
|
+
expect(stderrSpy).toHaveBeenCalledWith(
|
|
245
|
+
"Undo complete (1 step reversed).\n",
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("writes the plural undo completion message", async () => {
|
|
250
|
+
runUndoMock.mockResolvedValue({ undoCount: 2 });
|
|
251
|
+
|
|
252
|
+
const runSetupTaskWithMocks = await loadRunSetupTask();
|
|
253
|
+
const result = await runSetupTaskWithMocks({} as never, {
|
|
254
|
+
undo: true,
|
|
255
|
+
yes: true,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(result.tag).toBe("exit");
|
|
259
|
+
expect(stderrSpy).toHaveBeenCalledWith(
|
|
260
|
+
"Undo complete (2 steps reversed).\n",
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("returns exit 1 when undo fails", async () => {
|
|
265
|
+
runUndoMock.mockRejectedValue(new Error("cannot undo"));
|
|
266
|
+
|
|
267
|
+
const runSetupTaskWithMocks = await loadRunSetupTask();
|
|
268
|
+
const result = await runSetupTaskWithMocks({} as never, {
|
|
269
|
+
undo: true,
|
|
270
|
+
yes: true,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(result.tag).toBe("exit");
|
|
274
|
+
if (result.tag === "exit") {
|
|
275
|
+
expect(result.code).toBe(1);
|
|
276
|
+
}
|
|
277
|
+
expect(stderrSpy).toHaveBeenCalledWith("Undo failed: cannot undo\n");
|
|
278
|
+
});
|
|
279
|
+
});
|