@canonical/pragma-cli 0.30.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 (254) hide show
  1. package/README.md +64 -35
  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/parseConfigValues.ts +2 -0
  12. package/src/config/readConfigLayers.test.ts +2 -0
  13. package/src/config/readConfigLayers.ts +3 -0
  14. package/src/config/types.ts +7 -0
  15. package/src/constants.ts +3 -1
  16. package/src/domains/block/commands/sample.ts +2 -1
  17. package/src/domains/block/formatters/list.test.ts +3 -3
  18. package/src/domains/block/formatters/lookup.test.ts +5 -5
  19. package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
  20. package/src/domains/block/stories.ts +9 -3
  21. package/src/domains/config/commands/channel.test.ts +16 -0
  22. package/src/domains/config/commands/channel.ts +14 -1
  23. package/src/domains/config/commands/framework.test.ts +14 -0
  24. package/src/domains/config/commands/framework.ts +13 -1
  25. package/src/domains/config/commands/tier.test.ts +7 -0
  26. package/src/domains/config/commands/tier.ts +14 -1
  27. package/src/domains/config/operations/validateTier.test.ts +9 -0
  28. package/src/domains/config/operations/validateTier.ts +10 -2
  29. package/src/domains/create/generatorPack/bundled.ts +49 -0
  30. package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
  31. package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
  32. package/src/domains/create/index.ts +13 -7
  33. package/src/domains/create/mcp/specs.ts +6 -161
  34. package/src/domains/create/renderGeneratorUi.test.ts +103 -0
  35. package/src/domains/create/renderGeneratorUi.ts +65 -0
  36. package/src/domains/doctor/formatters/doctor.test.ts +50 -2
  37. package/src/domains/doctor/formatters/doctor.ts +90 -38
  38. package/src/domains/doctor/operations/checks/checkMcpCommands.ts +9 -6
  39. package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -12
  40. package/src/domains/doctor/operations/checks.test.ts +6 -4
  41. package/src/domains/doctor/operations/types.ts +23 -1
  42. package/src/domains/graph/commands/inspect.test.ts +2 -2
  43. package/src/domains/graph/commands/query.test.ts +5 -3
  44. package/src/domains/graph/formatters/inspect.ts +7 -7
  45. package/src/domains/graph/formatters/query.test.ts +87 -0
  46. package/src/domains/graph/formatters/query.ts +42 -5
  47. package/src/domains/graph/operations/inspectUri.test.ts +5 -5
  48. package/src/domains/graphql/commands/serve.test.ts +12 -6
  49. package/src/domains/graphql/commands/serve.ts +16 -6
  50. package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
  51. package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
  52. package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
  53. package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
  54. package/src/domains/graphql/operations/compileSchema.test.ts +2 -2
  55. package/src/domains/graphql/operations/createSchemaServer.test.ts +2 -2
  56. package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
  57. package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
  58. package/src/domains/graphql/operations/index.ts +5 -0
  59. package/src/domains/info/formatters/upgrade.test.ts +7 -4
  60. package/src/domains/info/formatters/upgrade.ts +3 -1
  61. package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
  62. package/src/domains/info/operations/collectEntityCounts.ts +123 -0
  63. package/src/domains/info/operations/index.ts +1 -0
  64. package/src/domains/info/types.ts +13 -0
  65. package/src/domains/llm/capabilities.test.ts +23 -3
  66. package/src/domains/llm/commands/capabilities.ts +12 -3
  67. package/src/domains/llm/commands/llm.ts +5 -1
  68. package/src/domains/llm/data/commandReference.ts +20 -11
  69. package/src/domains/llm/data/decisionTrees.ts +10 -5
  70. package/src/domains/llm/data/toolCatalog.ts +49 -18
  71. package/src/domains/llm/formatters/orientation.ts +5 -1
  72. package/src/domains/llm/llmCommand.test.ts +11 -3
  73. package/src/domains/llm/mcp/specs.ts +3 -4
  74. package/src/domains/llm/operations/collectContext.test.ts +64 -0
  75. package/src/domains/llm/operations/collectContext.ts +7 -17
  76. package/src/domains/modifier/commands/sample.ts +2 -1
  77. package/src/domains/modifier/operations/lookup.ts +5 -3
  78. package/src/domains/modifier/stories.ts +9 -0
  79. package/src/domains/ontology/commands/list.test.ts +5 -3
  80. package/src/domains/ontology/commands/show.test.ts +85 -9
  81. package/src/domains/ontology/formatters/index.ts +1 -0
  82. package/src/domains/ontology/formatters/list.ts +37 -12
  83. package/src/domains/ontology/formatters/show.ts +358 -67
  84. package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
  85. package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
  86. package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
  87. package/src/domains/ontology/helpers/index.ts +14 -0
  88. package/src/domains/ontology/helpers/queryClasses.ts +32 -16
  89. package/src/domains/ontology/helpers/queryProperties.ts +39 -6
  90. package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
  91. package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
  92. package/src/domains/ontology/operations/listOntologies.ts +126 -30
  93. package/src/domains/ontology/operations/showOntology.test.ts +131 -12
  94. package/src/domains/ontology/operations/showOntology.ts +289 -7
  95. package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
  96. package/src/domains/ontology/stories.ts +53 -8
  97. package/src/domains/refs/operations/updateRefs.ts +0 -1
  98. package/src/domains/setup/commands/index.ts +1 -0
  99. package/src/domains/setup/commands/setup.ts +70 -0
  100. package/src/domains/setup/index.ts +7 -3
  101. package/src/domains/setup/operations/setupLsp.test.ts +3 -2
  102. package/src/domains/setup/operations/setupLsp.ts +7 -2
  103. package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
  104. package/src/domains/shared/bootStore.test.ts +82 -0
  105. package/src/domains/shared/bootStore.ts +56 -20
  106. package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
  107. package/src/domains/shared/extractLocalName.test.ts +3 -3
  108. package/src/domains/shared/formatters.ts +23 -2
  109. package/src/domains/shared/listTiers.ts +40 -0
  110. package/src/domains/shared/loaders/bundledLoader.ts +48 -0
  111. package/src/domains/shared/loaders/gitLoader.ts +1 -2
  112. package/src/domains/shared/loaders/localLoader.ts +2 -18
  113. package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
  114. package/src/domains/shared/loaders/readPackageDir.ts +46 -8
  115. package/src/domains/shared/lookupMany.test.ts +15 -0
  116. package/src/domains/shared/lookupMany.ts +22 -6
  117. package/src/domains/shared/namespaces.ts +13 -5
  118. package/src/domains/shared/parseSampleCount.test.ts +47 -0
  119. package/src/domains/shared/parseSampleCount.ts +45 -0
  120. package/src/domains/shared/prefixes.test.ts +211 -0
  121. package/src/domains/shared/prefixes.ts +182 -15
  122. package/src/domains/shared/renderers.ts +5 -5
  123. package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
  124. package/src/domains/shared/resolveLookupExitCode.ts +42 -0
  125. package/src/domains/shared/semanticPackage.test.ts +89 -0
  126. package/src/domains/shared/semanticPackage.ts +17 -2
  127. package/src/domains/shared/stories/compileLookupCommand.ts +3 -1
  128. package/src/domains/shared/stories/compileLookupTool.ts +1 -1
  129. package/src/domains/shared/stories/pack/applyPackFilters.test.ts +49 -0
  130. package/src/domains/shared/stories/pack/applyPackFilters.ts +46 -11
  131. package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
  132. package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
  133. package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +30 -0
  134. package/src/domains/shared/stories/pack/buildLookupQuery.ts +75 -1
  135. package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
  136. package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
  137. package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
  138. package/src/domains/shared/stories/pack/collectPackStories.test.ts +305 -7
  139. package/src/domains/shared/stories/pack/collectPackStories.ts +90 -8
  140. package/src/domains/shared/stories/pack/compilePackCommands.ts +17 -7
  141. package/src/domains/shared/stories/pack/compilePackStories.test.ts +607 -2
  142. package/src/domains/shared/stories/pack/compilePackStories.ts +544 -73
  143. package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +20 -8
  144. package/src/domains/shared/stories/pack/index.ts +14 -0
  145. package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
  146. package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
  147. package/src/domains/shared/stories/pack/standardParity.test.ts +293 -220
  148. package/src/domains/shared/stories/pack/types.ts +123 -3
  149. package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +502 -0
  150. package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +475 -24
  151. package/src/domains/shared/stories/types.ts +9 -1
  152. package/src/domains/shared/types/index.ts +5 -9
  153. package/src/domains/shared/types/ontology.ts +119 -24
  154. package/src/domains/shared/types/standard.ts +9 -52
  155. package/src/domains/token/commands/lookup.test.ts +8 -0
  156. package/src/domains/token/commands/lookup.ts +12 -0
  157. package/src/domains/token/commands/sample.ts +2 -1
  158. package/src/domains/token/featureFlag.ts +17 -0
  159. package/src/domains/token/index.ts +5 -0
  160. package/src/domains/token/mcp/specs.ts +13 -1
  161. package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
  162. package/src/domains/trace/commands/show.ts +30 -4
  163. package/src/index.ts +1 -13
  164. package/src/mcp/registerResources.test.ts +2 -2
  165. package/src/mcp/resources/pickPrimaryType.test.ts +5 -5
  166. package/src/mcp/tools/index.ts +18 -10
  167. package/src/mcp/tools/registerTools.test.ts +106 -30
  168. package/src/pipeline/collectCommands.test.ts +151 -1
  169. package/src/pipeline/collectCommands.ts +34 -19
  170. package/src/pipeline/createProgram.test.ts +5 -1
  171. package/src/pipeline/createProgram.ts +2 -2
  172. package/src/pipeline/firstRun.test.ts +80 -0
  173. package/src/pipeline/firstRun.ts +99 -0
  174. package/src/pipeline/parseGlobalFlags.test.ts +102 -1
  175. package/src/pipeline/parseGlobalFlags.ts +98 -9
  176. package/src/pipeline/resolveCommandKind.test.ts +12 -0
  177. package/src/pipeline/resolveCommandKind.ts +4 -0
  178. package/src/pipeline/rootHelp.test.ts +87 -0
  179. package/src/pipeline/rootHelp.ts +265 -0
  180. package/src/pipeline/runCli.orchestration.test.ts +28 -12
  181. package/src/pipeline/runCli.test.ts +17 -4
  182. package/src/pipeline/runCli.ts +162 -19
  183. package/src/testing/cli.ts +30 -2
  184. package/src/testing/dsFixtures.ts +21 -4
  185. package/src/testing/index.ts +1 -4
  186. package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +4 -4
  187. package/src/testing/integration/cli-user-stories.test.ts +63 -0
  188. package/src/testing/integration/cross-binary-byte-equality.test.ts +2 -2
  189. package/src/testing/integration/edge-cases.test.ts +62 -50
  190. package/src/testing/integration/executor-execution.test.ts +6 -4
  191. package/src/testing/integration/executor-golden.test.ts +2 -2
  192. package/src/testing/integration/mcp-surface.test.ts +40 -15
  193. package/src/testing/integration/parity.test.ts +119 -70
  194. package/src/testing/setupXdgIsolation.ts +16 -5
  195. package/src/testing/standardParityFixtures.ts +26 -83
  196. package/src/testing/store.ts +4 -4
  197. package/src/domains/create/commands/component.test.ts +0 -166
  198. package/src/domains/create/commands/component.ts +0 -123
  199. package/src/domains/create/commands/index.ts +0 -3
  200. package/src/domains/create/commands/package.test.ts +0 -35
  201. package/src/domains/create/commands/package.ts +0 -21
  202. package/src/domains/create/generators.ts +0 -8
  203. package/src/domains/standard/commands/categories.test.ts +0 -70
  204. package/src/domains/standard/commands/categories.ts +0 -48
  205. package/src/domains/standard/commands/index.ts +0 -6
  206. package/src/domains/standard/commands/list.test.ts +0 -105
  207. package/src/domains/standard/commands/list.ts +0 -11
  208. package/src/domains/standard/commands/lookup.test.ts +0 -135
  209. package/src/domains/standard/commands/lookup.ts +0 -14
  210. package/src/domains/standard/commands/sample.ts +0 -57
  211. package/src/domains/standard/formatters/categories.test.ts +0 -44
  212. package/src/domains/standard/formatters/categories.ts +0 -38
  213. package/src/domains/standard/formatters/index.ts +0 -10
  214. package/src/domains/standard/formatters/list.test.ts +0 -169
  215. package/src/domains/standard/formatters/list.ts +0 -184
  216. package/src/domains/standard/formatters/lookup.test.ts +0 -95
  217. package/src/domains/standard/formatters/lookup.ts +0 -101
  218. package/src/domains/standard/formatters/sample.ts +0 -15
  219. package/src/domains/standard/formatters/types.ts +0 -31
  220. package/src/domains/standard/index.ts +0 -40
  221. package/src/domains/standard/mcp/index.ts +0 -2
  222. package/src/domains/standard/mcp/specs.ts +0 -89
  223. package/src/domains/standard/operations/categories.test.ts +0 -61
  224. package/src/domains/standard/operations/categories.ts +0 -38
  225. package/src/domains/standard/operations/index.ts +0 -6
  226. package/src/domains/standard/operations/list.test.ts +0 -75
  227. package/src/domains/standard/operations/list.ts +0 -64
  228. package/src/domains/standard/operations/lookup.test.ts +0 -58
  229. package/src/domains/standard/operations/lookup.ts +0 -151
  230. package/src/domains/standard/operations/sample.ts +0 -35
  231. package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
  232. package/src/domains/standard/orchestration/index.ts +0 -4
  233. package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
  234. package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -33
  235. package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
  236. package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
  237. package/src/domains/standard/standardConfig.ts +0 -42
  238. package/src/domains/standard/stories.test.ts +0 -24
  239. package/src/domains/standard/stories.ts +0 -151
  240. package/src/domains/standard/types.ts +0 -15
  241. package/src/domains/tier/commands/index.ts +0 -3
  242. package/src/domains/tier/commands/list.test.ts +0 -67
  243. package/src/domains/tier/commands/list.ts +0 -9
  244. package/src/domains/tier/formatters/index.ts +0 -3
  245. package/src/domains/tier/formatters/list.test.ts +0 -33
  246. package/src/domains/tier/formatters/list.ts +0 -45
  247. package/src/domains/tier/index.ts +0 -17
  248. package/src/domains/tier/mcp/index.ts +0 -2
  249. package/src/domains/tier/mcp/specs.ts +0 -12
  250. package/src/domains/tier/operations/index.ts +0 -3
  251. package/src/domains/tier/operations/list.test.ts +0 -32
  252. package/src/domains/tier/operations/list.ts +0 -33
  253. package/src/domains/tier/stories.test.ts +0 -22
  254. package/src/domains/tier/stories.ts +0 -52
@@ -1,40 +0,0 @@
1
- /**
2
- * @module Standard domain — CLI commands and operations.
3
- *
4
- * Commands: `pragma standard list`, `pragma standard lookup <name>`,
5
- * `pragma standard categories`.
6
- * Operations: {@link lookupStandard}, {@link listStandards}, {@link listCategories}.
7
- */
8
-
9
- import type { CommandDefinition } from "@canonical/cli-core";
10
- import type { PragmaContext } from "../shared/context.js";
11
- import {
12
- categoriesCommand,
13
- listCommand,
14
- lookupCommand,
15
- sampleCommand,
16
- } from "./commands/index.js";
17
-
18
- export function commands(ctx: PragmaContext): CommandDefinition[] {
19
- return [
20
- listCommand(ctx),
21
- lookupCommand(ctx),
22
- categoriesCommand(ctx),
23
- sampleCommand(ctx),
24
- ];
25
- }
26
-
27
- export { specs as mcpSpecs } from "./mcp/index.js";
28
- export {
29
- listCategories,
30
- listStandards,
31
- lookupStandard,
32
- sampleStandards,
33
- } from "./operations/index.js";
34
- export {
35
- buildStandardFilters,
36
- resolveStandardList,
37
- resolveStandardLookup,
38
- standardEmptyError,
39
- } from "./orchestration/index.js";
40
- export { standardConfig } from "./standardConfig.js";
@@ -1,2 +0,0 @@
1
- /** @module Standard domain MCP barrel — standard_list, standard_lookup, standard_categories. */
2
- export { default as specs } from "./specs.js";
@@ -1,89 +0,0 @@
1
- /**
2
- * MCP tool specs for the standard domain — standard_list, standard_lookup,
3
- * standard_categories, standard_sample.
4
- *
5
- * The list and lookup tools are compiled from the standard read stories in
6
- * `../stories.ts`; categories and sample are spec'd by hand. The adapter
7
- * layer converts these into registered MCP tools via `registerFromSpec()`.
8
- */
9
-
10
- import {
11
- compileLookupTool,
12
- compileReadTool,
13
- condense,
14
- } from "../../shared/stories/index.js";
15
- import type { ToolSpec } from "../../shared/ToolSpec.js";
16
- import {
17
- categoriesFormatters as standardCatFmt,
18
- sampleFormatters as standardSampleFmt,
19
- } from "../formatters/index.js";
20
- import { listCategories, sampleStandards } from "../operations/index.js";
21
- import { standardListStory, standardLookupStory } from "../stories.js";
22
-
23
- const specs: readonly ToolSpec[] = [
24
- compileReadTool(standardListStory),
25
- compileLookupTool(standardLookupStory),
26
- {
27
- name: "standard_categories",
28
- description: "List all code standard categories.",
29
- params: {
30
- condensed: {
31
- type: "boolean",
32
- description: "Token-optimized output",
33
- optional: true,
34
- },
35
- },
36
- readOnly: true,
37
- async execute(rt, { condensed }) {
38
- const result = await listCategories(rt.store);
39
-
40
- if (condensed) {
41
- return condense(standardCatFmt.llm(result));
42
- }
43
-
44
- return { data: result, meta: { count: result.length } };
45
- },
46
- },
47
- {
48
- name: "standard_sample",
49
- description:
50
- "Return 1–5 randomly selected complete code standard instances as exemplars. Use BEFORE writing queries to see actual data shapes, property names, and value formats. Each call returns different instances.",
51
- params: {
52
- count: {
53
- type: "string",
54
- description: "Number of samples (1–5, default 2)",
55
- optional: true,
56
- },
57
- condensed: {
58
- type: "boolean",
59
- description: "Token-optimized output",
60
- optional: true,
61
- },
62
- },
63
- readOnly: true,
64
- async execute(rt, { count, condensed }) {
65
- const n = Number(count ?? 2);
66
- const result = await sampleStandards(rt.store, n);
67
- const nextSteps = [
68
- `These are ${result.samples.length} of ${result.totalCount} total standards.`,
69
- "Use standard_lookup to inspect specific standards by name.",
70
- "Use standard_list with category param to filter by category.",
71
- ];
72
-
73
- if (condensed) {
74
- return condense(standardSampleFmt.llm({ ...result, nextSteps }));
75
- }
76
-
77
- return {
78
- data: {
79
- samples: result.samples,
80
- totalCount: result.totalCount,
81
- nextSteps,
82
- },
83
- meta: { count: result.samples.length },
84
- };
85
- },
86
- },
87
- ] as const;
88
-
89
- export default specs;
@@ -1,61 +0,0 @@
1
- import type { Store } from "@canonical/ke";
2
- import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
- import { createTestStore, DS_ALL_TTL } from "#testing";
4
- import { PREFIX_MAP } from "../../shared/prefixes.js";
5
- import listCategories from "./categories.js";
6
-
7
- let store: Store;
8
- let cleanup: () => void;
9
-
10
- beforeAll(async () => {
11
- const result = await createTestStore({ ttl: DS_ALL_TTL });
12
- store = result.store;
13
- cleanup = result.cleanup;
14
- });
15
-
16
- afterAll(() => cleanup());
17
-
18
- describe("listCategories", () => {
19
- it("returns categories with standard counts", async () => {
20
- const result = await listCategories(store);
21
- expect(result.length).toBe(2);
22
-
23
- const react = result.find((c) => c.name === "react");
24
- expect(react?.standardCount).toBe(2);
25
-
26
- const code = result.find((c) => c.name === "code");
27
- expect(code?.standardCount).toBe(1);
28
- });
29
-
30
- it("returns sorted by name", async () => {
31
- const result = await listCategories(store);
32
- const names = result.map((c) => c.name);
33
- const sorted = [...names].sort();
34
- expect(names).toEqual(sorted);
35
- });
36
-
37
- it("includes categories with zero standards", async () => {
38
- const ttlWithEmptyCategory = `
39
- @prefix cs: <${PREFIX_MAP.cs}> .
40
- cs:empty_cat a cs:Category ; cs:slug "empty" .
41
- cs:filled_cat a cs:Category ; cs:slug "filled" .
42
- cs:s1 a cs:CodeStandard ;
43
- cs:name "filled/one" ;
44
- cs:hasCategory cs:filled_cat ;
45
- cs:description "A standard" .
46
- `;
47
- const { store: testStore, cleanup: testCleanup } = await createTestStore({
48
- ttl: ttlWithEmptyCategory,
49
- });
50
- try {
51
- const result = await listCategories(testStore);
52
- const empty = result.find((c) => c.name === "empty");
53
- expect(empty).toBeDefined();
54
- expect(empty?.standardCount).toBe(0);
55
- const filled = result.find((c) => c.name === "filled");
56
- expect(filled?.standardCount).toBe(1);
57
- } finally {
58
- testCleanup();
59
- }
60
- });
61
- });
@@ -1,38 +0,0 @@
1
- /**
2
- * Lists all standard categories with the number of standards in each.
3
- *
4
- * @param store - ke store to query
5
- * @returns array of category summaries ordered by name, empty when none exist
6
- * @note Queries ke store
7
- */
8
-
9
- import type { Store } from "@canonical/ke";
10
- import { buildQuery } from "../../shared/buildQuery.js";
11
- import { P } from "../../shared/prefixes.js";
12
- import type { CategorySummary } from "../../shared/types/index.js";
13
-
14
- export default async function listCategories(
15
- store: Store,
16
- ): Promise<CategorySummary[]> {
17
- const result = await store.query(
18
- buildQuery(`
19
- SELECT ?categoryName (COUNT(?standard) AS ?count)
20
- WHERE {
21
- ?cat a ${P.cs}Category ;
22
- ${P.cs}slug ?categoryName .
23
- OPTIONAL {
24
- ?standard a ${P.cs}CodeStandard ;
25
- ${P.cs}hasCategory ?cat .
26
- }
27
- }
28
- GROUP BY ?categoryName
29
- ORDER BY ?categoryName
30
- `),
31
- );
32
-
33
- if (result.type !== "select") return [];
34
- return result.bindings.map((b) => ({
35
- name: b.categoryName ?? "",
36
- standardCount: Number.parseInt(b.count ?? "0", 10) || 0,
37
- }));
38
- }
@@ -1,6 +0,0 @@
1
- /** @module Re-exports standard operations (`listCategories`, `listStandards`, `lookupStandard`, `sampleStandards`). */
2
-
3
- export { default as listCategories } from "./categories.js";
4
- export { default as listStandards } from "./list.js";
5
- export { default as lookupStandard } from "./lookup.js";
6
- export { default as sampleStandards } from "./sample.js";
@@ -1,75 +0,0 @@
1
- import type { Store } from "@canonical/ke";
2
- import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
- import { createTestStore, DS_ALL_TTL } from "#testing";
4
- import listStandards from "./list.js";
5
-
6
- let store: Store;
7
- let cleanup: () => void;
8
-
9
- beforeAll(async () => {
10
- const result = await createTestStore({ ttl: DS_ALL_TTL });
11
- store = result.store;
12
- cleanup = result.cleanup;
13
- });
14
-
15
- afterAll(() => cleanup());
16
-
17
- describe("listStandards", () => {
18
- it("returns all standards", async () => {
19
- const result = await listStandards(store);
20
- expect(result.length).toBe(3);
21
- const names = result.map((s) => s.name);
22
- expect(names).toContain("react/component/folder-structure");
23
- expect(names).toContain("react/component/props");
24
- expect(names).toContain("code/function/purity");
25
- });
26
-
27
- it("includes category names", async () => {
28
- const result = await listStandards(store);
29
- const folder = result.find(
30
- (s) => s.name === "react/component/folder-structure",
31
- );
32
- expect(folder?.category).toBe("react");
33
- });
34
-
35
- it("returns sorted by name", async () => {
36
- const result = await listStandards(store);
37
- const names = result.map((s) => s.name);
38
- const sorted = [...names].sort();
39
- expect(names).toEqual(sorted);
40
- });
41
-
42
- it("filters by category", async () => {
43
- const result = await listStandards(store, { category: "react" });
44
- expect(result.length).toBe(2);
45
- for (const s of result) {
46
- expect(s.category).toBe("react");
47
- }
48
- });
49
-
50
- it("filters by search term in name", async () => {
51
- const result = await listStandards(store, { search: "folder" });
52
- expect(result.length).toBe(1);
53
- expect(result[0]?.name).toBe("react/component/folder-structure");
54
- });
55
-
56
- it("filters by search term in description", async () => {
57
- const result = await listStandards(store, { search: "pure" });
58
- expect(result.length).toBe(1);
59
- expect(result[0]?.name).toBe("code/function/purity");
60
- });
61
-
62
- it("returns empty for non-matching category", async () => {
63
- const result = await listStandards(store, { category: "nonexistent" });
64
- expect(result.length).toBe(0);
65
- });
66
-
67
- it("combines category and search filters", async () => {
68
- const result = await listStandards(store, {
69
- category: "react",
70
- search: "props",
71
- });
72
- expect(result.length).toBe(1);
73
- expect(result[0]?.name).toBe("react/component/props");
74
- });
75
- });
@@ -1,64 +0,0 @@
1
- /**
2
- * Lists all code standards, optionally filtered by category or search term.
3
- *
4
- * @param store - ke store to query
5
- * @param filters - optional category and/or search text filters
6
- * @returns array of standard summaries ordered by name, empty when none match
7
- * @note Queries ke store
8
- */
9
-
10
- import type { Store, URI } from "@canonical/ke";
11
- import { escapeSparqlValue } from "@canonical/ke";
12
- import { buildQuery } from "../../shared/buildQuery.js";
13
- import { P } from "../../shared/prefixes.js";
14
- import type {
15
- StandardListFilters,
16
- StandardSummary,
17
- } from "../../shared/types/index.js";
18
-
19
- export default async function listStandards(
20
- store: Store,
21
- filters?: StandardListFilters,
22
- ): Promise<StandardSummary[]> {
23
- const filterClauses: string[] = [];
24
-
25
- if (filters?.category) {
26
- const escaped = escapeSparqlValue(filters.category.toLowerCase());
27
- filterClauses.push(
28
- `?standard ${P.cs}hasCategory ?filterCat . ?filterCat ${P.cs}slug ?catSlug . FILTER(?catSlug = ${escaped})`,
29
- );
30
- }
31
-
32
- if (filters?.search) {
33
- const escaped = escapeSparqlValue(filters.search.toLowerCase());
34
- filterClauses.push(
35
- `FILTER(CONTAINS(LCASE(?name), ${escaped}) || CONTAINS(LCASE(?description), ${escaped}))`,
36
- );
37
- }
38
-
39
- const result = await store.query(
40
- buildQuery(`
41
- SELECT ?standard ?name ?categoryName ?description
42
- WHERE {
43
- ?standard a ${P.cs}CodeStandard ;
44
- ${P.cs}name ?name ;
45
- ${P.cs}description ?description .
46
- OPTIONAL {
47
- ?standard ${P.cs}hasCategory ?cat .
48
- ?cat ${P.cs}slug ?categoryName .
49
- }
50
- ${filterClauses.join("\n ")}
51
- }
52
- ORDER BY ?name
53
- `),
54
- );
55
-
56
- if (result.type !== "select") return [];
57
-
58
- return result.bindings.map((b) => ({
59
- uri: (b.standard ?? "") as URI,
60
- name: b.name ?? "",
61
- category: b.categoryName ?? "",
62
- description: b.description ?? "",
63
- }));
64
- }
@@ -1,58 +0,0 @@
1
- import type { Store } from "@canonical/ke";
2
- import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
- import { PragmaError } from "#error";
4
- import { createTestStore, DS_ALL_TTL } from "#testing";
5
- import lookupStandard from "./lookup.js";
6
-
7
- let store: Store;
8
- let cleanup: () => void;
9
-
10
- beforeAll(async () => {
11
- const result = await createTestStore({ ttl: DS_ALL_TTL });
12
- store = result.store;
13
- cleanup = result.cleanup;
14
- });
15
-
16
- afterAll(() => cleanup());
17
-
18
- describe("lookupStandard", () => {
19
- it("returns detailed data with dos and donts", async () => {
20
- const result = await lookupStandard(
21
- store,
22
- "react/component/folder-structure",
23
- );
24
- expect(result.name).toBe("react/component/folder-structure");
25
- expect(result.category).toBe("react");
26
- expect(result.dos.length).toBeGreaterThan(0);
27
- expect(result.donts.length).toBeGreaterThan(0);
28
- });
29
-
30
- it("throws PragmaError.notFound for unknown standard", async () => {
31
- await expect(lookupStandard(store, "nonexistent")).rejects.toThrow(
32
- PragmaError,
33
- );
34
-
35
- try {
36
- await lookupStandard(store, "nonexistent");
37
- } catch (e) {
38
- expect((e as PragmaError).code).toBe("ENTITY_NOT_FOUND");
39
- }
40
- });
41
-
42
- it("resolves prefixed IRIs and compacts extends", async () => {
43
- const result = await lookupStandard(store, "cs:react_props");
44
- expect(result.name).toBe("react/component/props");
45
- expect(result.extends).toBe("cs:react_folder");
46
- });
47
-
48
- it("resolves full IRIs", async () => {
49
- const result = await lookupStandard(
50
- store,
51
- "http://pragma.canonical.com/codestandards#react_props",
52
- );
53
- expect(result.name).toBe("react/component/props");
54
- expect(result.uri).toBe(
55
- "http://pragma.canonical.com/codestandards#react_props",
56
- );
57
- });
58
- });
@@ -1,151 +0,0 @@
1
- /**
2
- * Look up detailed information for a single code standard by name.
3
- *
4
- * Queries the base standard data plus its dos and donts code blocks,
5
- * then assembles a {@link StandardDetailed} object.
6
- *
7
- * @param store - ke store to query
8
- * @param name - standard name (e.g. "react/component/folder-structure")
9
- * @returns full standard detail including dos/donts code blocks
10
- * @throws PragmaError.notFound if the standard does not exist
11
- * @note Queries ke store
12
- */
13
-
14
- import type { Store, URI } from "@canonical/ke";
15
- import { escapeSparqlValue } from "@canonical/ke";
16
- import { PragmaError } from "#error";
17
- import resolveUri from "../../graph/helpers/resolveUri.js";
18
- import { buildQuery } from "../../shared/buildQuery.js";
19
- import compactUri from "../../shared/compactUri.js";
20
- import { P } from "../../shared/prefixes.js";
21
- import {
22
- detectCrossDomain,
23
- listDomainNames,
24
- suggestNames,
25
- } from "../../shared/suggestions/index.js";
26
- import type { CodeBlock, StandardDetailed } from "../../shared/types/index.js";
27
-
28
- export default async function lookupStandard(
29
- store: Store,
30
- nameOrUri: string,
31
- ): Promise<StandardDetailed> {
32
- const subjectClause = buildLookupSubjectClause(nameOrUri, store.prefixes);
33
- const bindClause =
34
- subjectClause === null ? "" : `BIND(${subjectClause} AS ?standard)`;
35
- const matchClause =
36
- subjectClause === null
37
- ? `?standard ${P.cs}name ${escapeSparqlValue(nameOrUri)} .`
38
- : "";
39
-
40
- const baseResult = await store.query(
41
- buildQuery(`
42
- SELECT ?standard ?name ?categoryName ?description ?extends
43
- WHERE {
44
- ${bindClause}
45
- ?standard a ${P.cs}CodeStandard ;
46
- ${P.cs}name ?name ;
47
- ${P.cs}description ?description .
48
- ${matchClause}
49
- OPTIONAL {
50
- ?standard ${P.cs}hasCategory ?cat .
51
- ?cat ${P.cs}slug ?categoryName .
52
- }
53
- OPTIONAL { ?standard ${P.cs}extends ?extends }
54
- }
55
- LIMIT 1
56
- `),
57
- );
58
-
59
- if (baseResult.type !== "select" || baseResult.bindings.length === 0) {
60
- const candidates = await listDomainNames(store, "standard");
61
- const crossDomain = await detectCrossDomain(nameOrUri, "standard", store);
62
- throw PragmaError.notFound("standard", nameOrUri, {
63
- suggestions: suggestNames(nameOrUri, candidates),
64
- recovery: {
65
- message: "List available standards.",
66
- cli: "pragma standard list",
67
- mcp: { tool: "standard_list" },
68
- },
69
- crossDomain: crossDomain ?? undefined,
70
- });
71
- }
72
-
73
- // Safe: length check above guarantees bindings[0] exists
74
- const base = baseResult.bindings[0] as (typeof baseResult.bindings)[number];
75
- const standardUri = base.standard;
76
-
77
- // Fetch dos (cs:do → cs:Example with cs:description, cs:language, cs:code)
78
- const dosResult = await store.query(
79
- buildQuery(`
80
- SELECT ?description ?language ?code
81
- WHERE {
82
- <${standardUri}> ${P.cs}do ?example .
83
- OPTIONAL { ?example ${P.cs}description ?description }
84
- OPTIONAL { ?example ${P.cs}language ?language }
85
- OPTIONAL { ?example ${P.cs}code ?code }
86
- }
87
- `),
88
- );
89
-
90
- const dos: CodeBlock[] =
91
- dosResult.type === "select"
92
- ? dosResult.bindings.map((b) => ({
93
- language: b.language ?? "typescript",
94
- code: b.code ?? "",
95
- caption: b.description,
96
- }))
97
- : [];
98
-
99
- // Fetch donts (cs:dont → cs:Example with cs:description, cs:language, cs:code)
100
- const dontsResult = await store.query(
101
- buildQuery(`
102
- SELECT ?description ?language ?code
103
- WHERE {
104
- <${standardUri}> ${P.cs}dont ?example .
105
- OPTIONAL { ?example ${P.cs}description ?description }
106
- OPTIONAL { ?example ${P.cs}language ?language }
107
- OPTIONAL { ?example ${P.cs}code ?code }
108
- }
109
- `),
110
- );
111
-
112
- const donts: CodeBlock[] =
113
- dontsResult.type === "select"
114
- ? dontsResult.bindings.map((b) => ({
115
- language: b.language ?? "typescript",
116
- code: b.code ?? "",
117
- caption: b.description,
118
- }))
119
- : [];
120
-
121
- return {
122
- uri: (standardUri ?? "") as URI,
123
- name: base.name ?? nameOrUri,
124
- category: base.categoryName ?? "",
125
- description: base.description ?? "",
126
- extends: base.extends
127
- ? compactUri(base.extends, store.prefixes)
128
- : undefined,
129
- dos,
130
- donts,
131
- };
132
- }
133
-
134
- function buildLookupSubjectClause(
135
- query: string,
136
- prefixes: Readonly<Record<string, string>>,
137
- ): string | null {
138
- if (!looksLikeUri(query)) {
139
- return null;
140
- }
141
-
142
- return `<${resolveUri(query, prefixes)}>`;
143
- }
144
-
145
- function looksLikeUri(query: string): boolean {
146
- return (
147
- query.startsWith("http://") ||
148
- query.startsWith("https://") ||
149
- query.includes(":")
150
- );
151
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Returns a small number of complete standard instances as exemplars.
3
- *
4
- * Composes the existing list and lookup operations to avoid duplicating
5
- * any SPARQL logic. Agents use this to learn the data shape before querying.
6
- *
7
- * @param store - ke store to query
8
- * @param count - number of samples (clamped 1–5, default 2)
9
- * @returns sample result with detailed standards and total count
10
- * @note Queries ke store
11
- */
12
-
13
- import type { Store } from "@canonical/ke";
14
- import pickRandom from "../../shared/pickRandom.js";
15
- import type {
16
- SampleResult,
17
- StandardDetailed,
18
- } from "../../shared/types/index.js";
19
- import listStandards from "./list.js";
20
- import lookupStandard from "./lookup.js";
21
-
22
- export default async function sampleStandards(
23
- store: Store,
24
- count = 2,
25
- ): Promise<SampleResult<StandardDetailed>> {
26
- const clamped = Math.max(1, Math.min(5, count));
27
- const all = await listStandards(store);
28
- const selected = pickRandom(all, clamped);
29
-
30
- const samples = await Promise.all(
31
- selected.map((s) => lookupStandard(store, s.name)),
32
- );
33
-
34
- return { samples, totalCount: all.length };
35
- }
@@ -1,11 +0,0 @@
1
- import type { StandardListFilters } from "../../shared/types/index.js";
2
-
3
- export default function buildStandardFilters(params: {
4
- category?: string;
5
- search?: string;
6
- }): StandardListFilters {
7
- return {
8
- category: params.category,
9
- search: params.search,
10
- };
11
- }
@@ -1,4 +0,0 @@
1
- export { default as buildStandardFilters } from "./buildStandardFilters.js";
2
- export { default as resolveStandardList } from "./resolveStandardList.js";
3
- export { default as resolveStandardLookup } from "./resolveStandardLookup.js";
4
- export { default as standardEmptyError } from "./standardEmptyError.js";
@@ -1,45 +0,0 @@
1
- import type { ListParams } from "../../shared/contracts.js";
2
- import type { PragmaRuntime } from "../../shared/runtime.js";
3
- import type {
4
- Disclosure,
5
- StandardListFilters,
6
- } from "../../shared/types/index.js";
7
- import { listStandards, lookupStandard } from "../operations/index.js";
8
- import type { StandardListResolution } from "../types.js";
9
- import buildStandardFilters from "./buildStandardFilters.js";
10
- import standardEmptyError from "./standardEmptyError.js";
11
-
12
- export default async function resolveStandardList(
13
- rt: Pick<PragmaRuntime, "store">,
14
- params: ListParams & StandardListFilters,
15
- ): Promise<StandardListResolution> {
16
- const filters = buildStandardFilters(params);
17
- const items = await listStandards(rt.store, filters);
18
-
19
- if (items.length === 0) {
20
- throw standardEmptyError(filters);
21
- }
22
-
23
- const disclosure: Disclosure = params.detailed
24
- ? { level: "detailed" }
25
- : params.digest
26
- ? { level: "digest" }
27
- : { level: "summary" };
28
-
29
- const details =
30
- disclosure.level === "summary"
31
- ? undefined
32
- : await Promise.all(
33
- items.map((item) =>
34
- lookupStandard(rt.store, item.name).catch(() => null),
35
- ),
36
- );
37
-
38
- return {
39
- params,
40
- items,
41
- filters,
42
- disclosure,
43
- details,
44
- };
45
- }