claudeglass 0.9.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.
- claudeglass/__init__.py +192 -0
- claudeglass/__main__.py +17 -0
- claudeglass/advice.py +943 -0
- claudeglass/backtest.py +315 -0
- claudeglass/baseline.py +705 -0
- claudeglass/cache.py +576 -0
- claudeglass/capture.py +961 -0
- claudeglass/capture_catalogue.py +1769 -0
- claudeglass/capture_tags.py +348 -0
- claudeglass/capture_view.py +748 -0
- claudeglass/carry.py +938 -0
- claudeglass/change_points.py +603 -0
- claudeglass/classify.py +1137 -0
- claudeglass/claude_md_review.py +1074 -0
- claudeglass/cli.py +4775 -0
- claudeglass/coaching.py +130 -0
- claudeglass/compaction.py +763 -0
- claudeglass/compaction_sim.py +1609 -0
- claudeglass/compare.py +733 -0
- claudeglass/config.py +1297 -0
- claudeglass/context_budget.py +1168 -0
- claudeglass/context_files.py +322 -0
- claudeglass/corpus.py +435 -0
- claudeglass/counterfactual.py +385 -0
- claudeglass/discovery.py +691 -0
- claudeglass/elasticity.py +936 -0
- claudeglass/events.py +1631 -0
- claudeglass/exports.py +497 -0
- claudeglass/fixes.py +1057 -0
- claudeglass/footprint.py +631 -0
- claudeglass/habits.py +3196 -0
- claudeglass/handoff.py +552 -0
- claudeglass/helptext.py +4513 -0
- claudeglass/hook_costs.py +582 -0
- claudeglass/hook_health.py +1349 -0
- claudeglass/hooks/__init__.py +0 -0
- claudeglass/hooks/capture-catalogue.json +418 -0
- claudeglass/hooks/capture-hook.py +971 -0
- claudeglass/hooks/snapshot-config.py +2089 -0
- claudeglass/ignores.py +218 -0
- claudeglass/impact.py +619 -0
- claudeglass/installer.py +772 -0
- claudeglass/invocation.py +225 -0
- claudeglass/jsonl.py +133 -0
- claudeglass/limits.py +794 -0
- claudeglass/model.py +1280 -0
- claudeglass/model_gate.py +156 -0
- claudeglass/model_swap.py +1060 -0
- claudeglass/monthly.py +572 -0
- claudeglass/onboarding.py +838 -0
- claudeglass/pages.py +192 -0
- claudeglass/parse.py +1895 -0
- claudeglass/phases.py +327 -0
- claudeglass/pricing.py +1160 -0
- claudeglass/pricing.toml +284 -0
- claudeglass/probe.py +253 -0
- claudeglass/profiles/__init__.py +53 -0
- claudeglass/profiles/apply.py +1060 -0
- claudeglass/profiles/catalogue/discovery-scrape.toml +34 -0
- claudeglass/profiles/catalogue/implementation-heavy.toml +36 -0
- claudeglass/profiles/catalogue/interactive-chat.toml +26 -0
- claudeglass/profiles/catalogue/overnight-batch.toml +33 -0
- claudeglass/profiles/catalogue/overseer-fanout.toml +33 -0
- claudeglass/profiles/catalogue/plan-then-build.toml +27 -0
- claudeglass/profiles/catalogue/planning-requirements.toml +27 -0
- claudeglass/profiles/catalogue/workflow-ultracode.toml +35 -0
- claudeglass/profiles/catalogue.py +215 -0
- claudeglass/profiles/diff.py +353 -0
- claudeglass/profiles/frontmatter.py +618 -0
- claudeglass/profiles/goals.py +777 -0
- claudeglass/profiles/schema.py +703 -0
- claudeglass/quality.py +1309 -0
- claudeglass/quick_actions.py +1192 -0
- claudeglass/recache.py +1005 -0
- claudeglass/recommend.py +2526 -0
- claudeglass/reconcile.py +778 -0
- claudeglass/render/__init__.py +12 -0
- claudeglass/render/csv_out.py +98 -0
- claudeglass/render/html.py +417 -0
- claudeglass/render/json_out.py +86 -0
- claudeglass/render/markdown.py +238 -0
- claudeglass/render/tables.py +258 -0
- claudeglass/report.py +2162 -0
- claudeglass/run_split.py +586 -0
- claudeglass/savers.py +1320 -0
- claudeglass/scorecard.py +455 -0
- claudeglass/service/__init__.py +54 -0
- claudeglass/service/api.py +3163 -0
- claudeglass/service/coaching_job.py +109 -0
- claudeglass/service/codewatch.py +202 -0
- claudeglass/service/contracts.py +329 -0
- claudeglass/service/explain.py +147 -0
- claudeglass/service/monthly_job.py +128 -0
- claudeglass/service/rebuild.py +244 -0
- claudeglass/service/schema.py +506 -0
- claudeglass/service/serve.py +381 -0
- claudeglass/service/static/THIRD_PARTY.sha256 +7 -0
- claudeglass/service/static/api.js +428 -0
- claudeglass/service/static/app.css +5087 -0
- claudeglass/service/static/app.js +874 -0
- claudeglass/service/static/charts-types.js +2416 -0
- claudeglass/service/static/charts.js +802 -0
- claudeglass/service/static/core.js +236 -0
- claudeglass/service/static/costs.js +239 -0
- claudeglass/service/static/d3.js +14 -0
- claudeglass/service/static/evidence.js +261 -0
- claudeglass/service/static/favicon.svg +8 -0
- claudeglass/service/static/fonts/InterVariable-4.1.woff2 +0 -0
- claudeglass/service/static/fonts/JetBrainsMono-2.304-Medium.woff2 +0 -0
- claudeglass/service/static/fonts/JetBrainsMono-2.304-Regular.woff2 +0 -0
- claudeglass/service/static/fonts/LICENSE-Inter-4.1.txt +92 -0
- claudeglass/service/static/fonts/OFL-JetBrainsMono-2.304.txt +93 -0
- claudeglass/service/static/format.js +471 -0
- claudeglass/service/static/grid.js +1413 -0
- claudeglass/service/static/icons.js +110 -0
- claudeglass/service/static/index.html +55 -0
- claudeglass/service/static/links.js +541 -0
- claudeglass/service/static/page-actions.js +1170 -0
- claudeglass/service/static/page-agents.js +325 -0
- claudeglass/service/static/page-cache.js +219 -0
- claudeglass/service/static/page-capture.js +503 -0
- claudeglass/service/static/page-data.js +103 -0
- claudeglass/service/static/page-glossary.js +464 -0
- claudeglass/service/static/page-habits.js +236 -0
- claudeglass/service/static/page-overview.js +819 -0
- claudeglass/service/static/page-setup.js +1181 -0
- claudeglass/service/static/page-spend.js +756 -0
- claudeglass/service/static/palette.js +838 -0
- claudeglass/service/static/shell.js +554 -0
- claudeglass/service/static/theme-boot.js +20 -0
- claudeglass/service/static/ui.js +1140 -0
- claudeglass/service/static/vendor/LICENSE-d3-7.9.0 +13 -0
- claudeglass/service/static/vendor/d3-7.9.0.min.js +2 -0
- claudeglass/service/store.py +2039 -0
- claudeglass/service/storelock.py +166 -0
- claudeglass/service/watcher.py +1415 -0
- claudeglass/setup_flow.py +795 -0
- claudeglass/setup_status.py +361 -0
- claudeglass/shell_writes.py +492 -0
- claudeglass/signals.py +257 -0
- claudeglass/skills_review.py +571 -0
- claudeglass/snapshots.py +1308 -0
- claudeglass/statusline.py +2273 -0
- claudeglass/team.py +650 -0
- claudeglass/tools/__init__.py +10 -0
- claudeglass/tools/log_usage.py +663 -0
- claudeglass/tools/scrub.py +809 -0
- claudeglass/topology.py +1122 -0
- claudeglass/ttl.py +1995 -0
- claudeglass/units.py +129 -0
- claudeglass/upgrade.py +147 -0
- claudeglass/usage.py +311 -0
- claudeglass/waste.py +806 -0
- claudeglass/whatif.py +467 -0
- claudeglass/workflows.py +299 -0
- claudeglass/workstyle.py +315 -0
- claudeglass-0.9.0.dist-info/METADATA +596 -0
- claudeglass-0.9.0.dist-info/RECORD +162 -0
- claudeglass-0.9.0.dist-info/WHEEL +5 -0
- claudeglass-0.9.0.dist-info/entry_points.txt +2 -0
- claudeglass-0.9.0.dist-info/licenses/LICENSE +21 -0
- claudeglass-0.9.0.dist-info/top_level.txt +1 -0
claudeglass/__init__.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""claudeglass: config-aware token and prompt-cache analytics for
|
|
2
|
+
Claude Code transcripts.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
__version__ = "0.9.0"
|
|
8
|
+
|
|
9
|
+
#: Bump when transcript-parsing logic changes in a way that could change
|
|
10
|
+
#: results computed from a previously cached file.
|
|
11
|
+
#:
|
|
12
|
+
#: Bumped to 2 by the wp11-cache/wp12a-fixtures merge: parse.py's
|
|
13
|
+
#: ttl_split_unknown/pre_split_turns fix (main's 709239a) and
|
|
14
|
+
#: workstyle.py's chat-only-before-single-model reorder (main's 0f892e3)
|
|
15
|
+
#: both landed on main after wp11-cache branched from 25c68c1 (before
|
|
16
|
+
#: PARSER_VERSION existed), so a cache entry written under the old
|
|
17
|
+
#: parsing behaviour must not be treated as still valid.
|
|
18
|
+
#:
|
|
19
|
+
#: Bumped to 3 by f99901f (fix(privacy): redact relative Windows paths
|
|
20
|
+
#: and @-tokens at parse time): ``_redact_paths`` in parse.py now catches
|
|
21
|
+
#: relative ``Users\``/``home/`` paths and any ``@``-bearing token that
|
|
22
|
+
#: previously survived redaction into ``cmd_prefix``, so a digest cached
|
|
23
|
+
#: from before that fix reflects a leakier ``cmd_prefix`` and must be
|
|
24
|
+
#: treated as stale.
|
|
25
|
+
#:
|
|
26
|
+
#: Bumped to 4 by the capture-improvements batch: parse.py now derives
|
|
27
|
+
#: seven new additive ``Turn`` fields from a transcript's raw lines
|
|
28
|
+
#: (``tool_wait_s``/``model_latency_s``/``tool_result_chars_by_tool``,
|
|
29
|
+
#: ``agent_brief_chars``/``tool_input_chars_by_tool``,
|
|
30
|
+
#: ``read_target_hashes``, ``human_prompt_chars``/``human_prompt_has_paste``
|
|
31
|
+
#: -- see model.py's module docstring), none of which a pre-batch digest
|
|
32
|
+
#: cache entry ever computed, so it must not be treated as still valid.
|
|
33
|
+
#:
|
|
34
|
+
#: Bumped to 5 by the v3-limits batch: parse.py now detects usage-limit
|
|
35
|
+
#: pauses (LIMIT_HIT/LIMIT_RESUME/AGENT_TERMINATED events,
|
|
36
|
+
#: Turn.synthetic_kind, Turn.gap_cause -- see model.py's module
|
|
37
|
+
#: docstring), none of which a pre-batch digest cache entry ever
|
|
38
|
+
#: computed, so it must not be treated as still valid.
|
|
39
|
+
#:
|
|
40
|
+
#: Bumped to 6 by the v4-wasted-turns batch: parse.py now derives two new
|
|
41
|
+
#: additive ``Turn`` fields, ``tool_error_count``/``tool_error_chars``,
|
|
42
|
+
#: from each turn's own tool_result blocks that carry ``is_error: true``
|
|
43
|
+
#: (see model.py's module docstring), which no pre-batch digest cache
|
|
44
|
+
#: entry ever computed, so it must not be treated as still valid.
|
|
45
|
+
#:
|
|
46
|
+
#: Bumped to 7 by the readability batch: events.py now measures an
|
|
47
|
+
#: attachment's ``Event.size_chars`` from the real ``rendered`` shape (a
|
|
48
|
+
#: list of ``{"content": str}`` blocks) or its content fields, where it
|
|
49
|
+
#: previously read only a bare string and so recorded ``None``; it also
|
|
50
|
+
#: adds per-source ``instructions`` sizes and ``prompt_snapshot``
|
|
51
|
+
#: system/tool sizes to ``Event.detail``.
|
|
52
|
+
#:
|
|
53
|
+
#: Bumped to 8 by the context-files batch: events.py now keeps one record
|
|
54
|
+
#: per instruction file (salted path hash, type, path-scoped, size) for
|
|
55
|
+
#: ``instructions`` and ``nested_memory``, and one per skill (name, size)
|
|
56
|
+
#: for ``skill_listing`` and ``invoked_skills``; parse.py adds
|
|
57
|
+
#: ``Turn.skills_invoked``.
|
|
58
|
+
#:
|
|
59
|
+
#: Bumped to 9 by the quality-signals batch: parse.py adds
|
|
60
|
+
#: ``Turn.stop_reason``, ``tool_calls_by_tool``, ``tool_errors_by_tool``, ``edit_target_hashes``
|
|
61
|
+
#: and ``human_correction``; events.py records task-notification and
|
|
62
|
+
#: agent-result outcomes in ``Event.detail``.
|
|
63
|
+
#:
|
|
64
|
+
#: Bumped to 10 by the same batch: discovery.py records a workflow
|
|
65
|
+
#: agent's end state on ``TranscriptMeta.workflow_agent_state``, which a
|
|
66
|
+
#: version-9 digest (already written by a development build) lacks.
|
|
67
|
+
#:
|
|
68
|
+
#: Bumped to 11: parse.py took a turn's usage from the first line of a
|
|
69
|
+
#: streamed reply, whose output_tokens is a partial count and which has no
|
|
70
|
+
#: thinking_tokens; it now keeps the most complete snapshot among the
|
|
71
|
+
#: reply's lines, so older digests undercount output and thinking tokens.
|
|
72
|
+
#:
|
|
73
|
+
#: Bumped to 12: parse.py records why each failed tool call failed
|
|
74
|
+
#: (``Turn.tool_errors_by_kind``), which waste.py needs to tell a failing
|
|
75
|
+
#: test or a hook block from a call that couldn't run.
|
|
76
|
+
#:
|
|
77
|
+
#: Bumped to 13 by the three-pricing-fixes batch: parse.py now records
|
|
78
|
+
#: ``Turn.speed`` from each turn's own ``usage.speed`` (see model.py's
|
|
79
|
+
#: module docstring), which pricing.py needs to apply a model's fast-mode
|
|
80
|
+
#: rate multiplier. A pre-13 digest has no ``speed`` recorded, so every
|
|
81
|
+
#: transcript is re-parsed once to pick it up.
|
|
82
|
+
#:
|
|
83
|
+
#: Bumped to 14 by the edit-capture batch: ``Turn.edit_target_hashes`` now
|
|
84
|
+
#: covers MultiEdit and the files a Bash or PowerShell command writes
|
|
85
|
+
#: (``shell_writes.py``), drops edits whose tool call failed, and paths
|
|
86
|
+
#: are normalised further before hashing (Git Bash's ``/c/`` form, ``.``
|
|
87
|
+
#: and ``..``), so older digests undercount edits and can't match a file
|
|
88
|
+
#: written by a shell command to the same file edited with Edit. The same
|
|
89
|
+
#: bump adds ``Turn.retry_marker``/``result_marker`` (the quality markers
|
|
90
|
+
#: Claude can be asked to write; see ``quality.MARKER_LINES``).
|
|
91
|
+
#:
|
|
92
|
+
#: Bumped to 15 by the metrics-capture batch: parse.py reads capture tags
|
|
93
|
+
#: (``Turn.cap``, ``spawn_marker``, ``retry_marker`` gains ``scope``) and
|
|
94
|
+
#: capture notes (``Turn.cap_note_chars``, ``TranscriptMeta.cap_*``), and
|
|
95
|
+
#: records ``agent_result_chars``, ``prompt_flags``, ``plan_stats`` and
|
|
96
|
+
#: ``commands_run``. Three miscounts are fixed at the same time:
|
|
97
|
+
#: ``read_target_hashes`` covered edits as well as reads, so every edited
|
|
98
|
+
#: file looked re-read; ``hook_system_message`` lines (shown to you, never
|
|
99
|
+
#: to the model) were counted as hook context; and task notifications
|
|
100
|
+
#: weren't sized, so a background agent's report had no size.
|
|
101
|
+
#:
|
|
102
|
+
#: Bumped to 16 by the feedback batch: parse.py reads your /tl-feedback
|
|
103
|
+
#: answers (``Turn.feedback``) from the skill's ``[tl-fb: ...]`` line or,
|
|
104
|
+
#: failing that, from the AskUserQuestion result itself. ``commands_run``
|
|
105
|
+
#: now names skills you ran with a slash too: they are written
|
|
106
|
+
#: ``<command-message>`` first, as your message, and were missed.
|
|
107
|
+
#:
|
|
108
|
+
#: Bumped to 17 by the P2 capture-integrity batch: capture_tags.py now
|
|
109
|
+
#: strips the exact reminder sentence before the tail match (a tag glued
|
|
110
|
+
#: to a reminder no longer swallows it); a ``[tl-fb: ...]`` line only
|
|
111
|
+
#: counts when the cycle's first turn actually ran ``/tl-feedback``, a
|
|
112
|
+
#: forged one is ignored; a captured tag key is kept only when the
|
|
113
|
+
#: session's note said to ask for it (``cap_injections > 0`` and the key
|
|
114
|
+
#: is a requested metric); ``reported_task`` counts once per cycle, not
|
|
115
|
+
#: once per line; ``Turn.skills_invoked`` now runs skill names through
|
|
116
|
+
#: ``SKILL_NAME_PATTERN`` and drops one whose ``Skill`` call errored, so a
|
|
117
|
+
#: skill can no longer self-authorise a capture note by name alone; and
|
|
118
|
+
#: capture.py's coverage denominator drops feedback/interrupted/max_tokens
|
|
119
|
+
#: cycles, merges a cycle's tags key by key instead of replacing the
|
|
120
|
+
#: whole set, sizes ``_big_output`` per call instead of per turn, and
|
|
121
|
+
#: ``_WRAP`` carries a ``:Tool`` suffix. None of this is recoverable from
|
|
122
|
+
#: an older digest, so every transcript is re-parsed once to pick it up.
|
|
123
|
+
#:
|
|
124
|
+
#: Bumped to 18 by the P3 hook-lifecycle batch: a HOOK_OUTPUT event's
|
|
125
|
+
#: ``Event.detail`` now carries the closed hook-event bucket it ran under
|
|
126
|
+
#: (``hookName``, e.g. ``"PreToolUse"``, never the matcher/tool-name
|
|
127
|
+
#: suffix -- SURV-HE, G7), its real ``durationMs`` when Claude Code
|
|
128
|
+
#: recorded one (CAP-9/F10: this used to be dropped), and, only when
|
|
129
|
+
#: ``True``, whether the call ran ClaudeGlass's own capture hook script
|
|
130
|
+
#: (``capture``, never the command string itself). ``hook_health.py``'s
|
|
131
|
+
#: new ``count_hook_errors``/``measure_deep_wait`` both read these
|
|
132
|
+
#: straight off already-parsed events; a pre-18 digest has none of them,
|
|
133
|
+
#: so every transcript is re-parsed once to pick them up.
|
|
134
|
+
#: Bumped to 19 by the parser-signals batch (SURV-4/5/6/7): ``thinking_drop``
|
|
135
|
+
#: joins the CACHE_SIGNAL family; ``task_status``/``structured_output``
|
|
136
|
+
#: attachments get their own ``EventKind``s instead of falling into the
|
|
137
|
+
#: generic ATTACHMENT catch-all; ``cost-state`` lines are read for their
|
|
138
|
+
#: own ``totalCostUSD``/``hasUnknownModelCost`` (``TranscriptMeta.
|
|
139
|
+
#: cc_cost_usd``/``cc_cost_has_unknown_model``) instead of falling
|
|
140
|
+
#: through to UNKNOWN; a tool_result's or human prompt's own image/
|
|
141
|
+
#: document content blocks are sized by the documented image-token rule
|
|
142
|
+
#: instead of silently counting as 0 chars; and a line type no detection
|
|
143
|
+
#: rule recognises at all is now counted separately
|
|
144
|
+
#: (``TranscriptResult.parser_notes["unknown_line_types"]``) from one
|
|
145
|
+
#: this parser knows about and deliberately ignores. None of this is
|
|
146
|
+
#: recoverable from an older digest, so every transcript is re-parsed
|
|
147
|
+
#: once to pick it up.
|
|
148
|
+
#:
|
|
149
|
+
#: Bumped to 20 by the docs-and-privacy sweep: ``Diagnostics.
|
|
150
|
+
#: ignored_line_types`` now keys on the sanitised line type (or
|
|
151
|
+
#: ``"other"``), like ``unknown_line_types`` already did, instead of the
|
|
152
|
+
#: raw ``type`` straight off the wire. A cached pre-20 digest still holds
|
|
153
|
+
#: the raw keys, so every transcript is re-parsed once to drop them.
|
|
154
|
+
#:
|
|
155
|
+
#: Bumped to 21: ``capture_tags.filter_tag`` now drops tag keys only the
|
|
156
|
+
#: other scope is asked for, and a subagent's ``[tl: ...]`` no longer
|
|
157
|
+
#: counts as a tag (it could set a whole prompt cycle's task or level);
|
|
158
|
+
#: a subagent's ``out=`` is kept when a large-output note asked for it.
|
|
159
|
+
#: A cached pre-21 digest still holds the unfiltered tags.
|
|
160
|
+
#:
|
|
161
|
+
#: Bumped to 22: hook events now carry the hook's label (its script's file
|
|
162
|
+
#: name), why a failing hook failed and whether its script path is
|
|
163
|
+
#: relative; turns carry the context your hooks added and the calls they
|
|
164
|
+
#: blocked (and any unchanged re-send). None of this is in a pre-22
|
|
165
|
+
#: digest.
|
|
166
|
+
#:
|
|
167
|
+
#: Bumped to 23: a hook event also says whether its command uses a
|
|
168
|
+
#: Windows ``%VAR%`` variable, and a quoted script path with a tab in it
|
|
169
|
+
#: is no longer read as relative. A pre-23 digest has neither.
|
|
170
|
+
#:
|
|
171
|
+
#: Bumped to 24: discovery.py gives an agent under a workflow run's folder
|
|
172
|
+
#: ``TranscriptMeta.kind == "workflow-agent"``; it was ``"subagent"``, so
|
|
173
|
+
#: every table split by kind folded workflow agents into subagents. A
|
|
174
|
+
#: pre-24 digest (the parse cache's and the service store's alike) still
|
|
175
|
+
#: holds the old kind.
|
|
176
|
+
#:
|
|
177
|
+
#: Bumped to 25: a coaching note (``tl-coach v``, the capture hook's live
|
|
178
|
+
#: hints) is its own ``coaching_note`` event, and counts to
|
|
179
|
+
#: ``Turn.cap_note_chars`` but not to ``cap_injections``; a capture note
|
|
180
|
+
#: sharing an attachment with one keeps the coaching part apart
|
|
181
|
+
#: (``detail["coach"]``/``["coach_chars"]``). Notes on a message you send
|
|
182
|
+
#: (``UserPromptSubmit``) keep that hook name. A pre-25 digest counted
|
|
183
|
+
#: neither.
|
|
184
|
+
PARSER_VERSION = 25
|
|
185
|
+
|
|
186
|
+
#: Bump when the model.py contract changes in a way that invalidates the
|
|
187
|
+
#: on-disk digest cache (see model.py's module docstring for the contract
|
|
188
|
+
#: rules: fields may be added with defaults, never renamed or removed
|
|
189
|
+
#: without a SCHEMA_VERSION bump).
|
|
190
|
+
SCHEMA_VERSION = 1
|
|
191
|
+
|
|
192
|
+
__all__ = ["__version__", "PARSER_VERSION", "SCHEMA_VERSION"]
|
claudeglass/__main__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""``python -m claudeglass`` entry point.
|
|
2
|
+
|
|
3
|
+
Also the entry point a ``python -m zipapp``-built ``claudeglass.pyz``
|
|
4
|
+
uses (see the plan's "Distribution without pip" section): zipapp runs
|
|
5
|
+
``__main__.py`` at the archive root, and without ``sys.exit(main())``
|
|
6
|
+
here the process would always exit 0 regardless of what ``main()``
|
|
7
|
+
returned, silently losing every non-zero exit code (no-data, bad-input,
|
|
8
|
+
...) a caller or CI script depends on.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
from .cli import main
|
|
16
|
+
|
|
17
|
+
sys.exit(main())
|