Skip to content

feat: gate skills behind an intent.skills allowlist#156

Merged
LadyBluenotes merged 19 commits into
mainfrom
m1-allowlist
Jun 13, 2026
Merged

feat: gate skills behind an intent.skills allowlist#156
LadyBluenotes merged 19 commits into
mainfrom
m1-allowlist

Conversation

@LadyBluenotes

@LadyBluenotes LadyBluenotes commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

Adds package.json#intent.skills as an allowlist that decides which discovered packages may contribute skills. Until now, every installed package with a skills/ directory was trusted and surfaced. A skill is instructions an agent follows, so which packages can contribute is a trust decision — this makes that decision explicit.

Extends intent.exclude to skill-level granularity and routes every surface (list, load, install, stale fallback) through a single policy chokepoint.

Closes #145

How it works

intent.skills is an array of source entries:

  • @scope/pkg / pkg — an npm package in the dependency tree
  • workspace:@scope/pkg — a workspace member
  • git:<host>/<repo>#<ref> — reserved, parsed and validated but rejected until M2

Three whole-list special forms:

  • Absent (no key): every package surfaced, with a deprecation notice on stderr each run until you set the key. This is the v0 upgrade path; a future version will require an explicit allowlist.
  • Empty ([]): deny-all.
  • Wildcard (["*"]): allow-all, with an acknowledged-risk notice.

intent.exclude is applied after the allowlist and now matches skills, not just packages: @scope/pkg#search-params, globs like @scope/pkg#experimental-*, and cross-package *#experimental-*.

Design notes

  • Discovery never grants trust. Scanning finds packages; the allowlist gates them. Trust does not propagate to transitive dependencies.
  • Static discovery only. Intent reads package files; it never imports, requires, or executes a discovered package. The one sanctioned exception — loading Yarn's PnP runtime to resolve package locations — is enforced by an ESLint rule.
  • Notices vs warnings are separate channels. Trust/onboarding advisories (migration, allow-all, empty, unlisted) print to stderr as notices; discovery diagnostics stay on stdout as warnings. This keeps an agent reading list stdout from ingesting housekeeping messages as data. load filters notices out structurally on every path, and --json exposes notices as a separate field.
  • Matching is name-only in M1. workspace:foo and foo both authorize a discovered foo, because the scanner does not yet distinguish a workspace member from a same-named npm package. This errs toward permitting a same-named package, never toward denying one you listed. M2 tightens matching to kind + id once the scanner carries that signal.

Deliberately deferred

  • No persisted once-guard for the migration notice — it prints each run until you set the key. A per-project/per-machine cache adds filesystem writes to a read-only command and still re-fires on fresh clones / CI, for a transitional message that disappears when the allowlist becomes mandatory. Docs say "each run," matching behavior.
  • stale does not carry the consumer migration notice (it is maintainer-facing).
  • Empty-mode's "no packages" stdout line shows the reason only on stderr / in --json; making it stdout-mode-aware belongs with the future require-allowlist work.

Tests

  • Allowlist parser: source kinds, special forms, malformed-entry reporting, wildcard rules.
  • Policy: allow/deny matrix, unlisted summary, declared-but-undiscovered, exclude interaction, dedup.
  • All four surfaces filter excluded and unlisted sources.
  • CLI regression: the migration notice prints to stderr and never to stdout (mutation-checked: routing it to stdout fails the test).

Summary by CodeRabbit

  • New Features
    • Added clearer guidance for configuring intent.skills and intent.exclude, including allowlist/denylist behavior and new Concepts documentation.
    • intent list, intent load, intent install, and intent stale now surface notices in addition to warnings.
  • Bug Fixes
    • Improved handling of excluded and unlisted packages/skills, with more precise refusal and error messages.
    • Updated output so allowed packages and skills are shown more consistently across commands.
  • Documentation
    • Expanded quick-start, overview, and CLI docs to explain configuration, trust, and discovery behavior.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an intent.skills allowlist and skill-level exclude patterns to the Intent CLI. Introduces skill-sources.ts (parser), source-policy.ts (policy enforcement and unified scanForPolicedIntents), and refactored excludes.ts with matchesPackage/matchesSkill matchers. Rewires all four CLI commands and core scan paths. Adds notices propagation throughout, ESLint static-discovery guards, comprehensive tests, and new Concepts documentation (trust model, configuration).

Changes

intent.skills allowlist + skill-level excludes

Layer / File(s) Summary
Core type contracts
packages/intent/src/types.ts, packages/intent/src/core/types.ts
ScanResult gains notices: Array<string>; IntentSkillList gains notices, IntentSkillListDebug gains noticeCount, and IntentCoreErrorCode expands with package-not-listed and skill-excluded.
skill-sources.ts parser
packages/intent/src/core/skill-sources.ts
New module defining SkillSource, SkillSourcesConfig (absent/empty/allow-all/explicit modes), SkillSourcesParseError with aggregated issues, and parseSkillSources — the strict fail-whole-list parser for intent.skills entries.
excludes.ts: ExcludeMatcher + isSkillExcluded
packages/intent/src/core/excludes.ts
ExcludeMatcher refactored from matches to matchesPackage + optional matchesSkill; exports getConfigDirs; glob compilation extended with #-separated skill segments; new isSkillExcluded with prefix-alias awareness.
source-policy.ts: policy gate + scanForPolicedIntents
packages/intent/src/core/source-policy.ts
New module: notice string constants, checkLoadAllowed (returns structured refusals), applySourcePolicy (filters packages/skills and emits de-duplicated notices), readSkillSourcesConfig, and scanForPolicedIntents (unified scanning entry point).
scanner.ts: notices init + static-discovery docs
packages/intent/src/scanner.ts
Both ScanResult return paths initialized with notices: []; top-of-file comments document the static-discovery invariant; ESLint exemption added at the sanctioned Yarn PnP load site.
core.ts: rewire list + load through policed scan
packages/intent/src/core.ts
listIntentSkills uses scanForPolicedIntents and populates warnings/notices/conflicts directly from its output; resolveIntentSkillInCwd uses checkLoadAllowed before scanning and scanForPolicedIntents for the full-scan path.
CLI output + support wiring
packages/intent/src/cli-output.ts, packages/intent/src/cli-support.ts
printNotices added and re-exported; scanIntentsOrFail accepts IntentCoreOptions and delegates to scanForPolicedIntents.
CLI commands: notices + IntentCoreOptions
packages/intent/src/commands/install.ts, ...list.ts, ...load.ts, ...stale.ts
All four commands pass IntentCoreOptions instead of ScanOptions, print notices at all exit paths, remove unused _scanIntentsOrFail override hooks from list and load, and improve stale error handling via isCliFailure.
ESLint static-discovery guards
eslint.config.js
Two new rule blocks: policed-scanner-import bans direct scanForIntents usage; static-discovery forbids dynamic import() with computed paths and require()/createRequire().resolve.
Unit tests
packages/intent/tests/skill-sources.test.ts, tests/excludes.test.ts, tests/source-policy.test.ts, tests/core.test.ts, tests/resolver.test.ts, tests/install-writer.test.ts
Comprehensive new test suites for parseSkillSources, isSkillExcluded, applySourcePolicy/readSkillSourcesConfig, and extended listIntentSkills/loadIntentSkill allowlist/exclude scenarios; fixture helpers updated with notices: [].
Integration + CLI tests
packages/intent/tests/integration/source-policy-surfaces.test.ts, tests/cli.test.ts
Integration tests covering all four CLI surfaces (list, load, install --map, stale --json); CLI tests require intent.skills in fixtures and validate migration-notice stderr routing; obsolete load scan-spy test removed.
Docs: trust model, configuration, CLI, overview
docs/concepts/trust-model.md, docs/concepts/configuration.md, docs/config.json, docs/overview.md, docs/getting-started/quick-start-consumers.md, docs/cli/intent-*.md
New trust-model and configuration concept pages; CLI reference pages updated for allowlist/skill-exclude semantics; overview and consumer quick-start revised; Concepts section added to nav.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as CLI command (install/list/load/stale)
  participant CLISupport as cli-support.ts
  participant SPF as scanForPolicedIntents
  participant Scanner as scanForIntents
  participant RSC as readSkillSourcesConfig
  participant ASP as applySourcePolicy
  participant Core as core.ts (listIntentSkills / resolveIntentSkillInCwd)

  CLI->>CLISupport: scanIntentsOrFail(IntentCoreOptions)
  CLISupport->>SPF: { cwd, scanOptions, coreOptions }
  SPF->>Scanner: scanForIntents(scanOptions)
  Scanner-->>SPF: ScanResult { packages, warnings, notices: [] }
  SPF->>RSC: readSkillSourcesConfig(cwd)
  RSC-->>SPF: SkillSourcesConfig (absent/empty/allow-all/explicit)
  SPF->>ASP: applySourcePolicy(packages, { config, excludeMatchers })
  ASP-->>SPF: SourcePolicyResult { packages, notices }
  SPF-->>CLISupport: PolicedScan { scan, excludePatterns }
  CLISupport-->>CLI: ScanResult (with notices)
  CLI->>Core: listIntentSkills / resolveIntentSkillInCwd
  Core-->>CLI: IntentSkillList { skills, warnings, notices }
  CLI->>CLI: printWarnings + printNotices (stderr)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • Explicit skill sources (allowlist) #145: The changes directly implement the package.json#intent.skills allowlist gate, skill-level #-pattern excludes, notices propagation, and the documented trust model described in that issue.

Possibly related PRs

  • TanStack/intent#124: Both PRs modify packages/intent/src/cli-support.ts, core/excludes.ts, and core.ts — this PR builds the policy layer directly on top of the exclude and core scanning infrastructure touched by that earlier PR.

Poem

🐇 Hopping through the skill tree with care,
An allowlist now guards what packages share.
intent.skills says who gets to play,
Unlisted bunnies? Gently turned away.
With notices on stderr, trust is clear —
Only sanctioned packages make it here! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: gate skills behind an intent.skills allowlist' is specific and clear, accurately summarizing the main feature addition: implementing an allowlist mechanism for controlling which packages can contribute skills.
Description check ✅ Passed The PR description comprehensively covers the changes, including how the allowlist works, design principles, and deferred decisions. While the checklist items are not checked, the description itself is complete and informative.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch m1-allowlist

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud

nx-cloud Bot commented Jun 13, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 349014b

Command Status Duration Result
nx affected --targets=test:eslint,test:sherif,t... ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-13 23:34:33 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jun 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/TanStack/intent/@tanstack/intent@156

commit: 349014b

@codspeed-hq

codspeed-hq Bot commented Jun 13, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing m1-allowlist (349014b) with main (94a7a82)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (61c95c8) during the generation of this report, so 94a7a82 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/intent/src/commands/list.ts (1)

95-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Route notices to stderr in JSON mode instead of stdout JSON payload.

Line 95 enters JSON mode and returns on Line 102 before printNotices(...) runs, so notices are emitted on stdout as jsonResult.notices rather than the stderr notice channel used elsewhere. This breaks the notice-channel contract and makes JSON mode behavior inconsistent with non-JSON mode.

Suggested fix
   if (options.json) {
     const {
       debug: _debug,
       packageManager: _packageManager,
+      notices: _notices,
       ...jsonResult
     } = result
     console.log(JSON.stringify(jsonResult, null, 2))
+    printNotices(result.notices)
     return
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/intent/src/commands/list.ts` around lines 95 - 103, In the list
command’s JSON branch in list.ts (the options.json handling inside the list
command), make sure notices are still routed through the stderr notice channel
instead of being left inside the JSON payload; keep JSON output on stdout, but
remove notices from the serialized result and call the existing printNotices
flow or equivalent stderr path before returning so JSON mode matches the
non-JSON notice contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/intent/src/commands/list.ts`:
- Around line 95-103: In the list command’s JSON branch in list.ts (the
options.json handling inside the list command), make sure notices are still
routed through the stderr notice channel instead of being left inside the JSON
payload; keep JSON output on stdout, but remove notices from the serialized
result and call the existing printNotices flow or equivalent stderr path before
returning so JSON mode matches the non-JSON notice contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42c78428-fb91-4875-a253-f8d56bd52ea2

📥 Commits

Reviewing files that changed from the base of the PR and between 61c95c8 and 349014b.

📒 Files selected for processing (31)
  • docs/cli/intent-install.md
  • docs/cli/intent-list.md
  • docs/cli/intent-load.md
  • docs/cli/intent-stale.md
  • docs/concepts/configuration.md
  • docs/concepts/trust-model.md
  • docs/config.json
  • docs/getting-started/quick-start-consumers.md
  • docs/overview.md
  • eslint.config.js
  • packages/intent/src/cli-output.ts
  • packages/intent/src/cli-support.ts
  • packages/intent/src/commands/install.ts
  • packages/intent/src/commands/list.ts
  • packages/intent/src/commands/load.ts
  • packages/intent/src/commands/stale.ts
  • packages/intent/src/core.ts
  • packages/intent/src/core/excludes.ts
  • packages/intent/src/core/skill-sources.ts
  • packages/intent/src/core/source-policy.ts
  • packages/intent/src/core/types.ts
  • packages/intent/src/scanner.ts
  • packages/intent/src/types.ts
  • packages/intent/tests/cli.test.ts
  • packages/intent/tests/core.test.ts
  • packages/intent/tests/excludes.test.ts
  • packages/intent/tests/install-writer.test.ts
  • packages/intent/tests/integration/source-policy-surfaces.test.ts
  • packages/intent/tests/resolver.test.ts
  • packages/intent/tests/skill-sources.test.ts
  • packages/intent/tests/source-policy.test.ts
💤 Files with no reviewable changes (1)
  • packages/intent/src/commands/load.ts

@LadyBluenotes LadyBluenotes merged commit 0e9d30e into main Jun 13, 2026
11 of 12 checks passed
@LadyBluenotes LadyBluenotes deleted the m1-allowlist branch June 13, 2026 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explicit skill sources (allowlist)

1 participant