@canonical/pragma-cli 0.32.0 → 0.34.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 +72 -0
- package/dist/pragma +0 -0
- package/package.json +19 -15
- package/pragma.conf.ts +664 -0
- package/src/bin.ts +15 -6
- package/src/capabilities/block.parity.test.ts +250 -0
- package/src/capabilities/capabilities/capabilities.render.ts +3 -2
- package/src/capabilities/capabilities/capabilities.test.ts +10 -1
- package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
- package/src/capabilities/capabilities/catalog.ts +20 -8
- package/src/capabilities/capabilities/hints.ts +21 -36
- package/src/capabilities/capabilities/types.ts +2 -0
- package/src/capabilities/colophon/collectColophon.ts +34 -19
- package/src/capabilities/colophon/colophon.render.test.ts +31 -4
- package/src/capabilities/colophon/colophon.render.ts +13 -4
- package/src/capabilities/colophon/colophon.test.ts +3 -3
- package/src/capabilities/colophon/colophon.verb.ts +11 -4
- package/src/capabilities/colophon/markdownTerminal.ts +33 -14
- package/src/capabilities/colophon/types.ts +16 -7
- package/src/capabilities/config/collectConfigShow.test.ts +57 -0
- package/src/capabilities/config/collectConfigShow.ts +41 -4
- package/src/capabilities/config/fields.ts +10 -108
- package/src/capabilities/config/set.test.ts +6 -6
- package/src/capabilities/config/set.verb.ts +12 -8
- package/src/capabilities/config/show.render.test.ts +137 -0
- package/src/capabilities/config/show.render.ts +57 -16
- package/src/capabilities/config/show.verb.ts +9 -8
- package/src/capabilities/config/types.ts +8 -0
- package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
- package/src/capabilities/create/byteEquality.test.ts +82 -101
- package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
- package/src/capabilities/create/constants.ts +75 -0
- package/src/capabilities/create/create.mcp.test.ts +2 -2
- package/src/capabilities/create/create.test.ts +192 -16
- package/src/capabilities/create/create.verb.ts +208 -79
- package/src/capabilities/create/index.ts +2 -10
- package/src/capabilities/create/pickGenerator.ts +34 -28
- package/src/capabilities/create/templates.embedded.generated.ts +4 -29
- package/src/capabilities/create/types.ts +10 -6
- package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
- package/src/capabilities/distribution.test.ts +120 -0
- package/src/capabilities/distribution.ts +59 -0
- package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
- package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
- package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
- package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
- package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
- package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
- package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
- package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
- package/src/capabilities/doctor/doctor.render.test.ts +170 -0
- package/src/capabilities/doctor/doctor.render.ts +117 -34
- package/src/capabilities/doctor/doctor.test.ts +68 -9
- package/src/capabilities/doctor/doctor.verb.ts +6 -2
- package/src/capabilities/doctor/runChecks.ts +33 -9
- package/src/capabilities/doctor/types.ts +16 -1
- package/src/capabilities/emptyRecovery.test.ts +32 -15
- package/src/capabilities/graph/inspect.verb.ts +3 -2
- package/src/capabilities/graph/query.verb.ts +3 -2
- package/src/capabilities/graph/runQuery.ts +8 -5
- package/src/capabilities/index.ts +22 -18
- package/src/capabilities/info/collectInfo.ts +10 -6
- package/src/capabilities/info/info.render.ts +3 -2
- package/src/capabilities/info/info.verb.ts +3 -2
- package/src/capabilities/lazy.test.ts +93 -2
- package/src/capabilities/ontology/index.ts +10 -3
- package/src/capabilities/ontology/ontology.test.ts +157 -5
- package/src/capabilities/ontology/verbs.ts +203 -104
- package/src/capabilities/prompt/prompt.render.test.ts +94 -0
- package/src/capabilities/prompt/prompt.render.ts +23 -6
- package/src/capabilities/prompt/prompt.test.ts +68 -1
- package/src/capabilities/prompt/verbs.ts +24 -11
- package/src/capabilities/reference.test.ts +32 -0
- package/src/capabilities/resources/provider.ts +58 -25
- package/src/capabilities/resources/resources.test.ts +176 -21
- package/src/capabilities/scopeTypeSync.test.ts +37 -0
- package/src/capabilities/setup/index.ts +7 -1
- package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
- package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
- package/src/capabilities/setup/operations/setupLsp.ts +56 -12
- package/src/capabilities/setup/operations/setupMcp.ts +216 -39
- package/src/capabilities/setup/operations/setupSkills.ts +10 -4
- package/src/capabilities/setup/setup.render.test.ts +109 -0
- package/src/capabilities/setup/setup.render.ts +61 -14
- package/src/capabilities/setup/setup.test.ts +352 -1
- package/src/capabilities/setup/setup.verb.ts +68 -9
- package/src/capabilities/setup/shell.ts +29 -3
- package/src/capabilities/setup/types.ts +59 -2
- package/src/capabilities/shared/bands.ts +16 -0
- package/src/capabilities/skill/discover.ts +11 -5
- package/src/capabilities/skill/render.ts +4 -5
- package/src/capabilities/skill/verbs.ts +7 -7
- package/src/capabilities/sources/collectStatus.ts +69 -49
- package/src/capabilities/sources/index.ts +1 -1
- package/src/capabilities/sources/installSkills.ts +2 -2
- package/src/capabilities/sources/runUpdate.ts +146 -98
- package/src/capabilities/sources/sources.test.ts +264 -84
- package/src/capabilities/sources/status.render.test.ts +94 -0
- package/src/capabilities/sources/status.render.ts +55 -33
- package/src/capabilities/sources/status.verb.ts +15 -8
- package/src/capabilities/sources/types.ts +18 -25
- package/src/capabilities/sources/update.render.ts +9 -2
- package/src/capabilities/sources/update.verb.ts +12 -23
- package/src/capabilities/surface.test.ts +65 -17
- package/src/capabilities/tier.parity.test.ts +144 -0
- package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
- package/src/constants.test.ts +9 -7
- package/src/constants.ts +49 -23
- package/src/identity.test.ts +333 -0
- package/src/index.ts +2 -2
- package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
- package/src/kernel/completion/complete.test.ts +3 -5
- package/src/kernel/completion/emitScripts.test.ts +1 -1
- package/src/kernel/completion/emitScripts.ts +4 -2
- package/src/kernel/completion/entitySource.test.ts +185 -41
- package/src/kernel/completion/entitySource.ts +131 -113
- package/src/kernel/completion/model.test.ts +3 -3
- package/src/kernel/completion/model.ts +11 -1
- package/src/kernel/completion/parse.test.ts +2 -4
- package/src/kernel/completion/safety.test.ts +82 -36
- package/src/kernel/completion/shellDrive.test.ts +592 -0
- package/src/kernel/completion/templates/fish.ts +28 -6
- package/src/kernel/completion/types.ts +3 -3
- package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
- package/src/kernel/config/defaults.test.ts +85 -0
- package/src/kernel/config/defaults.ts +12 -23
- package/src/kernel/config/evaluateProjectConfig.ts +2 -3
- package/src/kernel/config/findProjectConfig.ts +4 -3
- package/src/kernel/config/firstRun.ts +12 -5
- package/src/kernel/config/globalConfig.ts +2 -2
- package/src/kernel/config/index.ts +4 -5
- package/src/kernel/config/paths.ts +17 -17
- package/src/kernel/config/readConfig.test.ts +242 -6
- package/src/kernel/config/readConfig.ts +8 -8
- package/src/kernel/config/schema.test.ts +50 -0
- package/src/kernel/config/schema.ts +97 -10
- package/src/kernel/config/types.ts +73 -19
- package/src/kernel/copy.test.ts +453 -0
- package/src/kernel/error/PragmaError.test.ts +5 -5
- package/src/kernel/error/constants.ts +3 -2
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +22 -45
- package/src/kernel/error/recovery.ts +22 -27
- package/src/kernel/error/renderError.ts +1 -1
- package/src/kernel/error/types.ts +5 -4
- package/src/kernel/packs/collect.test.ts +195 -11
- package/src/kernel/packs/collect.ts +213 -58
- package/src/kernel/packs/compile.test.ts +163 -7
- package/src/kernel/packs/compile.ts +15 -12
- package/src/kernel/packs/disclosure.test.ts +5 -2
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
- package/src/kernel/packs/graphqlSource.test.ts +6 -3
- package/src/kernel/packs/iri.ts +5 -7
- package/src/kernel/packs/renderPack.ts +5 -3
- package/src/kernel/packs/resolveEntity.ts +21 -17
- package/src/kernel/packs/runBodies.ts +15 -14
- package/src/kernel/packs/schema.ts +96 -10
- package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
- package/src/kernel/packs/sparql/runSelect.ts +73 -34
- package/src/kernel/packs/toolDescription.test.ts +10 -2
- package/src/kernel/packs/types.ts +39 -4
- package/src/kernel/packs/uniqueness.ts +4 -4
- package/src/kernel/project/cli/buildProgram.test.ts +60 -11
- package/src/kernel/project/cli/buildProgram.ts +11 -0
- package/src/kernel/project/cli/dispatch.test.ts +67 -0
- package/src/kernel/project/cli/dispatch.ts +34 -12
- package/src/kernel/project/cli/globalFlags.test.ts +12 -4
- package/src/kernel/project/cli/globalFlags.ts +14 -14
- package/src/kernel/project/cli/helpFormat.ts +1 -1
- package/src/kernel/project/cli/rootHelp.ts +109 -107
- package/src/kernel/project/mcp/buildServer.ts +11 -6
- package/src/kernel/project/mcp/instructions.ts +12 -7
- package/src/kernel/project/mcp/mcpError.ts +52 -0
- package/src/kernel/project/mcp/parity.test.ts +39 -1
- package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
- package/src/kernel/project/mcp/prompts/provider.ts +51 -7
- package/src/kernel/project/mcp/prompts/source.ts +68 -67
- package/src/kernel/project/mcp/registerVerb.ts +25 -6
- package/src/kernel/project/mcp/serve.ts +11 -3
- package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
- package/src/kernel/render/disclosure.ts +4 -2
- package/src/kernel/render/formatters.ts +5 -4
- package/src/kernel/render/prefixes.test.ts +36 -0
- package/src/kernel/render/prefixes.ts +40 -13
- package/src/kernel/render/renderers.test.ts +35 -1
- package/src/kernel/render/renderers.ts +25 -7
- package/src/kernel/render/style.test.ts +31 -0
- package/src/kernel/render/style.ts +74 -0
- package/src/kernel/runtime/graphpack/build.ts +57 -3
- package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
- package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
- package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +37 -11
- package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
- package/src/kernel/runtime/graphpack/hash.ts +3 -2
- package/src/kernel/runtime/graphpack/index.ts +1 -1
- package/src/kernel/runtime/graphpack/manifest.ts +114 -12
- package/src/kernel/runtime/graphpack/read.ts +32 -22
- package/src/kernel/runtime/graphpack/schemas.ts +74 -0
- package/src/kernel/runtime/graphpack/stories.ts +84 -0
- package/src/kernel/runtime/graphpack/types.ts +72 -45
- package/src/kernel/runtime/loadSession.ts +4 -12
- package/src/kernel/runtime/paths.ts +64 -15
- package/src/kernel/runtime/probe.test.ts +8 -4
- package/src/kernel/runtime/readEntity.ts +6 -5
- package/src/kernel/runtime/refs/gitOps.ts +1 -19
- package/src/kernel/runtime/refs/parseRef.ts +7 -7
- package/src/kernel/runtime/refs/resolve.ts +141 -65
- package/src/kernel/runtime/resolveSources.test.ts +63 -24
- package/src/kernel/runtime/resolveSources.ts +36 -25
- package/src/kernel/runtime/storeReadiness.ts +62 -0
- package/src/kernel/runtime/storeless.test.ts +1 -1
- package/src/kernel/runtime/types.ts +1 -1
- package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
- package/src/kernel/spec/emitReference.test.ts +196 -0
- package/src/kernel/spec/emitReference.ts +624 -0
- package/src/kernel/spec/emitSurface.ts +13 -7
- package/src/kernel/spec/index.ts +2 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.ts +8 -0
- package/src/kernel/spec/types.ts +29 -9
- package/src/kernel/spec/validate.ts +2 -1
- package/src/kernel/vocabulary.test.ts +164 -0
- package/src/kernel/vocabulary.ts +146 -0
- package/src/testing/PARITY_GAPS.ts +9 -3
- package/src/testing/behavioral/README.md +3 -3
- package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
- package/src/testing/behavioral/completion.test.ts +21 -8
- package/src/testing/behavioral/disclosure.test.ts +12 -0
- package/src/testing/behavioral/docExamples.test.ts +283 -0
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
- package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
- package/src/testing/behavioral/harness.foundation.test.ts +14 -5
- package/src/testing/behavioral/journeys.cli.test.ts +116 -73
- package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
- package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
- package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
- package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
- package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
- package/src/testing/behavioral/liveReadSurface.ts +4 -3
- package/src/testing/behavioral/mcpSurface.test.ts +10 -5
- package/src/testing/behavioral/staleCommands.test.ts +86 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
- package/src/testing/eval/cases/assembly.ts +16 -9
- package/src/testing/eval/cases/effectful.ts +31 -16
- package/src/testing/eval/cases/readNouns.ts +23 -18
- package/src/testing/eval/cases/stable.ts +5 -1
- package/src/testing/eval/eval.test.ts +7 -1
- package/src/testing/fixtures/fixtureCapability.ts +201 -1
- package/src/testing/fixtures/graph/canonical.ts +6 -3
- package/src/testing/fixtures/graph/defaultPack.ts +179 -0
- package/src/testing/fixtures/graph/owlExport.ts +97 -0
- package/src/testing/helpers/extractSnippets.ts +29 -0
- package/src/testing/helpers/fixtureGraph.ts +33 -16
- package/src/testing/helpers/golden.ts +1 -1
- package/src/testing/helpers/packRuntime.ts +2 -3
- package/src/testing/helpers/projectCli.ts +2 -1
- package/src/testing/helpers/projectMcp.ts +6 -0
- package/src/testing/perf/budgets.test.ts +4 -0
- package/src/testing/perf/budgets.ts +22 -7
- package/src/testing/perf/globalSetup.ts +121 -6
- package/src/types.ts +2 -2
- package/src/capabilities/block/blockList.verb.ts +0 -150
- package/src/capabilities/block/index.ts +0 -25
- package/src/capabilities/block/pack.ts +0 -155
- package/src/capabilities/block/parity.test.ts +0 -180
- package/src/capabilities/block/tierChain.ts +0 -78
- package/src/capabilities/colophon/pragmaColophon.ts +0 -66
- package/src/capabilities/config/field.test.ts +0 -188
- package/src/capabilities/modifier/index.ts +0 -15
- package/src/capabilities/modifier/pack.ts +0 -64
- package/src/capabilities/standard/index.ts +0 -18
- package/src/capabilities/standard/pack.ts +0 -128
- package/src/capabilities/tier/index.ts +0 -19
- package/src/capabilities/tier/lookup.render.ts +0 -34
- package/src/capabilities/tier/lookup.test.ts +0 -68
- package/src/capabilities/tier/lookup.verb.ts +0 -44
- package/src/capabilities/tier/pack.ts +0 -28
- package/src/capabilities/tier/runLookup.ts +0 -59
- package/src/capabilities/token/addConfig.render.ts +0 -27
- package/src/capabilities/token/addConfig.test.ts +0 -62
- package/src/capabilities/token/addConfig.verb.ts +0 -40
- package/src/capabilities/token/index.ts +0 -19
- package/src/capabilities/token/pack.ts +0 -58
- package/src/capabilities/token/runAddConfig.ts +0 -95
- package/src/kernel/completion/bashDrive.test.ts +0 -73
- package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
- package/src/kernel/runtime/lock.ts +0 -90
|
@@ -3,24 +3,34 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Listing zero rows is a calm SUCCESS, not an error. The run body returns `[]`
|
|
5
5
|
* (no throw), dispatch renders a non-blank message and exits 0, and JSON stays
|
|
6
|
-
* `[]`. A
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* `[]`. A story's declared `emptyRecovery` becomes that message's HINT — but as
|
|
7
|
+
* guidance on the success path, never a thrown EMPTY_RESULTS (which maps to
|
|
8
|
+
* exit 1 and would break the uniform `ok:true` list contract).
|
|
9
|
+
*
|
|
10
|
+
* The story declares the command WITHOUT a binary name (`sources update`); the
|
|
11
|
+
* renderer prepends the CONSUMING distribution's. The hints asserted below are
|
|
12
|
+
* therefore the JOINED string, which is what a user reads — a story that named
|
|
13
|
+
* a binary itself would render it twice, and the grammar now refuses one.
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
13
17
|
import { compilePack } from "../kernel/packs/compile.js";
|
|
14
18
|
import type { PackDefinition } from "../kernel/packs/types.js";
|
|
19
|
+
import { distributionSource } from "../kernel/packs/types.js";
|
|
15
20
|
import { verbKey } from "../kernel/packs/uniqueness.js";
|
|
16
21
|
import { executeVerb } from "../kernel/project/cli/dispatch.js";
|
|
17
22
|
import { DEFAULT_PREFIX_MAP } from "../kernel/render/prefixes.js";
|
|
18
23
|
import type { PragmaRuntime } from "../kernel/runtime/types.js";
|
|
19
24
|
import type { VerbSpec } from "../kernel/spec/types.js";
|
|
20
25
|
import { buildFixtureRuntime } from "../testing/helpers/packRuntime.js";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
import { declaredStories } from "./distribution.js";
|
|
27
|
+
|
|
28
|
+
/** A story `pragma.conf.ts` declares — absent means it was renamed or dropped. */
|
|
29
|
+
function storyFor(noun: string): PackDefinition {
|
|
30
|
+
const story = declaredStories.get(noun);
|
|
31
|
+
if (!story) throw new Error(`pragma.conf.ts declares no story for "${noun}"`);
|
|
32
|
+
return story;
|
|
33
|
+
}
|
|
24
34
|
|
|
25
35
|
const PREFIXES = {
|
|
26
36
|
ds: "https://ds.canonical.com/",
|
|
@@ -61,19 +71,21 @@ afterAll(async () => {
|
|
|
61
71
|
});
|
|
62
72
|
|
|
63
73
|
const listVerb = (pack: PackDefinition): VerbSpec =>
|
|
64
|
-
compilePack(
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
compilePack(
|
|
75
|
+
pack,
|
|
76
|
+
distributionSource("pragma.conf.ts"),
|
|
77
|
+
DEFAULT_PREFIX_MAP,
|
|
78
|
+
).find((v) => verbKey(v.path) === `${pack.noun} list`) as VerbSpec;
|
|
67
79
|
|
|
68
80
|
/**
|
|
69
|
-
* Every
|
|
81
|
+
* Every declared story's list verb, with the hint expected in its empty message:
|
|
70
82
|
* an authored `emptyRecovery` surfaces `pragma sources update`; the others fall
|
|
71
83
|
* back to the generic build/broaden hint.
|
|
72
84
|
*/
|
|
73
85
|
const PACK_LISTS: readonly { pack: PackDefinition; hint: RegExp }[] = [
|
|
74
|
-
{ pack:
|
|
75
|
-
{ pack:
|
|
76
|
-
{ pack:
|
|
86
|
+
{ pack: storyFor("modifier"), hint: /pragma sources update/ },
|
|
87
|
+
{ pack: storyFor("token"), hint: /pragma sources update/ },
|
|
88
|
+
{ pack: storyFor("standard"), hint: /broaden your filter/ },
|
|
77
89
|
];
|
|
78
90
|
|
|
79
91
|
const REAL = { dryRun: false, undo: false, yes: false };
|
|
@@ -104,7 +116,12 @@ describe("pack list empty-state (U5, PROTECTED)", () => {
|
|
|
104
116
|
});
|
|
105
117
|
|
|
106
118
|
it("dispatch prints the empty message on stdout and exits 0 (end-to-end)", async () => {
|
|
107
|
-
const outcome = await executeVerb(
|
|
119
|
+
const outcome = await executeVerb(
|
|
120
|
+
listVerb(storyFor("modifier")),
|
|
121
|
+
{},
|
|
122
|
+
REAL,
|
|
123
|
+
rt,
|
|
124
|
+
);
|
|
108
125
|
expect(outcome.exitCode).toBe(0);
|
|
109
126
|
expect(outcome.stdout).toContain("No modifier entries found.");
|
|
110
127
|
expect(outcome.stdout).toContain("pragma sources update");
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* name or absolute IRI addresses the subject exactly.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { BIN_NAME } from "../../constants.js";
|
|
11
12
|
import type { InspectResult } from "../../kernel/runtime/readEntity.js";
|
|
12
13
|
import { readEntity } from "../../kernel/runtime/readEntity.js";
|
|
13
14
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
@@ -32,8 +33,8 @@ const inspectVerb: VerbSpec<Record<string, unknown>, InspectResult> = {
|
|
|
32
33
|
],
|
|
33
34
|
output: { formatters: inspectFormatters },
|
|
34
35
|
examples: [
|
|
35
|
-
{ cmd:
|
|
36
|
-
{ cmd:
|
|
36
|
+
{ cmd: `${BIN_NAME} graph inspect ds:button` },
|
|
37
|
+
{ cmd: `${BIN_NAME} graph inspect https://ds.canonical.com/button` },
|
|
37
38
|
],
|
|
38
39
|
capability: {
|
|
39
40
|
needsStore: true,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* query surfaces as INVALID_INPUT with a recovery pointer to `ontology list`.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { BIN_NAME } from "../../constants.js";
|
|
12
13
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
13
14
|
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
14
15
|
import type { VerbSpec } from "../../kernel/spec/types.js";
|
|
@@ -35,11 +36,11 @@ const queryVerb: VerbSpec<Record<string, unknown>, QueryResult> = {
|
|
|
35
36
|
output: { formatters: queryFormatters },
|
|
36
37
|
examples: [
|
|
37
38
|
{
|
|
38
|
-
cmd:
|
|
39
|
+
cmd: `${BIN_NAME} graph query "SELECT ?s WHERE { ?s a ds:Component }"`,
|
|
39
40
|
note: "list every component subject",
|
|
40
41
|
},
|
|
41
42
|
{
|
|
42
|
-
cmd:
|
|
43
|
+
cmd: `${BIN_NAME} graph query "ASK { ds:button a ds:Component }" --format json`,
|
|
43
44
|
},
|
|
44
45
|
],
|
|
45
46
|
capability: {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { QueryResult } from "@canonical/ke";
|
|
13
13
|
import { PragmaError } from "../../kernel/error/PragmaError.js";
|
|
14
|
+
import { cliRecovery } from "../../kernel/error/recovery.js";
|
|
14
15
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -39,11 +40,13 @@ export async function runQuery(
|
|
|
39
40
|
message: detail
|
|
40
41
|
? `Invalid SPARQL query: ${detail}`
|
|
41
42
|
: "Invalid SPARQL query.",
|
|
42
|
-
recovery:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
recovery: cliRecovery(
|
|
44
|
+
"ontology list",
|
|
45
|
+
"Check your SPARQL syntax and the loaded namespaces.",
|
|
46
|
+
{
|
|
47
|
+
tool: "ontology_list",
|
|
48
|
+
},
|
|
49
|
+
),
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
}
|
|
@@ -1,46 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The capability registry:
|
|
3
|
-
*
|
|
2
|
+
* The capability registry: every capability module the projectors consume
|
|
3
|
+
* (D8 — test-only fixtures are never listed here).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Two sources, in this order: the literal array of AUTHORED modules (in
|
|
6
|
+
* authoring order; projectors sort where they need to), then every story
|
|
7
|
+
* `pragma.conf.ts` declares that no authored module already claims. Since
|
|
8
|
+
* L-OPEN-9 no DATA noun is authored — the authored array is the kernel's own
|
|
9
|
+
* nouns and nothing else, and every design-system noun appears only in the
|
|
10
|
+
* config. No noun literal is written here: the second half is derived.
|
|
11
|
+
*
|
|
12
|
+
* Importing this barrel pulls only spec + formatter modules — every run body is
|
|
13
|
+
* behind a lazy dynamic import, and `distribution.ts` is zod-free — so the
|
|
9
14
|
* `--help`/`__complete` fast paths stay free of the config reader and zod.
|
|
10
15
|
*/
|
|
11
16
|
|
|
12
17
|
import type { CapabilityModule } from "../kernel/spec/types.js";
|
|
13
|
-
import { blockModule } from "./block/index.js";
|
|
14
18
|
import { capabilitiesModule } from "./capabilities/index.js";
|
|
15
19
|
import { colophonModule } from "./colophon/index.js";
|
|
16
20
|
import { configModule } from "./config/index.js";
|
|
17
21
|
import { createModule } from "./create/index.js";
|
|
22
|
+
import { storyModules } from "./distribution.js";
|
|
18
23
|
import { doctorModule } from "./doctor/index.js";
|
|
19
24
|
import { graphModule } from "./graph/index.js";
|
|
20
25
|
import { infoModule } from "./info/index.js";
|
|
21
26
|
import { metaModule } from "./meta/index.js";
|
|
22
|
-
import { modifierModule } from "./modifier/index.js";
|
|
23
27
|
import { ontologyModule } from "./ontology/index.js";
|
|
24
28
|
import { promptModule } from "./prompt/index.js";
|
|
25
29
|
import { setupModule } from "./setup/index.js";
|
|
26
30
|
import { skillModule } from "./skill/index.js";
|
|
27
31
|
import { sourcesModule } from "./sources/index.js";
|
|
28
|
-
import { standardModule } from "./standard/index.js";
|
|
29
|
-
import { tierModule } from "./tier/index.js";
|
|
30
|
-
import { tokenModule } from "./token/index.js";
|
|
31
32
|
import { upgradeModule } from "./upgrade/index.js";
|
|
32
33
|
|
|
33
|
-
/**
|
|
34
|
-
|
|
34
|
+
/** The modules with hand-written code, in authoring order. */
|
|
35
|
+
const authored: readonly CapabilityModule[] = [
|
|
35
36
|
infoModule,
|
|
36
37
|
configModule,
|
|
37
38
|
createModule,
|
|
38
39
|
sourcesModule,
|
|
39
|
-
standardModule,
|
|
40
|
-
tierModule,
|
|
41
|
-
modifierModule,
|
|
42
|
-
tokenModule,
|
|
43
|
-
blockModule,
|
|
44
40
|
ontologyModule,
|
|
45
41
|
skillModule,
|
|
46
42
|
graphModule,
|
|
@@ -52,3 +48,11 @@ export const capabilities: readonly CapabilityModule[] = [
|
|
|
52
48
|
colophonModule,
|
|
53
49
|
metaModule,
|
|
54
50
|
];
|
|
51
|
+
|
|
52
|
+
const claimed = new Set(authored.map((module) => module.name));
|
|
53
|
+
|
|
54
|
+
/** Every capability module: the authored ones, then the unclaimed declared stories. */
|
|
55
|
+
export const capabilities: readonly CapabilityModule[] = [
|
|
56
|
+
...authored,
|
|
57
|
+
...[...storyModules.values()].filter((module) => !claimed.has(module.name)),
|
|
58
|
+
];
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* the npm registry — this deliberately REVERSES PR1's networkless-D11 stance for
|
|
7
7
|
* `info` (the reversal IS the enrichment), unconditional because the covenant
|
|
8
8
|
* `info` verb carries no flag, 3s timeout, silent-fail — and (b) a storeless
|
|
9
|
-
* entity total from the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* entity total, read from the index of whichever pack the boot decision names
|
|
10
|
+
* (`resolveSources` → `readPackIndex`, the same predicate `sources status` and
|
|
11
|
+
* `doctor` switch on) so `info` never counts a graph this project's reads
|
|
12
|
+
* refuse. Neither enrichment boots the store, so the storeless-guarantee spy
|
|
13
|
+
* still sees `store.booted === false`. Kept out of the spec module so building
|
|
14
|
+
* the command tree never pulls it onto the `--help` fast path.
|
|
13
15
|
*/
|
|
14
16
|
|
|
15
17
|
import {
|
|
@@ -17,6 +19,7 @@ import {
|
|
|
17
19
|
readPackIndex,
|
|
18
20
|
} from "../../kernel/completion/entitySource.js";
|
|
19
21
|
import { readConfig } from "../../kernel/config/readConfig.js";
|
|
22
|
+
import { resolveSources } from "../../kernel/runtime/resolveSources.js";
|
|
20
23
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
21
24
|
import {
|
|
22
25
|
detectInstallSource,
|
|
@@ -50,8 +53,9 @@ export async function collectInfo(runtime: PragmaRuntime): Promise<InfoData> {
|
|
|
50
53
|
: undefined;
|
|
51
54
|
const updateSkipped = registry === undefined;
|
|
52
55
|
|
|
53
|
-
// Entity total: storeless
|
|
54
|
-
|
|
56
|
+
// Entity total: storeless, over the pack the boot decision names — so a
|
|
57
|
+
// configured-but-unbuilt project reports no total rather than the snapshot's.
|
|
58
|
+
const index = readPackIndex(resolveSources(layers, runtime.cwd));
|
|
55
59
|
const entities = index ? entityTotal(index) : undefined;
|
|
56
60
|
|
|
57
61
|
return {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Formatters for `pragma info` — plain, llm, json (no ink).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { BIN_NAME } from "../../constants.js";
|
|
5
6
|
import type { ConfigOrigin } from "../../kernel/config/types.js";
|
|
6
7
|
import type { Formatters } from "../../kernel/spec/types.js";
|
|
7
8
|
import type { InfoData } from "./types.js";
|
|
@@ -15,7 +16,7 @@ export const infoFormatters: Formatters<InfoData> = {
|
|
|
15
16
|
plain(data) {
|
|
16
17
|
const { config } = data;
|
|
17
18
|
const lines = [
|
|
18
|
-
|
|
19
|
+
`${BIN_NAME} v${data.version}`,
|
|
19
20
|
` Installed via: ${data.installSource}`,
|
|
20
21
|
` tier: ${config.tier ?? "(none)"}${originMarker(config.origins.tier)}`,
|
|
21
22
|
` channel: ${config.channel}${originMarker(config.origins.channel)}`,
|
|
@@ -45,7 +46,7 @@ export const infoFormatters: Formatters<InfoData> = {
|
|
|
45
46
|
llm(data) {
|
|
46
47
|
const { config } = data;
|
|
47
48
|
const lines = [
|
|
48
|
-
`#
|
|
49
|
+
`# ${BIN_NAME} v${data.version}`,
|
|
49
50
|
`- Installed via: ${data.installSource}`,
|
|
50
51
|
`- Tier: ${config.tier ?? "(none)"}${originMarker(config.origins.tier)}`,
|
|
51
52
|
`- Channel: ${config.channel}${originMarker(config.origins.channel)}`,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* the config reader or zod onto the fast path.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { BIN_NAME } from "../../constants.js";
|
|
9
10
|
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
10
11
|
import type { CapabilityModule, VerbSpec } from "../../kernel/spec/types.js";
|
|
11
12
|
import { infoFormatters } from "./info.render.js";
|
|
@@ -18,9 +19,9 @@ const infoVerb: VerbSpec<Record<string, unknown>, InfoData> = {
|
|
|
18
19
|
params: [],
|
|
19
20
|
output: { formatters: infoFormatters },
|
|
20
21
|
examples: [
|
|
21
|
-
{ cmd:
|
|
22
|
+
{ cmd: `${BIN_NAME} info`, note: "human-readable summary" },
|
|
22
23
|
{
|
|
23
|
-
cmd:
|
|
24
|
+
cmd: `${BIN_NAME} info --format json`,
|
|
24
25
|
note: "the full {ok,data,meta} envelope",
|
|
25
26
|
},
|
|
26
27
|
],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { dirname, relative, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { describe, expect, it } from "vitest";
|
|
5
5
|
|
|
@@ -45,10 +45,63 @@ describe("lazy dispatch — module-graph probe (PROTECTED)", () => {
|
|
|
45
45
|
expect(has(graph, "info/info.render.ts")).toBe(true);
|
|
46
46
|
expect(has(graph, "config/show.render.ts")).toBe(true);
|
|
47
47
|
|
|
48
|
-
// ...but run bodies and the config
|
|
48
|
+
// ...but run bodies and the config LAYER stay behind the dynamic boundary:
|
|
49
|
+
// no reader, no defaults layer, no zod schema. The distribution config
|
|
50
|
+
// itself IS on the graph — `constants.ts` projects the program's identity
|
|
51
|
+
// from it, `render/prefixes.ts` its domain namespaces and
|
|
52
|
+
// `kernel/vocabulary.ts` its domain terms — which is safe only because it
|
|
53
|
+
// is inert data (pinned below).
|
|
49
54
|
expect(has(graph, "info/collectInfo.ts")).toBe(false);
|
|
50
55
|
expect(has(graph, "config/collectConfigShow.ts")).toBe(false);
|
|
51
56
|
expect(has(graph, "kernel/config/readConfig.ts")).toBe(false);
|
|
57
|
+
expect(has(graph, "kernel/config/defaults.ts")).toBe(false);
|
|
58
|
+
expect(has(graph, "kernel/config/schema.ts")).toBe(false);
|
|
59
|
+
expect(has(graph, "pragma.conf.ts")).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("the distribution config is inert data — it imports nothing that runs", () => {
|
|
63
|
+
const conf = resolve(here, "../../pragma.conf.ts");
|
|
64
|
+
const pkgRoot = resolve(here, "../..");
|
|
65
|
+
// The graph walker follows `from "…"` textually and cannot tell an
|
|
66
|
+
// `import type` from a value import, so the type-only `config/types.ts`
|
|
67
|
+
// and `packs/types.ts` show up here even though nothing of either survives
|
|
68
|
+
// compilation. Assert that NO file on the graph has a value import — not
|
|
69
|
+
// just the conf. That is the claim in the title, and it is what keeps
|
|
70
|
+
// `--help`/`__complete`/`--version` free of module-init work when another
|
|
71
|
+
// lane or a fork edits any of these three (`packs/types.ts` in particular
|
|
72
|
+
// is the grammar that type-checks the declared stories, and its own
|
|
73
|
+
// docblock promises it stays zod-free BECAUSE it lands here).
|
|
74
|
+
const graph = [...staticImportGraph(conf)];
|
|
75
|
+
for (const file of graph) {
|
|
76
|
+
expect(readFileSync(file, "utf-8"), file).not.toMatch(
|
|
77
|
+
/^import (?!type\b)/m,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
// The enumeration is exact, not a subset: a type import from a module that
|
|
81
|
+
// itself imports something grows this list and fails here.
|
|
82
|
+
expect(graph.map((f) => relative(pkgRoot, f)).sort()).toEqual([
|
|
83
|
+
"pragma.conf.ts",
|
|
84
|
+
"src/kernel/config/types.ts",
|
|
85
|
+
"src/kernel/packs/types.ts",
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("the dispatch path never reaches the embedded n-quads module (PROTECTED)", () => {
|
|
90
|
+
// `activeStories` reads the pack's carried stories from their OWN generated
|
|
91
|
+
// module rather than through `graphpack/embedded.ts`, which statically
|
|
92
|
+
// imports the ~1.9 MB `pack.generated.ts`. Restoring that edge costs a
|
|
93
|
+
// measured +23 ms on EVERY dispatched command, and the whole test suite
|
|
94
|
+
// passes with it restored — the split is prose in three docblocks and
|
|
95
|
+
// nothing else. This is the constraint those docblocks describe.
|
|
96
|
+
for (const entry of [
|
|
97
|
+
"../kernel/packs/collect.ts",
|
|
98
|
+
"../kernel/runtime/graphpack/stories.ts",
|
|
99
|
+
"../kernel/runtime/resolveSources.ts",
|
|
100
|
+
]) {
|
|
101
|
+
const graph = staticImportGraph(resolve(here, entry));
|
|
102
|
+
expect(has(graph, "embedded/pack.generated.ts"), entry).toBe(false);
|
|
103
|
+
expect(has(graph, "graphpack/embedded.ts"), entry).toBe(false);
|
|
104
|
+
}
|
|
52
105
|
});
|
|
53
106
|
|
|
54
107
|
it("the help path (buildProgram) imports no zod schema module", () => {
|
|
@@ -86,4 +139,42 @@ describe("lazy dispatch — module-graph probe (PROTECTED)", () => {
|
|
|
86
139
|
}
|
|
87
140
|
}
|
|
88
141
|
});
|
|
142
|
+
|
|
143
|
+
it("NO module on that graph statically imports zod (PROTECTED)", () => {
|
|
144
|
+
// This used to pin an exact set of one — `graphpack/types.ts`, reached via
|
|
145
|
+
// `resolveSources` → `packIsComplete` → `readManifest` → `manifestSchema
|
|
146
|
+
// .parse` — as the one genuine zod value import on the storeless graph,
|
|
147
|
+
// measured at ~3–4 ms of a ~30 ms budget. Its own comment said an exact set
|
|
148
|
+
// was what would make the day it got fixed impossible to miss. That day is
|
|
149
|
+
// this commit: the schemas moved to `graphpack/schemas.ts`, imported only by
|
|
150
|
+
// the two readers already off the fast path, and `readManifest` validates
|
|
151
|
+
// structurally instead.
|
|
152
|
+
//
|
|
153
|
+
// The set is now EMPTY, which is strictly stronger than the pin it replaces:
|
|
154
|
+
// it no longer tolerates one named module, so a brand-new module value-
|
|
155
|
+
// importing zod fails here too — the gap the previous form left open.
|
|
156
|
+
const graph = staticImportGraph(resolve(here, "index.ts"));
|
|
157
|
+
const pkgRoot = resolve(here, "..", "..");
|
|
158
|
+
const zodImporters = [...graph]
|
|
159
|
+
.filter((file) => /from\s*["']zod["']/.test(readFileSync(file, "utf-8")))
|
|
160
|
+
.map((file) => relative(pkgRoot, file))
|
|
161
|
+
.sort();
|
|
162
|
+
expect(zodImporters).toEqual([]);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("the help path (buildProgram) imports zod from nowhere (PROTECTED)", () => {
|
|
166
|
+
// The named-module check above (`kernel/config/schema.ts`,
|
|
167
|
+
// `kernel/spec/validate.ts`) could only catch the two modules it names — a
|
|
168
|
+
// third zod importer reaching the help path would have passed it silently.
|
|
169
|
+
// Generalized (ruling R4) to the property those names were standing in for.
|
|
170
|
+
const graph = staticImportGraph(
|
|
171
|
+
resolve(here, "../kernel/project/cli/buildProgram.ts"),
|
|
172
|
+
);
|
|
173
|
+
const pkgRoot = resolve(here, "..", "..");
|
|
174
|
+
const zodImporters = [...graph]
|
|
175
|
+
.filter((file) => /from\s*["']zod["']/.test(readFileSync(file, "utf-8")))
|
|
176
|
+
.map((file) => relative(pkgRoot, file))
|
|
177
|
+
.sort();
|
|
178
|
+
expect(zodImporters).toEqual([]);
|
|
179
|
+
});
|
|
89
180
|
});
|
|
@@ -3,10 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { CapabilityModule } from "../../kernel/spec/types.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ontologyListVerb,
|
|
8
|
+
ontologyLookupVerb,
|
|
9
|
+
ontologyShowVerb,
|
|
10
|
+
} from "./verbs.js";
|
|
7
11
|
|
|
8
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The `ontology` capability module (`list`, `lookup`, and the deprecated `show`
|
|
14
|
+
* alias — AV-228 B1).
|
|
15
|
+
*/
|
|
9
16
|
export const ontologyModule: CapabilityModule = {
|
|
10
17
|
name: "ontology",
|
|
11
|
-
verbs: [ontologyListVerb, ontologyShowVerb],
|
|
18
|
+
verbs: [ontologyListVerb, ontologyLookupVerb, ontologyShowVerb],
|
|
12
19
|
};
|
|
@@ -15,13 +15,23 @@ import {
|
|
|
15
15
|
BLOCK_PREFIXES,
|
|
16
16
|
BLOCK_TTL,
|
|
17
17
|
} from "../../testing/fixtures/blockGraph.js";
|
|
18
|
+
import { CANONICAL_TTL } from "../../testing/fixtures/graph/canonical.js";
|
|
19
|
+
import {
|
|
20
|
+
bootFixtureRuntime,
|
|
21
|
+
type FixtureGraph,
|
|
22
|
+
} from "../../testing/helpers/fixtureGraph.js";
|
|
18
23
|
import { buildFixtureRuntime } from "../../testing/helpers/packRuntime.js";
|
|
24
|
+
import { projectMcp } from "../../testing/helpers/projectMcp.js";
|
|
25
|
+
import { capabilities } from "../index.js";
|
|
19
26
|
import { ontologyModule } from "./index.js";
|
|
20
27
|
import type { OntologyShowData, OntologySummary } from "./queries.js";
|
|
21
28
|
|
|
22
29
|
const listVerb = ontologyModule.verbs.find(
|
|
23
30
|
(v) => verbKey(v.path) === "ontology list",
|
|
24
31
|
) as VerbSpec;
|
|
32
|
+
const lookupVerb = ontologyModule.verbs.find(
|
|
33
|
+
(v) => verbKey(v.path) === "ontology lookup",
|
|
34
|
+
) as VerbSpec;
|
|
25
35
|
const showVerb = ontologyModule.verbs.find(
|
|
26
36
|
(v) => verbKey(v.path) === "ontology show",
|
|
27
37
|
) as VerbSpec;
|
|
@@ -48,9 +58,12 @@ describe("ontology list", () => {
|
|
|
48
58
|
});
|
|
49
59
|
});
|
|
50
60
|
|
|
51
|
-
describe("ontology
|
|
61
|
+
describe("ontology lookup (primary by-name read)", () => {
|
|
52
62
|
it("renders the class hierarchy with per-class instance counts from the index", async () => {
|
|
53
|
-
const data = (await
|
|
63
|
+
const data = (await lookupVerb.run(
|
|
64
|
+
{ prefix: "ds" },
|
|
65
|
+
rt,
|
|
66
|
+
)) as OntologyShowData;
|
|
54
67
|
const component = data.classes.find((c) => c.uri.endsWith("Component"));
|
|
55
68
|
expect(component?.superclass).toBe("https://ds.canonical.com/UIBlock");
|
|
56
69
|
// Two Component individuals (Button, Modal) in the fixture.
|
|
@@ -58,8 +71,34 @@ describe("ontology show", () => {
|
|
|
58
71
|
expect(data.properties).toEqual([]);
|
|
59
72
|
});
|
|
60
73
|
|
|
74
|
+
it("--detail detailed folds onto disclosure and includes properties (B5)", async () => {
|
|
75
|
+
// Ontology honours the canonical `--detail` instead of only its bespoke
|
|
76
|
+
// `--properties`: standard/detailed add the properties section.
|
|
77
|
+
const detailed = {
|
|
78
|
+
...rt,
|
|
79
|
+
globalFlags: { ...rt.globalFlags, detail: "detailed" as const },
|
|
80
|
+
};
|
|
81
|
+
const data = (await lookupVerb.run(
|
|
82
|
+
{ prefix: "ds" },
|
|
83
|
+
detailed,
|
|
84
|
+
)) as OntologyShowData;
|
|
85
|
+
expect(data.properties.length).toBeGreaterThan(0);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("--detail summary keeps classes only (no properties)", async () => {
|
|
89
|
+
const summary = {
|
|
90
|
+
...rt,
|
|
91
|
+
globalFlags: { ...rt.globalFlags, detail: "summary" as const },
|
|
92
|
+
};
|
|
93
|
+
const data = (await lookupVerb.run(
|
|
94
|
+
{ prefix: "ds" },
|
|
95
|
+
summary,
|
|
96
|
+
)) as OntologyShowData;
|
|
97
|
+
expect(data.properties).toEqual([]);
|
|
98
|
+
});
|
|
99
|
+
|
|
61
100
|
it("--class focuses on one class and its properties", async () => {
|
|
62
|
-
const data = (await
|
|
101
|
+
const data = (await lookupVerb.run(
|
|
63
102
|
{ prefix: "ds", class: "BlockProperty" },
|
|
64
103
|
rt,
|
|
65
104
|
)) as OntologyShowData;
|
|
@@ -77,18 +116,131 @@ describe("ontology show", () => {
|
|
|
77
116
|
});
|
|
78
117
|
|
|
79
118
|
it("--full-uris flag rides on the payload for the renderer", async () => {
|
|
80
|
-
const data = (await
|
|
119
|
+
const data = (await lookupVerb.run(
|
|
81
120
|
{ prefix: "ds", fullUris: true },
|
|
82
121
|
rt,
|
|
83
122
|
)) as OntologyShowData;
|
|
84
123
|
expect(data.fullUris).toBe(true);
|
|
85
|
-
const llm =
|
|
124
|
+
const llm = lookupVerb.output.formatters.llm(data);
|
|
86
125
|
expect(llm).toContain("https://ds.canonical.com/Component");
|
|
87
126
|
});
|
|
88
127
|
|
|
89
128
|
it("rejects an unknown prefix with recovery", async () => {
|
|
129
|
+
await expect(lookupVerb.run({ prefix: "nope" }, rt)).rejects.toThrow(
|
|
130
|
+
/prefix/i,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("ontology show — deprecated alias of lookup (AV-228 B1)", () => {
|
|
136
|
+
it("is marked deprecated in its summary and steers callers to `ontology lookup`", () => {
|
|
137
|
+
expect(showVerb.summary.toLowerCase()).toContain("deprecated");
|
|
138
|
+
expect(showVerb.summary).toContain("ontology lookup");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("shares the lookup handler — same payload for the same input", async () => {
|
|
142
|
+
const viaLookup = (await lookupVerb.run(
|
|
143
|
+
{ prefix: "ds", properties: true },
|
|
144
|
+
rt,
|
|
145
|
+
)) as OntologyShowData;
|
|
146
|
+
const viaShow = (await showVerb.run(
|
|
147
|
+
{ prefix: "ds", properties: true },
|
|
148
|
+
rt,
|
|
149
|
+
)) as OntologyShowData;
|
|
150
|
+
expect(viaShow).toEqual(viaLookup);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("still rejects an unknown prefix (the alias is a full, callable verb)", async () => {
|
|
90
154
|
await expect(showVerb.run({ prefix: "nope" }, rt)).rejects.toThrow(
|
|
91
155
|
/prefix/i,
|
|
92
156
|
);
|
|
93
157
|
});
|
|
94
158
|
});
|
|
159
|
+
|
|
160
|
+
describe("ontology_show honours detail over MCP (B5)", () => {
|
|
161
|
+
// The CLI B5 cases above seed `globalFlags.detail` and call `showVerb.run`
|
|
162
|
+
// directly. Over MCP there are NO global flags — the ONLY detail channel is
|
|
163
|
+
// the per-tool `detail` param the projector injects from the VerbSpec
|
|
164
|
+
// disclosure. Under an AMBIENT config `detail: standard`, an `ontology_show`
|
|
165
|
+
// with no declared disclosure would force the properties section with no
|
|
166
|
+
// per-call escape (the AV-228 MCP-opt-out asymmetry `block`/`standard` lack);
|
|
167
|
+
// declaring disclosure lights the param up, so an agent can ask for
|
|
168
|
+
// classes-only per call and can equally override upward.
|
|
169
|
+
let fixture: FixtureGraph;
|
|
170
|
+
let mcp: Awaited<ReturnType<typeof projectMcp>>;
|
|
171
|
+
|
|
172
|
+
beforeAll(async () => {
|
|
173
|
+
// Ambient config detail=standard → origins.detail === "project", so it
|
|
174
|
+
// outranks the verb's `summary` default inside resolvePackDetail.
|
|
175
|
+
fixture = await bootFixtureRuntime({
|
|
176
|
+
ttl: CANONICAL_TTL,
|
|
177
|
+
config: { detail: "standard" },
|
|
178
|
+
});
|
|
179
|
+
mcp = await projectMcp(capabilities, fixture.cwd);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
afterAll(async () => {
|
|
183
|
+
await mcp.cleanup();
|
|
184
|
+
await fixture.dispose();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("injects a detail param onto the ontology_show tool (symmetric with block/standard)", async () => {
|
|
188
|
+
const tools = await mcp.listTools();
|
|
189
|
+
const show = tools.find((t) => t.name === "ontology_show");
|
|
190
|
+
const schema = show?.inputSchema as {
|
|
191
|
+
properties?: Record<string, unknown>;
|
|
192
|
+
};
|
|
193
|
+
expect(schema.properties?.detail).toBeDefined();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("exposes the primary ontology_lookup tool with the same injected detail param (AV-228 B1)", async () => {
|
|
197
|
+
const tools = await mcp.listTools();
|
|
198
|
+
const lookup = tools.find((t) => t.name === "ontology_lookup");
|
|
199
|
+
expect(lookup).toBeDefined();
|
|
200
|
+
const schema = lookup?.inputSchema as {
|
|
201
|
+
properties?: Record<string, unknown>;
|
|
202
|
+
};
|
|
203
|
+
expect(schema.properties?.detail).toBeDefined();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("ontology_lookup honours a per-call detail=summary despite the ambient standard", async () => {
|
|
207
|
+
const result = await mcp.callTool("ontology_lookup", {
|
|
208
|
+
prefix: "ds",
|
|
209
|
+
detail: "summary",
|
|
210
|
+
});
|
|
211
|
+
expect(result.ok).toBe(true);
|
|
212
|
+
const data = result.data as { classes: unknown[]; properties: unknown[] };
|
|
213
|
+
expect(data.classes.length).toBeGreaterThan(0);
|
|
214
|
+
expect(data.properties).toEqual([]);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("honours the ambient detail=standard config when no per-call detail is set", async () => {
|
|
218
|
+
// Proves the ambient level really is standard, so the summary case below is
|
|
219
|
+
// a genuine per-call override rather than merely the spec default.
|
|
220
|
+
const result = await mcp.callTool("ontology_show", { prefix: "ds" });
|
|
221
|
+
expect(result.ok).toBe(true);
|
|
222
|
+
const data = result.data as { properties: unknown[] };
|
|
223
|
+
expect(data.properties.length).toBeGreaterThan(0);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("a per-call detail=summary returns classes-only despite the ambient standard", async () => {
|
|
227
|
+
const result = await mcp.callTool("ontology_show", {
|
|
228
|
+
prefix: "ds",
|
|
229
|
+
detail: "summary",
|
|
230
|
+
});
|
|
231
|
+
expect(result.ok).toBe(true);
|
|
232
|
+
const data = result.data as { classes: unknown[]; properties: unknown[] };
|
|
233
|
+
expect(data.classes.length).toBeGreaterThan(0);
|
|
234
|
+
expect(data.properties).toEqual([]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("an explicit detail=detailed overrides upward to include properties", async () => {
|
|
238
|
+
const result = await mcp.callTool("ontology_show", {
|
|
239
|
+
prefix: "ds",
|
|
240
|
+
detail: "detailed",
|
|
241
|
+
});
|
|
242
|
+
expect(result.ok).toBe(true);
|
|
243
|
+
const data = result.data as { properties: unknown[] };
|
|
244
|
+
expect(data.properties.length).toBeGreaterThan(0);
|
|
245
|
+
});
|
|
246
|
+
});
|