afv-library/skills/agentforce-d360-analyze/scripts/_shared/cli_override.py

99 lines
3.3 KiB
Python

"""Shared CLI override helper for d360 entry scripts.
D360 has 5 independent entry scripts (``fetch_dc.py``, ``assemble_dc.py``,
``render_dc.py``, ``resolve_session.py``, ``discover_sessions.py``) — each
needs the same ``--data-dir`` / ``--cache-dir`` flags and the same
3-level namespace rebind. This module factors that duplication into one
helper.
Usage in each entry script::
from _shared.cli_override import add_cli_flags, apply_overrides
parser = argparse.ArgumentParser(...)
parser.add_argument(...)
add_cli_flags(parser)
args = parser.parse_args()
apply_overrides(args, caller_globals=globals())
The ``caller_globals=globals()`` parameter is the magic that lets us
rebind the entry script's own ``DATA_ROOT`` / ``CACHE_ROOT`` snapshot —
without it, only ``paths.X`` and ``config.X`` would update, and the
entry script's local ``DATA_ROOT`` would still point at the default.
"""
from __future__ import annotations
import argparse
from pathlib import Path
_SKILL_NAME = "investigating-agentforce-d360"
def add_cli_flags(parser: argparse.ArgumentParser) -> None:
"""Add --data-dir / --cache-dir flags to the given parser.
Defaults are runtime-agnostic (``~/.vibe/...``); other
runtimes (AFV OOTB, Codex, Cursor, OpenCode) override at invocation
time.
"""
parser.add_argument(
"--data-dir",
type=Path,
default=None,
help=(
"Override data root (default: "
"~/.vibe/data/investigating-agentforce-d360)."
),
)
parser.add_argument(
"--cache-dir",
type=Path,
default=None,
help=(
"Override cache root (default: "
"~/.vibe/cache/investigating-agentforce-d360)."
),
)
def apply_overrides(args: argparse.Namespace, caller_globals: dict) -> None:
"""Apply --data-dir / --cache-dir overrides across all 3 namespace levels.
Must be called BEFORE any pipeline code reads ``DATA_ROOT`` or
``CACHE_ROOT``.
Levels rebound:
1. ``paths.DATA_ROOT`` (the source of truth)
2. ``config.DATA_ROOT`` (the re-export)
3. The caller's own local ``DATA_ROOT`` (via ``caller_globals``)
Why 3 levels? Python's ``from X import Y`` snapshots ``Y`` into the
caller's local namespace at import time. Mutating ``X.Y`` later does
NOT update the caller's local binding. Without the 3rd-level rebind,
any function in the caller that references ``DATA_ROOT`` still sees
the default path even after ``paths.DATA_ROOT`` was overridden.
"""
if not (args.data_dir or args.cache_dir):
return
from _shared import runtime, paths
import config
if args.data_dir:
runtime.set_data_root_override(args.data_dir)
new_data = runtime.resolve_data_root(_SKILL_NAME)
paths.DATA_ROOT = new_data
config.DATA_ROOT = new_data
# Rebind caller's local snapshot, if it captured DATA_ROOT.
if "DATA_ROOT" in caller_globals:
caller_globals["DATA_ROOT"] = new_data
if args.cache_dir:
runtime.set_cache_root_override(args.cache_dir)
new_cache = runtime.resolve_cache_root(_SKILL_NAME)
paths.CACHE_ROOT = new_cache
if hasattr(config, "CACHE_ROOT"):
config.CACHE_ROOT = new_cache
if "CACHE_ROOT" in caller_globals:
caller_globals["CACHE_ROOT"] = new_cache