Compare commits

..

8 Commits

Author SHA1 Message Date
40c0dfb3df 4.0.19
Some checks failed
Default (tags) / security (push) Failing after 22s
Default (tags) / test (push) Failing after 15s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-10-28 18:38:18 +00:00
4f243289b8 fix(license): Update license files 2025-10-28 18:38:18 +00:00
2d28939986 4.0.18
Some checks failed
Default (tags) / security (push) Failing after 27s
Default (tags) / test (push) Failing after 26s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-10-28 15:42:40 +00:00
01623eab2a fix(smartcli): Allow passing argv to startParse and improve getUserArgs Deno/runtime handling; update tests and add license 2025-10-28 15:42:39 +00:00
5c65c43589 4.0.17
Some checks failed
Default (tags) / security (push) Failing after 21s
Default (tags) / test (push) Failing after 13s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-10-28 15:10:45 +00:00
72109e478f fix(license): Add MIT license and local Claude settings 2025-10-28 15:10:44 +00:00
53d9956735 4.0.16
Some checks failed
Default (tags) / security (push) Failing after 21s
Default (tags) / test (push) Failing after 13s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-10-28 14:59:46 +00:00
913f8556d0 fix(smartcli.helpers): Improve CLI argument parsing and Deno runtime detection; use getUserArgs consistently 2025-10-28 14:59:46 +00:00
8 changed files with 77 additions and 31 deletions

View File

@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 Push.Rocks Copyright (c) 2015 Task Venture Capital GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,5 +1,35 @@
# Changelog # Changelog
## 2025-10-28 - 4.0.19 - fix(license)
Update license files and add local tool settings
- Update LICENSE header to reference Task Venture Capital GmbH as copyright holder
- Add a new license file containing the full MIT license text
- Add .claude/settings.local.json to store local tool permission settings
## 2025-10-28 - 4.0.18 - fix(smartcli)
Allow passing argv to startParse and improve getUserArgs Deno/runtime handling; update tests and add license
- Smartcli.startParse now accepts an optional testArgv parameter to bypass automatic runtime detection (makes testing deterministic).
- getUserArgs logic refined: always prefer Deno.args when available (handles Deno run and compiled executables reliably) and improve execPath fallback and slicing behavior for Node/Bun/other launchers.
- Tests updated: test/test.node+deno+bun.ts now passes process.argv explicitly to startParse to avoid Deno.args interference in test environments.
- Added MIT LICENSE file and a local .claude/settings.local.json for environment/permission settings.
## 2025-10-28 - 4.0.17 - fix(license)
Add MIT license and local Claude settings
- Add LICENSE file (MIT) to repository
- Add .claude/settings.local.json with local permissions for tooling
## 2025-10-28 - 4.0.16 - fix(smartcli.helpers)
Improve CLI argument parsing and Deno runtime detection; use getUserArgs consistently
- Enhance getUserArgs() to prefer Deno.args but detect when process.argv was manipulated (e.g. in tests) and fallback to manual parsing
- Add robust handling of process.execPath / execPath basename and compute correct argv offset for known launchers vs. compiled executables
- Call getUserArgs() (no explicit process.argv) from Smartcli.getOption and Smartcli.startParse to ensure consistent cross-runtime behavior
- Expand readme.hints.md with detailed cross-runtime examples and explanation of Deno.args vs process.argv for compiled executables
- Add local claude settings file for tooling configuration
## 2025-10-28 - 4.0.15 - fix(smartcli.helpers) ## 2025-10-28 - 4.0.15 - fix(smartcli.helpers)
Add robust getUserArgs helper and refactor Smartcli to use it; add MIT license and update documentation Add robust getUserArgs helper and refactor Smartcli to use it; add MIT license and update documentation

View File

@@ -1,7 +1,7 @@
{ {
"name": "@push.rocks/smartcli", "name": "@push.rocks/smartcli",
"private": false, "private": false,
"version": "4.0.15", "version": "4.0.19",
"description": "A library that simplifies building reactive command-line applications using observables, with robust support for commands, arguments, options, aliases, and asynchronous operation management.", "description": "A library that simplifies building reactive command-line applications using observables, with robust support for commands, arguments, options, aliases, and asynchronous operation management.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",

View File

@@ -1,28 +1,42 @@
## Cross-Runtime Compatibility ## Cross-Runtime Compatibility
### CLI Argument Parsing ### CLI Argument Parsing
The module uses a robust cross-runtime approach for parsing command-line arguments: The module uses a robust cross-runtime approach for parsing command-line arguments through the `getUserArgs()` utility in `ts/smartcli.helpers.ts`.
**Key Implementation:** **Runtime-Specific Implementations:**
- `getUserArgs()` utility (in `ts/smartcli.helpers.ts`) handles process.argv differences across Node.js, Deno, and Bun
- Uses `process.execPath` basename detection instead of content-based heuristics
- Prefers `Deno.args` when available (for Deno run/compiled), unless argv is explicitly provided
**Runtime Differences:** | Runtime | process.argv Structure | Preferred API | Reason |
- **Node.js**: `process.argv = ["/path/to/node", "/path/to/script.js", ...userArgs]` |---------|------------------------|---------------|---------|
- **Deno (run)**: `process.argv = ["deno", "/path/to/script.ts", ...userArgs]` (but `Deno.args` is preferred) | **Node.js** | `["/path/to/node", "/path/to/script.js", ...userArgs]` | Manual parsing | No native user-args API |
- **Deno (compiled)**: `process.argv = ["/path/to/executable", ...userArgs]` (custom executable name) | **Deno run** | `["deno", "/path/to/script.ts", ...userArgs]` | `Deno.args` ✅ | Pre-filtered by runtime |
- **Bun**: `process.argv = ["/path/to/bun", "/path/to/script.ts", ...userArgs]` | **Deno compiled** | `["/path/to/binary", "/tmp/deno-compile-.../mod.ts", ...userArgs]` | `Deno.args` ✅ | Filters internal bundle path |
| **Bun** | `["/path/to/bun", "/path/to/script.ts", ...userArgs]` | Manual parsing | Bun.argv not pre-filtered |
**How it works:** **Why Deno.args is Critical for Compiled Executables:**
1. If `Deno.args` exists and no custom argv provided, use it directly
2. Otherwise, detect runtime by checking `process.execPath` basename
3. If basename is a known launcher (node, deno, bun, tsx, ts-node), skip 2 args
4. If basename is unknown (compiled executable), skip only 1 arg
5. Safety check: if offset would skip everything, don't skip anything (handles test edge cases)
This approach works correctly with: Deno compiled executables insert an internal bundle path at `argv[1]`:
- Standard runtime execution ```javascript
- Compiled executables (Deno compile, Node pkg, etc.) process.argv = [
- Custom-named executables "/usr/local/bin/moxytool", // argv[0] - executable
- Test environments with unusual argv setups "/tmp/deno-compile-moxytool/mod.ts", // argv[1] - INTERNAL bundle path
"scripts", // argv[2] - actual user command
"--option" // argv[3+] - user args
]
Deno.args = ["scripts", "--option"] // ✓ Correctly filtered by Deno runtime
```
**getUserArgs() Logic:**
1. **Prefer Deno.args** when available (unless process.argv appears manipulated for testing)
2. **Fallback to manual parsing** for Node.js and Bun:
- Check `process.execPath` basename
- Known launchers (node, deno, bun, tsx, ts-node) → skip 2 args
- Unknown (compiled executables) → skip 1 arg
3. **Test detection**: If `process.argv.length > 2` in Deno, use manual parsing (handles test manipulation)
**Key Benefits:**
- ✅ Works with custom-named compiled executables
- ✅ Handles Deno's internal bundle path automatically
- ✅ Compatible with test environments
- ✅ No heuristics needed for Deno (runtime does the work)

View File

@@ -19,7 +19,8 @@ tap.test('should add an command', async (toolsArg) => {
console.log(process.argv); console.log(process.argv);
process.argv.splice(2, 0, 'awesome'); process.argv.splice(2, 0, 'awesome');
console.log(process.argv); console.log(process.argv);
smartCliTestObject.startParse(); // Pass process.argv explicitly for testing (bypasses Deno.args in Deno environments)
smartCliTestObject.startParse(process.argv);
await done.promise; await done.promise;
}); });

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartcli', name: '@push.rocks/smartcli',
version: '4.0.15', version: '4.0.19',
description: 'A library that simplifies building reactive command-line applications using observables, with robust support for commands, arguments, options, aliases, and asynchronous operation management.' description: 'A library that simplifies building reactive command-line applications using observables, with robust support for commands, arguments, options, aliases, and asynchronous operation management.'
} }

View File

@@ -92,7 +92,7 @@ export class Smartcli {
* getOption * getOption
*/ */
public getOption(optionNameArg: string) { public getOption(optionNameArg: string) {
const userArgs = getUserArgs(process.argv); const userArgs = getUserArgs();
const parsedYargs = plugins.yargsParser(userArgs); const parsedYargs = plugins.yargsParser(userArgs);
return parsedYargs[optionNameArg]; return parsedYargs[optionNameArg];
} }
@@ -123,11 +123,11 @@ export class Smartcli {
/** /**
* start the process of evaluating commands * start the process of evaluating commands
* @param testArgv - Optional argv override for testing (bypasses automatic runtime detection)
*/ */
public startParse(): void { public startParse(testArgv?: string[]): void {
// Get user arguments, properly handling Node.js, Deno (run/compiled), and Bun // Get user arguments, properly handling Node.js, Deno (run/compiled), and Bun
// Pass process.argv explicitly to handle test scenarios where it's modified const userArgs = testArgv ? getUserArgs(testArgv) : getUserArgs();
const userArgs = getUserArgs(process.argv);
const parsedYArgs = plugins.yargsParser(userArgs); const parsedYArgs = plugins.yargsParser(userArgs);
const wantedCommand = parsedYArgs._[0]; const wantedCommand = parsedYArgs._[0];

View File

@@ -13,14 +13,15 @@ export function getUserArgs(argv?: string[]): string[] {
// Prefer Deno.args when available and no custom argv provided; // Prefer Deno.args when available and no custom argv provided;
// it's the most reliable for Deno run and compiled. // it's the most reliable for Deno run and compiled.
// Deno.args is ALWAYS correct in Deno environments - it handles the internal bundle path automatically.
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
const g: any = typeof globalThis !== 'undefined' ? globalThis : {}; const g: any = typeof globalThis !== 'undefined' ? globalThis : {};
if (!useProvidedArgv && g.Deno && g.Deno.args && Array.isArray(g.Deno.args)) { if (!useProvidedArgv && g.Deno && g.Deno.args && Array.isArray(g.Deno.args)) {
return g.Deno.args.slice(); return g.Deno.args.slice();
} }
const a = const a = argv ?? (typeof process !== 'undefined' && Array.isArray(process.argv) ? process.argv : []);
argv ?? (typeof process !== 'undefined' && Array.isArray(process.argv) ? process.argv : []);
if (!Array.isArray(a) || a.length === 0) return []; if (!Array.isArray(a) || a.length === 0) return [];