feat(skills): SkillMatcher extraction + SQLite registry #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: AI PR Review | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [opened, synchronize] | |
| concurrency: | |
| group: pr-review-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| review: | |
| runs-on: ubuntu-latest | |
| if: github.event.pull_request.draft == false | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get PR diff | |
| id: diff | |
| run: | | |
| git fetch origin ${{ github.event.pull_request.base.ref }} | |
| DIFF=$(git diff origin/${{ github.event.pull_request.base.ref }}...HEAD -- '*.ts' '*.tsx' | head -c 80000) | |
| echo "diff<<DIFF_EOF" >> "$GITHUB_OUTPUT" | |
| echo "$DIFF" >> "$GITHUB_OUTPUT" | |
| echo "DIFF_EOF" >> "$GITHUB_OUTPUT" | |
| - name: Get changed files | |
| id: files | |
| run: | | |
| git fetch origin ${{ github.event.pull_request.base.ref }} | |
| FILES=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}...HEAD -- '*.ts' '*.tsx') | |
| echo "files<<FILES_EOF" >> "$GITHUB_OUTPUT" | |
| echo "$FILES" >> "$GITHUB_OUTPUT" | |
| echo "FILES_EOF" >> "$GITHUB_OUTPUT" | |
| - name: AI Review | |
| id: review | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| BODY=$(cat <<'PROMPT' | |
| You are a senior TypeScript reviewer for StackMemory (AI context management, SQLite/FTS5, MCP tools). | |
| Review this PR diff for: | |
| 1. **Bugs** — logic errors, off-by-one, null/undefined issues, resource leaks | |
| 2. **Security** — SQL injection, secret exposure, unsafe input handling | |
| 3. **Performance** — N+1 queries, missing indexes, unbounded loops, memory leaks | |
| 4. **API design** — breaking changes, missing validation, inconsistent naming | |
| 5. **Test gaps** — untested edge cases, missing error path coverage | |
| Skip: style nits, import order, minor naming preferences. | |
| For each finding: file:line, severity (bug/warn/nit), what's wrong, suggested fix. | |
| If the PR looks good, say so briefly. | |
| ## Changed files | |
| ${{ steps.files.outputs.files }} | |
| ## Diff | |
| ```diff | |
| ${{ steps.diff.outputs.diff }} | |
| ``` | |
| PROMPT | |
| ) | |
| RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \ | |
| -H "Content-Type: application/json" \ | |
| -H "x-api-key: $ANTHROPIC_API_KEY" \ | |
| -H "anthropic-version: 2023-06-01" \ | |
| -d "$(jq -n \ | |
| --arg body "$BODY" \ | |
| '{ | |
| model: "claude-sonnet-4-20250514", | |
| max_tokens: 4096, | |
| messages: [{role: "user", content: $body}] | |
| }')") | |
| REVIEW=$(echo "$RESPONSE" | jq -r '.content[0].text // "Review failed: " + (.error.message // "unknown error")') | |
| echo "review<<REVIEW_EOF" >> "$GITHUB_OUTPUT" | |
| echo "$REVIEW" >> "$GITHUB_OUTPUT" | |
| echo "REVIEW_EOF" >> "$GITHUB_OUTPUT" | |
| - name: Post review comment | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const review = `${{ steps.review.outputs.review }}`; | |
| if (!review || review.startsWith('Review failed:')) { | |
| core.warning('AI review failed: ' + review); | |
| return; | |
| } | |
| // Find existing bot comment to update | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const botComment = comments.find(c => | |
| c.user.type === 'Bot' && c.body.includes('<!-- ai-pr-review -->') | |
| ); | |
| const body = `<!-- ai-pr-review -->\n## AI Review\n\n${review}\n\n---\n*Automated review by Claude Sonnet*`; | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body, | |
| }); | |
| } |