omnibiofex 4.0.0 ā 4.2.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 +15 -0
- package/package.json +1 -1
- package/src/commands/diagram.js +305 -0
package/bin/obx
CHANGED
|
@@ -46,6 +46,21 @@ program.command('hypothesis').description('Generate hypotheses').argument('<topi
|
|
|
46
46
|
const { timeline } = require('../src/commands/timeline');
|
|
47
47
|
program.command('timeline').description('Research timeline').argument('<topic>').action(timeline);
|
|
48
48
|
|
|
49
|
+
// ==================== DIAGRAM COMMAND ====================
|
|
50
|
+
const { diagram } = require('../src/commands/diagram');
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.command('diagram')
|
|
54
|
+
.description('š Generate ASCII research ecosystem diagrams')
|
|
55
|
+
.argument('<topic>', 'Research topic')
|
|
56
|
+
.option('--style <style>', 'Diagram style: minimal, detailed, flowchart, network', 'detailed')
|
|
57
|
+
.option('--save', 'Save diagram to file')
|
|
58
|
+
.option('--publish', 'Save and prepare for publishing')
|
|
59
|
+
.action(async (topic, options) => {
|
|
60
|
+
await diagram(topic, options);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// ==================== DATA COMMANDS ====================
|
|
49
64
|
const { dataset, code } = require('../src/commands/data');
|
|
50
65
|
program.command('dataset').description('Analyze dataset').argument('<file>').action(dataset);
|
|
51
66
|
program.command('code').description('Review code').argument('[directory]', '.').action(code);
|
package/package.json
CHANGED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { getAuthToken, isAuthenticated } = require('../auth');
|
|
6
|
+
const { PremiumSpinner, sleep } = require('../utils/display');
|
|
7
|
+
|
|
8
|
+
const BACKEND_URL = 'https://obxvisionassistant-yyedhmslhq-uc.a.run.app';
|
|
9
|
+
const DIAGRAMS_DIR = path.join(os.homedir(), 'obx-diagrams');
|
|
10
|
+
|
|
11
|
+
// Ensure diagrams directory exists
|
|
12
|
+
if (!fs.existsSync(DIAGRAMS_DIR)) {
|
|
13
|
+
fs.mkdirSync(DIAGRAMS_DIR, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Diagram styles with costs
|
|
17
|
+
const STYLES = {
|
|
18
|
+
minimal: { name: 'Minimal', cost: 50, description: 'Clean, simple flowchart' },
|
|
19
|
+
detailed: { name: 'Detailed', cost: 80, description: 'Comprehensive ecosystem' },
|
|
20
|
+
flowchart: { name: 'Flowchart', cost: 60, description: 'Step-by-step process' },
|
|
21
|
+
network: { name: 'Network', cost: 70, description: 'Interconnected nodes' }
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Generate ASCII banner
|
|
25
|
+
function banner(title, width = 80) {
|
|
26
|
+
const top = `ā${'ā'.repeat(width)}ā`;
|
|
27
|
+
const bottom = `ā${'ā'.repeat(width)}ā`;
|
|
28
|
+
const padding = Math.max(0, width - title.length - 2);
|
|
29
|
+
const leftPad = Math.floor(padding / 2);
|
|
30
|
+
const rightPad = padding - leftPad;
|
|
31
|
+
const content = `ā ${' '.repeat(leftPad)}${title}${' '.repeat(rightPad)} ā`;
|
|
32
|
+
return `${top}\n${content}\n${bottom}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Generate node box
|
|
36
|
+
function node(text, width = 20) {
|
|
37
|
+
const safeText = text.substring(0, width - 2);
|
|
38
|
+
const padding = Math.max(0, width - safeText.length - 2);
|
|
39
|
+
const leftPad = Math.floor(padding / 2);
|
|
40
|
+
const rightPad = padding - leftPad;
|
|
41
|
+
const top = `ā${'ā'.repeat(width)}ā`;
|
|
42
|
+
const bottom = `ā${'ā'.repeat(width)}ā`;
|
|
43
|
+
const content = `ā ${' '.repeat(leftPad)}${safeText}${' '.repeat(rightPad)} ā`;
|
|
44
|
+
return `${top}\n${content}\n${bottom}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Generate arrow
|
|
48
|
+
function arrow(direction = 'down') {
|
|
49
|
+
switch (direction) {
|
|
50
|
+
case 'down': return 'ā\nā¼';
|
|
51
|
+
case 'right': return 'āāāā¶';
|
|
52
|
+
case 'left': return 'āāāā';
|
|
53
|
+
case 'up': return 'ā²\nā';
|
|
54
|
+
default: return 'ā\nā¼';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Generate detailed diagram
|
|
59
|
+
function generateDetailedDiagram(topic, data) {
|
|
60
|
+
const width = 80;
|
|
61
|
+
let diagram = banner(`${topic.toUpperCase()} RESEARCH ECOSYSTEM`, width) + '\n\n';
|
|
62
|
+
|
|
63
|
+
// Problem identification
|
|
64
|
+
diagram += `${' '.repeat(25)}${node('Research Problem', 25)}\n`;
|
|
65
|
+
diagram += `${' '.repeat(37)}${arrow('down')}\n\n`;
|
|
66
|
+
|
|
67
|
+
// Three input sources
|
|
68
|
+
const sources = data.components?.slice(0, 3) || ['Literature', 'Data', 'Theory'];
|
|
69
|
+
diagram += `${' '.repeat(5)}${node(sources[0] || 'Literature', 18)} ${node(sources[1] || 'Data', 18)} ${node(sources[2] || 'Theory', 18)}\n`;
|
|
70
|
+
diagram += `${' '.repeat(14)}\\ | /\n`;
|
|
71
|
+
diagram += `${' '.repeat(15)}\\ | /\n`;
|
|
72
|
+
diagram += `${' '.repeat(16)}\\ | /\n`;
|
|
73
|
+
diagram += `${' '.repeat(17)}ā¼ ā¼ ā¼\n`;
|
|
74
|
+
diagram += `${' '.repeat(15)}${node('Integration Layer', 30)}\n`;
|
|
75
|
+
diagram += `${' '.repeat(29)}${arrow('down')}\n`;
|
|
76
|
+
|
|
77
|
+
// Analysis phase
|
|
78
|
+
diagram += `${' '.repeat(20)}${node('Analysis Phase', 25)}\n`;
|
|
79
|
+
diagram += `${' '.repeat(32)}${arrow('down')}\n\n`;
|
|
80
|
+
|
|
81
|
+
// Four analysis types
|
|
82
|
+
const methods = data.methods?.slice(0, 4) || ['Statistical', 'Qualitative', 'Comparative', 'Predictive'];
|
|
83
|
+
diagram += `${node(methods[0], 14)} ${node(methods[1], 14)} ${node(methods[2], 14)} ${node(methods[3], 14)}\n`;
|
|
84
|
+
diagram += `${' '.repeat(7)}\\ | | /\n`;
|
|
85
|
+
diagram += `${' '.repeat(8)}\\ | | /\n`;
|
|
86
|
+
diagram += `${' '.repeat(9)}\\ | | /\n`;
|
|
87
|
+
diagram += `${' '.repeat(10)}ā¼ ā¼ ā¼ ā¼\n`;
|
|
88
|
+
diagram += `${' '.repeat(15)}${node('Synthesis & Insights', 35)}\n`;
|
|
89
|
+
diagram += `${' '.repeat(32)}${arrow('down')}\n`;
|
|
90
|
+
|
|
91
|
+
// Output phase
|
|
92
|
+
diagram += `${' '.repeat(20)}${node('Output Phase', 25)}\n`;
|
|
93
|
+
diagram += `${' '.repeat(32)}${arrow('down')}\n\n`;
|
|
94
|
+
|
|
95
|
+
// Three outputs
|
|
96
|
+
const outputs = data.outputs?.slice(0, 3) || ['Publication', 'Implementation', 'Future Work'];
|
|
97
|
+
diagram += `${node(outputs[0], 18)} ${node(outputs[1], 18)} ${node(outputs[2], 18)}\n`;
|
|
98
|
+
|
|
99
|
+
return diagram;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Generate minimal diagram
|
|
103
|
+
function generateMinimalDiagram(topic, data) {
|
|
104
|
+
const width = 60;
|
|
105
|
+
let diagram = banner(topic.toUpperCase(), width) + '\n\n';
|
|
106
|
+
|
|
107
|
+
diagram += `${' '.repeat(20)}${node('Problem', 20)}\n`;
|
|
108
|
+
diagram += `${' '.repeat(29)}${arrow('down')}\n`;
|
|
109
|
+
diagram += `${' '.repeat(10)}${node('Research', 20)}\n`;
|
|
110
|
+
diagram += `${' '.repeat(19)}${arrow('down')}\n`;
|
|
111
|
+
diagram += `${' '.repeat(10)}${node('Analysis', 20)}\n`;
|
|
112
|
+
diagram += `${' '.repeat(19)}${arrow('down')}\n`;
|
|
113
|
+
diagram += `${' '.repeat(10)}${node('Solution', 20)}\n`;
|
|
114
|
+
|
|
115
|
+
return diagram;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Generate flowchart diagram
|
|
119
|
+
function generateFlowchartDiagram(topic, data) {
|
|
120
|
+
const width = 60;
|
|
121
|
+
const steps = data.steps?.length > 0 ? data.steps : ['Research', 'Analysis', 'Implementation'];
|
|
122
|
+
let diagram = banner(`${topic.toUpperCase()} WORKFLOW`, width) + '\n\n';
|
|
123
|
+
|
|
124
|
+
steps.forEach((step, i) => {
|
|
125
|
+
const stepText = `Step ${i + 1}: ${step}`;
|
|
126
|
+
diagram += `${' '.repeat(15)}${node(stepText, 45)}\n`;
|
|
127
|
+
if (i < steps.length - 1) {
|
|
128
|
+
diagram += `${' '.repeat(37)}${arrow('down')}\n`;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
diagram += `\n${' '.repeat(25)}${node('ā Complete', 20)}\n`;
|
|
133
|
+
return diagram;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Generate network diagram
|
|
137
|
+
function generateNetworkDiagram(topic, data) {
|
|
138
|
+
const width = 70;
|
|
139
|
+
const nodes = data.nodes?.slice(0, 3) || ['Component A', 'Component B', 'Component C'];
|
|
140
|
+
let diagram = banner(`${topic.toUpperCase()} NETWORK`, width) + '\n\n';
|
|
141
|
+
|
|
142
|
+
diagram += `${' '.repeat(25)}${node(topic, 25)}\n`;
|
|
143
|
+
diagram += `${' '.repeat(37)}|\n`;
|
|
144
|
+
diagram += `${' '.repeat(20)}āāāāāāāāāā¼āāāāāāāāā\n`;
|
|
145
|
+
diagram += `${' '.repeat(20)}ā ā ā\n`;
|
|
146
|
+
diagram += `${' '.repeat(20)}ā¼ ā¼ ā¼\n`;
|
|
147
|
+
diagram += `${node(nodes[0], 18)} ${node(nodes[1], 18)} ${node(nodes[2], 18)}\n`;
|
|
148
|
+
diagram += `${' '.repeat(9)}ā ā ā\n`;
|
|
149
|
+
diagram += `${' '.repeat(9)}āāāāāāāāāā¼āāāāāāāāā\n`;
|
|
150
|
+
diagram += `${' '.repeat(18)}ā¼\n`;
|
|
151
|
+
diagram += `${' '.repeat(15)}${node('Integration', 25)}\n`;
|
|
152
|
+
|
|
153
|
+
return diagram;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Main diagram command
|
|
157
|
+
async function diagram(topic, options = {}) {
|
|
158
|
+
if (!isAuthenticated()) {
|
|
159
|
+
console.error(chalk.red('ā Not authenticated. Please run: obx login'));
|
|
160
|
+
process.exit(1);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!topic) {
|
|
165
|
+
console.error(chalk.red('ā Please provide a topic'));
|
|
166
|
+
console.log(chalk.gray('Usage: obx diagram "topic" [--style minimal|detailed|flowchart|network] [--save]'));
|
|
167
|
+
process.exit(1);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const style = options.style || 'detailed';
|
|
172
|
+
const save = options.save || false;
|
|
173
|
+
const cost = STYLES[style]?.cost || 80;
|
|
174
|
+
|
|
175
|
+
console.log(chalk.hex('#F24E1E')('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
176
|
+
console.log(chalk.white.bold('š ASCII DIAGRAM GENERATOR'));
|
|
177
|
+
console.log(chalk.hex('#F24E1E')('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'));
|
|
178
|
+
|
|
179
|
+
console.log(chalk.white(` š Topic: ${chalk.hex('#F24E1E')(topic)}`));
|
|
180
|
+
console.log(chalk.white(` šØ Style: ${chalk.hex('#F24E1E')(STYLES[style]?.name || 'Detailed')}`));
|
|
181
|
+
console.log(chalk.white(` š° Cost: ${chalk.green(cost + ' RCC')}`));
|
|
182
|
+
|
|
183
|
+
const spinner = new PremiumSpinner('Generating diagram');
|
|
184
|
+
spinner.start();
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const token = await getAuthToken();
|
|
188
|
+
|
|
189
|
+
spinner.update('Analyzing topic with AI');
|
|
190
|
+
await sleep(500);
|
|
191
|
+
|
|
192
|
+
// ā
FIX: Use DEEP_RESEARCH task type (already supported by backend)
|
|
193
|
+
const response = await fetch(BACKEND_URL, {
|
|
194
|
+
method: 'POST',
|
|
195
|
+
headers: {
|
|
196
|
+
'Content-Type': 'application/json',
|
|
197
|
+
'Authorization': `Bearer ${token}`
|
|
198
|
+
},
|
|
199
|
+
body: JSON.stringify({
|
|
200
|
+
taskType: 'DEEP_RESEARCH', // ā Use existing task type
|
|
201
|
+
message: `Generate a structured breakdown of "${topic}" for an ASCII diagram.
|
|
202
|
+
|
|
203
|
+
Return ONLY a valid JSON object with this exact structure (no other text):
|
|
204
|
+
{
|
|
205
|
+
"components": ["component1", "component2", "component3", "component4"],
|
|
206
|
+
"methods": ["method1", "method2", "method3", "method4"],
|
|
207
|
+
"steps": ["step1", "step2", "step3", "step4", "step5"],
|
|
208
|
+
"nodes": ["node1", "node2", "node3"],
|
|
209
|
+
"outputs": ["output1", "output2", "output3"]
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
Make the components, methods, steps, nodes, and outputs specific to "${topic}".
|
|
213
|
+
Return ONLY the JSON object, nothing else.`,
|
|
214
|
+
model: 'deep'
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (!response.ok) {
|
|
219
|
+
const errorText = await response.text();
|
|
220
|
+
throw new Error(`Backend returned ${response.status}: ${errorText.substring(0, 100)}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
spinner.update('Rendering ASCII art');
|
|
224
|
+
await sleep(500);
|
|
225
|
+
|
|
226
|
+
const data = await response.json();
|
|
227
|
+
|
|
228
|
+
// Parse AI response - extract JSON from the response
|
|
229
|
+
let aiData = {
|
|
230
|
+
components: [],
|
|
231
|
+
methods: [],
|
|
232
|
+
steps: [],
|
|
233
|
+
nodes: [],
|
|
234
|
+
outputs: []
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
// Try to find JSON in the response
|
|
239
|
+
const responseText = data.response || '';
|
|
240
|
+
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
|
|
241
|
+
|
|
242
|
+
if (jsonMatch) {
|
|
243
|
+
aiData = JSON.parse(jsonMatch[0]);
|
|
244
|
+
}
|
|
245
|
+
} catch (e) {
|
|
246
|
+
console.log(chalk.yellow(' ā Using default structure'));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Generate diagram based on style
|
|
250
|
+
let asciiDiagram;
|
|
251
|
+
switch (style) {
|
|
252
|
+
case 'minimal':
|
|
253
|
+
asciiDiagram = generateMinimalDiagram(topic, aiData);
|
|
254
|
+
break;
|
|
255
|
+
case 'flowchart':
|
|
256
|
+
asciiDiagram = generateFlowchartDiagram(topic, aiData);
|
|
257
|
+
break;
|
|
258
|
+
case 'network':
|
|
259
|
+
asciiDiagram = generateNetworkDiagram(topic, aiData);
|
|
260
|
+
break;
|
|
261
|
+
case 'detailed':
|
|
262
|
+
default:
|
|
263
|
+
asciiDiagram = generateDetailedDiagram(topic, aiData);
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
spinner.succeed('ā Diagram generated');
|
|
268
|
+
|
|
269
|
+
// Display diagram
|
|
270
|
+
console.log('\n' + chalk.cyan(asciiDiagram) + '\n');
|
|
271
|
+
|
|
272
|
+
// Save if requested
|
|
273
|
+
if (save) {
|
|
274
|
+
const timestamp = Date.now();
|
|
275
|
+
const safeTopic = topic.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 50);
|
|
276
|
+
|
|
277
|
+
// Save ASCII version
|
|
278
|
+
const txtFilename = `${safeTopic}-${timestamp}.txt`;
|
|
279
|
+
const txtPath = path.join(DIAGRAMS_DIR, txtFilename);
|
|
280
|
+
fs.writeFileSync(txtPath, asciiDiagram, 'utf8');
|
|
281
|
+
console.log(chalk.green(` š¾ Saved: ${txtPath}`));
|
|
282
|
+
|
|
283
|
+
// Save Markdown version
|
|
284
|
+
const mdFilename = `${safeTopic}-${timestamp}.md`;
|
|
285
|
+
const mdPath = path.join(DIAGRAMS_DIR, mdFilename);
|
|
286
|
+
const mdContent = `# ${topic} Research Diagram\n\n**Style:** ${STYLES[style].name}\n**Generated:** ${new Date().toLocaleString()}\n\n\`\`\`\n${asciiDiagram}\n\`\`\`\n`;
|
|
287
|
+
fs.writeFileSync(mdPath, mdContent, 'utf8');
|
|
288
|
+
console.log(chalk.green(` š¾ Saved: ${mdPath}`));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
console.log(chalk.hex('#F24E1E')('\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'));
|
|
292
|
+
|
|
293
|
+
} catch (error) {
|
|
294
|
+
spinner.fail('Failed to generate diagram');
|
|
295
|
+
console.error(chalk.red(error.message));
|
|
296
|
+
|
|
297
|
+
if (error.message.includes('402') || error.message.includes('Insufficient')) {
|
|
298
|
+
console.log(chalk.yellow('\n š” You need more RCC credits. Run: obx buy'));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
module.exports = { diagram, STYLES };
|