mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 14:39:42 +02:00
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:
+31
-4
@@ -1264,22 +1264,32 @@ class ClaudemanApp {
|
||||
// Get respawn config from modal inputs
|
||||
getModalRespawnConfig() {
|
||||
const updatePrompt = document.getElementById('modalRespawnPrompt').value;
|
||||
const idleTimeout = parseInt(document.getElementById('modalRespawnIdleTimeout').value) || 5;
|
||||
const sendClear = document.getElementById('modalRespawnSendClear').checked;
|
||||
const sendInit = document.getElementById('modalRespawnSendInit').checked;
|
||||
const kickstartPrompt = document.getElementById('modalRespawnKickstart').value.trim() || undefined;
|
||||
const durationStr = document.getElementById('modalRespawnDuration').value;
|
||||
const durationMinutes = durationStr ? parseInt(durationStr) : null;
|
||||
|
||||
// Auto-compact settings
|
||||
const autoCompactEnabled = document.getElementById('modalAutoCompactEnabled').checked;
|
||||
const autoCompactThreshold = parseInt(document.getElementById('modalAutoCompactThreshold').value) || 110000;
|
||||
const autoCompactPrompt = document.getElementById('modalAutoCompactPrompt').value.trim() || undefined;
|
||||
|
||||
// Auto-clear settings
|
||||
const autoClearEnabled = document.getElementById('modalAutoClearEnabled').checked;
|
||||
const autoClearThreshold = parseInt(document.getElementById('modalAutoClearThreshold').value) || 100000;
|
||||
const autoClearThreshold = parseInt(document.getElementById('modalAutoClearThreshold').value) || 140000;
|
||||
|
||||
return {
|
||||
respawnConfig: {
|
||||
updatePrompt,
|
||||
idleTimeoutMs: idleTimeout * 1000,
|
||||
sendClear,
|
||||
sendInit,
|
||||
kickstartPrompt,
|
||||
},
|
||||
durationMinutes,
|
||||
autoCompactEnabled,
|
||||
autoCompactThreshold,
|
||||
autoCompactPrompt,
|
||||
autoClearEnabled,
|
||||
autoClearThreshold
|
||||
};
|
||||
@@ -1291,7 +1301,15 @@ class ClaudemanApp {
|
||||
return;
|
||||
}
|
||||
|
||||
const { respawnConfig, durationMinutes, autoClearEnabled, autoClearThreshold } = this.getModalRespawnConfig();
|
||||
const {
|
||||
respawnConfig,
|
||||
durationMinutes,
|
||||
autoCompactEnabled,
|
||||
autoCompactThreshold,
|
||||
autoCompactPrompt,
|
||||
autoClearEnabled,
|
||||
autoClearThreshold
|
||||
} = this.getModalRespawnConfig();
|
||||
|
||||
try {
|
||||
// Enable respawn on the session
|
||||
@@ -1303,6 +1321,15 @@ class ClaudemanApp {
|
||||
const data = await res.json();
|
||||
if (data.error) throw new Error(data.error);
|
||||
|
||||
// Set auto-compact if enabled
|
||||
if (autoCompactEnabled) {
|
||||
await fetch(`/api/sessions/${this.editingSessionId}/auto-compact`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ enabled: true, threshold: autoCompactThreshold, prompt: autoCompactPrompt })
|
||||
});
|
||||
}
|
||||
|
||||
// Set auto-clear if enabled
|
||||
if (autoClearEnabled) {
|
||||
await fetch(`/api/sessions/${this.editingSessionId}/auto-clear`, {
|
||||
|
||||
@@ -204,14 +204,6 @@
|
||||
<input type="text" id="modalRespawnPrompt" value="update all the docs and CLAUDE.md" placeholder="Prompt to send when idle">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Idle Timeout</label>
|
||||
<div class="input-suffix">
|
||||
<input type="number" id="modalRespawnIdleTimeout" value="5" min="1" max="60">
|
||||
<span>sec</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Run Duration</label>
|
||||
<div class="input-suffix">
|
||||
@@ -235,6 +227,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Kickstart Prompt</label>
|
||||
<input type="text" id="modalRespawnKickstart" value="" placeholder="Optional: prompt if /init doesn't trigger work">
|
||||
<span class="form-hint">Sent only when /init completes but Claude stays idle</span>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Auto-Compact Context</label>
|
||||
<div class="auto-clear-row">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" id="modalAutoCompactEnabled">
|
||||
<span>Enable at</span>
|
||||
</label>
|
||||
<div class="input-suffix input-suffix-sm">
|
||||
<input type="number" id="modalAutoCompactThreshold" value="110000" min="10000" max="500000" step="10000">
|
||||
<span>tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" id="modalAutoCompactPrompt" value="" placeholder="Optional: focus prompt for compact" class="mt-sm">
|
||||
<span class="form-hint">Summarizes context while preserving key information</span>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Auto-Clear Context</label>
|
||||
<div class="auto-clear-row">
|
||||
@@ -243,10 +257,11 @@
|
||||
<span>Enable at</span>
|
||||
</label>
|
||||
<div class="input-suffix input-suffix-sm">
|
||||
<input type="number" id="modalAutoClearThreshold" value="100000" min="10000" max="500000" step="10000">
|
||||
<input type="number" id="modalAutoClearThreshold" value="140000" min="10000" max="500000" step="10000">
|
||||
<span>tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="form-hint">Completely resets context (use higher threshold than compact)</span>
|
||||
</div>
|
||||
|
||||
<div class="respawn-actions">
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
/* Utility classes */
|
||||
.mt-sm { margin-top: 0.5rem; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
background: var(--bg-dark);
|
||||
|
||||
@@ -515,6 +515,29 @@ export class WebServer extends EventEmitter {
|
||||
};
|
||||
});
|
||||
|
||||
// Set auto-compact on a session
|
||||
this.app.post('/api/sessions/:id/auto-compact', async (req) => {
|
||||
const { id } = req.params as { id: string };
|
||||
const body = req.body as { enabled: boolean; threshold?: number; prompt?: string };
|
||||
const session = this.sessions.get(id);
|
||||
|
||||
if (!session) {
|
||||
return { error: 'Session not found' };
|
||||
}
|
||||
|
||||
session.setAutoCompact(body.enabled, body.threshold, body.prompt);
|
||||
this.broadcast('session:updated', session.toDetailedState());
|
||||
|
||||
return {
|
||||
success: true,
|
||||
autoCompact: {
|
||||
enabled: session.autoCompactEnabled,
|
||||
threshold: session.autoCompactThreshold,
|
||||
prompt: session.autoCompactPrompt,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Quick run (create session, run prompt, return result, then cleanup)
|
||||
this.app.post('/api/run', async (req) => {
|
||||
// Prevent unbounded session creation
|
||||
@@ -912,6 +935,11 @@ export class WebServer extends EventEmitter {
|
||||
this.broadcast('session:autoClear', { sessionId: session.id, ...data });
|
||||
this.broadcast('session:updated', session.toDetailedState());
|
||||
});
|
||||
|
||||
session.on('autoCompact', (data: { tokens: number; threshold: number; prompt?: string }) => {
|
||||
this.broadcast('session:autoCompact', { sessionId: session.id, ...data });
|
||||
this.broadcast('session:updated', session.toDetailedState());
|
||||
});
|
||||
}
|
||||
|
||||
private setupRespawnListeners(sessionId: string, controller: RespawnController): void {
|
||||
|
||||
Reference in New Issue
Block a user