agentweaver 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -19
- package/dist/artifacts.js +6 -1
- package/dist/doctor/checks/cwd-context.js +4 -3
- package/dist/doctor/checks/env-diagnostics.js +168 -71
- package/dist/doctor/checks/flow-readiness.js +210 -198
- package/dist/doctor/index.js +1 -1
- package/dist/doctor/orchestrator.js +18 -7
- package/dist/doctor/runner.js +9 -8
- package/dist/doctor/types.js +12 -0
- package/dist/index.js +119 -55
- package/dist/interactive-ui.js +25 -25
- package/dist/pipeline/declarative-flows.js +1 -0
- package/dist/pipeline/flow-catalog.js +4 -0
- package/dist/pipeline/flow-specs/auto-common.json +1 -0
- package/dist/pipeline/flow-specs/auto-golang.json +2 -1
- package/dist/pipeline/flow-specs/bugz/bug-analyze.json +1 -0
- package/dist/pipeline/flow-specs/bugz/bug-fix.json +1 -0
- package/dist/pipeline/flow-specs/design-review.json +239 -0
- package/dist/pipeline/flow-specs/git-commit.json +1 -0
- package/dist/pipeline/flow-specs/gitlab/gitlab-diff-review.json +3 -2
- package/dist/pipeline/flow-specs/gitlab/gitlab-review.json +3 -2
- package/dist/pipeline/flow-specs/gitlab/mr-description.json +1 -0
- package/dist/pipeline/flow-specs/go/run-go-linter-loop.json +3 -2
- package/dist/pipeline/flow-specs/go/run-go-tests-loop.json +3 -2
- package/dist/pipeline/flow-specs/implement.json +13 -0
- package/dist/pipeline/flow-specs/plan-revise.json +261 -0
- package/dist/pipeline/flow-specs/plan.json +2 -1
- package/dist/pipeline/flow-specs/review/review-fix.json +1 -0
- package/dist/pipeline/flow-specs/review/review-loop.json +1 -0
- package/dist/pipeline/flow-specs/review/review-project.json +1 -0
- package/dist/pipeline/flow-specs/review/review.json +2 -1
- package/dist/pipeline/flow-specs/task-describe.json +67 -8
- package/dist/pipeline/node-registry.js +8 -0
- package/dist/pipeline/nodes/ensure-summary-json-node.js +59 -0
- package/dist/pipeline/nodes/git-commit-node.js +1 -1
- package/dist/pipeline/nodes/git-status-node.js +1 -1
- package/dist/pipeline/nodes/review-findings-form-node.js +8 -8
- package/dist/pipeline/nodes/user-input-node.js +2 -2
- package/dist/pipeline/prompt-registry.js +3 -1
- package/dist/pipeline/spec-types.js +2 -0
- package/dist/pipeline/value-resolver.js +11 -1
- package/dist/prompts.js +49 -2
- package/dist/runtime/design-review-input-contract.js +112 -0
- package/dist/runtime/plan-revise-input-contract.js +144 -0
- package/dist/runtime/process-runner.js +2 -2
- package/dist/runtime/ready-to-merge.js +10 -0
- package/dist/scope.js +13 -4
- package/dist/structured-artifact-schema-registry.js +1 -0
- package/dist/structured-artifact-schemas.json +117 -0
- package/dist/structured-artifacts.js +6 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ Typical usage looks like:
|
|
|
10
10
|
|
|
11
11
|
The important part is not that exact chain. The point is that AgentWeaver lets you design, operate, and evolve durable agent harnesses instead of accumulating one-off prompts and shell glue.
|
|
12
12
|
|
|
13
|
+
Для planning-heavy задач типовой путь теперь может включать и `plan -> design-review -> implement`, где `design-review` проверяет качество артефактов планирования до начала кодинга.
|
|
14
|
+
|
|
13
15
|
## What It Does
|
|
14
16
|
|
|
15
17
|
- Fetches Jira issue context by issue key or browse URL
|
|
@@ -69,25 +71,26 @@ This keeps workflow design in JSON while keeping implementation details in typed
|
|
|
69
71
|
|
|
70
72
|
User-invokable built-in commands currently map to these flow specs:
|
|
71
73
|
|
|
72
|
-
- `plan`
|
|
73
|
-
- `
|
|
74
|
-
- `
|
|
75
|
-
- `
|
|
76
|
-
- `review-
|
|
77
|
-
- `review-
|
|
78
|
-
- `
|
|
79
|
-
- `bug-fix
|
|
80
|
-
- `
|
|
81
|
-
- `
|
|
82
|
-
- `gitlab-review`
|
|
83
|
-
- `
|
|
84
|
-
- `
|
|
85
|
-
- `run-go-
|
|
86
|
-
- `
|
|
87
|
-
- `auto-
|
|
88
|
-
- `
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
- `plan` — fetches Jira task with attachments, generates clarifying questions for the developer, collects answers, and produces design, implementation plan, and QA plan as structured JSON and markdown artifacts
|
|
75
|
+
- `design-review` — выполняет структурированную критику planning artifacts и пишет dedicated artifact `design-review/v1`; статус `approved_with_warnings` считается ready-to-proceed и по-прежнему допускает `ready-to-merge.md`
|
|
76
|
+
- `task-describe` — generates a brief task description from a Jira issue or from manual input; when Jira is provided, fetches the issue and summarizes it; otherwise accepts free-form text and analyzes the codebase to produce a richer description
|
|
77
|
+
- `implement` — runs LLM-backed implementation based on previously approved design and plan artifacts; executes code changes locally in the project working directory
|
|
78
|
+
- `review` — performs code review of current changes against the task design and plan; produces structured review findings with severity levels and a ready-to-merge verdict
|
|
79
|
+
- `review-fix` — takes review findings, auto-selects blockers and criticals (or lets the developer pick manually), builds a targeted fix prompt, and applies fixes locally; runs mandatory checks after modifications
|
|
80
|
+
- `review-loop` — iteratively runs review → review-fix cycles up to 5 times; stops early when ready-to-merge is achieved; each iteration auto-selects blockers and critical findings for fixing
|
|
81
|
+
- `bug-analyze` — fetches a Bug-type Jira issue, validates the issue type, generates or reuses a cached task summary, and produces structured bug analysis: root cause hypothesis, fix design, and step-by-step fix plan
|
|
82
|
+
- `bug-fix` — applies the fix designed in bug-analyze; uses the root cause hypothesis, fix design, and fix plan artifacts as the source of truth to implement code changes locally
|
|
83
|
+
- `git-commit` — four-phase commit workflow: collects git status and diff, generates a commit message via LLM, presents a file selection form, then shows the editable message for confirmation and executes the commit
|
|
84
|
+
- `gitlab-diff-review` — prompts for a GitLab merge request URL, fetches the MR diff via GitLab API, and runs LLM-backed code review producing structured findings with severity levels and a ready-to-merge verdict
|
|
85
|
+
- `gitlab-review` — prompts for a GitLab merge request URL, fetches existing code review comments via GitLab API, assesses which findings are fair and which can be dismissed, then runs review-fix to apply fixes for the accepted findings
|
|
86
|
+
- `mr-description` — generates a concise merge request description based on the task context and current code changes; produces both markdown and structured JSON artifacts
|
|
87
|
+
- `run-go-tests-loop` — runs `run_go_tests.py` and analyzes failures; if tests fail, sends the error output to LLM for a fix and retries; repeats up to 5 attempts, stopping early on success
|
|
88
|
+
- `run-go-linter-loop` — runs `run_go_linter.py` and analyzes output; if the linter reports issues, sends them to LLM for a fix and retries; repeats up to 5 attempts, stopping early on success
|
|
89
|
+
- `auto-golang` — end-to-end resumable pipeline for Go projects: plan → implement → linter loop → test loop → review loop → final linter loop → final test loop; supports `--from` to restart from a specific phase and `auto-status`/`auto-reset` for state management
|
|
90
|
+
- `auto-common` — end-to-end resumable pipeline without language-specific checks: plan → implement → review loop; simplified alternative to auto-golang for projects that do not need Go linter/test loops
|
|
91
|
+
- `doctor` — diagnostics command that runs system, executor, and flow readiness health checks; supports filtering by category or check ID and JSON output
|
|
92
|
+
|
|
93
|
+
There are also built-in nested/helper flows that are loaded declaratively but are not direct top-level CLI commands, for example `review-project` (project-level code review used internally when no prior design/plan artifacts are present).
|
|
91
94
|
|
|
92
95
|
## Requirements
|
|
93
96
|
|
|
@@ -201,6 +204,7 @@ Direct flow execution:
|
|
|
201
204
|
|
|
202
205
|
```bash
|
|
203
206
|
agentweaver plan DEMO-1234
|
|
207
|
+
agentweaver design-review DEMO-1234
|
|
204
208
|
agentweaver task-describe DEMO-1234
|
|
205
209
|
agentweaver implement DEMO-1234
|
|
206
210
|
agentweaver review DEMO-1234
|
|
@@ -225,6 +229,7 @@ From a source checkout:
|
|
|
225
229
|
|
|
226
230
|
```bash
|
|
227
231
|
node dist/index.js plan DEMO-1234
|
|
232
|
+
node dist/index.js design-review DEMO-1234
|
|
228
233
|
node dist/index.js implement DEMO-1234
|
|
229
234
|
node dist/index.js review DEMO-1234
|
|
230
235
|
node dist/index.js auto-golang DEMO-1234
|
package/dist/artifacts.js
CHANGED
|
@@ -2,7 +2,6 @@ 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
|
-
export const REVIEW_FILE_RE = /^review-(.+)-(\d+)\.md$/;
|
|
6
5
|
export const READY_TO_MERGE_FILE = "ready-to-merge.md";
|
|
7
6
|
export function scopesRootDir() {
|
|
8
7
|
return path.join(process.cwd(), ".agentweaver", "scopes");
|
|
@@ -219,6 +218,12 @@ export function reviewFile(taskKey, iteration) {
|
|
|
219
218
|
export function reviewJsonFile(taskKey, iteration) {
|
|
220
219
|
return artifactJsonFile("review", taskKey, iteration);
|
|
221
220
|
}
|
|
221
|
+
export function designReviewFile(taskKey, iteration) {
|
|
222
|
+
return artifactFile("design-review", taskKey, iteration);
|
|
223
|
+
}
|
|
224
|
+
export function designReviewJsonFile(taskKey, iteration) {
|
|
225
|
+
return artifactJsonFile("design-review", taskKey, iteration);
|
|
226
|
+
}
|
|
222
227
|
export function reviewFixFile(taskKey, iteration) {
|
|
223
228
|
return artifactFile("review-fix", taskKey, iteration);
|
|
224
229
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { accessSync, constants, existsSync } from "node:fs";
|
|
2
|
-
import { DoctorStatus } from "../types.js";
|
|
2
|
+
import { DoctorImpact, DoctorStatus } from "../types.js";
|
|
3
3
|
import { CATEGORY } from "./category.js";
|
|
4
|
-
const SOFT_CHECK_ID = "cwd-context-01";
|
|
5
4
|
export const cwdContextCheck = {
|
|
6
5
|
id: "cwd-context-01",
|
|
7
6
|
category: CATEGORY.CWD_CONTEXT,
|
|
@@ -37,6 +36,7 @@ export const cwdContextCheck = {
|
|
|
37
36
|
if (permissionStatus === DoctorStatus.Ok && gitStatus === DoctorStatus.Ok) {
|
|
38
37
|
return {
|
|
39
38
|
id: "cwd-context-01",
|
|
39
|
+
impact: DoctorImpact.Blocking,
|
|
40
40
|
status: DoctorStatus.Ok,
|
|
41
41
|
title: "cwd-context",
|
|
42
42
|
message: `${permissionMessage}; ${gitStatusMessage}`,
|
|
@@ -46,6 +46,7 @@ export const cwdContextCheck = {
|
|
|
46
46
|
if (permissionStatus === DoctorStatus.Ok && gitStatus === DoctorStatus.Warn) {
|
|
47
47
|
return {
|
|
48
48
|
id: "cwd-context-01",
|
|
49
|
+
impact: DoctorImpact.Advisory,
|
|
49
50
|
status: DoctorStatus.Warn,
|
|
50
51
|
title: "cwd-context",
|
|
51
52
|
message: `${permissionMessage}; ${gitStatusMessage} (soft warning)`,
|
|
@@ -55,6 +56,7 @@ export const cwdContextCheck = {
|
|
|
55
56
|
}
|
|
56
57
|
const result = {
|
|
57
58
|
id: "cwd-context-01",
|
|
59
|
+
impact: DoctorImpact.Blocking,
|
|
58
60
|
status: permissionStatus,
|
|
59
61
|
title: "cwd-context",
|
|
60
62
|
message: permissionMessage,
|
|
@@ -66,4 +68,3 @@ export const cwdContextCheck = {
|
|
|
66
68
|
return result;
|
|
67
69
|
},
|
|
68
70
|
};
|
|
69
|
-
export const SOFT_CHECK_IDS = [SOFT_CHECK_ID];
|
|
@@ -1,8 +1,9 @@
|
|
|
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";
|
|
6
7
|
const MONITORED_KEYS = [
|
|
7
8
|
"JIRA_API_KEY",
|
|
8
9
|
"JIRA_USERNAME",
|
|
@@ -17,19 +18,16 @@ const MONITORED_KEYS = [
|
|
|
17
18
|
];
|
|
18
19
|
const SECRET_KEYS = new Set(["JIRA_API_KEY", "GITLAB_TOKEN"]);
|
|
19
20
|
const JIRA_AUTH_MODE_ALLOWED_VALUES = ["auto", "basic", "bearer"];
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const end = value.slice(-3);
|
|
31
|
-
return `${start}***${end}`;
|
|
32
|
-
}
|
|
21
|
+
const KEY_NOTES = {
|
|
22
|
+
JIRA_API_KEY: "Required for Jira-backed flows.",
|
|
23
|
+
JIRA_USERNAME: "Required only for Jira Cloud basic auth.",
|
|
24
|
+
GITLAB_TOKEN: "Required for GitLab-backed flows.",
|
|
25
|
+
AGENTWEAVER_HOME: "Optional override for the AgentWeaver package home.",
|
|
26
|
+
CODEX_BIN: "Optional override for the codex executable path.",
|
|
27
|
+
CODEX_MODEL: "Optional fallback model override for Codex-backed executors.",
|
|
28
|
+
OPENCODE_BIN: "Optional override for the opencode executable path.",
|
|
29
|
+
OPENCODE_MODEL: "Optional fallback model override for OpenCode-backed executors.",
|
|
30
|
+
};
|
|
33
31
|
function globalConfigDir() {
|
|
34
32
|
return path.join(os.homedir(), ".agentweaver");
|
|
35
33
|
}
|
|
@@ -69,101 +67,200 @@ function getProjectEnvPath() {
|
|
|
69
67
|
function getGlobalEnvPath() {
|
|
70
68
|
return path.join(globalConfigDir(), ".env");
|
|
71
69
|
}
|
|
72
|
-
function determineSource(key, shellSnapshot, currentValue) {
|
|
70
|
+
function determineSource(key, shellSnapshot, projectEnv, globalEnv, currentValue) {
|
|
73
71
|
if (currentValue === null) {
|
|
74
72
|
return "missing";
|
|
75
73
|
}
|
|
76
|
-
|
|
77
|
-
if (isInShellSnapshot) {
|
|
74
|
+
if (Object.prototype.hasOwnProperty.call(shellSnapshot, key)) {
|
|
78
75
|
return "shell";
|
|
79
76
|
}
|
|
80
|
-
|
|
81
|
-
if (globalEnv.hasOwnProperty(key)) {
|
|
82
|
-
return "global";
|
|
83
|
-
}
|
|
84
|
-
const projectEnv = parseEnvFileRaw(getProjectEnvPath());
|
|
85
|
-
if (projectEnv.hasOwnProperty(key)) {
|
|
77
|
+
if (Object.prototype.hasOwnProperty.call(projectEnv, key)) {
|
|
86
78
|
return "project-local";
|
|
87
79
|
}
|
|
80
|
+
if (Object.prototype.hasOwnProperty.call(globalEnv, key)) {
|
|
81
|
+
return "global";
|
|
82
|
+
}
|
|
88
83
|
return "shell";
|
|
89
84
|
}
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return path.join(os.homedir(), ".agentweaver");
|
|
94
|
-
default:
|
|
95
|
-
return null;
|
|
85
|
+
function defaultNote(key) {
|
|
86
|
+
if (key === "JIRA_AUTH_MODE") {
|
|
87
|
+
return "Defaults to auto.";
|
|
96
88
|
}
|
|
89
|
+
return null;
|
|
97
90
|
}
|
|
98
91
|
function validateJiraAuthMode(value) {
|
|
99
92
|
if (value === null) {
|
|
100
93
|
return true;
|
|
101
94
|
}
|
|
102
|
-
return JIRA_AUTH_MODE_ALLOWED_VALUES.includes(value);
|
|
95
|
+
return JIRA_AUTH_MODE_ALLOWED_VALUES.includes(value.trim().toLowerCase());
|
|
96
|
+
}
|
|
97
|
+
function keyNote(key) {
|
|
98
|
+
return KEY_NOTES[key];
|
|
99
|
+
}
|
|
100
|
+
function formatKeyLine(info) {
|
|
101
|
+
const source = info.state === "unset" ? "unset" : info.source === "default" ? "default" : info.source;
|
|
102
|
+
const secretLabel = info.isSecret ? ", secret" : "";
|
|
103
|
+
const noteLabel = info.note ? `, ${info.note}` : "";
|
|
104
|
+
return `- ${info.key} (${source}${secretLabel}${noteLabel})`;
|
|
105
|
+
}
|
|
106
|
+
function buildDetails(configured, defaulted, unset, invalid, warnings) {
|
|
107
|
+
const lines = [];
|
|
108
|
+
if (warnings.length > 0) {
|
|
109
|
+
lines.push("warnings:");
|
|
110
|
+
for (const warning of warnings) {
|
|
111
|
+
lines.push(`- ${warning}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (configured.length > 0) {
|
|
115
|
+
if (lines.length > 0) {
|
|
116
|
+
lines.push("");
|
|
117
|
+
}
|
|
118
|
+
lines.push("configured:");
|
|
119
|
+
for (const info of configured) {
|
|
120
|
+
lines.push(formatKeyLine(info));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (defaulted.length > 0) {
|
|
124
|
+
if (lines.length > 0) {
|
|
125
|
+
lines.push("");
|
|
126
|
+
}
|
|
127
|
+
lines.push("defaulted:");
|
|
128
|
+
for (const info of defaulted) {
|
|
129
|
+
lines.push(formatKeyLine(info));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (unset.length > 0) {
|
|
133
|
+
if (lines.length > 0) {
|
|
134
|
+
lines.push("");
|
|
135
|
+
}
|
|
136
|
+
lines.push("unset:");
|
|
137
|
+
for (const info of unset) {
|
|
138
|
+
lines.push(formatKeyLine(info));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (invalid.length > 0) {
|
|
142
|
+
if (lines.length > 0) {
|
|
143
|
+
lines.push("");
|
|
144
|
+
}
|
|
145
|
+
lines.push("invalid:");
|
|
146
|
+
for (const info of invalid) {
|
|
147
|
+
lines.push(formatKeyLine(info));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return lines.join("\n");
|
|
103
151
|
}
|
|
104
152
|
function checkEnvDiagnostics() {
|
|
105
153
|
const shellSnapshot = {};
|
|
106
154
|
for (const key of Object.keys(process.env)) {
|
|
107
|
-
|
|
155
|
+
const value = process.env[key];
|
|
156
|
+
if (value !== undefined) {
|
|
157
|
+
shellSnapshot[key] = value;
|
|
158
|
+
}
|
|
108
159
|
}
|
|
109
160
|
const projectEnv = parseEnvFileRaw(getProjectEnvPath());
|
|
110
161
|
const globalEnv = parseEnvFileRaw(getGlobalEnvPath());
|
|
111
162
|
const keyInfos = [];
|
|
112
|
-
|
|
163
|
+
const warnings = [];
|
|
164
|
+
const jiraApiKey = process.env.JIRA_API_KEY?.trim() || null;
|
|
165
|
+
const jiraUsername = process.env.JIRA_USERNAME?.trim() || null;
|
|
166
|
+
const jiraAuthModeRaw = process.env.JIRA_AUTH_MODE?.trim() || null;
|
|
167
|
+
const jiraBaseUrl = process.env.JIRA_BASE_URL?.trim() || null;
|
|
113
168
|
for (const key of MONITORED_KEYS) {
|
|
114
|
-
const currentValue = process.env[key]
|
|
115
|
-
const source = determineSource(key, shellSnapshot, currentValue);
|
|
169
|
+
const currentValue = process.env[key]?.trim() || null;
|
|
116
170
|
const isSecret = SECRET_KEYS.has(key);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
171
|
+
const source = determineSource(key, shellSnapshot, projectEnv, globalEnv, currentValue);
|
|
172
|
+
const defaultHint = currentValue === null ? defaultNote(key) : null;
|
|
173
|
+
let state = "configured";
|
|
174
|
+
if (currentValue === null && defaultHint) {
|
|
175
|
+
state = "defaulted";
|
|
176
|
+
}
|
|
177
|
+
else if (currentValue === null) {
|
|
178
|
+
state = "unset";
|
|
120
179
|
}
|
|
121
|
-
|
|
122
|
-
|
|
180
|
+
if (key === "JIRA_AUTH_MODE" && currentValue !== null && !validateJiraAuthMode(currentValue)) {
|
|
181
|
+
state = "invalid";
|
|
123
182
|
}
|
|
124
|
-
const
|
|
183
|
+
const note = state === "defaulted" ? defaultHint ?? undefined : keyNote(key);
|
|
184
|
+
keyInfos.push({
|
|
125
185
|
key,
|
|
126
|
-
source,
|
|
127
|
-
|
|
128
|
-
maskedValue,
|
|
186
|
+
source: state === "defaulted" ? "default" : source,
|
|
187
|
+
state,
|
|
129
188
|
isSecret,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
189
|
+
...(note ? { note } : {}),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
const jiraHasAnyConfig = !!(jiraApiKey || jiraBaseUrl || jiraUsername || jiraAuthModeRaw);
|
|
193
|
+
const jiraHasCorePair = !!(jiraApiKey && jiraBaseUrl);
|
|
194
|
+
if ((jiraApiKey && !jiraBaseUrl) || (!jiraApiKey && jiraBaseUrl)) {
|
|
195
|
+
warnings.push("Jira configuration is partial: set both JIRA_API_KEY and JIRA_BASE_URL for Jira-backed flows.");
|
|
196
|
+
}
|
|
197
|
+
if (jiraAuthModeRaw !== null && !validateJiraAuthMode(jiraAuthModeRaw)) {
|
|
198
|
+
warnings.push("JIRA_AUTH_MODE must be one of: auto, basic, bearer.");
|
|
199
|
+
}
|
|
200
|
+
if (jiraHasCorePair && jiraBaseUrl) {
|
|
201
|
+
const authMode = jiraAuthModeRaw?.toLowerCase() || "auto";
|
|
202
|
+
const isCloud = detectJiraDeployment(jiraBaseUrl) === "cloud";
|
|
203
|
+
const usesBasicAuth = authMode === "basic" || (authMode === "auto" && isCloud);
|
|
204
|
+
if (usesBasicAuth && !jiraUsername) {
|
|
205
|
+
warnings.push("JIRA_USERNAME is required for Jira Cloud basic auth with the current Jira URL and auth mode.");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (jiraHasAnyConfig && jiraUsername && !jiraApiKey && !jiraBaseUrl) {
|
|
209
|
+
warnings.push("JIRA_USERNAME is set without the Jira API key/base URL pair.");
|
|
210
|
+
}
|
|
211
|
+
const configured = keyInfos.filter((info) => info.state === "configured");
|
|
212
|
+
const defaulted = keyInfos.filter((info) => info.state === "defaulted");
|
|
213
|
+
const unset = keyInfos.filter((info) => info.state === "unset");
|
|
214
|
+
const invalid = keyInfos.filter((info) => info.state === "invalid");
|
|
215
|
+
const status = warnings.length > 0 ? DoctorStatus.Warn : DoctorStatus.Ok;
|
|
216
|
+
const summary = {
|
|
217
|
+
checked: keyInfos.length,
|
|
218
|
+
configured: configured.length,
|
|
219
|
+
defaulted: defaulted.length,
|
|
220
|
+
unset: unset.length,
|
|
221
|
+
invalid: invalid.length,
|
|
222
|
+
secrets: keyInfos.filter((info) => info.isSecret).length,
|
|
223
|
+
};
|
|
224
|
+
const messageParts = [
|
|
225
|
+
`${summary.checked} keys checked`,
|
|
226
|
+
`${summary.configured} configured`,
|
|
227
|
+
];
|
|
228
|
+
if (summary.defaulted > 0) {
|
|
229
|
+
messageParts.push(`${summary.defaulted} defaulted`);
|
|
230
|
+
}
|
|
231
|
+
if (summary.unset > 0) {
|
|
232
|
+
messageParts.push(`${summary.unset} unset`);
|
|
233
|
+
}
|
|
234
|
+
if (summary.invalid > 0) {
|
|
235
|
+
messageParts.push(`${summary.invalid} invalid`);
|
|
236
|
+
}
|
|
237
|
+
const details = buildDetails(configured, defaulted, unset, invalid, warnings);
|
|
238
|
+
const data = {
|
|
239
|
+
kind: "env-config",
|
|
240
|
+
summary,
|
|
241
|
+
warnings,
|
|
242
|
+
keys: keyInfos,
|
|
243
|
+
};
|
|
244
|
+
return {
|
|
154
245
|
id: "env-diagnostics-01",
|
|
246
|
+
impact: DoctorImpact.Advisory,
|
|
155
247
|
status,
|
|
156
248
|
title: "env-config",
|
|
157
|
-
message:
|
|
158
|
-
...(
|
|
159
|
-
|
|
249
|
+
message: messageParts.join(", "),
|
|
250
|
+
...(warnings.length > 0
|
|
251
|
+
? { hint: `${warnings.length} configuration issue${warnings.length === 1 ? "" : "s"} detected` }
|
|
252
|
+
: unset.length > 0
|
|
253
|
+
? { hint: "Unset keys are optional unless you use the related integrations or overrides" }
|
|
254
|
+
: {}),
|
|
255
|
+
details,
|
|
256
|
+
data,
|
|
160
257
|
};
|
|
161
|
-
return result;
|
|
162
258
|
}
|
|
163
259
|
export const envDiagnosticsCheck = {
|
|
164
260
|
id: "env-diagnostics-01",
|
|
165
261
|
category: CATEGORY.ENV_DIAGNOSTICS,
|
|
166
262
|
title: "env-config",
|
|
263
|
+
impact: DoctorImpact.Advisory,
|
|
167
264
|
dependencies: [],
|
|
168
265
|
execute: async () => {
|
|
169
266
|
return checkEnvDiagnostics();
|