chore: remove non-functional subagent input feature

Subagents (Task tool background agents) are autonomous and cannot
receive input mid-execution. They complete and return results to
the parent session. Removed the input UI and API endpoint.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-26 04:35:50 +01:00
co-authored by Claude Opus 4.5
parent 6187444118
commit 3f73a5f908
12 changed files with 1302 additions and 48 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ When user says "COM": 1) Increment version in BOTH `package.json` AND `CLAUDE.md
Claudeman is a Claude Code session manager with a web interface and autonomous Ralph Loop. It spawns Claude CLI processes via PTY, streams output in real-time via SSE, and supports scheduled/timed runs.
**Version**: 0.1376 (must match `package.json`)
**Version**: 0.1377 (must match `package.json`)
**Tech Stack**: TypeScript (ES2022/NodeNext, strict mode), Node.js, Fastify, Server-Sent Events, node-pty
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claudeman",
"version": "0.1376",
"version": "0.1377",
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
"type": "module",
"main": "dist/index.js",
+597
View File
@@ -181,3 +181,600 @@ curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8
Or via web UI: Open session tab -> Respawn panel -> Enable
### Fix for Issue #11
**Enable respawn controller**
Via API:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/enable'
```
Or via web UI: Open session tab -> Respawn panel -> Enable
### Fix for Issue #12
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #13
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #14
**Watching state stuck**
The controller is watching but not detecting idle. Possible causes:
1. Session is actively working (expected behavior)
2. Idle detection thresholds too high
Check session activity:
```bash
curl -sk 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98' | jq '{status, lastActivityAt}'
```
If truly idle, try adjusting config:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/config' -H 'Content-Type: application/json' -d '{"idleThresholdMs": 30000}'
```
### Fix for Issue #15
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #16
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #17
**Watching state stuck**
The controller is watching but not detecting idle. Possible causes:
1. Session is actively working (expected behavior)
2. Idle detection thresholds too high
Check session activity:
```bash
curl -sk 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98' | jq '{status, lastActivityAt}'
```
If truly idle, try adjusting config:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/config' -H 'Content-Type: application/json' -d '{"idleThresholdMs": 30000}'
```
### Fix for Issue #18
**Send/wait state stuck**
Stuck in waiting_update - the controller is trying to send a command but may not be receiving expected response.
Check terminal output:
```bash
curl -sk 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/terminal?tail=10000' | tail -100
```
Try restarting respawn:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/stop'
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/start'
```
### Fix for Issue #19
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #20
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #21
**Check server status**
1. Check if claudeman-web is running: `systemctl --user status claudeman-web`
2. If down, restart: `systemctl --user restart claudeman-web`
3. Check server logs: `journalctl --user -u claudeman-web -n 50`
### Fix for Issue #22
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #23
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #24
**Watching state stuck**
The controller is watching but not detecting idle. Possible causes:
1. Session is actively working (expected behavior)
2. Idle detection thresholds too high
Check session activity:
```bash
curl -sk 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98' | jq '{status, lastActivityAt}'
```
If truly idle, try adjusting config:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/config' -H 'Content-Type: application/json' -d '{"idleThresholdMs": 30000}'
```
### Fix for Issue #25
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #26
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #27
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #28
**Watching state stuck**
The controller is watching but not detecting idle. Possible causes:
1. Session is actively working (expected behavior)
2. Idle detection thresholds too high
Check session activity:
```bash
curl -sk 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98' | jq '{status, lastActivityAt}'
```
If truly idle, try adjusting config:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/config' -H 'Content-Type: application/json' -d '{"idleThresholdMs": 30000}'
```
### Fix for Issue #29
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #30
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #31
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #32
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #33
**Watching state stuck**
The controller is watching but not detecting idle. Possible causes:
1. Session is actively working (expected behavior)
2. Idle detection thresholds too high
Check session activity:
```bash
curl -sk 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98' | jq '{status, lastActivityAt}'
```
If truly idle, try adjusting config:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/config' -H 'Content-Type: application/json' -d '{"idleThresholdMs": 30000}'
```
### Fix for Issue #34
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #35
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #36
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #37
**Watching state stuck**
The controller is watching but not detecting idle. Possible causes:
1. Session is actively working (expected behavior)
2. Idle detection thresholds too high
Check session activity:
```bash
curl -sk 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98' | jq '{status, lastActivityAt}'
```
If truly idle, try adjusting config:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/config' -H 'Content-Type: application/json' -d '{"idleThresholdMs": 30000}'
```
### Fix for Issue #38
**Check for hung session**
The session may be hung. Check:
1. Session process status:
```bash
screen -ls | grep reddit
```
2. Attach to screen and check:
```bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
```
3. If hung, restart the session via web UI or:
```bash
curl -sk -X POST 'https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/stop'
# Then start a new session
```
### Fix for Issue #39
**Check server status**
1. Check if claudeman-web is running: `systemctl --user status claudeman-web`
2. If down, restart: `systemctl --user restart claudeman-web`
3. Check server logs: `journalctl --user -u claudeman-web -n 50`
### Fix for Issue #40
**Check server status**
1. Check if claudeman-web is running: `systemctl --user status claudeman-web`
2. If down, restart: `systemctl --user restart claudeman-web`
3. Check server logs: `journalctl --user -u claudeman-web -n 50`
+232
View File
@@ -79,3 +79,235 @@ Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptRecei
Respawn is currently disabled for this session. State: false
### Issue #11 [INFO] - 2026-01-26 00:56:53
**Respawn controller not enabled**
Respawn is currently disabled for this session. State: false
### Issue #12 [WARNING] - 2026-01-26 01:01:59
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #13 [WARNING] - 2026-01-26 01:07:06
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #14 [WARNING] - 2026-01-26 01:08:38
**State stuck: watching for 10+ minutes**
The respawn controller has been in 'watching' state for over 10 minutes.
Time since activity: 79ms
Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptReceived":false,"idlePromptTime":null,"completionMessageDetected":false,"completionMessageTime":null,"outputSilent":false,"msSinceLastOutput":79,"tokensStable":true,"lastTokenCount":6800,"msSinceTokenChange":65527,"workingPatternsAbsent":true,"msSinceLastWorking":69681,"aiCheck":{"status":"ready","lastVerdict":null,"lastReasoning":null,"lastCheckDurationMs":null,"cooldownEndsAt":null,"consecutiveErrors":0,"totalChecks":0,"disabledReason":null},"confidenceLevel":35,"statusText":"Watching for idle signals","waitingFor":"Silence + no working patterns + tokens stable","activeTimers":[{"name":"no-output-fallback","remainingMs":29921,"totalMs":30000},{"name":"pre-filter","remainingMs":9921,"totalMs":10000},{"name":"auto-accept","remainingMs":7921,"totalMs":8000}],"recentActions":[{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769386118502},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769386118502},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769386118502},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769386118502},{"type":"timer","detail":"Started no-output-fallback: 30s (fallback if no output at all)","timestamp":1769386118502},{"type":"timer-cancel","detail":"no-output-fallback: restarting","timestamp":1769386118502},{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769386118159},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769386118159},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769386118159},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769386118159}],"currentPhase":"Monitoring for idle","nextAction":"Waiting for silence + no working patterns"}
### Issue #15 [WARNING] - 2026-01-26 01:12:13
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #16 [WARNING] - 2026-01-26 01:17:19
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #17 [WARNING] - 2026-01-26 01:18:52
**State stuck: watching for 10+ minutes**
The respawn controller has been in 'watching' state for over 10 minutes.
Time since activity: 62ms
Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptReceived":false,"idlePromptTime":null,"completionMessageDetected":false,"completionMessageTime":null,"outputSilent":false,"msSinceLastOutput":62,"tokensStable":true,"lastTokenCount":6800,"msSinceTokenChange":28940,"workingPatternsAbsent":true,"msSinceLastWorking":37043,"aiCheck":{"status":"ready","lastVerdict":null,"lastReasoning":null,"lastCheckDurationMs":null,"cooldownEndsAt":null,"consecutiveErrors":0,"totalChecks":0,"disabledReason":null},"confidenceLevel":35,"statusText":"Watching for idle signals","waitingFor":"Silence + no working patterns + tokens stable","activeTimers":[{"name":"no-output-fallback","remainingMs":29938,"totalMs":30000},{"name":"pre-filter","remainingMs":9938,"totalMs":10000},{"name":"auto-accept","remainingMs":7938,"totalMs":8000}],"recentActions":[{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769386731865},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769386731865},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769386731865},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769386731865},{"type":"timer","detail":"Started no-output-fallback: 30s (fallback if no output at all)","timestamp":1769386731865},{"type":"timer-cancel","detail":"no-output-fallback: restarting","timestamp":1769386731865},{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769386731693},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769386731693},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769386731693},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769386731693}],"currentPhase":"Monitoring for idle","nextAction":"Waiting for silence + no working patterns"}
### Issue #18 [WARNING] - 2026-01-26 02:04:01
**State stuck: waiting_update for 10+ minutes**
The respawn controller has been in 'waiting_update' state for over 10 minutes.
Time since activity: 94ms
Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptReceived":false,"idlePromptTime":null,"completionMessageDetected":false,"completionMessageTime":null,"outputSilent":false,"msSinceLastOutput":94,"tokensStable":true,"lastTokenCount":7200,"msSinceTokenChange":98386,"workingPatternsAbsent":true,"msSinceLastWorking":672047,"aiCheck":{"status":"ready","lastVerdict":"IDLE","lastReasoning":"The terminal output is completely empty, which indicates no active processing is occurring. An empty buffer with no spinner characters, no activity text, and no partial output suggests the session has finished any previous work and is waiting for new input.","lastCheckDurationMs":26087,"cooldownEndsAt":null,"consecutiveErrors":0,"totalChecks":1,"disabledReason":null},"confidenceLevel":35,"statusText":"Respawn step: waiting_update","waitingFor":"Step completion","activeTimers":[{"name":"step-confirm","remainingMs":6552,"totalMs":10000},{"name":"no-output-fallback","remainingMs":29906,"totalMs":30000},{"name":"pre-filter","remainingMs":9906,"totalMs":10000},{"name":"auto-accept","remainingMs":7906,"totalMs":8000}],"recentActions":[{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769389441228},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769389441228},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769389441228},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769389441228},{"type":"timer","detail":"Started no-output-fallback: 30s (fallback if no output at all)","timestamp":1769389441228},{"type":"timer-cancel","detail":"no-output-fallback: restarting","timestamp":1769389441228},{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769389441125},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769389441125},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769389441125},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769389441125}],"currentPhase":"Waiting for update to complete","nextAction":"Will send /clear when done"}
### Issue #19 [WARNING] - 2026-01-26 02:11:41
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #20 [WARNING] - 2026-01-26 02:51:26
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #21 [CRITICAL] - 2026-01-26 02:55:59
**API request failed or session not found**
Could not fetch session data. Server may be down or session deleted.
### Issue #22 [WARNING] - 2026-01-26 02:59:08
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #23 [WARNING] - 2026-01-26 03:04:16
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #24 [WARNING] - 2026-01-26 03:07:51
**State stuck: watching for 10+ minutes**
The respawn controller has been in 'watching' state for over 10 minutes.
Time since activity: 25ms
Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptReceived":false,"idlePromptTime":null,"completionMessageDetected":false,"completionMessageTime":null,"outputSilent":false,"msSinceLastOutput":25,"tokensStable":true,"lastTokenCount":7700,"msSinceTokenChange":65906,"workingPatternsAbsent":true,"msSinceLastWorking":70325,"aiCheck":{"status":"ready","lastVerdict":null,"lastReasoning":null,"lastCheckDurationMs":null,"cooldownEndsAt":null,"consecutiveErrors":0,"totalChecks":0,"disabledReason":null},"confidenceLevel":35,"statusText":"Watching for idle signals","waitingFor":"Silence + no working patterns + tokens stable","activeTimers":[{"name":"no-output-fallback","remainingMs":29975,"totalMs":30000},{"name":"pre-filter","remainingMs":9975,"totalMs":10000},{"name":"auto-accept","remainingMs":7975,"totalMs":8000}],"recentActions":[{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769393271145},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769393271145},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769393271145},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769393271145},{"type":"timer","detail":"Started no-output-fallback: 30s (fallback if no output at all)","timestamp":1769393271145},{"type":"timer-cancel","detail":"no-output-fallback: restarting","timestamp":1769393271145},{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769393271013},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769393271013},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769393271013},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769393271013}],"currentPhase":"Monitoring for idle","nextAction":"Waiting for silence + no working patterns"}
### Issue #25 [WARNING] - 2026-01-26 03:09:23
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #26 [WARNING] - 2026-01-26 03:17:50
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #27 [WARNING] - 2026-01-26 03:22:56
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #28 [WARNING] - 2026-01-26 03:27:34
**State stuck: watching for 10+ minutes**
The respawn controller has been in 'watching' state for over 10 minutes.
Time since activity: 137ms
Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptReceived":false,"idlePromptTime":null,"completionMessageDetected":false,"completionMessageTime":null,"outputSilent":false,"msSinceLastOutput":137,"tokensStable":true,"lastTokenCount":7700,"msSinceTokenChange":21484,"workingPatternsAbsent":true,"msSinceLastWorking":20053,"aiCheck":{"status":"ready","lastVerdict":null,"lastReasoning":null,"lastCheckDurationMs":null,"cooldownEndsAt":null,"consecutiveErrors":0,"totalChecks":0,"disabledReason":null},"confidenceLevel":35,"statusText":"Watching for idle signals","waitingFor":"Silence + no working patterns + tokens stable","activeTimers":[{"name":"no-output-fallback","remainingMs":29863,"totalMs":30000},{"name":"pre-filter","remainingMs":9863,"totalMs":10000},{"name":"auto-accept","remainingMs":7863,"totalMs":8000}],"recentActions":[{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769394453842},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769394453842},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769394453842},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769394453842},{"type":"timer","detail":"Started no-output-fallback: 30s (fallback if no output at all)","timestamp":1769394453842},{"type":"timer-cancel","detail":"no-output-fallback: restarting","timestamp":1769394453842},{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769394453666},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769394453666},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769394453666},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769394453666}],"currentPhase":"Monitoring for idle","nextAction":"Waiting for silence + no working patterns"}
### Issue #29 [WARNING] - 2026-01-26 03:28:04
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #30 [WARNING] - 2026-01-26 03:33:11
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #31 [WARNING] - 2026-01-26 03:40:25
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #32 [WARNING] - 2026-01-26 03:45:32
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #33 [WARNING] - 2026-01-26 03:47:35
**State stuck: watching for 10+ minutes**
The respawn controller has been in 'watching' state for over 10 minutes.
Time since activity: 177ms
Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptReceived":false,"idlePromptTime":null,"completionMessageDetected":false,"completionMessageTime":null,"outputSilent":false,"msSinceLastOutput":177,"tokensStable":true,"lastTokenCount":7800,"msSinceTokenChange":62625,"workingPatternsAbsent":true,"msSinceLastWorking":43015,"aiCheck":{"status":"ready","lastVerdict":null,"lastReasoning":null,"lastCheckDurationMs":null,"cooldownEndsAt":null,"consecutiveErrors":0,"totalChecks":0,"disabledReason":null},"confidenceLevel":35,"statusText":"Watching for idle signals","waitingFor":"Silence + no working patterns + tokens stable","activeTimers":[{"name":"no-output-fallback","remainingMs":29823,"totalMs":30000},{"name":"pre-filter","remainingMs":9823,"totalMs":10000},{"name":"auto-accept","remainingMs":7823,"totalMs":8000}],"recentActions":[{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769395655315},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769395655315},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769395655315},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769395655315},{"type":"timer","detail":"Started no-output-fallback: 30s (fallback if no output at all)","timestamp":1769395655315},{"type":"timer-cancel","detail":"no-output-fallback: restarting","timestamp":1769395655315},{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769395655111},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769395655111},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769395655111},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769395655111}],"currentPhase":"Monitoring for idle","nextAction":"Waiting for silence + no working patterns"}
### Issue #34 [WARNING] - 2026-01-26 03:54:03
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #35 [WARNING] - 2026-01-26 04:03:44
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #36 [WARNING] - 2026-01-26 04:08:51
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #37 [WARNING] - 2026-01-26 04:11:55
**State stuck: watching for 10+ minutes**
The respawn controller has been in 'watching' state for over 10 minutes.
Time since activity: 73ms
Detection status: {"stopHookReceived":false,"stopHookTime":null,"idlePromptReceived":false,"idlePromptTime":null,"completionMessageDetected":false,"completionMessageTime":null,"outputSilent":false,"msSinceLastOutput":73,"tokensStable":true,"lastTokenCount":8100,"msSinceTokenChange":271932,"workingPatternsAbsent":true,"msSinceLastWorking":175737,"aiCheck":{"status":"ready","lastVerdict":null,"lastReasoning":null,"lastCheckDurationMs":null,"cooldownEndsAt":null,"consecutiveErrors":0,"totalChecks":0,"disabledReason":null},"confidenceLevel":35,"statusText":"Watching for idle signals","waitingFor":"Silence + no working patterns + tokens stable","activeTimers":[{"name":"no-output-fallback","remainingMs":29927,"totalMs":30000},{"name":"pre-filter","remainingMs":9927,"totalMs":10000},{"name":"auto-accept","remainingMs":7927,"totalMs":8000}],"recentActions":[{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769397114896},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769397114896},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769397114896},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769397114896},{"type":"timer","detail":"Started no-output-fallback: 30s (fallback if no output at all)","timestamp":1769397114896},{"type":"timer-cancel","detail":"no-output-fallback: restarting","timestamp":1769397114896},{"type":"timer","detail":"Started auto-accept: 8s (plan mode detection)","timestamp":1769397114770},{"type":"timer-cancel","detail":"auto-accept: restarting","timestamp":1769397114770},{"type":"timer","detail":"Started pre-filter: 10s (checking idle conditions)","timestamp":1769397114770},{"type":"timer-cancel","detail":"pre-filter: restarting","timestamp":1769397114770}],"currentPhase":"Monitoring for idle","nextAction":"Waiting for silence + no working patterns"}
### Issue #38 [WARNING] - 2026-01-26 04:13:57
**Tokens not increasing while busy**
Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: 101458, Previous: 101458
This could indicate a hung Claude process.
### Issue #39 [CRITICAL] - 2026-01-26 04:14:58
**API request failed or session not found**
Could not fetch session data. Server may be down or session deleted.
### Issue #40 [CRITICAL] - 2026-01-26 04:19:33
**API request failed or session not found**
Could not fetch session data. Server may be down or session deleted.
+24
View File
@@ -35,3 +35,27 @@ Re-enable respawn controller:\n```bash\ncurl -sk -X POST https://localhost:3000/
Re-enable respawn controller:\n```bash\ncurl -sk -X POST https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/enable\n```
---
### Issue: State stuck in waiting_update
**Detected:** 2026-01-26 02:06:38
**Proposed Fix:**
The respawn controller may be stuck. Options:\n1. Check terminal for blocking prompts\n2. Send input to unstick:\n```bash\ncurl -sk -X POST https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/input -d '{"input": "\\r"}'\n```\n3. Restart respawn:\n```bash\ncurl -sk -X POST https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/stop\ncurl -sk -X POST https://localhost:3000/api/sessions/5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98/respawn/start\n```
---
### Issue: API not responding
**Detected:** 2026-01-26 02:49:16
**Proposed Fix:**
Check if claudeman-web service is running:\n```bash\nsystemctl --user status claudeman-web\nsystemctl --user restart claudeman-web\n```
---
### Issue: API not responding
**Detected:** 2026-01-26 04:14:53
**Proposed Fix:**
Check if claudeman-web service is running:\n```bash\nsystemctl --user status claudeman-web\nsystemctl --user restart claudeman-web\n```
---
+100
View File
@@ -24,3 +24,103 @@ Respawn appears to have been disabled (was in state: watching)
### [WARNING] 2026-01-26 00:41:19
Respawn appears to have been disabled (was in state: waiting_update)
### [WARNING] 2026-01-26 02:06:38
State stuck in 'waiting_update' for 30 minutes. Detection: confidence=35, timeSinceActivity=174ms
### [ERROR] 2026-01-26 02:49:16
API not responding - server may be down
### [WARNING] 2026-01-26 02:50:17
No activity for 1769392217009ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 02:53:21
No activity for 1769392401003ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 02:54:21
No activity for 1769392461155ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 02:56:35
No activity for 1769392595274ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:13:52
No activity for 1769393632129ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:14:52
No activity for 1769393692319ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:15:53
No activity for 1769393752923ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:16:54
No activity for 1769393813234ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:35:16
No activity for 1769394915929ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:36:16
No activity for 1769394976058ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:51:31
No activity for 1769395891117ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:52:32
No activity for 1769395952525ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:53:33
No activity for 1769396012695ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:57:39
No activity for 1769396259699ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 03:58:39
No activity for 1769396319835ms while in state 'stopped' - may be stuck
### [ERROR] 2026-01-26 04:14:53
API not responding - server may be down
### [WARNING] 2026-01-26 04:15:54
No activity for 1769397353803ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 04:16:54
No activity for 1769397414083ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 04:18:58
No activity for 1769397538370ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 04:20:16
No activity for 1769397615608ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 04:21:16
No activity for 1769397676098ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 04:24:24
No activity for 1769397863919ms while in state 'stopped' - may be stuck
### [WARNING] 2026-01-26 04:25:24
No activity for 1769397924056ms while in state 'stopped' - may be stuck
+251 -42
View File
@@ -115,6 +115,8 @@ export interface BashToolParserConfig {
sessionId: string;
/** Whether the parser is enabled (default: true) */
enabled?: boolean;
/** Working directory for resolving relative paths */
workingDir?: string;
}
// ========== BashToolParser Class ==========
@@ -140,6 +142,11 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
private _activeTools: Map<string, ActiveBashTool> = new Map();
private _lineBuffer: string = '';
private _lastToolId: string | null = null;
private _workingDir: string;
private _homeDir: string;
// Track normalized paths to detect equivalents (normalized -> original)
private _normalizedPathMap: Map<string, string> = new Map();
// Debouncing
private _pendingUpdate: boolean = false;
@@ -149,6 +156,8 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
super();
this._sessionId = config.sessionId;
this._enabled = config.enabled ?? true;
this._workingDir = config.workingDir || process.cwd();
this._homeDir = process.env.HOME || '/home/user';
}
// ========== Public Accessors ==========
@@ -168,8 +177,205 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
return Array.from(this._activeTools.values());
}
/** Current working directory used for path resolution */
get workingDir(): string {
return this._workingDir;
}
// ========== Path Normalization ==========
/**
* Normalize a file path to its canonical form.
* - Expands ~ to home directory
* - Resolves relative paths against working directory
* - Normalizes . and .. components
* - Removes trailing slashes
*/
normalizePath(path: string): string {
if (!path) return '';
let normalized = path.trim();
// Expand ~ to home directory
if (normalized.startsWith('~/')) {
normalized = this._homeDir + normalized.slice(1);
} else if (normalized === '~') {
normalized = this._homeDir;
}
// If not absolute, resolve against working directory
if (!normalized.startsWith('/')) {
normalized = this._workingDir + '/' + normalized;
}
// Normalize path components (resolve . and ..)
const parts = normalized.split('/');
const stack: string[] = [];
for (const part of parts) {
if (part === '' || part === '.') {
continue;
} else if (part === '..') {
if (stack.length > 1) {
stack.pop();
}
} else {
stack.push(part);
}
}
return '/' + stack.join('/');
}
/**
* Extract just the filename from a path.
*/
private getFilename(path: string): string {
const parts = path.split('/');
return parts[parts.length - 1] || '';
}
/**
* Check if a path is a "shallow root path" - an absolute path with only one
* component after root (e.g., /test.txt, /file.log).
* These are often typos where the user meant a relative path.
*/
private isShallowRootPath(path: string): boolean {
if (!path.startsWith('/')) return false;
const parts = path.split('/').filter(p => p !== '');
return parts.length === 1;
}
/**
* Check if a path is inside (or is) the working directory.
*/
isPathInWorkingDir(path: string): boolean {
const normalized = this.normalizePath(path);
return normalized.startsWith(this._workingDir + '/') || normalized === this._workingDir;
}
/**
* Smart path equivalence check.
* Two paths are considered equivalent if:
* 1. They normalize to the same path (standard case)
* 2. One is a "shallow root path" (e.g., /test.txt) and the other is the
* same filename inside the working directory - the shallow root path
* is likely a typo and they probably meant the same file.
*/
pathsAreEquivalent(path1: string, path2: string): boolean {
const norm1 = this.normalizePath(path1);
const norm2 = this.normalizePath(path2);
// Standard check: exact normalized match
if (norm1 === norm2) return true;
// Smart check: shallow root path vs working dir path with same filename
const file1 = this.getFilename(norm1);
const file2 = this.getFilename(norm2);
if (file1 !== file2) return false; // Different filenames, can't be equivalent
const shallow1 = this.isShallowRootPath(path1);
const shallow2 = this.isShallowRootPath(path2);
const inWorkDir1 = this.isPathInWorkingDir(norm1);
const inWorkDir2 = this.isPathInWorkingDir(norm2);
// If one is shallow root (e.g., /test.txt) and other is in working dir
// with same filename, treat as equivalent (user likely made a typo)
if (shallow1 && inWorkDir2) return true;
if (shallow2 && inWorkDir1) return true;
return false;
}
/**
* Given multiple paths, deduplicate and return the "best" paths.
* Uses smart equivalence checking:
* - Standard normalization for relative vs absolute paths
* - Detects likely typos (e.g., /file.txt when workingDir/file.txt exists)
* - Prefers paths inside the working directory
* - Prefers longer, more explicit paths
*/
deduplicatePaths(paths: string[]): string[] {
if (paths.length <= 1) return paths;
const result: string[] = [];
const seenNormalized = new Set<string>();
// Sort paths: prefer paths in working dir first, then by length (longer first)
const sortedPaths = [...paths].sort((a, b) => {
const aInWorkDir = this.isPathInWorkingDir(a);
const bInWorkDir = this.isPathInWorkingDir(b);
if (aInWorkDir && !bInWorkDir) return -1;
if (bInWorkDir && !aInWorkDir) return 1;
return b.length - a.length; // Longer paths first
});
for (const path of sortedPaths) {
const normalized = this.normalizePath(path);
// Check if we've already seen an equivalent path
let isDuplicate = false;
for (const existing of result) {
if (this.pathsAreEquivalent(path, existing)) {
isDuplicate = true;
break;
}
}
if (!isDuplicate && !seenNormalized.has(normalized)) {
result.push(path);
seenNormalized.add(normalized);
}
}
return result;
}
// ========== Public Methods ==========
/**
* Update the working directory (e.g., when session changes directory).
*/
setWorkingDir(workingDir: string): void {
this._workingDir = workingDir;
}
/**
* Check if a file path is already being tracked by an active tool.
* Uses path normalization to detect equivalent paths.
* For example, "/test.txt" and "/home/user/test.txt" when workingDir
* is "/home/user" would NOT be considered equivalent (different files),
* but "test.txt" and "/home/user/test.txt" WOULD be (same file).
*/
isFilePathTracked(filePath: string): boolean {
const normalizedNew = this.normalizePath(filePath);
return Array.from(this._activeTools.values()).some(t => {
if (t.status !== 'running') return false;
return t.filePaths.some(existingPath => {
const normalizedExisting = this.normalizePath(existingPath);
return normalizedExisting === normalizedNew;
});
});
}
/**
* Get all tracked paths (normalized) for debugging.
*/
getTrackedPaths(): { raw: string; normalized: string }[] {
const paths: { raw: string; normalized: string }[] = [];
for (const tool of this._activeTools.values()) {
if (tool.status === 'running') {
for (const path of tool.filePaths) {
paths.push({ raw: path, normalized: this.normalizePath(path) });
}
}
}
return paths;
}
/**
* Enables the parser.
*/
@@ -189,6 +395,7 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
*/
reset(): void {
this._activeTools.clear();
this._normalizedPathMap.clear();
this._lineBuffer = '';
this._lastToolId = null;
this.emitUpdate();
@@ -244,6 +451,11 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
if (this.isFileViewerCommand(command)) {
const filePaths = this.extractFilePaths(command);
// Skip if any file path is already tracked (cross-pattern dedup)
if (filePaths.some(fp => this.isFilePathTracked(fp))) {
return;
}
if (filePaths.length > 0) {
const tool: ActiveBashTool = {
id: uuidv4(),
@@ -309,22 +521,20 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
sessionId: this._sessionId,
};
// Don't add duplicates (same file path within last 5 seconds)
const isDuplicate = Array.from(this._activeTools.values()).some(
t => t.filePaths.includes(filePath) && (Date.now() - t.startedAt) < 5000
);
if (!isDuplicate) {
this._activeTools.set(tool.id, tool);
this.emit('toolStart', tool);
this.scheduleUpdate();
// Auto-remove suggestions after 30 seconds
setTimeout(() => {
this._activeTools.delete(tool.id);
this.scheduleUpdate();
}, 30000);
// Don't add if file path already tracked (cross-pattern dedup)
if (this.isFilePathTracked(filePath)) {
return;
}
this._activeTools.set(tool.id, tool);
this.emit('toolStart', tool);
this.scheduleUpdate();
// Auto-remove suggestions after 30 seconds
setTimeout(() => {
this._activeTools.delete(tool.id);
this.scheduleUpdate();
}, 30000);
return;
}
@@ -337,32 +547,28 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
// Skip if it looks invalid
if (INVALID_PATH_PATTERN.test(filePath)) continue;
// Don't add duplicates
const isDuplicate = Array.from(this._activeTools.values()).some(
t => t.filePaths.includes(filePath) && (Date.now() - t.startedAt) < 10000
);
// Skip if file path already tracked (cross-pattern dedup)
if (this.isFilePathTracked(filePath)) continue;
if (!isDuplicate) {
const tool: ActiveBashTool = {
id: uuidv4(),
command: `View: ${filePath}`,
filePaths: [filePath],
timeout: undefined,
startedAt: Date.now(),
status: 'running',
sessionId: this._sessionId,
};
const tool: ActiveBashTool = {
id: uuidv4(),
command: `View: ${filePath}`,
filePaths: [filePath],
timeout: undefined,
startedAt: Date.now(),
status: 'running',
sessionId: this._sessionId,
};
this._activeTools.set(tool.id, tool);
this.emit('toolStart', tool);
this._activeTools.set(tool.id, tool);
this.emit('toolStart', tool);
this.scheduleUpdate();
// Auto-remove after 60 seconds
setTimeout(() => {
this._activeTools.delete(tool.id);
this.scheduleUpdate();
// Auto-remove after 60 seconds
setTimeout(() => {
this._activeTools.delete(tool.id);
this.scheduleUpdate();
}, 60000);
}
}, 60000);
}
}
@@ -385,9 +591,10 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
/**
* Extract file paths from a command string.
* Returns deduplicated paths, preferring more complete/absolute versions.
*/
private extractFilePaths(command: string): string[] {
const paths: string[] = [];
const rawPaths: string[] = [];
let match;
// Reset regex state
@@ -409,12 +616,13 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
// Clean up path (remove trailing punctuation)
const cleanPath = path.replace(/[,;:]+$/, '');
if (cleanPath && !paths.includes(cleanPath)) {
paths.push(cleanPath);
if (cleanPath) {
rawPaths.push(cleanPath);
}
}
return paths;
// Deduplicate paths that resolve to the same file
return this.deduplicatePaths(rawPaths);
}
/**
@@ -454,6 +662,7 @@ export class BashToolParser extends EventEmitter<BashToolParserEvents> {
this._updateTimer = null;
}
this._activeTools.clear();
this._normalizedPathMap.clear();
this.removeAllListeners();
}
}
+1 -1
View File
@@ -469,7 +469,7 @@ export class Session extends EventEmitter {
this._ralphTracker.on('completionDetected', this._ralphHandlers.completionDetected);
// Initialize Bash tool parser and forward events (store handlers for cleanup)
this._bashToolParser = new BashToolParser({ sessionId: this.id });
this._bashToolParser = new BashToolParser({ sessionId: this.id, workingDir: this.workingDir });
this._bashToolHandlers = {
toolStart: (tool) => this.emit('bashToolStart', tool),
toolEnd: (tool) => this.emit('bashToolEnd', tool),
+14
View File
@@ -665,8 +665,17 @@ class ClaudemanApp {
registerFilePathLinkProvider() {
const self = this;
// Debug: Track if provider is being invoked
let lastInvokedLine = -1;
this.terminal.registerLinkProvider({
provideLinks(bufferLineNumber, callback) {
// Debug logging - only log if line changed to avoid spam
if (bufferLineNumber !== lastInvokedLine) {
lastInvokedLine = bufferLineNumber;
console.debug('[LinkProvider] Checking line:', bufferLineNumber);
}
const buffer = self.terminal.buffer.active;
const line = buffer.getLine(bufferLineNumber);
@@ -736,9 +745,14 @@ class ClaudemanApp {
addLink(match[1], match.index);
}
if (links.length > 0) {
console.debug('[LinkProvider] Found links:', links.map(l => l.text));
}
callback(links.length > 0 ? links : undefined);
}
});
console.log('[LinkProvider] File path link provider registered');
}
showWelcome() {
+6 -3
View File
@@ -1829,6 +1829,7 @@ export class WebServer extends EventEmitter {
return createErrorResponse(ApiErrorCode.OPERATION_FAILED, 'Subagent not found or already completed');
});
// ========== Hook Events ==========
this.app.post('/api/hook-event', async (req) => {
@@ -3005,6 +3006,11 @@ export class WebServer extends EventEmitter {
async start(): Promise<void> {
await this.setupRoutes();
// Restore screen sessions BEFORE accepting connections
// This prevents race conditions where clients connect before state is ready
await this.restoreScreenSessions();
await this.app.listen({ port: this.port, host: '0.0.0.0' });
const protocol = this.https ? 'https' : 'http';
console.log(`Claudeman web interface running at ${protocol}://localhost:${this.port}`);
@@ -3027,9 +3033,6 @@ export class WebServer extends EventEmitter {
this.recordPeriodicTokenUsage();
}, 5 * 60 * 1000);
// Restore screen sessions from previous run
await this.restoreScreenSessions();
// Start subagent watcher for Claude Code background agent visibility (if enabled)
if (this.isSubagentTrackingEnabled()) {
subagentWatcher.start();
+71
View File
@@ -0,0 +1,71 @@
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
headless: true,
args: ['--ignore-certificate-errors']
});
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
// Capture console messages
const consoleLogs = [];
page.on('console', msg => {
consoleLogs.push('[' + msg.type() + '] ' + msg.text());
});
console.log('Opening https://localhost:3000...');
await page.goto('https://localhost:3000', { waitUntil: 'networkidle' });
await page.waitForTimeout(2000);
console.log('Looking for shell button...');
// Click Run Shell button
const shellBtn = page.locator('button:has-text("Run Shell")');
if (await shellBtn.count() > 0) {
await shellBtn.click();
console.log('Clicked Run Shell');
} else {
console.log('Run Shell button not found');
}
await page.waitForTimeout(3000);
// Type command in terminal
console.log('Typing command...');
await page.keyboard.type('echo "tail -f /tmp/test.log"');
await page.keyboard.press('Enter');
await page.waitForTimeout(2000);
// Take screenshot
await page.screenshot({ path: '/tmp/link-test-1.png', fullPage: true });
console.log('Screenshot 1 saved');
// Move mouse over terminal area (approximate coordinates for /tmp/test.log)
const terminal = page.locator('.xterm-screen');
const box = await terminal.boundingBox();
if (box) {
console.log('Terminal at: ' + box.x + ', ' + box.y + ', ' + box.width + 'x' + box.height);
// Move mouse across the area where the path should be (roughly line 2)
const y = box.y + 50; // Approximate line 2
for (let x = box.x + 50; x < box.x + 300; x += 10) {
await page.mouse.move(x, y);
await page.waitForTimeout(50);
}
}
await page.waitForTimeout(1000);
await page.screenshot({ path: '/tmp/link-test-2.png', fullPage: true });
console.log('Screenshot 2 saved');
// Print console logs
console.log('\n=== Console Logs ===');
consoleLogs.filter(l => l.includes('LinkProvider')).forEach(l => console.log(l));
// Check if "Found links" appeared
const foundLinks = consoleLogs.some(l => l.includes('Found links'));
console.log('\nLinks detected:', foundLinks);
await browser.close();
})();
+4
View File
@@ -0,0 +1,4 @@
{
"status": "failed",
"failedTests": []
}