agentweaver 0.1.0 → 0.1.3

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.
Files changed (77) hide show
  1. package/README.md +30 -5
  2. package/dist/artifacts.js +24 -2
  3. package/dist/executors/claude-executor.js +46 -0
  4. package/dist/executors/claude-summary-executor.js +31 -0
  5. package/dist/executors/codex-docker-executor.js +27 -0
  6. package/dist/executors/codex-local-executor.js +25 -0
  7. package/dist/executors/command-check-executor.js +14 -0
  8. package/dist/executors/configs/claude-config.js +12 -0
  9. package/dist/executors/configs/claude-summary-config.js +8 -0
  10. package/dist/executors/configs/codex-docker-config.js +10 -0
  11. package/dist/executors/configs/codex-local-config.js +8 -0
  12. package/dist/executors/configs/jira-fetch-config.js +4 -0
  13. package/dist/executors/configs/process-config.js +3 -0
  14. package/dist/executors/configs/verify-build-config.js +7 -0
  15. package/dist/executors/jira-fetch-executor.js +11 -0
  16. package/dist/executors/process-executor.js +21 -0
  17. package/dist/executors/types.js +1 -0
  18. package/dist/executors/verify-build-executor.js +22 -0
  19. package/dist/index.js +456 -699
  20. package/dist/interactive-ui.js +536 -182
  21. package/dist/jira.js +3 -1
  22. package/dist/pipeline/auto-flow.js +9 -0
  23. package/dist/pipeline/build-failure-summary.js +6 -0
  24. package/dist/pipeline/checks.js +15 -0
  25. package/dist/pipeline/context.js +19 -0
  26. package/dist/pipeline/declarative-flow-runner.js +246 -0
  27. package/dist/pipeline/declarative-flows.js +24 -0
  28. package/dist/pipeline/flow-runner.js +13 -0
  29. package/dist/pipeline/flow-specs/auto.json +471 -0
  30. package/dist/pipeline/flow-specs/implement.json +47 -0
  31. package/dist/pipeline/flow-specs/plan.json +88 -0
  32. package/dist/pipeline/flow-specs/preflight.json +174 -0
  33. package/dist/pipeline/flow-specs/review-fix.json +76 -0
  34. package/dist/pipeline/flow-specs/review.json +233 -0
  35. package/dist/pipeline/flow-specs/test-fix.json +24 -0
  36. package/dist/pipeline/flow-specs/test-linter-fix.json +24 -0
  37. package/dist/pipeline/flow-specs/test.json +19 -0
  38. package/dist/pipeline/flow-types.js +1 -0
  39. package/dist/pipeline/flows/implement-flow.js +47 -0
  40. package/dist/pipeline/flows/plan-flow.js +42 -0
  41. package/dist/pipeline/flows/preflight-flow.js +19 -0
  42. package/dist/pipeline/flows/review-fix-flow.js +62 -0
  43. package/dist/pipeline/flows/review-flow.js +124 -0
  44. package/dist/pipeline/flows/test-fix-flow.js +12 -0
  45. package/dist/pipeline/flows/test-flow.js +32 -0
  46. package/dist/pipeline/node-registry.js +71 -0
  47. package/dist/pipeline/node-runner.js +20 -0
  48. package/dist/pipeline/nodes/build-failure-summary-node.js +71 -0
  49. package/dist/pipeline/nodes/claude-prompt-node.js +54 -0
  50. package/dist/pipeline/nodes/claude-summary-node.js +38 -0
  51. package/dist/pipeline/nodes/codex-docker-prompt-node.js +32 -0
  52. package/dist/pipeline/nodes/codex-local-prompt-node.js +32 -0
  53. package/dist/pipeline/nodes/command-check-node.js +10 -0
  54. package/dist/pipeline/nodes/file-check-node.js +15 -0
  55. package/dist/pipeline/nodes/implement-codex-node.js +16 -0
  56. package/dist/pipeline/nodes/jira-fetch-node.js +25 -0
  57. package/dist/pipeline/nodes/plan-codex-node.js +32 -0
  58. package/dist/pipeline/nodes/review-claude-node.js +38 -0
  59. package/dist/pipeline/nodes/review-reply-codex-node.js +40 -0
  60. package/dist/pipeline/nodes/summary-file-load-node.js +16 -0
  61. package/dist/pipeline/nodes/task-summary-node.js +42 -0
  62. package/dist/pipeline/nodes/verify-build-node.js +14 -0
  63. package/dist/pipeline/prompt-registry.js +22 -0
  64. package/dist/pipeline/prompt-runtime.js +18 -0
  65. package/dist/pipeline/registry.js +23 -0
  66. package/dist/pipeline/spec-compiler.js +200 -0
  67. package/dist/pipeline/spec-loader.js +14 -0
  68. package/dist/pipeline/spec-types.js +1 -0
  69. package/dist/pipeline/spec-validator.js +290 -0
  70. package/dist/pipeline/types.js +10 -0
  71. package/dist/pipeline/value-resolver.js +199 -0
  72. package/dist/prompts.js +1 -3
  73. package/dist/runtime/command-resolution.js +139 -0
  74. package/dist/runtime/docker-runtime.js +51 -0
  75. package/dist/runtime/process-runner.js +112 -0
  76. package/dist/tui.js +73 -0
  77. package/package.json +3 -2
@@ -0,0 +1,62 @@
1
+ import { artifactFile, planArtifacts, requireArtifacts } from "../../artifacts.js";
2
+ import { TaskRunnerError } from "../../errors.js";
3
+ import { REVIEW_FIX_PROMPT_TEMPLATE, formatPrompt, formatTemplate } from "../../prompts.js";
4
+ import { runFlow } from "../flow-runner.js";
5
+ import { runNode } from "../node-runner.js";
6
+ import { codexLocalPromptNode } from "../nodes/codex-local-prompt-node.js";
7
+ import { verifyBuildNode } from "../nodes/verify-build-node.js";
8
+ export function createReviewFixFlowDefinition(iteration) {
9
+ return {
10
+ kind: "review-fix-flow",
11
+ version: 1,
12
+ steps: [
13
+ {
14
+ id: "run_codex_review_fix",
15
+ async run(stepContext, stepParams) {
16
+ const reviewReplyFile = artifactFile("review-reply", stepParams.taskKey, iteration);
17
+ const reviewFixFile = artifactFile("review-fix", stepParams.taskKey, iteration);
18
+ const prompt = formatPrompt(formatTemplate(REVIEW_FIX_PROMPT_TEMPLATE, {
19
+ review_reply_file: reviewReplyFile,
20
+ items: stepParams.reviewFixPoints ?? "",
21
+ review_fix_file: reviewFixFile,
22
+ }), stepParams.extraPrompt);
23
+ await runNode(codexLocalPromptNode, stepContext, {
24
+ prompt,
25
+ labelText: `Running Codex review-fix mode locally (iteration ${iteration})`,
26
+ requiredArtifacts: stepContext.dryRun ? [] : [reviewFixFile],
27
+ missingArtifactsMessage: "Review-fix mode did not produce the required review-fix artifact.",
28
+ });
29
+ return { completed: true, metadata: { iteration } };
30
+ },
31
+ },
32
+ {
33
+ id: "verify_build_after_review_fix",
34
+ async run(stepContext, stepParams) {
35
+ if (!stepParams.runFollowupVerify) {
36
+ return { completed: true, metadata: { skipped: true } };
37
+ }
38
+ try {
39
+ await runNode(verifyBuildNode, stepContext, {
40
+ dockerComposeFile: stepParams.dockerComposeFile,
41
+ labelText: "Running build verification in isolated Docker",
42
+ });
43
+ }
44
+ catch (error) {
45
+ if (stepParams.onVerifyBuildFailure) {
46
+ await stepParams.onVerifyBuildFailure(String(error.output ?? ""));
47
+ }
48
+ throw error;
49
+ }
50
+ return { completed: true };
51
+ },
52
+ },
53
+ ],
54
+ };
55
+ }
56
+ export async function runReviewFixFlow(context, params) {
57
+ requireArtifacts(planArtifacts(params.taskKey), "Review-fix mode requires plan artifacts from the planning phase.");
58
+ if (params.latestIteration === null) {
59
+ throw new TaskRunnerError(`Review-fix mode requires at least one review-reply-${params.taskKey}-N.md artifact.`);
60
+ }
61
+ await runFlow(createReviewFixFlowDefinition(params.latestIteration), context, params);
62
+ }
@@ -0,0 +1,124 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { REVIEW_REPLY_FILE_RE, artifactFile, planArtifacts, readyToMergeFile, requireArtifacts, taskWorkspaceDir, } from "../../artifacts.js";
3
+ import { REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, formatTemplate } from "../../prompts.js";
4
+ import { printPanel } from "../../tui.js";
5
+ import { runFlow } from "../flow-runner.js";
6
+ import { runNode } from "../node-runner.js";
7
+ import { claudeSummaryNode } from "../nodes/claude-summary-node.js";
8
+ import { reviewClaudeNode } from "../nodes/review-claude-node.js";
9
+ import { reviewReplyCodexNode } from "../nodes/review-reply-codex-node.js";
10
+ function nextReviewIterationForTask(taskKey) {
11
+ let maxIndex = 0;
12
+ const workspaceDir = taskWorkspaceDir(taskKey);
13
+ if (!existsSync(workspaceDir)) {
14
+ return 1;
15
+ }
16
+ for (const entry of readdirSync(workspaceDir, { withFileTypes: true })) {
17
+ if (!entry.isFile()) {
18
+ continue;
19
+ }
20
+ const match = REVIEW_REPLY_FILE_RE.exec(entry.name);
21
+ if (match && match[1] === taskKey) {
22
+ const current = Number.parseInt(match[2] ?? "0", 10);
23
+ maxIndex = Math.max(maxIndex, current);
24
+ }
25
+ }
26
+ return maxIndex + 1;
27
+ }
28
+ export function createReviewFlowDefinition(iteration) {
29
+ return {
30
+ kind: "review-flow",
31
+ version: 1,
32
+ steps: [
33
+ {
34
+ id: "run_claude_review",
35
+ async run(stepContext, stepParams) {
36
+ await runNode(reviewClaudeNode, stepContext, {
37
+ jiraTaskFile: stepParams.jiraTaskFile,
38
+ taskKey: stepParams.taskKey,
39
+ iteration,
40
+ claudeCmd: stepParams.claudeCmd,
41
+ ...(stepParams.extraPrompt !== undefined ? { extraPrompt: stepParams.extraPrompt } : {}),
42
+ });
43
+ return { completed: true, metadata: { iteration } };
44
+ },
45
+ },
46
+ {
47
+ id: "summarize_review",
48
+ async run(stepContext, stepParams) {
49
+ const reviewFile = artifactFile("review", stepParams.taskKey, iteration);
50
+ const reviewSummaryFile = artifactFile("review-summary", stepParams.taskKey, iteration);
51
+ if (stepContext.dryRun) {
52
+ return { completed: true, metadata: { skipped: true } };
53
+ }
54
+ await runNode(claudeSummaryNode, stepContext, {
55
+ claudeCmd: stepParams.claudeCmd,
56
+ outputFile: reviewSummaryFile,
57
+ prompt: formatTemplate(REVIEW_SUMMARY_PROMPT_TEMPLATE, {
58
+ review_file: reviewFile,
59
+ review_summary_file: reviewSummaryFile,
60
+ }),
61
+ summaryTitle: "Claude Comments",
62
+ verbose: stepContext.verbose,
63
+ });
64
+ return { completed: true };
65
+ },
66
+ },
67
+ {
68
+ id: "run_codex_review_reply",
69
+ async run(stepContext, stepParams) {
70
+ await runNode(reviewReplyCodexNode, stepContext, {
71
+ jiraTaskFile: stepParams.jiraTaskFile,
72
+ taskKey: stepParams.taskKey,
73
+ iteration,
74
+ codexCmd: stepParams.codexCmd,
75
+ ...(stepParams.extraPrompt !== undefined ? { extraPrompt: stepParams.extraPrompt } : {}),
76
+ });
77
+ return { completed: true };
78
+ },
79
+ },
80
+ {
81
+ id: "summarize_review_reply",
82
+ async run(stepContext, stepParams) {
83
+ const reviewReplyFile = artifactFile("review-reply", stepParams.taskKey, iteration);
84
+ const reviewReplySummaryFile = artifactFile("review-reply-summary", stepParams.taskKey, iteration);
85
+ if (stepContext.dryRun) {
86
+ return { completed: true, metadata: { skipped: true } };
87
+ }
88
+ await runNode(claudeSummaryNode, stepContext, {
89
+ claudeCmd: stepParams.claudeCmd,
90
+ outputFile: reviewReplySummaryFile,
91
+ prompt: formatTemplate(REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, {
92
+ review_reply_file: reviewReplyFile,
93
+ review_reply_summary_file: reviewReplySummaryFile,
94
+ }),
95
+ summaryTitle: "Codex Reply Summary",
96
+ verbose: stepContext.verbose,
97
+ });
98
+ return { completed: true };
99
+ },
100
+ },
101
+ {
102
+ id: "check_ready_to_merge",
103
+ async run(stepContext, stepParams) {
104
+ const readyToMerge = !stepContext.dryRun && existsSync(readyToMergeFile(stepParams.taskKey));
105
+ if (readyToMerge) {
106
+ printPanel("Ready To Merge", "Изменения готовы к merge\nФайл ready-to-merge.md создан.", "green");
107
+ }
108
+ return { completed: true, metadata: { readyToMerge } };
109
+ },
110
+ },
111
+ ],
112
+ };
113
+ }
114
+ async function runReviewFlowInternal(context, params, iteration) {
115
+ requireArtifacts(planArtifacts(params.taskKey), "Review mode requires plan artifacts from the planning phase.");
116
+ const result = await runFlow(createReviewFlowDefinition(iteration), context, params);
117
+ const readyToMerge = result.steps.find((step) => step.id === "check_ready_to_merge")?.result.metadata?.readyToMerge === true;
118
+ return { readyToMerge };
119
+ }
120
+ export async function runReviewFlow(context, params) {
121
+ const iteration = nextReviewIterationForTask(params.taskKey);
122
+ const result = await runReviewFlowInternal(context, params, iteration);
123
+ return { readyToMerge: result.readyToMerge, iteration };
124
+ }
@@ -0,0 +1,12 @@
1
+ import { planArtifacts, requireArtifacts } from "../../artifacts.js";
2
+ import { TEST_FIX_PROMPT_TEMPLATE, TEST_LINTER_FIX_PROMPT_TEMPLATE, formatPrompt } from "../../prompts.js";
3
+ import { runNode } from "../node-runner.js";
4
+ import { codexLocalPromptNode } from "../nodes/codex-local-prompt-node.js";
5
+ export async function runTestFixFlow(context, params) {
6
+ requireArtifacts(planArtifacts(params.taskKey), `${params.command} mode requires plan artifacts from the planning phase.`);
7
+ const prompt = formatPrompt(params.command === "test-fix" ? TEST_FIX_PROMPT_TEMPLATE : TEST_LINTER_FIX_PROMPT_TEMPLATE, params.extraPrompt);
8
+ await runNode(codexLocalPromptNode, context, {
9
+ prompt,
10
+ labelText: `Running Codex ${params.command} mode locally`,
11
+ });
12
+ }
@@ -0,0 +1,32 @@
1
+ import { planArtifacts, requireArtifacts } from "../../artifacts.js";
2
+ import { runFlow } from "../flow-runner.js";
3
+ import { runNode } from "../node-runner.js";
4
+ import { verifyBuildNode } from "../nodes/verify-build-node.js";
5
+ export const testFlowDefinition = {
6
+ kind: "test-flow",
7
+ version: 1,
8
+ steps: [
9
+ {
10
+ id: "verify_build",
11
+ async run(context, params) {
12
+ try {
13
+ await runNode(verifyBuildNode, context, {
14
+ dockerComposeFile: params.dockerComposeFile,
15
+ labelText: "Running build verification in isolated Docker",
16
+ });
17
+ }
18
+ catch (error) {
19
+ if (params.onVerifyBuildFailure) {
20
+ await params.onVerifyBuildFailure(String(error.output ?? ""));
21
+ }
22
+ throw error;
23
+ }
24
+ return { completed: true };
25
+ },
26
+ },
27
+ ],
28
+ };
29
+ export async function runTestFlow(context, params) {
30
+ requireArtifacts(planArtifacts(params.taskKey), "Test mode requires plan artifacts from the planning phase.");
31
+ await runFlow(testFlowDefinition, context, params);
32
+ }
@@ -0,0 +1,71 @@
1
+ import { buildFailureSummaryNode } from "./nodes/build-failure-summary-node.js";
2
+ import { claudePromptNode } from "./nodes/claude-prompt-node.js";
3
+ import { codexDockerPromptNode } from "./nodes/codex-docker-prompt-node.js";
4
+ import { codexLocalPromptNode } from "./nodes/codex-local-prompt-node.js";
5
+ import { commandCheckNode } from "./nodes/command-check-node.js";
6
+ import { fileCheckNode } from "./nodes/file-check-node.js";
7
+ import { jiraFetchNode } from "./nodes/jira-fetch-node.js";
8
+ import { planCodexNode } from "./nodes/plan-codex-node.js";
9
+ import { reviewClaudeNode } from "./nodes/review-claude-node.js";
10
+ import { reviewReplyCodexNode } from "./nodes/review-reply-codex-node.js";
11
+ import { summaryFileLoadNode } from "./nodes/summary-file-load-node.js";
12
+ import { verifyBuildNode } from "./nodes/verify-build-node.js";
13
+ const builtInNodes = {
14
+ "build-failure-summary": buildFailureSummaryNode,
15
+ "claude-prompt": claudePromptNode,
16
+ "codex-docker-prompt": codexDockerPromptNode,
17
+ "codex-local-prompt": codexLocalPromptNode,
18
+ "command-check": commandCheckNode,
19
+ "file-check": fileCheckNode,
20
+ "jira-fetch": jiraFetchNode,
21
+ "plan-codex": planCodexNode,
22
+ "review-claude": reviewClaudeNode,
23
+ "review-reply-codex": reviewReplyCodexNode,
24
+ "summary-file-load": summaryFileLoadNode,
25
+ "verify-build": verifyBuildNode,
26
+ };
27
+ const builtInNodeMetadata = {
28
+ "build-failure-summary": { kind: "build-failure-summary", version: 1, prompt: "forbidden", requiredParams: ["output"] },
29
+ "claude-prompt": { kind: "claude-prompt", version: 1, prompt: "required", requiredParams: ["labelText"] },
30
+ "codex-docker-prompt": {
31
+ kind: "codex-docker-prompt",
32
+ version: 1,
33
+ prompt: "required",
34
+ requiredParams: ["dockerComposeFile", "labelText"],
35
+ },
36
+ "codex-local-prompt": { kind: "codex-local-prompt", version: 1, prompt: "required", requiredParams: ["labelText"] },
37
+ "command-check": { kind: "command-check", version: 1, prompt: "forbidden", requiredParams: ["commands"] },
38
+ "file-check": { kind: "file-check", version: 1, prompt: "forbidden", requiredParams: ["path"] },
39
+ "jira-fetch": { kind: "jira-fetch", version: 1, prompt: "forbidden", requiredParams: ["jiraApiUrl", "outputFile"] },
40
+ "plan-codex": { kind: "plan-codex", version: 1, prompt: "forbidden", requiredParams: ["prompt", "requiredArtifacts"] },
41
+ "review-claude": {
42
+ kind: "review-claude",
43
+ version: 1,
44
+ prompt: "forbidden",
45
+ requiredParams: ["jiraTaskFile", "taskKey", "iteration", "claudeCmd"],
46
+ },
47
+ "review-reply-codex": {
48
+ kind: "review-reply-codex",
49
+ version: 1,
50
+ prompt: "forbidden",
51
+ requiredParams: ["jiraTaskFile", "taskKey", "iteration", "codexCmd"],
52
+ },
53
+ "summary-file-load": { kind: "summary-file-load", version: 1, prompt: "forbidden", requiredParams: ["path"] },
54
+ "verify-build": { kind: "verify-build", version: 1, prompt: "forbidden", requiredParams: ["dockerComposeFile", "labelText"] },
55
+ };
56
+ export function createNodeRegistry() {
57
+ return {
58
+ get(kind) {
59
+ return builtInNodes[kind];
60
+ },
61
+ getMeta(kind) {
62
+ return builtInNodeMetadata[kind];
63
+ },
64
+ has(kind) {
65
+ return kind in builtInNodes;
66
+ },
67
+ kinds() {
68
+ return Object.keys(builtInNodes);
69
+ },
70
+ };
71
+ }
@@ -0,0 +1,20 @@
1
+ import { setCurrentNode } from "../tui.js";
2
+ import { runNodeChecks } from "./checks.js";
3
+ export async function runNode(node, context, params, options = {}) {
4
+ setCurrentNode(node.kind);
5
+ try {
6
+ const result = await node.run(context, params);
7
+ if (!options.skipChecks) {
8
+ const checks = node.checks?.(context, params, result) ?? [];
9
+ runNodeChecks(checks);
10
+ }
11
+ return result;
12
+ }
13
+ finally {
14
+ setCurrentNode(null);
15
+ }
16
+ }
17
+ export async function runNodeByKind(kind, context, params, options = {}) {
18
+ const node = context.nodes.get(kind);
19
+ return runNode(node, context, params, options);
20
+ }
@@ -0,0 +1,71 @@
1
+ import { toExecutorContext } from "../types.js";
2
+ const DEFAULT_CLAUDE_MODEL = "haiku";
3
+ function truncateText(text, maxChars = 12000) {
4
+ return text.length <= maxChars ? text.trim() : text.trim().slice(-maxChars);
5
+ }
6
+ function fallbackBuildFailureSummary(output) {
7
+ const lines = output
8
+ .split(/\r?\n/)
9
+ .map((line) => line.trim())
10
+ .filter(Boolean);
11
+ const tail = lines.length > 0 ? lines.slice(-8) : ["No build output captured."];
12
+ return `Не удалось получить summary через Claude.\n\nПоследние строки лога:\n${tail.join("\n")}`;
13
+ }
14
+ function claudeModel(env) {
15
+ return env.CLAUDE_MODEL?.trim() || env.CLAUDE_SUMMARY_MODEL?.trim() || env.CLAUDE_REVIEW_MODEL?.trim() || DEFAULT_CLAUDE_MODEL;
16
+ }
17
+ export const buildFailureSummaryNode = {
18
+ kind: "build-failure-summary",
19
+ version: 1,
20
+ async run(context, params) {
21
+ if (!params.output.trim()) {
22
+ return {
23
+ value: {
24
+ summaryText: "Build verification failed, but no output was captured.",
25
+ },
26
+ };
27
+ }
28
+ let claudeCmd;
29
+ try {
30
+ claudeCmd = context.runtime.resolveCmd("claude", "CLAUDE_BIN");
31
+ }
32
+ catch {
33
+ return {
34
+ value: {
35
+ summaryText: fallbackBuildFailureSummary(params.output),
36
+ },
37
+ };
38
+ }
39
+ const model = claudeModel(context.env);
40
+ const prompt = "Ниже лог упавшей build verification.\n" +
41
+ "Сделай краткое резюме на русском языке, без воды.\n" +
42
+ "Нужно обязательно выделить:\n" +
43
+ "1. Где именно упало.\n" +
44
+ "2. Главную причину падения.\n" +
45
+ "3. Что нужно исправить дальше, если это очевидно.\n" +
46
+ "Ответ дай максимум 5 короткими пунктами.\n\n" +
47
+ `Лог:\n${truncateText(params.output)}`;
48
+ try {
49
+ const executor = context.executors.get("process");
50
+ const result = await executor.execute(toExecutorContext(context), {
51
+ argv: [claudeCmd, "--model", model, "-p", prompt],
52
+ env: { ...context.env },
53
+ dryRun: false,
54
+ verbose: false,
55
+ label: `claude:${model}`,
56
+ }, executor.defaultConfig);
57
+ return {
58
+ value: {
59
+ summaryText: result.output.trim() || fallbackBuildFailureSummary(params.output),
60
+ },
61
+ };
62
+ }
63
+ catch {
64
+ return {
65
+ value: {
66
+ summaryText: fallbackBuildFailureSummary(params.output),
67
+ },
68
+ };
69
+ }
70
+ },
71
+ };
@@ -0,0 +1,54 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { requireArtifacts } from "../../artifacts.js";
3
+ import { printInfo, printPrompt, printSummary } from "../../tui.js";
4
+ import { toExecutorContext } from "../types.js";
5
+ export const claudePromptNode = {
6
+ kind: "claude-prompt",
7
+ version: 1,
8
+ async run(context, params) {
9
+ printInfo(params.labelText);
10
+ printPrompt("Claude", params.prompt);
11
+ const executor = context.executors.get("claude");
12
+ const value = await executor.execute(toExecutorContext(context), {
13
+ prompt: params.prompt,
14
+ ...(params.command ? { command: params.command } : {}),
15
+ ...(params.model ? { model: params.model } : {}),
16
+ env: { ...context.env },
17
+ }, executor.defaultConfig);
18
+ const outputs = [
19
+ ...(params.requiredArtifacts ?? []).map((path) => ({ kind: "artifact", path, required: true })),
20
+ ...(params.outputFile ? [{ kind: "artifact", path: params.outputFile, required: true }] : []),
21
+ ];
22
+ if (!params.outputFile) {
23
+ return { value, outputs };
24
+ }
25
+ requireArtifacts([params.outputFile], params.missingArtifactsMessage ?? `Claude prompt did not produce ${params.outputFile}.`);
26
+ const artifactText = readFileSync(params.outputFile, "utf8").trim();
27
+ if (params.summaryTitle) {
28
+ printSummary(params.summaryTitle, artifactText);
29
+ }
30
+ return {
31
+ value: {
32
+ ...value,
33
+ artifactText,
34
+ },
35
+ outputs,
36
+ };
37
+ },
38
+ checks(_context, params) {
39
+ const requiredArtifacts = [
40
+ ...(params.requiredArtifacts ?? []),
41
+ ...(params.outputFile ? [params.outputFile] : []),
42
+ ];
43
+ if (requiredArtifacts.length === 0) {
44
+ return [];
45
+ }
46
+ return [
47
+ {
48
+ kind: "require-artifacts",
49
+ paths: requiredArtifacts,
50
+ message: params.missingArtifactsMessage ?? "Claude prompt node did not produce required artifacts.",
51
+ },
52
+ ];
53
+ },
54
+ };
@@ -0,0 +1,38 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { requireArtifacts } from "../../artifacts.js";
3
+ import { printInfo, printPrompt, printSummary } from "../../tui.js";
4
+ import { toExecutorContext } from "../types.js";
5
+ export const claudeSummaryNode = {
6
+ kind: "claude-summary",
7
+ version: 1,
8
+ async run(context, params) {
9
+ printInfo(`Preparing summary in ${params.outputFile}`);
10
+ printPrompt("Claude", params.prompt);
11
+ const executor = context.executors.get("claude");
12
+ const value = await executor.execute(toExecutorContext(context), {
13
+ prompt: params.prompt,
14
+ ...(params.model ? { model: params.model } : {}),
15
+ ...(params.claudeCmd ? { command: params.claudeCmd } : {}),
16
+ env: { ...context.env },
17
+ }, executor.defaultConfig);
18
+ requireArtifacts([params.outputFile], `Claude summary did not produce ${params.outputFile}.`);
19
+ const artifactText = readFileSync(params.outputFile, "utf8").trim();
20
+ printSummary(params.summaryTitle, artifactText);
21
+ return {
22
+ value: {
23
+ ...value,
24
+ artifactText,
25
+ },
26
+ outputs: [{ kind: "artifact", path: params.outputFile, required: true }],
27
+ };
28
+ },
29
+ checks(_context, params) {
30
+ return [
31
+ {
32
+ kind: "require-artifacts",
33
+ paths: [params.outputFile],
34
+ message: `Claude summary did not produce ${params.outputFile}.`,
35
+ },
36
+ ];
37
+ },
38
+ };
@@ -0,0 +1,32 @@
1
+ import { printInfo, printPrompt } from "../../tui.js";
2
+ import { toExecutorContext } from "../types.js";
3
+ export const codexDockerPromptNode = {
4
+ kind: "codex-docker-prompt",
5
+ version: 1,
6
+ async run(context, params) {
7
+ printInfo(params.labelText);
8
+ printPrompt("Codex", params.prompt);
9
+ const executor = context.executors.get("codex-docker");
10
+ const value = await executor.execute(toExecutorContext(context), {
11
+ dockerComposeFile: params.dockerComposeFile,
12
+ prompt: params.prompt,
13
+ ...(params.model ? { model: params.model } : {}),
14
+ }, executor.defaultConfig);
15
+ return {
16
+ value,
17
+ outputs: (params.requiredArtifacts ?? []).map((path) => ({ kind: "artifact", path, required: true })),
18
+ };
19
+ },
20
+ checks(_context, params) {
21
+ if (!params.requiredArtifacts || params.requiredArtifacts.length === 0) {
22
+ return [];
23
+ }
24
+ return [
25
+ {
26
+ kind: "require-artifacts",
27
+ paths: params.requiredArtifacts,
28
+ message: params.missingArtifactsMessage ?? "Codex docker node did not produce required artifacts.",
29
+ },
30
+ ];
31
+ },
32
+ };
@@ -0,0 +1,32 @@
1
+ import { printInfo, printPrompt } from "../../tui.js";
2
+ import { toExecutorContext } from "../types.js";
3
+ export const codexLocalPromptNode = {
4
+ kind: "codex-local-prompt",
5
+ version: 1,
6
+ async run(context, params) {
7
+ printInfo(params.labelText);
8
+ printPrompt("Codex", params.prompt);
9
+ const executor = context.executors.get("codex-local");
10
+ const value = await executor.execute(toExecutorContext(context), {
11
+ prompt: params.prompt,
12
+ ...(params.model ? { model: params.model } : {}),
13
+ env: { ...context.env },
14
+ }, executor.defaultConfig);
15
+ return {
16
+ value,
17
+ outputs: (params.requiredArtifacts ?? []).map((path) => ({ kind: "artifact", path, required: true })),
18
+ };
19
+ },
20
+ checks(_context, params) {
21
+ if (!params.requiredArtifacts || params.requiredArtifacts.length === 0) {
22
+ return [];
23
+ }
24
+ return [
25
+ {
26
+ kind: "require-artifacts",
27
+ paths: params.requiredArtifacts,
28
+ message: params.missingArtifactsMessage ?? "Codex local node did not produce required artifacts.",
29
+ },
30
+ ];
31
+ },
32
+ };
@@ -0,0 +1,10 @@
1
+ import { toExecutorContext } from "../types.js";
2
+ export const commandCheckNode = {
3
+ kind: "command-check",
4
+ version: 1,
5
+ async run(context, params) {
6
+ const executor = context.executors.get("command-check");
7
+ const value = await executor.execute(toExecutorContext(context), params, executor.defaultConfig);
8
+ return { value };
9
+ },
10
+ };
@@ -0,0 +1,15 @@
1
+ import { existsSync } from "node:fs";
2
+ import { printPanel } from "../../tui.js";
3
+ export const fileCheckNode = {
4
+ kind: "file-check",
5
+ version: 1,
6
+ async run(_context, params) {
7
+ const exists = existsSync(params.path);
8
+ if (exists && params.panelTitle && params.foundMessage) {
9
+ printPanel(params.panelTitle, params.foundMessage, params.tone ?? "green");
10
+ }
11
+ return {
12
+ value: { exists },
13
+ };
14
+ },
15
+ };
@@ -0,0 +1,16 @@
1
+ import { printInfo, printPrompt } from "../../tui.js";
2
+ import { toExecutorContext } from "../types.js";
3
+ export const implementCodexNode = {
4
+ kind: "implement-codex",
5
+ version: 1,
6
+ async run(context, params) {
7
+ printInfo(params.labelText);
8
+ printPrompt("Codex", params.prompt);
9
+ const executor = context.executors.get("codex-docker");
10
+ const value = await executor.execute(toExecutorContext(context), {
11
+ dockerComposeFile: params.dockerComposeFile,
12
+ prompt: params.prompt,
13
+ }, executor.defaultConfig);
14
+ return { value };
15
+ },
16
+ };
@@ -0,0 +1,25 @@
1
+ import { toExecutorContext } from "../types.js";
2
+ export const jiraFetchNode = {
3
+ kind: "jira-fetch",
4
+ version: 1,
5
+ async run(context, params) {
6
+ const executor = context.executors.get("jira-fetch");
7
+ const value = await executor.execute(toExecutorContext(context), {
8
+ jiraApiUrl: params.jiraApiUrl,
9
+ outputFile: params.outputFile,
10
+ }, executor.defaultConfig);
11
+ return {
12
+ value,
13
+ outputs: [{ kind: "file", path: params.outputFile, required: true }],
14
+ };
15
+ },
16
+ checks(_context, params) {
17
+ return [
18
+ {
19
+ kind: "require-file",
20
+ path: params.outputFile,
21
+ message: `Jira fetch node did not produce ${params.outputFile}.`,
22
+ },
23
+ ];
24
+ },
25
+ };
@@ -0,0 +1,32 @@
1
+ import { printInfo, printPrompt } from "../../tui.js";
2
+ import { toExecutorContext } from "../types.js";
3
+ export const planCodexNode = {
4
+ kind: "plan-codex",
5
+ version: 1,
6
+ async run(context, params) {
7
+ printInfo("Running Codex planning mode");
8
+ printPrompt("Codex", params.prompt);
9
+ const executor = context.executors.get("codex-local");
10
+ const input = {
11
+ prompt: params.prompt,
12
+ env: { ...context.env },
13
+ };
14
+ if (params.command) {
15
+ input.command = params.command;
16
+ }
17
+ const value = await executor.execute(toExecutorContext(context), input, executor.defaultConfig);
18
+ return {
19
+ value,
20
+ outputs: params.requiredArtifacts.map((path) => ({ kind: "artifact", path, required: true })),
21
+ };
22
+ },
23
+ checks(_context, params) {
24
+ return [
25
+ {
26
+ kind: "require-artifacts",
27
+ paths: params.requiredArtifacts,
28
+ message: "Plan mode did not produce the required artifacts.",
29
+ },
30
+ ];
31
+ },
32
+ };