关于 cpm-planner
cpm-planner 是一个 MCP 服务,使用关键路径法根据任务图安排 Agent 工作。它识别进度约束,将文件集合互不重叠的就绪交付项分配给并行工作者,并支持计划变体、风险分析、挣值跟踪和可选的 AI 审查。
主要功能
- 01计算关键路径和进度浮时
- 02分配文件集合不重叠的并行任务组
- 03保存并比较代码化计划变体
- 04执行资源平衡和蒙特卡洛风险分析
- 05按基线跟踪挣值
- 06审查计划改进建议
适用场景
- 01安排 Agent 工作并识别瓶颈任务
- 02为并行工作者分配任务以避免文件冲突
- 03比较计划变体并评估进度风险
- 04跟踪项目进度、成本和计划表现
MCP 相关
它通过 stdio 向 MCP 客户端提供计划、调度、状态和挣值工具。客户端可将其注册为命令,用于协调 Agent 工作。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm ghcr.io/praxec/cpm-planner:0.5.0本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/praxec/cpm-planner:0.5.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
cpm-planner
cpm-planner 是一款用于 Agent 工作的关键路径法(CPM)规划器,以 MCP 服务的形式提供。提交任务图后,它会计算计划:最早和最晚开始时间与完成时间、时差、关键路径,以及决定完工时间的瓶颈任务。随后,它会分配考虑锁定状态的就绪任务组,这些任务组中的交付项文件集彼此不重叠,因此多个工作者可以并行执行而不会发生冲突。计划可以作为代码形式的文件保存在代码库中,并支持分支变体和对比;只读工具可以检查计划、根据资源容量进行资源平衡并运行蒙特卡洛风险分析;挣值工具可以根据冻结的基线跟踪 PV、EV、AC、SPI 和 CPI;可选的 AI 评审还可以提出经过验证的计划改进。任何 MCP 客户端(Claude Code、Cursor、VS Code、Codex、自定义编排器或 praxec 工作流)都可以通过 stdio 驱动它。它是一个独立工具,不依赖 praxec。
目录
安装
预构建二进制文件(推荐)
已为 Linux、macOS 和 Windows 的 x86_64 与 ARM64 平台发布预构建包。
安装程序会识别你的操作系统和 CPU,并下载匹配的归档文件。它们会
在安装前验证归档文件的 SHA-256 是否与发布版本的 checksums.sha256 一致,
然后在用户本地目录中原子替换二进制文件。如果校验和不匹配,安装程序会以非零状态退出,
不会安装任何内容。再次运行安装程序会原地升级。
# Linux / macOS -> ~/.local/bin/cpm-planner
curl -fsSL https://github.com/praxec/cpm-planner/releases/latest/download/install.sh | sh
# Windows (PowerShell) -> %LOCALAPPDATA%\Programs\cpm-planner\cpm-planner.exe
irm https://github.com/praxec/cpm-planner/releases/latest/download/install.ps1 | iex
安装程序会打印安装路径的绝对路径。向 GUI 客户端注册服务时,请使用该路径。
安装程序详情:检查脚本、PATH、升级和直接下载
如果你想先查看脚本再运行,请先下载并检查,然后再运行
(<version> 的说明请参阅固定版本):
# Linux / macOS
curl -fsSLO https://github.com/praxec/cpm-planner/releases/latest/download/install.sh
less install.sh
sh install.sh --version <version>
# Windows
irm https://github.com/praxec/cpm-planner/releases/latest/download/install.ps1 -OutFile install.ps1
Get-Content install.ps1
.\install.ps1 -Version <version>
PATH。 如果安装目录不在你的 PATH 中,安装程序会打印要添加的确切行。
传入 --add-to-path(Windows 上为 -AddToPath),即可让安装程序代为添加;
此操作仅限用户级,且绝不会使用 sudo。
在 Linux 和 macOS 上,只有当对应行尚未存在时,安装程序才会将其追加到:
- zsh 对应的
~/.zshrc; - bash 对应的
~/.bash_profile(如果存在),否则为~/.bashrc; - 其他 shell 对应的
~/.profile。
对于 fish,安装程序会打印 fish_add_path <dir>,供你运行,而不会写入文件。
下载。 安装程序只接受 https:// 下载 URL。
升级。 升级后,请重启 MCP 客户端,使其启动新的二进制文件。在 Windows 上,旧的 exe 会被重命名为 cpm-planner.exe.old,并在下次运行时删除。
直接下载。 你也可以自行下载归档文件,并使用 checksums.sha256 对其进行校验;同时也发布了机器可读的 release-manifest.json。所有资源均可在 https://github.com/praxec/cpm-planner/releases/latest 获取。
| 平台 | 资源 |
|---|---|
| Linux x86_64 | cpm-planner-x86_64-unknown-linux-gnu.tar.gz |
| Linux ARM64 | cpm-planner-aarch64-unknown-linux-gnu.tar.gz |
| macOS x86_64 | cpm-planner-x86_64-apple-darwin.tar.gz |
| macOS Apple Silicon | cpm-planner-aarch64-apple-darwin.tar.gz |
| Windows x86_64 | cpm-planner-x86_64-pc-windows-msvc.zip |
| Windows ARM64 | cpm-planner-aarch64-pc-windows-msvc.zip |
固定版本
在本 README 的命令中,<version> 是诸如 v0.2.0 的发布标签。请从发布页面中选择一个版本。
# Linux / macOS
curl -fsSL https://github.com/praxec/cpm-planner/releases/latest/download/install.sh | sh -s -- --version <version>
# Windows (PowerShell): the piped form cannot take parameters, so use the environment variable...
$env:PRAXEC_VERSION = '<version>'; irm https://github.com/praxec/cpm-planner/releases/latest/download/install.ps1 | iex
# ...or a script block
& ([scriptblock]::Create((irm https://github.com/praxec/cpm-planner/releases/latest/download/install.ps1))) -Version <version>
npm
使用 Node 18 或更高版本,从 npm 安装 cpm-planner 命令:
npm install -g @matthew-cochran/cpm
cpm-planner --version
此软件包是一个轻量级启动器。它会下载适用于你平台的发布二进制文件、验证校验和并缓存该文件;此操作会在安装期间进行,若 npm 跳过安装脚本,则会在首次运行时进行。软件包版本与发布版本一致,因此 npm install -g @matthew-cochran/[email protected] 可以固定到指定版本。若要运行而不安装,请使用 npx -y @matthew-cochran/cpm。详情和环境变量请参阅 npm/README.md。
从源代码构建
使用 Cargo 构建并安装带标签的发布版本(需要 Rust 1.99 或更高版本):
cargo install --git https://github.com/praxec/cpm-planner --tag <version> --locked
在下一次执行 cargo publish 之前,crates.io 上的列表会落后于 GitHub 发布版本,因此 cargo install cpm-planner 目前无法安装最新版本。请按上述方式从标签安装。
Docker
docker pull ghcr.io/praxec/cpm-planner
docker run -i --rm -v cpm-planner-data:/data ghcr.io/praxec/cpm-planner
-i 会保持 stdin 打开,以供 stdio 传输使用。
此镜像设置了 CPM_PLANNER_DB=/data/cpm-planner.db,并将 /data 声明为由非特权 app 用户拥有的卷,因此命名卷可以在多次运行间保留计划状态。对于绑定挂载(-v "$PWD:/data"),请以你自己的身份运行,确保目录可写:--user "$(id -u):$(id -g)"。仅在需要覆盖路径时传入 -e CPM_PLANNER_DB=...。
要将此镜像注册到客户端,请将 docker 放入 command,其余内容放入 args。例如,在 Claude Code 中:
claude mcp add cpm-planner --scope user -- docker run -i --rm -v cpm-planner-data:/data ghcr.io/praxec/cpm-planner
验证安装
cpm-planner --version
它会输出 cpm-planner <version>。若要在不使用客户端的情况下检查 MCP 端,请运行仓库中的冒烟测试脚本。该脚本会执行 MCP initialize 和 tools/list 握手;如果缺少 plan.submit、plan.status 或 plan.get,则会失败:
node scripts/mcp-smoke.mjs /absolute/path/to/cpm-planner
快速入门:将计划作为代码
将计划(PlanGraph)写入 .cpm-planner/plans/<name>/<variant>.json:
plan.lint {path: ".cpm-planner/plans/checkout/main.json"}— 静态检查,不写入状态。plan.sync {path: ".cpm-planner/plans/checkout/main.json"}— 注册该变体并返回其plan_id。plan.status {plan_id}— 查看进度安排、关键路径、就绪集和锁。
plan.sync 会根据内容哈希追踪文件;如果被追踪的文件与存储的头部图不一致,plan.status 会报告 definition_drift。
快速入门:挣值管理
冻结基线、记录进度,然后读取报告(earning_rule: "weighted" 时,earned_pct 按比例挣值;fifty_fifty 会在可交付成果开始进行或报告了任何 earned_pct 后计入 50%;默认的 zero_hundred 则在完成时计入全部挣值):
plan.baseline {plan_id}— 冻结 CPM 进度安排和预算,作为基线 1。plan.mark_status {plan_id, deliverable_id, caller_id, status: {"status": "in_progress"}, earned_pct: 50, actual_effort_hours: 4}— 报告进度和实际成本。plan.ev {plan_id}— 查看 PV、EV、AC、SV、CV、SPI、CPI、EAC 和警报。plan.snapshot {plan_id, format: "markdown"}— 追加读数并导出 Markdown 表格。
分母为零的比率会显示为 null,并在 undefined 中说明;警报(SPI_BELOW_0_9、CPI_BELOW_0_9)会比较最近两次存储的读数。
与 AI Agent 配合使用
任何 MCP 客户端都可以通过 stdio 运行 cpm-planner:将 cpm-planner
二进制文件注册为命令,或者在 Node 18 或更高版本中使用 npx -y @matthew-cochran/cpm。
对于图形界面客户端,请使用安装程序输出的绝对路径。
使用 cpm-planner skills install --target <tool> --user(或 --project <dir>)安装 Agent 技能(/cpm-plan 等),其中
<tool> 可以是 claude、codex、cursor、copilot、gemini、all 或
agents-md(仅限项目)。
docs/AGENT-INSTALL.md 提供各客户端的安装步骤(Claude
Code、Claude Desktop、Codex、Cursor、VS Code、Gemini CLI 等)、env
代码块、验证和故障排除。llms.txt 为大模型编制了文档索引。
MCP 工具
| 工具 | 功能 |
|---|---|
plan.submit | 提交任务图;返回计划 ID(基于任务图和调用方实现幂等)。前置项可以是交付项 ID 字符串,也可以是对象 {id, consumes?, kind?: artifact|interface, lag_hours?};两种形式都可以通过 plan.get 原样读回。交付项可以设置 milestone: true(验收点;除非指定估算值或工期,否则时长为零;在 plan.status 的 milestones 中报告)。可选的 earning_rule(zero_hundred,默认值:仅在完成时计入 100%;fifty_fifty:一旦开始或报告任何 earned_pct,即计入 50%;weighted:按报告的 earned_pct 计入)用于设定已挣价值如何计入部分进度,也是计划标识的一部分。交付项的可选 duration_hours(日历时间)会取代工作量,作为其排定时长;前置项的 lag_hours 会延迟依赖项的开始时间;工作量仍是成本计算依据。可选的 estimate {optimistic, likely, pessimistic}(0 <= optimistic <= likely <= pessimistic)是三点工作量估算。排定时长优先级:duration_hours > estimated_effort_hours > estimate.likely > 里程碑的 0 > 估算器默认值;仅当未设置 duration_hours 和 estimated_effort_hours 时,Monte Carlo 才会对估算值进行抽样。限制:最多 5000 个交付项,且每个小时数值(工作量、工期、滞后时间、估算值)必须为有限数值且介于 0 到 1000000 之间(否则返回 INVALID_GRAPH)。指定 name(可选的 project/variant;variant 默认为 main)后,会注册为具名变体,而非未命名计划。 |
plan.acquire_cohort | 原子性地获取最多 N 个就绪交付项,且它们的文件集互不重叠(owned_files 条目可以是 {path, mode: "append"};同一路径上的追加声明可以共同租用,并显示在 shared_paths 中;仅当多个交付项由前置项排序,或所有声明均为追加时,同一个文件才可由多个交付项所有);可选的 ttl_seconds 设置租约 TTL(限制为服务端最大值);可选的 ids 和 filter.metadata 用于指定交付项;metadata.kind = "manual" 的交付项永远不会被租用;响应会在 blocked 中列出未租用的候选项(以及 blocked_count、needs_operator)。 |
plan.heartbeat | 刷新持有的锁的 TTL;可选的 ttl_seconds 设置新的 TTL(限制为服务端最大值)。 |
plan.mark_status | 将交付项标记为已完成/失败;同时释放其锁。未持有锁时,只有前置项全部完成才能标记为已完成(否则返回 PREREQUISITES_INCOMPLETE),且每次无锁标记都会记录审计信息。无锁的 in_progress(所有者工作或手动工作)会在服务重启后保留,且不可租用;可通过无锁的 ready(或任何其他状态)标记将其交还。可选的已挣价值进度:earned_pct(整数,0 到 100,仅用于 in_progress;与 complete 一起提交时会被接受并忽略)、actual_effort_hours(截至目前的总工作量,必须为有限数值且介于 0 到 1000000 之间;会取代租用时长作为实际成本)以及 evidence(最多 2048 个字符,会追加到交付项的证据列表中;列表最多保留 100 条);违规时返回 INVALID_ACTUALS,包括 earned_pct 为负数、小数或大于 100。plan.select 会复制其携带的每个已完成交付项的实际数据(租用时长会累加;被携带交付项报告的百分比和小时数优先;保留最新的 100 条证据)。每次租约结束(完成、失败、强制释放、过期、接受覆盖、强制修订/选择/归档)都会将其时长计入交付项的租用时长;过期租约按其到期时间计算。 |
plan.status | 只读的计划及其锁快照。critical_path 始终从 __start__ 延伸到 __finish__(这两个合成端点的工作量为零;它们的 schedule 行带有 synthetic: true,且 __start__/__finish__ 是保留交付项 ID,提交时会被 INVALID_GRAPH 拒绝)。强制修订若使租约失效,会报告 superseded_revision;前置项完成后可重新获取该交付项,但旧持有者无法将其标记为已完成。当所有交付项都已完成时,plan_complete 为 true;此时 plan.completed 审计事件仅触发一次。milestones 为每个满足 milestone: true(或旧版 metadata.milestone == true)的交付项提供一行数据:id、critical_path(从 __start__ 到该交付项的最长链)、hours(其最早完成时间)和 complete。对于具名变体,还会报告 name、variant、selected、archived 和 active_plan(该变体归档时,项目唯一活动分支的名称;未命名计划的 archived 和 active_plan 为 null),以及 definition_drift:对于缺少项目根目录的变体、没有受跟踪计划文件的变体,或文件缺失、不可读或大于 8 MiB 时,值为 null(未知);否则,若文件字节与上次同步或导出的哈希一致,或文件图与当前头部图相同(重新格式化不算漂移),值为 false;若文件图与当前头部图不同(或无法解析),值为 true。因此,对文件关联变体执行内联 plan.revise/plan.sync 后,在文件重新导出或同步之前,结果会报告 true。 |
plan.get | 按 plan_id、plan 分支名称或 active: true 选取计划,并返回存储的计划图(读回已提交的内容)。 |
plan.force_release | 运维人员的紧急释放工具:无论锁的持有者/TTL 如何都可释放锁;reset_counters: true 还会清除过期/失败计数。无锁的 in_progress 没有锁,因此 plan.force_release 不会更改其状态;可通过无锁的 plan.mark_status 将其交还。 |
plan.accept | 经理/所有者验收:无需持有租约即可完成交付项(记录审计信息及证据)。 |
plan.lint | 提交前执行静态检查:循环(包含循环路径)、冗余边、缺少理由的边、未指向契约的接口边、未为任何里程碑提供输入的交付项,以及文件无序重叠(#20)。 |
plan.schedule | 根据资源容量(默认使用 metadata.owner)对任务图进行资源调配:工期、各交付项的开始/结束时间、各资源的负载、驱动链(依赖等待与具名资源等待),以及项目缓冲和输入缓冲(#19)。交付项可通过 metadata.resources: {"agent":1,"builder":1} 原子性地占用多个资源池;每个被占用的资源池都必须有足够容量。duration_hours 配合 metadata.wall_clock_only: true 可建模会阻塞依赖项、但不消耗容量的 CI/观测时间。project_buffer_pct 范围为 0 到 100(默认值为 25)。 |
plan.simulate | 对任务图、已存储计划或计划文件执行只读假设分析(不会持久化任何内容):包括 lint、关键路径、排期、里程碑、可选的资源排期(schedule,输入项与 plan.schedule 相同,且遵循相同的 INVALID_CAPACITIES: 规则)、Monte Carlo(monte_carlo:iterations 为 1 到 50000,默认值为 2000;seed 默认值为 0xC0FFEE;iterations × (deliverables + prerequisite edges) 不得超过 200000000),以及评分卡(#23)。 |
plan.release.compile | 将有版本控制且有界的工作流/晋级元数据编译为通过 lint 的普通计划:包括受保护的 PR、并行检查/汇合、所有者等待/审批/合并、生产者重试回执、保留的制品、原生固定成本集成轮次/证据复用,以及集中验收。报告批处理与逐变更门禁的成本效益分析及支配性警告;离线运行,绝不执行或验收工作(发布工作流)。 |
plan.release.diff | 将保留的生成 graph 与当前 workflow 元数据进行比较;在修订或建立基线前报告策略/估算漂移以及新增、移除或更改的 ID(发布工作流)。对应的 CLI 命令:cpm-planner release compile、release lint 和 release diff。 |
plan.sync | 根据计划文件(路径格式为 .cpm-planner/plans/<name>/<variant>.json)或内联 graph,注册或更新具名计划分支的一个变体(需要 name;variant 默认为 main)。文件通过禁止跟随符号链接的受限路径读取,并按内容哈希跟踪,以检测漂移;project 默认为自动发现的项目根目录;使用 path 时,给出的任何 name/variant/project 都必须与文件路径一致(否则返回 INVALID_PATH: name/variant/project must match the plan file path);force 会释放已移除交付项的活动锁。 |
plan.list | 列出 project 的所有计划分支(默认值:自动发现的项目根目录),按名称排序,变体也按名称排序;除非指定 include_archived,否则不包括已归档的分支和变体。active: true 仅返回项目的活动分支,且不能与 include_archived 同时使用。 |
plan.export | 将 plan_id 的头部图写入其自身的变体文件或受限 path,并返回相对于根目录的路径。如果目标是另一个变体受跟踪的计划文件,或是该变体自身存在尚未同步的本地修改的文件,则拒绝操作(返回 INVALID_PATH),除非设置 force: true。写入的文件不会同步,除非导出到该变体自身受跟踪的文件;此时会记录其哈希(之后漂移为 false)。 |
plan.revise | 就地替换计划图,并沿用现有进度(未更改的交付项保留状态/计数器;更改或重新开放的交付项会重新派生)。交付项可以通过 metadata.consumes_revision_from 声明一个直接前置项;若更改该屏障或任何前置项,除非指定 force,否则会拒绝其活动租约;指定 force 时会报告诊断信息 superseded_revision,并保留其先前的证据/小时数。返回新版本及差异;force 会释放已移除交付项的活动锁。 |
plan.fork | 复制具名变体的头部图,应用按顺序排列的 edits,并将结果注册为同一分支下的新草稿变体(未选中)。若目标分支已归档,会在写入任何文件之前返回 ARCHIVE_REFUSED;可从活动分支中已归档的变体分叉。若文件写入后注册失败,会删除该文件。 |
plan.select | 将具名变体设为其分支选中的(唯一可执行)变体,并沿用进度;force 会释放先前选中变体上的锁。 |
plan.archive | 归档(archived 默认为 true)或取消归档整个分支或其中一个 variant。已归档的变体仍可读取,但拒绝同步、选择和执行(返回 ARCHIVE_REFUSED)。 |
plan.compare | 根据评分卡比较已存储的计划(plan_ids:2 到 16 个不同 ID;或在 project 中指定 plan 分支名称(默认值为自动发现的根目录),比较所有未归档变体,最多 16 个):包括 Pareto 前沿、加权排名和推荐计划;得分相同的计划排名相同,tied_with_recommended 列出与推荐计划并列的变体。权重必须为有限数值且 >= 0。使用 monte_carlo 时,所有变体的 iterations × (deliverables + prerequisite edges) 总和不得超过 200000000(否则返回 INVALID_GRAPH)。只读;评分在异步运行时之外执行。 |
plan.baseline | 将计划当前的 CPM 排期(每个交付项的最早开始/完成时间)和预算(工作量依据 × metadata.cost_rate,默认值为 1)冻结为下一个编号的已挣价值基线。可选的 start(RFC 3339,默认为当前时间)和 calendar {hours_per_day (0 < h ≤ 24, default 8), workdays (default mon–fri), utc_offset_minutes (default 0)}(省略时:首个基线按墙上时钟小时计算;重新建立基线时沿用上一基线的日历)。第一个基线的编号为 1;重新建立基线需要非空白的 reason(最多 2048 个字符,否则返回 INVALID_GRAPH),并保留实际数据和快照。基线、实际数据和快照归属于同一变体:新选中的变体会使用自身的基线 1,无需提供理由。执行侧操作:仅适用于选中且未归档的变体(否则返回 VARIANT_NOT_SELECTED / ARCHIVE_REFUSED)。记录为 plan.ev.baselined 审计事件。 |
plan.ev | 根据最新基线生成截至 as_of 的已挣价值报告(as_of 为 RFC 3339 格式,默认为当前时间)。as_of 是 PV 状态日期;EV 和 AC 反映截至调用执行时记录的进度和实际数据。返回 BAC、PV、EV、AC、SV、CV、SPI、CPI、EAC、ETC、VAC、TCPI、逐交付项数据、已消耗的关键路径浮时、SPI_BELOW_0_9 / CPI_BELOW_0_9 警报(根据当前基线中截至 as_of 的最新两个已存储、非回填快照判断指标是否低于 0.9;当前读数不计入这两个快照),以及 excluded_unbaselined(基线建立后新增的交付项)。AC 汇总计划所有已记录的小时数,包括已移除和未纳入基线的交付项(按基线行的成本率计算;若无成本率则按 1 计算),因此即使重新建立基线,支出也不会消失。若 plan.revise 移除了已纳入基线的交付项,则会保留其被移除时已挣得的百分比(已完成时为 100,否则按其已挣价值规则计算),并将行状态报告为 removed。重新添加该交付项时,其已挣百分比会重置,但小时数仍会持续累加。分母为零的比率会返回 null,并在 undefined 中说明原因;输出永远不会包含 NaN 或无穷大。可对任何变体(包括已归档变体)执行只读操作;已归档变体会报告 archived: true,且当项目恰好有一个活动分支时会报告其 active_plan;在 plan.baseline 之前返回 NOT_BASELINED。 |
plan.snapshot | 计算 plan.ev 报告并将其追加为快照(as_of 是 PV 状态日期;EV 和 AC 反映调用执行时记录的进度)。若快照的 as_of 早于 taken_at 超过一小时,则标记为 backfilled: true:不会触发警报,后续警报也会跳过该快照。返回其 summary(其中 undefined 会说明每个空比率的原因;警报仅考虑截至该快照自身位置的读数:该快照,以及当前基线中按 as_of 排序、位于其之前的最新非回填快照,因此回填快照不会从更新的读数触发警报)、snapshot_count 和包含最新 100 个快照的 export(按 as_of 排序,同一日期按拍摄时间排序,最旧的在前,因此回填的 as_of 会按日期顺序排列;若回填快照比这 100 个更早,则仍会存储并计数,但不会列出):可以是摘要列表(format: "json",默认值),也可以是包含日期、PV、EV、AC、SPI、CPI、EAC 列的 Markdown 表格(format: "markdown",未定义的比率显示为 n/a)。执行侧规则与 plan.baseline 相同;在建立基线之前返回 NOT_BASELINED。 |
plan.review | 对任务图、已存储计划或计划文件进行可选的 AI 审查(只读;graph、plan_id、path 三者中必须且只能指定一个;可选的 capacities / resource_key / project_buffer_pct 用于平衡工期;max_questions 范围为 1 到 64,默认值为 64)。始终执行 lint;若 lint 报错,则返回 status: "invalid_graph",且不会调用评审器。否则会批量调用一次 Jev,询问可能存在的错误依赖和缺失依赖、拆分及接口拆分候选项、以及崩溃选项,并返回建议性的 findings 和 proposals:plan.fork 编辑列表;每个列表都通过 simulate 验证(lint 通过且缩短工期),并按 hours_saved / max(cost, 1) 排名。若没有密钥或 LLM 设置不可用,则 status 为 review_unavailable,并附带 reason 和 lint 结果。参阅计划审查(可选)。 |
plan.status、plan.get、plan.ev、plan.snapshot、plan.baseline、 | |
plan.acquire_cohort、plan.heartbeat、plan.mark_status、plan.accept 和 | |
plan.force_release 通过以下三种方式之一定位计划:plan_id、plan(计划行名称,解析为其选定变体),或 active: true(项目中唯一未归档,且其选定变体未归档、未完成的计划行)。plan 和 active 还接受可选的 project(默认值:发现的项目根目录);将 project 与 plan_id 一起使用会导致参数无效。没有活动计划行时返回 NO_ACTIVE_PLAN;有多个活动计划行时返回 AMBIGUOUS_ACTIVE_PLAN 并列出这些计划行;未知计划行或没有选定变体的计划行返回 PLAN_NOT_FOUND。 | |
周期性任务和 Agent 应使用 active(或 plan)查找计划,而不要固定使用 plan_id,这样它们才能跟随工作转到下一计划行。active 仅匹配未完成的计划行;其他计划行上线时,它可能会变更(或变得不明确):先解析一次(plan.status 和 plan.acquire_cohort 会返回 plan_id),然后在心跳、状态标记以及该计划最终的 plan.status/plan.ev 中继续使用此 plan_id。已归档变体的固定 ID 仍可通过 plan.status/plan.ev 读取;这两个工具会报告 archived: true,并通过 active_plan 指明当前进行的工作所在位置,而快照、基线和执行会因 ARCHIVE_REFUSED 而被拒绝。 |
plan.lint、plan.simulate 和 plan.review 必须且只能使用内联 graph、已存储的 plan_id 或计划文件 path 三者之一;plan.schedule 接受 graph 或 plan_id;
plan.schedule 和 plan.simulate 会拒绝 plan.submit 所拒绝的内容,而 plan.lint 会将其报告为问题。对于相同平台和工具链,给定种子时 Monte Carlo 输出可复现;由于浮点数学函数可能存在差异,不保证不同目标平台或编译器版本之间的结果逐位完全相同。
计划即代码工作流
将计划编写为文件,保存到 .cpm-planner/plans/<name>/<variant>.json,使用 plan.lint {path} 检查,通过 plan.sync {path} 注册,然后执行返回的 plan_id。一个具名计划行可以有多个变体,但只能有一个选定变体:plan.fork 根据结构化编辑创建草案,plan.compare 对变体评分,plan.select 则指定一个可执行变体。请将定义保存在已跟踪的文件中,切勿保留未跟踪的临时图。
计划审查(可选)
plan.review 会通过 OpenRouter 向 Jev(typesafe/jev-1.13)——TypeSafe 的校准判断模型——询问一个通过 lint 检查的计划:每次审查最多批量提出 64 个问题。设置 OPENROUTER_API_KEY(或 CPM_OPENROUTER_KEY_FILE)后才会启用;若未设置密钥,工具仍会响应,并返回 review_unavailable 和 lint 报告。无效的 LLM 设置不会导致服务停止:它会在启动时记录日志,而 plan.review 会返回 review_unavailable,并说明具体设置项。
-
概率仅供参考。 请将审查结果视为第二意见。提案会通过机械方式验证(编辑后的计划必须通过 lint 检查,并模拟出更短的总工期);使用
plan.fork {edits}应用一项提案,然后运行plan.compare。 -
成本。 每次实际调用评审模型的
plan.review请求,都会产生一笔 OpenRouter 费用;Jev 在 OpenRouter 上的输入费用约为每百万个 token 0.042 美元。每份报告都会在服务提供方提供usage时附上该信息。每台服务最多同时运行 2 次调用评审模型的审查;更多请求会等待空位(在调用模型前就结束的审查,例如缺少密钥或存在 lint 错误的审查,无需等待)。 -
隐私。 以下是该单次请求中发送到 OpenRouter 的全部内容:
- 固定的任务句子、计划总工期和关键路径(ID);
- 问题:每个问题都会包含其类型、问题中提及的交付物 ID,以及引用这些 ID 的问题句子;根据问题类型,句子还会包含前置项的
consumes文本、交付物的计划工时、计划的计划工时中位数,以及触发缺失依赖问题的启发式信号(描述提及了另一交付物、文件位于相同或嵌套目录中、共享metadata.owner); - 对于问题提及的每项交付物:其 ID、计划工时、浮动工时、
critical和milestone标记、所拥有的文件路径(完整发送,不截断)、前置项(ID、consumes、类型),以及description、artifact和owner元数据。description、artifact、owner和consumes均截断至 500 个字符。
图中的其他内容(其他交付物、其他元数据键、估算)均不会发送。请勿审查其内容可能不适合分享给 OpenRouter 的计划。
-
审计。 密钥不会被记录或返回,错误放置的 LLM 变量值也不会。每次
plan.review调用都会记录一条plan.review审计事件,无论结果如何(包括缺少计划、路径错误或审查失败);唯一不记录审计的情况是因参数无效(invalid_params)而拒绝调用。事件仅包含以下字段:status:ok、review_unavailable、invalid_graph或error(审查失败:PLAN_NOT_FOUND、INVALID_PATH、INVALID_CAPACITIES等);code:error时为错误前缀,否则为 null;failure_class:若review_unavailable是由评审模型调用失败所致,则为其类别(unauthorized、rate_limited、timeout、upstream、decode、transport、invalid_request);否则为 null(包括缺少密钥和 LLM 设置不可用的情况);plan_id:被审查的已存储计划;若使用内联graph或path,则为 null;question_count:发送的问题数量(未调用评审模型时为 0);jev_called:是否调用了评审模型;prompt_hash:规范化请求的 sha256;未调用评审模型时为 null;model:服务提供方报告的模型;若调用失败,则为已配置的模型;未调用评审模型时为 null;endpoint:评审模型端点的主机名;未配置评审模型、lint 检查发现错误或审查以error结束时为 null。
提示词本身绝不会被记录。
作为库使用
CPM 内核也是一个纯 Rust 库,可独立于 MCP 使用:
use cpm_planner::{CpmAlgorithm, Task, TaskKind};
let mut tasks = vec![
Task::new("design", "Design", TaskKind::Custom { description: "design".into() }, 4.0),
Task::new("build", "Build", TaskKind::Custom { description: "build".into() }, 8.0)
.depends_on("design"),
Task::new("test", "Test", TaskKind::Custom { description: "test".into() }, 2.0)
.depends_on("build"),
];
let result = CpmAlgorithm::calculate(&mut tasks);
println!("critical path: {:?}", result.critical_path); // ["design", "build", "test"]
// also: result.bottlenecks, result.optimal_duration_parallel,
// and per-task .float (slack) / .is_critical on each Task.
crate 级文档(运行 cargo doc --open,或在当前版本发布到 docs.rs 后查阅)中包含一个可运行的示例,演示如何通过提交、租用和完成来驱动规划器。
llm::jev::JevJudge(plan.review 裁判)拥有自己的 HTTP 客户端和连接池。池化连接由打开它的 tokio 运行时驱动,因此请在同一个运行时中使用裁判及其克隆,并为其他运行时创建新的裁判。
Praxec
cpm-planner 完全独立:它使用纯 MCP 通信,不依赖任何特定客户端的代码。Praxec 工具包将其作为 MCP 工具使用。要将其接入 praxec 工作流,请声明一个 MCP 连接(仅使用协议,不共享代码):
connections:
planner:
kind: mcp
command: cpm-planner
最简单的启动方式是使用一条命令完成设置,同时启动它以及使用它的工作流工具包:
curl -fsSL https://raw.githubusercontent.com/praxec/packs/main/setup.sh | bash
请参阅工具包注册表,了解此工具的提供方坐标(容器镜像或发布版二进制文件),以及依赖它的工具包。
环境变量
在客户端的 env 块中设置这些变量(或使用 -e/--env 标志,或 docker run -e)。这些变量均为可选。
| 变量 | 默认值 | 含义 |
|---|---|---|
CPM_PLANNER_DB | 所有操作系统上均为 ~/.local/share/praxec/cpm-planner.db(~ 表示 HOME,否则为 USERPROFILE:Windows 上为 %USERPROFILE%\.local\share\praxec\cpm-planner.db);Docker 镜像中为 /data/cpm-planner.db | 用于保存可跨进程持久化规划器状态的 SQLite 路径;:memory: 表示临时状态。 |
CPM_PLANNER_STATE_MODE | 未设置 | 未设置时,按常规方式打开持久化状态。如果现有数据库为空,或其旁置收据检测到已知计划缺失,启动将拒绝对其进行修改。只有在查看服务器报告的带时间戳备份路径后,才可设置为 recover;initialize 则明确接受现有的空位置。每条 MCP 结果都会通过 durable_state 报告解析后的位置、结果和任何备份路径。 |
CPM_PROJECT_ROOT | cwd 中包含 .cpm-planner/ 或 .git 的最近祖先目录 | 计划即代码文件(.cpm-planner/plans/<name>/<variant>.json)的仓库根目录。需要根目录的工具在未找到时会报告 INVALID_PATH: no project root (set CPM_PROJECT_ROOT or run inside a repo)。 |
CPM_MAX_TTL_SECS | 28800(8 小时) | 服务端对 plan.acquire_cohort 和 plan.heartbeat 的 ttl_seconds 上限;超过上限的请求值会被截断。必须是 1..=2592000 范围内的整数(最多 30 天),否则启动将中止。 |
OPENROUTER_API_KEY | 未设置 | plan.review 使用的 OpenRouter 密钥。未设置或为空时:plan.review 会报告 review_unavailable(“no OpenRouter key configured”)。绝不记录或返回该密钥。 |
CPM_OPENROUTER_KEY_FILE | 未设置 | 存放 OpenRouter 密钥的文件(读取内容会去除首尾空白;最多 4096 字节),仅在未设置 OPENROUTER_API_KEY 时使用。在 unix 上,如果文件对所有用户可读(o+r)或为空,则会忽略该文件并发出警告(请对其运行 chmod 600)。 |
CPM_JEV_MODEL | typesafe/jev-1.13 | plan.review 使用的 Jev 模型 ID:最多 128 个字符,且只能包含 A-Z a-z 0-9 . _ : / -,不得包含密钥;其他任何值都会被记录(仅记录变量名,不记录变量值),并使 plan.review 不可用。 |
CPM_JEV_ENDPOINT | https://openrouter.ai/api/v1/systemone | Jev 端点 URL:必须使用 https://;仅当主机为环回地址时才允许使用 http://,且不得包含凭据。 |
CPM_LLM_TIMEOUT_SECS | 30 | 每次大模型调用的超时上限,以秒为单位,必须是 1..=300 范围内的整数。 |
CPM_LLM_MODEL | deepseek/deepseek-v4.1-flash | 生成式(聊天)模型 ID:最多 128 个字符,且只能包含 A-Z a-z 0-9 . _ : / -。调用前会拒绝前沿厂商的模型,并返回包含模型名称的类型化错误:anthropic/、openai/(openai/gpt-oss* 除外)、google/、x-ai/、xai/。 |
租约默认有效期为 5 分钟。对于长时间运行的工作,请在获取租约或发送心跳时传入 ttl_seconds(≤ 服务端上限),并至少每隔 ttl/3 发送一次心跳。
文档
- docs/README.md:项目文档索引。
- docs/AGENT-INSTALL.md:为你的 AI 编码工具安装、注册并验证 cpm-planner。
- docs/architecture.md:模块、请求流程、存储架构和并发。
- docs/releasing.md:发布操作指南。
- CONTRIBUTING.md、SECURITY.md、SUPPORT.md、CHANGELOG.md。
贡献
欢迎贡献。有关工具链、门禁和分支模型,请参阅 CONTRIBUTING.md。 所有参与者均同意遵守行为准则。
许可证
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。