zeno-cli 0.3.4__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 (69) hide show
  1. zeno_adapters/__init__.py +17 -0
  2. zeno_adapters/_common.py +38 -0
  3. zeno_adapters/anthropic.py +68 -0
  4. zeno_adapters/claude_code.py +101 -0
  5. zeno_adapters/crewai.py +92 -0
  6. zeno_adapters/langgraph.py +49 -0
  7. zeno_adapters/openai.py +108 -0
  8. zeno_cli/__init__.py +1 -0
  9. zeno_cli/_hooks/cc_bridge.py +1016 -0
  10. zeno_cli/doctor.py +535 -0
  11. zeno_cli/hook_install.py +269 -0
  12. zeno_cli/hud/__init__.py +1 -0
  13. zeno_cli/hud/hud_install.py +652 -0
  14. zeno_cli/hud/zeno_attention.py +288 -0
  15. zeno_cli/hud/zeno_cognition.py +457 -0
  16. zeno_cli/hud/zeno_hud.py +496 -0
  17. zeno_cli/interview_invites.py +342 -0
  18. zeno_cli/login.py +241 -0
  19. zeno_cli/main.py +2534 -0
  20. zeno_cli/onboard.py +206 -0
  21. zeno_cli/outreach.py +456 -0
  22. zeno_cli/version.py +67 -0
  23. zeno_cli-0.3.4.dist-info/METADATA +161 -0
  24. zeno_cli-0.3.4.dist-info/RECORD +69 -0
  25. zeno_cli-0.3.4.dist-info/WHEEL +4 -0
  26. zeno_cli-0.3.4.dist-info/entry_points.txt +4 -0
  27. zeno_core/__init__.py +67 -0
  28. zeno_core/analytics.py +193 -0
  29. zeno_core/rtlx_s.py +460 -0
  30. zeno_core/streak.py +178 -0
  31. zeno_core/tlx_s.py +192 -0
  32. zeno_sdk/__init__.py +6 -0
  33. zeno_sdk/_generated/__init__.py +6 -0
  34. zeno_sdk/_generated/client.py +819 -0
  35. zeno_sdk/_migrations/alembic/env.py +33 -0
  36. zeno_sdk/_migrations/alembic/script.py.mako +18 -0
  37. zeno_sdk/_migrations/alembic/versions/0001_initial.py +79 -0
  38. zeno_sdk/_migrations/alembic/versions/0002_cognition_samples.py +53 -0
  39. zeno_sdk/_migrations/alembic/versions/0003_cognition_drivers.py +41 -0
  40. zeno_sdk/_migrations/alembic/versions/0004_transcript_intelligence.py +248 -0
  41. zeno_sdk/_migrations/alembic.ini +35 -0
  42. zeno_sdk/_runtime.py +12 -0
  43. zeno_sdk/adapters/__init__.py +15 -0
  44. zeno_sdk/adapters/anthropic.py +5 -0
  45. zeno_sdk/adapters/claude_code.py +5 -0
  46. zeno_sdk/adapters/crewai.py +5 -0
  47. zeno_sdk/adapters/langgraph.py +5 -0
  48. zeno_sdk/adapters/openai.py +5 -0
  49. zeno_sdk/auth.py +25 -0
  50. zeno_sdk/client.py +87 -0
  51. zeno_sdk/config.py +61 -0
  52. zeno_sdk/daemon.py +72 -0
  53. zeno_sdk/privacy.py +46 -0
  54. zeno_sdk/session.py +179 -0
  55. zeno_sdk/storage.py +487 -0
  56. zeno_sdk/types/__init__.py +121 -0
  57. zeno_session_intel/__init__.py +19 -0
  58. zeno_session_intel/analytics.py +588 -0
  59. zeno_session_intel/compression.py +123 -0
  60. zeno_session_intel/ingest.py +376 -0
  61. zeno_session_intel/model.py +129 -0
  62. zeno_session_intel/parsers/__init__.py +31 -0
  63. zeno_session_intel/parsers/claude_code.py +169 -0
  64. zeno_session_intel/parsers/codex.py +265 -0
  65. zeno_session_intel/parsers/cursor.py +198 -0
  66. zeno_session_intel/prices.py +281 -0
  67. zeno_session_intel/schema.py +277 -0
  68. zeno_session_intel/signals.py +319 -0
  69. zeno_session_intel/taxonomy.py +71 -0
zeno_cli/version.py ADDED
@@ -0,0 +1,67 @@
1
+ """CLI version + git-SHA helpers.
2
+
3
+ Surfaced via `zeno --version` and `zeno version`. The version string is the
4
+ zeno-cli pyproject.toml version (today: 0.1.0), resolved at runtime via
5
+ importlib.metadata so a wheel install and the editable workspace agree. The
6
+ git SHA is best-effort: if the repo is reachable via git in the working tree
7
+ above this file, we surface a short SHA; otherwise we omit it cleanly.
8
+
9
+ Used by `zeno doctor` too - the first row in the diagnostic is the version
10
+ banner so support requests get a fingerprint.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import subprocess
16
+ from pathlib import Path
17
+
18
+
19
+ def package_version() -> str:
20
+ """Return the installed zeno-cli version, or "0.0.0+unknown" on failure.
21
+
22
+ Uses ``importlib.metadata`` so an editable install (uv workspace) sees
23
+ the same version as a wheel install. Falls back to a sentinel string
24
+ when zeno-cli isn't actually installed (e.g. running from source via
25
+ PYTHONPATH only - the workspace test setup).
26
+ """
27
+ try:
28
+ from importlib.metadata import version # noqa: PLC0415
29
+
30
+ return version("zeno-cli")
31
+ except Exception:
32
+ return "0.0.0+unknown"
33
+
34
+
35
+ def git_sha(short: bool = True, max_depth: int = 8) -> str | None:
36
+ """Return the short git SHA of the working tree, or None if unavailable.
37
+
38
+ Walks up from this file looking for a .git directory so the SHA reflects
39
+ the source tree the CLI was launched from (important for editable
40
+ installs from a uv workspace). Returns None for installed wheels in
41
+ site-packages where there is no .git nearby.
42
+ """
43
+ here = Path(__file__).resolve()
44
+ for parent in [here.parent, *here.parents][:max_depth]:
45
+ if (parent / ".git").exists():
46
+ try:
47
+ fmt = "--short" if short else "--verify"
48
+ out = subprocess.run(
49
+ ["git", "rev-parse", fmt, "HEAD"],
50
+ capture_output=True,
51
+ text=True,
52
+ timeout=2,
53
+ cwd=str(parent),
54
+ )
55
+ if out.returncode == 0:
56
+ return out.stdout.strip() or None
57
+ except (OSError, subprocess.SubprocessError):
58
+ return None
59
+ return None
60
+
61
+
62
+ def version_string() -> str:
63
+ """One-line version banner: '<pkg-version> (<sha>)' or '<pkg-version>'."""
64
+ sha = git_sha()
65
+ if sha:
66
+ return f"{package_version()} ({sha})"
67
+ return package_version()
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: zeno-cli
3
+ Version: 0.3.4
4
+ Summary: Zeno CLI - measure the supervision cost of AI-assisted work
5
+ Project-URL: Homepage, https://zeno.center
6
+ Project-URL: Repository, https://github.com/Marwan01/zeno
7
+ Author-email: Mar Helali <mar@1dev.space>
8
+ License-Expression: Apache-2.0
9
+ Keywords: ai,claude-code,cognitive-load,rtlx-s,session-analytics,supervision
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: aiosqlite>=0.20.0
18
+ Requires-Dist: alembic>=1.13.2
19
+ Requires-Dist: keyring>=25.3.0
20
+ Requires-Dist: matplotlib>=3.9.2
21
+ Requires-Dist: numpy>=2.0
22
+ Requires-Dist: pydantic>=2.9.0
23
+ Requires-Dist: rich>=13.8.1
24
+ Requires-Dist: scipy>=1.14.1
25
+ Requires-Dist: textual>=0.86.1
26
+ Description-Content-Type: text/markdown
27
+
28
+ # zeno-cli
29
+
30
+ Measure the supervision cost of AI-assisted work, from your terminal. `zeno`
31
+ records RTLX-S load probes + session activity, fits your personal "babysitting
32
+ tax" curve, and surfaces where your optimal number of parallel agents sits.
33
+
34
+ ## Install
35
+
36
+ Install the `zeno` CLI straight from the monorepo over SSH. It is one
37
+ self-contained package: the SDK, core, adapters, session-intelligence engine,
38
+ and SQLite migrations all ship inside it. Requires **Python >= 3.12** and GitHub
39
+ SSH access to the repo.
40
+
41
+ ```sh
42
+ uv tool install \
43
+ --from "git+ssh://git@github.com/Marwan01/zeno.git@cli-v0.3.3#subdirectory=apps/cli" \
44
+ zeno-cli
45
+ ```
46
+
47
+ `pipx install "git+ssh://git@github.com/Marwan01/zeno.git@cli-v0.3.3#subdirectory=apps/cli"`
48
+ works the same way. The git clone runs an in-tree build, so the hatchling build
49
+ hook (`apps/cli/hatch_build.py`) vendors the workspace packages into the wheel.
50
+ **Update** by bumping the tag: re-run with `@cli-v0.3.3` (uv) or
51
+ `pipx install --force ...@<newtag>`.
52
+
53
+ **Why git+ssh and not an index:** TestPyPI is not a durable distribution channel
54
+ and a private index is overkill at team scale. A public PyPI wheel comes only
55
+ when non-repo-access users do. The packaging blocker for it is now resolved - the
56
+ build is sdist-clean (`uv build --sdist` -> a wheel built from that sdist installs
57
+ and runs every verb; see the build-hook note under [Packaging](#packaging-notes)),
58
+ so the remaining gate is purely "do public users exist yet", not a broken build.
59
+
60
+ ### Install channels (staged)
61
+
62
+ The distribution channel widens as the audience does. Use the one that matches
63
+ where the project is, not the most convenient:
64
+
65
+ | Stage | Channel | Command | Status |
66
+ | :--- | :--- | :--- | :--- |
67
+ | Now | git+ssh tag | `uv tool install --from "git+ssh://git@github.com/Marwan01/zeno.git@cli-v0.3.3#subdirectory=apps/cli" zeno-cli` | Live (needs repo SSH access) |
68
+ | Next | PyPI | `uv tool install zeno-cli` (or `pipx install zeno-cli`) | Lands when `.github/workflows/publish.yml` runs on the first GitHub release; the packaging metadata is ready |
69
+ | Later | Homebrew | `brew install zeno-cli` | Formula not written yet |
70
+
71
+ The PyPI publish is OIDC Trusted-Publishing gated and fires only on a published
72
+ GitHub release - see `.github/workflows/publish.yml`. Until that first release,
73
+ the git+ssh tag is the supported path.
74
+
75
+ **From a built wheel** (offline / air-gapped; the wheel is fully self-contained):
76
+
77
+ ```sh
78
+ uv build --wheel --package zeno-cli # -> dist/zeno_cli-*.whl
79
+ uv tool install ./dist/zeno_cli-0.3.3-py3-none-any.whl
80
+ ```
81
+
82
+ **From source** (development inside the monorepo, no install):
83
+
84
+ ```sh
85
+ uv run --project /path/to/zeno zeno <cmd>
86
+ ```
87
+
88
+ New here? Follow `docs/ADOPT.md` for the 10-minute install -> capture -> survey loop.
89
+
90
+ ## Use
91
+
92
+ ```sh
93
+ zeno survey # log how a session felt (~6s, RTLX-S 5-item probe)
94
+ zeno status # tier, streak, tax curve, cognitive state, today's survey
95
+ zeno curve # render the babysitting-tax curve (needs ~10 sessions of data)
96
+ zeno weekly # 7-day rollup
97
+ zeno doctor # health check: API reachability + identity
98
+ zeno login # browser sign-in; stores a Clerk JWT in the OS keyring
99
+ zeno whoami # show the identity + tier the API sees for your token
100
+ zeno logout # clear the stored token
101
+ ```
102
+
103
+ ### Authentication
104
+
105
+ `zeno login` runs a browser + loopback sign-in and stores the resulting Clerk
106
+ JWT in your OS keyring; the CLI sends it as a `Bearer` token so the API
107
+ attributes requests to your user. Token resolution order, used by every
108
+ command: `ZENO_API_TOKEN` env override first, then the keyring token from
109
+ `zeno login`, then none (local-only). Use `zeno login --no-browser` for SSH /
110
+ headless (prints the URL), and `--authorize-url` / `ZENO_LOGIN_AUTHORIZE_URL`
111
+ to target a non-default bridge.
112
+
113
+ > The dashboard `/cli/authorize` bridge page + the Clerk `zeno-api` JWT template
114
+ > that mint the token are a documented follow-up (not yet shipped). See
115
+ > `src/zeno_cli/login.py` for the callback contract.
116
+
117
+ Full daily-loop guide: `docs/CLI_QUICKSTART.md`. Requires Python >= 3.12.
118
+
119
+ ## Cognition HUD add-on
120
+
121
+ `zeno-hud-bar` is the single differentiated cognition line (`att / eff / drv`) that
122
+ stacks UNDER whatever popular HUD you already run, instead of replacing it. Claude Code
123
+ has one statusLine slot, so zeno rides the HUD you have:
124
+
125
+ ```sh
126
+ zeno hud install # auto-detect: ccstatusline (native widget) or claude-hud (wrapper)
127
+ zeno hud install --dry-run # print what would change, write nothing
128
+ zeno hud status # what is detected + whether the line is wired in
129
+ zeno hud uninstall # remove from both adapters (idempotent; --restore for byte-for-byte)
130
+ ```
131
+
132
+ `--target {auto,ccstatusline,claude-hud}` forces an adapter (auto prefers ccstatusline
133
+ when both are present). Every edit is backed up and reversible. The bar is crash-safe: on
134
+ any error it prints an empty line and exits 0, so it never breaks the host HUD. Full model
135
+ and both recipes: `docs/HUD_ADDON.md`.
136
+
137
+ ## Packaging notes
138
+
139
+ - **sdist-clean via a build hook.** `apps/cli/hatch_build.py` (a custom hatchling
140
+ build hook, enabled on BOTH the `wheel` and `sdist` targets) vendors the four
141
+ workspace packages (`zeno_core` / `zeno_sdk` / `zeno_adapters` /
142
+ `zeno_session_intel`) plus the SDK alembic tree into an in-root, gitignored
143
+ `_vendor_build/` stage at build time. The wheel target flattens them to the
144
+ archive top level (the exact layout the old static `force-include` produced, so
145
+ absolute imports resolve unchanged); the sdist target ships the whole
146
+ `_vendor_build/` stage inside the tarball so a wheel built FROM the sdist finds
147
+ the sources locally. This replaced the static `[tool.hatch...force-include]` of
148
+ `../../packages/...` relative-parent paths, whose `..` escaped the sdist root and
149
+ were silently dropped - a wheel from that sdist died with "Forced include not
150
+ found". Repo source under `packages/*` is never moved, so the uv workspace, the
151
+ API, and the test suite keep importing the editable members unchanged; the
152
+ vendored copy is purely a build artifact.
153
+ - `zeno ingest` / `zeno usage` are backed by `zeno_session_intel`, vendored
154
+ alongside the other internal packages by the build hook. It is stdlib-only with
155
+ relative-only internal imports, so it flattens into the wheel with zero import
156
+ rewrites. Omit it and both subcommands abort with a "not bundled" hint at runtime
157
+ (the regression fixed in 0.3.3); `scripts/install-smoke.sh` now exercises every
158
+ advertised subcommand so this cannot ship silently again.
159
+ - Agent-framework adapters (`zeno_sdk.adapters` -> crewai / openai / anthropic /
160
+ langgraph) are off the CLI path and pull heavy framework deps; they are kept
161
+ optional and will be exposed as extras (e.g. `zeno-cli[crewai]`) in a later stage.
@@ -0,0 +1,69 @@
1
+ zeno_cli/__init__.py,sha256=k_rgP6JpTT711x3-8Pss91qDM-W8iE212qb98h8yL8w,36
2
+ zeno_cli/doctor.py,sha256=2fYpTBiZRT7Oxmc0vJs5N_nxRXfPwDUsmRs5GE6oQpY,21565
3
+ zeno_cli/hook_install.py,sha256=2T_R-cpe_TdjJMjjye4IFr4G1I0GldIgxCoCQqL5Te4,9679
4
+ zeno_cli/interview_invites.py,sha256=7c9PhjTNsRdhvsK-dibEtfH-SIgIgDemJyAzZ0wdFSU,12276
5
+ zeno_cli/login.py,sha256=0TrlNlumx4vTDxNdREE1FjEJPevZRTZVvBUMmIcyjVU,9727
6
+ zeno_cli/main.py,sha256=jexg-7f0IHEhwZMHuNaxeGSZivJvTfC-2Npip5s_UeY,94823
7
+ zeno_cli/onboard.py,sha256=3c8NwAvuaFf_uDBgnwvLPKgPoh2kKg3xO74fEJR9YYs,8505
8
+ zeno_cli/outreach.py,sha256=GJo1FeMhZQ2yhlZ10qKC0aNNAz2AC7DDfcQ-Eke9DuE,15691
9
+ zeno_cli/version.py,sha256=n8mZkywYtXEOSR2YnohJ3gLJ_hcxLgPU6F5Oj_2jFgo,2459
10
+ zeno_cli/_hooks/cc_bridge.py,sha256=EgOszKaREn1T0x0SfR1LRCMB32xa5jD144UebAlT8ZQ,40531
11
+ zeno_cli/hud/__init__.py,sha256=leofDy-tpR_QI7fLtKtHt3afDR3uGtOM_0YIWJjEdnA,81
12
+ zeno_cli/hud/hud_install.py,sha256=vWGa0CGsgMzasSW6WYvfP4IrF5P9Rku9qlb7esX8Vbk,25519
13
+ zeno_cli/hud/zeno_attention.py,sha256=vg-_UIAkAiNLd32JfLlQo_4-5yMsEDdMPd8eSauDI_E,11216
14
+ zeno_cli/hud/zeno_cognition.py,sha256=WuFCMH8RVLQegQlnqcjBoijqKeZ5uym35h-tX3fSMEM,18505
15
+ zeno_cli/hud/zeno_hud.py,sha256=J9oxTIsua8p6qfusjlyGO8ly_WinUnpp4rWPGaqL-34,20301
16
+ zeno_adapters/__init__.py,sha256=KQT3Gf4ihdqsIOPBo5jaZeamv8pC3xim0wr_7Z7SZz8,575
17
+ zeno_adapters/_common.py,sha256=s9Vlr0yZDIAka8xiy947UNxnmBjU-XbyWdLzx6FCG18,1165
18
+ zeno_adapters/anthropic.py,sha256=lAOfpnGzKbzn5MLDULDVWXxy_VwIx8YYYpBa4rHAMDo,2373
19
+ zeno_adapters/claude_code.py,sha256=o0xlv3lxA6caiOZTrRZVbUlpDHzLBDKzE-mBWvlctYQ,3086
20
+ zeno_adapters/crewai.py,sha256=YHVNg0vE9v8JEUP0H3-bhpkj_fVnqqBbqD1AaDiomYc,3413
21
+ zeno_adapters/langgraph.py,sha256=TzTgCG5iGhAa_wIMJ9XEllvfgyUvplOVT46MnCjQchE,1806
22
+ zeno_adapters/openai.py,sha256=mGIwkqgNiIhMrPeQ2fJRL-chj55RyrxTSobBC3yUKbA,3607
23
+ zeno_core/__init__.py,sha256=nPS4Sc_J6RVCHkaLtschKWi0DokrKS4zUW1K_gEJAcI,1510
24
+ zeno_core/analytics.py,sha256=GV44S1YjDrON3kU6OwshSRONSQTEVcWkijrgD1AALjw,6595
25
+ zeno_core/rtlx_s.py,sha256=TQABm29VCLDxSbzQRq4bKmlPqSysYHQniw85klItHOM,19217
26
+ zeno_core/streak.py,sha256=lQKHL4jXr53LrWebRqR1C2mfsrlacaV4qJfP15b-XeE,6826
27
+ zeno_core/tlx_s.py,sha256=uCBRFaMMwEFNpmbUY00EOWYEf-4-_toitv80AiZNB9o,6154
28
+ zeno_sdk/__init__.py,sha256=zpIpXr9s0JrctbykFiEBxFSQZMdRQvpopAUt-c2Cngw,127
29
+ zeno_sdk/_runtime.py,sha256=HaIO3tlu1bXJNfYB10KubuDtQoWhUZTEhH_uyhQMM18,348
30
+ zeno_sdk/auth.py,sha256=5uvKCeMSslAGI7jcdVHGq8bjMwwV_kW18LvcoQ1f94I,499
31
+ zeno_sdk/client.py,sha256=WNmBygABgeZSo01anqbTkh4CygUX3s4fbl-99TuMTXQ,2759
32
+ zeno_sdk/config.py,sha256=ERnRdgqyl2o7OXJm5tV3YymTHkiR0Q8yTyUrTX_fD_c,1908
33
+ zeno_sdk/daemon.py,sha256=FdY-_df9vRM54uKGcdKYCXUabUmSNs4AH0f0MTEDTXw,2258
34
+ zeno_sdk/privacy.py,sha256=P4OuxwgfMOVvZfEkTAP15GZm5156wTM3vNO-l8P8tZI,1374
35
+ zeno_sdk/session.py,sha256=aQ6G9fOnnLtDLzGGyqu_2mgHO7JBieseY8TyCbSlpJo,6100
36
+ zeno_sdk/storage.py,sha256=3wf2ED8TeTNIrnQmQlJRTXIgLDVcp266grcaVOVEH4o,19076
37
+ zeno_sdk/_generated/__init__.py,sha256=LyDCZa0IW5FIpGsSH8j9owFzuVepSgycbx47T4w-5QQ,96
38
+ zeno_sdk/_generated/client.py,sha256=5Abp7n_ST0AsPwsY6BVUhOgkZib3sRnFWfwSmgkp0pc,26729
39
+ zeno_sdk/_migrations/alembic.ini,sha256=0gpaG_bxrMvXtPuM9eUlEbl6mD0BqwvglNZ52PxL-O4,526
40
+ zeno_sdk/_migrations/alembic/env.py,sha256=iz-0Ta1jTqgiZa42zczrrHWZgucF8I6TlKOiaqUB2LU,866
41
+ zeno_sdk/_migrations/alembic/script.py.mako,sha256=Jc9jK0i7Tb0UW4FKhYeffle1wcBwJklOJ0hO3EYyyUY,325
42
+ zeno_sdk/_migrations/alembic/versions/0001_initial.py,sha256=jw7WHmoPTWSu-nWj5xyU4nIJsdIywkGww4-NSPtYK90,3061
43
+ zeno_sdk/_migrations/alembic/versions/0002_cognition_samples.py,sha256=aOd76chDv36T9N4slvESE_TrHdCC5oMQjzqH67x2yF4,2151
44
+ zeno_sdk/_migrations/alembic/versions/0003_cognition_drivers.py,sha256=OqLzyFX3ri2BrL9kE8ZXaNtD5nqxqtURaHNmYPG93Ig,1198
45
+ zeno_sdk/_migrations/alembic/versions/0004_transcript_intelligence.py,sha256=vMkWSGI-WpZYV5nMioQW1rAnVgUdxVrnYg5kyCr0QNM,12724
46
+ zeno_sdk/adapters/__init__.py,sha256=qF1-El8YWrdQjYJpOyvOcZa3FdwoCvRGYQWyRjk71Bk,543
47
+ zeno_sdk/adapters/anthropic.py,sha256=OMZ8-UEXIHH5KoD9SKcQNX2B66oJU6M5KZGz9d90e7I,109
48
+ zeno_sdk/adapters/claude_code.py,sha256=gvXAJiS4q9bixsz33SMhFKASw1vVAOVQcAUUY8lkKE4,111
49
+ zeno_sdk/adapters/crewai.py,sha256=yeR5ihDZYrXfjw5Bg8fG_3QTHjQwE_0NDayELBHqAPo,154
50
+ zeno_sdk/adapters/langgraph.py,sha256=zWbVzdkU3zdkJwEqOzj1H652ECXNq0gRC_aSLFM0EXg,109
51
+ zeno_sdk/adapters/openai.py,sha256=EwIyTBTFtXOxuHbA31WNkMoQV4jdqukb8XMlpz-UiVg,106
52
+ zeno_sdk/types/__init__.py,sha256=knlgMTaeZ4gDBLpuAC4_mFvQ_oFktO9co9-XhfX1emQ,3267
53
+ zeno_session_intel/__init__.py,sha256=hPlu9-Qwxe2hlevZW6kQs_pgEzJqZWspXNNHy7zNt_8,880
54
+ zeno_session_intel/analytics.py,sha256=gw68B-h9nO7nkmg5VO3ue00Giqhq4vskJCP5D6DkNH4,21639
55
+ zeno_session_intel/compression.py,sha256=3q1NklgUrhC2K47dNmydqvGnnn59KRbwQBSnvdyCM6g,5066
56
+ zeno_session_intel/ingest.py,sha256=KAdoxK6yZnOicYrFFQxJR5O1ONd9kH1OOBPdQi2cPgg,15030
57
+ zeno_session_intel/model.py,sha256=jP2cocAVjl_MlBI0bralV_TFUkalo-PRP480aLjH9FY,4235
58
+ zeno_session_intel/prices.py,sha256=uMxVYL6N4HqK1_usJuEeXWtlDkmZ_wMRtF6yNiC67Bw,9905
59
+ zeno_session_intel/schema.py,sha256=VTmUWvSXB7zgH_zZpnEFdjqJ2fIQ1pQds3OX0oKMTXo,12376
60
+ zeno_session_intel/signals.py,sha256=JrInX4_QiOhPQfrky_OMHwjdb_lip-w44h-E_Xua1bg,11539
61
+ zeno_session_intel/taxonomy.py,sha256=u_9COlFHeI3sMg0Nqd_kg2sFfDjdQwx6R-OW0vA7iCQ,2107
62
+ zeno_session_intel/parsers/__init__.py,sha256=rEzx44_To5fZ4IeblMio7Dxx29w9vAEn6YjRTWEf9yM,1323
63
+ zeno_session_intel/parsers/claude_code.py,sha256=SeiT9jrolIsCvSxXUlb4fUWzHEWLekqA4-b6MY4hXH0,6405
64
+ zeno_session_intel/parsers/codex.py,sha256=5qn28QItZMiiDY9OvjfuRrgNfUozpcThQngZqueVqp8,10220
65
+ zeno_session_intel/parsers/cursor.py,sha256=DX1DZHlu1DxzWTGrDnVc4GMf52KdV8MtlwbO_ryix34,7352
66
+ zeno_cli-0.3.4.dist-info/METADATA,sha256=OIBlzm7x6cizBQ37C3tfrwYOUO2SGcMwKWg63wF190g,7792
67
+ zeno_cli-0.3.4.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
68
+ zeno_cli-0.3.4.dist-info/entry_points.txt,sha256=e4KAP7N_td8T76VwMcY_Ey0eCz2L8zAO_mpeUKr7CAQ,128
69
+ zeno_cli-0.3.4.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ zeno = zeno_cli.main:main
3
+ zeno-hud = zeno_cli.hud.zeno_hud:main
4
+ zeno-hud-bar = zeno_cli.hud.zeno_hud:bar_main
zeno_core/__init__.py ADDED
@@ -0,0 +1,67 @@
1
+ """Core analytics package."""
2
+
3
+ from .analytics import (
4
+ CurveResult,
5
+ SessionPoint,
6
+ fit_babysitting_tax_curve,
7
+ render_curve_ascii,
8
+ render_weekly_table,
9
+ save_curve_png,
10
+ weekly_summary,
11
+ )
12
+ from .rtlx_s import (
13
+ RTLXS_ITEMS,
14
+ RTLXS_SCHEMA_VERSION,
15
+ RTLXResponse,
16
+ load_last_defaults,
17
+ run_rtlxs_survey_tui,
18
+ save_last_defaults,
19
+ validate_item_keys,
20
+ )
21
+ from .streak import (
22
+ DAY_CUTOFF_HOUR,
23
+ StreakResult,
24
+ compute_streak,
25
+ has_logged_today,
26
+ read_streak_from_local_storage,
27
+ )
28
+ from .tlx_s import (
29
+ SUBSCALES,
30
+ TLXSurveyResult,
31
+ compute_babysitting_tax_index,
32
+ compute_composite_load,
33
+ run_tlx_survey_tui,
34
+ should_prompt_probe,
35
+ validate_item_wording,
36
+ )
37
+
38
+ __all__ = [
39
+ "SUBSCALES",
40
+ "TLXSurveyResult",
41
+ "CurveResult",
42
+ "SessionPoint",
43
+ "fit_babysitting_tax_curve",
44
+ "render_curve_ascii",
45
+ "render_weekly_table",
46
+ "save_curve_png",
47
+ "weekly_summary",
48
+ "compute_babysitting_tax_index",
49
+ "compute_composite_load",
50
+ "run_tlx_survey_tui",
51
+ "should_prompt_probe",
52
+ "validate_item_wording",
53
+ # RTLX-S (research 1, 2026-06-07 PM3)
54
+ "RTLXS_ITEMS",
55
+ "RTLXS_SCHEMA_VERSION",
56
+ "RTLXResponse",
57
+ "load_last_defaults",
58
+ "run_rtlxs_survey_tui",
59
+ "save_last_defaults",
60
+ "validate_item_keys",
61
+ # Streak tracker (research 1 PM3 follow-up, 2026-06-07)
62
+ "DAY_CUTOFF_HOUR",
63
+ "StreakResult",
64
+ "compute_streak",
65
+ "has_logged_today",
66
+ "read_streak_from_local_storage",
67
+ ]
zeno_core/analytics.py ADDED
@@ -0,0 +1,193 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from dataclasses import dataclass
5
+ from datetime import UTC, datetime, timedelta
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ # Resolves the np.ndarray annotations below for type-checkers / ruff
11
+ # WITHOUT a runtime import (these names are strings under
12
+ # `from __future__ import annotations`).
13
+ import numpy as np
14
+
15
+ # numpy / scipy / matplotlib (and rich) are imported lazily INSIDE the functions
16
+ # that use them, not at module top. Importing zeno_core.analytics used to pull
17
+ # the full scientific stack (~185ms: matplotlib.pyplot alone ~70ms, plus numpy
18
+ # and scipy), which every `zeno --version` / `status` / `tips` paid even though
19
+ # those paths never fit or plot a curve. Deferring keeps the hot path cheap;
20
+ # the libs load once, on first call, and are cached in sys.modules thereafter.
21
+ # (`from __future__ import annotations` makes the np.ndarray signatures below
22
+ # strings, so they do not trigger an import at definition time.)
23
+
24
+
25
+ @dataclass(slots=True)
26
+ class SessionPoint:
27
+ session_id: str
28
+ n_agents_active: float
29
+ composite_load: float
30
+ output_quality: float | None
31
+ ended_at: datetime | None = None
32
+
33
+
34
+ @dataclass(slots=True)
35
+ class CurveResult:
36
+ xs: list[float]
37
+ composite_curve: list[float]
38
+ quality_curve: list[float]
39
+ optimal_n_agents: int | None
40
+ calibration_in_progress: bool
41
+
42
+
43
+ def trim_outliers(points: list[SessionPoint]) -> list[SessionPoint]:
44
+ if len(points) < 20:
45
+ return points
46
+
47
+ import numpy as np
48
+
49
+ loads = np.array([point.composite_load for point in points], dtype=float)
50
+ low, high = np.percentile(loads, [5, 95])
51
+ filtered = [point for point in points if low <= point.composite_load <= high]
52
+ return filtered
53
+
54
+
55
+ def _tricube(distance: np.ndarray) -> np.ndarray:
56
+ import numpy as np
57
+
58
+ clipped = np.clip(1 - np.power(np.abs(distance), 3), 0, None)
59
+ return np.power(clipped, 3)
60
+
61
+
62
+ def lowess_smooth(xs: np.ndarray, ys: np.ndarray, bandwidth: float) -> np.ndarray:
63
+ import numpy as np
64
+ from scipy import linalg
65
+
66
+ n = len(xs)
67
+ if n == 0:
68
+ return np.array([])
69
+ if n == 1:
70
+ return ys.copy()
71
+
72
+ span = max(2, int(math.ceil(bandwidth * n)))
73
+ smoothed = np.zeros(n, dtype=float)
74
+ x_design = np.column_stack((np.ones(n), xs))
75
+
76
+ for index, x0 in enumerate(xs):
77
+ distances = np.abs(xs - x0)
78
+ h = np.partition(distances, min(span - 1, n - 1))[min(span - 1, n - 1)]
79
+ if h == 0:
80
+ weights = np.where(distances == 0, 1.0, 0.0)
81
+ else:
82
+ weights = _tricube(distances / h)
83
+ sqrt_w = np.sqrt(weights)
84
+ wx = x_design * sqrt_w[:, None]
85
+ wy = ys * sqrt_w
86
+ beta, *_ = linalg.lstsq(wx, wy)
87
+ smoothed[index] = beta[0] + beta[1] * x0
88
+ return smoothed
89
+
90
+
91
+ def fit_babysitting_tax_curve(points: list[SessionPoint]) -> CurveResult:
92
+ import numpy as np
93
+
94
+ filtered = trim_outliers(points)
95
+ if len(filtered) < 10:
96
+ return CurveResult([], [], [], None, calibration_in_progress=True)
97
+
98
+ bandwidth = 0.5 if len(filtered) < 50 else 0.3
99
+ sorted_points = sorted(filtered, key=lambda point: point.n_agents_active)
100
+ xs = np.array([point.n_agents_active for point in sorted_points], dtype=float)
101
+ composite = np.array([point.composite_load for point in sorted_points], dtype=float)
102
+ quality_values = np.array(
103
+ [
104
+ point.output_quality if point.output_quality is not None else 0.0
105
+ for point in sorted_points
106
+ ],
107
+ dtype=float,
108
+ )
109
+
110
+ composite_curve = lowess_smooth(xs, composite, bandwidth)
111
+ quality_curve = lowess_smooth(xs, quality_values, bandwidth)
112
+
113
+ max_index = int(np.argmax(quality_curve))
114
+ optimal = int(round(xs[max_index])) if len(xs) else None
115
+ return CurveResult(
116
+ xs=xs.tolist(),
117
+ composite_curve=composite_curve.tolist(),
118
+ quality_curve=quality_curve.tolist(),
119
+ optimal_n_agents=optimal,
120
+ calibration_in_progress=False,
121
+ )
122
+
123
+
124
+ def render_curve_ascii(curve: CurveResult) -> str:
125
+ if curve.calibration_in_progress:
126
+ return "Calibration in progress: need at least 10 sessions."
127
+ if not curve.xs:
128
+ return "No curve data available."
129
+
130
+ import numpy as np
131
+
132
+ chars = "▁▂▃▄▅▆▇█"
133
+ values = np.array(curve.quality_curve, dtype=float)
134
+ if np.allclose(values.max(), values.min()):
135
+ sparkline = chars[0] * len(values)
136
+ else:
137
+ scaled = (values - values.min()) / (values.max() - values.min())
138
+ sparkline = "".join(chars[int(round(item * (len(chars) - 1)))] for item in scaled)
139
+ return f"Quality curve: {sparkline}"
140
+
141
+
142
+ def save_curve_png(curve: CurveResult, output_path: Path) -> None:
143
+ import matplotlib
144
+
145
+ matplotlib.use("Agg")
146
+ from matplotlib import pyplot as plt
147
+
148
+ output_path.parent.mkdir(parents=True, exist_ok=True)
149
+ plt.figure(figsize=(8, 4))
150
+ plt.plot(curve.xs, curve.composite_curve, label="Composite Load", color="#7a1f2f")
151
+ plt.plot(curve.xs, curve.quality_curve, label="Output Quality", color="#1f4f7a")
152
+ plt.xlabel("n_agents_active")
153
+ plt.ylabel("score")
154
+ plt.title("Babysitting Tax Curve")
155
+ plt.legend()
156
+ plt.tight_layout()
157
+ plt.savefig(output_path)
158
+ plt.close()
159
+
160
+
161
+ def weekly_summary(points: list[SessionPoint], *, now: datetime | None = None) -> dict[str, float]:
162
+ if not points:
163
+ return {"sessions": 0, "avg_composite_load": 0.0, "avg_quality": 0.0}
164
+
165
+ import numpy as np
166
+
167
+ now = now or datetime.now(tz=UTC)
168
+ week_ago = now - timedelta(days=7)
169
+ recent = [point for point in points if point.ended_at and point.ended_at >= week_ago]
170
+ if not recent:
171
+ return {"sessions": 0, "avg_composite_load": 0.0, "avg_quality": 0.0}
172
+ loads = [point.composite_load for point in recent]
173
+ quality = [point.output_quality or 0.0 for point in recent]
174
+ return {
175
+ "sessions": float(len(recent)),
176
+ "avg_composite_load": float(np.mean(loads)),
177
+ "avg_quality": float(np.mean(quality)),
178
+ }
179
+
180
+
181
+ def render_weekly_table(summary: dict[str, float]) -> str:
182
+ from rich.console import Console
183
+ from rich.table import Table
184
+
185
+ console = Console(record=True)
186
+ table = Table(title="Weekly Babysitting Summary")
187
+ table.add_column("Metric")
188
+ table.add_column("Value")
189
+ table.add_row("Sessions", str(int(summary["sessions"])))
190
+ table.add_row("Avg Composite Load", f"{summary['avg_composite_load']:.2f}")
191
+ table.add_row("Avg Output Quality", f"{summary['avg_quality']:.2f}")
192
+ console.print(table)
193
+ return console.export_text()