claude-code-templates 1.24.16 → 1.24.17

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.
@@ -173,6 +173,244 @@ class ChatsMobile {
173
173
  }
174
174
  });
175
175
 
176
+ // API to get unique working directories from conversations
177
+ this.app.get('/api/directories', (req, res) => {
178
+ try {
179
+ // Extract unique directories from conversations
180
+ const directories = new Set();
181
+
182
+ this.data.conversations.forEach(conv => {
183
+ if (conv.project && conv.project.trim()) {
184
+ directories.add(conv.project);
185
+ }
186
+ });
187
+
188
+ // Convert to array and sort alphabetically
189
+ const sortedDirectories = Array.from(directories).sort((a, b) =>
190
+ a.toLowerCase().localeCompare(b.toLowerCase())
191
+ );
192
+
193
+ res.json({
194
+ directories: sortedDirectories,
195
+ count: sortedDirectories.length,
196
+ timestamp: new Date().toISOString()
197
+ });
198
+ } catch (error) {
199
+ console.error('Error getting directories:', error);
200
+ res.status(500).json({ error: 'Internal server error' });
201
+ }
202
+ });
203
+
204
+ // API to search conversations with advanced filters
205
+ this.app.post('/api/search', async (req, res) => {
206
+ try {
207
+ const { query, workingDirectory, dateFrom, dateTo, contentSearch } = req.body;
208
+
209
+ let results = [...this.data.conversations];
210
+
211
+ // Filter by working directory (project)
212
+ if (workingDirectory && workingDirectory.trim()) {
213
+ results = results.filter(conv => {
214
+ if (!conv.project) return false;
215
+ return conv.project.toLowerCase().includes(workingDirectory.toLowerCase());
216
+ });
217
+ }
218
+
219
+ // Filter by date range
220
+ if (dateFrom) {
221
+ const fromDate = new Date(dateFrom);
222
+ results = results.filter(conv => new Date(conv.created) >= fromDate);
223
+ }
224
+
225
+ if (dateTo) {
226
+ const toDate = new Date(dateTo);
227
+ toDate.setHours(23, 59, 59, 999); // Include entire day
228
+ results = results.filter(conv => new Date(conv.created) <= toDate);
229
+ }
230
+
231
+ // Filter by conversation metadata (filename, id)
232
+ if (query && query.trim()) {
233
+ const searchTerm = query.toLowerCase();
234
+ results = results.filter(conv =>
235
+ conv.filename.toLowerCase().includes(searchTerm) ||
236
+ conv.id.toLowerCase().includes(searchTerm) ||
237
+ (conv.project && conv.project.toLowerCase().includes(searchTerm))
238
+ );
239
+ }
240
+
241
+ // Search within message content
242
+ if (contentSearch && contentSearch.trim()) {
243
+ const contentTerm = contentSearch.toLowerCase();
244
+ const matchingConversations = [];
245
+
246
+ for (const conv of results) {
247
+ try {
248
+ const messages = await this.conversationAnalyzer.getParsedConversation(conv.filePath);
249
+
250
+ // Search in message content
251
+ const hasMatch = messages.some(msg => {
252
+ // Search in text content
253
+ if (typeof msg.content === 'string') {
254
+ return msg.content.toLowerCase().includes(contentTerm);
255
+ }
256
+
257
+ // Search in array content (tool use, text blocks)
258
+ if (Array.isArray(msg.content)) {
259
+ return msg.content.some(block => {
260
+ if (block.type === 'text' && block.text) {
261
+ return block.text.toLowerCase().includes(contentTerm);
262
+ }
263
+ if (block.type === 'tool_use' && block.name) {
264
+ return block.name.toLowerCase().includes(contentTerm);
265
+ }
266
+ return false;
267
+ });
268
+ }
269
+
270
+ return false;
271
+ });
272
+
273
+ if (hasMatch) {
274
+ matchingConversations.push(conv);
275
+ }
276
+ } catch (error) {
277
+ // Skip conversations that can't be parsed
278
+ this.log('warn', `Error searching in conversation ${conv.id}:`, error.message);
279
+ }
280
+ }
281
+
282
+ results = matchingConversations;
283
+ }
284
+
285
+ // Sort by last modified (most recent first)
286
+ results.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
287
+
288
+ res.json({
289
+ results: results,
290
+ count: results.length,
291
+ filters: {
292
+ query,
293
+ workingDirectory,
294
+ dateFrom,
295
+ dateTo,
296
+ contentSearch
297
+ },
298
+ timestamp: new Date().toISOString()
299
+ });
300
+ } catch (error) {
301
+ console.error('Error searching conversations:', error);
302
+ res.status(500).json({ error: 'Internal server error', message: error.message });
303
+ }
304
+ });
305
+
306
+ // API to search within a specific conversation
307
+ this.app.post('/api/conversations/:id/search', async (req, res) => {
308
+ try {
309
+ const conversationId = req.params.id;
310
+ const { query } = req.body;
311
+ const conversation = this.data.conversations.find(conv => conv.id === conversationId);
312
+
313
+ if (!conversation) {
314
+ return res.status(404).json({ error: 'Conversation not found' });
315
+ }
316
+
317
+ if (!query || !query.trim()) {
318
+ return res.json({
319
+ matches: [],
320
+ totalMatches: 0,
321
+ conversationId: conversationId
322
+ });
323
+ }
324
+
325
+ // Get all messages from the conversation
326
+ const allMessages = await this.conversationAnalyzer.getParsedConversation(conversation.filePath);
327
+ const searchTerm = query.toLowerCase();
328
+ const matches = [];
329
+
330
+ // Search through all messages
331
+ allMessages.forEach((msg, index) => {
332
+ let messageText = '';
333
+ let allText = [];
334
+
335
+ // Extract text from message content
336
+ if (typeof msg.content === 'string') {
337
+ allText.push(msg.content);
338
+ } else if (Array.isArray(msg.content)) {
339
+ msg.content.forEach(block => {
340
+ if (block.type === 'text' && block.text) {
341
+ allText.push(block.text);
342
+ }
343
+ // Also search in tool_use content
344
+ if (block.type === 'tool_use') {
345
+ if (block.name) allText.push(block.name);
346
+ if (block.input) {
347
+ allText.push(JSON.stringify(block.input));
348
+ }
349
+ }
350
+ });
351
+ }
352
+
353
+ // IMPORTANT: Also search in tool results (this is where code blocks appear!)
354
+ if (msg.toolResults && Array.isArray(msg.toolResults)) {
355
+ msg.toolResults.forEach(toolResult => {
356
+ if (toolResult.content) {
357
+ if (typeof toolResult.content === 'string') {
358
+ allText.push(toolResult.content);
359
+ } else if (Array.isArray(toolResult.content)) {
360
+ toolResult.content.forEach(block => {
361
+ if (block.type === 'text' && block.text) {
362
+ allText.push(block.text);
363
+ }
364
+ });
365
+ }
366
+ }
367
+ });
368
+ }
369
+
370
+ // Combine all text
371
+ messageText = allText.join(' ');
372
+
373
+ // Search in the combined text
374
+ if (messageText.toLowerCase().includes(searchTerm)) {
375
+ // Find all positions of the search term in this message
376
+ const lowerText = messageText.toLowerCase();
377
+ let position = 0;
378
+ let matchCount = 0;
379
+
380
+ while ((position = lowerText.indexOf(searchTerm, position)) !== -1) {
381
+ matchCount++;
382
+ position += searchTerm.length;
383
+ }
384
+
385
+ matches.push({
386
+ messageIndex: index,
387
+ messageId: msg.id,
388
+ role: msg.role,
389
+ timestamp: msg.timestamp,
390
+ preview: this.getMessagePreview(messageText, searchTerm),
391
+ matchCount: matchCount
392
+ });
393
+ }
394
+ });
395
+
396
+ console.log(`🔍 Search in conversation ${conversationId}:`, {
397
+ query: query,
398
+ messagesWithMatches: matches.length,
399
+ totalOccurrences: matches.reduce((sum, m) => sum + m.matchCount, 0)
400
+ });
401
+
402
+ res.json({
403
+ matches: matches,
404
+ totalMatches: matches.length,
405
+ conversationId: conversationId,
406
+ query: query
407
+ });
408
+ } catch (error) {
409
+ console.error('Error searching in conversation:', error);
410
+ res.status(500).json({ error: 'Internal server error', message: error.message });
411
+ }
412
+ });
413
+
176
414
  // API to get specific conversation messages (with pagination support)
177
415
  this.app.get('/api/conversations/:id/messages', async (req, res) => {
178
416
  try {
@@ -418,6 +656,26 @@ class ChatsMobile {
418
656
  console.log(chalk.gray('🔧 WebSocket server setup prepared'));
419
657
  }
420
658
 
659
+ /**
660
+ * Helper function to get message preview with context
661
+ */
662
+ getMessagePreview(text, searchTerm, contextLength = 100) {
663
+ const lowerText = text.toLowerCase();
664
+ const lowerTerm = searchTerm.toLowerCase();
665
+ const position = lowerText.indexOf(lowerTerm);
666
+
667
+ if (position === -1) return text.substring(0, contextLength);
668
+
669
+ const start = Math.max(0, position - contextLength / 2);
670
+ const end = Math.min(text.length, position + searchTerm.length + contextLength / 2);
671
+
672
+ let preview = text.substring(start, end);
673
+ if (start > 0) preview = '...' + preview;
674
+ if (end < text.length) preview = preview + '...';
675
+
676
+ return preview;
677
+ }
678
+
421
679
  /**
422
680
  * Load initial conversation data
423
681
  */