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

Add `package.json#intent.skills` source allowlisting to gate which discovered packages can contribute skills.

`intent.exclude` now supports skill-level matching (for example `@scope/pkg#skill-id` and globs), and policy filtering is applied consistently across `intent list`, `intent load`, `intent install`, and `intent stale`. Notices are surfaced separately from warnings to keep command output machine-safe.
12 changes: 12 additions & 0 deletions .changeset/green-dingos-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@tanstack/intent': minor
---

Add a persistent `intent exclude` command for managing `package.json#intent.exclude` (`list`, `add`, `remove`), and document it in the CLI/config guides.

Add notice suppression controls for automation:

- `--no-notices` on `intent list` and `intent install`
- `INTENT_NO_NOTICES=1` environment variable

Remove one-off CLI exclude flags from command surfaces (`list/load --exclude`); excludes are now managed via `package.json#intent.exclude` and `intent exclude`.
43 changes: 43 additions & 0 deletions docs/cli/intent-exclude.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: intent exclude
id: intent-exclude
---

`intent exclude` manages `package.json#intent.exclude` entries.

```bash
npx @tanstack/intent@latest exclude [list|add|remove] [pattern] [--json]
```

## Options

- `--json`: print the configured exclude patterns as JSON

## Actions

1. `list` (default): print current excludes
2. `add <pattern>`: append one exclude pattern
3. `remove <pattern>`: remove one exclude pattern

## Examples

```bash
npx @tanstack/intent@latest exclude
npx @tanstack/intent@latest exclude list --json
npx @tanstack/intent@latest exclude add @tanstack/router#experimental-*
npx @tanstack/intent@latest exclude remove @tanstack/router#experimental-*
```

## Behavior

- Reads and writes the current working directory `package.json`
- Creates `intent.exclude` when missing
- Keeps existing excludes and appends new patterns in order
- Validates pattern syntax before writing
- Refuses invalid `package.json` structures for `intent` and `intent.exclude`

## Related

- [Configuration](../concepts/configuration)
- [intent list](./intent-list)
- [intent load](./intent-load)
5 changes: 4 additions & 1 deletion docs/cli/intent-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ id: intent-install
`intent install` 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]
npx @tanstack/intent@latest install [--map] [--dry-run] [--print-prompt] [--global] [--global-only] [--no-notices]
```

## Options
Expand All @@ -16,6 +16,7 @@ npx @tanstack/intent@latest install [--map] [--dry-run] [--print-prompt] [--glob
- `--print-prompt`: print the agent setup prompt instead of writing files
- `--global`: include global packages after project packages when `--map` is passed
- `--global-only`: install mappings from global packages only when `--map` is passed
- `--no-notices`: suppress non-critical notices on stderr

## Behavior

Expand Down Expand Up @@ -75,6 +76,8 @@ skills:
- 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.`

To suppress trust and migration notices in automation, pass `--no-notices`.

## Related

- [intent list](./intent-list)
Expand Down
10 changes: 6 additions & 4 deletions docs/cli/intent-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,31 @@ id: intent-list
`intent list` discovers skill-enabled packages and prints available skills.

```bash
npx @tanstack/intent@latest list [--json] [--debug] [--exclude <pattern>] [--global] [--global-only]
npx @tanstack/intent@latest list [--json] [--debug] [--global] [--global-only] [--no-notices]
```

## Options

- `--json`: print JSON instead of text output
- `--debug`: print discovery debug details to stderr
- `--exclude <pattern>`: exclude package names matching a simple glob; can be passed more than once
- `--global`: include global packages after project packages
- `--global-only`: list global packages only
- `--no-notices`: suppress non-critical notices on stderr

## 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 and skills matched by package.json `intent.exclude` or `--exclude`
- Excludes packages and skills matched by package.json `intent.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
- Package table columns: `PACKAGE`, `SOURCE`, `VERSION`, `SKILLS`
- Skill tree grouped by package
- Optional warnings section (`⚠ ...` per warning)
- Optional notices section on stderr (`ℹ ...` per notice), suppressed by `--no-notices`

`SOURCE` is a lightweight indicator showing whether the selected package came from local discovery or explicit global scanning.
When both local and global packages are scanned, local packages take precedence.
Expand Down Expand Up @@ -113,7 +114,8 @@ A package that ships skills but is not listed is dropped. When packages are drop
## Excludes

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.
Intent reads `intent.exclude` arrays from package.json files while walking from the workspace or project root to the current working directory.
Manage persistent excludes with `intent exclude add|remove|list`.

```json
{
Expand Down
5 changes: 2 additions & 3 deletions docs/cli/intent-load.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ id: intent-load
`intent load` loads a compact skill identity from the current install and prints the matching `SKILL.md` content.

```bash
npx @tanstack/intent@latest load <package>#<skill> [--path] [--json] [--debug] [--exclude <pattern>] [--global] [--global-only]
npx @tanstack/intent@latest load <package>#<skill> [--path] [--json] [--debug] [--global] [--global-only]
```

## Options

- `--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 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 @@ -24,7 +23,7 @@ npx @tanstack/intent@latest load <package>#<skill> [--path] [--json] [--debug] [
- Scans project-local packages by default
- Includes global packages only when `--global` or `--global-only` is passed
- 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`
- Refuses before scanning when the target package or skill matches `intent.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
21 changes: 20 additions & 1 deletion docs/concepts/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,28 @@ A package that ships skills but is not listed is dropped. When packages are drop

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.

### Suppressing notices temporarily

Use `--no-notices` to suppress non-critical notices on stderr for one run:

```bash
npx @tanstack/intent@latest list --no-notices
npx @tanstack/intent@latest install --map --no-notices
```

For CI or wrapper scripts, set `INTENT_NO_NOTICES=1` to suppress notices without changing command arguments.

## `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.
`intent.exclude` removes packages or individual skills after the allowlist resolves.

Use `intent exclude` to manage this list from the CLI:

```bash
npx @tanstack/intent@latest exclude add @tanstack/router#experimental-*
npx @tanstack/intent@latest exclude remove @tanstack/router#experimental-*
npx @tanstack/intent@latest exclude list
```

```json
{
Expand Down
4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
"label": "intent install",
"to": "cli/intent-install"
},
{
"label": "intent exclude",
"to": "cli/intent-exclude"
},
{
"label": "intent list",
"to": "cli/intent-list"
Expand Down
21 changes: 20 additions & 1 deletion packages/intent/src/cli-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,27 @@ export function printWarnings(warnings: Array<string>): void {
}
}

export function printNotices(notices: Array<string>): void {
export interface NoticeOutputOptions {
noNotices?: boolean
}

const TRUE_LIKE_VALUES = new Set(['1', 'true', 'yes', 'on'])

function envSuppressesNotices(): boolean {
const value = process.env.INTENT_NO_NOTICES?.trim().toLowerCase()
return value ? TRUE_LIKE_VALUES.has(value) : false
}

function shouldSuppressNotices(options: NoticeOutputOptions = {}): boolean {
return options.noNotices === true || envSuppressesNotices()
}

export function printNotices(
notices: Array<string>,
options: NoticeOutputOptions = {},
): void {
if (notices.length === 0) return
if (shouldSuppressNotices(options)) return

console.error('Notices:')
for (const notice of notices) {
Expand Down
14 changes: 8 additions & 6 deletions packages/intent/src/cli-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ export { printNotices, printWarnings } from './cli-output.js'

export interface GlobalScanFlags {
debug?: boolean
exclude?: string | Array<string>
global?: boolean
globalOnly?: boolean
notices?: boolean
noNotices?: boolean
}

export interface StaleTargetResult {
Expand Down Expand Up @@ -89,16 +90,17 @@ export function coreOptionsFromGlobalFlags(

return {
debug: options.debug,
exclude: Array.isArray(options.exclude)
? options.exclude
: options.exclude
? [options.exclude]
: undefined,
global: options.global,
globalOnly: options.globalOnly,
}
}

export function noticeOptionsFromGlobalFlags(options: GlobalScanFlags): {
noNotices?: boolean
} {
return { noNotices: options.noNotices || options.notices === false }
}

function formatDebugValue(value: string | number | Array<string>): string {
if (Array.isArray(value)) {
return value.length > 0 ? value.join(', ') : '(none)'
Expand Down
37 changes: 28 additions & 9 deletions packages/intent/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url'
import { cac } from 'cac'
import { fail, isCliFailure } from './cli-error.js'
import type { CAC } from 'cac'
import type { ExcludeCommandOptions } from './commands/exclude.js'
import type { InstallCommandOptions } from './commands/install.js'
import type { ListCommandOptions } from './commands/list.js'
import type { LoadCommandOptions } from './commands/load.js'
Expand All @@ -20,14 +21,12 @@ function createCli(): CAC {
'list',
'Discover intent-enabled packages from the project or workspace',
)
.usage(
'list [--json] [--debug] [--exclude <pattern>] [--global] [--global-only]',
)
.usage('list [--json] [--debug] [--global] [--global-only] [--no-notices]')
.option('--json', 'Output JSON')
.option('--debug', 'Print discovery debug details to stderr')
.option('--exclude <pattern>', 'Exclude package name glob')
.option('--global', 'Include global packages after project packages')
.option('--global-only', 'List global packages only')
.option('--no-notices', 'Suppress non-critical notices on stderr')
.example('list')
.example('list --json')
.example('list --global')
Expand All @@ -37,14 +36,33 @@ function createCli(): CAC {
})

cli
.command('load [use]', 'Load a compact skill use and print its SKILL.md')
.usage(
'load <use> [--path] [--json] [--debug] [--exclude <pattern>] [--global] [--global-only]',
.command(
'exclude [action] [pattern]',
'Manage package.json intent.exclude entries',
)
.usage('exclude [list|add|remove] [pattern] [--json]')
.option('--json', 'Output JSON list of configured exclude patterns')
.example('exclude')
.example('exclude list --json')
.example('exclude add @tanstack/router#experimental-*')
.example('exclude remove @tanstack/router#experimental-*')
.action(
async (
action: string | undefined,
pattern: string | undefined,
options: ExcludeCommandOptions,
) => {
const { runExcludeCommand } = await import('./commands/exclude.js')
await runExcludeCommand(action, pattern, options)
},
)

cli
.command('load [use]', 'Load a compact skill use and print its SKILL.md')
.usage('load <use> [--path] [--json] [--debug] [--global] [--global-only]')
.option('--path', 'Print the resolved skill path instead of file content')
.option('--json', 'Output JSON')
.option('--debug', 'Print resolution debug details to stderr')
.option('--exclude <pattern>', 'Exclude package name glob')
.option('--global', 'Load from project packages, then global packages')
.option('--global-only', 'Load from global packages only')
.example('load @tanstack/query#core')
Expand Down Expand Up @@ -86,7 +104,7 @@ function createCli(): CAC {
'Create or update skill loading guidance in an agent config file',
)
.usage(
'install [--map] [--dry-run] [--print-prompt] [--global] [--global-only]',
'install [--map] [--dry-run] [--print-prompt] [--global] [--global-only] [--no-notices]',
)
.option('--map', 'Write explicit skill-to-task mappings')
.option('--dry-run', 'Print the generated block without writing')
Expand All @@ -96,6 +114,7 @@ function createCli(): CAC {
)
.option('--global', 'Include global packages after project packages')
.option('--global-only', 'Install mappings from global packages only')
.option('--no-notices', 'Suppress non-critical notices on stderr')
.example('install')
.example('install --map')
.example('install --dry-run')
Expand Down
Loading
Loading