@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Test helper: boot a store-backed {@link PragmaRuntime} at a REAL `cwd` whose
|
|
3
|
-
*
|
|
3
|
+
* active-pack pointer names a built fixture pack.
|
|
4
4
|
*
|
|
5
5
|
* This is distinct from (and complements) `packRuntime.ts#buildFixtureRuntime`:
|
|
6
6
|
* that helper hands back an in-process runtime wired to a custom `LazyStore`,
|
|
@@ -8,14 +8,18 @@
|
|
|
8
8
|
* generalizes the `runtimeFor` + `buildUpdateTask` pattern proven in
|
|
9
9
|
* `capabilities/sources/sources.test.ts`: it writes an actual
|
|
10
10
|
* `pragma.config.ts` + package directory, runs the REAL `sources update` Task
|
|
11
|
-
* (resolve → build →
|
|
12
|
-
* file, EVERY independent runtime construction that resolves config from that
|
|
11
|
+
* (resolve → build → point), and returns a `cwd`. Because the pointer is a
|
|
12
|
+
* real file, EVERY independent runtime construction that resolves config from that
|
|
13
13
|
* `cwd` — `bootRuntime(flags, cwd)` (CLI dispatch), `projectMcp(modules, cwd)`
|
|
14
14
|
* (MCP), and `runCli(args, { cwd })` (spawn) — boots the SAME cached pack. That
|
|
15
15
|
* is the property PR4's cross-surface parity tests need and PR3's single-surface
|
|
16
16
|
* parity tests did not: proof that CLI-json and MCP-json agree over a graph
|
|
17
17
|
* built the same way `sources update` builds one for a real project.
|
|
18
18
|
*
|
|
19
|
+
* A fixture package may also ship `stories/*.json` (the `stories` option), which
|
|
20
|
+
* `sources update` carries into the built pack — that is the package tier a
|
|
21
|
+
* third-party noun arrives through.
|
|
22
|
+
*
|
|
19
23
|
* Hermetic: the pack cache lives under the isolated `$XDG_CACHE_HOME`
|
|
20
24
|
* `setupXdgIsolation` installs; `dispose()` removes the temp directories (the
|
|
21
25
|
* shared content-addressed pack cache itself is left for reuse/cleanup by the
|
|
@@ -30,7 +34,7 @@ import { buildUpdateTask } from "../../capabilities/sources/runUpdate.js";
|
|
|
30
34
|
import { VERSION } from "../../constants.js";
|
|
31
35
|
import type {
|
|
32
36
|
ConfigLayers,
|
|
33
|
-
|
|
37
|
+
PackDeclaration,
|
|
34
38
|
PragmaConfig,
|
|
35
39
|
} from "../../kernel/config/types.js";
|
|
36
40
|
import { bootRuntime } from "../../kernel/runtime/boot.js";
|
|
@@ -52,34 +56,39 @@ export interface FixtureGraphOptions {
|
|
|
52
56
|
readonly ttl: string;
|
|
53
57
|
/**
|
|
54
58
|
* Config fields to bake into the written `pragma.config.ts`, e.g.
|
|
55
|
-
* `{ tier: "apps/lxd", channel: "prerelease" }`. `
|
|
59
|
+
* `{ tier: "apps/lxd", channel: "prerelease" }`. `packs` is always set by
|
|
56
60
|
* this helper (to the fixture package) — do not override it here.
|
|
57
61
|
*/
|
|
58
|
-
readonly config?: Omit<Partial<PragmaConfig>, "
|
|
62
|
+
readonly config?: Omit<Partial<PragmaConfig>, "packs">;
|
|
63
|
+
/**
|
|
64
|
+
* `stories/*.json` the fixture PACKAGE ships, as raw text keyed by file name
|
|
65
|
+
* — deliberately raw, so a test can ship malformed or schema-invalid content
|
|
66
|
+
* and assert that it is dropped rather than fatal.
|
|
67
|
+
*/
|
|
68
|
+
readonly stories?: Readonly<Record<string, string>>;
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
/** A booted fixture runtime plus its `cwd` (usable by any surface) and disposal. */
|
|
62
72
|
export interface FixtureGraph {
|
|
63
73
|
/** A REAL `bootRuntime` at {@link cwd} — the same one CLI dispatch uses. */
|
|
64
74
|
readonly runtime: PragmaRuntime;
|
|
65
|
-
/** The project directory whose `pragma.
|
|
75
|
+
/** The project directory whose `pragma.config.ts` + active-pack pointer were written. */
|
|
66
76
|
readonly cwd: string;
|
|
67
77
|
/** Remove the temp directories and dispose the store session, if booted. */
|
|
68
78
|
dispose(): Promise<void>;
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
/** A synthetic runtime with an IN-MEMORY config — used only to drive the build Task. */
|
|
72
|
-
function builderRuntime(cwd: string,
|
|
82
|
+
function builderRuntime(cwd: string, packs: PackDeclaration[]): PragmaRuntime {
|
|
73
83
|
const layers: ConfigLayers = {
|
|
74
|
-
config: { channel: "normal",
|
|
84
|
+
config: { channel: "normal", packs },
|
|
75
85
|
origins: {
|
|
76
86
|
tier: "default",
|
|
77
87
|
channel: "default",
|
|
78
88
|
detail: "default",
|
|
79
|
-
|
|
89
|
+
packs: "project",
|
|
80
90
|
stories: "default",
|
|
81
91
|
prefixes: "default",
|
|
82
|
-
prompts: "default",
|
|
83
92
|
},
|
|
84
93
|
global: { path: "/nonexistent", exists: false },
|
|
85
94
|
project: { exists: false },
|
|
@@ -102,8 +111,9 @@ function configField(key: string, value: unknown): string {
|
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
/**
|
|
105
|
-
* Build a store-backed runtime whose `cwd`
|
|
106
|
-
* shared store-backed fixture path for cross-surface (CLI/MCP/spawn)
|
|
114
|
+
* Build a store-backed runtime whose `cwd` is pointed at a real built pack —
|
|
115
|
+
* the ONE shared store-backed fixture path for cross-surface (CLI/MCP/spawn)
|
|
116
|
+
* tests.
|
|
107
117
|
*
|
|
108
118
|
* @param options - The fixture graph TTL and any config overrides.
|
|
109
119
|
* @returns The runtime, its cwd, and a disposer.
|
|
@@ -116,11 +126,18 @@ export async function bootFixtureRuntime(
|
|
|
116
126
|
const pkgDir = mkdtempSync(join(tmpdir(), "pragma-fixture-pkg-"));
|
|
117
127
|
mkdirSync(join(pkgDir, "definitions"), { recursive: true });
|
|
118
128
|
writeFileSync(join(pkgDir, "definitions", "fixture.ttl"), options.ttl);
|
|
129
|
+
const stories = Object.entries(options.stories ?? {});
|
|
130
|
+
if (stories.length > 0) {
|
|
131
|
+
mkdirSync(join(pkgDir, "stories"), { recursive: true });
|
|
132
|
+
for (const [file, content] of stories) {
|
|
133
|
+
writeFileSync(join(pkgDir, "stories", file), content);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
119
136
|
|
|
120
|
-
const
|
|
137
|
+
const packs: PackDeclaration[] = [
|
|
121
138
|
{ name: "fixture", source: `file://${pkgDir}` },
|
|
122
139
|
];
|
|
123
|
-
const fields = [configField("
|
|
140
|
+
const fields = [configField("packs", packs)];
|
|
124
141
|
for (const [key, value] of Object.entries(options.config ?? {})) {
|
|
125
142
|
fields.push(configField(key, value));
|
|
126
143
|
}
|
|
@@ -129,7 +146,7 @@ export async function bootFixtureRuntime(
|
|
|
129
146
|
`export default {\n${fields.join("\n")}\n};\n`,
|
|
130
147
|
);
|
|
131
148
|
|
|
132
|
-
await runTask(await buildUpdateTask(builderRuntime(cwd,
|
|
149
|
+
await runTask(await buildUpdateTask(builderRuntime(cwd, packs)));
|
|
133
150
|
|
|
134
151
|
const runtime = bootRuntime(FIXTURE_FLAGS, cwd);
|
|
135
152
|
|
|
@@ -36,7 +36,7 @@ export interface FixtureRuntimeOptions {
|
|
|
36
36
|
/** Explicit `--detail` level for this invocation. */
|
|
37
37
|
readonly detail?: DetailLevel;
|
|
38
38
|
/** Config `detail` value and its origin (drives {@link resolvePackDetail}). */
|
|
39
|
-
readonly configDetail?:
|
|
39
|
+
readonly configDetail?: DetailLevel;
|
|
40
40
|
/** Origin of the config `detail` (default `"default"`). */
|
|
41
41
|
readonly detailOrigin?: ConfigOrigin;
|
|
42
42
|
}
|
|
@@ -93,10 +93,9 @@ export async function buildFixtureRuntime(
|
|
|
93
93
|
tier: "default",
|
|
94
94
|
channel: "default",
|
|
95
95
|
detail: options.detailOrigin ?? "default",
|
|
96
|
-
|
|
96
|
+
packs: "default",
|
|
97
97
|
stories: "default",
|
|
98
98
|
prefixes: "default",
|
|
99
|
-
prompts: "default",
|
|
100
99
|
},
|
|
101
100
|
global: { path: "", exists: false },
|
|
102
101
|
project: { exists: false },
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Command } from "commander";
|
|
10
|
+
import { BIN_NAME } from "../../constants.js";
|
|
10
11
|
import { buildProgram } from "../../kernel/project/cli/buildProgram.js";
|
|
11
12
|
import type { GlobalFlags } from "../../kernel/runtime/types.js";
|
|
12
13
|
import type { CapabilityModule, VerbSpec } from "../../kernel/spec/types.js";
|
|
@@ -31,5 +32,5 @@ export function projectCli(
|
|
|
31
32
|
globalFlags: GlobalFlags = TEST_FLAGS,
|
|
32
33
|
): Command {
|
|
33
34
|
const verbs: VerbSpec[] = modules.flatMap((module) => [...module.verbs]);
|
|
34
|
-
return buildProgram(verbs, { globalFlags, programName:
|
|
35
|
+
return buildProgram(verbs, { globalFlags, programName: BIN_NAME });
|
|
35
36
|
}
|
|
@@ -42,6 +42,8 @@ export interface McpHarness {
|
|
|
42
42
|
completeResource(partial: string): Promise<string[]>;
|
|
43
43
|
/** The server capabilities negotiated at initialize (tools/resources/prompts). */
|
|
44
44
|
serverCapabilities(): ServerCapabilities | undefined;
|
|
45
|
+
/** The `serverInfo` the server introduced itself with at initialize. */
|
|
46
|
+
serverInfo(): { name: string; version: string } | undefined;
|
|
45
47
|
/** The server `instructions` string sent at initialize. */
|
|
46
48
|
instructions(): string | undefined;
|
|
47
49
|
/** List the native MCP prompts (`prompts/list`). */
|
|
@@ -120,6 +122,10 @@ export async function projectMcp(
|
|
|
120
122
|
serverCapabilities() {
|
|
121
123
|
return client.getServerCapabilities();
|
|
122
124
|
},
|
|
125
|
+
serverInfo() {
|
|
126
|
+
const info = client.getServerVersion();
|
|
127
|
+
return info ? { name: info.name, version: info.version } : undefined;
|
|
128
|
+
},
|
|
123
129
|
instructions() {
|
|
124
130
|
return client.getInstructions();
|
|
125
131
|
},
|
|
@@ -96,6 +96,10 @@ describe("perf budgets (PROTECTED)", () => {
|
|
|
96
96
|
// __store-probe boots the embedded pack from its n-quads cache and queries
|
|
97
97
|
// it — the full store-backed verb cost in the compiled binary. The first
|
|
98
98
|
// spawn materializes the pack; warmups absorb it, then it is a cache hit.
|
|
99
|
+
// Note that with 9 kept samples a nearest-rank p95 IS the maximum (see
|
|
100
|
+
// BUDGETS.md's p95-stabilization section) — deliberately kept, because the
|
|
101
|
+
// honest statistic here is the one that catches real cost, and the ceiling
|
|
102
|
+
// is sized with a 1.25× margin over a projected p95 to absorb it.
|
|
99
103
|
const result = measureCommand(BINARY, ["__store-probe"], {
|
|
100
104
|
runs: 12,
|
|
101
105
|
warmups: 3,
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
* The ceilings the protected budget tests assert against. The surface
|
|
5
5
|
* covenant's designed target for `--help`/`__complete` is 50 ms; the day-1 perf
|
|
6
6
|
* spike (commit 6) found that unrealistic on the build hardware (cold Bun
|
|
7
|
-
* process start alone is ~45 ms here), so — per the plan —
|
|
8
|
-
* to roughly 2× the measured median rather than silently weakening the
|
|
9
|
-
*
|
|
7
|
+
* process start alone is ~45 ms here), so — per the plan — those ceilings are
|
|
8
|
+
* set to roughly 2× the measured median rather than silently weakening the
|
|
9
|
+
* target. {@link BUDGET_WARM_STORE_MS} is derived differently (from a projected
|
|
10
|
+
* p95, and it lands TIGHTER than 2× its median); its arithmetic is written out
|
|
11
|
+
* in full in BUDGETS.md, as are the measurements and environment for all of
|
|
12
|
+
* them.
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
15
|
/** `pragma --help` ceiling (ms). Designed 50; measured median ~61 → 2×. */
|
|
@@ -20,11 +23,23 @@ export const BUDGET_PROJECT_CONFIG_MS = 10;
|
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* Warm store-backed verb ceiling (ms) — a store boot from the cached n-quads
|
|
23
|
-
* dump plus a query, in the compiled binary.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
+
* dump plus a query, in the compiled binary.
|
|
27
|
+
*
|
|
28
|
+
* Re-derived when the embed became the distribution's real 8 479-triple graph
|
|
29
|
+
* instead of a 23-triple sample. Netted against a `--version` control from the
|
|
30
|
+
* SAME binary in the SAME run (this box's process start swings 60–287 ms with
|
|
31
|
+
* page-cache state alone), the real pack's store work is 2.83× the sample's —
|
|
32
|
+
* the median of five repetitions across two protocols. Projected onto the
|
|
33
|
+
* reference box, whose own toy store work is 147 − 45.5 = 101.5 ms: 45.5 +
|
|
34
|
+
* 101.5 × 2.83 ≈ 333 ms median, and 333 × (176/147) ≈ 398 ms p95 using the
|
|
35
|
+
* reference box's own dispersion for this command. `ceil(398 × 1.25 / 50) × 50`
|
|
36
|
+
* = 500 — which is 1.6× the projected median, i.e. tighter than the
|
|
37
|
+
* 2×-of-median rule the ceilings above use. Every input, every step, and the
|
|
38
|
+
* raw measurements are in BUDGETS.md; the designed `<300ms` target stays in the
|
|
39
|
+
* surface covenant, exactly as the 50 ms `help` target survived its 130 ms
|
|
40
|
+
* ceiling.
|
|
26
41
|
*/
|
|
27
|
-
export const BUDGET_WARM_STORE_MS =
|
|
42
|
+
export const BUDGET_WARM_STORE_MS = 500;
|
|
28
43
|
|
|
29
44
|
/**
|
|
30
45
|
* Warm in-process MCP tool-call ceiling (ms) — PR7 graduates this from seeded to
|
|
@@ -1,24 +1,139 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Vitest global setup: build the compiled `dist/pragma` once, before the suite,
|
|
3
|
-
* if it is missing.
|
|
3
|
+
* if it is missing OR older than the sources it was built from.
|
|
4
4
|
*
|
|
5
5
|
* Shared by both configs, because two suites spawn the binary: the perf budgets
|
|
6
6
|
* (src/testing/perf/**, `test:perf`) and the storeless-guarantee guards in
|
|
7
7
|
* src/kernel/completion/safety.test.ts (the main `test:vitest` pass). Wiring it
|
|
8
8
|
* into both means `bun run test` on a clean checkout provisions the binary with
|
|
9
9
|
* no manual build step — neither suite may assume it is pre-built.
|
|
10
|
+
*
|
|
11
|
+
* STALENESS, not mere existence. `existsSync` alone meant every spawned-binary
|
|
12
|
+
* guard ran against whatever binary happened to be on disk: a mutation to
|
|
13
|
+
* `bin.ts` making the storeless `__complete` path write to `$HOME` and boot the
|
|
14
|
+
* store — precisely what `safety.test.ts` is PROTECTED to forbid — was live
|
|
15
|
+
* when run from source and invisible through the gate. The guard whose own
|
|
16
|
+
* docblock warns that an unreachable bundle "would leave the whole suite green"
|
|
17
|
+
* was itself green against a binary that predated the change under test.
|
|
18
|
+
*
|
|
19
|
+
* STALENESS INCLUDES THE EMBEDDED WORKSPACE DEPS. The binary bundles every
|
|
20
|
+
* `@canonical/*` workspace package it (transitively) imports — task, summon-*,
|
|
21
|
+
* ke, … — so an edit to `packages/runtime/task` is a change to what
|
|
22
|
+
* `dist/pragma` runs, yet this package's own `src` never moves. The gate
|
|
23
|
+
* therefore also watches each workspace-linked dependency (found by following
|
|
24
|
+
* the `node_modules/<name>` symlinks, transitively) — its `src` (the authored
|
|
25
|
+
* source), `dist` (what the bundler actually embeds, per the dep's export map)
|
|
26
|
+
* and `package.json`. Registry deps resolve into a `node_modules` store path
|
|
27
|
+
* and are skipped: their content only changes with a lockfile change, which
|
|
28
|
+
* `bun install` surfaces as a fresh symlink mtime anyway. Content hashing of
|
|
29
|
+
* the build inputs was considered and DECLINED (PR7 ruling R5): an mtime
|
|
30
|
+
* comparison is a handful of stats, needs no state file to compare against,
|
|
31
|
+
* and the failure mode of a false "stale" is one redundant 10 s build — while
|
|
32
|
+
* hashing every input on every suite start costs more than the rebuild it
|
|
33
|
+
* avoids. CI is fresh-checkout either way; this gate exists for developer
|
|
34
|
+
* boxes that have built before.
|
|
10
35
|
*/
|
|
11
36
|
|
|
12
37
|
import { spawnSync } from "node:child_process";
|
|
13
|
-
import {
|
|
38
|
+
import { lstatSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
39
|
+
import { join, sep } from "node:path";
|
|
14
40
|
import { fileURLToPath } from "node:url";
|
|
15
41
|
|
|
42
|
+
/** Everything the compiled binary is built FROM, relative to the package root. */
|
|
43
|
+
const INPUTS = ["src", "scripts", "pragma.conf.ts", "package.json"];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* What feeds the binary inside one workspace dependency: its authored source,
|
|
47
|
+
* the compiled output the bundler embeds, and its manifest. Deliberately NOT
|
|
48
|
+
* the whole package dir — that would watch `coverage/` and `node_modules/`,
|
|
49
|
+
* whose mtimes move on every test run and would force a rebuild loop.
|
|
50
|
+
*/
|
|
51
|
+
const DEP_INPUTS = ["src", "dist", "package.json"];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The newest modification time under `path`, or 0 when it does not exist.
|
|
55
|
+
* A nested `node_modules` is never descended into (a dep's own deps are
|
|
56
|
+
* enumerated as roots of their own by {@link workspaceDepRoots}).
|
|
57
|
+
*
|
|
58
|
+
* @param path - A file or directory.
|
|
59
|
+
* @returns Epoch milliseconds of the newest entry beneath it.
|
|
60
|
+
* @note Impure — stats the source tree.
|
|
61
|
+
*/
|
|
62
|
+
function newestMtime(path: string): number {
|
|
63
|
+
let stats: ReturnType<typeof statSync>;
|
|
64
|
+
try {
|
|
65
|
+
stats = statSync(path);
|
|
66
|
+
} catch {
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
if (!stats.isDirectory()) return stats.mtimeMs;
|
|
70
|
+
let newest = stats.mtimeMs;
|
|
71
|
+
for (const entry of readdirSync(path)) {
|
|
72
|
+
if (entry === "node_modules") continue;
|
|
73
|
+
newest = Math.max(newest, newestMtime(join(path, entry)));
|
|
74
|
+
}
|
|
75
|
+
return newest;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Every workspace package the binary can embed, transitively: follow each
|
|
80
|
+
* `node_modules/<name>` symlink whose target lives OUTSIDE any `node_modules`
|
|
81
|
+
* store (that is what distinguishes a workspace link from a registry install),
|
|
82
|
+
* then repeat from the target's own `node_modules`. Cycles are cut by the
|
|
83
|
+
* visited set.
|
|
84
|
+
*
|
|
85
|
+
* @param pkgRoot - This package's root directory.
|
|
86
|
+
* @returns Absolute real paths of the linked workspace dependency roots.
|
|
87
|
+
* @note Impure — reads `node_modules` link farms.
|
|
88
|
+
*/
|
|
89
|
+
function workspaceDepRoots(pkgRoot: string): string[] {
|
|
90
|
+
const visited = new Set<string>([realpathSync(pkgRoot)]);
|
|
91
|
+
const queue = [realpathSync(pkgRoot)];
|
|
92
|
+
while (queue.length > 0) {
|
|
93
|
+
const dir = queue.pop() as string;
|
|
94
|
+
const nm = join(dir, "node_modules");
|
|
95
|
+
let entries: string[];
|
|
96
|
+
try {
|
|
97
|
+
entries = readdirSync(nm);
|
|
98
|
+
} catch {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const links = entries.flatMap((entry) => {
|
|
102
|
+
if (!entry.startsWith("@")) return [join(nm, entry)];
|
|
103
|
+
try {
|
|
104
|
+
return readdirSync(join(nm, entry)).map((e) => join(nm, entry, e));
|
|
105
|
+
} catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
for (const link of links) {
|
|
110
|
+
let real: string;
|
|
111
|
+
try {
|
|
112
|
+
if (!lstatSync(link).isSymbolicLink()) continue;
|
|
113
|
+
real = realpathSync(link);
|
|
114
|
+
} catch {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (real.split(sep).includes("node_modules")) continue;
|
|
118
|
+
if (visited.has(real)) continue;
|
|
119
|
+
visited.add(real);
|
|
120
|
+
queue.push(real);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const root = realpathSync(pkgRoot);
|
|
124
|
+
return [...visited].filter((dir) => dir !== root);
|
|
125
|
+
}
|
|
126
|
+
|
|
16
127
|
export default function setup(): void {
|
|
17
128
|
const root = fileURLToPath(new URL("../../../", import.meta.url));
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
129
|
+
const built = newestMtime(join(root, "dist", "pragma"));
|
|
130
|
+
const fresh =
|
|
131
|
+
built > 0 &&
|
|
132
|
+
INPUTS.every((input) => newestMtime(join(root, input)) < built) &&
|
|
133
|
+
workspaceDepRoots(root).every((dep) =>
|
|
134
|
+
DEP_INPUTS.every((input) => newestMtime(join(dep, input)) < built),
|
|
135
|
+
);
|
|
136
|
+
if (fresh) return;
|
|
22
137
|
|
|
23
138
|
const result = spawnSync("bun", ["run", "scripts/build.ts"], {
|
|
24
139
|
cwd: root,
|
package/src/types.ts
CHANGED
|
@@ -12,8 +12,8 @@ export type { DetailLevel, OutputFormat } from "./constants.js";
|
|
|
12
12
|
// — Config authoring ————————————————————————————————————————————————————————
|
|
13
13
|
export type {
|
|
14
14
|
Channel,
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
PackDeclaration,
|
|
16
|
+
PackSource,
|
|
17
17
|
PragmaConfig,
|
|
18
18
|
} from "./kernel/config/types.js";
|
|
19
19
|
// — Error model —————————————————————————————————————————————————————————————
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
2
|
-
import { verbKey } from "../../kernel/packs/uniqueness.js";
|
|
3
|
-
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
4
|
-
import type { VerbSpec } from "../../kernel/spec/types.js";
|
|
5
|
-
import { OWL_EXPORT_TTL } from "../../testing/fixtures/graph/owlExport.js";
|
|
6
|
-
import { bootFixtureRuntime } from "../../testing/helpers/fixtureGraph.js";
|
|
7
|
-
import type { BlockRow } from "./blockList.verb.js";
|
|
8
|
-
import { blockModule } from "./index.js";
|
|
9
|
-
|
|
10
|
-
const listVerb = blockModule.verbs.find(
|
|
11
|
-
(verb) => verbKey(verb.path) === "block list",
|
|
12
|
-
) as VerbSpec;
|
|
13
|
-
|
|
14
|
-
const names = (rows: BlockRow[]): string[] =>
|
|
15
|
-
rows.map((row) => row.name).sort();
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The OWL-export fixture ships an UNTIERED `ds:Component` (`ds:datePicker`, no
|
|
19
|
-
* `ds:tier`) beside a tiered one (`ds:button`). The pre-fix `block list` gated
|
|
20
|
-
* on a REQUIRED `ds:tier` triple pattern, so `--all-tiers` — which drops the
|
|
21
|
-
* FILTER but not the pattern — still hid the untiered block, contradicting the
|
|
22
|
-
* flag's promise to show every block (A2). A tier-scoped view legitimately
|
|
23
|
-
* omits an untiered block (it joins the list through its tier); `--all-tiers`
|
|
24
|
-
* is the escape that reveals it.
|
|
25
|
-
*/
|
|
26
|
-
describe("block list — untiered blocks under --all-tiers (A2)", () => {
|
|
27
|
-
describe("no tier configured", () => {
|
|
28
|
-
let runtime: PragmaRuntime;
|
|
29
|
-
let dispose: () => Promise<void>;
|
|
30
|
-
|
|
31
|
-
beforeAll(async () => {
|
|
32
|
-
const fixture = await bootFixtureRuntime({ ttl: OWL_EXPORT_TTL });
|
|
33
|
-
runtime = fixture.runtime;
|
|
34
|
-
dispose = fixture.dispose;
|
|
35
|
-
});
|
|
36
|
-
afterAll(() => dispose());
|
|
37
|
-
|
|
38
|
-
it("the default view still omits the untiered block", async () => {
|
|
39
|
-
const rows = (await listVerb.run({}, runtime)) as BlockRow[];
|
|
40
|
-
expect(names(rows)).toEqual(["Button"]);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("--all-tiers reveals the untiered block", async () => {
|
|
44
|
-
const rows = (await listVerb.run(
|
|
45
|
-
{ allTiers: true },
|
|
46
|
-
runtime,
|
|
47
|
-
)) as BlockRow[];
|
|
48
|
-
expect(names(rows)).toEqual(["Button", "Date Picker"]);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe("a tier IS configured", () => {
|
|
53
|
-
let runtime: PragmaRuntime;
|
|
54
|
-
let dispose: () => Promise<void>;
|
|
55
|
-
|
|
56
|
-
beforeAll(async () => {
|
|
57
|
-
const fixture = await bootFixtureRuntime({
|
|
58
|
-
ttl: OWL_EXPORT_TTL,
|
|
59
|
-
config: { tier: "global", channel: "normal" },
|
|
60
|
-
});
|
|
61
|
-
runtime = fixture.runtime;
|
|
62
|
-
dispose = fixture.dispose;
|
|
63
|
-
});
|
|
64
|
-
afterAll(() => dispose());
|
|
65
|
-
|
|
66
|
-
it("a scoped tier lists its tiered block, not the untiered one", async () => {
|
|
67
|
-
const rows = (await listVerb.run({}, runtime)) as BlockRow[];
|
|
68
|
-
expect(names(rows)).toEqual(["Button"]);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("--all-tiers overrides the scope and reveals the untiered block", async () => {
|
|
72
|
-
const rows = (await listVerb.run(
|
|
73
|
-
{ allTiers: true },
|
|
74
|
-
runtime,
|
|
75
|
-
)) as BlockRow[];
|
|
76
|
-
expect(names(rows)).toContain("Date Picker");
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `block list` — the ONE hand-written read (Risk5).
|
|
3
|
-
*
|
|
4
|
-
* A block list is config-filtered in ways packs cannot express declaratively:
|
|
5
|
-
* the tier CHAIN (a tier makes its whole parent chain visible), the channel
|
|
6
|
-
* (release-level visibility), and the `--all-tiers` escape. Everything else in
|
|
7
|
-
* PR3 is a declarative pack; this verb ports that behaviour directly. It reads
|
|
8
|
-
* the effective tier/channel from config and runs one generated SELECT (no user
|
|
9
|
-
* input in the query — the tier is config, the channel a closed enum).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { runSelect } from "../../kernel/packs/sparql/runSelect.js";
|
|
13
|
-
import type {
|
|
14
|
-
ColumnDef,
|
|
15
|
-
RenderListOptions,
|
|
16
|
-
} from "../../kernel/render/contracts.js";
|
|
17
|
-
import { DEFAULT_PREFIX_MAP } from "../../kernel/render/prefixes.js";
|
|
18
|
-
import {
|
|
19
|
-
renderListLlm,
|
|
20
|
-
renderListPlain,
|
|
21
|
-
} from "../../kernel/render/renderers.js";
|
|
22
|
-
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
23
|
-
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
24
|
-
import type { Formatters, VerbSpec } from "../../kernel/spec/types.js";
|
|
25
|
-
import { buildChannelFilter, buildTierFilter } from "./tierChain.js";
|
|
26
|
-
|
|
27
|
-
/** One block summary row. */
|
|
28
|
-
export interface BlockRow {
|
|
29
|
-
readonly uri: string;
|
|
30
|
-
readonly name: string;
|
|
31
|
-
readonly type: string;
|
|
32
|
-
readonly tier: string;
|
|
33
|
-
readonly modifiers: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Local name of an IRI (`…/global` → `global`; `…#Thing` → `Thing`). */
|
|
37
|
-
function localName(uri: string): string {
|
|
38
|
-
const hash = uri.lastIndexOf("#");
|
|
39
|
-
if (hash !== -1) return uri.slice(hash + 1);
|
|
40
|
-
const slash = uri.lastIndexOf("/");
|
|
41
|
-
return slash !== -1 ? uri.slice(slash + 1) : uri;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Normalize a block's rdf:type to one of the four block kinds. */
|
|
45
|
-
function normalizeType(typeUri: string | undefined): BlockRow["type"] {
|
|
46
|
-
const local = localName(typeUri ?? "").toLowerCase();
|
|
47
|
-
return local === "pattern" || local === "layout" || local === "subcomponent"
|
|
48
|
-
? local
|
|
49
|
-
: "component";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const listColumns: ColumnDef<BlockRow>[] = [
|
|
53
|
-
{ key: "name", label: "Name" },
|
|
54
|
-
{ key: "type", label: "Type" },
|
|
55
|
-
{ key: "tier", label: "Tier" },
|
|
56
|
-
{ key: "modifiers", label: "Modifiers", showWhenEmpty: false },
|
|
57
|
-
{ key: "uri", label: "IRI" },
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
const listOptions: RenderListOptions<BlockRow> = {
|
|
61
|
-
heading: "Blocks",
|
|
62
|
-
columns: listColumns,
|
|
63
|
-
prefixes: DEFAULT_PREFIX_MAP,
|
|
64
|
-
// Zero blocks is a calm success (exit 0), not an error — the list is scoped by
|
|
65
|
-
// the active tier chain and channel, so the message names that scope and the
|
|
66
|
-
// hint offers the escape hatches (a cold store fails earlier with
|
|
67
|
-
// STORE_UNAVAILABLE, so reaching here means the store is built).
|
|
68
|
-
emptyMessage: "No blocks found under the current tier and channel.",
|
|
69
|
-
emptyHint:
|
|
70
|
-
"Use --all-tiers to ignore the tier filter, or run `pragma sources update` if the store is empty.",
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const listFormatters: Formatters<BlockRow[]> = {
|
|
74
|
-
plain: (rows) => renderListPlain(rows, listOptions),
|
|
75
|
-
llm: (rows) => renderListLlm(rows, listOptions),
|
|
76
|
-
json: (rows) => JSON.stringify(rows, null, 2),
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/** Build the config-filtered block SELECT for the resolved tier/channel. */
|
|
80
|
-
function buildQuery(
|
|
81
|
-
tier: string | undefined,
|
|
82
|
-
channel: import("../../kernel/config/types.js").Channel,
|
|
83
|
-
allTiers: boolean,
|
|
84
|
-
): string {
|
|
85
|
-
const tierFilter = allTiers ? "" : buildTierFilter(tier);
|
|
86
|
-
const channelFilter = buildChannelFilter(channel);
|
|
87
|
-
// Under --all-tiers, `ds:name`/`ds:tier` become OPTIONAL so a real block that
|
|
88
|
-
// ships without either is no longer hidden — the flag promises "all blocks",
|
|
89
|
-
// yet the pre-fix query gated on those as REQUIRED patterns, dropping the
|
|
90
|
-
// FILTER but not the pattern (A2). In the scoped/default view they stay
|
|
91
|
-
// required: a block joins the list through its tier, so an untiered block is
|
|
92
|
-
// intentionally out of a tier-scoped view (and --all-tiers is the escape).
|
|
93
|
-
const nameTierPatterns = allTiers
|
|
94
|
-
? [
|
|
95
|
-
" OPTIONAL { ?component ds:name ?name }",
|
|
96
|
-
" OPTIONAL { ?component ds:tier ?tier }",
|
|
97
|
-
]
|
|
98
|
-
: [" ?component ds:name ?name ; ds:tier ?tier ."];
|
|
99
|
-
return [
|
|
100
|
-
"SELECT ?component ?type ?name ?tier",
|
|
101
|
-
' (GROUP_CONCAT(DISTINCT ?modName; separator=", ") AS ?modifiers)',
|
|
102
|
-
"WHERE {",
|
|
103
|
-
" VALUES ?type { ds:Component ds:Pattern ds:Layout ds:Subcomponent }",
|
|
104
|
-
" ?component a ?type .",
|
|
105
|
-
...nameTierPatterns,
|
|
106
|
-
tierFilter ? ` ${tierFilter}` : "",
|
|
107
|
-
` ${channelFilter}`,
|
|
108
|
-
" OPTIONAL { ?component ds:hasModifierFamily ?mod . ?mod ds:name ?modName }",
|
|
109
|
-
"}",
|
|
110
|
-
"GROUP BY ?component ?type ?name ?tier",
|
|
111
|
-
"ORDER BY ?name",
|
|
112
|
-
]
|
|
113
|
-
.filter((line) => line !== "")
|
|
114
|
-
.join("\n");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const listVerb: VerbSpec<Record<string, unknown>, BlockRow[]> = {
|
|
118
|
-
path: ["block", "list"],
|
|
119
|
-
summary: "List blocks visible under the current tier and channel.",
|
|
120
|
-
doc: "List design system blocks visible under the active tier chain and channel. Optionally list across every tier, ignoring the tier filter.",
|
|
121
|
-
params: [
|
|
122
|
-
{
|
|
123
|
-
kind: "boolean",
|
|
124
|
-
name: "allTiers",
|
|
125
|
-
doc: "Show blocks from all tiers, ignoring the tier filter.",
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
output: { formatters: listFormatters },
|
|
129
|
-
examples: [
|
|
130
|
-
{ cmd: "pragma block list" },
|
|
131
|
-
{ cmd: "pragma block list --all-tiers" },
|
|
132
|
-
{ cmd: "pragma block list --format llm" },
|
|
133
|
-
],
|
|
134
|
-
capability: {
|
|
135
|
-
needsStore: true,
|
|
136
|
-
mutates: false,
|
|
137
|
-
mcp: {
|
|
138
|
-
expose: true,
|
|
139
|
-
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
run: async (params: Record<string, unknown>, rt: PragmaRuntime) => {
|
|
143
|
-
const layers = await rt.loadConfig();
|
|
144
|
-
const query = buildQuery(
|
|
145
|
-
layers.config.tier,
|
|
146
|
-
layers.config.channel,
|
|
147
|
-
params.allTiers === true,
|
|
148
|
-
);
|
|
149
|
-
const rows = await runSelect(rt, query, "block");
|
|
150
|
-
return rows.map((row) => ({
|
|
151
|
-
uri: row.component ?? "",
|
|
152
|
-
// An unnamed block still needs a token to show — fall back to its IRI's
|
|
153
|
-
// local name rather than rendering a blank row (A2).
|
|
154
|
-
name: row.name ?? localName(row.component ?? ""),
|
|
155
|
-
type: normalizeType(row.type),
|
|
156
|
-
tier: localName(row.tier ?? ""),
|
|
157
|
-
modifiers: row.modifiers ?? "",
|
|
158
|
-
}));
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
/** The hand-written `block list` verb. */
|
|
163
|
-
export const blockListVerb = asVerb(listVerb);
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `block` capability module — a COMPOSITE: the hand-written `block list`
|
|
3
|
-
* (tier chain + channel + --all-tiers) plus the bundled pack's GraphQL `lookup`.
|
|
4
|
-
*
|
|
5
|
-
* This is the one noun where a declarative pack and a hand-written verb share a
|
|
6
|
-
* module; the pack ships lookup-only, so there is no `(noun, verb)` collision.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { compilePack } from "../../kernel/packs/compile.js";
|
|
10
|
-
import { DEFAULT_PREFIX_MAP } from "../../kernel/render/prefixes.js";
|
|
11
|
-
import type { CapabilityModule } from "../../kernel/spec/types.js";
|
|
12
|
-
import { blockListVerb } from "./blockList.verb.js";
|
|
13
|
-
import { blockPack } from "./pack.js";
|
|
14
|
-
|
|
15
|
-
/** The `block` capability module (`list` hand-written, `lookup` from the pack). */
|
|
16
|
-
export const blockModule: CapabilityModule = {
|
|
17
|
-
name: "block",
|
|
18
|
-
verbs: [
|
|
19
|
-
blockListVerb,
|
|
20
|
-
...compilePack(blockPack, "bundled:block", DEFAULT_PREFIX_MAP),
|
|
21
|
-
],
|
|
22
|
-
// The bundled design-system domain colophon (authored on the flagship UI-block
|
|
23
|
-
// noun), surfaced by `pragma colophon` after pragma's own.
|
|
24
|
-
colophon: blockPack.colophon,
|
|
25
|
-
};
|