hyperweave 0.2.4__tar.gz → 0.2.5__tar.gz

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 (334) hide show
  1. hyperweave-0.2.5/.pre-commit-config.yaml +31 -0
  2. {hyperweave-0.2.4 → hyperweave-0.2.5}/CHANGELOG.md +6 -0
  3. {hyperweave-0.2.4 → hyperweave-0.2.5}/PKG-INFO +2 -2
  4. {hyperweave-0.2.4 → hyperweave-0.2.5}/README.md +1 -1
  5. {hyperweave-0.2.4 → hyperweave-0.2.5}/pyproject.toml +1 -0
  6. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/_version.py +2 -2
  7. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/resolvers/chart.py +3 -11
  8. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/connectors/github.py +2 -7
  9. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/render/chart_engine.py +96 -43
  10. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/fixtures/session.json +1 -1
  11. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_chart_engine.py +40 -3
  12. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_chart_frame.py +3 -11
  13. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_serve_live_state.py +3 -9
  14. {hyperweave-0.2.4 → hyperweave-0.2.5}/uv.lock +91 -0
  15. {hyperweave-0.2.4 → hyperweave-0.2.5}/.dockerignore +0 -0
  16. {hyperweave-0.2.4 → hyperweave-0.2.5}/.github/workflows/ci.yml +0 -0
  17. {hyperweave-0.2.4 → hyperweave-0.2.5}/.github/workflows/deploy.yml +0 -0
  18. {hyperweave-0.2.4 → hyperweave-0.2.5}/.github/workflows/publish.yml +0 -0
  19. {hyperweave-0.2.4 → hyperweave-0.2.5}/.gitignore +0 -0
  20. {hyperweave-0.2.4 → hyperweave-0.2.5}/Dockerfile +0 -0
  21. {hyperweave-0.2.4 → hyperweave-0.2.5}/LICENSE +0 -0
  22. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/buttons/button-liquid.svg +0 -0
  23. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/cards/card-butterfly.svg +0 -0
  24. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/cards/card-python.svg +0 -0
  25. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/cards/card-sunflower.svg +0 -0
  26. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/cards/card-waves.svg +0 -0
  27. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
  28. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
  29. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
  30. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/banner.svg +0 -0
  31. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
  32. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
  33. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
  34. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
  35. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
  36. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
  37. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
  38. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
  39. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
  40. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
  41. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/brutalist-emerald/strip.svg +0 -0
  42. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
  43. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
  44. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
  45. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/banner.svg +0 -0
  46. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
  47. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/icons/github.svg +0 -0
  48. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
  49. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
  50. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/icons/x.svg +0 -0
  51. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
  52. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
  53. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
  54. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
  55. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
  56. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/chrome-horizon/strip.svg +0 -0
  57. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/telemetry/master_card.svg +0 -0
  58. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/telemetry/receipt.svg +0 -0
  59. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/examples/telemetry/rhythm_strip.svg +0 -0
  60. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/footers/inneraura-footer-liquid.svg +0 -0
  61. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/footers/inneraura-footer-purple.svg +0 -0
  62. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/footers/inneraura-footer.svg +0 -0
  63. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/hyperweave-banner.svg +0 -0
  64. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-discord.svg +0 -0
  65. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-docs.svg +0 -0
  66. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-github.svg +0 -0
  67. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
  68. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
  69. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
  70. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-x.svg +0 -0
  71. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
  72. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/marquees/badge-showcase-triple.svg +0 -0
  73. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/marquees/genome-marquee-triple.svg +0 -0
  74. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/marquees/sample-badges.svg +0 -0
  75. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/404.svg +0 -0
  76. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/callout-icons.svg +0 -0
  77. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/divider.svg +0 -0
  78. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/favicon.svg +0 -0
  79. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/hero-banner.svg +0 -0
  80. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
  81. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
  82. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/loader.svg +0 -0
  83. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/mintlify-assets/og-image.svg +0 -0
  84. {hyperweave-0.2.4 → hyperweave-0.2.5}/assets/timelines/hyperweave-roadmap.svg +0 -0
  85. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/assets/favicon.svg +0 -0
  86. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/assets/hyperweave-banner.svg +0 -0
  87. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/assets/hyperweave-navbar-logo.svg +0 -0
  88. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/assets/og-image.svg +0 -0
  89. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/explanation/architecture.mdx +0 -0
  90. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/explanation/camo-compatibility.mdx +0 -0
  91. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/explanation/cim-compliance.mdx +0 -0
  92. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/explanation/genome-profile-system.mdx +0 -0
  93. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/explanation/metadata-tiers.mdx +0 -0
  94. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/how-to/add-motion-to-badges.mdx +0 -0
  95. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/how-to/create-session-receipts.mdx +0 -0
  96. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
  97. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/how-to/use-live-data-badges.mdx +0 -0
  98. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/introduction.mdx +0 -0
  99. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/mint.json +0 -0
  100. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/quickstart.mdx +0 -0
  101. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/reference/cli.mdx +0 -0
  102. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/reference/compose-spec.mdx +0 -0
  103. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/reference/genomes.mdx +0 -0
  104. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/reference/http-api.mdx +0 -0
  105. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/reference/mcp-tools.mdx +0 -0
  106. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/reference/motions.mdx +0 -0
  107. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/reference/telemetry-contract.mdx +0 -0
  108. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/spec/hyperweave-protocol.mdx +0 -0
  109. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
  110. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/tutorials/compose-your-first-badge.mdx +0 -0
  111. {hyperweave-0.2.4 → hyperweave-0.2.5}/docs/tutorials/install-session-telemetry.mdx +0 -0
  112. {hyperweave-0.2.4 → hyperweave-0.2.5}/fly.toml +0 -0
  113. {hyperweave-0.2.4 → hyperweave-0.2.5}/hooks/install.py +0 -0
  114. {hyperweave-0.2.4 → hyperweave-0.2.5}/hooks/session_end.sh +0 -0
  115. {hyperweave-0.2.4 → hyperweave-0.2.5}/justfile +0 -0
  116. {hyperweave-0.2.4 → hyperweave-0.2.5}/scripts/extract_glyphs.py +0 -0
  117. {hyperweave-0.2.4 → hyperweave-0.2.5}/scripts/generate_hw_compliant.py +0 -0
  118. {hyperweave-0.2.4 → hyperweave-0.2.5}/scripts/generate_proofset.py +0 -0
  119. {hyperweave-0.2.4 → hyperweave-0.2.5}/scripts/stress_test.py +0 -0
  120. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/__init__.py +0 -0
  121. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/__main__.py +0 -0
  122. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/cli.py +0 -0
  123. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/__init__.py +0 -0
  124. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/assembler.py +0 -0
  125. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/context.py +0 -0
  126. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/engine.py +0 -0
  127. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/lanes.py +0 -0
  128. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/resolver.py +0 -0
  129. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/resolvers/__init__.py +0 -0
  130. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/resolvers/stats.py +0 -0
  131. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/compose/resolvers/timeline.py +0 -0
  132. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/config/__init__.py +0 -0
  133. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/config/genome_validator.py +0 -0
  134. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/config/loader.py +0 -0
  135. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/config/registry.py +0 -0
  136. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/config/settings.py +0 -0
  137. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/connectors/__init__.py +0 -0
  138. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/connectors/arxiv.py +0 -0
  139. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/connectors/base.py +0 -0
  140. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/connectors/cache.py +0 -0
  141. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/connectors/rest.py +0 -0
  142. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/__init__.py +0 -0
  143. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/color.py +0 -0
  144. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/contracts.py +0 -0
  145. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/enums.py +0 -0
  146. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/models.py +0 -0
  147. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/schema.py +0 -0
  148. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/state.py +0 -0
  149. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/text.py +0 -0
  150. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/core/thresholds.py +0 -0
  151. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/css/accessibility.css +0 -0
  152. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/css/bridge.css +0 -0
  153. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/css/expression.css +0 -0
  154. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/css/phase-colors.css +0 -0
  155. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/css/status.css +0 -0
  156. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/css/telemetry.css +0 -0
  157. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/font-metrics/inter.json +0 -0
  158. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
  159. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
  160. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
  161. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
  162. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/genomes/brutalist-emerald.json +0 -0
  163. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/genomes/chrome-horizon.json +0 -0
  164. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/genomes/telemetry-void.json +0 -0
  165. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/glyphs.json +0 -0
  166. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
  167. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
  168. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
  169. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
  170. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
  171. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/bars.yaml +0 -0
  172. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/breach.yaml +0 -0
  173. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/broadcast.yaml +0 -0
  174. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/cascade.yaml +0 -0
  175. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/collapse.yaml +0 -0
  176. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/converge.yaml +0 -0
  177. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/crash.yaml +0 -0
  178. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/drop.yaml +0 -0
  179. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/kinetic/pulse.yaml +0 -0
  180. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/motions/static.yaml +0 -0
  181. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/policies/normal.json +0 -0
  182. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/policies/permissive.json +0 -0
  183. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/policies/ungoverned.json +0 -0
  184. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
  185. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
  186. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
  187. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/profiles/chrome.yaml +0 -0
  188. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/specimens.yaml +0 -0
  189. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
  190. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
  191. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
  192. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/telemetry/tool-classes.yaml +0 -0
  193. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
  194. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/arrow.json +0 -0
  195. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/aurora.json +0 -0
  196. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/beacon.json +0 -0
  197. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/bracket3d.json +0 -0
  198. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/chevron.json +0 -0
  199. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/crosshair.json +0 -0
  200. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/diamond.json +0 -0
  201. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/rocket.json +0 -0
  202. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/rules/bar.json +0 -0
  203. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
  204. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
  205. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
  206. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/rules/straight.json +0 -0
  207. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/rules/wave.json +0 -0
  208. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/data/terminals/stijl.json +0 -0
  209. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/kit.py +0 -0
  210. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/mcp/__init__.py +0 -0
  211. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/mcp/__main__.py +0 -0
  212. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/mcp/server.py +0 -0
  213. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/py.typed +0 -0
  214. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/render/__init__.py +0 -0
  215. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/render/fonts.py +0 -0
  216. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/render/glyphs.py +0 -0
  217. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/render/motion.py +0 -0
  218. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/render/templates.py +0 -0
  219. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/serve/__init__.py +0 -0
  220. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/serve/app.py +0 -0
  221. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/__init__.py +0 -0
  222. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/capture.py +0 -0
  223. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/contract.py +0 -0
  224. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/corrections.py +0 -0
  225. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/cost.py +0 -0
  226. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/models.py +0 -0
  227. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/parser.py +0 -0
  228. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/telemetry/stages.py +0 -0
  229. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
  230. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
  231. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
  232. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
  233. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/metadata.svg.j2 +0 -0
  234. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
  235. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
  236. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
  237. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/status.svg.j2 +0 -0
  238. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
  239. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/components/treemap.svg.j2 +0 -0
  240. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/document.svg.j2 +0 -0
  241. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
  242. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
  243. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
  244. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
  245. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +0 -0
  246. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
  247. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
  248. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
  249. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/banner/brutalist-defs.j2 +0 -0
  250. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/banner/chrome-defs.j2 +0 -0
  251. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/banner/default-defs.j2 +0 -0
  252. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/banner.svg.j2 +0 -0
  253. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
  254. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +0 -0
  255. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
  256. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/chart/chrome-content.j2 +0 -0
  257. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
  258. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
  259. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
  260. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
  261. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
  262. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
  263. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
  264. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
  265. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
  266. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
  267. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-counter/brutalist-content.j2 +0 -0
  268. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-counter/brutalist-defs.j2 +0 -0
  269. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-counter/chrome-content.j2 +0 -0
  270. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-counter/chrome-defs.j2 +0 -0
  271. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-counter.svg.j2 +0 -0
  272. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
  273. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
  274. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
  275. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
  276. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
  277. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-vertical/brutalist-content.j2 +0 -0
  278. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-vertical/brutalist-defs.j2 +0 -0
  279. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-vertical/chrome-content.j2 +0 -0
  280. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-vertical/chrome-defs.j2 +0 -0
  281. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/marquee-vertical.svg.j2 +0 -0
  282. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
  283. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
  284. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
  285. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
  286. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
  287. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/stats/chrome-content.j2 +0 -0
  288. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
  289. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
  290. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
  291. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
  292. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
  293. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -0
  294. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -0
  295. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
  296. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
  297. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
  298. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
  299. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/strip.svg.j2 +0 -0
  300. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/timeline/default-content.j2 +0 -0
  301. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/frames/timeline.svg.j2 +0 -0
  302. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
  303. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
  304. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
  305. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
  306. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
  307. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/bars.svg.j2 +0 -0
  308. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/breach.svg.j2 +0 -0
  309. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/broadcast.svg.j2 +0 -0
  310. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/cascade.svg.j2 +0 -0
  311. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/collapse.svg.j2 +0 -0
  312. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/converge.svg.j2 +0 -0
  313. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/crash.svg.j2 +0 -0
  314. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/drop.svg.j2 +0 -0
  315. {hyperweave-0.2.4 → hyperweave-0.2.5}/src/hyperweave/templates/motions/kinetic/pulse.svg.j2 +0 -0
  316. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/__init__.py +0 -0
  317. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/conftest.py +0 -0
  318. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/fixtures/github_contributions/synthetic.html +0 -0
  319. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/fixtures/session.jsonl +0 -0
  320. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_assembler.py +0 -0
  321. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_connectors.py +0 -0
  322. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_core.py +0 -0
  323. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_github_scrape.py +0 -0
  324. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_kit.py +0 -0
  325. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_mcp.py +0 -0
  326. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_paradigm_dispatch.py +0 -0
  327. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_proofset.py +0 -0
  328. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_render.py +0 -0
  329. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_serve.py +0 -0
  330. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_state_inference.py +0 -0
  331. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_stats_card.py +0 -0
  332. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_telemetry.py +0 -0
  333. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_telemetry_integration.py +0 -0
  334. {hyperweave-0.2.4 → hyperweave-0.2.5}/tests/test_timeline.py +0 -0
@@ -0,0 +1,31 @@
1
+ # Runs `ruff check` + `ruff format` automatically on every `git commit`.
2
+ # Mirrors the two lint steps CI enforces in .github/workflows/ci.yml so CI
3
+ # never surfaces formatting or lint failures that local commits should catch.
4
+ #
5
+ # One-time setup after cloning:
6
+ # uv sync --group dev
7
+ # uv run pre-commit install
8
+ #
9
+ # Manual run across the whole repo:
10
+ # uv run pre-commit run --all-files
11
+
12
+ repos:
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.9.0
15
+ hooks:
16
+ - id: ruff
17
+ args: [--fix]
18
+ - id: ruff-format
19
+
20
+ - repo: https://github.com/pre-commit/pre-commit-hooks
21
+ rev: v5.0.0
22
+ hooks:
23
+ - id: trailing-whitespace
24
+ exclude: \.svg$
25
+ - id: end-of-file-fixer
26
+ exclude: \.(svg|b64)$
27
+ - id: check-yaml
28
+ - id: check-toml
29
+ - id: check-merge-conflict
30
+ - id: check-added-large-files
31
+ args: [--maxkb=500]
@@ -5,6 +5,12 @@ All notable changes to HyperWeave are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.5] - 2026-04-13
9
+
10
+ ### Fixed
11
+
12
+ - **Chart bezier regression on certain data ranges.** The chrome chart bezier path used horizontal tangents at every anchor, which produced a smooth curve only when input points were widely and evenly spaced. On tighter or uneven distributions — common with real GitHub stargazer data — the curve degenerated into flat-then-vertical segments. Replaced with Fritsch-Carlson monotonic cubic interpolation (the same algorithm D3's `curveMonotoneX` uses), which guarantees the curve passes through every anchor, never overshoots between points, and handles any point spacing robustly.
13
+
8
14
  ## [0.2.4] - 2026-04-13
9
15
 
10
16
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperweave
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Headless visual output layer for AI agents. One API call → self-contained SVG.
5
5
  Project-URL: Repository, https://github.com/InnerAura/hyperweave
6
6
  Project-URL: Issues, https://github.com/InnerAura/hyperweave/issues
@@ -397,4 +397,4 @@ HyperWeave is early. If you're interested in building genomes, extending frame t
397
397
  [x]: https://x.com/InnerAuraLabs
398
398
  [youtube]: https://www.youtube.com/@InnerAuraLabs
399
399
 
400
- [return-top]: https://raw.githubusercontent.com/InnerAura/hyperweave/f36c8969d15d76da4400ebcfaa04ec1e2eacb170/assets/buttons/button-liquid.svg
400
+ [return-top]: https://raw.githubusercontent.com/InnerAura/hyperweave/f36c8969d15d76da4400ebcfaa04ec1e2eacb170/assets/buttons/button-liquid.svg
@@ -365,4 +365,4 @@ HyperWeave is early. If you're interested in building genomes, extending frame t
365
365
  [x]: https://x.com/InnerAuraLabs
366
366
  [youtube]: https://www.youtube.com/@InnerAuraLabs
367
367
 
368
- [return-top]: https://raw.githubusercontent.com/InnerAura/hyperweave/f36c8969d15d76da4400ebcfaa04ec1e2eacb170/assets/buttons/button-liquid.svg
368
+ [return-top]: https://raw.githubusercontent.com/InnerAura/hyperweave/f36c8969d15d76da4400ebcfaa04ec1e2eacb170/assets/buttons/button-liquid.svg
@@ -77,6 +77,7 @@ dev = [
77
77
  "ruff>=0.9",
78
78
  "mypy>=1.14",
79
79
  "types-pyyaml>=6.0.12.20250915",
80
+ "pre-commit>=4.0",
80
81
  ]
81
82
 
82
83
  [tool.pytest.ini_options]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.4'
22
- __version_tuple__ = version_tuple = (0, 2, 4)
21
+ __version__ = version = '0.2.5'
22
+ __version_tuple__ = version_tuple = (0, 2, 5)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -40,11 +40,7 @@ def resolve_chart(
40
40
  # date labels at bottom, and hero value at right.
41
41
  # Dimensions match the target SVGs in tier2/genomes/.
42
42
  paradigm = genome.get("paradigms", {}).get("chart", "brutalist")
43
- vp = (
44
- Viewport(x=80, y=160, w=750, h=250)
45
- if paradigm == "chrome"
46
- else Viewport(x=80, y=150, w=760, h=245)
47
- )
43
+ vp = Viewport(x=80, y=160, w=750, h=250) if paradigm == "chrome" else Viewport(x=80, y=150, w=760, h=245)
48
44
 
49
45
  # Three-state machine. "fresh" preserved (not renamed to "live") for
50
46
  # backward compat with the existing data-hw-status contract; "empty" is
@@ -59,12 +55,8 @@ def resolve_chart(
59
55
  current_stars = 0
60
56
  empty_message = "DATA UNAVAILABLE"
61
57
  else:
62
- current_stars = int(
63
- connector.get("current_stars") or connector.get("stars_total") or 0
64
- )
65
- raw_points = list(
66
- connector.get("points") or connector.get("star_history") or []
67
- )
58
+ current_stars = int(connector.get("current_stars") or connector.get("stars_total") or 0)
59
+ raw_points = list(connector.get("points") or connector.get("star_history") or [])
68
60
  if current_stars == 0:
69
61
  # Truthful zero-star state (brand-new repo) — render empty, don't fabricate.
70
62
  status = "empty"
@@ -191,10 +191,7 @@ async def fetch_stargazer_history(
191
191
  # a duplicate-date "now" point, producing a zero time-range polyline. Use
192
192
  # each stargazer's own timestamp instead — the whole page fits in one call.
193
193
  if total_pages == 1:
194
- single_page_url = (
195
- f"https://api.github.com/repos/{identifier}/stargazers"
196
- f"?per_page={_STARGAZER_PAGE_SIZE}&page=1"
197
- )
194
+ single_page_url = f"https://api.github.com/repos/{identifier}/stargazers?per_page={_STARGAZER_PAGE_SIZE}&page=1"
198
195
  page_payload = await fetch_json(
199
196
  single_page_url,
200
197
  provider=PROVIDER,
@@ -204,9 +201,7 @@ async def fetch_stargazer_history(
204
201
  if isinstance(page_payload, list):
205
202
  for idx, entry in enumerate(page_payload):
206
203
  if isinstance(entry, dict) and entry.get("starred_at"):
207
- single_page_points.append(
208
- {"date": entry["starred_at"], "count": idx + 1}
209
- )
204
+ single_page_points.append({"date": entry["starred_at"], "count": idx + 1})
210
205
  single_page_result: dict[str, Any] = {
211
206
  "points": single_page_points,
212
207
  "current_stars": total_stars,
@@ -136,11 +136,7 @@ def _project_points(
136
136
  for i, p in enumerate(points):
137
137
  # When all timestamps are identical (t_span <= 0), distribute points
138
138
  # evenly by index rather than collapsing them all to vp.x.
139
- frac_t = (
140
- i / max(1, n - 1)
141
- if t_span <= 0
142
- else (p.date.timestamp() - t0) / t_span
143
- )
139
+ frac_t = i / max(1, n - 1) if t_span <= 0 else (p.date.timestamp() - t0) / t_span
144
140
  frac_v = (p.value - v_lo) / v_span
145
141
  px = vp.x + round(frac_t * vp.w)
146
142
  py = vp.y + vp.h - round(frac_v * vp.h)
@@ -157,30 +153,100 @@ def _build_polyline_points(projected: list[tuple[int, int]]) -> str:
157
153
 
158
154
 
159
155
  def _build_bezier_path(projected: list[tuple[int, int]]) -> str:
160
- """Build a smooth cubic bezier path string through the projected points.
161
-
162
- Uses the "Catmull-Rom-ish" trick: each segment's control points are at
163
- one third of the previous/next delta. This produces a visually smooth
164
- curve without cornering at every data point — matches the chrome-horizon
165
- mockup's bezier style.
156
+ """Build a smooth cubic bezier path using Fritsch-Carlson monotonic cubic interpolation.
157
+
158
+ This is the same curve D3 renders via ``curveMonotoneX``. For data with
159
+ monotonically increasing x-coordinates (e.g. any time-series chart like
160
+ star history), the curve is guaranteed to:
161
+
162
+ - Pass through every anchor point (C0 continuity).
163
+ - Be C1-continuous (smooth tangent at every anchor).
164
+ - Be monotonic wherever the input is monotonic (no dips between rising
165
+ points).
166
+ - Not overshoot — control handles stay within their segment in x,
167
+ regardless of uneven x-spacing.
168
+
169
+ The previous implementation placed horizontal control handles at every
170
+ anchor (``c1y = y_prev``, ``c2y = y_cur``). That shape produced two
171
+ visual artifacts on real data:
172
+
173
+ 1. **Flat-then-vertical** for bursty growth — horizontal tangents forced
174
+ each segment into a plateau → S-curve → plateau shape, so the chart
175
+ read as flat sections punctuated by sharp rises.
176
+
177
+ 2. **Self-intersecting segments** when two adjacent anchors were close
178
+ in x — the hard-coded ``dx = max(4, (x_cur - x_prev) // 3)`` produced
179
+ ``c2.x < c1.x``, rasterizing badly on mobile/Camo.
180
+
181
+ Fritsch-Carlson solves both by computing per-segment slopes, deriving a
182
+ tangent at each point from the neighboring slopes, and then rescaling
183
+ tangent magnitudes with the ``α² + β² > 9`` test so control handles
184
+ never extend past their segment.
185
+
186
+ References:
187
+ Fritsch, F. N.; Carlson, R. E. (1980). "Monotone Piecewise Cubic
188
+ Interpolation". SIAM Journal on Numerical Analysis. 17 (2): 238-246.
166
189
  """
167
- if not projected:
190
+ n = len(projected)
191
+ if n == 0:
168
192
  return ""
169
- if len(projected) == 1:
193
+ if n == 1:
170
194
  x, y = projected[0]
171
195
  return f"M{x},{y}"
172
-
196
+ if n == 2:
197
+ # Two points → straight-line bezier (no interior tangents to compute).
198
+ x0, y0 = projected[0]
199
+ x1, y1 = projected[1]
200
+ dx = (x1 - x0) / 3
201
+ c1x, c1y = round(x0 + dx), y0
202
+ c2x, c2y = round(x1 - dx), y1
203
+ return f"M{x0},{y0} C{c1x},{c1y} {c2x},{c2y} {x1},{y1}"
204
+
205
+ # 1. Per-segment slopes m_i = (y_{i+1} - y_i) / (x_{i+1} - x_i).
206
+ slopes: list[float] = []
207
+ for i in range(n - 1):
208
+ sx = projected[i + 1][0] - projected[i][0]
209
+ sy = projected[i + 1][1] - projected[i][1]
210
+ slopes.append(sy / sx if sx != 0 else 0.0)
211
+
212
+ # 2. Initial tangents at each anchor. Endpoints use the one adjacent slope;
213
+ # interior points use the average of the two adjacent slopes, but set to 0
214
+ # at turning points (where the two slopes have opposite sign).
215
+ tangents: list[float] = [0.0] * n
216
+ tangents[0] = slopes[0]
217
+ tangents[-1] = slopes[-1]
218
+ for i in range(1, n - 1):
219
+ if slopes[i - 1] * slopes[i] > 0:
220
+ tangents[i] = (slopes[i - 1] + slopes[i]) / 2
221
+ # else: turning point → tangent stays 0
222
+
223
+ # 3. Fritsch-Carlson overshoot prevention. For each segment, if the
224
+ # (alpha, beta) pair falls outside the monotonicity circle of radius 3,
225
+ # rescale both tangents by tau = 3 / sqrt(alpha^2 + beta^2).
226
+ for i in range(n - 1):
227
+ if slopes[i] == 0:
228
+ tangents[i] = 0.0
229
+ tangents[i + 1] = 0.0
230
+ continue
231
+ alpha = tangents[i] / slopes[i]
232
+ beta = tangents[i + 1] / slopes[i]
233
+ if alpha * alpha + beta * beta > 9:
234
+ tau = 3.0 / (alpha * alpha + beta * beta) ** 0.5
235
+ tangents[i] = tau * alpha * slopes[i]
236
+ tangents[i + 1] = tau * beta * slopes[i]
237
+
238
+ # 4. Convert tangents to Bezier control points. For each segment, the
239
+ # control x-offset is 1/3 of segment width; the y-offset is that same
240
+ # 1/3 width scaled by the anchor's tangent (slope).
173
241
  parts: list[str] = [f"M{projected[0][0]},{projected[0][1]}"]
174
- n = len(projected)
175
- for i in range(1, n):
176
- x_prev, y_prev = projected[i - 1]
177
- x_cur, y_cur = projected[i]
178
- # Horizontal offset for control handles — one third of segment width.
179
- dx = max(4, (x_cur - x_prev) // 3)
180
- c1x = x_prev + dx
181
- c1y = y_prev
182
- c2x = x_cur - dx
183
- c2y = y_cur
242
+ for i in range(n - 1):
243
+ x_prev, y_prev = projected[i]
244
+ x_cur, y_cur = projected[i + 1]
245
+ seg_dx = (x_cur - x_prev) / 3
246
+ c1x = round(x_prev + seg_dx)
247
+ c1y = round(y_prev + tangents[i] * seg_dx)
248
+ c2x = round(x_cur - seg_dx)
249
+ c2y = round(y_cur - tangents[i + 1] * seg_dx)
184
250
  parts.append(f"C{c1x},{c1y} {c2x},{c2y} {x_cur},{y_cur}")
185
251
  return " ".join(parts)
186
252
 
@@ -413,9 +479,7 @@ def _format_y_tick(value: int) -> str:
413
479
  return f"{s}K"
414
480
 
415
481
 
416
- def _build_y_labels(
417
- ticks: list[int], v_min: int, v_max: int, vp: Viewport
418
- ) -> list[dict[str, Any]]:
482
+ def _build_y_labels(ticks: list[int], v_min: int, v_max: int, vp: Viewport) -> list[dict[str, Any]]:
419
483
  """Project tick values into ``{y, text}`` dicts for template consumption.
420
484
 
421
485
  Uses the same (v_min, v_max) range the caller passes to
@@ -433,9 +497,7 @@ def _build_y_labels(
433
497
  return out
434
498
 
435
499
 
436
- def _build_x_year_labels(
437
- points: list[ChartPoint], vp: Viewport
438
- ) -> list[dict[str, Any]]:
500
+ def _build_x_year_labels(points: list[ChartPoint], vp: Viewport) -> list[dict[str, Any]]:
439
501
  """Return year-string labels positioned at jan-1 boundaries in the data range.
440
502
 
441
503
  Matches star-history.com's convention: just year numbers (2024, 2025, 2026)
@@ -454,9 +516,7 @@ def _build_x_year_labels(
454
516
  t1 = points[-1].date.timestamp()
455
517
  t_span = max(1.0, t1 - t0)
456
518
 
457
- labels: list[dict[str, Any]] = [
458
- {"x": vp.x, "text": str(y_start), "anchor": "start"}
459
- ]
519
+ labels: list[dict[str, Any]] = [{"x": vp.x, "text": str(y_start), "anchor": "start"}]
460
520
  for y in range(y_start + 1, y_end + 1):
461
521
  jan1 = datetime(y, 1, 1, tzinfo=UTC).timestamp()
462
522
  if t0 < jan1 <= t1:
@@ -489,9 +549,7 @@ def _build_empty_state(vp: Viewport, message: str) -> str:
489
549
  )
490
550
 
491
551
 
492
- def _build_gridlines_from_ticks(
493
- y_labels: list[dict[str, Any]], vp: Viewport
494
- ) -> str:
552
+ def _build_gridlines_from_ticks(y_labels: list[dict[str, Any]], vp: Viewport) -> str:
495
553
  """Horizontal gridlines at each Y-tick's Y-position.
496
554
 
497
555
  Replaces the uniform ``_build_gridlines`` when real data is present — so
@@ -501,10 +559,7 @@ def _build_gridlines_from_ticks(
501
559
  lines: list[str] = []
502
560
  for label in y_labels:
503
561
  y = int(label["y"])
504
- lines.append(
505
- f'<line x1="{vp.x}" y1="{y}" x2="{vp.x + vp.w}" y2="{y}" '
506
- f'class="hw-chart-gridline"/>'
507
- )
562
+ lines.append(f'<line x1="{vp.x}" y1="{y}" x2="{vp.x + vp.w}" y2="{y}" class="hw-chart-gridline"/>')
508
563
  return "".join(lines)
509
564
 
510
565
 
@@ -621,9 +676,7 @@ def build_chart_svg(
621
676
  # Empty state overlay: only when there are no data points AND a message
622
677
  # was provided. Without a message the chart degrades silently (useful for
623
678
  # embedded charts in stats.py that don't need a user-facing label).
624
- empty_state_fragment = (
625
- _build_empty_state(viewport, empty_message or "") if not points else ""
626
- )
679
+ empty_state_fragment = _build_empty_state(viewport, empty_message or "") if not points else ""
627
680
 
628
681
  return {
629
682
  "defs": "",
@@ -120,4 +120,4 @@
120
120
  }
121
121
  ],
122
122
  "agents": []
123
- }
123
+ }
@@ -140,6 +140,45 @@ def test_build_bezier_path_starts_with_M() -> None:
140
140
  assert "C" in out # cubic bezier control points present
141
141
 
142
142
 
143
+ def test_build_bezier_path_no_degenerate_segments_on_close_points() -> None:
144
+ """When adjacent anchors are close in x, control handles must not cross.
145
+
146
+ Regression for the chrome "flat-then-vertical" bug: the previous
147
+ horizontal-handle implementation used ``dx = max(4, (x_cur - x_prev) // 3)``,
148
+ which produced ``c2.x < c1.x`` when points were close. Catmull-Rom tangents
149
+ scale with local chord length and avoid this.
150
+ """
151
+ import re
152
+
153
+ # Points clustered tightly in x (simulates slow-growth early period).
154
+ points = [(80, 410), (81, 390), (88, 368), (103, 348), (128, 325), (830, 169)]
155
+ path = _build_bezier_path(points)
156
+ segments = re.findall(r"C(\d+),(\d+) (\d+),(\d+) (\d+),(\d+)", path)
157
+ assert len(segments) == len(points) - 1
158
+ for c1x, _, c2x, _, _, _ in segments:
159
+ assert int(c2x) >= int(c1x), f"degenerate segment: c1x={c1x}, c2x={c2x} (c2 must not precede c1)"
160
+
161
+
162
+ def test_build_bezier_path_tangents_follow_neighbor_slope() -> None:
163
+ """Tangents should respect data slope — not force horizontal tangent at every anchor.
164
+
165
+ With the old horizontal-handle approach, the first segment's c1 always had
166
+ ``c1.y == y_prev``. For data that rises, the new Catmull-Rom code shifts c1
167
+ toward the next point's y, producing a non-horizontal start.
168
+ """
169
+ # Rising data: y decreases (flipped SVG coords mean smaller y = higher on chart).
170
+ points = [(0, 400), (100, 300), (200, 200), (300, 100)]
171
+ path = _build_bezier_path(points)
172
+ import re
173
+
174
+ first_segment = re.match(r"M0,400 C(\d+),(\d+)", path)
175
+ assert first_segment is not None
176
+ c1y = int(first_segment.group(2))
177
+ # Old code produced c1y == 400 (horizontal tangent). New code should
178
+ # tilt toward y_next=300, so c1y < 400.
179
+ assert c1y < 400, f"expected tangent to tilt upward, got c1y={c1y}"
180
+
181
+
143
182
  def test_build_area_polygon_closes_to_baseline() -> None:
144
183
  pts = [(10, 50), (30, 30), (50, 20)]
145
184
  out = _build_area_polygon_points(pts, baseline_y=100)
@@ -285,9 +324,7 @@ def test_build_chart_svg_empty_with_message_renders_overlay(
285
324
  sample_viewport: Viewport,
286
325
  ) -> None:
287
326
  """Zero-data path with an empty_message renders a centered overlay label."""
288
- result = build_chart_svg(
289
- [], sample_viewport, structural={}, empty_message="NEW REPO · NO STARS YET"
290
- )
327
+ result = build_chart_svg([], sample_viewport, structural={}, empty_message="NEW REPO · NO STARS YET")
291
328
  assert "NEW REPO" in result["empty_state"]
292
329
  assert 'data-hw-zone="empty-state"' in result["empty_state"]
293
330
 
@@ -118,10 +118,7 @@ def test_chart_six_stars_renders_real_polyline_with_derived_labels() -> None:
118
118
  timestamps yield distinct x-coordinates, and the derived Y ticks show
119
119
  [0, 2, 4, 6] — never the hardcoded "3K".
120
120
  """
121
- points = [
122
- {"date": f"2025-{m:02d}-01T00:00:00Z", "count": i + 1}
123
- for i, m in enumerate((1, 3, 5, 7, 9, 11))
124
- ]
121
+ points = [{"date": f"2025-{m:02d}-01T00:00:00Z", "count": i + 1} for i, m in enumerate((1, 3, 5, 7, 9, 11))]
125
122
  spec = ComposeSpec(
126
123
  type="chart",
127
124
  genome_id="brutalist-emerald",
@@ -167,15 +164,10 @@ async def test_fetch_stargazer_history_single_page_uses_per_stargazer_timestamps
167
164
  if url.endswith("/repos/jiahongc/cc-companion"):
168
165
  return {"stargazers_count": 6}
169
166
  if "page=1" in url:
170
- return [
171
- {"starred_at": f"2025-0{m}-01T00:00:00Z"}
172
- for m in range(1, 7)
173
- ]
167
+ return [{"starred_at": f"2025-0{m}-01T00:00:00Z"} for m in range(1, 7)]
174
168
  return {}
175
169
 
176
- with patch(
177
- "hyperweave.connectors.github.fetch_json", side_effect=fake_fetch_json
178
- ):
170
+ with patch("hyperweave.connectors.github.fetch_json", side_effect=fake_fetch_json):
179
171
  result = await fetch_stargazer_history("jiahongc", "cc-companion")
180
172
 
181
173
  assert result["current_stars"] == 6
@@ -53,24 +53,18 @@ async def test_badge_route_infers_passing_state(client: AsyncClient) -> None:
53
53
 
54
54
  async def test_badge_route_explicit_state_wins(client: AsyncClient) -> None:
55
55
  """?state=passing must override the "failing" value inference."""
56
- resp = await client.get(
57
- "/v1/badge/build/failing/brutalist-emerald.static?state=passing"
58
- )
56
+ resp = await client.get("/v1/badge/build/failing/brutalist-emerald.static?state=passing")
59
57
  assert resp.status_code == 200
60
58
  assert _root_status(resp.text) == "passing"
61
59
 
62
60
 
63
61
  async def test_badge_route_percentage_ladder(client: AsyncClient) -> None:
64
62
  """Coverage 95%% should infer passing; 50%% should infer critical."""
65
- resp_high = await client.get(
66
- "/v1/badge/coverage/95%25/brutalist-emerald.static"
67
- )
63
+ resp_high = await client.get("/v1/badge/coverage/95%25/brutalist-emerald.static")
68
64
  assert resp_high.status_code == 200
69
65
  assert _root_status(resp_high.text) == "passing"
70
66
 
71
- resp_low = await client.get(
72
- "/v1/badge/coverage/50%25/brutalist-emerald.static"
73
- )
67
+ resp_low = await client.get("/v1/badge/coverage/50%25/brutalist-emerald.static")
74
68
  assert resp_low.status_code == 200
75
69
  assert _root_status(resp_low.text) == "critical"
76
70
 
@@ -171,6 +171,15 @@ wheels = [
171
171
  { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
172
172
  ]
173
173
 
174
+ [[package]]
175
+ name = "cfgv"
176
+ version = "3.5.0"
177
+ source = { registry = "https://pypi.org/simple" }
178
+ sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" }
179
+ wheels = [
180
+ { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" },
181
+ ]
182
+
174
183
  [[package]]
175
184
  name = "click"
176
185
  version = "8.3.1"
@@ -344,6 +353,15 @@ wheels = [
344
353
  { url = "https://files.pythonhosted.org/packages/06/06/d68a5d5d292c2ad2bc6a02e5ca2cb1bb9c15e941ab02f004a06a342d7f0f/cyclopts-4.10.0-py3-none-any.whl", hash = "sha256:50f333382a60df8d40ec14aa2e627316b361c4f478598ada1f4169d959bf9ea7", size = 204097, upload-time = "2026-03-14T14:09:32.504Z" },
345
354
  ]
346
355
 
356
+ [[package]]
357
+ name = "distlib"
358
+ version = "0.4.0"
359
+ source = { registry = "https://pypi.org/simple" }
360
+ sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
361
+ wheels = [
362
+ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
363
+ ]
364
+
347
365
  [[package]]
348
366
  name = "dnspython"
349
367
  version = "2.8.0"
@@ -453,6 +471,15 @@ wheels = [
453
471
  { url = "https://files.pythonhosted.org/packages/70/ea/570122de7e24f72138d006f799768e14cc1ccf7fcb22b7750b2bd276c711/fastmcp-3.1.1-py3-none-any.whl", hash = "sha256:8132ba069d89f14566b3266919d6d72e2ec23dd45d8944622dca407e9beda7eb", size = 633754, upload-time = "2026-03-14T19:12:22.736Z" },
454
472
  ]
455
473
 
474
+ [[package]]
475
+ name = "filelock"
476
+ version = "3.25.2"
477
+ source = { registry = "https://pypi.org/simple" }
478
+ sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" }
479
+ wheels = [
480
+ { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" },
481
+ ]
482
+
456
483
  [[package]]
457
484
  name = "h11"
458
485
  version = "0.16.0"
@@ -546,6 +573,7 @@ dependencies = [
546
573
  [package.dev-dependencies]
547
574
  dev = [
548
575
  { name = "mypy" },
576
+ { name = "pre-commit" },
549
577
  { name = "pytest" },
550
578
  { name = "pytest-asyncio" },
551
579
  { name = "pytest-cov" },
@@ -574,6 +602,7 @@ requires-dist = [
574
602
  [package.metadata.requires-dev]
575
603
  dev = [
576
604
  { name = "mypy", specifier = ">=1.14" },
605
+ { name = "pre-commit", specifier = ">=4.0" },
577
606
  { name = "pytest", specifier = ">=8.0" },
578
607
  { name = "pytest-asyncio", specifier = ">=0.24" },
579
608
  { name = "pytest-cov", specifier = ">=6.0" },
@@ -586,6 +615,15 @@ dev = [
586
615
  { name = "types-pyyaml", specifier = ">=6.0.12.20250915" },
587
616
  ]
588
617
 
618
+ [[package]]
619
+ name = "identify"
620
+ version = "2.6.18"
621
+ source = { registry = "https://pypi.org/simple" }
622
+ sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" }
623
+ wheels = [
624
+ { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" },
625
+ ]
626
+
589
627
  [[package]]
590
628
  name = "idna"
591
629
  version = "3.11"
@@ -957,6 +995,15 @@ wheels = [
957
995
  { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
958
996
  ]
959
997
 
998
+ [[package]]
999
+ name = "nodeenv"
1000
+ version = "1.10.0"
1001
+ source = { registry = "https://pypi.org/simple" }
1002
+ sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" }
1003
+ wheels = [
1004
+ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" },
1005
+ ]
1006
+
960
1007
  [[package]]
961
1008
  name = "openapi-pydantic"
962
1009
  version = "0.5.1"
@@ -1027,6 +1074,22 @@ wheels = [
1027
1074
  { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
1028
1075
  ]
1029
1076
 
1077
+ [[package]]
1078
+ name = "pre-commit"
1079
+ version = "4.5.1"
1080
+ source = { registry = "https://pypi.org/simple" }
1081
+ dependencies = [
1082
+ { name = "cfgv" },
1083
+ { name = "identify" },
1084
+ { name = "nodeenv" },
1085
+ { name = "pyyaml" },
1086
+ { name = "virtualenv" },
1087
+ ]
1088
+ sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" }
1089
+ wheels = [
1090
+ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" },
1091
+ ]
1092
+
1030
1093
  [[package]]
1031
1094
  name = "py-key-value-aio"
1032
1095
  version = "0.4.4"
@@ -1304,6 +1367,19 @@ wheels = [
1304
1367
  { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" },
1305
1368
  ]
1306
1369
 
1370
+ [[package]]
1371
+ name = "python-discovery"
1372
+ version = "1.2.2"
1373
+ source = { registry = "https://pypi.org/simple" }
1374
+ dependencies = [
1375
+ { name = "filelock" },
1376
+ { name = "platformdirs" },
1377
+ ]
1378
+ sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" }
1379
+ wheels = [
1380
+ { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" },
1381
+ ]
1382
+
1307
1383
  [[package]]
1308
1384
  name = "python-dotenv"
1309
1385
  version = "1.2.2"
@@ -1706,6 +1782,21 @@ wheels = [
1706
1782
  { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" },
1707
1783
  ]
1708
1784
 
1785
+ [[package]]
1786
+ name = "virtualenv"
1787
+ version = "21.2.1"
1788
+ source = { registry = "https://pypi.org/simple" }
1789
+ dependencies = [
1790
+ { name = "distlib" },
1791
+ { name = "filelock" },
1792
+ { name = "platformdirs" },
1793
+ { name = "python-discovery" },
1794
+ ]
1795
+ sdist = { url = "https://files.pythonhosted.org/packages/97/c5/aff062c66b42e2183201a7ace10c6b2e959a9a16525c8e8ca8e59410d27a/virtualenv-21.2.1.tar.gz", hash = "sha256:b66ffe81301766c0d5e2208fc3576652c59d44e7b731fc5f5ed701c9b537fa78", size = 5844770, upload-time = "2026-04-09T18:47:11.482Z" }
1796
+ wheels = [
1797
+ { url = "https://files.pythonhosted.org/packages/20/0e/f083a76cb590e60dff3868779558eefefb8dfb7c9ed020babc7aa014ccbf/virtualenv-21.2.1-py3-none-any.whl", hash = "sha256:bd16b49c53562b28cf1a3ad2f36edb805ad71301dee70ddc449e5c88a9f919a2", size = 5828326, upload-time = "2026-04-09T18:47:09.331Z" },
1798
+ ]
1799
+
1709
1800
  [[package]]
1710
1801
  name = "watchfiles"
1711
1802
  version = "1.1.1"
File without changes
File without changes
File without changes
File without changes