+
+
+
+
+ |
+ |
+ |
+ claude-code-remote@{{project}} - Task Completed |
+
+
+
+
+
+
+
+
+
+ $ cat user_request.txt
+
+
{{userQuestion}}
+
+
+
+
+
+ $ claude-code execute
+
+<<<<<<< HEAD
+
+
+ [INFO] Processing request...
+ [INFO] Task execution started at {{timestamp}}
+
+
{{claudeResponse}}
+
+
[SUCCESS] Task completed successfully ✓
+=======
+
{{claudeResponse}}
+
+ [SUCCESS] Task completed successfully ✓
+
+
+
+ {{subagentActivities}}
+
+
+
+
$ claude-code help --continue
+
+
→ TO CONTINUE THIS SESSION:
+
+ Reply to this email directly with your next instruction.
+ Examples:
+ • "Add error handling to the function"
+ • "Write unit tests for this code"
+ • "Optimize the performance"
+>>>>>>> upstream/master
+
+
+
+
+ {{subagentActivities}}
+
+
+
+
+ $ claude-code --help continue
+
+
+
TO CONTINUE THIS SESSION:
+
+ Reply to this email directly with your next instruction.
+ Examples:
+ • "Add error handling to the function"
+ • "Write unit tests for this code"
+ • "Optimize the performance"
+
+
+
+
+
+
+
+ $ tail -n 1000 execution.log
+
+
+
+ [Click to view full execution trace]
+
+
+
{{fullExecutionTrace}}
+
+
+
+
+
+
+ `,
+ text: `
+[Claude-Code-Remote #{{token}}] Claude Code Task Completed - {{projectDir}} | {{shortQuestion}}
+
+Project: {{projectDir}}
+Time: {{timestamp}}
+Status: {{type}}
+
+📝 Your Question:
+{{userQuestion}}
+
+🤖 Claude's Response:
+{{claudeResponse}}
+
+{{subagentActivities}}
+
+<<<<<<< HEAD
+====== FULL EXECUTION TRACE ======
+{{fullExecutionTrace}}
+==================================
+
+=======
+>>>>>>> upstream/master
+How to Continue Conversation:
+To continue conversation with Claude Code, please reply to this email directly and enter your instructions in the email body.
+
+Example Replies:
+• "Please continue optimizing the code"
+• "Generate unit tests"
+• "Explain the purpose of this function"
+
+Session ID: {{sessionId}}
+Security Note: Please do not forward this email, session will automatically expire after 24 hours
+ `
+ },
+ waiting: {
+ subject: '[Claude-Code-Remote #{{token}}] Claude Code Waiting for Input - {{project}}',
+ html: `
+
+
+
+
+
+
+ |
+ |
+ |
+ claude-code-remote@{{project}} - Waiting for Input |
+
+
+
+
+
+
+
+
+
$ claude-code status
+
+ PROJECT: {{projectDir}}
+ SESSION: #{{token}}
+ STATUS: ⏳ Waiting for input
+ TIME: {{timestamp}}
+
+
+
+
+
+
$ claude-code wait
+
+ [WAITING] Claude needs your input to continue...
+
+
+ {{message}}
+
+
+
+
+
+
$ claude-code help --respond
+
+
→ ACTION REQUIRED:
+
+ Claude is waiting for your guidance.
+ Reply to this email with your instructions to continue.
+
+
+
+
+
+
+
$ echo $SESSION_INFO
+
+ SESSION_ID={{sessionId}}
+ EXPIRES_IN=24h
+ SECURITY=Do not forward this email
+ POWERED_BY=Claude-Code-Remote
+
+
+
+
+
+ `,
+ text: `
+[Claude-Code-Remote #{{token}}] Claude Code Waiting for Input - {{projectDir}}
+
+Project: {{projectDir}}
+Time: {{timestamp}}
+Status: {{type}}
+
+⏳ Waiting for Processing: {{message}}
+
+Claude needs your further guidance. Please reply to this email to tell Claude what to do next.
+
+Session ID: {{sessionId}}
+Security Note: Please do not forward this email, session will automatically expire after 24 hours
+ `
+ }
+ };
+
+ return templates[type] || templates.completed;
+ }
+
+ validateConfig() {
+ if (!this.config.smtp) {
+ return { valid: false, error: 'SMTP configuration required' };
+ }
+
+ if (!this.config.smtp.host) {
+ return { valid: false, error: 'SMTP host required' };
+ }
+
+ if (!this.config.smtp.auth || !this.config.smtp.auth.user || !this.config.smtp.auth.pass) {
+ return { valid: false, error: 'SMTP authentication required' };
+ }
+
+ if (!this.config.to) {
+ return { valid: false, error: 'Recipient email required' };
+ }
+
+ return { valid: true };
+ }
+
+ async test() {
+ try {
+ if (!this.transporter) {
+ throw new Error('Email transporter not initialized');
+ }
+
+ // Verify SMTP connection
+ await this.transporter.verify();
+
+ // Send test email
+ const testNotification = {
+ type: 'completed',
+ title: 'Claude-Code-Remote Test',
+ message: 'This is a test email to verify that the email notification function is working properly.',
+ project: 'Claude-Code-Remote-Test',
+ metadata: {
+ test: true,
+ timestamp: new Date().toISOString(),
+ userQuestion: 'This is a test notification',
+ claudeResponse: 'Email notification system is working correctly.',
+ fullExecutionTrace: `> claude-remote test
+
+🧪 Testing email notification system...
+
+[2025-08-01T06:29:28.893Z] [Config] [INFO] Configuration loaded successfully
+[2025-08-01T06:29:28.918Z] [Notifier] [INFO] Initialized 2 channels
+[2025-08-01T06:29:29.015Z] [Channel:desktop] [INFO] Notification sent successfully
+[2025-08-01T06:29:32.880Z] [Channel:email] [INFO] Email sent successfully
+
+✅ Test completed successfully!
+
+This is a test trace to demonstrate how the full execution trace will appear in actual usage.
+When Claude Code completes a task, this section will contain the complete terminal output including:
+- User commands
+- Claude's responses
+- Subagent activities
+- Error messages
+- Debug information
+
+The trace provides complete transparency about what happened during task execution.`
+ }
+ };
+
+ const result = await this._sendImpl(testNotification);
+ return result;
+ } catch (error) {
+ this.logger.error('Email test failed:', error.message);
+ return false;
+ }
+ }
+
+ getStatus() {
+ const baseStatus = super.getStatus();
+ return {
+ ...baseStatus,
+ configured: this.validateConfig().valid,
+ supportsRelay: true,
+ smtp: {
+ host: this.config.smtp?.host || 'not configured',
+ port: this.config.smtp?.port || 'not configured',
+ secure: this.config.smtp?.secure || false
+ },
+ recipient: this.config.to || 'not configured'
+ };
+ }
+}
+
+module.exports = EmailChannel;
\ No newline at end of file
diff --git a/src/data/processed-messages.json b/src/data/processed-messages.json
deleted file mode 100644
index 5c0b24a..0000000
--- a/src/data/processed-messages.json
+++ /dev/null
@@ -1,46 +0,0 @@
-[
- {
- "id": 1312,
- "timestamp": 1754022727524
- },
- {
- "id": 1315,
- "timestamp": 1754022727524
- },
- {
- "id": 1310,
- "timestamp": 1754022727524
- },
- {
- "id": 1323,
- "timestamp": 1754022727524
- },
- {
- "id": 1331,
- "timestamp": 1754022727524
- },
- {
- "id": 1334,
- "timestamp": 1754022727524
- },
- {
- "id": 1342,
- "timestamp": 1754022727524
- },
- {
- "id": 1346,
- "timestamp": 1754022727524
- },
- {
- "id": 1348,
- "timestamp": 1754022727524
- },
- {
- "id": 180,
- "timestamp": 1754022727524
- },
- {
- "id": 1691,
- "timestamp": 1754022727524
- }
-]
\ No newline at end of file
diff --git a/src/data/sent-messages.json b/src/data/sent-messages.json
deleted file mode 100644
index b379c13..0000000
--- a/src/data/sent-messages.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "messages": []
-}
\ No newline at end of file
diff --git a/src/data/session-map.json b/src/data/session-map.json
deleted file mode 100644
index 9e26dfe..0000000
--- a/src/data/session-map.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
\ No newline at end of file
diff --git a/src/data/sessions/02bd4449-bdcf-464e-916e-61bc62a18dd2.json b/src/data/sessions/02bd4449-bdcf-464e-916e-61bc62a18dd2.json
deleted file mode 100644
index 2836d10..0000000
--- a/src/data/sessions/02bd4449-bdcf-464e-916e-61bc62a18dd2.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "02bd4449-bdcf-464e-916e-61bc62a18dd2",
- "token": "0KGM60XO",
- "type": "pty",
- "created": "2025-07-27T14:29:29.132Z",
- "expires": "2025-07-28T14:29:29.132Z",
- "createdAt": 1753626569,
- "expiresAt": 1753712969,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/09466a43-c495-4a30-ac08-eb425748a28c.json b/src/data/sessions/09466a43-c495-4a30-ac08-eb425748a28c.json
deleted file mode 100644
index 42856ac..0000000
--- a/src/data/sessions/09466a43-c495-4a30-ac08-eb425748a28c.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "09466a43-c495-4a30-ac08-eb425748a28c",
- "token": "1NTEJPH7",
- "type": "pty",
- "created": "2025-07-27T14:15:43.425Z",
- "expires": "2025-07-28T14:15:43.425Z",
- "createdAt": 1753625743,
- "expiresAt": 1753712143,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/0caed3af-35ae-4b42-9081-b1a959735bde.json b/src/data/sessions/0caed3af-35ae-4b42-9081-b1a959735bde.json
deleted file mode 100644
index 63dff0f..0000000
--- a/src/data/sessions/0caed3af-35ae-4b42-9081-b1a959735bde.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "0caed3af-35ae-4b42-9081-b1a959735bde",
- "token": "N9PPKGTO",
- "type": "pty",
- "created": "2025-07-27T13:56:45.557Z",
- "expires": "2025-07-28T13:56:45.557Z",
- "createdAt": 1753624605,
- "expiresAt": 1753711005,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/0f694f4c-f8a4-476a-946a-3dc057c3bc46.json b/src/data/sessions/0f694f4c-f8a4-476a-946a-3dc057c3bc46.json
deleted file mode 100644
index 009627e..0000000
--- a/src/data/sessions/0f694f4c-f8a4-476a-946a-3dc057c3bc46.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "0f694f4c-f8a4-476a-946a-3dc057c3bc46",
- "token": "2XQP1N0P",
- "type": "pty",
- "created": "2025-07-27T14:09:21.440Z",
- "expires": "2025-07-28T14:09:21.440Z",
- "createdAt": 1753625361,
- "expiresAt": 1753711761,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/117a1097-dd97-41ab-a276-e4820adc8da8.json b/src/data/sessions/117a1097-dd97-41ab-a276-e4820adc8da8.json
deleted file mode 100644
index 3e835a1..0000000
--- a/src/data/sessions/117a1097-dd97-41ab-a276-e4820adc8da8.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "117a1097-dd97-41ab-a276-e4820adc8da8",
- "token": "65S5UGHZ",
- "type": "pty",
- "created": "2025-07-27T13:54:56.875Z",
- "expires": "2025-07-28T13:54:56.875Z",
- "createdAt": 1753624496,
- "expiresAt": 1753710896,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/2a8622dd-a0e9-4f4d-9cc8-a3bb432e9621.json b/src/data/sessions/2a8622dd-a0e9-4f4d-9cc8-a3bb432e9621.json
deleted file mode 100644
index f14d71d..0000000
--- a/src/data/sessions/2a8622dd-a0e9-4f4d-9cc8-a3bb432e9621.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "2a8622dd-a0e9-4f4d-9cc8-a3bb432e9621",
- "token": "ONY66DAE",
- "type": "pty",
- "created": "2025-07-27T08:18:31.722Z",
- "expires": "2025-07-28T08:18:31.722Z",
- "createdAt": 1753604311,
- "expiresAt": 1753690711,
- "cwd": "/Users/jessytsui/dev/TaskPing",
- "notification": {
- "type": "waiting",
- "project": "TaskPing",
- "message": "[TaskPing] Claude需要您的进一步指导"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/3248adc2-eb7b-4eb2-a57c-b9ce320cb4ec.json b/src/data/sessions/3248adc2-eb7b-4eb2-a57c-b9ce320cb4ec.json
deleted file mode 100644
index 5650c34..0000000
--- a/src/data/sessions/3248adc2-eb7b-4eb2-a57c-b9ce320cb4ec.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "3248adc2-eb7b-4eb2-a57c-b9ce320cb4ec",
- "token": "7HUMGXOT",
- "type": "pty",
- "created": "2025-07-27T07:27:44.413Z",
- "expires": "2025-07-28T07:27:44.413Z",
- "createdAt": 1753601264,
- "expiresAt": 1753687664,
- "cwd": "/Users/jessytsui/dev/TaskPing",
- "notification": {
- "type": "completed",
- "project": "TaskPing",
- "message": "[TaskPing] 任务已完成,Claude正在等待下一步指令"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/633a2687-81e7-456e-9995-3321ce3f3b2b.json b/src/data/sessions/633a2687-81e7-456e-9995-3321ce3f3b2b.json
deleted file mode 100644
index 9f9c64b..0000000
--- a/src/data/sessions/633a2687-81e7-456e-9995-3321ce3f3b2b.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "633a2687-81e7-456e-9995-3321ce3f3b2b",
- "token": "187JDGZ0",
- "type": "pty",
- "created": "2025-07-27T14:13:43.575Z",
- "expires": "2025-07-28T14:13:43.575Z",
- "createdAt": 1753625623,
- "expiresAt": 1753712023,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/772628f1-414b-4242-bc8f-660ad53b6c23.json b/src/data/sessions/772628f1-414b-4242-bc8f-660ad53b6c23.json
deleted file mode 100644
index d924513..0000000
--- a/src/data/sessions/772628f1-414b-4242-bc8f-660ad53b6c23.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "772628f1-414b-4242-bc8f-660ad53b6c23",
- "token": "D8561S3A",
- "type": "pty",
- "created": "2025-07-27T14:18:24.930Z",
- "expires": "2025-07-28T14:18:24.930Z",
- "createdAt": 1753625904,
- "expiresAt": 1753712304,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/7f6e11b3-0ac9-44b1-a75f-d3a15a8ec46e.json b/src/data/sessions/7f6e11b3-0ac9-44b1-a75f-d3a15a8ec46e.json
deleted file mode 100644
index 20d53bd..0000000
--- a/src/data/sessions/7f6e11b3-0ac9-44b1-a75f-d3a15a8ec46e.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "7f6e11b3-0ac9-44b1-a75f-d3a15a8ec46e",
- "token": "G3QE3STQ",
- "type": "pty",
- "created": "2025-07-27T13:20:03.954Z",
- "expires": "2025-07-28T13:20:03.954Z",
- "createdAt": 1753622403,
- "expiresAt": 1753708803,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "TaskPing-Test",
- "message": "This is a test email to verify that the email notification function is working properly."
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/99132172-7a97-46f7-b282-b22054d6e599.json b/src/data/sessions/99132172-7a97-46f7-b282-b22054d6e599.json
deleted file mode 100644
index b0d6e1f..0000000
--- a/src/data/sessions/99132172-7a97-46f7-b282-b22054d6e599.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "99132172-7a97-46f7-b282-b22054d6e599",
- "token": "5XO64F9Z",
- "type": "pty",
- "created": "2025-07-27T14:17:26.966Z",
- "expires": "2025-07-28T14:17:26.966Z",
- "createdAt": 1753625846,
- "expiresAt": 1753712246,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/a3f2b4f9-811e-4721-914f-f025919c2530.json b/src/data/sessions/a3f2b4f9-811e-4721-914f-f025919c2530.json
deleted file mode 100644
index ad54ee6..0000000
--- a/src/data/sessions/a3f2b4f9-811e-4721-914f-f025919c2530.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "a3f2b4f9-811e-4721-914f-f025919c2530",
- "token": "P9UBHY8L",
- "type": "pty",
- "created": "2025-07-27T14:25:25.848Z",
- "expires": "2025-07-28T14:25:25.848Z",
- "createdAt": 1753626325,
- "expiresAt": 1753712725,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/a966681d-5cfd-47b9-bb1b-c7ee9655b97b.json b/src/data/sessions/a966681d-5cfd-47b9-bb1b-c7ee9655b97b.json
deleted file mode 100644
index d6ade57..0000000
--- a/src/data/sessions/a966681d-5cfd-47b9-bb1b-c7ee9655b97b.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "a966681d-5cfd-47b9-bb1b-c7ee9655b97b",
- "token": "QHFI9FIJ",
- "type": "pty",
- "created": "2025-07-27T09:15:53.533Z",
- "expires": "2025-07-28T09:15:53.533Z",
- "createdAt": 1753607753,
- "expiresAt": 1753694153,
- "cwd": "/Users/jessytsui/dev/TaskPing",
- "notification": {
- "type": "waiting",
- "project": "TaskPing",
- "message": "[TaskPing] Claude needs your further guidance"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/b2408839-8a64-4a07-8ceb-4a7a82ea5b25.json b/src/data/sessions/b2408839-8a64-4a07-8ceb-4a7a82ea5b25.json
deleted file mode 100644
index d11d1b7..0000000
--- a/src/data/sessions/b2408839-8a64-4a07-8ceb-4a7a82ea5b25.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "b2408839-8a64-4a07-8ceb-4a7a82ea5b25",
- "token": "Z0Q98XCC",
- "type": "pty",
- "created": "2025-07-27T13:52:54.736Z",
- "expires": "2025-07-28T13:52:54.736Z",
- "createdAt": 1753624374,
- "expiresAt": 1753710774,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "TaskPing-Test",
- "message": "This is a test email to verify that the email notification function is working properly."
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/b8dac307-8b4b-4286-aa73-324b9b659e60.json b/src/data/sessions/b8dac307-8b4b-4286-aa73-324b9b659e60.json
deleted file mode 100644
index 2601b0d..0000000
--- a/src/data/sessions/b8dac307-8b4b-4286-aa73-324b9b659e60.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "b8dac307-8b4b-4286-aa73-324b9b659e60",
- "token": "NSYKTAWC",
- "type": "pty",
- "created": "2025-07-27T14:14:10.103Z",
- "expires": "2025-07-28T14:14:10.103Z",
- "createdAt": 1753625650,
- "expiresAt": 1753712050,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/c7b6750f-6246-4ed3-bca5-81201ab980ee.json b/src/data/sessions/c7b6750f-6246-4ed3-bca5-81201ab980ee.json
deleted file mode 100644
index 800cf71..0000000
--- a/src/data/sessions/c7b6750f-6246-4ed3-bca5-81201ab980ee.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "c7b6750f-6246-4ed3-bca5-81201ab980ee",
- "token": "5CLDW6NQ",
- "type": "pty",
- "created": "2025-07-27T07:42:04.988Z",
- "expires": "2025-07-28T07:42:04.988Z",
- "createdAt": 1753602124,
- "expiresAt": 1753688524,
- "cwd": "/Users/jessytsui/dev/TaskPing",
- "notification": {
- "type": "completed",
- "project": "TaskPing",
- "message": "[TaskPing] 任务已完成,Claude正在等待下一步指令"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/c7c5c95d-4541-47f6-b27a-35c0fd563413.json b/src/data/sessions/c7c5c95d-4541-47f6-b27a-35c0fd563413.json
deleted file mode 100644
index 5b8a21f..0000000
--- a/src/data/sessions/c7c5c95d-4541-47f6-b27a-35c0fd563413.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "c7c5c95d-4541-47f6-b27a-35c0fd563413",
- "token": "TTRQKVM9",
- "type": "pty",
- "created": "2025-07-27T14:24:05.433Z",
- "expires": "2025-07-28T14:24:05.433Z",
- "createdAt": 1753626245,
- "expiresAt": 1753712645,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/d33e49aa-a58f-46b0-8829-dfef7f474600.json b/src/data/sessions/d33e49aa-a58f-46b0-8829-dfef7f474600.json
deleted file mode 100644
index 5e3ff0b..0000000
--- a/src/data/sessions/d33e49aa-a58f-46b0-8829-dfef7f474600.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "d33e49aa-a58f-46b0-8829-dfef7f474600",
- "token": "B7R9OR3K",
- "type": "pty",
- "created": "2025-07-27T14:27:25.292Z",
- "expires": "2025-07-28T14:27:25.292Z",
- "createdAt": 1753626445,
- "expiresAt": 1753712845,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/da40ba76-7047-41e0-95f2-081db87c1b3b.json b/src/data/sessions/da40ba76-7047-41e0-95f2-081db87c1b3b.json
deleted file mode 100644
index e89de3b..0000000
--- a/src/data/sessions/da40ba76-7047-41e0-95f2-081db87c1b3b.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "da40ba76-7047-41e0-95f2-081db87c1b3b",
- "token": "GR0GED2E",
- "type": "pty",
- "created": "2025-07-27T14:23:35.122Z",
- "expires": "2025-07-28T14:23:35.122Z",
- "createdAt": 1753626215,
- "expiresAt": 1753712615,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/e6e973b6-20dd-497f-a988-02482af63336.json b/src/data/sessions/e6e973b6-20dd-497f-a988-02482af63336.json
deleted file mode 100644
index 9a3594d..0000000
--- a/src/data/sessions/e6e973b6-20dd-497f-a988-02482af63336.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "e6e973b6-20dd-497f-a988-02482af63336",
- "token": "YTGT6F6F",
- "type": "pty",
- "created": "2025-07-27T14:04:00.467Z",
- "expires": "2025-07-28T14:04:00.467Z",
- "createdAt": 1753625040,
- "expiresAt": 1753711440,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/e844d2ae-9098-4528-9e05-e77904a35be3.json b/src/data/sessions/e844d2ae-9098-4528-9e05-e77904a35be3.json
deleted file mode 100644
index ef88a99..0000000
--- a/src/data/sessions/e844d2ae-9098-4528-9e05-e77904a35be3.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "e844d2ae-9098-4528-9e05-e77904a35be3",
- "token": "GKPSGCBS",
- "type": "pty",
- "created": "2025-07-27T14:13:38.965Z",
- "expires": "2025-07-28T14:13:38.965Z",
- "createdAt": 1753625618,
- "expiresAt": 1753712018,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/f0d0635b-59f2-45eb-acfc-d649b12fd2d6.json b/src/data/sessions/f0d0635b-59f2-45eb-acfc-d649b12fd2d6.json
deleted file mode 100644
index 5c62133..0000000
--- a/src/data/sessions/f0d0635b-59f2-45eb-acfc-d649b12fd2d6.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "f0d0635b-59f2-45eb-acfc-d649b12fd2d6",
- "token": "JQAOXCYJ",
- "type": "pty",
- "created": "2025-07-27T14:26:30.022Z",
- "expires": "2025-07-28T14:26:30.022Z",
- "createdAt": 1753626390,
- "expiresAt": 1753712790,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/sessions/f8f915ee-ab64-471c-b3d2-71cb84d2b5fe.json b/src/data/sessions/f8f915ee-ab64-471c-b3d2-71cb84d2b5fe.json
deleted file mode 100644
index 1f6ce6b..0000000
--- a/src/data/sessions/f8f915ee-ab64-471c-b3d2-71cb84d2b5fe.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "id": "f8f915ee-ab64-471c-b3d2-71cb84d2b5fe",
- "token": "5NXM173C",
- "type": "pty",
- "created": "2025-07-27T14:33:54.331Z",
- "expires": "2025-07-28T14:33:54.331Z",
- "createdAt": 1753626834,
- "expiresAt": 1753713234,
- "cwd": "/Users/jessytsui/dev/Claude-Code-Remote",
- "notification": {
- "type": "completed",
- "project": "Claude-Code-Remote",
- "message": "[Claude-Code-Remote] Task completed, Claude is waiting for next instruction"
- },
- "status": "waiting",
- "commandCount": 0,
- "maxCommands": 10
-}
\ No newline at end of file
diff --git a/src/data/subagent-activities.json b/src/data/subagent-activities.json
deleted file mode 100644
index 9e26dfe..0000000
--- a/src/data/subagent-activities.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
\ No newline at end of file
diff --git a/src/utils/tmux-monitor.js b/src/utils/tmux-monitor.js
index 73ba423..af7b636 100644
--- a/src/utils/tmux-monitor.js
+++ b/src/utils/tmux-monitor.js
@@ -6,11 +6,13 @@
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
+const TraceCapture = require('./trace-capture');
class TmuxMonitor {
constructor() {
this.captureDir = path.join(__dirname, '../data/tmux-captures');
this._ensureCaptureDir();
+ this.traceCapture = new TraceCapture();
}
_ensureCaptureDir() {
@@ -75,7 +77,7 @@ class TmuxMonitor {
const allLines = content.split('\n');
const recentLines = allLines.slice(-lines);
- return this.extractConversation(recentLines.join('\n'));
+ return this.extractConversation(recentLines.join('\n'), sessionName);
} catch (error) {
console.error(`Failed to get conversation for session ${sessionName}:`, error.message);
return { userQuestion: '', claudeResponse: '' };
@@ -95,19 +97,172 @@ class TmuxMonitor {
stdio: ['ignore', 'pipe', 'ignore']
});
- return this.extractConversation(buffer);
+ return this.extractConversation(buffer, sessionName);
} catch (error) {
console.error(`Failed to get tmux buffer for session ${sessionName}:`, error.message);
return { userQuestion: '', claudeResponse: '' };
}
}
+ /**
+ * Get full execution trace from tmux session
+ * @param {string} sessionName - The tmux session name
+ * @param {number} lines - Number of lines to retrieve
+ * @returns {string} - Full execution trace
+ */
+ getFullExecutionTrace(sessionName, lines = 1000) {
+ try {
+ let content;
+ if (!fs.existsSync(path.join(this.captureDir, `${sessionName}.log`))) {
+ // If no capture file, try to get from tmux buffer
+ content = this.getFullTraceFromTmuxBuffer(sessionName, lines);
+ } else {
+ // Read the capture file
+ content = fs.readFileSync(path.join(this.captureDir, `${sessionName}.log`), 'utf8');
+ }
+
+ // Always filter content to only show from last user input
+ content = this._filterByTimestamp(content);
+
+ // Clean up the trace by removing the command prompt box
+ return this._cleanExecutionTrace(content);
+ } catch (error) {
+ console.error(`Failed to get full trace for session ${sessionName}:`, error.message);
+ return '';
+ }
+ }
+
+ /**
+ * Filter content to only include lines after the last user input
+ * @param {string} content - The full content
+ * @param {number} timestamp - Unix timestamp in milliseconds (not used in current implementation)
+ * @returns {string} - Filtered content
+ */
+ _filterByTimestamp(content, timestamp) {
+ const lines = content.split('\n');
+ let lastUserInputIndex = -1;
+
+ // Find the LAST occurrence of user input (line starting with "> ")
+ for (let i = lines.length - 1; i >= 0; i--) {
+ const line = lines[i];
+ // Check for user input pattern: "> " at the start of the line
+ if (line.startsWith('> ') && line.length > 2) {
+ lastUserInputIndex = i;
+ break;
+ }
+ }
+
+ // If we found user input, return everything from that point
+ if (lastUserInputIndex >= 0) {
+ return lines.slice(lastUserInputIndex).join('\n');
+ }
+
+ // If no user input found, return last 100 lines as fallback
+ return lines.slice(-100).join('\n');
+ }
+
+ /**
+ * Clean execution trace by removing command prompt and status line
+ * Also removes the complete user input and final Claude response
+ * @param {string} trace - Raw execution trace
+ * @returns {string} - Cleaned trace
+ */
+ _cleanExecutionTrace(trace) {
+ const lines = trace.split('\n');
+ const cleanedLines = [];
+ let inUserInput = false;
+ let skipNextEmptyLine = false;
+ let lastClaudeResponseStart = -1;
+
+ // Find where the last Claude response starts
+ for (let i = lines.length - 1; i >= 0; i--) {
+ if (lines[i].startsWith('⏺ ')) {
+ lastClaudeResponseStart = i;
+ break;
+ }
+ }
+
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+
+ // Skip everything from the last Claude response onward
+ if (lastClaudeResponseStart !== -1 && i >= lastClaudeResponseStart) {
+ // But we want to show everything BEFORE the last response
+ break;
+ }
+
+ // Start of user input
+ if (line.startsWith('> ')) {
+ inUserInput = true;
+ skipNextEmptyLine = true;
+ continue;
+ }
+
+ // Still in user input (continuation lines)
+ if (inUserInput) {
+ // Check if we've reached the end of user input
+ if (line.trim() === '' || line.startsWith('⏺')) {
+ inUserInput = false;
+ if (skipNextEmptyLine && line.trim() === '') {
+ skipNextEmptyLine = false;
+ continue;
+ }
+ } else {
+ continue; // Skip user input continuation lines
+ }
+ }
+
+ // Check if we've hit the command prompt box
+ if (line.includes('╭─') && line.includes('─╮')) {
+ break;
+ }
+
+ // Skip empty command prompt lines
+ if (line.match(/^│\s*>\s*│$/)) {
+ break;
+ }
+
+ cleanedLines.push(line);
+ }
+
+ // Remove empty lines at the beginning and end
+ while (cleanedLines.length > 0 && cleanedLines[0].trim() === '') {
+ cleanedLines.shift();
+ }
+ while (cleanedLines.length > 0 && cleanedLines[cleanedLines.length - 1].trim() === '') {
+ cleanedLines.pop();
+ }
+
+ return cleanedLines.join('\n');
+ }
+
+ /**
+ * Get full trace from tmux buffer
+ * @param {string} sessionName - The tmux session name
+ * @param {number} lines - Number of lines to retrieve
+ */
+ getFullTraceFromTmuxBuffer(sessionName, lines = 1000) {
+ try {
+ // Capture the pane contents
+ const buffer = execSync(`tmux capture-pane -t ${sessionName} -p -S -${lines}`, {
+ encoding: 'utf8',
+ stdio: ['ignore', 'pipe', 'ignore']
+ });
+
+ return buffer;
+ } catch (error) {
+ console.error(`Failed to get tmux buffer for session ${sessionName}:`, error.message);
+ return '';
+ }
+ }
+
/**
* Extract user question and Claude response from captured text
* @param {string} text - The captured text
+ * @param {string} sessionName - The tmux session name (optional)
* @returns {Object} - { userQuestion, claudeResponse }
*/
- extractConversation(text) {
+ extractConversation(text, sessionName = null) {
const lines = text.split('\n');
let userQuestion = '';
@@ -116,17 +271,39 @@ class TmuxMonitor {
let inResponse = false;
// Find the most recent user question and Claude response
+ let inUserInput = false;
+ let userQuestionLines = [];
+
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
// Detect user input (line starting with "> " followed by content)
if (line.startsWith('> ') && line.length > 2) {
- userQuestion = line.substring(2).trim();
+ userQuestionLines = [line.substring(2).trim()];
+ inUserInput = true;
inResponse = false; // Reset response capture
responseLines = []; // Clear previous response
+
+ // Record user input timestamp if session name provided
+ if (sessionName) {
+ this.traceCapture.recordUserInput(sessionName);
+ }
+
continue;
}
+ // Continue capturing multi-line user input
+ if (inUserInput && !line.startsWith('⏺') && line.length > 0) {
+ userQuestionLines.push(line);
+ continue;
+ }
+
+ // End of user input
+ if (inUserInput && (line.startsWith('⏺') || line.length === 0)) {
+ inUserInput = false;
+ userQuestion = userQuestionLines.join(' ');
+ }
+
// Detect Claude response (line starting with "⏺ " or other response indicators)
if (line.startsWith('⏺ ') ||
(inResponse && line.length > 0 &&
diff --git a/src/utils/trace-capture.js b/src/utils/trace-capture.js
new file mode 100644
index 0000000..08def2f
--- /dev/null
+++ b/src/utils/trace-capture.js
@@ -0,0 +1,115 @@
+/**
+ * Trace Capture Utility
+ * Tracks user input timestamps for smart execution trace capture
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+class TraceCapture {
+ constructor() {
+ this.dataDir = path.join(__dirname, '../data');
+ this.timestampFile = path.join(this.dataDir, 'user-input-timestamps.json');
+ this._ensureDataDir();
+ }
+
+ _ensureDataDir() {
+ if (!fs.existsSync(this.dataDir)) {
+ fs.mkdirSync(this.dataDir, { recursive: true });
+ }
+ }
+
+ /**
+ * Load timestamp data
+ */
+ _loadTimestamps() {
+ try {
+ if (fs.existsSync(this.timestampFile)) {
+ const data = fs.readFileSync(this.timestampFile, 'utf8');
+ return JSON.parse(data);
+ }
+ } catch (error) {
+ console.error('Failed to load timestamps:', error.message);
+ }
+ return {};
+ }
+
+ /**
+ * Save timestamp data
+ */
+ _saveTimestamps(data) {
+ try {
+ fs.writeFileSync(this.timestampFile, JSON.stringify(data, null, 2));
+ } catch (error) {
+ console.error('Failed to save timestamps:', error.message);
+ }
+ }
+
+ /**
+ * Record user input timestamp for a session
+ * @param {string} sessionName - The tmux session name
+ * @param {number} timestamp - Unix timestamp in milliseconds
+ */
+ recordUserInput(sessionName, timestamp = Date.now()) {
+ const timestamps = this._loadTimestamps();
+
+ if (!timestamps[sessionName]) {
+ timestamps[sessionName] = {
+ inputs: []
+ };
+ }
+
+ timestamps[sessionName].inputs.push({
+ timestamp: timestamp,
+ date: new Date(timestamp).toISOString()
+ });
+
+ // Keep only last 10 inputs per session to avoid growing too large
+ if (timestamps[sessionName].inputs.length > 10) {
+ timestamps[sessionName].inputs = timestamps[sessionName].inputs.slice(-10);
+ }
+
+ this._saveTimestamps(timestamps);
+ }
+
+ /**
+ * Get the most recent user input timestamp for a session
+ * @param {string} sessionName - The tmux session name
+ * @returns {number|null} - Unix timestamp or null if not found
+ */
+ getLastUserInputTime(sessionName) {
+ const timestamps = this._loadTimestamps();
+
+ if (timestamps[sessionName] && timestamps[sessionName].inputs.length > 0) {
+ const lastInput = timestamps[sessionName].inputs[timestamps[sessionName].inputs.length - 1];
+ return lastInput.timestamp;
+ }
+
+ return null;
+ }
+
+ /**
+ * Clean up old session data (older than 7 days)
+ */
+ cleanup() {
+ const timestamps = this._loadTimestamps();
+ const now = Date.now();
+ const sevenDaysAgo = now - (7 * 24 * 60 * 60 * 1000);
+
+ for (const sessionName in timestamps) {
+ const session = timestamps[sessionName];
+
+ // Remove old inputs
+ session.inputs = session.inputs.filter(input => input.timestamp > sevenDaysAgo);
+
+ // Remove session if no inputs remain
+ if (session.inputs.length === 0) {
+ delete timestamps[sessionName];
+ }
+ }
+
+ this._saveTimestamps(timestamps);
+ }
+}
+
+module.exports = TraceCapture;
\ No newline at end of file