@specverse/engines 5.1.0 → 6.0.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 (92) 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 +157 -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 +70 -39
  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/behavior-ai-service.d.ts +35 -28
  14. package/dist/ai/behavior-ai-service.d.ts.map +1 -1
  15. package/dist/ai/behavior-ai-service.js +95 -128
  16. package/dist/ai/behavior-ai-service.js.map +1 -1
  17. package/dist/ai/index.d.ts +26 -26
  18. package/dist/ai/index.d.ts.map +1 -1
  19. package/dist/ai/index.js +40 -29
  20. package/dist/ai/index.js.map +1 -1
  21. package/dist/ai/model-resolver.d.ts +13 -0
  22. package/dist/ai/model-resolver.d.ts.map +1 -0
  23. package/dist/ai/model-resolver.js +87 -0
  24. package/dist/ai/model-resolver.js.map +1 -0
  25. package/dist/ai/providers/claude-cli.d.ts +25 -0
  26. package/dist/ai/providers/claude-cli.d.ts.map +1 -0
  27. package/dist/ai/providers/claude-cli.js +185 -0
  28. package/dist/ai/providers/claude-cli.js.map +1 -0
  29. package/dist/ai/providers/index.d.ts +8 -5
  30. package/dist/ai/providers/index.d.ts.map +1 -1
  31. package/dist/ai/providers/index.js +7 -5
  32. package/dist/ai/providers/index.js.map +1 -1
  33. package/dist/ai/providers/stub.d.ts +15 -0
  34. package/dist/ai/providers/stub.d.ts.map +1 -0
  35. package/dist/ai/providers/stub.js +64 -0
  36. package/dist/ai/providers/stub.js.map +1 -0
  37. package/dist/ai/skill-detection.d.ts +5 -0
  38. package/dist/ai/skill-detection.d.ts.map +1 -0
  39. package/dist/ai/skill-detection.js +27 -0
  40. package/dist/ai/skill-detection.js.map +1 -0
  41. package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +184 -0
  42. package/dist/libs/instance-factories/tools/templates/mcp/mcp-server-generator.js +289 -15
  43. package/libs/instance-factories/cli/templates/commander/command-generator.ts +184 -0
  44. package/libs/instance-factories/tools/templates/mcp/mcp-server-generator.ts +322 -16
  45. package/package.json +8 -3
  46. package/assets/prompts/core/CHANGELOG.md +0 -158
  47. package/assets/prompts/core/MIGRATION-v6-to-v7.md +0 -379
  48. package/assets/prompts/core/base-terminal-prompt.md +0 -201
  49. package/assets/prompts/core/examples/example-usage.ts +0 -140
  50. package/assets/prompts/core/schemas/prompt.schema.json +0 -309
  51. package/assets/prompts/core/schemas/prompt.schema.yaml +0 -229
  52. package/assets/prompts/core/standard/archive/v1/analyse.prompt.yaml +0 -259
  53. package/assets/prompts/core/standard/archive/v1/create.prompt.yaml +0 -302
  54. package/assets/prompts/core/standard/archive/v1/materialise.prompt.yaml +0 -328
  55. package/assets/prompts/core/standard/archive/v1/realize.prompt.yaml +0 -606
  56. package/assets/prompts/core/standard/archive/v2/README.md +0 -110
  57. package/assets/prompts/core/standard/archive/v2/analyse.prompt.yaml +0 -151
  58. package/assets/prompts/core/standard/archive/v2/create.prompt.yaml +0 -151
  59. package/assets/prompts/core/standard/archive/v2/materialise.prompt.yaml +0 -132
  60. package/assets/prompts/core/standard/archive/v2/realize.prompt.yaml +0 -147
  61. package/assets/prompts/core/standard/archive/v3/README.md +0 -279
  62. package/assets/prompts/core/standard/archive/v3/analyse.prompt.yaml +0 -309
  63. package/assets/prompts/core/standard/archive/v3/create.prompt.yaml +0 -351
  64. package/assets/prompts/core/standard/archive/v3/materialise.prompt.yaml +0 -247
  65. package/assets/prompts/core/standard/archive/v3/realize.prompt.yaml +0 -344
  66. package/assets/prompts/core/standard/archive/v4/README.md +0 -79
  67. package/assets/prompts/core/standard/archive/v4/analyse.prompt.yaml +0 -204
  68. package/assets/prompts/core/standard/archive/v4/create.prompt.yaml +0 -185
  69. package/assets/prompts/core/standard/archive/v5/README.md +0 -224
  70. package/assets/prompts/core/standard/archive/v5/analyse.prompt.yaml +0 -209
  71. package/assets/prompts/core/standard/archive/v5/create.prompt.yaml +0 -225
  72. package/assets/prompts/core/standard/archive/v5/materialise.prompt.yaml +0 -242
  73. package/assets/prompts/core/standard/archive/v5/realize.prompt.yaml +0 -336
  74. package/assets/prompts/core/standard/archive/v6/README.md +0 -187
  75. package/assets/prompts/core/standard/archive/v6/analyse.prompt.yaml +0 -219
  76. package/assets/prompts/core/standard/archive/v6/create.prompt.yaml +0 -180
  77. package/assets/prompts/core/standard/archive/v6/materialise.prompt.yaml +0 -203
  78. package/assets/prompts/core/standard/archive/v6/realize.prompt.yaml +0 -215
  79. package/assets/prompts/core/standard/archive/v7/analyse.prompt.nick.yaml +0 -144
  80. package/assets/prompts/core/standard/archive/v7/analyse.prompt.old.yaml +0 -146
  81. package/assets/prompts/core/standard/archive/v7/analyse.prompt.yaml +0 -129
  82. package/assets/prompts/core/standard/archive/v7/create.prompt.yaml +0 -146
  83. package/assets/prompts/core/standard/archive/v7/materialise.prompt.yaml +0 -297
  84. package/assets/prompts/core/standard/archive/v7/realize.prompt.yaml +0 -294
  85. package/assets/prompts/core/standard/archive/v8/README.md +0 -400
  86. package/assets/prompts/core/standard/archive/v8/analyse.prompt.yaml +0 -185
  87. package/assets/prompts/core/standard/archive/v8/create.prompt.yaml +0 -203
  88. package/assets/prompts/core/standard/archive/v8/materialise.prompt.yaml +0 -297
  89. package/assets/prompts/core/standard/archive/v8/realize.prompt.yaml +0 -294
  90. package/assets/prompts/templates/api-orchestrator-template.yaml +0 -188
  91. package/assets/prompts/templates/claude-integration-template.md +0 -121
  92. package/assets/prompts/templates/terminal-prompt-template.md +0 -97
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stub.js","sourceRoot":"","sources":["../../../src/ai/providers/stub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function detectInstalledSkill(skillName?: string): {
2
+ found: boolean;
3
+ location: string | null;
4
+ };
5
+ //# sourceMappingURL=skill-detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-detection.d.ts","sourceRoot":"","sources":["../../src/ai/skill-detection.ts"],"names":[],"mappings":"AAgBA,wBAAgB,oBAAoB,CAAC,SAAS,SAAc,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CASzG"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Detect whether the SpecVerse Claude skill is installed in a location
3
+ * that Claude Code will auto-load.
4
+ *
5
+ * Claude Code discovers skills from:
6
+ * - ~/.claude/skills/<name>/ (global — shipped by `spv skill install --global`)
7
+ * - <cwd>/.claude/skills/<name>/ (project-local — `spv skill install --project`)
8
+ *
9
+ * If either is present with a SKILL.md, we assume Claude-CLI-backed behavior
10
+ * generation will benefit from the skill's auto-loaded context — meaning we
11
+ * can send a leaner system prompt (skip the fullContext block).
12
+ */
13
+ import { existsSync } from 'fs';
14
+ import { homedir } from 'os';
15
+ import { join } from 'path';
16
+ export function detectInstalledSkill(skillName = 'specverse') {
17
+ const candidates = [
18
+ join(process.cwd(), '.claude', 'skills', skillName, 'SKILL.md'),
19
+ join(homedir(), '.claude', 'skills', skillName, 'SKILL.md'),
20
+ ];
21
+ for (const path of candidates) {
22
+ if (existsSync(path))
23
+ return { found: true, location: path };
24
+ }
25
+ return { found: false, location: null };
26
+ }
27
+ //# sourceMappingURL=skill-detection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-detection.js","sourceRoot":"","sources":["../../src/ai/skill-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,oBAAoB,CAAC,SAAS,GAAG,WAAW;IAC1D,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC;QAC/D,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC;KAC5D,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC"}
@@ -1114,6 +1114,190 @@ import type { ParserEngine } from '@specverse/types';`,
1114
1114
  console.log('Processing job: ' + jobId);
1115
1115
  await manager.processJob(jobId);
1116
1116
  console.log('Job processed successfully');`
1117
+ },
1118
+ // === skill subcommands ===
1119
+ "skill.install": {
1120
+ imports: `import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';
1121
+ import { resolve, dirname, join } from 'path';
1122
+ import { homedir } from 'os';
1123
+ import { fileURLToPath } from 'url';
1124
+ import { createRequire } from 'module';`,
1125
+ handler: `const requireResolve = createRequire(import.meta.url);
1126
+
1127
+ // 1. Resolve target directory.
1128
+ let targetDir: string;
1129
+ if (options.target) {
1130
+ targetDir = resolve(options.target);
1131
+ } else if (options.global) {
1132
+ targetDir = join(homedir(), '.claude', 'skills', 'specverse');
1133
+ } else {
1134
+ // Default: project-local (current working directory).
1135
+ targetDir = join(process.env.SPECVERSE_USER_CWD || process.cwd(), '.claude', 'skills', 'specverse');
1136
+ }
1137
+ mkdirSync(targetDir, { recursive: true });
1138
+ mkdirSync(join(targetDir, 'reference'), { recursive: true });
1139
+ mkdirSync(join(targetDir, 'workflows'), { recursive: true });
1140
+
1141
+ // 2. Helpers to resolve into installed packages.
1142
+ // For @specverse/self specifically, we fall back to walking up from
1143
+ // import.meta.url \u2014 handy when running the in-repo bootstrap CLI where
1144
+ // self can't resolve its own package.json via require.
1145
+ const selfRoot = (() => {
1146
+ try {
1147
+ const pkgJson = requireResolve.resolve('@specverse/self/package.json');
1148
+ return dirname(pkgJson);
1149
+ } catch {
1150
+ let dir = dirname(fileURLToPath(import.meta.url));
1151
+ for (let i = 0; i < 8; i++) {
1152
+ const candidate = join(dir, 'package.json');
1153
+ if (existsSync(candidate)) {
1154
+ try {
1155
+ const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
1156
+ if (pkg?.name === '@specverse/self') return dir;
1157
+ } catch { /* continue */ }
1158
+ }
1159
+ const parent = dirname(dir);
1160
+ if (parent === dir) break;
1161
+ dir = parent;
1162
+ }
1163
+ return null;
1164
+ }
1165
+ })();
1166
+
1167
+ const resolvePkg = (pkg: string, rel: string): string | null => {
1168
+ if (pkg === '@specverse/self') {
1169
+ return selfRoot ? join(selfRoot, rel) : null;
1170
+ }
1171
+ try {
1172
+ const pkgJson = requireResolve.resolve(pkg + '/package.json');
1173
+ return join(dirname(pkgJson), rel);
1174
+ } catch { return null; }
1175
+ };
1176
+
1177
+ // 3. Copy SKILL.md from @specverse/self.
1178
+ const skillMdSrc = resolvePkg('@specverse/self', 'skills/specverse/SKILL.md');
1179
+ if (!skillMdSrc || !existsSync(skillMdSrc)) {
1180
+ console.error('Cannot locate SKILL.md in @specverse/self \u2014 install failed.');
1181
+ process.exit(1);
1182
+ }
1183
+ writeFileSync(join(targetDir, 'SKILL.md'), readFileSync(skillMdSrc, 'utf8'));
1184
+
1185
+ // 4. Copy reference files (schema + ai-guidance + minimal-example + guide).
1186
+ const refCopies: [string, string, string][] = [
1187
+ ['@specverse/entities', 'schema/SPECVERSE-SCHEMA.json', 'reference/schema.json'],
1188
+ ['@specverse/entities', 'schema/SPECVERSE-SCHEMA-AI.yaml', 'reference/ai-guidance.yaml'],
1189
+ ['@specverse/entities', 'schema/MINIMAL-SYNTAX-REFERENCE.specly', 'reference/minimal-example.specly'],
1190
+ ['@specverse/self', 'docs/guides/SPECVERSE-COMPLETE-GUIDE.md', 'reference/guide.md'],
1191
+ ];
1192
+ let refCopied = 0;
1193
+ for (const [pkg, rel, dest] of refCopies) {
1194
+ const src = resolvePkg(pkg, rel);
1195
+ if (src && existsSync(src)) {
1196
+ writeFileSync(join(targetDir, dest), readFileSync(src, 'utf8'));
1197
+ refCopied++;
1198
+ } else {
1199
+ console.warn(' (skipped ' + dest + ' \u2014 ' + pkg + '/' + rel + ' not found)');
1200
+ }
1201
+ }
1202
+
1203
+ // 5. Build cli-reference.md from the running CLI's own \`--help\`.
1204
+ // Minimal content \u2014 a pointer to the spv help and a static summary.
1205
+ const cliRef = [
1206
+ '# SpecVerse CLI Reference',
1207
+ '',
1208
+ 'Run \`spv <command> --help\` for flag details on any command. Common subcommands:',
1209
+ '',
1210
+ '| Command | Purpose |',
1211
+ '|---|---|',
1212
+ '| \`spv init <name>\` | Scaffold a new project (templates: default / full-stack / backend-only / frontend-only) |',
1213
+ '| \`spv validate <spec>\` | Parse + schema-validate a .specly file |',
1214
+ '| \`spv validate-bundle <dir>\` | Validate an entity bundle (for engine extenders) |',
1215
+ '| \`spv infer <spec>\` | Expand a minimal spec to full architecture (controllers / services / events / views) |',
1216
+ '| \`spv realize all <spec>\` | Generate production code from inferred spec + manifest |',
1217
+ '| \`spv gen diagrams <spec>\` | Emit mermaid diagrams (ER, lifecycle, architecture) |',
1218
+ '| \`spv ai template <op> <spec>\` | Dump a canonical workflow prompt filled in for a spec |',
1219
+ '| \`spv smoke\` | Verify the \`spv\` install end-to-end |',
1220
+ '| \`spv skill install [--global\\\\|--project]\` | Reinstall / refresh this skill |',
1221
+ '',
1222
+ 'For CI invocations, see the \`SPECVERSE-TOOLING.md\` guide in \`@specverse/self/docs/guides/\`.',
1223
+ '',
1224
+ ].join('\\n');
1225
+ writeFileSync(join(targetDir, 'reference', 'cli-reference.md'), cliRef);
1226
+
1227
+ // 6. Emit one workflow markdown per prompt YAML in @specverse/engines/assets/prompts/core/standard/default.
1228
+ const yamlLib = await import('js-yaml').catch(() => null) as any;
1229
+ const workflowsDir = resolvePkg('@specverse/engines', 'assets/prompts/core/standard/default');
1230
+ let workflowCount = 0;
1231
+ if (yamlLib && workflowsDir && existsSync(workflowsDir)) {
1232
+ const entries = readdirSync(workflowsDir).filter((f: string) => f.endsWith('.prompt.yaml'));
1233
+ for (const entry of entries) {
1234
+ try {
1235
+ const parsed = yamlLib.load(readFileSync(join(workflowsDir, entry), 'utf8')) as any;
1236
+ if (!parsed?.name) continue;
1237
+ const md = buildWorkflowMarkdown(parsed);
1238
+ writeFileSync(join(targetDir, 'workflows', parsed.name + '.md'), md);
1239
+ workflowCount++;
1240
+ } catch (err: any) {
1241
+ console.warn(' (skipped workflow ' + entry + ' \u2014 ' + err.message + ')');
1242
+ }
1243
+ }
1244
+ } else {
1245
+ console.warn(' (no workflows emitted \u2014 js-yaml or @specverse/engines prompts unavailable)');
1246
+ }
1247
+
1248
+ // 7. Report.
1249
+ console.log('Installed SpecVerse skill \u2192 ' + targetDir);
1250
+ console.log(' SKILL.md + ' + refCopied + ' reference file(s) + ' + workflowCount + ' workflow(s)');
1251
+ if (!options.global && !options.target) {
1252
+ console.log('');
1253
+ console.log('Tip: use --global to install to ~/.claude/skills/ for cross-project use.');
1254
+ }
1255
+
1256
+ function buildWorkflowMarkdown(parsed: any): string {
1257
+ const parts: string[] = [];
1258
+ parts.push('# SpecVerse workflow: ' + parsed.name);
1259
+ parts.push('');
1260
+ if (parsed.description) {
1261
+ parts.push('> ' + parsed.description);
1262
+ parts.push('');
1263
+ }
1264
+ const vars = Array.isArray(parsed?.user?.variables) ? parsed.user.variables : [];
1265
+ if (vars.length > 0) {
1266
+ parts.push('## Inputs');
1267
+ parts.push('');
1268
+ parts.push('| Variable | Required | Description |');
1269
+ parts.push('|---|---|---|');
1270
+ for (const v of vars) {
1271
+ const req = v.required === false ? 'no' : 'yes';
1272
+ const desc = (v.description || '').replace(/\\|/g, '\\\\|');
1273
+ parts.push('| \`' + v.name + '\` | ' + req + ' | ' + desc + ' |');
1274
+ }
1275
+ parts.push('');
1276
+ }
1277
+ if (parsed?.system?.role) {
1278
+ parts.push('## Role');
1279
+ parts.push('');
1280
+ parts.push(String(parsed.system.role).trim());
1281
+ parts.push('');
1282
+ }
1283
+ if (parsed?.system?.context) {
1284
+ parts.push('## Instructions');
1285
+ parts.push('');
1286
+ parts.push(String(parsed.system.context).trim());
1287
+ parts.push('');
1288
+ }
1289
+ if (parsed?.user?.template) {
1290
+ parts.push('## Task template');
1291
+ parts.push('');
1292
+ parts.push('When invoking this workflow, fill the variables above into the template below:');
1293
+ parts.push('');
1294
+ parts.push('\`\`\`');
1295
+ parts.push(String(parsed.user.template).trim());
1296
+ parts.push('\`\`\`');
1297
+ parts.push('');
1298
+ }
1299
+ return parts.join('\\n');
1300
+ }`
1117
1301
  }
1118
1302
  };
1119
1303
  function generateLeafCommand(name, description, commandStr, optionDefs, actionParams, serviceRef, exitCodes) {
@@ -1,12 +1,14 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "fs";
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
+ import { createRequire } from "module";
2
3
  import { join } from "path";
4
+ const require2 = createRequire(import.meta.url);
3
5
  function generateMCPServer(context) {
4
6
  const { spec, outputDir } = context;
5
7
  const mcpDir = join(outputDir || ".", "tools", "specverse-mcp");
6
8
  const srcDir = join(mcpDir, "src");
7
9
  if (!existsSync(srcDir)) mkdirSync(srcDir, { recursive: true });
8
10
  const distribution = extractMCPDistribution(spec);
9
- const version = distribution?.version || spec?.metadata?.version || spec?.version || "5.0.0";
11
+ const version = distribution?.version || resolveSelfVersion() || spec?.metadata?.version || spec?.version || "5.1.0";
10
12
  const description = distribution?.description || "SpecVerse MCP server \u2014 exposes the specverse CLI as MCP tools and the live spec schema + docs as MCP resources.";
11
13
  const displayName = distribution?.displayName || "SpecVerse MCP";
12
14
  const cliCommands = extractCLITools(spec);
@@ -14,10 +16,20 @@ function generateMCPServer(context) {
14
16
  writeFileSync(join(mcpDir, "tsconfig.json"), generateTsconfig());
15
17
  writeFileSync(join(srcDir, "server.ts"), generateServer(displayName, version));
16
18
  writeFileSync(join(srcDir, "cli-runner.ts"), generateCliRunner());
17
- writeFileSync(join(srcDir, "resources.ts"), generateResources());
19
+ writeFileSync(join(srcDir, "resources.ts"), generateResources(cliCommands));
18
20
  writeFileSync(join(srcDir, "tools.ts"), generateTools(cliCommands));
21
+ writeFileSync(join(srcDir, "prompts.ts"), generatePrompts());
19
22
  return `MCP server generated in: ${mcpDir}
20
- ${cliCommands.length} tools (one per CLI command), 2 resources (schema + docs)`;
23
+ ${cliCommands.length} tools, 5 resources, 6 prompts (derived from @specverse/engines/assets/prompts)`;
24
+ }
25
+ function resolveSelfVersion() {
26
+ try {
27
+ const pkgPath = require2.resolve("@specverse/self/package.json");
28
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
29
+ return pkg.version || null;
30
+ } catch {
31
+ return null;
32
+ }
21
33
  }
22
34
  function extractCLITools(spec) {
23
35
  const out = [];
@@ -118,10 +130,13 @@ function generatePackageJson(version, description) {
118
130
  },
119
131
  dependencies: {
120
132
  "@modelcontextprotocol/sdk": "^1.17.4",
121
- "@specverse/entities": "^5.0.0",
122
- "@specverse/self": "^5.0.0"
133
+ "@specverse/engines": "^6.0.0",
134
+ "@specverse/entities": "^5.1.0",
135
+ "@specverse/self": "^5.3.0",
136
+ "js-yaml": "^4.1.0"
123
137
  },
124
138
  devDependencies: {
139
+ "@types/js-yaml": "^4.0.9",
125
140
  "@types/node": "^20.19.11",
126
141
  typescript: "^5.9.2"
127
142
  },
@@ -156,13 +171,15 @@ function generateTsconfig() {
156
171
  }, null, 2) + "\n";
157
172
  }
158
173
  function generateServer(displayName, version) {
174
+ const instructions = SERVER_INSTRUCTIONS;
159
175
  return `#!/usr/bin/env node
160
176
  /**
161
177
  * SpecVerse MCP Server \u2014 stdio transport.
162
178
  *
163
- * Wires the MCP protocol to the generated tool registry + live resources.
164
- * No handwritten business logic \u2014 everything is derived from the spec
165
- * (tools from CLI commands) or from @specverse/entities (schema + docs).
179
+ * Wires the MCP protocol to the generated tool registry + live resources
180
+ * + canonical workflow prompts. Everything is derived from the spec
181
+ * (tools from CLI commands, resources from @specverse/entities + @specverse/self,
182
+ * prompts from @specverse/engines/assets/prompts/core/standard/default).
166
183
  */
167
184
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
168
185
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -171,13 +188,21 @@ import {
171
188
  CallToolRequestSchema,
172
189
  ListResourcesRequestSchema,
173
190
  ReadResourceRequestSchema,
191
+ ListPromptsRequestSchema,
192
+ GetPromptRequestSchema,
174
193
  } from '@modelcontextprotocol/sdk/types.js';
175
194
  import { TOOLS, callTool } from './tools.js';
176
195
  import { RESOURCES, readResource } from './resources.js';
196
+ import { PROMPTS, getPrompt } from './prompts.js';
197
+
198
+ const INSTRUCTIONS = ${JSON.stringify(instructions)};
177
199
 
178
200
  const server = new Server(
179
201
  { name: ${JSON.stringify(displayName)}, version: ${JSON.stringify(version)} },
180
- { capabilities: { tools: {}, resources: {} } },
202
+ {
203
+ capabilities: { tools: {}, resources: {}, prompts: {} },
204
+ instructions: INSTRUCTIONS,
205
+ },
181
206
  );
182
207
 
183
208
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
@@ -206,10 +231,49 @@ server.setRequestHandler(ReadResourceRequestSchema, async (req: { params: { uri:
206
231
  return readResource(req.params.uri);
207
232
  });
208
233
 
234
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
235
+ prompts: PROMPTS.map(p => ({
236
+ name: p.name,
237
+ description: p.description,
238
+ arguments: p.arguments,
239
+ })),
240
+ }));
241
+
242
+ server.setRequestHandler(GetPromptRequestSchema, async (req: { params: { name: string; arguments?: Record<string, string> } }) => {
243
+ const { name, arguments: args } = req.params;
244
+ return getPrompt(name, args ?? {});
245
+ });
246
+
209
247
  const transport = new StdioServerTransport();
210
248
  await server.connect(transport);
211
249
  `;
212
250
  }
251
+ const SERVER_INSTRUCTIONS = [
252
+ "SpecVerse is a declarative specification language. You describe WHAT a system does in a .specly file, and the engines generate HOW \u2014 backend (Fastify/Prisma), frontend (React/Tailwind), CLI, tools, diagrams.",
253
+ "",
254
+ "Before using tools, read these resources to ground yourself in the language:",
255
+ "- specverse://guide \u2014 canonical user guide (spec language, CLI reference, convention patterns)",
256
+ "- specverse://ai-guidance \u2014 curated hints for generating valid specs",
257
+ "- specverse://minimal-example \u2014 one minimal .specly showing every feature in ~100 lines",
258
+ "- specverse://schema \u2014 the JSON Schema (draft 2020-12) for .specly validation",
259
+ "- specverse://cli-reference \u2014 every spv CLI subcommand with flags + arguments",
260
+ "",
261
+ "Common high-leverage workflows are exposed as MCP prompts (preferred entry point):",
262
+ "- create: natural-language requirements \u2192 minimal .specly",
263
+ "- analyse: existing codebase \u2192 .specly that captures what is implemented",
264
+ "- materialise: .specly \u2192 production code",
265
+ "- realize: generate deployment configs",
266
+ "- behavior: generate AI behavior function stubs",
267
+ "- app-demo: interactive spec creation/modification for the runtime interpreter",
268
+ "",
269
+ "Tools map 1:1 to the spv CLI. Key distinctions:",
270
+ "- validate: check a .specly file parses + matches the schema",
271
+ "- validate-manifest: check an implementation manifest resolves cleanly",
272
+ "- validate-bundle: check an entity bundle (schema + examples + tests + docs + behaviour facets)",
273
+ "- infer: expand a minimal .specly to full architecture (controllers / services / events / views)",
274
+ "- realize: turn inferred spec + manifest into generated code (Fastify / Prisma / React / CLI / tools)",
275
+ "- init: scaffold a new project from a template"
276
+ ].join("\n");
213
277
  function generateCliRunner() {
214
278
  return `/**
215
279
  * Thin specverse CLI runner.
@@ -309,12 +373,16 @@ export async function callTool(name: string, args: Record<string, any>) {
309
373
  }
310
374
  `;
311
375
  }
312
- function generateResources() {
376
+ function generateResources(tools) {
377
+ const cliReference = buildCliReferenceMarkdown(tools);
313
378
  return `/**
314
- * Resource registry \u2014 exposes the live SpecVerse schema + user guide as
315
- * MCP resources. Schema is read from @specverse/entities (composed fragment
316
- * aggregate); guide is read from @specverse/self (canonical home for all
317
- * user-facing guides).
379
+ * Resource registry \u2014 exposes the canonical SpecVerse reference material as
380
+ * MCP resources:
381
+ * - schema (JSON Schema draft 2020-12, from @specverse/entities)
382
+ * - ai-guidance (curated LLM hints, from @specverse/entities)
383
+ * - minimal-example (one .specly covering all features, from @specverse/entities)
384
+ * - guide (user guide, from @specverse/self)
385
+ * - cli-reference (derived from the spec at realize time, embedded below)
318
386
  */
319
387
  import { readFileSync } from 'fs';
320
388
  import { createRequire } from 'module';
@@ -340,6 +408,8 @@ function resolveSelfFile(relative: string): string {
340
408
  return join(dirname(pkg), relative);
341
409
  }
342
410
 
411
+ const CLI_REFERENCE_MARKDOWN = ${JSON.stringify(cliReference)};
412
+
343
413
  export const RESOURCES: Resource[] = [
344
414
  {
345
415
  uri: 'specverse://schema',
@@ -351,6 +421,26 @@ export const RESOURCES: Resource[] = [
351
421
  mimeType: 'application/json',
352
422
  }),
353
423
  },
424
+ {
425
+ uri: 'specverse://ai-guidance',
426
+ name: 'SpecVerse AI Guidance Schema',
427
+ description: 'YAML schema annotated with examples and LLM guidance for generating valid specs. Read this before authoring or mutating .specly files.',
428
+ mimeType: 'application/x-yaml',
429
+ resolve: () => ({
430
+ text: readFileSync(resolveEntitiesFile('schema/SPECVERSE-SCHEMA-AI.yaml'), 'utf8'),
431
+ mimeType: 'application/x-yaml',
432
+ }),
433
+ },
434
+ {
435
+ uri: 'specverse://minimal-example',
436
+ name: 'SpecVerse Minimal Example',
437
+ description: 'One complete minimal .specly demonstrating every feature (~100 lines) \u2014 component, models, relationships, lifecycle, CURVED controller, service, view, event, manifest, deployment.',
438
+ mimeType: 'text/x-yaml',
439
+ resolve: () => ({
440
+ text: readFileSync(resolveEntitiesFile('schema/MINIMAL-SYNTAX-REFERENCE.specly'), 'utf8'),
441
+ mimeType: 'text/x-yaml',
442
+ }),
443
+ },
354
444
  {
355
445
  uri: 'specverse://guide',
356
446
  name: 'SpecVerse Complete Guide',
@@ -361,6 +451,16 @@ export const RESOURCES: Resource[] = [
361
451
  mimeType: 'text/markdown',
362
452
  }),
363
453
  },
454
+ {
455
+ uri: 'specverse://cli-reference',
456
+ name: 'SpecVerse CLI Reference',
457
+ description: 'Every spv CLI subcommand with its arguments, flags, and exit codes. Derived from the spec at realize time.',
458
+ mimeType: 'text/markdown',
459
+ resolve: () => ({
460
+ text: CLI_REFERENCE_MARKDOWN,
461
+ mimeType: 'text/markdown',
462
+ }),
463
+ },
364
464
  ];
365
465
 
366
466
  const BY_URI = new Map<string, Resource>(RESOURCES.map(r => [r.uri, r]));
@@ -377,6 +477,180 @@ export async function readResource(uri: string) {
377
477
  }
378
478
  `;
379
479
  }
480
+ function buildCliReferenceMarkdown(tools) {
481
+ const lines = [];
482
+ lines.push("# SpecVerse CLI Reference");
483
+ lines.push("");
484
+ 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).");
485
+ lines.push("");
486
+ for (const tool of tools) {
487
+ const cliCmd = tool.cliArgs.join(" ");
488
+ lines.push(`## \`spv ${cliCmd}\``);
489
+ lines.push("");
490
+ lines.push(tool.description);
491
+ lines.push("");
492
+ const props = tool.inputSchema?.properties || {};
493
+ const required = new Set(tool.inputSchema?.required || []);
494
+ const positional = new Set(tool.positional);
495
+ const argEntries = Object.entries(props);
496
+ if (argEntries.length > 0) {
497
+ lines.push("| Name | Kind | Required | Type | Description |");
498
+ lines.push("|---|---|---|---|---|");
499
+ for (const [name, schema] of argEntries) {
500
+ const kind = positional.has(name) ? "positional" : "flag";
501
+ const req = required.has(name) ? "yes" : "no";
502
+ const type = schema?.type || "string";
503
+ const desc = (schema?.description || "").replace(/\|/g, "\\|");
504
+ lines.push(`| \`${name}\` | ${kind} | ${req} | ${type} | ${desc} |`);
505
+ }
506
+ lines.push("");
507
+ }
508
+ }
509
+ return lines.join("\n");
510
+ }
511
+ function generatePrompts() {
512
+ return `/**
513
+ * Prompt registry \u2014 reads the canonical workflow prompts from
514
+ * @specverse/engines/assets/prompts/core/standard/default/ at startup and
515
+ * serves them as MCP prompts. Each YAML file (create, analyse, materialise,
516
+ * realize, behavior, app-demo) becomes one invocable prompt.
517
+ */
518
+ import { readdirSync, readFileSync } from 'fs';
519
+ import { createRequire } from 'module';
520
+ import { dirname, join, basename } from 'path';
521
+ import { load as yamlLoad } from 'js-yaml';
522
+
523
+ const require = createRequire(import.meta.url);
524
+
525
+ export interface PromptArgument {
526
+ name: string;
527
+ description?: string;
528
+ required: boolean;
529
+ }
530
+
531
+ export interface Prompt {
532
+ name: string;
533
+ description: string;
534
+ arguments: PromptArgument[];
535
+ source: any; // parsed YAML \u2014 kept so getPrompt can re-render
536
+ }
537
+
538
+ function resolveEntitiesFile(relative: string): string {
539
+ const pkg = require.resolve('@specverse/entities/package.json');
540
+ return join(dirname(pkg), relative);
541
+ }
542
+
543
+ function resolveEnginesDir(relative: string): string {
544
+ const pkg = require.resolve('@specverse/engines/package.json');
545
+ return join(dirname(pkg), relative);
546
+ }
547
+
548
+ // Auto-filled template vars \u2014 paths into @specverse/entities/schema/
549
+ const SCHEMA_VAR_PATHS: Record<string, string> = {
550
+ aiSchemaPath: resolveEntitiesFile('schema/SPECVERSE-SCHEMA-AI.yaml'),
551
+ referenceSchemaPath: resolveEntitiesFile('schema/MINIMAL-SYNTAX-REFERENCE.specly'),
552
+ referenceExamplePath: resolveEntitiesFile('schema/MINIMAL-SYNTAX-REFERENCE.specly'),
553
+ };
554
+
555
+ // User-facing vars the server does NOT auto-fill \u2014 surfaced as MCP prompt args.
556
+ const USER_FACING = new Set<string>([
557
+ ...Object.keys(SCHEMA_VAR_PATHS),
558
+ ]);
559
+
560
+ function loadPrompts(): Prompt[] {
561
+ const promptsDir = resolveEnginesDir('assets/prompts/core/standard/default');
562
+ const entries = readdirSync(promptsDir).filter(f => f.endsWith('.prompt.yaml'));
563
+ const prompts: Prompt[] = [];
564
+ for (const entry of entries) {
565
+ const full = join(promptsDir, entry);
566
+ try {
567
+ const parsed = yamlLoad(readFileSync(full, 'utf8')) as any;
568
+ if (!parsed?.name) continue;
569
+ const args = deriveArguments(parsed);
570
+ prompts.push({
571
+ name: parsed.name,
572
+ description: parsed.description || \`SpecVerse \${parsed.name} workflow\`,
573
+ arguments: args,
574
+ source: parsed,
575
+ });
576
+ } catch (err) {
577
+ // One bad prompt shouldn't kill the server; skip it.
578
+ console.error(\`[prompts] skipping \${basename(entry)}: \${(err as Error).message}\`);
579
+ }
580
+ }
581
+ return prompts;
582
+ }
583
+
584
+ /**
585
+ * Every SpecVerse prompt YAML declares user.variables[] in the canonical shape
586
+ * { name, type?, required?, description?, default? }. We surface the declared
587
+ * list verbatim, filtering out auto-filled schema-path vars.
588
+ */
589
+ function deriveArguments(parsed: any): PromptArgument[] {
590
+ const declared = parsed?.user?.variables;
591
+ if (!Array.isArray(declared)) return [];
592
+ return declared
593
+ .filter((v: any) => v && typeof v === 'object' && typeof v.name === 'string')
594
+ .filter((v: any) => !USER_FACING.has(v.name))
595
+ .map((v: any) => ({
596
+ name: v.name,
597
+ description: v.description || inferVarDescription(v.name, parsed),
598
+ required: v.required !== false,
599
+ }));
600
+ }
601
+
602
+ function inferVarDescription(varName: string, parsed: any): string {
603
+ // Friendly defaults for the known common vars; fall back to a humanised
604
+ // version of the variable name.
605
+ const defaults: Record<string, string> = {
606
+ requirements: 'Natural-language requirements to extract a specification from',
607
+ scale: 'Project scale \u2014 personal, business, or enterprise',
608
+ preferredTech: 'Preferred technology stack hint (e.g. "nextjs", "nestjs", "fastify+prisma", "auto")',
609
+ implementationPath: 'Path to the existing codebase to reverse-engineer into a .specly',
610
+ specPath: 'Path to an existing .specly file',
611
+ manifestPath: 'Path to an implementation manifest (manifests/implementation.yaml)',
612
+ outputDir: 'Output directory for generated artifacts',
613
+ modelName: 'Name of the model the behavior is attached to',
614
+ behaviorName: 'Name of the behavior method on the model',
615
+ };
616
+ if (defaults[varName]) return defaults[varName];
617
+ return varName.replace(/([A-Z])/g, ' $1').replace(/^./, c => c.toUpperCase());
618
+ }
619
+
620
+ function substitute(text: string, values: Record<string, string>): string {
621
+ return text.replace(/\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\}\\}/g, (match, name) => {
622
+ if (name in values) return values[name];
623
+ if (name in SCHEMA_VAR_PATHS) return SCHEMA_VAR_PATHS[name];
624
+ return match; // unknown var \u2014 leave the placeholder so it's visible
625
+ });
626
+ }
627
+
628
+ function renderMessages(prompt: Prompt, args: Record<string, string>): { role: 'user'; content: { type: 'text'; text: string } }[] {
629
+ const src = prompt.source;
630
+ const sections: string[] = [];
631
+ if (src?.system?.role) sections.push(\`[System role]\\n\${substitute(src.system.role, args).trim()}\`);
632
+ if (src?.system?.context) sections.push(\`[Context]\\n\${substitute(src.system.context, args).trim()}\`);
633
+ if (src?.user?.template) sections.push(substitute(src.user.template, args).trim());
634
+ const text = sections.filter(Boolean).join('\\n\\n');
635
+ return [{ role: 'user', content: { type: 'text', text } }];
636
+ }
637
+
638
+ export const PROMPTS: Prompt[] = loadPrompts();
639
+
640
+ const BY_NAME = new Map<string, Prompt>(PROMPTS.map(p => [p.name, p]));
641
+
642
+ export async function getPrompt(name: string, args: Record<string, string>) {
643
+ const prompt = BY_NAME.get(name);
644
+ if (!prompt) {
645
+ throw new Error(\`Unknown prompt: \${name}\`);
646
+ }
647
+ return {
648
+ description: prompt.description,
649
+ messages: renderMessages(prompt, args),
650
+ };
651
+ }
652
+ `;
653
+ }
380
654
  export {
381
655
  generateMCPServer as default
382
656
  };