Files
AirPlan-V2/scripts/airplan.py
AirPlan 6130478c96 feat: AirPlan V2 — 全专家插件强制路由 + 事件系统规范化
P0-8 扩大: do_mode.py finish_worker 全专家插件强制路由
- GUI→XDB, network→NDB, C/C++→SDB, done→Rvr, blocked/failed→Dbg
- 证据去重: 已有 xdbSessions/ndbSessions/sdbReports/rvrReviewed 则跳过

P1-GAP17: 事件 emit 规范化
- 新增 7 个事件常量 (TASK_ENTERED, TASK_FINISHED, ENGINE_ENTERED 等)
- 全部 emit 调用替换字符串字面量为常量,零残留
- 30 个事件类型常量全部定义且唯一

P1-GAP18: 事件日志原子轮转
- emit 计数器每 128 次检查轮转,避免每次 emit 读文件
- 清除未使用的 _emit_with_completion/_pending_merge_complete
- 原子轮转: tempfile+os.replace 保证不损坏

eng 极端接管: 强制调用全部专家插件 (Dbg/XDB/NDB/SDB/Rvr)
commands/do.md: 更新为全专家插件路由文档

全量测试: 69 通过, 0 失败

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 15:56:44 +08:00

194 lines
6.9 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
AirPlan V2 — 统一 CLI 入口。
V1 的 8 个独立插件合并为一个命令,通过 --mode 参数路由到对应子模式。
用法:
airplan --mode arc --project <project-root> [--todo <todo-md>]
airplan --mode eng --project <project-root> [--sub enter|status|plan|dispatch|monitor|merge|run|intervene]
airplan --mode do --project <project-root> --task-id <id>
airplan --mode dbg --project <project-root> [--task-id <id>]
airplan --mode sdb --project <project-root> [--backend cppcheck|clang-tidy|clippy|go-vet|tsc] [--target <path>]
airplan --mode ctx --project <project-root> [--sub now|status|pause|init]
airplan --mode dep --project <project-root> --task-id <id> --host <host> [--binary <path>]
airplan --mode tst --project <project-root> --task-id <id> [--framework <name>]
airplan --mode sec --project <project-root> --task-id <id> [--scan-path <path>]
airplan --mode rvr --project <project-root> --task-id <id>
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
def _add_lib_path() -> None:
root = Path(__file__).resolve().parents[1]
lib_path = root / "lib"
if str(lib_path) not in sys.path:
sys.path.insert(0, str(lib_path))
_add_lib_path()
from air_runtime.project_bootstrap import ensure_project_bootstrap
def _run_arc(args) -> None:
from air_runtime.modes.arc_mode import main as arc_main
arc_main(args)
def _run_eng(args) -> None:
from air_runtime.modes.eng_mode import main as eng_main
eng_main(args)
def _run_do(args) -> None:
from air_runtime.modes.do_mode import main as do_main
do_main(args)
def _run_dbg(args) -> None:
from air_runtime.modes.dbg_mode import main as dbg_main
dbg_main(args)
def _run_xdb(args) -> None:
from air_runtime.modes.xdb_mode import main as xdb_main
xdb_main(args)
def _run_sdb(args) -> None:
from air_runtime.modes.sdb_mode import main as sdb_main
sdb_main(args)
def _run_ndb(args) -> None:
from air_runtime.modes.ndb_mode import main as ndb_main
ndb_main(args)
def _run_ctx(args) -> None:
from air_runtime.modes.ctx_mode import main as ctx_main
ctx_main(args)
def _run_dep(args) -> None:
from air_runtime.modes.dep_mode import main as dep_main
dep_main(args)
def _run_tst(args) -> None:
from air_runtime.modes.tst_mode import main as tst_main
tst_main(args)
def _run_sec(args) -> None:
from air_runtime.modes.sec_mode import main as sec_main
sec_main(args)
def _run_rvr(args) -> None:
from air_runtime.modes.rvr_mode import main as rvr_main
rvr_main(args)
def _run_eng_orchestrator(args) -> None:
from air_runtime.modes.eng_orchestrator import main as eng_orch_main
eng_orch_main(args)
MODE_ROUTER = {
"arc": _run_arc,
"eng": _run_eng,
"do": _run_do,
"dbg": _run_dbg,
"xdb": _run_xdb,
"sdb": _run_sdb,
"ndb": _run_ndb,
"ctx": _run_ctx,
"dep": _run_dep,
"tst": _run_tst,
"sec": _run_sec,
"rvr": _run_rvr,
"eng_orchestrator": _run_eng_orchestrator,
}
MODE_HELP = {
"arc": "架构规划器 — 分析依赖、写集冲突、产出执行计划",
"eng": "调度引擎 — 波次派发、监控、合并、修复编排",
"do": "任务执行器 — 单任务切片执行与结果生成",
"dbg": "调试器 — 7步调试工作流、根因分析、修复回滚",
"xdb": "GUI验证器 — 截图取证、GUI操作验证",
"sdb": "静态分析器 — 多语言静态分析、diff模式",
"ndb": "网络调试器 — 抓包分析、TLS解密",
"ctx": "上下文管理器 — 压缩、Token估算、锁检测",
"dep": "部署器 — SSH远程构建、部署、systemd管理",
"tst": "测试运行器 — 统一测试执行与结果报告",
"sec": "安全扫描器 — 敏感数据检测与脱敏",
"rvr": "需求审查器 — 交付物与需求一致性验证",
}
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="AirPlan V2 — 统一制品驱动开发调度器",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="\n".join(f" {k:6s} {v}" for k, v in MODE_HELP.items()),
)
parser.add_argument("--mode", choices=list(MODE_ROUTER.keys()), required=True,
help="子模式选择")
parser.add_argument("--project", default=".",
help="项目根目录 (默认: .)")
parser.add_argument("--sub", default="",
help="子模式内的二级操作(如 eng 的 enter|status|plan 等)")
parser.add_argument("--todo", default="",
help="todo.md 路径arc/eng 使用)")
parser.add_argument("--task-id", default="",
help="任务 IDdo/dbg/xdb/sdb/ndb/dep/tst/sec/rvr 使用)")
parser.add_argument("--result", default="",
help="Worker result.json 路径eng merge 使用)")
parser.add_argument("--dispatch-group", default="",
help="派发组名eng dispatch 使用)")
parser.add_argument("--host", default="",
help="远程主机dep 使用)")
parser.add_argument("--binary", default="",
help="本地二进制路径dep 使用)")
parser.add_argument("--framework", default="",
help="测试框架tst 使用)")
parser.add_argument("--scan-path", default="",
help="扫描路径sec 使用)")
parser.add_argument("--sec-mode", default="blocking", choices=["advisory", "blocking"],
help="安全扫描模式sec 使用): advisory只报告/ blocking阻止合并")
parser.add_argument("--backend", default="",
help="静态分析后端sdb 使用): cppcheck|clang-tidy|clippy|go-vet|tsc")
parser.add_argument("--target", default="",
help="分析目标路径sdb 使用)")
parser.add_argument("--prefer", default="auto",
help="截图后端偏好xdb 使用): kms|xvfb|fallback|auto")
parser.add_argument("--output", default="",
help="截图输出文件名xdb 使用,默认 screenshot.png")
parser.add_argument("--task-text", default="",
help="任务描述文本do 使用,用于 UI 任务检测和路由)")
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> None:
args = parse_args(argv)
project_root = Path(args.project).expanduser().resolve()
ensure_project_bootstrap(project_root)
handler = MODE_ROUTER.get(args.mode)
if handler is None:
print(f"Unknown mode: {args.mode}", file=sys.stderr)
print(f"Available modes: {', '.join(MODE_ROUTER.keys())}", file=sys.stderr)
sys.exit(1)
handler(args)
if __name__ == "__main__":
main()