From 80f0e5fb4598b6861c60147df17ab3ea6592d779 Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Thu, 10 Sep 2026 22:11:48 -0700 Subject: [PATCH] fix: recognize workspace-root Intent devDependency --- .changeset/workspace-root-intent-warning.md | 5 ++ packages/intent/src/commands/validate.ts | 17 ++++++- packages/intent/tests/cli.test.ts | 56 +++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 .changeset/workspace-root-intent-warning.md diff --git a/.changeset/workspace-root-intent-warning.md b/.changeset/workspace-root-intent-warning.md new file mode 100644 index 00000000..6d5e52cf --- /dev/null +++ b/.changeset/workspace-root-intent-warning.md @@ -0,0 +1,5 @@ +--- +'@tanstack/intent': patch +--- + +Recognize Intent installed as a devDependency at the owning workspace root when validating package skills. diff --git a/packages/intent/src/commands/validate.ts b/packages/intent/src/commands/validate.ts index 71c6344e..fe451a19 100644 --- a/packages/intent/src/commands/validate.ts +++ b/packages/intent/src/commands/validate.ts @@ -98,8 +98,24 @@ function collectPackagingWarnings(context: ProjectContext): Array { if (!existsSync(pkgJsonPath)) return [] let pkgJson: Record + let devDeps: Record | undefined try { pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8')) + devDeps = pkgJson.devDependencies as Record | undefined + if ( + !devDeps?.['@tanstack/intent'] && + context.workspaceRoot && + context.workspaceRoot !== context.packageRoot + ) { + const workspaceManifestPath = join(context.workspaceRoot, 'package.json') + if (existsSync(workspaceManifestPath)) { + const workspaceManifest = JSON.parse( + readFileSync(workspaceManifestPath, 'utf8'), + ) as Record + devDeps = workspaceManifest.devDependencies as + Record | undefined + } + } } catch (err) { const msg = err instanceof Error ? err.message : String(err) return [`Could not parse package.json: ${msg}`] @@ -107,7 +123,6 @@ function collectPackagingWarnings(context: ProjectContext): Array { const warnings: Array = [] - const devDeps = pkgJson.devDependencies as Record | undefined if (!devDeps?.['@tanstack/intent']) { warnings.push('@tanstack/intent is not in devDependencies') } diff --git a/packages/intent/tests/cli.test.ts b/packages/intent/tests/cli.test.ts index 1d77ac6e..6588258b 100644 --- a/packages/intent/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -3197,6 +3197,62 @@ describe('cli commands', () => { expect(output).toContain('Framework skills must have a "requires" field') }) + it.each(['package.json', 'pnpm-workspace.yaml'])( + 'recognizes Intent at the workspace root declared by %s', + async (workspaceFile) => { + const root = mkdtempSync( + join(realTmpdir, 'intent-cli-validate-root-dep-'), + ) + tempDirs.push(root) + const manifest = { + private: true, + ...(workspaceFile === 'package.json' + ? { workspaces: ['packages/*'] } + : {}), + devDependencies: { '@tanstack/intent': '^0.4.0' }, + } + writeJson(join(root, 'package.json'), manifest) + if (workspaceFile === 'pnpm-workspace.yaml') + writeFileSync( + join(root, 'pnpm-workspace.yaml'), + 'packages:\n - packages/*\n', + ) + const packageDir = join(root, 'packages', 'client') + writeJson(join(packageDir, 'package.json'), { + name: 'client', + keywords: ['tanstack-intent'], + files: ['skills'], + }) + writeSkillMd(join(packageDir, 'skills', 'query'), { + name: 'query', + description: 'Query the client.', + }) + + for (const cwd of [root, packageDir]) { + process.chdir(cwd) + logSpy.mockClear() + expect(await main(['validate'])).toBe(0) + expect(logSpy.mock.calls.flat().join('\n')).not.toContain( + '@tanstack/intent is not in devDependencies', + ) + } + + writeJson(join(root, 'package.json'), { + ...manifest, + devDependencies: {}, + }) + writeJson(join(root, 'packages', 'tooling', 'package.json'), { + name: 'tooling', + devDependencies: { '@tanstack/intent': '^0.4.0' }, + }) + logSpy.mockClear() + expect(await main(['validate'])).toBe(0) + expect(logSpy.mock.calls.flat().join('\n')).toContain( + '@tanstack/intent is not in devDependencies', + ) + }, + ) + it('validates package skills from repo root without root packaging warnings', async () => { const root = mkdtempSync(join(realTmpdir, 'intent-cli-validate-mono-')) tempDirs.push(root)