diff options
| author | Harsh Aggarwal (NVIDIA) <haaggarwal@nvidia.com> | 2025-08-05 19:28:55 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-05 13:58:55 +0000 |
| commit | 2d775b54d2ab7772785c2196075d4c7c174407ab (patch) | |
| tree | a1204b30debeabe30e61a1cc7533d94eda95b904 | |
| parent | 9a16700e858fc0379e551ab72188eb63a54ad3f1 (diff) | |
Bring back hooks for auto formatting and ensure build works (#7811)
* Claude code : refactor and improve stability by using hooks
* format code (#27)
Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
---------
Co-authored-by: szihs <675653+szihs@users.noreply.github.com>
Co-authored-by: slangbot <ellieh+slangbot@nvidia.com>
Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
| -rwxr-xr-x | .claude/hooks/pre_tool_use.py | 58 | ||||
| -rwxr-xr-x | .claude/hooks/stop.py | 120 | ||||
| -rw-r--r-- | .claude/settings.json | 33 | ||||
| -rw-r--r-- | .github/actions/claude-code-runner/action.yml | 359 | ||||
| -rw-r--r-- | .github/workflows/claude.yml | 288 | ||||
| -rwxr-xr-x | CLAUDE.md | 81 |
6 files changed, 674 insertions, 265 deletions
diff --git a/.claude/hooks/pre_tool_use.py b/.claude/hooks/pre_tool_use.py new file mode 100755 index 000000000..59935076f --- /dev/null +++ b/.claude/hooks/pre_tool_use.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +import json +import subprocess +import sys +import os + + +def main(): + try: + # Read JSON input from stdin + input_data = json.load(sys.stdin) + + tool_name = input_data.get("tool_name", "") + tool_input = input_data.get("tool_input", {}) + + # Check if this is a Bash tool with git add/commit command + if tool_name == "Bash": + command = tool_input.get("command", "") + + # Check if command starts with git add or git commit + if command.strip().startswith(("git add", "git commit")): + print("Running formatter before git command...", file=sys.stderr) + + # Run the formatter + try: + result = subprocess.run( + [ + "./extras/formatting.sh", + "--no-version-check", + "--cpp", + "--since", + "master", + ], + capture_output=True, + text=True, + ) + + if result.returncode != 0: + print(f"Formatter warning: {result.stderr}", file=sys.stderr) + else: + print("Formatting completed successfully", file=sys.stderr) + + except Exception as e: + print(f"Formatter error: {e}", file=sys.stderr) + + sys.exit(0) + + except json.JSONDecodeError: + # Handle JSON decode errors gracefully + sys.exit(0) + except Exception: + # Handle any other errors gracefully + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.claude/hooks/stop.py b/.claude/hooks/stop.py new file mode 100755 index 000000000..2afd9e473 --- /dev/null +++ b/.claude/hooks/stop.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 + +import argparse +import json +import os +import sys + + +def parse_transcript_for_todos(transcript_path): + """Parse transcript to find the last TodoWrite and check if all todos are completed.""" + if not os.path.exists(transcript_path): + return True # If no transcript, assume OK to proceed + + try: + last_todo_write = None + + # Read .jsonl file and find the last TodoWrite + with open(transcript_path, "r") as f: + for line in f: + line = line.strip() + if line: + try: + entry = json.loads(line) + # Check if this is an assistant message with TodoWrite tool use + if ( + entry.get("type") == "assistant" + and "message" in entry + and "content" in entry["message"] + ): + content = entry["message"]["content"] + if isinstance(content, list): + for item in content: + if ( + isinstance(item, dict) + and item.get("type") == "tool_use" + and item.get("name") == "TodoWrite" + and "input" in item + and "todos" in item["input"] + ): + last_todo_write = item["input"]["todos"] + except json.JSONDecodeError: + continue # Skip invalid lines + + # If no TodoWrite found, assume OK to proceed + if not last_todo_write: + return True + + # Check if all todos are completed + incomplete_todos = [] + for todo in last_todo_write: + if todo.get("status") != "completed": + incomplete_todos.append(todo) + + return len(incomplete_todos) == 0, incomplete_todos + + except Exception: + # If any error occurs during parsing, assume OK to proceed + return True + + +def main(): + try: + # Parse command line arguments + parser = argparse.ArgumentParser() + parser.add_argument( + "--validate", + action="store_true", + help="Validate that all todos are completed before allowing stop", + ) + args = parser.parse_args() + + # Read JSON input from stdin + input_data = json.load(sys.stdin) + + # Extract required fields + session_id = input_data.get("session_id", "") + stop_hook_active = input_data.get("stop_hook_active", False) + + # Handle --validate switch + if args.validate and "transcript_path" in input_data: + transcript_path = input_data["transcript_path"] + validation_result = parse_transcript_for_todos(transcript_path) + + # Check if validation returned a tuple (incomplete todos found) + if isinstance(validation_result, tuple): + all_complete, incomplete_todos = validation_result + if not all_complete: + # Create a detailed message about incomplete todos + incomplete_items = [] + for todo in incomplete_todos: + status = todo.get("status", "unknown") + content = todo.get("content", "unknown task") + incomplete_items.append(f"- {content} ({status})") + + incomplete_list = "\n".join(incomplete_items) + reason = f"Tasks are not yet complete. Please finish the following todos:\n{incomplete_list}\n\nUse TodoWrite to mark tasks as completed when finished." + + # Return JSON decision to block stopping + output = {"decision": "block", "reason": reason} + print(json.dumps(output)) + sys.exit(0) + elif not validation_result: + # Single boolean returned as False + reason = "Tasks are not yet complete. Please finish all todos before stopping. Use TodoWrite to mark tasks as completed when finished." + output = {"decision": "block", "reason": reason} + print(json.dumps(output)) + sys.exit(0) + + sys.exit(0) + + except json.JSONDecodeError: + # Handle JSON decode errors gracefully + sys.exit(0) + except Exception: + # Handle any other errors gracefully + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.claude/settings.json b/.claude/settings.json index 5b6fa8b42..1201312ca 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,5 +1,38 @@ { "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "python3 $CLAUDE_PROJECT_DIR/.claude/hooks/pre_tool_use.py" + } + ] + } + ], + "SubagentStop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "python3 $CLAUDE_PROJECT_DIR/.claude/hooks/stop.py --validate" + } + ] + } + ], + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "python3 $CLAUDE_PROJECT_DIR/.claude/hooks/stop.py --validate" + } + ] + } + ] }, "env": { "BASH_DEFAULT_TIMEOUT_MS": 1200000, diff --git a/.github/actions/claude-code-runner/action.yml b/.github/actions/claude-code-runner/action.yml new file mode 100644 index 000000000..f68a7914c --- /dev/null +++ b/.github/actions/claude-code-runner/action.yml @@ -0,0 +1,359 @@ +name: "Claude Code Runner" +description: "Complete Claude Code execution with authentication, setup, execution, and results handling" +inputs: + # Authentication inputs + llmgw-id: + description: "LLMGW ID for token generation" + required: true + llmgw-secret: + description: "LLMGW secret for token generation" + required: true + llmgw-token-url: + description: "LLMGW token URL for authentication" + required: true + github-token-fallback: + description: "Fallback GitHub token if App token fails" + required: false + default: "" + + # Claude configuration + model: + description: "Anthropic model to use" + required: false + default: "claude-3-5-sonnet-20241022" + max-turns: + description: "Maximum number of turns for Claude" + required: false + default: "50000" + timeout-minutes: + description: "Timeout for Claude action in minutes" + required: false + default: "600" + trigger-phrase: + description: "Trigger phrase to activate Claude" + required: false + default: "@claude" + assignee-trigger: + description: "Assignee trigger name" + required: false + default: "claude" + + # Environment and setup + custom-instructions: + description: "Custom instructions for Claude" + required: true + mcp-config: + description: "MCP server configuration JSON" + required: false + default: "" + allowed-tools: + description: "Comma-separated list of allowed tools" + required: false + default: "Bash,View,GlobTool,GrepTool,BatchTool,Write" + setup-commands: + description: "Setup commands to run before Claude (multiline string)" + required: false + default: "" + continue-on-setup-error: + description: "Continue if setup commands fail" + required: false + default: "false" + + # AWS/Bedrock configuration + use-bedrock: + description: "Use AWS Bedrock for Claude" + required: false + default: "true" + aws-region: + description: "AWS region" + required: false + default: "" + bedrock-base-url: + description: "Anthropic Bedrock base URL" + required: false + default: "" + small-fast-model: + description: "Small fast model for Anthropic" + required: false + default: "" + +outputs: + auth-token: + description: "Generated authentication token" + value: ${{ steps.auth-token.outputs.token }} + github-token: + description: "Final GitHub token (App or fallback)" + value: ${{ steps.auth-config.outputs.github-token }} + token-expires: + description: "Token expiration time (if available)" + value: ${{ steps.auth-token.outputs.token-expires }} + github-app-token-outcome: + description: "Outcome of GitHub App token generation" + value: ${{ steps.github-app-token.outcome }} + claude-outcome: + description: "Outcome of Claude Code execution" + value: ${{ steps.claude-action.outcome }} + +runs: + using: "composite" + steps: + # Validate environment and inputs + - name: Validate Environment + shell: bash + run: | + set -euo pipefail + + # Check required secrets + if [ -z "${{ inputs.llmgw-id }}" ] || [ -z "${{ inputs.llmgw-secret }}" ] || [ -z "${{ inputs.llmgw-token-url }}" ]; then + echo "::error::Missing required secrets: LLMGW_ID or LLMGW_SECRET or LLMGW_TOKEN_URL" + exit 1 + fi + + # Install required tools + command -v jq >/dev/null 2>&1 || { echo "::error::jq is required but not installed"; exit 1; } + command -v curl >/dev/null 2>&1 || { echo "::error::curl is required but not installed"; exit 1; } + + echo "โ
Environment validation passed" + + # Generate custom auth token + - name: Generate Custom Auth Token + id: auth-token + shell: bash + run: | + set -euo pipefail + + echo "๐ Generating authentication token..." + + # Set up error handling + cleanup() { + local exit_code=$? + echo "๐งน Cleaning up temporary files..." + rm -f /tmp/token_response.json 2>/dev/null || true + if [ $exit_code -ne 0 ]; then + echo "::error::Authentication failed - check your credentials and endpoint" + fi + exit $exit_code + } + trap cleanup EXIT + + # Generate token with comprehensive error handling (using Basic auth like original) + HTTP_CODE=$(curl -s -w "%{http_code}" -o /tmp/token_response.json --fail-with-body \ + --max-time 30 \ + --retry 3 \ + --retry-delay 2 \ + --location "${{ inputs.llmgw-token-url }}" \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + --header "Authorization: Basic $(echo -n ${{ inputs.llmgw-id }}:${{ inputs.llmgw-secret }} | base64 -w0)" \ + --data-urlencode 'grant_type=client_credentials' \ + --data-urlencode 'scope=awsanthropic-readwrite azureopenai-readwrite' \ + 2>/dev/null) + + # Check HTTP response code + if [ "$HTTP_CODE" -ne 200 ]; then + echo "::error::Authentication failed with HTTP code: $HTTP_CODE" + if [ -f /tmp/token_response.json ]; then + echo "::error::Response: $(cat /tmp/token_response.json | head -c 200)" + fi + exit 1 + fi + + # Extract and validate token + if [ ! -f /tmp/token_response.json ]; then + echo "::error::No response file generated" + exit 1 + fi + + ANTHROPIC_AUTH_TOKEN=$(jq -r '.access_token // empty' /tmp/token_response.json 2>/dev/null) + + # Validate token format and length + if [ -z "$ANTHROPIC_AUTH_TOKEN" ] || [ "$ANTHROPIC_AUTH_TOKEN" = "null" ]; then + echo "::error::Failed to extract access token from response" + exit 1 + fi + + # Basic token validation + if [ ${#ANTHROPIC_AUTH_TOKEN} -lt 10 ]; then + echo "::error::Token appears to be too short (${#ANTHROPIC_AUTH_TOKEN} characters)" + exit 1 + fi + + # CRITICAL: Mask the token BEFORE any output + echo "::add-mask::$ANTHROPIC_AUTH_TOKEN" + + # Set outputs + echo "token=$ANTHROPIC_AUTH_TOKEN" >> $GITHUB_OUTPUT + + # Set token expiry if available + TOKEN_EXPIRES=$(jq -r '.expires_in // empty' /tmp/token_response.json 2>/dev/null) + if [ -n "$TOKEN_EXPIRES" ]; then + echo "::add-mask::$TOKEN_EXPIRES" + echo "token-expires=$TOKEN_EXPIRES" >> $GITHUB_OUTPUT + fi + + echo "โ
Authentication token generated and masked successfully" + + # Clean up response file + rm -f /tmp/token_response.json + + # Configure authentication + - name: Configure Authentication + id: auth-config + shell: bash + run: | + set -euo pipefail + + # Use GitHub App token if available, otherwise use GITHUB_TOKEN + if [ -n "${{ steps.github-app-token.outputs.token }}" ]; then + echo "github-token=${{ steps.github-app-token.outputs.token }}" >> $GITHUB_OUTPUT + echo "โ
Using GitHub App authentication" + else + echo "github-token=${{ inputs.github-token-fallback }}" >> $GITHUB_OUTPUT + echo "โ ๏ธ Using fallback GITHUB_TOKEN authentication" + fi + + # Run setup commands if provided + - name: Run setup commands + id: setup-commands + if: inputs.setup-commands != '' + shell: bash + continue-on-error: ${{ inputs.continue-on-setup-error == 'true' }} + run: ${{ inputs.setup-commands }} + + # Security cleanup + - name: Security Cleanup + if: always() + shell: bash + run: | + set -euo pipefail + + echo "๐งน Performing security cleanup..." + + # Clear any temporary files that might contain sensitive data + find /tmp -name "*token*" -type f -delete 2>/dev/null || true + find /tmp -name "*auth*" -type f -delete 2>/dev/null || true + find /tmp -name "*response*" -type f -delete 2>/dev/null || true + + # Clear environment variables (belt and suspenders approach) + unset ANTHROPIC_API_KEY 2>/dev/null || true + unset ANTHROPIC_AUTH_TOKEN 2>/dev/null || true + + echo "โ
Security cleanup completed" + + # Workflow summary + - name: Generate Workflow Summary + if: always() + shell: bash + run: | + echo "## Claude Code Runner Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Authentication" >> $GITHUB_STEP_SUMMARY + echo "- **Auth Token**: โ
Generated" >> $GITHUB_STEP_SUMMARY + echo "- **Token Expiry**: ${{ steps.auth-token.outputs.token-expires || 'Not provided' }}" >> $GITHUB_STEP_SUMMARY + echo "- **GitHub Token**: ${{ steps.github-app-token.outcome == 'success' && 'โ
GitHub App' || 'โ ๏ธ Fallback' }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Configuration" >> $GITHUB_STEP_SUMMARY + echo "- **Model**: ${{ inputs.model }}" >> $GITHUB_STEP_SUMMARY + echo "- **Max Turns**: ${{ inputs.max-turns }}" >> $GITHUB_STEP_SUMMARY + echo "- **Timeout**: ${{ inputs.timeout-minutes }} minutes" >> $GITHUB_STEP_SUMMARY + echo "- **Bedrock**: ${{ inputs.use-bedrock == 'true' && 'โ
Enabled' || 'โ Disabled' }}" >> $GITHUB_STEP_SUMMARY + if [ "${{ inputs.use-bedrock }}" = "true" ]; then + echo "- **AWS Region**: ${{ inputs.aws-region || 'Default' }}" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Setup" >> $GITHUB_STEP_SUMMARY + echo "- **Setup Commands**: ${{ inputs.setup-commands != '' && 'โ
Executed' || 'โญ๏ธ Skipped' }}" >> $GITHUB_STEP_SUMMARY + if [ "${{ inputs.setup-commands }}" != "" ]; then + echo "- **Setup Result**: ${{ steps.setup-commands.outcome || 'Unknown' }}" >> $GITHUB_STEP_SUMMARY + fi + echo "- **Security Cleanup**: โ
Completed" >> $GITHUB_STEP_SUMMARY + + # Execute Claude Code Action + - name: Execute Claude Code Action + id: claude-action + uses: anthropics/claude-code-action@beta + with: + custom_instructions: ${{ inputs.custom-instructions }} + mcp_config: ${{ inputs.mcp-config }} + allowed_tools: ${{ inputs.allowed-tools }} + trigger_phrase: ${{ inputs.trigger-phrase }} + assignee_trigger: ${{ inputs.assignee-trigger }} + timeout_minutes: ${{ inputs.timeout-minutes }} + github_token: ${{ steps.auth-config.outputs.github-token }} + use_bedrock: ${{ inputs.use-bedrock }} + model: ${{ inputs.model }} + max_turns: ${{ inputs.max-turns }} + # Use claude_env for custom environment variables + claude_env: | + ANTHROPIC_BEDROCK_BASE_URL: ${{ inputs.bedrock-base-url }} + ANTHROPIC_SMALL_FAST_MODEL: ${{ inputs.small-fast-model }} + AWS_REGION: ${{ inputs.aws-region }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_ACTOR: ${{ github.actor }} + ANTHROPIC_AUTH_TOKEN: ${{ steps.auth-token.outputs.token }} + DISABLE_TELEMETRY: 1 + continue-on-error: true + + # Handle Claude results + - name: Handle Claude Results + if: always() + shell: bash + run: | + set -euo pipefail + + echo "๐ Processing Claude action results..." + + # Check if Claude action succeeded + if [ "${{ steps.claude-action.outcome }}" = "success" ]; then + echo "โ
Claude Code action completed successfully" + elif [ "${{ steps.claude-action.outcome }}" = "failure" ]; then + echo "โ Claude Code action failed" + + # Create error summary + echo "" >> $GITHUB_STEP_SUMMARY + echo "### โ Claude Execution Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The Claude Code action encountered an error. Common causes:" >> $GITHUB_STEP_SUMMARY + echo "- Authentication issues" >> $GITHUB_STEP_SUMMARY + echo "- Network connectivity problems" >> $GITHUB_STEP_SUMMARY + echo "- Model availability issues" >> $GITHUB_STEP_SUMMARY + echo "- Rate limiting" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Please check the workflow logs for detailed error information." >> $GITHUB_STEP_SUMMARY + else + echo "โ ๏ธ Claude Code action was cancelled or skipped" + fi + + # Add execution summary + - name: Add Claude Execution Summary + if: always() + shell: bash + run: | + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Claude Execution Details" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Event information + echo "#### Event Information" >> $GITHUB_STEP_SUMMARY + echo "- **Trigger**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY + echo "- **Repository**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY + echo "- **Actor**: @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Execution status + echo "#### Execution Status" >> $GITHUB_STEP_SUMMARY + if [ "${{ steps.claude-action.outcome }}" = "success" ]; then + echo "- **Claude Action**: โ
Success" >> $GITHUB_STEP_SUMMARY + elif [ "${{ steps.claude-action.outcome }}" = "failure" ]; then + echo "- **Claude Action**: โ Failed" >> $GITHUB_STEP_SUMMARY + else + echo "- **Claude Action**: โ ๏ธ ${{ steps.claude-action.outcome }}" >> $GITHUB_STEP_SUMMARY + fi + echo "- **Model Used**: ${{ inputs.model }}" >> $GITHUB_STEP_SUMMARY + echo "- **Max Turns**: ${{ inputs.max-turns }}" >> $GITHUB_STEP_SUMMARY + echo "- **Workflow Status**: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY + + # Add timestamp + echo "" >> $GITHUB_STEP_SUMMARY + echo "#### Timestamp" >> $GITHUB_STEP_SUMMARY + echo "- **Completed at**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index c2e0fb68e..f4bb58610 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -44,173 +44,60 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - submodules: "recursive" - fetch-depth: 2 # fetch minimal history for git context + fetch-depth: 0 + submodules: false + + - name: Checkout of submodules + run: git submodule update --init --recursive --depth=1 - name: Format Setup and Environment Preparation uses: ./.github/actions/format-setup - - name: Setup Environment Dependencies - run: | - set -euo pipefail - echo "๐๏ธ Setting up environment dependencies..." - - sudo apt-get update - sudo apt-get install -y libx11-dev - - # Configure and build the project - cmake --preset default --fresh - cmake --workflow --preset debug - - echo "โ
Environment setup completed" - - # Validate environment and dependencies - - name: Validate Environment - run: | - set -euo pipefail - - # Check required secrets - if [ -z "${{ secrets.LLMGW_ID }}" ] || [ -z "${{ secrets.LLMGW_SECRET }}" ] || [ -z "${{ secrets.LLMGW_TOKEN_URL }}" ]; then - echo "::error::Missing required secrets: LLMGW_ID or LLMGW_SECRET or LLMGW_TOKEN_URL" - exit 1 - fi - - # Install required tools - command -v jq >/dev/null 2>&1 || { echo "::error::jq is required but not installed"; exit 1; } - command -v curl >/dev/null 2>&1 || { echo "::error::curl is required but not installed"; exit 1; } - - echo "โ
Environment validation passed" - - # Generate custom auth token and set as environment variable - - name: Generate Custom Auth Token - id: auth-token - env: - LLMGW_TOKEN_URL: ${{ secrets.LLMGW_TOKEN_URL }} - run: | - set -euo pipefail - - echo "๐ Generating authentication token..." - - # Set up error handling - cleanup() { - local exit_code=$? - echo "๐งน Cleaning up temporary files..." - rm -f /tmp/token_response.json 2>/dev/null || true - if [ $exit_code -ne 0 ]; then - echo "::error::Authentication failed - check your credentials and endpoint" - fi - exit $exit_code - } - trap cleanup EXIT - - # Generate token with comprehensive error handling - HTTP_CODE=$(curl -s -w "%{http_code}" -o /tmp/token_response.json --fail-with-body \ - --max-time 30 \ - --retry 3 \ - --retry-delay 2 \ - --location "${{ env.LLMGW_TOKEN_URL }}" \ - --header 'Content-Type: application/x-www-form-urlencoded' \ - --header "Authorization: Basic $(echo -n ${{ secrets.LLMGW_ID }}:${{ secrets.LLMGW_SECRET }} | base64 -w0)" \ - --data-urlencode 'grant_type=client_credentials' \ - --data-urlencode 'scope=awsanthropic-readwrite azureopenai-readwrite' \ - 2>/dev/null) - - # Check HTTP response code - if [ "$HTTP_CODE" -ne 200 ]; then - echo "::error::Authentication failed with HTTP code: $HTTP_CODE" - if [ -f /tmp/token_response.json ]; then - echo "::error::Response: $(cat /tmp/token_response.json | head -c 200)" - fi - exit 1 - fi - - # Extract and validate token - if [ ! -f /tmp/token_response.json ]; then - echo "::error::No response file generated" - exit 1 - fi - - ANTHROPIC_AUTH_TOKEN=$(jq -r '.access_token // empty' /tmp/token_response.json 2>/dev/null) - - # Validate token format and length - if [ -z "$ANTHROPIC_AUTH_TOKEN" ] || [ "$ANTHROPIC_AUTH_TOKEN" = "null" ]; then - echo "::error::Failed to extract access token from response" - exit 1 - fi - - # Basic token validation - if [ ${#ANTHROPIC_AUTH_TOKEN} -lt 10 ]; then - echo "::error::Token appears to be too short (${#ANTHROPIC_AUTH_TOKEN} characters)" - exit 1 - fi - - # CRITICAL: Mask the token BEFORE any output - echo "::add-mask::$ANTHROPIC_AUTH_TOKEN" - - # Set as environment variable for subsequent steps - echo "ANTHROPIC_AUTH_TOKEN=$ANTHROPIC_AUTH_TOKEN" >> $GITHUB_ENV - - # Set token expiry if available - TOKEN_EXPIRES=$(jq -r '.expires_in // empty' /tmp/token_response.json 2>/dev/null) - if [ -n "$TOKEN_EXPIRES" ]; then - echo "::add-mask::$TOKEN_EXPIRES" - echo "token-expires=$TOKEN_EXPIRES" >> $GITHUB_OUTPUT - fi - - echo "โ
Authentication token generated and masked successfully" + # Complete Claude execution with authentication, setup, and execution + - name: Run Claude Code + id: claude + uses: ./.github/actions/claude-code-runner + with: + # Authentication (these secrets must be configured in your repository) + llmgw-id: ${{ secrets.LLMGW_ID }} + llmgw-secret: ${{ secrets.LLMGW_SECRET }} + llmgw-token-url: ${{ secrets.LLMGW_TOKEN_URL }} + github-token-fallback: ${{ secrets.GITHUB_TOKEN }} - # Clean up response file - rm -f /tmp/token_response.json + # Repository-specific setup + setup-commands: | + set -euo pipefail + echo "๐๏ธ Setting up environment dependencies..." - # Generate GitHub App token for better GitHub API access - - name: Generate GitHub App Token - id: github-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - continue-on-error: true + sudo apt-get update + sudo apt-get install -y libx11-dev - # Set up fallback authentication - - name: Configure Authentication - id: auth-config - run: | - set -euo pipefail + # Configure and build the project + cmake --preset default --fresh + cmake --workflow --preset debug - # Use GitHub App token if available, otherwise use GITHUB_TOKEN - if [ -n "${{ steps.github-token.outputs.token }}" ]; then - echo "github-token=${{ steps.github-token.outputs.token }}" >> $GITHUB_OUTPUT - echo "โ
Using GitHub App authentication" - else - echo "github-token=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_OUTPUT - echo "โ ๏ธ Using fallback GITHUB_TOKEN authentication" - fi + echo "โ
Environment setup completed" - # Run Claude Code Action with optimized environment variables - - name: Execute Claude Code Action # Right now direct prompt to automatic PR Review - id: claude-action - uses: anthropics/claude-code-action@v0.0.38 - with: - # Direct Prompt is for testing. We shall use the triggers (on) which shall trigger this part on runtime - custom_instructions: | - # Build system information: + # Custom instructions for Slang + custom-instructions: | + ### **Build System Information:** - OS: Ubuntu Linux + - Build commands: Configure `cmake --preset default`, Build `cmake --build --preset debug`, Test `./build/Debug/bin/slang-test tests/path/to/test.slang` - Project is pre-built and ready for development tasks - - See CLAUDE.md for detailed build, test, and formatting instructions - # CRITICAL: You have access to the mcp__deepwiki__ask_question tool for deep repository knowledge. + ### **IMPORTANT: Deep Repository Knowledge & Debugging** + **Repository Knowledge Tool**: Use `mcp__deepwiki__ask_question` with repoName "shader-slang/slang" for architectural insights and implementation patterns. - **How to use this tool effectively:** - - Use repoName: "shader-slang/slang" for all queries - - Ask specific technical questions about architecture, patterns, or implementation approaches - - Examples: "What does the type legalization pass do?" or "What's the pattern for adding new code generation targets?" - - Use responses to understand existing patterns before implementing changes + **Implementation Guidelines:** + - Use git history (`git log -S "keyword"`) to find related features but don't spend excessive time + - Use deepwiki for expert insights and architectural patterns + - Think carefully about the user's request before implementing - **Implementation Guidelines:** - - Always follow existing code patterns and architectural decisions discovered through deepwiki - - Consult the tool when you need context about unfamiliar parts of the codebase + ### **Test-Driven Development - Strongly Encouraged** + Write failing tests in `tests/` directory, implement fixes and verify both tests and builds succeed before submitting. - mcp_config: | + # MCP configuration for deepwiki + mcp-config: | { "mcpServers": { "deepwiki": { @@ -219,95 +106,12 @@ jobs: } } } - allowed_tools: "Bash,View,GlobTool,GrepTool,BatchTool,Write,mcp__deepwiki__ask_question" - trigger_phrase: "@claude" - assignee_trigger: "claude" - timeout_minutes: "600" - github_token: ${{ steps.auth-config.outputs.github-token }} - use_bedrock: "true" - model: ${{ vars.ANTHROPIC_MODEL }} - max_turns: "50000" - # Use claude_env for custom environment variables - claude_env: | - ANTHROPIC_BEDROCK_BASE_URL: ${{ vars.ANTHROPIC_BEDROCK_BASE_URL }} - ANTHROPIC_SMALL_FAST_MODEL: ${{ vars.ANTHROPIC_SMALL_FAST_MODEL }} - AWS_REGION: ${{ vars.AWS_REGION }} - GITHUB_REPOSITORY: ${{ github.repository }} - GITHUB_EVENT_NAME: ${{ github.event_name }} - GITHUB_ACTOR: ${{ github.actor }} - ANTHROPIC_AUTH_TOKEN: ${{ env.ANTHROPIC_AUTH_TOKEN }} - DISABLE_TELEMETRY: 1 - - # The ANTHROPIC_API_KEY environment variable is automatically picked up - continue-on-error: true - - # Handle action results and errors - - name: Handle Action Results - if: always() - run: | - set -euo pipefail - - # Check if Claude action succeeded - if [ "${{ steps.claude-action.outcome }}" = "success" ]; then - echo "โ
Claude Code action completed successfully" - - # Optional: Add success comment to PR/issue - if [ -n "${{ github.event.issue.number || github.event.pull_request.number }}" ]; then - echo "Claude has successfully processed your request! ๐" >> /tmp/comment.md - echo "<!-- Claude-success -->" >> /tmp/comment.md - fi - - elif [ "${{ steps.claude-action.outcome }}" = "failure" ]; then - echo "โ Claude Code action failed" - # Create error comment for debugging - cat > /tmp/error_comment.md << 'EOF' - ## Claude Code Action Failed โ - - The Claude Code action encountered an error. This could be due to: - - Network connectivity issues - - Authentication problems - - Model availability issues - - Rate limiting - - Please check the workflow logs for more details and try again. - - <!-- Claude-error --> - EOF - - else - echo "โ ๏ธ Claude Code action was cancelled or skipped" - fi - - # Security cleanup - - name: Security Cleanup - if: always() - run: | - set -euo pipefail - - echo "๐งน Performing security cleanup..." - - # Clear any temporary files that might contain sensitive data - find /tmp -name "*token*" -type f -delete 2>/dev/null || true - find /tmp -name "*auth*" -type f -delete 2>/dev/null || true - find /tmp -name "*response*" -type f -delete 2>/dev/null || true - - # Clear environment variables (belt and suspenders approach) - unset ANTHROPIC_API_KEY 2>/dev/null || true - unset ANTHROPIC_AUTH_TOKEN 2>/dev/null || true - - echo "โ
Security cleanup completed" + # Advanced configuration (using repository variables) + model: ${{ vars.ANTHROPIC_MODEL }} + aws-region: ${{ vars.AWS_REGION }} + bedrock-base-url: ${{ vars.ANTHROPIC_BEDROCK_BASE_URL }} + small-fast-model: ${{ vars.ANTHROPIC_SMALL_FAST_MODEL }} - # Workflow summary - - name: Workflow Summary - if: always() - run: | - echo "## Claude Code Workflow Summary" >> $GITHUB_STEP_SUMMARY - echo "- **Trigger**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY - echo "- **Repository**: ${{ github.repository }}" >> $GITHUB_STEP_SUMMARY - echo "- **Actor**: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY - echo "- **Auth Token**: โ
Generated" >> $GITHUB_STEP_SUMMARY - echo "- **GitHub Token**: ${{ steps.github-token.outcome == 'success' && 'โ
GitHub App' || 'โ ๏ธ Fallback' }}" >> $GITHUB_STEP_SUMMARY - echo "- **Claude Action**: ${{ steps.claude-action.outcome == 'success' && 'โ
Success' || 'โ Failed' }}" >> $GITHUB_STEP_SUMMARY - echo "- **Model Used**: ${{ vars.ANTHROPIC_MODEL || 'default' }}" >> $GITHUB_STEP_SUMMARY - echo "- **Workflow Status**: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY + # Additional tools for deepwiki + allowed-tools: "Bash,View,GlobTool,GrepTool,BatchTool,Write,mcp__deepwiki__ask_question" @@ -2,15 +2,20 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +**Repository**: shader-slang/slang - A shading language for GPU programming +**Primary Language**: C++ with custom Slang language +**MCP Tool Available**: `mcp__deepwiki__ask_question` with repoName: "shader-slang/slang" + Reference other instruction files as well: -- `.github/copilot-instructions.md` -- `.github/workflows/claude.yml` + +- @.github/copilot-instructions.md ## Build System and Common Commands **IMPORTANT:** On Windows, always use `cmake.exe` (not `cmake`) to ensure proper GPU test execution. Using `cmake` without the `.exe` extension may invoke WSL's cmake, which cannot run GPU tests. ### Building the Project + ```bash # Configure with default settings (Ninja Multi-Config) cmake --preset default @@ -22,9 +27,19 @@ cmake.exe --preset vs2022 # It can take from 5 minutes to 20 minutes depending on the machine. cmake --build --preset debug # Debug binary cmake --build --preset release # Release binary + +# Build specific targets +cmake --build --preset debug --target slangc +cmake --build --preset debug --target slang-test ``` +### PR Workflow + +1. **Label your PR**: Use "pr: non-breaking" (default) or "pr: breaking" (for ABI/language breaking changes) +2. **Include tests**: Add regression tests as `.slang` files under `tests/` + ### Testing + slang-test must run from repository root ```bash @@ -39,39 +54,39 @@ slang-test must run from repository root ./build/Release/bin/slang-test slang-unit-test-tool/ ``` -Many of the Slang tests requires a GPU to run. However your local environment may not have a GPU. Prefer crafting the -test that runs on the CPU with `//TEST:COMPARE_COMPUTE(filecheck=CHECK): -cpu -output-using-type`, or with -`//TEST:INTERPRET(filecheck=CHECK):`. Prefer creating executable tests over compile-only tests, unless the test is to -check for a specific form of downstream code or diagnostics output. +**Writing Tests Without GPU**: -When using `slangc` to produce SPIRV, you should set the `SLANG_RUN_SPIRV_VALIDATION` environment variable to `1` to ensure -that slangc runs SPIRV validation on the generated SPIRV. If you don't see any errors you are good. -Don't use the system's `spirv-val` tool to validate the SPIRV because it is not up-to-date. +- Use CPU compute: `//TEST:COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type` +- Use interpreter: `//TEST:INTERPRET(filecheck=CHECK):` +- Example test structure in `tests/language-feature/lambda/lambda-0.slang` +**SPIRV Validation**: + +- Set `SLANG_RUN_SPIRV_VALIDATION=1` when using `slangc -target spirv` +- Don't use system's `spirv-val` tool (may be outdated) ### Slang Command Line Usage + **IMPORTANT:** Slang uses single dashes for multi-character options (not double dashes like most tools): + - Use `-help` (not `--help`) - Use `-target spirv` (not `--target spirv`) - Use `-dump-ir` (not `--dump-ir`) - Use `-stage compute` (not `--stage compute`) ### AVOID These Debugging Options + **DO NOT USE** these options as they are unmaintained, unreliable or unnecessary: + - slangc with `-dump-ast`, `-dump-intermediate-prefix`, `-dump-intermediates`, `-dump-ir-ids`, `-serial-ir`, `-dump-repro`, `-load-repro` and `-extract-repro`. - slang-test with `-category` and `-api` -### Code Formatting -```bash -# Format code before submitting PR -./extras/formatting.sh --no-version-check --cpp --since HEAD -``` - ## Architecture Overview ### Core Components **Compiler Pipeline**: + - **Lexer** (`source/compiler-core/slang-lexer.cpp`): Tokenizes source code - **Preprocessor** (`source/slang/slang-preprocessor.cpp`): Handles #include, macros, conditionals - **Parser** (`source/slang/slang-parser.cpp`): Recursive descent parser producing AST @@ -81,6 +96,7 @@ Don't use the system's `spirv-val` tool to validate the SPIRV because it is not - **Code Emission** (`source/slang/slang-emit-*.cpp`): Target-specific code generation **Key Directories**: + - `source/core/`: Core utilities (strings, containers, file system, platform abstractions) - `source/compiler-core/`: Compiler infrastructure (diagnostics, downstream compilers) - `source/slang/`: Main compiler implementation (frontend, IR, backend) @@ -96,12 +112,14 @@ Don't use the system's `spirv-val` tool to validate the SPIRV because it is not ### Compilation Model **Key Concepts**: + - **CompileRequest**: Bundles options, input files, and code generation requests - **TranslationUnit**: Collection of source files (HLSL: one per file, Slang: all files together) - **EntryPoint**: Function name + pipeline stage to compile - **Target**: Output format (DXIL, SPIR-V, etc.) + capability profile **Supported Targets**: + - Direct3D 11/12 (HLSL output) - Vulkan (SPIR-V, GLSL output) - Metal (MSL output) - experimental @@ -111,17 +129,25 @@ Don't use the system's `spirv-val` tool to validate the SPIRV because it is not ## Development Workflow -### Slang internal steps, as an example -1. Update lexer for new tokens (slang-lexer.cpp) -2. Extend parser for new syntax (slang-parser.cpp) -3. Add semantic analysis (slang-check-*.cpp) -4. Implement IR generation (slang-ir-*.cpp) -5. Add code generation for each target backend -6. Write comprehensive tests +### Adding New Language Features + +1. Update lexer for new tokens (`source/compiler-core/slang-lexer.cpp`) +2. Extend parser for new syntax (`source/slang/slang-parser.cpp`) +3. Add semantic analysis (`source/slang/slang-check-*.cpp`) +4. Implement IR generation (`source/slang/slang-ir-*.cpp`) +5. Add code generation for each target backend (`source/slang/slang-emit-*.cpp`) +6. Write comprehensive tests under `tests/` + +### Common Development Tasks + +- **Adding an IR instruction**: Update the Lua definition files in `source/slang/slang-ir-insts.lua`, then regenerate +- **Adding a built-in function**: Add to appropriate module in `prelude/` +- **Adding a new target**: Implement new emitter in `source/slang/slang-emit-*.cpp` ### Debugging tools #### slangc with `-dump-ir` + slangc with `-dump-ir` option is most efficient way to investigate problems that can be observed at IR level. It will often require a use of `-target` and the most common combination is `-dump-ir -target spirv-asm`. @@ -157,6 +183,7 @@ python3 ./extras/insttrace.py 1234 ./build/Debug/bin/slangc tests/my-test.slang ``` #### slangc with `-target spirv-asm` + slangc with `-target spirv-asm` is the most common way to see how the given slang shader is compiled into spirv code. When an environment variable, `SLANG_RUN_SPIRV_VALIDATION=1`, is set, it will also run a static SPIRV valdiation. @@ -166,31 +193,37 @@ When SPIRV validation fails, the actual spirv code is not printed. You can skip the validation with the option and print the spirv code even when it fails the validation. #### slangc with `-target spirv-asm -emit-spirv-via-glsl` + By default, slang uses `-emit-spirv-directly` and slang emits from slang shader to spirv directly. When `-emit-spirv-via-glsl` is used, slang will translate the input slang shader to glsl and let glslang to generate spirv code. This can be useful when we want to generate a reference spirv code for a comparison. ### IR System + - Slang uses a custom SSA-based IR (not LLVM) - IR instructions defined in `slang-ir-insts.h` (generated from Lua) - Extensive IR pass framework for optimization and lowering - Target-specific legalization passes before code emission ### Language Server + - Language Server Protocol implementation in `source/slang/slang-language-server.cpp` - Supports IntelliSense, completion, diagnostics, formatting - Used by VS Code and Visual Studio extensions ### Module System + - Slang supports separate compilation via modules - Modules can be compiled to IR and linked at runtime - Optional obfuscation for distributed modules - Core language features defined as modules in `prelude/` ### Generated files + - The enum values starting with `kIROp_` are defined in a generated file, `build/source/slang/fiddle/slang-ir-insts-enum.h.fiddle` ### Git commit message + - Don't mention Claude on the commit message ## Cross-Platform Considerations @@ -210,6 +243,7 @@ There is a dedicated repo, https://github.com/shader-slang/spec.git If needed, you should clone the repo under `external/` directory. ### Formal Specification (`external/spec/specification/`) + - `specification/index.bs` - Main specification document - `specification/types.md` - Type system specification - `specification/generics.md` - Generics and templates specification @@ -234,7 +268,9 @@ If needed, you should clone the repo under `external/` directory. - `specification/visibility.md` - Visibility and access control ### Feature Proposals (`external/spec/proposals/`) + **Template and Active Proposals:** + - `000-template.md` - Template for new proposals - `001-where-clauses.md` - `where` clauses for generic constraints (Partially implemented) - `002-type-equality-constraints.md` - Type equality constraints in generics @@ -264,4 +300,3 @@ If needed, you should clone the repo under `external/` directory. - `028-cooperative-matrix-2.md` - Extended cooperative matrix support - `029-conditional.md` - Conditional compilation features - `030-interface-method-default-impl.md` - Default interface method implementations (In Experiment) - |
