summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarsh Aggarwal (NVIDIA) <haaggarwal@nvidia.com>2025-08-05 19:28:55 +0530
committerGitHub <noreply@github.com>2025-08-05 13:58:55 +0000
commit2d775b54d2ab7772785c2196075d4c7c174407ab (patch)
treea1204b30debeabe30e61a1cc7533d94eda95b904
parent9a16700e858fc0379e551ab72188eb63a54ad3f1 (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.py58
-rwxr-xr-x.claude/hooks/stop.py120
-rw-r--r--.claude/settings.json33
-rw-r--r--.github/actions/claude-code-runner/action.yml359
-rw-r--r--.github/workflows/claude.yml288
-rwxr-xr-xCLAUDE.md81
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"
diff --git a/CLAUDE.md b/CLAUDE.md
index 48fed1fee..dc7f0bbff 100755
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -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)
-