agentweaver 0.1.5 → 0.1.6
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/Dockerfile.codex +55 -0
- package/README.md +10 -6
- package/dist/artifacts.js +9 -0
- package/dist/executors/configs/fetch-gitlab-review-config.js +3 -0
- package/dist/executors/fetch-gitlab-review-executor.js +25 -0
- package/dist/gitlab.js +153 -0
- package/dist/index.js +41 -91
- package/dist/pipeline/flow-specs/auto.json +2 -2
- package/dist/pipeline/flow-specs/gitlab-review.json +347 -0
- package/dist/pipeline/flow-specs/implement.json +0 -9
- package/dist/pipeline/flow-specs/review-fix.json +2 -11
- package/dist/pipeline/flow-specs/run-linter-loop.json +17 -11
- package/dist/pipeline/flow-specs/run-tests-loop.json +17 -11
- package/dist/pipeline/node-registry.js +20 -1
- package/dist/pipeline/nodes/fetch-gitlab-review-node.js +34 -0
- package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +105 -0
- package/dist/pipeline/nodes/local-script-check-node.js +81 -0
- package/dist/pipeline/nodes/review-findings-form-node.js +14 -14
- package/dist/pipeline/prompt-registry.js +1 -3
- package/dist/pipeline/registry.js +2 -0
- package/dist/pipeline/value-resolver.js +7 -1
- package/dist/prompts.js +0 -2
- package/dist/structured-artifacts.js +33 -0
- package/docker-compose.yml +384 -0
- package/package.json +7 -3
- package/run_linter.sh +89 -0
- package/run_tests.sh +113 -0
- package/verify_build.sh +104 -0
- package/dist/executors/claude-summary-executor.js +0 -31
- package/dist/executors/configs/claude-summary-config.js +0 -8
- package/dist/pipeline/flow-runner.js +0 -13
- package/dist/pipeline/flow-specs/test-fix.json +0 -24
- package/dist/pipeline/flow-specs/test-linter-fix.json +0 -24
- package/dist/pipeline/flow-specs/test.json +0 -19
- package/dist/pipeline/flow-types.js +0 -1
- package/dist/pipeline/flows/implement-flow.js +0 -47
- package/dist/pipeline/flows/plan-flow.js +0 -42
- package/dist/pipeline/flows/review-fix-flow.js +0 -62
- package/dist/pipeline/flows/review-flow.js +0 -124
- package/dist/pipeline/flows/test-fix-flow.js +0 -12
- package/dist/pipeline/flows/test-flow.js +0 -32
- package/dist/pipeline/nodes/claude-summary-node.js +0 -38
- package/dist/pipeline/nodes/implement-codex-node.js +0 -16
- package/dist/pipeline/nodes/task-summary-node.js +0 -42
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { TaskRunnerError } from "../../errors.js";
|
|
4
|
+
function commentLine(comment) {
|
|
5
|
+
if (typeof comment.new_line === "number") {
|
|
6
|
+
return comment.new_line;
|
|
7
|
+
}
|
|
8
|
+
if (typeof comment.old_line === "number") {
|
|
9
|
+
return comment.old_line;
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
function commentLocation(comment) {
|
|
14
|
+
const filePath = typeof comment.file_path === "string" ? comment.file_path.trim() : "";
|
|
15
|
+
const line = commentLine(comment);
|
|
16
|
+
if (!filePath) {
|
|
17
|
+
return "general";
|
|
18
|
+
}
|
|
19
|
+
return line === null ? filePath : `${filePath}:${line}`;
|
|
20
|
+
}
|
|
21
|
+
function toReviewFinding(comment, index) {
|
|
22
|
+
const body = typeof comment.body === "string" ? comment.body.trim() : "";
|
|
23
|
+
if (!body) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const author = typeof comment.author === "string" ? comment.author.trim() : "unknown";
|
|
27
|
+
const location = commentLocation(comment);
|
|
28
|
+
const preview = body.replace(/\s+/g, " ");
|
|
29
|
+
const titlePreview = preview.length > 72 ? `${preview.slice(0, 69)}...` : preview;
|
|
30
|
+
const discussionId = typeof comment.discussion_id === "string" ? comment.discussion_id.trim() : "";
|
|
31
|
+
const createdAt = typeof comment.created_at === "string" ? comment.created_at.trim() : "";
|
|
32
|
+
return {
|
|
33
|
+
severity: "medium",
|
|
34
|
+
title: `GitLab comment ${index + 1} | ${location} | ${author} | ${titlePreview}`,
|
|
35
|
+
description: [
|
|
36
|
+
`Location: ${location}`,
|
|
37
|
+
`Author: ${author}`,
|
|
38
|
+
discussionId ? `Discussion: ${discussionId}` : "",
|
|
39
|
+
createdAt ? `Created at: ${createdAt}` : "",
|
|
40
|
+
"",
|
|
41
|
+
body,
|
|
42
|
+
]
|
|
43
|
+
.filter((line) => line.length > 0)
|
|
44
|
+
.join("\n"),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function renderReviewMarkdown(artifact) {
|
|
48
|
+
const lines = [
|
|
49
|
+
"# Review",
|
|
50
|
+
"",
|
|
51
|
+
artifact.summary,
|
|
52
|
+
"",
|
|
53
|
+
`Ready to merge: ${artifact.ready_to_merge ? "yes" : "no"}`,
|
|
54
|
+
"",
|
|
55
|
+
];
|
|
56
|
+
if (artifact.findings.length === 0) {
|
|
57
|
+
lines.push("Замечаний не найдено.");
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
60
|
+
artifact.findings.forEach((finding, index) => {
|
|
61
|
+
lines.push(`## Finding ${index + 1}`);
|
|
62
|
+
lines.push(`- Severity: ${finding.severity}`);
|
|
63
|
+
lines.push(`- Title: ${finding.title}`);
|
|
64
|
+
lines.push("");
|
|
65
|
+
lines.push(finding.description);
|
|
66
|
+
lines.push("");
|
|
67
|
+
});
|
|
68
|
+
return lines.join("\n");
|
|
69
|
+
}
|
|
70
|
+
export const gitlabReviewArtifactsNode = {
|
|
71
|
+
kind: "gitlab-review-artifacts",
|
|
72
|
+
version: 1,
|
|
73
|
+
async run(_context, params) {
|
|
74
|
+
let parsed;
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(readFileSync(params.gitlabReviewJsonFile, "utf8"));
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
throw new TaskRunnerError(`Failed to read GitLab review artifact from ${params.gitlabReviewJsonFile}: ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
const gitlabReview = parsed;
|
|
82
|
+
const findings = (Array.isArray(gitlabReview.comments) ? gitlabReview.comments : [])
|
|
83
|
+
.map((comment, index) => toReviewFinding(comment, index))
|
|
84
|
+
.filter((finding) => finding !== null);
|
|
85
|
+
const artifact = {
|
|
86
|
+
summary: findings.length > 0
|
|
87
|
+
? `Импортировано ${findings.length} комментариев код-ревью из GitLab.`
|
|
88
|
+
: "Комментарии код-ревью в GitLab не найдены.",
|
|
89
|
+
ready_to_merge: findings.length === 0,
|
|
90
|
+
findings,
|
|
91
|
+
};
|
|
92
|
+
writeFileSync(params.reviewJsonFile, `${JSON.stringify(artifact, null, 2)}\n`, "utf8");
|
|
93
|
+
writeFileSync(params.reviewFile, `${renderReviewMarkdown(artifact)}\n`, "utf8");
|
|
94
|
+
return {
|
|
95
|
+
value: {
|
|
96
|
+
findingsCount: findings.length,
|
|
97
|
+
readyToMerge: artifact.ready_to_merge,
|
|
98
|
+
},
|
|
99
|
+
outputs: [
|
|
100
|
+
{ kind: "artifact", path: params.reviewFile, required: true },
|
|
101
|
+
{ kind: "artifact", path: params.reviewJsonFile, required: true },
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { TaskRunnerError } from "../../errors.js";
|
|
2
|
+
import { printInfo } from "../../tui.js";
|
|
3
|
+
function parseStructuredResult(output, commandLabel) {
|
|
4
|
+
const lines = output
|
|
5
|
+
.split(/\r?\n/)
|
|
6
|
+
.map((line) => line.replace(/\u001b\[[0-9;]*m/g, "").trim())
|
|
7
|
+
.filter(Boolean);
|
|
8
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
9
|
+
const line = lines[index];
|
|
10
|
+
if (!line) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const candidates = [];
|
|
14
|
+
if (line.startsWith("{") && line.endsWith("}")) {
|
|
15
|
+
candidates.push(line);
|
|
16
|
+
}
|
|
17
|
+
const firstBrace = line.indexOf("{");
|
|
18
|
+
const lastBrace = line.lastIndexOf("}");
|
|
19
|
+
if (firstBrace >= 0 && lastBrace > firstBrace) {
|
|
20
|
+
const slice = line.slice(firstBrace, lastBrace + 1).trim();
|
|
21
|
+
if (slice && !candidates.includes(slice)) {
|
|
22
|
+
candidates.push(slice);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const rawJson of candidates) {
|
|
26
|
+
let parsed;
|
|
27
|
+
try {
|
|
28
|
+
parsed = JSON.parse(rawJson);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const candidate = parsed;
|
|
37
|
+
if (typeof candidate.ok !== "boolean" ||
|
|
38
|
+
typeof candidate.kind !== "string" ||
|
|
39
|
+
typeof candidate.stage !== "string" ||
|
|
40
|
+
typeof candidate.exitCode !== "number" ||
|
|
41
|
+
typeof candidate.summary !== "string" ||
|
|
42
|
+
typeof candidate.command !== "string") {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const details = candidate.details;
|
|
46
|
+
if (details !== undefined && (!details || typeof details !== "object" || Array.isArray(details))) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
ok: candidate.ok,
|
|
51
|
+
kind: candidate.kind,
|
|
52
|
+
stage: candidate.stage,
|
|
53
|
+
exitCode: candidate.exitCode,
|
|
54
|
+
summary: candidate.summary,
|
|
55
|
+
command: candidate.command,
|
|
56
|
+
details: details ?? {},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw new TaskRunnerError(`Structured result is missing or invalid in output of '${commandLabel}'.`);
|
|
61
|
+
}
|
|
62
|
+
export const localScriptCheckNode = {
|
|
63
|
+
kind: "local-script-check",
|
|
64
|
+
version: 1,
|
|
65
|
+
async run(context, params) {
|
|
66
|
+
printInfo(params.labelText);
|
|
67
|
+
const output = await context.runtime.runCommand(params.argv, {
|
|
68
|
+
dryRun: context.dryRun,
|
|
69
|
+
verbose: context.verbose,
|
|
70
|
+
label: params.argv.join(" "),
|
|
71
|
+
printFailureOutput: true,
|
|
72
|
+
env: { ...context.env },
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
value: {
|
|
76
|
+
output,
|
|
77
|
+
parsed: parseStructuredResult(output, params.argv.join(" ")),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -6,20 +6,20 @@ export const reviewFindingsFormNode = {
|
|
|
6
6
|
async run(_context, params) {
|
|
7
7
|
let parsed;
|
|
8
8
|
try {
|
|
9
|
-
parsed = JSON.parse(readFileSync(params.
|
|
9
|
+
parsed = JSON.parse(readFileSync(params.reviewReplyJsonFile, "utf8"));
|
|
10
10
|
}
|
|
11
11
|
catch (error) {
|
|
12
|
-
throw new TaskRunnerError(`Failed to read review
|
|
12
|
+
throw new TaskRunnerError(`Failed to read review reply from ${params.reviewReplyJsonFile}: ${error.message}`);
|
|
13
13
|
}
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const selectableFindings =
|
|
17
|
-
.map((
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
const reviewReply = parsed;
|
|
15
|
+
const responses = Array.isArray(reviewReply.responses) ? reviewReply.responses : [];
|
|
16
|
+
const selectableFindings = responses
|
|
17
|
+
.map((response) => ({
|
|
18
|
+
findingTitle: typeof response.finding_title === "string" ? response.finding_title.trim() : "",
|
|
19
|
+
disposition: typeof response.disposition === "string" ? response.disposition.trim() : "",
|
|
20
|
+
action: typeof response.action === "string" ? response.action.trim() : "",
|
|
21
21
|
}))
|
|
22
|
-
.filter((
|
|
22
|
+
.filter((response) => response.findingTitle.length > 0);
|
|
23
23
|
const fields = [
|
|
24
24
|
{
|
|
25
25
|
id: "apply_all",
|
|
@@ -35,10 +35,10 @@ export const reviewFindingsFormNode = {
|
|
|
35
35
|
type: "multi-select",
|
|
36
36
|
label: "Какие findings исправить сейчас",
|
|
37
37
|
help: "Space переключает пункт. Если apply_all=false, выберите хотя бы один finding.",
|
|
38
|
-
options: selectableFindings.map((
|
|
39
|
-
value:
|
|
40
|
-
label:
|
|
41
|
-
...(
|
|
38
|
+
options: selectableFindings.map((response) => ({
|
|
39
|
+
value: response.findingTitle,
|
|
40
|
+
label: `${response.findingTitle} | ${response.disposition || "-"}`,
|
|
41
|
+
...(response.action ? { description: response.action } : {}),
|
|
42
42
|
})),
|
|
43
43
|
default: [],
|
|
44
44
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BUG_ANALYZE_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_REPLY_PROMPT_TEMPLATE, REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_TESTS_LOOP_FIX_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE,
|
|
1
|
+
import { BUG_ANALYZE_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_REPLY_PROMPT_TEMPLATE, REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_TESTS_LOOP_FIX_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, } from "../prompts.js";
|
|
2
2
|
const promptTemplates = {
|
|
3
3
|
"bug-analyze": BUG_ANALYZE_PROMPT_TEMPLATE,
|
|
4
4
|
"bug-fix": BUG_FIX_PROMPT_TEMPLATE,
|
|
@@ -13,8 +13,6 @@ const promptTemplates = {
|
|
|
13
13
|
"run-linter-loop-fix": RUN_LINTER_LOOP_FIX_PROMPT_TEMPLATE,
|
|
14
14
|
"run-tests-loop-fix": RUN_TESTS_LOOP_FIX_PROMPT_TEMPLATE,
|
|
15
15
|
"task-summary": TASK_SUMMARY_PROMPT_TEMPLATE,
|
|
16
|
-
"test-fix": TEST_FIX_PROMPT_TEMPLATE,
|
|
17
|
-
"test-linter-fix": TEST_LINTER_FIX_PROMPT_TEMPLATE,
|
|
18
16
|
};
|
|
19
17
|
export function isPromptTemplateRef(value) {
|
|
20
18
|
return value in promptTemplates;
|
|
@@ -2,12 +2,14 @@ import { commandCheckExecutor } from "../executors/command-check-executor.js";
|
|
|
2
2
|
import { claudeExecutor } from "../executors/claude-executor.js";
|
|
3
3
|
import { codexDockerExecutor } from "../executors/codex-docker-executor.js";
|
|
4
4
|
import { codexLocalExecutor } from "../executors/codex-local-executor.js";
|
|
5
|
+
import { fetchGitLabReviewExecutor } from "../executors/fetch-gitlab-review-executor.js";
|
|
5
6
|
import { jiraFetchExecutor } from "../executors/jira-fetch-executor.js";
|
|
6
7
|
import { processExecutor } from "../executors/process-executor.js";
|
|
7
8
|
import { verifyBuildExecutor } from "../executors/verify-build-executor.js";
|
|
8
9
|
const builtInExecutors = {
|
|
9
10
|
process: processExecutor,
|
|
10
11
|
"command-check": commandCheckExecutor,
|
|
12
|
+
"fetch-gitlab-review": fetchGitLabReviewExecutor,
|
|
11
13
|
"jira-fetch": jiraFetchExecutor,
|
|
12
14
|
"codex-local": codexLocalExecutor,
|
|
13
15
|
"codex-docker": codexDockerExecutor,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, reviewReplyFile, reviewReplyJsonFile, taskSummaryFile, taskSummaryJsonFile, } from "../artifacts.js";
|
|
2
|
+
import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, gitlabReviewFile, gitlabReviewInputJsonFile, gitlabReviewJsonFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, reviewReplyFile, reviewReplyJsonFile, taskSummaryFile, taskSummaryJsonFile, } from "../artifacts.js";
|
|
3
3
|
import { TaskRunnerError } from "../errors.js";
|
|
4
4
|
import { formatTemplate } from "../prompts.js";
|
|
5
5
|
function readStepRef(segments, context, originalPath) {
|
|
@@ -88,6 +88,12 @@ function resolveArtifact(spec, context) {
|
|
|
88
88
|
return designFile(taskKey);
|
|
89
89
|
case "design-json-file":
|
|
90
90
|
return designJsonFile(taskKey);
|
|
91
|
+
case "gitlab-review-file":
|
|
92
|
+
return gitlabReviewFile(taskKey);
|
|
93
|
+
case "gitlab-review-input-json-file":
|
|
94
|
+
return gitlabReviewInputJsonFile(taskKey);
|
|
95
|
+
case "gitlab-review-json-file":
|
|
96
|
+
return gitlabReviewJsonFile(taskKey);
|
|
91
97
|
case "jira-description-file":
|
|
92
98
|
return jiraDescriptionFile(taskKey);
|
|
93
99
|
case "jira-description-json-file":
|
package/dist/prompts.js
CHANGED
|
@@ -56,8 +56,6 @@ export const REVIEW_FIX_PROMPT_TEMPLATE = "Используй только ст
|
|
|
56
56
|
export const TASK_SUMMARY_PROMPT_TEMPLATE = "Посмотри в {jira_task_file}. " +
|
|
57
57
|
"Сделай краткое резюме задачи, на 1-2 абзаца. " +
|
|
58
58
|
"Сначала запиши source-of-truth JSON в {task_summary_json_file} в виде объекта { summary: string }, затем markdown-версию в {task_summary_file}.";
|
|
59
|
-
export const TEST_FIX_PROMPT_TEMPLATE = "Прогони тесты, исправь ошибки.";
|
|
60
|
-
export const TEST_LINTER_FIX_PROMPT_TEMPLATE = "Прогони линтер, исправь замечания.";
|
|
61
59
|
export const RUN_TESTS_LOOP_FIX_PROMPT_TEMPLATE = "Запусти ./run_tests.sh, проанализируй последнюю ошибку проверки, исправь код и подготовь изменения так, чтобы следующий прогон run_tests.sh прошёл успешно.";
|
|
62
60
|
export const RUN_LINTER_LOOP_FIX_PROMPT_TEMPLATE = "Запусти ./run_linter.sh, проанализируй последнюю ошибку линтера или генерации, исправь код и подготовь изменения так, чтобы следующий прогон run_linter.sh прошёл успешно.";
|
|
63
61
|
export const AUTO_REVIEW_FIX_EXTRA_PROMPT = "Исправлять только блокеры, критикалы и важные";
|
|
@@ -54,6 +54,11 @@ function validateBriefText(value, path, issues) {
|
|
|
54
54
|
}
|
|
55
55
|
expectNonEmptyString(value.summary, `${path}.summary`, issues);
|
|
56
56
|
}
|
|
57
|
+
function expectNumber(value, path, issues) {
|
|
58
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
59
|
+
issues.push(`${path} must be a number`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
57
62
|
function implementationDesignSchema() {
|
|
58
63
|
return {
|
|
59
64
|
id: "implementation-design/v1",
|
|
@@ -204,6 +209,33 @@ function reviewFixReportSchema() {
|
|
|
204
209
|
},
|
|
205
210
|
};
|
|
206
211
|
}
|
|
212
|
+
function gitlabReviewSchema() {
|
|
213
|
+
return {
|
|
214
|
+
id: "gitlab-review/v1",
|
|
215
|
+
validate({ path, value }) {
|
|
216
|
+
const issues = [];
|
|
217
|
+
if (!expectObject(value, path, issues)) {
|
|
218
|
+
return issues;
|
|
219
|
+
}
|
|
220
|
+
expectNonEmptyString(value.summary, `${path}.summary`, issues);
|
|
221
|
+
expectNonEmptyString(value.merge_request_url, `${path}.merge_request_url`, issues);
|
|
222
|
+
expectNonEmptyString(value.project_path, `${path}.project_path`, issues);
|
|
223
|
+
expectNumber(value.merge_request_iid, `${path}.merge_request_iid`, issues);
|
|
224
|
+
expectNonEmptyString(value.fetched_at, `${path}.fetched_at`, issues);
|
|
225
|
+
expectObjectArray(value.comments, `${path}.comments`, issues, (item, itemPath, currentIssues) => {
|
|
226
|
+
expectNonEmptyString(item.id, `${itemPath}.id`, currentIssues);
|
|
227
|
+
expectNonEmptyString(item.discussion_id, `${itemPath}.discussion_id`, currentIssues);
|
|
228
|
+
expectNonEmptyString(item.body, `${itemPath}.body`, currentIssues);
|
|
229
|
+
expectNonEmptyString(item.author, `${itemPath}.author`, currentIssues);
|
|
230
|
+
expectNonEmptyString(item.created_at, `${itemPath}.created_at`, currentIssues);
|
|
231
|
+
if (item.file_path !== undefined && item.file_path !== null) {
|
|
232
|
+
expectNonEmptyString(item.file_path, `${itemPath}.file_path`, currentIssues);
|
|
233
|
+
}
|
|
234
|
+
}, true);
|
|
235
|
+
return issues;
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
207
239
|
function userInputSchema() {
|
|
208
240
|
return {
|
|
209
241
|
id: "user-input/v1",
|
|
@@ -223,6 +255,7 @@ const schemas = {
|
|
|
223
255
|
"bug-analysis/v1": bugAnalysisSchema(),
|
|
224
256
|
"bug-fix-design/v1": bugFixDesignSchema(),
|
|
225
257
|
"bug-fix-plan/v1": bugFixPlanSchema(),
|
|
258
|
+
"gitlab-review/v1": gitlabReviewSchema(),
|
|
226
259
|
"implementation-design/v1": implementationDesignSchema(),
|
|
227
260
|
"implementation-plan/v1": implementationPlanSchema(),
|
|
228
261
|
"jira-description/v1": { id: "jira-description/v1", validate: ({ path, value }) => {
|