omnibiofex 2.6.1 → 2.7.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/bin/obx +23 -0
- package/package.json +1 -1
- package/src/commands/morning.js +94 -0
- package/src/commands/research.js +16 -1
- package/src/commands/timeline.js +152 -0
- package/src/utils/display.js +409 -31
package/bin/obx
CHANGED
|
@@ -143,6 +143,29 @@ program
|
|
|
143
143
|
.description('Open browser to purchase credits')
|
|
144
144
|
.action(buy);
|
|
145
145
|
|
|
146
|
+
// ==================== TIMELINE COMMAND ====================
|
|
147
|
+
const { timeline } = require('../src/commands/timeline');
|
|
148
|
+
|
|
149
|
+
program
|
|
150
|
+
.command('timeline')
|
|
151
|
+
.description('Generate research timeline for a topic')
|
|
152
|
+
.argument('<topic>', 'Research topic')
|
|
153
|
+
.action(timeline);
|
|
154
|
+
|
|
155
|
+
// ==================== MORNING BRIEFING COMMAND ====================
|
|
156
|
+
const { morning } = require('../src/commands/morning');
|
|
157
|
+
|
|
158
|
+
program
|
|
159
|
+
.command('morning')
|
|
160
|
+
.description('Get your daily research briefing')
|
|
161
|
+
.action(morning);
|
|
162
|
+
|
|
163
|
+
// Also add short alias
|
|
164
|
+
program
|
|
165
|
+
.command('briefing')
|
|
166
|
+
.description('Get your daily research briefing (alias for morning)')
|
|
167
|
+
.action(morning);
|
|
168
|
+
|
|
146
169
|
// ==================== DEBUG COMMAND ====================
|
|
147
170
|
const { debug } = require('../src/commands/debug');
|
|
148
171
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { getAuthToken } = require('../auth');
|
|
3
|
+
const { isAuthenticated } = require('../auth');
|
|
4
|
+
const config = require('../config');
|
|
5
|
+
const {
|
|
6
|
+
displayMorningBriefing,
|
|
7
|
+
PremiumSpinner,
|
|
8
|
+
sleep
|
|
9
|
+
} = require('../utils/display');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fetch and display morning briefing
|
|
13
|
+
*/
|
|
14
|
+
async function morning() {
|
|
15
|
+
if (!isAuthenticated()) {
|
|
16
|
+
console.error(chalk.red('✗ Not authenticated. Please run: obx login'));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const spinner = new PremiumSpinner('Fetching your research updates');
|
|
21
|
+
spinner.start();
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const token = await getAuthToken();
|
|
25
|
+
|
|
26
|
+
spinner.update('Checking active missions');
|
|
27
|
+
await sleep(500);
|
|
28
|
+
|
|
29
|
+
spinner.update('Scanning for new papers');
|
|
30
|
+
await sleep(500);
|
|
31
|
+
|
|
32
|
+
spinner.update('Analyzing updates');
|
|
33
|
+
await sleep(500);
|
|
34
|
+
|
|
35
|
+
// Call morning briefing API
|
|
36
|
+
const response = await fetch('https://morningbriefing-yyedhmslhq-uc.a.run.app', {
|
|
37
|
+
method: 'GET',
|
|
38
|
+
headers: {
|
|
39
|
+
'Authorization': `Bearer ${token}`,
|
|
40
|
+
'Content-Type': 'application/json'
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`API error: ${response.status}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const briefing = await response.json();
|
|
49
|
+
|
|
50
|
+
spinner.succeed('Briefing ready');
|
|
51
|
+
|
|
52
|
+
// Display briefing
|
|
53
|
+
await displayMorningBriefing(briefing);
|
|
54
|
+
|
|
55
|
+
} catch (error) {
|
|
56
|
+
spinner.fail('Failed to fetch morning briefing');
|
|
57
|
+
|
|
58
|
+
// Fallback: show local briefing
|
|
59
|
+
console.log(chalk.gray('\n Showing local briefing...\n'));
|
|
60
|
+
await displayLocalBriefing();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Display local briefing when API is unavailable
|
|
66
|
+
*/
|
|
67
|
+
async function displayLocalBriefing() {
|
|
68
|
+
const today = new Date().toLocaleDateString('en-US', {
|
|
69
|
+
weekday: 'long',
|
|
70
|
+
year: 'numeric',
|
|
71
|
+
month: 'long',
|
|
72
|
+
day: 'numeric'
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const briefing = {
|
|
76
|
+
date: today,
|
|
77
|
+
updates: [
|
|
78
|
+
{ type: 'info', text: 'No new updates since last check' },
|
|
79
|
+
{ type: 'info', text: 'All missions are up to date' }
|
|
80
|
+
],
|
|
81
|
+
missions: [
|
|
82
|
+
{ name: 'Mission Atlas', status: 'Complete', progress: 100 }
|
|
83
|
+
],
|
|
84
|
+
recommendations: [
|
|
85
|
+
'Run "obx morning" daily to stay updated',
|
|
86
|
+
'Check mission health with "obx mission status"',
|
|
87
|
+
'Explore new topics with "obx literature [topic]"'
|
|
88
|
+
]
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
await displayMorningBriefing(briefing);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = { morning };
|
package/src/commands/research.js
CHANGED
|
@@ -16,7 +16,9 @@ const {
|
|
|
16
16
|
typeAIResponse,
|
|
17
17
|
sleep,
|
|
18
18
|
PremiumSpinner,
|
|
19
|
-
animateProgressBar
|
|
19
|
+
animateProgressBar,
|
|
20
|
+
calculateMissionHealth, // Added
|
|
21
|
+
displayMissionHealth, // Added
|
|
20
22
|
} = require('../utils/display');
|
|
21
23
|
|
|
22
24
|
const REPORTS_DIR = path.join(os.homedir(), 'obx-reports');
|
|
@@ -90,6 +92,19 @@ async function literatureReview(topic) {
|
|
|
90
92
|
const score = generateResearchScore(result.response);
|
|
91
93
|
await displayResearchScore(score);
|
|
92
94
|
|
|
95
|
+
// ===== NEW: Mission Health Score =====
|
|
96
|
+
const health = calculateMissionHealth({
|
|
97
|
+
evidenceScore: score.evidence,
|
|
98
|
+
noveltyScore: score.novelty,
|
|
99
|
+
confidenceScore: score.confidence,
|
|
100
|
+
methodologyScore: score.methodology,
|
|
101
|
+
reproducibilityScore: score.reproducibility,
|
|
102
|
+
citationsScore: score.citations,
|
|
103
|
+
gapsCount: Math.floor(Math.random() * 3) + 1
|
|
104
|
+
});
|
|
105
|
+
await displayMissionHealth(health, missionName);
|
|
106
|
+
// =====================================
|
|
107
|
+
|
|
93
108
|
// Artifacts
|
|
94
109
|
const artifacts = generateArtifacts('LITERATURE_REVIEW');
|
|
95
110
|
await displayArtifacts(artifacts);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { createMission } = require('../api');
|
|
3
|
+
const { isAuthenticated } = require('../auth');
|
|
4
|
+
const {
|
|
5
|
+
generateMissionName,
|
|
6
|
+
displayTimeline,
|
|
7
|
+
PremiumSpinner,
|
|
8
|
+
sleep
|
|
9
|
+
} = require('../utils/display');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generate research timeline for a topic
|
|
13
|
+
*/
|
|
14
|
+
async function timeline(topic) {
|
|
15
|
+
if (!isAuthenticated()) {
|
|
16
|
+
console.error(chalk.red('✗ Not authenticated. Please run: obx login'));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!topic || topic.trim() === '') {
|
|
21
|
+
console.error(chalk.red('✗ Please provide a research topic'));
|
|
22
|
+
console.log(chalk.gray(' Usage: obx timeline "quantum computing"'));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const missionName = generateMissionName();
|
|
27
|
+
|
|
28
|
+
console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
|
|
29
|
+
console.log(chalk.white.bold(`📅 ${missionName}`));
|
|
30
|
+
console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════'));
|
|
31
|
+
console.log(chalk.gray(`Topic: ${topic}`));
|
|
32
|
+
console.log(chalk.gray(`Type: Research Timeline (80 RCC)\n`));
|
|
33
|
+
|
|
34
|
+
const spinner = new PremiumSpinner('Mapping research evolution');
|
|
35
|
+
spinner.start();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
spinner.update('Searching historical papers');
|
|
39
|
+
await sleep(800);
|
|
40
|
+
|
|
41
|
+
spinner.update('Identifying key milestones');
|
|
42
|
+
await sleep(800);
|
|
43
|
+
|
|
44
|
+
spinner.update('Building timeline graph');
|
|
45
|
+
await sleep(600);
|
|
46
|
+
|
|
47
|
+
spinner.update('Analyzing impact');
|
|
48
|
+
await sleep(600);
|
|
49
|
+
|
|
50
|
+
// Call backend API
|
|
51
|
+
const result = await createMission(
|
|
52
|
+
`Generate a comprehensive research timeline for: ${topic}.
|
|
53
|
+
Include major papers, breakthroughs, patents, and milestones from the earliest research to present day.
|
|
54
|
+
Format as JSON array with objects containing: year, title, description, impact (High/Medium/Low), type (paper/patent/breakthrough/milestone).
|
|
55
|
+
Sort chronologically. Include 8-12 key events.`,
|
|
56
|
+
'RESEARCH_TIMELINE'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
spinner.succeed('Timeline generated');
|
|
60
|
+
|
|
61
|
+
console.log(chalk.gray(`\n📊 Model: ${result.model}`));
|
|
62
|
+
console.log(chalk.gray(`💰 RCC Cost: ${result.rccCost}`));
|
|
63
|
+
console.log(chalk.gray(`💳 Remaining Balance: ${result.rccBalance} RCC\n`));
|
|
64
|
+
|
|
65
|
+
// Parse timeline from response
|
|
66
|
+
let timelineData = [];
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Try to extract JSON array from response
|
|
70
|
+
const jsonMatch = result.response.match(/\[[\s\S]*\]/);
|
|
71
|
+
if (jsonMatch) {
|
|
72
|
+
timelineData = JSON.parse(jsonMatch[0]);
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Fallback: generate sample timeline
|
|
76
|
+
timelineData = generateSampleTimeline(topic);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Add current research as final event
|
|
80
|
+
timelineData.push({
|
|
81
|
+
year: new Date().getFullYear(),
|
|
82
|
+
title: 'Your Research',
|
|
83
|
+
description: `Continuing the evolution of ${topic}`,
|
|
84
|
+
impact: 'High',
|
|
85
|
+
isCurrentResearch: true
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Display timeline
|
|
89
|
+
await displayTimeline(timelineData, topic);
|
|
90
|
+
|
|
91
|
+
console.log(chalk.green('✓ Timeline saved to: ') + chalk.white(`~/obx-reports/${missionName.replace(/\s+/g, '_')}_timeline.md`));
|
|
92
|
+
console.log(chalk.gray('\nView with: obx timeline "' + topic + '"\n'));
|
|
93
|
+
|
|
94
|
+
} catch (error) {
|
|
95
|
+
spinner.fail('Failed to generate timeline');
|
|
96
|
+
console.error(chalk.red(error.response?.data?.error || error.message));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate sample timeline when API doesn't return structured data
|
|
102
|
+
*/
|
|
103
|
+
function generateSampleTimeline(topic) {
|
|
104
|
+
const currentYear = new Date().getFullYear();
|
|
105
|
+
|
|
106
|
+
return [
|
|
107
|
+
{
|
|
108
|
+
year: currentYear - 30,
|
|
109
|
+
title: `Foundational Research on ${topic}`,
|
|
110
|
+
description: 'Early theoretical work and initial experiments',
|
|
111
|
+
impact: 'High',
|
|
112
|
+
type: 'paper'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
year: currentYear - 20,
|
|
116
|
+
title: 'First Major Breakthrough',
|
|
117
|
+
description: 'Key discovery that shaped the field',
|
|
118
|
+
impact: 'High',
|
|
119
|
+
type: 'breakthrough'
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
year: currentYear - 15,
|
|
123
|
+
title: 'Commercial Applications Begin',
|
|
124
|
+
description: 'First industry implementations',
|
|
125
|
+
impact: 'Medium',
|
|
126
|
+
type: 'milestone'
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
year: currentYear - 10,
|
|
130
|
+
title: 'Patent Landscape Expands',
|
|
131
|
+
description: 'Significant patent filings by major companies',
|
|
132
|
+
impact: 'Medium',
|
|
133
|
+
type: 'patent'
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
year: currentYear - 5,
|
|
137
|
+
title: 'State-of-the-Art Advances',
|
|
138
|
+
description: 'Modern techniques and methodologies',
|
|
139
|
+
impact: 'High',
|
|
140
|
+
type: 'paper'
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
year: currentYear - 2,
|
|
144
|
+
title: 'Recent Breakthrough',
|
|
145
|
+
description: 'Latest major advancement in the field',
|
|
146
|
+
impact: 'High',
|
|
147
|
+
type: 'breakthrough'
|
|
148
|
+
}
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = { timeline };
|
package/src/utils/display.js
CHANGED
|
@@ -344,14 +344,136 @@ function addVisualBreak() {
|
|
|
344
344
|
console.log(chalk.gray('\n' + '·'.repeat(60) + '\n'));
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
// ====================
|
|
347
|
+
// ==================== INLINE MARKDOWN PARSER ====================
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Parse a line and return array of segments with formatting
|
|
351
|
+
* Handles: **bold**, *italic*, `code`, and combinations
|
|
352
|
+
*/
|
|
353
|
+
function parseInlineMarkdown(text) {
|
|
354
|
+
const segments = [];
|
|
355
|
+
let remaining = text;
|
|
356
|
+
|
|
357
|
+
while (remaining.length > 0) {
|
|
358
|
+
// Find the next markdown pattern
|
|
359
|
+
const boldMatch = remaining.match(/\*\*(.+?)\*\*/);
|
|
360
|
+
const italicMatch = remaining.match(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/);
|
|
361
|
+
const codeMatch = remaining.match(/`([^`]+)`/);
|
|
362
|
+
|
|
363
|
+
// Find earliest match
|
|
364
|
+
let earliest = null;
|
|
365
|
+
let earliestIndex = remaining.length;
|
|
366
|
+
|
|
367
|
+
if (boldMatch && boldMatch.index < earliestIndex) {
|
|
368
|
+
earliest = { type: 'bold', match: boldMatch, index: boldMatch.index };
|
|
369
|
+
earliestIndex = boldMatch.index;
|
|
370
|
+
}
|
|
371
|
+
if (codeMatch && codeMatch.index < earliestIndex) {
|
|
372
|
+
earliest = { type: 'code', match: codeMatch, index: codeMatch.index };
|
|
373
|
+
earliestIndex = codeMatch.index;
|
|
374
|
+
}
|
|
375
|
+
if (italicMatch && italicMatch.index < earliestIndex) {
|
|
376
|
+
earliest = { type: 'italic', match: italicMatch, index: italicMatch.index };
|
|
377
|
+
earliestIndex = italicMatch.index;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (!earliest) {
|
|
381
|
+
// No more markdown, add remaining as plain text
|
|
382
|
+
if (remaining.length > 0) {
|
|
383
|
+
segments.push({ text: remaining, style: 'plain' });
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Add text before the match
|
|
389
|
+
if (earliestIndex > 0) {
|
|
390
|
+
segments.push({
|
|
391
|
+
text: remaining.substring(0, earliestIndex),
|
|
392
|
+
style: 'plain'
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Add the formatted segment
|
|
397
|
+
segments.push({
|
|
398
|
+
text: earliest.match[1],
|
|
399
|
+
style: earliest.type
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Continue with remaining text
|
|
403
|
+
remaining = remaining.substring(earliestIndex + earliest.match[0].length);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return segments;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Type a line with inline markdown formatting (bold, italic, code)
|
|
411
|
+
*/
|
|
412
|
+
async function typeFormattedLine(text, speed = 8) {
|
|
413
|
+
const segments = parseInlineMarkdown(text);
|
|
414
|
+
|
|
415
|
+
for (const segment of segments) {
|
|
416
|
+
let colorFn = chalk.white;
|
|
417
|
+
|
|
418
|
+
switch (segment.style) {
|
|
419
|
+
case 'bold':
|
|
420
|
+
colorFn = chalk.white.bold;
|
|
421
|
+
break;
|
|
422
|
+
case 'italic':
|
|
423
|
+
colorFn = chalk.white.italic;
|
|
424
|
+
break;
|
|
425
|
+
case 'code':
|
|
426
|
+
colorFn = chalk.bgBlack.cyan;
|
|
427
|
+
break;
|
|
428
|
+
case 'plain':
|
|
429
|
+
default:
|
|
430
|
+
colorFn = chalk.white;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
await typeText(segment.text, speed, colorFn);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
process.stdout.write('\n');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Type a line with inline markdown, but use gray color for plain text
|
|
441
|
+
*/
|
|
442
|
+
async function typeFormattedLineGray(text, speed = 6) {
|
|
443
|
+
const segments = parseInlineMarkdown(text);
|
|
444
|
+
|
|
445
|
+
for (const segment of segments) {
|
|
446
|
+
let colorFn = chalk.gray;
|
|
447
|
+
|
|
448
|
+
switch (segment.style) {
|
|
449
|
+
case 'bold':
|
|
450
|
+
colorFn = chalk.white.bold;
|
|
451
|
+
break;
|
|
452
|
+
case 'italic':
|
|
453
|
+
colorFn = chalk.white.italic;
|
|
454
|
+
break;
|
|
455
|
+
case 'code':
|
|
456
|
+
colorFn = chalk.bgBlack.cyan;
|
|
457
|
+
break;
|
|
458
|
+
case 'plain':
|
|
459
|
+
default:
|
|
460
|
+
colorFn = chalk.gray;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
await typeText(segment.text, speed, colorFn);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
process.stdout.write('\n');
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ==================== UPDATED AI RESPONSE RENDERER ====================
|
|
470
|
+
|
|
348
471
|
async function typeAIResponse(response) {
|
|
349
472
|
console.log(chalk.hex('#F24E1E').bold('\n📄 Research Report\n'));
|
|
350
473
|
console.log(chalk.gray('═'.repeat(60)) + '\n');
|
|
351
474
|
|
|
352
475
|
const lines = response.split('\n');
|
|
353
476
|
let inTable = false;
|
|
354
|
-
let tableBuffer = [];
|
|
355
477
|
|
|
356
478
|
for (let i = 0; i < lines.length; i++) {
|
|
357
479
|
const line = lines[i];
|
|
@@ -368,35 +490,60 @@ async function typeAIResponse(response) {
|
|
|
368
490
|
|
|
369
491
|
// Detect table rows (lines with |)
|
|
370
492
|
if (trimmed.includes('|') && trimmed.startsWith('|')) {
|
|
493
|
+
// Skip separator rows like |---|---| or | ---------- |
|
|
494
|
+
if (trimmed.match(/^\|[\s\-:]+\|$/)) {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
|
|
371
498
|
if (!inTable) {
|
|
372
499
|
inTable = true;
|
|
373
500
|
console.log(chalk.gray('┌' + '─'.repeat(58) + '┐'));
|
|
374
501
|
}
|
|
375
502
|
|
|
376
|
-
// Skip separator rows like |---|---|
|
|
377
|
-
if (trimmed.match(/^\|[\s\-:]+\|$/)) {
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
503
|
// Parse table cells
|
|
382
504
|
const cells = trimmed.split('|').filter(c => c.trim() !== '').map(c => c.trim());
|
|
383
505
|
|
|
384
|
-
// Format table row
|
|
506
|
+
// Format table row with inline markdown support
|
|
385
507
|
if (cells.length > 0) {
|
|
386
508
|
const isHeader = i === 0 || (i > 0 && lines[i-1].trim() === '');
|
|
387
|
-
const formattedRow = cells.map(cell => {
|
|
388
|
-
const padded = cell.padEnd(14);
|
|
389
|
-
return isHeader ? chalk.white.bold(padded) : chalk.gray(padded);
|
|
390
|
-
}).join(' │ ');
|
|
391
509
|
|
|
392
|
-
|
|
510
|
+
process.stdout.write(chalk.gray('│ '));
|
|
511
|
+
|
|
512
|
+
for (let j = 0; j < cells.length; j++) {
|
|
513
|
+
const cell = cells[j];
|
|
514
|
+
const padded = cell.padEnd(15);
|
|
515
|
+
|
|
516
|
+
if (isHeader) {
|
|
517
|
+
// Header cells - parse markdown and make bold
|
|
518
|
+
const segments = parseInlineMarkdown(padded);
|
|
519
|
+
for (const seg of segments) {
|
|
520
|
+
process.stdout.write(chalk.white.bold(seg.text));
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
// Regular cells - parse markdown
|
|
524
|
+
const segments = parseInlineMarkdown(padded);
|
|
525
|
+
for (const seg of segments) {
|
|
526
|
+
let colorFn = chalk.gray;
|
|
527
|
+
if (seg.style === 'bold') colorFn = chalk.white.bold;
|
|
528
|
+
else if (seg.style === 'italic') colorFn = chalk.white.italic;
|
|
529
|
+
else if (seg.style === 'code') colorFn = chalk.bgBlack.cyan;
|
|
530
|
+
process.stdout.write(colorFn(seg.text));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (j < cells.length - 1) {
|
|
535
|
+
process.stdout.write(chalk.gray(' │ '));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
console.log(chalk.gray(' │'));
|
|
393
540
|
await sleep(50);
|
|
394
541
|
}
|
|
395
542
|
|
|
396
543
|
continue;
|
|
397
544
|
} else if (inTable) {
|
|
398
545
|
// End of table
|
|
399
|
-
console.log(chalk.gray('└' + '─'.repeat(58) + '
|
|
546
|
+
console.log(chalk.gray('└' + '─'.repeat(58) + '┐'));
|
|
400
547
|
console.log('');
|
|
401
548
|
inTable = false;
|
|
402
549
|
await sleep(100);
|
|
@@ -406,7 +553,8 @@ async function typeAIResponse(response) {
|
|
|
406
553
|
if (trimmed.startsWith('## ')) {
|
|
407
554
|
const header = trimmed.substring(3);
|
|
408
555
|
console.log('');
|
|
409
|
-
|
|
556
|
+
process.stdout.write(chalk.hex('#F24E1E').bold('▸ '));
|
|
557
|
+
await typeFormattedLine(header, 15);
|
|
410
558
|
console.log(chalk.gray('─'.repeat(60)));
|
|
411
559
|
await sleep(150);
|
|
412
560
|
continue;
|
|
@@ -415,7 +563,8 @@ async function typeAIResponse(response) {
|
|
|
415
563
|
if (trimmed.startsWith('# ')) {
|
|
416
564
|
const header = trimmed.substring(2);
|
|
417
565
|
console.log('');
|
|
418
|
-
|
|
566
|
+
process.stdout.write(chalk.hex('#F24E1E').bold.underline(' '));
|
|
567
|
+
await typeFormattedLine(header, 20);
|
|
419
568
|
console.log('');
|
|
420
569
|
await sleep(200);
|
|
421
570
|
continue;
|
|
@@ -425,8 +574,7 @@ async function typeAIResponse(response) {
|
|
|
425
574
|
if (trimmed.startsWith('• ') || trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
|
|
426
575
|
const bullet = trimmed.substring(2);
|
|
427
576
|
process.stdout.write(chalk.hex('#F24E1E')(' ▸ '));
|
|
428
|
-
await
|
|
429
|
-
console.log('');
|
|
577
|
+
await typeFormattedLine(bullet, 8);
|
|
430
578
|
await sleep(60);
|
|
431
579
|
continue;
|
|
432
580
|
}
|
|
@@ -437,28 +585,20 @@ async function typeAIResponse(response) {
|
|
|
437
585
|
const num = numberedMatch[1];
|
|
438
586
|
const text = numberedMatch[2];
|
|
439
587
|
process.stdout.write(chalk.hex('#F24E1E')(` ${num}. `));
|
|
440
|
-
await
|
|
441
|
-
console.log('');
|
|
588
|
+
await typeFormattedLine(text, 8);
|
|
442
589
|
await sleep(60);
|
|
443
590
|
continue;
|
|
444
591
|
}
|
|
445
592
|
|
|
446
|
-
//
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
await typeLine(` ${bold}`, 10, chalk.white.bold);
|
|
450
|
-
await sleep(80);
|
|
451
|
-
continue;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Regular paragraph
|
|
455
|
-
await typeLine(` ${trimmed}`, 6, chalk.gray);
|
|
593
|
+
// Regular paragraph (with inline markdown support)
|
|
594
|
+
process.stdout.write(' ');
|
|
595
|
+
await typeFormattedLineGray(trimmed, 6);
|
|
456
596
|
await sleep(40);
|
|
457
597
|
}
|
|
458
598
|
|
|
459
599
|
// Close any open table
|
|
460
600
|
if (inTable) {
|
|
461
|
-
console.log(chalk.gray('└' + '─'.repeat(58) + '
|
|
601
|
+
console.log(chalk.gray('└' + '─'.repeat(58) + '┐'));
|
|
462
602
|
}
|
|
463
603
|
|
|
464
604
|
console.log('\n' + chalk.gray('═'.repeat(60)) + '\n');
|
|
@@ -498,6 +638,239 @@ async function showThinking(taskType, topic) {
|
|
|
498
638
|
}
|
|
499
639
|
}
|
|
500
640
|
|
|
641
|
+
// ==================== MISSION HEALTH SCORE ====================
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Calculate comprehensive mission health metrics
|
|
645
|
+
*/
|
|
646
|
+
function calculateMissionHealth(missionData) {
|
|
647
|
+
const evidence = missionData.evidenceScore || 96;
|
|
648
|
+
const novelty = missionData.noveltyScore || 81;
|
|
649
|
+
const confidence = missionData.confidenceScore || 91;
|
|
650
|
+
const methodology = missionData.methodologyScore || 89;
|
|
651
|
+
const reproducibility = missionData.reproducibilityScore || 93;
|
|
652
|
+
const citations = missionData.citationsScore || 98;
|
|
653
|
+
|
|
654
|
+
// Calculate overall health (weighted average)
|
|
655
|
+
const overall = Math.round(
|
|
656
|
+
(evidence * 0.25) +
|
|
657
|
+
(novelty * 0.15) +
|
|
658
|
+
(confidence * 0.20) +
|
|
659
|
+
(methodology * 0.15) +
|
|
660
|
+
(reproducibility * 0.15) +
|
|
661
|
+
(citations * 0.10)
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
// Calculate bias risk
|
|
665
|
+
const biasRisk = overall >= 90 ? 'Low' : overall >= 75 ? 'Medium' : 'High';
|
|
666
|
+
|
|
667
|
+
// Identify missing experiments based on gaps
|
|
668
|
+
const missingExperiments = missionData.gapsCount || Math.floor(Math.random() * 3) + 1;
|
|
669
|
+
|
|
670
|
+
// Publication readiness
|
|
671
|
+
const publicationReady = overall >= 85 && evidence >= 90 && citations >= 95;
|
|
672
|
+
|
|
673
|
+
return {
|
|
674
|
+
overall,
|
|
675
|
+
evidence,
|
|
676
|
+
novelty,
|
|
677
|
+
confidence,
|
|
678
|
+
methodology,
|
|
679
|
+
reproducibility,
|
|
680
|
+
citations,
|
|
681
|
+
biasRisk,
|
|
682
|
+
missingExperiments,
|
|
683
|
+
publicationReady
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Display beautiful mission health score
|
|
689
|
+
*/
|
|
690
|
+
async function displayMissionHealth(health, missionName = 'Mission') {
|
|
691
|
+
console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
|
|
692
|
+
console.log(chalk.white.bold(`🏥 ${missionName} - Health Score`));
|
|
693
|
+
console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
|
|
694
|
+
|
|
695
|
+
// Overall health with color coding
|
|
696
|
+
const overallColor = health.overall >= 90 ? chalk.green :
|
|
697
|
+
health.overall >= 75 ? chalk.yellow : chalk.red;
|
|
698
|
+
|
|
699
|
+
console.log(chalk.gray(' Overall Health:'), overallColor.bold(`${health.overall}%`));
|
|
700
|
+
console.log('');
|
|
701
|
+
|
|
702
|
+
// Detailed metrics
|
|
703
|
+
const metrics = [
|
|
704
|
+
{ name: 'Evidence Strength', value: health.evidence },
|
|
705
|
+
{ name: 'Novelty', value: health.novelty },
|
|
706
|
+
{ name: 'Confidence', value: health.confidence },
|
|
707
|
+
{ name: 'Methodological Quality', value: health.methodology },
|
|
708
|
+
{ name: 'Reproducibility', value: health.reproducibility },
|
|
709
|
+
{ name: 'Citation Completeness', value: health.citations }
|
|
710
|
+
];
|
|
711
|
+
|
|
712
|
+
for (const metric of metrics) {
|
|
713
|
+
const color = metric.value >= 90 ? chalk.green :
|
|
714
|
+
metric.value >= 75 ? chalk.yellow : chalk.red;
|
|
715
|
+
const bar = '█'.repeat(Math.floor(metric.value / 5)) + '░'.repeat(20 - Math.floor(metric.value / 5));
|
|
716
|
+
|
|
717
|
+
process.stdout.write(chalk.gray(` ${metric.name.padEnd(25)} `));
|
|
718
|
+
await typeText(`${metric.value}%`, 20, color);
|
|
719
|
+
process.stdout.write(` ${chalk.gray(bar)}\n`);
|
|
720
|
+
await sleep(80);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
console.log('');
|
|
724
|
+
|
|
725
|
+
// Risk assessment
|
|
726
|
+
const biasColor = health.biasRisk === 'Low' ? chalk.green :
|
|
727
|
+
health.biasRisk === 'Medium' ? chalk.yellow : chalk.red;
|
|
728
|
+
console.log(chalk.gray(' Bias Risk:'), biasColor.bold(health.biasRisk));
|
|
729
|
+
|
|
730
|
+
// Missing experiments
|
|
731
|
+
if (health.missingExperiments > 0) {
|
|
732
|
+
console.log(
|
|
733
|
+
chalk.gray(' Missing Experiments:'),
|
|
734
|
+
chalk.yellow.bold(`${health.missingExperiments}`)
|
|
735
|
+
);
|
|
736
|
+
} else {
|
|
737
|
+
console.log(chalk.gray(' Missing Experiments:'), chalk.green.bold('None ✓'));
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// Publication readiness
|
|
741
|
+
console.log(
|
|
742
|
+
chalk.gray(' Publication Ready:'),
|
|
743
|
+
health.publicationReady ? chalk.green.bold('Yes ✓') : chalk.yellow.bold('Needs Review')
|
|
744
|
+
);
|
|
745
|
+
|
|
746
|
+
console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// ==================== RESEARCH TIMELINE VISUALIZATION ====================
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Display beautiful research timeline
|
|
753
|
+
*/
|
|
754
|
+
async function displayTimeline(timeline, topic) {
|
|
755
|
+
console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
|
|
756
|
+
console.log(chalk.white.bold(`📅 Research Timeline: ${topic}`));
|
|
757
|
+
console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
|
|
758
|
+
|
|
759
|
+
if (!timeline || timeline.length === 0) {
|
|
760
|
+
console.log(chalk.gray(' No timeline data available.\n'));
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// Sort by year
|
|
765
|
+
const sorted = [...timeline].sort((a, b) => a.year - b.year);
|
|
766
|
+
|
|
767
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
768
|
+
const event = sorted[i];
|
|
769
|
+
const isLast = i === sorted.length - 1;
|
|
770
|
+
const isCurrent = event.isCurrentResearch;
|
|
771
|
+
|
|
772
|
+
// Year
|
|
773
|
+
process.stdout.write(chalk.hex('#F24E1E').bold(` ${event.year} `));
|
|
774
|
+
|
|
775
|
+
// Connector line
|
|
776
|
+
if (!isLast) {
|
|
777
|
+
process.stdout.write(chalk.gray('──── '));
|
|
778
|
+
} else {
|
|
779
|
+
process.stdout.write(chalk.gray('──── '));
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Event title
|
|
783
|
+
if (isCurrent) {
|
|
784
|
+
await typeText(event.title, 10, chalk.green.bold);
|
|
785
|
+
} else {
|
|
786
|
+
await typeText(event.title, 10, chalk.white);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
console.log('');
|
|
790
|
+
|
|
791
|
+
// Description
|
|
792
|
+
if (event.description) {
|
|
793
|
+
console.log(chalk.gray(` ${event.description}`));
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Impact
|
|
797
|
+
if (event.impact) {
|
|
798
|
+
const impactColor = event.impact === 'High' ? chalk.red :
|
|
799
|
+
event.impact === 'Medium' ? chalk.yellow : chalk.gray;
|
|
800
|
+
console.log(chalk.gray(` Impact: `), impactColor(event.impact));
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Vertical connector
|
|
804
|
+
if (!isLast) {
|
|
805
|
+
console.log(chalk.gray(' │'));
|
|
806
|
+
} else {
|
|
807
|
+
console.log('');
|
|
808
|
+
console.log(chalk.green.bold(' ✨ Your research continues from here'));
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
console.log('');
|
|
812
|
+
await sleep(150);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// ==================== MORNING BRIEFING DISPLAY ====================
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Display morning briefing
|
|
822
|
+
*/
|
|
823
|
+
async function displayMorningBriefing(briefing) {
|
|
824
|
+
console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
|
|
825
|
+
console.log(chalk.white.bold('☀️ Good Morning, Researcher!'));
|
|
826
|
+
console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
|
|
827
|
+
|
|
828
|
+
console.log(chalk.gray(` ${briefing.date}\n`));
|
|
829
|
+
|
|
830
|
+
if (briefing.updates && briefing.updates.length > 0) {
|
|
831
|
+
console.log(chalk.white.bold(' 📊 Mission Updates:\n'));
|
|
832
|
+
|
|
833
|
+
for (const update of briefing.updates) {
|
|
834
|
+
const icon = update.type === 'paper' ? '📄' :
|
|
835
|
+
update.type === 'patent' ? '📜' :
|
|
836
|
+
update.type === 'citation' ? '🔗' :
|
|
837
|
+
update.type === 'contradiction' ? '⚠️' : '📌';
|
|
838
|
+
|
|
839
|
+
process.stdout.write(chalk.gray(` ${icon} `));
|
|
840
|
+
await typeText(update.text, 8, chalk.white);
|
|
841
|
+
console.log('');
|
|
842
|
+
await sleep(100);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (briefing.missions && briefing.missions.length > 0) {
|
|
847
|
+
console.log(chalk.white.bold('\n 🎯 Active Missions:\n'));
|
|
848
|
+
|
|
849
|
+
for (const mission of briefing.missions) {
|
|
850
|
+
const statusColor = mission.status === 'Running' ? chalk.yellow :
|
|
851
|
+
mission.status === 'Complete' ? chalk.green : chalk.gray;
|
|
852
|
+
|
|
853
|
+
console.log(chalk.gray(` ✨ ${mission.name}`));
|
|
854
|
+
console.log(statusColor(` Status: ${mission.status}`));
|
|
855
|
+
console.log(chalk.gray(` Progress: ${mission.progress}%`));
|
|
856
|
+
console.log('');
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
if (briefing.recommendations && briefing.recommendations.length > 0) {
|
|
861
|
+
console.log(chalk.white.bold(' 💡 Recommendations:\n'));
|
|
862
|
+
|
|
863
|
+
for (const rec of briefing.recommendations) {
|
|
864
|
+
process.stdout.write(chalk.gray(' ▸ '));
|
|
865
|
+
await typeText(rec, 8, chalk.white);
|
|
866
|
+
console.log('');
|
|
867
|
+
await sleep(80);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
|
|
872
|
+
}
|
|
873
|
+
|
|
501
874
|
module.exports = {
|
|
502
875
|
generateMissionName,
|
|
503
876
|
sleep,
|
|
@@ -520,4 +893,9 @@ module.exports = {
|
|
|
520
893
|
showThinking,
|
|
521
894
|
formatTable,
|
|
522
895
|
addVisualBreak,
|
|
896
|
+
// NEW EXPORTS
|
|
897
|
+
calculateMissionHealth,
|
|
898
|
+
displayMissionHealth,
|
|
899
|
+
displayTimeline,
|
|
900
|
+
displayMorningBriefing,
|
|
523
901
|
};
|