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
5 changes: 5 additions & 0 deletions .changeset/fair-tools-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/intent': minor
---

Add grouped interactive first-run skill permission setup to `intent install`. Preview discovered skills and their permission scope, choose package-wide or exact-skill access, and confirm before saving. Excluded skills cannot be selected, empty discovery leaves setup available for retry, and completion reports available skills and the next command.
44 changes: 37 additions & 7 deletions docs/cli/intent-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: intent install
id: intent-install
---

`intent install` creates or updates an `intent-skills` guidance block in a project guidance file.
`intent install` confirms skill-source permissions on first use, then creates or updates an `intent-skills` guidance block in a project guidance file.

```bash
npx @tanstack/intent@latest install [--map] [--dry-run] [--print-prompt] [--global] [--global-only] [--no-notices]
Expand All @@ -25,13 +25,39 @@ npx @tanstack/intent@latest install [--map] [--dry-run] [--print-prompt] [--glob

## Behavior

### Default guidance
### Default install

- Writes lightweight skill loading guidance by default.
- Creates `AGENTS.md` when no managed block exists.
- Updates an existing managed block in a supported config file.
- Preserves all content outside the managed block.
- Verifies the managed block before reporting success.
If `intent.skills` is already configured, including through workspace inheritance, `install` only updates guidance. It does not prompt or change `package.json`.

Otherwise, first-run setup requires an interactive terminal. Non-TTY execution fails before discovery or writes. Node.js 20.12.0 or newer is required.

#### First-run flow

1. **Review** discovered npm and workspace packages, versions, and skill descriptions. Excluded candidates appear in the overview but cannot be selected.
2. **Choose** permissions. After the overview, Intent asks about allow-all; choose No to select individual packages or skills. Press Space to toggle grouped choices and Enter to review.
3. **Confirm** the exact `intent.skills` value, destination file, and trust change. Confirmation defaults to No.
4. **Finish** with verified guidance, available skill and package counts, and a package-manager-aware `list` command. If no skills are enabled, Intent explains how to edit `intent.skills`.

#### Permission choices

| Choice | Effect |
| --- | --- |
| All skills in a package | Permits its current and future skills. |
| Individual skill | Permits only the named skill. |
| Allow all sources | Writes `["*"]` and skips narrower selection. |
| Select nothing | Explicitly confirms writing `[]`, disabling current and future sources until `intent.skills` is edited. |

Existing `intent.exclude` rules always apply and remain unchanged.

#### Files and retry behavior

Permissions go in the nearest owning `package.json`. Inside a workspace package, this is that package's file. The update preserves formatting and uses an atomic replacement; if the file changes after preview, Intent stops and asks you to retry.

After permissions are saved, Intent updates an existing managed guidance block in a supported config file, or creates one in `AGENTS.md`. Content outside the block is preserved, and the block is verified before success is reported.

- **No skills found, or all excluded:** explains how to retry and writes nothing. Empty discovery does not create a deny-all policy.
- **Decline or cancel a prompt:** writes neither permissions nor guidance.
- **`--dry-run`:** performs discovery and selection, previews permissions and guidance, and writes neither file.

### Mapping mode

Expand Down Expand Up @@ -88,6 +114,10 @@ tanstackIntent:
| Mappings unchanged | `No changes to AGENTS.md; 2 mappings already current.` |
| Guidance created | `Created AGENTS.md with skill loading guidance.` |
| Guidance unchanged | `No changes to AGENTS.md; skill loading guidance already current.` |
| Permissions updated | `Permissions: updated package.json.` |
| Permissions canceled | `Permissions: canceled.` |
| Guidance result after setup | `Guidance: created AGENTS.md.` |
| Guidance failure after setup | `Guidance: failed: <error>` |
| Placement tip | `Tip: Keep the intent-skills block near the top of AGENTS.md so agents read it before task-specific instructions.` |
| No actionable skills in `--map` mode | `No intent-enabled skills found.` |

Expand Down
76 changes: 57 additions & 19 deletions docs/concepts/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,31 @@ title: Configuration
id: configuration
---

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

- **`skills`** permits packages or individual skills.
- **`exclude`** blocks packages or skills after permissions are evaluated.

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

## Configuration inheritance

- **`intent.skills`:** Intent uses the nearest non-null declaration between the current working directory and the workspace or project root. A nearer declaration replaces its parent. An omitted or null value inherits the nearest parent declaration.
- **`intent.exclude`:** Intent combines arrays from the root through the current working directory, then adds excludes passed by the caller.
| Key | Inheritance rule |
| --- | --- |
| `intent.skills` | Uses the nearest non-null declaration between the current directory and the workspace or project root. A nearer declaration replaces its parent; omitted or null values inherit. |
| `intent.exclude` | Combines arrays from the root through the current directory, then adds excludes passed by the caller. |

## `intent.skills`

Expand All @@ -27,9 +37,9 @@ Intent reads consumer configuration from the `intent` object in `package.json`.
- Resolve through `load`.
- Contribute mappings to `install --map`.

The default `install` command writes generic loading guidance without scanning packages. See [Trust model](./trust-model) for the reasoning and lifecycle boundaries.
Default `install` helps configure permissions on first use. See [Existing projects](#existing-projects) for how it handles saved or inherited configuration, and [Trust model](./trust-model) for the trust boundaries.

Package selectors permit every skill in the package. Exact selectors use `<package>#<skill>` and permit only the named skill. If the same package matches both forms, the package selector takes precedence and permits every skill. `intent.exclude` is applied afterward and can still remove a permitted package or skill.
Package selectors permit current and future skills in the package. Exact selectors use `<package>#<skill>` and permit only that skill. If both match, the package selector takes precedence. `intent.exclude` is applied afterward and can still block either choice.

### Source entries

Expand All @@ -45,25 +55,51 @@ Each array entry names one source:
| `workspace:@scope/*` | workspace | Every discovered workspace package whose name matches the pattern. |
| `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. Exact selectors require one non-empty package name and one non-empty, non-wildcard skill name. Package patterns support `*` wildcards, including scoped patterns such as `@tanstack/*`, but cannot be combined with an exact skill selector.
#### Validation rules

- Exact selectors require a non-empty package name and a non-empty skill name without wildcards.
- Package patterns support `*`, including scoped patterns such as `@tanstack/*`. Patterns cannot be combined with an exact skill selector.
- Source kinds must match: bare selectors permit npm sources; `workspace:` selectors permit workspace sources.
- `git:` entries are rejected, including entries containing `#` for a Git ref.

Intent matches both the package name and source kind: a bare package or exact selector permits only an npm source, and a `workspace:` selector permits only a workspace source. `git:` entries remain unsupported and are rejected, including entries that contain `#` for a Git ref.
A malformed entry fails the whole command. Intent reports every bad entry at once.

### Special forms

| Form | Result | Notice |
| --- | --- | --- |
| **Absent:** no `intent.skills` key | Surfaces every discovered package as an upgrade path for existing projects. A future version will require an explicit allowlist. | Deprecation notice on stderr on each run until you set `intent.skills`. |
| **Absent:** no effective `intent.skills` key | Discovery commands surface every discovered package as migration behavior. | Deprecation notice until you configure permissions. |
| **Empty:** `"skills": []` | Surfaces no packages. | Info notice on stderr. |
| **Wildcard:** `"skills": ["*"]` | Surfaces every discovered package across package scopes and source kinds. This is broader than a pattern such as `@tanstack/*`. | Acknowledged-risk notice on stderr because unvetted skills may reach your agent. |
| **Wildcard:** `"skills": ["*"]` | Permits every discovered package across scopes and source kinds, broader than `@tanstack/*`. | Acknowledged-risk notice: unvetted skills may reach your agent. |

All policy notices go to stderr. Exclusions still apply to these forms.

#### Discovery notices

A package that ships skills but is not listed is dropped. In human output, Intent adds one policy notice naming packages dropped this way so you can opt in. Agent sessions receive only the hidden package and skill counts. A listed package that was not discovered is reported as a notice as well.
| Situation | Notice |
| --- | --- |
| Discovered package is not permitted | Human output names omitted packages in one notice. Agent sessions receive only hidden package and skill counts. |
| Configured package was not discovered | Reports that the package was not discovered. |
| Package was explicitly excluded | No unlisted-source notice. |

### Existing projects

Run `intent list` to see which packages the current policy surfaces.

A project without `intent.skills` uses the absent form: Intent surfaces every discovered package and prints its deprecation notice. Add an allowlist to permit specific sources before a future version requires one.
| Current configuration | Default `intent install` behavior |
| --- | --- |
| Saved or inherited `intent.skills` | Updates guidance only. Keeps permissions unchanged and does not prompt. |
| No effective `intent.skills` | Starts interactive permission setup. Non-TTY execution fails without writes. |

First-run setup discovers candidates before policy filtering and shows their versions and descriptions. It previews the selected allowlist and nearest owning `package.json`, then requires confirmation before saving permissions and installing guidance.

- **Excluded candidates** stay visible in the overview but cannot be selected. Exclusions remain unchanged.
- **Package-wide choices** include current and future skills and remove redundant selected children. Exact choices permit only the named skill.
- **Allow-all** is offered separately after the overview and saves `["*"]` alone.
- **An empty selection** explicitly confirms disabling all skills with `[]`.
- **Empty or fully excluded discovery** writes nothing, so setup can be retried.

See [Default install](../cli/intent-install#default-install) for picker controls, previews, and cancellation behavior.

### Suppressing notices temporarily

Expand Down Expand Up @@ -98,12 +134,14 @@ npx @tanstack/intent@latest exclude list
}
```

Pattern grammar:
### Exclusion patterns

- 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#*`.
| Pattern | Excludes |
| --- | --- |
| `@scope/pkg` | The whole package. |
| `@scope/pkg#search-params` | One named skill. |
| `@scope/pkg#experimental-*` | Matching skills in one package. |
| `*#experimental-*` | Matching skills across packages. |
| `@scope/pkg#*` | The whole package, using the `#*` shortcut. |

Only exact names and `*` wildcards are supported on each segment. Excludes are source-kind agnostic, so a package pattern excludes matching npm and workspace sources. An excluded package does not trigger the unlisted-source notice, because an exclude is an explicit decision.
Each segment supports exact names and `*` wildcards only. Excludes apply to both npm and workspace sources with matching names, regardless of source kind.
34 changes: 30 additions & 4 deletions docs/concepts/trust-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,43 @@ title: Trust model
id: trust-model
---

Intent discovers skills from your dependencies and can surface permitted skills through its CLI and agent integrations. 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.
Skills contain instructions for an agent. Choosing which packages can supply those instructions is a trust decision, controlled by 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 an exact entry or `*` pattern in the allowlist matches its package name and source kind. An unlisted package is dropped, and Intent reports it so you can opt in or ignore it.
When configured, `package.json#intent.skills` controls which discovered skills can surface through the CLI and agent integrations:

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.
- **Package entries** permit current and future skills from matching packages.
- **Exact skill entries** permit only the named skill.
- **Source kinds stay separate:** `foo` permits an npm source; `workspace:foo` permits a workspace source. Their wildcard patterns remain kind-specific. The exact `*` entry permits every discovered npm and workspace source.

Trust does not propagate. A listed package may depend on another package that ships skills, but that dependency stays unlisted unless another entry matches it. A bare entry such as `foo` permits an npm source, while `workspace:foo` permits a workspace source. Their wildcard forms remain kind-specific. The exact `*` entry permits every discovered npm and workspace source.
Trust does not propagate to dependencies. A dependency that ships skills needs its own matching entry. Intent omits unlisted packages and reports them so you can opt in or ignore them.

### Projects without an allowlist

The gate is opt-in today. Without an effective `intent.skills` declaration, discovery commands still surface every discovered package and print a deprecation notice to stderr. A future version will require an explicit allowlist. See [Special forms](./configuration#special-forms).

Default `intent install` handles this state through interactive permission setup.

## First-run permission review

When no effective policy exists, `intent install` follows this flow:

1. **Discover:** show npm and workspace packages, versions, and skill descriptions. Excluded candidates appear in the overview but cannot be selected.
2. **Choose:** select package-wide or exact-skill permissions. An empty selection explicitly confirms disabling all skills.
3. **Review:** show the exact `intent.skills` value and destination file.
4. **Confirm:** replace `package.json` atomically only after affirmative confirmation, then install guidance.

| Outcome | Files changed |
| --- | --- |
| No skills discovered, or all excluded | None. |
| Cancel any prompt | None. |
| Run first-time setup without a TTY | None; the command fails. |
| Save permissions, then fail to write or verify guidance | Confirmed permissions remain saved; the guidance failure is reported separately. |

The completion summary reports skills available under the saved policy. It does not prove that an agent loaded or applied them. See [Default install](../cli/intent-install#default-install) for picker controls and permission choices.

## Static discovery

Expand Down
Loading
Loading