diff --git a/README.md b/README.md index 0157107d..4c3eb49f 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ moshcode install codex # npm i -g @openai/codex moshcode install kimi # curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash moshcode install qwen # npm i -g @qwen-code/qwen-code moshcode install deepseek # npm i -g @serjm/deepseek-code +moshcode install mimocode # curl -fsSL https://mimo.xiaomi.com/install | bash (binary: mimo) moshcode install openagents # curl -fsSL https://openagents.org/install.sh | bash ``` @@ -114,6 +115,7 @@ moshcode agents gemini # gemini --approval-mode=yolo moshcode agents kimi # kimi --yolo (autonomous) moshcode agents qwen # qwen --approval-mode=yolo (autonomous) moshcode agents deepseek # deepseek-code --turbo (autonomous) +moshcode agents mimocode # mimo --dangerously-skip-permissions --trust (autonomous) moshcode agents aider # aider --yes-always (autonomous) moshcode agents openagents # openagents (dashboard) ``` @@ -457,8 +459,8 @@ of the traffic and a tenth of the price; folding them into `in` makes a $3 session look like a $60 one. Attribution is engine + directory + "started before this run did", so a session that shares a directory with another agent can pick up its neighbour's work — `--json` carries the run list when you need to check. -gemini, kimi, qwen, deepseek and openagents keep no readable usage log, so they -report no cost rather than zero cost. +gemini, kimi, qwen, deepseek, mimocode and openagents keep no readable usage +log, so they report no cost rather than zero cost. ### Driving it from a script or another agent diff --git a/src/cli-schema.mjs b/src/cli-schema.mjs index b284075a..9a412fa4 100644 --- a/src/cli-schema.mjs +++ b/src/cli-schema.mjs @@ -196,7 +196,7 @@ export const CORE_CLI_COMMANDS = [ + "codex's rollout token counts, opencode's per-message cost, aider's chat history. a figure marked " + "`~` was worked out from published rates and is what the tokens WOULD cost on the api; unmarked " + "figures are the engine's own arithmetic. models with no rate show tokens and no cost — add yours " - + "to ~/.moshcode/pricing.json. gemini, kimi, deepseek and openagents log nothing readable, " + + "to ~/.moshcode/pricing.json. gemini, kimi, deepseek, mimocode and openagents log nothing readable, " + "so they report no cost rather than zero. under the table, `burn` is the same windows for every " + "engine — last 1 min, 15 min, 1 hour, 4 hours, 8 hours and the report window — with what the " + "requests inside each cost, that cost per hour, and the runs behind it; --json carries them as `burn`.", diff --git a/src/cost.mjs b/src/cost.mjs index 92f581f3..d443c103 100644 --- a/src/cost.mjs +++ b/src/cost.mjs @@ -740,7 +740,7 @@ export const COST_READERS = { }; /** Engines moshcode can launch but cannot cost — named so the report can say so. */ -export const UNCOSTED_ENGINES = ["gemini", "kimi", "deepseek", "openagents"]; +export const UNCOSTED_ENGINES = ["gemini", "kimi", "deepseek", "mimocode", "openagents"]; /** * Finish a run: price it, and record where the price came from. diff --git a/src/engines.mjs b/src/engines.mjs index 9b41c09e..9c0d3d4c 100644 --- a/src/engines.mjs +++ b/src/engines.mjs @@ -262,6 +262,58 @@ export const ENGINES = { agentArgs: ["--turbo"], install: { cmd: "npm", args: ["install", "-g", "@serjm/deepseek-code"] }, }, + mimocode: { + desc: "MiMo Code — Xiaomi's agentic coding CLI (binary: mimo)", + // The installer names the binary `mimo`, not `mimocode`: the product is + // MiMo Code, the model family is MiMo, and the shell command is the short + // one. Its aliases below accept every spelling. + bin: "mimo", + // An opencode derivative (same `run`/`serve`/`acp`/`mcp` verbs, `--continue`, + // sessions, `--pure`) with a Claude-shaped permissions layer bolted on: + // `--dangerously-skip-permissions` (alias `--yolo`) auto-approves every + // tool call the config does not explicitly deny. `--never-ask` is the step + // past that — it also answers the agent's own questions — which is more + // than /agents means for every other engine here, so it stays off. + // + // `--trust` skips the workspace trust check. Without it a fresh directory + // opens on "Is this a project you created or one you trust?" and an + // autonomous session is not autonomous until someone presses Enter. The + // bypass flag then puts up a second dialog of its own on EVERY launch + // ("WARNING: Bypass Permissions mode", default "No, exit") that no flag + // suppresses; `boot` below answers it for swarms, and a human at + // `/agents mimocode` answers it once by hand. + agentArgs: ["--dangerously-skip-permissions", "--trust"], + // No agentsView: `mimo agent` is a management subcommand (create/list) that + // prints and exits, not a live view to land on. + install: { cmd: "bash", args: ["-c", "curl -fsSL https://mimo.xiaomi.com/install | bash"] }, + // Its own updater knows it was installed by the curl script ("Using method: + // curl") and swaps the binary in place; on the current version it says so + // and exits 0 rather than re-downloading. Verified with 0.1.14. + upgrade: { cmd: "mimo", args: ["upgrade"] }, + // The installer drops the binary in ~/.mimocode/bin and only appends that + // to a shell rc, so PATH will not see `mimo` until the next shell — + // including in the moshcode session that just installed it. + binDirs: [path.join(homedir(), ".mimocode", "bin")], + resume: ["--continue"], + // Dialogs the engine puts up BEFORE any work (PRD 0015), and the keys that + // answer them. Both observed on 0.1.14 under a pty. The trust check defaults + // to "Yes, I trust this folder", so Enter alone takes it — the opposite of + // Claude Code's, where Enter exits. The bypass warning defaults to "No, + // exit (recommended)", so it needs Down first. + boot: [ + { pattern: /\bIs this a project you created or one you trust\b/i, keys: ["Enter"], label: "trust this folder" }, + { pattern: /\bYes, I accept the risks and want to skip permissions\b/i, keys: ["Down", "Enter"], label: "accept bypass permissions" }, + ], + state: { + // opencode lineage, so opencode's permission wording — plus the two boot + // dialogs above, which wait on a human exactly as a permission prompt does. + blocked: [ + /\bpermission (?:request|required)\b/i, /\ballow this (?:command|tool)\b/i, + /\bIs this a project you created or one you trust\b/i, + /\bWARNING: Bypass Permissions mode\b/i, + ], + }, + }, openagents: { desc: "OpenAgents — multi-agent launcher + dashboard (openagents.org)", // The package installs three identical bins — `agn`, `openagents`, and @@ -326,6 +378,9 @@ export const ENGINE_ALIASES = { // `dsc` and `deepseek-code` are the binary names the package installs; accept // both as engine names so whichever one someone has seen resolves. ds: "deepseek", dsc: "deepseek", "deepseek-code": "deepseek", + // `mimo` is the binary the installer writes; the rest are how the product is + // spelled on mimo.xiaomi.com and in its own docs ("MiMo Code"). + mimo: "mimocode", "mimo-code": "mimocode", xiaomi: "mimocode", // Same idea for openagents: `agn` and `agent-connector` are the other two // binary names its package installs, and the domain is how it's advertised. oa: "openagents", agn: "openagents", "agent-connector": "openagents", "openagents.org": "openagents", @@ -445,6 +500,7 @@ const AI_EXEC = { kimi: (p) => ["-p", p], // kimi prompt mode (prints the response, text by default) qwen: (p) => ["-p", p], // qwen prompt mode (gemini-derived) deepseek: (p) => ["--headless", "-p", p], // deepseek: -p runs one prompt and exits; --headless drops the TUI so stdout is pipe-clean + mimocode: (p) => ["run", p], // mimocode one-shot (opencode-derived: `mimo run `) // openagents is absent on purpose: it answers no prompts itself, it starts // the engines that do. ai() throws a named error rather than picking it. }; @@ -468,7 +524,7 @@ export function aiExecArgs(engine, prompt) { */ export function pickAiEngine(preferred) { const wanted = preferred ? resolveEngine(preferred)?.[0] : null; - const order = preferred ? (wanted ? [wanted] : []) : ["claude", "codex", "opencode", "privacycode", "gemini", "kimi", "qwen", "deepseek", "aider"]; + const order = preferred ? (wanted ? [wanted] : []) : ["claude", "codex", "opencode", "privacycode", "gemini", "kimi", "qwen", "deepseek", "mimocode", "aider"]; for (const key of order) { if (Object.hasOwn(ENGINES, key) && Object.hasOwn(AI_EXEC, key) && isInstalled(ENGINES[key].bin, ENGINES[key].binDirs)) return key; } diff --git a/test/engines.test.mjs b/test/engines.test.mjs index ee51b842..daf32a67 100644 --- a/test/engines.test.mjs +++ b/test/engines.test.mjs @@ -27,6 +27,7 @@ const EXPECTED_AGENT_ARGS = { kimi: ["--yolo"], qwen: ["--approval-mode=yolo"], deepseek: ["--turbo"], + mimocode: ["--dangerously-skip-permissions", "--trust"], openagents: [], // a launcher, not an agent: nothing of its own to auto-approve aider: ["--yes-always"], }; @@ -44,6 +45,7 @@ const EXPECTED_LAUNCH_ARGS = { kimi: ["--yolo"], // no agents view — kimi has no agent list to land on qwen: ["--approval-mode=yolo"], deepseek: ["--turbo"], + mimocode: ["--dangerously-skip-permissions", "--trust"], // no agents view — `mimo agent` prints and exits openagents: [], // bare launch opens the dashboard, which is its agent list aider: ["--yes-always"], };