@canonical/pragma-cli 0.40.0 → 0.42.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 +2 -2
- package/dist/package.json +16 -16
- package/dist/src/capabilities/colophon/markdownTerminal.js.map +1 -1
- package/dist/src/capabilities/config/show.render.js.map +1 -1
- package/dist/src/capabilities/create/constants.js +6 -3
- package/dist/src/capabilities/create/constants.js.map +1 -1
- package/dist/src/capabilities/create/create.render.js.map +1 -1
- package/dist/src/capabilities/create/create.verb.js +22 -3
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/create/createSurface.generated.js +22 -0
- package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
- package/dist/src/capabilities/create/index.js +1 -1
- package/dist/src/capabilities/create/pickGenerator.js +1 -0
- package/dist/src/capabilities/create/pickGenerator.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
- package/dist/src/capabilities/resources/provider.js.map +1 -1
- package/dist/src/capabilities/setup/operations/writability.js.map +1 -1
- package/dist/src/capabilities/setup/plan.render.js.map +1 -1
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/shared/packageManager.js.map +1 -1
- package/dist/src/capabilities/skill/discover.js.map +1 -1
- package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
- package/dist/src/capabilities/sources/status.render.js.map +1 -1
- package/dist/src/kernel/completion/complete.js.map +1 -1
- package/dist/src/kernel/completion/emitScripts.js.map +1 -1
- package/dist/src/kernel/completion/parse.js.map +1 -1
- package/dist/src/kernel/completion/rank.js.map +1 -1
- package/dist/src/kernel/completion/resolve.js.map +1 -1
- package/dist/src/kernel/config/readConfig.js.map +1 -1
- package/dist/src/kernel/error/PragmaError.js.map +1 -1
- package/dist/src/kernel/packs/collect.js.map +1 -1
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
- package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
- package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
- package/dist/src/kernel/render/envelope.js.map +1 -1
- package/dist/src/kernel/render/renderers.js.map +1 -1
- package/dist/src/kernel/runtime/boot.js.map +1 -1
- package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
- package/package.json +16 -16
- package/src/capabilities/create/constants.ts +6 -3
- package/src/capabilities/create/create.mcp.test.ts +173 -1
- package/src/capabilities/create/create.test.ts +4 -2
- package/src/capabilities/create/create.verb.ts +29 -3
- package/src/capabilities/create/createGrammar.test.ts +40 -4
- package/src/capabilities/create/createSurface.generated.ts +22 -0
- package/src/capabilities/create/crossCli.subprocess.test.ts +53 -14
- package/src/capabilities/create/index.ts +1 -1
- package/src/capabilities/create/pickGenerator.ts +1 -0
- package/src/capabilities/create/shippedCreate.subprocess.test.ts +28 -7
- package/src/capabilities/surface.test.ts +29 -4
- package/src/kernel/completion/shellDrive.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/testing/behavioral/README.md +1 -1
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* existed to turn that crash into a clean refusal.
|
|
18
18
|
*
|
|
19
19
|
* There is no virtual filesystem now. Every generator's templates are real
|
|
20
|
-
* files under its own package, so all
|
|
21
|
-
* all
|
|
20
|
+
* files under its own package, so all four nouns run from the shipped entry and
|
|
21
|
+
* all four are asserted here. THE REFUSAL CASES ARE GONE ON PURPOSE: a test
|
|
22
22
|
* asserting `create package` refuses would now be asserting a defect.
|
|
23
23
|
*
|
|
24
24
|
* The svelte + lit cases stay load-bearing: `types.ts.ejs` / `index.ts.ejs` /
|
|
@@ -28,7 +28,13 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
mkdirSync,
|
|
33
|
+
mkdtempSync,
|
|
34
|
+
readdirSync,
|
|
35
|
+
readFileSync,
|
|
36
|
+
writeFileSync,
|
|
37
|
+
} from "node:fs";
|
|
32
38
|
import { tmpdir } from "node:os";
|
|
33
39
|
import { dirname, join, resolve } from "node:path";
|
|
34
40
|
import { fileURLToPath } from "node:url";
|
|
@@ -70,7 +76,12 @@ function snapshot(dir: string): Map<string, string> {
|
|
|
70
76
|
* No case installs dependencies (`runInstall` defaults false), so every tree is
|
|
71
77
|
* generator output alone and byte-comparable.
|
|
72
78
|
*/
|
|
73
|
-
const CASES: ReadonlyArray<{
|
|
79
|
+
const CASES: ReadonlyArray<{
|
|
80
|
+
label: string;
|
|
81
|
+
args: readonly string[];
|
|
82
|
+
/** Files that must already exist in the cwd (`page` adds to a domain). */
|
|
83
|
+
seed?: Readonly<Record<string, string>>;
|
|
84
|
+
}> = [
|
|
74
85
|
...(["react", "svelte", "lit"] as const).map((framework) => ({
|
|
75
86
|
label: `component ${framework}`,
|
|
76
87
|
// The framework is a PATH SEGMENT, not a flag — `create` mirrors summon's
|
|
@@ -88,6 +99,11 @@ const CASES: ReadonlyArray<{ label: string; args: readonly string[] }> = [
|
|
|
88
99
|
// `application/react`, and the tree mirrors summon's.
|
|
89
100
|
args: ["create", "application", "react", "probeapp", "--yes"],
|
|
90
101
|
},
|
|
102
|
+
{
|
|
103
|
+
label: "page",
|
|
104
|
+
args: ["create", "page", "probe/detail", "--yes"],
|
|
105
|
+
seed: { "src/domains/probe/routes.ts": "export default {};\n" },
|
|
106
|
+
},
|
|
91
107
|
];
|
|
92
108
|
|
|
93
109
|
/** Run one create invocation in its own cwd and snapshot what it wrote. */
|
|
@@ -95,19 +111,24 @@ function create(
|
|
|
95
111
|
command: string,
|
|
96
112
|
prefix: readonly string[],
|
|
97
113
|
args: readonly string[],
|
|
114
|
+
seed: Readonly<Record<string, string>> = {},
|
|
98
115
|
): Map<string, string> {
|
|
99
116
|
const dir = freshCwd();
|
|
117
|
+
for (const [file, content] of Object.entries(seed)) {
|
|
118
|
+
mkdirSync(dirname(join(dir, file)), { recursive: true });
|
|
119
|
+
writeFileSync(join(dir, file), content);
|
|
120
|
+
}
|
|
100
121
|
execFileSync(command, [...prefix, ...args], { cwd: dir, stdio: "pipe" });
|
|
101
122
|
return snapshot(dir);
|
|
102
123
|
}
|
|
103
124
|
|
|
104
125
|
describe("shipped pragma create (PROTECTED)", () => {
|
|
105
|
-
for (const { label, args } of CASES) {
|
|
126
|
+
for (const { label, args, seed } of CASES) {
|
|
106
127
|
it(`${label}: shipped entry ≡ source run, byte-for-byte`, () => {
|
|
107
128
|
// (1) The shipped entry, exactly as a consumer's `pragma` runs it.
|
|
108
|
-
const shipped = create(process.execPath, [shippedEntry], args);
|
|
129
|
+
const shipped = create(process.execPath, [shippedEntry], args, seed);
|
|
109
130
|
// (2) A source run — the reference output.
|
|
110
|
-
const source = create("bun", [sourceEntry], args);
|
|
131
|
+
const source = create("bun", [sourceEntry], args, seed);
|
|
111
132
|
|
|
112
133
|
// Wrote something. Before the distribution stopped shipping a compiled
|
|
113
134
|
// binary, `package` and `application` wrote NOTHING here — they refused.
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type Covenant,
|
|
10
10
|
} from "../kernel/spec/surfaceConformance.js";
|
|
11
11
|
import { projectMcp } from "../testing/helpers/projectMcp.js";
|
|
12
|
+
import { CREATE_GENERATORS } from "./create/constants.js";
|
|
12
13
|
import { CREATE_SURFACE } from "./create/createSurface.generated.js";
|
|
13
14
|
import { capabilities } from "./index.js";
|
|
14
15
|
|
|
@@ -199,7 +200,7 @@ describe("surface conformance — capabilities ⊆ covenant (PROTECTED)", () =>
|
|
|
199
200
|
// args carry the kebab positional the usage line prints, so the covenant
|
|
200
201
|
// names `--no-with-styles` and `[component-path]`, never a
|
|
201
202
|
// `--with-styles` the CLI rejects or a `[componentPath]` its help never
|
|
202
|
-
// prints. Pin the
|
|
203
|
+
// prints. Pin the four entries verbatim so a prompt edit that moves the
|
|
203
204
|
// covenant is SEEN.
|
|
204
205
|
expect(emitted.nouns.create?.verbs).toEqual([
|
|
205
206
|
{
|
|
@@ -247,6 +248,13 @@ describe("surface conformance — capabilities ⊆ covenant (PROTECTED)", () =>
|
|
|
247
248
|
mutates: true,
|
|
248
249
|
mcp: "create_application",
|
|
249
250
|
},
|
|
251
|
+
{
|
|
252
|
+
v: "page",
|
|
253
|
+
// The one prompt is the positional page path, so there is no flag.
|
|
254
|
+
args: ["[page-path]"],
|
|
255
|
+
mutates: true,
|
|
256
|
+
mcp: "create_page",
|
|
257
|
+
},
|
|
250
258
|
]);
|
|
251
259
|
});
|
|
252
260
|
|
|
@@ -267,8 +275,25 @@ describe("surface conformance — capabilities ⊆ covenant (PROTECTED)", () =>
|
|
|
267
275
|
);
|
|
268
276
|
}
|
|
269
277
|
}
|
|
278
|
+
// A create verb carries flags if and only if one of its generators has a
|
|
279
|
+
// non-positional prompt (`page`, whose one prompt is positional, carries
|
|
280
|
+
// none) — checked per verb against the projected surface.
|
|
281
|
+
for (const verb of emitted.nouns.create?.verbs ?? []) {
|
|
282
|
+
const binding =
|
|
283
|
+
CREATE_GENERATORS[verb.v as keyof typeof CREATE_GENERATORS];
|
|
284
|
+
expect(binding, `no binding for create ${verb.v}`).toBeDefined();
|
|
285
|
+
const hasFlagPrompt = (binding.paths as readonly string[]).some(
|
|
286
|
+
(commandPath) =>
|
|
287
|
+
CREATE_SURFACE[commandPath]?.prompts.some(
|
|
288
|
+
(prompt) => prompt.positional !== true,
|
|
289
|
+
) === true,
|
|
290
|
+
);
|
|
291
|
+
expect(
|
|
292
|
+
(verb.flags?.length ?? 0) > 0,
|
|
293
|
+
`create ${verb.v}: flags iff a non-positional prompt`,
|
|
294
|
+
).toBe(hasFlagPrompt);
|
|
295
|
+
}
|
|
270
296
|
for (const verb of emitted.nouns.create?.verbs ?? []) {
|
|
271
|
-
expect(verb.flags?.length ?? 0).toBeGreaterThan(0);
|
|
272
297
|
for (const token of verb.flags ?? []) {
|
|
273
298
|
expect([...registered.values()]).toContain(token);
|
|
274
299
|
}
|
|
@@ -378,14 +403,14 @@ describe("surface COMPLETE — emitted == covenant (PROTECTED)", () => {
|
|
|
378
403
|
// The CLOSING direction: assertConforms already proves emitted ⊆ covenant;
|
|
379
404
|
// this proves covenant ⊆ emitted, so together the tool sets are EQUAL — the
|
|
380
405
|
// surface-complete milestone. After PR7, every covenant tool is realized.
|
|
381
|
-
it("emits every covenant tool (all
|
|
406
|
+
it("emits every covenant tool (all 51) — set equality with the covenant", () => {
|
|
382
407
|
const emittedTools = new Set(emitted.mcpSurface.tools);
|
|
383
408
|
const missing = golden.mcpSurface.tools.filter((t) => !emittedTools.has(t));
|
|
384
409
|
expect(missing).toEqual([]);
|
|
385
410
|
expect([...emitted.mcpSurface.tools].sort()).toEqual(
|
|
386
411
|
[...golden.mcpSurface.tools].sort(),
|
|
387
412
|
);
|
|
388
|
-
expect(emitted.mcpSurface.tools).toHaveLength(
|
|
413
|
+
expect(emitted.mcpSurface.tools).toHaveLength(51);
|
|
389
414
|
});
|
|
390
415
|
|
|
391
416
|
// The covenant edit: the non-tool MCP surface is frozen too.
|
|
@@ -339,7 +339,7 @@ const STRUCTURE = [
|
|
|
339
339
|
at: "pragma create <TAB>",
|
|
340
340
|
words: ["pragma", "create", ""],
|
|
341
341
|
cword: 2,
|
|
342
|
-
offers: ["application", "component", "package"],
|
|
342
|
+
offers: ["application", "component", "package", "page"],
|
|
343
343
|
},
|
|
344
344
|
{
|
|
345
345
|
at: "pragma --format <TAB>",
|
|
@@ -546,7 +546,7 @@ describe.skipIf(!hasShell("fish"))(
|
|
|
546
546
|
},
|
|
547
547
|
{
|
|
548
548
|
line: "pragma create ",
|
|
549
|
-
offers: ["application", "component", "package"],
|
|
549
|
+
offers: ["application", "component", "package", "page"],
|
|
550
550
|
},
|
|
551
551
|
// fish's own inline convention: it returns the whole `--flag=value` word,
|
|
552
552
|
// not the bare value bash offers. Assert what fish returns.
|
|
@@ -84,8 +84,8 @@ describe("surface conformance (PROTECTED)", () => {
|
|
|
84
84
|
);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
it("freezes exactly
|
|
88
|
-
expect(golden.mcpSurface.tools).toHaveLength(
|
|
87
|
+
it("freezes exactly 51 designed MCP tools including info, the config family, colophon", () => {
|
|
88
|
+
expect(golden.mcpSurface.tools).toHaveLength(51);
|
|
89
89
|
expect(golden.mcpSurface.tools).toContain("info");
|
|
90
90
|
expect(golden.mcpSurface.tools).toContain("config_show");
|
|
91
91
|
expect(golden.mcpSurface.tools).toContain("config_get");
|
|
@@ -136,7 +136,7 @@ assuming a mismatch is a bug:
|
|
|
136
136
|
## Extension points this PR left for you, by owner
|
|
137
137
|
|
|
138
138
|
- **PR5 (`create`)** — `C1` in the port ledger. No journey exists yet for
|
|
139
|
-
`create component/package/application` dry-run/real/byte-equality. Add
|
|
139
|
+
`create component/package/application/page` dry-run/real/byte-equality. Add
|
|
140
140
|
`journeys.create.test.ts` here plus its own eval cases.
|
|
141
141
|
- **PR6 (config mutation, info enrichment, doctor, upgrade, setup, `graph
|
|
142
142
|
query`)** — `C2`/`C3`/`C4` — **BUILT**. Each new verb ships its own
|