diff --git a/.changeset/config.json b/.changeset/config.json index 1b5ea90a..bd439093 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -2,7 +2,7 @@ "$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json", "changelog": [ "@svitejs/changesets-changelog-github-compact", - { "repo": "TanStack/playbooks" } + { "repo": "TanStack/intent" } ], "commit": false, "access": "public", diff --git a/.github/workflows/validate-skills.yml b/.github/workflows/validate-skills.yml index be8b0f23..6b8336d3 100644 --- a/.github/workflows/validate-skills.yml +++ b/.github/workflows/validate-skills.yml @@ -3,8 +3,8 @@ name: Validate Skills on: pull_request: paths: - - 'packages/playbooks/skills/**' - - 'packages/playbooks/package_map.yaml' + - 'packages/intent/skills/**' + - 'packages/intent/package_map.yaml' - 'scripts/validate-skills.ts' jobs: diff --git a/knip.json b/knip.json index 2ac5c855..023c5fdf 100644 --- a/knip.json +++ b/knip.json @@ -6,7 +6,7 @@ ".": { "entry": ["scripts/*.ts"] }, - "packages/playbooks": { + "packages/intent": { "ignore": ["meta/**"] } } diff --git a/package.json b/package.json index 8d0ac1b0..6b7eb35b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "repository": { "type": "git", - "url": "git+https://github.com/TanStack/playbooks.git" + "url": "git+https://github.com/TanStack/intent.git" }, "packageManager": "pnpm@10.26.1", "type": "module", @@ -17,7 +17,7 @@ "clean": "find agents -name 'dist' -type d -prune -exec rm -rf {} +", "clean:node_modules": "find agents -name 'node_modules' -type d -prune -exec rm -rf {} +", "clean:all": "pnpm run clean && pnpm run clean:node_modules", - "copy:readme": "cp README.md packages/playbooks/README.md", + "copy:readme": "cp README.md packages/intent/README.md", "dev": "pnpm run watch", "format": "prettier --experimental-cli --ignore-unknown '**/*' --write", "lint:fix": "nx affected --target=lint:fix --exclude=examples/**", diff --git a/packages/intent/CHANGELOG.md b/packages/intent/CHANGELOG.md new file mode 100644 index 00000000..17e41f3c --- /dev/null +++ b/packages/intent/CHANGELOG.md @@ -0,0 +1,7 @@ +# @tanstack/intent + +## 0.1.0 + +### Patch Changes + +- Add `intent-library` end-user CLI for library consumers. Libraries wire it up via a generated shim (`intent setup --shim`) to expose an `intent` bin. Running `intent list` recursively discovers skills across the library's dependency tree; `intent install` prints an agent-driven prompt to map skills to project tasks in CLAUDE.md. ([#9](https://github.com/TanStack/intent/pull/9)) diff --git a/packages/intent/README.md b/packages/intent/README.md new file mode 100644 index 00000000..60e3f594 --- /dev/null +++ b/packages/intent/README.md @@ -0,0 +1,66 @@ +# @tanstack/intent + +Ship compositional knowledge for AI coding agents alongside your npm packages. + +Skills are npm packages of knowledge — encoding how tools work together, what patterns apply for which goals, and what to avoid. Skills travel with the tool via `npm update`, not the model's training cutoff. + +`@tanstack/intent` is the toolkit for generating, discovering, and maintaining skills for your library. + +## Install + +```bash +pnpm add -D @tanstack/intent +``` + +## Quick Start + +### For library consumers + +Set up intent discovery in your project's agent config files (CLAUDE.md, .cursorrules, etc.): + +```bash +npx intent init +``` + +List available skills from installed packages: + +```bash +npx intent list +``` + +### For library maintainers + +Generate skills for your library using the guided scaffold workflow: + +```bash +npx intent scaffold +``` + +Validate your skill files: + +```bash +npx intent validate +``` + +Copy CI and Oz workflow templates into your repo: + +```bash +npx intent setup +``` + +## CLI Commands + +| Command | Description | +| ----------------------- | ----------------------------------------------- | +| `intent init` | Inject intent discovery into agent config files | +| `intent list [--json]` | Discover intent-enabled packages | +| `intent meta` | List meta-skills for library maintainers | +| `intent scaffold` | Print the guided skill generation prompt | +| `intent validate [dir]` | Validate SKILL.md files | +| `intent setup` | Copy CI/Oz workflow templates | +| `intent stale [--json]` | Check skills for version drift | +| `intent feedback` | Submit skill feedback | + +## License + +[MIT](./LICENSE) diff --git a/packages/playbooks/meta/domain-discovery/SKILL.md b/packages/intent/meta/domain-discovery/SKILL.md similarity index 99% rename from packages/playbooks/meta/domain-discovery/SKILL.md rename to packages/intent/meta/domain-discovery/SKILL.md index b53b3918..0bc4499a 100644 --- a/packages/playbooks/meta/domain-discovery/SKILL.md +++ b/packages/intent/meta/domain-discovery/SKILL.md @@ -650,7 +650,7 @@ fill only the **Domain Discovery** section now. Leave the other sections in place for later steps to complete. ``` -# Playbook Meta Skill Feedback +# Intent Meta Skill Feedback ## Domain Discovery - What worked well: diff --git a/packages/playbooks/meta/generate-skill/SKILL.md b/packages/intent/meta/generate-skill/SKILL.md similarity index 98% rename from packages/playbooks/meta/generate-skill/SKILL.md rename to packages/intent/meta/generate-skill/SKILL.md index 8a5bf8df..3e57466e 100644 --- a/packages/playbooks/meta/generate-skill/SKILL.md +++ b/packages/intent/meta/generate-skill/SKILL.md @@ -24,7 +24,7 @@ metadata: # Skill Generation -You are generating a SKILL.md file for the `@tanstack/playbooks` agent skills +You are generating a SKILL.md file for the `@tanstack/intent` agent skills repo. Skills in this repo are written for coding agents (Claude Code, Cursor, Copilot, Warp Oz, Codex), not for human readers. Your output will be loaded into an agent's context window and used to guide code generation. @@ -381,7 +381,7 @@ If no draft exists, create it using this exact template. ``` -# Playbook Meta Skill Feedback +# Intent Meta Skill Feedback ## Domain Discovery @@ -414,6 +414,6 @@ If no draft exists, create it using this exact template. ``` Ask the maintainer if they want to edit the feedback. Then ask if you should -send it as a GitHub issue to TanStack/playbooks. Use the issue title: -`[meta-feedback] playbook meta skill`. Only submit if they confirm. +send it as a GitHub issue to TanStack/intent. Use the issue title: +`[meta-feedback] intent meta skill`. Only submit if they confirm. ``` diff --git a/packages/playbooks/meta/skill-staleness-check/SKILL.md b/packages/intent/meta/skill-staleness-check/SKILL.md similarity index 97% rename from packages/playbooks/meta/skill-staleness-check/SKILL.md rename to packages/intent/meta/skill-staleness-check/SKILL.md index 53e98a84..2a675b8c 100644 --- a/packages/playbooks/meta/skill-staleness-check/SKILL.md +++ b/packages/intent/meta/skill-staleness-check/SKILL.md @@ -1,7 +1,7 @@ --- name: skill-staleness-check description: > - Evaluate playbook skills for staleness when source files change in upstream + Evaluate intent skills for staleness when source files change in upstream TanStack package repos. Driven by Oz automation on webhook trigger. Matches changed files against metadata.sources, evaluates whether diffs affect documented behavior, rewrites stale skills using skill-generate, checks @@ -21,7 +21,7 @@ metadata: # Skill Staleness Check -You are an Oz automation agent. Your job is to evaluate whether playbook +You are an Oz automation agent. Your job is to evaluate whether intent skills are stale after upstream source changes, and if so, update them and open PRs. You act autonomously end-to-end. PRs contain already-updated skill content, not suggestions. @@ -46,7 +46,7 @@ Webhook payload from an upstream package repo merge to main: ## Step 1 — Match changed files to skills -Read all SKILL.md files under `packages/playbooks/skills/`. For each skill, +Read all SKILL.md files under `packages/intent/skills/`. For each skill, extract `sources` from the frontmatter. Match `changed_files` from the webhook against `sources` entries across all @@ -243,7 +243,7 @@ requires: - `gh` CLI installed and authenticated - Read access to upstream TanStack package repos (query, router, db, form, table) -- Write access to the playbooks repo for creating branches and PRs +- Write access to the intent repo for creating branches and PRs ### Rate limiting diff --git a/packages/playbooks/meta/templates/oz/domain-discovery.md b/packages/intent/meta/templates/oz/domain-discovery.md similarity index 89% rename from packages/playbooks/meta/templates/oz/domain-discovery.md rename to packages/intent/meta/templates/oz/domain-discovery.md index bc4641ab..1a8a0ad5 100644 --- a/packages/playbooks/meta/templates/oz/domain-discovery.md +++ b/packages/intent/meta/templates/oz/domain-discovery.md @@ -19,7 +19,7 @@ Use the `generate-skills-oz.yml` workflow template instead. ## Prompt -Read the meta-skill at `node_modules/@tanstack/playbooks/meta/domain-discovery/SKILL.md` +Read the meta-skill at `node_modules/@tanstack/intent/meta/domain-discovery/SKILL.md` and follow its instructions for the library described below. ### Library details @@ -50,4 +50,4 @@ Tell the user: - "Domain discovery complete. Artifacts written to domain_map.yaml and skill_spec.md." - "Next step: load the tree-generator meta-skill to generate SKILL.md files." -- "Run `npx playbook feedback --meta --interactive` to share how this went." +- "Run `npx intent feedback --meta --interactive` to share how this went." diff --git a/packages/playbooks/meta/templates/oz/feedback-collection.md b/packages/intent/meta/templates/oz/feedback-collection.md similarity index 85% rename from packages/playbooks/meta/templates/oz/feedback-collection.md rename to packages/intent/meta/templates/oz/feedback-collection.md index bbd269ac..8b5ea2cf 100644 --- a/packages/playbooks/meta/templates/oz/feedback-collection.md +++ b/packages/intent/meta/templates/oz/feedback-collection.md @@ -1,13 +1,13 @@ # Oz Prompt: Meta-Skill Feedback Collection Use this prompt to collect structured feedback about the meta-skill experience -and submit it to TanStack/playbooks. +and submit it to TanStack/intent. --- ## Prompt -You are collecting feedback about the TanStack Playbooks meta-skill experience. +You are collecting feedback about the TanStack Intent meta-skill experience. The user just ran one or more meta-skills (domain-discovery, tree-generator, generate-skill, or skill-staleness-check) and you need to capture what worked and what didn't. @@ -63,7 +63,7 @@ Walk through these questions one at a time: } ``` -2. Save to `playbook-meta-feedback.json` -3. Run: `npx playbook feedback --meta --file playbook-meta-feedback.json` -4. If `gh` CLI is available, this submits directly to TanStack/playbooks. +2. Save to `intent-meta-feedback.json` +3. Run: `npx intent feedback --meta --file intent-meta-feedback.json` +4. If `gh` CLI is available, this submits directly to TanStack/intent. Otherwise, it saves a markdown file the user can paste into a GitHub Discussion. diff --git a/packages/playbooks/meta/templates/oz/skill-update.md b/packages/intent/meta/templates/oz/skill-update.md similarity index 83% rename from packages/playbooks/meta/templates/oz/skill-update.md rename to packages/intent/meta/templates/oz/skill-update.md index 58df6ddc..23253044 100644 --- a/packages/playbooks/meta/templates/oz/skill-update.md +++ b/packages/intent/meta/templates/oz/skill-update.md @@ -6,13 +6,13 @@ version change and surgically update any that are outdated. ## Prerequisites - Existing skills in `skills/` directory -- `@tanstack/playbooks` installed +- `@tanstack/intent` installed --- ## Prompt -Read the meta-skill at `node_modules/@tanstack/playbooks/meta/skill-staleness-check/SKILL.md` +Read the meta-skill at `node_modules/@tanstack/intent/meta/skill-staleness-check/SKILL.md` and follow its instructions. ### Context @@ -30,11 +30,11 @@ and follow its instructions. 4. For each skill, classify the impact: no-impact, version-bump, content-update, breaking 5. For content updates and breaking changes: - Read the generate-skill meta-skill at - `node_modules/@tanstack/playbooks/meta/generate-skill/SKILL.md` + `node_modules/@tanstack/intent/meta/generate-skill/SKILL.md` - Use regeneration mode — surgical updates, not full rewrites - Add old patterns as new Common Mistake entries for breaking changes 6. Bump `library_version` in all updated skill frontmatter -7. Run `npx playbook validate skills/` to verify +7. Run `npx intent validate skills/` to verify ### Output diff --git a/packages/playbooks/meta/templates/oz/tree-generation.md b/packages/intent/meta/templates/oz/tree-generation.md similarity index 78% rename from packages/playbooks/meta/templates/oz/tree-generation.md rename to packages/intent/meta/templates/oz/tree-generation.md index fcbd6387..0e79ffcc 100644 --- a/packages/playbooks/meta/templates/oz/tree-generation.md +++ b/packages/intent/meta/templates/oz/tree-generation.md @@ -6,13 +6,13 @@ to generate a complete skill tree from domain discovery artifacts. ## Prerequisites - `domain_map.yaml` and `skill_spec.md` in the repo (from domain discovery) -- `@tanstack/playbooks` installed +- `@tanstack/intent` installed --- ## Prompt -Read the meta-skill at `node_modules/@tanstack/playbooks/meta/tree-generator/SKILL.md` +Read the meta-skill at `node_modules/@tanstack/intent/meta/tree-generator/SKILL.md` and follow Workflow A (Generate) instructions. ### Inputs @@ -27,7 +27,7 @@ and follow Workflow A (Generate) instructions. 2. Plan the file tree based on the skill inventory 3. Generate all SKILL.md files following the tree-generator spec 4. Write files under `skills/` in the appropriate package directory -5. Run `npx playbook validate skills/` to verify all files pass +5. Run `npx intent validate skills/` to verify all files pass 6. Fix any validation errors ### Output structure @@ -44,5 +44,5 @@ Tell the user: - "Next steps:" - "1. Review the generated skills for accuracy" - "2. Add `\"skills\"` to your package.json `files` array" - - "3. Add the `playbook` field to your package.json" - - "4. Run `npx playbook feedback --meta --interactive` to share how this went" + - "3. Add the `intent` field to your package.json" + - "4. Run `npx intent feedback --meta --interactive` to share how this went" diff --git a/packages/playbooks/meta/templates/workflows/generate-skills-oz.yml b/packages/intent/meta/templates/workflows/generate-skills-oz.yml similarity index 82% rename from packages/playbooks/meta/templates/workflows/generate-skills-oz.yml rename to packages/intent/meta/templates/workflows/generate-skills-oz.yml index afee86e8..b60754d8 100644 --- a/packages/playbooks/meta/templates/workflows/generate-skills-oz.yml +++ b/packages/intent/meta/templates/workflows/generate-skills-oz.yml @@ -6,7 +6,7 @@ # Requirements: # - WARP_API_KEY repository secret (from Warp Settings > Platform) # -# Template variables (replaced by `playbook setup`): +# Template variables (replaced by `intent setup`): # {{PACKAGE_NAME}} — e.g. @tanstack/query # {{REPO}} — e.g. TanStack/query # {{DOCS_PATH}} — e.g. docs/ @@ -17,7 +17,7 @@ on: workflow_dispatch: inputs: docs_path: - description: 'Path to library docs (default from playbook config)' + description: 'Path to library docs (default from intent config)' required: false default: '{{DOCS_PATH}}' type: string @@ -47,7 +47,7 @@ jobs: node-version: 20 - name: Install dependencies - run: npm install @tanstack/playbooks yaml + run: npm install @tanstack/intent yaml - name: Run Oz agent uses: warpdotdev/oz-agent-action@v1 @@ -57,7 +57,7 @@ jobs: ## Setup - 1. Read the meta-skill at node_modules/@tanstack/playbooks/meta/domain-discovery/SKILL.md + 1. Read the meta-skill at node_modules/@tanstack/intent/meta/domain-discovery/SKILL.md 2. The library source is in this repo at {{DOCS_PATH}} 3. The package name is {{PACKAGE_NAME}} and the repo is {{REPO}} @@ -68,16 +68,16 @@ jobs: discovery mode (Phase 1 + Phase 3 only — skip interview phases). After producing the domain map, read the meta-skill at - node_modules/@tanstack/playbooks/meta/tree-generator/SKILL.md and + node_modules/@tanstack/intent/meta/tree-generator/SKILL.md and run Workflow A to generate the complete skill tree under skills/. ## Output 1. Write domain_map.yaml and skill_spec.md to the repo root 2. Write generated SKILL.md files under the appropriate skills/ directory - 3. Run `npx playbook validate skills/` to verify all files pass + 3. Run `npx intent validate skills/` to verify all files pass 4. Create a branch `skills/generate-{{PACKAGE_NAME}}` and open a PR with: - - Title: "feat: generate playbook skills for {{PACKAGE_NAME}}" + - Title: "feat: generate intent skills for {{PACKAGE_NAME}}" - Body summarizing what was generated and any gaps flagged - Co-Authored-By: Oz diff --git a/packages/playbooks/meta/templates/workflows/notify-playbooks.yml b/packages/intent/meta/templates/workflows/notify-playbooks.yml similarity index 64% rename from packages/playbooks/meta/templates/workflows/notify-playbooks.yml rename to packages/intent/meta/templates/workflows/notify-playbooks.yml index 57165b30..cc186856 100644 --- a/packages/playbooks/meta/templates/workflows/notify-playbooks.yml +++ b/packages/intent/meta/templates/workflows/notify-playbooks.yml @@ -1,19 +1,19 @@ -# notify-playbooks.yml — Drop this into your library repo's .github/workflows/ +# notify-intent.yml — Drop this into your library repo's .github/workflows/ # -# Fires a repository_dispatch event to TanStack/playbooks whenever docs or +# 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 playbooks repo. +# check workflow in the intent repo. # # Requirements: -# - A fine-grained PAT with contents:write on TanStack/playbooks stored -# as the PLAYBOOK_NOTIFY_TOKEN repository secret. +# - A fine-grained PAT with contents:write on TanStack/intent stored +# as the INTENT_NOTIFY_TOKEN repository secret. # -# Template variables (replaced by `playbook setup`): +# Template variables (replaced by `intent setup`): # {{PACKAGE_NAME}} — e.g. @tanstack/query # {{DOCS_PATH}} — e.g. docs/** # {{SRC_PATH}} — e.g. packages/query-core/src/** -name: Notify Playbooks +name: Notify Intent on: push: @@ -24,7 +24,7 @@ on: jobs: notify: - name: Notify TanStack Playbooks + name: Notify TanStack Intent runs-on: ubuntu-latest steps: - name: Checkout @@ -38,11 +38,11 @@ jobs: FILES=$(git diff --name-only HEAD~1 HEAD | jq -R -s -c 'split("\n") | map(select(length > 0))') echo "files=$FILES" >> "$GITHUB_OUTPUT" - - name: Dispatch to playbooks repo + - name: Dispatch to intent repo uses: peter-evans/repository-dispatch@v3 with: - token: ${{ secrets.PLAYBOOK_NOTIFY_TOKEN }} - repository: TanStack/playbooks + token: ${{ secrets.INTENT_NOTIFY_TOKEN }} + repository: TanStack/intent event-type: skill-check client-payload: | { diff --git a/packages/playbooks/meta/templates/workflows/update-skills-oz.yml b/packages/intent/meta/templates/workflows/update-skills-oz.yml similarity index 89% rename from packages/playbooks/meta/templates/workflows/update-skills-oz.yml rename to packages/intent/meta/templates/workflows/update-skills-oz.yml index c13d6305..1b4761a8 100644 --- a/packages/playbooks/meta/templates/workflows/update-skills-oz.yml +++ b/packages/intent/meta/templates/workflows/update-skills-oz.yml @@ -8,7 +8,7 @@ # - WARP_API_KEY repository secret # - Skills must already exist in the repo (run generate-skills-oz first) # -# Template variables (replaced by `playbook setup`): +# Template variables (replaced by `intent setup`): # {{PACKAGE_NAME}} — e.g. @tanstack/query # {{REPO}} — e.g. TanStack/query @@ -46,7 +46,7 @@ jobs: node-version: 20 - name: Install dependencies - run: npm install @tanstack/playbooks yaml + run: npm install @tanstack/intent yaml - name: Determine version id: version @@ -66,7 +66,7 @@ jobs: ## Setup - 1. Read the meta-skill at node_modules/@tanstack/playbooks/meta/skill-staleness-check/SKILL.md + 1. Read the meta-skill at node_modules/@tanstack/intent/meta/skill-staleness-check/SKILL.md 2. The package is {{PACKAGE_NAME}} in repo {{REPO}} 3. The new version is ${{ steps.version.outputs.version }} @@ -79,7 +79,7 @@ jobs: 3. Read the CHANGELOG or migration guide for changes between versions 4. Classify each skill: no-impact, version-bump-only, content-update, or breaking-change 5. For skills needing updates, read the generate-skill meta-skill at - node_modules/@tanstack/playbooks/meta/generate-skill/SKILL.md and + node_modules/@tanstack/intent/meta/generate-skill/SKILL.md and use regeneration mode (surgical updates, not full rewrites) ## Output @@ -88,7 +88,7 @@ jobs: If skills need updating: 1. Update the affected SKILL.md files - 2. Run `npx playbook validate skills/` to verify + 2. Run `npx intent validate skills/` to verify 3. Create branch `skills/update-${{ steps.version.outputs.version }}` 4. Open a PR with: - Title: "skill: update for {{PACKAGE_NAME}}@${{ steps.version.outputs.version }}" diff --git a/packages/playbooks/meta/templates/workflows/validate-skills.yml b/packages/intent/meta/templates/workflows/validate-skills.yml similarity index 87% rename from packages/playbooks/meta/templates/workflows/validate-skills.yml rename to packages/intent/meta/templates/workflows/validate-skills.yml index bdef59e9..8f39716a 100644 --- a/packages/playbooks/meta/templates/workflows/validate-skills.yml +++ b/packages/intent/meta/templates/workflows/validate-skills.yml @@ -25,8 +25,8 @@ jobs: with: node-version: 20 - - name: Install playbooks CLI - run: npm install -g @tanstack/playbooks + - name: Install intent CLI + run: npm install -g @tanstack/intent - name: Find and validate skills run: | @@ -39,14 +39,14 @@ jobs: for dir in packages/*/skills; do if [ -d "$dir" ]; then echo "Validating $dir..." - playbook validate "$dir" + intent validate "$dir" fi done exit 0 fi if [ -n "$SKILLS_DIR" ]; then - playbook validate "$SKILLS_DIR" + intent validate "$SKILLS_DIR" else echo "No skills/ directory found — skipping validation." fi diff --git a/packages/playbooks/meta/tree-generator/SKILL.md b/packages/intent/meta/tree-generator/SKILL.md similarity index 99% rename from packages/playbooks/meta/tree-generator/SKILL.md rename to packages/intent/meta/tree-generator/SKILL.md index d7ed5a6d..8cdf7e97 100644 --- a/packages/playbooks/meta/tree-generator/SKILL.md +++ b/packages/intent/meta/tree-generator/SKILL.md @@ -40,7 +40,7 @@ Every skill has a `type` field in its frontmatter. Valid types: | `composition` | Integration between two or more libraries | `electric-drizzle` | | `security` | Audit checklist or security validation | `electric-security-check` | -Agents discover skills via `tanstack playbook list` and read them directly +Agents discover skills via `tanstack intent list` and read them directly from `node_modules`. Framework skills declare a `requires` dependency on their core skill so agents load them in the right order. @@ -219,7 +219,7 @@ skills/ ``` **Router skill:** A router skill (lightweight entry point with a decision -table) is optional. If the playbook CLI provides `list` and `show` +table) is optional. If the intent CLI provides `list` and `show` commands, agents can discover skills directly without a router. Only create a router skill if the skill set is large enough (15+) that browsing the list is insufficient, or if the nested structure needs @@ -828,7 +828,7 @@ Domain Discovery, keep it and fill only the **Tree Generator** section. If no draft exists, create it using this exact template. ``` -# Playbook Meta Skill Feedback +# Intent Meta Skill Feedback ## Domain Discovery - What worked well: diff --git a/packages/playbooks/package.json b/packages/intent/package.json similarity index 54% rename from packages/playbooks/package.json rename to packages/intent/package.json index 1a0fc5bb..013a3d07 100644 --- a/packages/playbooks/package.json +++ b/packages/intent/package.json @@ -1,26 +1,25 @@ { - "name": "@tanstack/playbooks", - "version": "0.1.0", + "name": "@tanstack/intent", + "version": "0.0.1", "description": "Ship compositional knowledge for AI coding agents alongside your npm packages", "license": "MIT", "type": "module", "repository": { "type": "git", - "url": "https://github.com/tanstack/playbooks" + "url": "https://github.com/tanstack/intent" }, "exports": { ".": { "import": "./dist/index.mjs", "types": "./dist/index.d.mts" }, - "./playbook-library": { - "import": "./dist/playbook-library.mjs" + "./intent-library": { + "import": "./dist/intent-library.mjs" } }, "bin": { - "playbooks": "dist/cli.mjs", - "playbook": "dist/cli.mjs", - "playbook-library": "dist/playbook-library.mjs" + "intent": "dist/cli.mjs", + "intent-library": "dist/intent-library.mjs" }, "files": [ "dist", @@ -33,7 +32,7 @@ "tsdown": "^0.19.0" }, "scripts": { - "build": "tsdown src/index.ts src/cli.ts src/setup.ts src/playbook-library.ts src/library-scanner.ts --format esm --dts", + "build": "tsdown src/index.ts src/cli.ts src/setup.ts src/intent-library.ts src/library-scanner.ts --format esm --dts", "test:lib": "vitest run" } } diff --git a/packages/playbooks/src/cli.ts b/packages/intent/src/cli.ts similarity index 86% rename from packages/playbooks/src/cli.ts rename to packages/intent/src/cli.ts index 0e16fbdd..fe5def33 100644 --- a/packages/playbooks/src/cli.ts +++ b/packages/intent/src/cli.ts @@ -6,7 +6,7 @@ import { release } from 'node:os' import { dirname, join, relative, sep } from 'node:path' import { fileURLToPath } from 'node:url' import { parse as parseYaml } from 'yaml' -import { scanForPlaybooks } from './scanner.js' +import { scanForIntents } from './scanner.js' import type { ScanResult } from './types.js' import { findSkillFiles, parseFrontmatter } from './utils.js' @@ -29,7 +29,7 @@ async function cmdList(args: string[]): Promise { let result: ScanResult try { - result = await scanForPlaybooks() + result = await scanForIntents() } catch (err) { console.error((err as Error).message) process.exit(1) @@ -41,7 +41,7 @@ async function cmdList(args: string[]): Promise { } if (result.packages.length === 0) { - console.log('No playbook-enabled packages found.') + console.log('No intent-enabled packages found.') if (result.warnings.length > 0) { console.log(`\nWarnings:`) for (const w of result.warnings) console.log(` ⚠ ${w}`) @@ -49,11 +49,11 @@ async function cmdList(args: string[]): Promise { return } - console.log(`Playbook-enabled packages (${result.packages.length} found):\n`) + console.log(`Intent-enabled packages (${result.packages.length} found):\n`) for (const pkg of result.packages) { - const reqStr = pkg.playbook.requires?.length - ? ` (requires: ${pkg.playbook.requires.join(', ')})` + const reqStr = pkg.intent.requires?.length + ? ` (requires: ${pkg.intent.requires.join(', ')})` : '' console.log(`${pkg.name} v${pkg.version}${reqStr}`) @@ -103,7 +103,7 @@ function cmdMeta(): void { } console.log(`\nUsage: load the SKILL.md into your AI agent conversation.`) - console.log(`Path: node_modules/@tanstack/playbooks/meta//SKILL.md`) + console.log(`Path: node_modules/@tanstack/intent/meta//SKILL.md`) } function cmdValidate(args: string[]): void { @@ -264,8 +264,8 @@ function cmdScaffold(): void { ) } - const prompt = `You are an AI assistant helping a library maintainer scaffold Playbook skills. -You MUST use the Playbooks meta skills in this exact order and follow their output requirements. + const prompt = `You are an AI assistant helping a library maintainer scaffold Intent skills. +You MUST use the Intent meta skills in this exact order and follow their output requirements. Before you start, ask the maintainer for their skills root path. - Default: skills/ @@ -295,17 +295,17 @@ Guidance for the maintainer: - Use the library's actual terminology from docs and source. At the end, produce a single Markdown feedback doc with three sections (Domain Discovery, Tree Generator, Generate Skill). -Ask if the maintainer wants to edit it, then ask if you should send it as a GitHub issue to TanStack/playbooks. -Use the issue title: [meta-feedback] playbook meta skill. +Ask if the maintainer wants to edit it, then ask if you should send it as a GitHub issue to TanStack/intent. +Use the issue title: [meta-feedback] intent meta skill. Finish with a short checklist: -- Run npx playbook validate +- Run npx intent validate - Commit skills/ and skills/_artifacts/ (artifacts are repo-only) - Exclude skills/_artifacts/ from package publishing -- Add README snippet: If you use an AI agent, run npx playbook init +- Add README snippet: If you use an AI agent, run npx intent init ` - console.log('🚀 Playbook Scaffold Prompt') + console.log('🚀 Intent Scaffold Prompt') console.log('✨ Copy the prompt below into your AI agent:\n') console.log(prompt) @@ -321,18 +321,18 @@ Finish with a short checklist: // Main // --------------------------------------------------------------------------- -const USAGE = `TanStack Playbooks CLI +const USAGE = `TanStack Intent CLI Usage: - playbook list [--json] Discover playbook-enabled packages - playbook meta List meta-skills for maintainers - playbook validate [] Validate skill files (default: skills/) - playbook init Set up playbook discovery in agent configs - playbook scaffold Print maintainer scaffold prompt - playbook setup [--workflows] [--oz] [--all] Copy CI/Oz templates into your repo - playbook stale Check skills for staleness - playbook feedback --submit --file Submit skill feedback - playbook feedback --meta --submit --file Submit meta-skill feedback` + intent list [--json] Discover intent-enabled packages + intent meta List meta-skills for maintainers + intent validate [] Validate skill files (default: skills/) + intent init Set up intent discovery in agent configs + intent scaffold Print maintainer scaffold prompt + intent setup [--workflows] [--oz] [--all] Copy CI/Oz templates into your repo + intent stale Check skills for staleness + intent feedback --submit --file Submit skill feedback + intent feedback --meta --submit --file Submit meta-skill feedback` const command = process.argv[2] const commandArgs = process.argv.slice(3) @@ -352,8 +352,7 @@ switch (command) { const initRoot = process.cwd() const result = runInit(initRoot) - for (const f of result.injected) - console.log(`✓ Added playbook block to ${f}`) + for (const f of result.injected) console.log(`✓ Added intent block to ${f}`) for (const f of result.skipped) console.log(` Already present in ${f}`) for (const f of result.created) console.log(`✓ Created ${f}`) @@ -363,7 +362,7 @@ switch (command) { console.log( 'No agent config files found (AGENTS.md, CLAUDE.md, .cursorrules, .github/copilot-instructions.md).', ) - console.log('Create one of these files and run playbook init again.') + console.log('Create one of these files and run intent init again.') } } @@ -376,7 +375,7 @@ switch (command) { } case 'stale': { const { checkStaleness } = await import('./staleness.js') - const { scanForPlaybooks: scanStale } = await import('./scanner.js') + const { scanForIntents: scanStale } = await import('./scanner.js') let staleResult try { staleResult = await scanStale() @@ -386,7 +385,7 @@ switch (command) { } if (staleResult.packages.length === 0) { - console.log('No playbook-enabled packages found.') + console.log('No intent-enabled packages found.') break } diff --git a/packages/playbooks/src/feedback.ts b/packages/intent/src/feedback.ts similarity index 95% rename from packages/playbooks/src/feedback.ts rename to packages/intent/src/feedback.ts index 43f8edc2..33f311a4 100644 --- a/packages/playbooks/src/feedback.ts +++ b/packages/intent/src/feedback.ts @@ -3,11 +3,11 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { join } from 'node:path' import type { FeedbackPayload, + IntentProjectConfig, MetaFeedbackPayload, - PlaybookProjectConfig, } from './types.js' -const META_FEEDBACK_REPO = 'TanStack/playbooks' +const META_FEEDBACK_REPO = 'TanStack/intent' // --------------------------------------------------------------------------- // Secret detection @@ -52,23 +52,23 @@ function getHomeConfigDir(): string { } export function resolveFrequency(root: string): string { - // 1. User override (~/.config/playbook/config.json) - const userConfigPath = join(getHomeConfigDir(), 'playbook', 'config.json') + // 1. User override (~/.config/intent/config.json) + const userConfigPath = join(getHomeConfigDir(), 'intent', 'config.json') try { const userCfg = JSON.parse( readFileSync(userConfigPath, 'utf8'), - ) as Partial + ) as Partial if (userCfg.feedback?.frequency) return userCfg.feedback.frequency } catch { /* fallback */ } // 2. Project config - const projectConfigPath = join(root, 'playbook.config.json') + const projectConfigPath = join(root, 'intent.config.json') try { const projCfg = JSON.parse( readFileSync(projectConfigPath, 'utf8'), - ) as Partial + ) as Partial if (projCfg.feedback?.frequency) return projCfg.feedback.frequency } catch { /* fallback */ @@ -376,9 +376,9 @@ export function runFeedback(args: string[]): void { if (!submitFlag || !filePath) { if (isMeta) { - console.error('Usage: playbook feedback --meta --submit --file ') + console.error('Usage: intent feedback --meta --submit --file ') } else { - console.error('Usage: playbook feedback --submit --file ') + console.error('Usage: intent feedback --submit --file ') } process.exit(1) } @@ -415,7 +415,7 @@ export function runFeedback(args: string[]): void { } const payload = raw as MetaFeedbackPayload - const fallbackPath = `playbook-meta-feedback-${dateSuffix}.md` + const fallbackPath = `intent-meta-feedback-${dateSuffix}.md` const result = submitMetaFeedback(payload, { ghAvailable, @@ -450,7 +450,7 @@ export function runFeedback(args: string[]): void { const payload = raw as FeedbackPayload const repo = payload.package.replace(/^@/, '').replace(/\//, '/') - const fallbackPath = `playbook-feedback-${dateSuffix}.md` + const fallbackPath = `intent-feedback-${dateSuffix}.md` const result = submitFeedback(payload, repo, { ghAvailable, diff --git a/packages/playbooks/src/index.ts b/packages/intent/src/index.ts similarity index 80% rename from packages/playbooks/src/index.ts rename to packages/intent/src/index.ts index bec9b7fc..3bd4eedd 100644 --- a/packages/playbooks/src/index.ts +++ b/packages/intent/src/index.ts @@ -1,4 +1,4 @@ -export { scanForPlaybooks } from './scanner.js' +export { scanForIntents } from './scanner.js' export { checkStaleness } from './staleness.js' export { containsSecrets, @@ -13,8 +13,8 @@ export { } from './feedback.js' export { detectAgentConfigs, - hasPlaybookBlock, - injectPlaybookBlock, + hasIntentBlock, + injectIntentBlock, readProjectConfig, runInit, writeProjectConfig, @@ -24,11 +24,11 @@ export { runSetup } from './setup.js' export type { AgentName, FeedbackPayload, + IntentConfig, + IntentPackage, + IntentProjectConfig, MetaFeedbackPayload, MetaSkillName, - PlaybookConfig, - PlaybookPackage, - PlaybookProjectConfig, ScanResult, SkillEntry, StalenessReport, diff --git a/packages/playbooks/src/init.ts b/packages/intent/src/init.ts similarity index 74% rename from packages/playbooks/src/init.ts rename to packages/intent/src/init.ts index 379b9351..95056c11 100644 --- a/packages/playbooks/src/init.ts +++ b/packages/intent/src/init.ts @@ -1,6 +1,6 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs' import { join } from 'node:path' -import type { PlaybookProjectConfig } from './types.js' +import type { IntentProjectConfig } from './types.js' // --------------------------------------------------------------------------- // Constants @@ -13,16 +13,16 @@ const AGENT_CONFIG_FILES = [ '.github/copilot-instructions.md', ] as const -const PLAYBOOK_BLOCK_MARKER = '## Playbook Skills' +const INTENT_BLOCK_MARKER = '## Intent Skills' -const PLAYBOOK_BLOCK = `## Playbook Skills +const INTENT_BLOCK = `## Intent Skills -This project uses TanStack Playbooks. Run \`npx playbook list\` to discover +This project uses TanStack Intent. Run \`npx intent list\` to discover available AI coding skills. Before working with a library that has skills, read the relevant SKILL.md file at the path shown in the list output. ` -const DEFAULT_CONFIG: PlaybookProjectConfig = { +const DEFAULT_CONFIG: IntentProjectConfig = { feedback: { frequency: 'every-5', }, @@ -42,17 +42,17 @@ export function detectAgentConfigs(root: string): string[] { // Injection // --------------------------------------------------------------------------- -export function hasPlaybookBlock(filePath: string): boolean { +export function hasIntentBlock(filePath: string): boolean { try { const content = readFileSync(filePath, 'utf8') - return content.includes(PLAYBOOK_BLOCK_MARKER) + return content.includes(INTENT_BLOCK_MARKER) } catch { return false } } -export function injectPlaybookBlock(filePath: string): boolean { - if (hasPlaybookBlock(filePath)) return false +export function injectIntentBlock(filePath: string): boolean { + if (hasIntentBlock(filePath)) return false let content: string try { @@ -63,7 +63,7 @@ export function injectPlaybookBlock(filePath: string): boolean { const separator = content.length > 0 && !content.endsWith('\n\n') ? '\n\n' : '' - const updated = content + separator + PLAYBOOK_BLOCK + const updated = content + separator + INTENT_BLOCK writeFileSync(filePath, updated) return true } @@ -73,17 +73,17 @@ export function injectPlaybookBlock(filePath: string): boolean { // --------------------------------------------------------------------------- export function writeProjectConfig(root: string): string { - const configPath = join(root, 'playbook.config.json') + const configPath = join(root, 'intent.config.json') if (!existsSync(configPath)) { writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + '\n') } return configPath } -export function readProjectConfig(root: string): PlaybookProjectConfig | null { - const configPath = join(root, 'playbook.config.json') +export function readProjectConfig(root: string): IntentProjectConfig | null { + const configPath = join(root, 'intent.config.json') try { - return JSON.parse(readFileSync(configPath, 'utf8')) as PlaybookProjectConfig + return JSON.parse(readFileSync(configPath, 'utf8')) as IntentProjectConfig } catch { return null } @@ -107,7 +107,7 @@ export function runInit(root: string): InitResult { const created: string[] = [] for (const filePath of detected) { - if (injectPlaybookBlock(filePath)) { + if (injectIntentBlock(filePath)) { injected.push(filePath) } else { skipped.push(filePath) diff --git a/packages/playbooks/src/playbook-library.ts b/packages/intent/src/intent-library.ts similarity index 89% rename from packages/playbooks/src/playbook-library.ts rename to packages/intent/src/intent-library.ts index fb50f753..f0e2984d 100644 --- a/packages/playbooks/src/playbook-library.ts +++ b/packages/intent/src/intent-library.ts @@ -19,7 +19,7 @@ async function cmdList(): Promise { } if (result.packages.length === 0) { - console.log('No playbook-enabled packages found.') + console.log('No intent-enabled packages found.') if (result.warnings.length > 0) { console.log('\nWarnings:') for (const w of result.warnings) console.log(` ⚠ ${w}`) @@ -80,14 +80,14 @@ Follow these steps in order: 1. CHECK FOR EXISTING MAPPINGS Search the project's agent config files (CLAUDE.md, AGENTS.md, .cursorrules, .github/copilot-instructions.md) for a block delimited by: - - + + - If found: show the user the current mappings and ask "What would you like to update?" Then skip to step 4 with their requested changes. - If not found: continue to step 2. 2. DISCOVER AVAILABLE SKILLS - Run: playbook list + Run: intent list This outputs each skill's name, description, and full path — grouped by package. 3. SCAN THE REPOSITORY @@ -107,20 +107,20 @@ Follow these steps in order: Once you have the full set of mappings, write or update the agent config file (prefer CLAUDE.md; create it if none exists) with this exact block: - + # Skill mappings — when working in these areas, load the linked skill file into context. skills: - task: "describe the task or code area here" load: "node_modules/package-name/skills/skill-name/SKILL.md" - + Rules: - Use the user's own words for task descriptions - - Include the exact path from \`playbook list\` output so agents can load it directly + - Include the exact path from \`intent list\` output so agents can load it directly - Keep entries concise — this block is read on every agent task - Preserve all content outside the block tags unchanged` - console.log('🚀 Playbook Install') + console.log('🚀 Intent Install') console.log('✨ Copy the prompt below into your AI agent:\n') console.log(prompt) @@ -136,11 +136,11 @@ skills: // Main // --------------------------------------------------------------------------- -const USAGE = `TanStack Playbooks +const USAGE = `TanStack Intent Usage: - playbook list List all available skills from this library and its dependencies - playbook install Set up skill-to-task mappings in your agent config` + intent list List all available skills from this library and its dependencies + intent install Set up skill-to-task mappings in your agent config` const command = process.argv[2] diff --git a/packages/playbooks/src/library-scanner.ts b/packages/intent/src/library-scanner.ts similarity index 96% rename from packages/playbooks/src/library-scanner.ts rename to packages/intent/src/library-scanner.ts index e0771cb3..7f5fe2ae 100644 --- a/packages/playbooks/src/library-scanner.ts +++ b/packages/intent/src/library-scanner.ts @@ -41,10 +41,10 @@ function findHomeDir(scriptPath: string): string | null { } } -function hasPlaybookBin(pkg: Record): boolean { +function hasIntentBin(pkg: Record): boolean { const bin = pkg.bin if (!bin || typeof bin !== 'object') return false - return 'playbook' in (bin as Record) + return 'intent' in (bin as Record) } function getDeps(pkg: Record): string[] { @@ -147,7 +147,7 @@ export async function scanLibrary( const depDir = join(nodeModulesDir, depName) if (!existsSync(depDir)) continue const depPkg = readPkgJson(depDir) - if (depPkg && hasPlaybookBin(depPkg)) { + if (depPkg && hasIntentBin(depPkg)) { processPackage(depName, depDir) } } diff --git a/packages/playbooks/src/scanner.ts b/packages/intent/src/scanner.ts similarity index 88% rename from packages/playbooks/src/scanner.ts rename to packages/intent/src/scanner.ts index d2b56407..870f89b3 100644 --- a/packages/playbooks/src/scanner.ts +++ b/packages/intent/src/scanner.ts @@ -1,8 +1,8 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs' import { join, relative, sep } from 'node:path' import type { - PlaybookConfig, - PlaybookPackage, + IntentConfig, + IntentPackage, ScanResult, SkillEntry, } from './types.js' @@ -18,7 +18,7 @@ function detectPackageManager(root: string): PackageManager { // Check for unsupported environments first if (existsSync(join(root, '.pnp.cjs')) || existsSync(join(root, '.pnp.js'))) { throw new Error( - 'Yarn PnP is not yet supported. Add `nodeLinker: node-modules` to your .yarnrc.yml to use playbooks.', + 'Yarn PnP is not yet supported. Add `nodeLinker: node-modules` to your .yarnrc.yml to use intent.', ) } if ( @@ -26,7 +26,7 @@ function detectPackageManager(root: string): PackageManager { !existsSync(join(root, 'node_modules')) ) { throw new Error( - 'Deno without node_modules is not yet supported. Add `"nodeModulesDir": "auto"` to your deno.json to use playbooks.', + 'Deno without node_modules is not yet supported. Add `"nodeModulesDir": "auto"` to your deno.json to use intent.', ) } @@ -39,15 +39,15 @@ function detectPackageManager(root: string): PackageManager { } // --------------------------------------------------------------------------- -// Playbook field validation +// Intent field validation // --------------------------------------------------------------------------- -function validatePlaybookField( +function validateIntentField( pkgName: string, - playbook: unknown, -): PlaybookConfig | null { - if (!playbook || typeof playbook !== 'object') return null - const pb = playbook as Record + intent: unknown, +): IntentConfig | null { + if (!intent || typeof intent !== 'object') return null + const pb = intent as Record if (pb.version !== 1) return null if (typeof pb.repo !== 'string' || !pb.repo) return null @@ -112,17 +112,17 @@ function discoverSkills(skillsDir: string, baseName: string): SkillEntry[] { // Topological sort on requires // --------------------------------------------------------------------------- -function topoSort(packages: PlaybookPackage[]): PlaybookPackage[] { +function topoSort(packages: IntentPackage[]): IntentPackage[] { const byName = new Map(packages.map((p) => [p.name, p])) const visited = new Set() - const sorted: PlaybookPackage[] = [] + const sorted: IntentPackage[] = [] function visit(name: string): void { if (visited.has(name)) return visited.add(name) const pkg = byName.get(name) if (!pkg) return - for (const dep of pkg.playbook.requires ?? []) { + for (const dep of pkg.intent.requires ?? []) { visit(dep) } sorted.push(pkg) @@ -138,12 +138,12 @@ function topoSort(packages: PlaybookPackage[]): PlaybookPackage[] { // Main scanner // --------------------------------------------------------------------------- -export async function scanForPlaybooks(root?: string): Promise { +export async function scanForIntents(root?: string): Promise { const projectRoot = root ?? process.cwd() const packageManager = detectPackageManager(projectRoot) const nodeModulesDir = join(projectRoot, 'node_modules') - const packages: PlaybookPackage[] = [] + const packages: IntentPackage[] = [] const warnings: string[] = [] if (!existsSync(nodeModulesDir)) { @@ -200,11 +200,11 @@ export async function scanForPlaybooks(root?: string): Promise { const pkgVersion = typeof pkgJson.version === 'string' ? pkgJson.version : '0.0.0' - // Validate playbook field - const playbook = validatePlaybookField(pkgName, pkgJson.playbook) - if (!playbook) { + // Validate intent field + const intent = validateIntentField(pkgName, pkgJson.intent) + if (!intent) { warnings.push( - `${pkgName} has a skills/ directory but missing or invalid "playbook" field in package.json`, + `${pkgName} has a skills/ directory but missing or invalid "intent" field in package.json`, ) continue } @@ -215,7 +215,7 @@ export async function scanForPlaybooks(root?: string): Promise { packages.push({ name: pkgName, version: pkgVersion, - playbook, + intent, skills, }) } diff --git a/packages/playbooks/src/setup.ts b/packages/intent/src/setup.ts similarity index 86% rename from packages/playbooks/src/setup.ts rename to packages/intent/src/setup.ts index 998c8431..fe49ad9c 100644 --- a/packages/playbooks/src/setup.ts +++ b/packages/intent/src/setup.ts @@ -39,14 +39,14 @@ function detectVars(root: string): TemplateVars { } const name = typeof pkgJson.name === 'string' ? pkgJson.name : 'unknown' - const playbook = pkgJson.playbook as Record | undefined + const intent = pkgJson.intent as Record | undefined const repo = - typeof playbook?.repo === 'string' - ? playbook.repo + typeof intent?.repo === 'string' + ? intent.repo : name.replace(/^@/, '').replace(/\//, '/') - const docs = typeof playbook?.docs === 'string' ? playbook.docs : 'docs/' + const docs = typeof intent?.docs === 'string' ? intent.docs : 'docs/' // Best-guess src path from common monorepo patterns const shortName = name.replace(/^@[^/]+\//, '') @@ -114,15 +114,15 @@ function copyTemplates( // --------------------------------------------------------------------------- const SHIM_CONTENT = `#!/usr/bin/env node -// Auto-generated by @tanstack/playbooks setup -// Exposes the playbook end-user CLI for consumers of this library. +// Auto-generated by @tanstack/intent setup +// Exposes the intent end-user CLI for consumers of this library. // Commit this file, then add to your package.json: -// "bin": { "playbook": "./bin/playbook.js" } -await import('@tanstack/playbooks/playbook-library') +// "bin": { "intent": "./bin/intent.js" } +await import('@tanstack/intent/intent-library') ` function generateShim(root: string, result: SetupResult): void { - const shimPath = join(root, 'bin', 'playbook.js') + const shimPath = join(root, 'bin', 'intent.js') if (existsSync(shimPath)) { result.skipped.push(shimPath) @@ -169,7 +169,7 @@ export function runSetup( if (installOz) { const srcDir = join(templatesDir, 'oz') - const destDir = join(root, '.playbook', 'oz') + const destDir = join(root, '.intent', 'oz') const { copied, skipped } = copyTemplates(srcDir, destDir, vars) result.oz = copied result.skipped.push(...skipped) @@ -185,10 +185,10 @@ export function runSetup( for (const f of result.skipped) console.log(` Already exists: ${f}`) if (result.shim) { - console.log(`✓ Generated playbook shim: ${result.shim}`) + console.log(`✓ Generated intent shim: ${result.shim}`) console.log(`\n Add to your package.json:`) - console.log(` "bin": { "playbook": "./bin/playbook.js" }`) - console.log(`\n Add bin/playbook.js to your package.json "files" array.`) + console.log(` "bin": { "intent": "./bin/intent.js" }`) + console.log(`\n Add bin/intent.js to your package.json "files" array.`) } if ( @@ -197,9 +197,7 @@ export function runSetup( result.shim === null && result.skipped.length === 0 ) { - console.log( - 'No templates directory found. Is @tanstack/playbooks installed?', - ) + console.log('No templates directory found. Is @tanstack/intent installed?') } else if (result.workflows.length > 0 || result.oz.length > 0) { console.log(`\nTemplate variables applied:`) console.log(` Package: ${vars.PACKAGE_NAME}`) diff --git a/packages/playbooks/src/staleness.ts b/packages/intent/src/staleness.ts similarity index 100% rename from packages/playbooks/src/staleness.ts rename to packages/intent/src/staleness.ts diff --git a/packages/playbooks/src/types.ts b/packages/intent/src/types.ts similarity index 91% rename from packages/playbooks/src/types.ts rename to packages/intent/src/types.ts index ece98f41..cfd06976 100644 --- a/packages/playbooks/src/types.ts +++ b/packages/intent/src/types.ts @@ -1,8 +1,8 @@ // --------------------------------------------------------------------------- -// Playbook config (lives in library package.json under "playbook" key) +// Intent config (lives in library package.json under "intent" key) // --------------------------------------------------------------------------- -export interface PlaybookConfig { +export interface IntentConfig { version: number repo: string docs: string @@ -15,14 +15,14 @@ export interface PlaybookConfig { export interface ScanResult { packageManager: 'npm' | 'pnpm' | 'yarn' | 'bun' | 'unknown' - packages: PlaybookPackage[] + packages: IntentPackage[] warnings: string[] } -export interface PlaybookPackage { +export interface IntentPackage { name: string version: string - playbook: PlaybookConfig + intent: IntentConfig skills: SkillEntry[] } @@ -104,7 +104,7 @@ export interface MetaFeedbackPayload { // Config types // --------------------------------------------------------------------------- -export interface PlaybookProjectConfig { +export interface IntentProjectConfig { feedback: { frequency: string // "always" | "every-N" | "never" } diff --git a/packages/playbooks/src/utils.ts b/packages/intent/src/utils.ts similarity index 100% rename from packages/playbooks/src/utils.ts rename to packages/intent/src/utils.ts diff --git a/packages/playbooks/tests/cli.test.ts b/packages/intent/tests/cli.test.ts similarity index 89% rename from packages/playbooks/tests/cli.test.ts rename to packages/intent/tests/cli.test.ts index f714a1dc..8bd3a9f6 100644 --- a/packages/playbooks/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -4,12 +4,12 @@ import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' import { parse as parseYaml } from 'yaml' -// ── Meta-skills tests (playbook meta) ── +// ── Meta-skills tests (intent meta) ── const thisDir = dirname(fileURLToPath(import.meta.url)) const metaDir = join(thisDir, '..', 'meta') -describe('playbook meta', () => { +describe('intent meta', () => { it('meta directory exists', () => { expect(existsSync(metaDir)).toBe(true) }) @@ -50,7 +50,7 @@ describe('playbook meta', () => { // ── Validate command logic ── -describe('playbook validate', () => { +describe('intent validate', () => { it('finds SKILL.md files in meta directory', () => { function findSkillFiles(dir: string): string[] { const files: string[] = [] @@ -72,15 +72,15 @@ describe('playbook validate', () => { // ── Scanner JSON output shape ── -describe('playbook list --json shape', () => { - it('scanForPlaybooks returns correct shape', async () => { - const { scanForPlaybooks } = await import('../src/scanner.js') +describe('intent list --json shape', () => { + it('scanForIntents returns correct shape', async () => { + const { scanForIntents } = await import('../src/scanner.js') // Run against a dir with no node_modules — should return valid shape const { mkdtempSync } = await import('node:fs') const { tmpdir } = await import('node:os') const root = mkdtempSync(join(tmpdir(), 'cli-test-')) - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result).toHaveProperty('packageManager') expect(result).toHaveProperty('packages') expect(result).toHaveProperty('warnings') diff --git a/packages/playbooks/tests/feedback.test.ts b/packages/intent/tests/feedback.test.ts similarity index 95% rename from packages/playbooks/tests/feedback.test.ts rename to packages/intent/tests/feedback.test.ts index 250a3c6a..d2ea1581 100644 --- a/packages/playbooks/tests/feedback.test.ts +++ b/packages/intent/tests/feedback.test.ts @@ -26,7 +26,7 @@ let tmpDir: string function setupDir(): string { const dir = join( tmpdir(), - `playbook-fb-test-${Date.now()}-${Math.random().toString(36).slice(2)}`, + `intent-fb-test-${Date.now()}-${Math.random().toString(36).slice(2)}`, ) mkdirSync(dir, { recursive: true }) return dir @@ -214,7 +214,7 @@ describe('submitFeedback', () => { describe('resolveFrequency', () => { it('returns project config frequency when set', () => { writeFileSync( - join(tmpDir, 'playbook.config.json'), + join(tmpDir, 'intent.config.json'), JSON.stringify({ feedback: { frequency: 'always' } }), ) expect(resolveFrequency(tmpDir)).toBe('always') @@ -226,9 +226,9 @@ describe('resolveFrequency', () => { it('reads user override via XDG_CONFIG_HOME', () => { const configDir = join(tmpDir, 'xdg') - mkdirSync(join(configDir, 'playbook'), { recursive: true }) + mkdirSync(join(configDir, 'intent'), { recursive: true }) writeFileSync( - join(configDir, 'playbook', 'config.json'), + join(configDir, 'intent', 'config.json'), JSON.stringify({ feedback: { frequency: 'never' } }), ) @@ -248,15 +248,15 @@ describe('resolveFrequency', () => { it('user override takes precedence over project config', () => { // Project says "always" writeFileSync( - join(tmpDir, 'playbook.config.json'), + join(tmpDir, 'intent.config.json'), JSON.stringify({ feedback: { frequency: 'always' } }), ) // User override says "never" const configDir = join(tmpDir, 'xdg2') - mkdirSync(join(configDir, 'playbook'), { recursive: true }) + mkdirSync(join(configDir, 'intent'), { recursive: true }) writeFileSync( - join(configDir, 'playbook', 'config.json'), + join(configDir, 'intent', 'config.json'), JSON.stringify({ feedback: { frequency: 'never' } }), ) diff --git a/packages/playbooks/tests/init.test.ts b/packages/intent/tests/init.test.ts similarity index 80% rename from packages/playbooks/tests/init.test.ts rename to packages/intent/tests/init.test.ts index f1e22e94..44056702 100644 --- a/packages/playbooks/tests/init.test.ts +++ b/packages/intent/tests/init.test.ts @@ -10,8 +10,8 @@ import { tmpdir } from 'node:os' import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { detectAgentConfigs, - hasPlaybookBlock, - injectPlaybookBlock, + hasIntentBlock, + injectIntentBlock, readProjectConfig, runInit, writeProjectConfig, @@ -62,25 +62,25 @@ describe('detectAgentConfigs', () => { }) }) -describe('injectPlaybookBlock', () => { +describe('injectIntentBlock', () => { it('injects block into existing file', () => { const filePath = join(root, 'AGENTS.md') writeFileSync(filePath, '# Agent Instructions\n\nSome existing content.\n') - const injected = injectPlaybookBlock(filePath) + const injected = injectIntentBlock(filePath) expect(injected).toBe(true) const content = readFileSync(filePath, 'utf8') - expect(content).toContain('## Playbook Skills') - expect(content).toContain('npx playbook list') + expect(content).toContain('## Intent Skills') + expect(content).toContain('npx intent list') expect(content).toContain('Some existing content.') }) it('is idempotent — skips if block already present', () => { const filePath = join(root, 'AGENTS.md') - writeFileSync(filePath, '# Agent\n\n## Playbook Skills\n\nAlready here.\n') + writeFileSync(filePath, '# Agent\n\n## Intent Skills\n\nAlready here.\n') - const injected = injectPlaybookBlock(filePath) + const injected = injectIntentBlock(filePath) expect(injected).toBe(false) }) @@ -88,34 +88,34 @@ describe('injectPlaybookBlock', () => { const filePath = join(root, 'CLAUDE.md') writeFileSync(filePath, '') - const injected = injectPlaybookBlock(filePath) + const injected = injectIntentBlock(filePath) expect(injected).toBe(true) const content = readFileSync(filePath, 'utf8') - expect(content).toContain('## Playbook Skills') + expect(content).toContain('## Intent Skills') }) }) -describe('hasPlaybookBlock', () => { +describe('hasIntentBlock', () => { it('returns true when block exists', () => { const filePath = join(root, 'test.md') - writeFileSync(filePath, '## Playbook Skills\n\nContent.') - expect(hasPlaybookBlock(filePath)).toBe(true) + writeFileSync(filePath, '## Intent Skills\n\nContent.') + expect(hasIntentBlock(filePath)).toBe(true) }) it('returns false when block missing', () => { const filePath = join(root, 'test.md') - writeFileSync(filePath, '# No playbook here') - expect(hasPlaybookBlock(filePath)).toBe(false) + writeFileSync(filePath, '# No intent here') + expect(hasIntentBlock(filePath)).toBe(false) }) it('returns false for non-existent file', () => { - expect(hasPlaybookBlock(join(root, 'nope.md'))).toBe(false) + expect(hasIntentBlock(join(root, 'nope.md'))).toBe(false) }) }) describe('writeProjectConfig', () => { - it('creates playbook.config.json with defaults', () => { + it('creates intent.config.json with defaults', () => { const configPath = writeProjectConfig(root) expect(existsSync(configPath)).toBe(true) @@ -124,7 +124,7 @@ describe('writeProjectConfig', () => { }) it('does not overwrite existing config', () => { - const configPath = join(root, 'playbook.config.json') + const configPath = join(root, 'intent.config.json') writeFileSync( configPath, JSON.stringify({ feedback: { frequency: 'never' } }), @@ -139,7 +139,7 @@ describe('writeProjectConfig', () => { describe('readProjectConfig', () => { it('reads existing config', () => { writeFileSync( - join(root, 'playbook.config.json'), + join(root, 'intent.config.json'), JSON.stringify({ feedback: { frequency: 'always' } }), ) const config = readProjectConfig(root) @@ -164,17 +164,17 @@ describe('runInit', () => { // Verify injection happened expect(readFileSync(join(root, 'AGENTS.md'), 'utf8')).toContain( - '## Playbook Skills', + '## Intent Skills', ) expect(readFileSync(join(root, 'CLAUDE.md'), 'utf8')).toContain( - '## Playbook Skills', + '## Intent Skills', ) }) it('skips already-initialized files', () => { writeFileSync( join(root, 'AGENTS.md'), - '## Playbook Skills\n\nAlready done.\n', + '## Intent Skills\n\nAlready done.\n', ) const result = runInit(root) @@ -183,7 +183,7 @@ describe('runInit', () => { }) it('handles mixed state (some initialized, some not)', () => { - writeFileSync(join(root, 'AGENTS.md'), '## Playbook Skills\n\nDone.\n') + writeFileSync(join(root, 'AGENTS.md'), '## Intent Skills\n\nDone.\n') writeFileSync(join(root, 'CLAUDE.md'), '# Fresh\n') const result = runInit(root) diff --git a/packages/playbooks/tests/library-scanner.test.ts b/packages/intent/tests/library-scanner.test.ts similarity index 91% rename from packages/playbooks/tests/library-scanner.test.ts rename to packages/intent/tests/library-scanner.test.ts index 70b388ce..19ac7faf 100644 --- a/packages/playbooks/tests/library-scanner.test.ts +++ b/packages/intent/tests/library-scanner.test.ts @@ -28,10 +28,10 @@ function writeSkillMd(dir: string, frontmatter: Record): void { ) } -// Simulate the script path as it would appear in a library's bin/playbook.js shim. +// Simulate the script path as it would appear in a library's bin/intent.js shim. // findHomeDir walks up from dirname(scriptPath) to find the nearest package.json. function shimPath(pkgDir: string): string { - return join(pkgDir, 'bin', 'playbook.js') + return join(pkgDir, 'bin', 'intent.js') } // --------------------------------------------------------------------------- @@ -59,7 +59,7 @@ describe('scanLibrary', () => { name: '@tanstack/router', version: '1.2.0', description: 'Type-safe router for React', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, }) const skillDir = createDir(pkgDir, 'skills', 'routing') writeSkillMd(skillDir, { @@ -86,7 +86,7 @@ describe('scanLibrary', () => { writeJson(join(pkgDir, 'package.json'), { name: '@tanstack/router', version: '1.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, }) const skillDir = createDir(pkgDir, 'skills', 'routing') writeSkillMd(skillDir, { name: 'routing', description: 'Routing patterns' }) @@ -97,14 +97,14 @@ describe('scanLibrary', () => { expect(skill.path).toBe(join(pkgDir, 'skills', 'routing', 'SKILL.md')) }) - it('recursively discovers deps with bin.playbook', async () => { + it('recursively discovers deps with bin.intent', async () => { // Home package: @tanstack/router, depends on @tanstack/query const routerDir = createDir(root, 'node_modules', '@tanstack', 'router') writeJson(join(routerDir, 'package.json'), { name: '@tanstack/router', version: '1.0.0', description: 'Router', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, dependencies: { '@tanstack/query': '^5.0.0' }, }) const routerSkill = createDir(routerDir, 'skills', 'routing') @@ -119,7 +119,7 @@ describe('scanLibrary', () => { name: '@tanstack/query', version: '5.0.0', description: 'Async state management', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, }) const querySkill = createDir(queryDir, 'skills', 'fetching') writeSkillMd(querySkill, { @@ -146,7 +146,7 @@ describe('scanLibrary', () => { writeJson(join(pkgDir, 'package.json'), { name: '@tanstack/router', version: '1.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, peerDependencies: { '@tanstack/query': '^5.0.0' }, }) @@ -154,7 +154,7 @@ describe('scanLibrary', () => { writeJson(join(queryDir, 'package.json'), { name: '@tanstack/query', version: '5.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, }) const querySkill = createDir(queryDir, 'skills', 'fetching') writeSkillMd(querySkill, { name: 'fetching', description: 'Fetching' }) @@ -165,12 +165,12 @@ describe('scanLibrary', () => { expect(names).toContain('@tanstack/query') }) - it('skips deps without bin.playbook', async () => { + it('skips deps without bin.intent', async () => { const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router') writeJson(join(pkgDir, 'package.json'), { name: '@tanstack/router', version: '1.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, dependencies: { react: '^18.0.0' }, }) @@ -178,7 +178,7 @@ describe('scanLibrary', () => { writeJson(join(reactDir, 'package.json'), { name: 'react', version: '18.0.0', - // no bin.playbook + // no bin.intent }) const reactSkill = createDir(reactDir, 'skills', 'hooks') writeSkillMd(reactSkill, { name: 'hooks', description: 'React hooks' }) @@ -194,7 +194,7 @@ describe('scanLibrary', () => { writeJson(join(pkgDir, 'package.json'), { name: '@tanstack/router', version: '1.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, }) // No skills/ directory @@ -210,7 +210,7 @@ describe('scanLibrary', () => { writeJson(join(routerDir, 'package.json'), { name: '@tanstack/router', version: '1.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, dependencies: { '@tanstack/query': '^5.0.0' }, }) @@ -218,7 +218,7 @@ describe('scanLibrary', () => { writeJson(join(queryDir, 'package.json'), { name: '@tanstack/query', version: '5.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, dependencies: { '@tanstack/router': '^1.0.0' }, // circular back }) @@ -235,7 +235,7 @@ describe('scanLibrary', () => { writeJson(join(pkgDir, 'package.json'), { name: '@tanstack/router', version: '1.0.0', - bin: { playbook: './bin/playbook.js' }, + bin: { intent: './bin/intent.js' }, }) const routingDir = createDir(pkgDir, 'skills', 'routing') writeSkillMd(routingDir, { @@ -258,7 +258,7 @@ describe('scanLibrary', () => { }) it('returns a warning when home package.json cannot be found', async () => { - const fakeScript = join(root, 'nowhere', 'bin', 'playbook.js') + const fakeScript = join(root, 'nowhere', 'bin', 'intent.js') const result = await scanLibrary(fakeScript, root) diff --git a/packages/playbooks/tests/meta-feedback.test.ts b/packages/intent/tests/meta-feedback.test.ts similarity index 98% rename from packages/playbooks/tests/meta-feedback.test.ts rename to packages/intent/tests/meta-feedback.test.ts index f3c9fabb..e854a9a5 100644 --- a/packages/playbooks/tests/meta-feedback.test.ts +++ b/packages/intent/tests/meta-feedback.test.ts @@ -24,7 +24,7 @@ let tmpDir: string function setupDir(): string { const dir = join( tmpdir(), - `playbook-meta-fb-test-${Date.now()}-${Math.random().toString(36).slice(2)}`, + `intent-meta-fb-test-${Date.now()}-${Math.random().toString(36).slice(2)}`, ) mkdirSync(dir, { recursive: true }) return dir diff --git a/packages/playbooks/tests/scanner.test.ts b/packages/intent/tests/scanner.test.ts similarity index 80% rename from packages/playbooks/tests/scanner.test.ts rename to packages/intent/tests/scanner.test.ts index 01e49a8d..7942ee39 100644 --- a/packages/playbooks/tests/scanner.test.ts +++ b/packages/intent/tests/scanner.test.ts @@ -2,7 +2,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { join } from 'node:path' import { tmpdir } from 'node:os' import { afterEach, beforeEach, describe, expect, it } from 'vitest' -import { scanForPlaybooks } from '../src/scanner.js' +import { scanForIntents } from '../src/scanner.js' // ── Helpers ── @@ -31,7 +31,7 @@ function writeSkillMd(dir: string, frontmatter: Record): void { let root: string beforeEach(() => { - root = mkdtempSync(join(tmpdir(), 'playbook-test-')) + root = mkdtempSync(join(tmpdir(), 'intent-test-')) }) afterEach(() => { @@ -40,29 +40,29 @@ afterEach(() => { // ── Tests ── -describe('scanForPlaybooks', () => { +describe('scanForIntents', () => { it('returns empty packages when no node_modules exists', async () => { - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages).toEqual([]) expect(result.warnings).toEqual([]) }) - it('returns empty packages when node_modules has no playbook packages', async () => { + it('returns empty packages when node_modules has no intent packages', async () => { createDir(root, 'node_modules', 'some-lib') writeJson(join(root, 'node_modules', 'some-lib', 'package.json'), { name: 'some-lib', version: '1.0.0', }) - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages).toEqual([]) }) - it('discovers a playbook-enabled package with skills', async () => { + it('discovers an intent-enabled package with skills', async () => { const pkgDir = createDir(root, 'node_modules', '@tanstack', 'db') writeJson(join(pkgDir, 'package.json'), { name: '@tanstack/db', version: '0.5.2', - playbook: { + intent: { version: 1, repo: 'TanStack/db', docs: 'docs/', @@ -75,7 +75,7 @@ describe('scanForPlaybooks', () => { type: 'core', }) - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages).toHaveLength(1) expect(result.packages[0]!.name).toBe('@tanstack/db') expect(result.packages[0]!.version).toBe('0.5.2') @@ -91,7 +91,7 @@ describe('scanForPlaybooks', () => { writeJson(join(pkgDir, 'package.json'), { name: '@tanstack/db', version: '0.5.2', - playbook: { version: 1, repo: 'TanStack/db', docs: 'docs/' }, + intent: { version: 1, repo: 'TanStack/db', docs: 'docs/' }, }) const coreDir = createDir(pkgDir, 'skills', 'db-core') writeSkillMd(coreDir, { name: 'db-core', description: 'Core' }) @@ -101,38 +101,38 @@ describe('scanForPlaybooks', () => { description: 'Queries', }) - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages[0]!.skills).toHaveLength(2) const names = result.packages[0]!.skills.map((s) => s.name) expect(names).toContain('db-core') expect(names).toContain('db-core/live-queries') }) - it('warns on skills/ dir without valid playbook field', async () => { + it('warns on skills/ dir without valid intent field', async () => { const pkgDir = createDir(root, 'node_modules', 'bad-pkg') writeJson(join(pkgDir, 'package.json'), { name: 'bad-pkg', version: '1.0.0', - // no playbook field + // no intent field }) createDir(pkgDir, 'skills', 'some-skill') - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages).toHaveLength(0) expect(result.warnings).toHaveLength(1) expect(result.warnings[0]).toContain('bad-pkg') }) - it('warns on invalid playbook version', async () => { + it('warns on invalid intent version', async () => { const pkgDir = createDir(root, 'node_modules', 'wrong-ver') writeJson(join(pkgDir, 'package.json'), { name: 'wrong-ver', version: '1.0.0', - playbook: { version: 99, repo: 'foo/bar', docs: 'docs/' }, + intent: { version: 99, repo: 'foo/bar', docs: 'docs/' }, }) createDir(pkgDir, 'skills', 'some-skill') - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages).toHaveLength(0) expect(result.warnings).toHaveLength(1) }) @@ -143,7 +143,7 @@ describe('scanForPlaybooks', () => { writeJson(join(coreDir, 'package.json'), { name: '@tanstack/db', version: '0.5.0', - playbook: { version: 1, repo: 'TanStack/db', docs: 'docs/' }, + intent: { version: 1, repo: 'TanStack/db', docs: 'docs/' }, }) const coreSkill = createDir(coreDir, 'skills', 'db-core') writeSkillMd(coreSkill, { name: 'db-core', description: 'Core' }) @@ -153,7 +153,7 @@ describe('scanForPlaybooks', () => { writeJson(join(reactDir, 'package.json'), { name: '@tanstack/react-db', version: '0.5.0', - playbook: { + intent: { version: 1, repo: 'TanStack/db', docs: 'docs/', @@ -166,7 +166,7 @@ describe('scanForPlaybooks', () => { description: 'React bindings', }) - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages).toHaveLength(2) // Core should come first expect(result.packages[0]!.name).toBe('@tanstack/db') @@ -178,11 +178,11 @@ describe('scanForPlaybooks', () => { writeJson(join(pkgDir, 'package.json'), { name: 'no-skills', version: '1.0.0', - playbook: { version: 1, repo: 'foo/bar', docs: 'docs/' }, + intent: { version: 1, repo: 'foo/bar', docs: 'docs/' }, }) // No skills/ directory - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packages).toHaveLength(0) expect(result.warnings).toHaveLength(0) }) @@ -192,46 +192,46 @@ describe('package manager detection', () => { it('detects npm from package-lock.json', async () => { writeFileSync(join(root, 'package-lock.json'), '{}') createDir(root, 'node_modules') - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packageManager).toBe('npm') }) it('detects pnpm from pnpm-lock.yaml', async () => { writeFileSync(join(root, 'pnpm-lock.yaml'), '') createDir(root, 'node_modules') - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packageManager).toBe('pnpm') }) it('detects yarn from yarn.lock', async () => { writeFileSync(join(root, 'yarn.lock'), '') createDir(root, 'node_modules') - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packageManager).toBe('yarn') }) it('detects bun from bun.lockb', async () => { writeFileSync(join(root, 'bun.lockb'), '') createDir(root, 'node_modules') - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packageManager).toBe('bun') }) it('returns unknown when no lockfile found', async () => { createDir(root, 'node_modules') - const result = await scanForPlaybooks(root) + const result = await scanForIntents(root) expect(result.packageManager).toBe('unknown') }) it('throws for Yarn PnP', async () => { writeFileSync(join(root, '.pnp.cjs'), '') - await expect(scanForPlaybooks(root)).rejects.toThrow('Yarn PnP') + await expect(scanForIntents(root)).rejects.toThrow('Yarn PnP') }) it('throws for Deno without node_modules', async () => { writeFileSync(join(root, 'deno.json'), '{}') // No node_modules dir - await expect(scanForPlaybooks(root)).rejects.toThrow( + await expect(scanForIntents(root)).rejects.toThrow( 'Deno without node_modules', ) }) diff --git a/packages/playbooks/tests/setup.test.ts b/packages/intent/tests/setup.test.ts similarity index 89% rename from packages/playbooks/tests/setup.test.ts rename to packages/intent/tests/setup.test.ts index 5bdc5f4e..73e66d9c 100644 --- a/packages/playbooks/tests/setup.test.ts +++ b/packages/intent/tests/setup.test.ts @@ -23,7 +23,7 @@ beforeEach(() => { mkdirSync(join(metaDir, 'templates', 'oz'), { recursive: true }) writeFileSync( - join(metaDir, 'templates', 'workflows', 'notify-playbooks.yml'), + join(metaDir, 'templates', 'workflows', 'notify-intent.yml'), 'package: {{PACKAGE_NAME}}\nrepo: {{REPO}}\ndocs: {{DOCS_PATH}}\nsrc: {{SRC_PATH}}', ) writeFileSync( @@ -43,24 +43,24 @@ describe('runSetup', () => { expect(result.oz).toHaveLength(1) expect(result.skipped).toHaveLength(0) - const wfPath = join(root, '.github', 'workflows', 'notify-playbooks.yml') + const wfPath = join(root, '.github', 'workflows', 'notify-intent.yml') expect(existsSync(wfPath)).toBe(true) const content = readFileSync(wfPath, 'utf8') expect(content).toContain('package: unknown') }) - it('substitutes variables from package.json playbook field', () => { + it('substitutes variables from package.json intent field', () => { writeFileSync( join(root, 'package.json'), JSON.stringify({ name: '@tanstack/query', - playbook: { repo: 'TanStack/query', docs: 'docs/' }, + intent: { repo: 'TanStack/query', docs: 'docs/' }, }), ) const result = runSetup(root, metaDir, []) const wfContent = readFileSync( - join(root, '.github', 'workflows', 'notify-playbooks.yml'), + join(root, '.github', 'workflows', 'notify-intent.yml'), 'utf8', ) expect(wfContent).toContain('package: @tanstack/query') diff --git a/packages/playbooks/tests/skills.test.ts b/packages/intent/tests/skills.test.ts similarity index 100% rename from packages/playbooks/tests/skills.test.ts rename to packages/intent/tests/skills.test.ts diff --git a/packages/playbooks/tests/staleness.test.ts b/packages/intent/tests/staleness.test.ts similarity index 99% rename from packages/playbooks/tests/staleness.test.ts rename to packages/intent/tests/staleness.test.ts index fc34685a..32d37488 100644 --- a/packages/playbooks/tests/staleness.test.ts +++ b/packages/intent/tests/staleness.test.ts @@ -13,7 +13,7 @@ let tmpDir: string function setupDir(): string { const dir = join( tmpdir(), - `playbook-stale-test-${Date.now()}-${Math.random().toString(36).slice(2)}`, + `intent-stale-test-${Date.now()}-${Math.random().toString(36).slice(2)}`, ) mkdirSync(dir, { recursive: true }) return dir diff --git a/packages/playbooks/tsconfig.json b/packages/intent/tsconfig.json similarity index 100% rename from packages/playbooks/tsconfig.json rename to packages/intent/tsconfig.json diff --git a/packages/playbooks/vitest.config.ts b/packages/intent/vitest.config.ts similarity index 85% rename from packages/playbooks/vitest.config.ts rename to packages/intent/vitest.config.ts index a691c9ac..d38b74e7 100644 --- a/packages/playbooks/vitest.config.ts +++ b/packages/intent/vitest.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - name: 'playbooks', + name: 'intent', include: ['tests/**/*.test.ts'], }, }) diff --git a/packages/playbooks/CHANGELOG.md b/packages/playbooks/CHANGELOG.md deleted file mode 100644 index 2df1e0c3..00000000 --- a/packages/playbooks/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @tanstack/playbooks - -## 0.1.0 - -### Patch Changes - -- Add `playbook-library` end-user CLI for library consumers. Libraries wire it up via a generated shim (`playbook setup --shim`) to expose a `playbook` bin. Running `playbook list` recursively discovers skills across the library's dependency tree; `playbook install` prints an agent-driven prompt to map skills to project tasks in CLAUDE.md. ([#9](https://github.com/TanStack/playbooks/pull/9)) diff --git a/packages/playbooks/README.md b/packages/playbooks/README.md deleted file mode 100644 index 6cf82599..00000000 --- a/packages/playbooks/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# @tanstack/playbooks - -Ship compositional knowledge for AI coding agents alongside your npm packages. - -Playbooks are npm packages of skills — encoding how tools work together, what patterns apply for which goals, and what to avoid. Skills travel with the tool via `npm update`, not the model's training cutoff. - -`@tanstack/playbooks` is the toolkit for generating, discovering, and maintaining skills for your library. - -## Install - -```bash -pnpm add -D @tanstack/playbooks -``` - -## Quick Start - -### For library consumers - -Set up playbook discovery in your project's agent config files (CLAUDE.md, .cursorrules, etc.): - -```bash -npx playbook init -``` - -List available skills from installed packages: - -```bash -npx playbook list -``` - -### For library maintainers - -Generate skills for your library using the guided scaffold workflow: - -```bash -npx playbook scaffold -``` - -Validate your skill files: - -```bash -npx playbook validate -``` - -Copy CI and Oz workflow templates into your repo: - -```bash -npx playbook setup -``` - -## CLI Commands - -| Command | Description | -| ------------------------- | ------------------------------------------------- | -| `playbook init` | Inject playbook discovery into agent config files | -| `playbook list [--json]` | Discover playbook-enabled packages | -| `playbook meta` | List meta-skills for library maintainers | -| `playbook scaffold` | Print the guided skill generation prompt | -| `playbook validate [dir]` | Validate SKILL.md files | -| `playbook setup` | Copy CI/Oz workflow templates | -| `playbook stale [--json]` | Check skills for version drift | -| `playbook feedback` | Submit skill feedback | - -## License - -[MIT](./LICENSE) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79937505..5aa7ba8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,7 +67,7 @@ importers: specifier: ^2.7.0 version: 2.8.2 - packages/playbooks: + packages/intent: dependencies: yaml: specifier: ^2.7.0 diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 078ecabe..91670a6c 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -7,10 +7,10 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url)) await generateReferenceDocs({ packages: [ { - name: 'playbooks', - entryPoints: [resolve(__dirname, '../packages/playbooks/src/index.ts')], - tsconfig: resolve(__dirname, '../packages/playbooks/tsconfig.docs.json'), - outputDir: resolve(__dirname, '../docs/playbooks'), + name: 'intent', + entryPoints: [resolve(__dirname, '../packages/intent/src/index.ts')], + tsconfig: resolve(__dirname, '../packages/intent/tsconfig.docs.json'), + outputDir: resolve(__dirname, '../docs/intent'), }, ], }) diff --git a/scripts/validate-skills.ts b/scripts/validate-skills.ts index 28ec6232..ea56ce4f 100644 --- a/scripts/validate-skills.ts +++ b/scripts/validate-skills.ts @@ -40,10 +40,10 @@ const PROHIBITED_PATTERNS: Array<{ pattern: RegExp; description: string }> = [ ] const ALLOWED_SHELL_COMMANDS = [ - 'playbook list', - 'playbook feedback', + 'intent list', + 'intent feedback', 'npm install @tanstack/', - 'npx playbook', + 'npx intent', ] // ── Helpers ── diff --git a/vitest.workspace.js b/vitest.workspace.js index 801ad469..70deda66 100644 --- a/vitest.workspace.js +++ b/vitest.workspace.js @@ -2,6 +2,6 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - projects: ['./packages/playbooks/vitest.config.ts'], + projects: ['./packages/intent/vitest.config.ts'], }, })