refactor: DLO-16/17/18/20 — CLI simplification, config Pydantic, planner split, type system unification

DLO-16: Reduce cli.py from 1073 to 83 lines by registering Click commands from commands/*.py modules
DLO-17: Migrate Config to Pydantic BaseModel for validation
DLO-18: Split planner.py (826 lines) into orchestration, path rendering, and duplicate handling modules
DLO-20: Unify type system — convert 14 dataclasses to Pydantic BaseModel, keep TypedDicts as JSON schema hints

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
windyboy
2026-09-27 10:47:04 +08:00
co-authored by Claude Sonnet 4.5
parent 8a60aaf9a9
commit fe03a31dd4
23 changed files with 1964 additions and 2714 deletions
+58 -1
View File
@@ -15,7 +15,7 @@ from vlm.cli_helpers import (
resolve_legacy_default_input_path,
review_plan_tui_streams_ok,
)
from vlm.context import CLIContext
from vlm.context import CLIContext, pass_context
from vlm.io import load_analysis_json, load_identities_json
from vlm.plan_render import (
duplicate_groups_from_plan,
@@ -242,3 +242,60 @@ def apply_review_cmd(
f"Apply review failed: {e}",
exc_info=True,
)
@click.command(name="review-plan")
@click.option("--input", type=click.Path(exists=True, path_type=Path), default=lambda: default_artifact_path("plan.json"))
@click.option("--output", type=click.Path(path_type=Path), default=lambda: default_artifact_path("plan_manual_review.csv"))
@click.option("--season-threshold", type=int, default=20, show_default=True)
@click.option("--episode-threshold", type=int, default=40, show_default=True)
@click.option("--preview-limit", type=int, default=10, show_default=True)
@click.option("--show-all", is_flag=True, default=False)
@click.option("--tui", is_flag=True, default=False)
@click.option("--identities", type=click.Path(path_type=Path), default=None)
@click.option("--analysis", type=click.Path(path_type=Path), default=None)
@click.option("--group-by", type=click.Choice(["none", "reason", "title", "duplicate"], case_sensitive=False), default="none", show_default=True)
@click.option("--sample-safe", type=int, default=0, show_default=True)
@click.option("--structure-preview", type=click.Path(path_type=Path), default=None)
@pass_context
def review_plan(
ctx: CLIContext,
input: Path,
output: Path,
season_threshold: int,
episode_threshold: int,
preview_limit: int,
show_all: bool,
tui: bool,
identities: Optional[Path],
analysis: Optional[Path],
group_by: str,
sample_safe: int,
structure_preview: Optional[Path],
):
"""Review a plan and export high-risk operations for manual confirmation."""
review_plan_cmd(
ctx,
input,
output,
season_threshold,
episode_threshold,
preview_limit,
show_all,
tui,
identities,
analysis,
group_by,
sample_safe,
structure_preview,
)
@click.command(name="apply-review")
@click.option("--plan", type=click.Path(exists=True, path_type=Path), default=Path("plan.json"))
@click.option("--csv", type=click.Path(exists=True, path_type=Path), default=Path("plan_manual_review.csv"))
@click.option("--output", type=click.Path(path_type=Path), default=None)
@pass_context
def apply_review(ctx: CLIContext, plan: Path, csv: Path, output: Optional[Path]):
"""Apply modifications from a manual review CSV back to the plan JSON."""
apply_review_cmd(ctx, plan, csv, output)