agentweaver 0.1.13 → 0.1.15
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/README.md +24 -19
- package/dist/artifacts.js +6 -1
- package/dist/doctor/checks/cwd-context.js +4 -3
- package/dist/doctor/checks/env-diagnostics.js +168 -71
- package/dist/doctor/checks/flow-readiness.js +210 -198
- package/dist/doctor/index.js +1 -1
- package/dist/doctor/orchestrator.js +18 -7
- package/dist/doctor/runner.js +9 -8
- package/dist/doctor/types.js +12 -0
- package/dist/index.js +119 -55
- package/dist/interactive-ui.js +25 -25
- package/dist/pipeline/declarative-flows.js +1 -0
- package/dist/pipeline/flow-catalog.js +4 -0
- package/dist/pipeline/flow-specs/auto-common.json +1 -0
- package/dist/pipeline/flow-specs/auto-golang.json +2 -1
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +1 -0
- package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
- package/dist/pipeline/flow-specs/design-review.json +239 -0
- package/dist/pipeline/flow-specs/git-commit.json +1 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +3 -2
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +3 -2
- package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
- package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +3 -2
- package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +3 -2
- package/dist/pipeline/flow-specs/implement.json +13 -0
- package/dist/pipeline/flow-specs/plan-revise.json +261 -0
- package/dist/pipeline/flow-specs/plan.json +2 -1
- package/dist/pipeline/flow-specs/review/review-fix.json +1 -0
- package/dist/pipeline/flow-specs/review/review-loop.json +1 -0
- package/dist/pipeline/flow-specs/review/review-project.json +1 -0
- package/dist/pipeline/flow-specs/review/review.json +2 -1
- package/dist/pipeline/flow-specs/task-describe.json +67 -8
- package/dist/pipeline/node-registry.js +8 -0
- package/dist/pipeline/nodes/ensure-summary-json-node.js +59 -0
- package/dist/pipeline/nodes/git-commit-node.js +1 -1
- package/dist/pipeline/nodes/git-status-node.js +1 -1
- package/dist/pipeline/nodes/review-findings-form-node.js +8 -8
- package/dist/pipeline/nodes/user-input-node.js +2 -2
- package/dist/pipeline/prompt-registry.js +3 -1
- package/dist/pipeline/spec-types.js +2 -0
- package/dist/pipeline/value-resolver.js +11 -1
- package/dist/prompts.js +49 -2
- package/dist/runtime/design-review-input-contract.js +112 -0
- package/dist/runtime/plan-revise-input-contract.js +144 -0
- package/dist/runtime/process-runner.js +2 -2
- package/dist/runtime/ready-to-merge.js +10 -0
- package/dist/scope.js +13 -4
- package/dist/structured-artifact-schema-registry.js +1 -0
- package/dist/structured-artifact-schemas.json +117 -0
- package/dist/structured-artifacts.js +6 -0
- package/package.json +3 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "plan-flow",
|
|
3
3
|
"version": 1,
|
|
4
|
+
"description": "Fetches Jira task with attachments, generates clarifying questions for the developer, collects answers, and produces design, implementation plan, and QA plan as structured JSON and markdown artifacts.",
|
|
4
5
|
"phases": [
|
|
5
6
|
{
|
|
6
7
|
"id": "plan",
|
|
@@ -158,7 +159,7 @@
|
|
|
158
159
|
"formId": { "const": "planning-questions" },
|
|
159
160
|
"title": { "const": "Planning Questions" },
|
|
160
161
|
"description": {
|
|
161
|
-
"const": "
|
|
162
|
+
"const": "Answer the model's questions if they are needed for an accurate design/plan. If there are no questions, this step will complete automatically."
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "review-fix-flow",
|
|
3
3
|
"version": 1,
|
|
4
|
+
"description": "Takes review findings, auto-selects blockers and criticals (or lets the developer pick manually), builds a targeted fix prompt, and applies fixes locally. Runs mandatory checks after modifications.",
|
|
4
5
|
"constants": {
|
|
5
6
|
"autoReviewFixExtraPrompt": "Fix only blockers and criticals"
|
|
6
7
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "review-loop-flow",
|
|
3
3
|
"version": 1,
|
|
4
|
+
"description": "Iteratively runs review → review-fix cycles up to 5 times. Stops early when ready-to-merge is achieved. Each iteration auto-selects blockers and critical findings for fixing.",
|
|
4
5
|
"constants": {
|
|
5
6
|
"autoReviewFixExtraPrompt": "Fix only blockers and criticals. Skip minor and warning severity."
|
|
6
7
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "review-project-flow",
|
|
3
3
|
"version": 1,
|
|
4
|
+
"description": "Project-level code review. Runs a single LLM review step using the review-project prompt template. Used internally by the review command when no prior design/plan artifacts are present.",
|
|
4
5
|
"phases": [
|
|
5
6
|
{
|
|
6
7
|
"id": "review",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "review-flow",
|
|
3
3
|
"version": 1,
|
|
4
|
+
"description": "Performs code review of current changes against the task design and plan. Produces structured review findings with severity levels and a ready-to-merge verdict.",
|
|
4
5
|
"phases": [
|
|
5
6
|
{
|
|
6
7
|
"id": "review",
|
|
@@ -116,7 +117,7 @@
|
|
|
116
117
|
}
|
|
117
118
|
},
|
|
118
119
|
"panelTitle": { "const": "Ready To Merge" },
|
|
119
|
-
"foundMessage": { "const": "
|
|
120
|
+
"foundMessage": { "const": "Changes are ready to merge.\nFile ready-to-merge.md has been created." },
|
|
120
121
|
"tone": { "const": "green" }
|
|
121
122
|
}
|
|
122
123
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"kind": "task-describe-flow",
|
|
3
3
|
"version": 1,
|
|
4
|
+
"description": "Generates a brief task description from a Jira issue or from manual input. When Jira is provided, fetches the issue and summarizes it; otherwise accepts free-form text and analyzes the codebase to produce a richer description.",
|
|
4
5
|
"phases": [
|
|
5
6
|
{
|
|
6
7
|
"id": "task_describe",
|
|
@@ -12,7 +13,7 @@
|
|
|
12
13
|
"formId": { "const": "task-describe-source-input" },
|
|
13
14
|
"title": { "const": "Task Describe Source" },
|
|
14
15
|
"description": {
|
|
15
|
-
"const": "
|
|
16
|
+
"const": "Provide a Jira issue key / browse URL, or describe the task manually."
|
|
16
17
|
},
|
|
17
18
|
"submitLabel": { "const": "Generate description" },
|
|
18
19
|
"fields": {
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
"id": "jira_ref",
|
|
23
24
|
"type": "text",
|
|
24
25
|
"label": "Jira issue key or browse URL",
|
|
25
|
-
"help": "
|
|
26
|
+
"help": "Example: DEMO-3288 or https://jira.example.com/browse/DEMO-3288. Leave empty to describe the task manually.",
|
|
26
27
|
"required": false
|
|
27
28
|
}
|
|
28
29
|
},
|
|
@@ -31,11 +32,11 @@
|
|
|
31
32
|
"id": "additional_instructions",
|
|
32
33
|
"type": "text",
|
|
33
34
|
"label": "Additional instructions",
|
|
34
|
-
"help": "
|
|
35
|
+
"help": "Optional instructions for description generation. Example: link to another local repo, related project, constraints, or useful context.",
|
|
35
36
|
"required": false,
|
|
36
37
|
"multiline": true,
|
|
37
38
|
"rows": 4,
|
|
38
|
-
"placeholder": "
|
|
39
|
+
"placeholder": "e.g. see also ../billing-service, API docs in docs/task-templates.md"
|
|
39
40
|
}
|
|
40
41
|
},
|
|
41
42
|
{
|
|
@@ -43,11 +44,11 @@
|
|
|
43
44
|
"id": "task_description",
|
|
44
45
|
"type": "text",
|
|
45
46
|
"label": "Task description",
|
|
46
|
-
"help": "
|
|
47
|
+
"help": "Briefly describe the task in your own words. Fill this field only if Jira is not used.",
|
|
47
48
|
"required": false,
|
|
48
49
|
"multiline": true,
|
|
49
50
|
"rows": 3,
|
|
50
|
-
"placeholder": "
|
|
51
|
+
"placeholder": "e.g. add a status filter to the order list and persist the selected value in the URL"
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
]
|
|
@@ -234,10 +235,37 @@
|
|
|
234
235
|
}
|
|
235
236
|
},
|
|
236
237
|
"message": "Task describe mode did not produce the Jira description artifact."
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"id": "ensure_task_describe_json_from_jira_markdown",
|
|
243
|
+
"node": "ensure-summary-json",
|
|
244
|
+
"when": {
|
|
245
|
+
"all": [
|
|
246
|
+
{ "ref": "steps.task_describe.collect_task_source.value.values.jira_ref" },
|
|
247
|
+
{ "not": { "ref": "context.dryRun" } }
|
|
248
|
+
]
|
|
249
|
+
},
|
|
250
|
+
"params": {
|
|
251
|
+
"markdownFile": {
|
|
252
|
+
"artifact": {
|
|
253
|
+
"kind": "jira-description-file",
|
|
254
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
255
|
+
"iteration": { "ref": "params.iteration" }
|
|
256
|
+
}
|
|
237
257
|
},
|
|
258
|
+
"outputFile": {
|
|
259
|
+
"artifact": {
|
|
260
|
+
"kind": "jira-description-json-file",
|
|
261
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
262
|
+
"iteration": { "ref": "params.iteration" }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
"expect": [
|
|
238
267
|
{
|
|
239
268
|
"kind": "require-structured-artifacts",
|
|
240
|
-
"when": { "not": { "ref": "context.dryRun" } },
|
|
241
269
|
"items": [
|
|
242
270
|
{
|
|
243
271
|
"path": {
|
|
@@ -329,10 +357,41 @@
|
|
|
329
357
|
}
|
|
330
358
|
},
|
|
331
359
|
"message": "Task describe mode did not produce the Jira description artifact."
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"id": "ensure_task_describe_json_from_input_markdown",
|
|
365
|
+
"node": "ensure-summary-json",
|
|
366
|
+
"when": {
|
|
367
|
+
"all": [
|
|
368
|
+
{
|
|
369
|
+
"not": {
|
|
370
|
+
"ref": "steps.task_describe.collect_task_source.value.values.jira_ref"
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
{ "not": { "ref": "context.dryRun" } }
|
|
374
|
+
]
|
|
375
|
+
},
|
|
376
|
+
"params": {
|
|
377
|
+
"markdownFile": {
|
|
378
|
+
"artifact": {
|
|
379
|
+
"kind": "jira-description-file",
|
|
380
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
381
|
+
"iteration": { "ref": "params.iteration" }
|
|
382
|
+
}
|
|
332
383
|
},
|
|
384
|
+
"outputFile": {
|
|
385
|
+
"artifact": {
|
|
386
|
+
"kind": "jira-description-json-file",
|
|
387
|
+
"taskKey": { "ref": "params.taskKey" },
|
|
388
|
+
"iteration": { "ref": "params.iteration" }
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
"expect": [
|
|
333
393
|
{
|
|
334
394
|
"kind": "require-structured-artifacts",
|
|
335
|
-
"when": { "not": { "ref": "context.dryRun" } },
|
|
336
395
|
"items": [
|
|
337
396
|
{
|
|
338
397
|
"path": {
|
|
@@ -3,6 +3,7 @@ import { buildReviewFixPromptNode } from "./nodes/build-review-fix-prompt-node.j
|
|
|
3
3
|
import { codexPromptNode } from "./nodes/codex-prompt-node.js";
|
|
4
4
|
import { commandCheckNode } from "./nodes/command-check-node.js";
|
|
5
5
|
import { commitMessageFormNode } from "./nodes/commit-message-form-node.js";
|
|
6
|
+
import { ensureSummaryJsonNode } from "./nodes/ensure-summary-json-node.js";
|
|
6
7
|
import { fetchGitLabDiffNode } from "./nodes/fetch-gitlab-diff-node.js";
|
|
7
8
|
import { fetchGitLabReviewNode } from "./nodes/fetch-gitlab-review-node.js";
|
|
8
9
|
import { fileCheckNode } from "./nodes/file-check-node.js";
|
|
@@ -32,6 +33,7 @@ const builtInNodes = {
|
|
|
32
33
|
"codex-prompt": codexPromptNode,
|
|
33
34
|
"command-check": commandCheckNode,
|
|
34
35
|
"commit-message-form": commitMessageFormNode,
|
|
36
|
+
"ensure-summary-json": ensureSummaryJsonNode,
|
|
35
37
|
"fetch-gitlab-diff": fetchGitLabDiffNode,
|
|
36
38
|
"fetch-gitlab-review": fetchGitLabReviewNode,
|
|
37
39
|
"file-check": fileCheckNode,
|
|
@@ -90,6 +92,12 @@ const builtInNodeMetadata = {
|
|
|
90
92
|
prompt: "forbidden",
|
|
91
93
|
requiredParams: ["commitMessageFile", "formId", "title", "outputFile"],
|
|
92
94
|
},
|
|
95
|
+
"ensure-summary-json": {
|
|
96
|
+
kind: "ensure-summary-json",
|
|
97
|
+
version: 1,
|
|
98
|
+
prompt: "forbidden",
|
|
99
|
+
requiredParams: ["markdownFile", "outputFile"],
|
|
100
|
+
},
|
|
93
101
|
"fetch-gitlab-diff": {
|
|
94
102
|
kind: "fetch-gitlab-diff",
|
|
95
103
|
version: 1,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { TaskRunnerError } from "../../errors.js";
|
|
4
|
+
function toSummaryText(markdown) {
|
|
5
|
+
return markdown
|
|
6
|
+
.replace(/\r\n/g, "\n")
|
|
7
|
+
.replace(/^#{1,6}\s+/gm, "")
|
|
8
|
+
.replace(/^\s*[-*+]\s+/gm, "- ")
|
|
9
|
+
.replace(/`([^`]*)`/g, "$1")
|
|
10
|
+
.replace(/\*\*(.*?)\*\*/g, "$1")
|
|
11
|
+
.replace(/__(.*?)__/g, "$1")
|
|
12
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
13
|
+
.trim();
|
|
14
|
+
}
|
|
15
|
+
function hasValidSummaryArtifact(outputFile) {
|
|
16
|
+
if (!existsSync(outputFile)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(readFileSync(outputFile, "utf8"));
|
|
21
|
+
return typeof parsed.summary === "string" && parsed.summary.trim().length > 0;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export const ensureSummaryJsonNode = {
|
|
28
|
+
kind: "ensure-summary-json",
|
|
29
|
+
version: 1,
|
|
30
|
+
async run(_context, params) {
|
|
31
|
+
if (hasValidSummaryArtifact(params.outputFile)) {
|
|
32
|
+
return {
|
|
33
|
+
value: {
|
|
34
|
+
outputFile: params.outputFile,
|
|
35
|
+
created: false,
|
|
36
|
+
repaired: false,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (!existsSync(params.markdownFile)) {
|
|
41
|
+
throw new TaskRunnerError(`Cannot create summary JSON ${params.outputFile}: markdown source ${params.markdownFile} was not found.`);
|
|
42
|
+
}
|
|
43
|
+
const summary = toSummaryText(readFileSync(params.markdownFile, "utf8"));
|
|
44
|
+
if (!summary) {
|
|
45
|
+
throw new TaskRunnerError(`Cannot create summary JSON ${params.outputFile}: markdown source ${params.markdownFile} is empty.`);
|
|
46
|
+
}
|
|
47
|
+
mkdirSync(path.dirname(params.outputFile), { recursive: true });
|
|
48
|
+
const repaired = existsSync(params.outputFile);
|
|
49
|
+
writeFileSync(params.outputFile, `${JSON.stringify({ summary }, null, 2)}\n`, "utf8");
|
|
50
|
+
return {
|
|
51
|
+
value: {
|
|
52
|
+
outputFile: params.outputFile,
|
|
53
|
+
created: !repaired,
|
|
54
|
+
repaired,
|
|
55
|
+
},
|
|
56
|
+
outputs: [{ kind: "artifact", path: params.outputFile, required: true }],
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
@@ -6,7 +6,7 @@ export const gitCommitNode = {
|
|
|
6
6
|
async run(context, params) {
|
|
7
7
|
printInfo(params.labelText ?? "Committing changes");
|
|
8
8
|
if (context.dryRun) {
|
|
9
|
-
printInfo("DRY RUN: git commit
|
|
9
|
+
printInfo("DRY RUN: git commit was not executed");
|
|
10
10
|
return {
|
|
11
11
|
value: {
|
|
12
12
|
output: "",
|
|
@@ -187,7 +187,7 @@ export const gitStatusNode = {
|
|
|
187
187
|
});
|
|
188
188
|
const files = parsePorcelain(porcelainOutput);
|
|
189
189
|
if (files.length === 0) {
|
|
190
|
-
throw new TaskRunnerError("
|
|
190
|
+
throw new TaskRunnerError("No changed files to commit.");
|
|
191
191
|
}
|
|
192
192
|
const diff = await context.runtime.runCommand(["git", "diff"], {
|
|
193
193
|
dryRun: context.dryRun,
|
|
@@ -48,8 +48,8 @@ export const reviewFindingsFormNode = {
|
|
|
48
48
|
{
|
|
49
49
|
id: "apply_all",
|
|
50
50
|
type: "boolean",
|
|
51
|
-
label: "
|
|
52
|
-
help: "
|
|
51
|
+
label: "Fix all findings in this iteration",
|
|
52
|
+
help: "If enabled, the selection list below does not limit the scope of fixes.",
|
|
53
53
|
default: selectableFindings.length === 0,
|
|
54
54
|
},
|
|
55
55
|
];
|
|
@@ -57,8 +57,8 @@ export const reviewFindingsFormNode = {
|
|
|
57
57
|
fields.push({
|
|
58
58
|
id: "selected_findings",
|
|
59
59
|
type: "multi-select",
|
|
60
|
-
label: "
|
|
61
|
-
help: "Space
|
|
60
|
+
label: "Which findings to fix now",
|
|
61
|
+
help: "Space toggles an item. If apply_all is off, select at least one finding.",
|
|
62
62
|
options: selectableFindings.map((finding) => ({
|
|
63
63
|
value: finding.title,
|
|
64
64
|
label: `${finding.title} | ${finding.severity || "-"}`,
|
|
@@ -80,17 +80,17 @@ export const reviewFindingsFormNode = {
|
|
|
80
80
|
fields.push({
|
|
81
81
|
id: "extra_notes",
|
|
82
82
|
type: "text",
|
|
83
|
-
label: "
|
|
84
|
-
help: "
|
|
83
|
+
label: "Additional instructions",
|
|
84
|
+
help: "Short comment for this review-fix iteration.",
|
|
85
85
|
default: "",
|
|
86
|
-
placeholder: "
|
|
86
|
+
placeholder: "e.g. fix only blockers",
|
|
87
87
|
});
|
|
88
88
|
return {
|
|
89
89
|
value: {
|
|
90
90
|
formId: params.formId,
|
|
91
91
|
title: params.title,
|
|
92
92
|
...(params.description ? { description: params.description } : {}),
|
|
93
|
-
submitLabel: "
|
|
93
|
+
submitLabel: "Run review-fix",
|
|
94
94
|
fields,
|
|
95
95
|
findingCount: selectableFindings.length,
|
|
96
96
|
},
|
|
@@ -46,8 +46,8 @@ function buildTaskDescribePromptSuffix(params, values) {
|
|
|
46
46
|
].join("\n\n")
|
|
47
47
|
: "",
|
|
48
48
|
summaryText: additionalInstructions
|
|
49
|
-
?
|
|
50
|
-
:
|
|
49
|
+
? `Task source: Jira\nJira: ${jiraRef}\n\nAdditional instructions:\n${additionalInstructions}`
|
|
50
|
+
: `Task source: Jira\nJira: ${jiraRef}`,
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
return {
|
|
@@ -1,8 +1,9 @@
|
|
|
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,
|
|
1
|
+
import { BUG_ANALYZE_PROMPT_TEMPLATE, COMMIT_MESSAGE_PROMPT_TEMPLATE, DESIGN_REVIEW_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_PROMPT_TEMPLATE, PLAN_QUESTIONS_PROMPT_TEMPLATE, PLAN_REVISE_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
|
+
"design-review": DESIGN_REVIEW_PROMPT_TEMPLATE,
|
|
6
7
|
"gitlab-diff-review": GITLAB_DIFF_REVIEW_PROMPT_TEMPLATE,
|
|
7
8
|
"gitlab-review": GITLAB_REVIEW_PROMPT_TEMPLATE,
|
|
8
9
|
implement: IMPLEMENT_PROMPT_TEMPLATE,
|
|
@@ -10,6 +11,7 @@ const promptTemplates = {
|
|
|
10
11
|
"mr-description": MR_DESCRIPTION_PROMPT_TEMPLATE,
|
|
11
12
|
"plan-questions": PLAN_QUESTIONS_PROMPT_TEMPLATE,
|
|
12
13
|
plan: PLAN_PROMPT_TEMPLATE,
|
|
14
|
+
"plan-revise": PLAN_REVISE_PROMPT_TEMPLATE,
|
|
13
15
|
review: REVIEW_PROMPT_TEMPLATE,
|
|
14
16
|
"review-project": REVIEW_PROJECT_PROMPT_TEMPLATE,
|
|
15
17
|
"review-fix": REVIEW_FIX_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, reviewAssessmentFile, reviewAssessmentJsonFile, 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, designReviewFile, designReviewJsonFile, 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) {
|
|
@@ -88,6 +88,16 @@ function resolveArtifact(spec, context) {
|
|
|
88
88
|
return designFile(taskKey, iteration);
|
|
89
89
|
case "design-json-file":
|
|
90
90
|
return designJsonFile(taskKey, iteration);
|
|
91
|
+
case "design-review-file":
|
|
92
|
+
if (iteration === undefined) {
|
|
93
|
+
throw new TaskRunnerError("design-review-file requires iteration");
|
|
94
|
+
}
|
|
95
|
+
return designReviewFile(taskKey, iteration);
|
|
96
|
+
case "design-review-json-file":
|
|
97
|
+
if (iteration === undefined) {
|
|
98
|
+
throw new TaskRunnerError("design-review-json-file requires iteration");
|
|
99
|
+
}
|
|
100
|
+
return designReviewJsonFile(taskKey, iteration);
|
|
91
101
|
case "gitlab-diff-file":
|
|
92
102
|
return gitlabDiffFile(taskKey, iteration);
|
|
93
103
|
case "gitlab-diff-json-file":
|
package/dist/prompts.js
CHANGED
|
@@ -52,13 +52,32 @@ export const MR_DESCRIPTION_PROMPT_TEMPLATE = "Review the task in {jira_task_fil
|
|
|
52
52
|
"Prepare a very brief intent description for the merge request without implementation details, file lists, or technical details. " +
|
|
53
53
|
`First write the source-of-truth JSON to {mr_description_json_file}. ${strictSchemaInstruction("{mr_description_json_file}", "mr-description/v1")}Then write the derivative markdown version to {mr_description_file}. `;
|
|
54
54
|
export const IMPLEMENT_PROMPT_TEMPLATE = "Use only structured artifacts as source of truth. " +
|
|
55
|
-
"Analyze the system design {design_json_file}, implementation plan {plan_json_file}, and proceed with implementation according to
|
|
56
|
-
"
|
|
55
|
+
"Analyze the system design {design_json_file}, implementation plan {plan_json_file}, and QA plan {qa_json_file}, then proceed with implementation according to those artifacts. " +
|
|
56
|
+
"Treat the QA plan as the source of truth for the minimum required test scenarios, edge cases, regression checks, and validation behavior that the implementation must satisfy. " +
|
|
57
|
+
"When the repository contains automated tests, add or update tests for the key scenarios from the QA plan whenever it is practical in the current codebase. " +
|
|
58
|
+
"If some QA scenarios cannot be automated in the current change, still implement the code so those scenarios are satisfied and keep them explicit in your reasoning while editing. " +
|
|
59
|
+
"Markdown artifacts such as {design_file}, {plan_file}, and {qa_file} are intended only for human reading and should not define the implementation. ";
|
|
57
60
|
export const REVIEW_PROMPT_TEMPLATE = "Conduct a code review of the current changes. " +
|
|
58
61
|
"Use only structured artifacts as source of truth: the task in {jira_task_file}, design in {design_json_file}, and plan in {plan_json_file}. " +
|
|
59
62
|
`First write the structured result to {review_json_file}. ${strictSchemaInstruction("{review_json_file}", "review-findings/v1")}` +
|
|
60
63
|
"Then write the derivative markdown version to {review_file}. " +
|
|
61
64
|
"If ready_to_merge=true and there are no blockers preventing merge - create the ready-to-merge.md file.";
|
|
65
|
+
export const DESIGN_REVIEW_PROMPT_TEMPLATE = "Conduct a structured planning critique as a specification critic, not as an implementer. " +
|
|
66
|
+
"Use structured JSON artifacts as the source of truth for semantics. " +
|
|
67
|
+
"Required planning inputs: design markdown {design_file}, design JSON {design_json_file}, implementation plan markdown {plan_file}, implementation plan JSON {plan_json_file}. " +
|
|
68
|
+
"Review the markdown files as derivative human-readable renderings of the same planning run, but do not let markdown override the structured JSON. " +
|
|
69
|
+
"Optional supplemental context is provided through these variables and may contain the literal value 'not provided' when absent: QA markdown {qa_file}, QA JSON {qa_json_file}, Jira task JSON {jira_task_file}, Jira attachments manifest {jira_attachments_manifest_file}, Jira attachments context {jira_attachments_context_file}, planning answers JSON {planning_answers_json_file}. " +
|
|
70
|
+
"When an optional variable is 'not provided', treat that source as unavailable and do not invent details from it. " +
|
|
71
|
+
"Evaluate completeness, consistency, implementation readiness, risk coverage, QA coverage, and scope discipline across the available planning artifacts and optional context. " +
|
|
72
|
+
"Identify blocking findings, major non-blocking findings, warnings, missing information, consistency check results, QA coverage gaps, and concise recommended actions. " +
|
|
73
|
+
"Use exactly one status value: approved, approved_with_warnings, or needs_revision. " +
|
|
74
|
+
"Set status to needs_revision when any blocking finding exists or when required information is missing in a way that blocks safe implementation start. " +
|
|
75
|
+
"Set status to approved_with_warnings when there are no blocking findings, but there are major findings, warnings, non-blocking missing information items, QA coverage gaps, or non-blocking consistency issues. " +
|
|
76
|
+
"Set status to approved only when there are no unresolved blocking findings, major findings, warnings, missing information items, or QA coverage gaps. " +
|
|
77
|
+
`First write the structured design review to {review_json_file}. ${strictSchemaInstruction("{review_json_file}", "design-review/v1")}` +
|
|
78
|
+
"Then write the derivative markdown version to {review_file}. " +
|
|
79
|
+
"Create ready-to-merge.md only when status is approved or approved_with_warnings. " +
|
|
80
|
+
"Do not create ready-to-merge.md when status is needs_revision.";
|
|
62
81
|
export const REVIEW_PROJECT_PROMPT_TEMPLATE = "Conduct a code review of current changes in the project without Jira context. " +
|
|
63
82
|
"Evaluate the quality of changes based on current code, tests, regression risks, and overall engineering quality. " +
|
|
64
83
|
`First write the structured result to {review_json_file}. ${strictSchemaInstruction("{review_json_file}", "review-findings/v1")}` +
|
|
@@ -116,6 +135,34 @@ export const COMMIT_MESSAGE_PROMPT_TEMPLATE = "Generate a commit message for the
|
|
|
116
135
|
"3) Include task key from Jira task. " +
|
|
117
136
|
"4) Commit message language: English. " +
|
|
118
137
|
"5) Write JSON to {commit_message_json_file}: {\"subject\": \"...\"}.";
|
|
138
|
+
export const PLAN_REVISE_PROMPT_TEMPLATE = "Revise the planning artifacts based on the design-review verdict. " +
|
|
139
|
+
"Use structured JSON artifacts as the source of truth for semantics. " +
|
|
140
|
+
"First revise the structured JSON artifacts; only after the JSON is complete and schema-valid should you write the derivative markdown files. " +
|
|
141
|
+
"Markdown must not influence JSON structure or types. " +
|
|
142
|
+
"The design-review verdict JSON {review_json_file} is the primary source of revision instructions — treat its blocking findings, major findings, and recommended actions as mandatory revision targets. " +
|
|
143
|
+
"The design-review markdown {review_file} is a derivative rendering only and must not override the structured verdict. " +
|
|
144
|
+
"Required source planning inputs: design JSON {design_json_file}, design markdown {design_file}, plan JSON {plan_json_file}, plan markdown {plan_file}. " +
|
|
145
|
+
"Optional source QA inputs (may be 'not provided'): QA JSON {qa_json_file}, QA markdown {qa_file}. " +
|
|
146
|
+
"When QA inputs are 'not provided', synthesize a new QA plan from the revised design and plan. " +
|
|
147
|
+
"Optional supplemental context (may be 'not provided'): Jira task JSON {jira_task_file}, Jira attachments manifest {jira_attachments_manifest_file}, Jira attachments context {jira_attachments_context_file}, planning answers JSON {planning_answers_json_file}. " +
|
|
148
|
+
"When an optional variable is 'not provided', treat that source as unavailable and do not invent details from it. " +
|
|
149
|
+
"For every blocking finding and major finding in the verdict, address it directly in the revised artifacts. " +
|
|
150
|
+
"Preserve all content from the original artifacts that is not directly addressed by findings in the verdict — do not drop details, sections, or decisions that remain valid. " +
|
|
151
|
+
"Preserve semantics, not source formatting conventions from the verdict: do not copy verdict-style nested objects into fields whose schemas require plain strings. " +
|
|
152
|
+
"For implementation-design/v1 specifically, goals, non_goals, components, current_state, target_state, business_rules, migration_strategy, database_changes, api_changes, risks, acceptance_criteria, and open_questions must remain arrays of non-empty strings. " +
|
|
153
|
+
"Only affected_code and decisions may contain nested objects in implementation-design/v1. " +
|
|
154
|
+
"If you need to preserve extra detail such as mitigation, resolution, or answer text for a string-array field, fold that detail into a single English sentence string instead of creating an object. " +
|
|
155
|
+
"Produce the following revised outputs: " +
|
|
156
|
+
`revised design JSON to {revised_design_json_file}. ${strictSchemaInstruction("{revised_design_json_file}", "implementation-design/v1")}` +
|
|
157
|
+
"Revised design markdown to {revised_design_file}. " +
|
|
158
|
+
`revised plan JSON to {revised_plan_json_file}. ${strictSchemaInstruction("{revised_plan_json_file}", "implementation-plan/v1")}` +
|
|
159
|
+
"Revised plan markdown to {revised_plan_file}. " +
|
|
160
|
+
`revised QA JSON to {revised_qa_json_file}. ${strictSchemaInstruction("{revised_qa_json_file}", "qa-plan/v1")}` +
|
|
161
|
+
"Revised QA markdown to {revised_qa_file}. " +
|
|
162
|
+
"Create ready-to-merge.md only when all blocking findings from the verdict have been addressed in the revised artifacts. " +
|
|
163
|
+
"Do not create ready-to-merge.md if any blocking finding remains unresolved. " +
|
|
164
|
+
"JSON files must be valid and contain only JSON without markdown wrapping. " +
|
|
165
|
+
"Markdown files must be comprehensive derivative representations of the corresponding JSON artifacts.";
|
|
119
166
|
export const AUTO_REVIEW_FIX_EXTRA_PROMPT = "Fix only blockers, criticals, and important issues";
|
|
120
167
|
export function formatTemplate(template, values) {
|
|
121
168
|
let result = template;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { designFile, designJsonFile, jiraAttachmentsContextFile, jiraAttachmentsManifestFile, jiraTaskFile, latestArtifactIteration, planFile, planJsonFile, planningAnswersJsonFile, qaFile, qaJsonFile, requireArtifacts, } from "../artifacts.js";
|
|
3
|
+
import { TaskRunnerError } from "../errors.js";
|
|
4
|
+
import { validateStructuredArtifacts } from "../structured-artifacts.js";
|
|
5
|
+
const OPTIONAL_INPUT_NOT_PROVIDED = "not provided";
|
|
6
|
+
function requiredPlanningArtifactPaths(taskKey, iteration) {
|
|
7
|
+
return {
|
|
8
|
+
designFile: designFile(taskKey, iteration),
|
|
9
|
+
designJsonFile: designJsonFile(taskKey, iteration),
|
|
10
|
+
planFile: planFile(taskKey, iteration),
|
|
11
|
+
planJsonFile: planJsonFile(taskKey, iteration),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function resolveLatestCompletedPlanningIteration(taskKey) {
|
|
15
|
+
const latestKnownIteration = Math.max(latestArtifactIteration(taskKey, "design", "md") ?? 0, latestArtifactIteration(taskKey, "design", "json") ?? 0, latestArtifactIteration(taskKey, "plan", "md") ?? 0, latestArtifactIteration(taskKey, "plan", "json") ?? 0);
|
|
16
|
+
for (let iteration = latestKnownIteration; iteration >= 1; iteration -= 1) {
|
|
17
|
+
const requiredPaths = Object.values(requiredPlanningArtifactPaths(taskKey, iteration));
|
|
18
|
+
if (requiredPaths.every((candidate) => existsSync(candidate))) {
|
|
19
|
+
return iteration;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const fallbackIteration = latestKnownIteration || 1;
|
|
23
|
+
const fallbackPaths = Object.values(requiredPlanningArtifactPaths(taskKey, fallbackIteration));
|
|
24
|
+
requireArtifacts(fallbackPaths, "Design-review requires design and plan markdown/JSON artifacts from the latest completed planning run.");
|
|
25
|
+
throw new TaskRunnerError("Unreachable design-review planning artifact resolution state.");
|
|
26
|
+
}
|
|
27
|
+
function resolveOptionalPromptFile(filePath) {
|
|
28
|
+
if (!existsSync(filePath)) {
|
|
29
|
+
return {
|
|
30
|
+
present: false,
|
|
31
|
+
path: null,
|
|
32
|
+
promptValue: OPTIONAL_INPUT_NOT_PROVIDED,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
present: true,
|
|
37
|
+
path: filePath,
|
|
38
|
+
promptValue: filePath,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function resolveOptionalValidatedStructuredFile(filePath, schemaId, message) {
|
|
42
|
+
const resolved = resolveOptionalPromptFile(filePath);
|
|
43
|
+
if (!resolved.present) {
|
|
44
|
+
return resolved;
|
|
45
|
+
}
|
|
46
|
+
validateStructuredArtifacts([{ path: filePath, schemaId }], message);
|
|
47
|
+
return resolved;
|
|
48
|
+
}
|
|
49
|
+
function resolveOptionalQaPair(taskKey, iteration) {
|
|
50
|
+
const markdownPath = qaFile(taskKey, iteration);
|
|
51
|
+
const jsonPath = qaJsonFile(taskKey, iteration);
|
|
52
|
+
const markdownExists = existsSync(markdownPath);
|
|
53
|
+
const jsonExists = existsSync(jsonPath);
|
|
54
|
+
if (!markdownExists && !jsonExists) {
|
|
55
|
+
return {
|
|
56
|
+
hasQaArtifacts: false,
|
|
57
|
+
qaFilePath: null,
|
|
58
|
+
qaJsonFilePath: null,
|
|
59
|
+
qaFile: OPTIONAL_INPUT_NOT_PROVIDED,
|
|
60
|
+
qaJsonFile: OPTIONAL_INPUT_NOT_PROVIDED,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (!markdownExists || !jsonExists) {
|
|
64
|
+
requireArtifacts([markdownPath, jsonPath], "Design-review accepts QA artifacts only as a complete markdown/JSON pair for the selected planning iteration.");
|
|
65
|
+
}
|
|
66
|
+
validateStructuredArtifacts([{ path: jsonPath, schemaId: "qa-plan/v1" }], "Design-review QA structured artifact is invalid.");
|
|
67
|
+
return {
|
|
68
|
+
hasQaArtifacts: true,
|
|
69
|
+
qaFilePath: markdownPath,
|
|
70
|
+
qaJsonFilePath: jsonPath,
|
|
71
|
+
qaFile: markdownPath,
|
|
72
|
+
qaJsonFile: jsonPath,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolves the full design-review input contract from artifacts already present in the task scope.
|
|
77
|
+
* Required planning artifacts must come from one completed planning iteration. Optional contextual
|
|
78
|
+
* inputs are exposed as stable prompt values and explicit presence flags so the flow can remain
|
|
79
|
+
* deterministic when some context is absent.
|
|
80
|
+
*/
|
|
81
|
+
export function resolveDesignReviewInputContract(taskKey) {
|
|
82
|
+
const planningIteration = resolveLatestCompletedPlanningIteration(taskKey);
|
|
83
|
+
const requiredArtifacts = requiredPlanningArtifactPaths(taskKey, planningIteration);
|
|
84
|
+
requireArtifacts(Object.values(requiredArtifacts), "Design-review requires design and plan markdown/JSON artifacts from the latest completed planning run.");
|
|
85
|
+
validateStructuredArtifacts([
|
|
86
|
+
{ path: requiredArtifacts.designJsonFile, schemaId: "implementation-design/v1" },
|
|
87
|
+
{ path: requiredArtifacts.planJsonFile, schemaId: "implementation-plan/v1" },
|
|
88
|
+
], "Design-review required planning structured artifacts are invalid.");
|
|
89
|
+
const qaArtifacts = resolveOptionalQaPair(taskKey, planningIteration);
|
|
90
|
+
const jiraTask = resolveOptionalPromptFile(jiraTaskFile(taskKey));
|
|
91
|
+
const jiraAttachmentsManifest = resolveOptionalPromptFile(jiraAttachmentsManifestFile(taskKey));
|
|
92
|
+
const jiraAttachmentsContext = resolveOptionalPromptFile(jiraAttachmentsContextFile(taskKey));
|
|
93
|
+
const planningAnswers = resolveOptionalValidatedStructuredFile(planningAnswersJsonFile(taskKey), "user-input/v1", "Design-review planning answers structured artifact is invalid.");
|
|
94
|
+
return {
|
|
95
|
+
planningIteration,
|
|
96
|
+
...requiredArtifacts,
|
|
97
|
+
...qaArtifacts,
|
|
98
|
+
hasJiraTaskFile: jiraTask.present,
|
|
99
|
+
jiraTaskFilePath: jiraTask.path,
|
|
100
|
+
jiraTaskFile: jiraTask.promptValue,
|
|
101
|
+
hasJiraAttachmentsManifestFile: jiraAttachmentsManifest.present,
|
|
102
|
+
jiraAttachmentsManifestFilePath: jiraAttachmentsManifest.path,
|
|
103
|
+
jiraAttachmentsManifestFile: jiraAttachmentsManifest.promptValue,
|
|
104
|
+
hasJiraAttachmentsContextFile: jiraAttachmentsContext.present,
|
|
105
|
+
jiraAttachmentsContextFilePath: jiraAttachmentsContext.path,
|
|
106
|
+
jiraAttachmentsContextFile: jiraAttachmentsContext.promptValue,
|
|
107
|
+
hasPlanningAnswersJsonFile: planningAnswers.present,
|
|
108
|
+
planningAnswersJsonFilePath: planningAnswers.path,
|
|
109
|
+
planningAnswersJsonFile: planningAnswers.promptValue,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export { OPTIONAL_INPUT_NOT_PROVIDED };
|