@codebakers/cli 3.8.8 → 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.
@@ -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
  ║ ║
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "3.8.8",
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": {
@@ -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
  ║ ║