brain-dev 2.0.5 → 2.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.
@@ -3,13 +3,13 @@
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const { readState, writeState } = require('../state.cjs');
6
- const { loadTemplate, interpolate } = require('../templates.cjs');
6
+ const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
7
7
  const { getAgent, resolveModel } = require('../agents.cjs');
8
8
  const { logEvent } = require('../logger.cjs');
9
9
  const { output, error } = require('../core.cjs');
10
10
  const { recordInvocation, estimateTokens } = require('../cost.cjs');
11
11
  const { acquireLock, releaseLock } = require('../lock.cjs');
12
- const { generateExpertise } = require('../stack-expert.cjs');
12
+ const { generateExpertise, getDetectedFramework } = require('../stack-expert.cjs');
13
13
 
14
14
  /**
15
15
  * Find a phase directory under .brain/phases/ matching a phase number.
@@ -347,8 +347,9 @@ async function run(args = [], opts = {}) {
347
347
  const spotCheckInstruction = 'After completing all tasks, the orchestrator will verify SUMMARY.md, file existence, and commit count.';
348
348
  const debuggerSpawnInstruction = 'If auto-retry fails, a debugger agent will be spawned with the error context.';
349
349
 
350
- // Load executor template and interpolate
351
- const template = loadTemplate('executor');
350
+ // Load executor template with framework overlay
351
+ const framework = getDetectedFramework(brainDir);
352
+ const template = loadTemplateWithOverlay('executor', framework);
352
353
  const prompt = interpolate(template, {
353
354
  plan_path: targetPlan.path,
354
355
  summary_path: summaryPath,
@@ -4,12 +4,12 @@ const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const { readState, writeState } = require('../state.cjs');
6
6
  const { parseRoadmap } = require('../roadmap.cjs');
7
- const { loadTemplate, interpolate } = require('../templates.cjs');
7
+ const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
8
8
  const { getAgent, resolveModel } = require('../agents.cjs');
9
9
  const { logEvent } = require('../logger.cjs');
10
10
  const { estimateFromPlan, getDefaultBudget, checkBudget } = require('../complexity.cjs');
11
11
  const { output, error, success } = require('../core.cjs');
12
- const { generateExpertise } = require('../stack-expert.cjs');
12
+ const { generateExpertise, getDetectedFramework } = require('../stack-expert.cjs');
13
13
 
14
14
  /**
15
15
  * Find a phase directory under .brain/phases/ matching a phase number.
@@ -157,7 +157,8 @@ function getAlternatives(tech) {
157
157
  * @returns {{ prompt: string, output_dir: string }}
158
158
  */
159
159
  function generatePlannerPrompt(phase, brainDir) {
160
- const template = loadTemplate('planner');
160
+ const framework = getDetectedFramework(brainDir);
161
+ const template = loadTemplateWithOverlay('planner', framework);
161
162
 
162
163
  const contextContent = readContext(brainDir, phase.number);
163
164
  const researchContent = readResearchSummary(brainDir, phase.number);
@@ -8,12 +8,46 @@ const { output, error, prefix } = require('../core.cjs');
8
8
  const { logEvent } = require('../logger.cjs');
9
9
  const { loadTemplate, interpolate } = require('../templates.cjs');
10
10
  const { getAgent, resolveModel } = require('../agents.cjs');
11
+ const { generateExpertise } = require('../stack-expert.cjs');
11
12
  const {
12
13
  RESEARCH_AREAS, CORE_QUESTIONS, buildBrownfieldQuestions,
13
14
  readDetection, buildCodebaseContext, generateProjectMd,
14
15
  generateRequirementsMd, extractFeatures, extractSection
15
16
  } = require('../story-helpers.cjs');
16
17
 
18
+ /**
19
+ * Get research areas with optional framework-specific additions.
20
+ * @param {Array} baseAreas - Default RESEARCH_AREAS
21
+ * @param {object|null} detection - Detection result
22
+ * @returns {Array} Research areas (may include framework-specific one)
23
+ */
24
+ function getResearchAreas(baseAreas, detection) {
25
+ const areas = [...baseAreas];
26
+ const framework = detection?.stack?.primary?.framework || detection?.stack?.framework;
27
+ if (!framework) return areas;
28
+
29
+ const fwLower = framework.toLowerCase().replace(/[^a-z]/g, '-');
30
+
31
+ // Add framework-specific research area
32
+ areas.push({
33
+ name: `${framework} Patterns`,
34
+ area: `${framework} best practices`,
35
+ description: `${framework}-specific patterns, conventions, ecosystem tools, and common pitfalls`,
36
+ file: `${fwLower}-patterns.md`
37
+ });
38
+
39
+ // Enrich architecture area with framework context
40
+ const archIdx = areas.findIndex(a => a.area && a.area.toLowerCase().includes('architecture'));
41
+ if (archIdx >= 0) {
42
+ areas[archIdx] = {
43
+ ...areas[archIdx],
44
+ description: `${framework} architecture patterns and ${areas[archIdx].description}`
45
+ };
46
+ }
47
+
48
+ return areas;
49
+ }
50
+
17
51
  async function run(args = [], opts = {}) {
18
52
  const { values, positionals } = parseArgs({
19
53
  args,
@@ -317,8 +351,12 @@ function stepResearch(brainDir, storyDir, storyMeta, state) {
317
351
  const researchDir = path.join(storyDir, 'research');
318
352
  fs.mkdirSync(researchDir, { recursive: true });
319
353
 
320
- // Build researcher agent prompts
321
- const agents = RESEARCH_AREAS.map(area => {
354
+ // Build researcher agent prompts with framework-aware areas
355
+ const detection = readDetection(brainDir);
356
+ const researchAreas = getResearchAreas(RESEARCH_AREAS, detection);
357
+ const stackContext = generateExpertise(brainDir, 'general');
358
+
359
+ const agents = researchAreas.map(area => {
322
360
  const agentDef = getAgent('researcher');
323
361
  const model = resolveModel('researcher', state);
324
362
  return {
@@ -330,6 +368,7 @@ function stepResearch(brainDir, storyDir, storyMeta, state) {
330
368
  prompt: [
331
369
  `# Research Focus: ${area.area}`,
332
370
  '',
371
+ stackContext ? `## Stack Context\n${stackContext}\n` : '',
333
372
  `You are a ${area.description} researcher.`,
334
373
  '',
335
374
  '## Project Context',
@@ -346,7 +385,6 @@ function stepResearch(brainDir, storyDir, storyMeta, state) {
346
385
  });
347
386
 
348
387
  // Check brownfield and build mapper agents if codebase not yet mapped
349
- const detection = readDetection(brainDir);
350
388
  const isBrownfield = detection && detection.type === 'brownfield';
351
389
  const codebaseDir = path.join(brainDir, 'codebase');
352
390
  const codebaseMapped = fs.existsSync(codebaseDir) &&
@@ -3,6 +3,22 @@ const fs = require('node:fs');
3
3
  const path = require('node:path');
4
4
  const { output, prefix } = require('../core.cjs');
5
5
 
6
+ /**
7
+ * Read installed version from brain.json or package.json fallback.
8
+ * @param {string} brainDir
9
+ * @returns {string}
10
+ */
11
+ function getInstalledVersion(brainDir) {
12
+ try {
13
+ const state = JSON.parse(fs.readFileSync(path.join(brainDir, 'brain.json'), 'utf8'));
14
+ if (state.version && state.version !== 'unknown') return state.version;
15
+ } catch {}
16
+ try {
17
+ return require(path.join(__dirname, '..', '..', '..', 'package.json')).version;
18
+ } catch {}
19
+ return 'unknown';
20
+ }
21
+
6
22
  async function run(args = [], opts = {}) {
7
23
  const brainDir = opts.brainDir || path.join(process.cwd(), '.brain');
8
24
 
@@ -13,12 +29,14 @@ async function run(args = [], opts = {}) {
13
29
  cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
14
30
  } catch {}
15
31
 
32
+ const installed = cache?.installed || getInstalledVersion(brainDir);
33
+
16
34
  if (!cache || !cache.update_available) {
17
35
  const lines = [
18
36
  prefix('You are on the latest version.'),
19
- prefix(`Installed: ${cache?.installed || 'unknown'}`)
37
+ prefix(`Installed: ${installed}`)
20
38
  ];
21
- output({ action: 'up-to-date', installed: cache?.installed || 'unknown' }, lines.join('\n'));
39
+ output({ action: 'up-to-date', installed }, lines.join('\n'));
22
40
  return { action: 'up-to-date' };
23
41
  }
24
42
 
@@ -3,14 +3,14 @@
3
3
  const fs = require('node:fs');
4
4
  const path = require('node:path');
5
5
  const { readState, writeState, atomicWriteSync } = require('../state.cjs');
6
- const { loadTemplate, interpolate } = require('../templates.cjs');
6
+ const { loadTemplate, interpolate, loadTemplateWithOverlay } = require('../templates.cjs');
7
7
  const { getAgent, resolveModel } = require('../agents.cjs');
8
8
  const { logEvent } = require('../logger.cjs');
9
9
  const { output, error, success } = require('../core.cjs');
10
10
  const antiPatterns = require('../anti-patterns.cjs');
11
11
  const { buildDebuggerSpawnInstructions } = require('./execute.cjs');
12
12
  const { isPathWithinRoot } = require('../security.cjs');
13
- const { generateExpertise } = require('../stack-expert.cjs');
13
+ const { generateExpertise, getDetectedFramework } = require('../stack-expert.cjs');
14
14
 
15
15
  /**
16
16
  * Find a phase directory under .brain/phases/ matching a phase number.
@@ -255,8 +255,9 @@ async function run(args = [], opts = {}) {
255
255
  key_links: combinedMustHaves.key_links.length
256
256
  });
257
257
 
258
- // Load verifier template and interpolate
259
- const template = loadTemplate('verifier');
258
+ // Load verifier template with framework overlay
259
+ const framework = getDetectedFramework(brainDir);
260
+ const template = loadTemplateWithOverlay('verifier', framework);
260
261
  const mustHavesFormatted = [
261
262
  '**Truths:**',
262
263
  ...combinedMustHaves.truths.map(t => `- ${t}`),
@@ -24,39 +24,76 @@ const STACK_PATTERNS = {
24
24
  // Framework-level patterns (file structure, key concepts)
25
25
  frameworks: {
26
26
  'Laravel': {
27
- structure: 'app/Models, app/Http/Controllers, routes/api.php, database/migrations',
28
- keyCommands: 'php artisan make:model, make:controller, make:migration, migrate',
29
- patterns: 'Eloquent ORM, Form Requests for validation, Resource controllers, Middleware',
30
- antiPatterns: 'N+1 queries (use eager loading), fat controllers (use Services/Actions), raw SQL in controllers',
31
- testing: 'Feature tests in tests/Feature, Unit tests in tests/Unit, use RefreshDatabase trait'
27
+ structure: 'app/Models, app/Http/Controllers, app/Http/Requests, app/Services, routes/api.php, database/migrations',
28
+ keyCommands: 'php artisan make:model -mf, make:controller --api, make:migration, make:request, make:resource, migrate, test',
29
+ patterns: 'Eloquent ORM, Form Requests for validation, API Resources for serialization, Resource controllers, Middleware, Service classes for business logic, Action classes for single-purpose operations',
30
+ antiPatterns: 'N+1 queries (use eager loading with/load), fat controllers (extract to Services/Actions), raw SQL in controllers, logic in models (use Services), not using Form Requests for validation',
31
+ testing: 'Feature tests in tests/Feature, Unit tests in tests/Unit, use RefreshDatabase trait, factories for test data',
32
+ migration: 'php artisan make:migration create_X_table, php artisan migrate, php artisan migrate:rollback',
33
+ commonBugs: 'N+1 query on relationships (use ->with()), mass assignment vulnerability (use $fillable), missing foreign key constraints, not using transactions for multi-table writes, queue job serialization with deleted models',
34
+ verificationRules: ['Migrations exist for all new models', '.env.example has all required keys', 'Routes registered in routes/api.php or routes/web.php', 'Form Requests validate all user input', 'API Resources used for JSON responses', 'Factories exist for all models'],
35
+ planningHints: ['Plan migration tasks BEFORE model/controller tasks', 'Each model needs: migration, model, factory, controller, request, resource, test', 'Group related migrations in same plan', 'Include seeder tasks for test data'],
36
+ testExamples: {
37
+ unit: "public function test_user_can_be_created(): void {\n $user = User::factory()->create(['email' => 'test@example.com']);\n $this->assertDatabaseHas('users', ['email' => 'test@example.com']);\n}",
38
+ integration: "$response = $this->postJson('/api/users', ['name' => 'Test', 'email' => 'test@example.com']);\n$response->assertStatus(201)->assertJsonStructure(['data' => ['id', 'name', 'email']]);"
39
+ }
32
40
  },
33
41
  'Next.js': {
34
- structure: 'app/ (App Router), components/, lib/, public/, app/api/ for routes',
35
- keyCommands: 'npx next dev, next build, next start',
36
- patterns: 'Server Components by default, use client for interactivity, Server Actions, Route Handlers',
37
- antiPatterns: 'useEffect for data fetching (use Server Components), client-side state for server data, large client bundles',
38
- testing: 'Jest + React Testing Library, or Vitest, test in __tests__/ or *.test.tsx'
42
+ structure: 'app/ (App Router), app/api/ for Route Handlers, components/, lib/, public/',
43
+ keyCommands: 'npx next dev, next build, next start, next lint',
44
+ patterns: 'Server Components by default, use client directive for interactivity, Server Actions for mutations, Route Handlers for API, Metadata API for SEO, Suspense for loading states',
45
+ antiPatterns: 'useEffect for data fetching (use Server Components), client-side state for server data, large client bundles, not using loading.tsx/error.tsx, fetching in client when server component suffices',
46
+ testing: 'Jest + React Testing Library, or Vitest, test in __tests__/ or *.test.tsx',
47
+ commonBugs: 'Hydration mismatch (server vs client render difference), importing server-only code in client components, not handling loading/error states, stale cache in fetch(), middleware running on every request',
48
+ verificationRules: ['Page components exist in app/ directory', 'loading.tsx exists for async pages', 'error.tsx exists for error boundaries', 'API Route Handlers use correct HTTP method exports', 'Client components have "use client" directive', 'Metadata exported for SEO pages'],
49
+ planningHints: ['Plan layout components before page components', 'Server Components first, add "use client" only when needed', 'Plan API Route Handlers separate from page components', 'Include loading.tsx and error.tsx for each route group'],
50
+ testExamples: {
51
+ unit: "import { render, screen } from '@testing-library/react';\nimport Page from './page';\n\ntest('renders heading', () => {\n render(<Page />);\n expect(screen.getByRole('heading')).toBeInTheDocument();\n});",
52
+ integration: "const response = await fetch('/api/users', { method: 'POST', body: JSON.stringify({name: 'Test'}) });\nexpect(response.status).toBe(201);"
53
+ }
39
54
  },
40
55
  'React Native': {
41
- structure: 'app/ (Expo Router) or src/screens, components/, hooks/, services/',
42
- keyCommands: 'npx expo start, expo prebuild, eas build',
43
- patterns: 'Functional components + hooks, React Navigation/Expo Router, AsyncStorage, StyleSheet',
44
- antiPatterns: 'Inline styles (use StyleSheet), heavy computation on JS thread, large FlatList without optimization',
45
- testing: 'Jest + React Native Testing Library, detox for E2E'
56
+ structure: 'app/ (Expo Router) or src/screens, components/, hooks/, services/, assets/',
57
+ keyCommands: 'npx expo start, expo prebuild, eas build, npx jest',
58
+ patterns: 'Functional components + hooks, React Navigation/Expo Router, AsyncStorage, StyleSheet.create, Platform.select for platform-specific code',
59
+ antiPatterns: 'Inline styles (use StyleSheet.create), heavy computation on JS thread, large FlatList without getItemLayout/keyExtractor, not using Platform.OS for platform checks, synchronous storage calls',
60
+ testing: 'Jest + React Native Testing Library, detox for E2E',
61
+ commonBugs: 'Platform-specific rendering not tested on both OS, keyboard covering inputs (use KeyboardAvoidingView), memory leaks from unsubscribed listeners, navigation state persistence issues, StatusBar behavior differences iOS vs Android',
62
+ verificationRules: ['Platform-specific code uses Platform.OS or Platform.select', 'FlatList has keyExtractor and getItemLayout', 'Navigation screens registered in navigator', 'Assets imported correctly (require() for local)', 'No web-only APIs used (document, window)'],
63
+ planningHints: ['Plan shared components before screen-specific ones', 'Consider both iOS and Android in task descriptions', 'Plan navigation structure before individual screens', 'Include platform-specific testing tasks'],
64
+ testExamples: {
65
+ unit: "import { render, screen } from '@testing-library/react-native';\nimport { Button } from './Button';\n\ntest('renders button text', () => {\n render(<Button title='Press me' />);\n expect(screen.getByText('Press me')).toBeTruthy();\n});",
66
+ integration: "const { getByTestId, getByText } = render(<LoginScreen />);\nfireEvent.changeText(getByTestId('email-input'), 'test@example.com');\nfireEvent.press(getByText('Login'));"
67
+ }
46
68
  },
47
69
  'Express': {
48
- structure: 'routes/, middleware/, controllers/, models/',
49
- keyCommands: 'node server.js, npm start',
50
- patterns: 'Router middleware chain, error handling middleware, async/await handlers',
51
- antiPatterns: 'Callback hell, no error middleware, sync operations blocking event loop',
52
- testing: 'Jest/Mocha + supertest for HTTP assertions'
70
+ structure: 'routes/, middleware/, controllers/, models/, validators/',
71
+ keyCommands: 'node server.js, npm start, npm test',
72
+ patterns: 'Router middleware chain, error handling middleware, async/await with express-async-errors, request validation with Joi/Zod, controller-service separation',
73
+ antiPatterns: 'Callback hell, no error middleware, sync operations blocking event loop, not validating request body, try/catch in every route (use express-async-errors)',
74
+ testing: 'Jest/Mocha + supertest for HTTP assertions',
75
+ commonBugs: 'Unhandled promise rejections crashing server, missing error middleware (must have 4 params), route order matters (specific before generic), CORS misconfiguration, not closing DB connections on shutdown',
76
+ verificationRules: ['Error handling middleware exists (4 params: err, req, res, next)', 'Routes validate input before processing', 'Async handlers properly catch errors', 'CORS configured for production origins', 'Graceful shutdown handler exists'],
77
+ planningHints: ['Plan middleware before routes', 'Error handling middleware is a separate task at the end', 'Group related routes in same router file', 'Plan validation schemas alongside route handlers'],
78
+ testExamples: {
79
+ unit: "const request = require('supertest');\nconst app = require('../app');\n\ntest('GET /api/users returns 200', async () => {\n const res = await request(app).get('/api/users');\n expect(res.status).toBe(200);\n});",
80
+ integration: "const res = await request(app).post('/api/users').send({name: 'Test', email: 'test@example.com'});\nexpect(res.status).toBe(201);\nexpect(res.body.data).toHaveProperty('id');"
81
+ }
53
82
  },
54
83
  'Django': {
55
- structure: 'apps/<name>/models.py, views.py, urls.py, templates/',
56
- keyCommands: 'python manage.py runserver, makemigrations, migrate, createsuperuser',
57
- patterns: 'Class-based views, Django ORM, Forms/Serializers, middleware',
58
- antiPatterns: 'N+1 queries (use select_related/prefetch_related), logic in views (use services)',
59
- testing: 'pytest-django or TestCase, factory_boy for fixtures'
84
+ structure: 'apps/<name>/models.py, views.py, urls.py, serializers.py, admin.py, templates/',
85
+ keyCommands: 'python manage.py runserver, makemigrations, migrate, createsuperuser, test, shell',
86
+ patterns: 'Class-based views, Django ORM, DRF Serializers, ModelViewSet, middleware, signals, management commands',
87
+ antiPatterns: 'N+1 queries (use select_related/prefetch_related), logic in views (use services), not using serializers for validation, raw SQL without parameterization',
88
+ testing: 'pytest-django or TestCase, factory_boy for fixtures',
89
+ migration: 'python manage.py makemigrations, python manage.py migrate, python manage.py migrate <app> zero (rollback)',
90
+ commonBugs: 'Circular imports between apps, migration conflicts in teams, not using select_related causing N+1, signals firing during tests unexpectedly, timezone-naive datetime comparisons',
91
+ verificationRules: ['Migrations created for all model changes', 'URLs registered in app urls.py and included in root urls.py', 'Admin registered for all models', 'Serializers validate all user input', 'Permissions set on views'],
92
+ planningHints: ['Plan models and migrations before views', 'Each app should be self-contained', 'Plan serializers alongside models', 'Include admin registration tasks', 'Plan management commands for data operations'],
93
+ testExamples: {
94
+ unit: "from django.test import TestCase\nfrom .models import User\n\nclass UserModelTest(TestCase):\n def test_create_user(self):\n user = User.objects.create(email='test@example.com')\n self.assertEqual(user.email, 'test@example.com')",
95
+ integration: "from rest_framework.test import APITestCase\n\nclass UserAPITest(APITestCase):\n def test_create_user(self):\n response = self.client.post('/api/users/', {'email': 'test@example.com'})\n self.assertEqual(response.status_code, 201)"
96
+ }
60
97
  },
61
98
  'Rails': {
62
99
  structure: 'app/models, app/controllers, config/routes.rb, db/migrate',
@@ -94,11 +131,19 @@ const STACK_PATTERNS = {
94
131
  testing: 'JUnit 5 + Mockito, @SpringBootTest, @WebMvcTest'
95
132
  },
96
133
  'FastAPI': {
97
- structure: 'app/main.py, app/routers/, app/models/, app/schemas/',
98
- keyCommands: 'uvicorn app.main:app --reload',
99
- patterns: 'Pydantic models, dependency injection, async/await, path operations',
100
- antiPatterns: 'Sync database calls in async endpoints, no Pydantic validation, global state',
101
- testing: 'pytest + httpx AsyncClient'
134
+ structure: 'app/main.py, app/routers/, app/models/, app/schemas/, app/dependencies.py, app/crud/',
135
+ keyCommands: 'uvicorn app.main:app --reload, pytest, alembic upgrade head',
136
+ patterns: 'Pydantic models for validation, Depends() for dependency injection, async/await, path operations, background tasks, lifespan events',
137
+ antiPatterns: 'Sync database calls in async endpoints, no Pydantic validation, global mutable state, not using Depends() for shared logic, blocking the event loop',
138
+ testing: 'pytest + httpx AsyncClient',
139
+ migration: 'alembic init, alembic revision --autogenerate -m "description", alembic upgrade head',
140
+ commonBugs: 'Mixing sync/async database drivers, Pydantic v2 model_validator vs v1 validator, circular imports in routers, not closing DB connections in lifespan, CORS middleware order matters',
141
+ verificationRules: ['Pydantic schemas exist for all request/response models', 'Dependencies use Depends() injection', 'Async endpoints use async database driver', 'Alembic migrations exist for schema changes', 'Error handlers return consistent JSON format'],
142
+ planningHints: ['Plan Pydantic schemas before routers', 'Plan database models with Alembic migrations', 'Group related endpoints in same router', 'Plan dependency injection for shared resources (DB, auth)'],
143
+ testExamples: {
144
+ unit: "from httpx import AsyncClient, ASGITransport\nfrom app.main import app\n\nasync def test_read_users():\n async with AsyncClient(transport=ASGITransport(app=app), base_url='http://test') as ac:\n response = await ac.get('/users')\n assert response.status_code == 200",
145
+ integration: "async def test_create_user(client: AsyncClient):\n response = await client.post('/users', json={'email': 'test@example.com'})\n assert response.status_code == 201\n assert response.json()['email'] == 'test@example.com'"
146
+ }
102
147
  },
103
148
  'Astro': {
104
149
  structure: 'src/pages, src/components, src/layouts, public/',
@@ -235,26 +280,90 @@ function generateExpertise(brainDir, role = 'general') {
235
280
  sections.push('');
236
281
  }
237
282
 
238
- // Role-specific guidance
239
- if (role === 'planner') {
283
+ // Role-specific deep guidance with framework-specific content
284
+ if (role === 'planner' && fwInfo) {
240
285
  sections.push('### Planner Guidance');
241
286
  sections.push('- Ensure task file paths match framework directory structure');
242
287
  sections.push('- Include test tasks using the detected test framework');
243
288
  sections.push('- Set must_haves that reference framework conventions');
289
+ if (fwInfo.planningHints) {
290
+ sections.push('');
291
+ sections.push(`### ${framework} Planning Rules`);
292
+ for (const hint of fwInfo.planningHints) {
293
+ sections.push(`- ${hint}`);
294
+ }
295
+ }
296
+ if (fwInfo.migration) {
297
+ sections.push(`- Migration commands: ${fwInfo.migration}`);
298
+ }
299
+ if (fwInfo.testExamples) {
300
+ sections.push('');
301
+ sections.push(`### Example must_haves for ${framework}`);
302
+ sections.push('```yaml');
303
+ sections.push('truths:');
304
+ if (fwInfo.verificationRules) {
305
+ for (const rule of fwInfo.verificationRules.slice(0, 3)) {
306
+ sections.push(` - "${rule}"`);
307
+ }
308
+ }
309
+ sections.push('```');
310
+ }
244
311
  sections.push('');
245
- } else if (role === 'executor') {
312
+ } else if (role === 'executor' && fwInfo) {
246
313
  sections.push('### Executor Guidance');
247
314
  sections.push('- Use framework CLI commands (listed above) to scaffold where possible');
248
315
  sections.push('- Follow naming conventions strictly');
249
316
  sections.push('- AVOID all listed anti-patterns');
250
317
  sections.push('- Write tests in the detected test framework');
318
+ if (fwInfo.commonBugs) {
319
+ sections.push('');
320
+ sections.push(`### Common ${framework} Bugs to Avoid`);
321
+ for (const [i, bug] of fwInfo.commonBugs.split(', ').entries()) {
322
+ sections.push(`${i + 1}. ${bug}`);
323
+ }
324
+ }
325
+ if (fwInfo.testExamples) {
326
+ sections.push('');
327
+ sections.push(`### Example Test (${framework})`);
328
+ sections.push('```');
329
+ sections.push(fwInfo.testExamples.unit);
330
+ sections.push('```');
331
+ }
251
332
  sections.push('');
252
- } else if (role === 'verifier') {
333
+ } else if (role === 'verifier' && fwInfo) {
253
334
  sections.push('### Verifier Guidance');
254
335
  sections.push('- Verify files are in correct framework directories');
255
336
  sections.push('- Check naming follows detected conventions');
256
337
  sections.push('- Scan for anti-patterns from the list above');
257
338
  sections.push('- Verify tests use the correct test framework');
339
+ if (fwInfo.verificationRules) {
340
+ sections.push('');
341
+ sections.push(`### ${framework} Verification Checklist`);
342
+ for (const rule of fwInfo.verificationRules) {
343
+ sections.push(`- [ ] ${rule}`);
344
+ }
345
+ }
346
+ sections.push('');
347
+ } else {
348
+ // Generic role guidance (no framework info or general role)
349
+ if (role === 'planner') {
350
+ sections.push('### Planner Guidance');
351
+ sections.push('- Ensure task file paths match framework directory structure');
352
+ sections.push('- Include test tasks using the detected test framework');
353
+ sections.push('- Set must_haves that reference framework conventions');
354
+ } else if (role === 'executor') {
355
+ sections.push('### Executor Guidance');
356
+ sections.push('- Use framework CLI commands (listed above) to scaffold where possible');
357
+ sections.push('- Follow naming conventions strictly');
358
+ sections.push('- AVOID all listed anti-patterns');
359
+ sections.push('- Write tests in the detected test framework');
360
+ } else if (role === 'verifier') {
361
+ sections.push('### Verifier Guidance');
362
+ sections.push('- Verify files are in correct framework directories');
363
+ sections.push('- Check naming follows detected conventions');
364
+ sections.push('- Scan for anti-patterns from the list above');
365
+ sections.push('- Verify tests use the correct test framework');
366
+ }
258
367
  sections.push('');
259
368
  }
260
369
 
@@ -312,9 +421,29 @@ function generateContext7Queries(detectionOrBrainDir) {
312
421
  return lines.join('\n');
313
422
  }
314
423
 
424
+ /**
425
+ * Get the detected framework name mapped to overlay directory name.
426
+ * @param {string} brainDir
427
+ * @returns {string|null} Overlay directory name (e.g., 'laravel', 'nextjs') or null
428
+ */
429
+ function getDetectedFramework(brainDir) {
430
+ const detection = readDetection(brainDir);
431
+ if (!detection?.stack?.primary?.framework && !detection?.stack?.framework) return null;
432
+ const fw = (detection.stack.primary?.framework || detection.stack.framework).toLowerCase();
433
+ const map = {
434
+ 'laravel': 'laravel', 'next.js': 'nextjs', 'react native': 'react-native',
435
+ 'express': 'express', 'django': 'django', 'fastapi': 'fastapi',
436
+ 'rails': 'rails', 'vue.js': 'vuejs', 'nestjs': 'nestjs',
437
+ 'flutter': 'flutter', 'spring boot': 'spring-boot', 'sveltekit': 'sveltekit',
438
+ 'astro': 'astro'
439
+ };
440
+ return map[fw] || null;
441
+ }
442
+
315
443
  module.exports = {
316
444
  generateExpertise,
317
445
  generateContext7Queries,
318
446
  findFrameworkPattern,
447
+ getDetectedFramework,
319
448
  STACK_PATTERNS
320
449
  };
@@ -45,4 +45,22 @@ function resolvePath(obj, dotPath) {
45
45
  return current;
46
46
  }
47
47
 
48
- module.exports = { loadTemplate, interpolate };
48
+ /**
49
+ * Load a template with optional framework-specific overlay appended.
50
+ * Overlay files live in bin/templates/overlays/{framework}/{name}-overlay.md.
51
+ * @param {string} name - Template name (without .md)
52
+ * @param {string|null} framework - Framework overlay directory name (e.g., 'laravel', 'nextjs')
53
+ * @returns {string} Template content with overlay appended (if exists)
54
+ */
55
+ function loadTemplateWithOverlay(name, framework) {
56
+ let template = loadTemplate(name);
57
+ if (framework) {
58
+ const overlayPath = path.join(TEMPLATES_DIR, 'overlays', framework, `${name}-overlay.md`);
59
+ if (fs.existsSync(overlayPath)) {
60
+ template += '\n\n' + fs.readFileSync(overlayPath, 'utf8');
61
+ }
62
+ }
63
+ return template;
64
+ }
65
+
66
+ module.exports = { loadTemplate, interpolate, loadTemplateWithOverlay };
@@ -0,0 +1,31 @@
1
+ ## Laravel-Specific Execution Rules
2
+
3
+ ### Migrations
4
+ - Always create migration before model: `php artisan make:migration create_X_table`
5
+ - Use `php artisan make:model -mf` to scaffold model + migration + factory together
6
+ - Test migration rollback: `php artisan migrate:rollback --step=1`
7
+ - Never modify existing migrations in production — create new ones
8
+
9
+ ### Eloquent
10
+ - Use query scopes for reusable queries: `scopeActive`, `scopeVerified`
11
+ - Never use raw SQL in controllers — use Eloquent or Query Builder
12
+ - Always eager-load relationships: `User::with('posts')->get()` to prevent N+1
13
+ - Use `$fillable` or `$guarded` on every model — never leave mass assignment unprotected
14
+
15
+ ### Controllers
16
+ - Use API Resource Controllers: `php artisan make:controller UserController --api`
17
+ - Validate with Form Requests, not inline: `php artisan make:request StoreUserRequest`
18
+ - Return API Resources for JSON: `return new UserResource($user)`
19
+ - Keep controllers thin — extract business logic to Service or Action classes
20
+
21
+ ### Queue Jobs
22
+ - Always implement `ShouldQueue` for background tasks
23
+ - Use `$tries` and `$backoff` properties for retry strategy
24
+ - Implement `failed()` method for error handling
25
+ - Never use `resolve()` in job constructor — serialize only IDs
26
+
27
+ ### Testing
28
+ - Use `RefreshDatabase` trait for database tests
29
+ - Use factories for test data: `User::factory()->create()`
30
+ - Assert database state: `$this->assertDatabaseHas('users', ['email' => ...])`
31
+ - Use `actingAs($user)` for authenticated requests
@@ -0,0 +1,33 @@
1
+ ## Laravel-Specific Planning
2
+
3
+ ### Task Ordering
4
+ 1. **Database first**: Migrations and models before controllers
5
+ 2. **Validation second**: Form Requests before controller logic
6
+ 3. **API last**: Resources and routes after business logic
7
+ 4. **Tests throughout**: Each task should include its test
8
+
9
+ ### Standard Feature Pattern
10
+ For each new feature, plan these tasks in order:
11
+ 1. Migration + Model + Factory
12
+ 2. Form Request (validation rules)
13
+ 3. Service/Action class (business logic)
14
+ 4. Controller (thin, delegates to service)
15
+ 5. API Resource (JSON serialization)
16
+ 6. Route registration + middleware
17
+ 7. Feature test
18
+
19
+ ### must_haves Template for Laravel
20
+ ```yaml
21
+ truths:
22
+ - "Migration creates {table} with all required columns"
23
+ - "Model has $fillable for mass-assignable fields"
24
+ - "Form Request validates all user input with correct rules"
25
+ - "Controller delegates to Service, returns API Resource"
26
+ - "Feature test covers happy path and validation errors"
27
+ artifacts:
28
+ - path: "database/migrations/xxxx_create_{table}_table.php"
29
+ - path: "app/Models/{Model}.php"
30
+ - path: "app/Http/Requests/{StoreModel}Request.php"
31
+ - path: "app/Http/Controllers/{Model}Controller.php"
32
+ - path: "tests/Feature/{Model}Test.php"
33
+ ```
@@ -0,0 +1,28 @@
1
+ ## Laravel-Specific Verification
2
+
3
+ ### Migration Checks
4
+ - [ ] Migration file exists for every new model
5
+ - [ ] Migration has both `up()` and `down()` methods
6
+ - [ ] Foreign key constraints defined with `->constrained()`
7
+ - [ ] Indexes added for frequently queried columns
8
+
9
+ ### Model Checks
10
+ - [ ] `$fillable` or `$guarded` property set
11
+ - [ ] Relationships defined (hasMany, belongsTo, etc.)
12
+ - [ ] Factory exists in `database/factories/`
13
+
14
+ ### Controller Checks
15
+ - [ ] Form Request used for validation (not inline `$request->validate()`)
16
+ - [ ] API Resource used for JSON responses
17
+ - [ ] No business logic in controller (delegated to Service/Action)
18
+
19
+ ### Route Checks
20
+ - [ ] Route registered in `routes/api.php` or `routes/web.php`
21
+ - [ ] Route uses middleware for authentication/authorization
22
+ - [ ] RESTful naming convention followed
23
+
24
+ ### Test Checks
25
+ - [ ] Feature test exists in `tests/Feature/`
26
+ - [ ] Uses `RefreshDatabase` trait
27
+ - [ ] Factory used for test data
28
+ - [ ] HTTP assertions used (`assertStatus`, `assertJson`, etc.)
@@ -0,0 +1,24 @@
1
+ ## Next.js-Specific Execution Rules
2
+
3
+ ### App Router
4
+ - Server Components are the default — only add `"use client"` when you need interactivity
5
+ - Use `loading.tsx` for Suspense boundaries on async pages
6
+ - Use `error.tsx` for error boundaries
7
+ - Place shared layouts in `layout.tsx` at each route segment
8
+
9
+ ### Data Fetching
10
+ - Fetch data in Server Components, not in client useEffect
11
+ - Use Server Actions for mutations (form submissions, data writes)
12
+ - Cache fetch results with `next.revalidate` or `cache: 'force-cache'`
13
+ - Use `generateStaticParams()` for static generation of dynamic routes
14
+
15
+ ### API Route Handlers
16
+ - Export named functions matching HTTP methods: `GET`, `POST`, `PUT`, `DELETE`
17
+ - Use `NextRequest` and `NextResponse` types
18
+ - Place in `app/api/` directory
19
+
20
+ ### Testing
21
+ - Use `@testing-library/react` for component tests
22
+ - Mock `next/navigation` hooks in tests
23
+ - Test Server Components as async functions
24
+ - Use `render()` + `screen.getByRole()` pattern
@@ -0,0 +1,21 @@
1
+ ## Next.js-Specific Verification
2
+
3
+ ### Route Structure
4
+ - [ ] Page components exist in `app/` directory structure
5
+ - [ ] `loading.tsx` exists for pages with async data fetching
6
+ - [ ] `error.tsx` exists for route segments with error-prone operations
7
+ - [ ] `layout.tsx` exists for shared navigation/headers
8
+
9
+ ### Component Checks
10
+ - [ ] Client components have `"use client"` directive at top
11
+ - [ ] Server Components do NOT use hooks (useState, useEffect)
12
+ - [ ] No `useEffect` for data fetching in server-renderable pages
13
+
14
+ ### API Route Checks
15
+ - [ ] Route Handlers export correct HTTP method functions
16
+ - [ ] Responses use `NextResponse.json()` format
17
+ - [ ] Error responses return appropriate status codes
18
+
19
+ ### Metadata
20
+ - [ ] Pages export `metadata` or `generateMetadata()` for SEO
21
+ - [ ] Dynamic routes have `generateStaticParams()` if applicable
@@ -0,0 +1,28 @@
1
+ ## React Native-Specific Execution Rules
2
+
3
+ ### Platform Handling
4
+ - Use `Platform.OS` or `Platform.select()` for platform-specific code
5
+ - Create `.ios.tsx` and `.android.tsx` files for complex platform differences
6
+ - Always test on both iOS and Android (or document platform limitations)
7
+
8
+ ### Navigation
9
+ - Define navigation structure before building screens
10
+ - Use typed navigation with `RootStackParamList`
11
+ - Handle deep linking configuration early
12
+
13
+ ### Performance
14
+ - Use `FlatList` with `keyExtractor` and `getItemLayout` for long lists
15
+ - Avoid inline functions in render — use `useCallback`
16
+ - Use `React.memo()` for pure components
17
+ - Move heavy computation off the JS thread with `InteractionManager`
18
+
19
+ ### Styling
20
+ - Always use `StyleSheet.create()` — never inline style objects
21
+ - Use `Dimensions` or `useWindowDimensions` for responsive layouts
22
+ - Test on different screen sizes
23
+
24
+ ### Testing
25
+ - Use `@testing-library/react-native` for component tests
26
+ - Mock native modules with `jest.mock()`
27
+ - Test user interactions with `fireEvent`
28
+ - Include platform-specific test cases
@@ -0,0 +1,20 @@
1
+ ## React Native-Specific Verification
2
+
3
+ ### Platform Checks
4
+ - [ ] Platform-specific code uses `Platform.OS` or `Platform.select()`
5
+ - [ ] No web-only APIs used (`document`, `window`, `localStorage`)
6
+ - [ ] Assets imported with `require()` for local files
7
+
8
+ ### Component Checks
9
+ - [ ] `FlatList` has `keyExtractor` and optimized rendering
10
+ - [ ] `StyleSheet.create()` used (no inline style objects)
11
+ - [ ] Navigation screens registered in navigator configuration
12
+
13
+ ### Performance Checks
14
+ - [ ] No inline arrow functions in JSX render (use `useCallback`)
15
+ - [ ] Heavy lists use `FlatList` not `ScrollView` with `.map()`
16
+ - [ ] Images have explicit dimensions
17
+
18
+ ### Testing Checks
19
+ - [ ] Component tests use `@testing-library/react-native`
20
+ - [ ] Native modules properly mocked in jest setup
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brain-dev",
3
- "version": "2.0.5",
3
+ "version": "2.1.0",
4
4
  "description": "AI-powered development workflow orchestrator",
5
5
  "author": "halilcosdu",
6
6
  "license": "MIT",