feat(spawn): rename builder->aipass_framework, {{CITIZEN_CLASS}} placeholder, retire birthright, per-project registry targeting (TDPLAN-0010, FPLAN-0294)
This commit is contained in:
@@ -57,14 +57,38 @@ from aipass.spawn.apps.handlers.class_registry import (
|
||||
from aipass.spawn.apps.handlers.json import json_handler
|
||||
|
||||
# Default template location (relative to spawn package root)
|
||||
DEFAULT_TEMPLATE = Path(__file__).parents[2] / "templates" / "builder"
|
||||
DEFAULT_TEMPLATE = Path(__file__).parents[2] / "templates" / "aipass_framework"
|
||||
|
||||
_PROJECT_MARKERS = (".git", "pyproject.toml", "setup.py", "setup.cfg")
|
||||
|
||||
|
||||
def _find_project_registry(target: Path) -> Path:
|
||||
"""Walk up from target to find a project root, return its registry path.
|
||||
|
||||
Used when the default find_registry returned a registry outside the
|
||||
target's project (e.g. AIPass's own registry for an external target).
|
||||
"""
|
||||
for parent in [target.parent] + list(target.parent.parents):
|
||||
if any((parent / m).exists() for m in _PROJECT_MARKERS):
|
||||
return parent / "AIPASS_REGISTRY.json"
|
||||
if parent == parent.parent:
|
||||
break
|
||||
return target.parent / "AIPASS_REGISTRY.json"
|
||||
|
||||
|
||||
_META_TAB_KEYS = {"TODOS_META", "KEY_LEARNINGS_META", "SESSIONS_META", "OBSERVATIONS_META"}
|
||||
|
||||
|
||||
def _load_meta_tabs():
|
||||
"""Load memory meta-tab values from @memory's renderer. Raises on failure."""
|
||||
from aipass.memory.apps.handlers.tracking.tab_renderer import render_all_meta_tabs
|
||||
"""Load memory meta-tab values from @memory's renderer.
|
||||
|
||||
Returns empty dict when @memory is unavailable (standalone project).
|
||||
"""
|
||||
try:
|
||||
from aipass.memory.apps.handlers.tracking.tab_renderer import render_all_meta_tabs
|
||||
except ImportError:
|
||||
logger.info("[spawn] @memory not available — meta-tab placeholders will be empty")
|
||||
return {}
|
||||
|
||||
tabs = render_all_meta_tabs()
|
||||
missing = _META_TAB_KEYS - set(tabs or {})
|
||||
@@ -170,7 +194,7 @@ def _spawn_agent(
|
||||
profile=None,
|
||||
template_dir=None,
|
||||
registry_path=None,
|
||||
citizen_class="builder",
|
||||
citizen_class="aipass_framework",
|
||||
):
|
||||
"""
|
||||
Create a new AIPass agent from template.
|
||||
@@ -183,7 +207,7 @@ def _spawn_agent(
|
||||
profile: AIPass profile override (default: auto-detect)
|
||||
template_dir: Custom template directory (default: class-based lookup)
|
||||
registry_path: Path to AIPASS_REGISTRY.json (default: auto-discover)
|
||||
citizen_class: Citizen class name (default: "builder")
|
||||
citizen_class: Citizen class name (default: "aipass_framework")
|
||||
|
||||
Returns:
|
||||
Dict with creation results:
|
||||
@@ -227,8 +251,13 @@ def _spawn_agent(
|
||||
branch_lower = normalize_branch_name(folder_name, "lower")
|
||||
detected_profile = profile or detect_profile(target)
|
||||
|
||||
# Determine citizen number from registry
|
||||
# Determine registry — per-project, never borrow another project's
|
||||
reg_path = Path(registry_path) if registry_path else find_registry(target.parent)
|
||||
try:
|
||||
target.relative_to(reg_path.parent)
|
||||
except ValueError:
|
||||
logger.info("[spawn] Target %s outside registry %s — resolving project-local registry", target, reg_path)
|
||||
reg_path = _find_project_registry(target)
|
||||
citizen_number = get_next_citizen_number(reg_path)
|
||||
|
||||
# Build placeholder replacements
|
||||
@@ -241,6 +270,7 @@ def _spawn_agent(
|
||||
purpose=purpose or "New agent - purpose TBD",
|
||||
profile=detected_profile,
|
||||
citizen_number=citizen_number,
|
||||
citizen_class=citizen_class,
|
||||
meta_tabs=meta_tabs,
|
||||
)
|
||||
|
||||
|
||||
@@ -74,9 +74,8 @@ def handle_regenerate_registry(args: list[str]) -> int:
|
||||
"""Parse args and execute template registry regeneration.
|
||||
|
||||
Args patterns:
|
||||
[] -> regenerate builder (default)
|
||||
["builder"] -> regenerate builder template
|
||||
["birthright"] -> regenerate birthright template
|
||||
[] -> regenerate aipass_framework (default)
|
||||
["aipass_framework"] -> regenerate aipass_framework template
|
||||
["--all"] -> regenerate all template registries
|
||||
["--help"] -> show help
|
||||
|
||||
@@ -115,9 +114,9 @@ def handle_regenerate_registry(args: list[str]) -> int:
|
||||
json_handler.log_operation("regenerate_registry_all", data={"classes": list(classes)})
|
||||
return 1 if had_error else 0
|
||||
|
||||
# Single class — default to builder
|
||||
# Single class — default to aipass_framework
|
||||
clean_args = [a for a in args if not a.startswith("--")]
|
||||
class_name = clean_args[0] if clean_args else "builder"
|
||||
class_name = clean_args[0] if clean_args else "aipass_framework"
|
||||
|
||||
available = get_available_classes()
|
||||
if class_name not in available:
|
||||
@@ -153,7 +152,7 @@ def _print_help() -> None:
|
||||
"""Print usage help for regenerate-registry command."""
|
||||
warning("Usage: drone @spawn regenerate-registry [class_name | --all]")
|
||||
console.print()
|
||||
console.print(" [green](no args)[/green] Regenerate builder template registry (default)")
|
||||
console.print(" [green](no args)[/green] Regenerate aipass_framework template registry (default)")
|
||||
console.print(" [green]<class>[/green] Regenerate registry for a specific template class")
|
||||
console.print(" [green]--all[/green] Regenerate registries for all template classes")
|
||||
console.print()
|
||||
|
||||
@@ -79,8 +79,7 @@ def handle_update(args: list[str]) -> int:
|
||||
|
||||
Args patterns:
|
||||
["@branch"] -> update single branch (uses passport's class)
|
||||
["builder", "--all"] -> update all builder-class branches
|
||||
["birthright", "--all"] -> update all birthright-class branches
|
||||
["aipass_framework", "--all"] -> update all aipass_framework-class branches
|
||||
["--all"] -> BLOCKED (must specify class)
|
||||
["--dry-run", "@branch"] -> preview mode
|
||||
["--dry-run", "--all"] -> BLOCKED
|
||||
@@ -93,8 +92,7 @@ def handle_update(args: list[str]) -> int:
|
||||
warning("Usage: drone @spawn update <@branch|class --all> [--apply] [--dry-run] [--trace]")
|
||||
console.print()
|
||||
console.print(" [green]@branch[/green] Update a single branch (uses its own class)")
|
||||
console.print(" [green]builder --all[/green] Update all builder-class branches")
|
||||
console.print(" [green]birthright --all[/green] Update all birthright-class branches")
|
||||
console.print(" [green]aipass_framework --all[/green] Update all aipass_framework-class branches")
|
||||
console.print(" [green]--apply[/green] Execute changes (default is preview-only)")
|
||||
console.print(" [green]--dry-run[/green] Preview changes without modifying files [dim](default)[/dim]")
|
||||
console.print(" [green]--trace[/green] Enable verbose logging")
|
||||
@@ -104,8 +102,7 @@ def handle_update(args: list[str]) -> int:
|
||||
warning("Usage: drone @spawn update <@branch|class --all> [--apply] [--dry-run] [--trace]")
|
||||
console.print()
|
||||
console.print(" [green]@branch[/green] Update a single branch (uses its own class)")
|
||||
console.print(" [green]builder --all[/green] Update all builder-class branches")
|
||||
console.print(" [green]birthright --all[/green] Update all birthright-class branches")
|
||||
console.print(" [green]aipass_framework --all[/green] Update all aipass_framework-class branches")
|
||||
console.print(" [green]--apply[/green] Execute changes (default is preview-only)")
|
||||
console.print(" [green]--dry-run[/green] Preview changes without modifying files [dim](default)[/dim]")
|
||||
console.print(" [green]--trace[/green] Enable verbose logging")
|
||||
|
||||
Reference in New Issue
Block a user