Package not found. Please check the package name and try again.
@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.
- package/dist/commands/go.d.ts +4 -0
- package/dist/commands/go.js +215 -0
- package/dist/mcp/server.js +209 -0
- package/package.json +1 -1
- package/src/commands/go.ts +252 -0
- package/src/mcp/server.ts +222 -0
package/dist/commands/go.d.ts
CHANGED
|
@@ -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 {};
|
package/dist/commands/go.js
CHANGED
|
@@ -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/dist/mcp/server.js
CHANGED
|
@@ -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
package/src/commands/go.ts
CHANGED
|
@@ -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();
|