@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
|
@@ -25,14 +25,14 @@ import {
|
|
|
25
25
|
import { bootRuntime } from "../../kernel/runtime/boot.js";
|
|
26
26
|
import type { GlobalFlags } from "../../kernel/runtime/types.js";
|
|
27
27
|
import type { ParamSpec } from "../../kernel/spec/types.js";
|
|
28
|
+
import { CREATE_GENERATORS } from "./constants.js";
|
|
28
29
|
import {
|
|
29
|
-
|
|
30
|
-
createComponentVerb,
|
|
31
|
-
createPackageVerb,
|
|
30
|
+
createVerbs,
|
|
32
31
|
INCLUDE_FLAG_ALIASES,
|
|
33
32
|
isModuleNotFound,
|
|
34
33
|
} from "./create.verb.js";
|
|
35
34
|
import { generatorToParams } from "./generatorToVerbSpec.js";
|
|
35
|
+
import { createModule } from "./index.js";
|
|
36
36
|
|
|
37
37
|
const FLAGS: GlobalFlags = {
|
|
38
38
|
llm: false,
|
|
@@ -53,7 +53,7 @@ async function runIn(
|
|
|
53
53
|
process.chdir(dir);
|
|
54
54
|
try {
|
|
55
55
|
return await executeVerb(
|
|
56
|
-
|
|
56
|
+
createVerbs.component,
|
|
57
57
|
params,
|
|
58
58
|
mutation,
|
|
59
59
|
bootRuntime(FLAGS, dir),
|
|
@@ -86,7 +86,11 @@ describe("create — real generation + stamp", () => {
|
|
|
86
86
|
expect(readFileSync(main, "utf-8").split("\n")[0]).toBe(
|
|
87
87
|
"// Generated by @canonical/summon-component:react v0.1.0",
|
|
88
88
|
);
|
|
89
|
-
|
|
89
|
+
// Explicit timeout, like the package's other real-run gates: this drives a
|
|
90
|
+
// full generator run (~2.4s idle), and under a contended CI matrix the 5s
|
|
91
|
+
// vitest default fails it on load, not on a defect — both build-matrix
|
|
92
|
+
// legs timed out at 5000ms on a run whose import phase alone took 88s.
|
|
93
|
+
}, 60_000);
|
|
90
94
|
|
|
91
95
|
it("--dry-run writes nothing and previews effects (no Prompt lines)", async () => {
|
|
92
96
|
const dir = freshCwd();
|
|
@@ -101,6 +105,45 @@ describe("create — real generation + stamp", () => {
|
|
|
101
105
|
expect(walk(dir)).toEqual([]); // nothing written
|
|
102
106
|
});
|
|
103
107
|
|
|
108
|
+
it("--dry-run reports the bytes the real run writes (post-stamp)", async () => {
|
|
109
|
+
// The mock previewer never called `onEffectStart`, so summon's stamping
|
|
110
|
+
// transform did not run and every planned byte count under-reported the
|
|
111
|
+
// written file by the stamp's length. The honest previewer runs it.
|
|
112
|
+
const params = {
|
|
113
|
+
framework: "react",
|
|
114
|
+
componentPath: "src/components/Widget",
|
|
115
|
+
withStyles: true,
|
|
116
|
+
withStories: false,
|
|
117
|
+
withSsrTests: false,
|
|
118
|
+
};
|
|
119
|
+
const previewDir = freshCwd();
|
|
120
|
+
const preview = await runIn(previewDir, params, {
|
|
121
|
+
dryRun: true,
|
|
122
|
+
undo: false,
|
|
123
|
+
yes: false,
|
|
124
|
+
});
|
|
125
|
+
expect(preview.exitCode).toBe(0);
|
|
126
|
+
expect(walk(previewDir)).toEqual([]);
|
|
127
|
+
|
|
128
|
+
const planned = new Map<string, number>();
|
|
129
|
+
for (const line of (preview.stdout ?? "").split("\n")) {
|
|
130
|
+
const match = /^ {2}- Write file: (.+) \((\d+) bytes\)$/.exec(line);
|
|
131
|
+
if (match) planned.set(match[1] as string, Number(match[2]));
|
|
132
|
+
}
|
|
133
|
+
expect(planned.size).toBeGreaterThan(0);
|
|
134
|
+
|
|
135
|
+
const realDir = freshCwd();
|
|
136
|
+
expect((await runIn(realDir, params)).exitCode).toBe(0);
|
|
137
|
+
for (const [relative, bytes] of planned) {
|
|
138
|
+
expect({
|
|
139
|
+
file: relative,
|
|
140
|
+
bytes: readFileSync(join(realDir, relative), "utf-8").length,
|
|
141
|
+
}).toEqual({ file: relative, bytes });
|
|
142
|
+
}
|
|
143
|
+
// Same set of files, not just the same counts for the ones checked.
|
|
144
|
+
expect(walk(realDir).sort()).toEqual([...planned.keys()].sort());
|
|
145
|
+
});
|
|
146
|
+
|
|
104
147
|
it("--undo reverses a run", async () => {
|
|
105
148
|
const dir = freshCwd();
|
|
106
149
|
await runIn(dir, {
|
|
@@ -123,7 +166,9 @@ describe("create — real generation + stamp", () => {
|
|
|
123
166
|
);
|
|
124
167
|
expect(outcome.exitCode).toBe(0);
|
|
125
168
|
expect(outcome.stdout).toMatch(/Undid \d+ step/);
|
|
126
|
-
|
|
169
|
+
// TWO real runs (generate, then undo) — the same load-sensitivity as the
|
|
170
|
+
// stamp test above, doubled; same explicit ceiling.
|
|
171
|
+
}, 60_000);
|
|
127
172
|
|
|
128
173
|
it("rejects an unknown framework with INVALID_INPUT", async () => {
|
|
129
174
|
const dir = freshCwd();
|
|
@@ -131,7 +176,7 @@ describe("create — real generation + stamp", () => {
|
|
|
131
176
|
// framework param to exercise pickGenerator's own guard too.
|
|
132
177
|
const runtime = bootRuntime(FLAGS, dir);
|
|
133
178
|
await expect(
|
|
134
|
-
|
|
179
|
+
createVerbs.component.run({ framework: "vue" }, {
|
|
135
180
|
...runtime,
|
|
136
181
|
interaction: { isTTY: false, transport: "cli", yes: true },
|
|
137
182
|
} as never),
|
|
@@ -217,9 +262,19 @@ describe("lazy-React discipline (PROTECTED)", () => {
|
|
|
217
262
|
"@canonical/summon-component",
|
|
218
263
|
"@canonical/summon-package",
|
|
219
264
|
"@canonical/summon-application",
|
|
265
|
+
// The generators pull summon-core, and summon-core is what costs the
|
|
266
|
+
// fast paths (+43 MB RSS once it loads). Dropping the `type` from one of
|
|
267
|
+
// the four `import type … from "@canonical/summon-core"` lines in this
|
|
268
|
+
// directory is a one-word regression nothing else catches.
|
|
269
|
+
"@canonical/summon-core",
|
|
220
270
|
];
|
|
221
271
|
for (const file of graph) {
|
|
222
|
-
|
|
272
|
+
// Type-only imports are erased before runtime, so they load nothing —
|
|
273
|
+
// strip them before asking what the file actually pulls in.
|
|
274
|
+
const src = readFileSync(file, "utf-8").replace(
|
|
275
|
+
/\bimport\s+type\b[^;]*;/g,
|
|
276
|
+
"",
|
|
277
|
+
);
|
|
223
278
|
for (const pkg of forbidden) {
|
|
224
279
|
const esc = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
225
280
|
expect(
|
|
@@ -285,7 +340,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
285
340
|
(generators as Record<string, { prompts: unknown[] }>)["component/react"]
|
|
286
341
|
.prompts as never,
|
|
287
342
|
);
|
|
288
|
-
const verb =
|
|
343
|
+
const verb = createVerbs.component.params;
|
|
289
344
|
|
|
290
345
|
// The verb SYNTHESIZES a `framework` enum (the 3 framework generators
|
|
291
346
|
// collapse to one verb): react|svelte|lit, default react — no generator
|
|
@@ -328,7 +383,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
328
383
|
.prompts as never,
|
|
329
384
|
);
|
|
330
385
|
// The package verb's grammar IS generatorToParams(PACKAGE_MIRROR).
|
|
331
|
-
expect(behavioural(
|
|
386
|
+
expect(behavioural(createVerbs.package.params)).toEqual(
|
|
332
387
|
behavioural(applyRunInstallOverride(real)),
|
|
333
388
|
);
|
|
334
389
|
|
|
@@ -336,7 +391,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
336
391
|
const realRunInstall = real.find((p) => p.name === "runInstall") as
|
|
337
392
|
| Record<string, unknown>
|
|
338
393
|
| undefined;
|
|
339
|
-
const mirrorRunInstall =
|
|
394
|
+
const mirrorRunInstall = createVerbs.package.params.find(
|
|
340
395
|
(p) => p.name === "runInstall",
|
|
341
396
|
) as Record<string, unknown> | undefined;
|
|
342
397
|
expect(realRunInstall?.default).toBe(true);
|
|
@@ -375,14 +430,14 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
375
430
|
// generator's `ssr`/`router`/`forms`.
|
|
376
431
|
expect(
|
|
377
432
|
behavioural(
|
|
378
|
-
toGeneratorNames("application",
|
|
433
|
+
toGeneratorNames("application", createVerbs.application.params),
|
|
379
434
|
),
|
|
380
435
|
).toEqual(behavioural(applyRunInstallOverride(real)));
|
|
381
436
|
|
|
382
437
|
// Guard the B8 rename itself: ALL application include-flags ARE on the
|
|
383
438
|
// `--with-X` convention (params `withSsr`/`withRouter`/`withForms`/
|
|
384
439
|
// `withRelay`), not the bare `ssr`/`router`/`forms`/`relay` booleans.
|
|
385
|
-
const names =
|
|
440
|
+
const names = createVerbs.application.params.map((p) => p.name);
|
|
386
441
|
expect(names).toContain("withSsr");
|
|
387
442
|
expect(names).toContain("withRouter");
|
|
388
443
|
expect(names).toContain("withForms");
|
|
@@ -393,7 +448,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
393
448
|
const realRunInstall = real.find((p) => p.name === "runInstall") as
|
|
394
449
|
| Record<string, unknown>
|
|
395
450
|
| undefined;
|
|
396
|
-
const mirrorRunInstall =
|
|
451
|
+
const mirrorRunInstall = createVerbs.application.params.find(
|
|
397
452
|
(p) => p.name === "runInstall",
|
|
398
453
|
) as Record<string, unknown> | undefined;
|
|
399
454
|
expect(realRunInstall?.default).toBe(true);
|
|
@@ -401,7 +456,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
401
456
|
|
|
402
457
|
// appPath is the positional entry point (its default carries across).
|
|
403
458
|
expect(
|
|
404
|
-
|
|
459
|
+
createVerbs.application.params.find((p) => p.name === "appPath"),
|
|
405
460
|
).toMatchObject({ kind: "string", positional: true, default: "my-app" });
|
|
406
461
|
});
|
|
407
462
|
});
|
|
@@ -430,3 +485,95 @@ describe("compiled-binary create gate (M4)", () => {
|
|
|
430
485
|
expect(isModuleNotFound("nope")).toBe(false);
|
|
431
486
|
});
|
|
432
487
|
});
|
|
488
|
+
|
|
489
|
+
describe("declared generator bindings (PROTECTED)", () => {
|
|
490
|
+
// CREATE_GENERATORS is the one place the create surface's generator facts are
|
|
491
|
+
// written down. What is checkable against something outside itself — the live
|
|
492
|
+
// generator maps and the embedded manifest — is checked here.
|
|
493
|
+
|
|
494
|
+
it("every binding resolves to the generator it names", async () => {
|
|
495
|
+
const { pickGenerator } = await import("./pickGenerator.js");
|
|
496
|
+
// `meta.name` is the generator's OWN identity, so a swapped `key` or a
|
|
497
|
+
// framework the map does not carry turns this red — which `toBeDefined()`
|
|
498
|
+
// on a non-nullable return could not.
|
|
499
|
+
for (const framework of CREATE_GENERATORS.component.frameworks) {
|
|
500
|
+
expect(pickGenerator("component", { framework }).meta.name).toBe(
|
|
501
|
+
`component/${framework}`,
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
expect(pickGenerator("package", {}).meta.name).toBe("package");
|
|
505
|
+
expect(pickGenerator("application", {}).meta.name).toBe(
|
|
506
|
+
"application/react",
|
|
507
|
+
);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it("exactly the two source-run-only verbs publish the availability caveat", () => {
|
|
511
|
+
// A LITERAL list, for the same reason `compiledCreate.subprocess.test.ts`
|
|
512
|
+
// uses one: the caveat is DERIVED from `readsEmbeddedTemplates`, so an
|
|
513
|
+
// assertion derived from the same bit would agree with itself no matter
|
|
514
|
+
// which way the bit was flipped. These two are the nouns that test proves
|
|
515
|
+
// refuse on a real compiled binary, so this is what the reference is held
|
|
516
|
+
// to — and flipping the bit turns this red, along with the reference
|
|
517
|
+
// drift-guard, in the same run.
|
|
518
|
+
const caveated = createModule.verbs
|
|
519
|
+
.filter((verb) => verb.summary.includes("Source-run only."))
|
|
520
|
+
.map((verb) => verb.path.at(1));
|
|
521
|
+
expect(caveated).toEqual(["package", "application"]);
|
|
522
|
+
for (const verb of createModule.verbs) {
|
|
523
|
+
const kind = verb.path.at(1);
|
|
524
|
+
const gated = caveated.includes(kind);
|
|
525
|
+
// The `doc` is what `tools.md` and the MCP tool description render, so
|
|
526
|
+
// the explanation has to travel with the marker, and it quotes the
|
|
527
|
+
// command it is about — a copy-paste between bindings is then visible.
|
|
528
|
+
expect(verb.doc?.includes("UNSUPPORTED") ?? false, kind).toBe(gated);
|
|
529
|
+
if (gated) {
|
|
530
|
+
expect(verb.doc).toContain(`create ${kind}`);
|
|
531
|
+
// And it still says what the verb MAKES. `doc ?? summary` means a doc
|
|
532
|
+
// REPLACES the summary on the MCP surface, so a caveat alone left an
|
|
533
|
+
// agent no statement anywhere of what `create_package` scaffolds.
|
|
534
|
+
expect(verb.doc).toContain(
|
|
535
|
+
verb.summary.replace(" Source-run only.", ""),
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("the --framework enum offers the declared frameworks and defaults to one of them", () => {
|
|
542
|
+
const framework = createVerbs.component.params.find(
|
|
543
|
+
(p) => p.name === "framework",
|
|
544
|
+
);
|
|
545
|
+
// A `values: []` enum with a `default` would leave every `--framework`
|
|
546
|
+
// rejected while `--help` still advertises a default — the hazard this names.
|
|
547
|
+
// (That every value resolves is the test above.)
|
|
548
|
+
expect(framework?.kind).toBe("enum");
|
|
549
|
+
const values = (framework as { values: readonly string[] }).values;
|
|
550
|
+
expect(values.length).toBeGreaterThan(0);
|
|
551
|
+
expect(values).toContain(framework?.default);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it("embeds only the templates the component loader can key", async () => {
|
|
555
|
+
const { TEMPLATES } = await import("./templates.embedded.generated.js");
|
|
556
|
+
const keys = Object.keys(TEMPLATES);
|
|
557
|
+
// summon-component's `qualifiedKey()` prefixes EVERY lookup with
|
|
558
|
+
// `component/`, so any other entry ships dead weight the binary can never
|
|
559
|
+
// read — and a binding embedded without a manifest-reading generator would
|
|
560
|
+
// put one here. Non-empty first: `.every()` over an empty list is vacuous.
|
|
561
|
+
expect(keys.length).toBeGreaterThan(0);
|
|
562
|
+
expect(keys.filter((k) => !k.startsWith("component/"))).toEqual([]);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("create surfaces exactly the three declared nouns", () => {
|
|
566
|
+
// A LITERAL surface pin, deliberately not derived from CREATE_GENERATORS:
|
|
567
|
+
// surfacing a noun also needs a hand-written prompt mirror, path param and
|
|
568
|
+
// examples in create.verb.ts, so the surface is a deliberate SUBSET of what
|
|
569
|
+
// the declared packages ship (`@canonical/summon-application` also ships
|
|
570
|
+
// `domain`, `route` and `wrapper`). Pinning OUR three nouns rather than any
|
|
571
|
+
// third-party package's generator list keeps an upstream release from
|
|
572
|
+
// turning this red.
|
|
573
|
+
expect(createModule.verbs.map((v) => v.path[1])).toEqual([
|
|
574
|
+
"component",
|
|
575
|
+
"package",
|
|
576
|
+
"application",
|
|
577
|
+
]);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The `create` verbs — `component` / `package` / `application` (path capped at
|
|
3
|
-
* two segments; the
|
|
3
|
+
* two segments; the declared component frameworks collapse to one verb + a
|
|
4
4
|
* `--framework` enum). Each is a mutating, interactive, storeless verb.
|
|
5
5
|
*
|
|
6
6
|
* LAZY DISPATCH (R9 + lazy-React): the params are STATIC — built by the
|
|
7
7
|
* generator→grammar adapter over static prompt MIRRORS, never the live
|
|
8
|
-
* generators (importing a generator
|
|
8
|
+
* generators (importing a generator pulls summon-core, and with it React). The
|
|
9
9
|
* `run` body lazily `import()`s `pickGenerator` + summon-core, so `buildProgram`
|
|
10
10
|
* / `--help` / `__complete` / reads never load summon-core or the generators —
|
|
11
11
|
* and `create --yes` never loads React (the Ink UI is dynamic-only, and only the
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
|
|
16
16
|
import type { GeneratorResult, PromptDefinition } from "@canonical/summon-core";
|
|
17
17
|
import type { Task } from "@canonical/task";
|
|
18
|
+
import { BIN_NAME } from "../../constants.js";
|
|
18
19
|
import { PragmaError } from "../../kernel/error/PragmaError.js";
|
|
19
20
|
import type { PragmaRuntime } from "../../kernel/runtime/types.js";
|
|
20
21
|
import type { ParamSpec, VerbSpec } from "../../kernel/spec/types.js";
|
|
22
|
+
import { CREATE_GENERATORS } from "./constants.js";
|
|
21
23
|
import { createFormatters } from "./create.render.js";
|
|
22
24
|
import { generatorToParams } from "./generatorToVerbSpec.js";
|
|
23
25
|
import { assertInsideWorkspace } from "./pathJail.js";
|
|
@@ -27,13 +29,14 @@ import type { CreateKind } from "./types.js";
|
|
|
27
29
|
// Static params (mirrors of the generators' prompts — see the module doc)
|
|
28
30
|
// =============================================================================
|
|
29
31
|
|
|
30
|
-
/** `--framework` — the
|
|
32
|
+
/** `--framework` — the declared component generators collapsed to one enum. */
|
|
31
33
|
const FRAMEWORK_PARAM: ParamSpec = {
|
|
32
34
|
kind: "enum",
|
|
33
35
|
name: "framework",
|
|
34
36
|
doc: "Component framework.",
|
|
35
|
-
values:
|
|
36
|
-
default
|
|
37
|
+
values: CREATE_GENERATORS.component.frameworks,
|
|
38
|
+
// The FIRST declared framework, so the default is always inside the enum.
|
|
39
|
+
default: CREATE_GENERATORS.component.frameworks[0],
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
/**
|
|
@@ -234,20 +237,16 @@ const PATH_PARAM: Record<CreateKind, string | undefined> = {
|
|
|
234
237
|
// The lazy run — the one-line summon↔pragma seam per invocation
|
|
235
238
|
// =============================================================================
|
|
236
239
|
|
|
237
|
-
/**
|
|
238
|
-
* Build the `create` Task for one invocation: pick the generator, jail its
|
|
239
|
-
* output path, pre-validate flag/arg answers, pick the prompt strategy against
|
|
240
|
-
* the interaction context, wire `runtime.exec`, and return `execute`.
|
|
241
|
-
*
|
|
242
|
-
* @returns A `Promise<Task<GeneratorResult>>` (the union's third arm) the
|
|
243
|
-
* dispatcher/MCP handler awaits into a Task before interpreting.
|
|
244
|
-
*/
|
|
245
240
|
/**
|
|
246
241
|
* The standalone `bun build --compile` binary resolves every bundled module
|
|
247
242
|
* under the virtual `/$bunfs` filesystem — a marker absent from any source or
|
|
248
|
-
* `bun`-run module URL.
|
|
249
|
-
*
|
|
250
|
-
*
|
|
243
|
+
* `bun`-run module URL. Only a generator that routes its template reads through
|
|
244
|
+
* the embedded manifest can run there
|
|
245
|
+
* (`CREATE_GENERATORS[kind].readsEmbeddedTemplates`);
|
|
246
|
+
* the others call `template({ source })`, which falls through to
|
|
247
|
+
* `readFile(source)` and dies with `ENOENT … /$bunfs/…` after `mkdir` has
|
|
248
|
+
* already run, leaving partially-created files behind.
|
|
249
|
+
* `compiledCreate.subprocess.test.ts` pins the refusal AND the empty cwd.
|
|
251
250
|
*/
|
|
252
251
|
const IS_COMPILED_BINARY = import.meta.url.includes("/$bunfs/");
|
|
253
252
|
|
|
@@ -281,23 +280,22 @@ export function isModuleNotFound(cause: unknown): boolean {
|
|
|
281
280
|
* `--compile` bundler includes them — they stay behind this lazy boundary, so
|
|
282
281
|
* the fast paths and `create --yes` still load neither summon-core nor React).
|
|
283
282
|
*
|
|
284
|
-
* The manifest is injected BEFORE `pickGenerator` —
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
283
|
+
* The manifest is injected BEFORE `pickGenerator` — the component loader
|
|
284
|
+
* consults it when its disk read fails (the compiled binary), and the generators
|
|
285
|
+
* load their templates on first `generate()`. It is imported from
|
|
286
|
+
* summon-component's `loadTemplate` submodule so this does not evaluate the
|
|
287
|
+
* generator index first. In a source run the disk read wins and the manifest is
|
|
288
|
+
* inert.
|
|
290
289
|
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* {@link isModuleNotFound} turns into the same clean gate.
|
|
290
|
+
* A binding whose generator does not read through that manifest is gated to a
|
|
291
|
+
* source run here ({@link IS_COMPILED_BINARY}). A stale resolution failure is a
|
|
292
|
+
* defensive backstop {@link isModuleNotFound} turns into the same clean gate.
|
|
295
293
|
*/
|
|
296
294
|
async function loadCreateRuntime(kind: CreateKind) {
|
|
297
|
-
if (IS_COMPILED_BINARY && kind
|
|
295
|
+
if (IS_COMPILED_BINARY && !CREATE_GENERATORS[kind].readsEmbeddedTemplates) {
|
|
298
296
|
throw new PragmaError({
|
|
299
297
|
code: "UNSUPPORTED",
|
|
300
|
-
message: `\`create ${kind}\` is not available in the compiled
|
|
298
|
+
message: `\`create ${kind}\` is not available in the compiled ${BIN_NAME} binary — ${SOURCE_ONLY_REASON}. Run it from a source checkout, or use the \`summon\` CLI.`,
|
|
301
299
|
recovery: {
|
|
302
300
|
message: `Run \`create ${kind}\` from a source checkout, or use \`summon\`.`,
|
|
303
301
|
},
|
|
@@ -331,15 +329,26 @@ async function loadCreateRuntime(kind: CreateKind) {
|
|
|
331
329
|
}
|
|
332
330
|
}
|
|
333
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Build the `create` Task for one invocation: pick the generator, jail its
|
|
334
|
+
* output path, pre-validate flag/arg answers, pick the prompt strategy against
|
|
335
|
+
* the interaction context, wire `runtime.exec`, and return `execute`.
|
|
336
|
+
*
|
|
337
|
+
* @param kind - The create noun.
|
|
338
|
+
* @param params - The coerced params for this invocation.
|
|
339
|
+
* @param rt - The pragma runtime.
|
|
340
|
+
* @returns A `Promise<Task<GeneratorResult>>` (the union's third arm) the
|
|
341
|
+
* dispatcher/MCP handler awaits into a Task before interpreting.
|
|
342
|
+
*/
|
|
334
343
|
async function runCreate(
|
|
335
344
|
kind: CreateKind,
|
|
336
345
|
params: Record<string, unknown>,
|
|
337
346
|
rt: PragmaRuntime,
|
|
338
347
|
): Promise<Task<GeneratorResult>> {
|
|
339
|
-
// Lazy: importing these
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
348
|
+
// Lazy: importing these pulls summon-core (and with it React) — kept off every
|
|
349
|
+
// non-create path. Now STATIC dynamic imports so `--compile` bundles them; the
|
|
350
|
+
// embedded `.ejs` manifest is injected here. A binding that does not read
|
|
351
|
+
// through it stays a source-run feature in the binary.
|
|
343
352
|
const { pickGenerator, summon } = await loadCreateRuntime(kind);
|
|
344
353
|
|
|
345
354
|
// Normalize the CLI/MCP `--with-X` include-flags to the generator prompt names
|
|
@@ -374,7 +383,13 @@ async function runCreate(
|
|
|
374
383
|
// `onCancel` (H2): an in-Ink Ctrl-C during execution aborts the run so the
|
|
375
384
|
// interpreter stops writing — raw mode swallows the SIGINT, so the wizard
|
|
376
385
|
// drives the abort. Shared with setup by construction.
|
|
377
|
-
|
|
386
|
+
// `cwd` also reaches the wizard: its confirm-gate preview reads the same
|
|
387
|
+
// jail-checked write root the interpreter resolves effect paths against.
|
|
388
|
+
const session = summon.inkPrompt(generator, {
|
|
389
|
+
signal,
|
|
390
|
+
onCancel: abort,
|
|
391
|
+
cwd: rt.cwd,
|
|
392
|
+
});
|
|
378
393
|
// Thread the per-call write root: the interpreter resolves the generator's
|
|
379
394
|
// relative output paths against `rt.cwd` — the SAME dir the SEC-2 jail
|
|
380
395
|
// validated above — so the write can never escape the checked directory.
|
|
@@ -433,10 +448,51 @@ const CREATE_CAPABILITY = {
|
|
|
433
448
|
},
|
|
434
449
|
};
|
|
435
450
|
|
|
451
|
+
/**
|
|
452
|
+
* Why a binding is source-run only — the ONE authoring of it, shared by the
|
|
453
|
+
* documentation below and by {@link loadCreateRuntime}'s `UNSUPPORTED` refusal,
|
|
454
|
+
* so what the reference promises and what the binary does cannot disagree.
|
|
455
|
+
*/
|
|
456
|
+
const SOURCE_ONLY_REASON =
|
|
457
|
+
"its generator reads templates from disk, which the binary does not carry";
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* The published caveat for a binding the compiled binary cannot run. DERIVED
|
|
461
|
+
* from `CREATE_GENERATORS[kind].readsEmbeddedTemplates` — flipping that bit
|
|
462
|
+
* moves the caveat, `docs/reference/*.md`, and `create.test.ts`'s binding
|
|
463
|
+
* assertion together.
|
|
464
|
+
*
|
|
465
|
+
* It LEADS WITH THE PURPOSE, because `tools.md` and the MCP tool description
|
|
466
|
+
* both render `doc ?? summary` — so a caveat alone REPLACED the only sentence
|
|
467
|
+
* saying what the tool makes, and an agent enumerating tools could not learn
|
|
468
|
+
* that `create_package` scaffolds a package. The tool works from a source
|
|
469
|
+
* checkout; describing nothing but its refusal made a live tool
|
|
470
|
+
* undiscoverable. The repeated sentence in `commands.md`'s section is the
|
|
471
|
+
* cheaper of the two costs.
|
|
472
|
+
*
|
|
473
|
+
* It names no CLI flag (`mcp/toolDescriptions.test.ts` forbids one, and an
|
|
474
|
+
* agent has no flags): the plan-only refusal is stated in the terms BOTH
|
|
475
|
+
* surfaces share.
|
|
476
|
+
*
|
|
477
|
+
* @param kind - The create binding.
|
|
478
|
+
* @param summary - The verb's own one-liner, which the `doc` replaces on MCP.
|
|
479
|
+
* @returns The `doc` for a source-run-only binding, or `undefined`.
|
|
480
|
+
*/
|
|
481
|
+
function buildAvailabilityDoc(
|
|
482
|
+
kind: CreateKind,
|
|
483
|
+
summary: string,
|
|
484
|
+
): string | undefined {
|
|
485
|
+
if (CREATE_GENERATORS[kind].readsEmbeddedTemplates) return undefined;
|
|
486
|
+
return `${summary} From the compiled ${BIN_NAME} binary, \`create ${kind}\` refuses with \`UNSUPPORTED\` and writes nothing. Asking it only to PLAN refuses too — the gate runs while the plan is built — so a successful plan is never evidence it would run. The cause is that ${SOURCE_ONLY_REASON}. Run it from a source checkout, or use the \`summon\` CLI.`;
|
|
487
|
+
}
|
|
488
|
+
|
|
436
489
|
/**
|
|
437
490
|
* Build a create verb. `run` presents `Promise<Task<R>>` through the `Task<R>`
|
|
438
491
|
* arm by an honest cast at this one site (mirroring `sources update`): a literal
|
|
439
492
|
* `Promise<Task<R>>` arm in the union would poison async read-verb inference.
|
|
493
|
+
*
|
|
494
|
+
* A binding the compiled binary cannot run says so in its `summary` (which is
|
|
495
|
+
* what `--help` and the noun listing show) and explains itself in its `doc`.
|
|
440
496
|
*/
|
|
441
497
|
function createVerb(
|
|
442
498
|
kind: CreateKind,
|
|
@@ -444,9 +500,11 @@ function createVerb(
|
|
|
444
500
|
params: ParamSpec[],
|
|
445
501
|
examples: VerbSpec["examples"],
|
|
446
502
|
): VerbSpec<Record<string, unknown>, GeneratorResult> {
|
|
503
|
+
const doc = buildAvailabilityDoc(kind, summary);
|
|
447
504
|
return {
|
|
448
505
|
path: ["create", kind],
|
|
449
|
-
summary,
|
|
506
|
+
summary: doc ? `${summary} Source-run only.` : summary,
|
|
507
|
+
...(doc ? { doc } : {}),
|
|
450
508
|
params,
|
|
451
509
|
output: { formatters: createFormatters },
|
|
452
510
|
examples,
|
|
@@ -456,38 +514,51 @@ function createVerb(
|
|
|
456
514
|
};
|
|
457
515
|
}
|
|
458
516
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
517
|
+
/**
|
|
518
|
+
* The `create` verbs, one per declared generator binding — so adding a binding
|
|
519
|
+
* to {@link CREATE_GENERATORS} without surfacing it is a type error rather than
|
|
520
|
+
* a silent no-op. Key order is authoring order, which `index.ts` preserves into
|
|
521
|
+
* the command tree, `--help` and the emitted surface.
|
|
522
|
+
*/
|
|
523
|
+
export const createVerbs: Record<
|
|
524
|
+
CreateKind,
|
|
525
|
+
VerbSpec<Record<string, unknown>, GeneratorResult>
|
|
526
|
+
> = {
|
|
527
|
+
component: createVerb(
|
|
528
|
+
"component",
|
|
529
|
+
"Scaffold a React, Svelte, or Lit component.",
|
|
530
|
+
componentParams,
|
|
531
|
+
[
|
|
532
|
+
{
|
|
533
|
+
cmd: `${BIN_NAME} create component src/components/Button --framework react`,
|
|
534
|
+
note: "React component with tests, stories, and styles",
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
cmd: `${BIN_NAME} create component src/lib/Card --framework svelte --dry-run`,
|
|
538
|
+
note: "preview the files without writing",
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
),
|
|
542
|
+
package: createVerb(
|
|
543
|
+
"package",
|
|
544
|
+
"Scaffold a new npm package for the monorepo.",
|
|
545
|
+
packageParams,
|
|
546
|
+
[
|
|
547
|
+
{
|
|
548
|
+
cmd: `${BIN_NAME} create package --name @canonical/my-lib --type library`,
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
cmd: `${BIN_NAME} create package --name @canonical/my-tool --run-install`,
|
|
552
|
+
},
|
|
553
|
+
],
|
|
554
|
+
),
|
|
555
|
+
application: createVerb(
|
|
556
|
+
"application",
|
|
557
|
+
"Scaffold a full React application with SSR and routing.",
|
|
558
|
+
applicationParams,
|
|
559
|
+
[
|
|
560
|
+
{ cmd: `${BIN_NAME} create application my-app` },
|
|
561
|
+
{ cmd: `${BIN_NAME} create application my-app --with-relay` },
|
|
562
|
+
],
|
|
563
|
+
),
|
|
564
|
+
};
|
|
@@ -8,18 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import { asVerb } from "../../kernel/spec/asVerb.js";
|
|
10
10
|
import type { CapabilityModule } from "../../kernel/spec/types.js";
|
|
11
|
-
import {
|
|
12
|
-
createApplicationVerb,
|
|
13
|
-
createComponentVerb,
|
|
14
|
-
createPackageVerb,
|
|
15
|
-
} from "./create.verb.js";
|
|
11
|
+
import { createVerbs } from "./create.verb.js";
|
|
16
12
|
|
|
17
13
|
/** The `create` capability module. */
|
|
18
14
|
export const createModule: CapabilityModule = {
|
|
19
15
|
name: "create",
|
|
20
|
-
verbs:
|
|
21
|
-
asVerb(createComponentVerb),
|
|
22
|
-
asVerb(createPackageVerb),
|
|
23
|
-
asVerb(createApplicationVerb),
|
|
24
|
-
],
|
|
16
|
+
verbs: Object.values(createVerbs).map(asVerb),
|
|
25
17
|
};
|