ultra-dex 3.1.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.
Files changed (52) hide show
  1. package/README.md +79 -74
  2. package/assets/code-patterns/clerk-middleware.ts +138 -0
  3. package/assets/code-patterns/prisma-schema.prisma +224 -0
  4. package/assets/code-patterns/rls-policies.sql +246 -0
  5. package/assets/code-patterns/server-actions.ts +191 -0
  6. package/assets/code-patterns/trpc-router.ts +258 -0
  7. package/assets/cursor-rules/13-ai-integration.mdc +155 -0
  8. package/assets/cursor-rules/14-server-components.mdc +81 -0
  9. package/assets/cursor-rules/15-server-actions.mdc +102 -0
  10. package/assets/cursor-rules/16-edge-middleware.mdc +105 -0
  11. package/assets/cursor-rules/17-streaming-ssr.mdc +138 -0
  12. package/bin/ultra-dex.js +50 -1
  13. package/lib/commands/agents.js +16 -13
  14. package/lib/commands/banner.js +43 -21
  15. package/lib/commands/build.js +26 -17
  16. package/lib/commands/cloud.js +780 -0
  17. package/lib/commands/doctor.js +98 -79
  18. package/lib/commands/exec.js +434 -0
  19. package/lib/commands/generate.js +19 -16
  20. package/lib/commands/github.js +475 -0
  21. package/lib/commands/init.js +52 -56
  22. package/lib/commands/scaffold.js +151 -0
  23. package/lib/commands/search.js +477 -0
  24. package/lib/commands/serve.js +15 -13
  25. package/lib/commands/state.js +43 -70
  26. package/lib/commands/swarm.js +31 -9
  27. package/lib/config/theme.js +47 -0
  28. package/lib/mcp/client.js +502 -0
  29. package/lib/providers/agent-sdk.js +630 -0
  30. package/lib/providers/anthropic-agents.js +580 -0
  31. package/lib/templates/code/clerk-middleware.ts +138 -0
  32. package/lib/templates/code/prisma-schema.prisma +224 -0
  33. package/lib/templates/code/rls-policies.sql +246 -0
  34. package/lib/templates/code/server-actions.ts +191 -0
  35. package/lib/templates/code/trpc-router.ts +258 -0
  36. package/lib/themes/doomsday.js +229 -0
  37. package/lib/ui/index.js +5 -0
  38. package/lib/ui/interface.js +241 -0
  39. package/lib/ui/spinners.js +116 -0
  40. package/lib/ui/theme.js +183 -0
  41. package/lib/utils/agents.js +32 -0
  42. package/lib/utils/browser.js +373 -0
  43. package/lib/utils/help.js +64 -0
  44. package/lib/utils/messages.js +35 -0
  45. package/lib/utils/progress.js +24 -0
  46. package/lib/utils/prompts.js +47 -0
  47. package/lib/utils/spinners.js +46 -0
  48. package/lib/utils/status.js +31 -0
  49. package/lib/utils/tables.js +41 -0
  50. package/lib/utils/theme-state.js +9 -0
  51. package/lib/utils/version-display.js +32 -0
  52. package/package.json +19 -4
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Browser Automation Module (Playwright-based)
3
+ * Enables Research agent to browse the web, take screenshots, and interact with pages
4
+ * This is what makes Ultra-Dex truly intelligent - it can LEARN from the web
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+
11
+ // ============================================================================
12
+ // BROWSER CONFIGURATION
13
+ // ============================================================================
14
+
15
+ const BROWSER_CONFIG = {
16
+ // Default browser settings
17
+ headless: true,
18
+ timeout: 30000,
19
+ viewport: { width: 1280, height: 720 },
20
+
21
+ // Screenshot settings
22
+ screenshotDir: '.ultra-dex/screenshots',
23
+
24
+ // User agent
25
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Ultra-Dex/3.2.0 Research Agent',
26
+
27
+ // Blocked resources (for faster loading)
28
+ blockedResources: ['image', 'stylesheet', 'font', 'media'],
29
+ };
30
+
31
+ // ============================================================================
32
+ // BROWSER AUTOMATION CLASS
33
+ // ============================================================================
34
+
35
+ /**
36
+ * Browser automation for Research agent
37
+ * Uses Playwright for reliable cross-browser automation
38
+ */
39
+ export class BrowserAutomation {
40
+ constructor(options = {}) {
41
+ this.options = { ...BROWSER_CONFIG, ...options };
42
+ this.browser = null;
43
+ this.context = null;
44
+ this.page = null;
45
+ this.playwright = null;
46
+ }
47
+
48
+ /**
49
+ * Launch browser
50
+ */
51
+ async launch() {
52
+ try {
53
+ // Dynamic import of playwright
54
+ const { chromium } = await import('playwright');
55
+ this.playwright = { chromium };
56
+
57
+ this.browser = await chromium.launch({
58
+ headless: this.options.headless,
59
+ });
60
+
61
+ this.context = await this.browser.newContext({
62
+ userAgent: this.options.userAgent,
63
+ viewport: this.options.viewport,
64
+ });
65
+
66
+ // Block unnecessary resources for faster loading
67
+ if (this.options.blockedResources.length > 0) {
68
+ await this.context.route('**/*', (route) => {
69
+ if (this.options.blockedResources.includes(route.request().resourceType())) {
70
+ route.abort();
71
+ } else {
72
+ route.continue();
73
+ }
74
+ });
75
+ }
76
+
77
+ this.page = await this.context.newPage();
78
+ this.page.setDefaultTimeout(this.options.timeout);
79
+
80
+ return true;
81
+ } catch (err) {
82
+ console.log(chalk.yellow(`Browser launch failed: ${err.message}`));
83
+ console.log(chalk.gray('Install Playwright: npm install playwright'));
84
+ return false;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Close browser
90
+ */
91
+ async close() {
92
+ if (this.browser) {
93
+ await this.browser.close();
94
+ this.browser = null;
95
+ this.context = null;
96
+ this.page = null;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Navigate to URL
102
+ */
103
+ async navigate(url) {
104
+ if (!this.page) {
105
+ await this.launch();
106
+ }
107
+
108
+ await this.page.goto(url, { waitUntil: 'domcontentloaded' });
109
+ return { url: this.page.url(), title: await this.page.title() };
110
+ }
111
+
112
+ /**
113
+ * Get page content (text extraction)
114
+ */
115
+ async getPageContent(url) {
116
+ await this.navigate(url);
117
+
118
+ // Extract text content
119
+ const content = await this.page.evaluate(() => {
120
+ // Remove scripts and styles
121
+ const scripts = document.querySelectorAll('script, style, noscript');
122
+ scripts.forEach(el => el.remove());
123
+
124
+ // Get main content areas
125
+ const mainContent = document.querySelector('main, article, .content, #content, .main');
126
+ const target = mainContent || document.body;
127
+
128
+ // Extract text
129
+ const text = target.innerText || target.textContent || '';
130
+
131
+ // Clean up whitespace
132
+ return text
133
+ .replace(/\s+/g, ' ')
134
+ .replace(/\n\s*\n/g, '\n')
135
+ .trim()
136
+ .substring(0, 50000); // Limit content size
137
+ });
138
+
139
+ // Extract links
140
+ const links = await this.page.evaluate(() => {
141
+ return Array.from(document.querySelectorAll('a[href]'))
142
+ .slice(0, 50)
143
+ .map(a => ({
144
+ text: a.innerText?.trim().substring(0, 100),
145
+ href: a.href,
146
+ }))
147
+ .filter(l => l.text && l.href.startsWith('http'));
148
+ });
149
+
150
+ // Extract headings
151
+ const headings = await this.page.evaluate(() => {
152
+ return Array.from(document.querySelectorAll('h1, h2, h3'))
153
+ .slice(0, 20)
154
+ .map(h => ({
155
+ level: h.tagName.toLowerCase(),
156
+ text: h.innerText?.trim().substring(0, 200),
157
+ }))
158
+ .filter(h => h.text);
159
+ });
160
+
161
+ return {
162
+ url: this.page.url(),
163
+ title: await this.page.title(),
164
+ content: content.substring(0, 20000), // Limit for context window
165
+ headings,
166
+ links: links.slice(0, 20),
167
+ wordCount: content.split(/\s+/).length,
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Take screenshot
173
+ */
174
+ async screenshot(url, options = {}) {
175
+ await this.navigate(url);
176
+
177
+ const { fullPage = false, path: customPath } = options;
178
+
179
+ // Ensure screenshot directory exists
180
+ await fs.mkdir(this.options.screenshotDir, { recursive: true });
181
+
182
+ // Generate filename
183
+ const timestamp = Date.now();
184
+ const filename = customPath || path.join(
185
+ this.options.screenshotDir,
186
+ `screenshot-${timestamp}.png`
187
+ );
188
+
189
+ await this.page.screenshot({
190
+ path: filename,
191
+ fullPage,
192
+ });
193
+
194
+ return {
195
+ path: filename,
196
+ url: this.page.url(),
197
+ title: await this.page.title(),
198
+ timestamp: new Date().toISOString(),
199
+ };
200
+ }
201
+
202
+ /**
203
+ * Click an element
204
+ */
205
+ async click(selector) {
206
+ await this.page.click(selector);
207
+ await this.page.waitForLoadState('domcontentloaded');
208
+
209
+ return {
210
+ clicked: selector,
211
+ url: this.page.url(),
212
+ title: await this.page.title(),
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Fill a form field
218
+ */
219
+ async fill(selector, value) {
220
+ await this.page.fill(selector, value);
221
+
222
+ return {
223
+ filled: selector,
224
+ value: value.substring(0, 50), // Don't expose full value
225
+ };
226
+ }
227
+
228
+ /**
229
+ * Wait for selector
230
+ */
231
+ async waitFor(selector, options = {}) {
232
+ const { timeout = 10000, state = 'visible' } = options;
233
+
234
+ try {
235
+ await this.page.waitForSelector(selector, { timeout, state });
236
+ return { success: true, selector };
237
+ } catch {
238
+ return { success: false, selector, error: 'Timeout waiting for element' };
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Execute JavaScript on page
244
+ */
245
+ async evaluate(script) {
246
+ const result = await this.page.evaluate(script);
247
+ return { result };
248
+ }
249
+
250
+ /**
251
+ * Get page metadata
252
+ */
253
+ async getMetadata(url) {
254
+ await this.navigate(url);
255
+
256
+ const metadata = await this.page.evaluate(() => {
257
+ const getMeta = (name) => {
258
+ const el = document.querySelector(`meta[name="${name}"], meta[property="${name}"]`);
259
+ return el?.getAttribute('content') || null;
260
+ };
261
+
262
+ return {
263
+ title: document.title,
264
+ description: getMeta('description') || getMeta('og:description'),
265
+ keywords: getMeta('keywords'),
266
+ author: getMeta('author'),
267
+ ogImage: getMeta('og:image'),
268
+ ogTitle: getMeta('og:title'),
269
+ canonical: document.querySelector('link[rel="canonical"]')?.href,
270
+ };
271
+ });
272
+
273
+ return { url: this.page.url(), ...metadata };
274
+ }
275
+
276
+ /**
277
+ * Search Google and get results
278
+ */
279
+ async searchGoogle(query) {
280
+ const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
281
+ await this.navigate(searchUrl);
282
+
283
+ // Wait for results
284
+ await this.page.waitForSelector('#search', { timeout: 10000 }).catch(() => {});
285
+
286
+ const results = await this.page.evaluate(() => {
287
+ const items = document.querySelectorAll('#search .g');
288
+ return Array.from(items).slice(0, 10).map(item => {
289
+ const linkEl = item.querySelector('a');
290
+ const titleEl = item.querySelector('h3');
291
+ const snippetEl = item.querySelector('.VwiC3b');
292
+
293
+ return {
294
+ title: titleEl?.innerText || '',
295
+ url: linkEl?.href || '',
296
+ snippet: snippetEl?.innerText || '',
297
+ };
298
+ }).filter(r => r.url && r.title);
299
+ });
300
+
301
+ return { query, results };
302
+ }
303
+
304
+ /**
305
+ * Fetch documentation from common sources
306
+ */
307
+ async fetchDocs(library, topic = '') {
308
+ const docSources = {
309
+ react: 'https://react.dev/reference',
310
+ nextjs: 'https://nextjs.org/docs',
311
+ prisma: 'https://www.prisma.io/docs',
312
+ typescript: 'https://www.typescriptlang.org/docs',
313
+ tailwind: 'https://tailwindcss.com/docs',
314
+ node: 'https://nodejs.org/docs/latest/api',
315
+ };
316
+
317
+ const baseUrl = docSources[library.toLowerCase()];
318
+ if (!baseUrl) {
319
+ // Fall back to Google search
320
+ return this.searchGoogle(`${library} ${topic} documentation`);
321
+ }
322
+
323
+ const url = topic ? `${baseUrl}/${topic}` : baseUrl;
324
+ return this.getPageContent(url);
325
+ }
326
+ }
327
+
328
+ // ============================================================================
329
+ // LIGHTWEIGHT FETCH (No Browser)
330
+ // ============================================================================
331
+
332
+ /**
333
+ * Simple HTTP fetch for when browser is overkill
334
+ */
335
+ export async function simpleFetch(url) {
336
+ const response = await fetch(url, {
337
+ headers: {
338
+ 'User-Agent': BROWSER_CONFIG.userAgent,
339
+ },
340
+ });
341
+
342
+ const contentType = response.headers.get('content-type') || '';
343
+
344
+ if (contentType.includes('application/json')) {
345
+ return { type: 'json', data: await response.json() };
346
+ }
347
+
348
+ const text = await response.text();
349
+
350
+ // Simple HTML to text conversion
351
+ if (contentType.includes('text/html')) {
352
+ const cleaned = text
353
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
354
+ .replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')
355
+ .replace(/<[^>]+>/g, ' ')
356
+ .replace(/\s+/g, ' ')
357
+ .trim();
358
+
359
+ return { type: 'html', data: cleaned.substring(0, 50000) };
360
+ }
361
+
362
+ return { type: 'text', data: text.substring(0, 50000) };
363
+ }
364
+
365
+ // ============================================================================
366
+ // EXPORTS
367
+ // ============================================================================
368
+
369
+ export default {
370
+ BrowserAutomation,
371
+ simpleFetch,
372
+ BROWSER_CONFIG,
373
+ };
@@ -0,0 +1,64 @@
1
+ import chalk from 'chalk';
2
+ import gradient from 'gradient-string';
3
+ import { isDoomsdayMode } from './theme-state.js';
4
+ import { showHelp as showDoomsdayHelp } from '../themes/doomsday.js';
5
+
6
+ const ultraGradient = gradient(['#6366f1', '#8b5cf6', '#d946ef']);
7
+
8
+ export function showHelp() {
9
+ if (isDoomsdayMode()) {
10
+ return showDoomsdayHelp();
11
+ }
12
+
13
+ console.log('');
14
+ console.log(ultraGradient(' ═══════════════════════════════════════════════'));
15
+ console.log(ultraGradient(' ║ U L T R A - D E X : O R C H E S T R A T I O N'));
16
+ console.log(ultraGradient(' ═══════════════════════════════════════════════'));
17
+ console.log('');
18
+
19
+ const sections = [
20
+ {
21
+ title: '🚀 PROJECT SETUP',
22
+ commands: [
23
+ ['init', 'Initialize new project'],
24
+ ['generate', 'Generate implementation plan'],
25
+ ['swarm', 'Run agent pipeline']
26
+ ]
27
+ },
28
+ {
29
+ title: '🛡️ QUALITY & DEFENSE',
30
+ commands: [
31
+ ['review', 'Run code review'],
32
+ ['validate', 'Check project integrity'],
33
+ ['hooks', 'Install git hooks']
34
+ ]
35
+ },
36
+ {
37
+ title: '⚡ ACTIVE KERNEL',
38
+ commands: [
39
+ ['serve', 'Start MCP server & dashboard'],
40
+ ['dashboard', 'Open web dashboard'],
41
+ ['agents', 'List available agents']
42
+ ]
43
+ },
44
+ {
45
+ title: '📦 DEPLOYMENT',
46
+ commands: [
47
+ ['build', 'Execute next task'],
48
+ ['deploy', 'Deploy application'],
49
+ ['doctor', 'System diagnostics']
50
+ ]
51
+ }
52
+ ];
53
+
54
+ sections.forEach(section => {
55
+ console.log(` ${chalk.hex('#8b5cf6').bold(section.title)}`);
56
+ section.commands.forEach(([cmd, desc]) => {
57
+ console.log(` ${chalk.hex('#6366f1')(cmd.padEnd(16))} ${chalk.dim(desc)}`);
58
+ });
59
+ console.log('');
60
+ });
61
+
62
+ console.log(chalk.dim(' "AI Orchestration Meta-Layer for Professional SaaS Development"'));
63
+ console.log('');
64
+ }
@@ -0,0 +1,35 @@
1
+ // cli/lib/utils/messages.js
2
+ export const professionalMessages = {
3
+ start: [
4
+ "AI Orchestration initialized. Ready for mission.",
5
+ "Analyzing project graph for optimal path...",
6
+ "System check complete. Starting task execution.",
7
+ "Leveraging 16 specialized agents for development."
8
+ ],
9
+
10
+ success: [
11
+ "✓ Task completed successfully. Alignment verified.",
12
+ "✓ System integrity confirmed. Code merged.",
13
+ "✓ Orchestration successful. Results saved.",
14
+ "✓ Professional SaaS standards achieved."
15
+ ],
16
+
17
+ error: [
18
+ "✕ Task failed. Analyzing logs for recovery...",
19
+ "✕ System anomaly detected. Diagnostic required.",
20
+ "✕ Orchestration interrupted. Please check configuration.",
21
+ "✕ Quality gate failed. Refactoring recommended."
22
+ ],
23
+
24
+ loading: [
25
+ "Initializing agent pipeline...",
26
+ "Scanning project context...",
27
+ "Optimizing orchestration logic...",
28
+ "Verifying system state..."
29
+ ]
30
+ };
31
+
32
+ export function getRandomMessage(type) {
33
+ const messages = professionalMessages[type];
34
+ return messages[Math.floor(Math.random() * messages.length)];
35
+ }
@@ -0,0 +1,24 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+
4
+ export function showProgress(tasks) {
5
+ const total = tasks.length;
6
+ console.log('');
7
+ console.log(chalk.hex('#8b5cf6').bold(' ⚡ EXECUTING TASKS...'));
8
+ console.log('');
9
+
10
+ tasks.forEach((task, idx) => {
11
+ // Simple vertical list for now
12
+ console.log(` ${chalk.hex('#d946ef')('►')} ${task}`);
13
+ });
14
+ console.log('');
15
+ }
16
+
17
+ export function progressBar(current, total, width = 40) {
18
+ const percentage = Math.round((current / total) * 100);
19
+ const filled = Math.round((current / total) * width);
20
+ const empty = width - filled;
21
+
22
+ const bar = chalk.hex('#6366f1')('█'.repeat(filled)) + chalk.dim('░'.repeat(empty));
23
+ return `${bar} ${chalk.hex('#d946ef')(percentage + '%')}`;
24
+ }
@@ -0,0 +1,47 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import gradient from 'gradient-string';
4
+
5
+ export async function selectAgent() {
6
+ const agents = [
7
+ { name: '🏛️ CTO - Architecture decisions', value: 'cto' },
8
+ { name: '📋 Planner - Task breakdown', value: 'planner' },
9
+ { name: '🔧 Backend - API & server', value: 'backend' },
10
+ { name: '🎨 Frontend - UI components', value: 'frontend' },
11
+ { name: '💾 Database - Schema & queries', value: 'database' },
12
+ { name: '🔐 Auth - Authentication', value: 'auth' },
13
+ { name: '🛡️ Security - Security review', value: 'security' },
14
+ { name: '📝 Testing - Write tests', value: 'testing' },
15
+ { name: '📖 Docs - Documentation', value: 'documentation' },
16
+ { name: '👀 Reviewer - Code review', value: 'reviewer' }
17
+ ];
18
+ const { agent } = await inquirer.prompt([{
19
+ type: 'list',
20
+ name: 'agent',
21
+ message: gradient(['#6366f1', '#8b5cf6'])('Select an agent:'),
22
+ choices: agents,
23
+ pageSize: 12
24
+ }]);
25
+
26
+ return agent;
27
+ }
28
+
29
+ export async function confirmAction(message) {
30
+ const { confirm } = await inquirer.prompt([{
31
+ type: 'confirm',
32
+ name: 'confirm',
33
+ message: chalk.yellow(message),
34
+ default: false
35
+ }]);
36
+ return confirm;
37
+ }
38
+
39
+ export async function inputText(message, defaultValue = '') {
40
+ const { value } = await inquirer.prompt([{
41
+ type: 'input',
42
+ name: 'value',
43
+ message: chalk.cyan(message),
44
+ default: defaultValue
45
+ }]);
46
+ return value;
47
+ }
@@ -0,0 +1,46 @@
1
+ import ora from 'ora';
2
+ import chalk from 'chalk';
3
+
4
+ const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
5
+
6
+ export function createSpinner(text) {
7
+ return ora({
8
+ text: chalk.cyan(text),
9
+ spinner: {
10
+ interval: 80,
11
+ frames: spinnerFrames
12
+ },
13
+ color: 'magenta'
14
+ });
15
+ }
16
+
17
+ export function success(text) {
18
+ return ora().succeed(chalk.green(text));
19
+ }
20
+
21
+ export function fail(text) {
22
+ return ora().fail(chalk.red(text));
23
+ }
24
+
25
+ export function info(text) {
26
+ return ora().info(chalk.blue(text));
27
+ }
28
+
29
+ export function warn(text) {
30
+ return ora().warn(chalk.yellow(text));
31
+ }
32
+
33
+ // Task list with progress
34
+ export async function runTasks(tasks) {
35
+ for (const task of tasks) {
36
+ const spinner = createSpinner(task.title);
37
+ spinner.start();
38
+ try {
39
+ await task.fn();
40
+ spinner.succeed(chalk.green(task.title));
41
+ } catch (error) {
42
+ spinner.fail(chalk.red(`${task.title}: ${error.message}`));
43
+ throw error;
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,31 @@
1
+ import chalk from 'chalk';
2
+ import figures from 'figures';
3
+
4
+ export const icons = {
5
+ success: chalk.hex('#22c55e')(figures.tick),
6
+ error: chalk.hex('#ef4444')(figures.cross),
7
+ warning: chalk.hex('#f59e0b')(figures.warning),
8
+ info: chalk.hex('#6366f1')(figures.info),
9
+ pending: chalk.hex('#6b7280')(figures.circle),
10
+ running: chalk.hex('#d946ef')(figures.play),
11
+ pointer: chalk.hex('#8b5cf6')(figures.pointer),
12
+ bullet: chalk.dim(figures.bullet)
13
+ };
14
+
15
+ export function showInfinityStatus() {
16
+ // Deprecated function, kept for compatibility if called elsewhere but showing nothing or simple status
17
+ }
18
+
19
+ export function statusLine(icon, text) {
20
+ console.log(` ${icon} ${text}`);
21
+ }
22
+
23
+ export function header(text) {
24
+ console.log('');
25
+ console.log(chalk.bold.hex('#8b5cf6')(` ${text}`));
26
+ console.log(chalk.dim(' ' + '─'.repeat(50)));
27
+ }
28
+
29
+ export function separator() {
30
+ console.log('');
31
+ }
@@ -0,0 +1,41 @@
1
+ import Table from 'cli-table3';
2
+ import chalk from 'chalk';
3
+ import gradient from 'gradient-string';
4
+
5
+ export function createTable(headers, rows) {
6
+ const table = new Table({
7
+ head: headers.map(h => gradient(['#6366f1', '#8b5cf6'])(h)),
8
+ chars: {
9
+ 'top': '─', 'top-mid': '┬', 'top-left': '╭', 'top-right': '╮',
10
+ 'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '╰', 'bottom-right': '╯',
11
+ 'left': '│', 'left-mid': '├', 'mid': '─', 'mid-mid': '┼',
12
+ 'right': '│', 'right-mid': '┤', 'middle': '│'
13
+ },
14
+ style: {
15
+ head: ['magenta'],
16
+ border: ['dim']
17
+ }
18
+ });
19
+
20
+ rows.forEach(row => table.push(row));
21
+ return table.toString();
22
+ }
23
+
24
+ export function showAgentsTable(agents) {
25
+ const headers = ['Tier', 'Agent', 'Status'];
26
+ const rows = agents.map(a => [
27
+ chalk.dim(a.tier || 'N/A'),
28
+ chalk.cyan(a.name),
29
+ a.status === 'ready' ? chalk.green('●') : chalk.yellow('○')
30
+ ]);
31
+ console.log(createTable(headers, rows));
32
+ }
33
+
34
+ export function showCommandsTable(commands) {
35
+ const headers = ['Command', 'Description'];
36
+ const rows = commands.map(c => [
37
+ chalk.cyan(c.name),
38
+ chalk.dim(c.description)
39
+ ]);
40
+ console.log(createTable(headers, rows));
41
+ }
@@ -0,0 +1,9 @@
1
+ export let isDoomsday = false;
2
+
3
+ export function setDoomsdayMode(enabled) {
4
+ isDoomsday = enabled;
5
+ }
6
+
7
+ export function isDoomsdayMode() {
8
+ return isDoomsday;
9
+ }