@mariozechner/pi-agent
Version:
General-purpose agent with tool calling and session persistence
158 lines • 5.95 kB
JavaScript
import { homedir } from "os";
import { resolve } from "path";
export function parseArgs(defs, args) {
const result = { _: [] };
const aliasMap = {};
// Build alias map and set defaults
for (const [key, def] of Object.entries(defs)) {
if (def.alias) {
aliasMap[def.alias] = key;
}
if (def.default !== undefined) {
result[key] = def.default;
}
else if (def.type === "flag" || def.type === "boolean") {
result[key] = false;
}
}
// Parse arguments
for (let i = 0; i < args.length; i++) {
const arg = args[i];
// Check if it's a flag
if (arg.startsWith("--")) {
const flagName = arg.slice(2);
const key = aliasMap[flagName] || flagName;
const def = defs[key];
if (!def) {
// Unknown flag, add to positional args
result._.push(arg);
continue;
}
if (def.type === "flag") {
// Simple on/off flag
result[key] = true;
}
else if (i + 1 < args.length) {
// Flag with value
const value = args[++i];
let parsedValue;
switch (def.type) {
case "boolean":
parsedValue = value === "true" || value === "1" || value === "yes";
break;
case "int":
parsedValue = parseInt(value, 10);
if (Number.isNaN(parsedValue)) {
throw new Error(`Invalid integer value for --${key}: ${value}`);
}
break;
case "float":
parsedValue = parseFloat(value);
if (Number.isNaN(parsedValue)) {
throw new Error(`Invalid float value for --${key}: ${value}`);
}
break;
case "string":
parsedValue = value;
break;
case "file": {
// Resolve ~ to home directory and make absolute
let path = value;
if (path.startsWith("~")) {
path = path.replace("~", homedir());
}
parsedValue = resolve(path);
break;
}
}
// Validate against choices if specified
if (def.choices) {
const validValues = def.choices.map((c) => (typeof c === "string" ? c : c.value));
if (!validValues.includes(parsedValue)) {
throw new Error(`Invalid value for --${key}: "${parsedValue}". Valid choices: ${validValues.join(", ")}`);
}
}
result[key] = parsedValue;
}
else {
throw new Error(`Flag --${key} requires a value`);
}
}
else if (arg.startsWith("-") && arg.length === 2) {
// Short flag like -h
const flagChar = arg[1];
const key = aliasMap[flagChar] || flagChar;
const def = defs[key];
if (!def) {
result._.push(arg);
continue;
}
if (def.type === "flag") {
result[key] = true;
}
else {
throw new Error(`Short flag -${flagChar} cannot have a value`);
}
}
else {
// Positional argument
result._.push(arg);
}
}
return result;
}
export function printHelp(defs, usage) {
console.log(usage);
console.log("\nOptions:");
for (const [key, def] of Object.entries(defs)) {
let line = ` --${key}`;
if (def.alias) {
line += `, -${def.alias}`;
}
if (def.type !== "flag") {
if (def.choices) {
// Show choices instead of type
const simpleChoices = def.choices.filter((c) => typeof c === "string");
if (simpleChoices.length === def.choices.length) {
// All choices are simple strings
line += ` <${simpleChoices.join("|")}>`;
}
else {
// Has descriptions, just show the type
const typeStr = def.type === "file" ? "path" : def.type;
line += ` <${typeStr}>`;
}
}
else {
const typeStr = def.type === "file" ? "path" : def.type;
line += ` <${typeStr}>`;
}
}
if (def.description) {
// Pad to align descriptions
line = line.padEnd(30) + def.description;
}
if (def.default !== undefined && def.type !== "flag" && def.showDefault !== false) {
if (typeof def.showDefault === "string") {
line += ` (default: ${def.showDefault})`;
}
else {
line += ` (default: ${def.default})`;
}
}
console.log(line);
// Print choices with descriptions if available
if (def.choices) {
const hasDescriptions = def.choices.some((c) => typeof c === "object" && c.description);
if (hasDescriptions) {
for (const choice of def.choices) {
if (typeof choice === "object") {
const choiceLine = ` ${choice.value}`.padEnd(30) + (choice.description || "");
console.log(choiceLine);
}
}
}
}
}
}
//# sourceMappingURL=args.js.map