diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index 477cc6ab3..7887c3146 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -116,6 +116,16 @@ copy_smoke_binary() { cp "$BINARY" "$destination" } +# A fixture home that belongs to a Claude Code user. An empty ~/.claude is not +# evidence of one (#1180: other tools create that directory, and install then +# configured a client the user never had); the user config Claude Code itself +# writes is. Same seed as test_mark_claude_code_installed in tests/test_cli.c. +mark_claude_code_installed() { + local home="$1" + mkdir -p "$home/.claude" + echo '{}' > "$home/.claude.json" +} + # Retire this run's private account daemon (if one is running) and wait until # it reports not-running. Install/uninstall flows leave an ephemeral daemon # draining asynchronously whose mapped generation backing and open logs @@ -2149,10 +2159,10 @@ if ! grep -q '^name: codebase-memory$' "$HERMES_HOME_DIR/skills/codebase-memory/ exit 1 fi echo "OK 8x-i: Hermes durable delegation skill" -if ! grep -q '^hooks:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null || - ! grep -q '^ pre_llm_call:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null || - ! grep -q 'hook-augment' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null || - ! grep -q -- '--dialect hermes' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null; then +if ! grep -q '^hooks:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null || + ! grep -q '^ pre_llm_call:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null || + ! grep -q 'hook-augment' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null || + ! grep -q -- '--dialect hermes' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null; then echo "FAIL 8x-ii: Hermes pre_llm_call context hook missing" exit 1 fi @@ -3154,8 +3164,8 @@ fi echo "OK 9l: JSON agents, lifecycle hooks, and Kilo cleaned; foreign settings preserved" # 9m: YAML/TOML new agents are cleaned. -if grep -q '^ codebase-memory-mcp:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null || - grep -q '^ pre_llm_call:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null || +if grep -q '^ codebase-memory-mcp:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null || + grep -q '^ pre_llm_call:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null || grep -q '^ codebase-memory-mcp:' "$GOOSE_CFG" 2>/dev/null || grep -q 'codebase-memory-mcp' "$FAKE_HOME/.grok/config.toml" 2>/dev/null || grep -q '^name = "codebase-memory-mcp"' "$FAKE_HOME/.vibe/config.toml" 2>/dev/null; then @@ -3209,7 +3219,7 @@ echo "OK 9m-i: durable instruction blocks removed" # 9n: Skills removed (consolidated skill dir) if [ -d "$FAKE_HOME/.claude/skills/codebase-memory" ] || - [ -d "$FAKE_HOME/.hermes/skills/codebase-memory" ] || + [ -d "$HERMES_HOME_DIR/skills/codebase-memory" ] || [ -d "$FAKE_HOME/.agents/skills/codebase-memory" ] || [ -d "$FAKE_HOME/.qoder/skills/codebase-memory" ] || [ -d "$CUSTOM_KIMI_HOME/skills/codebase-memory" ] || @@ -3287,9 +3297,40 @@ echo "OK 9b-1: install with minimal agents exits cleanly" retire_account_daemon "9b-1-cleanup" smoke_rmtree "$EMPTY_HOME" +# 9b-1b (#1180): an empty ~/.claude directory is not a Claude Code install. +# The empty home above is this venue's control: where it showed no Claude Code, +# the same home plus a bare ~/.claude must not be configured for it either. +# +# SKIP_WHITELIST: a venue with a claude executable (a developer machine) detects +# Claude Code in every home, so there is nothing for the bare directory to +# change. Narrowing PATH for this one install does not make such a venue +# claude-free: with HOME as the scanned home the product also probes fixed +# locations (/usr/local/bin, /opt/homebrew/bin). A venue without one, such as +# the Linux test image, runs the check. +if grep -q 'Detected agents:.* Claude-Code' <<<"$INSTALL_OUT"; then + echo "SKIP 9b-1b: a claude executable is installed in this venue (whitelisted above)" +else + BARE_HOME=$(smoke_mktemp_dir) + mkdir -p "$BARE_HOME/.claude" "$BARE_HOME/.local/bin" + BARE_RC=0 + BARE_OUT=$(HOME="$BARE_HOME" LOCALAPPDATA="$BARE_HOME/AppData/Local" "$BINARY" install -y 2>&1) || BARE_RC=$? + if [ "$BARE_RC" -ge 128 ] || + ! grep -q 'Detected agents:' <<<"$BARE_OUT" || + grep -q 'Detected agents:.* Claude-Code' <<<"$BARE_OUT" || + [ -e "$BARE_HOME/.claude.json" ] || + [ -n "$(ls -A "$BARE_HOME/.claude" 2>/dev/null)" ]; then + echo "FAIL 9b-1b: a bare ~/.claude directory was configured as a Claude Code install" + exit 1 + fi + echo "OK 9b-1b: a bare ~/.claude directory is not configured as Claude Code" + retire_account_daemon "9b-1b-cleanup" + smoke_rmtree "$BARE_HOME" +fi + # 9b-2: Install twice (idempotent) IDEM_HOME=$(smoke_mktemp_dir) -mkdir -p "$IDEM_HOME/.claude" "$IDEM_HOME/.local/bin" +mkdir -p "$IDEM_HOME/.local/bin" +mark_claude_code_installed "$IDEM_HOME" copy_smoke_binary "$IDEM_HOME/.local/bin/codebase-memory-mcp" run_no_crash 9b-2 env HOME="$IDEM_HOME" LOCALAPPDATA="$IDEM_HOME/AppData/Local" "$BINARY" install -y IDEM_INSTALLER="$BINARY" @@ -3313,7 +3354,8 @@ smoke_rmtree "$IDEM_HOME" # 9b-3: Uninstall without prior install CLEAN_HOME=$(smoke_mktemp_dir) -mkdir -p "$CLEAN_HOME/.claude" "$CLEAN_HOME/.local/bin" +mkdir -p "$CLEAN_HOME/.local/bin" +mark_claude_code_installed "$CLEAN_HOME" UNINSTALL_RC=0 UNINSTALL_OUT=$(HOME="$CLEAN_HOME" "$BINARY" uninstall -y -n 2>&1) || UNINSTALL_RC=$? if [ "$UNINSTALL_RC" -ge 128 ]; then @@ -3337,7 +3379,8 @@ smoke_rmtree "$CORRUPT_HOME" # 9b-8: Double uninstall DBL_HOME=$(smoke_mktemp_dir) -mkdir -p "$DBL_HOME/.claude" "$DBL_HOME/.local/bin" +mkdir -p "$DBL_HOME/.local/bin" +mark_claude_code_installed "$DBL_HOME" copy_smoke_binary "$DBL_HOME/.local/bin/codebase-memory-mcp" run_no_crash 9b-8-install env HOME="$DBL_HOME" "$BINARY" install -y DBL_UNINSTALLER="$BINARY" @@ -3834,7 +3877,7 @@ if [ "$DL_OS" != "windows" ] && [ -f "$REPO_ROOT/install.sh" ]; then echo "--- Phase 13: install.sh E2E ---" INSTALL_TEST_HOME=$(smoke_mktemp_dir) INSTALL_TEST_DIR=$(smoke_mktemp_dir) - mkdir -p "$INSTALL_TEST_HOME/.claude" + mark_claude_code_installed "$INSTALL_TEST_HOME" mkdir -p "$INSTALL_TEST_HOME/.local/bin" # 13a: run install.sh with local URL + isolated HOME @@ -3871,7 +3914,8 @@ if [ "$DL_OS" != "windows" ] && [ -f "$REPO_ROOT/install.sh" ]; then echo "OK 13d: no signing needed ($DL_OS)" fi - # 13e: agent configs created (at least Claude Code since we made ~/.claude) + # 13e: agent configs created (at least Claude Code: the fixture home is a + # Claude Code user's) if [ -f "$INSTALL_TEST_HOME/.claude.json" ] && grep -q 'codebase-memory-mcp' "$INSTALL_TEST_HOME/.claude.json" 2>/dev/null; then echo "OK 13e: agent configs created by install.sh" else @@ -3898,7 +3942,7 @@ elif [ -f "$REPO_ROOT/install.ps1" ] && command -v powershell.exe &>/dev/null; t echo "--- Phase 13: install.ps1 E2E (Windows) ---" PS1_TEST_HOME=$(smoke_mktemp_dir) PS1_TEST_DIR=$(smoke_mktemp_dir) - mkdir -p "$PS1_TEST_HOME/.claude" + mark_claude_code_installed "$PS1_TEST_HOME" # Convert MSYS paths to Windows paths for PowerShell if command -v cygpath &>/dev/null; then