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
9 changes: 9 additions & 0 deletions .changeset/afraid-trees-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@tanstack/intent': patch
---

Harden Intent skill checks for nested workspaces, generated GitHub workflows, Yarn PnP discovery, and Agent Skills spec compatibility.

`intent validate` now discovers package-local `skills/` directories from workspace configuration, including nested layouts. The generated `check-skills.yml` workflow now delegates PR validation and release/manual review PR generation to the CLI. `intent stale --github-review` writes review files with the reasons each skill or package was flagged.

Intent package scanning also supports Yarn PnP projects through Yarn's PnP API, and validation now emits warning-only Agent Skills spec compatibility notices without failing existing Intent skills.
9 changes: 6 additions & 3 deletions docs/cli/intent-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ npx @tanstack/intent@latest setup
- Ensures `files` includes required publish entries
- Preserves existing indentation
- `setup`
- Copies templates from `@tanstack/intent/meta/templates/workflows` to `.github/workflows`
- Copies the `check-skills.yml` workflow template from `@tanstack/intent/meta/templates/workflows` to `.github/workflows`
- Applies variable substitution (`PACKAGE_NAME`, `PACKAGE_LABEL`, `PAYLOAD_PACKAGE`, `REPO`, `DOCS_PATH`, `SRC_PATH`, `WATCH_PATHS`)
- Detects the workspace root in monorepos and writes repo-level workflows there
- Skips files that already exist at destination
Expand All @@ -41,9 +41,12 @@ npx @tanstack/intent@latest setup
- Missing or invalid `package.json` when running `edit-package-json`
- Missing template source when running `setup`

## Notes
## Notes

- `setup` skips existing files
- `check-skills.yml` validates skills on PRs and opens review PRs from release/manual runs
- To adopt updated workflow templates, delete or move the old generated workflow files first, then rerun `setup`
- If your repo has an older generated `validate-skills.yml`, remove it after adopting the current `check-skills.yml`; PR validation now lives in `check-skills.yml`
- In monorepos, run `setup` from either the repo root or a package directory; Intent writes workflows to the workspace root

## Related
Expand Down
30 changes: 16 additions & 14 deletions docs/getting-started/quick-start-maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Run these commands to prepare your package for skill publishing:
# Update package.json with required fields
npx @tanstack/intent@latest edit-package-json

# Copy CI workflow templates (validate + stale checks)
# Copy the CI workflow template
npx @tanstack/intent@latest setup
```

Expand All @@ -112,9 +112,13 @@ npx @tanstack/intent@latest setup
- `files` array entries for `skills/`
- For single packages: also adds `!skills/_artifacts` to exclude artifacts from npm
- For monorepos: skips the artifacts exclusion (artifacts live at repo root)
- `setup` copies workflow templates to `.github/workflows/` for automated validation and staleness checking

### 5. Ship skills with your package
- `setup` copies `check-skills.yml` to `.github/workflows/` for automated validation and staleness checking

`setup` does not overwrite existing workflow files. To pick up newer generated workflows, delete or move the old generated files in `.github/workflows/`, then rerun `npx @tanstack/intent@latest setup`.

If your repo already has an older generated `validate-skills.yml`, remove it after adopting the current `check-skills.yml`; PR validation now runs from `check-skills.yml`.

### 5. Ship skills with your package

Skills ship inside your npm package. When you publish:

Expand All @@ -133,18 +137,16 @@ Consumers who install your library automatically get the skills. They discover l

## Ongoing Maintenance (Manual or Agent-Assisted)

### 6. Set up CI workflows

After running `setup`, you'll have two workflows in `.github/workflows/`:

**validate-skills.yml** (runs on PRs touching `skills/`)
- Validates SKILL.md frontmatter and structure
- Ensures files stay under 500 lines
- Runs automatically on every pull request that modifies skills

**check-skills.yml** (runs on release or manual trigger)
### 6. Set up the CI workflow

After running `setup`, you'll have `check-skills.yml` in `.github/workflows/`:

**check-skills.yml** (runs on PRs touching skills/artifacts, release, or manual trigger)
- Validates SKILL.md frontmatter and structure
- Ensures files stay under 500 lines
- Automatically detects stale skills and coverage gaps after you publish a new release
- Opens one grouped review PR with an agent-friendly prompt
- Includes the reason each skill or package was flagged
- Requires you to copy the prompt into Claude Code, Cursor, or your agent to update skills

### 7. Update stale skills
Expand Down
214 changes: 34 additions & 180 deletions packages/intent/meta/templates/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`):
# {{PACKAGE_LABEL}} — 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": "{{PACKAGE_LABEL}}",
"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 "{{PACKAGE_LABEL}}"

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

This file was deleted.

2 changes: 1 addition & 1 deletion packages/intent/src/cli-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface StaleTargetResult {
workflowAdvisories: Array<string>
}

export const INTENT_CHECK_SKILLS_WORKFLOW_VERSION = 2
export const INTENT_CHECK_SKILLS_WORKFLOW_VERSION = 3

export function getMetaDir(): string {
const thisDir = dirname(fileURLToPath(import.meta.url))
Expand Down
Loading
Loading