@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/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-CJDQ69DP.js"></script>
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-BHQThXog.css">
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.10.4",
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/index.js"
8
+ "claude-code-ui": "server/cli.js",
9
+ "cloudcli": "server/cli.js"
9
10
  },
10
11
  "files": [
11
12
  "server/",
@@ -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
- console.log('📝 Captured session ID:', message.session_id);
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
+ });
@@ -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
- console.log(`Connected to SQLite database at: ${DB_PATH}`);
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(' Error handling project changes:', 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(' Chokidar watcher error:', 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(' Failed to setup projects watcher:', 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(' WebSocket authentication failed');
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(' WebSocket authenticated for user:', user.username);
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('📄 File read request:', projectName, filePath);
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('🖼️ Binary file serve request:', projectName, filePath);
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('💾 File save request:', projectName, filePath);
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(' File tree error:', error.message);
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('🔗 Client connected to:', url);
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(' Unknown WebSocket path:', pathname);
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('💬 Chat WebSocket connected');
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('💬 User message:', data.command || '[Continue/Resume]');
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('🖱️ Cursor message:', data.command || '[Continue/Resume]');
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('🖱️ Cursor resume session (compat):', data.sessionId);
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('🛑 Abort session request:', data.sessionId);
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('🛑 Abort Cursor session:', data.sessionId);
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(' Chat WebSocket error:', error.message);
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('🚀 Starting shell in:', projectPath);
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('🔗 Detected URL for opening:', url);
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], `🌐 Opening in browser: ${url}`);
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(' Error spawning process:', spawnError);
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(' Shell WebSocket error:', error.message);
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(' Shell WebSocket error:', 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('🚀 Using Claude Agents SDK for Claude integration');
1422
- console.log(`📦 Running in ${isProduction ? 'PRODUCTION' : 'DEVELOPMENT'} mode`);
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(`⚠️ Note: Requests will be proxied to Vite dev server at http://localhost:${process.env.VITE_PORT || 5173}`);
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
- console.log(`Claude Code UI server running on http://0.0.0.0:${PORT}`);
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(' Failed to start server:', error);
1464
+ console.error('[ERROR] Failed to start server:', error);
1436
1465
  process.exit(1);
1437
1466
  }
1438
1467
  }
@@ -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 = `You are a git commit message generator. Based on the following file changes and diffs, generate a commit message in conventional commit format.
505
+ const prompt = `Generate a conventional commit message for these changes.
506
506
 
507
507
  REQUIREMENTS:
508
- - Use conventional commit format: type(scope): subject
509
- - Include a body that explains what changed and why
510
- - Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
511
- - Keep subject line under 50 characters
512
- - Wrap body at 72 characters
513
- - Be specific and descriptive
514
- - Return ONLY the commit message, nothing else - no markdown, no explanations, no code blocks
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 now:`;
522
+ Generate the commit message:`;
523
523
 
524
524
  try {
525
525
  // Create a simple writer that collects the response