@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
package/src/bin.ts
CHANGED
|
@@ -129,16 +129,25 @@ async function main(): Promise<void> {
|
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
// A real command merges config-declared story packs into the
|
|
133
|
-
// only); `--help` stays on the static, storeless capabilities
|
|
134
|
-
// and the golden hold. An invalid
|
|
132
|
+
// A real command merges the package- and config-declared story packs into the
|
|
133
|
+
// tree (DISPATCH only); `--help` stays on the static, storeless capabilities
|
|
134
|
+
// so its budget and the golden hold. An invalid CONFIG story surfaces as a
|
|
135
|
+
// rendered error; a package story that cannot be used is named on stderr and
|
|
136
|
+
// the command carries on (it is third-party data, and failing here would take
|
|
137
|
+
// `sources update` and `doctor` — the only recoveries — down with it).
|
|
135
138
|
let modules = capabilities;
|
|
136
139
|
if (!explicitHelp) {
|
|
137
140
|
try {
|
|
138
141
|
const { loadEffectiveModules } = await import(
|
|
139
142
|
"./kernel/packs/collect.js"
|
|
140
143
|
);
|
|
141
|
-
|
|
144
|
+
const effective = await loadEffectiveModules(capabilities, process.cwd());
|
|
145
|
+
modules = effective.modules;
|
|
146
|
+
for (const problem of effective.problems) {
|
|
147
|
+
process.stderr.write(
|
|
148
|
+
`Ignored story ${problem.source}: ${problem.message}\n`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
142
151
|
} catch (error) {
|
|
143
152
|
await renderStartupError(error, globalFlags.format === "json");
|
|
144
153
|
return;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block-noun semantic parity (PROTECTED) — the `block` story `pragma.conf.ts`
|
|
3
|
+
* declares, over the shared block fixture graph (the standard-noun pattern).
|
|
4
|
+
*
|
|
5
|
+
* The `block` noun has no hand-written code left (L-OPEN-9): `list` and
|
|
6
|
+
* `lookup` both compile from the declared story, so parity is asserted directly
|
|
7
|
+
* against the graph. Two halves:
|
|
8
|
+
*
|
|
9
|
+
* - `block list` — the UNFILTERED list. Every block in the store, whatever the
|
|
10
|
+
* tier or channel, including the UNTIERED subcomponent the old hand-written
|
|
11
|
+
* query's required `ds:tier` join dropped (A2, formerly reachable only under
|
|
12
|
+
* `--all-tiers`). The row shape the hand-written verb built in TypeScript —
|
|
13
|
+
* the name fallback, the lowercased type, the tier's local name — is now
|
|
14
|
+
* derived IN SPARQL by `BIND`/`COALESCE`, and this file is what holds those
|
|
15
|
+
* BINDs to the shapes the old `blockList.verb.ts` produced.
|
|
16
|
+
* - `block lookup` — unchanged GraphQL content parity, moved here from the
|
|
17
|
+
* deleted `capabilities/block/parity.test.ts`. Button and Modal resolve with
|
|
18
|
+
* the same content a direct SPARQL oracle returns (summary, guidance,
|
|
19
|
+
* anatomy, tier, modifier families with values, properties, subcomponents).
|
|
20
|
+
* The fixture DECLARES whenToUse/whenNotToUse (which the live graph superseded
|
|
21
|
+
* with ds:usage), so those sections are exercised here.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
25
|
+
import type { LookupOutput } from "../kernel/packs/resolveEntity.js";
|
|
26
|
+
import type { PackRow } from "../kernel/packs/types.js";
|
|
27
|
+
import { verbKey } from "../kernel/packs/uniqueness.js";
|
|
28
|
+
import type { PragmaRuntime } from "../kernel/runtime/types.js";
|
|
29
|
+
import type { VerbSpec } from "../kernel/spec/types.js";
|
|
30
|
+
import { BLOCK_PREFIXES, BLOCK_TTL } from "../testing/fixtures/blockGraph.js";
|
|
31
|
+
import { buildFixtureRuntime } from "../testing/helpers/packRuntime.js";
|
|
32
|
+
import { storyModules } from "./distribution.js";
|
|
33
|
+
|
|
34
|
+
const blockModule = storyModules.get("block");
|
|
35
|
+
if (!blockModule) {
|
|
36
|
+
throw new Error('pragma.conf.ts declares no story for "block"');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DS = "https://ds.canonical.com/";
|
|
40
|
+
|
|
41
|
+
const verb = (label: string): VerbSpec =>
|
|
42
|
+
blockModule.verbs.find(
|
|
43
|
+
(v) => verbKey(v.path) === `block ${label}`,
|
|
44
|
+
) as VerbSpec;
|
|
45
|
+
|
|
46
|
+
let rt: PragmaRuntime;
|
|
47
|
+
|
|
48
|
+
beforeAll(async () => {
|
|
49
|
+
({ rt } = await buildFixtureRuntime({
|
|
50
|
+
ttl: BLOCK_TTL,
|
|
51
|
+
prefixes: BLOCK_PREFIXES,
|
|
52
|
+
detail: "detailed",
|
|
53
|
+
}));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterAll(async () => {
|
|
57
|
+
(await rt.store.get()).store.dispose();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
async function lookup(name: string): Promise<Record<string, unknown>> {
|
|
61
|
+
const out = (await verb("lookup").run({ name: [name] }, rt)) as LookupOutput;
|
|
62
|
+
expect(out.errors).toEqual([]);
|
|
63
|
+
return out.results.at(0) as Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("block list parity — the declared, unfiltered list", () => {
|
|
67
|
+
it("lists EVERY block, including the untiered subcomponent (A2)", async () => {
|
|
68
|
+
// `ds:button.icon` is a `ds:Subcomponent` with NO `ds:tier`. The
|
|
69
|
+
// hand-written query inner-joined `?c ds:tier ?t` in its default view and
|
|
70
|
+
// surfaced it only under `--all-tiers`; the declared query's OPTIONAL join
|
|
71
|
+
// means there is no view it can fall out of.
|
|
72
|
+
const rows = (await verb("list").run({}, rt)) as PackRow[];
|
|
73
|
+
expect(rows.map((row) => row.name)).toEqual([
|
|
74
|
+
"Button",
|
|
75
|
+
"Button Icon",
|
|
76
|
+
"Modal",
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("derives the summary row shape in SPARQL, matching the old hand-built row", async () => {
|
|
81
|
+
const rows = (await verb("list").run({}, rt)) as PackRow[];
|
|
82
|
+
const byName = new Map(rows.map((row) => [row.name, row]));
|
|
83
|
+
const button = byName.get("Button");
|
|
84
|
+
// `type`: the LOWERCASED local name of the matched class — the BIND that
|
|
85
|
+
// replaced `normalizeType`.
|
|
86
|
+
expect(button?.type).toBe("component");
|
|
87
|
+
expect(byName.get("Button Icon")?.type).toBe("subcomponent");
|
|
88
|
+
// `tier`: the tier IRI's local name — the BIND that replaced `localName`.
|
|
89
|
+
expect(button?.tier).toBe("global");
|
|
90
|
+
expect(button?.uri).toBe(`${DS}button`);
|
|
91
|
+
// GROUP_CONCAT of the family names (order-independent).
|
|
92
|
+
expect(String(button?.modifiers).split(", ").sort()).toEqual([
|
|
93
|
+
"density",
|
|
94
|
+
"importance",
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("an untiered block simply carries no tier — it is not dropped or blanked", async () => {
|
|
99
|
+
const rows = (await verb("list").run({}, rt)) as PackRow[];
|
|
100
|
+
const icon = rows.find((row) => row.name === "Button Icon");
|
|
101
|
+
expect(icon).toBeDefined();
|
|
102
|
+
expect(icon?.tier).toBeUndefined();
|
|
103
|
+
expect(icon?.uri).toBe(`${DS}button.icon`);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("is CONFIG-INDEPENDENT: a configured tier and channel change nothing", async () => {
|
|
107
|
+
// The signed-off consequence of L-OPEN-9, pinned at the verb: the run body
|
|
108
|
+
// never reads `loadConfig()`, so the same rows come back under any scope.
|
|
109
|
+
const scoped = {
|
|
110
|
+
...rt,
|
|
111
|
+
loadConfig: async () => ({
|
|
112
|
+
...(await rt.loadConfig()),
|
|
113
|
+
config: {
|
|
114
|
+
...(await rt.loadConfig()).config,
|
|
115
|
+
tier: "apps/lxd",
|
|
116
|
+
channel: "experimental" as const,
|
|
117
|
+
},
|
|
118
|
+
}),
|
|
119
|
+
} as PragmaRuntime;
|
|
120
|
+
const scopedRows = (await verb("list").run({}, scoped)) as PackRow[];
|
|
121
|
+
const plainRows = (await verb("list").run({}, rt)) as PackRow[];
|
|
122
|
+
expect(scopedRows).toEqual(plainRows);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("zero rows is a calm empty list, not an error (the declared-family semantics)", async () => {
|
|
126
|
+
// D10-A, measured: `makeListRun` returns `[]` and never throws on
|
|
127
|
+
// emptiness — identical exit-code behaviour to the hand-written verb, which
|
|
128
|
+
// also returned an empty array. Only the empty MESSAGE changed (the pack's
|
|
129
|
+
// `emptyRecovery` replaces the `--all-tiers` hint that no longer exists).
|
|
130
|
+
const { rt: emptyRt } = await buildFixtureRuntime({
|
|
131
|
+
ttl: `
|
|
132
|
+
@prefix ds: <${DS}> .
|
|
133
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
134
|
+
ds:Component a owl:Class .
|
|
135
|
+
`,
|
|
136
|
+
prefixes: BLOCK_PREFIXES,
|
|
137
|
+
});
|
|
138
|
+
try {
|
|
139
|
+
const rows = (await verb("list").run({}, emptyRt)) as PackRow[];
|
|
140
|
+
expect(rows).toEqual([]);
|
|
141
|
+
const plain = verb("list").output.formatters.plain(rows);
|
|
142
|
+
expect(plain).toContain("No blocks in the store.");
|
|
143
|
+
expect(plain).toContain("pragma sources update");
|
|
144
|
+
} finally {
|
|
145
|
+
(await emptyRt.store.get()).store.dispose();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("block lookup — Button content parity (GraphQL, detailed)", () => {
|
|
151
|
+
it("resolves the full Button spec with content matching the SPARQL oracle", async () => {
|
|
152
|
+
const button = await lookup("Button");
|
|
153
|
+
expect(button.uri).toBe(`${DS}button`);
|
|
154
|
+
expect(button.name).toBe("Button");
|
|
155
|
+
expect(button.tier).toBe(`${DS}global`);
|
|
156
|
+
|
|
157
|
+
// Guidance + anatomy sections — each cross-checked against the graph.
|
|
158
|
+
expect(button.summary).toBe(await oracle(`${DS}button`, "ds:summary"));
|
|
159
|
+
expect(button.whenToUse).toBe(await oracle(`${DS}button`, "ds:whenToUse"));
|
|
160
|
+
expect(button.whenNotToUse).toBe(
|
|
161
|
+
await oracle(`${DS}button`, "ds:whenNotToUse"),
|
|
162
|
+
);
|
|
163
|
+
expect(button.guidelines).toBe(
|
|
164
|
+
await oracle(`${DS}button`, "ds:guidelines"),
|
|
165
|
+
);
|
|
166
|
+
expect(button.anatomyDsl).toBe(
|
|
167
|
+
await oracle(`${DS}button`, "ds:anatomyDsl"),
|
|
168
|
+
);
|
|
169
|
+
expect(button.figmaLink).toBe(await oracle(`${DS}button`, "ds:figmaLink"));
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("resolves the nested modifier families with values (inverse union)", async () => {
|
|
173
|
+
const button = await lookup("Button");
|
|
174
|
+
const families = button.modifierFamilies as {
|
|
175
|
+
name: string;
|
|
176
|
+
values?: string[];
|
|
177
|
+
}[];
|
|
178
|
+
expect(families.map((f) => f.name).sort()).toEqual([
|
|
179
|
+
"density",
|
|
180
|
+
"importance",
|
|
181
|
+
]);
|
|
182
|
+
const importance = families.find((f) => f.name === "importance");
|
|
183
|
+
expect(importance?.values?.sort()).toEqual(["primary", "secondary"]);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("resolves properties and the subtype-scoped subcomponents identity field", async () => {
|
|
187
|
+
const button = await lookup("Button");
|
|
188
|
+
expect(button.properties).toEqual([
|
|
189
|
+
{ name: "disabled", type: "boolean", optional: "true" },
|
|
190
|
+
]);
|
|
191
|
+
expect(button.subcomponents).toEqual([
|
|
192
|
+
{ name: "Button Icon", uri: `${DS}button.icon` },
|
|
193
|
+
]);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("renders every declared section in the llm output", async () => {
|
|
197
|
+
const out = (await verb("lookup").run({ name: ["Button"] }, rt)) as never;
|
|
198
|
+
const llm = verb("lookup").output.formatters.llm(out);
|
|
199
|
+
expect(llm).toContain("## Button");
|
|
200
|
+
expect(llm).toContain("### When to use");
|
|
201
|
+
expect(llm).toContain("### Anatomy (DSL)");
|
|
202
|
+
expect(llm).toContain("### Modifier Families");
|
|
203
|
+
expect(llm).toContain("primary");
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe("block lookup — Modal content parity (GraphQL, detailed)", () => {
|
|
208
|
+
it("resolves the full Modal spec (a different block, same document engine)", async () => {
|
|
209
|
+
const modal = await lookup("Modal");
|
|
210
|
+
expect(modal.uri).toBe(`${DS}modal`);
|
|
211
|
+
expect(modal.name).toBe("Modal");
|
|
212
|
+
expect(modal.summary).toBe(await oracle(`${DS}modal`, "ds:summary"));
|
|
213
|
+
expect(modal.whenToUse).toBe(await oracle(`${DS}modal`, "ds:whenToUse"));
|
|
214
|
+
const families = modal.modifierFamilies as {
|
|
215
|
+
name: string;
|
|
216
|
+
values?: string[];
|
|
217
|
+
}[];
|
|
218
|
+
expect(families.map((f) => f.name)).toEqual(["size"]);
|
|
219
|
+
expect(families[0]?.values?.sort()).toEqual(["large", "small"]);
|
|
220
|
+
expect(modal.properties).toEqual([
|
|
221
|
+
{ name: "open", type: "boolean", optional: "false" },
|
|
222
|
+
]);
|
|
223
|
+
// Modal has no subcomponents — the subtype-scoped selection is simply absent.
|
|
224
|
+
expect(modal.subcomponents).toEqual([]);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("block lookup — disclosure trims to the base view at summary", () => {
|
|
229
|
+
it("summary keeps name/tier/summary and drops the detailed sections/expands", async () => {
|
|
230
|
+
const out = (await verb("lookup").run(
|
|
231
|
+
{ name: ["Button"] },
|
|
232
|
+
{ ...rt, globalFlags: { ...rt.globalFlags, detail: "summary" } },
|
|
233
|
+
)) as LookupOutput;
|
|
234
|
+
const button = out.results.at(0) as Record<string, unknown>;
|
|
235
|
+
expect(button.name).toBe("Button");
|
|
236
|
+
expect(button.summary).toBeDefined();
|
|
237
|
+
expect(button.whenToUse).toBeUndefined();
|
|
238
|
+
expect(button.anatomyDsl).toBeUndefined();
|
|
239
|
+
expect(button.modifierFamilies).toBeUndefined();
|
|
240
|
+
expect(button.figmaLink).toBeUndefined();
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
/** Oracle: a single scalar from the graph, queried directly. */
|
|
245
|
+
async function oracle(subject: string, predicate: string): Promise<string> {
|
|
246
|
+
const result = await rt.query.sparql(
|
|
247
|
+
`SELECT ?v WHERE { <${subject}> ${predicate} ?v }`,
|
|
248
|
+
);
|
|
249
|
+
return result.type === "select" ? (result.bindings[0]?.v ?? "") : "";
|
|
250
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* (what the MCP tool returns through the envelope).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { BIN_NAME } from "../../constants.js";
|
|
9
10
|
import type { Formatters } from "../../kernel/spec/types.js";
|
|
10
11
|
import type { CapabilitiesData, ToolCategory } from "./types.js";
|
|
11
12
|
|
|
@@ -19,7 +20,7 @@ const CATEGORY_ORDER: readonly ToolCategory[] = [
|
|
|
19
20
|
|
|
20
21
|
export const capabilitiesFormatters: Formatters<CapabilitiesData> = {
|
|
21
22
|
plain(data) {
|
|
22
|
-
const lines: string[] = [
|
|
23
|
+
const lines: string[] = [`${BIN_NAME} v${data.version}`, ""];
|
|
23
24
|
|
|
24
25
|
lines.push("Conventions");
|
|
25
26
|
lines.push(` ${data.conventions.system}`);
|
|
@@ -53,7 +54,7 @@ export const capabilitiesFormatters: Formatters<CapabilitiesData> = {
|
|
|
53
54
|
},
|
|
54
55
|
|
|
55
56
|
llm(data) {
|
|
56
|
-
const lines: string[] = [`#
|
|
57
|
+
const lines: string[] = [`# ${BIN_NAME} v${data.version}`, ""];
|
|
57
58
|
|
|
58
59
|
lines.push("## Conventions");
|
|
59
60
|
lines.push(`- ${data.conventions.system}`);
|
|
@@ -7,12 +7,21 @@
|
|
|
7
7
|
* counts) is deliberately DROPPED here (it would require a store boot at
|
|
8
8
|
* orientation) and lives in `info` / `config show` / `sources status` instead.
|
|
9
9
|
*
|
|
10
|
-
* `run` is a lazy thunk that dynamic-imports the catalog
|
|
11
|
-
* registry barrel, so building the command tree
|
|
12
|
-
* the `--help`/`__complete` fast path, and the
|
|
13
|
-
* (`store.booted` stays false — the storeless-spy
|
|
10
|
+
* `run` is a lazy thunk that dynamic-imports the catalog, the capability
|
|
11
|
+
* registry barrel and the dispatch-time merge, so building the command tree
|
|
12
|
+
* never pulls the derivation onto the `--help`/`__complete` fast path, and the
|
|
13
|
+
* tool never boots the store (`store.booted` stays false — the storeless-spy
|
|
14
|
+
* invariant).
|
|
15
|
+
*
|
|
16
|
+
* It reports the EFFECTIVE modules, not the static registry: the MCP server
|
|
17
|
+
* registers its tools from the same merge, so a catalog built from the static
|
|
18
|
+
* set would omit exactly the config- and package-declared nouns `tools/list`
|
|
19
|
+
* advertises — the hand-maintained drift this catalog exists to end. With no
|
|
20
|
+
* declared stories the merge returns the registry by identity, so the payload is
|
|
21
|
+
* unchanged.
|
|
14
22
|
*/
|
|
15
23
|
|
|
24
|
+
import { BIN_NAME } from "../../constants.js";
|
|
16
25
|
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
17
26
|
import type { VerbSpec } from "../../kernel/spec/types.js";
|
|
18
27
|
import { capabilitiesFormatters } from "./capabilities.render.js";
|
|
@@ -20,14 +29,16 @@ import type { CapabilitiesData } from "./types.js";
|
|
|
20
29
|
|
|
21
30
|
const capabilitiesVerb: VerbSpec<Record<string, unknown>, CapabilitiesData> = {
|
|
22
31
|
path: ["capabilities"],
|
|
23
|
-
summary:
|
|
24
|
-
"Discover pragma conventions, the annotated tool catalog, and the discovery sequence.",
|
|
32
|
+
summary: `Discover ${BIN_NAME} conventions, the annotated tool catalog, and the discovery sequence.`,
|
|
25
33
|
doc: "Storeless orientation for agents. Returns the conventions (KG / tier-channel / SPARQL model), a four-stage discovery sequence, and every live tool with a behavioural use_when hint and category — all derived from the live grammar, so it never drifts. Call it first at session start.",
|
|
26
34
|
params: [],
|
|
27
35
|
output: { formatters: capabilitiesFormatters },
|
|
28
36
|
examples: [
|
|
29
|
-
{ cmd:
|
|
30
|
-
{
|
|
37
|
+
{ cmd: `${BIN_NAME} capabilities`, note: "the annotated tool catalog" },
|
|
38
|
+
{
|
|
39
|
+
cmd: `${BIN_NAME} capabilities --format json`,
|
|
40
|
+
note: "the structured map",
|
|
41
|
+
},
|
|
31
42
|
],
|
|
32
43
|
capability: {
|
|
33
44
|
needsStore: false,
|
|
@@ -37,11 +48,18 @@ const capabilitiesVerb: VerbSpec<Record<string, unknown>, CapabilitiesData> = {
|
|
|
37
48
|
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
38
49
|
},
|
|
39
50
|
},
|
|
40
|
-
run: (_params,
|
|
41
|
-
|
|
42
|
-
(
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
run: async (_params, runtime) => {
|
|
52
|
+
const [catalog, registry, collect] = await Promise.all([
|
|
53
|
+
import("./catalog.js"),
|
|
54
|
+
import("../index.js"),
|
|
55
|
+
import("../../kernel/packs/collect.js"),
|
|
56
|
+
]);
|
|
57
|
+
const { modules } = await collect.loadEffectiveModules(
|
|
58
|
+
registry.capabilities,
|
|
59
|
+
runtime.cwd,
|
|
60
|
+
);
|
|
61
|
+
return catalog.buildCapabilitiesData(modules);
|
|
62
|
+
},
|
|
45
63
|
};
|
|
46
64
|
|
|
47
65
|
/** The `capabilities` verb, widened for registry composition. */
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* orientation from the SAME source and the two can never diverge.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { VERSION } from "../../constants.js";
|
|
17
|
+
import { BIN_NAME, PROGRAM_DESCRIPTION, VERSION } from "../../constants.js";
|
|
18
18
|
import { emitSurface } from "../../kernel/spec/emitSurface.js";
|
|
19
19
|
import type { CapabilityModule } from "../../kernel/spec/types.js";
|
|
20
20
|
import { TOOL_HINTS } from "./hints.js";
|
|
@@ -26,18 +26,26 @@ import type {
|
|
|
26
26
|
} from "./types.js";
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
* The four orientation conventions
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* The four orientation conventions, and the single source both the
|
|
30
|
+
* `capabilities` tool and the MCP handshake read, so the two cannot contradict
|
|
31
|
+
* each other. `system` projects the distribution's identity rather than naming
|
|
32
|
+
* a domain — the live tool catalog the same handshake carries already says
|
|
33
|
+
* which nouns exist. `querying` is verbatim from the old shell (the SPARQL
|
|
34
|
+
* model is still accurate for v2); `model` stopped claiming reads are
|
|
35
|
+
* tier/channel-SCOPED when the hand-written filtering was removed (L-OPEN-9)
|
|
36
|
+
* — the hierarchy is graph data now, not a query gate; `mutations` is new in
|
|
37
|
+
* v2, surfacing the plan-first/confirm gate.
|
|
33
38
|
*/
|
|
34
39
|
export const CONVENTIONS = {
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
// `help` is authored as a bare phrase (`--help` renders it as one), so the
|
|
41
|
+
// self-description trails in parentheses rather than after a period this
|
|
42
|
+
// string would have to add — a fork writing "Explore the recipe graph."
|
|
43
|
+
// otherwise reads "recipe graph.. A CLI and MCP server…".
|
|
44
|
+
system: `${BIN_NAME} — ${PROGRAM_DESCRIPTION} (a CLI and MCP server over a knowledge graph).`,
|
|
37
45
|
model:
|
|
38
|
-
"
|
|
46
|
+
"The graph models a tier hierarchy (e.g. global > apps > apps/lxd) and release channels (normal, experimental, prerelease) as DATA on each entity. Reads are unscoped — every list shows everything; filter by inspecting an entity's tier/release fields or with graph_query.",
|
|
39
47
|
querying:
|
|
40
|
-
"All queries run against an RDF triple store. Prefixed IRIs (e.g.
|
|
48
|
+
"All queries run against an RDF triple store. Prefixed IRIs (e.g. prefix:name) identify entities. Use ontology_list to discover the active namespaces.",
|
|
41
49
|
mutations:
|
|
42
50
|
"Mutating tools are plan-first: call once WITHOUT confirm to get a plan (meta.planOnly, no writes), then repeat the call with confirm: true to execute.",
|
|
43
51
|
} as const;
|
|
@@ -83,7 +91,7 @@ export function buildDiscoverySequence(
|
|
|
83
91
|
stage: 2,
|
|
84
92
|
tool: "sources_status",
|
|
85
93
|
purpose:
|
|
86
|
-
"
|
|
94
|
+
"See which pack is answering. A fresh install answers reads from the snapshot embedded in the binary and needs no build; only an `unavailable` status requires sources_update (confirm: true), which is a project that declared its own packs and has not built them.",
|
|
87
95
|
},
|
|
88
96
|
{
|
|
89
97
|
stage: 3,
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
*
|
|
9
9
|
* `use_when` strings for the surviving tools are ported verbatim from the old
|
|
10
10
|
* shell's `domains/llm/data/toolCatalog.ts`; the net-new v2 tools (config_set,
|
|
11
|
-
* graph_query, setup, upgrade, doctor, capabilities, prompt_*,
|
|
12
|
-
*
|
|
11
|
+
* graph_query, setup, upgrade, doctor, capabilities, prompt_*, sources_*,
|
|
12
|
+
* tier_lookup, create_*) are authored here.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
import { BIN_NAME } from "../../constants.js";
|
|
15
16
|
import type { ToolHint } from "./types.js";
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -36,14 +37,14 @@ export const TOOL_HINTS: Record<string, ToolHint> = {
|
|
|
36
37
|
},
|
|
37
38
|
info: {
|
|
38
39
|
category: "diagnostic",
|
|
39
|
-
use_when:
|
|
40
|
+
use_when: `Checking ${BIN_NAME} version, configuration summary, and store state`,
|
|
40
41
|
},
|
|
41
42
|
|
|
42
43
|
// — Read ——————————————————————————————————————————————————————————————————
|
|
43
44
|
block_list: {
|
|
44
45
|
category: "read",
|
|
45
46
|
use_when:
|
|
46
|
-
"Browsing
|
|
47
|
+
"Browsing all components, patterns, layouts, and subcomponents in the store",
|
|
47
48
|
},
|
|
48
49
|
block_lookup: {
|
|
49
50
|
category: "read",
|
|
@@ -57,12 +58,12 @@ export const TOOL_HINTS: Record<string, ToolHint> = {
|
|
|
57
58
|
},
|
|
58
59
|
colophon: {
|
|
59
60
|
category: "read",
|
|
60
|
-
use_when:
|
|
61
|
-
"Understanding how pragma and the active domain are built — the toolchain + design-system colophon, for onboarding or a demo",
|
|
61
|
+
use_when: `Understanding how ${BIN_NAME} and the active domain are built — the toolchain + domain colophon, for onboarding or a demo`,
|
|
62
62
|
},
|
|
63
63
|
config_show: {
|
|
64
64
|
category: "read",
|
|
65
|
-
use_when:
|
|
65
|
+
use_when:
|
|
66
|
+
"Checking the active configuration (tier, channel, detail) and which layer set each field",
|
|
66
67
|
},
|
|
67
68
|
graph_inspect: {
|
|
68
69
|
category: "read",
|
|
@@ -105,8 +106,7 @@ export const TOOL_HINTS: Record<string, ToolHint> = {
|
|
|
105
106
|
},
|
|
106
107
|
prompt_list: {
|
|
107
108
|
category: "read",
|
|
108
|
-
use_when:
|
|
109
|
-
"Browsing the workflow prompt templates the design-system graph offers",
|
|
109
|
+
use_when: "Browsing the workflow prompt templates the active graph offers",
|
|
110
110
|
},
|
|
111
111
|
prompt_lookup: {
|
|
112
112
|
category: "read",
|
|
@@ -178,8 +178,7 @@ export const TOOL_HINTS: Record<string, ToolHint> = {
|
|
|
178
178
|
},
|
|
179
179
|
create_component: {
|
|
180
180
|
category: "write",
|
|
181
|
-
use_when:
|
|
182
|
-
"Scaffolding a new design-system component (React, Svelte, or Lit)",
|
|
181
|
+
use_when: "Scaffolding a new component (React, Svelte, or Lit)",
|
|
183
182
|
},
|
|
184
183
|
create_package: {
|
|
185
184
|
category: "write",
|
|
@@ -193,20 +192,15 @@ export const TOOL_HINTS: Record<string, ToolHint> = {
|
|
|
193
192
|
},
|
|
194
193
|
setup: {
|
|
195
194
|
category: "write",
|
|
196
|
-
use_when:
|
|
197
|
-
"Installing pragma's shell completions, MCP config, skills, and LSP into the environment",
|
|
195
|
+
use_when: `Installing ${BIN_NAME}'s shell completions, MCP config, skills, and LSP into the environment`,
|
|
198
196
|
},
|
|
199
197
|
sources_update: {
|
|
200
198
|
category: "write",
|
|
201
199
|
use_when:
|
|
202
|
-
"Building or refreshing the knowledge-graph pack from the configured
|
|
203
|
-
},
|
|
204
|
-
"token_add-config": {
|
|
205
|
-
category: "write",
|
|
206
|
-
use_when: "Generating a tokens.config.mjs for the terrazzo token pipeline",
|
|
200
|
+
"Building or refreshing the knowledge-graph pack from the configured packs",
|
|
207
201
|
},
|
|
208
202
|
upgrade: {
|
|
209
203
|
category: "write",
|
|
210
|
-
use_when:
|
|
204
|
+
use_when: `Upgrading the ${BIN_NAME} CLI itself to the latest release`,
|
|
211
205
|
},
|
|
212
206
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Collect the `colophon` payload — the verb's run body (lazily imported).
|
|
3
3
|
*
|
|
4
|
-
* STORELESS: it
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* storeless-guarantee spy still sees `store.booted === false`.
|
|
4
|
+
* STORELESS: it resolves the EFFECTIVE capability modules exactly as a real
|
|
5
|
+
* command would — through `loadEffectiveModules`, so a story declared by a
|
|
6
|
+
* PACKAGE the active pack carries surfaces its colophon here too, not only a
|
|
7
|
+
* config-declared one. It never boots the triple store, so the
|
|
8
|
+
* storeless-guarantee spy still sees `store.booted === false`. The static
|
|
9
|
+
* import of the validated distribution config (`config/defaults.js`) is safe
|
|
10
|
+
* here because this module sits behind the verb's dynamic `import()` — it
|
|
11
|
+
* never lands on the `--help`/`__complete` fast path.
|
|
9
12
|
*
|
|
10
13
|
* The registry is reached through a RUNTIME dynamic `import("../index.js")`
|
|
11
14
|
* inside this async body — never a static import — so the static
|
|
@@ -13,44 +16,56 @@
|
|
|
13
16
|
* into a static import.
|
|
14
17
|
*/
|
|
15
18
|
|
|
19
|
+
import { BIN_NAME } from "../../constants.js";
|
|
20
|
+
import distribution from "../../kernel/config/defaults.js";
|
|
16
21
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
17
22
|
import type { CapabilityModule } from "../../kernel/spec/types.js";
|
|
18
|
-
import { PRAGMA_COLOPHON, PRAGMA_COLOPHON_SUMMARY } from "./pragmaColophon.js";
|
|
19
23
|
import type { ColophonData, ColophonSection } from "./types.js";
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Assemble the colophon data for the current runtime.
|
|
23
27
|
*
|
|
28
|
+
* The toolchain section is DECLARED, not authored here: the distribution
|
|
29
|
+
* config's `colophon` field carries the narrative (and its condensed summary),
|
|
30
|
+
* and this collector renders whatever it declares under the distribution's own
|
|
31
|
+
* name — a fork edits its config, never this module. A distribution declaring
|
|
32
|
+
* no `colophon` gets no toolchain section: the packs' domain colophons stand
|
|
33
|
+
* alone. The `kind: "pragma"` value is a frozen JSON discriminant (wire
|
|
34
|
+
* compatibility), not the distribution's name.
|
|
35
|
+
*
|
|
24
36
|
* @param runtime - The per-invocation runtime.
|
|
25
|
-
* @returns The storeless colophon payload:
|
|
26
|
-
* pack/domain that declares a `colophon`.
|
|
37
|
+
* @returns The storeless colophon payload: the distribution's declared section,
|
|
38
|
+
* then each active pack/domain that declares a `colophon`.
|
|
27
39
|
* @note Impure — reads the config layers (never boots the store).
|
|
28
40
|
*/
|
|
29
41
|
export async function collectColophon(
|
|
30
42
|
runtime: PragmaRuntime,
|
|
31
43
|
): Promise<ColophonData> {
|
|
32
|
-
const sections: ColophonSection[] = [
|
|
33
|
-
|
|
44
|
+
const sections: ColophonSection[] = [];
|
|
45
|
+
if (distribution.colophon) {
|
|
46
|
+
sections.push({
|
|
34
47
|
kind: "pragma",
|
|
35
|
-
title:
|
|
36
|
-
markdown:
|
|
37
|
-
summary
|
|
48
|
+
title: BIN_NAME,
|
|
49
|
+
markdown: distribution.colophon.markdown,
|
|
50
|
+
...(distribution.colophon.summary === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { summary: distribution.colophon.summary }),
|
|
38
53
|
source: "built-in",
|
|
39
|
-
}
|
|
40
|
-
|
|
54
|
+
});
|
|
55
|
+
}
|
|
41
56
|
|
|
42
|
-
const {
|
|
57
|
+
const { loadEffectiveModules } = await import(
|
|
43
58
|
"../../kernel/packs/collect.js"
|
|
44
59
|
);
|
|
45
60
|
const { capabilities } = await import("../index.js");
|
|
46
61
|
|
|
47
62
|
let modules: readonly CapabilityModule[] = capabilities;
|
|
48
63
|
try {
|
|
49
|
-
|
|
50
|
-
modules = assembleEffectiveModules(capabilities, layers);
|
|
64
|
+
modules = (await loadEffectiveModules(capabilities, runtime.cwd)).modules;
|
|
51
65
|
} catch {
|
|
52
66
|
// A bad config story is surfaced by the real commands; the colophon degrades
|
|
53
|
-
// to the static
|
|
67
|
+
// to the static modules rather than failing the narrative. (A bad PACKAGE
|
|
68
|
+
// story never throws — it is dropped and reported by doctor.)
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
for (const module of modules) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* `infoVerb` / `doctorVerb` / `capabilitiesVerb`.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { BIN_NAME } from "../../constants.js";
|
|
10
11
|
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
11
12
|
import type { CapabilityModule, VerbSpec } from "../../kernel/spec/types.js";
|
|
12
13
|
import { colophonFormatters } from "./colophon.render.js";
|
|
@@ -14,14 +15,17 @@ import type { ColophonData } from "./types.js";
|
|
|
14
15
|
|
|
15
16
|
const colophonVerb: VerbSpec<Record<string, unknown>, ColophonData> = {
|
|
16
17
|
path: ["colophon"],
|
|
17
|
-
summary:
|
|
18
|
-
doc:
|
|
18
|
+
summary: `Narrate how ${BIN_NAME} and the active domain are made.`,
|
|
19
|
+
doc: `Storeless — a colophon for the toolchain. Prints the story the distribution declares for itself (its config's \`colophon\`) followed by the active pack's domain colophon. Also available as a condensed Markdown narration for agents, or as a structured JSON projection of the sections.`,
|
|
19
20
|
params: [],
|
|
20
21
|
output: { formatters: colophonFormatters },
|
|
21
22
|
examples: [
|
|
22
|
-
{ cmd: "pragma colophon", note: "the toolchain + active domain story" },
|
|
23
23
|
{
|
|
24
|
-
cmd:
|
|
24
|
+
cmd: `${BIN_NAME} colophon`,
|
|
25
|
+
note: "the toolchain + active domain story",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
cmd: `${BIN_NAME} colophon --format llm`,
|
|
25
29
|
note: "condensed Markdown for agents",
|
|
26
30
|
},
|
|
27
31
|
],
|