agentweaver 0.1.19 → 0.1.20
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 +47 -7
- package/dist/artifacts.js +9 -0
- package/dist/executors/git-commit-executor.js +24 -6
- package/dist/flow-state.js +3 -8
- package/dist/git/git-diff-parser.js +223 -0
- package/dist/git/git-service.js +562 -0
- package/dist/git/git-stage-selection.js +24 -0
- package/dist/git/git-status-parser.js +171 -0
- package/dist/git/git-types.js +1 -0
- package/dist/index.js +450 -108
- package/dist/interactive/auto-flow.js +644 -0
- package/dist/interactive/controller.js +417 -9
- package/dist/interactive/progress.js +194 -1
- package/dist/interactive/state.js +25 -0
- package/dist/interactive/web/index.js +97 -12
- package/dist/interactive/web/protocol.js +216 -1
- package/dist/interactive/web/server.js +72 -14
- package/dist/interactive/web/static/app.js +1603 -49
- package/dist/interactive/web/static/index.html +76 -11
- package/dist/interactive/web/static/styles.css +1 -1
- package/dist/interactive/web/static/styles.input.css +901 -47
- package/dist/pipeline/auto-flow-blocks.js +307 -0
- package/dist/pipeline/auto-flow-config.js +273 -0
- package/dist/pipeline/auto-flow-identity.js +49 -0
- package/dist/pipeline/auto-flow-presets.js +52 -0
- package/dist/pipeline/auto-flow-resolver.js +830 -0
- package/dist/pipeline/auto-flow-types.js +17 -0
- package/dist/pipeline/context.js +1 -0
- package/dist/pipeline/declarative-flows.js +27 -1
- package/dist/pipeline/flow-specs/auto-common-guided.json +11 -0
- package/dist/pipeline/flow-specs/auto-golang.json +12 -1
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +54 -1
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +19 -1
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +33 -1
- package/dist/pipeline/flow-specs/review/review-project.json +19 -1
- package/dist/pipeline/flow-specs/task-source/manual-jira-input.json +70 -0
- package/dist/pipeline/node-registry.js +9 -0
- package/dist/pipeline/nodes/codex-prompt-node.js +8 -1
- package/dist/pipeline/nodes/flow-run-node.js +5 -3
- package/dist/pipeline/nodes/git-status-node.js +2 -168
- package/dist/pipeline/nodes/manual-jira-task-input-node.js +146 -0
- package/dist/pipeline/nodes/opencode-prompt-node.js +8 -1
- package/dist/pipeline/nodes/plan-codex-node.js +8 -1
- package/dist/pipeline/spec-loader.js +14 -4
- package/dist/runtime/artifact-catalog.js +29 -5
- package/dist/runtime/settings.js +114 -0
- package/dist/scope.js +14 -4
- package/package.json +1 -1
- package/dist/pipeline/flow-specs/auto-common.json +0 -179
- package/dist/pipeline/flow-specs/auto-simple.json +0 -141
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { printSummary } from "../../tui.js";
|
|
3
|
+
import { requestUserInputInTerminal, validateUserInputValues, } from "../../user-input.js";
|
|
4
|
+
function summarizeDescription(description) {
|
|
5
|
+
return description
|
|
6
|
+
.split(/\r?\n/)
|
|
7
|
+
.map((line) => line.trim())
|
|
8
|
+
.find((line) => line.length > 0)
|
|
9
|
+
?.slice(0, 140) || "Manual Jira task description";
|
|
10
|
+
}
|
|
11
|
+
function buildManualJiraPayload(taskKey, description) {
|
|
12
|
+
const summary = summarizeDescription(description);
|
|
13
|
+
return {
|
|
14
|
+
id: `manual-${taskKey}`,
|
|
15
|
+
key: taskKey,
|
|
16
|
+
self: null,
|
|
17
|
+
source: "manual-jira-fallback",
|
|
18
|
+
fields: {
|
|
19
|
+
summary,
|
|
20
|
+
description,
|
|
21
|
+
issuetype: {
|
|
22
|
+
name: "Manual task",
|
|
23
|
+
},
|
|
24
|
+
labels: ["manual-jira-fallback"],
|
|
25
|
+
attachment: [],
|
|
26
|
+
comment: {
|
|
27
|
+
comments: [],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
manual_input: {
|
|
31
|
+
description,
|
|
32
|
+
captured_at: new Date().toISOString(),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export const manualJiraTaskInputNode = {
|
|
37
|
+
kind: "manual-jira-task-input",
|
|
38
|
+
version: 1,
|
|
39
|
+
async run(context, params) {
|
|
40
|
+
const form = {
|
|
41
|
+
formId: "manual-jira-task-input",
|
|
42
|
+
title: "Manual Jira Task",
|
|
43
|
+
description: "Paste the Jira task description when Jira access is unavailable.",
|
|
44
|
+
submitLabel: "Continue",
|
|
45
|
+
fields: [
|
|
46
|
+
{
|
|
47
|
+
id: "task_description",
|
|
48
|
+
type: "text",
|
|
49
|
+
label: "Task description",
|
|
50
|
+
help: "Paste the Jira task text here. This will be stored as the raw Jira task artifact for this flow.",
|
|
51
|
+
required: true,
|
|
52
|
+
multiline: true,
|
|
53
|
+
rows: 10,
|
|
54
|
+
placeholder: "Paste Jira task title, description, acceptance criteria, comments, and links here.",
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
const requester = context.requestUserInput ?? requestUserInputInTerminal;
|
|
59
|
+
const result = await requester(form);
|
|
60
|
+
validateUserInputValues(form, result.values);
|
|
61
|
+
const description = String(result.values.task_description ?? "").trim();
|
|
62
|
+
writeFileSync(params.outputFile, `${JSON.stringify(buildManualJiraPayload(params.taskKey, description), null, 2)}\n`, "utf8");
|
|
63
|
+
if (params.attachmentsManifestFile) {
|
|
64
|
+
writeFileSync(params.attachmentsManifestFile, `${JSON.stringify({ source: "manual-jira-fallback", issueKey: params.taskKey, attachments: [] }, null, 2)}\n`, "utf8");
|
|
65
|
+
}
|
|
66
|
+
if (params.attachmentsContextFile) {
|
|
67
|
+
writeFileSync(params.attachmentsContextFile, "No Jira attachments were provided for the manual Jira fallback.\n", "utf8");
|
|
68
|
+
}
|
|
69
|
+
printSummary("Manual Jira Task", description);
|
|
70
|
+
const outputs = [
|
|
71
|
+
{
|
|
72
|
+
kind: "file",
|
|
73
|
+
path: params.outputFile,
|
|
74
|
+
required: true,
|
|
75
|
+
manifest: {
|
|
76
|
+
publish: true,
|
|
77
|
+
logicalKey: "artifacts/jira-task.json",
|
|
78
|
+
payloadFamily: "helper-json",
|
|
79
|
+
schemaId: "helper-json/v1",
|
|
80
|
+
schemaVersion: 1,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
if (params.attachmentsManifestFile) {
|
|
85
|
+
outputs.push({
|
|
86
|
+
kind: "artifact",
|
|
87
|
+
path: params.attachmentsManifestFile,
|
|
88
|
+
required: true,
|
|
89
|
+
manifest: {
|
|
90
|
+
publish: true,
|
|
91
|
+
logicalKey: "artifacts/jira-attachments.json",
|
|
92
|
+
payloadFamily: "helper-json",
|
|
93
|
+
schemaId: "helper-json/v1",
|
|
94
|
+
schemaVersion: 1,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (params.attachmentsContextFile) {
|
|
99
|
+
outputs.push({
|
|
100
|
+
kind: "artifact",
|
|
101
|
+
path: params.attachmentsContextFile,
|
|
102
|
+
required: true,
|
|
103
|
+
manifest: {
|
|
104
|
+
publish: true,
|
|
105
|
+
logicalKey: "jira-attachments-context.txt",
|
|
106
|
+
payloadFamily: "plain-text",
|
|
107
|
+
schemaId: "plain-text/v1",
|
|
108
|
+
schemaVersion: 1,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
value: {
|
|
114
|
+
outputFile: params.outputFile,
|
|
115
|
+
...(params.attachmentsManifestFile ? { attachmentsManifestFile: params.attachmentsManifestFile } : {}),
|
|
116
|
+
...(params.attachmentsContextFile ? { attachmentsContextFile: params.attachmentsContextFile } : {}),
|
|
117
|
+
descriptionLength: description.length,
|
|
118
|
+
},
|
|
119
|
+
outputs,
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
checks(_context, params) {
|
|
123
|
+
const checks = [
|
|
124
|
+
{
|
|
125
|
+
kind: "require-file",
|
|
126
|
+
path: params.outputFile,
|
|
127
|
+
message: `Manual Jira task input did not produce ${params.outputFile}.`,
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
if (params.attachmentsManifestFile) {
|
|
131
|
+
checks.push({
|
|
132
|
+
kind: "require-file",
|
|
133
|
+
path: params.attachmentsManifestFile,
|
|
134
|
+
message: `Manual Jira task input did not produce ${params.attachmentsManifestFile}.`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (params.attachmentsContextFile) {
|
|
138
|
+
checks.push({
|
|
139
|
+
kind: "require-file",
|
|
140
|
+
path: params.attachmentsContextFile,
|
|
141
|
+
message: `Manual Jira task input did not produce ${params.attachmentsContextFile}.`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return checks;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
@@ -14,7 +14,14 @@ export const opencodePromptNode = {
|
|
|
14
14
|
}, executor.defaultConfig);
|
|
15
15
|
return {
|
|
16
16
|
value,
|
|
17
|
-
outputs: (params.requiredArtifacts ?? []).map((path) => ({
|
|
17
|
+
outputs: (params.requiredArtifacts ?? []).map((path) => ({
|
|
18
|
+
kind: "artifact",
|
|
19
|
+
path,
|
|
20
|
+
required: true,
|
|
21
|
+
manifest: {
|
|
22
|
+
publish: true,
|
|
23
|
+
},
|
|
24
|
+
})),
|
|
18
25
|
};
|
|
19
26
|
},
|
|
20
27
|
checks(_context, params) {
|
|
@@ -17,7 +17,14 @@ export const planCodexNode = {
|
|
|
17
17
|
const value = await executor.execute(toExecutorContext(context), input, executor.defaultConfig);
|
|
18
18
|
return {
|
|
19
19
|
value,
|
|
20
|
-
outputs: params.requiredArtifacts.map((path) => ({
|
|
20
|
+
outputs: params.requiredArtifacts.map((path) => ({
|
|
21
|
+
kind: "artifact",
|
|
22
|
+
path,
|
|
23
|
+
required: true,
|
|
24
|
+
manifest: {
|
|
25
|
+
publish: true,
|
|
26
|
+
},
|
|
27
|
+
})),
|
|
21
28
|
};
|
|
22
29
|
},
|
|
23
30
|
checks(_context, params) {
|
|
@@ -3,6 +3,8 @@ import path from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { TaskRunnerError } from "../errors.js";
|
|
5
5
|
import { agentweaverConfigDir } from "../runtime/env-loader.js";
|
|
6
|
+
import { resolveBuiltInAutoFlowSpecByFileName } from "./auto-flow-resolver.js";
|
|
7
|
+
import { VIRTUAL_BUILT_IN_AUTO_FLOW_FILE_NAMES } from "./auto-flow-presets.js";
|
|
6
8
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
7
9
|
const BUILT_IN_FLOW_SPECS_DIR = path.join(MODULE_DIR, "flow-specs");
|
|
8
10
|
function parseFlowSpec(filePath) {
|
|
@@ -23,12 +25,14 @@ export function globalFlowSpecsDir() {
|
|
|
23
25
|
return path.join(agentweaverConfigDir(), ".flows");
|
|
24
26
|
}
|
|
25
27
|
export function listBuiltInFlowSpecFiles() {
|
|
28
|
+
const files = new Set(VIRTUAL_BUILT_IN_AUTO_FLOW_FILE_NAMES);
|
|
26
29
|
if (!existsSync(BUILT_IN_FLOW_SPECS_DIR)) {
|
|
27
|
-
return [];
|
|
30
|
+
return [...files].sort((left, right) => left.localeCompare(right));
|
|
31
|
+
}
|
|
32
|
+
for (const filePath of collectJsonFilesRecursively(BUILT_IN_FLOW_SPECS_DIR)) {
|
|
33
|
+
files.add(path.relative(BUILT_IN_FLOW_SPECS_DIR, filePath));
|
|
28
34
|
}
|
|
29
|
-
return
|
|
30
|
-
.map((filePath) => path.relative(BUILT_IN_FLOW_SPECS_DIR, filePath))
|
|
31
|
-
.sort((left, right) => left.localeCompare(right));
|
|
35
|
+
return [...files].sort((left, right) => left.localeCompare(right));
|
|
32
36
|
}
|
|
33
37
|
function collectJsonFilesRecursively(directory) {
|
|
34
38
|
const entries = readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
@@ -60,6 +64,12 @@ export function listGlobalFlowSpecFiles() {
|
|
|
60
64
|
return collectJsonFilesRecursively(directory);
|
|
61
65
|
}
|
|
62
66
|
export function loadFlowSpecSync(source) {
|
|
67
|
+
if (source.source === "built-in") {
|
|
68
|
+
const resolvedAutoFlowSpec = resolveBuiltInAutoFlowSpecByFileName(source.fileName);
|
|
69
|
+
if (resolvedAutoFlowSpec) {
|
|
70
|
+
return resolvedAutoFlowSpec;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
63
73
|
return parseFlowSpec(source.source === "built-in" ? resolveBuiltInFlowSpecPath(source.fileName) : source.filePath);
|
|
64
74
|
}
|
|
65
75
|
export function loadBuiltInFlowSpecSync(fileName) {
|
|
@@ -323,6 +323,9 @@ function roleRank(role) {
|
|
|
323
323
|
function phaseSortKey(phaseId) {
|
|
324
324
|
return phaseId ?? UNCLASSIFIED_PHASE_ID;
|
|
325
325
|
}
|
|
326
|
+
function typeGroupKey(item) {
|
|
327
|
+
return `${item.role}:${item.title}`;
|
|
328
|
+
}
|
|
326
329
|
function compareCatalogItems(left, right) {
|
|
327
330
|
const phaseComparison = phaseSortKey(left.phaseId).localeCompare(phaseSortKey(right.phaseId));
|
|
328
331
|
if (phaseComparison !== 0) {
|
|
@@ -336,6 +339,10 @@ function compareCatalogItems(left, right) {
|
|
|
336
339
|
if (titleComparison !== 0) {
|
|
337
340
|
return titleComparison;
|
|
338
341
|
}
|
|
342
|
+
const updatedComparison = right.updatedAt.localeCompare(left.updatedAt);
|
|
343
|
+
if (updatedComparison !== 0) {
|
|
344
|
+
return updatedComparison;
|
|
345
|
+
}
|
|
339
346
|
return left.relativePath.localeCompare(right.relativePath);
|
|
340
347
|
}
|
|
341
348
|
export function groupArtifactCatalog(items) {
|
|
@@ -348,11 +355,28 @@ export function groupArtifactCatalog(items) {
|
|
|
348
355
|
}
|
|
349
356
|
return Array.from(grouped.entries())
|
|
350
357
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
351
|
-
.map(([phaseId, phaseItems]) =>
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
358
|
+
.map(([phaseId, phaseItems]) => {
|
|
359
|
+
const sortedPhaseItems = phaseItems.slice().sort(compareCatalogItems);
|
|
360
|
+
const typeGroups = new Map();
|
|
361
|
+
for (const item of sortedPhaseItems) {
|
|
362
|
+
const key = typeGroupKey(item);
|
|
363
|
+
const groupItems = typeGroups.get(key) ?? [];
|
|
364
|
+
groupItems.push(item);
|
|
365
|
+
typeGroups.set(key, groupItems);
|
|
366
|
+
}
|
|
367
|
+
const nestedGroups = Array.from(typeGroups.entries())
|
|
368
|
+
.map(([key, groupItems]) => ({
|
|
369
|
+
phaseId: `${phaseId}:${key}`,
|
|
370
|
+
title: groupItems[0]?.title ?? "Artifacts",
|
|
371
|
+
items: groupItems.slice().sort(compareCatalogItems),
|
|
372
|
+
}));
|
|
373
|
+
return {
|
|
374
|
+
phaseId,
|
|
375
|
+
title: phaseId === UNCLASSIFIED_PHASE_ID ? "Unclassified" : toTitleCase(phaseId),
|
|
376
|
+
items: sortedPhaseItems,
|
|
377
|
+
...(nestedGroups.length > 1 || nestedGroups.some((group) => group.items.length > 1) ? { groups: nestedGroups } : {}),
|
|
378
|
+
};
|
|
379
|
+
});
|
|
356
380
|
}
|
|
357
381
|
export function listArtifactCatalog(input) {
|
|
358
382
|
const seenPayloadPaths = new Set();
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { agentweaverConfigDir } from "./env-loader.js";
|
|
4
|
+
export const WEB_UI_AUTO_FLOW_HEIGHT_MIN = 120;
|
|
5
|
+
export const WEB_UI_AUTO_FLOW_HEIGHT_MAX = 640;
|
|
6
|
+
export const WEB_UI_WORKSPACE_SPLIT_MIN = 24;
|
|
7
|
+
export const WEB_UI_WORKSPACE_SPLIT_MAX = 58;
|
|
8
|
+
export const DEFAULT_AGENTWEAVER_WEB_UI_SETTINGS = {
|
|
9
|
+
theme: "light",
|
|
10
|
+
autoFlowHeight: null,
|
|
11
|
+
workspaceSplit: 36,
|
|
12
|
+
logAutoscroll: true,
|
|
13
|
+
};
|
|
14
|
+
export function agentweaverSettingsPath() {
|
|
15
|
+
return path.join(agentweaverConfigDir(), "settings.json");
|
|
16
|
+
}
|
|
17
|
+
function isRecord(value) {
|
|
18
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
function clampInteger(value, min, max, fallback) {
|
|
21
|
+
const numeric = Number(value);
|
|
22
|
+
if (!Number.isFinite(numeric)) {
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
return Math.min(max, Math.max(min, Math.round(numeric)));
|
|
26
|
+
}
|
|
27
|
+
function normalizeTheme(value, fallback) {
|
|
28
|
+
return value === "dark" || value === "light" ? value : fallback;
|
|
29
|
+
}
|
|
30
|
+
export function normalizeWebUiSettings(value) {
|
|
31
|
+
const raw = isRecord(value) ? value : {};
|
|
32
|
+
const defaults = DEFAULT_AGENTWEAVER_WEB_UI_SETTINGS;
|
|
33
|
+
const rawHeight = raw["autoFlowHeight"];
|
|
34
|
+
const autoFlowHeight = rawHeight === null
|
|
35
|
+
? null
|
|
36
|
+
: Number.isFinite(Number(rawHeight))
|
|
37
|
+
? clampInteger(rawHeight, WEB_UI_AUTO_FLOW_HEIGHT_MIN, WEB_UI_AUTO_FLOW_HEIGHT_MAX, defaults.autoFlowHeight ?? WEB_UI_AUTO_FLOW_HEIGHT_MAX)
|
|
38
|
+
: defaults.autoFlowHeight;
|
|
39
|
+
return {
|
|
40
|
+
theme: normalizeTheme(raw["theme"], defaults.theme),
|
|
41
|
+
autoFlowHeight,
|
|
42
|
+
workspaceSplit: clampInteger(raw["workspaceSplit"], WEB_UI_WORKSPACE_SPLIT_MIN, WEB_UI_WORKSPACE_SPLIT_MAX, defaults.workspaceSplit),
|
|
43
|
+
logAutoscroll: typeof raw["logAutoscroll"] === "boolean" ? raw["logAutoscroll"] : defaults.logAutoscroll,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function normalizeWebUiSettingsPatch(patch) {
|
|
47
|
+
const normalized = {};
|
|
48
|
+
if (patch.theme !== undefined) {
|
|
49
|
+
normalized.theme = normalizeTheme(patch.theme, DEFAULT_AGENTWEAVER_WEB_UI_SETTINGS.theme);
|
|
50
|
+
}
|
|
51
|
+
if ("autoFlowHeight" in patch) {
|
|
52
|
+
normalized.autoFlowHeight = patch.autoFlowHeight === null || patch.autoFlowHeight === undefined
|
|
53
|
+
? null
|
|
54
|
+
: clampInteger(patch.autoFlowHeight, WEB_UI_AUTO_FLOW_HEIGHT_MIN, WEB_UI_AUTO_FLOW_HEIGHT_MAX, WEB_UI_AUTO_FLOW_HEIGHT_MAX);
|
|
55
|
+
}
|
|
56
|
+
if (patch.workspaceSplit !== undefined) {
|
|
57
|
+
normalized.workspaceSplit = clampInteger(patch.workspaceSplit, WEB_UI_WORKSPACE_SPLIT_MIN, WEB_UI_WORKSPACE_SPLIT_MAX, DEFAULT_AGENTWEAVER_WEB_UI_SETTINGS.workspaceSplit);
|
|
58
|
+
}
|
|
59
|
+
if (patch.logAutoscroll !== undefined) {
|
|
60
|
+
normalized.logAutoscroll = Boolean(patch.logAutoscroll);
|
|
61
|
+
}
|
|
62
|
+
return normalized;
|
|
63
|
+
}
|
|
64
|
+
function readRawSettings() {
|
|
65
|
+
const filePath = agentweaverSettingsPath();
|
|
66
|
+
if (!existsSync(filePath)) {
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
71
|
+
return isRecord(parsed) ? parsed : {};
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function writeJsonAtomic(filePath, value) {
|
|
78
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
79
|
+
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
80
|
+
writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
81
|
+
renameSync(tempPath, filePath);
|
|
82
|
+
}
|
|
83
|
+
export function loadAgentWeaverSettings() {
|
|
84
|
+
const raw = readRawSettings();
|
|
85
|
+
return {
|
|
86
|
+
kind: "agentweaver-settings",
|
|
87
|
+
version: 1,
|
|
88
|
+
webUi: normalizeWebUiSettings(raw["webUi"]),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function saveAgentWeaverSettings(settings) {
|
|
92
|
+
const raw = readRawSettings();
|
|
93
|
+
const normalized = {
|
|
94
|
+
kind: "agentweaver-settings",
|
|
95
|
+
version: 1,
|
|
96
|
+
webUi: normalizeWebUiSettings(settings.webUi),
|
|
97
|
+
};
|
|
98
|
+
writeJsonAtomic(agentweaverSettingsPath(), {
|
|
99
|
+
...raw,
|
|
100
|
+
...normalized,
|
|
101
|
+
});
|
|
102
|
+
return normalized;
|
|
103
|
+
}
|
|
104
|
+
export function updateWebUiSettings(patch) {
|
|
105
|
+
const current = loadAgentWeaverSettings();
|
|
106
|
+
const next = saveAgentWeaverSettings({
|
|
107
|
+
...current,
|
|
108
|
+
webUi: {
|
|
109
|
+
...current.webUi,
|
|
110
|
+
...normalizeWebUiSettingsPatch(patch),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
return next.webUi;
|
|
114
|
+
}
|
package/dist/scope.js
CHANGED
|
@@ -109,19 +109,24 @@ export function attachJiraContext(scope, jiraRef) {
|
|
|
109
109
|
jiraTaskFile: jiraTaskFile(scope.scopeKey),
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
|
-
export function buildJiraTaskInputForm() {
|
|
112
|
+
export function buildJiraTaskInputForm(options = {}) {
|
|
113
|
+
const required = options.required ?? true;
|
|
113
114
|
return {
|
|
114
115
|
formId: "jira-task-input",
|
|
115
116
|
title: "Jira Task",
|
|
116
|
-
description:
|
|
117
|
+
description: required
|
|
118
|
+
? "Provide a Jira issue key or browse URL for a task-driven flow."
|
|
119
|
+
: "Provide a Jira issue key or browse URL, or leave it empty to paste the task description manually.",
|
|
117
120
|
submitLabel: "Continue",
|
|
118
121
|
fields: [
|
|
119
122
|
{
|
|
120
123
|
id: "jira_ref",
|
|
121
124
|
type: "text",
|
|
122
125
|
label: "Jira issue key or browse URL",
|
|
123
|
-
help:
|
|
124
|
-
|
|
126
|
+
help: required
|
|
127
|
+
? "Example: DEMO-3288 or https://jira.example.com/browse/DEMO-3288"
|
|
128
|
+
: "Leave empty to enter the task description manually in the next step.",
|
|
129
|
+
required,
|
|
125
130
|
},
|
|
126
131
|
],
|
|
127
132
|
};
|
|
@@ -134,3 +139,8 @@ export async function requestJiraContext(requestUserInput) {
|
|
|
134
139
|
}
|
|
135
140
|
return parseJiraContext(jiraRef);
|
|
136
141
|
}
|
|
142
|
+
export async function requestOptionalJiraContext(requestUserInput) {
|
|
143
|
+
const result = await requestUserInput(buildJiraTaskInputForm({ required: false }));
|
|
144
|
+
const jiraRef = String(result.values.jira_ref ?? "").trim();
|
|
145
|
+
return jiraRef ? parseJiraContext(jiraRef) : null;
|
|
146
|
+
}
|
package/package.json
CHANGED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"kind": "auto-flow",
|
|
3
|
-
"version": 1,
|
|
4
|
-
"description": "End-to-end resumable pipeline without language-specific checks. Runs: Jira fetch → task source normalization → plan → design-review loop → implement → review loop. Planning gate requires design-review approval, or an explicit user decision to continue after the maximum revision attempts, before implementation.",
|
|
5
|
-
"phases": [
|
|
6
|
-
{
|
|
7
|
-
"id": "source",
|
|
8
|
-
"steps": [
|
|
9
|
-
{
|
|
10
|
-
"id": "fetch_jira_source",
|
|
11
|
-
"node": "flow-run",
|
|
12
|
-
"params": {
|
|
13
|
-
"fileName": { "const": "jira-fetch.json" },
|
|
14
|
-
"labelText": { "const": "Fetching Jira task source" },
|
|
15
|
-
"jiraApiUrl": { "ref": "params.jiraApiUrl" },
|
|
16
|
-
"taskKey": { "ref": "params.taskKey" }
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
]
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"id": "normalize",
|
|
23
|
-
"steps": [
|
|
24
|
-
{
|
|
25
|
-
"id": "run_normalize_source",
|
|
26
|
-
"node": "flow-run",
|
|
27
|
-
"params": {
|
|
28
|
-
"fileName": { "const": "normalize-task-source.json" },
|
|
29
|
-
"labelText": { "const": "Normalizing task source" },
|
|
30
|
-
"taskKey": { "ref": "params.taskKey" },
|
|
31
|
-
"iteration": { "ref": "params.taskContextIteration" },
|
|
32
|
-
"llmExecutor": { "ref": "params.llmExecutor" },
|
|
33
|
-
"llmModel": { "ref": "params.llmModel" },
|
|
34
|
-
"extraPrompt": { "ref": "params.extraPrompt" }
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"id": "plan",
|
|
41
|
-
"steps": [
|
|
42
|
-
{
|
|
43
|
-
"id": "run_plan_flow",
|
|
44
|
-
"node": "flow-run",
|
|
45
|
-
"params": {
|
|
46
|
-
"fileName": { "const": "plan.json" },
|
|
47
|
-
"labelText": { "const": "Running planning flow" },
|
|
48
|
-
"taskKey": { "ref": "params.taskKey" },
|
|
49
|
-
"taskContextIteration": { "ref": "params.taskContextIteration" },
|
|
50
|
-
"designIteration": { "ref": "params.designIteration" },
|
|
51
|
-
"planIteration": { "ref": "params.planIteration" },
|
|
52
|
-
"qaIteration": { "ref": "params.qaIteration" },
|
|
53
|
-
"llmExecutor": { "ref": "params.llmExecutor" },
|
|
54
|
-
"llmModel": { "ref": "params.llmModel" },
|
|
55
|
-
"extraPrompt": { "ref": "params.extraPrompt" },
|
|
56
|
-
"mdLang": { "ref": "params.mdLang" }
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
]
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"id": "design_review_loop",
|
|
63
|
-
"steps": [
|
|
64
|
-
{
|
|
65
|
-
"id": "run_design_review_loop",
|
|
66
|
-
"node": "flow-run",
|
|
67
|
-
"params": {
|
|
68
|
-
"fileName": { "const": "design-review-loop.json" },
|
|
69
|
-
"labelText": { "const": "Running design-review loop" },
|
|
70
|
-
"taskKey": { "ref": "params.taskKey" },
|
|
71
|
-
"baseIteration": { "ref": "params.designReviewBaseIteration" },
|
|
72
|
-
"workspaceDir": { "ref": "params.workspaceDir" },
|
|
73
|
-
"extraPrompt": { "ref": "params.extraPrompt" },
|
|
74
|
-
"llmExecutor": { "ref": "params.llmExecutor" },
|
|
75
|
-
"llmModel": { "ref": "params.llmModel" }
|
|
76
|
-
},
|
|
77
|
-
"stopFlowIf": {
|
|
78
|
-
"equals": [
|
|
79
|
-
{ "ref": "steps.design_review_loop.run_design_review_loop.value.executionState.terminationOutcome" },
|
|
80
|
-
{ "const": "stopped" }
|
|
81
|
-
]
|
|
82
|
-
},
|
|
83
|
-
"stopFlowOutcome": "stopped"
|
|
84
|
-
}
|
|
85
|
-
]
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"id": "implement",
|
|
89
|
-
"steps": [
|
|
90
|
-
{
|
|
91
|
-
"id": "resolve_planning_bundle",
|
|
92
|
-
"node": "planning-bundle",
|
|
93
|
-
"params": {
|
|
94
|
-
"taskKey": { "ref": "params.taskKey" }
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"id": "run_implement",
|
|
99
|
-
"node": "llm-prompt",
|
|
100
|
-
"routingGroup": "implementation",
|
|
101
|
-
"prompt": {
|
|
102
|
-
"templateRef": "implement",
|
|
103
|
-
"vars": {
|
|
104
|
-
"design_file": { "ref": "steps.implement.resolve_planning_bundle.value.designFile" },
|
|
105
|
-
"design_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.designJsonFile" },
|
|
106
|
-
"plan_file": { "ref": "steps.implement.resolve_planning_bundle.value.planFile" },
|
|
107
|
-
"plan_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.planJsonFile" },
|
|
108
|
-
"qa_file": { "ref": "steps.implement.resolve_planning_bundle.value.qaFile" },
|
|
109
|
-
"qa_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.qaJsonFile" },
|
|
110
|
-
"project_guidance_file": { "const": "not provided" },
|
|
111
|
-
"project_guidance_json_file": { "const": "not provided" }
|
|
112
|
-
},
|
|
113
|
-
"extraPrompt": { "ref": "params.extraPrompt" },
|
|
114
|
-
"format": "task-prompt"
|
|
115
|
-
},
|
|
116
|
-
"params": {
|
|
117
|
-
"labelText": { "const": "Running implementation mode locally" },
|
|
118
|
-
"model": { "ref": "params.llmModel" },
|
|
119
|
-
"executor": { "ref": "params.llmExecutor" }
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
"id": "notify_implement_complete",
|
|
124
|
-
"node": "telegram-notify",
|
|
125
|
-
"params": {
|
|
126
|
-
"message": {
|
|
127
|
-
"template": "Implementation phase for {taskKey} complete.",
|
|
128
|
-
"vars": {
|
|
129
|
-
"taskKey": { "ref": "params.taskKey" }
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
]
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"id": "review-loop",
|
|
138
|
-
"steps": [
|
|
139
|
-
{
|
|
140
|
-
"id": "run_review_loop",
|
|
141
|
-
"node": "flow-run",
|
|
142
|
-
"stopFlowIf": {
|
|
143
|
-
"not": {
|
|
144
|
-
"equals": [
|
|
145
|
-
{ "ref": "steps.review-loop.run_review_loop.value.executionState.terminationOutcome" },
|
|
146
|
-
{ "const": "success" }
|
|
147
|
-
]
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
"stopFlowOutcome": "stopped",
|
|
151
|
-
"params": {
|
|
152
|
-
"fileName": { "const": "review-loop.json" },
|
|
153
|
-
"labelText": { "const": "Running review-loop" },
|
|
154
|
-
"taskKey": { "ref": "params.taskKey" },
|
|
155
|
-
"baseIteration": { "ref": "params.baseIteration" },
|
|
156
|
-
"workspaceDir": { "ref": "params.workspaceDir" },
|
|
157
|
-
"extraPrompt": { "ref": "params.extraPrompt" },
|
|
158
|
-
"reviewFixPoints": { "ref": "params.reviewFixPoints" },
|
|
159
|
-
"reviewBlockingSeverities": { "ref": "params.reviewBlockingSeverities" },
|
|
160
|
-
"llmExecutor": { "ref": "params.llmExecutor" },
|
|
161
|
-
"llmModel": { "ref": "params.llmModel" }
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
"id": "notify_task_complete",
|
|
166
|
-
"node": "telegram-notify",
|
|
167
|
-
"params": {
|
|
168
|
-
"message": {
|
|
169
|
-
"template": "Task {taskKey} complete.",
|
|
170
|
-
"vars": {
|
|
171
|
-
"taskKey": { "ref": "params.taskKey" }
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
]
|
|
177
|
-
}
|
|
178
|
-
]
|
|
179
|
-
}
|