@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
@@ -0,0 +1,1207 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`golden: executeGenerator — components > lit — dry-run plan 1`] = `
4
+ "
5
+ ├─ Info Generating Lit web component: Button
6
+ ├─ Create dir src/components/Button
7
+ ├─ Create file src/components/Button/Button.ts
8
+ ├─ Create file src/components/Button/index.ts
9
+ ├─ Create file src/components/Button/types.ts
10
+ ├─ Create file src/components/Button/Button.tests.ts
11
+ ├─ Create file src/components/Button/Button.stories.ts
12
+ ├─ Create file src/components/Button/styles.css
13
+ ├─ Create file src/components/index.ts
14
+ └─ Info Created Button component at src/components/Button
15
+
16
+ Dry-run complete. No files were modified.
17
+ Tip: Use --show-files to see generated file contents
18
+ "
19
+ `;
20
+
21
+ exports[`golden: executeGenerator — components > lit — json plan 1`] = `
22
+ "{
23
+ "generator": {
24
+ "name": "component/lit",
25
+ "version": "0.1.0",
26
+ "description": "Generate a Lit web component with TypeScript, tests, stories, and styles"
27
+ },
28
+ "answers": {
29
+ "componentPath": "src/components/Button",
30
+ "withStyles": true,
31
+ "withStories": true
32
+ },
33
+ "plan": [
34
+ {
35
+ "action": "info",
36
+ "path": "Generating Lit web component: Button"
37
+ },
38
+ {
39
+ "action": "mkdir",
40
+ "path": "src/components/Button"
41
+ },
42
+ {
43
+ "action": "create",
44
+ "path": "src/components/Button/Button.ts",
45
+ "lines": 25
46
+ },
47
+ {
48
+ "action": "create",
49
+ "path": "src/components/Button/index.ts",
50
+ "lines": 3
51
+ },
52
+ {
53
+ "action": "create",
54
+ "path": "src/components/Button/types.ts",
55
+ "lines": 7
56
+ },
57
+ {
58
+ "action": "create",
59
+ "path": "src/components/Button/Button.tests.ts",
60
+ "lines": 28
61
+ },
62
+ {
63
+ "action": "create",
64
+ "path": "src/components/Button/Button.stories.ts",
65
+ "lines": 18
66
+ },
67
+ {
68
+ "action": "create",
69
+ "path": "src/components/Button/styles.css",
70
+ "lines": 50
71
+ },
72
+ {
73
+ "action": "create",
74
+ "path": "src/components/index.ts",
75
+ "lines": 2
76
+ },
77
+ {
78
+ "action": "info",
79
+ "path": "Created Button component at src/components/Button"
80
+ }
81
+ ],
82
+ "files": {
83
+ "src/components/Button/Button.ts": "import { html, LitElement } from \\"lit\\";\\nimport { customElement } from \\"lit/decorators.js\\";\\nimport styles from \\"./styles.css\\";\\nimport type { ButtonProps } from \\"./types.js\\";\\n\\nconst componentCssClassName = \\"ds button\\";\\n\\n/**\\n * Button web component.\\n *\\n * @implements ds:global.component.button\\n */\\n@customElement(\\"ds-button\\")\\nexport default class Button extends LitElement implements ButtonProps {\\n static styles = styles;\\n\\n render() {\\n return html\`\\n <div class=\\"\${componentCssClassName}\\">\\n <slot></slot>\\n </div>\\n \`;\\n }\\n}\\n",
84
+ "src/components/Button/index.ts": "export { default as Button } from \\"./Button.js\\";\\nexport type { ButtonProps } from \\"./types.js\\";\\n",
85
+ "src/components/Button/types.ts": "/**\\n * Props for the Button component.\\n */\\nexport interface ButtonProps {\\n // Define props specific to Button here\\n}\\n",
86
+ "src/components/Button/Button.tests.ts": "import { afterEach, beforeEach, describe, expect, it } from \\"vitest\\";\\nimport \\"./Button.js\\";\\nimport type Button from \\"./Button.js\\";\\n\\ndescribe(\\"Button component\\", () => {\\n let elem: Button;\\n\\n beforeEach(() => {\\n elem = document.createElement(\\"ds-button\\") as Button;\\n document.body.appendChild(elem);\\n });\\n\\n afterEach(() => {\\n elem.remove();\\n });\\n\\n it(\\"should render component\\", async () => {\\n await customElements.whenDefined(\\"ds-button\\");\\n\\n const container = elem.shadowRoot?.querySelector(\\".ds.button\\");\\n expect(container).toBeTruthy();\\n });\\n\\n it(\\"should have correct tag name\\", () => {\\n expect(elem.tagName.toLowerCase()).toBe(\\"ds-button\\");\\n });\\n});\\n",
87
+ "src/components/Button/Button.stories.ts": "import type { Meta, StoryObj } from \\"@storybook/web-components-vite\\";\\nimport { html } from \\"lit\\";\\n\\nimport \\"./Button.js\\";\\nimport type Button from \\"./Button.js\\";\\n\\nconst meta = {\\n title: \\"Web Components/Button\\",\\n tags: [\\"autodocs\\"],\\n} satisfies Meta<Button>;\\n\\nexport default meta;\\ntype Story = StoryObj<Button>;\\n\\nexport const Default: Story = {\\n render: () => html\`<ds-button>Hello, Button!</ds-button>\`,\\n};\\n",
88
+ "src/components/Button/styles.css": "/*\\n * Lit Layout Primer\\n * Delete this comment block when you're done implementing.\\n *\\n * Styles live in a shadow DOM, isolated from the page by default.\\n *\\n * Two layers:\\n *\\n * 1) :host — the outer shell, how this component presents to its parent layout.\\n * display, width, height (always apply)\\n * grid-column, grid-row, align-self, justify-self (parent must be grid/flex)\\n *\\n * 2) .ds.button — the inner shell, how this component arranges its shadow children.\\n * display: grid/flex, gap, grid-template-columns, align-items, justify-content\\n *\\n * Subgrid pattern — use when the parent defines grid tracks you want to inherit:\\n *\\n * In the parent component:\\n * .ds.parent { display: grid; grid-template-columns: repeat(4, 1fr); }\\n *\\n * In this component:\\n * :host { display: grid; grid-column: span 2; }\\n * .ds.button { display: grid; grid-template-columns: subgrid; }\\n *\\n * In this example, the parent establishes a 4-column grid.\\n * This component claims 2 of those columns and inherits their widths via subgrid.\\n *\\n * Keep in mind:\\n * - Custom elements are display: inline by default — always set display on :host.\\n * - Flex/grid rules only affect direct children of the container, not deeper descendants.\\n * - External CSS cannot pierce the shadow boundary — expose CSS custom properties for theming.\\n */\\n\\n:host {\\n /*\\n * Host controls the outer layout role.\\n * Declare component CSS variables here when needed.\\n * Variables must be on :host to be accessible inside the shadow tree.\\n *\\n * Example:\\n * --button-color-background: var(--color-background-default);\\n * --button-color-text: var(--color-text-default);\\n */\\n display: inline-flex;\\n}\\n\\n.ds.button {\\n /* Inner layout and visual styles go here. */\\n}\\n",
89
+ "src/components/index.ts": "export * from \\"./Button/index.js\\";\\n"
90
+ },
91
+ "executeCommand": "summon component/lit --component-path src/components/Button --yes"
92
+ }
93
+ "
94
+ `;
95
+
96
+ exports[`golden: executeGenerator — components > lit — llm plan 1`] = `
97
+ "# component/lit
98
+
99
+ > Generate a Lit web component with TypeScript, tests, stories, and styles
100
+ > v0.1.0
101
+
102
+ ## Answers
103
+
104
+ | Option | Value |
105
+ |--------|-------|
106
+ | componentPath | src/components/Button |
107
+ | withStyles | true |
108
+ | withStories | true |
109
+
110
+ ## Plan
111
+
112
+ | Action | Path | Lines |
113
+ |--------|------|-------|
114
+ | info | Generating Lit web component: Button | |
115
+ | mkdir | src/components/Button | |
116
+ | create | src/components/Button/Button.ts | 25 |
117
+ | create | src/components/Button/index.ts | 3 |
118
+ | create | src/components/Button/types.ts | 7 |
119
+ | create | src/components/Button/Button.tests.ts | 28 |
120
+ | create | src/components/Button/Button.stories.ts | 18 |
121
+ | create | src/components/Button/styles.css | 50 |
122
+ | create | src/components/index.ts | 2 |
123
+ | info | Created Button component at src/components/Button | |
124
+
125
+ ## Files
126
+
127
+ ### src/components/Button/Button.ts
128
+
129
+ \`\`\`ts
130
+ import { html, LitElement } from "lit";
131
+ import { customElement } from "lit/decorators.js";
132
+ import styles from "./styles.css";
133
+ import type { ButtonProps } from "./types.js";
134
+
135
+ const componentCssClassName = "ds button";
136
+
137
+ /**
138
+ * Button web component.
139
+ *
140
+ * @implements ds:global.component.button
141
+ */
142
+ @customElement("ds-button")
143
+ export default class Button extends LitElement implements ButtonProps {
144
+ static styles = styles;
145
+
146
+ render() {
147
+ return html\`
148
+ <div class="\${componentCssClassName}">
149
+ <slot></slot>
150
+ </div>
151
+ \`;
152
+ }
153
+ }
154
+
155
+ \`\`\`
156
+
157
+ ### src/components/Button/index.ts
158
+
159
+ \`\`\`ts
160
+ export { default as Button } from "./Button.js";
161
+ export type { ButtonProps } from "./types.js";
162
+
163
+ \`\`\`
164
+
165
+ ### src/components/Button/types.ts
166
+
167
+ \`\`\`ts
168
+ /**
169
+ * Props for the Button component.
170
+ */
171
+ export interface ButtonProps {
172
+ // Define props specific to Button here
173
+ }
174
+
175
+ \`\`\`
176
+
177
+ ### src/components/Button/Button.tests.ts
178
+
179
+ \`\`\`ts
180
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
181
+ import "./Button.js";
182
+ import type Button from "./Button.js";
183
+
184
+ describe("Button component", () => {
185
+ let elem: Button;
186
+
187
+ beforeEach(() => {
188
+ elem = document.createElement("ds-button") as Button;
189
+ document.body.appendChild(elem);
190
+ });
191
+
192
+ afterEach(() => {
193
+ elem.remove();
194
+ });
195
+
196
+ it("should render component", async () => {
197
+ await customElements.whenDefined("ds-button");
198
+
199
+ const container = elem.shadowRoot?.querySelector(".ds.button");
200
+ expect(container).toBeTruthy();
201
+ });
202
+
203
+ it("should have correct tag name", () => {
204
+ expect(elem.tagName.toLowerCase()).toBe("ds-button");
205
+ });
206
+ });
207
+
208
+ \`\`\`
209
+
210
+ ### src/components/Button/Button.stories.ts
211
+
212
+ \`\`\`ts
213
+ import type { Meta, StoryObj } from "@storybook/web-components-vite";
214
+ import { html } from "lit";
215
+
216
+ import "./Button.js";
217
+ import type Button from "./Button.js";
218
+
219
+ const meta = {
220
+ title: "Web Components/Button",
221
+ tags: ["autodocs"],
222
+ } satisfies Meta<Button>;
223
+
224
+ export default meta;
225
+ type Story = StoryObj<Button>;
226
+
227
+ export const Default: Story = {
228
+ render: () => html\`<ds-button>Hello, Button!</ds-button>\`,
229
+ };
230
+
231
+ \`\`\`
232
+
233
+ ### src/components/Button/styles.css
234
+
235
+ \`\`\`css
236
+ /*
237
+ * Lit Layout Primer
238
+ * Delete this comment block when you're done implementing.
239
+ *
240
+ * Styles live in a shadow DOM, isolated from the page by default.
241
+ *
242
+ * Two layers:
243
+ *
244
+ * 1) :host — the outer shell, how this component presents to its parent layout.
245
+ * display, width, height (always apply)
246
+ * grid-column, grid-row, align-self, justify-self (parent must be grid/flex)
247
+ *
248
+ * 2) .ds.button — the inner shell, how this component arranges its shadow children.
249
+ * display: grid/flex, gap, grid-template-columns, align-items, justify-content
250
+ *
251
+ * Subgrid pattern — use when the parent defines grid tracks you want to inherit:
252
+ *
253
+ * In the parent component:
254
+ * .ds.parent { display: grid; grid-template-columns: repeat(4, 1fr); }
255
+ *
256
+ * In this component:
257
+ * :host { display: grid; grid-column: span 2; }
258
+ * .ds.button { display: grid; grid-template-columns: subgrid; }
259
+ *
260
+ * In this example, the parent establishes a 4-column grid.
261
+ * This component claims 2 of those columns and inherits their widths via subgrid.
262
+ *
263
+ * Keep in mind:
264
+ * - Custom elements are display: inline by default — always set display on :host.
265
+ * - Flex/grid rules only affect direct children of the container, not deeper descendants.
266
+ * - External CSS cannot pierce the shadow boundary — expose CSS custom properties for theming.
267
+ */
268
+
269
+ :host {
270
+ /*
271
+ * Host controls the outer layout role.
272
+ * Declare component CSS variables here when needed.
273
+ * Variables must be on :host to be accessible inside the shadow tree.
274
+ *
275
+ * Example:
276
+ * --button-color-background: var(--color-background-default);
277
+ * --button-color-text: var(--color-text-default);
278
+ */
279
+ display: inline-flex;
280
+ }
281
+
282
+ .ds.button {
283
+ /* Inner layout and visual styles go here. */
284
+ }
285
+
286
+ \`\`\`
287
+
288
+ ### src/components/index.ts
289
+
290
+ \`\`\`ts
291
+ export * from "./Button/index.js";
292
+
293
+ \`\`\`
294
+
295
+ ---
296
+
297
+ Dry-run complete. No files were modified.
298
+
299
+ To execute:
300
+
301
+ \`\`\`sh
302
+ summon component/lit --component-path src/components/Button --yes
303
+ \`\`\`
304
+ "
305
+ `;
306
+
307
+ exports[`golden: executeGenerator — components > react — dry-run plan 1`] = `
308
+ "
309
+ ├─ Info Generating React component: Button
310
+ ├─ Create dir src/components/Button
311
+ ├─ Create file src/components/Button/Button.tsx
312
+ ├─ Create file src/components/Button/types.ts
313
+ ├─ Create file src/components/Button/index.ts
314
+ ├─ Create file src/components/Button/Button.tests.tsx
315
+ ├─ Create file src/components/Button/Button.ssr.tests.tsx
316
+ ├─ Create file src/components/Button/Button.stories.tsx
317
+ ├─ Create file src/components/Button/styles.css
318
+ ├─ Create file src/components/index.ts
319
+ └─ Info Created Button component at src/components/Button
320
+
321
+ Dry-run complete. No files were modified.
322
+ Tip: Use --show-files to see generated file contents
323
+ "
324
+ `;
325
+
326
+ exports[`golden: executeGenerator — components > react — json plan 1`] = `
327
+ "{
328
+ "generator": {
329
+ "name": "component/react",
330
+ "version": "0.1.0",
331
+ "description": "Generate a React component with TypeScript, tests, stories, and styles"
332
+ },
333
+ "answers": {
334
+ "componentPath": "src/components/Button",
335
+ "withStyles": true,
336
+ "withStories": true,
337
+ "withSsrTests": true
338
+ },
339
+ "plan": [
340
+ {
341
+ "action": "info",
342
+ "path": "Generating React component: Button"
343
+ },
344
+ {
345
+ "action": "mkdir",
346
+ "path": "src/components/Button"
347
+ },
348
+ {
349
+ "action": "create",
350
+ "path": "src/components/Button/Button.tsx",
351
+ "lines": 26
352
+ },
353
+ {
354
+ "action": "create",
355
+ "path": "src/components/Button/types.ts",
356
+ "lines": 7
357
+ },
358
+ {
359
+ "action": "create",
360
+ "path": "src/components/Button/index.ts",
361
+ "lines": 3
362
+ },
363
+ {
364
+ "action": "create",
365
+ "path": "src/components/Button/Button.tests.tsx",
366
+ "lines": 23
367
+ },
368
+ {
369
+ "action": "create",
370
+ "path": "src/components/Button/Button.ssr.tests.tsx",
371
+ "lines": 12
372
+ },
373
+ {
374
+ "action": "create",
375
+ "path": "src/components/Button/Button.stories.tsx",
376
+ "lines": 25
377
+ },
378
+ {
379
+ "action": "create",
380
+ "path": "src/components/Button/styles.css",
381
+ "lines": 4
382
+ },
383
+ {
384
+ "action": "create",
385
+ "path": "src/components/index.ts",
386
+ "lines": 2
387
+ },
388
+ {
389
+ "action": "info",
390
+ "path": "Created Button component at src/components/Button"
391
+ }
392
+ ],
393
+ "files": {
394
+ "src/components/Button/Button.tsx": "import type React from \\"react\\";\\nimport type { ButtonProps } from \\"./types.js\\";\\nimport \\"./styles.css\\";\\n\\nconst componentCssClassName = \\"ds button\\";\\n\\n/**\\n * Button component\\n */\\nconst Button = ({\\n\\tclassName,\\n\\tchildren,\\n\\t...props\\n}: ButtonProps): React.ReactElement => {\\n\\treturn (\\n\\t\\t<div\\n\\t\\t\\tclassName={[componentCssClassName, className].filter(Boolean).join(\\" \\")}\\n\\t\\t\\t{...props}\\n\\t\\t>\\n\\t\\t\\t{children}\\n\\t\\t</div>\\n\\t);\\n};\\n\\nexport default Button;\\n",
395
+ "src/components/Button/types.ts": "import type { HTMLAttributes, ReactNode } from \\"react\\";\\n\\nexport interface ButtonProps extends HTMLAttributes<HTMLDivElement> {\\n\\t/** Content to render inside the component */\\n\\tchildren?: ReactNode;\\n}\\n",
396
+ "src/components/Button/index.ts": "export { default as Button } from \\"./Button.js\\";\\nexport type * from \\"./types.js\\";\\n",
397
+ "src/components/Button/Button.tests.tsx": "import { describe, expect, it } from \\"vitest\\";\\nimport { render, screen } from \\"@testing-library/react\\";\\nimport Button from \\"./Button.js\\";\\n\\ndescribe(\\"Button\\", () => {\\n\\tit(\\"renders children\\", () => {\\n\\t\\trender(<Button>Test content</Button>);\\n\\t\\texpect(screen.getByText(\\"Test content\\")).toBeInTheDocument();\\n\\t});\\n\\n\\tit(\\"applies custom className\\", () => {\\n\\t\\trender(<Button className=\\"custom-class\\">Content</Button>);\\n\\t\\tconst element = screen.getByText(\\"Content\\");\\n\\t\\texpect(element.className).toContain(\\"ds button\\");\\n\\t\\texpect(element.className).toContain(\\"custom-class\\");\\n\\t});\\n\\n\\tit(\\"passes through additional props\\", () => {\\n\\t\\trender(<Button data-testid=\\"test-component\\">Content</Button>);\\n\\t\\texpect(screen.getByTestId(\\"test-component\\")).toBeInTheDocument();\\n\\t});\\n});\\n",
398
+ "src/components/Button/Button.ssr.tests.tsx": "import { describe, expect, it } from \\"vitest\\";\\nimport { renderToString } from \\"react-dom/server\\";\\nimport Button from \\"./Button.js\\";\\n\\ndescribe(\\"Button SSR\\", () => {\\n\\tit(\\"renders without hydration errors\\", () => {\\n\\t\\tconst html = renderToString(<Button>Test content</Button>);\\n\\t\\texpect(html).toContain(\\"Test content\\");\\n\\t\\texpect(html).toContain(\\"ds button\\");\\n\\t});\\n});\\n",
399
+ "src/components/Button/Button.stories.tsx": "import type { Meta, StoryObj } from \\"@storybook/react-vite\\";\\nimport Button from \\"./Button.js\\";\\n\\nconst meta: Meta<typeof Button> = {\\n\\ttitle: \\"Components/Button\\",\\n\\tcomponent: Button,\\n\\ttags: [\\"autodocs\\"],\\n};\\n\\nexport default meta;\\ntype Story = StoryObj<typeof Button>;\\n\\nexport const Default: Story = {\\n\\targs: {\\n\\t\\tchildren: \\"Hello, Button!\\",\\n\\t},\\n};\\n\\nexport const WithCustomClass: Story = {\\n\\targs: {\\n\\t\\tchildren: \\"Custom styled\\",\\n\\t\\tclassName: \\"custom-class\\",\\n\\t},\\n};\\n",
400
+ "src/components/Button/styles.css": ".ds.button {\\n\\t/* Component styles */\\n}\\n",
401
+ "src/components/index.ts": "export * from \\"./Button/index.js\\";\\n"
402
+ },
403
+ "executeCommand": "summon component/react --component-path src/components/Button --yes"
404
+ }
405
+ "
406
+ `;
407
+
408
+ exports[`golden: executeGenerator — components > react — llm plan 1`] = `
409
+ "# component/react
410
+
411
+ > Generate a React component with TypeScript, tests, stories, and styles
412
+ > v0.1.0
413
+
414
+ ## Answers
415
+
416
+ | Option | Value |
417
+ |--------|-------|
418
+ | componentPath | src/components/Button |
419
+ | withStyles | true |
420
+ | withStories | true |
421
+ | withSsrTests | true |
422
+
423
+ ## Plan
424
+
425
+ | Action | Path | Lines |
426
+ |--------|------|-------|
427
+ | info | Generating React component: Button | |
428
+ | mkdir | src/components/Button | |
429
+ | create | src/components/Button/Button.tsx | 26 |
430
+ | create | src/components/Button/types.ts | 7 |
431
+ | create | src/components/Button/index.ts | 3 |
432
+ | create | src/components/Button/Button.tests.tsx | 23 |
433
+ | create | src/components/Button/Button.ssr.tests.tsx | 12 |
434
+ | create | src/components/Button/Button.stories.tsx | 25 |
435
+ | create | src/components/Button/styles.css | 4 |
436
+ | create | src/components/index.ts | 2 |
437
+ | info | Created Button component at src/components/Button | |
438
+
439
+ ## Files
440
+
441
+ ### src/components/Button/Button.tsx
442
+
443
+ \`\`\`tsx
444
+ import type React from "react";
445
+ import type { ButtonProps } from "./types.js";
446
+ import "./styles.css";
447
+
448
+ const componentCssClassName = "ds button";
449
+
450
+ /**
451
+ * Button component
452
+ */
453
+ const Button = ({
454
+ className,
455
+ children,
456
+ ...props
457
+ }: ButtonProps): React.ReactElement => {
458
+ return (
459
+ <div
460
+ className={[componentCssClassName, className].filter(Boolean).join(" ")}
461
+ {...props}
462
+ >
463
+ {children}
464
+ </div>
465
+ );
466
+ };
467
+
468
+ export default Button;
469
+
470
+ \`\`\`
471
+
472
+ ### src/components/Button/types.ts
473
+
474
+ \`\`\`ts
475
+ import type { HTMLAttributes, ReactNode } from "react";
476
+
477
+ export interface ButtonProps extends HTMLAttributes<HTMLDivElement> {
478
+ /** Content to render inside the component */
479
+ children?: ReactNode;
480
+ }
481
+
482
+ \`\`\`
483
+
484
+ ### src/components/Button/index.ts
485
+
486
+ \`\`\`ts
487
+ export { default as Button } from "./Button.js";
488
+ export type * from "./types.js";
489
+
490
+ \`\`\`
491
+
492
+ ### src/components/Button/Button.tests.tsx
493
+
494
+ \`\`\`tsx
495
+ import { describe, expect, it } from "vitest";
496
+ import { render, screen } from "@testing-library/react";
497
+ import Button from "./Button.js";
498
+
499
+ describe("Button", () => {
500
+ it("renders children", () => {
501
+ render(<Button>Test content</Button>);
502
+ expect(screen.getByText("Test content")).toBeInTheDocument();
503
+ });
504
+
505
+ it("applies custom className", () => {
506
+ render(<Button className="custom-class">Content</Button>);
507
+ const element = screen.getByText("Content");
508
+ expect(element.className).toContain("ds button");
509
+ expect(element.className).toContain("custom-class");
510
+ });
511
+
512
+ it("passes through additional props", () => {
513
+ render(<Button data-testid="test-component">Content</Button>);
514
+ expect(screen.getByTestId("test-component")).toBeInTheDocument();
515
+ });
516
+ });
517
+
518
+ \`\`\`
519
+
520
+ ### src/components/Button/Button.ssr.tests.tsx
521
+
522
+ \`\`\`tsx
523
+ import { describe, expect, it } from "vitest";
524
+ import { renderToString } from "react-dom/server";
525
+ import Button from "./Button.js";
526
+
527
+ describe("Button SSR", () => {
528
+ it("renders without hydration errors", () => {
529
+ const html = renderToString(<Button>Test content</Button>);
530
+ expect(html).toContain("Test content");
531
+ expect(html).toContain("ds button");
532
+ });
533
+ });
534
+
535
+ \`\`\`
536
+
537
+ ### src/components/Button/Button.stories.tsx
538
+
539
+ \`\`\`tsx
540
+ import type { Meta, StoryObj } from "@storybook/react-vite";
541
+ import Button from "./Button.js";
542
+
543
+ const meta: Meta<typeof Button> = {
544
+ title: "Components/Button",
545
+ component: Button,
546
+ tags: ["autodocs"],
547
+ };
548
+
549
+ export default meta;
550
+ type Story = StoryObj<typeof Button>;
551
+
552
+ export const Default: Story = {
553
+ args: {
554
+ children: "Hello, Button!",
555
+ },
556
+ };
557
+
558
+ export const WithCustomClass: Story = {
559
+ args: {
560
+ children: "Custom styled",
561
+ className: "custom-class",
562
+ },
563
+ };
564
+
565
+ \`\`\`
566
+
567
+ ### src/components/Button/styles.css
568
+
569
+ \`\`\`css
570
+ .ds.button {
571
+ /* Component styles */
572
+ }
573
+
574
+ \`\`\`
575
+
576
+ ### src/components/index.ts
577
+
578
+ \`\`\`ts
579
+ export * from "./Button/index.js";
580
+
581
+ \`\`\`
582
+
583
+ ---
584
+
585
+ Dry-run complete. No files were modified.
586
+
587
+ To execute:
588
+
589
+ \`\`\`sh
590
+ summon component/react --component-path src/components/Button --yes
591
+ \`\`\`
592
+ "
593
+ `;
594
+
595
+ exports[`golden: executeGenerator — components > svelte — dry-run plan 1`] = `
596
+ "
597
+ ├─ Info Generating Svelte component: Button
598
+ ├─ Create dir src/components/Button
599
+ ├─ Create file src/components/Button/Button.svelte
600
+ ├─ Create file src/components/Button/types.ts
601
+ ├─ Create file src/components/Button/index.ts
602
+ ├─ Create file src/components/Button/Button.svelte.test.ts
603
+ ├─ Create file src/components/Button/Button.ssr.test.ts
604
+ ├─ Create file src/components/Button/Button.stories.svelte
605
+ ├─ Create file src/components/Button/styles.css
606
+ ├─ Create file src/components/index.ts
607
+ └─ Info Created Button component at src/components/Button
608
+
609
+ Dry-run complete. No files were modified.
610
+ Tip: Use --show-files to see generated file contents
611
+ "
612
+ `;
613
+
614
+ exports[`golden: executeGenerator — components > svelte — json plan 1`] = `
615
+ "{
616
+ "generator": {
617
+ "name": "component/svelte",
618
+ "version": "0.1.0",
619
+ "description": "Generate a Svelte 5 component with TypeScript, tests, stories, and styles"
620
+ },
621
+ "answers": {
622
+ "componentPath": "src/components/Button",
623
+ "withStyles": true,
624
+ "withStories": true,
625
+ "withSsrTests": true,
626
+ "useTsStories": false
627
+ },
628
+ "plan": [
629
+ {
630
+ "action": "info",
631
+ "path": "Generating Svelte component: Button"
632
+ },
633
+ {
634
+ "action": "mkdir",
635
+ "path": "src/components/Button"
636
+ },
637
+ {
638
+ "action": "create",
639
+ "path": "src/components/Button/Button.svelte",
640
+ "lines": 20
641
+ },
642
+ {
643
+ "action": "create",
644
+ "path": "src/components/Button/types.ts",
645
+ "lines": 36
646
+ },
647
+ {
648
+ "action": "create",
649
+ "path": "src/components/Button/index.ts",
650
+ "lines": 3
651
+ },
652
+ {
653
+ "action": "create",
654
+ "path": "src/components/Button/Button.svelte.test.ts",
655
+ "lines": 78
656
+ },
657
+ {
658
+ "action": "create",
659
+ "path": "src/components/Button/Button.ssr.test.ts",
660
+ "lines": 84
661
+ },
662
+ {
663
+ "action": "create",
664
+ "path": "src/components/Button/Button.stories.svelte",
665
+ "lines": 26
666
+ },
667
+ {
668
+ "action": "create",
669
+ "path": "src/components/Button/styles.css",
670
+ "lines": 4
671
+ },
672
+ {
673
+ "action": "create",
674
+ "path": "src/components/index.ts",
675
+ "lines": 2
676
+ },
677
+ {
678
+ "action": "info",
679
+ "path": "Created Button component at src/components/Button"
680
+ }
681
+ ],
682
+ "files": {
683
+ "src/components/Button/Button.svelte": "<script lang=\\"ts\\">\\n\\timport type { ButtonProps } from \\"./types.js\\";\\n\\timport \\"./styles.css\\";\\n\\n\\tconst componentCssClassName = \\"ds button\\";\\n\\n\\tlet {\\n\\t\\tclass: className,\\n\\t\\tchildren,\\n\\t\\t...rest\\n\\t}: ButtonProps = $props();\\n</script>\\n\\n<div\\n\\tclass={[componentCssClassName, className]}\\n\\t{...rest}\\n>\\n\\t{@render children?.()}\\n</div>\\n",
684
+ "src/components/Button/types.ts": "import type { Snippet } from \\"svelte\\";\\nimport type { SvelteHTMLElements } from \\"svelte/elements\\";\\n\\n/*\\n\\tFor components that render specific HTML elements with extended prop sets,\\n\\tmake sure to change \`\\"div\\"\` to the appropriate HTML element type.\\n*/\\ntype BaseProps = SvelteHTMLElements[\\"div\\"];\\n\\nexport interface ButtonProps extends BaseProps {\\n\\t/** Content to render inside the component */\\n\\tchildren?: Snippet;\\n\\t// Define additional props specific to Button here\\n}\\n\\n/*\\n\\t...or consider using appropriate interfaces directly exported from \`svelte/elements\`.\\n\\n\\tFor example:\\n\\t- \`HTMLButtonAttributes\` (provides \`disabled\`, \`type\`, etc.)\\n\\t- \`HTMLAnchorAttributes\` (provides \`href\`, \`target\`, etc.)\\n\\t- \`HTMLInputAttributes\` (provides \`type\`, \`checked\`, \`value\`, etc.)\\n\\t- \`HTMLFormAttributes\` (provides \`action\`, \`method\`, etc.)\\n\\n\\t\`\`\`typescript\\n\\timport type { HTMLButtonAttributes } from \\"svelte/elements\\";\\n\\n\\texport interface MyButtonProps extends HTMLButtonAttributes {\\n\\t\\t// Your custom props here\\n\\t}\\n\\t\`\`\`\\n\\n\\tSee Svelte documentation on typing wrapper components:\\n\\thttps://svelte.dev/docs/svelte/typescript#Typing-wrapper-components\\n*/\\n",
685
+ "src/components/Button/index.ts": "export { default as Button } from \\"./Button.svelte\\";\\nexport type { ButtonProps } from \\"./types.js\\";\\n",
686
+ "src/components/Button/Button.svelte.test.ts": "import type { Locator } from \\"@vitest/browser/context\\";\\nimport { createRawSnippet } from \\"svelte\\";\\nimport type { ComponentProps } from \\"svelte\\";\\nimport { describe, expect, it } from \\"vitest\\";\\nimport { render } from \\"vitest-browser-svelte\\";\\nimport type { RenderResult } from \\"vitest-browser-svelte\\";\\nimport Component from \\"./Button.svelte\\";\\n\\ndescribe(\\"Button component\\", () => {\\n\\tconst baseProps = {\\n\\t\\tchildren: createRawSnippet(() => ({\\n\\t\\t\\trender: () => \`<span>Button</span>\`,\\n\\t\\t})),\\n\\t} satisfies ComponentProps<typeof Component>;\\n\\n\\tit(\\"renders\\", async () => {\\n\\t\\tconst page = render(Component, { ...baseProps });\\n\\t\\tawait expect.element(componentLocator(page)).toBeInTheDocument();\\n\\t\\tawait expect.element(page.getByText(\\"Button\\")).toBeVisible();\\n\\t});\\n\\n\\tdescribe(\\"attributes\\", () => {\\n\\t\\tit.each([\\n\\t\\t\\t[\\"id\\", \\"test-id\\"],\\n\\t\\t\\t[\\"aria-label\\", \\"test-aria-label\\"],\\n\\t\\t])(\\"applies %s\\", async (attribute, expected) => {\\n\\t\\t\\tconst page = render(Component, { ...baseProps, [attribute]: expected });\\n\\t\\t\\tawait expect\\n\\t\\t\\t\\t.element(componentLocator(page))\\n\\t\\t\\t\\t.toHaveAttribute(attribute, expected);\\n\\t\\t});\\n\\n\\t\\tit(\\"applies classes\\", async () => {\\n\\t\\t\\tconst page = render(Component, { ...baseProps, class: \\"test-class\\" });\\n\\t\\t\\tawait expect.element(componentLocator(page)).toHaveClass(\\"test-class\\");\\n\\t\\t\\tawait expect.element(componentLocator(page)).toHaveClass(\\"ds\\");\\n\\t\\t\\tawait expect\\n\\t\\t\\t\\t.element(componentLocator(page))\\n\\t\\t\\t\\t.toHaveClass(\\"button\\");\\n\\t\\t});\\n\\n\\t\\tit(\\"applies style\\", async () => {\\n\\t\\t\\tconst page = render(Component, {\\n\\t\\t\\t\\t...baseProps,\\n\\t\\t\\t\\tstyle: \\"color: orange;\\",\\n\\t\\t\\t});\\n\\t\\t\\tawait expect\\n\\t\\t\\t\\t.element(componentLocator(page))\\n\\t\\t\\t\\t.toHaveStyle({ color: \\"orange\\" });\\n\\t\\t});\\n\\t});\\n});\\n\\n// Note: Prefer role/semantics-oriented ways of selecting elements (e.g., by role, label, etc.) not only for component roots but for all elements to enhance accessibility and maintainability.\\n// To select the component's root element, use one of the available [Locators](https://vitest.dev/guide/browser/locators.html).\\nfunction componentLocator(page: RenderResult<typeof Component>): Locator {\\n\\t// 1. SEMANTIC ROLES (PREFERRED) - These the most closely resemble how assistive technologies interact with your component\\n\\t// return page.getByRole(\\"link\\", { name: \\"Learn more\\" });\\n\\n\\t// 2. LABELS - To test the association between labels and form controls\\n\\t// return page.getByLabel(\\"Email address\\");\\n\\n\\t// 3. PLACEHOLDER TEXT - Useful for input fields with placeholders\\n\\t// return page.getByPlaceholder(\\"Enter your email\\");\\n\\n\\t// 4. TEXT CONTENT - Good for testing the presence of unique texts and selecting non-interactive elements\\n\\t// return page.getByText(\\"Button\\");\\n\\n\\t// 5. ALT TEXT - For images and visual elements\\n\\t// return page.getByAltText(\\"Company logo\\");\\n\\n\\t// 6. TITLE ATTRIBUTE - For elements with title tooltips\\n\\t// return page.getByTitle(\\"Close dialog\\");\\n\\n\\t// 7. TEST IDS (LAST RESORT) - When semantic options aren't available\\n\\t// return page.getByTestId(\\"button\\");\\n}\\n",
687
+ "src/components/Button/Button.ssr.test.ts": "import { render } from \\"@canonical/svelte-ssr-test\\";\\nimport type { RenderResult } from \\"@canonical/svelte-ssr-test\\";\\nimport { createRawSnippet } from \\"svelte\\";\\nimport type { ComponentProps } from \\"svelte\\";\\nimport { describe, expect, it } from \\"vitest\\";\\nimport Component from \\"./Button.svelte\\";\\n\\ndescribe(\\"Button SSR\\", () => {\\n\\tconst baseProps = {\\n\\t\\tchildren: createRawSnippet(() => ({\\n\\t\\t\\trender: () => \`<span>Button</span>\`,\\n\\t\\t})),\\n\\t} satisfies ComponentProps<typeof Component>;\\n\\n\\tdescribe(\\"basics\\", () => {\\n\\t\\tit(\\"doesn't throw\\", () => {\\n\\t\\t\\texpect(() => {\\n\\t\\t\\t\\trender(Component, { props: { ...baseProps } });\\n\\t\\t\\t}).not.toThrow();\\n\\t\\t});\\n\\n\\t\\tit(\\"renders\\", () => {\\n\\t\\t\\tconst page = render(Component, { props: { ...baseProps } });\\n\\t\\t\\texpect(componentLocator(page)).toBeInstanceOf(page.window.HTMLDivElement);\\n\\t\\t});\\n\\t});\\n\\n\\tdescribe(\\"attributes\\", () => {\\n\\t\\tit.each([\\n\\t\\t\\t[\\"id\\", \\"test-id\\"],\\n\\t\\t\\t[\\"aria-label\\", \\"test-aria-label\\"],\\n\\t\\t])(\\"applies %s\\", (attribute, expected) => {\\n\\t\\t\\tconst page = render(Component, {\\n\\t\\t\\t\\tprops: { ...baseProps, [attribute]: expected },\\n\\t\\t\\t});\\n\\t\\t\\texpect(componentLocator(page).getAttribute(attribute)).toBe(expected);\\n\\t\\t});\\n\\n\\t\\tit(\\"applies classes\\", () => {\\n\\t\\t\\tconst page = render(Component, {\\n\\t\\t\\t\\tprops: { class: \\"test-class\\", ...baseProps },\\n\\t\\t\\t});\\n\\t\\t\\texpect(componentLocator(page).classList).toContain(\\"test-class\\");\\n\\t\\t\\texpect(componentLocator(page).classList).toContain(\\"ds\\");\\n\\t\\t\\texpect(componentLocator(page).classList).toContain(\\"button\\");\\n\\t\\t});\\n\\n\\t\\tit(\\"applies style\\", () => {\\n\\t\\t\\tconst page = render(Component, {\\n\\t\\t\\t\\tprops: { style: \\"color: orange;\\", ...baseProps },\\n\\t\\t\\t});\\n\\t\\t\\texpect(componentLocator(page).style.color).toBe(\\"orange\\");\\n\\t\\t});\\n\\t});\\n});\\n\\n// Note: Prefer role/semantics-oriented ways of selecting elements (e.g., by role, label, etc.) not only for component roots but for all elements to enhance accessibility and maintainability.\\n// To select the component's root element, use one of the available [Queries](https://testing-library.com/docs/queries/about/#priority).\\nfunction componentLocator(page: RenderResult): HTMLElement {\\n\\t// 1. SEMANTIC ROLES (PREFERRED) - These the most closely resemble how assistive technologies interact with your component\\n\\t// return page.getByRole(\\"link\\", { name: \\"Learn more\\" });\\n\\n\\t// 2. LABELS - To test the association between labels and form controls\\n\\t// return page.getByLabelText(\\"Email address\\");\\n\\n\\t// 3. PLACEHOLDER TEXT - Useful for input fields with placeholders\\n\\t// return page.getByPlaceholderText(\\"Enter your email\\");\\n\\n\\t// 4. TEXT CONTENT - Good for testing the presence of unique texts and selecting non-interactive elements\\n\\t// return page.getByText(\\"Button\\");\\n\\n\\t// 5. DISPLAY VALUE - For selecting form elements by their current value\\n\\t// return page.getByDisplayValue(\\"test value\\");\\n\\n\\t// 6. ALT TEXT - For images and visual elements\\n\\t// return page.getByAltText(\\"Company logo\\");\\n\\n\\t// 7. TITLE ATTRIBUTE - For elements with title tooltips\\n\\t// return page.getByTitle(\\"Close dialog\\");\\n\\n\\t// 8. TEST IDS (LAST RESORT) - When semantic options aren't available\\n\\t// return page.getByTestId(\\"button\\");\\n}\\n",
688
+ "src/components/Button/Button.stories.svelte": "<script lang=\\"ts\\" module>\\n\\timport { defineMeta } from \\"@storybook/addon-svelte-csf\\";\\n\\timport Button from \\"./Button.svelte\\";\\n\\n\\tconst { Story } = defineMeta({\\n\\t\\ttitle: \\"Components/Button\\",\\n\\t\\tcomponent: Button,\\n\\t\\ttags: [\\"autodocs\\"],\\n\\t});\\n</script>\\n\\n<Story name=\\"Default\\">\\n\\tHello, Button!\\n</Story>\\n\\n<Story\\n name=\\"WithCustomClass\\"\\n args={{\\n class: \\"custom-class\\",\\n id: \\"custom-id\\",\\n style: \\"background-color: tomato;\\",\\n }}\\n>\\n\\tCustom styled\\n</Story>\\n",
689
+ "src/components/Button/styles.css": ".ds.button {\\n\\t/* Component styles */\\n}\\n",
690
+ "src/components/index.ts": "export * from \\"./Button/index.js\\";\\n"
691
+ },
692
+ "executeCommand": "summon component/svelte --component-path src/components/Button --yes"
693
+ }
694
+ "
695
+ `;
696
+
697
+ exports[`golden: executeGenerator — components > svelte — llm plan 1`] = `
698
+ "# component/svelte
699
+
700
+ > Generate a Svelte 5 component with TypeScript, tests, stories, and styles
701
+ > v0.1.0
702
+
703
+ ## Answers
704
+
705
+ | Option | Value |
706
+ |--------|-------|
707
+ | componentPath | src/components/Button |
708
+ | withStyles | true |
709
+ | withStories | true |
710
+ | withSsrTests | true |
711
+ | useTsStories | false |
712
+
713
+ ## Plan
714
+
715
+ | Action | Path | Lines |
716
+ |--------|------|-------|
717
+ | info | Generating Svelte component: Button | |
718
+ | mkdir | src/components/Button | |
719
+ | create | src/components/Button/Button.svelte | 20 |
720
+ | create | src/components/Button/types.ts | 36 |
721
+ | create | src/components/Button/index.ts | 3 |
722
+ | create | src/components/Button/Button.svelte.test.ts | 78 |
723
+ | create | src/components/Button/Button.ssr.test.ts | 84 |
724
+ | create | src/components/Button/Button.stories.svelte | 26 |
725
+ | create | src/components/Button/styles.css | 4 |
726
+ | create | src/components/index.ts | 2 |
727
+ | info | Created Button component at src/components/Button | |
728
+
729
+ ## Files
730
+
731
+ ### src/components/Button/Button.svelte
732
+
733
+ \`\`\`svelte
734
+ <script lang="ts">
735
+ import type { ButtonProps } from "./types.js";
736
+ import "./styles.css";
737
+
738
+ const componentCssClassName = "ds button";
739
+
740
+ let {
741
+ class: className,
742
+ children,
743
+ ...rest
744
+ }: ButtonProps = $props();
745
+ </script>
746
+
747
+ <div
748
+ class={[componentCssClassName, className]}
749
+ {...rest}
750
+ >
751
+ {@render children?.()}
752
+ </div>
753
+
754
+ \`\`\`
755
+
756
+ ### src/components/Button/types.ts
757
+
758
+ \`\`\`ts
759
+ import type { Snippet } from "svelte";
760
+ import type { SvelteHTMLElements } from "svelte/elements";
761
+
762
+ /*
763
+ For components that render specific HTML elements with extended prop sets,
764
+ make sure to change \`"div"\` to the appropriate HTML element type.
765
+ */
766
+ type BaseProps = SvelteHTMLElements["div"];
767
+
768
+ export interface ButtonProps extends BaseProps {
769
+ /** Content to render inside the component */
770
+ children?: Snippet;
771
+ // Define additional props specific to Button here
772
+ }
773
+
774
+ /*
775
+ ...or consider using appropriate interfaces directly exported from \`svelte/elements\`.
776
+
777
+ For example:
778
+ - \`HTMLButtonAttributes\` (provides \`disabled\`, \`type\`, etc.)
779
+ - \`HTMLAnchorAttributes\` (provides \`href\`, \`target\`, etc.)
780
+ - \`HTMLInputAttributes\` (provides \`type\`, \`checked\`, \`value\`, etc.)
781
+ - \`HTMLFormAttributes\` (provides \`action\`, \`method\`, etc.)
782
+
783
+ \`\`\`typescript
784
+ import type { HTMLButtonAttributes } from "svelte/elements";
785
+
786
+ export interface MyButtonProps extends HTMLButtonAttributes {
787
+ // Your custom props here
788
+ }
789
+ \`\`\`
790
+
791
+ See Svelte documentation on typing wrapper components:
792
+ https://svelte.dev/docs/svelte/typescript#Typing-wrapper-components
793
+ */
794
+
795
+ \`\`\`
796
+
797
+ ### src/components/Button/index.ts
798
+
799
+ \`\`\`ts
800
+ export { default as Button } from "./Button.svelte";
801
+ export type { ButtonProps } from "./types.js";
802
+
803
+ \`\`\`
804
+
805
+ ### src/components/Button/Button.svelte.test.ts
806
+
807
+ \`\`\`ts
808
+ import type { Locator } from "@vitest/browser/context";
809
+ import { createRawSnippet } from "svelte";
810
+ import type { ComponentProps } from "svelte";
811
+ import { describe, expect, it } from "vitest";
812
+ import { render } from "vitest-browser-svelte";
813
+ import type { RenderResult } from "vitest-browser-svelte";
814
+ import Component from "./Button.svelte";
815
+
816
+ describe("Button component", () => {
817
+ const baseProps = {
818
+ children: createRawSnippet(() => ({
819
+ render: () => \`<span>Button</span>\`,
820
+ })),
821
+ } satisfies ComponentProps<typeof Component>;
822
+
823
+ it("renders", async () => {
824
+ const page = render(Component, { ...baseProps });
825
+ await expect.element(componentLocator(page)).toBeInTheDocument();
826
+ await expect.element(page.getByText("Button")).toBeVisible();
827
+ });
828
+
829
+ describe("attributes", () => {
830
+ it.each([
831
+ ["id", "test-id"],
832
+ ["aria-label", "test-aria-label"],
833
+ ])("applies %s", async (attribute, expected) => {
834
+ const page = render(Component, { ...baseProps, [attribute]: expected });
835
+ await expect
836
+ .element(componentLocator(page))
837
+ .toHaveAttribute(attribute, expected);
838
+ });
839
+
840
+ it("applies classes", async () => {
841
+ const page = render(Component, { ...baseProps, class: "test-class" });
842
+ await expect.element(componentLocator(page)).toHaveClass("test-class");
843
+ await expect.element(componentLocator(page)).toHaveClass("ds");
844
+ await expect
845
+ .element(componentLocator(page))
846
+ .toHaveClass("button");
847
+ });
848
+
849
+ it("applies style", async () => {
850
+ const page = render(Component, {
851
+ ...baseProps,
852
+ style: "color: orange;",
853
+ });
854
+ await expect
855
+ .element(componentLocator(page))
856
+ .toHaveStyle({ color: "orange" });
857
+ });
858
+ });
859
+ });
860
+
861
+ // Note: Prefer role/semantics-oriented ways of selecting elements (e.g., by role, label, etc.) not only for component roots but for all elements to enhance accessibility and maintainability.
862
+ // To select the component's root element, use one of the available [Locators](https://vitest.dev/guide/browser/locators.html).
863
+ function componentLocator(page: RenderResult<typeof Component>): Locator {
864
+ // 1. SEMANTIC ROLES (PREFERRED) - These the most closely resemble how assistive technologies interact with your component
865
+ // return page.getByRole("link", { name: "Learn more" });
866
+
867
+ // 2. LABELS - To test the association between labels and form controls
868
+ // return page.getByLabel("Email address");
869
+
870
+ // 3. PLACEHOLDER TEXT - Useful for input fields with placeholders
871
+ // return page.getByPlaceholder("Enter your email");
872
+
873
+ // 4. TEXT CONTENT - Good for testing the presence of unique texts and selecting non-interactive elements
874
+ // return page.getByText("Button");
875
+
876
+ // 5. ALT TEXT - For images and visual elements
877
+ // return page.getByAltText("Company logo");
878
+
879
+ // 6. TITLE ATTRIBUTE - For elements with title tooltips
880
+ // return page.getByTitle("Close dialog");
881
+
882
+ // 7. TEST IDS (LAST RESORT) - When semantic options aren't available
883
+ // return page.getByTestId("button");
884
+ }
885
+
886
+ \`\`\`
887
+
888
+ ### src/components/Button/Button.ssr.test.ts
889
+
890
+ \`\`\`ts
891
+ import { render } from "@canonical/svelte-ssr-test";
892
+ import type { RenderResult } from "@canonical/svelte-ssr-test";
893
+ import { createRawSnippet } from "svelte";
894
+ import type { ComponentProps } from "svelte";
895
+ import { describe, expect, it } from "vitest";
896
+ import Component from "./Button.svelte";
897
+
898
+ describe("Button SSR", () => {
899
+ const baseProps = {
900
+ children: createRawSnippet(() => ({
901
+ render: () => \`<span>Button</span>\`,
902
+ })),
903
+ } satisfies ComponentProps<typeof Component>;
904
+
905
+ describe("basics", () => {
906
+ it("doesn't throw", () => {
907
+ expect(() => {
908
+ render(Component, { props: { ...baseProps } });
909
+ }).not.toThrow();
910
+ });
911
+
912
+ it("renders", () => {
913
+ const page = render(Component, { props: { ...baseProps } });
914
+ expect(componentLocator(page)).toBeInstanceOf(page.window.HTMLDivElement);
915
+ });
916
+ });
917
+
918
+ describe("attributes", () => {
919
+ it.each([
920
+ ["id", "test-id"],
921
+ ["aria-label", "test-aria-label"],
922
+ ])("applies %s", (attribute, expected) => {
923
+ const page = render(Component, {
924
+ props: { ...baseProps, [attribute]: expected },
925
+ });
926
+ expect(componentLocator(page).getAttribute(attribute)).toBe(expected);
927
+ });
928
+
929
+ it("applies classes", () => {
930
+ const page = render(Component, {
931
+ props: { class: "test-class", ...baseProps },
932
+ });
933
+ expect(componentLocator(page).classList).toContain("test-class");
934
+ expect(componentLocator(page).classList).toContain("ds");
935
+ expect(componentLocator(page).classList).toContain("button");
936
+ });
937
+
938
+ it("applies style", () => {
939
+ const page = render(Component, {
940
+ props: { style: "color: orange;", ...baseProps },
941
+ });
942
+ expect(componentLocator(page).style.color).toBe("orange");
943
+ });
944
+ });
945
+ });
946
+
947
+ // Note: Prefer role/semantics-oriented ways of selecting elements (e.g., by role, label, etc.) not only for component roots but for all elements to enhance accessibility and maintainability.
948
+ // To select the component's root element, use one of the available [Queries](https://testing-library.com/docs/queries/about/#priority).
949
+ function componentLocator(page: RenderResult): HTMLElement {
950
+ // 1. SEMANTIC ROLES (PREFERRED) - These the most closely resemble how assistive technologies interact with your component
951
+ // return page.getByRole("link", { name: "Learn more" });
952
+
953
+ // 2. LABELS - To test the association between labels and form controls
954
+ // return page.getByLabelText("Email address");
955
+
956
+ // 3. PLACEHOLDER TEXT - Useful for input fields with placeholders
957
+ // return page.getByPlaceholderText("Enter your email");
958
+
959
+ // 4. TEXT CONTENT - Good for testing the presence of unique texts and selecting non-interactive elements
960
+ // return page.getByText("Button");
961
+
962
+ // 5. DISPLAY VALUE - For selecting form elements by their current value
963
+ // return page.getByDisplayValue("test value");
964
+
965
+ // 6. ALT TEXT - For images and visual elements
966
+ // return page.getByAltText("Company logo");
967
+
968
+ // 7. TITLE ATTRIBUTE - For elements with title tooltips
969
+ // return page.getByTitle("Close dialog");
970
+
971
+ // 8. TEST IDS (LAST RESORT) - When semantic options aren't available
972
+ // return page.getByTestId("button");
973
+ }
974
+
975
+ \`\`\`
976
+
977
+ ### src/components/Button/Button.stories.svelte
978
+
979
+ \`\`\`svelte
980
+ <script lang="ts" module>
981
+ import { defineMeta } from "@storybook/addon-svelte-csf";
982
+ import Button from "./Button.svelte";
983
+
984
+ const { Story } = defineMeta({
985
+ title: "Components/Button",
986
+ component: Button,
987
+ tags: ["autodocs"],
988
+ });
989
+ </script>
990
+
991
+ <Story name="Default">
992
+ Hello, Button!
993
+ </Story>
994
+
995
+ <Story
996
+ name="WithCustomClass"
997
+ args={{
998
+ class: "custom-class",
999
+ id: "custom-id",
1000
+ style: "background-color: tomato;",
1001
+ }}
1002
+ >
1003
+ Custom styled
1004
+ </Story>
1005
+
1006
+ \`\`\`
1007
+
1008
+ ### src/components/Button/styles.css
1009
+
1010
+ \`\`\`css
1011
+ .ds.button {
1012
+ /* Component styles */
1013
+ }
1014
+
1015
+ \`\`\`
1016
+
1017
+ ### src/components/index.ts
1018
+
1019
+ \`\`\`ts
1020
+ export * from "./Button/index.js";
1021
+
1022
+ \`\`\`
1023
+
1024
+ ---
1025
+
1026
+ Dry-run complete. No files were modified.
1027
+
1028
+ To execute:
1029
+
1030
+ \`\`\`sh
1031
+ summon component/svelte --component-path src/components/Button --yes
1032
+ \`\`\`
1033
+ "
1034
+ `;
1035
+
1036
+ exports[`golden: executeGenerator — package > library — dry-run plan 1`] = `
1037
+ "
1038
+ ├─ Info Creating package: @canonical/example
1039
+ ├─ Info Type: library
1040
+ ├─ Create dir example
1041
+ ├─ Create dir example/src
1042
+ ├─ Create file example/package.json
1043
+ ├─ Create file example/tsconfig.json
1044
+ ├─ Create file example/biome.json
1045
+ ├─ Create file example/src/index.ts
1046
+ ├─ Create file example/README.md
1047
+ ├─ Info Package created at ./example
1048
+ ├─ Info Running bun install...
1049
+ ├─ Execute bun install
1050
+ ├─ Info Dependencies installed successfully
1051
+ ├─ Info
1052
+ ├─ Info Next steps:
1053
+ ├─ Info cd example
1054
+ ├─ Info bun run check
1055
+ └─ Info
1056
+
1057
+ Dry-run complete. No files were modified.
1058
+ Tip: Use --show-files to see generated file contents
1059
+ "
1060
+ `;
1061
+
1062
+ exports[`golden: executeGenerator — package > library — json plan 1`] = `
1063
+ "{
1064
+ "generator": {
1065
+ "name": "package",
1066
+ "version": "0.1.0",
1067
+ "description": "Generate a new npm package with proper configuration for the pragma monorepo"
1068
+ },
1069
+ "answers": {
1070
+ "name": "@canonical/example",
1071
+ "type": "library",
1072
+ "description": "",
1073
+ "withReact": false,
1074
+ "withStorybook": false,
1075
+ "withCli": false,
1076
+ "withPrTemplate": false,
1077
+ "runInstall": true
1078
+ },
1079
+ "plan": [
1080
+ {
1081
+ "action": "info",
1082
+ "path": "Creating package: @canonical/example"
1083
+ },
1084
+ {
1085
+ "action": "info",
1086
+ "path": "Type: library"
1087
+ },
1088
+ {
1089
+ "action": "mkdir",
1090
+ "path": "example"
1091
+ },
1092
+ {
1093
+ "action": "mkdir",
1094
+ "path": "example/src"
1095
+ },
1096
+ {
1097
+ "action": "create",
1098
+ "path": "example/package.json",
1099
+ "lines": 1
1100
+ },
1101
+ {
1102
+ "action": "create",
1103
+ "path": "example/tsconfig.json",
1104
+ "lines": 1
1105
+ },
1106
+ {
1107
+ "action": "create",
1108
+ "path": "example/biome.json",
1109
+ "lines": 1
1110
+ },
1111
+ {
1112
+ "action": "create",
1113
+ "path": "example/src/index.ts",
1114
+ "lines": 1
1115
+ },
1116
+ {
1117
+ "action": "create",
1118
+ "path": "example/README.md",
1119
+ "lines": 1
1120
+ },
1121
+ {
1122
+ "action": "info",
1123
+ "path": "Package created at ./example"
1124
+ },
1125
+ {
1126
+ "action": "info",
1127
+ "path": "Running bun install..."
1128
+ },
1129
+ {
1130
+ "action": "exec",
1131
+ "path": "bun install"
1132
+ },
1133
+ {
1134
+ "action": "info",
1135
+ "path": "Dependencies installed successfully"
1136
+ },
1137
+ {
1138
+ "action": "info",
1139
+ "path": ""
1140
+ },
1141
+ {
1142
+ "action": "info",
1143
+ "path": "Next steps:"
1144
+ },
1145
+ {
1146
+ "action": "info",
1147
+ "path": " cd example"
1148
+ },
1149
+ {
1150
+ "action": "info",
1151
+ "path": " bun run check"
1152
+ },
1153
+ {
1154
+ "action": "info",
1155
+ "path": ""
1156
+ }
1157
+ ],
1158
+ "files": {
1159
+ "example/package.json": "[mock content of <repo>/packages/summon/package/src/templates/package.json.ejs]",
1160
+ "example/tsconfig.json": "[mock content of <repo>/packages/summon/package/src/templates/tsconfig.json.ejs]",
1161
+ "example/biome.json": "[mock content of <repo>/packages/summon/package/src/templates/biome.json.ejs]",
1162
+ "example/src/index.ts": "[mock content of <repo>/packages/summon/package/src/templates/index.ts.ejs]",
1163
+ "example/README.md": "[mock content of <repo>/packages/summon/package/src/templates/README.md.ejs]"
1164
+ },
1165
+ "executeCommand": "summon package --name @canonical/example --type library --description --yes"
1166
+ }
1167
+ "
1168
+ `;
1169
+
1170
+ exports[`golden: runSetupTask — setups > completions (bash) — dry-run 1`] = `
1171
+ "Dry run — the following operations would be performed:
1172
+
1173
+ ├─ Info Setting up completions for bash...
1174
+ ├─ Create dir <home>/.local/share/bash-completion/completions
1175
+ ├─ Create file <home>/.local/share/bash-completion/completions/pragma
1176
+ ├─ Info ✓ Completions installed for bash.
1177
+ └─ Info Run \`source ~/.bashrc\` or start a new shell to activate.
1178
+
1179
+ No changes were made."
1180
+ `;
1181
+
1182
+ exports[`golden: runSetupTask — setups > lsp — dry-run 1`] = `
1183
+ "Dry run — the following operations would be performed:
1184
+
1185
+ ├─ Info Ensuring the Terrazzo LSP VS Code extension is installed...
1186
+ ├─ Execute bunx @canonical/terrazzo-lsp-extension
1187
+ └─ Info ✓ Terrazzo LSP extension is installed (up to date).
1188
+
1189
+ No changes were made."
1190
+ `;
1191
+
1192
+ exports[`golden: runSetupTask — setups > lsp — dry-run json 1`] = `
1193
+ "[
1194
+ {
1195
+ "action": "Log",
1196
+ "message": "Ensuring the Terrazzo LSP VS Code extension is installed..."
1197
+ },
1198
+ {
1199
+ "action": "Exec"
1200
+ },
1201
+ {
1202
+ "action": "Log",
1203
+ "message": "✓ Terrazzo LSP extension is installed (up to date)."
1204
+ }
1205
+ ]
1206
+ "
1207
+ `;