| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Filesystem-routed commands for local projects and user-global tools.
@async/cli turns directories into a CLI. A command like:
cli gh pull 123 --rebaseis routed to a directory, and the words that did not match become the script's arguments:
.cli/gh/pull/script.ts argv: ["123", "--rebase"]
No registry, no config file, no argument framework. One lookup rule covers two trees: a repo-local .cli/ overlay that travels with the project, and a user-global ~/.cli for your personal tools. Proven commands get promoted from one to the other with a single command.
Docs map: this README is the guided tour. ROUTING.md is the normative routing rules. API_SURFACE.md is the complete API reference. SPEC.md is the design contract.
pnpm add -D @async/cli # per project
pnpm add -g @async/cli # or globallyRequires Node 24+. The package ships two identical binaries, cli and async-cli, for setups where cli is taken.
# scaffold .cli/gh/pull/script.ts in this repo
cli --new gh pull
# it is immediately runnable (scaffolded overlays are auto-trusted)
cli gh pull 123 --rebase # script.ts gets ["123", "--rebase"]
# see what exists, and what would run
cli --list
cli --which gh pull
# promote it to your personal ~/.cli once it earns it
cli --mv gh pull --to rootCloned a repo that ships its own .cli/? Commands are visible immediately but refuse to run until you approve them once:
cli --list # inspection never needs trust
cli --trust # approve this repo's overlay, then run normallyThe full rules with worked examples live in ROUTING.md. The short version:
Rule 2 is what makes overlays powerful: a repo can define .cli/gh/script.ts and take over the whole gh ... namespace while you work in that repo, even though your ~/.cli/gh/clone exists. That is deliberate — and it is exactly why local overlays are trust-gated. cli --list marks anything shadowed, cli --which shows what was hidden.
A command script is a plain Node ESM program — no SDK, no wrapper:
// cli: Open a pull request against main
// cli-cwd: project-root
const [id, ...rest] = process.argv.slice(2);
console.log(`pulling ${id} in ${process.cwd()}`);| Contract | Detail |
|---|---|
| Arguments | process.argv.slice(2), exactly as typed after the command |
| Description | First line // cli: ... shows in --list, help, MCP |
| Working dir | Caller's cwd; // cli-cwd: project-root or script-dir to change |
| Stdio / exit | Inherited; your exit code is the command's exit code |
| Languages | .js/.mjs run directly; .ts/.mts via Node 24 type stripping |
| Helpers | Put shared code in lib/ or _anything/ — never routed |
| Environment | CLI_SCRIPT, CLI_ROOT, CLI_SCOPE, CLI_PROJECT_ROOT, CLI_COMMAND, CLI_CALLER_CWD |
Templates: keep reusable starting points in _templates/<name>/ in any command tree and scaffold from them with cli --new api users --template worker.
cli --new gh pr # scaffold (nearest local overlay, or --root)
cli --edit gh pr # open the script in $VISUAL / $EDITOR
cli --rm gh pr # delete (nested commands require --force)
cli --cp gh pr --to root # copy local -> user-global
cli --mv gh pr --to local # move user-global -> this repoTransfers move whole command directories, preserve the command path, refuse to overwrite, and warn when a script imports through ../ (such imports may break outside their original tree).
Local overlays are arbitrary code that arrives with a git clone, and shadowing means they can capture commands you type from muscle memory. So execution is gated, direnv-style:
cli --trust # approve the overlays discovered from here
cli --trust --status # trusted | changed | untrusted, per overlay
cli --untrust # revokeTrust records a content hash of the whole overlay; any change flips it to changed and blocks execution (exit 3) until you re-approve. Your own actions count as consent: --new, --cp/--mv --to local, and --add --to local keep the target overlay trusted without ceremony. ~/.cli is always trusted. Set ASYNC_CLI_TRUST=off to disable the gate in controlled environments (such as CI).
Any Git repository with a .cli/ tree is a command pack:
cli --add https://github.com/org/pack.git # into ~/.cli
cli --add https://github.com/org/pack.git --prefix vendor # namespaced: cli vendor <cmd>
cli --add https://github.com/org/pack.git --to local # into this repoInstalls are all-or-nothing: conflicts are listed and nothing is written unless you pass --force.
The same command tree is discoverable by machines:
cli --agents --write # pin a pointer block into AGENTS.md (--claude for CLAUDE.md)
cli --list --json # stable inventory: commands, descriptions, scripts, shadows
cli --mcp # MCP stdio server: commands become callable tools--mcp needs zero dependencies and exposes each non-shadowed command as a tool (gh pull becomes gh__pull) taking { "args": [...] }. Untrusted overlays are excluded and local trust is rechecked before execution.
Starting MCP mode authorizes the connected stdio client to invoke every listed command and pass arguments. Keep that stream local to a trusted MCP host, and apply any narrower tool approval policy in the host. See SECURITY.md for the complete boundary.
eval "$(cli --completions bash)" # bash
eval "$(cli --completions zsh)" # zsh
cli --completions fish | source # fishTab-completion covers command segments (shadow-aware) and built-in flags.
cli --doctor # human report, exit 1 on errors
cli --doctor --json # { problems, summary } for toolingFinds ambiguous script directories, ../ imports that break transfers, empty command directories, untrusted or drifted overlays, stale --agents blocks, missing descriptions, and shadowed commands.
| Command | Does |
|---|---|
| cli <words...> [args...] | Route and run a command |
| cli help [prefix] | Usage, or commands below a prefix |
| cli --list [--json] | Inventory, including shadowed entries |
| cli --which <words...> | Selected script and what it shadows |
| cli --new <words...> [--root] [--template <name>] | Scaffold a command |
| cli --edit <words...> | Open the script in $VISUAL/$EDITOR |
| cli --rm <words...> [--root] [--force] | Remove a command directory |
| cli --cp <words...> [--to root|local] | Copy between trees |
| cli --mv <words...> [--to root|local] | Move between trees |
| cli --add <git-url> [--to root|local] [--prefix <name>] [--force] | Install a command pack |
| cli --trust [--status] / cli --untrust | Manage overlay trust |
| cli --doctor [--json] | Audit the command trees |
| cli --completions <bash|zsh|fish> | Emit shell completions |
| cli --mcp | Serve commands over MCP stdio |
| cli --agents [--write|--check] [--claude] | Manage the context pointer block |
| cli --version | Print the package version |
Exit codes: the script's own for command runs; 2 for usage and routing errors; 3 for trust refusals; 1 for --doctor with errors and --agents --check drift.
pnpm run build
pnpm test
pnpm run pack:check
pnpm run release:checkThis repo's CI, Pages, previews, and release lifecycle are generated by @async/pipeline from pipeline.ts. Do not hand-edit .github/workflows/async-pipeline.yml or the locks under .locks/pipeline/. Edit pipeline.ts, then regenerate and check:
pnpm run pipeline:sync:generate
pnpm run pipeline:sync:check
pnpm run pipeline:github:checkCommon pipeline commands:
pnpm run pipeline:verify # local run of the verify job
pnpm run pipeline:pages # build the docs site task
pnpm run release:check # forced full verify used before releasesReleases ride the pipeline update train: when @async/pipeline publishes a release, it dispatches an async-dep-bump event to this repo, and the generated dependency-bump job bumps the pinned dependency, regenerates the synced surfaces, verifies with release:check, and lands on main when green or opens a pull request when not.
Maintainers who want the shell to prefer this checkout over an npm-installed copy can link the local binaries:
pnpm run local:link
pnpm run local:status
pnpm run local:unlink| Back | FazBrowse Home | New Git URL |