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.
Files changed (162) hide show
  1. claudeglass/__init__.py +192 -0
  2. claudeglass/__main__.py +17 -0
  3. claudeglass/advice.py +943 -0
  4. claudeglass/backtest.py +315 -0
  5. claudeglass/baseline.py +705 -0
  6. claudeglass/cache.py +576 -0
  7. claudeglass/capture.py +961 -0
  8. claudeglass/capture_catalogue.py +1769 -0
  9. claudeglass/capture_tags.py +348 -0
  10. claudeglass/capture_view.py +748 -0
  11. claudeglass/carry.py +938 -0
  12. claudeglass/change_points.py +603 -0
  13. claudeglass/classify.py +1137 -0
  14. claudeglass/claude_md_review.py +1074 -0
  15. claudeglass/cli.py +4775 -0
  16. claudeglass/coaching.py +130 -0
  17. claudeglass/compaction.py +763 -0
  18. claudeglass/compaction_sim.py +1609 -0
  19. claudeglass/compare.py +733 -0
  20. claudeglass/config.py +1297 -0
  21. claudeglass/context_budget.py +1168 -0
  22. claudeglass/context_files.py +322 -0
  23. claudeglass/corpus.py +435 -0
  24. claudeglass/counterfactual.py +385 -0
  25. claudeglass/discovery.py +691 -0
  26. claudeglass/elasticity.py +936 -0
  27. claudeglass/events.py +1631 -0
  28. claudeglass/exports.py +497 -0
  29. claudeglass/fixes.py +1057 -0
  30. claudeglass/footprint.py +631 -0
  31. claudeglass/habits.py +3196 -0
  32. claudeglass/handoff.py +552 -0
  33. claudeglass/helptext.py +4513 -0
  34. claudeglass/hook_costs.py +582 -0
  35. claudeglass/hook_health.py +1349 -0
  36. claudeglass/hooks/__init__.py +0 -0
  37. claudeglass/hooks/capture-catalogue.json +418 -0
  38. claudeglass/hooks/capture-hook.py +971 -0
  39. claudeglass/hooks/snapshot-config.py +2089 -0
  40. claudeglass/ignores.py +218 -0
  41. claudeglass/impact.py +619 -0
  42. claudeglass/installer.py +772 -0
  43. claudeglass/invocation.py +225 -0
  44. claudeglass/jsonl.py +133 -0
  45. claudeglass/limits.py +794 -0
  46. claudeglass/model.py +1280 -0
  47. claudeglass/model_gate.py +156 -0
  48. claudeglass/model_swap.py +1060 -0
  49. claudeglass/monthly.py +572 -0
  50. claudeglass/onboarding.py +838 -0
  51. claudeglass/pages.py +192 -0
  52. claudeglass/parse.py +1895 -0
  53. claudeglass/phases.py +327 -0
  54. claudeglass/pricing.py +1160 -0
  55. claudeglass/pricing.toml +284 -0
  56. claudeglass/probe.py +253 -0
  57. claudeglass/profiles/__init__.py +53 -0
  58. claudeglass/profiles/apply.py +1060 -0
  59. claudeglass/profiles/catalogue/discovery-scrape.toml +34 -0
  60. claudeglass/profiles/catalogue/implementation-heavy.toml +36 -0
  61. claudeglass/profiles/catalogue/interactive-chat.toml +26 -0
  62. claudeglass/profiles/catalogue/overnight-batch.toml +33 -0
  63. claudeglass/profiles/catalogue/overseer-fanout.toml +33 -0
  64. claudeglass/profiles/catalogue/plan-then-build.toml +27 -0
  65. claudeglass/profiles/catalogue/planning-requirements.toml +27 -0
  66. claudeglass/profiles/catalogue/workflow-ultracode.toml +35 -0
  67. claudeglass/profiles/catalogue.py +215 -0
  68. claudeglass/profiles/diff.py +353 -0
  69. claudeglass/profiles/frontmatter.py +618 -0
  70. claudeglass/profiles/goals.py +777 -0
  71. claudeglass/profiles/schema.py +703 -0
  72. claudeglass/quality.py +1309 -0
  73. claudeglass/quick_actions.py +1192 -0
  74. claudeglass/recache.py +1005 -0
  75. claudeglass/recommend.py +2526 -0
  76. claudeglass/reconcile.py +778 -0
  77. claudeglass/render/__init__.py +12 -0
  78. claudeglass/render/csv_out.py +98 -0
  79. claudeglass/render/html.py +417 -0
  80. claudeglass/render/json_out.py +86 -0
  81. claudeglass/render/markdown.py +238 -0
  82. claudeglass/render/tables.py +258 -0
  83. claudeglass/report.py +2162 -0
  84. claudeglass/run_split.py +586 -0
  85. claudeglass/savers.py +1320 -0
  86. claudeglass/scorecard.py +455 -0
  87. claudeglass/service/__init__.py +54 -0
  88. claudeglass/service/api.py +3163 -0
  89. claudeglass/service/coaching_job.py +109 -0
  90. claudeglass/service/codewatch.py +202 -0
  91. claudeglass/service/contracts.py +329 -0
  92. claudeglass/service/explain.py +147 -0
  93. claudeglass/service/monthly_job.py +128 -0
  94. claudeglass/service/rebuild.py +244 -0
  95. claudeglass/service/schema.py +506 -0
  96. claudeglass/service/serve.py +381 -0
  97. claudeglass/service/static/THIRD_PARTY.sha256 +7 -0
  98. claudeglass/service/static/api.js +428 -0
  99. claudeglass/service/static/app.css +5087 -0
  100. claudeglass/service/static/app.js +874 -0
  101. claudeglass/service/static/charts-types.js +2416 -0
  102. claudeglass/service/static/charts.js +802 -0
  103. claudeglass/service/static/core.js +236 -0
  104. claudeglass/service/static/costs.js +239 -0
  105. claudeglass/service/static/d3.js +14 -0
  106. claudeglass/service/static/evidence.js +261 -0
  107. claudeglass/service/static/favicon.svg +8 -0
  108. claudeglass/service/static/fonts/InterVariable-4.1.woff2 +0 -0
  109. claudeglass/service/static/fonts/JetBrainsMono-2.304-Medium.woff2 +0 -0
  110. claudeglass/service/static/fonts/JetBrainsMono-2.304-Regular.woff2 +0 -0
  111. claudeglass/service/static/fonts/LICENSE-Inter-4.1.txt +92 -0
  112. claudeglass/service/static/fonts/OFL-JetBrainsMono-2.304.txt +93 -0
  113. claudeglass/service/static/format.js +471 -0
  114. claudeglass/service/static/grid.js +1413 -0
  115. claudeglass/service/static/icons.js +110 -0
  116. claudeglass/service/static/index.html +55 -0
  117. claudeglass/service/static/links.js +541 -0
  118. claudeglass/service/static/page-actions.js +1170 -0
  119. claudeglass/service/static/page-agents.js +325 -0
  120. claudeglass/service/static/page-cache.js +219 -0
  121. claudeglass/service/static/page-capture.js +503 -0
  122. claudeglass/service/static/page-data.js +103 -0
  123. claudeglass/service/static/page-glossary.js +464 -0
  124. claudeglass/service/static/page-habits.js +236 -0
  125. claudeglass/service/static/page-overview.js +819 -0
  126. claudeglass/service/static/page-setup.js +1181 -0
  127. claudeglass/service/static/page-spend.js +756 -0
  128. claudeglass/service/static/palette.js +838 -0
  129. claudeglass/service/static/shell.js +554 -0
  130. claudeglass/service/static/theme-boot.js +20 -0
  131. claudeglass/service/static/ui.js +1140 -0
  132. claudeglass/service/static/vendor/LICENSE-d3-7.9.0 +13 -0
  133. claudeglass/service/static/vendor/d3-7.9.0.min.js +2 -0
  134. claudeglass/service/store.py +2039 -0
  135. claudeglass/service/storelock.py +166 -0
  136. claudeglass/service/watcher.py +1415 -0
  137. claudeglass/setup_flow.py +795 -0
  138. claudeglass/setup_status.py +361 -0
  139. claudeglass/shell_writes.py +492 -0
  140. claudeglass/signals.py +257 -0
  141. claudeglass/skills_review.py +571 -0
  142. claudeglass/snapshots.py +1308 -0
  143. claudeglass/statusline.py +2273 -0
  144. claudeglass/team.py +650 -0
  145. claudeglass/tools/__init__.py +10 -0
  146. claudeglass/tools/log_usage.py +663 -0
  147. claudeglass/tools/scrub.py +809 -0
  148. claudeglass/topology.py +1122 -0
  149. claudeglass/ttl.py +1995 -0
  150. claudeglass/units.py +129 -0
  151. claudeglass/upgrade.py +147 -0
  152. claudeglass/usage.py +311 -0
  153. claudeglass/waste.py +806 -0
  154. claudeglass/whatif.py +467 -0
  155. claudeglass/workflows.py +299 -0
  156. claudeglass/workstyle.py +315 -0
  157. claudeglass-0.9.0.dist-info/METADATA +596 -0
  158. claudeglass-0.9.0.dist-info/RECORD +162 -0
  159. claudeglass-0.9.0.dist-info/WHEEL +5 -0
  160. claudeglass-0.9.0.dist-info/entry_points.txt +2 -0
  161. claudeglass-0.9.0.dist-info/licenses/LICENSE +21 -0
  162. claudeglass-0.9.0.dist-info/top_level.txt +1 -0
@@ -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"]
@@ -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())