diff --git a/.claude/.mcp.json b/.claude/.mcp.json new file mode 100644 index 0000000..0e61620 --- /dev/null +++ b/.claude/.mcp.json @@ -0,0 +1,16 @@ +{ + "mcpServers": { + "ols-mcp": { + "type": "stdio", + "command": "uvx", + "args": [ + "ols-mcp" + ], + "env": {} + }, + "pubmed": { + "type": "http", + "url": "https://pubmed.mcp.claude.com/mcp" + } + } +} diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..29f0ffc --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [ + "Bash(*)", + "Edit", + "MultiEdit", + "NotebookEdit", + "FileEdit", + "WebFetch", + "WebSearch", + "Write" + ] + } +} diff --git a/.github/actions/claude-code-action/action.yml b/.github/actions/claude-code-action/action.yml new file mode 100644 index 0000000..7616989 --- /dev/null +++ b/.github/actions/claude-code-action/action.yml @@ -0,0 +1,180 @@ +name: "Claude Code Action" +description: "Run Claude Code in GitHub Actions workflows" + +inputs: + github_token: + description: "GitHub token with repo and issues permissions" + required: true + anthropic_api_key: + description: "Anthropic API key" + required: false + cborg_api_key: + description: "CBORG API key" + required: false + claude_code_oauth_token: + description: "Claude Code OAuth token" + required: false + prompt: + description: "The prompt to send to Claude Code" + required: false + default: "" + prompt_file: + description: "Path to a file containing the prompt to send to Claude Code" + required: false + default: "" + allowed_tools: + description: "Comma-separated list of allowed tools for Claude Code to use" + required: false + default: "" + output_file: + description: "File to save Claude Code output to (optional)" + required: false + default: "" + timeout_minutes: + description: "Timeout in minutes for Claude Code execution" + required: false + default: "10" + install_github_mcp: + description: "Whether to install the GitHub MCP server" + required: false + default: "false" + install_artl_mcp: + description: "Whether to install the ARTL MCP server" + required: false + default: "false" + + +runs: + using: "composite" + steps: + - name: Install uvx + shell: bash + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + source $HOME/.cargo/env + which uvx || echo "uvx not found in PATH, installing via pip" + pip install uv || true + + - name: Install Claude Code + shell: bash + run: npm install -g @anthropic-ai/claude-code + + - name: Install GitHub MCP Server + if: inputs.install_github_mcp == 'true' + shell: bash + run: | + claude mcp add-json github '{ + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server:sha-ff3036d" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${{ inputs.GITHUB_TOKEN }}" + } + }' + + - name: Install ARTL MCP Server + if: inputs.install_artl_mcp == 'true' + shell: bash + run: | + claude mcp add-json artl '{ + "command": "uvx", + "args": [ + "artl-mcp" + ] + }' + + - name: Prepare Prompt File + shell: bash + id: prepare_prompt + run: | + + # Check if either prompt or prompt_file is provided + if [ -z "${{ inputs.prompt }}" ] && [ -z "${{ inputs.prompt_file }}" ]; then + echo "::error::Neither 'prompt' nor 'prompt_file' was provided. At least one is required." + exit 1 + fi + + # Determine which prompt source to use + if [ ! -z "${{ inputs.prompt_file }}" ]; then + # Check if the prompt file exists + if [ ! -f "${{ inputs.prompt_file }}" ]; then + echo "::error::Prompt file '${{ inputs.prompt_file }}' does not exist." + exit 1 + fi + + # Use the provided prompt file + PROMPT_PATH="${{ inputs.prompt_file }}" + else + mkdir -p /tmp/claude-action + PROMPT_PATH="/tmp/claude-action/prompt.txt" + echo "${{ inputs.prompt }}" > "$PROMPT_PATH" + fi + + # Verify the prompt file is not empty + if [ ! -s "$PROMPT_PATH" ]; then + echo "::error::Prompt is empty. Please provide a non-empty prompt." + exit 1 + fi + + # Save the prompt path for the next step + echo "PROMPT_PATH=$PROMPT_PATH" >> $GITHUB_ENV + + - name: Run Claude Code + shell: bash + id: run_claude + run: | + + if [ ! -z "${{ inputs.claude_code_oauth_token }}" ]; then + echo CLAUDE AUTH TOKEN IS NOT SET + else + echo CLAUDE AUTH TOKEN IS SET + export ANTHROPIC_API_KEY= + fi + + ALLOWED_TOOLS_ARG="" + if [ ! -z "${{ inputs.allowed_tools }}" ]; then + ALLOWED_TOOLS_ARG="--allowedTools ${{ inputs.allowed_tools }}" + fi + + # Set a timeout to ensure the command doesn't run indefinitely + timeout_seconds=$((${{ inputs.timeout_minutes }} * 60)) + + if [ -z "${{ inputs.output_file }}" ]; then + # Run Claude Code and output to console + claude \ + -p \ + --verbose \ + --output-format stream-json \ + "$(cat ${{ env.PROMPT_PATH }})" \ + ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} + else + # Run Claude Code and tee output to console and file + claude \ + -p \ + --verbose \ + --output-format stream-json \ + "$(cat ${{ env.PROMPT_PATH }})" \ + ${{ inputs.allowed_tools != '' && format('--allowedTools "{0}"', inputs.allowed_tools) || '' }} | tee output.txt + + # Process output.txt into JSON in a separate step + jq -s '.' output.txt > output.json + + # Extract the result from the last item in the array (system message) + jq -r '.[-1].result' output.json > "${{ inputs.output_file }}" + + echo "Complete output saved to output.json, final response saved to ${{ inputs.output_file }}" + fi + + env: + ANTHROPIC_API_KEY: ${{ inputs.anthropic_api_key }} + # ANTHROPIC_AUTH_TOKEN: ${{ inputs.cborg_api_key }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ inputs.claude_code_oauth_token }} + GITHUB_TOKEN: ${{ inputs.github_token }} + # ANTHROPIC_BASE_URL: "https://api.cborg.lbl.gov" + # DISABLE_NON_ESSENTIAL_MODEL_CALLS: "1" diff --git a/.github/actions/claude-issue-summarize-action/action.yml b/.github/actions/claude-issue-summarize-action/action.yml new file mode 100644 index 0000000..896af91 --- /dev/null +++ b/.github/actions/claude-issue-summarize-action/action.yml @@ -0,0 +1,78 @@ +name: "Claude Issue summarize Action" +description: "Automatically Summarize GitHub issues using Claude Code" + +inputs: + timeout_minutes: + description: "Timeout in minutes for execution" + required: false + default: "5" + anthropic_api_key: + description: "Anthropic API key" + required: false + cborg_api_key: + description: "CBORG API key" + required: false + claude_code_oauth_token: + description: "Claude Code OAuth token" + required: false + github_token: + description: "GitHub token with repo and issues permissions" + required: true + +runs: + using: "composite" + steps: + - name: Checkout repository code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + + - name: Create prompt file + shell: bash + run: | + mkdir -p /tmp/claude-prompts + cat > /tmp/claude-prompts/claude-issue-summarize-prompt.txt << 'EOF' + You're an Summarization assistant for GitHub issues. Your task is to analyze the issue, look up related issues, and summarize the issue. + + Remember that this repo is primarily a note-taking and memory device for Chris Mungall, so anything you can do to enhance and enrich that would be helpful. + + IMPORTANT: Don't make any PRs. You should ONLY post a response, IN THE SAME ISSUE. + + Issue Information: + - REPO: ${{ github.repository }} + - ISSUE_NUMBER: ${{ github.event.issue.number }} + + TASK OVERVIEW: + + 1. First, use your judgement to determine the course of action. + - If the issue is an epic or has links to other issues, read those issues, and summarize them. Summarize their status etc. Given an overall summary. + - If the issue is an idea or a vague high level description, run with the idea, do deep research, give heloful suggestions with links to relevant papers etc. + - If there are links in the issue to websites, database entities, etc, then look these up. + - If there are links or references to papers, then looks these up and summarize (don't guess content - if you can't access the paper, then say "I can't access the paper") + - Try using artl-mcp to get paper fulltext. + - If you want a more powerful AI agent to take on this one and make a PR, use this magic phrase: `@dragon-ai-agent please ` + + 2. Next, use the GitHub tools to get context about the issue: + - You have access to these github tools: + - mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels + - mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments + - mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting) + - mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues + - mcp__github__list_issues: Use this to understand patterns in how other issues are labeled + - You can also use web searching and fetching. + + - It's okay to not add any information if the issue is not clear. + EOF + + - name: Run Claude Code + uses: ./.github/actions/claude-code-action + with: + prompt_file: /tmp/claude-prompts/claude-issue-summarize-prompt.txt + allowed_tools: "Bash(gh label list),WebFetch,Fetch,LS,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__add_issue_comment,mcp__github__search_issues,mcp__github__list_issues" + install_github_mcp: "true" + install_artl_mcp: "true" + timeout_minutes: ${{ inputs.timeout_minutes }} + anthropic_api_key: ${{ inputs.anthropic_api_key }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + cborg_api_key: ${{ inputs.cborg_api_key }} + github_token: ${{ inputs.github_token }} diff --git a/.github/actions/claude-issue-triage-action/action.yml b/.github/actions/claude-issue-triage-action/action.yml new file mode 100644 index 0000000..caaad89 --- /dev/null +++ b/.github/actions/claude-issue-triage-action/action.yml @@ -0,0 +1,95 @@ +name: "Claude Issue Triage Action" +description: "Automatically triage GitHub issues using Claude Code" + +inputs: + timeout_minutes: + description: "Timeout in minutes for execution" + required: false + default: "5" + anthropic_api_key: + description: "Anthropic API key" + required: false + claude_code_oauth_token: + description: "Claude Code OAuth token" + required: false + cborg_api_key: + description: "CBORG API key" + required: true + github_token: + description: "GitHub token with repo and issues permissions" + required: true + +runs: + using: "composite" + steps: + - name: Checkout repository code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + + - name: Create prompt file + shell: bash + run: | + mkdir -p /tmp/claude-prompts + cat > /tmp/claude-prompts/claude-issue-triage-prompt.txt << 'EOF' + You're an issue triage assistant for GitHub issues. Your task is to analyze the issue and select appropriate labels from the provided list. + + IMPORTANT: Don't post any comments or messages to the issue. Your only action should be to apply labels. + + Issue Information: + - REPO: ${{ github.repository }} + - ISSUE_NUMBER: ${{ github.event.issue.number }} + + TASK OVERVIEW: + + 1. First, fetch the list of labels available in this repository by running: `gh label list`. Run exactly this command with nothing else. + + 2. Next, use the GitHub tools to get context about the issue: + - You have access to these tools: + - mcp__github__get_issue: Use this to retrieve the current issue's details including title, description, and existing labels + - mcp__github__get_issue_comments: Use this to read any discussion or additional context provided in the comments + - mcp__github__update_issue: Use this to apply labels to the issue (do not use this for commenting) + - mcp__github__search_issues: Use this to find similar issues that might provide context for proper categorization and to identify potential duplicate issues + - mcp__github__list_issues: Use this to understand patterns in how other issues are labeled + - Start by using mcp__github__get_issue to get the issue details + + 3. Analyze the issue content, considering: + - The issue title and description + - The type of issue (bug report, feature request, question, etc.) + - Technical areas mentioned + - Severity or priority indicators + - User impact + - Components affected + + 4. Select appropriate labels from the available labels list provided above: + - Choose labels that accurately reflect the issue's nature + - Be specific but comprehensive + - Select priority labels if you can determine urgency (high-priority, med-priority, or low-priority) + - Consider platform labels (android, ios) if applicable + - If you find similar issues using mcp__github__search_issues, consider using a "duplicate" label if appropriate. Only do so if the issue is a duplicate of another OPEN issue. + + 5. Apply the selected labels: + - Use mcp__github__update_issue to apply your selected labels + - DO NOT post any comments explaining your decision + - DO NOT communicate directly with users + - If no labels are clearly applicable, do not apply any labels + + IMPORTANT GUIDELINES: + - Be thorough in your analysis + - Only select labels from the provided list above + - DO NOT post any comments to the issue + - Your ONLY action should be to apply labels using mcp__github__update_issue + - It's okay to not add any labels if none are clearly applicable + EOF + + - name: Run Claude Code + uses: ./.github/actions/claude-code-action + with: + prompt_file: /tmp/claude-prompts/claude-issue-triage-prompt.txt + allowed_tools: "Bash(gh label list),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue,mcp__github__search_issues,mcp__github__list_issues" + install_github_mcp: "true" + timeout_minutes: ${{ inputs.timeout_minutes }} + anthropic_api_key: ${{ inputs.anthropic_api_key }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + cborg_api_key: ${{ inputs.cborg_api_key }} + github_token: ${{ inputs.github_token }} diff --git a/.github/ai-controllers.json b/.github/ai-controllers.json new file mode 100644 index 0000000..e6a0c19 --- /dev/null +++ b/.github/ai-controllers.json @@ -0,0 +1 @@ +["cmungall"] diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 120000 index 0000000..be77ac8 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/.github/copilot-setup-steps.yml b/.github/copilot-setup-steps.yml new file mode 100644 index 0000000..a33b310 --- /dev/null +++ b/.github/copilot-setup-steps.yml @@ -0,0 +1,40 @@ +name: "Copilot Setup Steps" + + +# Automatically run the setup steps when they are changed to allow for easy validation, and +# allow manual testing through the repository's "Actions" tab +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + # Set the permissions to the lowest permissions possible needed for your steps. + # Copilot will be given its own token for its operations. + permissions: + # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. + contents: read + + # You can define any steps you want, and they will run before the agent starts. + # If you do not check out your code, Copilot will do this for you. + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Install Python tools + run: | + uv sync + source .venv/bin/activate + shell: bash + diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 0000000..5bf8ce5 --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,78 @@ +name: Claude Code Review + +on: + pull_request: + types: [opened, synchronize] + # Optional: Only run on specific file changes + # paths: + # - "src/**/*.ts" + # - "src/**/*.tsx" + # - "src/**/*.js" + # - "src/**/*.jsx" + +jobs: + claude-review: + # Optional: Filter by PR author + # if: | + # github.event.pull_request.user.login == 'external-contributor' || + # github.event.pull_request.user.login == 'new-developer' || + # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code Review + id: claude-review + uses: anthropics/claude-code-action@beta + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4) + # model: "claude-opus-4-20250514" + + # Direct prompt for automated review (no @claude mention needed) + direct_prompt: | + Please review this pull request and provide feedback on: + - Code quality and best practices + - Potential bugs or issues + - Performance considerations + - Security concerns + - Test coverage + + Be constructive and helpful in your feedback. + + # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR + # use_sticky_comment: true + + # Optional: Customize review based on file types + # direct_prompt: | + # Review this PR focusing on: + # - For TypeScript files: Type safety and proper interface usage + # - For API endpoints: Security, input validation, and error handling + # - For React components: Performance, accessibility, and best practices + # - For tests: Coverage, edge cases, and test quality + + # Optional: Different prompts for different authors + # direct_prompt: | + # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && + # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' || + # 'Please provide a thorough code review focusing on our coding standards and best practices.' }} + + # Optional: Add specific tools for running tests or linting + # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)" + + # Optional: Skip review for certain conditions + # if: | + # !contains(github.event.pull_request.title, '[skip-review]') && + # !contains(github.event.pull_request.title, '[WIP]') + diff --git a/.github/workflows/claude-issue-summarize.yml b/.github/workflows/claude-issue-summarize.yml new file mode 100644 index 0000000..710195f --- /dev/null +++ b/.github/workflows/claude-issue-summarize.yml @@ -0,0 +1,27 @@ +name: Claude Issue summarize +description: "Automatically summarize GitHub issues using Claude Code" + +on: + issues: + types: [opened] + +jobs: + summarize-issue: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + + - name: Run Claude Issue summarize + uses: ./.github/actions/claude-issue-summarize-action + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + cborg_api_key: ${{ secrets.CBORG_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/claude-issue-triage.yml b/.github/workflows/claude-issue-triage.yml new file mode 100644 index 0000000..ddec43e --- /dev/null +++ b/.github/workflows/claude-issue-triage.yml @@ -0,0 +1,27 @@ +name: Claude Issue Triage +description: "Automatically triage GitHub issues using Claude Code" + +on: + issues: + types: [opened] + +jobs: + triage-issue: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + + - name: Run Claude Issue Triage + uses: ./.github/actions/claude-issue-triage-action + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + cborg_api_key: ${{ secrets.CBORG_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/claude-ontology-review.yml b/.github/workflows/claude-ontology-review.yml new file mode 100644 index 0000000..1f847b0 --- /dev/null +++ b/.github/workflows/claude-ontology-review.yml @@ -0,0 +1,115 @@ +# This action invokes claude to review a PR. +# This is a generic action - you can customize, or delete in favor of the ontology review PR. +# -- +# This github action is from the copier template in https://github.com/ai4curation/github-ai-integrations, donated by the Monarch Initiative +# For more documentation, see https://ai4curation.github.io/aidocs/how-tos/set-up-github-actions/ +# Author: Chris Mungall (@cmungall) +name: Claude Code Ontology Review + +on: + pull_request: + types: [opened, synchronize] + paths: + - "src/ontology" + +jobs: + claude-review: + # Optional: Filter by PR author + # if: | + # github.event.pull_request.user.login == 'external-contributor' || + # github.event.pull_request.user.login == 'new-developer' || + # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code Ontology Review + id: claude-review + uses: anthropics/claude-code-action@beta + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # Optional: Specify model (commenting defaults to Claude Sonnet 4) + model: "claude-opus-4-1-20250805" + + # Direct prompt for automated review (no @claude mention needed) + direct_prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.pull_request.number }} + + This PR modifies critical ontology files. + + Review this PR against our team checklist: + + ## Issue alignment + - [ ] The PR aligns with the source issue, and only closes it if all components of the issue addressed + - [ ] The PR summary gives sufficient context for any decisions made, attributing them to issue authors where appropriate + + ## Ontology Metadata + - [ ] PR is linked to source issue(s) via appropriate annotation property + - [ ] any added definitions have provenance + + ## Identifiers and hallucinations + - [ ] all identifiers are CURIEs (or URLs), with standard prefixes + - [ ] I have looked up term identifiers and confirm that they denote what is intended + - [ ] all PMIDs and publication references resolve to the appropriate article/title + + ## Design patterns (DPs) + - [ ] any new terms conform to documented DPs for this ontology + - [ ] if the term fits into a DP, then the label is constructed in a standard way (although scientific norms prevail) + - [ ] if the term is compositional in nature, then it should conform to a DP, and have a relevant logical definition + + ## Definitions (if the PR adds or updates a definition) + - [ ] Definitions are present for any new term + - [ ] Definitions conform to the style guide for this ontology + - [ ] Definitions follows genus-differentia form (while prioritizing normal scientific phrasing over awkward ontology language) + - [ ] The genus of the definition is an ancestor of the term + - [ ] The differentia of the definition are necessary and sufficient for distinguishing from the genus + - [ ] The definition has appropriate provenance + - [ ] I have looked up the provenance, and it is correct and relevant + - [ ] The label, definition, and logical definition (when present) are largely consistent + + ## Relationships and logical axioms + - [ ] If a logical definition is not present, then there should be at least one is_a (subClassOf) to the most precise parent term + - [ ] If a logical definition is present, then there is reasonable expectation the term should classify precisely + - [ ] Other relationships are present to a reasonable degree and as dictated by the general ontology stye (e.g part-ofs for anatomy) + - [ ] Additional logical axioms (such as disjointness) are added as appropriate, depending on ontology style, and design patterns + - [ ] Additional logical axioms make sense and do not over-constrain + + Be constructive and helpful in your feedback. + + # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR + use_sticky_comment: true + + # Optional: Customize review based on file types + # direct_prompt: | + # Review this PR focusing on: + # - For TypeScript files: Type safety and proper interface usage + # - For API endpoints: Security, input validation, and error handling + # - For React components: Performance, accessibility, and best practices + # - For tests: Coverage, edge cases, and test quality + + # Optional: Different prompts for different authors + # direct_prompt: | + # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && + # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' || + # 'Please provide a thorough code review focusing on our coding standards and best practices.' }} + + # Optional: Add specific tools for running tests or linting + # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)" + + # Optional: Skip review for certain conditions + # if: | + # !contains(github.event.pull_request.title, '[skip-review]') && + # !contains(github.event.pull_request.title, '[WIP]') + diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000..e1d4a43 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,90 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Install python tools + run: | + uv sync + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@beta + + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + mcp_config: | + { + "mcpServers": { + "ols": { + "command": "uvx", + "args": [ + "ols-mcp" + ] + }, + "sequential-thinking": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-sequential-thinking" + ] + } + } + } + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1) + # model: "claude-opus-4-1-20250805" + + # Optional: Customize the trigger phrase (default: @claude) + # trigger_phrase: "/claude" + + # Optional: Trigger when specific user is assigned to an issue + # assignee_trigger: "claude-bot" + + # Optional: Allow Claude to run specific commands + allowed_tools: "Bash(*),FileEdit,Edit,MultiEdit,WebSearch,WebFetch,mcp__ols_mcp__search_all_ontologies,mcp__ols_mcp__get_terms_from_ontology" + + # Optional: Add custom instructions for Claude to customize its behavior for your project + # custom_instructions: | + # Follow our coding standards + # Ensure all new code has tests + # Use TypeScript for new files + + # Optional: Custom environment variables for Claude + # claude_env: | + # NODE_ENV: test + diff --git a/.github/workflows/dragon-ai.yml b/.github/workflows/dragon-ai.yml new file mode 100644 index 0000000..584bb43 --- /dev/null +++ b/.github/workflows/dragon-ai.yml @@ -0,0 +1,190 @@ +name: Dragon AI Agent GitHub Mentions + +env: + ROBOT_VERSION: v1.9.7 + TOOLS_DIR: ${{ github.workspace }}/tools + +on: + issues: + types: [opened, edited] + issue_comment: + types: [created, edited] + pull_request: + types: [opened, edited] + pull_request_review_comment: + types: [created, edited] + +jobs: + check-mention: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.check.outputs.result }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + # this seems to be necessary to ensure actions uses the latest; see + # https://github.com/actions/checkout/issues/439 + with: + ref: ${{ github.ref }} + + - name: Check for qualifying mention + id: check + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.PAT_FOR_PR }} # Use PAT instead of default token + script: | + // Load allowed users from config + const fs = require('fs'); + let allowedUsers = []; + try { + const configContent = fs.readFileSync('.github/ai-controllers.json', 'utf8'); + allowedUsers = JSON.parse(configContent); + } catch (error) { + console.log('Error loading allowed users:', error); + allowedUsers = ['cmungall']; // Fallback + } + + // Get content and user from event payload directly to avoid API calls when possible + let content = ''; + let userLogin = ''; + let itemType = ''; + let itemNumber = 0; + + if (context.eventName === 'issues') { + content = context.payload.issue.body || ''; + userLogin = context.payload.issue.user.login; + itemType = 'issue'; + itemNumber = context.payload.issue.number; + } else if (context.eventName === 'pull_request') { + content = context.payload.pull_request.body || ''; + userLogin = context.payload.pull_request.user.login; + itemType = 'pull_request'; + itemNumber = context.payload.pull_request.number; + } else if (context.eventName === 'issue_comment') { + content = context.payload.comment.body || ''; + userLogin = context.payload.comment.user.login; + itemType = 'issue'; + itemNumber = context.payload.issue.number; + } else if (context.eventName === 'pull_request_review_comment') { + content = context.payload.comment.body || ''; + userLogin = context.payload.comment.user.login; + itemType = 'pull_request'; + itemNumber = context.payload.pull_request.number; + } + + // Check if user is allowed and mention exists + const isAllowed = allowedUsers.includes(userLogin); + const mentionRegex = /@dragon-ai-agent\s+please\s+(.*)/i; + const mentionMatch = content.match(mentionRegex); + + const qualifiedMention = isAllowed && mentionMatch !== null; + const prompt = qualifiedMention ? mentionMatch[1].trim() : ''; + + console.log(`User: ${userLogin}, Allowed: ${isAllowed}, Has mention: ${mentionMatch !== null}`); + + return { + qualifiedMention, + itemType, + itemNumber, + prompt, + user: userLogin, + controllers: allowedUsers.map(u => '@' + u).join(', ') + }; + + respond-to-mention: + needs: check-mention + if: fromJSON(needs.check-mention.outputs.result).qualifiedMention == true + permissions: + contents: write + pull-requests: write + issues: write + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + token: ${{ secrets.PAT_FOR_PR }} # Use PAT for checkout to allow committing later + + - name: Configure Git + run: | + git config --global user.name "Dragon-AI Agent" + git config --global user.email "dragon-ai-agent[bot]@users.noreply.github.com" + + - name: Create tools directory + run: mkdir -p ${{ env.TOOLS_DIR }} + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: install claude + run: | + npm install -g @anthropic-ai/claude-code + + + - name: Add tools to PATH + run: | + echo "${{ env.TOOLS_DIR }}" >> $GITHUB_PATH + ls -alt ${{ github.workspace }} + ls -alt ${{ env.TOOLS_DIR }} + + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Install python tools + run: | + uv venv + source .venv/bin/activate + + - name: Set up environment + run: | + # Safely write prompt to file + mkdir -p /tmp/claude-input + echo "${{ fromJSON(needs.check-mention.outputs.result).prompt }}" > /tmp/claude-input/prompt.txt + + - name: Create structured Claude prompt + env: + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GH_TOKEN: ${{ secrets.PAT_FOR_PR }} + run: | + cat > /tmp/claude-input/claude_prompt.txt << EOL + You are @dragon-ai-agent. + You're responding to a request from or relayed by @${{ fromJSON(needs.check-mention.outputs.result).user }} on GitHub ${{ fromJSON(needs.check-mention.outputs.result).itemType }} #${{ fromJSON(needs.check-mention.outputs.result).itemNumber }}. + You only respond to requests from the following authorized controllers: ${{ fromJSON(needs.check-mention.outputs.result).controllers }}. + + You should use \`gh\` to communicate with the user via the GitHub issue/ticket. + If instructed to modify files, you should make changes on a branch and submit a PR, communicating clearly and in + detail on the PR. + + Always end by informing the user what you did (or were not able to do) with a message, either on an issue or a PR, + as appropriate. + + The request is below, enclosed in triple backticks: + \`\`\` + $(cat /tmp/claude-input/prompt.txt) + \`\`\` + + However, you should use `gh` to read the complete context of the request, and look at any linked issues or PRs + that provide additional context. + + EOL + + + - name: Run Claude Code in headless mode + id: claude-response + env: + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + + export PATH="${{ env.TOOLS_DIR }}:$PATH" + + # Run Claude with proper permissions + claude -p "$(cat /tmp/claude-input/claude_prompt.txt)" \ + --permission-mode bypassPermissions \ + --output-format stream-json \ + --verbose + diff --git a/.goosehints b/.goosehints new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/.goosehints @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e1ffe42 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,77 @@ +# AGENTS.md for semantic-sql + +SQL and SQLite builds of common OWL ontologies, including all of OBO + +TODO: fill in extra description here + +## Repo management + +This repo uses `uv` for managing dependencies. Never use commands like `pip` to add or manage dependencies. +`uv run` is the best way to run things, unless you are using `justfile` or `makefile` target + +`mkdocs` is used for documentation.## This is a LinkML Schema repository + +Layout: + + * `src/semantic_sql/schema/semantic_sql.yaml` - LinkML source schema (edit this) + * `project` - derived files (do not edit these directly, they are derived from the LinkML) + * `src/docs` - source markdown for documentation + * `docs` - derived docs - do not edit these directly + * `src/data/examples/{valid,invalid}` - example data files + * always include positive examples of each class in the `valid` subfolder + * include negative examples for unit tests and to help illustrate pitfalls + * format is `ClassName-{SOMENAME}.yaml` + * `examples` - derived examples. Do not edit these directly + +Building and testing: + +* `just --list` to see all commands +* `just gen-project` to generate `project` files +* `just test` to test schema and pos/neg examples +* `just lint` analogous to ruff for python + +These are wrappers on top of existing linkml commands such as `gen-project`, `linkml-convert`, `linkml-run-examples`. +You can run the underlying commands (with `uv run ...`) but in general justfile targets should be favored. + +Best practice: + +* For full documentation, see https://linkml.io/linkml/ +* Follow LinkML naming conventions (CamelCase for classes, snake_case for slots/attributes) +* For schemas with polymorphism, consider using field `type` marked as a `type_designator: true` +* Include meaningful descriptions of each element +* map to standards where appropriate (e.g. dcterms) +* Never guess OBO term IDs. Always use the OLS MCP to look for relevant ontology terms +* be proactive in using due diligence to do deep research on the domain, and look at existing standards## This is a Python repository + +Layout: + + * `src/semantic_sql/` - Code goes here + * `docs` - mkdocs docs + * `mkdocs.yml` - index of docs + * `tests/input` - example files + +Building and testing: + +* `just --list` to see all commands +* `just test` performs unit tests, doctests, ruff/liniting +* `just test-full` as above plus integration tests + +You can run the underlying commands (with `uv run ...`) but in general justfile targets should be favored. + +Best practice: + +* Use doctests liberally - these serve as both explanatory examples for humans and as unit tests +* For longer examples, write pytest tests +* always write pytest functional style rather than unittest OO style +* use modern pytest idioms, including `@pytest.mark.parametrize` to test for combinations of inputs +* NEVER write mock tests unless requested. I need to rely on tests to know if something breaks +* For tests that have external dependencies, you can do `@pytest.mark.integration` +* Do not "fix" issues by changing or weakening test conditions. Try harder, or ask questions if a test fails. +* Avoid try/except blocks, these can mask bugs +* Fail fast is a good principle +* Follow the DRY principle +* Avoid repeating chunks of code, but also avoid premature over-abstraction +* Pydantic or LinkML is favored for data objects +* For state in engine-style OO classes, dataclasses is favored +* Declarative principles are favored +* Always use type hints, always document methods and classes \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file