@mcoda/core 0.1.8 → 0.1.11

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 (216) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +2 -2
  3. package/dist/api/AgentsApi.d.ts +9 -1
  4. package/dist/api/AgentsApi.d.ts.map +1 -1
  5. package/dist/api/AgentsApi.js +201 -6
  6. package/dist/api/QaTasksApi.d.ts.map +1 -1
  7. package/dist/api/QaTasksApi.js +6 -0
  8. package/dist/api/TasksApi.d.ts.map +1 -1
  9. package/dist/api/TasksApi.js +1 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -0
  13. package/dist/prompts/PdrPrompts.d.ts.map +1 -1
  14. package/dist/prompts/PdrPrompts.js +9 -1
  15. package/dist/prompts/SdsPrompts.d.ts.map +1 -1
  16. package/dist/prompts/SdsPrompts.js +9 -0
  17. package/dist/services/agents/AgentRatingFormula.d.ts +27 -0
  18. package/dist/services/agents/AgentRatingFormula.d.ts.map +1 -0
  19. package/dist/services/agents/AgentRatingFormula.js +45 -0
  20. package/dist/services/agents/AgentRatingService.d.ts +60 -0
  21. package/dist/services/agents/AgentRatingService.d.ts.map +1 -0
  22. package/dist/services/agents/AgentRatingService.js +363 -0
  23. package/dist/services/agents/GatewayAgentService.d.ts +11 -0
  24. package/dist/services/agents/GatewayAgentService.d.ts.map +1 -1
  25. package/dist/services/agents/GatewayAgentService.js +525 -84
  26. package/dist/services/agents/GatewayHandoff.d.ts +11 -0
  27. package/dist/services/agents/GatewayHandoff.d.ts.map +1 -0
  28. package/dist/services/agents/GatewayHandoff.js +141 -0
  29. package/dist/services/agents/RoutingService.d.ts +1 -0
  30. package/dist/services/agents/RoutingService.d.ts.map +1 -1
  31. package/dist/services/agents/RoutingService.js +4 -4
  32. package/dist/services/backlog/BacklogService.d.ts +23 -0
  33. package/dist/services/backlog/BacklogService.d.ts.map +1 -1
  34. package/dist/services/backlog/BacklogService.js +62 -7
  35. package/dist/services/backlog/TaskOrderingHeuristics.d.ts +12 -0
  36. package/dist/services/backlog/TaskOrderingHeuristics.d.ts.map +1 -0
  37. package/dist/services/backlog/TaskOrderingHeuristics.js +56 -0
  38. package/dist/services/backlog/TaskOrderingService.d.ts +17 -4
  39. package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -1
  40. package/dist/services/backlog/TaskOrderingService.js +538 -79
  41. package/dist/services/docs/DocInventory.d.ts +11 -0
  42. package/dist/services/docs/DocInventory.d.ts.map +1 -0
  43. package/dist/services/docs/DocInventory.js +230 -0
  44. package/dist/services/docs/DocgenRunContext.d.ts +59 -0
  45. package/dist/services/docs/DocgenRunContext.d.ts.map +1 -0
  46. package/dist/services/docs/DocgenRunContext.js +4 -0
  47. package/dist/services/docs/DocsService.d.ts +70 -3
  48. package/dist/services/docs/DocsService.d.ts.map +1 -1
  49. package/dist/services/docs/DocsService.js +1930 -89
  50. package/dist/services/docs/alignment/DocAlignmentGraph.d.ts +23 -0
  51. package/dist/services/docs/alignment/DocAlignmentGraph.d.ts.map +1 -0
  52. package/dist/services/docs/alignment/DocAlignmentGraph.js +78 -0
  53. package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts +19 -0
  54. package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts.map +1 -0
  55. package/dist/services/docs/alignment/DocAlignmentPatcher.js +222 -0
  56. package/dist/services/docs/patch/DocPatchEngine.d.ts +57 -0
  57. package/dist/services/docs/patch/DocPatchEngine.d.ts.map +1 -0
  58. package/dist/services/docs/patch/DocPatchEngine.js +331 -0
  59. package/dist/services/docs/review/Glossary.d.ts +16 -0
  60. package/dist/services/docs/review/Glossary.d.ts.map +1 -0
  61. package/dist/services/docs/review/Glossary.js +47 -0
  62. package/dist/services/docs/review/ReviewReportRenderer.d.ts +3 -0
  63. package/dist/services/docs/review/ReviewReportRenderer.d.ts.map +1 -0
  64. package/dist/services/docs/review/ReviewReportRenderer.js +133 -0
  65. package/dist/services/docs/review/ReviewReportSchema.d.ts +39 -0
  66. package/dist/services/docs/review/ReviewReportSchema.d.ts.map +1 -0
  67. package/dist/services/docs/review/ReviewReportSchema.js +47 -0
  68. package/dist/services/docs/review/ReviewTypes.d.ts +76 -0
  69. package/dist/services/docs/review/ReviewTypes.d.ts.map +1 -0
  70. package/dist/services/docs/review/ReviewTypes.js +94 -0
  71. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts +7 -0
  72. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts.map +1 -0
  73. package/dist/services/docs/review/gates/AdminOpenApiSpecGate.js +93 -0
  74. package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts +7 -0
  75. package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts.map +1 -0
  76. package/dist/services/docs/review/gates/ApiPathConsistencyGate.js +308 -0
  77. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts +8 -0
  78. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts.map +1 -0
  79. package/dist/services/docs/review/gates/BuildReadyCompletenessGate.js +278 -0
  80. package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts +8 -0
  81. package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts.map +1 -0
  82. package/dist/services/docs/review/gates/DeploymentBlueprintGate.js +487 -0
  83. package/dist/services/docs/review/gates/NoMaybesGate.d.ts +8 -0
  84. package/dist/services/docs/review/gates/NoMaybesGate.d.ts.map +1 -0
  85. package/dist/services/docs/review/gates/NoMaybesGate.js +145 -0
  86. package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts +7 -0
  87. package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts.map +1 -0
  88. package/dist/services/docs/review/gates/OpenApiCoverageGate.js +266 -0
  89. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts +7 -0
  90. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts.map +1 -0
  91. package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.js +59 -0
  92. package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts +7 -0
  93. package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts.map +1 -0
  94. package/dist/services/docs/review/gates/OpenQuestionsGate.js +200 -0
  95. package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts +7 -0
  96. package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts.map +1 -0
  97. package/dist/services/docs/review/gates/PdrInterfacesGate.js +159 -0
  98. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts +8 -0
  99. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts.map +1 -0
  100. package/dist/services/docs/review/gates/PdrOpenQuestionsGate.js +129 -0
  101. package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts +7 -0
  102. package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts.map +1 -0
  103. package/dist/services/docs/review/gates/PdrOwnershipGate.js +169 -0
  104. package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts +10 -0
  105. package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts.map +1 -0
  106. package/dist/services/docs/review/gates/PlaceholderArtifactGate.js +261 -0
  107. package/dist/services/docs/review/gates/RfpConsentGate.d.ts +6 -0
  108. package/dist/services/docs/review/gates/RfpConsentGate.d.ts.map +1 -0
  109. package/dist/services/docs/review/gates/RfpConsentGate.js +127 -0
  110. package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts +7 -0
  111. package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts.map +1 -0
  112. package/dist/services/docs/review/gates/RfpDefinitionGate.js +173 -0
  113. package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts +7 -0
  114. package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts.map +1 -0
  115. package/dist/services/docs/review/gates/SdsAdaptersGate.js +196 -0
  116. package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts +7 -0
  117. package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts.map +1 -0
  118. package/dist/services/docs/review/gates/SdsDecisionsGate.js +89 -0
  119. package/dist/services/docs/review/gates/SdsOpsGate.d.ts +7 -0
  120. package/dist/services/docs/review/gates/SdsOpsGate.d.ts.map +1 -0
  121. package/dist/services/docs/review/gates/SdsOpsGate.js +162 -0
  122. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts +7 -0
  123. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts.map +1 -0
  124. package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.js +166 -0
  125. package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts +7 -0
  126. package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts.map +1 -0
  127. package/dist/services/docs/review/gates/SqlRequiredTablesGate.js +273 -0
  128. package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts +7 -0
  129. package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts.map +1 -0
  130. package/dist/services/docs/review/gates/SqlSyntaxGate.js +203 -0
  131. package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts +9 -0
  132. package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts.map +1 -0
  133. package/dist/services/docs/review/gates/TerminologyNormalizationGate.js +217 -0
  134. package/dist/services/docs/review/glossary.json +47 -0
  135. package/dist/services/estimate/EstimateService.d.ts +2 -0
  136. package/dist/services/estimate/EstimateService.d.ts.map +1 -1
  137. package/dist/services/estimate/EstimateService.js +66 -18
  138. package/dist/services/estimate/VelocityService.d.ts +4 -0
  139. package/dist/services/estimate/VelocityService.d.ts.map +1 -1
  140. package/dist/services/estimate/VelocityService.js +179 -36
  141. package/dist/services/estimate/types.d.ts +1 -0
  142. package/dist/services/estimate/types.d.ts.map +1 -1
  143. package/dist/services/execution/GatewayTrioService.d.ts +200 -0
  144. package/dist/services/execution/GatewayTrioService.d.ts.map +1 -0
  145. package/dist/services/execution/GatewayTrioService.js +2492 -0
  146. package/dist/services/execution/QaApiRunner.d.ts +30 -0
  147. package/dist/services/execution/QaApiRunner.d.ts.map +1 -0
  148. package/dist/services/execution/QaApiRunner.js +881 -0
  149. package/dist/services/execution/QaFollowupService.d.ts +2 -0
  150. package/dist/services/execution/QaFollowupService.d.ts.map +1 -1
  151. package/dist/services/execution/QaFollowupService.js +9 -2
  152. package/dist/services/execution/QaPlanValidator.d.ts +10 -0
  153. package/dist/services/execution/QaPlanValidator.d.ts.map +1 -0
  154. package/dist/services/execution/QaPlanValidator.js +128 -0
  155. package/dist/services/execution/QaProfileService.d.ts +27 -1
  156. package/dist/services/execution/QaProfileService.d.ts.map +1 -1
  157. package/dist/services/execution/QaProfileService.js +354 -7
  158. package/dist/services/execution/QaTasksService.d.ts +59 -1
  159. package/dist/services/execution/QaTasksService.d.ts.map +1 -1
  160. package/dist/services/execution/QaTasksService.js +3347 -318
  161. package/dist/services/execution/QaTestCommandBuilder.d.ts +51 -0
  162. package/dist/services/execution/QaTestCommandBuilder.d.ts.map +1 -0
  163. package/dist/services/execution/QaTestCommandBuilder.js +495 -0
  164. package/dist/services/execution/TaskSelectionService.d.ts +4 -2
  165. package/dist/services/execution/TaskSelectionService.d.ts.map +1 -1
  166. package/dist/services/execution/TaskSelectionService.js +144 -28
  167. package/dist/services/execution/TaskStateService.d.ts +19 -6
  168. package/dist/services/execution/TaskStateService.d.ts.map +1 -1
  169. package/dist/services/execution/TaskStateService.js +128 -13
  170. package/dist/services/execution/WorkOnTasksService.d.ts +32 -1
  171. package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
  172. package/dist/services/execution/WorkOnTasksService.js +4667 -722
  173. package/dist/services/jobs/JobInsightsService.d.ts +4 -0
  174. package/dist/services/jobs/JobInsightsService.d.ts.map +1 -1
  175. package/dist/services/jobs/JobInsightsService.js +51 -5
  176. package/dist/services/jobs/JobResumeService.d.ts.map +1 -1
  177. package/dist/services/jobs/JobResumeService.js +23 -10
  178. package/dist/services/jobs/JobService.d.ts +56 -4
  179. package/dist/services/jobs/JobService.d.ts.map +1 -1
  180. package/dist/services/jobs/JobService.js +232 -1
  181. package/dist/services/openapi/OpenApiService.d.ts +51 -0
  182. package/dist/services/openapi/OpenApiService.d.ts.map +1 -1
  183. package/dist/services/openapi/OpenApiService.js +953 -106
  184. package/dist/services/planning/CreateTasksService.d.ts +21 -0
  185. package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
  186. package/dist/services/planning/CreateTasksService.js +569 -31
  187. package/dist/services/planning/RefineTasksService.d.ts +9 -0
  188. package/dist/services/planning/RefineTasksService.d.ts.map +1 -1
  189. package/dist/services/planning/RefineTasksService.js +409 -59
  190. package/dist/services/review/CodeReviewService.d.ts +18 -0
  191. package/dist/services/review/CodeReviewService.d.ts.map +1 -1
  192. package/dist/services/review/CodeReviewService.js +1309 -167
  193. package/dist/services/review/ReviewNormalizer.d.ts +9 -0
  194. package/dist/services/review/ReviewNormalizer.d.ts.map +1 -0
  195. package/dist/services/review/ReviewNormalizer.js +147 -0
  196. package/dist/services/shared/AuthErrors.d.ts +3 -0
  197. package/dist/services/shared/AuthErrors.d.ts.map +1 -0
  198. package/dist/services/shared/AuthErrors.js +17 -0
  199. package/dist/services/shared/DocdexGuidance.d.ts +7 -0
  200. package/dist/services/shared/DocdexGuidance.d.ts.map +1 -0
  201. package/dist/services/shared/DocdexGuidance.js +12 -0
  202. package/dist/services/shared/ProjectGuidance.d.ts +17 -0
  203. package/dist/services/shared/ProjectGuidance.d.ts.map +1 -0
  204. package/dist/services/shared/ProjectGuidance.js +78 -0
  205. package/dist/services/system/ToolDenylist.d.ts +13 -0
  206. package/dist/services/system/ToolDenylist.d.ts.map +1 -0
  207. package/dist/services/system/ToolDenylist.js +85 -0
  208. package/dist/services/tasks/TaskCommentFormatter.d.ts +20 -0
  209. package/dist/services/tasks/TaskCommentFormatter.d.ts.map +1 -0
  210. package/dist/services/tasks/TaskCommentFormatter.js +54 -0
  211. package/dist/services/telemetry/TelemetryService.d.ts.map +1 -1
  212. package/dist/services/telemetry/TelemetryService.js +39 -7
  213. package/dist/workspace/WorkspaceManager.d.ts +26 -0
  214. package/dist/workspace/WorkspaceManager.d.ts.map +1 -1
  215. package/dist/workspace/WorkspaceManager.js +206 -32
  216. package/package.json +6 -5
@@ -0,0 +1,487 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import YAML from "yaml";
4
+ const DEPENDENCY_DEFINITIONS = [
5
+ { key: "mysql", label: "MySQL", keywords: ["mysql", "mariadb"] },
6
+ { key: "redis", label: "Redis", keywords: ["redis"] },
7
+ { key: "nats", label: "NATS", keywords: ["nats"] },
8
+ { key: "minio", label: "MinIO", keywords: ["minio", "object storage", "s3"] },
9
+ { key: "clickhouse", label: "ClickHouse", keywords: ["clickhouse"] },
10
+ ];
11
+ const ENV_REF_PATTERN = /\${([A-Z][A-Z0-9_]+)}/g;
12
+ const ENV_SECTION_PATTERN = /^\s*(environment|data|stringData)\s*:\s*$/i;
13
+ const ENV_KEY_PATTERN = /^\s*([A-Z][A-Z0-9_]+)\s*:/;
14
+ const SERVICE_PORT_NAME = "SERVICE_PORT";
15
+ const isFenceLine = (line) => /^```|^~~~/.test(line.trim());
16
+ const extractDependencies = (content) => {
17
+ const matches = new Map();
18
+ const lines = content.split(/\r?\n/);
19
+ let inFence = false;
20
+ for (let i = 0; i < lines.length; i += 1) {
21
+ const line = lines[i] ?? "";
22
+ const trimmed = line.trim();
23
+ if (isFenceLine(trimmed)) {
24
+ inFence = !inFence;
25
+ continue;
26
+ }
27
+ if (inFence || !trimmed)
28
+ continue;
29
+ const normalized = trimmed.toLowerCase();
30
+ for (const dependency of DEPENDENCY_DEFINITIONS) {
31
+ if (matches.has(dependency.key))
32
+ continue;
33
+ const keyword = dependency.keywords.find((entry) => normalized.includes(entry));
34
+ if (keyword) {
35
+ matches.set(dependency.key, { line: i + 1, keyword });
36
+ }
37
+ }
38
+ }
39
+ return matches;
40
+ };
41
+ const parseEnvExample = (content) => {
42
+ const names = new Set();
43
+ const lineMap = new Map();
44
+ let port;
45
+ const lines = content.split(/\r?\n/);
46
+ lines.forEach((line, index) => {
47
+ const trimmed = line.trim();
48
+ if (!trimmed || trimmed.startsWith("#"))
49
+ return;
50
+ const [rawName, rawValue] = trimmed.split("=", 2);
51
+ const name = rawName?.trim();
52
+ if (!name || !/^[A-Z][A-Z0-9_]+$/.test(name))
53
+ return;
54
+ names.add(name);
55
+ if (!lineMap.has(name)) {
56
+ lineMap.set(name, index + 1);
57
+ }
58
+ if (name === SERVICE_PORT_NAME && rawValue) {
59
+ const parsed = Number.parseInt(rawValue.trim(), 10);
60
+ if (Number.isFinite(parsed))
61
+ port = parsed;
62
+ }
63
+ });
64
+ return { names, port, lineMap };
65
+ };
66
+ const parseEnvDoc = (content) => {
67
+ const names = new Set();
68
+ const lines = content.split(/\r?\n/);
69
+ for (const line of lines) {
70
+ if (!line.includes("|"))
71
+ continue;
72
+ const columns = line.split("|").map((entry) => entry.trim());
73
+ if (columns.length < 3)
74
+ continue;
75
+ const name = columns[1];
76
+ if (!name || name.toLowerCase() === "name")
77
+ continue;
78
+ if (/^[A-Z][A-Z0-9_]+$/.test(name)) {
79
+ names.add(name);
80
+ }
81
+ }
82
+ return names;
83
+ };
84
+ const collectEnvReferences = async (record) => {
85
+ const refs = new Map();
86
+ try {
87
+ const content = await fs.readFile(record.path, "utf8");
88
+ const lines = content.split(/\r?\n/);
89
+ let inSection = false;
90
+ let sectionIndent = 0;
91
+ for (let i = 0; i < lines.length; i += 1) {
92
+ const line = lines[i] ?? "";
93
+ const trimmed = line.trim();
94
+ const sectionMatch = line.match(ENV_SECTION_PATTERN);
95
+ if (sectionMatch) {
96
+ inSection = true;
97
+ sectionIndent = line.match(/^\s*/)?.[0]?.length ?? 0;
98
+ continue;
99
+ }
100
+ const indent = line.match(/^\s*/)?.[0]?.length ?? 0;
101
+ if (inSection && indent <= sectionIndent && trimmed.length > 0) {
102
+ inSection = false;
103
+ }
104
+ if (inSection) {
105
+ const keyMatch = line.match(ENV_KEY_PATTERN);
106
+ if (keyMatch?.[1]) {
107
+ if (!refs.has(keyMatch[1])) {
108
+ refs.set(keyMatch[1], { path: record.path, line: i + 1 });
109
+ }
110
+ }
111
+ }
112
+ const envRegex = new RegExp(ENV_REF_PATTERN);
113
+ let match;
114
+ while ((match = envRegex.exec(line)) !== null) {
115
+ const name = match[1];
116
+ if (name && !refs.has(name)) {
117
+ refs.set(name, { path: record.path, line: i + 1 });
118
+ }
119
+ }
120
+ }
121
+ }
122
+ catch {
123
+ return refs;
124
+ }
125
+ return refs;
126
+ };
127
+ const extractComposeServices = (content) => {
128
+ const services = new Map();
129
+ const lines = content.split(/\r?\n/);
130
+ let inServices = false;
131
+ let baseIndent = 0;
132
+ for (let i = 0; i < lines.length; i += 1) {
133
+ const line = lines[i] ?? "";
134
+ const trimmed = line.trim();
135
+ if (!trimmed)
136
+ continue;
137
+ if (!inServices && /^services\s*:/i.test(trimmed)) {
138
+ inServices = true;
139
+ baseIndent = line.match(/^\s*/)?.[0]?.length ?? 0;
140
+ continue;
141
+ }
142
+ if (inServices) {
143
+ const indent = line.match(/^\s*/)?.[0]?.length ?? 0;
144
+ if (indent <= baseIndent) {
145
+ inServices = false;
146
+ continue;
147
+ }
148
+ const match = line.match(/^\s*([A-Za-z0-9_-]+)\s*:\s*$/);
149
+ if (match?.[1]) {
150
+ services.set(match[1], i + 1);
151
+ }
152
+ }
153
+ }
154
+ return services;
155
+ };
156
+ const extractServicesFromBlueprints = async (records) => {
157
+ const services = new Map();
158
+ const compose = records.find((record) => path.basename(record.path).toLowerCase().includes("docker-compose"));
159
+ if (compose) {
160
+ try {
161
+ const content = await fs.readFile(compose.path, "utf8");
162
+ const extracted = extractComposeServices(content);
163
+ extracted.forEach((line, name) => {
164
+ services.set(name, { path: compose.path, line });
165
+ });
166
+ return services;
167
+ }
168
+ catch {
169
+ // fall through to filename-based detection
170
+ }
171
+ }
172
+ for (const record of records) {
173
+ const base = path.basename(record.path).toLowerCase();
174
+ if (!base.endsWith("-deployment.yaml") && !base.endsWith("-deployment.yml")) {
175
+ continue;
176
+ }
177
+ const name = base.replace(/-deployment\.ya?ml$/i, "");
178
+ if (!services.has(name)) {
179
+ services.set(name, { path: record.path, line: 1 });
180
+ }
181
+ }
182
+ return services;
183
+ };
184
+ const parseOpenApi = (raw) => {
185
+ if (!raw || !raw.trim())
186
+ return undefined;
187
+ try {
188
+ return YAML.parse(raw);
189
+ }
190
+ catch {
191
+ try {
192
+ return JSON.parse(raw);
193
+ }
194
+ catch {
195
+ return undefined;
196
+ }
197
+ }
198
+ };
199
+ const extractPortFromUrl = (value) => {
200
+ const trimmed = value.trim();
201
+ if (!trimmed)
202
+ return undefined;
203
+ const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
204
+ try {
205
+ const parsed = new URL(hasScheme ? trimmed : `http://${trimmed}`);
206
+ if (parsed.port)
207
+ return Number(parsed.port);
208
+ if (hasScheme) {
209
+ if (parsed.protocol === "https:")
210
+ return 443;
211
+ if (parsed.protocol === "http:")
212
+ return 80;
213
+ }
214
+ }
215
+ catch {
216
+ const portMatch = trimmed.match(/:(\d{2,5})(?:\/|$)/);
217
+ if (portMatch)
218
+ return Number(portMatch[1]);
219
+ }
220
+ return undefined;
221
+ };
222
+ const extractOpenApiPorts = (content) => {
223
+ const doc = parseOpenApi(content);
224
+ const ports = [];
225
+ const servers = Array.isArray(doc?.servers) ? doc.servers : [];
226
+ for (const server of servers) {
227
+ if (!server || typeof server.url !== "string")
228
+ continue;
229
+ const port = extractPortFromUrl(server.url);
230
+ if (port)
231
+ ports.push(port);
232
+ }
233
+ return ports;
234
+ };
235
+ const buildIssue = (input) => ({
236
+ id: input.id,
237
+ gateId: "gate-deployment-blueprint-validator",
238
+ severity: input.severity,
239
+ category: "deployment",
240
+ artifact: "deployment",
241
+ message: input.message,
242
+ remediation: input.remediation,
243
+ location: input.location,
244
+ metadata: input.metadata,
245
+ });
246
+ export const runDeploymentBlueprintGate = async (input) => {
247
+ const { artifacts, buildReady } = input;
248
+ const blueprintRecords = artifacts.blueprints ?? [];
249
+ if (blueprintRecords.length === 0) {
250
+ return {
251
+ gateId: "gate-deployment-blueprint-validator",
252
+ gateName: "Deployment Blueprint Validator",
253
+ status: "skipped",
254
+ issues: [],
255
+ notes: ["No deployment blueprint artifacts available for validation."],
256
+ };
257
+ }
258
+ if (!artifacts.sds) {
259
+ return {
260
+ gateId: "gate-deployment-blueprint-validator",
261
+ gateName: "Deployment Blueprint Validator",
262
+ status: "skipped",
263
+ issues: [],
264
+ notes: ["No SDS artifact available for deployment blueprint validation."],
265
+ };
266
+ }
267
+ const issues = [];
268
+ const notes = [];
269
+ const envExampleRecord = blueprintRecords.find((record) => path.basename(record.path).toLowerCase().endsWith(".env.example"));
270
+ const envDocRecord = blueprintRecords.find((record) => path.basename(record.path).toLowerCase().includes("env-secrets"));
271
+ const manifestRecords = blueprintRecords.filter((record) => record !== envExampleRecord && record !== envDocRecord);
272
+ let envExample;
273
+ if (envExampleRecord) {
274
+ try {
275
+ envExample = parseEnvExample(await fs.readFile(envExampleRecord.path, "utf8"));
276
+ }
277
+ catch (error) {
278
+ notes.push(`Unable to read env example ${envExampleRecord.path}: ${error.message ?? String(error)}`);
279
+ }
280
+ }
281
+ let envDocNames;
282
+ if (envDocRecord) {
283
+ try {
284
+ envDocNames = parseEnvDoc(await fs.readFile(envDocRecord.path, "utf8"));
285
+ }
286
+ catch (error) {
287
+ notes.push(`Unable to read env mapping doc ${envDocRecord.path}: ${error.message ?? String(error)}`);
288
+ }
289
+ }
290
+ const manifestEnvRefs = new Map();
291
+ for (const record of manifestRecords) {
292
+ const refs = await collectEnvReferences(record);
293
+ refs.forEach((value, key) => {
294
+ if (!manifestEnvRefs.has(key))
295
+ manifestEnvRefs.set(key, value);
296
+ });
297
+ }
298
+ if (manifestEnvRefs.size > 0) {
299
+ if (!envExampleRecord) {
300
+ issues.push(buildIssue({
301
+ id: "gate-deployment-blueprint-missing-env-example",
302
+ severity: "high",
303
+ message: "Deployment blueprint is missing .env.example for manifest env vars.",
304
+ remediation: "Provide a .env.example file listing all manifest environment variables.",
305
+ location: {
306
+ kind: "heading",
307
+ heading: "Deployment",
308
+ path: manifestRecords[0]?.path,
309
+ },
310
+ metadata: { issueType: "missing_env_example_file" },
311
+ }));
312
+ }
313
+ if (!envDocRecord) {
314
+ issues.push(buildIssue({
315
+ id: "gate-deployment-blueprint-missing-env-doc",
316
+ severity: "medium",
317
+ message: "Deployment blueprint is missing env-secrets documentation.",
318
+ remediation: "Document deployment environment variables in env-secrets.md.",
319
+ location: {
320
+ kind: "heading",
321
+ heading: "Deployment",
322
+ path: manifestRecords[0]?.path,
323
+ },
324
+ metadata: { issueType: "missing_env_doc_file" },
325
+ }));
326
+ }
327
+ }
328
+ if (envExample) {
329
+ for (const [name, reference] of manifestEnvRefs.entries()) {
330
+ if (!envExample.names.has(name)) {
331
+ issues.push(buildIssue({
332
+ id: `gate-deployment-blueprint-missing-env-example-${name.toLowerCase()}`,
333
+ severity: "high",
334
+ message: `Environment variable ${name} is referenced in manifests but missing from .env.example.`,
335
+ remediation: `Add ${name} to .env.example and document its usage.`,
336
+ location: {
337
+ kind: "line_range",
338
+ path: reference.path,
339
+ lineStart: reference.line,
340
+ lineEnd: reference.line,
341
+ excerpt: name,
342
+ },
343
+ metadata: { issueType: "missing_env_example", name },
344
+ }));
345
+ }
346
+ }
347
+ }
348
+ if (envDocNames) {
349
+ for (const [name, reference] of manifestEnvRefs.entries()) {
350
+ if (!envDocNames.has(name)) {
351
+ issues.push(buildIssue({
352
+ id: `gate-deployment-blueprint-missing-env-doc-${name.toLowerCase()}`,
353
+ severity: "medium",
354
+ message: `Environment variable ${name} is referenced in manifests but missing from env-secrets.md.`,
355
+ remediation: `Document ${name} in env-secrets.md with usage details.`,
356
+ location: {
357
+ kind: "line_range",
358
+ path: reference.path,
359
+ lineStart: reference.line,
360
+ lineEnd: reference.line,
361
+ excerpt: name,
362
+ },
363
+ metadata: { issueType: "missing_env_documentation", name },
364
+ }));
365
+ }
366
+ }
367
+ }
368
+ let expectedDependencies = new Map();
369
+ try {
370
+ const sdsContent = await fs.readFile(artifacts.sds.path, "utf8");
371
+ expectedDependencies = extractDependencies(sdsContent);
372
+ }
373
+ catch (error) {
374
+ notes.push(`Unable to read SDS ${artifacts.sds.path}: ${error.message ?? String(error)}`);
375
+ }
376
+ const detectedServices = await extractServicesFromBlueprints(blueprintRecords);
377
+ const detectedDependencyKeys = new Set();
378
+ detectedServices.forEach((_value, name) => {
379
+ const key = DEPENDENCY_DEFINITIONS.find((entry) => entry.key === name)?.key;
380
+ if (key)
381
+ detectedDependencyKeys.add(key);
382
+ });
383
+ for (const dependency of DEPENDENCY_DEFINITIONS) {
384
+ const expected = expectedDependencies.has(dependency.key);
385
+ const actual = detectedDependencyKeys.has(dependency.key);
386
+ if (expected && !actual) {
387
+ const match = expectedDependencies.get(dependency.key);
388
+ issues.push(buildIssue({
389
+ id: `gate-deployment-blueprint-missing-dependency-${dependency.key}`,
390
+ severity: "high",
391
+ message: `Deployment blueprint is missing ${dependency.label} even though SDS references it.`,
392
+ remediation: `Add ${dependency.label} services/manifests to the deployment blueprint.`,
393
+ location: {
394
+ kind: "line_range",
395
+ path: artifacts.sds.path,
396
+ lineStart: match?.line ?? 1,
397
+ lineEnd: match?.line ?? 1,
398
+ excerpt: match?.keyword ?? dependency.label,
399
+ },
400
+ metadata: { issueType: "missing_dependency", dependency: dependency.key },
401
+ }));
402
+ }
403
+ if (!expected && actual) {
404
+ const serviceInfo = detectedServices.get(dependency.key);
405
+ issues.push(buildIssue({
406
+ id: `gate-deployment-blueprint-unexpected-dependency-${dependency.key}`,
407
+ severity: "medium",
408
+ message: `Deployment blueprint includes ${dependency.label} but SDS does not select it.`,
409
+ remediation: `Remove ${dependency.label} from manifests or update SDS decisions.`,
410
+ location: {
411
+ kind: "line_range",
412
+ path: serviceInfo?.path ?? envExampleRecord?.path ?? artifacts.sds.path,
413
+ lineStart: serviceInfo?.line ?? 1,
414
+ lineEnd: serviceInfo?.line ?? 1,
415
+ excerpt: dependency.label,
416
+ },
417
+ metadata: { issueType: "unexpected_dependency", dependency: dependency.key },
418
+ }));
419
+ }
420
+ }
421
+ const openapiPorts = [];
422
+ if (artifacts.openapi?.length) {
423
+ for (const record of artifacts.openapi) {
424
+ try {
425
+ const content = await fs.readFile(record.path, "utf8");
426
+ const ports = extractOpenApiPorts(content);
427
+ if (ports.length > 0) {
428
+ openapiPorts.push(...ports);
429
+ }
430
+ }
431
+ catch (error) {
432
+ notes.push(`Unable to read OpenAPI spec ${record.path}: ${error.message ?? String(error)}`);
433
+ }
434
+ }
435
+ }
436
+ if (openapiPorts.length > 0) {
437
+ const expectedPort = envExample?.port;
438
+ if (!expectedPort) {
439
+ issues.push(buildIssue({
440
+ id: "gate-deployment-blueprint-missing-service-port",
441
+ severity: "medium",
442
+ message: "Deployment blueprint is missing SERVICE_PORT for OpenAPI server alignment.",
443
+ remediation: "Include SERVICE_PORT in .env.example and ensure manifests reference it.",
444
+ location: {
445
+ kind: "heading",
446
+ heading: "Deployment",
447
+ path: envExampleRecord?.path ?? artifacts.sds.path,
448
+ },
449
+ metadata: { issueType: "missing_service_port" },
450
+ }));
451
+ }
452
+ else if (!openapiPorts.includes(expectedPort)) {
453
+ issues.push(buildIssue({
454
+ id: "gate-deployment-blueprint-port-mismatch",
455
+ severity: "high",
456
+ message: `Deployment blueprint uses port ${expectedPort} but OpenAPI servers use ${openapiPorts.join(", ")}.`,
457
+ remediation: "Align SERVICE_PORT and manifests with the OpenAPI server port.",
458
+ location: {
459
+ kind: "line_range",
460
+ path: envExampleRecord?.path ?? artifacts.sds.path,
461
+ lineStart: envExample?.lineMap.get(SERVICE_PORT_NAME) ?? 1,
462
+ lineEnd: envExample?.lineMap.get(SERVICE_PORT_NAME) ?? 1,
463
+ excerpt: SERVICE_PORT_NAME,
464
+ },
465
+ metadata: {
466
+ issueType: "port_mismatch",
467
+ servicePort: expectedPort,
468
+ openapiPorts,
469
+ },
470
+ }));
471
+ }
472
+ }
473
+ const status = issues.length === 0 ? "pass" : buildReady ? "fail" : "warn";
474
+ return {
475
+ gateId: "gate-deployment-blueprint-validator",
476
+ gateName: "Deployment Blueprint Validator",
477
+ status,
478
+ issues,
479
+ notes: notes.length ? notes : undefined,
480
+ metadata: {
481
+ envVarsReferenced: Array.from(manifestEnvRefs.keys()),
482
+ serviceDependenciesExpected: Array.from(expectedDependencies.keys()),
483
+ serviceDependenciesDetected: Array.from(detectedDependencyKeys),
484
+ openapiPorts,
485
+ },
486
+ };
487
+ };
@@ -0,0 +1,8 @@
1
+ import { DocgenArtifactInventory } from "../../DocgenRunContext.js";
2
+ import { ReviewGateResult } from "../ReviewTypes.js";
3
+ export interface NoMaybesGateInput {
4
+ artifacts: DocgenArtifactInventory;
5
+ enabled: boolean;
6
+ }
7
+ export declare const runNoMaybesGate: (input: NoMaybesGateInput) => Promise<ReviewGateResult>;
8
+ //# sourceMappingURL=NoMaybesGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoMaybesGate.d.ts","sourceRoot":"","sources":["../../../../../src/services/docs/review/gates/NoMaybesGate.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAA+B,MAAM,mBAAmB,CAAC;AAElF,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,uBAAuB,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;CAClB;AAoID,eAAO,MAAM,eAAe,GAAU,OAAO,iBAAiB,KAAG,OAAO,CAAC,gBAAgB,CAwCxF,CAAC"}
@@ -0,0 +1,145 @@
1
+ import { promises as fs } from "node:fs";
2
+ const INDECISIVE_PATTERNS = [
3
+ { id: "maybe", label: "Maybe language", pattern: /\bmaybe\b/i },
4
+ { id: "optional", label: "Optional language", pattern: /\boptional\b/i },
5
+ { id: "could", label: "Could language", pattern: /\bcould\b/i },
6
+ { id: "might", label: "Might language", pattern: /\bmight\b/i },
7
+ { id: "possibly", label: "Possibly language", pattern: /\bpossibly\b/i },
8
+ { id: "either", label: "Either/Or language", pattern: /\beither\b/i },
9
+ { id: "tbd", label: "TBD language", pattern: /\btbd\b/i },
10
+ ];
11
+ const DECISION_HEADINGS = [
12
+ /architecture/i,
13
+ /technology stack/i,
14
+ /tech stack/i,
15
+ /deployment/i,
16
+ /security/i,
17
+ /data model/i,
18
+ /interfaces/i,
19
+ /operations/i,
20
+ /ops/i,
21
+ ];
22
+ const OPTIONS_HEADINGS = [/options considered/i, /alternatives/i, /trade-?offs/i, /choices/i];
23
+ const isFenceLine = (line) => /^```|^~~~/.test(line.trim());
24
+ const headingLevel = (heading) => heading.match(/^#{1,6}/)?.[0].length ?? 0;
25
+ const matchesHeading = (heading, patterns) => patterns.some((pattern) => pattern.test(heading));
26
+ const buildIssue = (input) => {
27
+ const severity = "medium";
28
+ const message = `${input.pattern.label} detected in decision-required section.`;
29
+ return {
30
+ id: `gate-no-maybes-${input.record.kind}-${input.pattern.id}-${input.lineNumber}`,
31
+ gateId: "gate-no-maybes",
32
+ severity,
33
+ category: "decision",
34
+ artifact: input.record.kind,
35
+ message,
36
+ remediation: "Replace indecisive language with a concrete decision.",
37
+ location: {
38
+ kind: "line_range",
39
+ path: input.record.path,
40
+ lineStart: input.lineNumber,
41
+ lineEnd: input.lineNumber,
42
+ excerpt: input.pattern.label,
43
+ },
44
+ metadata: {
45
+ patternId: input.pattern.id,
46
+ heading: input.heading,
47
+ },
48
+ };
49
+ };
50
+ const scanRecord = async (record) => {
51
+ const issues = [];
52
+ const notes = [];
53
+ try {
54
+ const content = await fs.readFile(record.path, "utf8");
55
+ const lines = content.split(/\r?\n/);
56
+ let inFence = false;
57
+ let decisionLevel = null;
58
+ let optionsLevel = null;
59
+ let currentHeading;
60
+ for (let i = 0; i < lines.length; i += 1) {
61
+ const line = lines[i] ?? "";
62
+ const trimmed = line.trim();
63
+ if (!trimmed)
64
+ continue;
65
+ if (isFenceLine(trimmed)) {
66
+ inFence = !inFence;
67
+ continue;
68
+ }
69
+ const headingMatch = trimmed.match(/^#{1,6}\s+(.*)$/);
70
+ if (headingMatch) {
71
+ const level = headingLevel(trimmed);
72
+ currentHeading = headingMatch[1]?.trim() || undefined;
73
+ if (currentHeading && matchesHeading(currentHeading, OPTIONS_HEADINGS)) {
74
+ optionsLevel = level;
75
+ }
76
+ else if (currentHeading && matchesHeading(currentHeading, DECISION_HEADINGS)) {
77
+ decisionLevel = level;
78
+ optionsLevel = null;
79
+ }
80
+ else {
81
+ if (decisionLevel !== null && level <= decisionLevel)
82
+ decisionLevel = null;
83
+ if (optionsLevel !== null && level <= optionsLevel)
84
+ optionsLevel = null;
85
+ }
86
+ continue;
87
+ }
88
+ if (inFence)
89
+ continue;
90
+ const inDecisionSection = decisionLevel !== null && optionsLevel === null;
91
+ if (!inDecisionSection)
92
+ continue;
93
+ for (const pattern of INDECISIVE_PATTERNS) {
94
+ if (!pattern.pattern.test(trimmed))
95
+ continue;
96
+ issues.push(buildIssue({
97
+ record,
98
+ lineNumber: i + 1,
99
+ pattern,
100
+ heading: currentHeading,
101
+ }));
102
+ }
103
+ }
104
+ }
105
+ catch (error) {
106
+ notes.push(`Unable to scan ${record.path}: ${error.message ?? String(error)}`);
107
+ }
108
+ return { issues, notes };
109
+ };
110
+ export const runNoMaybesGate = async (input) => {
111
+ if (!input.enabled) {
112
+ return {
113
+ gateId: "gate-no-maybes",
114
+ gateName: "No Maybes",
115
+ status: "skipped",
116
+ issues: [],
117
+ notes: ["No-maybes gate disabled."],
118
+ };
119
+ }
120
+ const records = [input.artifacts.pdr, input.artifacts.sds].filter((record) => Boolean(record));
121
+ if (records.length === 0) {
122
+ return {
123
+ gateId: "gate-no-maybes",
124
+ gateName: "No Maybes",
125
+ status: "skipped",
126
+ issues: [],
127
+ notes: ["No PDR/SDS artifacts available for indecisive language checks."],
128
+ };
129
+ }
130
+ const issues = [];
131
+ const notes = [];
132
+ for (const record of records) {
133
+ const result = await scanRecord(record);
134
+ issues.push(...result.issues);
135
+ notes.push(...result.notes);
136
+ }
137
+ const status = issues.length > 0 ? "fail" : "pass";
138
+ return {
139
+ gateId: "gate-no-maybes",
140
+ gateName: "No Maybes",
141
+ status,
142
+ issues,
143
+ notes: notes.length > 0 ? notes : undefined,
144
+ };
145
+ };
@@ -0,0 +1,7 @@
1
+ import { DocgenArtifactInventory } from "../../DocgenRunContext.js";
2
+ import { ReviewGateResult } from "../ReviewTypes.js";
3
+ export interface OpenApiCoverageGateInput {
4
+ artifacts: DocgenArtifactInventory;
5
+ }
6
+ export declare const runOpenApiCoverageGate: (input: OpenApiCoverageGateInput) => Promise<ReviewGateResult>;
7
+ //# sourceMappingURL=OpenApiCoverageGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiCoverageGate.d.ts","sourceRoot":"","sources":["../../../../../src/services/docs/review/gates/OpenApiCoverageGate.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAA+B,MAAM,mBAAmB,CAAC;AAOlF,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,uBAAuB,CAAC;CACpC;AA2ND,eAAO,MAAM,sBAAsB,GACjC,OAAO,wBAAwB,KAC9B,OAAO,CAAC,gBAAgB,CA+E1B,CAAC"}