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
34 changes: 24 additions & 10 deletions apps/create-agentuity/bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,33 @@ const require = createRequire(import.meta.url);
const pkg = require('./package.json');

// Determine the dist-tag based on create-agentuity version
// Prerelease versions (e.g., 2.0.0-beta.1) should use @next
// Stable versions should use @latest
function getDistTag(version) {
// Check for prerelease identifiers: alpha, beta, rc, canary, next, etc.
// Must match the logic in scripts/publish.ts:
// - Beta versions (-beta.) → use @beta
// - Other prereleases (-alpha., -rc., etc.) → use @next
// - Stable versions → use @latest
export function getDistTag(version) {
// Check for beta prerelease first
if (/-beta\./.test(version)) {
return 'beta';
}
// Check for other prerelease identifiers: alpha, rc, canary, next, etc.
if (/-([a-zA-Z]+)/.test(version)) {
return 'next';
}
return 'latest';
}

const distTag = getDistTag(pkg.version);
const args = process.argv.slice(2);
const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], {
stdio: 'inherit',
});
process.exit(result.status || 0);
// Only run when executed directly, not when imported for testing
const isMain =
typeof Bun !== 'undefined'
? Bun.main === new URL(import.meta.url).pathname
: process.argv[1] === new URL(import.meta.url).pathname;
Comment on lines +26 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Bun and Node.js ESM, what is the recommended cross-platform way to detect "executed as main module" from import.meta.url, and is URL.pathname safe for Windows path comparisons?

💡 Result:

In Bun and recent Node.js (v22.18+ LTS and v24.2+ current as of 2026), the recommended cross-platform way to detect if the module is executed as the main module is to use import.meta.main, a boolean that is true if the current module is the entry point of the process. This property is available in Bun docs and Node.js ESM documentation. It eliminates the need for manual URL comparisons. For older Node.js versions lacking import.meta.main, the cross-platform fallback is: js import { pathToFileURL } from 'node:url'; import { fileURLToPath } from 'node:url'; const mainPath = process.argv[1] ? fileURLToPath(pathToFileURL(process.argv[1]).href) : null; const currentPath = fileURLToPath(import.meta.url); const isMain = mainPath && mainPath === currentPath; However, with import.meta.main standardized across Bun and modern Node.js, it is the preferred method. URL.pathname is NOT safe for Windows path comparisons. On Windows, new URL(import.meta.url).pathname starts with / (e.g., /C:/path/file.js), using forward slashes, which does not match process.argv[1] (e.g., C:\path\file.js with backslashes). Direct string or pathname comparisons fail cross-platform and on Windows. Always convert via fileURLToPath(import.meta.url) and pathToFileURL(process.argv[1]) for safe handling of percent-encoding, slashes, and drive letters.

Citations:


🏁 Script executed:

cat -n apps/create-agentuity/bin.js | head -35

Repository: agentuity/sdk

Length of output: 1439


🏁 Script executed:

fd package.json | head -5 | xargs cat | grep -A 5 -B 5 "engines\|node"

Repository: agentuity/sdk

Length of output: 39


🏁 Script executed:

find . -name "package.json" -type f | xargs grep -l "create-agentuity" 2>/dev/null

Repository: agentuity/sdk

Length of output: 93


🏁 Script executed:

cat apps/create-agentuity/package.json | grep -A 3 -B 3 "engines\|\"node"

Repository: agentuity/sdk

Length of output: 39


🏁 Script executed:

cat apps/create-agentuity/package.json

Repository: agentuity/sdk

Length of output: 599


🏁 Script executed:

cat package.json | grep -A 5 -B 5 "engines"

Repository: agentuity/sdk

Length of output: 39


🏁 Script executed:

cat .nvmrc .node-version .tool-versions 2>/dev/null || echo "No version file found"

Repository: agentuity/sdk

Length of output: 78


Use import.meta.main for cross-platform isMain detection, or fileURLToPath() as a fallback.

Lines 28–29 compare filesystem paths against URL.pathname, which is unsafe on Windows and will cause CLI execution to fail. On Windows, new URL(import.meta.url).pathname produces /C:/path/file.js (with leading / and forward slashes) while process.argv[1] contains C:\path\file.js (backslashes), so the comparison always fails.

For modern Node.js (v22.18+ LTS, v24.2+) and Bun, use import.meta.main (a boolean flag that is true when the module is executed as the entry point). For older Node.js versions, use fileURLToPath(import.meta.url) to safely convert the URL to a filesystem path for comparison.

🔧 Proposed fix (modern)
 import { spawnSync } from 'node:child_process';
 import { createRequire } from 'node:module';
@@
-const isMain =
-	typeof Bun !== 'undefined'
-		? Bun.main === new URL(import.meta.url).pathname
-		: process.argv[1] === new URL(import.meta.url).pathname;
+const isMain = import.meta.main;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/create-agentuity/bin.js` around lines 26 - 29, Replace the fragile
pathname comparison in the isMain detection with a cross-platform approach:
prefer import.meta.main when available (use it for Node v22.18+/v24.2+ and Bun),
and for older Node fall back to converting import.meta.url to a filesystem path
via fileURLToPath(import.meta.url) and compare that to process.argv[1]; keep Bun
detection (Bun.main) only as a fallback if needed. Update the const isMain
declaration (the symbol named isMain) to implement this order of checks so
comparisons no longer rely on URL.pathname and will work on Windows.


if (isMain) {
const distTag = getDistTag(pkg.version);
const args = process.argv.slice(2);
const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], {
stdio: 'inherit',
});
process.exit(result.status || 0);
Comment on lines +34 to +37

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For Node.js child_process.spawnSync, when are result.statusandresult.error set, and what exit code handling is recommended for spawn errors or signal termination?

💡 Result:

result.status and result.error in Node.js child_process.spawnSync: - result.status: Set to the exit code of the child process when it exits normally (integer, typically 0 for success). Set to null if the process was terminated by a signal (result.signal is set) or if spawning failed (result.error is set). - result.error: Set to an Error instance only when the spawning itself fails (e.g., command not found - ENOENT, permission denied - EACCES, too many processes - EMFILE/ENFILE). In this case, result.status is null, and stdout/stderr/output are null. If the child spawns successfully but exits with non-zero code or signal, result.error is undefined. Exit code handling recommendations: - Always check if (result.error) { /* handle spawn failure, e.g. log result.error.code/path/syscall / } - Then if (result.status !== 0 || result.signal) { / handle child failure */ }. Non-zero status indicates child error; signal (non-null) indicates termination by signal (e.g. SIGTERM, SIGKILL). For signals, consider using util.convertProcessSignalToExitCode(signal) to map to an equivalent exit code. - Example from docs (adapted for spawnSync): try { const result = spawnSync('ls'); if (result.error) { console.error(Spawn failed: ${result.error.code}); // e.g. 'ENOENT' } else if (result.status !== 0) { console.error(Child exited with code ${result.status}); console.error(stdout: ${result.stdout}); console.error(stderr: ${result.stderr}); } else if (result.signal) { console.error(Killed by signal ${result.signal}); } else { // success } } catch (err) { // Only for timeout or other sync errors } Note: spawnSync does not throw on non-zero exit (unlike execSync); always inspect the returned object. Use { stdio: 'pipe' } or specific config to capture stdout/stderr.

Citations:


🏁 Script executed:

cat -n apps/create-agentuity/bin.js | sed -n '30,40p'

Repository: agentuity/sdk

Length of output: 361


🏁 Script executed:

rg "spawnSync|execSync" apps/create-agentuity/bin.js

Repository: agentuity/sdk

Length of output: 190


Check for spawn failures before exiting.

Line 37 uses result.status || 0, which masks spawn failures. When spawnSync fails (result.error set) or the process is terminated by signal (result.status becomes null), the expression null || 0 returns 0, causing the script to exit successfully despite the subprocess failure. Additionally, if the subprocess exits with code 0, this still works correctly, but any non-zero exit code should propagate.

🔧 Proposed fix
 	const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], {
 		stdio: 'inherit',
 	});
-	process.exit(result.status || 0);
+	if (result.error) {
+		console.error(result.error.message);
+		process.exit(1);
+	}
+	process.exit(result.status ?? 1);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], {
stdio: 'inherit',
});
process.exit(result.status || 0);
const result = spawnSync('bunx', [`@agentuity/cli@${distTag}`, 'create', ...args], {
stdio: 'inherit',
});
if (result.error) {
console.error(result.error.message);
process.exit(1);
}
process.exit(result.status ?? 1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/create-agentuity/bin.js` around lines 34 - 37, The current spawnSync
invocation result is not checked for failures: inspect the spawnSync return
(result) from spawnSync('bunx', ...) and if result.error is set or result.status
is not a number (null due to signal) exit with a non-zero code instead of 0;
otherwise propagate result.status. Concretely, after spawnSync check
result.error and call process.exit(result.error.code || 1), if result.status is
null but result.signal exists call process.exit(1) (or map signal to 128+signal
if desired), and only call process.exit(result.status) when typeof result.status
=== 'number' to ensure subprocess failures and signals are not masked.

}
47 changes: 47 additions & 0 deletions apps/create-agentuity/bin.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bun test
import { describe, test, expect } from 'bun:test';
import { getDistTag } from './bin.js';

describe('getDistTag', () => {
describe('beta versions', () => {
test('2.0.0-beta.0 should return beta', () => {
expect(getDistTag('2.0.0-beta.0')).toBe('beta');
});
test('2.0.0-beta.1 should return beta', () => {
expect(getDistTag('2.0.0-beta.1')).toBe('beta');
});
test('1.0.0-beta.10 should return beta', () => {
expect(getDistTag('1.0.0-beta.10')).toBe('beta');
});
});

describe('other prerelease versions', () => {
test('2.0.0-alpha.0 should return next', () => {
expect(getDistTag('2.0.0-alpha.0')).toBe('next');
});
test('2.0.0-rc.1 should return next', () => {
expect(getDistTag('2.0.0-rc.1')).toBe('next');
});
test('1.0.0-canary.3 should return next', () => {
expect(getDistTag('1.0.0-canary.3')).toBe('next');
});
test('2.0.0-next.5 should return next', () => {
expect(getDistTag('2.0.0-next.5')).toBe('next');
});
});

describe('stable versions', () => {
test('1.0.0 should return latest', () => {
expect(getDistTag('1.0.0')).toBe('latest');
});
test('1.0.62 should return latest', () => {
expect(getDistTag('1.0.62')).toBe('latest');
});
test('2.0.0 should return latest', () => {
expect(getDistTag('2.0.0')).toBe('latest');
});
test('10.20.30 should return latest', () => {
expect(getDistTag('10.20.30')).toBe('latest');
});
});
});
2 changes: 1 addition & 1 deletion apps/create-agentuity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"scripts": {
"build": "echo 'No build required'",
"test": "echo 'No tests for create-agentuity (CLI wrapper)'"
"test": "bun test bin.test.js"
},
"files": [
"bin.js"
Expand Down
Loading