Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions packages/intent/src/commands/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ function printListDebug(result: IntentSkillList): void {
['skills', result.debug.skillCount],
['warnings', result.debug.warningCount],
['conflicts', result.debug.conflictCount],
['packageJsonReadCount', result.debug.scan.packageJsonReadCount],
['packageJsonCacheHits', result.debug.scan.packageJsonCacheHits],
])
}

Expand Down
2 changes: 2 additions & 0 deletions packages/intent/src/commands/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ function printLoadDebug(loaded: LoadedIntentSkill | ResolvedIntentSkill): void {
['skill', loaded.debug.skillName],
['path', loaded.debug.path],
['warnings', loaded.debug.warningCount],
['packageJsonReadCount', loaded.debug.scan.packageJsonReadCount],
['packageJsonCacheHits', loaded.debug.scan.packageJsonCacheHits],
])
}

Expand Down
21 changes: 19 additions & 2 deletions packages/intent/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
isPackageExcluded,
warningMentionsPackage,
} from './core/excludes.js'
import { createIntentFsCache, type IntentFsCache } from './fs-cache.js'
import { rewriteLoadedSkillMarkdownDestinations } from './core/markdown.js'
import { resolveSkillUseFastPath } from './core/load-resolution.js'
import { resolveProjectContext } from './core/project-context.js'
Expand Down Expand Up @@ -80,6 +81,13 @@ function getScanScope(options: ScanOptions): ScanScope {
return options.scope ?? (options.includeGlobal ? 'local-and-global' : 'local')
}

function withFsCache(
options: ScanOptions,
fsCache: IntentFsCache,
): ScanOptions & { fsCache: IntentFsCache } {
return { ...options, fsCache }
}

function resolveCoreCwd(options: IntentCoreOptions): string {
return resolve(process.cwd(), options.cwd ?? process.cwd())
}
Expand All @@ -89,8 +97,9 @@ export function listIntentSkills(
): IntentSkillList {
const cwd = resolveCoreCwd(options)
const scanOptions = toScanOptions(options)
const fsCache = createIntentFsCache()
const projectContext = resolveProjectContext({ cwd })
const scanResult = scanForIntents(cwd, scanOptions)
const scanResult = scanForIntents(cwd, withFsCache(scanOptions, fsCache))
const excludePatterns = getEffectiveExcludePatterns(options, projectContext)
const excludeMatchers = compileExcludePatterns(excludePatterns)
const excludedPackages = scanResult.packages
Expand Down Expand Up @@ -144,6 +153,7 @@ export function listIntentSkills(
skillCount: result.skills.length,
warningCount: result.warnings.length,
conflictCount: result.conflicts.length,
scan: scanResult.stats ?? fsCache.getStats(),
}
}

Expand Down Expand Up @@ -220,12 +230,14 @@ function toResolvedIntentSkill(
function createLoadedSkillDebug({
cwd,
excludes,
scan,
resolution,
resolved,
scope,
}: {
cwd: string
excludes: Array<string>
scan: LoadedIntentSkillDebug['scan']
resolution: LoadedIntentSkillDebug['resolution']
resolved: ResolveSkillResult
scope: ScanScope
Expand All @@ -241,6 +253,7 @@ function createLoadedSkillDebug({
source: resolved.source,
path: resolved.path,
warningCount: resolved.warnings.length,
scan,
}
}

Expand All @@ -263,6 +276,7 @@ function resolveIntentSkillInCwd(
)
}

const fsCache = createIntentFsCache()
const projectContext = resolveProjectContext({ cwd })
const excludePatterns = getEffectiveExcludePatterns(options, projectContext)
const excludeMatchers = compileExcludePatterns(excludePatterns)
Expand All @@ -281,6 +295,7 @@ function resolveIntentSkillInCwd(
options,
projectContext,
cwd,
fsCache,
)
if (fastPathResolved) {
return toResolvedIntentSkill(
Expand All @@ -293,13 +308,14 @@ function resolveIntentSkillInCwd(
excludes: excludePatterns,
resolution: 'fast-path',
resolved: fastPathResolved,
scan: fsCache.getStats(),
scope,
})
: undefined,
)
}

const scanResult = scanForIntents(cwd, scanOptions)
const scanResult = scanForIntents(cwd, withFsCache(scanOptions, fsCache))
let resolved: ReturnType<typeof resolveSkillUse>
try {
resolved = resolveSkillUse(use, scanResult)
Expand All @@ -322,6 +338,7 @@ function resolveIntentSkillInCwd(
excludes: excludePatterns,
resolution: 'full-scan',
resolved,
scan: scanResult.stats ?? fsCache.getStats(),
scope,
})
: undefined,
Expand Down
27 changes: 18 additions & 9 deletions packages/intent/src/core/load-resolution.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { existsSync } from 'node:fs'
import { dirname, join, resolve } from 'node:path'
import { createIntentFsCache, type IntentFsCache } from '../fs-cache.js'
import { resolveSkillEntry, type ResolveSkillResult } from '../resolver.js'
import { scanIntentPackageAtRoot } from '../scanner.js'
import { resolveWorkspacePackages } from '../workspace-patterns.js'
import { findWorkspacePackages } from '../workspace-patterns.js'
import { getDeps, resolveDepDir } from '../utils.js'
import { warningMentionsPackage } from './excludes.js'
import { readPackageJson } from './package-json.js'
import {
resolveProjectContext,
type ProjectContext,
Expand All @@ -21,6 +21,7 @@ interface WorkspacePackageInfo {

function readWorkspacePackageInfos(
context: ProjectContext,
fsCache: IntentFsCache,
): Array<WorkspacePackageInfo> {
const dirs = new Set<string>()

Expand All @@ -31,16 +32,13 @@ function readWorkspacePackageInfos(
if (context.workspaceRoot) {
dirs.add(context.workspaceRoot)

for (const dir of resolveWorkspacePackages(
context.workspaceRoot,
context.workspacePatterns,
)) {
for (const dir of findWorkspacePackages(context.workspaceRoot)) {
dirs.add(dir)
}
}

return [...dirs].flatMap((dir) => {
const packageJson = readPackageJson(dir)
const packageJson = fsCache.readPackageJson(dir)
if (!packageJson) return []

return [
Expand Down Expand Up @@ -154,10 +152,11 @@ function getDirectLoadFastPathCandidateDirs(
function getWorkspaceLoadFastPathCandidateDirs(
packageName: string,
context: ProjectContext,
fsCache: IntentFsCache,
): Array<string> {
const candidates: Array<string> = []
const seen = new Set<string>()
const workspacePackages = readWorkspacePackageInfos(context)
const workspacePackages = readWorkspacePackageInfos(context, fsCache)

for (const pkg of workspacePackages) {
if (pkg.name === packageName) {
Expand Down Expand Up @@ -212,10 +211,12 @@ function resolveFromPackageRoots(
packageRoots: Array<string>,
parsedUse: SkillUse,
cwd: string,
fsCache: IntentFsCache,
): ResolveSkillResult | null {
for (const packageRoot of packageRoots) {
const scanned = scanIntentPackageAtRoot(packageRoot, {
fallbackName: parsedUse.packageName,
fsCache,
projectRoot: cwd,
skillNameHint: parsedUse.skillName,
})
Expand All @@ -225,6 +226,7 @@ function resolveFromPackageRoots(
if (scanned.package?.name === parsedUse.packageName) {
const fallbackScanned = scanIntentPackageAtRoot(packageRoot, {
fallbackName: parsedUse.packageName,
fsCache,
projectRoot: cwd,
})
const fallbackResolved = resolveScannedPackageSkill(
Expand All @@ -243,6 +245,7 @@ export function resolveSkillUseFastPath(
options: IntentCoreOptions,
context = resolveProjectContext({ cwd: process.cwd() }),
cwd = context.cwd,
fsCache = createIntentFsCache(),
): ResolveSkillResult | null {
if (options.globalOnly) return null
if (shouldSkipFastPathForYarnPnp(context, cwd)) return null
Expand All @@ -251,6 +254,7 @@ export function resolveSkillUseFastPath(
getDirectLoadFastPathCandidateDirs(parsedUse.packageName, context, cwd),
parsedUse,
cwd,
fsCache,
)
if (directResolved) return directResolved

Expand All @@ -259,8 +263,13 @@ export function resolveSkillUseFastPath(
}

return resolveFromPackageRoots(
getWorkspaceLoadFastPathCandidateDirs(parsedUse.packageName, context),
getWorkspaceLoadFastPathCandidateDirs(
parsedUse.packageName,
context,
fsCache,
),
parsedUse,
cwd,
fsCache,
)
}
11 changes: 10 additions & 1 deletion packages/intent/src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { IntentPackage, ScanScope, VersionConflict } from '../types.js'
import type {
IntentPackage,
ScanScope,
ScanStats,
VersionConflict,
} from '../types.js'

export interface IntentCoreOptions {
cwd?: string
Expand Down Expand Up @@ -60,6 +65,7 @@ export interface IntentSkillListDebug {
skillCount: number
warningCount: number
conflictCount: number
scan: IntentScanDebugStats
}

export interface LoadedIntentSkillDebug {
Expand All @@ -73,8 +79,11 @@ export interface LoadedIntentSkillDebug {
source: IntentPackage['source']
path: string
warningCount: number
scan: IntentScanDebugStats
}

export interface IntentScanDebugStats extends ScanStats {}

export type IntentCoreErrorCode =
| 'invalid-options'
| 'invalid-skill-use'
Expand Down
34 changes: 15 additions & 19 deletions packages/intent/src/discovery/walk.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { existsSync, readFileSync } from 'node:fs'
import { existsSync } from 'node:fs'
import { join } from 'node:path'
import { listNodeModulesPackageDirs, resolveDepDir, getDeps } from '../utils.js'
import {
readWorkspacePatterns,
resolveWorkspacePackages,
} from '../workspace-patterns.js'
import { findWorkspacePackages } from '../workspace-patterns.js'
import type { IntentFsCache } from '../fs-cache.js'
import type { IntentPackage } from '../types.js'

type PackageJson = Record<string, unknown>

export interface CreateDependencyWalkerOptions {
fsCache: IntentFsCache
projectRoot: string
readPkgJson: (dirPath: string) => PackageJson | null
tryRegister: (dirPath: string, fallbackName: string) => boolean
Expand Down Expand Up @@ -83,29 +82,26 @@ export function createDependencyWalker(opts: CreateDependencyWalkerOptions) {
dirPath: string,
label: string,
): PackageJson | null {
try {
return JSON.parse(
readFileSync(join(dirPath, 'package.json'), 'utf8'),
) as PackageJson
} catch (err) {
const code = (err as NodeJS.ErrnoException).code
const result = opts.fsCache.readPackageJsonResult(dirPath)
if (!result.packageJson) {
const code = (result.error as NodeJS.ErrnoException | null)?.code
if (code !== 'ENOENT') {
opts.warnings.push(
`Could not read ${label} package.json at ${dirPath}: ${(err as Error).message}`,
`Could not read ${label} package.json at ${dirPath}: ${
result.error instanceof Error
? result.error.message
: 'invalid package.json'
}`,
)
}
return null
}

return result.packageJson
}

function walkWorkspacePackages(): void {
const workspacePatterns = readWorkspacePatterns(opts.projectRoot)
if (!workspacePatterns) return

for (const wsDir of resolveWorkspacePackages(
opts.projectRoot,
workspacePatterns,
)) {
for (const wsDir of findWorkspacePackages(opts.projectRoot)) {
const wsNodeModules = join(wsDir, 'node_modules')
if (existsSync(wsNodeModules)) {
for (const dirPath of listNodeModulesPackageDirs(wsNodeModules)) {
Expand Down
Loading
Loading