agentweaver 0.1.15 → 0.1.17
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 +76 -19
- package/dist/artifact-manifest.js +219 -0
- package/dist/artifacts.js +88 -3
- package/dist/doctor/checks/env-diagnostics.js +25 -0
- package/dist/doctor/checks/executors.js +2 -2
- package/dist/doctor/checks/flow-readiness.js +15 -18
- package/dist/flow-state.js +212 -15
- package/dist/index.js +539 -209
- package/dist/interactive/blessed-session.js +361 -0
- package/dist/interactive/controller.js +1326 -0
- package/dist/interactive/create-interactive-session.js +5 -0
- package/dist/interactive/ink/index.js +597 -0
- package/dist/interactive/progress.js +245 -0
- package/dist/interactive/selectors.js +14 -0
- package/dist/interactive/session.js +1 -0
- package/dist/interactive/state.js +34 -0
- package/dist/interactive/tree.js +155 -0
- package/dist/interactive/types.js +1 -0
- package/dist/interactive/view-model.js +1 -0
- package/dist/interactive-ui.js +159 -194
- package/dist/pipeline/auto-flow.js +9 -6
- package/dist/pipeline/context.js +7 -5
- package/dist/pipeline/declarative-flow-runner.js +212 -6
- package/dist/pipeline/declarative-flows.js +63 -17
- package/dist/pipeline/execution-routing-config.js +15 -0
- package/dist/pipeline/flow-catalog.js +50 -12
- package/dist/pipeline/flow-run-resume.js +29 -0
- package/dist/pipeline/flow-specs/auto-common.json +90 -360
- package/dist/pipeline/flow-specs/auto-golang.json +81 -360
- package/dist/pipeline/flow-specs/auto-simple.json +141 -0
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +2 -0
- package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
- package/dist/pipeline/flow-specs/design-review/design-review-loop.json +316 -0
- package/dist/pipeline/flow-specs/design-review.json +10 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +11 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +2 -0
- package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
- package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +2 -0
- package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +2 -0
- package/dist/pipeline/flow-specs/implement.json +13 -6
- package/dist/pipeline/flow-specs/instant-task.json +177 -0
- package/dist/pipeline/flow-specs/normalize-task-source.json +311 -0
- package/dist/pipeline/flow-specs/plan-revise.json +7 -1
- package/dist/pipeline/flow-specs/plan.json +51 -71
- package/dist/pipeline/flow-specs/review/review-fix.json +24 -4
- package/dist/pipeline/flow-specs/review/review-loop.json +351 -45
- package/dist/pipeline/flow-specs/review/review-project-loop.json +590 -0
- package/dist/pipeline/flow-specs/review/review-project.json +12 -0
- package/dist/pipeline/flow-specs/review/review.json +37 -31
- package/dist/pipeline/flow-specs/task-describe.json +2 -0
- package/dist/pipeline/flow-specs/task-source/jira-fetch.json +70 -0
- package/dist/pipeline/flow-specs/task-source/manual-input.json +216 -0
- package/dist/pipeline/launch-profile-config.js +30 -18
- package/dist/pipeline/node-contract.js +1 -0
- package/dist/pipeline/node-registry.js +115 -6
- package/dist/pipeline/node-runner.js +3 -2
- package/dist/pipeline/nodes/build-review-fix-prompt-node.js +5 -1
- package/dist/pipeline/nodes/clear-ready-to-merge-node.js +11 -0
- package/dist/pipeline/nodes/commit-message-form-node.js +8 -0
- package/dist/pipeline/nodes/design-review-verdict-node.js +36 -0
- package/dist/pipeline/nodes/ensure-summary-json-node.js +13 -2
- package/dist/pipeline/nodes/fetch-gitlab-diff-node.js +19 -2
- package/dist/pipeline/nodes/fetch-gitlab-review-node.js +19 -2
- package/dist/pipeline/nodes/flow-run-node.js +242 -8
- package/dist/pipeline/nodes/git-commit-form-node.js +8 -0
- package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +19 -2
- package/dist/pipeline/nodes/jira-fetch-node.js +50 -4
- package/dist/pipeline/nodes/llm-prompt-node.js +38 -36
- package/dist/pipeline/nodes/planning-bundle-node.js +10 -0
- package/dist/pipeline/nodes/review-verdict-node.js +86 -0
- package/dist/pipeline/nodes/select-files-form-node.js +8 -0
- package/dist/pipeline/nodes/structured-summary-node.js +24 -0
- package/dist/pipeline/nodes/user-input-node.js +38 -3
- package/dist/pipeline/nodes/write-selection-file-node.js +20 -4
- package/dist/pipeline/plugin-loader.js +389 -0
- package/dist/pipeline/plugin-types.js +1 -0
- package/dist/pipeline/prompt-registry.js +3 -1
- package/dist/pipeline/prompt-runtime.js +4 -1
- package/dist/pipeline/registry.js +71 -4
- package/dist/pipeline/review-iteration.js +26 -0
- package/dist/pipeline/spec-compiler.js +3 -0
- package/dist/pipeline/spec-loader.js +14 -0
- package/dist/pipeline/spec-types.js +3 -0
- package/dist/pipeline/spec-validator.js +20 -0
- package/dist/pipeline/value-resolver.js +76 -2
- package/dist/plugin-sdk.js +1 -0
- package/dist/prompts.js +36 -14
- package/dist/review-severity.js +45 -0
- package/dist/runtime/artifact-registry.js +405 -0
- package/dist/runtime/design-review-input-contract.js +17 -16
- package/dist/runtime/env-loader.js +3 -0
- package/dist/runtime/execution-routing-store.js +134 -0
- package/dist/runtime/execution-routing.js +233 -0
- package/dist/runtime/interactive-execution-routing.js +471 -0
- package/dist/runtime/plan-revise-input-contract.js +35 -32
- package/dist/runtime/planning-bundle.js +123 -0
- package/dist/runtime/ready-to-merge.js +22 -1
- package/dist/runtime/review-input-contract.js +100 -0
- package/dist/structured-artifact-schema-registry.js +9 -0
- package/dist/structured-artifact-schemas.json +140 -1
- package/dist/structured-artifacts.js +77 -6
- package/dist/user-input.js +70 -3
- package/docs/example/.flows/examples/claude-example.json +50 -0
- package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
- package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
- package/docs/examples/.flows/claude-example.json +50 -0
- package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
- package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
- package/docs/plugin-sdk.md +731 -0
- package/package.json +11 -4
|
@@ -2,50 +2,52 @@ import { TaskRunnerError } from "../../errors.js";
|
|
|
2
2
|
import { printInfo, printPrompt } from "../../tui.js";
|
|
3
3
|
import { isAllowedModelForExecutor, isLlmExecutorId, } from "../launch-profile-config.js";
|
|
4
4
|
import { toExecutorContext } from "../types.js";
|
|
5
|
+
function outputsForArtifacts(requiredArtifacts) {
|
|
6
|
+
return Array.from(new Set(requiredArtifacts ?? [])).map((outputPath) => ({
|
|
7
|
+
kind: "artifact",
|
|
8
|
+
path: outputPath,
|
|
9
|
+
required: true,
|
|
10
|
+
manifest: {
|
|
11
|
+
publish: true,
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
5
15
|
export const llmPromptNode = {
|
|
6
16
|
kind: "llm-prompt",
|
|
7
17
|
version: 1,
|
|
8
18
|
async run(context, params) {
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
const routedProfile = params.routingGroup
|
|
20
|
+
? context.executionRouting?.groups[params.routingGroup]
|
|
21
|
+
: undefined;
|
|
22
|
+
const fallbackProfile = context.executionRouting?.defaultRoute;
|
|
23
|
+
const executor = params.routingGroup
|
|
24
|
+
? routedProfile?.executor ?? params.executor ?? fallbackProfile?.executor
|
|
25
|
+
: params.executor ?? fallbackProfile?.executor;
|
|
26
|
+
const model = params.routingGroup
|
|
27
|
+
? routedProfile?.model ?? params.model ?? fallbackProfile?.model
|
|
28
|
+
: params.model ?? fallbackProfile?.model;
|
|
29
|
+
if (!executor || !isLlmExecutorId(executor, context.executors)) {
|
|
30
|
+
throw new TaskRunnerError(`Unsupported llm executor '${String(executor ?? params.executor ?? "undefined")}'.`);
|
|
11
31
|
}
|
|
12
|
-
if (
|
|
13
|
-
throw new TaskRunnerError(`Model '${
|
|
32
|
+
if (model && !isAllowedModelForExecutor(executor, model, context.executors)) {
|
|
33
|
+
throw new TaskRunnerError(`Model '${model}' is not allowed for executor '${executor}'.`);
|
|
14
34
|
}
|
|
15
35
|
printInfo(params.labelText);
|
|
16
|
-
printPrompt(`LLM:${
|
|
36
|
+
printPrompt(`LLM:${executor}`, params.prompt);
|
|
17
37
|
const executorContext = toExecutorContext(context);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
value
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
if (params.executor === "opencode") {
|
|
34
|
-
const executor = context.executors.get("opencode");
|
|
35
|
-
const value = await executor.execute(executorContext, {
|
|
36
|
-
prompt: params.prompt,
|
|
37
|
-
...(params.model ? { model: params.model } : {}),
|
|
38
|
-
env: { ...context.env },
|
|
39
|
-
}, executor.defaultConfig);
|
|
40
|
-
return {
|
|
41
|
-
value: {
|
|
42
|
-
...value,
|
|
43
|
-
executor: "opencode",
|
|
44
|
-
},
|
|
45
|
-
outputs: (params.requiredArtifacts ?? []).map((path) => ({ kind: "artifact", path, required: true })),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
throw new TaskRunnerError(`Unsupported llm executor '${params.executor}'.`);
|
|
38
|
+
const resolvedExecutor = context.executors.get(executor);
|
|
39
|
+
const value = await resolvedExecutor.execute(executorContext, {
|
|
40
|
+
prompt: params.prompt,
|
|
41
|
+
...(model ? { model } : {}),
|
|
42
|
+
env: { ...context.env },
|
|
43
|
+
}, resolvedExecutor.defaultConfig);
|
|
44
|
+
return {
|
|
45
|
+
value: {
|
|
46
|
+
...value,
|
|
47
|
+
executor,
|
|
48
|
+
},
|
|
49
|
+
outputs: outputsForArtifacts(params.requiredArtifacts),
|
|
50
|
+
};
|
|
49
51
|
},
|
|
50
52
|
checks(_context, params) {
|
|
51
53
|
if (!params.requiredArtifacts || params.requiredArtifacts.length === 0) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { resolveLatestPlanningBundle } from "../../runtime/planning-bundle.js";
|
|
2
|
+
export const planningBundleNode = {
|
|
3
|
+
kind: "planning-bundle",
|
|
4
|
+
version: 1,
|
|
5
|
+
async run(_context, params) {
|
|
6
|
+
return {
|
|
7
|
+
value: resolveLatestPlanningBundle(params.taskKey),
|
|
8
|
+
};
|
|
9
|
+
},
|
|
10
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { reviewJsonFile, latestArtifactIteration, readyToMergeFile } from "../../artifacts.js";
|
|
3
|
+
import { TaskRunnerError } from "../../errors.js";
|
|
4
|
+
import { resolveBlockingReviewSeverities, normalizeReviewSeverity } from "../../review-severity.js";
|
|
5
|
+
import { validateStructuredArtifacts } from "../../structured-artifacts.js";
|
|
6
|
+
import { clearReadyToMergeFile, writeReadyToMergeFile } from "../../runtime/ready-to-merge.js";
|
|
7
|
+
function readReviewVerdictFile(path) {
|
|
8
|
+
if (!existsSync(path)) {
|
|
9
|
+
throw new TaskRunnerError(`Review findings file not found: ${path}`);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new TaskRunnerError(`Failed to parse review findings JSON: ${path}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export const reviewVerdictNode = {
|
|
19
|
+
kind: "review-verdict",
|
|
20
|
+
version: 1,
|
|
21
|
+
async run(context, params) {
|
|
22
|
+
const iteration = params.iteration ?? latestArtifactIteration(params.taskKey, "review", "json") ?? 1;
|
|
23
|
+
const jsonPath = reviewJsonFile(params.taskKey, iteration);
|
|
24
|
+
validateStructuredArtifacts([{ path: jsonPath, schemaId: "review-findings/v1" }], "Review findings are invalid or missing.");
|
|
25
|
+
const report = readReviewVerdictFile(jsonPath);
|
|
26
|
+
const findings = Array.isArray(report.findings) ? report.findings : [];
|
|
27
|
+
const blockingSeverities = resolveBlockingReviewSeverities(params.blockingSeverities);
|
|
28
|
+
const blockingSeveritySet = new Set(blockingSeverities);
|
|
29
|
+
const blockingFindingTitles = findings
|
|
30
|
+
.filter((finding) => {
|
|
31
|
+
const severity = normalizeReviewSeverity(finding.severity);
|
|
32
|
+
return severity !== null && blockingSeveritySet.has(severity);
|
|
33
|
+
})
|
|
34
|
+
.map((finding) => (typeof finding.title === "string" ? finding.title.trim() : ""))
|
|
35
|
+
.filter((title) => title.length > 0);
|
|
36
|
+
const readyToMerge = blockingFindingTitles.length === 0;
|
|
37
|
+
const summary = typeof report.summary === "string" && report.summary.trim().length > 0
|
|
38
|
+
? report.summary.trim()
|
|
39
|
+
: readyToMerge
|
|
40
|
+
? "No blocking findings."
|
|
41
|
+
: `Blocking findings: ${blockingFindingTitles.join(", ")}`;
|
|
42
|
+
const normalizedReport = {
|
|
43
|
+
...report,
|
|
44
|
+
ready_to_merge: readyToMerge,
|
|
45
|
+
};
|
|
46
|
+
writeFileSync(jsonPath, `${JSON.stringify(normalizedReport, null, 2)}\n`, "utf8");
|
|
47
|
+
if (readyToMerge) {
|
|
48
|
+
writeReadyToMergeFile(params.taskKey, {
|
|
49
|
+
...(context.mdLang !== undefined ? { mdLang: context.mdLang } : {}),
|
|
50
|
+
summary,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
clearReadyToMergeFile(params.taskKey);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
value: {
|
|
58
|
+
readyToMerge,
|
|
59
|
+
blockingSeverities,
|
|
60
|
+
blockingFindingTitles,
|
|
61
|
+
blockingFindingsCount: blockingFindingTitles.length,
|
|
62
|
+
summary,
|
|
63
|
+
reviewJsonFile: jsonPath,
|
|
64
|
+
},
|
|
65
|
+
outputs: [
|
|
66
|
+
{
|
|
67
|
+
kind: "artifact",
|
|
68
|
+
path: jsonPath,
|
|
69
|
+
required: true,
|
|
70
|
+
manifest: {
|
|
71
|
+
publish: true,
|
|
72
|
+
schemaId: "review-findings/v1",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
kind: "file",
|
|
77
|
+
path: readyToMergeFile(params.taskKey),
|
|
78
|
+
required: false,
|
|
79
|
+
manifest: {
|
|
80
|
+
publish: true,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
|
|
3
4
|
import { TaskRunnerError } from "../../errors.js";
|
|
4
5
|
import { validateUserInputValues } from "../../user-input.js";
|
|
5
6
|
export const selectFilesFormNode = {
|
|
@@ -65,6 +66,13 @@ export const selectFilesFormNode = {
|
|
|
65
66
|
kind: "artifact",
|
|
66
67
|
path: params.outputFile,
|
|
67
68
|
required: true,
|
|
69
|
+
manifest: {
|
|
70
|
+
publish: true,
|
|
71
|
+
logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
|
|
72
|
+
payloadFamily: "structured-json",
|
|
73
|
+
schemaId: "user-input/v1",
|
|
74
|
+
schemaVersion: 1,
|
|
75
|
+
},
|
|
68
76
|
},
|
|
69
77
|
],
|
|
70
78
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { TaskRunnerError } from "../../errors.js";
|
|
3
|
+
export const structuredSummaryNode = {
|
|
4
|
+
kind: "structured-summary",
|
|
5
|
+
version: 1,
|
|
6
|
+
async run(_context, params) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = JSON.parse(readFileSync(params.path, "utf8"));
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
throw new TaskRunnerError(`Structured summary node could not parse JSON from ${params.path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13
|
+
}
|
|
14
|
+
const summary = typeof parsed.summary === "string" ? parsed.summary.trim() : "";
|
|
15
|
+
if (!summary) {
|
|
16
|
+
throw new TaskRunnerError(`Structured summary node did not find a non-empty summary in ${params.path}.`);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
value: {
|
|
20
|
+
summary,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { TaskRunnerError } from "../../errors.js";
|
|
3
|
+
import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
|
|
3
4
|
import { printSummary } from "../../tui.js";
|
|
4
|
-
import { requestUserInputInTerminal, validateUserInputValues, } from "../../user-input.js";
|
|
5
|
+
import { applyInitialUserInputValues, requestUserInputInTerminal, validateUserInputValues, } from "../../user-input.js";
|
|
5
6
|
function labelForSingleValue(field, value) {
|
|
6
7
|
if (field.type !== "single-select" && field.type !== "multi-select") {
|
|
7
8
|
return value;
|
|
@@ -64,6 +65,23 @@ function buildTaskDescribePromptSuffix(params, values) {
|
|
|
64
65
|
: `Task source: user-input\n\n${taskDescription}`,
|
|
65
66
|
};
|
|
66
67
|
}
|
|
68
|
+
function buildInstantTaskPromptSuffix(params, values) {
|
|
69
|
+
const taskDescription = typeof values.task_description === "string" ? values.task_description.trim() : "";
|
|
70
|
+
const additionalInstructions = typeof values.additional_instructions === "string" ? values.additional_instructions.trim() : "";
|
|
71
|
+
return {
|
|
72
|
+
promptSuffix: [
|
|
73
|
+
"Use the manual instant-task request below as the source of truth for task intent.",
|
|
74
|
+
`User input file: ${params.outputFile}`,
|
|
75
|
+
`Task description:\n${taskDescription}`,
|
|
76
|
+
additionalInstructions ? `Additional instructions:\n${additionalInstructions}` : "",
|
|
77
|
+
]
|
|
78
|
+
.filter((item) => item.trim().length > 0)
|
|
79
|
+
.join("\n\n"),
|
|
80
|
+
summaryText: additionalInstructions
|
|
81
|
+
? `Task source: instant-task\n\n${taskDescription}\n\nAdditional instructions:\n${additionalInstructions}`
|
|
82
|
+
: `Task source: instant-task\n\n${taskDescription}`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
67
85
|
function buildPromptSuffix(params, values) {
|
|
68
86
|
if (params.formId === "review-fix-selection") {
|
|
69
87
|
return buildReviewFixPromptSuffix(params, values);
|
|
@@ -71,6 +89,9 @@ function buildPromptSuffix(params, values) {
|
|
|
71
89
|
if (params.formId === "task-describe-source-input") {
|
|
72
90
|
return buildTaskDescribePromptSuffix(params, values);
|
|
73
91
|
}
|
|
92
|
+
if (params.formId === "instant-task-input") {
|
|
93
|
+
return buildInstantTaskPromptSuffix(params, values);
|
|
94
|
+
}
|
|
74
95
|
if (params.fields.length === 0) {
|
|
75
96
|
return {
|
|
76
97
|
promptSuffix: "",
|
|
@@ -101,12 +122,13 @@ export const userInputNode = {
|
|
|
101
122
|
kind: "user-input",
|
|
102
123
|
version: 1,
|
|
103
124
|
async run(context, params) {
|
|
125
|
+
const fields = applyInitialUserInputValues(params.fields, params.initialValues);
|
|
104
126
|
const form = {
|
|
105
127
|
formId: params.formId,
|
|
106
128
|
title: params.title,
|
|
107
129
|
...(params.description ? { description: params.description } : {}),
|
|
108
130
|
...(params.submitLabel ? { submitLabel: params.submitLabel } : {}),
|
|
109
|
-
fields
|
|
131
|
+
fields,
|
|
110
132
|
};
|
|
111
133
|
const requester = context.requestUserInput ?? requestUserInputInTerminal;
|
|
112
134
|
const result = await requester(form);
|
|
@@ -130,7 +152,20 @@ export const userInputNode = {
|
|
|
130
152
|
promptSuffix: rendered.promptSuffix,
|
|
131
153
|
summaryText: rendered.summaryText,
|
|
132
154
|
},
|
|
133
|
-
outputs: [
|
|
155
|
+
outputs: [
|
|
156
|
+
{
|
|
157
|
+
kind: "artifact",
|
|
158
|
+
path: params.outputFile,
|
|
159
|
+
required: true,
|
|
160
|
+
manifest: {
|
|
161
|
+
publish: true,
|
|
162
|
+
logicalKey: buildLogicalKeyForPayload(context.issueKey, params.outputFile),
|
|
163
|
+
payloadFamily: "structured-json",
|
|
164
|
+
schemaId: "user-input/v1",
|
|
165
|
+
schemaVersion: 1,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
134
169
|
};
|
|
135
170
|
},
|
|
136
171
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import { buildLogicalKeyForPayload } from "../../artifact-manifest.js";
|
|
3
4
|
import { TaskRunnerError } from "../../errors.js";
|
|
4
|
-
|
|
5
|
+
import { normalizeReviewSeverity, resolveBlockingReviewSeverities } from "../../review-severity.js";
|
|
5
6
|
export const writeSelectionFileNode = {
|
|
6
7
|
kind: "write-selection-file",
|
|
7
8
|
version: 1,
|
|
@@ -15,15 +16,16 @@ export const writeSelectionFileNode = {
|
|
|
15
16
|
}
|
|
16
17
|
const reviewFindings = parsed;
|
|
17
18
|
const findings = Array.isArray(reviewFindings.findings) ? reviewFindings.findings : [];
|
|
19
|
+
const blockingSeverities = resolveBlockingReviewSeverities(params.blockingSeverities);
|
|
18
20
|
const selectedFindings = findings
|
|
19
21
|
.filter((finding) => {
|
|
20
|
-
const severity =
|
|
22
|
+
const severity = normalizeReviewSeverity(finding.severity);
|
|
21
23
|
const disposition = typeof finding.disposition === "string" ? finding.disposition.trim().toLowerCase() : null;
|
|
22
|
-
return
|
|
24
|
+
return severity !== null && blockingSeverities.includes(severity) && disposition !== "resolved";
|
|
23
25
|
})
|
|
24
26
|
.map((finding) => finding.title)
|
|
25
27
|
.filter((title) => typeof title === "string" && title.trim().length > 0);
|
|
26
|
-
const applyAll =
|
|
28
|
+
const applyAll = false;
|
|
27
29
|
const artifact = {
|
|
28
30
|
form_id: "review-fix-selection",
|
|
29
31
|
submitted_at: new Date().toISOString(),
|
|
@@ -41,6 +43,20 @@ export const writeSelectionFileNode = {
|
|
|
41
43
|
selectedFindings,
|
|
42
44
|
applyAll,
|
|
43
45
|
},
|
|
46
|
+
outputs: [
|
|
47
|
+
{
|
|
48
|
+
kind: "artifact",
|
|
49
|
+
path: params.outputFile,
|
|
50
|
+
required: true,
|
|
51
|
+
manifest: {
|
|
52
|
+
publish: true,
|
|
53
|
+
logicalKey: buildLogicalKeyForPayload(_context.issueKey, params.outputFile),
|
|
54
|
+
payloadFamily: "structured-json",
|
|
55
|
+
schemaId: "user-input/v1",
|
|
56
|
+
schemaVersion: 1,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
44
60
|
};
|
|
45
61
|
},
|
|
46
62
|
};
|