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
1 change: 1 addition & 0 deletions docs/cli/intent-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ npx @tanstack/intent@latest install [--map] [--dry-run] [--print-prompt] [--glob
- Updates an existing managed block in a supported config file.
- Preserves all content outside the managed block.
- Scans packages and writes compact `when` and `use` mappings only when `--map` is passed.
- Surfaces packages permitted by `package.json#intent.skills` in `--map` mode. See [Configuration](../concepts/configuration).
- Skips reference, meta, maintainer, and maintainer-only skills in `--map` mode.
- Writes compact `when` and `use` entries instead of load paths in `--map` mode.
- Verifies the managed block before reporting success.
Expand Down
37 changes: 33 additions & 4 deletions docs/cli/intent-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ npx @tanstack/intent@latest list [--json] [--debug] [--exclude <pattern>] [--glo
## What you get

- Scans project and workspace dependencies for intent-enabled packages and skills
- Surfaces packages permitted by `package.json#intent.skills` (see [Allowlist](#allowlist))
- Includes global packages only when `--global` or `--global-only` is passed
- Includes warnings from discovery
- Excludes packages matched by package.json `intent.exclude` or `--exclude`
- Excludes packages and skills matched by package.json `intent.exclude` or `--exclude`
- Prints debug details to stderr when `--debug` is passed
- If no packages are discovered, prints `No intent-enabled packages found.`
- Summary line with package count and skill count
Expand Down Expand Up @@ -83,20 +84,48 @@ When both local and global packages are scanned, local packages take precedence.
When the same package exists both locally and globally and global scanning is enabled, `intent list` prefers the local package.
When project `node_modules` exists, `intent list` scans it. In Yarn PnP projects without usable `node_modules`, `intent list` uses Yarn's PnP API.

## Allowlist

`package.json#intent.skills` is the allowlist that decides which discovered packages are surfaced. Only listed packages contribute skills.

```json
{
"intent": {
"skills": ["@tanstack/query", "workspace:@scope/internal"]
}
}
```

Each entry is one source:

- `@scope/pkg` or `pkg`: an npm package reachable through the dependency tree.
- `workspace:@scope/pkg`: a package in the current workspace.
- `git:<host>/<repo>#<ref>`: reserved, and not yet supported.

The list as a whole has three special forms:

- **Absent** (no `intent.skills` key): every discovered package is surfaced, with a deprecation notice printed to stderr on each run until you set `intent.skills`. This is the upgrade path for existing projects. A future version will require an explicit allowlist.
- **Empty** (`"skills": []`): no package is surfaced, with an info notice printed to stderr.
- **Wildcard** (`"skills": ["*"]`): every discovered package is surfaced, with an acknowledged-risk notice printed to stderr.

A package that ships skills but is not listed is dropped. When packages are dropped this way, Intent prints one summary line naming them so you can opt in. A listed package that was not discovered is reported as well. Matching is currently by package name. See [Configuration](../concepts/configuration) and [Trust model](../concepts/trust-model).

## Excludes

Package excludes are hard filters for packages that should not be used in a repo.
Package excludes are hard filters for packages that should not be used in a repo, applied after the allowlist.
Intent reads `intent.exclude` arrays from package.json files while walking from the workspace or project root to the current working directory, then appends any `--exclude` flags.

```json
{
"intent": {
"exclude": ["@tanstack/*devtools*"]
"exclude": ["@tanstack/*devtools*", "@tanstack/router#experimental-*"]
}
}
```

Exclude patterns match full package names. In v1, only exact names and `*` wildcards are supported.
A pattern without `#` excludes a whole package. A pattern with `#` excludes a single skill (`@scope/pkg#search-params`), and the skill segment may itself be a glob (`@scope/pkg#experimental-*`). A pattern may cross package boundaries at skill granularity (`*#experimental-*`). The `#*` shortcut (`@scope/pkg#*`) excludes the whole package. Only exact names and `*` wildcards are supported on each segment. Bare package-name patterns keep working unchanged.

An excluded package never triggers the unlisted-source warning, because an exclude is an explicit decision rather than an oversight.

## Common errors

Expand Down
9 changes: 7 additions & 2 deletions docs/cli/intent-load.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ npx @tanstack/intent@latest load <package>#<skill> [--path] [--json] [--debug] [
- `--path`: print the resolved skill path instead of the file content
- `--json`: print structured JSON with metadata and content
- `--debug`: print resolution debug details to stderr
- `--exclude <pattern>`: exclude package names matching a simple glob; can be passed more than once
- `--exclude <pattern>`: exclude a package or skill matching a simple glob; can be passed more than once
- `--global`: load from project packages first, then global packages
- `--global-only`: load from global packages only

Expand All @@ -23,7 +23,8 @@ npx @tanstack/intent@latest load <package>#<skill> [--path] [--json] [--debug] [
- Validates `<package>#<skill>` before scanning
- Scans project-local packages by default
- Includes global packages only when `--global` or `--global-only` is passed
- Fails before scanning when the target package matches package.json `intent.exclude` or `--exclude`
- Refuses before scanning when the target package is not permitted by `package.json#intent.skills`
- Refuses before scanning when the target package or skill matches `intent.exclude` or `--exclude`
- Prefers local packages when `--global` is used and the same package exists locally and globally
- Accepts an unambiguous short skill name when a package-prefixed skill exists
- Prints raw `SKILL.md` content by default
Expand Down Expand Up @@ -66,9 +67,13 @@ npx @tanstack/intent@latest load some-lib#core --path
- Missing package: `Cannot resolve skill use "...": package "..." was not found.`
- Missing skill: `Cannot resolve skill use "...": skill "..." was not found in package "...".`
- Skill suggestion: `Did you mean @tanstack/router-core#router-core/auth-and-guards?`
- Unlisted package: `Cannot load skill use "...": package "..." is not listed in intent.skills.`
- Excluded package: `Cannot load skill use "...": package "..." is excluded by Intent configuration.`
- Excluded skill: `Cannot load skill use "...": skill "..." is excluded by Intent configuration.`

## Related

- [intent list](./intent-list)
- [intent install](./intent-install)
- [Trust model](../concepts/trust-model)
- [Configuration](../concepts/configuration)
107 changes: 54 additions & 53 deletions docs/cli/intent-stale.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,29 @@ npx @tanstack/intent@latest stale [--json]

## Behavior

- Checks the current package by default
- From a monorepo root, checks workspace packages that ship skills and also reports public workspace packages with no skill or artifact coverage
- When `dir` is provided, scopes the check to the targeted package or skills directory
- Computes one staleness report per package
- Reads repo-root `_artifacts/*domain_map.yaml` and `_artifacts/*skill_tree.yaml` when present
- Flags public workspace packages that are not represented by generated skills or artifact coverage
- Skips workspace packages with `"private": true`
- Prints text output by default or JSON with `--json`
- Prints a non-failing workflow update reminder when `.github/workflows/check-skills.yml` is missing the current `intent-workflow-version` stamp
- If no packages are found, prints `No intent-enabled packages found.`

Artifact coverage ignores can be recorded in `_artifacts/*skill_tree.yaml` or `_artifacts/*domain_map.yaml`:

```yaml
coverage:
ignored_packages:
- '@tanstack/internal-tooling'
- name: packages/devtools-fixture
reason: test fixture only
```

Ignored packages are excluded from missing coverage signals. Private workspace packages are excluded automatically.
- Checks the current package by default
- From a monorepo root, checks workspace packages that ship skills and also reports public workspace packages with no skill or artifact coverage
- Applies the `package.json#intent.skills` allowlist when falling back to installed dependencies; workspace packages are first-party and checked regardless. See [Configuration](../concepts/configuration).
- When `dir` is provided, scopes the check to the targeted package or skills directory
- Computes one staleness report per package
- Reads repo-root `_artifacts/*domain_map.yaml` and `_artifacts/*skill_tree.yaml` when present
- Flags public workspace packages that are not represented by generated skills or artifact coverage
- Skips workspace packages with `"private": true`
- Prints text output by default or JSON with `--json`
- Prints a non-failing workflow update reminder when `.github/workflows/check-skills.yml` is missing the current `intent-workflow-version` stamp
- If no packages are found, prints `No intent-enabled packages found.`

Artifact coverage ignores can be recorded in `_artifacts/*skill_tree.yaml` or `_artifacts/*domain_map.yaml`:

```yaml
coverage:
ignored_packages:
- '@tanstack/internal-tooling'
- name: packages/devtools-fixture
reason: test fixture only
```

Ignored packages are excluded from missing coverage signals. Private workspace packages are excluded automatically.

## JSON report schema

Expand All @@ -49,54 +50,54 @@ Ignored packages are excluded from missing coverage signals. Private workspace p
"currentVersion": "string | null",
"skillVersion": "string | null",
"versionDrift": "major | minor | patch | null",
"skills": [
{
"name": "string",
"reasons": ["string"],
"needsReview": true
}
],
"signals": [
{
"type": "missing-package-coverage",
"library": "string",
"subject": "string",
"reasons": ["string"],
"needsReview": true,
"packageName": "string",
"packageRoot": "string"
}
]
}
]
"skills": [
{
"name": "string",
"reasons": ["string"],
"needsReview": true
}
],
"signals": [
{
"type": "missing-package-coverage",
"library": "string",
"subject": "string",
"reasons": ["string"],
"needsReview": true,
"packageName": "string",
"packageRoot": "string"
}
]
}
]
```

Report fields:

- `library`: package name
- `currentVersion`: latest version from npm registry (or `null` if unavailable)
- `skillVersion`: `library_version` from skills (or `null`)
- `versionDrift`: `major | minor | patch | null`
- `skills`: array of per-skill checks
- `signals`: array of artifact and workspace coverage checks
- `versionDrift`: `major | minor | patch | null`
- `skills`: array of per-skill checks
- `signals`: array of artifact and workspace coverage checks

Skill fields:

- `name`
- `reasons`: one or more staleness reasons
- `needsReview`: boolean (`true` when reasons exist)

Reason generation:

- `version drift (<skillVersion> → <currentVersion>)`
- `new source (<path>)` when a declared source has no stored sync SHA
- artifact parse warnings, unresolved artifact skill paths, source drift, artifact library version drift, and missing workspace package coverage
Reason generation:
- `version drift (<skillVersion> → <currentVersion>)`
- `new source (<path>)` when a declared source has no stored sync SHA
- artifact parse warnings, unresolved artifact skill paths, source drift, artifact library version drift, and missing workspace package coverage

## Text output

- Report header format: `<library> (<skillVersion> → <currentVersion>) [<versionDrift> drift]`
- When no skill reasons exist: `All skills up-to-date`
- Otherwise: one warning line per stale skill or review signal (`⚠ <name>: <reason1>, <reason2>, ...`)
- Report header format: `<library> (<skillVersion> → <currentVersion>) [<versionDrift> drift]`
- When no skill reasons exist: `All skills up-to-date`
- Otherwise: one warning line per stale skill or review signal (`⚠ <name>: <reason1>, <reason2>, ...`)

## Common errors

Expand Down
69 changes: 69 additions & 0 deletions docs/concepts/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: Configuration
id: configuration
---

Intent reads consumer configuration from the `intent` object in `package.json`. Two keys control which skills reach your agent: `skills` (the allowlist) and `exclude` (the blocklist).

```json
{
"intent": {
"skills": ["@tanstack/query", "workspace:@scope/internal"],
"exclude": ["@tanstack/router#experimental-*"]
}
}
```

Intent merges these keys from every `package.json` between the current working directory and the workspace or project root. A monorepo package inherits the root configuration and adds its own.

## `intent.skills`

`intent.skills` is the allowlist. Only packages it permits contribute skills to `list`, `load`, `install`, and `stale`. See [Trust model](./trust-model) for the reasoning.

### Source entries

Each array entry names one source:

| Entry | Kind | Meaning |
| ----- | ---- | ------- |
| `@scope/pkg` or `pkg` | npm | A package reachable through the dependency tree, direct or transitive. |
| `workspace:@scope/pkg` | workspace | A package in the current workspace. |
| `git:<host>/<repo>#<ref>` | git | Reserved. Not yet supported, and rejected until a future version adds it. |

A malformed entry fails the whole command, and every bad entry is reported at once. Intent currently matches an allowlist entry against a discovered package by name. This matching will tighten in a future version.

### Special forms

The list as a whole has three special forms:

- **Absent.** No `intent.skills` key. Every discovered package is surfaced, and Intent prints a deprecation notice to stderr on each run until you set `intent.skills`. This is the upgrade path for existing projects. A future version will require an explicit allowlist.
- **Empty.** `"skills": []`. No package is surfaced. Intent prints an info notice to stderr.
- **Wildcard.** `"skills": ["*"]`. Every discovered package is surfaced. Intent prints an acknowledged-risk notice to stderr, since unvetted skills may reach your agent.

A package that ships skills but is not listed is dropped. When packages are dropped this way, Intent prints one summary line naming them so you can opt in. A listed package that was not discovered is reported as well.

### Existing projects

A project that has not set `intent.skills` keeps working. Intent surfaces every discovered package and prints the deprecation notice described under the absent form. Nothing breaks. Add an allowlist when you are ready, before a future version requires one. Run `intent list` to confirm which packages are surfaced.

## `intent.exclude`

`intent.exclude` removes packages or individual skills after the allowlist resolves. It also accepts the `--exclude <pattern>` flag on `list` and `load` for one-off runs.

```json
{
"intent": {
"exclude": ["@tanstack/*devtools*", "@tanstack/router#experimental-*"]
}
}
```

Pattern grammar:

- A pattern without `#` excludes a whole package: `@scope/pkg`.
- A pattern with `#` excludes a single skill: `@scope/pkg#search-params`.
- The skill segment may be a glob: `@scope/pkg#experimental-*`.
- A pattern may cross package boundaries at skill granularity: `*#experimental-*`.
- The `#*` shortcut excludes the whole package: `@scope/pkg#*`.

Only exact names and `*` wildcards are supported on each segment. Bare package-name patterns keep working unchanged. An excluded package does not trigger the unlisted-source warning, because an exclude is an explicit decision.
28 changes: 28 additions & 0 deletions docs/concepts/trust-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: Trust model
id: trust-model
---

Intent surfaces skills from your dependencies into your coding agent's guidance. A skill is instructions an agent follows, so the set of packages allowed to contribute skills is a trust decision. Intent makes that decision explicit through the `intent.skills` allowlist.

## Explicit sources

A package ships skills in a `skills/` directory. Discovery finds every installed package that has one, including transitive dependencies. Discovery does not grant trust.

`package.json#intent.skills` is the gate. A discovered package contributes skills only when it appears in the allowlist. An unlisted package is dropped, and Intent reports it so you can opt in or ignore it.

The gate is opt-in today. A project with no `intent.skills` key still surfaces every discovered package, and Intent prints a deprecation notice to stderr on each run until you set `intent.skills`. A future version will require an explicit allowlist. See the [special forms](./configuration#special-forms) in Configuration.

Trust does not propagate. A listed package may depend on another package that ships skills, but that dependency stays unlisted until you add it to `intent.skills` yourself. You allow each source on its own.

## Static discovery

Intent reads package data as files. It never imports, requires, or executes the code of a discovered package to find or load a skill. Adding a package to your dependency tree cannot run that package's code through Intent.

One exception is sanctioned: in Yarn Plug'n'Play projects, Intent loads Yarn's PnP runtime (`.pnp.cjs`) to map package identities to readable locations. It loads no package entry points, bins, lifecycle scripts, or other package-provided JavaScript. An ESLint rule enforces this invariant in the discovery code.

## What the allowlist does not cover yet

Matching is currently by package name. A `workspace:foo` entry and a bare `foo` entry both authorize a discovered package named `foo`, because the scanner does not yet distinguish a workspace member from a published package of the same name. This errs toward permitting a same-named package, never toward denying one you listed. A future version tightens matching once the scanner carries that signal.

The `git:` source kind is reserved. Intent parses and validates the shape, then rejects it until a future version can pin the resolved ref and content hash. A git entry never loads silently.
13 changes: 13 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@
}
]
},
{
"label": "Concepts",
"children": [
{
"label": "Trust Model",
"to": "concepts/trust-model"
},
{
"label": "Configuration",
"to": "concepts/configuration"
}
]
},
{
"label": "CLI Reference",
"children": [
Expand Down
Loading
Loading