Files
the-nexus/lazarus/cli.py
Alexander Whitestone e530565d3c WIP: Claude Code progress on #880
Automated salvage commit — agent session ended (exit 124).
Work in progress, may need continuation.
2026-04-06 14:16:00 -04:00

163 lines
5.4 KiB
Python

"""Lazarus CLI — invite agents, manage mission cells."""
from __future__ import annotations
import argparse
import json
import platform
import sys
import uuid
from pathlib import Path
from lazarus.cell import MissionCell, create_cell, load_cell
from lazarus.isolation import setup_level1_isolation
from lazarus.roster import DEFAULT_REGISTRY_PATH, MissionRoster
def _default_base_path() -> Path:
if platform.system() == "Linux":
return Path("/var/missions")
return Path.home() / ".nexus" / "missions"
# ─── command handlers ─────────────────────────────────────────────────────────
def cmd_invite(args: argparse.Namespace) -> int:
"""Create or load a mission cell and invite an agent."""
base_path = Path(args.base_path) if args.base_path else _default_base_path()
mission_id: str = args.mission or str(uuid.uuid4())
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
# Create or load cell
cell_dir = base_path / mission_id
if (cell_dir / "cell.json").exists():
cell = load_cell(cell_dir)
else:
cell = create_cell(
mission_id=mission_id,
name=args.name or mission_id,
repo_url=args.repo,
base_path=base_path,
)
# Level 1 isolation
setup_level1_isolation(cell)
# Register mission and invite agent
roster.add_mission(cell)
entry = roster.invite_agent(mission_id, args.agent, role=args.role)
roster.save()
summary = {
"mission_id": mission_id,
"agent": args.agent,
"role": entry.role,
"status": entry.status,
"cell_path": cell.cell_path,
"repo_url": cell.repo_url,
"invited_at": entry.invited_at,
}
print(json.dumps(summary, indent=2))
return 0
def cmd_list(args: argparse.Namespace) -> int:
"""List missions, optionally filtered by state."""
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
missions = roster.list_missions(state=args.state)
print(json.dumps(missions, indent=2))
return 0
def cmd_status(args: argparse.Namespace) -> int:
"""Show details of a specific mission."""
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
mission = roster.get_mission(args.mission_id)
if mission is None:
print(f"Mission {args.mission_id!r} not found.", file=sys.stderr)
return 1
agents = roster.list_agents(args.mission_id)
output = dict(mission)
output["agent_count"] = len(agents)
output["agents"] = [a.to_dict() for a in agents]
print(json.dumps(output, indent=2))
return 0
def cmd_depart(args: argparse.Namespace) -> int:
"""Mark an agent as departed from a mission."""
roster = MissionRoster(Path(args.registry) if args.registry else DEFAULT_REGISTRY_PATH)
try:
entry = roster.depart_agent(args.mission, args.agent)
except KeyError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1
roster.save()
print(json.dumps(entry.to_dict(), indent=2))
return 0
# ─── argument parsing ─────────────────────────────────────────────────────────
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="lazarus",
description="Lazarus Pit — agent invitation and mission cell management",
)
parser.add_argument(
"--registry",
default=None,
help="Path to mission roster JSON (default: ~/.nexus/mission-roster.json)",
)
sub = parser.add_subparsers(dest="command", required=True)
# invite
p_invite = sub.add_parser("invite", help="Invite an agent to a mission")
p_invite.add_argument("agent", help="Agent name to invite")
p_invite.add_argument("--mission", default=None, help="Mission ID (UUID generated if omitted)")
p_invite.add_argument("--repo", required=True, help="Repository URL for the mission")
p_invite.add_argument("--base-path", default=None, help="Base directory for mission cells")
p_invite.add_argument("--role", default="contributor", help="Agent role (default: contributor)")
p_invite.add_argument("--name", default=None, help="Human-readable mission name")
# list
p_list = sub.add_parser("list", help="List missions")
p_list.add_argument("--state", default=None, help="Filter by state (pending/active/archived/destroyed)")
# status
p_status = sub.add_parser("status", help="Show mission status")
p_status.add_argument("mission_id", help="Mission ID")
# depart
p_depart = sub.add_parser("depart", help="Mark agent as departed")
p_depart.add_argument("agent", help="Agent name")
p_depart.add_argument("--mission", required=True, help="Mission ID")
return parser
def main(argv: list[str] | None = None) -> int:
parser = _build_parser()
args = parser.parse_args(argv)
dispatch = {
"invite": cmd_invite,
"list": cmd_list,
"status": cmd_status,
"depart": cmd_depart,
}
handler = dispatch.get(args.command)
if handler is None:
parser.print_help()
return 1
return handler(args)
if __name__ == "__main__":
sys.exit(main())