holt-cli 0.1.0__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.
Files changed (65) hide show
  1. holt/__init__.py +0 -0
  2. holt/agent/__init__.py +0 -0
  3. holt/agent/entry.py +86 -0
  4. holt/agent/findings.py +49 -0
  5. holt/agent/landing.py +154 -0
  6. holt/agent/pipeline.py +244 -0
  7. holt/agent/progression.py +408 -0
  8. holt/agent/signals.py +220 -0
  9. holt/agent/stages.py +533 -0
  10. holt/agent/verdict.py +226 -0
  11. holt/agent/verify.py +140 -0
  12. holt/baseline.py +89 -0
  13. holt/baseline_matched.py +116 -0
  14. holt/cli.py +616 -0
  15. holt/discover.py +497 -0
  16. holt/evidence/__init__.py +3 -0
  17. holt/evidence/fixtures.py +154 -0
  18. holt/evidence/github_graphql.py +538 -0
  19. holt/evidence/provider.py +77 -0
  20. holt/evidence/redact.py +79 -0
  21. holt/issues.py +41 -0
  22. holt/model.py +516 -0
  23. holt/profile.py +126 -0
  24. holt/report.py +157 -0
  25. holt/tui/__init__.py +0 -0
  26. holt/tui/animation.py +84 -0
  27. holt/tui/app.py +294 -0
  28. holt/tui/clipboard.py +89 -0
  29. holt/tui/commands.py +134 -0
  30. holt/tui/discovery.py +305 -0
  31. holt/tui/env.py +49 -0
  32. holt/tui/events.py +245 -0
  33. holt/tui/mascot.py +121 -0
  34. holt/tui/models.py +590 -0
  35. holt/tui/observe.py +297 -0
  36. holt/tui/screens/__init__.py +35 -0
  37. holt/tui/screens/assessment.py +337 -0
  38. holt/tui/screens/confirm.py +62 -0
  39. holt/tui/screens/discover.py +444 -0
  40. holt/tui/screens/home.py +519 -0
  41. holt/tui/screens/inspector.py +106 -0
  42. holt/tui/screens/live.py +335 -0
  43. holt/tui/screens/models.py +393 -0
  44. holt/tui/screens/next_steps.py +425 -0
  45. holt/tui/screens/profile.py +129 -0
  46. holt/tui/session.py +711 -0
  47. holt/tui/store.py +458 -0
  48. holt/tui/theme.py +479 -0
  49. holt/tui/visual.py +33 -0
  50. holt/tui/widgets/__init__.py +0 -0
  51. holt/tui/widgets/candidates.py +78 -0
  52. holt/tui/widgets/claims.py +59 -0
  53. holt/tui/widgets/disclosure.py +121 -0
  54. holt/tui/widgets/evidence.py +121 -0
  55. holt/tui/widgets/masthead.py +122 -0
  56. holt/tui/widgets/recent.py +167 -0
  57. holt/tui/widgets/scrolling.py +38 -0
  58. holt/tui/widgets/stages.py +232 -0
  59. holt/types.py +48 -0
  60. holt_cli-0.1.0.dist-info/METADATA +198 -0
  61. holt_cli-0.1.0.dist-info/RECORD +65 -0
  62. holt_cli-0.1.0.dist-info/WHEEL +4 -0
  63. holt_cli-0.1.0.dist-info/entry_points.txt +2 -0
  64. holt_cli-0.1.0.dist-info/licenses/LICENSE +201 -0
  65. holt_cli-0.1.0.dist-info/licenses/NOTICE +4 -0
@@ -0,0 +1,232 @@
1
+ """The stage list, and the moment Stage D removes a claim.
2
+
3
+ There is no progress bar here. A bar would have to invent a denominator — the
4
+ number of threads a stage will read is not known until it has read them — and a
5
+ bar that fills at a rate unrelated to the work is a lie told smoothly. What a
6
+ running stage shows instead is a spinner, which claims only that something is
7
+ happening, and an elapsed clock, which is a real number.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import time
13
+
14
+ from rich.text import Text
15
+ from textual.app import ComposeResult
16
+ from textual.containers import Vertical
17
+ from textual.widget import Widget
18
+
19
+ from holt.tui import animation, events, theme
20
+ from holt.tui.visual import Line
21
+ from holt.tui.widgets.evidence import cite
22
+
23
+
24
+ class StageRow(Line):
25
+ """One stage: a gutter mark, its name, and where it has got to.
26
+
27
+ Three states, and they are visually distinct without relying on colour:
28
+ pending is an em dash, running is a turning spinner and a clock, finished is
29
+ the stage's own one-line result.
30
+ """
31
+
32
+ def __init__(self, stage: str, **kwargs) -> None:
33
+ super().__init__(**kwargs)
34
+ self.stage = stage
35
+ self._model = ""
36
+ self._state = "pending"
37
+ self._status = ""
38
+ self._started = 0.0
39
+ self._elapsed = 0.0
40
+ self._tick = 0
41
+ self._timer = None
42
+
43
+ def on_mount(self) -> None:
44
+ self._repaint()
45
+
46
+ def started(self, model: str) -> None:
47
+ self._model = model
48
+ self._state = "running"
49
+ self._started = time.monotonic()
50
+ self._tick = 0
51
+ if self._timer is None and animation.enabled():
52
+ self._timer = self.set_interval(animation.SPINNER_INTERVAL, self._spin)
53
+ self._repaint()
54
+
55
+ def finished(self, summary: str, seconds: float) -> None:
56
+ self._state = "done"
57
+ self._status = summary or "done"
58
+ if self._started:
59
+ self._elapsed = time.monotonic() - self._started
60
+ self._stop()
61
+ self._repaint()
62
+
63
+ def _spin(self) -> None:
64
+ self._tick += 1
65
+ self._repaint()
66
+
67
+ def _stop(self) -> None:
68
+ if self._timer is not None:
69
+ self._timer.stop()
70
+ self._timer = None
71
+
72
+ def on_unmount(self) -> None:
73
+ self._stop()
74
+
75
+ def _repaint(self) -> None:
76
+ text = Text()
77
+ text.append(f"{events.stage_mark(self.stage):<2} ", style=theme.FAINT)
78
+ text.append(f"{events.stage_title(self.stage):<16}", style=theme.DIM)
79
+
80
+ if self._state == "pending":
81
+ text.append("—", style=theme.FAINT)
82
+ elif self._state == "running":
83
+ spin = animation.frame(self._tick) if animation.enabled() else "·"
84
+ text.append(f"{spin} ", style=theme.CITE)
85
+ elapsed = time.monotonic() - self._started if self._started else 0.0
86
+ text.append(animation.elapsed(elapsed), style=theme.FAINT)
87
+ else:
88
+ text.append(self._status)
89
+ if self._elapsed >= 0.05:
90
+ text.append(f" {animation.elapsed(self._elapsed)}", style=theme.FAINT)
91
+ self.update(text)
92
+
93
+
94
+ class StageList(Vertical):
95
+ """Rows for the stages we expect, plus any the engine grows later.
96
+
97
+ A stage the engine adds appears at the end of the list with a blank gutter
98
+ mark rather than being dropped on the floor, so the interface reports the
99
+ pipeline that ran instead of the pipeline it was written against.
100
+ """
101
+
102
+ def compose(self) -> ComposeResult:
103
+ for stage, _mark in events.STAGE_ORDER:
104
+ if stage in events.OPTIONAL_STAGES:
105
+ continue
106
+ yield StageRow(stage, id=f"stage-{stage}")
107
+
108
+ def row(self, stage: str) -> StageRow | None:
109
+ for row in self.query(StageRow):
110
+ if row.stage == stage:
111
+ return row
112
+ return None
113
+
114
+ def ensure(self, stage: str) -> StageRow:
115
+ existing = self.row(stage)
116
+ if existing is not None:
117
+ return existing
118
+ row = StageRow(stage)
119
+ self.mount(row)
120
+ animation.reveal(row)
121
+ return row
122
+
123
+ def settle(self) -> None:
124
+ """Stop every spinner. Called when the run ends, however it ended.
125
+
126
+ Without this, a failure part-way through leaves the stage it died on
127
+ turning forever, which reads as still working.
128
+ """
129
+ for row in self.query(StageRow):
130
+ if row._state == "running":
131
+ row._state = "pending"
132
+ row._stop()
133
+ row._repaint()
134
+
135
+
136
+ class EmittedFinding(Line):
137
+ """A claim a stage just made, with the ids it says support it.
138
+
139
+ Shown before Stage D has had its say, which is the point: the live view is
140
+ where you watch a claim arrive and then, sometimes, be taken away.
141
+ """
142
+
143
+ def __init__(
144
+ self, event: events.FindingEmitted, repo: str | None = None, **kwargs
145
+ ) -> None:
146
+ super().__init__(classes="finding", **kwargs)
147
+ self.event = event
148
+ self.repo = repo
149
+
150
+ def on_mount(self) -> None:
151
+ self.update(self._line())
152
+ animation.reveal(self)
153
+
154
+ def _line(self) -> Text:
155
+ event = self.event
156
+ text = Text()
157
+ if event.field == "thread_outcome" and isinstance(event.value, dict):
158
+ for eid in event.evidence_ids[:1]:
159
+ text.append_text(cite(eid, width=44, repo=self.repo))
160
+ text.append(" ")
161
+ text.append(str(event.value.get("outcome", "")), style=theme.DIM)
162
+ else:
163
+ text.append(f"{event.field} = {event.value}")
164
+ for eid in event.evidence_ids[:1]:
165
+ text.append(" ")
166
+ text.append_text(cite(eid, width=40, repo=self.repo))
167
+ return text
168
+
169
+
170
+ class DroppedFinding(Widget):
171
+ """Stage D removed a claim.
172
+
173
+ Given room rather than compressed to a line. This is the behaviour the whole
174
+ pipeline is arranged around, and the reason a reader can trust the rest of
175
+ the report: a claim the model made, whose evidence did not hold up, and which
176
+ was therefore dropped rather than softened into a hedge.
177
+ """
178
+
179
+ def __init__(self, event: events.FindingDropped, **kwargs) -> None:
180
+ super().__init__(**kwargs)
181
+ self.event = event
182
+
183
+ def on_mount(self) -> None:
184
+ animation.reveal(self)
185
+
186
+ def compose(self) -> ComposeResult:
187
+ event = self.event
188
+ head = Text()
189
+ head.append("✗ ", style=theme.DROP)
190
+ head.append(f"{event.field} = {event.value!r}")
191
+ yield Line(head)
192
+
193
+ unquoted = getattr(event, "reason", "unresolved") == "unquoted"
194
+ for eid in event.cited:
195
+ row = Text(" ")
196
+ row.append("cited ", style=theme.DIM)
197
+ # An unquoted claim cites a thread that is perfectly real; it is the
198
+ # words that are not there, and colouring the id as unresolvable
199
+ # would say the opposite of what happened.
200
+ row.append_text(cite(eid, resolves=unquoted, width=50))
201
+ row.append(
202
+ " says no such thing" if unquoted else " does not resolve",
203
+ style=theme.DROP,
204
+ )
205
+ yield Line(row)
206
+ if not event.cited:
207
+ row = Text(" ")
208
+ row.append("cited ", style=theme.DIM)
209
+ row.append("no evidence at all", style=theme.DROP)
210
+ yield Line(row)
211
+
212
+ yield Line(
213
+ Text(
214
+ "Dropped, not softened. The quoted words are not in that thread, so "
215
+ "the claim does not reach the reader."
216
+ if unquoted else
217
+ "Dropped, not softened. The claim was asserted without a record that "
218
+ "carries it, so it does not reach the reader.",
219
+ style=theme.FAINT,
220
+ ),
221
+ classes="drop-reason",
222
+ )
223
+
224
+
225
+ def unknown(event: object) -> Line:
226
+ """Anything this file has no specific rendering for.
227
+
228
+ An event added to the schema after this screen was written must still
229
+ produce a truthful line. Nothing in the UI may raise on an event it has not
230
+ seen before.
231
+ """
232
+ return Line(Text(events.describe(event), style=theme.FAINT), classes="finding")
holt/types.py ADDED
@@ -0,0 +1,48 @@
1
+ """Core types shared by the evidence layer, the agent stages, and the eval harness."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import UTC, datetime
7
+ from enum import Enum
8
+ from typing import Any
9
+
10
+ # The temporal holdout boundary.
11
+ #
12
+ # The agent may only see records at or before T; labels are computed only from
13
+ # records after T. T sits just past the model's May 2026 training cutoff so that
14
+ # the label window (Jun-Aug 2026) falls outside training data entirely. Moving T
15
+ # earlier would drag the label window *into* training data, which is the leak
16
+ # that actually matters.
17
+ T_CUTOFF = datetime(2026, 6, 1, tzinfo=UTC)
18
+
19
+
20
+ class Window(str, Enum):
21
+ """Which side of the holdout a provider is permitted to read."""
22
+
23
+ PRE_T = "pre_t"
24
+ POST_T = "post_t"
25
+
26
+
27
+ @dataclass(frozen=True, slots=True)
28
+ class EvidenceRecord:
29
+ """A single retrieved fact, addressable by a stable id.
30
+
31
+ Every claim Holt makes must cite an ``evidence_id`` that resolves back to one
32
+ of these. Stage D drops any finding whose id does not.
33
+ """
34
+
35
+ evidence_id: str
36
+ source: str
37
+ url: str
38
+ timestamp: datetime
39
+ payload: dict[str, Any]
40
+
41
+ def __post_init__(self) -> None:
42
+ if self.timestamp.tzinfo is None:
43
+ raise ValueError(
44
+ f"{self.evidence_id}: timestamp must be timezone-aware; "
45
+ "naive datetimes cannot be compared against the holdout boundary"
46
+ )
47
+ if not self.evidence_id:
48
+ raise ValueError("evidence_id must be non-empty")
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.5
2
+ Name: holt-cli
3
+ Version: 0.1.0
4
+ Summary: Evidence-backed assessment of whether a GitHub repository is a viable opportunity for an external contributor.
5
+ Project-URL: Homepage, https://github.com/holt-oss/holt
6
+ Project-URL: Documentation, https://github.com/holt-oss/holt/tree/main/docs
7
+ Project-URL: Repository, https://github.com/holt-oss/holt.git
8
+ Project-URL: Issues, https://github.com/holt-oss/holt/issues
9
+ Project-URL: Changelog, https://github.com/holt-oss/holt/blob/main/CHANGELOG.md
10
+ Project-URL: Security, https://github.com/holt-oss/holt/security/policy
11
+ Author-email: Aahil Khan <aahilminookhan@gmail.com>
12
+ Maintainer-email: Aahil Khan <aahilminookhan@gmail.com>
13
+ License-Expression: Apache-2.0
14
+ License-File: LICENSE
15
+ License-File: NOTICE
16
+ Keywords: cli,contributors,github,open-source,repository-analysis
17
+ Classifier: Development Status :: 3 - Alpha
18
+ Classifier: Environment :: Console
19
+ Classifier: Intended Audience :: Developers
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3 :: Only
23
+ Classifier: Topic :: Software Development
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: anthropic>=0.60
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: openai>=3.6.0
28
+ Requires-Dist: textual>=0.80
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Holt
32
+
33
+ [![CI](https://github.com/holt-oss/holt/actions/workflows/ci.yml/badge.svg)](https://github.com/holt-oss/holt/actions/workflows/ci.yml)
34
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-83a9ff.svg)](https://github.com/holt-oss/holt/blob/main/LICENSE)
35
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-69c7a6.svg)](https://github.com/holt-oss/holt/blob/main/pyproject.toml)
36
+
37
+ **Choose an open-source repository that is worth your time.**
38
+
39
+ Holt reads recent GitHub contribution history and shows what happens when
40
+ outside contributors try to help: whether they receive a response, whether
41
+ substantive work gets reviewed and merged, and where newcomer contributions
42
+ actually land. It turns that evidence into a verdict you can inspect before
43
+ committing days or weeks to a project.
44
+
45
+ ![Holt terminal interface](https://raw.githubusercontent.com/holt-oss/holt/main/assets/holt.png)
46
+
47
+ Holt is read-only. It never opens pull requests, posts comments, or contacts
48
+ maintainers. Every factual claim in a full report links back to the pull request,
49
+ review, or comment that supports it.
50
+
51
+ > Winner of **Most useful real-world workflow** at the micro1 Frontier
52
+ > Engineering Challenge. Holt is now maintained as an independent open-source
53
+ > product by [`holt-oss`](https://github.com/holt-oss).
54
+
55
+ ## Install
56
+
57
+ Holt is pre-1.0. You need [`uv`](https://docs.astral.sh/uv/) and Python 3.11 or
58
+ newer.
59
+
60
+ ```sh
61
+ uv tool install holt-cli
62
+ ```
63
+
64
+ Create a classic GitHub token with no scopes and expose it to Holt:
65
+
66
+ ```sh
67
+ export GITHUB_TOKEN=your_token
68
+ ```
69
+
70
+ Start with the rules-only report. It calls no model and needs no model API key:
71
+
72
+ ```sh
73
+ holt analyze pallets/flask --live --no-model
74
+ ```
75
+
76
+ For a written report with cited findings and quotations, configure a supported
77
+ model provider and run the full analysis:
78
+
79
+ ```sh
80
+ export OPENAI_API_KEY=your_key
81
+ holt analyze pallets/flask --live
82
+ ```
83
+
84
+ Run `holt models` to inspect or change the provider. OpenAI, Anthropic, Gemini,
85
+ Ollama, and OpenAI-compatible endpoints are supported.
86
+
87
+ ## Use the terminal interface
88
+
89
+ Run Holt without a subcommand to open the interactive terminal interface:
90
+
91
+ ```sh
92
+ holt
93
+ ```
94
+
95
+ From there you can assess a repository, revisit past reports, inspect the
96
+ evidence behind a claim, compare candidates, and configure model providers.
97
+
98
+ ## What you get
99
+
100
+ A repository assessment answers five practical questions:
101
+
102
+ - Do first-time contributors get meaningful work merged?
103
+ - How quickly does someone usually respond?
104
+ - Are pull requests reviewed by people or accepted mechanically?
105
+ - Which parts of the codebase have accepted outsider work?
106
+ - What specific evidence supports the verdict?
107
+
108
+ The headline is one of:
109
+
110
+ | Verdict | Meaning |
111
+ |---|---|
112
+ | **Worth your time** | There is evidence that outsiders can land useful work. |
113
+ | **Not worth your time** | The observed contribution path is a poor fit for the time you have. |
114
+ | **Not enough evidence** | Holt cannot support either conclusion from the available history. |
115
+
116
+ Treat the verdict as a filter, not an oracle. Open the cited evidence before
117
+ making a significant commitment.
118
+
119
+ ## Commands
120
+
121
+ | Command | Purpose |
122
+ |---|---|
123
+ | `holt analyze <owner/repo> --live` | Assess one repository using current GitHub data. |
124
+ | `holt compare <repo>… --live` | Compare repositories side by side. |
125
+ | `holt profile` | Save the languages, topics, contribution type, and time budget you want. |
126
+ | `holt discover --live` | Find and screen repositories for that profile. |
127
+ | `holt next <repo> --as <login> --live` | Rank open issues after you have contributed to a project. |
128
+ | `holt models` | Inspect or change the model provider. |
129
+ | `holt tui` | Open the interactive terminal interface. |
130
+
131
+ See [USAGE.md](https://github.com/holt-oss/holt/blob/main/USAGE.md) for the
132
+ complete workflow and
133
+ [docs/COMMANDS.md](https://github.com/holt-oss/holt/blob/main/docs/COMMANDS.md)
134
+ for command details.
135
+
136
+ ## How it works
137
+
138
+ Holt separates evidence collection, interpretation, verification, and the final
139
+ decision:
140
+
141
+ ```text
142
+ GitHub history
143
+ ↓
144
+ contribution signals + cited findings
145
+ ↓
146
+ evidence verification
147
+ ↓
148
+ deterministic verdict
149
+ ↓
150
+ written explanation
151
+ ```
152
+
153
+ The model may interpret threads and explain the result, but it does not choose
154
+ the verdict. Unsupported findings are removed before the decision is computed.
155
+ The same verified inputs therefore produce the same verdict.
156
+
157
+ The architecture and its boundaries are documented in
158
+ [docs/DESIGN.md](https://github.com/holt-oss/holt/blob/main/docs/DESIGN.md).
159
+
160
+ ## Evaluation and limitations
161
+
162
+ Holt has been measured on two temporally held-out repository pools. On the
163
+ out-of-sample pool it reached 0.82 balanced accuracy and 0.63 Matthews
164
+ correlation, compared with 0.61 and 0.21 for a single prompt over repository
165
+ metadata and documentation. The committed fixtures and recordings make those
166
+ results reproducible.
167
+
168
+ The evaluation is evidence, not a guarantee. Repository cultures change, quiet
169
+ periods can look worse than they are, and no benchmark captures whether a
170
+ particular issue matches your skills. Holt is designed to show its work so you
171
+ can disagree with it intelligently.
172
+
173
+ - [Evaluation design and limitations](https://github.com/holt-oss/holt/blob/main/docs/EVALUATION.md)
174
+ - [Reproduce the published results](https://github.com/holt-oss/holt/blob/main/REPRODUCTION.md)
175
+ - [Recorded example analyses](https://github.com/holt-oss/holt/blob/main/trajectories/README.md)
176
+
177
+ ## Contributing
178
+
179
+ Holt welcomes focused improvements to evidence providers, verdict rules,
180
+ terminal UX, documentation, evaluation, and platform support.
181
+
182
+ ```sh
183
+ git clone https://github.com/holt-oss/holt.git
184
+ cd holt
185
+ uv sync
186
+ uv run pytest -rs
187
+ ```
188
+
189
+ Read [CONTRIBUTING.md](https://github.com/holt-oss/holt/blob/main/CONTRIBUTING.md)
190
+ before opening a pull request. Community participation is governed by the
191
+ [Code of Conduct](https://github.com/holt-oss/holt/blob/main/CODE_OF_CONDUCT.md),
192
+ and security reports follow
193
+ [SECURITY.md](https://github.com/holt-oss/holt/blob/main/SECURITY.md).
194
+
195
+ ## License
196
+
197
+ Holt is licensed under the
198
+ [Apache License 2.0](https://github.com/holt-oss/holt/blob/main/LICENSE).
@@ -0,0 +1,65 @@
1
+ holt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ holt/baseline.py,sha256=Ros5hIpPKezf7gLK_Gt8YfUNO_-sO68y-RmpQYs0v-M,3163
3
+ holt/baseline_matched.py,sha256=XLQVNXPAWtzruhCkWcLRl6VvIbhgr03IqtUEaCFnsaE,4791
4
+ holt/cli.py,sha256=lsCBohRH9jZ2C-Xev6uQxLupZdnr3qehS02TT-3ffnI,26167
5
+ holt/discover.py,sha256=mCFJwno7mOs66pNej-Msw3j3KKWPnoUFPlm9XNeLgoI,20786
6
+ holt/issues.py,sha256=250OcM1IPQ9UT7VgV2EHpfu74yLcM34OQ8Ipp1aujrY,1543
7
+ holt/model.py,sha256=wJXU64FRuLN4Jbk3MqqXRseN59tsxacMDqbsRAj5mf4,20112
8
+ holt/profile.py,sha256=iCwfkqMqA39EgtUWoQXM0jqhezkktct3WSFW2BxPh3s,4921
9
+ holt/report.py,sha256=PkZCUfEJyttRLEJz_LdqIehpC15WQ5uSGmL_9PP6YbM,6556
10
+ holt/types.py,sha256=wzRi6iQvs_aSYRrQjJl3K5fHPyvvAoK36o9yuLA47rE,1549
11
+ holt/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ holt/agent/entry.py,sha256=F_ouy81cFmwIHeYbNw_xxwHSvVc7tyFivnlL9mx4IUg,3798
13
+ holt/agent/findings.py,sha256=t_XsTM7DhRT_pWb2oElIp9tfEDtdq6__obpuBJYeb1Y,1495
14
+ holt/agent/landing.py,sha256=cVQ2om1qNGivLXQAYmfC4HSWsinYG4G_qHPKwQNXTrU,5980
15
+ holt/agent/pipeline.py,sha256=eQqZ_NL2o-Fcia2enmWESU6dpQlmyxekXpsb6Ea7Kpk,10488
16
+ holt/agent/progression.py,sha256=4Rpjmo2qDFxbTfl8MDy9Y8TqPYCKzEanCo7YuwxDE80,15991
17
+ holt/agent/signals.py,sha256=H4qhVfN87Q-vuROZ_JQLJJ59THYH2nuwF5r9nusPlM8,9258
18
+ holt/agent/stages.py,sha256=lsLK7CPjo4o4PxBV4oOaJa3z_msBBu5xI3MTmdrfZWM,21153
19
+ holt/agent/verdict.py,sha256=10pC7aoQvJl9yZzpKtQ1QE4Y7KvQ7H7NZ8uEGHgmFOE,10268
20
+ holt/agent/verify.py,sha256=_PwvVYoRxQtOentE0502seEh78bR07goQ83We6Kf7-g,5760
21
+ holt/evidence/__init__.py,sha256=xbcUlLUkTIbO0eWf53MGAE6J7sul38Zqt5Xq2d1ztfM,126
22
+ holt/evidence/fixtures.py,sha256=We9lGRgKST6sJJ6c6GrIxLaxSZks1ODdEcnaUEgLtf0,5358
23
+ holt/evidence/github_graphql.py,sha256=EUaK_0BrR7Mvel8J378cOT-8fzan0DfsoXdyE7NyHmc,20789
24
+ holt/evidence/provider.py,sha256=zjT-Tn4wPfr5E7kf20qSjMOBX_LSzh4nO2bc-hpQhSg,3154
25
+ holt/evidence/redact.py,sha256=pRivtIIrnJlsc-V2KO_kzA15yapNPZGeu08cmPoNM3k,2793
26
+ holt/tui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ holt/tui/animation.py,sha256=IGjD6pQDijhZ2CVroffVSL5urXBJCXeGHnUABC9EH3s,3017
28
+ holt/tui/app.py,sha256=2irYvm_HKLaECM4oexhDsOn12C1AnktSzyMF4jhk2S0,12867
29
+ holt/tui/clipboard.py,sha256=ObyGDFj8c7ytvOFIzl7tmemfmn9XZ5wfYn-lNg59Bvo,3632
30
+ holt/tui/commands.py,sha256=Up2Ysz5YkvKCVie-29ay83fpYm-f7mK3XOF6bKxXlMA,5788
31
+ holt/tui/discovery.py,sha256=2BCdJ6Zp2KheaAI_D6shV7h-JedeqlfJNjlvo4h-ccY,11704
32
+ holt/tui/env.py,sha256=CQ5HB8DxO0uRkcK1rI0V2VscaDAEMOaMy8OYkqHvRzM,1697
33
+ holt/tui/events.py,sha256=T4qrzVckOqLzKn-H07Xu2nDDNjD1w-s723FtfoOCTwQ,7609
34
+ holt/tui/mascot.py,sha256=6OVYRekH9MDMXqDCQOtK0Tfqhbt2FRt-RdakXSZaYq8,4670
35
+ holt/tui/models.py,sha256=_NHxrW7oXq1YItLxr7paQ9aM1zCcOeEoGFu3SetC-gk,23351
36
+ holt/tui/observe.py,sha256=W0oEtTPnAp9YMOHNEFLdysUKC8bXI0kzrDevuln0Q_c,11355
37
+ holt/tui/session.py,sha256=m83m2y5y0AQ3CkWY8ORGidNKNG1MTp61aY6SVaTUNqU,30330
38
+ holt/tui/store.py,sha256=ZZqUlUM_t7Krq4FQ6vTTUd7lMK4rjGO2k64IBNGbwdM,17937
39
+ holt/tui/theme.py,sha256=O3MJeLcYVl7N2PyxINF5z2OpUE_eKycslispwy4KUy0,13067
40
+ holt/tui/visual.py,sha256=C8uGapA1DvxE2v5e9AYUYF1PX8hHymKbIhWBV2na1w8,1148
41
+ holt/tui/screens/__init__.py,sha256=wbGP0Q_YuK4JObPxtX4xrkCdTy5i_6FsEhWKrhSZWak,1051
42
+ holt/tui/screens/assessment.py,sha256=tHYSkaJqO461pkx0mW9aJBVN39QftVC3tgZD_DbqEVs,14319
43
+ holt/tui/screens/confirm.py,sha256=DHFKJfQDWlTJW30ZPMwVjXC1d0kwE8WLvZj4LH_99iM,1945
44
+ holt/tui/screens/discover.py,sha256=gZHLUYi2JMtd70gM3rhmIrhaB9V-nSN6IfPWg5Ag_6Q,18660
45
+ holt/tui/screens/home.py,sha256=5zVJMa6eFZJ9JsMOuSdUCyEV8HNCMI_Adx1DeSiQ1yc,23159
46
+ holt/tui/screens/inspector.py,sha256=PpX0gQiioT8LxMjuwwSMLKy75F4X1igOZWJ_sevNOCI,3953
47
+ holt/tui/screens/live.py,sha256=vLRQbSuK5tAATrZEy-4jjlFe3OCLSLLZw9X1mEK5Gao,14180
48
+ holt/tui/screens/models.py,sha256=7t-zQk-JK-LnjILwuQPIJL3Dru-zENfPTf9z-lmdILc,15376
49
+ holt/tui/screens/next_steps.py,sha256=dyyf06Ft3qqSnxLMKXrh0H0-yLPRJUF41jPIS76l4hg,18654
50
+ holt/tui/screens/profile.py,sha256=lK2SGllhUwFhmmqH1t7wa9nf3zvSC0NwAz_X7PPZHMo,4730
51
+ holt/tui/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ holt/tui/widgets/candidates.py,sha256=pK77I3e0c0e0a-23OetG8ZszWuzptzfXX5gmnxG6FoI,2720
53
+ holt/tui/widgets/claims.py,sha256=UjCm354yQAM5CYcDj-pvGKN51coRUXtZ9VM_1tKGMew,2035
54
+ holt/tui/widgets/disclosure.py,sha256=U6kHO1ouosEe301kDQ_JqbgHtCR9yCOnbIPGZsC0kAc,4523
55
+ holt/tui/widgets/evidence.py,sha256=3znmJpQ32eSKBNqMorE2e345C7LS4VbBqDJgodYOFQ8,4181
56
+ holt/tui/widgets/masthead.py,sha256=fEK8ShfZquDU_wXQQBNJT772So7y9QQxW221koxsFks,4400
57
+ holt/tui/widgets/recent.py,sha256=upzuQcnbWaxYTmbSpERiltQe5hBbT_cIZ-vanwOq57I,6302
58
+ holt/tui/widgets/scrolling.py,sha256=X7NCOQ4kXpG7YPc5ndJ0htZrfT_ds03C-IIkZ0BPZdg,1747
59
+ holt/tui/widgets/stages.py,sha256=hlsIzDVhbUwXugYElAWgwn8eFRXPxGJ5nU6853HizHE,8153
60
+ holt_cli-0.1.0.dist-info/METADATA,sha256=-QyBSwCv_nHElq05s17pyGcNDs9Wf6O6aDkJ7z47w9U,7464
61
+ holt_cli-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
62
+ holt_cli-0.1.0.dist-info/entry_points.txt,sha256=nv3JMCxhB1SvX6AW7vjfHjez7bKJP6GiNHQoB3KrPQ0,39
63
+ holt_cli-0.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
64
+ holt_cli-0.1.0.dist-info/licenses/NOTICE,sha256=6qCav9tAXKnPlsDpGdbqDRJFONQnNBDGiz5xC3eersQ,96
65
+ holt_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ holt = holt.cli:main