agentweaver 0.1.14 → 0.1.16

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 (100) hide show
  1. package/README.md +29 -7
  2. package/dist/artifact-manifest.js +219 -0
  3. package/dist/artifacts.js +21 -1
  4. package/dist/doctor/checks/cwd-context.js +4 -3
  5. package/dist/doctor/checks/env-diagnostics.js +193 -71
  6. package/dist/doctor/checks/flow-readiness.js +212 -203
  7. package/dist/doctor/index.js +1 -1
  8. package/dist/doctor/orchestrator.js +18 -7
  9. package/dist/doctor/runner.js +9 -8
  10. package/dist/doctor/types.js +12 -0
  11. package/dist/flow-state.js +75 -15
  12. package/dist/index.js +499 -199
  13. package/dist/interactive/blessed-session.js +361 -0
  14. package/dist/interactive/controller.js +1293 -0
  15. package/dist/interactive/create-interactive-session.js +5 -0
  16. package/dist/interactive/ink/index.js +576 -0
  17. package/dist/interactive/progress.js +245 -0
  18. package/dist/interactive/selectors.js +14 -0
  19. package/dist/interactive/session.js +1 -0
  20. package/dist/interactive/state.js +34 -0
  21. package/dist/interactive/tree.js +155 -0
  22. package/dist/interactive/types.js +1 -0
  23. package/dist/interactive/view-model.js +1 -0
  24. package/dist/interactive-ui.js +159 -194
  25. package/dist/pipeline/context.js +1 -0
  26. package/dist/pipeline/declarative-flow-runner.js +212 -6
  27. package/dist/pipeline/declarative-flows.js +27 -0
  28. package/dist/pipeline/execution-routing-config.js +15 -0
  29. package/dist/pipeline/flow-catalog.js +23 -3
  30. package/dist/pipeline/flow-run-resume.js +29 -0
  31. package/dist/pipeline/flow-specs/auto-common.json +89 -360
  32. package/dist/pipeline/flow-specs/auto-golang.json +58 -363
  33. package/dist/pipeline/flow-specs/auto-simple.json +141 -0
  34. package/dist/pipeline/flow-specs/bugz/bug-analyze.json +2 -0
  35. package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
  36. package/dist/pipeline/flow-specs/design-review/design-review-loop.json +304 -0
  37. package/dist/pipeline/flow-specs/design-review.json +249 -0
  38. package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +11 -0
  39. package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +2 -0
  40. package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
  41. package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +2 -0
  42. package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +2 -0
  43. package/dist/pipeline/flow-specs/implement.json +24 -5
  44. package/dist/pipeline/flow-specs/instant-task.json +177 -0
  45. package/dist/pipeline/flow-specs/normalize-task-source.json +311 -0
  46. package/dist/pipeline/flow-specs/plan-revise.json +267 -0
  47. package/dist/pipeline/flow-specs/plan.json +48 -70
  48. package/dist/pipeline/flow-specs/review/review-fix.json +24 -4
  49. package/dist/pipeline/flow-specs/review/review-loop.json +351 -45
  50. package/dist/pipeline/flow-specs/review/review-project-loop.json +590 -0
  51. package/dist/pipeline/flow-specs/review/review-project.json +12 -0
  52. package/dist/pipeline/flow-specs/review/review.json +37 -31
  53. package/dist/pipeline/flow-specs/task-describe.json +62 -2
  54. package/dist/pipeline/flow-specs/task-source/jira-fetch.json +70 -0
  55. package/dist/pipeline/flow-specs/task-source/manual-input.json +216 -0
  56. package/dist/pipeline/node-registry.js +49 -1
  57. package/dist/pipeline/node-runner.js +3 -2
  58. package/dist/pipeline/nodes/build-review-fix-prompt-node.js +5 -1
  59. package/dist/pipeline/nodes/clear-ready-to-merge-node.js +11 -0
  60. package/dist/pipeline/nodes/commit-message-form-node.js +8 -0
  61. package/dist/pipeline/nodes/design-review-verdict-node.js +36 -0
  62. package/dist/pipeline/nodes/ensure-summary-json-node.js +70 -0
  63. package/dist/pipeline/nodes/fetch-gitlab-diff-node.js +19 -2
  64. package/dist/pipeline/nodes/fetch-gitlab-review-node.js +19 -2
  65. package/dist/pipeline/nodes/flow-run-node.js +226 -7
  66. package/dist/pipeline/nodes/git-commit-form-node.js +8 -0
  67. package/dist/pipeline/nodes/gitlab-review-artifacts-node.js +19 -2
  68. package/dist/pipeline/nodes/jira-fetch-node.js +50 -4
  69. package/dist/pipeline/nodes/llm-prompt-node.js +32 -12
  70. package/dist/pipeline/nodes/planning-bundle-node.js +10 -0
  71. package/dist/pipeline/nodes/review-verdict-node.js +86 -0
  72. package/dist/pipeline/nodes/select-files-form-node.js +8 -0
  73. package/dist/pipeline/nodes/structured-summary-node.js +24 -0
  74. package/dist/pipeline/nodes/user-input-node.js +38 -3
  75. package/dist/pipeline/nodes/write-selection-file-node.js +20 -4
  76. package/dist/pipeline/prompt-registry.js +5 -1
  77. package/dist/pipeline/prompt-runtime.js +4 -1
  78. package/dist/pipeline/review-iteration.js +26 -0
  79. package/dist/pipeline/spec-compiler.js +2 -0
  80. package/dist/pipeline/spec-types.js +5 -0
  81. package/dist/pipeline/spec-validator.js +14 -0
  82. package/dist/pipeline/value-resolver.js +84 -1
  83. package/dist/prompts.js +82 -13
  84. package/dist/review-severity.js +45 -0
  85. package/dist/runtime/artifact-registry.js +402 -0
  86. package/dist/runtime/design-review-input-contract.js +113 -0
  87. package/dist/runtime/env-loader.js +3 -0
  88. package/dist/runtime/execution-routing-store.js +134 -0
  89. package/dist/runtime/execution-routing.js +227 -0
  90. package/dist/runtime/interactive-execution-routing.js +462 -0
  91. package/dist/runtime/plan-revise-input-contract.js +147 -0
  92. package/dist/runtime/planning-bundle.js +123 -0
  93. package/dist/runtime/ready-to-merge.js +31 -0
  94. package/dist/runtime/review-input-contract.js +100 -0
  95. package/dist/scope.js +11 -2
  96. package/dist/structured-artifact-schema-registry.js +10 -0
  97. package/dist/structured-artifact-schemas.json +257 -1
  98. package/dist/structured-artifacts.js +83 -6
  99. package/dist/user-input.js +70 -3
  100. package/package.json +6 -3
@@ -1,8 +1,10 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import { DoctorStatus } from "../types.js";
4
+ import { DoctorImpact, DoctorStatus } from "../types.js";
5
5
  import { CATEGORY } from "./category.js";
6
+ import { detectJiraDeployment } from "../../jira.js";
7
+ import { AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV, parseReviewSeverityCsv, } from "../../review-severity.js";
6
8
  const MONITORED_KEYS = [
7
9
  "JIRA_API_KEY",
8
10
  "JIRA_USERNAME",
@@ -10,6 +12,7 @@ const MONITORED_KEYS = [
10
12
  "JIRA_BASE_URL",
11
13
  "GITLAB_TOKEN",
12
14
  "AGENTWEAVER_HOME",
15
+ "AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES",
13
16
  "CODEX_BIN",
14
17
  "CODEX_MODEL",
15
18
  "OPENCODE_BIN",
@@ -17,19 +20,17 @@ const MONITORED_KEYS = [
17
20
  ];
18
21
  const SECRET_KEYS = new Set(["JIRA_API_KEY", "GITLAB_TOKEN"]);
19
22
  const JIRA_AUTH_MODE_ALLOWED_VALUES = ["auto", "basic", "bearer"];
20
- function maskSecret(value) {
21
- if (value.length <= 6) {
22
- if (value.length <= 3) {
23
- return "***";
24
- }
25
- const start = value.slice(0, Math.ceil(value.length / 2));
26
- const end = value.slice(Math.ceil(value.length / 2));
27
- return `${start}***${end}`;
28
- }
29
- const start = value.slice(0, 3);
30
- const end = value.slice(-3);
31
- return `${start}***${end}`;
32
- }
23
+ const KEY_NOTES = {
24
+ JIRA_API_KEY: "Required for Jira-backed flows.",
25
+ JIRA_USERNAME: "Required only for Jira Cloud basic auth.",
26
+ GITLAB_TOKEN: "Required for GitLab-backed flows.",
27
+ AGENTWEAVER_HOME: "Optional override for the AgentWeaver package home.",
28
+ AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES: "Optional default list of merge-blocking severities for review and review-fix.",
29
+ CODEX_BIN: "Optional override for the codex executable path.",
30
+ CODEX_MODEL: "Optional fallback model override for Codex-backed executors.",
31
+ OPENCODE_BIN: "Optional override for the opencode executable path.",
32
+ OPENCODE_MODEL: "Optional fallback model override for OpenCode-backed executors.",
33
+ };
33
34
  function globalConfigDir() {
34
35
  return path.join(os.homedir(), ".agentweaver");
35
36
  }
@@ -69,101 +70,222 @@ function getProjectEnvPath() {
69
70
  function getGlobalEnvPath() {
70
71
  return path.join(globalConfigDir(), ".env");
71
72
  }
72
- function determineSource(key, shellSnapshot, currentValue) {
73
+ function determineSource(key, shellSnapshot, projectEnv, globalEnv, currentValue) {
73
74
  if (currentValue === null) {
74
75
  return "missing";
75
76
  }
76
- const isInShellSnapshot = shellSnapshot.hasOwnProperty(key);
77
- if (isInShellSnapshot) {
77
+ if (Object.prototype.hasOwnProperty.call(shellSnapshot, key)) {
78
78
  return "shell";
79
79
  }
80
- const globalEnv = parseEnvFileRaw(getGlobalEnvPath());
81
- if (globalEnv.hasOwnProperty(key)) {
82
- return "global";
83
- }
84
- const projectEnv = parseEnvFileRaw(getProjectEnvPath());
85
- if (projectEnv.hasOwnProperty(key)) {
80
+ if (Object.prototype.hasOwnProperty.call(projectEnv, key)) {
86
81
  return "project-local";
87
82
  }
83
+ if (Object.prototype.hasOwnProperty.call(globalEnv, key)) {
84
+ return "global";
85
+ }
88
86
  return "shell";
89
87
  }
90
- function getDefaultValue(key) {
91
- switch (key) {
92
- case "AGENTWEAVER_HOME":
93
- return path.join(os.homedir(), ".agentweaver");
94
- default:
95
- return null;
88
+ function defaultNote(key) {
89
+ if (key === "JIRA_AUTH_MODE") {
90
+ return "Defaults to auto.";
91
+ }
92
+ if (key === AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV) {
93
+ return "Defaults to blocker,critical,high.";
96
94
  }
95
+ return null;
97
96
  }
98
97
  function validateJiraAuthMode(value) {
99
98
  if (value === null) {
100
99
  return true;
101
100
  }
102
- return JIRA_AUTH_MODE_ALLOWED_VALUES.includes(value);
101
+ return JIRA_AUTH_MODE_ALLOWED_VALUES.includes(value.trim().toLowerCase());
102
+ }
103
+ function validateReviewBlockingSeverities(value) {
104
+ if (value === null) {
105
+ return true;
106
+ }
107
+ try {
108
+ parseReviewSeverityCsv(value);
109
+ return true;
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ }
115
+ function keyNote(key) {
116
+ return KEY_NOTES[key];
117
+ }
118
+ function formatKeyLine(info) {
119
+ const source = info.state === "unset" ? "unset" : info.source === "default" ? "default" : info.source;
120
+ const secretLabel = info.isSecret ? ", secret" : "";
121
+ const noteLabel = info.note ? `, ${info.note}` : "";
122
+ return `- ${info.key} (${source}${secretLabel}${noteLabel})`;
123
+ }
124
+ function buildDetails(configured, defaulted, unset, invalid, warnings) {
125
+ const lines = [];
126
+ if (warnings.length > 0) {
127
+ lines.push("warnings:");
128
+ for (const warning of warnings) {
129
+ lines.push(`- ${warning}`);
130
+ }
131
+ }
132
+ if (configured.length > 0) {
133
+ if (lines.length > 0) {
134
+ lines.push("");
135
+ }
136
+ lines.push("configured:");
137
+ for (const info of configured) {
138
+ lines.push(formatKeyLine(info));
139
+ }
140
+ }
141
+ if (defaulted.length > 0) {
142
+ if (lines.length > 0) {
143
+ lines.push("");
144
+ }
145
+ lines.push("defaulted:");
146
+ for (const info of defaulted) {
147
+ lines.push(formatKeyLine(info));
148
+ }
149
+ }
150
+ if (unset.length > 0) {
151
+ if (lines.length > 0) {
152
+ lines.push("");
153
+ }
154
+ lines.push("unset:");
155
+ for (const info of unset) {
156
+ lines.push(formatKeyLine(info));
157
+ }
158
+ }
159
+ if (invalid.length > 0) {
160
+ if (lines.length > 0) {
161
+ lines.push("");
162
+ }
163
+ lines.push("invalid:");
164
+ for (const info of invalid) {
165
+ lines.push(formatKeyLine(info));
166
+ }
167
+ }
168
+ return lines.join("\n");
103
169
  }
104
170
  function checkEnvDiagnostics() {
105
171
  const shellSnapshot = {};
106
172
  for (const key of Object.keys(process.env)) {
107
- shellSnapshot[key] = process.env[key];
173
+ const value = process.env[key];
174
+ if (value !== undefined) {
175
+ shellSnapshot[key] = value;
176
+ }
108
177
  }
109
178
  const projectEnv = parseEnvFileRaw(getProjectEnvPath());
110
179
  const globalEnv = parseEnvFileRaw(getGlobalEnvPath());
111
180
  const keyInfos = [];
112
- let hasWarnings = false;
181
+ const warnings = [];
182
+ const jiraApiKey = process.env.JIRA_API_KEY?.trim() || null;
183
+ const jiraUsername = process.env.JIRA_USERNAME?.trim() || null;
184
+ const jiraAuthModeRaw = process.env.JIRA_AUTH_MODE?.trim() || null;
185
+ const jiraBaseUrl = process.env.JIRA_BASE_URL?.trim() || null;
186
+ const reviewBlockingSeveritiesRaw = process.env[AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV]?.trim() || null;
113
187
  for (const key of MONITORED_KEYS) {
114
- const currentValue = process.env[key] ?? null;
115
- const source = determineSource(key, shellSnapshot, currentValue);
188
+ const currentValue = process.env[key]?.trim() || null;
116
189
  const isSecret = SECRET_KEYS.has(key);
117
- let maskedValue = null;
118
- if (currentValue !== null && isSecret) {
119
- maskedValue = maskSecret(currentValue);
190
+ const source = determineSource(key, shellSnapshot, projectEnv, globalEnv, currentValue);
191
+ const defaultHint = currentValue === null ? defaultNote(key) : null;
192
+ let state = "configured";
193
+ if (currentValue === null && defaultHint) {
194
+ state = "defaulted";
120
195
  }
121
- else if (currentValue !== null) {
122
- maskedValue = currentValue;
196
+ else if (currentValue === null) {
197
+ state = "unset";
123
198
  }
124
- const keyInfo = {
199
+ if (key === "JIRA_AUTH_MODE" && currentValue !== null && !validateJiraAuthMode(currentValue)) {
200
+ state = "invalid";
201
+ }
202
+ if (key === AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV && currentValue !== null && !validateReviewBlockingSeverities(currentValue)) {
203
+ state = "invalid";
204
+ }
205
+ const note = state === "defaulted" ? defaultHint ?? undefined : keyNote(key);
206
+ keyInfos.push({
125
207
  key,
126
- source,
127
- value: currentValue,
128
- maskedValue,
208
+ source: state === "defaulted" ? "default" : source,
209
+ state,
129
210
  isSecret,
130
- };
131
- keyInfos.push(keyInfo);
132
- if (source === "missing") {
133
- hasWarnings = true;
134
- }
135
- if (key === "JIRA_AUTH_MODE" && currentValue !== null) {
136
- if (!validateJiraAuthMode(currentValue)) {
137
- hasWarnings = true;
138
- }
139
- }
140
- }
141
- const status = hasWarnings ? DoctorStatus.Warn : DoctorStatus.Ok;
142
- const keyCount = keyInfos.length;
143
- const missingCount = keyInfos.filter(k => k.source === "missing").length;
144
- const secretCount = keyInfos.filter(k => k.isSecret).length;
145
- const summaryParts = [];
146
- summaryParts.push(`${keyCount} keys checked`);
147
- if (missingCount > 0) {
148
- summaryParts.push(`${missingCount} missing`);
149
- }
150
- if (secretCount > 0) {
151
- summaryParts.push(`${secretCount} secrets`);
152
- }
153
- const result = {
211
+ ...(note ? { note } : {}),
212
+ });
213
+ }
214
+ const jiraHasAnyConfig = !!(jiraApiKey || jiraBaseUrl || jiraUsername || jiraAuthModeRaw);
215
+ const jiraHasCorePair = !!(jiraApiKey && jiraBaseUrl);
216
+ if ((jiraApiKey && !jiraBaseUrl) || (!jiraApiKey && jiraBaseUrl)) {
217
+ warnings.push("Jira configuration is partial: set both JIRA_API_KEY and JIRA_BASE_URL for Jira-backed flows.");
218
+ }
219
+ if (jiraAuthModeRaw !== null && !validateJiraAuthMode(jiraAuthModeRaw)) {
220
+ warnings.push("JIRA_AUTH_MODE must be one of: auto, basic, bearer.");
221
+ }
222
+ if (reviewBlockingSeveritiesRaw !== null && !validateReviewBlockingSeverities(reviewBlockingSeveritiesRaw)) {
223
+ warnings.push("AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES must be a comma-separated list of: blocker, critical, high, medium, low, info.");
224
+ }
225
+ if (jiraHasCorePair && jiraBaseUrl) {
226
+ const authMode = jiraAuthModeRaw?.toLowerCase() || "auto";
227
+ const isCloud = detectJiraDeployment(jiraBaseUrl) === "cloud";
228
+ const usesBasicAuth = authMode === "basic" || (authMode === "auto" && isCloud);
229
+ if (usesBasicAuth && !jiraUsername) {
230
+ warnings.push("JIRA_USERNAME is required for Jira Cloud basic auth with the current Jira URL and auth mode.");
231
+ }
232
+ }
233
+ else if (jiraHasAnyConfig && jiraUsername && !jiraApiKey && !jiraBaseUrl) {
234
+ warnings.push("JIRA_USERNAME is set without the Jira API key/base URL pair.");
235
+ }
236
+ const configured = keyInfos.filter((info) => info.state === "configured");
237
+ const defaulted = keyInfos.filter((info) => info.state === "defaulted");
238
+ const unset = keyInfos.filter((info) => info.state === "unset");
239
+ const invalid = keyInfos.filter((info) => info.state === "invalid");
240
+ const status = warnings.length > 0 ? DoctorStatus.Warn : DoctorStatus.Ok;
241
+ const summary = {
242
+ checked: keyInfos.length,
243
+ configured: configured.length,
244
+ defaulted: defaulted.length,
245
+ unset: unset.length,
246
+ invalid: invalid.length,
247
+ secrets: keyInfos.filter((info) => info.isSecret).length,
248
+ };
249
+ const messageParts = [
250
+ `${summary.checked} keys checked`,
251
+ `${summary.configured} configured`,
252
+ ];
253
+ if (summary.defaulted > 0) {
254
+ messageParts.push(`${summary.defaulted} defaulted`);
255
+ }
256
+ if (summary.unset > 0) {
257
+ messageParts.push(`${summary.unset} unset`);
258
+ }
259
+ if (summary.invalid > 0) {
260
+ messageParts.push(`${summary.invalid} invalid`);
261
+ }
262
+ const details = buildDetails(configured, defaulted, unset, invalid, warnings);
263
+ const data = {
264
+ kind: "env-config",
265
+ summary,
266
+ warnings,
267
+ keys: keyInfos,
268
+ };
269
+ return {
154
270
  id: "env-diagnostics-01",
271
+ impact: DoctorImpact.Advisory,
155
272
  status,
156
273
  title: "env-config",
157
- message: summaryParts.join(", "),
158
- ...(missingCount > 0 ? { hint: `${missingCount} configuration keys are missing` } : {}),
159
- details: JSON.stringify(keyInfos),
274
+ message: messageParts.join(", "),
275
+ ...(warnings.length > 0
276
+ ? { hint: `${warnings.length} configuration issue${warnings.length === 1 ? "" : "s"} detected` }
277
+ : unset.length > 0
278
+ ? { hint: "Unset keys are optional unless you use the related integrations or overrides" }
279
+ : {}),
280
+ details,
281
+ data,
160
282
  };
161
- return result;
162
283
  }
163
284
  export const envDiagnosticsCheck = {
164
285
  id: "env-diagnostics-01",
165
286
  category: CATEGORY.ENV_DIAGNOSTICS,
166
287
  title: "env-config",
288
+ impact: DoctorImpact.Advisory,
167
289
  dependencies: [],
168
290
  execute: async () => {
169
291
  return checkEnvDiagnostics();