@canonical/pragma-cli 0.38.0 → 0.40.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 +1 -1
- package/dist/package.json +14 -14
- package/dist/pragma.conf.js +273 -176
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
- package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
- package/dist/src/capabilities/capabilities/catalog.js +47 -51
- package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
- package/dist/src/capabilities/capabilities/index.js +2 -0
- package/dist/src/capabilities/capabilities/index.js.map +1 -1
- package/dist/src/capabilities/capabilities/types.js +5 -5
- package/dist/src/capabilities/colophon/colophon.render.js +7 -4
- package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
- package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
- package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
- package/dist/src/capabilities/config/get.verb.js +2 -0
- package/dist/src/capabilities/config/get.verb.js.map +1 -1
- package/dist/src/capabilities/config/runField.js +2 -2
- package/dist/src/capabilities/config/runField.js.map +1 -1
- package/dist/src/capabilities/config/set.verb.js +2 -0
- package/dist/src/capabilities/config/set.verb.js.map +1 -1
- package/dist/src/capabilities/config/show.verb.js +1 -0
- package/dist/src/capabilities/config/show.verb.js.map +1 -1
- package/dist/src/capabilities/config/unset.verb.js +2 -0
- package/dist/src/capabilities/config/unset.verb.js.map +1 -1
- package/dist/src/capabilities/create/constants.js +2 -2
- package/dist/src/capabilities/create/create.verb.js +14 -4
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/distribution.js +3 -1
- package/dist/src/capabilities/distribution.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
- package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
- package/dist/src/capabilities/graph/connect.verb.js +2 -0
- package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.render.js +7 -1
- package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.verb.js +2 -0
- package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/query.render.js +37 -5
- package/dist/src/capabilities/graph/query.render.js.map +1 -1
- package/dist/src/capabilities/graph/query.verb.js +2 -0
- package/dist/src/capabilities/graph/query.verb.js.map +1 -1
- package/dist/src/capabilities/graph/queryText.js +28 -0
- package/dist/src/capabilities/graph/queryText.js.map +1 -1
- package/dist/src/capabilities/graph/runConnect.js +3 -3
- package/dist/src/capabilities/graph/runConnect.js.map +1 -1
- package/dist/src/capabilities/graph/runQuery.js +8 -6
- package/dist/src/capabilities/graph/runQuery.js.map +1 -1
- package/dist/src/capabilities/info/info.verb.js +2 -0
- package/dist/src/capabilities/info/info.verb.js.map +1 -1
- package/dist/src/capabilities/meta/mcp.verb.js +1 -0
- package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
- package/dist/src/capabilities/ontology/verbs.js +7 -10
- package/dist/src/capabilities/ontology/verbs.js.map +1 -1
- package/dist/src/capabilities/prompt/verbs.js +5 -4
- package/dist/src/capabilities/prompt/verbs.js.map +1 -1
- package/dist/src/capabilities/resources/provider.js +6 -3
- package/dist/src/capabilities/resources/provider.js.map +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +13 -5
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/shared/calls.js +16 -0
- package/dist/src/capabilities/shared/calls.js.map +1 -0
- package/dist/src/capabilities/skill/render.js +7 -5
- package/dist/src/capabilities/skill/render.js.map +1 -1
- package/dist/src/capabilities/skill/verbs.js +5 -4
- package/dist/src/capabilities/skill/verbs.js.map +1 -1
- package/dist/src/capabilities/sources/collectStatus.js +6 -1
- package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
- package/dist/src/capabilities/sources/index.js +4 -3
- package/dist/src/capabilities/sources/index.js.map +1 -1
- package/dist/src/capabilities/sources/reset.render.js +37 -0
- package/dist/src/capabilities/sources/reset.render.js.map +1 -0
- package/dist/src/capabilities/sources/reset.verb.js +51 -0
- package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
- package/dist/src/capabilities/sources/runReset.js +59 -0
- package/dist/src/capabilities/sources/runReset.js.map +1 -0
- package/dist/src/capabilities/sources/runUpdate.js +7 -6
- package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
- package/dist/src/capabilities/sources/status.render.js +24 -8
- package/dist/src/capabilities/sources/status.render.js.map +1 -1
- package/dist/src/capabilities/sources/status.verb.js +1 -0
- package/dist/src/capabilities/sources/status.verb.js.map +1 -1
- package/dist/src/capabilities/sources/update.verb.js +2 -0
- package/dist/src/capabilities/sources/update.verb.js.map +1 -1
- package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
- package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
- package/dist/src/capabilities/version/version.verb.js +1 -0
- package/dist/src/capabilities/version/version.verb.js.map +1 -1
- package/dist/src/constants.js +4 -3
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/error/index.js +1 -1
- package/dist/src/kernel/error/index.js.map +1 -1
- package/dist/src/kernel/error/recovery.js +24 -42
- package/dist/src/kernel/error/recovery.js.map +1 -1
- package/dist/src/kernel/packs/collect.js +123 -9
- package/dist/src/kernel/packs/collect.js.map +1 -1
- package/dist/src/kernel/packs/compile.js +74 -15
- package/dist/src/kernel/packs/compile.js.map +1 -1
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
- package/dist/src/kernel/packs/iri.js +2 -4
- package/dist/src/kernel/packs/iri.js.map +1 -1
- package/dist/src/kernel/packs/renderPack.js +90 -50
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +202 -90
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +89 -30
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +66 -12
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
- package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
- package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
- package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
- package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
- package/dist/src/kernel/packs/storyRules.js +31 -4
- package/dist/src/kernel/packs/storyRules.js.map +1 -1
- package/dist/src/kernel/packs/tierScope.js +3 -3
- package/dist/src/kernel/packs/tierScope.js.map +1 -1
- package/dist/src/kernel/packs/types.js +4 -0
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +4 -0
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +32 -8
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/suggestNames.js +44 -1
- package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
- package/dist/src/kernel/project/cli/verbHelp.js +17 -18
- package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
- package/dist/src/kernel/project/mcp/buildServer.js +11 -4
- package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
- package/dist/src/kernel/project/mcp/envelope.js +3 -2
- package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
- package/dist/src/kernel/project/mcp/instructions.js +101 -31
- package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +3 -3
- package/dist/src/kernel/render/turtle.js +21 -4
- package/dist/src/kernel/render/turtle.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/build.js +54 -1
- package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
- package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/read.js +3 -5
- package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
- package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
- package/dist/src/kernel/runtime/loadSession.js +8 -1
- package/dist/src/kernel/runtime/loadSession.js.map +1 -1
- package/dist/src/kernel/runtime/packVersion.js +75 -0
- package/dist/src/kernel/runtime/packVersion.js.map +1 -0
- package/dist/src/kernel/runtime/readEntity.js +43 -4
- package/dist/src/kernel/runtime/readEntity.js.map +1 -1
- package/dist/src/kernel/runtime/resolveSources.js +61 -9
- package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
- package/dist/src/kernel/runtime/storeReadiness.js +3 -2
- package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
- package/dist/src/kernel/spec/call.js +167 -0
- package/dist/src/kernel/spec/call.js.map +1 -0
- package/dist/src/kernel/spec/emitReference.js +9 -13
- package/dist/src/kernel/spec/emitReference.js.map +1 -1
- package/dist/src/kernel/spec/emitSurface.js +8 -1
- package/dist/src/kernel/spec/emitSurface.js.map +1 -1
- package/dist/src/kernel/spec/guidance.js +63 -0
- package/dist/src/kernel/spec/guidance.js.map +1 -0
- package/dist/src/kernel/spec/wireType.js +36 -0
- package/dist/src/kernel/spec/wireType.js.map +1 -0
- package/package.json +14 -14
- package/pragma.conf.ts +294 -177
- package/src/capabilities/block.parity.test.ts +1 -1
- package/src/capabilities/callRule.test.ts +147 -0
- package/src/capabilities/capabilities/capabilities.test.ts +47 -19
- package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
- package/src/capabilities/capabilities/catalog.ts +60 -55
- package/src/capabilities/capabilities/index.ts +2 -0
- package/src/capabilities/capabilities/types.ts +16 -19
- package/src/capabilities/colophon/colophon.render.ts +9 -5
- package/src/capabilities/colophon/colophon.verb.ts +1 -0
- package/src/capabilities/config/get.verb.ts +2 -0
- package/src/capabilities/config/runField.ts +3 -3
- package/src/capabilities/config/set.verb.ts +3 -0
- package/src/capabilities/config/show.verb.ts +2 -0
- package/src/capabilities/config/unset.verb.ts +2 -0
- package/src/capabilities/create/constants.ts +2 -2
- package/src/capabilities/create/create.verb.ts +16 -0
- package/src/capabilities/create/createGrammar.test.ts +1 -1
- package/src/capabilities/distribution.test.ts +6 -3
- package/src/capabilities/distribution.ts +6 -0
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
- package/src/capabilities/doctor/doctor.test.ts +38 -1
- package/src/capabilities/doctor/doctor.verb.ts +2 -0
- package/src/capabilities/emptyRecovery.test.ts +5 -0
- package/src/capabilities/graph/connect.verb.ts +2 -0
- package/src/capabilities/graph/inspect.render.ts +9 -1
- package/src/capabilities/graph/inspect.test.ts +73 -0
- package/src/capabilities/graph/inspect.verb.ts +2 -0
- package/src/capabilities/graph/query.render.test.ts +35 -0
- package/src/capabilities/graph/query.render.ts +45 -7
- package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
- package/src/capabilities/graph/query.verb.ts +2 -0
- package/src/capabilities/graph/queryText.test.ts +55 -1
- package/src/capabilities/graph/queryText.ts +47 -0
- package/src/capabilities/graph/runConnect.ts +4 -5
- package/src/capabilities/graph/runQuery.ts +23 -9
- package/src/capabilities/info/info.verb.ts +2 -0
- package/src/capabilities/links.shipped.exec.test.ts +398 -0
- package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
- package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
- package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
- package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
- package/src/capabilities/meta/mcp.verb.ts +2 -0
- package/src/capabilities/ontology/ontology.test.ts +24 -0
- package/src/capabilities/ontology/verbs.ts +16 -12
- package/src/capabilities/prompt/verbs.ts +10 -4
- package/src/capabilities/resources/provider.ts +7 -3
- package/src/capabilities/resources/resources.test.ts +2 -1
- package/src/capabilities/setup/setup.verb.ts +16 -0
- package/src/capabilities/shared/calls.ts +20 -0
- package/src/capabilities/skill/render.ts +9 -6
- package/src/capabilities/skill/verbs.ts +8 -4
- package/src/capabilities/sources/collectStatus.ts +6 -1
- package/src/capabilities/sources/index.ts +4 -3
- package/src/capabilities/sources/reset.render.test.ts +33 -0
- package/src/capabilities/sources/reset.render.ts +43 -0
- package/src/capabilities/sources/reset.verb.ts +59 -0
- package/src/capabilities/sources/runReset.ts +65 -0
- package/src/capabilities/sources/runUpdate.ts +12 -11
- package/src/capabilities/sources/sources.test.ts +158 -3
- package/src/capabilities/sources/status.render.test.ts +63 -1
- package/src/capabilities/sources/status.render.ts +25 -8
- package/src/capabilities/sources/status.verb.ts +2 -0
- package/src/capabilities/sources/types.ts +26 -0
- package/src/capabilities/sources/update.verb.ts +3 -0
- package/src/capabilities/surface.test.ts +2 -2
- package/src/capabilities/token.parity.test.ts +3 -2
- package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
- package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
- package/src/capabilities/variable.parity.test.ts +2 -1
- package/src/capabilities/version/version.verb.ts +1 -0
- package/src/capabilities/wireContract.test.ts +110 -0
- package/src/constants.ts +4 -3
- package/src/kernel/copy.test.ts +1 -1
- package/src/kernel/error/PragmaError.test.ts +7 -3
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +52 -18
- package/src/kernel/error/recovery.ts +25 -46
- package/src/kernel/error/types.ts +1 -1
- package/src/kernel/packs/collect.test.ts +79 -0
- package/src/kernel/packs/collect.ts +151 -11
- package/src/kernel/packs/compile.test.ts +107 -10
- package/src/kernel/packs/compile.ts +100 -11
- package/src/kernel/packs/filteredEmpty.test.ts +22 -5
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
- package/src/kernel/packs/graphqlSource.test.ts +42 -0
- package/src/kernel/packs/iri.ts +5 -4
- package/src/kernel/packs/listNotice.test.ts +1 -1
- package/src/kernel/packs/nounLink.test.ts +385 -0
- package/src/kernel/packs/renderPack.ts +137 -57
- package/src/kernel/packs/resolveEntity.test.ts +170 -1
- package/src/kernel/packs/resolveEntity.ts +281 -101
- package/src/kernel/packs/runBodies.ts +133 -31
- package/src/kernel/packs/schema.ts +77 -12
- package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
- package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
- package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
- package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
- package/src/kernel/packs/sparql/filterValues.ts +50 -31
- package/src/kernel/packs/sparql/runSelect.ts +4 -6
- package/src/kernel/packs/storyRules.ts +37 -4
- package/src/kernel/packs/tierScope.test.ts +120 -6
- package/src/kernel/packs/tierScope.ts +5 -7
- package/src/kernel/packs/toolDescription.test.ts +59 -74
- package/src/kernel/packs/types.ts +93 -11
- package/src/kernel/project/cli/buildProgram.ts +4 -0
- package/src/kernel/project/cli/dispatch.ts +34 -8
- package/src/kernel/project/cli/suggest.test.ts +77 -0
- package/src/kernel/project/cli/suggestNames.ts +47 -1
- package/src/kernel/project/cli/verbHelp.ts +16 -19
- package/src/kernel/project/mcp/buildServer.ts +15 -4
- package/src/kernel/project/mcp/envelope.ts +3 -2
- package/src/kernel/project/mcp/instructions.ts +123 -35
- package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
- package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
- package/src/kernel/project/mcp/registerVerb.ts +67 -29
- package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
- package/src/kernel/render/renderers.test.ts +2 -2
- package/src/kernel/render/renderers.ts +3 -3
- package/src/kernel/render/turtle.ts +24 -4
- package/src/kernel/runtime/graphpack/build.ts +58 -1
- package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
- package/src/kernel/runtime/graphpack/manifest.ts +15 -0
- package/src/kernel/runtime/graphpack/read.ts +4 -6
- package/src/kernel/runtime/graphpack/schemas.ts +1 -0
- package/src/kernel/runtime/graphpack/types.ts +13 -0
- package/src/kernel/runtime/loadSession.ts +8 -1
- package/src/kernel/runtime/packVersion.test.ts +61 -0
- package/src/kernel/runtime/packVersion.ts +84 -0
- package/src/kernel/runtime/readEntity.ts +61 -6
- package/src/kernel/runtime/resolveSources.test.ts +90 -7
- package/src/kernel/runtime/resolveSources.ts +91 -10
- package/src/kernel/runtime/storeReadiness.ts +4 -4
- package/src/kernel/runtime/types.ts +12 -0
- package/src/kernel/spec/call.test.ts +95 -0
- package/src/kernel/spec/call.ts +186 -0
- package/src/kernel/spec/emitReference.ts +9 -13
- package/src/kernel/spec/emitSurface.ts +9 -1
- package/src/kernel/spec/guidance.test.ts +49 -0
- package/src/kernel/spec/guidance.ts +68 -0
- package/src/kernel/spec/index.ts +4 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/types.ts +64 -7
- package/src/kernel/spec/wireType.ts +53 -0
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
- package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
- package/src/testing/behavioral/parity.test.ts +40 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
- package/src/testing/eval/cases/assembly.ts +4 -3
- package/src/testing/eval/cases/stable.ts +2 -3
- package/src/testing/helpers/callSchema.ts +40 -0
- package/src/testing/helpers/parity.ts +35 -2
- package/src/testing/setupCallChecking.ts +30 -0
- package/dist/src/capabilities/capabilities/hints.js +0 -223
- package/dist/src/capabilities/capabilities/hints.js.map +0 -1
- package/src/capabilities/capabilities/hints.ts +0 -266
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
} from "../../kernel/config/types.js";
|
|
24
24
|
import { PragmaError } from "../../kernel/error/PragmaError.js";
|
|
25
25
|
import { executeVerb } from "../../kernel/project/cli/dispatch.js";
|
|
26
|
+
import { styleFor } from "../../kernel/render/style.js";
|
|
26
27
|
import { bootRuntime } from "../../kernel/runtime/boot.js";
|
|
27
28
|
import { createQueryFacade } from "../../kernel/runtime/facade.js";
|
|
28
29
|
import { readManifest } from "../../kernel/runtime/graphpack/manifest.js";
|
|
@@ -44,6 +45,7 @@ import { discoverSkills } from "../skill/discover.js";
|
|
|
44
45
|
import { collectStatus } from "./collectStatus.js";
|
|
45
46
|
import { sourcesModule } from "./index.js";
|
|
46
47
|
import { buildUpdateTask } from "./runUpdate.js";
|
|
48
|
+
import { renderSourcesStatusPlain } from "./status.render.js";
|
|
47
49
|
|
|
48
50
|
const FLAGS: GlobalFlags = {
|
|
49
51
|
llm: false,
|
|
@@ -69,15 +71,25 @@ const tmp = (prefix: string): string => {
|
|
|
69
71
|
return dir;
|
|
70
72
|
};
|
|
71
73
|
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* A runtime whose config is the given pack list (no config files needed).
|
|
76
|
+
*
|
|
77
|
+
* `packsOrigin` is the knob the boot decision reads to tell "this project pinned
|
|
78
|
+
* its own packs" from "the distribution's defaults" — `"project"` by default,
|
|
79
|
+
* since that is what a test that BUILDS a pack is describing.
|
|
80
|
+
*/
|
|
81
|
+
function runtimeFor(
|
|
82
|
+
cwd: string,
|
|
83
|
+
packs: PackDeclaration[],
|
|
84
|
+
packsOrigin: "default" | "project" = "project",
|
|
85
|
+
): PragmaRuntime {
|
|
74
86
|
const layers: ConfigLayers = {
|
|
75
87
|
config: { channel: "normal", packs },
|
|
76
88
|
origins: {
|
|
77
89
|
tier: "default",
|
|
78
90
|
channel: "default",
|
|
79
91
|
detail: "default",
|
|
80
|
-
packs:
|
|
92
|
+
packs: packsOrigin,
|
|
81
93
|
stories: "default",
|
|
82
94
|
prefixes: "default",
|
|
83
95
|
},
|
|
@@ -562,6 +574,9 @@ describe("sources update — data-failure classification (U6)", () => {
|
|
|
562
574
|
expect(err.recovery?.message ?? "").not.toContain("report this issue");
|
|
563
575
|
// The recovery points the user at a runnable, useful next step.
|
|
564
576
|
expect(err.recovery?.cli).toBe("pragma sources update --verbose");
|
|
577
|
+
// `--verbose` is a global CLI flag with no MCP counterpart, and a hint
|
|
578
|
+
// never carries `confirm`: the plan the tool returns says how to proceed.
|
|
579
|
+
expect(err.recovery?.mcp).toEqual({ tool: "sources_update", params: {} });
|
|
565
580
|
// Nothing was pointed at on failure.
|
|
566
581
|
expect(readActivePack(cwd)).toBeUndefined();
|
|
567
582
|
});
|
|
@@ -1255,3 +1270,143 @@ describe("sources status CLI-json == MCP tool (PROTECTED)", () => {
|
|
|
1255
1270
|
expect((cliEnvelope.data as { store: string }).store).toBe("embedded");
|
|
1256
1271
|
});
|
|
1257
1272
|
});
|
|
1273
|
+
|
|
1274
|
+
describe("a pack an older CLI built (upgrade healing)", () => {
|
|
1275
|
+
const resetVerb = sourcesModule.verbs[2] as VerbSpec;
|
|
1276
|
+
const REAL = { dryRun: false, undo: false, yes: true };
|
|
1277
|
+
/** Any version below the running CLI's — what an older build recorded. */
|
|
1278
|
+
const OLDER = "0.1.0";
|
|
1279
|
+
|
|
1280
|
+
/** Build a real pack for `cwd`, then rewrite the builder version it records. */
|
|
1281
|
+
async function builtWithVersion(
|
|
1282
|
+
cwd: string,
|
|
1283
|
+
version: string,
|
|
1284
|
+
): Promise<string> {
|
|
1285
|
+
const pkg = filePackage();
|
|
1286
|
+
const runtime = runtimeFor(cwd, [
|
|
1287
|
+
{ name: "pkg-old", source: `file://${pkg}` },
|
|
1288
|
+
]);
|
|
1289
|
+
const result = await runTask(await buildUpdateTask(runtime));
|
|
1290
|
+
const path = join(packDir(result.contentHash), "manifest.json");
|
|
1291
|
+
const manifest = JSON.parse(readFileSync(path, "utf-8"));
|
|
1292
|
+
writeFileSync(path, JSON.stringify({ ...manifest, version }));
|
|
1293
|
+
return result.contentHash;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
it("status reports the snapshot answering, and names the pack passed over", async () => {
|
|
1297
|
+
const cwd = tmp("pragma-proj-");
|
|
1298
|
+
const hash = await builtWithVersion(cwd, OLDER);
|
|
1299
|
+
|
|
1300
|
+
// The reported install: a pointer to a pack an older CLI built, and a config
|
|
1301
|
+
// that pins no packs of its own (the distribution's defaults).
|
|
1302
|
+
const status = await collectStatus(runtimeFor(cwd, [], "default"));
|
|
1303
|
+
expect(status.store).toBe("embedded");
|
|
1304
|
+
expect(status.ignoredPack).toEqual({
|
|
1305
|
+
contentHash: hash,
|
|
1306
|
+
builtBy: OLDER,
|
|
1307
|
+
builtAt: expect.any(String),
|
|
1308
|
+
});
|
|
1309
|
+
expect(renderSourcesStatusPlain(status, styleFor(false))).toContain(
|
|
1310
|
+
`a pack built by pragma ${OLDER} on`,
|
|
1311
|
+
);
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
it("a project with its OWN packs keeps reading its pack, stale or not", async () => {
|
|
1315
|
+
const cwd = tmp("pragma-proj-");
|
|
1316
|
+
const hash = await builtWithVersion(cwd, OLDER);
|
|
1317
|
+
|
|
1318
|
+
// Byte-identical to the behaviour before the upgrade row existed: its own
|
|
1319
|
+
// packs are its own graph, and the snapshot is not a substitute for them.
|
|
1320
|
+
const status = await collectStatus(
|
|
1321
|
+
runtimeFor(cwd, [{ name: "pkg-old", source: "file:///x" }], "project"),
|
|
1322
|
+
);
|
|
1323
|
+
expect(status.store).toBe("built");
|
|
1324
|
+
expect(status.contentHash).toBe(hash);
|
|
1325
|
+
expect(status.ignoredPack).toBeNull();
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
it("re-running the update makes the pack answer again, and the notice goes", async () => {
|
|
1329
|
+
const cwd = tmp("pragma-proj-");
|
|
1330
|
+
const hash = await builtWithVersion(cwd, OLDER);
|
|
1331
|
+
const pkg = filePackage();
|
|
1332
|
+
|
|
1333
|
+
// The STRICTEST form of this case: the sources have not moved, so the update
|
|
1334
|
+
// reuses the very same content-addressed directory rather than building a
|
|
1335
|
+
// new one. Reuse has to refresh the recorded builder version, or the update
|
|
1336
|
+
// the notice recommends would change nothing and the project would be stuck.
|
|
1337
|
+
const rebuilt = await runTask(
|
|
1338
|
+
await buildUpdateTask(
|
|
1339
|
+
runtimeFor(cwd, [{ name: "pkg-old", source: `file://${pkg}` }]),
|
|
1340
|
+
),
|
|
1341
|
+
);
|
|
1342
|
+
expect(rebuilt.contentHash).toBe(hash);
|
|
1343
|
+
expect(rebuilt.reused).toBe(true);
|
|
1344
|
+
expect(readManifest(packDir(hash))?.version).toBe(VERSION);
|
|
1345
|
+
|
|
1346
|
+
const status = await collectStatus(runtimeFor(cwd, [], "default"));
|
|
1347
|
+
expect(status.store).toBe("built");
|
|
1348
|
+
expect(status.ignoredPack).toBeNull();
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
it("`sources reset` drops the pointer, and the snapshot answers", async () => {
|
|
1352
|
+
const cwd = tmp("pragma-proj-");
|
|
1353
|
+
const hash = await builtWithVersion(cwd, OLDER);
|
|
1354
|
+
|
|
1355
|
+
const outcome = await executeVerb(
|
|
1356
|
+
resetVerb,
|
|
1357
|
+
{},
|
|
1358
|
+
REAL,
|
|
1359
|
+
bootRuntime(FLAGS, cwd),
|
|
1360
|
+
);
|
|
1361
|
+
expect(outcome.exitCode).toBe(0);
|
|
1362
|
+
expect(outcome.stdout).toContain(hash.slice(0, 12));
|
|
1363
|
+
expect(outcome.stdout).toContain("snapshot shipped with the CLI");
|
|
1364
|
+
// The pointer is gone, so the boot has nothing to pass over any more.
|
|
1365
|
+
expect(readActivePack(cwd)).toBeUndefined();
|
|
1366
|
+
const status = await collectStatus(bootRuntime(FLAGS, cwd));
|
|
1367
|
+
expect(status.store).toBe("embedded");
|
|
1368
|
+
expect(status.ignoredPack).toBeNull();
|
|
1369
|
+
// The pack's own files are left alone: the cache is content-addressed and
|
|
1370
|
+
// shared, so another project reading the same hash is untouched, and the
|
|
1371
|
+
// next update reuses it instead of rebuilding.
|
|
1372
|
+
expect(existsSync(packDir(hash))).toBe(true);
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
it("a second reset is a calm no-op, not a failure", async () => {
|
|
1376
|
+
const cwd = tmp("pragma-proj-");
|
|
1377
|
+
await builtWithVersion(cwd, OLDER);
|
|
1378
|
+
await executeVerb(resetVerb, {}, REAL, bootRuntime(FLAGS, cwd));
|
|
1379
|
+
|
|
1380
|
+
const again = await executeVerb(
|
|
1381
|
+
resetVerb,
|
|
1382
|
+
{},
|
|
1383
|
+
REAL,
|
|
1384
|
+
bootRuntime(FLAGS, cwd),
|
|
1385
|
+
);
|
|
1386
|
+
expect(again.exitCode).toBe(0);
|
|
1387
|
+
expect(again.stdout).toContain("nothing to reset");
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
it("MCP sources_reset without confirm plans only, and removes nothing", async () => {
|
|
1391
|
+
const cwd = tmp("pragma-proj-");
|
|
1392
|
+
const hash = await builtWithVersion(cwd, OLDER);
|
|
1393
|
+
|
|
1394
|
+
const mcp = await projectMcp([sourcesModule], cwd);
|
|
1395
|
+
const envelope = await mcp.callTool("sources_reset"); // no confirm
|
|
1396
|
+
expect(envelope.ok).toBe(true);
|
|
1397
|
+
expect(envelope.meta).toMatchObject({
|
|
1398
|
+
planOnly: true,
|
|
1399
|
+
confirmRequired: true,
|
|
1400
|
+
});
|
|
1401
|
+
const plan = (envelope.data as { plan: string[] }).plan;
|
|
1402
|
+
expect(plan.some((line) => line.includes(activePackPath(cwd)))).toBe(true);
|
|
1403
|
+
expect(readActivePack(cwd)).toBe(hash);
|
|
1404
|
+
|
|
1405
|
+
// And with `confirm`, the same call performs it.
|
|
1406
|
+
const done = await mcp.callTool("sources_reset", { confirm: true });
|
|
1407
|
+
await mcp.cleanup();
|
|
1408
|
+
expect(done.ok).toBe(true);
|
|
1409
|
+
expect((done.data as { removed: boolean }).removed).toBe(true);
|
|
1410
|
+
expect(readActivePack(cwd)).toBeUndefined();
|
|
1411
|
+
});
|
|
1412
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { type RenderStyle, styleFor } from "../../kernel/render/style.js";
|
|
3
|
-
import { renderSourcesStatusPlain } from "./status.render.js";
|
|
3
|
+
import { renderSourcesStatusPlain, statusFormatters } from "./status.render.js";
|
|
4
4
|
import type { SourcesStatusData } from "./types.js";
|
|
5
5
|
|
|
6
6
|
/** A project reading from its own built pack, with two configured sources. */
|
|
@@ -11,6 +11,7 @@ const BUILT: SourcesStatusData = {
|
|
|
11
11
|
sourceRef: "ds,extra",
|
|
12
12
|
builtAt: "2026-01-01",
|
|
13
13
|
entityCount: 42,
|
|
14
|
+
ignoredPack: null,
|
|
14
15
|
sources: [
|
|
15
16
|
{ name: "ds", ref: "git+https://example.test/ds.git#main" },
|
|
16
17
|
{ name: "extra", ref: "extra@1" },
|
|
@@ -84,6 +85,45 @@ describe("renderSourcesStatusPlain", () => {
|
|
|
84
85
|
expect(out).not.toContain(" pack: ");
|
|
85
86
|
});
|
|
86
87
|
|
|
88
|
+
it("names the built pack the snapshot is answering in place of", () => {
|
|
89
|
+
// The upgrade state: this project HAS a pack, an older CLI built it, and the
|
|
90
|
+
// snapshot is answering. The headline still reports what answers (the
|
|
91
|
+
// snapshot); the extra line is the pack that is being passed over, and both
|
|
92
|
+
// ways out of it.
|
|
93
|
+
const out = renderSourcesStatusPlain(
|
|
94
|
+
{
|
|
95
|
+
...BUILT,
|
|
96
|
+
store: "embedded",
|
|
97
|
+
ignoredPack: {
|
|
98
|
+
contentHash: "0e82d35c66687b8a",
|
|
99
|
+
builtBy: "0.37.0",
|
|
100
|
+
builtAt: "2026-09-10T21:49:00.411Z",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
styleFor(false),
|
|
104
|
+
);
|
|
105
|
+
expect(out).toContain("Store: embedded snapshot");
|
|
106
|
+
expect(out).toContain(
|
|
107
|
+
" 0e82d35c6668: a pack built by pragma 0.37.0 on 2026-09-10 is ignored — run `pragma sources update` to rebuild it or `pragma sources reset` to remove it",
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("the llm form names the ignored pack and both ways out", () => {
|
|
112
|
+
const out = statusFormatters.llm({
|
|
113
|
+
...BUILT,
|
|
114
|
+
store: "embedded",
|
|
115
|
+
ignoredPack: {
|
|
116
|
+
contentHash: "0e82d35c66687b8a",
|
|
117
|
+
builtBy: "0.37.0",
|
|
118
|
+
builtAt: "2026-09-10T21:49:00.411Z",
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
expect(out).toContain(
|
|
122
|
+
"- Ignored: a pack built by pragma 0.37.0 on 2026-09-10 is ignored",
|
|
123
|
+
);
|
|
124
|
+
expect(out).toContain("`pragma sources reset`");
|
|
125
|
+
});
|
|
126
|
+
|
|
87
127
|
it("says so when no packs are configured", () => {
|
|
88
128
|
const out = renderSourcesStatusPlain(
|
|
89
129
|
{ ...BUILT, sources: [] },
|
|
@@ -92,3 +132,25 @@ describe("renderSourcesStatusPlain", () => {
|
|
|
92
132
|
expect(out).toContain(" (none configured)");
|
|
93
133
|
});
|
|
94
134
|
});
|
|
135
|
+
|
|
136
|
+
describe("sources status — the condensed form keeps the next step", () => {
|
|
137
|
+
it("says what to run when the store is not built, and nothing extra when it is", () => {
|
|
138
|
+
const unavailable: SourcesStatusData = {
|
|
139
|
+
...BUILT,
|
|
140
|
+
store: "unavailable",
|
|
141
|
+
contentHash: null,
|
|
142
|
+
sourceRef: null,
|
|
143
|
+
builtAt: null,
|
|
144
|
+
entityCount: null,
|
|
145
|
+
};
|
|
146
|
+
expect(statusFormatters.llm(unavailable)).toContain(
|
|
147
|
+
"- Store: not built (run `pragma sources update`)",
|
|
148
|
+
);
|
|
149
|
+
expect(
|
|
150
|
+
statusFormatters.llm({ ...unavailable, store: "embedded" }),
|
|
151
|
+
).toContain(
|
|
152
|
+
"- Store: embedded snapshot (run `pragma sources update` to build from the configured packs)",
|
|
153
|
+
);
|
|
154
|
+
expect(statusFormatters.llm(BUILT)).toContain("- Store: ready");
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
* Formatters for `pragma sources status` — plain, llm, json.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { BIN_NAME } from "../../constants.js";
|
|
6
5
|
import { defaultStyle, type RenderStyle } from "../../kernel/render/style.js";
|
|
6
|
+
import { describeIgnoredPack } from "../../kernel/runtime/resolveSources.js";
|
|
7
|
+
import { renderCall } from "../../kernel/spec/call.js";
|
|
7
8
|
import type { Formatters } from "../../kernel/spec/index.js";
|
|
9
|
+
import { BUILD_STORE_CALL } from "../shared/calls.js";
|
|
8
10
|
import type { SourcesStatusData } from "./types.js";
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -12,11 +14,13 @@ import type { SourcesStatusData } from "./types.js";
|
|
|
12
14
|
* explicitly: the distribution's snapshot answers reads, but it is a snapshot,
|
|
13
15
|
* so reporting it as "up to date" would be a lie.
|
|
14
16
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
17
|
+
function storeHeadline(store: SourcesStatusData["store"]): string {
|
|
18
|
+
const update = `run \`${renderCall(BUILD_STORE_CALL, "cli")}\``;
|
|
19
|
+
if (store === "built") return "ready";
|
|
20
|
+
return store === "embedded"
|
|
21
|
+
? `embedded snapshot (${update} to build from the configured packs)`
|
|
22
|
+
: `not built (${update})`;
|
|
23
|
+
}
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Render `sources status` as plain text.
|
|
@@ -32,13 +36,22 @@ export function renderSourcesStatusPlain(
|
|
|
32
36
|
data: SourcesStatusData,
|
|
33
37
|
style: RenderStyle = defaultStyle(),
|
|
34
38
|
): string {
|
|
35
|
-
const lines = [`Store: ${
|
|
39
|
+
const lines = [`Store: ${storeHeadline(data.store)}`];
|
|
36
40
|
if (data.contentHash !== null) {
|
|
37
41
|
lines.push(
|
|
38
42
|
` pack: ${data.contentHash.slice(0, 12)} — ${data.entityCount ?? "?"} entities, built ${data.builtAt ?? "?"}`,
|
|
39
43
|
` from: ${data.sourceRef ?? "?"}`,
|
|
40
44
|
);
|
|
41
45
|
}
|
|
46
|
+
// The pack this project built and is NOT reading. It says so on its own line
|
|
47
|
+
// rather than in the headline: the headline answers "what is answering", and
|
|
48
|
+
// the answer (the snapshot) is unchanged — this is the extra fact that the
|
|
49
|
+
// directory has a pack and it is being passed over.
|
|
50
|
+
if (data.ignoredPack !== null) {
|
|
51
|
+
lines.push(
|
|
52
|
+
` ${data.ignoredPack.contentHash.slice(0, 12)}: ${describeIgnoredPack(data.ignoredPack)}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
42
55
|
lines.push("", style.enabled ? style.bold("Sources:") : "Sources:");
|
|
43
56
|
if (data.sources.length === 0) {
|
|
44
57
|
lines.push(" (none configured)");
|
|
@@ -62,7 +75,11 @@ export const statusFormatters: Formatters<SourcesStatusData> = {
|
|
|
62
75
|
},
|
|
63
76
|
|
|
64
77
|
llm(data) {
|
|
65
|
-
|
|
78
|
+
// The next step rides the condensed form too: an agent reading `embedded`
|
|
79
|
+
// or `unavailable` bare was told a state and not what to do about it.
|
|
80
|
+
const lines = [`# sources`, `- Store: ${storeHeadline(data.store)}`];
|
|
81
|
+
if (data.ignoredPack !== null)
|
|
82
|
+
lines.push(`- Ignored: ${describeIgnoredPack(data.ignoredPack)}`);
|
|
66
83
|
if (data.sourceRef !== null) lines.push(`- From: ${data.sourceRef}`);
|
|
67
84
|
if (data.entityCount !== null)
|
|
68
85
|
lines.push(`- Entities: ${data.entityCount}`);
|
|
@@ -21,6 +21,8 @@ export const statusVerb: VerbSpec<
|
|
|
21
21
|
path: ["sources", "status"],
|
|
22
22
|
summary: "Report which pack answers reads, and the packs it was built from.",
|
|
23
23
|
doc: "Storeless — reads config and the pack cache without booting the store, so it works even when the store is cold. Reports whether reads are answered by a locally built pack, by the embedded snapshot, or not at all.",
|
|
24
|
+
useWhen:
|
|
25
|
+
"when results look empty or stale, to see which data pack is answering",
|
|
24
26
|
params: [],
|
|
25
27
|
output: { formatters: statusFormatters },
|
|
26
28
|
examples: [
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* (the Task that resolves, builds, and points the project at a pack).
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { IgnoredPack } from "../../kernel/runtime/resolveSources.js";
|
|
7
|
+
|
|
6
8
|
/** The `sources status` payload — assembled without booting the store. */
|
|
7
9
|
export interface SourcesStatusData {
|
|
8
10
|
readonly cwd: string;
|
|
@@ -19,6 +21,15 @@ export interface SourcesStatusData {
|
|
|
19
21
|
readonly builtAt: string | null;
|
|
20
22
|
/** Total indexed entity count from the answering pack, or null. */
|
|
21
23
|
readonly entityCount: number | null;
|
|
24
|
+
/**
|
|
25
|
+
* The project's own built pack when the boot passed over it — built by an
|
|
26
|
+
* older CLI, in a project that declares no packs of its own, so the embedded
|
|
27
|
+
* snapshot answers instead. `store` is `"embedded"` in that case (it reports
|
|
28
|
+
* which pack ANSWERS, and the snapshot does), and this is how status says the
|
|
29
|
+
* pack is still there and why it is not being read. Null whenever there is
|
|
30
|
+
* nothing passed over, which is every other state.
|
|
31
|
+
*/
|
|
32
|
+
readonly ignoredPack: IgnoredPack | null;
|
|
22
33
|
/** The configured pack declarations, as written in the config. */
|
|
23
34
|
readonly sources: readonly {
|
|
24
35
|
readonly name: string;
|
|
@@ -26,6 +37,21 @@ export interface SourcesStatusData {
|
|
|
26
37
|
}[];
|
|
27
38
|
}
|
|
28
39
|
|
|
40
|
+
/** The `sources reset` result payload. */
|
|
41
|
+
export interface SourcesResetData {
|
|
42
|
+
/** Whether a pointer was there to remove (false = nothing was built). */
|
|
43
|
+
readonly removed: boolean;
|
|
44
|
+
/** The content hash the removed pointer named, or null when there was none. */
|
|
45
|
+
readonly contentHash: string | null;
|
|
46
|
+
/**
|
|
47
|
+
* What answers this project's reads now that the pointer is gone — the
|
|
48
|
+
* embedded snapshot, or nothing at all because the project declares its own
|
|
49
|
+
* packs and must build them. Resolved from config in the run body, so the
|
|
50
|
+
* result can say which of the two it left behind instead of guessing.
|
|
51
|
+
*/
|
|
52
|
+
readonly answers: "embedded" | "unavailable";
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
/** One resolved source in the `sources update` result. */
|
|
30
56
|
export interface UpdatedSource {
|
|
31
57
|
readonly name: string;
|
|
@@ -26,6 +26,9 @@ export const updateVerb: VerbSpec<
|
|
|
26
26
|
path: ["sources", "update"],
|
|
27
27
|
summary: "Resolve configured packs and build the local store from them.",
|
|
28
28
|
doc: "Resolves each configured pack (git, file, or npm) and builds one local pack from them, which every later run reads without touching the network. Put a commit SHA in a pack source ref to pin it to that revision.",
|
|
29
|
+
useWhen:
|
|
30
|
+
"when asked to fetch or rebuild the design-system data, or when a read reports that the store is unavailable",
|
|
31
|
+
example: { skipInvalid: true },
|
|
29
32
|
params: [
|
|
30
33
|
{
|
|
31
34
|
kind: "boolean",
|
|
@@ -378,14 +378,14 @@ describe("surface COMPLETE — emitted == covenant (PROTECTED)", () => {
|
|
|
378
378
|
// The CLOSING direction: assertConforms already proves emitted ⊆ covenant;
|
|
379
379
|
// this proves covenant ⊆ emitted, so together the tool sets are EQUAL — the
|
|
380
380
|
// surface-complete milestone. After PR7, every covenant tool is realized.
|
|
381
|
-
it("emits every covenant tool (all
|
|
381
|
+
it("emits every covenant tool (all 50) — set equality with the covenant", () => {
|
|
382
382
|
const emittedTools = new Set(emitted.mcpSurface.tools);
|
|
383
383
|
const missing = golden.mcpSurface.tools.filter((t) => !emittedTools.has(t));
|
|
384
384
|
expect(missing).toEqual([]);
|
|
385
385
|
expect([...emitted.mcpSurface.tools].sort()).toEqual(
|
|
386
386
|
[...golden.mcpSurface.tools].sort(),
|
|
387
387
|
);
|
|
388
|
-
expect(emitted.mcpSurface.tools).toHaveLength(
|
|
388
|
+
expect(emitted.mcpSurface.tools).toHaveLength(50);
|
|
389
389
|
});
|
|
390
390
|
|
|
391
391
|
// The covenant edit: the non-tool MCP surface is frozen too.
|
|
@@ -34,7 +34,7 @@ import { DEFAULT_PREFIX_MAP } from "../kernel/render/prefixes.js";
|
|
|
34
34
|
import type { PragmaRuntime } from "../kernel/runtime/types.js";
|
|
35
35
|
import type { VerbSpec } from "../kernel/spec/types.js";
|
|
36
36
|
import { buildFixtureRuntime } from "../testing/helpers/packRuntime.js";
|
|
37
|
-
import { declaredStories } from "./distribution.js";
|
|
37
|
+
import { declaredLookups, declaredStories } from "./distribution.js";
|
|
38
38
|
|
|
39
39
|
const tokenPack = declaredStories.get("token");
|
|
40
40
|
if (!tokenPack) {
|
|
@@ -269,6 +269,7 @@ const verbs = () =>
|
|
|
269
269
|
tokenPack,
|
|
270
270
|
distributionSource("pragma.conf.ts"),
|
|
271
271
|
DEFAULT_PREFIX_MAP,
|
|
272
|
+
declaredLookups,
|
|
272
273
|
);
|
|
273
274
|
const verb = (label: string) =>
|
|
274
275
|
verbs().find((v) => verbKey(v.path) === `token ${label}`) as VerbSpec;
|
|
@@ -624,7 +625,7 @@ describe("token consumers — the binding tuple, either spelling (PROTECTED)", (
|
|
|
624
625
|
answered as never,
|
|
625
626
|
);
|
|
626
627
|
expect(notice).toContain("matches `--variable button-color-background`.");
|
|
627
|
-
expect(notice).toContain("
|
|
628
|
+
expect(notice).toContain("stands for no token");
|
|
628
629
|
});
|
|
629
630
|
|
|
630
631
|
it("--key and --state narrow to one tuple each", async () => {
|
|
@@ -44,7 +44,7 @@ import { bootRuntime } from "../kernel/runtime/boot.js";
|
|
|
44
44
|
import type { PragmaRuntime } from "../kernel/runtime/types.js";
|
|
45
45
|
import type { CapabilityModule, VerbSpec } from "../kernel/spec/types.js";
|
|
46
46
|
import { TEST_FLAGS } from "../testing/helpers/projectCli.js";
|
|
47
|
-
import { declaredStories } from "./distribution.js";
|
|
47
|
+
import { declaredLookups, declaredStories } from "./distribution.js";
|
|
48
48
|
|
|
49
49
|
const SOURCE = distributionSource("pragma.conf.ts");
|
|
50
50
|
|
|
@@ -55,7 +55,7 @@ const modules = new Map<string, CapabilityModule>(
|
|
|
55
55
|
NOUNS.map((noun) => {
|
|
56
56
|
const story = declaredStories.get(noun);
|
|
57
57
|
if (!story) throw new Error(`pragma.conf.ts declares no "${noun}" story`);
|
|
58
|
-
return [noun, compileStoryModule(story, SOURCE, {})];
|
|
58
|
+
return [noun, compileStoryModule(story, SOURCE, {}, declaredLookups)];
|
|
59
59
|
}),
|
|
60
60
|
);
|
|
61
61
|
|
|
@@ -536,6 +536,18 @@ describe("the shipped nouns answer, end to end (PROTECTED)", () => {
|
|
|
536
536
|
}
|
|
537
537
|
});
|
|
538
538
|
|
|
539
|
+
it("several mistyped symbols each get a share of the suggestions", async () => {
|
|
540
|
+
const refusal = await rows("token", "values", {
|
|
541
|
+
symbol: ["color.tex", "color.bordr"],
|
|
542
|
+
}).catch((error: unknown) => error);
|
|
543
|
+
expect(refusal).toMatchObject({
|
|
544
|
+
code: "INVALID_INPUT",
|
|
545
|
+
message: 'No token is named "color.tex", "color.bordr".',
|
|
546
|
+
});
|
|
547
|
+
const { suggestions } = refusal as { suggestions: string[] };
|
|
548
|
+
expect(suggestions.slice(0, 2)).toEqual(["color.text", "color.border"]);
|
|
549
|
+
});
|
|
550
|
+
|
|
539
551
|
it("a dash-stripped variable name resolves, with its declarations", async () => {
|
|
540
552
|
const variable = await lookup("variable", "color-text");
|
|
541
553
|
expect(variable).toMatchObject({
|
|
@@ -24,6 +24,7 @@ const upgradeVerb: VerbSpec<Record<string, unknown>, UpgradeData> = {
|
|
|
24
24
|
path: ["upgrade"],
|
|
25
25
|
summary: `Upgrade the ${BIN_NAME} CLI to the latest version.`,
|
|
26
26
|
doc: "Checks the registry for the active channel's latest release and runs your package manager's global-update command. Preview the update before applying it.",
|
|
27
|
+
useWhen: "when asked to update the CLI itself to the latest release",
|
|
27
28
|
params: [],
|
|
28
29
|
output: { formatters: upgradeFormatters },
|
|
29
30
|
examples: [
|
|
@@ -34,7 +34,7 @@ import { DEFAULT_PREFIX_MAP } from "../kernel/render/prefixes.js";
|
|
|
34
34
|
import type { PragmaRuntime } from "../kernel/runtime/types.js";
|
|
35
35
|
import type { VerbSpec } from "../kernel/spec/types.js";
|
|
36
36
|
import { buildFixtureRuntime } from "../testing/helpers/packRuntime.js";
|
|
37
|
-
import { declaredStories } from "./distribution.js";
|
|
37
|
+
import { declaredLookups, declaredStories } from "./distribution.js";
|
|
38
38
|
|
|
39
39
|
const variablePack = declaredStories.get("variable");
|
|
40
40
|
if (!variablePack) {
|
|
@@ -183,6 +183,7 @@ const verbs = () =>
|
|
|
183
183
|
variablePack,
|
|
184
184
|
distributionSource("pragma.conf.ts"),
|
|
185
185
|
DEFAULT_PREFIX_MAP,
|
|
186
|
+
declaredLookups,
|
|
186
187
|
);
|
|
187
188
|
const verb = (label: string) =>
|
|
188
189
|
verbs().find((v) => verbKey(v.path) === `variable ${label}`) as VerbSpec;
|
|
@@ -15,6 +15,7 @@ const versionVerb: VerbSpec<Record<string, unknown>, string> = {
|
|
|
15
15
|
path: ["version"],
|
|
16
16
|
summary: "Print the CLI version.",
|
|
17
17
|
doc: "Prints the version `--version` prints — one value, two spellings of the same read.",
|
|
18
|
+
useWhen: "when asked only for the installed version number",
|
|
18
19
|
params: [],
|
|
19
20
|
output: {
|
|
20
21
|
formatters: {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire contract of list-valued parameters, derived from the registry.
|
|
3
|
+
*
|
|
4
|
+
* Two recorded failures are the reason this file exists. A weak model asked
|
|
5
|
+
* for twenty tokens' variables with twenty `variable_list` calls, because over
|
|
6
|
+
* MCP a filter took ONE value while the CLI took the flag repeated; a stronger
|
|
7
|
+
* one passed `name: "color.text"` to `token_lookup` and was refused by schema
|
|
8
|
+
* validation over a pair of brackets. So: every parameter that takes several
|
|
9
|
+
* values ADVERTISES an array, and accepts one bare value as a list of one.
|
|
10
|
+
*
|
|
11
|
+
* Derived rather than listed — a story declared tomorrow is held to the rule
|
|
12
|
+
* by the act of declaring it.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { buildToolShape } from "../kernel/project/mcp/registerVerb.js";
|
|
18
|
+
import { toolName } from "../kernel/spec/index.js";
|
|
19
|
+
import type { ParamSpec, VerbSpec } from "../kernel/spec/types.js";
|
|
20
|
+
import { type McpHarness, projectMcp } from "../testing/helpers/projectMcp.js";
|
|
21
|
+
import { capabilities } from "./index.js";
|
|
22
|
+
|
|
23
|
+
const exposed: readonly VerbSpec[] = capabilities
|
|
24
|
+
.flatMap((module) => module.verbs)
|
|
25
|
+
.filter((verb) => !verb.hidden && verb.capability.mcp.expose !== false);
|
|
26
|
+
|
|
27
|
+
/** Every `[tool, param]` pair whose param satisfies `test`. */
|
|
28
|
+
function paramsWhere(
|
|
29
|
+
test: (param: ParamSpec) => boolean,
|
|
30
|
+
): (readonly [string, string, VerbSpec, ParamSpec])[] {
|
|
31
|
+
return exposed.flatMap((verb) =>
|
|
32
|
+
verb.params
|
|
33
|
+
.filter(test)
|
|
34
|
+
.map((param) => [toolName(verb.path), param.name, verb, param] as const),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** One admissible value for a param: its first enum value, else any word. */
|
|
39
|
+
const sample = (param: ParamSpec): string =>
|
|
40
|
+
param.kind === "enum" ? (param.values.at(0) ?? "") : "anything";
|
|
41
|
+
|
|
42
|
+
/** Parse ONE field of a tool's input shape, leaving its other params aside. */
|
|
43
|
+
function parseField(verb: VerbSpec, param: ParamSpec, value: unknown) {
|
|
44
|
+
const field = buildToolShape(verb)[param.name];
|
|
45
|
+
if (!field) throw new Error(`no "${param.name}" in the tool shape`);
|
|
46
|
+
return z.object({ [param.name]: field }).safeParse({ [param.name]: value });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const variadic = paramsWhere((param) => param.kind === "string[]");
|
|
50
|
+
const repeatable = paramsWhere(
|
|
51
|
+
(param) => param.kind !== "string[]" && param.repeatable === true,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
describe("a parameter that takes several values takes one, too", () => {
|
|
55
|
+
it("the registry has both kinds (the derivation is not vacuous)", () => {
|
|
56
|
+
expect(variadic.length).toBeGreaterThan(0);
|
|
57
|
+
expect(repeatable.length).toBeGreaterThan(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it.each(variadic)(
|
|
61
|
+
"%s { %s }: a bare string is a list of one",
|
|
62
|
+
(_tool, name, verb, param) => {
|
|
63
|
+
expect(parseField(verb, param, "color.text")).toEqual({
|
|
64
|
+
success: true,
|
|
65
|
+
data: { [name]: ["color.text"] },
|
|
66
|
+
});
|
|
67
|
+
expect(parseField(verb, param, ["a", "b"]).success).toBe(true);
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
it.each(repeatable)(
|
|
72
|
+
"%s { %s }: an array and a bare string are both accepted",
|
|
73
|
+
(_tool, name, verb, param) => {
|
|
74
|
+
const value = sample(param);
|
|
75
|
+
expect(parseField(verb, param, [value, value]).success).toBe(true);
|
|
76
|
+
expect(parseField(verb, param, value)).toEqual({
|
|
77
|
+
success: true,
|
|
78
|
+
data: { [name]: [value] },
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("what is advertised is a plain array", () => {
|
|
85
|
+
let harness: McpHarness;
|
|
86
|
+
let schemas: Map<string, Record<string, Record<string, unknown>>>;
|
|
87
|
+
beforeAll(async () => {
|
|
88
|
+
harness = await projectMcp(capabilities);
|
|
89
|
+
schemas = new Map(
|
|
90
|
+
(await harness.listTools()).map((tool) => [
|
|
91
|
+
tool.name,
|
|
92
|
+
((tool.inputSchema as { properties?: unknown }).properties ??
|
|
93
|
+
{}) as Record<string, Record<string, unknown>>,
|
|
94
|
+
]),
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
afterAll(() => harness.cleanup());
|
|
98
|
+
|
|
99
|
+
// `anyOf` is what a `string | string[]` union would emit, and it is the
|
|
100
|
+
// shape the weaker models read badly — so the coercion must not leak into
|
|
101
|
+
// the advertised schema.
|
|
102
|
+
it.each([...variadic, ...repeatable])(
|
|
103
|
+
"%s { %s } is `type: array`, never `anyOf`",
|
|
104
|
+
(tool, name) => {
|
|
105
|
+
const advertised = schemas.get(tool)?.[name];
|
|
106
|
+
expect(advertised?.type).toBe("array");
|
|
107
|
+
expect(advertised).not.toHaveProperty("anyOf");
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
});
|
package/src/constants.ts
CHANGED
|
@@ -61,9 +61,10 @@ const PROJECT_CONFIG_FILENAME = `${identity.name}.config.ts`;
|
|
|
61
61
|
/**
|
|
62
62
|
* The distribution's name plus a space: the prefix a `recovery.cli` hint carries
|
|
63
63
|
* so it quotes a command the installed binary answers to (D5). Kernel hints are
|
|
64
|
-
* authored through `
|
|
65
|
-
*
|
|
66
|
-
*
|
|
64
|
+
* authored through `callRecovery`, whose call renderer prepends it; a pack's
|
|
65
|
+
* `emptyRecovery.call` names a verb path and never a binary (`packs/schema.ts`
|
|
66
|
+
* rejects a command line there), because the CONSUMING distribution's renderer
|
|
67
|
+
* supplies its own. Use {@link BIN_NAME} for prose —
|
|
67
68
|
* this constant's trailing space belongs to the recovery invariant, not to
|
|
68
69
|
* sentences.
|
|
69
70
|
*/
|