crewrota architecture: the source map
crewrota is a Node/TypeScript CLI with no CLI framework: src/cli.ts parses the arguments by hand and calls one module per area.
Every call to Claude Code and to quota-axi goes through src/claude.ts, which is what lets the tests swap both for fakes.
The npm package ships dist/ (without the tests), templates/, widget/ and NOTICE; the Claude Code plugin is installed from the GitHub repo, not from npm.
The repo
Section titled “The repo”src/ the CLI, in TypeScript; built to dist/src/test/ node:test tests; fixtures/ holds the fake claude and the fake quota-axiplugin/ the Claude Code plugin: .claude-plugin/plugin.json, skills/firstmate/, hooks/.claude-plugin/ marketplace.json: one plugin, crewrota, from ./plugintemplates/dot-claude/ the root .claude/ that crew init writeswidget/ crewrota-widget.pyw (the quota widget, Python with Tk) and crewrota.1m.sh (the SwiftBar and xbar plugin)scripts/ install.ps1 and install.sh, check-private.mjs, hooks/pre-pushdocs/ these docs, as plain Markdown.github/workflows/ ci.ymlThe modules
Section titled “The modules”| Module | What it does |
|---|---|
src/cli.ts |
Argument parsing and every command; the help text; the exit code |
src/members.ts |
The creation workflow (crew add), use, rename, drop |
src/menu.ts |
The one-screen crew menu (@clack/core), the crew quota and menubar lines, and the @clack/prompts questions |
src/launch.ts |
Builds the Claude argument list, repairs links, starts Claude in the member folder |
src/ships.ts |
Ships: create, join, leave, rename, import, the ship’s mcp.json |
src/backup.ts |
crew backup and crew restore: one zip through bsdtar, the project folder renames, the tar runner |
src/context.ts |
What a backup carries beyond the ships: finds project .claude folders and repos from transcripts, copies with links resolved, the key check, folder mapping, clone |
src/progress.ts |
The one-line progress bar on stderr, shown only on a terminal and not when CI=true |
src/shared.ts |
Shared items from the main ~/.claude: links, copies, plugin lists, mcpServers |
src/links.ts |
Link helpers (junctions on Windows), folder moves to removed/, session file counts |
src/rows.ts |
Member rows for the menu and crew list; reads quota.json |
src/statusline.ts |
crew statusline: writes quota.json and prints the line; gives a member the status line at launch |
src/shortcuts.ts |
crew shortcut: VS Code workspace files, the Start Menu .lnk, the VS Code and shell lookup |
src/widget.ts |
crew widget: starts the .pyw with Python, closes it through widget.close, the Startup shortcut |
src/say.ts |
The site palette (24-bit colour, named-colour fallback, plain when piped) and the [i], [!] and [OK] tags |
src/identity.ts |
Email and organisation from a member’s .claude.json |
src/claude.ts |
The only place that runs claude and quota-axi |
src/doctor.ts |
crew doctor: the setup checks with per-OS fix lines |
src/setup.ts |
crew setup: the default tools, one list, one yes, an injectable runner; the doctor’s tool lines |
src/init.ts |
crew init: copies templates/dot-claude/ without overwriting |
src/config.ts |
config.json: load, save, the remembered model and effort |
src/paths.ts |
Config, crew home, member, ship and main-setup paths |
src/names.ts |
The name rule for members, ships and shortcuts |
Only claude.ts runs Claude Code and quota-axi
Section titled “Only claude.ts runs Claude Code and quota-axi”src/claude.ts holds every call:
claude --version(with acmd.exefallback for an npm shim on Windows),claude auth status --json,claude auth login,claude auth logout, plainclaudefor a first login, and the session launch;- the warm-up print run (
probeRateLimits), the one place a message is sent; - quota-axi, started with Node from
node_moduleswith--provider claude --json, never through npx.
Each sets CLAUDE_CONFIG_DIR to the member folder.
CREWROTA_CLAUDE and CREWROTA_QUOTA_AXI replace the two programs, which is how the tests and the demo crews run without a real account.
Keep it this way: a new call to Claude Code goes into claude.ts, never a spawn('claude', ...) elsewhere.
How a command flows
Section titled “How a command flows”main()incli.tspicks the command from the first argument. A first argument that is a member’s name meanscrew run <member>.- The command parses its own flags with
parseArgs: value flags take the next argument, any other-xis an unknown option, and--ends crewrota’s arguments. - It loads
config.json, does its work through the modules, saves the config, and returns an exit code. - A thrown
Errorbecomescrewrota: <message>on stderr and exit code 1. - Commands that would ask a question call
needTerminalfirst, which fails when stdin and stdout are not both a terminal.
Dependencies
Section titled “Dependencies”| Package | Version | Used for |
|---|---|---|
@clack/core |
1.5.1, pinned | The one-screen menu |
@clack/prompts |
^1.8.1 | Questions, notes, the hidden password prompt |
quota-axi |
0.1.68, pinned | Reading each member’s usage |
Development only: typescript ^7.0.2 and @types/node ^22.20.5.