@canonical/pragma-cli 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/pragma +0 -0
- package/package.json +18 -15
- package/pragma.conf.ts +664 -0
- package/src/bin.ts +13 -4
- package/src/capabilities/block.parity.test.ts +250 -0
- package/src/capabilities/capabilities/capabilities.render.ts +3 -2
- package/src/capabilities/capabilities/capabilities.verb.ts +31 -13
- package/src/capabilities/capabilities/catalog.ts +18 -10
- package/src/capabilities/capabilities/hints.ts +13 -19
- package/src/capabilities/colophon/collectColophon.ts +34 -19
- package/src/capabilities/colophon/colophon.verb.ts +8 -4
- package/src/capabilities/colophon/types.ts +16 -7
- package/src/capabilities/config/collectConfigShow.test.ts +57 -0
- package/src/capabilities/config/collectConfigShow.ts +41 -4
- package/src/capabilities/config/fields.ts +0 -27
- package/src/capabilities/config/set.verb.ts +7 -3
- package/src/capabilities/config/show.render.test.ts +77 -5
- package/src/capabilities/config/show.render.ts +7 -12
- package/src/capabilities/config/show.verb.ts +4 -3
- package/src/capabilities/config/types.ts +8 -0
- package/src/capabilities/create/byteEquality.test.ts +78 -100
- package/src/capabilities/create/compiledCreate.subprocess.test.ts +52 -15
- package/src/capabilities/create/constants.ts +75 -0
- package/src/capabilities/create/create.mcp.test.ts +2 -2
- package/src/capabilities/create/create.test.ts +162 -15
- package/src/capabilities/create/create.verb.ts +140 -69
- package/src/capabilities/create/index.ts +2 -10
- package/src/capabilities/create/pickGenerator.ts +34 -28
- package/src/capabilities/create/templates.embedded.generated.ts +4 -29
- package/src/capabilities/create/types.ts +10 -6
- package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
- package/src/capabilities/distribution.test.ts +120 -0
- package/src/capabilities/distribution.ts +59 -0
- package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
- package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
- package/src/capabilities/doctor/checks/checkMcpConfigured.ts +7 -6
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
- package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
- package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
- package/src/capabilities/doctor/doctor.render.test.ts +6 -6
- package/src/capabilities/doctor/doctor.render.ts +2 -1
- package/src/capabilities/doctor/doctor.test.ts +37 -8
- package/src/capabilities/doctor/doctor.verb.ts +6 -2
- package/src/capabilities/doctor/runChecks.ts +5 -5
- package/src/capabilities/doctor/types.ts +1 -1
- package/src/capabilities/emptyRecovery.test.ts +32 -15
- package/src/capabilities/graph/inspect.verb.ts +3 -2
- package/src/capabilities/graph/query.verb.ts +3 -2
- package/src/capabilities/graph/runQuery.ts +8 -5
- package/src/capabilities/index.ts +22 -18
- package/src/capabilities/info/collectInfo.ts +10 -6
- package/src/capabilities/info/info.render.ts +3 -2
- package/src/capabilities/info/info.verb.ts +3 -2
- package/src/capabilities/lazy.test.ts +93 -2
- package/src/capabilities/ontology/verbs.ts +21 -21
- package/src/capabilities/prompt/prompt.test.ts +12 -6
- package/src/capabilities/prompt/verbs.ts +24 -11
- package/src/capabilities/resources/provider.ts +30 -11
- package/src/capabilities/resources/resources.test.ts +85 -29
- package/src/capabilities/setup/operations/setupGenerator.ts +16 -9
- package/src/capabilities/setup/operations/setupMcp.ts +2 -2
- package/src/capabilities/setup/operations/setupSkills.ts +3 -3
- package/src/capabilities/setup/setup.verb.ts +12 -5
- package/src/capabilities/setup/shell.ts +29 -3
- package/src/capabilities/skill/discover.ts +11 -5
- package/src/capabilities/skill/render.ts +4 -5
- package/src/capabilities/skill/verbs.ts +7 -7
- package/src/capabilities/sources/collectStatus.ts +55 -50
- package/src/capabilities/sources/index.ts +1 -1
- package/src/capabilities/sources/installSkills.ts +2 -2
- package/src/capabilities/sources/runUpdate.ts +113 -122
- package/src/capabilities/sources/sources.test.ts +111 -117
- package/src/capabilities/sources/status.render.test.ts +53 -24
- package/src/capabilities/sources/status.render.ts +24 -29
- package/src/capabilities/sources/status.verb.ts +15 -8
- package/src/capabilities/sources/types.ts +18 -25
- package/src/capabilities/sources/update.render.ts +9 -2
- package/src/capabilities/sources/update.verb.ts +12 -23
- package/src/capabilities/surface.test.ts +33 -10
- package/src/capabilities/tier.parity.test.ts +144 -0
- package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
- package/src/constants.test.ts +8 -6
- package/src/constants.ts +47 -21
- package/src/identity.test.ts +333 -0
- package/src/index.ts +2 -2
- package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +5 -2
- package/src/kernel/completion/emitScripts.test.ts +1 -1
- package/src/kernel/completion/emitScripts.ts +4 -2
- package/src/kernel/completion/entitySource.test.ts +185 -41
- package/src/kernel/completion/entitySource.ts +117 -107
- package/src/kernel/completion/model.test.ts +1 -1
- package/src/kernel/completion/model.ts +11 -0
- package/src/kernel/completion/safety.test.ts +82 -36
- package/src/kernel/completion/shellDrive.test.ts +592 -0
- package/src/kernel/completion/templates/fish.ts +28 -6
- package/src/kernel/completion/types.ts +3 -3
- package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
- package/src/kernel/config/defaults.test.ts +85 -0
- package/src/kernel/config/defaults.ts +12 -23
- package/src/kernel/config/evaluateProjectConfig.ts +2 -3
- package/src/kernel/config/findProjectConfig.ts +4 -3
- package/src/kernel/config/firstRun.ts +12 -5
- package/src/kernel/config/globalConfig.ts +2 -2
- package/src/kernel/config/index.ts +4 -5
- package/src/kernel/config/paths.ts +17 -17
- package/src/kernel/config/readConfig.test.ts +242 -6
- package/src/kernel/config/readConfig.ts +8 -8
- package/src/kernel/config/schema.test.ts +50 -0
- package/src/kernel/config/schema.ts +97 -10
- package/src/kernel/config/types.ts +73 -19
- package/src/kernel/copy.test.ts +453 -0
- package/src/kernel/error/PragmaError.test.ts +1 -1
- package/src/kernel/error/constants.ts +3 -2
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +22 -45
- package/src/kernel/error/recovery.ts +22 -27
- package/src/kernel/error/types.ts +5 -4
- package/src/kernel/packs/collect.test.ts +195 -11
- package/src/kernel/packs/collect.ts +213 -58
- package/src/kernel/packs/compile.test.ts +163 -7
- package/src/kernel/packs/compile.ts +15 -12
- package/src/kernel/packs/disclosure.test.ts +5 -2
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
- package/src/kernel/packs/graphqlSource.test.ts +6 -3
- package/src/kernel/packs/iri.ts +5 -7
- package/src/kernel/packs/renderPack.ts +5 -3
- package/src/kernel/packs/resolveEntity.ts +21 -17
- package/src/kernel/packs/runBodies.ts +15 -14
- package/src/kernel/packs/schema.ts +96 -10
- package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
- package/src/kernel/packs/sparql/runSelect.ts +73 -34
- package/src/kernel/packs/toolDescription.test.ts +10 -2
- package/src/kernel/packs/types.ts +39 -4
- package/src/kernel/packs/uniqueness.ts +4 -4
- package/src/kernel/project/cli/buildProgram.test.ts +9 -4
- package/src/kernel/project/cli/dispatch.test.ts +67 -0
- package/src/kernel/project/cli/dispatch.ts +34 -12
- package/src/kernel/project/cli/helpFormat.ts +1 -1
- package/src/kernel/project/cli/rootHelp.ts +105 -105
- package/src/kernel/project/mcp/buildServer.ts +11 -6
- package/src/kernel/project/mcp/instructions.ts +11 -6
- package/src/kernel/project/mcp/parity.test.ts +39 -1
- package/src/kernel/project/mcp/prompts/prompts.test.ts +120 -5
- package/src/kernel/project/mcp/prompts/provider.ts +22 -8
- package/src/kernel/project/mcp/prompts/source.ts +68 -67
- package/src/kernel/project/mcp/registerVerb.ts +25 -6
- package/src/kernel/project/mcp/serve.ts +11 -3
- package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
- package/src/kernel/render/disclosure.ts +4 -2
- package/src/kernel/render/prefixes.test.ts +36 -0
- package/src/kernel/render/prefixes.ts +40 -13
- package/src/kernel/render/renderers.test.ts +1 -1
- package/src/kernel/runtime/graphpack/build.ts +29 -3
- package/src/kernel/runtime/graphpack/buildIndex.test.ts +46 -0
- package/src/kernel/runtime/graphpack/buildIndex.ts +52 -17
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -5
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -3
- package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +37 -11
- package/src/kernel/runtime/graphpack/graphpack.test.ts +205 -5
- package/src/kernel/runtime/graphpack/hash.ts +3 -2
- package/src/kernel/runtime/graphpack/index.ts +1 -1
- package/src/kernel/runtime/graphpack/manifest.ts +114 -12
- package/src/kernel/runtime/graphpack/read.ts +4 -9
- package/src/kernel/runtime/graphpack/schemas.ts +74 -0
- package/src/kernel/runtime/graphpack/stories.ts +84 -0
- package/src/kernel/runtime/graphpack/types.ts +61 -48
- package/src/kernel/runtime/paths.ts +64 -15
- package/src/kernel/runtime/probe.test.ts +8 -4
- package/src/kernel/runtime/readEntity.ts +6 -5
- package/src/kernel/runtime/refs/gitOps.ts +1 -19
- package/src/kernel/runtime/refs/parseRef.ts +7 -7
- package/src/kernel/runtime/refs/resolve.ts +78 -62
- package/src/kernel/runtime/resolveSources.test.ts +63 -24
- package/src/kernel/runtime/resolveSources.ts +36 -25
- package/src/kernel/runtime/storeReadiness.ts +9 -8
- package/src/kernel/runtime/storeless.test.ts +1 -1
- package/src/kernel/runtime/wasmEmbed.test.ts +22 -19
- package/src/kernel/spec/emitReference.test.ts +3 -3
- package/src/kernel/spec/emitReference.ts +144 -11
- package/src/kernel/spec/emitSurface.ts +9 -5
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.ts +8 -0
- package/src/kernel/spec/types.ts +29 -9
- package/src/kernel/spec/validate.ts +2 -1
- package/src/kernel/vocabulary.test.ts +164 -0
- package/src/kernel/vocabulary.ts +146 -0
- package/src/testing/PARITY_GAPS.ts +8 -2
- package/src/testing/behavioral/README.md +3 -3
- package/src/testing/behavioral/completion.test.ts +19 -6
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +1 -1
- package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
- package/src/testing/behavioral/harness.foundation.test.ts +14 -5
- package/src/testing/behavioral/journeys.cli.test.ts +116 -75
- package/src/testing/behavioral/journeys.defaultPack.test.ts +82 -176
- package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +9 -6
- package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
- package/src/testing/behavioral/liveReadSurface.ts +4 -3
- package/src/testing/behavioral/mcpSurface.test.ts +9 -4
- package/src/testing/behavioral/staleCommands.test.ts +10 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +7 -7
- package/src/testing/eval/cases/assembly.ts +13 -6
- package/src/testing/eval/cases/effectful.ts +25 -13
- package/src/testing/eval/cases/readNouns.ts +22 -20
- package/src/testing/eval/cases/stable.ts +5 -1
- package/src/testing/eval/eval.test.ts +7 -1
- package/src/testing/fixtures/fixtureCapability.ts +45 -1
- package/src/testing/fixtures/graph/canonical.ts +6 -3
- package/src/testing/fixtures/graph/defaultPack.ts +6 -5
- package/src/testing/fixtures/graph/owlExport.ts +7 -5
- package/src/testing/helpers/fixtureGraph.ts +33 -16
- package/src/testing/helpers/packRuntime.ts +2 -3
- package/src/testing/helpers/projectCli.ts +2 -1
- package/src/testing/helpers/projectMcp.ts +6 -0
- package/src/testing/perf/budgets.test.ts +4 -0
- package/src/testing/perf/budgets.ts +22 -7
- package/src/testing/perf/globalSetup.ts +121 -6
- package/src/types.ts +2 -2
- package/src/capabilities/block/blockList.test.ts +0 -79
- package/src/capabilities/block/blockList.verb.ts +0 -163
- package/src/capabilities/block/index.ts +0 -25
- package/src/capabilities/block/pack.ts +0 -166
- package/src/capabilities/block/parity.test.ts +0 -187
- package/src/capabilities/block/tierChain.ts +0 -78
- package/src/capabilities/colophon/pragmaColophon.ts +0 -66
- package/src/capabilities/modifier/index.ts +0 -15
- package/src/capabilities/modifier/pack.ts +0 -64
- package/src/capabilities/standard/index.ts +0 -18
- package/src/capabilities/standard/pack.ts +0 -128
- package/src/capabilities/tier/index.ts +0 -19
- package/src/capabilities/tier/lookup.render.test.ts +0 -72
- package/src/capabilities/tier/lookup.render.ts +0 -59
- package/src/capabilities/tier/lookup.test.ts +0 -83
- package/src/capabilities/tier/lookup.verb.ts +0 -44
- package/src/capabilities/tier/pack.ts +0 -28
- package/src/capabilities/tier/runLookup.ts +0 -87
- package/src/capabilities/token/addConfig.render.ts +0 -27
- package/src/capabilities/token/addConfig.test.ts +0 -62
- package/src/capabilities/token/addConfig.verb.ts +0 -40
- package/src/capabilities/token/index.ts +0 -19
- package/src/capabilities/token/pack.ts +0 -58
- package/src/capabilities/token/runAddConfig.ts +0 -95
- package/src/kernel/completion/bashDrive.test.ts +0 -73
- package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
- package/src/kernel/runtime/lock.ts +0 -90
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
11
|
+
import { RECOVERY_CLI_PREFIX } from "../../constants.js";
|
|
11
12
|
import { buildFixtureRuntime } from "../../testing/helpers/packRuntime.js";
|
|
12
13
|
import type { PragmaRuntime } from "../runtime/types.js";
|
|
13
14
|
import { compilePack } from "./compile.js";
|
|
14
15
|
import type { LookupOutput } from "./resolveEntity.js";
|
|
15
16
|
import { parsePackDefinition } from "./schema.js";
|
|
16
17
|
import type { PackDefinition, PackRow } from "./types.js";
|
|
18
|
+
import { distributionSource } from "./types.js";
|
|
17
19
|
import { assertUniqueVerbs, verbKey } from "./uniqueness.js";
|
|
18
20
|
|
|
19
21
|
const PREFIXES = {
|
|
@@ -92,8 +94,33 @@ describe("pack compiler — round-trip + shape (PROTECTED, storeless)", () => {
|
|
|
92
94
|
expect(parsePackDefinition(raw, "test:widget")).toEqual(WIDGET_PACK);
|
|
93
95
|
});
|
|
94
96
|
|
|
97
|
+
it("round-trips a fixed-count sample — the field the no-argument samples declare", () => {
|
|
98
|
+
// `sample.fixedCount` is a documented grammar field (packs/types.ts) that
|
|
99
|
+
// three of the distribution's own stories use and the covenant freezes
|
|
100
|
+
// (`block sample` takes no argument). It was missing from the `.strict()`
|
|
101
|
+
// sample schema, so ANY config- or package-declared story using it died
|
|
102
|
+
// with a fatal CONFIG_ERROR before ever reaching the compiler.
|
|
103
|
+
const lookup = WIDGET_PACK.lookup as NonNullable<PackDefinition["lookup"]>;
|
|
104
|
+
const fixed: PackDefinition = {
|
|
105
|
+
...WIDGET_PACK,
|
|
106
|
+
lookup: { ...lookup, sample: { fixedCount: true } },
|
|
107
|
+
};
|
|
108
|
+
expect(parsePackDefinition(JSON.parse(JSON.stringify(fixed)), "t")).toEqual(
|
|
109
|
+
fixed,
|
|
110
|
+
);
|
|
111
|
+
// …and the compiler honours it: no `[count]` positional on the sample verb.
|
|
112
|
+
const sample = compilePack(fixed, distributionSource("t"), PREFIXES).find(
|
|
113
|
+
(v) => verbKey(v.path) === "widget sample",
|
|
114
|
+
);
|
|
115
|
+
expect(sample?.params).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
|
|
95
118
|
it("projects list, lookup, and sample verbs with unique keys", () => {
|
|
96
|
-
const verbs = compilePack(
|
|
119
|
+
const verbs = compilePack(
|
|
120
|
+
WIDGET_PACK,
|
|
121
|
+
distributionSource("bundled:widget"),
|
|
122
|
+
PREFIXES,
|
|
123
|
+
);
|
|
97
124
|
expect(verbs.map((v) => verbKey(v.path))).toEqual([
|
|
98
125
|
"widget list",
|
|
99
126
|
"widget lookup",
|
|
@@ -103,7 +130,11 @@ describe("pack compiler — round-trip + shape (PROTECTED, storeless)", () => {
|
|
|
103
130
|
});
|
|
104
131
|
|
|
105
132
|
it("projects list filters as params (enum) + a search flag", () => {
|
|
106
|
-
const [list] = compilePack(
|
|
133
|
+
const [list] = compilePack(
|
|
134
|
+
WIDGET_PACK,
|
|
135
|
+
distributionSource("bundled:widget"),
|
|
136
|
+
PREFIXES,
|
|
137
|
+
);
|
|
107
138
|
expect(list?.params.map((p) => p.name)).toEqual(["kind", "search"]);
|
|
108
139
|
const kind = list?.params.find((p) => p.name === "kind");
|
|
109
140
|
expect(kind?.kind).toBe("enum");
|
|
@@ -112,7 +143,11 @@ describe("pack compiler — round-trip + shape (PROTECTED, storeless)", () => {
|
|
|
112
143
|
});
|
|
113
144
|
|
|
114
145
|
it("projects the lookup as a variadic name-completing positional + disclosure", () => {
|
|
115
|
-
const verbs = compilePack(
|
|
146
|
+
const verbs = compilePack(
|
|
147
|
+
WIDGET_PACK,
|
|
148
|
+
distributionSource("bundled:widget"),
|
|
149
|
+
PREFIXES,
|
|
150
|
+
);
|
|
116
151
|
const lookup = verbs.find((v) => verbKey(v.path) === "widget lookup");
|
|
117
152
|
const name = lookup?.params.at(0);
|
|
118
153
|
expect(name?.kind).toBe("string[]");
|
|
@@ -132,7 +167,7 @@ describe("pack compiler — round-trip + shape (PROTECTED, storeless)", () => {
|
|
|
132
167
|
PackDefinition["lookup"]
|
|
133
168
|
>;
|
|
134
169
|
const nameParam = (def: PackDefinition) =>
|
|
135
|
-
compilePack(def, "bundled:widget", PREFIXES)
|
|
170
|
+
compilePack(def, distributionSource("bundled:widget"), PREFIXES)
|
|
136
171
|
.find((v) => verbKey(v.path) === "widget lookup")
|
|
137
172
|
?.params.at(0);
|
|
138
173
|
|
|
@@ -162,6 +197,125 @@ describe("pack compiler — round-trip + shape (PROTECTED, storeless)", () => {
|
|
|
162
197
|
});
|
|
163
198
|
});
|
|
164
199
|
|
|
200
|
+
describe("the grammar rejects what the compiler cannot build (PROTECTED)", () => {
|
|
201
|
+
// A definition that compiles to a duplicate `(noun, verb)` key or a duplicate
|
|
202
|
+
// CLI option is not merely odd — it throws out of `assembleEffectiveModules`
|
|
203
|
+
// or out of `buildProgram`, both of which run BEFORE the command tree exists.
|
|
204
|
+
// For a package-shipped story that means every command dies, `doctor` and
|
|
205
|
+
// `sources update` included. The collision is a property of the definition,
|
|
206
|
+
// so it is rejected here, where a package story degrades to a dropped file.
|
|
207
|
+
const listShape = {
|
|
208
|
+
query: "SELECT ?uri WHERE { ?uri a ex:Widget }",
|
|
209
|
+
columns: [{ field: "uri" }],
|
|
210
|
+
};
|
|
211
|
+
const parse = (def: unknown) => () =>
|
|
212
|
+
parsePackDefinition(def, "pkg/stories/widget.json");
|
|
213
|
+
|
|
214
|
+
it("rejects an extra verb that repeats list, lookup, sample, or itself", () => {
|
|
215
|
+
const lookup = { by: "ex:name", type: "ex:Widget", sample: true };
|
|
216
|
+
expect(
|
|
217
|
+
parse({
|
|
218
|
+
noun: "widget",
|
|
219
|
+
list: listShape,
|
|
220
|
+
verbs: [{ ...listShape, verb: "list" }],
|
|
221
|
+
}),
|
|
222
|
+
).toThrow(/verb "list" is already compiled/);
|
|
223
|
+
expect(
|
|
224
|
+
parse({
|
|
225
|
+
noun: "widget",
|
|
226
|
+
lookup,
|
|
227
|
+
verbs: [{ ...listShape, verb: "lookup" }],
|
|
228
|
+
}),
|
|
229
|
+
).toThrow(/verb "lookup" is already compiled/);
|
|
230
|
+
expect(
|
|
231
|
+
parse({
|
|
232
|
+
noun: "widget",
|
|
233
|
+
lookup,
|
|
234
|
+
verbs: [{ ...listShape, verb: "sample" }],
|
|
235
|
+
}),
|
|
236
|
+
).toThrow(/verb "sample" is already compiled/);
|
|
237
|
+
expect(
|
|
238
|
+
parse({
|
|
239
|
+
noun: "widget",
|
|
240
|
+
list: listShape,
|
|
241
|
+
verbs: [
|
|
242
|
+
{ ...listShape, verb: "categories" },
|
|
243
|
+
{ ...listShape, verb: "categories" },
|
|
244
|
+
],
|
|
245
|
+
}),
|
|
246
|
+
).toThrow(/verb "categories" is already compiled/);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("rejects a filter param declared twice, on list or on an extra verb", () => {
|
|
250
|
+
const twice = [
|
|
251
|
+
{ param: "kind", variable: "uri" },
|
|
252
|
+
{ param: "kind", variable: "uri" },
|
|
253
|
+
];
|
|
254
|
+
expect(
|
|
255
|
+
parse({ noun: "widget", list: { ...listShape, filters: twice } }),
|
|
256
|
+
).toThrow(/filter param "kind" is declared twice/);
|
|
257
|
+
expect(
|
|
258
|
+
parse({
|
|
259
|
+
noun: "widget",
|
|
260
|
+
list: listShape,
|
|
261
|
+
verbs: [{ ...listShape, verb: "categories", filters: twice }],
|
|
262
|
+
}),
|
|
263
|
+
).toThrow(/filter param "kind" is declared twice/);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("still accepts the distinct forms, and they compile to unique keys/params", () => {
|
|
267
|
+
const definition = parsePackDefinition(
|
|
268
|
+
{
|
|
269
|
+
noun: "widget",
|
|
270
|
+
list: {
|
|
271
|
+
...listShape,
|
|
272
|
+
filters: [
|
|
273
|
+
{ param: "kind", variable: "uri" },
|
|
274
|
+
{ param: "tier", variable: "uri" },
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
verbs: [{ ...listShape, verb: "categories" }],
|
|
278
|
+
lookup: { by: "ex:name", type: "ex:Widget", sample: true },
|
|
279
|
+
},
|
|
280
|
+
"pkg/stories/widget.json",
|
|
281
|
+
);
|
|
282
|
+
const verbs = compilePack(
|
|
283
|
+
definition,
|
|
284
|
+
{ label: "pkg/stories/widget.json", origin: "package" },
|
|
285
|
+
PREFIXES,
|
|
286
|
+
);
|
|
287
|
+
expect(verbs.map((v) => verbKey(v.path))).toEqual([
|
|
288
|
+
"widget list",
|
|
289
|
+
"widget categories",
|
|
290
|
+
"widget lookup",
|
|
291
|
+
"widget sample",
|
|
292
|
+
]);
|
|
293
|
+
expect(() => assertUniqueVerbs(verbs)).not.toThrow();
|
|
294
|
+
expect(verbs.at(0)?.params.map((p) => p.name)).toEqual(["kind", "tier"]);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("names the shape a noun must have, not the regex", () => {
|
|
298
|
+
expect(parse({ noun: "Bad Noun", list: listShape })).toThrow(
|
|
299
|
+
/noun: must be lowercase kebab-case/,
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("rejects an emptyRecovery.cli that names a binary, and names the change", () => {
|
|
304
|
+
// The hint is rendered as `<consuming distribution> <cli>`, so a story
|
|
305
|
+
// carrying the old prefixed form would render `pragma pragma sources
|
|
306
|
+
// update`. Third-party packs are third-party DATA: they get an error that
|
|
307
|
+
// names the grammar change, not a silently doubled string.
|
|
308
|
+
const withCli = (cli: string) => ({
|
|
309
|
+
noun: "widget",
|
|
310
|
+
list: { ...listShape, emptyRecovery: { message: "None.", cli } },
|
|
311
|
+
});
|
|
312
|
+
expect(parse(withCli(`${RECOVERY_CLI_PREFIX}sources update`))).toThrow(
|
|
313
|
+
/WITHOUT the binary name/,
|
|
314
|
+
);
|
|
315
|
+
expect(parse(withCli("sources update"))).not.toThrow();
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
165
319
|
describe("pack compiler — SPARQL fetch path (PROTECTED)", () => {
|
|
166
320
|
let rt: PragmaRuntime;
|
|
167
321
|
|
|
@@ -178,9 +332,11 @@ describe("pack compiler — SPARQL fetch path (PROTECTED)", () => {
|
|
|
178
332
|
});
|
|
179
333
|
|
|
180
334
|
const run = <R>(verbLabel: string, params: Record<string, unknown>) => {
|
|
181
|
-
const verb = compilePack(
|
|
182
|
-
|
|
183
|
-
|
|
335
|
+
const verb = compilePack(
|
|
336
|
+
WIDGET_PACK,
|
|
337
|
+
distributionSource("bundled:widget"),
|
|
338
|
+
PREFIXES,
|
|
339
|
+
).find((v) => verbKey(v.path) === `widget ${verbLabel}`);
|
|
184
340
|
if (!verb) throw new Error(`no widget ${verbLabel}`);
|
|
185
341
|
return verb.run(params, rt) as Promise<R>;
|
|
186
342
|
};
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Pure, storeless spec-generation: it builds paths, params, formatters, and run
|
|
9
9
|
* closures without touching the store or zod, so it runs on the
|
|
10
|
-
* `--help`/`__complete` fast path (the
|
|
11
|
-
* dynamic merge is
|
|
10
|
+
* `--help`/`__complete` fast path (the distribution's declared stories compile
|
|
11
|
+
* at import, in `capabilities/distribution.ts`; the dynamic merge is
|
|
12
|
+
* `collect.assembleEffectiveModules`/`loadEffectiveModules`).
|
|
12
13
|
* The heavy work is deferred into the run closures (behind the lazy runtime
|
|
13
14
|
* facade).
|
|
14
15
|
*/
|
|
15
16
|
|
|
17
|
+
import { BIN_NAME } from "../../constants.js";
|
|
16
18
|
import type { PragmaRuntime } from "../runtime/types.js";
|
|
17
19
|
import { asVerb } from "../spec/asVerb.js";
|
|
18
20
|
import type { DisclosureSpec, ParamSpec, VerbSpec } from "../spec/types.js";
|
|
@@ -35,6 +37,7 @@ import type {
|
|
|
35
37
|
PackLookup,
|
|
36
38
|
PackRow,
|
|
37
39
|
PackSearch,
|
|
40
|
+
StorySource,
|
|
38
41
|
} from "./types.js";
|
|
39
42
|
|
|
40
43
|
// The run bodies pull the SPARQL/GraphQL fetch layer; they are dynamic-imported
|
|
@@ -62,7 +65,7 @@ const READ_CAPABILITY = {
|
|
|
62
65
|
*/
|
|
63
66
|
export function compilePack(
|
|
64
67
|
definition: PackDefinition,
|
|
65
|
-
source:
|
|
68
|
+
source: StorySource,
|
|
66
69
|
prefixes: Readonly<Record<string, string>>,
|
|
67
70
|
): VerbSpec[] {
|
|
68
71
|
const { noun } = definition;
|
|
@@ -111,7 +114,7 @@ interface ListVerbMeta {
|
|
|
111
114
|
readonly summary: string;
|
|
112
115
|
/** The authored MCP tool description (from `toolDescription`), if any. */
|
|
113
116
|
readonly doc?: string;
|
|
114
|
-
readonly source:
|
|
117
|
+
readonly source: StorySource;
|
|
115
118
|
readonly prefixes: Readonly<Record<string, string>>;
|
|
116
119
|
}
|
|
117
120
|
|
|
@@ -135,17 +138,17 @@ function compileListVerb(shape: PackList, meta: ListVerbMeta): VerbSpec {
|
|
|
135
138
|
}),
|
|
136
139
|
},
|
|
137
140
|
examples: [
|
|
138
|
-
{ cmd:
|
|
141
|
+
{ cmd: `${BIN_NAME} ${meta.noun} ${meta.verb}` },
|
|
139
142
|
...(filterExample
|
|
140
143
|
? [
|
|
141
144
|
{
|
|
142
|
-
cmd:
|
|
145
|
+
cmd: `${BIN_NAME} ${meta.noun} ${meta.verb} --${filterExample.param} ${quoteExample(
|
|
143
146
|
filterExample.values?.at(0) ?? "",
|
|
144
147
|
)}`,
|
|
145
148
|
},
|
|
146
149
|
]
|
|
147
150
|
: []),
|
|
148
|
-
{ cmd:
|
|
151
|
+
{ cmd: `${BIN_NAME} ${meta.noun} ${meta.verb} --format llm` },
|
|
149
152
|
],
|
|
150
153
|
capability: READ_CAPABILITY,
|
|
151
154
|
run: (params: Record<string, unknown>, rt: PragmaRuntime) =>
|
|
@@ -160,7 +163,7 @@ function compileListVerb(shape: PackList, meta: ListVerbMeta): VerbSpec {
|
|
|
160
163
|
function compileLookupVerb(
|
|
161
164
|
lookup: PackLookup,
|
|
162
165
|
noun: string,
|
|
163
|
-
source:
|
|
166
|
+
source: StorySource,
|
|
164
167
|
prefixes: Readonly<Record<string, string>>,
|
|
165
168
|
): VerbSpec {
|
|
166
169
|
// Derive-by-default: every lookup completes its `<name>` from the pack index
|
|
@@ -197,7 +200,7 @@ function compileLookupVerb(
|
|
|
197
200
|
...(lookup.toolDescription ? { doc: lookup.toolDescription } : {}),
|
|
198
201
|
params: [nameParam],
|
|
199
202
|
output: { formatters: lookupFormatters(lookup, prefixes) },
|
|
200
|
-
examples: [{ cmd:
|
|
203
|
+
examples: [{ cmd: `${BIN_NAME} ${noun} lookup <name>` }],
|
|
201
204
|
...(lookup.disclosure
|
|
202
205
|
? { disclosure: disclosureSpec(lookup.disclosure) }
|
|
203
206
|
: {}),
|
|
@@ -214,7 +217,7 @@ function compileLookupVerb(
|
|
|
214
217
|
function compileSampleVerb(
|
|
215
218
|
lookup: PackLookup,
|
|
216
219
|
noun: string,
|
|
217
|
-
source:
|
|
220
|
+
source: StorySource,
|
|
218
221
|
prefixes: Readonly<Record<string, string>>,
|
|
219
222
|
): VerbSpec {
|
|
220
223
|
const defaultCount = sampleDefaultCount(lookup);
|
|
@@ -241,8 +244,8 @@ function compileSampleVerb(
|
|
|
241
244
|
params: countParam,
|
|
242
245
|
output: { formatters: sampleFormatters(lookup, noun, prefixes) },
|
|
243
246
|
examples: [
|
|
244
|
-
{ cmd:
|
|
245
|
-
...(config?.fixedCount ? [] : [{ cmd:
|
|
247
|
+
{ cmd: `${BIN_NAME} ${noun} sample` },
|
|
248
|
+
...(config?.fixedCount ? [] : [{ cmd: `${BIN_NAME} ${noun} sample 3` }]),
|
|
246
249
|
],
|
|
247
250
|
capability: READ_CAPABILITY,
|
|
248
251
|
run: (params: Record<string, unknown>, rt: PragmaRuntime) =>
|
|
@@ -66,13 +66,16 @@ const PACK: PackDefinition = {
|
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
const lookupVerb = () =>
|
|
69
|
-
compilePack(PACK, "test:widget", PREFIXES).find(
|
|
69
|
+
compilePack(PACK, { label: "test:widget", origin: "package" }, PREFIXES).find(
|
|
70
70
|
(v) => verbKey(v.path) === "widget lookup",
|
|
71
71
|
);
|
|
72
72
|
|
|
73
73
|
async function lookupAt(
|
|
74
74
|
detail?: "summary" | "standard" | "detailed",
|
|
75
|
-
config?: {
|
|
75
|
+
config?: {
|
|
76
|
+
configDetail: "summary" | "standard" | "detailed";
|
|
77
|
+
detailOrigin: "global" | "project";
|
|
78
|
+
},
|
|
76
79
|
): Promise<Record<string, unknown>> {
|
|
77
80
|
const { rt } = await buildFixtureRuntime({
|
|
78
81
|
ttl: TTL,
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { PragmaError } from "../../error/PragmaError.js";
|
|
18
|
+
import { cliRecovery } from "../../error/recovery.js";
|
|
18
19
|
import type { PragmaRuntime } from "../../runtime/types.js";
|
|
19
20
|
import type { PackChildRow, PackEntity, PackLookup } from "../types.js";
|
|
20
21
|
import {
|
|
@@ -55,12 +56,12 @@ export async function fetchGraphqlLookup(
|
|
|
55
56
|
.map((error) => error.message)
|
|
56
57
|
.join("; ");
|
|
57
58
|
throw PragmaError.storeUnavailable(`GraphQL lookup failed: ${messages}`, {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
// `graph inspect` needs a <uri>; `ontology list` is runnable as-is.
|
|
60
|
+
recovery: cliRecovery(
|
|
61
|
+
"ontology list",
|
|
62
|
+
"Check the loaded ontology and namespaces.",
|
|
63
|
+
{ tool: "ontology_list" },
|
|
64
|
+
),
|
|
64
65
|
});
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -72,10 +73,10 @@ export async function fetchGraphqlLookup(
|
|
|
72
73
|
throw new PragmaError({
|
|
73
74
|
code: "ENTITY_NOT_FOUND",
|
|
74
75
|
message: `Could not resolve <${entityUri}> — its rdf:type may be missing from the ontology.`,
|
|
75
|
-
recovery:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
recovery: cliRecovery(
|
|
77
|
+
`graph inspect ${entityUri}`,
|
|
78
|
+
"Inspect the entity's triples to check its rdf:type.",
|
|
79
|
+
),
|
|
79
80
|
});
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -17,6 +17,7 @@ import { buildLookupDocument } from "./graphql/buildLookupDocument.js";
|
|
|
17
17
|
import type { LookupOutput } from "./resolveEntity.js";
|
|
18
18
|
import { parsePackDefinition } from "./schema.js";
|
|
19
19
|
import type { PackDefinition, PackLookup } from "./types.js";
|
|
20
|
+
import { distributionSource } from "./types.js";
|
|
20
21
|
import { verbKey } from "./uniqueness.js";
|
|
21
22
|
|
|
22
23
|
const DS = "https://ds.canonical.com/";
|
|
@@ -292,9 +293,11 @@ describe("GraphQL lookup end to end + shape parity with SPARQL (PROTECTED)", ()
|
|
|
292
293
|
});
|
|
293
294
|
|
|
294
295
|
const lookupVia = (definition: PackDefinition, name: string) => {
|
|
295
|
-
const verb = compilePack(
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
const verb = compilePack(
|
|
297
|
+
definition,
|
|
298
|
+
distributionSource("t"),
|
|
299
|
+
BLOCK_PREFIXES,
|
|
300
|
+
).find((v) => verbKey(v.path) === `${definition.noun} lookup`);
|
|
298
301
|
if (!verb) throw new Error("no lookup verb");
|
|
299
302
|
return verb.run({ name: [name] }, rt) as Promise<LookupOutput>;
|
|
300
303
|
};
|
package/src/kernel/packs/iri.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { PragmaError } from "../error/PragmaError.js";
|
|
11
|
+
import { cliRecovery } from "../error/recovery.js";
|
|
11
12
|
|
|
12
13
|
/** Characters not allowed inside a SPARQL `<IRI>`. */
|
|
13
14
|
const UNSAFE_IRI_PATTERN = /[<>"{}|\\^`\s]/;
|
|
@@ -39,8 +40,7 @@ export function resolveUri(
|
|
|
39
40
|
if (colonIdx === -1) {
|
|
40
41
|
throw PragmaError.invalidInput("uri", uri, {
|
|
41
42
|
recovery: {
|
|
42
|
-
message:
|
|
43
|
-
'Use a prefixed URI (e.g. "ds:global.component.button") or a full URI.',
|
|
43
|
+
message: 'Use a prefixed URI (e.g. "prefix:name") or a full URI.',
|
|
44
44
|
},
|
|
45
45
|
});
|
|
46
46
|
}
|
|
@@ -60,11 +60,9 @@ export function resolveUri(
|
|
|
60
60
|
|
|
61
61
|
throw PragmaError.invalidInput("prefix", prefix, {
|
|
62
62
|
validOptions: Object.keys(prefixes),
|
|
63
|
-
recovery: {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
mcp: { tool: "ontology_list" },
|
|
67
|
-
},
|
|
63
|
+
recovery: cliRecovery("ontology list", "List known ontology prefixes.", {
|
|
64
|
+
tool: "ontology_list",
|
|
65
|
+
}),
|
|
68
66
|
});
|
|
69
67
|
}
|
|
70
68
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* them (disclosure gates the fetch), never HOW it is laid out.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { BIN_NAME, RECOVERY_CLI_PREFIX } from "../../constants.js";
|
|
12
13
|
import type {
|
|
13
14
|
ColumnDef,
|
|
14
15
|
LookupField,
|
|
@@ -52,8 +53,7 @@ export interface RenderMeta {
|
|
|
52
53
|
* list on a BUILT store (a cold store would have failed with STORE_UNAVAILABLE
|
|
53
54
|
* first) means "nothing matched", so point at both possible fixes.
|
|
54
55
|
*/
|
|
55
|
-
const DEFAULT_EMPTY_HINT =
|
|
56
|
-
"If you haven't built the store yet, run `pragma sources update`; otherwise broaden your filter or channel.";
|
|
56
|
+
const DEFAULT_EMPTY_HINT = `If you haven't built the store yet, run \`${BIN_NAME} sources update\`; otherwise broaden your filter or channel.`;
|
|
57
57
|
|
|
58
58
|
/** Build the list formatters for a list-shaped verb (list or an extra verb). */
|
|
59
59
|
export function listFormatters(
|
|
@@ -69,7 +69,9 @@ export function listFormatters(
|
|
|
69
69
|
// `emptyRecovery` becomes the hint; otherwise the generic build/broaden hint.
|
|
70
70
|
const emptyHint = shape.emptyRecovery
|
|
71
71
|
? `${shape.emptyRecovery.message}${
|
|
72
|
-
shape.emptyRecovery.cli
|
|
72
|
+
shape.emptyRecovery.cli
|
|
73
|
+
? ` Run \`${RECOVERY_CLI_PREFIX}${shape.emptyRecovery.cli}\`.`
|
|
74
|
+
: ""
|
|
73
75
|
}`
|
|
74
76
|
: DEFAULT_EMPTY_HINT;
|
|
75
77
|
const options: RenderListOptions<PackRow> = {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { PragmaError } from "../error/PragmaError.js";
|
|
16
|
+
import { cliRecovery } from "../error/recovery.js";
|
|
16
17
|
import { suggestNames } from "../project/cli/suggestNames.js";
|
|
17
18
|
import type { PragmaRuntime } from "../runtime/types.js";
|
|
18
19
|
import { activeExpands } from "./disclosure.js";
|
|
@@ -27,7 +28,12 @@ import {
|
|
|
27
28
|
buildNameResolveQuery,
|
|
28
29
|
} from "./sparql/buildLookupQuery.js";
|
|
29
30
|
import { runSelect } from "./sparql/runSelect.js";
|
|
30
|
-
import type {
|
|
31
|
+
import type {
|
|
32
|
+
PackChildRow,
|
|
33
|
+
PackEntity,
|
|
34
|
+
PackLookup,
|
|
35
|
+
StorySource,
|
|
36
|
+
} from "./types.js";
|
|
31
37
|
|
|
32
38
|
/** A structured per-query lookup failure (never rejects the whole batch). */
|
|
33
39
|
export interface LookupError {
|
|
@@ -56,17 +62,15 @@ export async function resolveLookup(
|
|
|
56
62
|
lookup: PackLookup,
|
|
57
63
|
noun: string,
|
|
58
64
|
queries: readonly string[],
|
|
59
|
-
source:
|
|
65
|
+
source: StorySource,
|
|
60
66
|
prefixes: Readonly<Record<string, string>>,
|
|
61
67
|
level: string | undefined,
|
|
62
68
|
): Promise<LookupOutput> {
|
|
63
69
|
if (queries.length === 0) {
|
|
64
70
|
throw PragmaError.invalidInput("names", "(empty)", {
|
|
65
|
-
recovery: {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
mcp: { tool: `${noun}_list` },
|
|
69
|
-
},
|
|
71
|
+
recovery: cliRecovery(`${noun} list`, `List available ${noun} entries.`, {
|
|
72
|
+
tool: `${noun}_list`,
|
|
73
|
+
}),
|
|
70
74
|
});
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -111,7 +115,7 @@ async function expandQueries(
|
|
|
111
115
|
rt: LookupRuntime,
|
|
112
116
|
lookup: PackLookup,
|
|
113
117
|
noun: string,
|
|
114
|
-
source:
|
|
118
|
+
source: StorySource,
|
|
115
119
|
queries: readonly string[],
|
|
116
120
|
): Promise<{ names: string[]; globErrors: LookupError[] }> {
|
|
117
121
|
if (!queries.some(isGlobPattern))
|
|
@@ -144,7 +148,7 @@ async function lookupOne(
|
|
|
144
148
|
lookup: PackLookup,
|
|
145
149
|
noun: string,
|
|
146
150
|
query: string,
|
|
147
|
-
source:
|
|
151
|
+
source: StorySource,
|
|
148
152
|
prefixes: Readonly<Record<string, string>>,
|
|
149
153
|
level: string | undefined,
|
|
150
154
|
): Promise<PackEntity> {
|
|
@@ -161,11 +165,9 @@ async function lookupOne(
|
|
|
161
165
|
const candidates = await listEntityNames(rt, lookup, source);
|
|
162
166
|
throw PragmaError.notFound(noun, query, {
|
|
163
167
|
suggestions: suggestNames(query, candidates),
|
|
164
|
-
recovery: {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
mcp: { tool: `${noun}_list` },
|
|
168
|
-
},
|
|
168
|
+
recovery: cliRecovery(`${noun} list`, `List available ${noun} entries.`, {
|
|
169
|
+
tool: `${noun}_list`,
|
|
170
|
+
}),
|
|
169
171
|
});
|
|
170
172
|
}
|
|
171
173
|
|
|
@@ -175,7 +177,9 @@ async function lookupOne(
|
|
|
175
177
|
lookup,
|
|
176
178
|
base.uri,
|
|
177
179
|
base.name ?? query,
|
|
178
|
-
source,
|
|
180
|
+
// The GraphQL lane's only use of the source is CONFIG_ERROR attribution,
|
|
181
|
+
// which is right for any origin — so it takes the label, not provenance.
|
|
182
|
+
source.label,
|
|
179
183
|
prefixes,
|
|
180
184
|
level,
|
|
181
185
|
);
|
|
@@ -205,7 +209,7 @@ function buildEntityQuery(
|
|
|
205
209
|
throw PragmaError.invalidInput("name", query, {
|
|
206
210
|
recovery: {
|
|
207
211
|
message:
|
|
208
|
-
"Use an absolute IRI (https://…), a prefixed name (
|
|
212
|
+
"Use an absolute IRI (https://…), a prefixed name (prefix:local), or a plain entity name.",
|
|
209
213
|
},
|
|
210
214
|
});
|
|
211
215
|
}
|
|
@@ -225,7 +229,7 @@ function looksLikeIri(query: string): boolean {
|
|
|
225
229
|
export async function listEntityNames(
|
|
226
230
|
rt: LookupRuntime,
|
|
227
231
|
lookup: PackLookup,
|
|
228
|
-
source:
|
|
232
|
+
source: StorySource,
|
|
229
233
|
): Promise<string[]> {
|
|
230
234
|
const rows = await runSelect(rt, buildLookupNamesQuery(lookup), source);
|
|
231
235
|
return rows.map((row) => row.name ?? "").filter((name) => name !== "");
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { PragmaError } from "../error/PragmaError.js";
|
|
13
|
+
import { cliRecovery } from "../error/recovery.js";
|
|
13
14
|
import type { PragmaRuntime } from "../runtime/types.js";
|
|
14
15
|
import { resolvePackDetail } from "./disclosure.js";
|
|
15
16
|
import type { SampleOutput } from "./renderPack.js";
|
|
@@ -22,14 +23,14 @@ import { parseSampleCount, pickRandom } from "./sample.js";
|
|
|
22
23
|
import { applyPackFilters } from "./sparql/applyFilters.js";
|
|
23
24
|
import { applyPackSearch } from "./sparql/applySearch.js";
|
|
24
25
|
import { runSelect } from "./sparql/runSelect.js";
|
|
25
|
-
import type { PackList, PackLookup, PackRow } from "./types.js";
|
|
26
|
+
import type { PackList, PackLookup, PackRow, StorySource } from "./types.js";
|
|
26
27
|
|
|
27
28
|
/** The highest canonical level — sample fetches everything for shape discovery. */
|
|
28
29
|
const HIGHEST_LEVEL = "detailed";
|
|
29
30
|
|
|
30
31
|
/** Facts a list-shaped run body needs beyond its `shape`. */
|
|
31
32
|
export interface ListRunMeta {
|
|
32
|
-
readonly source:
|
|
33
|
+
readonly source: StorySource;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
/**
|
|
@@ -61,7 +62,7 @@ export function makeListRun(
|
|
|
61
62
|
export function makeLookupRun(
|
|
62
63
|
lookup: PackLookup,
|
|
63
64
|
noun: string,
|
|
64
|
-
source:
|
|
65
|
+
source: StorySource,
|
|
65
66
|
prefixes: Readonly<Record<string, string>>,
|
|
66
67
|
): (
|
|
67
68
|
params: Record<string, unknown>,
|
|
@@ -88,11 +89,11 @@ export function makeLookupRun(
|
|
|
88
89
|
code: first.code as PragmaError["code"],
|
|
89
90
|
message: first.message,
|
|
90
91
|
suggestions: first.suggestions ? [...first.suggestions] : undefined,
|
|
91
|
-
recovery:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
recovery: cliRecovery(
|
|
93
|
+
`${noun} list`,
|
|
94
|
+
`List available ${noun} entries.`,
|
|
95
|
+
{ tool: `${noun}_list` },
|
|
96
|
+
),
|
|
96
97
|
});
|
|
97
98
|
}
|
|
98
99
|
}
|
|
@@ -104,7 +105,7 @@ export function makeLookupRun(
|
|
|
104
105
|
export function makeSampleRun(
|
|
105
106
|
lookup: PackLookup,
|
|
106
107
|
noun: string,
|
|
107
|
-
source:
|
|
108
|
+
source: StorySource,
|
|
108
109
|
prefixes: Readonly<Record<string, string>>,
|
|
109
110
|
defaultCount: number,
|
|
110
111
|
): (
|
|
@@ -119,11 +120,11 @@ export function makeSampleRun(
|
|
|
119
120
|
if (names.length === 0) {
|
|
120
121
|
throw PragmaError.emptyResults(noun, {
|
|
121
122
|
message: `No ${noun} entries to sample.`,
|
|
122
|
-
recovery:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
recovery: cliRecovery(
|
|
124
|
+
`${noun} list`,
|
|
125
|
+
`List available ${noun} entries.`,
|
|
126
|
+
{ tool: `${noun}_list` },
|
|
127
|
+
),
|
|
127
128
|
});
|
|
128
129
|
}
|
|
129
130
|
const selected = pickRandom(names, count);
|