@canonical/pragma-cli 0.27.1-experimental.0 → 0.29.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 (169) hide show
  1. package/dist/pragma +0 -0
  2. package/package.json +16 -17
  3. package/src/config/readConfig.ts +38 -2
  4. package/src/config/types.ts +12 -1
  5. package/src/config/writeConfig.ts +12 -0
  6. package/src/constants.ts +12 -1
  7. package/src/domains/block/commands/index.ts +2 -1
  8. package/src/domains/block/commands/sample.ts +55 -0
  9. package/src/domains/block/formatters/index.ts +2 -1
  10. package/src/domains/block/formatters/sample.ts +21 -0
  11. package/src/domains/block/index.ts +3 -3
  12. package/src/domains/block/mcp/specs.ts +48 -0
  13. package/src/domains/block/operations/index.ts +2 -1
  14. package/src/domains/block/operations/lookup.ts +9 -0
  15. package/src/domains/block/operations/sample.ts +41 -0
  16. package/src/domains/block/orchestration/blockEmptyError.test.ts +26 -0
  17. package/src/domains/block/orchestration/resolveBlockLookup.ts +10 -3
  18. package/src/domains/config/commands/framework.test.ts +88 -0
  19. package/src/domains/config/commands/framework.ts +85 -0
  20. package/src/domains/config/commands/index.ts +2 -0
  21. package/src/domains/config/commands/trace.test.ts +148 -0
  22. package/src/domains/config/commands/trace.ts +80 -0
  23. package/src/domains/config/formatters/framework.ts +34 -0
  24. package/src/domains/config/formatters/index.ts +2 -0
  25. package/src/domains/config/formatters/trace.ts +28 -0
  26. package/src/domains/config/index.ts +15 -2
  27. package/src/domains/config/operations/index.ts +1 -0
  28. package/src/domains/config/operations/validateFramework.ts +24 -0
  29. package/src/domains/config/tasks/index.ts +2 -0
  30. package/src/domains/config/tasks/setChannel.ts +7 -1
  31. package/src/domains/config/tasks/setFramework.ts +40 -0
  32. package/src/domains/config/tasks/setTier.ts +7 -1
  33. package/src/domains/config/tasks/setTrace.ts +38 -0
  34. package/src/domains/doctor/operations/checks/checkKeStore.ts +5 -5
  35. package/src/domains/doctor/operations/checks/checkPackageRefs.ts +35 -41
  36. package/src/domains/doctor/operations/checks.test.ts +4 -1
  37. package/src/domains/doctor/operations/runChecks.test.ts +72 -0
  38. package/src/domains/doctor/operations/runChecks.ts +48 -12
  39. package/src/domains/graphql/commands/build.test.ts +194 -0
  40. package/src/domains/graphql/commands/build.ts +125 -0
  41. package/src/domains/graphql/commands/check.test.ts +139 -0
  42. package/src/domains/graphql/commands/check.ts +87 -0
  43. package/src/domains/graphql/commands/index.ts +4 -0
  44. package/src/domains/graphql/formatters/index.ts +3 -0
  45. package/src/domains/graphql/formatters/report.ts +78 -0
  46. package/src/domains/graphql/helpers/index.ts +4 -0
  47. package/src/domains/graphql/helpers/parsePrefixes.test.ts +44 -0
  48. package/src/domains/graphql/helpers/parsePrefixes.ts +38 -0
  49. package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +56 -0
  50. package/src/domains/graphql/helpers/resolveSourceFiles.ts +45 -0
  51. package/src/domains/graphql/index.ts +21 -0
  52. package/src/domains/graphql/operations/compileSchema.test.ts +119 -0
  53. package/src/domains/graphql/operations/compileSchema.ts +109 -0
  54. package/src/domains/graphql/operations/index.ts +7 -0
  55. package/src/domains/graphql/types.ts +29 -0
  56. package/src/domains/info/operations/collectInfo.test.ts +2 -2
  57. package/src/domains/info/operations/collectInfo.ts +4 -4
  58. package/src/domains/llm/capabilities.test.ts +91 -0
  59. package/src/domains/llm/commands/capabilities.ts +28 -0
  60. package/src/domains/llm/commands/index.ts +1 -0
  61. package/src/domains/llm/data/commandReference.ts +90 -18
  62. package/src/domains/llm/data/decisionTrees.ts +15 -13
  63. package/src/domains/llm/data/index.ts +2 -0
  64. package/src/domains/llm/data/toolCatalog.ts +184 -0
  65. package/src/domains/llm/formatters/capabilities.ts +50 -0
  66. package/src/domains/llm/formatters/index.ts +1 -0
  67. package/src/domains/llm/index.ts +3 -2
  68. package/src/domains/llm/mcp/index.ts +1 -1
  69. package/src/domains/llm/mcp/specs.ts +59 -52
  70. package/src/domains/llm/types.ts +40 -0
  71. package/src/domains/modifier/commands/index.ts +2 -1
  72. package/src/domains/modifier/commands/sample.ts +53 -0
  73. package/src/domains/modifier/formatters/index.ts +2 -1
  74. package/src/domains/modifier/formatters/sample.ts +15 -0
  75. package/src/domains/modifier/index.ts +7 -3
  76. package/src/domains/modifier/mcp/specs.ts +47 -0
  77. package/src/domains/modifier/operations/index.ts +2 -1
  78. package/src/domains/modifier/operations/lookup.ts +9 -0
  79. package/src/domains/modifier/operations/sample.ts +32 -0
  80. package/src/domains/modifier/orchestration/modifierEmptyError.test.ts +26 -0
  81. package/src/domains/modifier/orchestration/modifierEmptyError.ts +7 -2
  82. package/src/domains/modifier/orchestration/resolveModifierLookup.ts +14 -2
  83. package/src/domains/refs/operations/index.ts +1 -1
  84. package/src/domains/refs/operations/paths.ts +29 -1
  85. package/src/domains/refs/operations/updateRefs.test.ts +6 -3
  86. package/src/domains/refs/operations/updateRefs.ts +3 -2
  87. package/src/domains/shared/bootStore.ts +88 -49
  88. package/src/domains/shared/contracts.ts +1 -0
  89. package/src/domains/shared/emptyErrorRecovery.test.ts +109 -0
  90. package/src/domains/shared/loaders/bundledLoader.ts +108 -0
  91. package/src/domains/shared/loaders/gitLoader.ts +31 -0
  92. package/src/domains/shared/loaders/index.ts +6 -0
  93. package/src/domains/shared/loaders/localLoader.ts +73 -0
  94. package/src/domains/shared/loaders/readPackageDir.ts +112 -0
  95. package/src/domains/shared/lookupMany.ts +9 -1
  96. package/src/domains/shared/packages.ts +21 -120
  97. package/src/domains/shared/pickRandom.ts +22 -0
  98. package/src/domains/shared/runtime.ts +6 -3
  99. package/src/domains/shared/sampleFormatters.ts +47 -0
  100. package/src/domains/shared/semanticPackage.ts +96 -0
  101. package/src/domains/shared/suggestions/detectCrossDomain.test.ts +43 -0
  102. package/src/domains/shared/suggestions/detectCrossDomain.ts +116 -0
  103. package/src/domains/shared/suggestions/expandGlob.test.ts +92 -0
  104. package/src/domains/shared/suggestions/expandGlob.ts +51 -0
  105. package/src/domains/shared/suggestions/expandLookupQueries.ts +57 -0
  106. package/src/domains/shared/suggestions/index.ts +7 -0
  107. package/src/domains/shared/suggestions/listDomainNames.ts +61 -0
  108. package/src/domains/shared/suggestions/suggestNames.test.ts +70 -0
  109. package/src/domains/shared/suggestions/suggestNames.ts +97 -0
  110. package/src/domains/shared/types/index.ts +1 -0
  111. package/src/domains/shared/types/runtime.ts +4 -1
  112. package/src/domains/shared/types/sample.ts +17 -0
  113. package/src/domains/skill/helpers/resolveSkillSources.ts +49 -16
  114. package/src/domains/standard/commands/index.ts +2 -1
  115. package/src/domains/standard/commands/sample.ts +57 -0
  116. package/src/domains/standard/formatters/index.ts +2 -1
  117. package/src/domains/standard/formatters/sample.ts +15 -0
  118. package/src/domains/standard/index.ts +8 -1
  119. package/src/domains/standard/mcp/specs.ts +48 -1
  120. package/src/domains/standard/operations/index.ts +2 -1
  121. package/src/domains/standard/operations/lookup.ts +9 -0
  122. package/src/domains/standard/operations/sample.ts +35 -0
  123. package/src/domains/standard/orchestration/resolveStandardLookup.ts +14 -2
  124. package/src/domains/standard/orchestration/standardEmptyError.test.ts +33 -0
  125. package/src/domains/standard/orchestration/standardEmptyError.ts +16 -6
  126. package/src/domains/token/commands/index.ts +2 -1
  127. package/src/domains/token/commands/sample.ts +53 -0
  128. package/src/domains/token/formatters/index.ts +2 -1
  129. package/src/domains/token/formatters/sample.ts +16 -0
  130. package/src/domains/token/index.ts +8 -1
  131. package/src/domains/token/mcp/specs.ts +47 -1
  132. package/src/domains/token/operations/index.ts +2 -1
  133. package/src/domains/token/operations/lookup.ts +9 -0
  134. package/src/domains/token/operations/sample.ts +32 -0
  135. package/src/domains/token/orchestration/resolveTokenLookup.ts +14 -2
  136. package/src/domains/token/orchestration/tokenEmptyError.test.ts +33 -0
  137. package/src/domains/token/orchestration/tokenEmptyError.ts +5 -2
  138. package/src/domains/trace/commands/clear.ts +71 -0
  139. package/src/domains/trace/commands/index.ts +5 -0
  140. package/src/domains/trace/commands/sessions.ts +32 -0
  141. package/src/domains/trace/commands/show.ts +145 -0
  142. package/src/domains/trace/formatters/index.ts +9 -0
  143. package/src/domains/trace/formatters/sessions.ts +80 -0
  144. package/src/domains/trace/formatters/show.ts +122 -0
  145. package/src/domains/trace/index.ts +17 -0
  146. package/src/domains/trace/operations/index.ts +8 -0
  147. package/src/domains/trace/operations/listSessions.test.ts +55 -0
  148. package/src/domains/trace/operations/listSessions.ts +52 -0
  149. package/src/domains/trace/operations/readTraceLog.test.ts +100 -0
  150. package/src/domains/trace/operations/readTraceLog.ts +104 -0
  151. package/src/domains/trace/tracePlugin.test.ts +171 -0
  152. package/src/domains/trace/tracePlugin.ts +159 -0
  153. package/src/domains/trace/types.ts +65 -0
  154. package/src/error/PragmaError.ts +15 -2
  155. package/src/error/types.ts +15 -1
  156. package/src/mcp/error/serializeErrorPayload.ts +7 -0
  157. package/src/mcp/error/types.ts +8 -0
  158. package/src/mcp/registerResources.test.ts +38 -16
  159. package/src/mcp/registerResources.ts +57 -8
  160. package/src/mcp/tools/registerTools.test.ts +22 -7
  161. package/src/mcp/utils/wrapTool.test.ts +1 -0
  162. package/src/pipeline/collectCommands.ts +7 -1
  163. package/src/pipeline/renderError.ts +15 -0
  164. package/src/pipeline/resolveCommandKind.ts +8 -1
  165. package/src/pipeline/runCli.ts +12 -1
  166. package/src/testing/createTestMcpClient.ts +1 -0
  167. package/src/testing/graphqlFixtures.ts +72 -0
  168. package/src/testing/index.ts +7 -0
  169. package/src/testing/integration/mcp-surface.test.ts +11 -5
@@ -135,6 +135,53 @@ async function listAllSubjects(store: Store): Promise<string[]> {
135
135
  return result.bindings.map((b) => b.s ?? "").filter((s) => isUri(s));
136
136
  }
137
137
 
138
+ const RDFS_LABEL = "http://www.w3.org/2000/01/rdf-schema#label";
139
+
140
+ /**
141
+ * Fetch the rdfs:label of the entity's rdf:type class.
142
+ *
143
+ * Returns the label of the first typed class that has one, or null.
144
+ */
145
+ async function fetchTypeLabel(
146
+ store: Store,
147
+ fullUri: string,
148
+ ): Promise<string | null> {
149
+ const result = await store.query(
150
+ buildQuery(`
151
+ SELECT ?typeLabel
152
+ WHERE {
153
+ <${fullUri}> <${RDF_TYPE}> ?type .
154
+ ?type <${RDFS_LABEL}> ?typeLabel .
155
+ }
156
+ LIMIT 1
157
+ `),
158
+ );
159
+
160
+ if (result.type !== "select" || result.bindings.length === 0) return null;
161
+ return result.bindings[0]?.typeLabel ?? null;
162
+ }
163
+
164
+ /**
165
+ * Build a resource description from type label and graph description.
166
+ *
167
+ * Produces: "Component — Buttons are used to trigger…" or just "Component"
168
+ * when no description exists, or just the truncated description when no
169
+ * type is recognized.
170
+ */
171
+ function buildResourceDescription(
172
+ typeLabel: string | null,
173
+ description: string | null,
174
+ ): string | undefined {
175
+ if (!typeLabel && !description) return undefined;
176
+ const truncated =
177
+ description && description.length > 120
178
+ ? `${description.slice(0, 117)}...`
179
+ : description;
180
+ if (!typeLabel) return truncated ?? undefined;
181
+ if (!truncated) return typeLabel;
182
+ return `${typeLabel} — ${truncated}`;
183
+ }
184
+
138
185
  /**
139
186
  * Read a single entity: all its triples with level-1 object summaries.
140
187
  */
@@ -234,17 +281,23 @@ export default function registerResources(
234
281
  const { store } = runtime;
235
282
  const prefixes = store.prefixes;
236
283
 
237
- const template = new ResourceTemplate("pragma:{+uri}", {
284
+ const template = new ResourceTemplate("{+uri}", {
238
285
  list: async () => {
239
286
  const subjects = await listAllSubjects(store);
240
287
  const resources = await Promise.all(
241
288
  subjects.map(async (fullUri) => {
242
289
  const prefixed = compactUri(fullUri, prefixes);
243
- const label = await fetchLabel(store, fullUri, prefixes);
290
+ const [label, typeLabel, desc] = await Promise.all([
291
+ fetchLabel(store, fullUri, prefixes),
292
+ fetchTypeLabel(store, fullUri),
293
+ fetchDescription(store, fullUri, prefixes),
294
+ ]);
244
295
  return {
245
- uri: `pragma:${prefixed}`,
296
+ uri: prefixed,
246
297
  name: label ?? prefixed,
298
+ description: buildResourceDescription(typeLabel, desc),
247
299
  mimeType: "application/json" as const,
300
+ annotations: { audience: ["assistant" as const] },
248
301
  };
249
302
  }),
250
303
  );
@@ -264,11 +317,7 @@ export default function registerResources(
264
317
  server.registerResource(
265
318
  "graph-entity",
266
319
  template,
267
- {
268
- description:
269
- "An entity in the pragma knowledge graph. Returns all properties with level-1 relation summaries.",
270
- mimeType: "application/json",
271
- },
320
+ { mimeType: "application/json" },
272
321
  async (url, variables) => {
273
322
  try {
274
323
  const prefixedUri = variables.uri as string;
@@ -52,9 +52,9 @@ function parseData(result: Record<string, unknown>): unknown {
52
52
  // =============================================================================
53
53
 
54
54
  describe("tool listing", () => {
55
- it("registers 25 tools", async () => {
55
+ it("registers 29 tools", async () => {
56
56
  const { tools } = await client.listTools();
57
- expect(tools).toHaveLength(25);
57
+ expect(tools).toHaveLength(29);
58
58
  });
59
59
 
60
60
  it("all tools have descriptions", async () => {
@@ -94,6 +94,11 @@ describe("tool listing", () => {
94
94
  expect(names).toContain("doctor");
95
95
  expect(names).toContain("info");
96
96
  expect(names).toContain("capabilities");
97
+ // Sample tools
98
+ expect(names).toContain("block_sample");
99
+ expect(names).toContain("standard_sample");
100
+ expect(names).toContain("token_sample");
101
+ expect(names).toContain("modifier_sample");
97
102
  });
98
103
 
99
104
  it("no tool names contain pragma_ prefix", async () => {
@@ -1020,13 +1025,16 @@ describe("info", () => {
1020
1025
  // =============================================================================
1021
1026
 
1022
1027
  describe("capabilities", () => {
1023
- it("returns accurate tool list and counts", async () => {
1028
+ it("returns enriched tool catalog with counts", async () => {
1024
1029
  const result = await client.callTool({
1025
1030
  name: "capabilities",
1026
1031
  arguments: {},
1027
1032
  });
1028
1033
  const data = parseData(result) as {
1029
- tools: string[];
1034
+ version: string;
1035
+ conventions: Record<string, string>;
1036
+ discovery_sequence: { stage: number; tool: string; purpose: string }[];
1037
+ tools: { name: string; category: string; use_when: string }[];
1030
1038
  counts: {
1031
1039
  total: number;
1032
1040
  read: number;
@@ -1034,14 +1042,21 @@ describe("capabilities", () => {
1034
1042
  orientation: number;
1035
1043
  diagnostic: number;
1036
1044
  };
1045
+ limits: Record<string, unknown>;
1037
1046
  };
1038
- expect(data.tools).toContain("block_list");
1039
- expect(data.tools).toContain("capabilities");
1040
- expect(data.counts.total).toBe(25);
1047
+ const toolNames = data.tools.map((t) => t.name);
1048
+ expect(toolNames).toContain("block_list");
1049
+ expect(toolNames).toContain("capabilities");
1050
+ expect(data.tools.every((t) => t.use_when.length > 0)).toBe(true);
1051
+ expect(data.counts.total).toBe(29);
1041
1052
  expect(data.counts.read).toBeGreaterThan(0);
1042
1053
  expect(data.counts.write).toBe(5);
1043
1054
  expect(data.counts.orientation).toBe(2);
1044
1055
  expect(data.counts.diagnostic).toBe(2);
1056
+ expect(data.version).toBeDefined();
1057
+ expect(data.conventions).toBeDefined();
1058
+ expect(data.discovery_sequence.length).toBe(3);
1059
+ expect(data.limits).toBeDefined();
1045
1060
  });
1046
1061
  });
1047
1062
 
@@ -7,6 +7,7 @@ const stubRuntime = {
7
7
  store: {} as PragmaRuntime["store"],
8
8
  config: { tier: undefined, channel: "normal" },
9
9
  cwd: "/test",
10
+ packages: [],
10
11
  dispose: () => {},
11
12
  } as PragmaRuntime;
12
13
 
@@ -4,8 +4,12 @@ import { commands as configCommands } from "../domains/config/index.js";
4
4
  import { commands as createCommands } from "../domains/create/index.js";
5
5
  import { doctorCommand } from "../domains/doctor/commands/index.js";
6
6
  import { commands as graphCommands } from "../domains/graph/index.js";
7
+ import { commands as graphqlCommands } from "../domains/graphql/index.js";
7
8
  import { infoCommand, upgradeCommand } from "../domains/info/index.js";
8
- import { buildLlmCommand } from "../domains/llm/index.js";
9
+ import {
10
+ buildCapabilitiesCommand,
11
+ buildLlmCommand,
12
+ } from "../domains/llm/index.js";
9
13
  import { commands as modifierCommands } from "../domains/modifier/index.js";
10
14
  import { commands as ontologyCommands } from "../domains/ontology/index.js";
11
15
  import { commands as setupCommands } from "../domains/setup/index.js";
@@ -38,10 +42,12 @@ export default function collectCommands(
38
42
  ...blockCommands(ctx),
39
43
  ...ontologyCommands(ctx),
40
44
  ...graphCommands(ctx),
45
+ ...graphqlCommands(),
41
46
  ...skillCommands(ctx),
42
47
  doctorCommand,
43
48
  infoCommand,
44
49
  upgradeCommand,
45
50
  buildLlmCommand(ctx),
51
+ buildCapabilitiesCommand(),
46
52
  ];
47
53
  }
@@ -43,6 +43,14 @@ function renderErrorPlain(error: PragmaError): string {
43
43
  lines.push(`Valid options: ${error.validOptions.join(", ")}`);
44
44
  }
45
45
 
46
+ if (error.crossDomain) {
47
+ lines.push("");
48
+ lines.push(
49
+ `"${error.entity?.name ?? ""}" exists as a ${error.crossDomain.entityType}.`,
50
+ );
51
+ lines.push(`Run \`${error.crossDomain.cli}\``);
52
+ }
53
+
46
54
  if (error.recovery) {
47
55
  lines.push("");
48
56
  if (error.recovery.cli) {
@@ -82,6 +90,12 @@ function renderErrorLlm(error: PragmaError): string {
82
90
  lines.push(`Valid options: ${error.validOptions.join(", ")}`);
83
91
  }
84
92
 
93
+ if (error.crossDomain) {
94
+ lines.push(
95
+ `Cross-domain: "${error.entity?.name ?? ""}" exists as a ${error.crossDomain.entityType}. \`${error.crossDomain.cli}\``,
96
+ );
97
+ }
98
+
85
99
  if (error.recovery) {
86
100
  if (error.recovery.cli) {
87
101
  lines.push(`Recovery: \`${error.recovery.cli}\``);
@@ -106,6 +120,7 @@ function renderErrorJson(error: PragmaError): string {
106
120
  entity: error.entity,
107
121
  suggestions: error.suggestions,
108
122
  recovery: error.recovery,
123
+ crossDomain: error.crossDomain,
109
124
  filters: error.filters,
110
125
  validOptions: error.validOptions,
111
126
  });
@@ -8,7 +8,14 @@
8
8
  import type { CommandKind } from "./types.js";
9
9
 
10
10
  /** Commands that do not require the ke store to be booted. */
11
- const STORE_SKIP_COMMANDS = new Set(["setup", "mcp", "update-refs"]);
11
+ const STORE_SKIP_COMMANDS = new Set([
12
+ "setup",
13
+ "mcp",
14
+ "update-refs",
15
+ "capabilities",
16
+ "trace",
17
+ "graphql",
18
+ ]);
12
19
 
13
20
  function findCommandArg(argv: readonly string[]): string | undefined {
14
21
  const args = argv.slice(2);
@@ -1,10 +1,13 @@
1
1
  import type { GlobalFlags } from "@canonical/cli-core";
2
2
  import { CommanderError } from "commander";
3
+ import { commands as graphqlCommands } from "../domains/graphql/index.js";
4
+ import { buildCapabilitiesCommand } from "../domains/llm/index.js";
3
5
  import { commands as refsCommands } from "../domains/refs/index.js";
4
6
  import { commands as setupCommands } from "../domains/setup/index.js";
5
7
  import type { PragmaContext } from "../domains/shared/context.js";
6
8
  import type { PragmaRuntime } from "../domains/shared/runtime.js";
7
9
  import { bootPragma } from "../domains/shared/runtime.js";
10
+ import { commands as traceCommands } from "../domains/trace/index.js";
8
11
  import { PragmaError } from "../error/index.js";
9
12
  import collectCommands from "./collectCommands.js";
10
13
  import createProgram from "./createProgram.js";
@@ -78,6 +81,7 @@ async function handleRootHelp(globalFlags: GlobalFlags): Promise<void> {
78
81
  store: {} as PragmaRuntime["store"],
79
82
  config: { tier: undefined, channel: "normal" },
80
83
  cwd: process.cwd(),
84
+ packages: [],
81
85
  dispose: () => {},
82
86
  globalFlags,
83
87
  interactive: runInteractiveCommand,
@@ -137,11 +141,18 @@ async function runStoreSkip(
137
141
  store: {} as PragmaRuntime["store"],
138
142
  config: { tier: undefined, channel: "normal" },
139
143
  cwd: process.cwd(),
144
+ packages: [],
140
145
  dispose: () => {},
141
146
  globalFlags,
142
147
  interactive: runInteractiveCommand,
143
148
  };
144
- const commands = [...setupCommands(), ...refsCommands()];
149
+ const commands = [
150
+ ...setupCommands(),
151
+ ...refsCommands(),
152
+ ...traceCommands(),
153
+ ...graphqlCommands(),
154
+ buildCapabilitiesCommand(),
155
+ ];
145
156
  const program = createProgram(commands, stubCtx);
146
157
 
147
158
  try {
@@ -37,6 +37,7 @@ export default async function createTestMcpClient(options?: {
37
37
  store,
38
38
  config,
39
39
  cwd,
40
+ packages: [],
40
41
  dispose: () => cleanupStore(),
41
42
  };
42
43
 
@@ -0,0 +1,72 @@
1
+ /**
2
+ * TTL fixtures for graphql domain tests.
3
+ *
4
+ * Three compile outcomes: a clean ontology (no diagnostics), an ontology
5
+ * with an unresolvable field-name collision (M001 error diagnostic, schema
6
+ * still composes), and an ontology whose reserved `__` field name fails
7
+ * schema composition entirely (C003 → CompilationError).
8
+ */
9
+
10
+ /** Namespace used by all graphql fixtures. */
11
+ export const EX_NAMESPACE = "http://example.org/";
12
+
13
+ /** `--prefix` entry registering {@link EX_NAMESPACE}. */
14
+ export const EX_PREFIX_ENTRY = `ex=${EX_NAMESPACE}`;
15
+
16
+ const prefixBlock = `@prefix ex: <${EX_NAMESPACE}> .
17
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
18
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
19
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .`;
20
+
21
+ /** Compiles to `type Thing` with zero diagnostics. */
22
+ export const GRAPHQL_CLEAN_TTL = `${prefixBlock}
23
+
24
+ ex:Thing a owl:Class ;
25
+ rdfs:label "Thing" .
26
+
27
+ ex:name a owl:DatatypeProperty ;
28
+ rdfs:domain ex:Thing ;
29
+ rdfs:range xsd:string ;
30
+ rdfs:label "name" .
31
+
32
+ ex:widget a ex:Thing ;
33
+ ex:name "Widget" .
34
+ `;
35
+
36
+ /**
37
+ * Produces an M001 error diagnostic (three properties collapse to
38
+ * `Thing.name` and the namespace-prefixed rename collides too), but the
39
+ * schema still composes — no CompilationError.
40
+ */
41
+ export const GRAPHQL_ERROR_TTL = `${prefixBlock}
42
+
43
+ ex:Thing a owl:Class ;
44
+ rdfs:label "Thing" .
45
+
46
+ ex:name a owl:DatatypeProperty ;
47
+ rdfs:domain ex:Thing ;
48
+ rdfs:range xsd:string .
49
+
50
+ ex:hasName a owl:DatatypeProperty ;
51
+ rdfs:domain ex:Thing ;
52
+ rdfs:range xsd:string .
53
+
54
+ ex:isName a owl:DatatypeProperty ;
55
+ rdfs:domain ex:Thing ;
56
+ rdfs:range xsd:string .
57
+ `;
58
+
59
+ /**
60
+ * Fails schema composition (C003): `__bad` survives name sanitization but
61
+ * `__` is reserved by GraphQL introspection, so compile() throws
62
+ * CompilationError.
63
+ */
64
+ export const GRAPHQL_FATAL_TTL = `${prefixBlock}
65
+
66
+ ex:Thing a owl:Class ;
67
+ rdfs:label "Thing" .
68
+
69
+ ex:__bad a owl:DatatypeProperty ;
70
+ rdfs:domain ex:Thing ;
71
+ rdfs:range xsd:string .
72
+ `;
@@ -3,6 +3,13 @@ export type { CommandResult } from "./cli.js";
3
3
  export { runCommand } from "./cli.js";
4
4
  export { default as createTestMcpClient } from "./createTestMcpClient.js";
5
5
  export { DS_ALL_TTL, DS_ONTOLOGY_TTL, DS_TIERS_TTL } from "./dsFixtures.js";
6
+ export {
7
+ EX_NAMESPACE,
8
+ EX_PREFIX_ENTRY,
9
+ GRAPHQL_CLEAN_TTL,
10
+ GRAPHQL_ERROR_TTL,
11
+ GRAPHQL_FATAL_TTL,
12
+ } from "./graphqlFixtures.js";
6
13
  export type { PragmaTestStoreOptions } from "./store.js";
7
14
  export { createTestStore } from "./store.js";
8
15
  export type { TestMcpClientResult } from "./types.js";
@@ -41,17 +41,23 @@ function parseEnvelope(
41
41
  // ---------------------------------------------------------------------------
42
42
 
43
43
  describe("capabilities", () => {
44
- it("returns tool list with counts", async () => {
44
+ it("returns enriched tool catalog with conventions", async () => {
45
45
  const res = await client.callTool({ name: "capabilities", arguments: {} });
46
46
  const body = parseEnvelope(res);
47
47
  expect(body.ok).toBe(true);
48
48
  const data = body.data as {
49
- tools: string[];
49
+ version: string;
50
+ conventions: Record<string, string>;
51
+ tools: { name: string; category: string; use_when: string }[];
50
52
  counts: Record<string, number>;
51
53
  };
52
- expect(data.tools).toContain("block_list");
53
- expect(data.tools).toContain("capabilities");
54
- expect(data.counts.total).toBe(25);
54
+ const toolNames = data.tools.map((t) => t.name);
55
+ expect(toolNames).toContain("block_list");
56
+ expect(toolNames).toContain("capabilities");
57
+ expect(data.tools.every((t) => t.use_when.length > 0)).toBe(true);
58
+ expect(data.counts.total).toBe(29);
59
+ expect(data.conventions).toBeDefined();
60
+ expect(data.version).toBeDefined();
55
61
  });
56
62
  });
57
63