feat: add kickstart prompt and auto-compact context options

Kickstart Prompt:
- Remove Idle Timeout setting from respawn UI (uses 10s default)
- Add Kickstart Prompt field sent when /init doesn't trigger work
- New states: monitoring_init, sending_kickstart, waiting_kickstart
- Monitors 3s after /init to detect if Claude started working

Auto-Compact Context:
- Add auto-compact at 110k tokens (sends /compact with optional prompt)
- Preserves summarized context unlike /clear which wipes everything
- Optional focus prompt to guide what compact should preserve
- Blocks auto-clear while compacting to prevent conflicts

Also updates auto-clear default threshold from 100k to 140k tokens.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-19 13:08:53 +01:00
co-authored by Claude Opus 4.5
parent 0949b52830
commit d711ec8b3a
8 changed files with 255 additions and 29 deletions
+81 -9
View File
@@ -17,17 +17,20 @@ const READY_INDICATOR = '↵ send';
* Respawn sequence states
*
* The controller cycles through these states:
* WATCHING → SENDING_UPDATE → WAITING_UPDATE → SENDING_CLEAR → WAITING_CLEAR → SENDING_INIT → WAITING_INIT → WATCHING
* WATCHING → SENDING_UPDATE → WAITING_UPDATE → SENDING_CLEAR → WAITING_CLEAR → SENDING_INIT → WAITING_INIT → MONITORING_INIT → (maybe SENDING_KICKSTART → WAITING_KICKSTART) → WATCHING
*/
export type RespawnState =
| 'watching' // Watching for idle, ready to start respawn sequence
| 'sending_update' // About to send the update docs prompt
| 'waiting_update' // Waiting for update to complete
| 'sending_clear' // About to send /clear
| 'waiting_clear' // Waiting for clear to complete
| 'sending_init' // About to send /init
| 'waiting_init' // Waiting for init to complete
| 'stopped'; // Controller stopped
| 'watching' // Watching for idle, ready to start respawn sequence
| 'sending_update' // About to send the update docs prompt
| 'waiting_update' // Waiting for update to complete
| 'sending_clear' // About to send /clear
| 'waiting_clear' // Waiting for clear to complete
| 'sending_init' // About to send /init
| 'waiting_init' // Waiting for init to complete
| 'monitoring_init' // Monitoring if /init triggered work
| 'sending_kickstart' // About to send kickstart prompt
| 'waiting_kickstart' // Waiting for kickstart to complete
| 'stopped'; // Controller stopped
export interface RespawnConfig {
/** How long to wait after seeing prompt before considering truly idle (ms) */
@@ -42,6 +45,8 @@ export interface RespawnConfig {
sendClear: boolean;
/** Whether to send /init after /clear */
sendInit: boolean;
/** Optional prompt to send if /init doesn't trigger work */
kickstartPrompt?: string;
}
export interface RespawnEvents {
@@ -210,6 +215,13 @@ export class RespawnController extends EventEmitter {
this.promptDetected = false;
this.lastActivityTime = Date.now();
this.clearIdleTimer();
// If we're monitoring init and work started, go to watching (no kickstart needed)
if (this._state === 'monitoring_init') {
this.log('/init triggered work, skipping kickstart');
this.emit('stepCompleted', 'init');
this.completeCycle();
}
return;
}
@@ -235,6 +247,12 @@ export class RespawnController extends EventEmitter {
case 'waiting_init':
this.checkInitComplete();
break;
case 'monitoring_init':
this.checkMonitoringInitIdle();
break;
case 'waiting_kickstart':
this.checkKickstartComplete();
break;
}
return;
}
@@ -287,7 +305,61 @@ export class RespawnController extends EventEmitter {
private checkInitComplete(): void {
this.clearIdleTimer();
this.log('/init completed (ready indicator)');
// If kickstart prompt is configured, monitor to see if /init triggered work
if (this.config.kickstartPrompt) {
this.startMonitoringInit();
} else {
this.emit('stepCompleted', 'init');
this.completeCycle();
}
}
private startMonitoringInit(): void {
this.setState('monitoring_init');
this.terminalBuffer = '';
this.workingDetected = false;
this.log('Monitoring if /init triggered work...');
// Give Claude a moment to start working before checking for idle
this.stepTimer = setTimeout(() => {
// If still in monitoring state and no work detected, consider it idle
if (this._state === 'monitoring_init' && !this.workingDetected) {
this.checkMonitoringInitIdle();
}
}, 3000); // 3 second grace period for /init to trigger work
}
private checkMonitoringInitIdle(): void {
this.clearIdleTimer();
if (this.stepTimer) {
clearTimeout(this.stepTimer);
this.stepTimer = null;
}
this.log('/init did not trigger work, sending kickstart prompt');
this.emit('stepCompleted', 'init');
this.sendKickstart();
}
private sendKickstart(): void {
this.setState('sending_kickstart');
this.terminalBuffer = '';
this.stepTimer = setTimeout(() => {
const prompt = this.config.kickstartPrompt!;
this.log(`Sending kickstart prompt: "${prompt}"`);
this.session.write(prompt + '\n');
this.emit('stepSent', 'kickstart', prompt);
this.setState('waiting_kickstart');
this.promptDetected = false;
this.workingDetected = false;
}, this.config.interStepDelayMs);
}
private checkKickstartComplete(): void {
this.clearIdleTimer();
this.log('Kickstart completed (ready indicator)');
this.emit('stepCompleted', 'kickstart');
this.completeCycle();
}