* add project setup
* add tools to eslint
* remove contributing.md
* update claude md
* chore: simplify CI to single Node.js version (22)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* use node 20
* adjust formatting
* fix linting
* format
* fix layout
* fix: align CI node version to .nvmrc (22), remove redundant gotcha
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: arkon <arkon.85@hotmail.com>
- Add 300-char rolling window to catch working patterns split across PTY chunks
- Check completion message BEFORE working patterns (priority fix)
- Clear rolling window on completion message (transition point)
- Increase working pattern absence threshold from 3s to 8s
- Add Session.isWorking safety check before confirming idle
- Add 20+ more working patterns (Compiling, Building, Processing, etc.)
- Make AI idle checker prompt more conservative (err toward WORKING)
- Update documentation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AiCheckerBase abstract class to eliminate ~700 lines of duplication
between AiIdleChecker and AiPlanChecker
- Fix missing persistence fields (completionConfirmMs, noOutputTimeoutMs)
that caused custom timing values to be lost on server restart
- Extract restoreRespawnController() helper to dedupe session restoration
logic between state.json and screens.json paths
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The ai-plan-checker.ts was passing the prompt directly as a shell argument,
which can cause E2BIG errors when the terminal buffer is large (8KB+).
This fix applies the same temp file approach already used in ai-idle-checker.ts:
- Write prompt to a temp file instead of passing as shell argument
- Pipe the file to claude via stdin: `cat prompt.txt | claude -p ...`
- Clean up prompt file after check completes
Also fixes a race condition in the cancel() method of both AI checkers where
the poll timer could fire between setting checkCancelled and clearing timers.
Now timers are cleared before resolving the promise to prevent this race.
Includes test utilities and analysis documents for the respawn controller
created by other agents:
- test/respawn-test-utils.ts - MockSession, MockAiIdleChecker utilities
- test/respawn-analysis.md - Code analysis and issue identification
- test/respawn-scenarios.md - Test scenario documentation
- test/respawn-test-plan.md - Testing architecture documentation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- persistSessionState() now saves respawnEnabled based on config.enabled
(user intent) instead of controller.state (running state)
- cleanupSession() saves respawn config BEFORE removing controller
- AiIdleChecker and AiPlanChecker constructors filter undefined values
to prevent overwriting defaults (fixes "timed out after undefinedms")
- updateConfig() methods also filter undefined values
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The AI idle checker was passing the prompt (including up to 16KB of terminal
buffer) as a command-line argument to bash -c, which exceeded the Linux
argument size limit causing "spawn E2BIG" errors.
Now writes the prompt to a temp file and pipes it to claude via stdin,
avoiding the argument size limit entirely. Temp file is cleaned up both
in the normal case (by the shell command) and on cancellation/timeout.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two bugs fixed:
1. Respawn controller got stuck after AI check returned WORKING/ERROR.
The pre-filter and no-output timers fired once but were never restarted,
leaving the controller in a dead 'watching' state with no retry mechanism.
Now restarts both timers after any non-IDLE AI check result and on
cooldown expiry. Also seeds the controller's terminal buffer from the
session's existing output so the first AI check has context.
2. AI idle checker couldn't find the claude binary in restricted environments
(systemd service). The spawned screen ran `claude -p` without PATH
augmentation. Now exports getAugmentedPath() from session.ts and prepends
it in the AI checker's bash command.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace the "Worked for Xm Xs" pattern as the sole primary idle
detection signal with a final AI-powered check. When pre-filter
conditions are met (output silence, no working patterns, tokens
stable), a fresh Claude CLI session is spawned in a screen to
analyze terminal output and provide a definitive IDLE/WORKING
verdict before proceeding with the respawn cycle.
New state in state machine: `ai_checking` (between pre-filter
confirmation and `sending_update`). WORKING verdict triggers a
3-minute cooldown. Errors auto-disable after 3 consecutive
failures, falling back to the existing noOutputTimeoutMs safety net.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>