@involvex/super-agent-cli 0.0.79 → 0.0.81
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/.prettierignore +3 -1
- package/assets/images/banner.png +0 -0
- package/assets/images/favicon.png +0 -0
- package/assets/images/logo.png +0 -0
- package/dist/index.js +28 -21
- package/dist/super-agent-cli.exe +0 -0
- package/dist/super-agent.js +12 -0
- package/dist/web/app.js +418 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/index.html +71 -0
- package/dist/web/logo.png +0 -0
- package/dist/web/styles.css +444 -0
- package/eslint.config.mjs +1 -0
- package/package.json +10 -6
- package/super-agent.js +12 -0
- package/vscode-extension/.vscodeignore +39 -0
- package/vscode-extension/LICENSE +21 -0
- package/vscode-extension/README.md +2 -0
- package/vscode-extension/build.js +37 -4
- package/vscode-extension/icon.png +0 -0
- package/vscode-extension/package.json +36 -3
- package/.kilocode/mcp.json +0 -15
- /package/{vscode-extension/assets → assets/vscode}/icon.svg +0 -0
package/.prettierignore
CHANGED
|
Binary file
|
|
Binary file
|
|
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.
|
|
1734
|
+
version: "0.0.81",
|
|
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,20 +1759,23 @@ var require_package = __commonJS((exports, module) => {
|
|
|
1759
1759
|
},
|
|
1760
1760
|
main: "dist/index.js",
|
|
1761
1761
|
bin: {
|
|
1762
|
-
"super-agent": "
|
|
1762
|
+
"super-agent": "super-agent.js"
|
|
1763
1763
|
},
|
|
1764
1764
|
workspaces: [
|
|
1765
1765
|
"@plugins/templates/*",
|
|
1766
|
-
"@plugins/examples/*"
|
|
1766
|
+
"@plugins/examples/*",
|
|
1767
|
+
"vscode-extension"
|
|
1767
1768
|
],
|
|
1768
1769
|
scripts: {
|
|
1769
1770
|
prebuild: "bun run format && bun run lint:fix && bun run typecheck",
|
|
1770
|
-
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",
|
|
1771
1772
|
"build:bun": "bun build src/index.ts --outdir ./dist --target node --packages external --format esm",
|
|
1772
1773
|
"build:plugins": "bun run -F @involvex/super-agent\\* build",
|
|
1774
|
+
"build:vscode": "bun run -F super-agent-vscode build",
|
|
1773
1775
|
"build:web": "bun run scripts/build-web.ts",
|
|
1774
1776
|
changelog: "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
|
1775
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",
|
|
1776
1779
|
dev: "bun run --watch src/index.ts",
|
|
1777
1780
|
"dev:node": "tsx src/index.ts",
|
|
1778
1781
|
format: "prettier --write .",
|
|
@@ -1780,8 +1783,9 @@ var require_package = __commonJS((exports, module) => {
|
|
|
1780
1783
|
"install:bun": "bun install",
|
|
1781
1784
|
lint: "eslint . --ext .js,.jsx,.ts,.tsx --ignore-pattern dist",
|
|
1782
1785
|
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix --ignore-pattern dist",
|
|
1783
|
-
|
|
1784
|
-
|
|
1786
|
+
"package:vscode": "bun run -F super-agent-vscode package",
|
|
1787
|
+
start: "bun run dist/index.js",
|
|
1788
|
+
"start:node": "node dist/index.js",
|
|
1785
1789
|
test: "vitest run",
|
|
1786
1790
|
"test:coverage": "vitest run --coverage",
|
|
1787
1791
|
"test:integration": "vitest run src/tests/integration",
|
|
@@ -2905,8 +2909,8 @@ init_config();
|
|
|
2905
2909
|
init_settings_manager();
|
|
2906
2910
|
import { exec as exec2 } from "child_process";
|
|
2907
2911
|
import { promisify as promisify2 } from "util";
|
|
2908
|
-
import * as fs4 from "fs-extra";
|
|
2909
2912
|
import * as path4 from "path";
|
|
2913
|
+
import fs4 from "fs-extra";
|
|
2910
2914
|
var execAsync2 = promisify2(exec2);
|
|
2911
2915
|
|
|
2912
2916
|
class PluginManager {
|
|
@@ -3471,8 +3475,8 @@ async function getAllSuperAgentTools() {
|
|
|
3471
3475
|
// src/plugins/repository-manager.ts
|
|
3472
3476
|
import { exec as exec3 } from "child_process";
|
|
3473
3477
|
import { promisify as promisify3 } from "util";
|
|
3474
|
-
import * as fs5 from "fs-extra";
|
|
3475
3478
|
import * as path5 from "path";
|
|
3479
|
+
import fs5 from "fs-extra";
|
|
3476
3480
|
var execAsync3 = promisify3(exec3);
|
|
3477
3481
|
var BUILTIN_REPOSITORIES = {
|
|
3478
3482
|
agents: {
|
|
@@ -3962,7 +3966,7 @@ function getChatManager() {
|
|
|
3962
3966
|
}
|
|
3963
3967
|
|
|
3964
3968
|
// src/hooks/use-input-handler.ts
|
|
3965
|
-
import
|
|
3969
|
+
import fs11 from "fs-extra";
|
|
3966
3970
|
function useInputHandler({
|
|
3967
3971
|
agent,
|
|
3968
3972
|
chatHistory,
|
|
@@ -11708,7 +11712,6 @@ import * as fs20 from "fs-extra";
|
|
|
11708
11712
|
import * as path20 from "path";
|
|
11709
11713
|
import open from "open";
|
|
11710
11714
|
import mime from "mime";
|
|
11711
|
-
var __dirname = "/home/runner/work/super-agent-cli/super-agent-cli/src/web";
|
|
11712
11715
|
|
|
11713
11716
|
class WebServer {
|
|
11714
11717
|
httpServer;
|
|
@@ -11731,20 +11734,24 @@ class WebServer {
|
|
|
11731
11734
|
}
|
|
11732
11735
|
async handleHttpRequest(req, res) {
|
|
11733
11736
|
const url = req.url || "/";
|
|
11734
|
-
|
|
11737
|
+
let requestedPath = url === "/" ? "index.html" : url.substring(1);
|
|
11735
11738
|
const sanitizedPath = requestedPath.split("?")[0].split("#")[0];
|
|
11736
|
-
const
|
|
11737
|
-
const
|
|
11738
|
-
|
|
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)) {
|
|
11739
11746
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
11740
11747
|
res.end("Forbidden");
|
|
11741
11748
|
return;
|
|
11742
11749
|
}
|
|
11743
11750
|
try {
|
|
11744
11751
|
if (await fs20.pathExists(absolutePath)) {
|
|
11745
|
-
const
|
|
11752
|
+
const stat2 = await fs20.stat(absolutePath);
|
|
11746
11753
|
let filePath = absolutePath;
|
|
11747
|
-
if (
|
|
11754
|
+
if (stat2.isDirectory()) {
|
|
11748
11755
|
filePath = path20.join(absolutePath, "index.html");
|
|
11749
11756
|
}
|
|
11750
11757
|
const content = await fs20.readFile(filePath);
|
|
@@ -12146,7 +12153,7 @@ function createIndexCommand() {
|
|
|
12146
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) => {
|
|
12147
12154
|
try {
|
|
12148
12155
|
const rootDir = path21.resolve(directory);
|
|
12149
|
-
const
|
|
12156
|
+
const outputFile = options.output ? path21.resolve(options.output) : path21.join(rootDir, "index.md");
|
|
12150
12157
|
const maxDepth = parseInt(options.depth);
|
|
12151
12158
|
console.log(chalk4.blue(`Indexing directory: ${rootDir}`));
|
|
12152
12159
|
const ig = ignore().add(DEFAULT_IGNORES);
|
|
@@ -12243,8 +12250,8 @@ Date: ${new Date().toISOString()}
|
|
|
12243
12250
|
|
|
12244
12251
|
`;
|
|
12245
12252
|
}
|
|
12246
|
-
await fs21.writeFile(
|
|
12247
|
-
console.log(chalk4.green(`✓ Index generated at: ${
|
|
12253
|
+
await fs21.writeFile(outputFile, outputContent);
|
|
12254
|
+
console.log(chalk4.green(`✓ Index generated at: ${outputFile}`));
|
|
12248
12255
|
} catch (error) {
|
|
12249
12256
|
console.error(chalk4.red(`Error indexing directory: ${error.message}`));
|
|
12250
12257
|
process.exit(1);
|
package/dist/super-agent-cli.exe
CHANGED
|
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"));
|
package/dist/web/app.js
ADDED
|
@@ -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, "&")
|
|
32
|
+
.replace(/</g, "<")
|
|
33
|
+
.replace(/>/g, ">")
|
|
34
|
+
.replace(/"/g, """)
|
|
35
|
+
.replace(/'/g, "'");
|
|
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
|