claudeboard 2.16.0 → 3.1.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 +89 -93
- package/bin/cli.js +198 -238
- package/bin/init-context.js +22 -0
- package/package.json +22 -43
- package/public/app.js +1411 -0
- package/public/index.html +250 -0
- package/public/style.css +1872 -0
- package/src/context-template.md +20 -0
- package/src/notifier.js +65 -0
- package/src/orchestrator.js +800 -0
- package/src/scanner.js +153 -0
- package/src/server.js +205 -0
- package/src/store.js +182 -0
- package/src/verifier.js +131 -0
- package/agents/architect.js +0 -166
- package/agents/board-client.js +0 -126
- package/agents/claude-api.js +0 -124
- package/agents/claude-resolver.js +0 -167
- package/agents/developer.js +0 -224
- package/agents/expo-health.js +0 -727
- package/agents/orchestrator.js +0 -306
- package/agents/qa.js +0 -336
- package/dashboard/index.html +0 -1980
- package/dashboard/server.js +0 -412
- package/sql/setup.sql +0 -57
- package/tools/filesystem.js +0 -95
- package/tools/screenshot.js +0 -74
- package/tools/supabase-reader.js +0 -74
- package/tools/terminal.js +0 -63
package/bin/cli.js
CHANGED
|
@@ -1,259 +1,219 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// bin/cli.js
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
const PKG_VERSION = '3.1.0';
|
|
8
|
+
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────
|
|
10
|
+
// Helpers
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────
|
|
12
|
+
function header(subtitle) {
|
|
13
|
+
console.log(chalk.bold.hex('#e3c69a')('ClaudeBoard v3 ✦') + (subtitle ? chalk.dim(' ' + subtitle) : ''));
|
|
14
|
+
console.log(chalk.dim('Visual orchestrator for Claude Code agent teams'));
|
|
15
|
+
console.log('');
|
|
16
|
+
}
|
|
2
17
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────
|
|
19
|
+
// claudeboard init
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────
|
|
21
|
+
function runInit() {
|
|
22
|
+
header('init');
|
|
23
|
+
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
const boardDir = path.join(cwd, '.claudeboard');
|
|
26
|
+
const projectName = path.basename(cwd);
|
|
27
|
+
|
|
28
|
+
console.log(chalk.dim('Directorio: ') + chalk.cyan(cwd));
|
|
29
|
+
console.log('');
|
|
30
|
+
|
|
31
|
+
// 1. Create .claudeboard/
|
|
32
|
+
if (!fs.existsSync(boardDir)) {
|
|
33
|
+
fs.mkdirSync(boardDir, { recursive: true });
|
|
34
|
+
console.log(chalk.green('✔') + ' Carpeta .claudeboard/ creada');
|
|
35
|
+
} else {
|
|
36
|
+
console.log(chalk.yellow('~') + ' .claudeboard/ ya existe — no se sobreescribe');
|
|
37
|
+
}
|
|
17
38
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
39
|
+
// 2. Write config.json only if missing
|
|
40
|
+
const configPath = path.join(boardDir, 'config.json');
|
|
41
|
+
if (!fs.existsSync(configPath)) {
|
|
42
|
+
fs.writeFileSync(configPath, JSON.stringify({ projectName, createdAt: new Date().toISOString() }, null, 2));
|
|
43
|
+
console.log(chalk.green('✔') + ' config.json inicializado');
|
|
44
|
+
}
|
|
23
45
|
|
|
24
|
-
|
|
46
|
+
// 3. Update .gitignore
|
|
47
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
48
|
+
const ignoreEntry = '.claudeboard/';
|
|
49
|
+
let gitignored = false;
|
|
25
50
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
if (fs.existsSync(gitignorePath)) {
|
|
52
|
+
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
53
|
+
if (!content.includes(ignoreEntry)) {
|
|
54
|
+
fs.appendFileSync(gitignorePath, `\n# ClaudeBoard — datos del board (tareas, historial, handoffs)\n${ignoreEntry}\n`);
|
|
55
|
+
gitignored = true;
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
fs.writeFileSync(gitignorePath, `# ClaudeBoard — datos del board (tareas, historial, handoffs)\n${ignoreEntry}\n`);
|
|
59
|
+
gitignored = true;
|
|
35
60
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
61
|
+
if (gitignored) console.log(chalk.green('✔') + ' .claudeboard/ agregado al .gitignore');
|
|
62
|
+
else console.log(chalk.yellow('~') + ' .gitignore ya incluye .claudeboard/');
|
|
63
|
+
} catch { /* non-fatal */ }
|
|
64
|
+
|
|
65
|
+
// 4. Create a starter context.md if missing
|
|
66
|
+
const contextPath = path.join(boardDir, 'context.md');
|
|
67
|
+
if (!fs.existsSync(contextPath)) {
|
|
68
|
+
fs.writeFileSync(contextPath, `# Guías de marca / contexto de "${projectName}"\n\n<!-- Opcional: agregá colores, tipografías, tono de voz, reglas del proyecto. -->\n<!-- Los agentes lo leerán antes de trabajar. -->\n`);
|
|
69
|
+
console.log(chalk.green('✔') + ' .claudeboard/context.md creado (editalo con tus guías de marca)');
|
|
40
70
|
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// ─── INIT ────────────────────────────────────────────────────────────────────
|
|
44
|
-
program
|
|
45
|
-
.command("init")
|
|
46
|
-
.description("Initialize claudeboard in a project")
|
|
47
|
-
.action(async () => {
|
|
48
|
-
console.log(LOGO);
|
|
49
|
-
const { default: Enquirer } = await import("enquirer");
|
|
50
|
-
const enquirer = new Enquirer();
|
|
51
|
-
console.log(chalk.bold("Let's set up your AI engineering team — powered by High Value, LLC.\n"));
|
|
52
|
-
|
|
53
|
-
const answers = await enquirer.prompt([
|
|
54
|
-
{ type: "input", name: "projectName", message: "Project name:", initial: path.basename(process.cwd()) },
|
|
55
|
-
{
|
|
56
|
-
type: "select",
|
|
57
|
-
name: "appType",
|
|
58
|
-
message: "App type:",
|
|
59
|
-
choices: ["mobile (Expo / React Native)", "web (Vite + React)"],
|
|
60
|
-
},
|
|
61
|
-
{ type: "input", name: "supabaseUrl", message: "Supabase URL:", hint: "https://xxxx.supabase.co" },
|
|
62
|
-
{ type: "input", name: "supabaseKey", message: "Supabase anon key:" },
|
|
63
|
-
{ type: "password", name: "anthropicKey", message: "Anthropic API key:", hint: "sk-ant-..." },
|
|
64
|
-
{ type: "input", name: "port", message: "Dashboard port:", initial: "3131" },
|
|
65
|
-
]);
|
|
66
|
-
|
|
67
|
-
const spinner = ora("Creating config...").start();
|
|
68
|
-
|
|
69
|
-
const config = {
|
|
70
|
-
projectName: answers.projectName,
|
|
71
|
-
appType: answers.appType.startsWith("mobile") ? "mobile" : "web",
|
|
72
|
-
port: parseInt(answers.port),
|
|
73
|
-
supabaseUrl: answers.supabaseUrl,
|
|
74
|
-
supabaseKey: answers.supabaseKey,
|
|
75
|
-
anthropicKey: answers.anthropicKey,
|
|
76
|
-
projectDir: process.cwd(),
|
|
77
|
-
createdAt: new Date().toISOString(),
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
fs.writeFileSync(path.join(process.cwd(), ".claudeboard.json"), JSON.stringify(config, null, 2));
|
|
81
|
-
|
|
82
|
-
const sqlSrc = path.join(__dirname, "../sql/setup.sql");
|
|
83
|
-
fs.copyFileSync(sqlSrc, path.join(process.cwd(), "claudeboard-setup.sql"));
|
|
84
|
-
|
|
85
|
-
spinner.succeed(chalk.green("Config created!"));
|
|
86
|
-
|
|
87
|
-
console.log(`
|
|
88
|
-
${chalk.bold("Next steps:")}
|
|
89
|
-
|
|
90
|
-
${chalk.cyan("1.")} Run the SQL in your Supabase SQL Editor:
|
|
91
|
-
${chalk.dim("→ claudeboard-setup.sql")}
|
|
92
|
-
|
|
93
|
-
${chalk.cyan("2.")} Start the dashboard:
|
|
94
|
-
${chalk.bold("claudeboard start")}
|
|
95
|
-
|
|
96
|
-
${chalk.cyan("3.")} Run the AI team with your PRD:
|
|
97
|
-
${chalk.bold("claudeboard run --prd ./PRD.md --project ./your-app")}
|
|
98
|
-
`);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// ─── START DASHBOARD ─────────────────────────────────────────────────────────
|
|
102
|
-
program
|
|
103
|
-
.command("start")
|
|
104
|
-
.description("Start the dashboard server")
|
|
105
|
-
.option("-p, --port <port>", "Port number")
|
|
106
|
-
.action(async (opts) => {
|
|
107
|
-
console.log(LOGO);
|
|
108
|
-
const config = loadConfig();
|
|
109
|
-
const port = opts.port || config.port || 3131;
|
|
110
|
-
const spinner = ora("Starting dashboard...").start();
|
|
111
|
-
|
|
112
|
-
const serverPath = path.join(__dirname, "../dashboard/server.js");
|
|
113
|
-
const server = spawn("node", [serverPath], {
|
|
114
|
-
env: {
|
|
115
|
-
...process.env,
|
|
116
|
-
SUPABASE_URL: config.supabaseUrl,
|
|
117
|
-
SUPABASE_KEY: config.supabaseKey,
|
|
118
|
-
ANTHROPIC_API_KEY: config.anthropicKey || process.env.ANTHROPIC_API_KEY || "",
|
|
119
|
-
PORT: String(port),
|
|
120
|
-
PROJECT_NAME: config.projectName,
|
|
121
|
-
PROJECT_DIR: config.projectDir || process.cwd(),
|
|
122
|
-
},
|
|
123
|
-
stdio: "pipe",
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
server.stdout.on("data", (data) => {
|
|
127
|
-
if (data.toString().includes("READY")) {
|
|
128
|
-
spinner.succeed(chalk.green("Dashboard running!"));
|
|
129
|
-
console.log(`\n ${chalk.bold("→")} ${chalk.cyan(`http://localhost:${port}`)}\n`);
|
|
130
|
-
console.log(chalk.dim(" Press Ctrl+C to stop\n"));
|
|
131
|
-
open(`http://localhost:${port}`);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
server.stderr.on("data", (d) => console.error(chalk.red(d.toString())));
|
|
136
|
-
process.on("SIGINT", () => { server.kill(); process.exit(0); });
|
|
137
|
-
});
|
|
138
71
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
.
|
|
142
|
-
|
|
143
|
-
.requiredOption("--prd <path>", "Path to PRD markdown file")
|
|
144
|
-
.requiredOption("--project <path>", "Path to your app project directory")
|
|
145
|
-
.option("--restart", "Force restart from scratch (ignore existing tasks in board)")
|
|
146
|
-
.option("--expo-port <port>", "Expo Web port for QA screenshots", "8081")
|
|
147
|
-
.option("--ios", "Use iOS Simulator instead of Expo Web for QA (requires Xcode)")
|
|
148
|
-
.action(async (opts) => {
|
|
149
|
-
console.log(LOGO);
|
|
150
|
-
const config = loadConfig();
|
|
151
|
-
|
|
152
|
-
if (!fs.existsSync(opts.prd)) {
|
|
153
|
-
console.log(chalk.red(`PRD not found: ${opts.prd}`)); process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
if (!fs.existsSync(opts.project)) {
|
|
156
|
-
console.log(chalk.red(`Project not found: ${opts.project}`)); process.exit(1);
|
|
157
|
-
}
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(chalk.bold('¡Listo! ') + chalk.dim('Corré ') + chalk.cyan('claudeboard') + chalk.dim(' para iniciar el board en este proyecto.'));
|
|
74
|
+
console.log('');
|
|
75
|
+
}
|
|
158
76
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
77
|
+
// ─────────────────────────────────────────────────────────────────
|
|
78
|
+
// Team selection prompt
|
|
79
|
+
// ─────────────────────────────────────────────────────────────────
|
|
80
|
+
const TEAMS = [
|
|
81
|
+
{ label: '🧑💻 Solo Dev ', desc: '1 agente, enfocado, bajo costo de tokens', maxAgents: 1 },
|
|
82
|
+
{ label: '👥 Small Team ', desc: '3 agentes (Lead + 2 especialistas)', maxAgents: 3 },
|
|
83
|
+
{ label: '🚀 Full Team ', desc: '6 agentes (Lead + 5 especialistas)', maxAgents: 6 },
|
|
84
|
+
{ label: '🏭 Enterprise ', desc: '9 agentes (Lead + 8 especialistas, máx paralelismo)', maxAgents: 9 },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
function drawMenu(teams, selected, lineCount) {
|
|
88
|
+
const lines = [chalk.bold('? ') + 'Seleccioná tu equipo de agentes:'];
|
|
89
|
+
for (let i = 0; i < teams.length; i++) {
|
|
90
|
+
const t = teams[i];
|
|
91
|
+
const cursor = i === selected ? chalk.cyan('❯') : ' ';
|
|
92
|
+
const label = i === selected ? chalk.bold(t.label) : chalk.dim(t.label);
|
|
93
|
+
lines.push(` ${cursor} ${label} ${chalk.dim(t.desc)}`);
|
|
94
|
+
}
|
|
95
|
+
if (lineCount > 0) process.stdout.write(`\x1b[${lineCount}A\x1b[0J`);
|
|
96
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
97
|
+
return lines.length;
|
|
98
|
+
}
|
|
165
99
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
100
|
+
function promptTeamSelection(skipMaxAgents) {
|
|
101
|
+
if (skipMaxAgents !== undefined || !process.stdin.isTTY) return Promise.resolve(null);
|
|
102
|
+
|
|
103
|
+
return new Promise((resolve) => {
|
|
104
|
+
let selected = 0;
|
|
105
|
+
let lineCount = drawMenu(TEAMS, selected, 0);
|
|
106
|
+
|
|
107
|
+
process.stdin.setRawMode(true);
|
|
108
|
+
process.stdin.resume();
|
|
109
|
+
process.stdin.setEncoding('utf8');
|
|
110
|
+
|
|
111
|
+
function onKey(key) {
|
|
112
|
+
if (key === '\x1b[A') { selected = Math.max(0, selected - 1); lineCount = drawMenu(TEAMS, selected, lineCount); }
|
|
113
|
+
else if (key === '\x1b[B') { selected = Math.min(TEAMS.length - 1, selected + 1); lineCount = drawMenu(TEAMS, selected, lineCount); }
|
|
114
|
+
else if (key === '\r' || key === '\n') {
|
|
115
|
+
process.stdin.removeListener('data', onKey);
|
|
116
|
+
process.stdin.setRawMode(false);
|
|
117
|
+
process.stdin.pause();
|
|
118
|
+
process.stdout.write(`\x1b[${lineCount}A\x1b[0J`);
|
|
119
|
+
const team = TEAMS[selected];
|
|
120
|
+
process.stdout.write(chalk.bold('✔ ') + 'Equipo: ' + chalk.cyan(team.label.trim()) + chalk.dim(' — ' + team.desc) + '\n');
|
|
121
|
+
resolve(team);
|
|
122
|
+
} else if (key === '\x03') {
|
|
123
|
+
process.stdin.setRawMode(false);
|
|
124
|
+
process.exit(0);
|
|
174
125
|
}
|
|
175
|
-
console.log();
|
|
176
|
-
process.exit(1);
|
|
177
126
|
}
|
|
178
|
-
|
|
179
|
-
const version = execSync(`"${claudePath}" --version`, { stdio: "pipe" }).toString().trim();
|
|
180
|
-
console.log(chalk.dim(` Claude Code → ${version} (${claudePath})\n`));
|
|
181
|
-
} catch {
|
|
182
|
-
console.log(chalk.dim(` Claude Code → ${claudePath}\n`));
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const resolvedProject = path.resolve(opts.project);
|
|
186
|
-
|
|
187
|
-
// Persist the project path so `claudeboard start` uses the right directory for Expo
|
|
188
|
-
const configPath = path.join(process.cwd(), ".claudeboard.json");
|
|
189
|
-
try {
|
|
190
|
-
const saved = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
191
|
-
saved.projectDir = resolvedProject;
|
|
192
|
-
fs.writeFileSync(configPath, JSON.stringify(saved, null, 2));
|
|
193
|
-
} catch {}
|
|
194
|
-
|
|
195
|
-
const { runOrchestrator } = await import("../agents/orchestrator.js");
|
|
196
|
-
|
|
197
|
-
await runOrchestrator({
|
|
198
|
-
prdPath: path.resolve(opts.prd),
|
|
199
|
-
projectPath: resolvedProject,
|
|
200
|
-
supabaseUrl: config.supabaseUrl,
|
|
201
|
-
supabaseKey: config.supabaseKey,
|
|
202
|
-
projectName: config.projectName,
|
|
203
|
-
appType: config.appType || "mobile",
|
|
204
|
-
expoPort: parseInt(opts.expoPort),
|
|
205
|
-
forceRestart: !!opts.restart,
|
|
206
|
-
useIOS: !!opts.ios,
|
|
207
|
-
});
|
|
127
|
+
process.stdin.on('data', onKey);
|
|
208
128
|
});
|
|
129
|
+
}
|
|
209
130
|
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
131
|
+
// ─────────────────────────────────────────────────────────────────
|
|
132
|
+
// claudeboard start (default when no subcommand)
|
|
133
|
+
// ─────────────────────────────────────────────────────────────────
|
|
134
|
+
async function runStart(rawArgs) {
|
|
135
|
+
// Parse simple flags: --port, --open, --webhook, --max-agents
|
|
136
|
+
const args = rawArgs || [];
|
|
137
|
+
const getFlag = (name, shortName, def) => {
|
|
138
|
+
const longIdx = args.indexOf(`--${name}`);
|
|
139
|
+
const shortIdx = shortName ? args.indexOf(`-${shortName}`) : -1;
|
|
140
|
+
const idx = longIdx !== -1 ? longIdx : shortIdx;
|
|
141
|
+
if (idx !== -1 && args[idx + 1] !== undefined) return args[idx + 1];
|
|
142
|
+
// --name=value form
|
|
143
|
+
const pair = args.find(a => a.startsWith(`--${name}=`));
|
|
144
|
+
if (pair) return pair.split('=')[1];
|
|
145
|
+
return def;
|
|
146
|
+
};
|
|
147
|
+
const flagExists = (name) => args.includes(`--${name}`);
|
|
148
|
+
|
|
149
|
+
const port = parseInt(getFlag('port', 'p', '3000'), 10) || 3000;
|
|
150
|
+
const openBrowser = getFlag('open', null, 'true') !== 'false';
|
|
151
|
+
const webhook = getFlag('webhook', null, null);
|
|
152
|
+
const maxAgentsRaw = getFlag('max-agents', null, undefined);
|
|
153
|
+
|
|
154
|
+
// ── Auto-init if .claudeboard/ doesn't exist ──────────────────
|
|
155
|
+
const cwd = process.cwd();
|
|
156
|
+
const boardDir = path.join(cwd, '.claudeboard');
|
|
157
|
+
if (!fs.existsSync(boardDir)) {
|
|
158
|
+
console.log(chalk.dim('No se encontró .claudeboard/ — inicializando automáticamente…'));
|
|
159
|
+
console.log('');
|
|
160
|
+
runInit();
|
|
161
|
+
}
|
|
225
162
|
|
|
226
|
-
|
|
227
|
-
|
|
163
|
+
header();
|
|
164
|
+
console.log(chalk.dim('Proyecto: ') + chalk.cyan(path.basename(cwd)) + chalk.dim(' (' + cwd + ')'));
|
|
165
|
+
console.log('');
|
|
228
166
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
167
|
+
const team = await promptTeamSelection(maxAgentsRaw);
|
|
168
|
+
const maxAgents = maxAgentsRaw !== undefined
|
|
169
|
+
? (parseInt(maxAgentsRaw, 10) || 1)
|
|
170
|
+
: (team ? team.maxAgents : 1);
|
|
232
171
|
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
.description("Show current board status in terminal")
|
|
237
|
-
.action(async () => {
|
|
238
|
-
const config = loadConfig();
|
|
239
|
-
const { initBoard, getStats } = await import("../agents/board-client.js");
|
|
240
|
-
initBoard(config.supabaseUrl, config.supabaseKey, config.projectName);
|
|
172
|
+
// Store minimal config (no projectPath — it's always process.cwd())
|
|
173
|
+
const { setConfig } = require(path.join(__dirname, '..', 'src', 'store'));
|
|
174
|
+
if (webhook) setConfig({ webhook });
|
|
241
175
|
|
|
242
|
-
|
|
243
|
-
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log(chalk.dim(`Iniciando en puerto ${port}…`));
|
|
178
|
+
console.log('');
|
|
244
179
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
console.log(` [${chalk.green(bar)}] ${chalk.bold(stats.pct + "%")}`);
|
|
248
|
-
console.log(`\n ${chalk.green("✓ Done:")} ${stats.done}`);
|
|
249
|
-
console.log(` ${chalk.yellow("◌ Running:")} ${stats.in_progress}`);
|
|
250
|
-
console.log(` ${chalk.dim("○ Todo:")} ${stats.todo}`);
|
|
251
|
-
console.log(` ${chalk.red("✗ Error:")} ${stats.error}\n`);
|
|
252
|
-
});
|
|
180
|
+
const { createServer } = require(path.join(__dirname, '..', 'src', 'server'));
|
|
181
|
+
createServer({ port, maxAgents, webhook, openBrowser });
|
|
253
182
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
.description("AI engineering team — from PRD to working app, autonomously — powered by High Value, LLC")
|
|
257
|
-
.version(_pkg.version);
|
|
183
|
+
console.log(chalk.dim('Ctrl+C para detener'));
|
|
184
|
+
}
|
|
258
185
|
|
|
259
|
-
|
|
186
|
+
// ─────────────────────────────────────────────────────────────────
|
|
187
|
+
// Entry point — dispatch subcommand
|
|
188
|
+
// ─────────────────────────────────────────────────────────────────
|
|
189
|
+
const args = process.argv.slice(2);
|
|
190
|
+
const subcommand = args[0];
|
|
191
|
+
|
|
192
|
+
if (subcommand === '--version' || subcommand === '-v') {
|
|
193
|
+
console.log(PKG_VERSION);
|
|
194
|
+
} else if (subcommand === '--help' || subcommand === '-h') {
|
|
195
|
+
console.log(chalk.bold.hex('#e3c69a')('ClaudeBoard v3 ✦') + chalk.dim(' v' + PKG_VERSION));
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log(chalk.bold('Uso:'));
|
|
198
|
+
console.log(' claudeboard init Inicializa .claudeboard/ en el proyecto actual');
|
|
199
|
+
console.log(' claudeboard Inicia el board (en el directorio actual)');
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log(chalk.bold('Opciones de start:'));
|
|
202
|
+
console.log(' -p, --port <n> Puerto (default: 3000)');
|
|
203
|
+
console.log(' --max-agents <n> Cantidad de agentes paralelos (evita el prompt)');
|
|
204
|
+
console.log(' --open false No abrir el browser automáticamente');
|
|
205
|
+
console.log(' --webhook <url> URL de webhook para notificaciones');
|
|
206
|
+
console.log('');
|
|
207
|
+
console.log(chalk.bold('Ejemplo — múltiples proyectos en simultáneo:'));
|
|
208
|
+
console.log(chalk.dim(' cd ~/projects/app-a && claudeboard --port 3000'));
|
|
209
|
+
console.log(chalk.dim(' cd ~/projects/app-b && claudeboard --port 3001'));
|
|
210
|
+
} else if (subcommand === 'init') {
|
|
211
|
+
runInit();
|
|
212
|
+
} else if (!subcommand || subcommand === 'start' || subcommand.startsWith('-')) {
|
|
213
|
+
// No subcommand or "start" or flags → default to start
|
|
214
|
+
runStart(args.filter(a => a !== 'start'));
|
|
215
|
+
} else {
|
|
216
|
+
console.log(chalk.red(`Subcomando desconocido: "${subcommand}"`));
|
|
217
|
+
console.log(chalk.dim('Corré `claudeboard --help` para ver los comandos disponibles.'));
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/init-context.js — copies context template to .claudeboard/context.md in the current project
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const targetDir = path.join(process.cwd(), '.claudeboard');
|
|
7
|
+
const targetFile = path.join(targetDir, 'context.md');
|
|
8
|
+
const templateFile = path.join(__dirname, '..', 'src', 'context-template.md');
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(targetDir)) {
|
|
11
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (fs.existsSync(targetFile)) {
|
|
15
|
+
console.log(`Already exists: ${targetFile}`);
|
|
16
|
+
console.log('Edit it directly to customize your project context.');
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
fs.copyFileSync(templateFile, targetFile);
|
|
21
|
+
console.log(`Created: ${targetFile}`);
|
|
22
|
+
console.log('Edit .claudeboard/context.md to add your project brand guidelines and design tokens.');
|
package/package.json
CHANGED
|
@@ -1,57 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudeboard",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"type": "module",
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "Visual orchestrator for Claude Code agent teams",
|
|
6
5
|
"bin": {
|
|
7
|
-
"claudeboard": "
|
|
6
|
+
"claudeboard": "bin/cli.js"
|
|
8
7
|
},
|
|
9
8
|
"files": [
|
|
10
9
|
"bin/",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"tools/",
|
|
14
|
-
"sql/",
|
|
10
|
+
"src/",
|
|
11
|
+
"public/",
|
|
15
12
|
"README.md"
|
|
16
13
|
],
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"@supabase/supabase-js": "^2.43.1",
|
|
20
|
-
"chalk": "^5.3.0",
|
|
21
|
-
"commander": "^12.0.0",
|
|
22
|
-
"cors": "^2.8.5",
|
|
23
|
-
"dotenv": "^16.4.5",
|
|
24
|
-
"enquirer": "^2.4.1",
|
|
25
|
-
"express": "^4.18.3",
|
|
26
|
-
"node-pty": "^1.0.0",
|
|
27
|
-
"open": "^10.1.0",
|
|
28
|
-
"ora": "^8.0.1",
|
|
29
|
-
"puppeteer": "^22.8.0",
|
|
30
|
-
"ws": "^8.17.0"
|
|
31
|
-
},
|
|
32
|
-
"engines": {
|
|
33
|
-
"node": ">=18.0.0"
|
|
34
|
-
},
|
|
35
|
-
"publishConfig": {
|
|
36
|
-
"access": "public"
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node bin/cli.js"
|
|
37
16
|
},
|
|
38
|
-
"repository": {
|
|
39
|
-
"type": "git",
|
|
40
|
-
"url": "https://github.com/highvaluedigital/claudeboard"
|
|
41
|
-
},
|
|
42
|
-
"author": "highvaluedigital",
|
|
43
|
-
"license": "MIT",
|
|
44
17
|
"keywords": [
|
|
45
18
|
"claude",
|
|
46
19
|
"ai",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"automation",
|
|
20
|
+
"orchestrator",
|
|
21
|
+
"agents",
|
|
50
22
|
"dashboard",
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
23
|
+
"kanban"
|
|
24
|
+
],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"commander": "^12.0.0",
|
|
32
|
+
"express": "^4.18.2",
|
|
33
|
+
"open": "^10.1.0",
|
|
34
|
+
"ws": "^8.16.0"
|
|
35
|
+
}
|
|
57
36
|
}
|