@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.
- package/CHANGELOG.md +3 -0
- package/README.md +2 -2
- package/dist/api/AgentsApi.d.ts +9 -1
- package/dist/api/AgentsApi.d.ts.map +1 -1
- package/dist/api/AgentsApi.js +201 -6
- package/dist/api/QaTasksApi.d.ts.map +1 -1
- package/dist/api/QaTasksApi.js +6 -0
- package/dist/api/TasksApi.d.ts.map +1 -1
- package/dist/api/TasksApi.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/prompts/PdrPrompts.d.ts.map +1 -1
- package/dist/prompts/PdrPrompts.js +9 -1
- package/dist/prompts/SdsPrompts.d.ts.map +1 -1
- package/dist/prompts/SdsPrompts.js +9 -0
- package/dist/services/agents/AgentRatingFormula.d.ts +27 -0
- package/dist/services/agents/AgentRatingFormula.d.ts.map +1 -0
- package/dist/services/agents/AgentRatingFormula.js +45 -0
- package/dist/services/agents/AgentRatingService.d.ts +60 -0
- package/dist/services/agents/AgentRatingService.d.ts.map +1 -0
- package/dist/services/agents/AgentRatingService.js +363 -0
- package/dist/services/agents/GatewayAgentService.d.ts +11 -0
- package/dist/services/agents/GatewayAgentService.d.ts.map +1 -1
- package/dist/services/agents/GatewayAgentService.js +525 -84
- package/dist/services/agents/GatewayHandoff.d.ts +11 -0
- package/dist/services/agents/GatewayHandoff.d.ts.map +1 -0
- package/dist/services/agents/GatewayHandoff.js +141 -0
- package/dist/services/agents/RoutingService.d.ts +1 -0
- package/dist/services/agents/RoutingService.d.ts.map +1 -1
- package/dist/services/agents/RoutingService.js +4 -4
- package/dist/services/backlog/BacklogService.d.ts +23 -0
- package/dist/services/backlog/BacklogService.d.ts.map +1 -1
- package/dist/services/backlog/BacklogService.js +62 -7
- package/dist/services/backlog/TaskOrderingHeuristics.d.ts +12 -0
- package/dist/services/backlog/TaskOrderingHeuristics.d.ts.map +1 -0
- package/dist/services/backlog/TaskOrderingHeuristics.js +56 -0
- package/dist/services/backlog/TaskOrderingService.d.ts +17 -4
- package/dist/services/backlog/TaskOrderingService.d.ts.map +1 -1
- package/dist/services/backlog/TaskOrderingService.js +538 -79
- package/dist/services/docs/DocInventory.d.ts +11 -0
- package/dist/services/docs/DocInventory.d.ts.map +1 -0
- package/dist/services/docs/DocInventory.js +230 -0
- package/dist/services/docs/DocgenRunContext.d.ts +59 -0
- package/dist/services/docs/DocgenRunContext.d.ts.map +1 -0
- package/dist/services/docs/DocgenRunContext.js +4 -0
- package/dist/services/docs/DocsService.d.ts +70 -3
- package/dist/services/docs/DocsService.d.ts.map +1 -1
- package/dist/services/docs/DocsService.js +1930 -89
- package/dist/services/docs/alignment/DocAlignmentGraph.d.ts +23 -0
- package/dist/services/docs/alignment/DocAlignmentGraph.d.ts.map +1 -0
- package/dist/services/docs/alignment/DocAlignmentGraph.js +78 -0
- package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts +19 -0
- package/dist/services/docs/alignment/DocAlignmentPatcher.d.ts.map +1 -0
- package/dist/services/docs/alignment/DocAlignmentPatcher.js +222 -0
- package/dist/services/docs/patch/DocPatchEngine.d.ts +57 -0
- package/dist/services/docs/patch/DocPatchEngine.d.ts.map +1 -0
- package/dist/services/docs/patch/DocPatchEngine.js +331 -0
- package/dist/services/docs/review/Glossary.d.ts +16 -0
- package/dist/services/docs/review/Glossary.d.ts.map +1 -0
- package/dist/services/docs/review/Glossary.js +47 -0
- package/dist/services/docs/review/ReviewReportRenderer.d.ts +3 -0
- package/dist/services/docs/review/ReviewReportRenderer.d.ts.map +1 -0
- package/dist/services/docs/review/ReviewReportRenderer.js +133 -0
- package/dist/services/docs/review/ReviewReportSchema.d.ts +39 -0
- package/dist/services/docs/review/ReviewReportSchema.d.ts.map +1 -0
- package/dist/services/docs/review/ReviewReportSchema.js +47 -0
- package/dist/services/docs/review/ReviewTypes.d.ts +76 -0
- package/dist/services/docs/review/ReviewTypes.d.ts.map +1 -0
- package/dist/services/docs/review/ReviewTypes.js +94 -0
- package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts +7 -0
- package/dist/services/docs/review/gates/AdminOpenApiSpecGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/AdminOpenApiSpecGate.js +93 -0
- package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts +7 -0
- package/dist/services/docs/review/gates/ApiPathConsistencyGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/ApiPathConsistencyGate.js +308 -0
- package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts +8 -0
- package/dist/services/docs/review/gates/BuildReadyCompletenessGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/BuildReadyCompletenessGate.js +278 -0
- package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts +8 -0
- package/dist/services/docs/review/gates/DeploymentBlueprintGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/DeploymentBlueprintGate.js +487 -0
- package/dist/services/docs/review/gates/NoMaybesGate.d.ts +8 -0
- package/dist/services/docs/review/gates/NoMaybesGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/NoMaybesGate.js +145 -0
- package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts +7 -0
- package/dist/services/docs/review/gates/OpenApiCoverageGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/OpenApiCoverageGate.js +266 -0
- package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts +7 -0
- package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/OpenApiSchemaSanityGate.js +59 -0
- package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts +7 -0
- package/dist/services/docs/review/gates/OpenQuestionsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/OpenQuestionsGate.js +200 -0
- package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts +7 -0
- package/dist/services/docs/review/gates/PdrInterfacesGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PdrInterfacesGate.js +159 -0
- package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts +8 -0
- package/dist/services/docs/review/gates/PdrOpenQuestionsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PdrOpenQuestionsGate.js +129 -0
- package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts +7 -0
- package/dist/services/docs/review/gates/PdrOwnershipGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PdrOwnershipGate.js +169 -0
- package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts +10 -0
- package/dist/services/docs/review/gates/PlaceholderArtifactGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/PlaceholderArtifactGate.js +261 -0
- package/dist/services/docs/review/gates/RfpConsentGate.d.ts +6 -0
- package/dist/services/docs/review/gates/RfpConsentGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/RfpConsentGate.js +127 -0
- package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts +7 -0
- package/dist/services/docs/review/gates/RfpDefinitionGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/RfpDefinitionGate.js +173 -0
- package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsAdaptersGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsAdaptersGate.js +196 -0
- package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsDecisionsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsDecisionsGate.js +89 -0
- package/dist/services/docs/review/gates/SdsOpsGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsOpsGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsOpsGate.js +162 -0
- package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SdsPolicyTelemetryGate.js +166 -0
- package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SqlRequiredTablesGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SqlRequiredTablesGate.js +273 -0
- package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts +7 -0
- package/dist/services/docs/review/gates/SqlSyntaxGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/SqlSyntaxGate.js +203 -0
- package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts +9 -0
- package/dist/services/docs/review/gates/TerminologyNormalizationGate.d.ts.map +1 -0
- package/dist/services/docs/review/gates/TerminologyNormalizationGate.js +217 -0
- package/dist/services/docs/review/glossary.json +47 -0
- package/dist/services/estimate/EstimateService.d.ts +2 -0
- package/dist/services/estimate/EstimateService.d.ts.map +1 -1
- package/dist/services/estimate/EstimateService.js +66 -18
- package/dist/services/estimate/VelocityService.d.ts +4 -0
- package/dist/services/estimate/VelocityService.d.ts.map +1 -1
- package/dist/services/estimate/VelocityService.js +179 -36
- package/dist/services/estimate/types.d.ts +1 -0
- package/dist/services/estimate/types.d.ts.map +1 -1
- package/dist/services/execution/GatewayTrioService.d.ts +200 -0
- package/dist/services/execution/GatewayTrioService.d.ts.map +1 -0
- package/dist/services/execution/GatewayTrioService.js +2492 -0
- package/dist/services/execution/QaApiRunner.d.ts +30 -0
- package/dist/services/execution/QaApiRunner.d.ts.map +1 -0
- package/dist/services/execution/QaApiRunner.js +881 -0
- package/dist/services/execution/QaFollowupService.d.ts +2 -0
- package/dist/services/execution/QaFollowupService.d.ts.map +1 -1
- package/dist/services/execution/QaFollowupService.js +9 -2
- package/dist/services/execution/QaPlanValidator.d.ts +10 -0
- package/dist/services/execution/QaPlanValidator.d.ts.map +1 -0
- package/dist/services/execution/QaPlanValidator.js +128 -0
- package/dist/services/execution/QaProfileService.d.ts +27 -1
- package/dist/services/execution/QaProfileService.d.ts.map +1 -1
- package/dist/services/execution/QaProfileService.js +354 -7
- package/dist/services/execution/QaTasksService.d.ts +59 -1
- package/dist/services/execution/QaTasksService.d.ts.map +1 -1
- package/dist/services/execution/QaTasksService.js +3347 -318
- package/dist/services/execution/QaTestCommandBuilder.d.ts +51 -0
- package/dist/services/execution/QaTestCommandBuilder.d.ts.map +1 -0
- package/dist/services/execution/QaTestCommandBuilder.js +495 -0
- package/dist/services/execution/TaskSelectionService.d.ts +4 -2
- package/dist/services/execution/TaskSelectionService.d.ts.map +1 -1
- package/dist/services/execution/TaskSelectionService.js +144 -28
- package/dist/services/execution/TaskStateService.d.ts +19 -6
- package/dist/services/execution/TaskStateService.d.ts.map +1 -1
- package/dist/services/execution/TaskStateService.js +128 -13
- package/dist/services/execution/WorkOnTasksService.d.ts +32 -1
- package/dist/services/execution/WorkOnTasksService.d.ts.map +1 -1
- package/dist/services/execution/WorkOnTasksService.js +4667 -722
- package/dist/services/jobs/JobInsightsService.d.ts +4 -0
- package/dist/services/jobs/JobInsightsService.d.ts.map +1 -1
- package/dist/services/jobs/JobInsightsService.js +51 -5
- package/dist/services/jobs/JobResumeService.d.ts.map +1 -1
- package/dist/services/jobs/JobResumeService.js +23 -10
- package/dist/services/jobs/JobService.d.ts +56 -4
- package/dist/services/jobs/JobService.d.ts.map +1 -1
- package/dist/services/jobs/JobService.js +232 -1
- package/dist/services/openapi/OpenApiService.d.ts +51 -0
- package/dist/services/openapi/OpenApiService.d.ts.map +1 -1
- package/dist/services/openapi/OpenApiService.js +953 -106
- package/dist/services/planning/CreateTasksService.d.ts +21 -0
- package/dist/services/planning/CreateTasksService.d.ts.map +1 -1
- package/dist/services/planning/CreateTasksService.js +569 -31
- package/dist/services/planning/RefineTasksService.d.ts +9 -0
- package/dist/services/planning/RefineTasksService.d.ts.map +1 -1
- package/dist/services/planning/RefineTasksService.js +409 -59
- package/dist/services/review/CodeReviewService.d.ts +18 -0
- package/dist/services/review/CodeReviewService.d.ts.map +1 -1
- package/dist/services/review/CodeReviewService.js +1309 -167
- package/dist/services/review/ReviewNormalizer.d.ts +9 -0
- package/dist/services/review/ReviewNormalizer.d.ts.map +1 -0
- package/dist/services/review/ReviewNormalizer.js +147 -0
- package/dist/services/shared/AuthErrors.d.ts +3 -0
- package/dist/services/shared/AuthErrors.d.ts.map +1 -0
- package/dist/services/shared/AuthErrors.js +17 -0
- package/dist/services/shared/DocdexGuidance.d.ts +7 -0
- package/dist/services/shared/DocdexGuidance.d.ts.map +1 -0
- package/dist/services/shared/DocdexGuidance.js +12 -0
- package/dist/services/shared/ProjectGuidance.d.ts +17 -0
- package/dist/services/shared/ProjectGuidance.d.ts.map +1 -0
- package/dist/services/shared/ProjectGuidance.js +78 -0
- package/dist/services/system/ToolDenylist.d.ts +13 -0
- package/dist/services/system/ToolDenylist.d.ts.map +1 -0
- package/dist/services/system/ToolDenylist.js +85 -0
- package/dist/services/tasks/TaskCommentFormatter.d.ts +20 -0
- package/dist/services/tasks/TaskCommentFormatter.d.ts.map +1 -0
- package/dist/services/tasks/TaskCommentFormatter.js +54 -0
- package/dist/services/telemetry/TelemetryService.d.ts.map +1 -1
- package/dist/services/telemetry/TelemetryService.js +39 -7
- package/dist/workspace/WorkspaceManager.d.ts +26 -0
- package/dist/workspace/WorkspaceManager.d.ts.map +1 -1
- package/dist/workspace/WorkspaceManager.js +206 -32
- package/package.json +6 -5
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import { buildDocInventory } from "../DocInventory.js";
|
|
4
|
+
const MARKDOWN_EXTENSIONS = new Set([".md", ".markdown"]);
|
|
5
|
+
const YAML_EXTENSIONS = new Set([".yaml", ".yml", ".json"]);
|
|
6
|
+
const inferFormat = (filePath) => {
|
|
7
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
8
|
+
if (MARKDOWN_EXTENSIONS.has(ext))
|
|
9
|
+
return "markdown";
|
|
10
|
+
if (YAML_EXTENSIONS.has(ext))
|
|
11
|
+
return "yaml";
|
|
12
|
+
return "text";
|
|
13
|
+
};
|
|
14
|
+
const normalizeHeading = (value) => {
|
|
15
|
+
return value
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[`*_]/g, "")
|
|
18
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
19
|
+
.replace(/\s+/g, " ")
|
|
20
|
+
.trim();
|
|
21
|
+
};
|
|
22
|
+
const parseHeadingLine = (line) => {
|
|
23
|
+
const match = line.match(/^\s{0,3}(#{1,6})\s+(.+?)\s*$/);
|
|
24
|
+
if (!match)
|
|
25
|
+
return undefined;
|
|
26
|
+
const level = match[1].length;
|
|
27
|
+
const text = match[2].replace(/\s+#*\s*$/, "").trim();
|
|
28
|
+
return { level, text };
|
|
29
|
+
};
|
|
30
|
+
const splitLines = (content) => {
|
|
31
|
+
const hadTrailingNewline = content.endsWith("\n");
|
|
32
|
+
const lines = content.split(/\r?\n/);
|
|
33
|
+
if (hadTrailingNewline && lines.length && lines[lines.length - 1] === "") {
|
|
34
|
+
lines.pop();
|
|
35
|
+
}
|
|
36
|
+
return { lines, hadTrailingNewline };
|
|
37
|
+
};
|
|
38
|
+
const joinLines = (lines, hadTrailingNewline) => {
|
|
39
|
+
const joined = lines.join("\n");
|
|
40
|
+
if (hadTrailingNewline)
|
|
41
|
+
return `${joined}\n`;
|
|
42
|
+
return joined;
|
|
43
|
+
};
|
|
44
|
+
const trimEmptyLines = (lines) => {
|
|
45
|
+
let start = 0;
|
|
46
|
+
let end = lines.length;
|
|
47
|
+
while (start < end && lines[start]?.trim() === "")
|
|
48
|
+
start += 1;
|
|
49
|
+
while (end > start && lines[end - 1]?.trim() === "")
|
|
50
|
+
end -= 1;
|
|
51
|
+
return lines.slice(start, end);
|
|
52
|
+
};
|
|
53
|
+
const normalizeLineRange = (lineStart, lineEnd, totalLines) => {
|
|
54
|
+
if (!Number.isFinite(lineStart) || !Number.isFinite(lineEnd))
|
|
55
|
+
return undefined;
|
|
56
|
+
if (lineStart < 1 || lineEnd < 1)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (lineStart > lineEnd)
|
|
59
|
+
return undefined;
|
|
60
|
+
if (totalLines === 0)
|
|
61
|
+
return undefined;
|
|
62
|
+
const start = Math.max(1, Math.min(totalLines, Math.floor(lineStart)));
|
|
63
|
+
const end = Math.max(1, Math.min(totalLines, Math.floor(lineEnd)));
|
|
64
|
+
if (start > end)
|
|
65
|
+
return undefined;
|
|
66
|
+
return { start, end };
|
|
67
|
+
};
|
|
68
|
+
const removeRange = (lines, startIdx, endIdx) => {
|
|
69
|
+
const next = lines.slice(0, startIdx).concat(lines.slice(endIdx + 1));
|
|
70
|
+
while (startIdx > 0 && startIdx < next.length && next[startIdx] === "" && next[startIdx - 1] === "") {
|
|
71
|
+
next.splice(startIdx, 1);
|
|
72
|
+
}
|
|
73
|
+
if (next[0] === "")
|
|
74
|
+
next.shift();
|
|
75
|
+
return next;
|
|
76
|
+
};
|
|
77
|
+
const findHeadingIndex = (lines, heading) => {
|
|
78
|
+
const target = normalizeHeading(heading);
|
|
79
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
80
|
+
const parsed = parseHeadingLine(lines[i] ?? "");
|
|
81
|
+
if (!parsed)
|
|
82
|
+
continue;
|
|
83
|
+
if (normalizeHeading(parsed.text) === target) {
|
|
84
|
+
return { index: i, level: parsed.level };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
};
|
|
89
|
+
const findSectionRange = (lines, headingIndex, headingLevel) => {
|
|
90
|
+
let end = lines.length;
|
|
91
|
+
for (let i = headingIndex + 1; i < lines.length; i += 1) {
|
|
92
|
+
const parsed = parseHeadingLine(lines[i] ?? "");
|
|
93
|
+
if (!parsed)
|
|
94
|
+
continue;
|
|
95
|
+
if (parsed.level <= headingLevel) {
|
|
96
|
+
end = i;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { start: headingIndex, end };
|
|
101
|
+
};
|
|
102
|
+
const withSectionSpacing = (lines, insertAt, sectionLines) => {
|
|
103
|
+
const needsLeadingBlank = insertAt > 0 && (lines[insertAt - 1]?.trim() ?? "") !== "";
|
|
104
|
+
const needsTrailingBlank = insertAt < lines.length && (lines[insertAt]?.trim() ?? "") !== "";
|
|
105
|
+
const padded = sectionLines.slice();
|
|
106
|
+
if (needsLeadingBlank)
|
|
107
|
+
padded.unshift("");
|
|
108
|
+
if (needsTrailingBlank)
|
|
109
|
+
padded.push("");
|
|
110
|
+
return padded;
|
|
111
|
+
};
|
|
112
|
+
const buildSectionLines = (heading, content, level) => {
|
|
113
|
+
const headingLevel = Math.min(6, Math.max(1, level ?? 2));
|
|
114
|
+
const headingLine = `${"#".repeat(headingLevel)} ${heading.trim()}`;
|
|
115
|
+
const bodyLines = trimEmptyLines(content.split(/\r?\n/));
|
|
116
|
+
const lines = [headingLine];
|
|
117
|
+
lines.push("");
|
|
118
|
+
if (bodyLines.length > 0) {
|
|
119
|
+
lines.push(...bodyLines);
|
|
120
|
+
}
|
|
121
|
+
return lines;
|
|
122
|
+
};
|
|
123
|
+
const resolvePreferred = (artifacts) => ({
|
|
124
|
+
pdrPath: artifacts.pdr?.path,
|
|
125
|
+
sdsPath: artifacts.sds?.path,
|
|
126
|
+
});
|
|
127
|
+
const applyReplaceSection = (lines, op, format, filePath) => {
|
|
128
|
+
if (op.location.path && path.resolve(op.location.path) !== path.resolve(filePath)) {
|
|
129
|
+
return { lines, step: { operation: op, applied: false, reason: "location path mismatch" } };
|
|
130
|
+
}
|
|
131
|
+
if (op.location.kind === "heading") {
|
|
132
|
+
if (format !== "markdown") {
|
|
133
|
+
return { lines, step: { operation: op, applied: false, reason: "heading replace requires markdown" } };
|
|
134
|
+
}
|
|
135
|
+
const headingMatch = findHeadingIndex(lines, op.location.heading);
|
|
136
|
+
if (!headingMatch) {
|
|
137
|
+
const appended = lines.slice();
|
|
138
|
+
const sectionLines = buildSectionLines(op.location.heading, op.content, op.headingLevel);
|
|
139
|
+
appended.push(...withSectionSpacing(appended, appended.length, sectionLines));
|
|
140
|
+
return {
|
|
141
|
+
lines: appended,
|
|
142
|
+
step: {
|
|
143
|
+
operation: op,
|
|
144
|
+
applied: true,
|
|
145
|
+
reason: "heading not found; appended section",
|
|
146
|
+
range: { lineStart: Math.max(1, appended.length - sectionLines.length + 1), lineEnd: appended.length },
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const { start, end } = findSectionRange(lines, headingMatch.index, headingMatch.level);
|
|
151
|
+
const replacement = buildSectionLines(lines[headingMatch.index] ?? op.location.heading, op.content, headingMatch.level)
|
|
152
|
+
.map((line, idx) => (idx === 0 ? lines[headingMatch.index] ?? line : line));
|
|
153
|
+
const nextLines = lines.slice(0, start).concat(replacement, lines.slice(end));
|
|
154
|
+
return {
|
|
155
|
+
lines: nextLines,
|
|
156
|
+
step: {
|
|
157
|
+
operation: op,
|
|
158
|
+
applied: true,
|
|
159
|
+
range: { lineStart: start + 1, lineEnd: start + replacement.length },
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const range = normalizeLineRange(op.location.lineStart, op.location.lineEnd, lines.length);
|
|
164
|
+
if (!range) {
|
|
165
|
+
return { lines, step: { operation: op, applied: false, reason: "invalid line range" } };
|
|
166
|
+
}
|
|
167
|
+
const bodyLines = trimEmptyLines(op.content.split(/\r?\n/));
|
|
168
|
+
const replacement = bodyLines.length > 0 ? bodyLines : [""];
|
|
169
|
+
const nextLines = lines
|
|
170
|
+
.slice(0, range.start - 1)
|
|
171
|
+
.concat(replacement, lines.slice(range.end));
|
|
172
|
+
return {
|
|
173
|
+
lines: nextLines,
|
|
174
|
+
step: {
|
|
175
|
+
operation: op,
|
|
176
|
+
applied: true,
|
|
177
|
+
range: { lineStart: range.start, lineEnd: range.start + replacement.length - 1 },
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
const applyInsertSection = (lines, op, format, filePath) => {
|
|
182
|
+
if (format !== "markdown") {
|
|
183
|
+
return { lines, step: { operation: op, applied: false, reason: "insert requires markdown" } };
|
|
184
|
+
}
|
|
185
|
+
const sectionLines = buildSectionLines(op.heading, op.content, op.headingLevel);
|
|
186
|
+
const position = op.position ?? "append";
|
|
187
|
+
let insertAt = lines.length;
|
|
188
|
+
if (position !== "append" && op.location) {
|
|
189
|
+
if (op.location.path && path.resolve(op.location.path) !== path.resolve(filePath)) {
|
|
190
|
+
return { lines, step: { operation: op, applied: false, reason: "location path mismatch" } };
|
|
191
|
+
}
|
|
192
|
+
if (op.location.kind === "heading") {
|
|
193
|
+
const headingMatch = findHeadingIndex(lines, op.location.heading);
|
|
194
|
+
if (headingMatch) {
|
|
195
|
+
if (position === "before") {
|
|
196
|
+
insertAt = headingMatch.index;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const range = findSectionRange(lines, headingMatch.index, headingMatch.level);
|
|
200
|
+
insertAt = range.end;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const range = normalizeLineRange(op.location.lineStart, op.location.lineEnd, lines.length);
|
|
206
|
+
if (range) {
|
|
207
|
+
insertAt = position === "before" ? range.start - 1 : range.end;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const padded = withSectionSpacing(lines, insertAt, sectionLines);
|
|
212
|
+
const nextLines = lines.slice(0, insertAt).concat(padded, lines.slice(insertAt));
|
|
213
|
+
return {
|
|
214
|
+
lines: nextLines,
|
|
215
|
+
step: {
|
|
216
|
+
operation: op,
|
|
217
|
+
applied: true,
|
|
218
|
+
range: { lineStart: insertAt + 1, lineEnd: insertAt + padded.length },
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
const applyRemoveBlock = (lines, op, format, filePath) => {
|
|
223
|
+
if (op.location.path && path.resolve(op.location.path) !== path.resolve(filePath)) {
|
|
224
|
+
return { lines, step: { operation: op, applied: false, reason: "location path mismatch" } };
|
|
225
|
+
}
|
|
226
|
+
if (op.location.kind === "heading") {
|
|
227
|
+
if (format !== "markdown") {
|
|
228
|
+
return { lines, step: { operation: op, applied: false, reason: "heading remove requires markdown" } };
|
|
229
|
+
}
|
|
230
|
+
const headingMatch = findHeadingIndex(lines, op.location.heading);
|
|
231
|
+
if (!headingMatch) {
|
|
232
|
+
return { lines, step: { operation: op, applied: false, reason: "heading not found" } };
|
|
233
|
+
}
|
|
234
|
+
const range = findSectionRange(lines, headingMatch.index, headingMatch.level);
|
|
235
|
+
const nextLines = removeRange(lines, range.start, range.end - 1);
|
|
236
|
+
return {
|
|
237
|
+
lines: nextLines,
|
|
238
|
+
step: {
|
|
239
|
+
operation: op,
|
|
240
|
+
applied: true,
|
|
241
|
+
range: { lineStart: range.start + 1, lineEnd: range.end },
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const range = normalizeLineRange(op.location.lineStart, op.location.lineEnd, lines.length);
|
|
246
|
+
if (!range) {
|
|
247
|
+
return { lines, step: { operation: op, applied: false, reason: "invalid line range" } };
|
|
248
|
+
}
|
|
249
|
+
const nextLines = removeRange(lines, range.start - 1, range.end - 1);
|
|
250
|
+
return {
|
|
251
|
+
lines: nextLines,
|
|
252
|
+
step: {
|
|
253
|
+
operation: op,
|
|
254
|
+
applied: true,
|
|
255
|
+
range: { lineStart: range.start, lineEnd: range.end },
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
export class DocPatchEngine {
|
|
260
|
+
async apply(input) {
|
|
261
|
+
const { runContext, patches, dryRun } = input;
|
|
262
|
+
const results = [];
|
|
263
|
+
const warnings = [];
|
|
264
|
+
let changedAny = false;
|
|
265
|
+
for (const patch of patches) {
|
|
266
|
+
const format = patch.format ?? inferFormat(patch.path);
|
|
267
|
+
let content = "";
|
|
268
|
+
let steps = [];
|
|
269
|
+
let hadTrailingNewline = false;
|
|
270
|
+
let lines = [];
|
|
271
|
+
try {
|
|
272
|
+
content = await fs.readFile(patch.path, "utf8");
|
|
273
|
+
const split = splitLines(content);
|
|
274
|
+
lines = split.lines;
|
|
275
|
+
hadTrailingNewline = split.hadTrailingNewline;
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
const message = `Doc patch skipped for ${patch.path}: ${error.message ?? String(error)}`;
|
|
279
|
+
warnings.push(message);
|
|
280
|
+
steps = patch.operations.map((operation) => ({
|
|
281
|
+
operation,
|
|
282
|
+
applied: false,
|
|
283
|
+
reason: "failed to read file",
|
|
284
|
+
}));
|
|
285
|
+
results.push({ path: patch.path, format, changed: false, steps });
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
for (const operation of patch.operations) {
|
|
289
|
+
let outcome;
|
|
290
|
+
if (operation.type === "replace_section") {
|
|
291
|
+
outcome = applyReplaceSection(lines, operation, format, patch.path);
|
|
292
|
+
}
|
|
293
|
+
else if (operation.type === "insert_section") {
|
|
294
|
+
outcome = applyInsertSection(lines, operation, format, patch.path);
|
|
295
|
+
}
|
|
296
|
+
else if (operation.type === "remove_block") {
|
|
297
|
+
outcome = applyRemoveBlock(lines, operation, format, patch.path);
|
|
298
|
+
}
|
|
299
|
+
if (!outcome)
|
|
300
|
+
continue;
|
|
301
|
+
steps.push(outcome.step);
|
|
302
|
+
if (outcome.step.applied) {
|
|
303
|
+
lines = outcome.lines;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const nextContent = joinLines(lines, hadTrailingNewline);
|
|
307
|
+
const changed = nextContent !== content;
|
|
308
|
+
changedAny = changedAny || changed;
|
|
309
|
+
if (!dryRun && changed) {
|
|
310
|
+
await fs.writeFile(patch.path, nextContent, "utf8");
|
|
311
|
+
}
|
|
312
|
+
results.push({ path: patch.path, format, changed, steps });
|
|
313
|
+
}
|
|
314
|
+
if (!dryRun && changedAny) {
|
|
315
|
+
try {
|
|
316
|
+
const updatedArtifacts = await buildDocInventory({
|
|
317
|
+
workspace: runContext.workspace,
|
|
318
|
+
preferred: resolvePreferred(runContext.artifacts),
|
|
319
|
+
});
|
|
320
|
+
runContext.artifacts = updatedArtifacts;
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
warnings.push(`Doc inventory refresh failed: ${error.message ?? String(error)}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (warnings.length > 0) {
|
|
327
|
+
runContext.warnings.push(...warnings);
|
|
328
|
+
}
|
|
329
|
+
return { results, updatedArtifacts: runContext.artifacts, warnings };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface GlossaryEntry {
|
|
2
|
+
key: string;
|
|
3
|
+
term: string;
|
|
4
|
+
description: string;
|
|
5
|
+
aliases?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface GlossaryData {
|
|
8
|
+
version: 1;
|
|
9
|
+
entries: GlossaryEntry[];
|
|
10
|
+
canonicalPhrases: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export declare const loadGlossary: (overridePath?: string) => GlossaryData;
|
|
13
|
+
export declare const getGlossaryEntry: (key: string, glossary?: GlossaryData) => GlossaryEntry | undefined;
|
|
14
|
+
export declare const formatGlossaryForPrompt: (glossary?: GlossaryData) => string;
|
|
15
|
+
export declare const GLOSSARY_PROMPT_SNIPPET: string;
|
|
16
|
+
//# sourceMappingURL=Glossary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Glossary.d.ts","sourceRoot":"","sources":["../../../../src/services/docs/review/Glossary.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAkBD,eAAO,MAAM,YAAY,GAAI,eAAe,MAAM,KAAG,YAYpD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,KAAK,MAAM,EAAE,WAAW,YAAY,KAAG,aAAa,GAAG,SAGvF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,WAAW,YAAY,KAAG,MASjE,CAAC;AAEF,eAAO,MAAM,uBAAuB,QAA4B,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import glossaryFallback from "./glossary.json" with { type: "json" };
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const DEFAULT_GLOSSARY_PATH = fileURLToPath(new URL("./glossary.json", import.meta.url));
|
|
5
|
+
let cachedGlossary = null;
|
|
6
|
+
let cachedPath = null;
|
|
7
|
+
const normalizeGlossary = (data) => {
|
|
8
|
+
const version = data?.version === 1 ? 1 : 1;
|
|
9
|
+
const entries = Array.isArray(data?.entries)
|
|
10
|
+
? data.entries.filter((entry) => entry && typeof entry.term === "string")
|
|
11
|
+
: [];
|
|
12
|
+
const canonicalPhrases = data?.canonicalPhrases && typeof data.canonicalPhrases === "object"
|
|
13
|
+
? data.canonicalPhrases
|
|
14
|
+
: {};
|
|
15
|
+
return { version, entries, canonicalPhrases };
|
|
16
|
+
};
|
|
17
|
+
export const loadGlossary = (overridePath) => {
|
|
18
|
+
const glossaryPath = overridePath || process.env.MCODA_GLOSSARY_PATH || DEFAULT_GLOSSARY_PATH;
|
|
19
|
+
if (cachedGlossary && cachedPath === glossaryPath)
|
|
20
|
+
return cachedGlossary;
|
|
21
|
+
try {
|
|
22
|
+
const raw = fs.readFileSync(glossaryPath, "utf8");
|
|
23
|
+
cachedGlossary = normalizeGlossary(JSON.parse(raw));
|
|
24
|
+
cachedPath = glossaryPath;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
cachedGlossary = normalizeGlossary(glossaryFallback);
|
|
28
|
+
cachedPath = DEFAULT_GLOSSARY_PATH;
|
|
29
|
+
}
|
|
30
|
+
return cachedGlossary;
|
|
31
|
+
};
|
|
32
|
+
export const getGlossaryEntry = (key, glossary) => {
|
|
33
|
+
const resolved = glossary ?? loadGlossary();
|
|
34
|
+
return resolved.entries.find((entry) => entry.key === key);
|
|
35
|
+
};
|
|
36
|
+
export const formatGlossaryForPrompt = (glossary) => {
|
|
37
|
+
const resolved = glossary ?? loadGlossary();
|
|
38
|
+
if (!resolved.entries.length)
|
|
39
|
+
return "Glossary: (no entries loaded).";
|
|
40
|
+
const lines = ["Glossary (canonical terminology):"];
|
|
41
|
+
for (const entry of resolved.entries) {
|
|
42
|
+
const alias = entry.aliases && entry.aliases.length > 0 ? ` Aliases: ${entry.aliases.join(", ")}.` : "";
|
|
43
|
+
lines.push(`- ${entry.term}: ${entry.description}.${alias}`.trim());
|
|
44
|
+
}
|
|
45
|
+
return lines.join("\n");
|
|
46
|
+
};
|
|
47
|
+
export const GLOSSARY_PROMPT_SNIPPET = formatGlossaryForPrompt();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReviewReportRenderer.d.ts","sourceRoot":"","sources":["../../../../src/services/docs/review/ReviewReportRenderer.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AA+BnF,eAAO,MAAM,kBAAkB,GAAI,OAAO,YAAY,KAAG,MA+GxD,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { normalizeReviewReport } from "./ReviewReportSchema.js";
|
|
2
|
+
import { sortIssues } from "./ReviewTypes.js";
|
|
3
|
+
const ARTIFACT_ORDER = ["pdr", "sds", "openapi", "sql", "deployment"];
|
|
4
|
+
const statusLabel = (status) => status.toUpperCase();
|
|
5
|
+
const formatLocation = (issue) => {
|
|
6
|
+
const location = issue.location;
|
|
7
|
+
if (location.kind === "heading") {
|
|
8
|
+
return location.heading ? `Heading: ${location.heading}` : "Heading: (unspecified)";
|
|
9
|
+
}
|
|
10
|
+
return `Lines ${location.lineStart}-${location.lineEnd}`;
|
|
11
|
+
};
|
|
12
|
+
const formatIssueLine = (issue) => {
|
|
13
|
+
const location = formatLocation(issue);
|
|
14
|
+
return `- (${issue.severity}) [${issue.gateId}] ${issue.message} (${location}) -> ${issue.remediation}`;
|
|
15
|
+
};
|
|
16
|
+
const sortArtifacts = (artifacts) => {
|
|
17
|
+
return artifacts.slice().sort((a, b) => {
|
|
18
|
+
const ai = ARTIFACT_ORDER.indexOf(a);
|
|
19
|
+
const bi = ARTIFACT_ORDER.indexOf(b);
|
|
20
|
+
if (ai === -1 && bi === -1)
|
|
21
|
+
return a.localeCompare(b);
|
|
22
|
+
if (ai === -1)
|
|
23
|
+
return 1;
|
|
24
|
+
if (bi === -1)
|
|
25
|
+
return -1;
|
|
26
|
+
return ai - bi;
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
export const renderReviewReport = (input) => {
|
|
30
|
+
const report = normalizeReviewReport(input);
|
|
31
|
+
const lines = [];
|
|
32
|
+
lines.push("# Docgen Review Report");
|
|
33
|
+
lines.push("");
|
|
34
|
+
lines.push(`- Generated: ${report.generatedAt}`);
|
|
35
|
+
lines.push(`- Iteration: ${report.iteration.current}/${report.iteration.max} (${report.iteration.status})`);
|
|
36
|
+
lines.push(`- Status: ${statusLabel(report.status)}`);
|
|
37
|
+
lines.push(`- Total issues: ${report.summary.issueCount}`);
|
|
38
|
+
lines.push("");
|
|
39
|
+
lines.push("## Summary");
|
|
40
|
+
lines.push(`- Status: ${statusLabel(report.summary.status)}`);
|
|
41
|
+
lines.push(`- Issues: ${report.summary.issueCount} (blocker: ${report.summary.severityCounts.blocker}, high: ${report.summary.severityCounts.high}, medium: ${report.summary.severityCounts.medium}, low: ${report.summary.severityCounts.low}, info: ${report.summary.severityCounts.info})`);
|
|
42
|
+
lines.push(`- Gates: fail: ${report.summary.gateCounts.fail}, warn: ${report.summary.gateCounts.warn}, pass: ${report.summary.gateCounts.pass}, skipped: ${report.summary.gateCounts.skipped}`);
|
|
43
|
+
lines.push("");
|
|
44
|
+
if (report.metadata?.iterationReports?.length) {
|
|
45
|
+
lines.push("## Prior Iterations");
|
|
46
|
+
for (const entry of report.metadata.iterationReports) {
|
|
47
|
+
lines.push(`- ${entry}`);
|
|
48
|
+
}
|
|
49
|
+
lines.push("");
|
|
50
|
+
}
|
|
51
|
+
lines.push("## Gate Summary");
|
|
52
|
+
if (report.gateResults.length === 0) {
|
|
53
|
+
lines.push("- None");
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const sortedGates = report.gateResults.slice().sort((a, b) => a.gateId.localeCompare(b.gateId));
|
|
57
|
+
for (const gate of sortedGates) {
|
|
58
|
+
lines.push(`- ${statusLabel(gate.status)} ${gate.gateId} (${gate.issues.length} issues)`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
lines.push("");
|
|
62
|
+
lines.push("## Issues by Artifact");
|
|
63
|
+
if (report.issues.length === 0) {
|
|
64
|
+
lines.push("- None");
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const grouped = new Map();
|
|
68
|
+
for (const issue of sortIssues(report.issues)) {
|
|
69
|
+
const current = grouped.get(issue.artifact) ?? [];
|
|
70
|
+
current.push(issue);
|
|
71
|
+
grouped.set(issue.artifact, current);
|
|
72
|
+
}
|
|
73
|
+
const artifacts = sortArtifacts(Array.from(grouped.keys()));
|
|
74
|
+
for (const artifact of artifacts) {
|
|
75
|
+
lines.push(`### ${artifact}`);
|
|
76
|
+
for (const issue of grouped.get(artifact) ?? []) {
|
|
77
|
+
lines.push(formatIssueLine(issue));
|
|
78
|
+
}
|
|
79
|
+
lines.push("");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
lines.push("## Fixes Applied");
|
|
83
|
+
if (report.fixesApplied.length === 0) {
|
|
84
|
+
lines.push("- None");
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const fixes = report.fixesApplied.slice().sort((a, b) => a.issueId.localeCompare(b.issueId));
|
|
88
|
+
for (const fix of fixes) {
|
|
89
|
+
lines.push(`- ${fix.issueId}: ${fix.summary}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
lines.push("");
|
|
93
|
+
lines.push("## Remaining Open Items");
|
|
94
|
+
if (report.remainingOpenItems.length === 0) {
|
|
95
|
+
lines.push("- None");
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
for (const issue of sortIssues(report.remainingOpenItems)) {
|
|
99
|
+
lines.push(formatIssueLine(issue));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
lines.push("");
|
|
103
|
+
lines.push("## Decisions");
|
|
104
|
+
if (report.decisions.length === 0) {
|
|
105
|
+
lines.push("- None");
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const decisions = report.decisions.slice().sort((a, b) => a.decidedAt.localeCompare(b.decidedAt));
|
|
109
|
+
for (const decision of decisions) {
|
|
110
|
+
const related = decision.relatedIssueIds?.length
|
|
111
|
+
? ` (issues: ${decision.relatedIssueIds.join(", ")})`
|
|
112
|
+
: "";
|
|
113
|
+
lines.push(`- ${decision.summary}: ${decision.rationale}${related}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
lines.push("");
|
|
117
|
+
lines.push("## Cross-Document Deltas");
|
|
118
|
+
if (report.deltas.length === 0) {
|
|
119
|
+
lines.push("- None");
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
const deltas = report.deltas.slice().sort((a, b) => {
|
|
123
|
+
const artifactDiff = sortArtifacts([a.artifact, b.artifact])[0] === a.artifact ? -1 : 1;
|
|
124
|
+
if (a.artifact === b.artifact)
|
|
125
|
+
return a.path.localeCompare(b.path);
|
|
126
|
+
return artifactDiff;
|
|
127
|
+
});
|
|
128
|
+
for (const delta of deltas) {
|
|
129
|
+
lines.push(`- ${delta.artifact}: ${delta.path} -> ${delta.summary}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ReviewDecision, type ReviewFix, type ReviewGateResult, type ReviewIssue, type ReviewOutcomeStatus, type ReviewSummary } from "./ReviewTypes.js";
|
|
2
|
+
import { DocArtifactKind } from "../DocgenRunContext.js";
|
|
3
|
+
export interface ReviewReportIteration {
|
|
4
|
+
current: number;
|
|
5
|
+
max: number;
|
|
6
|
+
status: "in_progress" | "completed" | "max_iterations";
|
|
7
|
+
}
|
|
8
|
+
export interface ReviewReportDelta {
|
|
9
|
+
artifact: DocArtifactKind;
|
|
10
|
+
path: string;
|
|
11
|
+
summary: string;
|
|
12
|
+
beforeChecksum?: string;
|
|
13
|
+
afterChecksum?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ReviewReportV1 {
|
|
16
|
+
version: 1;
|
|
17
|
+
generatedAt: string;
|
|
18
|
+
iteration: ReviewReportIteration;
|
|
19
|
+
status: ReviewOutcomeStatus;
|
|
20
|
+
summary: ReviewSummary;
|
|
21
|
+
gateResults: ReviewGateResult[];
|
|
22
|
+
issues: ReviewIssue[];
|
|
23
|
+
remainingOpenItems: ReviewIssue[];
|
|
24
|
+
fixesApplied: ReviewFix[];
|
|
25
|
+
decisions: ReviewDecision[];
|
|
26
|
+
deltas: ReviewReportDelta[];
|
|
27
|
+
metadata?: {
|
|
28
|
+
commandName?: string;
|
|
29
|
+
commandRunId?: string;
|
|
30
|
+
jobId?: string;
|
|
31
|
+
projectKey?: string;
|
|
32
|
+
iterationReports?: string[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export type ReviewReport = ReviewReportV1;
|
|
36
|
+
export declare const normalizeReviewReport: (report: ReviewReport) => ReviewReportV1;
|
|
37
|
+
export declare const validateReviewReport: (report: ReviewReport) => ReviewReportV1;
|
|
38
|
+
export declare const serializeReviewReport: (report: ReviewReport) => string;
|
|
39
|
+
//# sourceMappingURL=ReviewReportSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReviewReportSchema.d.ts","sourceRoot":"","sources":["../../../../src/services/docs/review/ReviewReportSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,GAAG,WAAW,GAAG,gBAAgB,CAAC;CACxD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,qBAAqB,CAAC;IACjC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,kBAAkB,EAAE,WAAW,EAAE,CAAC;IAClC,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE;QACT,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC7B,CAAC;CACH;AAED,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC;AAoB1C,eAAO,MAAM,qBAAqB,GAAI,QAAQ,YAAY,KAAG,cAa5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,YAAY,KAAG,cAY3D,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,QAAQ,YAAY,KAAG,MAG5D,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { summarizeGateResults, } from "./ReviewTypes.js";
|
|
2
|
+
const ensureString = (value, label) => {
|
|
3
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
4
|
+
throw new Error(`Review report validation failed: ${label} must be a non-empty string.`);
|
|
5
|
+
}
|
|
6
|
+
};
|
|
7
|
+
const ensureNumber = (value, label) => {
|
|
8
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
9
|
+
throw new Error(`Review report validation failed: ${label} must be a number.`);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const ensureArray = (value, label) => {
|
|
13
|
+
if (!Array.isArray(value)) {
|
|
14
|
+
throw new Error(`Review report validation failed: ${label} must be an array.`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export const normalizeReviewReport = (report) => {
|
|
18
|
+
if (report.version !== 1) {
|
|
19
|
+
throw new Error(`Unsupported review report version: ${report.version}`);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
...report,
|
|
23
|
+
issues: report.issues ?? [],
|
|
24
|
+
remainingOpenItems: report.remainingOpenItems ?? [],
|
|
25
|
+
fixesApplied: report.fixesApplied ?? [],
|
|
26
|
+
decisions: report.decisions ?? [],
|
|
27
|
+
deltas: report.deltas ?? [],
|
|
28
|
+
summary: report.summary ?? summarizeGateResults(report.gateResults ?? []),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export const validateReviewReport = (report) => {
|
|
32
|
+
const normalized = normalizeReviewReport(report);
|
|
33
|
+
ensureString(normalized.generatedAt, "generatedAt");
|
|
34
|
+
ensureNumber(normalized.iteration?.current, "iteration.current");
|
|
35
|
+
ensureNumber(normalized.iteration?.max, "iteration.max");
|
|
36
|
+
ensureArray(normalized.gateResults, "gateResults");
|
|
37
|
+
ensureArray(normalized.issues, "issues");
|
|
38
|
+
ensureArray(normalized.remainingOpenItems, "remainingOpenItems");
|
|
39
|
+
ensureArray(normalized.fixesApplied, "fixesApplied");
|
|
40
|
+
ensureArray(normalized.decisions, "decisions");
|
|
41
|
+
ensureArray(normalized.deltas, "deltas");
|
|
42
|
+
return normalized;
|
|
43
|
+
};
|
|
44
|
+
export const serializeReviewReport = (report) => {
|
|
45
|
+
const validated = validateReviewReport(report);
|
|
46
|
+
return JSON.stringify(validated, null, 2);
|
|
47
|
+
};
|