agentweaver 0.1.9 → 0.1.11

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 (101) hide show
  1. package/README.md +226 -200
  2. package/dist/artifacts.js +101 -56
  3. package/dist/errors.js +7 -0
  4. package/dist/executors/{codex-local-executor.js → codex-executor.js} +4 -4
  5. package/dist/executors/configs/{codex-local-config.js → codex-config.js} +1 -1
  6. package/dist/executors/configs/jira-fetch-config.js +2 -0
  7. package/dist/executors/configs/telegram-notifier-config.js +3 -0
  8. package/dist/executors/fetch-gitlab-diff-executor.js +1 -1
  9. package/dist/executors/fetch-gitlab-review-executor.js +1 -1
  10. package/dist/executors/git-commit-executor.js +25 -0
  11. package/dist/executors/telegram-notifier-executor.js +54 -0
  12. package/dist/flow-state.js +46 -1
  13. package/dist/gitlab.js +13 -8
  14. package/dist/index.js +507 -520
  15. package/dist/interactive-ui.js +495 -87
  16. package/dist/jira.js +52 -5
  17. package/dist/pipeline/auto-flow.js +6 -6
  18. package/dist/pipeline/context.js +1 -0
  19. package/dist/pipeline/declarative-flows.js +7 -4
  20. package/dist/pipeline/flow-catalog.js +60 -23
  21. package/dist/pipeline/flow-model-settings.js +77 -0
  22. package/dist/pipeline/flow-specs/auto-common.json +446 -0
  23. package/dist/pipeline/flow-specs/auto-golang.json +563 -0
  24. package/dist/pipeline/flow-specs/{bug-analyze.json → bugz/bug-analyze.json} +43 -25
  25. package/dist/pipeline/flow-specs/{bug-fix.json → bugz/bug-fix.json} +5 -4
  26. package/dist/pipeline/flow-specs/git-commit.json +196 -0
  27. package/dist/pipeline/flow-specs/{gitlab-diff-review.json → gitlab/gitlab-diff-review.json} +20 -50
  28. package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +165 -0
  29. package/dist/pipeline/flow-specs/{mr-description.json → gitlab/mr-description.json} +17 -10
  30. package/dist/pipeline/flow-specs/{run-go-linter-loop.json → go/run-go-linter-loop.json} +40 -14
  31. package/dist/pipeline/flow-specs/{run-go-tests-loop.json → go/run-go-tests-loop.json} +40 -14
  32. package/dist/pipeline/flow-specs/implement.json +5 -4
  33. package/dist/pipeline/flow-specs/plan.json +40 -148
  34. package/dist/pipeline/flow-specs/{review-fix.json → review/review-fix.json} +73 -13
  35. package/dist/pipeline/flow-specs/review/review-loop.json +280 -0
  36. package/dist/pipeline/flow-specs/review/review-project.json +87 -0
  37. package/dist/pipeline/flow-specs/review/review.json +126 -0
  38. package/dist/pipeline/flow-specs/task-describe.json +191 -11
  39. package/dist/pipeline/launch-profile-config.js +38 -0
  40. package/dist/pipeline/node-registry.js +75 -45
  41. package/dist/pipeline/nodes/build-failure-summary-node.js +16 -29
  42. package/dist/pipeline/nodes/build-review-fix-prompt-node.js +36 -0
  43. package/dist/pipeline/nodes/codex-prompt-node.js +41 -0
  44. package/dist/pipeline/nodes/commit-message-form-node.js +79 -0
  45. package/dist/pipeline/nodes/git-commit-form-node.js +138 -0
  46. package/dist/pipeline/nodes/git-commit-node.js +28 -0
  47. package/dist/pipeline/nodes/git-status-node.js +221 -0
  48. package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +10 -6
  49. package/dist/pipeline/nodes/jira-context-node.js +10 -0
  50. package/dist/pipeline/nodes/llm-prompt-node.js +62 -0
  51. package/dist/pipeline/nodes/plan-codex-node.js +1 -1
  52. package/dist/pipeline/nodes/read-file-node.js +11 -0
  53. package/dist/pipeline/nodes/review-findings-form-node.js +18 -14
  54. package/dist/pipeline/nodes/select-files-form-node.js +72 -0
  55. package/dist/pipeline/nodes/telegram-notifier-node.js +28 -0
  56. package/dist/pipeline/nodes/user-input-node.js +29 -8
  57. package/dist/pipeline/nodes/write-selection-file-node.js +46 -0
  58. package/dist/pipeline/prompt-registry.js +2 -4
  59. package/dist/pipeline/prompt-runtime.js +13 -3
  60. package/dist/pipeline/registry.js +6 -8
  61. package/dist/pipeline/spec-compiler.js +5 -0
  62. package/dist/pipeline/spec-loader.js +18 -7
  63. package/dist/pipeline/spec-types.js +7 -3
  64. package/dist/pipeline/spec-validator.js +4 -0
  65. package/dist/pipeline/types.js +1 -0
  66. package/dist/pipeline/value-resolver.js +40 -38
  67. package/dist/prompts.js +104 -110
  68. package/dist/runtime/agentweaver-home.js +8 -0
  69. package/dist/runtime/command-resolution.js +0 -38
  70. package/dist/runtime/env-loader.js +43 -0
  71. package/dist/runtime/process-runner.js +45 -1
  72. package/dist/structured-artifact-schema-registry.js +53 -0
  73. package/dist/structured-artifact-schemas.json +0 -20
  74. package/dist/structured-artifacts.js +3 -43
  75. package/dist/user-input.js +30 -2
  76. package/package.json +2 -6
  77. package/Dockerfile.codex +0 -56
  78. package/dist/executors/claude-executor.js +0 -46
  79. package/dist/executors/codex-docker-executor.js +0 -27
  80. package/dist/executors/configs/claude-config.js +0 -12
  81. package/dist/executors/configs/codex-docker-config.js +0 -10
  82. package/dist/executors/configs/verify-build-config.js +0 -7
  83. package/dist/executors/verify-build-executor.js +0 -123
  84. package/dist/pipeline/flow-specs/auto.json +0 -979
  85. package/dist/pipeline/flow-specs/gitlab-review.json +0 -317
  86. package/dist/pipeline/flow-specs/plan-opencode.json +0 -603
  87. package/dist/pipeline/flow-specs/preflight.json +0 -206
  88. package/dist/pipeline/flow-specs/review-project.json +0 -243
  89. package/dist/pipeline/flow-specs/review.json +0 -312
  90. package/dist/pipeline/flow-specs/run-linter-loop.json +0 -155
  91. package/dist/pipeline/flow-specs/run-tests-loop.json +0 -155
  92. package/dist/pipeline/flows/preflight-flow.js +0 -19
  93. package/dist/pipeline/nodes/claude-prompt-node.js +0 -54
  94. package/dist/pipeline/nodes/codex-docker-prompt-node.js +0 -32
  95. package/dist/pipeline/nodes/codex-local-prompt-node.js +0 -32
  96. package/dist/pipeline/nodes/review-claude-node.js +0 -38
  97. package/dist/pipeline/nodes/review-reply-codex-node.js +0 -40
  98. package/dist/pipeline/nodes/verify-build-node.js +0 -15
  99. package/dist/runtime/docker-runtime.js +0 -51
  100. package/docker-compose.yml +0 -445
  101. package/verify_build.sh +0 -105
package/dist/artifacts.js CHANGED
@@ -1,9 +1,8 @@
1
- import { existsSync, mkdirSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readdirSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
4
  import { TaskRunnerError } from "./errors.js";
5
5
  export const REVIEW_FILE_RE = /^review-(.+)-(\d+)\.md$/;
6
- export const REVIEW_REPLY_FILE_RE = /^review-reply-(.+)-(\d+)\.md$/;
7
6
  export const READY_TO_MERGE_FILE = "ready-to-merge.md";
8
7
  export function scopesRootDir() {
9
8
  return path.join(process.cwd(), ".agentweaver", "scopes");
@@ -47,17 +46,51 @@ export function artifactFile(prefix, taskKey, iteration) {
47
46
  export function artifactJsonFile(prefix, taskKey, iteration) {
48
47
  return taskArtifactsFile(taskKey, `${prefix}-${taskKey}-${iteration}.json`);
49
48
  }
50
- export function designFile(taskKey) {
51
- return artifactFile("design", taskKey, 1);
49
+ function escapeRegExp(value) {
50
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
52
51
  }
53
- export function designJsonFile(taskKey) {
54
- return artifactJsonFile("design", taskKey, 1);
52
+ function latestVersionedArtifactIteration(taskKey, prefix, extension, directory) {
53
+ if (!existsSync(directory)) {
54
+ return null;
55
+ }
56
+ const re = new RegExp(`^${escapeRegExp(prefix)}-${escapeRegExp(taskKey)}-(\\d+)\\.${extension}$`);
57
+ let maxIteration = null;
58
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
59
+ if (!entry.isFile()) {
60
+ continue;
61
+ }
62
+ const match = re.exec(entry.name);
63
+ if (!match) {
64
+ continue;
65
+ }
66
+ const currentIteration = Number.parseInt(match[1] ?? "0", 10);
67
+ maxIteration = maxIteration === null ? currentIteration : Math.max(maxIteration, currentIteration);
68
+ }
69
+ return maxIteration;
70
+ }
71
+ export function latestArtifactIteration(taskKey, prefix, extension = "md") {
72
+ return latestVersionedArtifactIteration(taskKey, prefix, extension, extension === "md" ? taskWorkspaceDir(taskKey) : taskArtifactsDir(taskKey));
73
+ }
74
+ export function nextArtifactIteration(taskKey, prefix, extension = "md") {
75
+ return (latestArtifactIteration(taskKey, prefix, extension) ?? 0) + 1;
76
+ }
77
+ function versionedMarkdownArtifactFile(taskKey, prefix, iteration) {
78
+ return artifactFile(prefix, taskKey, iteration ?? (latestArtifactIteration(taskKey, prefix, "md") ?? 1));
55
79
  }
56
- export function planFile(taskKey) {
57
- return artifactFile("plan", taskKey, 1);
80
+ function versionedJsonArtifactFile(taskKey, prefix, iteration) {
81
+ return artifactJsonFile(prefix, taskKey, iteration ?? (latestArtifactIteration(taskKey, prefix, "json") ?? 1));
58
82
  }
59
- export function planJsonFile(taskKey) {
60
- return artifactJsonFile("plan", taskKey, 1);
83
+ export function designFile(taskKey, iteration) {
84
+ return versionedMarkdownArtifactFile(taskKey, "design", iteration);
85
+ }
86
+ export function designJsonFile(taskKey, iteration) {
87
+ return versionedJsonArtifactFile(taskKey, "design", iteration);
88
+ }
89
+ export function planFile(taskKey, iteration) {
90
+ return versionedMarkdownArtifactFile(taskKey, "plan", iteration);
91
+ }
92
+ export function planJsonFile(taskKey, iteration) {
93
+ return versionedJsonArtifactFile(taskKey, "plan", iteration);
61
94
  }
62
95
  export function planningQuestionsJsonFile(taskKey) {
63
96
  return taskArtifactsFile(taskKey, `planning-questions-${taskKey}.json`);
@@ -65,35 +98,35 @@ export function planningQuestionsJsonFile(taskKey) {
65
98
  export function planningAnswersJsonFile(taskKey) {
66
99
  return taskArtifactsFile(taskKey, `planning-answers-${taskKey}.json`);
67
100
  }
68
- export function bugAnalyzeFile(taskKey) {
69
- return taskWorkspaceFile(taskKey, `bug-analyze-${taskKey}.md`);
101
+ export function bugAnalyzeFile(taskKey, iteration) {
102
+ return versionedMarkdownArtifactFile(taskKey, "bug-analyze", iteration);
70
103
  }
71
- export function bugAnalyzeJsonFile(taskKey) {
72
- return taskArtifactsFile(taskKey, `bug-analyze-${taskKey}.json`);
104
+ export function bugAnalyzeJsonFile(taskKey, iteration) {
105
+ return versionedJsonArtifactFile(taskKey, "bug-analyze", iteration);
73
106
  }
74
- export function bugFixDesignFile(taskKey) {
75
- return taskWorkspaceFile(taskKey, `bug-fix-design-${taskKey}.md`);
107
+ export function bugFixDesignFile(taskKey, iteration) {
108
+ return versionedMarkdownArtifactFile(taskKey, "bug-fix-design", iteration);
76
109
  }
77
- export function bugFixDesignJsonFile(taskKey) {
78
- return taskArtifactsFile(taskKey, `bug-fix-design-${taskKey}.json`);
110
+ export function bugFixDesignJsonFile(taskKey, iteration) {
111
+ return versionedJsonArtifactFile(taskKey, "bug-fix-design", iteration);
79
112
  }
80
- export function bugFixPlanFile(taskKey) {
81
- return taskWorkspaceFile(taskKey, `bug-fix-plan-${taskKey}.md`);
113
+ export function bugFixPlanFile(taskKey, iteration) {
114
+ return versionedMarkdownArtifactFile(taskKey, "bug-fix-plan", iteration);
82
115
  }
83
- export function bugFixPlanJsonFile(taskKey) {
84
- return taskArtifactsFile(taskKey, `bug-fix-plan-${taskKey}.json`);
116
+ export function bugFixPlanJsonFile(taskKey, iteration) {
117
+ return versionedJsonArtifactFile(taskKey, "bug-fix-plan", iteration);
85
118
  }
86
- export function qaFile(taskKey) {
87
- return artifactFile("qa", taskKey, 1);
119
+ export function qaFile(taskKey, iteration) {
120
+ return versionedMarkdownArtifactFile(taskKey, "qa", iteration);
88
121
  }
89
- export function qaJsonFile(taskKey) {
90
- return artifactJsonFile("qa", taskKey, 1);
122
+ export function qaJsonFile(taskKey, iteration) {
123
+ return versionedJsonArtifactFile(taskKey, "qa", iteration);
91
124
  }
92
- export function taskSummaryFile(taskKey) {
93
- return artifactFile("task", taskKey, 1);
125
+ export function taskSummaryFile(taskKey, iteration) {
126
+ return versionedMarkdownArtifactFile(taskKey, "task", iteration);
94
127
  }
95
- export function taskSummaryJsonFile(taskKey) {
96
- return artifactJsonFile("task", taskKey, 1);
128
+ export function taskSummaryJsonFile(taskKey, iteration) {
129
+ return versionedJsonArtifactFile(taskKey, "task", iteration);
97
130
  }
98
131
  export function readyToMergeFile(taskKey) {
99
132
  return taskWorkspaceFile(taskKey, READY_TO_MERGE_FILE);
@@ -110,41 +143,59 @@ export function jiraAttachmentsManifestFile(taskKey) {
110
143
  export function jiraAttachmentsContextFile(taskKey) {
111
144
  return taskWorkspaceFile(taskKey, `jira-attachments-context-${taskKey}.txt`);
112
145
  }
113
- export function jiraDescriptionFile(taskKey) {
114
- return taskWorkspaceFile(taskKey, `jira-${taskKey}-description.md`);
146
+ export function jiraDescriptionFile(taskKey, iteration) {
147
+ return versionedMarkdownArtifactFile(taskKey, "jira-description", iteration);
148
+ }
149
+ export function jiraDescriptionJsonFile(taskKey, iteration) {
150
+ return versionedJsonArtifactFile(taskKey, "jira-description", iteration);
115
151
  }
116
- export function jiraDescriptionJsonFile(taskKey) {
117
- return taskArtifactsFile(taskKey, `jira-${taskKey}-description.json`);
152
+ export function taskDescribeInputJsonFile(taskKey) {
153
+ return taskArtifactsFile(taskKey, `task-describe-input-${taskKey}.json`);
118
154
  }
119
- export function mrDescriptionFile(taskKey) {
120
- return taskWorkspaceFile(taskKey, `mr-description-${taskKey}.md`);
155
+ export function gitStatusJsonFile(taskKey) {
156
+ return taskArtifactsFile(taskKey, `git-status-${taskKey}.json`);
121
157
  }
122
- export function mrDescriptionJsonFile(taskKey) {
123
- return taskArtifactsFile(taskKey, `mr-description-${taskKey}.json`);
158
+ export function gitDiffFile(taskKey) {
159
+ return taskWorkspaceFile(taskKey, `git-diff-${taskKey}.txt`);
124
160
  }
125
- export function gitlabReviewFile(taskKey) {
126
- return taskWorkspaceFile(taskKey, `gitlab-review-${taskKey}.md`);
161
+ export function gitCommitMessageJsonFile(taskKey) {
162
+ return taskArtifactsFile(taskKey, `git-commit-message-${taskKey}.json`);
127
163
  }
128
- export function gitlabReviewJsonFile(taskKey) {
129
- return taskArtifactsFile(taskKey, `gitlab-review-${taskKey}.json`);
164
+ export function gitCommitInputJsonFile(taskKey) {
165
+ return taskArtifactsFile(taskKey, `git-commit-input-${taskKey}.json`);
166
+ }
167
+ export function selectFilesOutputJsonFile(taskKey) {
168
+ return taskArtifactsFile(taskKey, `select-files-output-${taskKey}.json`);
169
+ }
170
+ export function commitMessageOutputJsonFile(taskKey) {
171
+ return taskArtifactsFile(taskKey, `commit-message-output-${taskKey}.json`);
172
+ }
173
+ export function mrDescriptionFile(taskKey, iteration) {
174
+ return versionedMarkdownArtifactFile(taskKey, "mr-description", iteration);
175
+ }
176
+ export function mrDescriptionJsonFile(taskKey, iteration) {
177
+ return versionedJsonArtifactFile(taskKey, "mr-description", iteration);
178
+ }
179
+ export function gitlabReviewFile(taskKey, iteration) {
180
+ return versionedMarkdownArtifactFile(taskKey, "gitlab-review", iteration);
181
+ }
182
+ export function gitlabReviewJsonFile(taskKey, iteration) {
183
+ return versionedJsonArtifactFile(taskKey, "gitlab-review", iteration);
130
184
  }
131
185
  export function gitlabReviewInputJsonFile(taskKey) {
132
186
  return taskArtifactsFile(taskKey, `gitlab-review-input-${taskKey}.json`);
133
187
  }
134
- export function gitlabDiffFile(taskKey) {
135
- return taskWorkspaceFile(taskKey, `gitlab-diff-${taskKey}.md`);
188
+ export function gitlabDiffFile(taskKey, iteration) {
189
+ return versionedMarkdownArtifactFile(taskKey, "gitlab-diff", iteration);
136
190
  }
137
- export function gitlabDiffJsonFile(taskKey) {
138
- return taskArtifactsFile(taskKey, `gitlab-diff-${taskKey}.json`);
191
+ export function gitlabDiffJsonFile(taskKey, iteration) {
192
+ return versionedJsonArtifactFile(taskKey, "gitlab-diff", iteration);
139
193
  }
140
194
  export function gitlabDiffReviewInputJsonFile(taskKey) {
141
195
  return taskArtifactsFile(taskKey, `gitlab-diff-review-input-${taskKey}.json`);
142
196
  }
143
- export function autoStateFile(taskKey) {
144
- return taskArtifactsFile(taskKey, `.agentweaver-state-${taskKey}.json`);
145
- }
146
197
  export function flowStateFile(scopeKey, flowId) {
147
- return scopeArtifactsFile(scopeKey, `.agentweaver-flow-state-${flowId}.json`);
198
+ return scopeArtifactsFile(scopeKey, `.agentweaver-flow-state-${encodeURIComponent(flowId)}.json`);
148
199
  }
149
200
  export function planArtifacts(taskKey) {
150
201
  return [designFile(taskKey), designJsonFile(taskKey), planFile(taskKey), planJsonFile(taskKey), qaFile(taskKey), qaJsonFile(taskKey)];
@@ -165,12 +216,6 @@ export function reviewFile(taskKey, iteration) {
165
216
  export function reviewJsonFile(taskKey, iteration) {
166
217
  return artifactJsonFile("review", taskKey, iteration);
167
218
  }
168
- export function reviewReplyFile(taskKey, iteration) {
169
- return artifactFile("review-reply", taskKey, iteration);
170
- }
171
- export function reviewReplyJsonFile(taskKey, iteration) {
172
- return artifactJsonFile("review-reply", taskKey, iteration);
173
- }
174
219
  export function reviewFixFile(taskKey, iteration) {
175
220
  return artifactFile("review-fix", taskKey, iteration);
176
221
  }
package/dist/errors.js CHANGED
@@ -4,3 +4,10 @@ export class TaskRunnerError extends Error {
4
4
  this.name = "TaskRunnerError";
5
5
  }
6
6
  }
7
+ export class FlowInterruptedError extends TaskRunnerError {
8
+ returnCode = 130;
9
+ constructor(message = "Flow interrupted by user.") {
10
+ super(message);
11
+ this.name = "FlowInterruptedError";
12
+ }
13
+ }
@@ -1,12 +1,12 @@
1
- import { codexLocalExecutorDefaultConfig } from "./configs/codex-local-config.js";
1
+ import { codexExecutorDefaultConfig } from "./configs/codex-config.js";
2
2
  import { processExecutor } from "./process-executor.js";
3
3
  function resolveModel(config, env) {
4
4
  return env[config.modelEnvVar]?.trim() || config.defaultModel;
5
5
  }
6
- export const codexLocalExecutor = {
7
- kind: "codex-local",
6
+ export const codexExecutor = {
7
+ kind: "codex",
8
8
  version: 1,
9
- defaultConfig: codexLocalExecutorDefaultConfig,
9
+ defaultConfig: codexExecutorDefaultConfig,
10
10
  async execute(context, input, config) {
11
11
  const env = input.env ?? context.env;
12
12
  const command = input.command ?? context.runtime.resolveCmd(config.defaultCommand, config.commandEnvVar);
@@ -1,4 +1,4 @@
1
- export const codexLocalExecutorDefaultConfig = {
1
+ export const codexExecutorDefaultConfig = {
2
2
  commandEnvVar: "CODEX_BIN",
3
3
  defaultCommand: "codex",
4
4
  modelEnvVar: "CODEX_MODEL",
@@ -1,4 +1,6 @@
1
1
  export const jiraFetchExecutorDefaultConfig = {
2
2
  authEnvVar: "JIRA_API_KEY",
3
+ usernameEnvVar: "JIRA_USERNAME",
4
+ authModeEnvVar: "JIRA_AUTH_MODE",
3
5
  acceptHeader: "application/json",
4
6
  };
@@ -0,0 +1,3 @@
1
+ export const telegramNotifierExecutorDefaultConfig = {
2
+ printFailureOutput: false,
3
+ };
@@ -15,7 +15,7 @@ export const fetchGitLabDiffExecutor = {
15
15
  context.ui.writeStdout(`Saving GitLab diff markdown to: ${input.outputFile}\n`);
16
16
  context.ui.writeStdout(`Saving GitLab diff JSON to: ${input.outputJsonFile}\n`);
17
17
  }
18
- const artifact = await fetchGitLabMergeRequestDiff(input.mergeRequestUrl, input.outputFile, input.outputJsonFile);
18
+ const artifact = await fetchGitLabMergeRequestDiff(input.mergeRequestUrl, input.outputFile, input.outputJsonFile, context.mdLang);
19
19
  return {
20
20
  outputFile: input.outputFile,
21
21
  outputJsonFile: input.outputJsonFile,
@@ -14,7 +14,7 @@ export const fetchGitLabReviewExecutor = {
14
14
  context.ui.writeStdout(`Saving GitLab review markdown to: ${input.outputFile}\n`);
15
15
  context.ui.writeStdout(`Saving GitLab review JSON to: ${input.outputJsonFile}\n`);
16
16
  }
17
- const artifact = await fetchGitLabReview(input.mergeRequestUrl, input.outputFile, input.outputJsonFile);
17
+ const artifact = await fetchGitLabReview(input.mergeRequestUrl, input.outputFile, input.outputJsonFile, context.mdLang);
18
18
  return {
19
19
  outputFile: input.outputFile,
20
20
  outputJsonFile: input.outputJsonFile,
@@ -0,0 +1,25 @@
1
+ export const gitCommitExecutor = {
2
+ kind: "git-commit",
3
+ version: 1,
4
+ defaultConfig: {},
5
+ async execute(context, input) {
6
+ if (input.files.length > 0) {
7
+ await context.runtime.runCommand(["git", "add", ...input.files], {
8
+ dryRun: context.dryRun,
9
+ verbose: context.verbose,
10
+ label: "git add",
11
+ });
12
+ }
13
+ const commitArgs = input.editEnabled
14
+ ? ["git", "commit", "-e", "-m", input.message]
15
+ : ["git", "commit", "-m", input.message];
16
+ const output = await context.runtime.runCommand(commitArgs, {
17
+ dryRun: context.dryRun,
18
+ verbose: context.verbose,
19
+ label: "git commit",
20
+ });
21
+ const match = output.match(/\[\S+ ([0-9a-f]{7,40})\]/);
22
+ const commitHash = match?.[1] ?? null;
23
+ return { output, commitHash };
24
+ },
25
+ };
@@ -0,0 +1,54 @@
1
+ import { telegramNotifierExecutorDefaultConfig } from "./configs/telegram-notifier-config.js";
2
+ export const telegramNotifierExecutor = {
3
+ kind: "telegram-notifier",
4
+ version: 1,
5
+ defaultConfig: telegramNotifierExecutorDefaultConfig,
6
+ async execute(context, input, config) {
7
+ const botToken = context.env.TELEGRAM_BOT_TOKEN;
8
+ if (!botToken) {
9
+ context.ui.writeStderr(`Telegram notifier error: BOT_TOKEN environment variable is not set\n`);
10
+ return { success: false };
11
+ }
12
+ const chatId = context.env.TELEGRAM_CHAT_ID ?? input.chatId;
13
+ if (!chatId) {
14
+ context.ui.writeStderr(`Telegram notifier error: chatId is required (set TELEGRAM_CHAT_ID env or pass in input)\n`);
15
+ return { success: false };
16
+ }
17
+ try {
18
+ const response = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
19
+ method: "POST",
20
+ headers: {
21
+ "Content-Type": "application/json",
22
+ },
23
+ body: JSON.stringify({
24
+ chat_id: chatId,
25
+ text: input.text,
26
+ }),
27
+ });
28
+ if (!response.ok) {
29
+ const errorText = await response.text();
30
+ if (config.printFailureOutput) {
31
+ context.ui.writeStderr(`Telegram API error: ${response.status} ${response.statusText}\n${errorText}\n`);
32
+ }
33
+ return { success: false };
34
+ }
35
+ const data = (await response.json());
36
+ if (!data.ok) {
37
+ context.ui.writeStderr(`Telegram API error: ${JSON.stringify(data)}\n`);
38
+ return { success: false };
39
+ }
40
+ const messageId = data.result?.message_id;
41
+ if (messageId !== undefined) {
42
+ return { success: true, messageId };
43
+ }
44
+ return { success: true };
45
+ }
46
+ catch (error) {
47
+ const errorMessage = error instanceof Error ? error.message : String(error);
48
+ if (config.printFailureOutput) {
49
+ context.ui.writeStderr(`Telegram notifier error: ${errorMessage}\n`);
50
+ }
51
+ return { success: false };
52
+ }
53
+ },
54
+ };
@@ -29,14 +29,16 @@ export function stripExecutionStatePayload(executionState) {
29
29
  })),
30
30
  };
31
31
  }
32
- export function createFlowRunState(scopeKey, flowId, executionState) {
32
+ export function createFlowRunState(scopeKey, flowId, executionState, jiraRef, launchProfile) {
33
33
  return {
34
34
  schemaVersion: FLOW_STATE_SCHEMA_VERSION,
35
35
  flowId,
36
36
  scopeKey,
37
+ ...(jiraRef ? { jiraRef } : {}),
37
38
  status: "pending",
38
39
  currentStep: null,
39
40
  updatedAt: nowIso8601(),
41
+ ...(launchProfile ? { launchProfile } : {}),
40
42
  executionState: stripExecutionStatePayload(executionState),
41
43
  };
42
44
  }
@@ -120,6 +122,49 @@ function normalizePhaseState(phase) {
120
122
  steps: normalizedSteps,
121
123
  };
122
124
  }
125
+ function createPendingPhaseState(phase) {
126
+ return {
127
+ id: phase.id,
128
+ status: "pending",
129
+ repeatVars: { ...phase.repeatVars },
130
+ steps: phase.steps.map((step) => ({
131
+ id: step.id,
132
+ status: "pending",
133
+ })),
134
+ };
135
+ }
136
+ export function rewindFlowRunStateToPhase(state, orderedPhases, targetPhaseId) {
137
+ const targetIndex = orderedPhases.findIndex((phase) => phase.id === targetPhaseId);
138
+ if (targetIndex < 0) {
139
+ throw new TaskRunnerError(`Unknown flow phase '${targetPhaseId}'.`);
140
+ }
141
+ const existingPhaseStates = new Map(state.executionState.phases.map((phase) => [phase.id, phase]));
142
+ const rewoundPhases = [];
143
+ for (const [index, phase] of orderedPhases.entries()) {
144
+ if (index < targetIndex) {
145
+ const existingPhaseState = existingPhaseStates.get(phase.id);
146
+ if (!existingPhaseState) {
147
+ throw new TaskRunnerError(`Cannot restart from phase '${targetPhaseId}' because earlier phase '${phase.id}' has no persisted execution state.`);
148
+ }
149
+ if (existingPhaseState.status !== "done" && existingPhaseState.status !== "skipped") {
150
+ throw new TaskRunnerError(`Cannot restart from phase '${targetPhaseId}' because earlier phase '${phase.id}' is not completed in persisted state.`);
151
+ }
152
+ rewoundPhases.push(existingPhaseState);
153
+ continue;
154
+ }
155
+ rewoundPhases.push(createPendingPhaseState(phase));
156
+ }
157
+ state.status = "pending";
158
+ state.currentStep = null;
159
+ state.lastError = null;
160
+ state.executionState = {
161
+ ...state.executionState,
162
+ terminated: false,
163
+ phases: rewoundPhases,
164
+ };
165
+ delete state.executionState.terminationReason;
166
+ return state;
167
+ }
123
168
  export function prepareFlowStateForResume(state) {
124
169
  state.status = "pending";
125
170
  state.lastError = null;
package/dist/gitlab.js CHANGED
@@ -3,6 +3,9 @@ import { writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { TaskRunnerError } from "./errors.js";
5
5
  const MERGE_REQUEST_PATH_RE = /^(?<projectPath>.+?)\/-\/merge_requests\/(?<iid>\d+)(?:\/.*)?$/;
6
+ function normalizeMarkdownLanguage(mdLang) {
7
+ return mdLang === "en" ? "en" : "ru";
8
+ }
6
9
  function normalizeUrl(value) {
7
10
  return value.trim().replace(/\/+$/, "");
8
11
  }
@@ -118,7 +121,8 @@ function normalizeDiscussionNotes(discussions) {
118
121
  }));
119
122
  });
120
123
  }
121
- function buildGitLabReviewMarkdown(artifact) {
124
+ function buildGitLabReviewMarkdown(artifact, mdLang) {
125
+ const lang = normalizeMarkdownLanguage(mdLang);
122
126
  const lines = [
123
127
  "# GitLab Review",
124
128
  "",
@@ -130,7 +134,7 @@ function buildGitLabReviewMarkdown(artifact) {
130
134
  "",
131
135
  ];
132
136
  if (artifact.comments.length === 0) {
133
- lines.push("Код-ревью комментариев не найдено.");
137
+ lines.push(lang === "en" ? "No code review comments found." : "Код-ревью комментариев не найдено.");
134
138
  return lines.join("\n");
135
139
  }
136
140
  artifact.comments.forEach((comment, index) => {
@@ -243,7 +247,8 @@ async function fetchMergeRequestDiffs(target, token) {
243
247
  page = chunk.nextPage;
244
248
  }
245
249
  }
246
- function buildGitLabMergeRequestDiffMarkdown(artifact) {
250
+ function buildGitLabMergeRequestDiffMarkdown(artifact, mdLang) {
251
+ const lang = normalizeMarkdownLanguage(mdLang);
247
252
  const lines = [
248
253
  "# GitLab MR Diff",
249
254
  "",
@@ -265,7 +270,7 @@ function buildGitLabMergeRequestDiffMarkdown(artifact) {
265
270
  lines.push("## Description", "", description, "");
266
271
  }
267
272
  if (artifact.files.length === 0) {
268
- lines.push("Изменений в diff не найдено.");
273
+ lines.push(lang === "en" ? "No changes found in the diff." : "Изменений в diff не найдено.");
269
274
  return lines.join("\n");
270
275
  }
271
276
  artifact.files.forEach((file, index) => {
@@ -287,7 +292,7 @@ function buildGitLabMergeRequestDiffMarkdown(artifact) {
287
292
  });
288
293
  return lines.join("\n");
289
294
  }
290
- export async function fetchGitLabReview(mergeRequestUrl, outputFile, outputJsonFile) {
295
+ export async function fetchGitLabReview(mergeRequestUrl, outputFile, outputJsonFile, mdLang) {
291
296
  const token = process.env.GITLAB_TOKEN?.trim();
292
297
  if (!token) {
293
298
  throw new TaskRunnerError("GITLAB_TOKEN is required for gitlab-review flow.");
@@ -307,10 +312,10 @@ export async function fetchGitLabReview(mergeRequestUrl, outputFile, outputJsonF
307
312
  mkdirSync(path.dirname(outputFile), { recursive: true });
308
313
  mkdirSync(path.dirname(outputJsonFile), { recursive: true });
309
314
  await writeFile(outputJsonFile, `${JSON.stringify(artifact, null, 2)}\n`, "utf8");
310
- await writeFile(outputFile, `${buildGitLabReviewMarkdown(artifact)}\n`, "utf8");
315
+ await writeFile(outputFile, `${buildGitLabReviewMarkdown(artifact, mdLang)}\n`, "utf8");
311
316
  return artifact;
312
317
  }
313
- export async function fetchGitLabMergeRequestDiff(mergeRequestUrl, outputFile, outputJsonFile) {
318
+ export async function fetchGitLabMergeRequestDiff(mergeRequestUrl, outputFile, outputJsonFile, mdLang) {
314
319
  const token = process.env.GITLAB_TOKEN?.trim();
315
320
  if (!token) {
316
321
  throw new TaskRunnerError("GITLAB_TOKEN is required for gitlab-diff-review flow.");
@@ -342,6 +347,6 @@ export async function fetchGitLabMergeRequestDiff(mergeRequestUrl, outputFile, o
342
347
  mkdirSync(path.dirname(outputFile), { recursive: true });
343
348
  mkdirSync(path.dirname(outputJsonFile), { recursive: true });
344
349
  await writeFile(outputJsonFile, `${JSON.stringify(artifact, null, 2)}\n`, "utf8");
345
- await writeFile(outputFile, `${buildGitLabMergeRequestDiffMarkdown(artifact)}\n`, "utf8");
350
+ await writeFile(outputFile, `${buildGitLabMergeRequestDiffMarkdown(artifact, mdLang)}\n`, "utf8");
346
351
  return artifact;
347
352
  }