claudeboard 2.15.4 → 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 -230
- 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 -141
- 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 -584
- package/agents/orchestrator.js +0 -303
- 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,251 +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
|
-
{ type: "input", name: "supabaseUrl", message: "Supabase URL:", hint: "https://xxxx.supabase.co" },
|
|
56
|
-
{ type: "input", name: "supabaseKey", message: "Supabase anon key:" },
|
|
57
|
-
{ type: "password", name: "anthropicKey", message: "Anthropic API key:", hint: "sk-ant-..." },
|
|
58
|
-
{ type: "input", name: "port", message: "Dashboard port:", initial: "3131" },
|
|
59
|
-
]);
|
|
60
|
-
|
|
61
|
-
const spinner = ora("Creating config...").start();
|
|
62
|
-
|
|
63
|
-
const config = {
|
|
64
|
-
projectName: answers.projectName,
|
|
65
|
-
port: parseInt(answers.port),
|
|
66
|
-
supabaseUrl: answers.supabaseUrl,
|
|
67
|
-
supabaseKey: answers.supabaseKey,
|
|
68
|
-
anthropicKey: answers.anthropicKey,
|
|
69
|
-
projectDir: process.cwd(),
|
|
70
|
-
createdAt: new Date().toISOString(),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
fs.writeFileSync(path.join(process.cwd(), ".claudeboard.json"), JSON.stringify(config, null, 2));
|
|
74
|
-
|
|
75
|
-
const sqlSrc = path.join(__dirname, "../sql/setup.sql");
|
|
76
|
-
fs.copyFileSync(sqlSrc, path.join(process.cwd(), "claudeboard-setup.sql"));
|
|
77
|
-
|
|
78
|
-
spinner.succeed(chalk.green("Config created!"));
|
|
79
|
-
|
|
80
|
-
console.log(`
|
|
81
|
-
${chalk.bold("Next steps:")}
|
|
82
|
-
|
|
83
|
-
${chalk.cyan("1.")} Run the SQL in your Supabase SQL Editor:
|
|
84
|
-
${chalk.dim("→ claudeboard-setup.sql")}
|
|
85
|
-
|
|
86
|
-
${chalk.cyan("2.")} Start the dashboard:
|
|
87
|
-
${chalk.bold("claudeboard start")}
|
|
88
|
-
|
|
89
|
-
${chalk.cyan("3.")} Run the AI team with your PRD:
|
|
90
|
-
${chalk.bold("claudeboard run --prd ./PRD.md --project ./your-app")}
|
|
91
|
-
`);
|
|
92
|
-
});
|
|
93
71
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.
|
|
97
|
-
|
|
98
|
-
.option("-p, --port <port>", "Port number")
|
|
99
|
-
.action(async (opts) => {
|
|
100
|
-
console.log(LOGO);
|
|
101
|
-
const config = loadConfig();
|
|
102
|
-
const port = opts.port || config.port || 3131;
|
|
103
|
-
const spinner = ora("Starting dashboard...").start();
|
|
104
|
-
|
|
105
|
-
const serverPath = path.join(__dirname, "../dashboard/server.js");
|
|
106
|
-
const server = spawn("node", [serverPath], {
|
|
107
|
-
env: {
|
|
108
|
-
...process.env,
|
|
109
|
-
SUPABASE_URL: config.supabaseUrl,
|
|
110
|
-
SUPABASE_KEY: config.supabaseKey,
|
|
111
|
-
ANTHROPIC_API_KEY: config.anthropicKey || process.env.ANTHROPIC_API_KEY || "",
|
|
112
|
-
PORT: String(port),
|
|
113
|
-
PROJECT_NAME: config.projectName,
|
|
114
|
-
PROJECT_DIR: config.projectDir || process.cwd(),
|
|
115
|
-
},
|
|
116
|
-
stdio: "pipe",
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
server.stdout.on("data", (data) => {
|
|
120
|
-
if (data.toString().includes("READY")) {
|
|
121
|
-
spinner.succeed(chalk.green("Dashboard running!"));
|
|
122
|
-
console.log(`\n ${chalk.bold("→")} ${chalk.cyan(`http://localhost:${port}`)}\n`);
|
|
123
|
-
console.log(chalk.dim(" Press Ctrl+C to stop\n"));
|
|
124
|
-
open(`http://localhost:${port}`);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
server.stderr.on("data", (d) => console.error(chalk.red(d.toString())));
|
|
129
|
-
process.on("SIGINT", () => { server.kill(); process.exit(0); });
|
|
130
|
-
});
|
|
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
|
+
}
|
|
131
76
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
+
}
|
|
151
99
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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);
|
|
167
125
|
}
|
|
168
|
-
console.log();
|
|
169
|
-
process.exit(1);
|
|
170
126
|
}
|
|
171
|
-
|
|
172
|
-
const version = execSync(`"${claudePath}" --version`, { stdio: "pipe" }).toString().trim();
|
|
173
|
-
console.log(chalk.dim(` Claude Code → ${version} (${claudePath})\n`));
|
|
174
|
-
} catch {
|
|
175
|
-
console.log(chalk.dim(` Claude Code → ${claudePath}\n`));
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const resolvedProject = path.resolve(opts.project);
|
|
179
|
-
|
|
180
|
-
// Persist the project path so `claudeboard start` uses the right directory for Expo
|
|
181
|
-
const configPath = path.join(process.cwd(), ".claudeboard.json");
|
|
182
|
-
try {
|
|
183
|
-
const saved = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
184
|
-
saved.projectDir = resolvedProject;
|
|
185
|
-
fs.writeFileSync(configPath, JSON.stringify(saved, null, 2));
|
|
186
|
-
} catch {}
|
|
187
|
-
|
|
188
|
-
const { runOrchestrator } = await import("../agents/orchestrator.js");
|
|
189
|
-
|
|
190
|
-
await runOrchestrator({
|
|
191
|
-
prdPath: path.resolve(opts.prd),
|
|
192
|
-
projectPath: resolvedProject,
|
|
193
|
-
supabaseUrl: config.supabaseUrl,
|
|
194
|
-
supabaseKey: config.supabaseKey,
|
|
195
|
-
projectName: config.projectName,
|
|
196
|
-
expoPort: parseInt(opts.expoPort),
|
|
197
|
-
forceRestart: !!opts.restart,
|
|
198
|
-
useIOS: !!opts.ios,
|
|
199
|
-
});
|
|
127
|
+
process.stdin.on('data', onKey);
|
|
200
128
|
});
|
|
129
|
+
}
|
|
201
130
|
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
+
}
|
|
209
162
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
163
|
+
header();
|
|
164
|
+
console.log(chalk.dim('Proyecto: ') + chalk.cyan(path.basename(cwd)) + chalk.dim(' (' + cwd + ')'));
|
|
165
|
+
console.log('');
|
|
213
166
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
167
|
+
const team = await promptTeamSelection(maxAgentsRaw);
|
|
168
|
+
const maxAgents = maxAgentsRaw !== undefined
|
|
169
|
+
? (parseInt(maxAgentsRaw, 10) || 1)
|
|
170
|
+
: (team ? team.maxAgents : 1);
|
|
217
171
|
|
|
218
|
-
|
|
219
|
-
|
|
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 });
|
|
220
175
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log(chalk.dim(`Iniciando en puerto ${port}…`));
|
|
178
|
+
console.log('');
|
|
224
179
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
.command("status")
|
|
228
|
-
.description("Show current board status in terminal")
|
|
229
|
-
.action(async () => {
|
|
230
|
-
const config = loadConfig();
|
|
231
|
-
const { initBoard, getStats } = await import("../agents/board-client.js");
|
|
232
|
-
initBoard(config.supabaseUrl, config.supabaseKey, config.projectName);
|
|
233
|
-
|
|
234
|
-
const stats = await getStats();
|
|
235
|
-
const bar = "█".repeat(Math.floor(stats.pct / 5)) + "░".repeat(20 - Math.floor(stats.pct / 5));
|
|
236
|
-
|
|
237
|
-
console.log(LOGO);
|
|
238
|
-
console.log(chalk.bold(` ${config.projectName}\n`));
|
|
239
|
-
console.log(` [${chalk.green(bar)}] ${chalk.bold(stats.pct + "%")}`);
|
|
240
|
-
console.log(`\n ${chalk.green("✓ Done:")} ${stats.done}`);
|
|
241
|
-
console.log(` ${chalk.yellow("◌ Running:")} ${stats.in_progress}`);
|
|
242
|
-
console.log(` ${chalk.dim("○ Todo:")} ${stats.todo}`);
|
|
243
|
-
console.log(` ${chalk.red("✗ Error:")} ${stats.error}\n`);
|
|
244
|
-
});
|
|
180
|
+
const { createServer } = require(path.join(__dirname, '..', 'src', 'server'));
|
|
181
|
+
createServer({ port, maxAgents, webhook, openBrowser });
|
|
245
182
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
.description("AI engineering team — from PRD to working app, autonomously — powered by High Value, LLC")
|
|
249
|
-
.version(_pkg.version);
|
|
183
|
+
console.log(chalk.dim('Ctrl+C para detener'));
|
|
184
|
+
}
|
|
250
185
|
|
|
251
|
-
|
|
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
|
}
|