spectoflow 0.15.0 → 0.16.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 +13 -4
- package/bin/spectoflow.js +202 -30
- package/package.json +1 -1
- package/templates/README.md +5 -1
- package/templates/skills/clarify/SKILL.md +19 -2
package/README.md
CHANGED
|
@@ -38,13 +38,22 @@ Every command works both ways — `spectoflow <cmd>` when installed globally, or
|
|
|
38
38
|
### CLI
|
|
39
39
|
|
|
40
40
|
```
|
|
41
|
-
spectoflow init [dir] [--agent=claude,codex] scaffold a project (auto-detects
|
|
41
|
+
spectoflow init [dir] [--agent=claude,codex] scaffold a project (auto-detects agents; wires Playwright MCP)
|
|
42
42
|
spectoflow update [--dry-run] refresh framework files to this kit version
|
|
43
|
-
spectoflow dashboard [--port=NNNN] run the local control plane (default 4319)
|
|
44
|
-
spectoflow dashboard stop (or: stop) stop the running dashboard
|
|
45
43
|
spectoflow status progress + whether the dashboard is running
|
|
44
|
+
|
|
45
|
+
spectoflow dashboard [--port=NNNN] start the control plane in the background (hands the prompt back)
|
|
46
|
+
spectoflow dashboard status is it running? (url + pid)
|
|
47
|
+
spectoflow dashboard stop (or: stop) stop the running dashboard
|
|
48
|
+
spectoflow dashboard restart stop then start
|
|
49
|
+
|
|
50
|
+
spectoflow list agents, skills and the workflow at a glance
|
|
51
|
+
spectoflow agents list the team personas
|
|
52
|
+
spectoflow skills list the procedures
|
|
53
|
+
spectoflow workflow show the enabled pipeline steps
|
|
54
|
+
|
|
46
55
|
spectoflow --version (-v) print the version
|
|
47
|
-
spectoflow --help (-h) show help
|
|
56
|
+
spectoflow --help (-h) show help (append -h to any command for its help)
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
`init` scaffolds:
|
package/bin/spectoflow.js
CHANGED
|
@@ -22,6 +22,60 @@ const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
|
22
22
|
const paint = (code) => (s) => (useColor ? `\x1b[${code}m${s}\x1b[0m` : String(s));
|
|
23
23
|
const c = { g: paint('32'), cy: paint('36'), b: paint('34'), y: paint('33'), dim: paint('2'), bold: paint('1'), amber: paint('38;5;179') };
|
|
24
24
|
|
|
25
|
+
// ---- branding ---------------------------------------------------------------
|
|
26
|
+
const LOGO = [
|
|
27
|
+
'┌─┐┌─┐┌─┐┌─┐┌┬┐┌─┐┌─┐┬ ┌─┐┬ ┬',
|
|
28
|
+
'└─┐├─┘├┤ │ │ │ │├┤ │ │ ││││',
|
|
29
|
+
'└─┘┴ └─┘└─┘ ┴ └─┘┴ ┴─┘└─┘└┴┘',
|
|
30
|
+
];
|
|
31
|
+
const TAGLINE = 'agent-agnostic spec-driven development · real-time control plane';
|
|
32
|
+
// Full logo block — for init and help (the moments a human is reading, not scripting).
|
|
33
|
+
const banner = () => `\n${LOGO.map((l) => ' ' + c.amber(l)).join('\n')}\n ${c.dim('v' + VERSION + ' · ' + TAGLINE)}\n`;
|
|
34
|
+
// One-line brand — for the header of secondary command outputs.
|
|
35
|
+
const brandLine = () => `${c.amber('spectoflow')} ${c.dim('v' + VERSION)}`;
|
|
36
|
+
|
|
37
|
+
// ---- framework introspection (list agents / skills / workflow) --------------
|
|
38
|
+
// Read from the project's .spectoflow when present, else the bundled kit — so `list` works anywhere.
|
|
39
|
+
function frameworkSource() {
|
|
40
|
+
const local = path.resolve('.spectoflow');
|
|
41
|
+
return fs.existsSync(local) ? { dir: local, scope: 'project' } : { dir: TPL, scope: 'kit' };
|
|
42
|
+
}
|
|
43
|
+
// Tiny frontmatter reader — a few scalar keys, no YAML dependency.
|
|
44
|
+
function frontmatter(file) {
|
|
45
|
+
let txt = '';
|
|
46
|
+
try { txt = fs.readFileSync(file, 'utf8'); } catch { return {}; }
|
|
47
|
+
const m = txt.match(/^---\n([\s\S]*?)\n---/);
|
|
48
|
+
if (!m) return {};
|
|
49
|
+
const out = {};
|
|
50
|
+
for (const line of m[1].split('\n')) {
|
|
51
|
+
const mm = line.match(/^([A-Za-z_]+):\s*(.*)$/);
|
|
52
|
+
if (mm) out[mm[1]] = mm[2].replace(/^["']|["']$/g, '').trim();
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
function listAgents(dir) {
|
|
57
|
+
const d = path.join(dir, 'agents');
|
|
58
|
+
if (!fs.existsSync(d)) return [];
|
|
59
|
+
return fs.readdirSync(d).filter((f) => f.endsWith('.md')).map((f) => {
|
|
60
|
+
const fm = frontmatter(path.join(d, f));
|
|
61
|
+
return { name: fm.name || f.replace(/\.md$/, ''), capability: fm.capability || '', description: fm.description || '' };
|
|
62
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
63
|
+
}
|
|
64
|
+
function listSkills(dir) {
|
|
65
|
+
const d = path.join(dir, 'skills');
|
|
66
|
+
if (!fs.existsSync(d)) return [];
|
|
67
|
+
return fs.readdirSync(d, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => {
|
|
68
|
+
const fm = frontmatter(path.join(d, e.name, 'SKILL.md'));
|
|
69
|
+
return { name: fm.name || e.name, capability: fm.capability || '', description: fm.description || '' };
|
|
70
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
71
|
+
}
|
|
72
|
+
function readWorkflowSteps(dir) {
|
|
73
|
+
let txt = '';
|
|
74
|
+
try { txt = fs.readFileSync(path.join(dir, 'workflow.md'), 'utf8'); } catch { return []; }
|
|
75
|
+
return txt.split('\n').map((l) => l.match(/^- \[([ xX])\]\s+(.+?)\s*(\{.*\})?\s*$/))
|
|
76
|
+
.filter(Boolean).map((m) => ({ on: m[1].toLowerCase() === 'x', name: m[2] }));
|
|
77
|
+
}
|
|
78
|
+
|
|
25
79
|
// ---- dashboard port + running-state probe ------------------------------------
|
|
26
80
|
// Precedence: --port=NNNN > SPECTOFLOW_PORT env > 4319 (matches templates/dashboard/server.js).
|
|
27
81
|
function resolvePort(args) {
|
|
@@ -154,16 +208,18 @@ function init() {
|
|
|
154
208
|
if (!giText.includes(line)) fs.appendFileSync(gi, ((fs.existsSync(gi) && fs.readFileSync(gi, 'utf8').length) ? '\n' : '') + line + '\n');
|
|
155
209
|
}
|
|
156
210
|
|
|
157
|
-
console.log(
|
|
158
|
-
console.log(
|
|
159
|
-
console.log('
|
|
160
|
-
|
|
161
|
-
|
|
211
|
+
console.log(banner());
|
|
212
|
+
console.log(`${c.g('✓')} installed in ${c.bold(target)}`);
|
|
213
|
+
console.log(` ${c.dim('.spectoflow/')} framework — brain, workflow, agents, skills, policy, dashboard, config`);
|
|
214
|
+
console.log(` ${c.dim('specs/ plans/')} markdown artifacts (your source of truth)`);
|
|
215
|
+
written.forEach((w) => console.log(` ${c.cy('+')} ${w}`));
|
|
216
|
+
notes.forEach((n) => console.log(` ${c.y('!')} ${c.dim(n)}`));
|
|
162
217
|
const port = resolvePort(argv);
|
|
163
|
-
console.log('
|
|
164
|
-
console.log('
|
|
165
|
-
console.log('
|
|
166
|
-
console.log(`
|
|
218
|
+
console.log(`\n${c.bold('Next')}`);
|
|
219
|
+
console.log(` ${c.dim('1)')} Open your agent here — or just say what you want to build.`);
|
|
220
|
+
console.log(` ${c.dim('2)')} ${c.g('spectoflow dashboard')} ${c.dim('→ http://localhost:' + port)}`);
|
|
221
|
+
console.log(` ${c.dim('3)')} ${c.g('spectoflow list')} ${c.dim('see the agents, skills & workflow you got')}`);
|
|
222
|
+
console.log('');
|
|
167
223
|
}
|
|
168
224
|
|
|
169
225
|
function update() {
|
|
@@ -182,7 +238,7 @@ function update() {
|
|
|
182
238
|
const detail = note ? c.dim(note) : c.dim(list.slice(0, 6).join(', ') + (list.length > 6 ? ` +${list.length - 6} more` : ''));
|
|
183
239
|
console.log(` ${sym} ${painter(label.padEnd(9))} ${n} ${detail}`);
|
|
184
240
|
};
|
|
185
|
-
console.log(
|
|
241
|
+
console.log(banner());
|
|
186
242
|
console.log(` ${c.bold('spectoflow update')} ${c.dim(from)} ${c.amber('→')} ${c.bold(r.toVersion)}${dryRun ? c.dim(' (dry-run)') : ''}`);
|
|
187
243
|
console.log('');
|
|
188
244
|
row(c.g('✓'), 'refreshed', r.refreshed, c.g);
|
|
@@ -198,21 +254,55 @@ function update() {
|
|
|
198
254
|
console.log('');
|
|
199
255
|
}
|
|
200
256
|
|
|
201
|
-
// THE launch command —
|
|
202
|
-
//
|
|
257
|
+
// THE launch command — routes the subcommands, then starts. Starting spawns the server DETACHED and
|
|
258
|
+
// hands the prompt straight back (no foreground blocking), then prints the commands to drive it.
|
|
203
259
|
async function dashboard() {
|
|
204
|
-
|
|
260
|
+
const sub = argv[1];
|
|
261
|
+
if (sub === 'stop') return stopDashboard();
|
|
262
|
+
if (sub === 'status') return dashboardStatus();
|
|
263
|
+
if (sub === 'restart') return restartDashboard();
|
|
264
|
+
return startDashboard();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Start in the background and return control. Probes first so a second start just reports the running
|
|
268
|
+
// one instead of spawning a duplicate (and never crashes on EADDRINUSE).
|
|
269
|
+
async function startDashboard() {
|
|
205
270
|
const port = resolvePort(argv);
|
|
206
271
|
const url = `http://localhost:${port}`;
|
|
207
272
|
if (await probeDashboard(port)) {
|
|
208
|
-
console.log(
|
|
209
|
-
return;
|
|
273
|
+
console.log(`${c.g('●')} dashboard already running → ${c.bold(url)}`);
|
|
274
|
+
return printDashboardCommands();
|
|
210
275
|
}
|
|
211
276
|
const local = path.resolve('.spectoflow', 'dashboard', 'server.js');
|
|
212
277
|
const bundled = path.join(TPL, 'dashboard', 'server.js');
|
|
213
278
|
const env = Object.assign({}, process.env, { SPECTOFLOW_PORT: String(port) });
|
|
214
|
-
spawn('node', [fs.existsSync(local) ? local : bundled], { stdio: '
|
|
215
|
-
|
|
279
|
+
const child = spawn('node', [fs.existsSync(local) ? local : bundled], { detached: true, stdio: 'ignore', env });
|
|
280
|
+
child.unref(); // let this CLI exit while the server keeps running
|
|
281
|
+
console.log(`${c.g('✓')} dashboard started → ${c.bold(url)} ${c.dim('(pid ' + child.pid + ')')}`);
|
|
282
|
+
printDashboardCommands();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function printDashboardCommands() {
|
|
286
|
+
console.log('');
|
|
287
|
+
console.log(` ${c.dim('status ')} ${c.g('spectoflow dashboard status')} ${c.dim('is it up? (url + pid)')}`);
|
|
288
|
+
console.log(` ${c.dim('stop ')} ${c.g('spectoflow dashboard stop')} ${c.dim('(alias: spectoflow stop)')}`);
|
|
289
|
+
console.log(` ${c.dim('restart')} ${c.g('spectoflow dashboard restart')} ${c.dim('stop then start')}`);
|
|
290
|
+
console.log('');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function dashboardStatus() {
|
|
294
|
+
const port = resolvePort(argv);
|
|
295
|
+
const running = await probeDashboard(port);
|
|
296
|
+
let pid = null;
|
|
297
|
+
try { pid = JSON.parse(fs.readFileSync(path.join(process.cwd(), '.spectoflow', '.dashboard.lock'), 'utf8')).pid; } catch {}
|
|
298
|
+
if (running) console.log(`${c.g('●')} dashboard running → ${c.bold('http://localhost:' + port)}${pid ? c.dim(' (pid ' + pid + ')') : ''}`);
|
|
299
|
+
else console.log(`${c.dim('○')} dashboard not running`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function restartDashboard() {
|
|
303
|
+
await stopDashboard();
|
|
304
|
+
await new Promise((r) => setTimeout(r, 400)); // let the port free up before rebinding
|
|
305
|
+
return startDashboard();
|
|
216
306
|
}
|
|
217
307
|
|
|
218
308
|
// Stop the running dashboard: read the pidfile it wrote, verify it's actually up, then terminate it
|
|
@@ -259,25 +349,107 @@ async function status() {
|
|
|
259
349
|
|
|
260
350
|
function version() { console.log(`spectoflow v${VERSION}`); }
|
|
261
351
|
|
|
262
|
-
|
|
352
|
+
// ---- explore commands -------------------------------------------------------
|
|
353
|
+
function printAgents(withBrand = true) {
|
|
354
|
+
const { dir, scope } = frameworkSource();
|
|
355
|
+
const rows = listAgents(dir);
|
|
356
|
+
if (withBrand) console.log(`${brandLine()} ${c.dim('· agents (' + scope + ')')}`);
|
|
357
|
+
const w = Math.max(4, ...rows.map((r) => r.name.length));
|
|
358
|
+
rows.forEach((r) => console.log(` ${c.g(r.name.padEnd(w))} ${c.dim((r.capability || '').padEnd(14))} ${r.description}`));
|
|
359
|
+
if (!rows.length) console.log(c.dim(' (none found)'));
|
|
360
|
+
}
|
|
361
|
+
function printSkills(withBrand = true) {
|
|
362
|
+
const { dir, scope } = frameworkSource();
|
|
363
|
+
const rows = listSkills(dir);
|
|
364
|
+
if (withBrand) console.log(`${brandLine()} ${c.dim('· skills (' + scope + ')')}`);
|
|
365
|
+
const w = Math.max(4, ...rows.map((r) => r.name.length));
|
|
366
|
+
rows.forEach((r) => console.log(` ${c.cy(r.name.padEnd(w))} ${c.dim((r.capability || '').padEnd(14))} ${r.description}`));
|
|
367
|
+
if (!rows.length) console.log(c.dim(' (none found)'));
|
|
368
|
+
}
|
|
369
|
+
function printWorkflow(withBrand = true) {
|
|
370
|
+
const { dir, scope } = frameworkSource();
|
|
371
|
+
const steps = readWorkflowSteps(dir);
|
|
372
|
+
if (withBrand) console.log(`${brandLine()} ${c.dim('· workflow (' + scope + ')')}`);
|
|
373
|
+
steps.forEach((s) => console.log(` ${s.on ? c.g('●') : c.dim('○')} ${s.on ? s.name : c.dim(s.name + ' (disabled)')}`));
|
|
374
|
+
if (!steps.length) console.log(c.dim(' (no workflow.md)'));
|
|
375
|
+
}
|
|
376
|
+
function listAll() {
|
|
377
|
+
const { scope } = frameworkSource();
|
|
378
|
+
console.log(banner());
|
|
379
|
+
console.log(`${c.bold('Agents')} ${c.dim('— stable team personas (' + scope + ')')}`);
|
|
380
|
+
printAgents(false);
|
|
381
|
+
console.log(`\n${c.bold('Skills')} ${c.dim('— evolving procedures')}`);
|
|
382
|
+
printSkills(false);
|
|
383
|
+
console.log(`\n${c.bold('Workflow')} ${c.dim('— enabled pipeline steps')}`);
|
|
384
|
+
printWorkflow(false);
|
|
385
|
+
console.log('');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ---- help (global + per-command) --------------------------------------------
|
|
389
|
+
const help = () => console.log(`${banner()}
|
|
390
|
+
${c.dim('Usage:')} spectoflow ${c.g('<command>')} ${c.dim('[options]')} ${c.dim('· append -h to any command for its help')}
|
|
263
391
|
|
|
264
|
-
${c.
|
|
392
|
+
${c.bold('Project')}
|
|
393
|
+
${c.g('init')} ${c.dim('[dir] [--agent=a,b]')} scaffold a project (auto-detects agents; wires Playwright MCP)
|
|
394
|
+
${c.g('update')} ${c.dim('[--dry-run]')} refresh framework files to this kit version
|
|
395
|
+
${c.g('status')} progress + whether the dashboard is running
|
|
265
396
|
|
|
266
|
-
${c.bold('
|
|
267
|
-
${c.g('
|
|
268
|
-
${c.g('
|
|
269
|
-
${c.g('dashboard')}
|
|
270
|
-
${c.g('dashboard
|
|
271
|
-
${c.g('status')} print progress + whether the dashboard is running
|
|
397
|
+
${c.bold('Dashboard')}
|
|
398
|
+
${c.g('dashboard')} ${c.dim('[--port=NNNN]')} start the control plane in the background (default 4319)
|
|
399
|
+
${c.g('dashboard status')} is it running? (url + pid)
|
|
400
|
+
${c.g('dashboard stop')} stop it ${c.dim('(alias: stop)')}
|
|
401
|
+
${c.g('dashboard restart')} stop then start
|
|
272
402
|
|
|
273
|
-
${c.bold('
|
|
274
|
-
|
|
275
|
-
|
|
403
|
+
${c.bold('Explore')}
|
|
404
|
+
${c.g('list')} agents, skills and the workflow at a glance
|
|
405
|
+
${c.g('agents')} list the team personas
|
|
406
|
+
${c.g('skills')} list the procedures
|
|
407
|
+
${c.g('workflow')} show the enabled pipeline steps
|
|
408
|
+
|
|
409
|
+
${c.bold('Options')}
|
|
410
|
+
${c.g('-v')}, ${c.g('--version')} print the version
|
|
411
|
+
${c.g('-h')}, ${c.g('--help')} show this help
|
|
276
412
|
|
|
277
413
|
${c.dim('Docs:')} https://github.com/georgesmomo/spectoflow`);
|
|
278
414
|
|
|
279
|
-
|
|
415
|
+
// Per-command help — shown when -h/--help follows a command (e.g. `spectoflow dashboard -h`).
|
|
416
|
+
const HELP = {
|
|
417
|
+
init: `${c.bold('spectoflow init')} ${c.dim('[dir] [--agent=a,b]')}\n
|
|
418
|
+
Scaffold spectoflow into <dir> (default: current directory).
|
|
419
|
+
Auto-detects installed agents (${c.dim('claude, codex, cursor, gemini')}) and writes their entry
|
|
420
|
+
shims; override with ${c.g('--agent=claude,codex')}. Also wires ${c.bold('Playwright MCP')} into the
|
|
421
|
+
project's ${c.dim('.mcp.json')} (idempotent — never touches an existing entry).
|
|
422
|
+
${c.dim('An existing CLAUDE.md is preserved as CLAUDE.md.tomerge for you to merge on first run.')}`,
|
|
423
|
+
update: `${c.bold('spectoflow update')} ${c.dim('[--dry-run]')}\n
|
|
424
|
+
Refresh framework-owned files (engine, dashboard, default agents & skills, AGENTS.md, policy…)
|
|
425
|
+
to this CLI's version, ${c.bold('preserving your work')}: config.json, workflow.md, specs/, plans/
|
|
426
|
+
and any agent/skill you edited are never overwritten (an edited file's new version lands as
|
|
427
|
+
${c.dim('*.new')} for you to merge). ${c.g('--dry-run')} previews without writing.`,
|
|
428
|
+
dashboard: `${c.bold('spectoflow dashboard')} ${c.dim('[--port=NNNN] [status|stop|restart]')}\n
|
|
429
|
+
Start the local control plane in the ${c.bold('background')} (default ${c.dim('4319')} or
|
|
430
|
+
${c.dim('$SPECTOFLOW_PORT')}) and hand the prompt back. Subcommands:
|
|
431
|
+
${c.g('status')} is it running? (url + pid)
|
|
432
|
+
${c.g('stop')} stop it ${c.dim('(alias: spectoflow stop)')}
|
|
433
|
+
${c.g('restart')} stop then start`,
|
|
434
|
+
status: `${c.bold('spectoflow status')}\n
|
|
435
|
+
Print project progress from ${c.dim('plans/*.md')} (tasks done, specs, agents, skills, in-progress
|
|
436
|
+
items) and whether the dashboard is currently running.`,
|
|
437
|
+
list: `${c.bold('spectoflow list')}\n
|
|
438
|
+
Show the ${c.g('agents')}, ${c.cy('skills')} and ${c.bold('workflow')} of the current project
|
|
439
|
+
(or the bundled kit when run outside a project) at a glance.`,
|
|
440
|
+
agents: `${c.bold('spectoflow agents')}\n List the stable team personas (name · capability · role).`,
|
|
441
|
+
skills: `${c.bold('spectoflow skills')}\n List the evolving procedures (name · capability · what it does).`,
|
|
442
|
+
workflow: `${c.bold('spectoflow workflow')}\n Show the pipeline steps, marking which are enabled (●) or disabled (○).`,
|
|
443
|
+
stop: `${c.bold('spectoflow stop')}\n Stop the running dashboard (alias for ${c.g('spectoflow dashboard stop')}).`,
|
|
444
|
+
};
|
|
445
|
+
const showHelp = (name) => console.log('\n' + HELP[name].trim() + '\n');
|
|
446
|
+
|
|
447
|
+
// ---- dispatch ---------------------------------------------------------------
|
|
448
|
+
const fns = { init, update, dashboard, stop: stopDashboard, status, list: listAll, agents: () => printAgents(), skills: () => printSkills(), workflow: () => printWorkflow(), help, version };
|
|
449
|
+
const wantsHelp = argv.slice(1).some((a) => a === '-h' || a === '--help');
|
|
450
|
+
|
|
280
451
|
if (['-v', '-V', '--version', 'version'].includes(cmd)) version();
|
|
281
|
-
else if (['-h', '--help'].includes(cmd)) help();
|
|
452
|
+
else if (['-h', '--help', 'help'].includes(cmd)) help();
|
|
453
|
+
else if (fns[cmd] && wantsHelp && HELP[cmd]) showHelp(cmd);
|
|
282
454
|
else if (fns[cmd]) fns[cmd]();
|
|
283
455
|
else help();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spectoflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Agent-agnostic spec-driven development framework + real-time local control plane. Markdown artifacts, intent router, workflow-by-scope.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spec-driven-development",
|
package/templates/README.md
CHANGED
|
@@ -18,12 +18,16 @@ sit at the project root and just point back here.
|
|
|
18
18
|
order-taker: on an ambiguous request ("login displays badly") it reflects it back and asks **one
|
|
19
19
|
targeted question at a time** (each with a recommendation) until the need is crisp, then executes
|
|
20
20
|
(skill `clarify`, wired into the agent's memory in `AGENTS.md`).
|
|
21
|
-
- **Watch it live** in the dashboard:
|
|
21
|
+
- **Watch it live** in the dashboard (it starts in the background and hands the prompt back):
|
|
22
22
|
```
|
|
23
23
|
spectoflow dashboard # → http://localhost:4319 (or: node .spectoflow/dashboard/server.js)
|
|
24
|
+
spectoflow dashboard status # is it running? (url + pid)
|
|
24
25
|
spectoflow dashboard stop # stop it (alias: spectoflow stop)
|
|
26
|
+
spectoflow dashboard restart # stop then start
|
|
25
27
|
spectoflow status # progress + whether the dashboard is running
|
|
26
28
|
```
|
|
29
|
+
- **See what you got:** `spectoflow list` (agents, skills & workflow at a glance), or `spectoflow
|
|
30
|
+
agents` / `spectoflow skills` / `spectoflow workflow`. Append `-h` to any command for its help.
|
|
27
31
|
- **Change how it runs** in the dashboard's **Settings** tab (autonomy mode, output language, and the
|
|
28
32
|
dashboard **design**), or by editing `config.json`.
|
|
29
33
|
- **Update the framework** to a newer kit: `spectoflow update` (preserves your edits; a file you
|
|
@@ -25,8 +25,11 @@ Whenever a request is ambiguous or under-specified and acting on it would mean g
|
|
|
25
25
|
Skip it when the request is already unambiguous and testable — over-questioning is its own failure.
|
|
26
26
|
|
|
27
27
|
## Method — reflect, then one question at a time
|
|
28
|
-
1. **
|
|
29
|
-
|
|
28
|
+
1. **Acknowledge, then reflect it back — warmly and naturally.** Open by showing you've taken the
|
|
29
|
+
request on board, then lead into the clarification as a way to serve it *better* — never as an
|
|
30
|
+
interrogation. Restate the request in one sentence, name the goal as you understand it, and surface
|
|
31
|
+
your assumptions so a wrong one is easy to correct. Vary the wording every time (see **Tone**);
|
|
32
|
+
never a fixed opener, never a heavy bordered form.
|
|
30
33
|
2. **Ask ONE question — the highest-value one first.** The single question that most reduces
|
|
31
34
|
uncertainty about what to build. Carry **your recommended default and a one-line reason** ("I'd
|
|
32
35
|
assume the layout breaks on mobile, since that's the common case — is that it?"). Prefer a small set
|
|
@@ -44,6 +47,20 @@ Skip it when the request is already unambiguous and testable — over-questionin
|
|
|
44
47
|
6. **Then hand off** the confirmed need to the normal Router flow (Classify → Gate → Load → Run), or to
|
|
45
48
|
`brainstorm` / `analyze-requirements` for a new build. Clarify **replaces nothing** downstream.
|
|
46
49
|
|
|
50
|
+
## Tone — natural and immersive, never templated
|
|
51
|
+
Sound like a thoughtful colleague, not a form. Acknowledge the ask, then segue into the question as a
|
|
52
|
+
way to get it right — in your own words **each time**. The *spirit* is fixed (acknowledge → reflect →
|
|
53
|
+
one question, with a recommendation); the *phrasing* is never fixed. Do **not** copy these sentences
|
|
54
|
+
verbatim — they only show the register:
|
|
55
|
+
> "Got it — I'm with you on <request>. One thing I'd like to nail down before I start: …"
|
|
56
|
+
> "Taken on board. To make sure I build the right thing rather than guess: …"
|
|
57
|
+
> "Understood. So I aim at the real need here, quick check: …"
|
|
58
|
+
|
|
59
|
+
Match the depth to the request: a small tweak needs at most a light one-line confirm; a genuinely
|
|
60
|
+
complex or new-build request, once the core intent is clear, simply **flows into the normal path**
|
|
61
|
+
(`brainstorm` → analysis → spec → plan) — you already know that road, so don't keep interrogating.
|
|
62
|
+
Keep it light: prose and a question, not a bordered panel of options unless options genuinely help.
|
|
63
|
+
|
|
47
64
|
## Guardrails
|
|
48
65
|
- **One question at a time** — never a wall of questions. This is the whole point.
|
|
49
66
|
- **Only ask what changes the outcome.** If an answer wouldn't change what you'd do, don't ask it.
|