Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 16 additions & 19 deletions scripts/suggest-optimize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,14 @@ fi

# Determine which language families actually had changes
HAS_PYTHON_CHANGES="false"
HAS_JAVA_CHANGES="false"
HAS_JS_CHANGES="false"
if echo "$CHANGED_COMMITS" | grep -qE '\.py$'; then
HAS_PYTHON_CHANGES="true"
fi
if echo "$CHANGED_COMMITS" | grep -qE '\.java$'; then
HAS_JAVA_CHANGES="true"
fi
if echo "$CHANGED_COMMITS" | grep -qE '\.(js|ts|jsx|tsx)$'; then
HAS_JS_CHANGES="true"
fi
Expand Down Expand Up @@ -136,7 +140,7 @@ detect_project() {
PROJECT_CONFIGURED="false"
local search_dir="$PWD"
while true; do
# Check codeflash.toml first (Java projects)
# Check codeflash.toml first (configured Java projects)
if [ -f "$search_dir/codeflash.toml" ]; then
PROJECT_TYPE="java"
PROJECT_DIR="$search_dir"
Expand All @@ -146,6 +150,14 @@ detect_project() {
fi
break
fi
# Detect unconfigured Java projects by build file (pom.xml / build.gradle)
if [ -f "$search_dir/pom.xml" ] || [ -f "$search_dir/build.gradle" ] || [ -f "$search_dir/build.gradle.kts" ]; then
PROJECT_TYPE="java"
PROJECT_DIR="$search_dir"
PROJECT_CONFIG_PATH=""
# No codeflash.toml yet — PROJECT_CONFIGURED stays false
break
fi
if [ -f "$search_dir/pyproject.toml" ]; then
PROJECT_TYPE="python"
PROJECT_DIR="$search_dir"
Expand Down Expand Up @@ -282,7 +294,7 @@ Then, add \`Bash(*codeflash*)\` to the \`permissions.allow\` array in \`$SETTING
fi

# --- Java project path -----------------------------------------------------
if [ "$PROJECT_TYPE" = "java" ]; then
if [ "$PROJECT_TYPE" = "java" ] && [ "$HAS_JAVA_CHANGES" = "true" ]; then
INSTALL_CMD="pip install codeflash"

# Find codeflash binary: check PATH first, then uv run (Java projects don't use venvs)
Expand Down Expand Up @@ -316,23 +328,8 @@ Then, add \`Bash(*codeflash*)\` to the \`permissions.allow\` array in \`$SETTING
exit 0
fi

# Not configured
if [ "$PROJECT_CONFIGURED" != "true" ]; then
SETUP_MSG="Java files were changed in a recent commit, but codeflash is not configured yet.

Run \`${CODEFLASH_BIN} init --yes\` in \`${CHECK_DIR}\` to automatically detect and configure the Java project, then run \`${CODEFLASH_BIN} --subagent\` as a background Bash task (run_in_background: true) to optimize the code for performance."

if [ "$CODEFLASH_AUTO_ALLOWED" != "true" ]; then
SETUP_MSG="${SETUP_MSG}

Then, add \`Bash(*codeflash*)\` to the \`permissions.allow\` array in \`$SETTINGS_JSON\` (create the file and \`.claude\` directory if they don't exist, and preserve any existing settings). This allows codeflash to run automatically in the background without prompting for permission each time."
fi

jq -nc --arg reason "$SETUP_MSG" '{"decision": "block", "reason": $reason, "systemMessage": $reason}'
exit 0
fi

# Everything ready — build codeflash command for Java
# Java CLI auto-detects config from pom.xml/build.gradle — no pre-configuration needed.
# Just run codeflash --subagent and it handles everything.
if [ -n "$PROJECT_DIR" ] && [ "$PROJECT_DIR" != "$PWD" ]; then
RUN_CMD="cd $PROJECT_DIR && $CODEFLASH_BIN --subagent"
else
Expand Down
74 changes: 74 additions & 0 deletions tests/helpers/setup.bash
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ add_ts_commit() {
git -C "$REPO" commit -m "add $file" >/dev/null 2>&1
}

add_java_commit() {
local file="${1:-App.java}"
mkdir -p "$REPO/$(dirname "$file")"
echo "public class App { public static void main(String[] args) {} }" > "$REPO/$file"
git -C "$REPO" add -A >/dev/null 2>&1
local ts
ts=$(future_timestamp)
GIT_COMMITTER_DATE="@$ts" GIT_AUTHOR_DATE="@$ts" \
git -C "$REPO" commit -m "add $file" >/dev/null 2>&1
}

add_irrelevant_commit() {
local file="${1:-data.txt}"
echo "some data" > "$REPO/$file"
Expand Down Expand Up @@ -173,6 +184,69 @@ EOF
git -C "$REPO" commit -m "add package.json" --allow-empty >/dev/null 2>&1
}

# Create pom.xml (Maven project marker).
create_pom_xml() {
cat > "$REPO/pom.xml" << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>test-project</artifactId>
<version>1.0</version>
</project>
EOF
git -C "$REPO" add -A >/dev/null 2>&1
git -C "$REPO" commit -m "add pom.xml" --allow-empty >/dev/null 2>&1
}

# Create build.gradle (Gradle project marker).
create_build_gradle() {
cat > "$REPO/build.gradle" << 'EOF'
plugins { id 'java' }
EOF
git -C "$REPO" add -A >/dev/null 2>&1
git -C "$REPO" commit -m "add build.gradle" --allow-empty >/dev/null 2>&1
}

# Create codeflash.toml. configured=true adds [tool.codeflash].
create_codeflash_toml() {
local configured="${1:-true}"
if [ "$configured" = "true" ]; then
cat > "$REPO/codeflash.toml" << 'EOF'
[tool.codeflash]
module-root = "src/main/java"
tests-root = "src/test/java"
ignore-paths = []
formatter-cmds = ["disabled"]
EOF
else
cat > "$REPO/codeflash.toml" << 'EOF'
# empty codeflash config
EOF
fi
git -C "$REPO" add -A >/dev/null 2>&1
git -C "$REPO" commit -m "add codeflash.toml" --allow-empty >/dev/null 2>&1
}

# Create a mock codeflash binary on PATH (for Java projects that don't use venvs).
# Usage: setup_mock_codeflash [installed=true]
setup_mock_codeflash() {
local installed="${1:-true}"
mkdir -p "$MOCK_BIN"

if [ "$installed" = "true" ]; then
cat > "$MOCK_BIN/codeflash" << 'MOCK'
#!/bin/bash
echo "codeflash 0.1.0"
exit 0
MOCK
else
rm -f "$MOCK_BIN/codeflash"
return
fi
chmod +x "$MOCK_BIN/codeflash"
}

# Create .claude/settings.json with Bash(*codeflash*) auto-allowed
create_auto_allow() {
mkdir -p "$REPO/.claude"
Expand Down
109 changes: 109 additions & 0 deletions tests/test_suggest_optimize.bats
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,115 @@ setup() {
assert_reason_contains "npx codeflash --subagent"
}

# ═══════════════════════════════════════════════════════════════════════════════
# Java projects
# ═══════════════════════════════════════════════════════════════════════════════

# Setup: pom.xml exists (Maven project). Mock codeflash binary on PATH.
# One .java file committed after session start. No codeflash.toml.
# Validates: Fresh Java project detected via pom.xml (not codeflash.toml).
# The CLI auto-configures on first run, so the hook should just
# tell Claude to run codeflash --subagent directly.
# Expected: Block with reason containing "codeflash --subagent" and
# "run_in_background".
@test "java: pom.xml + codeflash installed → run codeflash" {
add_java_commit
create_pom_xml
setup_mock_codeflash true

run run_hook false "PATH=$MOCK_BIN:$PATH"
assert_block
assert_reason_contains "codeflash --subagent"
assert_reason_contains "run_in_background"
}

# Setup: pom.xml exists. No codeflash binary on PATH, uv mocked to fail.
# One .java commit.
# Validates: When codeflash is not installed, the hook should prompt
# the user to install it before optimization can run.
# Expected: Block with reason containing "pip install codeflash".
@test "java: pom.xml + NOT installed → install prompt" {
add_java_commit
create_pom_xml
# Use a minimal PATH that excludes codeflash and real uv.
# Mock uv to fail so it doesn't find a global codeflash.
cat > "$MOCK_BIN/uv" << 'MOCK'
#!/bin/bash
exit 1
MOCK
chmod +x "$MOCK_BIN/uv"

run run_hook false "PATH=$MOCK_BIN:/usr/bin:/bin"
assert_block
assert_reason_contains "pip install codeflash"
}

# Setup: codeflash.toml with [tool.codeflash] section (already configured).
# Mock codeflash binary on PATH. One .java commit.
# Validates: Projects that already have codeflash.toml (from a previous
# codeflash run or manual init) are detected and handled correctly.
# Expected: Block with reason containing "codeflash --subagent".
@test "java: codeflash.toml configured + installed → run codeflash" {
add_java_commit
create_codeflash_toml true
setup_mock_codeflash true

run run_hook false "PATH=$MOCK_BIN:$PATH"
assert_block
assert_reason_contains "codeflash --subagent"
}

# Setup: build.gradle exists (Gradle project). Mock codeflash on PATH.
# One .java commit.
# Validates: Gradle projects are detected via build.gradle, same as Maven
# projects are detected via pom.xml.
# Expected: Block with reason containing "codeflash --subagent".
@test "java: build.gradle + installed → run codeflash" {
add_java_commit
create_build_gradle
setup_mock_codeflash true

run run_hook false "PATH=$MOCK_BIN:$PATH"
assert_block
assert_reason_contains "codeflash --subagent"
}

# Setup: pom.xml exists. Mock codeflash on PATH. Commit only touches .py
# file (no .java files).
# Validates: The HAS_JAVA_CHANGES guard prevents the Java path from firing
# when no .java files were actually committed. Even though
# detect_project() finds pom.xml and sets PROJECT_TYPE=java,
# the guard skips the Java path. The Python fallback path may still
# fire (it checks HAS_PYTHON_CHANGES, not PROJECT_TYPE).
# Expected: Block does NOT contain "Java" — the Java path was correctly skipped.
@test "java: pom.xml but only .py committed → no Java trigger" {
add_python_commit
create_pom_xml

run run_hook false "PATH=$MOCK_BIN:$PATH"
assert_block
assert_reason_not_contains "Java"
}

# Setup: Both pom.xml and pyproject.toml exist. Mock codeflash on PATH.
# Commit touches .java file only.
# Validates: When both Java and Python markers exist, pom.xml is checked
# before pyproject.toml in detect_project(). With only .java
# changes, the Java path fires (not Python).
# Expected: Block with "codeflash --subagent" and NOT "npx" or "venv".
@test "java: pom.xml takes precedence over pyproject.toml" {
add_java_commit
create_pom_xml
create_pyproject true
setup_mock_codeflash true

run run_hook false "PATH=$MOCK_BIN:$PATH"
assert_block
assert_reason_contains "codeflash --subagent"
assert_reason_not_contains "npx"
assert_reason_not_contains "virtual environment"
}

# ═══════════════════════════════════════════════════════════════════════════════
# Permissions — auto-allow instructions
# ═══════════════════════════════════════════════════════════════════════════════
Expand Down
Loading