hyperweave 0.2.7__tar.gz → 0.2.9__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 (360) hide show
  1. {hyperweave-0.2.7 → hyperweave-0.2.9}/CHANGELOG.md +30 -0
  2. {hyperweave-0.2.7 → hyperweave-0.2.9}/PKG-INFO +1 -1
  3. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/_version.py +2 -2
  4. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/context.py +9 -5
  5. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/connectors/base.py +63 -0
  6. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/connectors/github.py +210 -24
  7. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/render/chart_engine.py +198 -124
  8. hyperweave-0.2.9/src/hyperweave/templates/components/chart-area.svg.j2 +11 -0
  9. hyperweave-0.2.9/src/hyperweave/templates/components/chart-axes.svg.j2 +2 -0
  10. hyperweave-0.2.9/src/hyperweave/templates/components/chart-empty-state.svg.j2 +8 -0
  11. hyperweave-0.2.9/src/hyperweave/templates/components/chart-gridlines.svg.j2 +2 -0
  12. hyperweave-0.2.9/src/hyperweave/templates/components/chart-milestone.svg.j2 +7 -0
  13. hyperweave-0.2.9/src/hyperweave/templates/components/chart-polyline.svg.j2 +10 -0
  14. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/chart/brutalist-content.j2 +22 -8
  15. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/chart/chrome-content.j2 +21 -7
  16. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_chart_engine.py +184 -42
  17. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_connectors.py +467 -0
  18. {hyperweave-0.2.7 → hyperweave-0.2.9}/.dockerignore +0 -0
  19. {hyperweave-0.2.7 → hyperweave-0.2.9}/.github/workflows/ci.yml +0 -0
  20. {hyperweave-0.2.7 → hyperweave-0.2.9}/.github/workflows/deploy.yml +0 -0
  21. {hyperweave-0.2.7 → hyperweave-0.2.9}/.github/workflows/publish.yml +0 -0
  22. {hyperweave-0.2.7 → hyperweave-0.2.9}/.gitignore +0 -0
  23. {hyperweave-0.2.7 → hyperweave-0.2.9}/.pre-commit-config.yaml +0 -0
  24. {hyperweave-0.2.7 → hyperweave-0.2.9}/Dockerfile +0 -0
  25. {hyperweave-0.2.7 → hyperweave-0.2.9}/LICENSE +0 -0
  26. {hyperweave-0.2.7 → hyperweave-0.2.9}/README.md +0 -0
  27. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/buttons/button-liquid.svg +0 -0
  28. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/cards/card-butterfly.svg +0 -0
  29. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/cards/card-python.svg +0 -0
  30. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/cards/card-sunflower.svg +0 -0
  31. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/cards/card-waves.svg +0 -0
  32. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/badge_critical.svg +0 -0
  33. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/badge_passing.svg +0 -0
  34. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/badge_warning.svg +0 -0
  35. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/banner.svg +0 -0
  36. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/icons/discord.svg +0 -0
  37. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/icons/notion.svg +0 -0
  38. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/icons/reddit.svg +0 -0
  39. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/icons/spotify.svg +0 -0
  40. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/icons/youtube.svg +0 -0
  41. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/marquee_counter.svg +0 -0
  42. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/marquee_horizontal.svg +0 -0
  43. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/marquee_vertical.svg +0 -0
  44. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/profile-cards/chart_stars_full.svg +0 -0
  45. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/profile-cards/stats.svg +0 -0
  46. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/brutalist-emerald/strip.svg +0 -0
  47. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/badge_critical.svg +0 -0
  48. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/badge_passing.svg +0 -0
  49. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/badge_warning.svg +0 -0
  50. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/banner.svg +0 -0
  51. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/icons/bluesky.svg +0 -0
  52. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/icons/github.svg +0 -0
  53. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/icons/instagram.svg +0 -0
  54. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/icons/mastodon.svg +0 -0
  55. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/icons/x.svg +0 -0
  56. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/marquee_counter.svg +0 -0
  57. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/marquee_horizontal.svg +0 -0
  58. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/marquee_vertical.svg +0 -0
  59. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/profile-cards/chart_stars_full.svg +0 -0
  60. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/profile-cards/stats.svg +0 -0
  61. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/chrome-horizon/strip.svg +0 -0
  62. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/telemetry/master_card.svg +0 -0
  63. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/telemetry/receipt.svg +0 -0
  64. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/examples/telemetry/rhythm_strip.svg +0 -0
  65. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/footers/inneraura-footer-liquid.svg +0 -0
  66. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/footers/inneraura-footer-purple.svg +0 -0
  67. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/footers/inneraura-footer.svg +0 -0
  68. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/hyperweave-banner.svg +0 -0
  69. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-discord.svg +0 -0
  70. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-docs.svg +0 -0
  71. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-github.svg +0 -0
  72. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-instagram.svg +0 -0
  73. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-linkedin.svg +0 -0
  74. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-tiktok.svg +0 -0
  75. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-x.svg +0 -0
  76. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/icons/cobalt-sapphire-youtube.svg +0 -0
  77. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/marquees/badge-showcase-triple.svg +0 -0
  78. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/marquees/genome-marquee-triple.svg +0 -0
  79. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/marquees/sample-badges.svg +0 -0
  80. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/404.svg +0 -0
  81. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/callout-icons.svg +0 -0
  82. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/divider.svg +0 -0
  83. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/favicon.svg +0 -0
  84. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/hero-banner.svg +0 -0
  85. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/hyperweave-banner-v2.svg +0 -0
  86. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/hyperweave-navbar-logo.svg +0 -0
  87. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/loader.svg +0 -0
  88. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/mintlify-assets/og-image.svg +0 -0
  89. {hyperweave-0.2.7 → hyperweave-0.2.9}/assets/timelines/hyperweave-roadmap.svg +0 -0
  90. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/assets/favicon.svg +0 -0
  91. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/assets/hyperweave-banner.svg +0 -0
  92. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/assets/hyperweave-navbar-logo.svg +0 -0
  93. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/assets/og-image.svg +0 -0
  94. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/explanation/architecture.mdx +0 -0
  95. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/explanation/camo-compatibility.mdx +0 -0
  96. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/explanation/cim-compliance.mdx +0 -0
  97. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/explanation/genome-profile-system.mdx +0 -0
  98. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/explanation/metadata-tiers.mdx +0 -0
  99. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/how-to/add-motion-to-badges.mdx +0 -0
  100. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/how-to/create-session-receipts.mdx +0 -0
  101. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/how-to/set-up-claude-code-hooks.mdx +0 -0
  102. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/how-to/use-live-data-badges.mdx +0 -0
  103. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/introduction.mdx +0 -0
  104. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/mint.json +0 -0
  105. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/quickstart.mdx +0 -0
  106. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/reference/cli.mdx +0 -0
  107. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/reference/compose-spec.mdx +0 -0
  108. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/reference/genomes.mdx +0 -0
  109. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/reference/http-api.mdx +0 -0
  110. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/reference/mcp-tools.mdx +0 -0
  111. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/reference/motions.mdx +0 -0
  112. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/reference/telemetry-contract.mdx +0 -0
  113. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/spec/hyperweave-protocol.mdx +0 -0
  114. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/tutorials/build-an-artifact-kit.mdx +0 -0
  115. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/tutorials/compose-your-first-badge.mdx +0 -0
  116. {hyperweave-0.2.7 → hyperweave-0.2.9}/docs/tutorials/install-session-telemetry.mdx +0 -0
  117. {hyperweave-0.2.7 → hyperweave-0.2.9}/fly.toml +0 -0
  118. {hyperweave-0.2.7 → hyperweave-0.2.9}/hooks/install.py +0 -0
  119. {hyperweave-0.2.7 → hyperweave-0.2.9}/hooks/session_end.sh +0 -0
  120. {hyperweave-0.2.7 → hyperweave-0.2.9}/justfile +0 -0
  121. {hyperweave-0.2.7 → hyperweave-0.2.9}/pyproject.toml +0 -0
  122. {hyperweave-0.2.7 → hyperweave-0.2.9}/scripts/extract_font_metrics.py +0 -0
  123. {hyperweave-0.2.7 → hyperweave-0.2.9}/scripts/extract_glyphs.py +0 -0
  124. {hyperweave-0.2.7 → hyperweave-0.2.9}/scripts/generate_hw_compliant.py +0 -0
  125. {hyperweave-0.2.7 → hyperweave-0.2.9}/scripts/generate_proofset.py +0 -0
  126. {hyperweave-0.2.7 → hyperweave-0.2.9}/scripts/stress_test.py +0 -0
  127. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/__init__.py +0 -0
  128. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/__main__.py +0 -0
  129. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/cli.py +0 -0
  130. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/__init__.py +0 -0
  131. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/assembler.py +0 -0
  132. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/engine.py +0 -0
  133. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/lanes.py +0 -0
  134. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/resolver.py +0 -0
  135. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/resolvers/__init__.py +0 -0
  136. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/resolvers/chart.py +0 -0
  137. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/resolvers/stats.py +0 -0
  138. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/resolvers/timeline.py +0 -0
  139. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/rhythm.py +0 -0
  140. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/compose/validate_paradigms.py +0 -0
  141. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/config/__init__.py +0 -0
  142. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/config/genome_validator.py +0 -0
  143. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/config/loader.py +0 -0
  144. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/config/registry.py +0 -0
  145. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/config/settings.py +0 -0
  146. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/connectors/__init__.py +0 -0
  147. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/connectors/arxiv.py +0 -0
  148. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/connectors/cache.py +0 -0
  149. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/connectors/rest.py +0 -0
  150. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/__init__.py +0 -0
  151. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/color.py +0 -0
  152. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/contracts.py +0 -0
  153. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/enums.py +0 -0
  154. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/font_metrics.py +0 -0
  155. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/models.py +0 -0
  156. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/paradigm.py +0 -0
  157. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/schema.py +0 -0
  158. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/state.py +0 -0
  159. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/text.py +0 -0
  160. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/core/thresholds.py +0 -0
  161. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/css/accessibility.css +0 -0
  162. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/css/bridge.css +0 -0
  163. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/css/expression.css +0 -0
  164. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/css/phase-colors.css +0 -0
  165. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/css/status.css +0 -0
  166. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/css/telemetry.css +0 -0
  167. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/font-metrics/inter.json +0 -0
  168. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/font-metrics/jetbrains-mono.json +0 -0
  169. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/font-metrics/orbitron.json +0 -0
  170. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/fonts/jetbrains-mono.b64 +0 -0
  171. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/fonts/jetbrains-mono.meta.json +0 -0
  172. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/fonts/orbitron.b64 +0 -0
  173. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/fonts/orbitron.meta.json +0 -0
  174. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/genomes/brutalist-emerald.json +0 -0
  175. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/genomes/chrome-horizon.json +0 -0
  176. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/genomes/telemetry-void.json +0 -0
  177. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/glyphs.json +0 -0
  178. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/border/chromatic-pulse.yaml +0 -0
  179. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/border/corner-trace.yaml +0 -0
  180. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/border/dual-orbit.yaml +0 -0
  181. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/border/entanglement.yaml +0 -0
  182. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/border/rimrun.yaml +0 -0
  183. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/bars.yaml +0 -0
  184. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/breach.yaml +0 -0
  185. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/broadcast.yaml +0 -0
  186. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/cascade.yaml +0 -0
  187. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/collapse.yaml +0 -0
  188. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/converge.yaml +0 -0
  189. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/crash.yaml +0 -0
  190. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/drop.yaml +0 -0
  191. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/kinetic/pulse.yaml +0 -0
  192. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/motions/static.yaml +0 -0
  193. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/paradigms/brutalist.yaml +0 -0
  194. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/paradigms/chrome.yaml +0 -0
  195. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/paradigms/default.yaml +0 -0
  196. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/policies/normal.json +0 -0
  197. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/policies/permissive.json +0 -0
  198. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/policies/ungoverned.json +0 -0
  199. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/profiles/brutalist.contract.json +0 -0
  200. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/profiles/brutalist.yaml +0 -0
  201. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/profiles/chrome.contract.json +0 -0
  202. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/profiles/chrome.yaml +0 -0
  203. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/specimens.yaml +0 -0
  204. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/telemetry/model-pricing.yaml +0 -0
  205. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/telemetry/stage-config.yaml +0 -0
  206. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/telemetry/stage-labels.yaml +0 -0
  207. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/telemetry/tool-classes.yaml +0 -0
  208. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/telemetry/tool-colors.yaml +0 -0
  209. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/arrow.json +0 -0
  210. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/aurora.json +0 -0
  211. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/beacon.json +0 -0
  212. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/bracket3d.json +0 -0
  213. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/chevron.json +0 -0
  214. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/crosshair.json +0 -0
  215. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/diamond.json +0 -0
  216. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/rocket.json +0 -0
  217. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/rules/bar.json +0 -0
  218. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/rules/dashed.json +0 -0
  219. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/rules/five-wave.json +0 -0
  220. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/rules/spectral.json +0 -0
  221. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/rules/straight.json +0 -0
  222. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/rules/wave.json +0 -0
  223. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/data/terminals/stijl.json +0 -0
  224. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/kit.py +0 -0
  225. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/mcp/__init__.py +0 -0
  226. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/mcp/__main__.py +0 -0
  227. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/mcp/server.py +0 -0
  228. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/py.typed +0 -0
  229. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/render/__init__.py +0 -0
  230. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/render/fonts.py +0 -0
  231. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/render/glyphs.py +0 -0
  232. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/render/motion.py +0 -0
  233. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/render/templates.py +0 -0
  234. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/serve/__init__.py +0 -0
  235. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/serve/app.py +0 -0
  236. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/__init__.py +0 -0
  237. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/capture.py +0 -0
  238. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/contract.py +0 -0
  239. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/corrections.py +0 -0
  240. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/cost.py +0 -0
  241. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/models.py +0 -0
  242. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/parser.py +0 -0
  243. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/telemetry/stages.py +0 -0
  244. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/chart-markers/circle.svg.j2 +0 -0
  245. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/chart-markers/diamond.svg.j2 +0 -0
  246. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/chart-markers/endpoint-diamond.svg.j2 +0 -0
  247. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/chart-markers/endpoint-rect.svg.j2 +0 -0
  248. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/chart-markers/rect.svg.j2 +0 -0
  249. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/filter.svg.j2 +0 -0
  250. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/glyph-inline.svg.j2 +0 -0
  251. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/glyph.svg.j2 +0 -0
  252. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/gradient.svg.j2 +0 -0
  253. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/metadata.svg.j2 +0 -0
  254. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/metric.svg.j2 +0 -0
  255. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/phase-segment.svg.j2 +0 -0
  256. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/rule.svg.j2 +0 -0
  257. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/status.svg.j2 +0 -0
  258. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/terminal.svg.j2 +0 -0
  259. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/components/treemap.svg.j2 +0 -0
  260. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/document.svg.j2 +0 -0
  261. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/error-badge.svg.j2 +0 -0
  262. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/badge/brutalist-content.j2 +0 -0
  263. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/badge/brutalist-defs.j2 +0 -0
  264. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/badge/chrome-content.j2 +0 -0
  265. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/badge/chrome-defs.j2 +0 -0
  266. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/badge/default-content.j2 +0 -0
  267. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/badge/default-defs.j2 +0 -0
  268. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/badge.svg.j2 +0 -0
  269. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/banner/brutalist-defs.j2 +0 -0
  270. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/banner/chrome-content.j2 +0 -0
  271. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/banner/chrome-defs.j2 +0 -0
  272. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/banner/default-content.j2 +0 -0
  273. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/banner/default-defs.j2 +0 -0
  274. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/banner.svg.j2 +0 -0
  275. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/catalog.svg.j2 +0 -0
  276. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/chart/brutalist-defs.j2 +0 -0
  277. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/chart/chrome-defs.j2 +0 -0
  278. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/chart.svg.j2 +0 -0
  279. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/divider.svg.j2 +0 -0
  280. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/icon/brutalist-content.j2 +0 -0
  281. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/icon/brutalist-defs.j2 +0 -0
  282. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/icon/chrome-content.j2 +0 -0
  283. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/icon/chrome-defs.j2 +0 -0
  284. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/icon/default-content.j2 +0 -0
  285. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/icon/default-defs.j2 +0 -0
  286. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/icon.svg.j2 +0 -0
  287. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-counter/brutalist-content.j2 +0 -0
  288. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-counter/brutalist-defs.j2 +0 -0
  289. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-counter/chrome-content.j2 +0 -0
  290. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-counter/chrome-defs.j2 +0 -0
  291. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-counter.svg.j2 +0 -0
  292. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-content.j2 +0 -0
  293. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-horizontal/brutalist-defs.j2 +0 -0
  294. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-horizontal/chrome-content.j2 +0 -0
  295. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-horizontal/chrome-defs.j2 +0 -0
  296. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-horizontal.svg.j2 +0 -0
  297. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-vertical/brutalist-content.j2 +0 -0
  298. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-vertical/brutalist-defs.j2 +0 -0
  299. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-vertical/chrome-content.j2 +0 -0
  300. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-vertical/chrome-defs.j2 +0 -0
  301. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/marquee-vertical.svg.j2 +0 -0
  302. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/master-card.svg.j2 +0 -0
  303. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/receipt.svg.j2 +0 -0
  304. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/rhythm-strip.svg.j2 +0 -0
  305. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/stats/brutalist-content.j2 +0 -0
  306. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/stats/brutalist-defs.j2 +0 -0
  307. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/stats/chrome-content.j2 +0 -0
  308. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/stats/chrome-defs.j2 +0 -0
  309. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/stats.svg.j2 +0 -0
  310. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/brutalist-content.j2 +0 -0
  311. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/brutalist-defs.j2 +0 -0
  312. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/brutalist-status.j2 +0 -0
  313. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/chrome-content.j2 +0 -0
  314. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/chrome-defs.j2 +0 -0
  315. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/chrome-status.j2 +0 -0
  316. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/default-content.j2 +0 -0
  317. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/default-defs.j2 +0 -0
  318. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip/default-status.j2 +0 -0
  319. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/strip.svg.j2 +0 -0
  320. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/timeline/default-content.j2 +0 -0
  321. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/frames/timeline.svg.j2 +0 -0
  322. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/border/chromatic-pulse.svg.j2 +0 -0
  323. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/border/corner-trace.svg.j2 +0 -0
  324. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/border/dual-orbit.svg.j2 +0 -0
  325. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/border/entanglement.svg.j2 +0 -0
  326. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/border/rimrun.svg.j2 +0 -0
  327. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/bars.svg.j2 +0 -0
  328. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/breach.svg.j2 +0 -0
  329. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/broadcast.svg.j2 +0 -0
  330. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/cascade.svg.j2 +0 -0
  331. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/collapse.svg.j2 +0 -0
  332. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/converge.svg.j2 +0 -0
  333. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/crash.svg.j2 +0 -0
  334. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/drop.svg.j2 +0 -0
  335. {hyperweave-0.2.7 → hyperweave-0.2.9}/src/hyperweave/templates/motions/kinetic/pulse.svg.j2 +0 -0
  336. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/__init__.py +0 -0
  337. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/conftest.py +0 -0
  338. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/fixtures/github_contributions/synthetic.html +0 -0
  339. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/fixtures/session.json +0 -0
  340. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/fixtures/session.jsonl +0 -0
  341. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/helpers.py +0 -0
  342. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_assembler.py +0 -0
  343. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_chart_frame.py +0 -0
  344. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_core.py +0 -0
  345. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_github_scrape.py +0 -0
  346. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_kit.py +0 -0
  347. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_mcp.py +0 -0
  348. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_paradigm_dispatch.py +0 -0
  349. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_paradigm_extensibility.py +0 -0
  350. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_proofset.py +0 -0
  351. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_render.py +0 -0
  352. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_resolver_rhythm.py +0 -0
  353. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_serve.py +0 -0
  354. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_serve_live_state.py +0 -0
  355. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_state_inference.py +0 -0
  356. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_stats_card.py +0 -0
  357. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_telemetry.py +0 -0
  358. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_telemetry_integration.py +0 -0
  359. {hyperweave-0.2.7 → hyperweave-0.2.9}/tests/test_timeline.py +0 -0
  360. {hyperweave-0.2.7 → hyperweave-0.2.9}/uv.lock +0 -0
@@ -5,6 +5,36 @@ 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.9] - 2026-04-21
9
+
10
+ ### Fixed
11
+
12
+ - **Star history charts now render an authentic growth curve for any repo size.** v0.2.8 fetched only the most-recent 2,000 stargazers for any repo with more than ~2,000 stars, which meant both the 361,000-star `openclaw/openclaw` and the 40,000-star `JuliusBrussee/caveman` rendered as flat horizontal lines near their total-star value — the captured window's variation was invisible on the full-range Y-axis. Star history now uses GraphQL cursor-offset sampling to pull 12 real stargazer timestamps evenly distributed across the repository's full lifetime, regardless of whether the repo has 30 stars or 500,000. The resulting curve shows actual adoption shape — slow-early, explosive-middle, tapering-late — rather than either a hockey-stick or a flat line.
13
+ - **X-axis date labels adapt to the repository's lifetime.** A viral repo that went from 0 to 40K stars in two weeks was being labeled with a single "2026" at the center of its axis; a mature repo with 10 years of history showed the same year-only format. The axis now selects granularity from the actual temporal span — daily labels ("Apr 05", "Apr 12") when the span is under two weeks, weekly under 90 days, month-plus-year under two years, yearly up to ten years, and every-other-year beyond. Labels maintain a minimum 48-pixel horizontal gap so they never collide — the "202324" pileup is gone.
14
+
15
+ ### Changed
16
+
17
+ - **Parallel GraphQL dispatch is now bounded.** Cold fetches fan out twelve cursor-offset queries across four concurrent in-flight requests via `asyncio.Semaphore`. This keeps cold-fetch latency under ~1 second while staying well below per-minute abuse-detection thresholds that bursty same-token parallelism could otherwise trip.
18
+
19
+ ## [0.2.8] - 2026-04-20
20
+
21
+ ### Fixed
22
+
23
+ - **Star history charts now render readably on large repositories.** Previously, repos with more than ~40,000 stars produced a flat-then-vertical "hockey stick" because GitHub's stargazer REST endpoint hard-caps deep pagination, so the only data reachable was a small cluster of early-history stars followed by a single jump to today. Star history now sources from GitHub's GraphQL API and adapts its sampling window to the repo: small repos continue to get full lifetime views, large repos get a detailed recent-growth curve similar to the one star-history.com shows.
24
+ - **Star-count milestone labels no longer stack on top of each other.** When sampled points clustered temporally, the milestone labels (`500`, `1K`, `5K`, `10K`, ...) would render in a single illegible pile on top of the crossing point. Milestones now maintain a minimum 40-pixel horizontal gap; any labels that would overlap with an already-placed one are dropped in favor of the first (lowest-threshold) milestone in the cluster.
25
+
26
+ ### Changed
27
+
28
+ - **All chart rendering now flows through template partials.** The chart engine's remaining rendering paths — axes, gridlines, polyline, area fill, milestones, and the empty-state overlay — joined the marker layer in Jinja. The engine module returns structured Python data; Jinja partials under `templates/components/` produce the final SVG. No visual change; output is byte-identical for the shipped genomes on unchanged inputs.
29
+
30
+ ### Added
31
+
32
+ - **`fetch_graphql` connector primitive.** New async helper in the connector layer that handles POST with JSON bodies, GitHub bearer-token rotation, circuit-breaker coordination, and SSRF validation — mirroring the existing `fetch_json` contract. This unlocks future migration of the stats card's six REST sub-fetches to a single GraphQL call, and keeps a single canonical place for authenticated-POST plumbing.
33
+
34
+ ### Dev
35
+
36
+ - GraphQL stargazer pipeline is covered by unit tests for the primary path, REST fallback on failure, no-token skip, mega-repo window cap, small-repo history exhaustion, downsampling correctness, and current-UTC now-point stamping.
37
+
8
38
  ## [0.2.7] - 2026-04-20
9
39
 
10
40
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperweave
3
- Version: 0.2.7
3
+ Version: 0.2.9
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
@@ -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.7'
22
- __version_tuple__ = version_tuple = (0, 2, 7)
21
+ __version__ = version = '0.2.9'
22
+ __version_tuple__ = version_tuple = (0, 2, 9)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -337,12 +337,16 @@ def _ctx_chart(spec: ComposeSpec, resolved: ResolvedArtifact, css: dict[str, str
337
337
  ctx["chart_viewport_w"] = 0
338
338
  ctx["chart_viewport_h"] = 0
339
339
  ctx["chart_defs"] = ""
340
- ctx["chart_axes"] = ""
341
- ctx["chart_gridlines"] = ""
342
- ctx["chart_area"] = ""
343
- ctx["chart_polyline"] = ""
340
+ # Post-v0.2.8: axes / gridlines / milestones / markers all return structured
341
+ # lists; polyline / area / empty_state are dicts or None so templates can
342
+ # use ``{% if %}`` to guard includes without a StrictUndefined trap.
343
+ ctx["chart_axes"] = []
344
+ ctx["chart_gridlines"] = []
345
+ ctx["chart_area"] = None
346
+ ctx["chart_polyline"] = None
344
347
  ctx["chart_markers"] = []
345
- ctx["chart_milestones"] = ""
348
+ ctx["chart_milestones"] = []
349
+ ctx["chart_empty_state"] = None
346
350
  ctx["data_hw_status"] = "fresh"
347
351
  ctx.update(resolved.frame_context)
348
352
  return ctx
@@ -216,3 +216,66 @@ async def fetch_text(
216
216
  merged.update(headers)
217
217
  response = await fetch(url, provider=provider, headers=merged)
218
218
  return response.text
219
+
220
+
221
+ # GraphQL POST
222
+
223
+ _GITHUB_GRAPHQL_URL: str = "https://api.github.com/graphql"
224
+
225
+
226
+ async def fetch_graphql(
227
+ query: str,
228
+ variables: dict[str, Any] | None = None,
229
+ *,
230
+ provider: str = "github",
231
+ url: str = _GITHUB_GRAPHQL_URL,
232
+ ) -> dict[str, Any]:
233
+ """POST a GraphQL query and return the parsed JSON response.
234
+
235
+ Mirrors :func:`fetch_json`'s contract (SSRF validation, per-provider
236
+ circuit breaker, timeouts, GitHub Bearer-token injection) but speaks
237
+ POST with a JSON body. The single GraphQL endpoint for GitHub is
238
+ baked into the default URL so callers don't have to remember it.
239
+
240
+ GraphQL always returns HTTP 200 with a ``data`` field and an optional
241
+ ``errors`` list — HTTP-level failures trip the breaker, but query-level
242
+ errors (invalid field, missing perms) come through as response body
243
+ and are the caller's responsibility to inspect.
244
+ """
245
+ validate_url(url)
246
+
247
+ breaker = get_breaker(provider)
248
+ if not breaker.allow_request():
249
+ raise CircuitOpenError(
250
+ f"Circuit breaker open for provider {provider!r}. Retry after {breaker.recovery_timeout}s."
251
+ )
252
+
253
+ merged_headers: dict[str, str] = {
254
+ "User-Agent": f"HyperWeave/{__version__} (https://hyperweave.app)",
255
+ "Accept": "application/json",
256
+ "Content-Type": "application/json",
257
+ }
258
+ if provider == "github":
259
+ token = _get_github_token()
260
+ if token:
261
+ merged_headers["Authorization"] = f"Bearer {token}"
262
+
263
+ timeout = httpx.Timeout(
264
+ connect=CONNECT_TIMEOUT,
265
+ read=TOTAL_TIMEOUT,
266
+ write=TOTAL_TIMEOUT,
267
+ pool=TOTAL_TIMEOUT,
268
+ )
269
+
270
+ body: dict[str, Any] = {"query": query, "variables": variables or {}}
271
+
272
+ try:
273
+ async with httpx.AsyncClient(timeout=timeout) as client:
274
+ response = await client.post(url, headers=merged_headers, json=body)
275
+ response.raise_for_status()
276
+ breaker.record_success()
277
+ data: dict[str, Any] = response.json()
278
+ return data
279
+ except (httpx.HTTPStatusError, httpx.RequestError) as exc:
280
+ breaker.record_failure()
281
+ raise ConnectorError(f"GraphQL fetch failed for {provider!r}: {exc}") from exc
@@ -3,12 +3,20 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
+ import base64
6
7
  import math
7
8
  import re
8
9
  from datetime import UTC, datetime
9
10
  from typing import Any
10
11
 
11
- from hyperweave.connectors.base import fetch_json, fetch_text
12
+ from hyperweave.connectors.base import (
13
+ CircuitOpenError,
14
+ ConnectorError,
15
+ _get_github_token,
16
+ fetch_graphql,
17
+ fetch_json,
18
+ fetch_text,
19
+ )
12
20
  from hyperweave.connectors.cache import get_cache
13
21
 
14
22
  PROVIDER = "github"
@@ -144,6 +152,47 @@ _STARGAZER_PAGE_SIZE = 100
144
152
  _STARGAZER_PAGE_CAP = 400
145
153
  _STARGAZER_ACCEPT_HEADER = "application/vnd.github.v3.star+json"
146
154
 
155
+ # GraphQL cursor-offset sampling constants.
156
+ #
157
+ # GitHub's GraphQL cursors decode to the literal text ``cursor:<N>`` where
158
+ # N is a 0-indexed offset into the stargazer list. By Base64-encoding
159
+ # ``cursor:<N-1>`` we can construct an ``after:`` anchor that lets ``first: 1``
160
+ # return exactly the Nth stargazer. This bypasses GitHub's 400-page REST cap
161
+ # and lets us sample any offset in a 500K-star repo cheaply.
162
+ #
163
+ # 12 offsets * 1 node per call = 12 GraphQL points per cold fetch.
164
+ # ``asyncio.Semaphore(_GRAPHQL_CONCURRENCY)`` caps fan-out so bursty
165
+ # same-token parallelism doesn't trigger GitHub's per-minute abuse detection.
166
+ _DEFAULT_SAMPLE_COUNT = 12
167
+ _GRAPHQL_CONCURRENCY = 4
168
+
169
+ _CURSOR_OFFSET_QUERY = """
170
+ query StargazerAtOffset($owner: String!, $repo: String!, $cursor: String) {
171
+ repository(owner: $owner, name: $repo) {
172
+ stargazerCount
173
+ stargazers(first: 1, after: $cursor) {
174
+ edges { starredAt }
175
+ }
176
+ }
177
+ }
178
+ """
179
+
180
+
181
+ def _cursor_for_offset(offset: int) -> str | None:
182
+ """Return the Base64-encoded cursor for a 1-indexed stargazer position.
183
+
184
+ GitHub's GraphQL cursors decode to ``cursor:<N>`` where N is 0-indexed.
185
+ Constructing them lets us sample any offset within a repo's stargazer
186
+ list, bypassing both the REST 400-page cap and the cost of walking
187
+ every page via cursor pagination.
188
+
189
+ Returns None for ``offset <= 1`` (no cursor needed — this IS the first
190
+ entry; passing ``after: None`` to GraphQL starts from the beginning).
191
+ """
192
+ if offset <= 1:
193
+ return None
194
+ return base64.b64encode(f"cursor:{offset - 1}".encode()).decode()
195
+
147
196
 
148
197
  async def fetch_stargazer_history(
149
198
  owner: str,
@@ -152,19 +201,14 @@ async def fetch_stargazer_history(
152
201
  ) -> dict[str, Any]:
153
202
  """Fetch sampled star history for ``owner/repo`` as cumulative data points.
154
203
 
155
- Strategy:
156
- 1. GET ``/repos/{owner}/{repo}`` total ``stargazers_count``.
157
- 2. Compute total pages (30 per page on the stargazers endpoint).
158
- 3. Sample ``sample_pages`` evenly distributed page numbers across
159
- ``[1, total_pages]`` and fetch them concurrently with the
160
- ``application/vnd.github.v3.star+json`` ``Accept`` header so each
161
- element contains a ``starred_at`` timestamp.
162
- 4. From each sampled page take the FIRST starred_at and use it as the
163
- time marker for the cumulative count at ``(page - 1) * 30``.
164
- 5. Cache under ``github:{owner}/{repo}:stargazer-history``.
165
-
166
- Returns a dict with keys ``points`` (list of ``{date, count}``), ``current_stars``,
167
- ``repo``, ``ttl``. Raises ``ValueError`` on invalid identifier.
204
+ Public dispatcher. Prefers GraphQL when a token is available (unlocks the
205
+ recent-growth window on mega-repos beyond GitHub's 400-page REST cap);
206
+ falls back to REST sampling on GraphQL failure or missing token. Caches
207
+ successful results under ``github:{owner}/{repo}:stargazer-history``.
208
+
209
+ Returns a dict with keys ``points`` (list of ``{date, count}``),
210
+ ``current_stars``, ``repo``, ``ttl``. Raises ``ValueError`` on invalid
211
+ identifier.
168
212
  """
169
213
  if not owner or not repo:
170
214
  raise ValueError("fetch_stargazer_history requires owner and repo")
@@ -176,25 +220,171 @@ async def fetch_stargazer_history(
176
220
  if cached is not None:
177
221
  return cached # type: ignore[no-any-return]
178
222
 
223
+ result: dict[str, Any] | None = None
224
+
225
+ # Primary path: GraphQL cursor-offset sampling (requires auth). Constructs
226
+ # cursors for N evenly-distributed stargazer offsets across [1, total_stars]
227
+ # and fetches one stargazer per offset. Bypasses GitHub's 400-page REST cap,
228
+ # so a 361K-star repo gets 12 real points across its full lifetime instead
229
+ # of a hockey-stick or flat line.
230
+ if _get_github_token():
231
+ try:
232
+ result = await _fetch_stargazer_history_graphql(owner, repo, sample_pages)
233
+ except (ConnectorError, CircuitOpenError):
234
+ result = None # fall through to REST
235
+
236
+ if result is None:
237
+ # Fallback path: REST sampling. Works unauth at 60 req/hr but caps at
238
+ # ~40k stars on mega-repos (GitHub's deep-pagination wall).
239
+ result = await _fetch_stargazer_history_rest(owner, repo, sample_pages)
240
+
241
+ cache.set(cache_key, result, STARGAZER_HISTORY_TTL)
242
+ return result
243
+
244
+
245
+ async def _fetch_stargazer_history_graphql(
246
+ owner: str,
247
+ repo: str,
248
+ sample_count: int = _DEFAULT_SAMPLE_COUNT,
249
+ ) -> dict[str, Any]:
250
+ """Sample N stargazer timestamps at evenly-distributed cursor offsets.
251
+
252
+ Strategy: given a repo's ``total_stars``, fetch one stargazer at each of
253
+ N evenly-spaced offsets across ``[1, total_stars]``. Each offset maps to
254
+ a constructed cursor (``base64("cursor:<N-1>")``); GraphQL returns that
255
+ stargazer's ``starred_at``. Pair with the known offset to form a
256
+ ``{date, count}`` point. Appends a now-point at ``total_stars``.
257
+
258
+ Works for any repo size — 30 stars, 2938 stars, 361k stars — because
259
+ cursor-offset sampling is not subject to GitHub's 400-page REST cap.
260
+
261
+ Concurrency is bounded at ``_GRAPHQL_CONCURRENCY`` to avoid tripping
262
+ per-minute abuse-detection heuristics on bursty parallel fan-out.
263
+ """
264
+ identifier = f"{owner}/{repo}"
265
+
266
+ # First query: learn total_stars and pick up the first stargazer's
267
+ # timestamp in the same round-trip (offset 1 uses no cursor anyway).
268
+ first_response = await fetch_graphql(
269
+ _CURSOR_OFFSET_QUERY,
270
+ {"owner": owner, "repo": repo, "cursor": None},
271
+ provider=PROVIDER,
272
+ )
273
+ repo_data = (first_response.get("data") or {}).get("repository")
274
+ if not repo_data:
275
+ return _empty_stargazer_result(identifier)
276
+
277
+ total_stars = int(repo_data.get("stargazerCount", 0))
278
+ if total_stars == 0:
279
+ return _empty_stargazer_result(identifier)
280
+
281
+ first_edges = (repo_data.get("stargazers") or {}).get("edges") or []
282
+ first_starred_at: str | None = None
283
+ if first_edges and isinstance(first_edges[0], dict):
284
+ first_starred_at = first_edges[0].get("starredAt")
285
+
286
+ # Clamp sample count to total_stars — small repos get every star sampled
287
+ # (no point requesting 12 offsets from a 5-star repo).
288
+ effective_samples = min(sample_count, total_stars)
289
+
290
+ # Compute evenly-distributed offsets across the full stargazer list.
291
+ # Always includes position 1 (oldest) and position total_stars (newest).
292
+ if effective_samples <= 1:
293
+ offsets = [1]
294
+ else:
295
+ step = (total_stars - 1) / (effective_samples - 1)
296
+ offsets = sorted({max(1, round(1 + step * i)) for i in range(effective_samples)})
297
+
298
+ # Bounded concurrent fan-out: semaphore throttles to _GRAPHQL_CONCURRENCY
299
+ # in-flight requests at a time. For 12 offsets at concurrency 4, that's
300
+ # 3 waves of ≤4 calls each → ~1s cold-fetch latency.
301
+ sem = asyncio.Semaphore(_GRAPHQL_CONCURRENCY)
302
+
303
+ async def _fetch_one(offset: int) -> tuple[int, str | None]:
304
+ # Offset 1 is already in hand from the total_stars lookup.
305
+ if offset == 1 and first_starred_at is not None:
306
+ return offset, first_starred_at
307
+ cursor = _cursor_for_offset(offset)
308
+ async with sem:
309
+ resp = await fetch_graphql(
310
+ _CURSOR_OFFSET_QUERY,
311
+ {"owner": owner, "repo": repo, "cursor": cursor},
312
+ provider=PROVIDER,
313
+ )
314
+ edges_data = (((resp.get("data") or {}).get("repository") or {}).get("stargazers") or {}).get("edges") or []
315
+ starred_at: str | None = None
316
+ if edges_data and isinstance(edges_data[0], dict):
317
+ starred_at = edges_data[0].get("starredAt")
318
+ return offset, starred_at
319
+
320
+ results = await asyncio.gather(
321
+ *[_fetch_one(off) for off in offsets],
322
+ return_exceptions=True,
323
+ )
324
+
325
+ points: list[dict[str, Any]] = []
326
+ for item in results:
327
+ if isinstance(item, BaseException):
328
+ continue
329
+ offset, starred_at = item
330
+ if starred_at is None:
331
+ continue
332
+ points.append({"date": starred_at, "count": offset})
333
+
334
+ # Append the honest now-point: current timestamp at the real total count.
335
+ if points:
336
+ points.append({"date": datetime.now(UTC).isoformat(), "count": total_stars})
337
+ points.sort(key=lambda p: p["date"])
338
+
339
+ return {
340
+ "points": points,
341
+ "current_stars": total_stars,
342
+ "repo": identifier,
343
+ "ttl": STARGAZER_HISTORY_TTL,
344
+ }
345
+
346
+
347
+ def _empty_stargazer_result(identifier: str) -> dict[str, Any]:
348
+ """Shared empty-state shape for zero-star or missing-repo cases."""
349
+ return {
350
+ "points": [],
351
+ "current_stars": 0,
352
+ "repo": identifier,
353
+ "ttl": STARGAZER_HISTORY_TTL,
354
+ }
355
+
356
+
357
+ async def _fetch_stargazer_history_rest(
358
+ owner: str,
359
+ repo: str,
360
+ sample_pages: int = 12,
361
+ ) -> dict[str, Any]:
362
+ """REST-based stargazer sampling — the v0.2.7 behavior preserved as fallback.
363
+
364
+ Used when no ``HW_GITHUB_TOKENS`` is set or when the GraphQL path fails.
365
+ Samples evenly across pages ``[1, min(total_pages, 400)]`` and stamps the
366
+ now-point with the current UTC timestamp. Bounded by GitHub's 400-page
367
+ deep-pagination cap: produces a truthful but limited view of mega-repos.
368
+ """
369
+ identifier = f"{owner}/{repo}"
370
+
179
371
  # Step 1: total stars
180
372
  repo_url = f"https://api.github.com/repos/{identifier}"
181
373
  repo_data = await fetch_json(repo_url, provider=PROVIDER)
182
374
  total_stars = int(repo_data.get("stargazers_count", 0))
183
375
  if total_stars == 0:
184
- empty_result: dict[str, Any] = {
376
+ return {
185
377
  "points": [],
186
378
  "current_stars": 0,
187
379
  "repo": identifier,
188
380
  "ttl": STARGAZER_HISTORY_TTL,
189
381
  }
190
- cache.set(cache_key, empty_result, STARGAZER_HISTORY_TTL)
191
- return empty_result
192
382
 
193
383
  # Step 2: compute total pages (clamped at GitHub's deep-pagination cap)
194
384
  total_pages = max(1, math.ceil(total_stars / _STARGAZER_PAGE_SIZE))
195
385
  effective_pages = min(total_pages, _STARGAZER_PAGE_CAP)
196
386
 
197
- # Single-page case: repo has ≤ 30 stars. The "first starred_at of the page"
387
+ # Single-page case: repo has ≤ 100 stars. The "first starred_at of the page"
198
388
  # sampling trick would otherwise collapse to a single aggregated point plus
199
389
  # a duplicate-date "now" point, producing a zero time-range polyline. Use
200
390
  # each stargazer's own timestamp instead — the whole page fits in one call.
@@ -210,14 +400,12 @@ async def fetch_stargazer_history(
210
400
  for idx, entry in enumerate(page_payload):
211
401
  if isinstance(entry, dict) and entry.get("starred_at"):
212
402
  single_page_points.append({"date": entry["starred_at"], "count": idx + 1})
213
- single_page_result: dict[str, Any] = {
403
+ return {
214
404
  "points": single_page_points,
215
405
  "current_stars": total_stars,
216
406
  "repo": identifier,
217
407
  "ttl": STARGAZER_HISTORY_TTL,
218
408
  }
219
- cache.set(cache_key, single_page_result, STARGAZER_HISTORY_TTL)
220
- return single_page_result
221
409
 
222
410
  # Cap the sample count at the number of reachable pages.
223
411
  sample_count = min(sample_pages, effective_pages)
@@ -264,14 +452,12 @@ async def fetch_stargazer_history(
264
452
  points.append({"date": datetime.now(UTC).isoformat(), "count": total_stars})
265
453
  points.sort(key=lambda p: p["date"])
266
454
 
267
- result: dict[str, Any] = {
455
+ return {
268
456
  "points": points,
269
457
  "current_stars": total_stars,
270
458
  "repo": identifier,
271
459
  "ttl": STARGAZER_HISTORY_TTL,
272
460
  }
273
- cache.set(cache_key, result, STARGAZER_HISTORY_TTL)
274
- return result
275
461
 
276
462
 
277
463
  # ── Session 2A+2B: contribution calendar scraping ──────────────────────────