@codebakers/cli 3.8.7 → 3.8.9

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.
@@ -1,9 +1,17 @@
1
1
  interface GoOptions {
2
2
  verbose?: boolean;
3
+ type?: 'personal' | 'client' | 'business';
4
+ name?: string;
5
+ describe?: 'guided' | 'template' | 'paste' | 'chat' | 'files';
6
+ skipReview?: boolean;
3
7
  }
4
8
  /**
5
9
  * Zero-friction entry point - start using CodeBakers instantly
6
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)
7
15
  */
8
16
  export declare function go(options?: GoOptions): Promise<void>;
9
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
  ║ ║
@@ -1113,19 +1328,29 @@ async function setupProject(options = {}, auth) {
1113
1328
  }
1114
1329
  async function setupNewProject(cwd, options = {}, auth) {
1115
1330
  console.log(chalk_1.default.cyan('\n ━━━ New Project Setup ━━━\n'));
1116
- // Get project info
1117
- console.log(chalk_1.default.white(' What kind of project is this?\n'));
1118
- console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('PERSONAL') + chalk_1.default.gray(' - Just building for myself'));
1119
- console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('CLIENT') + chalk_1.default.gray(' - Building for someone else'));
1120
- console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('BUSINESS') + chalk_1.default.gray(' - My own product/startup\n'));
1121
- let typeChoice = '';
1122
- while (!['1', '2', '3'].includes(typeChoice)) {
1123
- typeChoice = await prompt(' Enter 1, 2, or 3: ');
1124
- }
1125
- const typeMap = { '1': 'personal', '2': 'client', '3': 'business' };
1126
- const projectType = typeMap[typeChoice];
1331
+ let projectType;
1332
+ let projectName;
1127
1333
  const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
1128
- const projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
1334
+ // Use flags if provided (non-interactive mode for AI)
1335
+ if (options.type) {
1336
+ projectType = options.type;
1337
+ projectName = options.name || defaultName;
1338
+ console.log(chalk_1.default.green(` Using: ${projectType.toUpperCase()} project named "${projectName}"\n`));
1339
+ }
1340
+ else {
1341
+ // Interactive mode - ask questions
1342
+ console.log(chalk_1.default.white(' What kind of project is this?\n'));
1343
+ console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('PERSONAL') + chalk_1.default.gray(' - Just building for myself'));
1344
+ console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('CLIENT') + chalk_1.default.gray(' - Building for someone else'));
1345
+ console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('BUSINESS') + chalk_1.default.gray(' - My own product/startup\n'));
1346
+ let typeChoice = '';
1347
+ while (!['1', '2', '3'].includes(typeChoice)) {
1348
+ typeChoice = await prompt(' Enter 1, 2, or 3: ');
1349
+ }
1350
+ const typeMap = { '1': 'personal', '2': 'client', '3': 'business' };
1351
+ projectType = typeMap[typeChoice];
1352
+ projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
1353
+ }
1129
1354
  console.log(chalk_1.default.green(`\n ✓ Setting up "${projectName}" as ${projectType.toUpperCase()} project\n`));
1130
1355
  // Install bootstrap files
1131
1356
  console.log(chalk_1.default.white(' Installing CodeBakers...\n'));
@@ -1140,15 +1365,26 @@ async function setupNewProject(cwd, options = {}, auth) {
1140
1365
  // Update .gitignore
1141
1366
  updateGitignore(cwd);
1142
1367
  // How to describe project
1143
- console.log(chalk_1.default.white('\n 📝 How would you like to describe your project?\n'));
1144
- console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('GUIDED QUESTIONS') + chalk_1.default.gray(' - I\'ll ask you step by step'));
1145
- console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('WRITE A PRD') + chalk_1.default.gray(' - Create a blank template to fill out'));
1146
- console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('PASTE/UPLOAD PRD') + chalk_1.default.gray(' - I already have requirements written'));
1147
- console.log(chalk_1.default.gray(' 4. ') + chalk_1.default.cyan('DESCRIBE IN CHAT') + chalk_1.default.gray(' - Just tell the AI what you want'));
1148
- console.log(chalk_1.default.gray(' 5. ') + chalk_1.default.cyan('SHARE FILES') + chalk_1.default.gray(' - I\'ll share docs/mockups/screenshots\n'));
1149
1368
  let describeChoice = '';
1150
- while (!['1', '2', '3', '4', '5'].includes(describeChoice)) {
1151
- describeChoice = await prompt(' Enter 1-5: ');
1369
+ // Use flag if provided (non-interactive mode for AI)
1370
+ if (options.describe) {
1371
+ const describeMap = {
1372
+ 'guided': '1', 'template': '2', 'paste': '3', 'chat': '4', 'files': '5'
1373
+ };
1374
+ describeChoice = describeMap[options.describe] || '4';
1375
+ console.log(chalk_1.default.green(` Using: ${options.describe} mode for project description\n`));
1376
+ }
1377
+ else {
1378
+ // Interactive mode
1379
+ console.log(chalk_1.default.white('\n 📝 How would you like to describe your project?\n'));
1380
+ console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('GUIDED QUESTIONS') + chalk_1.default.gray(' - I\'ll ask you step by step'));
1381
+ console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('WRITE A PRD') + chalk_1.default.gray(' - Create a blank template to fill out'));
1382
+ console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('PASTE/UPLOAD PRD') + chalk_1.default.gray(' - I already have requirements written'));
1383
+ console.log(chalk_1.default.gray(' 4. ') + chalk_1.default.cyan('DESCRIBE IN CHAT') + chalk_1.default.gray(' - Just tell the AI what you want'));
1384
+ console.log(chalk_1.default.gray(' 5. ') + chalk_1.default.cyan('SHARE FILES') + chalk_1.default.gray(' - I\'ll share docs/mockups/screenshots\n'));
1385
+ while (!['1', '2', '3', '4', '5'].includes(describeChoice)) {
1386
+ describeChoice = await prompt(' Enter 1-5: ');
1387
+ }
1152
1388
  }
1153
1389
  let prdCreated = false;
1154
1390
  if (describeChoice === '1') {
@@ -1244,15 +1480,36 @@ async function setupExistingProject(cwd, projectInfo, options = {}, auth) {
1244
1480
  }
1245
1481
  // Get project name
1246
1482
  const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
1247
- const projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
1248
- // Code review offer
1249
- console.log(chalk_1.default.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
1250
- console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('YES, REVIEW & FIX') + chalk_1.default.gray(' - Run audit, then auto-fix issues'));
1251
- console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('REVIEW ONLY') + chalk_1.default.gray(' - Just show me the issues'));
1252
- console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('SKIP') + chalk_1.default.gray(' - Just install CodeBakers\n'));
1253
- let reviewChoice = '';
1254
- while (!['1', '2', '3'].includes(reviewChoice)) {
1255
- reviewChoice = await prompt(' Enter 1, 2, or 3: ');
1483
+ let projectName;
1484
+ let reviewChoice;
1485
+ // Use flags if provided (non-interactive mode for AI)
1486
+ if (options.name) {
1487
+ projectName = options.name;
1488
+ console.log(chalk_1.default.green(`\n Using project name: "${projectName}"\n`));
1489
+ }
1490
+ else {
1491
+ projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
1492
+ }
1493
+ // Use skipReview flag or ask
1494
+ if (options.skipReview) {
1495
+ reviewChoice = '3';
1496
+ console.log(chalk_1.default.gray(' Skipping code review (--skip-review flag)\n'));
1497
+ }
1498
+ else if (options.type) {
1499
+ // If running in non-interactive mode, default to skip review
1500
+ reviewChoice = '3';
1501
+ console.log(chalk_1.default.gray(' Skipping code review (non-interactive mode)\n'));
1502
+ }
1503
+ else {
1504
+ // Interactive mode - ask about code review
1505
+ console.log(chalk_1.default.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
1506
+ console.log(chalk_1.default.gray(' 1. ') + chalk_1.default.cyan('YES, REVIEW & FIX') + chalk_1.default.gray(' - Run audit, then auto-fix issues'));
1507
+ console.log(chalk_1.default.gray(' 2. ') + chalk_1.default.cyan('REVIEW ONLY') + chalk_1.default.gray(' - Just show me the issues'));
1508
+ console.log(chalk_1.default.gray(' 3. ') + chalk_1.default.cyan('SKIP') + chalk_1.default.gray(' - Just install CodeBakers\n'));
1509
+ reviewChoice = '';
1510
+ while (!['1', '2', '3'].includes(reviewChoice)) {
1511
+ reviewChoice = await prompt(' Enter 1, 2, or 3: ');
1512
+ }
1256
1513
  }
1257
1514
  let auditScore;
1258
1515
  if (reviewChoice !== '3') {
package/dist/index.js CHANGED
@@ -202,7 +202,18 @@ program
202
202
  .alias('start')
203
203
  .description('Start using CodeBakers instantly (no signup required)')
204
204
  .option('-v, --verbose', 'Show detailed debug output for troubleshooting')
205
- .action((options) => (0, go_js_1.go)({ verbose: options.verbose }));
205
+ // Non-interactive flags for programmatic use (e.g., by AI assistants)
206
+ .option('-t, --type <type>', 'Project type: personal, client, or business')
207
+ .option('-n, --name <name>', 'Project name')
208
+ .option('-d, --describe <mode>', 'Description mode: guided, template, paste, chat, or files')
209
+ .option('--skip-review', 'Skip the review question for existing projects')
210
+ .action((options) => (0, go_js_1.go)({
211
+ verbose: options.verbose,
212
+ type: options.type,
213
+ name: options.name,
214
+ describe: options.describe,
215
+ skipReview: options.skipReview,
216
+ }));
206
217
  program
207
218
  .command('extend')
208
219
  .description('Extend your free trial with GitHub')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.8.7",
3
+ "version": "3.8.9",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -771,6 +771,227 @@ function updateGitignore(cwd: string): void {
771
771
 
772
772
  interface GoOptions {
773
773
  verbose?: boolean;
774
+ // Non-interactive flags for programmatic use (e.g., by AI assistants)
775
+ type?: 'personal' | 'client' | 'business';
776
+ name?: string;
777
+ describe?: 'guided' | 'template' | 'paste' | 'chat' | 'files';
778
+ skipReview?: boolean;
779
+ }
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('');
774
995
  }
775
996
 
776
997
  interface ConfirmData {
@@ -833,12 +1054,48 @@ function log(message: string, options?: GoOptions): void {
833
1054
  /**
834
1055
  * Zero-friction entry point - start using CodeBakers instantly
835
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)
836
1061
  */
837
1062
  export async function go(options: GoOptions = {}): Promise<void> {
838
1063
  log('Starting go command...', options);
839
1064
  log(`API URL: ${getApiUrl()}`, options);
840
1065
  log(`Working directory: ${process.cwd()}`, options);
841
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
+
842
1099
  console.log(chalk.blue(`
843
1100
  ╔═══════════════════════════════════════════════════════════╗
844
1101
  ║ ║
@@ -1240,22 +1497,31 @@ async function setupProject(options: GoOptions = {}, auth?: AuthInfo): Promise<v
1240
1497
  async function setupNewProject(cwd: string, options: GoOptions = {}, auth?: AuthInfo): Promise<void> {
1241
1498
  console.log(chalk.cyan('\n ━━━ New Project Setup ━━━\n'));
1242
1499
 
1243
- // Get project info
1244
- console.log(chalk.white(' What kind of project is this?\n'));
1245
- console.log(chalk.gray(' 1. ') + chalk.cyan('PERSONAL') + chalk.gray(' - Just building for myself'));
1246
- console.log(chalk.gray(' 2. ') + chalk.cyan('CLIENT') + chalk.gray(' - Building for someone else'));
1247
- console.log(chalk.gray(' 3. ') + chalk.cyan('BUSINESS') + chalk.gray(' - My own product/startup\n'));
1248
-
1249
- let typeChoice = '';
1250
- while (!['1', '2', '3'].includes(typeChoice)) {
1251
- typeChoice = await prompt(' Enter 1, 2, or 3: ');
1252
- }
1500
+ let projectType: string;
1501
+ let projectName: string;
1502
+ const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
1253
1503
 
1254
- const typeMap: Record<string, string> = { '1': 'personal', '2': 'client', '3': 'business' };
1255
- const projectType = typeMap[typeChoice];
1504
+ // Use flags if provided (non-interactive mode for AI)
1505
+ if (options.type) {
1506
+ projectType = options.type;
1507
+ projectName = options.name || defaultName;
1508
+ console.log(chalk.green(` Using: ${projectType.toUpperCase()} project named "${projectName}"\n`));
1509
+ } else {
1510
+ // Interactive mode - ask questions
1511
+ console.log(chalk.white(' What kind of project is this?\n'));
1512
+ console.log(chalk.gray(' 1. ') + chalk.cyan('PERSONAL') + chalk.gray(' - Just building for myself'));
1513
+ console.log(chalk.gray(' 2. ') + chalk.cyan('CLIENT') + chalk.gray(' - Building for someone else'));
1514
+ console.log(chalk.gray(' 3. ') + chalk.cyan('BUSINESS') + chalk.gray(' - My own product/startup\n'));
1515
+
1516
+ let typeChoice = '';
1517
+ while (!['1', '2', '3'].includes(typeChoice)) {
1518
+ typeChoice = await prompt(' Enter 1, 2, or 3: ');
1519
+ }
1256
1520
 
1257
- const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
1258
- const projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
1521
+ const typeMap: Record<string, string> = { '1': 'personal', '2': 'client', '3': 'business' };
1522
+ projectType = typeMap[typeChoice];
1523
+ projectName = await prompt(` Project name (${defaultName}): `) || defaultName;
1524
+ }
1259
1525
 
1260
1526
  console.log(chalk.green(`\n ✓ Setting up "${projectName}" as ${projectType.toUpperCase()} project\n`));
1261
1527
 
@@ -1276,16 +1542,27 @@ async function setupNewProject(cwd: string, options: GoOptions = {}, auth?: Auth
1276
1542
  updateGitignore(cwd);
1277
1543
 
1278
1544
  // How to describe project
1279
- console.log(chalk.white('\n 📝 How would you like to describe your project?\n'));
1280
- console.log(chalk.gray(' 1. ') + chalk.cyan('GUIDED QUESTIONS') + chalk.gray(' - I\'ll ask you step by step'));
1281
- console.log(chalk.gray(' 2. ') + chalk.cyan('WRITE A PRD') + chalk.gray(' - Create a blank template to fill out'));
1282
- console.log(chalk.gray(' 3. ') + chalk.cyan('PASTE/UPLOAD PRD') + chalk.gray(' - I already have requirements written'));
1283
- console.log(chalk.gray(' 4. ') + chalk.cyan('DESCRIBE IN CHAT') + chalk.gray(' - Just tell the AI what you want'));
1284
- console.log(chalk.gray(' 5. ') + chalk.cyan('SHARE FILES') + chalk.gray(' - I\'ll share docs/mockups/screenshots\n'));
1285
-
1286
1545
  let describeChoice = '';
1287
- while (!['1', '2', '3', '4', '5'].includes(describeChoice)) {
1288
- describeChoice = await prompt(' Enter 1-5: ');
1546
+
1547
+ // Use flag if provided (non-interactive mode for AI)
1548
+ if (options.describe) {
1549
+ const describeMap: Record<string, string> = {
1550
+ 'guided': '1', 'template': '2', 'paste': '3', 'chat': '4', 'files': '5'
1551
+ };
1552
+ describeChoice = describeMap[options.describe] || '4';
1553
+ console.log(chalk.green(` Using: ${options.describe} mode for project description\n`));
1554
+ } else {
1555
+ // Interactive mode
1556
+ console.log(chalk.white('\n 📝 How would you like to describe your project?\n'));
1557
+ console.log(chalk.gray(' 1. ') + chalk.cyan('GUIDED QUESTIONS') + chalk.gray(' - I\'ll ask you step by step'));
1558
+ console.log(chalk.gray(' 2. ') + chalk.cyan('WRITE A PRD') + chalk.gray(' - Create a blank template to fill out'));
1559
+ console.log(chalk.gray(' 3. ') + chalk.cyan('PASTE/UPLOAD PRD') + chalk.gray(' - I already have requirements written'));
1560
+ console.log(chalk.gray(' 4. ') + chalk.cyan('DESCRIBE IN CHAT') + chalk.gray(' - Just tell the AI what you want'));
1561
+ console.log(chalk.gray(' 5. ') + chalk.cyan('SHARE FILES') + chalk.gray(' - I\'ll share docs/mockups/screenshots\n'));
1562
+
1563
+ while (!['1', '2', '3', '4', '5'].includes(describeChoice)) {
1564
+ describeChoice = await prompt(' Enter 1-5: ');
1565
+ }
1289
1566
  }
1290
1567
 
1291
1568
  let prdCreated = false;
@@ -1384,17 +1661,36 @@ async function setupExistingProject(cwd: string, projectInfo: ProjectInfo, optio
1384
1661
 
1385
1662
  // Get project name
1386
1663
  const defaultName = cwd.split(/[\\/]/).pop() || 'my-project';
1387
- const projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
1664
+ let projectName: string;
1665
+ let reviewChoice: string;
1388
1666
 
1389
- // Code review offer
1390
- console.log(chalk.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
1391
- console.log(chalk.gray(' 1. ') + chalk.cyan('YES, REVIEW & FIX') + chalk.gray(' - Run audit, then auto-fix issues'));
1392
- console.log(chalk.gray(' 2. ') + chalk.cyan('REVIEW ONLY') + chalk.gray(' - Just show me the issues'));
1393
- console.log(chalk.gray(' 3. ') + chalk.cyan('SKIP') + chalk.gray(' - Just install CodeBakers\n'));
1667
+ // Use flags if provided (non-interactive mode for AI)
1668
+ if (options.name) {
1669
+ projectName = options.name;
1670
+ console.log(chalk.green(`\n Using project name: "${projectName}"\n`));
1671
+ } else {
1672
+ projectName = await prompt(`\n Project name (${defaultName}): `) || defaultName;
1673
+ }
1394
1674
 
1395
- let reviewChoice = '';
1396
- while (!['1', '2', '3'].includes(reviewChoice)) {
1397
- reviewChoice = await prompt(' Enter 1, 2, or 3: ');
1675
+ // Use skipReview flag or ask
1676
+ if (options.skipReview) {
1677
+ reviewChoice = '3';
1678
+ console.log(chalk.gray(' Skipping code review (--skip-review flag)\n'));
1679
+ } else if (options.type) {
1680
+ // If running in non-interactive mode, default to skip review
1681
+ reviewChoice = '3';
1682
+ console.log(chalk.gray(' Skipping code review (non-interactive mode)\n'));
1683
+ } else {
1684
+ // Interactive mode - ask about code review
1685
+ console.log(chalk.white('\n Want me to review your code and bring it up to CodeBakers standards?\n'));
1686
+ console.log(chalk.gray(' 1. ') + chalk.cyan('YES, REVIEW & FIX') + chalk.gray(' - Run audit, then auto-fix issues'));
1687
+ console.log(chalk.gray(' 2. ') + chalk.cyan('REVIEW ONLY') + chalk.gray(' - Just show me the issues'));
1688
+ console.log(chalk.gray(' 3. ') + chalk.cyan('SKIP') + chalk.gray(' - Just install CodeBakers\n'));
1689
+
1690
+ reviewChoice = '';
1691
+ while (!['1', '2', '3'].includes(reviewChoice)) {
1692
+ reviewChoice = await prompt(' Enter 1, 2, or 3: ');
1693
+ }
1398
1694
  }
1399
1695
 
1400
1696
  let auditScore: number | undefined;
package/src/index.ts CHANGED
@@ -224,7 +224,18 @@ program
224
224
  .alias('start')
225
225
  .description('Start using CodeBakers instantly (no signup required)')
226
226
  .option('-v, --verbose', 'Show detailed debug output for troubleshooting')
227
- .action((options) => go({ verbose: options.verbose }));
227
+ // Non-interactive flags for programmatic use (e.g., by AI assistants)
228
+ .option('-t, --type <type>', 'Project type: personal, client, or business')
229
+ .option('-n, --name <name>', 'Project name')
230
+ .option('-d, --describe <mode>', 'Description mode: guided, template, paste, chat, or files')
231
+ .option('--skip-review', 'Skip the review question for existing projects')
232
+ .action((options) => go({
233
+ verbose: options.verbose,
234
+ type: options.type,
235
+ name: options.name,
236
+ describe: options.describe,
237
+ skipReview: options.skipReview,
238
+ }));
228
239
 
229
240
  program
230
241
  .command('extend')