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/review-current-permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/intent': minor
---

Add `intent install --review` to revisit existing skill permissions, inspect current access, and confirm additions, removals, and individual exclusions. Preserve undiscovered rules and inherited permissions unless explicitly changed. Keep default configured installs guidance-only, reuse one discovery scan throughout review, and compile permission rules once per selection pass.
27 changes: 26 additions & 1 deletion docs/cli/intent-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ npx @tanstack/intent@latest install [--map] [--dry-run] [--print-prompt] [--glob

## Options

### Permission review

- `--review`: review current skill permissions interactively, then update guidance

### Guidance output

- `--map`: write explicit task-to-skill mappings instead of lightweight loading guidance
Expand All @@ -27,7 +31,7 @@ npx @tanstack/intent@latest install [--map] [--dry-run] [--print-prompt] [--glob

### Default install

If `intent.skills` is already configured, including through workspace inheritance, `install` only updates guidance. It does not prompt or change `package.json`.
If `intent.skills` is already configured, including through workspace inheritance, `install` only updates guidance. It does not prompt or change `package.json`. Run `intent install --review` to change permissions.

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.

Expand Down Expand Up @@ -66,6 +70,27 @@ After permissions are saved, Intent updates an existing managed guidance block i
- **Decline or cancel a prompt:** writes neither permissions nor guidance.
- **`--dry-run`:** performs discovery and selection, previews permissions and guidance, and writes neither file.


### Review existing permissions

```bash
npx @tanstack/intent@latest install --review
```

Review starts from the current `intent.skills` rules. Continue with them, add packages/scopes/individual skills, remove explicit rules, or review individual skills within enabled packages. Existing rules stay intact unless you change them, including rules for packages or skills that are **not discovered**. Removing a rule requires unchecking it; Intent never removes it automatically.

**Inspect access and descriptions** shows whether each current candidate is permitted by a matching rule or blocked by the allowlist or `intent.exclude`. Searchable lists show at most six options at a time; descriptions appear on request. Package and scope rules continue to cover future matching skills. Adding a skill already covered by an existing rule does not add a redundant permission.

Unchecking a skill covered by a broader rule adds an exclusion. Existing exclusions stay in effect and cannot be removed through this picker; use [`intent exclude`](./intent-exclude) from the directory containing the exclusion to remove one.

The confirmation previews the destination, additions, removals, and new exclusions. Choose **Show exact proposed configuration** in the review menu for complete arrays. Canceling writes neither permissions nor guidance. `--review --dry-run` walks through review and prints the preview without saving either file.

In a workspace, inherited permissions are the starting selection. If you change them, confirmation creates an override in the nearest owning `package.json`; it does not edit the ancestor. Continuing unchanged preserves inheritance. Inherited exclusions still apply. If a policy manifest changes during review, the command stops and asks you to retry.

Review requires a terminal and cannot be combined with `--map`, `--print-prompt`, `--global`, or `--global-only`. With no effective policy, `--review` opens first-run setup. Plain `install` retains its guidance-only behavior for configured projects.

Review scans local candidates once and reuses that result throughout the prompts and completion counts. It compares current permissions with proposed edits. It does **not** detect newly discovered skills relative to an earlier run, content changes, hashes, or delivery drift. Permissions and guidance results are reported separately; a guidance failure after saving does not undo confirmed permissions.

### Mapping mode

- Scans packages and writes compact `id`, `run`, and `for` mappings only when `--map` is passed.
Expand Down
6 changes: 6 additions & 0 deletions docs/concepts/trust-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ When no effective policy exists, `intent install` follows this flow:

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.

## Revisiting permissions

Run `intent install --review` to revisit current permissions. Existing decisions are retained until you confirm changes. The review can add permission rules, remove selected rules, and add individual exclusions under broader rules. Existing exclusions continue to win.

A review inside a workspace starts with inherited permissions. Confirmed edits create a local override; an unchanged review preserves inheritance. This reviews permission configuration, not whether skill content has changed. See [Review existing permissions](../cli/intent-install#review-existing-permissions).

## 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.
Expand Down
4 changes: 3 additions & 1 deletion packages/intent/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ function createCli(runtime: InstallCommandRuntime = {}): CAC {
'Create or update skill loading guidance in an agent config file',
)
.usage(
'install [--map] [--dry-run] [--print-prompt] [--global] [--global-only] [--no-notices]',
'install [--review] [--map] [--dry-run] [--print-prompt] [--global] [--global-only] [--no-notices]',
)
.option('--review', 'Review and change skill permissions interactively')
.option('--map', 'Write explicit skill-to-task mappings')
.option('--dry-run', 'Print the generated block without writing')
.option(
Expand All @@ -138,6 +139,7 @@ function createCli(runtime: InstallCommandRuntime = {}): CAC {
.option('--global-only', 'Install mappings from global packages only')
.option('--no-notices', 'Suppress non-critical notices on stderr')
.example('install')
.example('install --review')
.example('install --map')
.example('install --dry-run')
.example('install --print-prompt')
Expand Down
43 changes: 29 additions & 14 deletions packages/intent/src/commands/install/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ tanstackIntent:
export interface InstallCommandOptions extends GlobalScanFlags {
dryRun?: boolean
map?: boolean
review?: boolean
printPrompt?: boolean
}

Expand Down Expand Up @@ -211,6 +212,19 @@ export async function runInstallCommand(
scanIntentsOrFail: (coreOptions?: IntentCoreOptions) => Promise<ScanResult>,
runtime: InstallCommandRuntime = {},
): Promise<void> {
if (
options.review &&
(options.map || options.printPrompt || options.global || options.globalOnly)
) {
fail(
'--review cannot be combined with --map, --print-prompt, --global, or --global-only.',
)
}
if (options.review && !(runtime.isTTY ?? process.stdin.isTTY === true)) {
fail(
'Permission review requires an interactive terminal. Run `intent install --review` in a terminal.',
)
}
if (options.printPrompt) {
console.log(INSTALL_PROMPT)
return
Expand All @@ -225,7 +239,7 @@ export async function runInstallCommand(
ReturnType<typeof setupInitialPermissions>
> | null = null

if (policy.mode === 'absent') {
if (policy.mode === 'absent' || options.review) {
const isTTY = runtime.isTTY ?? process.stdin.isTTY === true
if (!isTTY) {
fail(
Expand All @@ -236,6 +250,7 @@ export async function runInstallCommand(
try {
permissions = await setupInitialPermissions({
dryRun: options.dryRun,
review: options.review,
root: process.cwd(),
runtime: {
prompts: runtime.permissionPrompts ?? createPermissionPrompts(),
Expand Down Expand Up @@ -273,7 +288,8 @@ export async function runInstallCommand(
return
}

const available = permissions ? await scanIntentsOrFail() : null
const available =
permissions && !permissions.available ? await scanIntentsOrFail() : null
try {
const result = writeIntentSkillsBlock({
...generated,
Expand Down Expand Up @@ -305,26 +321,25 @@ export async function runInstallCommand(
} else {
printWriteResult(result)
}
if (!permissions)
console.log('To change permissions, run intent install --review.')
printPlacementTip(result.targetPath)
if (available && permissions) {
const packages = available.packages.filter(
(pkg) => pkg.skills.length > 0,
)
const skillCount = packages.reduce(
(count, pkg) => count + pkg.skills.length,
0,
)
if (permissions && (available || permissions.available)) {
const packages =
available?.packages.filter((pkg) => pkg.skills.length > 0) ?? []
const packageCount = permissions.available?.packages ?? packages.length
const skillCount =
permissions.available?.skills ??
packages.reduce((count, pkg) => count + pkg.skills.length, 0)
console.log(
`Available: ${skillCount} ${skillCount === 1 ? 'skill' : 'skills'} from ${packages.length} ${packages.length === 1 ? 'package' : 'packages'}.`,
`Available: ${skillCount} ${skillCount === 1 ? 'skill' : 'skills'} from ${packageCount} ${packageCount === 1 ? 'package' : 'packages'}.`,
)
if (skillCount > 0) {
console.log(
`Next: ${formatIntentCommand(detectIntentCommandPackageManager(), 'list')}`,
)
} else {
console.log(
`To enable skills, edit intent.skills in ${formatTargetPath(permissions.packageJsonPath)} and run intent install again.`,
)
console.log('To change permissions, run intent install --review.')
}
}
return
Expand Down
Loading
Loading