@contractspec/bundle.workspace 1.44.1 → 1.45.1

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 (149) hide show
  1. package/README.md +5 -1
  2. package/dist/_virtual/rolldown_runtime.js +1 -19
  3. package/dist/ai/agents/cursor-agent.js +4 -4
  4. package/dist/ai/agents/cursor-agent.js.map +1 -1
  5. package/dist/ai/client.d.ts +14 -0
  6. package/dist/ai/client.d.ts.map +1 -1
  7. package/dist/ai/client.js +27 -1
  8. package/dist/ai/client.js.map +1 -1
  9. package/dist/ai/index.d.ts +1 -9
  10. package/dist/ai/index.js +1 -20
  11. package/dist/ai/prompts/index.js +1 -1
  12. package/dist/index.d.ts +42 -19
  13. package/dist/index.js +39 -150
  14. package/dist/services/agent-guide/agent-guide-service.js +3 -3
  15. package/dist/services/agent-guide/agent-guide-service.js.map +1 -1
  16. package/dist/services/ci-check/ci-check-service.d.ts.map +1 -1
  17. package/dist/services/ci-check/ci-check-service.js +69 -2
  18. package/dist/services/ci-check/ci-check-service.js.map +1 -1
  19. package/dist/services/ci-check/types.d.ts +1 -1
  20. package/dist/services/ci-check/types.d.ts.map +1 -1
  21. package/dist/services/ci-check/types.js +4 -2
  22. package/dist/services/ci-check/types.js.map +1 -1
  23. package/dist/services/config.d.ts +1 -11
  24. package/dist/services/config.d.ts.map +1 -1
  25. package/dist/services/config.js +2 -16
  26. package/dist/services/config.js.map +1 -1
  27. package/dist/services/create/ai-generator.d.ts +84 -0
  28. package/dist/services/create/ai-generator.d.ts.map +1 -0
  29. package/dist/services/create/ai-generator.js +178 -0
  30. package/dist/services/create/ai-generator.js.map +1 -0
  31. package/dist/services/create/index.d.ts +27 -0
  32. package/dist/services/create/index.d.ts.map +1 -0
  33. package/dist/services/create/index.js +36 -0
  34. package/dist/services/create/index.js.map +1 -0
  35. package/dist/services/create/templates.d.ts +21 -0
  36. package/dist/services/create/templates.d.ts.map +1 -0
  37. package/dist/services/create/templates.js +37 -0
  38. package/dist/services/create/templates.js.map +1 -0
  39. package/dist/services/docs/docs-service.d.ts +19 -0
  40. package/dist/services/docs/docs-service.d.ts.map +1 -0
  41. package/dist/services/docs/docs-service.js +41 -0
  42. package/dist/services/docs/docs-service.js.map +1 -0
  43. package/dist/services/docs/index.d.ts +1 -0
  44. package/dist/services/docs/index.js +1 -0
  45. package/dist/services/doctor/checks/cli.js +3 -3
  46. package/dist/services/doctor/checks/cli.js.map +1 -1
  47. package/dist/services/doctor/checks/index.js +1 -0
  48. package/dist/services/doctor/checks/layers.js +139 -0
  49. package/dist/services/doctor/checks/layers.js.map +1 -0
  50. package/dist/services/doctor/doctor-service.d.ts.map +1 -1
  51. package/dist/services/doctor/doctor-service.js +2 -0
  52. package/dist/services/doctor/doctor-service.js.map +1 -1
  53. package/dist/services/doctor/types.d.ts +1 -1
  54. package/dist/services/doctor/types.d.ts.map +1 -1
  55. package/dist/services/doctor/types.js +4 -2
  56. package/dist/services/doctor/types.js.map +1 -1
  57. package/dist/services/formatter.d.ts +15 -0
  58. package/dist/services/formatter.d.ts.map +1 -0
  59. package/dist/services/formatter.js +26 -0
  60. package/dist/services/formatter.js.map +1 -0
  61. package/dist/services/impact/formatters.d.ts +5 -5
  62. package/dist/services/impact/formatters.d.ts.map +1 -1
  63. package/dist/services/impact/formatters.js.map +1 -1
  64. package/dist/services/impact/impact-detection-service.js +6 -6
  65. package/dist/services/impact/impact-detection-service.js.map +1 -1
  66. package/dist/services/impact/types.d.ts +3 -3
  67. package/dist/services/implementation/resolver.js +1 -1
  68. package/dist/services/implementation/resolver.js.map +1 -1
  69. package/dist/services/implementation/types.d.ts +1 -1
  70. package/dist/services/index.d.ts +31 -5
  71. package/dist/services/index.js +30 -4
  72. package/dist/services/integrity-diagram.js +1 -1
  73. package/dist/services/integrity-diagram.js.map +1 -1
  74. package/dist/services/integrity.d.ts +1 -1
  75. package/dist/services/integrity.js.map +1 -1
  76. package/dist/services/layer-discovery.d.ts +77 -0
  77. package/dist/services/layer-discovery.d.ts.map +1 -0
  78. package/dist/services/layer-discovery.js +121 -0
  79. package/dist/services/layer-discovery.js.map +1 -0
  80. package/dist/services/llm/index.d.ts +28 -0
  81. package/dist/services/llm/index.d.ts.map +1 -0
  82. package/dist/services/llm/index.js +187 -0
  83. package/dist/services/llm/index.js.map +1 -0
  84. package/dist/services/llm/verify-static.d.ts +26 -0
  85. package/dist/services/llm/verify-static.d.ts.map +1 -0
  86. package/dist/services/llm/verify-static.js +82 -0
  87. package/dist/services/llm/verify-static.js.map +1 -0
  88. package/dist/services/openapi/import-service.d.ts.map +1 -1
  89. package/dist/services/openapi/import-service.js +98 -4
  90. package/dist/services/openapi/import-service.js.map +1 -1
  91. package/dist/services/openapi/sync-service.js +1 -1
  92. package/dist/services/setup/config-generators.js +1 -1
  93. package/dist/services/setup/config-generators.js.map +1 -1
  94. package/dist/services/sync.d.ts +2 -1
  95. package/dist/services/sync.d.ts.map +1 -1
  96. package/dist/services/sync.js +2 -1
  97. package/dist/services/sync.js.map +1 -1
  98. package/dist/services/test/index.d.ts +1 -0
  99. package/dist/services/test/index.js +1 -0
  100. package/dist/services/test/test-service.d.ts +22 -0
  101. package/dist/services/test/test-service.d.ts.map +1 -0
  102. package/dist/services/test/test-service.js +81 -0
  103. package/dist/services/test/test-service.js.map +1 -0
  104. package/dist/services/validate/blueprint-validator.d.ts +23 -0
  105. package/dist/services/validate/blueprint-validator.d.ts.map +1 -0
  106. package/dist/services/validate/blueprint-validator.js +50 -0
  107. package/dist/services/validate/blueprint-validator.js.map +1 -0
  108. package/dist/services/validate/implementation-agent-validator.d.ts +20 -0
  109. package/dist/services/validate/implementation-agent-validator.d.ts.map +1 -0
  110. package/dist/services/validate/implementation-agent-validator.js +42 -0
  111. package/dist/services/validate/implementation-agent-validator.js.map +1 -0
  112. package/dist/services/{validate-implementation.d.ts → validate/implementation-validator.d.ts} +3 -3
  113. package/dist/services/validate/implementation-validator.d.ts.map +1 -0
  114. package/dist/services/{validate-implementation.js → validate/implementation-validator.js} +2 -2
  115. package/dist/services/validate/implementation-validator.js.map +1 -0
  116. package/dist/services/validate/index.d.ts +5 -0
  117. package/dist/services/validate/index.js +5 -0
  118. package/dist/services/{validate.d.ts → validate/spec-validator.d.ts} +5 -4
  119. package/dist/services/validate/spec-validator.d.ts.map +1 -0
  120. package/dist/services/{validate.js → validate/spec-validator.js} +6 -4
  121. package/dist/services/validate/spec-validator.js.map +1 -0
  122. package/dist/services/validate/tenant-validator.d.ts +21 -0
  123. package/dist/services/validate/tenant-validator.d.ts.map +1 -0
  124. package/dist/services/validate/tenant-validator.js +165 -0
  125. package/dist/services/validate/tenant-validator.js.map +1 -0
  126. package/dist/services/watch.js +2 -1
  127. package/dist/services/watch.js.map +1 -1
  128. package/dist/templates/data-view.template.js +3 -3
  129. package/dist/templates/data-view.template.js.map +1 -1
  130. package/dist/templates/event.template.js +3 -1
  131. package/dist/templates/event.template.js.map +1 -1
  132. package/dist/templates/operation.template.js +3 -1
  133. package/dist/templates/operation.template.js.map +1 -1
  134. package/dist/types.d.ts +1 -1
  135. package/dist/utils/module-loader.js +41 -0
  136. package/dist/utils/module-loader.js.map +1 -0
  137. package/package.json +9 -9
  138. package/dist/ai/index.d.ts.map +0 -1
  139. package/dist/ai/index.js.map +0 -1
  140. package/dist/index.d.ts.map +0 -1
  141. package/dist/index.js.map +0 -1
  142. package/dist/services/test.d.ts +0 -15
  143. package/dist/services/test.d.ts.map +0 -1
  144. package/dist/services/test.js +0 -30
  145. package/dist/services/test.js.map +0 -1
  146. package/dist/services/validate-implementation.d.ts.map +0 -1
  147. package/dist/services/validate-implementation.js.map +0 -1
  148. package/dist/services/validate.d.ts.map +0 -1
  149. package/dist/services/validate.js.map +0 -1
@@ -0,0 +1,121 @@
1
+ import { isExampleFile, isFeatureFile, scanAllSpecsFromSource, scanExampleSource, scanFeatureSource } from "@contractspec/module.workspace";
2
+
3
+ //#region src/services/layer-discovery.ts
4
+ /**
5
+ * Layer discovery service.
6
+ *
7
+ * Discovers all contract layer files in a workspace:
8
+ * - Features (*.feature.ts)
9
+ * - Examples (example.ts, *.example.ts)
10
+ * - App Configs (*.app-config.ts, *.blueprint.ts)
11
+ * - Workspace Config (.contractsrc.json)
12
+ */
13
+ /**
14
+ * Create an empty layer inventory.
15
+ */
16
+ function createEmptyLayerInventory() {
17
+ return {
18
+ features: /* @__PURE__ */ new Map(),
19
+ examples: /* @__PURE__ */ new Map(),
20
+ appConfigs: /* @__PURE__ */ new Map(),
21
+ workspaceConfigs: /* @__PURE__ */ new Map()
22
+ };
23
+ }
24
+ /**
25
+ * Discover all contract layers in a workspace.
26
+ */
27
+ async function discoverLayers(adapters, options = {}) {
28
+ const { fs, logger } = adapters;
29
+ const inventory = createEmptyLayerInventory();
30
+ const pattern = options.pattern ?? "**/*.{ts,tsx}";
31
+ logger.info("Scanning for contract layer files...");
32
+ const files = await fs.glob({ pattern });
33
+ for (const file of files) {
34
+ if (file.includes("node_modules") || file.includes("/dist/")) continue;
35
+ try {
36
+ const code = await fs.readFile(file);
37
+ if (isFeatureFile(file)) {
38
+ const result = scanFeatureSource(code, file);
39
+ inventory.features.set(result.key, result);
40
+ continue;
41
+ }
42
+ if (isExampleFile(file)) {
43
+ const result = scanExampleSource(code, file);
44
+ inventory.examples.set(result.key, result);
45
+ continue;
46
+ }
47
+ if (file.includes(".app-config.") || file.includes(".blueprint.")) {
48
+ const specs = scanAllSpecsFromSource(code, file);
49
+ for (const spec of specs) if (spec.specType === "app-config" && spec.key) inventory.appConfigs.set(spec.key, spec);
50
+ }
51
+ } catch {}
52
+ }
53
+ try {
54
+ const configFiles = await fs.glob({ pattern: "**/.contractsrc.json" });
55
+ for (const configFile of configFiles) {
56
+ if (configFile.includes("node_modules")) continue;
57
+ try {
58
+ const content = await fs.readFile(configFile);
59
+ const config = JSON.parse(content);
60
+ inventory.workspaceConfigs.set(configFile, {
61
+ file: configFile,
62
+ config,
63
+ valid: true,
64
+ errors: []
65
+ });
66
+ } catch (e) {
67
+ inventory.workspaceConfigs.set(configFile, {
68
+ file: configFile,
69
+ config: {},
70
+ valid: false,
71
+ errors: [e instanceof Error ? e.message : "Parse error"]
72
+ });
73
+ }
74
+ }
75
+ } catch {}
76
+ const stats = {
77
+ features: inventory.features.size,
78
+ examples: inventory.examples.size,
79
+ appConfigs: inventory.appConfigs.size,
80
+ workspaceConfigs: inventory.workspaceConfigs.size,
81
+ total: inventory.features.size + inventory.examples.size + inventory.appConfigs.size + inventory.workspaceConfigs.size
82
+ };
83
+ logger.info(`Discovered ${stats.features} features, ${stats.examples} examples, ${stats.appConfigs} app configs, ${stats.workspaceConfigs} workspace configs`);
84
+ return {
85
+ inventory,
86
+ stats
87
+ };
88
+ }
89
+ /**
90
+ * Get all layers as a flat list with locations.
91
+ */
92
+ function getAllLayerLocations(inventory) {
93
+ const locations = [];
94
+ for (const [key, feature] of inventory.features) locations.push({
95
+ key,
96
+ file: feature.filePath,
97
+ type: "feature"
98
+ });
99
+ for (const [key, example] of inventory.examples) locations.push({
100
+ key,
101
+ version: example.version,
102
+ file: example.filePath,
103
+ type: "example"
104
+ });
105
+ for (const [key, appConfig] of inventory.appConfigs) locations.push({
106
+ key,
107
+ version: appConfig.version,
108
+ file: appConfig.filePath,
109
+ type: "app-config"
110
+ });
111
+ for (const [, config] of inventory.workspaceConfigs) locations.push({
112
+ key: config.file,
113
+ file: config.file,
114
+ type: "workspace-config"
115
+ });
116
+ return locations;
117
+ }
118
+
119
+ //#endregion
120
+ export { createEmptyLayerInventory, discoverLayers, getAllLayerLocations };
121
+ //# sourceMappingURL=layer-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer-discovery.js","names":["locations: LayerLocation[]"],"sources":["../../src/services/layer-discovery.ts"],"sourcesContent":["/**\n * Layer discovery service.\n *\n * Discovers all contract layer files in a workspace:\n * - Features (*.feature.ts)\n * - Examples (example.ts, *.example.ts)\n * - App Configs (*.app-config.ts, *.blueprint.ts)\n * - Workspace Config (.contractsrc.json)\n */\n\nimport {\n isFeatureFile,\n scanFeatureSource,\n isExampleFile,\n scanExampleSource,\n type FeatureScanResult,\n type ExampleScanResult,\n type SpecScanResult,\n scanAllSpecsFromSource,\n} from '@contractspec/module.workspace';\nimport type { FsAdapter } from '../ports/fs';\nimport type { LoggerAdapter } from '../ports/logger';\n\n/**\n * Location of a layer file in the codebase.\n */\nexport interface LayerLocation {\n key: string;\n version?: string;\n file: string;\n type: 'feature' | 'example' | 'app-config' | 'workspace-config';\n}\n\n/**\n * Inventory of all discovered layers.\n */\nexport interface LayerInventory {\n /** Discovered features */\n features: Map<string, FeatureScanResult>;\n /** Discovered examples */\n examples: Map<string, ExampleScanResult>;\n /** Discovered app configs */\n appConfigs: Map<string, SpecScanResult>;\n /** Discovered workspace configs */\n workspaceConfigs: Map<string, WorkspaceConfigInfo>;\n}\n\n/**\n * Workspace config information.\n */\nexport interface WorkspaceConfigInfo {\n file: string;\n config: Record<string, unknown>;\n valid: boolean;\n errors: string[];\n}\n\n/**\n * Options for layer discovery.\n */\nexport interface LayerDiscoveryOptions {\n /** Glob pattern for file discovery */\n pattern?: string;\n /** Scan all packages in monorepo */\n all?: boolean;\n}\n\n/**\n * Result of layer discovery.\n */\nexport interface LayerDiscoveryResult {\n inventory: LayerInventory;\n stats: {\n features: number;\n examples: number;\n appConfigs: number;\n workspaceConfigs: number;\n total: number;\n };\n}\n\n/**\n * Create an empty layer inventory.\n */\nexport function createEmptyLayerInventory(): LayerInventory {\n return {\n features: new Map(),\n examples: new Map(),\n appConfigs: new Map(),\n workspaceConfigs: new Map(),\n };\n}\n\n/**\n * Discover all contract layers in a workspace.\n */\nexport async function discoverLayers(\n adapters: { fs: FsAdapter; logger: LoggerAdapter },\n options: LayerDiscoveryOptions = {}\n): Promise<LayerDiscoveryResult> {\n const { fs, logger } = adapters;\n const inventory = createEmptyLayerInventory();\n\n // Default pattern for spec files\n const pattern = options.pattern ?? '**/*.{ts,tsx}';\n\n // Find all TypeScript files\n logger.info('Scanning for contract layer files...');\n const files = await fs.glob({ pattern });\n\n // Process each file\n for (const file of files) {\n // Skip node_modules and dist\n if (file.includes('node_modules') || file.includes('/dist/')) {\n continue;\n }\n\n try {\n const code = await fs.readFile(file);\n\n // Check for feature files\n if (isFeatureFile(file)) {\n const result = scanFeatureSource(code, file);\n inventory.features.set(result.key, result);\n continue;\n }\n\n // Check for example files\n if (isExampleFile(file)) {\n const result = scanExampleSource(code, file);\n inventory.examples.set(result.key, result);\n continue;\n }\n\n // Check for app-config files\n if (file.includes('.app-config.') || file.includes('.blueprint.')) {\n const specs = scanAllSpecsFromSource(code, file);\n for (const spec of specs) {\n if (spec.specType === 'app-config' && spec.key) {\n inventory.appConfigs.set(spec.key, spec);\n }\n }\n }\n } catch {\n // Skip files that can't be read\n }\n }\n\n // Find workspace config files\n try {\n const configFiles = await fs.glob({ pattern: '**/.contractsrc.json' });\n for (const configFile of configFiles) {\n if (configFile.includes('node_modules')) continue;\n\n try {\n const content = await fs.readFile(configFile);\n const config = JSON.parse(content) as Record<string, unknown>;\n inventory.workspaceConfigs.set(configFile, {\n file: configFile,\n config,\n valid: true,\n errors: [],\n });\n } catch (e) {\n inventory.workspaceConfigs.set(configFile, {\n file: configFile,\n config: {},\n valid: false,\n errors: [e instanceof Error ? e.message : 'Parse error'],\n });\n }\n }\n } catch {\n // Glob failed, no config files found\n }\n\n const stats = {\n features: inventory.features.size,\n examples: inventory.examples.size,\n appConfigs: inventory.appConfigs.size,\n workspaceConfigs: inventory.workspaceConfigs.size,\n total:\n inventory.features.size +\n inventory.examples.size +\n inventory.appConfigs.size +\n inventory.workspaceConfigs.size,\n };\n\n logger.info(\n `Discovered ${stats.features} features, ${stats.examples} examples, ` +\n `${stats.appConfigs} app configs, ${stats.workspaceConfigs} workspace configs`\n );\n\n return { inventory, stats };\n}\n\n/**\n * Get all layers as a flat list with locations.\n */\nexport function getAllLayerLocations(\n inventory: LayerInventory\n): LayerLocation[] {\n const locations: LayerLocation[] = [];\n\n for (const [key, feature] of inventory.features) {\n locations.push({\n key,\n file: feature.filePath,\n type: 'feature',\n });\n }\n\n for (const [key, example] of inventory.examples) {\n locations.push({\n key,\n version: example.version,\n file: example.filePath,\n type: 'example',\n });\n }\n\n for (const [key, appConfig] of inventory.appConfigs) {\n locations.push({\n key,\n version: appConfig.version,\n file: appConfig.filePath,\n type: 'app-config',\n });\n }\n\n for (const [, config] of inventory.workspaceConfigs) {\n locations.push({\n key: config.file,\n file: config.file,\n type: 'workspace-config',\n });\n }\n\n return locations;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoFA,SAAgB,4BAA4C;AAC1D,QAAO;EACL,0BAAU,IAAI,KAAK;EACnB,0BAAU,IAAI,KAAK;EACnB,4BAAY,IAAI,KAAK;EACrB,kCAAkB,IAAI,KAAK;EAC5B;;;;;AAMH,eAAsB,eACpB,UACA,UAAiC,EAAE,EACJ;CAC/B,MAAM,EAAE,IAAI,WAAW;CACvB,MAAM,YAAY,2BAA2B;CAG7C,MAAM,UAAU,QAAQ,WAAW;AAGnC,QAAO,KAAK,uCAAuC;CACnD,MAAM,QAAQ,MAAM,GAAG,KAAK,EAAE,SAAS,CAAC;AAGxC,MAAK,MAAM,QAAQ,OAAO;AAExB,MAAI,KAAK,SAAS,eAAe,IAAI,KAAK,SAAS,SAAS,CAC1D;AAGF,MAAI;GACF,MAAM,OAAO,MAAM,GAAG,SAAS,KAAK;AAGpC,OAAI,cAAc,KAAK,EAAE;IACvB,MAAM,SAAS,kBAAkB,MAAM,KAAK;AAC5C,cAAU,SAAS,IAAI,OAAO,KAAK,OAAO;AAC1C;;AAIF,OAAI,cAAc,KAAK,EAAE;IACvB,MAAM,SAAS,kBAAkB,MAAM,KAAK;AAC5C,cAAU,SAAS,IAAI,OAAO,KAAK,OAAO;AAC1C;;AAIF,OAAI,KAAK,SAAS,eAAe,IAAI,KAAK,SAAS,cAAc,EAAE;IACjE,MAAM,QAAQ,uBAAuB,MAAM,KAAK;AAChD,SAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,aAAa,gBAAgB,KAAK,IACzC,WAAU,WAAW,IAAI,KAAK,KAAK,KAAK;;UAIxC;;AAMV,KAAI;EACF,MAAM,cAAc,MAAM,GAAG,KAAK,EAAE,SAAS,wBAAwB,CAAC;AACtE,OAAK,MAAM,cAAc,aAAa;AACpC,OAAI,WAAW,SAAS,eAAe,CAAE;AAEzC,OAAI;IACF,MAAM,UAAU,MAAM,GAAG,SAAS,WAAW;IAC7C,MAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,cAAU,iBAAiB,IAAI,YAAY;KACzC,MAAM;KACN;KACA,OAAO;KACP,QAAQ,EAAE;KACX,CAAC;YACK,GAAG;AACV,cAAU,iBAAiB,IAAI,YAAY;KACzC,MAAM;KACN,QAAQ,EAAE;KACV,OAAO;KACP,QAAQ,CAAC,aAAa,QAAQ,EAAE,UAAU,cAAc;KACzD,CAAC;;;SAGA;CAIR,MAAM,QAAQ;EACZ,UAAU,UAAU,SAAS;EAC7B,UAAU,UAAU,SAAS;EAC7B,YAAY,UAAU,WAAW;EACjC,kBAAkB,UAAU,iBAAiB;EAC7C,OACE,UAAU,SAAS,OACnB,UAAU,SAAS,OACnB,UAAU,WAAW,OACrB,UAAU,iBAAiB;EAC9B;AAED,QAAO,KACL,cAAc,MAAM,SAAS,aAAa,MAAM,SAAS,aACpD,MAAM,WAAW,gBAAgB,MAAM,iBAAiB,oBAC9D;AAED,QAAO;EAAE;EAAW;EAAO;;;;;AAM7B,SAAgB,qBACd,WACiB;CACjB,MAAMA,YAA6B,EAAE;AAErC,MAAK,MAAM,CAAC,KAAK,YAAY,UAAU,SACrC,WAAU,KAAK;EACb;EACA,MAAM,QAAQ;EACd,MAAM;EACP,CAAC;AAGJ,MAAK,MAAM,CAAC,KAAK,YAAY,UAAU,SACrC,WAAU,KAAK;EACb;EACA,SAAS,QAAQ;EACjB,MAAM,QAAQ;EACd,MAAM;EACP,CAAC;AAGJ,MAAK,MAAM,CAAC,KAAK,cAAc,UAAU,WACvC,WAAU,KAAK;EACb;EACA,SAAS,UAAU;EACnB,MAAM,UAAU;EAChB,MAAM;EACP,CAAC;AAGJ,MAAK,MAAM,GAAG,WAAW,UAAU,iBACjC,WAAU,KAAK;EACb,KAAK,OAAO;EACZ,MAAM,OAAO;EACb,MAAM;EACP,CAAC;AAGJ,QAAO"}
@@ -0,0 +1,28 @@
1
+ import { FsAdapter } from "../../ports/fs.js";
2
+ import { VerificationIssue, formatVerificationReport, verifyImplementationAgainstParsedSpec } from "./verify-static.js";
3
+ import { AgentType, ParsedSpec } from "@contractspec/module.workspace";
4
+
5
+ //#region src/services/llm/index.d.ts
6
+
7
+ /**
8
+ * Generate full markdown context for a feature, including referenced specs.
9
+ */
10
+ declare function generateFeatureContextMarkdown(feature: ParsedSpec, adapters: {
11
+ fs: FsAdapter;
12
+ }): Promise<string>;
13
+ /**
14
+ * Generate an implementation guide for a parsed spec.
15
+ * This logic was extracted from VSCode extension.
16
+ */
17
+ declare function generateGuideFromParsedSpec(spec: ParsedSpec, agent: AgentType): string;
18
+ /**
19
+ * Generate Cursor rules from parsed spec.
20
+ */
21
+ declare function generateCursorRulesFromParsedSpec(spec: ParsedSpec): string;
22
+ /**
23
+ * Export spec to clipboard format suitable for pasting into LLM.
24
+ */
25
+ declare function exportSpecForLLM(spec: ParsedSpec, format: 'guide' | 'rules' | 'prompt' | 'context' | 'full', agent?: AgentType): string;
26
+ //#endregion
27
+ export { exportSpecForLLM, generateCursorRulesFromParsedSpec, generateFeatureContextMarkdown, generateGuideFromParsedSpec };
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/services/llm/index.ts"],"sourcesContent":[],"mappings":";;;;;;;AAgGA;AAiIA;AAqCgB,iBAjPM,8BAAA,CAoPb,OAAyB,EAnPvB,UAmPuB,EAAA,QAAA,EAAA;MAlPhB;IACf;;;;;iBAwEa,2BAAA,OACR,mBACC;;;;iBA+HO,iCAAA,OAAwC;;;;iBAqCxC,gBAAA,OACR,+EAEC"}
@@ -0,0 +1,187 @@
1
+ import { listSpecs } from "../list.js";
2
+ import { formatVerificationReport, verifyImplementationAgainstParsedSpec } from "./verify-static.js";
3
+ import { loadSpecFromSource, specToMarkdown } from "@contractspec/module.workspace";
4
+
5
+ //#region src/services/llm/index.ts
6
+ /**
7
+ * LLM Service
8
+ *
9
+ * Provides services for generating LLM-friendly content from specs.
10
+ * Primarily used by VSCode and CLI for "Ask AI" features.
11
+ */
12
+ /**
13
+ * Generate full markdown context for a feature, including referenced specs.
14
+ */
15
+ async function generateFeatureContextMarkdown(feature, adapters) {
16
+ const parts = [];
17
+ parts.push(specToMarkdown(feature, "context"));
18
+ const allSpecs = await listSpecs(adapters);
19
+ const loadChild = async (ref) => {
20
+ const found = allSpecs.find((s) => s.key === ref.name && s.version === ref.version);
21
+ if (!found) return null;
22
+ return (await loadSpecFromSource(found.filePath)).find((s) => s.meta.key === ref.name) ?? null;
23
+ };
24
+ if (feature.operations?.length) {
25
+ parts.push(`\n## Operations (${feature.operations.length})\n`);
26
+ for (const op of feature.operations) {
27
+ const child = await loadChild(op);
28
+ if (child) parts.push(specToMarkdown(child, "full"));
29
+ else parts.push(`### ${op.name} (v${op.version})\n\n*Spec not found*`);
30
+ parts.push("---");
31
+ }
32
+ }
33
+ if (feature.events?.length) {
34
+ parts.push(`\n## Events (${feature.events.length})\n`);
35
+ for (const ev of feature.events) {
36
+ const child = await loadChild(ev);
37
+ if (child) parts.push(specToMarkdown(child, "full"));
38
+ else parts.push(`### ${ev.name} (v${ev.version})\n\n*Spec not found*`);
39
+ parts.push("---");
40
+ }
41
+ }
42
+ if (feature.presentations?.length) {
43
+ parts.push(`\n## Presentations (${feature.presentations.length})\n`);
44
+ for (const pres of feature.presentations) {
45
+ const child = await loadChild(pres);
46
+ if (child) parts.push(specToMarkdown(child, "full"));
47
+ else parts.push(`### ${pres.name} (v${pres.version})\n\n*Spec not found*`);
48
+ parts.push("---");
49
+ }
50
+ }
51
+ return parts.join("\n");
52
+ }
53
+ /**
54
+ * Generate an implementation guide for a parsed spec.
55
+ * This logic was extracted from VSCode extension.
56
+ */
57
+ function generateGuideFromParsedSpec(spec, agent) {
58
+ const lines = [];
59
+ lines.push(`# Implementation Guide: ${spec.meta.key}`);
60
+ lines.push("");
61
+ lines.push(`**Target Agent**: ${agent}`);
62
+ lines.push(`**Spec Type**: ${spec.specType}${spec.kind ? ` (${spec.kind})` : ""}`);
63
+ lines.push("");
64
+ if (spec.meta.description) {
65
+ lines.push("## Overview");
66
+ lines.push("");
67
+ lines.push(spec.meta.description);
68
+ lines.push("");
69
+ }
70
+ if (spec.meta.goal) {
71
+ lines.push("## Goal");
72
+ lines.push("");
73
+ lines.push(spec.meta.goal);
74
+ lines.push("");
75
+ }
76
+ if (spec.meta.context) {
77
+ lines.push("## Context");
78
+ lines.push("");
79
+ lines.push(spec.meta.context);
80
+ lines.push("");
81
+ }
82
+ lines.push("## Implementation Steps");
83
+ lines.push("");
84
+ if (spec.specType === "operation") {
85
+ lines.push(`1. Create the ${spec.kind ?? "operation"} handler function`);
86
+ if (spec.hasIo) {
87
+ lines.push("2. Implement input validation according to the schema");
88
+ lines.push("3. Implement the core business logic");
89
+ lines.push("4. Return output matching the expected schema");
90
+ }
91
+ if (spec.hasPolicy) lines.push("5. Enforce authorization and policies");
92
+ if (spec.emittedEvents && spec.emittedEvents.length > 0) {
93
+ lines.push("6. Emit events on success:");
94
+ for (const ev of spec.emittedEvents) lines.push(` - \`${ev.name}\` (v${ev.version})`);
95
+ }
96
+ lines.push("7. Add error handling for expected failure cases");
97
+ lines.push("8. Write tests covering success and error scenarios");
98
+ } else if (spec.specType === "feature") {
99
+ lines.push("1. Set up the feature module structure");
100
+ if (spec.operations && spec.operations.length > 0) {
101
+ lines.push("2. Implement operations:");
102
+ for (const op of spec.operations) lines.push(` - \`${op.name}\` (v${op.version})`);
103
+ }
104
+ if (spec.presentations && spec.presentations.length > 0) {
105
+ lines.push("3. Implement presentations:");
106
+ for (const pres of spec.presentations) lines.push(` - \`${pres.name}\` (v${pres.version})`);
107
+ }
108
+ lines.push("4. Wire up feature exports");
109
+ lines.push("5. Add integration tests");
110
+ } else {
111
+ lines.push("1. Review the spec requirements");
112
+ lines.push("2. Implement the core logic");
113
+ lines.push("3. Add tests");
114
+ lines.push("4. Document the implementation");
115
+ }
116
+ lines.push("");
117
+ lines.push("## Constraints");
118
+ lines.push("");
119
+ lines.push(`- Stability: ${spec.meta.stability ?? "experimental"}`);
120
+ if (spec.meta.owners && spec.meta.owners.length > 0) lines.push(`- Owners: ${spec.meta.owners.join(", ")}`);
121
+ if (spec.meta.tags && spec.meta.tags.length > 0) lines.push(`- Tags: ${spec.meta.tags.join(", ")}`);
122
+ lines.push("");
123
+ if (agent === "cursor-cli") {
124
+ lines.push("## Cursor Notes");
125
+ lines.push("");
126
+ lines.push("- Use Composer mode for multi-file changes");
127
+ lines.push("- Reference this guide in your cursor rules");
128
+ lines.push("- Break implementation into small, focused commits");
129
+ } else if (agent === "claude-code") {
130
+ lines.push("## Claude Code Notes");
131
+ lines.push("");
132
+ lines.push("- Use extended thinking for complex logic");
133
+ lines.push("- Ask for clarification on ambiguous requirements");
134
+ lines.push("- Provide step-by-step reasoning");
135
+ } else if (agent === "generic-mcp") {
136
+ lines.push("## General Notes");
137
+ lines.push("");
138
+ lines.push("- Use inline comments to guide generation");
139
+ lines.push("- Review generated code carefully");
140
+ }
141
+ lines.push("");
142
+ lines.push("## Spec Definition");
143
+ lines.push("");
144
+ lines.push(specToMarkdown(spec, "full"));
145
+ return lines.join("\n");
146
+ }
147
+ /**
148
+ * Generate Cursor rules from parsed spec.
149
+ */
150
+ function generateCursorRulesFromParsedSpec(spec) {
151
+ const lines = [];
152
+ lines.push(`# ${spec.meta.key}`);
153
+ lines.push("");
154
+ lines.push(`Description: ${spec.meta.description ?? "No description"}`);
155
+ lines.push("");
156
+ lines.push("## Rules");
157
+ lines.push("");
158
+ if (spec.specType === "operation") {
159
+ lines.push(`- This is a ${spec.kind ?? "operation"} spec`);
160
+ if (spec.hasIo) lines.push("- Validate input and output against schemas");
161
+ if (spec.hasPolicy) lines.push("- Enforce authorization policies");
162
+ if (spec.emittedEvents && spec.emittedEvents.length > 0) lines.push("- Emit documented events");
163
+ } else if (spec.specType === "feature") {
164
+ lines.push("- Implement all operations and presentations");
165
+ lines.push("- Follow modular architecture");
166
+ } else lines.push(`- Follow ${spec.specType} patterns`);
167
+ lines.push("- Follow project code quality standards");
168
+ lines.push("- Write tests for new functionality");
169
+ return lines.join("\n");
170
+ }
171
+ /**
172
+ * Export spec to clipboard format suitable for pasting into LLM.
173
+ */
174
+ function exportSpecForLLM(spec, format, agent = "generic-mcp") {
175
+ switch (format) {
176
+ case "guide": return generateGuideFromParsedSpec(spec, agent);
177
+ case "rules": return generateCursorRulesFromParsedSpec(spec);
178
+ case "prompt": return specToMarkdown(spec, "prompt");
179
+ case "context": return specToMarkdown(spec, "context");
180
+ case "full":
181
+ default: return specToMarkdown(spec, "full");
182
+ }
183
+ }
184
+
185
+ //#endregion
186
+ export { exportSpecForLLM, generateCursorRulesFromParsedSpec, generateFeatureContextMarkdown, generateGuideFromParsedSpec };
187
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["parts: string[]","lines: string[]"],"sources":["../../../src/services/llm/index.ts"],"sourcesContent":["/**\n * LLM Service\n *\n * Provides services for generating LLM-friendly content from specs.\n * Primarily used by VSCode and CLI for \"Ask AI\" features.\n */\n\nimport {\n type AgentType,\n loadSpecFromSource,\n type ParsedSpec,\n specToMarkdown,\n} from '@contractspec/module.workspace';\nimport { listSpecs } from '../list';\nimport type { FsAdapter } from '../../ports/fs';\n\nexport * from './verify-static';\n\n/**\n * Generate full markdown context for a feature, including referenced specs.\n */\nexport async function generateFeatureContextMarkdown(\n feature: ParsedSpec,\n adapters: { fs: FsAdapter }\n): Promise<string> {\n const parts: string[] = [];\n\n // 1. Render feature header/context\n parts.push(specToMarkdown(feature, 'context'));\n\n // 2. Find all referenced specs\n // We need to scan the workspace to find them\n // Optimization: In a real app we might have a pre-built index\n const allSpecs = await listSpecs(adapters);\n\n // Helper to find and load a spec\n const loadChild = async (ref: { name: string; version: string }) => {\n const found = allSpecs.find(\n (s) => s.key === ref.name && s.version === ref.version\n );\n if (!found) return null;\n\n // Load full parsed spec\n const specs = await loadSpecFromSource(found.filePath);\n return specs.find((s) => s.meta.key === ref.name) ?? null;\n };\n\n // 3. Render operations\n if (feature.operations?.length) {\n parts.push(`\\n## Operations (${feature.operations.length})\\n`);\n for (const op of feature.operations) {\n const child = await loadChild(op);\n if (child) {\n parts.push(specToMarkdown(child, 'full'));\n } else {\n parts.push(`### ${op.name} (v${op.version})\\n\\n*Spec not found*`);\n }\n parts.push('---');\n }\n }\n\n // 4. Render events\n if (feature.events?.length) {\n parts.push(`\\n## Events (${feature.events.length})\\n`);\n for (const ev of feature.events) {\n const child = await loadChild(ev);\n if (child) {\n parts.push(specToMarkdown(child, 'full'));\n } else {\n parts.push(`### ${ev.name} (v${ev.version})\\n\\n*Spec not found*`);\n }\n parts.push('---');\n }\n }\n\n // 5. Render presentations\n if (feature.presentations?.length) {\n parts.push(`\\n## Presentations (${feature.presentations.length})\\n`);\n for (const pres of feature.presentations) {\n const child = await loadChild(pres);\n if (child) {\n parts.push(specToMarkdown(child, 'full'));\n } else {\n parts.push(`### ${pres.name} (v${pres.version})\\n\\n*Spec not found*`);\n }\n parts.push('---');\n }\n }\n\n return parts.join('\\n');\n}\n\n/**\n * Generate an implementation guide for a parsed spec.\n * This logic was extracted from VSCode extension.\n */\nexport function generateGuideFromParsedSpec(\n spec: ParsedSpec,\n agent: AgentType\n): string {\n const lines: string[] = [];\n\n // Header\n lines.push(`# Implementation Guide: ${spec.meta.key}`);\n lines.push('');\n lines.push(`**Target Agent**: ${agent}`);\n lines.push(\n `**Spec Type**: ${spec.specType}${spec.kind ? ` (${spec.kind})` : ''}`\n );\n lines.push('');\n\n // Description\n if (spec.meta.description) {\n lines.push('## Overview');\n lines.push('');\n lines.push(spec.meta.description);\n lines.push('');\n }\n\n // Goal and Context\n if (spec.meta.goal) {\n lines.push('## Goal');\n lines.push('');\n lines.push(spec.meta.goal);\n lines.push('');\n }\n\n if (spec.meta.context) {\n lines.push('## Context');\n lines.push('');\n lines.push(spec.meta.context);\n lines.push('');\n }\n\n // Implementation Steps\n lines.push('## Implementation Steps');\n lines.push('');\n\n if (spec.specType === 'operation') {\n lines.push(`1. Create the ${spec.kind ?? 'operation'} handler function`);\n if (spec.hasIo) {\n lines.push('2. Implement input validation according to the schema');\n lines.push('3. Implement the core business logic');\n lines.push('4. Return output matching the expected schema');\n }\n if (spec.hasPolicy) {\n lines.push('5. Enforce authorization and policies');\n }\n if (spec.emittedEvents && spec.emittedEvents.length > 0) {\n lines.push('6. Emit events on success:');\n for (const ev of spec.emittedEvents) {\n lines.push(` - \\`${ev.name}\\` (v${ev.version})`);\n }\n }\n lines.push('7. Add error handling for expected failure cases');\n lines.push('8. Write tests covering success and error scenarios');\n } else if (spec.specType === 'feature') {\n lines.push('1. Set up the feature module structure');\n if (spec.operations && spec.operations.length > 0) {\n lines.push('2. Implement operations:');\n for (const op of spec.operations) {\n lines.push(` - \\`${op.name}\\` (v${op.version})`);\n }\n }\n if (spec.presentations && spec.presentations.length > 0) {\n lines.push('3. Implement presentations:');\n for (const pres of spec.presentations) {\n lines.push(` - \\`${pres.name}\\` (v${pres.version})`);\n }\n }\n lines.push('4. Wire up feature exports');\n lines.push('5. Add integration tests');\n } else {\n lines.push('1. Review the spec requirements');\n lines.push('2. Implement the core logic');\n lines.push('3. Add tests');\n lines.push('4. Document the implementation');\n }\n\n lines.push('');\n\n // Constraints\n lines.push('## Constraints');\n lines.push('');\n lines.push(`- Stability: ${spec.meta.stability ?? 'experimental'}`);\n if (spec.meta.owners && spec.meta.owners.length > 0) {\n lines.push(`- Owners: ${spec.meta.owners.join(', ')}`);\n }\n if (spec.meta.tags && spec.meta.tags.length > 0) {\n lines.push(`- Tags: ${spec.meta.tags.join(', ')}`);\n }\n lines.push('');\n\n // Agent-specific notes\n if (agent === 'cursor-cli') {\n lines.push('## Cursor Notes');\n lines.push('');\n lines.push('- Use Composer mode for multi-file changes');\n lines.push('- Reference this guide in your cursor rules');\n lines.push('- Break implementation into small, focused commits');\n } else if (agent === 'claude-code') {\n lines.push('## Claude Code Notes');\n lines.push('');\n lines.push('- Use extended thinking for complex logic');\n lines.push('- Ask for clarification on ambiguous requirements');\n lines.push('- Provide step-by-step reasoning');\n } else if (agent === 'generic-mcp') {\n lines.push('## General Notes');\n lines.push('');\n lines.push('- Use inline comments to guide generation');\n lines.push('- Review generated code carefully');\n }\n\n // Append context formatted spec\n lines.push('');\n lines.push('## Spec Definition');\n lines.push('');\n lines.push(specToMarkdown(spec, 'full'));\n\n return lines.join('\\n');\n}\n\n/**\n * Generate Cursor rules from parsed spec.\n */\nexport function generateCursorRulesFromParsedSpec(spec: ParsedSpec): string {\n const lines: string[] = [];\n\n lines.push(`# ${spec.meta.key}`);\n lines.push('');\n lines.push(`Description: ${spec.meta.description ?? 'No description'}`);\n lines.push('');\n lines.push('## Rules');\n lines.push('');\n\n if (spec.specType === 'operation') {\n lines.push(`- This is a ${spec.kind ?? 'operation'} spec`);\n if (spec.hasIo) {\n lines.push('- Validate input and output against schemas');\n }\n if (spec.hasPolicy) {\n lines.push('- Enforce authorization policies');\n }\n if (spec.emittedEvents && spec.emittedEvents.length > 0) {\n lines.push('- Emit documented events');\n }\n } else if (spec.specType === 'feature') {\n lines.push('- Implement all operations and presentations');\n lines.push('- Follow modular architecture');\n } else {\n lines.push(`- Follow ${spec.specType} patterns`);\n }\n\n lines.push('- Follow project code quality standards');\n lines.push('- Write tests for new functionality');\n\n return lines.join('\\n');\n}\n\n/**\n * Export spec to clipboard format suitable for pasting into LLM.\n */\nexport function exportSpecForLLM(\n spec: ParsedSpec,\n format: 'guide' | 'rules' | 'prompt' | 'context' | 'full',\n agent: AgentType = 'generic-mcp'\n): string {\n switch (format) {\n case 'guide':\n return generateGuideFromParsedSpec(spec, agent);\n case 'rules':\n return generateCursorRulesFromParsedSpec(spec);\n case 'prompt':\n return specToMarkdown(spec, 'prompt');\n case 'context':\n return specToMarkdown(spec, 'context');\n case 'full':\n default:\n return specToMarkdown(spec, 'full');\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { ParsedSpec } from "@contractspec/module.workspace";
2
+ import { VerificationTier } from "@contractspec/lib.contracts/llm";
3
+
4
+ //#region src/services/llm/verify-static.d.ts
5
+
6
+ /**
7
+ * Verification issue found during static check.
8
+ */
9
+ interface VerificationIssue$1 {
10
+ severity: 'error' | 'warning' | 'info';
11
+ message: string;
12
+ category: string;
13
+ line?: number;
14
+ }
15
+ /**
16
+ * Verify implementation against parsed spec.
17
+ * This is a simplified verification that checks for basic patterns.
18
+ */
19
+ declare function verifyImplementationAgainstParsedSpec(spec: ParsedSpec, implementationCode: string, tiers: VerificationTier[]): VerificationIssue$1[];
20
+ /**
21
+ * Format verification report as markdown.
22
+ */
23
+ declare function formatVerificationReport(spec: ParsedSpec, issues: VerificationIssue$1[]): string;
24
+ //#endregion
25
+ export { VerificationIssue$1 as VerificationIssue, formatVerificationReport, verifyImplementationAgainstParsedSpec };
26
+ //# sourceMappingURL=verify-static.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-static.d.ts","names":[],"sources":["../../../src/services/llm/verify-static.ts"],"sourcesContent":[],"mappings":";;;;;;;AAyGA;UA5FiB,mBAAA;;;;;;;;;;iBAWD,qCAAA,OACR,+CAEC,qBACN;;;;iBA6Ea,wBAAA,OACR,oBACE"}
@@ -0,0 +1,82 @@
1
+ //#region src/services/llm/verify-static.ts
2
+ /**
3
+ * Verify implementation against parsed spec.
4
+ * This is a simplified verification that checks for basic patterns.
5
+ */
6
+ function verifyImplementationAgainstParsedSpec(spec, implementationCode, tiers) {
7
+ const issues = [];
8
+ if (tiers.includes("structure")) {
9
+ if (!/export\s+(async\s+)?function|export\s+class|export\s+const/.test(implementationCode)) issues.push({
10
+ severity: "warning",
11
+ message: "No exports found in implementation",
12
+ category: "structure"
13
+ });
14
+ if (spec.meta.key && !implementationCode.includes(spec.meta.key)) issues.push({
15
+ severity: "info",
16
+ message: `Spec name "${spec.meta.key}" not found in implementation`,
17
+ category: "structure"
18
+ });
19
+ }
20
+ if (tiers.includes("behavior")) {
21
+ if (!/try\s*{|catch\s*\(|throw\s+new/.test(implementationCode)) issues.push({
22
+ severity: "warning",
23
+ message: "No error handling patterns found",
24
+ category: "behavior"
25
+ });
26
+ if (spec.specType === "operation") {
27
+ if (!/async\s+|await\s+|Promise/.test(implementationCode)) issues.push({
28
+ severity: "info",
29
+ message: "No async patterns found (operations typically use async)",
30
+ category: "behavior"
31
+ });
32
+ }
33
+ if (spec.emittedEvents && spec.emittedEvents.length > 0) {
34
+ if (!/emit|publish|dispatch|fire/i.test(implementationCode)) issues.push({
35
+ severity: "warning",
36
+ message: `Spec emits ${spec.emittedEvents.length} event(s) but no event emission found`,
37
+ category: "behavior"
38
+ });
39
+ }
40
+ }
41
+ return issues;
42
+ }
43
+ /**
44
+ * Format verification report as markdown.
45
+ */
46
+ function formatVerificationReport(spec, issues) {
47
+ const lines = [];
48
+ lines.push(`# Verification Report: ${spec.meta.key}`);
49
+ lines.push("");
50
+ const errors = issues.filter((i) => i.severity === "error");
51
+ const warnings = issues.filter((i) => i.severity === "warning");
52
+ const infos = issues.filter((i) => i.severity === "info");
53
+ lines.push("## Summary");
54
+ lines.push("");
55
+ lines.push(`- Errors: ${errors.length}`);
56
+ lines.push(`- Warnings: ${warnings.length}`);
57
+ lines.push(`- Info: ${infos.length}`);
58
+ lines.push("");
59
+ if (errors.length > 0) {
60
+ lines.push("## Errors");
61
+ lines.push("");
62
+ for (const issue of errors) lines.push(`- ❌ **${issue.category}**: ${issue.message}`);
63
+ lines.push("");
64
+ }
65
+ if (warnings.length > 0) {
66
+ lines.push("## Warnings");
67
+ lines.push("");
68
+ for (const issue of warnings) lines.push(`- ⚠️ **${issue.category}**: ${issue.message}`);
69
+ lines.push("");
70
+ }
71
+ if (infos.length > 0) {
72
+ lines.push("## Info");
73
+ lines.push("");
74
+ for (const issue of infos) lines.push(`- ℹ️ **${issue.category}**: ${issue.message}`);
75
+ lines.push("");
76
+ }
77
+ return lines.join("\n");
78
+ }
79
+
80
+ //#endregion
81
+ export { formatVerificationReport, verifyImplementationAgainstParsedSpec };
82
+ //# sourceMappingURL=verify-static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-static.js","names":["issues: VerificationIssue[]","lines: string[]"],"sources":["../../../src/services/llm/verify-static.ts"],"sourcesContent":["/**\n * Static verification of implementation against parsed spec.\n *\n * This performs lightweight checks based on regex and static analysis\n * without requiring a full build or runtime environment.\n */\n\nimport type { VerificationTier } from '@contractspec/lib.contracts/llm';\nimport type { ParsedSpec } from '@contractspec/module.workspace';\n\n/**\n * Verification issue found during static check.\n */\nexport interface VerificationIssue {\n severity: 'error' | 'warning' | 'info';\n message: string;\n category: string;\n line?: number;\n}\n\n/**\n * Verify implementation against parsed spec.\n * This is a simplified verification that checks for basic patterns.\n */\nexport function verifyImplementationAgainstParsedSpec(\n spec: ParsedSpec,\n implementationCode: string,\n tiers: VerificationTier[]\n): VerificationIssue[] {\n const issues: VerificationIssue[] = [];\n\n // Structure checks (Tier 1)\n if (tiers.includes('structure')) {\n // Check for function/class export\n const hasExport =\n /export\\s+(async\\s+)?function|export\\s+class|export\\s+const/.test(\n implementationCode\n );\n if (!hasExport) {\n issues.push({\n severity: 'warning',\n message: 'No exports found in implementation',\n category: 'structure',\n });\n }\n\n // Check for spec name reference\n if (spec.meta.key && !implementationCode.includes(spec.meta.key)) {\n issues.push({\n severity: 'info',\n message: `Spec name \"${spec.meta.key}\" not found in implementation`,\n category: 'structure',\n });\n }\n }\n\n // Behavior checks (Tier 2)\n if (tiers.includes('behavior')) {\n // Check for error handling\n const hasErrorHandling = /try\\s*{|catch\\s*\\(|throw\\s+new/.test(\n implementationCode\n );\n if (!hasErrorHandling) {\n issues.push({\n severity: 'warning',\n message: 'No error handling patterns found',\n category: 'behavior',\n });\n }\n\n // Check for async patterns if needed\n if (spec.specType === 'operation') {\n const hasAsyncPattern = /async\\s+|await\\s+|Promise/.test(\n implementationCode\n );\n if (!hasAsyncPattern) {\n issues.push({\n severity: 'info',\n message: 'No async patterns found (operations typically use async)',\n category: 'behavior',\n });\n }\n }\n\n // Check for event emission if spec defines emitted events\n if (spec.emittedEvents && spec.emittedEvents.length > 0) {\n const hasEventEmit = /emit|publish|dispatch|fire/i.test(\n implementationCode\n );\n if (!hasEventEmit) {\n issues.push({\n severity: 'warning',\n message: `Spec emits ${spec.emittedEvents.length} event(s) but no event emission found`,\n category: 'behavior',\n });\n }\n }\n }\n\n return issues;\n}\n\n/**\n * Format verification report as markdown.\n */\nexport function formatVerificationReport(\n spec: ParsedSpec,\n issues: VerificationIssue[]\n): string {\n const lines: string[] = [];\n\n lines.push(`# Verification Report: ${spec.meta.key}`);\n lines.push('');\n\n const errors = issues.filter((i) => i.severity === 'error');\n const warnings = issues.filter((i) => i.severity === 'warning');\n const infos = issues.filter((i) => i.severity === 'info');\n\n lines.push('## Summary');\n lines.push('');\n lines.push(`- Errors: ${errors.length}`);\n lines.push(`- Warnings: ${warnings.length}`);\n lines.push(`- Info: ${infos.length}`);\n lines.push('');\n\n if (errors.length > 0) {\n lines.push('## Errors');\n lines.push('');\n for (const issue of errors) {\n lines.push(`- ❌ **${issue.category}**: ${issue.message}`);\n }\n lines.push('');\n }\n\n if (warnings.length > 0) {\n lines.push('## Warnings');\n lines.push('');\n for (const issue of warnings) {\n lines.push(`- ⚠️ **${issue.category}**: ${issue.message}`);\n }\n lines.push('');\n }\n\n if (infos.length > 0) {\n lines.push('## Info');\n lines.push('');\n for (const issue of infos) {\n lines.push(`- ℹ️ **${issue.category}**: ${issue.message}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":";;;;;AAwBA,SAAgB,sCACd,MACA,oBACA,OACqB;CACrB,MAAMA,SAA8B,EAAE;AAGtC,KAAI,MAAM,SAAS,YAAY,EAAE;AAM/B,MAAI,CAHF,6DAA6D,KAC3D,mBACD,CAED,QAAO,KAAK;GACV,UAAU;GACV,SAAS;GACT,UAAU;GACX,CAAC;AAIJ,MAAI,KAAK,KAAK,OAAO,CAAC,mBAAmB,SAAS,KAAK,KAAK,IAAI,CAC9D,QAAO,KAAK;GACV,UAAU;GACV,SAAS,cAAc,KAAK,KAAK,IAAI;GACrC,UAAU;GACX,CAAC;;AAKN,KAAI,MAAM,SAAS,WAAW,EAAE;AAK9B,MAAI,CAHqB,iCAAiC,KACxD,mBACD,CAEC,QAAO,KAAK;GACV,UAAU;GACV,SAAS;GACT,UAAU;GACX,CAAC;AAIJ,MAAI,KAAK,aAAa,aAIpB;OAAI,CAHoB,4BAA4B,KAClD,mBACD,CAEC,QAAO,KAAK;IACV,UAAU;IACV,SAAS;IACT,UAAU;IACX,CAAC;;AAKN,MAAI,KAAK,iBAAiB,KAAK,cAAc,SAAS,GAIpD;OAAI,CAHiB,8BAA8B,KACjD,mBACD,CAEC,QAAO,KAAK;IACV,UAAU;IACV,SAAS,cAAc,KAAK,cAAc,OAAO;IACjD,UAAU;IACX,CAAC;;;AAKR,QAAO;;;;;AAMT,SAAgB,yBACd,MACA,QACQ;CACR,MAAMC,QAAkB,EAAE;AAE1B,OAAM,KAAK,0BAA0B,KAAK,KAAK,MAAM;AACrD,OAAM,KAAK,GAAG;CAEd,MAAM,SAAS,OAAO,QAAQ,MAAM,EAAE,aAAa,QAAQ;CAC3D,MAAM,WAAW,OAAO,QAAQ,MAAM,EAAE,aAAa,UAAU;CAC/D,MAAM,QAAQ,OAAO,QAAQ,MAAM,EAAE,aAAa,OAAO;AAEzD,OAAM,KAAK,aAAa;AACxB,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,aAAa,OAAO,SAAS;AACxC,OAAM,KAAK,eAAe,SAAS,SAAS;AAC5C,OAAM,KAAK,WAAW,MAAM,SAAS;AACrC,OAAM,KAAK,GAAG;AAEd,KAAI,OAAO,SAAS,GAAG;AACrB,QAAM,KAAK,YAAY;AACvB,QAAM,KAAK,GAAG;AACd,OAAK,MAAM,SAAS,OAClB,OAAM,KAAK,SAAS,MAAM,SAAS,MAAM,MAAM,UAAU;AAE3D,QAAM,KAAK,GAAG;;AAGhB,KAAI,SAAS,SAAS,GAAG;AACvB,QAAM,KAAK,cAAc;AACzB,QAAM,KAAK,GAAG;AACd,OAAK,MAAM,SAAS,SAClB,OAAM,KAAK,UAAU,MAAM,SAAS,MAAM,MAAM,UAAU;AAE5D,QAAM,KAAK,GAAG;;AAGhB,KAAI,MAAM,SAAS,GAAG;AACpB,QAAM,KAAK,UAAU;AACrB,QAAM,KAAK,GAAG;AACd,OAAK,MAAM,SAAS,MAClB,OAAM,KAAK,UAAU,MAAM,SAAS,MAAM,MAAM,UAAU;AAE5D,QAAM,KAAK,GAAG;;AAGhB,QAAO,MAAM,KAAK,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"import-service.d.ts","names":[],"sources":["../../../src/services/openapi/import-service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAuBqC,iBAHf,wBAAA,CAGe,mBAAA,EAFd,iBAEc,EAAA,OAAA,EAD1B,2BAC0B,EAAA,QAAA,EAAA;EAC1B,EAAA,EADO,SACP;EAAR,MAAA,EADkC,aAClC;CAAO,CAAA,EAAP,OAAO,CAAC,0BAAD,CAAA"}
1
+ {"version":3,"file":"import-service.d.ts","names":[],"sources":["../../../src/services/openapi/import-service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AA2DqC,iBAHf,wBAAA,CAGe,mBAAA,EAFd,iBAEc,EAAA,OAAA,EAD1B,2BAC0B,EAAA,QAAA,EAAA;EAC1B,EAAA,EADO,SACP;EAAR,MAAA,EADkC,aAClC;CAAO,CAAA,EAAP,OAAO,CAAC,0BAAD,CAAA"}
@@ -1,16 +1,33 @@
1
+ import { basename, dirname, join } from "path";
1
2
  import { importFromOpenApi, parseOpenApi } from "@contractspec/lib.contracts-transformers/openapi";
2
- import { dirname, join } from "path";
3
3
 
4
4
  //#region src/services/openapi/import-service.ts
5
5
  /**
6
6
  * OpenAPI import service - imports specs from OpenAPI documents.
7
7
  */
8
8
  /**
9
+ * Get output directory for spec type
10
+ */
11
+ function getOutputDir(type, options, config) {
12
+ if (options.outputDir) return options.outputDir;
13
+ const baseDir = config.outputDir ?? "src";
14
+ const conventions = config.conventions ?? {
15
+ operations: "operations",
16
+ events: "events"
17
+ };
18
+ switch (type) {
19
+ case "operation": return join(baseDir, conventions.operations.split("|")[0] ?? "operations");
20
+ case "event": return join(baseDir, conventions.events ?? "events");
21
+ case "model": return join(baseDir, "models");
22
+ default: return baseDir;
23
+ }
24
+ }
25
+ /**
9
26
  * Import ContractSpec specs from an OpenAPI document.
10
27
  */
11
28
  async function importFromOpenApiService(contractspecOptions, options, adapters) {
12
29
  const { fs, logger } = adapters;
13
- const { source, outputDir, prefix, tags, exclude, defaultStability, defaultOwners, defaultAuth, dryRun = false } = options;
30
+ const { source, prefix, tags, exclude, defaultStability, defaultOwners, defaultAuth, dryRun = false } = options;
14
31
  logger.info(`Importing from OpenAPI: ${source}`);
15
32
  const parseResult = await parseOpenApi(source, {
16
33
  fetch: globalThis.fetch,
@@ -30,12 +47,29 @@ async function importFromOpenApiService(contractspecOptions, options, adapters)
30
47
  const files = [];
31
48
  const skippedOperations = [];
32
49
  const errorMessages = [];
50
+ const specsByDir = /* @__PURE__ */ new Map();
33
51
  for (const spec of importResult.operationSpecs) {
34
- const filePath = join(outputDir, spec.fileName);
52
+ let type = "operation";
53
+ let match = null;
54
+ if (spec.code.includes("defineEvent(")) {
55
+ type = "event";
56
+ match = spec.code.match(/export const (\w+)\s*=\s*defineEvent/);
57
+ } else if ((spec.code.includes("defineSchemaModel(") || spec.code.includes("new EnumType(") || spec.code.includes("ScalarTypeEnum.") || spec.code.includes("new ZodSchemaType(") || spec.code.includes("z.enum(") || spec.code.includes("new JsonSchemaType(") || spec.code.includes("new GraphQLSchemaType(")) && !spec.code.includes("defineCommand(") && !spec.code.includes("defineQuery(")) type = "model";
58
+ else {
59
+ type = "operation";
60
+ match = spec.code.match(/export const (\w+)\s*=\s*define(?:Command|Query)/);
61
+ }
62
+ const filePath = join(getOutputDir(type, options, contractspecOptions), spec.fileName);
63
+ if (!match && type === "model") {
64
+ if (spec.code.includes("new ZodSchemaType(")) match = spec.code.match(/export const (\w+)\s*=\s*new ZodSchemaType\(/);
65
+ else if (spec.code.includes("new JsonSchemaType(")) match = spec.code.match(/export const (\w+)\s*=\s*new JsonSchemaType\(/);
66
+ else if (spec.code.includes("new GraphQLSchemaType(")) match = spec.code.match(/export const (\w+)\s*=\s*new GraphQLSchemaType\(/);
67
+ if (!match) match = spec.code.match(/export const (\w+)\s*=/);
68
+ }
35
69
  if (dryRun) logger.info(`[DRY RUN] Would create: ${filePath}`);
36
70
  else {
37
71
  const dir = dirname(filePath);
38
- await fs.mkdir(dir);
72
+ if (!await fs.exists(dir)) await fs.mkdir(dir);
39
73
  await fs.writeFile(filePath, spec.code);
40
74
  logger.info(`Created: ${filePath}`);
41
75
  }
@@ -44,6 +78,66 @@ async function importFromOpenApiService(contractspecOptions, options, adapters)
44
78
  operationId: spec.source.sourceId,
45
79
  specName: spec.fileName.replace(".ts", "")
46
80
  });
81
+ if (match) {
82
+ const dir = dirname(filePath);
83
+ const existing = specsByDir.get(dir) || [];
84
+ existing.push({
85
+ file: basename(filePath),
86
+ name: match[1],
87
+ type
88
+ });
89
+ specsByDir.set(dir, existing);
90
+ }
91
+ }
92
+ if (!dryRun && files.length > 0) for (const [dir, specs] of specsByDir.entries()) {
93
+ if (specs.length === 0) continue;
94
+ const types = specs.map((s) => s.type);
95
+ const isOperations = types.every((t) => t === "operation");
96
+ const isEvents = types.every((t) => t === "event");
97
+ const isModels = types.every((t) => t === "model");
98
+ let registryCode = `/**\n * Auto-generated registry file.\n */\n`;
99
+ specs.forEach((s) => {
100
+ const importPath = `./${basename(s.file, ".ts")}`;
101
+ registryCode += `import { ${s.name} } from '${importPath}';\n`;
102
+ });
103
+ registryCode += "\n";
104
+ let hasRegistry = false;
105
+ if (isOperations) {
106
+ registryCode += `import { OperationSpecRegistry } from '@contractspec/lib.contracts';\n\n`;
107
+ registryCode += `export const operationRegistry = new OperationSpecRegistry();\n`;
108
+ specs.forEach((s) => {
109
+ registryCode += `operationRegistry.register(${s.name});\n`;
110
+ });
111
+ hasRegistry = true;
112
+ } else if (isEvents) {
113
+ registryCode += `import { EventRegistry } from '@contractspec/lib.contracts';\n\n`;
114
+ registryCode += `export const eventRegistry = new EventRegistry();\n`;
115
+ specs.forEach((s) => {
116
+ registryCode += `eventRegistry.register(${s.name});\n`;
117
+ });
118
+ hasRegistry = true;
119
+ } else if (isModels) {
120
+ registryCode += `import { ModelRegistry } from '@contractspec/lib.contracts';\n\n`;
121
+ registryCode += `export const modelRegistry = new ModelRegistry();\n`;
122
+ specs.forEach((s) => {
123
+ registryCode += `modelRegistry.register(${s.name});\n`;
124
+ });
125
+ hasRegistry = true;
126
+ }
127
+ if (hasRegistry) {
128
+ const registryPath = join(dir, "registry.ts");
129
+ await fs.writeFile(registryPath, registryCode);
130
+ logger.info(`Created/Updated registry: ${registryPath}`);
131
+ }
132
+ let indexCode = `/**\n * Auto-generated barrel file.\n */\n\n`;
133
+ specs.forEach((s) => {
134
+ const importPath = `./${basename(s.file, ".ts")}`;
135
+ indexCode += `export * from '${importPath}';\n`;
136
+ });
137
+ if (hasRegistry) indexCode += `export * from './registry';\n`;
138
+ const indexPath = join(dir, "index.ts");
139
+ await fs.writeFile(indexPath, indexCode);
140
+ logger.info(`Created/Updated index: ${indexPath}`);
47
141
  }
48
142
  for (const skipped of importResult.skipped) {
49
143
  skippedOperations.push({