@maestroai/cli 0.1.1 → 0.1.2
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/dist/bin/maestro.js +2 -1
- package/dist/bin/maestro.js.map +1 -1
- package/dist/chunk-A3PN4QCM.js +69 -0
- package/dist/chunk-A3PN4QCM.js.map +1 -0
- package/dist/{chunk-5ZDNPLKN.js → chunk-RWLHK222.js} +368 -97
- package/dist/chunk-RWLHK222.js.map +1 -0
- package/dist/src/index.js +2 -1
- package/dist/src/tui/init/InitTUI.d.ts +32 -0
- package/dist/src/tui/init/InitTUI.js +545 -0
- package/dist/src/tui/init/InitTUI.js.map +1 -0
- package/package.json +3 -2
- package/dist/chunk-5ZDNPLKN.js.map +0 -1
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkClaudeInstalled,
|
|
3
|
+
getClaudeInstallInstructions
|
|
4
|
+
} from "./chunk-A3PN4QCM.js";
|
|
5
|
+
|
|
1
6
|
// src/index.ts
|
|
2
7
|
import { Command } from "commander";
|
|
3
8
|
|
|
4
9
|
// src/commands/init.ts
|
|
5
10
|
import * as fs from "fs";
|
|
6
|
-
import * as
|
|
11
|
+
import * as path from "path";
|
|
7
12
|
import * as readline from "readline";
|
|
8
13
|
import chalk from "chalk";
|
|
9
14
|
import figures from "figures";
|
|
@@ -15,72 +20,6 @@ import {
|
|
|
15
20
|
resetTaskCounter,
|
|
16
21
|
getDefaultSystemPrompt
|
|
17
22
|
} from "@maestroai/core";
|
|
18
|
-
|
|
19
|
-
// src/utils/claude-check.ts
|
|
20
|
-
import { existsSync } from "fs";
|
|
21
|
-
import { execSync } from "child_process";
|
|
22
|
-
import path from "path";
|
|
23
|
-
import os from "os";
|
|
24
|
-
function checkClaudeInstalled() {
|
|
25
|
-
try {
|
|
26
|
-
const claudePath = execSync("command -v claude", { encoding: "utf-8" }).trim();
|
|
27
|
-
if (!claudePath) {
|
|
28
|
-
const homeDir = os.homedir();
|
|
29
|
-
const commonPaths = [
|
|
30
|
-
path.join(homeDir, ".local", "bin", "claude"),
|
|
31
|
-
"/usr/local/bin/claude",
|
|
32
|
-
path.join(homeDir, "bin", "claude")
|
|
33
|
-
];
|
|
34
|
-
for (const checkPath of commonPaths) {
|
|
35
|
-
if (existsSync(checkPath)) {
|
|
36
|
-
try {
|
|
37
|
-
const version2 = execSync("claude --version", { encoding: "utf-8" }).trim();
|
|
38
|
-
return { installed: true, path: checkPath, version: version2 };
|
|
39
|
-
} catch {
|
|
40
|
-
return { installed: true, path: checkPath };
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
installed: false,
|
|
46
|
-
error: "Claude CLI not found in PATH or common installation directories"
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
let version;
|
|
50
|
-
try {
|
|
51
|
-
version = execSync("claude --version", { encoding: "utf-8" }).trim();
|
|
52
|
-
} catch {
|
|
53
|
-
}
|
|
54
|
-
return { installed: true, path: claudePath, version };
|
|
55
|
-
} catch (error) {
|
|
56
|
-
return {
|
|
57
|
-
installed: false,
|
|
58
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function getClaudeInstallInstructions() {
|
|
63
|
-
const platform = os.platform();
|
|
64
|
-
if (platform === "darwin") {
|
|
65
|
-
return `Install Claude CLI for macOS:
|
|
66
|
-
npm install -g @anthropic-ai/claude-code
|
|
67
|
-
|
|
68
|
-
Or using Homebrew:
|
|
69
|
-
brew install anthropic/claude/claude-code`;
|
|
70
|
-
}
|
|
71
|
-
if (platform === "linux") {
|
|
72
|
-
return `Install Claude CLI for Linux:
|
|
73
|
-
npm install -g @anthropic-ai/claude-code`;
|
|
74
|
-
}
|
|
75
|
-
if (platform === "win32") {
|
|
76
|
-
return `Install Claude CLI for Windows:
|
|
77
|
-
npm install -g @anthropic-ai/claude-code`;
|
|
78
|
-
}
|
|
79
|
-
return `Install Claude CLI:
|
|
80
|
-
npm install -g @anthropic-ai/claude-code`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// src/commands/init.ts
|
|
84
23
|
function ask(rl, question, defaultValue) {
|
|
85
24
|
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
86
25
|
return new Promise((resolve5) => {
|
|
@@ -96,7 +35,7 @@ function detectExistingProject(dir) {
|
|
|
96
35
|
hasExistingCode: false,
|
|
97
36
|
configFiles: []
|
|
98
37
|
};
|
|
99
|
-
const pkgJsonPath =
|
|
38
|
+
const pkgJsonPath = path.resolve(dir, "package.json");
|
|
100
39
|
if (fs.existsSync(pkgJsonPath)) {
|
|
101
40
|
result.hasExistingCode = true;
|
|
102
41
|
result.configFiles.push("package.json");
|
|
@@ -118,26 +57,26 @@ function detectExistingProject(dir) {
|
|
|
118
57
|
} catch {
|
|
119
58
|
}
|
|
120
59
|
}
|
|
121
|
-
if (fs.existsSync(
|
|
60
|
+
if (fs.existsSync(path.resolve(dir, "tsconfig.json"))) {
|
|
122
61
|
result.hasExistingCode = true;
|
|
123
62
|
result.configFiles.push("tsconfig.json");
|
|
124
63
|
}
|
|
125
|
-
if (fs.existsSync(
|
|
64
|
+
if (fs.existsSync(path.resolve(dir, "pyproject.toml"))) {
|
|
126
65
|
result.hasExistingCode = true;
|
|
127
66
|
result.configFiles.push("pyproject.toml");
|
|
128
67
|
result.type = "python";
|
|
129
68
|
}
|
|
130
|
-
if (fs.existsSync(
|
|
69
|
+
if (fs.existsSync(path.resolve(dir, "requirements.txt"))) {
|
|
131
70
|
result.hasExistingCode = true;
|
|
132
71
|
result.configFiles.push("requirements.txt");
|
|
133
72
|
if (result.type === "unknown") result.type = "python";
|
|
134
73
|
}
|
|
135
|
-
if (fs.existsSync(
|
|
74
|
+
if (fs.existsSync(path.resolve(dir, "go.mod"))) {
|
|
136
75
|
result.hasExistingCode = true;
|
|
137
76
|
result.configFiles.push("go.mod");
|
|
138
77
|
result.type = "go";
|
|
139
78
|
}
|
|
140
|
-
if (fs.existsSync(
|
|
79
|
+
if (fs.existsSync(path.resolve(dir, "Cargo.toml"))) {
|
|
141
80
|
result.hasExistingCode = true;
|
|
142
81
|
result.configFiles.push("Cargo.toml");
|
|
143
82
|
result.type = "rust";
|
|
@@ -162,7 +101,29 @@ function plannedToTask(planned) {
|
|
|
162
101
|
};
|
|
163
102
|
}
|
|
164
103
|
function registerInitCommand(program) {
|
|
165
|
-
program.command("init").description("Initialize a new Maestro project").option("--no-plan", "skip automatic task planning (create empty todo.md)").option("--model <model>", "model to use for planning", "sonnet").option("-d, --dir <path>", "working directory for project context", ".").option("-i, --instructions <path>", "path to an instructions/requirements file for the planner").action(async (options) => {
|
|
104
|
+
program.command("init").description("Initialize a new Maestro project").option("--no-tui", "use basic stdin prompts instead of interactive terminal UI").option("--no-plan", "skip automatic task planning (create empty todo.md)").option("--model <model>", "model to use for planning", "sonnet").option("-d, --dir <path>", "working directory for project context", ".").option("-i, --instructions <path>", "path to an instructions/requirements file for the planner").action(async (options) => {
|
|
105
|
+
if (options.tui !== false) {
|
|
106
|
+
const { renderInitTUI } = await import("./src/tui/init/InitTUI.js");
|
|
107
|
+
const result = await renderInitTUI({
|
|
108
|
+
dir: options.dir,
|
|
109
|
+
instructions: options.instructions,
|
|
110
|
+
onExit: () => process.exit(0)
|
|
111
|
+
});
|
|
112
|
+
if (!result) {
|
|
113
|
+
process.exit(0);
|
|
114
|
+
}
|
|
115
|
+
await createProjectWithConfig({
|
|
116
|
+
projectName: result.projectName,
|
|
117
|
+
description: result.description,
|
|
118
|
+
devCount: result.devCount,
|
|
119
|
+
techStack: result.techStack,
|
|
120
|
+
targetDir: path.resolve(options.dir),
|
|
121
|
+
options,
|
|
122
|
+
instructionsContent: options.instructions ? fs.readFileSync(path.resolve(options.instructions), "utf-8").trim() : "",
|
|
123
|
+
detected: detectExistingProject(path.resolve(options.dir))
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
166
127
|
const rl = readline.createInterface({
|
|
167
128
|
input: process.stdin,
|
|
168
129
|
output: process.stdout
|
|
@@ -195,7 +156,7 @@ function registerInitCommand(program) {
|
|
|
195
156
|
}
|
|
196
157
|
console.log();
|
|
197
158
|
}
|
|
198
|
-
const targetDir =
|
|
159
|
+
const targetDir = path.resolve(options.dir);
|
|
199
160
|
if (targetDir !== process.cwd()) {
|
|
200
161
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
201
162
|
process.chdir(targetDir);
|
|
@@ -214,7 +175,7 @@ function registerInitCommand(program) {
|
|
|
214
175
|
}
|
|
215
176
|
let instructionsContent = "";
|
|
216
177
|
if (options.instructions) {
|
|
217
|
-
const instrPath =
|
|
178
|
+
const instrPath = path.resolve(options.instructions);
|
|
218
179
|
if (!fs.existsSync(instrPath)) {
|
|
219
180
|
console.error(chalk.red(` ${figures.cross} Instructions file not found: ${instrPath}`));
|
|
220
181
|
process.exit(1);
|
|
@@ -223,7 +184,7 @@ function registerInitCommand(program) {
|
|
|
223
184
|
console.log(chalk.cyan(` ${figures.info} Loaded instructions from ${chalk.bold(options.instructions)} (${instructionsContent.split("\n").length} lines)`));
|
|
224
185
|
console.log();
|
|
225
186
|
}
|
|
226
|
-
const projectName = await ask(rl, " Project name",
|
|
187
|
+
const projectName = await ask(rl, " Project name", path.basename(targetDir));
|
|
227
188
|
console.log();
|
|
228
189
|
console.log(chalk.dim(" Describe what the agents should work on. This can be:"));
|
|
229
190
|
console.log(chalk.dim(" - A new app to build from scratch"));
|
|
@@ -316,7 +277,7 @@ ${frontend ? ` frontend: "${frontend}"
|
|
|
316
277
|
` : ""}${database ? ` database: "${database}"
|
|
317
278
|
` : ""}${otherTech ? ` other: "${otherTech}"
|
|
318
279
|
` : ""}` : ""}`;
|
|
319
|
-
const maestroYamlPath =
|
|
280
|
+
const maestroYamlPath = path.resolve("maestro.yaml");
|
|
320
281
|
if (fs.existsSync(maestroYamlPath)) {
|
|
321
282
|
const overwrite = await ask(rl, ` ${chalk.yellow("maestro.yaml already exists. Overwrite? (y/n)")}`, "n");
|
|
322
283
|
if (overwrite.toLowerCase() === "y") {
|
|
@@ -336,13 +297,13 @@ ${frontend ? ` frontend: "${frontend}"
|
|
|
336
297
|
".maestro/sessions"
|
|
337
298
|
];
|
|
338
299
|
for (const dir of dirs) {
|
|
339
|
-
const dirPath =
|
|
300
|
+
const dirPath = path.resolve(dir);
|
|
340
301
|
if (!fs.existsSync(dirPath)) {
|
|
341
302
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
342
303
|
console.log(` ${chalk.green(figures.tick)} Created ${chalk.bold(dir + "/")}`);
|
|
343
304
|
}
|
|
344
305
|
}
|
|
345
|
-
const gitignorePath =
|
|
306
|
+
const gitignorePath = path.resolve(".maestro/.gitignore");
|
|
346
307
|
if (!fs.existsSync(gitignorePath)) {
|
|
347
308
|
fs.writeFileSync(gitignorePath, `logs/
|
|
348
309
|
sessions/
|
|
@@ -357,7 +318,7 @@ stop
|
|
|
357
318
|
database: database || void 0,
|
|
358
319
|
other: otherTech || void 0
|
|
359
320
|
} : void 0;
|
|
360
|
-
const agentsDir =
|
|
321
|
+
const agentsDir = path.resolve(".agents");
|
|
361
322
|
if (!fs.existsSync(agentsDir)) {
|
|
362
323
|
fs.mkdirSync(agentsDir, { recursive: true });
|
|
363
324
|
console.log(` ${chalk.green(figures.tick)} Created ${chalk.bold(".agents/")}`);
|
|
@@ -365,7 +326,7 @@ stop
|
|
|
365
326
|
const roles = ["orchestrator", "project-manager", "architect", "developer", "designer", "qa-engineer", "devops", "technical-writer", "code-reviewer"];
|
|
366
327
|
let createdPromptCount = 0;
|
|
367
328
|
for (const role of roles) {
|
|
368
|
-
const promptPath =
|
|
329
|
+
const promptPath = path.resolve(agentsDir, `${role}.md`);
|
|
369
330
|
if (!fs.existsSync(promptPath)) {
|
|
370
331
|
const prompt = getDefaultSystemPrompt({
|
|
371
332
|
role,
|
|
@@ -390,7 +351,7 @@ stop
|
|
|
390
351
|
if (createdPromptCount > 0) {
|
|
391
352
|
console.log(` ${chalk.green(figures.tick)} Created ${createdPromptCount} agent prompt files in ${chalk.bold(".agents/")}`);
|
|
392
353
|
}
|
|
393
|
-
const todoPath =
|
|
354
|
+
const todoPath = path.resolve("todo.md");
|
|
394
355
|
const hasExistingTodo = fs.existsSync(todoPath);
|
|
395
356
|
let existingTasks = [];
|
|
396
357
|
if (hasExistingTodo) {
|
|
@@ -571,10 +532,320 @@ function writeEmptyTodo(todoPath, projectName) {
|
|
|
571
532
|
console.log(` ${chalk.green(figures.tick)} Created ${chalk.bold("todo.md")}`);
|
|
572
533
|
}
|
|
573
534
|
}
|
|
535
|
+
async function createProjectWithConfig(config) {
|
|
536
|
+
const { projectName, description, devCount, techStack, targetDir, options, instructionsContent, detected } = config;
|
|
537
|
+
if (targetDir !== process.cwd()) {
|
|
538
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
539
|
+
process.chdir(targetDir);
|
|
540
|
+
}
|
|
541
|
+
console.log();
|
|
542
|
+
console.log(chalk.bold(" MAESTRO") + " - Project Initialization");
|
|
543
|
+
console.log();
|
|
544
|
+
if (detected.hasExistingCode) {
|
|
545
|
+
console.log(chalk.cyan(` ${figures.info} Existing ${detected.type} project detected`));
|
|
546
|
+
console.log(chalk.dim(` Found: ${detected.configFiles.join(", ")}`));
|
|
547
|
+
const detectedTech = Object.values(detected.techStack).filter(Boolean);
|
|
548
|
+
if (detectedTech.length > 0) {
|
|
549
|
+
console.log(chalk.dim(` Detected tech: ${detectedTech.join(", ")}`));
|
|
550
|
+
}
|
|
551
|
+
console.log();
|
|
552
|
+
}
|
|
553
|
+
const techParts = [];
|
|
554
|
+
if (techStack.frontend) techParts.push(`Frontend: ${techStack.frontend}`);
|
|
555
|
+
if (techStack.uiLibrary) techParts.push(`UI: ${techStack.uiLibrary}`);
|
|
556
|
+
if (techStack.backend) techParts.push(`Backend: ${techStack.backend}`);
|
|
557
|
+
if (techStack.database) techParts.push(`Database: ${techStack.database}`);
|
|
558
|
+
if (techStack.other) techParts.push(`Other: ${techStack.other}`);
|
|
559
|
+
const workingDirValue = ".";
|
|
560
|
+
const configDescription = description || (instructionsContent ? `See instructions file: ${options.instructions}` : "A Maestro-managed project");
|
|
561
|
+
const configContent = `# Maestro Project Configuration
|
|
562
|
+
name: "${projectName}"
|
|
563
|
+
description: "${configDescription}"
|
|
564
|
+
version: "1.0.0"
|
|
565
|
+
|
|
566
|
+
agents:
|
|
567
|
+
- role: orchestrator
|
|
568
|
+
count: 1
|
|
569
|
+
|
|
570
|
+
- role: project-manager
|
|
571
|
+
count: 1
|
|
572
|
+
|
|
573
|
+
- role: architect
|
|
574
|
+
count: 1
|
|
575
|
+
|
|
576
|
+
- role: developer
|
|
577
|
+
count: ${devCount}
|
|
578
|
+
|
|
579
|
+
- role: designer
|
|
580
|
+
count: 1
|
|
581
|
+
|
|
582
|
+
- role: qa-engineer
|
|
583
|
+
count: 1
|
|
584
|
+
|
|
585
|
+
- role: devops
|
|
586
|
+
count: 1
|
|
587
|
+
|
|
588
|
+
- role: technical-writer
|
|
589
|
+
count: 1
|
|
590
|
+
|
|
591
|
+
- role: code-reviewer
|
|
592
|
+
count: 1
|
|
593
|
+
|
|
594
|
+
settings:
|
|
595
|
+
workingDirectory: "${workingDirValue}"
|
|
596
|
+
todoFile: "todo.md"
|
|
597
|
+
messagesDirectory: ".maestro/messages"
|
|
598
|
+
logsDirectory: ".maestro/logs"
|
|
599
|
+
defaultModel: "claude-sonnet-4-20250514"
|
|
600
|
+
maxConcurrentAgents: 10
|
|
601
|
+
pollIntervalMs: 5000
|
|
602
|
+
maxTotalBudgetUsd: 50.00
|
|
603
|
+
|
|
604
|
+
# Claude CLI configuration
|
|
605
|
+
# NOTE: Agents run with skip-permissions. Process safety (no builds, no Docker,
|
|
606
|
+
# no servers) is enforced via system prompts in .agents/*.md. Edit those files
|
|
607
|
+
# to customise safety rules.
|
|
608
|
+
claudeCommand: claude
|
|
609
|
+
claudeArgs:
|
|
610
|
+
- "--dangerously-skip-permissions"
|
|
611
|
+
|
|
612
|
+
feedback:
|
|
613
|
+
interactionMode: supervised
|
|
614
|
+
requirePlanApproval: true
|
|
615
|
+
progressReportIntervalMs: 60000
|
|
616
|
+
milestonePercentages: [25, 50, 75, 100]
|
|
617
|
+
questionTimeoutMs: 300000
|
|
618
|
+
${techParts.length > 0 ? `
|
|
619
|
+
techStack:
|
|
620
|
+
${techStack.frontend ? ` frontend: "${techStack.frontend}"
|
|
621
|
+
` : ""}${techStack.uiLibrary ? ` uiLibrary: "${techStack.uiLibrary}"
|
|
622
|
+
` : ""}${techStack.backend ? ` backend: "${techStack.backend}"
|
|
623
|
+
` : ""}${techStack.database ? ` database: "${techStack.database}"
|
|
624
|
+
` : ""}${techStack.other ? ` other: "${techStack.other}"
|
|
625
|
+
` : ""}` : ""}`;
|
|
626
|
+
const maestroYamlPath = path.resolve("maestro.yaml");
|
|
627
|
+
if (fs.existsSync(maestroYamlPath)) {
|
|
628
|
+
fs.writeFileSync(maestroYamlPath, configContent, "utf-8");
|
|
629
|
+
console.log(` ${chalk.green(figures.tick)} Updated ${chalk.bold("maestro.yaml")}`);
|
|
630
|
+
} else {
|
|
631
|
+
fs.writeFileSync(maestroYamlPath, configContent, "utf-8");
|
|
632
|
+
console.log(` ${chalk.green(figures.tick)} Created ${chalk.bold("maestro.yaml")}`);
|
|
633
|
+
}
|
|
634
|
+
const dirs = [
|
|
635
|
+
".maestro",
|
|
636
|
+
".maestro/messages",
|
|
637
|
+
".maestro/logs",
|
|
638
|
+
".maestro/sessions"
|
|
639
|
+
];
|
|
640
|
+
for (const dir of dirs) {
|
|
641
|
+
const dirPath = path.resolve(dir);
|
|
642
|
+
if (!fs.existsSync(dirPath)) {
|
|
643
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
644
|
+
console.log(` ${chalk.green(figures.tick)} Created ${chalk.bold(dir + "/")}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const gitignorePath = path.resolve(".maestro/.gitignore");
|
|
648
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
649
|
+
fs.writeFileSync(gitignorePath, `logs/
|
|
650
|
+
sessions/
|
|
651
|
+
stop
|
|
652
|
+
`, "utf-8");
|
|
653
|
+
console.log(` ${chalk.green(figures.tick)} Created ${chalk.bold(".maestro/.gitignore")}`);
|
|
654
|
+
}
|
|
655
|
+
const agentsDir = path.resolve(".agents");
|
|
656
|
+
if (!fs.existsSync(agentsDir)) {
|
|
657
|
+
fs.mkdirSync(agentsDir, { recursive: true });
|
|
658
|
+
console.log(` ${chalk.green(figures.tick)} Created ${chalk.bold(".agents/")}`);
|
|
659
|
+
}
|
|
660
|
+
const roles = ["orchestrator", "project-manager", "architect", "developer", "designer", "qa-engineer", "devops", "technical-writer", "code-reviewer"];
|
|
661
|
+
let createdPromptCount = 0;
|
|
662
|
+
for (const role of roles) {
|
|
663
|
+
const promptPath = path.resolve(agentsDir, `${role}.md`);
|
|
664
|
+
if (!fs.existsSync(promptPath)) {
|
|
665
|
+
const prompt = getDefaultSystemPrompt({
|
|
666
|
+
role,
|
|
667
|
+
agentId: "{{agentId}}",
|
|
668
|
+
projectName: "{{projectName}}",
|
|
669
|
+
todoFilePath: "{{todoFilePath}}",
|
|
670
|
+
inboxPath: "{{inboxPath}}",
|
|
671
|
+
outboxPath: "{{outboxPath}}",
|
|
672
|
+
workingDirectory: "{{workingDirectory}}",
|
|
673
|
+
techStack: techParts.length > 0 ? techStack : void 0
|
|
674
|
+
});
|
|
675
|
+
const header = [
|
|
676
|
+
`<!-- Maestro Agent Prompt: ${role} -->`,
|
|
677
|
+
`<!-- Edit this file to customise the ${role} agent's behaviour. -->`,
|
|
678
|
+
`<!-- Delete this file to fall back to the built-in default prompt. -->`,
|
|
679
|
+
""
|
|
680
|
+
].join("\n");
|
|
681
|
+
fs.writeFileSync(promptPath, header + prompt + "\n", "utf-8");
|
|
682
|
+
createdPromptCount++;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (createdPromptCount > 0) {
|
|
686
|
+
console.log(` ${chalk.green(figures.tick)} Created ${createdPromptCount} agent prompt files in ${chalk.bold(".agents/")}`);
|
|
687
|
+
}
|
|
688
|
+
const todoPath = path.resolve("todo.md");
|
|
689
|
+
const hasExistingTodo = fs.existsSync(todoPath);
|
|
690
|
+
let existingTasks = [];
|
|
691
|
+
if (hasExistingTodo) {
|
|
692
|
+
const parser = new TaskParser();
|
|
693
|
+
const existing = parser.parse(fs.readFileSync(todoPath, "utf-8"));
|
|
694
|
+
existingTasks = existing.tasks;
|
|
695
|
+
console.log(chalk.cyan(` ${figures.info} Found existing ${chalk.bold("todo.md")} with ${existingTasks.length} tasks`));
|
|
696
|
+
}
|
|
697
|
+
if (options.plan) {
|
|
698
|
+
console.log();
|
|
699
|
+
const planner = new Planner({
|
|
700
|
+
model: options.model,
|
|
701
|
+
workingDirectory: process.cwd(),
|
|
702
|
+
techStack: techParts.length > 0 ? techStack : void 0
|
|
703
|
+
});
|
|
704
|
+
let planGoal = description || `Set up and develop ${projectName}`;
|
|
705
|
+
if (instructionsContent) {
|
|
706
|
+
planGoal = instructionsContent;
|
|
707
|
+
if (description) {
|
|
708
|
+
planGoal = `${description}
|
|
709
|
+
|
|
710
|
+
--- Detailed Instructions ---
|
|
711
|
+
${instructionsContent}`;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
if (detected.hasExistingCode) {
|
|
715
|
+
planGoal += `
|
|
716
|
+
|
|
717
|
+
This is an existing ${detected.type} project. Found config files: ${detected.configFiles.join(", ")}.`;
|
|
718
|
+
planGoal += `
|
|
719
|
+
Build on the existing codebase rather than creating from scratch.`;
|
|
720
|
+
}
|
|
721
|
+
if (existingTasks.length > 0) {
|
|
722
|
+
planGoal += `
|
|
723
|
+
|
|
724
|
+
EXISTING TASKS (do NOT duplicate these \u2014 only create NEW tasks for work not already covered):`;
|
|
725
|
+
for (const t of existingTasks) {
|
|
726
|
+
planGoal += `
|
|
727
|
+
- [${t.status}] ${t.id}: ${t.title}`;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
731
|
+
let spinnerIndex = 0;
|
|
732
|
+
let spinnerActive = true;
|
|
733
|
+
const spinnerInterval = setInterval(() => {
|
|
734
|
+
if (!spinnerActive) return;
|
|
735
|
+
const frame = chalk.cyan(spinnerFrames[spinnerIndex]);
|
|
736
|
+
process.stdout.write(`\r ${frame} ${chalk.dim("Planning tasks with Claude...")}`);
|
|
737
|
+
spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
|
|
738
|
+
}, 80);
|
|
739
|
+
try {
|
|
740
|
+
const plannedTasks = await planner.decompose(planGoal);
|
|
741
|
+
spinnerActive = false;
|
|
742
|
+
clearInterval(spinnerInterval);
|
|
743
|
+
process.stdout.write("\r " + chalk.green(figures.tick) + " " + chalk.dim("Planning tasks with Claude... done") + "\n");
|
|
744
|
+
let maxExistingNum = 0;
|
|
745
|
+
for (const t of existingTasks) {
|
|
746
|
+
const match = t.id.match(/^T-(\d+)$/);
|
|
747
|
+
if (match) {
|
|
748
|
+
maxExistingNum = Math.max(maxExistingNum, parseInt(match[1], 10));
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
resetTaskCounter(maxExistingNum);
|
|
752
|
+
const titleToId = /* @__PURE__ */ new Map();
|
|
753
|
+
for (const t of existingTasks) {
|
|
754
|
+
titleToId.set(t.title, t.id);
|
|
755
|
+
}
|
|
756
|
+
const newTasks = [];
|
|
757
|
+
for (const planned of plannedTasks) {
|
|
758
|
+
const task = plannedToTask(planned);
|
|
759
|
+
task.dependencies = planned.dependencies.map((depTitle) => titleToId.get(depTitle)).filter((id) => id != null);
|
|
760
|
+
titleToId.set(planned.title, task.id);
|
|
761
|
+
newTasks.push(task);
|
|
762
|
+
}
|
|
763
|
+
const allTasks = [...existingTasks, ...newTasks];
|
|
764
|
+
const writer = new TaskWriter();
|
|
765
|
+
const todoContent = writer.write(projectName, allTasks);
|
|
766
|
+
fs.writeFileSync(todoPath, todoContent, "utf-8");
|
|
767
|
+
if (existingTasks.length > 0) {
|
|
768
|
+
console.log(
|
|
769
|
+
chalk.green(` ${figures.tick} Updated ${chalk.bold("todo.md")}: ${existingTasks.length} existing + ${newTasks.length} new tasks`)
|
|
770
|
+
);
|
|
771
|
+
} else {
|
|
772
|
+
console.log(
|
|
773
|
+
chalk.green(` ${figures.tick} Created ${chalk.bold("todo.md")} with ${newTasks.length} planned tasks`)
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
console.log();
|
|
777
|
+
if (newTasks.length > 0) {
|
|
778
|
+
if (existingTasks.length > 0) {
|
|
779
|
+
console.log(chalk.dim(" New tasks:"));
|
|
780
|
+
}
|
|
781
|
+
for (const task of newTasks) {
|
|
782
|
+
const priorityColor = task.priority === "critical" ? chalk.red : task.priority === "high" ? chalk.yellow : task.priority === "medium" ? chalk.cyan : chalk.dim;
|
|
783
|
+
console.log(
|
|
784
|
+
` ${priorityColor(task.id)} ${task.title}`
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
} catch (err) {
|
|
789
|
+
spinnerActive = false;
|
|
790
|
+
clearInterval(spinnerInterval);
|
|
791
|
+
process.stdout.write("\r " + chalk.yellow(figures.warning) + " " + chalk.dim("Planning tasks with Claude... failed") + "\n");
|
|
792
|
+
console.log(
|
|
793
|
+
chalk.yellow(
|
|
794
|
+
` ${figures.warning} Planning failed: ${err.message}`
|
|
795
|
+
)
|
|
796
|
+
);
|
|
797
|
+
if (!hasExistingTodo) {
|
|
798
|
+
writeEmptyTodo(todoPath, projectName);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
} else if (!hasExistingTodo) {
|
|
802
|
+
writeEmptyTodo(todoPath, projectName);
|
|
803
|
+
}
|
|
804
|
+
console.log();
|
|
805
|
+
console.log(chalk.green(" Project initialized successfully!"));
|
|
806
|
+
console.log();
|
|
807
|
+
console.log(chalk.bold(" Process Safety"));
|
|
808
|
+
console.log();
|
|
809
|
+
console.log(chalk.dim(" Agents share your machine's CPU and memory. To keep things stable,"));
|
|
810
|
+
console.log(chalk.dim(" they follow built-in process safety rules:"));
|
|
811
|
+
console.log();
|
|
812
|
+
console.log(` ${chalk.green(figures.tick)} ${chalk.white("Agents will")}:`);
|
|
813
|
+
console.log(chalk.dim(" - Write production code, tests, configs, docs, and infrastructure files"));
|
|
814
|
+
console.log(chalk.dim(" - Run lightweight commands (linters, type-checks on single files)"));
|
|
815
|
+
console.log(chalk.dim(" - Run targeted test files (a single spec, not the full suite)"));
|
|
816
|
+
console.log(chalk.dim(" - Install individual packages (e.g. npm install <pkg>)"));
|
|
817
|
+
console.log();
|
|
818
|
+
console.log(` ${chalk.red(figures.cross)} ${chalk.white("Agents will not")}:`);
|
|
819
|
+
console.log(chalk.dim(" - Run builds (npm run build, cargo build, make, etc.)"));
|
|
820
|
+
console.log(chalk.dim(" - Run Docker commands (docker build, docker compose up, etc.)"));
|
|
821
|
+
console.log(chalk.dim(" - Start dev servers (npm run dev, npm start, flask run, etc.)"));
|
|
822
|
+
console.log(chalk.dim(" - Run full dependency installs (npm install with no args)"));
|
|
823
|
+
console.log(chalk.dim(" - Run full test suites (npm test, pytest with no args)"));
|
|
824
|
+
console.log();
|
|
825
|
+
console.log(` ${chalk.cyan(figures.info)} ${chalk.white("You handle")}:`);
|
|
826
|
+
console.log(chalk.dim(" - Building the project when agents finish writing code"));
|
|
827
|
+
console.log(chalk.dim(" - Running Docker and starting dev servers"));
|
|
828
|
+
console.log(chalk.dim(" - Running full test suites and CI pipelines"));
|
|
829
|
+
console.log(chalk.dim(" - Reviewing and approving agent plans (in supervised mode)"));
|
|
830
|
+
console.log();
|
|
831
|
+
console.log(chalk.dim(` Want full autonomy? Edit the process safety section in ${chalk.bold(".agents/*.md")}`));
|
|
832
|
+
console.log(chalk.dim(` to loosen or remove these restrictions per role.`));
|
|
833
|
+
console.log();
|
|
834
|
+
console.log(` Next steps:`);
|
|
835
|
+
console.log(` 1. Review ${chalk.bold("todo.md")} and edit tasks if needed`);
|
|
836
|
+
console.log(` 2. Edit ${chalk.bold("maestro.yaml")} to adjust agents and settings`);
|
|
837
|
+
console.log(` 3. Customize agent prompts in ${chalk.bold(".agents/")} (optional)`);
|
|
838
|
+
console.log(` 4. Run ${chalk.bold("maestro start")} to begin`);
|
|
839
|
+
if (instructionsContent) {
|
|
840
|
+
console.log();
|
|
841
|
+
console.log(chalk.dim(` Instructions from ${chalk.bold(options.instructions)} were used for task planning.`));
|
|
842
|
+
}
|
|
843
|
+
console.log();
|
|
844
|
+
}
|
|
574
845
|
|
|
575
846
|
// src/commands/start.ts
|
|
576
847
|
import * as fs3 from "fs";
|
|
577
|
-
import * as
|
|
848
|
+
import * as path3 from "path";
|
|
578
849
|
import chalk2 from "chalk";
|
|
579
850
|
import figures2 from "figures";
|
|
580
851
|
import {
|
|
@@ -585,7 +856,7 @@ import {
|
|
|
585
856
|
|
|
586
857
|
// src/logging/file-logger.ts
|
|
587
858
|
import * as fs2 from "fs";
|
|
588
|
-
import * as
|
|
859
|
+
import * as path2 from "path";
|
|
589
860
|
var FileLogger = class {
|
|
590
861
|
logsDir;
|
|
591
862
|
streams = /* @__PURE__ */ new Map();
|
|
@@ -641,7 +912,7 @@ var FileLogger = class {
|
|
|
641
912
|
getOrCreateStream(agentId) {
|
|
642
913
|
let stream = this.streams.get(agentId);
|
|
643
914
|
if (!stream) {
|
|
644
|
-
const logFile =
|
|
915
|
+
const logFile = path2.join(this.logsDir, `${agentId}.log`);
|
|
645
916
|
stream = fs2.createWriteStream(logFile, { flags: "a" });
|
|
646
917
|
this.streams.set(agentId, stream);
|
|
647
918
|
}
|
|
@@ -672,7 +943,7 @@ function registerStartCommand(program) {
|
|
|
672
943
|
program.command("start").description("Start the Maestro orchestrator").option("-c, --config <path>", "path to maestro.yaml", "./maestro.yaml").option("-d, --dir <path>", "working directory for project context", ".").option("--no-tui", "run without TUI (log to console)").option("-m, --mode <mode>", "interaction mode (unattended, supervised, interactive)", "supervised").option("-v, --verbose", "show detailed agent output including tool calls", false).action(async (options) => {
|
|
673
944
|
if (options.dir && options.dir !== ".") {
|
|
674
945
|
try {
|
|
675
|
-
process.chdir(
|
|
946
|
+
process.chdir(path3.resolve(options.dir));
|
|
676
947
|
console.log(chalk2.dim(` Working directory changed to: ${process.cwd()}`));
|
|
677
948
|
} catch (err) {
|
|
678
949
|
console.error(
|
|
@@ -681,7 +952,7 @@ function registerStartCommand(program) {
|
|
|
681
952
|
process.exit(1);
|
|
682
953
|
}
|
|
683
954
|
}
|
|
684
|
-
const configPath =
|
|
955
|
+
const configPath = path3.resolve(options.config);
|
|
685
956
|
if (!fs3.existsSync(configPath)) {
|
|
686
957
|
console.error(
|
|
687
958
|
chalk2.red(`${figures2.cross} Config file not found: ${configPath}`)
|
|
@@ -691,7 +962,7 @@ function registerStartCommand(program) {
|
|
|
691
962
|
);
|
|
692
963
|
process.exit(1);
|
|
693
964
|
}
|
|
694
|
-
const stopFilePath =
|
|
965
|
+
const stopFilePath = path3.resolve(".maestro/stop");
|
|
695
966
|
if (fs3.existsSync(stopFilePath)) {
|
|
696
967
|
fs3.unlinkSync(stopFilePath);
|
|
697
968
|
}
|
|
@@ -1019,7 +1290,7 @@ ${figures2.star} Milestone: ${milestone.name} - ${milestone.message}`));
|
|
|
1019
1290
|
|
|
1020
1291
|
// src/commands/status.ts
|
|
1021
1292
|
import * as fs4 from "fs";
|
|
1022
|
-
import * as
|
|
1293
|
+
import * as path4 from "path";
|
|
1023
1294
|
import chalk3 from "chalk";
|
|
1024
1295
|
import figures3 from "figures";
|
|
1025
1296
|
function parseTodoFile(content) {
|
|
@@ -1079,9 +1350,9 @@ function parseTodoFile(content) {
|
|
|
1079
1350
|
}
|
|
1080
1351
|
function registerStatusCommand(program) {
|
|
1081
1352
|
program.command("status").description("Show current project status").option("-c, --config <path>", "path to maestro.yaml", "./maestro.yaml").action(async (options) => {
|
|
1082
|
-
const todoPath =
|
|
1083
|
-
const stopFilePath =
|
|
1084
|
-
const isRunning = !fs4.existsSync(stopFilePath) && fs4.existsSync(
|
|
1353
|
+
const todoPath = path4.resolve("todo.md");
|
|
1354
|
+
const stopFilePath = path4.resolve(".maestro/stop");
|
|
1355
|
+
const isRunning = !fs4.existsSync(stopFilePath) && fs4.existsSync(path4.resolve(".maestro"));
|
|
1085
1356
|
console.log();
|
|
1086
1357
|
console.log(chalk3.bold(" MAESTRO") + " - Project Status");
|
|
1087
1358
|
console.log();
|
|
@@ -1125,13 +1396,13 @@ function registerStatusCommand(program) {
|
|
|
1125
1396
|
|
|
1126
1397
|
// src/commands/stop.ts
|
|
1127
1398
|
import * as fs5 from "fs";
|
|
1128
|
-
import * as
|
|
1399
|
+
import * as path5 from "path";
|
|
1129
1400
|
import chalk4 from "chalk";
|
|
1130
1401
|
import figures4 from "figures";
|
|
1131
1402
|
function registerStopCommand(program) {
|
|
1132
1403
|
program.command("stop").description("Stop the running Maestro orchestrator").action(async () => {
|
|
1133
|
-
const maestroDir =
|
|
1134
|
-
const stopFilePath =
|
|
1404
|
+
const maestroDir = path5.resolve(".maestro");
|
|
1405
|
+
const stopFilePath = path5.resolve(".maestro/stop");
|
|
1135
1406
|
if (!fs5.existsSync(maestroDir)) {
|
|
1136
1407
|
console.error(
|
|
1137
1408
|
chalk4.red(
|
|
@@ -1169,4 +1440,4 @@ function createProgram() {
|
|
|
1169
1440
|
export {
|
|
1170
1441
|
createProgram
|
|
1171
1442
|
};
|
|
1172
|
-
//# sourceMappingURL=chunk-
|
|
1443
|
+
//# sourceMappingURL=chunk-RWLHK222.js.map
|