Skip to content

Commit 66937b2

Browse files
KyleAMathewsclaude
andauthored
fix: resolve symlink paths in CLI isMain guard for npx compatibility (#75)
The isMain entry-point guard compared import.meta.url (which resolves symlinks) against pathToFileURL(process.argv[1]) (which preserves symlinks). When running via npx or on macOS (/tmp → /private/tmp), these never matched, so main() was silently never called. Fix by comparing resolved filesystem paths using realpathSync, wrapped in a try-catch for defensive safety. Also fix 3 pre-existing test failures caused by the same macOS /var → /private/var divergence. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6c4e503 commit 66937b2

3 files changed

Lines changed: 23 additions & 13 deletions

File tree

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+
Fix CLI silently doing nothing when run via `npx` due to symlink path mismatch in the `isMain` entry-point guard. Also fix 3 pre-existing test failures on macOS caused by `/var``/private/var` symlink divergence.

packages/intent/src/cli.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#!/usr/bin/env node
22

3-
import { existsSync, readFileSync, readdirSync } from 'node:fs'
3+
import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs'
44
import { dirname, join, relative, sep } from 'node:path'
5-
import { fileURLToPath, pathToFileURL } from 'node:url'
5+
import { fileURLToPath } from 'node:url'
66
import { INSTALL_PROMPT } from './install-prompt.js'
77
import type { ScanResult } from './types.js'
88

@@ -748,9 +748,12 @@ export async function main(argv: Array<string> = process.argv.slice(2)) {
748748
}
749749
}
750750

751-
const isMain =
752-
process.argv[1] !== undefined &&
753-
import.meta.url === pathToFileURL(process.argv[1]).href
751+
let isMain = false
752+
try {
753+
isMain =
754+
process.argv[1] !== undefined &&
755+
fileURLToPath(import.meta.url) === realpathSync(process.argv[1])
756+
} catch {}
754757

755758
if (isMain) {
756759
const exitCode = await main()

packages/intent/tests/cli.test.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
mkdirSync,
44
mkdtempSync,
55
readFileSync,
6+
realpathSync,
67
rmSync,
78
writeFileSync,
89
} from 'node:fs'
@@ -16,6 +17,7 @@ import { main, USAGE } from '../src/cli.js'
1617
const thisDir = dirname(fileURLToPath(import.meta.url))
1718
const metaDir = join(thisDir, '..', 'meta')
1819
const packageJsonPath = join(thisDir, '..', 'package.json')
20+
const realTmpdir = realpathSync(tmpdir())
1921

2022
function writeJson(filePath: string, data: unknown): void {
2123
mkdirSync(dirname(filePath), { recursive: true })
@@ -144,7 +146,7 @@ describe('cli commands', () => {
144146
})
145147

146148
it('lists installed intent packages as json', async () => {
147-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-list-'))
149+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-list-'))
148150
tempDirs.push(root)
149151
const pkgDir = join(root, 'node_modules', '@tanstack', 'db')
150152

@@ -180,7 +182,7 @@ describe('cli commands', () => {
180182
})
181183

182184
it('explains which package version was chosen when conflicts exist', async () => {
183-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-conflicts-'))
185+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-conflicts-'))
184186
tempDirs.push(root)
185187

186188
writeJson(join(root, 'package.json'), {
@@ -239,7 +241,7 @@ describe('cli commands', () => {
239241
})
240242

241243
it('validates a well-formed skills directory', async () => {
242-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-validate-'))
244+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-validate-'))
243245
tempDirs.push(root)
244246

245247
writeSkillMd(join(root, 'skills', 'db-core'), {
@@ -258,7 +260,7 @@ describe('cli commands', () => {
258260
})
259261

260262
it('validates package skills from repo root without root packaging warnings', async () => {
261-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-validate-mono-'))
263+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-validate-mono-'))
262264
tempDirs.push(root)
263265

264266
writeJson(join(root, 'package.json'), {
@@ -289,7 +291,7 @@ describe('cli commands', () => {
289291
})
290292

291293
it('fails cleanly when validate is run without a skills directory', async () => {
292-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-missing-skills-'))
294+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-missing-skills-'))
293295
tempDirs.push(root)
294296
process.chdir(root)
295297

@@ -302,7 +304,7 @@ describe('cli commands', () => {
302304
})
303305

304306
it('fails cleanly for unsupported yarn pnp projects', async () => {
305-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-pnp-'))
307+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-pnp-'))
306308
tempDirs.push(root)
307309
writeJson(join(root, 'package.json'), { name: 'app', private: true })
308310
writeFileSync(join(root, '.pnp.cjs'), 'module.exports = {}\n')
@@ -317,7 +319,7 @@ describe('cli commands', () => {
317319
})
318320

319321
it('fails cleanly for deno projects without node_modules', async () => {
320-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-deno-'))
322+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-deno-'))
321323
tempDirs.push(root)
322324
writeJson(join(root, 'package.json'), { name: 'app', private: true })
323325
writeFileSync(join(root, 'deno.json'), '{"nodeModulesDir":"none"}\n')
@@ -332,7 +334,7 @@ describe('cli commands', () => {
332334
})
333335

334336
it('checks workspace packages for staleness from the monorepo root', async () => {
335-
const root = mkdtempSync(join(tmpdir(), 'intent-cli-stale-mono-'))
337+
const root = mkdtempSync(join(realTmpdir, 'intent-cli-stale-mono-'))
336338
tempDirs.push(root)
337339

338340
writeJson(join(root, 'package.json'), {

0 commit comments

Comments
 (0)