Skip to content
Merged
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
214 changes: 34 additions & 180 deletions .github/workflows/check-skills.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
# check-skills.yml — Drop this into your library repo's .github/workflows/
#
# Checks intent skills after a release and opens or updates one review PR when
# existing skills, artifact coverage, or workspace package coverage need review.
# Validates intent skills on PRs. After a release or manual run, opens or
# updates one review PR when existing skills, artifact coverage, or workspace
# package coverage need review.
#
# Triggers: new release published, or manual workflow_dispatch.
# Triggers: pull requests touching skills/artifacts, new release published, or
# manual workflow_dispatch.
#
# intent-workflow-version: 2
# intent-workflow-version: 3
#
# Template variables (replaced by `intent setup`):
# @tanstack/router — e.g. @tanstack/query or my-workspace workspace

name: Check Skills

on:
pull_request:
paths:
- 'skills/**'
- '**/skills/**'
- '_artifacts/**'
- '**/_artifacts/**'
release:
types: [published]
workflow_dispatch: {}
Expand All @@ -22,8 +30,28 @@ permissions:
pull-requests: write

jobs:
check:
validate:
name: Validate intent skills
if: github.event_name == 'pull_request'
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
run: npm install -g @tanstack/intent

- name: Validate skills
run: intent validate --github-summary

review:
name: Check intent skill coverage
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -42,181 +70,7 @@ jobs:
- name: Check skills
id: stale
run: |
set +e
intent stale --json > stale.json
STATUS=$?
set -e

cat stale.json

if [ "$STATUS" -ne 0 ]; then
echo "has_review=true" >> "$GITHUB_OUTPUT"
echo "check_failed=true" >> "$GITHUB_OUTPUT"
cat > review-items.json <<'JSON'
[
{
"type": "stale-check-failed",
"library": "@tanstack/router",
"subject": "intent stale --json",
"reasons": ["The stale check command failed. Review the workflow logs before updating skills."]
}
]
JSON
else
node <<'NODE'
const fs = require('fs')
const reports = JSON.parse(fs.readFileSync('stale.json', 'utf8'))
const items = []

for (const report of reports) {
for (const skill of report.skills ?? []) {
if (!skill?.needsReview) continue
items.push({
type: 'stale-skill',
library: report.library,
subject: skill.name,
reasons: skill.reasons ?? [],
})
}

for (const signal of report.signals ?? []) {
if (signal?.needsReview === false) continue
items.push({
type: signal?.type ?? 'review-signal',
library: signal?.library ?? report.library,
subject:
signal?.packageName ??
signal?.packageRoot ??
signal?.skill ??
signal?.artifactPath ??
signal?.subject ??
report.library,
reasons: signal?.reasons ?? [],
artifactPath: signal?.artifactPath,
packageName: signal?.packageName,
packageRoot: signal?.packageRoot,
skill: signal?.skill,
})
}
}

fs.writeFileSync('review-items.json', JSON.stringify(items, null, 2) + '\n')
fs.appendFileSync(
process.env.GITHUB_OUTPUT,
`has_review=${items.length > 0 ? 'true' : 'false'}\n`,
)
NODE
fi

{
echo "review_items<<EOF"
cat review-items.json
echo "EOF"
} >> "$GITHUB_OUTPUT"

- name: Write clean summary
if: steps.stale.outputs.has_review == 'false'
run: |
{
echo "### Intent skill review"
echo ""
echo "No stale skills or coverage gaps found."
} >> "$GITHUB_STEP_SUMMARY"

- name: Build review PR body
if: steps.stale.outputs.has_review == 'true'
run: |
node <<'NODE'
const fs = require('fs')
const items = JSON.parse(fs.readFileSync('review-items.json', 'utf8'))
const grouped = new Map()

for (const item of items) {
grouped.set(item.type, (grouped.get(item.type) ?? 0) + 1)
}

const signalRows = [...grouped.entries()]
.sort(([a], [b]) => a.localeCompare(b))
.map(([type, count]) => `| \`${type}\` | ${count} |`)

const itemRows = items.map((item) => {
const subject = item.subject ? `\`${item.subject}\`` : '-'
const reasons = item.reasons?.length ? item.reasons.join('; ') : '-'
return `| \`${item.type}\` | ${subject} | \`${item.library}\` | ${reasons} |`
})

const prompt = [
'You are helping maintain Intent skills for this repository.',
'',
'Goal:',
'Resolve the Intent skill review signals below while preserving the existing scope, taxonomy, and maintainer-reviewed artifacts.',
'',
'Review signals:',
JSON.stringify(items, null, 2),
'',
'Required workflow:',
'1. Read the existing `_artifacts/*domain_map.yaml`, `_artifacts/*skill_tree.yaml`, and generated `skills/**/SKILL.md` files.',
'2. Read each flagged package package.json, public exports, README/docs if present, and source entry points.',
'3. Compare flagged packages against the existing domains, skills, tasks, packages, covers, sources, tensions, and cross-references in the artifacts.',
'4. For each signal, decide whether it means existing skill coverage, a missing generated skill, a new skill candidate, out-of-scope coverage, or deferred work.',
'',
'Maintainer questions:',
'Before editing skills or artifacts, ask the maintainer:',
'1. For each flagged package, is this package user-facing enough to need agent guidance?',
'2. If yes, should it extend an existing skill or become a new skill?',
'3. If it extends an existing skill, which current skill should own it?',
'4. If it is out of scope, what short reason should be recorded in artifact coverage ignores?',
'5. Are any of these packages experimental or unstable enough to exclude for now?',
'',
'Decision rules:',
'- Do not auto-generate skills.',
'- Do not create broad new skill areas without maintainer confirmation.',
'- Prefer adding package coverage to an existing skill when the package is an implementation variant of an existing domain.',
'- Create a new skill only when the package introduces a distinct developer task or failure mode.',
'- Preserve current naming, path, and package layout conventions.',
'- Keep generated skills under the package-local `skills/` directory.',
'- Keep repo-root `_artifacts` as the reviewed plan.',
'',
'If maintainer confirms updates:',
'1. Update the relevant `_artifacts/*domain_map.yaml` or `_artifacts/*skill_tree.yaml`.',
'2. Update or create `SKILL.md` files only for confirmed coverage changes.',
'3. Keep `sources` aligned between artifact skill entries and SKILL frontmatter.',
'4. Bump `library_version` only for skills whose covered source package version changed.',
'5. Run `npx @tanstack/intent@latest validate` on touched skill directories.',
'6. Summarize every package as one of: existing-skill coverage, new skill, ignored, or deferred.',
].join('\n')

const body = [
'## Intent Skill Review Needed',
'',
'Intent found skills, artifact coverage, or workspace package coverage that need maintainer review.',
'',
'### Summary',
'',
'| Signal | Count |',
'| --- | ---: |',
...signalRows,
'',
'### Review Items',
'',
'| Signal | Subject | Library | Reason |',
'| --- | --- | --- | --- |',
...itemRows,
'',
'### Agent Prompt',
'',
'Paste this into your coding agent:',
'',
'```text',
prompt,
'```',
'',
'This PR is a review reminder only. It does not update skills automatically.',
].join('\n')

fs.writeFileSync('pr-body.md', body + '\n')
fs.writeFileSync(process.env.GITHUB_STEP_SUMMARY, body + '\n')
NODE
intent stale --github-review --package-label "@tanstack/router"
Comment thread
LadyBluenotes marked this conversation as resolved.

- name: Open or update review PR
if: steps.stale.outputs.has_review == 'true'
Expand Down
52 changes: 0 additions & 52 deletions .github/workflows/validate-skills.yml

This file was deleted.

Loading