@codebakers/cli 2.3.0 → 2.5.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.
@@ -502,6 +502,76 @@ class CodeBakersServer {
502
502
  properties: {},
503
503
  },
504
504
  },
505
+ {
506
+ name: 'run_tests',
507
+ description: 'Run the project test suite (npm test or configured test command). Use after completing a feature to verify everything works. Returns test results with pass/fail status.',
508
+ inputSchema: {
509
+ type: 'object',
510
+ properties: {
511
+ filter: {
512
+ type: 'string',
513
+ description: 'Optional filter to run specific tests (passed to test runner)',
514
+ },
515
+ watch: {
516
+ type: 'boolean',
517
+ description: 'Run in watch mode (default: false)',
518
+ },
519
+ },
520
+ },
521
+ },
522
+ {
523
+ name: 'report_pattern_gap',
524
+ description: 'Report when a user request cannot be fully handled by existing patterns. This helps improve CodeBakers by tracking what patterns are missing. The AI should automatically call this when it encounters something outside pattern coverage.',
525
+ inputSchema: {
526
+ type: 'object',
527
+ properties: {
528
+ category: {
529
+ type: 'string',
530
+ description: 'Category of the gap (e.g., "third-party-apis", "mobile", "blockchain", "iot")',
531
+ },
532
+ request: {
533
+ type: 'string',
534
+ description: 'What the user asked for',
535
+ },
536
+ context: {
537
+ type: 'string',
538
+ description: 'Additional context about what was needed',
539
+ },
540
+ handledWith: {
541
+ type: 'string',
542
+ description: 'Which existing patterns were used as fallback',
543
+ },
544
+ wasSuccessful: {
545
+ type: 'boolean',
546
+ description: 'Whether the request was handled successfully despite the gap',
547
+ },
548
+ },
549
+ required: ['category', 'request'],
550
+ },
551
+ },
552
+ {
553
+ name: 'track_analytics',
554
+ description: 'Track CLI usage analytics for improving smart triggers and recommendations. Called automatically by the system for key events.',
555
+ inputSchema: {
556
+ type: 'object',
557
+ properties: {
558
+ eventType: {
559
+ type: 'string',
560
+ enum: ['trigger_fired', 'trigger_accepted', 'trigger_dismissed', 'topic_learned', 'command_used', 'pattern_fetched', 'build_started', 'build_completed', 'feature_added', 'audit_run', 'design_cloned'],
561
+ description: 'Type of event to track',
562
+ },
563
+ eventData: {
564
+ type: 'object',
565
+ description: 'Additional data specific to the event',
566
+ },
567
+ projectHash: {
568
+ type: 'string',
569
+ description: 'Hash of project path for grouping analytics',
570
+ },
571
+ },
572
+ required: ['eventType'],
573
+ },
574
+ },
505
575
  ],
506
576
  }));
507
577
  // Handle tool calls
@@ -543,6 +613,12 @@ class CodeBakersServer {
543
613
  return this.handleUpgrade(args);
544
614
  case 'project_status':
545
615
  return this.handleProjectStatus();
616
+ case 'run_tests':
617
+ return this.handleRunTests(args);
618
+ case 'report_pattern_gap':
619
+ return this.handleReportPatternGap(args);
620
+ case 'track_analytics':
621
+ return this.handleTrackAnalytics(args);
546
622
  default:
547
623
  throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
548
624
  }
@@ -1460,6 +1536,7 @@ Just describe what you want to build! I'll automatically:
1460
1536
  async handleUpgrade(args) {
1461
1537
  const { areas = ['all'], severity = 'all', dryRun = false } = args;
1462
1538
  const context = this.gatherProjectContext();
1539
+ const cwd = process.cwd();
1463
1540
  let response = `# ⬆️ Project Upgrade Analysis\n\n`;
1464
1541
  // Stack detection
1465
1542
  response += `## Your Stack (Preserving As-Is)\n\n`;
@@ -1493,6 +1570,83 @@ Just describe what you want to build! I'll automatically:
1493
1570
  const hasNext = context.dependencies.includes('next');
1494
1571
  response += `| Framework | ${hasNext ? 'Next.js' : 'Unknown'} | ✓ Keeping |\n`;
1495
1572
  response += `\n---\n\n`;
1573
+ // Git Analysis Section
1574
+ response += `## Deep Analysis\n\n`;
1575
+ // Check if git repo
1576
+ const isGitRepo = fs.existsSync(path.join(cwd, '.git'));
1577
+ if (isGitRepo) {
1578
+ response += `### Git Hot Spots (Most Changed Files)\n\n`;
1579
+ try {
1580
+ // Get files with most commits
1581
+ const gitLog = (0, child_process_1.execSync)('git log --pretty=format: --name-only --since="6 months ago" 2>/dev/null | sort | uniq -c | sort -rg | head -10', { cwd, encoding: 'utf-8', timeout: 10000 }).trim();
1582
+ if (gitLog) {
1583
+ const hotSpots = gitLog.split('\n')
1584
+ .map(line => line.trim())
1585
+ .filter(line => line && !line.includes('node_modules'))
1586
+ .slice(0, 5);
1587
+ if (hotSpots.length > 0) {
1588
+ hotSpots.forEach((line, i) => {
1589
+ const match = line.match(/^\s*(\d+)\s+(.+)$/);
1590
+ if (match) {
1591
+ const [, count, file] = match;
1592
+ response += `${i + 1}. \`${file}\` - ${count} changes\n`;
1593
+ }
1594
+ });
1595
+ response += `\n`;
1596
+ }
1597
+ }
1598
+ // Count fix commits
1599
+ const fixCount = (0, child_process_1.execSync)('git log --oneline --since="6 months ago" 2>/dev/null | grep -i "fix" | wc -l', { cwd, encoding: 'utf-8', timeout: 5000 }).trim();
1600
+ if (parseInt(fixCount) > 0) {
1601
+ response += `**Bug Fix Commits:** ${fixCount} (in last 6 months)\n\n`;
1602
+ }
1603
+ }
1604
+ catch {
1605
+ response += `*(Git analysis unavailable)*\n\n`;
1606
+ }
1607
+ }
1608
+ else {
1609
+ response += `*(Not a git repository - skipping git analysis)*\n\n`;
1610
+ }
1611
+ // TODO/FIXME Scan
1612
+ response += `### Developer Notes (TODO/FIXME)\n\n`;
1613
+ try {
1614
+ const todoScan = (0, child_process_1.execSync)('grep -r "TODO\\|FIXME\\|HACK\\|XXX\\|BUG" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" -l 2>/dev/null | head -20', { cwd, encoding: 'utf-8', timeout: 10000 }).trim();
1615
+ if (todoScan) {
1616
+ const todoFiles = todoScan.split('\n').filter(f => !f.includes('node_modules'));
1617
+ const todoCount = todoFiles.length;
1618
+ if (todoCount > 0) {
1619
+ response += `Found developer notes in **${todoCount} files**:\n\n`;
1620
+ // Count by type
1621
+ try {
1622
+ const todoTypes = (0, child_process_1.execSync)('grep -roh "TODO\\|FIXME\\|HACK\\|XXX\\|BUG" --include="*.ts" --include="*.tsx" 2>/dev/null | sort | uniq -c | sort -rg', { cwd, encoding: 'utf-8', timeout: 10000 }).trim();
1623
+ if (todoTypes) {
1624
+ todoTypes.split('\n').forEach(line => {
1625
+ const match = line.trim().match(/^\s*(\d+)\s+(.+)$/);
1626
+ if (match) {
1627
+ const [, count, type] = match;
1628
+ const icon = type === 'FIXME' || type === 'BUG' ? '🔴' :
1629
+ type === 'HACK' ? '🟡' : '📝';
1630
+ response += `- ${icon} ${count} ${type}s\n`;
1631
+ }
1632
+ });
1633
+ response += `\n`;
1634
+ }
1635
+ }
1636
+ catch {
1637
+ // Ignore count errors
1638
+ }
1639
+ response += `*I can implement these TODOs and fix FIXMEs during upgrade.*\n\n`;
1640
+ }
1641
+ }
1642
+ else {
1643
+ response += `✅ No TODO/FIXME comments found - clean codebase!\n\n`;
1644
+ }
1645
+ }
1646
+ catch {
1647
+ response += `*(TODO scan unavailable)*\n\n`;
1648
+ }
1649
+ response += `---\n\n`;
1496
1650
  // Scan for upgrade opportunities
1497
1651
  response += `## Upgrade Opportunities\n\n`;
1498
1652
  const upgrades = [];
@@ -1550,18 +1704,26 @@ Just describe what you want to build! I'll automatically:
1550
1704
  if (upgrades.length === 0) {
1551
1705
  response += `✅ No major upgrade opportunities detected!\n\n`;
1552
1706
  }
1707
+ // Review mode options
1708
+ response += `---\n\n`;
1709
+ response += `## Review Modes\n\n`;
1710
+ response += `Pick a focus for the upgrade:\n\n`;
1711
+ response += `- **Security Audit** - Auth, secrets, injections, OWASP top 10\n`;
1712
+ response += `- **Performance Review** - Bundle size, queries, caching\n`;
1713
+ response += `- **Code Quality** - Patterns, DRY, complexity\n`;
1714
+ response += `- **Pre-Launch** - Everything for production\n`;
1715
+ response += `- **Quick Scan** - Top 5 issues only\n`;
1716
+ response += `- **Comprehensive** - All of the above\n\n`;
1553
1717
  // Recommendations
1554
1718
  response += `---\n\n`;
1555
1719
  response += `## Recommended Actions\n\n`;
1556
1720
  if (dryRun) {
1557
1721
  response += `**(Dry Run Mode - No changes will be made)**\n\n`;
1558
1722
  }
1559
- response += `1. Run \`run_audit\` for detailed code quality check\n`;
1560
- response += `2. Use \`heal\` to auto-fix common issues\n`;
1561
- response += `3. Request specific upgrades like:\n`;
1562
- response += ` - "Add Zod validation to my API routes"\n`;
1563
- response += ` - "Add error boundaries to components"\n`;
1564
- response += ` - "Set up Playwright testing"\n\n`;
1723
+ response += `1. Tell me your main concerns (security, performance, etc.)\n`;
1724
+ response += `2. I'll prioritize fixes based on your needs\n`;
1725
+ response += `3. Hot spot files get fixed first (where bugs live)\n`;
1726
+ response += `4. I'll implement your TODOs and fix FIXMEs along the way\n\n`;
1565
1727
  response += `---\n\n`;
1566
1728
  response += `**Key Principle:** Your stack stays the same. Only code quality patterns are upgraded.\n`;
1567
1729
  return {
@@ -1676,6 +1838,160 @@ Just describe what you want to build! I'll automatically:
1676
1838
  }],
1677
1839
  };
1678
1840
  }
1841
+ handleRunTests(args) {
1842
+ const { filter, watch = false } = args;
1843
+ const cwd = process.cwd();
1844
+ // Detect test runner from package.json
1845
+ let testCommand = 'npm test';
1846
+ try {
1847
+ const pkgPath = path.join(cwd, 'package.json');
1848
+ if (fs.existsSync(pkgPath)) {
1849
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
1850
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1851
+ if (deps['@playwright/test']) {
1852
+ testCommand = 'npx playwright test';
1853
+ }
1854
+ else if (deps['vitest']) {
1855
+ testCommand = 'npx vitest run';
1856
+ }
1857
+ else if (deps['jest']) {
1858
+ testCommand = 'npx jest';
1859
+ }
1860
+ }
1861
+ }
1862
+ catch {
1863
+ // Use default
1864
+ }
1865
+ // Add filter if provided
1866
+ if (filter) {
1867
+ testCommand += ` ${filter}`;
1868
+ }
1869
+ // Add watch mode
1870
+ if (watch) {
1871
+ testCommand = testCommand.replace(' run', ''); // Remove 'run' for vitest watch
1872
+ if (testCommand.includes('vitest')) {
1873
+ testCommand = testCommand.replace('vitest run', 'vitest');
1874
+ }
1875
+ }
1876
+ let response = `# 🧪 Running Tests\n\n`;
1877
+ response += `**Command:** \`${testCommand}\`\n\n`;
1878
+ try {
1879
+ const output = (0, child_process_1.execSync)(testCommand, {
1880
+ cwd,
1881
+ encoding: 'utf-8',
1882
+ timeout: 120000, // 2 minute timeout
1883
+ stdio: ['pipe', 'pipe', 'pipe'],
1884
+ });
1885
+ response += `## ✅ Tests Passed\n\n`;
1886
+ response += '```\n' + output.slice(-2000) + '\n```\n';
1887
+ }
1888
+ catch (error) {
1889
+ const execError = error;
1890
+ response += `## ❌ Tests Failed\n\n`;
1891
+ if (execError.stdout) {
1892
+ response += '### Output\n```\n' + execError.stdout.slice(-2000) + '\n```\n\n';
1893
+ }
1894
+ if (execError.stderr) {
1895
+ response += '### Errors\n```\n' + execError.stderr.slice(-1000) + '\n```\n\n';
1896
+ }
1897
+ response += `**Exit Code:** ${execError.status || 1}\n\n`;
1898
+ response += `---\n\n*Fix the failing tests and run again.*`;
1899
+ }
1900
+ return {
1901
+ content: [{
1902
+ type: 'text',
1903
+ text: response,
1904
+ }],
1905
+ };
1906
+ }
1907
+ async handleReportPatternGap(args) {
1908
+ const { category, request, context, handledWith, wasSuccessful = true } = args;
1909
+ try {
1910
+ const response = await fetch(`${this.apiUrl}/api/pattern-gaps`, {
1911
+ method: 'POST',
1912
+ headers: {
1913
+ 'Content-Type': 'application/json',
1914
+ Authorization: `Bearer ${this.apiKey}`,
1915
+ },
1916
+ body: JSON.stringify({
1917
+ category,
1918
+ request,
1919
+ context,
1920
+ handledWith,
1921
+ wasSuccessful,
1922
+ }),
1923
+ });
1924
+ if (!response.ok) {
1925
+ const error = await response.json().catch(() => ({}));
1926
+ throw new Error(error.error || 'Failed to report pattern gap');
1927
+ }
1928
+ const result = await response.json();
1929
+ if (result.deduplicated) {
1930
+ return {
1931
+ content: [{
1932
+ type: 'text',
1933
+ text: `📊 Pattern gap already reported recently (category: ${category}). No duplicate created.`,
1934
+ }],
1935
+ };
1936
+ }
1937
+ return {
1938
+ content: [{
1939
+ type: 'text',
1940
+ text: `✅ Pattern gap reported successfully.\n\n**Category:** ${category}\n**Request:** ${request}\n\nThis helps improve CodeBakers for everyone!`,
1941
+ }],
1942
+ };
1943
+ }
1944
+ catch (error) {
1945
+ const message = error instanceof Error ? error.message : 'Unknown error';
1946
+ return {
1947
+ content: [{
1948
+ type: 'text',
1949
+ text: `⚠️ Could not report pattern gap: ${message}\n\n(This doesn't affect your current work)`,
1950
+ }],
1951
+ };
1952
+ }
1953
+ }
1954
+ async handleTrackAnalytics(args) {
1955
+ const { eventType, eventData, projectHash } = args;
1956
+ try {
1957
+ const response = await fetch(`${this.apiUrl}/api/cli/analytics`, {
1958
+ method: 'POST',
1959
+ headers: {
1960
+ 'Content-Type': 'application/json',
1961
+ Authorization: `Bearer ${this.apiKey}`,
1962
+ },
1963
+ body: JSON.stringify({
1964
+ eventType,
1965
+ eventData,
1966
+ projectHash,
1967
+ }),
1968
+ });
1969
+ if (!response.ok) {
1970
+ // Silently fail - analytics shouldn't interrupt user workflow
1971
+ return {
1972
+ content: [{
1973
+ type: 'text',
1974
+ text: `📊 Analytics tracked: ${eventType}`,
1975
+ }],
1976
+ };
1977
+ }
1978
+ return {
1979
+ content: [{
1980
+ type: 'text',
1981
+ text: `📊 Analytics tracked: ${eventType}`,
1982
+ }],
1983
+ };
1984
+ }
1985
+ catch {
1986
+ // Silently fail
1987
+ return {
1988
+ content: [{
1989
+ type: 'text',
1990
+ text: `📊 Analytics tracked: ${eventType}`,
1991
+ }],
1992
+ };
1993
+ }
1994
+ }
1679
1995
  async run() {
1680
1996
  const transport = new stdio_js_1.StdioServerTransport();
1681
1997
  await this.server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/mcp/server.ts CHANGED
@@ -532,6 +532,79 @@ class CodeBakersServer {
532
532
  properties: {},
533
533
  },
534
534
  },
535
+ {
536
+ name: 'run_tests',
537
+ description:
538
+ 'Run the project test suite (npm test or configured test command). Use after completing a feature to verify everything works. Returns test results with pass/fail status.',
539
+ inputSchema: {
540
+ type: 'object' as const,
541
+ properties: {
542
+ filter: {
543
+ type: 'string',
544
+ description: 'Optional filter to run specific tests (passed to test runner)',
545
+ },
546
+ watch: {
547
+ type: 'boolean',
548
+ description: 'Run in watch mode (default: false)',
549
+ },
550
+ },
551
+ },
552
+ },
553
+ {
554
+ name: 'report_pattern_gap',
555
+ description:
556
+ 'Report when a user request cannot be fully handled by existing patterns. This helps improve CodeBakers by tracking what patterns are missing. The AI should automatically call this when it encounters something outside pattern coverage.',
557
+ inputSchema: {
558
+ type: 'object' as const,
559
+ properties: {
560
+ category: {
561
+ type: 'string',
562
+ description: 'Category of the gap (e.g., "third-party-apis", "mobile", "blockchain", "iot")',
563
+ },
564
+ request: {
565
+ type: 'string',
566
+ description: 'What the user asked for',
567
+ },
568
+ context: {
569
+ type: 'string',
570
+ description: 'Additional context about what was needed',
571
+ },
572
+ handledWith: {
573
+ type: 'string',
574
+ description: 'Which existing patterns were used as fallback',
575
+ },
576
+ wasSuccessful: {
577
+ type: 'boolean',
578
+ description: 'Whether the request was handled successfully despite the gap',
579
+ },
580
+ },
581
+ required: ['category', 'request'],
582
+ },
583
+ },
584
+ {
585
+ name: 'track_analytics',
586
+ description:
587
+ 'Track CLI usage analytics for improving smart triggers and recommendations. Called automatically by the system for key events.',
588
+ inputSchema: {
589
+ type: 'object' as const,
590
+ properties: {
591
+ eventType: {
592
+ type: 'string',
593
+ enum: ['trigger_fired', 'trigger_accepted', 'trigger_dismissed', 'topic_learned', 'command_used', 'pattern_fetched', 'build_started', 'build_completed', 'feature_added', 'audit_run', 'design_cloned'],
594
+ description: 'Type of event to track',
595
+ },
596
+ eventData: {
597
+ type: 'object',
598
+ description: 'Additional data specific to the event',
599
+ },
600
+ projectHash: {
601
+ type: 'string',
602
+ description: 'Hash of project path for grouping analytics',
603
+ },
604
+ },
605
+ required: ['eventType'],
606
+ },
607
+ },
535
608
  ],
536
609
  }));
537
610
 
@@ -595,6 +668,15 @@ class CodeBakersServer {
595
668
  case 'project_status':
596
669
  return this.handleProjectStatus();
597
670
 
671
+ case 'run_tests':
672
+ return this.handleRunTests(args as { filter?: string; watch?: boolean });
673
+
674
+ case 'report_pattern_gap':
675
+ return this.handleReportPatternGap(args as { category: string; request: string; context?: string; handledWith?: string; wasSuccessful?: boolean });
676
+
677
+ case 'track_analytics':
678
+ return this.handleTrackAnalytics(args as { eventType: string; eventData?: Record<string, unknown>; projectHash?: string });
679
+
598
680
  default:
599
681
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
600
682
  }
@@ -1647,6 +1729,7 @@ Just describe what you want to build! I'll automatically:
1647
1729
  private async handleUpgrade(args: { areas?: string[]; severity?: string; dryRun?: boolean }) {
1648
1730
  const { areas = ['all'], severity = 'all', dryRun = false } = args;
1649
1731
  const context = this.gatherProjectContext();
1732
+ const cwd = process.cwd();
1650
1733
 
1651
1734
  let response = `# ⬆️ Project Upgrade Analysis\n\n`;
1652
1735
 
@@ -1680,6 +1763,104 @@ Just describe what you want to build! I'll automatically:
1680
1763
 
1681
1764
  response += `\n---\n\n`;
1682
1765
 
1766
+ // Git Analysis Section
1767
+ response += `## Deep Analysis\n\n`;
1768
+
1769
+ // Check if git repo
1770
+ const isGitRepo = fs.existsSync(path.join(cwd, '.git'));
1771
+
1772
+ if (isGitRepo) {
1773
+ response += `### Git Hot Spots (Most Changed Files)\n\n`;
1774
+ try {
1775
+ // Get files with most commits
1776
+ const gitLog = execSync(
1777
+ 'git log --pretty=format: --name-only --since="6 months ago" 2>/dev/null | sort | uniq -c | sort -rg | head -10',
1778
+ { cwd, encoding: 'utf-8', timeout: 10000 }
1779
+ ).trim();
1780
+
1781
+ if (gitLog) {
1782
+ const hotSpots = gitLog.split('\n')
1783
+ .map(line => line.trim())
1784
+ .filter(line => line && !line.includes('node_modules'))
1785
+ .slice(0, 5);
1786
+
1787
+ if (hotSpots.length > 0) {
1788
+ hotSpots.forEach((line, i) => {
1789
+ const match = line.match(/^\s*(\d+)\s+(.+)$/);
1790
+ if (match) {
1791
+ const [, count, file] = match;
1792
+ response += `${i + 1}. \`${file}\` - ${count} changes\n`;
1793
+ }
1794
+ });
1795
+ response += `\n`;
1796
+ }
1797
+ }
1798
+
1799
+ // Count fix commits
1800
+ const fixCount = execSync(
1801
+ 'git log --oneline --since="6 months ago" 2>/dev/null | grep -i "fix" | wc -l',
1802
+ { cwd, encoding: 'utf-8', timeout: 5000 }
1803
+ ).trim();
1804
+
1805
+ if (parseInt(fixCount) > 0) {
1806
+ response += `**Bug Fix Commits:** ${fixCount} (in last 6 months)\n\n`;
1807
+ }
1808
+ } catch {
1809
+ response += `*(Git analysis unavailable)*\n\n`;
1810
+ }
1811
+ } else {
1812
+ response += `*(Not a git repository - skipping git analysis)*\n\n`;
1813
+ }
1814
+
1815
+ // TODO/FIXME Scan
1816
+ response += `### Developer Notes (TODO/FIXME)\n\n`;
1817
+ try {
1818
+ const todoScan = execSync(
1819
+ 'grep -r "TODO\\|FIXME\\|HACK\\|XXX\\|BUG" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" -l 2>/dev/null | head -20',
1820
+ { cwd, encoding: 'utf-8', timeout: 10000 }
1821
+ ).trim();
1822
+
1823
+ if (todoScan) {
1824
+ const todoFiles = todoScan.split('\n').filter(f => !f.includes('node_modules'));
1825
+ const todoCount = todoFiles.length;
1826
+
1827
+ if (todoCount > 0) {
1828
+ response += `Found developer notes in **${todoCount} files**:\n\n`;
1829
+
1830
+ // Count by type
1831
+ try {
1832
+ const todoTypes = execSync(
1833
+ 'grep -roh "TODO\\|FIXME\\|HACK\\|XXX\\|BUG" --include="*.ts" --include="*.tsx" 2>/dev/null | sort | uniq -c | sort -rg',
1834
+ { cwd, encoding: 'utf-8', timeout: 10000 }
1835
+ ).trim();
1836
+
1837
+ if (todoTypes) {
1838
+ todoTypes.split('\n').forEach(line => {
1839
+ const match = line.trim().match(/^\s*(\d+)\s+(.+)$/);
1840
+ if (match) {
1841
+ const [, count, type] = match;
1842
+ const icon = type === 'FIXME' || type === 'BUG' ? '🔴' :
1843
+ type === 'HACK' ? '🟡' : '📝';
1844
+ response += `- ${icon} ${count} ${type}s\n`;
1845
+ }
1846
+ });
1847
+ response += `\n`;
1848
+ }
1849
+ } catch {
1850
+ // Ignore count errors
1851
+ }
1852
+
1853
+ response += `*I can implement these TODOs and fix FIXMEs during upgrade.*\n\n`;
1854
+ }
1855
+ } else {
1856
+ response += `✅ No TODO/FIXME comments found - clean codebase!\n\n`;
1857
+ }
1858
+ } catch {
1859
+ response += `*(TODO scan unavailable)*\n\n`;
1860
+ }
1861
+
1862
+ response += `---\n\n`;
1863
+
1683
1864
  // Scan for upgrade opportunities
1684
1865
  response += `## Upgrade Opportunities\n\n`;
1685
1866
 
@@ -1745,6 +1926,17 @@ Just describe what you want to build! I'll automatically:
1745
1926
  response += `✅ No major upgrade opportunities detected!\n\n`;
1746
1927
  }
1747
1928
 
1929
+ // Review mode options
1930
+ response += `---\n\n`;
1931
+ response += `## Review Modes\n\n`;
1932
+ response += `Pick a focus for the upgrade:\n\n`;
1933
+ response += `- **Security Audit** - Auth, secrets, injections, OWASP top 10\n`;
1934
+ response += `- **Performance Review** - Bundle size, queries, caching\n`;
1935
+ response += `- **Code Quality** - Patterns, DRY, complexity\n`;
1936
+ response += `- **Pre-Launch** - Everything for production\n`;
1937
+ response += `- **Quick Scan** - Top 5 issues only\n`;
1938
+ response += `- **Comprehensive** - All of the above\n\n`;
1939
+
1748
1940
  // Recommendations
1749
1941
  response += `---\n\n`;
1750
1942
  response += `## Recommended Actions\n\n`;
@@ -1753,12 +1945,10 @@ Just describe what you want to build! I'll automatically:
1753
1945
  response += `**(Dry Run Mode - No changes will be made)**\n\n`;
1754
1946
  }
1755
1947
 
1756
- response += `1. Run \`run_audit\` for detailed code quality check\n`;
1757
- response += `2. Use \`heal\` to auto-fix common issues\n`;
1758
- response += `3. Request specific upgrades like:\n`;
1759
- response += ` - "Add Zod validation to my API routes"\n`;
1760
- response += ` - "Add error boundaries to components"\n`;
1761
- response += ` - "Set up Playwright testing"\n\n`;
1948
+ response += `1. Tell me your main concerns (security, performance, etc.)\n`;
1949
+ response += `2. I'll prioritize fixes based on your needs\n`;
1950
+ response += `3. Hot spot files get fixed first (where bugs live)\n`;
1951
+ response += `4. I'll implement your TODOs and fix FIXMEs along the way\n\n`;
1762
1952
 
1763
1953
  response += `---\n\n`;
1764
1954
  response += `**Key Principle:** Your stack stays the same. Only code quality patterns are upgraded.\n`;
@@ -1888,6 +2078,175 @@ Just describe what you want to build! I'll automatically:
1888
2078
  };
1889
2079
  }
1890
2080
 
2081
+ private handleRunTests(args: { filter?: string; watch?: boolean }) {
2082
+ const { filter, watch = false } = args;
2083
+ const cwd = process.cwd();
2084
+
2085
+ // Detect test runner from package.json
2086
+ let testCommand = 'npm test';
2087
+ try {
2088
+ const pkgPath = path.join(cwd, 'package.json');
2089
+ if (fs.existsSync(pkgPath)) {
2090
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
2091
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
2092
+
2093
+ if (deps['@playwright/test']) {
2094
+ testCommand = 'npx playwright test';
2095
+ } else if (deps['vitest']) {
2096
+ testCommand = 'npx vitest run';
2097
+ } else if (deps['jest']) {
2098
+ testCommand = 'npx jest';
2099
+ }
2100
+ }
2101
+ } catch {
2102
+ // Use default
2103
+ }
2104
+
2105
+ // Add filter if provided
2106
+ if (filter) {
2107
+ testCommand += ` ${filter}`;
2108
+ }
2109
+
2110
+ // Add watch mode
2111
+ if (watch) {
2112
+ testCommand = testCommand.replace(' run', ''); // Remove 'run' for vitest watch
2113
+ if (testCommand.includes('vitest')) {
2114
+ testCommand = testCommand.replace('vitest run', 'vitest');
2115
+ }
2116
+ }
2117
+
2118
+ let response = `# 🧪 Running Tests\n\n`;
2119
+ response += `**Command:** \`${testCommand}\`\n\n`;
2120
+
2121
+ try {
2122
+ const output = execSync(testCommand, {
2123
+ cwd,
2124
+ encoding: 'utf-8',
2125
+ timeout: 120000, // 2 minute timeout
2126
+ stdio: ['pipe', 'pipe', 'pipe'],
2127
+ });
2128
+
2129
+ response += `## ✅ Tests Passed\n\n`;
2130
+ response += '```\n' + output.slice(-2000) + '\n```\n';
2131
+ } catch (error) {
2132
+ const execError = error as { stdout?: string; stderr?: string; status?: number };
2133
+ response += `## ❌ Tests Failed\n\n`;
2134
+
2135
+ if (execError.stdout) {
2136
+ response += '### Output\n```\n' + execError.stdout.slice(-2000) + '\n```\n\n';
2137
+ }
2138
+ if (execError.stderr) {
2139
+ response += '### Errors\n```\n' + execError.stderr.slice(-1000) + '\n```\n\n';
2140
+ }
2141
+
2142
+ response += `**Exit Code:** ${execError.status || 1}\n\n`;
2143
+ response += `---\n\n*Fix the failing tests and run again.*`;
2144
+ }
2145
+
2146
+ return {
2147
+ content: [{
2148
+ type: 'text' as const,
2149
+ text: response,
2150
+ }],
2151
+ };
2152
+ }
2153
+
2154
+ private async handleReportPatternGap(args: { category: string; request: string; context?: string; handledWith?: string; wasSuccessful?: boolean }) {
2155
+ const { category, request, context, handledWith, wasSuccessful = true } = args;
2156
+
2157
+ try {
2158
+ const response = await fetch(`${this.apiUrl}/api/pattern-gaps`, {
2159
+ method: 'POST',
2160
+ headers: {
2161
+ 'Content-Type': 'application/json',
2162
+ Authorization: `Bearer ${this.apiKey}`,
2163
+ },
2164
+ body: JSON.stringify({
2165
+ category,
2166
+ request,
2167
+ context,
2168
+ handledWith,
2169
+ wasSuccessful,
2170
+ }),
2171
+ });
2172
+
2173
+ if (!response.ok) {
2174
+ const error = await response.json().catch(() => ({}));
2175
+ throw new Error(error.error || 'Failed to report pattern gap');
2176
+ }
2177
+
2178
+ const result = await response.json();
2179
+
2180
+ if (result.deduplicated) {
2181
+ return {
2182
+ content: [{
2183
+ type: 'text' as const,
2184
+ text: `📊 Pattern gap already reported recently (category: ${category}). No duplicate created.`,
2185
+ }],
2186
+ };
2187
+ }
2188
+
2189
+ return {
2190
+ content: [{
2191
+ type: 'text' as const,
2192
+ text: `✅ Pattern gap reported successfully.\n\n**Category:** ${category}\n**Request:** ${request}\n\nThis helps improve CodeBakers for everyone!`,
2193
+ }],
2194
+ };
2195
+ } catch (error) {
2196
+ const message = error instanceof Error ? error.message : 'Unknown error';
2197
+ return {
2198
+ content: [{
2199
+ type: 'text' as const,
2200
+ text: `⚠️ Could not report pattern gap: ${message}\n\n(This doesn't affect your current work)`,
2201
+ }],
2202
+ };
2203
+ }
2204
+ }
2205
+
2206
+ private async handleTrackAnalytics(args: { eventType: string; eventData?: Record<string, unknown>; projectHash?: string }) {
2207
+ const { eventType, eventData, projectHash } = args;
2208
+
2209
+ try {
2210
+ const response = await fetch(`${this.apiUrl}/api/cli/analytics`, {
2211
+ method: 'POST',
2212
+ headers: {
2213
+ 'Content-Type': 'application/json',
2214
+ Authorization: `Bearer ${this.apiKey}`,
2215
+ },
2216
+ body: JSON.stringify({
2217
+ eventType,
2218
+ eventData,
2219
+ projectHash,
2220
+ }),
2221
+ });
2222
+
2223
+ if (!response.ok) {
2224
+ // Silently fail - analytics shouldn't interrupt user workflow
2225
+ return {
2226
+ content: [{
2227
+ type: 'text' as const,
2228
+ text: `📊 Analytics tracked: ${eventType}`,
2229
+ }],
2230
+ };
2231
+ }
2232
+
2233
+ return {
2234
+ content: [{
2235
+ type: 'text' as const,
2236
+ text: `📊 Analytics tracked: ${eventType}`,
2237
+ }],
2238
+ };
2239
+ } catch {
2240
+ // Silently fail
2241
+ return {
2242
+ content: [{
2243
+ type: 'text' as const,
2244
+ text: `📊 Analytics tracked: ${eventType}`,
2245
+ }],
2246
+ };
2247
+ }
2248
+ }
2249
+
1891
2250
  async run(): Promise<void> {
1892
2251
  const transport = new StdioServerTransport();
1893
2252
  await this.server.connect(transport);