@magic-ingredients/tiny-brain-local 0.20.1 → 0.21.1

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.
Files changed (32) hide show
  1. package/dist/core/mcp-server.d.ts.map +1 -1
  2. package/dist/core/mcp-server.js +11 -8
  3. package/dist/core/repo-registration.d.ts +30 -0
  4. package/dist/core/repo-registration.d.ts.map +1 -0
  5. package/dist/core/repo-registration.js +34 -0
  6. package/dist/prompts/persona/persona.prompt.js +1 -1
  7. package/dist/prompts/planning/planning.prompt.d.ts +0 -8
  8. package/dist/prompts/planning/planning.prompt.d.ts.map +1 -1
  9. package/dist/prompts/planning/planning.prompt.js +0 -193
  10. package/dist/prompts/prompt-registry.d.ts.map +1 -1
  11. package/dist/prompts/prompt-registry.js +0 -2
  12. package/dist/services/analyse-service.d.ts +15 -29
  13. package/dist/services/analyse-service.d.ts.map +1 -1
  14. package/dist/services/analyse-service.js +99 -236
  15. package/dist/services/repo-service.d.ts +5 -0
  16. package/dist/services/repo-service.d.ts.map +1 -1
  17. package/dist/services/repo-service.js +67 -42
  18. package/dist/storage/local-filesystem-adapter.d.ts.map +1 -1
  19. package/dist/storage/local-filesystem-adapter.js +0 -3
  20. package/dist/tools/dashboard/dashboard.tool.d.ts +4 -3
  21. package/dist/tools/dashboard/dashboard.tool.d.ts.map +1 -1
  22. package/dist/tools/dashboard/dashboard.tool.js +53 -6
  23. package/dist/tools/plan/plan.tool.d.ts +0 -1
  24. package/dist/tools/plan/plan.tool.d.ts.map +1 -1
  25. package/dist/tools/plan/plan.tool.js +10 -56
  26. package/dist/tools/quality/quality.tool.js +1 -1
  27. package/dist/tools/recommendations/recommendations.tool.d.ts +13 -0
  28. package/dist/tools/recommendations/recommendations.tool.d.ts.map +1 -0
  29. package/dist/tools/recommendations/recommendations.tool.js +178 -0
  30. package/dist/tools/tool-registry.d.ts.map +1 -1
  31. package/dist/tools/tool-registry.js +4 -0
  32. package/package.json +4 -10
@@ -4,12 +4,12 @@
4
4
  * Handles repository tech stack analysis.
5
5
  * Focused solely on analysis - no context file management.
6
6
  */
7
- import { analyseRepository, formatTechStackChanges, detectTechStackChanges, ConfigService, RepoConfigService, LibraryClient, TechContextService, AgentsMdService, } from '@magic-ingredients/tiny-brain-core';
7
+ import { analyseRepository, formatTechStackChanges, detectTechStackChanges, ConfigService, ConfigSetupService, RepoConfigService, LibraryClient, TechContextService, AgentsMdService, RecommendationService, } from '@magic-ingredients/tiny-brain-core';
8
8
  import { RepoService } from './repo-service.js';
9
9
  import { fileURLToPath } from 'url';
10
10
  import { dirname, join } from 'path';
11
11
  import { existsSync } from 'fs';
12
- import { mkdir, copyFile, readFile, readdir, chmod, stat, writeFile } from 'fs/promises';
12
+ import { readFile, readdir, writeFile } from 'fs/promises';
13
13
  import { getPackageVersion } from '../utils/package-version.js';
14
14
  /**
15
15
  * Service for managing repository analysis
@@ -21,12 +21,14 @@ export class AnalyseService {
21
21
  libraryClient;
22
22
  configService;
23
23
  techContextService;
24
+ recommendationService;
24
25
  constructor(context) {
25
26
  this.context = context;
26
27
  this.configService = new ConfigService(context);
27
28
  this.repoService = new RepoService(context);
28
29
  this.libraryClient = new LibraryClient();
29
30
  this.techContextService = new TechContextService(process.cwd());
31
+ this.recommendationService = new RecommendationService(process.cwd());
30
32
  }
31
33
  /**
32
34
  * Perform repository analysis with unified behavior for initialized/uninitialized repos
@@ -38,15 +40,14 @@ export class AnalyseService {
38
40
  this.context.logger.info(`Repository analysis complete: ${currentAnalysis.languages.join(', ')}`);
39
41
  // Register repository in dashboard
40
42
  await this.registerRepository();
41
- // Initialize documentation directories based on config
42
- const prdInitialized = await this.initializePrdDirectory();
43
- const adrInitialized = await this.initializeAdrDirectory();
44
- const qualityInitialized = await this.initializeQualityDirectory();
45
- const fixesInitialized = await this.initializeFixesDirectory();
46
- // Initialize git hooks for TDD workflow
47
- const gitHooksInstalled = await this.initializeGitHooks();
48
- // Inject skill permissions into .claude/settings.json
49
- const skillPermissionsAdded = await this.injectSkillPermissions();
43
+ // Perform config setup (directories, hooks, permissions, CLAUDE.md)
44
+ const configSetupResult = await this.performConfigSetup();
45
+ const prdInitialized = configSetupResult?.prdInitialized ?? false;
46
+ const adrInitialized = configSetupResult?.adrInitialized ?? false;
47
+ const qualityInitialized = configSetupResult?.qualityInitialized ?? false;
48
+ const fixesInitialized = configSetupResult?.fixesInitialized ?? false;
49
+ const gitHooksInstalled = configSetupResult?.gitHooksInstalled ?? false;
50
+ const skillPermissionsAdded = configSetupResult?.skillPermissionsAdded ?? [];
50
51
  // Check if stack has changed using hash-based comparison
51
52
  const analysisInput = {
52
53
  languages: currentAnalysis.languages,
@@ -80,6 +81,11 @@ export class AnalyseService {
80
81
  if (!dryRun) {
81
82
  writtenTechContexts = await this.fetchAndWriteTechContexts(currentAnalysis);
82
83
  }
84
+ // Fetch and write recommendations from TBR
85
+ let recommendations = [];
86
+ if (!dryRun) {
87
+ recommendations = await this.fetchAndWriteRecommendations(currentAnalysis);
88
+ }
83
89
  // Get enableAgenticCoding preference and sync tech agents
84
90
  // When enabled, copies tech contexts from .tiny-brain/tech/ to .claude/agents/
85
91
  const enableAgentic = await this.configService.isAgenticCodingEnabled();
@@ -92,10 +98,6 @@ export class AnalyseService {
92
98
  if (!dryRun && manageAgentsMd) {
93
99
  agentsMdStatus = await this.generateAgentsMd(currentAnalysis);
94
100
  }
95
- // Write workflow sections to CLAUDE.md based on config flags (SDD, TDD)
96
- if (!dryRun) {
97
- await this.repoService.writeWorkflowSections({ contextPath });
98
- }
99
101
  // Check context block version (for backwards compatibility during migration)
100
102
  const currentVersion = getPackageVersion();
101
103
  const existingContent = await this.repoService.readRepoBlockFromContextFile(contextPath);
@@ -120,6 +122,7 @@ export class AnalyseService {
120
122
  skillPermissionsAdded: skillPermissionsAdded.length > 0 ? skillPermissionsAdded : undefined,
121
123
  writtenTechContexts,
122
124
  agentsMdStatus,
125
+ recommendations,
123
126
  };
124
127
  }
125
128
  if (!stackChanged) {
@@ -141,6 +144,7 @@ export class AnalyseService {
141
144
  skillPermissionsAdded: skillPermissionsAdded.length > 0 ? skillPermissionsAdded : undefined,
142
145
  writtenTechContexts,
143
146
  agentsMdStatus,
147
+ recommendations,
144
148
  };
145
149
  }
146
150
  // Changes detected - compute diff for display purposes
@@ -180,6 +184,7 @@ export class AnalyseService {
180
184
  skillPermissionsAdded: skillPermissionsAdded.length > 0 ? skillPermissionsAdded : undefined,
181
185
  writtenTechContexts,
182
186
  agentsMdStatus,
187
+ recommendations,
183
188
  };
184
189
  }
185
190
  /**
@@ -319,239 +324,52 @@ export class AnalyseService {
319
324
  }
320
325
  }
321
326
  /**
322
- * Initialize PRD directory structure for SDD workflow
323
- * @returns true if directory was created
324
- */
325
- async initializePrdDirectory() {
326
- try {
327
- const enableSDD = await this.configService.isSDDEnabled();
328
- if (!enableSDD) {
329
- this.context.logger.debug('SDD disabled, skipping PRD directory initialization');
330
- return false;
331
- }
332
- const repoRoot = process.cwd();
333
- const prdDir = join(repoRoot, 'docs', 'prd');
334
- if (existsSync(prdDir)) {
335
- return false; // Already exists
336
- }
337
- await mkdir(prdDir, { recursive: true });
338
- this.context.logger.info('✅ Created docs/prd/ directory');
339
- return true;
340
- }
341
- catch (error) {
342
- this.context.logger.warn(`Failed to initialize PRD directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
343
- return false;
344
- }
345
- }
346
- /**
347
- * Initialize ADR directory structure
348
- * @returns true if directory was created
327
+ * Perform config setup using core ConfigSetupService.
328
+ * Delegates directory initialization, git hooks, skill permissions, and CLAUDE.md
329
+ * context block writing to the shared service.
349
330
  */
350
- async initializeAdrDirectory() {
331
+ async performConfigSetup() {
351
332
  try {
352
- const enableADR = await this.configService.isADREnabled();
353
- if (!enableADR) {
354
- this.context.logger.debug('ADR disabled, skipping ADR directory initialization');
355
- return false;
356
- }
357
- const repoRoot = process.cwd();
358
- const adrDir = join(repoRoot, 'docs', 'adr');
359
- if (existsSync(adrDir)) {
360
- return false; // Already exists
361
- }
362
- await mkdir(adrDir, { recursive: true });
363
- this.context.logger.info('✅ Created docs/adr/ directory');
364
- return true;
365
- }
366
- catch (error) {
367
- this.context.logger.warn(`Failed to initialize ADR directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
368
- return false;
369
- }
370
- }
371
- /**
372
- * Initialize Quality directory structure
373
- * @returns true if directory was created
374
- */
375
- async initializeQualityDirectory() {
376
- try {
377
- const enableQuality = await this.configService.isQualityEnabled();
378
- if (!enableQuality) {
379
- this.context.logger.debug('Quality disabled, skipping Quality directory initialization');
380
- return false;
381
- }
382
- const repoRoot = process.cwd();
383
- const qualityDir = join(repoRoot, 'docs', 'quality');
384
- if (existsSync(qualityDir)) {
385
- return false; // Already exists
386
- }
387
- await mkdir(qualityDir, { recursive: true });
388
- this.context.logger.info('✅ Created docs/quality/ directory');
389
- return true;
390
- }
391
- catch (error) {
392
- this.context.logger.warn(`Failed to initialize Quality directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
393
- return false;
394
- }
395
- }
396
- /**
397
- * Initialize fixes tracking directory
398
- * @returns true if directory was created
399
- */
400
- async initializeFixesDirectory() {
401
- try {
402
- const enableSDD = await this.configService.isSDDEnabled();
403
- if (!enableSDD) {
404
- this.context.logger.debug('SDD disabled, skipping fixes directory initialization');
405
- return false;
406
- }
407
- const repoRoot = process.cwd();
408
- const fixesDir = join(repoRoot, '.tiny-brain', 'fixes');
409
- if (existsSync(fixesDir)) {
410
- return false; // Already exists
411
- }
412
- await mkdir(fixesDir, { recursive: true });
413
- this.context.logger.info('✅ Created .tiny-brain/fixes/ directory');
414
- return true;
333
+ const hooksSourceDir = this.resolveHooksDir();
334
+ const version = getPackageVersion();
335
+ const configSetup = new ConfigSetupService({
336
+ repoPath: process.cwd(),
337
+ version,
338
+ hooksSourceDir,
339
+ logger: {
340
+ info: (msg) => this.context.logger.info(msg),
341
+ debug: (msg) => this.context.logger.debug(msg),
342
+ warn: (msg) => this.context.logger.warn(msg),
343
+ },
344
+ });
345
+ return await configSetup.performSetup();
415
346
  }
416
347
  catch (error) {
417
- this.context.logger.warn(`Failed to initialize fixes directory: ${error instanceof Error ? error.message : 'Unknown error'}`);
418
- return false;
348
+ this.context.logger.warn(`Config setup failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
349
+ return undefined;
419
350
  }
420
351
  }
421
352
  /**
422
- * Initialize git hooks for TDD workflow
423
- * Copies hook templates to .git/hooks/ if not already present
424
- * @returns true if any hooks were installed
353
+ * Resolve hooks template directory from the core package.
354
+ * Tries the core package's templates first (monorepo layout), then falls back
355
+ * to the MCP package's own templates for backward compatibility.
425
356
  */
426
- async initializeGitHooks() {
357
+ resolveHooksDir() {
427
358
  try {
428
- const repoRoot = process.cwd();
429
- const gitHooksDir = join(repoRoot, '.git', 'hooks');
430
- // Check if .git directory exists (is this a git repo?)
431
- if (!existsSync(join(repoRoot, '.git'))) {
432
- this.context.logger.debug('Not a git repository, skipping git hooks initialization');
433
- return false;
434
- }
435
- // Find the source hook templates in the installed package
436
359
  const __filename = fileURLToPath(import.meta.url);
437
360
  const __dirname = dirname(__filename);
438
- const srcHooksDir = join(__dirname, '..', '..', 'templates', 'hooks');
439
- // Verify source hooks directory exists
440
- if (!existsSync(srcHooksDir)) {
441
- this.context.logger.debug(`Hook templates not found at ${srcHooksDir}, skipping`);
442
- return false;
443
- }
444
- // Ensure .git/hooks directory exists
445
- await mkdir(gitHooksDir, { recursive: true });
446
- // List of hooks to install
447
- const hooks = ['commit-msg', 'post-commit', 'pre-commit'];
448
- let anyInstalled = false;
449
- for (const hookName of hooks) {
450
- const srcHook = join(srcHooksDir, hookName);
451
- const destHook = join(gitHooksDir, hookName);
452
- // Skip if source doesn't exist
453
- if (!existsSync(srcHook)) {
454
- continue;
455
- }
456
- // Skip if hook already exists (don't overwrite user customizations)
457
- if (existsSync(destHook)) {
458
- // Check if it's our hook by looking for our signature comment
459
- try {
460
- const existingContent = await readFile(destHook, 'utf-8');
461
- if (!existingContent.includes('Installed by: tiny-brain')) {
462
- this.context.logger.debug(`Skipping ${hookName} - custom hook already exists`);
463
- continue;
464
- }
465
- // Our hook already installed, check if source is newer
466
- const srcStat = await stat(srcHook);
467
- const destStat = await stat(destHook);
468
- if (srcStat.mtime <= destStat.mtime) {
469
- continue; // Already up to date
470
- }
471
- }
472
- catch {
473
- continue; // Can't read, skip
474
- }
475
- }
476
- // Copy hook
477
- await copyFile(srcHook, destHook);
478
- // Make executable (755)
479
- await chmod(destHook, 0o755);
480
- anyInstalled = true;
481
- this.context.logger.info(`✅ Installed git hook: ${hookName}`);
482
- }
483
- if (anyInstalled) {
484
- this.context.logger.info('Git hooks initialized in .git/hooks/');
485
- }
486
- return anyInstalled;
487
- }
488
- catch (error) {
489
- this.context.logger.warn(`Failed to initialize git hooks: ${error instanceof Error ? error.message : 'Unknown error'}`);
490
- return false;
491
- }
492
- }
493
- /**
494
- * Inject skill permissions into .claude/settings.json
495
- * These permissions allow skills to read/write to specific directories without prompts
496
- * @returns Array of permissions that were added (empty if none added)
497
- */
498
- async injectSkillPermissions() {
499
- const SKILL_PERMISSIONS = [
500
- 'Write(.tiny-brain/**)',
501
- 'Write(docs/prd/**)',
502
- 'Write(docs/adr/**)',
503
- 'Write(docs/quality/**)',
504
- 'Read(.tiny-brain/**)',
505
- 'Read(docs/prd/**)',
506
- 'Read(docs/adr/**)',
507
- 'Read(docs/quality/**)',
508
- 'Read(.claude/skills/**/templates/**)',
509
- ];
510
- try {
511
- const repoRoot = process.cwd();
512
- const settingsDir = join(repoRoot, '.claude');
513
- const settingsPath = join(settingsDir, 'settings.json');
514
- // Ensure .claude directory exists
515
- await mkdir(settingsDir, { recursive: true });
516
- // Read existing settings or start with empty object
517
- let settings = {};
518
- if (existsSync(settingsPath)) {
519
- try {
520
- const content = await readFile(settingsPath, 'utf-8');
521
- settings = JSON.parse(content);
522
- }
523
- catch {
524
- this.context.logger.warn('Failed to parse existing settings.json, will create new one');
525
- settings = {};
526
- }
527
- }
528
- // Ensure permissions.allow array exists
529
- if (!settings.permissions) {
530
- settings.permissions = {};
531
- }
532
- const permissions = settings.permissions;
533
- if (!Array.isArray(permissions.allow)) {
534
- permissions.allow = [];
535
- }
536
- const allowList = permissions.allow;
537
- // Add missing permissions
538
- const addedPermissions = [];
539
- for (const perm of SKILL_PERMISSIONS) {
540
- if (!allowList.includes(perm)) {
541
- allowList.push(perm);
542
- addedPermissions.push(perm);
543
- }
544
- }
545
- // Only write if we added new permissions
546
- if (addedPermissions.length > 0) {
547
- await writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
548
- this.context.logger.info(`Added ${addedPermissions.length} skill permissions to .claude/settings.json`);
549
- }
550
- return addedPermissions;
361
+ // First try: core package's templates (via monorepo workspace link)
362
+ const coreHooksDir = join(__dirname, '..', '..', '..', 'tiny-brain-core', 'templates', 'hooks');
363
+ if (existsSync(coreHooksDir))
364
+ return coreHooksDir;
365
+ // Second try: the MCP package's own templates (backward compatibility)
366
+ const mcpHooksDir = join(__dirname, '..', '..', 'templates', 'hooks');
367
+ if (existsSync(mcpHooksDir))
368
+ return mcpHooksDir;
369
+ return undefined;
551
370
  }
552
- catch (error) {
553
- this.context.logger.warn(`Failed to inject skill permissions: ${error instanceof Error ? error.message : 'Unknown error'}`);
554
- return [];
371
+ catch {
372
+ return undefined;
555
373
  }
556
374
  }
557
375
  /**
@@ -585,6 +403,30 @@ export class AnalyseService {
585
403
  return [];
586
404
  }
587
405
  }
406
+ /**
407
+ * Fetch recommendations from TBR and write them via RecommendationService
408
+ */
409
+ async fetchAndWriteRecommendations(analysis) {
410
+ try {
411
+ const localContext = this.context;
412
+ if (!localContext.authToken?.token) {
413
+ this.context.logger.debug('No auth token available for recommendations API call');
414
+ return [];
415
+ }
416
+ const response = await this.libraryClient.getRecommendations(analysis, localContext.authToken.token);
417
+ if (!response?.recommendations || response.recommendations.length === 0) {
418
+ this.context.logger.debug('No recommendations returned from TBR');
419
+ return [];
420
+ }
421
+ await this.recommendationService.writeRecommendations(response.recommendations, response.fetchedAt);
422
+ this.context.logger.info(`Fetched ${response.recommendations.length} recommendation(s) from TBR`);
423
+ return response.recommendations;
424
+ }
425
+ catch (error) {
426
+ this.context.logger.warn(`Failed to fetch recommendations from TBR: ${error instanceof Error ? error.message : 'Unknown error'}`);
427
+ return [];
428
+ }
429
+ }
588
430
  /**
589
431
  * Format analysis output for display
590
432
  */
@@ -704,6 +546,27 @@ export class AnalyseService {
704
546
  : 'Up to date';
705
547
  lines.push(`\nAGENTS.md: ${statusLabel}`);
706
548
  }
549
+ // Recommendations section (shown when recommendations exist)
550
+ if (result.recommendations && result.recommendations.length > 0) {
551
+ lines.push('\n🎯 Recommendations:');
552
+ const groupLabels = { skill: 'Skills', agent: 'Agents', hook: 'Hooks' };
553
+ const grouped = new Map();
554
+ for (const rec of result.recommendations) {
555
+ const existing = grouped.get(rec.type) ?? [];
556
+ grouped.set(rec.type, [...existing, rec]);
557
+ }
558
+ for (const [type, label] of Object.entries(groupLabels)) {
559
+ const recs = grouped.get(type);
560
+ if (recs && recs.length > 0) {
561
+ lines.push(`\n ${label}:`);
562
+ for (const rec of recs) {
563
+ lines.push(` • ${rec.name} — ${rec.description}`);
564
+ lines.push(` Reason: ${rec.reason}`);
565
+ }
566
+ }
567
+ }
568
+ lines.push('\n Use `recommendations operation=accept` to install');
569
+ }
707
570
  return lines.join('\n');
708
571
  }
709
572
  /**
@@ -8,6 +8,7 @@
8
8
  import type { RequestContext } from '../types/request-context.js';
9
9
  export interface WriteWorkflowSectionsOptions {
10
10
  contextPath?: string;
11
+ isMonorepo?: boolean;
11
12
  }
12
13
  /**
13
14
  * Service for repository context operations
@@ -75,6 +76,10 @@ export declare class RepoService {
75
76
  * Format the Progress Tracking Auto-Commit Configuration section
76
77
  */
77
78
  private formatProgressTrackingSection;
79
+ /**
80
+ * Format the Worktree Merge Workflow section
81
+ */
82
+ private formatWorktreeMergeSection;
78
83
  /**
79
84
  * Format the Operational Tracking Directory section
80
85
  */
@@ -1 +1 @@
1
- {"version":3,"file":"repo-service.d.ts","sourceRoot":"","sources":["../../src/services/repo-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,WAAW;IAMV,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA2B;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAyB;IAE/D,OAAO,CAAC,aAAa,CAAgB;gBAEjB,OAAO,EAAE,cAAc;IAS3C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACG,4BAA4B,CAChC,eAAe,GAAE,MAAoB,GACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmCzB;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOvD;;OAEG;IACH,cAAc,IAAI,OAAO;IAUzB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcjD;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAa5B;;;;;;;;OAQG;IACG,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IA6FtF;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA0BhC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA6GlC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAoChC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAwErC;;OAEG;IACH,OAAO,CAAC,gCAAgC;CAyCzC"}
1
+ {"version":3,"file":"repo-service.d.ts","sourceRoot":"","sources":["../../src/services/repo-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,WAAW;IAMV,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA2B;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAyB;IAE/D,OAAO,CAAC,aAAa,CAAgB;gBAEjB,OAAO,EAAE,cAAc;IAS3C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACG,4BAA4B,CAChC,eAAe,GAAE,MAAoB,GACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmCzB;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOvD;;OAEG;IACH,cAAc,IAAI,OAAO;IAUzB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcjD;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAa5B;;;;;;;;OAQG;IACG,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkGtF;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA6BhC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAgGlC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA4ChC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAiErC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA6BlC;;OAEG;IACH,OAAO,CAAC,gCAAgC;CAyCzC"}
@@ -155,7 +155,7 @@ export class RepoService {
155
155
  // Build the workflow sections content
156
156
  let workflowContent = '';
157
157
  // Repository Context (always when workflow sections are enabled)
158
- workflowContent += this.formatRepoContextSection(enableAgentic);
158
+ workflowContent += this.formatRepoContextSection(enableAgentic, options.isMonorepo);
159
159
  // Commit Message Format (when SDD enabled)
160
160
  if (enableSDD) {
161
161
  workflowContent += this.formatCommitMessageSection();
@@ -168,6 +168,10 @@ export class RepoService {
168
168
  if (enableSDD) {
169
169
  workflowContent += this.formatProgressTrackingSection();
170
170
  }
171
+ // Worktree Merge Workflow (when SDD enabled)
172
+ if (enableSDD) {
173
+ workflowContent += this.formatWorktreeMergeSection();
174
+ }
171
175
  // Operational Tracking Directory (when SDD enabled)
172
176
  if (enableSDD) {
173
177
  workflowContent += this.formatOperationalTrackingSection();
@@ -226,13 +230,15 @@ export class RepoService {
226
230
  * Format the Repository Context section
227
231
  * When agentic coding is enabled, includes a mandatory agent delegation section
228
232
  */
229
- formatRepoContextSection(enableAgenticCoding) {
230
- let content = `## Repository Context
231
-
232
- Before starting work, read \`AGENTS.md\` for comprehensive project context (tech stack, commands, structure).
233
- Also read \`.tiny-brain/analysis.json\` for detailed detection data and test patterns.
234
-
235
- `;
233
+ formatRepoContextSection(enableAgenticCoding, isMonorepo) {
234
+ let content = `## Repository Context\n\n`;
235
+ if (isMonorepo) {
236
+ content += `Before starting work, read the root \`AGENTS.md\` for comprehensive project context (tech stack, commands, structure).\nWhen working in a specific package, also read that package's \`AGENTS.md\` for package-specific details.\n`;
237
+ }
238
+ else {
239
+ content += `Before starting work, read \`AGENTS.md\` for comprehensive project context (tech stack, commands, structure).\n`;
240
+ }
241
+ content += `Also read \`.tiny-brain/analysis.json\` for detailed detection data and test patterns.\n\n`;
236
242
  if (enableAgenticCoding) {
237
243
  content += `## Agent Delegation (MANDATORY)
238
244
 
@@ -297,19 +303,6 @@ Description of changes...
297
303
 
298
304
  **WARNING:** The commit-msg hook will reject commits missing required headers.
299
305
 
300
- ### Updating Markdown After Commits
301
-
302
- **For PRD tasks:**
303
- 1. Open the feature file: \`docs/prd/{prd-id}/features/{feature-id}.md\`
304
- 2. Update the task with status and commitSha:
305
- \`\`\`markdown
306
- ### 1. Task description
307
- status: completed
308
- commitSha: abc1234
309
- \`\`\`
310
- 3. Run: \`npx tiny-brain sync-file docs/prd/{prd-id}/features/{feature-id}.md\`
311
- 4. If ALL tasks in the feature are complete, update the PRD status
312
-
313
306
  ### Fix Status Workflow
314
307
 
315
308
  Fix documents have three statuses: \`documented\` → \`in_progress\` → \`resolved\`
@@ -317,7 +310,7 @@ Fix documents have three statuses: \`documented\` → \`in_progress\` → \`reso
317
310
  **When starting work on a fix:**
318
311
  1. Open the fix file: \`.tiny-brain/fixes/{fix-id}.md\`
319
312
  2. Update frontmatter: \`status: in_progress\`
320
- 3. Run: \`npx tiny-brain sync-file .tiny-brain/fixes/{fix-id}.md\`
313
+ 3. Run: \`npx tiny-brain sync-progress .tiny-brain/fixes/{fix-id}.md\`
321
314
 
322
315
  **After each commit:**
323
316
  1. Update the completed task(s) in the markdown:
@@ -333,7 +326,7 @@ Fix documents have three statuses: \`documented\` → \`in_progress\` → \`reso
333
326
  status: superseded
334
327
  commitSha: null
335
328
  \`\`\`
336
- 4. Run: \`npx tiny-brain sync-file .tiny-brain/fixes/{fix-id}.md\`
329
+ 4. Run: \`npx tiny-brain sync-progress .tiny-brain/fixes/{fix-id}.md\`
337
330
 
338
331
  **When all tasks are complete:**
339
332
  1. **ONLY set \`status: resolved\`** when ALL tasks are accounted for:
@@ -354,9 +347,9 @@ Fix documents have three statuses: \`documented\` → \`in_progress\` → \`reso
354
347
  - path/to/file1.ts
355
348
  - path/to/file2.ts
356
349
  \`\`\`
357
- 3. Run: \`npx tiny-brain sync-file .tiny-brain/fixes/{fix-id}.md\`
350
+ 3. Run: \`npx tiny-brain sync-progress .tiny-brain/fixes/{fix-id}.md\`
358
351
 
359
- **Note:** The markdown file is the source of truth. The \`sync-file\` command updates \`progress.json\` from the markdown.
352
+ **Note:** The markdown file is the source of truth. The \`sync-progress\` command updates \`progress.json\` from the markdown.
360
353
 
361
354
  `;
362
355
  }
@@ -371,30 +364,38 @@ IMPORTANT: This repository follows strict Test-Driven Development (TDD) with a 3
371
364
  **Red → Green → Refactor Cycle:**
372
365
 
373
366
  1. **Red Phase** (\`test:\` or \`test(scope):\` commits):
367
+ - **CRITICAL — you MUST run before writing tests:**
368
+ \`npx tiny-brain update-phase --phase red --event start --task '...' [--fix ID | --prd ID --feature ID]\`
374
369
  - Write failing tests first
375
370
  - Tests SHOULD fail (that's the point!)
376
371
  - Use: \`git commit -m "test: ..."\` or \`git commit -m "test(api): ..."\`
377
372
  - Git hook automatically runs typecheck + lint but SKIPS tests
378
373
  - Tracked in: \`testCommitSha\` field
374
+ - **After commit:** post-commit hook automatically transitions to \`green:start\`
379
375
 
380
376
  2. **Green Phase** (\`feat:\` or \`feat(scope):\` commits):
377
+ - Phase started automatically by post-commit hook after \`test:\` commit (no manual step needed)
381
378
  - Implement minimum code to make tests pass
382
379
  - Use: \`git commit -m "feat: ..."\` or \`git commit -m "feat(auth): ..."\`
383
380
  - Git hook automatically runs full checks (typecheck + lint + test)
384
381
  - Tracked in: \`commitSha\` field
385
382
  - **Marks task as COMPLETED**
383
+ - **After commit:** adversarial review hook sets \`adversarial:start\` and outputs review instructions
386
384
 
387
385
  3. **Refactor Phase** (\`refactor:\` or \`refactor(scope):\` commits - optional):
386
+ - Triggered by adversarial review suggestions
388
387
  - Improve code quality without changing behavior
389
388
  - Use: \`git commit -m "refactor: ..."\` or \`git commit -m "refactor(plan): ..."\`
390
389
  - Git hook automatically runs full checks (typecheck + lint + test)
391
- - Tracked in: \`refactorCommitSha\` field
390
+ - **After commit:** post-commit hook automatically transitions \`adversarial-refactor:complete\`
391
+
392
+ 4. **Progress Commit** (end of cycle):
393
+ - After the full RED→GREEN→REFACTOR cycle is complete
394
+ - Run: \`npx tiny-brain commit-progress\`
395
+
396
+ **What you must do manually:** Only \`update-phase --phase red --event start\` before writing tests. Everything else is automated by hooks.
392
397
 
393
- **Why This Matters:**
394
- - Git hooks automatically detect commit type and run appropriate checks
395
- - Separate commit tracking enables Feature 9 (TDD phase tracking in dashboard)
396
- - Provides audit trail of development process
397
- - Only \`feat:\` or \`feat(scope):\` commits mark tasks as completed
398
+ **What you must NOT do:** Do not manually call \`update-phase\` for green:start, adversarial:start, or adversarial-refactor:complete — the hooks handle these.
398
399
 
399
400
  `;
400
401
  }
@@ -410,18 +411,11 @@ By default, tiny-brain uses **manual mode** for progress.json commits. This mean
410
411
 
411
412
  When you complete a task with a \`feat:\` or \`test:\` commit:
412
413
  1. The post-commit hook updates progress.json automatically
413
- 2. You'll see a friendly message with instructions:
414
+ 2. You'll see a friendly message with instructions
415
+ 3. Commit the progress.json changes by running:
416
+ \`\`\`bash
417
+ npx tiny-brain commit-progress
414
418
  \`\`\`
415
- 📝 Progress tracking updated!
416
-
417
- To commit progress.json changes, run:
418
- git add .tiny-brain/progress/*.json
419
- git commit -m "chore: update progress tracking"
420
-
421
- 💡 Tip: Enable auto-commit to skip this step:
422
- npx tiny-brain config preferences set autoCommitProgress true
423
- \`\`\`
424
- 3. Commit the progress.json changes when you're ready
425
419
 
426
420
  ### Enabling Auto-Commit
427
421
 
@@ -470,6 +464,37 @@ Settings follow this precedence (highest to lowest):
470
464
 
471
465
  This allows you to set a global preference but override it per repository as needed.
472
466
 
467
+ `;
468
+ }
469
+ /**
470
+ * Format the Worktree Merge Workflow section
471
+ */
472
+ formatWorktreeMergeSection() {
473
+ return `## Worktree Merge Workflow
474
+
475
+ When merging a worktree branch back into the main branch, \`progress.json\` files may conflict because both branches tracked different task completions. The \`post-merge\` git hook handles this automatically.
476
+
477
+ ### How It Works
478
+
479
+ 1. After a merge, the hook detects if any \`.tiny-brain/progress/*.json\` or \`.tiny-brain/fixes/progress.json\` files changed
480
+ 2. If so, it re-runs \`npx tiny-brain sync-progress\` on all markdown sources to regenerate progress.json
481
+ 3. The regenerated files are staged and committed as \`chore: re-sync progress after merge\`
482
+
483
+ ### Handling Merge Conflicts in progress.json
484
+
485
+ If you encounter a merge conflict in progress.json:
486
+ 1. Accept either version (it doesn't matter which)
487
+ 2. Complete the merge
488
+ 3. The post-merge hook will regenerate progress.json from the markdown sources automatically
489
+
490
+ ### Manual Re-sync
491
+
492
+ If the hook doesn't run or you need to re-sync manually:
493
+ \`\`\`bash
494
+ npx tiny-brain sync-progress docs/prd/*/features/*.md
495
+ npx tiny-brain sync-progress .tiny-brain/fixes/*.md
496
+ \`\`\`
497
+
473
498
  `;
474
499
  }
475
500
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"local-filesystem-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/local-filesystem-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAOnF;;GAEG;AACH,qBAAa,6BAA8B,YAAW,wBAAwB;IAC5E,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,UAAU,CAAU;IACrB,WAAW,EAAE,MAAM,CAAC;gBAEf,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE;IAMvD,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAYV,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAanB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvF,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAqCxE,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAuBjD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajE,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5E,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5E,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAalE,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5D,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiB/B,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDhG,iBAAiB,IAAI;QACnB,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;QACzB,WAAW,CAAC,EAAE,OAAO,oCAAoC,EAAE,iBAAiB,CAAC;QAC7E,iBAAiB,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAQD;;OAEG;YACW,qBAAqB;IAWnC;;OAEG;IACH,WAAW,IAAI,MAAM;CAGtB"}
1
+ {"version":3,"file":"local-filesystem-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/local-filesystem-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAOnF;;GAEG;AACH,qBAAa,6BAA8B,YAAW,wBAAwB;IAC5E,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,UAAU,CAAU;IACrB,WAAW,EAAE,MAAM,CAAC;gBAEf,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE;IAMvD,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAYV,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAanB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvF,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAqCxE,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAuBjD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajE,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5E,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5E,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAalE,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY5D,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiB/B,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDhG,iBAAiB,IAAI;QACnB,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;QACzB,WAAW,CAAC,EAAE,OAAO,oCAAoC,EAAE,iBAAiB,CAAC;QAC7E,iBAAiB,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAQD;;OAEG;YACW,qBAAqB;IAWnC;;OAEG;IACH,WAAW,IAAI,MAAM;CAGtB"}