v0.20 alpha skill-driven python core

This commit is contained in:
kai
2026-05-06 16:26:41 +08:00
parent d1169646b8
commit db626f1d58
87 changed files with 5213 additions and 2865 deletions
+185
View File
@@ -0,0 +1,185 @@
#!/usr/bin/env python3
"""Deploy platform adapter templates outside the repository checkout."""
from __future__ import annotations
import argparse
import os
import shutil
import sys
from dataclasses import dataclass, field
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from scripts.runtime.skills import SkillRegistry
CODEX_TEMPLATE = REPO_ROOT / "codex_adapter_templates" / "codex"
@dataclass
class DeployResult:
platform: str
target: Path
written: list[Path] = field(default_factory=list)
skipped: list[Path] = field(default_factory=list)
planned: list[Path] = field(default_factory=list)
backups: list[Path] = field(default_factory=list)
def default_codex_home(
*,
env: dict[str, str] | None = None,
user_home: Path | None = None,
) -> Path:
values = os.environ if env is None else env
if values.get("CODEX_HOME"):
return Path(values["CODEX_HOME"]).expanduser()
home = Path.home() if user_home is None else user_home
return home / ".codex"
def copy_tree_contents(
src: Path,
dst: Path,
*,
force: bool,
dry_run: bool = False,
backup_existing: bool = True,
exclude: set[Path] | None = None,
) -> DeployResult:
if not src.exists():
raise FileNotFoundError(f"adapter template source not found: {src}")
result = DeployResult(platform="copy", target=dst)
excluded = exclude or set()
for item in sorted(src.rglob("*")):
rel = item.relative_to(src)
if rel in excluded:
continue
target = dst / rel
if item.is_dir():
if not dry_run:
target.mkdir(parents=True, exist_ok=True)
continue
if target.exists() and not force:
result.skipped.append(target)
continue
result.planned.append(target)
if dry_run:
continue
target.parent.mkdir(parents=True, exist_ok=True)
if target.exists() and force and backup_existing:
backup = target.with_name(f"{target.name}.bak")
shutil.copy2(target, backup)
result.backups.append(backup)
shutil.copy2(item, target)
result.written.append(target)
return result
def _merge_results(platform: str, target: Path, parts: list[DeployResult]) -> DeployResult:
merged = DeployResult(platform=platform, target=target)
for part in parts:
merged.written.extend(part.written)
merged.skipped.extend(part.skipped)
merged.planned.extend(part.planned)
merged.backups.extend(part.backups)
return merged
def copy_registered_skills(dst: Path, *, force: bool, dry_run: bool = False) -> DeployResult:
result = DeployResult(platform="skills", target=dst)
for skill in SkillRegistry().list():
part = copy_tree_contents(skill.path.parent, dst / skill.name, force=force, dry_run=dry_run)
result.written.extend(part.written)
result.skipped.extend(part.skipped)
result.planned.extend(part.planned)
result.backups.extend(part.backups)
return result
def deploy_codex(
*,
target: Path | None = None,
force: bool = False,
skip_skills: bool = False,
dry_run: bool = False,
include_config: bool = False,
repo_root: Path = REPO_ROOT,
) -> DeployResult:
codex_home = (target or default_codex_home()).expanduser()
template = repo_root / "codex_adapter_templates" / "codex"
parts = [
copy_tree_contents(
template,
codex_home,
force=force,
dry_run=dry_run,
exclude=set() if include_config else {Path("config.toml")},
),
]
if not skip_skills:
parts.append(copy_registered_skills(codex_home / "skills", force=force, dry_run=dry_run))
return _merge_results("codex", codex_home, parts)
def print_result(result: DeployResult) -> None:
action = "planned" if result.planned and not result.written else "written"
print(f"{result.platform} adapter deployment")
print(f" target: {result.target}")
print(f" files {action}: {len(result.planned if action == 'planned' else result.written)}")
print(f" files skipped: {len(result.skipped)}")
print(f" backups: {len(result.backups)}")
if result.written:
for path in result.written:
print(f" {path}")
elif result.planned:
for path in result.planned:
print(f" {path}")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Deploy Deep Research surface adapters")
sub = parser.add_subparsers(dest="platform", required=True)
codex = sub.add_parser("codex", help="Deploy Codex adapter to CODEX_HOME or a target directory")
codex.add_argument("--target", type=Path, help="Codex home target; defaults to $CODEX_HOME or ~/.codex")
codex.add_argument("--force", action="store_true", help="overwrite existing files and create .bak backups")
codex.add_argument("--skip-skills", action="store_true", help="do not copy canonical skills into target/skills")
codex.add_argument("--include-config", action="store_true", help="also copy config.toml; off by default to avoid overwriting global Codex config")
codex.add_argument("--dry-run", action="store_true", help="show files that would be written")
return parser
def main() -> int:
args = build_parser().parse_args()
if args.platform == "codex":
result = deploy_codex(
target=args.target,
force=args.force,
skip_skills=args.skip_skills,
dry_run=args.dry_run,
include_config=args.include_config,
)
print_result(result)
print()
print("Run Codex from this repository after deployment:")
if args.include_config:
print(" codex --profile deep-research")
else:
print(" codex")
print("Note: config.toml is not copied by default. Use --include-config only if you want the bundled profile.")
return 0
raise SystemExit(f"unsupported platform: {args.platform}")
if __name__ == "__main__":
raise SystemExit(main())