claude-code-templates 1.15.0 → 1.15.1
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 +7 -7
- package/bin/create-claude-config.js +15 -8
- package/package.json +2 -3
- package/src/analytics/core/AgentAnalyzer.js +17 -3
- package/src/analytics/core/ProcessDetector.js +23 -7
- package/src/analytics/core/StateCalculator.js +102 -33
- package/src/analytics/data/DataCache.js +7 -7
- package/src/analytics-web/chats_mobile.html +2590 -0
- package/src/analytics-web/components/App.js +10 -10
- package/src/analytics-web/components/SessionTimer.js +1 -1
- package/src/analytics-web/components/Sidebar.js +5 -14
- package/src/analytics-web/index.html +932 -78
- package/src/analytics.js +263 -5
- package/src/chats-mobile.js +682 -0
- package/src/claude-api-proxy.js +460 -0
- package/src/file-operations.js +422 -83
- package/src/health-check.js +310 -0
- package/src/index.js +944 -56
- package/src/tracking-service.js +31 -34
- package/components/agents/api-security-audit.md +0 -92
- package/components/agents/database-optimization.md +0 -94
- package/components/agents/react-performance-optimization.md +0 -64
- package/components/commands/check-file.md +0 -53
- package/components/commands/generate-tests.md +0 -68
- package/components/mcps/deepgraph-nextjs.json +0 -12
- package/components/mcps/deepgraph-react.json +0 -12
- package/components/mcps/deepgraph-typescript.json +0 -12
- package/components/mcps/deepgraph-vue.json +0 -12
- package/components/mcps/filesystem-access.json +0 -12
- package/components/mcps/github-integration.json +0 -11
- package/components/mcps/memory-integration.json +0 -8
- package/components/mcps/mysql-integration.json +0 -11
- package/components/mcps/postgresql-integration.json +0 -11
- package/components/mcps/web-fetch.json +0 -8
- package/src/analytics-web/components/AgentsPage.js +0 -4761
- package/templates/common/.claude/commands/git-workflow.md +0 -239
- package/templates/common/.claude/commands/project-setup.md +0 -316
- package/templates/common/.mcp.json +0 -41
- package/templates/common/CLAUDE.md +0 -109
- package/templates/common/README.md +0 -96
- package/templates/go/.mcp.json +0 -78
- package/templates/go/README.md +0 -25
- package/templates/javascript-typescript/.claude/commands/api-endpoint.md +0 -51
- package/templates/javascript-typescript/.claude/commands/debug.md +0 -52
- package/templates/javascript-typescript/.claude/commands/lint.md +0 -48
- package/templates/javascript-typescript/.claude/commands/npm-scripts.md +0 -48
- package/templates/javascript-typescript/.claude/commands/refactor.md +0 -55
- package/templates/javascript-typescript/.claude/commands/test.md +0 -61
- package/templates/javascript-typescript/.claude/commands/typescript-migrate.md +0 -51
- package/templates/javascript-typescript/.claude/settings.json +0 -142
- package/templates/javascript-typescript/.mcp.json +0 -80
- package/templates/javascript-typescript/CLAUDE.md +0 -185
- package/templates/javascript-typescript/README.md +0 -259
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/components.md +0 -63
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/services.md +0 -62
- package/templates/javascript-typescript/examples/node-api/.claude/commands/api-endpoint.md +0 -46
- package/templates/javascript-typescript/examples/node-api/.claude/commands/database.md +0 -56
- package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +0 -61
- package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +0 -57
- package/templates/javascript-typescript/examples/node-api/CLAUDE.md +0 -102
- package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +0 -29
- package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +0 -44
- package/templates/javascript-typescript/examples/react-app/.claude/commands/state-management.md +0 -45
- package/templates/javascript-typescript/examples/react-app/CLAUDE.md +0 -81
- package/templates/javascript-typescript/examples/react-app/agents/react-performance-optimization.md +0 -530
- package/templates/javascript-typescript/examples/react-app/agents/react-state-management.md +0 -295
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/components.md +0 -46
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/composables.md +0 -51
- package/templates/python/.claude/commands/lint.md +0 -111
- package/templates/python/.claude/commands/test.md +0 -73
- package/templates/python/.claude/settings.json +0 -153
- package/templates/python/.mcp.json +0 -78
- package/templates/python/CLAUDE.md +0 -276
- package/templates/python/examples/django-app/.claude/commands/admin.md +0 -264
- package/templates/python/examples/django-app/.claude/commands/django-model.md +0 -124
- package/templates/python/examples/django-app/.claude/commands/views.md +0 -222
- package/templates/python/examples/django-app/CLAUDE.md +0 -313
- package/templates/python/examples/django-app/agents/django-api-security.md +0 -642
- package/templates/python/examples/django-app/agents/django-database-optimization.md +0 -752
- package/templates/python/examples/fastapi-app/.claude/commands/api-endpoints.md +0 -513
- package/templates/python/examples/fastapi-app/.claude/commands/auth.md +0 -775
- package/templates/python/examples/fastapi-app/.claude/commands/database.md +0 -657
- package/templates/python/examples/fastapi-app/.claude/commands/deployment.md +0 -160
- package/templates/python/examples/fastapi-app/.claude/commands/testing.md +0 -927
- package/templates/python/examples/fastapi-app/CLAUDE.md +0 -229
- package/templates/python/examples/flask-app/.claude/commands/app-factory.md +0 -384
- package/templates/python/examples/flask-app/.claude/commands/blueprint.md +0 -243
- package/templates/python/examples/flask-app/.claude/commands/database.md +0 -410
- package/templates/python/examples/flask-app/.claude/commands/deployment.md +0 -620
- package/templates/python/examples/flask-app/.claude/commands/flask-route.md +0 -217
- package/templates/python/examples/flask-app/.claude/commands/testing.md +0 -559
- package/templates/python/examples/flask-app/CLAUDE.md +0 -391
- package/templates/ruby/.claude/commands/model.md +0 -360
- package/templates/ruby/.claude/commands/test.md +0 -480
- package/templates/ruby/.claude/settings.json +0 -146
- package/templates/ruby/.mcp.json +0 -83
- package/templates/ruby/CLAUDE.md +0 -284
- package/templates/ruby/examples/rails-app/.claude/commands/authentication.md +0 -490
- package/templates/ruby/examples/rails-app/CLAUDE.md +0 -376
- package/templates/rust/.mcp.json +0 -78
- package/templates/rust/README.md +0 -26
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { v4: uuidv4 } = require('uuid');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
class ClaudeAPIProxy {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.app = express();
|
|
11
|
+
this.port = 3335;
|
|
12
|
+
this.claudeDir = path.join(os.homedir(), '.claude');
|
|
13
|
+
|
|
14
|
+
// Store active sessions and conversation contexts
|
|
15
|
+
this.activeSessions = new Map();
|
|
16
|
+
this.conversationContexts = new Map();
|
|
17
|
+
|
|
18
|
+
this.setupMiddleware();
|
|
19
|
+
this.setupRoutes();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setupMiddleware() {
|
|
23
|
+
this.app.use(express.json());
|
|
24
|
+
this.app.use((req, res, next) => {
|
|
25
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
26
|
+
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
27
|
+
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
|
|
28
|
+
if (req.method === 'OPTIONS') {
|
|
29
|
+
res.sendStatus(200);
|
|
30
|
+
} else {
|
|
31
|
+
next();
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setupRoutes() {
|
|
37
|
+
// Get active conversations/sessions
|
|
38
|
+
this.app.get('/api/sessions', async (req, res) => {
|
|
39
|
+
try {
|
|
40
|
+
const sessions = await this.getActiveSessions();
|
|
41
|
+
res.json({ sessions });
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error('Error getting sessions:', error);
|
|
44
|
+
res.status(500).json({ error: error.message });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Send message to Claude (main endpoint)
|
|
49
|
+
this.app.post('/api/send-message', async (req, res) => {
|
|
50
|
+
try {
|
|
51
|
+
const { sessionId, message, projectPath } = req.body;
|
|
52
|
+
|
|
53
|
+
if (!sessionId || !message) {
|
|
54
|
+
return res.status(400).json({ error: 'sessionId and message are required' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const result = await this.sendMessageToClaude(sessionId, message, projectPath);
|
|
58
|
+
res.json(result);
|
|
59
|
+
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('Error sending message:', error);
|
|
62
|
+
res.status(500).json({ error: error.message });
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Get conversation history
|
|
67
|
+
this.app.get('/api/conversation/:sessionId', async (req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const { sessionId } = req.params;
|
|
70
|
+
const conversation = await this.getConversationHistory(sessionId);
|
|
71
|
+
res.json({ conversation });
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('Error getting conversation:', error);
|
|
74
|
+
res.status(500).json({ error: error.message });
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getActiveSessions() {
|
|
80
|
+
const projectsDir = path.join(this.claudeDir, 'projects');
|
|
81
|
+
|
|
82
|
+
if (!(await fs.pathExists(projectsDir))) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const sessions = [];
|
|
87
|
+
const projectDirs = await fs.readdir(projectsDir);
|
|
88
|
+
|
|
89
|
+
for (const projectDir of projectDirs) {
|
|
90
|
+
const projectPath = path.join(projectsDir, projectDir);
|
|
91
|
+
const files = await fs.readdir(projectPath);
|
|
92
|
+
|
|
93
|
+
for (const file of files) {
|
|
94
|
+
if (file.endsWith('.jsonl')) {
|
|
95
|
+
const sessionId = path.basename(file, '.jsonl');
|
|
96
|
+
const filePath = path.join(projectPath, file);
|
|
97
|
+
const stats = await fs.stat(filePath);
|
|
98
|
+
|
|
99
|
+
// Get basic info about the session
|
|
100
|
+
const lastMessage = await this.getLastMessage(filePath);
|
|
101
|
+
|
|
102
|
+
sessions.push({
|
|
103
|
+
sessionId,
|
|
104
|
+
projectPath: this.decodeProjectPath(projectDir),
|
|
105
|
+
filePath,
|
|
106
|
+
lastModified: stats.mtime,
|
|
107
|
+
lastMessage: lastMessage?.content || 'No messages',
|
|
108
|
+
messageCount: await this.getMessageCount(filePath)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Sort by most recent activity
|
|
115
|
+
return sessions.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
decodeProjectPath(encodedPath) {
|
|
119
|
+
return encodedPath.replace(/-/g, '/').replace(/^Users/, '/Users');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getLastMessage(filePath) {
|
|
123
|
+
try {
|
|
124
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
125
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
126
|
+
|
|
127
|
+
if (lines.length === 0) return null;
|
|
128
|
+
|
|
129
|
+
const lastLine = lines[lines.length - 1];
|
|
130
|
+
const lastMessage = JSON.parse(lastLine);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
content: this.extractMessageContent(lastMessage),
|
|
134
|
+
timestamp: lastMessage.timestamp,
|
|
135
|
+
role: lastMessage.message?.role || lastMessage.type
|
|
136
|
+
};
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Error reading last message:', error);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async getMessageCount(filePath) {
|
|
144
|
+
try {
|
|
145
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
146
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
147
|
+
return lines.length;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
extractMessageContent(messageObj) {
|
|
154
|
+
if (messageObj.message?.content) {
|
|
155
|
+
if (typeof messageObj.message.content === 'string') {
|
|
156
|
+
return messageObj.message.content;
|
|
157
|
+
}
|
|
158
|
+
if (Array.isArray(messageObj.message.content)) {
|
|
159
|
+
const textContent = messageObj.message.content
|
|
160
|
+
.filter(item => item.type === 'text')
|
|
161
|
+
.map(item => item.text)
|
|
162
|
+
.join(' ');
|
|
163
|
+
return textContent || '[Tool use or other content]';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return '[No content]';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async sendMessageToClaude(sessionId, messageContent, projectPath) {
|
|
170
|
+
console.log(chalk.blue(`📤 Sending message to session ${sessionId}`));
|
|
171
|
+
|
|
172
|
+
// Find the conversation file
|
|
173
|
+
const conversationFile = await this.findConversationFile(sessionId, projectPath);
|
|
174
|
+
|
|
175
|
+
if (!conversationFile) {
|
|
176
|
+
throw new Error(`Conversation file not found for session ${sessionId}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Get conversation context
|
|
180
|
+
const context = await this.getConversationContext(conversationFile);
|
|
181
|
+
|
|
182
|
+
// Create user message in Claude Code format
|
|
183
|
+
const userMessage = this.createUserMessage(messageContent, context, sessionId);
|
|
184
|
+
|
|
185
|
+
// Append to JSONL file
|
|
186
|
+
await this.appendToConversation(conversationFile, userMessage);
|
|
187
|
+
|
|
188
|
+
console.log(chalk.green(`✅ Message sent to ${conversationFile}`));
|
|
189
|
+
|
|
190
|
+
// Try to notify Claude Code process about the file change
|
|
191
|
+
await this.notifyClaudeProcess();
|
|
192
|
+
|
|
193
|
+
// TODO: Monitor for Claude Code response
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
success: true,
|
|
197
|
+
messageId: userMessage.uuid,
|
|
198
|
+
sessionId,
|
|
199
|
+
message: 'Message sent to Claude Code conversation'
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async findConversationFile(sessionId, projectPath) {
|
|
204
|
+
const projectsDir = path.join(this.claudeDir, 'projects');
|
|
205
|
+
|
|
206
|
+
// If projectPath provided, look there first
|
|
207
|
+
if (projectPath) {
|
|
208
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
209
|
+
const targetDir = path.join(projectsDir, encodedPath);
|
|
210
|
+
const conversationFile = path.join(targetDir, `${sessionId}.jsonl`);
|
|
211
|
+
|
|
212
|
+
if (await fs.pathExists(conversationFile)) {
|
|
213
|
+
return conversationFile;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Otherwise, search all projects
|
|
218
|
+
const projectDirs = await fs.readdir(projectsDir);
|
|
219
|
+
|
|
220
|
+
for (const projectDir of projectDirs) {
|
|
221
|
+
const conversationFile = path.join(projectsDir, projectDir, `${sessionId}.jsonl`);
|
|
222
|
+
|
|
223
|
+
if (await fs.pathExists(conversationFile)) {
|
|
224
|
+
return conversationFile;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
encodeProjectPath(projectPath) {
|
|
232
|
+
return projectPath.replace(/\\//g, '-').replace(/^-/, '');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async getConversationContext(conversationFile) {
|
|
236
|
+
try {
|
|
237
|
+
const content = await fs.readFile(conversationFile, 'utf8');
|
|
238
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
239
|
+
|
|
240
|
+
if (lines.length === 0) {
|
|
241
|
+
return { lastMessage: null, cwd: process.cwd(), version: '1.0.44' };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Find the last valid JSON line (iterate backwards)
|
|
245
|
+
let lastMessage = null;
|
|
246
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
247
|
+
const line = lines[i].trim();
|
|
248
|
+
try {
|
|
249
|
+
lastMessage = JSON.parse(line);
|
|
250
|
+
break; // Found valid JSON, break out of loop
|
|
251
|
+
} catch (jsonError) {
|
|
252
|
+
// Skip invalid JSON lines
|
|
253
|
+
console.warn(`Skipping invalid JSON line ${i + 1}: ${line.substring(0, 50)}...`);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!lastMessage) {
|
|
259
|
+
console.warn('No valid JSON message found in conversation file');
|
|
260
|
+
return { lastMessage: null, cwd: process.cwd(), version: '1.0.44' };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
lastMessage,
|
|
265
|
+
cwd: lastMessage.cwd || process.cwd(),
|
|
266
|
+
version: lastMessage.version || '1.0.44',
|
|
267
|
+
sessionId: lastMessage.sessionId
|
|
268
|
+
};
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error('Error getting conversation context:', error);
|
|
271
|
+
return { lastMessage: null, cwd: process.cwd(), version: '1.0.44' };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
createUserMessage(content, context, sessionId) {
|
|
276
|
+
const uuid = uuidv4();
|
|
277
|
+
const timestamp = new Date().toISOString();
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
parentUuid: context.lastMessage?.uuid || null,
|
|
281
|
+
isSidechain: false,
|
|
282
|
+
userType: "external",
|
|
283
|
+
cwd: context.cwd,
|
|
284
|
+
sessionId: sessionId,
|
|
285
|
+
version: context.version,
|
|
286
|
+
type: "user",
|
|
287
|
+
message: {
|
|
288
|
+
role: "user",
|
|
289
|
+
content: content
|
|
290
|
+
},
|
|
291
|
+
uuid: uuid,
|
|
292
|
+
timestamp: timestamp
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async appendToConversation(conversationFile, messageObj) {
|
|
297
|
+
const messageJson = JSON.stringify(messageObj);
|
|
298
|
+
await fs.appendFile(conversationFile, messageJson + '\n');
|
|
299
|
+
|
|
300
|
+
// Force file system change notification by touching the file
|
|
301
|
+
const now = new Date();
|
|
302
|
+
await fs.utimes(conversationFile, now, now);
|
|
303
|
+
|
|
304
|
+
console.log(chalk.green(`📝 Message appended to ${path.basename(conversationFile)}`));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async getConversationHistory(sessionId) {
|
|
308
|
+
const conversationFile = await this.findConversationFile(sessionId);
|
|
309
|
+
|
|
310
|
+
if (!conversationFile) {
|
|
311
|
+
throw new Error(`Conversation not found for session ${sessionId}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const content = await fs.readFile(conversationFile, 'utf8');
|
|
315
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
316
|
+
|
|
317
|
+
const messages = lines
|
|
318
|
+
.map(line => {
|
|
319
|
+
try {
|
|
320
|
+
return JSON.parse(line);
|
|
321
|
+
} catch (e) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
.filter(msg => msg !== null);
|
|
326
|
+
|
|
327
|
+
return messages;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
start() {
|
|
331
|
+
return new Promise((resolve) => {
|
|
332
|
+
this.server = this.app.listen(this.port, () => {
|
|
333
|
+
console.log(chalk.green(`🌉 Claude API Proxy running on http://localhost:${this.port}`));
|
|
334
|
+
console.log(chalk.blue(`📡 Ready to intercept and send messages to Claude Code`));
|
|
335
|
+
resolve();
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
stop() {
|
|
341
|
+
if (this.server) {
|
|
342
|
+
this.server.close();
|
|
343
|
+
console.log(chalk.yellow(`🔌 Claude API Proxy stopped`));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
module.exports = ClaudeAPIProxy;
|
|
349
|
+
|
|
350
|
+
// Method to notify Claude Code process
|
|
351
|
+
ClaudeAPIProxy.prototype.notifyClaudeProcess = async function() {
|
|
352
|
+
try {
|
|
353
|
+
console.log(chalk.blue('🔔 Attempting to activate Claude Code process...'));
|
|
354
|
+
|
|
355
|
+
// Method 1: Find Claude Code process and try to send input
|
|
356
|
+
const { exec, spawn } = require('child_process');
|
|
357
|
+
|
|
358
|
+
// First, find Claude Code processes
|
|
359
|
+
exec('ps aux | grep "claude"', (error, stdout, stderr) => {
|
|
360
|
+
if (stdout) {
|
|
361
|
+
const claudeProcesses = stdout.split('\n')
|
|
362
|
+
.filter(line => line.includes('claude') && !line.includes('grep'))
|
|
363
|
+
.filter(line => !line.includes('claude-code-templates')); // Exclude our dashboard
|
|
364
|
+
|
|
365
|
+
console.log(chalk.blue(`🔍 Found ${claudeProcesses.length} Claude process(es)`));
|
|
366
|
+
|
|
367
|
+
claudeProcesses.forEach(processLine => {
|
|
368
|
+
const pid = processLine.trim().split(/\s+/)[1];
|
|
369
|
+
console.log(chalk.gray(` - PID ${pid}: ${processLine.substring(0, 100)}...`));
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Method 2: Try to write to the Claude Code terminal using applescript (macOS)
|
|
375
|
+
if (process.platform === 'darwin') {
|
|
376
|
+
this.tryAppleScriptNotification();
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Method 3: Try sending wake-up signal
|
|
380
|
+
try {
|
|
381
|
+
exec('pkill -SIGUSR1 claude', () => {});
|
|
382
|
+
} catch (e) {/* ignore */}
|
|
383
|
+
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.log(chalk.gray('🔕 Could not notify Claude Code process'));
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// Try to use AppleScript to send input to Claude Code terminal
|
|
390
|
+
ClaudeAPIProxy.prototype.tryAppleScriptNotification = function() {
|
|
391
|
+
try {
|
|
392
|
+
const { exec } = require('child_process');
|
|
393
|
+
|
|
394
|
+
// This AppleScript tries to find Terminal/iTerm with Claude Code and send a key
|
|
395
|
+
const appleScript = `
|
|
396
|
+
tell application "System Events"
|
|
397
|
+
set claudeFound to false
|
|
398
|
+
try
|
|
399
|
+
-- Try Terminal first
|
|
400
|
+
tell application "Terminal"
|
|
401
|
+
repeat with w in windows
|
|
402
|
+
repeat with t in tabs of w
|
|
403
|
+
if (custom title of t contains "claude" or name of t contains "claude") then
|
|
404
|
+
set claudeFound to true
|
|
405
|
+
set frontmost to true
|
|
406
|
+
do script "" in t -- Send empty command to wake up
|
|
407
|
+
exit repeat
|
|
408
|
+
end if
|
|
409
|
+
end repeat
|
|
410
|
+
if claudeFound then exit repeat
|
|
411
|
+
end repeat
|
|
412
|
+
end tell
|
|
413
|
+
end try
|
|
414
|
+
|
|
415
|
+
if not claudeFound then
|
|
416
|
+
try
|
|
417
|
+
-- Try iTerm2
|
|
418
|
+
tell application "iTerm"
|
|
419
|
+
repeat with w in windows
|
|
420
|
+
repeat with t in tabs of w
|
|
421
|
+
tell current session of t
|
|
422
|
+
if (name contains "claude") then
|
|
423
|
+
set claudeFound to true
|
|
424
|
+
select
|
|
425
|
+
write text ""
|
|
426
|
+
exit repeat
|
|
427
|
+
end if
|
|
428
|
+
end tell
|
|
429
|
+
end repeat
|
|
430
|
+
if claudeFound then exit repeat
|
|
431
|
+
end repeat
|
|
432
|
+
end tell
|
|
433
|
+
end try
|
|
434
|
+
end if
|
|
435
|
+
end tell
|
|
436
|
+
`;
|
|
437
|
+
|
|
438
|
+
exec(`osascript -e '${appleScript.replace(/'/g, "\\'")}'`, (error) => {
|
|
439
|
+
if (error) {
|
|
440
|
+
console.log(chalk.gray('🔕 AppleScript notification failed'));
|
|
441
|
+
} else {
|
|
442
|
+
console.log(chalk.green('✅ AppleScript notification sent'));
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
} catch (error) {
|
|
447
|
+
// Silent fail
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
// If run directly
|
|
452
|
+
if (require.main === module) {
|
|
453
|
+
const proxy = new ClaudeAPIProxy();
|
|
454
|
+
proxy.start();
|
|
455
|
+
|
|
456
|
+
process.on('SIGINT', () => {
|
|
457
|
+
proxy.stop();
|
|
458
|
+
process.exit(0);
|
|
459
|
+
});
|
|
460
|
+
}
|