@specverse/engines 5.0.2 → 5.2.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/assets/prompts/core/standard/default/analyse.prompt.yaml +5 -5
- package/assets/prompts/core/standard/default/app-demo.prompt.yaml +21 -1
- package/assets/prompts/core/standard/default/behavior.prompt.yaml +150 -0
- package/assets/prompts/core/standard/default/create.prompt.yaml +3 -3
- package/assets/prompts/core/standard/default/materialise.prompt.yaml +804 -774
- package/assets/prompts/core/standard/default/realize.prompt.yaml +581 -544
- package/assets/prompts/core/standard/v9/analyse.prompt.yaml +5 -5
- package/assets/prompts/core/standard/v9/app-demo.prompt.yaml +233 -0
- package/assets/prompts/core/standard/v9/behavior.prompt.yaml +33 -9
- package/assets/prompts/core/standard/v9/create.prompt.yaml +3 -3
- package/assets/prompts/core/standard/v9/materialise.prompt.yaml +804 -774
- package/assets/prompts/core/standard/v9/realize.prompt.yaml +581 -544
- package/dist/ai/commands/fill.d.ts.map +1 -1
- package/dist/ai/commands/fill.js +16 -7
- package/dist/ai/commands/fill.js.map +1 -1
- package/dist/ai/commands/template.d.ts.map +1 -1
- package/dist/ai/commands/template.js +17 -8
- package/dist/ai/commands/template.js.map +1 -1
- package/dist/bundles/deriveCatalog.d.ts +18 -0
- package/dist/bundles/deriveCatalog.d.ts.map +1 -0
- package/dist/bundles/deriveCatalog.js +263 -0
- package/dist/bundles/deriveCatalog.js.map +1 -0
- package/dist/bundles/index.d.ts +15 -0
- package/dist/bundles/index.d.ts.map +1 -0
- package/dist/bundles/index.js +15 -0
- package/dist/bundles/index.js.map +1 -0
- package/dist/bundles/types.d.ts +53 -0
- package/dist/bundles/types.d.ts.map +1 -0
- package/dist/bundles/types.js +22 -0
- package/dist/bundles/types.js.map +1 -0
- package/dist/bundles/validate.d.ts +55 -0
- package/dist/bundles/validate.d.ts.map +1 -0
- package/dist/bundles/validate.js +471 -0
- package/dist/bundles/validate.js.map +1 -0
- package/dist/inference/quint-transpiler.js +2 -2
- package/dist/inference/quint-transpiler.js.map +1 -1
- package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +1 -1
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +227 -0
- package/dist/libs/instance-factories/tools/templates/mcp/mcp-server-generator.js +295 -14
- package/libs/instance-factories/applications/templates/react/runtime-package-json-generator.ts +1 -1
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +227 -0
- package/libs/instance-factories/tools/templates/mcp/mcp-server-generator.ts +328 -15
- package/package.json +9 -5
- package/assets/examples/09-api/ai-spec.yaml +0 -194
- package/assets/examples/09-api/converted.yaml +0 -95
- package/assets/examples/09-api/diagram-architecture.mmd +0 -10
- package/assets/examples/09-api/diagram-er.mmd +0 -10
- package/assets/examples/09-api/documentation.html +0 -104
- package/assets/examples/09-api/documentation.md +0 -95
- package/assets/examples/09-api/inferred-spec.yaml +0 -420
- package/assets/examples/09-api/openapi.json +0 -61
- package/assets/examples/10-api/README.md +0 -216
- package/assets/examples/10-api/ai-spec.yaml +0 -194
- package/assets/examples/10-api/converted.yaml +0 -96
- package/assets/examples/10-api/diagram-architecture.mmd +0 -10
- package/assets/examples/10-api/diagram-er.mmd +0 -10
- package/assets/examples/10-api/documentation.html +0 -104
- package/assets/examples/10-api/documentation.md +0 -95
- package/assets/examples/10-api/inferred-spec.yaml +0 -7
- package/assets/examples/10-api/metadata.yaml +0 -89
- package/assets/examples/10-api/openapi.json +0 -61
- package/assets/examples/10-api/package-integration-test.js +0 -177
- package/assets/examples/10-api/usage-example.js +0 -323
- package/assets/examples/10-api/usage-example.ts +0 -363
- package/assets/examples/10-api/workflow-test.js +0 -113
- package/assets/examples/validate-examples-with-expected-failures.cjs +0 -328
- package/assets/examples/validate-examples.cjs +0 -225
- package/assets/prompts/MOVED.md +0 -35
- package/assets/prompts/SUMMARY-v8-PROMOTION.md +0 -445
- package/assets/prompts/core/CHANGELOG.md +0 -158
- package/assets/prompts/core/MIGRATION-v6-to-v7.md +0 -379
- package/assets/prompts/core/base-terminal-prompt.md +0 -201
- package/assets/prompts/core/examples/example-usage.ts +0 -140
- package/assets/prompts/core/schemas/prompt.schema.json +0 -309
- package/assets/prompts/core/schemas/prompt.schema.yaml +0 -229
- package/assets/prompts/core/standard/archive/v1/analyse.prompt.yaml +0 -259
- package/assets/prompts/core/standard/archive/v1/create.prompt.yaml +0 -302
- package/assets/prompts/core/standard/archive/v1/materialise.prompt.yaml +0 -328
- package/assets/prompts/core/standard/archive/v1/realize.prompt.yaml +0 -606
- package/assets/prompts/core/standard/archive/v2/README.md +0 -110
- package/assets/prompts/core/standard/archive/v2/analyse.prompt.yaml +0 -151
- package/assets/prompts/core/standard/archive/v2/create.prompt.yaml +0 -151
- package/assets/prompts/core/standard/archive/v2/materialise.prompt.yaml +0 -132
- package/assets/prompts/core/standard/archive/v2/realize.prompt.yaml +0 -147
- package/assets/prompts/core/standard/archive/v3/README.md +0 -279
- package/assets/prompts/core/standard/archive/v3/analyse.prompt.yaml +0 -309
- package/assets/prompts/core/standard/archive/v3/create.prompt.yaml +0 -351
- package/assets/prompts/core/standard/archive/v3/materialise.prompt.yaml +0 -247
- package/assets/prompts/core/standard/archive/v3/realize.prompt.yaml +0 -344
- package/assets/prompts/core/standard/archive/v4/README.md +0 -79
- package/assets/prompts/core/standard/archive/v4/analyse.prompt.yaml +0 -204
- package/assets/prompts/core/standard/archive/v4/create.prompt.yaml +0 -185
- package/assets/prompts/core/standard/archive/v5/README.md +0 -224
- package/assets/prompts/core/standard/archive/v5/analyse.prompt.yaml +0 -209
- package/assets/prompts/core/standard/archive/v5/create.prompt.yaml +0 -225
- package/assets/prompts/core/standard/archive/v5/materialise.prompt.yaml +0 -242
- package/assets/prompts/core/standard/archive/v5/realize.prompt.yaml +0 -336
- package/assets/prompts/core/standard/archive/v6/README.md +0 -187
- package/assets/prompts/core/standard/archive/v6/analyse.prompt.yaml +0 -219
- package/assets/prompts/core/standard/archive/v6/create.prompt.yaml +0 -180
- package/assets/prompts/core/standard/archive/v6/materialise.prompt.yaml +0 -203
- package/assets/prompts/core/standard/archive/v6/realize.prompt.yaml +0 -215
- package/assets/prompts/core/standard/archive/v7/analyse.prompt.nick.yaml +0 -144
- package/assets/prompts/core/standard/archive/v7/analyse.prompt.old.yaml +0 -146
- package/assets/prompts/core/standard/archive/v7/analyse.prompt.yaml +0 -129
- package/assets/prompts/core/standard/archive/v7/create.prompt.yaml +0 -146
- package/assets/prompts/core/standard/archive/v7/materialise.prompt.yaml +0 -297
- package/assets/prompts/core/standard/archive/v7/realize.prompt.yaml +0 -294
- package/assets/prompts/core/standard/archive/v8/README.md +0 -400
- package/assets/prompts/core/standard/archive/v8/analyse.prompt.yaml +0 -185
- package/assets/prompts/core/standard/archive/v8/create.prompt.yaml +0 -203
- package/assets/prompts/core/standard/archive/v8/materialise.prompt.yaml +0 -297
- package/assets/prompts/core/standard/archive/v8/realize.prompt.yaml +0 -294
- package/assets/prompts/templates/api-orchestrator-template.yaml +0 -188
- package/assets/prompts/templates/claude-integration-template.md +0 -121
- package/assets/prompts/templates/terminal-prompt-template.md +0 -97
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { TemplateContext } from '@specverse/types';
|
|
15
|
-
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
16
|
-
import {
|
|
15
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
16
|
+
import { createRequire } from 'module';
|
|
17
|
+
import { dirname, join } from 'path';
|
|
18
|
+
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
17
20
|
|
|
18
21
|
export default function generateMCPServer(context: TemplateContext): string {
|
|
19
22
|
const { spec, outputDir } = context;
|
|
@@ -23,7 +26,14 @@ export default function generateMCPServer(context: TemplateContext): string {
|
|
|
23
26
|
if (!existsSync(srcDir)) mkdirSync(srcDir, { recursive: true });
|
|
24
27
|
|
|
25
28
|
const distribution = extractMCPDistribution(spec);
|
|
26
|
-
|
|
29
|
+
// Prefer @specverse/self's real version over the spec's stale component
|
|
30
|
+
// version label. Falls back to the spec + default.
|
|
31
|
+
const version =
|
|
32
|
+
distribution?.version ||
|
|
33
|
+
resolveSelfVersion() ||
|
|
34
|
+
spec?.metadata?.version ||
|
|
35
|
+
spec?.version ||
|
|
36
|
+
'5.1.0';
|
|
27
37
|
const description = distribution?.description
|
|
28
38
|
|| 'SpecVerse MCP server — exposes the specverse CLI as MCP tools and the live spec schema + docs as MCP resources.';
|
|
29
39
|
const displayName = distribution?.displayName || 'SpecVerse MCP';
|
|
@@ -34,10 +44,26 @@ export default function generateMCPServer(context: TemplateContext): string {
|
|
|
34
44
|
writeFileSync(join(mcpDir, 'tsconfig.json'), generateTsconfig());
|
|
35
45
|
writeFileSync(join(srcDir, 'server.ts'), generateServer(displayName, version));
|
|
36
46
|
writeFileSync(join(srcDir, 'cli-runner.ts'), generateCliRunner());
|
|
37
|
-
writeFileSync(join(srcDir, 'resources.ts'), generateResources());
|
|
47
|
+
writeFileSync(join(srcDir, 'resources.ts'), generateResources(cliCommands));
|
|
38
48
|
writeFileSync(join(srcDir, 'tools.ts'), generateTools(cliCommands));
|
|
49
|
+
writeFileSync(join(srcDir, 'prompts.ts'), generatePrompts());
|
|
50
|
+
|
|
51
|
+
return `MCP server generated in: ${mcpDir}\n ${cliCommands.length} tools, 5 resources, 6 prompts (derived from @specverse/engines/assets/prompts)`;
|
|
52
|
+
}
|
|
39
53
|
|
|
40
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Read the running @specverse/self install's version from its package.json so
|
|
56
|
+
* the MCP server reports a real version instead of a stale component label.
|
|
57
|
+
* Returns null if @specverse/self isn't resolvable (e.g. during in-repo tests).
|
|
58
|
+
*/
|
|
59
|
+
function resolveSelfVersion(): string | null {
|
|
60
|
+
try {
|
|
61
|
+
const pkgPath = require.resolve('@specverse/self/package.json');
|
|
62
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
63
|
+
return pkg.version || null;
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
41
67
|
}
|
|
42
68
|
|
|
43
69
|
// ─── spec extraction ───────────────────────────────────────────────────────
|
|
@@ -182,9 +208,13 @@ function generatePackageJson(version: string, description: string): string {
|
|
|
182
208
|
},
|
|
183
209
|
dependencies: {
|
|
184
210
|
'@modelcontextprotocol/sdk': '^1.17.4',
|
|
185
|
-
'@specverse/
|
|
211
|
+
'@specverse/engines': '^5.2.0',
|
|
212
|
+
'@specverse/entities': '^5.1.0',
|
|
213
|
+
'@specverse/self': '^5.2.0',
|
|
214
|
+
'js-yaml': '^4.1.0',
|
|
186
215
|
},
|
|
187
216
|
devDependencies: {
|
|
217
|
+
'@types/js-yaml': '^4.0.9',
|
|
188
218
|
'@types/node': '^20.19.11',
|
|
189
219
|
typescript: '^5.9.2',
|
|
190
220
|
},
|
|
@@ -221,13 +251,15 @@ function generateTsconfig(): string {
|
|
|
221
251
|
}
|
|
222
252
|
|
|
223
253
|
function generateServer(displayName: string, version: string): string {
|
|
254
|
+
const instructions = SERVER_INSTRUCTIONS;
|
|
224
255
|
return `#!/usr/bin/env node
|
|
225
256
|
/**
|
|
226
257
|
* SpecVerse MCP Server — stdio transport.
|
|
227
258
|
*
|
|
228
|
-
* Wires the MCP protocol to the generated tool registry + live resources
|
|
229
|
-
*
|
|
230
|
-
* (tools from CLI commands
|
|
259
|
+
* Wires the MCP protocol to the generated tool registry + live resources
|
|
260
|
+
* + canonical workflow prompts. Everything is derived from the spec
|
|
261
|
+
* (tools from CLI commands, resources from @specverse/entities + @specverse/self,
|
|
262
|
+
* prompts from @specverse/engines/assets/prompts/core/standard/default).
|
|
231
263
|
*/
|
|
232
264
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
233
265
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -236,13 +268,21 @@ import {
|
|
|
236
268
|
CallToolRequestSchema,
|
|
237
269
|
ListResourcesRequestSchema,
|
|
238
270
|
ReadResourceRequestSchema,
|
|
271
|
+
ListPromptsRequestSchema,
|
|
272
|
+
GetPromptRequestSchema,
|
|
239
273
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
240
274
|
import { TOOLS, callTool } from './tools.js';
|
|
241
275
|
import { RESOURCES, readResource } from './resources.js';
|
|
276
|
+
import { PROMPTS, getPrompt } from './prompts.js';
|
|
277
|
+
|
|
278
|
+
const INSTRUCTIONS = ${JSON.stringify(instructions)};
|
|
242
279
|
|
|
243
280
|
const server = new Server(
|
|
244
281
|
{ name: ${JSON.stringify(displayName)}, version: ${JSON.stringify(version)} },
|
|
245
|
-
{
|
|
282
|
+
{
|
|
283
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
284
|
+
instructions: INSTRUCTIONS,
|
|
285
|
+
},
|
|
246
286
|
);
|
|
247
287
|
|
|
248
288
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
@@ -271,11 +311,52 @@ server.setRequestHandler(ReadResourceRequestSchema, async (req: { params: { uri:
|
|
|
271
311
|
return readResource(req.params.uri);
|
|
272
312
|
});
|
|
273
313
|
|
|
314
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
315
|
+
prompts: PROMPTS.map(p => ({
|
|
316
|
+
name: p.name,
|
|
317
|
+
description: p.description,
|
|
318
|
+
arguments: p.arguments,
|
|
319
|
+
})),
|
|
320
|
+
}));
|
|
321
|
+
|
|
322
|
+
server.setRequestHandler(GetPromptRequestSchema, async (req: { params: { name: string; arguments?: Record<string, string> } }) => {
|
|
323
|
+
const { name, arguments: args } = req.params;
|
|
324
|
+
return getPrompt(name, args ?? {});
|
|
325
|
+
});
|
|
326
|
+
|
|
274
327
|
const transport = new StdioServerTransport();
|
|
275
328
|
await server.connect(transport);
|
|
276
329
|
`;
|
|
277
330
|
}
|
|
278
331
|
|
|
332
|
+
const SERVER_INSTRUCTIONS = [
|
|
333
|
+
'SpecVerse is a declarative specification language. You describe WHAT a system does in a .specly file, and the engines generate HOW — backend (Fastify/Prisma), frontend (React/Tailwind), CLI, tools, diagrams.',
|
|
334
|
+
'',
|
|
335
|
+
'Before using tools, read these resources to ground yourself in the language:',
|
|
336
|
+
'- specverse://guide — canonical user guide (spec language, CLI reference, convention patterns)',
|
|
337
|
+
'- specverse://ai-guidance — curated hints for generating valid specs',
|
|
338
|
+
'- specverse://minimal-example — one minimal .specly showing every feature in ~100 lines',
|
|
339
|
+
'- specverse://schema — the JSON Schema (draft 2020-12) for .specly validation',
|
|
340
|
+
'- specverse://cli-reference — every spv CLI subcommand with flags + arguments',
|
|
341
|
+
'',
|
|
342
|
+
'Common high-leverage workflows are exposed as MCP prompts (preferred entry point):',
|
|
343
|
+
'- create: natural-language requirements → minimal .specly',
|
|
344
|
+
'- analyse: existing codebase → .specly that captures what is implemented',
|
|
345
|
+
'- materialise: .specly → production code',
|
|
346
|
+
'- realize: generate deployment configs',
|
|
347
|
+
'- behavior: generate AI behavior function stubs',
|
|
348
|
+
'- app-demo: interactive spec creation/modification for the runtime interpreter',
|
|
349
|
+
'',
|
|
350
|
+
'Tools map 1:1 to the spv CLI. Key distinctions:',
|
|
351
|
+
'- validate: check a .specly file parses + matches the schema',
|
|
352
|
+
'- validate-manifest: check an implementation manifest resolves cleanly',
|
|
353
|
+
'- validate-bundle: check an entity bundle (schema + examples + tests + docs + behaviour facets)',
|
|
354
|
+
'- infer: expand a minimal .specly to full architecture (controllers / services / events / views)',
|
|
355
|
+
'- realize: turn inferred spec + manifest into generated code (Fastify / Prisma / React / CLI / tools)',
|
|
356
|
+
'- init: scaffold a new project from a template',
|
|
357
|
+
].join('\n');
|
|
358
|
+
|
|
359
|
+
|
|
279
360
|
function generateCliRunner(): string {
|
|
280
361
|
return `/**
|
|
281
362
|
* Thin specverse CLI runner.
|
|
@@ -377,11 +458,16 @@ export async function callTool(name: string, args: Record<string, any>) {
|
|
|
377
458
|
`;
|
|
378
459
|
}
|
|
379
460
|
|
|
380
|
-
function generateResources(): string {
|
|
461
|
+
function generateResources(tools: CLITool[]): string {
|
|
462
|
+
const cliReference = buildCliReferenceMarkdown(tools);
|
|
381
463
|
return `/**
|
|
382
|
-
* Resource registry — exposes the
|
|
383
|
-
* MCP resources
|
|
384
|
-
*
|
|
464
|
+
* Resource registry — exposes the canonical SpecVerse reference material as
|
|
465
|
+
* MCP resources:
|
|
466
|
+
* - schema (JSON Schema draft 2020-12, from @specverse/entities)
|
|
467
|
+
* - ai-guidance (curated LLM hints, from @specverse/entities)
|
|
468
|
+
* - minimal-example (one .specly covering all features, from @specverse/entities)
|
|
469
|
+
* - guide (user guide, from @specverse/self)
|
|
470
|
+
* - cli-reference (derived from the spec at realize time, embedded below)
|
|
385
471
|
*/
|
|
386
472
|
import { readFileSync } from 'fs';
|
|
387
473
|
import { createRequire } from 'module';
|
|
@@ -402,6 +488,13 @@ function resolveEntitiesFile(relative: string): string {
|
|
|
402
488
|
return join(dirname(pkg), relative);
|
|
403
489
|
}
|
|
404
490
|
|
|
491
|
+
function resolveSelfFile(relative: string): string {
|
|
492
|
+
const pkg = require.resolve('@specverse/self/package.json');
|
|
493
|
+
return join(dirname(pkg), relative);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const CLI_REFERENCE_MARKDOWN = ${JSON.stringify(cliReference)};
|
|
497
|
+
|
|
405
498
|
export const RESOURCES: Resource[] = [
|
|
406
499
|
{
|
|
407
500
|
uri: 'specverse://schema',
|
|
@@ -413,13 +506,43 @@ export const RESOURCES: Resource[] = [
|
|
|
413
506
|
mimeType: 'application/json',
|
|
414
507
|
}),
|
|
415
508
|
},
|
|
509
|
+
{
|
|
510
|
+
uri: 'specverse://ai-guidance',
|
|
511
|
+
name: 'SpecVerse AI Guidance Schema',
|
|
512
|
+
description: 'YAML schema annotated with examples and LLM guidance for generating valid specs. Read this before authoring or mutating .specly files.',
|
|
513
|
+
mimeType: 'application/x-yaml',
|
|
514
|
+
resolve: () => ({
|
|
515
|
+
text: readFileSync(resolveEntitiesFile('schema/SPECVERSE-SCHEMA-AI.yaml'), 'utf8'),
|
|
516
|
+
mimeType: 'application/x-yaml',
|
|
517
|
+
}),
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
uri: 'specverse://minimal-example',
|
|
521
|
+
name: 'SpecVerse Minimal Example',
|
|
522
|
+
description: 'One complete minimal .specly demonstrating every feature (~100 lines) — component, models, relationships, lifecycle, CURVED controller, service, view, event, manifest, deployment.',
|
|
523
|
+
mimeType: 'text/x-yaml',
|
|
524
|
+
resolve: () => ({
|
|
525
|
+
text: readFileSync(resolveEntitiesFile('schema/MINIMAL-SYNTAX-REFERENCE.specly'), 'utf8'),
|
|
526
|
+
mimeType: 'text/x-yaml',
|
|
527
|
+
}),
|
|
528
|
+
},
|
|
416
529
|
{
|
|
417
530
|
uri: 'specverse://guide',
|
|
418
531
|
name: 'SpecVerse Complete Guide',
|
|
419
532
|
description: 'The canonical user guide — spec language, convention patterns, CLI reference.',
|
|
420
533
|
mimeType: 'text/markdown',
|
|
421
534
|
resolve: () => ({
|
|
422
|
-
text: readFileSync(
|
|
535
|
+
text: readFileSync(resolveSelfFile('docs/guides/SPECVERSE-COMPLETE-GUIDE.md'), 'utf8'),
|
|
536
|
+
mimeType: 'text/markdown',
|
|
537
|
+
}),
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
uri: 'specverse://cli-reference',
|
|
541
|
+
name: 'SpecVerse CLI Reference',
|
|
542
|
+
description: 'Every spv CLI subcommand with its arguments, flags, and exit codes. Derived from the spec at realize time.',
|
|
543
|
+
mimeType: 'text/markdown',
|
|
544
|
+
resolve: () => ({
|
|
545
|
+
text: CLI_REFERENCE_MARKDOWN,
|
|
423
546
|
mimeType: 'text/markdown',
|
|
424
547
|
}),
|
|
425
548
|
},
|
|
@@ -439,3 +562,193 @@ export async function readResource(uri: string) {
|
|
|
439
562
|
}
|
|
440
563
|
`;
|
|
441
564
|
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Build the cli-reference.md content from the parsed CLI tool list.
|
|
568
|
+
* Embedded as a string constant in the generated resources.ts so the MCP
|
|
569
|
+
* server doesn't need filesystem access for it at runtime.
|
|
570
|
+
*/
|
|
571
|
+
function buildCliReferenceMarkdown(tools: CLITool[]): string {
|
|
572
|
+
const lines: string[] = [];
|
|
573
|
+
lines.push('# SpecVerse CLI Reference');
|
|
574
|
+
lines.push('');
|
|
575
|
+
lines.push('Generated from the self-spec at realize time. Every entry here matches an MCP tool exposed by this server (same name, same input schema).');
|
|
576
|
+
lines.push('');
|
|
577
|
+
for (const tool of tools) {
|
|
578
|
+
const cliCmd = tool.cliArgs.join(' ');
|
|
579
|
+
lines.push(`## \`spv ${cliCmd}\``);
|
|
580
|
+
lines.push('');
|
|
581
|
+
lines.push(tool.description);
|
|
582
|
+
lines.push('');
|
|
583
|
+
const props = (tool.inputSchema as any)?.properties || {};
|
|
584
|
+
const required = new Set(((tool.inputSchema as any)?.required || []) as string[]);
|
|
585
|
+
const positional = new Set(tool.positional);
|
|
586
|
+
const argEntries = Object.entries(props) as [string, any][];
|
|
587
|
+
if (argEntries.length > 0) {
|
|
588
|
+
lines.push('| Name | Kind | Required | Type | Description |');
|
|
589
|
+
lines.push('|---|---|---|---|---|');
|
|
590
|
+
for (const [name, schema] of argEntries) {
|
|
591
|
+
const kind = positional.has(name) ? 'positional' : 'flag';
|
|
592
|
+
const req = required.has(name) ? 'yes' : 'no';
|
|
593
|
+
const type = (schema as any)?.type || 'string';
|
|
594
|
+
const desc = ((schema as any)?.description || '').replace(/\|/g, '\\|');
|
|
595
|
+
lines.push(`| \`${name}\` | ${kind} | ${req} | ${type} | ${desc} |`);
|
|
596
|
+
}
|
|
597
|
+
lines.push('');
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return lines.join('\n');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Emit prompts.ts — loads the canonical workflow prompts from
|
|
605
|
+
* @specverse/engines/assets/prompts/core/standard/default/ at module-init
|
|
606
|
+
* time. Each YAML prompt becomes one MCP prompt; the declared
|
|
607
|
+
* {{template variables}} surface as MCP prompt arguments the client fills.
|
|
608
|
+
* Schema paths ({{aiSchemaPath}}, {{referenceSchemaPath}},
|
|
609
|
+
* {{referenceExamplePath}}) are auto-filled with package-relative paths so
|
|
610
|
+
* the caller doesn't have to know where they live on disk.
|
|
611
|
+
*/
|
|
612
|
+
function generatePrompts(): string {
|
|
613
|
+
return `/**
|
|
614
|
+
* Prompt registry — reads the canonical workflow prompts from
|
|
615
|
+
* @specverse/engines/assets/prompts/core/standard/default/ at startup and
|
|
616
|
+
* serves them as MCP prompts. Each YAML file (create, analyse, materialise,
|
|
617
|
+
* realize, behavior, app-demo) becomes one invocable prompt.
|
|
618
|
+
*/
|
|
619
|
+
import { readdirSync, readFileSync } from 'fs';
|
|
620
|
+
import { createRequire } from 'module';
|
|
621
|
+
import { dirname, join, basename } from 'path';
|
|
622
|
+
import { load as yamlLoad } from 'js-yaml';
|
|
623
|
+
|
|
624
|
+
const require = createRequire(import.meta.url);
|
|
625
|
+
|
|
626
|
+
export interface PromptArgument {
|
|
627
|
+
name: string;
|
|
628
|
+
description?: string;
|
|
629
|
+
required: boolean;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export interface Prompt {
|
|
633
|
+
name: string;
|
|
634
|
+
description: string;
|
|
635
|
+
arguments: PromptArgument[];
|
|
636
|
+
source: any; // parsed YAML — kept so getPrompt can re-render
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function resolveEntitiesFile(relative: string): string {
|
|
640
|
+
const pkg = require.resolve('@specverse/entities/package.json');
|
|
641
|
+
return join(dirname(pkg), relative);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function resolveEnginesDir(relative: string): string {
|
|
645
|
+
const pkg = require.resolve('@specverse/engines/package.json');
|
|
646
|
+
return join(dirname(pkg), relative);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Auto-filled template vars — paths into @specverse/entities/schema/
|
|
650
|
+
const SCHEMA_VAR_PATHS: Record<string, string> = {
|
|
651
|
+
aiSchemaPath: resolveEntitiesFile('schema/SPECVERSE-SCHEMA-AI.yaml'),
|
|
652
|
+
referenceSchemaPath: resolveEntitiesFile('schema/MINIMAL-SYNTAX-REFERENCE.specly'),
|
|
653
|
+
referenceExamplePath: resolveEntitiesFile('schema/MINIMAL-SYNTAX-REFERENCE.specly'),
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// User-facing vars the server does NOT auto-fill — surfaced as MCP prompt args.
|
|
657
|
+
const USER_FACING = new Set<string>([
|
|
658
|
+
...Object.keys(SCHEMA_VAR_PATHS),
|
|
659
|
+
]);
|
|
660
|
+
|
|
661
|
+
function loadPrompts(): Prompt[] {
|
|
662
|
+
const promptsDir = resolveEnginesDir('assets/prompts/core/standard/default');
|
|
663
|
+
const entries = readdirSync(promptsDir).filter(f => f.endsWith('.prompt.yaml'));
|
|
664
|
+
const prompts: Prompt[] = [];
|
|
665
|
+
for (const entry of entries) {
|
|
666
|
+
const full = join(promptsDir, entry);
|
|
667
|
+
try {
|
|
668
|
+
const parsed = yamlLoad(readFileSync(full, 'utf8')) as any;
|
|
669
|
+
if (!parsed?.name) continue;
|
|
670
|
+
const args = deriveArguments(parsed);
|
|
671
|
+
prompts.push({
|
|
672
|
+
name: parsed.name,
|
|
673
|
+
description: parsed.description || \`SpecVerse \${parsed.name} workflow\`,
|
|
674
|
+
arguments: args,
|
|
675
|
+
source: parsed,
|
|
676
|
+
});
|
|
677
|
+
} catch (err) {
|
|
678
|
+
// One bad prompt shouldn't kill the server; skip it.
|
|
679
|
+
console.error(\`[prompts] skipping \${basename(entry)}: \${(err as Error).message}\`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return prompts;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Every SpecVerse prompt YAML declares user.variables[] in the canonical shape
|
|
687
|
+
* { name, type?, required?, description?, default? }. We surface the declared
|
|
688
|
+
* list verbatim, filtering out auto-filled schema-path vars.
|
|
689
|
+
*/
|
|
690
|
+
function deriveArguments(parsed: any): PromptArgument[] {
|
|
691
|
+
const declared = parsed?.user?.variables;
|
|
692
|
+
if (!Array.isArray(declared)) return [];
|
|
693
|
+
return declared
|
|
694
|
+
.filter((v: any) => v && typeof v === 'object' && typeof v.name === 'string')
|
|
695
|
+
.filter((v: any) => !USER_FACING.has(v.name))
|
|
696
|
+
.map((v: any) => ({
|
|
697
|
+
name: v.name,
|
|
698
|
+
description: v.description || inferVarDescription(v.name, parsed),
|
|
699
|
+
required: v.required !== false,
|
|
700
|
+
}));
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function inferVarDescription(varName: string, parsed: any): string {
|
|
704
|
+
// Friendly defaults for the known common vars; fall back to a humanised
|
|
705
|
+
// version of the variable name.
|
|
706
|
+
const defaults: Record<string, string> = {
|
|
707
|
+
requirements: 'Natural-language requirements to extract a specification from',
|
|
708
|
+
scale: 'Project scale — personal, business, or enterprise',
|
|
709
|
+
preferredTech: 'Preferred technology stack hint (e.g. "nextjs", "nestjs", "fastify+prisma", "auto")',
|
|
710
|
+
implementationPath: 'Path to the existing codebase to reverse-engineer into a .specly',
|
|
711
|
+
specPath: 'Path to an existing .specly file',
|
|
712
|
+
manifestPath: 'Path to an implementation manifest (manifests/implementation.yaml)',
|
|
713
|
+
outputDir: 'Output directory for generated artifacts',
|
|
714
|
+
modelName: 'Name of the model the behavior is attached to',
|
|
715
|
+
behaviorName: 'Name of the behavior method on the model',
|
|
716
|
+
};
|
|
717
|
+
if (defaults[varName]) return defaults[varName];
|
|
718
|
+
return varName.replace(/([A-Z])/g, ' $1').replace(/^./, c => c.toUpperCase());
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function substitute(text: string, values: Record<string, string>): string {
|
|
722
|
+
return text.replace(/\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\}\\}/g, (match, name) => {
|
|
723
|
+
if (name in values) return values[name];
|
|
724
|
+
if (name in SCHEMA_VAR_PATHS) return SCHEMA_VAR_PATHS[name];
|
|
725
|
+
return match; // unknown var — leave the placeholder so it's visible
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function renderMessages(prompt: Prompt, args: Record<string, string>): { role: 'user'; content: { type: 'text'; text: string } }[] {
|
|
730
|
+
const src = prompt.source;
|
|
731
|
+
const sections: string[] = [];
|
|
732
|
+
if (src?.system?.role) sections.push(\`[System role]\\n\${substitute(src.system.role, args).trim()}\`);
|
|
733
|
+
if (src?.system?.context) sections.push(\`[Context]\\n\${substitute(src.system.context, args).trim()}\`);
|
|
734
|
+
if (src?.user?.template) sections.push(substitute(src.user.template, args).trim());
|
|
735
|
+
const text = sections.filter(Boolean).join('\\n\\n');
|
|
736
|
+
return [{ role: 'user', content: { type: 'text', text } }];
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export const PROMPTS: Prompt[] = loadPrompts();
|
|
740
|
+
|
|
741
|
+
const BY_NAME = new Map<string, Prompt>(PROMPTS.map(p => [p.name, p]));
|
|
742
|
+
|
|
743
|
+
export async function getPrompt(name: string, args: Record<string, string>) {
|
|
744
|
+
const prompt = BY_NAME.get(name);
|
|
745
|
+
if (!prompt) {
|
|
746
|
+
throw new Error(\`Unknown prompt: \${name}\`);
|
|
747
|
+
}
|
|
748
|
+
return {
|
|
749
|
+
description: prompt.description,
|
|
750
|
+
messages: renderMessages(prompt, args),
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
`;
|
|
754
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@specverse/engines",
|
|
3
|
-
"version": "5.0
|
|
4
|
-
"description": "SpecVerse toolchain \u2014 parser, inference, realize, generators, AI, registry",
|
|
3
|
+
"version": "5.2.0",
|
|
4
|
+
"description": "SpecVerse toolchain \u2014 parser, inference, realize, generators, AI, registry, bundles",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"types": "./dist/registry/index.d.ts",
|
|
36
36
|
"import": "./dist/registry/index.js"
|
|
37
37
|
},
|
|
38
|
+
"./bundles": {
|
|
39
|
+
"types": "./dist/bundles/index.d.ts",
|
|
40
|
+
"import": "./dist/bundles/index.js"
|
|
41
|
+
},
|
|
38
42
|
"./package.json": "./package.json"
|
|
39
43
|
},
|
|
40
44
|
"scripts": {
|
|
@@ -42,9 +46,9 @@
|
|
|
42
46
|
"clean": "rm -rf dist"
|
|
43
47
|
},
|
|
44
48
|
"dependencies": {
|
|
45
|
-
"@specverse/entities": "^5.
|
|
46
|
-
"@specverse/runtime": "^5.0.
|
|
47
|
-
"@specverse/types": "^5.
|
|
49
|
+
"@specverse/entities": "^5.1.0",
|
|
50
|
+
"@specverse/runtime": "^5.0.1",
|
|
51
|
+
"@specverse/types": "^5.1.0",
|
|
48
52
|
"ajv": "^8.17.0",
|
|
49
53
|
"ajv-formats": "^2.1.0",
|
|
50
54
|
"glob": "^10.0.0",
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
metadata:
|
|
2
|
-
component: SpecVerseFundamentals
|
|
3
|
-
version: 1.0.0
|
|
4
|
-
description: 'Example 01-01: Basic model definition with attributes'
|
|
5
|
-
tags: []
|
|
6
|
-
imports:
|
|
7
|
-
- from: '@specverse/primitives'
|
|
8
|
-
select:
|
|
9
|
-
- Money
|
|
10
|
-
exports: []
|
|
11
|
-
models:
|
|
12
|
-
- name: Product
|
|
13
|
-
attributes:
|
|
14
|
-
- name: id
|
|
15
|
-
type: UUID
|
|
16
|
-
constraints:
|
|
17
|
-
required: true
|
|
18
|
-
unique: false
|
|
19
|
-
pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
|
|
20
|
-
format: uuid
|
|
21
|
-
dbMapping:
|
|
22
|
-
columnName: id
|
|
23
|
-
columnType: UUID
|
|
24
|
-
index: false
|
|
25
|
-
nullable: false
|
|
26
|
-
validation:
|
|
27
|
-
rules:
|
|
28
|
-
- required
|
|
29
|
-
metadata:
|
|
30
|
-
encrypted: false
|
|
31
|
-
audit: false
|
|
32
|
-
- name: name
|
|
33
|
-
type: String
|
|
34
|
-
constraints:
|
|
35
|
-
required: true
|
|
36
|
-
unique: false
|
|
37
|
-
dbMapping:
|
|
38
|
-
columnName: name
|
|
39
|
-
columnType: VARCHAR(255)
|
|
40
|
-
index: false
|
|
41
|
-
nullable: false
|
|
42
|
-
validation:
|
|
43
|
-
rules:
|
|
44
|
-
- required
|
|
45
|
-
metadata:
|
|
46
|
-
encrypted: false
|
|
47
|
-
audit: false
|
|
48
|
-
- name: summary
|
|
49
|
-
type: String
|
|
50
|
-
constraints:
|
|
51
|
-
required: false
|
|
52
|
-
unique: false
|
|
53
|
-
dbMapping:
|
|
54
|
-
columnName: summary
|
|
55
|
-
columnType: VARCHAR(255)
|
|
56
|
-
index: false
|
|
57
|
-
nullable: true
|
|
58
|
-
validation:
|
|
59
|
-
rules: []
|
|
60
|
-
metadata:
|
|
61
|
-
encrypted: false
|
|
62
|
-
audit: false
|
|
63
|
-
- name: price
|
|
64
|
-
type: Money
|
|
65
|
-
constraints:
|
|
66
|
-
required: true
|
|
67
|
-
unique: false
|
|
68
|
-
dbMapping:
|
|
69
|
-
columnName: price
|
|
70
|
-
columnType: VARCHAR(255)
|
|
71
|
-
index: false
|
|
72
|
-
nullable: false
|
|
73
|
-
validation:
|
|
74
|
-
rules:
|
|
75
|
-
- required
|
|
76
|
-
metadata:
|
|
77
|
-
encrypted: false
|
|
78
|
-
audit: false
|
|
79
|
-
- name: inStock
|
|
80
|
-
type: Boolean
|
|
81
|
-
constraints:
|
|
82
|
-
required: false
|
|
83
|
-
unique: false
|
|
84
|
-
dbMapping:
|
|
85
|
-
columnName: in_stock
|
|
86
|
-
columnType: BOOLEAN
|
|
87
|
-
index: false
|
|
88
|
-
nullable: true
|
|
89
|
-
defaultValue: 'true'
|
|
90
|
-
validation:
|
|
91
|
-
rules: []
|
|
92
|
-
metadata:
|
|
93
|
-
encrypted: false
|
|
94
|
-
audit: false
|
|
95
|
-
- name: category
|
|
96
|
-
type: String
|
|
97
|
-
constraints:
|
|
98
|
-
required: true
|
|
99
|
-
unique: false
|
|
100
|
-
dbMapping:
|
|
101
|
-
columnName: category
|
|
102
|
-
columnType: VARCHAR(255)
|
|
103
|
-
index: false
|
|
104
|
-
nullable: false
|
|
105
|
-
validation:
|
|
106
|
-
rules:
|
|
107
|
-
- required
|
|
108
|
-
metadata:
|
|
109
|
-
encrypted: false
|
|
110
|
-
audit: false
|
|
111
|
-
- name: contactEmail
|
|
112
|
-
type: Email
|
|
113
|
-
constraints:
|
|
114
|
-
required: false
|
|
115
|
-
unique: false
|
|
116
|
-
pattern: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
|
|
117
|
-
format: email
|
|
118
|
-
dbMapping:
|
|
119
|
-
columnName: contact_email
|
|
120
|
-
columnType: VARCHAR(255)
|
|
121
|
-
index: false
|
|
122
|
-
nullable: true
|
|
123
|
-
validation:
|
|
124
|
-
rules:
|
|
125
|
-
- email
|
|
126
|
-
metadata:
|
|
127
|
-
encrypted: false
|
|
128
|
-
audit: false
|
|
129
|
-
relationships: []
|
|
130
|
-
behaviors:
|
|
131
|
-
- name: attachProfile
|
|
132
|
-
signature: 'attachProfile(profileName: String): Promise<Boolean>'
|
|
133
|
-
implementation:
|
|
134
|
-
preconditions:
|
|
135
|
-
- Profile exists and is compatible with this model
|
|
136
|
-
postconditions:
|
|
137
|
-
- Profile is attached
|
|
138
|
-
- Profile attributes are available
|
|
139
|
-
sideEffects: []
|
|
140
|
-
steps: []
|
|
141
|
-
transactional: true
|
|
142
|
-
metadata:
|
|
143
|
-
async: true
|
|
144
|
-
cacheable: false
|
|
145
|
-
idempotent: false
|
|
146
|
-
- name: detachProfile
|
|
147
|
-
signature: 'detachProfile(profileName: String): Promise<Boolean>'
|
|
148
|
-
implementation:
|
|
149
|
-
preconditions:
|
|
150
|
-
- Profile is currently attached
|
|
151
|
-
postconditions:
|
|
152
|
-
- Profile is detached
|
|
153
|
-
- Profile attributes are no longer available
|
|
154
|
-
sideEffects: []
|
|
155
|
-
steps: []
|
|
156
|
-
transactional: true
|
|
157
|
-
metadata:
|
|
158
|
-
async: true
|
|
159
|
-
cacheable: false
|
|
160
|
-
idempotent: false
|
|
161
|
-
- name: hasProfile
|
|
162
|
-
signature: 'hasProfile(profileName: String): Promise<Boolean>'
|
|
163
|
-
implementation:
|
|
164
|
-
preconditions: []
|
|
165
|
-
postconditions: []
|
|
166
|
-
sideEffects: []
|
|
167
|
-
steps: []
|
|
168
|
-
transactional: false
|
|
169
|
-
metadata:
|
|
170
|
-
async: true
|
|
171
|
-
cacheable: false
|
|
172
|
-
idempotent: false
|
|
173
|
-
lifecycle:
|
|
174
|
-
states: []
|
|
175
|
-
transitions: {}
|
|
176
|
-
currentStateField: state
|
|
177
|
-
dbMapping:
|
|
178
|
-
tableName: products
|
|
179
|
-
indexes: []
|
|
180
|
-
controllers: []
|
|
181
|
-
services: []
|
|
182
|
-
views: []
|
|
183
|
-
events: []
|
|
184
|
-
infrastructure:
|
|
185
|
-
database:
|
|
186
|
-
type: postgresql
|
|
187
|
-
migrations: true
|
|
188
|
-
seedData: false
|
|
189
|
-
messaging:
|
|
190
|
-
type: rabbitmq
|
|
191
|
-
queues: []
|
|
192
|
-
caching:
|
|
193
|
-
type: redis
|
|
194
|
-
ttl: 3600
|