You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: gate skills behind an intent.skills allowlist (#156)
* parse intent.skills allowlist into typed SkillSource config
* format
* extend intent.exclude matching to package#skill granularity
* format
* add applySourcePolicy allowlist gate and intent.skills reader
* route list through policed scan chokepoint, drop inline exclude filter
* gate load through allowlist and skill-level excludes on both paths
* police install and stale-fallback scans through the allowlist chokepoint
* assert all four surfaces filter excluded and unlisted sources
* fix
* lint-enforce the policed-scan chokepoint and static-discovery invariant
* make knip happy
* update
* remove dead scan-injection params and a vacuous load test
* docs
* avoid per-call warnings
* regression guard
* fix docs
* fixes
-`@scope/pkg` or `pkg`: an npm package reachable through the dependency tree.
102
+
-`workspace:@scope/pkg`: a package in the current workspace.
103
+
-`git:<host>/<repo>#<ref>`: reserved, and not yet supported.
104
+
105
+
The list as a whole has three special forms:
106
+
107
+
-**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.
108
+
-**Empty** (`"skills": []`): no package is surfaced, with an info notice printed to stderr.
109
+
-**Wildcard** (`"skills": ["*"]`): every discovered package is surfaced, with an acknowledged-risk notice printed to stderr.
110
+
111
+
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).
112
+
86
113
## Excludes
87
114
88
-
Package excludes are hard filters for packages that should not be used in a repo.
115
+
Package excludes are hard filters for packages that should not be used in a repo, applied after the allowlist.
89
116
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.
Exclude patterns match full package names. In v1, only exact names and `*` wildcards are supported.
126
+
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.
127
+
128
+
An excluded package never triggers the unlisted-source warning, because an exclude is an explicit decision rather than an oversight.
Copy file name to clipboardExpand all lines: docs/cli/intent-stale.md
+54-53Lines changed: 54 additions & 53 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,28 +15,29 @@ npx @tanstack/intent@latest stale [--json]
15
15
16
16
## Behavior
17
17
18
-
- Checks the current package by default
19
-
- From a monorepo root, checks workspace packages that ship skills and also reports public workspace packages with no skill or artifact coverage
20
-
- When `dir` is provided, scopes the check to the targeted package or skills directory
21
-
- Computes one staleness report per package
22
-
- Reads repo-root `_artifacts/*domain_map.yaml` and `_artifacts/*skill_tree.yaml` when present
23
-
- Flags public workspace packages that are not represented by generated skills or artifact coverage
24
-
- Skips workspace packages with `"private": true`
25
-
- Prints text output by default or JSON with `--json`
26
-
- Prints a non-failing workflow update reminder when `.github/workflows/check-skills.yml` is missing the current `intent-workflow-version` stamp
27
-
- If no packages are found, prints `No intent-enabled packages found.`
28
-
29
-
Artifact coverage ignores can be recorded in `_artifacts/*skill_tree.yaml` or `_artifacts/*domain_map.yaml`:
30
-
31
-
```yaml
32
-
coverage:
33
-
ignored_packages:
34
-
- '@tanstack/internal-tooling'
35
-
- name: packages/devtools-fixture
36
-
reason: test fixture only
37
-
```
38
-
39
-
Ignored packages are excluded from missing coverage signals. Private workspace packages are excluded automatically.
18
+
- Checks the current package by default
19
+
- From a monorepo root, checks workspace packages that ship skills and also reports public workspace packages with no skill or artifact coverage
20
+
- 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).
21
+
- When `dir` is provided, scopes the check to the targeted package or skills directory
22
+
- Computes one staleness report per package
23
+
- Reads repo-root `_artifacts/*domain_map.yaml` and `_artifacts/*skill_tree.yaml` when present
24
+
- Flags public workspace packages that are not represented by generated skills or artifact coverage
25
+
- Skips workspace packages with `"private": true`
26
+
- Prints text output by default or JSON with `--json`
27
+
- Prints a non-failing workflow update reminder when `.github/workflows/check-skills.yml` is missing the current `intent-workflow-version` stamp
28
+
- If no packages are found, prints `No intent-enabled packages found.`
29
+
30
+
Artifact coverage ignores can be recorded in `_artifacts/*skill_tree.yaml` or `_artifacts/*domain_map.yaml`:
31
+
32
+
```yaml
33
+
coverage:
34
+
ignored_packages:
35
+
- '@tanstack/internal-tooling'
36
+
- name: packages/devtools-fixture
37
+
reason: test fixture only
38
+
```
39
+
40
+
Ignored packages are excluded from missing coverage signals. Private workspace packages are excluded automatically.
40
41
41
42
## JSON report schema
42
43
@@ -49,54 +50,54 @@ Ignored packages are excluded from missing coverage signals. Private workspace p
49
50
"currentVersion": "string | null",
50
51
"skillVersion": "string | null",
51
52
"versionDrift": "major | minor | patch | null",
52
-
"skills": [
53
-
{
54
-
"name": "string",
55
-
"reasons": ["string"],
56
-
"needsReview": true
57
-
}
58
-
],
59
-
"signals": [
60
-
{
61
-
"type": "missing-package-coverage",
62
-
"library": "string",
63
-
"subject": "string",
64
-
"reasons": ["string"],
65
-
"needsReview": true,
66
-
"packageName": "string",
67
-
"packageRoot": "string"
68
-
}
69
-
]
70
-
}
71
-
]
53
+
"skills": [
54
+
{
55
+
"name": "string",
56
+
"reasons": ["string"],
57
+
"needsReview": true
58
+
}
59
+
],
60
+
"signals": [
61
+
{
62
+
"type": "missing-package-coverage",
63
+
"library": "string",
64
+
"subject": "string",
65
+
"reasons": ["string"],
66
+
"needsReview": true,
67
+
"packageName": "string",
68
+
"packageRoot": "string"
69
+
}
70
+
]
71
+
}
72
+
]
72
73
```
73
74
74
75
Report fields:
75
76
76
77
- `library`: package name
77
78
- `currentVersion`: latest version from npm registry (or `null` if unavailable)
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).
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.
18
+
19
+
## `intent.skills`
20
+
21
+
`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.
22
+
23
+
### Source entries
24
+
25
+
Each array entry names one source:
26
+
27
+
| Entry | Kind | Meaning |
28
+
| ----- | ---- | ------- |
29
+
|`@scope/pkg` or `pkg`| npm | A package reachable through the dependency tree, direct or transitive. |
30
+
|`workspace:@scope/pkg`| workspace | A package in the current workspace. |
31
+
|`git:<host>/<repo>#<ref>`| git | Reserved. Not yet supported, and rejected until a future version adds it. |
32
+
33
+
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.
34
+
35
+
### Special forms
36
+
37
+
The list as a whole has three special forms:
38
+
39
+
-**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.
40
+
-**Empty.**`"skills": []`. No package is surfaced. Intent prints an info notice to stderr.
41
+
-**Wildcard.**`"skills": ["*"]`. Every discovered package is surfaced. Intent prints an acknowledged-risk notice to stderr, since unvetted skills may reach your agent.
42
+
43
+
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.
44
+
45
+
### Existing projects
46
+
47
+
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.
48
+
49
+
## `intent.exclude`
50
+
51
+
`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.
- A pattern without `#` excludes a whole package: `@scope/pkg`.
64
+
- A pattern with `#` excludes a single skill: `@scope/pkg#search-params`.
65
+
- The skill segment may be a glob: `@scope/pkg#experimental-*`.
66
+
- A pattern may cross package boundaries at skill granularity: `*#experimental-*`.
67
+
- The `#*` shortcut excludes the whole package: `@scope/pkg#*`.
68
+
69
+
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.
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.
7
+
8
+
## Explicit sources
9
+
10
+
A package ships skills in a `skills/` directory. Discovery finds every installed package that has one, including transitive dependencies. Discovery does not grant trust.
11
+
12
+
`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.
13
+
14
+
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.
15
+
16
+
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.
17
+
18
+
## Static discovery
19
+
20
+
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.
21
+
22
+
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.
23
+
24
+
## What the allowlist does not cover yet
25
+
26
+
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.
27
+
28
+
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.
0 commit comments