| |
| """odysseus — one entrypoint for every odysseus-* CLI. |
| |
| Discovers `scripts/odysseus-<name>` siblings and dispatches to them, the |
| same way `git` finds `git-foo`. Pure passthrough: any flags after the |
| subcommand name are forwarded as-is, so `odysseus mail list --pretty` |
| behaves exactly like `odysseus-mail list --pretty`. |
| |
| odysseus # list every subcommand + short help |
| odysseus <name> # run odysseus-<name> |
| odysseus <name> <args...> # run odysseus-<name> <args...> |
| odysseus help # alias for `odysseus` (the listing) |
| odysseus help <name> # show the subcommand's own --help |
| odysseus --version # print version |
| |
| Designed to live in `scripts/`. Symlink it onto `$PATH` (`ln -s |
| $PWD/scripts/odysseus ~/.local/bin/odysseus`) to use it without typing |
| the full path. |
| """ |
| from __future__ import annotations |
|
|
| import os |
| import re |
| import subprocess |
| import sys |
| from pathlib import Path |
|
|
| VERSION = "0.1.0" |
|
|
| SCRIPTS_DIR = Path(__file__).resolve().parent |
|
|
|
|
| def _list_subcommands() -> list[Path]: |
| """Find every executable `odysseus-*` next to this file (excluding |
| the dispatcher itself + any .bak / .pre-* migration leftovers).""" |
| out = [] |
| for p in sorted(SCRIPTS_DIR.iterdir()): |
| if not p.is_file(): |
| continue |
| if p.name == "odysseus": |
| continue |
| if not p.name.startswith("odysseus-"): |
| continue |
| if any(p.name.endswith(suf) for suf in (".bak", ".pre-cli-refactor", ".pyc")): |
| continue |
| if not os.access(p, os.X_OK): |
| continue |
| out.append(p) |
| return out |
|
|
|
|
| def _short_help(path: Path) -> str: |
| """Pull the first non-blank line of the docstring (after the shebang) |
| as a 1-line description. Falls back to '(no description)'.""" |
| try: |
| text = path.read_text() |
| except Exception: |
| return "(unreadable)" |
| m = re.search(r'"""(.*?)"""', text, re.DOTALL) |
| if not m: |
| return "" |
| body = m.group(1).strip() |
| if not body: |
| return "" |
| |
| first = next((ln for ln in body.splitlines() if ln.strip()), "") |
| first = re.sub(r"^odysseus-\w+\s*—\s*", "", first).strip() |
| return first |
|
|
|
|
| def _is_runnable_subcommand(path: Path) -> bool: |
| return path.exists() and path.is_file() and os.access(path, os.X_OK) |
|
|
|
|
| def _print_listing() -> None: |
| """`odysseus` with no args (or `odysseus help`) — print the table.""" |
| sys.stdout.write(f"odysseus {VERSION} — every feature, on the shell.\n\n") |
| sys.stdout.write("Usage: odysseus <subcommand> [args...]\n\n") |
| sys.stdout.write("Available subcommands:\n") |
| subs = _list_subcommands() |
| width = max((len(p.name[len("odysseus-"):]) for p in subs), default=10) |
| for p in subs: |
| name = p.name[len("odysseus-"):] |
| desc = _short_help(p) |
| sys.stdout.write(f" {name:<{width + 2}}{desc}\n") |
| sys.stdout.write( |
| "\nRun `odysseus help <subcommand>` for that tool's own --help.\n" |
| ) |
|
|
|
|
| def main(argv: list[str] | None = None) -> int: |
| argv = list(sys.argv[1:] if argv is None else argv) |
|
|
| if not argv or argv[0] in ("-h", "--help", "help") and len(argv) == 1: |
| _print_listing() |
| return 0 |
|
|
| if argv[0] in ("-v", "--version"): |
| sys.stdout.write(f"odysseus {VERSION}\n") |
| return 0 |
|
|
| if argv[0] == "help": |
| |
| if len(argv) < 2: |
| _print_listing() |
| return 0 |
| sub = SCRIPTS_DIR / f"odysseus-{argv[1]}" |
| if not _is_runnable_subcommand(sub): |
| sys.stderr.write(f"odysseus: unknown subcommand {argv[1]!r}\n") |
| return 1 |
| return subprocess.call([str(sub), "--help"]) |
|
|
| |
| name = argv[0] |
| sub = SCRIPTS_DIR / f"odysseus-{name}" |
| if not _is_runnable_subcommand(sub): |
| sys.stderr.write( |
| f"odysseus: unknown subcommand {name!r}. " |
| f"Try `odysseus help` to see available ones.\n" |
| ) |
| return 1 |
| |
| |
| |
| |
| repo_root = SCRIPTS_DIR.parent |
| venv_python = repo_root / "venv" / "bin" / "python" |
| py = str(venv_python) if venv_python.exists() else sys.executable |
| os.execv(py, [py, str(sub)] + argv[1:]) |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|