npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fnmoa/cli

v0.9.18

Published

moa — talk to a workspace from a terminal.

Readme

@fnmoa/cli

moa is the command line for fnMoa: a workspace that runs coding agents (Claude Code, Codex and others) as a team on your GitHub repository. It triages issues, writes the change, reviews it, runs the checks, and merges.

You use the CLI to:

  • install a fnMoa workspace on this machine, a Vercel Sandbox or Cloudflare: moa init writes a folder you can read and fill, and moa deploy deploys it;
  • look after it: moa deploy --dry-run (the plan), moa status, moa down;
  • work with it: give agents work, follow runs, talk to the Manager, ship an issue to a merged pull request.
npx @fnmoa/cli init machine    # or: npm install -g @fnmoa/cli, then `moa init machine`

Node 22 or newer. fnMoa is proprietary software and needs a licence, which is free for one workspace. See Licence.


Quick start

  1. Make an account. Go to https://fnmoa.locusgraph.com, sign in with GitHub, choose Start free on Plan and billing, and copy your key from Account → Licence key.

  2. Write the folder.

    moa init machine            # or: moa init cloudflare --domain example.com, moa init vercel
    cd fnmoa-machine

    | Target | Folder | Needs | |---|---|---| | machine | compose.yaml, .env | Docker with Compose. The workspace on 127.0.0.1:8787, the console on :3100. | | cloudflare | wrangler.jsonc, worker.js, Dockerfile, secrets.env | npx wrangler@4 login, your domain on Cloudflare, Workers Paid, Docker. | | vercel | sandbox.jsonc, secrets.env | vercel login (or VERCEL_TOKEN), Docker the first time. |

    Every folder also has a README.md with each step, by moa and by hand, a .gitignore that leaves the secrets out, and moa.json.

  3. Fill the secrets on the web. Sign in at https://fnmoa.locusgraph.com/account/secrets and fill each token there: GH_TOKEN, an agent key, the console sign-in, and the rest, each with a note on how to get it. They are sealed in your browser with a passphrase and kept in your account's vault; moa deploy pulls them in (the passphrase is asked once per machine).

    moa init asks you to approve a sign-in in the browser and writes this folder's licence (MOA_LICENCE) itself; --no-browser skips that, and FNMOA_LICENCE_KEY does the same job. The folder (secrets.env, .env on a machine, written 0600) keeps only what belongs to it: the owner token, the install id, the console's cookie secret, the licence, and a claude or codex login. Who may sign in to the console is MOA_LOGINS in the vault: GitHub usernames, comma separated, every one an owner; they sign in with GitHub through fnmoa.locusgraph.com. A folder from 0.7.x moves its secrets to the vault once with moa secrets import.

  4. Plan, then deploy.

    moa deploy --dry-run    # what would be made (+), changed (~) or kept (=), and what is missing (!)
    moa deploy              # makes it, deploys it, and logs `moa` in to it

    moa deploy records what it made in .moa/state.json, so running it again only changes what changed. On Cloudflare it makes the D1 database and R2 bucket and writes their ids into wrangler.jsonc.

codex on a ChatGPT plan

Run codex login on your machine, then: on Cloudflare and Vercel, copy ~/.codex/auth.json to codex/auth.json in the folder (moa deploy puts it in place once); on a machine, put it in .env as MOA_CODEX_AUTH (the README has the one-liner).


Looking after it

moa status [dir]                 # the workspace, the folder's owner token, the console
moa deploy [dir] --dry-run       # what a deploy would change; changes nothing
moa deploy [dir]                 # apply the folder again
moa init <target> . --update     # move the folder to this CLI's release; secrets kept
moa stop [dir]                   # stop what the folder deployed; every piece of data kept
moa start [dir]                  # start it again on the same data, and wait for the workspace
moa down [dir]                   # delete what moa deploy made, and nothing else; asks [--yes]

down deletes only what .moa/state.json says moa deploy made, by id. A database or bucket that was there before is left alone.

stop ends the compute and keeps the data: on Cloudflare the container is destroyed and stays down (the workspace answers 503, the console a page saying so) while D1, R2 and the Worker stay; on a machine it is docker compose stop, container and volumes kept; on Vercel the servers end and the persistent sandbox stops, its disk kept. start brings it back and prints the URLs; on Cloudflare the cold start takes a minute or two. moa status says "stopped" for a stopped Cloudflare deploy. In a deploy folder moa stop means the folder; with a job id it stops that job.

Logs are the platform's own: docker compose logs -f on a machine, npx wrangler@4 tail fnmoa-workspace on Cloudflare. A Cloudflare container sleeps after its idle hours, and a Vercel sandbox stops at the end of its session; the next request, or moa deploy, starts it again — a moa stop stays until moa start.

To update fnMoa, update the CLI, then:

moa init <target> . --update && moa deploy

Using a workspace

Log in to one

moa deploy logs you in to the workspace it deploys. To use one someone else runs:

moa login https://workspace.example.com --token <token>   # a token the owner gave you
moa login https://workspace.example.com --github          # sign in with GitHub
moa whoami                                                # which workspace, which tenant
moa use <profile>                                         # switch between saved workspaces

--github is for someone the owner has added on Console access. It prints a GitHub device code, waits while you approve it, and saves a token of your own, with your role and your daily spending cap. Removing you on Console access stops it at once.

Check it is ready

moa doctor
  ok  runs agents   sqlite, v0.4.0
  ok  agent CLIs    claude, codex
  NO  repository    none configured — pipelines need MOA_REPO
  NO  github login  gh has no token there — run `gh auth login` where the workspace runs

Exit 0 when it is ready, 2 when it is not.

Ship an issue

moa ship 41                        # issue #41 → pull request → reviewed → merged

Declare a team and give it work

moa agent create planner  --cli claude --role "You scope the work. You never write code." --peers builder
moa agent create builder  --cli claude --role "You write the code and its tests."         --peers reviewer
moa agent create reviewer --cli codex  --role "You read diffs and name defects."

moa run builder "fix the retry bug in src/http.ts" --watch
moa team work planner,builder,reviewer --watch
moa ask planner builder "which retry flag?"        # and wait for the answer
moa delegate planner builder "implement the retry fix"

--peers is who an agent may open a thread with. Replying inside a thread it is already part of needs no permission.

Follow what is happening

moa watch                  # every job in the workspace, as it changes
moa jobs                   # what is running now
moa runs                   # what ran, newest first [--failed] [--state running]
moa logs <runId> --watch   # every line a run produced, live
moa spend --since 24h      # what it cost, and what asked for it
moa inbox                  # who is waiting on an answer
moa delegations --blocked  # what is stuck, and the command to unstick it

Ctrl-C stops the work the command started and waits for it to stop. Press it twice to leave the work running; moa jobs finds it again.

Talk to the Manager

The Manager is the agent that plans and starts work for you, the same one as on the console's chat page.

moa manager say "start #41 with the core team"   # and wait for its answer
moa manager proposals              # what it proposes, each with its id
moa manager approve <id>           # say yes, and run it now
moa manager dismiss <id>           # say no
moa manager watch                  # what is running, and how it is going

say waits up to --timeout <s> (default 600). Past that it exits 2, and the Manager's answer is in the chat when it has one.

Choose where it may work

The Manager and the Lead touch only the allowed repositories, and a pipeline starts only on one. None allowed is none. Changing the list is an owner's.

moa repos allowed                  # the repositories it may touch
moa repos allow acme/web           # one the GitHub login here can push to
moa repos disallow acme/web        # take it back; the Manager stops watching it

Connect Envoy

Owners only. moa pair makes a one-time code and prints the line to send in Envoy; it works once, for ten minutes.

moa pair                                   # the /specialist add … line for Envoy
moa pair --repo acme/web,acme/api --cap 20 # only these repos, at most $20
moa pair list                              # who is paired, and the opt-in
moa pair same-owner on --owner nasim       # your yes in Envoy counts here
moa pair same-owner off                    # every yes is given in fnMoa

With no --repo, the agent's tasks may act on every repository the Manager watches. --owner is the part after @ in your agent's name (nasim for envoy@nasim). Without it, same-owner on takes a paired agent's owner, or the workspace's own. The name an agent signs with is matched without regard to case.


For scripts

Exit codes

  0  it did what it was asked
  1  it ran and failed — a red build, a CLI that would not start
  2  a rule stopped it — a budget, a policy, a bounced message, a timeout
  3  the command was typed wrong, or the token was refused

JSON

--json writes the answer to stdout and nothing else. Warnings go to stderr.

moa agent list --json | jq -r '.agents[] | "\(.name)\t\(.cli)\t\(.status)"'
moa run builder "say ok" --json | jq '{result: .result.result, cost: .result.costUsd}'
moa jobs --json | jq -r '.jobs[] | select(.state == "running") | .kind'
moa ship 41 --json | jq -r '.result.outcome'
moa doctor --json | jq -e '[.checks[] | select(.ok == false)] | length == 0'

The shapes those depend on are pinned by a test, so a script built on them does not break quietly:

  agent list     .agents[].name .agents[].cli .agents[].status .agents[].role
  inbox          .waiting[].to .waiting[].unread .waiting[].oldest
                 .waiting[].from
  run            .result.runId .result.result .result.isError .result.costUsd
  ask            .result.answered .result.answer .result.chain
  runs           .runs[].id .runs[].cause .runs[].costUsd
  delegations    .delegations[].from .delegations[].to .delegations[].task
                 .delegations[].blockedBy.body
  seats          .seats[].cli .seats[].used .seats[].hasCredential
                 .seats[].installed
  spend          .costUsd .byAgent[].agent .byCause[].cause
  jobs           .jobs[].kind .jobs[].state .jobs[].subject .jobs[].agent
                 .jobs[].progress.step
  doctor         .tenant .checks[].name .checks[].ok .checks[].detail
  ship           .result.outcome .result.reason .result.steps .result.costUsd

Exit codes still apply with --json: a bounced moa send exits 2 and prints the bounce.

Common flags

  --watch            print what the agent does, as it does it
  --detach           start the work and return at once
  --json             machine-readable output only
  --profile <name>   a saved workspace other than the current one
  --timeout <s>      stop waiting; the work carries on

Tab completion: eval "$(moa completion zsh)" (or bash) in your shell's rc file.


Where it keeps things

  ~/.moa/config.json           saved workspaces and their tokens (mode 0600)
  <folder>/secrets.env, .env   the deploy folder's secrets (mode 0600, gitignored)
  <folder>/.moa/state.json     what moa deploy made there, for moa down
  MOA_HOME                     use another folder than ~/.moa
  MOA_URL, MOA_TOKEN           talk to this workspace, ignoring the saved ones (CI)

Licence

fnMoa is proprietary: Copyright (c) 2026 fnLog0, all rights reserved. See LICENSE in this package. Using it needs a licence from https://fnmoa.locusgraph.com:

  • Free: one workspace on This machine.
  • Team and Business: more workspaces, on Vercel and Cloudflare too. Coming soon.

The workspace checks its licence once a day. If the plan lapses, there is a grace period. After that the workspace is read-only until the plan is paid again, and no data is deleted.

Versions 0.1.0 to 0.3.0 of this package are an older, unrelated CLI and are deprecated.