@canonical/pragma-cli 0.18.0 → 0.21.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.
- package/LICENSE +166 -0
- package/dist/pragma +0 -0
- package/package.json +31 -18
- package/src/constants.ts +1 -1
- package/src/domains/block/blockConfig.ts +47 -3
- package/src/domains/block/commands/list.test.ts +1 -1
- package/src/domains/block/commands/list.ts +27 -0
- package/src/domains/block/commands/lookup.ts +26 -52
- package/src/domains/block/formatters/index.ts +1 -1
- package/src/domains/block/formatters/list.test.ts +1 -1
- package/src/domains/block/formatters/list.ts +28 -42
- package/src/domains/block/formatters/lookup.test.ts +3 -2
- package/src/domains/block/formatters/lookup.ts +91 -296
- package/src/domains/block/formatters/types.ts +1 -1
- package/src/domains/block/operations/list.test.ts +1 -1
- package/src/domains/block/operations/list.ts +1 -1
- package/src/domains/block/operations/lookup.test.ts +29 -8
- package/src/domains/block/operations/lookup.ts +94 -16
- package/src/domains/block/orchestration/blockEmptyError.ts +1 -1
- package/src/domains/block/orchestration/buildBlockFilters.ts +1 -1
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -2
- package/src/domains/block/orchestration/resolveBlockList.ts +1 -1
- package/src/domains/block/orchestration/resolveBlockLookup.ts +8 -2
- package/src/domains/block/types.ts +1 -1
- package/src/domains/config/commands/channel.ts +6 -4
- package/src/domains/config/commands/tier.ts +6 -4
- package/src/domains/config/mcp/specs.ts +18 -6
- package/src/domains/config/operations/validateTier.ts +1 -1
- package/src/domains/config/tasks/index.ts +3 -0
- package/src/domains/config/tasks/setChannel.ts +30 -0
- package/src/domains/config/tasks/setTier.ts +33 -0
- package/src/domains/create/commands/package.test.ts +2 -2
- package/src/domains/doctor/mcp/index.ts +1 -1
- package/src/domains/doctor/mcp/specs.ts +2 -49
- package/src/domains/graph/commands/inspect.test.ts +1 -1
- package/src/domains/graph/formatters/inspect.ts +12 -7
- package/src/domains/graph/operations/inspectUri.ts +4 -1
- package/src/domains/info/commands/upgrade.ts +6 -3
- package/src/domains/info/index.ts +3 -1
- package/src/domains/info/mcp/index.ts +2 -0
- package/src/domains/info/mcp/specs.ts +65 -0
- package/src/domains/llm/data/commandReference.ts +4 -4
- package/src/domains/llm/data/decisionTrees.ts +14 -12
- package/src/domains/llm/operations/collectContext.ts +1 -1
- package/src/domains/modifier/commands/list.ts +9 -0
- package/src/domains/modifier/commands/lookup.ts +22 -42
- package/src/domains/modifier/formatters/index.ts +4 -1
- package/src/domains/modifier/formatters/list.test.ts +1 -1
- package/src/domains/modifier/formatters/list.ts +13 -26
- package/src/domains/modifier/formatters/lookup.test.ts +1 -1
- package/src/domains/modifier/formatters/lookup.ts +41 -29
- package/src/domains/modifier/index.ts +1 -0
- package/src/domains/modifier/modifierConfig.ts +36 -0
- package/src/domains/modifier/operations/list.ts +1 -1
- package/src/domains/modifier/operations/lookup.ts +4 -3
- package/src/domains/modifier/orchestration/resolveModifierLookup.ts +1 -1
- package/src/domains/modifier/types.ts +1 -1
- package/src/domains/ontology/commands/list.test.ts +1 -1
- package/src/domains/ontology/commands/list.ts +18 -0
- package/src/domains/ontology/commands/show.test.ts +1 -1
- package/src/domains/ontology/formatters/list.ts +5 -3
- package/src/domains/ontology/formatters/show.ts +38 -13
- package/src/domains/ontology/helpers/queryClasses.ts +1 -1
- package/src/domains/ontology/helpers/queryProperties.ts +1 -1
- package/src/domains/ontology/operations/listOntologies.ts +1 -1
- package/src/domains/ontology/operations/showOntology.ts +1 -1
- package/src/domains/setup/helpers/completionScripts.test.ts +3 -3
- package/src/domains/setup/helpers/completionScripts.ts +4 -4
- package/src/domains/shared/ToolSpec.ts +1 -1
- package/src/domains/shared/context.ts +3 -2
- package/src/domains/shared/contracts.ts +40 -1
- package/src/domains/shared/filters/buildFilterConfig.ts +1 -2
- package/src/domains/shared/filters/buildFilters.ts +1 -1
- package/src/domains/shared/filters/describeFilters.ts +1 -1
- package/src/domains/shared/formatters.ts +47 -0
- package/src/domains/shared/renderers.ts +410 -0
- package/src/domains/shared/runtime.ts +7 -22
- package/src/domains/shared/types/block.ts +107 -0
- package/src/domains/shared/types/common.ts +33 -0
- package/src/domains/shared/types/graph.ts +17 -0
- package/src/domains/shared/types/index.ts +49 -0
- package/src/domains/shared/types/modifier.ts +22 -0
- package/src/domains/shared/types/ontology.ts +50 -0
- package/src/domains/shared/types/runtime.ts +21 -0
- package/src/domains/shared/types/standard.ts +64 -0
- package/src/domains/shared/types/tier.ts +15 -0
- package/src/domains/shared/types/token.ts +30 -0
- package/src/domains/standard/commands/list.ts +9 -0
- package/src/domains/standard/commands/lookup.ts +22 -50
- package/src/domains/standard/formatters/categories.test.ts +1 -1
- package/src/domains/standard/formatters/categories.ts +1 -1
- package/src/domains/standard/formatters/index.ts +4 -1
- package/src/domains/standard/formatters/list.test.ts +4 -1
- package/src/domains/standard/formatters/list.ts +112 -84
- package/src/domains/standard/formatters/lookup.test.ts +1 -1
- package/src/domains/standard/formatters/lookup.ts +85 -70
- package/src/domains/standard/formatters/types.ts +1 -1
- package/src/domains/standard/index.ts +1 -0
- package/src/domains/standard/operations/categories.ts +1 -1
- package/src/domains/standard/operations/list.ts +1 -1
- package/src/domains/standard/operations/lookup.ts +1 -1
- package/src/domains/standard/orchestration/buildStandardFilters.ts +1 -1
- package/src/domains/standard/orchestration/resolveStandardList.ts +4 -1
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +1 -1
- package/src/domains/standard/orchestration/standardEmptyError.ts +1 -1
- package/src/domains/standard/standardConfig.ts +42 -0
- package/src/domains/standard/types.ts +1 -1
- package/src/domains/tier/commands/list.ts +16 -0
- package/src/domains/tier/formatters/list.test.ts +1 -1
- package/src/domains/tier/formatters/list.ts +11 -6
- package/src/domains/tier/operations/list.ts +1 -1
- package/src/domains/token/commands/list.ts +9 -0
- package/src/domains/token/commands/lookup.ts +22 -48
- package/src/domains/token/formatters/index.ts +4 -1
- package/src/domains/token/formatters/list.test.ts +1 -1
- package/src/domains/token/formatters/list.ts +15 -28
- package/src/domains/token/formatters/lookup.test.ts +2 -2
- package/src/domains/token/formatters/lookup.ts +80 -45
- package/src/domains/token/index.ts +1 -0
- package/src/domains/token/operations/list.ts +1 -1
- package/src/domains/token/operations/lookup.ts +1 -1
- package/src/domains/token/orchestration/resolveTokenLookup.ts +1 -1
- package/src/domains/token/tokenConfig.ts +38 -0
- package/src/domains/token/types.ts +1 -1
- package/src/index.ts +1 -1
- package/src/mcp/runMcpServer.test.ts +2 -2
- package/src/mcp/tools/index.ts +2 -0
- package/src/mcp/tools/registerTools.test.ts +8 -3
- package/src/package-manager/types.ts +1 -3
- package/src/pipeline/createProgram.ts +20 -1
- package/src/pipeline/index.ts +4 -1
- package/src/pipeline/parseGlobalFlags.test.ts +92 -0
- package/src/pipeline/parseGlobalFlags.ts +25 -0
- package/src/pipeline/runCli.orchestration.test.ts +1 -0
- package/src/pipeline/runCli.ts +11 -4
- package/src/pipeline/runInteractiveCommand.ts +198 -0
- package/src/testing/cli.ts +2 -2
- package/src/testing/fixtures.ts +2 -2
- package/src/testing/integration/boot.test.ts +2 -2
- package/src/testing/integration/cli-user-stories.test.ts +198 -0
- package/src/testing/integration/edge-cases.test.ts +263 -0
- package/src/testing/integration/parity.test.ts +377 -27
- package/src/testing/store.ts +1 -1
- package/src/tui/components/BulletList/BulletList.tsx +46 -0
- package/src/tui/components/BulletList/index.ts +2 -0
- package/src/tui/components/BulletList/types.ts +6 -0
- package/src/tui/components/Card/Card.tsx +73 -0
- package/src/tui/components/Card/index.ts +1 -0
- package/src/tui/components/Card/types.ts +12 -0
- package/src/tui/components/CodeBlock/CodeBlock.tsx +37 -0
- package/src/tui/components/CodeBlock/index.ts +2 -0
- package/src/tui/components/CodeBlock/types.ts +6 -0
- package/src/tui/components/DataRow/DataRow.tsx +80 -0
- package/src/tui/components/DataRow/index.ts +2 -0
- package/src/tui/components/DataRow/types.ts +14 -0
- package/src/tui/components/FieldRow/FieldRow.tsx +24 -0
- package/src/tui/components/FieldRow/index.ts +2 -0
- package/src/tui/components/FieldRow/types.ts +10 -0
- package/src/tui/components/HeaderRow/HeaderRow.tsx +32 -0
- package/src/tui/components/HeaderRow/index.ts +2 -0
- package/src/tui/components/HeaderRow/types.ts +8 -0
- package/src/tui/components/MarkdownText/MarkdownText.tsx +19 -0
- package/src/tui/components/MarkdownText/index.ts +2 -0
- package/src/tui/components/MarkdownText/types.ts +4 -0
- package/src/tui/components/MiniTable/MiniTable.tsx +82 -0
- package/src/tui/components/MiniTable/index.ts +2 -0
- package/src/tui/components/MiniTable/types.ts +6 -0
- package/src/tui/components/SectionRenderer/SectionRenderer.tsx +91 -0
- package/src/tui/components/SectionRenderer/index.ts +2 -0
- package/src/tui/components/SectionRenderer/types.ts +15 -0
- package/src/tui/components/TreeView/TreeView.tsx +84 -0
- package/src/tui/components/TreeView/index.ts +2 -0
- package/src/tui/components/TreeView/types.ts +6 -0
- package/src/tui/components/index.ts +19 -0
- package/src/tui/constants.ts +39 -0
- package/src/tui/createListView.ts +23 -0
- package/src/tui/createLookupView.ts +26 -0
- package/src/tui/helpers/computeWidths.ts +53 -0
- package/src/tui/helpers/fitColumns.ts +33 -0
- package/src/tui/helpers/formatMarkdown.ts +19 -0
- package/src/tui/helpers/getTerminalSize.ts +16 -0
- package/src/tui/helpers/index.ts +6 -0
- package/src/tui/helpers/syntaxColor.ts +67 -0
- package/src/tui/helpers/truncateText.ts +12 -0
- package/src/tui/hooks/index.ts +3 -0
- package/src/tui/hooks/types.ts +7 -0
- package/src/tui/hooks/useAutoExit.ts +17 -0
- package/src/tui/hooks/useTerminalSize.ts +19 -0
- package/src/tui/index.ts +15 -0
- package/src/tui/renderInk.tsx +18 -0
- package/src/tui/types.ts +12 -0
- package/src/tui/views/ListView/ListView.tsx +104 -0
- package/src/tui/views/ListView/index.ts +2 -0
- package/src/tui/views/ListView/types.ts +14 -0
- package/src/tui/views/LookupView/LookupView.tsx +244 -0
- package/src/tui/views/LookupView/index.ts +2 -0
- package/src/tui/views/LookupView/types.ts +16 -0
- package/src/tui/views/index.ts +4 -0
- package/src/domains/shared/types.test-d.ts +0 -127
- package/src/domains/shared/types.ts +0 -348
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic list and lookup renderers for CLI and LLM output modes.
|
|
3
|
+
*
|
|
4
|
+
* All domain formatters delegate to these pure, layout-only functions.
|
|
5
|
+
* Each renderer accepts a typed options bag (defined in contracts.ts)
|
|
6
|
+
* and returns a fully formatted string — no I/O, no side effects.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import compactUri from "./compactUri.js";
|
|
10
|
+
import type {
|
|
11
|
+
ColumnDef,
|
|
12
|
+
RenderListOptions,
|
|
13
|
+
RenderLookupOptions,
|
|
14
|
+
SectionDef,
|
|
15
|
+
} from "./contracts.js";
|
|
16
|
+
import { PREFIX_MAP } from "./prefixes.js";
|
|
17
|
+
|
|
18
|
+
type RenderMode = "plain" | "llm";
|
|
19
|
+
|
|
20
|
+
export function renderListPlain<T>(
|
|
21
|
+
items: readonly T[],
|
|
22
|
+
options: RenderListOptions<T>,
|
|
23
|
+
): string {
|
|
24
|
+
const prefixes = options.prefixes ?? PREFIX_MAP;
|
|
25
|
+
const rows = items.map((item) =>
|
|
26
|
+
options.columns
|
|
27
|
+
.map((column) => ({
|
|
28
|
+
column,
|
|
29
|
+
value: formatColumnValue(item, column, prefixes),
|
|
30
|
+
}))
|
|
31
|
+
.filter(({ column, value }) => shouldRenderColumn(column, value))
|
|
32
|
+
.map(({ value }) => value),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const widths = rows.reduce<number[]>(
|
|
36
|
+
(acc, row) =>
|
|
37
|
+
row.map((value, index) => Math.max(acc[index] ?? 0, value.length)),
|
|
38
|
+
[],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return rows
|
|
42
|
+
.map((row) =>
|
|
43
|
+
row
|
|
44
|
+
.map((value, index) => value.padEnd(widths[index] ?? value.length))
|
|
45
|
+
.join(" ")
|
|
46
|
+
.trimEnd(),
|
|
47
|
+
)
|
|
48
|
+
.join("\n");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function renderListLlm<T>(
|
|
52
|
+
items: readonly T[],
|
|
53
|
+
options: RenderListOptions<T>,
|
|
54
|
+
): string {
|
|
55
|
+
const prefixes = options.prefixes ?? PREFIX_MAP;
|
|
56
|
+
const lines = [`## ${options.heading} (${items.length})`, ""];
|
|
57
|
+
|
|
58
|
+
for (const item of items) {
|
|
59
|
+
const values = options.columns
|
|
60
|
+
.map((column) => ({
|
|
61
|
+
column,
|
|
62
|
+
value: formatColumnValue(item, column, prefixes),
|
|
63
|
+
}))
|
|
64
|
+
.filter(({ column, value }) => shouldRenderColumn(column, value));
|
|
65
|
+
|
|
66
|
+
const iri = values.find(({ column }) => column.key === "uri")?.value;
|
|
67
|
+
const name = values.find(({ column }) => column.key === "name")?.value;
|
|
68
|
+
const rest = values
|
|
69
|
+
.filter(({ column }) => column.key !== "uri" && column.key !== "name")
|
|
70
|
+
.map(({ value }) => value)
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
|
|
73
|
+
const parts: string[] = [];
|
|
74
|
+
if (iri) parts.push(`\`${iri}\``);
|
|
75
|
+
if (name) {
|
|
76
|
+
parts.push(iri ? `— **${name}**` : `**${name}**`);
|
|
77
|
+
}
|
|
78
|
+
if (rest.length > 0) {
|
|
79
|
+
parts.push(rest.join(" | "));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
lines.push(`- ${parts.join(" ")}`.trimEnd());
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function renderLookupPlain<T>(
|
|
89
|
+
entity: T,
|
|
90
|
+
options: RenderLookupOptions<T>,
|
|
91
|
+
): string {
|
|
92
|
+
return renderLookup(entity, options, "plain");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function renderLookupLlm<T>(
|
|
96
|
+
entity: T,
|
|
97
|
+
options: RenderLookupOptions<T>,
|
|
98
|
+
): string {
|
|
99
|
+
return renderLookup(entity, options, "llm");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function renderLookup<T>(
|
|
103
|
+
entity: T,
|
|
104
|
+
options: RenderLookupOptions<T>,
|
|
105
|
+
mode: RenderMode,
|
|
106
|
+
): string {
|
|
107
|
+
const title = options.title(entity);
|
|
108
|
+
const fields = renderLookupFields(entity, options, mode);
|
|
109
|
+
const sections = renderLookupSections(entity, options, mode);
|
|
110
|
+
|
|
111
|
+
if (mode === "llm") {
|
|
112
|
+
return [`## ${title}`, "", ...fields, ...sections].join("\n").trimEnd();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const headingRule = "═".repeat(Math.max(title.length, 24));
|
|
116
|
+
return [title, headingRule, "", ...fields, ...sections].join("\n").trimEnd();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function renderLookupFields<T>(
|
|
120
|
+
entity: T,
|
|
121
|
+
options: RenderLookupOptions<T>,
|
|
122
|
+
mode: RenderMode,
|
|
123
|
+
): string[] {
|
|
124
|
+
const prefixes = options.prefixes ?? PREFIX_MAP;
|
|
125
|
+
return options.fields.flatMap((field) => {
|
|
126
|
+
const value = field.value(entity);
|
|
127
|
+
if (isEmptyValue(value)) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const formatted = formatInlineValue(value, prefixes);
|
|
132
|
+
return mode === "llm"
|
|
133
|
+
? [`- ${field.label}: ${formatted}`]
|
|
134
|
+
: [` ${field.label}: ${formatted}`];
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function renderLookupSections<T>(
|
|
139
|
+
entity: T,
|
|
140
|
+
options: RenderLookupOptions<T>,
|
|
141
|
+
mode: RenderMode,
|
|
142
|
+
): string[] {
|
|
143
|
+
const sections: string[] = [];
|
|
144
|
+
|
|
145
|
+
for (const section of options.sections) {
|
|
146
|
+
const value = entity[section.key];
|
|
147
|
+
if (!section.showWhenEmpty && isEmptyValue(value)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const override = options.sectionOverrides?.[section.key]?.[mode];
|
|
152
|
+
const body = override
|
|
153
|
+
? override(entity, section)
|
|
154
|
+
: renderSectionValue(value, section, options, mode);
|
|
155
|
+
|
|
156
|
+
if (!body) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
sections.push("");
|
|
161
|
+
sections.push(
|
|
162
|
+
mode === "llm" ? `### ${section.heading}` : `${section.heading}:`,
|
|
163
|
+
);
|
|
164
|
+
sections.push(body);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return sections;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function renderSectionValue<T>(
|
|
171
|
+
value: unknown,
|
|
172
|
+
section: SectionDef<T>,
|
|
173
|
+
options: RenderLookupOptions<T>,
|
|
174
|
+
mode: RenderMode,
|
|
175
|
+
): string | null {
|
|
176
|
+
const prefixes = options.prefixes ?? PREFIX_MAP;
|
|
177
|
+
|
|
178
|
+
switch (section.kind) {
|
|
179
|
+
case "field":
|
|
180
|
+
return renderFieldValue(value, prefixes, mode);
|
|
181
|
+
case "code":
|
|
182
|
+
return renderCodeValue(value, section, options, mode);
|
|
183
|
+
case "list":
|
|
184
|
+
case "table":
|
|
185
|
+
case "nested-table":
|
|
186
|
+
return renderCollectionValue(value, prefixes, mode);
|
|
187
|
+
case "tree":
|
|
188
|
+
return renderTreeValue(value, prefixes, mode);
|
|
189
|
+
default:
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function renderFieldValue(
|
|
195
|
+
value: unknown,
|
|
196
|
+
prefixes: Readonly<Record<string, string>>,
|
|
197
|
+
mode: RenderMode,
|
|
198
|
+
): string | null {
|
|
199
|
+
if (isEmptyValue(value)) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const text = formatScalarValue(value, prefixes);
|
|
204
|
+
return mode === "llm" ? text : indentBlock(text);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function renderCodeValue<T>(
|
|
208
|
+
value: unknown,
|
|
209
|
+
section: SectionDef<T>,
|
|
210
|
+
options: RenderLookupOptions<T>,
|
|
211
|
+
mode: RenderMode,
|
|
212
|
+
): string | null {
|
|
213
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const language =
|
|
218
|
+
options.codeLanguage?.(section, value) ?? inferCodeLanguage(value);
|
|
219
|
+
if (mode === "llm") {
|
|
220
|
+
return [`\`\`\`${language}`, value, "```"].join("\n");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const header = language === "text" ? "" : `[${language}]\n`;
|
|
224
|
+
return indentBlock(`${header}${value}`.trimEnd());
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function renderCollectionValue(
|
|
228
|
+
value: unknown,
|
|
229
|
+
prefixes: Readonly<Record<string, string>>,
|
|
230
|
+
mode: RenderMode,
|
|
231
|
+
): string | null {
|
|
232
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const lines = value.map(
|
|
237
|
+
(entry) => `- ${formatCollectionEntry(entry, prefixes)}`,
|
|
238
|
+
);
|
|
239
|
+
return mode === "llm" ? lines.join("\n") : indentBlock(lines.join("\n"));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function renderTreeValue(
|
|
243
|
+
value: unknown,
|
|
244
|
+
prefixes: Readonly<Record<string, string>>,
|
|
245
|
+
mode: RenderMode,
|
|
246
|
+
): string | null {
|
|
247
|
+
const lines = buildTreeLines(value, prefixes);
|
|
248
|
+
if (lines.length === 0) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return mode === "llm" ? lines.join("\n") : indentBlock(lines.join("\n"));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function buildTreeLines(
|
|
256
|
+
value: unknown,
|
|
257
|
+
prefixes: Readonly<Record<string, string>>,
|
|
258
|
+
depth = 0,
|
|
259
|
+
): string[] {
|
|
260
|
+
if (Array.isArray(value)) {
|
|
261
|
+
return value.flatMap((entry) => buildTreeLines(entry, prefixes, depth));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (!value || typeof value !== "object") {
|
|
265
|
+
return typeof value === "string" && value.length > 0
|
|
266
|
+
? [`${" ".repeat(depth)}- ${formatScalarValue(value, prefixes)}`]
|
|
267
|
+
: [];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const rootedTree = value as { root?: unknown };
|
|
271
|
+
if (rootedTree.root) {
|
|
272
|
+
return buildTreeLines(rootedTree.root, prefixes, depth);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const treeNode = value as {
|
|
276
|
+
name?: unknown;
|
|
277
|
+
uri?: unknown;
|
|
278
|
+
children?: unknown;
|
|
279
|
+
};
|
|
280
|
+
const label =
|
|
281
|
+
typeof treeNode.name === "string" && treeNode.name.length > 0
|
|
282
|
+
? treeNode.name
|
|
283
|
+
: typeof treeNode.uri === "string"
|
|
284
|
+
? compactUri(treeNode.uri, prefixes)
|
|
285
|
+
: "item";
|
|
286
|
+
|
|
287
|
+
const lines = [`${" ".repeat(depth)}- ${label}`];
|
|
288
|
+
if (Array.isArray(treeNode.children)) {
|
|
289
|
+
lines.push(...buildTreeLines(treeNode.children, prefixes, depth + 1));
|
|
290
|
+
}
|
|
291
|
+
return lines;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function formatCollectionEntry(
|
|
295
|
+
value: unknown,
|
|
296
|
+
prefixes: Readonly<Record<string, string>>,
|
|
297
|
+
): string {
|
|
298
|
+
if (!value || typeof value !== "object") {
|
|
299
|
+
return formatScalarValue(value, prefixes);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const object = value as Record<string, unknown>;
|
|
303
|
+
return Object.entries(object)
|
|
304
|
+
.filter(([, entryValue]) => !isEmptyValue(entryValue))
|
|
305
|
+
.map(
|
|
306
|
+
([key, entryValue]) =>
|
|
307
|
+
`${key}: ${formatInlineValue(entryValue, prefixes)}`,
|
|
308
|
+
)
|
|
309
|
+
.join(" | ");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function formatColumnValue<T>(
|
|
313
|
+
item: T,
|
|
314
|
+
column: ColumnDef<T>,
|
|
315
|
+
prefixes: Readonly<Record<string, string>>,
|
|
316
|
+
): string {
|
|
317
|
+
const raw = item[column.key];
|
|
318
|
+
if (column.format) {
|
|
319
|
+
return column.format(raw);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return formatInlineValue(raw, prefixes);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function formatInlineValue(
|
|
326
|
+
value: unknown,
|
|
327
|
+
prefixes: Readonly<Record<string, string>>,
|
|
328
|
+
): string {
|
|
329
|
+
if (Array.isArray(value)) {
|
|
330
|
+
return value.map((entry) => formatInlineValue(entry, prefixes)).join(", ");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (value && typeof value === "object") {
|
|
334
|
+
return Object.entries(value as Record<string, unknown>)
|
|
335
|
+
.filter(([, entryValue]) => !isEmptyValue(entryValue))
|
|
336
|
+
.map(
|
|
337
|
+
([key, entryValue]) =>
|
|
338
|
+
`${key}=${formatInlineValue(entryValue, prefixes)}`,
|
|
339
|
+
)
|
|
340
|
+
.join("; ");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return formatScalarValue(value, prefixes);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function formatScalarValue(
|
|
347
|
+
value: unknown,
|
|
348
|
+
prefixes: Readonly<Record<string, string>>,
|
|
349
|
+
): string {
|
|
350
|
+
if (value === null || value === undefined) {
|
|
351
|
+
return "";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (typeof value === "string") {
|
|
355
|
+
return looksLikeUri(value) ? compactUri(value, prefixes) : value;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return String(value);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function shouldRenderColumn<T>(column: ColumnDef<T>, value: string): boolean {
|
|
362
|
+
if (value.length > 0) {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return column.showWhenEmpty === true;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function inferCodeLanguage(value: string): string {
|
|
370
|
+
const trimmed = value.trim();
|
|
371
|
+
if (trimmed.startsWith("@prefix") || trimmed.includes(" a ")) {
|
|
372
|
+
return "ttl";
|
|
373
|
+
}
|
|
374
|
+
if (trimmed.includes(":") && trimmed.includes("\n")) {
|
|
375
|
+
return "yaml";
|
|
376
|
+
}
|
|
377
|
+
return "text";
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function looksLikeUri(value: string): boolean {
|
|
381
|
+
return (
|
|
382
|
+
value.startsWith("http://") ||
|
|
383
|
+
value.startsWith("https://") ||
|
|
384
|
+
/^[a-z][a-z0-9+.-]*:/i.test(value)
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function isEmptyValue(value: unknown): boolean {
|
|
389
|
+
if (value === null || value === undefined) {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (typeof value === "string") {
|
|
394
|
+
return value.trim().length === 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (Array.isArray(value)) {
|
|
398
|
+
return value.length === 0;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function indentBlock(value: string, spaces = 2): string {
|
|
405
|
+
const indent = " ".repeat(spaces);
|
|
406
|
+
return value
|
|
407
|
+
.split("\n")
|
|
408
|
+
.map((line) => `${indent}${line}`)
|
|
409
|
+
.join("\n");
|
|
410
|
+
}
|
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unified boot path for pragma CLI, MCP, and completions server.
|
|
3
3
|
*
|
|
4
|
-
* `
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
* assembling store + config.
|
|
4
|
+
* `bootPragma()` is the single factory function that creates a
|
|
5
|
+
* `PragmaRuntime`. All three surfaces (CLI, MCP, completions) call it
|
|
6
|
+
* instead of manually assembling store + config.
|
|
8
7
|
*
|
|
9
8
|
* @note Impure — reads config from filesystem and boots ke store (WASM + TTL).
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
|
-
import type { SourceSpec
|
|
13
|
-
import {
|
|
11
|
+
import type { SourceSpec } from "@canonical/ke";
|
|
12
|
+
import { readConfig } from "#config";
|
|
14
13
|
import { bootStore } from "./bootStore.js";
|
|
14
|
+
import type { PragmaRuntime } from "./types/index.js";
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
* Boot-time state shared by CLI, MCP, and completions server.
|
|
18
|
-
*
|
|
19
|
-
* Four members:
|
|
20
|
-
* - `store` — the ke triple store, loaded and ready for SPARQL queries.
|
|
21
|
-
* - `config` — resolved pragma config (tier, channel).
|
|
22
|
-
* - `cwd` — working directory used for config and source resolution.
|
|
23
|
-
* - `dispose()` — tears down the store and frees WASM memory. Must be
|
|
24
|
-
* called exactly once; calling operations after dispose is undefined.
|
|
25
|
-
*/
|
|
26
|
-
export interface PragmaRuntime {
|
|
27
|
-
readonly store: Store;
|
|
28
|
-
readonly config: PragmaConfig;
|
|
29
|
-
readonly cwd: string;
|
|
30
|
-
dispose(): void;
|
|
31
|
-
}
|
|
16
|
+
export type { PragmaRuntime } from "./types/index.js";
|
|
32
17
|
|
|
33
18
|
/**
|
|
34
19
|
* Create a fully initialized `PragmaRuntime`.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** Block domain return types. */
|
|
2
|
+
|
|
3
|
+
import type { URI } from "@canonical/ke";
|
|
4
|
+
import type { StandardRef } from "./standard.js";
|
|
5
|
+
import type { TokenRef } from "./token.js";
|
|
6
|
+
|
|
7
|
+
/** Summary view of a design-system block (component, pattern, or primitive). */
|
|
8
|
+
export interface BlockSummary {
|
|
9
|
+
/** Full RDF URI identifying this block in the ke store. */
|
|
10
|
+
readonly uri: URI;
|
|
11
|
+
/** Human-readable name (local name extracted from URI). */
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** Normalized ontology type for the block. */
|
|
14
|
+
readonly type: "component" | "pattern" | "layout" | "subcomponent";
|
|
15
|
+
/** Tier path this block belongs to (e.g., `"global"`, `"apps/lxd"`). */
|
|
16
|
+
readonly tier: string;
|
|
17
|
+
/** Active modifier names applied to this block. */
|
|
18
|
+
readonly modifiers: readonly string[];
|
|
19
|
+
/** Framework implementations declared for this block, with availability flags. */
|
|
20
|
+
readonly implementations: readonly {
|
|
21
|
+
framework: string;
|
|
22
|
+
available: boolean;
|
|
23
|
+
}[];
|
|
24
|
+
/** Number of anatomy nodes (DOM elements) in this block's anatomy tree. */
|
|
25
|
+
readonly nodeCount: number;
|
|
26
|
+
/** Number of design tokens referenced by this block. */
|
|
27
|
+
readonly tokenCount: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Digest view of a block for richer summary displays. */
|
|
31
|
+
export interface BlockDigest extends BlockSummary {
|
|
32
|
+
/** Summary guidance when present in the graph. */
|
|
33
|
+
readonly summary: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Single node in a block's anatomy tree (recursive). */
|
|
37
|
+
export interface AnatomyNode {
|
|
38
|
+
/** Element name (e.g., `"trigger"`, `"panel"`). */
|
|
39
|
+
readonly name: string;
|
|
40
|
+
/** Whether the node is explicitly named or inferred as anonymous. */
|
|
41
|
+
readonly type: "named" | "anonymous";
|
|
42
|
+
/** Child nodes nested under this element. */
|
|
43
|
+
readonly children: readonly AnatomyNode[];
|
|
44
|
+
/** Named slot this node exposes, if any. */
|
|
45
|
+
readonly slot?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Wrapper around the root of a block's anatomy tree. */
|
|
49
|
+
export interface AnatomyTree {
|
|
50
|
+
/** Top-level anatomy node from which all children descend. */
|
|
51
|
+
readonly root: AnatomyNode;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Recursive subcomponent entry used in block detailed views. */
|
|
55
|
+
export interface BlockSubcomponent {
|
|
56
|
+
/** Full RDF URI of the subcomponent. */
|
|
57
|
+
readonly uri: URI;
|
|
58
|
+
/** Human-readable subcomponent name. */
|
|
59
|
+
readonly name: string;
|
|
60
|
+
/** Nested subcomponents owned by this subcomponent. */
|
|
61
|
+
readonly children: readonly BlockSubcomponent[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Detailed view of a block, extending the digest with anatomy, tokens, and standards. */
|
|
65
|
+
export interface BlockDetailed extends BlockDigest {
|
|
66
|
+
/** Guidance on when the block should be used. */
|
|
67
|
+
readonly whenToUse: string | null;
|
|
68
|
+
/** Guidance on when the block should not be used. */
|
|
69
|
+
readonly whenNotToUse: string | null;
|
|
70
|
+
/** Additional usage guidance. */
|
|
71
|
+
readonly guidelines: string | null;
|
|
72
|
+
/** YAML anatomy DSL payload when present. */
|
|
73
|
+
readonly anatomyDsl: string | null;
|
|
74
|
+
/** Legacy anatomy payload when present. */
|
|
75
|
+
readonly anatomyClassic: string | null;
|
|
76
|
+
/** Figma reference link when present. */
|
|
77
|
+
readonly figmaLink: string | null;
|
|
78
|
+
/** Full anatomy tree, or `null` if the block declares no anatomy. */
|
|
79
|
+
readonly anatomy: AnatomyTree | null;
|
|
80
|
+
/** Modifier families with their allowed values. */
|
|
81
|
+
readonly modifierValues: readonly {
|
|
82
|
+
family: string;
|
|
83
|
+
values: readonly string[];
|
|
84
|
+
}[];
|
|
85
|
+
/** Modifier families resolved as richer objects. */
|
|
86
|
+
readonly modifierFamilies: readonly {
|
|
87
|
+
uri: URI;
|
|
88
|
+
name: string;
|
|
89
|
+
values: readonly string[];
|
|
90
|
+
}[];
|
|
91
|
+
/** Property metadata resolved from blank nodes. */
|
|
92
|
+
readonly properties: readonly {
|
|
93
|
+
name: string;
|
|
94
|
+
propertyType: string;
|
|
95
|
+
optional: boolean;
|
|
96
|
+
defaultValue: string | null;
|
|
97
|
+
constraints: string | null;
|
|
98
|
+
}[];
|
|
99
|
+
/** Direct subcomponents of the block. */
|
|
100
|
+
readonly subcomponents: readonly BlockSubcomponent[];
|
|
101
|
+
/** File paths to framework-specific implementations. */
|
|
102
|
+
readonly implementationPaths: readonly { framework: string; path: string }[];
|
|
103
|
+
/** Design tokens referenced by this block. */
|
|
104
|
+
readonly tokens: readonly TokenRef[];
|
|
105
|
+
/** Standards (guidelines/rules) that apply to this block. */
|
|
106
|
+
readonly standards: readonly StandardRef[];
|
|
107
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Shared cross-domain types used by multiple domains and contracts. */
|
|
2
|
+
|
|
3
|
+
/** Runtime filter settings controlling tier and channel visibility in queries. */
|
|
4
|
+
export interface FilterConfig {
|
|
5
|
+
/** Active tier path, or `undefined` for all-tiers visibility. */
|
|
6
|
+
readonly tier: string | undefined;
|
|
7
|
+
/** Release channel determining which stability levels are visible. */
|
|
8
|
+
readonly channel: "normal" | "experimental" | "prerelease";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Controls how much detail an operation returns.
|
|
13
|
+
*
|
|
14
|
+
* - `"summary"` — minimal fields (names and counts).
|
|
15
|
+
* - `"digest"` — intermediate detail with optional example length cap.
|
|
16
|
+
* - `"detailed"` — full content including code blocks and anatomy.
|
|
17
|
+
*/
|
|
18
|
+
export type Disclosure =
|
|
19
|
+
| { readonly level: "summary" }
|
|
20
|
+
| { readonly level: "digest"; readonly maxExampleLength?: number }
|
|
21
|
+
| { readonly level: "detailed" };
|
|
22
|
+
|
|
23
|
+
/** Outcome of a batch operation: successful results alongside per-item errors. */
|
|
24
|
+
export interface BatchResult<T> {
|
|
25
|
+
/** Items that were processed successfully. */
|
|
26
|
+
readonly results: readonly T[];
|
|
27
|
+
/** Items that failed, each carrying the entity name, error code, and message. */
|
|
28
|
+
readonly errors: readonly {
|
|
29
|
+
name: string;
|
|
30
|
+
code: string;
|
|
31
|
+
message: string;
|
|
32
|
+
}[];
|
|
33
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Graph inspection return types. */
|
|
2
|
+
|
|
3
|
+
/** A predicate group from `graph inspect`: one predicate with all its objects. */
|
|
4
|
+
export interface PredicateGroup {
|
|
5
|
+
/** RDF predicate URI (e.g., `"rdfs:label"`). */
|
|
6
|
+
readonly predicate: string;
|
|
7
|
+
/** Object values (URIs or literals) associated with this predicate. */
|
|
8
|
+
readonly objects: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Result of `graph inspect <uri>`: all triples where URI is subject. */
|
|
12
|
+
export interface InspectResult {
|
|
13
|
+
/** The inspected subject URI. */
|
|
14
|
+
readonly uri: string;
|
|
15
|
+
/** Predicate groups aggregating all outgoing triples. */
|
|
16
|
+
readonly groups: readonly PredicateGroup[];
|
|
17
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module types
|
|
3
|
+
*
|
|
4
|
+
* Shared operation return types — contract between operations (D3) and
|
|
5
|
+
* CLI/MCP adapters. Same data, different presentations.
|
|
6
|
+
*
|
|
7
|
+
* Per-domain files keep each concern isolated; this barrel re-exports
|
|
8
|
+
* everything for consumers that import from `shared/types/index.js`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type {
|
|
12
|
+
AnatomyNode,
|
|
13
|
+
AnatomyTree,
|
|
14
|
+
BlockDetailed,
|
|
15
|
+
BlockDigest,
|
|
16
|
+
BlockSubcomponent,
|
|
17
|
+
BlockSummary,
|
|
18
|
+
} from "./block.js";
|
|
19
|
+
export type { BatchResult, Disclosure, FilterConfig } from "./common.js";
|
|
20
|
+
export type { InspectResult, PredicateGroup } from "./graph.js";
|
|
21
|
+
export type {
|
|
22
|
+
ModifierDetailed,
|
|
23
|
+
ModifierDigest,
|
|
24
|
+
ModifierFamily,
|
|
25
|
+
ModifierSummary,
|
|
26
|
+
} from "./modifier.js";
|
|
27
|
+
export type {
|
|
28
|
+
OntologyClass,
|
|
29
|
+
OntologyDetailed,
|
|
30
|
+
OntologyProperty,
|
|
31
|
+
OntologySummary,
|
|
32
|
+
} from "./ontology.js";
|
|
33
|
+
export type { PragmaRuntime } from "./runtime.js";
|
|
34
|
+
export type {
|
|
35
|
+
CategorySummary,
|
|
36
|
+
CodeBlock,
|
|
37
|
+
StandardDetailed,
|
|
38
|
+
StandardDigest,
|
|
39
|
+
StandardListFilters,
|
|
40
|
+
StandardRef,
|
|
41
|
+
StandardSummary,
|
|
42
|
+
} from "./standard.js";
|
|
43
|
+
export type { TierEntry } from "./tier.js";
|
|
44
|
+
export type {
|
|
45
|
+
TokenDetailed,
|
|
46
|
+
TokenDigest,
|
|
47
|
+
TokenRef,
|
|
48
|
+
TokenSummary,
|
|
49
|
+
} from "./token.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Modifier domain return types. */
|
|
2
|
+
|
|
3
|
+
import type { URI } from "@canonical/ke";
|
|
4
|
+
|
|
5
|
+
/** A modifier family (e.g., "size", "variant") with its allowed values. */
|
|
6
|
+
export interface ModifierFamily {
|
|
7
|
+
/** Full RDF URI identifying this modifier family. */
|
|
8
|
+
readonly uri: URI;
|
|
9
|
+
/** Human-readable family name. */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
/** Allowed values within this family (e.g., `["small", "medium", "large"]`). */
|
|
12
|
+
readonly values: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Summary view of a modifier family. */
|
|
16
|
+
export type ModifierSummary = ModifierFamily;
|
|
17
|
+
|
|
18
|
+
/** Digest view of a modifier family for generic list contracts. */
|
|
19
|
+
export type ModifierDigest = ModifierSummary;
|
|
20
|
+
|
|
21
|
+
/** Detailed view of a modifier family. */
|
|
22
|
+
export type ModifierDetailed = ModifierFamily;
|