Flanner

io.github.jaysonmulwav0.16.0更新于 Oct 7, 2026

Keeps AI coding agents' plan and design markdown managed and versioned, for Claude Code and Codex.

概览

AI 生成的概览

让 AI 编程助手创建、版本化并浏览受管理的计划与设计 Markdown 文件,且不进入 Git 提交。

功能
Flanner 是一个通过 MCP 暴露给 Claude Code、Codex 等助手的计划文件管理器。助手调用 get_plan_config、create_plan_file_tool、update_plan_file_tool 等工具写入设计文档、迁移计划和架构笔记;Flanner 把文件放入项目的计划目录,添加 YAML 前言并递增版本。它保留每个修订版本,让计划不进入 Git,并提供本地浏览器面板用于阅读、编辑和查看历史。
适用场景
当助手编写的 Markdown 计划与设计文档在仓库中堆积、逐渐过时或可能被误提交时值得加入。适合希望这些文件有版本历史和可浏览阅读视图,而不是散落 Markdown 的场景。
运行要求
需要本地 Python 环境(uv tool install、pipx 或 pip install flanner),或使用 Windows、macOS、Linux 的桌面应用。它以本地 stdio 进程运行;flanner init 会建立 SQLite 目录、.plans/ 目录并注册助手。可选:LINEAR_API_KEY 用于问题链接,FLANNER_INTEGRATIONS=1 启用该功能,FLANNER_HOME 或 FLANNER_DB_PATH 迁移数据位置。
安装前请注意
Web 界面和 http MCP 服务器绑定 127.0.0.1 且无身份验证,每个工具都以启动者的完整权限执行,不要将其暴露到 localhost 之外。存储为只追加:不会清理,retire 只是其他设备遵守的声明而非真正删除。Curb 命令会读取机器上的凭据和密钥,scrub 会替换文件中的密钥且无法撤销。可选的崩溃报告和每日版本检查可以关闭。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Flanner,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

Flanner

[PyPI] [Python] [CI] [License: MIT]

A plan-file manager for AI coding agents, wired into Claude Code and other assistants over MCP (Model Context Protocol).

Why

AI agents write markdown constantly: design docs, migration plans, architecture notes. It piles up fast, scattered across your repo, quietly going stale, and easy to commit by accident. Flanner gives those files one home, versions them automatically as the agent revises, and keeps them out of git until you decide otherwise, with a browsable reading view and an audit trail on top.

No, I'm not convinced. But why?

Those plan files pile up in two directions at once: scattered across your projects locally, and scattered across open issues in your project-management tool. Flanner is the choke point for both, keeping you organized on disk and linked to the issue each plan belongs to.

Flanner is local-first, and stays that way when a team uses it. Plans sync directly between your machines over an encrypted connection — there is no server holding them, and no upload step. The hosted side (Flanner Mesh) issues identities and decides who may read what; it never sees plan contents and could not read them if it wanted to. Next after that: clean links out to the tools teams already work in, from product trackers and chat to second brains like Notion.

Features

  • MCP integration: exposes plan-file tools to Claude Code and Codex.
  • Automatic headers and versioning: every plan gets YAML frontmatter, and each revision is a new version with a full history.
  • Git protection: plans live in .plans/ and are kept out of commits automatically.
  • Agent integration: flanner init wires CLAUDE.md, AGENTS.md, and a guard hook so agents save plans through flanner instead of scattering raw markdown.
  • Memory: the durable context around a plan — a constraint, a rejected library, a fact about the environment — kept as Markdown files a later session can search. By default an agent proposes and you approve; it refuses anything that looks like a credential, and shares with a teammate only when somebody asks for it.
  • Skills: what your agents actually load. Skill packages arrive from a project, your home directory and every installed plugin at once, and when two share a name one wins silently. flanner skills scan reads them all and flanner skills doctor says which copy is in effect and what is wrong with the rest. A read: nothing in a package is executed.
  • Curb: what each agent can reach. flanner curb map checks every Claude Code and Codex launch for readable credentials and for the channels that could carry them away, and rates it High, Medium or Low with the rule behind it. Its output never names a credential or a location, whoever runs it; flanner curb show puts those in a window on your screen. flanner curb sweep finds secrets agents left behind in transcripts, sessions, instruction files, skills, MCP configs, shell history and .env files, and says which were sent to a model provider. A read: nothing is changed, and nothing leaves the machine unless you ask a secret's own issuer to check it. With Flanner Mesh, flanner curb policy takes one agent policy your organization signs, applies the changes that only tighten once you enrol, and holds anything else for your approval; flanner curb fleet shows admins each device's policy state and counts, from minimised reports each device signs. flanner curb ci, also a GitHub Action at actions/curb-ci, judges agent steps in CI workflows the same way and writes SARIF; flanner curb app finds an application's own LLM calls; and the agent-blast-radius skill lets an agent run the redacted commands itself. flanner curb attribution gives each agent a signing key of its own, kept in your OS credential store, so its commits are labelled; flanner curb verify says which agent key signed each commit, checked against your organization's signed key registry. A signature shows which key signed, not who wrote the code.
  • Messages (Team Mesh): teammates can message each other inside their agents, device to device. flanner messages send previews before it sends, and flanner tells an agent to show a teammate's message and never act on it.
  • Issue tracker links (off in this build): tie a plan to its Linear (or JIRA) issue; with a LINEAR_API_KEY, flanner verifies the issue and shows its live state, in the CLI and the dashboard. Built but not supported yet: set FLANNER_INTEGRATIONS=1 to turn them on.
  • Reading view: a browser dashboard to read, edit, and walk the history of plans (light and dark, fully offline).
  • Per-project config: customize the plan directory per repository.

Quick start

bash
uv tool install flanner   # or: pipx install flanner, or: pip install flanner
cd your-project      # a git repo where plans should liveflanner init         # sets up the database, MCP registration, and a project

Rather not install Python? The desktop app for Windows, macOS and Linux is the same flanner with nothing to install first: the web UI in a window, the command line, and the MCP server Claude and Codex use, with background sync and updates. Download it from flanner.io/download; desktop/README.md covers building and releasing it.

uv tool install gives flanner an environment of its own and puts both flanner and flanner-mcp on your PATH. Both matter: your agent spawns flanner-mcp by name, so a project virtualenv can hide it from an agent started outside that environment. pip install flanner works, with that caveat.

Then ask your agent to work with plans:

"Create an architecture plan for the auth service"

"Show me the history of the architecture plan"

And open the dashboard to browse them:

bash
flanner web --open-browser     # http://localhost:8080

flanner init is safe to re-run. It detects your git root, creates .plans/, updates .gitignore, and installs the agent integration.

Three agents, three different files, and init handles all of it — there is no second command to remember. It writes a project-scoped .mcp.json that Claude Code reads, registers the server at user scope and in Claude Desktop's config, and adds managed blocks to CLAUDE.md and AGENTS.md. Codex reads AGENTS.md and registers MCP servers in ~/.codex/config.toml; when Codex is installed, init adds flanner's entry there, and prints the two lines to paste only when it cannot edit the file safely. flanner status shows a row per agent, each checked where that agent actually looks, and flanner setup re-runs the registration on its own if one of them needs repairing later.

--setup picks which of them you want, and repeats: flanner init --setup codex registers Codex and nothing else. --skip-claude registers with no agent, though the repository's own files are still written.

Adopting a repository somebody else set up? flanner init --sync also imports the plan files already committed in it, so flanner list shows them straight away.

CLI commands

bash
flanner init [--project-root PATH] [--plan-dir DIR]     # set up a projectflanner status                                          # projects, plan files, db pathflanner list [--project NAME] [--output json]           # list projects or a project's plansflanner sync [--project NAME] [--dry-run]               # import existing .plans/ filesflanner config NAME [--plan-dir DIR] [...]              # change project settingsflanner web [--port 8080] [--host 127.0.0.1] [--open-browser]flanner start [--port 8765] / flanner stop              # MCP server in the background, over httpflanner peer start / flanner peer stop                  # serve plans to teammates in the backgroundflanner mem remember "..." --category fact              # durable context for later sessionsflanner mem recall "..."                                # search what earlier sessions knewflanner mem list / show ID / supersede ID --with "..."  # browse, read, and correctflanner mem mode [off|explicit|suggest|auto_safe]       # how much this project capturesflanner mem share ID / flanner mem withdraw ID          # give one to the team, or ask them to stopflanner skills scan / flanner skills doctor             # what your agents load, and what is wrongflanner skills list [--all] / inspect NAME              # browse them, or read every copy of oneflanner skills observe enable / flanner skills report   # record which get used; init asksflanner skills adopt NAME / install HASH / rollback ID  # keep a copy, install it, put it backflanner skills share HASH / transfers / import ID       # send one to the team; receiving is not installingflanner curb map [--agent A] [-- LAUNCH ...]            # what each agent launch can reachflanner curb show                                       # names and locations, in a windowflanner curb inventory                                  # agents, MCP servers, hooks, jobsflanner curb sweep [--validate]                         # secrets agents left behind (needs flanner[sweep])flanner curb forget                                     # delete Curb's reports and digest keyflanner curb fix [--dry-run|--undo]                     # close what agents reach, after your OS says yesflanner curb test                                       # prove each block with decoys (costs tokens)flanner curb decoys [--renew|--remove]                  # the decoys the tester plantedflanner curb scrub FILE [--dry-run]                     # replace rotated secrets in a file (no undo)flanner curb log [--enable|--disable|--verify]          # each tool call's metadata, hash-chained and signedflanner curb observed                                   # what each agent has been seen usingflanner curb policy [--check-in|--enrol|--approve]      # your organization's signed agent policyflanner curb fleet                                      # every device, checked by its own signature (admins)flanner curb ci [PATH] [--sarif F] [--fail-on L]        # agent steps in CI workflows, as SARIFflanner curb app [PATH] [--sarif F]                     # LLM calls in an app's Python codeflanner curb attribution [--setup|--rotate]             # sign each agent's commits with its own keyflanner curb verify [REV|RANGE]                         # which agent key signed each commitflanner register [--force] / flanner unregister         # MCP registration with Claude Desktopflanner claude-info                                     # integration status

Most MCP clients spawn their own copy of the server over stdio and need neither start nor stop. They are for a client that only speaks http, two editors sharing one server, or working with flanner on its own. The server binds 127.0.0.1 and no option widens that: every tool acts with the full authority of whoever started it, and nothing authenticates a caller.

Plan file format

Every managed plan carries YAML frontmatter, generated by the tools and never hand-written:

markdown
---mcp_plan_file: trueproject_id: 3d816ecd-489a-4fa0-abe2-15ec93f60d5aplan_file_id: 59c34f9c-8471-47fc-97f2-8dcfefa15434plan_name: architectureversion: 2created_by: claude---
# Architecture Plan
Your plan content here...
Web interface

[flanner dashboard]

A server-rendered dashboard, no build step, works offline:

  • Dashboard (/): projects, stats, and recent activity
  • Project detail (/projects/{id}): a project's plans, paginated
  • Plan viewer (/plans/{id}): rendered markdown, version selector, frontmatter
  • Editor (/plans/{id}/edit) and version history (/plans/{id}/history)

The web UI binds 127.0.0.1 with no authentication. Do not expose it beyond localhost.

Where data lives
  • Catalog (SQLite): ~/.flanner/data.db, override with FLANNER_HOME or FLANNER_DB_PATH
  • Plan files: .plans/ in your repo, git-ignored, named name_v1.md, name_v2.md, and so on
Issue tracker links (Linear, JIRA)

Link plan files to issues so a plan and its ticket travel together. Built but not supported yet: the commands are hidden, and refuse to run unless FLANNER_INTEGRATIONS=1 is set.

bash
flanner linear auth                                     # verify LINEAR_API_KEY, print MCP snippetflanner linear config PROJECT --workspace acme          # linear.app/acmeflanner linear link PLAN --issue ENG-123 [--notes ...]  # link a plan to an issueflanner linear links [--project PROJECT]                # list all linksflanner linear show PLAN [--project PROJECT]            # links for one planflanner linear unlink PLAN [--issue ENG-123 | --all]flanner linear refresh PLAN                             # re-pull title/state (needs API key)

With LINEAR_API_KEY set, link verifies the issue exists and caches its title and state, --attach-url attaches a URL to the Linear issue, and refresh re-pulls live status. Without a key it stays link-only (stores the id, builds the URL). The key is read from the environment only, never stored on disk. See docs/LINEAR_INTEGRATION.md. A parallel flanner jira group links to JIRA issue keys (link-only).

Syncing plans between devices
bash
flanner peer serve                                      # answer authorised peersflanner peer pull <device-id> [--project NAME]          # pull what a peer holdsflanner peer status [<device-id>]                       # how this device is reached

A workspace is a team, and a team has more than one repository, so a pulled plan needs somewhere to land. A plan you already hold goes where it lives; otherwise --project, or the project you ran the command from, decides. Two local projects in one workspace with nothing to choose between them is reported rather than guessed at, and the next pull that names one writes what the first could not.

peer serve opens no listening port. It dials out and answers on that connection, so it needs no port forwarding, no VPN and no administrator rights. Devices find each other by public key rather than by address.

Being reachable grants nothing. A caller needs a signed request and an entitlement naming both its device and the workspace, and every artifact received is checked against its author's key, not the peer that handed it over. So a peer you sync with is not a peer you trust.

peer status answers the question a slow sync raises: direct or relayed? Both work. A relay is slower, and usually means a firewall that refuses to be punched through.

Connections go direct where possible and relay only where they must. Pass an http address instead of a device id to reach a peer already on your network, which needs flanner peer serve --http on the other side.

Platforms. Reaching a peer that has no address needs the iroh transport, which publishes builds for macOS on Apple Silicon, Linux on x86-64 and arm64, and Windows on x86-64. It is declared only for those, so installing flanner works everywhere; elsewhere it is simply absent and flanner peer status says so. Everything else in flanner is unaffected, and peers on a shared network still sync over an address. Messages are the exception: they always travel over iroh, so both devices need it.

Alpine and other musl distributions are the exception: the Linux build does not match there, so the install fails rather than skipping it. Use a glibc-based image, or install with --no-deps and add the remaining dependencies yourself.

Architecture

Layering is enforced by tests/test_architecture.py:

  • foundation (exceptions, utils, frontmatter, git_integration, jira_utils, linear_utils) imports nothing else from the package; the linear_api GraphQL client adds only exceptions
  • data (database, storage) sits on the foundation only
  • composition roots (server for MCP, web, cli) wire everything together and do not import each other (except cli, which launches both)

Decisions are recorded in docs/adr/, with more guides in docs/.

Performance

Measured on Windows AMD64, Python 3.13.1, SQLite on a local SSD. Reproduce with python benchmarks/bench.py.

OperationMedianp95Scale
create_project35.9 ms—one project
create_plan_file62.7 ms203.0 msn=100, ~2.4 KB body each
list_plan_files2.6 ms7.0 ms100 plans, n=20

Cold start, measured the same way:

CommandMedian (n=7)Before
flanner --version397 ms1470 ms
flanner --help342 ms1470 ms

SQLAlchemy was being imported by every command, including the ones that never open a store, and cost 630 ms of a 1.1 s import. It is now reached through thin wrappers that import it on first use, so a command that does not touch the database does not pay for it. flanner list does open the store, so its cost is real work rather than overhead.

Both are now under the 500 ms bar. These are from an editable install, which adds roughly 80 ms of import-finder overhead a normal pip install does not.

flanner list is no longer in this table. It opens the store, so its cost is real work rather than startup, and quoting it beside two commands that open nothing invited the comparison it does not deserve.

The numbers come from python benchmarks/bench.py, which measures the installed console script rather than python -m flanner.cli — they are not the same, and the one a person types is the one worth reporting. CI compares every run against benchmarks/baseline.json and fails past 3x, which catches an order-of-magnitude regression and nothing subtler; a shared runner's timings vary by a factor of two on identical code.

When something goes wrong

bash
flanner --verbose doctor        # where the time went, per stepflanner doctor --report         # a scrubbed summary to paste into an issue

--verbose prints a timing breakdown after the command, so "why was that slow" has an answer without a profiler.

doctor --report prints versions, platform, store size, catalog counts and whether the mesh transport installed. No paths, plan names, or plan contents — a local log holding a project name is fine, and something you paste into a public issue is not. It works when the store will not open, which is when you most need it.

The MCP server keeps ~/.flanner/mcp.log: one line per tool call, with the outcome and any error. That surface has no human watching it, so an agent that hits an error and quietly works around it would otherwise leave no trace at all. Plan bodies are never written there; flanner peer serve records the requests it answers to the same file.

Your plans, memories and skills never leave your machine. flanner sends nothing else unless you turn it on. See What flanner sends.

What flanner sends

Nothing, by default. Two things can be turned on, each with its own command. The version check has its own question at flanner init. Crash reports will too once they have somewhere to go: this build has no reporting address, so init does not ask about them and nothing is sent.

Where it goesWhat it carriesTurn it off
Version checkpypi.org, once a dayA request for flanner's public version list. Nothing about you or your work.flanner updates off
Crash reportsflanner's developers, through Sentry (EU)The error type; where in flanner it happened (module, function, line, paths relative to the package); the flanner version, OS, Python version and how flanner was installed; which surface crashed and the command name.flanner crash-reports off

A crash report never contains the error message, local variables, source lines, command arguments, full file paths, your user or machine name, or anything that identifies or counts people. flanner crash-reports show prints exactly what is waiting to be sent, or what was sent last. Reports are sent by a separate background process, never by the command that crashed. DO_NOT_TRACK=1 or FLANNER_CRASH_REPORTS=0 turns crash reports off whatever you answered.

Plan, memory and skill contents only ever travel through Flanner Mesh, directly to your own team's devices.

Exit codes

Scripts need to tell "fix your command" from "this machine is broken", so the two are different codes:

CodeMeansRetrying helps?
0It worked—
1You asked for something that cannot be done: no such project, no access, a workspace id that is not yoursOnly after you change the command
2The machine underneath failed: disk, permissions, a store that will not openNo

How it works

An agent calls get_plan_config to learn where plans go, then create_plan_file_tool or update_plan_file_tool to write them. Flanner places the file in the project's plan directory, adds the header, and bumps the version. Files stay in .plans/ (git-ignored), so they never land in a commit by accident.

Nothing is pruned, and nothing is erased. Every version, comment and review decision is kept. The store is append-only, there is no cleanup command, and the Settings page shows what that costs in bytes so the choice is visible rather than assumed. Deletion follows from the same design: flanner retire <plan> asks every peer to stop showing and serving a plan, and --restore undoes it, but it is a claim other devices honour rather than an erasure. A teammate who was offline when you ran it keeps the content until they next sync, and anyone already holding the bytes keeps them. That is the strongest promise an append-only store spread across machines you do not control can honestly make, so it is the one made here.

Keeping the agent on the rails. The MCP tools are the how; flanner init also installs two layers that make the agent actually use them. It writes a managed block into CLAUDE.md and AGENTS.md (guidance Claude Code and Codex read every session) plus a flanner-plan skill, so the agent knows to route plan docs through flanner. On top of that, a guard-write PreToolUse hook denies any raw write into the plan directory and points the agent back to create_plan_file_tool, so even if it ignores the guidance a plan cannot land as unmanaged markdown. The hook fails open and never blocks writes elsewhere.

Roadmap

Shipped in 0.9.0: peer-to-peer sync, shared workspaces, and review between teammates. Plans move directly between machines; nothing is uploaded. See Flanner Mesh for how that works and what it costs.

Shipped since: live updates in the web UI over server-sent events, a relay fallback for peers that cannot reach each other directly, and — in 0.12.0 — Flanner Memory and Flanner Skills.

Planned next:

  • Full-text search across plan bodies. The command palette indexes names today; the text inside a plan is not searchable yet.
  • Links out to product trackers, chat, and second brains like Notion
  • Running skill comparisons, not only storing them. flanner skills eval records results that a harness or a person produced; it does not yet run a suite against a model and a harness itself.
  • Observation for agents other than Claude Code, once there is an interface worth trusting. Skills reports usage as unknown rather than zero until then.

There is no plan to host plan contents. The catalog stays on your machine. That is a design decision, not a milestone waiting to be funded.

Contributing

Setup, the CI gates, how to run the unit and browser tests, benchmarks, and the release process are in CONTRIBUTING.md.

License

MIT

来源:README.md,提交 8d76846

工具

0
工具元数据尚未被收录。

版本历史

1
  1. v0.16.0最新Oct 7, 2026