Skip to content

Commit 36e696b

Browse files
LadyBluenotesautofix-ci[bot]KyleAMathewsclaude
authored
feat: add dependency-walked discovery and source-aware package reporting (#112)
* feat: enhance scanForIntents to include global packages * fix: update scanForIntents calls to toggle global package inclusion * feat: modify scanIntentsOrFail to accept options for global package inclusion * feat: add source attribute to package registration for local and global distinction * test: verify package source distinction between local and global * feat: refactor package registration logic into createPackageRegistrar * feat: implement dependency walker to streamline transitive dependency discovery * feat: reorder report check and enhance global package handling in CLI tests * feat: consolidate discovery exports into a single index file * refactor: remove unused walkDeps function from walk.ts * update some docs * update some docs * update some docs * ci: apply automated fixes * fix tests * Apply code simplification and review fixes - Simplify walk.ts/register.ts (extract helpers, unify walkDeps variants) - Restore ENOENT-discriminated warnings for malformed root/workspace package.json (prevents silent failures when config is corrupt) - Tighten CreateDependencyWalkerOptions.packages to IntentPackage[] to make shared mutable state between registrar and walker explicit - Add coverage for local-over-global precedence in list --json and for stale scoping to local packages when globals are configured Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add changeset for discovery-engine Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Kyle Mathews <mathews.kyle@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 79519ab commit 36e696b

File tree

18 files changed

+566
-234
lines changed

18 files changed

+566
-234
lines changed

.changeset/discovery-engine.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/intent': patch
3+
---
4+
5+
Refactor package discovery into a dedicated registrar and dependency walker so project, workspace, and transitive dependencies are scanned consistently. Track local vs. global package sources and surface that in `intent list` via a `SOURCE` column. `intent stale` is now scoped to local and workspace packages; global packages are only included by `intent list` (which explicitly opts in). A new `scanForIntents` option `includeGlobal` controls global scanning for programmatic callers — this replaces the previous implicit behavior where setting `INTENT_GLOBAL_NODE_MODULES` caused all commands to scan globals.

docs/cli/intent-install.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ skills:
2424
<!-- intent-skills:end -->
2525
```
2626

27-
They also ask you to:
28-
29-
1. Check for an existing block first
30-
2. Run `intent list` to discover installed skills
31-
3. Ask whether you want a config target other than `AGENTS.md`
32-
4. Update an existing block in place when one already exists
33-
5. Add task-to-skill mappings
34-
6. Preserve all content outside the tagged block
27+
They also ask you to:
28+
29+
1. Check for an existing block first
30+
2. Run `intent list` to discover installed skills, including any packages surfaced by the command's explicit global scan
31+
3. Ask whether you want a config target other than `AGENTS.md`
32+
4. Update an existing block in place when one already exists
33+
5. Add task-to-skill mappings
34+
6. Preserve all content outside the tagged block
3535

3636
If no existing block is found, `AGENTS.md` is the default target.
3737

docs/cli/intent-list.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ npx @tanstack/intent@latest list [--json]
1515

1616
## What you get
1717

18-
- Scans installed dependencies for intent-enabled packages and skills
18+
- Scans project and workspace dependencies for intent-enabled packages and skills
19+
- Intentionally includes accessible global packages when listing installed skills
1920
- Includes warnings from discovery
2021
- If no packages are discovered, prints `No intent-enabled packages found.`
2122
- Summary line with package count, skill count, and detected package manager
22-
- Package table columns: `PACKAGE`, `VERSION`, `SKILLS`, `REQUIRES`
23+
- Package table columns: `PACKAGE`, `SOURCE`, `VERSION`, `SKILLS`, `REQUIRES`
2324
- Skill tree grouped by package
2425
- Optional warnings section (`⚠ ...` per warning)
2526

2627
`REQUIRES` uses `intent.requires` values joined by `, `; empty values render as ``.
28+
`SOURCE` is a lightweight indicator showing whether the selected package came from local discovery or explicit global scanning.
2729

2830
## JSON output
2931

@@ -36,6 +38,7 @@ npx @tanstack/intent@latest list [--json]
3638
{
3739
"name": "string",
3840
"version": "string",
41+
"source": "local | global",
3942
"intent": {
4043
"version": 1,
4144
"repo": "string",
@@ -57,7 +60,7 @@ npx @tanstack/intent@latest list [--json]
5760
}
5861
```
5962

60-
`packages` are ordered using `intent.requires` when possible.
63+
`packages` are ordered using `intent.requires` when possible. When the same package exists both locally and globally, `intent list` prefers the local package.
6164

6265
## Common errors
6366

docs/cli/intent-stale.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ npx @tanstack/intent@latest stale [--json]
1515

1616
## Behavior
1717

18-
- Scans installed intent-enabled packages
18+
- Checks the current package by default, or all skill-bearing packages in the current workspace when run from a monorepo root
19+
- When `dir` is provided, scopes the check to the targeted package or skills directory
1920
- Computes one staleness report per package
2021
- Prints text output by default or JSON with `--json`
2122
- If no packages are found, prints `No intent-enabled packages found.`

docs/getting-started/quick-start-maintainers.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ Manually check which skills need updates with:
162162
npx @tanstack/intent@latest stale
163163
```
164164

165+
When run from a package, this checks that package's shipped skills. When run from a monorepo root, it checks the workspace packages that ship skills.
166+
165167
This detects:
166168
- **Version drift** — skill targets an older library version than currently installed
167169
- **New sources** — sources declared in frontmatter that weren't tracked before

docs/overview.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ id: overview
55

66
`@tanstack/intent` is a CLI for shipping and consuming Agent Skills as package artifacts.
77

8-
Skills are markdown documents that teach AI coding agents how to use your library correctly. Intent versions them with your releases, ships them inside npm packages, discovers them from local and accessible global `node_modules`, and helps agents load them automatically when working on matching tasks.
8+
Skills are markdown documents that teach AI coding agents how to use your library correctly. Intent versions them with your releases, ships them inside npm packages, discovers them from your project and workspace by default, and helps agents load them automatically when working on matching tasks.
99

1010
## What Intent does
1111

@@ -30,7 +30,7 @@ Intent provides tooling for two workflows:
3030
npx @tanstack/intent@latest list
3131
```
3232

33-
Scans local `node_modules` and any accessible global `node_modules` for intent-enabled packages, preferring local packages when both exist.
33+
Scans the current project's `node_modules` and workspace dependencies for intent-enabled packages. The CLI intentionally includes accessible global packages for this command and still prefers local packages when both exist.
3434

3535
```bash
3636
npx @tanstack/intent@latest install

packages/intent/src/cli-support.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { dirname, join, relative, resolve } from 'node:path'
33
import { fileURLToPath } from 'node:url'
44
import { fail } from './cli-error.js'
55
import { resolveProjectContext } from './core/project-context.js'
6-
import type { ScanResult, StalenessReport } from './types.js'
6+
import type { ScanOptions, ScanResult, StalenessReport } from './types.js'
77

88
export function printWarnings(warnings: Array<string>): void {
99
if (warnings.length === 0) return
@@ -19,11 +19,13 @@ export function getMetaDir(): string {
1919
return join(thisDir, '..', 'meta')
2020
}
2121

22-
export async function scanIntentsOrFail(): Promise<ScanResult> {
22+
export async function scanIntentsOrFail(
23+
options?: ScanOptions,
24+
): Promise<ScanResult> {
2325
const { scanForIntents } = await import('./scanner.js')
2426

2527
try {
26-
return scanForIntents()
28+
return scanForIntents(undefined, options)
2729
} catch (err) {
2830
fail(err instanceof Error ? err.message : String(err))
2931
}

packages/intent/src/cli.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@ function createCli(): CAC {
2323
cli.usage('<command> [options]')
2424

2525
cli
26-
.command('list', 'Discover intent-enabled packages')
26+
.command(
27+
'list',
28+
'Discover intent-enabled packages from the project, workspace, and explicit global scan',
29+
)
2730
.usage('list [--json]')
2831
.option('--json', 'Output JSON')
2932
.example('list')
3033
.example('list --json')
3134
.action(async (options: { json?: boolean }) => {
32-
await runListCommand(options, scanIntentsOrFail)
35+
await runListCommand(options, () =>
36+
scanIntentsOrFail({ includeGlobal: true }),
37+
)
3338
})
3439

3540
cli
@@ -68,7 +73,10 @@ function createCli(): CAC {
6873
})
6974

7075
cli
71-
.command('stale [dir]', 'Check skills for staleness')
76+
.command(
77+
'stale [dir]',
78+
'Check skills for staleness in the current package or workspace',
79+
)
7280
.usage('stale [dir] [--json]')
7381
.option('--json', 'Output JSON')
7482
.example('stale')

packages/intent/src/commands/list.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,16 @@ export async function runListCommand(
3333
options: { json?: boolean },
3434
scanIntentsOrFail: () => Promise<ScanResult>,
3535
): Promise<void> {
36-
const { computeSkillNameWidth, printSkillTree, printTable } =
37-
await import('../display.js')
3836
const result = await scanIntentsOrFail()
3937

4038
if (options.json) {
4139
console.log(JSON.stringify(result, null, 2))
4240
return
4341
}
4442

43+
const { computeSkillNameWidth, printSkillTree, printTable } =
44+
await import('../display.js')
45+
4546
const scanCoverage = formatScanCoverage(result)
4647

4748
if (result.packages.length === 0) {
@@ -69,11 +70,12 @@ export async function runListCommand(
6970

7071
const rows = result.packages.map((pkg) => [
7172
pkg.name,
73+
pkg.source,
7274
pkg.version,
7375
String(pkg.skills.length),
7476
pkg.intent.requires?.join(', ') || '–',
7577
])
76-
printTable(['PACKAGE', 'VERSION', 'SKILLS', 'REQUIRES'], rows)
78+
printTable(['PACKAGE', 'SOURCE', 'VERSION', 'SKILLS', 'REQUIRES'], rows)
7779

7880
printVersionConflicts(result)
7981

packages/intent/src/commands/stale.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ export async function runStaleCommand(
99
): Promise<void> {
1010
const { reports } = await resolveStaleTargets(targetDir)
1111

12-
if (reports.length === 0) {
13-
console.log('No intent-enabled packages found.')
12+
if (options.json) {
13+
console.log(JSON.stringify(reports, null, 2))
1414
return
1515
}
1616

17-
if (options.json) {
18-
console.log(JSON.stringify(reports, null, 2))
17+
if (reports.length === 0) {
18+
console.log('No intent-enabled packages found.')
1919
return
2020
}
2121

0 commit comments

Comments
 (0)