Mafdet AI Help Center中文

Mafdet CLI

@mafdet/cli is the official command line: call models, query your account, and run a permissioned local agent that reads and edits code on your own machine. The agent's tools execute locally — the server only returns structured tool calls, and no server-side agent permission is widened.

Requires Node.js 20 or newer.

Install

npm install -g @mafdet/cli

Or run it without installing:

npx @mafdet/cli doctor

The interactive session

Run mafdet with no arguments in a terminal and it opens a session: choose how to authenticate, pick a model, then work with the agent.

mafdet                       # read-only
mafdet --allow-write         # changes are shown as a diff and confirmed first
mafdet --allow-command "pnpm test"

It is the same agent as mafdet agent, drawn differently - the same tools, the same approvals, the same cost accounting. The flags mean exactly what they mean there, and a session given neither has no write or command tool loaded at all.

In a pipeline, under --json, in CI, with TERM=dumb, or with --no-tui, mafdet prints usage to stderr and exits 2, exactly as before. Nothing that reads the CLI's output will start receiving a drawing.

Quickstart

export MAFDET_API_KEY="sk-mafdet-live-..."
export MAFDET_MODEL="deepseek-v4-flash"

mafdet doctor                 # key, endpoints, model, balance, workspace, git
mafdet models list            # what this key can actually call
mafdet chat "Explain TypeScript decorators"
mafdet account balance

mafdet doctor is the first thing to run when something is wrong. It checks Node, both endpoints, the key, the workspace, git, both API planes, the selected model, its tool-calling status and the balance — and only fails on a genuine blocker, so a missing optional scope is a warning rather than an error.

PowerShell

$env:MAFDET_API_KEY = "sk-mafdet-live-..."
$env:MAFDET_MODEL   = "deepseek-v4-flash"

mafdet doctor

Everything the CLI prints is ASCII, so output stays readable on a console running a non-UTF-8 code page (the Windows PowerShell 5.1 default). PowerShell pipes UTF-16 by default, so pass a prompt as an argument or use --input <file> rather than piping it.

Commands

CommandWhat it does
mafdet doctorOne pass over key, endpoints, model, tool-calling, balance, workspace, git
mafdet models list [--surface chat|embeddings|speech] [--agent]Models this key can call, joined with catalogue metadata
mafdet models show <id>One model in detail, including whether the agent may drive it
mafdet chat [prompt] [--model] [--system <file>] [--input <file>] [--save]Send a prompt; streams by default
mafdet agent "<goal>"Local agent; read-only unless told otherwise
mafdet agentInteractive session (needs a terminal)
mafdet embed [text|<file>] --model <id> [--output <file>]Text to vector
mafdet speech [text|<file>] --model <id> --output <file> --voiceText to an audio file
mafdet account balance|usage|subscriptionWallet, spend and plan (needs the account:read scope)
mafdet sessions list|show|usage|deleteConversations saved on this computer (never calls a model)
mafdet sessions usage <id> --refreshShow settled ledger figures alongside the local estimate
mafdet config listThe resolved settings and where each came from

--json emits one complete JSON document; --plain emits tab-separated rows with no header, for cut and awk.

The agent's permission model

Read-only by default: list, search, read files, and git status / git diff. It cannot change anything.

--allow-write adds edit_file and create_file, and then:

  • every change is shown as a diff and confirmed first, with Enter meaning no;
  • a non-interactive run cannot write at all, even with the flag — the write tools are not offered to the model in the first place;
  • there is no flag that approves everything in advance.

Approving a command means allowing that program to run on your machine, as you. The workspace bounds which files the agent's own tools may touch; it is not a sandbox and it does not bound the program. A short list of obviously destructive names is refused (sudo, su, doas, chown, shutdown, reboot, and invoking a program by path), but treat that as a guard against slips rather than as containment.

--allow-command "<prefix>" adds run_command, unprompted for exactly those prefixes: pnpm test permits pnpm test --filter web but never pnpm publish. There is no shell — the command is split into argv, so pipes, redirects, ;, &&, backticks and $( ) are refused with an explanation rather than sanitised.

Files that look like secrets — .env*, private keys, id_rsa*, kubeconfig, credentials*, .npmrc, and all of .ssh, .aws, .gnupg, .docker, .kube — are refused, with no override flag. The workspace boundary is decided on real paths, so a symlink cannot lead outside it.

In an interactive session: /model, /permissions, /diff, /cost, /compact, /exit.

Which models can drive the agent

Only models whose tool calling has been verified end to end. Check the AGENT column:

mafdet models list --agent

A model without it is refused rather than attempted, so you do not pay for a run that breaks halfway. mafdet models show <id> says which case a model is in.

Credentials

The key is read from MAFDET_API_KEY. There is deliberately no --api-key flag — it would put the key into shell history and the process list. Keys are never written to a config file and never printed in full, only as a fingerprint like sk-mafdet-live-<redacted>.

A project may carry a .mafdet.json with model — and nothing else. Credentials and endpoints in a project file are rejected outright: cloning a repository must never change where your key is sent.

For mafdet account ... the key needs the account:read scope (create one in API Keys). Without it the other commands still work.

Exit codes

Scripts may branch on these.

CodeMeaning
0Success
1General failure (including doctor reporting a failed check)
2Bad command line or configuration
3Authentication or permission failure
4Model or API unavailable
5Local permission denied
6Cancelled by the user
7Cost, time, or loop ceiling reached

Results and machine-readable JSON go to stdout; progress, warnings, summaries and approval prompts go to stderr. So mafdet chat ... > answer.txt captures the answer alone, and mafdet models list --json | jq always parses.

Saved conversations

Interactive sessions are saved on this computer, and only on this computer. They are not uploaded, and Mafdet holds no copy of them.

That is a statement about the transcript, not about the model. The messages you send are still sent to the API to be answered and still appear in your usage records; what stays local is the saved copy.

PlatformPath
Linux / WSL${XDG_STATE_HOME:-~/.local/state}/mafdet/sessions
macOS~/Library/Application Support/mafdet/sessions
Windows%LOCALAPPDATA%\mafdet\sessions

The files are plain text. A transcript holds your messages, the model's replies, and any source the agent read while working, so treat a session directory the way you would treat the project it came from. API keys are the exception: everything passes through redaction on the way to disk, so a key pasted into a prompt is stored as <redacted>.

The agent cannot read these directories, even when you run mafdet from your home directory and the store is technically inside the workspace.

mafdet --no-history            # this run is not recorded at all
mafdet chat "..." --save       # a one-shot chat is saved only when asked
mafdet sessions list           # what is saved, and the path when nothing is
mafdet sessions delete <id> --yes

A single mafdet chat is not a conversation anyone resumes, so it is not recorded unless --save is given. --no-history still wins over --save, and a chat cancelled with Ctrl+C is not saved at all.

Deleting removes the local messages and that session's usage mapping. It does not remove API usage, billing records or charges — those belong to the API key, not to the transcript. It is also an ordinary file delete rather than a secure erase: backups, snapshots and the filesystem itself may still hold copies.

Reconciling what a session cost

mafdet sessions usage <id> totals a conversation from the local record. Those figures are estimates, computed with the gateway's own arithmetic but computed on your machine.

--refresh asks the billing ledger for settled figures. It pages through the current API key's usage events for the session's time window; request IDs stay local and are used only to match returned rows. No messages, title, workspace or session ID are sent. Requests paid for by a different API key are left alone and reported, so you know to run it again with that key.

A request the ledger has not settled stays pending, and a request that never returned an ID stays unknown. Neither is shown as 0, which would read as "it was free".

Cost

chat prints tokens and an estimated charge to stderr, computed with the same arithmetic the gateway bills with. The wallet remains the authority — see Usage & billing.

Cancelling with Ctrl+C closes the connection. In testing against production a cancelled stream produced no charge, but that is an observation, not a guarantee: if the upstream has already finished generating, a charge can still land. The summary after a cancel is labelled partial: for that reason.

Known limitations

These are stated rather than left to be discovered:

  • macOS and Windows are not verified. Linux, WSL, tmux, SSH and SSH over tmux were checked on real terminals, on Node 20 and 22.
  • On Windows, the command timeout is written but unverified. It uses taskkill /T where POSIX signals the process group; the shape of that call is tested, but it has never run on a real Windows machine, so whether every grandchild is reached there is unknown. Affects --allow-command only.
  • /compact truncates the conversation rather than summarising it. A long run also compacts itself near the context window, dropping the oldest tool exchanges from what is sent — your saved transcript keeps them.
  • The compatibility promise for --json shapes and exit codes becomes binding at 1.0.0; being 0.x, semver still permits a break in a minor version.

Next steps