throughline-ratify 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2026 Henry J Grech-Cini
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """throughline-ratify — a full-screen terminal companion for
4
+ working through the throughline items that await human ratification."""
5
+ from __future__ import annotations
6
+
7
+ from importlib.metadata import PackageNotFoundError, version as _version
8
+
9
+ try:
10
+ __version__ = _version("throughline-ratify")
11
+ except PackageNotFoundError: # running from a source tree
12
+ __version__ = "0.0.0+unknown"
13
+
14
+ __all__ = ["__version__"]
@@ -0,0 +1,94 @@
1
+ # Copyright (c) 2026 Henry J Grech-Cini
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """``tl-ratify`` — the entry point.
4
+
5
+ Launches the full-screen ratification cockpit over the throughline project
6
+ enclosing ``--path`` (default: the current directory). ``--list`` prints the same
7
+ worklist to stdout without curses, for pipelines, CI logs and quick glances.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import sys
13
+ from importlib.metadata import PackageNotFoundError, version as _pkg_version
14
+
15
+ from . import core
16
+
17
+
18
+ def _v(name: str) -> str:
19
+ try:
20
+ return _pkg_version(name)
21
+ except PackageNotFoundError: # pragma: no cover - source tree
22
+ return "0.0.0+unknown"
23
+
24
+
25
+ def _version_string() -> str:
26
+ return (
27
+ f"tl-ratify {_v('throughline-ratify')} "
28
+ f"(throughline-compose {_v('throughline-compose')}, throughline {_v('throughline')})"
29
+ )
30
+
31
+
32
+ def build_parser() -> argparse.ArgumentParser:
33
+ p = argparse.ArgumentParser(
34
+ prog="tl-ratify",
35
+ description="A full-screen assistant for ratifying throughline items (compose-aware).",
36
+ )
37
+ p.add_argument("--version", action="version", version=_version_string())
38
+ p.add_argument("-C", "--path", default=".", help="project root or a path within it (default: .)")
39
+ p.add_argument("--by", default=None,
40
+ help="the ratifier recorded on sign-off (default: the current user)")
41
+ p.add_argument("--list", action="store_true",
42
+ help="print the ratification worklist and exit (no TUI)")
43
+ p.add_argument("--all", action="store_true",
44
+ help="with --list, include already-ratified items")
45
+ p.add_argument("--sort", choices=core.SORTS, default="concern",
46
+ help="worklist ordering: concern (default), roots (shallowest "
47
+ "grounding depth first) or leaves (deepest first)")
48
+ return p
49
+
50
+
51
+ def _print_list(session: core.Session, show_all: bool, sort: str) -> int:
52
+ rows = core.build_queue(session, show_all=show_all, sort=sort)
53
+ scope = "composed union" if session.composed else "local graph"
54
+ done, total = core.ratification_progress(session)
55
+ print(f"{session.project_name} \u2014 {scope} \u2502 {done}/{total} ratified")
56
+ if session.composed:
57
+ for s in session.sources:
58
+ print(f" source {s.namespace}: {s.location}")
59
+ if not rows:
60
+ print("nothing to ratify \u2014 all clear")
61
+ return 0
62
+ header = "item(s) shown" if show_all else "item(s) pending ratification"
63
+ print(f"{len(rows)} {header} (sort: {sort}):\n")
64
+ for r in rows:
65
+ mark = "ratify-ready" if r.ratifiable_now else r.concern
66
+ print(f" {r.icon} {r.uid:<14} [{r.status:<10}] {mark:<12} {r.title}")
67
+ return 0
68
+
69
+
70
+ def main(argv: list[str] | None = None) -> int:
71
+ args = build_parser().parse_args(argv)
72
+ try:
73
+ session = core.open_session(args.path)
74
+ except core.RatifierError as exc:
75
+ print(f"tl-ratify: {exc}", file=sys.stderr)
76
+ return 2
77
+
78
+ if args.list:
79
+ return _print_list(session, args.all, args.sort)
80
+
81
+ if not sys.stdout.isatty():
82
+ print("tl-ratify: not a terminal; use --list for non-interactive output",
83
+ file=sys.stderr)
84
+ return 2
85
+
86
+ from . import tui # deferred: only import curses when we actually open the UI
87
+
88
+ ratifier = args.by or core.default_ratifier()
89
+ tui.run(session, ratifier)
90
+ return 0
91
+
92
+
93
+ if __name__ == "__main__": # pragma: no cover
94
+ raise SystemExit(main())