agentweaver 0.1.16 → 0.1.18
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 +148 -27
- package/dist/artifacts.js +114 -3
- package/dist/doctor/checks/executors.js +2 -2
- package/dist/flow-state.js +138 -1
- package/dist/index.js +421 -82
- package/dist/interactive/controller.js +305 -36
- package/dist/interactive/ink/index.js +24 -3
- package/dist/interactive/state.js +1 -0
- package/dist/interactive/tree.js +2 -2
- package/dist/interactive/web/index.js +179 -0
- package/dist/interactive/web/protocol.js +154 -0
- package/dist/interactive/web/server.js +575 -0
- package/dist/interactive/web/static/app.js +709 -0
- package/dist/interactive/web/static/index.html +77 -0
- package/dist/interactive/web/static/styles.css +2 -0
- package/dist/interactive/web/static/styles.input.css +469 -0
- package/dist/pipeline/auto-flow.js +9 -6
- package/dist/pipeline/context.js +6 -5
- package/dist/pipeline/declarative-flows.js +39 -20
- package/dist/pipeline/flow-catalog.js +40 -14
- package/dist/pipeline/flow-specs/auto-common-guided.json +313 -0
- package/dist/pipeline/flow-specs/auto-common.json +4 -1
- package/dist/pipeline/flow-specs/auto-golang.json +27 -1
- package/dist/pipeline/flow-specs/design-review/design-review-loop.json +15 -1
- package/dist/pipeline/flow-specs/design-review.json +2 -0
- package/dist/pipeline/flow-specs/implement.json +3 -1
- package/dist/pipeline/flow-specs/plan.json +8 -2
- package/dist/pipeline/flow-specs/playbook-init.json +199 -0
- package/dist/pipeline/flow-specs/review/review-fix.json +3 -1
- package/dist/pipeline/flow-specs/review/review-loop.json +4 -0
- package/dist/pipeline/flow-specs/review/review.json +2 -0
- package/dist/pipeline/launch-profile-config.js +30 -18
- package/dist/pipeline/node-contract.js +1 -0
- package/dist/pipeline/node-registry.js +119 -5
- package/dist/pipeline/nodes/flow-run-node.js +200 -173
- package/dist/pipeline/nodes/llm-prompt-node.js +15 -33
- package/dist/pipeline/nodes/playbook-ensure-node.js +115 -0
- package/dist/pipeline/nodes/playbook-inventory-node.js +51 -0
- package/dist/pipeline/nodes/playbook-questions-form-node.js +166 -0
- package/dist/pipeline/nodes/playbook-write-node.js +243 -0
- package/dist/pipeline/nodes/project-guidance-node.js +69 -0
- package/dist/pipeline/plugin-loader.js +389 -0
- package/dist/pipeline/plugin-types.js +1 -0
- package/dist/pipeline/prompt-registry.js +4 -1
- package/dist/pipeline/prompt-runtime.js +6 -2
- package/dist/pipeline/registry.js +71 -4
- package/dist/pipeline/spec-compiler.js +1 -0
- package/dist/pipeline/spec-loader.js +14 -0
- package/dist/pipeline/spec-types.js +19 -0
- package/dist/pipeline/spec-validator.js +6 -0
- package/dist/pipeline/value-resolver.js +41 -2
- package/dist/playbook/practice-candidates.js +12 -0
- package/dist/playbook/repo-inventory.js +208 -0
- package/dist/plugin-sdk.js +1 -0
- package/dist/prompts.js +31 -0
- package/dist/runtime/artifact-registry.js +3 -0
- package/dist/runtime/execution-routing.js +25 -19
- package/dist/runtime/interactive-execution-routing.js +66 -57
- package/dist/runtime/playbook.js +485 -0
- package/dist/runtime/project-guidance.js +339 -0
- package/dist/structured-artifact-schema-registry.js +8 -0
- package/dist/structured-artifact-schemas.json +235 -0
- package/dist/structured-artifacts.js +7 -1
- package/docs/declarative-workflows.md +565 -0
- package/docs/example/.flows/examples/claude-example.json +50 -0
- package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
- package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
- package/docs/examples/.flows/claude-example.json +50 -0
- package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
- package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
- package/docs/features.md +77 -0
- package/docs/playbook.md +327 -0
- package/docs/plugin-sdk.md +731 -0
- package/package.json +13 -4
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, writeSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designReviewFile, designReviewJsonFile, gitlabDiffFile, gitlabDiffJsonFile, ensureScopeWorkspaceDir, gitlabReviewFile, gitlabReviewJsonFile, instantTaskInputJsonFile, latestArtifactIteration, nextArtifactIteration, readyToMergeFile, requireArtifacts, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixSelectionJsonFile, reviewJsonFile, scopeWorkspaceDir, flowStateFile, taskSummaryFile, } from "./artifacts.js";
|
|
6
|
+
import { archiveActiveAttempt, bugAnalyzeArtifacts, bugAnalyzeJsonFile, bugFixDesignJsonFile, bugFixPlanJsonFile, designReviewFile, designReviewJsonFile, gitlabDiffFile, gitlabDiffJsonFile, ensureScopeWorkspaceDir, gitlabReviewFile, gitlabReviewJsonFile, instantTaskInputJsonFile, latestArtifactIteration, nextArtifactIteration, readyToMergeFile, requireArtifacts, reviewAssessmentFile, reviewAssessmentJsonFile, reviewFile, reviewFixSelectionJsonFile, reviewJsonFile, scopeWorkspaceDir, flowStateFile, taskSummaryFile, } from "./artifacts.js";
|
|
7
7
|
import { FlowInterruptedError, TaskRunnerError } from "./errors.js";
|
|
8
|
-
import { createFlowRunState,
|
|
8
|
+
import { createFlowRunState, classifyFlowLaunchAvailability, loadFlowRunState, prepareFlowStateForContinue, prepareFlowStateForResume, resetFlowRunState, rewindFlowRunStateToPhase, saveFlowRunState, stripExecutionStatePayload, } from "./flow-state.js";
|
|
9
9
|
import { requireJiraTaskFile } from "./jira.js";
|
|
10
10
|
import { validateStructuredArtifacts } from "./structured-artifacts.js";
|
|
11
11
|
import { AGENTWEAVER_REVIEW_BLOCKING_SEVERITIES_ENV, parseReviewSeverityCsv, resolveReviewBlockingSeveritiesFromEnv, } from "./review-severity.js";
|
|
@@ -15,6 +15,7 @@ import { createPipelineContext } from "./pipeline/context.js";
|
|
|
15
15
|
import { collectFlowRoutingGroups, loadDeclarativeFlow } from "./pipeline/declarative-flows.js";
|
|
16
16
|
import { runExpandedPhase } from "./pipeline/declarative-flow-runner.js";
|
|
17
17
|
import { builtInCommandFlowFile, findCatalogEntry, flowRoutingGroups, isBuiltInCommandFlowId, loadInteractiveFlowCatalog, toDeclarativeFlowRef, } from "./pipeline/flow-catalog.js";
|
|
18
|
+
import { createPipelineRegistryContext } from "./pipeline/plugin-loader.js";
|
|
18
19
|
import { DEFAULT_LAUNCH_PROFILE, } from "./pipeline/launch-profile-config.js";
|
|
19
20
|
import { withCanonicalReviewLoopParams } from "./pipeline/review-iteration.js";
|
|
20
21
|
import { evaluateCondition, resolveValue } from "./pipeline/value-resolver.js";
|
|
@@ -31,12 +32,14 @@ import { clearReadyToMergeFile } from "./runtime/ready-to-merge.js";
|
|
|
31
32
|
import { describeExecutionRouting, executorsForRoutingGroups, resolveExecutionRouting, } from "./runtime/execution-routing.js";
|
|
32
33
|
import { requestInteractiveExecutionRouting } from "./runtime/interactive-execution-routing.js";
|
|
33
34
|
import { createInteractiveSession } from "./interactive/create-interactive-session.js";
|
|
35
|
+
import { createWebInteractiveSession } from "./interactive/web/index.js";
|
|
34
36
|
import { bye, printError, printInfo, printPanel, printSummary, setFlowExecutionState, stripAnsi, } from "./tui.js";
|
|
35
37
|
import { requestUserInputInTerminal } from "./user-input.js";
|
|
36
38
|
import { runDoctorCommand } from "./doctor/index.js";
|
|
37
|
-
import {
|
|
39
|
+
import { requestJiraContext, resolveProjectScope, } from "./scope.js";
|
|
38
40
|
const COMMANDS = [
|
|
39
41
|
"auto-golang",
|
|
42
|
+
"auto-common-guided",
|
|
40
43
|
"auto-common",
|
|
41
44
|
"auto-simple",
|
|
42
45
|
"auto-status",
|
|
@@ -52,7 +55,9 @@ const COMMANDS = [
|
|
|
52
55
|
"mr-description",
|
|
53
56
|
"plan",
|
|
54
57
|
"plan-revise",
|
|
58
|
+
"playbook-init",
|
|
55
59
|
"task-describe",
|
|
60
|
+
"web",
|
|
56
61
|
"implement",
|
|
57
62
|
"review",
|
|
58
63
|
"review-fix",
|
|
@@ -60,8 +65,17 @@ const COMMANDS = [
|
|
|
60
65
|
"run-go-tests-loop",
|
|
61
66
|
"run-go-linter-loop",
|
|
62
67
|
];
|
|
68
|
+
const INTERACTIVE_SCOPE_WATCH_INTERVAL_MS = 1500;
|
|
69
|
+
const WEB_AUTH_USERNAME_ENV = "AGENTWEAVER_WEB_USERNAME";
|
|
70
|
+
const WEB_AUTH_PASSWORD_ENV = "AGENTWEAVER_WEB_PASSWORD";
|
|
63
71
|
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
64
72
|
const PACKAGE_ROOT = path.resolve(MODULE_DIR, "..");
|
|
73
|
+
function writeStdoutSync(text) {
|
|
74
|
+
writeSync(process.stdout.fd, text);
|
|
75
|
+
}
|
|
76
|
+
function writeStderrSync(text) {
|
|
77
|
+
writeSync(process.stderr.fd, text);
|
|
78
|
+
}
|
|
65
79
|
function createRuntimeServices(signal) {
|
|
66
80
|
return {
|
|
67
81
|
resolveCmd,
|
|
@@ -70,6 +84,37 @@ function createRuntimeServices(signal) {
|
|
|
70
84
|
};
|
|
71
85
|
}
|
|
72
86
|
const runtimeServices = createRuntimeServices();
|
|
87
|
+
function isExternalWebHost(host) {
|
|
88
|
+
const normalized = (host?.trim() || "127.0.0.1").toLowerCase();
|
|
89
|
+
if (normalized === "127.0.0.1" || normalized === "::1" || normalized === "localhost") {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const unbracketed = normalized.startsWith("[") && normalized.endsWith("]") ? normalized.slice(1, -1) : normalized;
|
|
93
|
+
if (unbracketed === "::1") {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
function resolveWebAuthConfig() {
|
|
99
|
+
const username = process.env[WEB_AUTH_USERNAME_ENV]?.trim() ?? "";
|
|
100
|
+
const password = process.env[WEB_AUTH_PASSWORD_ENV] ?? "";
|
|
101
|
+
const hasUsername = username.length > 0;
|
|
102
|
+
const hasPassword = password.length > 0;
|
|
103
|
+
if (hasUsername !== hasPassword) {
|
|
104
|
+
throw new TaskRunnerError(`Web UI auth requires both ${WEB_AUTH_USERNAME_ENV} and ${WEB_AUTH_PASSWORD_ENV}.`);
|
|
105
|
+
}
|
|
106
|
+
if (!hasUsername || !hasPassword) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return { username, password };
|
|
110
|
+
}
|
|
111
|
+
function requireWebAuthForHost(host, auth) {
|
|
112
|
+
if (!isExternalWebHost(host) || auth) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
throw new TaskRunnerError(`External Web UI binding requires ${WEB_AUTH_USERNAME_ENV} and ${WEB_AUTH_PASSWORD_ENV}. ` +
|
|
116
|
+
"Use localhost for no-auth local access, or configure credentials before using --listen-all or --host with an external interface.");
|
|
117
|
+
}
|
|
73
118
|
function buildFailureOutputPreview(output) {
|
|
74
119
|
const normalized = stripAnsi(output).replace(/\r\n/g, "\n").trim();
|
|
75
120
|
if (!normalized) {
|
|
@@ -109,6 +154,7 @@ function usage() {
|
|
|
109
154
|
agentweaver
|
|
110
155
|
agentweaver <jira-browse-url|jira-issue-key>
|
|
111
156
|
agentweaver --force <jira-browse-url|jira-issue-key>
|
|
157
|
+
agentweaver web [--no-open] [--host <host>|--listen-all] [<jira-browse-url|jira-issue-key>]
|
|
112
158
|
agentweaver git-commit [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
113
159
|
agentweaver gitlab-diff-review [--dry] [--verbose] [--prompt <text>] [--scope <name>]
|
|
114
160
|
agentweaver gitlab-review [--dry] [--verbose] [--prompt <text>] [--scope <name>]
|
|
@@ -120,6 +166,7 @@ function usage() {
|
|
|
120
166
|
agentweaver mr-description [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
121
167
|
agentweaver plan [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [<jira-browse-url|jira-issue-key>]
|
|
122
168
|
agentweaver plan-revise [--dry] [--verbose] [--prompt <text>] <jira-browse-url|jira-issue-key>
|
|
169
|
+
agentweaver playbook-init [--dry] [--verbose] [--prompt <text>] [--accept-playbook-draft] [--scope <name>]
|
|
123
170
|
agentweaver task-describe [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
124
171
|
agentweaver implement [--dry] [--verbose] [--prompt <text>] [--scope <name>] [<jira-browse-url|jira-issue-key>]
|
|
125
172
|
agentweaver review [--dry] [--verbose] [--prompt <text>] [--scope <name>] [--blocking-severities <list>] [<jira-browse-url|jira-issue-key>]
|
|
@@ -130,6 +177,7 @@ function usage() {
|
|
|
130
177
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] [<jira-browse-url|jira-issue-key>]
|
|
131
178
|
agentweaver auto-golang [--dry] [--verbose] [--prompt <text>] --from <phase> [<jira-browse-url|jira-issue-key>]
|
|
132
179
|
agentweaver auto-golang --help-phases
|
|
180
|
+
agentweaver auto-common-guided [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] [--accept-playbook-draft] <jira-browse-url|jira-issue-key>
|
|
133
181
|
agentweaver auto-common [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
134
182
|
agentweaver auto-common --help-phases
|
|
135
183
|
agentweaver auto-simple [--dry] [--verbose] [--prompt <text>] [--md-lang <en|ru>] <jira-browse-url|jira-issue-key>
|
|
@@ -145,12 +193,19 @@ Interactive Mode:
|
|
|
145
193
|
Flags:
|
|
146
194
|
--version Show package version
|
|
147
195
|
--force In interactive mode, regenerate task summary in Jira-backed flows
|
|
196
|
+
--no-open Web command only: print the Web UI URL without opening a browser
|
|
197
|
+
--host Web command only: bind Web UI to this host (default: 127.0.0.1)
|
|
198
|
+
--listen-all Web command only: bind Web UI to 0.0.0.0
|
|
148
199
|
--dry Fetch Jira task, but print codex/opencode commands instead of executing them
|
|
149
200
|
--verbose Show live stdout/stderr of launched commands
|
|
150
201
|
--scope Explicit workflow scope name for non-Jira runs except instant-task
|
|
151
202
|
--prompt Extra prompt text appended to the base prompt
|
|
203
|
+
--resume Resume an interrupted run when valid
|
|
204
|
+
--continue Continue a terminated iterative run when valid
|
|
205
|
+
--restart Archive the active attempt and start a fresh run
|
|
152
206
|
--blocking-severities Comma-separated severities that block merge and drive review-fix auto-selection
|
|
153
|
-
--md-lang Language for markdown
|
|
207
|
+
--md-lang Language for workflow markdown artifacts only: en (English) or ru (Russian, default)
|
|
208
|
+
--accept-playbook-draft Non-interactively accept generated playbook content for playbook-init or auto-common-guided missing-manifest runs
|
|
154
209
|
|
|
155
210
|
Required environment variables:
|
|
156
211
|
JIRA_API_KEY Jira API token used for Jira-backed flows (Bearer by default, or Basic with Jira Cloud)
|
|
@@ -166,9 +221,15 @@ Optional environment variables:
|
|
|
166
221
|
CODEX_MODEL
|
|
167
222
|
OPENCODE_BIN
|
|
168
223
|
OPENCODE_MODEL
|
|
224
|
+
AGENTWEAVER_WEB_NO_OPEN Set to 1 to disable browser auto-open for agentweaver web
|
|
225
|
+
${WEB_AUTH_USERNAME_ENV} Web UI Basic auth username; required for external Web UI binding
|
|
226
|
+
${WEB_AUTH_PASSWORD_ENV} Web UI Basic auth password; required for external Web UI binding
|
|
169
227
|
|
|
170
228
|
Notes:
|
|
171
229
|
- Jira-backed task flows will ask for Jira task via user-input when it is not passed as an argument. task-describe can also work from a manual task description without Jira.
|
|
230
|
+
- agentweaver web binds to 127.0.0.1 by default on an operating-system-assigned port and does not require auth unless Web UI credentials are configured.
|
|
231
|
+
- External Web UI binding through --listen-all, --host 0.0.0.0, --host ::, non-loopback IPs, or hostnames other than localhost requires ${WEB_AUTH_USERNAME_ENV} and ${WEB_AUTH_PASSWORD_ENV}.
|
|
232
|
+
- Web UI Basic auth over plain HTTP is suitable only on trusted networks; use TLS termination or a reverse proxy on untrusted networks.
|
|
172
233
|
- instant-task always uses the current branch-derived project scope and rejects explicit scope overrides or Jira arguments.
|
|
173
234
|
- All flow state and artifacts are stored in the current project scope by default.
|
|
174
235
|
- gitlab-review and gitlab-diff-review ask for GitLab merge request URL via user-input.
|
|
@@ -186,12 +247,12 @@ function packageVersion() {
|
|
|
186
247
|
function normalizeAutoPhaseId(phaseId) {
|
|
187
248
|
return phaseId.trim().toLowerCase().replaceAll("-", "_");
|
|
188
249
|
}
|
|
189
|
-
function autoPhaseIds() {
|
|
190
|
-
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" }).phases.map((phase) => phase.id);
|
|
250
|
+
async function autoPhaseIds() {
|
|
251
|
+
return (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" })).phases.map((phase) => phase.id);
|
|
191
252
|
}
|
|
192
|
-
function validateAutoPhaseId(phaseId) {
|
|
253
|
+
async function validateAutoPhaseId(phaseId) {
|
|
193
254
|
const normalized = normalizeAutoPhaseId(phaseId);
|
|
194
|
-
if (!autoPhaseIds().includes(normalized)) {
|
|
255
|
+
if (!(await autoPhaseIds()).includes(normalized)) {
|
|
195
256
|
throw new TaskRunnerError(`Unknown auto-golang phase: ${phaseId}\nUse 'agentweaver auto-golang --help-phases' or '/help auto-golang' to list valid phases.`);
|
|
196
257
|
}
|
|
197
258
|
return normalized;
|
|
@@ -215,6 +276,17 @@ function buildFlowResumeDetails(state) {
|
|
|
215
276
|
}
|
|
216
277
|
return lines.join("\n");
|
|
217
278
|
}
|
|
279
|
+
function buildFlowContinueDetails(state) {
|
|
280
|
+
const lines = [
|
|
281
|
+
"Continuable loop boundary found.",
|
|
282
|
+
`Updated: ${state.updatedAt}`,
|
|
283
|
+
];
|
|
284
|
+
if (state.continuation?.stopPhaseId && state.continuation?.stopStepId) {
|
|
285
|
+
lines.push(`Stopped at: ${state.continuation.stopPhaseId}:${state.continuation.stopStepId}`);
|
|
286
|
+
}
|
|
287
|
+
lines.push("Continue will preserve existing artifacts and start the next iteration from active inputs.");
|
|
288
|
+
return lines.join("\n");
|
|
289
|
+
}
|
|
218
290
|
function buildResolverContext(pipelineContext, flowParams, flowConstants, repeatVars, executionState) {
|
|
219
291
|
return {
|
|
220
292
|
flowParams,
|
|
@@ -288,7 +360,7 @@ function validateDeclarativePhaseResumeState(phase, phaseState, pipelineContext,
|
|
|
288
360
|
}
|
|
289
361
|
}
|
|
290
362
|
}
|
|
291
|
-
function validateDeclarativeFlowResumeState(flowEntry, config, state, executionRouting, runtime = runtimeServices) {
|
|
363
|
+
async function validateDeclarativeFlowResumeState(flowEntry, config, state, executionRouting, runtime = runtimeServices) {
|
|
292
364
|
if (state.flowId === "auto-common") {
|
|
293
365
|
const persistedPhaseIds = state.executionState.phases.map((p) => p.id);
|
|
294
366
|
const hasLegacyPlanningGatePhases = persistedPhaseIds.some((id) => ["design_review", "verdict", "plan_revision", "design_review_repeat", "verdict_repeat"].includes(id));
|
|
@@ -308,7 +380,7 @@ function validateDeclarativeFlowResumeState(flowEntry, config, state, executionR
|
|
|
308
380
|
if (flowRequiresTaskScope(flowEntry) && !config.jiraRef) {
|
|
309
381
|
throw new TaskRunnerError("Resume is impossible because Jira context is missing for this flow state. Use restart.");
|
|
310
382
|
}
|
|
311
|
-
const pipelineContext = createPipelineContext({
|
|
383
|
+
const pipelineContext = await createPipelineContext({
|
|
312
384
|
issueKey: config.taskKey,
|
|
313
385
|
jiraRef: config.jiraRef,
|
|
314
386
|
dryRun: config.dryRun,
|
|
@@ -338,51 +410,59 @@ function buildInteractiveBaseConfig(flowId, scope) {
|
|
|
338
410
|
...(flowId !== "instant-task" && scope.jiraRef ? { jiraRef: scope.jiraRef } : {}),
|
|
339
411
|
});
|
|
340
412
|
}
|
|
341
|
-
function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
413
|
+
async function lookupInteractiveFlowResume(flowEntry, currentScope) {
|
|
342
414
|
const directState = loadFlowRunState(currentScope.scopeKey, flowEntry.id);
|
|
343
|
-
|
|
415
|
+
const availability = classifyFlowLaunchAvailability(directState);
|
|
416
|
+
if (directState && availability.resume.available) {
|
|
344
417
|
try {
|
|
345
418
|
const effectiveScope = scopeWithRestoredJiraContext(currentScope, directState);
|
|
346
419
|
const baseConfig = buildInteractiveBaseConfig(flowEntry.id, effectiveScope);
|
|
347
420
|
const config = buildRuntimeConfig(baseConfig, effectiveScope);
|
|
348
|
-
validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.executionRouting);
|
|
421
|
+
await validateDeclarativeFlowResumeState(flowEntry, config, directState, directState.executionRouting);
|
|
349
422
|
return {
|
|
350
|
-
|
|
351
|
-
hasExistingState: true,
|
|
423
|
+
...availability,
|
|
352
424
|
details: buildFlowResumeDetails(directState),
|
|
353
425
|
};
|
|
354
426
|
}
|
|
355
427
|
catch (error) {
|
|
356
428
|
return {
|
|
357
|
-
|
|
358
|
-
|
|
429
|
+
...availability,
|
|
430
|
+
resume: {
|
|
431
|
+
available: false,
|
|
432
|
+
reason: error.message,
|
|
433
|
+
},
|
|
359
434
|
details: `Interrupted run found, but resume is unavailable.\n${error.message}`,
|
|
360
435
|
};
|
|
361
436
|
}
|
|
362
437
|
}
|
|
438
|
+
if (directState && availability.continue.available) {
|
|
439
|
+
return {
|
|
440
|
+
...availability,
|
|
441
|
+
details: buildFlowContinueDetails(directState),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
363
444
|
return {
|
|
364
|
-
|
|
365
|
-
hasExistingState: Boolean(directState),
|
|
445
|
+
...availability,
|
|
366
446
|
};
|
|
367
447
|
}
|
|
368
|
-
function printAutoPhasesHelp() {
|
|
369
|
-
const phaseLines = ["Available auto-golang phases:", "", ...autoPhaseIds()];
|
|
448
|
+
async function printAutoPhasesHelp() {
|
|
449
|
+
const phaseLines = ["Available auto-golang phases:", "", ...(await autoPhaseIds())];
|
|
370
450
|
phaseLines.push("", "You can resume auto-golang from a phase with:", "agentweaver auto-golang --from <phase> <jira>", "or in interactive mode:", "/auto-golang --from <phase>");
|
|
371
451
|
printPanel("Auto-Golang Phases", phaseLines.join("\n"), "magenta");
|
|
372
452
|
}
|
|
373
|
-
function autoCommonPhaseIds() {
|
|
374
|
-
return loadDeclarativeFlow({ source: "built-in", fileName
|
|
453
|
+
async function autoCommonPhaseIds(fileName = "auto-common.json") {
|
|
454
|
+
return (await loadDeclarativeFlow({ source: "built-in", fileName })).phases.map((phase) => phase.id);
|
|
375
455
|
}
|
|
376
|
-
function printAutoCommonPhasesHelp() {
|
|
377
|
-
const phaseLines = [
|
|
378
|
-
phaseLines.push("",
|
|
379
|
-
printPanel("Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
456
|
+
async function printAutoCommonPhasesHelp(command = "auto-common", fileName = "auto-common.json") {
|
|
457
|
+
const phaseLines = [`Available ${command} phases:`, "", ...(await autoCommonPhaseIds(fileName))];
|
|
458
|
+
phaseLines.push("", `You can run ${command} with:`, `agentweaver ${command} <jira>`);
|
|
459
|
+
printPanel(command === "auto-common-guided" ? "Auto-Common Guided Phases" : "Auto-Common Phases", phaseLines.join("\n"), "magenta");
|
|
380
460
|
}
|
|
381
|
-
function autoSimplePhaseIds() {
|
|
382
|
-
return loadDeclarativeFlow({ source: "built-in", fileName: "auto-simple.json" }).phases.map((phase) => phase.id);
|
|
461
|
+
async function autoSimplePhaseIds() {
|
|
462
|
+
return (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-simple.json" })).phases.map((phase) => phase.id);
|
|
383
463
|
}
|
|
384
|
-
function printAutoSimplePhasesHelp() {
|
|
385
|
-
const phaseLines = ["Available auto-simple phases:", "", ...autoSimplePhaseIds()];
|
|
464
|
+
async function printAutoSimplePhasesHelp() {
|
|
465
|
+
const phaseLines = ["Available auto-simple phases:", "", ...(await autoSimplePhaseIds())];
|
|
386
466
|
phaseLines.push("", "You can run auto-simple with:", "agentweaver auto-simple <jira>");
|
|
387
467
|
printPanel("Auto-Simple Phases", phaseLines.join("\n"), "magenta");
|
|
388
468
|
}
|
|
@@ -400,11 +480,12 @@ function buildBaseConfig(command, options = {}) {
|
|
|
400
480
|
reviewFixPoints: options.reviewFixPoints ?? null,
|
|
401
481
|
reviewBlockingSeverities: options.reviewBlockingSeverities ?? resolveReviewBlockingSeveritiesFromEnv(),
|
|
402
482
|
extraPrompt: options.extraPrompt ?? null,
|
|
403
|
-
autoFromPhase: options.autoFromPhase
|
|
483
|
+
autoFromPhase: options.autoFromPhase ?? null,
|
|
404
484
|
mdLang: options.mdLang ?? null,
|
|
405
485
|
dryRun: options.dryRun ?? false,
|
|
406
486
|
verbose: options.verbose ?? false,
|
|
407
487
|
...(options.doctorArgs !== undefined ? { doctorArgs: options.doctorArgs } : {}),
|
|
488
|
+
...(options.acceptPlaybookDraft !== undefined ? { acceptPlaybookDraft: options.acceptPlaybookDraft } : {}),
|
|
408
489
|
};
|
|
409
490
|
}
|
|
410
491
|
function commandRequiresTask(command) {
|
|
@@ -414,6 +495,7 @@ function commandRequiresTask(command) {
|
|
|
414
495
|
command === "design-review" ||
|
|
415
496
|
command === "mr-description" ||
|
|
416
497
|
command === "auto-golang" ||
|
|
498
|
+
command === "auto-common-guided" ||
|
|
417
499
|
command === "auto-common" ||
|
|
418
500
|
command === "auto-simple" ||
|
|
419
501
|
command === "auto-status" ||
|
|
@@ -425,6 +507,7 @@ function commandSupportsProjectScope(command) {
|
|
|
425
507
|
command === "gitlab-diff-review" ||
|
|
426
508
|
command === "gitlab-review" ||
|
|
427
509
|
command === "instant-task" ||
|
|
510
|
+
command === "playbook-init" ||
|
|
428
511
|
command === "task-describe" ||
|
|
429
512
|
command === "implement" ||
|
|
430
513
|
command === "review" ||
|
|
@@ -498,29 +581,42 @@ function routingForPrerequisites(launchProfile, executionRouting) {
|
|
|
498
581
|
function flowSpecFileForPrerequisiteChecks(command) {
|
|
499
582
|
return isBuiltInCommandFlowId(command) ? builtInCommandFlowFile(command) : null;
|
|
500
583
|
}
|
|
501
|
-
function commandRoutingGroupsForPrerequisiteChecks(command, cwd) {
|
|
584
|
+
async function commandRoutingGroupsForPrerequisiteChecks(command, cwd) {
|
|
502
585
|
const fileName = flowSpecFileForPrerequisiteChecks(command);
|
|
503
586
|
if (!fileName) {
|
|
504
587
|
return [];
|
|
505
588
|
}
|
|
506
|
-
return collectFlowRoutingGroups(loadDeclarativeFlow({ source: "built-in", fileName }), cwd);
|
|
589
|
+
return collectFlowRoutingGroups(await loadDeclarativeFlow({ source: "built-in", fileName }), cwd);
|
|
507
590
|
}
|
|
508
|
-
function resolveExecutorPrerequisite(executor) {
|
|
591
|
+
function resolveExecutorPrerequisite(executor, registryContext) {
|
|
509
592
|
if (executor === "codex") {
|
|
510
593
|
resolveCmd("codex", "CODEX_BIN");
|
|
511
594
|
return;
|
|
512
595
|
}
|
|
513
|
-
|
|
596
|
+
if (executor === "opencode") {
|
|
597
|
+
resolveCmd("opencode", "OPENCODE_BIN");
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
const definition = registryContext.executors.get(executor);
|
|
601
|
+
const config = definition.defaultConfig;
|
|
602
|
+
if (config
|
|
603
|
+
&& typeof config === "object"
|
|
604
|
+
&& !Array.isArray(config)
|
|
605
|
+
&& typeof config.defaultCommand === "string"
|
|
606
|
+
&& typeof config.commandEnvVar === "string") {
|
|
607
|
+
resolveCmd(config.defaultCommand, config.commandEnvVar);
|
|
608
|
+
}
|
|
514
609
|
}
|
|
515
|
-
function checkPrerequisites(config, launchProfile, executionRouting) {
|
|
610
|
+
async function checkPrerequisites(config, launchProfile, executionRouting) {
|
|
611
|
+
const registryContext = await createPipelineRegistryContext(process.cwd());
|
|
516
612
|
const routing = routingForPrerequisites(launchProfile, executionRouting);
|
|
517
|
-
const groups = commandRoutingGroupsForPrerequisiteChecks(config.command, process.cwd());
|
|
613
|
+
const groups = await commandRoutingGroupsForPrerequisiteChecks(config.command, process.cwd());
|
|
518
614
|
for (const executor of executorsForRoutingGroups(routing, groups)) {
|
|
519
|
-
resolveExecutorPrerequisite(executor);
|
|
615
|
+
resolveExecutorPrerequisite(executor, registryContext);
|
|
520
616
|
}
|
|
521
617
|
}
|
|
522
|
-
function checkAutoPrerequisites(config, launchProfile, executionRouting) {
|
|
523
|
-
checkPrerequisites(config, launchProfile, executionRouting);
|
|
618
|
+
async function checkAutoPrerequisites(config, launchProfile, executionRouting) {
|
|
619
|
+
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
524
620
|
}
|
|
525
621
|
function autoFlowParams(config, forceRefreshSummary = false) {
|
|
526
622
|
return {
|
|
@@ -536,6 +632,8 @@ function autoFlowParams(config, forceRefreshSummary = false) {
|
|
|
536
632
|
reviewBlockingSeverities: config.reviewBlockingSeverities,
|
|
537
633
|
forceRefresh: forceRefreshSummary,
|
|
538
634
|
mdLang: config.mdLang,
|
|
635
|
+
acceptPlaybookDraft: config.command === "auto-common-guided" ? config.acceptPlaybookDraft === true : false,
|
|
636
|
+
launchMode: config.command === "auto-common-guided" ? config.autoFromPhase ?? "restart" : undefined,
|
|
539
637
|
runGoTestsScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_tests.py"),
|
|
540
638
|
runGoLinterScript: path.join(agentweaverHome(PACKAGE_ROOT), "run_go_linter.py"),
|
|
541
639
|
runGoTestsIteration: nextArtifactIteration(config.taskKey, "run-go-tests-result", "json"),
|
|
@@ -616,7 +714,7 @@ function interactiveFlowDefinition(entry) {
|
|
|
616
714
|
description: flow.description ?? "No description available for this flow.",
|
|
617
715
|
source: entry.source,
|
|
618
716
|
treePath: [...entry.treePath],
|
|
619
|
-
...(entry.source
|
|
717
|
+
...(entry.source !== "built-in" ? { sourcePath: entry.absolutePath } : {}),
|
|
620
718
|
phases: flow.phases.map((phase) => ({
|
|
621
719
|
id: phase.id,
|
|
622
720
|
repeatVars: Object.fromEntries(Object.entries(phase.repeatVars).map(([key, value]) => [key, value])),
|
|
@@ -666,7 +764,7 @@ function findCurrentFlowExecutionStep(state) {
|
|
|
666
764
|
return null;
|
|
667
765
|
}
|
|
668
766
|
async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, overrides = {}, requestUserInput = requestUserInputInTerminal, setSummary, launchMode = "restart", runtime = runtimeServices) {
|
|
669
|
-
const context = createPipelineContext({
|
|
767
|
+
const context = await createPipelineContext({
|
|
670
768
|
issueKey: config.taskKey,
|
|
671
769
|
jiraRef: config.jiraRef,
|
|
672
770
|
dryRun: config.dryRun,
|
|
@@ -677,7 +775,7 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
677
775
|
requestUserInput,
|
|
678
776
|
...(overrides.executionRouting ? { executionRouting: overrides.executionRouting } : {}),
|
|
679
777
|
});
|
|
680
|
-
const flow = loadDeclarativeFlow(flowRef);
|
|
778
|
+
const flow = await loadDeclarativeFlow(flowRef);
|
|
681
779
|
const initialExecutionState = {
|
|
682
780
|
flowKind: flow.kind,
|
|
683
781
|
flowVersion: flow.version,
|
|
@@ -685,9 +783,12 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
685
783
|
terminationOutcome: "success",
|
|
686
784
|
phases: [],
|
|
687
785
|
};
|
|
688
|
-
|
|
786
|
+
const existingStateForRestart = launchMode === "restart" ? loadFlowRunState(config.scope.scopeKey, flowId) : null;
|
|
787
|
+
let persistedState = launchMode === "resume" || launchMode === "continue"
|
|
788
|
+
? loadFlowRunState(config.scope.scopeKey, flowId)
|
|
789
|
+
: null;
|
|
689
790
|
if (persistedState && launchMode === "resume") {
|
|
690
|
-
validateDeclarativeFlowResumeState({
|
|
791
|
+
await validateDeclarativeFlowResumeState({
|
|
691
792
|
id: flowId,
|
|
692
793
|
source: flow.source,
|
|
693
794
|
fileName: flow.fileName,
|
|
@@ -700,7 +801,13 @@ async function runDeclarativeFlowByRef(flowId, flowRef, config, flowParams, over
|
|
|
700
801
|
} }) : undefined), runtime);
|
|
701
802
|
persistedState = prepareFlowStateForResume(persistedState);
|
|
702
803
|
}
|
|
804
|
+
else if (persistedState && launchMode === "continue") {
|
|
805
|
+
persistedState = prepareFlowStateForContinue(persistedState, flow.phases);
|
|
806
|
+
}
|
|
703
807
|
else if (launchMode === "restart") {
|
|
808
|
+
if (existingStateForRestart) {
|
|
809
|
+
archiveActiveAttempt(config.scope.scopeKey);
|
|
810
|
+
}
|
|
704
811
|
resetFlowRunState(config.scope.scopeKey, flowId);
|
|
705
812
|
}
|
|
706
813
|
const executionState = persistedState?.executionState ?? initialExecutionState;
|
|
@@ -772,7 +879,7 @@ async function runDeclarativeFlowBySpecFile(fileName, config, flowParams, overri
|
|
|
772
879
|
...defaultDeclarativeFlowParams(config, false, overrides),
|
|
773
880
|
...flowParams,
|
|
774
881
|
};
|
|
775
|
-
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, withCanonicalReviewLoopParams(loadDeclarativeFlow({ source: "built-in", fileName }).kind, mergedFlowParams), overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
882
|
+
await runDeclarativeFlowByRef(config.command, { source: "built-in", fileName }, config, withCanonicalReviewLoopParams((await loadDeclarativeFlow({ source: "built-in", fileName })).kind, mergedFlowParams), overrides, requestUserInput, setSummary, launchMode, runtime);
|
|
776
883
|
}
|
|
777
884
|
function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overrides = {}) {
|
|
778
885
|
const iteration = nextReviewIterationForTask(config.taskKey);
|
|
@@ -804,10 +911,15 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overr
|
|
|
804
911
|
mdLang: config.mdLang,
|
|
805
912
|
llmExecutor: launchProfile.executor,
|
|
806
913
|
llmModel: launchProfile.model,
|
|
914
|
+
projectGuidanceFile: "not provided",
|
|
915
|
+
projectGuidanceJsonFile: "not provided",
|
|
916
|
+
repairProjectGuidanceFile: "not provided",
|
|
917
|
+
repairProjectGuidanceJsonFile: "not provided",
|
|
807
918
|
launchProfile,
|
|
808
919
|
executionRouting,
|
|
809
920
|
iteration,
|
|
810
921
|
baseIteration: iteration,
|
|
922
|
+
designReviewBaseIteration: nextDesignReviewIterationForTask(config.taskKey),
|
|
811
923
|
latestIteration,
|
|
812
924
|
taskContextIteration: latestTaskContext ?? nextArtifactIteration(config.taskKey, "task-context", "json"),
|
|
813
925
|
taskSummaryIteration: nextArtifactIteration(config.taskKey, "task"),
|
|
@@ -818,6 +930,60 @@ function defaultDeclarativeFlowParams(config, forceRefreshSummary = false, overr
|
|
|
818
930
|
forceRefresh: forceRefreshSummary,
|
|
819
931
|
};
|
|
820
932
|
}
|
|
933
|
+
function countAvailableNonRestartActions(availability) {
|
|
934
|
+
return Number(availability.resume.available) + Number(availability.continue.available);
|
|
935
|
+
}
|
|
936
|
+
async function chooseLaunchMode(flowId, scopeKey, explicitLaunchMode, requestUserInput) {
|
|
937
|
+
const state = loadFlowRunState(scopeKey, flowId);
|
|
938
|
+
const availability = classifyFlowLaunchAvailability(state);
|
|
939
|
+
if (explicitLaunchMode) {
|
|
940
|
+
const selectedAvailability = availability[explicitLaunchMode];
|
|
941
|
+
if (!selectedAvailability.available) {
|
|
942
|
+
throw new TaskRunnerError(`${explicitLaunchMode.charAt(0).toUpperCase()}${explicitLaunchMode.slice(1)} is not available for '${flowId}'. ${selectedAvailability.reason}`);
|
|
943
|
+
}
|
|
944
|
+
return explicitLaunchMode;
|
|
945
|
+
}
|
|
946
|
+
if (!availability.hasExistingState) {
|
|
947
|
+
return "restart";
|
|
948
|
+
}
|
|
949
|
+
const availableNonRestart = countAvailableNonRestartActions(availability);
|
|
950
|
+
if (availableNonRestart === 0) {
|
|
951
|
+
return "restart";
|
|
952
|
+
}
|
|
953
|
+
const interactive = requestUserInput !== requestUserInputInTerminal || (process.stdin.isTTY && process.stdout.isTTY);
|
|
954
|
+
if (!interactive) {
|
|
955
|
+
throw new TaskRunnerError(`Multiple actions are valid for '${flowId}'. Re-run with one of: --resume, --continue, --restart.`);
|
|
956
|
+
}
|
|
957
|
+
const result = await requestUserInput({
|
|
958
|
+
formId: `launch-mode-${flowId}`,
|
|
959
|
+
title: "Launch Action",
|
|
960
|
+
description: `Select how to start '${flowId}'.`,
|
|
961
|
+
submitLabel: "Start",
|
|
962
|
+
fields: [
|
|
963
|
+
{
|
|
964
|
+
id: "launchMode",
|
|
965
|
+
type: "single-select",
|
|
966
|
+
label: "Action",
|
|
967
|
+
required: true,
|
|
968
|
+
default: availability.continue.available ? "continue" : availability.resume.available ? "resume" : "restart",
|
|
969
|
+
options: [
|
|
970
|
+
...(availability.resume.available
|
|
971
|
+
? [{ value: "resume", label: "Resume", description: availability.resume.reason }]
|
|
972
|
+
: []),
|
|
973
|
+
...(availability.continue.available
|
|
974
|
+
? [{ value: "continue", label: "Continue", description: availability.continue.reason }]
|
|
975
|
+
: []),
|
|
976
|
+
{ value: "restart", label: "Restart", description: availability.restart.reason },
|
|
977
|
+
],
|
|
978
|
+
},
|
|
979
|
+
],
|
|
980
|
+
});
|
|
981
|
+
const selected = result.values.launchMode;
|
|
982
|
+
if (selected !== "resume" && selected !== "continue" && selected !== "restart") {
|
|
983
|
+
throw new TaskRunnerError(`Invalid launch action selected for '${flowId}'.`);
|
|
984
|
+
}
|
|
985
|
+
return selected;
|
|
986
|
+
}
|
|
821
987
|
const TASK_SCOPE_PARAM_REFS = new Set(["params.jiraApiUrl", "params.jiraBrowseUrl", "params.jiraTaskFile"]);
|
|
822
988
|
function valueReferencesTaskScopeParams(value) {
|
|
823
989
|
if (Array.isArray(value)) {
|
|
@@ -840,7 +1006,7 @@ function flowRequiresTaskScope(entry) {
|
|
|
840
1006
|
return valueReferencesTaskScopeParams(entry.flow.phases);
|
|
841
1007
|
}
|
|
842
1008
|
async function summarizeBuildFailure(output) {
|
|
843
|
-
return summarizeBuildFailureViaPipeline(createPipelineContext({
|
|
1009
|
+
return summarizeBuildFailureViaPipeline(await createPipelineContext({
|
|
844
1010
|
issueKey: "build-failure-summary",
|
|
845
1011
|
jiraRef: "build-failure-summary",
|
|
846
1012
|
dryRun: false,
|
|
@@ -855,7 +1021,7 @@ function requireJiraConfig(config) {
|
|
|
855
1021
|
throw new TaskRunnerError(`Command '${config.command}' requires Jira context in the current project scope.`);
|
|
856
1022
|
}
|
|
857
1023
|
}
|
|
858
|
-
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false,
|
|
1024
|
+
async function executeCommand(baseConfig, runFollowupVerify = true, requestUserInput = requestUserInputInTerminal, resolvedScope, setSummary, forceRefreshSummary = false, explicitLaunchMode, launchProfile, executionRouting, selectedRoutingPreset, runtime = runtimeServices) {
|
|
859
1025
|
if (baseConfig.command === "doctor") {
|
|
860
1026
|
const exitCode = await runDoctorCommand(baseConfig.doctorArgs ?? []);
|
|
861
1027
|
return exitCode === 0;
|
|
@@ -870,8 +1036,11 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
870
1036
|
: launchProfile
|
|
871
1037
|
? { launchProfile }
|
|
872
1038
|
: {};
|
|
1039
|
+
const launchMode = config.command === "auto-status" || config.command === "auto-reset"
|
|
1040
|
+
? "restart"
|
|
1041
|
+
: await chooseLaunchMode(config.command, config.scope.scopeKey, explicitLaunchMode, requestUserInput);
|
|
873
1042
|
if (config.command === "instant-task") {
|
|
874
|
-
checkPrerequisites(config, launchProfile, executionRouting);
|
|
1043
|
+
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
875
1044
|
const hasPersistedInstantTaskState = loadFlowRunState(config.scope.scopeKey, "instant-task") !== null;
|
|
876
1045
|
const repromptInstantTaskInput = launchMode === "restart"
|
|
877
1046
|
&& hasPersistedInstantTaskState
|
|
@@ -900,7 +1069,8 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
900
1069
|
let effectiveLaunchProfile = launchProfile;
|
|
901
1070
|
let effectiveExecutionRouting = executionRouting;
|
|
902
1071
|
if (config.autoFromPhase) {
|
|
903
|
-
|
|
1072
|
+
config.autoFromPhase = await validateAutoPhaseId(config.autoFromPhase);
|
|
1073
|
+
const flow = await loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" });
|
|
904
1074
|
const persistedState = loadFlowRunState(config.scope.scopeKey, "auto-golang");
|
|
905
1075
|
if (!persistedState) {
|
|
906
1076
|
throw new TaskRunnerError(`Cannot restart auto-golang from phase '${config.autoFromPhase}' because persisted flow state was not found.`);
|
|
@@ -912,7 +1082,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
912
1082
|
effectiveExecutionRouting ??= persistedState.executionRouting;
|
|
913
1083
|
printPanel("Auto-Golang Resume", `Auto-golang pipeline will continue from phase: ${config.autoFromPhase}`, "yellow");
|
|
914
1084
|
}
|
|
915
|
-
checkAutoPrerequisites(config, effectiveLaunchProfile, effectiveExecutionRouting);
|
|
1085
|
+
await checkAutoPrerequisites(config, effectiveLaunchProfile, effectiveExecutionRouting);
|
|
916
1086
|
await runDeclarativeFlowBySpecFile("auto-golang.json", config, autoFlowParams(config, forceRefreshSummary), effectiveExecutionRouting
|
|
917
1087
|
? {
|
|
918
1088
|
launchProfile: effectiveExecutionRouting.defaultRoute,
|
|
@@ -924,18 +1094,18 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
924
1094
|
: {}, requestUserInput, setSummary, effectiveLaunchMode, runtime);
|
|
925
1095
|
return false;
|
|
926
1096
|
}
|
|
927
|
-
if (config.command === "auto-common") {
|
|
1097
|
+
if (config.command === "auto-common" || config.command === "auto-common-guided") {
|
|
928
1098
|
requireJiraConfig(config);
|
|
929
|
-
checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
1099
|
+
await checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
930
1100
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
931
1101
|
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
932
1102
|
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
933
|
-
await runDeclarativeFlowBySpecFile("auto-common.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1103
|
+
await runDeclarativeFlowBySpecFile(config.command === "auto-common-guided" ? "auto-common-guided.json" : "auto-common.json", config, autoFlowParams(config, forceRefreshSummary), flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
934
1104
|
return false;
|
|
935
1105
|
}
|
|
936
1106
|
if (config.command === "auto-simple") {
|
|
937
1107
|
requireJiraConfig(config);
|
|
938
|
-
checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
1108
|
+
await checkAutoPrerequisites(config, launchProfile, executionRouting);
|
|
939
1109
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl;
|
|
940
1110
|
process.env.JIRA_API_URL = config.jiraApiUrl;
|
|
941
1111
|
process.env.JIRA_TASK_FILE = config.jiraTaskFile;
|
|
@@ -949,7 +1119,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
949
1119
|
return false;
|
|
950
1120
|
}
|
|
951
1121
|
const currentStep = findCurrentFlowExecutionStep(state) ?? state.currentStep ?? "-";
|
|
952
|
-
const phaseOrder = loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" }).phases;
|
|
1122
|
+
const phaseOrder = (await loadDeclarativeFlow({ source: "built-in", fileName: "auto-golang.json" })).phases;
|
|
953
1123
|
const lines = [
|
|
954
1124
|
`Issue: ${config.taskKey}`,
|
|
955
1125
|
`Status: ${state.status}`,
|
|
@@ -986,7 +1156,7 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
986
1156
|
printPanel("Auto-Golang Reset", removed ? `State file ${flowStateFile(config.scope.scopeKey, "auto-golang")} removed.` : "No flow state file found.", "yellow");
|
|
987
1157
|
return false;
|
|
988
1158
|
}
|
|
989
|
-
checkPrerequisites(config, launchProfile, executionRouting);
|
|
1159
|
+
await checkPrerequisites(config, launchProfile, executionRouting);
|
|
990
1160
|
if (config.jiraBrowseUrl && config.jiraApiUrl && config.jiraTaskFile) {
|
|
991
1161
|
process.env.JIRA_BROWSE_URL = config.jiraBrowseUrl ?? "";
|
|
992
1162
|
process.env.JIRA_API_URL = config.jiraApiUrl ?? "";
|
|
@@ -1031,6 +1201,14 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1031
1201
|
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1032
1202
|
return false;
|
|
1033
1203
|
}
|
|
1204
|
+
if (config.command === "playbook-init") {
|
|
1205
|
+
await runDeclarativeFlowBySpecFile("playbook-init.json", config, {
|
|
1206
|
+
taskKey: config.taskKey,
|
|
1207
|
+
extraPrompt: config.extraPrompt,
|
|
1208
|
+
acceptPlaybookDraft: config.acceptPlaybookDraft === true,
|
|
1209
|
+
}, flowOverrides, requestUserInput, setSummary, launchMode, runtime);
|
|
1210
|
+
return false;
|
|
1211
|
+
}
|
|
1034
1212
|
if (config.command === "bug-analyze") {
|
|
1035
1213
|
requireJiraConfig(config);
|
|
1036
1214
|
if (config.verbose) {
|
|
@@ -1290,22 +1468,22 @@ async function executeCommand(baseConfig, runFollowupVerify = true, requestUserI
|
|
|
1290
1468
|
}
|
|
1291
1469
|
throw new TaskRunnerError(`Unsupported command: ${config.command}`);
|
|
1292
1470
|
}
|
|
1293
|
-
function parseCliArgs(argv) {
|
|
1471
|
+
async function parseCliArgs(argv) {
|
|
1294
1472
|
if (argv.includes("--version") || argv.includes("-v")) {
|
|
1295
|
-
|
|
1473
|
+
writeStdoutSync(`${packageVersion()}\n`);
|
|
1296
1474
|
process.exit(0);
|
|
1297
1475
|
}
|
|
1298
1476
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
1299
|
-
|
|
1477
|
+
writeStdoutSync(`${usage()}\n`);
|
|
1300
1478
|
process.exit(0);
|
|
1301
1479
|
}
|
|
1302
1480
|
if (argv.length === 0) {
|
|
1303
|
-
|
|
1481
|
+
writeStderrSync(`${usage()}\n`);
|
|
1304
1482
|
process.exit(1);
|
|
1305
1483
|
}
|
|
1306
1484
|
const command = argv[0];
|
|
1307
1485
|
if (!COMMANDS.includes(command)) {
|
|
1308
|
-
|
|
1486
|
+
writeStderrSync(`${usage()}\n`);
|
|
1309
1487
|
process.exit(1);
|
|
1310
1488
|
}
|
|
1311
1489
|
let dry = false;
|
|
@@ -1317,6 +1495,10 @@ function parseCliArgs(argv) {
|
|
|
1317
1495
|
let helpPhases = false;
|
|
1318
1496
|
let jiraRef;
|
|
1319
1497
|
let mdLang;
|
|
1498
|
+
let launchMode;
|
|
1499
|
+
let acceptPlaybookDraft = false;
|
|
1500
|
+
let webNoOpen = process.env.AGENTWEAVER_WEB_NO_OPEN === "1";
|
|
1501
|
+
let webHost;
|
|
1320
1502
|
const doctorArgs = [];
|
|
1321
1503
|
for (let index = 1; index < argv.length; index += 1) {
|
|
1322
1504
|
const token = argv[index] ?? "";
|
|
@@ -1332,6 +1514,61 @@ function parseCliArgs(argv) {
|
|
|
1332
1514
|
helpPhases = true;
|
|
1333
1515
|
continue;
|
|
1334
1516
|
}
|
|
1517
|
+
if (token === "--accept-playbook-draft") {
|
|
1518
|
+
acceptPlaybookDraft = true;
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
if (token === "--no-open") {
|
|
1522
|
+
if (command !== "web") {
|
|
1523
|
+
writeStderrSync("Error: --no-open is only supported after the web command.\n");
|
|
1524
|
+
process.exit(1);
|
|
1525
|
+
}
|
|
1526
|
+
webNoOpen = true;
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
if (token === "--listen-all") {
|
|
1530
|
+
if (command !== "web") {
|
|
1531
|
+
writeStderrSync("Error: --listen-all is only supported after the web command.\n");
|
|
1532
|
+
process.exit(1);
|
|
1533
|
+
}
|
|
1534
|
+
webHost = "0.0.0.0";
|
|
1535
|
+
continue;
|
|
1536
|
+
}
|
|
1537
|
+
if (token === "--host") {
|
|
1538
|
+
if (command !== "web") {
|
|
1539
|
+
writeStderrSync("Error: --host is only supported after the web command.\n");
|
|
1540
|
+
process.exit(1);
|
|
1541
|
+
}
|
|
1542
|
+
const hostValue = argv[index + 1]?.trim();
|
|
1543
|
+
if (!hostValue || hostValue.startsWith("-")) {
|
|
1544
|
+
writeStderrSync("Error: --host requires a host value.\n");
|
|
1545
|
+
process.exit(1);
|
|
1546
|
+
}
|
|
1547
|
+
webHost = hostValue;
|
|
1548
|
+
index += 1;
|
|
1549
|
+
continue;
|
|
1550
|
+
}
|
|
1551
|
+
if (token.startsWith("--host=")) {
|
|
1552
|
+
if (command !== "web") {
|
|
1553
|
+
writeStderrSync("Error: --host is only supported after the web command.\n");
|
|
1554
|
+
process.exit(1);
|
|
1555
|
+
}
|
|
1556
|
+
const hostValue = token.slice("--host=".length).trim();
|
|
1557
|
+
if (!hostValue) {
|
|
1558
|
+
writeStderrSync("Error: --host requires a host value.\n");
|
|
1559
|
+
process.exit(1);
|
|
1560
|
+
}
|
|
1561
|
+
webHost = hostValue;
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1564
|
+
if (token === "--resume" || token === "--continue" || token === "--restart") {
|
|
1565
|
+
if (launchMode) {
|
|
1566
|
+
writeStderrSync("Error: --resume, --continue, and --restart are mutually exclusive.\n");
|
|
1567
|
+
process.exit(1);
|
|
1568
|
+
}
|
|
1569
|
+
launchMode = token.slice(2);
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1335
1572
|
if (token === "--prompt") {
|
|
1336
1573
|
prompt = argv[index + 1];
|
|
1337
1574
|
index += 1;
|
|
@@ -1362,7 +1599,7 @@ function parseCliArgs(argv) {
|
|
|
1362
1599
|
mdLang = langValue;
|
|
1363
1600
|
}
|
|
1364
1601
|
else {
|
|
1365
|
-
|
|
1602
|
+
writeStderrSync("Error: --md-lang accepts only 'en' or 'ru' as values.\n");
|
|
1366
1603
|
process.exit(1);
|
|
1367
1604
|
}
|
|
1368
1605
|
index += 1;
|
|
@@ -1374,7 +1611,7 @@ function parseCliArgs(argv) {
|
|
|
1374
1611
|
mdLang = langValue;
|
|
1375
1612
|
}
|
|
1376
1613
|
else {
|
|
1377
|
-
|
|
1614
|
+
writeStderrSync("Error: --md-lang accepts only 'en' or 'ru' as values.\n");
|
|
1378
1615
|
process.exit(1);
|
|
1379
1616
|
}
|
|
1380
1617
|
continue;
|
|
@@ -1387,15 +1624,15 @@ function parseCliArgs(argv) {
|
|
|
1387
1624
|
}
|
|
1388
1625
|
}
|
|
1389
1626
|
if (command === "auto-golang" && helpPhases) {
|
|
1390
|
-
printAutoPhasesHelp();
|
|
1627
|
+
await printAutoPhasesHelp();
|
|
1391
1628
|
process.exit(0);
|
|
1392
1629
|
}
|
|
1393
|
-
if (command === "auto-common" && helpPhases) {
|
|
1394
|
-
printAutoCommonPhasesHelp();
|
|
1630
|
+
if ((command === "auto-common" || command === "auto-common-guided") && helpPhases) {
|
|
1631
|
+
await printAutoCommonPhasesHelp(command, command === "auto-common-guided" ? "auto-common-guided.json" : "auto-common.json");
|
|
1395
1632
|
process.exit(0);
|
|
1396
1633
|
}
|
|
1397
1634
|
if (command === "auto-simple" && helpPhases) {
|
|
1398
|
-
printAutoSimplePhasesHelp();
|
|
1635
|
+
await printAutoSimplePhasesHelp();
|
|
1399
1636
|
process.exit(0);
|
|
1400
1637
|
}
|
|
1401
1638
|
return {
|
|
@@ -1410,6 +1647,10 @@ function parseCliArgs(argv) {
|
|
|
1410
1647
|
...(autoFromPhase !== undefined ? { autoFromPhase } : {}),
|
|
1411
1648
|
...(mdLang !== undefined ? { mdLang } : {}),
|
|
1412
1649
|
...(doctorArgs.length > 0 ? { doctorArgs } : {}),
|
|
1650
|
+
...(launchMode !== undefined ? { launchMode } : {}),
|
|
1651
|
+
...(acceptPlaybookDraft ? { acceptPlaybookDraft } : {}),
|
|
1652
|
+
...(command === "web" ? { webNoOpen } : {}),
|
|
1653
|
+
...(command === "web" && webHost !== undefined ? { webHost } : {}),
|
|
1413
1654
|
};
|
|
1414
1655
|
}
|
|
1415
1656
|
function buildConfigFromArgs(args) {
|
|
@@ -1423,32 +1664,75 @@ function buildConfigFromArgs(args) {
|
|
|
1423
1664
|
dryRun: args.dry,
|
|
1424
1665
|
verbose: args.verbose,
|
|
1425
1666
|
...(args.doctorArgs !== undefined ? { doctorArgs: args.doctorArgs } : {}),
|
|
1667
|
+
...(args.acceptPlaybookDraft !== undefined ? { acceptPlaybookDraft: args.acceptPlaybookDraft } : {}),
|
|
1426
1668
|
});
|
|
1427
1669
|
}
|
|
1428
|
-
async function
|
|
1670
|
+
async function runInteractiveWithSessionFactory(createSession, jiraRef, forceRefresh = false, scopeName, installSignalCleanup = false) {
|
|
1429
1671
|
let currentScope = resolveProjectScope(scopeName, jiraRef);
|
|
1430
|
-
const
|
|
1431
|
-
const flowCatalog = loadInteractiveFlowCatalog(process.cwd());
|
|
1672
|
+
const flowCatalog = await loadInteractiveFlowCatalog(process.cwd());
|
|
1432
1673
|
let activeAbortController = null;
|
|
1433
1674
|
let activeFlowId = null;
|
|
1675
|
+
let pendingScopeSwitch = null;
|
|
1676
|
+
const autoScopeSwitchEnabled = !scopeName?.trim() && !jiraRef?.trim();
|
|
1677
|
+
let lastObservedGitScope = currentScope;
|
|
1678
|
+
let ui;
|
|
1434
1679
|
let exiting = false;
|
|
1435
|
-
const
|
|
1680
|
+
const applyScopeSwitch = (nextScope, reason) => {
|
|
1681
|
+
const previousScope = currentScope;
|
|
1682
|
+
currentScope = nextScope;
|
|
1683
|
+
ui.setScope(currentScope.scopeKey, currentScope.jiraIssueKey ?? null, currentScope.gitBranchName);
|
|
1684
|
+
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1685
|
+
ui.appendLog(`[scope] ${reason}: ${previousScope.scopeKey} -> ${currentScope.scopeKey}`);
|
|
1686
|
+
};
|
|
1687
|
+
const handleObservedScope = (observedScope, reason) => {
|
|
1688
|
+
if (observedScope.scopeKey === currentScope.scopeKey
|
|
1689
|
+
&& observedScope.gitBranchName === currentScope.gitBranchName) {
|
|
1690
|
+
pendingScopeSwitch = null;
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
if (activeAbortController) {
|
|
1694
|
+
if (!pendingScopeSwitch
|
|
1695
|
+
|| pendingScopeSwitch.scopeKey !== observedScope.scopeKey
|
|
1696
|
+
|| pendingScopeSwitch.gitBranchName !== observedScope.gitBranchName) {
|
|
1697
|
+
pendingScopeSwitch = observedScope;
|
|
1698
|
+
ui.appendLog(`[scope] ${reason}: switch to ${observedScope.scopeKey} pending until current flow finishes`);
|
|
1699
|
+
}
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
pendingScopeSwitch = null;
|
|
1703
|
+
applyScopeSwitch(observedScope, reason);
|
|
1704
|
+
};
|
|
1705
|
+
const refreshScopeFromGit = (reason) => {
|
|
1706
|
+
if (!autoScopeSwitchEnabled) {
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
const observedScope = resolveProjectScope(null, null);
|
|
1710
|
+
if (observedScope.scopeKey === lastObservedGitScope.scopeKey
|
|
1711
|
+
&& observedScope.gitBranchName === lastObservedGitScope.gitBranchName) {
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
lastObservedGitScope = observedScope;
|
|
1715
|
+
handleObservedScope(observedScope, reason);
|
|
1716
|
+
};
|
|
1717
|
+
ui = createSession({
|
|
1436
1718
|
scopeKey: currentScope.scopeKey,
|
|
1437
1719
|
jiraIssueKey: currentScope.jiraIssueKey ?? null,
|
|
1438
1720
|
summaryText: "",
|
|
1439
1721
|
cwd: process.cwd(),
|
|
1440
|
-
gitBranchName,
|
|
1722
|
+
gitBranchName: currentScope.gitBranchName,
|
|
1441
1723
|
version: packageVersion(),
|
|
1442
1724
|
flows: interactiveFlowDefinitions(flowCatalog),
|
|
1443
1725
|
getRunConfirmation: async (flowId) => {
|
|
1726
|
+
refreshScopeFromGit("git scope refresh before launch confirmation");
|
|
1444
1727
|
const flowEntry = findCatalogEntry(flowId, flowCatalog);
|
|
1445
1728
|
if (!flowEntry) {
|
|
1446
1729
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1447
1730
|
}
|
|
1448
|
-
const resumeLookup = lookupInteractiveFlowResume(flowEntry, currentScope);
|
|
1731
|
+
const resumeLookup = await lookupInteractiveFlowResume(flowEntry, currentScope);
|
|
1449
1732
|
return resumeLookup;
|
|
1450
1733
|
},
|
|
1451
1734
|
onRun: async (flowId, launchMode) => {
|
|
1735
|
+
refreshScopeFromGit("git scope refresh before flow launch");
|
|
1452
1736
|
const abortController = new AbortController();
|
|
1453
1737
|
activeAbortController = abortController;
|
|
1454
1738
|
activeFlowId = flowId;
|
|
@@ -1457,7 +1741,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1457
1741
|
if (!flowEntry) {
|
|
1458
1742
|
throw new TaskRunnerError(`Unknown flow: ${flowId}`);
|
|
1459
1743
|
}
|
|
1460
|
-
const routingGroups = flowRoutingGroups(flowEntry, process.cwd());
|
|
1744
|
+
const routingGroups = await flowRoutingGroups(flowEntry, process.cwd());
|
|
1461
1745
|
const resumeState = launchMode === "resume" ? loadFlowRunState(currentScope.scopeKey, flowId) : null;
|
|
1462
1746
|
if (resumeState) {
|
|
1463
1747
|
currentScope = scopeWithRestoredJiraContext(currentScope, resumeState);
|
|
@@ -1484,7 +1768,7 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1484
1768
|
const jiraContext = await requestJiraContext((form) => ui.requestUserInput(form));
|
|
1485
1769
|
currentScope = resolveProjectScope(null, jiraContext.jiraRef);
|
|
1486
1770
|
}
|
|
1487
|
-
ui.setScope(currentScope.scopeKey, currentScope.jiraIssueKey ?? null);
|
|
1771
|
+
ui.setScope(currentScope.scopeKey, currentScope.jiraIssueKey ?? null, currentScope.gitBranchName);
|
|
1488
1772
|
if (previousScopeKey !== currentScope.scopeKey || currentScope.jiraIssueKey) {
|
|
1489
1773
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1490
1774
|
}
|
|
@@ -1526,6 +1810,11 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1526
1810
|
if (activeAbortController === abortController) {
|
|
1527
1811
|
activeAbortController = null;
|
|
1528
1812
|
activeFlowId = null;
|
|
1813
|
+
if (pendingScopeSwitch && !exiting) {
|
|
1814
|
+
const nextScope = pendingScopeSwitch;
|
|
1815
|
+
pendingScopeSwitch = null;
|
|
1816
|
+
applyScopeSwitch(nextScope, "git scope refresh after flow completion");
|
|
1817
|
+
}
|
|
1529
1818
|
}
|
|
1530
1819
|
}
|
|
1531
1820
|
},
|
|
@@ -1537,6 +1826,10 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1537
1826
|
activeAbortController.abort();
|
|
1538
1827
|
},
|
|
1539
1828
|
onExit: () => {
|
|
1829
|
+
if (activeAbortController) {
|
|
1830
|
+
ui.interruptActiveForm();
|
|
1831
|
+
activeAbortController.abort();
|
|
1832
|
+
}
|
|
1540
1833
|
exiting = true;
|
|
1541
1834
|
},
|
|
1542
1835
|
});
|
|
@@ -1547,13 +1840,45 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1547
1840
|
ui.appendLog("[scope] project scope active; task summary will appear after a Jira-backed flow runs");
|
|
1548
1841
|
}
|
|
1549
1842
|
syncInteractiveTaskSummary(ui, currentScope, forceRefresh);
|
|
1843
|
+
const scopeWatchInterval = autoScopeSwitchEnabled
|
|
1844
|
+
? setInterval(() => {
|
|
1845
|
+
if (!exiting) {
|
|
1846
|
+
refreshScopeFromGit("git branch changed");
|
|
1847
|
+
}
|
|
1848
|
+
}, INTERACTIVE_SCOPE_WATCH_INTERVAL_MS)
|
|
1849
|
+
: null;
|
|
1550
1850
|
return await new Promise((resolve, reject) => {
|
|
1851
|
+
let cleanupStarted = false;
|
|
1852
|
+
const requestExit = () => {
|
|
1853
|
+
if (activeAbortController) {
|
|
1854
|
+
ui.interruptActiveForm();
|
|
1855
|
+
activeAbortController.abort();
|
|
1856
|
+
}
|
|
1857
|
+
exiting = true;
|
|
1858
|
+
};
|
|
1859
|
+
const onSigint = () => requestExit();
|
|
1860
|
+
const onSigterm = () => requestExit();
|
|
1861
|
+
if (installSignalCleanup) {
|
|
1862
|
+
process.once("SIGINT", onSigint);
|
|
1863
|
+
process.once("SIGTERM", onSigterm);
|
|
1864
|
+
}
|
|
1551
1865
|
const interval = setInterval(() => {
|
|
1552
1866
|
if (!exiting) {
|
|
1553
1867
|
return;
|
|
1554
1868
|
}
|
|
1555
1869
|
clearInterval(interval);
|
|
1556
1870
|
try {
|
|
1871
|
+
if (cleanupStarted) {
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
cleanupStarted = true;
|
|
1875
|
+
if (scopeWatchInterval) {
|
|
1876
|
+
clearInterval(scopeWatchInterval);
|
|
1877
|
+
}
|
|
1878
|
+
if (installSignalCleanup) {
|
|
1879
|
+
process.off("SIGINT", onSigint);
|
|
1880
|
+
process.off("SIGTERM", onSigterm);
|
|
1881
|
+
}
|
|
1557
1882
|
ui.destroy();
|
|
1558
1883
|
bye();
|
|
1559
1884
|
resolve(0);
|
|
@@ -1564,6 +1889,12 @@ async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
|
1564
1889
|
}, 100);
|
|
1565
1890
|
});
|
|
1566
1891
|
}
|
|
1892
|
+
async function runInteractive(jiraRef, forceRefresh = false, scopeName) {
|
|
1893
|
+
return await runInteractiveWithSessionFactory(createInteractiveSession, jiraRef, forceRefresh, scopeName);
|
|
1894
|
+
}
|
|
1895
|
+
async function runWebInteractive(jiraRef, forceRefresh = false, noOpen = false, host, auth) {
|
|
1896
|
+
return await runInteractiveWithSessionFactory((options) => createWebInteractiveSession(options, { noOpen, ...(host ? { host } : {}), ...(auth ? { auth } : {}), printInfo }), jiraRef, forceRefresh, null, true);
|
|
1897
|
+
}
|
|
1567
1898
|
export async function main(argv = process.argv.slice(2)) {
|
|
1568
1899
|
loadTieredEnv(process.cwd());
|
|
1569
1900
|
let forceRefresh = false;
|
|
@@ -1573,14 +1904,22 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1573
1904
|
args.shift();
|
|
1574
1905
|
}
|
|
1575
1906
|
try {
|
|
1907
|
+
if (args[0] === "--no-open") {
|
|
1908
|
+
throw new TaskRunnerError("--no-open is only supported after the web command.");
|
|
1909
|
+
}
|
|
1576
1910
|
if (args.length === 0) {
|
|
1577
1911
|
return await runInteractive(undefined, forceRefresh);
|
|
1578
1912
|
}
|
|
1579
1913
|
if (args.length === 1 && !args[0]?.startsWith("-") && !COMMANDS.includes(args[0])) {
|
|
1580
1914
|
return await runInteractive(args[0] ?? "", forceRefresh);
|
|
1581
1915
|
}
|
|
1582
|
-
const parsedArgs = parseCliArgs(args);
|
|
1583
|
-
|
|
1916
|
+
const parsedArgs = await parseCliArgs(args);
|
|
1917
|
+
if (parsedArgs.command === "web") {
|
|
1918
|
+
const webAuth = resolveWebAuthConfig();
|
|
1919
|
+
requireWebAuthForHost(parsedArgs.webHost, webAuth);
|
|
1920
|
+
return await runWebInteractive(parsedArgs.jiraRef, forceRefresh, parsedArgs.webNoOpen === true, parsedArgs.webHost, webAuth);
|
|
1921
|
+
}
|
|
1922
|
+
const commandCompleted = await executeCommand(buildConfigFromArgs(parsedArgs), true, requestUserInputInTerminal, undefined, undefined, false, parsedArgs.launchMode);
|
|
1584
1923
|
if (parsedArgs.command === "doctor") {
|
|
1585
1924
|
return commandCompleted ? 0 : 1;
|
|
1586
1925
|
}
|
|
@@ -1588,12 +1927,12 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1588
1927
|
}
|
|
1589
1928
|
catch (error) {
|
|
1590
1929
|
if (error instanceof TaskRunnerError) {
|
|
1591
|
-
|
|
1930
|
+
writeStderrSync(`Error: ${error.message}\n`);
|
|
1592
1931
|
return 1;
|
|
1593
1932
|
}
|
|
1594
1933
|
const returnCode = Number(error.returnCode);
|
|
1595
1934
|
if (!Number.isNaN(returnCode)) {
|
|
1596
|
-
|
|
1935
|
+
writeStderrSync(`Error: ${formatProcessFailure(error)}\n`);
|
|
1597
1936
|
return returnCode || 1;
|
|
1598
1937
|
}
|
|
1599
1938
|
throw error;
|