mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-08 02:03:34 +08:00
fix: harden windows observer prompt handling
This commit is contained in:
@@ -169,6 +169,18 @@ Rules:
|
|||||||
- Examples of project patterns: use React functional components, follow Django REST framework conventions
|
- Examples of project patterns: use React functional components, follow Django REST framework conventions
|
||||||
PROMPT
|
PROMPT
|
||||||
|
|
||||||
|
# Read the prompt into memory before the Claude subprocess is spawned.
|
||||||
|
# On Windows/MSYS2, the mktemp path can differ from the shell's later path
|
||||||
|
# resolution, so relying on cat "$prompt_file" inside the claude invocation
|
||||||
|
# can fail even though the file was created successfully.
|
||||||
|
prompt_content="$(cat "$prompt_file" 2>/dev/null || true)"
|
||||||
|
rm -f "$prompt_file"
|
||||||
|
if [ -z "$prompt_content" ]; then
|
||||||
|
echo "[$(date)] Failed to load observer prompt content, skipping analysis" >> "$LOG_FILE"
|
||||||
|
rm -f "$analysis_file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
timeout_seconds="${ECC_OBSERVER_TIMEOUT_SECONDS:-120}"
|
timeout_seconds="${ECC_OBSERVER_TIMEOUT_SECONDS:-120}"
|
||||||
max_turns="${ECC_OBSERVER_MAX_TURNS:-20}"
|
max_turns="${ECC_OBSERVER_MAX_TURNS:-20}"
|
||||||
exit_code=0
|
exit_code=0
|
||||||
@@ -185,17 +197,16 @@ PROMPT
|
|||||||
|
|
||||||
# Ensure CWD is PROJECT_DIR so the relative analysis_relpath resolves correctly
|
# Ensure CWD is PROJECT_DIR so the relative analysis_relpath resolves correctly
|
||||||
# on all platforms, not just when the observer happens to be launched from the project root.
|
# on all platforms, not just when the observer happens to be launched from the project root.
|
||||||
cd "$PROJECT_DIR" || { echo "[$(date)] Failed to cd to PROJECT_DIR ($PROJECT_DIR), skipping analysis" >> "$LOG_FILE"; rm -f "$prompt_file" "$analysis_file"; return; }
|
cd "$PROJECT_DIR" || { echo "[$(date)] Failed to cd to PROJECT_DIR ($PROJECT_DIR), skipping analysis" >> "$LOG_FILE"; rm -f "$analysis_file"; return; }
|
||||||
|
|
||||||
# Prevent observe.sh from recording this automated Haiku session as observations.
|
# Prevent observe.sh from recording this automated Haiku session as observations.
|
||||||
# Pass prompt via -p flag instead of stdin redirect for Windows compatibility (#842).
|
# Pass prompt via -p flag instead of stdin redirect for Windows compatibility (#842).
|
||||||
|
# prompt_content is already loaded in-memory so this no longer depends on the
|
||||||
|
# mktemp absolute path continuing to resolve after cwd changes (#1296).
|
||||||
ECC_SKIP_OBSERVE=1 ECC_HOOK_PROFILE=minimal claude --model haiku --max-turns "$max_turns" --print \
|
ECC_SKIP_OBSERVE=1 ECC_HOOK_PROFILE=minimal claude --model haiku --max-turns "$max_turns" --print \
|
||||||
--allowedTools "Read,Write" \
|
--allowedTools "Read,Write" \
|
||||||
-p "$(cat "$prompt_file")" >> "$LOG_FILE" 2>&1 &
|
-p "$prompt_content" >> "$LOG_FILE" 2>&1 &
|
||||||
claude_pid=$!
|
claude_pid=$!
|
||||||
# prompt_file content was already expanded by the shell; remove early to avoid
|
|
||||||
# leaving stale temp files during the (potentially long) analysis window.
|
|
||||||
rm -f "$prompt_file"
|
|
||||||
|
|
||||||
(
|
(
|
||||||
sleep "$timeout_seconds"
|
sleep "$timeout_seconds"
|
||||||
|
|||||||
@@ -2433,6 +2433,9 @@ async function runTests() {
|
|||||||
assert.ok(!observerLoopSource.includes('--max-turns 3'), 'observer-loop should not hardcode a 3-turn limit');
|
assert.ok(!observerLoopSource.includes('--max-turns 3'), 'observer-loop should not hardcode a 3-turn limit');
|
||||||
assert.ok(observerLoopSource.includes('ECC_SKIP_OBSERVE=1'), 'observer-loop should suppress observe.sh for automated sessions');
|
assert.ok(observerLoopSource.includes('ECC_SKIP_OBSERVE=1'), 'observer-loop should suppress observe.sh for automated sessions');
|
||||||
assert.ok(observerLoopSource.includes('ECC_HOOK_PROFILE=minimal'), 'observer-loop should run automated analysis with the minimal hook profile');
|
assert.ok(observerLoopSource.includes('ECC_HOOK_PROFILE=minimal'), 'observer-loop should run automated analysis with the minimal hook profile');
|
||||||
|
assert.ok(observerLoopSource.includes('prompt_content="$(cat "$prompt_file" 2>/dev/null || true)"'), 'observer-loop should read prompt_file into memory before claude is spawned');
|
||||||
|
assert.ok(observerLoopSource.includes('-p "$prompt_content"'), 'observer-loop should pass in-memory prompt content to claude');
|
||||||
|
assert.ok(!observerLoopSource.includes('-p "$(cat "$prompt_file")"'), 'observer-loop should not re-read prompt_file at invocation time');
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
passed++;
|
passed++;
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ test('default max analysis lines is 500', () => {
|
|||||||
test('analysis temp file is created and cleaned up', () => {
|
test('analysis temp file is created and cleaned up', () => {
|
||||||
const content = fs.readFileSync(observerLoopPath, 'utf8');
|
const content = fs.readFileSync(observerLoopPath, 'utf8');
|
||||||
assert.ok(content.includes('ecc-observer-analysis'), 'Should create a temp analysis file');
|
assert.ok(content.includes('ecc-observer-analysis'), 'Should create a temp analysis file');
|
||||||
assert.ok(content.includes('rm -f "$prompt_file" "$analysis_file"'), 'Should clean up both prompt and analysis temp files');
|
assert.ok(content.includes('rm -f "$prompt_file"'), 'Should clean up the prompt temp file after loading it');
|
||||||
|
assert.ok(content.includes('rm -f "$analysis_file"'), 'Should clean up the analysis temp file');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('observer-loop uses project-local temp directory for analysis artifacts', () => {
|
test('observer-loop uses project-local temp directory for analysis artifacts', () => {
|
||||||
@@ -174,6 +175,13 @@ test('observer-loop uses project-local temp directory for analysis artifacts', (
|
|||||||
assert.ok(content.includes('mktemp "${observer_tmp_dir}/ecc-observer-prompt.'), 'Prompt temp file should use the project temp dir');
|
assert.ok(content.includes('mktemp "${observer_tmp_dir}/ecc-observer-prompt.'), 'Prompt temp file should use the project temp dir');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('observer-loop loads prompt content before invoking claude', () => {
|
||||||
|
const content = fs.readFileSync(observerLoopPath, 'utf8');
|
||||||
|
assert.ok(content.includes('prompt_content="$(cat "$prompt_file" 2>/dev/null || true)"'), 'Prompt should be read into memory before the claude invocation');
|
||||||
|
assert.ok(content.includes('-p "$prompt_content"'), 'Claude should receive the in-memory prompt content');
|
||||||
|
assert.ok(!content.includes('-p "$(cat "$prompt_file")"'), 'Claude should not depend on re-reading the prompt file during invocation');
|
||||||
|
});
|
||||||
|
|
||||||
test('observer-loop prompt requires direct instinct writes without asking permission', () => {
|
test('observer-loop prompt requires direct instinct writes without asking permission', () => {
|
||||||
const content = fs.readFileSync(observerLoopPath, 'utf8');
|
const content = fs.readFileSync(observerLoopPath, 'utf8');
|
||||||
const heredocStart = content.indexOf('cat > "$prompt_file" <<PROMPT');
|
const heredocStart = content.indexOf('cat > "$prompt_file" <<PROMPT');
|
||||||
|
|||||||
Reference in New Issue
Block a user