@evref-bl/dev-nexus 0.1.0-alpha.0
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 +677 -0
- package/dist/browserOpener.d.ts +9 -0
- package/dist/browserOpener.js +47 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +2374 -0
- package/dist/gitWorktreeService.d.ts +57 -0
- package/dist/gitWorktreeService.js +157 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +47 -0
- package/dist/nexusAgentMcpConfig.d.ts +30 -0
- package/dist/nexusAgentMcpConfig.js +228 -0
- package/dist/nexusAutomation.d.ts +103 -0
- package/dist/nexusAutomation.js +390 -0
- package/dist/nexusAutomationAgentLaunch.d.ts +148 -0
- package/dist/nexusAutomationAgentLaunch.js +855 -0
- package/dist/nexusAutomationAgentProfile.d.ts +39 -0
- package/dist/nexusAutomationAgentProfile.js +103 -0
- package/dist/nexusAutomationAgentSurface.d.ts +62 -0
- package/dist/nexusAutomationAgentSurface.js +90 -0
- package/dist/nexusAutomationCommandExecutor.d.ts +29 -0
- package/dist/nexusAutomationCommandExecutor.js +251 -0
- package/dist/nexusAutomationConfig.d.ts +114 -0
- package/dist/nexusAutomationConfig.js +547 -0
- package/dist/nexusAutomationEnqueue.d.ts +37 -0
- package/dist/nexusAutomationEnqueue.js +128 -0
- package/dist/nexusAutomationRunOnce.d.ts +91 -0
- package/dist/nexusAutomationRunOnce.js +586 -0
- package/dist/nexusAutomationScheduler.d.ts +50 -0
- package/dist/nexusAutomationScheduler.js +196 -0
- package/dist/nexusAutomationStatus.d.ts +55 -0
- package/dist/nexusAutomationStatus.js +462 -0
- package/dist/nexusAutomationTarget.d.ts +19 -0
- package/dist/nexusAutomationTarget.js +33 -0
- package/dist/nexusAutomationTargetCycle.d.ts +90 -0
- package/dist/nexusAutomationTargetCycle.js +282 -0
- package/dist/nexusAutomationTargetReport.d.ts +136 -0
- package/dist/nexusAutomationTargetReport.js +504 -0
- package/dist/nexusAutomationWorktreeSetup.d.ts +89 -0
- package/dist/nexusAutomationWorktreeSetup.js +661 -0
- package/dist/nexusCoordination.d.ts +198 -0
- package/dist/nexusCoordination.js +1018 -0
- package/dist/nexusExtension.d.ts +31 -0
- package/dist/nexusExtension.js +1 -0
- package/dist/nexusHomeConfig.d.ts +38 -0
- package/dist/nexusHomeConfig.js +133 -0
- package/dist/nexusMcpServer.d.ts +31 -0
- package/dist/nexusMcpServer.js +1036 -0
- package/dist/nexusPluginCapabilities.d.ts +197 -0
- package/dist/nexusPluginCapabilities.js +201 -0
- package/dist/nexusProjectConfig.d.ts +95 -0
- package/dist/nexusProjectConfig.js +880 -0
- package/dist/nexusProjectHomeService.d.ts +121 -0
- package/dist/nexusProjectHomeService.js +171 -0
- package/dist/nexusProjectLifecycle.d.ts +62 -0
- package/dist/nexusProjectLifecycle.js +205 -0
- package/dist/nexusProjectOperations.d.ts +101 -0
- package/dist/nexusProjectOperations.js +296 -0
- package/dist/nexusProjectRegistry.d.ts +42 -0
- package/dist/nexusProjectRegistry.js +91 -0
- package/dist/nexusProjectScaffold.d.ts +25 -0
- package/dist/nexusProjectScaffold.js +61 -0
- package/dist/nexusProjectTemplate.d.ts +34 -0
- package/dist/nexusProjectTemplate.js +354 -0
- package/dist/nexusSkills.d.ts +134 -0
- package/dist/nexusSkills.js +647 -0
- package/dist/nexusWorkerContextBundle.d.ts +142 -0
- package/dist/nexusWorkerContextBundle.js +375 -0
- package/dist/processSupervisor.d.ts +89 -0
- package/dist/processSupervisor.js +440 -0
- package/dist/vibeKanbanApi.d.ts +11 -0
- package/dist/vibeKanbanApi.js +14 -0
- package/dist/vibeKanbanAuth.d.ts +25 -0
- package/dist/vibeKanbanAuth.js +101 -0
- package/dist/vibeKanbanBoardAdapter.d.ts +36 -0
- package/dist/vibeKanbanBoardAdapter.js +196 -0
- package/dist/vibeKanbanMcpConfig.d.ts +36 -0
- package/dist/vibeKanbanMcpConfig.js +191 -0
- package/dist/vibeKanbanProjectAdapter.d.ts +39 -0
- package/dist/vibeKanbanProjectAdapter.js +113 -0
- package/dist/vibeKanbanWorkspaceSetup.d.ts +1 -0
- package/dist/vibeKanbanWorkspaceSetup.js +96 -0
- package/dist/workItemService.d.ts +60 -0
- package/dist/workItemService.js +163 -0
- package/dist/workTrackingGitHubProvider.d.ts +71 -0
- package/dist/workTrackingGitHubProvider.js +663 -0
- package/dist/workTrackingGitLabProvider.d.ts +62 -0
- package/dist/workTrackingGitLabProvider.js +523 -0
- package/dist/workTrackingJiraProvider.d.ts +67 -0
- package/dist/workTrackingJiraProvider.js +652 -0
- package/dist/workTrackingLocalProvider.d.ts +49 -0
- package/dist/workTrackingLocalProvider.js +463 -0
- package/dist/workTrackingProviderService.d.ts +21 -0
- package/dist/workTrackingProviderService.js +117 -0
- package/dist/workTrackingTypes.d.ts +202 -0
- package/dist/workTrackingTypes.js +1 -0
- package/dist/workTrackingVibeProvider.d.ts +35 -0
- package/dist/workTrackingVibeProvider.js +119 -0
- package/dist/worktreeExecutionMetadata.d.ts +76 -0
- package/dist/worktreeExecutionMetadata.js +239 -0
- package/package.json +37 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,2374 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createNexusAutomationCommandExecutor, } from "./nexusAutomationCommandExecutor.js";
|
|
7
|
+
import { createNexusAutomationAgentCommandLauncher, runNexusAutomationAgentLaunchOnce, } from "./nexusAutomationAgentLaunch.js";
|
|
8
|
+
import { resolveNexusAutomationAgentCommand, } from "./nexusAutomationAgentProfile.js";
|
|
9
|
+
import { enqueueNexusAutomationWorkItem, } from "./nexusAutomationEnqueue.js";
|
|
10
|
+
import { runNexusAutomationOnce, } from "./nexusAutomationRunOnce.js";
|
|
11
|
+
import { runNexusAutomationScheduler, } from "./nexusAutomationScheduler.js";
|
|
12
|
+
import { getNexusAutomationStatus, } from "./nexusAutomationStatus.js";
|
|
13
|
+
import { getNexusAutomationAgentProfileSummary, getNexusAutomationEligibleWorkSummary, } from "./nexusAutomationAgentSurface.js";
|
|
14
|
+
import { appendNexusAutomationTargetCycleRecord, readNexusAutomationTargetCycleLedger, } from "./nexusAutomationTargetCycle.js";
|
|
15
|
+
import { buildNexusAutomationTargetReport, } from "./nexusAutomationTargetReport.js";
|
|
16
|
+
import { createNexusCoordinationHandoff, getNexusCoordinationIntegrationPlan, getNexusCoordinationStatus, parseNexusCoordinationHandoffStatus, } from "./nexusCoordination.js";
|
|
17
|
+
import { materializeNexusProjectAgentMcpConfig, } from "./nexusAgentMcpConfig.js";
|
|
18
|
+
import { runDevNexusMcpStdioServer } from "./nexusMcpServer.js";
|
|
19
|
+
import { createDefaultNexusHomeConfigBase, defaultNexusHomePath, loadNexusHomeConfigFile, nexusHomeConfigPath, resolveNexusHome, saveNexusHomeConfigFile, validateNexusHomeConfigBase, } from "./nexusHomeConfig.js";
|
|
20
|
+
import { loadProjectConfig, } from "./nexusProjectConfig.js";
|
|
21
|
+
import { configureNexusProjectTracker, createNexusProject, getNexusProjectStatus, importNexusProject, linkNexusProjectTracker, listNexusProjects, } from "./nexusProjectHomeService.js";
|
|
22
|
+
import { resolvePrimaryProjectComponent, resolveProjectComponents, } from "./nexusProjectLifecycle.js";
|
|
23
|
+
import { buildNexusProjectStatusForPath, } from "./nexusProjectRegistry.js";
|
|
24
|
+
import { createWorkItemService, } from "./workItemService.js";
|
|
25
|
+
export function usage() {
|
|
26
|
+
return [
|
|
27
|
+
"Usage:",
|
|
28
|
+
" dev-nexus --help",
|
|
29
|
+
" dev-nexus mcp-stdio",
|
|
30
|
+
" dev-nexus home init [home-path] [options]",
|
|
31
|
+
" dev-nexus project create <name> [options]",
|
|
32
|
+
" dev-nexus project import <source-root> [options]",
|
|
33
|
+
" dev-nexus project list [options]",
|
|
34
|
+
" dev-nexus project status <project-id-or-root> [options]",
|
|
35
|
+
" dev-nexus project mcp refresh <project-root> [options]",
|
|
36
|
+
" dev-nexus project tracker configure <project> --provider <provider> [options]",
|
|
37
|
+
" dev-nexus project tracker link <project> --tracker-project-id <id> [options]",
|
|
38
|
+
" dev-nexus coordination status <project-root> [options]",
|
|
39
|
+
" dev-nexus coordination handoff <project-root> <work-item-id> --status <status> [options]",
|
|
40
|
+
" dev-nexus coordination integrate <project-root> [options]",
|
|
41
|
+
" dev-nexus work-item create <project-root> --title <title> [options]",
|
|
42
|
+
" dev-nexus work-item list <project-root> [options]",
|
|
43
|
+
" dev-nexus work-item get <project-root> <work-item-id> [options]",
|
|
44
|
+
" dev-nexus work-item update <project-root> <work-item-id> [options]",
|
|
45
|
+
" dev-nexus work-item comment <project-root> <work-item-id> --body <text> [options]",
|
|
46
|
+
" dev-nexus automation status <project-root> [options]",
|
|
47
|
+
" dev-nexus automation eligible-work <project-root> [options]",
|
|
48
|
+
" dev-nexus automation agent-profiles <project-root> [options]",
|
|
49
|
+
" dev-nexus automation enqueue <project-root> --title <title> [options]",
|
|
50
|
+
" dev-nexus automation target-cycle list <project-root> [options]",
|
|
51
|
+
" dev-nexus automation target-cycle record <project-root> --status <status> [options]",
|
|
52
|
+
" dev-nexus automation target-report <project-root> [options]",
|
|
53
|
+
" dev-nexus automation run-once <project-root> [--command <command>] [options]",
|
|
54
|
+
" dev-nexus automation schedule <project-root> [--command <command>] [options]",
|
|
55
|
+
"",
|
|
56
|
+
"Options for home init:",
|
|
57
|
+
" --projects-root <path>",
|
|
58
|
+
" --workspaces-root <path>",
|
|
59
|
+
" --json",
|
|
60
|
+
"",
|
|
61
|
+
"Options for project commands:",
|
|
62
|
+
" --home <path> defaults to DEV_NEXUS_HOME or ~/.dev-nexus",
|
|
63
|
+
" --json",
|
|
64
|
+
"",
|
|
65
|
+
"Options for project create:",
|
|
66
|
+
" --home <path>",
|
|
67
|
+
" --root <path>",
|
|
68
|
+
" --from <git-url>",
|
|
69
|
+
" --git-init",
|
|
70
|
+
" --tracker-project-id <id>",
|
|
71
|
+
" --json",
|
|
72
|
+
"",
|
|
73
|
+
"Options for project import:",
|
|
74
|
+
" --home <path>",
|
|
75
|
+
" --project-root <path>",
|
|
76
|
+
" --name <name>",
|
|
77
|
+
" --tracker-project-id <id>",
|
|
78
|
+
" --json",
|
|
79
|
+
"",
|
|
80
|
+
"Options for project mcp refresh:",
|
|
81
|
+
" --agent <codex|claude> repeatable; defaults to project mcp.agentTargets or codex",
|
|
82
|
+
" --json",
|
|
83
|
+
"",
|
|
84
|
+
"Options for project tracker configure:",
|
|
85
|
+
" --home <path>",
|
|
86
|
+
" --provider <local|github|gitlab|jira>",
|
|
87
|
+
" --host <host>",
|
|
88
|
+
" --repository-owner <owner>",
|
|
89
|
+
" --repository-name <name>",
|
|
90
|
+
" --repository-id <id>",
|
|
91
|
+
" --project-key <key>",
|
|
92
|
+
" --issue-type <type>",
|
|
93
|
+
" --store-path <path>",
|
|
94
|
+
" --json",
|
|
95
|
+
"",
|
|
96
|
+
"Options for project tracker link:",
|
|
97
|
+
" --home <path>",
|
|
98
|
+
" --tracker-project-id <id>",
|
|
99
|
+
" --json",
|
|
100
|
+
"",
|
|
101
|
+
"Options for coordination status:",
|
|
102
|
+
" --component <id> defaults to component inferred from --worktree or current directory",
|
|
103
|
+
" --work-item <id>",
|
|
104
|
+
" --worktree <path> git worktree or source checkout used for status",
|
|
105
|
+
" --json",
|
|
106
|
+
"",
|
|
107
|
+
"Options for coordination handoff:",
|
|
108
|
+
" --component <id> defaults to component inferred from --worktree or current directory",
|
|
109
|
+
" --status <working|ready|blocked|merged>",
|
|
110
|
+
" --host <id>",
|
|
111
|
+
" --agent <id>",
|
|
112
|
+
" --changed-area <path> repeatable",
|
|
113
|
+
" --decision <text> repeatable",
|
|
114
|
+
" --verification <text>",
|
|
115
|
+
" --integration-preference <text>",
|
|
116
|
+
" --note <text>",
|
|
117
|
+
" --worktree <path> git worktree or source checkout used for status",
|
|
118
|
+
" --json",
|
|
119
|
+
"",
|
|
120
|
+
"Options for coordination integrate:",
|
|
121
|
+
" --component <id> defaults to component inferred from --worktree or current directory",
|
|
122
|
+
" --work-item <id>",
|
|
123
|
+
" --target-branch <branch> defaults to component or automation publication target",
|
|
124
|
+
" --fetch fetch configured remote when automation safety allows host mutation",
|
|
125
|
+
" --worktree <path> git worktree or source checkout used for planning",
|
|
126
|
+
" --json",
|
|
127
|
+
"",
|
|
128
|
+
"Options for work-item create:",
|
|
129
|
+
" --component <id> defaults to the primary component",
|
|
130
|
+
" --title <title>",
|
|
131
|
+
" --description <text>",
|
|
132
|
+
" --status <todo|ready|in_progress|blocked|done|wont_do>",
|
|
133
|
+
" --label <label> repeatable",
|
|
134
|
+
" --assignee <assignee> repeatable",
|
|
135
|
+
" --milestone <text>",
|
|
136
|
+
" --json",
|
|
137
|
+
"",
|
|
138
|
+
"Options for work-item list:",
|
|
139
|
+
" --component <id> defaults to the primary component",
|
|
140
|
+
" --status <todo|ready|in_progress|blocked|done|wont_do> repeatable",
|
|
141
|
+
" --label <label> repeatable",
|
|
142
|
+
" --assignee <assignee> repeatable",
|
|
143
|
+
" --search <text>",
|
|
144
|
+
" --limit <count>",
|
|
145
|
+
" --json",
|
|
146
|
+
"",
|
|
147
|
+
"Options for work-item get:",
|
|
148
|
+
" --component <id> defaults to the primary component",
|
|
149
|
+
" --json",
|
|
150
|
+
"",
|
|
151
|
+
"Options for work-item update:",
|
|
152
|
+
" --component <id> defaults to the primary component",
|
|
153
|
+
" --title <title>",
|
|
154
|
+
" --description <text>",
|
|
155
|
+
" --clear-description",
|
|
156
|
+
" --status <todo|ready|in_progress|blocked|done|wont_do>",
|
|
157
|
+
" --label <label> repeatable, replaces labels when provided",
|
|
158
|
+
" --clear-labels",
|
|
159
|
+
" --assignee <assignee> repeatable, replaces assignees when provided",
|
|
160
|
+
" --clear-assignees",
|
|
161
|
+
" --milestone <text>",
|
|
162
|
+
" --clear-milestone",
|
|
163
|
+
" --json",
|
|
164
|
+
"",
|
|
165
|
+
"Options for work-item comment:",
|
|
166
|
+
" --component <id> defaults to the primary component",
|
|
167
|
+
" --body <text>",
|
|
168
|
+
" --json",
|
|
169
|
+
"",
|
|
170
|
+
"Options for automation status:",
|
|
171
|
+
" --json",
|
|
172
|
+
"",
|
|
173
|
+
"Options for automation eligible-work:",
|
|
174
|
+
" --json",
|
|
175
|
+
"",
|
|
176
|
+
"Options for automation agent-profiles:",
|
|
177
|
+
" --json",
|
|
178
|
+
"",
|
|
179
|
+
"Options for automation enqueue:",
|
|
180
|
+
" --title <title>",
|
|
181
|
+
" --description <text>",
|
|
182
|
+
" --status <todo|ready|in_progress|blocked|done|wont_do>",
|
|
183
|
+
" --label <label> repeatable, added after selector labels",
|
|
184
|
+
" --assignee <assignee> repeatable, added after selector assignees",
|
|
185
|
+
" --milestone <text>",
|
|
186
|
+
" --json",
|
|
187
|
+
"",
|
|
188
|
+
"Options for automation target-cycle list:",
|
|
189
|
+
" --json",
|
|
190
|
+
"",
|
|
191
|
+
"Options for automation target-cycle record:",
|
|
192
|
+
" --cycle-id <id>",
|
|
193
|
+
" --run-id <id>",
|
|
194
|
+
" --status <started|dispatched|completed|blocked|failed|skipped>",
|
|
195
|
+
" --summary <text>",
|
|
196
|
+
" --eligible-work-items <count>",
|
|
197
|
+
" --work-item <component-id:id> repeatable",
|
|
198
|
+
" --work-item-status <selected|dispatched|in_progress|completed|blocked|skipped>",
|
|
199
|
+
" --work-item-agent-profile <id>",
|
|
200
|
+
" --work-item-note <text>",
|
|
201
|
+
" --blocker <text> repeatable",
|
|
202
|
+
" --note <text> repeatable",
|
|
203
|
+
" --json",
|
|
204
|
+
"",
|
|
205
|
+
"Options for automation target-report:",
|
|
206
|
+
" --json",
|
|
207
|
+
"",
|
|
208
|
+
"Options for automation run-once:",
|
|
209
|
+
" --command <command> shell command to run; overrides automation.executor.command, automation.agent.command, or coordinator profile command",
|
|
210
|
+
" --run-id <id>",
|
|
211
|
+
" --owner <name>",
|
|
212
|
+
" --branch <name>",
|
|
213
|
+
" --worktree-name <name>",
|
|
214
|
+
" --base-ref <ref>",
|
|
215
|
+
" --full also run configured full verification commands",
|
|
216
|
+
" --timeout-ms <ms> applies to each command",
|
|
217
|
+
" --json",
|
|
218
|
+
"",
|
|
219
|
+
"Options for automation schedule:",
|
|
220
|
+
" --command <command> shell command to run; overrides automation.executor.command, automation.agent.command, or coordinator profile command",
|
|
221
|
+
" --owner <name>",
|
|
222
|
+
" --base-ref <ref>",
|
|
223
|
+
" --interval-ms <ms> overrides project automation.schedule.intervalMs",
|
|
224
|
+
" --max-ticks <count> stop after this many scheduler polls",
|
|
225
|
+
" --max-runs <count> stop after this many run-once executions",
|
|
226
|
+
" --run-id-prefix <prefix>",
|
|
227
|
+
" --full also run configured full verification commands",
|
|
228
|
+
" --timeout-ms <ms> applies to each command",
|
|
229
|
+
" --json",
|
|
230
|
+
].join("\n");
|
|
231
|
+
}
|
|
232
|
+
export async function main(argv, dependencies = {}) {
|
|
233
|
+
const stdout = dependencies.stdout ?? process.stdout;
|
|
234
|
+
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
|
|
235
|
+
writeLine(stdout, usage());
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
238
|
+
if (argv[0] === "home") {
|
|
239
|
+
return handleHomeCommand(argv, dependencies);
|
|
240
|
+
}
|
|
241
|
+
if (argv[0] === "project") {
|
|
242
|
+
return handleProjectCommand(argv, dependencies);
|
|
243
|
+
}
|
|
244
|
+
if (argv[0] === "coordination") {
|
|
245
|
+
return handleCoordinationCommand(argv, dependencies);
|
|
246
|
+
}
|
|
247
|
+
if (argv[0] === "work-item") {
|
|
248
|
+
return handleWorkItemCommand(argv, dependencies);
|
|
249
|
+
}
|
|
250
|
+
if (argv[0] === "automation") {
|
|
251
|
+
return handleAutomationCommand(argv, dependencies);
|
|
252
|
+
}
|
|
253
|
+
if (argv[0] === "mcp-stdio") {
|
|
254
|
+
await runDevNexusMcpStdioServer();
|
|
255
|
+
return 0;
|
|
256
|
+
}
|
|
257
|
+
throw new Error("dev-nexus requires home, project, coordination, work-item, automation, mcp-stdio, or --help");
|
|
258
|
+
}
|
|
259
|
+
async function handleHomeCommand(argv, dependencies) {
|
|
260
|
+
if (argv[1] !== "init") {
|
|
261
|
+
throw new Error("home requires init");
|
|
262
|
+
}
|
|
263
|
+
const parsed = parseHomeInitCommand(argv);
|
|
264
|
+
const homePath = resolveNexusHome(parsed.homePath);
|
|
265
|
+
const configPath = nexusHomeConfigPath(homePath);
|
|
266
|
+
if (fs.existsSync(configPath)) {
|
|
267
|
+
throw new Error(`DevNexus home already exists: ${configPath}`);
|
|
268
|
+
}
|
|
269
|
+
const config = createDefaultNexusHomeConfigBase(homePath, {
|
|
270
|
+
...(parsed.projectsRoot !== undefined ? { projectsRoot: parsed.projectsRoot } : {}),
|
|
271
|
+
...(parsed.workspacesRoot !== undefined ? { workspacesRoot: parsed.workspacesRoot } : {}),
|
|
272
|
+
});
|
|
273
|
+
const savedPath = saveNexusHomeConfigFile(homePath, config, validateNexusHomeConfigBase);
|
|
274
|
+
printHomeInitResult({ homePath, configPath: savedPath, config }, parsed, dependencies.stdout ?? process.stdout);
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
async function handleProjectCommand(argv, dependencies) {
|
|
278
|
+
const command = argv[1];
|
|
279
|
+
if (command === "create") {
|
|
280
|
+
const parsed = parseProjectCreateCommand(argv);
|
|
281
|
+
const result = createNexusProject({
|
|
282
|
+
homePath: resolvedCommandHomePath(parsed.homePath),
|
|
283
|
+
homeStore: fileProjectHomeStore(),
|
|
284
|
+
name: parsed.name,
|
|
285
|
+
...(parsed.root !== undefined ? { root: parsed.root } : {}),
|
|
286
|
+
...(parsed.from !== undefined ? { from: parsed.from } : {}),
|
|
287
|
+
...(parsed.gitInit !== undefined ? { gitInit: parsed.gitInit } : {}),
|
|
288
|
+
...(parsed.trackerProjectId !== undefined
|
|
289
|
+
? { vibeKanbanProjectId: parsed.trackerProjectId }
|
|
290
|
+
: {}),
|
|
291
|
+
...(dependencies.projectGitRunner ? { gitRunner: dependencies.projectGitRunner } : {}),
|
|
292
|
+
});
|
|
293
|
+
printProjectCreateResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
294
|
+
return 0;
|
|
295
|
+
}
|
|
296
|
+
if (command === "import") {
|
|
297
|
+
const parsed = parseProjectImportCommand(argv);
|
|
298
|
+
const result = importNexusProject({
|
|
299
|
+
homePath: resolvedCommandHomePath(parsed.homePath),
|
|
300
|
+
homeStore: fileProjectHomeStore(),
|
|
301
|
+
root: parsed.root,
|
|
302
|
+
...(parsed.projectRoot !== undefined ? { projectRoot: parsed.projectRoot } : {}),
|
|
303
|
+
...(parsed.name !== undefined ? { name: parsed.name } : {}),
|
|
304
|
+
...(parsed.trackerProjectId !== undefined
|
|
305
|
+
? { vibeKanbanProjectId: parsed.trackerProjectId }
|
|
306
|
+
: {}),
|
|
307
|
+
...(dependencies.projectGitRunner ? { gitRunner: dependencies.projectGitRunner } : {}),
|
|
308
|
+
});
|
|
309
|
+
printProjectImportResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
if (command === "list") {
|
|
313
|
+
const parsed = parseProjectListCommand(argv);
|
|
314
|
+
const result = listNexusProjects({
|
|
315
|
+
homePath: resolvedCommandHomePath(parsed.homePath),
|
|
316
|
+
homeStore: fileProjectHomeStore(),
|
|
317
|
+
});
|
|
318
|
+
printProjectListResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
if (command === "status") {
|
|
322
|
+
const parsed = parseProjectStatusCommand(argv);
|
|
323
|
+
const result = resolveProjectStatusForCli(parsed);
|
|
324
|
+
printProjectStatusResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
if (command === "mcp") {
|
|
328
|
+
return handleProjectMcpCommand(argv, dependencies);
|
|
329
|
+
}
|
|
330
|
+
if (command === "tracker") {
|
|
331
|
+
return handleProjectTrackerCommand(argv, dependencies);
|
|
332
|
+
}
|
|
333
|
+
throw new Error("project requires create, import, list, status, mcp, or tracker");
|
|
334
|
+
}
|
|
335
|
+
async function handleProjectMcpCommand(argv, dependencies) {
|
|
336
|
+
const command = argv[2];
|
|
337
|
+
if (command !== "refresh") {
|
|
338
|
+
throw new Error("project mcp requires refresh");
|
|
339
|
+
}
|
|
340
|
+
const parsed = parseProjectMcpRefreshCommand(argv);
|
|
341
|
+
const projectRoot = path.resolve(parsed.projectRoot);
|
|
342
|
+
const projectConfig = loadProjectConfig(projectRoot);
|
|
343
|
+
const result = materializeNexusProjectAgentMcpConfig({
|
|
344
|
+
projectRoot,
|
|
345
|
+
mcpConfig: projectConfig.mcp,
|
|
346
|
+
...(parsed.agents.length > 0
|
|
347
|
+
? { agentTargets: parsed.agents.map((agent) => ({ agent })) }
|
|
348
|
+
: {}),
|
|
349
|
+
});
|
|
350
|
+
printProjectMcpRefreshResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
async function handleProjectTrackerCommand(argv, dependencies) {
|
|
354
|
+
const command = argv[2];
|
|
355
|
+
if (command === "configure") {
|
|
356
|
+
const parsed = parseProjectTrackerConfigureCommand(argv);
|
|
357
|
+
const result = configureNexusProjectTracker({
|
|
358
|
+
homePath: resolvedCommandHomePath(parsed.homePath),
|
|
359
|
+
homeStore: fileProjectHomeStore(),
|
|
360
|
+
project: parsed.project,
|
|
361
|
+
provider: parsed.provider,
|
|
362
|
+
...(parsed.host !== undefined ? { host: parsed.host } : {}),
|
|
363
|
+
...(parsed.repositoryOwner !== undefined
|
|
364
|
+
? { repositoryOwner: parsed.repositoryOwner }
|
|
365
|
+
: {}),
|
|
366
|
+
...(parsed.repositoryName !== undefined
|
|
367
|
+
? { repositoryName: parsed.repositoryName }
|
|
368
|
+
: {}),
|
|
369
|
+
...(parsed.repositoryId !== undefined ? { repositoryId: parsed.repositoryId } : {}),
|
|
370
|
+
...(parsed.projectKey !== undefined ? { projectKey: parsed.projectKey } : {}),
|
|
371
|
+
...(parsed.issueType !== undefined ? { issueType: parsed.issueType } : {}),
|
|
372
|
+
...(parsed.storePath !== undefined ? { storePath: parsed.storePath } : {}),
|
|
373
|
+
});
|
|
374
|
+
printProjectTrackerConfigureResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
375
|
+
return 0;
|
|
376
|
+
}
|
|
377
|
+
if (command === "link") {
|
|
378
|
+
const parsed = parseProjectTrackerLinkCommand(argv);
|
|
379
|
+
const result = linkNexusProjectTracker({
|
|
380
|
+
homePath: resolvedCommandHomePath(parsed.homePath),
|
|
381
|
+
homeStore: fileProjectHomeStore(),
|
|
382
|
+
project: parsed.project,
|
|
383
|
+
trackerProjectId: parsed.trackerProjectId,
|
|
384
|
+
});
|
|
385
|
+
printProjectTrackerLinkResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
throw new Error("project tracker requires configure or link");
|
|
389
|
+
}
|
|
390
|
+
async function handleCoordinationCommand(argv, dependencies) {
|
|
391
|
+
const command = argv[1];
|
|
392
|
+
if (command === "status") {
|
|
393
|
+
const parsed = parseCoordinationStatusCommand(argv);
|
|
394
|
+
const status = await getNexusCoordinationStatus({
|
|
395
|
+
projectRoot: parsed.projectRoot,
|
|
396
|
+
componentId: parsed.componentId,
|
|
397
|
+
workItemId: parsed.workItemId,
|
|
398
|
+
currentPath: parsed.currentPath ?? process.cwd(),
|
|
399
|
+
gitRunner: dependencies.gitRunner,
|
|
400
|
+
now: dependencies.now,
|
|
401
|
+
});
|
|
402
|
+
printCoordinationStatusResult(status, parsed, dependencies.stdout ?? process.stdout);
|
|
403
|
+
return 0;
|
|
404
|
+
}
|
|
405
|
+
if (command === "handoff") {
|
|
406
|
+
const parsed = parseCoordinationHandoffCommand(argv);
|
|
407
|
+
const result = await createNexusCoordinationHandoff({
|
|
408
|
+
projectRoot: parsed.projectRoot,
|
|
409
|
+
componentId: parsed.componentId,
|
|
410
|
+
workItemId: parsed.workItemId,
|
|
411
|
+
status: parsed.status,
|
|
412
|
+
hostId: parsed.hostId,
|
|
413
|
+
agentId: parsed.agentId,
|
|
414
|
+
changedAreas: parsed.changedAreas,
|
|
415
|
+
decisions: parsed.decisions,
|
|
416
|
+
verificationSummary: parsed.verificationSummary,
|
|
417
|
+
integrationPreference: parsed.integrationPreference,
|
|
418
|
+
note: parsed.note,
|
|
419
|
+
currentPath: parsed.currentPath ?? process.cwd(),
|
|
420
|
+
gitRunner: dependencies.gitRunner,
|
|
421
|
+
now: dependencies.now,
|
|
422
|
+
});
|
|
423
|
+
printCoordinationHandoffResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
424
|
+
return 0;
|
|
425
|
+
}
|
|
426
|
+
if (command === "integrate") {
|
|
427
|
+
const parsed = parseCoordinationIntegrateCommand(argv);
|
|
428
|
+
const plan = await getNexusCoordinationIntegrationPlan({
|
|
429
|
+
projectRoot: parsed.projectRoot,
|
|
430
|
+
componentId: parsed.componentId,
|
|
431
|
+
workItemId: parsed.workItemId,
|
|
432
|
+
targetBranch: parsed.targetBranch,
|
|
433
|
+
fetch: parsed.fetch,
|
|
434
|
+
currentPath: parsed.currentPath ?? process.cwd(),
|
|
435
|
+
gitRunner: dependencies.gitRunner,
|
|
436
|
+
now: dependencies.now,
|
|
437
|
+
});
|
|
438
|
+
printCoordinationIntegrationPlan(plan, parsed, dependencies.stdout ?? process.stdout);
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
throw new Error("coordination requires status, handoff, or integrate");
|
|
442
|
+
}
|
|
443
|
+
async function handleWorkItemCommand(argv, dependencies) {
|
|
444
|
+
const command = argv[1];
|
|
445
|
+
if (command === "create") {
|
|
446
|
+
const parsed = parseWorkItemCreateCommand(argv);
|
|
447
|
+
const item = await workItemService(parsed.projectRoot, dependencies)
|
|
448
|
+
.createWorkItem({
|
|
449
|
+
projectRoot: path.resolve(parsed.projectRoot),
|
|
450
|
+
componentId: parsed.componentId,
|
|
451
|
+
title: parsed.title,
|
|
452
|
+
description: parsed.description,
|
|
453
|
+
status: parsed.status,
|
|
454
|
+
labels: parsed.labels,
|
|
455
|
+
assignees: parsed.assignees,
|
|
456
|
+
milestone: parsed.milestone,
|
|
457
|
+
});
|
|
458
|
+
printWorkItemCreateResult(item, parsed, dependencies.stdout ?? process.stdout);
|
|
459
|
+
return 0;
|
|
460
|
+
}
|
|
461
|
+
if (command === "list") {
|
|
462
|
+
const parsed = parseWorkItemListCommand(argv);
|
|
463
|
+
const items = await workItemService(parsed.projectRoot, dependencies)
|
|
464
|
+
.listWorkItems({
|
|
465
|
+
projectRoot: path.resolve(parsed.projectRoot),
|
|
466
|
+
componentId: parsed.componentId,
|
|
467
|
+
status: statusQuery(parsed.statuses),
|
|
468
|
+
labels: parsed.labels,
|
|
469
|
+
assignees: parsed.assignees,
|
|
470
|
+
search: parsed.search,
|
|
471
|
+
limit: parsed.limit,
|
|
472
|
+
});
|
|
473
|
+
printWorkItemListResult(items, parsed, dependencies.stdout ?? process.stdout);
|
|
474
|
+
return 0;
|
|
475
|
+
}
|
|
476
|
+
if (command === "get") {
|
|
477
|
+
const parsed = parseWorkItemGetCommand(argv);
|
|
478
|
+
const item = await workItemService(parsed.projectRoot, dependencies)
|
|
479
|
+
.getWorkItem({
|
|
480
|
+
projectRoot: path.resolve(parsed.projectRoot),
|
|
481
|
+
componentId: parsed.componentId,
|
|
482
|
+
id: parsed.itemId,
|
|
483
|
+
});
|
|
484
|
+
printWorkItemGetResult(item, parsed, dependencies.stdout ?? process.stdout);
|
|
485
|
+
return 0;
|
|
486
|
+
}
|
|
487
|
+
if (command === "update") {
|
|
488
|
+
const parsed = parseWorkItemUpdateCommand(argv);
|
|
489
|
+
const item = await workItemService(parsed.projectRoot, dependencies)
|
|
490
|
+
.updateWorkItem({
|
|
491
|
+
projectRoot: path.resolve(parsed.projectRoot),
|
|
492
|
+
componentId: parsed.componentId,
|
|
493
|
+
ref: { id: parsed.itemId },
|
|
494
|
+
patch: parsed.patch,
|
|
495
|
+
});
|
|
496
|
+
printWorkItemUpdateResult(item, parsed, dependencies.stdout ?? process.stdout);
|
|
497
|
+
return 0;
|
|
498
|
+
}
|
|
499
|
+
if (command === "comment") {
|
|
500
|
+
const parsed = parseWorkItemCommentCommand(argv);
|
|
501
|
+
const comment = await workItemService(parsed.projectRoot, dependencies)
|
|
502
|
+
.addComment({
|
|
503
|
+
projectRoot: path.resolve(parsed.projectRoot),
|
|
504
|
+
componentId: parsed.componentId,
|
|
505
|
+
ref: { id: parsed.itemId },
|
|
506
|
+
body: parsed.body,
|
|
507
|
+
});
|
|
508
|
+
printWorkItemCommentResult(comment, parsed, dependencies.stdout ?? process.stdout);
|
|
509
|
+
return 0;
|
|
510
|
+
}
|
|
511
|
+
throw new Error("work-item requires create, list, get, update, or comment");
|
|
512
|
+
}
|
|
513
|
+
async function handleAutomationCommand(argv, dependencies) {
|
|
514
|
+
if (argv[1] === "status") {
|
|
515
|
+
const parsed = parseAutomationStatusCommand(argv);
|
|
516
|
+
const result = await getNexusAutomationStatus({
|
|
517
|
+
projectRoot: parsed.projectRoot,
|
|
518
|
+
now: dependencies.now,
|
|
519
|
+
});
|
|
520
|
+
printAutomationStatusResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
521
|
+
return 0;
|
|
522
|
+
}
|
|
523
|
+
if (argv[1] === "eligible-work") {
|
|
524
|
+
const parsed = parseAutomationEligibleWorkCommand(argv);
|
|
525
|
+
const result = await getNexusAutomationEligibleWorkSummary({
|
|
526
|
+
projectRoot: parsed.projectRoot,
|
|
527
|
+
now: dependencies.now,
|
|
528
|
+
});
|
|
529
|
+
printAutomationEligibleWorkResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
530
|
+
return 0;
|
|
531
|
+
}
|
|
532
|
+
if (argv[1] === "agent-profiles") {
|
|
533
|
+
const parsed = parseAutomationAgentProfilesCommand(argv);
|
|
534
|
+
const result = getNexusAutomationAgentProfileSummary(parsed.projectRoot);
|
|
535
|
+
printAutomationAgentProfilesResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
536
|
+
return 0;
|
|
537
|
+
}
|
|
538
|
+
if (argv[1] === "enqueue") {
|
|
539
|
+
const parsed = parseAutomationEnqueueCommand(argv);
|
|
540
|
+
const result = await enqueueNexusAutomationWorkItem({
|
|
541
|
+
projectRoot: parsed.projectRoot,
|
|
542
|
+
title: parsed.title,
|
|
543
|
+
description: parsed.description,
|
|
544
|
+
status: parsed.status,
|
|
545
|
+
labels: parsed.labels,
|
|
546
|
+
assignees: parsed.assignees,
|
|
547
|
+
milestone: parsed.milestone,
|
|
548
|
+
now: dependencies.now,
|
|
549
|
+
});
|
|
550
|
+
printAutomationEnqueueResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
551
|
+
return 0;
|
|
552
|
+
}
|
|
553
|
+
if (argv[1] === "target-cycle") {
|
|
554
|
+
return handleAutomationTargetCycleCommand(argv, dependencies);
|
|
555
|
+
}
|
|
556
|
+
if (argv[1] === "target-report") {
|
|
557
|
+
const parsed = parseAutomationTargetReportCommand(argv);
|
|
558
|
+
const result = buildNexusAutomationTargetReport({
|
|
559
|
+
projectRoot: parsed.projectRoot,
|
|
560
|
+
now: dependencies.now?.(),
|
|
561
|
+
});
|
|
562
|
+
printAutomationTargetReportResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
563
|
+
return 0;
|
|
564
|
+
}
|
|
565
|
+
if (argv[1] === "schedule") {
|
|
566
|
+
const parsed = parseAutomationScheduleCommand(argv);
|
|
567
|
+
const commandOptions = resolveAutomationCommandCliOptions("schedule", parsed);
|
|
568
|
+
const stdout = dependencies.stdout ?? process.stdout;
|
|
569
|
+
const result = await runNexusAutomationScheduler({
|
|
570
|
+
projectRoot: parsed.projectRoot,
|
|
571
|
+
owner: parsed.owner,
|
|
572
|
+
baseRef: parsed.baseRef,
|
|
573
|
+
intervalMs: parsed.intervalMs,
|
|
574
|
+
maxTicks: parsed.maxTicks,
|
|
575
|
+
maxRuns: parsed.maxRuns,
|
|
576
|
+
runIdPrefix: parsed.runIdPrefix,
|
|
577
|
+
gitRunner: dependencies.gitRunner,
|
|
578
|
+
now: dependencies.now,
|
|
579
|
+
onTick: parsed.json
|
|
580
|
+
? undefined
|
|
581
|
+
: (tick) => printAutomationScheduleTick(tick, stdout),
|
|
582
|
+
...(commandOptions.mode === "agent_launch"
|
|
583
|
+
? {
|
|
584
|
+
agentLauncher: createNexusAutomationAgentCommandLauncher({
|
|
585
|
+
command: commandOptions.command,
|
|
586
|
+
commandRunner: dependencies.commandRunner,
|
|
587
|
+
timeoutMs: commandOptions.timeoutMs,
|
|
588
|
+
}),
|
|
589
|
+
}
|
|
590
|
+
: {
|
|
591
|
+
executor: createNexusAutomationCommandExecutor({
|
|
592
|
+
command: commandOptions.command,
|
|
593
|
+
commandRunner: dependencies.commandRunner,
|
|
594
|
+
gitRunner: dependencies.gitRunner,
|
|
595
|
+
runFullVerification: commandOptions.runFullVerification,
|
|
596
|
+
timeoutMs: commandOptions.timeoutMs,
|
|
597
|
+
}),
|
|
598
|
+
}),
|
|
599
|
+
});
|
|
600
|
+
printAutomationScheduleResult(result, parsed, stdout);
|
|
601
|
+
return 0;
|
|
602
|
+
}
|
|
603
|
+
if (argv[1] !== "run-once") {
|
|
604
|
+
throw new Error("automation requires status, eligible-work, agent-profiles, enqueue, target-cycle, target-report, run-once, or schedule");
|
|
605
|
+
}
|
|
606
|
+
const parsed = parseAutomationRunOnceCommand(argv);
|
|
607
|
+
const commandOptions = resolveAutomationCommandCliOptions("run-once", parsed);
|
|
608
|
+
if (commandOptions.mode === "agent_launch") {
|
|
609
|
+
const result = await runNexusAutomationAgentLaunchOnce({
|
|
610
|
+
projectRoot: parsed.projectRoot,
|
|
611
|
+
runId: parsed.runId,
|
|
612
|
+
owner: parsed.owner,
|
|
613
|
+
now: dependencies.now,
|
|
614
|
+
launcher: createNexusAutomationAgentCommandLauncher({
|
|
615
|
+
command: commandOptions.command,
|
|
616
|
+
commandRunner: dependencies.commandRunner,
|
|
617
|
+
timeoutMs: commandOptions.timeoutMs,
|
|
618
|
+
}),
|
|
619
|
+
});
|
|
620
|
+
printAutomationAgentLaunchResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
const result = await runNexusAutomationOnce({
|
|
624
|
+
projectRoot: parsed.projectRoot,
|
|
625
|
+
runId: parsed.runId,
|
|
626
|
+
owner: parsed.owner,
|
|
627
|
+
branchName: parsed.branchName,
|
|
628
|
+
worktreeName: parsed.worktreeName,
|
|
629
|
+
baseRef: parsed.baseRef,
|
|
630
|
+
gitRunner: dependencies.gitRunner,
|
|
631
|
+
now: dependencies.now,
|
|
632
|
+
executor: createNexusAutomationCommandExecutor({
|
|
633
|
+
command: commandOptions.command,
|
|
634
|
+
commandRunner: dependencies.commandRunner,
|
|
635
|
+
gitRunner: dependencies.gitRunner,
|
|
636
|
+
runFullVerification: commandOptions.runFullVerification,
|
|
637
|
+
timeoutMs: commandOptions.timeoutMs,
|
|
638
|
+
}),
|
|
639
|
+
});
|
|
640
|
+
printAutomationRunOnceResult(result, parsed, dependencies.stdout ?? process.stdout);
|
|
641
|
+
}
|
|
642
|
+
return 0;
|
|
643
|
+
}
|
|
644
|
+
async function handleAutomationTargetCycleCommand(argv, dependencies) {
|
|
645
|
+
const command = argv[2];
|
|
646
|
+
if (command === "list") {
|
|
647
|
+
const parsed = parseAutomationTargetCycleListCommand(argv);
|
|
648
|
+
const { projectConfig, automationConfig } = automationConfigForProjectRoot(parsed.projectRoot);
|
|
649
|
+
const ledger = readNexusAutomationTargetCycleLedger(path.resolve(parsed.projectRoot), automationConfig);
|
|
650
|
+
printAutomationTargetCycleListResult({
|
|
651
|
+
projectConfig,
|
|
652
|
+
ledger,
|
|
653
|
+
}, parsed, dependencies.stdout ?? process.stdout);
|
|
654
|
+
return 0;
|
|
655
|
+
}
|
|
656
|
+
if (command === "record") {
|
|
657
|
+
const parsed = parseAutomationTargetCycleRecordCommand(argv);
|
|
658
|
+
const { projectConfig, automationConfig } = automationConfigForProjectRoot(parsed.projectRoot);
|
|
659
|
+
const ledger = appendNexusAutomationTargetCycleRecord({
|
|
660
|
+
projectRoot: path.resolve(parsed.projectRoot),
|
|
661
|
+
config: automationConfig,
|
|
662
|
+
now: dependencies.now?.(),
|
|
663
|
+
record: {
|
|
664
|
+
...(parsed.cycleId ? { id: parsed.cycleId } : {}),
|
|
665
|
+
projectId: projectConfig.id,
|
|
666
|
+
targetId: automationConfig.target.id,
|
|
667
|
+
objective: automationConfig.target.objective,
|
|
668
|
+
...(parsed.runId ? { runId: parsed.runId } : {}),
|
|
669
|
+
status: parsed.status,
|
|
670
|
+
summary: parsed.summary ?? null,
|
|
671
|
+
eligibleWorkItemCount: parsed.eligibleWorkItemCount ?? null,
|
|
672
|
+
workItems: parsed.workItems,
|
|
673
|
+
blockers: parsed.blockers,
|
|
674
|
+
notes: parsed.notes,
|
|
675
|
+
},
|
|
676
|
+
});
|
|
677
|
+
printAutomationTargetCycleRecordResult({
|
|
678
|
+
projectConfig,
|
|
679
|
+
record: ledger.cycles.at(-1),
|
|
680
|
+
ledger,
|
|
681
|
+
}, parsed, dependencies.stdout ?? process.stdout);
|
|
682
|
+
return 0;
|
|
683
|
+
}
|
|
684
|
+
throw new Error("automation target-cycle requires list or record");
|
|
685
|
+
}
|
|
686
|
+
function resolveAutomationCommandCliOptions(commandName, parsed) {
|
|
687
|
+
const config = loadProjectConfig(path.resolve(parsed.projectRoot));
|
|
688
|
+
const mode = config.automation?.mode ?? "run_once";
|
|
689
|
+
const automationConfig = config.automation;
|
|
690
|
+
const configuredCommand = mode === "agent_launch"
|
|
691
|
+
? automationConfig
|
|
692
|
+
? resolveNexusAutomationAgentCommand({
|
|
693
|
+
automationConfig,
|
|
694
|
+
overrideCommand: parsed.command,
|
|
695
|
+
commandName,
|
|
696
|
+
}).command
|
|
697
|
+
: parsed.command
|
|
698
|
+
: parsed.command ?? automationConfig?.executor.command;
|
|
699
|
+
const configuredTimeoutMs = mode === "agent_launch"
|
|
700
|
+
? automationConfig?.agent.timeoutMs
|
|
701
|
+
: automationConfig?.executor.timeoutMs;
|
|
702
|
+
const command = configuredCommand ?? undefined;
|
|
703
|
+
if (!command) {
|
|
704
|
+
throw new Error(mode === "agent_launch"
|
|
705
|
+
? `automation ${commandName} requires --command or project config automation.agent.command`
|
|
706
|
+
: `automation ${commandName} requires --command or project config automation.executor.command`);
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
mode,
|
|
710
|
+
command,
|
|
711
|
+
runFullVerification: parsed.runFullVerification ??
|
|
712
|
+
config.automation?.executor.runFullVerification ??
|
|
713
|
+
false,
|
|
714
|
+
...(parsed.timeoutMs ?? configuredTimeoutMs
|
|
715
|
+
? { timeoutMs: parsed.timeoutMs ?? configuredTimeoutMs ?? undefined }
|
|
716
|
+
: {}),
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function workItemService(projectRoot, dependencies) {
|
|
720
|
+
return createWorkItemService({
|
|
721
|
+
resolveProject: (selector) => resolveDirectProject(projectRoot, selector.componentId),
|
|
722
|
+
now: dependencies.now,
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
function resolveDirectProject(projectRoot, componentId) {
|
|
726
|
+
const resolvedProjectRoot = path.resolve(projectRoot);
|
|
727
|
+
const config = loadProjectConfig(resolvedProjectRoot);
|
|
728
|
+
const component = componentId
|
|
729
|
+
? resolveProjectComponents(resolvedProjectRoot, config).find((candidate) => candidate.id === componentId)
|
|
730
|
+
: resolvePrimaryProjectComponent(resolvedProjectRoot, config);
|
|
731
|
+
if (!component) {
|
|
732
|
+
throw new Error(`Project component is not configured: ${componentId}`);
|
|
733
|
+
}
|
|
734
|
+
if (!component.workTracking) {
|
|
735
|
+
throw new Error(`Component ${component.id} work tracking is not configured`);
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
homePath: config.home ?? "",
|
|
739
|
+
projectRoot: resolvedProjectRoot,
|
|
740
|
+
projectId: config.id,
|
|
741
|
+
projectName: config.name,
|
|
742
|
+
componentId: component.id,
|
|
743
|
+
componentName: component.name,
|
|
744
|
+
sourceRoot: component.sourceRoot,
|
|
745
|
+
workTracking: component.workTracking,
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function parseHomeInitCommand(argv) {
|
|
749
|
+
const rest = argv.slice(2);
|
|
750
|
+
const parsed = {
|
|
751
|
+
homePath: defaultNexusHomePath(),
|
|
752
|
+
};
|
|
753
|
+
let homePathProvided = false;
|
|
754
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
755
|
+
const arg = rest[index];
|
|
756
|
+
const next = () => {
|
|
757
|
+
index += 1;
|
|
758
|
+
if (index >= rest.length) {
|
|
759
|
+
throw new Error(`${arg} requires a value`);
|
|
760
|
+
}
|
|
761
|
+
return rest[index];
|
|
762
|
+
};
|
|
763
|
+
switch (arg) {
|
|
764
|
+
case "--projects-root":
|
|
765
|
+
parsed.projectsRoot = next();
|
|
766
|
+
break;
|
|
767
|
+
case "--workspaces-root":
|
|
768
|
+
parsed.workspacesRoot = next();
|
|
769
|
+
break;
|
|
770
|
+
case "--json":
|
|
771
|
+
parsed.json = true;
|
|
772
|
+
break;
|
|
773
|
+
default:
|
|
774
|
+
if (arg.startsWith("--")) {
|
|
775
|
+
throw new Error(`Unknown home init option: ${arg}`);
|
|
776
|
+
}
|
|
777
|
+
if (homePathProvided) {
|
|
778
|
+
throw new Error("home init accepts at most one home path");
|
|
779
|
+
}
|
|
780
|
+
homePathProvided = true;
|
|
781
|
+
parsed.homePath = arg;
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return parsed;
|
|
786
|
+
}
|
|
787
|
+
function parseProjectCreateCommand(argv) {
|
|
788
|
+
const [, , name, ...rest] = argv;
|
|
789
|
+
if (!name || name.startsWith("--")) {
|
|
790
|
+
throw new Error("project create requires a name");
|
|
791
|
+
}
|
|
792
|
+
const parsed = { name };
|
|
793
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
794
|
+
const arg = rest[index];
|
|
795
|
+
const next = () => {
|
|
796
|
+
index += 1;
|
|
797
|
+
if (index >= rest.length) {
|
|
798
|
+
throw new Error(`${arg} requires a value`);
|
|
799
|
+
}
|
|
800
|
+
return rest[index];
|
|
801
|
+
};
|
|
802
|
+
switch (arg) {
|
|
803
|
+
case "--home":
|
|
804
|
+
parsed.homePath = next();
|
|
805
|
+
break;
|
|
806
|
+
case "--root":
|
|
807
|
+
parsed.root = next();
|
|
808
|
+
break;
|
|
809
|
+
case "--from":
|
|
810
|
+
parsed.from = next();
|
|
811
|
+
break;
|
|
812
|
+
case "--git-init":
|
|
813
|
+
parsed.gitInit = true;
|
|
814
|
+
break;
|
|
815
|
+
case "--tracker-project-id":
|
|
816
|
+
parsed.trackerProjectId = next();
|
|
817
|
+
break;
|
|
818
|
+
case "--json":
|
|
819
|
+
parsed.json = true;
|
|
820
|
+
break;
|
|
821
|
+
default:
|
|
822
|
+
throw new Error(`Unknown project create option: ${arg}`);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return parsed;
|
|
826
|
+
}
|
|
827
|
+
function parseProjectImportCommand(argv) {
|
|
828
|
+
const [, , root, ...rest] = argv;
|
|
829
|
+
if (!root || root.startsWith("--")) {
|
|
830
|
+
throw new Error("project import requires a source root");
|
|
831
|
+
}
|
|
832
|
+
const parsed = { root };
|
|
833
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
834
|
+
const arg = rest[index];
|
|
835
|
+
const next = () => {
|
|
836
|
+
index += 1;
|
|
837
|
+
if (index >= rest.length) {
|
|
838
|
+
throw new Error(`${arg} requires a value`);
|
|
839
|
+
}
|
|
840
|
+
return rest[index];
|
|
841
|
+
};
|
|
842
|
+
switch (arg) {
|
|
843
|
+
case "--home":
|
|
844
|
+
parsed.homePath = next();
|
|
845
|
+
break;
|
|
846
|
+
case "--project-root":
|
|
847
|
+
parsed.projectRoot = next();
|
|
848
|
+
break;
|
|
849
|
+
case "--name":
|
|
850
|
+
parsed.name = next();
|
|
851
|
+
break;
|
|
852
|
+
case "--tracker-project-id":
|
|
853
|
+
parsed.trackerProjectId = next();
|
|
854
|
+
break;
|
|
855
|
+
case "--json":
|
|
856
|
+
parsed.json = true;
|
|
857
|
+
break;
|
|
858
|
+
default:
|
|
859
|
+
throw new Error(`Unknown project import option: ${arg}`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
return parsed;
|
|
863
|
+
}
|
|
864
|
+
function parseProjectListCommand(argv) {
|
|
865
|
+
const rest = argv.slice(2);
|
|
866
|
+
const parsed = {};
|
|
867
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
868
|
+
const arg = rest[index];
|
|
869
|
+
const next = () => {
|
|
870
|
+
index += 1;
|
|
871
|
+
if (index >= rest.length) {
|
|
872
|
+
throw new Error(`${arg} requires a value`);
|
|
873
|
+
}
|
|
874
|
+
return rest[index];
|
|
875
|
+
};
|
|
876
|
+
switch (arg) {
|
|
877
|
+
case "--home":
|
|
878
|
+
parsed.homePath = next();
|
|
879
|
+
break;
|
|
880
|
+
case "--json":
|
|
881
|
+
parsed.json = true;
|
|
882
|
+
break;
|
|
883
|
+
default:
|
|
884
|
+
throw new Error(`Unknown project list option: ${arg}`);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return parsed;
|
|
888
|
+
}
|
|
889
|
+
function parseProjectStatusCommand(argv) {
|
|
890
|
+
const [, , project, ...rest] = argv;
|
|
891
|
+
if (!project || project.startsWith("--")) {
|
|
892
|
+
throw new Error("project status requires a project id or root");
|
|
893
|
+
}
|
|
894
|
+
const parsed = { project };
|
|
895
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
896
|
+
const arg = rest[index];
|
|
897
|
+
const next = () => {
|
|
898
|
+
index += 1;
|
|
899
|
+
if (index >= rest.length) {
|
|
900
|
+
throw new Error(`${arg} requires a value`);
|
|
901
|
+
}
|
|
902
|
+
return rest[index];
|
|
903
|
+
};
|
|
904
|
+
switch (arg) {
|
|
905
|
+
case "--home":
|
|
906
|
+
parsed.homePath = next();
|
|
907
|
+
break;
|
|
908
|
+
case "--json":
|
|
909
|
+
parsed.json = true;
|
|
910
|
+
break;
|
|
911
|
+
default:
|
|
912
|
+
throw new Error(`Unknown project status option: ${arg}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return parsed;
|
|
916
|
+
}
|
|
917
|
+
function parseProjectMcpRefreshCommand(argv) {
|
|
918
|
+
const [, , , projectRoot, ...rest] = argv;
|
|
919
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
920
|
+
throw new Error("project mcp refresh requires a project root");
|
|
921
|
+
}
|
|
922
|
+
const parsed = {
|
|
923
|
+
projectRoot,
|
|
924
|
+
agents: [],
|
|
925
|
+
};
|
|
926
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
927
|
+
const arg = rest[index];
|
|
928
|
+
const next = () => {
|
|
929
|
+
index += 1;
|
|
930
|
+
if (index >= rest.length) {
|
|
931
|
+
throw new Error(`${arg} requires a value`);
|
|
932
|
+
}
|
|
933
|
+
return rest[index];
|
|
934
|
+
};
|
|
935
|
+
switch (arg) {
|
|
936
|
+
case "--agent":
|
|
937
|
+
parsed.agents.push(next());
|
|
938
|
+
break;
|
|
939
|
+
case "--json":
|
|
940
|
+
parsed.json = true;
|
|
941
|
+
break;
|
|
942
|
+
default:
|
|
943
|
+
throw new Error(`Unknown project mcp refresh option: ${arg}`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return parsed;
|
|
947
|
+
}
|
|
948
|
+
function parseProjectTrackerConfigureCommand(argv) {
|
|
949
|
+
const [, , , project, ...rest] = argv;
|
|
950
|
+
if (!project || project.startsWith("--")) {
|
|
951
|
+
throw new Error("project tracker configure requires a project");
|
|
952
|
+
}
|
|
953
|
+
const parsed = { project };
|
|
954
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
955
|
+
const arg = rest[index];
|
|
956
|
+
const next = () => {
|
|
957
|
+
index += 1;
|
|
958
|
+
if (index >= rest.length) {
|
|
959
|
+
throw new Error(`${arg} requires a value`);
|
|
960
|
+
}
|
|
961
|
+
return rest[index];
|
|
962
|
+
};
|
|
963
|
+
switch (arg) {
|
|
964
|
+
case "--home":
|
|
965
|
+
parsed.homePath = next();
|
|
966
|
+
break;
|
|
967
|
+
case "--provider":
|
|
968
|
+
parsed.provider = parseTrackerProvider(next(), arg);
|
|
969
|
+
break;
|
|
970
|
+
case "--host":
|
|
971
|
+
parsed.host = next();
|
|
972
|
+
break;
|
|
973
|
+
case "--repository-owner":
|
|
974
|
+
parsed.repositoryOwner = next();
|
|
975
|
+
break;
|
|
976
|
+
case "--repository-name":
|
|
977
|
+
parsed.repositoryName = next();
|
|
978
|
+
break;
|
|
979
|
+
case "--repository-id":
|
|
980
|
+
parsed.repositoryId = next();
|
|
981
|
+
break;
|
|
982
|
+
case "--project-key":
|
|
983
|
+
parsed.projectKey = next();
|
|
984
|
+
break;
|
|
985
|
+
case "--issue-type":
|
|
986
|
+
parsed.issueType = next();
|
|
987
|
+
break;
|
|
988
|
+
case "--store-path":
|
|
989
|
+
parsed.storePath = next();
|
|
990
|
+
break;
|
|
991
|
+
case "--json":
|
|
992
|
+
parsed.json = true;
|
|
993
|
+
break;
|
|
994
|
+
default:
|
|
995
|
+
throw new Error(`Unknown project tracker configure option: ${arg}`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (!parsed.provider) {
|
|
999
|
+
throw new Error("project tracker configure requires --provider");
|
|
1000
|
+
}
|
|
1001
|
+
return parsed;
|
|
1002
|
+
}
|
|
1003
|
+
function parseProjectTrackerLinkCommand(argv) {
|
|
1004
|
+
const [, , , project, ...rest] = argv;
|
|
1005
|
+
if (!project || project.startsWith("--")) {
|
|
1006
|
+
throw new Error("project tracker link requires a project");
|
|
1007
|
+
}
|
|
1008
|
+
const parsed = { project };
|
|
1009
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1010
|
+
const arg = rest[index];
|
|
1011
|
+
const next = () => {
|
|
1012
|
+
index += 1;
|
|
1013
|
+
if (index >= rest.length) {
|
|
1014
|
+
throw new Error(`${arg} requires a value`);
|
|
1015
|
+
}
|
|
1016
|
+
return rest[index];
|
|
1017
|
+
};
|
|
1018
|
+
switch (arg) {
|
|
1019
|
+
case "--home":
|
|
1020
|
+
parsed.homePath = next();
|
|
1021
|
+
break;
|
|
1022
|
+
case "--tracker-project-id":
|
|
1023
|
+
parsed.trackerProjectId = next();
|
|
1024
|
+
break;
|
|
1025
|
+
case "--json":
|
|
1026
|
+
parsed.json = true;
|
|
1027
|
+
break;
|
|
1028
|
+
default:
|
|
1029
|
+
throw new Error(`Unknown project tracker link option: ${arg}`);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
if (!parsed.trackerProjectId) {
|
|
1033
|
+
throw new Error("project tracker link requires --tracker-project-id");
|
|
1034
|
+
}
|
|
1035
|
+
return parsed;
|
|
1036
|
+
}
|
|
1037
|
+
function parseCoordinationStatusCommand(argv) {
|
|
1038
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1039
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1040
|
+
throw new Error("coordination status requires a project root");
|
|
1041
|
+
}
|
|
1042
|
+
const parsed = { projectRoot };
|
|
1043
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1044
|
+
const arg = rest[index];
|
|
1045
|
+
const next = () => {
|
|
1046
|
+
index += 1;
|
|
1047
|
+
if (index >= rest.length) {
|
|
1048
|
+
throw new Error(`${arg} requires a value`);
|
|
1049
|
+
}
|
|
1050
|
+
return rest[index];
|
|
1051
|
+
};
|
|
1052
|
+
switch (arg) {
|
|
1053
|
+
case "--component":
|
|
1054
|
+
parsed.componentId = next();
|
|
1055
|
+
break;
|
|
1056
|
+
case "--work-item":
|
|
1057
|
+
parsed.workItemId = next();
|
|
1058
|
+
break;
|
|
1059
|
+
case "--worktree":
|
|
1060
|
+
parsed.currentPath = next();
|
|
1061
|
+
break;
|
|
1062
|
+
case "--json":
|
|
1063
|
+
parsed.json = true;
|
|
1064
|
+
break;
|
|
1065
|
+
default:
|
|
1066
|
+
throw new Error(`Unknown coordination status option: ${arg}`);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return parsed;
|
|
1070
|
+
}
|
|
1071
|
+
function parseCoordinationHandoffCommand(argv) {
|
|
1072
|
+
const [, , projectRoot, workItemId, ...rest] = argv;
|
|
1073
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1074
|
+
throw new Error("coordination handoff requires a project root");
|
|
1075
|
+
}
|
|
1076
|
+
if (!workItemId || workItemId.startsWith("--")) {
|
|
1077
|
+
throw new Error("coordination handoff requires a work item id");
|
|
1078
|
+
}
|
|
1079
|
+
const parsed = {
|
|
1080
|
+
projectRoot,
|
|
1081
|
+
workItemId,
|
|
1082
|
+
changedAreas: [],
|
|
1083
|
+
decisions: [],
|
|
1084
|
+
};
|
|
1085
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1086
|
+
const arg = rest[index];
|
|
1087
|
+
const next = () => {
|
|
1088
|
+
index += 1;
|
|
1089
|
+
if (index >= rest.length) {
|
|
1090
|
+
throw new Error(`${arg} requires a value`);
|
|
1091
|
+
}
|
|
1092
|
+
return rest[index];
|
|
1093
|
+
};
|
|
1094
|
+
switch (arg) {
|
|
1095
|
+
case "--component":
|
|
1096
|
+
parsed.componentId = next();
|
|
1097
|
+
break;
|
|
1098
|
+
case "--status":
|
|
1099
|
+
parsed.status = parseNexusCoordinationHandoffStatus(next(), arg);
|
|
1100
|
+
break;
|
|
1101
|
+
case "--host":
|
|
1102
|
+
parsed.hostId = next();
|
|
1103
|
+
break;
|
|
1104
|
+
case "--agent":
|
|
1105
|
+
parsed.agentId = next();
|
|
1106
|
+
break;
|
|
1107
|
+
case "--changed-area":
|
|
1108
|
+
parsed.changedAreas?.push(next());
|
|
1109
|
+
break;
|
|
1110
|
+
case "--decision":
|
|
1111
|
+
parsed.decisions?.push(next());
|
|
1112
|
+
break;
|
|
1113
|
+
case "--verification":
|
|
1114
|
+
parsed.verificationSummary = next();
|
|
1115
|
+
break;
|
|
1116
|
+
case "--integration-preference":
|
|
1117
|
+
parsed.integrationPreference = next();
|
|
1118
|
+
break;
|
|
1119
|
+
case "--note":
|
|
1120
|
+
parsed.note = next();
|
|
1121
|
+
break;
|
|
1122
|
+
case "--worktree":
|
|
1123
|
+
parsed.currentPath = next();
|
|
1124
|
+
break;
|
|
1125
|
+
case "--json":
|
|
1126
|
+
parsed.json = true;
|
|
1127
|
+
break;
|
|
1128
|
+
default:
|
|
1129
|
+
throw new Error(`Unknown coordination handoff option: ${arg}`);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (!parsed.status) {
|
|
1133
|
+
throw new Error("coordination handoff requires --status");
|
|
1134
|
+
}
|
|
1135
|
+
return parsed;
|
|
1136
|
+
}
|
|
1137
|
+
function parseCoordinationIntegrateCommand(argv) {
|
|
1138
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1139
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1140
|
+
throw new Error("coordination integrate requires a project root");
|
|
1141
|
+
}
|
|
1142
|
+
const parsed = { projectRoot };
|
|
1143
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1144
|
+
const arg = rest[index];
|
|
1145
|
+
const next = () => {
|
|
1146
|
+
index += 1;
|
|
1147
|
+
if (index >= rest.length) {
|
|
1148
|
+
throw new Error(`${arg} requires a value`);
|
|
1149
|
+
}
|
|
1150
|
+
return rest[index];
|
|
1151
|
+
};
|
|
1152
|
+
switch (arg) {
|
|
1153
|
+
case "--component":
|
|
1154
|
+
parsed.componentId = next();
|
|
1155
|
+
break;
|
|
1156
|
+
case "--work-item":
|
|
1157
|
+
parsed.workItemId = next();
|
|
1158
|
+
break;
|
|
1159
|
+
case "--target-branch":
|
|
1160
|
+
parsed.targetBranch = next();
|
|
1161
|
+
break;
|
|
1162
|
+
case "--fetch":
|
|
1163
|
+
parsed.fetch = true;
|
|
1164
|
+
break;
|
|
1165
|
+
case "--worktree":
|
|
1166
|
+
parsed.currentPath = next();
|
|
1167
|
+
break;
|
|
1168
|
+
case "--json":
|
|
1169
|
+
parsed.json = true;
|
|
1170
|
+
break;
|
|
1171
|
+
default:
|
|
1172
|
+
throw new Error(`Unknown coordination integrate option: ${arg}`);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
return parsed;
|
|
1176
|
+
}
|
|
1177
|
+
function parseWorkItemCreateCommand(argv) {
|
|
1178
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1179
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1180
|
+
throw new Error("work-item create requires a project root");
|
|
1181
|
+
}
|
|
1182
|
+
const parsed = {
|
|
1183
|
+
projectRoot,
|
|
1184
|
+
labels: [],
|
|
1185
|
+
assignees: [],
|
|
1186
|
+
};
|
|
1187
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1188
|
+
const arg = rest[index];
|
|
1189
|
+
const next = () => {
|
|
1190
|
+
index += 1;
|
|
1191
|
+
if (index >= rest.length) {
|
|
1192
|
+
throw new Error(`${arg} requires a value`);
|
|
1193
|
+
}
|
|
1194
|
+
return rest[index];
|
|
1195
|
+
};
|
|
1196
|
+
switch (arg) {
|
|
1197
|
+
case "--component":
|
|
1198
|
+
parsed.componentId = next();
|
|
1199
|
+
break;
|
|
1200
|
+
case "--title":
|
|
1201
|
+
parsed.title = next();
|
|
1202
|
+
break;
|
|
1203
|
+
case "--description":
|
|
1204
|
+
parsed.description = next();
|
|
1205
|
+
break;
|
|
1206
|
+
case "--status":
|
|
1207
|
+
parsed.status = parseWorkStatus(next(), arg);
|
|
1208
|
+
break;
|
|
1209
|
+
case "--label":
|
|
1210
|
+
parsed.labels?.push(next());
|
|
1211
|
+
break;
|
|
1212
|
+
case "--assignee":
|
|
1213
|
+
parsed.assignees?.push(next());
|
|
1214
|
+
break;
|
|
1215
|
+
case "--milestone":
|
|
1216
|
+
parsed.milestone = next();
|
|
1217
|
+
break;
|
|
1218
|
+
case "--json":
|
|
1219
|
+
parsed.json = true;
|
|
1220
|
+
break;
|
|
1221
|
+
default:
|
|
1222
|
+
throw new Error(`Unknown work-item create option: ${arg}`);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
if (!parsed.title) {
|
|
1226
|
+
throw new Error("work-item create requires --title");
|
|
1227
|
+
}
|
|
1228
|
+
return parsed;
|
|
1229
|
+
}
|
|
1230
|
+
function parseWorkItemListCommand(argv) {
|
|
1231
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1232
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1233
|
+
throw new Error("work-item list requires a project root");
|
|
1234
|
+
}
|
|
1235
|
+
const parsed = {
|
|
1236
|
+
projectRoot,
|
|
1237
|
+
statuses: [],
|
|
1238
|
+
labels: [],
|
|
1239
|
+
assignees: [],
|
|
1240
|
+
};
|
|
1241
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1242
|
+
const arg = rest[index];
|
|
1243
|
+
const next = () => {
|
|
1244
|
+
index += 1;
|
|
1245
|
+
if (index >= rest.length) {
|
|
1246
|
+
throw new Error(`${arg} requires a value`);
|
|
1247
|
+
}
|
|
1248
|
+
return rest[index];
|
|
1249
|
+
};
|
|
1250
|
+
switch (arg) {
|
|
1251
|
+
case "--component":
|
|
1252
|
+
parsed.componentId = next();
|
|
1253
|
+
break;
|
|
1254
|
+
case "--status":
|
|
1255
|
+
parsed.statuses.push(parseWorkStatus(next(), arg));
|
|
1256
|
+
break;
|
|
1257
|
+
case "--label":
|
|
1258
|
+
parsed.labels.push(next());
|
|
1259
|
+
break;
|
|
1260
|
+
case "--assignee":
|
|
1261
|
+
parsed.assignees.push(next());
|
|
1262
|
+
break;
|
|
1263
|
+
case "--search":
|
|
1264
|
+
parsed.search = next();
|
|
1265
|
+
break;
|
|
1266
|
+
case "--limit":
|
|
1267
|
+
parsed.limit = parsePositiveInteger(next(), arg);
|
|
1268
|
+
break;
|
|
1269
|
+
case "--json":
|
|
1270
|
+
parsed.json = true;
|
|
1271
|
+
break;
|
|
1272
|
+
default:
|
|
1273
|
+
throw new Error(`Unknown work-item list option: ${arg}`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return parsed;
|
|
1277
|
+
}
|
|
1278
|
+
function parseWorkItemGetCommand(argv) {
|
|
1279
|
+
const [, , projectRoot, itemId, ...rest] = argv;
|
|
1280
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1281
|
+
throw new Error("work-item get requires a project root");
|
|
1282
|
+
}
|
|
1283
|
+
if (!itemId || itemId.startsWith("--")) {
|
|
1284
|
+
throw new Error("work-item get requires a work item id");
|
|
1285
|
+
}
|
|
1286
|
+
const parsed = { projectRoot, itemId };
|
|
1287
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1288
|
+
const arg = rest[index];
|
|
1289
|
+
const next = () => {
|
|
1290
|
+
index += 1;
|
|
1291
|
+
if (index >= rest.length) {
|
|
1292
|
+
throw new Error(`${arg} requires a value`);
|
|
1293
|
+
}
|
|
1294
|
+
return rest[index];
|
|
1295
|
+
};
|
|
1296
|
+
switch (arg) {
|
|
1297
|
+
case "--component":
|
|
1298
|
+
parsed.componentId = next();
|
|
1299
|
+
break;
|
|
1300
|
+
case "--json":
|
|
1301
|
+
parsed.json = true;
|
|
1302
|
+
break;
|
|
1303
|
+
default:
|
|
1304
|
+
throw new Error(`Unknown work-item get option: ${arg}`);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return parsed;
|
|
1308
|
+
}
|
|
1309
|
+
function parseWorkItemUpdateCommand(argv) {
|
|
1310
|
+
const [, , projectRoot, itemId, ...rest] = argv;
|
|
1311
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1312
|
+
throw new Error("work-item update requires a project root");
|
|
1313
|
+
}
|
|
1314
|
+
if (!itemId || itemId.startsWith("--")) {
|
|
1315
|
+
throw new Error("work-item update requires a work item id");
|
|
1316
|
+
}
|
|
1317
|
+
const parsed = {
|
|
1318
|
+
projectRoot,
|
|
1319
|
+
itemId,
|
|
1320
|
+
patch: {},
|
|
1321
|
+
};
|
|
1322
|
+
let replaceLabels;
|
|
1323
|
+
let replaceAssignees;
|
|
1324
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1325
|
+
const arg = rest[index];
|
|
1326
|
+
const next = () => {
|
|
1327
|
+
index += 1;
|
|
1328
|
+
if (index >= rest.length) {
|
|
1329
|
+
throw new Error(`${arg} requires a value`);
|
|
1330
|
+
}
|
|
1331
|
+
return rest[index];
|
|
1332
|
+
};
|
|
1333
|
+
switch (arg) {
|
|
1334
|
+
case "--component":
|
|
1335
|
+
parsed.componentId = next();
|
|
1336
|
+
break;
|
|
1337
|
+
case "--title":
|
|
1338
|
+
parsed.patch.title = next();
|
|
1339
|
+
break;
|
|
1340
|
+
case "--description":
|
|
1341
|
+
if (parsed.patch.description === null) {
|
|
1342
|
+
throw new Error("--description conflicts with --clear-description");
|
|
1343
|
+
}
|
|
1344
|
+
parsed.patch.description = next();
|
|
1345
|
+
break;
|
|
1346
|
+
case "--clear-description":
|
|
1347
|
+
if (parsed.patch.description !== undefined) {
|
|
1348
|
+
throw new Error("--clear-description conflicts with --description");
|
|
1349
|
+
}
|
|
1350
|
+
parsed.patch.description = null;
|
|
1351
|
+
break;
|
|
1352
|
+
case "--status":
|
|
1353
|
+
parsed.patch.status = parseWorkStatus(next(), arg);
|
|
1354
|
+
break;
|
|
1355
|
+
case "--label":
|
|
1356
|
+
if (replaceLabels === undefined) {
|
|
1357
|
+
replaceLabels = [];
|
|
1358
|
+
}
|
|
1359
|
+
replaceLabels.push(next());
|
|
1360
|
+
break;
|
|
1361
|
+
case "--clear-labels":
|
|
1362
|
+
if (replaceLabels && replaceLabels.length > 0) {
|
|
1363
|
+
throw new Error("--clear-labels conflicts with --label");
|
|
1364
|
+
}
|
|
1365
|
+
replaceLabels = [];
|
|
1366
|
+
break;
|
|
1367
|
+
case "--assignee":
|
|
1368
|
+
if (replaceAssignees === undefined) {
|
|
1369
|
+
replaceAssignees = [];
|
|
1370
|
+
}
|
|
1371
|
+
replaceAssignees.push(next());
|
|
1372
|
+
break;
|
|
1373
|
+
case "--clear-assignees":
|
|
1374
|
+
if (replaceAssignees && replaceAssignees.length > 0) {
|
|
1375
|
+
throw new Error("--clear-assignees conflicts with --assignee");
|
|
1376
|
+
}
|
|
1377
|
+
replaceAssignees = [];
|
|
1378
|
+
break;
|
|
1379
|
+
case "--milestone":
|
|
1380
|
+
if (parsed.patch.milestone === null) {
|
|
1381
|
+
throw new Error("--milestone conflicts with --clear-milestone");
|
|
1382
|
+
}
|
|
1383
|
+
parsed.patch.milestone = next();
|
|
1384
|
+
break;
|
|
1385
|
+
case "--clear-milestone":
|
|
1386
|
+
if (parsed.patch.milestone !== undefined) {
|
|
1387
|
+
throw new Error("--clear-milestone conflicts with --milestone");
|
|
1388
|
+
}
|
|
1389
|
+
parsed.patch.milestone = null;
|
|
1390
|
+
break;
|
|
1391
|
+
case "--json":
|
|
1392
|
+
parsed.json = true;
|
|
1393
|
+
break;
|
|
1394
|
+
default:
|
|
1395
|
+
throw new Error(`Unknown work-item update option: ${arg}`);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
if (replaceLabels !== undefined) {
|
|
1399
|
+
parsed.patch.labels = replaceLabels;
|
|
1400
|
+
}
|
|
1401
|
+
if (replaceAssignees !== undefined) {
|
|
1402
|
+
parsed.patch.assignees = replaceAssignees;
|
|
1403
|
+
}
|
|
1404
|
+
if (Object.keys(parsed.patch).length === 0) {
|
|
1405
|
+
throw new Error("work-item update requires at least one field to update");
|
|
1406
|
+
}
|
|
1407
|
+
return parsed;
|
|
1408
|
+
}
|
|
1409
|
+
function parseWorkItemCommentCommand(argv) {
|
|
1410
|
+
const [, , projectRoot, itemId, ...rest] = argv;
|
|
1411
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1412
|
+
throw new Error("work-item comment requires a project root");
|
|
1413
|
+
}
|
|
1414
|
+
if (!itemId || itemId.startsWith("--")) {
|
|
1415
|
+
throw new Error("work-item comment requires a work item id");
|
|
1416
|
+
}
|
|
1417
|
+
const parsed = {
|
|
1418
|
+
projectRoot,
|
|
1419
|
+
itemId,
|
|
1420
|
+
};
|
|
1421
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1422
|
+
const arg = rest[index];
|
|
1423
|
+
const next = () => {
|
|
1424
|
+
index += 1;
|
|
1425
|
+
if (index >= rest.length) {
|
|
1426
|
+
throw new Error(`${arg} requires a value`);
|
|
1427
|
+
}
|
|
1428
|
+
return rest[index];
|
|
1429
|
+
};
|
|
1430
|
+
switch (arg) {
|
|
1431
|
+
case "--component":
|
|
1432
|
+
parsed.componentId = next();
|
|
1433
|
+
break;
|
|
1434
|
+
case "--body":
|
|
1435
|
+
parsed.body = next();
|
|
1436
|
+
break;
|
|
1437
|
+
case "--json":
|
|
1438
|
+
parsed.json = true;
|
|
1439
|
+
break;
|
|
1440
|
+
default:
|
|
1441
|
+
throw new Error(`Unknown work-item comment option: ${arg}`);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
if (!parsed.body) {
|
|
1445
|
+
throw new Error("work-item comment requires --body");
|
|
1446
|
+
}
|
|
1447
|
+
return parsed;
|
|
1448
|
+
}
|
|
1449
|
+
function parseAutomationEnqueueCommand(argv) {
|
|
1450
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1451
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1452
|
+
throw new Error("automation enqueue requires a project root");
|
|
1453
|
+
}
|
|
1454
|
+
const parsed = {
|
|
1455
|
+
projectRoot,
|
|
1456
|
+
labels: [],
|
|
1457
|
+
assignees: [],
|
|
1458
|
+
};
|
|
1459
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1460
|
+
const arg = rest[index];
|
|
1461
|
+
const next = () => {
|
|
1462
|
+
index += 1;
|
|
1463
|
+
if (index >= rest.length) {
|
|
1464
|
+
throw new Error(`${arg} requires a value`);
|
|
1465
|
+
}
|
|
1466
|
+
return rest[index];
|
|
1467
|
+
};
|
|
1468
|
+
switch (arg) {
|
|
1469
|
+
case "--title":
|
|
1470
|
+
parsed.title = next();
|
|
1471
|
+
break;
|
|
1472
|
+
case "--description":
|
|
1473
|
+
parsed.description = next();
|
|
1474
|
+
break;
|
|
1475
|
+
case "--status":
|
|
1476
|
+
parsed.status = parseWorkStatus(next(), arg);
|
|
1477
|
+
break;
|
|
1478
|
+
case "--label":
|
|
1479
|
+
parsed.labels?.push(next());
|
|
1480
|
+
break;
|
|
1481
|
+
case "--assignee":
|
|
1482
|
+
parsed.assignees?.push(next());
|
|
1483
|
+
break;
|
|
1484
|
+
case "--milestone":
|
|
1485
|
+
parsed.milestone = next();
|
|
1486
|
+
break;
|
|
1487
|
+
case "--json":
|
|
1488
|
+
parsed.json = true;
|
|
1489
|
+
break;
|
|
1490
|
+
default:
|
|
1491
|
+
throw new Error(`Unknown automation enqueue option: ${arg}`);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
if (!parsed.title) {
|
|
1495
|
+
throw new Error("automation enqueue requires --title");
|
|
1496
|
+
}
|
|
1497
|
+
return parsed;
|
|
1498
|
+
}
|
|
1499
|
+
function parseAutomationTargetCycleListCommand(argv) {
|
|
1500
|
+
const [, , , projectRoot, ...rest] = argv;
|
|
1501
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1502
|
+
throw new Error("automation target-cycle list requires a project root");
|
|
1503
|
+
}
|
|
1504
|
+
const parsed = { projectRoot };
|
|
1505
|
+
for (const arg of rest) {
|
|
1506
|
+
switch (arg) {
|
|
1507
|
+
case "--json":
|
|
1508
|
+
parsed.json = true;
|
|
1509
|
+
break;
|
|
1510
|
+
default:
|
|
1511
|
+
throw new Error(`Unknown automation target-cycle list option: ${arg}`);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return parsed;
|
|
1515
|
+
}
|
|
1516
|
+
function parseAutomationTargetCycleRecordCommand(argv) {
|
|
1517
|
+
const [, , , projectRoot, ...rest] = argv;
|
|
1518
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1519
|
+
throw new Error("automation target-cycle record requires a project root");
|
|
1520
|
+
}
|
|
1521
|
+
const parsed = {
|
|
1522
|
+
projectRoot,
|
|
1523
|
+
workItems: [],
|
|
1524
|
+
blockers: [],
|
|
1525
|
+
notes: [],
|
|
1526
|
+
};
|
|
1527
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1528
|
+
const arg = rest[index];
|
|
1529
|
+
const next = () => {
|
|
1530
|
+
index += 1;
|
|
1531
|
+
if (index >= rest.length) {
|
|
1532
|
+
throw new Error(`${arg} requires a value`);
|
|
1533
|
+
}
|
|
1534
|
+
return rest[index];
|
|
1535
|
+
};
|
|
1536
|
+
switch (arg) {
|
|
1537
|
+
case "--cycle-id":
|
|
1538
|
+
parsed.cycleId = next();
|
|
1539
|
+
break;
|
|
1540
|
+
case "--run-id":
|
|
1541
|
+
parsed.runId = next();
|
|
1542
|
+
break;
|
|
1543
|
+
case "--status":
|
|
1544
|
+
parsed.status = parseTargetCycleStatus(next(), arg);
|
|
1545
|
+
break;
|
|
1546
|
+
case "--summary":
|
|
1547
|
+
parsed.summary = next();
|
|
1548
|
+
break;
|
|
1549
|
+
case "--eligible-work-items":
|
|
1550
|
+
parsed.eligibleWorkItemCount = parseNonNegativeInteger(next(), arg);
|
|
1551
|
+
break;
|
|
1552
|
+
case "--work-item":
|
|
1553
|
+
parsed.workItems?.push(parseTargetCycleWorkItem(next(), arg));
|
|
1554
|
+
break;
|
|
1555
|
+
case "--work-item-status":
|
|
1556
|
+
lastParsedTargetCycleWorkItem(parsed, arg).cycleStatus =
|
|
1557
|
+
parseTargetCycleWorkItemStatus(next(), arg);
|
|
1558
|
+
break;
|
|
1559
|
+
case "--work-item-agent-profile":
|
|
1560
|
+
lastParsedTargetCycleWorkItem(parsed, arg).agentProfileId = next();
|
|
1561
|
+
break;
|
|
1562
|
+
case "--work-item-note":
|
|
1563
|
+
lastParsedTargetCycleWorkItem(parsed, arg).notes = next();
|
|
1564
|
+
break;
|
|
1565
|
+
case "--blocker":
|
|
1566
|
+
parsed.blockers?.push(next());
|
|
1567
|
+
break;
|
|
1568
|
+
case "--note":
|
|
1569
|
+
parsed.notes?.push(next());
|
|
1570
|
+
break;
|
|
1571
|
+
case "--json":
|
|
1572
|
+
parsed.json = true;
|
|
1573
|
+
break;
|
|
1574
|
+
default:
|
|
1575
|
+
throw new Error(`Unknown automation target-cycle record option: ${arg}`);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
if (!parsed.status) {
|
|
1579
|
+
throw new Error("automation target-cycle record requires --status");
|
|
1580
|
+
}
|
|
1581
|
+
return parsed;
|
|
1582
|
+
}
|
|
1583
|
+
function parseAutomationTargetReportCommand(argv) {
|
|
1584
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1585
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1586
|
+
throw new Error("automation target-report requires a project root");
|
|
1587
|
+
}
|
|
1588
|
+
const parsed = { projectRoot };
|
|
1589
|
+
for (const arg of rest) {
|
|
1590
|
+
switch (arg) {
|
|
1591
|
+
case "--json":
|
|
1592
|
+
parsed.json = true;
|
|
1593
|
+
break;
|
|
1594
|
+
default:
|
|
1595
|
+
throw new Error(`Unknown automation target-report option: ${arg}`);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return parsed;
|
|
1599
|
+
}
|
|
1600
|
+
function parseAutomationRunOnceCommand(argv) {
|
|
1601
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1602
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1603
|
+
throw new Error("automation run-once requires a project root");
|
|
1604
|
+
}
|
|
1605
|
+
const parsed = { projectRoot };
|
|
1606
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1607
|
+
const arg = rest[index];
|
|
1608
|
+
const next = () => {
|
|
1609
|
+
index += 1;
|
|
1610
|
+
if (index >= rest.length) {
|
|
1611
|
+
throw new Error(`${arg} requires a value`);
|
|
1612
|
+
}
|
|
1613
|
+
return rest[index];
|
|
1614
|
+
};
|
|
1615
|
+
switch (arg) {
|
|
1616
|
+
case "--command":
|
|
1617
|
+
parsed.command = next();
|
|
1618
|
+
break;
|
|
1619
|
+
case "--run-id":
|
|
1620
|
+
parsed.runId = next();
|
|
1621
|
+
break;
|
|
1622
|
+
case "--owner":
|
|
1623
|
+
parsed.owner = next();
|
|
1624
|
+
break;
|
|
1625
|
+
case "--branch":
|
|
1626
|
+
parsed.branchName = next();
|
|
1627
|
+
break;
|
|
1628
|
+
case "--worktree-name":
|
|
1629
|
+
parsed.worktreeName = next();
|
|
1630
|
+
break;
|
|
1631
|
+
case "--base-ref":
|
|
1632
|
+
parsed.baseRef = next();
|
|
1633
|
+
break;
|
|
1634
|
+
case "--timeout-ms":
|
|
1635
|
+
parsed.timeoutMs = parsePositiveInteger(next(), arg);
|
|
1636
|
+
break;
|
|
1637
|
+
case "--full":
|
|
1638
|
+
parsed.runFullVerification = true;
|
|
1639
|
+
break;
|
|
1640
|
+
case "--json":
|
|
1641
|
+
parsed.json = true;
|
|
1642
|
+
break;
|
|
1643
|
+
default:
|
|
1644
|
+
throw new Error(`Unknown automation run-once option: ${arg}`);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
return parsed;
|
|
1648
|
+
}
|
|
1649
|
+
function parseAutomationStatusCommand(argv) {
|
|
1650
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1651
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1652
|
+
throw new Error("automation status requires a project root");
|
|
1653
|
+
}
|
|
1654
|
+
const parsed = { projectRoot };
|
|
1655
|
+
for (const arg of rest) {
|
|
1656
|
+
switch (arg) {
|
|
1657
|
+
case "--json":
|
|
1658
|
+
parsed.json = true;
|
|
1659
|
+
break;
|
|
1660
|
+
default:
|
|
1661
|
+
throw new Error(`Unknown automation status option: ${arg}`);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return parsed;
|
|
1665
|
+
}
|
|
1666
|
+
function parseAutomationEligibleWorkCommand(argv) {
|
|
1667
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1668
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1669
|
+
throw new Error("automation eligible-work requires a project root");
|
|
1670
|
+
}
|
|
1671
|
+
const parsed = { projectRoot };
|
|
1672
|
+
for (const arg of rest) {
|
|
1673
|
+
switch (arg) {
|
|
1674
|
+
case "--json":
|
|
1675
|
+
parsed.json = true;
|
|
1676
|
+
break;
|
|
1677
|
+
default:
|
|
1678
|
+
throw new Error(`Unknown automation eligible-work option: ${arg}`);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
return parsed;
|
|
1682
|
+
}
|
|
1683
|
+
function parseAutomationAgentProfilesCommand(argv) {
|
|
1684
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1685
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1686
|
+
throw new Error("automation agent-profiles requires a project root");
|
|
1687
|
+
}
|
|
1688
|
+
const parsed = { projectRoot };
|
|
1689
|
+
for (const arg of rest) {
|
|
1690
|
+
switch (arg) {
|
|
1691
|
+
case "--json":
|
|
1692
|
+
parsed.json = true;
|
|
1693
|
+
break;
|
|
1694
|
+
default:
|
|
1695
|
+
throw new Error(`Unknown automation agent-profiles option: ${arg}`);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
return parsed;
|
|
1699
|
+
}
|
|
1700
|
+
function parseAutomationScheduleCommand(argv) {
|
|
1701
|
+
const [, , projectRoot, ...rest] = argv;
|
|
1702
|
+
if (!projectRoot || projectRoot.startsWith("--")) {
|
|
1703
|
+
throw new Error("automation schedule requires a project root");
|
|
1704
|
+
}
|
|
1705
|
+
const parsed = { projectRoot };
|
|
1706
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1707
|
+
const arg = rest[index];
|
|
1708
|
+
const next = () => {
|
|
1709
|
+
index += 1;
|
|
1710
|
+
if (index >= rest.length) {
|
|
1711
|
+
throw new Error(`${arg} requires a value`);
|
|
1712
|
+
}
|
|
1713
|
+
return rest[index];
|
|
1714
|
+
};
|
|
1715
|
+
switch (arg) {
|
|
1716
|
+
case "--command":
|
|
1717
|
+
parsed.command = next();
|
|
1718
|
+
break;
|
|
1719
|
+
case "--owner":
|
|
1720
|
+
parsed.owner = next();
|
|
1721
|
+
break;
|
|
1722
|
+
case "--base-ref":
|
|
1723
|
+
parsed.baseRef = next();
|
|
1724
|
+
break;
|
|
1725
|
+
case "--interval-ms":
|
|
1726
|
+
parsed.intervalMs = parsePositiveInteger(next(), arg);
|
|
1727
|
+
break;
|
|
1728
|
+
case "--max-ticks":
|
|
1729
|
+
parsed.maxTicks = parsePositiveInteger(next(), arg);
|
|
1730
|
+
break;
|
|
1731
|
+
case "--max-runs":
|
|
1732
|
+
parsed.maxRuns = parsePositiveInteger(next(), arg);
|
|
1733
|
+
break;
|
|
1734
|
+
case "--run-id-prefix":
|
|
1735
|
+
parsed.runIdPrefix = next();
|
|
1736
|
+
break;
|
|
1737
|
+
case "--timeout-ms":
|
|
1738
|
+
parsed.timeoutMs = parsePositiveInteger(next(), arg);
|
|
1739
|
+
break;
|
|
1740
|
+
case "--full":
|
|
1741
|
+
parsed.runFullVerification = true;
|
|
1742
|
+
break;
|
|
1743
|
+
case "--json":
|
|
1744
|
+
parsed.json = true;
|
|
1745
|
+
break;
|
|
1746
|
+
default:
|
|
1747
|
+
throw new Error(`Unknown automation schedule option: ${arg}`);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return parsed;
|
|
1751
|
+
}
|
|
1752
|
+
function printHomeInitResult(result, parsed, stdout) {
|
|
1753
|
+
const payload = { ok: true, ...result };
|
|
1754
|
+
if (parsed.json) {
|
|
1755
|
+
writeJson(stdout, payload);
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
writeLine(stdout, "DevNexus home initialized.");
|
|
1759
|
+
writeLine(stdout, ` Home: ${result.homePath}`);
|
|
1760
|
+
writeLine(stdout, ` Config: ${result.configPath}`);
|
|
1761
|
+
writeLine(stdout, ` Projects root: ${result.config.paths.projectsRoot}`);
|
|
1762
|
+
}
|
|
1763
|
+
function printProjectCreateResult(result, parsed, stdout) {
|
|
1764
|
+
const payload = { ok: true, ...result };
|
|
1765
|
+
if (parsed.json) {
|
|
1766
|
+
writeJson(stdout, payload);
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
writeLine(stdout, "DevNexus project created.");
|
|
1770
|
+
printProjectStatusText(result.reference, stdout);
|
|
1771
|
+
writeLine(stdout, ` Config: ${result.projectConfigPath}`);
|
|
1772
|
+
}
|
|
1773
|
+
function printProjectImportResult(result, parsed, stdout) {
|
|
1774
|
+
const payload = { ok: true, ...result };
|
|
1775
|
+
if (parsed.json) {
|
|
1776
|
+
writeJson(stdout, payload);
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
writeLine(stdout, "DevNexus project imported.");
|
|
1780
|
+
printProjectStatusText(result.reference, stdout);
|
|
1781
|
+
writeLine(stdout, ` Config: ${result.projectConfigPath}`);
|
|
1782
|
+
}
|
|
1783
|
+
function printProjectListResult(result, parsed, stdout) {
|
|
1784
|
+
const payload = { ok: true, ...result };
|
|
1785
|
+
if (parsed.json) {
|
|
1786
|
+
writeJson(stdout, payload);
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
writeLine(stdout, `DevNexus projects: ${result.projects.length}`);
|
|
1790
|
+
for (const project of result.projects) {
|
|
1791
|
+
writeLine(stdout, ` ${project.id} ${project.projectRoot}`);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
function printProjectStatusResult(project, parsed, stdout) {
|
|
1795
|
+
const payload = { ok: true, project };
|
|
1796
|
+
if (parsed.json) {
|
|
1797
|
+
writeJson(stdout, payload);
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
writeLine(stdout, `DevNexus project ${project.id}.`);
|
|
1801
|
+
printProjectStatusText(project, stdout);
|
|
1802
|
+
writeLine(stdout, ` Work tracking: ${project.workTracking?.provider ?? "not configured"}`);
|
|
1803
|
+
writeLine(stdout, ` Components: ${project.components.length}`);
|
|
1804
|
+
for (const component of project.components) {
|
|
1805
|
+
writeLine(stdout, ` ${component.id} [${component.role}] ${component.sourceRoot}`);
|
|
1806
|
+
}
|
|
1807
|
+
writeLine(stdout, ` Config exists: ${project.projectConfigExists}`);
|
|
1808
|
+
writeLine(stdout, ` Worktrees root: ${project.worktreesRoot}`);
|
|
1809
|
+
}
|
|
1810
|
+
function printProjectMcpRefreshResult(result, parsed, stdout) {
|
|
1811
|
+
const payload = { ok: true, ...result };
|
|
1812
|
+
if (parsed.json) {
|
|
1813
|
+
writeJson(stdout, payload);
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
writeLine(stdout, "DevNexus MCP agent config refreshed.");
|
|
1817
|
+
writeLine(stdout, ` Agent targets: ${result.agentTargets.length}`);
|
|
1818
|
+
for (const target of result.agentTargets) {
|
|
1819
|
+
writeLine(stdout, ` ${target.agent}: ${target.configPath} (${target.serverName})`);
|
|
1820
|
+
}
|
|
1821
|
+
if (result.gitExcludeEntries.length > 0) {
|
|
1822
|
+
writeLine(stdout, ` Git exclude entries: ${result.gitExcludeEntries.length}`);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
function printProjectTrackerConfigureResult(result, parsed, stdout) {
|
|
1826
|
+
const payload = { ok: true, ...result };
|
|
1827
|
+
if (parsed.json) {
|
|
1828
|
+
writeJson(stdout, payload);
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
writeLine(stdout, "DevNexus project tracker configured.");
|
|
1832
|
+
writeLine(stdout, ` Project: ${result.project.id}`);
|
|
1833
|
+
writeLine(stdout, ` Provider: ${result.workTracking.provider}`);
|
|
1834
|
+
}
|
|
1835
|
+
function printProjectTrackerLinkResult(result, parsed, stdout) {
|
|
1836
|
+
const payload = { ok: true, ...result };
|
|
1837
|
+
if (parsed.json) {
|
|
1838
|
+
writeJson(stdout, payload);
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
writeLine(stdout, "DevNexus project tracker linked.");
|
|
1842
|
+
writeLine(stdout, ` Project: ${result.project.id}`);
|
|
1843
|
+
writeLine(stdout, ` Tracker project: ${result.vibeKanbanProjectId}`);
|
|
1844
|
+
}
|
|
1845
|
+
function printCoordinationStatusResult(status, parsed, stdout) {
|
|
1846
|
+
const payload = { ok: true, status };
|
|
1847
|
+
if (parsed.json) {
|
|
1848
|
+
writeJson(stdout, payload);
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
writeLine(stdout, "DevNexus coordination status.");
|
|
1852
|
+
writeLine(stdout, ` Project: ${status.project.id}`);
|
|
1853
|
+
writeLine(stdout, ` Component: ${status.component.id}`);
|
|
1854
|
+
if (status.workItem) {
|
|
1855
|
+
writeLine(stdout, ` Work item: ${status.workItem.id} ${status.workItem.title}`);
|
|
1856
|
+
}
|
|
1857
|
+
writeLine(stdout, ` Repository: ${status.git.repositoryPath ?? "not resolved"}`);
|
|
1858
|
+
writeLine(stdout, ` Branch: ${status.git.branch ?? "unknown"}`);
|
|
1859
|
+
writeLine(stdout, ` Dirty: ${status.git.dirty === null ? "unknown" : String(status.git.dirty)}`);
|
|
1860
|
+
writeLine(stdout, ` Pushed: ${status.git.pushed === null ? "unknown" : String(status.git.pushed)}`);
|
|
1861
|
+
writeLine(stdout, ` Handoffs: ${status.handoffs.records.length}`);
|
|
1862
|
+
writeLine(stdout, ` Next action: ${status.nextAction}`);
|
|
1863
|
+
}
|
|
1864
|
+
function printCoordinationHandoffResult(result, parsed, stdout) {
|
|
1865
|
+
const payload = { ok: true, ...result };
|
|
1866
|
+
if (parsed.json) {
|
|
1867
|
+
writeJson(stdout, payload);
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
writeLine(stdout, "DevNexus coordination handoff recorded.");
|
|
1871
|
+
writeLine(stdout, ` Project: ${result.project.id}`);
|
|
1872
|
+
writeLine(stdout, ` Component: ${result.component.id}`);
|
|
1873
|
+
writeLine(stdout, ` Work item: ${result.record.workItemId}`);
|
|
1874
|
+
writeLine(stdout, ` Status: ${result.record.status}`);
|
|
1875
|
+
writeLine(stdout, ` Branch: ${result.record.branch ?? "unknown"}`);
|
|
1876
|
+
writeLine(stdout, ` Comment: ${result.comment.id}`);
|
|
1877
|
+
}
|
|
1878
|
+
function printCoordinationIntegrationPlan(plan, parsed, stdout) {
|
|
1879
|
+
const payload = { ok: true, plan };
|
|
1880
|
+
if (parsed.json) {
|
|
1881
|
+
writeJson(stdout, payload);
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
writeLine(stdout, "DevNexus coordination integration plan.");
|
|
1885
|
+
writeLine(stdout, ` Project: ${plan.project.id}`);
|
|
1886
|
+
writeLine(stdout, ` Component: ${plan.component.id}`);
|
|
1887
|
+
writeLine(stdout, ` Target: ${plan.target.ref}`);
|
|
1888
|
+
writeLine(stdout, ` Handoff branches: ${plan.branches.length}`);
|
|
1889
|
+
writeLine(stdout, ` Conflicts: ${plan.branches.filter((branch) => branch.merge.status === "conflict").length}`);
|
|
1890
|
+
writeLine(stdout, ` Decision conflicts: ${plan.decisionConflicts.length}`);
|
|
1891
|
+
if (plan.suggestedOrder.length > 0) {
|
|
1892
|
+
writeLine(stdout, " Suggested order:");
|
|
1893
|
+
for (const step of plan.suggestedOrder) {
|
|
1894
|
+
writeLine(stdout, ` ${step.direction}`);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
writeLine(stdout, ` Next action: ${plan.nextAction}`);
|
|
1898
|
+
}
|
|
1899
|
+
function printWorkItemCreateResult(item, parsed, stdout) {
|
|
1900
|
+
const payload = { ok: true, workItem: item };
|
|
1901
|
+
if (parsed.json) {
|
|
1902
|
+
writeJson(stdout, payload);
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
writeLine(stdout, "DevNexus work item created.");
|
|
1906
|
+
writeLine(stdout, ` Id: ${item.id}`);
|
|
1907
|
+
writeLine(stdout, ` Title: ${item.title}`);
|
|
1908
|
+
writeLine(stdout, ` Status: ${item.status}`);
|
|
1909
|
+
}
|
|
1910
|
+
function printWorkItemListResult(items, parsed, stdout) {
|
|
1911
|
+
const payload = { ok: true, workItems: items };
|
|
1912
|
+
if (parsed.json) {
|
|
1913
|
+
writeJson(stdout, payload);
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
writeLine(stdout, `DevNexus work items: ${items.length}`);
|
|
1917
|
+
for (const item of items) {
|
|
1918
|
+
writeLine(stdout, ` ${item.id} [${item.status}] ${item.title}`);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
function printWorkItemGetResult(item, parsed, stdout) {
|
|
1922
|
+
const payload = { ok: true, workItem: item };
|
|
1923
|
+
if (parsed.json) {
|
|
1924
|
+
writeJson(stdout, payload);
|
|
1925
|
+
return;
|
|
1926
|
+
}
|
|
1927
|
+
writeLine(stdout, `DevNexus work item ${item.id}.`);
|
|
1928
|
+
writeLine(stdout, ` Title: ${item.title}`);
|
|
1929
|
+
writeLine(stdout, ` Status: ${item.status}`);
|
|
1930
|
+
}
|
|
1931
|
+
function printWorkItemUpdateResult(item, parsed, stdout) {
|
|
1932
|
+
const payload = { ok: true, workItem: item };
|
|
1933
|
+
if (parsed.json) {
|
|
1934
|
+
writeJson(stdout, payload);
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
writeLine(stdout, "DevNexus work item updated.");
|
|
1938
|
+
writeLine(stdout, ` Id: ${item.id}`);
|
|
1939
|
+
writeLine(stdout, ` Title: ${item.title}`);
|
|
1940
|
+
writeLine(stdout, ` Status: ${item.status}`);
|
|
1941
|
+
}
|
|
1942
|
+
function printWorkItemCommentResult(comment, parsed, stdout) {
|
|
1943
|
+
const payload = { ok: true, comment };
|
|
1944
|
+
if (parsed.json) {
|
|
1945
|
+
writeJson(stdout, payload);
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
writeLine(stdout, "DevNexus work item comment added.");
|
|
1949
|
+
writeLine(stdout, ` Id: ${comment.id}`);
|
|
1950
|
+
}
|
|
1951
|
+
function printAutomationScheduleTick(tick, stdout) {
|
|
1952
|
+
const runStatus = tick.run ? ` run=${tick.run.status}` : "";
|
|
1953
|
+
const wait = tick.waitMs === null ? "" : ` waitMs=${tick.waitMs}`;
|
|
1954
|
+
writeLine(stdout, `DevNexus scheduler tick ${tick.index}: ${tick.status.status} action=${tick.action}${runStatus}${wait}`);
|
|
1955
|
+
}
|
|
1956
|
+
function printAutomationEnqueueResult(result, parsed, stdout) {
|
|
1957
|
+
const payload = { ok: true, ...result };
|
|
1958
|
+
if (parsed.json) {
|
|
1959
|
+
writeJson(stdout, payload);
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
writeLine(stdout, "DevNexus automation work item enqueued.");
|
|
1963
|
+
writeLine(stdout, ` Project: ${projectLabel(result.projectConfig)}`);
|
|
1964
|
+
writeLine(stdout, ` Id: ${result.workItem.id}`);
|
|
1965
|
+
writeLine(stdout, ` Title: ${result.workItem.title}`);
|
|
1966
|
+
writeLine(stdout, ` Status: ${result.workItem.status}`);
|
|
1967
|
+
}
|
|
1968
|
+
function printAutomationTargetCycleListResult(result, parsed, stdout) {
|
|
1969
|
+
const payload = { ok: true, ...result };
|
|
1970
|
+
if (parsed.json) {
|
|
1971
|
+
writeJson(stdout, payload);
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
writeLine(stdout, "DevNexus target cycles.");
|
|
1975
|
+
writeLine(stdout, ` Project: ${projectLabel(result.projectConfig)}`);
|
|
1976
|
+
writeLine(stdout, ` Cycles: ${result.ledger.cycles.length}`);
|
|
1977
|
+
const lastCycle = result.ledger.cycles.at(-1);
|
|
1978
|
+
if (lastCycle) {
|
|
1979
|
+
writeLine(stdout, ` Last cycle: ${lastCycle.id} ${lastCycle.status}`);
|
|
1980
|
+
if (lastCycle.summary) {
|
|
1981
|
+
writeLine(stdout, ` Summary: ${lastCycle.summary}`);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
function printAutomationTargetCycleRecordResult(result, parsed, stdout) {
|
|
1986
|
+
const payload = { ok: true, ...result };
|
|
1987
|
+
if (parsed.json) {
|
|
1988
|
+
writeJson(stdout, payload);
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
writeLine(stdout, "DevNexus target cycle recorded.");
|
|
1992
|
+
writeLine(stdout, ` Project: ${projectLabel(result.projectConfig)}`);
|
|
1993
|
+
writeLine(stdout, ` Cycle: ${result.record.id}`);
|
|
1994
|
+
writeLine(stdout, ` Status: ${result.record.status}`);
|
|
1995
|
+
writeLine(stdout, ` Cycles recorded: ${result.ledger.cycles.length}`);
|
|
1996
|
+
}
|
|
1997
|
+
function printAutomationTargetReportResult(result, parsed, stdout) {
|
|
1998
|
+
const payload = { ok: true, report: result };
|
|
1999
|
+
if (parsed.json) {
|
|
2000
|
+
writeJson(stdout, payload);
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
writeLine(stdout, `DevNexus target report: ${result.status}.`);
|
|
2004
|
+
writeLine(stdout, ` Project: ${result.project.id} (${result.project.name})`);
|
|
2005
|
+
writeLine(stdout, ` Reason: ${result.statusReason}`);
|
|
2006
|
+
if (result.target?.objective) {
|
|
2007
|
+
writeLine(stdout, ` Objective: ${result.target.objective}`);
|
|
2008
|
+
}
|
|
2009
|
+
if (result.cycleSummary) {
|
|
2010
|
+
writeLine(stdout, ` Target cycles: ${result.cycleSummary.cycleCount}`);
|
|
2011
|
+
}
|
|
2012
|
+
if (result.runSummary) {
|
|
2013
|
+
writeLine(stdout, ` Automation runs: ${result.runSummary.runCount}`);
|
|
2014
|
+
}
|
|
2015
|
+
writeLine(stdout, ` Relaunch decision: ${result.relaunchDecision.type} (${result.relaunchDecision.reason})`);
|
|
2016
|
+
if (result.workItemSummary) {
|
|
2017
|
+
writeLine(stdout, ` Work item refs: ${result.workItemSummary.uniqueReferences.length}`);
|
|
2018
|
+
}
|
|
2019
|
+
if (result.blockers.length > 0) {
|
|
2020
|
+
writeLine(stdout, ` Blockers: ${result.blockers.length}`);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
function printAutomationScheduleResult(result, parsed, stdout) {
|
|
2024
|
+
const payload = { ok: true, ...result };
|
|
2025
|
+
if (parsed.json) {
|
|
2026
|
+
writeJson(stdout, payload);
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
writeLine(stdout, "DevNexus automation scheduler stopped.");
|
|
2030
|
+
writeLine(stdout, ` Reason: ${result.stoppedReason}`);
|
|
2031
|
+
writeLine(stdout, ` Ticks: ${result.ticks.length}`);
|
|
2032
|
+
writeLine(stdout, ` Runs: ${result.runs.length}`);
|
|
2033
|
+
const lastTick = result.ticks.at(-1);
|
|
2034
|
+
if (lastTick) {
|
|
2035
|
+
writeLine(stdout, ` Last status: ${lastTick.status.status}`);
|
|
2036
|
+
writeLine(stdout, ` Last action: ${lastTick.action}`);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
function printAutomationRunOnceResult(result, parsed, stdout) {
|
|
2040
|
+
const payload = { ok: true, ...result };
|
|
2041
|
+
if (parsed.json) {
|
|
2042
|
+
writeJson(stdout, payload);
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
writeLine(stdout, `DevNexus automation run ${result.status}.`);
|
|
2046
|
+
writeLine(stdout, ` Run: ${result.runId}`);
|
|
2047
|
+
writeLine(stdout, ` Project: ${projectLabel(result.projectConfig)}`);
|
|
2048
|
+
writeLine(stdout, ` Summary: ${result.summary}`);
|
|
2049
|
+
if (result.workItem) {
|
|
2050
|
+
writeLine(stdout, ` Work item: ${result.workItem.id} ${result.workItem.title}`);
|
|
2051
|
+
}
|
|
2052
|
+
if (result.worktree) {
|
|
2053
|
+
writeLine(stdout, ` Worktree: ${result.worktree.worktreePath}`);
|
|
2054
|
+
writeLine(stdout, ` Branch: ${result.worktree.branchName}`);
|
|
2055
|
+
}
|
|
2056
|
+
if (result.execution) {
|
|
2057
|
+
writeLine(stdout, ` Verification: ${result.execution.verification.length} record(s)`);
|
|
2058
|
+
writeLine(stdout, ` Commits: ${result.execution.commitIds.length}`);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
function printAutomationAgentLaunchResult(result, parsed, stdout) {
|
|
2062
|
+
const payload = { ok: true, ...result };
|
|
2063
|
+
if (parsed.json) {
|
|
2064
|
+
writeJson(stdout, payload);
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
writeLine(stdout, `DevNexus automation agent launch ${result.status}.`);
|
|
2068
|
+
writeLine(stdout, ` Run: ${result.runId}`);
|
|
2069
|
+
writeLine(stdout, ` Project: ${projectLabel(result.projectConfig)}`);
|
|
2070
|
+
writeLine(stdout, ` Summary: ${result.summary}`);
|
|
2071
|
+
writeLine(stdout, ` Eligible work items: ${result.eligibleWorkItems.length}`);
|
|
2072
|
+
if (result.contextFile) {
|
|
2073
|
+
writeLine(stdout, ` Context: ${result.contextFile}`);
|
|
2074
|
+
}
|
|
2075
|
+
if (result.resultFile) {
|
|
2076
|
+
writeLine(stdout, ` Result file: ${result.resultFile}`);
|
|
2077
|
+
}
|
|
2078
|
+
if (result.launch?.verification) {
|
|
2079
|
+
writeLine(stdout, ` Verification: ${result.launch.verification.length} record(s)`);
|
|
2080
|
+
}
|
|
2081
|
+
if (result.launch?.commitIds) {
|
|
2082
|
+
writeLine(stdout, ` Commits: ${result.launch.commitIds.length}`);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
function printAutomationStatusResult(result, parsed, stdout) {
|
|
2086
|
+
const payload = { ok: true, ...result };
|
|
2087
|
+
if (parsed.json) {
|
|
2088
|
+
writeJson(stdout, payload);
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
writeLine(stdout, `DevNexus automation status: ${result.status}.`);
|
|
2092
|
+
writeLine(stdout, ` Project: ${projectLabel(result.projectConfig)}`);
|
|
2093
|
+
writeLine(stdout, ` Summary: ${result.summary}`);
|
|
2094
|
+
if (result.target) {
|
|
2095
|
+
writeLine(stdout, ` Target state: ${result.target.statePath}`);
|
|
2096
|
+
if (result.target.objective) {
|
|
2097
|
+
writeLine(stdout, ` Target objective: ${result.target.objective}`);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
if (result.agent) {
|
|
2101
|
+
if (result.agent.coordinatorProfileId) {
|
|
2102
|
+
writeLine(stdout, ` Coordinator profile: ${result.agent.coordinatorProfileId}`);
|
|
2103
|
+
}
|
|
2104
|
+
writeLine(stdout, ` Max concurrent subagents: ${result.agent.maxConcurrentSubagents}`);
|
|
2105
|
+
}
|
|
2106
|
+
if (result.selectedWorkItem) {
|
|
2107
|
+
writeLine(stdout, ` Selected work item: ${result.selectedWorkItem.id} ${result.selectedWorkItem.title}`);
|
|
2108
|
+
}
|
|
2109
|
+
if (result.eligibleWorkItems) {
|
|
2110
|
+
writeLine(stdout, ` Eligible work items: ${result.eligibleWorkItems.length}`);
|
|
2111
|
+
}
|
|
2112
|
+
if (result.targetCycles) {
|
|
2113
|
+
writeLine(stdout, ` Target cycles: ${result.targetCycles.cycleCount}`);
|
|
2114
|
+
if (result.targetCycles.lastCycle) {
|
|
2115
|
+
writeLine(stdout, ` Last target cycle: ${result.targetCycles.lastCycle.id} ${result.targetCycles.lastCycle.status}`);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
if (result.candidateCount !== null) {
|
|
2119
|
+
writeLine(stdout, ` Candidates: ${result.candidateCount}`);
|
|
2120
|
+
}
|
|
2121
|
+
if (result.lock) {
|
|
2122
|
+
writeLine(stdout, ` Lock: ${result.lock.status}`);
|
|
2123
|
+
}
|
|
2124
|
+
if (result.ledger) {
|
|
2125
|
+
writeLine(stdout, ` Runs recorded: ${result.ledger.runs.length}`);
|
|
2126
|
+
const lastRun = result.ledger.runs.at(-1);
|
|
2127
|
+
if (lastRun) {
|
|
2128
|
+
writeLine(stdout, ` Last run: ${lastRun.id} ${lastRun.status} ${lastRun.summary ?? ""}`.trimEnd());
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
function printAutomationEligibleWorkResult(result, parsed, stdout) {
|
|
2133
|
+
const payload = { ok: true, ...result };
|
|
2134
|
+
if (parsed.json) {
|
|
2135
|
+
writeJson(stdout, payload);
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
writeLine(stdout, `DevNexus eligible work: ${result.eligibleWorkItemCount}.`);
|
|
2139
|
+
writeLine(stdout, ` Project: ${result.project.id} (${result.project.name})`);
|
|
2140
|
+
writeLine(stdout, ` Status: ${result.status}`);
|
|
2141
|
+
if (result.selector) {
|
|
2142
|
+
writeLine(stdout, ` Selector: ${formatAutomationSelector(result.selector)}`);
|
|
2143
|
+
}
|
|
2144
|
+
for (const component of result.components) {
|
|
2145
|
+
writeLine(stdout, ` ${component.componentId} (${component.componentName}): ${component.workItems.length}`);
|
|
2146
|
+
for (const item of component.workItems) {
|
|
2147
|
+
writeLine(stdout, ` ${item.id} [${item.status}] ${item.title}`);
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
function printAutomationAgentProfilesResult(result, parsed, stdout) {
|
|
2152
|
+
const payload = { ok: true, ...result };
|
|
2153
|
+
if (parsed.json) {
|
|
2154
|
+
writeJson(stdout, payload);
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
writeLine(stdout, "DevNexus agent profiles.");
|
|
2158
|
+
writeLine(stdout, ` Project: ${result.project.id} (${result.project.name})`);
|
|
2159
|
+
writeLine(stdout, ` Automation: ${result.automationEnabled ? result.automationMode : "disabled"}`);
|
|
2160
|
+
if (result.coordinatorProfileId) {
|
|
2161
|
+
writeLine(stdout, ` Coordinator profile: ${result.coordinatorProfileId}`);
|
|
2162
|
+
}
|
|
2163
|
+
if (result.maxConcurrentSubagents !== null) {
|
|
2164
|
+
writeLine(stdout, ` Max concurrent subagents: ${result.maxConcurrentSubagents}`);
|
|
2165
|
+
}
|
|
2166
|
+
if (result.safety) {
|
|
2167
|
+
writeLine(stdout, ` Safety: ${result.safety.profile} hostMutation=${result.safety.allowHostMutation} dependencyInstall=${result.safety.allowDependencyInstall} liveServices=${result.safety.allowLiveServices}`);
|
|
2168
|
+
}
|
|
2169
|
+
writeLine(stdout, ` Profiles: ${result.profiles.length}`);
|
|
2170
|
+
for (const profile of result.profiles) {
|
|
2171
|
+
writeLine(stdout, ` ${profile.id} executor=${profile.executor} model=${profile.model ?? "none"} version=${profile.version ?? "none"} variant=${profile.variant ?? "none"} reasoning=${profile.reasoning ?? "none"} intelligence=${profile.intelligence ?? "none"} intendedUse=${profile.intendedUse} safety=${profile.safety.profile} command=${profile.commandConfigured ? "yes" : "no"} args=${profile.argsCount}`);
|
|
2172
|
+
}
|
|
2173
|
+
writeLine(stdout, ` Plugin capabilities: ${result.pluginCapabilities.length}`);
|
|
2174
|
+
for (const plugin of result.pluginCapabilities) {
|
|
2175
|
+
writeLine(stdout, ` ${plugin.pluginId} capabilities=${plugin.capabilityCount}`);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
function formatAutomationSelector(selector) {
|
|
2179
|
+
const parts = [];
|
|
2180
|
+
if (selector.statuses.length > 0) {
|
|
2181
|
+
parts.push(`status=${selector.statuses.join(",")}`);
|
|
2182
|
+
}
|
|
2183
|
+
if (selector.labels.length > 0) {
|
|
2184
|
+
parts.push(`label=${selector.labels.join(",")}`);
|
|
2185
|
+
}
|
|
2186
|
+
if (selector.excludeLabels.length > 0) {
|
|
2187
|
+
parts.push(`excludeLabel=${selector.excludeLabels.join(",")}`);
|
|
2188
|
+
}
|
|
2189
|
+
if (selector.assignees.length > 0) {
|
|
2190
|
+
parts.push(`assignee=${selector.assignees.join(",")}`);
|
|
2191
|
+
}
|
|
2192
|
+
if (selector.search) {
|
|
2193
|
+
parts.push(`search=${selector.search}`);
|
|
2194
|
+
}
|
|
2195
|
+
parts.push(`limit=${selector.limit}`);
|
|
2196
|
+
return parts.length > 0 ? parts.join(" ") : "none";
|
|
2197
|
+
}
|
|
2198
|
+
function projectLabel(config) {
|
|
2199
|
+
return `${config.id} (${config.name})`;
|
|
2200
|
+
}
|
|
2201
|
+
function automationConfigForProjectRoot(projectRoot) {
|
|
2202
|
+
const projectConfig = loadProjectConfig(path.resolve(projectRoot));
|
|
2203
|
+
const automationConfig = projectConfig.automation;
|
|
2204
|
+
if (!automationConfig) {
|
|
2205
|
+
throw new Error("Project automation is not configured");
|
|
2206
|
+
}
|
|
2207
|
+
return {
|
|
2208
|
+
projectConfig,
|
|
2209
|
+
automationConfig,
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
function printProjectStatusText(project, stdout) {
|
|
2213
|
+
writeLine(stdout, ` Id: ${project.id}`);
|
|
2214
|
+
writeLine(stdout, ` Name: ${project.name}`);
|
|
2215
|
+
writeLine(stdout, ` Root: ${project.projectRoot}`);
|
|
2216
|
+
}
|
|
2217
|
+
function fileProjectHomeStore() {
|
|
2218
|
+
return {
|
|
2219
|
+
resolveHomePath: resolveNexusHome,
|
|
2220
|
+
loadHomeConfig: (homePath) => loadNexusHomeConfigFile(homePath, validateNexusHomeConfigBase),
|
|
2221
|
+
saveHomeConfig: (homePath, registry) => saveNexusHomeConfigFile(homePath, registry, validateNexusHomeConfigBase),
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
function resolvedCommandHomePath(homePath) {
|
|
2225
|
+
return resolveNexusHome(homePath ?? defaultNexusHomePath());
|
|
2226
|
+
}
|
|
2227
|
+
function resolveProjectStatusForCli(parsed) {
|
|
2228
|
+
if (parsed.homePath) {
|
|
2229
|
+
return getNexusProjectStatus({
|
|
2230
|
+
homePath: resolvedCommandHomePath(parsed.homePath),
|
|
2231
|
+
homeStore: fileProjectHomeStore(),
|
|
2232
|
+
project: parsed.project,
|
|
2233
|
+
}).project;
|
|
2234
|
+
}
|
|
2235
|
+
try {
|
|
2236
|
+
return buildNexusProjectStatusForPath(parsed.project);
|
|
2237
|
+
}
|
|
2238
|
+
catch (pathError) {
|
|
2239
|
+
try {
|
|
2240
|
+
return getNexusProjectStatus({
|
|
2241
|
+
homePath: resolvedCommandHomePath(undefined),
|
|
2242
|
+
homeStore: fileProjectHomeStore(),
|
|
2243
|
+
project: parsed.project,
|
|
2244
|
+
}).project;
|
|
2245
|
+
}
|
|
2246
|
+
catch {
|
|
2247
|
+
throw pathError;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
function statusQuery(statuses) {
|
|
2252
|
+
if (statuses.length === 0) {
|
|
2253
|
+
return undefined;
|
|
2254
|
+
}
|
|
2255
|
+
return statuses.length === 1 ? statuses[0] : statuses;
|
|
2256
|
+
}
|
|
2257
|
+
function parseWorkStatus(value, optionName) {
|
|
2258
|
+
if (value === "todo" ||
|
|
2259
|
+
value === "ready" ||
|
|
2260
|
+
value === "in_progress" ||
|
|
2261
|
+
value === "blocked" ||
|
|
2262
|
+
value === "done" ||
|
|
2263
|
+
value === "wont_do") {
|
|
2264
|
+
return value;
|
|
2265
|
+
}
|
|
2266
|
+
throw new Error(`${optionName} must be a valid work status`);
|
|
2267
|
+
}
|
|
2268
|
+
function parseTargetCycleStatus(value, optionName) {
|
|
2269
|
+
if (value === "started" ||
|
|
2270
|
+
value === "dispatched" ||
|
|
2271
|
+
value === "completed" ||
|
|
2272
|
+
value === "blocked" ||
|
|
2273
|
+
value === "failed" ||
|
|
2274
|
+
value === "skipped") {
|
|
2275
|
+
return value;
|
|
2276
|
+
}
|
|
2277
|
+
throw new Error(`${optionName} must be a valid target cycle status`);
|
|
2278
|
+
}
|
|
2279
|
+
function parseTargetCycleWorkItemStatus(value, optionName) {
|
|
2280
|
+
if (value === "eligible" ||
|
|
2281
|
+
value === "selected" ||
|
|
2282
|
+
value === "dispatched" ||
|
|
2283
|
+
value === "in_progress" ||
|
|
2284
|
+
value === "completed" ||
|
|
2285
|
+
value === "blocked" ||
|
|
2286
|
+
value === "skipped") {
|
|
2287
|
+
return value;
|
|
2288
|
+
}
|
|
2289
|
+
throw new Error(`${optionName} must be a valid target cycle work item status`);
|
|
2290
|
+
}
|
|
2291
|
+
function parseTargetCycleWorkItem(value, optionName) {
|
|
2292
|
+
const separator = value.indexOf(":");
|
|
2293
|
+
if (separator < 0) {
|
|
2294
|
+
if (!value.trim()) {
|
|
2295
|
+
throw new Error(`${optionName} must be a non-empty work item id`);
|
|
2296
|
+
}
|
|
2297
|
+
return {
|
|
2298
|
+
id: value.trim(),
|
|
2299
|
+
cycleStatus: "selected",
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
const componentId = value.slice(0, separator).trim();
|
|
2303
|
+
const id = value.slice(separator + 1).trim();
|
|
2304
|
+
if (!componentId || !id) {
|
|
2305
|
+
throw new Error(`${optionName} must be <component-id:id> or <id>`);
|
|
2306
|
+
}
|
|
2307
|
+
return {
|
|
2308
|
+
componentId,
|
|
2309
|
+
id,
|
|
2310
|
+
cycleStatus: "selected",
|
|
2311
|
+
};
|
|
2312
|
+
}
|
|
2313
|
+
function lastParsedTargetCycleWorkItem(parsed, optionName) {
|
|
2314
|
+
const item = parsed.workItems?.at(-1);
|
|
2315
|
+
if (!item) {
|
|
2316
|
+
throw new Error(`${optionName} requires a preceding --work-item`);
|
|
2317
|
+
}
|
|
2318
|
+
return item;
|
|
2319
|
+
}
|
|
2320
|
+
function parseTrackerProvider(value, optionName) {
|
|
2321
|
+
if (value === "local" ||
|
|
2322
|
+
value === "github" ||
|
|
2323
|
+
value === "gitlab" ||
|
|
2324
|
+
value === "jira") {
|
|
2325
|
+
return value;
|
|
2326
|
+
}
|
|
2327
|
+
throw new Error(`${optionName} must be local, github, gitlab, or jira`);
|
|
2328
|
+
}
|
|
2329
|
+
function parsePositiveInteger(value, optionName) {
|
|
2330
|
+
const parsed = Number(value);
|
|
2331
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
2332
|
+
throw new Error(`${optionName} must be a positive integer`);
|
|
2333
|
+
}
|
|
2334
|
+
return parsed;
|
|
2335
|
+
}
|
|
2336
|
+
function parseNonNegativeInteger(value, optionName) {
|
|
2337
|
+
const parsed = Number(value);
|
|
2338
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
2339
|
+
throw new Error(`${optionName} must be a non-negative integer`);
|
|
2340
|
+
}
|
|
2341
|
+
return parsed;
|
|
2342
|
+
}
|
|
2343
|
+
function writeLine(stdout, line = "") {
|
|
2344
|
+
stdout.write(`${line}\n`);
|
|
2345
|
+
}
|
|
2346
|
+
function writeJson(stdout, value) {
|
|
2347
|
+
stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
2348
|
+
}
|
|
2349
|
+
function isCliEntrypoint() {
|
|
2350
|
+
const entrypoint = process.argv[1];
|
|
2351
|
+
if (!entrypoint) {
|
|
2352
|
+
return false;
|
|
2353
|
+
}
|
|
2354
|
+
const normalize = (filePath) => {
|
|
2355
|
+
const resolved = path.resolve(filePath);
|
|
2356
|
+
try {
|
|
2357
|
+
return fs.realpathSync.native(resolved);
|
|
2358
|
+
}
|
|
2359
|
+
catch {
|
|
2360
|
+
return resolved;
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
return normalize(entrypoint) === normalize(fileURLToPath(import.meta.url));
|
|
2364
|
+
}
|
|
2365
|
+
if (isCliEntrypoint()) {
|
|
2366
|
+
main(process.argv.slice(2))
|
|
2367
|
+
.then((exitCode) => {
|
|
2368
|
+
process.exitCode = exitCode;
|
|
2369
|
+
})
|
|
2370
|
+
.catch((error) => {
|
|
2371
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
2372
|
+
process.exitCode = 1;
|
|
2373
|
+
});
|
|
2374
|
+
}
|