Skip to content

feat(skills): SkillMatcher extraction + SQLite registry #4

feat(skills): SkillMatcher extraction + SQLite registry

feat(skills): SkillMatcher extraction + SQLite registry #4

Workflow file for this run

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,
});
}