midas-mcp 3.0.0 → 3.3.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/LICENSE +21 -0
- package/dist/ai.d.ts +2 -0
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +28 -45
- package/dist/ai.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +129 -135
- package/dist/analyzer.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +109 -15
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +8 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +76 -7
- package/dist/context.js.map +1 -1
- package/dist/docs/DEPLOYMENT.md +190 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/monitoring.d.ts +97 -0
- package/dist/monitoring.d.ts.map +1 -0
- package/dist/monitoring.js +258 -0
- package/dist/monitoring.js.map +1 -0
- package/dist/prompts/grow.d.ts.map +1 -1
- package/dist/prompts/grow.js +155 -47
- package/dist/prompts/grow.js.map +1 -1
- package/dist/providers.d.ts +46 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +349 -0
- package/dist/providers.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +28 -3
- package/dist/server.js.map +1 -1
- package/dist/state/phase.d.ts +19 -4
- package/dist/state/phase.d.ts.map +1 -1
- package/dist/state/phase.js +19 -10
- package/dist/state/phase.js.map +1 -1
- package/dist/tools/analyze.d.ts.map +1 -1
- package/dist/tools/analyze.js +21 -9
- package/dist/tools/analyze.js.map +1 -1
- package/dist/tools/completeness.d.ts +36 -0
- package/dist/tools/completeness.d.ts.map +1 -0
- package/dist/tools/completeness.js +838 -0
- package/dist/tools/completeness.js.map +1 -0
- package/dist/tools/config.d.ts +73 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +94 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/grow.d.ts +157 -0
- package/dist/tools/grow.d.ts.map +1 -0
- package/dist/tools/grow.js +532 -0
- package/dist/tools/grow.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/validate.d.ts +60 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +234 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/verify.d.ts +4 -4
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +302 -180
- package/dist/tui.js.map +1 -1
- package/docs/DEPLOYMENT.md +190 -0
- package/package.json +17 -5
- package/dist/tests/analyze.test.d.ts +0 -2
- package/dist/tests/analyze.test.d.ts.map +0 -1
- package/dist/tests/analyze.test.js +0 -120
- package/dist/tests/analyze.test.js.map +0 -1
- package/dist/tests/context.test.d.ts +0 -2
- package/dist/tests/context.test.d.ts.map +0 -1
- package/dist/tests/context.test.js +0 -213
- package/dist/tests/context.test.js.map +0 -1
- package/dist/tests/edge-cases.test.d.ts +0 -2
- package/dist/tests/edge-cases.test.d.ts.map +0 -1
- package/dist/tests/edge-cases.test.js +0 -234
- package/dist/tests/edge-cases.test.js.map +0 -1
- package/dist/tests/journal.test.d.ts +0 -2
- package/dist/tests/journal.test.d.ts.map +0 -1
- package/dist/tests/journal.test.js +0 -184
- package/dist/tests/journal.test.js.map +0 -1
- package/dist/tests/metrics.test.d.ts +0 -2
- package/dist/tests/metrics.test.d.ts.map +0 -1
- package/dist/tests/metrics.test.js +0 -178
- package/dist/tests/metrics.test.js.map +0 -1
- package/dist/tests/phase.test.d.ts +0 -2
- package/dist/tests/phase.test.d.ts.map +0 -1
- package/dist/tests/phase.test.js +0 -110
- package/dist/tests/phase.test.js.map +0 -1
- package/dist/tests/prompts.test.d.ts +0 -2
- package/dist/tests/prompts.test.d.ts.map +0 -1
- package/dist/tests/prompts.test.js +0 -157
- package/dist/tests/prompts.test.js.map +0 -1
- package/dist/tests/search.test.d.ts +0 -2
- package/dist/tests/search.test.d.ts.map +0 -1
- package/dist/tests/search.test.js +0 -228
- package/dist/tests/search.test.js.map +0 -1
- package/dist/tests/security.test.d.ts +0 -2
- package/dist/tests/security.test.d.ts.map +0 -1
- package/dist/tests/security.test.js +0 -105
- package/dist/tests/security.test.js.map +0 -1
- package/dist/tests/server.test.d.ts +0 -2
- package/dist/tests/server.test.d.ts.map +0 -1
- package/dist/tests/server.test.js +0 -93
- package/dist/tests/server.test.js.map +0 -1
- package/dist/tests/techstack.test.d.ts +0 -2
- package/dist/tests/techstack.test.d.ts.map +0 -1
- package/dist/tests/techstack.test.js +0 -187
- package/dist/tests/techstack.test.js.map +0 -1
- package/dist/tests/tools.test.d.ts +0 -2
- package/dist/tests/tools.test.d.ts.map +0 -1
- package/dist/tests/tools.test.js +0 -137
- package/dist/tests/tools.test.js.map +0 -1
- package/dist/tests/tracker.test.d.ts +0 -2
- package/dist/tests/tracker.test.d.ts.map +0 -1
- package/dist/tests/tracker.test.js +0 -197
- package/dist/tests/tracker.test.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -20,12 +20,14 @@ const cyan = `${ESC}[36m`;
|
|
|
20
20
|
const magenta = `${ESC}[35m`;
|
|
21
21
|
const white = `${ESC}[37m`;
|
|
22
22
|
const red = `${ESC}[31m`;
|
|
23
|
-
// Screen control -
|
|
24
|
-
const
|
|
25
|
-
const
|
|
23
|
+
// Screen control - NO alternate buffer (allows terminal scrolling)
|
|
24
|
+
const cursorHome = `${ESC}[H`; // Move cursor to top-left
|
|
25
|
+
const clearToEnd = `${ESC}[J`; // Clear from cursor to end of screen
|
|
26
26
|
const clearScreen = `${ESC}[2J${ESC}[H`; // Clear screen + cursor to home
|
|
27
27
|
const hideCursor = `${ESC}[?25l`; // Hide cursor during redraw
|
|
28
28
|
const showCursor = `${ESC}[?25h`; // Show cursor after redraw
|
|
29
|
+
const saveCursor = `${ESC}7`; // Save cursor position
|
|
30
|
+
const restoreCursor = `${ESC}8`; // Restore cursor position
|
|
29
31
|
const PHASE_COLORS = {
|
|
30
32
|
EAGLE_SIGHT: yellow,
|
|
31
33
|
BUILD: blue,
|
|
@@ -211,6 +213,35 @@ function drawUI(state, _projectPath) {
|
|
|
211
213
|
lines.push(`${cyan}╚${hLine}╝${reset}`);
|
|
212
214
|
return lines.join('\n');
|
|
213
215
|
}
|
|
216
|
+
// Help screen
|
|
217
|
+
if (state.showingHelp) {
|
|
218
|
+
lines.push(emptyRow());
|
|
219
|
+
lines.push(row(`${bold}${cyan}HELP - Keyboard Shortcuts${reset}`));
|
|
220
|
+
lines.push(emptyRow());
|
|
221
|
+
lines.push(row(`${dim}Navigation:${reset}`));
|
|
222
|
+
lines.push(row(` ${bold}[r]${reset} Analyze Re-analyze the project`));
|
|
223
|
+
lines.push(row(` ${bold}[c]${reset} Copy Copy suggested prompt to clipboard`));
|
|
224
|
+
lines.push(row(` ${bold}[n]${reset} Next Advance to next phase step`));
|
|
225
|
+
lines.push(row(` ${bold}[p]${reset} Prev Go back to previous step`));
|
|
226
|
+
lines.push(emptyRow());
|
|
227
|
+
lines.push(row(`${dim}Actions:${reset}`));
|
|
228
|
+
lines.push(row(` ${bold}[v]${reset} Paste Paste AI response for analysis`));
|
|
229
|
+
lines.push(row(` ${bold}[j]${reset} Journal Save current session to journal`));
|
|
230
|
+
lines.push(row(` ${bold}[a]${reset} Add Rules Add .cursorrules to project`));
|
|
231
|
+
lines.push(emptyRow());
|
|
232
|
+
lines.push(row(`${dim}Display:${reset}`));
|
|
233
|
+
lines.push(row(` ${bold}[b]${reset} Beginner Toggle simplified display mode`));
|
|
234
|
+
lines.push(row(` ${bold}[?]${reset} Help Show/hide this help screen`));
|
|
235
|
+
lines.push(row(` ${bold}[R]${reset} Reset Hard reset TUI state`));
|
|
236
|
+
lines.push(emptyRow());
|
|
237
|
+
lines.push(row(`${dim}Exit:${reset}`));
|
|
238
|
+
lines.push(row(` ${bold}[q]${reset} Quit Exit Midas TUI`));
|
|
239
|
+
lines.push(emptyRow());
|
|
240
|
+
lines.push(`${cyan}╠${hLine}╣${reset}`);
|
|
241
|
+
lines.push(row(`${dim}Press any key to close help${reset}`));
|
|
242
|
+
lines.push(`${cyan}╚${hLine}╝${reset}`);
|
|
243
|
+
return lines.join('\n');
|
|
244
|
+
}
|
|
214
245
|
if (state.isAnalyzing) {
|
|
215
246
|
lines.push(emptyRow());
|
|
216
247
|
lines.push(row(`${magenta}...${reset} ${bold}Analyzing project${reset}`));
|
|
@@ -338,7 +369,7 @@ function drawUI(state, _projectPath) {
|
|
|
338
369
|
}
|
|
339
370
|
lines.push(emptyRow());
|
|
340
371
|
lines.push(`${cyan}╠${hLine}╣${reset}`);
|
|
341
|
-
lines.push(row(`${dim}[c]${reset} Copy ${dim}[
|
|
372
|
+
lines.push(row(`${dim}[c]${reset} Copy ${dim}[r]${reset} Analyze ${dim}[v]${reset} Verify ${dim}[?]${reset} Help ${dim}[q]${reset} Quit`));
|
|
342
373
|
lines.push(`${cyan}╚${hLine}╝${reset}`);
|
|
343
374
|
return lines.join('\n');
|
|
344
375
|
}
|
|
@@ -346,8 +377,8 @@ export async function runInteractive() {
|
|
|
346
377
|
const projectPath = process.cwd();
|
|
347
378
|
// Check for API key on first run
|
|
348
379
|
await ensureApiKey();
|
|
349
|
-
//
|
|
350
|
-
process.stdout.write(
|
|
380
|
+
// Clear screen and save position (no alternate buffer - allows terminal scrolling)
|
|
381
|
+
process.stdout.write(saveCursor + clearScreen);
|
|
351
382
|
// Set up raw mode
|
|
352
383
|
if (process.stdin.isTTY) {
|
|
353
384
|
process.stdin.setRawMode(true);
|
|
@@ -356,7 +387,7 @@ export async function runInteractive() {
|
|
|
356
387
|
process.stdin.setEncoding('utf8');
|
|
357
388
|
// Cleanup function to restore terminal state
|
|
358
389
|
const cleanup = () => {
|
|
359
|
-
process.stdout.write(showCursor +
|
|
390
|
+
process.stdout.write(showCursor + '\n');
|
|
360
391
|
if (process.stdin.isTTY) {
|
|
361
392
|
process.stdin.setRawMode(false);
|
|
362
393
|
}
|
|
@@ -375,6 +406,14 @@ export async function runInteractive() {
|
|
|
375
406
|
console.error('Uncaught exception:', err);
|
|
376
407
|
process.exit(1);
|
|
377
408
|
});
|
|
409
|
+
process.on('unhandledRejection', (reason) => {
|
|
410
|
+
cleanup();
|
|
411
|
+
console.error('Unhandled rejection:', reason);
|
|
412
|
+
process.exit(1);
|
|
413
|
+
});
|
|
414
|
+
// Debounce tracking for rapid key presses
|
|
415
|
+
let lastKeyTime = 0;
|
|
416
|
+
const DEBOUNCE_MS = 50;
|
|
378
417
|
// Start a new session for metrics tracking
|
|
379
418
|
const currentPhase = loadState(projectPath).current;
|
|
380
419
|
const sessionId = startSession(projectPath, currentPhase);
|
|
@@ -389,6 +428,8 @@ export async function runInteractive() {
|
|
|
389
428
|
hasApiKey: hasApiKey(),
|
|
390
429
|
showingSessionStart: true, // Start with session starter prompt
|
|
391
430
|
showingRejectionInput: false,
|
|
431
|
+
showingHelp: false,
|
|
432
|
+
beginnerMode: false, // Toggle with 'b' key
|
|
392
433
|
sessionStarterPrompt: getSessionStarterPrompt(projectPath),
|
|
393
434
|
sessionId,
|
|
394
435
|
sessionStreak: metrics.currentStreak,
|
|
@@ -398,14 +439,30 @@ export async function runInteractive() {
|
|
|
398
439
|
suggestionAcceptanceRate: getSuggestionAcceptanceRate(projectPath),
|
|
399
440
|
};
|
|
400
441
|
const render = () => {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
442
|
+
try {
|
|
443
|
+
// Hide cursor, clear, draw, show cursor - prevents flicker
|
|
444
|
+
process.stdout.write(hideCursor + clearScreen);
|
|
445
|
+
process.stdout.write(drawUI(tuiState, projectPath));
|
|
446
|
+
if (tuiState.message) {
|
|
447
|
+
process.stdout.write(`\n ${tuiState.message}`);
|
|
448
|
+
tuiState.message = '';
|
|
449
|
+
}
|
|
450
|
+
process.stdout.write(showCursor);
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
// Attempt recovery
|
|
454
|
+
process.stdout.write(clearScreen + showCursor);
|
|
455
|
+
console.error('Render error:', error);
|
|
407
456
|
}
|
|
408
|
-
|
|
457
|
+
};
|
|
458
|
+
// Reset state to recover from bad state
|
|
459
|
+
const resetState = () => {
|
|
460
|
+
tuiState.analysis = null;
|
|
461
|
+
tuiState.isAnalyzing = false;
|
|
462
|
+
tuiState.showingSessionStart = false;
|
|
463
|
+
tuiState.showingRejectionInput = false;
|
|
464
|
+
tuiState.message = `${yellow}!${reset} State reset. Press [r] to re-analyze.`;
|
|
465
|
+
tuiState.filesChanged = false;
|
|
409
466
|
};
|
|
410
467
|
const runAnalysis = async () => {
|
|
411
468
|
if (!tuiState.hasApiKey) {
|
|
@@ -445,6 +502,7 @@ export async function runInteractive() {
|
|
|
445
502
|
render();
|
|
446
503
|
};
|
|
447
504
|
const promptForRejectionReason = async () => {
|
|
505
|
+
const TIMEOUT_MS = 60000; // 60 second timeout
|
|
448
506
|
return new Promise((resolve) => {
|
|
449
507
|
console.log(`\n ${yellow}Why are you declining this suggestion?${reset}`);
|
|
450
508
|
console.log(` ${dim}(This helps Midas learn. Press Enter to skip.)${reset}\n`);
|
|
@@ -455,7 +513,16 @@ export async function runInteractive() {
|
|
|
455
513
|
input: process.stdin,
|
|
456
514
|
output: process.stdout,
|
|
457
515
|
});
|
|
516
|
+
// Timeout protection
|
|
517
|
+
const timeout = setTimeout(() => {
|
|
518
|
+
rl.close();
|
|
519
|
+
if (process.stdin.isTTY) {
|
|
520
|
+
process.stdin.setRawMode(true);
|
|
521
|
+
}
|
|
522
|
+
resolve(''); // Timeout = no reason given
|
|
523
|
+
}, TIMEOUT_MS);
|
|
458
524
|
rl.question(' > ', (answer) => {
|
|
525
|
+
clearTimeout(timeout);
|
|
459
526
|
rl.close();
|
|
460
527
|
if (process.stdin.isTTY) {
|
|
461
528
|
process.stdin.setRawMode(true);
|
|
@@ -466,9 +533,10 @@ export async function runInteractive() {
|
|
|
466
533
|
};
|
|
467
534
|
// Multi-line input for pasting AI responses
|
|
468
535
|
const promptForResponse = async () => {
|
|
536
|
+
const TIMEOUT_MS = 120000; // 2 minute timeout for pasting
|
|
469
537
|
return new Promise((resolve) => {
|
|
470
|
-
//
|
|
471
|
-
process.stdout.write(
|
|
538
|
+
// Move to bottom and show input area
|
|
539
|
+
process.stdout.write('\n');
|
|
472
540
|
console.log(`\n ${cyan}━━━ Paste AI Response ━━━${reset}`);
|
|
473
541
|
console.log(` ${dim}Paste the response, then press Enter twice to submit.${reset}\n`);
|
|
474
542
|
if (process.stdin.isTTY) {
|
|
@@ -480,12 +548,24 @@ export async function runInteractive() {
|
|
|
480
548
|
});
|
|
481
549
|
const lines = [];
|
|
482
550
|
let emptyLineCount = 0;
|
|
551
|
+
let timeoutId = null;
|
|
552
|
+
// Timeout protection - auto-cancel after 2 minutes
|
|
553
|
+
timeoutId = setTimeout(() => {
|
|
554
|
+
rl.close();
|
|
555
|
+
if (process.stdin.isTTY) {
|
|
556
|
+
process.stdin.setRawMode(true);
|
|
557
|
+
}
|
|
558
|
+
process.stdout.write(clearScreen);
|
|
559
|
+
resolve(null);
|
|
560
|
+
}, TIMEOUT_MS);
|
|
483
561
|
const finishInput = () => {
|
|
562
|
+
if (timeoutId)
|
|
563
|
+
clearTimeout(timeoutId);
|
|
484
564
|
rl.close();
|
|
485
565
|
if (process.stdin.isTTY) {
|
|
486
566
|
process.stdin.setRawMode(true);
|
|
487
567
|
}
|
|
488
|
-
process.stdout.write(
|
|
568
|
+
process.stdout.write(clearScreen);
|
|
489
569
|
// Remove trailing empty lines
|
|
490
570
|
while (lines.length > 0 && lines[lines.length - 1].trim() === '') {
|
|
491
571
|
lines.pop();
|
|
@@ -542,7 +622,7 @@ export async function runInteractive() {
|
|
|
542
622
|
if (process.stdin.isTTY) {
|
|
543
623
|
process.stdin.setRawMode(true);
|
|
544
624
|
}
|
|
545
|
-
|
|
625
|
+
// Don't clear here - finishInput handles it
|
|
546
626
|
});
|
|
547
627
|
});
|
|
548
628
|
};
|
|
@@ -588,203 +668,245 @@ export async function runInteractive() {
|
|
|
588
668
|
}
|
|
589
669
|
});
|
|
590
670
|
process.stdin.on('data', async (key) => {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
// Session start screen handling
|
|
602
|
-
if (tuiState.showingSessionStart) {
|
|
603
|
-
if (key === 'c') {
|
|
604
|
-
try {
|
|
605
|
-
await copyToClipboard(tuiState.sessionStarterPrompt);
|
|
606
|
-
tuiState.message = `${green}OK${reset} Session starter copied! Paste it in your new Cursor chat.`;
|
|
607
|
-
}
|
|
608
|
-
catch {
|
|
609
|
-
tuiState.message = `${yellow}!${reset} Could not copy.`;
|
|
610
|
-
}
|
|
671
|
+
// Debounce rapid key presses
|
|
672
|
+
const now = Date.now();
|
|
673
|
+
if (now - lastKeyTime < DEBOUNCE_MS)
|
|
674
|
+
return;
|
|
675
|
+
lastKeyTime = now;
|
|
676
|
+
try {
|
|
677
|
+
// Shift+R for hard reset (recovery from bad state)
|
|
678
|
+
if (key === 'R') {
|
|
679
|
+
resetState();
|
|
611
680
|
render();
|
|
612
681
|
return;
|
|
613
682
|
}
|
|
614
|
-
if (key === '
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
683
|
+
if (key === 'q' || key === '\u0003') {
|
|
684
|
+
// End session and save metrics
|
|
685
|
+
const endPhase = tuiState.analysis?.currentPhase || { phase: 'IDLE' };
|
|
686
|
+
endSession(projectPath, tuiState.sessionId, endPhase);
|
|
687
|
+
clearInterval(activityInterval);
|
|
688
|
+
stopWatchingEvents();
|
|
689
|
+
cleanup(); // Restore terminal state
|
|
690
|
+
console.log(`\n ${cyan}Midas${reset} signing off. Session saved. Happy vibecoding!\n`);
|
|
691
|
+
process.exit(0);
|
|
621
692
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
tuiState.message = `${green}OK${reset} User Rules copied! Paste in Cursor Settings -> Rules for AI`;
|
|
626
|
-
}
|
|
627
|
-
catch {
|
|
628
|
-
tuiState.message = `${yellow}!${reset} Could not copy.`;
|
|
629
|
-
}
|
|
693
|
+
// Help screen - any key closes it
|
|
694
|
+
if (tuiState.showingHelp) {
|
|
695
|
+
tuiState.showingHelp = false;
|
|
630
696
|
render();
|
|
631
697
|
return;
|
|
632
698
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
699
|
+
// Session start screen handling
|
|
700
|
+
if (tuiState.showingSessionStart) {
|
|
701
|
+
if (key === 'c') {
|
|
702
|
+
try {
|
|
703
|
+
await copyToClipboard(tuiState.sessionStarterPrompt);
|
|
704
|
+
tuiState.message = `${green}OK${reset} Session starter copied! Paste it in your new Cursor chat.`;
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
tuiState.message = `${yellow}!${reset} Could not copy.`;
|
|
708
|
+
}
|
|
709
|
+
render();
|
|
710
|
+
return;
|
|
642
711
|
}
|
|
643
|
-
|
|
644
|
-
tuiState.
|
|
712
|
+
if (key === 'p') {
|
|
713
|
+
tuiState.showingSessionStart = false;
|
|
714
|
+
render();
|
|
715
|
+
if (tuiState.hasApiKey) {
|
|
716
|
+
await runAnalysis();
|
|
717
|
+
}
|
|
718
|
+
return;
|
|
645
719
|
}
|
|
720
|
+
if (key === 'u') {
|
|
721
|
+
try {
|
|
722
|
+
await copyUserRules();
|
|
723
|
+
tuiState.message = `${green}OK${reset} User Rules copied! Paste in Cursor Settings -> Rules for AI`;
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
tuiState.message = `${yellow}!${reset} Could not copy.`;
|
|
727
|
+
}
|
|
728
|
+
render();
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
return; // Ignore other keys on session start screen
|
|
646
732
|
}
|
|
647
|
-
|
|
648
|
-
tuiState.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
tuiState.showingRejectionInput = true;
|
|
659
|
-
const reason = await promptForRejectionReason();
|
|
660
|
-
tuiState.showingRejectionInput = false;
|
|
661
|
-
recordSuggestionOutcome(projectPath, false, undefined, reason || undefined);
|
|
662
|
-
tuiState.suggestionAcceptanceRate = getSuggestionAcceptanceRate(projectPath);
|
|
663
|
-
if (reason) {
|
|
664
|
-
tuiState.message = `${yellow}OK${reset} Noted: "${truncate(reason, 40)}" - will learn from this.`;
|
|
733
|
+
if (key === 'c') {
|
|
734
|
+
if (tuiState.analysis?.suggestedPrompt) {
|
|
735
|
+
try {
|
|
736
|
+
await copyToClipboard(tuiState.analysis.suggestedPrompt);
|
|
737
|
+
tuiState.message = `${green}OK${reset} Prompt copied to clipboard!`;
|
|
738
|
+
logEvent(projectPath, { type: 'prompt_copied', message: tuiState.analysis.suggestedPrompt.slice(0, 100) });
|
|
739
|
+
recordPromptCopied(projectPath, tuiState.sessionId);
|
|
740
|
+
}
|
|
741
|
+
catch {
|
|
742
|
+
tuiState.message = `${yellow}!${reset} Could not copy.`;
|
|
743
|
+
}
|
|
665
744
|
}
|
|
666
745
|
else {
|
|
667
|
-
tuiState.message = `${yellow}
|
|
746
|
+
tuiState.message = `${yellow}!${reset} No prompt to copy.`;
|
|
668
747
|
}
|
|
669
|
-
logEvent(projectPath, {
|
|
670
|
-
type: 'prompt_copied',
|
|
671
|
-
message: 'Suggestion declined',
|
|
672
|
-
data: { reason: reason || 'No reason given' }
|
|
673
|
-
});
|
|
674
748
|
render();
|
|
675
749
|
}
|
|
676
|
-
|
|
677
|
-
|
|
750
|
+
if (key === 'r') {
|
|
751
|
+
await runAnalysis();
|
|
752
|
+
}
|
|
753
|
+
if (key === '?') {
|
|
754
|
+
// Toggle help screen
|
|
755
|
+
tuiState.showingHelp = !tuiState.showingHelp;
|
|
678
756
|
render();
|
|
757
|
+
return;
|
|
679
758
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
759
|
+
if (key === 'b') {
|
|
760
|
+
// Toggle beginner mode (simplified display)
|
|
761
|
+
tuiState.beginnerMode = !tuiState.beginnerMode;
|
|
762
|
+
tuiState.message = tuiState.beginnerMode
|
|
763
|
+
? `${green}OK${reset} Beginner mode ON - simplified display`
|
|
764
|
+
: `${green}OK${reset} Beginner mode OFF - full display`;
|
|
765
|
+
render();
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
if (key === 'x') {
|
|
769
|
+
// Decline/reject the current suggestion
|
|
770
|
+
if (tuiState.analysis?.suggestedPrompt) {
|
|
771
|
+
tuiState.showingRejectionInput = true;
|
|
772
|
+
const reason = await promptForRejectionReason();
|
|
773
|
+
tuiState.showingRejectionInput = false;
|
|
774
|
+
recordSuggestionOutcome(projectPath, false, undefined, reason || undefined);
|
|
775
|
+
tuiState.suggestionAcceptanceRate = getSuggestionAcceptanceRate(projectPath);
|
|
776
|
+
if (reason) {
|
|
777
|
+
tuiState.message = `${yellow}OK${reset} Noted: "${truncate(reason, 40)}" - will learn from this.`;
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
tuiState.message = `${yellow}OK${reset} Suggestion declined.`;
|
|
781
|
+
}
|
|
782
|
+
logEvent(projectPath, {
|
|
783
|
+
type: 'prompt_copied',
|
|
784
|
+
message: 'Suggestion declined',
|
|
785
|
+
data: { reason: reason || 'No reason given' }
|
|
786
|
+
});
|
|
787
|
+
render();
|
|
693
788
|
}
|
|
694
789
|
else {
|
|
695
|
-
tuiState.message = `${
|
|
790
|
+
tuiState.message = `${yellow}!${reset} No suggestion to decline.`;
|
|
696
791
|
render();
|
|
697
792
|
}
|
|
698
793
|
}
|
|
699
|
-
|
|
700
|
-
|
|
794
|
+
if (key === 'v') {
|
|
795
|
+
// Run verification gates
|
|
796
|
+
tuiState.message = `${magenta}...${reset} Running verification gates (build, test, lint)...`;
|
|
701
797
|
render();
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
798
|
+
try {
|
|
799
|
+
const { verify } = await import('./tools/verify.js');
|
|
800
|
+
const result = verify({ projectPath });
|
|
801
|
+
tuiState.gatesStatus = getGatesStatus(projectPath);
|
|
802
|
+
if (result.allPass) {
|
|
803
|
+
tuiState.message = `${green}OK${reset} All gates pass!${result.autoAdvanced ? ` Auto-advanced to ${result.autoAdvanced.to}` : ''}`;
|
|
804
|
+
// Re-analyze to get new suggestions
|
|
805
|
+
await runAnalysis();
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
tuiState.message = `${red}!${reset} Failing: ${result.failing.join(', ')}`;
|
|
809
|
+
render();
|
|
810
|
+
}
|
|
713
811
|
}
|
|
714
|
-
|
|
715
|
-
tuiState.message = `${red}!${reset}
|
|
812
|
+
catch (error) {
|
|
813
|
+
tuiState.message = `${red}!${reset} Verification failed.`;
|
|
814
|
+
render();
|
|
716
815
|
}
|
|
717
816
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
tuiState.message = `${yellow}!${reset} No response pasted.`;
|
|
728
|
-
render();
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
tuiState.message = `${magenta}...${reset} Analyzing response...`;
|
|
732
|
-
render();
|
|
733
|
-
try {
|
|
734
|
-
// Analyze the pasted response
|
|
735
|
-
const analysis = await analyzeResponse(projectPath, exchange.userPrompt, exchange.aiResponse);
|
|
736
|
-
// Auto-save to journal
|
|
737
|
-
const journalTitle = analysis.summary.slice(0, 80);
|
|
738
|
-
saveToJournal({
|
|
739
|
-
projectPath,
|
|
740
|
-
title: journalTitle,
|
|
741
|
-
conversation: `USER:\n${exchange.userPrompt}\n\nAI:\n${exchange.aiResponse}`,
|
|
742
|
-
tags: analysis.taskComplete ? ['completed'] : ['in-progress'],
|
|
743
|
-
});
|
|
744
|
-
// Record any errors to error memory
|
|
745
|
-
if (analysis.errors.length > 0) {
|
|
746
|
-
const { recordError } = await import('./tracker.js');
|
|
747
|
-
for (const err of analysis.errors) {
|
|
748
|
-
recordError(projectPath, err, undefined, undefined);
|
|
817
|
+
if (key === 'a') {
|
|
818
|
+
// Add .cursorrules to project
|
|
819
|
+
try {
|
|
820
|
+
const { writeCursorRules, detectTechStack } = await import('./techstack.js');
|
|
821
|
+
const projectName = projectPath.split('/').pop() || 'project';
|
|
822
|
+
const result = writeCursorRules(projectPath, projectName);
|
|
823
|
+
if (result.success) {
|
|
824
|
+
const stack = detectTechStack(projectPath);
|
|
825
|
+
tuiState.message = `${green}OK${reset} Created .cursorrules for ${stack.language}${stack.framework ? `/${stack.framework}` : ''}`;
|
|
749
826
|
}
|
|
827
|
+
else {
|
|
828
|
+
tuiState.message = `${red}!${reset} Failed to create .cursorrules`;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
catch (error) {
|
|
832
|
+
tuiState.message = `${red}!${reset} Error creating .cursorrules`;
|
|
750
833
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
});
|
|
761
|
-
// Update analysis with new suggested prompt
|
|
762
|
-
if (tuiState.analysis) {
|
|
763
|
-
tuiState.analysis.suggestedPrompt = analysis.suggestedNextPrompt;
|
|
764
|
-
tuiState.analysis.whatsNext = analysis.suggestedNextPrompt;
|
|
834
|
+
render();
|
|
835
|
+
}
|
|
836
|
+
if (key === 'i') {
|
|
837
|
+
// Input/paste AI response for analysis
|
|
838
|
+
const exchange = await promptForResponse();
|
|
839
|
+
if (!exchange) {
|
|
840
|
+
tuiState.message = `${yellow}!${reset} No response pasted.`;
|
|
841
|
+
render();
|
|
842
|
+
return;
|
|
765
843
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
844
|
+
tuiState.message = `${magenta}...${reset} Analyzing response...`;
|
|
845
|
+
render();
|
|
846
|
+
try {
|
|
847
|
+
// Analyze the pasted response
|
|
848
|
+
const analysis = await analyzeResponse(projectPath, exchange.userPrompt, exchange.aiResponse);
|
|
849
|
+
// Auto-save to journal
|
|
850
|
+
const journalTitle = analysis.summary.slice(0, 80);
|
|
851
|
+
saveToJournal({
|
|
852
|
+
projectPath,
|
|
853
|
+
title: journalTitle,
|
|
854
|
+
conversation: `USER:\n${exchange.userPrompt}\n\nAI:\n${exchange.aiResponse}`,
|
|
855
|
+
tags: analysis.taskComplete ? ['completed'] : ['in-progress'],
|
|
856
|
+
});
|
|
857
|
+
// Record any errors to error memory
|
|
858
|
+
if (analysis.errors.length > 0) {
|
|
859
|
+
const { recordError } = await import('./tracker.js');
|
|
860
|
+
for (const err of analysis.errors) {
|
|
861
|
+
recordError(projectPath, err, undefined, undefined);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
// Log event
|
|
865
|
+
logEvent(projectPath, {
|
|
866
|
+
type: 'ai_suggestion',
|
|
867
|
+
message: `Analyzed: ${analysis.summary}`,
|
|
868
|
+
data: {
|
|
869
|
+
accomplished: analysis.accomplished.length,
|
|
870
|
+
errors: analysis.errors.length,
|
|
871
|
+
taskComplete: analysis.taskComplete,
|
|
872
|
+
},
|
|
873
|
+
});
|
|
874
|
+
// Update analysis with new suggested prompt
|
|
875
|
+
if (tuiState.analysis) {
|
|
876
|
+
tuiState.analysis.suggestedPrompt = analysis.suggestedNextPrompt;
|
|
877
|
+
tuiState.analysis.whatsNext = analysis.suggestedNextPrompt;
|
|
878
|
+
}
|
|
879
|
+
// Show summary
|
|
880
|
+
const accomplishedCount = analysis.accomplished.length;
|
|
881
|
+
const errorsCount = analysis.errors.length;
|
|
882
|
+
let statusMsg = `${green}OK${reset} Analyzed: ${analysis.summary.slice(0, 40)}`;
|
|
883
|
+
if (accomplishedCount > 0)
|
|
884
|
+
statusMsg += ` | ${green}${accomplishedCount} done${reset}`;
|
|
885
|
+
if (errorsCount > 0)
|
|
886
|
+
statusMsg += ` | ${red}${errorsCount} errors${reset}`;
|
|
887
|
+
tuiState.message = statusMsg;
|
|
888
|
+
tuiState.filesChanged = true; // Trigger refresh prompt
|
|
889
|
+
// Re-analyze if task complete or errors found
|
|
890
|
+
if (analysis.taskComplete || analysis.shouldAdvancePhase) {
|
|
891
|
+
await runAnalysis();
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
render();
|
|
895
|
+
}
|
|
779
896
|
}
|
|
780
|
-
|
|
897
|
+
catch (error) {
|
|
898
|
+
tuiState.message = `${red}!${reset} Failed to analyze response.`;
|
|
781
899
|
render();
|
|
782
900
|
}
|
|
783
901
|
}
|
|
784
|
-
|
|
785
|
-
|
|
902
|
+
}
|
|
903
|
+
catch (error) {
|
|
904
|
+
// Global error handler for key processing
|
|
905
|
+
tuiState.message = `${red}!${reset} Error processing key. Press Shift+R to reset.`;
|
|
906
|
+
try {
|
|
786
907
|
render();
|
|
787
908
|
}
|
|
909
|
+
catch { /* ignore render errors */ }
|
|
788
910
|
}
|
|
789
911
|
});
|
|
790
912
|
}
|