@codebakers/cli 3.8.8 → 3.9.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.
@@ -8,6 +8,10 @@ interface GoOptions {
8
8
  /**
9
9
  * Zero-friction entry point - start using CodeBakers instantly
10
10
  * Single command for both trial and paid users
11
+ *
12
+ * SMART BEHAVIOR:
13
+ * - If CodeBakers already set up → Show context and resume from where you left off
14
+ * - If not set up → Run first-time setup (trial or login)
11
15
  */
12
16
  export declare function go(options?: GoOptions): Promise<void>;
13
17
  export {};
@@ -709,6 +709,190 @@ function updateGitignore(cwd) {
709
709
  }
710
710
  }
711
711
  }
712
+ function analyzeProjectState(cwd) {
713
+ const state = {
714
+ isSetUp: false,
715
+ hasPrd: false,
716
+ inProgressTasks: [],
717
+ completedTasks: [],
718
+ blockers: [],
719
+ suggestion: '',
720
+ };
721
+ // Check if CodeBakers is set up
722
+ const codebakersJsonPath = (0, path_1.join)(cwd, '.codebakers.json');
723
+ if (!(0, fs_1.existsSync)(codebakersJsonPath)) {
724
+ state.suggestion = 'Project not set up. Running first-time setup...';
725
+ return state;
726
+ }
727
+ state.isSetUp = true;
728
+ // Read .codebakers.json
729
+ try {
730
+ const cbState = JSON.parse((0, fs_1.readFileSync)(codebakersJsonPath, 'utf-8'));
731
+ state.projectName = cbState.projectName;
732
+ state.projectType = cbState.projectType;
733
+ }
734
+ catch {
735
+ // Ignore parse errors
736
+ }
737
+ // Check for PRD.md
738
+ const prdPath = (0, path_1.join)(cwd, 'PRD.md');
739
+ if ((0, fs_1.existsSync)(prdPath)) {
740
+ state.hasPrd = true;
741
+ try {
742
+ const prdContent = (0, fs_1.readFileSync)(prdPath, 'utf-8');
743
+ // Extract one-liner if present
744
+ const oneLineMatch = prdContent.match(/\*\*One-liner:\*\*\s*(.+)/);
745
+ if (oneLineMatch) {
746
+ state.prdSummary = oneLineMatch[1].trim();
747
+ }
748
+ else {
749
+ // Get first non-comment, non-header line
750
+ const lines = prdContent.split('\n').filter(l => l.trim() && !l.startsWith('#') && !l.startsWith('<!--'));
751
+ if (lines[0]) {
752
+ state.prdSummary = lines[0].substring(0, 100);
753
+ }
754
+ }
755
+ }
756
+ catch {
757
+ // Ignore read errors
758
+ }
759
+ }
760
+ // Read PROJECT-STATE.md for tasks
761
+ const projectStatePath = (0, path_1.join)(cwd, 'PROJECT-STATE.md');
762
+ if ((0, fs_1.existsSync)(projectStatePath)) {
763
+ try {
764
+ const content = (0, fs_1.readFileSync)(projectStatePath, 'utf-8');
765
+ // Extract In Progress section
766
+ const inProgressMatch = content.match(/## In Progress\n([\s\S]*?)(?=\n##|$)/);
767
+ if (inProgressMatch) {
768
+ const lines = inProgressMatch[1].split('\n')
769
+ .filter(l => l.trim().startsWith('-'))
770
+ .map(l => l.replace(/^-\s*/, '').trim())
771
+ .filter(l => l && !l.startsWith('<!--'));
772
+ state.inProgressTasks = lines;
773
+ }
774
+ // Extract Completed section (last 5)
775
+ const completedMatch = content.match(/## Completed\n([\s\S]*?)(?=\n##|$)/);
776
+ if (completedMatch) {
777
+ const lines = completedMatch[1].split('\n')
778
+ .filter(l => l.trim().startsWith('-'))
779
+ .map(l => l.replace(/^-\s*/, '').trim())
780
+ .filter(l => l && !l.startsWith('<!--'));
781
+ state.completedTasks = lines.slice(-5);
782
+ }
783
+ // Extract Blockers section
784
+ const blockersMatch = content.match(/## Blockers\n([\s\S]*?)(?=\n##|$)/);
785
+ if (blockersMatch) {
786
+ const lines = blockersMatch[1].split('\n')
787
+ .filter(l => l.trim().startsWith('-'))
788
+ .map(l => l.replace(/^-\s*/, '').trim())
789
+ .filter(l => l && !l.startsWith('<!--'));
790
+ state.blockers = lines;
791
+ }
792
+ }
793
+ catch {
794
+ // Ignore read errors
795
+ }
796
+ }
797
+ // Read DEVLOG for last session
798
+ const devlogPath = (0, path_1.join)(cwd, '.codebakers', 'DEVLOG.md');
799
+ if ((0, fs_1.existsSync)(devlogPath)) {
800
+ try {
801
+ const content = (0, fs_1.readFileSync)(devlogPath, 'utf-8');
802
+ // Get first session entry
803
+ const sessionMatch = content.match(/## .+?\n\*\*Session:\*\*\s*(.+)/);
804
+ if (sessionMatch) {
805
+ state.lastSession = sessionMatch[1].trim();
806
+ }
807
+ // Get "What was done" from most recent entry
808
+ const whatDoneMatch = content.match(/### What was done:\n([\s\S]*?)(?=\n###|---|\n\n)/);
809
+ if (whatDoneMatch && !state.lastSession) {
810
+ const lines = whatDoneMatch[1].split('\n')
811
+ .filter(l => l.trim().startsWith('-'))
812
+ .map(l => l.replace(/^-\s*/, '').trim());
813
+ if (lines[0]) {
814
+ state.lastSession = lines[0];
815
+ }
816
+ }
817
+ }
818
+ catch {
819
+ // Ignore read errors
820
+ }
821
+ }
822
+ // Determine suggestion based on state
823
+ if (state.blockers.length > 0) {
824
+ state.suggestion = `BLOCKED: ${state.blockers[0]}. Address this blocker first.`;
825
+ }
826
+ else if (state.inProgressTasks.length > 0) {
827
+ state.suggestion = `CONTINUE: ${state.inProgressTasks[0]}`;
828
+ }
829
+ else if (state.hasPrd && state.completedTasks.length === 0) {
830
+ state.suggestion = `START BUILDING: PRD exists. Begin implementing features from PRD.md`;
831
+ }
832
+ else if (!state.hasPrd) {
833
+ state.suggestion = `DEFINE PROJECT: No PRD found. Describe what you want to build.`;
834
+ }
835
+ else {
836
+ state.suggestion = `READY: Project set up. Ask for the next feature to build.`;
837
+ }
838
+ return state;
839
+ }
840
+ function showResumeContext(state) {
841
+ console.log(chalk_1.default.blue(`
842
+ ╔═══════════════════════════════════════════════════════════╗
843
+ ║ ║
844
+ ║ ${chalk_1.default.bold.white('CodeBakers - Resuming Session')} ║
845
+ ║ ║
846
+ ╚═══════════════════════════════════════════════════════════╝
847
+ `));
848
+ console.log(chalk_1.default.white(` 📁 Project: ${chalk_1.default.cyan(state.projectName || 'Unknown')}`));
849
+ if (state.prdSummary) {
850
+ console.log(chalk_1.default.gray(` 📝 ${state.prdSummary}`));
851
+ }
852
+ console.log('');
853
+ // Show blockers first (critical)
854
+ if (state.blockers.length > 0) {
855
+ console.log(chalk_1.default.red(' ⚠️ BLOCKERS:'));
856
+ for (const blocker of state.blockers) {
857
+ console.log(chalk_1.default.red(` • ${blocker}`));
858
+ }
859
+ console.log('');
860
+ }
861
+ // Show in-progress tasks
862
+ if (state.inProgressTasks.length > 0) {
863
+ console.log(chalk_1.default.yellow(' 🔄 IN PROGRESS:'));
864
+ for (const task of state.inProgressTasks) {
865
+ console.log(chalk_1.default.yellow(` • ${task}`));
866
+ }
867
+ console.log('');
868
+ }
869
+ // Show recent completed (context)
870
+ if (state.completedTasks.length > 0) {
871
+ console.log(chalk_1.default.green(' ✓ RECENTLY COMPLETED:'));
872
+ for (const task of state.completedTasks.slice(-3)) {
873
+ console.log(chalk_1.default.gray(` • ${task}`));
874
+ }
875
+ console.log('');
876
+ }
877
+ // Show last session timestamp if available
878
+ if (state.lastSession) {
879
+ console.log(chalk_1.default.gray(` 📅 Last session: ${state.lastSession}`));
880
+ console.log('');
881
+ }
882
+ // Show the suggestion prominently
883
+ console.log(chalk_1.default.cyan(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
884
+ console.log(chalk_1.default.white.bold(`\n → ${state.suggestion}\n`));
885
+ console.log(chalk_1.default.cyan(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
886
+ // Output machine-readable context for AI
887
+ console.log(chalk_1.default.gray(' [AI Context]'));
888
+ console.log(chalk_1.default.gray(` Project: ${state.projectName || 'Unknown'}`));
889
+ console.log(chalk_1.default.gray(` Status: ${state.inProgressTasks.length > 0 ? 'IN_PROGRESS' : state.blockers.length > 0 ? 'BLOCKED' : 'READY'}`));
890
+ console.log(chalk_1.default.gray(` Next Action: ${state.suggestion}`));
891
+ if (state.hasPrd) {
892
+ console.log(chalk_1.default.gray(` PRD: Available at PRD.md`));
893
+ }
894
+ console.log('');
895
+ }
712
896
  /**
713
897
  * Get CLI version from package.json
714
898
  */
@@ -754,11 +938,42 @@ function log(message, options) {
754
938
  /**
755
939
  * Zero-friction entry point - start using CodeBakers instantly
756
940
  * Single command for both trial and paid users
941
+ *
942
+ * SMART BEHAVIOR:
943
+ * - If CodeBakers already set up → Show context and resume from where you left off
944
+ * - If not set up → Run first-time setup (trial or login)
757
945
  */
758
946
  async function go(options = {}) {
759
947
  log('Starting go command...', options);
760
948
  log(`API URL: ${(0, config_js_1.getApiUrl)()}`, options);
761
949
  log(`Working directory: ${process.cwd()}`, options);
950
+ const cwd = process.cwd();
951
+ // =========================================================================
952
+ // SMART CONTEXT CHECK - If already set up, show resume context
953
+ // =========================================================================
954
+ const projectState = analyzeProjectState(cwd);
955
+ if (projectState.isSetUp) {
956
+ // Project already has CodeBakers - show context and resume
957
+ showResumeContext(projectState);
958
+ // Verify auth is still valid
959
+ const existingApiKey = (0, config_js_1.getApiKey)();
960
+ const existingTrial = (0, config_js_1.getTrialState)();
961
+ if (existingApiKey) {
962
+ console.log(chalk_1.default.green(' ✓ Authenticated (API key)\n'));
963
+ }
964
+ else if (existingTrial && !(0, config_js_1.isTrialExpired)()) {
965
+ const daysRemaining = (0, config_js_1.getTrialDaysRemaining)();
966
+ console.log(chalk_1.default.green(` ✓ Trial active (${daysRemaining} days remaining)\n`));
967
+ }
968
+ else if (existingTrial && (0, config_js_1.isTrialExpired)()) {
969
+ console.log(chalk_1.default.yellow(' ⚠️ Trial expired. Run `codebakers extend` or login.\n'));
970
+ }
971
+ // Don't run setup again - just show context
972
+ return;
973
+ }
974
+ // =========================================================================
975
+ // FIRST-TIME SETUP - Project not yet configured
976
+ // =========================================================================
762
977
  console.log(chalk_1.default.blue(`
763
978
  ╔═══════════════════════════════════════════════════════════╗
764
979
  ║ ║
@@ -1440,6 +1440,19 @@ class CodeBakersServer {
1440
1440
  properties: {},
1441
1441
  },
1442
1442
  },
1443
+ {
1444
+ name: 'resume_session',
1445
+ description: 'IMPORTANT: Call this AUTOMATICALLY at the start of any session, especially after conversation compaction/summarization. Returns full project context including: project name, PRD summary, in-progress tasks, completed tasks, blockers, and a suggested next action. This prevents losing context after Claude Code or Cursor compacts the conversation. Use when: (1) starting a new session, (2) conversation was just summarized, (3) you are unsure what you were working on, (4) user says "where was I?" or "what should I do next?".',
1446
+ inputSchema: {
1447
+ type: 'object',
1448
+ properties: {
1449
+ reason: {
1450
+ type: 'string',
1451
+ description: 'Why you are calling this (e.g., "session start", "after compaction", "context lost")',
1452
+ },
1453
+ },
1454
+ },
1455
+ },
1443
1456
  // Engineering workflow tools
1444
1457
  ...engineering_tools_js_1.ENGINEERING_TOOLS,
1445
1458
  ],
@@ -1570,6 +1583,8 @@ class CodeBakersServer {
1570
1583
  return this.handleProjectSync(args);
1571
1584
  case 'project_dashboard_url':
1572
1585
  return this.handleProjectDashboardUrl();
1586
+ case 'resume_session':
1587
+ return this.handleResumeSession(args);
1573
1588
  // Engineering workflow tools
1574
1589
  case 'engineering_start':
1575
1590
  case 'engineering_scope':
@@ -3354,6 +3369,200 @@ Just describe what you want to build! I'll automatically:
3354
3369
  }],
3355
3370
  };
3356
3371
  }
3372
+ handleResumeSession(args) {
3373
+ const { reason = 'session start' } = args;
3374
+ const cwd = process.cwd();
3375
+ // Initialize state object
3376
+ const state = {
3377
+ isSetUp: false,
3378
+ projectName: '',
3379
+ projectType: '',
3380
+ hasPrd: false,
3381
+ prdSummary: '',
3382
+ inProgressTasks: [],
3383
+ completedTasks: [],
3384
+ blockers: [],
3385
+ lastSession: '',
3386
+ suggestion: '',
3387
+ status: 'UNKNOWN',
3388
+ };
3389
+ // Check if CodeBakers is set up
3390
+ const codebakersJsonPath = path.join(cwd, '.codebakers.json');
3391
+ if (!fs.existsSync(codebakersJsonPath)) {
3392
+ state.suggestion = 'Project not set up. Run `codebakers go` in the terminal to start.';
3393
+ state.status = 'NOT_INITIALIZED';
3394
+ }
3395
+ else {
3396
+ state.isSetUp = true;
3397
+ // Read .codebakers.json
3398
+ try {
3399
+ const cbState = JSON.parse(fs.readFileSync(codebakersJsonPath, 'utf-8'));
3400
+ state.projectName = cbState.projectName || '';
3401
+ state.projectType = cbState.projectType || '';
3402
+ }
3403
+ catch {
3404
+ // Ignore parse errors
3405
+ }
3406
+ }
3407
+ // Check for PRD.md
3408
+ const prdPath = path.join(cwd, 'PRD.md');
3409
+ if (fs.existsSync(prdPath)) {
3410
+ state.hasPrd = true;
3411
+ try {
3412
+ const prdContent = fs.readFileSync(prdPath, 'utf-8');
3413
+ // Extract one-liner if present
3414
+ const oneLineMatch = prdContent.match(/\*\*One-liner:\*\*\s*(.+)/);
3415
+ if (oneLineMatch) {
3416
+ state.prdSummary = oneLineMatch[1].trim();
3417
+ }
3418
+ else {
3419
+ // Get first non-comment, non-header line
3420
+ const lines = prdContent.split('\n').filter(l => l.trim() && !l.startsWith('#') && !l.startsWith('<!--'));
3421
+ if (lines[0]) {
3422
+ state.prdSummary = lines[0].substring(0, 150);
3423
+ }
3424
+ }
3425
+ }
3426
+ catch {
3427
+ // Ignore read errors
3428
+ }
3429
+ }
3430
+ // Read PROJECT-STATE.md for tasks
3431
+ const projectStatePath = path.join(cwd, 'PROJECT-STATE.md');
3432
+ if (fs.existsSync(projectStatePath)) {
3433
+ try {
3434
+ const content = fs.readFileSync(projectStatePath, 'utf-8');
3435
+ // Extract In Progress section
3436
+ const inProgressMatch = content.match(/## In Progress\n([\s\S]*?)(?=\n##|$)/);
3437
+ if (inProgressMatch) {
3438
+ const lines = inProgressMatch[1].split('\n')
3439
+ .filter(l => l.trim().startsWith('-'))
3440
+ .map(l => l.replace(/^-\s*/, '').trim())
3441
+ .filter(l => l && !l.startsWith('<!--'));
3442
+ state.inProgressTasks = lines;
3443
+ }
3444
+ // Extract Completed section (last 5)
3445
+ const completedMatch = content.match(/## Completed\n([\s\S]*?)(?=\n##|$)/);
3446
+ if (completedMatch) {
3447
+ const lines = completedMatch[1].split('\n')
3448
+ .filter(l => l.trim().startsWith('-'))
3449
+ .map(l => l.replace(/^-\s*/, '').trim())
3450
+ .filter(l => l && !l.startsWith('<!--'));
3451
+ state.completedTasks = lines.slice(-5);
3452
+ }
3453
+ // Extract Blockers section
3454
+ const blockersMatch = content.match(/## Blockers\n([\s\S]*?)(?=\n##|$)/);
3455
+ if (blockersMatch) {
3456
+ const lines = blockersMatch[1].split('\n')
3457
+ .filter(l => l.trim().startsWith('-'))
3458
+ .map(l => l.replace(/^-\s*/, '').trim())
3459
+ .filter(l => l && !l.startsWith('<!--'));
3460
+ state.blockers = lines;
3461
+ }
3462
+ }
3463
+ catch {
3464
+ // Ignore read errors
3465
+ }
3466
+ }
3467
+ // Read DEVLOG for last session
3468
+ const devlogPath = path.join(cwd, '.codebakers', 'DEVLOG.md');
3469
+ if (fs.existsSync(devlogPath)) {
3470
+ try {
3471
+ const content = fs.readFileSync(devlogPath, 'utf-8');
3472
+ // Get first session entry
3473
+ const sessionMatch = content.match(/## .+?\n\*\*Session:\*\*\s*(.+)/);
3474
+ if (sessionMatch) {
3475
+ state.lastSession = sessionMatch[1].trim();
3476
+ }
3477
+ // Get "What was done" from most recent entry
3478
+ const whatDoneMatch = content.match(/### What was done:\n([\s\S]*?)(?=\n###|---|\n\n)/);
3479
+ if (whatDoneMatch && !state.lastSession) {
3480
+ const lines = whatDoneMatch[1].split('\n')
3481
+ .filter(l => l.trim().startsWith('-'))
3482
+ .map(l => l.replace(/^-\s*/, '').trim());
3483
+ if (lines[0]) {
3484
+ state.lastSession = lines[0];
3485
+ }
3486
+ }
3487
+ }
3488
+ catch {
3489
+ // Ignore read errors
3490
+ }
3491
+ }
3492
+ // Determine suggestion and status based on state
3493
+ if (!state.isSetUp) {
3494
+ state.status = 'NOT_INITIALIZED';
3495
+ state.suggestion = 'Run `codebakers go` in the terminal to set up the project.';
3496
+ }
3497
+ else if (state.blockers.length > 0) {
3498
+ state.status = 'BLOCKED';
3499
+ state.suggestion = `BLOCKED: ${state.blockers[0]}. Address this blocker first.`;
3500
+ }
3501
+ else if (state.inProgressTasks.length > 0) {
3502
+ state.status = 'IN_PROGRESS';
3503
+ state.suggestion = `CONTINUE: ${state.inProgressTasks[0]}`;
3504
+ }
3505
+ else if (state.hasPrd && state.completedTasks.length === 0) {
3506
+ state.status = 'READY_TO_BUILD';
3507
+ state.suggestion = 'START BUILDING: PRD exists. Begin implementing features from PRD.md';
3508
+ }
3509
+ else if (!state.hasPrd) {
3510
+ state.status = 'NEEDS_PRD';
3511
+ state.suggestion = 'DEFINE PROJECT: No PRD found. Ask the user what they want to build.';
3512
+ }
3513
+ else {
3514
+ state.status = 'READY';
3515
+ state.suggestion = 'READY: Project set up. Ask the user for the next feature to build.';
3516
+ }
3517
+ // Build response
3518
+ let response = `# 🔄 Session Context Recovered\n\n`;
3519
+ response += `**Reason:** ${reason}\n\n`;
3520
+ response += `## Project\n`;
3521
+ response += `- **Name:** ${state.projectName || 'Unknown'}\n`;
3522
+ response += `- **Type:** ${state.projectType || 'Not set'}\n`;
3523
+ response += `- **Status:** ${state.status}\n`;
3524
+ if (state.prdSummary) {
3525
+ response += `- **Description:** ${state.prdSummary}\n`;
3526
+ }
3527
+ response += `\n`;
3528
+ if (state.blockers.length > 0) {
3529
+ response += `## ⚠️ BLOCKERS (Address First!)\n`;
3530
+ for (const blocker of state.blockers) {
3531
+ response += `- ${blocker}\n`;
3532
+ }
3533
+ response += `\n`;
3534
+ }
3535
+ if (state.inProgressTasks.length > 0) {
3536
+ response += `## 🔄 In Progress\n`;
3537
+ for (const task of state.inProgressTasks) {
3538
+ response += `- ${task}\n`;
3539
+ }
3540
+ response += `\n`;
3541
+ }
3542
+ if (state.completedTasks.length > 0) {
3543
+ response += `## ✅ Recently Completed\n`;
3544
+ for (const task of state.completedTasks) {
3545
+ response += `- ${task}\n`;
3546
+ }
3547
+ response += `\n`;
3548
+ }
3549
+ if (state.lastSession) {
3550
+ response += `## 📅 Last Session\n`;
3551
+ response += `${state.lastSession}\n\n`;
3552
+ }
3553
+ response += `---\n\n`;
3554
+ response += `## 🎯 NEXT ACTION\n\n`;
3555
+ response += `**${state.suggestion}**\n\n`;
3556
+ response += `---\n\n`;
3557
+ response += `*Context recovered via CodeBakers resume_session. `;
3558
+ response += `This tool should be called automatically after conversation compaction.*`;
3559
+ return {
3560
+ content: [{
3561
+ type: 'text',
3562
+ text: response,
3563
+ }],
3564
+ };
3565
+ }
3357
3566
  handleRunTests(args) {
3358
3567
  const { filter, watch = false } = args;
3359
3568
  const cwd = process.cwd();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.8.8",
3
+ "version": "3.9.0",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -778,6 +778,222 @@ interface GoOptions {
778
778
  skipReview?: boolean;
779
779
  }
780
780
 
781
+ // ============================================================================
782
+ // SMART CONTEXT DETECTION - "Where am I? What's next?"
783
+ // ============================================================================
784
+
785
+ interface ProjectState {
786
+ isSetUp: boolean;
787
+ projectName?: string;
788
+ projectType?: string;
789
+ hasPrd: boolean;
790
+ prdSummary?: string;
791
+ inProgressTasks: string[];
792
+ completedTasks: string[];
793
+ blockers: string[];
794
+ lastSession?: string;
795
+ suggestion: string;
796
+ }
797
+
798
+ function analyzeProjectState(cwd: string): ProjectState {
799
+ const state: ProjectState = {
800
+ isSetUp: false,
801
+ hasPrd: false,
802
+ inProgressTasks: [],
803
+ completedTasks: [],
804
+ blockers: [],
805
+ suggestion: '',
806
+ };
807
+
808
+ // Check if CodeBakers is set up
809
+ const codebakersJsonPath = join(cwd, '.codebakers.json');
810
+ if (!existsSync(codebakersJsonPath)) {
811
+ state.suggestion = 'Project not set up. Running first-time setup...';
812
+ return state;
813
+ }
814
+
815
+ state.isSetUp = true;
816
+
817
+ // Read .codebakers.json
818
+ try {
819
+ const cbState = JSON.parse(readFileSync(codebakersJsonPath, 'utf-8'));
820
+ state.projectName = cbState.projectName;
821
+ state.projectType = cbState.projectType;
822
+ } catch {
823
+ // Ignore parse errors
824
+ }
825
+
826
+ // Check for PRD.md
827
+ const prdPath = join(cwd, 'PRD.md');
828
+ if (existsSync(prdPath)) {
829
+ state.hasPrd = true;
830
+ try {
831
+ const prdContent = readFileSync(prdPath, 'utf-8');
832
+ // Extract one-liner if present
833
+ const oneLineMatch = prdContent.match(/\*\*One-liner:\*\*\s*(.+)/);
834
+ if (oneLineMatch) {
835
+ state.prdSummary = oneLineMatch[1].trim();
836
+ } else {
837
+ // Get first non-comment, non-header line
838
+ const lines = prdContent.split('\n').filter(l =>
839
+ l.trim() && !l.startsWith('#') && !l.startsWith('<!--')
840
+ );
841
+ if (lines[0]) {
842
+ state.prdSummary = lines[0].substring(0, 100);
843
+ }
844
+ }
845
+ } catch {
846
+ // Ignore read errors
847
+ }
848
+ }
849
+
850
+ // Read PROJECT-STATE.md for tasks
851
+ const projectStatePath = join(cwd, 'PROJECT-STATE.md');
852
+ if (existsSync(projectStatePath)) {
853
+ try {
854
+ const content = readFileSync(projectStatePath, 'utf-8');
855
+
856
+ // Extract In Progress section
857
+ const inProgressMatch = content.match(/## In Progress\n([\s\S]*?)(?=\n##|$)/);
858
+ if (inProgressMatch) {
859
+ const lines = inProgressMatch[1].split('\n')
860
+ .filter(l => l.trim().startsWith('-'))
861
+ .map(l => l.replace(/^-\s*/, '').trim())
862
+ .filter(l => l && !l.startsWith('<!--'));
863
+ state.inProgressTasks = lines;
864
+ }
865
+
866
+ // Extract Completed section (last 5)
867
+ const completedMatch = content.match(/## Completed\n([\s\S]*?)(?=\n##|$)/);
868
+ if (completedMatch) {
869
+ const lines = completedMatch[1].split('\n')
870
+ .filter(l => l.trim().startsWith('-'))
871
+ .map(l => l.replace(/^-\s*/, '').trim())
872
+ .filter(l => l && !l.startsWith('<!--'));
873
+ state.completedTasks = lines.slice(-5);
874
+ }
875
+
876
+ // Extract Blockers section
877
+ const blockersMatch = content.match(/## Blockers\n([\s\S]*?)(?=\n##|$)/);
878
+ if (blockersMatch) {
879
+ const lines = blockersMatch[1].split('\n')
880
+ .filter(l => l.trim().startsWith('-'))
881
+ .map(l => l.replace(/^-\s*/, '').trim())
882
+ .filter(l => l && !l.startsWith('<!--'));
883
+ state.blockers = lines;
884
+ }
885
+ } catch {
886
+ // Ignore read errors
887
+ }
888
+ }
889
+
890
+ // Read DEVLOG for last session
891
+ const devlogPath = join(cwd, '.codebakers', 'DEVLOG.md');
892
+ if (existsSync(devlogPath)) {
893
+ try {
894
+ const content = readFileSync(devlogPath, 'utf-8');
895
+ // Get first session entry
896
+ const sessionMatch = content.match(/## .+?\n\*\*Session:\*\*\s*(.+)/);
897
+ if (sessionMatch) {
898
+ state.lastSession = sessionMatch[1].trim();
899
+ }
900
+ // Get "What was done" from most recent entry
901
+ const whatDoneMatch = content.match(/### What was done:\n([\s\S]*?)(?=\n###|---|\n\n)/);
902
+ if (whatDoneMatch && !state.lastSession) {
903
+ const lines = whatDoneMatch[1].split('\n')
904
+ .filter(l => l.trim().startsWith('-'))
905
+ .map(l => l.replace(/^-\s*/, '').trim());
906
+ if (lines[0]) {
907
+ state.lastSession = lines[0];
908
+ }
909
+ }
910
+ } catch {
911
+ // Ignore read errors
912
+ }
913
+ }
914
+
915
+ // Determine suggestion based on state
916
+ if (state.blockers.length > 0) {
917
+ state.suggestion = `BLOCKED: ${state.blockers[0]}. Address this blocker first.`;
918
+ } else if (state.inProgressTasks.length > 0) {
919
+ state.suggestion = `CONTINUE: ${state.inProgressTasks[0]}`;
920
+ } else if (state.hasPrd && state.completedTasks.length === 0) {
921
+ state.suggestion = `START BUILDING: PRD exists. Begin implementing features from PRD.md`;
922
+ } else if (!state.hasPrd) {
923
+ state.suggestion = `DEFINE PROJECT: No PRD found. Describe what you want to build.`;
924
+ } else {
925
+ state.suggestion = `READY: Project set up. Ask for the next feature to build.`;
926
+ }
927
+
928
+ return state;
929
+ }
930
+
931
+ function showResumeContext(state: ProjectState): void {
932
+ console.log(chalk.blue(`
933
+ ╔═══════════════════════════════════════════════════════════╗
934
+ ║ ║
935
+ ║ ${chalk.bold.white('CodeBakers - Resuming Session')} ║
936
+ ║ ║
937
+ ╚═══════════════════════════════════════════════════════════╝
938
+ `));
939
+
940
+ console.log(chalk.white(` 📁 Project: ${chalk.cyan(state.projectName || 'Unknown')}`));
941
+
942
+ if (state.prdSummary) {
943
+ console.log(chalk.gray(` 📝 ${state.prdSummary}`));
944
+ }
945
+
946
+ console.log('');
947
+
948
+ // Show blockers first (critical)
949
+ if (state.blockers.length > 0) {
950
+ console.log(chalk.red(' ⚠️ BLOCKERS:'));
951
+ for (const blocker of state.blockers) {
952
+ console.log(chalk.red(` • ${blocker}`));
953
+ }
954
+ console.log('');
955
+ }
956
+
957
+ // Show in-progress tasks
958
+ if (state.inProgressTasks.length > 0) {
959
+ console.log(chalk.yellow(' 🔄 IN PROGRESS:'));
960
+ for (const task of state.inProgressTasks) {
961
+ console.log(chalk.yellow(` • ${task}`));
962
+ }
963
+ console.log('');
964
+ }
965
+
966
+ // Show recent completed (context)
967
+ if (state.completedTasks.length > 0) {
968
+ console.log(chalk.green(' ✓ RECENTLY COMPLETED:'));
969
+ for (const task of state.completedTasks.slice(-3)) {
970
+ console.log(chalk.gray(` • ${task}`));
971
+ }
972
+ console.log('');
973
+ }
974
+
975
+ // Show last session timestamp if available
976
+ if (state.lastSession) {
977
+ console.log(chalk.gray(` 📅 Last session: ${state.lastSession}`));
978
+ console.log('');
979
+ }
980
+
981
+ // Show the suggestion prominently
982
+ console.log(chalk.cyan(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
983
+ console.log(chalk.white.bold(`\n → ${state.suggestion}\n`));
984
+ console.log(chalk.cyan(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
985
+
986
+ // Output machine-readable context for AI
987
+ console.log(chalk.gray(' [AI Context]'));
988
+ console.log(chalk.gray(` Project: ${state.projectName || 'Unknown'}`));
989
+ console.log(chalk.gray(` Status: ${state.inProgressTasks.length > 0 ? 'IN_PROGRESS' : state.blockers.length > 0 ? 'BLOCKED' : 'READY'}`));
990
+ console.log(chalk.gray(` Next Action: ${state.suggestion}`));
991
+ if (state.hasPrd) {
992
+ console.log(chalk.gray(` PRD: Available at PRD.md`));
993
+ }
994
+ console.log('');
995
+ }
996
+
781
997
  interface ConfirmData {
782
998
  version: string;
783
999
  moduleCount: number;
@@ -838,12 +1054,48 @@ function log(message: string, options?: GoOptions): void {
838
1054
  /**
839
1055
  * Zero-friction entry point - start using CodeBakers instantly
840
1056
  * Single command for both trial and paid users
1057
+ *
1058
+ * SMART BEHAVIOR:
1059
+ * - If CodeBakers already set up → Show context and resume from where you left off
1060
+ * - If not set up → Run first-time setup (trial or login)
841
1061
  */
842
1062
  export async function go(options: GoOptions = {}): Promise<void> {
843
1063
  log('Starting go command...', options);
844
1064
  log(`API URL: ${getApiUrl()}`, options);
845
1065
  log(`Working directory: ${process.cwd()}`, options);
846
1066
 
1067
+ const cwd = process.cwd();
1068
+
1069
+ // =========================================================================
1070
+ // SMART CONTEXT CHECK - If already set up, show resume context
1071
+ // =========================================================================
1072
+ const projectState = analyzeProjectState(cwd);
1073
+
1074
+ if (projectState.isSetUp) {
1075
+ // Project already has CodeBakers - show context and resume
1076
+ showResumeContext(projectState);
1077
+
1078
+ // Verify auth is still valid
1079
+ const existingApiKey = getApiKey();
1080
+ const existingTrial = getTrialState();
1081
+
1082
+ if (existingApiKey) {
1083
+ console.log(chalk.green(' ✓ Authenticated (API key)\n'));
1084
+ } else if (existingTrial && !isTrialExpired()) {
1085
+ const daysRemaining = getTrialDaysRemaining();
1086
+ console.log(chalk.green(` ✓ Trial active (${daysRemaining} days remaining)\n`));
1087
+ } else if (existingTrial && isTrialExpired()) {
1088
+ console.log(chalk.yellow(' ⚠️ Trial expired. Run `codebakers extend` or login.\n'));
1089
+ }
1090
+
1091
+ // Don't run setup again - just show context
1092
+ return;
1093
+ }
1094
+
1095
+ // =========================================================================
1096
+ // FIRST-TIME SETUP - Project not yet configured
1097
+ // =========================================================================
1098
+
847
1099
  console.log(chalk.blue(`
848
1100
  ╔═══════════════════════════════════════════════════════════╗
849
1101
  ║ ║
package/src/mcp/server.ts CHANGED
@@ -1552,6 +1552,20 @@ class CodeBakersServer {
1552
1552
  properties: {},
1553
1553
  },
1554
1554
  },
1555
+ {
1556
+ name: 'resume_session',
1557
+ description:
1558
+ 'IMPORTANT: Call this AUTOMATICALLY at the start of any session, especially after conversation compaction/summarization. Returns full project context including: project name, PRD summary, in-progress tasks, completed tasks, blockers, and a suggested next action. This prevents losing context after Claude Code or Cursor compacts the conversation. Use when: (1) starting a new session, (2) conversation was just summarized, (3) you are unsure what you were working on, (4) user says "where was I?" or "what should I do next?".',
1559
+ inputSchema: {
1560
+ type: 'object' as const,
1561
+ properties: {
1562
+ reason: {
1563
+ type: 'string',
1564
+ description: 'Why you are calling this (e.g., "session start", "after compaction", "context lost")',
1565
+ },
1566
+ },
1567
+ },
1568
+ },
1555
1569
  // Engineering workflow tools
1556
1570
  ...ENGINEERING_TOOLS,
1557
1571
  ],
@@ -1797,6 +1811,9 @@ class CodeBakersServer {
1797
1811
  case 'project_dashboard_url':
1798
1812
  return this.handleProjectDashboardUrl();
1799
1813
 
1814
+ case 'resume_session':
1815
+ return this.handleResumeSession(args as { reason?: string });
1816
+
1800
1817
  // Engineering workflow tools
1801
1818
  case 'engineering_start':
1802
1819
  case 'engineering_scope':
@@ -3784,6 +3801,211 @@ Just describe what you want to build! I'll automatically:
3784
3801
  };
3785
3802
  }
3786
3803
 
3804
+ private handleResumeSession(args: { reason?: string }) {
3805
+ const { reason = 'session start' } = args;
3806
+ const cwd = process.cwd();
3807
+
3808
+ // Initialize state object
3809
+ const state = {
3810
+ isSetUp: false,
3811
+ projectName: '',
3812
+ projectType: '',
3813
+ hasPrd: false,
3814
+ prdSummary: '',
3815
+ inProgressTasks: [] as string[],
3816
+ completedTasks: [] as string[],
3817
+ blockers: [] as string[],
3818
+ lastSession: '',
3819
+ suggestion: '',
3820
+ status: 'UNKNOWN',
3821
+ };
3822
+
3823
+ // Check if CodeBakers is set up
3824
+ const codebakersJsonPath = path.join(cwd, '.codebakers.json');
3825
+ if (!fs.existsSync(codebakersJsonPath)) {
3826
+ state.suggestion = 'Project not set up. Run `codebakers go` in the terminal to start.';
3827
+ state.status = 'NOT_INITIALIZED';
3828
+ } else {
3829
+ state.isSetUp = true;
3830
+
3831
+ // Read .codebakers.json
3832
+ try {
3833
+ const cbState = JSON.parse(fs.readFileSync(codebakersJsonPath, 'utf-8'));
3834
+ state.projectName = cbState.projectName || '';
3835
+ state.projectType = cbState.projectType || '';
3836
+ } catch {
3837
+ // Ignore parse errors
3838
+ }
3839
+ }
3840
+
3841
+ // Check for PRD.md
3842
+ const prdPath = path.join(cwd, 'PRD.md');
3843
+ if (fs.existsSync(prdPath)) {
3844
+ state.hasPrd = true;
3845
+ try {
3846
+ const prdContent = fs.readFileSync(prdPath, 'utf-8');
3847
+ // Extract one-liner if present
3848
+ const oneLineMatch = prdContent.match(/\*\*One-liner:\*\*\s*(.+)/);
3849
+ if (oneLineMatch) {
3850
+ state.prdSummary = oneLineMatch[1].trim();
3851
+ } else {
3852
+ // Get first non-comment, non-header line
3853
+ const lines = prdContent.split('\n').filter(l =>
3854
+ l.trim() && !l.startsWith('#') && !l.startsWith('<!--')
3855
+ );
3856
+ if (lines[0]) {
3857
+ state.prdSummary = lines[0].substring(0, 150);
3858
+ }
3859
+ }
3860
+ } catch {
3861
+ // Ignore read errors
3862
+ }
3863
+ }
3864
+
3865
+ // Read PROJECT-STATE.md for tasks
3866
+ const projectStatePath = path.join(cwd, 'PROJECT-STATE.md');
3867
+ if (fs.existsSync(projectStatePath)) {
3868
+ try {
3869
+ const content = fs.readFileSync(projectStatePath, 'utf-8');
3870
+
3871
+ // Extract In Progress section
3872
+ const inProgressMatch = content.match(/## In Progress\n([\s\S]*?)(?=\n##|$)/);
3873
+ if (inProgressMatch) {
3874
+ const lines = inProgressMatch[1].split('\n')
3875
+ .filter(l => l.trim().startsWith('-'))
3876
+ .map(l => l.replace(/^-\s*/, '').trim())
3877
+ .filter(l => l && !l.startsWith('<!--'));
3878
+ state.inProgressTasks = lines;
3879
+ }
3880
+
3881
+ // Extract Completed section (last 5)
3882
+ const completedMatch = content.match(/## Completed\n([\s\S]*?)(?=\n##|$)/);
3883
+ if (completedMatch) {
3884
+ const lines = completedMatch[1].split('\n')
3885
+ .filter(l => l.trim().startsWith('-'))
3886
+ .map(l => l.replace(/^-\s*/, '').trim())
3887
+ .filter(l => l && !l.startsWith('<!--'));
3888
+ state.completedTasks = lines.slice(-5);
3889
+ }
3890
+
3891
+ // Extract Blockers section
3892
+ const blockersMatch = content.match(/## Blockers\n([\s\S]*?)(?=\n##|$)/);
3893
+ if (blockersMatch) {
3894
+ const lines = blockersMatch[1].split('\n')
3895
+ .filter(l => l.trim().startsWith('-'))
3896
+ .map(l => l.replace(/^-\s*/, '').trim())
3897
+ .filter(l => l && !l.startsWith('<!--'));
3898
+ state.blockers = lines;
3899
+ }
3900
+ } catch {
3901
+ // Ignore read errors
3902
+ }
3903
+ }
3904
+
3905
+ // Read DEVLOG for last session
3906
+ const devlogPath = path.join(cwd, '.codebakers', 'DEVLOG.md');
3907
+ if (fs.existsSync(devlogPath)) {
3908
+ try {
3909
+ const content = fs.readFileSync(devlogPath, 'utf-8');
3910
+ // Get first session entry
3911
+ const sessionMatch = content.match(/## .+?\n\*\*Session:\*\*\s*(.+)/);
3912
+ if (sessionMatch) {
3913
+ state.lastSession = sessionMatch[1].trim();
3914
+ }
3915
+ // Get "What was done" from most recent entry
3916
+ const whatDoneMatch = content.match(/### What was done:\n([\s\S]*?)(?=\n###|---|\n\n)/);
3917
+ if (whatDoneMatch && !state.lastSession) {
3918
+ const lines = whatDoneMatch[1].split('\n')
3919
+ .filter(l => l.trim().startsWith('-'))
3920
+ .map(l => l.replace(/^-\s*/, '').trim());
3921
+ if (lines[0]) {
3922
+ state.lastSession = lines[0];
3923
+ }
3924
+ }
3925
+ } catch {
3926
+ // Ignore read errors
3927
+ }
3928
+ }
3929
+
3930
+ // Determine suggestion and status based on state
3931
+ if (!state.isSetUp) {
3932
+ state.status = 'NOT_INITIALIZED';
3933
+ state.suggestion = 'Run `codebakers go` in the terminal to set up the project.';
3934
+ } else if (state.blockers.length > 0) {
3935
+ state.status = 'BLOCKED';
3936
+ state.suggestion = `BLOCKED: ${state.blockers[0]}. Address this blocker first.`;
3937
+ } else if (state.inProgressTasks.length > 0) {
3938
+ state.status = 'IN_PROGRESS';
3939
+ state.suggestion = `CONTINUE: ${state.inProgressTasks[0]}`;
3940
+ } else if (state.hasPrd && state.completedTasks.length === 0) {
3941
+ state.status = 'READY_TO_BUILD';
3942
+ state.suggestion = 'START BUILDING: PRD exists. Begin implementing features from PRD.md';
3943
+ } else if (!state.hasPrd) {
3944
+ state.status = 'NEEDS_PRD';
3945
+ state.suggestion = 'DEFINE PROJECT: No PRD found. Ask the user what they want to build.';
3946
+ } else {
3947
+ state.status = 'READY';
3948
+ state.suggestion = 'READY: Project set up. Ask the user for the next feature to build.';
3949
+ }
3950
+
3951
+ // Build response
3952
+ let response = `# 🔄 Session Context Recovered\n\n`;
3953
+ response += `**Reason:** ${reason}\n\n`;
3954
+
3955
+ response += `## Project\n`;
3956
+ response += `- **Name:** ${state.projectName || 'Unknown'}\n`;
3957
+ response += `- **Type:** ${state.projectType || 'Not set'}\n`;
3958
+ response += `- **Status:** ${state.status}\n`;
3959
+ if (state.prdSummary) {
3960
+ response += `- **Description:** ${state.prdSummary}\n`;
3961
+ }
3962
+ response += `\n`;
3963
+
3964
+ if (state.blockers.length > 0) {
3965
+ response += `## ⚠️ BLOCKERS (Address First!)\n`;
3966
+ for (const blocker of state.blockers) {
3967
+ response += `- ${blocker}\n`;
3968
+ }
3969
+ response += `\n`;
3970
+ }
3971
+
3972
+ if (state.inProgressTasks.length > 0) {
3973
+ response += `## 🔄 In Progress\n`;
3974
+ for (const task of state.inProgressTasks) {
3975
+ response += `- ${task}\n`;
3976
+ }
3977
+ response += `\n`;
3978
+ }
3979
+
3980
+ if (state.completedTasks.length > 0) {
3981
+ response += `## ✅ Recently Completed\n`;
3982
+ for (const task of state.completedTasks) {
3983
+ response += `- ${task}\n`;
3984
+ }
3985
+ response += `\n`;
3986
+ }
3987
+
3988
+ if (state.lastSession) {
3989
+ response += `## 📅 Last Session\n`;
3990
+ response += `${state.lastSession}\n\n`;
3991
+ }
3992
+
3993
+ response += `---\n\n`;
3994
+ response += `## 🎯 NEXT ACTION\n\n`;
3995
+ response += `**${state.suggestion}**\n\n`;
3996
+
3997
+ response += `---\n\n`;
3998
+ response += `*Context recovered via CodeBakers resume_session. `;
3999
+ response += `This tool should be called automatically after conversation compaction.*`;
4000
+
4001
+ return {
4002
+ content: [{
4003
+ type: 'text' as const,
4004
+ text: response,
4005
+ }],
4006
+ };
4007
+ }
4008
+
3787
4009
  private handleRunTests(args: { filter?: string; watch?: boolean }) {
3788
4010
  const { filter, watch = false } = args;
3789
4011
  const cwd = process.cwd();