@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
package/src/kernel/copy.test.ts
CHANGED
|
@@ -438,7 +438,7 @@ describe("capability commands (PROTECTED)", () => {
|
|
|
438
438
|
it("no command quoted in capability prose is a bare literal", () => {
|
|
439
439
|
// The other half of the same promise, and the half the position rule cannot
|
|
440
440
|
// reach: an empty-state hint, a recovery MESSAGE (the second argument to
|
|
441
|
-
// `
|
|
441
|
+
// `callRecovery`, beside a first argument the position rule already covers),
|
|
442
442
|
// a `sources status` headline, a wizard step's title. They reach the same
|
|
443
443
|
// user with the same instruction, and seven of them named this distribution
|
|
444
444
|
// while the rule above reported zero exemptions.
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { ISSUES_URL } from "../../constants.js";
|
|
3
3
|
import { errorEnvelope, successEnvelope } from "../render/envelope.js";
|
|
4
4
|
import { PragmaError } from "./PragmaError.js";
|
|
5
|
-
import {
|
|
5
|
+
import { callRecovery } from "./recovery.js";
|
|
6
6
|
import { serializeError } from "./serialize.js";
|
|
7
7
|
|
|
8
8
|
describe("PragmaError factories", () => {
|
|
@@ -76,13 +76,17 @@ describe("serializeError", () => {
|
|
|
76
76
|
it("includes suggestions, recovery, validOptions, and filters when present", () => {
|
|
77
77
|
const error = PragmaError.notFound("block", "Buton", {
|
|
78
78
|
suggestions: ["Button"],
|
|
79
|
-
recovery:
|
|
79
|
+
recovery: callRecovery({ verb: "block list" }, "List blocks."),
|
|
80
80
|
});
|
|
81
81
|
expect(serializeError(error)).toEqual({
|
|
82
82
|
code: "ENTITY_NOT_FOUND",
|
|
83
83
|
message: 'block "Buton" not found.',
|
|
84
84
|
suggestions: ["Button"],
|
|
85
|
-
recovery: {
|
|
85
|
+
recovery: {
|
|
86
|
+
cli: "pragma block list",
|
|
87
|
+
message: "List blocks.",
|
|
88
|
+
mcp: { tool: "block_list", params: {} },
|
|
89
|
+
},
|
|
86
90
|
});
|
|
87
91
|
});
|
|
88
92
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { RECOVERY_CLI_PREFIX } from "../../constants.js";
|
|
3
|
-
import {
|
|
3
|
+
import { callRecovery } from "./recovery.js";
|
|
4
4
|
|
|
5
5
|
describe("recovery.cli invariant (D5)", () => {
|
|
6
6
|
it("carries the shipped distribution's recovery prefix", () => {
|
|
@@ -11,27 +11,61 @@ describe("recovery.cli invariant (D5)", () => {
|
|
|
11
11
|
expect(RECOVERY_CLI_PREFIX).toBe("pragma ");
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
it("
|
|
15
|
-
// The caller
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// the raw `cli:` literals that never
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
it("derives both spellings from the one call it is given", () => {
|
|
15
|
+
// The caller names a CALL, so a hint naming the wrong binary — or a CLI
|
|
16
|
+
// string and an MCP hint that disagree about an argument — is unwritable
|
|
17
|
+
// rather than merely asserted against. `kernel/copy.test.ts`'s position
|
|
18
|
+
// rule still sees the raw `cli:` literals that never reach this function.
|
|
19
|
+
expect(
|
|
20
|
+
callRecovery(
|
|
21
|
+
{ verb: "config unset", params: { key: "tier" } },
|
|
22
|
+
"Clear the field.",
|
|
23
|
+
),
|
|
24
|
+
).toEqual({
|
|
25
|
+
cli: "pragma config unset tier",
|
|
26
|
+
message: "Clear the field.",
|
|
27
|
+
// A mutating tool is plan-first: the recovery never carries `confirm`.
|
|
28
|
+
mcp: { tool: "config_unset", params: { key: "tier" } },
|
|
23
29
|
});
|
|
24
30
|
});
|
|
25
31
|
|
|
26
|
-
it("
|
|
32
|
+
it("spells a flag, quotes what a shell would split, and repeats a variadic positional", () => {
|
|
27
33
|
expect(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
callRecovery(
|
|
35
|
+
{ verb: "token consumers", params: { symbol: "color.text" } },
|
|
36
|
+
"m",
|
|
37
|
+
).cli,
|
|
38
|
+
).toBe("pragma token consumers --symbol color.text");
|
|
39
|
+
expect(
|
|
40
|
+
callRecovery(
|
|
41
|
+
{ verb: "graph query", params: { sparql: "ASK { ?s ?p 'x' }" } },
|
|
42
|
+
"m",
|
|
43
|
+
).cli,
|
|
44
|
+
).toBe("pragma graph query 'ASK { ?s ?p '\\''x'\\'' }'");
|
|
45
|
+
expect(
|
|
46
|
+
callRecovery(
|
|
47
|
+
{ verb: "block lookup", params: { name: ["Button", "Card"] } },
|
|
48
|
+
"m",
|
|
49
|
+
).cli,
|
|
50
|
+
).toBe("pragma block lookup Button Card");
|
|
51
|
+
expect(
|
|
52
|
+
callRecovery(
|
|
53
|
+
{ verb: "sources update", params: { skipInvalid: true } },
|
|
54
|
+
"m",
|
|
55
|
+
).cli,
|
|
56
|
+
).toBe("pragma sources update --skip-invalid");
|
|
57
|
+
expect(
|
|
58
|
+
callRecovery(
|
|
59
|
+
{ verb: "sources update", params: { skipInvalid: false } },
|
|
60
|
+
"m",
|
|
61
|
+
).cli,
|
|
62
|
+
).toBe("pragma sources update");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("names no MCP tool for a verb withheld from MCP", () => {
|
|
66
|
+
expect(callRecovery({ verb: "version" }, "See the version.")).toEqual({
|
|
67
|
+
cli: "pragma version",
|
|
68
|
+
message: "See the version.",
|
|
35
69
|
});
|
|
36
70
|
});
|
|
37
71
|
});
|
|
@@ -1,58 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The recovery
|
|
2
|
+
* The recovery invariant (D5): a `recovery.cli` hint quotes a command the
|
|
3
|
+
* installed binary answers to, so it begins with {@link RECOVERY_CLI_PREFIX}.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* A THIRD authoring route is also legal and reached by NEITHER guard: a `cli`
|
|
14
|
-
* DERIVED from the program's own name at the call site. One site does this —
|
|
15
|
-
* the create mount (`capabilities/create/mount.ts`,
|
|
16
|
-
* `[...detail.chain, suggestion].join(" ")`), whose `chain[0]` is the root
|
|
17
|
-
* Commander program's name, `BIN_NAME` by wiring. It bypasses
|
|
18
|
-
* {@link cliRecovery}, and the position rule is structurally blind to it (it
|
|
19
|
-
* flags QUOTED literals containing the distribution's name, never a computed
|
|
20
|
-
* expression) — so D5 holds there by derivation, and the derivation is pinned
|
|
21
|
-
* where the value surfaces: `createGrammar.test.ts` COMPOSES its expected
|
|
22
|
-
* `cli` from {@link RECOVERY_CLI_PREFIX}, so a renamed root program reddens
|
|
23
|
-
* the cell naming the constant. (A separate `startsWith` assertion cannot
|
|
24
|
-
* pin it — behind an exact pin of the same value it has no reachable
|
|
25
|
-
* failing input.)
|
|
26
|
-
*
|
|
27
|
-
* `kernel/packs/schema.ts` holds a user-authored pack's `emptyRecovery.cli` to
|
|
28
|
-
* the same shape from the other side — it must NOT carry a prefix, because the
|
|
29
|
-
* consuming distribution's renderer supplies one.
|
|
5
|
+
* {@link callRecovery} takes a CALL and the one call renderer spells it, prefix
|
|
6
|
+
* included, so a hint naming the wrong binary — or a `cli` and an `mcp` that
|
|
7
|
+
* disagree — is unwritable. Two routes do not pass through here:
|
|
8
|
+
* raw `cli:` object literals (`kernel/copy.test.ts` polices those at the
|
|
9
|
+
* position), and the create mount, which DERIVES its `cli` from the root
|
|
10
|
+
* program's name (`capabilities/create/mount.ts`; pinned by
|
|
11
|
+
* `createGrammar.test.ts`, whose expectation is composed from the prefix).
|
|
12
|
+
* A pack's `emptyRecovery.call` is held to the same shape from the other side
|
|
13
|
+
* by `kernel/packs/schema.ts`: a verb path, never a binary name.
|
|
30
14
|
*/
|
|
31
15
|
|
|
32
|
-
import {
|
|
16
|
+
import { callTool, renderCall } from "../spec/call.js";
|
|
17
|
+
import type { Call } from "../spec/types.js";
|
|
33
18
|
import type { Recovery } from "./types.js";
|
|
34
19
|
|
|
35
20
|
/**
|
|
36
|
-
* Build a {@link Recovery}
|
|
37
|
-
* prefix, optionally pairing it with an MCP tool an agent can call.
|
|
21
|
+
* Build a {@link Recovery} from the call that recovers.
|
|
38
22
|
*
|
|
39
|
-
* A recovery
|
|
40
|
-
*
|
|
41
|
-
*
|
|
23
|
+
* A recovery speaks to BOTH surfaces: `cli` guides a person, while `mcp` names
|
|
24
|
+
* the tool an agent invokes (an agent cannot run a shell command). Both derive
|
|
25
|
+
* from the one call, so they cannot disagree about the verb or its arguments;
|
|
26
|
+
* `mcp` is absent only when the verb is withheld from MCP. Neither spelling
|
|
27
|
+
* ever carries `confirm`: a mutating tool is plan-first, and the plan it
|
|
28
|
+
* returns says how to proceed.
|
|
42
29
|
*
|
|
43
|
-
* @param
|
|
30
|
+
* @param call - The verb to run and the params to run it with.
|
|
44
31
|
* @param message - Human-readable guidance shown alongside the command.
|
|
45
|
-
* @
|
|
46
|
-
* @returns A recovery hint whose `cli` names this distribution's binary.
|
|
32
|
+
* @returns A recovery hint carrying the call in both spellings.
|
|
47
33
|
*/
|
|
48
|
-
export function
|
|
49
|
-
|
|
50
|
-
message:
|
|
51
|
-
mcp?: Recovery["mcp"],
|
|
52
|
-
): Recovery {
|
|
53
|
-
return {
|
|
54
|
-
message,
|
|
55
|
-
cli: `${RECOVERY_CLI_PREFIX}${command}`,
|
|
56
|
-
...(mcp ? { mcp } : {}),
|
|
57
|
-
};
|
|
34
|
+
export function callRecovery(call: Call, message: string): Recovery {
|
|
35
|
+
const mcp = callTool(call);
|
|
36
|
+
return { message, cli: renderCall(call, "cli"), ...(mcp ? { mcp } : {}) };
|
|
58
37
|
}
|
|
@@ -16,7 +16,7 @@ type ErrorCode = (typeof ERROR_CODES)[number];
|
|
|
16
16
|
*
|
|
17
17
|
* `cli` — when present — is the command as a user would type it: this
|
|
18
18
|
* distribution's own bin name (`RECOVERY_CLI_PREFIX`) plus a grammar path (D5).
|
|
19
|
-
* `
|
|
19
|
+
* `callRecovery` takes the path alone and prepends the prefix, so there is no
|
|
20
20
|
* assertion to make; the one COMPUTED producer (the create mount) derives
|
|
21
21
|
* the prefix from the program's own name, pinned by a test expectation
|
|
22
22
|
* COMPOSED from `RECOVERY_CLI_PREFIX` — the routes are enumerated in the
|
|
@@ -19,6 +19,7 @@ import type { CapabilityModule, VerbSpec } from "../spec/types.js";
|
|
|
19
19
|
import {
|
|
20
20
|
assembleEffectiveModules,
|
|
21
21
|
loadEffectiveModules,
|
|
22
|
+
migrateLegacyRecoveries,
|
|
22
23
|
validateStories,
|
|
23
24
|
} from "./collect.js";
|
|
24
25
|
import { assertUniqueVerbs } from "./uniqueness.js";
|
|
@@ -210,6 +211,84 @@ describe("validateStories — package stories NEVER throw (PROTECTED)", () => {
|
|
|
210
211
|
]);
|
|
211
212
|
});
|
|
212
213
|
|
|
214
|
+
it("carries a package story's retired emptyRecovery.cli forward instead of losing the noun", () => {
|
|
215
|
+
// A pack built before the grammar changed still has `cli` on disk. The
|
|
216
|
+
// project config gets a hard error for it (its author can rename the key);
|
|
217
|
+
// a package is third-party data, so the hint is migrated when it IS a verb
|
|
218
|
+
// path and otherwise only the hint is dropped — never the noun.
|
|
219
|
+
const withCli = (noun: string, cli: string) => ({
|
|
220
|
+
noun,
|
|
221
|
+
list: {
|
|
222
|
+
...validPack(noun).list,
|
|
223
|
+
emptyRecovery: { message: "None yet.", cli },
|
|
224
|
+
},
|
|
225
|
+
verbs: [
|
|
226
|
+
{
|
|
227
|
+
...validPack(noun).list,
|
|
228
|
+
verb: "extras",
|
|
229
|
+
emptyRecovery: { message: "None yet.", cli },
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
});
|
|
233
|
+
const result = validateStories(
|
|
234
|
+
[
|
|
235
|
+
record(
|
|
236
|
+
"pkg/stories/recipe.json",
|
|
237
|
+
JSON.stringify(withCli("recipe", "sources update")),
|
|
238
|
+
),
|
|
239
|
+
record(
|
|
240
|
+
"pkg/stories/menu.json",
|
|
241
|
+
JSON.stringify(withCli("menu", "acme sources update --all")),
|
|
242
|
+
),
|
|
243
|
+
],
|
|
244
|
+
STATIC,
|
|
245
|
+
);
|
|
246
|
+
const [recipe, menu] = result.entries.map((entry) => entry.definition);
|
|
247
|
+
expect(recipe?.list?.emptyRecovery).toEqual({
|
|
248
|
+
message: "None yet.",
|
|
249
|
+
call: { verb: "sources update" },
|
|
250
|
+
});
|
|
251
|
+
expect(recipe?.verbs?.at(0)?.emptyRecovery?.call).toEqual({
|
|
252
|
+
verb: "sources update",
|
|
253
|
+
});
|
|
254
|
+
// Not a verb path: the story survives, the hint does not, and doctor hears.
|
|
255
|
+
expect(menu?.noun).toBe("menu");
|
|
256
|
+
expect(menu?.list?.emptyRecovery).toEqual({ message: "None yet." });
|
|
257
|
+
expect(result.problems.map((problem) => problem.source)).toEqual([
|
|
258
|
+
"pkg/stories/menu.json",
|
|
259
|
+
"pkg/stories/menu.json",
|
|
260
|
+
]);
|
|
261
|
+
expect(result.problems.at(0)?.message).toMatch(
|
|
262
|
+
/story is kept.*emptyRecovery\.cli.*emptyRecovery\.call/,
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("leaves a story with nothing to migrate, and a non-object, untouched", () => {
|
|
267
|
+
const story = validPack("recipe");
|
|
268
|
+
expect(migrateLegacyRecoveries(story)).toEqual({ raw: story, dropped: [] });
|
|
269
|
+
expect(migrateLegacyRecoveries("nope")).toEqual({
|
|
270
|
+
raw: "nope",
|
|
271
|
+
dropped: [],
|
|
272
|
+
});
|
|
273
|
+
// A half that already names a call keeps it; the stale `cli` beside it goes.
|
|
274
|
+
const both = {
|
|
275
|
+
message: "m",
|
|
276
|
+
cli: "doctor",
|
|
277
|
+
call: { verb: "sources update" },
|
|
278
|
+
};
|
|
279
|
+
expect(
|
|
280
|
+
migrateLegacyRecoveries({ noun: "x", verbs: [{ emptyRecovery: both }] }),
|
|
281
|
+
).toEqual({
|
|
282
|
+
raw: {
|
|
283
|
+
noun: "x",
|
|
284
|
+
verbs: [
|
|
285
|
+
{ emptyRecovery: { message: "m", call: { verb: "sources update" } } },
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
dropped: [],
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
213
292
|
it("last declaration wins for a noun, and the shadowed file is reported", () => {
|
|
214
293
|
const result = validateStories(
|
|
215
294
|
[
|
|
@@ -28,9 +28,11 @@ import type { ConfigLayers } from "../config/index.js";
|
|
|
28
28
|
import { PragmaError } from "../error/index.js";
|
|
29
29
|
import type { PackStoryRecord } from "../runtime/graphpack/stories.js";
|
|
30
30
|
import type { CapabilityModule } from "../spec/index.js";
|
|
31
|
-
import { compileStoryModule } from "./compile.js";
|
|
31
|
+
import { compileStoryModule, storyOf } from "./compile.js";
|
|
32
32
|
import { parsePackDefinition } from "./schema.js";
|
|
33
|
-
import
|
|
33
|
+
import { nounsNamed } from "./storyRules.js";
|
|
34
|
+
import type { NounLookups, PackDefinition, PackEntry } from "./types.js";
|
|
35
|
+
import { VERB_PATH_PATTERN } from "./types.js";
|
|
34
36
|
import { assertUniqueVerbs } from "./uniqueness.js";
|
|
35
37
|
|
|
36
38
|
/** One package-declared story that could not be used, and why. */
|
|
@@ -83,10 +85,14 @@ export function validateStories(
|
|
|
83
85
|
const problems: StoryProblem[] = [];
|
|
84
86
|
for (const record of records) {
|
|
85
87
|
try {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
const migrated = migrateLegacyRecoveries(JSON.parse(record.content));
|
|
89
|
+
for (const hint of migrated.dropped) {
|
|
90
|
+
problems.push({
|
|
91
|
+
source: record.source,
|
|
92
|
+
message: `its story is kept, but its emptyRecovery.cli hint "${hint}" is not a verb path and was dropped; the key is now emptyRecovery.call.`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const definition = parsePackDefinition(migrated.raw, record.source);
|
|
90
96
|
if (reserved.has(definition.noun)) {
|
|
91
97
|
problems.push({
|
|
92
98
|
source: record.source,
|
|
@@ -112,6 +118,55 @@ export function validateStories(
|
|
|
112
118
|
return { entries: [...byNoun.values()], problems };
|
|
113
119
|
}
|
|
114
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Carry a PACKAGE story written against the old grammar forward.
|
|
123
|
+
*
|
|
124
|
+
* `emptyRecovery.cli: "sources update"` became `emptyRecovery.call: { verb }`. A
|
|
125
|
+
* project's own config gets the hard error that names the change — its author
|
|
126
|
+
* can fix it. A package is third-party data, often already built into a pack on
|
|
127
|
+
* disk, and refusing the key would cost the user the whole noun for the sake of
|
|
128
|
+
* a hint. So the old string becomes a call when it IS a verb path, and
|
|
129
|
+
* otherwise only the hint is dropped, reported once under `doctor`.
|
|
130
|
+
*
|
|
131
|
+
* @param raw - The parsed story JSON (untrusted; returned unchanged when it
|
|
132
|
+
* carries no legacy key).
|
|
133
|
+
* @returns The story to validate, and the legacy hints that could not be kept.
|
|
134
|
+
*/
|
|
135
|
+
export function migrateLegacyRecoveries(raw: unknown): {
|
|
136
|
+
raw: unknown;
|
|
137
|
+
dropped: string[];
|
|
138
|
+
} {
|
|
139
|
+
const dropped: string[] = [];
|
|
140
|
+
const migrateHalf = (half: unknown): unknown => {
|
|
141
|
+
const recovery = (
|
|
142
|
+
half as { emptyRecovery?: Record<string, unknown> } | null
|
|
143
|
+
)?.emptyRecovery;
|
|
144
|
+
if (typeof recovery?.cli !== "string") return half;
|
|
145
|
+
const { cli, ...rest } = recovery;
|
|
146
|
+
const keep = VERB_PATH_PATTERN.test(cli);
|
|
147
|
+
if (!keep) dropped.push(cli);
|
|
148
|
+
return {
|
|
149
|
+
...(half as object),
|
|
150
|
+
emptyRecovery:
|
|
151
|
+
keep && rest.call === undefined
|
|
152
|
+
? { ...rest, call: { verb: cli } }
|
|
153
|
+
: rest,
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
if (typeof raw !== "object" || raw === null) return { raw, dropped };
|
|
157
|
+
const story = raw as { list?: unknown; verbs?: unknown };
|
|
158
|
+
return {
|
|
159
|
+
raw: {
|
|
160
|
+
...story,
|
|
161
|
+
...(story.list === undefined ? {} : { list: migrateHalf(story.list) }),
|
|
162
|
+
...(Array.isArray(story.verbs)
|
|
163
|
+
? { verbs: story.verbs.map(migrateHalf) }
|
|
164
|
+
: {}),
|
|
165
|
+
},
|
|
166
|
+
dropped,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
115
170
|
/**
|
|
116
171
|
* The PROJECT's config-declared stories, weakest tier first.
|
|
117
172
|
*
|
|
@@ -147,8 +202,8 @@ function projectStoryTiers(layers: ConfigLayers): readonly unknown[][] {
|
|
|
147
202
|
* carries (see {@link validateStories}); weaker than either config tier.
|
|
148
203
|
* @returns The effective modules, uniqueness-checked.
|
|
149
204
|
* @throws PragmaError CONFIG_ERROR on an invalid CONFIG story, a config story
|
|
150
|
-
* claiming an authored non-story noun,
|
|
151
|
-
* tier. Package stories were already screened and never throw here.
|
|
205
|
+
* claiming an authored non-story noun, a duplicate noun within one config
|
|
206
|
+
* tier, or a config or shipped story naming a noun no story declares. Package stories were already screened and never throw here.
|
|
152
207
|
*/
|
|
153
208
|
export function assembleEffectiveModules(
|
|
154
209
|
staticModules: readonly CapabilityModule[],
|
|
@@ -171,6 +226,19 @@ export function assembleEffectiveModules(
|
|
|
171
226
|
// Keyed by noun so the stronger tier REPLACES the weaker one — declaring a
|
|
172
227
|
// story both on its pack and at the top level is a refinement, not an error.
|
|
173
228
|
const dynamic = new Map<string, CapabilityModule>();
|
|
229
|
+
// The EFFECTIVE lookup of a noun, for shipped and project stories alike.
|
|
230
|
+
// Read at run time, so it sees every tier whatever order they compile in.
|
|
231
|
+
const nouns: NounLookups = (noun) => {
|
|
232
|
+
const module =
|
|
233
|
+
dynamic.get(noun) ?? staticModules.find((m) => m.name === noun);
|
|
234
|
+
const story = module && storyOf(module);
|
|
235
|
+
if (story && !story.definition.lookup) {
|
|
236
|
+
throw PragmaError.configError(
|
|
237
|
+
`The "${noun}" story in ${story.source.label} declares no lookup, so a filter that takes a ${noun} cannot resolve its value. Add a lookup to that story.`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return story?.definition.lookup;
|
|
241
|
+
};
|
|
174
242
|
for (const entry of packageStories) {
|
|
175
243
|
dynamic.set(
|
|
176
244
|
entry.definition.noun,
|
|
@@ -178,6 +246,7 @@ export function assembleEffectiveModules(
|
|
|
178
246
|
entry.definition,
|
|
179
247
|
{ label: entry.source, origin: "package" },
|
|
180
248
|
prefixes,
|
|
249
|
+
nouns,
|
|
181
250
|
),
|
|
182
251
|
);
|
|
183
252
|
}
|
|
@@ -205,6 +274,7 @@ export function assembleEffectiveModules(
|
|
|
205
274
|
definition,
|
|
206
275
|
{ label: "config", origin: "config" },
|
|
207
276
|
prefixes,
|
|
277
|
+
nouns,
|
|
208
278
|
),
|
|
209
279
|
);
|
|
210
280
|
}
|
|
@@ -212,12 +282,79 @@ export function assembleEffectiveModules(
|
|
|
212
282
|
|
|
213
283
|
// Drop the static module for any noun a config story overrides, then append
|
|
214
284
|
// the dynamic modules.
|
|
215
|
-
const kept = staticModules
|
|
285
|
+
const kept = staticModules
|
|
286
|
+
.filter((module) => !dynamic.has(module.name))
|
|
287
|
+
// Recompiled so a shipped filter naming an overridden noun resolves
|
|
288
|
+
// through the project's lookup, as the project's own stories do.
|
|
289
|
+
.map((module) => {
|
|
290
|
+
const story = storyOf(module);
|
|
291
|
+
return story
|
|
292
|
+
? compileStoryModule(
|
|
293
|
+
story.definition,
|
|
294
|
+
story.source,
|
|
295
|
+
story.prefixes,
|
|
296
|
+
nouns,
|
|
297
|
+
)
|
|
298
|
+
: module;
|
|
299
|
+
});
|
|
216
300
|
const effective = [...kept, ...dynamic.values()];
|
|
301
|
+
// A noun some story declares without a lookup is refused when a filter on it
|
|
302
|
+
// is used, not here: only a noun NO story declares is a declaration error.
|
|
303
|
+
// Package stories never throw; {@link screenPackageStories} drops them.
|
|
304
|
+
for (const module of effective) {
|
|
305
|
+
const story = storyOf(module);
|
|
306
|
+
const unknown =
|
|
307
|
+
story && story.source.origin !== "package"
|
|
308
|
+
? undeclaredNoun(story.definition, effective)
|
|
309
|
+
: undefined;
|
|
310
|
+
if (story && unknown) {
|
|
311
|
+
throw PragmaError.configError(
|
|
312
|
+
`Invalid story in ${story.source.label}: "${module.name}" names the noun "${unknown}", and no story declares it.`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
217
316
|
assertUniqueVerbs(effective.flatMap((module) => [...module.verbs]));
|
|
218
317
|
return effective;
|
|
219
318
|
}
|
|
220
319
|
|
|
320
|
+
/** The first noun a story names that no effective story declares. */
|
|
321
|
+
function undeclaredNoun(
|
|
322
|
+
definition: PackDefinition,
|
|
323
|
+
effective: readonly CapabilityModule[],
|
|
324
|
+
): string | undefined {
|
|
325
|
+
return nounsNamed(definition).find(
|
|
326
|
+
(noun) => !effective.some((m) => m.name === noun && storyOf(m)),
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Drop each PACKAGE story naming a noun no effective story declares, reporting
|
|
332
|
+
* it — judged over the assembled set, so the order of declaration is nothing.
|
|
333
|
+
*
|
|
334
|
+
* @param effective - The assembled modules.
|
|
335
|
+
* @returns The modules kept, and one problem per story dropped.
|
|
336
|
+
*/
|
|
337
|
+
export function screenPackageStories(effective: readonly CapabilityModule[]): {
|
|
338
|
+
readonly modules: readonly CapabilityModule[];
|
|
339
|
+
readonly problems: readonly StoryProblem[];
|
|
340
|
+
} {
|
|
341
|
+
const problems: StoryProblem[] = [];
|
|
342
|
+
const modules = effective.filter((module) => {
|
|
343
|
+
const story = storyOf(module);
|
|
344
|
+
const unknown =
|
|
345
|
+
story?.source.origin === "package"
|
|
346
|
+
? undeclaredNoun(story.definition, effective)
|
|
347
|
+
: undefined;
|
|
348
|
+
if (!story || unknown === undefined) return true;
|
|
349
|
+
problems.push({
|
|
350
|
+
source: story.source.label,
|
|
351
|
+
message: `it names the noun "${unknown}", and no story declares it.`,
|
|
352
|
+
});
|
|
353
|
+
return false;
|
|
354
|
+
});
|
|
355
|
+
return { modules: problems.length === 0 ? effective : modules, problems };
|
|
356
|
+
}
|
|
357
|
+
|
|
221
358
|
/**
|
|
222
359
|
* Load the effective modules for a real invocation: read the layered config and
|
|
223
360
|
* the answering pack's carried stories, and merge both into the static
|
|
@@ -252,8 +389,11 @@ export async function loadEffectiveModules(
|
|
|
252
389
|
activeStories(resolveSources(layers, cwd)),
|
|
253
390
|
staticModules,
|
|
254
391
|
);
|
|
392
|
+
const screened = screenPackageStories(
|
|
393
|
+
assembleEffectiveModules(staticModules, layers, entries),
|
|
394
|
+
);
|
|
255
395
|
return {
|
|
256
|
-
modules:
|
|
257
|
-
problems,
|
|
396
|
+
modules: screened.modules,
|
|
397
|
+
problems: [...problems, ...screened.problems],
|
|
258
398
|
};
|
|
259
399
|
}
|