UNPKG

akm-cli

Version:

akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur

126 lines (125 loc) • 5.1 kB
// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. /** * Process-level output mode singleton. * * Output mode (format + detail + shape) is parsed once at startup from * `process.argv` and the persisted user config. All subsequent `output()` * calls read from this in-memory singleton instead of re-scanning argv and * re-loading config on every call. * * Initialized from `cli.ts` before `runCli` dispatches the command (`cli.ts` * drives citty's `runCommand` directly rather than `runMain` — see the * top-level driver comment there). */ import { UsageError } from "../core/errors.js"; const OUTPUT_FORMATS = ["json", "yaml", "text", "jsonl", "md", "html"]; const DETAIL_LEVELS = ["brief", "normal", "full"]; const SHAPE_MODES = ["human", "agent", "summary"]; function parseOutputFormat(value) { if (!value) return undefined; if (OUTPUT_FORMATS.includes(value)) return value; throw new UsageError(`Invalid value for --format: ${value}. Expected one of: ${OUTPUT_FORMATS.join("|")}`, "INVALID_FORMAT_VALUE"); } export function parseDetailLevel(value) { if (!value) return undefined; if (DETAIL_LEVELS.includes(value)) return value; throw new UsageError(`Invalid value for --detail: ${value}. Expected one of: ${DETAIL_LEVELS.join("|")}`, "INVALID_DETAIL_VALUE"); } function parseShapeMode(value) { if (!value) return undefined; if (SHAPE_MODES.includes(value)) return value; throw new UsageError(`Invalid value for --shape: ${value}. Expected one of: ${SHAPE_MODES.join("|")}`, "INVALID_SHAPE_VALUE"); } export function parseFlagValue(argv, flag) { for (let i = 0; i < argv.length; i++) { const arg = argv[i]; if (arg === "--") break; if (arg === flag) return argv[i + 1]; if (arg.startsWith(`${flag}=`)) return arg.slice(flag.length + 1); } return undefined; } export function hasBooleanFlag(argv, flag) { for (const arg of argv) { if (arg === "--") return false; if (arg === flag || arg === `${flag}=true`) return true; } return false; } /** * Read a hyphenated arg out of citty's parsed `args` object. * * Verified live: citty DOES auto-camelise a DECLARED hyphenated arg — for * `"max-pages": { type: "string" }` in a command's `args`, citty's parsed * object answers both `args["max-pages"]` and `args.maxPages` (a `Proxy` * falls back through `camelCase`/`kebabCase`, and `--no-<flag>` negation is * handled the same way for booleans). What citty's own TS types do NOT give * callers is a statically-typed handle on that mapping, and command handlers * here type `args` as `unknown` at the boundary — so every read site still * needs a cast. This helper encapsulates that cast; it exists for typing * convenience, not to work around a citty parsing limitation. */ export function getHyphenatedArg(args, key) { if (typeof args !== "object" || args === null) return undefined; const value = args[key]; return value === undefined ? undefined : value; } /** Boolean variant of {@link getHyphenatedArg} for `--<flag>` switches. */ export function getHyphenatedBoolean(args, key) { return Boolean(getHyphenatedArg(args, key)); } /** * Resolve output mode from a synthetic argv array and config defaults. * Pure function — no IO. Suitable for unit tests. */ export function resolveOutputMode(argv, defaults = {}) { const format = parseOutputFormat(parseFlagValue(argv, "--format")) ?? defaults?.format ?? "json"; const rawDetail = parseFlagValue(argv, "--detail"); const rawShape = parseFlagValue(argv, "--shape"); // `--detail` is verbosity only (brief|normal|full); the projection presets // (`summary`/`agent`) and the `--for-agent` boolean were removed in 0.9.0 — // use `--shape`. Unknown `--detail` values fall through to the default. const detail = parseDetailLevel(rawDetail) ?? defaults?.detail ?? "brief"; const shape = parseShapeMode(rawShape) ?? "human"; const outputPath = parseFlagValue(argv, "--output"); return { format, detail, shape, ...(outputPath ? { outputPath } : {}) }; } let _mode; /** * Initialize the process-level output mode. Must be called once at startup * before any code calls `getOutputMode()`. Subsequent calls overwrite. */ export function initOutputMode(argv, defaults = {}) { _mode = resolveOutputMode(argv, defaults); return _mode; } /** * Read the process-level output mode. Throws if `initOutputMode()` was not * called first — that is a programmer error, not a runtime condition. */ export function getOutputMode() { if (!_mode) { throw new Error("OutputMode not initialized. Call initOutputMode() before getOutputMode()."); } return _mode; } /** * Reset the singleton. Test-only utility. */ export function resetOutputMode() { _mode = undefined; }