brain-dev 0.2.0 → 0.3.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 CHANGED
@@ -106,6 +106,35 @@ Brain creates a plan, executes it, and commits — all in one flow. No discuss,
106
106
 
107
107
  Quick tasks live in `.brain/quick/`, separate from phases. Tracked in STATE.md.
108
108
 
109
+ ## Significant Tasks
110
+
111
+ For features that need more than a quick fix but less than a new project:
112
+
113
+ ```bash
114
+ /brain:new-task "add payment integration with Stripe"
115
+ ```
116
+
117
+ Brain runs the full pipeline: discuss → plan → execute → verify → complete.
118
+
119
+ ```bash
120
+ /brain:new-task --research "add rate limiting" # With light research
121
+ /brain:new-task --light "fix auth redirect" # Skip discuss + verify
122
+ /brain:new-task --continue # Resume current task
123
+ /brain:new-task --list # List all tasks
124
+ ```
125
+
126
+ After completion, promote to your roadmap:
127
+
128
+ ```bash
129
+ /brain:new-task --promote 1 # Adds task as a phase in ROADMAP.md
130
+ ```
131
+
132
+ | Mode | Pipeline |
133
+ |------|----------|
134
+ | Standard | discuss → plan → execute → verify → complete |
135
+ | `--light` | plan → execute → commit |
136
+ | `--research` | discuss → research → plan → execute → verify → complete |
137
+
109
138
  ## State Machine
110
139
 
111
140
  Brain tracks progress through a deterministic state machine:
@@ -146,6 +175,7 @@ No supply chain risk. No transitive vulnerabilities. No `node_modules` bloat.
146
175
  | `/brain:verify` | 3-level verification against must-haves |
147
176
  | `/brain:complete` | Mark phase done, advance to next |
148
177
  | `/brain:quick` | Quick task — skip the ceremony |
178
+ | `/brain:new-task` | Significant task — full pipeline without a new project |
149
179
 
150
180
  ### Session
151
181
  | Command | Description |
@@ -132,6 +132,10 @@ async function main() {
132
132
  await require('./lib/commands/quick.cjs').run(args.slice(1));
133
133
  break;
134
134
 
135
+ case 'new-task':
136
+ await require('./lib/commands/new-task.cjs').run(args.slice(1));
137
+ break;
138
+
135
139
  case 'execute':
136
140
  if (args.includes('--auto') || args.includes('--dry-run') || args.includes('--stop')) {
137
141
  await require('./lib/commands/auto.cjs').run(args.slice(1));
@@ -0,0 +1,522 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { parseArgs } = require('node:util');
6
+ const { readState, writeState } = require('../state.cjs');
7
+ const { logEvent } = require('../logger.cjs');
8
+ const { output, error, prefix } = require('../core.cjs');
9
+ const { recordInvocation, estimateTokens } = require('../cost.cjs');
10
+
11
+ async function run(args = [], opts = {}) {
12
+ const { values, positionals } = parseArgs({
13
+ args,
14
+ options: {
15
+ research: { type: 'boolean', default: false },
16
+ light: { type: 'boolean', default: false },
17
+ continue: { type: 'boolean', default: false },
18
+ list: { type: 'boolean', default: false },
19
+ promote: { type: 'string' },
20
+ json: { type: 'boolean', default: false }
21
+ },
22
+ strict: false,
23
+ allowPositionals: true
24
+ });
25
+
26
+ const brainDir = opts.brainDir || path.join(process.cwd(), '.brain');
27
+ const state = readState(brainDir);
28
+
29
+ if (!state || !state.project?.initialized) {
30
+ error("Project not initialized. Run '/brain:new-project' first.");
31
+ return { error: 'not-initialized' };
32
+ }
33
+
34
+ // Handle --list
35
+ if (values.list) return handleList(brainDir, state);
36
+
37
+ // Handle --promote N
38
+ if (values.promote) {
39
+ const taskNum = parseInt(values.promote, 10);
40
+ if (isNaN(taskNum) || taskNum < 1) {
41
+ error("--promote requires a valid task number. Usage: brain-dev new-task --promote <N>");
42
+ return { error: 'invalid-task-number' };
43
+ }
44
+ return handlePromote(brainDir, state, taskNum);
45
+ }
46
+
47
+ // Handle --continue (resume current task)
48
+ if (values.continue) return handleContinue(brainDir, state);
49
+
50
+ // New task creation
51
+ const description = positionals.join(' ').trim();
52
+ if (!description) {
53
+ error("Usage: brain-dev new-task \"description\" [--research] [--light]");
54
+ return { error: 'no-description' };
55
+ }
56
+
57
+ return handleNewTask(brainDir, state, description, values);
58
+ }
59
+
60
+ function handleNewTask(brainDir, state, description, options) {
61
+ // Create task directory and metadata
62
+ const taskNum = (state.tasks?.count || 0) + 1;
63
+ const slug = description
64
+ .toLowerCase()
65
+ .replace(/[^a-z0-9]+/g, '-')
66
+ .replace(/^-|-$/g, '')
67
+ .slice(0, 40) || 'task';
68
+
69
+ const taskDir = path.join(brainDir, 'tasks', `${String(taskNum).padStart(2, '0')}-${slug}`);
70
+ fs.mkdirSync(taskDir, { recursive: true });
71
+
72
+ const mode = options.light ? 'light' : 'standard';
73
+ const taskMeta = {
74
+ num: taskNum,
75
+ slug,
76
+ description,
77
+ created: new Date().toISOString(),
78
+ status: 'initialized',
79
+ mode,
80
+ research: options.research || false,
81
+ parent_phase: state.phase?.current || null,
82
+ promoted_to_phase: null,
83
+ completed: null
84
+ };
85
+
86
+ fs.writeFileSync(path.join(taskDir, 'task.json'), JSON.stringify(taskMeta, null, 2));
87
+
88
+ // Update state
89
+ state.tasks = state.tasks || { current: null, count: 0, active: [], history: [] };
90
+ state.tasks.current = taskNum;
91
+ state.tasks.count = taskNum;
92
+ state.tasks.active.push({ num: taskNum, slug, status: 'initialized', description });
93
+ writeState(brainDir, state);
94
+
95
+ logEvent(brainDir, 0, { type: 'task-init', task: taskNum, slug, description, mode });
96
+
97
+ // Determine first step
98
+ const firstStep = mode === 'light' ? 'plan' : 'discuss';
99
+
100
+ // Build step instructions
101
+ const steps = buildStepInstructions(taskNum, slug, taskDir, brainDir, mode, options.research, description);
102
+
103
+ const humanLines = [
104
+ prefix(`Task #${taskNum} created: ${description}`),
105
+ prefix(`Mode: ${mode}${options.research ? ' + research' : ''}`),
106
+ prefix(`Directory: .brain/tasks/${String(taskNum).padStart(2, '0')}-${slug}/`),
107
+ '',
108
+ prefix(`Pipeline: ${mode === 'light' ? 'plan -> execute -> commit' : 'discuss -> plan -> execute -> verify -> complete'}`),
109
+ '',
110
+ prefix(`Next step: ${firstStep}`),
111
+ '',
112
+ steps[firstStep]
113
+ ].join('\n');
114
+
115
+ const result = {
116
+ action: 'task-created',
117
+ task: taskMeta,
118
+ nextStep: firstStep,
119
+ taskDir,
120
+ steps,
121
+ instructions: steps[firstStep]
122
+ };
123
+
124
+ output(result, humanLines);
125
+ return result;
126
+ }
127
+
128
+ function buildStepInstructions(taskNum, slug, taskDir, brainDir, mode, research, description) {
129
+ const padded = String(taskNum).padStart(2, '0');
130
+ const steps = {};
131
+
132
+ // DISCUSS step
133
+ steps.discuss = [
134
+ `## Step 1: Discuss — Task #${taskNum}`,
135
+ '',
136
+ `Discuss the architectural approach for: "${description}"`,
137
+ '',
138
+ 'Questions to explore:',
139
+ '1. What is the best approach for this task?',
140
+ '2. Are there existing patterns in the codebase to follow?',
141
+ '3. What are the risks or edge cases?',
142
+ '4. What dependencies does this have?',
143
+ '',
144
+ 'After discussion, save decisions to:',
145
+ `\`${taskDir}/CONTEXT.md\``,
146
+ '',
147
+ 'Then run: `npx brain-dev new-task --continue`'
148
+ ].join('\n');
149
+
150
+ // RESEARCH step (optional)
151
+ if (research) {
152
+ steps.research = [
153
+ `## Step 1.5: Research — Task #${taskNum}`,
154
+ '',
155
+ `Research the approach for: "${description}"`,
156
+ '',
157
+ 'Focus on:',
158
+ '1. How does the existing codebase handle similar features?',
159
+ '2. What are the best practices for this type of change?',
160
+ '',
161
+ 'Save findings to:',
162
+ `\`${taskDir}/RESEARCH.md\``,
163
+ '',
164
+ 'Then run: `npx brain-dev new-task --continue`'
165
+ ].join('\n');
166
+ }
167
+
168
+ // PLAN step
169
+ steps.plan = [
170
+ `## Step 2: Plan — Task #${taskNum}`,
171
+ '',
172
+ `Create an execution plan for: "${description}"`,
173
+ '',
174
+ 'Constraints:',
175
+ '- Max 3-4 tasks (this is a focused task, not a full phase)',
176
+ '- Target ~50% context budget',
177
+ '- Each task should be atomic and testable',
178
+ '- Include must_haves for verification',
179
+ '',
180
+ `Read context: \`${taskDir}/CONTEXT.md\` (if exists)`,
181
+ research ? `Read research: \`${taskDir}/RESEARCH.md\` (if exists)` : '',
182
+ '',
183
+ `Write plan to: \`${taskDir}/PLAN-1.md\``,
184
+ 'Use standard Brain plan format with YAML frontmatter.',
185
+ '',
186
+ 'Then run: `npx brain-dev new-task --continue`'
187
+ ].filter(Boolean).join('\n');
188
+
189
+ // EXECUTE step
190
+ steps.execute = [
191
+ `## Step 3: Execute — Task #${taskNum}`,
192
+ '',
193
+ `Execute the plan at: \`${taskDir}/PLAN-1.md\``,
194
+ '',
195
+ 'Instructions:',
196
+ '- Follow the plan tasks sequentially',
197
+ '- Make atomic commits per task',
198
+ '- If a task fails, retry once then escalate',
199
+ '',
200
+ `Write summary to: \`${taskDir}/SUMMARY-1.md\``,
201
+ 'Include: key files modified, decisions made, Self-Check status.',
202
+ '',
203
+ 'Then run: `npx brain-dev new-task --continue`'
204
+ ].join('\n');
205
+
206
+ // VERIFY step
207
+ if (mode !== 'light') {
208
+ steps.verify = [
209
+ `## Step 4: Verify — Task #${taskNum}`,
210
+ '',
211
+ `Verify the execution results against must_haves in: \`${taskDir}/PLAN-1.md\``,
212
+ '',
213
+ 'Verification levels:',
214
+ '1. EXISTS: Files exist and are non-empty',
215
+ '2. SUBSTANTIVE: Contains real implementation (no stubs)',
216
+ '3. WIRED: Properly imported and consumed',
217
+ '',
218
+ `Write results to: \`${taskDir}/VERIFICATION.md\``,
219
+ '',
220
+ 'Then run: `npx brain-dev new-task --continue`'
221
+ ].join('\n');
222
+ }
223
+
224
+ // COMPLETE step
225
+ steps.complete = [
226
+ `## Step 5: Complete — Task #${taskNum}`,
227
+ '',
228
+ 'Task completion options:',
229
+ '',
230
+ '**Option A: Keep as standalone task**',
231
+ '`npx brain-dev new-task --continue` (will auto-complete)',
232
+ '',
233
+ '**Option B: Promote to roadmap phase**',
234
+ `\`npx brain-dev new-task --promote ${taskNum}\``,
235
+ 'This adds the task as a new phase in ROADMAP.md.',
236
+ '',
237
+ 'Commit suggestion:',
238
+ `\`git add -A && git commit -m "feat(task-${padded}): ${slug}"\``
239
+ ].join('\n');
240
+
241
+ return steps;
242
+ }
243
+
244
+ function handleContinue(brainDir, state) {
245
+ const taskNum = state.tasks?.current;
246
+ if (!taskNum) {
247
+ error("No active task. Start one with: brain-dev new-task \"description\"");
248
+ return { error: 'no-active-task' };
249
+ }
250
+
251
+ // Find task directory
252
+ const tasksDir = path.join(brainDir, 'tasks');
253
+ if (!fs.existsSync(tasksDir)) {
254
+ error("No tasks directory found.");
255
+ return { error: 'no-tasks-dir' };
256
+ }
257
+
258
+ const padded = String(taskNum).padStart(2, '0');
259
+ const dirs = fs.readdirSync(tasksDir).filter(d => d.startsWith(padded + '-'));
260
+ if (dirs.length === 0) {
261
+ error(`Task #${taskNum} directory not found.`);
262
+ return { error: 'task-not-found' };
263
+ }
264
+
265
+ const taskDir = path.join(tasksDir, dirs[0]);
266
+ const taskMeta = JSON.parse(fs.readFileSync(path.join(taskDir, 'task.json'), 'utf8'));
267
+
268
+ // Determine current step based on what files exist
269
+ const hasContext = fs.existsSync(path.join(taskDir, 'CONTEXT.md'));
270
+ const hasResearch = fs.existsSync(path.join(taskDir, 'RESEARCH.md'));
271
+ const hasPlan = fs.existsSync(path.join(taskDir, 'PLAN-1.md'));
272
+ const hasSummary = fs.existsSync(path.join(taskDir, 'SUMMARY-1.md'));
273
+ const hasVerification = fs.existsSync(path.join(taskDir, 'VERIFICATION.md'));
274
+
275
+ const isLight = taskMeta.mode === 'light';
276
+ let nextStep;
277
+ let newStatus;
278
+
279
+ if (!hasContext && !isLight) {
280
+ nextStep = 'discuss';
281
+ newStatus = 'discussing';
282
+ } else if (taskMeta.research && !hasResearch) {
283
+ nextStep = 'research';
284
+ newStatus = 'researching';
285
+ } else if (!hasPlan) {
286
+ nextStep = 'plan';
287
+ newStatus = 'planning';
288
+ } else if (!hasSummary) {
289
+ nextStep = 'execute';
290
+ newStatus = 'executing';
291
+ } else if (!hasVerification && !isLight) {
292
+ nextStep = 'verify';
293
+ newStatus = 'verifying';
294
+ } else {
295
+ // All steps done — complete the task
296
+ return handleTaskComplete(brainDir, state, taskNum, taskDir, taskMeta);
297
+ }
298
+
299
+ // Update status
300
+ taskMeta.status = newStatus;
301
+ fs.writeFileSync(path.join(taskDir, 'task.json'), JSON.stringify(taskMeta, null, 2));
302
+
303
+ // Update state
304
+ state.tasks.active = state.tasks.active || [];
305
+ const activeIdx = state.tasks.active.findIndex(t => t.num === taskNum);
306
+ if (activeIdx >= 0) state.tasks.active[activeIdx].status = newStatus;
307
+ writeState(brainDir, state);
308
+
309
+ // Build step instructions
310
+ const steps = buildStepInstructions(
311
+ taskNum, taskMeta.slug, taskDir, brainDir,
312
+ taskMeta.mode, taskMeta.research, taskMeta.description
313
+ );
314
+
315
+ logEvent(brainDir, 0, { type: 'task-continue', task: taskNum, step: nextStep });
316
+
317
+ // Record cost for execute step
318
+ if (nextStep === 'execute' && hasPlan) {
319
+ try {
320
+ const planContent = fs.readFileSync(path.join(taskDir, 'PLAN-1.md'), 'utf8');
321
+ const inputTokens = estimateTokens(planContent);
322
+ recordInvocation(brainDir, {
323
+ agent: 'executor', phase: 0, plan: '01',
324
+ model: 'inherit', input_tokens: inputTokens, output_tokens: 0
325
+ });
326
+ } catch { /* non-fatal */ }
327
+ }
328
+
329
+ const humanLines = [
330
+ prefix(`Task #${taskNum}: ${taskMeta.description}`),
331
+ prefix(`Status: ${newStatus}`),
332
+ prefix(`Next step: ${nextStep}`),
333
+ '',
334
+ steps[nextStep]
335
+ ].join('\n');
336
+
337
+ output({
338
+ action: 'task-continue',
339
+ task: taskMeta,
340
+ nextStep,
341
+ instructions: steps[nextStep]
342
+ }, humanLines);
343
+
344
+ return { action: 'task-continue', task: taskMeta, nextStep };
345
+ }
346
+
347
+ function handleTaskComplete(brainDir, state, taskNum, taskDir, taskMeta) {
348
+ taskMeta.status = 'complete';
349
+ taskMeta.completed = new Date().toISOString();
350
+ fs.writeFileSync(path.join(taskDir, 'task.json'), JSON.stringify(taskMeta, null, 2));
351
+
352
+ // Move from active to history
353
+ state.tasks.active = state.tasks.active.filter(t => t.num !== taskNum);
354
+ state.tasks.history.push({
355
+ num: taskNum,
356
+ slug: taskMeta.slug,
357
+ description: taskMeta.description,
358
+ mode: taskMeta.mode,
359
+ completed: taskMeta.completed,
360
+ promoted_to_phase: null
361
+ });
362
+ state.tasks.current = null;
363
+ writeState(brainDir, state);
364
+
365
+ logEvent(brainDir, 0, { type: 'task-complete', task: taskNum, slug: taskMeta.slug });
366
+
367
+ const padded = String(taskNum).padStart(2, '0');
368
+ const humanLines = [
369
+ prefix(`Task #${taskNum} complete: ${taskMeta.description}`),
370
+ '',
371
+ prefix('Options:'),
372
+ prefix(' 1. Keep as standalone task (done!)'),
373
+ prefix(` 2. Promote to roadmap phase: brain-dev new-task --promote ${taskNum}`),
374
+ '',
375
+ prefix(`Suggested commit: git add -A && git commit -m "feat(task-${padded}): ${taskMeta.slug}"`)
376
+ ].join('\n');
377
+
378
+ output({
379
+ action: 'task-complete',
380
+ task: taskMeta,
381
+ promote_command: `brain-dev new-task --promote ${taskNum}`
382
+ }, humanLines);
383
+
384
+ return { action: 'task-complete', task: taskMeta };
385
+ }
386
+
387
+ function handlePromote(brainDir, state, taskNum) {
388
+ // Guard: phases must exist for promotion
389
+ if (!state.phase || !Array.isArray(state.phase.phases)) {
390
+ error("Project phases not initialized. Run '/brain:new-project' first.");
391
+ return { error: 'no-phases' };
392
+ }
393
+
394
+ // Find the task
395
+ const tasksDir = path.join(brainDir, 'tasks');
396
+ if (!fs.existsSync(tasksDir)) {
397
+ error(`No tasks directory found. Create a task first: brain-dev new-task "description"`);
398
+ return { error: 'no-tasks-dir' };
399
+ }
400
+ const padded = String(taskNum).padStart(2, '0');
401
+ const dirs = fs.readdirSync(tasksDir).filter(d => d.startsWith(padded + '-'));
402
+
403
+ if (dirs.length === 0) {
404
+ error(`Task #${taskNum} not found.`);
405
+ return { error: 'task-not-found' };
406
+ }
407
+
408
+ const taskDir = path.join(tasksDir, dirs[0]);
409
+ const taskMeta = JSON.parse(fs.readFileSync(path.join(taskDir, 'task.json'), 'utf8'));
410
+
411
+ // Insert as a new phase after current phase
412
+ try {
413
+ const { parseRoadmap, insertPhase, writeRoadmap } = require('../roadmap.cjs');
414
+ const roadmapPath = path.join(brainDir, 'ROADMAP.md');
415
+
416
+ if (!fs.existsSync(roadmapPath)) {
417
+ error("No ROADMAP.md found. Cannot promote without a roadmap.");
418
+ return { error: 'no-roadmap' };
419
+ }
420
+
421
+ const roadmapData = parseRoadmap(brainDir);
422
+ const afterPhase = state.phase?.current || roadmapData.phases.length;
423
+
424
+ const updatedRoadmap = insertPhase(roadmapData, afterPhase, {
425
+ name: taskMeta.description,
426
+ goal: `Implement: ${taskMeta.description} (promoted from task #${taskNum})`,
427
+ requirements: taskMeta.requirements || [],
428
+ status: taskMeta.status === 'complete' ? 'Complete' : 'In Progress'
429
+ });
430
+
431
+ writeRoadmap(brainDir, updatedRoadmap);
432
+
433
+ // Find the newly inserted phase (it was not in the original roadmapData)
434
+ const newPhase = updatedRoadmap.phases.find(
435
+ p => !roadmapData.phases.some(op => op.number === p.number)
436
+ );
437
+
438
+ // Update task metadata
439
+ taskMeta.promoted_to_phase = newPhase ? newPhase.number : afterPhase;
440
+ fs.writeFileSync(path.join(taskDir, 'task.json'), JSON.stringify(taskMeta, null, 2));
441
+
442
+ // Update state
443
+ state.phase.total = updatedRoadmap.phases.length;
444
+ state.phase.phases.push({
445
+ number: taskMeta.promoted_to_phase,
446
+ name: taskMeta.description,
447
+ status: taskMeta.status === 'complete' ? 'complete' : 'pending',
448
+ goal: newPhase ? newPhase.goal : taskMeta.description
449
+ });
450
+
451
+ // Update history
452
+ const historyEntry = state.tasks.history.find(t => t.num === taskNum);
453
+ if (historyEntry) historyEntry.promoted_to_phase = taskMeta.promoted_to_phase;
454
+
455
+ writeState(brainDir, state);
456
+
457
+ logEvent(brainDir, 0, { type: 'task-promote', task: taskNum, phase: taskMeta.promoted_to_phase });
458
+
459
+ // Copy artifacts to phase directory
460
+ const phaseSlug = taskMeta.slug;
461
+ const phasePadded = Number.isInteger(taskMeta.promoted_to_phase)
462
+ ? String(taskMeta.promoted_to_phase).padStart(2, '0')
463
+ : String(taskMeta.promoted_to_phase).replace('.', '_');
464
+ const phaseDir = path.join(brainDir, 'phases', `${phasePadded}-${phaseSlug}`);
465
+ fs.mkdirSync(phaseDir, { recursive: true });
466
+
467
+ // Copy plan, summary, verification if they exist
468
+ for (const file of ['PLAN-1.md', 'SUMMARY-1.md', 'VERIFICATION.md', 'CONTEXT.md']) {
469
+ const src = path.join(taskDir, file);
470
+ if (fs.existsSync(src)) {
471
+ fs.copyFileSync(src, path.join(phaseDir, file));
472
+ }
473
+ }
474
+
475
+ const humanLines = [
476
+ prefix(`Task #${taskNum} promoted to Phase ${taskMeta.promoted_to_phase}`),
477
+ prefix(`Phase: ${taskMeta.description}`),
478
+ prefix(`Artifacts copied to: .brain/phases/${phasePadded}-${phaseSlug}/`),
479
+ prefix('ROADMAP.md updated')
480
+ ].join('\n');
481
+
482
+ output({ action: 'task-promoted', task: taskMeta, phase: newPhase }, humanLines);
483
+ return { action: 'task-promoted', task: taskMeta, phase: newPhase };
484
+
485
+ } catch (e) {
486
+ error(`Promotion failed: ${e.message}`);
487
+ return { error: 'promotion-failed', message: e.message };
488
+ }
489
+ }
490
+
491
+ function handleList(brainDir, state) {
492
+ const tasks = state.tasks || { active: [], history: [], count: 0 };
493
+
494
+ const lines = [prefix(`Tasks (${tasks.count} total)`)];
495
+
496
+ if (tasks.active.length > 0) {
497
+ lines.push('');
498
+ lines.push(prefix('Active:'));
499
+ for (const t of tasks.active) {
500
+ lines.push(prefix(` #${t.num} ${t.slug} (${t.status})`));
501
+ }
502
+ }
503
+
504
+ if (tasks.history.length > 0) {
505
+ lines.push('');
506
+ lines.push(prefix('Completed:'));
507
+ for (const t of tasks.history.slice(-10)) {
508
+ const promoted = t.promoted_to_phase ? ` -> Phase ${t.promoted_to_phase}` : '';
509
+ lines.push(prefix(` #${t.num} ${t.slug}${promoted}`));
510
+ }
511
+ }
512
+
513
+ if (tasks.count === 0) {
514
+ lines.push(prefix(' No tasks yet. Create one: brain-dev new-task "description"'));
515
+ }
516
+
517
+ const result = { action: 'task-list', tasks };
518
+ output(result, lines.join('\n'));
519
+ return result;
520
+ }
521
+
522
+ module.exports = { run };
@@ -65,6 +65,15 @@ const COMMANDS = [
65
65
  needsState: true,
66
66
  args: ' --full Enable plan checking + verification\n --execute --task N Execute task N\n --verify --task N Verify task N (--full only)\n --complete --task N Complete and commit task N'
67
67
  },
68
+ {
69
+ name: 'new-task',
70
+ description: 'Create a significant task with full pipeline',
71
+ usage: 'brain-dev new-task "description" [--research] [--light]',
72
+ group: 'Lifecycle',
73
+ implemented: true,
74
+ needsState: true,
75
+ args: ' --research Include light research phase\n --light Skip discuss and verify (fast mode)\n --continue Resume the current active task\n --list List all tasks\n --promote N Promote task N to roadmap phase'
76
+ },
68
77
  {
69
78
  name: 'execute',
70
79
  description: 'Execute plans with verification',
package/bin/lib/state.cjs CHANGED
@@ -4,7 +4,7 @@ const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
 
6
6
  const CURRENT_SCHEMA = 'brain/v1';
7
- const CURRENT_VERSION = '0.8.0';
7
+ const CURRENT_VERSION = '0.9.0';
8
8
 
9
9
  /**
10
10
  * Atomic write: write to temp file, then rename.
@@ -176,6 +176,11 @@ function migrateState(data) {
176
176
  migrated.phase.last_stuck_at = null;
177
177
  }
178
178
 
179
+ // v0.9.0 fields (new-task)
180
+ if (!migrated.tasks) {
181
+ migrated.tasks = { current: null, count: 0, active: [], history: [] };
182
+ }
183
+
179
184
  return migrated;
180
185
  }
181
186
 
@@ -320,6 +325,22 @@ function generateStateMd(state) {
320
325
  lines.push('');
321
326
  }
322
327
 
328
+ // Active Tasks section (v0.9.0)
329
+ if (state.tasks && state.tasks.active && state.tasks.active.length > 0) {
330
+ lines.push('');
331
+ lines.push('## Active Tasks');
332
+ for (const t of state.tasks.active) {
333
+ lines.push(`- Task ${t.num}: ${t.slug} (${t.status})`);
334
+ }
335
+ }
336
+ if (state.tasks && state.tasks.count > 0) {
337
+ lines.push('');
338
+ lines.push(`## Task Stats`);
339
+ lines.push(`- Total: ${state.tasks.count}`);
340
+ lines.push(`- Active: ${state.tasks.active ? state.tasks.active.length : 0}`);
341
+ lines.push(`- Completed: ${state.tasks.history ? state.tasks.history.length : 0}`);
342
+ }
343
+
323
344
  lines.push('## Blockers');
324
345
 
325
346
  if (Array.isArray(blockers) && blockers.length > 0) {
@@ -441,6 +462,12 @@ function createDefaultState(platform) {
441
462
  preinline: true,
442
463
  budgets: {},
443
464
  condensed_summaries: true
465
+ },
466
+ tasks: {
467
+ current: null,
468
+ count: 0,
469
+ active: [],
470
+ history: []
444
471
  }
445
472
  };
446
473
  }
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: brain:new-task
3
+ description: Create a significant task with full discuss → plan → execute → verify → complete pipeline
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Edit
8
+ - Bash
9
+ - Grep
10
+ - Glob
11
+ - Agent
12
+ - AskUserQuestion
13
+ ---
14
+ <objective>
15
+ Create and execute a significant task that needs thoughtful planning, execution, and verification.
16
+ Unlike quick tasks, new-task applies the full pipeline. Unlike new-project, it works within the existing project context.
17
+ </objective>
18
+
19
+ <context>
20
+ Use new-task for features, refactors, or epics that need more ceremony than a quick fix but less than a new project.
21
+ Examples: "add payment integration", "refactor auth to JWT", "add rate limiting to API"
22
+ </context>
23
+
24
+ <process>
25
+ 1. Create: `npx brain-dev new-task "description"` [--research] [--light]
26
+ 2. Follow the step instructions output by the command
27
+ 3. After each step completes, run: `npx brain-dev new-task --continue`
28
+ 4. Pipeline: discuss → plan → execute → verify → complete
29
+ 5. On completion, optionally promote to roadmap: `npx brain-dev new-task --promote N`
30
+ 6. List tasks: `npx brain-dev new-task --list`
31
+ </process>
@@ -48,7 +48,7 @@ try {
48
48
  'Phase: ' + (data.phase && data.phase.current || 0) + ' (' + (data.phase && data.phase.status || 'initialized') + ')',
49
49
  'Next: ' + (data.nextAction || '/brain:new-project'),
50
50
  '',
51
- 'Commands: /brain:new-project, /brain:discuss, /brain:plan, /brain:execute, /brain:verify, /brain:complete, /brain:quick, /brain:progress, /brain:pause, /brain:resume, /brain:help, /brain:health, /brain:update, /brain:storm, /brain:adr, /brain:phase, /brain:config, /brain:map, /brain:recover, /brain:dashboard, /brain:auto (or execute --auto)',
51
+ 'Commands: /brain:new-project, /brain:discuss, /brain:plan, /brain:execute, /brain:verify, /brain:complete, /brain:quick, /brain:new-task, /brain:progress, /brain:pause, /brain:resume, /brain:help, /brain:health, /brain:update, /brain:storm, /brain:adr, /brain:phase, /brain:config, /brain:map, /brain:recover, /brain:dashboard, /brain:auto (or execute --auto)',
52
52
  '',
53
53
  'Instructions for Claude:',
54
54
  '- When user types /brain:<command>, run: npx brain-dev <command> [args]',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brain-dev",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "AI-powered development workflow orchestrator",
5
5
  "author": "halilcosdu",
6
6
  "license": "MIT",