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/refresh-workspace-discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@tanstack/intent': patch
---

Refresh workspace roots, patterns, and members between core operations. Keep workspace discovery reuse within the existing operation-local filesystem cache so listing and loading observe changed membership and source kinds.

Avoid enumerating unrelated workspace members and reading unused skill metadata during direct loads. Preserve fresh policy reads and final path checks.
4 changes: 2 additions & 2 deletions packages/intent/src/core/intent-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function listIntentSkills(
const cwd = resolveCoreCwd(options)
const scanOptions = toScanOptions(options)
const fsCache = createIntentFsCache()
const projectContext = resolveProjectContext({ cwd })
const projectContext = resolveProjectContext({ cwd, fsCache })
const { hiddenSourceCount, hiddenSources, scan, excludePatterns } =
scanForPolicedIntents({
cwd,
Expand Down Expand Up @@ -283,7 +283,7 @@ function resolveIntentSkillInCwd(
}

const fsCache = createIntentFsCache()
const projectContext = resolveProjectContext({ cwd })
const projectContext = resolveProjectContext({ cwd, fsCache })
const excludePatterns = getEffectiveExcludePatterns(
options,
projectContext,
Expand Down
3 changes: 2 additions & 1 deletion packages/intent/src/core/load-resolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function readWorkspacePackageInfos(
if (context.workspaceRoot) {
dirs.add(context.workspaceRoot)

for (const dir of findWorkspacePackages(context.workspaceRoot)) {
for (const dir of findWorkspacePackages(context.workspaceRoot, fsCache)) {
dirs.add(dir)
}
}
Expand Down Expand Up @@ -224,6 +224,7 @@ function resolveFromPackageRoots(
const scanned = scanIntentPackageAtRoot(packageRoot, {
fallbackName: parsedUse.packageName,
fsCache,
includeSkillMetadata: false,
projectRoot: cwd,
skillNameHint: parsedUse.skillName,
})
Expand Down
11 changes: 9 additions & 2 deletions packages/intent/src/core/project-context.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { existsSync, statSync } from 'node:fs'
import { dirname, join, relative, resolve } from 'node:path'
import { createIntentFsCache } from '../discovery/fs-cache.js'
import {
findWorkspaceRoot,
readWorkspacePatterns,
} from '../setup/workspace-patterns.js'
import type { IntentFsCache } from '../discovery/fs-cache.js'

export type ProjectContext = {
cwd: string
Expand All @@ -23,18 +25,23 @@ export type ProjectContext = {
export function resolveProjectContext({
cwd,
targetPath,
fsCache = createIntentFsCache(),
}: {
cwd: string
targetPath?: string
fsCache?: IntentFsCache
}): ProjectContext {
const resolvedCwd = resolve(cwd)
const resolvedTargetPath = targetPath
? resolve(resolvedCwd, targetPath)
: resolvedCwd
const packageRoot = findOwningPackageRoot(resolvedTargetPath)
const workspaceRoot = findWorkspaceRoot(packageRoot ?? resolvedTargetPath)
const workspaceRoot = findWorkspaceRoot(
packageRoot ?? resolvedTargetPath,
fsCache,
)
const workspacePatterns = workspaceRoot
? (readWorkspacePatterns(workspaceRoot) ?? [])
? (readWorkspacePatterns(workspaceRoot, fsCache) ?? [])
: []

return {
Expand Down
51 changes: 47 additions & 4 deletions packages/intent/src/discovery/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import {
findWorkspacePackages,
findWorkspaceRoot,
readWorkspacePatterns,
} from '../setup/workspace-patterns.js'
import { createIntentFsCache } from './fs-cache.js'
import { detectPackageManager } from './package-manager.js'
Expand Down Expand Up @@ -325,6 +326,7 @@ function discoverSkillByNameHint(
packageName: string,
skillNameHint: string,
readFs: ReadFs = nodeReadFs,
includeMetadata = true,
): Array<SkillEntry> {
const skills: Array<SkillEntry> = []
const seen = new Set<string>()
Expand All @@ -339,7 +341,9 @@ function discoverSkillByNameHint(

// Keep the hinted identity so loading can report its existing path error,
// without reading metadata from an unreadable or escaping target.
const skill = readSkillEntry(skillsDir, childDir, skillFile, readFs) ?? {
const skill = (includeMetadata
? readSkillEntry(skillsDir, childDir, skillFile, readFs)
: null) ?? {
name: hint,
path: skillFile,
description: '',
Expand Down Expand Up @@ -517,11 +521,44 @@ function getScanScope(options: ScanOptions): ScanScope {
function createWorkspacePackageKeySet(
workspaceRoot: string | null,
fsCache: IntentFsCache,
candidateRoot?: string,
): Set<string> {
if (!workspaceRoot) return new Set()

if (candidateRoot) {
const patterns = readWorkspacePatterns(workspaceRoot, fsCache) ?? []
const couldMatch = patterns.some((pattern) => {
if (pattern.startsWith('!')) return false
const segments = pattern.split('/')
const wildcard = segments.findIndex(
(segment) => segment === '*' || segment === '**',
)
if (
wildcard < 0 ||
segments
.slice(wildcard)
.some((segment) => !['*', '**'].includes(segment))
)
return true
try {
const readFs = fsCache.getReadFs()
const prefix = readFs.realpathSync(
join(workspaceRoot, ...segments.slice(0, wildcard)),
)
const candidate = readFs.realpathSync(candidateRoot)
const path = relative(prefix, candidate)
return (
path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path)
)
} catch {
return true
}
})
if (!couldMatch) return new Set()
}

const packagesByParent = new Map<string, Array<string>>()
for (const dir of findWorkspacePackages(workspaceRoot)) {
for (const dir of findWorkspacePackages(workspaceRoot, fsCache)) {
const parent = dirname(dir)
const dirs = packagesByParent.get(parent)
if (dirs) dirs.push(dir)
Expand Down Expand Up @@ -576,7 +613,7 @@ export function scanForIntents(
const scanScope = getScanScope(options)
const fsCache =
(options as ScanOptionsWithFsCache).fsCache ?? createIntentFsCache()
const workspaceRoot = findWorkspaceRoot(projectRoot)
const workspaceRoot = findWorkspaceRoot(projectRoot, fsCache)
const packageManager = detectPackageManager(
projectRoot,
[workspaceRoot],
Expand Down Expand Up @@ -835,6 +872,7 @@ export function scanForIntents(
export interface ScanIntentPackageAtRootOptions {
fallbackName?: string
fsCache?: IntentFsCache
includeSkillMetadata?: boolean
projectRoot?: string
source?: IntentPackage['source']
skillNameHint?: string
Expand All @@ -855,7 +893,11 @@ export function scanIntentPackageAtRoot(
const packageIndexes = new Map<string, number>()
const fsCache = options.fsCache ?? createIntentFsCache()
const getPackageKind = createPackageKindResolver(
createWorkspacePackageKeySet(findWorkspaceRoot(projectRoot), fsCache),
createWorkspacePackageKeySet(
findWorkspaceRoot(projectRoot, fsCache),
fsCache,
packageRoot,
),
fsCache.getFsIdentity,
)

Expand All @@ -873,6 +915,7 @@ export function scanIntentPackageAtRoot(
packageName,
options.skillNameHint!,
fsCache.getReadFs(),
options.includeSkillMetadata !== false,
)
: (skillsDir, packageName) =>
discoverSkills(skillsDir, packageName, fsCache, warnings),
Expand Down
2 changes: 1 addition & 1 deletion packages/intent/src/discovery/walk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function createDependencyWalker(opts: CreateDependencyWalkerOptions) {
}

function walkWorkspacePackages(): void {
for (const wsDir of findWorkspacePackages(opts.projectRoot)) {
for (const wsDir of findWorkspacePackages(opts.projectRoot, opts.fsCache)) {
opts.scanNodeModulesDir(join(wsDir, 'node_modules'))

const wsPkg = readPkgJsonWithWarning(wsDir, 'workspace')
Expand Down
Loading
Loading