@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.
- package/dist/pragma +0 -0
- package/package.json +31 -18
- package/src/bin.test.ts +2 -0
- package/src/bin.ts +1 -0
- package/src/compile-validation.ts +3 -4
- 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/embedWasm.ts +42 -0
- 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,50 @@
|
|
|
1
|
+
/** Ontology inspection return types. */
|
|
2
|
+
|
|
3
|
+
/** Summary of a loaded ontology namespace from `ontology list`. */
|
|
4
|
+
export interface OntologySummary {
|
|
5
|
+
/** Short prefix alias (e.g., `"ds"`, `"cs"`). */
|
|
6
|
+
readonly prefix: string;
|
|
7
|
+
/** Full namespace URI the prefix expands to. */
|
|
8
|
+
readonly namespace: string;
|
|
9
|
+
/** Number of OWL/RDFS classes defined in this namespace. */
|
|
10
|
+
readonly classCount: number;
|
|
11
|
+
/** Number of properties (object + datatype) defined in this namespace. */
|
|
12
|
+
readonly propertyCount: number;
|
|
13
|
+
readonly anatomyCount: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A class in an ontology's class hierarchy. */
|
|
17
|
+
export interface OntologyClass {
|
|
18
|
+
/** Full URI of the class. */
|
|
19
|
+
readonly uri: string;
|
|
20
|
+
/** Human-readable label (rdfs:label). */
|
|
21
|
+
readonly label: string;
|
|
22
|
+
/** URI of the direct superclass, if declared. */
|
|
23
|
+
readonly superclass?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A property in an ontology namespace. */
|
|
27
|
+
export interface OntologyProperty {
|
|
28
|
+
/** Full URI of the property. */
|
|
29
|
+
readonly uri: string;
|
|
30
|
+
/** Human-readable label (rdfs:label). */
|
|
31
|
+
readonly label: string;
|
|
32
|
+
/** URI of the domain class this property applies to, if declared. */
|
|
33
|
+
readonly domain?: string;
|
|
34
|
+
/** URI of the range class or datatype, if declared. */
|
|
35
|
+
readonly range?: string;
|
|
36
|
+
/** Whether this is an object property (links classes) or datatype property (links to literals). */
|
|
37
|
+
readonly type: "object" | "datatype";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Detailed view of a single ontology namespace from `ontology show`. */
|
|
41
|
+
export interface OntologyDetailed {
|
|
42
|
+
/** Short prefix alias (e.g., `"ds"`). */
|
|
43
|
+
readonly prefix: string;
|
|
44
|
+
/** Full namespace URI the prefix expands to. */
|
|
45
|
+
readonly namespace: string;
|
|
46
|
+
/** All classes defined in this namespace. */
|
|
47
|
+
readonly classes: readonly OntologyClass[];
|
|
48
|
+
/** All properties defined in this namespace. */
|
|
49
|
+
readonly properties: readonly OntologyProperty[];
|
|
50
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Boot-time state shared by CLI, MCP, and completions server. */
|
|
2
|
+
|
|
3
|
+
import type { Store } from "@canonical/ke";
|
|
4
|
+
import type { PragmaConfig } from "#config";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Boot-time state shared by CLI, MCP, and completions server.
|
|
8
|
+
*
|
|
9
|
+
* Four members:
|
|
10
|
+
* - `store` — the ke triple store, loaded and ready for SPARQL queries.
|
|
11
|
+
* - `config` — resolved pragma config (tier, channel).
|
|
12
|
+
* - `cwd` — working directory used for config and source resolution.
|
|
13
|
+
* - `dispose()` — tears down the store and frees WASM memory. Must be
|
|
14
|
+
* called exactly once; calling operations after dispose is undefined.
|
|
15
|
+
*/
|
|
16
|
+
export interface PragmaRuntime {
|
|
17
|
+
readonly store: Store;
|
|
18
|
+
readonly config: PragmaConfig;
|
|
19
|
+
readonly cwd: string;
|
|
20
|
+
dispose(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Standard domain return types. */
|
|
2
|
+
|
|
3
|
+
import type { URI } from "@canonical/ke";
|
|
4
|
+
|
|
5
|
+
/** Summary view of a design-system standard (guideline or rule). */
|
|
6
|
+
export interface StandardSummary {
|
|
7
|
+
/** Full RDF URI identifying this standard in the ke store. */
|
|
8
|
+
readonly uri: URI;
|
|
9
|
+
/** Human-readable name of the standard. */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
/** Category grouping (e.g., `"accessibility"`, `"layout"`). */
|
|
12
|
+
readonly category: string;
|
|
13
|
+
/** Brief prose description of what the standard prescribes. */
|
|
14
|
+
readonly description: string;
|
|
15
|
+
/** URI of the parent standard this one extends, if any. */
|
|
16
|
+
readonly extends?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Digest view of a standard for generic list contracts. */
|
|
20
|
+
export type StandardDigest = StandardSummary;
|
|
21
|
+
|
|
22
|
+
/** Fenced code block used in standard do/don't examples. */
|
|
23
|
+
export interface CodeBlock {
|
|
24
|
+
/** Programming language identifier for syntax highlighting. */
|
|
25
|
+
readonly language: string;
|
|
26
|
+
/** Source code content of the example. */
|
|
27
|
+
readonly code: string;
|
|
28
|
+
/** Optional explanatory caption displayed alongside the code. */
|
|
29
|
+
readonly caption?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Detailed view of a standard, extending the summary with code examples. */
|
|
33
|
+
export interface StandardDetailed extends StandardSummary {
|
|
34
|
+
/** Positive examples demonstrating correct usage. */
|
|
35
|
+
readonly dos: readonly CodeBlock[];
|
|
36
|
+
/** Negative examples demonstrating incorrect usage. */
|
|
37
|
+
readonly donts: readonly CodeBlock[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Summary of a standards category with its member count. */
|
|
41
|
+
export interface CategorySummary {
|
|
42
|
+
/** Category name (e.g., `"accessibility"`, `"layout"`). */
|
|
43
|
+
readonly name: string;
|
|
44
|
+
/** Number of standards belonging to this category. */
|
|
45
|
+
readonly standardCount: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Optional filters for narrowing the standards list. */
|
|
49
|
+
export interface StandardListFilters {
|
|
50
|
+
/** Restrict results to a single category name. */
|
|
51
|
+
readonly category?: string;
|
|
52
|
+
/** Free-text search term matched against standard names and descriptions. */
|
|
53
|
+
readonly search?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Lightweight reference to a standard, used in block cross-references. */
|
|
57
|
+
export interface StandardRef {
|
|
58
|
+
/** Human-readable standard name. */
|
|
59
|
+
readonly name: string;
|
|
60
|
+
/** Full RDF URI of the referenced standard. */
|
|
61
|
+
readonly uri: URI;
|
|
62
|
+
/** Category the referenced standard belongs to. */
|
|
63
|
+
readonly category: string;
|
|
64
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Tier hierarchy return types. */
|
|
2
|
+
|
|
3
|
+
import type { URI } from "@canonical/ke";
|
|
4
|
+
|
|
5
|
+
/** A single entry in the tier hierarchy (e.g., `global`, `apps`, `apps/lxd`). */
|
|
6
|
+
export interface TierEntry {
|
|
7
|
+
/** Full RDF URI identifying this tier in the ke store. */
|
|
8
|
+
readonly uri: URI;
|
|
9
|
+
/** Slash-separated tier path (e.g., `"apps/lxd"`). */
|
|
10
|
+
readonly path: string;
|
|
11
|
+
/** Path of the parent tier, if this is not the root. */
|
|
12
|
+
readonly parent?: string;
|
|
13
|
+
/** Nesting depth in the tier hierarchy (0 = root). */
|
|
14
|
+
readonly depth: number;
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Token domain return types. */
|
|
2
|
+
|
|
3
|
+
import type { URI } from "@canonical/ke";
|
|
4
|
+
|
|
5
|
+
/** Summary view of a design token. */
|
|
6
|
+
export interface TokenSummary {
|
|
7
|
+
/** Full RDF URI identifying this token in the ke store. */
|
|
8
|
+
readonly uri: URI;
|
|
9
|
+
/** Human-readable token name (e.g., `"color-primary"`). */
|
|
10
|
+
readonly name: string;
|
|
11
|
+
/** Token category (e.g., `"color"`, `"spacing"`, `"typography"`). */
|
|
12
|
+
readonly category: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Digest view of a token for generic list contracts. */
|
|
16
|
+
export type TokenDigest = TokenSummary;
|
|
17
|
+
|
|
18
|
+
/** Detailed view of a token, extending the summary with per-theme resolved values. */
|
|
19
|
+
export interface TokenDetailed extends TokenSummary {
|
|
20
|
+
/** Resolved values keyed by theme (e.g., `{ theme: "dark", value: "#fff" }`). */
|
|
21
|
+
readonly values: readonly { theme: string; value: string }[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Lightweight reference to a token (name + URI), used in cross-references. */
|
|
25
|
+
export interface TokenRef {
|
|
26
|
+
/** Human-readable token name. */
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/** Full RDF URI of the referenced token. */
|
|
29
|
+
readonly uri: URI;
|
|
30
|
+
}
|
|
@@ -11,11 +11,13 @@ 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";
|
|
15
16
|
import { selectFormatter } from "../../shared/formatters.js";
|
|
16
17
|
import { listFormatters } from "../formatters/index.js";
|
|
17
18
|
import type { StandardListOutput } from "../formatters/types.js";
|
|
18
19
|
import { resolveStandardList } from "../orchestration/index.js";
|
|
20
|
+
import { standardConfig } from "../standardConfig.js";
|
|
19
21
|
|
|
20
22
|
export default function buildListCommand(
|
|
21
23
|
ctx: PragmaContext,
|
|
@@ -75,6 +77,13 @@ export default function buildListCommand(
|
|
|
75
77
|
|
|
76
78
|
return createOutputResult(output, {
|
|
77
79
|
plain: selectFormatter(ctx, listFormatters),
|
|
80
|
+
ink: (data) =>
|
|
81
|
+
createListView({
|
|
82
|
+
heading: "Standards",
|
|
83
|
+
domain: "standard",
|
|
84
|
+
items: data.items,
|
|
85
|
+
columns: standardConfig.listColumns,
|
|
86
|
+
}),
|
|
78
87
|
});
|
|
79
88
|
},
|
|
80
89
|
};
|
|
@@ -10,9 +10,14 @@ import {
|
|
|
10
10
|
type CommandResult,
|
|
11
11
|
createOutputResult,
|
|
12
12
|
} from "@canonical/cli-core";
|
|
13
|
+
import { createLookupView } from "#tui";
|
|
13
14
|
import type { PragmaContext } from "../../shared/context.js";
|
|
14
15
|
import type { LookupResult } from "../../shared/contracts.js";
|
|
15
|
-
import {
|
|
16
|
+
import { renderLookupResults } from "../../shared/formatters.js";
|
|
17
|
+
import {
|
|
18
|
+
createInkLookupOptions,
|
|
19
|
+
lookupFormatters,
|
|
20
|
+
} from "../formatters/index.js";
|
|
16
21
|
import type { lookupStandard } from "../operations/index.js";
|
|
17
22
|
import { resolveStandardLookup } from "../orchestration/index.js";
|
|
18
23
|
|
|
@@ -60,7 +65,22 @@ export default function buildLookupCommand(
|
|
|
60
65
|
|
|
61
66
|
return createOutputResult<StandardLookupOutput>(
|
|
62
67
|
{ result: contract.result, detailed },
|
|
63
|
-
{
|
|
68
|
+
{
|
|
69
|
+
plain: ({ result, detailed: isDetailed }) =>
|
|
70
|
+
renderLookupResults({
|
|
71
|
+
ctx,
|
|
72
|
+
result,
|
|
73
|
+
formatters: lookupFormatters,
|
|
74
|
+
mapResult: (standard) => ({ standard, detailed: isDetailed }),
|
|
75
|
+
}),
|
|
76
|
+
ink: ({ result, detailed: isDetailed }) =>
|
|
77
|
+
createLookupView({
|
|
78
|
+
results: result.results,
|
|
79
|
+
errors: result.errors,
|
|
80
|
+
domain: "standard",
|
|
81
|
+
options: createInkLookupOptions(isDetailed),
|
|
82
|
+
}),
|
|
83
|
+
},
|
|
64
84
|
);
|
|
65
85
|
},
|
|
66
86
|
};
|
|
@@ -77,51 +97,3 @@ function normalizeNames(names: unknown, legacyName?: unknown): string[] {
|
|
|
77
97
|
}
|
|
78
98
|
return [];
|
|
79
99
|
}
|
|
80
|
-
|
|
81
|
-
function renderStandardLookupOutput(
|
|
82
|
-
ctx: PragmaContext,
|
|
83
|
-
): (data: StandardLookupOutput) => string {
|
|
84
|
-
return ({ result, detailed }) => {
|
|
85
|
-
const formatOne =
|
|
86
|
-
ctx.globalFlags.format === "json"
|
|
87
|
-
? lookupFormatters.json
|
|
88
|
-
: ctx.globalFlags.llm
|
|
89
|
-
? lookupFormatters.llm
|
|
90
|
-
: lookupFormatters.plain;
|
|
91
|
-
|
|
92
|
-
if (ctx.globalFlags.format === "json") {
|
|
93
|
-
if (result.results.length === 1 && result.errors.length === 0) {
|
|
94
|
-
const only = result.results[0];
|
|
95
|
-
return only
|
|
96
|
-
? lookupFormatters.json({ standard: only, detailed })
|
|
97
|
-
: JSON.stringify({ results: [], errors: result.errors }, null, 2);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return JSON.stringify(
|
|
101
|
-
{
|
|
102
|
-
results: result.results.map((standard) =>
|
|
103
|
-
JSON.parse(lookupFormatters.json({ standard, detailed })),
|
|
104
|
-
),
|
|
105
|
-
errors: result.errors,
|
|
106
|
-
},
|
|
107
|
-
null,
|
|
108
|
-
2,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const parts = result.results.map((standard) =>
|
|
113
|
-
formatOne({ standard, detailed }),
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
if (result.errors.length > 0) {
|
|
117
|
-
parts.push(
|
|
118
|
-
[
|
|
119
|
-
"Errors:",
|
|
120
|
-
...result.errors.map((error) => `- ${error.query}: ${error.message}`),
|
|
121
|
-
].join("\n"),
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return parts.join("\n\n");
|
|
126
|
-
};
|
|
127
|
-
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Formatters } from "../../shared/formatters.js";
|
|
11
|
-
import type { CategorySummary } from "../../shared/types.js";
|
|
11
|
+
import type { CategorySummary } from "../../shared/types/index.js";
|
|
12
12
|
|
|
13
13
|
const formatters: Formatters<readonly CategorySummary[]> = {
|
|
14
14
|
plain(categories) {
|
|
@@ -2,5 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
export { default as categoriesFormatters } from "./categories.js";
|
|
4
4
|
export { default as listFormatters } from "./list.js";
|
|
5
|
-
export {
|
|
5
|
+
export {
|
|
6
|
+
createInkLookupOptions,
|
|
7
|
+
default as lookupFormatters,
|
|
8
|
+
} from "./lookup.js";
|
|
6
9
|
export type { StandardListOutput, StandardLookupInput } from "./types.js";
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { URI } from "@canonical/ke";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
StandardDetailed,
|
|
5
|
+
StandardSummary,
|
|
6
|
+
} from "../../shared/types/index.js";
|
|
4
7
|
import formatters from "./list.js";
|
|
5
8
|
import type { StandardListOutput } from "./types.js";
|
|
6
9
|
|
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Three-mode formatter for `pragma standard list` output.
|
|
3
|
-
*
|
|
4
|
-
* - **plain** — terminal text with progressive disclosure.
|
|
5
|
-
* - **llm** — condensed Markdown consumed by LLM agents and reused
|
|
6
|
-
* by the MCP adapter when `condensed: true`.
|
|
7
|
-
* - **json** — structured JSON; enrichment varies by disclosure level.
|
|
8
|
-
*
|
|
9
|
-
* Disclosure levels:
|
|
10
|
-
* - summary — name + category + description (default)
|
|
11
|
-
* - digest — summary + first do example (truncated)
|
|
12
|
-
* - detailed — summary + full dos/donts
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
1
|
import type { Formatters } from "../../shared/formatters.js";
|
|
16
|
-
import type { CodeBlock, StandardDetailed } from "../../shared/types.js";
|
|
2
|
+
import type { CodeBlock, StandardDetailed } from "../../shared/types/index.js";
|
|
17
3
|
import type { StandardListOutput } from "./types.js";
|
|
18
4
|
|
|
19
5
|
// ---------------------------------------------------------------------------
|
|
@@ -22,6 +8,17 @@ import type { StandardListOutput } from "./types.js";
|
|
|
22
8
|
|
|
23
9
|
const DEFAULT_MAX_EXAMPLE_LENGTH = 120;
|
|
24
10
|
|
|
11
|
+
interface StandardListRow {
|
|
12
|
+
readonly uri: StandardDetailed["uri"];
|
|
13
|
+
readonly name: StandardDetailed["name"];
|
|
14
|
+
readonly category: StandardDetailed["category"];
|
|
15
|
+
readonly description: StandardDetailed["description"];
|
|
16
|
+
readonly extends?: StandardDetailed["extends"];
|
|
17
|
+
readonly example?: string;
|
|
18
|
+
readonly dos?: StandardDetailed["dos"];
|
|
19
|
+
readonly donts?: StandardDetailed["donts"];
|
|
20
|
+
}
|
|
21
|
+
|
|
25
22
|
function truncate(text: string, max: number): string {
|
|
26
23
|
const oneLine = text.replace(/\n/g, " ").trim();
|
|
27
24
|
return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max - 1)}…`;
|
|
@@ -40,82 +37,21 @@ function firstDoExample(
|
|
|
40
37
|
// Plain
|
|
41
38
|
// ---------------------------------------------------------------------------
|
|
42
39
|
|
|
43
|
-
function plain(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const cat = s.category ? ` [${s.category}]` : "";
|
|
48
|
-
lines.push(`${s.name}${cat}`);
|
|
49
|
-
lines.push(` ${s.description}`);
|
|
50
|
-
|
|
51
|
-
if (disclosure.level === "digest") {
|
|
52
|
-
const maxLen = disclosure.maxExampleLength ?? DEFAULT_MAX_EXAMPLE_LENGTH;
|
|
53
|
-
const example = firstDoExample(details?.[i], maxLen);
|
|
54
|
-
if (example) {
|
|
55
|
-
lines.push(` Example: ${example}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (disclosure.level === "detailed") {
|
|
60
|
-
const detail = details?.[i];
|
|
61
|
-
if (detail) {
|
|
62
|
-
if (detail.dos.length > 0) {
|
|
63
|
-
lines.push(" Do:");
|
|
64
|
-
for (const d of detail.dos) {
|
|
65
|
-
lines.push(` ${d.code}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (detail.donts.length > 0) {
|
|
69
|
-
lines.push(" Don't:");
|
|
70
|
-
for (const d of detail.donts) {
|
|
71
|
-
lines.push(` ${d.code}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return lines.join("\n");
|
|
40
|
+
function plain(output: StandardListOutput): string {
|
|
41
|
+
return buildRows(output)
|
|
42
|
+
.map((row) => formatPlainRow(row, output.disclosure.level))
|
|
43
|
+
.join("\n\n");
|
|
79
44
|
}
|
|
80
45
|
|
|
81
46
|
// ---------------------------------------------------------------------------
|
|
82
47
|
// LLM
|
|
83
48
|
// ---------------------------------------------------------------------------
|
|
84
49
|
|
|
85
|
-
function llm(
|
|
86
|
-
const lines
|
|
87
|
-
lines.push("## Standards");
|
|
88
|
-
lines.push("");
|
|
89
|
-
|
|
90
|
-
for (const [i, s] of items.entries()) {
|
|
91
|
-
const cat = s.category ? ` [${s.category}]` : "";
|
|
92
|
-
lines.push(`- **${s.name}**${cat}: ${s.description}`);
|
|
93
|
-
|
|
94
|
-
if (disclosure.level === "digest") {
|
|
95
|
-
const maxLen = disclosure.maxExampleLength ?? DEFAULT_MAX_EXAMPLE_LENGTH;
|
|
96
|
-
const example = firstDoExample(details?.[i], maxLen);
|
|
97
|
-
if (example) {
|
|
98
|
-
lines.push(` - Example: \`${example}\``);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
50
|
+
function llm(output: StandardListOutput): string {
|
|
51
|
+
const lines = ["## Standards", ""];
|
|
101
52
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (detail) {
|
|
105
|
-
if (detail.dos.length > 0) {
|
|
106
|
-
lines.push(" - **Do**:");
|
|
107
|
-
for (const d of detail.dos) {
|
|
108
|
-
lines.push(` - ${d.code}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (detail.donts.length > 0) {
|
|
112
|
-
lines.push(" - **Don't**:");
|
|
113
|
-
for (const d of detail.donts) {
|
|
114
|
-
lines.push(` - ${d.code}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
53
|
+
for (const row of buildRows(output)) {
|
|
54
|
+
lines.push(formatLlmRow(row, output.disclosure.level));
|
|
119
55
|
}
|
|
120
56
|
|
|
121
57
|
return lines.join("\n");
|
|
@@ -151,6 +87,98 @@ function json({ items, details, disclosure }: StandardListOutput): string {
|
|
|
151
87
|
// Export
|
|
152
88
|
// ---------------------------------------------------------------------------
|
|
153
89
|
|
|
90
|
+
/** Three-mode formatter for `pragma standard list` output. */
|
|
154
91
|
const formatters: Formatters<StandardListOutput> = { plain, llm, json };
|
|
155
92
|
|
|
156
93
|
export default formatters;
|
|
94
|
+
|
|
95
|
+
function buildRows({
|
|
96
|
+
items,
|
|
97
|
+
details,
|
|
98
|
+
disclosure,
|
|
99
|
+
}: StandardListOutput): readonly StandardListRow[] {
|
|
100
|
+
if (disclosure.level === "summary") {
|
|
101
|
+
return items;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (disclosure.level === "digest") {
|
|
105
|
+
const maxLen = disclosure.maxExampleLength ?? DEFAULT_MAX_EXAMPLE_LENGTH;
|
|
106
|
+
return items.map((item, index) => ({
|
|
107
|
+
...item,
|
|
108
|
+
extends: details?.[index]?.extends,
|
|
109
|
+
example: firstDoExample(details?.[index], maxLen),
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return items.map((item, index) => ({
|
|
114
|
+
...item,
|
|
115
|
+
extends: details?.[index]?.extends,
|
|
116
|
+
dos: details?.[index]?.dos,
|
|
117
|
+
donts: details?.[index]?.donts,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function formatPlainRow(
|
|
122
|
+
row: StandardListRow,
|
|
123
|
+
level: StandardListOutput["disclosure"]["level"],
|
|
124
|
+
): string {
|
|
125
|
+
const lines = [formatHeading(row), ` ${row.description}`];
|
|
126
|
+
|
|
127
|
+
if (row.extends) {
|
|
128
|
+
lines.push(` Extends: ${row.extends}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (level === "digest" && row.example) {
|
|
132
|
+
lines.push(` Example: ${row.example}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (level === "detailed") {
|
|
136
|
+
if (row.dos && row.dos.length > 0) {
|
|
137
|
+
lines.push(" Do:");
|
|
138
|
+
lines.push(...row.dos.map((item) => ` ${item.code}`));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (row.donts && row.donts.length > 0) {
|
|
142
|
+
lines.push(" Don't:");
|
|
143
|
+
lines.push(...row.donts.map((item) => ` ${item.code}`));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return lines.join("\n");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatLlmRow(
|
|
151
|
+
row: StandardListRow,
|
|
152
|
+
level: StandardListOutput["disclosure"]["level"],
|
|
153
|
+
): string {
|
|
154
|
+
const lines = [
|
|
155
|
+
`- **${row.name}**${row.category ? ` [${row.category}]` : ""}`,
|
|
156
|
+
];
|
|
157
|
+
lines.push(` ${row.description}`);
|
|
158
|
+
|
|
159
|
+
if (row.extends) {
|
|
160
|
+
lines.push(` Extends: ${row.extends}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (level === "digest" && row.example) {
|
|
164
|
+
lines.push(` Example: \`${row.example}\``);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (level === "detailed") {
|
|
168
|
+
if (row.dos && row.dos.length > 0) {
|
|
169
|
+
lines.push(" **Do**:");
|
|
170
|
+
lines.push(...row.dos.map((item) => ` - ${item.code}`));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (row.donts && row.donts.length > 0) {
|
|
174
|
+
lines.push(" **Don't**:");
|
|
175
|
+
lines.push(...row.donts.map((item) => ` - ${item.code}`));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return lines.join("\n");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function formatHeading(row: StandardListRow): string {
|
|
183
|
+
return row.category ? `${row.name} [${row.category}]` : row.name;
|
|
184
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { URI } from "@canonical/ke";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import type { StandardDetailed } from "../../shared/types.js";
|
|
3
|
+
import type { StandardDetailed } from "../../shared/types/index.js";
|
|
4
4
|
import formatters from "./lookup.js";
|
|
5
5
|
|
|
6
6
|
const DETAILED: StandardDetailed = {
|