@involvex/super-agent-cli 0.0.80 → 0.0.82

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 CHANGED
@@ -1,6 +1,9 @@
1
- # @involvex/super-agent-cli
1
+ ```
2
+ @involvex/super-agent-cli
3
+ ```
4
+
5
+ ![Super Agent CLI Banner](assets/images/banner.png)
2
6
 
3
- ![Super Agent CLI Banner](docs/banner.png)
4
7
  [![Security Scan](https://github.com/involvex/super-agent-cli/actions/workflows/security.yml/badge.svg)](https://github.com/involvex/super-agent-cli/actions/workflows/security.yml)
5
8
  ![Badge](https://img.shields.io/npm/v/@involvex/super-agent-cli?label=build&message=passing&color=brightgreen&style=social&logo=npm)
6
9
 
Binary file
package/dist/index.js CHANGED
@@ -1021,8 +1021,8 @@ __export(exports_image_generation, {
1021
1021
  getImageGenerationService: () => getImageGenerationService,
1022
1022
  ImageGenerationService: () => ImageGenerationService
1023
1023
  });
1024
- import * as fs7 from "fs-extra";
1025
1024
  import * as path7 from "path";
1025
+ import fs7 from "fs-extra";
1026
1026
 
1027
1027
  class ImageGenerationService {
1028
1028
  static instance;
@@ -1609,8 +1609,8 @@ __export(exports_indexer, {
1609
1609
  getFileIndexer: () => getFileIndexer,
1610
1610
  FileIndexer: () => FileIndexer
1611
1611
  });
1612
- import * as fs15 from "fs-extra";
1613
1612
  import * as path15 from "path";
1613
+ import fs15 from "fs-extra";
1614
1614
 
1615
1615
  class FileIndexer {
1616
1616
  static instance;
@@ -1731,7 +1731,7 @@ var init_indexer = __esm(() => {
1731
1731
  var require_package = __commonJS((exports, module) => {
1732
1732
  module.exports = {
1733
1733
  name: "@involvex/super-agent-cli",
1734
- version: "0.0.80",
1734
+ version: "0.0.82",
1735
1735
  description: "An open-source AI agent that brings the power of Super Agent directly into your terminal.",
1736
1736
  keywords: [
1737
1737
  "cli",
@@ -1759,7 +1759,7 @@ var require_package = __commonJS((exports, module) => {
1759
1759
  },
1760
1760
  main: "dist/index.js",
1761
1761
  bin: {
1762
- "super-agent": "dist/index.js"
1762
+ "super-agent": "super-agent.js"
1763
1763
  },
1764
1764
  workspaces: [
1765
1765
  "@plugins/templates/*",
@@ -1768,13 +1768,14 @@ var require_package = __commonJS((exports, module) => {
1768
1768
  ],
1769
1769
  scripts: {
1770
1770
  prebuild: "bun run format && bun run lint:fix && bun run typecheck",
1771
- build: "bun build src/index.ts --outdir ./dist --target node --packages external --format esm",
1771
+ build: "bun build src/index.ts --outdir ./dist --target node --packages external --format esm && bun run copy-bin && bun run build:web",
1772
1772
  "build:bun": "bun build src/index.ts --outdir ./dist --target node --packages external --format esm",
1773
1773
  "build:plugins": "bun run -F @involvex/super-agent\\* build",
1774
1774
  "build:vscode": "bun run -F super-agent-vscode build",
1775
1775
  "build:web": "bun run scripts/build-web.ts",
1776
1776
  changelog: "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
1777
1777
  compile: "bun run prebuild && bun build --compile src/index.ts --outfile ./dist/super-agent-cli.exe --config bunfig.toml",
1778
+ "copy-bin": "cp super-agent.js dist/super-agent.js",
1778
1779
  dev: "bun run --watch src/index.ts",
1779
1780
  "dev:node": "tsx src/index.ts",
1780
1781
  format: "prettier --write .",
@@ -1783,8 +1784,8 @@ var require_package = __commonJS((exports, module) => {
1783
1784
  lint: "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern dist",
1784
1785
  "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix --ignore-pattern dist",
1785
1786
  "package:vscode": "bun run -F super-agent-vscode package",
1786
- start: "node dist/index.js",
1787
- "start:bun": "bun run dist/index.js",
1787
+ start: "bun run dist/index.js",
1788
+ "start:node": "node dist/index.js",
1788
1789
  test: "vitest run",
1789
1790
  "test:coverage": "vitest run --coverage",
1790
1791
  "test:integration": "vitest run src/tests/integration",
@@ -2908,8 +2909,8 @@ init_config();
2908
2909
  init_settings_manager();
2909
2910
  import { exec as exec2 } from "child_process";
2910
2911
  import { promisify as promisify2 } from "util";
2911
- import * as fs4 from "fs-extra";
2912
2912
  import * as path4 from "path";
2913
+ import fs4 from "fs-extra";
2913
2914
  var execAsync2 = promisify2(exec2);
2914
2915
 
2915
2916
  class PluginManager {
@@ -3474,8 +3475,8 @@ async function getAllSuperAgentTools() {
3474
3475
  // src/plugins/repository-manager.ts
3475
3476
  import { exec as exec3 } from "child_process";
3476
3477
  import { promisify as promisify3 } from "util";
3477
- import * as fs5 from "fs-extra";
3478
3478
  import * as path5 from "path";
3479
+ import fs5 from "fs-extra";
3479
3480
  var execAsync3 = promisify3(exec3);
3480
3481
  var BUILTIN_REPOSITORIES = {
3481
3482
  agents: {
@@ -3965,7 +3966,7 @@ function getChatManager() {
3965
3966
  }
3966
3967
 
3967
3968
  // src/hooks/use-input-handler.ts
3968
- import * as fs11 from "fs-extra";
3969
+ import fs11 from "fs-extra";
3969
3970
  function useInputHandler({
3970
3971
  agent,
3971
3972
  chatHistory,
@@ -11711,7 +11712,6 @@ import * as fs20 from "fs-extra";
11711
11712
  import * as path20 from "path";
11712
11713
  import open from "open";
11713
11714
  import mime from "mime";
11714
- var __dirname = "/home/runner/work/super-agent-cli/super-agent-cli/src/web";
11715
11715
 
11716
11716
  class WebServer {
11717
11717
  httpServer;
@@ -11734,20 +11734,24 @@ class WebServer {
11734
11734
  }
11735
11735
  async handleHttpRequest(req, res) {
11736
11736
  const url = req.url || "/";
11737
- const requestedPath = url === "/" ? "index.html" : url;
11737
+ let requestedPath = url === "/" ? "index.html" : url.substring(1);
11738
11738
  const sanitizedPath = requestedPath.split("?")[0].split("#")[0];
11739
- const clientDir = path20.join(__dirname, "../web/client");
11740
- const absolutePath = path20.resolve(clientDir, sanitizedPath.substring(1));
11741
- if (!absolutePath.startsWith(clientDir)) {
11739
+ const distWebPath = path20.join(process.cwd(), "dist", "web");
11740
+ const sourceWebPath = path20.join(process.cwd(), "src", "web", "client");
11741
+ const clientDir = await fs20.pathExists(distWebPath) ? distWebPath : sourceWebPath;
11742
+ const absolutePath = path20.resolve(clientDir, sanitizedPath);
11743
+ const normalizedClientDir = path20.resolve(clientDir);
11744
+ const normalizedAbsolutePath = path20.resolve(absolutePath);
11745
+ if (!normalizedAbsolutePath.startsWith(normalizedClientDir)) {
11742
11746
  res.writeHead(403, { "Content-Type": "text/plain" });
11743
11747
  res.end("Forbidden");
11744
11748
  return;
11745
11749
  }
11746
11750
  try {
11747
11751
  if (await fs20.pathExists(absolutePath)) {
11748
- const stat5 = await fs20.stat(absolutePath);
11752
+ const stat2 = await fs20.stat(absolutePath);
11749
11753
  let filePath = absolutePath;
11750
- if (stat5.isDirectory()) {
11754
+ if (stat2.isDirectory()) {
11751
11755
  filePath = path20.join(absolutePath, "index.html");
11752
11756
  }
11753
11757
  const content = await fs20.readFile(filePath);
@@ -12149,7 +12153,7 @@ function createIndexCommand() {
12149
12153
  const indexCommand = new Command4("index").description(" recursively index a directory and save to a file").argument("[directory]", "Directory to index", ".").option("-o, --output <file>", "Output file path (default: index.md)").option("--no-ignore", "Disable .gitignore respecting").option("-d, --depth <depth>", "Max depth to traverse", "10").action(async (directory, options) => {
12150
12154
  try {
12151
12155
  const rootDir = path21.resolve(directory);
12152
- const outputFile2 = options.output ? path21.resolve(options.output) : path21.join(rootDir, "index.md");
12156
+ const outputFile = options.output ? path21.resolve(options.output) : path21.join(rootDir, "index.md");
12153
12157
  const maxDepth = parseInt(options.depth);
12154
12158
  console.log(chalk4.blue(`Indexing directory: ${rootDir}`));
12155
12159
  const ig = ignore().add(DEFAULT_IGNORES);
@@ -12246,8 +12250,8 @@ Date: ${new Date().toISOString()}
12246
12250
 
12247
12251
  `;
12248
12252
  }
12249
- await fs21.writeFile(outputFile2, outputContent);
12250
- console.log(chalk4.green(`✓ Index generated at: ${outputFile2}`));
12253
+ await fs21.writeFile(outputFile, outputContent);
12254
+ console.log(chalk4.green(`✓ Index generated at: ${outputFile}`));
12251
12255
  } catch (error) {
12252
12256
  console.error(chalk4.red(`Error indexing directory: ${error.message}`));
12253
12257
  process.exit(1);
Binary file
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bun
2
+ // Super Agent CLI wrapper script
3
+ // This ensures the bundled code runs with bun for proper module resolution
4
+
5
+ import { dirname, join } from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // Import and run the bundled CLI
12
+ import(join(__dirname, "dist", "index.js"));
@@ -0,0 +1,418 @@
1
+ // WebSocket connection
2
+ let ws = null;
3
+ let isConnected = false;
4
+
5
+ const messagesContainer = document.getElementById("messages");
6
+ const promptInput = document.getElementById("prompt-input");
7
+ const sendBtn = document.getElementById("send-btn");
8
+ const statusEl = document.getElementById("status");
9
+ const autocompleteEl = document.getElementById("autocomplete");
10
+ const fileTreeEl = document.getElementById("file-tree");
11
+ const sessionsListEl = document.getElementById("sessions-list");
12
+
13
+ // Slash commands
14
+ const SLASH_COMMANDS = [
15
+ { cmd: "/chat", desc: "Manage chat sessions" },
16
+ { cmd: "/config", desc: "Configure settings" },
17
+ { cmd: "/models", desc: "Select AI model" },
18
+ { cmd: "/provider", desc: "Select LLM provider" },
19
+ { cmd: "/mcp", desc: "MCP server management" },
20
+ { cmd: "/plugin", desc: "Plugin management" },
21
+ { cmd: "/search", desc: "Search files/code" },
22
+ { cmd: "/help", desc: "Show help" },
23
+ ];
24
+
25
+ // Security: HTML escape utility to prevent XSS
26
+ function escapeHtml(unsafe) {
27
+ if (typeof unsafe !== "string") {
28
+ return "";
29
+ }
30
+ return unsafe
31
+ .replace(/&/g, "&amp;")
32
+ .replace(/</g, "&lt;")
33
+ .replace(/>/g, "&gt;")
34
+ .replace(/"/g, "&quot;")
35
+ .replace(/'/g, "&#039;");
36
+ }
37
+
38
+ // Connect to WebSocket
39
+ function connect() {
40
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
41
+ const wsUrl = `${protocol}//${window.location.host}`;
42
+
43
+ ws = new WebSocket(wsUrl);
44
+
45
+ ws.onopen = () => {
46
+ isConnected = true;
47
+ updateStatus("Connected", "connected");
48
+ sendBtn.disabled = false;
49
+
50
+ // Load initial data
51
+ requestFileTree();
52
+ requestSessions();
53
+ };
54
+
55
+ ws.onclose = () => {
56
+ isConnected = false;
57
+ updateStatus("Disconnected - Retrying...", "disconnected");
58
+ sendBtn.disabled = true;
59
+ setTimeout(connect, 2000);
60
+ };
61
+
62
+ ws.onerror = error => {
63
+ console.error("WebSocket error:", error);
64
+ updateStatus("Connection error", "disconnected");
65
+ // Check if we're on Firebase Hosting or similar static hosting
66
+ // Firebase Hosting does NOT support WebSocket connections
67
+ // Show disconnected mode after connection fails
68
+ setTimeout(() => {
69
+ if (!isConnected) {
70
+ showDisconnectedMode();
71
+ }
72
+ }, 2000);
73
+ };
74
+
75
+ ws.onmessage = event => {
76
+ try {
77
+ const data = JSON.parse(event.data);
78
+ handleMessage(data);
79
+ } catch (error) {
80
+ console.error("Error parsing message:", error);
81
+ }
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Show disconnected mode with helpful instructions
87
+ * This is shown when the WebSocket connection fails, which happens on
88
+ * Firebase Hosting and other static hosting providers that don't support WebSocket.
89
+ */
90
+ function showDisconnectedMode() {
91
+ isConnected = false;
92
+ updateStatus("View-only mode - CLI not connected", "disconnected");
93
+
94
+ // Check if banner already exists to avoid duplicates
95
+ const existingBanner = document.querySelector(".disconnected-banner");
96
+ if (existingBanner) {
97
+ return;
98
+ }
99
+
100
+ // Show banner explaining the situation
101
+ const banner = document.createElement("div");
102
+ banner.className = "disconnected-banner";
103
+ banner.innerHTML = `
104
+ <div class="banner-content">
105
+ <strong>📡 CLI Connection Required</strong>
106
+ <p>This web interface is in view-only mode. For full functionality:</p>
107
+ <ol>
108
+ <li>Run <code>super-agent --web</code> in your project directory</li>
109
+ <li>Open <a href="http://localhost:3000">http://localhost:3000</a></li>
110
+ </ol>
111
+ <p class="note">Note: This web interface on Firebase Hosting is for demonstration only.
112
+ WebSocket connections are not supported on static hosting platforms.</p>
113
+ <button class="dismiss-btn">Dismiss</button>
114
+ </div>
115
+ `;
116
+
117
+ document.body.insertBefore(banner, document.body.firstChild);
118
+
119
+ // Add dismiss handler
120
+ banner.querySelector(".dismiss-btn").addEventListener("click", () => {
121
+ banner.remove();
122
+ });
123
+
124
+ // Disable interactive elements
125
+ sendBtn.disabled = true;
126
+ promptInput.disabled = true;
127
+ promptInput.placeholder = "Connect to CLI to send messages...";
128
+
129
+ // Add a message to the chat
130
+ addMessage(
131
+ "⚠️ WebSocket connection failed. This interface is running on a static hosting platform " +
132
+ "that doesn't support WebSocket connections. Please run 'super-agent --web' locally " +
133
+ "and connect to http://localhost:3000 for full functionality.",
134
+ "error",
135
+ );
136
+ }
137
+
138
+ // Update status indicator
139
+ function updateStatus(text, className = "") {
140
+ statusEl.textContent = text;
141
+ statusEl.className = `status ${className}`;
142
+ }
143
+
144
+ // Show update banner
145
+ function showUpdateBanner(currentVersion, latestVersion) {
146
+ const banner = document.createElement("div");
147
+ banner.className = "update-banner";
148
+
149
+ const span = document.createElement("span");
150
+ span.textContent = `📦 Update available: ${currentVersion} → ${latestVersion}`;
151
+
152
+ const button = document.createElement("button");
153
+ button.textContent = "✕";
154
+ button.onclick = () => banner.remove();
155
+
156
+ banner.appendChild(span);
157
+ banner.appendChild(button);
158
+ document.body.insertBefore(banner, document.body.firstChild);
159
+ }
160
+
161
+ // Request file tree
162
+ function requestFileTree() {
163
+ if (isConnected) {
164
+ ws.send(JSON.stringify({ type: "get_file_tree" }));
165
+ }
166
+ }
167
+
168
+ // Request sessions list
169
+ function requestSessions() {
170
+ if (isConnected) {
171
+ ws.send(JSON.stringify({ type: "list_sessions" }));
172
+ }
173
+ }
174
+
175
+ // Switch session
176
+ function switchSession(sessionId) {
177
+ if (isConnected) {
178
+ ws.send(JSON.stringify({ type: "switch_session", sessionId }));
179
+ }
180
+ }
181
+
182
+ // View file
183
+ function viewFile(filePath) {
184
+ if (isConnected) {
185
+ ws.send(JSON.stringify({ type: "get_file_content", path: filePath }));
186
+ }
187
+ }
188
+
189
+ // Handle incoming messages
190
+ function handleMessage(data) {
191
+ switch (data.type) {
192
+ case "update_available":
193
+ showUpdateBanner(data.currentVersion, data.latestVersion);
194
+ break;
195
+ case "file_tree":
196
+ renderFileTree(data.tree);
197
+ break;
198
+ case "file_content":
199
+ showFilePreview(data.path, data.content);
200
+ break;
201
+ case "sessions_list":
202
+ renderSessions(data.sessions);
203
+ break;
204
+ case "session_switched":
205
+ handleSessionSwitch(data.session);
206
+ break;
207
+ case "user_message":
208
+ addMessage(data.content, "user");
209
+ break;
210
+ case "assistant_message":
211
+ addMessage(data.content, "assistant");
212
+ break;
213
+ case "tool_call":
214
+ addMessage(`🔧 ${data.tool}: ${data.content}`, "tool");
215
+ break;
216
+ case "tool_result":
217
+ addMessage(`✅ ${data.tool}: ${data.content}`, "tool");
218
+ break;
219
+ case "error":
220
+ addMessage(`❌ Error: ${data.content}`, "error");
221
+ break;
222
+ case "done":
223
+ sendBtn.disabled = false;
224
+ promptInput.disabled = false;
225
+ updateStatus("Connected", "connected");
226
+ break;
227
+ }
228
+ }
229
+
230
+ // Render file tree
231
+ function renderFileTree(tree) {
232
+ if (!tree || tree.length === 0) {
233
+ fileTreeEl.innerHTML = '<p class="empty-state">No files indexed yet</p>';
234
+ return;
235
+ }
236
+
237
+ const directories = tree.filter(f => f.isDirectory);
238
+ const files = tree.filter(f => !f.isDirectory);
239
+
240
+ let html = "";
241
+
242
+ // Show directories first
243
+ directories.slice(0, 50).forEach(dir => {
244
+ const safeName = escapeHtml(dir.name);
245
+ html += `
246
+ <div class="file-item directory">
247
+ 📁 ${safeName}
248
+ </div>
249
+ `;
250
+ });
251
+
252
+ // Then files
253
+ files.slice(0, 100).forEach(file => {
254
+ const safeName = escapeHtml(file.name);
255
+ const safePath = escapeHtml(file.path);
256
+ html += `
257
+ <div class="file-item" onclick="viewFile('${safePath}')">
258
+ 📄 ${safeName}
259
+ </div>
260
+ `;
261
+ });
262
+
263
+ fileTreeEl.innerHTML = html;
264
+ }
265
+
266
+ // Render sessions
267
+ function renderSessions(sessions) {
268
+ if (!sessions || sessions.length === 0) {
269
+ sessionsListEl.innerHTML = '<p class="empty-state">No saved sessions</p>';
270
+ return;
271
+ }
272
+
273
+ let html = "";
274
+ sessions.forEach(session => {
275
+ const date = new Date(session.lastAccessed).toLocaleDateString();
276
+ const safeName = escapeHtml(session.name);
277
+ const safeId = escapeHtml(session.id);
278
+ const safeCount = escapeHtml(String(session.messageCount || 0));
279
+ const safeDate = escapeHtml(date);
280
+
281
+ html += `
282
+ <div class="session-item" onclick="switchSession('${safeId}')">
283
+ <div class="session-name">${safeName}</div>
284
+ <div class="session-meta">${safeCount} messages · ${safeDate}</div>
285
+ </div>
286
+ `;
287
+ });
288
+
289
+ sessionsListEl.innerHTML = html;
290
+ }
291
+
292
+ // Handle session switch
293
+ function handleSessionSwitch(session) {
294
+ // Clear current messages
295
+ messagesContainer.innerHTML = "";
296
+
297
+ // Load session messages
298
+ session.messages.forEach(msg => {
299
+ addMessage(msg.content, msg.role);
300
+ });
301
+
302
+ updateStatus(`Session: ${session.name}`, "connected");
303
+ }
304
+
305
+ // Show file preview
306
+ function showFilePreview(path, content) {
307
+ addMessage(`📄 ${path}:\n\`\`\`\n${content}\n\`\`\``, "assistant");
308
+ }
309
+
310
+ // Add message to chat
311
+ function addMessage(content, type) {
312
+ const messageEl = document.createElement("div");
313
+ messageEl.className = `message ${type}`;
314
+ messageEl.textContent = content;
315
+ messagesContainer.appendChild(messageEl);
316
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
317
+ }
318
+
319
+ // Slash command autocomplete
320
+ function handleAutocomplete(input) {
321
+ const value = input.trim();
322
+
323
+ if (!value.startsWith("/")) {
324
+ autocompleteEl.classList.add("hidden");
325
+ return;
326
+ }
327
+
328
+ const query = value.toLowerCase();
329
+ const matches = SLASH_COMMANDS.filter(cmd =>
330
+ cmd.cmd.toLowerCase().startsWith(query),
331
+ );
332
+
333
+ if (matches.length === 0) {
334
+ autocompleteEl.classList.add("hidden");
335
+ return;
336
+ }
337
+
338
+ let html = "";
339
+ matches.forEach((cmd, index) => {
340
+ const safeCmd = escapeHtml(cmd.cmd);
341
+ const safeDesc = escapeHtml(cmd.desc);
342
+ html += `
343
+ <div class="autocomplete-item" data-cmd="${safeCmd}" data-index="${index}">
344
+ <strong>${safeCmd}</strong> - ${safeDesc}
345
+ </div>
346
+ `;
347
+ });
348
+
349
+ autocompleteEl.innerHTML = html;
350
+ autocompleteEl.classList.remove("hidden");
351
+
352
+ // Add click handlers
353
+ autocompleteEl.querySelectorAll(".autocomplete-item").forEach(item => {
354
+ item.addEventListener("click", () => {
355
+ promptInput.value = item.dataset.cmd + " ";
356
+ autocompleteEl.classList.add("hidden");
357
+ promptInput.focus();
358
+ });
359
+ });
360
+ }
361
+
362
+ // Send message
363
+ function sendMessage() {
364
+ const prompt = promptInput.value.trim();
365
+ if (!prompt || !isConnected) {
366
+ return;
367
+ }
368
+
369
+ ws.send(
370
+ JSON.stringify({
371
+ type: "prompt",
372
+ content: prompt,
373
+ }),
374
+ );
375
+
376
+ promptInput.value = "";
377
+ autocompleteEl.classList.add("hidden");
378
+ sendBtn.disabled = true;
379
+ promptInput.disabled = true;
380
+ updateStatus("Processing...", "");
381
+ }
382
+
383
+ // Event listeners
384
+ sendBtn.addEventListener("click", sendMessage);
385
+
386
+ promptInput.addEventListener("input", e => {
387
+ handleAutocomplete(e.target.value);
388
+ });
389
+
390
+ promptInput.addEventListener("keydown", e => {
391
+ if (e.key === "Enter" && !e.shiftKey) {
392
+ e.preventDefault();
393
+
394
+ // Check if autocomplete is visible
395
+ if (!autocompleteEl.classList.contains("hidden")) {
396
+ const firstItem = autocompleteEl.querySelector(".autocomplete-item");
397
+ if (firstItem) {
398
+ promptInput.value = firstItem.dataset.cmd + " ";
399
+ autocompleteEl.classList.add("hidden");
400
+ return;
401
+ }
402
+ }
403
+
404
+ sendMessage();
405
+ } else if (e.key === "Escape") {
406
+ autocompleteEl.classList.add("hidden");
407
+ }
408
+ });
409
+
410
+ document
411
+ .getElementById("refresh-files")
412
+ .addEventListener("click", requestFileTree);
413
+ document
414
+ .getElementById("refresh-sessions")
415
+ .addEventListener("click", requestSessions);
416
+
417
+ // Initialize connection
418
+ connect();
Binary file
@@ -0,0 +1,71 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Super Agent - Web Interface</title>
7
+ <link rel="icon" type="image/png" href="favicon.png" />
8
+ <link rel="apple-touch-icon" href="logo.png" />
9
+ <link rel="stylesheet" href="styles.css" />
10
+ </head>
11
+ <body>
12
+ <div class="app-layout">
13
+ <!-- Sidebar -->
14
+ <div id="sidebar" class="sidebar">
15
+ <div class="sidebar-header">
16
+ <h3>📁 Files</h3>
17
+ <button id="refresh-files" class="icon-btn">↻</button>
18
+ </div>
19
+ <div id="file-tree" class="file-tree"></div>
20
+
21
+ <div class="sidebar-section">
22
+ <h3>💼 Sessions</h3>
23
+ <button id="refresh-sessions" class="icon-btn">↻</button>
24
+ </div>
25
+ <div id="sessions-list" class="sessions-list"></div>
26
+ </div>
27
+
28
+ <!-- Main Content -->
29
+ <div class="main-content">
30
+ <header>
31
+ <div
32
+ style="
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ gap: 20px;
37
+ "
38
+ >
39
+ <img
40
+ src="logo.png"
41
+ alt="Super Agent Logo"
42
+ style="width: 60px; height: 60px"
43
+ />
44
+ <div>
45
+ <h1>🤖 Super Agent</h1>
46
+ <p class="subtitle">AI-Powered Terminal Assistant</p>
47
+ </div>
48
+ </div>
49
+ </header>
50
+
51
+ <div id="chat-container">
52
+ <div id="messages"></div>
53
+ </div>
54
+
55
+ <div class="input-container">
56
+ <div id="autocomplete" class="autocomplete hidden"></div>
57
+ <textarea
58
+ id="prompt-input"
59
+ placeholder="Ask Super Agent anything... (type / for commands)"
60
+ rows="3"
61
+ ></textarea>
62
+ <button id="send-btn">Send</button>
63
+ </div>
64
+
65
+ <div id="status" class="status"></div>
66
+ </div>
67
+ </div>
68
+
69
+ <script src="app.js"></script>
70
+ </body>
71
+ </html>
Binary file
@@ -0,0 +1,444 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family:
9
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
10
+ Cantarell, sans-serif;
11
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
12
+ min-height: 100vh;
13
+ overflow: hidden;
14
+ }
15
+
16
+ .update-banner {
17
+ position: fixed;
18
+ top: 0;
19
+ left: 0;
20
+ right: 0;
21
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
22
+ color: white;
23
+ padding: 12px 20px;
24
+ display: flex;
25
+ justify-content: space-between;
26
+ align-items: center;
27
+ z-index: 1000;
28
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
29
+ animation: slideDown 0.3s ease;
30
+ }
31
+
32
+ @keyframes slideDown {
33
+ from {
34
+ transform: translateY(-100%);
35
+ }
36
+ to {
37
+ transform: translateY(0);
38
+ }
39
+ }
40
+
41
+ .update-banner button {
42
+ background: rgba(255, 255, 255, 0.2);
43
+ border: none;
44
+ color: white;
45
+ padding: 5px 10px;
46
+ border-radius: 5px;
47
+ cursor: pointer;
48
+ font-weight: bold;
49
+ }
50
+
51
+ .update-banner button:hover {
52
+ background: rgba(255, 255, 255, 0.3);
53
+ }
54
+
55
+ .app-layout {
56
+ display: flex;
57
+ height: 100vh;
58
+ padding: 20px;
59
+ gap: 20px;
60
+ }
61
+
62
+ .sidebar {
63
+ width: 280px;
64
+ background: white;
65
+ border-radius: 20px;
66
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
67
+ overflow: hidden;
68
+ display: flex;
69
+ flex-direction: column;
70
+ }
71
+
72
+ .sidebar-header {
73
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
74
+ color: white;
75
+ padding: 20px;
76
+ display: flex;
77
+ justify-content: space-between;
78
+ align-items: center;
79
+ }
80
+
81
+ .sidebar-header h3 {
82
+ font-size: 1.1rem;
83
+ margin: 0;
84
+ }
85
+
86
+ .sidebar-section {
87
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
88
+ color: white;
89
+ padding: 15px 20px;
90
+ display: flex;
91
+ justify-content: space-between;
92
+ align-items: center;
93
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
94
+ }
95
+
96
+ .sidebar-section h3 {
97
+ font-size: 1rem;
98
+ margin: 0;
99
+ }
100
+
101
+ .icon-btn {
102
+ background: rgba(255, 255, 255, 0.2);
103
+ border: none;
104
+ color: white;
105
+ width: 30px;
106
+ height: 30px;
107
+ border-radius: 50%;
108
+ cursor: pointer;
109
+ font-size: 1rem;
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ transition: background 0.2s;
114
+ }
115
+
116
+ .icon-btn:hover {
117
+ background: rgba(255, 255, 255, 0.3);
118
+ }
119
+
120
+ .file-tree,
121
+ .sessions-list {
122
+ flex: 1;
123
+ overflow-y: auto;
124
+ padding: 10px;
125
+ }
126
+
127
+ .file-item,
128
+ .session-item {
129
+ padding: 10px 15px;
130
+ border-radius: 8px;
131
+ margin-bottom: 5px;
132
+ cursor: pointer;
133
+ transition: background 0.2s;
134
+ font-size: 0.9rem;
135
+ }
136
+
137
+ .file-item:hover,
138
+ .session-item:hover {
139
+ background: #f0f0f0;
140
+ }
141
+
142
+ .file-item.directory {
143
+ font-weight: 600;
144
+ color: #667eea;
145
+ }
146
+
147
+ .session-item {
148
+ border-left: 3px solid #667eea;
149
+ }
150
+
151
+ .session-name {
152
+ font-weight: 600;
153
+ margin-bottom: 4px;
154
+ }
155
+
156
+ .session-meta {
157
+ font-size: 0.8rem;
158
+ color: #666;
159
+ }
160
+
161
+ .empty-state {
162
+ text-align: center;
163
+ color: #999;
164
+ padding: 20px;
165
+ font-size: 0.9rem;
166
+ }
167
+
168
+ .main-content {
169
+ flex: 1;
170
+ display: flex;
171
+ flex-direction: column;
172
+ background: white;
173
+ border-radius: 20px;
174
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
175
+ overflow: hidden;
176
+ }
177
+
178
+ header {
179
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
180
+ color: white;
181
+ padding: 30px;
182
+ text-align: center;
183
+ }
184
+
185
+ header h1 {
186
+ font-size: 2.5rem;
187
+ margin-bottom: 10px;
188
+ }
189
+
190
+ .subtitle {
191
+ opacity: 0.9;
192
+ font-size: 1.1rem;
193
+ }
194
+
195
+ #chat-container {
196
+ flex: 1;
197
+ overflow-y: auto;
198
+ padding: 30px;
199
+ background: #f8f9fa;
200
+ }
201
+
202
+ #messages {
203
+ display: flex;
204
+ flex-direction: column;
205
+ gap: 15px;
206
+ }
207
+
208
+ .message {
209
+ padding: 15px 20px;
210
+ border-radius: 15px;
211
+ max-width: 80%;
212
+ animation: slideIn 0.3s ease;
213
+ white-space: pre-wrap;
214
+ }
215
+
216
+ @keyframes slideIn {
217
+ from {
218
+ opacity: 0;
219
+ transform: translateY(10px);
220
+ }
221
+ to {
222
+ opacity: 1;
223
+ transform: translateY(0);
224
+ }
225
+ }
226
+
227
+ .message.user {
228
+ background: #667eea;
229
+ color: white;
230
+ align-self: flex-end;
231
+ margin-left: auto;
232
+ }
233
+
234
+ .message.assistant {
235
+ background: white;
236
+ color: #333;
237
+ align-self: flex-start;
238
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
239
+ }
240
+
241
+ .message.tool {
242
+ background: #fff3cd;
243
+ color: #856404;
244
+ align-self: flex-start;
245
+ font-size: 0.9rem;
246
+ font-family: monospace;
247
+ }
248
+
249
+ .message.error {
250
+ background: #f8d7da;
251
+ color: #721c24;
252
+ align-self: flex-start;
253
+ }
254
+
255
+ .input-container {
256
+ padding: 20px;
257
+ background: white;
258
+ border-top: 1px solid #e9ecef;
259
+ position: relative;
260
+ }
261
+
262
+ .autocomplete {
263
+ position: absolute;
264
+ bottom: 100%;
265
+ left: 20px;
266
+ right: 20px;
267
+ background: white;
268
+ border: 2px solid #667eea;
269
+ border-radius: 10px;
270
+ box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.1);
271
+ max-height: 200px;
272
+ overflow-y: auto;
273
+ margin-bottom: 10px;
274
+ }
275
+
276
+ .autocomplete.hidden {
277
+ display: none;
278
+ }
279
+
280
+ .autocomplete-item {
281
+ padding: 12px 15px;
282
+ cursor: pointer;
283
+ transition: background 0.2s;
284
+ border-bottom: 1px solid #f0f0f0;
285
+ }
286
+
287
+ .autocomplete-item:last-child {
288
+ border-bottom: none;
289
+ }
290
+
291
+ .autocomplete-item:hover {
292
+ background: #f8f9fa;
293
+ }
294
+
295
+ .autocomplete-item strong {
296
+ color: #667eea;
297
+ font-size: 0.95rem;
298
+ }
299
+
300
+ .input-container > div:not(.autocomplete) {
301
+ display: flex;
302
+ gap: 15px;
303
+ }
304
+
305
+ #prompt-input {
306
+ flex: 1;
307
+ padding: 15px;
308
+ border: 2px solid #e9ecef;
309
+ border-radius: 10px;
310
+ font-size: 1rem;
311
+ font-family: inherit;
312
+ resize: none;
313
+ transition: border-color 0.3s;
314
+ }
315
+
316
+ #prompt-input:focus {
317
+ outline: none;
318
+ border-color: #667eea;
319
+ }
320
+
321
+ #send-btn {
322
+ padding: 15px 30px;
323
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
324
+ color: white;
325
+ border: none;
326
+ border-radius: 10px;
327
+ font-size: 1rem;
328
+ font-weight: 600;
329
+ cursor: pointer;
330
+ transition:
331
+ transform 0.2s,
332
+ box-shadow 0.2s;
333
+ }
334
+
335
+ #send-btn:hover {
336
+ transform: translateY(-2px);
337
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
338
+ }
339
+
340
+ #send-btn:active {
341
+ transform: translateY(0);
342
+ }
343
+
344
+ #send-btn:disabled {
345
+ opacity: 0.5;
346
+ cursor: not-allowed;
347
+ }
348
+
349
+ .status {
350
+ padding: 10px 20px;
351
+ text-align: center;
352
+ font-size: 0.9rem;
353
+ color: #6c757d;
354
+ background: #e9ecef;
355
+ }
356
+
357
+ .status.connected {
358
+ background: #d4edda;
359
+ color: #155724;
360
+ }
361
+
362
+ .status.disconnected {
363
+ background: #f8d7da;
364
+ color: #721c24;
365
+ }
366
+
367
+ /* Disconnected banner styles */
368
+ .disconnected-banner {
369
+ position: fixed;
370
+ top: 0;
371
+ left: 0;
372
+ right: 0;
373
+ background: linear-gradient(135deg, #fff3cd 0%, #ffeeba 100%);
374
+ border-bottom: 2px solid #ffc107;
375
+ color: #856404;
376
+ padding: 20px;
377
+ z-index: 1000;
378
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
379
+ animation: slideDown 0.3s ease;
380
+ }
381
+
382
+ .disconnected-banner .banner-content {
383
+ max-width: 800px;
384
+ margin: 0 auto;
385
+ }
386
+
387
+ .disconnected-banner strong {
388
+ display: block;
389
+ font-size: 1.2rem;
390
+ margin-bottom: 10px;
391
+ }
392
+
393
+ .disconnected-banner p {
394
+ margin: 8px 0;
395
+ line-height: 1.5;
396
+ }
397
+
398
+ .disconnected-banner ol {
399
+ margin: 10px 0;
400
+ padding-left: 25px;
401
+ }
402
+
403
+ .disconnected-banner li {
404
+ margin: 8px 0;
405
+ }
406
+
407
+ .disconnected-banner code {
408
+ background: rgba(0, 0, 0, 0.1);
409
+ padding: 3px 8px;
410
+ border-radius: 4px;
411
+ font-family: monospace;
412
+ font-size: 0.95rem;
413
+ }
414
+
415
+ .disconnected-banner a {
416
+ color: #667eea;
417
+ font-weight: 600;
418
+ text-decoration: none;
419
+ }
420
+
421
+ .disconnected-banner a:hover {
422
+ text-decoration: underline;
423
+ }
424
+
425
+ .disconnected-banner .note {
426
+ font-size: 0.9rem;
427
+ opacity: 0.8;
428
+ margin-top: 12px;
429
+ }
430
+
431
+ .disconnected-banner .dismiss-btn {
432
+ float: right;
433
+ padding: 6px 12px;
434
+ background: rgba(0, 0, 0, 0.1);
435
+ border: 1px solid rgba(0, 0, 0, 0.2);
436
+ border-radius: 6px;
437
+ cursor: pointer;
438
+ font-weight: 500;
439
+ transition: background 0.2s;
440
+ }
441
+
442
+ .disconnected-banner .dismiss-btn:hover {
443
+ background: rgba(0, 0, 0, 0.2);
444
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@involvex/super-agent-cli",
3
- "version": "0.0.80",
3
+ "version": "0.0.82",
4
4
  "description": "An open-source AI agent that brings the power of Super Agent directly into your terminal.",
5
5
  "keywords": [
6
6
  "cli",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "main": "dist/index.js",
30
30
  "bin": {
31
- "super-agent": "dist/index.js"
31
+ "super-agent": "super-agent.js"
32
32
  },
33
33
  "workspaces": [
34
34
  "@plugins/templates/*",
@@ -37,13 +37,14 @@
37
37
  ],
38
38
  "scripts": {
39
39
  "prebuild": "bun run format && bun run lint:fix && bun run typecheck",
40
- "build": "bun build src/index.ts --outdir ./dist --target node --packages external --format esm",
40
+ "build": "bun build src/index.ts --outdir ./dist --target node --packages external --format esm && bun run copy-bin && bun run build:web",
41
41
  "build:bun": "bun build src/index.ts --outdir ./dist --target node --packages external --format esm",
42
42
  "build:plugins": "bun run -F @involvex/super-agent\\* build",
43
43
  "build:vscode": "bun run -F super-agent-vscode build",
44
44
  "build:web": "bun run scripts/build-web.ts",
45
45
  "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
46
46
  "compile": "bun run prebuild && bun build --compile src/index.ts --outfile ./dist/super-agent-cli.exe --config bunfig.toml",
47
+ "copy-bin": "cp super-agent.js dist/super-agent.js",
47
48
  "dev": "bun run --watch src/index.ts",
48
49
  "dev:node": "tsx src/index.ts",
49
50
  "format": "prettier --write .",
@@ -52,8 +53,8 @@
52
53
  "lint": "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern dist",
53
54
  "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix --ignore-pattern dist",
54
55
  "package:vscode": "bun run -F super-agent-vscode package",
55
- "start": "node dist/index.js",
56
- "start:bun": "bun run dist/index.js",
56
+ "start": "bun run dist/index.js",
57
+ "start:node": "node dist/index.js",
57
58
  "test": "vitest run",
58
59
  "test:coverage": "vitest run --coverage",
59
60
  "test:integration": "vitest run src/tests/integration",
package/super-agent.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bun
2
+ // Super Agent CLI wrapper script
3
+ // This ensures the bundled code runs with bun for proper module resolution
4
+
5
+ import { dirname, join } from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // Import and run the bundled CLI
12
+ import(join(__dirname, "dist", "index.js"));
@@ -62,6 +62,8 @@ You can:
62
62
 
63
63
  ## Development
64
64
 
65
+ **Note**: This extension uses `npm` for dependency management (required by `vsce` packaging tool), while the root project uses `bun`.
66
+
65
67
  ```bash
66
68
  # Install dependencies
67
69
  npm install
@@ -24,4 +24,35 @@ for (const file of assetsToCopy) {
24
24
  }
25
25
  }
26
26
 
27
+ // Copy ws dependency to out/node_modules for packaging
28
+ const wsSrc = path.join(__dirname, "node_modules", "ws");
29
+ const wsDest = path.join(outDir, "node_modules", "ws");
30
+
31
+ if (fs.existsSync(wsSrc)) {
32
+ // Copy recursively
33
+ copyDirectory(wsSrc, wsDest);
34
+ console.log("Copied ws to out/node_modules/");
35
+ } else {
36
+ console.warn("ws not found in node_modules");
37
+ }
38
+
39
+ function copyDirectory(src, dest) {
40
+ if (!fs.existsSync(dest)) {
41
+ fs.mkdirSync(dest, { recursive: true });
42
+ }
43
+
44
+ const entries = fs.readdirSync(src, { withFileTypes: true });
45
+
46
+ for (const entry of entries) {
47
+ const srcPath = path.join(src, entry.name);
48
+ const destPath = path.join(dest, entry.name);
49
+
50
+ if (entry.isDirectory()) {
51
+ copyDirectory(srcPath, destPath);
52
+ } else {
53
+ fs.copyFileSync(srcPath, destPath);
54
+ }
55
+ }
56
+ }
57
+
27
58
  // Note: icon.png is already in the extension root, no need to copy
@@ -33,6 +33,11 @@
33
33
  "title": "Open Super Agent Chat",
34
34
  "icon": "icon.png"
35
35
  },
36
+ {
37
+ "command": "super-agent.reconnect",
38
+ "title": "Reconnect to CLI",
39
+ "icon": "icon.png"
40
+ },
36
41
  {
37
42
  "command": "super-agent.mentionFile",
38
43
  "title": "Mention File in Chat"