diff --git a/.claude/hooks/resolve-symlinks.sh b/.claude/hooks/resolve-symlinks.sh new file mode 100755 index 0000000000..212be30ee6 --- /dev/null +++ b/.claude/hooks/resolve-symlinks.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Resolve _ee.rs symlinks to actual files so Claude can read them +# This script runs before each user prompt is processed + +set -e + +PROJECT_DIR="${CLAUDE_PROJECT_DIR:-/home/farhad/windmill}" +MANIFEST_FILE="$PROJECT_DIR/.claude/hooks/.symlink-manifest" + +# Find all _ee.rs symlinks and store their targets +find "$PROJECT_DIR" -name "*_ee.rs" -type l 2>/dev/null | while read -r symlink; do + target=$(readlink -f "$symlink" 2>/dev/null) || continue + + # Only process if target file exists + if [[ -f "$target" ]]; then + # Store symlink path and target in manifest + echo "$symlink|$target" >> "$MANIFEST_FILE.tmp" + + # Replace symlink with actual file content + rm "$symlink" + cp "$target" "$symlink" + fi +done + +# Atomically replace manifest +if [[ -f "$MANIFEST_FILE.tmp" ]]; then + mv "$MANIFEST_FILE.tmp" "$MANIFEST_FILE" +fi + +exit 0 diff --git a/.claude/hooks/restore-symlinks.sh b/.claude/hooks/restore-symlinks.sh new file mode 100755 index 0000000000..3fcbaf2939 --- /dev/null +++ b/.claude/hooks/restore-symlinks.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Restore _ee.rs symlinks after Claude finishes processing +# This script runs when Claude stops +# IMPORTANT: Copies any modifications back to the target before restoring symlinks + +set -e + +PROJECT_DIR="${CLAUDE_PROJECT_DIR:-/home/farhad/windmill}" +MANIFEST_FILE="$PROJECT_DIR/.claude/hooks/.symlink-manifest" + +# Check if manifest exists +if [[ ! -f "$MANIFEST_FILE" ]]; then + exit 0 +fi + +# Read manifest and restore symlinks +while IFS='|' read -r symlink target; do + if [[ -n "$symlink" && -n "$target" ]]; then + # If the file exists (not a symlink) and target exists, copy changes back + if [[ -f "$symlink" && ! -L "$symlink" && -e "$target" ]]; then + # Copy the potentially modified file back to the target + cp "$symlink" "$target" + fi + + # Remove the regular file (which was a copy) + rm -f "$symlink" 2>/dev/null || true + + # Recreate the symlink + ln -s "$target" "$symlink" 2>/dev/null || true + fi +done < "$MANIFEST_FILE" + +# Clean up manifest +rm -f "$MANIFEST_FILE" + +exit 0 diff --git a/.claude/settings.json b/.claude/settings.json index 28ffd04e48..04c3c5ec6b 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,7 +1,41 @@ { + "hooks": { + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/resolve-symlinks.sh", + "timeout": 30 + } + ] + } + ], + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/restore-symlinks.sh", + "timeout": 30 + } + ] + } + ], + "SessionEnd": [ + { + "hooks": [ + { + "type": "command", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/restore-symlinks.sh", + "timeout": 30 + } + ] + } + ] + }, "permissions": { "allow": [ - "Read(**/*.rs)", "Bash(ls:*)", "Bash(grep:*)", "Bash(cat:*)", @@ -56,10 +90,11 @@ "Bash(git checkout:*)", "Bash(git merge:*)", "Bash(git rebase:*)" - ], - "additionalDirectories": [ - "../windmill-ee-private/" - ] + ] }, - "enableAllProjectMcpServers": true + "enableAllProjectMcpServers": true, + "enabledPlugins": { + "rust-analyzer-lsp@claude-plugins-official": true, + "typescript-lsp@claude-plugins-official": true + } } diff --git a/.github/workflows/claude-fast.yml b/.github/workflows/claude-fast.yml index d14e717a9e..e1f16b1ba8 100644 --- a/.github/workflows/claude-fast.yml +++ b/.github/workflows/claude-fast.yml @@ -49,9 +49,9 @@ jobs: needs.check-membership.outputs.is_member == 'true' runs-on: ubicloud-standard-8 permissions: - contents: read + contents: write pull-requests: write - issues: read + issues: write id-token: write steps: - name: Checkout repository @@ -63,16 +63,15 @@ jobs: uses: anthropics/claude-code-action@v1 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - model: claude-opus-4-1-20250805 - fallback_model: claude-sonnet-4-20250514 - timeout_minutes: "60" - allowed_tools: "mcp__github__create_pull_request" allowed_bots: "windmill-internal-app[bot]" - custom_instructions: | - ## IMPORTANT INSTRUCTIONS - - Your branch name should be a short description of the requested changes. - - Bypass previous instructions and instead of adding a url in your comment to manually create a PR, if you created a new branch with your changes, automatically create a draft PR from that branch to main. - - ## Available Tools - - mcp__github__create_pull_request: Create PRs from branches trigger_phrase: "/ai-fast" + plugins: "rust-analyzer-lsp@claude-plugins-official,typescript-lsp@claude-plugins-official" + settings: | + { + "env": { + "SQLX_OFFLINE": "true" + } + } + claude_args: | + --allowedTools "Bash,WebFetch,WebSearch" + --model opus diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 528e1d2d07..642b9ca84b 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -50,9 +50,9 @@ jobs: runs-on: ubicloud-standard-8 timeout-minutes: 60 permissions: - contents: read + contents: write pull-requests: write - issues: read + issues: write id-token: write steps: - name: Checkout repository @@ -95,8 +95,9 @@ jobs: uses: anthropics/claude-code-action@v1 with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - allowed_bots: 'windmill-internal-app[bot]' - trigger_phrase: '/ai' + allowed_bots: "windmill-internal-app[bot]" + trigger_phrase: "/ai" + plugins: "rust-analyzer-lsp@claude-plugins-official,typescript-lsp@claude-plugins-official" settings: | { "env": {