aicodeman 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +403 -0
- package/dist/ai-checker-base.d.ts +175 -0
- package/dist/ai-checker-base.d.ts.map +1 -0
- package/dist/ai-checker-base.js +424 -0
- package/dist/ai-checker-base.js.map +1 -0
- package/dist/ai-idle-checker.d.ts +53 -0
- package/dist/ai-idle-checker.d.ts.map +1 -0
- package/dist/ai-idle-checker.js +141 -0
- package/dist/ai-idle-checker.js.map +1 -0
- package/dist/ai-plan-checker.d.ts +52 -0
- package/dist/ai-plan-checker.d.ts.map +1 -0
- package/dist/ai-plan-checker.js +103 -0
- package/dist/ai-plan-checker.js.map +1 -0
- package/dist/bash-tool-parser.d.ts +191 -0
- package/dist/bash-tool-parser.d.ts.map +1 -0
- package/dist/bash-tool-parser.js +598 -0
- package/dist/bash-tool-parser.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +460 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/buffer-limits.d.ts +59 -0
- package/dist/config/buffer-limits.d.ts.map +1 -0
- package/dist/config/buffer-limits.js +74 -0
- package/dist/config/buffer-limits.js.map +1 -0
- package/dist/config/map-limits.d.ts +40 -0
- package/dist/config/map-limits.d.ts.map +1 -0
- package/dist/config/map-limits.js +52 -0
- package/dist/config/map-limits.js.map +1 -0
- package/dist/file-stream-manager.d.ts +148 -0
- package/dist/file-stream-manager.d.ts.map +1 -0
- package/dist/file-stream-manager.js +351 -0
- package/dist/file-stream-manager.js.map +1 -0
- package/dist/hooks-config.d.ts +31 -0
- package/dist/hooks-config.d.ts.map +1 -0
- package/dist/hooks-config.js +115 -0
- package/dist/hooks-config.js.map +1 -0
- package/dist/image-watcher.d.ts +86 -0
- package/dist/image-watcher.d.ts.map +1 -0
- package/dist/image-watcher.js +275 -0
- package/dist/image-watcher.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/mux-factory.d.ts +13 -0
- package/dist/mux-factory.d.ts.map +1 -0
- package/dist/mux-factory.js +19 -0
- package/dist/mux-factory.js.map +1 -0
- package/dist/mux-interface.d.ts +145 -0
- package/dist/mux-interface.d.ts.map +1 -0
- package/dist/mux-interface.js +9 -0
- package/dist/mux-interface.js.map +1 -0
- package/dist/plan-orchestrator.d.ts +123 -0
- package/dist/plan-orchestrator.d.ts.map +1 -0
- package/dist/plan-orchestrator.js +500 -0
- package/dist/plan-orchestrator.js.map +1 -0
- package/dist/prompts/index.d.ts +9 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +9 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/planner.d.ts +14 -0
- package/dist/prompts/planner.d.ts.map +1 -0
- package/dist/prompts/planner.js +83 -0
- package/dist/prompts/planner.js.map +1 -0
- package/dist/prompts/research-agent.d.ts +10 -0
- package/dist/prompts/research-agent.d.ts.map +1 -0
- package/dist/prompts/research-agent.js +143 -0
- package/dist/prompts/research-agent.js.map +1 -0
- package/dist/push-store.d.ts +41 -0
- package/dist/push-store.d.ts.map +1 -0
- package/dist/push-store.js +168 -0
- package/dist/push-store.js.map +1 -0
- package/dist/ralph-config.d.ts +67 -0
- package/dist/ralph-config.d.ts.map +1 -0
- package/dist/ralph-config.js +134 -0
- package/dist/ralph-config.js.map +1 -0
- package/dist/ralph-loop.d.ts +124 -0
- package/dist/ralph-loop.d.ts.map +1 -0
- package/dist/ralph-loop.js +418 -0
- package/dist/ralph-loop.js.map +1 -0
- package/dist/ralph-tracker.d.ts +1081 -0
- package/dist/ralph-tracker.d.ts.map +1 -0
- package/dist/ralph-tracker.js +3343 -0
- package/dist/ralph-tracker.js.map +1 -0
- package/dist/respawn-controller.d.ts +1182 -0
- package/dist/respawn-controller.d.ts.map +1 -0
- package/dist/respawn-controller.js +2754 -0
- package/dist/respawn-controller.js.map +1 -0
- package/dist/run-summary.d.ts +123 -0
- package/dist/run-summary.d.ts.map +1 -0
- package/dist/run-summary.js +325 -0
- package/dist/run-summary.js.map +1 -0
- package/dist/session-lifecycle-log.d.ts +36 -0
- package/dist/session-lifecycle-log.d.ts.map +1 -0
- package/dist/session-lifecycle-log.js +101 -0
- package/dist/session-lifecycle-log.js.map +1 -0
- package/dist/session-manager.d.ts +97 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +224 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/session.d.ts +686 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +2025 -0
- package/dist/session.js.map +1 -0
- package/dist/state-store.d.ts +189 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +730 -0
- package/dist/state-store.js.map +1 -0
- package/dist/subagent-watcher.d.ts +345 -0
- package/dist/subagent-watcher.d.ts.map +1 -0
- package/dist/subagent-watcher.js +1469 -0
- package/dist/subagent-watcher.js.map +1 -0
- package/dist/task-queue.d.ts +108 -0
- package/dist/task-queue.d.ts.map +1 -0
- package/dist/task-queue.js +235 -0
- package/dist/task-queue.js.map +1 -0
- package/dist/task-tracker.d.ts +306 -0
- package/dist/task-tracker.d.ts.map +1 -0
- package/dist/task-tracker.js +488 -0
- package/dist/task-tracker.js.map +1 -0
- package/dist/task.d.ts +73 -0
- package/dist/task.d.ts.map +1 -0
- package/dist/task.js +177 -0
- package/dist/task.js.map +1 -0
- package/dist/team-watcher.d.ts +53 -0
- package/dist/team-watcher.d.ts.map +1 -0
- package/dist/team-watcher.js +313 -0
- package/dist/team-watcher.js.map +1 -0
- package/dist/templates/case-template.md +461 -0
- package/dist/templates/claude-md.d.ts +26 -0
- package/dist/templates/claude-md.d.ts.map +1 -0
- package/dist/templates/claude-md.js +74 -0
- package/dist/templates/claude-md.js.map +1 -0
- package/dist/tmux-manager.d.ts +181 -0
- package/dist/tmux-manager.d.ts.map +1 -0
- package/dist/tmux-manager.js +1405 -0
- package/dist/tmux-manager.js.map +1 -0
- package/dist/transcript-watcher.d.ts +110 -0
- package/dist/transcript-watcher.d.ts.map +1 -0
- package/dist/transcript-watcher.js +338 -0
- package/dist/transcript-watcher.js.map +1 -0
- package/dist/tunnel-manager.d.ts +54 -0
- package/dist/tunnel-manager.d.ts.map +1 -0
- package/dist/tunnel-manager.js +251 -0
- package/dist/tunnel-manager.js.map +1 -0
- package/dist/types.d.ts +1139 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +215 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/buffer-accumulator.d.ts +111 -0
- package/dist/utils/buffer-accumulator.d.ts.map +1 -0
- package/dist/utils/buffer-accumulator.js +172 -0
- package/dist/utils/buffer-accumulator.js.map +1 -0
- package/dist/utils/claude-cli-resolver.d.ts +26 -0
- package/dist/utils/claude-cli-resolver.d.ts.map +1 -0
- package/dist/utils/claude-cli-resolver.js +78 -0
- package/dist/utils/claude-cli-resolver.js.map +1 -0
- package/dist/utils/cleanup-manager.d.ts +165 -0
- package/dist/utils/cleanup-manager.d.ts.map +1 -0
- package/dist/utils/cleanup-manager.js +274 -0
- package/dist/utils/cleanup-manager.js.map +1 -0
- package/dist/utils/index.d.ts +19 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +19 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/lru-map.d.ts +140 -0
- package/dist/utils/lru-map.d.ts.map +1 -0
- package/dist/utils/lru-map.js +234 -0
- package/dist/utils/lru-map.js.map +1 -0
- package/dist/utils/nice-wrapper.d.ts +13 -0
- package/dist/utils/nice-wrapper.d.ts.map +1 -0
- package/dist/utils/nice-wrapper.js +17 -0
- package/dist/utils/nice-wrapper.js.map +1 -0
- package/dist/utils/opencode-cli-resolver.d.ts +21 -0
- package/dist/utils/opencode-cli-resolver.d.ts.map +1 -0
- package/dist/utils/opencode-cli-resolver.js +67 -0
- package/dist/utils/opencode-cli-resolver.js.map +1 -0
- package/dist/utils/regex-patterns.d.ts +64 -0
- package/dist/utils/regex-patterns.d.ts.map +1 -0
- package/dist/utils/regex-patterns.js +74 -0
- package/dist/utils/regex-patterns.js.map +1 -0
- package/dist/utils/stale-expiration-map.d.ts +159 -0
- package/dist/utils/stale-expiration-map.d.ts.map +1 -0
- package/dist/utils/stale-expiration-map.js +277 -0
- package/dist/utils/stale-expiration-map.js.map +1 -0
- package/dist/utils/string-similarity.d.ts +108 -0
- package/dist/utils/string-similarity.d.ts.map +1 -0
- package/dist/utils/string-similarity.js +189 -0
- package/dist/utils/string-similarity.js.map +1 -0
- package/dist/utils/token-validation.d.ts +39 -0
- package/dist/utils/token-validation.d.ts.map +1 -0
- package/dist/utils/token-validation.js +59 -0
- package/dist/utils/token-validation.js.map +1 -0
- package/dist/utils/type-safety.d.ts +33 -0
- package/dist/utils/type-safety.d.ts.map +1 -0
- package/dist/utils/type-safety.js +35 -0
- package/dist/utils/type-safety.js.map +1 -0
- package/dist/web/public/app.js +491 -0
- package/dist/web/public/app.js.br +0 -0
- package/dist/web/public/app.js.gz +0 -0
- package/dist/web/public/index.html +1675 -0
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/manifest.json +8 -0
- package/dist/web/public/mobile.css +1 -0
- package/dist/web/public/mobile.css.br +0 -0
- package/dist/web/public/mobile.css.gz +0 -0
- package/dist/web/public/ralph-wizard.js +1037 -0
- package/dist/web/public/ralph-wizard.js.br +0 -0
- package/dist/web/public/ralph-wizard.js.gz +0 -0
- package/dist/web/public/styles.css +1 -0
- package/dist/web/public/styles.css.br +0 -0
- package/dist/web/public/styles.css.gz +0 -0
- package/dist/web/public/sw.js +67 -0
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/upload.html +155 -0
- package/dist/web/public/upload.html.br +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js +1 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.br +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js +1 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.br +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js +2 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.br +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css +209 -0
- package/dist/web/public/vendor/xterm.css.br +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js +9 -0
- package/dist/web/public/vendor/xterm.min.js.br +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/schemas.d.ts +479 -0
- package/dist/web/schemas.d.ts.map +1 -0
- package/dist/web/schemas.js +448 -0
- package/dist/web/schemas.js.map +1 -0
- package/dist/web/server.d.ts +207 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +5784 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +110 -0
- package/scripts/postinstall.js +390 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Base Class for AI Checker Components
|
|
3
|
+
*
|
|
4
|
+
* Provides shared functionality for AI-powered checkers that spawn fresh Claude CLI
|
|
5
|
+
* sessions to analyze terminal output. This base class handles:
|
|
6
|
+
* - Mux session spawning and cleanup
|
|
7
|
+
* - Temp file management for output capture
|
|
8
|
+
* - Polling for completion markers
|
|
9
|
+
* - Cooldown management
|
|
10
|
+
* - Error handling and consecutive error tracking
|
|
11
|
+
* - Event emission for state changes
|
|
12
|
+
*
|
|
13
|
+
* Subclasses implement:
|
|
14
|
+
* - `muxNamePrefix`: Prefix for mux session names (e.g., 'codeman-aicheck-')
|
|
15
|
+
* - `doneMarker`: Completion marker in output file (e.g., '__AICHECK_DONE__')
|
|
16
|
+
* - `tempFilePrefix`: Prefix for temp files (e.g., 'codeman-aicheck')
|
|
17
|
+
* - `logPrefix`: Prefix for log messages (e.g., '[AiIdleChecker]')
|
|
18
|
+
* - `buildPrompt()`: Build the prompt from terminal buffer
|
|
19
|
+
* - `parseVerdict()`: Parse the verdict from AI output
|
|
20
|
+
* - `getPositiveVerdict()`: Return the "positive" verdict that doesn't trigger cooldown
|
|
21
|
+
* - `defaultConfig`: Default configuration values
|
|
22
|
+
*
|
|
23
|
+
* @module ai-checker-base
|
|
24
|
+
*/
|
|
25
|
+
import { execSync, spawn as childSpawn } from 'node:child_process';
|
|
26
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
27
|
+
import { tmpdir } from 'node:os';
|
|
28
|
+
import { join } from 'node:path';
|
|
29
|
+
import { EventEmitter } from 'node:events';
|
|
30
|
+
import { getAugmentedPath } from './utils/claude-cli-resolver.js';
|
|
31
|
+
import { ANSI_ESCAPE_PATTERN_SIMPLE } from './utils/index.js';
|
|
32
|
+
// ========== Security Validation ==========
|
|
33
|
+
/**
|
|
34
|
+
* Validates that a model name is safe for shell use.
|
|
35
|
+
* Model names should only contain alphanumeric characters, hyphens, underscores, and dots.
|
|
36
|
+
*/
|
|
37
|
+
function isValidModelName(model) {
|
|
38
|
+
if (!model || typeof model !== 'string')
|
|
39
|
+
return false;
|
|
40
|
+
// Allow: alphanumeric, hyphens, underscores, dots, slashes (for model paths like claude/opus-4.5)
|
|
41
|
+
// Max length 100 to prevent abuse
|
|
42
|
+
return /^[a-zA-Z0-9._/-]+$/.test(model) && model.length <= 100;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validates that a mux session name is safe for shell use.
|
|
46
|
+
* Names should only contain alphanumeric characters, hyphens, and underscores.
|
|
47
|
+
*/
|
|
48
|
+
function isValidMuxName(muxName) {
|
|
49
|
+
if (!muxName || typeof muxName !== 'string')
|
|
50
|
+
return false;
|
|
51
|
+
return /^[a-zA-Z0-9_-]+$/.test(muxName) && muxName.length <= 100;
|
|
52
|
+
}
|
|
53
|
+
// ========== Constants ==========
|
|
54
|
+
/** Poll interval for checking temp file completion */
|
|
55
|
+
const POLL_INTERVAL_MS = 500;
|
|
56
|
+
// ========== AiCheckerBase Class ==========
|
|
57
|
+
/**
|
|
58
|
+
* Abstract base class for AI-powered checkers.
|
|
59
|
+
* Handles spawning Claude CLI in a mux session to analyze terminal output.
|
|
60
|
+
*
|
|
61
|
+
* @template V - The verdict type (e.g., 'IDLE' | 'WORKING' | 'ERROR')
|
|
62
|
+
* @template C - The configuration type
|
|
63
|
+
* @template R - The result type
|
|
64
|
+
* @template S - The state type
|
|
65
|
+
*/
|
|
66
|
+
export class AiCheckerBase extends EventEmitter {
|
|
67
|
+
config;
|
|
68
|
+
sessionId;
|
|
69
|
+
// State
|
|
70
|
+
_status = 'ready';
|
|
71
|
+
lastVerdict = null;
|
|
72
|
+
lastReasoning = null;
|
|
73
|
+
lastCheckDurationMs = null;
|
|
74
|
+
cooldownEndsAt = null;
|
|
75
|
+
cooldownTimer = null;
|
|
76
|
+
consecutiveErrors = 0;
|
|
77
|
+
totalChecks = 0;
|
|
78
|
+
disabledReason = null;
|
|
79
|
+
// Active check state
|
|
80
|
+
checkMuxName = null;
|
|
81
|
+
checkTempFile = null;
|
|
82
|
+
checkPromptFile = null;
|
|
83
|
+
checkPollTimer = null;
|
|
84
|
+
checkTimeoutTimer = null;
|
|
85
|
+
checkStartTime = 0;
|
|
86
|
+
checkCancelled = false;
|
|
87
|
+
checkResolve = null;
|
|
88
|
+
constructor(sessionId, defaultConfig, config = {}) {
|
|
89
|
+
super();
|
|
90
|
+
this.sessionId = sessionId;
|
|
91
|
+
// Filter out undefined values to prevent overwriting defaults
|
|
92
|
+
const filteredConfig = Object.fromEntries(Object.entries(config).filter(([, v]) => v !== undefined));
|
|
93
|
+
this.config = { ...defaultConfig, ...filteredConfig };
|
|
94
|
+
}
|
|
95
|
+
/** Get the current status */
|
|
96
|
+
get status() {
|
|
97
|
+
return this._status;
|
|
98
|
+
}
|
|
99
|
+
/** Get comprehensive state for UI display */
|
|
100
|
+
getState() {
|
|
101
|
+
return {
|
|
102
|
+
status: this._status,
|
|
103
|
+
lastVerdict: this.lastVerdict,
|
|
104
|
+
lastReasoning: this.lastReasoning,
|
|
105
|
+
lastCheckDurationMs: this.lastCheckDurationMs,
|
|
106
|
+
cooldownEndsAt: this.cooldownEndsAt,
|
|
107
|
+
consecutiveErrors: this.consecutiveErrors,
|
|
108
|
+
totalChecks: this.totalChecks,
|
|
109
|
+
disabledReason: this.disabledReason,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** Check if the checker is on cooldown */
|
|
113
|
+
isOnCooldown() {
|
|
114
|
+
if (this.cooldownEndsAt === null)
|
|
115
|
+
return false;
|
|
116
|
+
return Date.now() < this.cooldownEndsAt;
|
|
117
|
+
}
|
|
118
|
+
/** Get remaining cooldown time in ms */
|
|
119
|
+
getCooldownRemainingMs() {
|
|
120
|
+
if (this.cooldownEndsAt === null)
|
|
121
|
+
return 0;
|
|
122
|
+
return Math.max(0, this.cooldownEndsAt - Date.now());
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Run an AI check against the provided terminal buffer.
|
|
126
|
+
* Spawns a fresh Claude CLI in a tmux session, captures output to temp file.
|
|
127
|
+
*
|
|
128
|
+
* @param terminalBuffer - Raw terminal output to analyze
|
|
129
|
+
* @returns The verdict result
|
|
130
|
+
*/
|
|
131
|
+
async check(terminalBuffer) {
|
|
132
|
+
if (this._status === 'disabled') {
|
|
133
|
+
return this.createErrorResult(`Disabled: ${this.disabledReason}`, 0);
|
|
134
|
+
}
|
|
135
|
+
if (this.isOnCooldown()) {
|
|
136
|
+
return this.createErrorResult('On cooldown', 0);
|
|
137
|
+
}
|
|
138
|
+
if (this._status === 'checking') {
|
|
139
|
+
return this.createErrorResult('Already checking', 0);
|
|
140
|
+
}
|
|
141
|
+
this._status = 'checking';
|
|
142
|
+
this.checkCancelled = false;
|
|
143
|
+
this.checkStartTime = Date.now();
|
|
144
|
+
this.totalChecks++;
|
|
145
|
+
this.emit('checkStarted');
|
|
146
|
+
this.log(`Starting ${this.checkDescription}`);
|
|
147
|
+
try {
|
|
148
|
+
const result = await this.runCheck(terminalBuffer);
|
|
149
|
+
if (this.checkCancelled) {
|
|
150
|
+
return this.createErrorResult('Cancelled', Date.now() - this.checkStartTime);
|
|
151
|
+
}
|
|
152
|
+
this.lastVerdict = result.verdict;
|
|
153
|
+
this.lastReasoning = result.reasoning;
|
|
154
|
+
this.lastCheckDurationMs = result.durationMs;
|
|
155
|
+
if (result.verdict === this.getPositiveVerdict()) {
|
|
156
|
+
this.consecutiveErrors = 0;
|
|
157
|
+
this._status = 'ready';
|
|
158
|
+
this.log(`${this.checkDescription} verdict: ${result.verdict} (${result.durationMs}ms) - ${result.reasoning}`);
|
|
159
|
+
}
|
|
160
|
+
else if (result.verdict === this.getNegativeVerdict()) {
|
|
161
|
+
this.consecutiveErrors = 0;
|
|
162
|
+
this.startCooldown(this.config.cooldownMs);
|
|
163
|
+
this.log(`${this.checkDescription} verdict: ${result.verdict} (${result.durationMs}ms) - ${result.reasoning}`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.handleError('Unexpected verdict');
|
|
167
|
+
}
|
|
168
|
+
this.emit('checkCompleted', result);
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
173
|
+
this.handleError(errorMsg);
|
|
174
|
+
const result = this.createErrorResult(errorMsg, Date.now() - this.checkStartTime);
|
|
175
|
+
this.emit('checkFailed', errorMsg);
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
this.cleanupCheck();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Cancel an in-progress check.
|
|
184
|
+
* Kills the mux session and cleans up.
|
|
185
|
+
*/
|
|
186
|
+
cancel() {
|
|
187
|
+
if (this._status !== 'checking')
|
|
188
|
+
return;
|
|
189
|
+
this.log(`Cancelling ${this.checkDescription}`);
|
|
190
|
+
this.checkCancelled = true;
|
|
191
|
+
// Clear poll/timeout timers first to prevent race condition where
|
|
192
|
+
// the poll timer fires between setting checkCancelled and cleanup
|
|
193
|
+
this.cleanupCheck();
|
|
194
|
+
// Resolve the pending promise after cleanup
|
|
195
|
+
if (this.checkResolve) {
|
|
196
|
+
this.checkResolve(this.createErrorResult('Cancelled', Date.now() - this.checkStartTime));
|
|
197
|
+
this.checkResolve = null;
|
|
198
|
+
}
|
|
199
|
+
this._status = 'ready';
|
|
200
|
+
}
|
|
201
|
+
/** Reset all state for a new cycle */
|
|
202
|
+
reset() {
|
|
203
|
+
this.cancel();
|
|
204
|
+
this.clearCooldown();
|
|
205
|
+
this.lastVerdict = null;
|
|
206
|
+
this.lastReasoning = null;
|
|
207
|
+
this.lastCheckDurationMs = null;
|
|
208
|
+
this.consecutiveErrors = 0;
|
|
209
|
+
this._status = this.disabledReason ? 'disabled' : 'ready';
|
|
210
|
+
}
|
|
211
|
+
/** Update configuration at runtime */
|
|
212
|
+
updateConfig(config) {
|
|
213
|
+
// Filter out undefined values to prevent overwriting existing config
|
|
214
|
+
const filteredConfig = Object.fromEntries(Object.entries(config).filter(([, v]) => v !== undefined));
|
|
215
|
+
this.config = { ...this.config, ...filteredConfig };
|
|
216
|
+
if (config.enabled === false) {
|
|
217
|
+
this.disable('Disabled by config');
|
|
218
|
+
}
|
|
219
|
+
else if (config.enabled === true && this._status === 'disabled') {
|
|
220
|
+
this.disabledReason = null;
|
|
221
|
+
this._status = 'ready';
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/** Get current config */
|
|
225
|
+
getConfig() {
|
|
226
|
+
return { ...this.config };
|
|
227
|
+
}
|
|
228
|
+
// ========== Private Methods ==========
|
|
229
|
+
async runCheck(terminalBuffer) {
|
|
230
|
+
// Security: Validate model name before use in shell commands
|
|
231
|
+
if (!isValidModelName(this.config.model)) {
|
|
232
|
+
throw new Error(`Invalid model name: ${this.config.model.substring(0, 50)}`);
|
|
233
|
+
}
|
|
234
|
+
// Prepare the terminal buffer (strip ANSI, trim to maxContextChars)
|
|
235
|
+
const stripped = terminalBuffer.replace(ANSI_ESCAPE_PATTERN_SIMPLE, '');
|
|
236
|
+
const trimmed = stripped.length > this.config.maxContextChars ? stripped.slice(-this.config.maxContextChars) : stripped;
|
|
237
|
+
// Build the prompt
|
|
238
|
+
const prompt = this.buildPrompt(trimmed);
|
|
239
|
+
// Generate temp files and mux session name
|
|
240
|
+
const shortId = this.sessionId.slice(0, 8);
|
|
241
|
+
const timestamp = Date.now();
|
|
242
|
+
this.checkTempFile = join(tmpdir(), `${this.tempFilePrefix}-${shortId}-${timestamp}.txt`);
|
|
243
|
+
this.checkPromptFile = join(tmpdir(), `${this.tempFilePrefix}-prompt-${shortId}-${timestamp}.txt`);
|
|
244
|
+
this.checkMuxName = `${this.muxNamePrefix}${shortId}`;
|
|
245
|
+
// Security: Validate mux name before use in shell commands
|
|
246
|
+
if (!isValidMuxName(this.checkMuxName)) {
|
|
247
|
+
throw new Error(`Invalid mux name generated: ${this.checkMuxName.substring(0, 50)}`);
|
|
248
|
+
}
|
|
249
|
+
// Ensure output temp file exists (empty) so we can poll it
|
|
250
|
+
writeFileSync(this.checkTempFile, '');
|
|
251
|
+
// Write prompt to file to avoid E2BIG error (argument list too long)
|
|
252
|
+
// The prompt can be 16KB+ which exceeds shell argument limits
|
|
253
|
+
writeFileSync(this.checkPromptFile, prompt);
|
|
254
|
+
// Build the command - read prompt from file via stdin to avoid argument size limits
|
|
255
|
+
// Quote model name to prevent command injection (model names should be simple alphanumeric but be safe)
|
|
256
|
+
const modelArg = `--model "${this.config.model.replace(/"/g, '\\"')}"`;
|
|
257
|
+
const augmentedPath = getAugmentedPath();
|
|
258
|
+
const claudeCmd = `cat "${this.checkPromptFile}" | claude -p ${modelArg} --output-format text`;
|
|
259
|
+
const fullCmd = `export PATH="${augmentedPath}"; ${claudeCmd} > "${this.checkTempFile}" 2>&1; echo "${this.doneMarker}" >> "${this.checkTempFile}"; rm -f "${this.checkPromptFile}"`;
|
|
260
|
+
// Spawn tmux session
|
|
261
|
+
try {
|
|
262
|
+
// Kill any leftover session with this name first (mux name already validated above)
|
|
263
|
+
try {
|
|
264
|
+
execSync(`tmux kill-session -t "${this.checkMuxName}" 2>/dev/null`, { timeout: 3000 });
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// No existing session, that's fine
|
|
268
|
+
}
|
|
269
|
+
const muxProcess = childSpawn('tmux', ['new-session', '-d', '-s', this.checkMuxName, 'bash', '-c', fullCmd], {
|
|
270
|
+
detached: true,
|
|
271
|
+
stdio: 'ignore',
|
|
272
|
+
});
|
|
273
|
+
muxProcess.unref();
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
throw new Error(`Failed to spawn ${this.checkDescription} tmux session: ${err instanceof Error ? err.message : String(err)}`);
|
|
277
|
+
}
|
|
278
|
+
// Poll the temp file for completion
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
const startTime = this.checkStartTime;
|
|
281
|
+
this.checkResolve = resolve;
|
|
282
|
+
// Guard flag to prevent both poll and timeout from resolving (race condition)
|
|
283
|
+
let resolved = false;
|
|
284
|
+
this.checkPollTimer = setInterval(() => {
|
|
285
|
+
if (this.checkCancelled || resolved) {
|
|
286
|
+
// Cancel or already resolved - stop polling
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
if (!this.checkTempFile || !existsSync(this.checkTempFile))
|
|
291
|
+
return;
|
|
292
|
+
const content = readFileSync(this.checkTempFile, 'utf-8');
|
|
293
|
+
if (content.includes(this.doneMarker)) {
|
|
294
|
+
resolved = true; // Mark as resolved first to prevent timeout race
|
|
295
|
+
const durationMs = Date.now() - startTime;
|
|
296
|
+
const result = this.parseOutput(content, durationMs);
|
|
297
|
+
this.checkResolve = null;
|
|
298
|
+
resolve(result);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// File might not be ready yet or was deleted during cleanup, keep polling
|
|
303
|
+
}
|
|
304
|
+
}, POLL_INTERVAL_MS);
|
|
305
|
+
// Set timeout
|
|
306
|
+
this.checkTimeoutTimer = setTimeout(() => {
|
|
307
|
+
if (this._status === 'checking' && !this.checkCancelled && !resolved) {
|
|
308
|
+
resolved = true; // Mark as resolved first to prevent poll race
|
|
309
|
+
this.checkResolve = null;
|
|
310
|
+
reject(new Error(`${this.checkDescription} timed out after ${this.config.checkTimeoutMs}ms`));
|
|
311
|
+
}
|
|
312
|
+
}, this.config.checkTimeoutMs);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
parseOutput(content, durationMs) {
|
|
316
|
+
// Remove the done marker and trim
|
|
317
|
+
const output = content.replace(this.doneMarker, '').trim();
|
|
318
|
+
if (!output) {
|
|
319
|
+
return this.createErrorResult(`Empty output from ${this.checkDescription}`, durationMs);
|
|
320
|
+
}
|
|
321
|
+
// Delegate to subclass for verdict parsing
|
|
322
|
+
const parsed = this.parseVerdict(output);
|
|
323
|
+
if (!parsed) {
|
|
324
|
+
return this.createErrorResult(`Could not parse verdict from: "${output.substring(0, 100)}"`, durationMs);
|
|
325
|
+
}
|
|
326
|
+
return this.createResult(parsed.verdict, parsed.reasoning, durationMs);
|
|
327
|
+
}
|
|
328
|
+
cleanupCheck() {
|
|
329
|
+
// Clear poll timer
|
|
330
|
+
if (this.checkPollTimer) {
|
|
331
|
+
clearInterval(this.checkPollTimer);
|
|
332
|
+
this.checkPollTimer = null;
|
|
333
|
+
}
|
|
334
|
+
// Clear timeout timer
|
|
335
|
+
if (this.checkTimeoutTimer) {
|
|
336
|
+
clearTimeout(this.checkTimeoutTimer);
|
|
337
|
+
this.checkTimeoutTimer = null;
|
|
338
|
+
}
|
|
339
|
+
// Kill the tmux session
|
|
340
|
+
if (this.checkMuxName) {
|
|
341
|
+
const muxName = this.checkMuxName;
|
|
342
|
+
try {
|
|
343
|
+
execSync(`tmux kill-session -t "${muxName}" 2>/dev/null`, { timeout: 2000 });
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// Session may already be dead
|
|
347
|
+
}
|
|
348
|
+
this.checkMuxName = null;
|
|
349
|
+
}
|
|
350
|
+
// Delete temp files
|
|
351
|
+
if (this.checkTempFile) {
|
|
352
|
+
try {
|
|
353
|
+
if (existsSync(this.checkTempFile)) {
|
|
354
|
+
unlinkSync(this.checkTempFile);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
// Best effort cleanup
|
|
359
|
+
}
|
|
360
|
+
this.checkTempFile = null;
|
|
361
|
+
}
|
|
362
|
+
if (this.checkPromptFile) {
|
|
363
|
+
try {
|
|
364
|
+
if (existsSync(this.checkPromptFile)) {
|
|
365
|
+
unlinkSync(this.checkPromptFile);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
// Best effort cleanup
|
|
370
|
+
}
|
|
371
|
+
this.checkPromptFile = null;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
handleError(errorMsg) {
|
|
375
|
+
this.consecutiveErrors++;
|
|
376
|
+
this.log(`${this.checkDescription} error (${this.consecutiveErrors}/${this.config.maxConsecutiveErrors}): ${errorMsg}`);
|
|
377
|
+
if (this.consecutiveErrors >= this.config.maxConsecutiveErrors) {
|
|
378
|
+
this.disable(`${this.config.maxConsecutiveErrors} consecutive errors: ${errorMsg}`);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
// P1-005: Exponential backoff for errors
|
|
382
|
+
// Base cooldown * 2^(consecutiveErrors-1), capped at 5 minutes
|
|
383
|
+
const backoffMultiplier = Math.pow(2, this.consecutiveErrors - 1);
|
|
384
|
+
const backoffCooldownMs = Math.min(this.config.errorCooldownMs * backoffMultiplier, 5 * 60 * 1000 // Max 5 minutes
|
|
385
|
+
);
|
|
386
|
+
this.log(`Exponential backoff: ${Math.round(backoffCooldownMs / 1000)}s (error #${this.consecutiveErrors})`);
|
|
387
|
+
this.startCooldown(backoffCooldownMs);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
startCooldown(durationMs) {
|
|
391
|
+
this.clearCooldown();
|
|
392
|
+
this.cooldownEndsAt = Date.now() + durationMs;
|
|
393
|
+
this._status = 'cooldown';
|
|
394
|
+
this.emit('cooldownStarted', this.cooldownEndsAt);
|
|
395
|
+
this.log(`Cooldown started: ${Math.round(durationMs / 1000)}s`);
|
|
396
|
+
this.cooldownTimer = setTimeout(() => {
|
|
397
|
+
this.cooldownEndsAt = null;
|
|
398
|
+
this._status = 'ready';
|
|
399
|
+
this.emit('cooldownEnded');
|
|
400
|
+
this.log('Cooldown ended');
|
|
401
|
+
}, durationMs);
|
|
402
|
+
}
|
|
403
|
+
clearCooldown() {
|
|
404
|
+
if (this.cooldownTimer) {
|
|
405
|
+
clearTimeout(this.cooldownTimer);
|
|
406
|
+
this.cooldownTimer = null;
|
|
407
|
+
}
|
|
408
|
+
this.cooldownEndsAt = null;
|
|
409
|
+
if (this._status === 'cooldown') {
|
|
410
|
+
this._status = 'ready';
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
disable(reason) {
|
|
414
|
+
this.disabledReason = reason;
|
|
415
|
+
this._status = 'disabled';
|
|
416
|
+
this.clearCooldown();
|
|
417
|
+
this.log(`${this.checkDescription} disabled: ${reason}`);
|
|
418
|
+
this.emit('disabled', reason);
|
|
419
|
+
}
|
|
420
|
+
log(message) {
|
|
421
|
+
this.emit('log', `${this.logPrefix} ${message}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
//# sourceMappingURL=ai-checker-base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-checker-base.js","sourceRoot":"","sources":["../src/ai-checker-base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AI-Powered Idle Checker for Respawn Controller
|
|
3
|
+
*
|
|
4
|
+
* Spawns a fresh Claude CLI session in a tmux session to analyze terminal output
|
|
5
|
+
* and provide a definitive IDLE/WORKING verdict. This replaces the "Worked for Xm Xs"
|
|
6
|
+
* pattern as the primary idle detection signal.
|
|
7
|
+
*
|
|
8
|
+
* ## How It Works
|
|
9
|
+
*
|
|
10
|
+
* 1. Generate temp file path for output capture
|
|
11
|
+
* 2. Spawn tmux: `tmux new-session -d -s codeman-aicheck-<short> bash -c 'claude -p ...'`
|
|
12
|
+
* 3. Poll the temp file every 500ms for `__AICHECK_DONE__` marker
|
|
13
|
+
* 4. Parse the file content for IDLE/WORKING on the first word
|
|
14
|
+
* 5. Kill tmux session and delete temp file
|
|
15
|
+
*
|
|
16
|
+
* ## Error Handling
|
|
17
|
+
*
|
|
18
|
+
* - Tmux spawn fails: 1-min cooldown, increment error counter
|
|
19
|
+
* - Check times out (90s): Kill session, 1-min cooldown
|
|
20
|
+
* - Can't parse IDLE/WORKING: Treat as WORKING, 1-min cooldown
|
|
21
|
+
* - 3 consecutive errors: Disable AI check, fall back to noOutputTimeoutMs
|
|
22
|
+
* - Claude CLI not found: Disable permanently
|
|
23
|
+
*
|
|
24
|
+
* @module ai-idle-checker
|
|
25
|
+
*/
|
|
26
|
+
import { AiCheckerBase, type AiCheckerConfigBase, type AiCheckerResultBase, type AiCheckerStateBase } from './ai-checker-base.js';
|
|
27
|
+
export type AiIdleCheckConfig = AiCheckerConfigBase;
|
|
28
|
+
export type AiCheckVerdict = 'IDLE' | 'WORKING' | 'ERROR';
|
|
29
|
+
export type AiCheckResult = AiCheckerResultBase<AiCheckVerdict>;
|
|
30
|
+
export type AiCheckState = AiCheckerStateBase<AiCheckVerdict>;
|
|
31
|
+
/**
|
|
32
|
+
* Manages AI-powered idle detection by spawning a fresh Claude CLI session
|
|
33
|
+
* to analyze terminal output and provide a definitive IDLE/WORKING verdict.
|
|
34
|
+
*/
|
|
35
|
+
export declare class AiIdleChecker extends AiCheckerBase<AiCheckVerdict, AiIdleCheckConfig, AiCheckResult, AiCheckState> {
|
|
36
|
+
protected readonly muxNamePrefix = "codeman-aicheck-";
|
|
37
|
+
protected readonly doneMarker = "__AICHECK_DONE__";
|
|
38
|
+
protected readonly tempFilePrefix = "codeman-aicheck";
|
|
39
|
+
protected readonly logPrefix = "[AiIdleChecker]";
|
|
40
|
+
protected readonly checkDescription = "AI idle check";
|
|
41
|
+
constructor(sessionId: string, config?: Partial<AiIdleCheckConfig>);
|
|
42
|
+
protected buildPrompt(terminalBuffer: string): string;
|
|
43
|
+
protected parseVerdict(output: string): {
|
|
44
|
+
verdict: AiCheckVerdict;
|
|
45
|
+
reasoning: string;
|
|
46
|
+
} | null;
|
|
47
|
+
protected getPositiveVerdict(): AiCheckVerdict;
|
|
48
|
+
protected getNegativeVerdict(): AiCheckVerdict;
|
|
49
|
+
protected getErrorVerdict(): AiCheckVerdict;
|
|
50
|
+
protected createErrorResult(reasoning: string, durationMs: number): AiCheckResult;
|
|
51
|
+
protected createResult(verdict: AiCheckVerdict, reasoning: string, durationMs: number): AiCheckResult;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=ai-idle-checker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-idle-checker.d.ts","sourceRoot":"","sources":["../src/ai-idle-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG,kBAAkB,CAAC,cAAc,CAAC,CAAC;AAgF9D;;;GAGG;AACH,qBAAa,aAAc,SAAQ,aAAa,CAAC,cAAc,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,CAAC;IAC9G,SAAS,CAAC,QAAQ,CAAC,aAAa,sBAAsB;IACtD,SAAS,CAAC,QAAQ,CAAC,UAAU,sBAAsB;IACnD,SAAS,CAAC,QAAQ,CAAC,cAAc,qBAAqB;IACtD,SAAS,CAAC,QAAQ,CAAC,SAAS,qBAAqB;IACjD,SAAS,CAAC,QAAQ,CAAC,gBAAgB,mBAAmB;gBAE1C,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM;IAItE,SAAS,CAAC,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAIrD,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAW7F,SAAS,CAAC,kBAAkB,IAAI,cAAc;IAI9C,SAAS,CAAC,kBAAkB,IAAI,cAAc;IAI9C,SAAS,CAAC,eAAe,IAAI,cAAc;IAI3C,SAAS,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa;IAIjF,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa;CAGtG"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AI-Powered Idle Checker for Respawn Controller
|
|
3
|
+
*
|
|
4
|
+
* Spawns a fresh Claude CLI session in a tmux session to analyze terminal output
|
|
5
|
+
* and provide a definitive IDLE/WORKING verdict. This replaces the "Worked for Xm Xs"
|
|
6
|
+
* pattern as the primary idle detection signal.
|
|
7
|
+
*
|
|
8
|
+
* ## How It Works
|
|
9
|
+
*
|
|
10
|
+
* 1. Generate temp file path for output capture
|
|
11
|
+
* 2. Spawn tmux: `tmux new-session -d -s codeman-aicheck-<short> bash -c 'claude -p ...'`
|
|
12
|
+
* 3. Poll the temp file every 500ms for `__AICHECK_DONE__` marker
|
|
13
|
+
* 4. Parse the file content for IDLE/WORKING on the first word
|
|
14
|
+
* 5. Kill tmux session and delete temp file
|
|
15
|
+
*
|
|
16
|
+
* ## Error Handling
|
|
17
|
+
*
|
|
18
|
+
* - Tmux spawn fails: 1-min cooldown, increment error counter
|
|
19
|
+
* - Check times out (90s): Kill session, 1-min cooldown
|
|
20
|
+
* - Can't parse IDLE/WORKING: Treat as WORKING, 1-min cooldown
|
|
21
|
+
* - 3 consecutive errors: Disable AI check, fall back to noOutputTimeoutMs
|
|
22
|
+
* - Claude CLI not found: Disable permanently
|
|
23
|
+
*
|
|
24
|
+
* @module ai-idle-checker
|
|
25
|
+
*/
|
|
26
|
+
import { AiCheckerBase, } from './ai-checker-base.js';
|
|
27
|
+
// ========== Constants ==========
|
|
28
|
+
const DEFAULT_AI_CHECK_CONFIG = {
|
|
29
|
+
enabled: true,
|
|
30
|
+
model: 'claude-opus-4-5-20251101',
|
|
31
|
+
maxContextChars: 16000,
|
|
32
|
+
checkTimeoutMs: 90000,
|
|
33
|
+
cooldownMs: 180000,
|
|
34
|
+
errorCooldownMs: 60000,
|
|
35
|
+
maxConsecutiveErrors: 3,
|
|
36
|
+
};
|
|
37
|
+
/** Pattern to match IDLE or WORKING as the first word of output */
|
|
38
|
+
const VERDICT_PATTERN = /^\s*(IDLE|WORKING)\b/i;
|
|
39
|
+
/**
|
|
40
|
+
* The prompt sent to the AI idle checker.
|
|
41
|
+
*
|
|
42
|
+
* P1-005: Enhanced with more specific working pattern examples and clearer structure.
|
|
43
|
+
*/
|
|
44
|
+
const AI_CHECK_PROMPT = `You are analyzing terminal output from a Claude Code CLI session. Determine if Claude has FINISHED working (IDLE) or is STILL WORKING (WORKING).
|
|
45
|
+
|
|
46
|
+
CRITICAL RULE: When in doubt, ALWAYS answer WORKING. False positives (saying IDLE when Claude is working) cause session interruptions. It's safer to wait longer than to interrupt active work.
|
|
47
|
+
|
|
48
|
+
## IDLE Indicators (need AT LEAST 2 of these together)
|
|
49
|
+
|
|
50
|
+
1. **Completion Summary** - The most reliable signal:
|
|
51
|
+
- "✻ Worked for Xm Ys" (e.g., "✻ Worked for 2m 46s")
|
|
52
|
+
- "Worked for Xs" (e.g., "Worked for 5s")
|
|
53
|
+
- Cost summary: "$X.XX spent" or "X tokens used"
|
|
54
|
+
|
|
55
|
+
2. **Input Prompt Visible**:
|
|
56
|
+
- The ❯ prompt character at the very end
|
|
57
|
+
- Empty line after completion summary
|
|
58
|
+
- Waiting cursor position
|
|
59
|
+
|
|
60
|
+
3. **Task Completion Language**:
|
|
61
|
+
- "All done", "Finished", "Completed successfully"
|
|
62
|
+
- Explicit "waiting for input" or similar
|
|
63
|
+
|
|
64
|
+
## WORKING Indicators (ANY ONE of these = answer WORKING)
|
|
65
|
+
|
|
66
|
+
### Active Processing Indicators:
|
|
67
|
+
- **Spinners**: ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏ (Braille), ◐ ◓ ◑ ◒ (quarter), ⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷
|
|
68
|
+
- **Activity Words**: Thinking, Writing, Reading, Running, Searching, Editing, Creating, Deleting, Analyzing, Executing, Synthesizing, Compiling, Building, Processing, Loading, Generating, Testing, Checking, Validating, Brewing, Formatting, Linting, Installing, Fetching, Downloading
|
|
69
|
+
|
|
70
|
+
### Tool Execution in Progress:
|
|
71
|
+
- Bash commands with no result shown yet
|
|
72
|
+
- "Running: npm test", "Executing command..."
|
|
73
|
+
- File read/write operations incomplete
|
|
74
|
+
- Progress bars or percentage indicators
|
|
75
|
+
- Test suite running (dots appearing, "Test Suites: X passed")
|
|
76
|
+
|
|
77
|
+
### Output Structure Issues:
|
|
78
|
+
- Truncated lines without completion
|
|
79
|
+
- JSON/code blocks not closed
|
|
80
|
+
- Multi-line output clearly incomplete
|
|
81
|
+
- "..." indicating more to come
|
|
82
|
+
- Output ending mid-sentence or mid-word
|
|
83
|
+
|
|
84
|
+
### Claude Planning/Thinking:
|
|
85
|
+
- "Let me...", "I'll...", "Now I need to..."
|
|
86
|
+
- TodoWrite updates without completion
|
|
87
|
+
- Plan mode approval prompts (numbered options)
|
|
88
|
+
|
|
89
|
+
## Terminal Output to Analyze
|
|
90
|
+
---
|
|
91
|
+
{TERMINAL_BUFFER}
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Your Response
|
|
95
|
+
First line: EXACTLY "IDLE" or "WORKING" (nothing else)
|
|
96
|
+
Second line onwards: Brief explanation of your reasoning.
|
|
97
|
+
|
|
98
|
+
Remember: When uncertain, answer WORKING.`;
|
|
99
|
+
// ========== AiIdleChecker Class ==========
|
|
100
|
+
/**
|
|
101
|
+
* Manages AI-powered idle detection by spawning a fresh Claude CLI session
|
|
102
|
+
* to analyze terminal output and provide a definitive IDLE/WORKING verdict.
|
|
103
|
+
*/
|
|
104
|
+
export class AiIdleChecker extends AiCheckerBase {
|
|
105
|
+
muxNamePrefix = 'codeman-aicheck-';
|
|
106
|
+
doneMarker = '__AICHECK_DONE__';
|
|
107
|
+
tempFilePrefix = 'codeman-aicheck';
|
|
108
|
+
logPrefix = '[AiIdleChecker]';
|
|
109
|
+
checkDescription = 'AI idle check';
|
|
110
|
+
constructor(sessionId, config = {}) {
|
|
111
|
+
super(sessionId, DEFAULT_AI_CHECK_CONFIG, config);
|
|
112
|
+
}
|
|
113
|
+
buildPrompt(terminalBuffer) {
|
|
114
|
+
return AI_CHECK_PROMPT.replace('{TERMINAL_BUFFER}', terminalBuffer);
|
|
115
|
+
}
|
|
116
|
+
parseVerdict(output) {
|
|
117
|
+
const match = output.match(VERDICT_PATTERN);
|
|
118
|
+
if (!match)
|
|
119
|
+
return null;
|
|
120
|
+
const verdict = match[1].toUpperCase();
|
|
121
|
+
const lines = output.split('\n');
|
|
122
|
+
const reasoning = lines.slice(1).join('\n').trim() || `AI determined: ${verdict}`;
|
|
123
|
+
return { verdict, reasoning };
|
|
124
|
+
}
|
|
125
|
+
getPositiveVerdict() {
|
|
126
|
+
return 'IDLE';
|
|
127
|
+
}
|
|
128
|
+
getNegativeVerdict() {
|
|
129
|
+
return 'WORKING';
|
|
130
|
+
}
|
|
131
|
+
getErrorVerdict() {
|
|
132
|
+
return 'ERROR';
|
|
133
|
+
}
|
|
134
|
+
createErrorResult(reasoning, durationMs) {
|
|
135
|
+
return { verdict: 'ERROR', reasoning, durationMs };
|
|
136
|
+
}
|
|
137
|
+
createResult(verdict, reasoning, durationMs) {
|
|
138
|
+
return { verdict, reasoning, durationMs };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=ai-idle-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-idle-checker.js","sourceRoot":"","sources":["../src/ai-idle-checker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,aAAa,GAId,MAAM,sBAAsB,CAAC;AAY9B,kCAAkC;AAElC,MAAM,uBAAuB,GAAsB;IACjD,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,0BAA0B;IACjC,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,KAAK;IACrB,UAAU,EAAE,MAAM;IAClB,eAAe,EAAE,KAAK;IACtB,oBAAoB,EAAE,CAAC;CACxB,CAAC;AAEF,mEAAmE;AACnE,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAEhD;;;;GAIG;AACH,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0CAsDkB,CAAC;AAE3C,4CAA4C;AAE5C;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,aAA6E;IAC3F,aAAa,GAAG,kBAAkB,CAAC;IACnC,UAAU,GAAG,kBAAkB,CAAC;IAChC,cAAc,GAAG,iBAAiB,CAAC;IACnC,SAAS,GAAG,iBAAiB,CAAC;IAC9B,gBAAgB,GAAG,eAAe,CAAC;IAEtD,YAAY,SAAiB,EAAE,SAAqC,EAAE;QACpE,KAAK,CAAC,SAAS,EAAE,uBAAuB,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IAES,WAAW,CAAC,cAAsB;QAC1C,OAAO,eAAe,CAAC,OAAO,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;IACtE,CAAC;IAES,YAAY,CAAC,MAAc;QACnC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAwB,CAAC;QAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,kBAAkB,OAAO,EAAE,CAAC;QAElF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAChC,CAAC;IAES,kBAAkB;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IAES,kBAAkB;QAC1B,OAAO,SAAS,CAAC;IACnB,CAAC;IAES,eAAe;QACvB,OAAO,OAAO,CAAC;IACjB,CAAC;IAES,iBAAiB,CAAC,SAAiB,EAAE,UAAkB;QAC/D,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;IACrD,CAAC;IAES,YAAY,CAAC,OAAuB,EAAE,SAAiB,EAAE,UAAkB;QACnF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;IAC5C,CAAC;CACF"}
|