mono-pilot
v0.2.14
Published
Cursor-compatible coding agent powered by pi-mono
Maintainers
Readme
MonoPilot
Cursor-compatible coding agent profile powered by pi.
MonoPilot is a lightweight, highly customizable Cursor-compatible coding agent built on top of the pi framework. It is designed for developers who want full control over how their coding agent behaves, prefer not to pay a middleman, and want to explore the limits of a lean agent architecture.
Why MonoPilot
- Transparent prompt/runtime envelope with inspection tooling.
- Cursor-styled tool layer replaces default pi tools so launch-time capability and behavior are defined by MonoPilot.
- Extensible tool layer with MCP support for custom tools and resources.
Quickstart
# Run directly without global install
npx mono-pilot
# Or install globally
npm install -g mono-pilot
mono-pilotUsage
# Interactive
mono-pilot
# Interactive game profile (murder mystery, role-play)
mono-pilot --mono-mode game
# One-shot prompt
mono-pilot -p "Refactor this module"
# One-shot game prompt
mono-pilot --mono-mode game -p "你现在是侦探,先做自我介绍"
# Game mode with explicit channel override
mono-pilot --mono-mode game --game-channel game:office-004
# Game mode with identity auto-load from workspace
# expects .mono-game/profile.json and optional .mono-game/identity.md
mono-pilot --mono-mode game
# Optional: limit tools per role
# profile.json example: { "displayName": "侦探", "tools": ["BusSend", "MailBox", "ReadFile"] }
# Continue previous session
mono-pilot --continueBy default, mono-pilot launches pi with:
--no-extensions--extension <mono-pilot extension>--tools ls(only when you do not pass--toolsor--no-tools)
If you pass --tools, MonoPilot removes built-in edit, write, read, grep, glob, and bash so the extension-provided Cursor-styled tools are used instead. If the list becomes empty, it falls back to ls. The write path is provided by the ApplyPatch tool from the extension.
What ships now
src/cli.ts– launcher that wrapspisrc/extensions/mono-pilot.ts– extension entrypoint (tool wiring)src/extensions/mono-game.ts– game-oriented extension entrypoint (story + bus tools)src/extensions/system-prompt.ts– provider-agnostic prompt stacksrc/extensions/user-message.ts– user message envelope assemblysrc/brief/– persistent agent memory ("brief" system), inspired by letta-ai/letta-code's memory architecture, renamed from "memory" to "brief" to distinguish condensed knowledge from conversation historysrc/memory/– memory search indexing + retrieval (builtin, SQLite + FTS)src/mcp/– config loading, JSON-RPC transport, server resolutionsrc/rules/– rule file discovery (shared by envelope and session hints)src/tools/– tool implementations and descriptions (seesrc/tools/README.md)
Cursor-styled tools
MonoPilot exposes a Cursor-style tool set to highlight capability at launch:
These replace pi defaults such as edit, write, read, grep, glob, and bash.
Default-to-MonoPilot mapping:
edit/write→ApplyPatchread→ReadFilegrep→rgglob→Globbash→Shell
The full Cursor-styled tool list exposed by the extension:
Shell– execute shell commands in the workspaceGlob– find paths by glob patternrg– search file content with ripgrepAstGrep– search code structure with ast-grep (run/scan, read-only)ReadFile– read file content with paginationDelete– delete files or directoriesSemanticSearch– semantic search by intentmemory_search– search indexed memory snippetsmemory_get– read a snippet from memory filesWebSearch– search the web with snippetsWebFetch– fetch and render web contentGenerateImage– generate images via Gemini API or OpenRouterAskQuestion– collect structured multiple-choice answersSubagent– launch delegated subprocessesListMcpResources– list MCP resources from configFetchMcpResource– fetch a specific MCP resourceListMcpTools– discover MCP tools and schemasCallMcpTool– invoke MCP tools by nameSwitchMode– switch interaction mode (option + m, cycles Plan → Ask → Agent)ExitPlanMode– exit Plan mode and switch back to Agent modeApplyPatch– apply single-file patches (supports diff-style @@ line hints)CodexApplyPatch– apply codex-style multi-file patches withAdd/Delete/Update/MoveoperationsMailBox– read queued bus messages (game mailbox)
User rules
MonoPilot injects user rules into the runtime envelope on each turn (handled by src/extensions/user-message.ts).
Rules are loaded from two locations:
~/.pi/rules/*.rule.txt– user-level rules (applies to all projects).pi/rules/*.rule.txt– project-level rules (workspace root)
When the same filename exists in both, the project rule wins. Each file becomes one <user_rule> block inside a <rules> envelope, sorted by filename. Empty files are ignored; the envelope is omitted if no rules are found.
MCP
- The user message envelope issues a lightweight MCP server
initializerequest to collect server instructions. - MCP tools then progressively load and surface resources, schemas, and execution only when needed.
- MCP configs are loaded from
.pi/mcp.json(project) and~/.pi/mcp.json(user); project entries take precedence on name conflicts.
Memory search
- Builtin memory search reads
~/.mono-pilot/config.json(memorySearchfield). If missing, defaults are used. - Local embeddings use
node-llama-cpp; configurememorySearch.local.modelPath(and optionalmodelCacheDir). - Session flush triggers are configured via
memorySearch.flush(onSessionSwitch,onSessionCompact,deltaBytes,deltaMessages). - Use
/build-memory --mode full|dirtyto rebuild or incrementally sync the current agent's memory index partition.
Cluster v2 logging
- Enable cluster_v2 with
MONO_PILOT_CLUSTER_V2=1orMONO_PILOT_CLUSTER_VERSION=2. - cluster_v2 logs are persisted to
~/.mono-pilot/logs/cluster_v2.YYYY-MM-DD.log. - Default behavior is file-only logging (keeps TUI clean). Set
MONO_PILOT_CLUSTER_V2_LOG_STDIO=1to also mirror logs to stdout/stderr. /clusteroperational subcommands includestatus,services,reelect,stepdown, andclose.
System events (TUI)
- MonoPilot keeps an in-session system event queue for low-disruption operational signals (for example SFTP, memory warmup/build/session flush, cluster failover, Discord collector progress, and Twitter pull status/failures).
- In interactive mode, info/warning/error system events show a bottom-right overlay toast (non-capturing), with latest-only replacement.
- Overlay can be dismissed with
Escor clicking header[×]; a 2-minute timeout remains as a fallback. - Use
/eventsto inspect recent events,/events <N>for more lines, and/events clearto reset.
Discord intelligence collector (leader-local)
When cluster_v2 is enabled, leader can run a local Discord IPC collector that subscribes to
message events and persists them as JSONL for intelligence gathering.
- No follower push
- No Discord channel interaction
- Local persistence only
Config in ~/.mono-pilot/config.json:
{
"discord": {
"enabled": true,
"clientId": "YOUR_DISCORD_APP_ID",
"channels": [
{
"id": "123456789012345678",
"alias": "My Server / general"
}
],
"events": ["MESSAGE_CREATE", "MESSAGE_UPDATE", "MESSAGE_DELETE"],
"scopes": ["rpc", "messages.read", "identify"],
"outputPath": "~/.mono-pilot/discord/messages.jsonl",
"includeRawPayload": false,
"systemEventBatchSize": 20
}
}Notes:
- Requires a locally running Discord desktop client.
- If
accessTokenis omitted, collector first tries cached auth from~/.mono-pilot/auth.json. - If cache is missing/invalid, collector runs RPC
AUTHORIZE+ OAuth token exchange, then stores tokens back to~/.mono-pilot/auth.json. - For first-time OAuth token exchange, many Discord apps require
clientSecret(and sometimesredirectUri) in config. accessTokenand cached tokens are never printed in logs.channels[].aliasis persisted aschannelAliasin each JSONL event record.- Collector enriches records by resolving
channelId -> channelNameandguildId -> guildNamevia RPC (GET_CHANNEL/GET_GUILD) when available. - Collector writes with daily rotation:
outputPathis treated as base name and actual files are<base>.YYYY-MM-DD.jsonl. - Collector emits system-events when a channel accumulates
systemEventBatchSizeMESSAGE_CREATE events, then resets the channel counter and continues. - Collector service is registered as
discord_intelin/cluster serviceswhen active.
Twitter intelligence collector (leader-local)
When cluster_v2 is enabled, leader can run a local Twitter/X collector via bird CLI.
The collector verifies browser profile/cookie access at startup, then periodically pulls the
"For You" timeline and persists JSONL records locally.
Config in ~/.mono-pilot/config.json:
{
"twitter": {
"enabled": true,
"outputPath": "~/.mono-pilot/twitter/home.jsonl",
"pullIntervalMinutes": 10,
"pullCount": 10,
"commandTimeoutMs": 30000,
"requestTimeoutMs": 15000,
"chromeProfile": "Profile 4",
"chromeProfileDir": "~/Library/Application Support/Google/Chrome",
"cookieSource": ["chrome"],
"cookieTimeoutMs": 5000,
"includeRawPayload": false
}
}Notes:
- Startup runs
bird check; if browser profile/cookies are not readable, collector startup fails and is skipped (no in-process retry loop). - On successful startup, collector runs one timeline pull immediately and then every
pullIntervalMinutes; it treatsbird homeandbird home --followingas two channels and switches between them when the current channel returns 0 tweets. - Persisted
tweets[]keeps bird top-level tweet objects as-is, including nested quote structure (not flattened). - Collector uses depth-1 enrichment (
tweet+quotedTweet) and short-link resolution: it recordsshortLinkMappings[](shortUrl -> resolvedUrl -> tweetId?) for all detected t.co links, and when a mapping resolves to a tweet id it attachesshortLinkMappings[].tweetFull(deduped by tweet id within a pull cycle, excluding self-reference wheretweetIdequals the host tweet id). - Before each pull, collector loads archived
tweetIdsnapshots from the recent two-day window (today+yesterday) and skips re-archiving duplicates that already havetweetFull; duplicate ids with missing historicaltweetFullare kept for backfill attempts. - Each persisted batch includes a unique
snapshotIdto avoid downstream overwrite collisions. - Persisted
feedreflects the actual source timeline for the batch (for_youorfollowing). - Per-cycle pull errors are reported as system-events/log warnings and skipped until next scheduled cycle (no immediate retry).
- Collector writes with daily rotation:
outputPathis treated as base name and actual files are<base>.YYYY-MM-DD.jsonl. - Collector service is registered as
twitter_intelin/cluster serviceswhen active.
Digest draft command
Use /digest draft to generate the digest draft in TUI.
Use /digest backfill to serially backfill missing archive fields in-place (tweetFull, quotedTweetFull, shortLinkMappings).
/digest draft reads the daily JSONL file (<outputBase>.YYYY-MM-DD.jsonl) and classifies tweets with TUI progress events.
/digest backfill scans archive files and writes updates back to the same JSONL lines (serial mode to keep upstream pressure low).
Config in ~/.mono-pilot/config.json:
{
"twitter": {
"digest": {
"classifier": {
"provider": "openai",
"model": "gpt-4o-mini",
"temperature": 0,
"maxTokens": 300,
"concurrency": 4
}
}
}
}Command examples:
/digest draft
/digest draft --date 2026-03-09
/digest draft --file ~/.mono-pilot/twitter/home.2026-03-09.jsonl --concurrency 8 --sample 6
/digest backfill
/digest backfill --date 2026-03-09
/digest backfill --file ~/.mono-pilot/twitter/home.2026-03-09.jsonlNotes:
- Classification categories are fixed to 7 classes: 技术 / 产品 / 融资并购 / 开源生态 / 组织动态 / 政策监管 / 学术研究.
- Depth-1 enriched fields (
tweetFull,quotedTweetFull) are used when present. - Draft rendering rewrites
t.coshort links toshortLinkMappings[].resolvedUrl; if the resolved link points to the host tweet itself, that short link is removed from draft text. - Draft mode now starts in background and rejects duplicate runs while active; check progress via
/events. - Draft artifacts are written to
~/.mono-pilot/twitter/draft.md(primary draft) and~/.mono-pilot/twitter/draft-debug.md(classification debug details). - Backfill mode is serial (non-concurrent), starts in background, and reuses per-run tweetId cache to avoid repeated
bird readcalls. /eventsincludes backfill start/file progress/end summary plus low-frequency item-level progress updates.
Image generation config
GenerateImage can read defaults from ~/.mono-pilot/config.json:
{
"imageGen": {
"provider": "openrouter",
"model": "google/gemini-3.1-flash-image-preview"
},
"imageGenProviders": {
"openrouter": {
"baseUrl": "https://openrouter.ai/api/v1",
"apiKey": "",
"authHeader": true,
"models": [
{ "id": "google/gemini-3.1-flash-image-preview", "name": "Nano Banana 2" }
]
}
}
}Use /image-model to switch the active provider/model stored in config:
/image-model
/image-model list
/image-model use openrouter google/gemini-3.1-flash-image-previewSFTP sync on ApplyPatch
If .vscode/sftp.json exists and a profile has uploadOnSave: true, patch tools can trigger uploads after successful writes:
ApplyPatch: uploads the affected file.CodexApplyPatch: uploads files inadded[]andmodified[](deleted[]is intentionally not auto-deleted remotely).
Set interactiveAuth: true to enable OTP prompts for /sftp commands and patch-triggered sync when needed.
Manual commands:
/sftp
/sftp upload path/to/file-or-dir
/sftp download path/to/file-or-dir
/sftp target <targetName>/sftp (without args) opens an interactive target selector (up/down to choose, enter to confirm).
[
{
"name": "prod",
"protocol": "sftp",
"host": "10.0.0.130",
"port": 22,
"username": "wanqian",
"privateKeyPath": "~/.ssh/id_rsa",
"passphrase": "...",
"remotePath": "/home/wanqian/project",
"uploadOnSave": true,
"interactiveAuth": true,
"hop": {
"host": "36.151.163.132",
"port": 22,
"username": "wanqian",
"privateKeyPath": "/Users/wanqian/.ssh/id_rsa",
"passphrase": "...",
"interactiveAuth": true
}
}
]hop enables single-jump SFTP tunneling (local -> hop -> target).
Both target and hop authentication are performed by the local client, so privateKeyPath values must point to local files.
Local development
git clone https://github.com/qianwan/mono-pilot.git
cd mono-pilot
npm install
npm run buildSource-mode development (no build needed on each change):
# Run from TypeScript sources directly
npm run dev
# Optional: auto-restart on file changes
npm run dev:watch
# Continue the latest session from source mode
npm run dev:continue
# Auto-restart + continue latest session
npm run dev:watch:continueWhen running in interactive mode, option+o opens the current workspace in nvim (falls back to vim), restores an existing workspace session from .mono-pilot/nvim/session.vim, and saves the session on exit.
Prompt inspection
# Build first (ensures dist extension exists)
npm run build
# Print injected system prompt + runtime envelope (snippet)
npm run inspect:injection
# Print full prompt and envelope to stdout
npm run inspect:injection:full
# Provide a custom user query to render
node scripts/inspect-injection.mjs --query="Summarize this repo"The report shows:
- the final system prompt after tool injection
- the runtime envelope built from
<rules>,<mcp_instructions>,<system_reminder>, and<user_query>
License
MIT
