@canonical/pragma-cli 0.20.0 → 0.22.0-experimental.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 (203) hide show
  1. package/dist/pragma +0 -0
  2. package/package.json +31 -18
  3. package/src/bin.test.ts +2 -0
  4. package/src/bin.ts +1 -0
  5. package/src/compile-validation.ts +3 -4
  6. package/src/constants.ts +1 -1
  7. package/src/domains/block/blockConfig.ts +47 -3
  8. package/src/domains/block/commands/list.test.ts +1 -1
  9. package/src/domains/block/commands/list.ts +27 -0
  10. package/src/domains/block/commands/lookup.ts +26 -52
  11. package/src/domains/block/formatters/index.ts +1 -1
  12. package/src/domains/block/formatters/list.test.ts +1 -1
  13. package/src/domains/block/formatters/list.ts +28 -42
  14. package/src/domains/block/formatters/lookup.test.ts +3 -2
  15. package/src/domains/block/formatters/lookup.ts +91 -296
  16. package/src/domains/block/formatters/types.ts +1 -1
  17. package/src/domains/block/operations/list.test.ts +1 -1
  18. package/src/domains/block/operations/list.ts +1 -1
  19. package/src/domains/block/operations/lookup.test.ts +29 -8
  20. package/src/domains/block/operations/lookup.ts +94 -16
  21. package/src/domains/block/orchestration/blockEmptyError.ts +1 -1
  22. package/src/domains/block/orchestration/buildBlockFilters.ts +1 -1
  23. package/src/domains/block/orchestration/enrichBlocks.ts +6 -2
  24. package/src/domains/block/orchestration/resolveBlockList.ts +1 -1
  25. package/src/domains/block/orchestration/resolveBlockLookup.ts +8 -2
  26. package/src/domains/block/types.ts +1 -1
  27. package/src/domains/config/commands/channel.ts +6 -4
  28. package/src/domains/config/commands/tier.ts +6 -4
  29. package/src/domains/config/mcp/specs.ts +18 -6
  30. package/src/domains/config/operations/validateTier.ts +1 -1
  31. package/src/domains/config/tasks/index.ts +3 -0
  32. package/src/domains/config/tasks/setChannel.ts +30 -0
  33. package/src/domains/config/tasks/setTier.ts +33 -0
  34. package/src/domains/create/commands/package.test.ts +2 -2
  35. package/src/domains/doctor/mcp/index.ts +1 -1
  36. package/src/domains/doctor/mcp/specs.ts +2 -49
  37. package/src/domains/graph/commands/inspect.test.ts +1 -1
  38. package/src/domains/graph/formatters/inspect.ts +12 -7
  39. package/src/domains/graph/operations/inspectUri.ts +4 -1
  40. package/src/domains/info/commands/upgrade.ts +6 -3
  41. package/src/domains/info/index.ts +3 -1
  42. package/src/domains/info/mcp/index.ts +2 -0
  43. package/src/domains/info/mcp/specs.ts +65 -0
  44. package/src/domains/llm/data/commandReference.ts +4 -4
  45. package/src/domains/llm/data/decisionTrees.ts +14 -12
  46. package/src/domains/llm/operations/collectContext.ts +1 -1
  47. package/src/domains/modifier/commands/list.ts +9 -0
  48. package/src/domains/modifier/commands/lookup.ts +22 -42
  49. package/src/domains/modifier/formatters/index.ts +4 -1
  50. package/src/domains/modifier/formatters/list.test.ts +1 -1
  51. package/src/domains/modifier/formatters/list.ts +13 -26
  52. package/src/domains/modifier/formatters/lookup.test.ts +1 -1
  53. package/src/domains/modifier/formatters/lookup.ts +41 -29
  54. package/src/domains/modifier/index.ts +1 -0
  55. package/src/domains/modifier/modifierConfig.ts +36 -0
  56. package/src/domains/modifier/operations/list.ts +1 -1
  57. package/src/domains/modifier/operations/lookup.ts +4 -3
  58. package/src/domains/modifier/orchestration/resolveModifierLookup.ts +1 -1
  59. package/src/domains/modifier/types.ts +1 -1
  60. package/src/domains/ontology/commands/list.test.ts +1 -1
  61. package/src/domains/ontology/commands/list.ts +18 -0
  62. package/src/domains/ontology/commands/show.test.ts +1 -1
  63. package/src/domains/ontology/formatters/list.ts +5 -3
  64. package/src/domains/ontology/formatters/show.ts +38 -13
  65. package/src/domains/ontology/helpers/queryClasses.ts +1 -1
  66. package/src/domains/ontology/helpers/queryProperties.ts +1 -1
  67. package/src/domains/ontology/operations/listOntologies.ts +1 -1
  68. package/src/domains/ontology/operations/showOntology.ts +1 -1
  69. package/src/domains/setup/helpers/completionScripts.test.ts +3 -3
  70. package/src/domains/setup/helpers/completionScripts.ts +4 -4
  71. package/src/domains/shared/ToolSpec.ts +1 -1
  72. package/src/domains/shared/context.ts +3 -2
  73. package/src/domains/shared/contracts.ts +40 -1
  74. package/src/domains/shared/filters/buildFilterConfig.ts +1 -2
  75. package/src/domains/shared/filters/buildFilters.ts +1 -1
  76. package/src/domains/shared/filters/describeFilters.ts +1 -1
  77. package/src/domains/shared/formatters.ts +47 -0
  78. package/src/domains/shared/renderers.ts +410 -0
  79. package/src/domains/shared/runtime.ts +7 -22
  80. package/src/domains/shared/types/block.ts +107 -0
  81. package/src/domains/shared/types/common.ts +33 -0
  82. package/src/domains/shared/types/graph.ts +17 -0
  83. package/src/domains/shared/types/index.ts +49 -0
  84. package/src/domains/shared/types/modifier.ts +22 -0
  85. package/src/domains/shared/types/ontology.ts +50 -0
  86. package/src/domains/shared/types/runtime.ts +21 -0
  87. package/src/domains/shared/types/standard.ts +64 -0
  88. package/src/domains/shared/types/tier.ts +15 -0
  89. package/src/domains/shared/types/token.ts +30 -0
  90. package/src/domains/standard/commands/list.ts +9 -0
  91. package/src/domains/standard/commands/lookup.ts +22 -50
  92. package/src/domains/standard/formatters/categories.test.ts +1 -1
  93. package/src/domains/standard/formatters/categories.ts +1 -1
  94. package/src/domains/standard/formatters/index.ts +4 -1
  95. package/src/domains/standard/formatters/list.test.ts +4 -1
  96. package/src/domains/standard/formatters/list.ts +112 -84
  97. package/src/domains/standard/formatters/lookup.test.ts +1 -1
  98. package/src/domains/standard/formatters/lookup.ts +85 -70
  99. package/src/domains/standard/formatters/types.ts +1 -1
  100. package/src/domains/standard/index.ts +1 -0
  101. package/src/domains/standard/operations/categories.ts +1 -1
  102. package/src/domains/standard/operations/list.ts +1 -1
  103. package/src/domains/standard/operations/lookup.ts +1 -1
  104. package/src/domains/standard/orchestration/buildStandardFilters.ts +1 -1
  105. package/src/domains/standard/orchestration/resolveStandardList.ts +4 -1
  106. package/src/domains/standard/orchestration/resolveStandardLookup.ts +1 -1
  107. package/src/domains/standard/orchestration/standardEmptyError.ts +1 -1
  108. package/src/domains/standard/standardConfig.ts +42 -0
  109. package/src/domains/standard/types.ts +1 -1
  110. package/src/domains/tier/commands/list.ts +16 -0
  111. package/src/domains/tier/formatters/list.test.ts +1 -1
  112. package/src/domains/tier/formatters/list.ts +11 -6
  113. package/src/domains/tier/operations/list.ts +1 -1
  114. package/src/domains/token/commands/list.ts +9 -0
  115. package/src/domains/token/commands/lookup.ts +22 -48
  116. package/src/domains/token/formatters/index.ts +4 -1
  117. package/src/domains/token/formatters/list.test.ts +1 -1
  118. package/src/domains/token/formatters/list.ts +15 -28
  119. package/src/domains/token/formatters/lookup.test.ts +2 -2
  120. package/src/domains/token/formatters/lookup.ts +80 -45
  121. package/src/domains/token/index.ts +1 -0
  122. package/src/domains/token/operations/list.ts +1 -1
  123. package/src/domains/token/operations/lookup.ts +1 -1
  124. package/src/domains/token/orchestration/resolveTokenLookup.ts +1 -1
  125. package/src/domains/token/tokenConfig.ts +38 -0
  126. package/src/domains/token/types.ts +1 -1
  127. package/src/embedWasm.ts +42 -0
  128. package/src/index.ts +1 -1
  129. package/src/mcp/runMcpServer.test.ts +2 -2
  130. package/src/mcp/tools/index.ts +2 -0
  131. package/src/mcp/tools/registerTools.test.ts +8 -3
  132. package/src/package-manager/types.ts +1 -3
  133. package/src/pipeline/createProgram.ts +20 -1
  134. package/src/pipeline/index.ts +4 -1
  135. package/src/pipeline/parseGlobalFlags.test.ts +92 -0
  136. package/src/pipeline/parseGlobalFlags.ts +25 -0
  137. package/src/pipeline/runCli.orchestration.test.ts +1 -0
  138. package/src/pipeline/runCli.ts +11 -4
  139. package/src/pipeline/runInteractiveCommand.ts +198 -0
  140. package/src/testing/cli.ts +2 -2
  141. package/src/testing/fixtures.ts +2 -2
  142. package/src/testing/integration/boot.test.ts +2 -2
  143. package/src/testing/integration/cli-user-stories.test.ts +198 -0
  144. package/src/testing/integration/edge-cases.test.ts +263 -0
  145. package/src/testing/integration/parity.test.ts +377 -27
  146. package/src/testing/store.ts +1 -1
  147. package/src/tui/components/BulletList/BulletList.tsx +46 -0
  148. package/src/tui/components/BulletList/index.ts +2 -0
  149. package/src/tui/components/BulletList/types.ts +6 -0
  150. package/src/tui/components/Card/Card.tsx +73 -0
  151. package/src/tui/components/Card/index.ts +1 -0
  152. package/src/tui/components/Card/types.ts +12 -0
  153. package/src/tui/components/CodeBlock/CodeBlock.tsx +37 -0
  154. package/src/tui/components/CodeBlock/index.ts +2 -0
  155. package/src/tui/components/CodeBlock/types.ts +6 -0
  156. package/src/tui/components/DataRow/DataRow.tsx +80 -0
  157. package/src/tui/components/DataRow/index.ts +2 -0
  158. package/src/tui/components/DataRow/types.ts +14 -0
  159. package/src/tui/components/FieldRow/FieldRow.tsx +24 -0
  160. package/src/tui/components/FieldRow/index.ts +2 -0
  161. package/src/tui/components/FieldRow/types.ts +10 -0
  162. package/src/tui/components/HeaderRow/HeaderRow.tsx +32 -0
  163. package/src/tui/components/HeaderRow/index.ts +2 -0
  164. package/src/tui/components/HeaderRow/types.ts +8 -0
  165. package/src/tui/components/MarkdownText/MarkdownText.tsx +19 -0
  166. package/src/tui/components/MarkdownText/index.ts +2 -0
  167. package/src/tui/components/MarkdownText/types.ts +4 -0
  168. package/src/tui/components/MiniTable/MiniTable.tsx +82 -0
  169. package/src/tui/components/MiniTable/index.ts +2 -0
  170. package/src/tui/components/MiniTable/types.ts +6 -0
  171. package/src/tui/components/SectionRenderer/SectionRenderer.tsx +91 -0
  172. package/src/tui/components/SectionRenderer/index.ts +2 -0
  173. package/src/tui/components/SectionRenderer/types.ts +15 -0
  174. package/src/tui/components/TreeView/TreeView.tsx +84 -0
  175. package/src/tui/components/TreeView/index.ts +2 -0
  176. package/src/tui/components/TreeView/types.ts +6 -0
  177. package/src/tui/components/index.ts +19 -0
  178. package/src/tui/constants.ts +39 -0
  179. package/src/tui/createListView.ts +23 -0
  180. package/src/tui/createLookupView.ts +26 -0
  181. package/src/tui/helpers/computeWidths.ts +53 -0
  182. package/src/tui/helpers/fitColumns.ts +33 -0
  183. package/src/tui/helpers/formatMarkdown.ts +19 -0
  184. package/src/tui/helpers/getTerminalSize.ts +16 -0
  185. package/src/tui/helpers/index.ts +6 -0
  186. package/src/tui/helpers/syntaxColor.ts +67 -0
  187. package/src/tui/helpers/truncateText.ts +12 -0
  188. package/src/tui/hooks/index.ts +3 -0
  189. package/src/tui/hooks/types.ts +7 -0
  190. package/src/tui/hooks/useAutoExit.ts +17 -0
  191. package/src/tui/hooks/useTerminalSize.ts +19 -0
  192. package/src/tui/index.ts +15 -0
  193. package/src/tui/renderInk.tsx +18 -0
  194. package/src/tui/types.ts +12 -0
  195. package/src/tui/views/ListView/ListView.tsx +104 -0
  196. package/src/tui/views/ListView/index.ts +2 -0
  197. package/src/tui/views/ListView/types.ts +14 -0
  198. package/src/tui/views/LookupView/LookupView.tsx +244 -0
  199. package/src/tui/views/LookupView/index.ts +2 -0
  200. package/src/tui/views/LookupView/types.ts +16 -0
  201. package/src/tui/views/index.ts +4 -0
  202. package/src/domains/shared/types.test-d.ts +0 -127
  203. package/src/domains/shared/types.ts +0 -348
package/dist/pragma ADDED
Binary file
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@canonical/pragma-cli",
3
3
  "description": "CLI and MCP server for Canonical's design system.",
4
- "version": "0.20.0",
4
+ "version": "0.22.0-experimental.0",
5
5
  "type": "module",
6
6
  "module": "src/index.ts",
7
7
  "types": "src/index.ts",
8
8
  "bin": {
9
- "pragma": "src/bin.ts"
9
+ "pragma": "dist/pragma"
10
10
  },
11
11
  "files": [
12
- "src"
12
+ "src",
13
+ "dist"
13
14
  ],
14
15
  "author": {
15
16
  "email": "webteam@canonical.com",
@@ -19,6 +20,12 @@
19
20
  "type": "git",
20
21
  "url": "https://github.com/canonical/pragma"
21
22
  },
23
+ "os": [
24
+ "linux"
25
+ ],
26
+ "cpu": [
27
+ "x64"
28
+ ],
22
29
  "license": "GPL-3.0",
23
30
  "bugs": {
24
31
  "url": "https://github.com/canonical/pragma/issues"
@@ -30,11 +37,13 @@
30
37
  "#error": "./src/error/index.ts",
31
38
  "#package-manager": "./src/package-manager/index.ts",
32
39
  "#pipeline": "./src/pipeline/index.ts",
33
- "#testing": "./src/testing/index.ts"
40
+ "#testing": "./src/testing/index.ts",
41
+ "#tui": "./src/tui/index.ts"
34
42
  },
35
43
  "scripts": {
36
- "build": "echo 'No build needed - runs directly from TypeScript'",
37
- "build:compile": "bun build --compile --minify src/bin.ts --outfile dist/pragma",
44
+ "build": "bun build --compile --minify --target=bun-linux-x64 src/bin.ts --outfile dist/pragma",
45
+ "build:compile": "bun build --compile --minify --target=bun-linux-x64 src/bin.ts --outfile dist/pragma",
46
+ "prepublishOnly": "bun run build",
38
47
  "check": "bun run check:biome && bun run check:ts && bun run check:webarchitect",
39
48
  "check:webarchitect": "webarchitect tool-ts",
40
49
  "check:fix": "bun run check:biome:fix && bun run check:ts",
@@ -47,12 +56,13 @@
47
56
  "test:compile": "bun run src/compile-validation.runner.ts"
48
57
  },
49
58
  "devDependencies": {
50
- "@biomejs/biome": "2.4.6",
51
- "@canonical/biome-config": "^0.20.0",
52
- "@canonical/typescript-config": "^0.20.0",
53
- "@canonical/webarchitect": "^0.20.0",
59
+ "@biomejs/biome": "2.4.9",
60
+ "@canonical/biome-config": "^0.21.0",
61
+ "@canonical/typescript-config": "^0.21.0",
62
+ "@canonical/webarchitect": "^0.21.0",
54
63
  "@types/bun": "^1.3.10",
55
64
  "@types/node": "^24.12.0",
65
+ "@types/react": "^19.2.14",
56
66
  "expect-type": "^1.3.0",
57
67
  "typescript": "^5.9.3",
58
68
  "vitest": "^4.0.18"
@@ -63,17 +73,20 @@
63
73
  "@canonical/design-system": "^0.1.2"
64
74
  },
65
75
  "dependencies": {
66
- "@canonical/cli-core": "^0.20.0",
67
- "@canonical/harnesses": "^0.20.0",
68
- "@canonical/ke": "^0.20.0",
69
- "@canonical/summon-component": "^0.20.0",
70
- "@canonical/summon-core": "^0.20.0",
71
- "@canonical/summon-package": "^0.20.0",
72
- "@canonical/task": "^0.20.0",
76
+ "@canonical/cli-core": "^0.21.0",
77
+ "@canonical/harnesses": "^0.21.0",
78
+ "@canonical/ke": "^0.21.0",
79
+ "@canonical/summon-component": "^0.21.0",
80
+ "@canonical/summon-core": "^0.21.0",
81
+ "@canonical/summon-package": "^0.21.0",
82
+ "@canonical/task": "^0.21.0",
73
83
  "@modelcontextprotocol/sdk": "^1.12.0",
74
84
  "chalk": "^5.6.2",
75
85
  "commander": "^14.0.3",
86
+ "ink": "^6.8.0",
87
+ "oxigraph": "^0.5.0",
88
+ "react": "^19.2.4",
76
89
  "zod": "^3.25.0"
77
90
  },
78
- "gitHead": "1aee634b278922f2aa1cfe2a11d3b14e64280905"
91
+ "gitHead": "8a6b554ad6058d90ae78892d6b2a2f6780d8bb0c"
79
92
  }
package/src/bin.test.ts CHANGED
@@ -12,6 +12,8 @@ vi.mock("./pipeline/runCli.js", () => ({
12
12
  default: runCliMock,
13
13
  }));
14
14
 
15
+ vi.mock("./embedWasm.js", () => ({}));
16
+
15
17
  describe("bin", () => {
16
18
  const originalArgv = process.argv;
17
19
 
package/src/bin.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  * @note Impure
9
9
  */
10
10
 
11
+ import "./embedWasm.js";
11
12
  import { detectLocalInstall } from "./package-manager/index.js";
12
13
  import runCli from "./pipeline/runCli.js";
13
14
 
@@ -6,7 +6,7 @@
6
6
  * and queries correctly when embedded in a compiled Bun binary.
7
7
  *
8
8
  * Tests exercised:
9
- * 1. Dynamic import of @canonical/ke (triggers WASM loading)
9
+ * 1. Static import of @canonical/ke (triggers WASM loading)
10
10
  * 2. createStore() with file sources
11
11
  * 3. SPARQL SELECT query with prefix expansion
12
12
  * 4. sparql tagged template with branded URI interpolation
@@ -18,9 +18,11 @@
18
18
  * It is a build validation program, not a library module.
19
19
  */
20
20
 
21
+ import "./embedWasm.js";
21
22
  import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
22
23
  import { tmpdir } from "node:os";
23
24
  import { join } from "node:path";
25
+ import { createNamespace, createStore, sparql } from "@canonical/ke";
24
26
 
25
27
  async function run() {
26
28
  const start = performance.now();
@@ -58,9 +60,6 @@ ex:charlie a schema:Person ;
58
60
  // --- 1. Load ke (triggers WASM) ---
59
61
  console.log("[validate] Loading @canonical/ke (triggers WASM)...");
60
62
  const wasmStart = performance.now();
61
- const { createStore, sparql, createNamespace } = await import(
62
- "@canonical/ke"
63
- );
64
63
  console.log(
65
64
  `[validate] WASM loaded in ${(performance.now() - wasmStart).toFixed(1)}ms`,
66
65
  );
package/src/constants.ts CHANGED
@@ -68,6 +68,7 @@ const PROPERTY_MAP: Record<string, OntologyPropertyMap> = {
68
68
  },
69
69
  };
70
70
 
71
+ export type { Channel, OntologyPropertyMap };
71
72
  export {
72
73
  PROGRAM_DESCRIPTION,
73
74
  PROGRAM_NAME,
@@ -75,4 +76,3 @@ export {
75
76
  VALID_CHANNELS,
76
77
  VERSION,
77
78
  };
78
- export type { Channel, OntologyPropertyMap };
@@ -5,8 +5,9 @@ import type {
5
5
  BlockDigest,
6
6
  BlockSummary,
7
7
  FilterConfig,
8
- } from "../shared/types.js";
8
+ } from "../shared/types/index.js";
9
9
 
10
+ /** Display-contract configuration for block entities (components, patterns, layouts). */
10
11
  export const blockConfig: EntityDisplayConfig<
11
12
  BlockSummary,
12
13
  BlockDigest,
@@ -25,6 +26,48 @@ export const blockConfig: EntityDisplayConfig<
25
26
  { key: "name", label: "Name" },
26
27
  { key: "type", label: "Type" },
27
28
  { key: "tier", label: "Tier" },
29
+ {
30
+ key: "modifiers",
31
+ label: "Modifiers",
32
+ showWhenEmpty: false,
33
+ format: (value) =>
34
+ Array.isArray(value)
35
+ ? value.filter((entry) => typeof entry === "string").join(", ")
36
+ : "",
37
+ },
38
+ {
39
+ key: "implementations",
40
+ label: "Implementations",
41
+ showWhenEmpty: false,
42
+ format: (value) =>
43
+ Array.isArray(value)
44
+ ? value
45
+ .filter(
46
+ (entry): entry is { framework: string; available: boolean } =>
47
+ typeof entry === "object" &&
48
+ entry !== null &&
49
+ "framework" in entry &&
50
+ "available" in entry,
51
+ )
52
+ .filter((entry) => entry.available)
53
+ .map((entry) => entry.framework)
54
+ .join(", ")
55
+ : "",
56
+ },
57
+ {
58
+ key: "nodeCount",
59
+ label: "Nodes",
60
+ showWhenEmpty: false,
61
+ format: (value) =>
62
+ typeof value === "number" && value > 0 ? String(value) : "",
63
+ },
64
+ {
65
+ key: "tokenCount",
66
+ label: "Tokens",
67
+ showWhenEmpty: false,
68
+ format: (value) =>
69
+ typeof value === "number" && value > 0 ? String(value) : "",
70
+ },
28
71
  ],
29
72
  digestColumns: [{ key: "summary", label: "Summary", showWhenEmpty: false }],
30
73
  lookupSections: [
@@ -32,11 +75,12 @@ export const blockConfig: EntityDisplayConfig<
32
75
  { key: "whenToUse", heading: "When to use", kind: "field" },
33
76
  { key: "whenNotToUse", heading: "When not to use", kind: "field" },
34
77
  { key: "guidelines", heading: "Guidelines", kind: "field" },
78
+ { key: "anatomy", heading: "Anatomy", kind: "tree" },
35
79
  { key: "anatomyDsl", heading: "Anatomy (DSL)", kind: "code" },
36
80
  { key: "anatomyClassic", heading: "Anatomy (classic)", kind: "field" },
37
- { key: "modifierFamilies", heading: "Modifier Families", kind: "table" },
81
+ { key: "modifierFamilies", heading: "Modifiers", kind: "table" },
38
82
  { key: "properties", heading: "Properties", kind: "nested-table" },
39
- { key: "subcomponents", heading: "Subcomponents", kind: "list" },
83
+ { key: "subcomponents", heading: "Subcomponents", kind: "tree" },
40
84
  { key: "implementationPaths", heading: "Implementations", kind: "list" },
41
85
  { key: "tokens", heading: "Tokens", kind: "list" },
42
86
  ],
@@ -3,7 +3,7 @@ import type { Store } from "@canonical/ke";
3
3
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
4
4
  import { createTestStore, DS_ALL_TTL } from "#testing";
5
5
  import type { PragmaContext } from "../../shared/context.js";
6
- import type { BlockSummary } from "../../shared/types.js";
6
+ import type { BlockSummary } from "../../shared/types/index.js";
7
7
  import buildListCommand from "./list.js";
8
8
 
9
9
  let store: Store;
@@ -11,8 +11,11 @@ import {
11
11
  type CommandResult,
12
12
  createOutputResult,
13
13
  } from "@canonical/cli-core";
14
+ import { createListView } from "#tui";
14
15
  import type { PragmaContext } from "../../shared/context.js";
16
+ import type { ColumnDef } from "../../shared/contracts.js";
15
17
  import { selectFormatter } from "../../shared/formatters.js";
18
+ import { blockConfig } from "../blockConfig.js";
16
19
  import { listFormatters } from "../formatters/index.js";
17
20
  import { resolveBlockList } from "../orchestration/index.js";
18
21
 
@@ -63,7 +66,31 @@ export default function buildListCommand(
63
66
 
64
67
  return createOutputResult(contract.result.items, {
65
68
  plain: selectFormatter(ctx, listFormatters),
69
+ ink: (data) =>
70
+ createListView({
71
+ heading: "Blocks",
72
+ domain: "block",
73
+ items: data,
74
+ columns: reorderColumns(blockConfig.listColumns, [
75
+ "name",
76
+ "type",
77
+ "tier",
78
+ "modifiers",
79
+ "uri",
80
+ ]),
81
+ }),
66
82
  });
67
83
  },
68
84
  };
69
85
  }
86
+
87
+ function reorderColumns<T>(
88
+ columns: readonly ColumnDef<T>[],
89
+ order: readonly string[],
90
+ ): readonly ColumnDef<T>[] {
91
+ const byKey = new Map(columns.map((col) => [col.key, col]));
92
+ return order.flatMap((key) => {
93
+ const col = byKey.get(key as keyof T & string);
94
+ return col ? [col] : [];
95
+ });
96
+ }
@@ -12,11 +12,14 @@ import {
12
12
  createOutputResult,
13
13
  } from "@canonical/cli-core";
14
14
  import { PragmaError } from "#error";
15
+ import { createLookupView } from "#tui";
15
16
  import type { PragmaContext } from "../../shared/context.js";
16
17
  import type { LookupResult } from "../../shared/contracts.js";
17
- import { lookupFormatters } from "../formatters/index.js";
18
+ import { renderLookupResults } from "../../shared/formatters.js";
19
+ import type { BlockDetailed } from "../../shared/types/index.js";
20
+ import { createLookupOptions, lookupFormatters } from "../formatters/index.js";
18
21
  import { resolveAspects } from "../helpers/index.js";
19
- import { listBlocks, type lookupBlock } from "../operations/index.js";
22
+ import { listBlocks } from "../operations/index.js";
20
23
  import {
21
24
  buildBlockFilters,
22
25
  resolveBlockLookup,
@@ -24,7 +27,7 @@ import {
24
27
  import type { AspectFlags } from "../types.js";
25
28
 
26
29
  interface BlockLookupOutput {
27
- readonly result: LookupResult<Awaited<ReturnType<typeof lookupBlock>>>;
30
+ readonly result: LookupResult<BlockDetailed>;
28
31
  readonly detailed: boolean;
29
32
  readonly aspects: AspectFlags;
30
33
  }
@@ -132,7 +135,26 @@ export default function buildLookupCommand(
132
135
 
133
136
  return createOutputResult<BlockLookupOutput>(
134
137
  { result: contract.result, detailed: showDetailed, aspects },
135
- { plain: renderBlockLookupOutput(ctx) },
138
+ {
139
+ plain: ({ result, detailed: isDetailed, aspects: selectedAspects }) =>
140
+ renderLookupResults({
141
+ ctx,
142
+ result,
143
+ formatters: lookupFormatters,
144
+ mapResult: (block) => ({
145
+ block,
146
+ detailed: isDetailed,
147
+ aspects: selectedAspects,
148
+ }),
149
+ }),
150
+ ink: ({ result, detailed: isDetailed, aspects: selectedAspects }) =>
151
+ createLookupView({
152
+ results: result.results,
153
+ errors: result.errors,
154
+ domain: "block",
155
+ options: createLookupOptions(isDetailed, selectedAspects),
156
+ }),
157
+ },
136
158
  );
137
159
  },
138
160
  };
@@ -149,51 +171,3 @@ function normalizeNames(names: unknown, legacyName?: unknown): string[] {
149
171
  }
150
172
  return [];
151
173
  }
152
-
153
- function renderBlockLookupOutput(
154
- ctx: PragmaContext,
155
- ): (data: BlockLookupOutput) => string {
156
- return ({ result, detailed, aspects }) => {
157
- const formatOne =
158
- ctx.globalFlags.format === "json"
159
- ? lookupFormatters.json
160
- : ctx.globalFlags.llm
161
- ? lookupFormatters.llm
162
- : lookupFormatters.plain;
163
-
164
- if (ctx.globalFlags.format === "json") {
165
- if (result.results.length === 1 && result.errors.length === 0) {
166
- const only = result.results[0];
167
- return only
168
- ? lookupFormatters.json({ block: only, detailed, aspects })
169
- : JSON.stringify({ results: [], errors: result.errors }, null, 2);
170
- }
171
-
172
- return JSON.stringify(
173
- {
174
- results: result.results.map((block) =>
175
- JSON.parse(lookupFormatters.json({ block, detailed, aspects })),
176
- ),
177
- errors: result.errors,
178
- },
179
- null,
180
- 2,
181
- );
182
- }
183
-
184
- const parts = result.results.map((block) =>
185
- formatOne({ block, detailed, aspects }),
186
- );
187
-
188
- if (result.errors.length > 0) {
189
- parts.push(
190
- [
191
- "Errors:",
192
- ...result.errors.map((error) => `- ${error.query}: ${error.message}`),
193
- ].join("\n"),
194
- );
195
- }
196
-
197
- return parts.join("\n\n");
198
- };
199
- }
@@ -1,4 +1,4 @@
1
1
  /** @module Re-exports block formatters (`lookupFormatters`, `listFormatters`). */
2
2
 
3
3
  export { default as listFormatters } from "./list.js";
4
- export { default as lookupFormatters } from "./lookup.js";
4
+ export { createLookupOptions, default as lookupFormatters } from "./lookup.js";
@@ -1,7 +1,7 @@
1
1
  import type { URI } from "@canonical/ke";
2
2
  import { describe, expect, it } from "vitest";
3
3
  import { PREFIX_MAP } from "../../shared/prefixes.js";
4
- import type { BlockSummary } from "../../shared/types.js";
4
+ import type { BlockSummary } from "../../shared/types/index.js";
5
5
  import formatters from "./list.js";
6
6
 
7
7
  const BUTTON: BlockSummary = {
@@ -1,55 +1,41 @@
1
- /**
2
- * Three-mode formatter for `pragma block list` output.
3
- *
4
- * - **plain** — styled terminal output with chalk; one line per block
5
- * showing name, tier, modifiers, and available implementations.
6
- * - **llm** — condensed Markdown consumed by LLM agents and reused
7
- * by the MCP adapter when `condensed: true`.
8
- * - **json** — structured JSON array for programmatic consumption.
9
- */
10
-
11
- import chalk from "chalk";
12
1
  import type { Formatters } from "../../shared/formatters.js";
13
- import type { BlockSummary } from "../../shared/types.js";
2
+ import { renderListPlain } from "../../shared/renderers.js";
3
+ import type { BlockSummary } from "../../shared/types/index.js";
4
+ import { blockConfig } from "../blockConfig.js";
14
5
 
6
+ /** Three-mode formatter for `pragma block list` output. */
15
7
  const formatters: Formatters<readonly BlockSummary[]> = {
16
- plain(components) {
17
- const lines: string[] = [];
18
-
19
- for (const c of components) {
20
- const tier = chalk.dim(`[${c.tier}]`);
21
- const mods =
22
- c.modifiers.length > 0 ? chalk.dim(` (${c.modifiers.join(", ")})`) : "";
23
- const impl = c.implementations
24
- .filter((i) => i.available)
25
- .map((i) => i.framework);
26
- const implStr = impl.length > 0 ? chalk.dim(` → ${impl.join(", ")}`) : "";
8
+ plain: (blocks) =>
9
+ renderListPlain(blocks, {
10
+ heading: "Blocks",
11
+ columns: blockConfig.listColumns,
12
+ }),
27
13
 
28
- lines.push(`${chalk.bold(c.name)} ${tier}${mods}${implStr}`);
29
- }
14
+ llm(blocks) {
15
+ const lines = ["## Blocks", ""];
30
16
 
31
- return lines.join("\n");
32
- },
17
+ for (const block of blocks) {
18
+ const parts = [`**${block.name}**`, `tier: ${block.tier}`];
33
19
 
34
- llm(components) {
35
- const lines: string[] = [];
20
+ if (block.modifiers.length > 0) {
21
+ parts.push(`modifiers: ${block.modifiers.join(", ")}`);
22
+ }
36
23
 
37
- lines.push("## Blocks");
38
- lines.push("");
24
+ const implementations = block.implementations
25
+ .filter((implementation) => implementation.available)
26
+ .map((implementation) => implementation.framework);
27
+ if (implementations.length > 0) {
28
+ parts.push(`implementations: ${implementations.join(", ")}`);
29
+ }
39
30
 
40
- for (const c of components) {
41
- const parts = [`**${c.name}**`, `tier: ${c.tier}`];
42
- if (c.modifiers.length > 0) {
43
- parts.push(`modifiers: ${c.modifiers.join(", ")}`);
31
+ if (block.nodeCount > 0) {
32
+ parts.push(`nodes: ${block.nodeCount}`);
44
33
  }
45
- const impl = c.implementations
46
- .filter((i) => i.available)
47
- .map((i) => i.framework);
48
- if (impl.length > 0) {
49
- parts.push(`implementations: ${impl.join(", ")}`);
34
+
35
+ if (block.tokenCount > 0) {
36
+ parts.push(`tokens: ${block.tokenCount}`);
50
37
  }
51
- if (c.nodeCount > 0) parts.push(`nodes: ${c.nodeCount}`);
52
- if (c.tokenCount > 0) parts.push(`tokens: ${c.tokenCount}`);
38
+
53
39
  lines.push(`- ${parts.join(" | ")}`);
54
40
  }
55
41
 
@@ -1,7 +1,7 @@
1
1
  import type { URI } from "@canonical/ke";
2
2
  import { describe, expect, it } from "vitest";
3
3
  import { PREFIX_MAP } from "../../shared/prefixes.js";
4
- import type { BlockDetailed } from "../../shared/types.js";
4
+ import type { BlockDetailed } from "../../shared/types/index.js";
5
5
  import type { AspectFlags } from "../types.js";
6
6
  import formatters from "./lookup.js";
7
7
 
@@ -263,7 +263,8 @@ describe("formatters.llm", () => {
263
263
  });
264
264
  expect(text).toContain("### Modifiers");
265
265
  expect(text).toContain("### Tokens");
266
- expect(text).not.toContain("### Anatomy");
266
+ expect(text).toContain("### Anatomy (DSL)");
267
+ expect(text).not.toContain("### Anatomy\n");
267
268
  });
268
269
  });
269
270