agentweaver 0.1.11 → 0.1.13
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/LICENSE +21 -0
- package/README.md +6 -0
- package/dist/artifacts.js +9 -0
- package/dist/doctor/checks/agentweaver-home.js +57 -0
- package/dist/doctor/checks/category.js +9 -0
- package/dist/doctor/checks/cwd-context.js +69 -0
- package/dist/doctor/checks/env-diagnostics.js +171 -0
- package/dist/doctor/checks/executors.js +106 -0
- package/dist/doctor/checks/flow-readiness.js +305 -0
- package/dist/doctor/checks/node-version.js +79 -0
- package/dist/doctor/checks/register.js +18 -0
- package/dist/doctor/checks/system.js +91 -0
- package/dist/doctor/index.js +4 -0
- package/dist/doctor/orchestrator.js +78 -0
- package/dist/doctor/registry.js +50 -0
- package/dist/doctor/runner.js +89 -0
- package/dist/doctor/types.js +12 -0
- package/dist/executors/codex-executor.js +2 -1
- package/dist/executors/jira-fetch-executor.js +1 -0
- package/dist/executors/opencode-executor.js +22 -11
- package/dist/executors/process-executor.js +3 -0
- package/dist/index.js +59 -35
- package/dist/interactive-ui.js +579 -159
- package/dist/jira.js +57 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +105 -21
- package/dist/pipeline/flow-specs/review/review-fix.json +1 -0
- package/dist/pipeline/flow-specs/review/review-loop.json +2 -0
- package/dist/pipeline/flow-specs/task-describe.json +64 -3
- package/dist/pipeline/nodes/jira-fetch-node.js +3 -0
- package/dist/pipeline/nodes/review-findings-form-node.js +33 -3
- package/dist/pipeline/nodes/user-input-node.js +18 -4
- package/dist/pipeline/prompt-registry.js +2 -1
- package/dist/pipeline/spec-types.js +2 -0
- package/dist/pipeline/value-resolver.js +11 -1
- package/dist/prompts.js +17 -2
- package/dist/runtime/process-runner.js +9 -3
- package/dist/structured-artifact-schema-registry.js +1 -0
- package/dist/structured-artifact-schemas.json +22 -0
- package/dist/user-input.js +8 -1
- package/package.json +4 -2
- package/dist/pipeline/flow-model-settings.js +0 -77
package/dist/jira.js
CHANGED
|
@@ -91,6 +91,26 @@ async function fetchAuthorizedBuffer(url, accept) {
|
|
|
91
91
|
}
|
|
92
92
|
return Buffer.from(await response.arrayBuffer());
|
|
93
93
|
}
|
|
94
|
+
function isSubtask(issue) {
|
|
95
|
+
return issue.fields?.issuetype?.subtask === true;
|
|
96
|
+
}
|
|
97
|
+
function getParentKey(issue) {
|
|
98
|
+
if (!issue.fields?.parent?.key) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return issue.fields.parent.key;
|
|
102
|
+
}
|
|
103
|
+
function normalizeIssue(jiraRaw) {
|
|
104
|
+
const issue = jiraRaw;
|
|
105
|
+
const key = issue.key ?? null;
|
|
106
|
+
const summary = typeof issue.fields?.summary === "string" ? issue.fields.summary : "";
|
|
107
|
+
const description = typeof issue.fields?.description === "string" ? issue.fields.description : "";
|
|
108
|
+
const type = typeof issue.fields?.issuetype?.name === "string" ? issue.fields.issuetype.name : "";
|
|
109
|
+
if (!key) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return { key, type, summary, description };
|
|
113
|
+
}
|
|
94
114
|
function toTextAttachmentContent(fileName, body) {
|
|
95
115
|
return [
|
|
96
116
|
`=== Attachment: ${fileName} ===`,
|
|
@@ -132,6 +152,42 @@ export async function fetchJiraIssue(jiraApiUrl, jiraTaskFile, attachmentsManife
|
|
|
132
152
|
const body = await fetchAuthorizedBuffer(jiraApiUrl, "application/json");
|
|
133
153
|
mkdirSync(path.dirname(jiraTaskFile), { recursive: true });
|
|
134
154
|
await writeFile(jiraTaskFile, body);
|
|
155
|
+
let enrichedFile;
|
|
156
|
+
try {
|
|
157
|
+
const rawIssue = JSON.parse(body.toString("utf8"));
|
|
158
|
+
const normalizedChild = normalizeIssue(rawIssue);
|
|
159
|
+
if (normalizedChild && isSubtask(rawIssue)) {
|
|
160
|
+
const parentKey = getParentKey(rawIssue);
|
|
161
|
+
let parentNormalized = null;
|
|
162
|
+
if (parentKey) {
|
|
163
|
+
try {
|
|
164
|
+
const parentUrl = buildJiraApiUrl(parentKey);
|
|
165
|
+
const parentBody = await fetchAuthorizedBuffer(parentUrl, "application/json");
|
|
166
|
+
const parentRaw = JSON.parse(parentBody.toString("utf8"));
|
|
167
|
+
parentNormalized = normalizeIssue(parentRaw);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
parentNormalized = null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (normalizedChild) {
|
|
174
|
+
const enrichedArtifact = {
|
|
175
|
+
issue: normalizedChild,
|
|
176
|
+
parent: parentNormalized,
|
|
177
|
+
context: {
|
|
178
|
+
scopeSource: normalizedChild.key,
|
|
179
|
+
businessContextSource: parentNormalized?.key ?? normalizedChild.key,
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
const parsedPath = path.parse(jiraTaskFile);
|
|
183
|
+
enrichedFile = path.join(parsedPath.dir, `${parsedPath.name}-enriched${parsedPath.ext}`);
|
|
184
|
+
await writeFile(enrichedFile, `${JSON.stringify(enrichedArtifact, null, 2)}\n`, "utf8");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Non-JSON response or parse error - skip enrichment
|
|
190
|
+
}
|
|
135
191
|
const attachments = parseJiraAttachments(body);
|
|
136
192
|
const manifestItems = [];
|
|
137
193
|
const planningContextChunks = [];
|
|
@@ -198,6 +254,7 @@ export async function fetchJiraIssue(jiraApiUrl, jiraTaskFile, attachmentsManife
|
|
|
198
254
|
planningContextAttachments: manifest.summary.planningContextCount,
|
|
199
255
|
...(attachmentsManifestFile ? { attachmentsManifestFile } : {}),
|
|
200
256
|
...(attachmentsContextFile ? { attachmentsContextFile } : {}),
|
|
257
|
+
...(enrichedFile ? { enrichedFile } : {}),
|
|
201
258
|
};
|
|
202
259
|
}
|
|
203
260
|
export function requireJiraTaskFile(jiraTaskFile) {
|
|
@@ -100,34 +100,72 @@
|
|
|
100
100
|
]
|
|
101
101
|
},
|
|
102
102
|
{
|
|
103
|
-
"id": "
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"
|
|
103
|
+
"id": "assess_gitlab_review",
|
|
104
|
+
"when": { "not": { "ref": "context.dryRun" } },
|
|
105
|
+
"node": "llm-prompt",
|
|
106
|
+
"prompt": {
|
|
107
|
+
"templateRef": "gitlab-review",
|
|
108
|
+
"vars": {
|
|
109
|
+
"gitlab_review_file": {
|
|
110
|
+
"artifact": {
|
|
111
|
+
"kind": "gitlab-review-file",
|
|
112
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
113
|
+
"iteration": { "ref": "params.gitlabReviewIteration" }
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"gitlab_review_json_file": {
|
|
117
|
+
"artifact": {
|
|
118
|
+
"kind": "gitlab-review-json-file",
|
|
119
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
120
|
+
"iteration": { "ref": "params.gitlabReviewIteration" }
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"review_file": {
|
|
124
|
+
"artifact": {
|
|
125
|
+
"kind": "review-file",
|
|
126
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
127
|
+
"iteration": { "ref": "params.iteration" }
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"review_json_file": {
|
|
131
|
+
"artifact": {
|
|
132
|
+
"kind": "review-json-file",
|
|
133
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
134
|
+
"iteration": { "ref": "params.iteration" }
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"review_assessment_file": {
|
|
138
|
+
"artifact": {
|
|
139
|
+
"kind": "review-assessment-file",
|
|
140
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
141
|
+
"iteration": { "ref": "params.iteration" }
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"review_assessment_json_file": {
|
|
145
|
+
"artifact": {
|
|
146
|
+
"kind": "review-assessment-json-file",
|
|
147
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
148
|
+
"iteration": { "ref": "params.iteration" }
|
|
149
|
+
}
|
|
111
150
|
}
|
|
112
151
|
},
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
152
|
+
"extraPrompt": { "ref": "params.extraPrompt" },
|
|
153
|
+
"format": "task-prompt"
|
|
154
|
+
},
|
|
155
|
+
"params": {
|
|
156
|
+
"labelText": {
|
|
157
|
+
"template": "Assessing GitLab findings and proposed fixes (iteration {iteration})",
|
|
158
|
+
"vars": {
|
|
117
159
|
"iteration": { "ref": "params.iteration" }
|
|
118
160
|
}
|
|
119
161
|
},
|
|
120
|
-
"
|
|
121
|
-
|
|
122
|
-
"kind": "review-json-file",
|
|
123
|
-
"taskKey": { "ref": "params.taskKey" },
|
|
124
|
-
"iteration": { "ref": "params.iteration" }
|
|
125
|
-
}
|
|
126
|
-
}
|
|
162
|
+
"model": { "ref": "params.llmModel" },
|
|
163
|
+
"executor": { "ref": "params.llmExecutor" }
|
|
127
164
|
},
|
|
128
165
|
"expect": [
|
|
129
166
|
{
|
|
130
167
|
"kind": "require-artifacts",
|
|
168
|
+
"when": { "not": { "ref": "context.dryRun" } },
|
|
131
169
|
"paths": {
|
|
132
170
|
"list": [
|
|
133
171
|
{
|
|
@@ -136,13 +174,21 @@
|
|
|
136
174
|
"taskKey": { "ref": "params.taskKey" },
|
|
137
175
|
"iteration": { "ref": "params.iteration" }
|
|
138
176
|
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"artifact": {
|
|
180
|
+
"kind": "review-assessment-file",
|
|
181
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
182
|
+
"iteration": { "ref": "params.iteration" }
|
|
183
|
+
}
|
|
139
184
|
}
|
|
140
185
|
]
|
|
141
186
|
},
|
|
142
|
-
"message": "GitLab review
|
|
187
|
+
"message": "GitLab review assessment did not produce the required artifacts."
|
|
143
188
|
},
|
|
144
189
|
{
|
|
145
190
|
"kind": "require-structured-artifacts",
|
|
191
|
+
"when": { "not": { "ref": "context.dryRun" } },
|
|
146
192
|
"items": [
|
|
147
193
|
{
|
|
148
194
|
"path": {
|
|
@@ -153,11 +199,49 @@
|
|
|
153
199
|
}
|
|
154
200
|
},
|
|
155
201
|
"schemaId": "review-findings/v1"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"path": {
|
|
205
|
+
"artifact": {
|
|
206
|
+
"kind": "review-assessment-json-file",
|
|
207
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
208
|
+
"iteration": { "ref": "params.iteration" }
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
"schemaId": "review-assessment/v1"
|
|
156
212
|
}
|
|
157
213
|
],
|
|
158
|
-
"message": "GitLab review
|
|
214
|
+
"message": "GitLab review assessment produced invalid structured artifacts."
|
|
159
215
|
}
|
|
160
216
|
]
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"id": "run_review_fix",
|
|
220
|
+
"when": { "not": { "ref": "context.dryRun" } },
|
|
221
|
+
"node": "flow-run",
|
|
222
|
+
"params": {
|
|
223
|
+
"fileName": { "const": "review-fix.json" },
|
|
224
|
+
"labelText": {
|
|
225
|
+
"template": "Running review-fix for GitLab review (iteration {iteration})",
|
|
226
|
+
"vars": {
|
|
227
|
+
"iteration": { "ref": "params.iteration" }
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
231
|
+
"latestIteration": { "ref": "params.iteration" },
|
|
232
|
+
"reviewAssessmentJsonFile": {
|
|
233
|
+
"artifact": {
|
|
234
|
+
"kind": "review-assessment-json-file",
|
|
235
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
236
|
+
"iteration": { "ref": "params.iteration" }
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"reviewFixSelectionJsonFile": { "ref": "params.reviewFixSelectionJsonFile" },
|
|
240
|
+
"reviewFixPoints": { "ref": "params.reviewFixPoints" },
|
|
241
|
+
"extraPrompt": { "ref": "params.extraPrompt" },
|
|
242
|
+
"llmModel": { "ref": "params.llmModel" },
|
|
243
|
+
"llmExecutor": { "ref": "params.llmExecutor" }
|
|
244
|
+
}
|
|
161
245
|
}
|
|
162
246
|
]
|
|
163
247
|
}
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
"taskKey": { "ref": "params.taskKey" },
|
|
100
100
|
"latestIteration": { "const": 1 },
|
|
101
|
+
"reviewAssessmentJsonFile": { "const": null },
|
|
101
102
|
"reviewFixSelectionJsonFile": {
|
|
102
103
|
"template": "{workspace}/review-fix-selection-{taskKey}-iter-{iteration}.json",
|
|
103
104
|
"vars": {
|
|
@@ -234,6 +235,7 @@
|
|
|
234
235
|
},
|
|
235
236
|
"taskKey": { "ref": "params.taskKey" },
|
|
236
237
|
"latestIteration": { "ref": "repeat.iteration" },
|
|
238
|
+
"reviewAssessmentJsonFile": { "const": null },
|
|
237
239
|
"reviewFixSelectionJsonFile": {
|
|
238
240
|
"template": "{workspace}/review-fix-selection-{taskKey}-iter-{iteration}.json",
|
|
239
241
|
"vars": {
|
|
@@ -26,6 +26,18 @@
|
|
|
26
26
|
"required": false
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
|
+
{
|
|
30
|
+
"const": {
|
|
31
|
+
"id": "additional_instructions",
|
|
32
|
+
"type": "text",
|
|
33
|
+
"label": "Additional instructions",
|
|
34
|
+
"help": "Необязательные указания для генерации описания. Например: ссылка на другой локальный репозиторий, связанный проект, ограничения или полезный контекст.",
|
|
35
|
+
"required": false,
|
|
36
|
+
"multiline": true,
|
|
37
|
+
"rows": 4,
|
|
38
|
+
"placeholder": "Например: смотри также ../billing-service, описание API есть в docs/task-templates.md"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
29
41
|
{
|
|
30
42
|
"const": {
|
|
31
43
|
"id": "task_description",
|
|
@@ -90,6 +102,18 @@
|
|
|
90
102
|
"kind": "jira-task-file",
|
|
91
103
|
"taskKey": { "ref": "params.taskKey" }
|
|
92
104
|
}
|
|
105
|
+
},
|
|
106
|
+
"attachmentsManifestFile": {
|
|
107
|
+
"artifact": {
|
|
108
|
+
"kind": "jira-attachments-manifest-file",
|
|
109
|
+
"taskKey": { "ref": "params.taskKey" }
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"attachmentsContextFile": {
|
|
113
|
+
"artifact": {
|
|
114
|
+
"kind": "jira-attachments-context-file",
|
|
115
|
+
"taskKey": { "ref": "params.taskKey" }
|
|
116
|
+
}
|
|
93
117
|
}
|
|
94
118
|
},
|
|
95
119
|
"expect": [
|
|
@@ -102,6 +126,26 @@
|
|
|
102
126
|
}
|
|
103
127
|
},
|
|
104
128
|
"message": "Jira fetch node did not produce the Jira task file."
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"kind": "require-file",
|
|
132
|
+
"path": {
|
|
133
|
+
"artifact": {
|
|
134
|
+
"kind": "jira-attachments-manifest-file",
|
|
135
|
+
"taskKey": { "ref": "params.taskKey" }
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"message": "Jira fetch node did not produce the Jira attachments manifest file."
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"kind": "require-file",
|
|
142
|
+
"path": {
|
|
143
|
+
"artifact": {
|
|
144
|
+
"kind": "jira-attachments-context-file",
|
|
145
|
+
"taskKey": { "ref": "params.taskKey" }
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"message": "Jira fetch node did not produce the Jira attachments context file."
|
|
105
149
|
}
|
|
106
150
|
]
|
|
107
151
|
},
|
|
@@ -112,7 +156,7 @@
|
|
|
112
156
|
"ref": "steps.task_describe.collect_task_source.value.values.jira_ref"
|
|
113
157
|
},
|
|
114
158
|
"prompt": {
|
|
115
|
-
"
|
|
159
|
+
"templateRef": "task-describe",
|
|
116
160
|
"vars": {
|
|
117
161
|
"jira_task_file": {
|
|
118
162
|
"artifact": {
|
|
@@ -120,6 +164,18 @@
|
|
|
120
164
|
"taskKey": { "ref": "params.taskKey" }
|
|
121
165
|
}
|
|
122
166
|
},
|
|
167
|
+
"jira_attachments_manifest_file": {
|
|
168
|
+
"artifact": {
|
|
169
|
+
"kind": "jira-attachments-manifest-file",
|
|
170
|
+
"taskKey": { "ref": "params.taskKey" }
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"jira_attachments_context_file": {
|
|
174
|
+
"artifact": {
|
|
175
|
+
"kind": "jira-attachments-context-file",
|
|
176
|
+
"taskKey": { "ref": "params.taskKey" }
|
|
177
|
+
}
|
|
178
|
+
},
|
|
123
179
|
"jira_description_file": {
|
|
124
180
|
"artifact": {
|
|
125
181
|
"kind": "jira-description-file",
|
|
@@ -135,7 +191,12 @@
|
|
|
135
191
|
}
|
|
136
192
|
}
|
|
137
193
|
},
|
|
138
|
-
"extraPrompt": {
|
|
194
|
+
"extraPrompt": {
|
|
195
|
+
"appendPrompt": {
|
|
196
|
+
"base": { "ref": "params.extraPrompt" },
|
|
197
|
+
"suffix": { "ref": "steps.task_describe.collect_task_source.value.promptSuffix" }
|
|
198
|
+
}
|
|
199
|
+
},
|
|
139
200
|
"format": "task-prompt"
|
|
140
201
|
},
|
|
141
202
|
"params": {
|
|
@@ -202,7 +263,7 @@
|
|
|
202
263
|
}
|
|
203
264
|
},
|
|
204
265
|
"prompt": {
|
|
205
|
-
"inlineTemplate": "Review the user-provided task description in {task_input_file}. Use the values.task_description field as source of truth. Formulate a typical Jira task description in simple product language, without overloading with technical details. Description structure: Problem, Context, What needs to be done, Acceptance criteria. If the user description lacks details, do not invent them and keep formulations general enough. The source-of-truth JSON must be in English for all generated semantic string values, while verbatim user-provided source text may be preserved as needed. First write the source-of-truth JSON to {jira_description_json_file} as object { summary: string }, then the markdown version to {jira_description_file}.",
|
|
266
|
+
"inlineTemplate": "Review the user-provided task description in {task_input_file}. Use the values.task_description field as source of truth. Study the repository code relevant to the task before writing the description, and use that analysis to add concise context about the current behavior, existing routes/modules, contracts, DTOs, or missing pieces directly related to the request. The result must be richer than a paraphrase of the input, but do not invent requirements that are not supported by the user text or the code. Formulate a typical Jira task description in simple product language, without overloading with technical details. Description structure: Problem, Context, What needs to be done, Acceptance criteria. Add concrete implementation context when it is supported by the code: mention where changes are likely needed, including specific routes, handlers, services, DTOs, API contracts, validation rules, repository methods, events, or UI screens. If the user description lacks details, do not invent them and keep formulations general enough. The source-of-truth JSON must be in English for all generated semantic string values, while verbatim user-provided source text may be preserved as needed. First write the source-of-truth JSON to {jira_description_json_file} as object { summary: string }, then the markdown version to {jira_description_file}.",
|
|
206
267
|
"vars": {
|
|
207
268
|
"task_input_file": {
|
|
208
269
|
"artifact": {
|
|
@@ -17,6 +17,9 @@ export const jiraFetchNode = {
|
|
|
17
17
|
if (params.attachmentsContextFile) {
|
|
18
18
|
outputs.push({ kind: "artifact", path: params.attachmentsContextFile, required: true });
|
|
19
19
|
}
|
|
20
|
+
if (value.enrichedFile) {
|
|
21
|
+
outputs.push({ kind: "artifact", path: value.enrichedFile, required: false });
|
|
22
|
+
}
|
|
20
23
|
return {
|
|
21
24
|
value,
|
|
22
25
|
outputs,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { TaskRunnerError } from "../../errors.js";
|
|
3
|
-
const SEVERITY_FIX_THRESHOLD = ["blocker", "critical", "major"];
|
|
4
3
|
export const reviewFindingsFormNode = {
|
|
5
4
|
kind: "review-findings-form",
|
|
6
5
|
version: 1,
|
|
@@ -13,6 +12,27 @@ export const reviewFindingsFormNode = {
|
|
|
13
12
|
throw new TaskRunnerError(`Failed to read review findings from ${params.reviewFindingsJsonFile}: ${error.message}`);
|
|
14
13
|
}
|
|
15
14
|
const reviewFindings = parsed;
|
|
15
|
+
let reviewAssessment = null;
|
|
16
|
+
if (typeof params.reviewAssessmentJsonFile === "string" && params.reviewAssessmentJsonFile.trim().length > 0) {
|
|
17
|
+
try {
|
|
18
|
+
reviewAssessment = JSON.parse(readFileSync(params.reviewAssessmentJsonFile, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new TaskRunnerError(`Failed to read review assessment from ${params.reviewAssessmentJsonFile}: ${error.message}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const assessmentByTitle = new Map();
|
|
25
|
+
for (const record of reviewAssessment?.assessments ?? []) {
|
|
26
|
+
const findingTitle = typeof record.finding_title === "string" ? record.finding_title.trim() : "";
|
|
27
|
+
const verdict = typeof record.verdict === "string" ? record.verdict.trim() : "";
|
|
28
|
+
const rationale = typeof record.rationale === "string" ? record.rationale.trim() : "";
|
|
29
|
+
const proposedFix = typeof record.proposed_fix === "string" ? record.proposed_fix.trim() : "";
|
|
30
|
+
const fixRequired = typeof record.fix_required === "boolean" ? record.fix_required : null;
|
|
31
|
+
if (findingTitle.length === 0 || assessmentByTitle.has(findingTitle)) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
assessmentByTitle.set(findingTitle, { verdict, rationale, proposedFix, fixRequired });
|
|
35
|
+
}
|
|
16
36
|
const findings = Array.isArray(reviewFindings.findings) ? reviewFindings.findings : [];
|
|
17
37
|
const selectableFindings = findings
|
|
18
38
|
.map((finding) => ({
|
|
@@ -20,9 +40,9 @@ export const reviewFindingsFormNode = {
|
|
|
20
40
|
title: typeof finding.title === "string" ? finding.title.trim() : "",
|
|
21
41
|
description: typeof finding.description === "string" ? finding.description.trim() : "",
|
|
22
42
|
disposition: typeof finding.disposition === "string" ? finding.disposition.trim().toLowerCase() : null,
|
|
43
|
+
assessment: assessmentByTitle.get(typeof finding.title === "string" ? finding.title.trim() : "") ?? null,
|
|
23
44
|
}))
|
|
24
45
|
.filter((finding) => finding.title.length > 0 &&
|
|
25
|
-
SEVERITY_FIX_THRESHOLD.includes(finding.severity) &&
|
|
26
46
|
finding.disposition !== "resolved");
|
|
27
47
|
const fields = [
|
|
28
48
|
{
|
|
@@ -42,7 +62,17 @@ export const reviewFindingsFormNode = {
|
|
|
42
62
|
options: selectableFindings.map((finding) => ({
|
|
43
63
|
value: finding.title,
|
|
44
64
|
label: `${finding.title} | ${finding.severity || "-"}`,
|
|
45
|
-
|
|
65
|
+
description: [
|
|
66
|
+
finding.description,
|
|
67
|
+
finding.assessment?.verdict ? `Verdict: ${finding.assessment.verdict}` : "",
|
|
68
|
+
finding.assessment?.rationale ? `Rationale: ${finding.assessment.rationale}` : "",
|
|
69
|
+
finding.assessment?.proposedFix ? `Proposed fix: ${finding.assessment.proposedFix}` : "",
|
|
70
|
+
typeof finding.assessment?.fixRequired === "boolean"
|
|
71
|
+
? `Fix required: ${finding.assessment.fixRequired ? "yes" : "no"}`
|
|
72
|
+
: "",
|
|
73
|
+
]
|
|
74
|
+
.filter((item) => item.trim().length > 0)
|
|
75
|
+
.join("\n\n"),
|
|
46
76
|
})),
|
|
47
77
|
default: [],
|
|
48
78
|
});
|
|
@@ -35,10 +35,19 @@ function buildReviewFixPromptSuffix(params, values) {
|
|
|
35
35
|
function buildTaskDescribePromptSuffix(params, values) {
|
|
36
36
|
const jiraRef = typeof values.jira_ref === "string" ? values.jira_ref.trim() : "";
|
|
37
37
|
const taskDescription = typeof values.task_description === "string" ? values.task_description.trim() : "";
|
|
38
|
+
const additionalInstructions = typeof values.additional_instructions === "string" ? values.additional_instructions.trim() : "";
|
|
38
39
|
if (jiraRef) {
|
|
39
40
|
return {
|
|
40
|
-
promptSuffix:
|
|
41
|
-
|
|
41
|
+
promptSuffix: additionalInstructions
|
|
42
|
+
? [
|
|
43
|
+
"Use the user-provided additional instructions together with the Jira task.",
|
|
44
|
+
`User input file: ${params.outputFile}`,
|
|
45
|
+
`Additional instructions:\n${additionalInstructions}`,
|
|
46
|
+
].join("\n\n")
|
|
47
|
+
: "",
|
|
48
|
+
summaryText: additionalInstructions
|
|
49
|
+
? `Источник задачи: Jira\nJira: ${jiraRef}\n\nДополнительные указания:\n${additionalInstructions}`
|
|
50
|
+
: `Источник задачи: Jira\nJira: ${jiraRef}`,
|
|
42
51
|
};
|
|
43
52
|
}
|
|
44
53
|
return {
|
|
@@ -46,8 +55,13 @@ function buildTaskDescribePromptSuffix(params, values) {
|
|
|
46
55
|
"Use the user task description as source of truth.",
|
|
47
56
|
`User input file: ${params.outputFile}`,
|
|
48
57
|
`Task description:\n${taskDescription}`,
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
additionalInstructions ? `Additional instructions:\n${additionalInstructions}` : "",
|
|
59
|
+
]
|
|
60
|
+
.filter((item) => item.trim().length > 0)
|
|
61
|
+
.join("\n\n"),
|
|
62
|
+
summaryText: additionalInstructions
|
|
63
|
+
? `Task source: user-input\n\n${taskDescription}\n\nAdditional instructions:\n${additionalInstructions}`
|
|
64
|
+
: `Task source: user-input\n\n${taskDescription}`,
|
|
51
65
|
};
|
|
52
66
|
}
|
|
53
67
|
function buildPromptSuffix(params, values) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { BUG_ANALYZE_PROMPT_TEMPLATE, COMMIT_MESSAGE_PROMPT_TEMPLATE, GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, JIRA_DESCRIPTION_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_QUESTIONS_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROJECT_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, } from "../prompts.js";
|
|
1
|
+
import { BUG_ANALYZE_PROMPT_TEMPLATE, COMMIT_MESSAGE_PROMPT_TEMPLATE, GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE, GITLAB_REVIEW_PROMPT_TEMPLATE, BUG_FIX_PROMPT_TEMPLATE, IMPLEMENT_PROMPT_TEMPLATE, JIRA_DESCRIPTION_PROMPT_TEMPLATE, MR_DESCRIPTION_PROMPT_TEMPLATE, PLAN_QUESTIONS_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROJECT_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, RUN_GO_LINTER_LOOP_FIX_PROMPT_TEMPLATE, RUN_GO_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,
|
|
5
5
|
"commit-message": COMMIT_MESSAGE_PROMPT_TEMPLATE,
|
|
6
6
|
"gitlab-diff-review": GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE,
|
|
7
|
+
"gitlab-review": GITLAB_REVIEW_PROMPT_TEMPLATE,
|
|
7
8
|
implement: IMPLEMENT_PROMPT_TEMPLATE,
|
|
8
9
|
"task-describe": JIRA_DESCRIPTION_PROMPT_TEMPLATE,
|
|
9
10
|
"mr-description": MR_DESCRIPTION_PROMPT_TEMPLATE,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, gitlabDiffFile, gitlabDiffJsonFile, gitlabDiffReviewInputJsonFile, gitlabReviewFile, gitlabReviewInputJsonFile, gitlabReviewJsonFile, jiraAttachmentsContextFile, jiraAttachmentsManifestFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planningAnswersJsonFile, planningQuestionsJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, runGoLinterResultJsonFile, runGoTestsResultJsonFile, taskSummaryFile, taskDescribeInputJsonFile, taskSummaryJsonFile, gitStatusJsonFile, gitCommitMessageJsonFile, gitCommitInputJsonFile, selectFilesOutputJsonFile, commitMessageOutputJsonFile, gitDiffFile as gitDiffFileHelper, } from "../artifacts.js";
|
|
2
|
+
import { artifactFile, bugAnalyzeArtifacts, bugAnalyzeFile, bugAnalyzeJsonFile, bugFixDesignFile, bugFixDesignJsonFile, bugFixPlanFile, bugFixPlanJsonFile, designFile, designJsonFile, gitlabDiffFile, gitlabDiffJsonFile, gitlabDiffReviewInputJsonFile, gitlabReviewFile, gitlabReviewInputJsonFile, gitlabReviewJsonFile, jiraAttachmentsContextFile, jiraAttachmentsManifestFile, jiraDescriptionFile, jiraDescriptionJsonFile, jiraTaskFile, mrDescriptionFile, mrDescriptionJsonFile, planningAnswersJsonFile, planningQuestionsJsonFile, planArtifacts, planFile, planJsonFile, qaFile, qaJsonFile, readyToMergeFile, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixFile, reviewFixJsonFile, reviewJsonFile, runGoLinterResultJsonFile, runGoTestsResultJsonFile, taskSummaryFile, taskDescribeInputJsonFile, taskSummaryJsonFile, gitStatusJsonFile, gitCommitMessageJsonFile, gitCommitInputJsonFile, selectFilesOutputJsonFile, commitMessageOutputJsonFile, gitDiffFile as gitDiffFileHelper, } from "../artifacts.js";
|
|
3
3
|
import { TaskRunnerError } from "../errors.js";
|
|
4
4
|
import { formatTemplate } from "../prompts.js";
|
|
5
5
|
function readStepRef(segments, context, originalPath) {
|
|
@@ -138,6 +138,16 @@ function resolveArtifact(spec, context) {
|
|
|
138
138
|
throw new TaskRunnerError("review-json-file requires iteration");
|
|
139
139
|
}
|
|
140
140
|
return reviewJsonFile(taskKey, iteration);
|
|
141
|
+
case "review-assessment-file":
|
|
142
|
+
if (iteration === undefined) {
|
|
143
|
+
throw new TaskRunnerError("review-assessment-file requires iteration");
|
|
144
|
+
}
|
|
145
|
+
return reviewAssessmentFile(taskKey, iteration);
|
|
146
|
+
case "review-assessment-json-file":
|
|
147
|
+
if (iteration === undefined) {
|
|
148
|
+
throw new TaskRunnerError("review-assessment-json-file requires iteration");
|
|
149
|
+
}
|
|
150
|
+
return reviewAssessmentJsonFile(taskKey, iteration);
|
|
141
151
|
case "review-fix-file":
|
|
142
152
|
if (iteration === undefined) {
|
|
143
153
|
throw new TaskRunnerError("review-fix-file requires iteration");
|
package/dist/prompts.js
CHANGED
|
@@ -70,6 +70,15 @@ export const GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE = "Conduct a code review of the
|
|
|
70
70
|
`First write the structured result to {review_json_file}. ${strictSchemaInstruction("{review_json_file}", "review-findings/v1")}` +
|
|
71
71
|
"Then write the derivative markdown version to {review_file}. " +
|
|
72
72
|
"If ready_to_merge=true and there are no blockers, create the {ready_to_merge_file} file.";
|
|
73
|
+
export const GITLAB_REVIEW_PROMPT_TEMPLATE = "Validate GitLab merge request review comments. " +
|
|
74
|
+
"Use the structured GitLab review artifact {gitlab_review_json_file} as source of truth, and markdown {gitlab_review_file} only as a convenient human-readable representation. " +
|
|
75
|
+
"Determine which comments are valid actionable findings that should be addressed in the current code. " +
|
|
76
|
+
"Ignore comments that are obsolete, already resolved, duplicates, purely conversational, or not actionable. " +
|
|
77
|
+
"Normalize the remaining actionable findings into the review findings schema with accurate severities, concise titles, and concrete descriptions. " +
|
|
78
|
+
"For each remaining finding, assess whether the complaint is fair in the current code and propose a concrete fix. " +
|
|
79
|
+
`First write the structured result to {review_json_file}. ${strictSchemaInstruction("{review_json_file}", "review-findings/v1")}` +
|
|
80
|
+
`Then write the structured assessment result to {review_assessment_json_file}. ${strictSchemaInstruction("{review_assessment_json_file}", "review-assessment/v1")}` +
|
|
81
|
+
"Then write the derivative markdown version to {review_file} and the derivative markdown assessment to {review_assessment_file}.";
|
|
73
82
|
export const REVIEW_SUMMARY_PROMPT_TEMPLATE = "Look at {review_file}. " +
|
|
74
83
|
"Create a brief list of comments without details, 3-7 items. " +
|
|
75
84
|
"Write the result to {review_summary_file}.";
|
|
@@ -82,10 +91,16 @@ export const REVIEW_FIX_PROMPT_TEMPLATE = "Use only structured artifacts as sour
|
|
|
82
91
|
export const TASK_SUMMARY_PROMPT_TEMPLATE = "Look at {jira_task_file}. " +
|
|
83
92
|
"Create a brief summary of the task, 1-2 paragraphs. " +
|
|
84
93
|
`First write the source-of-truth JSON to {task_summary_json_file}. ${strictSchemaInstruction("{task_summary_json_file}", "task-summary/v1")}Then write the markdown version to {task_summary_file}.`;
|
|
85
|
-
export const JIRA_DESCRIPTION_PROMPT_TEMPLATE = "
|
|
94
|
+
export const JIRA_DESCRIPTION_PROMPT_TEMPLATE = "Review the task in {jira_task_file}. " +
|
|
95
|
+
"Be sure to use the original issue description and all useful issue comments from the Jira payload as source material; if comments clarify scope, expected behavior, edge cases, or acceptance criteria, reflect that context in the result. " +
|
|
96
|
+
"Analyze additional materials from Jira attachments manifest {jira_attachments_manifest_file} and text context {jira_attachments_context_file}; if attachments contain more detailed requirements or constraints, treat them as source of truth alongside the Jira issue. " +
|
|
97
|
+
"Study the repository code relevant to the task before writing the description. Verify whether the mentioned APIs, routes, handlers, modules, entities, or screens already exist and how they currently behave. For example, if the task mentions a route such as GET v1/task-templates, inspect the code that implements or should implement that route and use that understanding to add concise but useful context. " +
|
|
98
|
+
"The result must be richer than a paraphrase of the title: include context from the task description, comments, attachments, and relevant code, but do not invent requirements that are not supported by those sources. " +
|
|
86
99
|
"Formulate a typical Jira task description in simple product language, without overloading with technical details. " +
|
|
87
100
|
"Description structure: Problem, Context, What needs to be done, Acceptance criteria. " +
|
|
88
|
-
"
|
|
101
|
+
"Add concrete implementation context when it is supported by the task and the code: mention where changes are likely needed, including specific routes, handlers, services, DTOs, API contracts, validation rules, repository methods, events, or UI screens. " +
|
|
102
|
+
"If the code already defines a relevant request/response contract or DTO, reflect that in the description at a high level so the task is actionable. " +
|
|
103
|
+
"Write only what helps understand the essence of the task and expected result; technical details, internal service names, data models, file names, REST methods, DTO names, contracts, and implementation steps should be mentioned when they add necessary context or make the task materially clearer. " +
|
|
89
104
|
`First write the source-of-truth JSON to {jira_description_json_file}. ${strictSchemaInstruction("{jira_description_json_file}", "jira-description/v1")}Then write the markdown version to {jira_description_file}.`;
|
|
90
105
|
export const RUN_GO_TESTS_LOOP_FIX_PROMPT_TEMPLATE = "Use the structured result of the last run of run_go_tests.py from {tests_result_json_file} as source of truth. " +
|
|
91
106
|
"Analyze the last test error, fix the code, and prepare changes so that the next run of run_go_tests.py succeeds.";
|
|
@@ -35,7 +35,7 @@ function formatLaunchDetails(statusLabel) {
|
|
|
35
35
|
return lines.join("\n");
|
|
36
36
|
}
|
|
37
37
|
export async function runCommand(argv, options = {}) {
|
|
38
|
-
const { env, dryRun = false, verbose = false, label, printFailureOutput = true, signal } = options;
|
|
38
|
+
const { env, dryRun = false, verbose = false, label, printFailureOutput = true, stdin, signal } = options;
|
|
39
39
|
const outputAdapter = getOutputAdapter();
|
|
40
40
|
if (signal?.aborted) {
|
|
41
41
|
throw new FlowInterruptedError();
|
|
@@ -49,9 +49,12 @@ export async function runCommand(argv, options = {}) {
|
|
|
49
49
|
if (verbose && outputAdapter.supportsPassthrough) {
|
|
50
50
|
await new Promise((resolve, reject) => {
|
|
51
51
|
const child = spawn(argv[0] ?? "", argv.slice(1), {
|
|
52
|
-
stdio: "inherit",
|
|
52
|
+
stdio: [stdin !== undefined ? "pipe" : "inherit", "inherit", "inherit"],
|
|
53
53
|
env,
|
|
54
54
|
});
|
|
55
|
+
if (stdin !== undefined) {
|
|
56
|
+
child.stdin?.end(stdin);
|
|
57
|
+
}
|
|
55
58
|
let abortTimer = null;
|
|
56
59
|
const abortHandler = () => {
|
|
57
60
|
child.kill("SIGTERM");
|
|
@@ -88,9 +91,12 @@ export async function runCommand(argv, options = {}) {
|
|
|
88
91
|
let output = "";
|
|
89
92
|
const child = spawn(argv[0] ?? "", argv.slice(1), {
|
|
90
93
|
env,
|
|
91
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
94
|
+
stdio: [stdin !== undefined ? "pipe" : "ignore", "pipe", "pipe"],
|
|
92
95
|
});
|
|
93
96
|
setCurrentExecutor(statusLabel);
|
|
97
|
+
if (stdin !== undefined) {
|
|
98
|
+
child.stdin?.end(stdin);
|
|
99
|
+
}
|
|
94
100
|
child.stdout?.on("data", (chunk) => {
|
|
95
101
|
const text = String(chunk);
|
|
96
102
|
output += text;
|