Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions .github/workflows/check-skills.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# check-skills.yml — Drop this into your library repo's .github/workflows/
#
# Checks for stale intent skills after a release and opens a review PR
# if any skills need attention. The PR body includes a prompt you can
# paste into Claude Code, Cursor, or any coding agent to update them.
#
# Triggers: new release published, or manual workflow_dispatch.
#
# Template variables (replaced by `intent setup`):
# @tanstack/react-router — e.g. @tanstack/query
#
# Adapted for TanStack Router monorepo: loops over all packages with skills.

name: Check Skills

on:
release:
types: [published]
workflow_dispatch: {}

permissions:
contents: write
pull-requests: write

jobs:
check:
name: Check for stale skills
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install intent CLI
run: npm install -g @tanstack/intent

- name: Install dependencies
run: npm install --ignore-scripts
env:
npm_config_legacy_peer_deps: 'true'

- name: Check staleness
id: stale
run: |
# Monorepo: collect stale reports from all packages with skills
ALL_OUTPUT="["
FIRST=true
for dir in packages/*/; do
if [ -d "$dir/skills" ]; then
PKG_OUTPUT=$(cd "$dir" && npx @tanstack/intent stale --json 2>/dev/null) || true
if [ -n "$PKG_OUTPUT" ] && [ "$PKG_OUTPUT" != "[]" ] && [ "$PKG_OUTPUT" != "No intent-enabled packages found." ]; then
if [ "$FIRST" = true ]; then
FIRST=false
else
ALL_OUTPUT="${ALL_OUTPUT},"
fi
# Strip outer brackets and append entries
ENTRIES=$(echo "$PKG_OUTPUT" | node -e "
const input = require('fs').readFileSync('/dev/stdin','utf8').trim();
try { const arr = JSON.parse(input); process.stdout.write(JSON.stringify(arr).slice(1, -1)); } catch {}
")
ALL_OUTPUT="${ALL_OUTPUT}${ENTRIES}"
fi
fi
done
ALL_OUTPUT="${ALL_OUTPUT}]"

echo "$ALL_OUTPUT"

# Check if any skills need review
NEEDS_REVIEW=$(echo "$ALL_OUTPUT" | node -e "
const input = require('fs').readFileSync('/dev/stdin','utf8');
try {
const reports = JSON.parse(input);
const stale = reports.flatMap(r =>
r.skills.filter(s => s.needsReview).map(s => ({ library: r.library, skill: s.name, reasons: s.reasons }))
);
if (stale.length > 0) {
console.log(JSON.stringify(stale));
}
} catch {}
")

if [ -z "$NEEDS_REVIEW" ]; then
echo "has_stale=false" >> "$GITHUB_OUTPUT"
else
echo "has_stale=true" >> "$GITHUB_OUTPUT"
# Escape for multiline GH output
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "stale_json<<$EOF" >> "$GITHUB_OUTPUT"
echo "$NEEDS_REVIEW" >> "$GITHUB_OUTPUT"
echo "$EOF" >> "$GITHUB_OUTPUT"
fi

- name: Build summary
if: steps.stale.outputs.has_stale == 'true'
id: summary
run: |
node -e "
const stale = JSON.parse(process.env.STALE_JSON);
const lines = stale.map(s =>
'- **' + s.skill + '** (' + s.library + '): ' + s.reasons.join(', ')
);
const summary = lines.join('\n');

const prompt = [
'Review and update the following stale intent skills for TanStack Router:',
'',
...stale.map(s => '- ' + s.skill + ': ' + s.reasons.join(', ')),
'',
'For each stale skill:',
'1. Read the current SKILL.md file',
'2. Check what changed in the library since the skill was last updated',
'3. Update the skill content to reflect current APIs and behavior',
'4. Run \`npx @tanstack/intent validate\` to verify the updated skill',
].join('\n');

// Write outputs
const fs = require('fs');
const env = fs.readFileSync(process.env.GITHUB_OUTPUT, 'utf8');
const eof = require('crypto').randomBytes(15).toString('base64');
fs.appendFileSync(process.env.GITHUB_OUTPUT,
'summary<<' + eof + '\n' + summary + '\n' + eof + '\n' +
'prompt<<' + eof + '\n' + prompt + '\n' + eof + '\n'
);
"
env:
STALE_JSON: ${{ steps.stale.outputs.stale_json }}

- name: Open review PR
if: steps.stale.outputs.has_stale == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ github.event.release.tag_name || 'manual' }}"
BRANCH="skills/review-${VERSION}"

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "$BRANCH"
git commit --allow-empty -m "chore: review stale skills for ${VERSION}"
git push origin "$BRANCH"

gh pr create \
--title "Review stale skills (${VERSION})" \
--body "$(cat <<'PREOF'
## Stale Skills Detected

The following skills may need updates after the latest release:

${{ steps.summary.outputs.summary }}

---

### Update Prompt

Paste this into your coding agent (Claude Code, Cursor, etc.):

~~~
${{ steps.summary.outputs.prompt }}
~~~

PREOF
)" \
--head "$BRANCH" \
--base main
54 changes: 54 additions & 0 deletions .github/workflows/notify-playbooks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# notify-intent.yml — Drop this into your library repo's .github/workflows/
#
# Fires a repository_dispatch event to TanStack/intent whenever docs or
# source files change on merge to main. This triggers the skill staleness
# check workflow in the intent repo.
#
# Requirements:
# - A fine-grained PAT with contents:write on TanStack/intent stored
# as the INTENT_NOTIFY_TOKEN repository secret.
#
# Template variables (replaced by `intent setup`):
# @tanstack/react-router — e.g. @tanstack/query
# docs/** — e.g. docs/**
# packages/*/src/** — e.g. packages/query-core/src/**
#
# Adapted for TanStack Router monorepo: watches all packages.

name: Notify Intent

on:
push:
branches: [main]
paths:
- 'docs/**'
- 'packages/*/src/**'

jobs:
notify:
name: Notify TanStack Intent
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Collect changed files
id: changes
run: |
FILES=$(git diff --name-only HEAD~1 HEAD | jq -R -s -c 'split("\n") | map(select(length > 0))')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use push range SHAs instead of HEAD~1..HEAD for changed files.

Line 40 misses files when a push contains multiple commits, so downstream skill checks can run on incomplete change sets.

Proposed fix
-          FILES=$(git diff --name-only HEAD~1 HEAD | jq -R -s -c 'split("\n") | map(select(length > 0))')
+          BASE_SHA="${{ github.event.before }}"
+          if [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
+            BASE_SHA="$(git rev-list --max-parents=0 HEAD)"
+          fi
+          FILES=$(git diff --name-only "$BASE_SHA" "${{ github.sha }}" | jq -R -s -c 'split("\n") | map(select(length > 0))')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/notify-playbooks.yml at line 40, The workflow uses git
diff --name-only HEAD~1 HEAD to populate FILES which misses changes when
multiple commits are pushed; replace that range with the push-range SHAs
provided by the GitHub event (use the event before/after SHAs) so FILES reflects
the full push. Update the command that sets FILES (the FILES=... assignment) to
call git diff --name-only using the push-range variables (event.before /
event.after or the GitHub-provided env vars like GITHUB_EVENT_BEFORE /
GITHUB_SHA) instead of HEAD~1 and HEAD so downstream checks operate on the
complete set of changed files.

echo "files=$FILES" >> "$GITHUB_OUTPUT"
- name: Dispatch to intent repo
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.INTENT_NOTIFY_TOKEN }}
repository: TanStack/intent
event-type: skill-check
client-payload: |
{
"package": "@tanstack/router",
"sha": "${{ github.sha }}",
"changed_files": ${{ steps.changes.outputs.files }}
}
52 changes: 52 additions & 0 deletions .github/workflows/validate-skills.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# validate-skills.yml — Drop this into your library repo's .github/workflows/
#
# Validates skill files on PRs that touch the skills/ directory.
# Ensures frontmatter is correct, names match paths, and files stay under
# the 500-line limit.

name: Validate Skills

on:
pull_request:
paths:
- 'skills/**'
- '**/skills/**'

jobs:
validate:
name: Validate skill files
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install intent CLI
run: npm install -g @tanstack/intent

- name: Find and validate skills
run: |
# Find all directories containing SKILL.md files
SKILLS_DIR=""
if [ -d "skills" ]; then
SKILLS_DIR="skills"
elif [ -d "packages" ]; then
# Monorepo — find skills/ under packages
for dir in packages/*/skills; do
if [ -d "$dir" ]; then
echo "Validating $dir..."
intent validate "$dir"
fi
done
exit 0
fi

if [ -n "$SKILLS_DIR" ]; then
intent validate "$SKILLS_DIR"
else
echo "No skills/ directory found — skipping validation."
fi
Loading
Loading