@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,1124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdtempSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
11
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
12
|
+
import { createTestMcpClient } from "#testing";
|
|
13
|
+
import type { McpErrorPayload } from "../types.js";
|
|
14
|
+
|
|
15
|
+
let client: Client;
|
|
16
|
+
let cleanup: () => Promise<void>;
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
const result = await createTestMcpClient();
|
|
20
|
+
client = result.client;
|
|
21
|
+
cleanup = result.cleanup;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
await cleanup();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parse the envelope from a tool response.
|
|
30
|
+
* Returns the full envelope object (with `ok`, `data`, `meta`, etc.).
|
|
31
|
+
*/
|
|
32
|
+
function parseEnvelope(
|
|
33
|
+
result: Record<string, unknown>,
|
|
34
|
+
): Record<string, unknown> {
|
|
35
|
+
const content = result.content as unknown[];
|
|
36
|
+
const first = content[0] as { type: string; text: string };
|
|
37
|
+
expect(first.type).toBe("text");
|
|
38
|
+
return JSON.parse(first.text) as Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extract the `data` field from a success envelope.
|
|
43
|
+
*/
|
|
44
|
+
function parseData(result: Record<string, unknown>): unknown {
|
|
45
|
+
const envelope = parseEnvelope(result);
|
|
46
|
+
expect(envelope.ok).toBe(true);
|
|
47
|
+
return envelope.data;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// =============================================================================
|
|
51
|
+
// Tool listing
|
|
52
|
+
// =============================================================================
|
|
53
|
+
|
|
54
|
+
describe("tool listing", () => {
|
|
55
|
+
it("registers 25 tools", async () => {
|
|
56
|
+
const { tools } = await client.listTools();
|
|
57
|
+
expect(tools).toHaveLength(25);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("all tools have descriptions", async () => {
|
|
61
|
+
const { tools } = await client.listTools();
|
|
62
|
+
for (const tool of tools) {
|
|
63
|
+
expect(tool.description).toBeTruthy();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("includes expected tool names", async () => {
|
|
68
|
+
const { tools } = await client.listTools();
|
|
69
|
+
const names = tools.map((t) => t.name);
|
|
70
|
+
// Existing tools
|
|
71
|
+
expect(names).toContain("block_list");
|
|
72
|
+
expect(names).toContain("block_lookup");
|
|
73
|
+
expect(names).toContain("standard_list");
|
|
74
|
+
expect(names).toContain("standard_lookup");
|
|
75
|
+
expect(names).toContain("standard_categories");
|
|
76
|
+
expect(names).toContain("modifier_list");
|
|
77
|
+
expect(names).toContain("modifier_lookup");
|
|
78
|
+
expect(names).toContain("token_list");
|
|
79
|
+
expect(names).toContain("token_lookup");
|
|
80
|
+
expect(names).toContain("tier_list");
|
|
81
|
+
expect(names).toContain("config_show");
|
|
82
|
+
expect(names).toContain("config_tier");
|
|
83
|
+
expect(names).toContain("config_channel");
|
|
84
|
+
expect(names).toContain("tokens_add_config");
|
|
85
|
+
expect(names).toContain("create_component");
|
|
86
|
+
expect(names).toContain("create_package");
|
|
87
|
+
expect(names).toContain("llm");
|
|
88
|
+
// New tools (Group C)
|
|
89
|
+
expect(names).toContain("ontology_list");
|
|
90
|
+
expect(names).toContain("ontology_show");
|
|
91
|
+
expect(names).toContain("graph_query");
|
|
92
|
+
expect(names).toContain("graph_inspect");
|
|
93
|
+
expect(names).toContain("skill_list");
|
|
94
|
+
expect(names).toContain("doctor");
|
|
95
|
+
expect(names).toContain("info");
|
|
96
|
+
expect(names).toContain("capabilities");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("no tool names contain pragma_ prefix", async () => {
|
|
100
|
+
const { tools } = await client.listTools();
|
|
101
|
+
for (const tool of tools) {
|
|
102
|
+
expect(tool.name).not.toMatch(/^pragma_/);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// =============================================================================
|
|
108
|
+
// Envelope shape
|
|
109
|
+
// =============================================================================
|
|
110
|
+
|
|
111
|
+
describe("envelope shape", () => {
|
|
112
|
+
it("success response has ok: true, data, and meta", async () => {
|
|
113
|
+
const result = await client.callTool({
|
|
114
|
+
name: "block_list",
|
|
115
|
+
arguments: {},
|
|
116
|
+
});
|
|
117
|
+
const envelope = parseEnvelope(result);
|
|
118
|
+
expect(envelope.ok).toBe(true);
|
|
119
|
+
expect(envelope).toHaveProperty("data");
|
|
120
|
+
expect(envelope).toHaveProperty("meta");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("error response has ok: false and error object", async () => {
|
|
124
|
+
const result = await client.callTool({
|
|
125
|
+
name: "config_channel",
|
|
126
|
+
arguments: { value: "beta" },
|
|
127
|
+
});
|
|
128
|
+
expect(result.isError).toBe(true);
|
|
129
|
+
const envelope = parseEnvelope(result);
|
|
130
|
+
expect(envelope.ok).toBe(false);
|
|
131
|
+
expect(envelope).toHaveProperty("error");
|
|
132
|
+
const error = envelope.error as Record<string, unknown>;
|
|
133
|
+
expect(error).toHaveProperty("code");
|
|
134
|
+
expect(error).toHaveProperty("message");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("list operations include count in meta", async () => {
|
|
138
|
+
const result = await client.callTool({
|
|
139
|
+
name: "block_list",
|
|
140
|
+
arguments: {},
|
|
141
|
+
});
|
|
142
|
+
const envelope = parseEnvelope(result);
|
|
143
|
+
const meta = envelope.meta as Record<string, unknown>;
|
|
144
|
+
expect(typeof meta.count).toBe("number");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("list operations include filters in meta", async () => {
|
|
148
|
+
const result = await client.callTool({
|
|
149
|
+
name: "block_list",
|
|
150
|
+
arguments: {},
|
|
151
|
+
});
|
|
152
|
+
const envelope = parseEnvelope(result);
|
|
153
|
+
const meta = envelope.meta as Record<string, unknown>;
|
|
154
|
+
expect(meta.filters).toBeDefined();
|
|
155
|
+
const filters = meta.filters as Record<string, string>;
|
|
156
|
+
expect(filters.channel).toBe("normal");
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// =============================================================================
|
|
161
|
+
// Block tools
|
|
162
|
+
// =============================================================================
|
|
163
|
+
|
|
164
|
+
describe("block_list", () => {
|
|
165
|
+
it("returns blocks", async () => {
|
|
166
|
+
const result = await client.callTool({
|
|
167
|
+
name: "block_list",
|
|
168
|
+
arguments: {},
|
|
169
|
+
});
|
|
170
|
+
const data = parseData(result) as unknown[];
|
|
171
|
+
expect(data.length).toBeGreaterThan(0);
|
|
172
|
+
const names = data.map((c) => (c as { name: string }).name);
|
|
173
|
+
expect(names).toContain("Button");
|
|
174
|
+
expect(names).toContain("Card");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("allTiers widens tier filter without changing channel", async () => {
|
|
178
|
+
const restricted = await createTestMcpClient({
|
|
179
|
+
config: { tier: "global", channel: "normal" },
|
|
180
|
+
});
|
|
181
|
+
try {
|
|
182
|
+
const without = await restricted.client.callTool({
|
|
183
|
+
name: "block_list",
|
|
184
|
+
arguments: {},
|
|
185
|
+
});
|
|
186
|
+
const withoutNames = (parseData(without) as { name: string }[]).map(
|
|
187
|
+
(c) => c.name,
|
|
188
|
+
);
|
|
189
|
+
expect(withoutNames).not.toContain("LXD Panel");
|
|
190
|
+
|
|
191
|
+
const withAll = await restricted.client.callTool({
|
|
192
|
+
name: "block_list",
|
|
193
|
+
arguments: { allTiers: true },
|
|
194
|
+
});
|
|
195
|
+
const withAllNames = (parseData(withAll) as { name: string }[]).map(
|
|
196
|
+
(c) => c.name,
|
|
197
|
+
);
|
|
198
|
+
expect(withAllNames).toContain("LXD Panel");
|
|
199
|
+
expect(withAllNames).not.toContain("Beta Widget");
|
|
200
|
+
} finally {
|
|
201
|
+
await restricted.cleanup();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe("block_lookup", () => {
|
|
207
|
+
it("returns detailed block data by default", async () => {
|
|
208
|
+
const result = await client.callTool({
|
|
209
|
+
name: "block_lookup",
|
|
210
|
+
arguments: { names: ["Button"] },
|
|
211
|
+
});
|
|
212
|
+
const data = parseData(result) as {
|
|
213
|
+
results: Record<string, unknown>[];
|
|
214
|
+
};
|
|
215
|
+
expect(data.results[0]?.name).toBe("Button");
|
|
216
|
+
expect(data.results[0]).toHaveProperty("modifierValues");
|
|
217
|
+
expect(data.results[0]).toHaveProperty("implementationPaths");
|
|
218
|
+
expect(data.results[0]).toHaveProperty("tokens");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("returns summary when detailed=false", async () => {
|
|
222
|
+
const result = await client.callTool({
|
|
223
|
+
name: "block_lookup",
|
|
224
|
+
arguments: { names: ["Button"], detailed: false },
|
|
225
|
+
});
|
|
226
|
+
const data = parseData(result) as {
|
|
227
|
+
results: Record<string, unknown>[];
|
|
228
|
+
};
|
|
229
|
+
expect(data.results[0]?.name).toBe("Button");
|
|
230
|
+
expect(data.results[0]).not.toHaveProperty("modifierValues");
|
|
231
|
+
expect(data.results[0]).not.toHaveProperty("implementationPaths");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("returns structured per-query errors for unknown blocks", async () => {
|
|
235
|
+
const result = await client.callTool({
|
|
236
|
+
name: "block_lookup",
|
|
237
|
+
arguments: { names: ["NonExistent"] },
|
|
238
|
+
});
|
|
239
|
+
const data = parseData(result) as {
|
|
240
|
+
results: unknown[];
|
|
241
|
+
errors: { code: string; query: string }[];
|
|
242
|
+
};
|
|
243
|
+
expect(data.results).toHaveLength(0);
|
|
244
|
+
expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
|
|
245
|
+
expect(data.errors[0]?.query).toBe("NonExistent");
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// =============================================================================
|
|
250
|
+
// Standard tools
|
|
251
|
+
// =============================================================================
|
|
252
|
+
|
|
253
|
+
describe("standard_list", () => {
|
|
254
|
+
it("returns standards", async () => {
|
|
255
|
+
const result = await client.callTool({
|
|
256
|
+
name: "standard_list",
|
|
257
|
+
arguments: {},
|
|
258
|
+
});
|
|
259
|
+
const data = parseData(result) as unknown[];
|
|
260
|
+
expect(data.length).toBeGreaterThan(0);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("filters by category", async () => {
|
|
264
|
+
const all = await client.callTool({
|
|
265
|
+
name: "standard_list",
|
|
266
|
+
arguments: {},
|
|
267
|
+
});
|
|
268
|
+
const allData = parseData(all) as { category: string }[];
|
|
269
|
+
const categories = [...new Set(allData.map((s) => s.category))];
|
|
270
|
+
|
|
271
|
+
const firstCategory = categories[0];
|
|
272
|
+
if (firstCategory) {
|
|
273
|
+
const filtered = await client.callTool({
|
|
274
|
+
name: "standard_list",
|
|
275
|
+
arguments: { category: firstCategory },
|
|
276
|
+
});
|
|
277
|
+
const filteredData = parseData(filtered) as { category: string }[];
|
|
278
|
+
expect(filteredData.length).toBeGreaterThan(0);
|
|
279
|
+
expect(
|
|
280
|
+
filteredData.every(
|
|
281
|
+
(s) => s.category.toLowerCase() === firstCategory.toLowerCase(),
|
|
282
|
+
),
|
|
283
|
+
).toBe(true);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("filters by search term", async () => {
|
|
288
|
+
const result = await client.callTool({
|
|
289
|
+
name: "standard_list",
|
|
290
|
+
arguments: { search: "function" },
|
|
291
|
+
});
|
|
292
|
+
const data = parseData(result) as { name: string; description: string }[];
|
|
293
|
+
for (const item of data) {
|
|
294
|
+
const matches =
|
|
295
|
+
item.name.toLowerCase().includes("function") ||
|
|
296
|
+
item.description.toLowerCase().includes("function");
|
|
297
|
+
expect(matches).toBe(true);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("standard_lookup", () => {
|
|
303
|
+
it("returns detailed standard by default", async () => {
|
|
304
|
+
const list = await client.callTool({
|
|
305
|
+
name: "standard_list",
|
|
306
|
+
arguments: {},
|
|
307
|
+
});
|
|
308
|
+
const standards = parseData(list) as { name: string }[];
|
|
309
|
+
expect(standards.length).toBeGreaterThan(0);
|
|
310
|
+
const standardName = standards[0]?.name ?? "";
|
|
311
|
+
|
|
312
|
+
const result = await client.callTool({
|
|
313
|
+
name: "standard_lookup",
|
|
314
|
+
arguments: { names: [standardName] },
|
|
315
|
+
});
|
|
316
|
+
const data = parseData(result) as {
|
|
317
|
+
results: Record<string, unknown>[];
|
|
318
|
+
};
|
|
319
|
+
expect(data.results[0]?.name).toBe(standardName);
|
|
320
|
+
expect(data.results[0]).toHaveProperty("dos");
|
|
321
|
+
expect(data.results[0]).toHaveProperty("donts");
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("returns summary when detailed=false", async () => {
|
|
325
|
+
const list = await client.callTool({
|
|
326
|
+
name: "standard_list",
|
|
327
|
+
arguments: {},
|
|
328
|
+
});
|
|
329
|
+
const standards = parseData(list) as { name: string }[];
|
|
330
|
+
const standardName = standards[0]?.name ?? "";
|
|
331
|
+
|
|
332
|
+
const result = await client.callTool({
|
|
333
|
+
name: "standard_lookup",
|
|
334
|
+
arguments: { names: [standardName], detailed: false },
|
|
335
|
+
});
|
|
336
|
+
const data = parseData(result) as {
|
|
337
|
+
results: Record<string, unknown>[];
|
|
338
|
+
};
|
|
339
|
+
expect(data.results[0]).not.toHaveProperty("dos");
|
|
340
|
+
expect(data.results[0]).not.toHaveProperty("donts");
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("returns per-query errors for unknown standard", async () => {
|
|
344
|
+
const result = await client.callTool({
|
|
345
|
+
name: "standard_lookup",
|
|
346
|
+
arguments: { names: ["nonexistent/standard"] },
|
|
347
|
+
});
|
|
348
|
+
const data = parseData(result) as {
|
|
349
|
+
errors: { code: string }[];
|
|
350
|
+
};
|
|
351
|
+
expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
describe("standard_categories", () => {
|
|
356
|
+
it("returns categories", async () => {
|
|
357
|
+
const result = await client.callTool({
|
|
358
|
+
name: "standard_categories",
|
|
359
|
+
arguments: {},
|
|
360
|
+
});
|
|
361
|
+
const data = parseData(result) as string[];
|
|
362
|
+
expect(data.length).toBeGreaterThan(0);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// =============================================================================
|
|
367
|
+
// Modifier tools
|
|
368
|
+
// =============================================================================
|
|
369
|
+
|
|
370
|
+
describe("modifier_list", () => {
|
|
371
|
+
it("returns modifier families", async () => {
|
|
372
|
+
const result = await client.callTool({
|
|
373
|
+
name: "modifier_list",
|
|
374
|
+
arguments: {},
|
|
375
|
+
});
|
|
376
|
+
const data = parseData(result) as { name: string; values: string[] }[];
|
|
377
|
+
expect(data.length).toBeGreaterThan(0);
|
|
378
|
+
const names = data.map((m) => m.name);
|
|
379
|
+
expect(names).toContain("importance");
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe("modifier_lookup", () => {
|
|
384
|
+
it("returns modifier values", async () => {
|
|
385
|
+
const result = await client.callTool({
|
|
386
|
+
name: "modifier_lookup",
|
|
387
|
+
arguments: { names: ["importance"] },
|
|
388
|
+
});
|
|
389
|
+
const data = parseData(result) as {
|
|
390
|
+
results: { name: string; values: string[] }[];
|
|
391
|
+
};
|
|
392
|
+
expect(data.results[0]?.name).toBe("importance");
|
|
393
|
+
expect(data.results[0]?.values).toContain("primary");
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("returns per-query errors for unknown modifier", async () => {
|
|
397
|
+
const result = await client.callTool({
|
|
398
|
+
name: "modifier_lookup",
|
|
399
|
+
arguments: { names: ["nonexistent"] },
|
|
400
|
+
});
|
|
401
|
+
const data = parseData(result) as {
|
|
402
|
+
errors: { code: string }[];
|
|
403
|
+
};
|
|
404
|
+
expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// =============================================================================
|
|
409
|
+
// Token tools
|
|
410
|
+
// =============================================================================
|
|
411
|
+
|
|
412
|
+
describe("token_list", () => {
|
|
413
|
+
it("returns tokens", async () => {
|
|
414
|
+
const result = await client.callTool({
|
|
415
|
+
name: "token_list",
|
|
416
|
+
arguments: {},
|
|
417
|
+
});
|
|
418
|
+
const data = parseData(result) as { name: string }[];
|
|
419
|
+
expect(data.length).toBeGreaterThan(0);
|
|
420
|
+
});
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
describe("token_lookup", () => {
|
|
424
|
+
it("returns token with theme values", async () => {
|
|
425
|
+
const result = await client.callTool({
|
|
426
|
+
name: "token_lookup",
|
|
427
|
+
arguments: { names: ["color.primary"] },
|
|
428
|
+
});
|
|
429
|
+
const data = parseData(result) as {
|
|
430
|
+
results: {
|
|
431
|
+
name: string;
|
|
432
|
+
values: { theme: string; value: string }[];
|
|
433
|
+
}[];
|
|
434
|
+
};
|
|
435
|
+
expect(data.results[0]?.name).toBe("color.primary");
|
|
436
|
+
expect((data.results[0]?.values ?? []).length).toBeGreaterThan(0);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("returns per-query errors for unknown token", async () => {
|
|
440
|
+
const result = await client.callTool({
|
|
441
|
+
name: "token_lookup",
|
|
442
|
+
arguments: { names: ["nonexistent.token"] },
|
|
443
|
+
});
|
|
444
|
+
const data = parseData(result) as {
|
|
445
|
+
errors: { code: string }[];
|
|
446
|
+
};
|
|
447
|
+
expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// =============================================================================
|
|
452
|
+
// Tier tools
|
|
453
|
+
// =============================================================================
|
|
454
|
+
|
|
455
|
+
describe("tier_list", () => {
|
|
456
|
+
it("returns tiers", async () => {
|
|
457
|
+
const result = await client.callTool({
|
|
458
|
+
name: "tier_list",
|
|
459
|
+
arguments: {},
|
|
460
|
+
});
|
|
461
|
+
const data = parseData(result) as { path: string }[];
|
|
462
|
+
expect(data.length).toBeGreaterThan(0);
|
|
463
|
+
const paths = data.map((t) => t.path);
|
|
464
|
+
expect(paths).toContain("global");
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// =============================================================================
|
|
469
|
+
// Config tools
|
|
470
|
+
// =============================================================================
|
|
471
|
+
|
|
472
|
+
describe("config_show", () => {
|
|
473
|
+
it("returns config", async () => {
|
|
474
|
+
const result = await client.callTool({
|
|
475
|
+
name: "config_show",
|
|
476
|
+
arguments: {},
|
|
477
|
+
});
|
|
478
|
+
const data = parseData(result) as {
|
|
479
|
+
tier: string | null;
|
|
480
|
+
channel: string;
|
|
481
|
+
};
|
|
482
|
+
expect(data.channel).toBe("normal");
|
|
483
|
+
expect(data.tier).toBeNull();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("returns condensed config text", async () => {
|
|
487
|
+
const result = await client.callTool({
|
|
488
|
+
name: "config_show",
|
|
489
|
+
arguments: { condensed: true },
|
|
490
|
+
});
|
|
491
|
+
const envelope = parseEnvelope(result);
|
|
492
|
+
expect(envelope.ok).toBe(true);
|
|
493
|
+
expect(envelope.condensed).toBe(true);
|
|
494
|
+
expect(envelope.text).toBe("Config: tier=(none) channel=normal");
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
describe("config_tier", () => {
|
|
499
|
+
it("sets, queries, and resets tier in workspace config", async () => {
|
|
500
|
+
const dir = mkdtempSync(join(tmpdir(), "pragma-mcp-tier-"));
|
|
501
|
+
|
|
502
|
+
try {
|
|
503
|
+
const scoped = await createTestMcpClient({ cwd: dir });
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
const setResult = await scoped.client.callTool({
|
|
507
|
+
name: "config_tier",
|
|
508
|
+
arguments: { path: "global" },
|
|
509
|
+
});
|
|
510
|
+
expect(parseData(setResult)).toEqual({ tier: "global", action: "set" });
|
|
511
|
+
|
|
512
|
+
const queryResult = await scoped.client.callTool({
|
|
513
|
+
name: "config_tier",
|
|
514
|
+
arguments: {},
|
|
515
|
+
});
|
|
516
|
+
expect(parseData(queryResult)).toEqual({
|
|
517
|
+
tier: "global",
|
|
518
|
+
action: "query",
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const resetResult = await scoped.client.callTool({
|
|
522
|
+
name: "config_tier",
|
|
523
|
+
arguments: { reset: true },
|
|
524
|
+
});
|
|
525
|
+
expect(parseData(resetResult)).toEqual({ tier: null, action: "reset" });
|
|
526
|
+
|
|
527
|
+
const queriedAfterReset = await scoped.client.callTool({
|
|
528
|
+
name: "config_tier",
|
|
529
|
+
arguments: {},
|
|
530
|
+
});
|
|
531
|
+
expect(parseData(queriedAfterReset)).toEqual({
|
|
532
|
+
tier: null,
|
|
533
|
+
action: "query",
|
|
534
|
+
});
|
|
535
|
+
} finally {
|
|
536
|
+
await scoped.cleanup();
|
|
537
|
+
}
|
|
538
|
+
} finally {
|
|
539
|
+
rmSync(dir, { recursive: true, force: true });
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
describe("config_channel", () => {
|
|
545
|
+
it("sets, queries, and resets channel in workspace config", async () => {
|
|
546
|
+
const dir = mkdtempSync(join(tmpdir(), "pragma-mcp-channel-"));
|
|
547
|
+
|
|
548
|
+
try {
|
|
549
|
+
const scoped = await createTestMcpClient({ cwd: dir });
|
|
550
|
+
|
|
551
|
+
try {
|
|
552
|
+
const setResult = await scoped.client.callTool({
|
|
553
|
+
name: "config_channel",
|
|
554
|
+
arguments: { value: "experimental" },
|
|
555
|
+
});
|
|
556
|
+
expect(parseData(setResult)).toEqual({
|
|
557
|
+
channel: "experimental",
|
|
558
|
+
action: "set",
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const queryResult = await scoped.client.callTool({
|
|
562
|
+
name: "config_channel",
|
|
563
|
+
arguments: {},
|
|
564
|
+
});
|
|
565
|
+
expect(parseData(queryResult)).toEqual({
|
|
566
|
+
channel: "experimental",
|
|
567
|
+
action: "query",
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
const resetResult = await scoped.client.callTool({
|
|
571
|
+
name: "config_channel",
|
|
572
|
+
arguments: { reset: true },
|
|
573
|
+
});
|
|
574
|
+
expect(parseData(resetResult)).toEqual({
|
|
575
|
+
channel: "normal",
|
|
576
|
+
action: "reset",
|
|
577
|
+
});
|
|
578
|
+
} finally {
|
|
579
|
+
await scoped.cleanup();
|
|
580
|
+
}
|
|
581
|
+
} finally {
|
|
582
|
+
rmSync(dir, { recursive: true, force: true });
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it("returns an error for invalid channel input", async () => {
|
|
587
|
+
const result = await client.callTool({
|
|
588
|
+
name: "config_channel",
|
|
589
|
+
arguments: { value: "beta" },
|
|
590
|
+
});
|
|
591
|
+
expect(result.isError).toBe(true);
|
|
592
|
+
const envelope = parseEnvelope(result);
|
|
593
|
+
const error = envelope.error as McpErrorPayload;
|
|
594
|
+
expect(error.code).toBe("INVALID_INPUT");
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
describe("tokens_add_config", () => {
|
|
599
|
+
it("writes a token config file and reports its path", async () => {
|
|
600
|
+
const dir = mkdtempSync(join(tmpdir(), "pragma-mcp-token-config-"));
|
|
601
|
+
|
|
602
|
+
try {
|
|
603
|
+
const scoped = await createTestMcpClient({ cwd: dir });
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
const result = await scoped.client.callTool({
|
|
607
|
+
name: "tokens_add_config",
|
|
608
|
+
arguments: {},
|
|
609
|
+
});
|
|
610
|
+
const data = parseData(result) as {
|
|
611
|
+
configPath: string;
|
|
612
|
+
tokenSources: string[];
|
|
613
|
+
installHint: string;
|
|
614
|
+
};
|
|
615
|
+
expect(data.configPath).toBe(join(dir, "tokens.config.mjs"));
|
|
616
|
+
expect(data.tokenSources).toContain(
|
|
617
|
+
"node_modules/@canonical/ds-global/tokens/**/*.json",
|
|
618
|
+
);
|
|
619
|
+
expect(data.installHint).toContain("@canonical/terrazzo-lsp");
|
|
620
|
+
expect(existsSync(data.configPath)).toBe(true);
|
|
621
|
+
} finally {
|
|
622
|
+
await scoped.cleanup();
|
|
623
|
+
}
|
|
624
|
+
} finally {
|
|
625
|
+
rmSync(dir, { recursive: true, force: true });
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it("returns recovery metadata when config already exists", async () => {
|
|
630
|
+
const dir = mkdtempSync(join(tmpdir(), "pragma-mcp-token-existing-"));
|
|
631
|
+
const configPath = join(dir, "tokens.config.mjs");
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
writeFileSync(configPath, "// existing\n", "utf-8");
|
|
635
|
+
const scoped = await createTestMcpClient({ cwd: dir });
|
|
636
|
+
|
|
637
|
+
try {
|
|
638
|
+
const result = await scoped.client.callTool({
|
|
639
|
+
name: "tokens_add_config",
|
|
640
|
+
arguments: {},
|
|
641
|
+
});
|
|
642
|
+
expect(result.isError).toBe(true);
|
|
643
|
+
const envelope = parseEnvelope(result);
|
|
644
|
+
const error = envelope.error as McpErrorPayload;
|
|
645
|
+
expect(error.code).toBe("INVALID_INPUT");
|
|
646
|
+
expect(error.recovery?.tool).toBe("tokens_add_config");
|
|
647
|
+
expect(error.recovery?.params).toEqual({ force: true });
|
|
648
|
+
} finally {
|
|
649
|
+
await scoped.cleanup();
|
|
650
|
+
}
|
|
651
|
+
} finally {
|
|
652
|
+
rmSync(dir, { recursive: true, force: true });
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it("overwrites an existing config when force=true", async () => {
|
|
657
|
+
const dir = mkdtempSync(join(tmpdir(), "pragma-mcp-token-force-"));
|
|
658
|
+
const configPath = join(dir, "tokens.config.mjs");
|
|
659
|
+
|
|
660
|
+
try {
|
|
661
|
+
writeFileSync(configPath, "// existing\n", "utf-8");
|
|
662
|
+
const scoped = await createTestMcpClient({ cwd: dir });
|
|
663
|
+
|
|
664
|
+
try {
|
|
665
|
+
const result = await scoped.client.callTool({
|
|
666
|
+
name: "tokens_add_config",
|
|
667
|
+
arguments: { force: true },
|
|
668
|
+
});
|
|
669
|
+
const data = parseData(result) as { configPath: string };
|
|
670
|
+
expect(data.configPath).toBe(configPath);
|
|
671
|
+
expect(readFileSync(configPath, "utf-8")).toContain(
|
|
672
|
+
'import { defineConfig } from "@canonical/terrazzo";',
|
|
673
|
+
);
|
|
674
|
+
} finally {
|
|
675
|
+
await scoped.cleanup();
|
|
676
|
+
}
|
|
677
|
+
} finally {
|
|
678
|
+
rmSync(dir, { recursive: true, force: true });
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
// =============================================================================
|
|
684
|
+
// Generator tools (D14 — mutating)
|
|
685
|
+
// =============================================================================
|
|
686
|
+
|
|
687
|
+
describe("create_component", () => {
|
|
688
|
+
it("returns JSON generation plan for react", async () => {
|
|
689
|
+
const result = await client.callTool({
|
|
690
|
+
name: "create_component",
|
|
691
|
+
arguments: {
|
|
692
|
+
framework: "react",
|
|
693
|
+
componentPath: "src/components/Button",
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
expect(result.isError).toBeFalsy();
|
|
697
|
+
const data = parseData(result) as Record<string, unknown>;
|
|
698
|
+
expect(data.generator).toBeDefined();
|
|
699
|
+
expect((data.generator as { name: string }).name).toBe("component/react");
|
|
700
|
+
expect(Array.isArray(data.plan)).toBe(true);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it("returns JSON generation plan for svelte", async () => {
|
|
704
|
+
const result = await client.callTool({
|
|
705
|
+
name: "create_component",
|
|
706
|
+
arguments: {
|
|
707
|
+
framework: "svelte",
|
|
708
|
+
componentPath: "src/lib/components/Toggle",
|
|
709
|
+
},
|
|
710
|
+
});
|
|
711
|
+
expect(result.isError).toBeFalsy();
|
|
712
|
+
const data = parseData(result) as Record<string, unknown>;
|
|
713
|
+
expect((data.generator as { name: string }).name).toBe("component/svelte");
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it("returns error for invalid framework", async () => {
|
|
717
|
+
const result = await client.callTool({
|
|
718
|
+
name: "create_component",
|
|
719
|
+
arguments: {
|
|
720
|
+
framework: "angular",
|
|
721
|
+
componentPath: "src/components/Button",
|
|
722
|
+
},
|
|
723
|
+
});
|
|
724
|
+
expect(result.isError).toBe(true);
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
describe("create_package", () => {
|
|
729
|
+
it("returns JSON generation plan", async () => {
|
|
730
|
+
const result = await client.callTool({
|
|
731
|
+
name: "create_package",
|
|
732
|
+
arguments: {
|
|
733
|
+
name: "@canonical/test-pkg",
|
|
734
|
+
type: "tool-ts",
|
|
735
|
+
},
|
|
736
|
+
});
|
|
737
|
+
expect(result.isError).toBeFalsy();
|
|
738
|
+
const data = parseData(result) as Record<string, unknown>;
|
|
739
|
+
expect((data.generator as { name: string }).name).toBe("package");
|
|
740
|
+
expect(Array.isArray(data.plan)).toBe(true);
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
// =============================================================================
|
|
745
|
+
// LLM Orientation
|
|
746
|
+
// =============================================================================
|
|
747
|
+
|
|
748
|
+
describe("llm", () => {
|
|
749
|
+
it("returns orientation data with context, trees, and commands", async () => {
|
|
750
|
+
const result = await client.callTool({
|
|
751
|
+
name: "llm",
|
|
752
|
+
arguments: {},
|
|
753
|
+
});
|
|
754
|
+
const data = parseData(result) as {
|
|
755
|
+
context: { counts: Record<string, number>; namespaces: string[] };
|
|
756
|
+
decisionTrees: { intent: string }[];
|
|
757
|
+
commandReference: { command: string }[];
|
|
758
|
+
};
|
|
759
|
+
expect(data.context.counts.blocks).toBeGreaterThan(0);
|
|
760
|
+
expect(data.context.counts.standards).toBeGreaterThan(0);
|
|
761
|
+
expect(data.context.namespaces.length).toBeGreaterThan(0);
|
|
762
|
+
expect(data.decisionTrees).toHaveLength(5);
|
|
763
|
+
expect(data.commandReference.length).toBeGreaterThan(0);
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
// =============================================================================
|
|
768
|
+
// Error handling
|
|
769
|
+
// =============================================================================
|
|
770
|
+
|
|
771
|
+
describe("error handling", () => {
|
|
772
|
+
it("all entity-not-found errors include recovery objects", async () => {
|
|
773
|
+
const notFoundCalls = [
|
|
774
|
+
{ name: "block_lookup", arguments: { names: ["X"] } },
|
|
775
|
+
{ name: "modifier_lookup", arguments: { names: ["X"] } },
|
|
776
|
+
{ name: "token_lookup", arguments: { names: ["X"] } },
|
|
777
|
+
{ name: "standard_lookup", arguments: { names: ["X"] } },
|
|
778
|
+
] as const;
|
|
779
|
+
|
|
780
|
+
for (const call of notFoundCalls) {
|
|
781
|
+
const result = await client.callTool(call);
|
|
782
|
+
const data = parseData(result) as {
|
|
783
|
+
errors: { code: string }[];
|
|
784
|
+
};
|
|
785
|
+
expect(data.errors[0]?.code).toBe("ENTITY_NOT_FOUND");
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
// =============================================================================
|
|
791
|
+
// Ontology tools
|
|
792
|
+
// =============================================================================
|
|
793
|
+
|
|
794
|
+
describe("ontology_list", () => {
|
|
795
|
+
it("returns ontologies", async () => {
|
|
796
|
+
const result = await client.callTool({
|
|
797
|
+
name: "ontology_list",
|
|
798
|
+
arguments: {},
|
|
799
|
+
});
|
|
800
|
+
const data = parseData(result) as { prefix: string }[];
|
|
801
|
+
expect(data.length).toBeGreaterThan(0);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it("returns error envelope shape on failure", async () => {
|
|
805
|
+
// ontology_list should always succeed with loaded data,
|
|
806
|
+
// so we just verify the success shape has meta.count
|
|
807
|
+
const result = await client.callTool({
|
|
808
|
+
name: "ontology_list",
|
|
809
|
+
arguments: {},
|
|
810
|
+
});
|
|
811
|
+
const envelope = parseEnvelope(result);
|
|
812
|
+
const meta = envelope.meta as Record<string, unknown>;
|
|
813
|
+
expect(typeof meta.count).toBe("number");
|
|
814
|
+
});
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
describe("ontology_show", () => {
|
|
818
|
+
it("returns ontology details for a valid prefix", async () => {
|
|
819
|
+
const result = await client.callTool({
|
|
820
|
+
name: "ontology_show",
|
|
821
|
+
arguments: { prefix: "ds" },
|
|
822
|
+
});
|
|
823
|
+
const data = parseData(result) as {
|
|
824
|
+
prefix: string;
|
|
825
|
+
classes: unknown[];
|
|
826
|
+
properties: unknown[];
|
|
827
|
+
};
|
|
828
|
+
expect(data.prefix).toBe("ds");
|
|
829
|
+
expect(data.classes.length).toBeGreaterThan(0);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it("returns error for unknown prefix", async () => {
|
|
833
|
+
const result = await client.callTool({
|
|
834
|
+
name: "ontology_show",
|
|
835
|
+
arguments: { prefix: "nonexistent" },
|
|
836
|
+
});
|
|
837
|
+
expect(result.isError).toBe(true);
|
|
838
|
+
const envelope = parseEnvelope(result);
|
|
839
|
+
expect(envelope.ok).toBe(false);
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
it("returns condensed ontology details", async () => {
|
|
843
|
+
const result = await client.callTool({
|
|
844
|
+
name: "ontology_show",
|
|
845
|
+
arguments: { prefix: "ds", condensed: true },
|
|
846
|
+
});
|
|
847
|
+
const envelope = parseEnvelope(result);
|
|
848
|
+
expect(envelope.ok).toBe(true);
|
|
849
|
+
expect(envelope.condensed).toBe(true);
|
|
850
|
+
expect(envelope.text).toEqual(expect.stringContaining("## ds:"));
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// =============================================================================
|
|
855
|
+
// Graph tools
|
|
856
|
+
// =============================================================================
|
|
857
|
+
|
|
858
|
+
describe("graph_query", () => {
|
|
859
|
+
it("executes a SPARQL query", async () => {
|
|
860
|
+
const result = await client.callTool({
|
|
861
|
+
name: "graph_query",
|
|
862
|
+
arguments: { sparql: "SELECT ?s WHERE { ?s ?p ?o } LIMIT 1" },
|
|
863
|
+
});
|
|
864
|
+
const data = parseData(result) as Record<string, unknown>;
|
|
865
|
+
expect(data).toBeDefined();
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
it("returns error for invalid SPARQL", async () => {
|
|
869
|
+
const result = await client.callTool({
|
|
870
|
+
name: "graph_query",
|
|
871
|
+
arguments: { sparql: "NOT VALID SPARQL" },
|
|
872
|
+
});
|
|
873
|
+
expect(result.isError).toBe(true);
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
it("returns condensed graph query output", async () => {
|
|
877
|
+
const result = await client.callTool({
|
|
878
|
+
name: "graph_query",
|
|
879
|
+
arguments: {
|
|
880
|
+
sparql: "SELECT ?s WHERE { ?s ?p ?o } LIMIT 1",
|
|
881
|
+
condensed: true,
|
|
882
|
+
},
|
|
883
|
+
});
|
|
884
|
+
const envelope = parseEnvelope(result);
|
|
885
|
+
expect(envelope.ok).toBe(true);
|
|
886
|
+
expect(envelope.condensed).toBe(true);
|
|
887
|
+
expect(envelope.text).toEqual(expect.stringContaining("bindings"));
|
|
888
|
+
});
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
describe("graph_inspect", () => {
|
|
892
|
+
it("inspects a valid URI", async () => {
|
|
893
|
+
// First get a valid URI from the graph
|
|
894
|
+
const queryResult = await client.callTool({
|
|
895
|
+
name: "graph_query",
|
|
896
|
+
arguments: { sparql: "SELECT ?s WHERE { ?s ?p ?o } LIMIT 1" },
|
|
897
|
+
});
|
|
898
|
+
const queryData = parseData(queryResult) as {
|
|
899
|
+
type: string;
|
|
900
|
+
bindings: { s?: string }[];
|
|
901
|
+
};
|
|
902
|
+
const validUri = queryData.bindings[0]?.s;
|
|
903
|
+
expect(validUri).toBeDefined();
|
|
904
|
+
if (!validUri) {
|
|
905
|
+
throw new Error("Expected a valid URI");
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const result = await client.callTool({
|
|
909
|
+
name: "graph_inspect",
|
|
910
|
+
arguments: { uri: validUri },
|
|
911
|
+
});
|
|
912
|
+
const data = parseData(result) as {
|
|
913
|
+
uri: string;
|
|
914
|
+
groups: { predicate: string; objects: string[] }[];
|
|
915
|
+
};
|
|
916
|
+
expect(data.uri).toBeDefined();
|
|
917
|
+
expect(data.groups.length).toBeGreaterThan(0);
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
it("returns error for unknown URI", async () => {
|
|
921
|
+
const result = await client.callTool({
|
|
922
|
+
name: "graph_inspect",
|
|
923
|
+
arguments: { uri: "https://example.com/nonexistent" },
|
|
924
|
+
});
|
|
925
|
+
expect(result.isError).toBe(true);
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
it("returns condensed graph inspection output", async () => {
|
|
929
|
+
const queryResult = await client.callTool({
|
|
930
|
+
name: "graph_query",
|
|
931
|
+
arguments: { sparql: "SELECT ?s WHERE { ?s ?p ?o } LIMIT 1" },
|
|
932
|
+
});
|
|
933
|
+
const queryData = parseData(queryResult) as {
|
|
934
|
+
bindings: { s?: string }[];
|
|
935
|
+
};
|
|
936
|
+
const validUri = queryData.bindings[0]?.s;
|
|
937
|
+
expect(validUri).toBeDefined();
|
|
938
|
+
if (!validUri) {
|
|
939
|
+
throw new Error("Expected a valid URI");
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
const result = await client.callTool({
|
|
943
|
+
name: "graph_inspect",
|
|
944
|
+
arguments: { uri: validUri, condensed: true },
|
|
945
|
+
});
|
|
946
|
+
const envelope = parseEnvelope(result);
|
|
947
|
+
expect(envelope.ok).toBe(true);
|
|
948
|
+
expect(envelope.condensed).toBe(true);
|
|
949
|
+
expect(envelope.text).toEqual(expect.stringContaining("## "));
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
// =============================================================================
|
|
954
|
+
// Skill tools
|
|
955
|
+
// =============================================================================
|
|
956
|
+
|
|
957
|
+
describe("skill_list", () => {
|
|
958
|
+
it("returns skills data", async () => {
|
|
959
|
+
const result = await client.callTool({
|
|
960
|
+
name: "skill_list",
|
|
961
|
+
arguments: {},
|
|
962
|
+
});
|
|
963
|
+
const data = parseData(result) as {
|
|
964
|
+
skills: unknown[];
|
|
965
|
+
sources: unknown[];
|
|
966
|
+
};
|
|
967
|
+
expect(data).toHaveProperty("skills");
|
|
968
|
+
expect(data).toHaveProperty("sources");
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
// =============================================================================
|
|
973
|
+
// Doctor tool
|
|
974
|
+
// =============================================================================
|
|
975
|
+
|
|
976
|
+
describe("doctor", () => {
|
|
977
|
+
it("returns health check results", async () => {
|
|
978
|
+
const result = await client.callTool({
|
|
979
|
+
name: "doctor",
|
|
980
|
+
arguments: {},
|
|
981
|
+
});
|
|
982
|
+
const data = parseData(result) as {
|
|
983
|
+
checks: { name: string; status: string }[];
|
|
984
|
+
passed: number;
|
|
985
|
+
failed: number;
|
|
986
|
+
};
|
|
987
|
+
expect(data.checks.length).toBeGreaterThan(0);
|
|
988
|
+
expect(typeof data.passed).toBe("number");
|
|
989
|
+
});
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
// =============================================================================
|
|
993
|
+
// Info tool
|
|
994
|
+
// =============================================================================
|
|
995
|
+
|
|
996
|
+
describe("info", () => {
|
|
997
|
+
it("returns pragma info", async () => {
|
|
998
|
+
const result = await client.callTool({
|
|
999
|
+
name: "info",
|
|
1000
|
+
arguments: {},
|
|
1001
|
+
});
|
|
1002
|
+
const data = parseData(result) as {
|
|
1003
|
+
version: string;
|
|
1004
|
+
channel: string;
|
|
1005
|
+
store: { tripleCount: number } | null;
|
|
1006
|
+
};
|
|
1007
|
+
expect(data.version).toBeDefined();
|
|
1008
|
+
expect(data.store).toBeDefined();
|
|
1009
|
+
expect(data.store?.tripleCount).toBeGreaterThan(0);
|
|
1010
|
+
});
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
// =============================================================================
|
|
1014
|
+
// Capabilities tool
|
|
1015
|
+
// =============================================================================
|
|
1016
|
+
|
|
1017
|
+
describe("capabilities", () => {
|
|
1018
|
+
it("returns accurate tool list and counts", async () => {
|
|
1019
|
+
const result = await client.callTool({
|
|
1020
|
+
name: "capabilities",
|
|
1021
|
+
arguments: {},
|
|
1022
|
+
});
|
|
1023
|
+
const data = parseData(result) as {
|
|
1024
|
+
tools: string[];
|
|
1025
|
+
counts: {
|
|
1026
|
+
total: number;
|
|
1027
|
+
read: number;
|
|
1028
|
+
write: number;
|
|
1029
|
+
orientation: number;
|
|
1030
|
+
diagnostic: number;
|
|
1031
|
+
};
|
|
1032
|
+
};
|
|
1033
|
+
expect(data.tools).toContain("block_list");
|
|
1034
|
+
expect(data.tools).toContain("capabilities");
|
|
1035
|
+
expect(data.counts.total).toBe(25);
|
|
1036
|
+
expect(data.counts.read).toBeGreaterThan(0);
|
|
1037
|
+
expect(data.counts.write).toBe(5);
|
|
1038
|
+
expect(data.counts.orientation).toBe(2);
|
|
1039
|
+
expect(data.counts.diagnostic).toBe(2);
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
// =============================================================================
|
|
1044
|
+
// Condensed responses
|
|
1045
|
+
// =============================================================================
|
|
1046
|
+
|
|
1047
|
+
describe("condensed parameter", () => {
|
|
1048
|
+
it("block_list returns condensed text", async () => {
|
|
1049
|
+
const result = await client.callTool({
|
|
1050
|
+
name: "block_list",
|
|
1051
|
+
arguments: { condensed: true },
|
|
1052
|
+
});
|
|
1053
|
+
const envelope = parseEnvelope(result);
|
|
1054
|
+
expect(envelope.ok).toBe(true);
|
|
1055
|
+
expect(envelope.condensed).toBe(true);
|
|
1056
|
+
expect(typeof envelope.text).toBe("string");
|
|
1057
|
+
expect(typeof envelope.tokens).toBe("string");
|
|
1058
|
+
expect(envelope).not.toHaveProperty("data");
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
it("standard_list returns condensed text", async () => {
|
|
1062
|
+
const result = await client.callTool({
|
|
1063
|
+
name: "standard_list",
|
|
1064
|
+
arguments: { condensed: true },
|
|
1065
|
+
});
|
|
1066
|
+
const envelope = parseEnvelope(result);
|
|
1067
|
+
expect(envelope.ok).toBe(true);
|
|
1068
|
+
expect(envelope.condensed).toBe(true);
|
|
1069
|
+
expect(typeof envelope.text).toBe("string");
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it("tier_list returns condensed text", async () => {
|
|
1073
|
+
const result = await client.callTool({
|
|
1074
|
+
name: "tier_list",
|
|
1075
|
+
arguments: { condensed: true },
|
|
1076
|
+
});
|
|
1077
|
+
const envelope = parseEnvelope(result);
|
|
1078
|
+
expect(envelope.ok).toBe(true);
|
|
1079
|
+
expect(envelope.condensed).toBe(true);
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
it("doctor returns condensed text", async () => {
|
|
1083
|
+
const result = await client.callTool({
|
|
1084
|
+
name: "doctor",
|
|
1085
|
+
arguments: { condensed: true },
|
|
1086
|
+
});
|
|
1087
|
+
const envelope = parseEnvelope(result);
|
|
1088
|
+
expect(envelope.ok).toBe(true);
|
|
1089
|
+
expect(envelope.condensed).toBe(true);
|
|
1090
|
+
expect(typeof envelope.text).toBe("string");
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
it("info returns condensed text", async () => {
|
|
1094
|
+
const result = await client.callTool({
|
|
1095
|
+
name: "info",
|
|
1096
|
+
arguments: { condensed: true },
|
|
1097
|
+
});
|
|
1098
|
+
const envelope = parseEnvelope(result);
|
|
1099
|
+
expect(envelope.ok).toBe(true);
|
|
1100
|
+
expect(envelope.condensed).toBe(true);
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
it("ontology_list returns condensed text", async () => {
|
|
1104
|
+
const result = await client.callTool({
|
|
1105
|
+
name: "ontology_list",
|
|
1106
|
+
arguments: { condensed: true },
|
|
1107
|
+
});
|
|
1108
|
+
const envelope = parseEnvelope(result);
|
|
1109
|
+
expect(envelope.ok).toBe(true);
|
|
1110
|
+
expect(envelope.condensed).toBe(true);
|
|
1111
|
+
expect((envelope.text as string).length).toBeGreaterThan(0);
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
it("token_lookup returns condensed text", async () => {
|
|
1115
|
+
const result = await client.callTool({
|
|
1116
|
+
name: "token_lookup",
|
|
1117
|
+
arguments: { names: ["color.primary"], condensed: true },
|
|
1118
|
+
});
|
|
1119
|
+
const envelope = parseEnvelope(result);
|
|
1120
|
+
expect(envelope.ok).toBe(true);
|
|
1121
|
+
expect(envelope.condensed).toBe(true);
|
|
1122
|
+
expect(typeof envelope.text).toBe("string");
|
|
1123
|
+
});
|
|
1124
|
+
});
|