@canonical/pragma-cli 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (410) hide show
  1. package/README.md +128 -47
  2. package/dist/pragma +0 -0
  3. package/package.json +15 -13
  4. package/src/completions/constants.ts +7 -0
  5. package/src/completions/handleQuery.test.ts +32 -0
  6. package/src/completions/handleQuery.ts +20 -1
  7. package/src/completions/queryCompletions.test.ts +5 -0
  8. package/src/completions/queryCompletions.ts +17 -3
  9. package/src/completions/querySocket.test.ts +31 -0
  10. package/src/completions/querySocket.ts +36 -9
  11. package/src/config/defaultConfig.test.ts +33 -0
  12. package/src/config/defaultConfig.ts +59 -0
  13. package/src/config/defaults.json +17 -0
  14. package/src/config/findProjectConfigPath.test.ts +53 -0
  15. package/src/config/findProjectConfigPath.ts +36 -0
  16. package/src/config/index.ts +17 -2
  17. package/src/config/mergeConfigFileUpdate.ts +58 -0
  18. package/src/config/parseConfigValues.ts +217 -0
  19. package/src/config/readConfig.test.ts +104 -13
  20. package/src/config/readConfig.ts +12 -153
  21. package/src/config/readConfigLayers.test.ts +109 -0
  22. package/src/config/readConfigLayers.ts +118 -0
  23. package/src/config/resolveGlobalConfigPath.ts +13 -0
  24. package/src/config/resolveWriteConfigPath.test.ts +53 -0
  25. package/src/config/resolveWriteConfigPath.ts +31 -0
  26. package/src/config/types.ts +83 -1
  27. package/src/config/writeConfig.ts +25 -55
  28. package/src/constants.ts +3 -1
  29. package/src/domains/block/commands/list.ts +5 -90
  30. package/src/domains/block/commands/lookup.ts +8 -167
  31. package/src/domains/block/commands/sample.ts +2 -1
  32. package/src/domains/block/formatters/list.test.ts +3 -3
  33. package/src/domains/block/formatters/lookup.test.ts +5 -5
  34. package/src/domains/block/mcp/specs.ts +16 -196
  35. package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
  36. package/src/domains/block/orchestration/resolveBlockLookup.ts +2 -1
  37. package/src/domains/block/stories.test.ts +28 -0
  38. package/src/domains/block/stories.ts +260 -0
  39. package/src/domains/config/commands/channel.test.ts +30 -3
  40. package/src/domains/config/commands/channel.ts +25 -8
  41. package/src/domains/config/commands/configScope.ts +46 -0
  42. package/src/domains/config/commands/framework.test.ts +32 -1
  43. package/src/domains/config/commands/framework.ts +26 -8
  44. package/src/domains/config/commands/show.ts +5 -45
  45. package/src/domains/config/commands/tier.test.ts +20 -2
  46. package/src/domains/config/commands/tier.ts +25 -8
  47. package/src/domains/config/commands/trace.test.ts +19 -2
  48. package/src/domains/config/commands/trace.ts +6 -3
  49. package/src/domains/config/formatters/channel.test.ts +20 -6
  50. package/src/domains/config/formatters/channel.ts +14 -11
  51. package/src/domains/config/formatters/framework.ts +14 -11
  52. package/src/domains/config/formatters/show.test.ts +27 -0
  53. package/src/domains/config/formatters/show.ts +27 -4
  54. package/src/domains/config/formatters/tier.test.ts +26 -10
  55. package/src/domains/config/formatters/tier.ts +19 -11
  56. package/src/domains/config/formatters/trace.ts +7 -7
  57. package/src/domains/config/mcp/specs.ts +33 -53
  58. package/src/domains/config/operations/show.test.ts +18 -0
  59. package/src/domains/config/operations/show.ts +7 -1
  60. package/src/domains/config/operations/types.ts +7 -0
  61. package/src/domains/config/operations/validateTier.test.ts +9 -0
  62. package/src/domains/config/operations/validateTier.ts +10 -2
  63. package/src/domains/config/stories.ts +42 -0
  64. package/src/domains/config/tasks/index.ts +1 -0
  65. package/src/domains/config/tasks/setChannel.ts +13 -28
  66. package/src/domains/config/tasks/setFramework.ts +11 -30
  67. package/src/domains/config/tasks/setTier.ts +10 -29
  68. package/src/domains/config/tasks/setTrace.ts +10 -28
  69. package/src/domains/config/tasks/writeConfigField.ts +41 -0
  70. package/src/domains/create/generatorPack/bundled.ts +49 -0
  71. package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
  72. package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
  73. package/src/domains/create/index.ts +13 -7
  74. package/src/domains/create/mcp/specs.ts +6 -161
  75. package/src/domains/create/renderGeneratorUi.test.ts +103 -0
  76. package/src/domains/create/renderGeneratorUi.ts +65 -0
  77. package/src/domains/doctor/formatters/doctor.test.ts +50 -2
  78. package/src/domains/doctor/formatters/doctor.ts +90 -38
  79. package/src/domains/doctor/operations/checks/checkConfigFile.ts +19 -5
  80. package/src/domains/doctor/operations/checks/checkMcpCommands.ts +133 -0
  81. package/src/domains/doctor/operations/checks/checkMcpConfigured.ts +1 -1
  82. package/src/domains/doctor/operations/checks/checkPackageRefs.ts +32 -26
  83. package/src/domains/doctor/operations/checks/checkSkillsSymlinked.ts +1 -1
  84. package/src/domains/doctor/operations/checks/index.ts +1 -0
  85. package/src/domains/doctor/operations/checks.test.ts +160 -7
  86. package/src/domains/doctor/operations/runChecks.test.ts +15 -6
  87. package/src/domains/doctor/operations/runChecks.ts +2 -0
  88. package/src/domains/doctor/operations/types.ts +23 -1
  89. package/src/domains/graph/commands/inspect.test.ts +2 -2
  90. package/src/domains/graph/commands/query.test.ts +5 -3
  91. package/src/domains/graph/formatters/inspect.ts +7 -7
  92. package/src/domains/graph/formatters/query.test.ts +87 -0
  93. package/src/domains/graph/formatters/query.ts +42 -5
  94. package/src/domains/graph/helpers/resolveUri.test.ts +50 -0
  95. package/src/domains/graph/helpers/resolveUri.ts +28 -21
  96. package/src/domains/graph/operations/inspectUri.test.ts +5 -5
  97. package/src/domains/graphql/commands/build.test.ts +5 -3
  98. package/src/domains/graphql/commands/build.ts +8 -16
  99. package/src/domains/graphql/commands/check.test.ts +5 -3
  100. package/src/domains/graphql/commands/check.ts +7 -13
  101. package/src/domains/graphql/commands/index.ts +1 -0
  102. package/src/domains/graphql/commands/serve.test.ts +46 -0
  103. package/src/domains/graphql/commands/serve.ts +141 -0
  104. package/src/domains/graphql/constants.ts +10 -0
  105. package/src/domains/graphql/helpers/gatherSources.test.ts +92 -0
  106. package/src/domains/graphql/helpers/gatherSources.ts +58 -0
  107. package/src/domains/graphql/helpers/index.ts +2 -0
  108. package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
  109. package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
  110. package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
  111. package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
  112. package/src/domains/graphql/helpers/routeGraphql.test.ts +43 -0
  113. package/src/domains/graphql/helpers/routeGraphql.ts +28 -0
  114. package/src/domains/graphql/index.ts +9 -6
  115. package/src/domains/graphql/operations/compileSchema.test.ts +23 -47
  116. package/src/domains/graphql/operations/compileSchema.ts +23 -25
  117. package/src/domains/graphql/operations/createSchemaServer.test.ts +50 -0
  118. package/src/domains/graphql/operations/createSchemaServer.ts +75 -0
  119. package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
  120. package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
  121. package/src/domains/graphql/operations/index.ts +10 -0
  122. package/src/domains/info/formatters/upgrade.test.ts +7 -4
  123. package/src/domains/info/formatters/upgrade.ts +3 -1
  124. package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
  125. package/src/domains/info/operations/collectEntityCounts.ts +123 -0
  126. package/src/domains/info/operations/collectInfo.test.ts +14 -3
  127. package/src/domains/info/operations/collectInfo.ts +12 -15
  128. package/src/domains/info/operations/index.ts +1 -0
  129. package/src/domains/info/types.ts +13 -0
  130. package/src/domains/llm/capabilities.test.ts +24 -4
  131. package/src/domains/llm/commands/capabilities.ts +12 -3
  132. package/src/domains/llm/commands/llm.ts +5 -1
  133. package/src/domains/llm/data/commandReference.ts +20 -11
  134. package/src/domains/llm/data/decisionTrees.ts +10 -5
  135. package/src/domains/llm/data/toolCatalog.ts +55 -18
  136. package/src/domains/llm/formatters/orientation.ts +5 -1
  137. package/src/domains/llm/llmCommand.test.ts +11 -3
  138. package/src/domains/llm/mcp/specs.ts +3 -4
  139. package/src/domains/llm/operations/collectContext.test.ts +64 -0
  140. package/src/domains/llm/operations/collectContext.ts +7 -17
  141. package/src/domains/modifier/commands/list.ts +5 -38
  142. package/src/domains/modifier/commands/lookup.ts +8 -79
  143. package/src/domains/modifier/commands/sample.ts +2 -1
  144. package/src/domains/modifier/mcp/specs.ts +15 -94
  145. package/src/domains/modifier/operations/lookup.ts +5 -3
  146. package/src/domains/modifier/orchestration/resolveModifierLookup.ts +2 -1
  147. package/src/domains/modifier/stories.test.ts +20 -0
  148. package/src/domains/modifier/stories.ts +86 -0
  149. package/src/domains/ontology/commands/list.test.ts +5 -3
  150. package/src/domains/ontology/commands/list.ts +5 -58
  151. package/src/domains/ontology/commands/show.test.ts +85 -9
  152. package/src/domains/ontology/commands/show.ts +6 -58
  153. package/src/domains/ontology/formatters/index.ts +1 -0
  154. package/src/domains/ontology/formatters/list.ts +37 -12
  155. package/src/domains/ontology/formatters/show.ts +361 -64
  156. package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
  157. package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
  158. package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
  159. package/src/domains/ontology/helpers/index.ts +14 -0
  160. package/src/domains/ontology/helpers/queryClasses.ts +32 -16
  161. package/src/domains/ontology/helpers/queryProperties.ts +39 -6
  162. package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
  163. package/src/domains/ontology/mcp/specs.ts +7 -84
  164. package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
  165. package/src/domains/ontology/operations/listOntologies.ts +126 -30
  166. package/src/domains/ontology/operations/showOntology.test.ts +131 -12
  167. package/src/domains/ontology/operations/showOntology.ts +289 -7
  168. package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
  169. package/src/domains/ontology/stories.test.ts +36 -0
  170. package/src/domains/ontology/stories.ts +153 -0
  171. package/src/domains/refs/operations/updateRefs.ts +2 -38
  172. package/src/domains/setup/commands/index.ts +1 -0
  173. package/src/domains/setup/commands/setup.ts +70 -0
  174. package/src/domains/setup/commands/skills.test.ts +6 -2
  175. package/src/domains/setup/commands/skills.ts +6 -2
  176. package/src/domains/setup/helpers/runSetupTask.test.ts +148 -150
  177. package/src/domains/setup/helpers/runSetupTask.ts +62 -70
  178. package/src/domains/setup/index.ts +7 -3
  179. package/src/domains/setup/operations/setupLsp.test.ts +3 -2
  180. package/src/domains/setup/operations/setupLsp.ts +7 -2
  181. package/src/domains/setup/operations/setupMcp.test.ts +6 -1
  182. package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
  183. package/src/domains/shared/bootStore.test.ts +82 -0
  184. package/src/domains/shared/bootStore.ts +57 -18
  185. package/src/domains/shared/compactUri.test.ts +11 -0
  186. package/src/domains/shared/compactUri.ts +10 -5
  187. package/src/domains/shared/context.ts +2 -2
  188. package/src/domains/shared/contracts.ts +11 -0
  189. package/src/domains/shared/createInteractivePromptSession.test.ts +439 -0
  190. package/src/domains/shared/createInteractivePromptSession.ts +236 -0
  191. package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
  192. package/src/domains/shared/extractLocalName.test.ts +3 -3
  193. package/src/domains/shared/formatters.ts +23 -2
  194. package/src/domains/shared/listTiers.ts +40 -0
  195. package/src/domains/shared/loaders/bundledLoader.test.ts +248 -0
  196. package/src/domains/shared/loaders/bundledLoader.ts +158 -12
  197. package/src/domains/shared/loaders/gitLoader.ts +1 -2
  198. package/src/domains/shared/loaders/index.ts +4 -1
  199. package/src/domains/shared/loaders/localLoader.ts +2 -4
  200. package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
  201. package/src/domains/shared/loaders/readPackageDir.ts +82 -9
  202. package/src/domains/shared/loaders/storyAssetsPlugin.test.ts +84 -0
  203. package/src/domains/shared/loaders/storyAssetsPlugin.ts +46 -0
  204. package/src/domains/shared/lookupMany.test.ts +135 -0
  205. package/src/domains/shared/lookupMany.ts +50 -2
  206. package/src/domains/shared/mergeAndParseRefs.test.ts +89 -0
  207. package/src/domains/shared/mergeAndParseRefs.ts +44 -0
  208. package/src/domains/shared/namespaces.ts +13 -5
  209. package/src/domains/shared/packages.ts +10 -17
  210. package/src/domains/shared/parseSampleCount.test.ts +47 -0
  211. package/src/domains/shared/parseSampleCount.ts +45 -0
  212. package/src/domains/shared/prefixes.test.ts +211 -0
  213. package/src/domains/shared/prefixes.ts +182 -15
  214. package/src/domains/shared/renderers.ts +5 -5
  215. package/src/domains/shared/resolveConfiguredGraphs.test.ts +27 -0
  216. package/src/domains/shared/resolveConfiguredGraphs.ts +41 -0
  217. package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
  218. package/src/domains/shared/resolveLookupExitCode.ts +42 -0
  219. package/src/domains/shared/runtime.ts +4 -63
  220. package/src/domains/shared/semanticPackage.test.ts +89 -0
  221. package/src/domains/shared/semanticPackage.ts +34 -3
  222. package/src/domains/shared/stories/compileLookupCommand.test.ts +145 -0
  223. package/src/domains/shared/stories/compileLookupCommand.ts +102 -0
  224. package/src/domains/shared/stories/compileLookupTool.test.ts +105 -0
  225. package/src/domains/shared/stories/compileLookupTool.ts +96 -0
  226. package/src/domains/shared/stories/compileReadCommand.test.ts +121 -0
  227. package/src/domains/shared/stories/compileReadCommand.ts +48 -0
  228. package/src/domains/shared/stories/compileReadTool.test.ts +64 -0
  229. package/src/domains/shared/stories/compileReadTool.ts +38 -0
  230. package/src/domains/shared/stories/condense.test.ts +18 -0
  231. package/src/domains/shared/stories/condense.ts +15 -0
  232. package/src/domains/shared/stories/index.ts +20 -0
  233. package/src/domains/shared/stories/normalizeNames.test.ts +20 -0
  234. package/src/domains/shared/stories/normalizeNames.ts +22 -0
  235. package/src/domains/shared/stories/pack/applyPackFilters.test.ts +151 -0
  236. package/src/domains/shared/stories/pack/applyPackFilters.ts +111 -0
  237. package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
  238. package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
  239. package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +81 -0
  240. package/src/domains/shared/stories/pack/buildLookupQuery.ts +154 -0
  241. package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
  242. package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
  243. package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
  244. package/src/domains/shared/stories/pack/collectPackStories.test.ts +406 -0
  245. package/src/domains/shared/stories/pack/collectPackStories.ts +158 -0
  246. package/src/domains/shared/stories/pack/compilePackCommands.ts +50 -0
  247. package/src/domains/shared/stories/pack/compilePackStories.test.ts +751 -0
  248. package/src/domains/shared/stories/pack/compilePackStories.ts +724 -0
  249. package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +55 -0
  250. package/src/domains/shared/stories/pack/index.ts +42 -0
  251. package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
  252. package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
  253. package/src/domains/shared/stories/pack/runSelectQuery.ts +31 -0
  254. package/src/domains/shared/stories/pack/standardParity.test.ts +381 -0
  255. package/src/domains/shared/stories/pack/types.ts +224 -0
  256. package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +742 -0
  257. package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +859 -0
  258. package/src/domains/shared/stories/projectCliParameters.test.ts +59 -0
  259. package/src/domains/shared/stories/projectCliParameters.ts +57 -0
  260. package/src/domains/shared/stories/projectToolParams.test.ts +50 -0
  261. package/src/domains/shared/stories/projectToolParams.ts +32 -0
  262. package/src/domains/shared/stories/requirePragmaContext.test.ts +23 -0
  263. package/src/domains/shared/stories/requirePragmaContext.ts +23 -0
  264. package/src/domains/shared/stories/resolveLookupDetailed.test.ts +57 -0
  265. package/src/domains/shared/stories/resolveLookupDetailed.ts +27 -0
  266. package/src/domains/shared/stories/types.ts +179 -0
  267. package/src/domains/shared/suggestions/expandGlob.ts +1 -0
  268. package/src/domains/shared/suggestions/suggestNames.ts +43 -10
  269. package/src/domains/shared/types/index.ts +5 -9
  270. package/src/domains/shared/types/ontology.ts +119 -24
  271. package/src/domains/shared/types/standard.ts +9 -52
  272. package/src/domains/skill/commands/index.ts +3 -1
  273. package/src/domains/skill/commands/list.test.ts +6 -2
  274. package/src/domains/skill/commands/list.ts +5 -63
  275. package/src/domains/skill/commands/lookup.test.ts +118 -0
  276. package/src/domains/skill/commands/lookup.ts +14 -0
  277. package/src/domains/skill/formatters/index.ts +1 -0
  278. package/src/domains/skill/formatters/lookup.test.ts +46 -0
  279. package/src/domains/skill/formatters/lookup.ts +57 -0
  280. package/src/domains/skill/helpers/resolveSkillSources.ts +0 -1
  281. package/src/domains/skill/index.ts +7 -3
  282. package/src/domains/skill/mcp/specs.ts +10 -29
  283. package/src/domains/skill/operations/discover.test.ts +61 -30
  284. package/src/domains/skill/operations/discover.ts +28 -37
  285. package/src/domains/skill/operations/index.ts +2 -1
  286. package/src/domains/skill/operations/list.test.ts +15 -7
  287. package/src/domains/skill/operations/lookup.test.ts +91 -0
  288. package/src/domains/skill/operations/lookup.ts +70 -0
  289. package/src/domains/skill/stories.test.ts +43 -0
  290. package/src/domains/skill/stories.ts +103 -0
  291. package/src/domains/skill/types.ts +8 -0
  292. package/src/domains/token/commands/lookup.test.ts +8 -0
  293. package/src/domains/token/commands/lookup.ts +12 -0
  294. package/src/domains/token/commands/sample.ts +2 -1
  295. package/src/domains/token/featureFlag.ts +17 -0
  296. package/src/domains/token/index.ts +5 -0
  297. package/src/domains/token/mcp/specs.ts +18 -2
  298. package/src/domains/token/orchestration/resolveTokenLookup.ts +2 -1
  299. package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
  300. package/src/domains/trace/commands/show.ts +30 -4
  301. package/src/domains/trace/tracePlugin.test.ts +1 -1
  302. package/src/index.ts +1 -13
  303. package/src/mcp/registerResources.test.ts +382 -74
  304. package/src/mcp/registerResources.ts +5 -348
  305. package/src/mcp/resources/buildGraphIndex.ts +167 -0
  306. package/src/mcp/resources/buildResourceList.test.ts +97 -0
  307. package/src/mcp/resources/buildResourceList.ts +176 -0
  308. package/src/mcp/resources/classifyEntity.test.ts +40 -0
  309. package/src/mcp/resources/classifyEntity.ts +27 -0
  310. package/src/mcp/resources/constants.ts +105 -0
  311. package/src/mcp/resources/displayPredicates.ts +62 -0
  312. package/src/mcp/resources/pickFirstValue.test.ts +29 -0
  313. package/src/mcp/resources/pickFirstValue.ts +26 -0
  314. package/src/mcp/resources/pickPrimaryType.test.ts +41 -0
  315. package/src/mcp/resources/pickPrimaryType.ts +26 -0
  316. package/src/mcp/resources/rankUriCompletions.test.ts +52 -0
  317. package/src/mcp/resources/rankUriCompletions.ts +64 -0
  318. package/src/mcp/resources/readEntity.ts +169 -0
  319. package/src/mcp/resources/registerResources.ts +126 -0
  320. package/src/mcp/resources/types.ts +128 -0
  321. package/src/mcp/tools/index.ts +22 -5
  322. package/src/mcp/tools/registerTools.test.ts +167 -34
  323. package/src/mcp/types.ts +4 -0
  324. package/src/pipeline/collectCommands.test.ts +151 -1
  325. package/src/pipeline/collectCommands.ts +35 -13
  326. package/src/pipeline/createProgram.test.ts +5 -1
  327. package/src/pipeline/createProgram.ts +2 -2
  328. package/src/pipeline/firstRun.test.ts +80 -0
  329. package/src/pipeline/firstRun.ts +99 -0
  330. package/src/pipeline/parseGlobalFlags.test.ts +102 -1
  331. package/src/pipeline/parseGlobalFlags.ts +98 -9
  332. package/src/pipeline/resolveCommandKind.test.ts +12 -0
  333. package/src/pipeline/resolveCommandKind.ts +4 -0
  334. package/src/pipeline/rootHelp.test.ts +87 -0
  335. package/src/pipeline/rootHelp.ts +265 -0
  336. package/src/pipeline/runCli.orchestration.test.ts +28 -12
  337. package/src/pipeline/runCli.test.ts +17 -4
  338. package/src/pipeline/runCli.ts +202 -25
  339. package/src/testing/cli.ts +30 -2
  340. package/src/testing/createTestMcpClient.ts +4 -1
  341. package/src/testing/dsFixtures.ts +21 -4
  342. package/src/testing/index.ts +7 -0
  343. package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +1207 -0
  344. package/src/testing/integration/cli-user-stories.test.ts +63 -0
  345. package/src/testing/integration/cross-binary-byte-equality.test.ts +129 -0
  346. package/src/testing/integration/edge-cases.test.ts +62 -50
  347. package/src/testing/integration/executor-execution.test.ts +103 -0
  348. package/src/testing/integration/executor-golden.test.ts +145 -0
  349. package/src/testing/integration/mcp-surface.test.ts +40 -15
  350. package/src/testing/integration/parity.test.ts +136 -82
  351. package/src/testing/regression/0001-skill-discovery-granularity.test.ts +71 -0
  352. package/src/testing/setupXdgIsolation.ts +26 -0
  353. package/src/testing/standardParityFixtures.ts +35 -0
  354. package/src/testing/store.ts +4 -4
  355. package/src/testing/storyFixtures.ts +63 -0
  356. package/src/domains/create/commands/component.test.ts +0 -166
  357. package/src/domains/create/commands/component.ts +0 -123
  358. package/src/domains/create/commands/index.ts +0 -3
  359. package/src/domains/create/commands/package.test.ts +0 -35
  360. package/src/domains/create/commands/package.ts +0 -21
  361. package/src/domains/create/generators.ts +0 -8
  362. package/src/domains/standard/commands/categories.test.ts +0 -70
  363. package/src/domains/standard/commands/categories.ts +0 -48
  364. package/src/domains/standard/commands/index.ts +0 -6
  365. package/src/domains/standard/commands/list.test.ts +0 -105
  366. package/src/domains/standard/commands/list.ts +0 -90
  367. package/src/domains/standard/commands/lookup.test.ts +0 -135
  368. package/src/domains/standard/commands/lookup.ts +0 -99
  369. package/src/domains/standard/commands/sample.ts +0 -57
  370. package/src/domains/standard/formatters/categories.test.ts +0 -44
  371. package/src/domains/standard/formatters/categories.ts +0 -38
  372. package/src/domains/standard/formatters/index.ts +0 -10
  373. package/src/domains/standard/formatters/list.test.ts +0 -169
  374. package/src/domains/standard/formatters/list.ts +0 -184
  375. package/src/domains/standard/formatters/lookup.test.ts +0 -95
  376. package/src/domains/standard/formatters/lookup.ts +0 -101
  377. package/src/domains/standard/formatters/sample.ts +0 -15
  378. package/src/domains/standard/formatters/types.ts +0 -31
  379. package/src/domains/standard/index.ts +0 -40
  380. package/src/domains/standard/mcp/index.ts +0 -2
  381. package/src/domains/standard/mcp/specs.ts +0 -244
  382. package/src/domains/standard/operations/categories.test.ts +0 -61
  383. package/src/domains/standard/operations/categories.ts +0 -38
  384. package/src/domains/standard/operations/index.ts +0 -6
  385. package/src/domains/standard/operations/list.test.ts +0 -75
  386. package/src/domains/standard/operations/list.ts +0 -64
  387. package/src/domains/standard/operations/lookup.test.ts +0 -58
  388. package/src/domains/standard/operations/lookup.ts +0 -151
  389. package/src/domains/standard/operations/sample.ts +0 -35
  390. package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
  391. package/src/domains/standard/orchestration/index.ts +0 -4
  392. package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
  393. package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -32
  394. package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
  395. package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
  396. package/src/domains/standard/standardConfig.ts +0 -42
  397. package/src/domains/standard/types.ts +0 -15
  398. package/src/domains/tier/commands/index.ts +0 -3
  399. package/src/domains/tier/commands/list.test.ts +0 -67
  400. package/src/domains/tier/commands/list.ts +0 -59
  401. package/src/domains/tier/formatters/index.ts +0 -3
  402. package/src/domains/tier/formatters/list.test.ts +0 -33
  403. package/src/domains/tier/formatters/list.ts +0 -45
  404. package/src/domains/tier/index.ts +0 -17
  405. package/src/domains/tier/mcp/index.ts +0 -2
  406. package/src/domains/tier/mcp/specs.ts +0 -38
  407. package/src/domains/tier/operations/index.ts +0 -3
  408. package/src/domains/tier/operations/list.test.ts +0 -32
  409. package/src/domains/tier/operations/list.ts +0 -33
  410. package/src/pipeline/runInteractiveCommand.ts +0 -198
@@ -1,7 +1,13 @@
1
1
  import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
2
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
- import { createTestMcpClient } from "#testing";
4
- import { P } from "../domains/shared/prefixes.js";
3
+ import { createTestMcpClient, createTestStore } from "#testing";
4
+ import { DEFAULT_PREFIX_MAP, P } from "../domains/shared/prefixes.js";
5
+ import type { PragmaRuntime } from "../domains/shared/runtime.js";
6
+ import createClientFromRuntime from "../testing/helpers/createTestMcpClient.js";
7
+ import {
8
+ ABOX_PER_CLASS_LIMIT,
9
+ COMPLETION_LIMIT,
10
+ } from "./resources/constants.js";
5
11
 
6
12
  let client: Client;
7
13
  let cleanup: () => Promise<void>;
@@ -22,64 +28,183 @@ interface ResourceContent {
22
28
  text: string;
23
29
  }
24
30
 
31
+ interface ListedMeta {
32
+ "pragma/box"?: string;
33
+ "pragma/category"?: string;
34
+ "pragma/type"?: string | null;
35
+ "pragma/instanceCount"?: number;
36
+ "pragma/instancesShown"?: number;
37
+ "pragma/truncated"?: boolean;
38
+ }
39
+
25
40
  function parseContents(result: { contents: unknown[] }): unknown {
26
- const first = result.contents[0] as ResourceContent;
41
+ const first = result.contents.at(0) as ResourceContent;
27
42
  return JSON.parse(first.text);
28
43
  }
29
44
 
45
+ function metaOf(resource: { _meta?: unknown }): ListedMeta {
46
+ return (resource._meta ?? {}) as ListedMeta;
47
+ }
48
+
49
+ /**
50
+ * Build an MCP client over a store with the given TTL and extra prefixes.
51
+ * Lets foreign/overlapping namespaces be registered, which the shared
52
+ * `createTestMcpClient` (fixed prefix set) cannot express.
53
+ */
54
+ async function createClientForTtl(
55
+ ttl: string,
56
+ prefixes?: Record<string, string>,
57
+ ): Promise<{ client: Client; cleanup: () => Promise<void> }> {
58
+ const { store, cleanup: closeStore } = await createTestStore({
59
+ ttl,
60
+ prefixes,
61
+ });
62
+ const runtime: PragmaRuntime = {
63
+ store,
64
+ config: { tier: undefined, channel: "normal" },
65
+ cwd: process.cwd(),
66
+ packages: [],
67
+ dispose: () => closeStore(),
68
+ };
69
+ const { client: scopedClient, cleanup: closeClient } =
70
+ await createClientFromRuntime(runtime);
71
+ return {
72
+ client: scopedClient,
73
+ cleanup: async () => {
74
+ await closeClient();
75
+ runtime.dispose();
76
+ },
77
+ };
78
+ }
79
+
30
80
  // =============================================================================
31
- // Resource listing
81
+ // Resource template
32
82
  // =============================================================================
33
83
 
34
- describe("resource listing", () => {
35
- it("registers 1 resource template", async () => {
84
+ describe("resource template", () => {
85
+ it("registers 1 resource template with the {+uri} pattern", async () => {
36
86
  const { resourceTemplates } = await client.listResourceTemplates();
37
87
  expect(resourceTemplates).toHaveLength(1);
38
- expect(resourceTemplates[0]?.uriTemplate).toBe("{+uri}");
88
+ expect(resourceTemplates.at(0)?.uriTemplate).toBe("{+uri}");
39
89
  });
40
90
 
41
- it("lists resources including known entities", async () => {
91
+ it("documents the TBox/ABox split in the template description", async () => {
92
+ const { resourceTemplates } = await client.listResourceTemplates();
93
+ expect(resourceTemplates.at(0)?.description).toMatch(/TBox/);
94
+ });
95
+ });
96
+
97
+ // =============================================================================
98
+ // Resource listing — presence
99
+ // =============================================================================
100
+
101
+ describe("resource listing — presence", () => {
102
+ it("lists known instances, OWL classes, and code standards", async () => {
42
103
  const { resources } = await client.listResources();
43
104
  const uris = resources.map((r) => r.uri);
44
105
  expect(uris).toContain(`${P.ds}global.component.button`);
45
- expect(uris).toContain(`${P.ds}global.component.card`);
46
106
  expect(uris).toContain(`${P.ds}global`);
107
+ expect(uris).toContain(`${P.ds}UIBlock`);
108
+ expect(uris).toContain(`${P.ds}Component`);
109
+ expect(uris).toContain(`${P.cs}react_folder`);
110
+ expect(uris).toContain(`${P.cs}code_purity`);
47
111
  });
112
+ });
113
+
114
+ // =============================================================================
115
+ // Resource listing — TBox/ABox separation
116
+ // =============================================================================
48
117
 
49
- it("lists OWL classes as resources", async () => {
118
+ describe("resource listing — TBox/ABox separation", () => {
119
+ it("tags classes as tbox/class in _meta", async () => {
50
120
  const { resources } = await client.listResources();
51
- const uris = resources.map((r) => r.uri);
52
- expect(uris).toContain(`${P.ds}UIBlock`);
53
- expect(uris).toContain(`${P.ds}Component`);
121
+ const uiBlock = resources.find((r) => r.uri === `${P.ds}UIBlock`);
122
+ expect(metaOf(uiBlock ?? {})["pragma/box"]).toBe("tbox");
123
+ expect(metaOf(uiBlock ?? {})["pragma/category"]).toBe("class");
54
124
  });
55
125
 
56
- it("lists code standards as resources", async () => {
126
+ it("tags properties as tbox/property in _meta", async () => {
57
127
  const { resources } = await client.listResources();
58
- const uris = resources.map((r) => r.uri);
59
- expect(uris).toContain(`${P.cs}react_folder`);
60
- expect(uris).toContain(`${P.cs}code_purity`);
128
+ const tier = resources.find((r) => r.uri === `${P.ds}tier`);
129
+ expect(metaOf(tier ?? {})["pragma/box"]).toBe("tbox");
130
+ expect(metaOf(tier ?? {})["pragma/category"]).toBe("property");
61
131
  });
62
132
 
63
- it("uses label as resource name when available", async () => {
133
+ it("tags individuals as abox/individual with their class in _meta", async () => {
64
134
  const { resources } = await client.listResources();
65
135
  const button = resources.find(
66
136
  (r) => r.uri === `${P.ds}global.component.button`,
67
137
  );
68
- expect(button?.name).toBe("Button");
138
+ expect(metaOf(button ?? {})["pragma/box"]).toBe("abox");
139
+ expect(metaOf(button ?? {})["pragma/category"]).toBe("individual");
140
+ expect(metaOf(button ?? {})["pragma/type"]).toBe(`${P.ds}Component`);
141
+ });
142
+
143
+ it("places all TBox schema before any ABox individual", async () => {
144
+ const { resources } = await client.listResources();
145
+ const firstAbox = resources.findIndex(
146
+ (r) => metaOf(r)["pragma/box"] === "abox",
147
+ );
148
+ const lastTbox = resources.reduce(
149
+ (acc, r, i) => (metaOf(r)["pragma/box"] === "tbox" ? i : acc),
150
+ -1,
151
+ );
152
+ expect(firstAbox).toBeGreaterThan(-1);
153
+ expect(lastTbox).toBeGreaterThan(-1);
154
+ expect(lastTbox).toBeLessThan(firstAbox);
155
+ });
156
+
157
+ it("ranks schema above individuals via annotation priority", async () => {
158
+ const { resources } = await client.listResources();
159
+ const cls = resources.find((r) => r.uri === `${P.ds}Component`);
160
+ const individual = resources.find(
161
+ (r) => r.uri === `${P.ds}global.component.button`,
162
+ );
163
+ const clsPriority = cls?.annotations?.priority ?? 0;
164
+ const individualPriority = individual?.annotations?.priority ?? 1;
165
+ expect(clsPriority).toBeGreaterThan(individualPriority);
69
166
  });
167
+ });
168
+
169
+ // =============================================================================
170
+ // Resource listing — names, descriptions, grouping
171
+ // =============================================================================
70
172
 
71
- it("includes type-aware description for components", async () => {
173
+ describe("resource listing — names and grouping", () => {
174
+ it("prefixes class names with their category", async () => {
175
+ const { resources } = await client.listResources();
176
+ const cls = resources.find((r) => r.uri === `${P.ds}Component`);
177
+ expect(cls?.name).toBe("Class · Component");
178
+ });
179
+
180
+ it("prefixes property names with their category", async () => {
181
+ const { resources } = await client.listResources();
182
+ const prop = resources.find((r) => r.uri === `${P.ds}tier`);
183
+ expect(prop?.name).toBe("Property · tier");
184
+ });
185
+
186
+ it("groups individuals under their class label in the name", async () => {
72
187
  const { resources } = await client.listResources();
73
188
  const button = resources.find(
74
189
  (r) => r.uri === `${P.ds}global.component.button`,
75
190
  );
76
- expect(button?.description).toMatch(/^Component/);
191
+ expect(button?.name).toBe("Component · Button");
77
192
  });
78
193
 
79
- it("includes type-aware description for code standards", async () => {
194
+ it("surfaces instance counts on class entries", async () => {
80
195
  const { resources } = await client.listResources();
81
- const standard = resources.find((r) => r.uri === `${P.cs}code_purity`);
82
- expect(standard?.description).toMatch(/^Code Standard/);
196
+ const cls = resources.find((r) => r.uri === `${P.ds}Component`);
197
+ expect(metaOf(cls ?? {})["pragma/instanceCount"]).toBe(4);
198
+ expect(metaOf(cls ?? {})["pragma/truncated"]).toBe(false);
199
+ expect(cls?.description).toMatch(/4 individuals/);
200
+ });
201
+
202
+ it("leads an individual description with its class label", async () => {
203
+ const { resources } = await client.listResources();
204
+ const button = resources.find(
205
+ (r) => r.uri === `${P.ds}global.component.button`,
206
+ );
207
+ expect(button?.description).toMatch(/^Component/);
83
208
  });
84
209
 
85
210
  it("marks resources with assistant audience", async () => {
@@ -92,24 +217,197 @@ describe("resource listing", () => {
92
217
  });
93
218
 
94
219
  // =============================================================================
95
- // Read instances
220
+ // Resource listing — capping the ABox long tail
221
+ // =============================================================================
222
+
223
+ describe("resource listing — capping", () => {
224
+ it("caps individuals per class and reports the remainder", async () => {
225
+ const overflow = ABOX_PER_CLASS_LIMIT + 5;
226
+ const individuals = Array.from(
227
+ { length: overflow },
228
+ (_, i) => `ds:widget_${i} a ds:Gadget ; ds:name "Widget ${i}" .`,
229
+ ).join("\n");
230
+ const ttl = `@prefix ds: <${DEFAULT_PREFIX_MAP.ds}> .
231
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
232
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
233
+
234
+ ds:Gadget a owl:Class ; rdfs:label "Gadget" .
235
+ ds:name a owl:DatatypeProperty .
236
+
237
+ ${individuals}
238
+ `;
239
+ const scoped = await createClientForTtl(ttl);
240
+ try {
241
+ const { resources } = await scoped.client.listResources();
242
+ const shown = resources.filter(
243
+ (r) => metaOf(r)["pragma/type"] === `${P.ds}Gadget`,
244
+ );
245
+ expect(shown).toHaveLength(ABOX_PER_CLASS_LIMIT);
246
+
247
+ const cls = resources.find((r) => r.uri === `${P.ds}Gadget`);
248
+ expect(metaOf(cls ?? {})["pragma/instanceCount"]).toBe(overflow);
249
+ expect(metaOf(cls ?? {})["pragma/instancesShown"]).toBe(
250
+ ABOX_PER_CLASS_LIMIT,
251
+ );
252
+ expect(metaOf(cls ?? {})["pragma/truncated"]).toBe(true);
253
+ expect(cls?.description).toMatch(
254
+ new RegExp(`showing ${ABOX_PER_CLASS_LIMIT} of ${overflow}`),
255
+ );
256
+ } finally {
257
+ await scoped.cleanup();
258
+ }
259
+ });
260
+ });
261
+
262
+ // =============================================================================
263
+ // Resource listing — foreign namespace label fallback
264
+ // =============================================================================
265
+
266
+ describe("resource listing — foreign namespace label fallback", () => {
267
+ const ttl = `
268
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
269
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
270
+ @prefix skos: <http://www.w3.org/2004/02/skos/core#> .
271
+ @prefix dcterms: <http://purl.org/dc/terms/> .
272
+ @prefix schema: <http://schema.org/> .
273
+ @prefix ex: <http://foreign.example/> .
274
+
275
+ ex:Widget a owl:Class ; skos:prefLabel "Foreign Widget" .
276
+ ex:gadget a ex:Widget ; dcterms:title "Shiny Gadget" .
277
+ ex:thing a ex:Widget ; schema:name "Plain Thing" .
278
+ `;
279
+
280
+ it("resolves a foreign class name via skos:prefLabel", async () => {
281
+ const scoped = await createClientForTtl(ttl, {
282
+ ex: "http://foreign.example/",
283
+ });
284
+ try {
285
+ const { resources } = await scoped.client.listResources();
286
+ const cls = resources.find((r) => r.uri === "ex:Widget");
287
+ expect(cls?.name).toBe("Class · Foreign Widget");
288
+ } finally {
289
+ await scoped.cleanup();
290
+ }
291
+ });
292
+
293
+ it("resolves foreign individual names via dcterms:title and schema:name", async () => {
294
+ const scoped = await createClientForTtl(ttl, {
295
+ ex: "http://foreign.example/",
296
+ });
297
+ try {
298
+ const { resources } = await scoped.client.listResources();
299
+ const gadget = resources.find((r) => r.uri === "ex:gadget");
300
+ const thing = resources.find((r) => r.uri === "ex:thing");
301
+ expect(gadget?.name).toBe("Foreign Widget · Shiny Gadget");
302
+ expect(thing?.name).toBe("Foreign Widget · Plain Thing");
303
+ } finally {
304
+ await scoped.cleanup();
305
+ }
306
+ });
307
+ });
308
+
309
+ // =============================================================================
310
+ // Resource listing — longest-namespace prefix resolution
311
+ // =============================================================================
312
+
313
+ describe("resource listing — longest-namespace prefix resolution", () => {
314
+ it("compacts with the longest matching namespace", async () => {
315
+ const ttl = `
316
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
317
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
318
+ @prefix base: <http://ex.org/> .
319
+ @prefix sub: <http://ex.org/sub/> .
320
+
321
+ base:Thing a owl:Class ; rdfs:label "Thing" .
322
+ sub:Widget a owl:Class ; rdfs:label "Widget" .
323
+ `;
324
+ // `base` is registered before `sub`; first-match compaction would
325
+ // mis-compact to "base:sub/Widget".
326
+ const scoped = await createClientForTtl(ttl, {
327
+ base: "http://ex.org/",
328
+ sub: "http://ex.org/sub/",
329
+ });
330
+ try {
331
+ const { resources } = await scoped.client.listResources();
332
+ const uris = resources.map((r) => r.uri);
333
+ expect(uris).toContain("sub:Widget");
334
+ expect(uris).not.toContain("base:sub/Widget");
335
+ } finally {
336
+ await scoped.cleanup();
337
+ }
338
+ });
339
+ });
340
+
341
+ // =============================================================================
342
+ // Autocomplete
343
+ // =============================================================================
344
+
345
+ async function completeUri(value: string): Promise<{
346
+ values: string[];
347
+ total?: number;
348
+ }> {
349
+ const result = await client.complete({
350
+ ref: { type: "ref/resource", uri: "{+uri}" },
351
+ argument: { name: "uri", value },
352
+ });
353
+ return result.completion;
354
+ }
355
+
356
+ describe("autocomplete", () => {
357
+ it("matches a URI substring, not just a prefix", async () => {
358
+ const { values } = await completeUri("button");
359
+ expect(values).toContain(`${P.ds}global.component.button`);
360
+ });
361
+
362
+ it("matches a human label fragment", async () => {
363
+ const { values } = await completeUri("Card");
364
+ expect(values).toContain(`${P.ds}global.component.card`);
365
+ });
366
+
367
+ it("returns compacted URIs, not full URIs", async () => {
368
+ const { values } = await completeUri("button");
369
+ expect(values.every((v) => !v.startsWith("http"))).toBe(true);
370
+ });
371
+
372
+ it("ranks an exact label match above a substring match", async () => {
373
+ const { values } = await completeUri("component");
374
+ const exactIdx = values.indexOf(`${P.ds}Component`);
375
+ const substringIdx = values.indexOf(`${P.ds}global.component.button`);
376
+ expect(exactIdx).toBeGreaterThan(-1);
377
+ expect(substringIdx).toBeGreaterThan(-1);
378
+ expect(exactIdx).toBeLessThan(substringIdx);
379
+ });
380
+
381
+ it("caps the number of suggestions", async () => {
382
+ const { values } = await completeUri("");
383
+ expect(values.length).toBeLessThanOrEqual(COMPLETION_LIMIT);
384
+ expect(values).toHaveLength(COMPLETION_LIMIT);
385
+ });
386
+ });
387
+
388
+ // =============================================================================
389
+ // Read — component instance (ABox)
96
390
  // =============================================================================
97
391
 
98
392
  describe("read component instance", () => {
99
- it("returns entity with types and label", async () => {
393
+ it("returns an abox individual with types, label, and class pointer", async () => {
100
394
  const result = await client.readResource({
101
395
  uri: `${P.ds}global.component.button`,
102
396
  });
103
397
  const entity = parseContents(result) as {
104
- uri: string;
105
398
  prefixed: string;
399
+ box: string;
400
+ category: string;
106
401
  types: string[];
107
402
  label: string | null;
108
- properties: { predicate: string; values: unknown[] }[];
403
+ instanceOf: string | null;
109
404
  };
110
405
  expect(entity.prefixed).toBe(`${P.ds}global.component.button`);
406
+ expect(entity.box).toBe("abox");
407
+ expect(entity.category).toBe("individual");
111
408
  expect(entity.types).toContain(`${P.ds}Component`);
112
409
  expect(entity.label).toBe("Button");
410
+ expect(entity.instanceOf).toBe(`${P.ds}Component`);
113
411
  });
114
412
 
115
413
  it("resolves level-1 URI objects to summaries", async () => {
@@ -119,20 +417,13 @@ describe("read component instance", () => {
119
417
  const entity = parseContents(result) as {
120
418
  properties: {
121
419
  predicate: string;
122
- values: {
123
- type: string;
124
- uri?: string;
125
- prefixed?: string;
126
- label?: string | null;
127
- }[];
420
+ values: { type: string; prefixed?: string }[];
128
421
  }[];
129
422
  };
130
-
131
423
  const tierProp = entity.properties.find(
132
424
  (p) => p.predicate === `${P.ds}tier`,
133
425
  );
134
- expect(tierProp).toBeDefined();
135
- const tierValue = tierProp?.values[0];
426
+ const tierValue = tierProp?.values.at(0);
136
427
  expect(tierValue?.type).toBe("uri");
137
428
  expect(tierValue?.prefixed).toBe(`${P.ds}global`);
138
429
  });
@@ -147,53 +438,85 @@ describe("read component instance", () => {
147
438
  values: { type: string; value?: string }[];
148
439
  }[];
149
440
  };
150
-
151
441
  const nameProp = entity.properties.find(
152
442
  (p) => p.predicate === `${P.ds}name`,
153
443
  );
154
- expect(nameProp).toBeDefined();
155
- expect(nameProp?.values[0]?.type).toBe("literal");
156
- expect(nameProp?.values[0]?.value).toBe("Button");
444
+ expect(nameProp?.values.at(0)?.type).toBe("literal");
445
+ expect(nameProp?.values.at(0)?.value).toBe("Button");
446
+ });
447
+
448
+ it("keeps URL-shaped literals as literals, not URIs", async () => {
449
+ const result = await client.readResource({
450
+ uri: `${P.ds}global.component.button`,
451
+ });
452
+ const entity = parseContents(result) as {
453
+ properties: {
454
+ predicate: string;
455
+ values: { type: string; value?: string }[];
456
+ }[];
457
+ };
458
+ const figma = entity.properties.find(
459
+ (p) => p.predicate === `${P.ds}figmaLink`,
460
+ );
461
+ const value = figma?.values.at(0);
462
+ expect(value?.type).toBe("literal");
463
+ expect(value?.value).toBe("https://figma.com/design/example/Button");
157
464
  });
158
465
  });
159
466
 
160
467
  // =============================================================================
161
- // Read OWL classes
468
+ // Read — OWL class (TBox)
162
469
  // =============================================================================
163
470
 
164
471
  describe("read OWL class", () => {
165
- it("returns class with rdfs:label", async () => {
166
- const result = await client.readResource({
167
- uri: `${P.ds}UIBlock`,
168
- });
472
+ it("returns a tbox class enriched with schema relations", async () => {
473
+ const result = await client.readResource({ uri: `${P.ds}UIBlock` });
169
474
  const entity = parseContents(result) as {
475
+ box: string;
476
+ category: string;
170
477
  types: string[];
171
- label: string | null;
478
+ subClasses?: string[];
479
+ instanceCount?: number;
480
+ declaredProperties?: string[];
172
481
  properties: {
173
482
  predicate: string;
174
483
  values: { type: string; value?: string }[];
175
484
  }[];
176
485
  };
486
+ expect(entity.box).toBe("tbox");
487
+ expect(entity.category).toBe("class");
177
488
  expect(entity.types).toContain(`${P.owl}Class`);
489
+ expect(entity.subClasses).toContain(`${P.ds}Component`);
490
+ expect(entity.subClasses).toContain(`${P.ds}Subcomponent`);
491
+ expect(entity.declaredProperties).toContain(`${P.ds}tier`);
492
+ expect(entity.instanceCount).toBe(0);
493
+
178
494
  const labelProp = entity.properties.find(
179
495
  (p) => p.predicate === `${P.rdfs}label`,
180
496
  );
181
- expect(labelProp?.values[0]?.value).toBe("UI Block");
497
+ expect(labelProp?.values.at(0)?.value).toBe("UI Block");
498
+ });
499
+
500
+ it("reports superclasses and instance count for a subclass", async () => {
501
+ const result = await client.readResource({ uri: `${P.ds}Component` });
502
+ const entity = parseContents(result) as {
503
+ superClasses?: string[];
504
+ instanceCount?: number;
505
+ };
506
+ expect(entity.superClasses).toContain(`${P.ds}UIBlock`);
507
+ expect(entity.instanceCount).toBe(4);
182
508
  });
183
509
  });
184
510
 
185
511
  // =============================================================================
186
- // Read code standards
512
+ // Read — code standard and tier
187
513
  // =============================================================================
188
514
 
189
515
  describe("read code standard", () => {
190
516
  it("returns standard with properties", async () => {
191
- const result = await client.readResource({
192
- uri: `${P.cs}code_purity`,
193
- });
517
+ const result = await client.readResource({ uri: `${P.cs}code_purity` });
194
518
  const entity = parseContents(result) as {
195
519
  types: string[];
196
- label: string | null;
197
520
  properties: {
198
521
  predicate: string;
199
522
  values: { type: string; value?: string }[];
@@ -204,41 +527,28 @@ describe("read code standard", () => {
204
527
  const nameProp = entity.properties.find(
205
528
  (p) => p.predicate === `${P.cs}name`,
206
529
  );
207
- expect(nameProp?.values[0]?.value).toBe("code/function/purity");
530
+ expect(nameProp?.values.at(0)?.value).toBe("code/function/purity");
208
531
 
209
532
  const doProp = entity.properties.find((p) => p.predicate === `${P.cs}do`);
210
- expect(doProp).toBeDefined();
211
533
  expect(doProp?.values.length).toBeGreaterThan(0);
212
-
213
- const dontProp = entity.properties.find(
214
- (p) => p.predicate === `${P.cs}dont`,
215
- );
216
- expect(dontProp).toBeDefined();
217
534
  });
218
535
  });
219
536
 
220
- // =============================================================================
221
- // Read tiers
222
- // =============================================================================
223
-
224
537
  describe("read tier", () => {
225
538
  it("returns tier with name", async () => {
226
- const result = await client.readResource({
227
- uri: `${P.ds}apps_lxd`,
228
- });
539
+ const result = await client.readResource({ uri: `${P.ds}apps_lxd` });
229
540
  const entity = parseContents(result) as {
230
541
  types: string[];
231
542
  properties: {
232
543
  predicate: string;
233
- values: { type: string; value?: string; prefixed?: string }[];
544
+ values: { type: string; value?: string }[];
234
545
  }[];
235
546
  };
236
547
  expect(entity.types).toContain(`${P.ds}Tier`);
237
-
238
548
  const nameProp = entity.properties.find(
239
549
  (p) => p.predicate === `${P.ds}name`,
240
550
  );
241
- expect(nameProp?.values[0]?.value).toBe("apps/lxd");
551
+ expect(nameProp?.values.at(0)?.value).toBe("apps/lxd");
242
552
  });
243
553
  });
244
554
 
@@ -248,10 +558,8 @@ describe("read tier", () => {
248
558
 
249
559
  describe("error handling", () => {
250
560
  it("returns text/plain error for unknown entity", async () => {
251
- const result = await client.readResource({
252
- uri: `${P.ds}nonexistent`,
253
- });
254
- const first = result.contents[0] as ResourceContent;
561
+ const result = await client.readResource({ uri: `${P.ds}nonexistent` });
562
+ const first = result.contents.at(0) as ResourceContent;
255
563
  expect(first.mimeType).toBe("text/plain");
256
564
  expect(first.text).toContain("ENTITY_NOT_FOUND");
257
565
  });