@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,266 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { extractOpenApiPaths, findOpenApiPathLine, normalizeOpenApiPath, } from "../../../openapi/OpenApiService.js";
3
+ const ISSUE_SEVERITY = {
4
+ doc_missing_openapi: "high",
5
+ openapi_missing_docs: "medium",
6
+ };
7
+ const RELEVANT_HEADING = /\b(interfaces?|api(?:s)?|endpoints?|routes?|contracts?)\b/i;
8
+ const EXAMPLE_HEADING = /example|sample/i;
9
+ const PATH_PATTERN = /\/[A-Za-z0-9._~{}-]+(?:\/[A-Za-z0-9._~{}-]+)*/g;
10
+ const URL_PATTERN = /https?:\/\/[^\s)]+/g;
11
+ const isFenceLine = (line) => /^```|^~~~/.test(line.trim());
12
+ const isRelevantHeading = (title) => RELEVANT_HEADING.test(title);
13
+ const isExampleHeading = (title) => EXAMPLE_HEADING.test(title);
14
+ const sanitizePath = (candidate) => {
15
+ let cleaned = candidate.trim();
16
+ cleaned = cleaned.replace(/^[`"'\\[(]+/, "");
17
+ cleaned = cleaned.replace(/[`"',.;:!?]+$/g, "");
18
+ cleaned = cleaned.replace(/[\]\)}]+$/g, "");
19
+ return cleaned;
20
+ };
21
+ const extractPathsFromLine = (line) => {
22
+ const results = [];
23
+ const urls = line.match(URL_PATTERN) ?? [];
24
+ for (const url of urls) {
25
+ try {
26
+ const parsed = new URL(url);
27
+ if (parsed.pathname && parsed.pathname !== "/") {
28
+ results.push(parsed.pathname);
29
+ }
30
+ }
31
+ catch {
32
+ // ignore invalid URLs
33
+ }
34
+ }
35
+ const scrubbed = line.replace(URL_PATTERN, " ");
36
+ const matches = scrubbed.matchAll(PATH_PATTERN);
37
+ for (const match of matches) {
38
+ const candidate = sanitizePath(match[0] ?? "");
39
+ if (!candidate || candidate === "/" || candidate.startsWith("//"))
40
+ continue;
41
+ results.push(candidate);
42
+ }
43
+ return results;
44
+ };
45
+ const extractDocEndpoints = async (record) => {
46
+ const entries = [];
47
+ const notes = [];
48
+ try {
49
+ const content = await fs.readFile(record.path, "utf8");
50
+ const lines = content.split(/\r?\n/);
51
+ let inFence = false;
52
+ let capture = false;
53
+ let sectionLevel = 0;
54
+ let skipExample = false;
55
+ let skipLevel = 0;
56
+ let currentHeading;
57
+ for (let i = 0; i < lines.length; i += 1) {
58
+ const line = lines[i] ?? "";
59
+ const trimmed = line.trim();
60
+ if (isFenceLine(trimmed)) {
61
+ inFence = !inFence;
62
+ continue;
63
+ }
64
+ if (inFence)
65
+ continue;
66
+ const headingMatch = trimmed.match(/^(#{1,6})\s+(.*)$/);
67
+ if (headingMatch) {
68
+ const level = headingMatch[1]?.length ?? 0;
69
+ if (skipExample && level <= skipLevel) {
70
+ skipExample = false;
71
+ }
72
+ const title = headingMatch[2]?.trim() ?? "";
73
+ currentHeading = title || undefined;
74
+ const relevant = isRelevantHeading(title);
75
+ if (relevant) {
76
+ capture = true;
77
+ sectionLevel = level;
78
+ skipExample = isExampleHeading(title);
79
+ skipLevel = skipExample ? level : 0;
80
+ continue;
81
+ }
82
+ if (capture && level <= sectionLevel) {
83
+ capture = false;
84
+ }
85
+ if (capture && isExampleHeading(title)) {
86
+ skipExample = true;
87
+ skipLevel = level;
88
+ }
89
+ continue;
90
+ }
91
+ if (!capture || skipExample || !trimmed)
92
+ continue;
93
+ const candidates = extractPathsFromLine(line);
94
+ for (const candidate of candidates) {
95
+ const normalized = normalizeOpenApiPath(candidate);
96
+ if (!normalized || normalized === "/")
97
+ continue;
98
+ entries.push({
99
+ path: candidate,
100
+ normalized,
101
+ lineNumber: i + 1,
102
+ heading: currentHeading,
103
+ record,
104
+ });
105
+ }
106
+ }
107
+ }
108
+ catch (error) {
109
+ notes.push(`Unable to read doc ${record.path}: ${error.message ?? String(error)}`);
110
+ }
111
+ return { entries, notes };
112
+ };
113
+ const extractOpenApiEndpoints = async (records) => {
114
+ const entries = [];
115
+ const notes = [];
116
+ for (const record of records) {
117
+ try {
118
+ const raw = await fs.readFile(record.path, "utf8");
119
+ const result = extractOpenApiPaths(raw);
120
+ if (result.errors.length > 0) {
121
+ notes.push(...result.errors.map((error) => `${record.path}: ${error}`));
122
+ }
123
+ for (const entry of result.paths) {
124
+ const normalized = normalizeOpenApiPath(entry);
125
+ if (!normalized || normalized === "/")
126
+ continue;
127
+ const lineNumber = findOpenApiPathLine(raw, entry) ?? 1;
128
+ entries.push({
129
+ path: entry,
130
+ normalized,
131
+ lineNumber,
132
+ record,
133
+ });
134
+ }
135
+ }
136
+ catch (error) {
137
+ notes.push(`Unable to read OpenAPI spec ${record.path}: ${error.message ?? String(error)}`);
138
+ }
139
+ }
140
+ return { entries, notes };
141
+ };
142
+ const dedupeByNormalized = (entries) => {
143
+ const seen = new Set();
144
+ const result = [];
145
+ for (const entry of entries) {
146
+ if (seen.has(entry.normalized))
147
+ continue;
148
+ seen.add(entry.normalized);
149
+ result.push(entry);
150
+ }
151
+ return result;
152
+ };
153
+ const summarizePaths = (label, entries) => {
154
+ if (entries.length === 0)
155
+ return undefined;
156
+ const preview = entries.slice(0, 5).map((entry) => entry.path).join(", ");
157
+ const suffix = entries.length > 5 ? ` (+${entries.length - 5} more)` : "";
158
+ return `${label} (${entries.length}): ${preview}${suffix}`;
159
+ };
160
+ const buildIssue = (input) => {
161
+ const { issueType, entry } = input;
162
+ let message = "";
163
+ let remediation = "";
164
+ switch (issueType) {
165
+ case "doc_missing_openapi":
166
+ message = `Endpoint "${entry.path}" appears in docs but is missing from OpenAPI.`;
167
+ remediation = "Add the endpoint to OpenAPI or remove it from the interface list.";
168
+ break;
169
+ case "openapi_missing_docs":
170
+ message = `OpenAPI endpoint "${entry.path}" is not mentioned in docs interface/API sections.`;
171
+ remediation = "Document the endpoint in the Interfaces/API sections.";
172
+ break;
173
+ default:
174
+ message = `OpenAPI coverage issue detected for ${entry.path}.`;
175
+ remediation = "Align doc interface lists with OpenAPI.";
176
+ }
177
+ return {
178
+ id: `gate-openapi-endpoint-coverage-${entry.record.kind}-${issueType}-${entry.lineNumber}`,
179
+ gateId: "gate-openapi-endpoint-coverage",
180
+ severity: ISSUE_SEVERITY[issueType],
181
+ category: "api",
182
+ artifact: entry.record.kind,
183
+ message,
184
+ remediation,
185
+ location: {
186
+ kind: "line_range",
187
+ path: entry.record.path,
188
+ lineStart: entry.lineNumber,
189
+ lineEnd: entry.lineNumber,
190
+ excerpt: entry.path,
191
+ },
192
+ metadata: {
193
+ issueType,
194
+ normalizedPath: entry.normalized,
195
+ heading: entry.heading,
196
+ },
197
+ };
198
+ };
199
+ export const runOpenApiCoverageGate = async (input) => {
200
+ const issues = [];
201
+ const notes = [];
202
+ const docRecords = [input.artifacts.pdr, input.artifacts.sds].filter((record) => Boolean(record));
203
+ const openapiRecords = input.artifacts.openapi ?? [];
204
+ if (docRecords.length === 0 && openapiRecords.length === 0) {
205
+ return {
206
+ gateId: "gate-openapi-endpoint-coverage",
207
+ gateName: "OpenAPI Endpoint Coverage",
208
+ status: "skipped",
209
+ issues,
210
+ notes: ["No PDR/SDS or OpenAPI artifacts available for endpoint coverage checks."],
211
+ };
212
+ }
213
+ if (docRecords.length === 0) {
214
+ notes.push("No PDR/SDS artifacts available for endpoint coverage checks.");
215
+ }
216
+ if (openapiRecords.length === 0) {
217
+ notes.push("No OpenAPI artifacts available for endpoint coverage checks.");
218
+ }
219
+ const docResults = await Promise.all(docRecords.map((record) => extractDocEndpoints(record)));
220
+ const docEntries = docResults.flatMap((result) => result.entries);
221
+ docResults.forEach((result) => notes.push(...result.notes));
222
+ const openapiResult = await extractOpenApiEndpoints(openapiRecords);
223
+ const openapiEntries = openapiResult.entries;
224
+ notes.push(...openapiResult.notes);
225
+ if (docEntries.length === 0 && openapiEntries.length === 0) {
226
+ return {
227
+ gateId: "gate-openapi-endpoint-coverage",
228
+ gateName: "OpenAPI Endpoint Coverage",
229
+ status: "skipped",
230
+ issues,
231
+ notes: notes.length > 0 ? notes : ["No endpoints detected in docs or OpenAPI."],
232
+ };
233
+ }
234
+ const uniqueDocEntries = dedupeByNormalized(docEntries);
235
+ const uniqueOpenapiEntries = dedupeByNormalized(openapiEntries);
236
+ const openapiSet = new Set(uniqueOpenapiEntries.map((entry) => entry.normalized));
237
+ const docSet = new Set(uniqueDocEntries.map((entry) => entry.normalized));
238
+ const docMissing = uniqueDocEntries.filter((entry) => !openapiSet.has(entry.normalized));
239
+ const openapiMissing = uniqueOpenapiEntries.filter((entry) => !docSet.has(entry.normalized));
240
+ for (const entry of docMissing) {
241
+ issues.push(buildIssue({ issueType: "doc_missing_openapi", entry }));
242
+ }
243
+ for (const entry of openapiMissing) {
244
+ issues.push(buildIssue({ issueType: "openapi_missing_docs", entry }));
245
+ }
246
+ const docSummary = summarizePaths("Doc endpoints missing in OpenAPI", docMissing);
247
+ if (docSummary)
248
+ notes.push(docSummary);
249
+ const openapiSummary = summarizePaths("OpenAPI endpoints missing in docs", openapiMissing);
250
+ if (openapiSummary)
251
+ notes.push(openapiSummary);
252
+ const status = issues.length > 0 ? "fail" : "pass";
253
+ return {
254
+ gateId: "gate-openapi-endpoint-coverage",
255
+ gateName: "OpenAPI Endpoint Coverage",
256
+ status,
257
+ issues,
258
+ notes: notes.length > 0 ? notes : undefined,
259
+ metadata: {
260
+ docPathCount: uniqueDocEntries.length,
261
+ openapiPathCount: uniqueOpenapiEntries.length,
262
+ docMissingCount: docMissing.length,
263
+ openapiMissingCount: openapiMissing.length,
264
+ },
265
+ };
266
+ };
@@ -0,0 +1,7 @@
1
+ import { DocgenArtifactInventory } from "../../DocgenRunContext.js";
2
+ import { ReviewGateResult } from "../ReviewTypes.js";
3
+ export interface OpenApiSchemaSanityGateInput {
4
+ artifacts: DocgenArtifactInventory;
5
+ }
6
+ export declare const runOpenApiSchemaSanityGate: (input: OpenApiSchemaSanityGateInput) => Promise<ReviewGateResult>;
7
+ //# sourceMappingURL=OpenApiSchemaSanityGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiSchemaSanityGate.d.ts","sourceRoot":"","sources":["../../../../../src/services/docs/review/gates/OpenApiSchemaSanityGate.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAe,MAAM,mBAAmB,CAAC;AAGlE,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,uBAAuB,CAAC;CACpC;AA2BD,eAAO,MAAM,0BAA0B,GACrC,OAAO,4BAA4B,KAClC,OAAO,CAAC,gBAAgB,CAwC1B,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { validateOpenApiSchemaContent } from "../../../openapi/OpenApiService.js";
3
+ const buildIssue = (input) => {
4
+ const excerpt = input.error.length > 120 ? `${input.error.slice(0, 117)}...` : input.error;
5
+ return {
6
+ id: `gate-openapi-schema-sanity-${input.index + 1}`,
7
+ gateId: "gate-openapi-schema-sanity",
8
+ severity: "high",
9
+ category: "api",
10
+ artifact: "openapi",
11
+ message: input.error,
12
+ remediation: "Fix OpenAPI schema errors and regenerate the spec.",
13
+ location: {
14
+ kind: "line_range",
15
+ path: input.record.path,
16
+ lineStart: 1,
17
+ lineEnd: 1,
18
+ excerpt,
19
+ },
20
+ metadata: { recordPath: input.record.path },
21
+ };
22
+ };
23
+ export const runOpenApiSchemaSanityGate = async (input) => {
24
+ const records = input.artifacts.openapi ?? [];
25
+ if (records.length === 0) {
26
+ return {
27
+ gateId: "gate-openapi-schema-sanity",
28
+ gateName: "OpenAPI Schema Sanity",
29
+ status: "skipped",
30
+ issues: [],
31
+ notes: ["No OpenAPI artifacts available for schema sanity checks."],
32
+ };
33
+ }
34
+ const issues = [];
35
+ const notes = [];
36
+ for (const record of records) {
37
+ try {
38
+ const content = await fs.readFile(record.path, "utf8");
39
+ const result = validateOpenApiSchemaContent(content);
40
+ if (result.errors.length > 0) {
41
+ result.errors.forEach((error, index) => {
42
+ issues.push(buildIssue({ record, error, index }));
43
+ });
44
+ }
45
+ }
46
+ catch (error) {
47
+ notes.push(`Unable to read OpenAPI spec ${record.path}: ${error.message ?? String(error)}`);
48
+ }
49
+ }
50
+ const status = issues.length === 0 ? "pass" : "fail";
51
+ return {
52
+ gateId: "gate-openapi-schema-sanity",
53
+ gateName: "OpenAPI Schema Sanity",
54
+ status,
55
+ issues,
56
+ notes: notes.length > 0 ? notes : undefined,
57
+ metadata: { issueCount: issues.length, artifactCount: records.length },
58
+ };
59
+ };
@@ -0,0 +1,7 @@
1
+ import { DocgenArtifactInventory } from "../../DocgenRunContext.js";
2
+ import { ReviewGateResult } from "../ReviewTypes.js";
3
+ export interface OpenQuestionsGateInput {
4
+ artifacts: DocgenArtifactInventory;
5
+ }
6
+ export declare const runOpenQuestionsGate: (input: OpenQuestionsGateInput) => Promise<ReviewGateResult>;
7
+ //# sourceMappingURL=OpenQuestionsGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenQuestionsGate.d.ts","sourceRoot":"","sources":["../../../../../src/services/docs/review/gates/OpenQuestionsGate.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAA+B,MAAM,mBAAmB,CAAC;AAElF,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,uBAAuB,CAAC;CACpC;AAuLD,eAAO,MAAM,oBAAoB,GAC/B,OAAO,sBAAsB,KAC5B,OAAO,CAAC,gBAAgB,CA+C1B,CAAC"}
@@ -0,0 +1,200 @@
1
+ import { promises as fs } from "node:fs";
2
+ const OPTIONAL_HINTS = [
3
+ "optional",
4
+ "nice to have",
5
+ "future",
6
+ "later",
7
+ "explore",
8
+ "could",
9
+ "might",
10
+ "maybe",
11
+ ];
12
+ const REQUIRED_HINTS = [
13
+ "must",
14
+ "required",
15
+ "need to decide",
16
+ "decision",
17
+ "blocker",
18
+ "blocking",
19
+ "critical",
20
+ "tbd",
21
+ "to be determined",
22
+ ];
23
+ const IMPLICIT_PATTERNS = [
24
+ /\?$/,
25
+ /\bshould we\b/i,
26
+ /\bneeds? decision\b/i,
27
+ /\bdecide (whether|if|on)\b/i,
28
+ /\bto be determined\b/i,
29
+ /\btbd\b/i,
30
+ /\bopen question\b/i,
31
+ ];
32
+ const isFenceLine = (line) => /^```|^~~~/.test(line.trim());
33
+ const isExampleHeading = (heading) => /example|sample/i.test(heading);
34
+ const isOpenQuestionsHeading = (heading) => /open (questions?|issues?|items?)|unresolved questions?/i.test(heading);
35
+ const RESOLVED_HINTS = [/^\s*resolved[:\]]/i, /^\s*decision:/i, /\[resolved\]/i];
36
+ const isResolvedLine = (line) => RESOLVED_HINTS.some((pattern) => pattern.test(line.trim()));
37
+ const normalizeQuestion = (text) => text
38
+ .toLowerCase()
39
+ .replace(/[^a-z0-9]+/g, " ")
40
+ .trim();
41
+ const classifyRequired = (text, inOpenSection) => {
42
+ const lower = text.toLowerCase();
43
+ if (lower.includes("[optional]"))
44
+ return false;
45
+ if (lower.includes("[required]"))
46
+ return true;
47
+ if (OPTIONAL_HINTS.some((hint) => lower.includes(hint)))
48
+ return false;
49
+ if (REQUIRED_HINTS.some((hint) => lower.includes(hint)))
50
+ return true;
51
+ return inOpenSection;
52
+ };
53
+ const resolveTarget = (text, record) => {
54
+ const lower = text.toLowerCase();
55
+ if (/(openapi|endpoint|api|route)/i.test(lower))
56
+ return "openapi";
57
+ if (/(schema|database|sql|table)/i.test(lower))
58
+ return "sql";
59
+ if (/(deploy|deployment|k8s|kubernetes|docker|infra)/i.test(lower))
60
+ return "deployment";
61
+ return record.kind;
62
+ };
63
+ const buildIssue = (question) => {
64
+ const severity = question.required ? "high" : "low";
65
+ const message = question.required
66
+ ? `Open question requires resolution: ${question.text}`
67
+ : `Optional exploration: ${question.text}`;
68
+ const remediation = question.required
69
+ ? `Resolve this decision in ${question.target}.`
70
+ : `Consider addressing this question in ${question.target}.`;
71
+ return {
72
+ id: `gate-open-questions-${question.record.kind}-${question.lineNumber}`,
73
+ gateId: "gate-open-questions",
74
+ severity,
75
+ category: "open_questions",
76
+ artifact: question.record.kind,
77
+ message,
78
+ remediation,
79
+ location: {
80
+ kind: "line_range",
81
+ path: question.record.path,
82
+ lineStart: question.lineNumber,
83
+ lineEnd: question.lineNumber,
84
+ excerpt: question.text,
85
+ },
86
+ metadata: {
87
+ question: question.text,
88
+ normalized: question.normalized,
89
+ required: question.required,
90
+ target: question.target,
91
+ heading: question.heading,
92
+ },
93
+ };
94
+ };
95
+ const cleanQuestionText = (line) => {
96
+ const trimmed = line.trim();
97
+ const withoutBullet = trimmed.replace(/^[-*+]\s+/, "").replace(/^\d+\.\s+/, "");
98
+ return withoutBullet.trim();
99
+ };
100
+ const isImplicitQuestion = (line) => IMPLICIT_PATTERNS.some((pattern) => pattern.test(line.trim()));
101
+ const extractQuestions = async (record) => {
102
+ const notes = [];
103
+ try {
104
+ const content = await fs.readFile(record.path, "utf8");
105
+ const lines = content.split(/\r?\n/);
106
+ const questions = [];
107
+ let inFence = false;
108
+ let allowSection = false;
109
+ let inOpenSection = false;
110
+ let currentHeading;
111
+ for (let i = 0; i < lines.length; i += 1) {
112
+ const line = lines[i] ?? "";
113
+ const trimmed = line.trim();
114
+ if (!trimmed)
115
+ continue;
116
+ if (isFenceLine(trimmed)) {
117
+ inFence = !inFence;
118
+ continue;
119
+ }
120
+ const headingMatch = trimmed.match(/^#{1,6}\s+(.*)$/);
121
+ if (headingMatch) {
122
+ currentHeading = headingMatch[1]?.trim() || undefined;
123
+ allowSection = currentHeading ? isExampleHeading(currentHeading) : false;
124
+ inOpenSection = currentHeading ? isOpenQuestionsHeading(currentHeading) : false;
125
+ continue;
126
+ }
127
+ if (inFence || allowSection)
128
+ continue;
129
+ if (isResolvedLine(trimmed))
130
+ continue;
131
+ const explicitQuestion = inOpenSection;
132
+ const implicitQuestion = !inOpenSection && isImplicitQuestion(trimmed);
133
+ if (!explicitQuestion && !implicitQuestion)
134
+ continue;
135
+ const questionText = cleanQuestionText(trimmed);
136
+ if (!questionText)
137
+ continue;
138
+ const normalized = normalizeQuestion(questionText);
139
+ if (!normalized)
140
+ continue;
141
+ questions.push({
142
+ text: questionText,
143
+ normalized,
144
+ required: classifyRequired(questionText, inOpenSection),
145
+ target: resolveTarget(questionText, record),
146
+ record,
147
+ lineNumber: i + 1,
148
+ heading: currentHeading,
149
+ });
150
+ }
151
+ return { questions, notes };
152
+ }
153
+ catch (error) {
154
+ notes.push(`Unable to scan ${record.path}: ${error.message ?? String(error)}`);
155
+ return { questions: [], notes };
156
+ }
157
+ };
158
+ export const runOpenQuestionsGate = async (input) => {
159
+ const records = [input.artifacts.pdr, input.artifacts.sds].filter((record) => Boolean(record));
160
+ if (records.length === 0) {
161
+ return {
162
+ gateId: "gate-open-questions",
163
+ gateName: "Open Questions Extraction",
164
+ status: "skipped",
165
+ issues: [],
166
+ notes: ["No PDR/SDS artifacts available for open question extraction."],
167
+ };
168
+ }
169
+ const notes = [];
170
+ const extracted = [];
171
+ for (const record of records) {
172
+ const result = await extractQuestions(record);
173
+ extracted.push(...result.questions);
174
+ notes.push(...result.notes);
175
+ }
176
+ const seen = new Set();
177
+ const unique = [];
178
+ for (const question of extracted) {
179
+ if (seen.has(question.normalized))
180
+ continue;
181
+ seen.add(question.normalized);
182
+ unique.push(question);
183
+ }
184
+ const issues = unique.map(buildIssue);
185
+ const requiredCount = unique.filter((q) => q.required).length;
186
+ const optionalCount = unique.length - requiredCount;
187
+ const status = requiredCount > 0 ? "fail" : optionalCount > 0 ? "warn" : "pass";
188
+ return {
189
+ gateId: "gate-open-questions",
190
+ gateName: "Open Questions Extraction",
191
+ status,
192
+ issues,
193
+ notes: notes.length > 0 ? notes : undefined,
194
+ metadata: {
195
+ questionCount: unique.length,
196
+ requiredCount,
197
+ optionalCount,
198
+ },
199
+ };
200
+ };
@@ -0,0 +1,7 @@
1
+ import { DocgenArtifactInventory } from "../../DocgenRunContext.js";
2
+ import { ReviewGateResult } from "../ReviewTypes.js";
3
+ export interface PdrInterfacesGateInput {
4
+ artifacts: DocgenArtifactInventory;
5
+ }
6
+ export declare const runPdrInterfacesGate: (input: PdrInterfacesGateInput) => Promise<ReviewGateResult>;
7
+ //# sourceMappingURL=PdrInterfacesGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PdrInterfacesGate.d.ts","sourceRoot":"","sources":["../../../../../src/services/docs/review/gates/PdrInterfacesGate.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAe,MAAM,mBAAmB,CAAC;AAGlE,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,uBAAuB,CAAC;CACpC;AAmFD,eAAO,MAAM,oBAAoB,GAC/B,OAAO,sBAAsB,KAC5B,OAAO,CAAC,gBAAgB,CAqG1B,CAAC"}