cligentic
← back to catalog
cligentic block
argv
Minimal POSIX argv parser for zero-framework CLIs. Zero dependencies. Handles --flag, --flag value, --flag=value, -f, combined short flags, positional args, -- stop, and kebab-to-camelCase normalization.
Install
NPM dependencies
zero deps — pure TS
Block dependencies
standalone
Size
84 LOC
Source
The full file, 84 lines.
This is the exact TypeScript that lands in your project when you run the install command. Read it, copy it, edit it, own it. cligentic never touches it again.
src/cli/foundation/argv.ts
// cligentic block: argv
//
// Minimal POSIX argv parser for zero-framework CLIs.
// No dependencies. Handles --flag, --flag value, --flag=value, -f, positional args.
//
// Usage:
// import { parseArgv } from "./foundation/argv";
//
// const args = parseArgv();
// // myapp --dry-run --output=file.json input.txt
// // => { _: ["input.txt"], dryRun: true, output: "file.json" }
export type ParsedArgs = { _: string[]; [key: string]: unknown };
function kebabToCamel(s: string): string {
return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
}
/**
* Parses process.argv.slice(2) by default, or the provided argv array.
*
* Rules:
* --flag → flag: true
* --flag value → flag: "value" (when next token doesn't start with -)
* --flag=value → flag: "value"
* -f → f: true
* -fv → f: true, v: true (combined short flags)
* -- → stop flag parsing; rest goes into _
* kebab-case keys → camelCase (--dry-run → dryRun)
*/
export function parseArgv(argv: string[] = process.argv.slice(2)): ParsedArgs {
const result: ParsedArgs = { _: [] };
let i = 0;
let stopped = false;
while (i < argv.length) {
const arg = argv[i];
if (stopped || arg === undefined) {
if (arg !== undefined) result._.push(arg);
i++;
continue;
}
if (arg === "--") {
stopped = true;
i++;
continue;
}
if (arg.startsWith("--")) {
const eqIdx = arg.indexOf("=");
if (eqIdx !== -1) {
const key = kebabToCamel(arg.slice(2, eqIdx));
result[key] = arg.slice(eqIdx + 1);
} else {
const key = kebabToCamel(arg.slice(2));
const next = argv[i + 1];
if (next !== undefined && !next.startsWith("-")) {
result[key] = next;
i++;
} else {
result[key] = true;
}
}
i++;
continue;
}
if (arg.startsWith("-") && arg.length > 1) {
for (let j = 1; j < arg.length; j++) {
result[arg[j] as string] = true;
}
i++;
continue;
}
result._.push(arg);
i++;
}
return result;
}