agentweaver 0.1.10 → 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 (106) hide show
  1. package/README.md +218 -224
  2. package/dist/artifacts.js +100 -55
  3. package/dist/executors/{codex-local-executor.js → codex-executor.js} +4 -4
  4. package/dist/executors/configs/{codex-local-config.js → codex-config.js} +1 -1
  5. package/dist/executors/configs/jira-fetch-config.js +2 -0
  6. package/dist/executors/configs/telegram-notifier-config.js +3 -0
  7. package/dist/executors/fetch-gitlab-diff-executor.js +1 -1
  8. package/dist/executors/fetch-gitlab-review-executor.js +1 -1
  9. package/dist/executors/git-commit-executor.js +25 -0
  10. package/dist/executors/telegram-notifier-executor.js +54 -0
  11. package/dist/flow-state.js +46 -1
  12. package/dist/gitlab.js +13 -8
  13. package/dist/index.js +469 -514
  14. package/dist/interactive-ui.js +144 -43
  15. package/dist/jira.js +52 -5
  16. package/dist/pipeline/auto-flow.js +6 -6
  17. package/dist/pipeline/context.js +1 -0
  18. package/dist/pipeline/flow-catalog.js +34 -4
  19. package/dist/pipeline/flow-model-settings.js +77 -0
  20. package/dist/pipeline/flow-specs/auto-common.json +446 -0
  21. package/dist/pipeline/flow-specs/auto-golang.json +563 -0
  22. package/dist/pipeline/flow-specs/{bug-analyze.json → bugz/bug-analyze.json} +43 -25
  23. package/dist/pipeline/flow-specs/{bug-fix.json → bugz/bug-fix.json} +5 -4
  24. package/dist/pipeline/flow-specs/git-commit.json +196 -0
  25. package/dist/pipeline/flow-specs/{gitlab-diff-review.json → gitlab/gitlab-diff-review.json} +20 -50
  26. package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +165 -0
  27. package/dist/pipeline/flow-specs/{mr-description.json → gitlab/mr-description.json} +17 -10
  28. package/dist/pipeline/flow-specs/{run-go-linter-loop.json → go/run-go-linter-loop.json} +40 -14
  29. package/dist/pipeline/flow-specs/{run-go-tests-loop.json → go/run-go-tests-loop.json} +40 -14
  30. package/dist/pipeline/flow-specs/implement.json +5 -4
  31. package/dist/pipeline/flow-specs/plan.json +40 -148
  32. package/dist/pipeline/flow-specs/{review-fix.json → review/review-fix.json} +73 -13
  33. package/dist/pipeline/flow-specs/review/review-loop.json +280 -0
  34. package/dist/pipeline/flow-specs/review/review-project.json +87 -0
  35. package/dist/pipeline/flow-specs/review/review.json +126 -0
  36. package/dist/pipeline/flow-specs/task-describe.json +191 -11
  37. package/dist/pipeline/launch-profile-config.js +38 -0
  38. package/dist/pipeline/node-registry.js +75 -45
  39. package/dist/pipeline/nodes/build-failure-summary-node.js +16 -29
  40. package/dist/pipeline/nodes/build-review-fix-prompt-node.js +36 -0
  41. package/dist/pipeline/nodes/codex-prompt-node.js +41 -0
  42. package/dist/pipeline/nodes/commit-message-form-node.js +79 -0
  43. package/dist/pipeline/nodes/git-commit-form-node.js +138 -0
  44. package/dist/pipeline/nodes/git-commit-node.js +28 -0
  45. package/dist/pipeline/nodes/git-status-node.js +221 -0
  46. package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +10 -6
  47. package/dist/pipeline/nodes/jira-context-node.js +10 -0
  48. package/dist/pipeline/nodes/llm-prompt-node.js +62 -0
  49. package/dist/pipeline/nodes/plan-codex-node.js +1 -1
  50. package/dist/pipeline/nodes/read-file-node.js +11 -0
  51. package/dist/pipeline/nodes/review-findings-form-node.js +18 -14
  52. package/dist/pipeline/nodes/select-files-form-node.js +72 -0
  53. package/dist/pipeline/nodes/telegram-notifier-node.js +28 -0
  54. package/dist/pipeline/nodes/user-input-node.js +29 -8
  55. package/dist/pipeline/nodes/write-selection-file-node.js +46 -0
  56. package/dist/pipeline/prompt-registry.js +2 -4
  57. package/dist/pipeline/prompt-runtime.js +13 -3
  58. package/dist/pipeline/registry.js +6 -8
  59. package/dist/pipeline/spec-compiler.js +5 -0
  60. package/dist/pipeline/spec-types.js +7 -3
  61. package/dist/pipeline/spec-validator.js +4 -0
  62. package/dist/pipeline/types.js +1 -0
  63. package/dist/pipeline/value-resolver.js +40 -38
  64. package/dist/prompts.js +104 -110
  65. package/dist/runtime/agentweaver-home.js +8 -0
  66. package/dist/runtime/command-resolution.js +0 -38
  67. package/dist/runtime/env-loader.js +43 -0
  68. package/dist/structured-artifact-schema-registry.js +53 -0
  69. package/dist/structured-artifact-schemas.json +0 -20
  70. package/dist/structured-artifacts.js +3 -43
  71. package/dist/user-input.js +30 -2
  72. package/package.json +2 -6
  73. package/Dockerfile.codex +0 -56
  74. package/dist/executors/claude-executor.js +0 -46
  75. package/dist/executors/codex-docker-executor.js +0 -27
  76. package/dist/executors/configs/claude-config.js +0 -12
  77. package/dist/executors/configs/codex-docker-config.js +0 -10
  78. package/dist/executors/configs/verify-build-config.js +0 -7
  79. package/dist/executors/verify-build-executor.js +0 -123
  80. package/dist/pipeline/flow-specs/auto.json +0 -979
  81. package/dist/pipeline/flow-specs/gitlab-review.json +0 -317
  82. package/dist/pipeline/flow-specs/opencode/auto-opencode.json +0 -1365
  83. package/dist/pipeline/flow-specs/opencode/bugz/bug-analyze-opencode.json +0 -382
  84. package/dist/pipeline/flow-specs/opencode/bugz/bug-fix-opencode.json +0 -56
  85. package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-diff-review-opencode.json +0 -308
  86. package/dist/pipeline/flow-specs/opencode/gitlab/gitlab-review-opencode.json +0 -437
  87. package/dist/pipeline/flow-specs/opencode/gitlab/mr-description-opencode.json +0 -117
  88. package/dist/pipeline/flow-specs/opencode/go/run-go-linter-loop-opencode.json +0 -321
  89. package/dist/pipeline/flow-specs/opencode/go/run-go-tests-loop-opencode.json +0 -321
  90. package/dist/pipeline/flow-specs/opencode/implement-opencode.json +0 -64
  91. package/dist/pipeline/flow-specs/opencode/plan-opencode.json +0 -603
  92. package/dist/pipeline/flow-specs/opencode/review/review-fix-opencode.json +0 -209
  93. package/dist/pipeline/flow-specs/opencode/review/review-opencode.json +0 -452
  94. package/dist/pipeline/flow-specs/opencode/task-describe-opencode.json +0 -148
  95. package/dist/pipeline/flow-specs/review-project.json +0 -243
  96. package/dist/pipeline/flow-specs/review.json +0 -312
  97. package/dist/pipeline/flows/preflight-flow.js +0 -19
  98. package/dist/pipeline/nodes/claude-prompt-node.js +0 -54
  99. package/dist/pipeline/nodes/codex-docker-prompt-node.js +0 -32
  100. package/dist/pipeline/nodes/codex-local-prompt-node.js +0 -32
  101. package/dist/pipeline/nodes/review-claude-node.js +0 -38
  102. package/dist/pipeline/nodes/review-reply-codex-node.js +0 -40
  103. package/dist/pipeline/nodes/verify-build-node.js +0 -15
  104. package/dist/runtime/docker-runtime.js +0 -51
  105. package/docker-compose.yml +0 -445
  106. package/verify_build.sh +0 -105
@@ -142,6 +142,7 @@ export class InteractiveUi {
142
142
  scopeKey;
143
143
  jiraIssueKey;
144
144
  summaryVisible;
145
+ version;
145
146
  constructor(options) {
146
147
  this.options = options;
147
148
  if (options.flows.length === 0) {
@@ -149,12 +150,13 @@ export class InteractiveUi {
149
150
  }
150
151
  this.flowMap = new Map(options.flows.map((flow) => [flow.id, flow]));
151
152
  this.flowTree = buildFlowTree(options.flows);
152
- this.selectedFlowId = options.flows[0]?.id ?? "auto";
153
+ this.selectedFlowId = options.flows[0]?.id ?? "auto-golang";
153
154
  this.visibleFlowItems = this.computeVisibleFlowItems();
154
155
  this.selectedFlowItemKey = this.visibleFlowItems[0]?.key ?? makeFlowKey(this.selectedFlowId);
155
156
  this.scopeKey = options.scopeKey;
156
157
  this.jiraIssueKey = options.jiraIssueKey ?? null;
157
158
  this.summaryVisible = options.summaryText.trim().length > 0;
159
+ this.version = options.version ?? "";
158
160
  this.screen = blessed.screen({
159
161
  smartCSR: true,
160
162
  fullUnicode: true,
@@ -721,10 +723,12 @@ export class InteractiveUi {
721
723
  const branchLabel = this.options.gitBranchName ? this.options.gitBranchName : "detached-head";
722
724
  const flowLabel = `${current}${this.busy ? " {yellow-fg}[running]{/yellow-fg}" : ""}`;
723
725
  const divider = " {gray-fg}│{/gray-fg} ";
726
+ const versionLabel = this.version ? `${divider}{bold}Version{/bold} {white-fg}${this.version}{/white-fg}` : "";
724
727
  this.header.setContent([
725
728
  "{bold}AgentWeaver{/bold}",
726
729
  divider,
727
730
  `{bold}Scope{/bold} {green-fg}${this.scopeKey}{/green-fg}`,
731
+ versionLabel,
728
732
  this.jiraIssueKey ? `${divider}{bold}Jira{/bold} {yellow-fg}${this.jiraIssueKey}{/yellow-fg}` : "",
729
733
  divider,
730
734
  `{bold}Flow{/bold} ${flowLabel}`,
@@ -745,19 +749,117 @@ export class InteractiveUi {
745
749
  }
746
750
  return this.activeFormSession.form.fields[this.activeFormSession.currentFieldIndex] ?? null;
747
751
  }
748
- renderTextInputValue(value, placeholder) {
749
- const rawText = value || placeholder || "Введите текст";
750
- const frameWidth = Math.max(36, rawText.length + 6);
752
+ renderTextInputValue(value, placeholder, rows = 1) {
753
+ const rawLines = (value || placeholder || "Введите текст").split("\n");
754
+ const visibleLines = rawLines.slice(0, Math.max(1, rows));
755
+ const contentWidth = visibleLines.reduce((max, line) => Math.max(max, line.length), 0);
756
+ const frameWidth = Math.max(36, contentWidth + 6);
751
757
  const innerWidth = Math.max(32, frameWidth - 4);
752
- const visibleText = rawText.length > innerWidth - 2 ? `${rawText.slice(0, innerWidth - 5)}...` : rawText;
753
- const padded = value
754
- ? `{white-fg}${visibleText.padEnd(innerWidth - 2, " ")}{/white-fg}`
755
- : `{gray-fg}${visibleText.padEnd(innerWidth - 2, " ")}{/gray-fg}`;
756
- return [
757
- `{cyan-fg}┌${"─".repeat(frameWidth - 2)}┐{/cyan-fg}`,
758
- `{cyan-fg}{/cyan-fg}{black-bg} {green-fg}>{/green-fg} ${padded} {/black-bg}{cyan-fg}{/cyan-fg}`,
759
- `{cyan-fg}└${"─".repeat(frameWidth - 2)}┘{/cyan-fg}`,
760
- ];
758
+ const renderedRows = Array.from({ length: Math.max(1, rows) }, (_, index) => {
759
+ const rawLine = visibleLines[index] ?? "";
760
+ const visibleText = rawLine.length > innerWidth - 2 ? `${rawLine.slice(0, innerWidth - 5)}...` : rawLine;
761
+ const color = value ? "white-fg" : "gray-fg";
762
+ return `{cyan-fg}│{/cyan-fg}{black-bg} ${index === 0 ? "{green-fg}>{/green-fg}" : " "} {${color}}${visibleText.padEnd(innerWidth - 2, " ")}{/${color}} {/black-bg}{cyan-fg}│{/cyan-fg}`;
763
+ });
764
+ return [`{cyan-fg}┌${"─".repeat(frameWidth - 2)}{/cyan-fg}`, ...renderedRows, `{cyan-fg}└${"─".repeat(frameWidth - 2)}{/cyan-fg}`];
765
+ }
766
+ formModalInnerHeight() {
767
+ const rawHeight = typeof this.formModal.height === "number" ? this.formModal.height : this.formModal?.lpos?.yi
768
+ ? this.formModal.lpos.yl - this.formModal.lpos.yi + 1
769
+ : 0;
770
+ const paddingTop = Number(this.formModal.padding?.top ?? 0);
771
+ const paddingBottom = Number(this.formModal.padding?.bottom ?? 0);
772
+ return Math.max(6, rawHeight - 2 - paddingTop - paddingBottom);
773
+ }
774
+ formModalInnerWidth() {
775
+ const rawWidth = typeof this.formModal.width === "number" ? this.formModal.width : this.formModal?.lpos?.xi
776
+ ? this.formModal.lpos.xl - this.formModal.lpos.xi + 1
777
+ : 0;
778
+ const paddingLeft = Number(this.formModal.padding?.left ?? 0);
779
+ const paddingRight = Number(this.formModal.padding?.right ?? 0);
780
+ return Math.max(24, rawWidth - 2 - paddingLeft - paddingRight);
781
+ }
782
+ wrapFormText(text, width) {
783
+ const normalized = text.trim();
784
+ if (!normalized) {
785
+ return [""];
786
+ }
787
+ const wrapped = [];
788
+ for (const paragraph of normalized.split("\n")) {
789
+ if (!paragraph.trim()) {
790
+ wrapped.push("");
791
+ continue;
792
+ }
793
+ let remaining = paragraph.trim();
794
+ while (remaining.length > width) {
795
+ let splitAt = remaining.lastIndexOf(" ", width);
796
+ if (splitAt <= 0) {
797
+ splitAt = width;
798
+ }
799
+ wrapped.push(remaining.slice(0, splitAt).trimEnd());
800
+ remaining = remaining.slice(splitAt).trimStart();
801
+ }
802
+ wrapped.push(remaining);
803
+ }
804
+ return wrapped.length > 0 ? wrapped : [""];
805
+ }
806
+ renderSelectableFieldWindow(field, value, currentOptionIndex, availableLines) {
807
+ const contentWidth = this.formModalInnerWidth();
808
+ const firstLineWidth = Math.max(12, contentWidth - 6);
809
+ const continuationWidth = Math.max(8, contentWidth - 6);
810
+ const descriptionWidth = Math.max(8, contentWidth - 4);
811
+ const renderedOptions = field.options.map((option, index) => {
812
+ const isCursor = index === currentOptionIndex;
813
+ const isSelected = field.type === "single-select"
814
+ ? value === option.value
815
+ : Array.isArray(value) && value.includes(option.value);
816
+ const cursor = isCursor ? "{cyan-fg}>{/cyan-fg}" : " ";
817
+ const marker = isSelected ? "[x]" : "[ ]";
818
+ const labelLines = this.wrapFormText(option.label, firstLineWidth);
819
+ const itemLines = [`${cursor} ${marker} ${labelLines[0] ?? ""}`];
820
+ for (const continuation of labelLines.slice(1)) {
821
+ itemLines.push(` ${continuation.slice(0, continuationWidth)}`);
822
+ }
823
+ if (option.description?.trim()) {
824
+ for (const descriptionLine of this.wrapFormText(option.description, descriptionWidth)) {
825
+ itemLines.push(` {gray-fg}${descriptionLine}{/gray-fg}`);
826
+ }
827
+ }
828
+ return itemLines;
829
+ });
830
+ if (renderedOptions.length === 0) {
831
+ return ["{gray-fg}Нет доступных вариантов.{/gray-fg}"];
832
+ }
833
+ let startIndex = 0;
834
+ let selectedStartLine = 0;
835
+ for (let index = 0; index < currentOptionIndex; index += 1) {
836
+ selectedStartLine += renderedOptions[index]?.length ?? 0;
837
+ }
838
+ let visibleLines = 0;
839
+ for (let index = 0; index < renderedOptions.length; index += 1) {
840
+ const itemHeight = renderedOptions[index]?.length ?? 0;
841
+ if (index < currentOptionIndex && selectedStartLine + itemHeight > availableLines) {
842
+ startIndex = index + 1;
843
+ selectedStartLine -= itemHeight;
844
+ }
845
+ }
846
+ const output = [];
847
+ for (let index = startIndex; index < renderedOptions.length; index += 1) {
848
+ const itemLines = renderedOptions[index] ?? [];
849
+ if (output.length > 0 && output.length + itemLines.length > availableLines) {
850
+ break;
851
+ }
852
+ if (output.length === 0 && itemLines.length > availableLines) {
853
+ output.push(...itemLines.slice(0, availableLines));
854
+ break;
855
+ }
856
+ output.push(...itemLines);
857
+ visibleLines += itemLines.length;
858
+ if (visibleLines >= availableLines) {
859
+ break;
860
+ }
861
+ }
862
+ return output;
761
863
  }
762
864
  renderActiveForm() {
763
865
  if (!this.activeFormSession) {
@@ -771,18 +873,20 @@ export class InteractiveUi {
771
873
  if (!field) {
772
874
  return;
773
875
  }
774
- const lines = [`{bold}${session.form.title}{/bold}`];
876
+ const headerLines = [`{bold}${session.form.title}{/bold}`];
775
877
  if (session.form.description?.trim()) {
776
- lines.push("");
777
- lines.push(session.form.description.trim());
878
+ headerLines.push("");
879
+ headerLines.push(session.form.description.trim());
778
880
  }
779
- lines.push("");
780
- lines.push(`Field ${session.currentFieldIndex + 1}/${session.form.fields.length}`);
781
- lines.push(`{yellow-fg}${field.label}{/yellow-fg}`);
881
+ headerLines.push("");
882
+ headerLines.push(`Field ${session.currentFieldIndex + 1}/${session.form.fields.length}`);
883
+ headerLines.push(`{yellow-fg}${field.label}{/yellow-fg}`);
782
884
  if (field.help?.trim()) {
783
- lines.push(field.help.trim());
885
+ headerLines.push(field.help.trim());
784
886
  }
785
- lines.push("");
887
+ headerLines.push("");
888
+ const footerLines = ["", "{green-fg}Ctrl+S{/green-fg}: submit", "{magenta-fg}Shift+Tab{/magenta-fg}: previous field", "{red-fg}Esc{/red-fg}: cancel"];
889
+ const lines = [...headerLines];
786
890
  if (field.type === "boolean") {
787
891
  const current = session.values[field.id] === true;
788
892
  lines.push(`${current ? "[x]" : "[ ]"} ${field.label}`);
@@ -792,37 +896,24 @@ export class InteractiveUi {
792
896
  }
793
897
  else if (field.type === "text") {
794
898
  const current = String(session.values[field.id] ?? "");
795
- lines.push(...this.renderTextInputValue(current, field.placeholder));
899
+ lines.push(...this.renderTextInputValue(current, field.placeholder, field.multiline ? Math.max(1, field.rows ?? 3) : 1));
796
900
  lines.push("");
797
- lines.push("Type text, Backspace: delete");
798
- lines.push("Enter/Tab: next field");
901
+ lines.push(field.multiline ? "Type text, Enter: new line, Backspace: delete" : "Type text, Backspace: delete");
902
+ lines.push("Tab: next field");
799
903
  }
800
904
  else {
801
905
  const currentOptionIndex = Math.min(session.currentOptionIndex, Math.max(0, field.options.length - 1));
802
906
  session.currentOptionIndex = currentOptionIndex;
803
- field.options.forEach((option, index) => {
804
- const isCursor = index === currentOptionIndex;
805
- const value = session.values[field.id];
806
- const isSelected = field.type === "single-select"
807
- ? value === option.value
808
- : Array.isArray(value) && value.includes(option.value);
809
- const cursor = isCursor ? "{cyan-fg}>{/cyan-fg}" : " ";
810
- const marker = isSelected ? "[x]" : "[ ]";
811
- lines.push(`${cursor} ${marker} ${option.label}`);
812
- if (option.description?.trim()) {
813
- lines.push(` {gray-fg}${option.description.trim()}{/gray-fg}`);
814
- }
815
- });
907
+ const availableLines = Math.max(3, this.formModalInnerHeight() - headerLines.length - footerLines.length - 3);
908
+ lines.push(...this.renderSelectableFieldWindow(field, session.values[field.id], currentOptionIndex, availableLines));
816
909
  lines.push("");
817
910
  lines.push("Up/Down: move");
818
911
  lines.push("Space: select/toggle");
819
912
  lines.push("Enter/Tab: next field");
820
913
  }
821
- lines.push("");
822
- lines.push("{green-fg}Ctrl+S{/green-fg}: submit");
823
- lines.push("{magenta-fg}Shift+Tab{/magenta-fg}: previous field");
824
- lines.push("{red-fg}Esc{/red-fg}: cancel");
914
+ lines.push(...footerLines);
825
915
  this.formModal.setContent(lines.join("\n"));
916
+ this.formModal.setScroll(0);
826
917
  this.formModal.show();
827
918
  this.formModal.setFront();
828
919
  this.formModal.focus();
@@ -880,7 +971,7 @@ export class InteractiveUi {
880
971
  this.renderActiveForm();
881
972
  }
882
973
  }
883
- appendActiveFormText(ch, key) {
974
+ appendActiveFormText(ch, key, appendNewline = false) {
884
975
  const session = this.activeFormSession;
885
976
  const field = this.currentFormField();
886
977
  if (!session || !field || field.type !== "text") {
@@ -892,6 +983,11 @@ export class InteractiveUi {
892
983
  this.renderActiveForm();
893
984
  return;
894
985
  }
986
+ if (appendNewline) {
987
+ session.values[field.id] = `${current}\n`;
988
+ this.renderActiveForm();
989
+ return;
990
+ }
895
991
  if (key.ctrl || key.meta || !ch || ch === "\r" || ch === "\n" || ch === "\t") {
896
992
  return;
897
993
  }
@@ -966,7 +1062,12 @@ export class InteractiveUi {
966
1062
  }
967
1063
  if (field.type === "text") {
968
1064
  if (key.name === "enter") {
969
- this.moveActiveFormField(1);
1065
+ if (field.multiline) {
1066
+ this.appendActiveFormText(ch, key, true);
1067
+ }
1068
+ else {
1069
+ this.moveActiveFormField(1);
1070
+ }
970
1071
  return;
971
1072
  }
972
1073
  this.appendActiveFormText(ch, key);
package/dist/jira.js CHANGED
@@ -5,6 +5,57 @@ import { TaskRunnerError } from "./errors.js";
5
5
  const ISSUE_KEY_RE = /^[A-Z][A-Z0-9_]*-[0-9]+$/;
6
6
  const TEXT_ATTACHMENT_EXTENSIONS = new Set([".md", ".json", ".txt"]);
7
7
  const DOWNLOAD_ONLY_ATTACHMENT_EXTENSIONS = new Set([".doc"]);
8
+ const JIRA_AUTH_MODES = new Set(["auto", "basic", "bearer"]);
9
+ function parseJiraAuthMode(rawMode) {
10
+ const mode = rawMode?.trim().toLowerCase() || "auto";
11
+ if (!JIRA_AUTH_MODES.has(mode)) {
12
+ throw new TaskRunnerError("JIRA_AUTH_MODE must be one of: auto, basic, bearer.");
13
+ }
14
+ return mode;
15
+ }
16
+ export function detectJiraDeployment(url) {
17
+ try {
18
+ return new URL(url).hostname.toLowerCase().includes("atlassian") ? "cloud" : "server";
19
+ }
20
+ catch {
21
+ return url.toLowerCase().includes("atlassian") ? "cloud" : "server";
22
+ }
23
+ }
24
+ export function resolveJiraAuthMode(url) {
25
+ const authMode = parseJiraAuthMode(process.env.JIRA_AUTH_MODE);
26
+ if (authMode !== "auto") {
27
+ return authMode;
28
+ }
29
+ return detectJiraDeployment(url) === "cloud" ? "basic" : "bearer";
30
+ }
31
+ export function buildJiraAuthHeaders(url) {
32
+ const jiraApiKey = process.env.JIRA_API_KEY?.trim();
33
+ if (!jiraApiKey) {
34
+ throw new TaskRunnerError("JIRA_API_KEY is required for Jira authentication.");
35
+ }
36
+ const authMode = resolveJiraAuthMode(url);
37
+ if (authMode === "bearer") {
38
+ return {
39
+ Authorization: `Bearer ${jiraApiKey}`,
40
+ };
41
+ }
42
+ const jiraUsername = process.env.JIRA_USERNAME?.trim();
43
+ if (!jiraUsername) {
44
+ const host = (() => {
45
+ try {
46
+ return new URL(url).host;
47
+ }
48
+ catch {
49
+ return "unknown";
50
+ }
51
+ })();
52
+ throw new TaskRunnerError(`JIRA_USERNAME is required for Jira Cloud Basic auth (detected from URL host: ${host}).`);
53
+ }
54
+ const encodedCredentials = Buffer.from(`${jiraUsername}:${jiraApiKey}`).toString("base64");
55
+ return {
56
+ Authorization: `Basic ${encodedCredentials}`,
57
+ };
58
+ }
8
59
  function sanitizeAttachmentFileName(fileName) {
9
60
  const parsed = path.parse(fileName);
10
61
  const baseName = parsed.name
@@ -29,13 +80,9 @@ function parseJiraAttachments(issueBody) {
29
80
  }
30
81
  }
31
82
  async function fetchAuthorizedBuffer(url, accept) {
32
- const jiraApiKey = process.env.JIRA_API_KEY;
33
- if (!jiraApiKey) {
34
- throw new TaskRunnerError("JIRA_API_KEY is required for Jira fetch.");
35
- }
36
83
  const response = await fetch(url, {
37
84
  headers: {
38
- Authorization: `Bearer ${jiraApiKey}`,
85
+ ...buildJiraAuthHeaders(url),
39
86
  Accept: accept,
40
87
  },
41
88
  });
@@ -1,9 +1,9 @@
1
1
  import { loadDeclarativeFlow } from "./declarative-flows.js";
2
- let cachedAutoFlow = null;
3
- export function loadAutoFlow() {
4
- if (cachedAutoFlow) {
5
- return cachedAutoFlow;
2
+ let cachedAutoGolangFlow = null;
3
+ export function loadAutoGolangFlow() {
4
+ if (cachedAutoGolangFlow) {
5
+ return cachedAutoGolangFlow;
6
6
  }
7
- cachedAutoFlow = loadDeclarativeFlow({ source: "built-in", fileName: "auto.json" });
8
- return cachedAutoFlow;
7
+ cachedAutoGolangFlow = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" });
8
+ return cachedAutoGolangFlow;
9
9
  }
@@ -11,6 +11,7 @@ export function createPipelineContext(input) {
11
11
  ui: getOutputAdapter(),
12
12
  dryRun: input.dryRun,
13
13
  verbose: input.verbose,
14
+ mdLang: input.mdLang ?? null,
14
15
  runtime: input.runtime,
15
16
  executors: createExecutorRegistry(),
16
17
  nodes: createNodeRegistry(),
@@ -1,12 +1,14 @@
1
1
  import path from "node:path";
2
2
  import { TaskRunnerError } from "../errors.js";
3
- import { loadAutoFlow } from "./auto-flow.js";
3
+ import { loadAutoGolangFlow } from "./auto-flow.js";
4
4
  import { loadDeclarativeFlow } from "./declarative-flows.js";
5
5
  import { listBuiltInFlowSpecFiles, listProjectFlowSpecFiles, projectFlowSpecsDir } from "./spec-loader.js";
6
6
  export const BUILT_IN_COMMAND_FLOW_IDS = [
7
- "auto",
7
+ "auto-golang",
8
+ "auto-common",
8
9
  "bug-analyze",
9
10
  "bug-fix",
11
+ "git-commit",
10
12
  "gitlab-diff-review",
11
13
  "gitlab-review",
12
14
  "mr-description",
@@ -15,13 +17,41 @@ export const BUILT_IN_COMMAND_FLOW_IDS = [
15
17
  "implement",
16
18
  "review",
17
19
  "review-fix",
20
+ "review-loop",
18
21
  "run-go-tests-loop",
19
22
  "run-go-linter-loop",
20
23
  ];
24
+ const BUILT_IN_COMMAND_FLOW_FILES = {
25
+ "auto-golang": "auto-golang.json",
26
+ "auto-common": "auto-common.json",
27
+ "bug-analyze": "bugz/bug-analyze.json",
28
+ "bug-fix": "bugz/bug-fix.json",
29
+ "git-commit": "git-commit.json",
30
+ "gitlab-diff-review": "gitlab/gitlab-diff-review.json",
31
+ "gitlab-review": "gitlab/gitlab-review.json",
32
+ "mr-description": "gitlab/mr-description.json",
33
+ plan: "plan.json",
34
+ "task-describe": "task-describe.json",
35
+ implement: "implement.json",
36
+ review: "review/review.json",
37
+ "review-fix": "review/review-fix.json",
38
+ "review-loop": "review/review-loop.json",
39
+ "run-go-tests-loop": "go/run-go-tests-loop.json",
40
+ "run-go-linter-loop": "go/run-go-linter-loop.json",
41
+ };
42
+ function builtInCommandIdForFile(fileName) {
43
+ for (const [flowId, candidate] of Object.entries(BUILT_IN_COMMAND_FLOW_FILES)) {
44
+ if (candidate === fileName) {
45
+ return flowId;
46
+ }
47
+ }
48
+ return null;
49
+ }
21
50
  function loadBuiltInCatalogEntry(fileName) {
51
+ const commandId = builtInCommandIdForFile(fileName);
22
52
  const relativePath = fileName.replace(/\.json$/i, "").split(/[\\/]+/).filter((segment) => segment.length > 0);
23
- const id = relativePath.join("/");
24
- const flow = id === "auto" ? loadAutoFlow() : loadDeclarativeFlow({ source: "built-in", fileName });
53
+ const id = commandId ?? relativePath.join("/");
54
+ const flow = id === "auto-golang" ? loadAutoGolangFlow() : loadDeclarativeFlow({ source: "built-in", fileName });
25
55
  return {
26
56
  id,
27
57
  source: "built-in",
@@ -0,0 +1,77 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { DEFAULT_MODEL_BY_EXECUTOR, defaultModelForExecutor } from "./launch-profile-config.js";
4
+ import { scopeArtifactsDir } from "../artifacts.js";
5
+ const FLOW_MODEL_SETTINGS_FILE = "agentweaver-flow-model-settings.json";
6
+ function flowModelSettingsPath(scopeKey) {
7
+ return path.join(scopeArtifactsDir(scopeKey), FLOW_MODEL_SETTINGS_FILE);
8
+ }
9
+ function ensureArtifactsDir(scopeKey) {
10
+ const artifactsDir = scopeArtifactsDir(scopeKey);
11
+ if (!existsSync(artifactsDir)) {
12
+ mkdirSync(artifactsDir, { recursive: true });
13
+ }
14
+ }
15
+ export function loadFlowModelSettings(scopeKey) {
16
+ const filePath = flowModelSettingsPath(scopeKey);
17
+ if (!existsSync(filePath)) {
18
+ return {};
19
+ }
20
+ try {
21
+ const content = readFileSync(filePath, "utf8");
22
+ const parsed = JSON.parse(content);
23
+ if (typeof parsed !== "object" || parsed === null) {
24
+ return {};
25
+ }
26
+ return parsed;
27
+ }
28
+ catch {
29
+ return {};
30
+ }
31
+ }
32
+ export function saveFlowModelSettings(scopeKey, store) {
33
+ ensureArtifactsDir(scopeKey);
34
+ const filePath = flowModelSettingsPath(scopeKey);
35
+ const tempPath = `${filePath}.tmp`;
36
+ const content = JSON.stringify(store, null, 2);
37
+ writeFileSync(tempPath, content, "utf8");
38
+ renameSync(tempPath, filePath);
39
+ }
40
+ export function getEffectiveModelForFlow(flowId, scopeKey, executor) {
41
+ const store = loadFlowModelSettings(scopeKey);
42
+ const flowSettings = store[flowId];
43
+ if (!flowSettings) {
44
+ return defaultModelForExecutor(executor);
45
+ }
46
+ if (flowSettings.lastSelectedModel && flowSettings.lastSelectedModel !== "default") {
47
+ return flowSettings.lastSelectedModel;
48
+ }
49
+ if (flowSettings.defaultModel && flowSettings.defaultModel !== "default") {
50
+ return flowSettings.defaultModel;
51
+ }
52
+ return defaultModelForExecutor(executor);
53
+ }
54
+ export function updateLastSelectedModel(flowId, scopeKey, executor, model) {
55
+ const store = loadFlowModelSettings(scopeKey);
56
+ const existingSettings = store[flowId];
57
+ if (existingSettings) {
58
+ store[flowId] = {
59
+ ...existingSettings,
60
+ executor,
61
+ defaultModel: model,
62
+ lastSelectedModel: model,
63
+ };
64
+ }
65
+ else {
66
+ store[flowId] = {
67
+ executor,
68
+ defaultModel: DEFAULT_MODEL_BY_EXECUTOR[executor],
69
+ lastSelectedModel: model,
70
+ };
71
+ }
72
+ saveFlowModelSettings(scopeKey, store);
73
+ }
74
+ export function getFlowModelSettings(flowId, scopeKey) {
75
+ const store = loadFlowModelSettings(scopeKey);
76
+ return store[flowId] ?? null;
77
+ }