"""Small read-only context bundles; no automatic source/credential dumping.""" from __future__ import annotations from concurrent.futures import ThreadPoolExecutor from pathlib import Path from .common import META_ROOT, now, read_json from .workspace import inspect_repository, load_project, selected_repositories def build_context( workspace_root: Path, project_file: Path | None, names: list[str], changed: bool ) -> dict: project = load_project(workspace_root, project_file) repos = selected_repositories(project, names) with ThreadPoolExecutor(max_workers=8) as pool: states = list(pool.map(inspect_repository, repos)) if changed: states = [ state for state in states if state["errors"] or state["dirty_entries"] or state["ahead"] or (state["head"] and not state["upstream"]) ] inventory = {} if not project_file: path = META_ROOT / "docs/project/ui-review-issue-inventory.json" if path.is_file(): payload = read_json(path) for item in payload.get("issues", []) if isinstance(payload, dict) else []: if isinstance(item, dict): inventory[item.get("repository")] = item for state in states: root = Path(state["path"]) state["instructions"] = [ str(path) for path in (root / "AGENTS.md",) if path.is_file() ] state["documentation"] = [ str(path) for path in ( root / "README.md", root / "docs/README.md", root / "docs/MODULE_ARCHITECTURE.md", ) if path.is_file() ] state["change_entry_count"] = len(state["dirty_entries"]) state["review_issue"] = inventory.get(state["name"], {}).get("url") state["suggested_check"] = ( f"./devkit check --repo {state['name']} --profile quick --dry-run" ) summary = [ f"{project.name}: {len(states)} repositories selected (offline; upstream counts may be stale)." ] for state in states: errors = f" ERROR: {'; '.join(state['errors'])}" if state["errors"] else "" summary.append( f"{state['name']}: {state['branch'] or '(detached/unborn)'}; {state['change_entry_count']} change entries; ahead={state['ahead']} behind={state['behind']}{errors}" ) return { "schema_version": 1, "generated_at": now(), "workspace_root": str(workspace_root), "project": project.name, "remote_checked": False, "repositories": states, "summary": summary, "_exit_code": 1 if any(state["errors"] for state in states) else 0, } def register(subparsers): parser = subparsers.add_parser( "context", help="Offline repository changes, ownership and relevant instruction paths", ) parser.add_argument("--repo", action="append", default=[]) parser.add_argument( "--changed", action="store_true", help="Show dirty or locally ahead repositories, retaining inspection errors", ) parser.set_defaults( handler=lambda args: build_context( args.workspace_root, args.project, args.repo, args.changed ) )