@siteboon/claude-code-ui 1.10.4 → 1.11.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 +55 -17
- package/dist/assets/index-B4_v-YUz.css +32 -0
- package/dist/assets/index-BZX1vtg9.js +932 -0
- package/dist/index.html +2 -2
- package/package.json +3 -2
- package/server/claude-sdk.js +11 -1
- package/server/cli.js +225 -0
- package/server/database/db.js +26 -1
- package/server/index.js +59 -30
- package/server/routes/git.js +9 -9
- package/dist/assets/index-BHQThXog.css +0 -32
- package/dist/assets/index-CJDQ69DP.js +0 -913
package/dist/index.html
CHANGED
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
|
|
26
26
|
<!-- Prevent zoom on iOS -->
|
|
27
27
|
<meta name="format-detection" content="telephone=no" />
|
|
28
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-BZX1vtg9.js"></script>
|
|
29
29
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-7V_UDHjJ.js">
|
|
30
30
|
<link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-B7BYDWj-.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="/assets/vendor-xterm-jI4BCHEb.js">
|
|
32
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
32
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B4_v-YUz.css">
|
|
33
33
|
</head>
|
|
34
34
|
<body>
|
|
35
35
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siteboon/claude-code-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "A web-based UI for Claude Code CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"claude-code-ui": "server/
|
|
8
|
+
"claude-code-ui": "server/cli.js",
|
|
9
|
+
"cloudcli": "server/cli.js"
|
|
9
10
|
},
|
|
10
11
|
"files": [
|
|
11
12
|
"server/",
|
package/server/claude-sdk.js
CHANGED
|
@@ -79,6 +79,16 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
79
79
|
// Map model (default to sonnet)
|
|
80
80
|
sdkOptions.model = options.model || 'sonnet';
|
|
81
81
|
|
|
82
|
+
// Map system prompt configuration
|
|
83
|
+
sdkOptions.systemPrompt = {
|
|
84
|
+
type: 'preset',
|
|
85
|
+
preset: 'claude_code' // Required to use CLAUDE.md
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Map setting sources for CLAUDE.md loading
|
|
89
|
+
// This loads CLAUDE.md from project, user (~/.config/claude/CLAUDE.md), and local directories
|
|
90
|
+
sdkOptions.settingSources = ['project', 'user', 'local'];
|
|
91
|
+
|
|
82
92
|
// Map resume session
|
|
83
93
|
if (sessionId) {
|
|
84
94
|
sdkOptions.resume = sessionId;
|
|
@@ -374,7 +384,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
374
384
|
for await (const message of queryInstance) {
|
|
375
385
|
// Capture session ID from first message
|
|
376
386
|
if (message.session_id && !capturedSessionId) {
|
|
377
|
-
|
|
387
|
+
|
|
378
388
|
capturedSessionId = message.session_id;
|
|
379
389
|
addSession(capturedSessionId, queryInstance, tempImagePaths, tempDir);
|
|
380
390
|
|
package/server/cli.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Claude Code UI CLI
|
|
4
|
+
*
|
|
5
|
+
* Provides command-line utilities for managing Claude Code UI
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* (no args) - Start the server (default)
|
|
9
|
+
* start - Start the server
|
|
10
|
+
* status - Show configuration and data locations
|
|
11
|
+
* help - Show help information
|
|
12
|
+
* version - Show version information
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
import { dirname } from 'path';
|
|
19
|
+
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = dirname(__filename);
|
|
22
|
+
|
|
23
|
+
// ANSI color codes for terminal output
|
|
24
|
+
const colors = {
|
|
25
|
+
reset: '\x1b[0m',
|
|
26
|
+
bright: '\x1b[1m',
|
|
27
|
+
dim: '\x1b[2m',
|
|
28
|
+
|
|
29
|
+
// Foreground colors
|
|
30
|
+
cyan: '\x1b[36m',
|
|
31
|
+
green: '\x1b[32m',
|
|
32
|
+
yellow: '\x1b[33m',
|
|
33
|
+
blue: '\x1b[34m',
|
|
34
|
+
magenta: '\x1b[35m',
|
|
35
|
+
white: '\x1b[37m',
|
|
36
|
+
gray: '\x1b[90m',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Helper to colorize text
|
|
40
|
+
const c = {
|
|
41
|
+
info: (text) => `${colors.cyan}${text}${colors.reset}`,
|
|
42
|
+
ok: (text) => `${colors.green}${text}${colors.reset}`,
|
|
43
|
+
warn: (text) => `${colors.yellow}${text}${colors.reset}`,
|
|
44
|
+
error: (text) => `${colors.yellow}${text}${colors.reset}`,
|
|
45
|
+
tip: (text) => `${colors.blue}${text}${colors.reset}`,
|
|
46
|
+
bright: (text) => `${colors.bright}${text}${colors.reset}`,
|
|
47
|
+
dim: (text) => `${colors.dim}${text}${colors.reset}`,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Load package.json for version info
|
|
51
|
+
const packageJsonPath = path.join(__dirname, '../package.json');
|
|
52
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
53
|
+
|
|
54
|
+
// Load environment variables from .env file if it exists
|
|
55
|
+
function loadEnvFile() {
|
|
56
|
+
try {
|
|
57
|
+
const envPath = path.join(__dirname, '../.env');
|
|
58
|
+
const envFile = fs.readFileSync(envPath, 'utf8');
|
|
59
|
+
envFile.split('\n').forEach(line => {
|
|
60
|
+
const trimmedLine = line.trim();
|
|
61
|
+
if (trimmedLine && !trimmedLine.startsWith('#')) {
|
|
62
|
+
const [key, ...valueParts] = trimmedLine.split('=');
|
|
63
|
+
if (key && valueParts.length > 0 && !process.env[key]) {
|
|
64
|
+
process.env[key] = valueParts.join('=').trim();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// .env file is optional
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Get the database path (same logic as db.js)
|
|
74
|
+
function getDatabasePath() {
|
|
75
|
+
loadEnvFile();
|
|
76
|
+
return process.env.DATABASE_PATH || path.join(__dirname, 'database', 'auth.db');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Get the installation directory
|
|
80
|
+
function getInstallDir() {
|
|
81
|
+
return path.join(__dirname, '..');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Show status command
|
|
85
|
+
function showStatus() {
|
|
86
|
+
console.log(`\n${c.bright('Claude Code UI - Status')}\n`);
|
|
87
|
+
console.log(c.dim('═'.repeat(60)));
|
|
88
|
+
|
|
89
|
+
// Version info
|
|
90
|
+
console.log(`\n${c.info('[INFO]')} Version: ${c.bright(packageJson.version)}`);
|
|
91
|
+
|
|
92
|
+
// Installation location
|
|
93
|
+
const installDir = getInstallDir();
|
|
94
|
+
console.log(`\n${c.info('[INFO]')} Installation Directory:`);
|
|
95
|
+
console.log(` ${c.dim(installDir)}`);
|
|
96
|
+
|
|
97
|
+
// Database location
|
|
98
|
+
const dbPath = getDatabasePath();
|
|
99
|
+
const dbExists = fs.existsSync(dbPath);
|
|
100
|
+
console.log(`\n${c.info('[INFO]')} Database Location:`);
|
|
101
|
+
console.log(` ${c.dim(dbPath)}`);
|
|
102
|
+
console.log(` Status: ${dbExists ? c.ok('[OK] Exists') : c.warn('[WARN] Not created yet (will be created on first run)')}`);
|
|
103
|
+
|
|
104
|
+
if (dbExists) {
|
|
105
|
+
const stats = fs.statSync(dbPath);
|
|
106
|
+
console.log(` Size: ${c.dim((stats.size / 1024).toFixed(2) + ' KB')}`);
|
|
107
|
+
console.log(` Modified: ${c.dim(stats.mtime.toLocaleString())}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Environment variables
|
|
111
|
+
console.log(`\n${c.info('[INFO]')} Configuration:`);
|
|
112
|
+
console.log(` PORT: ${c.bright(process.env.PORT || '3001')} ${c.dim(process.env.PORT ? '' : '(default)')}`);
|
|
113
|
+
console.log(` DATABASE_PATH: ${c.dim(process.env.DATABASE_PATH || '(using default location)')}`);
|
|
114
|
+
console.log(` CLAUDE_CLI_PATH: ${c.dim(process.env.CLAUDE_CLI_PATH || 'claude (default)')}`);
|
|
115
|
+
console.log(` CONTEXT_WINDOW: ${c.dim(process.env.CONTEXT_WINDOW || '160000 (default)')}`);
|
|
116
|
+
|
|
117
|
+
// Claude projects folder
|
|
118
|
+
const claudeProjectsPath = path.join(process.env.HOME, '.claude', 'projects');
|
|
119
|
+
const projectsExists = fs.existsSync(claudeProjectsPath);
|
|
120
|
+
console.log(`\n${c.info('[INFO]')} Claude Projects Folder:`);
|
|
121
|
+
console.log(` ${c.dim(claudeProjectsPath)}`);
|
|
122
|
+
console.log(` Status: ${projectsExists ? c.ok('[OK] Exists') : c.warn('[WARN] Not found')}`);
|
|
123
|
+
|
|
124
|
+
// Config file location
|
|
125
|
+
const envFilePath = path.join(__dirname, '../.env');
|
|
126
|
+
const envExists = fs.existsSync(envFilePath);
|
|
127
|
+
console.log(`\n${c.info('[INFO]')} Configuration File:`);
|
|
128
|
+
console.log(` ${c.dim(envFilePath)}`);
|
|
129
|
+
console.log(` Status: ${envExists ? c.ok('[OK] Exists') : c.warn('[WARN] Not found (using defaults)')}`);
|
|
130
|
+
|
|
131
|
+
console.log('\n' + c.dim('═'.repeat(60)));
|
|
132
|
+
console.log(`\n${c.tip('[TIP]')} Hints:`);
|
|
133
|
+
console.log(` ${c.dim('>')} Set DATABASE_PATH env variable to use a custom database location`);
|
|
134
|
+
console.log(` ${c.dim('>')} Create .env file in installation directory for persistent config`);
|
|
135
|
+
console.log(` ${c.dim('>')} Run "claude-code-ui" or "cloudcli start" to start the server`);
|
|
136
|
+
console.log(` ${c.dim('>')} Access the UI at http://localhost:3001 (or custom PORT)\n`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Show help
|
|
140
|
+
function showHelp() {
|
|
141
|
+
console.log(`
|
|
142
|
+
╔═══════════════════════════════════════════════════════════════╗
|
|
143
|
+
║ Claude Code UI - Command Line Tool ║
|
|
144
|
+
╚═══════════════════════════════════════════════════════════════╝
|
|
145
|
+
|
|
146
|
+
Usage:
|
|
147
|
+
claude-code-ui [command]
|
|
148
|
+
cloudcli [command]
|
|
149
|
+
|
|
150
|
+
Commands:
|
|
151
|
+
start Start the Claude Code UI server (default)
|
|
152
|
+
status Show configuration and data locations
|
|
153
|
+
help Show this help information
|
|
154
|
+
version Show version information
|
|
155
|
+
|
|
156
|
+
Examples:
|
|
157
|
+
$ claude-code-ui # Start the server
|
|
158
|
+
$ cloudcli status # Show configuration
|
|
159
|
+
$ cloudcli help # Show help
|
|
160
|
+
|
|
161
|
+
Environment Variables:
|
|
162
|
+
PORT Set server port (default: 3001)
|
|
163
|
+
DATABASE_PATH Set custom database location
|
|
164
|
+
CLAUDE_CLI_PATH Set custom Claude CLI path
|
|
165
|
+
CONTEXT_WINDOW Set context window size (default: 160000)
|
|
166
|
+
|
|
167
|
+
Configuration:
|
|
168
|
+
Create a .env file in the installation directory to set
|
|
169
|
+
persistent environment variables. Use 'cloudcli status' to
|
|
170
|
+
see the installation directory path.
|
|
171
|
+
|
|
172
|
+
Documentation:
|
|
173
|
+
${packageJson.homepage || 'https://github.com/siteboon/claudecodeui'}
|
|
174
|
+
|
|
175
|
+
Report Issues:
|
|
176
|
+
${packageJson.bugs?.url || 'https://github.com/siteboon/claudecodeui/issues'}
|
|
177
|
+
`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Show version
|
|
181
|
+
function showVersion() {
|
|
182
|
+
console.log(`${packageJson.version}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Start the server
|
|
186
|
+
async function startServer() {
|
|
187
|
+
// Import and run the server
|
|
188
|
+
await import('./index.js');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Main CLI handler
|
|
192
|
+
async function main() {
|
|
193
|
+
const args = process.argv.slice(2);
|
|
194
|
+
const command = args[0] || 'start';
|
|
195
|
+
|
|
196
|
+
switch (command) {
|
|
197
|
+
case 'start':
|
|
198
|
+
await startServer();
|
|
199
|
+
break;
|
|
200
|
+
case 'status':
|
|
201
|
+
case 'info':
|
|
202
|
+
showStatus();
|
|
203
|
+
break;
|
|
204
|
+
case 'help':
|
|
205
|
+
case '-h':
|
|
206
|
+
case '--help':
|
|
207
|
+
showHelp();
|
|
208
|
+
break;
|
|
209
|
+
case 'version':
|
|
210
|
+
case '-v':
|
|
211
|
+
case '--version':
|
|
212
|
+
showVersion();
|
|
213
|
+
break;
|
|
214
|
+
default:
|
|
215
|
+
console.error(`\n❌ Unknown command: ${command}`);
|
|
216
|
+
console.log(' Run "cloudcli help" for usage information.\n');
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Run the CLI
|
|
222
|
+
main().catch(error => {
|
|
223
|
+
console.error('\n❌ Error:', error.message);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
});
|
package/server/database/db.js
CHANGED
|
@@ -8,6 +8,20 @@ import { dirname } from 'path';
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
10
|
|
|
11
|
+
// ANSI color codes for terminal output
|
|
12
|
+
const colors = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bright: '\x1b[1m',
|
|
15
|
+
cyan: '\x1b[36m',
|
|
16
|
+
dim: '\x1b[2m',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const c = {
|
|
20
|
+
info: (text) => `${colors.cyan}${text}${colors.reset}`,
|
|
21
|
+
bright: (text) => `${colors.bright}${text}${colors.reset}`,
|
|
22
|
+
dim: (text) => `${colors.dim}${text}${colors.reset}`,
|
|
23
|
+
};
|
|
24
|
+
|
|
11
25
|
// Use DATABASE_PATH environment variable if set, otherwise use default location
|
|
12
26
|
const DB_PATH = process.env.DATABASE_PATH || path.join(__dirname, 'auth.db');
|
|
13
27
|
const INIT_SQL_PATH = path.join(__dirname, 'init.sql');
|
|
@@ -28,7 +42,18 @@ if (process.env.DATABASE_PATH) {
|
|
|
28
42
|
|
|
29
43
|
// Create database connection
|
|
30
44
|
const db = new Database(DB_PATH);
|
|
31
|
-
|
|
45
|
+
|
|
46
|
+
// Show app installation path prominently
|
|
47
|
+
const appInstallPath = path.join(__dirname, '../..');
|
|
48
|
+
console.log('');
|
|
49
|
+
console.log(c.dim('═'.repeat(60)));
|
|
50
|
+
console.log(`${c.info('[INFO]')} App Installation: ${c.bright(appInstallPath)}`);
|
|
51
|
+
console.log(`${c.info('[INFO]')} Database: ${c.dim(path.relative(appInstallPath, DB_PATH))}`);
|
|
52
|
+
if (process.env.DATABASE_PATH) {
|
|
53
|
+
console.log(` ${c.dim('(Using custom DATABASE_PATH from environment)')}`);
|
|
54
|
+
}
|
|
55
|
+
console.log(c.dim('═'.repeat(60)));
|
|
56
|
+
console.log('');
|
|
32
57
|
|
|
33
58
|
// Initialize database with schema
|
|
34
59
|
const initializeDatabase = async () => {
|
package/server/index.js
CHANGED
|
@@ -8,6 +8,26 @@ import { dirname } from 'path';
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
10
|
|
|
11
|
+
// ANSI color codes for terminal output
|
|
12
|
+
const colors = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bright: '\x1b[1m',
|
|
15
|
+
cyan: '\x1b[36m',
|
|
16
|
+
green: '\x1b[32m',
|
|
17
|
+
yellow: '\x1b[33m',
|
|
18
|
+
blue: '\x1b[34m',
|
|
19
|
+
dim: '\x1b[2m',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const c = {
|
|
23
|
+
info: (text) => `${colors.cyan}${text}${colors.reset}`,
|
|
24
|
+
ok: (text) => `${colors.green}${text}${colors.reset}`,
|
|
25
|
+
warn: (text) => `${colors.yellow}${text}${colors.reset}`,
|
|
26
|
+
tip: (text) => `${colors.blue}${text}${colors.reset}`,
|
|
27
|
+
bright: (text) => `${colors.bright}${text}${colors.reset}`,
|
|
28
|
+
dim: (text) => `${colors.dim}${text}${colors.reset}`,
|
|
29
|
+
};
|
|
30
|
+
|
|
11
31
|
try {
|
|
12
32
|
const envPath = path.join(__dirname, '../.env');
|
|
13
33
|
const envFile = fs.readFileSync(envPath, 'utf8');
|
|
@@ -116,7 +136,7 @@ async function setupProjectsWatcher() {
|
|
|
116
136
|
});
|
|
117
137
|
|
|
118
138
|
} catch (error) {
|
|
119
|
-
console.error('
|
|
139
|
+
console.error('[ERROR] Error handling project changes:', error);
|
|
120
140
|
}
|
|
121
141
|
}, 300); // 300ms debounce (slightly faster than before)
|
|
122
142
|
};
|
|
@@ -129,13 +149,13 @@ async function setupProjectsWatcher() {
|
|
|
129
149
|
.on('addDir', (dirPath) => debouncedUpdate('addDir', dirPath))
|
|
130
150
|
.on('unlinkDir', (dirPath) => debouncedUpdate('unlinkDir', dirPath))
|
|
131
151
|
.on('error', (error) => {
|
|
132
|
-
console.error('
|
|
152
|
+
console.error('[ERROR] Chokidar watcher error:', error);
|
|
133
153
|
})
|
|
134
154
|
.on('ready', () => {
|
|
135
155
|
});
|
|
136
156
|
|
|
137
157
|
} catch (error) {
|
|
138
|
-
console.error('
|
|
158
|
+
console.error('[ERROR] Failed to setup projects watcher:', error);
|
|
139
159
|
}
|
|
140
160
|
}
|
|
141
161
|
|
|
@@ -157,13 +177,13 @@ const wss = new WebSocketServer({
|
|
|
157
177
|
// Verify token
|
|
158
178
|
const user = authenticateWebSocket(token);
|
|
159
179
|
if (!user) {
|
|
160
|
-
console.log('
|
|
180
|
+
console.log('[WARN] WebSocket authentication failed');
|
|
161
181
|
return false;
|
|
162
182
|
}
|
|
163
183
|
|
|
164
184
|
// Store user info in the request for later use
|
|
165
185
|
info.req.user = user;
|
|
166
|
-
console.log('
|
|
186
|
+
console.log('[OK] WebSocket authenticated for user:', user.username);
|
|
167
187
|
return true;
|
|
168
188
|
}
|
|
169
189
|
});
|
|
@@ -462,7 +482,7 @@ app.get('/api/projects/:projectName/file', authenticateToken, async (req, res) =
|
|
|
462
482
|
const { projectName } = req.params;
|
|
463
483
|
const { filePath } = req.query;
|
|
464
484
|
|
|
465
|
-
console.log('
|
|
485
|
+
console.log('[DEBUG] File read request:', projectName, filePath);
|
|
466
486
|
|
|
467
487
|
// Security: ensure the requested path is inside the project root
|
|
468
488
|
if (!filePath) {
|
|
@@ -503,7 +523,7 @@ app.get('/api/projects/:projectName/files/content', authenticateToken, async (re
|
|
|
503
523
|
const { projectName } = req.params;
|
|
504
524
|
const { path: filePath } = req.query;
|
|
505
525
|
|
|
506
|
-
console.log('
|
|
526
|
+
console.log('[DEBUG] Binary file serve request:', projectName, filePath);
|
|
507
527
|
|
|
508
528
|
// Security: ensure the requested path is inside the project root
|
|
509
529
|
if (!filePath) {
|
|
@@ -557,7 +577,7 @@ app.put('/api/projects/:projectName/file', authenticateToken, async (req, res) =
|
|
|
557
577
|
const { projectName } = req.params;
|
|
558
578
|
const { filePath, content } = req.body;
|
|
559
579
|
|
|
560
|
-
console.log('
|
|
580
|
+
console.log('[DEBUG] File save request:', projectName, filePath);
|
|
561
581
|
|
|
562
582
|
// Security: ensure the requested path is inside the project root
|
|
563
583
|
if (!filePath) {
|
|
@@ -628,7 +648,7 @@ app.get('/api/projects/:projectName/files', authenticateToken, async (req, res)
|
|
|
628
648
|
const hiddenFiles = files.filter(f => f.name.startsWith('.'));
|
|
629
649
|
res.json(files);
|
|
630
650
|
} catch (error) {
|
|
631
|
-
console.error('
|
|
651
|
+
console.error('[ERROR] File tree error:', error.message);
|
|
632
652
|
res.status(500).json({ error: error.message });
|
|
633
653
|
}
|
|
634
654
|
});
|
|
@@ -636,7 +656,7 @@ app.get('/api/projects/:projectName/files', authenticateToken, async (req, res)
|
|
|
636
656
|
// WebSocket connection handler that routes based on URL path
|
|
637
657
|
wss.on('connection', (ws, request) => {
|
|
638
658
|
const url = request.url;
|
|
639
|
-
console.log('
|
|
659
|
+
console.log('[INFO] Client connected to:', url);
|
|
640
660
|
|
|
641
661
|
// Parse URL to get pathname without query parameters
|
|
642
662
|
const urlObj = new URL(url, 'http://localhost');
|
|
@@ -647,14 +667,14 @@ wss.on('connection', (ws, request) => {
|
|
|
647
667
|
} else if (pathname === '/ws') {
|
|
648
668
|
handleChatConnection(ws);
|
|
649
669
|
} else {
|
|
650
|
-
console.log('
|
|
670
|
+
console.log('[WARN] Unknown WebSocket path:', pathname);
|
|
651
671
|
ws.close();
|
|
652
672
|
}
|
|
653
673
|
});
|
|
654
674
|
|
|
655
675
|
// Handle chat WebSocket connections
|
|
656
676
|
function handleChatConnection(ws) {
|
|
657
|
-
console.log('
|
|
677
|
+
console.log('[INFO] Chat WebSocket connected');
|
|
658
678
|
|
|
659
679
|
// Add to connected clients for project updates
|
|
660
680
|
connectedClients.add(ws);
|
|
@@ -664,28 +684,28 @@ function handleChatConnection(ws) {
|
|
|
664
684
|
const data = JSON.parse(message);
|
|
665
685
|
|
|
666
686
|
if (data.type === 'claude-command') {
|
|
667
|
-
console.log('
|
|
687
|
+
console.log('[DEBUG] User message:', data.command || '[Continue/Resume]');
|
|
668
688
|
console.log('📁 Project:', data.options?.projectPath || 'Unknown');
|
|
669
689
|
console.log('🔄 Session:', data.options?.sessionId ? 'Resume' : 'New');
|
|
670
690
|
|
|
671
691
|
// Use Claude Agents SDK
|
|
672
692
|
await queryClaudeSDK(data.command, data.options, ws);
|
|
673
693
|
} else if (data.type === 'cursor-command') {
|
|
674
|
-
console.log('
|
|
694
|
+
console.log('[DEBUG] Cursor message:', data.command || '[Continue/Resume]');
|
|
675
695
|
console.log('📁 Project:', data.options?.cwd || 'Unknown');
|
|
676
696
|
console.log('🔄 Session:', data.options?.sessionId ? 'Resume' : 'New');
|
|
677
697
|
console.log('🤖 Model:', data.options?.model || 'default');
|
|
678
698
|
await spawnCursor(data.command, data.options, ws);
|
|
679
699
|
} else if (data.type === 'cursor-resume') {
|
|
680
700
|
// Backward compatibility: treat as cursor-command with resume and no prompt
|
|
681
|
-
console.log('
|
|
701
|
+
console.log('[DEBUG] Cursor resume session (compat):', data.sessionId);
|
|
682
702
|
await spawnCursor('', {
|
|
683
703
|
sessionId: data.sessionId,
|
|
684
704
|
resume: true,
|
|
685
705
|
cwd: data.options?.cwd
|
|
686
706
|
}, ws);
|
|
687
707
|
} else if (data.type === 'abort-session') {
|
|
688
|
-
console.log('
|
|
708
|
+
console.log('[DEBUG] Abort session request:', data.sessionId);
|
|
689
709
|
const provider = data.provider || 'claude';
|
|
690
710
|
let success;
|
|
691
711
|
|
|
@@ -703,7 +723,7 @@ function handleChatConnection(ws) {
|
|
|
703
723
|
success
|
|
704
724
|
}));
|
|
705
725
|
} else if (data.type === 'cursor-abort') {
|
|
706
|
-
console.log('
|
|
726
|
+
console.log('[DEBUG] Abort Cursor session:', data.sessionId);
|
|
707
727
|
const success = abortCursorSession(data.sessionId);
|
|
708
728
|
ws.send(JSON.stringify({
|
|
709
729
|
type: 'session-aborted',
|
|
@@ -742,7 +762,7 @@ function handleChatConnection(ws) {
|
|
|
742
762
|
}));
|
|
743
763
|
}
|
|
744
764
|
} catch (error) {
|
|
745
|
-
console.error('
|
|
765
|
+
console.error('[ERROR] Chat WebSocket error:', error.message);
|
|
746
766
|
ws.send(JSON.stringify({
|
|
747
767
|
type: 'error',
|
|
748
768
|
error: error.message
|
|
@@ -776,7 +796,7 @@ function handleShellConnection(ws) {
|
|
|
776
796
|
const initialCommand = data.initialCommand;
|
|
777
797
|
const isPlainShell = data.isPlainShell || (!!initialCommand && !hasSession) || provider === 'plain-shell';
|
|
778
798
|
|
|
779
|
-
console.log('
|
|
799
|
+
console.log('[INFO] Starting shell in:', projectPath);
|
|
780
800
|
console.log('📋 Session info:', hasSession ? `Resume session ${sessionId}` : (isPlainShell ? 'Plain shell mode' : 'New session'));
|
|
781
801
|
console.log('🤖 Provider:', isPlainShell ? 'plain-shell' : provider);
|
|
782
802
|
if (initialCommand) {
|
|
@@ -889,7 +909,7 @@ function handleShellConnection(ws) {
|
|
|
889
909
|
let match;
|
|
890
910
|
while ((match = pattern.exec(data)) !== null) {
|
|
891
911
|
const url = match[1];
|
|
892
|
-
console.log('
|
|
912
|
+
console.log('[DEBUG] Detected URL for opening:', url);
|
|
893
913
|
|
|
894
914
|
// Send URL opening message to client
|
|
895
915
|
ws.send(JSON.stringify({
|
|
@@ -899,7 +919,7 @@ function handleShellConnection(ws) {
|
|
|
899
919
|
|
|
900
920
|
// Replace the OPEN_URL pattern with a user-friendly message
|
|
901
921
|
if (pattern.source.includes('OPEN_URL')) {
|
|
902
|
-
outputData = outputData.replace(match[0],
|
|
922
|
+
outputData = outputData.replace(match[0], `[INFO] Opening in browser: ${url}`);
|
|
903
923
|
}
|
|
904
924
|
}
|
|
905
925
|
});
|
|
@@ -925,7 +945,7 @@ function handleShellConnection(ws) {
|
|
|
925
945
|
});
|
|
926
946
|
|
|
927
947
|
} catch (spawnError) {
|
|
928
|
-
console.error('
|
|
948
|
+
console.error('[ERROR] Error spawning process:', spawnError);
|
|
929
949
|
ws.send(JSON.stringify({
|
|
930
950
|
type: 'output',
|
|
931
951
|
data: `\r\n\x1b[31mError: ${spawnError.message}\x1b[0m\r\n`
|
|
@@ -951,7 +971,7 @@ function handleShellConnection(ws) {
|
|
|
951
971
|
}
|
|
952
972
|
}
|
|
953
973
|
} catch (error) {
|
|
954
|
-
console.error('
|
|
974
|
+
console.error('[ERROR] Shell WebSocket error:', error.message);
|
|
955
975
|
if (ws.readyState === WebSocket.OPEN) {
|
|
956
976
|
ws.send(JSON.stringify({
|
|
957
977
|
type: 'output',
|
|
@@ -970,7 +990,7 @@ function handleShellConnection(ws) {
|
|
|
970
990
|
});
|
|
971
991
|
|
|
972
992
|
ws.on('error', (error) => {
|
|
973
|
-
console.error('
|
|
993
|
+
console.error('[ERROR] Shell WebSocket error:', error);
|
|
974
994
|
});
|
|
975
995
|
}
|
|
976
996
|
// Audio transcription endpoint
|
|
@@ -1411,28 +1431,37 @@ async function startServer() {
|
|
|
1411
1431
|
try {
|
|
1412
1432
|
// Initialize authentication database
|
|
1413
1433
|
await initializeDatabase();
|
|
1414
|
-
console.log('✅ Database initialization skipped (testing)');
|
|
1415
1434
|
|
|
1416
1435
|
// Check if running in production mode (dist folder exists)
|
|
1417
1436
|
const distIndexPath = path.join(__dirname, '../dist/index.html');
|
|
1418
1437
|
const isProduction = fs.existsSync(distIndexPath);
|
|
1419
1438
|
|
|
1420
1439
|
// Log Claude implementation mode
|
|
1421
|
-
console.log('
|
|
1422
|
-
console.log(
|
|
1440
|
+
console.log(`${c.info('[INFO]')} Using Claude Agents SDK for Claude integration`);
|
|
1441
|
+
console.log(`${c.info('[INFO]')} Running in ${c.bright(isProduction ? 'PRODUCTION' : 'DEVELOPMENT')} mode`);
|
|
1423
1442
|
|
|
1424
1443
|
if (!isProduction) {
|
|
1425
|
-
console.log(
|
|
1444
|
+
console.log(`${c.warn('[WARN]')} Note: Requests will be proxied to Vite dev server at ${c.dim('http://localhost:' + (process.env.VITE_PORT || 5173))}`);
|
|
1426
1445
|
}
|
|
1427
1446
|
|
|
1428
1447
|
server.listen(PORT, '0.0.0.0', async () => {
|
|
1429
|
-
|
|
1448
|
+
const appInstallPath = path.join(__dirname, '..');
|
|
1449
|
+
|
|
1450
|
+
console.log('');
|
|
1451
|
+
console.log(c.dim('═'.repeat(63)));
|
|
1452
|
+
console.log(` ${c.bright('Claude Code UI Server - Ready')}`);
|
|
1453
|
+
console.log(c.dim('═'.repeat(63)));
|
|
1454
|
+
console.log('');
|
|
1455
|
+
console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://0.0.0.0:' + PORT)}`);
|
|
1456
|
+
console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`);
|
|
1457
|
+
console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`);
|
|
1458
|
+
console.log('');
|
|
1430
1459
|
|
|
1431
1460
|
// Start watching the projects folder for changes
|
|
1432
1461
|
await setupProjectsWatcher();
|
|
1433
1462
|
});
|
|
1434
1463
|
} catch (error) {
|
|
1435
|
-
console.error('
|
|
1464
|
+
console.error('[ERROR] Failed to start server:', error);
|
|
1436
1465
|
process.exit(1);
|
|
1437
1466
|
}
|
|
1438
1467
|
}
|
package/server/routes/git.js
CHANGED
|
@@ -502,16 +502,16 @@ router.post('/generate-commit-message', async (req, res) => {
|
|
|
502
502
|
*/
|
|
503
503
|
async function generateCommitMessageWithAI(files, diffContext, provider, projectPath) {
|
|
504
504
|
// Create the prompt
|
|
505
|
-
const prompt = `
|
|
505
|
+
const prompt = `Generate a conventional commit message for these changes.
|
|
506
506
|
|
|
507
507
|
REQUIREMENTS:
|
|
508
|
-
-
|
|
509
|
-
- Include
|
|
510
|
-
-
|
|
511
|
-
-
|
|
512
|
-
-
|
|
513
|
-
-
|
|
514
|
-
- Return ONLY the commit message
|
|
508
|
+
- Format: type(scope): subject
|
|
509
|
+
- Include body explaining what changed and why
|
|
510
|
+
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
|
|
511
|
+
- Subject under 50 chars, body wrapped at 72 chars
|
|
512
|
+
- Focus on user-facing changes, not implementation details
|
|
513
|
+
- Consider what's being added AND removed
|
|
514
|
+
- Return ONLY the commit message (no markdown, explanations, or code blocks)
|
|
515
515
|
|
|
516
516
|
FILES CHANGED:
|
|
517
517
|
${files.map(f => `- ${f}`).join('\n')}
|
|
@@ -519,7 +519,7 @@ ${files.map(f => `- ${f}`).join('\n')}
|
|
|
519
519
|
DIFFS:
|
|
520
520
|
${diffContext.substring(0, 4000)}
|
|
521
521
|
|
|
522
|
-
Generate the commit message
|
|
522
|
+
Generate the commit message:`;
|
|
523
523
|
|
|
524
524
|
try {
|
|
525
525
|
// Create a simple writer that collects the response
|