agentic-dev 0.2.17 → 0.2.19
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 +43 -3
- package/dist/bin/agentic-dev.js +510 -129
- package/dist/bin/agentic-dev.js.map +1 -1
- package/dist/lib/orchestration-assets.d.ts.map +1 -1
- package/dist/lib/orchestration-assets.js +134 -15
- package/dist/lib/orchestration-assets.js.map +1 -1
- package/dist/lib/scaffold.js +4 -4
- package/dist/lib/scaffold.js.map +1 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/bin/agentic-dev.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import process from "node:process";
|
|
5
|
-
import * as readline from "node:readline";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { DEFAULT_TEMPLATE_OWNER, ensureTargetDir, fetchTemplateRepos, finalizeRepositoryGit, installTemplateRepo, parseArgs, resolveGitHubOrchestration, resolveTemplateRepo, usage, } from "../lib/scaffold.js";
|
|
8
8
|
const DEFAULT_TARGET_DIR = ".";
|
|
9
9
|
const DEFAULT_AI_PROVIDERS = ["codex", "claude"];
|
|
10
|
-
const DEFAULT_PROVIDER_PROFILES = ["codex-
|
|
10
|
+
const DEFAULT_PROVIDER_PROFILES = ["codex-cli", "claude-cli"];
|
|
11
|
+
const DASHBOARD_TABS = ["Overview", "Agents", "Tasks", "Activity"];
|
|
11
12
|
const GITHUB_AUTH_CHOICES = [
|
|
12
13
|
{
|
|
13
14
|
label: "gh-session",
|
|
@@ -43,12 +44,11 @@ const AI_PROVIDER_CHOICES = [
|
|
|
43
44
|
},
|
|
44
45
|
];
|
|
45
46
|
const PROVIDER_PROFILE_CHOICES = [
|
|
46
|
-
{ label: "codex-
|
|
47
|
-
{ label: "
|
|
48
|
-
{ label: "claude-
|
|
49
|
-
{ label: "
|
|
50
|
-
{ label: "
|
|
51
|
-
{ label: "ollama-self-hosted", value: "ollama-self-hosted", description: "Local Ollama runtime" },
|
|
47
|
+
{ label: "codex-cli", value: "codex-cli", description: "Run tasks through the local Codex CLI session." },
|
|
48
|
+
{ label: "openai-api", value: "openai-api", description: "Call the OpenAI Responses API directly for coding tasks." },
|
|
49
|
+
{ label: "claude-cli", value: "claude-cli", description: "Run tasks through the local Claude Code CLI session." },
|
|
50
|
+
{ label: "anthropic-api", value: "anthropic-api", description: "Call the Anthropic Messages API directly for Claude tasks." },
|
|
51
|
+
{ label: "ollama-self-hosted", value: "ollama-self-hosted", description: "Use the local Ollama server/runtime." },
|
|
52
52
|
];
|
|
53
53
|
const GITHUB_PROJECT_MODE_CHOICES = [
|
|
54
54
|
{
|
|
@@ -74,13 +74,6 @@ const CONFIRM_CHOICES = [
|
|
|
74
74
|
description: "Stop without writing files",
|
|
75
75
|
},
|
|
76
76
|
];
|
|
77
|
-
function clearMenu(lines) {
|
|
78
|
-
if (lines <= 0) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
readline.moveCursor(process.stdout, 0, -lines);
|
|
82
|
-
readline.clearScreenDown(process.stdout);
|
|
83
|
-
}
|
|
84
77
|
function inferProjectName(targetDir) {
|
|
85
78
|
const normalized = targetDir.trim() || DEFAULT_TARGET_DIR;
|
|
86
79
|
const resolved = path.resolve(process.cwd(), normalized);
|
|
@@ -101,8 +94,42 @@ function uniqueStrings(values) {
|
|
|
101
94
|
function normalizeProviders(providers) {
|
|
102
95
|
return providers.length > 0 ? uniqueStrings(providers) : [...DEFAULT_AI_PROVIDERS];
|
|
103
96
|
}
|
|
97
|
+
function normalizeProviderProfile(value) {
|
|
98
|
+
const normalized = value.trim().toLowerCase();
|
|
99
|
+
switch (normalized) {
|
|
100
|
+
case "codex-subscription":
|
|
101
|
+
case "codex-cli":
|
|
102
|
+
return "codex-cli";
|
|
103
|
+
case "codex-api":
|
|
104
|
+
case "openai-api":
|
|
105
|
+
return "openai-api";
|
|
106
|
+
case "claude-subscription":
|
|
107
|
+
case "claude-cli":
|
|
108
|
+
return "claude-cli";
|
|
109
|
+
case "claude-api":
|
|
110
|
+
case "anthropic-api":
|
|
111
|
+
return "anthropic-api";
|
|
112
|
+
case "ollama-self-hosted":
|
|
113
|
+
return "ollama-self-hosted";
|
|
114
|
+
default:
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
104
118
|
function normalizeProviderProfiles(providerProfiles) {
|
|
105
|
-
|
|
119
|
+
const normalized = providerProfiles
|
|
120
|
+
.map((profile) => normalizeProviderProfile(profile))
|
|
121
|
+
.filter(Boolean);
|
|
122
|
+
return normalized.length > 0 ? uniqueStrings(normalized) : [...DEFAULT_PROVIDER_PROFILES];
|
|
123
|
+
}
|
|
124
|
+
function normalizeGithubAuthMode(value) {
|
|
125
|
+
const normalized = value.trim().toLowerCase();
|
|
126
|
+
if (normalized === "public" || normalized === "gh-session") {
|
|
127
|
+
return "gh";
|
|
128
|
+
}
|
|
129
|
+
if (normalized === "gh" || normalized === "env" || normalized === "pat") {
|
|
130
|
+
return normalized;
|
|
131
|
+
}
|
|
132
|
+
return "gh";
|
|
106
133
|
}
|
|
107
134
|
function hydrateOptions(options) {
|
|
108
135
|
const state = {
|
|
@@ -131,16 +158,6 @@ function hydrateOptions(options) {
|
|
|
131
158
|
}
|
|
132
159
|
return state;
|
|
133
160
|
}
|
|
134
|
-
function normalizeGithubAuthMode(value) {
|
|
135
|
-
const normalized = value.trim().toLowerCase();
|
|
136
|
-
if (normalized === "public" || normalized === "gh-session") {
|
|
137
|
-
return "gh";
|
|
138
|
-
}
|
|
139
|
-
if (normalized === "gh" || normalized === "env" || normalized === "pat") {
|
|
140
|
-
return normalized;
|
|
141
|
-
}
|
|
142
|
-
return "gh";
|
|
143
|
-
}
|
|
144
161
|
function applyRuntimeGitHubAuth(state) {
|
|
145
162
|
if (state.githubAuthMode === "pat" && state.githubPat) {
|
|
146
163
|
process.env.AGENTIC_GITHUB_TOKEN = state.githubPat;
|
|
@@ -179,21 +196,21 @@ function buildSteps(state, repos) {
|
|
|
179
196
|
key: "projectName",
|
|
180
197
|
type: "text",
|
|
181
198
|
label: "Project name",
|
|
182
|
-
description: "Written into scaffold metadata and
|
|
199
|
+
description: "Written into scaffold metadata and workspace labels.",
|
|
183
200
|
placeholder: inferProjectName(state.targetDir),
|
|
184
201
|
},
|
|
185
202
|
{
|
|
186
203
|
key: "githubRepositoryInput",
|
|
187
204
|
type: "text",
|
|
188
205
|
label: "GitHub repository",
|
|
189
|
-
description: "Use owner/name, a GitHub URL, or just a repo name to create
|
|
206
|
+
description: "Use owner/name, a GitHub URL, or just a repo name to create or reuse the repository.",
|
|
190
207
|
placeholder: state.projectName,
|
|
191
208
|
},
|
|
192
209
|
{
|
|
193
210
|
key: "githubAuthMode",
|
|
194
211
|
type: "single",
|
|
195
212
|
label: "GitHub credential source",
|
|
196
|
-
description: "Choose which
|
|
213
|
+
description: "Choose which credentials this run should use for repo/project orchestration. say828 template-* selection is always available.",
|
|
197
214
|
choices: GITHUB_AUTH_CHOICES,
|
|
198
215
|
},
|
|
199
216
|
];
|
|
@@ -210,7 +227,7 @@ function buildSteps(state, repos) {
|
|
|
210
227
|
key: "githubProjectMode",
|
|
211
228
|
type: "single",
|
|
212
229
|
label: "GitHub project mode",
|
|
213
|
-
description: "Choose whether
|
|
230
|
+
description: "Choose whether orchestration must reuse an existing GitHub Project or create one if needed.",
|
|
214
231
|
choices: GITHUB_PROJECT_MODE_CHOICES,
|
|
215
232
|
}, {
|
|
216
233
|
key: "githubProjectTitle",
|
|
@@ -227,14 +244,14 @@ function buildSteps(state, repos) {
|
|
|
227
244
|
}, {
|
|
228
245
|
key: "providerProfiles",
|
|
229
246
|
type: "multi",
|
|
230
|
-
label: "AI
|
|
231
|
-
description: "
|
|
247
|
+
label: "AI execution adapters",
|
|
248
|
+
description: "Toggle the runtime adapters this repo may use during orchestration.",
|
|
232
249
|
choices: PROVIDER_PROFILE_CHOICES,
|
|
233
250
|
}, {
|
|
234
251
|
key: "aiProviders",
|
|
235
252
|
type: "multi",
|
|
236
253
|
label: "Workspace agent surfaces",
|
|
237
|
-
description: "
|
|
254
|
+
description: "Toggle Codex, Claude, and Ollama metadata in the generated repo.",
|
|
238
255
|
choices: AI_PROVIDER_CHOICES,
|
|
239
256
|
});
|
|
240
257
|
if (!state.force && directoryHasUserFiles(state.targetDir)) {
|
|
@@ -261,7 +278,7 @@ function buildSteps(state, repos) {
|
|
|
261
278
|
key: "confirm",
|
|
262
279
|
type: "confirm",
|
|
263
280
|
label: "Review and run",
|
|
264
|
-
description: "
|
|
281
|
+
description: "All selections are collected first. Commands run only after final confirmation.",
|
|
265
282
|
choices: CONFIRM_CHOICES,
|
|
266
283
|
});
|
|
267
284
|
return steps;
|
|
@@ -284,29 +301,67 @@ function stepValue(state, step) {
|
|
|
284
301
|
}
|
|
285
302
|
return state[step.key];
|
|
286
303
|
}
|
|
287
|
-
function renderHeader(
|
|
288
|
-
return [
|
|
304
|
+
function renderHeader(title, subtitle, modeHint) {
|
|
305
|
+
return [
|
|
306
|
+
title,
|
|
307
|
+
subtitle,
|
|
308
|
+
"",
|
|
309
|
+
modeHint,
|
|
310
|
+
"",
|
|
311
|
+
];
|
|
312
|
+
}
|
|
313
|
+
function executionPreview(state, selectedRepo) {
|
|
314
|
+
const lines = [
|
|
315
|
+
`Project directory: ${path.resolve(process.cwd(), state.targetDir)}`,
|
|
316
|
+
`Project name: ${state.projectName}`,
|
|
317
|
+
`GitHub repository: ${state.githubRepositoryInput}`,
|
|
318
|
+
`GitHub project mode: ${state.githubProjectMode}`,
|
|
319
|
+
`GitHub project title: ${state.githubProjectTitle}`,
|
|
320
|
+
`GitHub credential source: ${state.githubAuthMode}`,
|
|
321
|
+
`Template repo: ${selectedRepo?.name || state.template}`,
|
|
322
|
+
`Workspace surfaces: ${state.aiProviders.join(", ")}`,
|
|
323
|
+
`Execution adapters: ${state.providerProfiles.join(", ")}`,
|
|
324
|
+
`Allow non-empty directory: ${state.force ? "yes" : "no"}`,
|
|
325
|
+
];
|
|
326
|
+
if (state.githubAuthMode === "pat") {
|
|
327
|
+
lines.push(`GitHub PAT supplied: ${state.githubPat ? "yes" : "no"}`);
|
|
328
|
+
}
|
|
329
|
+
lines.push("");
|
|
330
|
+
lines.push("Planned execution:");
|
|
331
|
+
lines.push(` 1. Ensure GitHub repo ${state.githubRepositoryInput}`);
|
|
332
|
+
lines.push(` 2. Ensure GitHub Project ${state.githubProjectTitle}`);
|
|
333
|
+
lines.push(` 3. Clone ${selectedRepo?.name || state.template}`);
|
|
334
|
+
lines.push(" 4. Install shared sub-agent assets and orchestration workflow");
|
|
335
|
+
lines.push(" 5. Copy .env.example to .env if needed");
|
|
336
|
+
lines.push(" 6. Run pnpm install");
|
|
337
|
+
lines.push(" 7. If the selected template has a default frontend target, install Playwright Chromium");
|
|
338
|
+
lines.push(" 8. If the selected template has a default frontend target and bootstrap is not skipped, run frontend parity bootstrap");
|
|
339
|
+
return lines;
|
|
289
340
|
}
|
|
290
341
|
function renderTextStep(step, state, buffers, index, total) {
|
|
291
|
-
const lines = renderHeader(step, index, total);
|
|
292
342
|
if (!buffers.has(step.key)) {
|
|
293
343
|
buffers.set(step.key, String(stepValue(state, step) || ""));
|
|
294
344
|
}
|
|
295
345
|
const rawValue = buffers.get(step.key) || "";
|
|
296
346
|
const displayValue = step.type === "password" ? "*".repeat(rawValue.length) : rawValue || step.placeholder || "";
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
347
|
+
return [
|
|
348
|
+
...renderHeader(`Agentic Dev Init ${index + 1}/${total}`, step.label, step.description),
|
|
349
|
+
`Value: ${displayValue}`,
|
|
350
|
+
"",
|
|
351
|
+
"Controls",
|
|
352
|
+
" ← previous screen",
|
|
353
|
+
" → or Enter next screen",
|
|
354
|
+
" Backspace edit",
|
|
355
|
+
" Ctrl+C cancel",
|
|
356
|
+
];
|
|
302
357
|
}
|
|
303
358
|
function renderSingleChoiceStep(step, state, cursors, index, total) {
|
|
304
|
-
const lines = renderHeader(step, index, total);
|
|
305
359
|
if (!cursors.has(step.key)) {
|
|
306
360
|
const currentValue = stepValue(state, step);
|
|
307
361
|
const currentIndex = Math.max(0, step.choices.findIndex((choice) => choice.value === currentValue));
|
|
308
362
|
cursors.set(step.key, currentIndex >= 0 ? currentIndex : 0);
|
|
309
363
|
}
|
|
364
|
+
const lines = renderHeader(`Agentic Dev Init ${index + 1}/${total}`, step.label, step.description);
|
|
310
365
|
const cursor = cursors.get(step.key) || 0;
|
|
311
366
|
step.choices.forEach((choice, choiceIndex) => {
|
|
312
367
|
const pointer = choiceIndex === cursor ? ">" : " ";
|
|
@@ -314,15 +369,18 @@ function renderSingleChoiceStep(step, state, cursors, index, total) {
|
|
|
314
369
|
lines.push(`${pointer} ${choice.label}${suffix}`);
|
|
315
370
|
});
|
|
316
371
|
lines.push("");
|
|
317
|
-
lines.push("Controls
|
|
318
|
-
|
|
319
|
-
|
|
372
|
+
lines.push("Controls");
|
|
373
|
+
lines.push(" ↑/↓ choose");
|
|
374
|
+
lines.push(" ← previous screen");
|
|
375
|
+
lines.push(" → or Enter next screen");
|
|
376
|
+
lines.push(" Ctrl+C cancel");
|
|
377
|
+
return lines;
|
|
320
378
|
}
|
|
321
379
|
function renderMultiChoiceStep(step, state, cursors, index, total) {
|
|
322
|
-
const lines = renderHeader(step, index, total);
|
|
323
380
|
if (!cursors.has(step.key)) {
|
|
324
381
|
cursors.set(step.key, 0);
|
|
325
382
|
}
|
|
383
|
+
const lines = renderHeader(`Agentic Dev Init ${index + 1}/${total}`, step.label, step.description);
|
|
326
384
|
const selected = new Set(uniqueStrings(stepValue(state, step) || []));
|
|
327
385
|
const cursor = cursors.get(step.key) || 0;
|
|
328
386
|
step.choices.forEach((choice, choiceIndex) => {
|
|
@@ -332,39 +390,16 @@ function renderMultiChoiceStep(step, state, cursors, index, total) {
|
|
|
332
390
|
lines.push(`${pointer} ${checked} ${choice.label}${suffix}`);
|
|
333
391
|
});
|
|
334
392
|
lines.push("");
|
|
335
|
-
lines.push("Controls
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
`Project directory: ${path.resolve(process.cwd(), state.targetDir)}`,
|
|
342
|
-
`Project name: ${state.projectName}`,
|
|
343
|
-
`GitHub repository: ${state.githubRepositoryInput}`,
|
|
344
|
-
`GitHub project mode: ${state.githubProjectMode}`,
|
|
345
|
-
`GitHub project title: ${state.githubProjectTitle}`,
|
|
346
|
-
`GitHub credential source: ${state.githubAuthMode}`,
|
|
347
|
-
`Template repo: ${selectedRepo?.name || state.template}`,
|
|
348
|
-
`AI providers: ${state.aiProviders.join(", ")}`,
|
|
349
|
-
`Provider profiles: ${state.providerProfiles.join(", ")}`,
|
|
350
|
-
`Allow non-empty directory: ${state.force ? "yes" : "no"}`,
|
|
351
|
-
];
|
|
352
|
-
if (state.githubAuthMode === "pat") {
|
|
353
|
-
lines.push(`GitHub PAT supplied: ${state.githubPat ? "yes" : "no"}`);
|
|
354
|
-
}
|
|
355
|
-
lines.push("Run plan:");
|
|
356
|
-
lines.push(` 1. Ensure GitHub repo ${state.githubRepositoryInput}`);
|
|
357
|
-
lines.push(` 2. Ensure GitHub Project ${state.githubProjectTitle}`);
|
|
358
|
-
lines.push(` 3. Clone ${selectedRepo?.name || state.template}`);
|
|
359
|
-
lines.push(" 4. Install shared sub-agent assets and orchestration workflow");
|
|
360
|
-
lines.push(" 5. Copy .env.example to .env if needed");
|
|
361
|
-
lines.push(" 6. Run pnpm install");
|
|
362
|
-
lines.push(" 7. If the selected template has a default frontend target, install Playwright Chromium");
|
|
363
|
-
lines.push(" 8. If the selected template has a default frontend target and bootstrap is not skipped, run frontend parity bootstrap");
|
|
393
|
+
lines.push("Controls");
|
|
394
|
+
lines.push(" ↑/↓ choose");
|
|
395
|
+
lines.push(" Space toggle");
|
|
396
|
+
lines.push(" ← previous screen");
|
|
397
|
+
lines.push(" → or Enter next screen");
|
|
398
|
+
lines.push(" Ctrl+C cancel");
|
|
364
399
|
return lines;
|
|
365
400
|
}
|
|
366
401
|
function renderConfirmStep(step, state, repos, cursors, index, total) {
|
|
367
|
-
const lines = renderHeader(
|
|
402
|
+
const lines = renderHeader(`Agentic Dev Init ${index + 1}/${total}`, step.label, step.description);
|
|
368
403
|
const selectedRepo = resolveTemplateRepo(state.template, repos);
|
|
369
404
|
executionPreview(state, selectedRepo).forEach((line) => lines.push(line));
|
|
370
405
|
lines.push("");
|
|
@@ -378,48 +413,73 @@ function renderConfirmStep(step, state, repos, cursors, index, total) {
|
|
|
378
413
|
lines.push(`${pointer} ${choice.label}${suffix}`);
|
|
379
414
|
});
|
|
380
415
|
lines.push("");
|
|
381
|
-
lines.push("Controls
|
|
382
|
-
|
|
383
|
-
|
|
416
|
+
lines.push("Controls");
|
|
417
|
+
lines.push(" ↑/↓ choose");
|
|
418
|
+
lines.push(" ← previous screen");
|
|
419
|
+
lines.push(" → or Enter confirm");
|
|
420
|
+
lines.push(" Ctrl+C cancel");
|
|
421
|
+
return lines;
|
|
422
|
+
}
|
|
423
|
+
function truncateLine(line, width) {
|
|
424
|
+
if (width <= 0) {
|
|
425
|
+
return "";
|
|
426
|
+
}
|
|
427
|
+
if (line.length <= width) {
|
|
428
|
+
return line;
|
|
429
|
+
}
|
|
430
|
+
if (width <= 1) {
|
|
431
|
+
return line.slice(0, width);
|
|
432
|
+
}
|
|
433
|
+
return `${line.slice(0, width - 1)}…`;
|
|
384
434
|
}
|
|
385
|
-
|
|
435
|
+
function renderFullScreen(lines) {
|
|
436
|
+
const width = process.stdout.columns || 120;
|
|
437
|
+
const height = process.stdout.rows || 40;
|
|
438
|
+
const clipped = lines.map((line) => truncateLine(line, width)).slice(0, height);
|
|
439
|
+
while (clipped.length < height) {
|
|
440
|
+
clipped.push("");
|
|
441
|
+
}
|
|
442
|
+
process.stdout.write("\u001b[H\u001b[2J");
|
|
443
|
+
process.stdout.write(clipped.join("\n"));
|
|
444
|
+
}
|
|
445
|
+
async function runFullScreenSession(render, onInput) {
|
|
386
446
|
const stdin = process.stdin;
|
|
387
447
|
const stdout = process.stdout;
|
|
388
448
|
const previousRawMode = typeof stdin.setRawMode === "function" ? stdin.isRaw : undefined;
|
|
449
|
+
const rerender = () => {
|
|
450
|
+
renderFullScreen(render());
|
|
451
|
+
};
|
|
452
|
+
stdout.write("\u001b[?1049h\u001b[?25l");
|
|
389
453
|
if (typeof stdin.setRawMode === "function") {
|
|
390
454
|
stdin.setRawMode(true);
|
|
391
455
|
}
|
|
392
456
|
stdin.resume();
|
|
393
457
|
stdin.setEncoding("utf8");
|
|
394
458
|
return new Promise((resolve, reject) => {
|
|
395
|
-
let renderedLines = render();
|
|
396
459
|
const cleanup = () => {
|
|
397
460
|
stdin.removeListener("data", handleData);
|
|
461
|
+
stdout.removeListener("resize", rerender);
|
|
398
462
|
if (typeof stdin.setRawMode === "function") {
|
|
399
463
|
stdin.setRawMode(Boolean(previousRawMode));
|
|
400
464
|
}
|
|
401
|
-
stdout.write("\
|
|
402
|
-
};
|
|
403
|
-
const rerender = () => {
|
|
404
|
-
clearMenu(renderedLines);
|
|
405
|
-
renderedLines = render();
|
|
465
|
+
stdout.write("\u001b[?25h\u001b[?1049l");
|
|
406
466
|
};
|
|
407
467
|
const handleData = (chunk) => {
|
|
408
468
|
try {
|
|
409
469
|
const result = onInput(chunk, rerender);
|
|
410
470
|
if (result !== undefined) {
|
|
411
|
-
clearMenu(renderedLines);
|
|
412
471
|
cleanup();
|
|
413
472
|
resolve(result);
|
|
414
473
|
}
|
|
415
474
|
}
|
|
416
475
|
catch (error) {
|
|
417
|
-
clearMenu(renderedLines);
|
|
418
476
|
cleanup();
|
|
419
477
|
reject(error);
|
|
420
478
|
}
|
|
421
479
|
};
|
|
480
|
+
stdout.on("resize", rerender);
|
|
422
481
|
stdin.on("data", handleData);
|
|
482
|
+
rerender();
|
|
423
483
|
});
|
|
424
484
|
}
|
|
425
485
|
function validateStepValue(step, state, value) {
|
|
@@ -464,7 +524,7 @@ function validateStepValue(step, state, value) {
|
|
|
464
524
|
if (step.key === "providerProfiles") {
|
|
465
525
|
const profiles = normalizeProviderProfiles(Array.isArray(value) ? value : []);
|
|
466
526
|
if (profiles.length === 0) {
|
|
467
|
-
throw new Error("Select at least one
|
|
527
|
+
throw new Error("Select at least one execution adapter.");
|
|
468
528
|
}
|
|
469
529
|
return profiles;
|
|
470
530
|
}
|
|
@@ -489,13 +549,12 @@ async function runWizard(initialState, repos) {
|
|
|
489
549
|
const buffers = new Map();
|
|
490
550
|
const cursors = new Map();
|
|
491
551
|
let stepIndex = 0;
|
|
492
|
-
return
|
|
552
|
+
return runFullScreenSession(() => {
|
|
493
553
|
const steps = buildSteps(state, repos);
|
|
494
554
|
if (stepIndex >= steps.length) {
|
|
495
555
|
stepIndex = steps.length - 1;
|
|
496
556
|
}
|
|
497
557
|
const step = steps[stepIndex];
|
|
498
|
-
process.stdout.write("\n");
|
|
499
558
|
if (step.type === "text" || step.type === "password") {
|
|
500
559
|
return renderTextStep(step, state, buffers, stepIndex, steps.length);
|
|
501
560
|
}
|
|
@@ -684,7 +743,7 @@ async function promptLinearly(initialState, repos) {
|
|
|
684
743
|
const projectTitleAnswer = await rl.question(`GitHub project title [${state.githubProjectTitle || `${state.projectName} Delivery`}]: `);
|
|
685
744
|
state.githubProjectTitle = projectTitleAnswer.trim() || state.githubProjectTitle || `${state.projectName} Delivery`;
|
|
686
745
|
state.template = await promptForChoice(rl, `Template repo from ${state.owner}`, buildTemplateChoices(repos));
|
|
687
|
-
state.providerProfiles = await promptForMultiChoice(rl, "AI
|
|
746
|
+
state.providerProfiles = await promptForMultiChoice(rl, "AI execution adapters", PROVIDER_PROFILE_CHOICES, state.providerProfiles);
|
|
688
747
|
state.aiProviders = await promptForMultiChoice(rl, "AI providers", AI_PROVIDER_CHOICES, state.aiProviders);
|
|
689
748
|
if (!state.force && directoryHasUserFiles(state.targetDir)) {
|
|
690
749
|
state.force = await promptForChoice(rl, "Target directory is not empty", [
|
|
@@ -758,6 +817,356 @@ function validateNonInteractiveState(state) {
|
|
|
758
817
|
}
|
|
759
818
|
return state;
|
|
760
819
|
}
|
|
820
|
+
function readJson(filePath) {
|
|
821
|
+
if (!fs.existsSync(filePath)) {
|
|
822
|
+
return null;
|
|
823
|
+
}
|
|
824
|
+
try {
|
|
825
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
826
|
+
}
|
|
827
|
+
catch {
|
|
828
|
+
return null;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
function readTaskList(snapshotRoot) {
|
|
832
|
+
const taskIndex = readJson(path.join(snapshotRoot, ".agentic-dev/generated/task-index.json"));
|
|
833
|
+
const indexed = Array.isArray(taskIndex?.tasks) ? taskIndex?.tasks : null;
|
|
834
|
+
if (indexed) {
|
|
835
|
+
return indexed.map((task) => ({
|
|
836
|
+
id: String(task.id || ""),
|
|
837
|
+
title: String(task.title || ""),
|
|
838
|
+
status: String(task.status || "open"),
|
|
839
|
+
source: String(task.source || ""),
|
|
840
|
+
issue_number: task.issue_number ?? null,
|
|
841
|
+
issue_url: task.issue_url ?? null,
|
|
842
|
+
}));
|
|
843
|
+
}
|
|
844
|
+
const taskIr = readJson(path.join(snapshotRoot, ".agentic-dev/generated/task-ir.json"));
|
|
845
|
+
const irTasks = Array.isArray(taskIr?.tasks) ? taskIr?.tasks : [];
|
|
846
|
+
return irTasks.map((task) => ({
|
|
847
|
+
id: String(task.id || ""),
|
|
848
|
+
title: String(task.title || ""),
|
|
849
|
+
status: String(task.status || "open"),
|
|
850
|
+
source: String(task.source || ""),
|
|
851
|
+
issue_number: null,
|
|
852
|
+
issue_url: null,
|
|
853
|
+
}));
|
|
854
|
+
}
|
|
855
|
+
function loadDashboardSnapshot(cwd) {
|
|
856
|
+
const setup = readJson(path.join(cwd, ".agentic-dev/setup.json"));
|
|
857
|
+
const orchestration = readJson(path.join(cwd, ".agentic-dev/orchestration.json"));
|
|
858
|
+
const queue = readJson(path.join(cwd, ".agentic-dev/generated/agent-queue.json"));
|
|
859
|
+
const dispatch = readJson(path.join(cwd, ".agentic-dev/generated/dispatch-plan.json"));
|
|
860
|
+
const journal = readJson(path.join(cwd, ".agentic-dev/generated/execution-journal.json"));
|
|
861
|
+
const closed = readJson(path.join(cwd, ".agentic-dev/generated/closed-tasks.json"));
|
|
862
|
+
const tasks = readTaskList(cwd);
|
|
863
|
+
const agents = Array.isArray(orchestration?.specialized_agents) ? orchestration?.specialized_agents : [];
|
|
864
|
+
const recentExecutions = Array.isArray(journal?.executions) ? journal?.executions : [];
|
|
865
|
+
return {
|
|
866
|
+
installed: Boolean(setup && orchestration),
|
|
867
|
+
cwd,
|
|
868
|
+
projectName: String(setup?.project_name || orchestration?.project_name || path.basename(cwd)),
|
|
869
|
+
templateRepo: String(setup?.template_repo || ""),
|
|
870
|
+
repositorySlug: String(orchestration?.github?.repository
|
|
871
|
+
? (orchestration?.github).repository.slug || ""
|
|
872
|
+
: ""),
|
|
873
|
+
projectTitle: String(orchestration?.github?.project
|
|
874
|
+
? (orchestration?.github).project.title || ""
|
|
875
|
+
: ""),
|
|
876
|
+
agentSurfaces: Array.isArray(setup?.ai_providers) ? setup?.ai_providers : [],
|
|
877
|
+
providers: Array.isArray(orchestration?.providers) ? orchestration?.providers : [],
|
|
878
|
+
agents: agents.map((agent) => ({
|
|
879
|
+
id: String(agent.id || ""),
|
|
880
|
+
description: String(agent.description || ""),
|
|
881
|
+
})),
|
|
882
|
+
tasks,
|
|
883
|
+
queueCount: Array.isArray(queue?.queue) ? queue.queue.length : 0,
|
|
884
|
+
dispatchCount: Array.isArray(dispatch?.dispatch) ? dispatch.dispatch.length : 0,
|
|
885
|
+
executionCount: recentExecutions.length,
|
|
886
|
+
closedCount: Array.isArray(closed?.closed) ? closed.closed.length : 0,
|
|
887
|
+
recentExecutions: recentExecutions
|
|
888
|
+
.map((item) => ({
|
|
889
|
+
task_id: String(item.task_id || ""),
|
|
890
|
+
title: String(item.title || ""),
|
|
891
|
+
provider: String(item.provider || ""),
|
|
892
|
+
adapter: String(item.adapter || ""),
|
|
893
|
+
execution_state: String(item.execution_state || ""),
|
|
894
|
+
started_at: item.started_at || "",
|
|
895
|
+
completed_at: item.completed_at || "",
|
|
896
|
+
issue_number: item.issue_number ?? null,
|
|
897
|
+
stdout: String(item.stdout || ""),
|
|
898
|
+
stderr: String(item.stderr || ""),
|
|
899
|
+
}))
|
|
900
|
+
.reverse(),
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function selectedTab(tabIndex) {
|
|
904
|
+
return DASHBOARD_TABS[Math.max(0, Math.min(DASHBOARD_TABS.length - 1, tabIndex))];
|
|
905
|
+
}
|
|
906
|
+
function renderDashboardTabs(tabIndex) {
|
|
907
|
+
return DASHBOARD_TABS.map((tab, index) => (index === tabIndex ? `[${tab}]` : ` ${tab} `)).join(" ");
|
|
908
|
+
}
|
|
909
|
+
function dashboardList(state) {
|
|
910
|
+
const tab = selectedTab(state.tabIndex);
|
|
911
|
+
if (tab === "Agents") {
|
|
912
|
+
return state.snapshot.agents.map((agent) => `${agent.id} - ${agent.description}`);
|
|
913
|
+
}
|
|
914
|
+
if (tab === "Tasks") {
|
|
915
|
+
return state.snapshot.tasks.map((task) => {
|
|
916
|
+
const issue = task.issue_number ? ` #${task.issue_number}` : "";
|
|
917
|
+
return `${task.status.toUpperCase()} ${task.id}${issue} - ${task.title}`;
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
if (tab === "Activity") {
|
|
921
|
+
return state.snapshot.recentExecutions.map((execution) => {
|
|
922
|
+
return `${execution.execution_state.toUpperCase()} ${execution.task_id} via ${execution.adapter || execution.provider}`;
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
return [];
|
|
926
|
+
}
|
|
927
|
+
function clampDashboardCursor(state) {
|
|
928
|
+
const items = dashboardList(state);
|
|
929
|
+
if (items.length === 0) {
|
|
930
|
+
state.listCursor = 0;
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
state.listCursor = Math.max(0, Math.min(state.listCursor, items.length - 1));
|
|
934
|
+
}
|
|
935
|
+
function renderOverview(snapshot) {
|
|
936
|
+
const openTasks = snapshot.tasks.filter((task) => task.status !== "closed").length;
|
|
937
|
+
const closedTasks = snapshot.tasks.filter((task) => task.status === "closed").length;
|
|
938
|
+
return [
|
|
939
|
+
`Project: ${snapshot.projectName}`,
|
|
940
|
+
`Template: ${snapshot.templateRepo || "(not recorded)"}`,
|
|
941
|
+
`Repository: ${snapshot.repositorySlug || "(not bound)"}`,
|
|
942
|
+
`GitHub Project: ${snapshot.projectTitle || "(not bound)"}`,
|
|
943
|
+
`Workspace surfaces: ${snapshot.agentSurfaces.join(", ") || "(none)"}`,
|
|
944
|
+
`Execution adapters: ${snapshot.providers.join(", ") || "(none)"}`,
|
|
945
|
+
"",
|
|
946
|
+
`Tasks: ${snapshot.tasks.length} total | ${openTasks} open | ${closedTasks} closed`,
|
|
947
|
+
`Queue: ${snapshot.queueCount} planned agent items`,
|
|
948
|
+
`Dispatch: ${snapshot.dispatchCount} dispatch items`,
|
|
949
|
+
`Executions: ${snapshot.executionCount} records`,
|
|
950
|
+
`Closed by close pass: ${snapshot.closedCount}`,
|
|
951
|
+
];
|
|
952
|
+
}
|
|
953
|
+
function renderAgents(state) {
|
|
954
|
+
const items = dashboardList(state);
|
|
955
|
+
if (items.length === 0) {
|
|
956
|
+
return ["No specialized agents recorded in .agentic-dev/orchestration.json."];
|
|
957
|
+
}
|
|
958
|
+
return items.map((item, index) => `${index === state.listCursor ? ">" : " "} ${item}`);
|
|
959
|
+
}
|
|
960
|
+
function renderTasks(state) {
|
|
961
|
+
if (state.snapshot.tasks.length === 0) {
|
|
962
|
+
return ["No task IR or task index found yet."];
|
|
963
|
+
}
|
|
964
|
+
const selected = state.snapshot.tasks[state.listCursor];
|
|
965
|
+
const lines = state.snapshot.tasks.slice(0, 12).map((task, index) => `${index === state.listCursor ? ">" : " "} ${task.status.toUpperCase()} ${task.id} - ${task.title}`);
|
|
966
|
+
if (selected) {
|
|
967
|
+
lines.push("");
|
|
968
|
+
lines.push(`Selected source: ${selected.source || "(unknown)"}`);
|
|
969
|
+
lines.push(`Selected issue: ${selected.issue_number ? `#${selected.issue_number}` : "(not mirrored yet)"}`);
|
|
970
|
+
}
|
|
971
|
+
return lines;
|
|
972
|
+
}
|
|
973
|
+
function renderActivity(state) {
|
|
974
|
+
if (state.snapshot.recentExecutions.length === 0) {
|
|
975
|
+
return ["No execution journal found yet."];
|
|
976
|
+
}
|
|
977
|
+
const selected = state.snapshot.recentExecutions[state.listCursor];
|
|
978
|
+
const lines = state.snapshot.recentExecutions
|
|
979
|
+
.slice(0, 12)
|
|
980
|
+
.map((execution, index) => `${index === state.listCursor ? ">" : " "} ${execution.execution_state.toUpperCase()} ${execution.task_id} via ${execution.adapter || execution.provider}`);
|
|
981
|
+
if (selected) {
|
|
982
|
+
lines.push("");
|
|
983
|
+
lines.push(`Task: ${selected.title}`);
|
|
984
|
+
lines.push(`Started: ${selected.started_at || "(unknown)"}`);
|
|
985
|
+
lines.push(`Completed: ${selected.completed_at || "(unknown)"}`);
|
|
986
|
+
if (selected.stderr) {
|
|
987
|
+
lines.push(`stderr: ${selected.stderr}`);
|
|
988
|
+
}
|
|
989
|
+
else if (selected.stdout) {
|
|
990
|
+
lines.push(`stdout: ${selected.stdout.split(/\r?\n/)[0] || "(empty)"}`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return lines;
|
|
994
|
+
}
|
|
995
|
+
function renderDashboard(state) {
|
|
996
|
+
const snapshot = state.snapshot;
|
|
997
|
+
const lines = renderHeader("Agentic Dev Dashboard", snapshot.installed
|
|
998
|
+
? "Installed orchestration repo detected."
|
|
999
|
+
: "No .agentic-dev install found in the current working directory.", `cwd: ${snapshot.cwd}`);
|
|
1000
|
+
lines.push(renderDashboardTabs(state.tabIndex));
|
|
1001
|
+
lines.push("");
|
|
1002
|
+
if (!snapshot.installed) {
|
|
1003
|
+
lines.push("This command is the management TUI.");
|
|
1004
|
+
lines.push("Initialize a repo first with:");
|
|
1005
|
+
lines.push(" agentic-dev init");
|
|
1006
|
+
lines.push("");
|
|
1007
|
+
lines.push("Controls");
|
|
1008
|
+
lines.push(" q quit");
|
|
1009
|
+
lines.push(" r refresh");
|
|
1010
|
+
return lines;
|
|
1011
|
+
}
|
|
1012
|
+
const tab = selectedTab(state.tabIndex);
|
|
1013
|
+
if (tab === "Overview") {
|
|
1014
|
+
lines.push(...renderOverview(snapshot));
|
|
1015
|
+
}
|
|
1016
|
+
else if (tab === "Agents") {
|
|
1017
|
+
lines.push(...renderAgents(state));
|
|
1018
|
+
}
|
|
1019
|
+
else if (tab === "Tasks") {
|
|
1020
|
+
lines.push(...renderTasks(state));
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
lines.push(...renderActivity(state));
|
|
1024
|
+
}
|
|
1025
|
+
lines.push("");
|
|
1026
|
+
lines.push(`Status: ${state.statusMessage}`);
|
|
1027
|
+
lines.push("");
|
|
1028
|
+
lines.push("Controls");
|
|
1029
|
+
lines.push(" ←/→ switch tabs");
|
|
1030
|
+
lines.push(" ↑/↓ move selection");
|
|
1031
|
+
lines.push(" r refresh");
|
|
1032
|
+
lines.push(" i build IR");
|
|
1033
|
+
lines.push(" t sync GitHub tasks");
|
|
1034
|
+
lines.push(" p plan queue");
|
|
1035
|
+
lines.push(" d build dispatch");
|
|
1036
|
+
lines.push(" e execute dispatch");
|
|
1037
|
+
lines.push(" c close completed tasks");
|
|
1038
|
+
lines.push(" q quit");
|
|
1039
|
+
return lines;
|
|
1040
|
+
}
|
|
1041
|
+
function runDashboardAction(cwd, key) {
|
|
1042
|
+
const scriptMap = {
|
|
1043
|
+
i: ".agentic-dev/runtime/sdd_to_ir.ts",
|
|
1044
|
+
t: ".agentic-dev/runtime/sync_project_tasks.ts",
|
|
1045
|
+
p: ".agentic-dev/runtime/run_multi_agent_queue.ts",
|
|
1046
|
+
d: ".agentic-dev/runtime/dispatch_agents.ts",
|
|
1047
|
+
e: ".agentic-dev/runtime/dispatch_execute.ts",
|
|
1048
|
+
c: ".agentic-dev/runtime/close_completed_tasks.ts",
|
|
1049
|
+
};
|
|
1050
|
+
const script = scriptMap[key];
|
|
1051
|
+
if (!script) {
|
|
1052
|
+
return "No action.";
|
|
1053
|
+
}
|
|
1054
|
+
if (!fs.existsSync(path.join(cwd, script))) {
|
|
1055
|
+
throw new Error(`Missing runtime script: ${script}`);
|
|
1056
|
+
}
|
|
1057
|
+
const result = spawnSync("npx", ["--yes", "tsx", script], {
|
|
1058
|
+
cwd,
|
|
1059
|
+
encoding: "utf-8",
|
|
1060
|
+
maxBuffer: 1024 * 1024 * 20,
|
|
1061
|
+
});
|
|
1062
|
+
if (result.status !== 0) {
|
|
1063
|
+
throw new Error((result.stderr || result.stdout || `Failed to run ${script}`).trim());
|
|
1064
|
+
}
|
|
1065
|
+
return (result.stdout || `${script} ok`).trim().split(/\r?\n/).join(" | ");
|
|
1066
|
+
}
|
|
1067
|
+
async function runDashboard() {
|
|
1068
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1069
|
+
throw new Error("Dashboard mode requires a TTY. Use `agentic-dev init` for non-interactive setup.");
|
|
1070
|
+
}
|
|
1071
|
+
const state = {
|
|
1072
|
+
tabIndex: 0,
|
|
1073
|
+
listCursor: 0,
|
|
1074
|
+
snapshot: loadDashboardSnapshot(process.cwd()),
|
|
1075
|
+
statusMessage: "Ready.",
|
|
1076
|
+
};
|
|
1077
|
+
clampDashboardCursor(state);
|
|
1078
|
+
await runFullScreenSession(() => renderDashboard(state), (chunk, rerender) => {
|
|
1079
|
+
if (chunk === "\u0003" || chunk.toLowerCase() === "q") {
|
|
1080
|
+
return true;
|
|
1081
|
+
}
|
|
1082
|
+
if (chunk === "\u001b[C") {
|
|
1083
|
+
state.tabIndex = (state.tabIndex + 1) % DASHBOARD_TABS.length;
|
|
1084
|
+
clampDashboardCursor(state);
|
|
1085
|
+
rerender();
|
|
1086
|
+
return undefined;
|
|
1087
|
+
}
|
|
1088
|
+
if (chunk === "\u001b[D") {
|
|
1089
|
+
state.tabIndex = (state.tabIndex - 1 + DASHBOARD_TABS.length) % DASHBOARD_TABS.length;
|
|
1090
|
+
clampDashboardCursor(state);
|
|
1091
|
+
rerender();
|
|
1092
|
+
return undefined;
|
|
1093
|
+
}
|
|
1094
|
+
if (chunk === "\u001b[A") {
|
|
1095
|
+
state.listCursor = Math.max(0, state.listCursor - 1);
|
|
1096
|
+
rerender();
|
|
1097
|
+
return undefined;
|
|
1098
|
+
}
|
|
1099
|
+
if (chunk === "\u001b[B") {
|
|
1100
|
+
state.listCursor += 1;
|
|
1101
|
+
clampDashboardCursor(state);
|
|
1102
|
+
rerender();
|
|
1103
|
+
return undefined;
|
|
1104
|
+
}
|
|
1105
|
+
const lower = chunk.toLowerCase();
|
|
1106
|
+
if (lower === "r") {
|
|
1107
|
+
state.snapshot = loadDashboardSnapshot(process.cwd());
|
|
1108
|
+
clampDashboardCursor(state);
|
|
1109
|
+
state.statusMessage = "Refreshed snapshot.";
|
|
1110
|
+
rerender();
|
|
1111
|
+
return undefined;
|
|
1112
|
+
}
|
|
1113
|
+
if ("itpdec".includes(lower)) {
|
|
1114
|
+
if (!state.snapshot.installed) {
|
|
1115
|
+
state.statusMessage = "Initialize the repo first with `agentic-dev init`.";
|
|
1116
|
+
rerender();
|
|
1117
|
+
return undefined;
|
|
1118
|
+
}
|
|
1119
|
+
try {
|
|
1120
|
+
const actionOutput = runDashboardAction(process.cwd(), lower);
|
|
1121
|
+
state.snapshot = loadDashboardSnapshot(process.cwd());
|
|
1122
|
+
clampDashboardCursor(state);
|
|
1123
|
+
state.statusMessage = actionOutput || "Action completed.";
|
|
1124
|
+
}
|
|
1125
|
+
catch (error) {
|
|
1126
|
+
state.statusMessage = error instanceof Error ? error.message : String(error);
|
|
1127
|
+
}
|
|
1128
|
+
rerender();
|
|
1129
|
+
return undefined;
|
|
1130
|
+
}
|
|
1131
|
+
return undefined;
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
async function runInitCommand(options) {
|
|
1135
|
+
const state = hydrateOptions(options);
|
|
1136
|
+
applyRuntimeGitHubAuth(state);
|
|
1137
|
+
const repos = await fetchTemplateRepos({ owner: state.owner });
|
|
1138
|
+
if (repos.length === 0) {
|
|
1139
|
+
throw new Error(`No public template-* repos found for ${state.owner}.`);
|
|
1140
|
+
}
|
|
1141
|
+
const resolvedState = options.yes
|
|
1142
|
+
? validateNonInteractiveState(sanitizeStateForInstall(state))
|
|
1143
|
+
: process.stdin.isTTY && process.stdout.isTTY
|
|
1144
|
+
? await runWizard(state, repos)
|
|
1145
|
+
: await promptLinearly(state, repos);
|
|
1146
|
+
applyRuntimeGitHubAuth(resolvedState);
|
|
1147
|
+
const orchestration = await resolveGitHubOrchestration(resolvedState);
|
|
1148
|
+
const selectedRepo = resolveTemplateRepo(resolvedState.template, repos);
|
|
1149
|
+
if (!selectedRepo) {
|
|
1150
|
+
throw new Error(`Template repo not found: ${resolvedState.template}`);
|
|
1151
|
+
}
|
|
1152
|
+
const destinationRoot = ensureTargetDir(resolvedState.targetDir, {
|
|
1153
|
+
force: resolvedState.force,
|
|
1154
|
+
});
|
|
1155
|
+
const result = installTemplateRepo({
|
|
1156
|
+
destinationRoot: path.resolve(destinationRoot),
|
|
1157
|
+
templateRepo: selectedRepo,
|
|
1158
|
+
setupSelections: {
|
|
1159
|
+
...resolvedState,
|
|
1160
|
+
...orchestration,
|
|
1161
|
+
},
|
|
1162
|
+
skipBootstrap: resolvedState.skipBootstrap,
|
|
1163
|
+
});
|
|
1164
|
+
finalizeRepositoryGit(path.resolve(destinationRoot), {
|
|
1165
|
+
...resolvedState,
|
|
1166
|
+
...orchestration,
|
|
1167
|
+
});
|
|
1168
|
+
printSuccess(result);
|
|
1169
|
+
}
|
|
761
1170
|
async function main() {
|
|
762
1171
|
let options;
|
|
763
1172
|
try {
|
|
@@ -774,10 +1183,11 @@ async function main() {
|
|
|
774
1183
|
console.log(usage());
|
|
775
1184
|
return;
|
|
776
1185
|
}
|
|
777
|
-
|
|
1186
|
+
let command = options.command;
|
|
1187
|
+
if (!["dashboard", "init"].includes(command)) {
|
|
778
1188
|
if (!options.targetDir) {
|
|
779
1189
|
options.targetDir = options.command;
|
|
780
|
-
|
|
1190
|
+
command = "init";
|
|
781
1191
|
}
|
|
782
1192
|
else {
|
|
783
1193
|
console.error(`Unsupported command: ${options.command}`);
|
|
@@ -788,40 +1198,11 @@ async function main() {
|
|
|
788
1198
|
}
|
|
789
1199
|
}
|
|
790
1200
|
try {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
797
|
-
const resolvedState = options.yes
|
|
798
|
-
? validateNonInteractiveState(sanitizeStateForInstall(state))
|
|
799
|
-
: process.stdin.isTTY && process.stdout.isTTY
|
|
800
|
-
? await runWizard(state, repos)
|
|
801
|
-
: await promptLinearly(state, repos);
|
|
802
|
-
applyRuntimeGitHubAuth(resolvedState);
|
|
803
|
-
const orchestration = await resolveGitHubOrchestration(resolvedState);
|
|
804
|
-
const selectedRepo = resolveTemplateRepo(resolvedState.template, repos);
|
|
805
|
-
if (!selectedRepo) {
|
|
806
|
-
throw new Error(`Template repo not found: ${resolvedState.template}`);
|
|
807
|
-
}
|
|
808
|
-
const destinationRoot = ensureTargetDir(resolvedState.targetDir, {
|
|
809
|
-
force: resolvedState.force,
|
|
810
|
-
});
|
|
811
|
-
const result = installTemplateRepo({
|
|
812
|
-
destinationRoot: path.resolve(destinationRoot),
|
|
813
|
-
templateRepo: selectedRepo,
|
|
814
|
-
setupSelections: {
|
|
815
|
-
...resolvedState,
|
|
816
|
-
...orchestration,
|
|
817
|
-
},
|
|
818
|
-
skipBootstrap: resolvedState.skipBootstrap,
|
|
819
|
-
});
|
|
820
|
-
finalizeRepositoryGit(path.resolve(destinationRoot), {
|
|
821
|
-
...resolvedState,
|
|
822
|
-
...orchestration,
|
|
823
|
-
});
|
|
824
|
-
printSuccess(result);
|
|
1201
|
+
if (command === "dashboard") {
|
|
1202
|
+
await runDashboard();
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
await runInitCommand(options);
|
|
825
1206
|
}
|
|
826
1207
|
catch (error) {
|
|
827
1208
|
console.error(error instanceof Error ? error.message : String(error));
|