@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.
Files changed (116) hide show
  1. package/assets/prompts/core/standard/default/analyse.prompt.yaml +5 -5
  2. package/assets/prompts/core/standard/default/app-demo.prompt.yaml +21 -1
  3. package/assets/prompts/core/standard/default/behavior.prompt.yaml +150 -0
  4. package/assets/prompts/core/standard/default/create.prompt.yaml +3 -3
  5. package/assets/prompts/core/standard/default/materialise.prompt.yaml +804 -774
  6. package/assets/prompts/core/standard/default/realize.prompt.yaml +581 -544
  7. package/assets/prompts/core/standard/v9/analyse.prompt.yaml +5 -5
  8. package/assets/prompts/core/standard/v9/app-demo.prompt.yaml +233 -0
  9. package/assets/prompts/core/standard/v9/behavior.prompt.yaml +33 -9
  10. package/assets/prompts/core/standard/v9/create.prompt.yaml +3 -3
  11. package/assets/prompts/core/standard/v9/materialise.prompt.yaml +804 -774
  12. package/assets/prompts/core/standard/v9/realize.prompt.yaml +581 -544
  13. package/dist/ai/commands/fill.d.ts.map +1 -1
  14. package/dist/ai/commands/fill.js +16 -7
  15. package/dist/ai/commands/fill.js.map +1 -1
  16. package/dist/ai/commands/template.d.ts.map +1 -1
  17. package/dist/ai/commands/template.js +17 -8
  18. package/dist/ai/commands/template.js.map +1 -1
  19. package/dist/bundles/deriveCatalog.d.ts +18 -0
  20. package/dist/bundles/deriveCatalog.d.ts.map +1 -0
  21. package/dist/bundles/deriveCatalog.js +263 -0
  22. package/dist/bundles/deriveCatalog.js.map +1 -0
  23. package/dist/bundles/index.d.ts +15 -0
  24. package/dist/bundles/index.d.ts.map +1 -0
  25. package/dist/bundles/index.js +15 -0
  26. package/dist/bundles/index.js.map +1 -0
  27. package/dist/bundles/types.d.ts +53 -0
  28. package/dist/bundles/types.d.ts.map +1 -0
  29. package/dist/bundles/types.js +22 -0
  30. package/dist/bundles/types.js.map +1 -0
  31. package/dist/bundles/validate.d.ts +55 -0
  32. package/dist/bundles/validate.d.ts.map +1 -0
  33. package/dist/bundles/validate.js +471 -0
  34. package/dist/bundles/validate.js.map +1 -0
  35. package/dist/inference/quint-transpiler.js +2 -2
  36. package/dist/inference/quint-transpiler.js.map +1 -1
  37. package/dist/libs/instance-factories/applications/templates/react/runtime-package-json-generator.js +1 -1
  38. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +227 -0
  39. package/dist/libs/instance-factories/tools/templates/mcp/mcp-server-generator.js +295 -14
  40. package/libs/instance-factories/applications/templates/react/runtime-package-json-generator.ts +1 -1
  41. package/libs/instance-factories/cli/templates/commander/command-generator.ts +227 -0
  42. package/libs/instance-factories/tools/templates/mcp/mcp-server-generator.ts +328 -15
  43. package/package.json +9 -5
  44. package/assets/examples/09-api/ai-spec.yaml +0 -194
  45. package/assets/examples/09-api/converted.yaml +0 -95
  46. package/assets/examples/09-api/diagram-architecture.mmd +0 -10
  47. package/assets/examples/09-api/diagram-er.mmd +0 -10
  48. package/assets/examples/09-api/documentation.html +0 -104
  49. package/assets/examples/09-api/documentation.md +0 -95
  50. package/assets/examples/09-api/inferred-spec.yaml +0 -420
  51. package/assets/examples/09-api/openapi.json +0 -61
  52. package/assets/examples/10-api/README.md +0 -216
  53. package/assets/examples/10-api/ai-spec.yaml +0 -194
  54. package/assets/examples/10-api/converted.yaml +0 -96
  55. package/assets/examples/10-api/diagram-architecture.mmd +0 -10
  56. package/assets/examples/10-api/diagram-er.mmd +0 -10
  57. package/assets/examples/10-api/documentation.html +0 -104
  58. package/assets/examples/10-api/documentation.md +0 -95
  59. package/assets/examples/10-api/inferred-spec.yaml +0 -7
  60. package/assets/examples/10-api/metadata.yaml +0 -89
  61. package/assets/examples/10-api/openapi.json +0 -61
  62. package/assets/examples/10-api/package-integration-test.js +0 -177
  63. package/assets/examples/10-api/usage-example.js +0 -323
  64. package/assets/examples/10-api/usage-example.ts +0 -363
  65. package/assets/examples/10-api/workflow-test.js +0 -113
  66. package/assets/examples/validate-examples-with-expected-failures.cjs +0 -328
  67. package/assets/examples/validate-examples.cjs +0 -225
  68. package/assets/prompts/MOVED.md +0 -35
  69. package/assets/prompts/SUMMARY-v8-PROMOTION.md +0 -445
  70. package/assets/prompts/core/CHANGELOG.md +0 -158
  71. package/assets/prompts/core/MIGRATION-v6-to-v7.md +0 -379
  72. package/assets/prompts/core/base-terminal-prompt.md +0 -201
  73. package/assets/prompts/core/examples/example-usage.ts +0 -140
  74. package/assets/prompts/core/schemas/prompt.schema.json +0 -309
  75. package/assets/prompts/core/schemas/prompt.schema.yaml +0 -229
  76. package/assets/prompts/core/standard/archive/v1/analyse.prompt.yaml +0 -259
  77. package/assets/prompts/core/standard/archive/v1/create.prompt.yaml +0 -302
  78. package/assets/prompts/core/standard/archive/v1/materialise.prompt.yaml +0 -328
  79. package/assets/prompts/core/standard/archive/v1/realize.prompt.yaml +0 -606
  80. package/assets/prompts/core/standard/archive/v2/README.md +0 -110
  81. package/assets/prompts/core/standard/archive/v2/analyse.prompt.yaml +0 -151
  82. package/assets/prompts/core/standard/archive/v2/create.prompt.yaml +0 -151
  83. package/assets/prompts/core/standard/archive/v2/materialise.prompt.yaml +0 -132
  84. package/assets/prompts/core/standard/archive/v2/realize.prompt.yaml +0 -147
  85. package/assets/prompts/core/standard/archive/v3/README.md +0 -279
  86. package/assets/prompts/core/standard/archive/v3/analyse.prompt.yaml +0 -309
  87. package/assets/prompts/core/standard/archive/v3/create.prompt.yaml +0 -351
  88. package/assets/prompts/core/standard/archive/v3/materialise.prompt.yaml +0 -247
  89. package/assets/prompts/core/standard/archive/v3/realize.prompt.yaml +0 -344
  90. package/assets/prompts/core/standard/archive/v4/README.md +0 -79
  91. package/assets/prompts/core/standard/archive/v4/analyse.prompt.yaml +0 -204
  92. package/assets/prompts/core/standard/archive/v4/create.prompt.yaml +0 -185
  93. package/assets/prompts/core/standard/archive/v5/README.md +0 -224
  94. package/assets/prompts/core/standard/archive/v5/analyse.prompt.yaml +0 -209
  95. package/assets/prompts/core/standard/archive/v5/create.prompt.yaml +0 -225
  96. package/assets/prompts/core/standard/archive/v5/materialise.prompt.yaml +0 -242
  97. package/assets/prompts/core/standard/archive/v5/realize.prompt.yaml +0 -336
  98. package/assets/prompts/core/standard/archive/v6/README.md +0 -187
  99. package/assets/prompts/core/standard/archive/v6/analyse.prompt.yaml +0 -219
  100. package/assets/prompts/core/standard/archive/v6/create.prompt.yaml +0 -180
  101. package/assets/prompts/core/standard/archive/v6/materialise.prompt.yaml +0 -203
  102. package/assets/prompts/core/standard/archive/v6/realize.prompt.yaml +0 -215
  103. package/assets/prompts/core/standard/archive/v7/analyse.prompt.nick.yaml +0 -144
  104. package/assets/prompts/core/standard/archive/v7/analyse.prompt.old.yaml +0 -146
  105. package/assets/prompts/core/standard/archive/v7/analyse.prompt.yaml +0 -129
  106. package/assets/prompts/core/standard/archive/v7/create.prompt.yaml +0 -146
  107. package/assets/prompts/core/standard/archive/v7/materialise.prompt.yaml +0 -297
  108. package/assets/prompts/core/standard/archive/v7/realize.prompt.yaml +0 -294
  109. package/assets/prompts/core/standard/archive/v8/README.md +0 -400
  110. package/assets/prompts/core/standard/archive/v8/analyse.prompt.yaml +0 -185
  111. package/assets/prompts/core/standard/archive/v8/create.prompt.yaml +0 -203
  112. package/assets/prompts/core/standard/archive/v8/materialise.prompt.yaml +0 -297
  113. package/assets/prompts/core/standard/archive/v8/realize.prompt.yaml +0 -294
  114. package/assets/prompts/templates/api-orchestrator-template.yaml +0 -188
  115. package/assets/prompts/templates/claude-integration-template.md +0 -121
  116. 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 { join } from 'path';
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
- const version = distribution?.version || spec?.metadata?.version || spec?.version || '5.0.0';
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
- return `MCP server generated in: ${mcpDir}\n ${cliCommands.length} tools (one per CLI command), 2 resources (schema + docs)`;
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/entities': '^5.0.0',
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
- * No handwritten business logic everything is derived from the spec
230
- * (tools from CLI commands) or from @specverse/entities (schema + docs).
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
- { capabilities: { tools: {}, resources: {} } },
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 live SpecVerse schema + user guide as
383
- * MCP resources. Read lazily from @specverse/entities at request time so
384
- * the contents always match the installed entities version.
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(resolveEntitiesFile('schema/SPECVERSE-COMPLETE-GUIDE.md'), 'utf8'),
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.2",
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.0.0",
46
- "@specverse/runtime": "^5.0.0",
47
- "@specverse/types": "^5.0.0",
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