AirPlan V2 initial release — unified scheduler with 12 sub-modes

Merges 8 V1 plugins into 1 unified plugin, adds 4 new components (dep, tst, sec, rvr).
12 sub-modes: arc, eng, do, dbg, xdb, sdb, ndb, ctx, dep, tst, sec, rvr.
L1 code-level guarantees: atomic writes, file locks, evidence gating, forced debug routing,
3-phase arc gate, evidence-first debug gate, Chinese language lock, scheduler boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AirPlan Team
2026-06-10 16:24:26 +08:00
commit 2c4b3340bf
81 changed files with 6005 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
from __future__ import annotations
import json
import shutil
from pathlib import Path
def resolve_plugin_paths() -> dict[str, Path]:
"""
解析插件的实际安装路径。
顺序: 1) 环境变量 AIRPLAN_HOME > 2) ~/.airplan > 3) 相对脚本位置推测
"""
# 1. 环境变量
if "AIRPLAN_HOME" in __import__("os").environ:
return {"root": Path(__import__("os").environ["AIRPLAN_HOME"])}
# 2. ~/.airplan 默认
home = Path.home()
default = home / ".airplan"
if default.exists():
return {"root": default}
# 3. 尝试从当前脚本位置推测
# 脚本位于 {root}/scripts/airplan.py
import sys
script_root = Path(sys.argv[0]).resolve().parent if sys.argv else None
if script_root and (script_root.name == "scripts"):
plugin_root = script_root.parent
if (plugin_root / ".claude-plugin").exists():
return {"root": plugin_root}
# 4. 从本模块位置推测
# 本模块位于 {root}/lib/air_runtime/installer.py
module_root = Path(__file__).resolve().parent.parent.parent # lib/air_runtime/installer -> lib/air_runtime -> lib -> root
if (module_root / ".claude-plugin").exists():
return {"root": module_root}
raise FileNotFoundError("Cannot locate plugin installation directory")
def get_plugin_meta() -> dict:
"""读取 plugin.json 元数据"""
paths = resolve_plugin_paths()
meta_path = paths["root"] / ".claude-plugin" / "plugin.json"
if not meta_path.exists():
raise FileNotFoundError(f"plugin.json not found at {meta_path}")
return json.loads(meta_path.read_text())
def post_install_verify() -> dict:
"""
安装后验证:检查所有依赖工具是否存在。
返回 {tool: bool} 映射False 表示缺失。
"""
required_tools = [
"git", # 版本控制
"cmake", # 构建
"ffmpeg", # XDB 截图
"xvfb-run", # XDB 虚拟显示
]
result = {"ok": True, "missing": []}
for tool in required_tools:
found = shutil.which(tool) is not None
result[tool] = found
if not found:
result["ok"] = False
result["missing"].append(tool)
# 检查 Python 依赖
try:
import yaml
result["pyyaml"] = True
except ImportError:
result["pyyaml"] = False
result["ok"] = False
result["missing"].append("pyyaml")
# 检查目录结构
paths = resolve_plugin_paths()
for subdir in ["lib", "scripts", "skills", "commands"]:
p = paths["root"] / subdir
result[f"dir_{subdir}"] = p.exists()
if not p.exists():
result["ok"] = False
result["missing"].append(f"dir:{subdir}")
return result
def verify_plugin_json_paths() -> dict:
"""
验证 plugin.json 里的路径是否可解析。
设计原文 P0-9 指出硬编码 $HOME 是问题,这里修复它。
"""
meta = get_plugin_meta()
issues = []
# 检查 scripts 路径
entry = meta.get("entry", {})
if isinstance(entry, dict):
script_path_str = entry.get("args", [""])[0] if entry.get("args") else ""
else:
script_path_str = str(entry)
if "$HOME" in script_path_str:
# 尝试解析
resolved = script_path_str.replace("$HOME", str(Path.home()))
if not Path(resolved).exists():
issues.append(f"script path not found: {resolved}")
else:
# 修复:改用相对路径或 AIRPLAN_HOME 变量
issues.append(f"script uses $HOME: {script_path_str} (should use relative path)")
# 检查 marketplace 路径
market_path = meta.get("marketplace", "")
if "$HOME" in market_path:
issues.append(f"marketplace uses $HOME: {market_path}")
return {
"issues": issues,
"resolved_paths": resolve_plugin_paths(),
}