v0.20.5 add antigravity clean workspace
This commit is contained in:
+15
-15
@@ -18,9 +18,9 @@ if str(REPO_ROOT) not in sys.path:
|
||||
from scripts.runtime.skills import SkillRegistry
|
||||
|
||||
CODEX_TEMPLATE = REPO_ROOT / "codex_adapter_templates" / "codex"
|
||||
ANTIGRAVITY_RULES_DIR = REPO_ROOT / ".agents" / "rules"
|
||||
ANTIGRAVITY_WORKFLOWS_DIR = REPO_ROOT / ".agents" / "workflows"
|
||||
ANTIGRAVITY_AGENTS_FILE = REPO_ROOT / ".agents" / "agents.md"
|
||||
ANTIGRAVITY_RULES_DIR = REPO_ROOT / ".agent" / "rules"
|
||||
ANTIGRAVITY_WORKFLOWS_DIR = REPO_ROOT / ".agent" / "workflows"
|
||||
ANTIGRAVITY_AGENTS_FILE = REPO_ROOT / ".agent" / "agents.md"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -192,20 +192,20 @@ def deploy_antigravity(
|
||||
) -> DeployResult:
|
||||
"""Deploy Antigravity workspace rules and skills into a workspace root.
|
||||
|
||||
This intentionally deploys to a workspace-local `.agents` directory, not
|
||||
This intentionally deploys to a workspace-local `.agent` directory, not
|
||||
global Antigravity/Gemini settings, so existing user configuration is not
|
||||
touched. Existing files are skipped unless `force=True`.
|
||||
"""
|
||||
workspace = (target or repo_root).expanduser()
|
||||
parts: list[DeployResult] = []
|
||||
if not skip_agents:
|
||||
parts.append(copy_antigravity_agents_file(workspace / ".agents", force=force, dry_run=dry_run))
|
||||
parts.append(copy_antigravity_agents_file(workspace / ".agent", force=force, dry_run=dry_run))
|
||||
if not skip_skills:
|
||||
parts.append(copy_registered_skills(workspace / ".agents" / "skills", force=force, dry_run=dry_run))
|
||||
parts.append(copy_registered_skills(workspace / ".agent" / "skills", force=force, dry_run=dry_run))
|
||||
if not skip_rules:
|
||||
parts.append(copy_antigravity_rules(workspace / ".agents" / "rules", force=force, dry_run=dry_run))
|
||||
parts.append(copy_antigravity_rules(workspace / ".agent" / "rules", force=force, dry_run=dry_run))
|
||||
if not skip_workflows:
|
||||
parts.append(copy_antigravity_workflows(workspace / ".agents" / "workflows", force=force, dry_run=dry_run))
|
||||
parts.append(copy_antigravity_workflows(workspace / ".agent" / "workflows", force=force, dry_run=dry_run))
|
||||
return _merge_results("antigravity", workspace, parts)
|
||||
|
||||
|
||||
@@ -238,10 +238,10 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
antigravity = sub.add_parser("antigravity", help="Deploy Antigravity workspace rules and skills")
|
||||
antigravity.add_argument("--target", type=Path, help="Workspace root; defaults to this repository")
|
||||
antigravity.add_argument("--force", action="store_true", help="overwrite existing files and create .bak backups")
|
||||
antigravity.add_argument("--skip-agents", action="store_true", help="do not copy role definitions into target/.agents/agents.md")
|
||||
antigravity.add_argument("--skip-skills", action="store_true", help="do not copy canonical skills into target/.agents/skills")
|
||||
antigravity.add_argument("--skip-rules", action="store_true", help="do not copy workspace rules into target/.agents/rules")
|
||||
antigravity.add_argument("--skip-workflows", action="store_true", help="do not copy workflows into target/.agents/workflows")
|
||||
antigravity.add_argument("--skip-agents", action="store_true", help="do not copy role definitions into target/.agent/agents.md")
|
||||
antigravity.add_argument("--skip-skills", action="store_true", help="do not copy canonical skills into target/.agent/skills")
|
||||
antigravity.add_argument("--skip-rules", action="store_true", help="do not copy workspace rules into target/.agent/rules")
|
||||
antigravity.add_argument("--skip-workflows", action="store_true", help="do not copy workflows into target/.agent/workflows")
|
||||
antigravity.add_argument("--dry-run", action="store_true", help="show files that would be written without writing")
|
||||
return parser
|
||||
|
||||
@@ -252,7 +252,6 @@ def main() -> int:
|
||||
result = deploy_codex(
|
||||
target=args.target,
|
||||
force=args.force,
|
||||
skip_agents=args.skip_agents,
|
||||
skip_skills=args.skip_skills,
|
||||
dry_run=args.dry_run,
|
||||
include_config=args.include_config,
|
||||
@@ -270,6 +269,7 @@ def main() -> int:
|
||||
result = deploy_antigravity(
|
||||
target=args.target,
|
||||
force=args.force,
|
||||
skip_agents=args.skip_agents,
|
||||
skip_skills=args.skip_skills,
|
||||
skip_rules=args.skip_rules,
|
||||
skip_workflows=args.skip_workflows,
|
||||
@@ -278,9 +278,9 @@ def main() -> int:
|
||||
print_result(result)
|
||||
print()
|
||||
print("Open the target workspace in Antigravity and enable/mention the workspace rule if needed:")
|
||||
print(" .agents/rules/deep-research-antigravity.md")
|
||||
print(" .agent/rules/deep-research-antigravity.md")
|
||||
print("Workflow installed when supported by your Antigravity build:")
|
||||
print(" .agents/workflows/deep-research-native.md")
|
||||
print(" .agent/workflows/deep-research-native.md")
|
||||
print("Existing files are skipped by default. Use --force only when you want .bak backups and replacement.")
|
||||
return 0
|
||||
raise SystemExit(f"unsupported platform: {args.platform}")
|
||||
|
||||
+14
-14
@@ -24,24 +24,25 @@ from scripts.deploy_adapters import default_codex_home, deploy_codex
|
||||
from scripts.runtime.skills import SkillRegistry
|
||||
|
||||
CODEX_TEMPLATE = REPO_ROOT / "codex_adapter_templates" / "codex"
|
||||
OPENCODE_ROOT = REPO_ROOT / ".opencode"
|
||||
AGENTS_SKILLS = REPO_ROOT / ".agents" / "skills"
|
||||
OPENCODE_SKILLS = OPENCODE_ROOT / "skills"
|
||||
AGENT_SKILLS = REPO_ROOT / ".agent" / "skills"
|
||||
|
||||
REQUIRED_PATHS = [
|
||||
"AGENTS.md",
|
||||
"GEMINI.md",
|
||||
"README.md",
|
||||
"PLAN.md",
|
||||
".opencode/opencode.json",
|
||||
".agent/agents.md",
|
||||
".agent/rules/deep-research-antigravity.md",
|
||||
".agent/workflows/deep-research-native.md",
|
||||
".agent/skills/search-strategy/SKILL.md",
|
||||
"codex_adapter_templates/codex/config.toml",
|
||||
"codex_adapter_templates/codex/agents/dr-pm.toml",
|
||||
"codex_adapter_templates/codex/commands/dr-run.md",
|
||||
".agents/skills/search-strategy/SKILL.md",
|
||||
"skills/deep-research/SKILL.md",
|
||||
"skills/document-ingest/SKILL.md",
|
||||
"scripts/dr.py",
|
||||
"scripts/deploy_adapters.py",
|
||||
"scripts/install_codex_adapter.py",
|
||||
"scripts/export_antigravity_workspace.py",
|
||||
]
|
||||
|
||||
REQUIRED_ENV_KEYS = [
|
||||
@@ -119,12 +120,12 @@ def check_deployment() -> int:
|
||||
if not (REPO_ROOT / item).exists():
|
||||
issues.append(f"missing required path: {item}")
|
||||
|
||||
tracked = git_tracked(["codex_adapter_templates", ".opencode", ".agents/skills", "skills"])
|
||||
legacy_tracked = git_tracked([".codex"])
|
||||
tracked = git_tracked(["codex_adapter_templates", ".agent", "skills"])
|
||||
legacy_tracked = git_tracked([".codex", ".opencode", ".claude", ".gemini", ".agents"])
|
||||
if legacy_tracked:
|
||||
warnings.append("legacy .codex files are still tracked; run: git rm -r --cached .codex")
|
||||
warnings.append("legacy platform adapter files are still tracked; prefer Antigravity clean workspace or remove them from the branch")
|
||||
for item in REQUIRED_PATHS:
|
||||
if item.startswith(("codex_adapter_templates/", ".opencode/", ".agents/")) and item not in tracked:
|
||||
if item.startswith(("codex_adapter_templates/", ".agent/")) and item not in tracked:
|
||||
warnings.append(f"not tracked by git: {item}")
|
||||
|
||||
skills = SkillRegistry().list()
|
||||
@@ -148,9 +149,9 @@ def check_deployment() -> int:
|
||||
print("Deep Research deployment check")
|
||||
print(f" repo: {REPO_ROOT}")
|
||||
print(f" codex template files tracked: {sum(1 for p in tracked if p.startswith('codex_adapter_templates/'))}")
|
||||
print(f" legacy .codex files tracked: {len(legacy_tracked)}")
|
||||
print(f" legacy platform adapter files tracked: {len(legacy_tracked)}")
|
||||
print(f" codex home: {codex_home}")
|
||||
print(f" opencode files tracked: {sum(1 for p in tracked if p.startswith('.opencode/'))}")
|
||||
print(f" antigravity .agent files tracked: {sum(1 for p in tracked if p.startswith('.agent/'))}")
|
||||
print(f" codex skills: {len(skills)}")
|
||||
|
||||
if warnings:
|
||||
@@ -171,7 +172,6 @@ def check_deployment() -> int:
|
||||
def repair(force: bool, codex_home: Path | None) -> int:
|
||||
try:
|
||||
codex_result = deploy_codex(target=codex_home, force=force)
|
||||
skills_written = copy_tree_contents(OPENCODE_SKILLS, AGENTS_SKILLS, force=force)
|
||||
except PermissionError as exc:
|
||||
print(f"repair failed: permission denied: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
@@ -182,7 +182,7 @@ def repair(force: bool, codex_home: Path | None) -> int:
|
||||
print("Repair completed.")
|
||||
print(f" Codex home files written: {len(codex_result.written)}")
|
||||
print(f" Codex home: {codex_result.target}")
|
||||
print(f" .agents skills written: {len(skills_written)}")
|
||||
print(f" .agent skills source: {AGENT_SKILLS}")
|
||||
return check_deployment()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Export a clean Antigravity-only workspace from a full checkout."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
FILES = [
|
||||
"AGENTS.md",
|
||||
"GEMINI.md",
|
||||
"README.md",
|
||||
"docs/antigravity-clean-workspace.md",
|
||||
"docs/platform-adapters.md",
|
||||
"scripts/dr.py",
|
||||
"scripts/deploy_adapters.py",
|
||||
]
|
||||
|
||||
DIRS = [
|
||||
".agent",
|
||||
"configs",
|
||||
"scripts/runtime",
|
||||
"scripts/reporting",
|
||||
]
|
||||
|
||||
EMPTY_DIRS = [
|
||||
"projects",
|
||||
]
|
||||
|
||||
EXCLUDED_NAMES = {
|
||||
"__pycache__",
|
||||
".pytest_cache",
|
||||
}
|
||||
|
||||
|
||||
def copy_file(src: Path, dst: Path, *, dry_run: bool) -> None:
|
||||
if dry_run:
|
||||
print(f"file {src.relative_to(REPO_ROOT)} -> {dst}")
|
||||
return
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
|
||||
def ignore_names(_: str, names: list[str]) -> set[str]:
|
||||
return {name for name in names if name in EXCLUDED_NAMES}
|
||||
|
||||
|
||||
def copy_dir(src: Path, dst: Path, *, dry_run: bool) -> None:
|
||||
if dry_run:
|
||||
print(f"dir {src.relative_to(REPO_ROOT)} -> {dst}")
|
||||
return
|
||||
if dst.exists():
|
||||
shutil.rmtree(dst)
|
||||
shutil.copytree(src, dst, ignore=ignore_names)
|
||||
|
||||
|
||||
def export_workspace(target: Path, *, force: bool, dry_run: bool) -> None:
|
||||
target = target.expanduser().resolve()
|
||||
if target == REPO_ROOT:
|
||||
raise SystemExit("target must not be the full repository root")
|
||||
if target.exists() and any(target.iterdir()) and not force:
|
||||
raise SystemExit(f"target is not empty: {target}; use --force to replace managed files")
|
||||
|
||||
if not dry_run:
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for rel in FILES:
|
||||
src = REPO_ROOT / rel
|
||||
if not src.exists():
|
||||
raise FileNotFoundError(src)
|
||||
copy_file(src, target / rel, dry_run=dry_run)
|
||||
|
||||
for rel in DIRS:
|
||||
src = REPO_ROOT / rel
|
||||
if not src.exists():
|
||||
raise FileNotFoundError(src)
|
||||
copy_dir(src, target / rel, dry_run=dry_run)
|
||||
|
||||
for rel in EMPTY_DIRS:
|
||||
if dry_run:
|
||||
print(f"dir {rel} -> {target / rel}")
|
||||
else:
|
||||
(target / rel).mkdir(parents=True, exist_ok=True)
|
||||
keep = target / rel / ".gitkeep"
|
||||
keep.touch(exist_ok=True)
|
||||
|
||||
if not dry_run:
|
||||
print(f"exported clean Antigravity workspace: {target}")
|
||||
print("open this target directory in Antigravity")
|
||||
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="Export a clean Antigravity-only workspace")
|
||||
parser.add_argument("--target", required=True, type=Path, help="output workspace directory")
|
||||
parser.add_argument("--force", action="store_true", help="replace managed files in a non-empty target")
|
||||
parser.add_argument("--dry-run", action="store_true", help="show what would be copied")
|
||||
return parser
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = build_parser().parse_args()
|
||||
export_workspace(args.target, force=args.force, dry_run=args.dry_run)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -8,7 +8,8 @@ from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
CANONICAL_SKILLS_DIR = REPO_ROOT / ".agents" / "skills"
|
||||
CANONICAL_SKILLS_DIR = REPO_ROOT / ".agent" / "skills"
|
||||
LEGACY_AGENTS_SKILLS_DIR = REPO_ROOT / ".agents" / "skills"
|
||||
PROJECT_SKILLS_DIR = REPO_ROOT / "skills"
|
||||
REQUIRED_SKILLS = {
|
||||
"search-strategy",
|
||||
@@ -38,6 +39,8 @@ class SkillRegistry:
|
||||
if self.canonical_dir == CANONICAL_SKILLS_DIR and PROJECT_SKILLS_DIR.exists():
|
||||
roots.append(PROJECT_SKILLS_DIR)
|
||||
roots.append(self.canonical_dir)
|
||||
if self.canonical_dir == CANONICAL_SKILLS_DIR and LEGACY_AGENTS_SKILLS_DIR.exists():
|
||||
roots.append(LEGACY_AGENTS_SKILLS_DIR)
|
||||
return roots
|
||||
|
||||
def list(self) -> list[SkillInfo]:
|
||||
@@ -104,5 +107,5 @@ class SkillRegistry:
|
||||
def default_adapter_skill_dirs() -> list[Path]:
|
||||
return [
|
||||
REPO_ROOT / ".opencode" / "skills",
|
||||
REPO_ROOT / ".agents" / "skills",
|
||||
REPO_ROOT / ".agent" / "skills",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user