@master4n/master-cli
Advanced tools
| import type { CommandInfo } from './catalog'; | ||
| /** Agent generation 1: token savers and deterministic ground-truth commands. */ | ||
| export declare const AGENT_COMMANDS: readonly CommandInfo[]; |
| import type { CommandInfo } from './catalog'; | ||
| /** The original toolkit: discovery, time, crypto primitives, OS/filesystem. */ | ||
| export declare const CLASSIC_COMMANDS: readonly CommandInfo[]; |
| import type { CommandInfo } from './catalog'; | ||
| /** | ||
| * Agent generation 3: OS-level commands — the things an agent normally needs | ||
| * platform-specific incantations (or a human's hands) for, made one-call and | ||
| * safe-by-default on macOS, Windows, and Linux. | ||
| */ | ||
| export declare const OS_COMMANDS: readonly CommandInfo[]; |
| import type { CommandInfo } from './catalog'; | ||
| /** | ||
| * Agent generation 2: the commands an AI coding agent reaches for constantly — | ||
| * code navigation, dependency tracing, bulk edits, readiness waits, and | ||
| * environment drift checks. | ||
| */ | ||
| export declare const POWER_COMMANDS: readonly CommandInfo[]; |
| declare const base: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default base; |
| declare const calc: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default calc; |
| declare const caseCmd: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<void>; | ||
| }; | ||
| export default caseCmd; |
| declare const clip: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<void>; | ||
| }; | ||
| export default clip; |
| declare const count: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default count; |
| declare const cron: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default cron; |
| /** | ||
| * Minimal, dependency-free 5-field cron engine (minute hour day-of-month month | ||
| * day-of-week). Supports * , - / lists, ranges, and steps. Standard semantics: | ||
| * when BOTH dom and dow are restricted, a date matches if EITHER matches (Vixie | ||
| * cron rule). | ||
| */ | ||
| export interface CronFields { | ||
| minute: Set<number>; | ||
| hour: Set<number>; | ||
| dayOfMonth: Set<number>; | ||
| month: Set<number>; | ||
| dayOfWeek: Set<number>; | ||
| domRestricted: boolean; | ||
| dowRestricted: boolean; | ||
| } | ||
| export declare function parseCron(expression: string): CronFields; | ||
| /** Next `count` run times strictly after `from` (local time), bounded to 5 years. */ | ||
| export declare function nextRuns(f: CronFields, from: Date, count: number): Date[]; |
| declare const diff: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default diff; |
| declare const disk: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default disk; |
| declare const dns: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<void>; | ||
| }; | ||
| export default dns; |
| declare const dotenv: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default dotenv; |
| declare const env: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => void; | ||
| }; | ||
| export default env; |
| /** | ||
| * TTY-only fallback for `mfn epoch` when no flags/positional are supplied. | ||
| * Lives in its own module to keep epoch.ts focused on the headless paths | ||
| * (and under the 150-line file budget). | ||
| */ | ||
| export declare function epochInteractive(argv: any, epochToDate: (argv: any, value: number) => void, dateToEpoch: (argv: any, input: string, format?: string, tz?: string) => void): Promise<void>; |
| declare const escapeCmd: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default escapeCmd; |
| declare const ext: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default ext; |
| declare const freq: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default freq; |
| declare const have: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default have; |
| declare const http: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default http; |
| declare const imports: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<void>; | ||
| }; | ||
| export default imports; |
| declare const ip: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => void; | ||
| }; | ||
| export default ip; |
| declare const jsonCmd: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<void>; | ||
| }; | ||
| export default jsonCmd; |
| declare const lines: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default lines; |
| declare const notify: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default notify; |
| declare const openCmd: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default openCmd; |
| declare const outlineCmd: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default outlineCmd; |
| /** | ||
| * Regex-based source outliner. Not a real parser — it's the 95% answer in | ||
| * ~zero time: top-level symbols with line numbers, so an agent can read a | ||
| * 50-token outline and then fetch only the lines it needs (`mfn lines`). | ||
| */ | ||
| export interface Symbol { | ||
| line: number; | ||
| kind: string; | ||
| name: string; | ||
| exported: boolean; | ||
| } | ||
| export declare const SUPPORTED_EXTENSIONS: string[]; | ||
| export declare function outline(text: string, extension: string): Symbol[]; |
| declare const pkgCmd: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default pkgCmd; |
| declare const ports: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default ports; |
| declare const procs: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default procs; |
| declare const recent: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default recent; |
| declare const regex: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default regex; |
| declare const replace: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default replace; |
| declare const repo: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default repo; |
| declare const schema: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default schema; |
| declare const semver: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => void; | ||
| }; | ||
| export default semver; |
| declare const size: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default size; |
| declare const sys: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => void; | ||
| }; | ||
| export default sys; |
| declare const trash: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<undefined>; | ||
| }; | ||
| export default trash; |
| declare const url: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => undefined; | ||
| }; | ||
| export default url; |
| declare const wait: { | ||
| command: string; | ||
| describe: string; | ||
| builder: (yargs: any) => any; | ||
| handler: (argv: any) => Promise<void>; | ||
| }; | ||
| export default wait; |
| /** | ||
| * Shape every command module exports and `src/index.ts` registers. Typing the | ||
| * registration (instead of `any`) means a malformed command module fails the | ||
| * typecheck instead of failing at runtime. | ||
| */ | ||
| export interface CliCommand { | ||
| /** yargs command spec, e.g. `epoch <value>` or `sc [pattern]`. */ | ||
| command: string; | ||
| /** One-line description shown in `mfn -h`. */ | ||
| describe: string; | ||
| /** yargs builder adding the command's options/examples. */ | ||
| builder: (yargs: any) => any; | ||
| /** Command implementation; receives parsed argv. */ | ||
| handler: (argv: any) => void | Promise<void>; | ||
| } |
| /** | ||
| * True when reading this file's CONTENT would expose credentials/keys/PII. | ||
| * Checks the literal path AND the resolved realpath, so an innocently named | ||
| * symlink ("notes.txt" → "~/.ssh/id_rsa") can't smuggle a secret past the | ||
| * basename check (found by an adversarial review). | ||
| */ | ||
| export declare function isSensitivePath(p: string): boolean; | ||
| /** Standard refusal message for a sensitive path (stable error: SensitivePath). */ | ||
| export declare const sensitivePathMessage: (p: string) => string; | ||
| /** Returns what kind of secret the text contains, or null when none detected. */ | ||
| export declare function scanSecrets(text: string): string | null; | ||
| /** | ||
| * Mask secret-shaped substrings inside text that a command echoes back. This | ||
| * is the content-level backstop to isSensitivePath's path-level check: even | ||
| * when a secret arrives via stdin (`cat .env | mfn freq`) — where there is no | ||
| * path to vet — a JWT/private key/cloud token is replaced before it reaches | ||
| * stdout. Returns the (possibly) masked text and how many tokens were masked. | ||
| */ | ||
| export declare function redactSecrets(text: string): { | ||
| text: string; | ||
| redactedCount: number; | ||
| }; | ||
| /** | ||
| * True for URLs that must never be probed: cloud metadata services. Local | ||
| * dev servers (localhost/127.0.0.1) stay allowed — probing them is the point. | ||
| */ | ||
| export declare function isBlockedUrl(url: URL): boolean; | ||
| export declare const blockedUrlMessage: (url: string) => string; |
| /** | ||
| * Rewrite yargs' "Unknown argument: X" into an agent-actionable message when X | ||
| * is the attempted command: name the problem correctly, suggest the nearest | ||
| * real command, and point at `mfn capabilities`. | ||
| */ | ||
| export declare function improveUnknownCommandMessage(msg: string): string; |
+90
| # Security Policy | ||
| ## Reporting a vulnerability | ||
| Please open a private report via | ||
| [GitHub Security Advisories](https://github.com/Master4Novice/master-cli/security/advisories/new) | ||
| rather than a public issue. You should receive a response within a week. | ||
| ## Threat model & design guarantees | ||
| `mfn` is a local developer tool. It is designed so that untrusted *input* | ||
| (strings, tokens, file contents, port numbers) can never escalate into code | ||
| execution or unintended process termination: | ||
| - **No shell interpolation.** Every external process (`npm`, `lsof`, `netstat`, | ||
| `kill`, `taskkill`) is spawned with `execFile` — arguments are passed as an | ||
| array, never interpolated into a shell string. A crafted package name or port | ||
| value cannot inject commands. | ||
| - **Strict input validation.** Ports must be integers in 1..65535; counts, | ||
| sizes, and byte lengths have hard upper bounds; PIDs parsed from `lsof`/ | ||
| `netstat` output must be strictly numeric before being passed to `kill`. | ||
| - **JWTs are decoded, never verified or transmitted.** `mfn decode` performs | ||
| local base64url decoding only; the token never leaves the machine, and the | ||
| output explicitly states the signature is not verified. | ||
| - **Crypto uses Node's CSPRNG.** `random` and `id` use `node:crypto` | ||
| (`randomBytes`, `randomUUID`) with rejection sampling — no `Math.random`, no | ||
| modulo bias. | ||
| - **Local cache is private.** `~/.mfn/cache` (recent ports, ignore lists) is | ||
| created with mode `0700`. | ||
| - **No network calls** except `mfn update`, which delegates to `npm` itself. | ||
| - **No telemetry.** Nothing is collected or sent anywhere. | ||
| ## Guardrails (active, no override flags) | ||
| `mfn` is built to be safe to hand to an AI agent. These guardrails are always | ||
| on — there are deliberately **no bypass flags**, because an agent will find and | ||
| use any flag that exists: | ||
| | Guardrail | Commands | What it prevents | | ||
| | --------- | -------- | ---------------- | | ||
| | **Sensitive-path refusal** (`SensitivePath`, exit 2) | `lines` `json` `schema` `diff` `freq` `regex -f` | Returning the CONTENT of credential stores: `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.kube`, `.env*`, `*.pem`, `*.key`, `id_rsa*`, `.npmrc`, `.netrc`, `shadow`, … An agent's context window is a log that never rotates. | | ||
| | **Clipboard secret redaction** | `clip` (read) | Passwords/tokens pasted through the clipboard (password managers). Secret-shaped content (private-key blocks, JWTs, AWS/GitHub/Slack/Google/npm/`sk-` tokens) is withheld with `redacted:true`. | | ||
| | **Env value scanning** | `env` | Redacts by NAME pattern (key/token/secret/…) **and** by VALUE shape — an innocently named variable holding a JWT is still redacted. `mfn env` with no names lists names only. | | ||
| | **Dotenv never reads values** | `dotenv` | Compares KEY presence between `.env` and `.env.example`; values are never parsed, stored, or returned. | | ||
| | **Cloud-metadata block** (`BlockedTarget`, exit 2) | `http` `wait -u` | SSRF credential theft via `169.254.169.254`, `metadata.google.internal`, Alibaba/AWS v6 endpoints. Localhost stays allowed — probing your own dev server is the point. | | ||
| | **Session-cookie redaction** | `http` | `set-cookie` response headers are replaced with `[redacted: session cookie]`. | | ||
| | **Scheme allow-list** | `open` | Only http(s) URLs or existing local paths; `javascript:`, `file:`, and custom protocol handlers are refused. | | ||
| | **Reversible delete only** | `trash` | Moves to the OS trash (never unlink); refuses `/`, home, cwd, and any parent of cwd — compared by realpath so symlinks can't fool it. | | ||
| | **PID floor + numeric check** | `kill` | A parsing surprise can never become `kill -9 0/1/-1`; ports validated 1..65535. | | ||
| | **Tool-name charset** | `have` | Probed names must be plain command names — no paths, no metacharacters. | | ||
| | **Dry-run by default** | `replace` | File modification requires explicit `--write`; scope is bounded to cwd. | | ||
| | **Body/range caps everywhere** | `http` (2KB preview) `lines` (2000) `diff` (20k lines) `clip` (1MB) … | One call can never flood a context window or memory. | | ||
| ## Guardrail scope & known limits (honest) | ||
| The credential guardrail is **defense-in-depth, not a security boundary**. It is | ||
| two layers: | ||
| 1. **Path layer** — refuses sensitive paths, resolving symlinks via realpath so | ||
| an innocently named symlink can't smuggle a secret file's content out. | ||
| 2. **Content layer** — masks secret-*shaped* substrings (private-key blocks, | ||
| JWTs, AWS/GitHub/Slack/Google/npm/`sk-` tokens) in the content that | ||
| `lines`/`freq`/`regex`/`json` echo, **including content piped via stdin** | ||
| where there is no path to vet. | ||
| Known limits, stated plainly: | ||
| - **Hardlinks** to a sensitive file are not detected at the path layer (a | ||
| hardlink shares the file's inode but has no distinguishable path). The content | ||
| layer still masks recognised token shapes; and creating a hardlink requires | ||
| the same read access as reading the original, so it grants no new capability. | ||
| - The content layer matches **known token shapes**, not arbitrary `KEY=value` | ||
| secrets — over-broad redaction would break legitimate config/log processing. | ||
| - An agent that already has shell access can read any file directly with `cat`. | ||
| These guardrails reduce *accidental* exposure through `mfn`'s own output | ||
| (the common path by which secrets leak into an agent's context window); they | ||
| are not a sandbox and do not claim to be. | ||
| ## Scope notes | ||
| - `mfn hash` supports `md5`/`sha1` for checksum interop with legacy systems — | ||
| they are not suitable for security purposes; use `sha256`/`sha512`. | ||
| - `mfn kill` sends `SIGKILL` to processes the invoking user owns; it cannot | ||
| affect other users' processes beyond what the OS already permits. | ||
| - `mfn update <package>` installs a named package globally via npm — only point | ||
| it at packages you trust, exactly as with `npm install -g`. | ||
| ## Supported versions | ||
| Only the latest published major (3.x) receives security fixes. |
+5
-1
| /** | ||
| * Single source of truth for the command catalogue — consumed by the welcome | ||
| * banner (the "tools" line) and by `mfn capabilities` (the agent-facing | ||
| * manifest). Keep this in sync when adding a command. | ||
| * manifest). Add new commands to the matching catalog.*.ts data module. | ||
| */ | ||
@@ -9,2 +9,4 @@ export interface CommandInfo { | ||
| name: string; | ||
| /** Functional grouping an agent can filter on. */ | ||
| category: 'discovery' | 'time' | 'crypto' | 'text' | 'data' | 'code' | 'net' | 'system'; | ||
| /** One-line human summary. */ | ||
@@ -16,1 +18,3 @@ summary: string; | ||
| export declare const COMMANDS: readonly CommandInfo[]; | ||
| /** Distinct categories in catalogue order (for grouped rendering). */ | ||
| export declare const CATEGORIES: readonly string[]; |
@@ -14,2 +14,39 @@ import cts from './cts'; | ||
| import port from './port'; | ||
| export { cts, sc, deco, date, epoch, killProcess, update, capabilities, id, hash, encode, random, port, }; | ||
| import jsonCmd from './json'; | ||
| import count from './count'; | ||
| import lines from './lines'; | ||
| import have from './have'; | ||
| import sys from './sys'; | ||
| import repo from './repo'; | ||
| import calc from './calc'; | ||
| import semver from './semver'; | ||
| import caseCmd from './case'; | ||
| import cron from './cron'; | ||
| import diff from './diff'; | ||
| import env from './env'; | ||
| import size from './size'; | ||
| import ext from './ext'; | ||
| import freq from './freq'; | ||
| import regex from './regex'; | ||
| import url from './url'; | ||
| import ip from './ip'; | ||
| import escapeCmd from './escape'; | ||
| import schema from './schema'; | ||
| import outlineCmd from './outline'; | ||
| import imports from './imports'; | ||
| import replace from './replace'; | ||
| import recent from './recent'; | ||
| import pkgCmd from './pkg'; | ||
| import dotenv from './dotenv'; | ||
| import wait from './wait'; | ||
| import ports from './ports'; | ||
| import http from './http'; | ||
| import base from './base'; | ||
| import clip from './clip'; | ||
| import notify from './notify'; | ||
| import openCmd from './open'; | ||
| import procs from './procs'; | ||
| import disk from './disk'; | ||
| import trash from './trash'; | ||
| import dns from './dns'; | ||
| export { cts, sc, deco, date, epoch, killProcess, update, capabilities, id, hash, encode, random, port, jsonCmd, count, lines, have, sys, repo, calc, semver, caseCmd, cron, diff, env, size, ext, freq, regex, url, ip, escapeCmd, schema, outlineCmd, imports, replace, recent, pkgCmd, dotenv, wait, ports, http, base, clip, notify, openCmd, procs, disk, trash, dns, }; |
| export * from './InquirerPrompt'; | ||
| export * from './ProcessInfo'; | ||
| export * from './CliCommand'; |
@@ -8,3 +8,3 @@ export declare function Logger(): { | ||
| export declare function CommandBuilder(instance: any): { | ||
| add: (alias: string, describe: string, builder: object, handler: (argv: any) => any) => void; | ||
| add: (alias: string, describe: string, builder: (yargs: any) => any, handler: (argv: any) => void | Promise<void>) => void; | ||
| }; | ||
@@ -11,0 +11,0 @@ /** |
+112
-43
| # @master4n/master-cli (`mfn`) | ||
| > A headless-friendly developer CLI that replaces boilerplate agents otherwise | ||
| > regenerate on every machine: epoch/date conversions, JWT decoding, freeing | ||
| > ports, finding files, and printing directory trees. Every command is designed | ||
| > to be called by both humans and AI agents. | ||
| > 50 headless, JSON-first commands for AI agents and developers: extract instead | ||
| > of dump (token savers), compute instead of guess (hallucination killers), | ||
| > one-call system/code/network facts, and cross-platform OS actions (clipboard, | ||
| > notifications, trash, processes). Every command behaves identically for a | ||
| > human at a terminal and an agent reading stdout. | ||
| ## Agent contract (read this first) | ||
| - **Zero-install:** `npx -y @master4n/master-cli <command> --json` works without a | ||
| global install; with it installed, the binary is `mfn`. | ||
| - **Discover commands:** `mfn capabilities --json` returns the full manifest | ||
| (`{ name, version, bin, conventions, commands:[{name,summary,examples}] }`). | ||
| (`{ name, version, bin, conventions, docs, categories, commands:[{name,category,summary,examples}] }`). | ||
| - **Machine output:** pass `--json`, OR just pipe the command (when stdout is not | ||
@@ -16,44 +19,110 @@ a TTY the CLI auto-emits JSON). Output is exactly one object on stdout: | ||
| - **Exit codes:** `0` success · `1` runtime error · `2` usage error. | ||
| - **Clean channels:** the welcome banner, spinners, and logs go to **stderr**; | ||
| **stdout carries only the JSON result**, so `mfn <cmd> --json | jq` always works. | ||
| - **No prompts in headless mode:** interactive prompts appear only on a TTY when | ||
| required input is missing; with `--json` or when piped, commands never block. | ||
| - **Strict parsing:** unknown commands, unknown flags, and missing required | ||
| arguments are rejected with `{ "ok": false, "error": "UsageError", ... }` and | ||
| exit `2` — a typo never silently "succeeds". | ||
| - **Clean channels:** banners/spinners/logs go to **stderr**; **stdout carries only | ||
| the JSON result**, so `mfn <cmd> --json | jq` always works. | ||
| - **No prompts in headless mode:** with `--json` or when piped, commands never block. | ||
| - **Strict parsing:** unknown commands/flags and missing args → `{ok:false, error:"UsageError"}`, exit 2. | ||
| - **Fast:** ~60ms per invocation; heavy/TTY-only deps load lazily. | ||
| ## Commands | ||
| ## Token savers (read less, extract exactly) | ||
| - `mfn id [-t uuid|uuid7|nano] [-n count] [--size N] --json` — generate ids. UUID v4 | ||
| (default), time-ordered UUID v7 (RFC 9562), or a URL-safe nano id. Returns `{ type, count, ids }`. | ||
| - `mfn hash [text] [-a md5|sha1|sha256|sha512] [-f file] [-e hex|base64|base64url] --json` | ||
| — hash a string, a file, or piped stdin. Returns `{ algo, encoding, source, bytes, hash }`. | ||
| - `mfn encode [text] [--as base64|base64url|hex|url] [-d] --json` — encode (or `-d` decode) | ||
| text or stdin. Returns `{ operation, codec, input, output }`. | ||
| - `mfn random [-b bytes] [-e hex|base64|base64url] | [-p [-l length]] --json` — secure random | ||
| bytes, or an unbiased password. Returns `{ kind, ..., value }`. | ||
| - `mfn port [--json] | [-c port] | [-n count]` — find free port(s), or check a specific port. | ||
| Returns `{ port }` / `{ count, ports }` / `{ port, available }`. | ||
| - `mfn epoch <value> [--tz] [--format] --json` — epoch → date (unit auto-detected: | ||
| s/ms/µs/ns). `mfn epoch --from <dateString> [--format] [--tz] --json` — date → epoch. | ||
| Fractional/invalid epochs fail with exit 1. | ||
| - `mfn date [from] [--tz] [--format] [--in-format] [--in-tz] --json` — convert/format | ||
| a date across timezones; omit `from` for now. Returns epoch + UTC + target-zone. | ||
| - `mfn decode -t <jwt> --json` — decode a JWT's header and payload (signature NOT verified). | ||
| If the payload has a numeric `exp`, also returns `expiry: { exp, expired, expiresInSeconds }`. | ||
| - `mfn kill -p <port...> [-y] --json` — kill the process(es) listening on the given | ||
| ports. Headless/`--json` (and `-y`) kill all matches without prompting; in headless | ||
| mode you MUST pass `-p` (no cached-port replay). Returns `{ killed, failed, notFound }`. | ||
| - `mfn sc [pattern] [--ignore...] [--depth] [--limit] --json` — fuzzy-find files/folders | ||
| under the current directory. Returns `{ pattern, root, count, truncated, matches }`. | ||
| - `mfn cts [--type text|svg|png|jpeg] [--ignore...] --json` — print the directory tree as | ||
| text (default) or export it to an image. Ignores node_modules/.git/.nx by default. | ||
| - `mfn update [package] --json` — update the CLI (or a named package) globally via npm. | ||
| - `mfn capabilities --json` — self-describing manifest of all of the above. | ||
| - `mfn json [query] [-f file] [--keys] [--length] --json` — one value from JSON by | ||
| dot/bracket path (`scripts.build`, `users[0].name`); stdin or file. Don't read the document. | ||
| - `mfn schema [query] [-f file] --json` — infer JSON shape (dot-paths → types, arrays | ||
| sampled). A 10MB payload becomes ~20 lines. | ||
| - `mfn count [text] [-f file] --json` — lines/words/chars/bytes + `tokensEstimate` | ||
| (~4 chars/token heuristic). Know the cost before reading. | ||
| - `mfn lines <file> [-s start] [-e end | -n count] --json` — exact 1-based line range, | ||
| capped at 2000 lines. Returns `totalLines` too. | ||
| - `mfn outline <file> [-k kind] [-e] --json` — symbols + line numbers for | ||
| .ts/.tsx/.js/.jsx/.py/.go/.md. Pair with `lines` to read only what matters. | ||
| - `mfn diff <a> <b> [-s] --json` — structured hunks `{aStart,aLines,bStart,bLines,removed,added}`; | ||
| `-s` for locations/counts only. | ||
| - `mfn freq [file] [-t top] [-m min] --json` — most repeated lines (log triage in one call). | ||
| - `mfn imports [file] | --who <module> --json` — a file's imports (grouped | ||
| relative/packages/builtin), or every file importing a module. | ||
| - `mfn repo [-n commits] --json` — git branch, staged/unstaged/untracked counts, | ||
| ahead/behind, remotes, recent commits. Replaces 4-5 git calls. | ||
| - `mfn sys --json` — OS, arch, node, CPU, memory, user, shell, timezone, cwd in one object. | ||
| - `mfn have <tools...> --json` — which tools exist, their path + version, in one call. | ||
| - `mfn size [dir] [-t top] --json` / `mfn ext [dir] --json` / `mfn recent [dir] [-t top] --json` | ||
| — disk usage, project composition by extension, newest files. | ||
| - `mfn ports [--min N] --json` — ALL listening TCP ports with pid/command. | ||
| - `mfn ip [-a] --json` — local interfaces/addresses; `primaryIPv4` convenience field. | ||
| - `mfn pkg [name] --json` — declared vs installed dependency versions; `missing` list. | ||
| - `mfn env [names...] [-p prefix] --json` — env vars; secret-looking values are | ||
| ALWAYS redacted; no names → name list only. | ||
| - `mfn dotenv [-f .env] [-e .env.example] --json` — missing/extra keys; values never read. | ||
| ## Exact computation (never guess) | ||
| - `mfn calc <expr> --json` — `+ - * / % ^` with parens; integer math in BigInt | ||
| (`2^53 + 1` → `9007199254740993`, exact). `exact:true|false` flag in output. | ||
| - `mfn base <value> [-f hex|dec|bin|oct] --json` — 0xff/0b1010/0o755/decimal → all bases, BigInt-safe. | ||
| - `mfn semver <versions...> [-b major|minor|patch] [-s] --json` — validate/compare/sort/bump | ||
| per semver.org (prerelease ordering correct). | ||
| - `mfn cron <expr> [-n next] --json` — validate 5-field cron (+ @daily etc.), plain-English | ||
| description, next run times (local + ISO + epochMs). | ||
| - `mfn regex <pattern> [text] [-f file] [--flags imsuvy] --json` — all matches with | ||
| line/index/groups. Test, don't assume. | ||
| - `mfn url <value> --json` — components + decoded query params (repeats → arrays). | ||
| - `mfn escape [text] [-a shell|json|regex|html|url|string] --json` — context-exact escaping. | ||
| - `mfn case [text] [-t camel|snake|kebab|pascal|constant|dot|path|title|sentence|lower|upper] --json` | ||
| — identifier style conversion; omit `-t` for all styles. | ||
| - `mfn epoch <value> --json` / `mfn epoch --from <date> --json` — epoch ↔ date | ||
| (unit auto-detected s/ms/µs/ns). | ||
| - `mfn date [from] [--tz] [--format] --json` — date across timezones (IANA), defaults to now. | ||
| - `mfn decode -t <jwt> --json` — JWT header+payload+expiry (signature NOT verified). | ||
| ## Actions (do, don't script) | ||
| - `mfn replace <search> <replacement> -g <glob> [--write] --json` — literal find/replace | ||
| across files; DRY-RUN by default, `--write` to apply; per-file counts. | ||
| - `mfn wait [-p port | -u url | -f file] [-t seconds] --json` — block until ready; | ||
| replaces sleep-and-retry loops. Exit 1 on timeout. | ||
| - `mfn kill -p <ports...> [-y] --json` — kill listeners on ports (headless: needs explicit -p). | ||
| - `mfn http <url> [-m GET|HEAD] [-H "Name: value"] --json` — status/headers/timing | ||
| + body preview capped at 2KB (never a full dump). Exit 1 on non-2xx. | ||
| - `mfn port [-c port | -n count] --json` — find free port(s) / check one. | ||
| - `mfn id [-t uuid|uuid7|nano] [-n count] --json` — UUID v4, time-ordered v7 (RFC 9562), nano. | ||
| - `mfn hash [text] [-a md5|sha1|sha256|sha512] [-f file] --json` — digest of string/file/stdin. | ||
| - `mfn encode [text] [--as base64|base64url|hex|url] [-d] --json` — encode/decode (strict charset). | ||
| - `mfn random [-b bytes] | [-p [-l length]] --json` — CSPRNG bytes / unbiased password. | ||
| - `mfn sc [pattern] [--depth] [--limit] --json` — fuzzy file find under cwd. | ||
| - `mfn cts [--type text|svg|png|jpeg] --json` — directory tree. | ||
| - `mfn update [package] --json` — global npm update of the CLI (or named package). | ||
| ## OS-level (cross-platform: macOS / Windows / Linux) | ||
| - `mfn clip [text] [-r] --json` — read/write the system clipboard. Write via arg or | ||
| stdin (`git diff | mfn clip`); read returns `{chars, text}`. Headless sessions fail | ||
| cleanly with `ClipboardUnavailable`. | ||
| - `mfn notify <message> [-t title] --json` — desktop notification (osascript / | ||
| notify-send / WinRT toast). Ping the user when a long task finishes. | ||
| - `mfn open <target> --json` — open an http(s) URL or existing file in the default | ||
| app/browser. Non-http schemes and missing paths are rejected (exit 2). | ||
| - `mfn procs [pattern] [-t top] --json` — search processes by name: pid/cpu/memMB, | ||
| sorted by cpu. One call instead of ps|grep (or tasklist parsing). | ||
| - `mfn disk [-a] --json` — per-mount totals/free/used% (df -kP / Win32_LogicalDisk). | ||
| - `mfn trash <paths...> --json` — move to the OS trash (reversible; ~/.Trash, XDG | ||
| spec, Recycle Bin). Refuses root/home/cwd and parents of cwd. Use instead of rm. | ||
| - `mfn dns <hostname> [-t a|aaaa|cname|mx|txt|ns] --json` — system resolver + | ||
| record sweep; missing record types are null, not errors. | ||
| ## Guardrails (always on — there are no bypass flags) | ||
| - File-content commands (`lines` `json` `schema` `diff` `freq` `regex -f`) refuse | ||
| credential/secret paths (`~/.ssh`, `.env*`, `*.pem`, `.npmrc`, …) → `SensitivePath`, exit 2. | ||
| - `clip` read redacts secret-shaped content (keys/JWTs/tokens) → `redacted:true`. | ||
| - `env` redacts by name AND value shape; `dotenv` never reads values at all. | ||
| - `http`/`wait -u` refuse cloud metadata endpoints (169.254.169.254 etc.) → `BlockedTarget`; | ||
| `http` redacts `set-cookie` response headers. | ||
| - `open` allows only http(s)/existing paths; `trash` is reversible and refuses | ||
| root/home/cwd; `kill` validates ports and PIDs; `replace` is dry-run unless `--write`. | ||
| - Full threat model: SECURITY.md (shipped in this package). | ||
| ## Notes | ||
| - Time/date features are powered by `@master4n/temporal-transformer` v2 (Luxon-backed, | ||
| integer epochs; Luxon format tokens, e.g. `yyyy-MM-dd HH:mm:ss`). | ||
| - Zero shell interpolation: process/port/package operations use `execFile` (no shell), | ||
| so inputs cannot inject commands. | ||
| - Time/date powered by `@master4n/temporal-transformer` v2 (Luxon-backed, integer epochs). | ||
| - Zero shell interpolation: every subprocess uses `execFile` (no shell) — inputs cannot | ||
| inject commands. See SECURITY.md (shipped in this package) for the full threat model. | ||
| - `count.tokensEstimate` is a ~4-chars/token heuristic, not a model tokenizer. |
+5
-1
| { | ||
| "name": "@master4n/master-cli", | ||
| "version": "3.0.2", | ||
| "version": "3.0.3", | ||
| "description": "AI-agent-friendly command-line toolkit: timestamp/date conversion, JWT decoding, port killing, file finding, and directory trees — headless, --json, with a self-describing manifest.", | ||
@@ -24,2 +24,6 @@ "type": "module", | ||
| "llm-tools", | ||
| "llms-txt", | ||
| "agent-friendly", | ||
| "claude-code", | ||
| "coding-agent", | ||
| "automation", | ||
@@ -26,0 +30,0 @@ "headless", |
+81
-20
| # @master4n/master-cli (`mfn`) | ||
| [](https://github.com/Master4Novice/master-cli/actions/workflows/ci.yml) | ||
| [](https://github.com/Master4Novice/master-cli/actions/workflows/codeql.yml) | ||
| [](https://snyk.io/test/github/Master4Novice/master-cli) | ||
| [](https://socket.dev/npm/package/@master4n/master-cli) | ||
| [](https://www.npmjs.com/package/@master4n/master-cli) | ||
@@ -9,7 +12,9 @@  | ||
| **Master CLI for developers and AI agents.** A set of headless, JSON-first | ||
| commands that replace the boilerplate agents otherwise regenerate on every | ||
| machine — timestamp/date conversion, JWT decoding, freeing ports, finding files, | ||
| and directory trees. Every command runs the same for a human at a terminal and | ||
| for an agent reading stdout. | ||
| **Master CLI for developers and AI agents.** 43 headless, JSON-first commands in | ||
| three families: **token savers** (extract exactly what you need — one JSON field, | ||
| a line range, a file outline — instead of dumping whole files into context), | ||
| **exact computation** (BigInt math, semver, cron, regex, timezones — verified | ||
| answers instead of guesses), and **one-call actions** (free a port, wait for a | ||
| server, bulk-replace with dry-run). Every command runs the same for a human at a | ||
| terminal and for an agent reading stdout, in ~60ms. | ||
@@ -24,2 +29,15 @@ ## Installation | ||
| ### Zero-install (agents & one-off use) | ||
| No global install needed — `npx` runs any command directly: | ||
| ```sh | ||
| npx -y @master4n/master-cli capabilities --json # discover every command | ||
| npx -y @master4n/master-cli epoch 1622547800 --json | ||
| ``` | ||
| > **For AI agents:** run `mfn capabilities --json` (or the npx form above) to get | ||
| > the machine-readable manifest, and read [`llms.txt`](./llms.txt) — it ships | ||
| > inside the npm package and documents the full agent contract. | ||
| ## The contract (why it's agent-friendly) | ||
@@ -50,22 +68,65 @@ | ||
| ## Commands | ||
| ## Commands (50) | ||
| Run `mfn capabilities` for the grouped list, `mfn <command> --help` for flags. | ||
| ### OS-level — one call on macOS, Windows, and Linux | ||
| | Command | What it does | Example | | ||
| | ------- | ------------ | ------- | | ||
| | `capabilities` | Self-describing manifest of every command | `mfn capabilities --json` | | ||
| | `id` | Generate IDs — UUID v4, time-ordered UUID v7, or URL-safe nano | `mfn id --json` · `mfn id -t uuid7 -n 3 --json` | | ||
| | `hash` | Hash a string, file, or stdin (md5/sha1/sha256/sha512) | `mfn hash hello --json` · `mfn hash -f ./x --json` | | ||
| | `encode` | Encode/decode text — base64, base64url, hex, url | `mfn encode hi --json` · `mfn encode aGk= -d --json` | | ||
| | `random` | Secure random bytes, or an unbiased password | `mfn random --json` · `mfn random -p -l 32 --json` | | ||
| | `port` | Find a free port, or check if one is available | `mfn port --json` · `mfn port -c 3000 --json` | | ||
| | `epoch` | Convert between epoch timestamps and dates (auto-detects s/ms/µs/ns) | `mfn epoch 1622547800 --json` · `mfn epoch --from 2021-06-01T11:43:20Z --json` | | ||
| | `date` | Convert/format a date across timezones (defaults to now) | `mfn date 2024-07-04T15:30:30Z --tz America/New_York --json` | | ||
| | `decode` | Decode a JWT (header + payload + expiry; signature **not** verified) | `mfn decode -t <jwt> --json` | | ||
| | `kill` | Kill the process(es) listening on given ports | `mfn kill -p 3000 8080 -y --json` | | ||
| | `sc` | Fuzzy-find files/folders under the current directory | `mfn sc service --json` | | ||
| | `cts` | Print (or export) a tree of the current directory | `mfn cts --json` · `mfn cts -t png` | | ||
| | `update` | Update the CLI (or a named package) to the latest version | `mfn update --json` | | ||
| | `clip` | Read/write the system clipboard | `git diff \| mfn clip --json` | | ||
| | `notify` | Desktop notification — ping the user when a task finishes | `mfn notify "build finished" --json` | | ||
| | `open` | Open a file/URL in the default app (validated first) | `mfn open coverage/index.html --json` | | ||
| | `procs` | Search processes by name: pid/cpu/mem | `mfn procs node --json` | | ||
| | `disk` | Per-mount disk usage without df parsing | `mfn disk --json` | | ||
| | `trash` | **Reversible** delete to the OS trash — never `rm -rf` | `mfn trash old-logs --json` | | ||
| | `dns` | A/AAAA/CNAME/MX/TXT/NS in one call | `mfn dns github.com --json` | | ||
| Run `mfn <command> --help` for the full flag list and more examples. | ||
| ### Token savers — read less, extract exactly | ||
| | Command | What it does | Example | | ||
| | ------- | ------------ | ------- | | ||
| | `json` | One value from JSON by path — don't read the document | `mfn json scripts.build -f package.json --json` | | ||
| | `schema` | Infer JSON shape (paths + types); 10MB payload → ~20 lines | `mfn schema -f response.json --json` | | ||
| | `count` | Lines/words/chars/bytes + **LLM token estimate** | `git diff \| mfn count --json` | | ||
| | `lines` | Exact line range of a file (1-based), never the whole file | `mfn lines src/app.ts -s 120 -n 30 --json` | | ||
| | `outline` | Symbols + line numbers (.ts .js .py .go .md) | `mfn outline src/app.ts --json` | | ||
| | `diff` | Structured hunks of two files, counts first | `mfn diff old.json new.json -s --json` | | ||
| | `freq` | Most repeated lines — log triage in one call | `mfn freq error.log -t 5 --json` | | ||
| | `imports` | A file's imports, or who imports a module | `mfn imports --who utility --json` | | ||
| | `repo` | Git branch/dirty/ahead-behind/commits in one object | `mfn repo --json` | | ||
| | `sys` / `have` / `ip` | System facts · tool versions · local addresses | `mfn have node git docker --json` | | ||
| | `size` / `ext` / `recent` | Disk usage · composition by extension · newest files | `mfn size -t 5 --json` | | ||
| | `ports` | ALL listening TCP ports with owning processes | `mfn ports --json` | | ||
| | `pkg` | Declared vs installed dependency versions | `mfn pkg --json` | | ||
| | `env` / `dotenv` | Env inspection (secrets **always** redacted) · .env completeness | `mfn dotenv --json` | | ||
| ### Exact computation — never guess | ||
| | Command | What it does | Example | | ||
| | ------- | ------------ | ------- | | ||
| | `calc` | BigInt-exact arithmetic — `2^53 + 1` comes out right | `mfn calc "2^53 + 1" --json` | | ||
| | `base` | hex/dec/bin/oct conversion, BigInt-safe | `mfn base 0xff --json` | | ||
| | `semver` | Validate/compare/sort/bump per semver.org | `mfn semver 1.10.0 1.9.2 --json` | | ||
| | `cron` | Validate + explain + next run times | `mfn cron "*/15 9-17 * * 1-5" --json` | | ||
| | `regex` | Test a pattern — matches with line/index/groups | `mfn regex "TODO" -f src/app.ts --json` | | ||
| | `url` | URL → components + decoded query params | `mfn url "https://x.com/a?b=1" --json` | | ||
| | `escape` | Context-exact escaping: shell, JSON, regex, HTML, URL | `mfn escape "it's" --json` | | ||
| | `case` | camel/snake/kebab/pascal/… conversion | `mfn case getUserName -t snake --json` | | ||
| | `epoch` / `date` | Epoch ↔ date (auto unit) · timezone conversion | `mfn epoch 1622547800 --json` | | ||
| | `decode` | JWT header + payload + expiry (signature not verified) | `mfn decode -t <jwt> --json` | | ||
| ### Actions — do, don't script | ||
| | Command | What it does | Example | | ||
| | ------- | ------------ | ------- | | ||
| | `replace` | Literal find/replace across files — **dry-run by default** | `mfn replace old new -g "src/**/*.ts" --json` | | ||
| | `wait` | Block until port/URL/file is ready — no sleep loops | `mfn wait -p 3000 -t 30 --json` | | ||
| | `kill` | Free the ports your dev server got stuck on | `mfn kill -p 3000 8080 -y --json` | | ||
| | `http` | Probe a URL: status/headers/timing, capped body preview | `mfn http localhost:3000/health --json` | | ||
| | `port` | Find a free port, or check one | `mfn port -c 3000 --json` | | ||
| | `id` / `hash` / `encode` / `random` | UUID v4/v7/nano · digests · codecs · CSPRNG | `mfn id -t uuid7 -n 3 --json` | | ||
| | `sc` / `cts` | Fuzzy file find · directory tree | `mfn sc service --json` | | ||
| | `capabilities` / `update` | Machine-readable manifest · self-update | `mfn capabilities --json` | | ||
| ### Examples | ||
@@ -72,0 +133,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
382583
101.31%77
165.52%1154
157.59%165
58.65%14
55.56%2
Infinity%