@canonical/pragma-cli 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -35
- package/dist/pragma +0 -0
- package/package.json +15 -13
- package/src/completions/constants.ts +7 -0
- package/src/completions/handleQuery.test.ts +32 -0
- package/src/completions/handleQuery.ts +20 -1
- package/src/completions/queryCompletions.test.ts +5 -0
- package/src/completions/queryCompletions.ts +17 -3
- package/src/completions/querySocket.test.ts +31 -0
- package/src/completions/querySocket.ts +36 -9
- package/src/config/parseConfigValues.ts +2 -0
- package/src/config/readConfigLayers.test.ts +2 -0
- package/src/config/readConfigLayers.ts +3 -0
- package/src/config/types.ts +7 -0
- package/src/constants.ts +3 -1
- package/src/domains/block/commands/sample.ts +2 -1
- package/src/domains/block/formatters/list.test.ts +3 -3
- package/src/domains/block/formatters/lookup.test.ts +5 -5
- package/src/domains/block/orchestration/enrichBlocks.ts +6 -3
- package/src/domains/block/stories.ts +9 -3
- package/src/domains/config/commands/channel.test.ts +16 -0
- package/src/domains/config/commands/channel.ts +14 -1
- package/src/domains/config/commands/framework.test.ts +14 -0
- package/src/domains/config/commands/framework.ts +13 -1
- package/src/domains/config/commands/tier.test.ts +7 -0
- package/src/domains/config/commands/tier.ts +14 -1
- package/src/domains/config/operations/validateTier.test.ts +9 -0
- package/src/domains/config/operations/validateTier.ts +10 -2
- package/src/domains/create/generatorPack/bundled.ts +49 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.test.ts +159 -0
- package/src/domains/create/generatorPack/compileGeneratorPack.ts +312 -0
- package/src/domains/create/index.ts +13 -7
- package/src/domains/create/mcp/specs.ts +6 -161
- package/src/domains/create/renderGeneratorUi.test.ts +103 -0
- package/src/domains/create/renderGeneratorUi.ts +65 -0
- package/src/domains/doctor/formatters/doctor.test.ts +50 -2
- package/src/domains/doctor/formatters/doctor.ts +90 -38
- package/src/domains/doctor/operations/checks/checkMcpCommands.ts +9 -6
- package/src/domains/doctor/operations/checks/checkPackageRefs.ts +30 -12
- package/src/domains/doctor/operations/checks.test.ts +6 -4
- package/src/domains/doctor/operations/types.ts +23 -1
- package/src/domains/graph/commands/inspect.test.ts +2 -2
- package/src/domains/graph/commands/query.test.ts +5 -3
- package/src/domains/graph/formatters/inspect.ts +7 -7
- package/src/domains/graph/formatters/query.test.ts +87 -0
- package/src/domains/graph/formatters/query.ts +42 -5
- package/src/domains/graph/operations/inspectUri.test.ts +5 -5
- package/src/domains/graphql/commands/serve.test.ts +12 -6
- package/src/domains/graphql/commands/serve.ts +16 -6
- package/src/domains/graphql/helpers/parsePrefixes.test.ts +3 -3
- package/src/domains/graphql/helpers/parsePrefixes.ts +3 -3
- package/src/domains/graphql/helpers/resolveSourceFiles.test.ts +9 -0
- package/src/domains/graphql/helpers/resolveSourceFiles.ts +24 -3
- package/src/domains/graphql/operations/compileSchema.test.ts +2 -2
- package/src/domains/graphql/operations/createSchemaServer.test.ts +2 -2
- package/src/domains/graphql/operations/ensureSchemaArtifact.test.ts +167 -0
- package/src/domains/graphql/operations/ensureSchemaArtifact.ts +178 -0
- package/src/domains/graphql/operations/index.ts +5 -0
- package/src/domains/info/formatters/upgrade.test.ts +7 -4
- package/src/domains/info/formatters/upgrade.ts +3 -1
- package/src/domains/info/operations/collectEntityCounts.test.ts +156 -0
- package/src/domains/info/operations/collectEntityCounts.ts +123 -0
- package/src/domains/info/operations/index.ts +1 -0
- package/src/domains/info/types.ts +13 -0
- package/src/domains/llm/capabilities.test.ts +23 -3
- package/src/domains/llm/commands/capabilities.ts +12 -3
- package/src/domains/llm/commands/llm.ts +5 -1
- package/src/domains/llm/data/commandReference.ts +20 -11
- package/src/domains/llm/data/decisionTrees.ts +10 -5
- package/src/domains/llm/data/toolCatalog.ts +49 -18
- package/src/domains/llm/formatters/orientation.ts +5 -1
- package/src/domains/llm/llmCommand.test.ts +11 -3
- package/src/domains/llm/mcp/specs.ts +3 -4
- package/src/domains/llm/operations/collectContext.test.ts +64 -0
- package/src/domains/llm/operations/collectContext.ts +7 -17
- package/src/domains/modifier/commands/sample.ts +2 -1
- package/src/domains/modifier/operations/lookup.ts +5 -3
- package/src/domains/modifier/stories.ts +9 -0
- package/src/domains/ontology/commands/list.test.ts +5 -3
- package/src/domains/ontology/commands/show.test.ts +85 -9
- package/src/domains/ontology/formatters/index.ts +1 -0
- package/src/domains/ontology/formatters/list.ts +37 -12
- package/src/domains/ontology/formatters/show.ts +358 -67
- package/src/domains/ontology/helpers/buildClassTree.ts +89 -0
- package/src/domains/ontology/helpers/completeClassNames.ts +43 -0
- package/src/domains/ontology/helpers/expandOntologyIris.ts +79 -0
- package/src/domains/ontology/helpers/index.ts +14 -0
- package/src/domains/ontology/helpers/queryClasses.ts +32 -16
- package/src/domains/ontology/helpers/queryProperties.ts +39 -6
- package/src/domains/ontology/helpers/queryTboxFacts.ts +160 -0
- package/src/domains/ontology/operations/listOntologies.test.ts +80 -3
- package/src/domains/ontology/operations/listOntologies.ts +126 -30
- package/src/domains/ontology/operations/showOntology.test.ts +131 -12
- package/src/domains/ontology/operations/showOntology.ts +289 -7
- package/src/domains/ontology/operations/showOntologyRaw.test.ts +4 -2
- package/src/domains/ontology/stories.ts +53 -8
- package/src/domains/refs/operations/updateRefs.ts +0 -1
- package/src/domains/setup/commands/index.ts +1 -0
- package/src/domains/setup/commands/setup.ts +70 -0
- package/src/domains/setup/index.ts +7 -3
- package/src/domains/setup/operations/setupLsp.test.ts +3 -2
- package/src/domains/setup/operations/setupLsp.ts +7 -2
- package/src/domains/shared/bootStore.bareCore.test.ts +63 -0
- package/src/domains/shared/bootStore.test.ts +82 -0
- package/src/domains/shared/bootStore.ts +56 -20
- package/src/domains/shared/emptyErrorRecovery.test.ts +23 -26
- package/src/domains/shared/extractLocalName.test.ts +3 -3
- package/src/domains/shared/formatters.ts +23 -2
- package/src/domains/shared/listTiers.ts +40 -0
- package/src/domains/shared/loaders/bundledLoader.ts +48 -0
- package/src/domains/shared/loaders/gitLoader.ts +1 -2
- package/src/domains/shared/loaders/localLoader.ts +2 -18
- package/src/domains/shared/loaders/readPackageDir.test.ts +58 -0
- package/src/domains/shared/loaders/readPackageDir.ts +46 -8
- package/src/domains/shared/lookupMany.test.ts +15 -0
- package/src/domains/shared/lookupMany.ts +22 -6
- package/src/domains/shared/namespaces.ts +13 -5
- package/src/domains/shared/parseSampleCount.test.ts +47 -0
- package/src/domains/shared/parseSampleCount.ts +45 -0
- package/src/domains/shared/prefixes.test.ts +211 -0
- package/src/domains/shared/prefixes.ts +182 -15
- package/src/domains/shared/renderers.ts +5 -5
- package/src/domains/shared/resolveLookupExitCode.test.ts +48 -0
- package/src/domains/shared/resolveLookupExitCode.ts +42 -0
- package/src/domains/shared/semanticPackage.test.ts +89 -0
- package/src/domains/shared/semanticPackage.ts +17 -2
- package/src/domains/shared/stories/compileLookupCommand.ts +3 -1
- package/src/domains/shared/stories/compileLookupTool.ts +1 -1
- package/src/domains/shared/stories/pack/applyPackFilters.test.ts +49 -0
- package/src/domains/shared/stories/pack/applyPackFilters.ts +46 -11
- package/src/domains/shared/stories/pack/applyPackSearch.test.ts +57 -0
- package/src/domains/shared/stories/pack/applyPackSearch.ts +41 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.test.ts +30 -0
- package/src/domains/shared/stories/pack/buildLookupQuery.ts +75 -1
- package/src/domains/shared/stories/pack/bundled/index.ts +23 -0
- package/src/domains/shared/stories/pack/bundled/standardPack.ts +164 -0
- package/src/domains/shared/stories/pack/bundled/tierPack.ts +50 -0
- package/src/domains/shared/stories/pack/collectPackStories.test.ts +305 -7
- package/src/domains/shared/stories/pack/collectPackStories.ts +90 -8
- package/src/domains/shared/stories/pack/compilePackCommands.ts +17 -7
- package/src/domains/shared/stories/pack/compilePackStories.test.ts +607 -2
- package/src/domains/shared/stories/pack/compilePackStories.ts +544 -73
- package/src/domains/shared/stories/pack/compilePackToolSpecs.ts +20 -8
- package/src/domains/shared/stories/pack/index.ts +14 -0
- package/src/domains/shared/stories/pack/reservedVerbs.test.ts +213 -0
- package/src/domains/shared/stories/pack/reservedVerbs.ts +170 -0
- package/src/domains/shared/stories/pack/standardParity.test.ts +293 -220
- package/src/domains/shared/stories/pack/types.ts +123 -3
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.test.ts +502 -0
- package/src/domains/shared/stories/pack/validateStoryPackDefinition.ts +475 -24
- package/src/domains/shared/stories/types.ts +9 -1
- package/src/domains/shared/types/index.ts +5 -9
- package/src/domains/shared/types/ontology.ts +119 -24
- package/src/domains/shared/types/standard.ts +9 -52
- package/src/domains/token/commands/lookup.test.ts +8 -0
- package/src/domains/token/commands/lookup.ts +12 -0
- package/src/domains/token/commands/sample.ts +2 -1
- package/src/domains/token/featureFlag.ts +17 -0
- package/src/domains/token/index.ts +5 -0
- package/src/domains/token/mcp/specs.ts +13 -1
- package/src/domains/trace/commands/resolveTraceLimit.test.ts +31 -0
- package/src/domains/trace/commands/show.ts +30 -4
- package/src/index.ts +1 -13
- package/src/mcp/registerResources.test.ts +2 -2
- package/src/mcp/resources/pickPrimaryType.test.ts +5 -5
- package/src/mcp/tools/index.ts +18 -10
- package/src/mcp/tools/registerTools.test.ts +106 -30
- package/src/pipeline/collectCommands.test.ts +151 -1
- package/src/pipeline/collectCommands.ts +34 -19
- package/src/pipeline/createProgram.test.ts +5 -1
- package/src/pipeline/createProgram.ts +2 -2
- package/src/pipeline/firstRun.test.ts +80 -0
- package/src/pipeline/firstRun.ts +99 -0
- package/src/pipeline/parseGlobalFlags.test.ts +102 -1
- package/src/pipeline/parseGlobalFlags.ts +98 -9
- package/src/pipeline/resolveCommandKind.test.ts +12 -0
- package/src/pipeline/resolveCommandKind.ts +4 -0
- package/src/pipeline/rootHelp.test.ts +87 -0
- package/src/pipeline/rootHelp.ts +265 -0
- package/src/pipeline/runCli.orchestration.test.ts +28 -12
- package/src/pipeline/runCli.test.ts +17 -4
- package/src/pipeline/runCli.ts +162 -19
- package/src/testing/cli.ts +30 -2
- package/src/testing/dsFixtures.ts +21 -4
- package/src/testing/index.ts +1 -4
- package/src/testing/integration/__snapshots__/executor-golden.test.ts.snap +4 -4
- package/src/testing/integration/cli-user-stories.test.ts +63 -0
- package/src/testing/integration/cross-binary-byte-equality.test.ts +2 -2
- package/src/testing/integration/edge-cases.test.ts +62 -50
- package/src/testing/integration/executor-execution.test.ts +6 -4
- package/src/testing/integration/executor-golden.test.ts +2 -2
- package/src/testing/integration/mcp-surface.test.ts +40 -15
- package/src/testing/integration/parity.test.ts +119 -70
- package/src/testing/setupXdgIsolation.ts +16 -5
- package/src/testing/standardParityFixtures.ts +26 -83
- package/src/testing/store.ts +4 -4
- package/src/domains/create/commands/component.test.ts +0 -166
- package/src/domains/create/commands/component.ts +0 -123
- package/src/domains/create/commands/index.ts +0 -3
- package/src/domains/create/commands/package.test.ts +0 -35
- package/src/domains/create/commands/package.ts +0 -21
- package/src/domains/create/generators.ts +0 -8
- package/src/domains/standard/commands/categories.test.ts +0 -70
- package/src/domains/standard/commands/categories.ts +0 -48
- package/src/domains/standard/commands/index.ts +0 -6
- package/src/domains/standard/commands/list.test.ts +0 -105
- package/src/domains/standard/commands/list.ts +0 -11
- package/src/domains/standard/commands/lookup.test.ts +0 -135
- package/src/domains/standard/commands/lookup.ts +0 -14
- package/src/domains/standard/commands/sample.ts +0 -57
- package/src/domains/standard/formatters/categories.test.ts +0 -44
- package/src/domains/standard/formatters/categories.ts +0 -38
- package/src/domains/standard/formatters/index.ts +0 -10
- package/src/domains/standard/formatters/list.test.ts +0 -169
- package/src/domains/standard/formatters/list.ts +0 -184
- package/src/domains/standard/formatters/lookup.test.ts +0 -95
- package/src/domains/standard/formatters/lookup.ts +0 -101
- package/src/domains/standard/formatters/sample.ts +0 -15
- package/src/domains/standard/formatters/types.ts +0 -31
- package/src/domains/standard/index.ts +0 -40
- package/src/domains/standard/mcp/index.ts +0 -2
- package/src/domains/standard/mcp/specs.ts +0 -89
- package/src/domains/standard/operations/categories.test.ts +0 -61
- package/src/domains/standard/operations/categories.ts +0 -38
- package/src/domains/standard/operations/index.ts +0 -6
- package/src/domains/standard/operations/list.test.ts +0 -75
- package/src/domains/standard/operations/list.ts +0 -64
- package/src/domains/standard/operations/lookup.test.ts +0 -58
- package/src/domains/standard/operations/lookup.ts +0 -151
- package/src/domains/standard/operations/sample.ts +0 -35
- package/src/domains/standard/orchestration/buildStandardFilters.ts +0 -11
- package/src/domains/standard/orchestration/index.ts +0 -4
- package/src/domains/standard/orchestration/resolveStandardList.ts +0 -45
- package/src/domains/standard/orchestration/resolveStandardLookup.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.test.ts +0 -33
- package/src/domains/standard/orchestration/standardEmptyError.ts +0 -29
- package/src/domains/standard/standardConfig.ts +0 -42
- package/src/domains/standard/stories.test.ts +0 -24
- package/src/domains/standard/stories.ts +0 -151
- package/src/domains/standard/types.ts +0 -15
- package/src/domains/tier/commands/index.ts +0 -3
- package/src/domains/tier/commands/list.test.ts +0 -67
- package/src/domains/tier/commands/list.ts +0 -9
- package/src/domains/tier/formatters/index.ts +0 -3
- package/src/domains/tier/formatters/list.test.ts +0 -33
- package/src/domains/tier/formatters/list.ts +0 -45
- package/src/domains/tier/index.ts +0 -17
- package/src/domains/tier/mcp/index.ts +0 -2
- package/src/domains/tier/mcp/specs.ts +0 -12
- package/src/domains/tier/operations/index.ts +0 -3
- package/src/domains/tier/operations/list.test.ts +0 -32
- package/src/domains/tier/operations/list.ts +0 -33
- package/src/domains/tier/stories.test.ts +0 -22
- package/src/domains/tier/stories.ts +0 -52
|
@@ -2,7 +2,7 @@ import type { Store } from "@canonical/ke";
|
|
|
2
2
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
3
3
|
import { PragmaError } from "#error";
|
|
4
4
|
import { createTestStore, DS_ALL_TTL } from "#testing";
|
|
5
|
-
import {
|
|
5
|
+
import { DEFAULT_PREFIX_MAP } from "../../shared/prefixes.js";
|
|
6
6
|
import showOntology from "./showOntology.js";
|
|
7
7
|
|
|
8
8
|
let store: Store;
|
|
@@ -17,31 +17,85 @@ beforeAll(async () => {
|
|
|
17
17
|
afterAll(() => cleanup());
|
|
18
18
|
|
|
19
19
|
describe("showOntology", () => {
|
|
20
|
-
it("returns classes
|
|
20
|
+
it("returns classes with attached properties for a prefix", async () => {
|
|
21
21
|
const result = await showOntology(store, "ds");
|
|
22
22
|
expect(result.prefix).toBe("ds");
|
|
23
|
-
expect(result.namespace).toBe(
|
|
23
|
+
expect(result.namespace).toBe(DEFAULT_PREFIX_MAP.ds);
|
|
24
24
|
expect(result.classes.length).toBeGreaterThan(0);
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
const uiBlock = result.classes.find((c) => c.iri === "ds:UIBlock");
|
|
27
|
+
expect(uiBlock).toBeDefined();
|
|
28
|
+
expect(uiBlock?.properties.map((p) => p.label)).toContain("name");
|
|
26
29
|
});
|
|
27
30
|
|
|
28
|
-
it("
|
|
31
|
+
it("preserves all subClassOf edges as compact IRIs", async () => {
|
|
29
32
|
const result = await showOntology(store, "ds");
|
|
30
33
|
const component = result.classes.find((c) => c.label === "Component");
|
|
31
34
|
expect(component).toBeDefined();
|
|
32
|
-
expect(component?.
|
|
35
|
+
expect(component?.subClassOf).toContain("ds:UIBlock");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("orders classes topologically (parents before children)", async () => {
|
|
39
|
+
const result = await showOntology(store, "ds");
|
|
40
|
+
const order = result.classes.map((c) => c.iri);
|
|
41
|
+
expect(order.indexOf("ds:UIBlock")).toBeLessThan(
|
|
42
|
+
order.indexOf("ds:Component"),
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("includes rdfs:comment when declared", async () => {
|
|
47
|
+
const result = await showOntology(store, "ds");
|
|
48
|
+
const component = result.classes.find((c) => c.iri === "ds:Component");
|
|
49
|
+
expect(component?.comment).toBe("A reusable UI building block.");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("counts instances per class", async () => {
|
|
53
|
+
const result = await showOntology(store, "ds");
|
|
54
|
+
const component = result.classes.find((c) => c.iri === "ds:Component");
|
|
55
|
+
expect(component?.instances).toBeGreaterThan(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("marks functional properties", async () => {
|
|
59
|
+
const result = await showOntology(store, "ds");
|
|
60
|
+
const uiBlock = result.classes.find((c) => c.iri === "ds:UIBlock");
|
|
61
|
+
const tier = uiBlock?.properties.find((p) => p.iri === "ds:tier");
|
|
62
|
+
expect(tier?.functional).toBe(true);
|
|
33
63
|
});
|
|
34
64
|
|
|
35
|
-
it("
|
|
65
|
+
it("attaches property domain and range as compact IRIs", async () => {
|
|
36
66
|
const result = await showOntology(store, "ds");
|
|
37
|
-
const
|
|
67
|
+
const uiBlock = result.classes.find((c) => c.iri === "ds:UIBlock");
|
|
68
|
+
const nameProp = uiBlock?.properties.find((p) => p.iri === "ds:name");
|
|
38
69
|
expect(nameProp).toBeDefined();
|
|
39
|
-
expect(nameProp?.domain).
|
|
40
|
-
expect(nameProp?.
|
|
70
|
+
expect(nameProp?.domain).toBe("ds:UIBlock");
|
|
71
|
+
expect(nameProp?.kind).toBe("datatype");
|
|
72
|
+
expect(nameProp?.range).toBe("xsd:string");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("emits a prefixes map covering the compact IRIs used", async () => {
|
|
76
|
+
const result = await showOntology(store, "ds");
|
|
77
|
+
expect(result.prefixes.ds).toBe(DEFAULT_PREFIX_MAP.ds);
|
|
78
|
+
expect(result.prefixes.xsd).toBeDefined();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("surfaces the owl:Ontology header", async () => {
|
|
82
|
+
const result = await showOntology(store, "ds");
|
|
83
|
+
expect(result.meta?.title).toBe("Design System Ontology");
|
|
84
|
+
expect(result.meta?.version).toBe("0.1.0-test");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("summarizes SHACL node shapes", async () => {
|
|
88
|
+
const result = await showOntology(store, "ds");
|
|
89
|
+
const shape = result.constraints?.find(
|
|
90
|
+
(c) => c.shape === "ds:ComponentShape",
|
|
91
|
+
);
|
|
92
|
+
expect(shape).toBeDefined();
|
|
93
|
+
expect(shape?.targetClass).toBe("ds:Component");
|
|
94
|
+
expect(shape?.propertyCount).toBe(2);
|
|
41
95
|
});
|
|
42
96
|
|
|
43
97
|
it("resolves full namespace URI", async () => {
|
|
44
|
-
const result = await showOntology(store,
|
|
98
|
+
const result = await showOntology(store, DEFAULT_PREFIX_MAP.ds);
|
|
45
99
|
expect(result.prefix).toBe("ds");
|
|
46
100
|
expect(result.classes.length).toBeGreaterThan(0);
|
|
47
101
|
});
|
|
@@ -59,7 +113,72 @@ describe("showOntology", () => {
|
|
|
59
113
|
it("returns cs ontology with standard classes", async () => {
|
|
60
114
|
const result = await showOntology(store, "cs");
|
|
61
115
|
expect(result.prefix).toBe("cs");
|
|
62
|
-
const standard = result.classes.find((c) => c.
|
|
116
|
+
const standard = result.classes.find((c) => c.iri.includes("CodeStandard"));
|
|
63
117
|
expect(standard).toBeDefined();
|
|
64
118
|
});
|
|
65
119
|
});
|
|
120
|
+
|
|
121
|
+
describe("showOntology — class focus", () => {
|
|
122
|
+
it("deep-dives into a class by label", async () => {
|
|
123
|
+
const result = await showOntology(store, "ds", { class: "Component" });
|
|
124
|
+
const focus = result.focus;
|
|
125
|
+
expect(focus).toBeDefined();
|
|
126
|
+
expect(focus?.iri).toBe("ds:Component");
|
|
127
|
+
expect(focus?.superChain).toEqual(["ds:UIBlock"]);
|
|
128
|
+
expect(focus?.instances).toBeGreaterThan(0);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("separates direct and inherited properties", async () => {
|
|
132
|
+
const result = await showOntology(store, "ds", { class: "Component" });
|
|
133
|
+
const inherited = result.focus?.inheritedProperties.map((p) => p.iri);
|
|
134
|
+
expect(inherited).toContain("ds:name");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("finds reverse references (properties ranging over the class)", async () => {
|
|
138
|
+
const result = await showOntology(store, "ds", { class: "UIBlock" });
|
|
139
|
+
const refs = result.focus?.referencedBy.map((p) => p.iri);
|
|
140
|
+
expect(refs).toContain("ds:implementsBlock");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("returns sample instance IRIs", async () => {
|
|
144
|
+
const result = await showOntology(store, "ds", { class: "Component" });
|
|
145
|
+
expect(result.focus?.sampleInstances.length).toBeGreaterThan(0);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("emits runnable follow-up queries with compact IRIs", async () => {
|
|
149
|
+
const result = await showOntology(store, "ds", { class: "Component" });
|
|
150
|
+
const queries = result.focus?.queries ?? [];
|
|
151
|
+
expect(queries.length).toBeGreaterThan(0);
|
|
152
|
+
expect(queries[0]?.sparql).toBe(
|
|
153
|
+
"SELECT ?instance WHERE { ?instance a ds:Component } LIMIT 20",
|
|
154
|
+
);
|
|
155
|
+
// A sample-instance inspection query is included when instances exist.
|
|
156
|
+
expect(queries.some((q) => q.sparql.includes("?predicate ?object"))).toBe(
|
|
157
|
+
true,
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("accepts a compact IRI and a local name", async () => {
|
|
162
|
+
const byIri = await showOntology(store, "ds", { class: "ds:Component" });
|
|
163
|
+
expect(byIri.focus?.iri).toBe("ds:Component");
|
|
164
|
+
|
|
165
|
+
const byLocal = await showOntology(store, "ds", { class: "component" });
|
|
166
|
+
expect(byLocal.focus?.iri).toBe("ds:Component");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("throws not-found with suggestions for an unknown class", async () => {
|
|
170
|
+
try {
|
|
171
|
+
await showOntology(store, "ds", { class: "Comp" });
|
|
172
|
+
expect.unreachable("should have thrown");
|
|
173
|
+
} catch (e) {
|
|
174
|
+
expect((e as PragmaError).code).toBe("ENTITY_NOT_FOUND");
|
|
175
|
+
expect((e as PragmaError).suggestions).toContain("Component");
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("still returns the full namespace structure alongside the focus", async () => {
|
|
180
|
+
const result = await showOntology(store, "ds", { class: "Component" });
|
|
181
|
+
expect(result.classes.length).toBeGreaterThan(0);
|
|
182
|
+
expect(result.focus).toBeDefined();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
import type { Store } from "@canonical/ke";
|
|
2
2
|
import { PragmaError } from "#error";
|
|
3
|
-
import
|
|
3
|
+
import compactUri from "../../shared/compactUri.js";
|
|
4
|
+
import { PREFIX_MAP } from "../../shared/prefixes.js";
|
|
5
|
+
import type {
|
|
6
|
+
OntologyClass,
|
|
7
|
+
OntologyClassFocus,
|
|
8
|
+
OntologyConstraint,
|
|
9
|
+
OntologyDetailed,
|
|
10
|
+
OntologyMeta,
|
|
11
|
+
OntologyProperty,
|
|
12
|
+
OntologyQueryHint,
|
|
13
|
+
} from "../../shared/types/index.js";
|
|
14
|
+
import buildClassTree, { flattenClassTree } from "../helpers/buildClassTree.js";
|
|
4
15
|
import queryClasses from "../helpers/queryClasses.js";
|
|
16
|
+
import type { RawOntologyProperty } from "../helpers/queryProperties.js";
|
|
5
17
|
import queryProperties from "../helpers/queryProperties.js";
|
|
18
|
+
import {
|
|
19
|
+
queryConstraints,
|
|
20
|
+
queryInstanceCounts,
|
|
21
|
+
queryOntologyMeta,
|
|
22
|
+
querySampleInstances,
|
|
23
|
+
} from "../helpers/queryTboxFacts.js";
|
|
6
24
|
import resolvePrefix from "../helpers/resolvePrefix.js";
|
|
7
25
|
|
|
26
|
+
/** Options for {@link showOntology}. */
|
|
27
|
+
export interface ShowOntologyOptions {
|
|
28
|
+
/** Class to deep-dive into (label, local name, or compact IRI). */
|
|
29
|
+
readonly class?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
8
32
|
/**
|
|
9
|
-
* Returns
|
|
33
|
+
* Returns the complete TBox structure for a namespace: the class hierarchy
|
|
34
|
+
* (topologically ordered, direct properties attached, instance counts),
|
|
35
|
+
* unattached properties, SHACL constraint summaries, the owl:Ontology
|
|
36
|
+
* header, and — when `options.class` is given — a per-class deep dive.
|
|
37
|
+
*
|
|
38
|
+
* This is the **single** structure every output mode projects; plain,
|
|
39
|
+
* `--llm`, `--format json`, and the MCP envelope must render from it and
|
|
40
|
+
* nothing else. All IRIs are compacted against the store's prefix map,
|
|
41
|
+
* which is included in the result for lossless expansion.
|
|
10
42
|
*
|
|
11
43
|
* Accepts a short prefix (e.g. `ds`) or a full namespace URI.
|
|
12
44
|
*
|
|
@@ -14,20 +46,29 @@ import resolvePrefix from "../helpers/resolvePrefix.js";
|
|
|
14
46
|
*
|
|
15
47
|
* @param store - The ke store to query.
|
|
16
48
|
* @param prefixOrUri - A short prefix or full namespace URI.
|
|
49
|
+
* @param options - Optional class focus.
|
|
17
50
|
* @returns An {@link OntologyDetailed} with classes and properties.
|
|
18
51
|
* @throws PragmaError.invalidInput if the prefix or namespace is unknown.
|
|
19
|
-
* @throws PragmaError.notFound if the namespace has no classes or
|
|
52
|
+
* @throws PragmaError.notFound if the namespace has no classes or
|
|
53
|
+
* properties, or the focused class does not exist.
|
|
20
54
|
*/
|
|
21
55
|
export default async function showOntology(
|
|
22
56
|
store: Store,
|
|
23
57
|
prefixOrUri: string,
|
|
58
|
+
options: ShowOntologyOptions = {},
|
|
24
59
|
): Promise<OntologyDetailed> {
|
|
25
60
|
const { prefix, namespace } = resolvePrefix(prefixOrUri, store.prefixes);
|
|
26
61
|
|
|
27
|
-
const
|
|
28
|
-
|
|
62
|
+
const [rawClasses, rawProperties, instanceCounts, rawConstraints, rawMeta] =
|
|
63
|
+
await Promise.all([
|
|
64
|
+
queryClasses(store, namespace),
|
|
65
|
+
queryProperties(store, namespace),
|
|
66
|
+
queryInstanceCounts(store, namespace),
|
|
67
|
+
queryConstraints(store, namespace),
|
|
68
|
+
queryOntologyMeta(store, namespace),
|
|
69
|
+
]);
|
|
29
70
|
|
|
30
|
-
if (
|
|
71
|
+
if (rawClasses.length === 0 && rawProperties.length === 0) {
|
|
31
72
|
throw PragmaError.notFound("ontology", prefixOrUri, {
|
|
32
73
|
recovery: {
|
|
33
74
|
message: "List loaded ontologies.",
|
|
@@ -37,5 +78,246 @@ export default async function showOntology(
|
|
|
37
78
|
});
|
|
38
79
|
}
|
|
39
80
|
|
|
40
|
-
|
|
81
|
+
// Core RDF vocabularies (xsd etc.) plus the store's resolved package
|
|
82
|
+
// prefixes; the store map wins on collisions.
|
|
83
|
+
const compactionMap: Readonly<Record<string, string>> = {
|
|
84
|
+
...PREFIX_MAP,
|
|
85
|
+
...store.prefixes,
|
|
86
|
+
};
|
|
87
|
+
const usedPrefixes = new Set<string>([prefix]);
|
|
88
|
+
const compact = (uri: string): string => {
|
|
89
|
+
const compacted = compactUri(uri, compactionMap);
|
|
90
|
+
const colon = compacted.indexOf(":");
|
|
91
|
+
if (compacted !== uri && colon > 0) {
|
|
92
|
+
usedPrefixes.add(compacted.slice(0, colon));
|
|
93
|
+
}
|
|
94
|
+
return compacted;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const toProperty = (raw: RawOntologyProperty): OntologyProperty => ({
|
|
98
|
+
iri: compact(raw.uri),
|
|
99
|
+
label: raw.label,
|
|
100
|
+
kind: raw.kind,
|
|
101
|
+
...(raw.domain ? { domain: compact(raw.domain) } : {}),
|
|
102
|
+
...(raw.range ? { range: compact(raw.range) } : {}),
|
|
103
|
+
...(raw.functional ? { functional: true as const } : {}),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Attach properties to their domain class; the rest are unattached.
|
|
107
|
+
const classUris = new Set(rawClasses.map((c) => c.uri));
|
|
108
|
+
const byDomain = new Map<string, OntologyProperty[]>();
|
|
109
|
+
const unattached: OntologyProperty[] = [];
|
|
110
|
+
for (const raw of rawProperties) {
|
|
111
|
+
if (raw.domain !== undefined && classUris.has(raw.domain)) {
|
|
112
|
+
const attached = byDomain.get(raw.domain) ?? [];
|
|
113
|
+
attached.push(toProperty(raw));
|
|
114
|
+
byDomain.set(raw.domain, attached);
|
|
115
|
+
} else {
|
|
116
|
+
unattached.push(toProperty(raw));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const unordered: OntologyClass[] = rawClasses.map((raw) => {
|
|
121
|
+
const instances = instanceCounts.get(raw.uri) ?? 0;
|
|
122
|
+
return {
|
|
123
|
+
iri: compact(raw.uri),
|
|
124
|
+
label: raw.label,
|
|
125
|
+
...(raw.comment ? { comment: raw.comment } : {}),
|
|
126
|
+
subClassOf: raw.subClassOf.map(compact),
|
|
127
|
+
...(instances > 0 ? { instances } : {}),
|
|
128
|
+
properties: (byDomain.get(raw.uri) ?? []).sort((a, b) =>
|
|
129
|
+
a.label.localeCompare(b.label),
|
|
130
|
+
),
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Canonical ordering: pre-order walk of the tree every renderer shares.
|
|
135
|
+
const classes = flattenClassTree(buildClassTree(unordered));
|
|
136
|
+
|
|
137
|
+
const constraints: OntologyConstraint[] = rawConstraints.map((raw) => ({
|
|
138
|
+
shape: compact(raw.shape),
|
|
139
|
+
...(raw.targetClass ? { targetClass: compact(raw.targetClass) } : {}),
|
|
140
|
+
propertyCount: raw.propertyCount,
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
const meta: OntologyMeta | undefined = rawMeta
|
|
144
|
+
? {
|
|
145
|
+
...(rawMeta.title ? { title: rawMeta.title } : {}),
|
|
146
|
+
...(rawMeta.version ? { version: rawMeta.version } : {}),
|
|
147
|
+
...(rawMeta.imports.length > 0
|
|
148
|
+
? { imports: rawMeta.imports.map(compact) }
|
|
149
|
+
: {}),
|
|
150
|
+
}
|
|
151
|
+
: undefined;
|
|
152
|
+
|
|
153
|
+
const focus = options.class
|
|
154
|
+
? await buildFocus(store, options.class, {
|
|
155
|
+
prefix,
|
|
156
|
+
namespace,
|
|
157
|
+
classes,
|
|
158
|
+
unattached,
|
|
159
|
+
constraints,
|
|
160
|
+
instanceCounts,
|
|
161
|
+
rawClassUris: rawClasses.map((c) => c.uri),
|
|
162
|
+
compactionMap,
|
|
163
|
+
})
|
|
164
|
+
: undefined;
|
|
165
|
+
|
|
166
|
+
const prefixes = Object.fromEntries(
|
|
167
|
+
[...usedPrefixes]
|
|
168
|
+
.sort()
|
|
169
|
+
.map((p) => [p, compactionMap[p]])
|
|
170
|
+
.filter(([, ns]) => ns !== undefined),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
prefix,
|
|
175
|
+
namespace,
|
|
176
|
+
prefixes,
|
|
177
|
+
...(meta ? { meta } : {}),
|
|
178
|
+
classes,
|
|
179
|
+
unattached: unattached.sort((a, b) => a.label.localeCompare(b.label)),
|
|
180
|
+
...(constraints.length > 0 ? { constraints } : {}),
|
|
181
|
+
...(focus ? { focus } : {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
interface FocusContext {
|
|
186
|
+
readonly prefix: string;
|
|
187
|
+
readonly namespace: string;
|
|
188
|
+
readonly classes: readonly OntologyClass[];
|
|
189
|
+
readonly unattached: readonly OntologyProperty[];
|
|
190
|
+
readonly constraints: readonly OntologyConstraint[];
|
|
191
|
+
readonly instanceCounts: ReadonlyMap<string, number>;
|
|
192
|
+
readonly rawClassUris: readonly string[];
|
|
193
|
+
readonly compactionMap: Readonly<Record<string, string>>;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Assemble the per-class deep dive from the already-loaded structure plus
|
|
198
|
+
* one sample-instances query. Everything else (chain, subclasses, reverse
|
|
199
|
+
* references, inherited properties) derives from the namespace data.
|
|
200
|
+
*/
|
|
201
|
+
async function buildFocus(
|
|
202
|
+
store: Store,
|
|
203
|
+
classQuery: string,
|
|
204
|
+
ctx: FocusContext,
|
|
205
|
+
): Promise<OntologyClassFocus> {
|
|
206
|
+
const needle = classQuery.toLowerCase();
|
|
207
|
+
const target = ctx.classes.find(
|
|
208
|
+
(c) =>
|
|
209
|
+
c.iri.toLowerCase() === needle ||
|
|
210
|
+
c.label.toLowerCase() === needle ||
|
|
211
|
+
localName(c.iri).toLowerCase() === needle,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if (!target) {
|
|
215
|
+
throw PragmaError.notFound("class", classQuery, {
|
|
216
|
+
suggestions: ctx.classes
|
|
217
|
+
.map((c) => localName(c.iri))
|
|
218
|
+
.filter((name) => name.toLowerCase().includes(needle))
|
|
219
|
+
.slice(0, 5),
|
|
220
|
+
recovery: {
|
|
221
|
+
message: "Show the namespace's class hierarchy.",
|
|
222
|
+
cli: `pragma ontology show ${ctx.prefix}`,
|
|
223
|
+
mcp: { tool: "ontology_show", params: { prefix: ctx.prefix } },
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const byIri = new Map(ctx.classes.map((c) => [c.iri, c]));
|
|
229
|
+
|
|
230
|
+
// Superclass chain within the namespace, nearest first, cycle-safe.
|
|
231
|
+
const superChain: string[] = [];
|
|
232
|
+
let cursor = target.subClassOf.find((p) => byIri.has(p));
|
|
233
|
+
while (cursor !== undefined && !superChain.includes(cursor)) {
|
|
234
|
+
superChain.push(cursor);
|
|
235
|
+
cursor = byIri.get(cursor)?.subClassOf.find((p) => byIri.has(p));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const inheritedProperties = superChain.flatMap(
|
|
239
|
+
(iri) => byIri.get(iri)?.properties ?? [],
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const allProperties = [
|
|
243
|
+
...ctx.classes.flatMap((c) => c.properties),
|
|
244
|
+
...ctx.unattached,
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
// The focused class's full URI is recoverable from the raw list (same
|
|
248
|
+
// index space as ctx.classes before ordering is irrelevant — match by
|
|
249
|
+
// compacted form).
|
|
250
|
+
const fullUri =
|
|
251
|
+
ctx.rawClassUris.find(
|
|
252
|
+
(uri) => compactUri(uri, ctx.compactionMap) === target.iri,
|
|
253
|
+
) ?? target.iri;
|
|
254
|
+
|
|
255
|
+
const sampleInstances = (await querySampleInstances(store, fullUri)).map(
|
|
256
|
+
(uri) => compactUri(uri, ctx.compactionMap),
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const referencedBy = allProperties
|
|
260
|
+
.filter((p) => p.range === target.iri && p.domain !== target.iri)
|
|
261
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
iri: target.iri,
|
|
265
|
+
label: target.label,
|
|
266
|
+
...(target.comment ? { comment: target.comment } : {}),
|
|
267
|
+
superChain,
|
|
268
|
+
subclasses: ctx.classes
|
|
269
|
+
.filter((c) => c.subClassOf.includes(target.iri))
|
|
270
|
+
.map((c) => c.iri)
|
|
271
|
+
.sort(),
|
|
272
|
+
instances: ctx.instanceCounts.get(fullUri) ?? 0,
|
|
273
|
+
directProperties: target.properties,
|
|
274
|
+
inheritedProperties,
|
|
275
|
+
referencedBy,
|
|
276
|
+
sampleInstances,
|
|
277
|
+
queries: buildQueryHints(target, referencedBy, sampleInstances),
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Ready-to-run SPARQL for the natural follow-ups from a class deep dive.
|
|
283
|
+
* Compact IRIs are used throughout — `pragma graph query` resolves them
|
|
284
|
+
* against the store's prefix map, so each hint is directly pasteable.
|
|
285
|
+
*/
|
|
286
|
+
function buildQueryHints(
|
|
287
|
+
target: OntologyClass,
|
|
288
|
+
referencedBy: readonly OntologyProperty[],
|
|
289
|
+
sampleInstances: readonly string[],
|
|
290
|
+
): OntologyQueryHint[] {
|
|
291
|
+
const hints: OntologyQueryHint[] = [
|
|
292
|
+
{
|
|
293
|
+
label: `instances of ${target.iri}`,
|
|
294
|
+
sparql: `SELECT ?instance WHERE { ?instance a ${target.iri} } LIMIT 20`,
|
|
295
|
+
},
|
|
296
|
+
];
|
|
297
|
+
|
|
298
|
+
const relation =
|
|
299
|
+
target.properties.find((p) => p.kind === "object") ??
|
|
300
|
+
referencedBy.find((p) => p.kind === "object");
|
|
301
|
+
if (relation) {
|
|
302
|
+
hints.push({
|
|
303
|
+
label: `traverse ${relation.iri}`,
|
|
304
|
+
sparql: `SELECT ?from ?to WHERE { ?from ${relation.iri} ?to } LIMIT 10`,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const sample = sampleInstances[0];
|
|
309
|
+
if (sample !== undefined) {
|
|
310
|
+
hints.push({
|
|
311
|
+
label: `everything about ${sample}`,
|
|
312
|
+
sparql: `SELECT ?predicate ?object WHERE { ${sample} ?predicate ?object }`,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return hints;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function localName(iri: string): string {
|
|
320
|
+
const colon = iri.lastIndexOf(":");
|
|
321
|
+
const slash = iri.lastIndexOf("/");
|
|
322
|
+
return iri.slice(Math.max(colon, slash) + 1);
|
|
41
323
|
}
|
|
@@ -2,7 +2,7 @@ import type { Store } from "@canonical/ke";
|
|
|
2
2
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
3
3
|
import { PragmaError } from "#error";
|
|
4
4
|
import { createTestStore, DS_ALL_TTL } from "#testing";
|
|
5
|
-
import {
|
|
5
|
+
import { DEFAULT_PREFIX_MAP } from "../../shared/prefixes.js";
|
|
6
6
|
import showOntologyRaw from "./showOntologyRaw.js";
|
|
7
7
|
|
|
8
8
|
let store: Store;
|
|
@@ -21,7 +21,9 @@ describe("showOntologyRaw", () => {
|
|
|
21
21
|
const triples = await showOntologyRaw(store, "ds");
|
|
22
22
|
expect(triples.length).toBeGreaterThan(0);
|
|
23
23
|
|
|
24
|
-
const hasDs = triples.some((t) =>
|
|
24
|
+
const hasDs = triples.some((t) =>
|
|
25
|
+
t.subject.startsWith(DEFAULT_PREFIX_MAP.ds),
|
|
26
|
+
);
|
|
25
27
|
expect(hasDs).toBe(true);
|
|
26
28
|
});
|
|
27
29
|
|
|
@@ -19,15 +19,20 @@ import type {
|
|
|
19
19
|
OntologyDetailed,
|
|
20
20
|
OntologySummary,
|
|
21
21
|
} from "../shared/types/index.js";
|
|
22
|
+
import type { OntologyShowInput } from "./formatters/index.js";
|
|
22
23
|
import { listFormatters, showFormatters } from "./formatters/index.js";
|
|
24
|
+
import completeClassNames from "./helpers/completeClassNames.js";
|
|
25
|
+
import expandOntologyIris from "./helpers/expandOntologyIris.js";
|
|
23
26
|
import { listOntologies, showOntology } from "./operations/index.js";
|
|
24
27
|
|
|
25
28
|
const ontologyListColumns: readonly ColumnDef<OntologySummary>[] = [
|
|
26
29
|
{ key: "prefix", label: "Prefix" },
|
|
27
30
|
{ key: "namespace", label: "Namespace" },
|
|
31
|
+
{ key: "version", label: "Version" },
|
|
28
32
|
{ key: "classCount", label: "Classes" },
|
|
29
|
-
{ key: "
|
|
30
|
-
{ key: "
|
|
33
|
+
{ key: "relationCount", label: "Relations" },
|
|
34
|
+
{ key: "attributeCount", label: "Attributes" },
|
|
35
|
+
{ key: "shapeCount", label: "Shapes" },
|
|
31
36
|
];
|
|
32
37
|
|
|
33
38
|
/** The `ontology list` / `ontology_list` read story. */
|
|
@@ -67,13 +72,13 @@ export const ontologyListStory: ReadStory<
|
|
|
67
72
|
};
|
|
68
73
|
|
|
69
74
|
/** The `ontology show <prefix>` / `ontology_show` read story. */
|
|
70
|
-
export const ontologyShowStory: ReadStory<OntologyDetailed,
|
|
75
|
+
export const ontologyShowStory: ReadStory<OntologyDetailed, OntologyShowInput> =
|
|
71
76
|
{
|
|
72
77
|
noun: "ontology",
|
|
73
78
|
verb: "show",
|
|
74
79
|
description: "Show ontology schema details",
|
|
75
80
|
toolDescription:
|
|
76
|
-
"Show
|
|
81
|
+
"Show the TBox for a namespace: class hierarchy with instance counts and relations, plus metadata and constraints.",
|
|
77
82
|
params: [
|
|
78
83
|
{
|
|
79
84
|
name: "prefix",
|
|
@@ -86,13 +91,53 @@ export const ontologyShowStory: ReadStory<OntologyDetailed, OntologyDetailed> =
|
|
|
86
91
|
const all = await listOntologies(ctx.store);
|
|
87
92
|
return all
|
|
88
93
|
.map((o) => o.prefix)
|
|
89
|
-
.filter(
|
|
94
|
+
.filter(
|
|
95
|
+
(p) =>
|
|
96
|
+
p.length > 0 &&
|
|
97
|
+
p.toLowerCase().startsWith(partial.toLowerCase()),
|
|
98
|
+
);
|
|
90
99
|
},
|
|
91
100
|
},
|
|
101
|
+
{
|
|
102
|
+
name: "class",
|
|
103
|
+
type: "string",
|
|
104
|
+
description:
|
|
105
|
+
"Deep-dive into one class (label, local name, or compact IRI)",
|
|
106
|
+
toolDescription:
|
|
107
|
+
"Class to deep-dive into: super chain, direct + inherited properties, reverse references, sample instances, and follow-up queries",
|
|
108
|
+
complete: async (partial, cmdCtx) => {
|
|
109
|
+
const ctx = requirePragmaContext(cmdCtx);
|
|
110
|
+
return completeClassNames(ctx.store, partial);
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "properties",
|
|
115
|
+
type: "boolean",
|
|
116
|
+
description:
|
|
117
|
+
"Include datatype properties (attributes); default shows relations only",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "fullUris",
|
|
121
|
+
type: "boolean",
|
|
122
|
+
description: "Show full URIs instead of compact prefixed IRIs",
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
examples: [
|
|
126
|
+
"pragma ontology show ds",
|
|
127
|
+
"pragma ontology show ds --properties",
|
|
128
|
+
"pragma ontology show ds --full-uris --format json",
|
|
129
|
+
"pragma ontology show ds --class Component",
|
|
92
130
|
],
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
131
|
+
resolve: (rt, params) =>
|
|
132
|
+
showOntology(rt.store, params.prefix as string, {
|
|
133
|
+
class: params.class as string | undefined,
|
|
134
|
+
}),
|
|
135
|
+
// --full-uris is applied here, once, so every projection (plain, llm,
|
|
136
|
+
// json, MCP condensed) inherits the same encoding from the same place.
|
|
137
|
+
toOutput: (ontology, params) => ({
|
|
138
|
+
ontology: params.fullUris ? expandOntologyIris(ontology) : ontology,
|
|
139
|
+
showProperties: Boolean(params.properties),
|
|
140
|
+
}),
|
|
96
141
|
formatters: showFormatters,
|
|
97
142
|
toEnvelope: (ontology) => ({ data: ontology }),
|
|
98
143
|
guardParams: (params) =>
|
|
@@ -11,7 +11,6 @@ import { readConfig } from "#config";
|
|
|
11
11
|
import { mergeAndParseRefs } from "../../shared/mergeAndParseRefs.js";
|
|
12
12
|
import { cloneRef, fetchRef, pruneCache } from "./gitOps.js";
|
|
13
13
|
import type { PackageRef, RawPackageEntry } from "./parseRef.js";
|
|
14
|
-
import { parsePackageEntry } from "./parseRef.js";
|
|
15
14
|
import { cacheRoot, gitCacheDir } from "./paths.js";
|
|
16
15
|
|
|
17
16
|
// ---------------------------------------------------------------------------
|
|
@@ -3,4 +3,5 @@ export { default as allCommand } from "./all.js";
|
|
|
3
3
|
export { default as completionsCommand } from "./completions.js";
|
|
4
4
|
export { default as lspCommand } from "./lsp.js";
|
|
5
5
|
export { default as mcpCommand } from "./mcp.js";
|
|
6
|
+
export { default as setupCommand } from "./setup.js";
|
|
6
7
|
export { default as skillsCommand } from "./skills.js";
|