scorpion-cli 0.1.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/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # Scorpion CLI πŸ¦‚
2
+
3
+ An end-to-end **agentic AI assistant** for your terminal, refined for **speed**, **aesthetics**, and **intelligence**.
4
+
5
+ Powered by [Ollama](https://ollama.com) and **qwen3.5:0.8b**, Scorpion doesn't just chatβ€”it takes action, manages your system, and researches the web for you **(for free, without API keys)**.
6
+
7
+ ```
8
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—
9
+ β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘
10
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘
11
+ β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘
12
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
13
+ β•šβ•β•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β• β•šβ•β•β•β•β•β• β•šβ•β• β•šβ•β•β•β•
14
+ ```
15
+
16
+ ## ✨ Key Features
17
+
18
+ - **🌐 Free & Private Web Search**: Builit-in scraping engine uses **DuckDuckGo**, **Bing**, and **Google News** to find real-time information without requiring any paid API keys.
19
+ - **🧠 Smart Agent Core**: Automatically detects when it needs to search the web (for events after 2023) vs. using internal knowledge.
20
+ - **πŸ”¬ Deep Research Mode**: A dedicated tool that scans **arXiv** (papers), **Hacker News** (tech discussions), and **Wikipedia** to generate comprehensive, cited reports.
21
+ - **⚑ Local & Fast**: Runs entirely on your machine using Ollama. No data leaves your system except for anonymous search queries.
22
+ - **🎨 Beautiful UI**: Rich ASCII tables, interactive spinners, live progress updates, and structured markdown rendering.
23
+ - **πŸ”§ System Control**: Execute commands, manage files, check system performance (CPU/RAM), and write code directly to files.
24
+
25
+ ## What It Can Do
26
+
27
+ - **"Research the latest developments in quantum computing"** (Triggers Deep Research)
28
+ - **"What's the current stock price of NVIDIA?"** (Triggers Web Search)
29
+ - **"Check my system CPU usage"** (Triggers System Tools)
30
+ - **"Create a Python script for a discord bot"** (Triggers File Tools)
31
+
32
+ ## Requirements
33
+
34
+ - **Node.js** 22.12+
35
+ - [Ollama](https://ollama.com/download) running locally
36
+ - **qwen3.5:0.8b** model (recommended):
37
+ ```bash
38
+ ollama pull qwen3.5:0.8b
39
+ ```
40
+
41
+ ## Quick Start
42
+
43
+ 1. **Clone & Install**
44
+ ```bash
45
+ git clone <repo-url>
46
+ cd scorpion-cli-2
47
+ npm install
48
+ ```
49
+
50
+ 2. **Run Scorpion**
51
+ ```bash
52
+ npm start
53
+ ```
54
+ *No API keys required! It works straight out of the box.*
55
+
56
+ ### Install from npm
57
+
58
+ After the `0.1.0` release, Scorpion can be installed on Windows, macOS, and Linux:
59
+
60
+ ```bash
61
+ # Global npm install
62
+ npm install --global scorpion-cli
63
+ scorpion
64
+
65
+ # Run without installing globally
66
+ npx scorpion-cli
67
+ ```
68
+
69
+ PowerShell:
70
+
71
+ ```powershell
72
+ irm https://raw.githubusercontent.com/amaansyed27/scorpion-cli-agent/v0.1.0/install.ps1 | iex
73
+ scorpion
74
+ ```
75
+
76
+ macOS/Linux:
77
+
78
+ ```bash
79
+ curl -fsSL https://raw.githubusercontent.com/amaansyed27/scorpion-cli-agent/v0.1.0/install.sh | sh
80
+ scorpion
81
+ ```
82
+
83
+ The current `https://aish.dawnlightlabs.com/install.ps1` endpoint belongs to AiSH and should not be used for Scorpion. Configure a separate Scorpion subdomain, such as `https://scorpion.dawnlightlabs.com/install.ps1`, to serve these installer files if you want a branded short URL.
84
+
85
+ Scorpion is distributed as one Node.js package rather than separate native binaries, so the same release works across all three platforms. Ollama must still be installed and running locally.
86
+
87
+ ## 🎯 Usage & Power Commands
88
+
89
+ Scorpion uses a natural language interface, but also supports powerful triggers for specific workflows:
90
+
91
+ Inside the interactive session, slash commands control the session:
92
+
93
+ ```text
94
+ /list List installed Ollama models
95
+ /model Interactively select an installed model
96
+ /model <name> Switch directly to a model
97
+ /settings Show the active model and settings
98
+ /think on|off|toggle Enable or disable visible thinking output
99
+ /help Show all commands
100
+ /clear Clear conversation history
101
+ /stats, /reports Show session statistics or saved reports
102
+ /export [md|json] Export the last report
103
+ /demo Show UI features
104
+ /exit Quit Scorpion
105
+ ```
106
+
107
+ ### **1. Deep Research Mode** (`@deep`)
108
+ Triggers an in-depth analysis session. It searches multiple sources, reads simplified content, and compiles a structured report with citations.
109
+
110
+ ```bash
111
+ > @deep The impact of AI agents on software engineering jobs
112
+ ```
113
+ *Also triggered by asking to "research", "analyze", or "explain in detail".*
114
+
115
+ ### **2. Quick Response** (`@quick`)
116
+ Forces a fast answer without searching the web, using only the model's internal knowledge.
117
+
118
+ ```bash
119
+ > @quick Explain the concept of recursion
120
+ ```
121
+
122
+ ### **3. System & Files**
123
+ Directly interact with your OS.
124
+
125
+ ```bash
126
+ > show my memory usage
127
+ > list files in the current directory
128
+ > create a README.md file for a react project
129
+ ```
130
+
131
+ ## πŸ› οΈ Configuration
132
+
133
+ Scorpion works with default settings, but you can configure it via environment variables if needed:
134
+
135
+ - `OLLAMA_HOST`: Set if your Ollama instance is not on `localhost:11434`.
136
+ - `OLLAMA_MODEL`: Override the default `qwen3.5:0.8b` model.
137
+
138
+ ## Troubleshooting
139
+
140
+ - **"Model not found"**: Run `ollama pull qwen3.5:0.8b` (or your preferred model) first.
141
+ - **"Connection failed"**: Ensure Ollama is running (`ollama serve`).
142
+
143
+ ## Built With
144
+
145
+ - **Ollama** - Local AI inference
146
+ - **Cheerio** - specialized web scraping & parsing
147
+ - **Node.js** - Runtime & System Interaction
148
+ - **Ora/Chalk** - Terminal styling & Animations
149
+
150
+ ## License
151
+
152
+ MIT
package/install.ps1 ADDED
@@ -0,0 +1,22 @@
1
+ $ErrorActionPreference = 'Stop'
2
+
3
+ $requiredNode = [version]'22.12.0'
4
+ $nodeCommand = Get-Command node -ErrorAction SilentlyContinue
5
+ $npmCommand = Get-Command npm -ErrorAction SilentlyContinue
6
+
7
+ if (-not $nodeCommand -or -not $npmCommand) {
8
+ throw 'Node.js 22.12 or newer is required. Install it from https://nodejs.org/ and run this installer again.'
9
+ }
10
+
11
+ $nodeVersion = [version]((& node --version).TrimStart('v'))
12
+ if ($nodeVersion -lt $requiredNode) {
13
+ throw "Node.js $requiredNode or newer is required. Detected $nodeVersion."
14
+ }
15
+
16
+ & npm install --global scorpion-cli@latest
17
+ if ($LASTEXITCODE -ne 0) {
18
+ throw 'Scorpion installation failed.'
19
+ }
20
+
21
+ Write-Host 'Scorpion CLI installed successfully.' -ForegroundColor Green
22
+ Write-Host 'Run: scorpion'
package/install.sh ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env sh
2
+ set -eu
3
+
4
+ required_node='22.12.0'
5
+
6
+ if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then
7
+ echo 'Node.js 22.12 or newer is required. Install it from https://nodejs.org/ and run this installer again.' >&2
8
+ exit 1
9
+ fi
10
+
11
+ node_version="$(node --version | sed 's/^v//')"
12
+ node -e "const [a,b,c] = process.argv[1].split('.').map(Number); const [x,y,z] = process.argv[2].split('.').map(Number); process.exit(a > x || (a === x && (b > y || (b === y && c >= z))) ? 0 : 1)" "$node_version" "$required_node" || {
13
+ echo "Node.js $required_node or newer is required. Detected $node_version." >&2
14
+ exit 1
15
+ }
16
+
17
+ npm install --global scorpion-cli@latest
18
+ echo 'Scorpion CLI installed successfully.'
19
+ echo 'Run: scorpion'
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "scorpion-cli",
3
+ "version": "0.1.0",
4
+ "description": "Agentic AI CLI assistant powered by Ollama",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "scorpion": "src/index.js"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "README.md",
13
+ "install.ps1",
14
+ "install.sh"
15
+ ],
16
+ "scripts": {
17
+ "start": "node src/index.js",
18
+ "dev": "node --watch src/index.js"
19
+ },
20
+ "keywords": [
21
+ "ai",
22
+ "cli",
23
+ "ollama",
24
+ "agent",
25
+ "assistant"
26
+ ],
27
+ "author": "Amaan",
28
+ "license": "MIT",
29
+ "engines": {
30
+ "node": ">=22.12.0"
31
+ },
32
+ "dependencies": {
33
+ "boxen": "^8.0.1",
34
+ "chalk": "^6.0.0",
35
+ "cheerio": "^1.2.0",
36
+ "commander": "^15.0.0",
37
+ "figlet": "^1.11.3",
38
+ "gradient-string": "^3.0.0",
39
+ "node-fetch": "^3.3.2",
40
+ "ollama": "^0.6.3",
41
+ "ora": "^9.4.1",
42
+ "systeminformation": "^5.33.1"
43
+ }
44
+ }
package/src/agent.js ADDED
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Agent Core
3
+ * Implements the agentic loop with tool calling and streaming
4
+ */
5
+
6
+ import { chat, DEFAULT_MODEL } from './ollama.js';
7
+ import { getAllToolSchemas, executeTool } from './registry.js';
8
+
9
+ /**
10
+ * Get current date/time for system prompt
11
+ */
12
+ function getCurrentDateTime() {
13
+ const now = new Date();
14
+ return {
15
+ date: now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),
16
+ time: now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true }),
17
+ year: now.getFullYear(),
18
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Build system prompt with real-time context
24
+ */
25
+ function buildSystemPrompt() {
26
+ const dt = getCurrentDateTime();
27
+ const currentYear = new Date().getFullYear();
28
+
29
+ return `You are Scorpion, a helpful AI assistant. Current date: ${dt.date}
30
+
31
+ CRITICAL RULES:
32
+ 1. Your knowledge is outdated (cutoff: mid-2023). For ANY current events, news, sports, or facts about people/companies/products, you MUST use web_search() first.
33
+ 2. Search for anything mentioning ${currentYear - 1} or ${currentYear}, or keywords: "latest", "recent", "today", "current"
34
+ 3. Answer from memory ONLY for: basic math, greetings, timeless concepts
35
+
36
+ MANDATORY: When answering sports/news, ALWAYS call web_search() before responding.
37
+
38
+ ---
39
+
40
+ RESPONSE FORMAT - use this structure for EVERY answer:
41
+
42
+ ## Main Topic
43
+
44
+ > Key Insight: One sentence summary
45
+
46
+ Your detailed explanation with **important terms** in bold and \`code\` for technical items.
47
+
48
+ - Key point 1
49
+ - Key point 2
50
+ - Key point 3
51
+
52
+ ---
53
+
54
+ ### Additional Context (if needed)
55
+
56
+ More details here.
57
+
58
+ ---
59
+
60
+ FORMATTING RULES:
61
+ - ## for main sections
62
+ - ### for subsections
63
+ - **bold** for names, key terms, numbers
64
+ - \`code\` for technical terms
65
+ - > for key insights/summaries
66
+ - Short paragraphs (2-3 sentences)
67
+ - Cite sources clearly
68
+
69
+ Be concise, organized, and always cite your sources.`;
70
+ }
71
+
72
+ /**
73
+ * Run the agent loop
74
+ * @param {string} userMessage - User's input message
75
+ * @param {Object} options - Agent options
76
+ * @param {Array} options.history - Conversation history
77
+ * @param {Function} options.onThinking - Callback for thinking output
78
+ * @param {Function} options.onContent - Callback for content output
79
+ * @param {Function} options.onToolCall - Callback when tool is called
80
+ * @param {Function} options.onToolResult - Callback with tool result
81
+ * @param {string} options.model - Model to use
82
+ * @returns {Object} - Final response and updated history
83
+ */
84
+ export async function runAgent(userMessage, options = {}) {
85
+ const {
86
+ history = [],
87
+ onThinking = () => { },
88
+ onContent = () => { },
89
+ onToolCall = () => { },
90
+ onToolResult = () => { },
91
+ model = DEFAULT_MODEL,
92
+ } = options;
93
+
94
+ // Build messages with dynamic system prompt (includes current date/time)
95
+ const messages = [
96
+ { role: 'system', content: buildSystemPrompt() },
97
+ ...history,
98
+ { role: 'user', content: userMessage }
99
+ ];
100
+
101
+ // Get all available tools
102
+ const tools = getAllToolSchemas();
103
+
104
+ let iterations = 0;
105
+ const maxIterations = 10; // Safety limit
106
+
107
+ while (iterations < maxIterations) {
108
+ iterations++;
109
+
110
+ // Call the model with streaming
111
+ const stream = await chat({
112
+ model,
113
+ messages,
114
+ tools,
115
+ think: true,
116
+ stream: true,
117
+ });
118
+
119
+ // Accumulate the response
120
+ let thinking = '';
121
+ let content = '';
122
+ let toolCalls = [];
123
+ let inThinking = false;
124
+
125
+ for await (const chunk of stream) {
126
+ // Handle thinking
127
+ if (chunk.message?.thinking) {
128
+ if (!inThinking) {
129
+ inThinking = true;
130
+ }
131
+ thinking += chunk.message.thinking;
132
+ onThinking(chunk.message.thinking);
133
+ }
134
+
135
+ // Handle content
136
+ if (chunk.message?.content) {
137
+ if (inThinking) {
138
+ inThinking = false;
139
+ }
140
+ content += chunk.message.content;
141
+ onContent(chunk.message.content);
142
+ }
143
+
144
+ // Handle tool calls
145
+ if (chunk.message?.tool_calls) {
146
+ toolCalls = chunk.message.tool_calls;
147
+ }
148
+ }
149
+
150
+ // Add assistant message to history
151
+ const assistantMessage = {
152
+ role: 'assistant',
153
+ content: content || undefined,
154
+ thinking: thinking || undefined,
155
+ tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
156
+ };
157
+ messages.push(assistantMessage);
158
+
159
+ // If there are tool calls, execute them
160
+ if (toolCalls.length > 0) {
161
+ for (const call of toolCalls) {
162
+ const toolName = call.function?.name;
163
+ const toolArgs = call.function?.arguments || {};
164
+
165
+ onToolCall({ name: toolName, args: toolArgs });
166
+
167
+ // Execute the tool
168
+ const result = await executeTool(toolName, toolArgs);
169
+
170
+ onToolResult({ name: toolName, result });
171
+
172
+ // Add tool result to messages
173
+ messages.push({
174
+ role: 'tool',
175
+ tool_name: toolName,
176
+ content: result,
177
+ });
178
+ }
179
+ // Continue the loop to let model process tool results
180
+ } else {
181
+ // No tool calls - agent is done
182
+ break;
183
+ }
184
+ }
185
+
186
+ // Extract final response
187
+ const finalMessage = messages[messages.length - 1];
188
+
189
+ // Update history (exclude system prompt)
190
+ const newHistory = messages.slice(1);
191
+
192
+ return {
193
+ content: finalMessage.content || '',
194
+ thinking: finalMessage.thinking,
195
+ history: newHistory,
196
+ iterations,
197
+ };
198
+ }
199
+
200
+ /**
201
+ * Run a single query without streaming (simpler interface)
202
+ * @param {string} query - User query
203
+ * @param {Array} history - Conversation history
204
+ * @returns {Promise<Object>} - Response object
205
+ */
206
+ export async function query(query, history = []) {
207
+ let fullContent = '';
208
+ let fullThinking = '';
209
+ const toolsUsed = [];
210
+
211
+ const result = await runAgent(query, {
212
+ history,
213
+ onThinking: (text) => { fullThinking += text; },
214
+ onContent: (text) => { fullContent += text; },
215
+ onToolCall: (tool) => { toolsUsed.push(tool.name); },
216
+ });
217
+
218
+ return {
219
+ content: fullContent || result.content,
220
+ thinking: fullThinking,
221
+ toolsUsed,
222
+ history: result.history,
223
+ };
224
+ }
225
+
226
+ export { buildSystemPrompt };
@@ -0,0 +1,193 @@
1
+ /**
2
+ * CLI Commands
3
+ * Export, stats, and utility commands
4
+ */
5
+
6
+ import { saveResearchReport, listReports } from './ui/export.js';
7
+ import { renderTable } from './ui/table.js';
8
+ import { barChart } from './ui/charts.js';
9
+ import { colors } from './ui/formatter.js';
10
+ import { listModels } from './ollama.js';
11
+
12
+ /**
13
+ * List models installed in the local Ollama instance.
14
+ * @param {string} currentModel - Currently selected model
15
+ * @returns {Promise<Array>} Installed models
16
+ */
17
+ export async function handleListModels(currentModel) {
18
+ try {
19
+ const models = await listModels();
20
+
21
+ if (models.length === 0) {
22
+ console.log(colors.info(' No Ollama models are installed.'));
23
+ console.log(colors.dim(' Pull one with: ollama pull <model>'));
24
+ return models;
25
+ }
26
+
27
+ console.log();
28
+ console.log(colors.bold.white(' Ollama Models'));
29
+ console.log(colors.dim(' ' + '─'.repeat(50)));
30
+ renderTable(models.map((model) => ({
31
+ Model: model.name === currentModel ? `${model.name} *` : model.name,
32
+ Size: formatBytes(model.size),
33
+ Modified: model.modified_at ? new Date(model.modified_at).toLocaleString() : 'β€”',
34
+ })), { compact: true });
35
+ console.log(colors.dim(' * active model'));
36
+ console.log();
37
+ return models;
38
+ } catch (error) {
39
+ console.log(colors.error(' Could not list Ollama models.'));
40
+ console.log(colors.dim(' Make sure Ollama is running: ollama serve'));
41
+ return [];
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Select an installed Ollama model using the REPL's existing input stream.
47
+ * @param {string} currentModel - Currently selected model
48
+ * @param {Function} ask - Function that prompts for one line of input
49
+ * @param {string} requestedModel - Optional model supplied with `/model <name>`
50
+ * @returns {Promise<string|null>} Selected model, or null when cancelled/unavailable
51
+ */
52
+ export async function handleModelSelect(currentModel, ask, requestedModel = '') {
53
+ let models;
54
+ try {
55
+ models = await listModels();
56
+ } catch (error) {
57
+ console.log(colors.error(' Could not connect to Ollama.'));
58
+ console.log(colors.dim(' Make sure Ollama is running: ollama serve'));
59
+ return null;
60
+ }
61
+ if (models.length === 0) {
62
+ console.log(colors.info(' No Ollama models are installed. Pull one with: ollama pull <model>'));
63
+ return null;
64
+ }
65
+
66
+ const requested = requestedModel.trim();
67
+ if (requested) {
68
+ const exactMatch = models.find((model) => model.name === requested);
69
+ if (exactMatch) return exactMatch.name;
70
+ console.log(colors.error(` Model '${requested}' is not installed. Use /list to see available models.`));
71
+ return null;
72
+ }
73
+
74
+ console.log();
75
+ console.log(colors.bold.white(' Select an Ollama Model'));
76
+ models.forEach((model, index) => {
77
+ const active = model.name === currentModel ? colors.success(' (active)') : '';
78
+ console.log(` ${colors.dim(`${index + 1}.`)} ${model.name}${active}`);
79
+ });
80
+ console.log(colors.dim(' Enter a number, model name, or press Enter to cancel.'));
81
+
82
+ const answer = (await ask(colors.accent(' Select model: '))).trim();
83
+ if (!answer) return null;
84
+
85
+ const selectedIndex = Number.parseInt(answer, 10);
86
+ if (Number.isInteger(selectedIndex) && String(selectedIndex) === answer && models[selectedIndex - 1]) {
87
+ return models[selectedIndex - 1].name;
88
+ }
89
+
90
+ const exactMatch = models.find((model) => model.name === answer);
91
+ if (exactMatch) return exactMatch.name;
92
+
93
+ console.log(colors.error(` '${answer}' is not a valid installed model.`));
94
+ return null;
95
+ }
96
+
97
+ function formatBytes(bytes = 0) {
98
+ if (!bytes) return 'β€”';
99
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
100
+ const index = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
101
+ return `${(bytes / (1024 ** index)).toFixed(index === 0 ? 0 : 1)} ${units[index]}`;
102
+ }
103
+
104
+ /**
105
+ * Handle export command
106
+ * @param {string} format - Export format (md, json)
107
+ */
108
+ export async function handleExport(format = 'md') {
109
+ console.log();
110
+ console.log(colors.accent(' πŸ’Ύ Export Report'));
111
+ console.log(colors.dim(' ─'.repeat(30)));
112
+ console.log();
113
+ console.log(colors.dim(' Exporting last research report...'));
114
+ console.log();
115
+
116
+ // TODO: Implement export of last report
117
+ console.log(colors.info(' β„Ή Export functionality available via Deep Research'));
118
+ console.log(colors.dim(' Reports are auto-saved to reports/ directory'));
119
+ console.log();
120
+ }
121
+
122
+ /**
123
+ * Handle list reports command
124
+ */
125
+ export async function handleListReports() {
126
+ await listReports();
127
+ }
128
+
129
+ /**
130
+ * Handle stats command
131
+ */
132
+ export async function handleStats() {
133
+ console.log();
134
+ console.log(colors.bold.white(' πŸ“Š Session Statistics'));
135
+ console.log(colors.dim(' ─'.repeat(50)));
136
+ console.log();
137
+
138
+ // Example stats (would be tracked in real implementation)
139
+ const stats = [
140
+ { label: 'Queries', value: 12 },
141
+ { label: 'Tool Calls', value: 45 },
142
+ { label: 'Deep Research', value: 3 },
143
+ { label: 'Quick Response', value: 9 }
144
+ ];
145
+
146
+ barChart(stats, { maxWidth: 30, color: 'cyan' });
147
+ }
148
+
149
+ /**
150
+ * Handle demo command (show all UI features)
151
+ */
152
+ export async function handleDemo() {
153
+ console.log();
154
+ console.log(colors.bold.white(' 🎨 UI Feature Demo'));
155
+ console.log(colors.dim(' ─'.repeat(50)));
156
+ console.log();
157
+
158
+ // Demo table
159
+ console.log(colors.accent(' πŸ“‹ Table Example:'));
160
+ console.log();
161
+ renderTable([
162
+ { Feature: 'Deep Research', Status: 'βœ“ Available', Type: 'Core' },
163
+ { Feature: 'Quick Mode', Status: 'βœ“ Available', Type: 'Core' },
164
+ { Feature: 'Export', Status: 'βœ“ Available', Type: 'Utility' }
165
+ ], { compact: true });
166
+
167
+ console.log();
168
+ console.log(colors.accent(' πŸ“Š Chart Example:'));
169
+ console.log();
170
+ barChart([
171
+ { label: 'arXiv', value: 85 },
172
+ { label: 'HN', value: 65 },
173
+ { label: 'Wikipedia', value: 92 }
174
+ ], { title: 'Source Reliability (%)', maxWidth: 40 });
175
+
176
+ console.log(colors.accent(' πŸ’‘ Available Commands:'));
177
+ console.log();
178
+ console.log(colors.dim(' help') + colors.white(' - Show @ trigger help'));
179
+ console.log(colors.dim(' stats') + colors.white(' - Show session stats'));
180
+ console.log(colors.dim(' reports') + colors.white(' - List saved reports'));
181
+ console.log(colors.dim(' demo') + colors.white(' - Show UI features'));
182
+ console.log(colors.dim(' clear') + colors.white(' - Clear conversation'));
183
+ console.log();
184
+ }
185
+
186
+ export default {
187
+ handleExport,
188
+ handleListReports,
189
+ handleListModels,
190
+ handleModelSelect,
191
+ handleStats,
192
+ handleDemo
193
+ };