From 4e2057d86a7741c9060e254ff968225db33fc6a7 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard <98355961+LadyBluenotes@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:10:05 -0700 Subject: [PATCH 1/4] fix: resolve stale targets from workspace package context --- packages/intent/src/cli-support.ts | 24 +++++- packages/intent/tests/cli.test.ts | 123 +++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/packages/intent/src/cli-support.ts b/packages/intent/src/cli-support.ts index 84db99e..962a8cf 100644 --- a/packages/intent/src/cli-support.ts +++ b/packages/intent/src/cli-support.ts @@ -1,7 +1,8 @@ import { existsSync, readFileSync } from 'node:fs' -import { dirname, join, relative } from 'node:path' +import { dirname, join, relative, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { fail } from './cli-error.js' +import { resolveProjectContext } from './core/project-context.js' import type { ScanResult, StalenessReport } from './types.js' export function printWarnings(warnings: Array): void { @@ -47,10 +48,29 @@ export async function resolveStaleTargets( targetDir?: string, ): Promise<{ reports: Array }> { const resolvedRoot = targetDir - ? join(process.cwd(), targetDir) + ? resolve(process.cwd(), targetDir) : process.cwd() + const context = resolveProjectContext({ + cwd: process.cwd(), + targetPath: targetDir, + }) const { checkStaleness } = await import('./staleness.js') + const targetsResolvedPackage = + context.packageRoot !== null && + (context.targetSkillsDir !== null || resolvedRoot !== context.workspaceRoot) + + if (targetsResolvedPackage && context.packageRoot) { + return { + reports: [ + await checkStaleness( + context.packageRoot, + readPackageName(context.packageRoot), + ), + ], + } + } + if (existsSync(join(resolvedRoot, 'skills'))) { return { reports: [ diff --git a/packages/intent/tests/cli.test.ts b/packages/intent/tests/cli.test.ts index 1f2e2e8..6f130cc 100644 --- a/packages/intent/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -229,6 +229,43 @@ describe('cli commands', () => { expect(output).toContain('Template variables applied:') }) + it('copies github workflow templates to the workspace root', async () => { + const root = mkdtempSync(join(realTmpdir, 'intent-cli-setup-gha-mono-')) + tempDirs.push(root) + + writeJson(join(root, 'package.json'), { + private: true, + workspaces: ['packages/*'], + }) + writeJson(join(root, 'packages', 'router', 'package.json'), { + name: '@tanstack/router', + version: '1.0.0', + intent: { version: 1, repo: 'TanStack/router', docs: 'docs/' }, + }) + writeSkillMd(join(root, 'packages', 'router', 'skills', 'routing'), { + name: 'routing', + description: 'Routing skill', + }) + + process.chdir(join(root, 'packages', 'router')) + + const exitCode = await main(['setup-github-actions']) + const rootWorkflowsDir = join(root, '.github', 'workflows') + const packageWorkflowsDir = join( + root, + 'packages', + 'router', + '.github', + 'workflows', + ) + const output = logSpy.mock.calls.flat().join('\n') + + expect(exitCode).toBe(0) + expect(existsSync(rootWorkflowsDir)).toBe(true) + expect(existsSync(packageWorkflowsDir)).toBe(false) + expect(output).toContain('Mode: monorepo') + }) + it('lists installed intent packages as json', async () => { const root = mkdtempSync(join(realTmpdir, 'intent-cli-list-')) tempDirs.push(root) @@ -484,6 +521,92 @@ describe('cli commands', () => { fetchSpy.mockRestore() }) + + it('checks only the targeted workspace package for staleness', async () => { + const root = mkdtempSync(join(realTmpdir, 'intent-cli-stale-target-')) + tempDirs.push(root) + + writeJson(join(root, 'package.json'), { + private: true, + workspaces: ['packages/*'], + }) + writeJson(join(root, 'packages', 'router', 'package.json'), { + name: '@tanstack/router', + }) + writeJson(join(root, 'packages', 'query', 'package.json'), { + name: '@tanstack/query', + }) + writeSkillMd(join(root, 'packages', 'router', 'skills', 'routing'), { + name: 'routing', + description: 'Routing skill', + library_version: '1.0.0', + }) + writeSkillMd(join(root, 'packages', 'query', 'skills', 'cache'), { + name: 'cache', + description: 'Caching skill', + library_version: '1.0.0', + }) + + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ + ok: true, + json: async () => ({ version: '1.0.0' }), + } as Response) + + process.chdir(root) + + const exitCode = await main(['stale', 'packages/router/skills', '--json']) + const output = logSpy.mock.calls.at(-1)?.[0] + const reports = JSON.parse(String(output)) as Array<{ library: string }> + + expect(exitCode).toBe(0) + expect(reports).toHaveLength(1) + expect(reports[0]!.library).toBe('@tanstack/router') + + fetchSpy.mockRestore() + }) + + it('checks the current workspace package for staleness from package cwd', async () => { + const root = mkdtempSync(join(realTmpdir, 'intent-cli-stale-package-cwd-')) + tempDirs.push(root) + + writeJson(join(root, 'package.json'), { + private: true, + workspaces: ['packages/*'], + }) + writeJson(join(root, 'packages', 'router', 'package.json'), { + name: '@tanstack/router', + }) + writeJson(join(root, 'packages', 'query', 'package.json'), { + name: '@tanstack/query', + }) + writeSkillMd(join(root, 'packages', 'router', 'skills', 'routing'), { + name: 'routing', + description: 'Routing skill', + library_version: '1.0.0', + }) + writeSkillMd(join(root, 'packages', 'query', 'skills', 'cache'), { + name: 'cache', + description: 'Caching skill', + library_version: '1.0.0', + }) + + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ + ok: true, + json: async () => ({ version: '1.0.0' }), + } as Response) + + process.chdir(join(root, 'packages', 'router')) + + const exitCode = await main(['stale', '--json']) + const output = logSpy.mock.calls.at(-1)?.[0] + const reports = JSON.parse(String(output)) as Array<{ library: string }> + + expect(exitCode).toBe(0) + expect(reports).toHaveLength(1) + expect(reports[0]!.library).toBe('@tanstack/router') + + fetchSpy.mockRestore() + }) }) describe('package metadata', () => { From b49d1cec536eb7dbe15d9e92df4e9811a58de4de Mon Sep 17 00:00:00 2001 From: LadyBluenotes Date: Mon, 30 Mar 2026 10:25:30 -0700 Subject: [PATCH 2/4] changeset --- .changeset/vast-bags-switch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/vast-bags-switch.md diff --git a/.changeset/vast-bags-switch.md b/.changeset/vast-bags-switch.md new file mode 100644 index 0000000..67d10df --- /dev/null +++ b/.changeset/vast-bags-switch.md @@ -0,0 +1,5 @@ +--- +'@tanstack/intent': patch +--- + +Fix intent stale so monorepo package paths resolve to the targeted workspace package instead of scanning the whole workspace. From 522d28aca0fd7f424ae69856f8e991787d445328 Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Tue, 31 Mar 2026 17:00:54 -0600 Subject: [PATCH 3/4] Simplify condition, strengthen test assertions, add coverage for edge cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Inline targetsResolvedPackage boolean to remove redundant null check - Add fetchSpy call count assertions to verify only targeted package is processed - Add test for targetDir without /skills suffix - Add test for absolute targetDir path (validates join→resolve fix) Co-Authored-By: Claude Opus 4.6 --- packages/intent/src/cli-support.ts | 7 ++- packages/intent/tests/cli.test.ts | 81 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/packages/intent/src/cli-support.ts b/packages/intent/src/cli-support.ts index 962a8cf..8bb0e3b 100644 --- a/packages/intent/src/cli-support.ts +++ b/packages/intent/src/cli-support.ts @@ -56,11 +56,10 @@ export async function resolveStaleTargets( }) const { checkStaleness } = await import('./staleness.js') - const targetsResolvedPackage = - context.packageRoot !== null && + if ( + context.packageRoot && (context.targetSkillsDir !== null || resolvedRoot !== context.workspaceRoot) - - if (targetsResolvedPackage && context.packageRoot) { + ) { return { reports: [ await checkStaleness( diff --git a/packages/intent/tests/cli.test.ts b/packages/intent/tests/cli.test.ts index 6f130cc..2b65a41 100644 --- a/packages/intent/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -561,6 +561,51 @@ describe('cli commands', () => { expect(exitCode).toBe(0) expect(reports).toHaveLength(1) expect(reports[0]!.library).toBe('@tanstack/router') + expect(fetchSpy).toHaveBeenCalledTimes(1) + + fetchSpy.mockRestore() + }) + + it('checks only the targeted workspace package when path omits /skills suffix', async () => { + const root = mkdtempSync(join(realTmpdir, 'intent-cli-stale-target-nosuffix-')) + tempDirs.push(root) + + writeJson(join(root, 'package.json'), { + private: true, + workspaces: ['packages/*'], + }) + writeJson(join(root, 'packages', 'router', 'package.json'), { + name: '@tanstack/router', + }) + writeJson(join(root, 'packages', 'query', 'package.json'), { + name: '@tanstack/query', + }) + writeSkillMd(join(root, 'packages', 'router', 'skills', 'routing'), { + name: 'routing', + description: 'Routing skill', + library_version: '1.0.0', + }) + writeSkillMd(join(root, 'packages', 'query', 'skills', 'cache'), { + name: 'cache', + description: 'Caching skill', + library_version: '1.0.0', + }) + + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ + ok: true, + json: async () => ({ version: '1.0.0' }), + } as Response) + + process.chdir(root) + + const exitCode = await main(['stale', 'packages/router', '--json']) + const output = logSpy.mock.calls.at(-1)?.[0] + const reports = JSON.parse(String(output)) as Array<{ library: string }> + + expect(exitCode).toBe(0) + expect(reports).toHaveLength(1) + expect(reports[0]!.library).toBe('@tanstack/router') + expect(fetchSpy).toHaveBeenCalledTimes(1) fetchSpy.mockRestore() }) @@ -601,12 +646,48 @@ describe('cli commands', () => { const output = logSpy.mock.calls.at(-1)?.[0] const reports = JSON.parse(String(output)) as Array<{ library: string }> + expect(exitCode).toBe(0) + expect(reports).toHaveLength(1) + expect(reports[0]!.library).toBe('@tanstack/router') + expect(fetchSpy).toHaveBeenCalledTimes(1) + + fetchSpy.mockRestore() + }) + + it('handles absolute targetDir path correctly', async () => { + const root = mkdtempSync(join(realTmpdir, 'intent-cli-stale-abs-')) + tempDirs.push(root) + + writeJson(join(root, 'package.json'), { + name: '@tanstack/router', + version: '1.0.0', + }) + writeSkillMd(join(root, 'skills', 'routing'), { + name: 'routing', + description: 'Routing skill', + library_version: '1.0.0', + }) + + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ + ok: true, + json: async () => ({ version: '1.0.0' }), + } as Response) + + const elsewhere = mkdtempSync(join(realTmpdir, 'intent-cli-stale-abs-cwd-')) + tempDirs.push(elsewhere) + process.chdir(elsewhere) + + const exitCode = await main(['stale', root, '--json']) + const output = logSpy.mock.calls.at(-1)?.[0] + const reports = JSON.parse(String(output)) as Array<{ library: string }> + expect(exitCode).toBe(0) expect(reports).toHaveLength(1) expect(reports[0]!.library).toBe('@tanstack/router') fetchSpy.mockRestore() }) + }) describe('package metadata', () => { From 2cb672dae23faac5579b7a05b528bad3aab082b4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:01:37 +0000 Subject: [PATCH 4/4] ci: apply automated fixes --- packages/intent/tests/cli.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/intent/tests/cli.test.ts b/packages/intent/tests/cli.test.ts index 2b65a41..28217d0 100644 --- a/packages/intent/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -567,7 +567,9 @@ describe('cli commands', () => { }) it('checks only the targeted workspace package when path omits /skills suffix', async () => { - const root = mkdtempSync(join(realTmpdir, 'intent-cli-stale-target-nosuffix-')) + const root = mkdtempSync( + join(realTmpdir, 'intent-cli-stale-target-nosuffix-'), + ) tempDirs.push(root) writeJson(join(root, 'package.json'), { @@ -687,7 +689,6 @@ describe('cli commands', () => { fetchSpy.mockRestore() }) - }) describe('package metadata', () => {