diff --git a/README.md b/README.md index 4be36b3..7a99187 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ Control [Claude Code](https://claude.ai/code) remotely via email. Start tasks lo ## šŸ“… Changelog ### January 2025 -- **2025-01-15**: Implement terminal-style UI for email notifications ([#8](https://github.com/JessyTsui/Claude-Code-Remote/pull/8) by [@vaclisinc](https://github.com/vaclisinc)) -- **2025-01-15**: Fix working directory issue - enable claude-remote to run from any directory ([#7](https://github.com/JessyTsui/Claude-Code-Remote/pull/7) by [@vaclisinc](https://github.com/vaclisinc)) -- **2025-01-14**: Fix self-reply loop issue when using same email for send/receive ([#4](https://github.com/JessyTsui/Claude-Code-Remote/pull/4) by [@vaclisinc](https://github.com/vaclisinc)) +- **2025-08-01**: Implement terminal-style UI for email notifications ([#8](https://github.com/JessyTsui/Claude-Code-Remote/pull/8) by [@vaclisinc](https://github.com/vaclisinc)) +- **2025-08-01**: Fix working directory issue - enable claude-remote to run from any directory ([#7](https://github.com/JessyTsui/Claude-Code-Remote/pull/7) by [@vaclisinc](https://github.com/vaclisinc)) +- **2025-07-31**: Fix self-reply loop issue when using same email for send/receive ([#4](https://github.com/JessyTsui/Claude-Code-Remote/pull/4) by [@vaclisinc](https://github.com/vaclisinc)) ### July 2025 - **2025-07-28**: Remove hardcoded values and implement environment-based configuration ([#2](https://github.com/JessyTsui/Claude-Code-Remote/pull/2) by [@kevinsslin](https://github.com/kevinsslin)) @@ -99,6 +99,7 @@ SESSION_MAP_PATH=/your/absolute/path/to/Claude-Code-Remote/src/data/session-map. ``` šŸ“Œ **Gmail users**: Create an [App Password](https://myaccount.google.com/security) instead of using your regular password. +> Note: You may need to enable two-step verification in your google account first before create app password. ### Step 3: Set Up Claude Code Hooks @@ -137,6 +138,8 @@ Add this configuration (replace `/your/absolute/path/` with your actual path): } ``` +> **Note**: Subagent notifications are disabled by default. To enable them, set `enableSubagentNotifications: true` in your config. See [Subagent Notifications Guide](./docs/SUBAGENT_NOTIFICATIONS.md) for details. + ### Step 4: Test Your Setup ```bash diff --git a/claude-remote.js b/claude-remote.js index c5bce63..8741f14 100755 --- a/claude-remote.js +++ b/claude-remote.js @@ -115,6 +115,62 @@ class ClaudeCodeRemoteCLI { // Automatically capture current tmux session conversation content const metadata = await this.captureCurrentConversation(); + + // Handle subagent notifications + if (type === 'waiting') { + const Config = require('./src/core/config'); + const config = new Config(); + config.load(); + const enableSubagentNotifications = config.get('enableSubagentNotifications', false); + + if (!enableSubagentNotifications) { + // Instead of skipping, track the subagent activity + const SubagentTracker = require('./src/utils/subagent-tracker'); + const tracker = new SubagentTracker(); + + // Use tmux session as the tracking key + const trackingKey = metadata.tmuxSession || 'default'; + + // Capture more detailed information about the subagent activity + const activityDetails = { + userQuestion: metadata.userQuestion || 'No question captured', + claudeResponse: metadata.claudeResponse || 'No response captured', + timestamp: new Date().toISOString(), + tmuxSession: metadata.tmuxSession + }; + + // Don't truncate the response too aggressively + if (activityDetails.claudeResponse && activityDetails.claudeResponse.length > 1000) { + activityDetails.claudeResponse = activityDetails.claudeResponse.substring(0, 1000) + '...[see full output in tmux]'; + } + + tracker.addActivity(trackingKey, { + type: 'SubagentStop', + description: metadata.userQuestion || 'Subagent task', + details: activityDetails + }); + + this.logger.info(`Subagent activity tracked for tmux session: ${trackingKey}`); + process.exit(0); + } + } + + // For completed notifications, include subagent activities + if (type === 'completed') { + const SubagentTracker = require('./src/utils/subagent-tracker'); + const tracker = new SubagentTracker(); + const trackingKey = metadata.tmuxSession || 'default'; + + // Get and format subagent activities + const subagentSummary = tracker.formatActivitiesForEmail(trackingKey); + if (subagentSummary) { + metadata.subagentActivities = subagentSummary; + this.logger.info('Including subagent activities in completion email'); + + // Clear activities after including them + tracker.clearActivities(trackingKey); + } + } const result = await this.notifier.notify(type, metadata); diff --git a/config/defaults/config.json b/config/defaults/config.json index f967e43..91f4e6a 100644 --- a/config/defaults/config.json +++ b/config/defaults/config.json @@ -5,6 +5,8 @@ "waiting": "Hero" }, "enabled": true, + "enableSubagentNotifications": false, + "subagentActivityDetail": "medium", "timeout": 5, "customMessages": { "completed": null, diff --git a/src/channels/email/smtp.js b/src/channels/email/smtp.js index 5d0f1e6..f821b38 100644 --- a/src/channels/email/smtp.js +++ b/src/channels/email/smtp.js @@ -24,6 +24,18 @@ class EmailChannel extends NotificationChannel { this._initializeTransporter(); } + _escapeHtml(text) { + if (!text) return ''; + const htmlEntities = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g, char => htmlEntities[char]); + } + _ensureDirectories() { if (!fs.existsSync(this.sessionsDir)) { fs.mkdirSync(this.sessionsDir, { recursive: true }); @@ -286,7 +298,8 @@ class EmailChannel extends NotificationChannel { userQuestion: userQuestion || 'No specified task', claudeResponse: claudeResponse || notification.message, projectDir: projectDir, - shortQuestion: shortQuestion || 'No specific question' + shortQuestion: shortQuestion || 'No specific question', + subagentActivities: notification.metadata?.subagentActivities || '' }; let subject = enhancedSubject; @@ -297,7 +310,9 @@ class EmailChannel extends NotificationChannel { Object.keys(variables).forEach(key => { const placeholder = new RegExp(`{{${key}}}`, 'g'); subject = subject.replace(placeholder, variables[key]); - html = html.replace(placeholder, variables[key]); + // Escape HTML entities for HTML content + html = html.replace(placeholder, this._escapeHtml(variables[key])); + // No escaping needed for plain text text = text.replace(placeholder, variables[key]); }); @@ -356,6 +371,8 @@ class EmailChannel extends NotificationChannel { + {{subagentActivities}} +
$ claude-code help --continue
@@ -398,6 +415,8 @@ Status: {{type}} šŸ¤– Claude's Response: {{claudeResponse}} +{{subagentActivities}} + How to Continue Conversation: To continue conversation with Claude Code, please reply to this email directly and enter your instructions in the email body. diff --git a/src/data/sent-messages.json b/src/data/sent-messages.json index 077538b..b379c13 100644 --- a/src/data/sent-messages.json +++ b/src/data/sent-messages.json @@ -1,137 +1,3 @@ { - "messages": [ - { - "messageId": "<52d15aa1-d5a4-4d7d-8f01-4752d7d5fc6f-1754021037319@claude-code-remote>", - "sessionId": "52d15aa1-d5a4-4d7d-8f01-4752d7d5fc6f", - "token": "49WUF9NS", - "type": "notification", - "sentAt": "2025-08-01T04:03:59.850Z" - }, - { - "messageId": "", - "sessionId": "eba8744e-8cc1-4fad-9dc8-69d558c51cca", - "token": "N9PHUN4Q", - "type": "notification", - "sentAt": "2025-08-01T04:06:52.776Z" - }, - { - "messageId": "<859daa99-1ea9-4c40-aa68-c3967a0d7e4e-1754021233658@claude-code-remote>", - "sessionId": "859daa99-1ea9-4c40-aa68-c3967a0d7e4e", - "token": "GXWFSL3S", - "type": "notification", - "sentAt": "2025-08-01T04:07:15.556Z" - }, - { - "messageId": "", - "sessionId": "a1ed6757-6782-4b22-a486-aab5a9d60a3c", - "token": "6EZXA6IN", - "type": "notification", - "sentAt": "2025-08-01T04:07:49.959Z" - }, - { - "messageId": "<2122d57c-8434-44f0-b4e6-7eafb40ed49d-1754021285815@claude-code-remote>", - "sessionId": "2122d57c-8434-44f0-b4e6-7eafb40ed49d", - "token": "ZQY1UOIJ", - "type": "notification", - "sentAt": "2025-08-01T04:08:07.833Z" - }, - { - "messageId": "<2b30b1f7-b9c3-4cb4-b889-11c58009bd07-1754021533703@claude-code-remote>", - "sessionId": "2b30b1f7-b9c3-4cb4-b889-11c58009bd07", - "token": "L4KQ8DVJ", - "type": "notification", - "sentAt": "2025-08-01T04:12:15.795Z" - }, - { - "messageId": "", - "sessionId": "e3392712-9a5b-4c96-a23c-5d97c226b415", - "token": "7ZFMSK5P", - "type": "notification", - "sentAt": "2025-08-01T04:30:41.978Z" - }, - { - "messageId": "<5d19da23-5c9d-4855-824e-fbd09a0d986c-1754022731688@claude-code-remote>", - "sessionId": "5d19da23-5c9d-4855-824e-fbd09a0d986c", - "token": "PJOE746Q", - "type": "notification", - "sentAt": "2025-08-01T04:32:13.100Z" - }, - { - "messageId": "", - "sessionId": "ed814c4b-0b97-4e10-8ad1-dae1a7cb7ef9", - "token": "C9I24JHD", - "type": "notification", - "sentAt": "2025-08-01T04:35:28.987Z" - }, - { - "messageId": "<46ae2185-2f70-4355-a3b8-9fc04fa01181-1754023256348@claude-code-remote>", - "sessionId": "46ae2185-2f70-4355-a3b8-9fc04fa01181", - "token": "GGBKJ2DV", - "type": "notification", - "sentAt": "2025-08-01T04:40:58.066Z" - }, - { - "messageId": "<49c51c77-8ecc-4a04-a7a8-5955f89411b1-1754023825872@claude-code-remote>", - "sessionId": "49c51c77-8ecc-4a04-a7a8-5955f89411b1", - "token": "DQH198YY", - "type": "notification", - "sentAt": "2025-08-01T04:50:27.443Z" - }, - { - "messageId": "", - "sessionId": "cc5a1eba-7e04-44e6-919f-0395ddf2ee08", - "token": "GQE6LLIF", - "type": "notification", - "sentAt": "2025-08-01T04:54:16.491Z" - }, - { - "messageId": "<63198b90-3b9d-4448-a932-7c39c771554a-1754024273245@claude-code-remote>", - "sessionId": "63198b90-3b9d-4448-a932-7c39c771554a", - "token": "WYKEGTXV", - "type": "notification", - "sentAt": "2025-08-01T04:57:54.938Z" - }, - { - "messageId": "", - "sessionId": "bfd8b8d0-bcc4-4336-a6eb-8ca3f8b6c96e", - "token": "ATUBLIN1", - "type": "notification", - "sentAt": "2025-08-01T05:19:18.866Z" - }, - { - "messageId": "", - "sessionId": "cdbfc3f9-e27e-47d9-82b9-9058f0180fff", - "token": "2W24RZTM", - "type": "notification", - "sentAt": "2025-08-01T05:20:35.851Z" - }, - { - "messageId": "", - "sessionId": "cf18cb3b-bc72-46ea-985f-2b7f29f0993f", - "token": "PHAVU604", - "type": "notification", - "sentAt": "2025-08-01T05:24:16.539Z" - }, - { - "messageId": "<6da5d6f6-7e68-47eb-8676-175d77f0ddbc-1754026029245@claude-code-remote>", - "sessionId": "6da5d6f6-7e68-47eb-8676-175d77f0ddbc", - "token": "C2APCY3V", - "type": "notification", - "sentAt": "2025-08-01T05:27:10.601Z" - }, - { - "messageId": "<9143d717-be05-4869-b40a-2c775addcfc4-1754026119754@claude-code-remote>", - "sessionId": "9143d717-be05-4869-b40a-2c775addcfc4", - "token": "N5P4GUG3", - "type": "notification", - "sentAt": "2025-08-01T05:28:41.439Z" - }, - { - "messageId": "<6fb2932d-d4ac-427b-8b7d-54af44fee1f2-1754026220881@claude-code-remote>", - "sessionId": "6fb2932d-d4ac-427b-8b7d-54af44fee1f2", - "token": "CZIKHCFO", - "type": "notification", - "sentAt": "2025-08-01T05:30:22.303Z" - } - ] + "messages": [] } \ No newline at end of file diff --git a/src/data/session-map.json b/src/data/session-map.json index bfe17e4..9e26dfe 100644 --- a/src/data/session-map.json +++ b/src/data/session-map.json @@ -1,731 +1 @@ -{ - "7HUMGXOT": { - "type": "pty", - "createdAt": 1753601264, - "expiresAt": 1753687664, - "cwd": "/Users/jessytsui/dev/TaskPing", - "sessionId": "3248adc2-eb7b-4eb2-a57c-b9ce320cb4ec", - "tmuxSession": "hailuo", - "description": "completed - TaskPing" - }, - "5CLDW6NQ": { - "type": "pty", - "createdAt": 1753602124, - "expiresAt": 1753688524, - "cwd": "/Users/jessytsui/dev/TaskPing", - "sessionId": "c7b6750f-6246-4ed3-bca5-81201ab980ee", - "tmuxSession": "hailuo", - "description": "completed - TaskPing" - }, - "ONY66DAE": { - "type": "pty", - "createdAt": 1753604311, - "expiresAt": 1753690711, - "cwd": "/Users/jessytsui/dev/TaskPing", - "sessionId": "2a8622dd-a0e9-4f4d-9cc8-a3bb432e9621", - "tmuxSession": "hailuo", - "description": "waiting - TaskPing" - }, - "QHFI9FIJ": { - "type": "pty", - "createdAt": 1753607753, - "expiresAt": 1753694153, - "cwd": "/Users/jessytsui/dev/TaskPing", - "sessionId": "a966681d-5cfd-47b9-bb1b-c7ee9655b97b", - "tmuxSession": "a-0", - "description": "waiting - TaskPing" - }, - "G3QE3STQ": { - "type": "pty", - "createdAt": 1753622403, - "expiresAt": 1753708803, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "7f6e11b3-0ac9-44b1-a75f-d3a15a8ec46e", - "tmuxSession": "claude-taskping", - "description": "completed - TaskPing-Test" - }, - "Z0Q98XCC": { - "type": "pty", - "createdAt": 1753624374, - "expiresAt": 1753710774, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "b2408839-8a64-4a07-8ceb-4a7a82ea5b25", - "tmuxSession": "claude-taskping", - "description": "completed - TaskPing-Test" - }, - "65S5UGHZ": { - "type": "pty", - "createdAt": 1753624496, - "expiresAt": 1753710896, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "117a1097-dd97-41ab-a276-e4820adc8da8", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "N9PPKGTO": { - "type": "pty", - "createdAt": 1753624605, - "expiresAt": 1753711005, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "0caed3af-35ae-4b42-9081-b1a959735bde", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "TEST12345": { - "type": "pty", - "createdAt": 1753628000, - "expiresAt": 1753714400, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "test-session-id", - "tmuxSession": "test-claude", - "description": "testing - Test Session" - }, - "YTGT6F6F": { - "type": "pty", - "createdAt": 1753625040, - "expiresAt": 1753711440, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "e6e973b6-20dd-497f-a988-02482af63336", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "2XQP1N0P": { - "type": "pty", - "createdAt": 1753625361, - "expiresAt": 1753711761, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "0f694f4c-f8a4-476a-946a-3dc057c3bc46", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "GKPSGCBS": { - "type": "pty", - "createdAt": 1753625618, - "expiresAt": 1753712018, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "e844d2ae-9098-4528-9e05-e77904a35be3", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "187JDGZ0": { - "type": "pty", - "createdAt": 1753625623, - "expiresAt": 1753712023, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "633a2687-81e7-456e-9995-3321ce3f3b2b", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "NSYKTAWC": { - "type": "pty", - "createdAt": 1753625650, - "expiresAt": 1753712050, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "b8dac307-8b4b-4286-aa73-324b9b659e60", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "1NTEJPH7": { - "type": "pty", - "createdAt": 1753625743, - "expiresAt": 1753712143, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "09466a43-c495-4a30-ac08-eb425748a28c", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "5XO64F9Z": { - "type": "pty", - "createdAt": 1753625846, - "expiresAt": 1753712246, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "99132172-7a97-46f7-b282-b22054d6e599", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "D8561S3A": { - "type": "pty", - "createdAt": 1753625904, - "expiresAt": 1753712304, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "772628f1-414b-4242-bc8f-660ad53b6c23", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "GR0GED2E": { - "type": "pty", - "createdAt": 1753626215, - "expiresAt": 1753712615, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "da40ba76-7047-41e0-95f2-081db87c1b3b", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "TTRQKVM9": { - "type": "pty", - "createdAt": 1753626245, - "expiresAt": 1753712645, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "c7c5c95d-4541-47f6-b27a-35c0fd563413", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "P9UBHY8L": { - "type": "pty", - "createdAt": 1753626325, - "expiresAt": 1753712725, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "a3f2b4f9-811e-4721-914f-f025919c2530", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "JQAOXCYJ": { - "type": "pty", - "createdAt": 1753626390, - "expiresAt": 1753712790, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "f0d0635b-59f2-45eb-acfc-d649b12fd2d6", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "B7R9OR3K": { - "type": "pty", - "createdAt": 1753626445, - "expiresAt": 1753712845, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "d33e49aa-a58f-46b0-8829-dfef7f474600", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "0KGM60XO": { - "type": "pty", - "createdAt": 1753626569, - "expiresAt": 1753712969, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "02bd4449-bdcf-464e-916e-61bc62a18dd2", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "5NXM173C": { - "type": "pty", - "createdAt": 1753626834, - "expiresAt": 1753713234, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "f8f915ee-ab64-471c-b3d2-71cb84d2b5fe", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "FU0A5E79": { - "type": "pty", - "createdAt": 1753627838, - "expiresAt": 1753714238, - "cwd": "/Users/jessytsui/dev/coverr_video", - "sessionId": "1df42826-eb8e-47ee-bc5a-5886a1234d84", - "tmuxSession": "video", - "description": "completed - coverr_video" - }, - "W3Y4UNV2": { - "type": "pty", - "createdAt": 1753627955, - "expiresAt": 1753714355, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "23f16dc9-4668-4f34-b650-7919d325efd9", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "69PWGS1L": { - "type": "pty", - "createdAt": 1753628092, - "expiresAt": 1753714492, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "8e847e83-677c-4b6a-9be8-184615731c9a", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "BP5WJBYZ": { - "type": "pty", - "createdAt": 1753631837, - "expiresAt": 1753718237, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "0c7ba308-f6da-44bc-a622-8bdb4c7613a5", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "IS18V1WH": { - "type": "pty", - "createdAt": 1753631949, - "expiresAt": 1753718349, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "563a4b27-f459-40fa-aa7b-df9c46643bab", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "QGLEAJLO": { - "type": "pty", - "createdAt": 1753632059, - "expiresAt": 1753718459, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "e81186b8-c990-42a7-a1ec-16009e671f32", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "66VKI9JR": { - "type": "pty", - "createdAt": 1753633791, - "expiresAt": 1753720191, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "29afd212-bfbb-443e-8e9f-7c4a08a9f178", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "MROLF6JQ": { - "type": "pty", - "createdAt": 1753633885, - "expiresAt": 1753720285, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "6856d493-a825-45e9-ad87-d3dab6ebd3ed", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "JH6IROYI": { - "type": "pty", - "createdAt": 1753635239, - "expiresAt": 1753721639, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "40829580-9b42-45c2-af54-994343101a4b", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "18E1GX7G": { - "type": "pty", - "createdAt": 1753635293, - "expiresAt": 1753721693, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "d9524fbe-8915-4175-89e0-9358ccd86921", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "8CZC4SWF": { - "type": "pty", - "createdAt": 1753635381, - "expiresAt": 1753721781, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "5d7673c5-5541-4c71-8f33-8270cde90376", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "L40V7BIU": { - "type": "pty", - "createdAt": 1753636194, - "expiresAt": 1753722594, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "b4112f01-a8f5-44fe-8bb6-85b1f16d5cd9", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "X0PKR9KZ": { - "type": "pty", - "createdAt": 1753636706, - "expiresAt": 1753723106, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "77747aee-ec17-4356-86be-ca20245eaa4c", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "K3G8GEM4": { - "type": "pty", - "createdAt": 1753636765, - "expiresAt": 1753723165, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "5b70bf42-ed29-4004-afad-a337be665c74", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "1U1074XQ": { - "type": "pty", - "createdAt": 1753636941, - "expiresAt": 1753723341, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "212bd122-ff8e-4299-88cc-93fed221ad2b", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "04Z5FEHE": { - "type": "pty", - "createdAt": 1753637049, - "expiresAt": 1753723449, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "da16cada-9121-47c7-b7b8-4b47cbc46c7c", - "tmuxSession": "claude-taskping", - "description": "completed - Claude-Code-Remote" - }, - "MSRLCGLS": { - "type": "pty", - "createdAt": 1753638288, - "expiresAt": 1753724688, - "cwd": "/Users/jessytsui", - "sessionId": "d245e06c-9595-48f9-8ef1-c3f826352d75", - "tmuxSession": "claude-taskping", - "description": "completed - jessytsui" - }, - "7O21O9ZR": { - "type": "pty", - "createdAt": 1753638308, - "expiresAt": 1753724708, - "cwd": "/Users/jessytsui", - "sessionId": "6ec8789d-5eb0-4138-9180-f32559336906", - "tmuxSession": "claude-taskping", - "description": "completed - jessytsui" - }, - "4S5KSCI3": { - "type": "pty", - "createdAt": 1753639559, - "expiresAt": 1753725959, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "302103bd-522f-4349-8041-979a62c606d8", - "tmuxSession": "claude-code-remote", - "description": "completed - Claude-Code-Remote" - }, - "VR3AB0NO": { - "type": "pty", - "createdAt": 1753640175, - "expiresAt": 1753726575, - "cwd": "/Users/jessytsui/dev/coverr_video", - "sessionId": "6808bb5d-4268-4f46-aad7-0f01677f781e", - "tmuxSession": "claude-code-remote", - "description": "completed - coverr_video" - }, - "RACVYH67": { - "type": "pty", - "createdAt": 1753640283, - "expiresAt": 1753726683, - "cwd": "/Users/jessytsui/dev/coverr_video", - "sessionId": "4d23ece0-6931-4d6d-bb89-b2ba2709bba6", - "tmuxSession": "claude-code-remote", - "description": "completed - coverr_video" - }, - "XBUMC8TX": { - "type": "pty", - "createdAt": 1753640359, - "expiresAt": 1753726759, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "50436697-1576-4f70-9ea7-39ae61261192", - "tmuxSession": "claude-code-remote", - "description": "completed - Claude-Code-Remote" - }, - "6L3BS2G9": { - "type": "pty", - "createdAt": 1753640370, - "expiresAt": 1753726770, - "cwd": "/Users/jessytsui/dev/coverr_video", - "sessionId": "5ec3baf8-8dcd-44b6-9583-4fac336c92e2", - "tmuxSession": "claude-code-remote", - "description": "completed - coverr_video" - }, - "ODFO312K": { - "type": "pty", - "createdAt": 1753640410, - "expiresAt": 1753726810, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "616f15e8-0ae6-4a3b-ac02-373bca701630", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "6XWPQCEF": { - "type": "pty", - "createdAt": 1753640528, - "expiresAt": 1753726928, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "b6d2d75b-c4b7-46cb-b922-d4f935e5424d", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "ZGXY36O1": { - "type": "pty", - "createdAt": 1753640690, - "expiresAt": 1753727090, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "3ce40b4d-3273-4662-a6cf-ead30c91f950", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "MUE5LCUY": { - "type": "pty", - "createdAt": 1753684860, - "expiresAt": 1753771260, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "698b6d2e-32f0-4eaa-8aa4-5bbf408b9640", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "KZVOG2PO": { - "type": "pty", - "createdAt": 1753684912, - "expiresAt": 1753771312, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "0f63ad7f-2312-427f-b1e6-d186a6be3667", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "5HDFXY41": { - "type": "pty", - "createdAt": 1753684929, - "expiresAt": 1753771329, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "22481ebb-0f9f-4f52-9668-347ed0fb0f98", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "NHXQJN3L": { - "type": "pty", - "createdAt": 1753685042, - "expiresAt": 1753771442, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "a1b56007-ef57-48bb-8aa7-449bc5f9b1f7", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "84LX5NLI": { - "type": "pty", - "createdAt": 1753685161, - "expiresAt": 1753771561, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "3535a857-c5da-4764-9367-9816955bfc30", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "3WYHK10U": { - "type": "pty", - "createdAt": 1753685478, - "expiresAt": 1753771878, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "68eca4f9-79f4-4047-ab98-636cd742deee", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "INZLQTW0": { - "type": "pty", - "createdAt": 1753685542, - "expiresAt": 1753771942, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "4bc09041-a751-43c0-93e1-f3b5a69d6aa1", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "TPV9W6VE": { - "type": "pty", - "createdAt": 1753685640, - "expiresAt": 1753772040, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "023bb55f-acc0-4d8a-a2f4-79be0bd9a863", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "IUNWWLOT": { - "type": "pty", - "createdAt": 1753685816, - "expiresAt": 1753772216, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "4e67ac76-5c0a-4229-b3cd-c4ff865c9df3", - "tmuxSession": "video", - "description": "completed - Claude-Code-Remote" - }, - "3E0T4KHA": { - "type": "pty", - "createdAt": 1754020190, - "expiresAt": 1754106590, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "1ce6c5e0-4151-4d51-9472-f481ed23f023", - "tmuxSession": "WavJaby", - "description": "completed - Claude-Code-Remote" - }, - "8BIFRACK": { - "type": "pty", - "createdAt": 1754020301, - "expiresAt": 1754106701, - "cwd": "/Users/vaclis./Documents/project/ReThreads", - "sessionId": "0fd97a36-da77-4c9b-917f-6963c0373458", - "tmuxSession": "my-project", - "description": "completed - ReThreads" - }, - "OG1SS2R9": { - "type": "pty", - "createdAt": 1754020306, - "expiresAt": 1754106706, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "43b979a7-9039-4d0f-8c09-b86365ef0e61", - "tmuxSession": "WavJaby", - "description": "completed - Claude-Code-Remote" - }, - "49WUF9NS": { - "type": "pty", - "createdAt": 1754021037, - "expiresAt": 1754107437, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "52d15aa1-d5a4-4d7d-8f01-4752d7d5fc6f", - "tmuxSession": "WavJaby", - "description": "completed - Claude-Code-Remote" - }, - "N9PHUN4Q": { - "type": "pty", - "createdAt": 1754021210, - "expiresAt": 1754107610, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "eba8744e-8cc1-4fad-9dc8-69d558c51cca", - "tmuxSession": "test-session", - "description": "completed - Claude-Code-Remote-Test" - }, - "GXWFSL3S": { - "type": "pty", - "createdAt": 1754021233, - "expiresAt": 1754107633, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "859daa99-1ea9-4c40-aa68-c3967a0d7e4e", - "tmuxSession": "WavJaby", - "description": "completed - Claude-Code-Remote" - }, - "6EZXA6IN": { - "type": "pty", - "createdAt": 1754021267, - "expiresAt": 1754107667, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "a1ed6757-6782-4b22-a486-aab5a9d60a3c", - "tmuxSession": "test-session", - "description": "completed - Claude-Code-Remote-Test" - }, - "ZQY1UOIJ": { - "type": "pty", - "createdAt": 1754021285, - "expiresAt": 1754107685, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "2122d57c-8434-44f0-b4e6-7eafb40ed49d", - "tmuxSession": "WavJaby", - "description": "completed - Claude-Code-Remote" - }, - "L4KQ8DVJ": { - "type": "pty", - "createdAt": 1754021533, - "expiresAt": 1754107933, - "cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote", - "sessionId": "2b30b1f7-b9c3-4cb4-b889-11c58009bd07", - "tmuxSession": "test-session", - "description": "completed - Claude-Code-Remote-Test" - }, - "7ZFMSK5P": { - "type": "pty", - "createdAt": 1754022640, - "expiresAt": 1754109040, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "e3392712-9a5b-4c96-a23c-5d97c226b415", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "PJOE746Q": { - "type": "pty", - "createdAt": 1754022731, - "expiresAt": 1754109131, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "5d19da23-5c9d-4855-824e-fbd09a0d986c", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "C9I24JHD": { - "type": "pty", - "createdAt": 1754022927, - "expiresAt": 1754109327, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "ed814c4b-0b97-4e10-8ad1-dae1a7cb7ef9", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "GGBKJ2DV": { - "type": "pty", - "createdAt": 1754023256, - "expiresAt": 1754109656, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "46ae2185-2f70-4355-a3b8-9fc04fa01181", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "DQH198YY": { - "type": "pty", - "createdAt": 1754023825, - "expiresAt": 1754110225, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "49c51c77-8ecc-4a04-a7a8-5955f89411b1", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "GQE6LLIF": { - "type": "pty", - "createdAt": 1754024054, - "expiresAt": 1754110454, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "cc5a1eba-7e04-44e6-919f-0395ddf2ee08", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "WYKEGTXV": { - "type": "pty", - "createdAt": 1754024273, - "expiresAt": 1754110673, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "63198b90-3b9d-4448-a932-7c39c771554a", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "ATUBLIN1": { - "type": "pty", - "createdAt": 1754025557, - "expiresAt": 1754111957, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "bfd8b8d0-bcc4-4336-a6eb-8ca3f8b6c96e", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "2W24RZTM": { - "type": "pty", - "createdAt": 1754025634, - "expiresAt": 1754112034, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "cdbfc3f9-e27e-47d9-82b9-9058f0180fff", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "PHAVU604": { - "type": "pty", - "createdAt": 1754025855, - "expiresAt": 1754112255, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "cf18cb3b-bc72-46ea-985f-2b7f29f0993f", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "C2APCY3V": { - "type": "pty", - "createdAt": 1754026029, - "expiresAt": 1754112429, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "6da5d6f6-7e68-47eb-8676-175d77f0ddbc", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "N5P4GUG3": { - "type": "pty", - "createdAt": 1754026119, - "expiresAt": 1754112519, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "9143d717-be05-4869-b40a-2c775addcfc4", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - }, - "CZIKHCFO": { - "type": "pty", - "createdAt": 1754026220, - "expiresAt": 1754112620, - "cwd": "/Users/jessytsui/dev/Claude-Code-Remote", - "sessionId": "6fb2932d-d4ac-427b-8b7d-54af44fee1f2", - "tmuxSession": "a", - "description": "completed - Claude-Code-Remote" - } -} \ No newline at end of file +{} \ No newline at end of file diff --git a/src/data/subagent-activities.json b/src/data/subagent-activities.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/data/subagent-activities.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/utils/subagent-tracker.js b/src/utils/subagent-tracker.js new file mode 100644 index 0000000..1f51940 --- /dev/null +++ b/src/utils/subagent-tracker.js @@ -0,0 +1,206 @@ +/** + * Subagent Activity Tracker + * Tracks subagent activities for including in completion emails + */ + +const fs = require('fs'); +const path = require('path'); + +class SubagentTracker { + constructor() { + this.dataDir = path.join(__dirname, '../data'); + this.trackingFile = path.join(this.dataDir, 'subagent-activities.json'); + this._ensureDataDir(); + } + + _escapeHtml(text) { + if (!text) return ''; + const htmlEntities = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return String(text).replace(/[&<>"']/g, char => htmlEntities[char]); + } + + _ensureDataDir() { + if (!fs.existsSync(this.dataDir)) { + fs.mkdirSync(this.dataDir, { recursive: true }); + } + } + + /** + * Load existing activities + */ + _loadActivities() { + if (!fs.existsSync(this.trackingFile)) { + return {}; + } + try { + return JSON.parse(fs.readFileSync(this.trackingFile, 'utf8')); + } catch (error) { + console.error('Failed to load subagent activities:', error); + return {}; + } + } + + /** + * Save activities to file + */ + _saveActivities(activities) { + try { + fs.writeFileSync(this.trackingFile, JSON.stringify(activities, null, 2)); + } catch (error) { + console.error('Failed to save subagent activities:', error); + } + } + + /** + * Add a subagent activity + */ + addActivity(sessionId, activity) { + const activities = this._loadActivities(); + + if (!activities[sessionId]) { + activities[sessionId] = { + startTime: new Date().toISOString(), + activities: [] + }; + } + + activities[sessionId].activities.push({ + timestamp: new Date().toISOString(), + type: activity.type || 'subagent', + description: activity.description || 'Subagent activity', + details: activity.details || {} + }); + + this._saveActivities(activities); + } + + /** + * Get activities for a session + */ + getActivities(sessionId) { + const activities = this._loadActivities(); + return activities[sessionId] || null; + } + + /** + * Clear activities for a session + */ + clearActivities(sessionId) { + const activities = this._loadActivities(); + delete activities[sessionId]; + this._saveActivities(activities); + } + + /** + * Clean up old activities (older than 24 hours) + */ + cleanupOldActivities() { + const activities = this._loadActivities(); + const now = new Date(); + const oneDayAgo = new Date(now - 24 * 60 * 60 * 1000); + + for (const sessionId in activities) { + const startTime = new Date(activities[sessionId].startTime); + if (startTime < oneDayAgo) { + delete activities[sessionId]; + } + } + + this._saveActivities(activities); + } + + /** + * Format activities for email + */ + formatActivitiesForEmail(sessionId) { + const sessionData = this.getActivities(sessionId); + if (!sessionData || sessionData.activities.length === 0) { + return ''; + } + + const activities = sessionData.activities; + const grouped = {}; + + // Group activities by type + activities.forEach(activity => { + const type = activity.type; + if (!grouped[type]) { + grouped[type] = []; + } + grouped[type].push(activity); + }); + + // Format as HTML + let html = ` + +
+
šŸ“Š Subagent Activities Summary
+
`; + + for (const [type, items] of Object.entries(grouped)) { + html += `
`; + html += `
${type} (${items.length} activities)
`; + html += `
    `; + items.forEach((item, index) => { + const time = new Date(item.timestamp).toLocaleTimeString(); + html += `
  • `; + html += `
    `; + html += `
    [${time}] ${this._escapeHtml(item.description || 'Subagent task')}
    `; + + if (item.details) { + // Show the question if available + if (item.details.userQuestion && item.details.userQuestion !== item.description) { + html += `
    → Question: ${this._escapeHtml(item.details.userQuestion)}
    `; + } + + // Show the response + if (item.details.claudeResponse) { + const response = item.details.claudeResponse; + // Check if this is just initialization text + if (response.includes('Initializing...') || response.includes('Concocting...')) { + html += `
    ā³ Subagent was processing... (full output available in tmux session)
    `; + } else { + html += `
    `; + html += this._escapeHtml(response); + html += `
    `; + } + } + } + html += `
    `; + html += `
  • `; + }); + html += `
`; + html += `
`; + } + + html += ` +
+
`; + + // Also format as plain text for text email + let text = '\nšŸ“Š Subagent Activities Summary\n\n'; + + for (const [type, items] of Object.entries(grouped)) { + text += `${type} (${items.length} activities)\n`; + items.forEach((item, index) => { + const time = new Date(item.timestamp).toLocaleTimeString(); + text += ` ${index + 1}. [${time}] ${item.description}\n`; + if (item.details && item.details.claudeResponse) { + text += ` ${item.details.claudeResponse}\n`; + } + }); + text += '\n'; + } + + // Return HTML for email (the email sender will use it appropriately) + return html; + } +} + +module.exports = SubagentTracker; \ No newline at end of file