jan-cli 0.27.0

YAML-defined CLI trees with progressive help, optional exec aliases, merged extra specs, and SQLite audit logging keyed by git branch
Documentation
about: |
  Summarize a sales CSV with pandas: parse dates, aggregate by region,
  and optionally filter to a product. Declares `packages.uv: [pandas==3.0.5]`
  so jan materializes a hash-keyed venv before run.
packages:
  uv:
    - pandas==3.0.5
inputs:
  csv:
    description: Path to sales CSV (columns date,region,product,units,revenue)
    default: packages/sales.csv
    type: file
  product:
    description: Optional product name to filter (empty = all products)
    default: ""
commands:
  help:
    about: Describe this script.
    exec:
      text: |
        summarize-sales

        Parse a sales CSV with pandas and print a regional summary.

        Requires: uv on PATH (jan installs pandas into a cached venv).

        Examples (from the jan-cli repo, with this tree preferred):

        jan use examples --root packages.spec.yaml
        jan --cwd examples summarize-sales run
        jan --cwd examples summarize-sales run --product gadget
        jan --cwd examples summarize-sales run --csv /path/to/other.csv

        Outputs units and revenue totals per region, plus a grand total.
  run:
    about: Load CSV, aggregate by region, print summary table.
    exec:
      argv:
        - python3
        - -c
        - |
          import sys
          from pathlib import Path

          import pandas as pd

          csv_path = Path("${{ inputs.csv }}").expanduser()
          product = "${{ inputs.product }}".strip()

          if not csv_path.is_file():
              print(f"CSV not found: {csv_path}", file=sys.stderr)
              print("Pass --csv <path> or run with --cwd pointing at the examples tree.", file=sys.stderr)
              sys.exit(2)

          df = pd.read_csv(csv_path, parse_dates=["date"])
          required = {"date", "region", "product", "units", "revenue"}
          missing = required - set(df.columns)
          if missing:
              print(f"CSV missing columns: {sorted(missing)}", file=sys.stderr)
              sys.exit(2)

          df["units"] = pd.to_numeric(df["units"], errors="coerce")
          df["revenue"] = pd.to_numeric(df["revenue"], errors="coerce")
          df = df.dropna(subset=["units", "revenue", "region"])

          if product:
              df = df[df["product"].str.casefold() == product.casefold()]
              if df.empty:
                  print(f"No rows for product={product!r}", file=sys.stderr)
                  sys.exit(1)

          by_region = (
              df.groupby("region", sort=True)
              .agg(units=("units", "sum"), revenue=("revenue", "sum"), rows=("date", "count"))
              .reset_index()
          )
          by_region["revenue"] = by_region["revenue"].map(lambda x: f"{x:,.2f}")
          by_region["units"] = by_region["units"].map(lambda x: f"{int(x)}")

          title = "Sales by region"
          if product:
              title += f" (product={product})"
          print(title)
          print(by_region.to_string(index=False))
          print()
          print(
              f"Grand total: {int(df['units'].sum())} units, "
              f"${df['revenue'].sum():,.2f} revenue "
              f"({len(df)} rows, {df['date'].min().date()} → {df['date'].max().date()})"
          )
      passthrough: true