ashlr-plugin 1.36.4

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 (499) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +39 -0
  3. package/.claude-plugin/plugin.json +36 -0
  4. package/.codex/agents/README.md +9 -0
  5. package/.codex/agents/ashlr-explorer.md +36 -0
  6. package/.codex/agents/ashlr-worker.md +38 -0
  7. package/.codex-plugin/plugin.json +45 -0
  8. package/.mcp.json +14 -0
  9. package/LICENSE +21 -0
  10. package/README.md +311 -0
  11. package/agents/ashlr-code.md +69 -0
  12. package/agents/ashlr-explore.md +44 -0
  13. package/agents/ashlr-plan.md +54 -0
  14. package/bench/fixtures/notebook-large.ipynb +660 -0
  15. package/bench/fixtures/notebook-small.ipynb +39 -0
  16. package/bench/fixtures/tasklist-closed.json +62 -0
  17. package/bench/fixtures/tasklist-large-mixed.json +27 -0
  18. package/bench/fixtures/tasklist-sprint-open.json +79 -0
  19. package/bench/fixtures/websearch-bun-runtime.json +32 -0
  20. package/bench/fixtures/websearch-react-hooks-patterns.json +32 -0
  21. package/bench/fixtures/websearch-typescript-generics.json +38 -0
  22. package/bench/refs/node-sdk/.refrev +1 -0
  23. package/bench/refs/node-sdk/README.md +17 -0
  24. package/bench/refs/node-sdk/src/agent/agent.ts +163 -0
  25. package/bench/refs/node-sdk/src/agent/create-agent-ui-stream.ts +83 -0
  26. package/bench/refs/node-sdk/src/agent/tool-loop-agent.ts +238 -0
  27. package/bench/refs/node-sdk/src/embed/embed-many.ts +409 -0
  28. package/bench/refs/node-sdk/src/embed/embed-result.ts +50 -0
  29. package/bench/refs/node-sdk/src/embed/embed.ts +257 -0
  30. package/bench/refs/node-sdk/src/error/invalid-argument-error.ts +34 -0
  31. package/bench/refs/node-sdk/src/error/invalid-tool-input-error.ts +33 -0
  32. package/bench/refs/node-sdk/src/error/no-object-generated-error.ts +70 -0
  33. package/bench/refs/node-sdk/src/generate-image/generate-image-result.ts +42 -0
  34. package/bench/refs/node-sdk/src/generate-image/generate-image.ts +362 -0
  35. package/bench/refs/node-sdk/src/generate-object/generate-object-result.ts +67 -0
  36. package/bench/refs/node-sdk/src/generate-object/generate-object.ts +501 -0
  37. package/bench/refs/node-sdk/src/generate-object/inject-json-instruction.ts +30 -0
  38. package/bench/refs/node-sdk/src/generate-object/output-strategy.ts +415 -0
  39. package/bench/refs/node-sdk/src/generate-object/repair-text.ts +12 -0
  40. package/bench/refs/node-sdk/src/generate-object/stream-object.ts +960 -0
  41. package/bench/refs/node-sdk/src/generate-speech/generate-speech.ts +191 -0
  42. package/bench/refs/node-sdk/src/generate-text/generate-text.ts +1484 -0
  43. package/bench/refs/node-sdk/src/generate-text/stream-text.ts +2708 -0
  44. package/bench/refs/node-sdk/src/generate-text/tool-call.ts +59 -0
  45. package/bench/refs/node-sdk/src/generate-text/tool-result.ts +36 -0
  46. package/bench/refs/node-sdk/src/telemetry/create-telemetry-dispatcher.ts +146 -0
  47. package/bench/refs/node-sdk/src/telemetry/telemetry-registry.ts +15 -0
  48. package/bench/refs/node-sdk/src/telemetry/telemetry.ts +223 -0
  49. package/bench/refs/node-sdk/src/util/async-iterable-stream.ts +94 -0
  50. package/bench/refs/node-sdk/src/util/consume-stream.ts +31 -0
  51. package/bench/refs/node-sdk/src/util/create-stitchable-stream.ts +112 -0
  52. package/bench/refs/node-sdk/src/util/download/download.ts +74 -0
  53. package/bench/refs/node-sdk/src/util/error-handler.ts +1 -0
  54. package/bench/refs/node-sdk/src/util/fix-json.ts +401 -0
  55. package/bench/refs/node-sdk/src/util/merge-objects.ts +79 -0
  56. package/bench/refs/python-lib/.refrev +1 -0
  57. package/bench/refs/python-lib/README.md +18 -0
  58. package/bench/refs/python-lib/pandas/core/_numba/executor.py +249 -0
  59. package/bench/refs/python-lib/pandas/core/_numba/extensions.py +586 -0
  60. package/bench/refs/python-lib/pandas/core/_numba/kernels/mean_.py +198 -0
  61. package/bench/refs/python-lib/pandas/core/_numba/kernels/min_max_.py +178 -0
  62. package/bench/refs/python-lib/pandas/core/_numba/kernels/sum_.py +255 -0
  63. package/bench/refs/python-lib/pandas/core/_numba/kernels/var_.py +251 -0
  64. package/bench/refs/python-lib/pandas/core/accessor.py +520 -0
  65. package/bench/refs/python-lib/pandas/core/api.py +138 -0
  66. package/bench/refs/python-lib/pandas/core/array_algos/masked_accumulations.py +97 -0
  67. package/bench/refs/python-lib/pandas/core/array_algos/masked_reductions.py +252 -0
  68. package/bench/refs/python-lib/pandas/core/array_algos/putmask.py +150 -0
  69. package/bench/refs/python-lib/pandas/core/array_algos/quantile.py +232 -0
  70. package/bench/refs/python-lib/pandas/core/array_algos/replace.py +159 -0
  71. package/bench/refs/python-lib/pandas/core/array_algos/take.py +609 -0
  72. package/bench/refs/python-lib/pandas/core/arraylike.py +534 -0
  73. package/bench/refs/python-lib/pandas/core/arrays/_arrow_string_mixins.py +434 -0
  74. package/bench/refs/python-lib/pandas/core/arrays/_mixins.py +630 -0
  75. package/bench/refs/python-lib/pandas/core/arrays/_ranges.py +276 -0
  76. package/bench/refs/python-lib/pandas/core/arrays/_utils.py +77 -0
  77. package/bench/refs/python-lib/pandas/core/arrays/arrow/accessors.py +521 -0
  78. package/bench/refs/python-lib/pandas/core/arrays/arrow/extension_types.py +174 -0
  79. package/bench/refs/python-lib/pandas/core/arrays/boolean.py +454 -0
  80. package/bench/refs/python-lib/pandas/core/arrays/floating.py +192 -0
  81. package/bench/refs/python-lib/pandas/core/arrays/integer.py +296 -0
  82. package/bench/refs/python-lib/pandas/core/arrays/masked.py +2140 -0
  83. package/bench/refs/python-lib/pandas/core/arrays/numeric.py +305 -0
  84. package/bench/refs/python-lib/pandas/core/arrays/numpy_.py +701 -0
  85. package/bench/refs/python-lib/pandas/core/arrays/period.py +1586 -0
  86. package/bench/refs/python-lib/pandas/core/arrays/sparse/accessor.py +505 -0
  87. package/bench/refs/python-lib/pandas/core/arrays/sparse/scipy_sparse.py +208 -0
  88. package/bench/refs/rust-project/.refrev +1 -0
  89. package/bench/refs/rust-project/README.md +17 -0
  90. package/bench/refs/rust-project/tokio/src/doc/os.rs +68 -0
  91. package/bench/refs/rust-project/tokio/src/fs/dir_builder.rs +129 -0
  92. package/bench/refs/rust-project/tokio/src/fs/file.rs +1113 -0
  93. package/bench/refs/rust-project/tokio/src/fs/mocks.rs +176 -0
  94. package/bench/refs/rust-project/tokio/src/fs/mod.rs +324 -0
  95. package/bench/refs/rust-project/tokio/src/fs/open_options.rs +856 -0
  96. package/bench/refs/rust-project/tokio/src/fs/read.rs +80 -0
  97. package/bench/refs/rust-project/tokio/src/fs/read_dir.rs +357 -0
  98. package/bench/refs/rust-project/tokio/src/fs/write.rs +99 -0
  99. package/bench/refs/rust-project/tokio/src/future/maybe_done.rs +123 -0
  100. package/bench/refs/rust-project/tokio/src/future/try_join.rs +82 -0
  101. package/bench/refs/rust-project/tokio/src/io/async_buf_read.rs +117 -0
  102. package/bench/refs/rust-project/tokio/src/io/async_read.rs +133 -0
  103. package/bench/refs/rust-project/tokio/src/io/async_seek.rs +96 -0
  104. package/bench/refs/rust-project/tokio/src/io/async_write.rs +402 -0
  105. package/bench/refs/rust-project/tokio/src/io/blocking.rs +339 -0
  106. package/bench/refs/rust-project/tokio/src/io/interest.rs +345 -0
  107. package/bench/refs/rust-project/tokio/src/io/join.rs +130 -0
  108. package/bench/refs/rust-project/tokio/src/io/mod.rs +308 -0
  109. package/bench/refs/rust-project/tokio/src/io/poll_evented.rs +319 -0
  110. package/bench/refs/rust-project/tokio/src/io/read_buf.rs +344 -0
  111. package/bench/refs/rust-project/tokio/src/io/ready.rs +322 -0
  112. package/bench/refs/rust-project/tokio/src/io/split.rs +166 -0
  113. package/bench/refs/rust-project/tokio/src/io/stderr.rs +148 -0
  114. package/bench/refs/rust-project/tokio/src/io/stdin.rs +98 -0
  115. package/bench/refs/rust-project/tokio/src/io/stdio_common.rs +222 -0
  116. package/bench/refs/rust-project/tokio/src/io/stdout.rs +197 -0
  117. package/bench/refs/rust-project/tokio/src/io/util/buf_reader.rs +311 -0
  118. package/bench/refs/rust-project/tokio/src/io/util/buf_stream.rs +207 -0
  119. package/bench/refs/rust-project/tokio/src/io/util/buf_writer.rs +310 -0
  120. package/bench/refs/rust-project/tokio/src/io/util/chain.rs +144 -0
  121. package/commands/_spawn-patterns.json +32 -0
  122. package/commands/ashlr-allow.md +21 -0
  123. package/commands/ashlr-benchmark.md +53 -0
  124. package/commands/ashlr-brief.md +97 -0
  125. package/commands/ashlr-budget.md +76 -0
  126. package/commands/ashlr-compact.md +54 -0
  127. package/commands/ashlr-dashboard.md +37 -0
  128. package/commands/ashlr-doctor.md +56 -0
  129. package/commands/ashlr-eco-mode.md +92 -0
  130. package/commands/ashlr-genome-init.md +24 -0
  131. package/commands/ashlr-genome-keygen.md +36 -0
  132. package/commands/ashlr-genome-loop.md +118 -0
  133. package/commands/ashlr-genome-push.md +36 -0
  134. package/commands/ashlr-genome-rewrap.md +48 -0
  135. package/commands/ashlr-genome-team-init.md +67 -0
  136. package/commands/ashlr-handoff.md +23 -0
  137. package/commands/ashlr-help.md +86 -0
  138. package/commands/ashlr-hook-timings.md +30 -0
  139. package/commands/ashlr-leaderboard.md +50 -0
  140. package/commands/ashlr-ollama-setup.md +39 -0
  141. package/commands/ashlr-orchestrate-status.md +72 -0
  142. package/commands/ashlr-orchestrate.md +83 -0
  143. package/commands/ashlr-parallelize.md +63 -0
  144. package/commands/ashlr-report-crash.md +33 -0
  145. package/commands/ashlr-resume.md +72 -0
  146. package/commands/ashlr-savings.md +24 -0
  147. package/commands/ashlr-settings.md +50 -0
  148. package/commands/ashlr-spawn.md +61 -0
  149. package/commands/ashlr-start.md +60 -0
  150. package/commands/ashlr-status.md +64 -0
  151. package/commands/ashlr-team-invite.md +47 -0
  152. package/commands/ashlr-tier.md +122 -0
  153. package/commands/ashlr-tour.md +60 -0
  154. package/commands/ashlr-update.md +155 -0
  155. package/commands/ashlr-upgrade.md +96 -0
  156. package/commands/goal.md +58 -0
  157. package/commands/loop.md +40 -0
  158. package/docs/CNAME +1 -0
  159. package/docs/PRO_TIER.md +104 -0
  160. package/docs/README.md +21 -0
  161. package/docs/architecture.md +415 -0
  162. package/docs/ashlr-pipe.md +161 -0
  163. package/docs/assets/logo.svg +19 -0
  164. package/docs/assets/og.png +0 -0
  165. package/docs/assets/og.svg +41 -0
  166. package/docs/benchmarks-v2.json +355 -0
  167. package/docs/benchmarks.json +102 -0
  168. package/docs/benchmarks.md +211 -0
  169. package/docs/billing.md +185 -0
  170. package/docs/cloud-genome.md +155 -0
  171. package/docs/competitive-wozcode.md +92 -0
  172. package/docs/deploy.md +325 -0
  173. package/docs/email-templates.md +113 -0
  174. package/docs/embed-tuning.md +103 -0
  175. package/docs/genome.md +120 -0
  176. package/docs/github-oauth-onboarding.md +127 -0
  177. package/docs/hero-video-script.md +129 -0
  178. package/docs/index.html +2262 -0
  179. package/docs/install-prompt.md +78 -0
  180. package/docs/install-windows.md +132 -0
  181. package/docs/install.ps1 +171 -0
  182. package/docs/install.sh +146 -0
  183. package/docs/launch/README.md +114 -0
  184. package/docs/launch/hn.md +49 -0
  185. package/docs/launch/reddit.md +45 -0
  186. package/docs/launch/x.md +65 -0
  187. package/docs/launch-post.md +112 -0
  188. package/docs/legal.md +78 -0
  189. package/docs/llm-providers.md +99 -0
  190. package/docs/multi-host-mcp.md +217 -0
  191. package/docs/multi-turn-architecture.md +108 -0
  192. package/docs/operations/deployment.md +108 -0
  193. package/docs/operations/error-monitoring.md +70 -0
  194. package/docs/operations/weekly-digest.md +70 -0
  195. package/docs/operations.md +494 -0
  196. package/docs/platform-support.md +133 -0
  197. package/docs/policy-packs.md +188 -0
  198. package/docs/press-kit/fact-sheet.md +78 -0
  199. package/docs/press-kit/founder-bio.md +3 -0
  200. package/docs/press-kit/logo.svg +33 -0
  201. package/docs/press-kit/screenshots.md +146 -0
  202. package/docs/pricing.md +251 -0
  203. package/docs/pro-backend-architecture.md +523 -0
  204. package/docs/producthunt.md +52 -0
  205. package/docs/router-migration.md +110 -0
  206. package/docs/session-log-schema.md +68 -0
  207. package/docs/social.md +95 -0
  208. package/docs/team-genome.md +528 -0
  209. package/docs/telemetry.md +194 -0
  210. package/docs/test-isolation.md +100 -0
  211. package/docs/upgrade.md +159 -0
  212. package/hooks/_hook-errors.ts +87 -0
  213. package/hooks/_hook-perf-emit.ts +113 -0
  214. package/hooks/_hook-timing-ledger.ts +1133 -0
  215. package/hooks/_nudge-throttle.ts +127 -0
  216. package/hooks/_recent-blocks.ts +127 -0
  217. package/hooks/audit-upload.ts +86 -0
  218. package/hooks/codex-hooks.json +171 -0
  219. package/hooks/commit-attribution.ts +179 -0
  220. package/hooks/edit-batching-nudge.ts +154 -0
  221. package/hooks/genome-scribe-hook.ts +219 -0
  222. package/hooks/hooks.json +294 -0
  223. package/hooks/orient-nudge-hook.ts +242 -0
  224. package/hooks/policy-enforce.ts +214 -0
  225. package/hooks/post-tool-use-embedding.ts +93 -0
  226. package/hooks/post-tool-use-genome.sh +37 -0
  227. package/hooks/post-tool-use-genome.ts +69 -0
  228. package/hooks/posttooluse-correlate.ts +122 -0
  229. package/hooks/posttooluse-dedupe-output.ts +241 -0
  230. package/hooks/posttooluse-genome-refresh.ts +223 -0
  231. package/hooks/posttooluse-native-nudge.ts +205 -0
  232. package/hooks/posttooluse-stale-result.ts +277 -0
  233. package/hooks/pretooluse-bash.ts +63 -0
  234. package/hooks/pretooluse-budget-guard.ts +318 -0
  235. package/hooks/pretooluse-common.ts +985 -0
  236. package/hooks/pretooluse-eco-router.ts +196 -0
  237. package/hooks/pretooluse-edit.sh +93 -0
  238. package/hooks/pretooluse-edit.ts +197 -0
  239. package/hooks/pretooluse-glob.ts +70 -0
  240. package/hooks/pretooluse-grep.sh +85 -0
  241. package/hooks/pretooluse-grep.ts +96 -0
  242. package/hooks/pretooluse-notebookedit.ts +121 -0
  243. package/hooks/pretooluse-read.sh +118 -0
  244. package/hooks/pretooluse-read.ts +118 -0
  245. package/hooks/pretooluse-task.ts +130 -0
  246. package/hooks/pretooluse-webfetch.ts +66 -0
  247. package/hooks/pretooluse-websearch.ts +85 -0
  248. package/hooks/pretooluse-write.ts +128 -0
  249. package/hooks/pulse-emit.ts +197 -0
  250. package/hooks/session-end-consolidate.sh +35 -0
  251. package/hooks/session-end-consolidate.ts +188 -0
  252. package/hooks/session-end-stats-gc.ts +80 -0
  253. package/hooks/session-log-append.sh +94 -0
  254. package/hooks/session-log-append.ts +116 -0
  255. package/hooks/session-start.ts +993 -0
  256. package/hooks/sessionend-hook-health-nudge.ts +451 -0
  257. package/hooks/sessionend-savings-nudge.ts +318 -0
  258. package/hooks/sessionend-status-line-nudge.ts +296 -0
  259. package/hooks/sessionstart-brief.ts +140 -0
  260. package/hooks/sessionstart-cost-refactor.ts +95 -0
  261. package/hooks/sessionstart-efficient.ts +110 -0
  262. package/hooks/sessionstart-genome-author.ts +93 -0
  263. package/hooks/sessionstart-lean-tools.ts +98 -0
  264. package/hooks/sessionstart-search.ts +97 -0
  265. package/hooks/stop-accounting.ts +152 -0
  266. package/hooks/subagent-stop-rollup.ts +162 -0
  267. package/hooks/userpromptsubmit-brief-trigger.ts +156 -0
  268. package/package.json +56 -0
  269. package/ports/README.md +108 -0
  270. package/ports/cursor/mcp.json +11 -0
  271. package/ports/goose/recipe.yaml +26 -0
  272. package/scripts/ashlr-mcp.ts +40 -0
  273. package/scripts/auto-update.ts +177 -0
  274. package/scripts/baseline-scan.ts +831 -0
  275. package/scripts/bench-orchestrate.ts +377 -0
  276. package/scripts/benchmark-refs.ts +292 -0
  277. package/scripts/bootstrap.mjs +100 -0
  278. package/scripts/brief-eval.ts +234 -0
  279. package/scripts/bump-version.ts +82 -0
  280. package/scripts/bun-resolve.d.mts +11 -0
  281. package/scripts/bun-resolve.mjs +59 -0
  282. package/scripts/calibrate-grep.ts +467 -0
  283. package/scripts/capture-grep-workload.ts +260 -0
  284. package/scripts/check-tier.ts +61 -0
  285. package/scripts/check-version-sync.ts +97 -0
  286. package/scripts/cli-orchestrate-status.ts +386 -0
  287. package/scripts/cli-orchestrate.ts +303 -0
  288. package/scripts/cli.ts +478 -0
  289. package/scripts/cloud-smoke-test.ts +313 -0
  290. package/scripts/coach-report.ts +373 -0
  291. package/scripts/context-db-migrate.ts +137 -0
  292. package/scripts/context-status.ts +43 -0
  293. package/scripts/demo-run.ts +255 -0
  294. package/scripts/deploy-smoke.sh +140 -0
  295. package/scripts/doctor.ts +992 -0
  296. package/scripts/embed-file-worker.ts +104 -0
  297. package/scripts/embed-tune.ts +308 -0
  298. package/scripts/errors-report.ts +428 -0
  299. package/scripts/find-test-leak.ts +69 -0
  300. package/scripts/generate-badge.ts +357 -0
  301. package/scripts/genome-auto-consolidate.ts +533 -0
  302. package/scripts/genome-auto-propose.ts +578 -0
  303. package/scripts/genome-cloud-pull.ts +363 -0
  304. package/scripts/genome-cloud-push.ts +382 -0
  305. package/scripts/genome-cloud-sync.ts +587 -0
  306. package/scripts/genome-commit-watcher.ts +295 -0
  307. package/scripts/genome-init.ts +986 -0
  308. package/scripts/genome-key.ts +233 -0
  309. package/scripts/genome-keygen.ts +168 -0
  310. package/scripts/genome-link.ts +59 -0
  311. package/scripts/genome-refresh-worker.ts +364 -0
  312. package/scripts/genome-rewrap.ts +90 -0
  313. package/scripts/genome-synthesizer.ts +679 -0
  314. package/scripts/genome-team-init.ts +312 -0
  315. package/scripts/handoff-pack.ts +438 -0
  316. package/scripts/hook-bootstrap.mjs +75 -0
  317. package/scripts/hook-timing-reader.ts +371 -0
  318. package/scripts/hook-timings-report.ts +554 -0
  319. package/scripts/install-genome-hooks.ts +196 -0
  320. package/scripts/install-onnx-model.ts +154 -0
  321. package/scripts/install-permissions.ts +276 -0
  322. package/scripts/install-status-line.ts +150 -0
  323. package/scripts/mcp-entrypoint.sh +78 -0
  324. package/scripts/mcp-entrypoint.ts +130 -0
  325. package/scripts/mdx-angle-bracket-lint.ts +152 -0
  326. package/scripts/measure-cold-start.ts +126 -0
  327. package/scripts/migrate-stats-to-sqlite.ts +86 -0
  328. package/scripts/ollama-setup.ts +498 -0
  329. package/scripts/onboarding-wizard.ts +1691 -0
  330. package/scripts/orchestrate-emit-telemetry.ts +177 -0
  331. package/scripts/orchestrate-expand.ts +517 -0
  332. package/scripts/orchestrate-run.ts +744 -0
  333. package/scripts/print-legend.ts +50 -0
  334. package/scripts/product-metadata.ts +38 -0
  335. package/scripts/prune-old-memories.ts +208 -0
  336. package/scripts/publish.sh +88 -0
  337. package/scripts/read-calibration.ts +122 -0
  338. package/scripts/report-crash.ts +226 -0
  339. package/scripts/run-benchmark.ts +750 -0
  340. package/scripts/run-root-tests.ts +28 -0
  341. package/scripts/savings-dashboard.ts +1258 -0
  342. package/scripts/savings-report-extras.ts +447 -0
  343. package/scripts/savings-status-line.ts +873 -0
  344. package/scripts/session-event-emit.ts +266 -0
  345. package/scripts/session-greet.ts +686 -0
  346. package/scripts/session-log-report.ts +551 -0
  347. package/scripts/session-resume.ts +614 -0
  348. package/scripts/set-hook-mode.ts +83 -0
  349. package/scripts/smoke-cross-platform.ts +117 -0
  350. package/scripts/smoke-realtime.ts +257 -0
  351. package/scripts/smoke-tools.ts +21 -0
  352. package/scripts/stats-cloud-pull.ts +213 -0
  353. package/scripts/stats-cloud-sync.ts +261 -0
  354. package/scripts/telemetry-flush.ts +160 -0
  355. package/scripts/telemetry-status.ts +266 -0
  356. package/scripts/timing-budget.ts +35 -0
  357. package/scripts/ui-animation.ts +120 -0
  358. package/scripts/ui.ts +431 -0
  359. package/scripts/upgrade-flow.ts +775 -0
  360. package/scripts/weekly-digest.ts +394 -0
  361. package/servers/_accounting.ts +78 -0
  362. package/servers/_ask-router.ts +84 -0
  363. package/servers/_ast-chunker.ts +434 -0
  364. package/servers/_ast-helpers.ts +183 -0
  365. package/servers/_ast-languages.ts +153 -0
  366. package/servers/_ast-refactor/_shared.ts +221 -0
  367. package/servers/_ast-refactor/cross-file-rename.ts +560 -0
  368. package/servers/_ast-refactor/extract-function.ts +357 -0
  369. package/servers/_ast-refactor/file-local-rename.ts +172 -0
  370. package/servers/_ast-refactor.ts +41 -0
  371. package/servers/_ast-skeleton.ts +107 -0
  372. package/servers/_bash-summarizers-registry.ts +1096 -0
  373. package/servers/_crash-dump.ts +210 -0
  374. package/servers/_cwd-clamp.ts +342 -0
  375. package/servers/_edit-log.ts +28 -0
  376. package/servers/_edit-match.ts +236 -0
  377. package/servers/_edit-validate.ts +196 -0
  378. package/servers/_embed-calibration.ts +123 -0
  379. package/servers/_embedding-cache.ts +401 -0
  380. package/servers/_embedding-model.ts +329 -0
  381. package/servers/_events.ts +113 -0
  382. package/servers/_genome-cache.ts +154 -0
  383. package/servers/_genome-commits.ts +172 -0
  384. package/servers/_genome-crypto-v2.ts +242 -0
  385. package/servers/_genome-crypto.ts +259 -0
  386. package/servers/_genome-discoveries.ts +140 -0
  387. package/servers/_genome-embed-populator.ts +216 -0
  388. package/servers/_genome-freshness.ts +134 -0
  389. package/servers/_genome-issues.ts +171 -0
  390. package/servers/_genome-live.ts +263 -0
  391. package/servers/_genome-prs.ts +188 -0
  392. package/servers/_genome-search.ts +479 -0
  393. package/servers/_genome-sync.ts +297 -0
  394. package/servers/_history-tracker.ts +279 -0
  395. package/servers/_http-helpers.ts +151 -0
  396. package/servers/_identity-hash.ts +151 -0
  397. package/servers/_llm-providers/anthropic.ts +132 -0
  398. package/servers/_llm-providers/cloud.ts +195 -0
  399. package/servers/_llm-providers/index.ts +350 -0
  400. package/servers/_llm-providers/local.ts +127 -0
  401. package/servers/_llm-providers/onnx.ts +393 -0
  402. package/servers/_llm-providers/types.ts +27 -0
  403. package/servers/_manifest-v2.ts +447 -0
  404. package/servers/_mcp-host.ts +93 -0
  405. package/servers/_nudge-events.ts +451 -0
  406. package/servers/_orchestrate-executor.ts +316 -0
  407. package/servers/_prefetch.ts +435 -0
  408. package/servers/_pricing.ts +256 -0
  409. package/servers/_pro.ts +381 -0
  410. package/servers/_read-cache.ts +44 -0
  411. package/servers/_router-handlers.ts +46 -0
  412. package/servers/_router.ts +55 -0
  413. package/servers/_run-with-timeout.ts +173 -0
  414. package/servers/_savings-render.ts +214 -0
  415. package/servers/_stats-sqlite.ts +640 -0
  416. package/servers/_stats.ts +1183 -0
  417. package/servers/_status-line-cells.ts +469 -0
  418. package/servers/_streaks.ts +162 -0
  419. package/servers/_summarize.ts +208 -0
  420. package/servers/_task-graph-render.ts +217 -0
  421. package/servers/_task-graph.ts +545 -0
  422. package/servers/_telemetry.ts +719 -0
  423. package/servers/_test-parsers.ts +530 -0
  424. package/servers/_test-watch.ts +626 -0
  425. package/servers/_text-helpers.ts +37 -0
  426. package/servers/_token-measure.ts +160 -0
  427. package/servers/_tool-base.ts +248 -0
  428. package/servers/_weekly-digest.ts +391 -0
  429. package/servers/ask-server-handlers.ts +49 -0
  430. package/servers/ask-server.ts +174 -0
  431. package/servers/bash-server-handlers.ts +138 -0
  432. package/servers/bash-server.ts +1136 -0
  433. package/servers/bench.ts +186 -0
  434. package/servers/diff-semantic-server-handlers.ts +39 -0
  435. package/servers/diff-semantic-server.ts +566 -0
  436. package/servers/diff-server-handlers.ts +317 -0
  437. package/servers/diff-server.ts +20 -0
  438. package/servers/edit-server-handlers.ts +31 -0
  439. package/servers/edit-server.ts +209 -0
  440. package/servers/edit-structural-server-handlers.ts +322 -0
  441. package/servers/efficiency-server-handlers.ts +161 -0
  442. package/servers/efficiency-server.ts +1135 -0
  443. package/servers/flush-server-handlers.ts +21 -0
  444. package/servers/flush-server.ts +19 -0
  445. package/servers/genome-server-handlers.ts +118 -0
  446. package/servers/genome-server.ts +397 -0
  447. package/servers/github-server-handlers.ts +187 -0
  448. package/servers/github-server.ts +856 -0
  449. package/servers/glob-server-handlers.ts +233 -0
  450. package/servers/glob-server.ts +20 -0
  451. package/servers/grep-server-handlers.ts +61 -0
  452. package/servers/grep-server.ts +564 -0
  453. package/servers/http-server-handlers.ts +36 -0
  454. package/servers/http-server.ts +149 -0
  455. package/servers/logs-server-handlers.ts +42 -0
  456. package/servers/logs-server.ts +359 -0
  457. package/servers/ls-server-handlers.ts +166 -0
  458. package/servers/ls-server.ts +20 -0
  459. package/servers/multi-edit-server-handlers.ts +51 -0
  460. package/servers/multi-edit-server.ts +422 -0
  461. package/servers/notebook-edit-server-handlers.ts +34 -0
  462. package/servers/notebook-edit-server.ts +181 -0
  463. package/servers/orient-server-handlers.ts +52 -0
  464. package/servers/orient-server.ts +436 -0
  465. package/servers/pipe-server-handlers.ts +68 -0
  466. package/servers/pipe-server.ts +300 -0
  467. package/servers/read-server-handlers.ts +42 -0
  468. package/servers/read-server.ts +206 -0
  469. package/servers/rename-file-server-handlers.ts +67 -0
  470. package/servers/rename-file-server.ts +699 -0
  471. package/servers/savings-server-handlers.ts +27 -0
  472. package/servers/savings-server.ts +54 -0
  473. package/servers/search-replace-regex-server-handlers.ts +96 -0
  474. package/servers/search-replace-regex-server.ts +751 -0
  475. package/servers/sql-server-handlers.ts +58 -0
  476. package/servers/sql-server.ts +505 -0
  477. package/servers/task-server-handlers.ts +358 -0
  478. package/servers/task-server.ts +24 -0
  479. package/servers/test-server-handlers.ts +351 -0
  480. package/servers/test-server.ts +20 -0
  481. package/servers/tree-server-handlers.ts +503 -0
  482. package/servers/tree-server.ts +20 -0
  483. package/servers/webfetch-server-handlers.ts +166 -0
  484. package/servers/webfetch-server.ts +20 -0
  485. package/servers/websearch-server-handlers.ts +215 -0
  486. package/servers/websearch-server.ts +19 -0
  487. package/servers/write-server-handlers.ts +27 -0
  488. package/servers/write-server.ts +97 -0
  489. package/skills/ashlr-brief/SKILL.md +80 -0
  490. package/skills/ashlr-codex-doctor/SKILL.md +21 -0
  491. package/skills/ashlr-codex-efficient-tools/SKILL.md +17 -0
  492. package/skills/ashlr-codex-genome-refresh/SKILL.md +20 -0
  493. package/skills/ashlr-codex-parallel-explore/SKILL.md +20 -0
  494. package/skills/ashlr-codex-savings/SKILL.md +17 -0
  495. package/skills/ashlr-cost-refactor/SKILL.md +50 -0
  496. package/skills/ashlr-efficient/SKILL.md +64 -0
  497. package/skills/ashlr-genome-author/SKILL.md +43 -0
  498. package/skills/ashlr-lean-tools/SKILL.md +43 -0
  499. package/skills/ashlr-search/SKILL.md +46 -0
@@ -0,0 +1,2262 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="description" content="ashlr — an open-source Codex and Claude Code plugin that keeps a line-item count of every token it saves you, and does something about it." />
7
+ <meta name="author" content="Mason Wyatt" />
8
+ <meta name="theme-color" content="#F3EADB" />
9
+
10
+ <!-- Open Graph / Facebook / LinkedIn / Slack -->
11
+ <meta property="og:type" content="website" />
12
+ <meta property="og:site_name" content="ashlr" />
13
+ <meta property="og:url" content="https://plugin.ashlr.ai/" />
14
+ <meta property="og:title" content="ashlr · The Token Ledger" />
15
+ <meta property="og:description" content="Open-source Codex and Claude Code plugin. Mean −57% token savings across reference repos. MIT." />
16
+ <meta property="og:image" content="https://plugin.ashlr.ai/assets/og.png" />
17
+ <meta property="og:image:width" content="1200" />
18
+ <meta property="og:image:height" content="630" />
19
+ <meta property="og:image:alt" content="ashlr · The Token Ledger — open-source Codex and Claude Code plugin" />
20
+
21
+ <!-- Twitter / X -->
22
+ <meta name="twitter:card" content="summary_large_image" />
23
+ <meta name="twitter:title" content="ashlr · The Token Ledger" />
24
+ <meta name="twitter:description" content="Open-source Codex and Claude Code plugin. Mean −57% token savings across reference repos. MIT." />
25
+ <meta name="twitter:image" content="https://plugin.ashlr.ai/assets/og.png" />
26
+
27
+ <link rel="canonical" href="https://plugin.ashlr.ai/" />
28
+
29
+ <!-- Favicon (logo matches ashlr.ai dev tools section) -->
30
+ <link rel="icon" type="image/svg+xml" href="/assets/logo.svg" />
31
+ <link rel="alternate icon" type="image/png" href="/assets/og.png" />
32
+ <link rel="apple-touch-icon" href="/assets/og.png" />
33
+ <link rel="mask-icon" href="/assets/logo.svg" color="#8B2E1A" />
34
+
35
+ <title>ashlr · The Token Ledger for Codex and Claude Code</title>
36
+
37
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
38
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
39
+ <link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght,SOFT,WONK@9..144,300..900,0..100,0..1&family=IBM+Plex+Sans:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
40
+
41
+ <style>
42
+ :root {
43
+ /* Parchment palette — ledger aesthetic */
44
+ --paper: #F3EADB;
45
+ --paper-deep: #ECE2CE;
46
+ --paper-shadow: #D9CDB3;
47
+ --ink: #121212;
48
+ --ink-80: rgba(18, 18, 18, 0.82);
49
+ --ink-55: rgba(18, 18, 18, 0.55);
50
+ --ink-30: rgba(18, 18, 18, 0.28);
51
+ --ink-10: rgba(18, 18, 18, 0.09);
52
+
53
+ /* Accountant's red — sparingly, for savings marks */
54
+ --debit: #8B2E1A;
55
+ --debit-deep: #5E1E11;
56
+ --credit: #4F5B3F;
57
+
58
+ /* Metrics */
59
+ --max-w: 1240px;
60
+ --gutter: clamp(24px, 4vw, 56px);
61
+
62
+ /* Typography */
63
+ --font-display: "Fraunces", ui-serif, Georgia, serif;
64
+ --font-body: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
65
+ --font-mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
66
+ }
67
+
68
+ @media (prefers-color-scheme: dark) {
69
+ :root {
70
+ --paper: #12120F;
71
+ --paper-deep: #1A1A14;
72
+ --paper-shadow: #000;
73
+ --ink: #F3EADB;
74
+ --ink-80: rgba(243, 234, 219, 0.84);
75
+ --ink-55: rgba(243, 234, 219, 0.6);
76
+ --ink-30: rgba(243, 234, 219, 0.32);
77
+ --ink-10: rgba(243, 234, 219, 0.1);
78
+ --debit: #D06B54;
79
+ --debit-deep: #8B2E1A;
80
+ --credit: #98A789;
81
+ }
82
+ }
83
+
84
+ * { box-sizing: border-box; margin: 0; padding: 0; }
85
+
86
+ html { scroll-behavior: smooth; }
87
+
88
+ body {
89
+ font-family: var(--font-body);
90
+ font-weight: 400;
91
+ color: var(--ink);
92
+ background: var(--paper);
93
+ line-height: 1.55;
94
+ font-feature-settings: "ss01" on, "cv11" on;
95
+ -webkit-font-smoothing: antialiased;
96
+ text-rendering: optimizeLegibility;
97
+ overflow-x: hidden;
98
+ }
99
+
100
+ /* Subtle paper grain — generated SVG noise */
101
+ body::before {
102
+ content: "";
103
+ position: fixed;
104
+ inset: 0;
105
+ pointer-events: none;
106
+ z-index: 1;
107
+ opacity: 0.35;
108
+ mix-blend-mode: multiply;
109
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0 0.1 0 0 0 0.05 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
110
+ }
111
+ @media (prefers-color-scheme: dark) {
112
+ body::before { mix-blend-mode: screen; opacity: 0.2; }
113
+ }
114
+
115
+ a {
116
+ color: var(--ink);
117
+ text-decoration: none;
118
+ transition: color 0.2s ease;
119
+ }
120
+
121
+ .wrap {
122
+ max-width: var(--max-w);
123
+ margin: 0 auto;
124
+ padding: 0 var(--gutter);
125
+ position: relative;
126
+ z-index: 2;
127
+ }
128
+
129
+ /* ------------ Header ------------ */
130
+ .header {
131
+ padding: 28px 0 24px;
132
+ border-bottom: 1px solid var(--ink-10);
133
+ font-family: var(--font-mono);
134
+ font-size: 13px;
135
+ text-transform: uppercase;
136
+ letter-spacing: 0.08em;
137
+ }
138
+
139
+ .header-inner {
140
+ display: flex;
141
+ justify-content: space-between;
142
+ align-items: baseline;
143
+ gap: 24px;
144
+ }
145
+
146
+ .brand {
147
+ font-family: var(--font-display);
148
+ font-size: 22px;
149
+ font-weight: 500;
150
+ font-variation-settings: "SOFT" 30, "opsz" 30;
151
+ letter-spacing: -0.01em;
152
+ text-transform: none;
153
+ display: inline-flex;
154
+ align-items: center;
155
+ gap: 10px;
156
+ }
157
+
158
+ .brand-logo {
159
+ width: 26px;
160
+ height: 26px;
161
+ display: inline-block;
162
+ transform: translateY(-1px);
163
+ }
164
+
165
+ .brand-mark {
166
+ display: inline-block;
167
+ width: 8px;
168
+ height: 8px;
169
+ background: var(--debit);
170
+ margin-right: 8px;
171
+ transform: translateY(-4px);
172
+ border-radius: 1px;
173
+ }
174
+
175
+ .nav {
176
+ display: flex;
177
+ gap: 36px;
178
+ }
179
+
180
+ .nav a {
181
+ color: var(--ink-55);
182
+ position: relative;
183
+ }
184
+ .nav a:hover { color: var(--ink); }
185
+ .nav a::after {
186
+ content: ""; position: absolute; left: 0; right: 0; bottom: -6px;
187
+ height: 1px; background: var(--debit);
188
+ transform: scaleX(0); transform-origin: left;
189
+ transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
190
+ }
191
+ .nav a:hover::after { transform: scaleX(1); }
192
+
193
+ /* ------------ Hero ------------ */
194
+ .hero {
195
+ padding: 72px 0 96px;
196
+ position: relative;
197
+ }
198
+
199
+ .hero::after {
200
+ content: "";
201
+ position: absolute;
202
+ left: var(--gutter);
203
+ right: var(--gutter);
204
+ bottom: 0;
205
+ height: 1px;
206
+ background: var(--ink-10);
207
+ }
208
+
209
+ .hero-meta {
210
+ display: flex;
211
+ justify-content: space-between;
212
+ font-family: var(--font-mono);
213
+ font-size: 12px;
214
+ letter-spacing: 0.06em;
215
+ text-transform: uppercase;
216
+ color: var(--ink-55);
217
+ margin-bottom: 56px;
218
+ }
219
+
220
+ .eyebrow {
221
+ font-family: var(--font-mono);
222
+ font-size: 12px;
223
+ letter-spacing: 0.16em;
224
+ text-transform: uppercase;
225
+ color: var(--debit);
226
+ margin-bottom: 28px;
227
+ display: flex;
228
+ align-items: center;
229
+ gap: 12px;
230
+ }
231
+
232
+ .eyebrow::before {
233
+ content: "";
234
+ width: 28px;
235
+ height: 1px;
236
+ background: var(--debit);
237
+ }
238
+
239
+ h1.display {
240
+ font-family: var(--font-display);
241
+ font-weight: 300;
242
+ font-size: clamp(56px, 9.5vw, 136px);
243
+ font-variation-settings: "SOFT" 40, "WONK" 0, "opsz" 144;
244
+ line-height: 0.93;
245
+ letter-spacing: -0.035em;
246
+ margin-bottom: 36px;
247
+ }
248
+
249
+ h1.display .italic {
250
+ font-style: italic;
251
+ font-weight: 300;
252
+ font-variation-settings: "SOFT" 80, "WONK" 1, "opsz" 144;
253
+ color: var(--debit);
254
+ }
255
+
256
+ .lead {
257
+ font-family: var(--font-display);
258
+ font-weight: 300;
259
+ font-variation-settings: "opsz" 36;
260
+ font-size: clamp(20px, 2.2vw, 27px);
261
+ line-height: 1.4;
262
+ color: var(--ink-80);
263
+ max-width: 680px;
264
+ margin-bottom: 48px;
265
+ letter-spacing: -0.005em;
266
+ }
267
+
268
+ .hero-ctas {
269
+ display: flex;
270
+ gap: 16px;
271
+ flex-wrap: wrap;
272
+ margin-bottom: 88px;
273
+ }
274
+
275
+ .btn {
276
+ font-family: var(--font-mono);
277
+ font-size: 13px;
278
+ letter-spacing: 0.08em;
279
+ text-transform: uppercase;
280
+ padding: 14px 22px;
281
+ border: 1px solid var(--ink);
282
+ display: inline-flex;
283
+ align-items: center;
284
+ gap: 10px;
285
+ cursor: pointer;
286
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
287
+ background: transparent;
288
+ color: var(--ink);
289
+ font-weight: 500;
290
+ }
291
+
292
+ .btn-primary {
293
+ background: var(--ink);
294
+ color: var(--paper);
295
+ }
296
+ .btn-primary:hover {
297
+ background: var(--debit);
298
+ border-color: var(--debit);
299
+ }
300
+
301
+ .btn-secondary:hover {
302
+ background: var(--ink);
303
+ color: var(--paper);
304
+ }
305
+
306
+ .btn .caret {
307
+ transition: transform 0.2s ease;
308
+ display: inline-block;
309
+ }
310
+ .btn:hover .caret { transform: translateX(4px); }
311
+
312
+ /* ------------ Ledger ------------ */
313
+ .ledger {
314
+ border: 1px solid var(--ink);
315
+ background: var(--paper-deep);
316
+ font-family: var(--font-mono);
317
+ font-size: 13px;
318
+ box-shadow: 6px 6px 0 var(--ink);
319
+ position: relative;
320
+ }
321
+
322
+ .ledger-head {
323
+ display: flex;
324
+ justify-content: space-between;
325
+ align-items: center;
326
+ padding: 16px 24px;
327
+ border-bottom: 1px solid var(--ink);
328
+ background: var(--paper);
329
+ }
330
+
331
+ .ledger-title {
332
+ font-family: var(--font-mono);
333
+ font-size: 11px;
334
+ letter-spacing: 0.18em;
335
+ text-transform: uppercase;
336
+ }
337
+
338
+ .ledger-title-right {
339
+ font-size: 11px;
340
+ color: var(--ink-55);
341
+ letter-spacing: 0.1em;
342
+ text-transform: uppercase;
343
+ }
344
+
345
+ .ledger-title-right .dot {
346
+ display: inline-block;
347
+ width: 6px; height: 6px;
348
+ border-radius: 50%;
349
+ background: var(--credit);
350
+ margin-right: 6px;
351
+ animation: pulse 2s infinite;
352
+ }
353
+
354
+ @keyframes pulse {
355
+ 0%, 100% { opacity: 1; }
356
+ 50% { opacity: 0.3; }
357
+ }
358
+
359
+ .ledger-row {
360
+ display: grid;
361
+ grid-template-columns: 110px 140px 1fr 140px;
362
+ padding: 13px 24px;
363
+ border-bottom: 1px dashed var(--ink-10);
364
+ align-items: center;
365
+ opacity: 0;
366
+ transform: translateY(6px);
367
+ animation: row-in 0.5s cubic-bezier(0.25, 1, 0.4, 1) forwards;
368
+ }
369
+
370
+ @keyframes row-in {
371
+ to { opacity: 1; transform: translateY(0); }
372
+ }
373
+
374
+ .ledger-row:last-child { border-bottom: none; }
375
+
376
+ .ledger-row .time { color: var(--ink-55); font-variant-numeric: tabular-nums; }
377
+ .ledger-row .op { color: var(--ink); font-weight: 500; }
378
+ .ledger-row .file { color: var(--ink-80); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
379
+ .ledger-row .saved {
380
+ color: var(--debit);
381
+ text-align: right;
382
+ font-variant-numeric: tabular-nums;
383
+ font-weight: 500;
384
+ }
385
+ .ledger-row .saved::before { content: "+"; margin-right: 1px; }
386
+
387
+ .ledger-totals {
388
+ display: grid;
389
+ grid-template-columns: 1fr 140px;
390
+ padding: 18px 24px;
391
+ border-top: 2px solid var(--ink);
392
+ background: var(--paper);
393
+ font-weight: 500;
394
+ }
395
+ .ledger-totals .lbl {
396
+ font-size: 11px;
397
+ letter-spacing: 0.16em;
398
+ text-transform: uppercase;
399
+ align-self: center;
400
+ }
401
+ .ledger-totals .val {
402
+ font-family: var(--font-mono);
403
+ font-size: 22px;
404
+ text-align: right;
405
+ color: var(--debit);
406
+ font-variant-numeric: tabular-nums;
407
+ letter-spacing: -0.01em;
408
+ }
409
+ .ledger-totals .val::before { content: "+"; }
410
+
411
+ /* ------------ Sections shared ------------ */
412
+ section {
413
+ padding: 112px 0;
414
+ position: relative;
415
+ }
416
+
417
+ section + section {
418
+ border-top: 1px solid var(--ink-10);
419
+ }
420
+
421
+ .section-label {
422
+ font-family: var(--font-mono);
423
+ font-size: 11px;
424
+ letter-spacing: 0.2em;
425
+ text-transform: uppercase;
426
+ color: var(--ink-55);
427
+ margin-bottom: 20px;
428
+ display: flex;
429
+ align-items: center;
430
+ gap: 16px;
431
+ }
432
+
433
+ .section-label .num {
434
+ display: inline-block;
435
+ padding: 3px 8px;
436
+ border: 1px solid var(--ink-30);
437
+ color: var(--ink);
438
+ font-weight: 500;
439
+ }
440
+
441
+ h2.section-head {
442
+ font-family: var(--font-display);
443
+ font-weight: 300;
444
+ font-variation-settings: "opsz" 72, "SOFT" 30;
445
+ font-size: clamp(36px, 5.5vw, 64px);
446
+ line-height: 1.02;
447
+ letter-spacing: -0.025em;
448
+ margin-bottom: 24px;
449
+ max-width: 860px;
450
+ }
451
+
452
+ h2.section-head .italic {
453
+ font-style: italic;
454
+ font-variation-settings: "opsz" 72, "SOFT" 80, "WONK" 1;
455
+ color: var(--debit);
456
+ }
457
+
458
+ .section-sub {
459
+ font-family: var(--font-display);
460
+ font-variation-settings: "opsz" 32;
461
+ font-size: 20px;
462
+ color: var(--ink-55);
463
+ max-width: 620px;
464
+ line-height: 1.45;
465
+ margin-bottom: 64px;
466
+ }
467
+
468
+ /* ------------ Problem section ------------ */
469
+ .problem-columns {
470
+ display: grid;
471
+ grid-template-columns: 1.3fr 1fr;
472
+ gap: 72px;
473
+ align-items: start;
474
+ }
475
+
476
+ @media (max-width: 820px) {
477
+ .problem-columns { grid-template-columns: 1fr; gap: 40px; }
478
+ }
479
+
480
+ .problem-body p {
481
+ font-family: var(--font-display);
482
+ font-variation-settings: "opsz" 18;
483
+ font-size: 19px;
484
+ line-height: 1.6;
485
+ color: var(--ink-80);
486
+ margin-bottom: 20px;
487
+ }
488
+
489
+ .problem-body p .pull {
490
+ color: var(--debit);
491
+ font-style: italic;
492
+ }
493
+
494
+ .receipt {
495
+ border: 1px solid var(--ink);
496
+ background: var(--paper-deep);
497
+ padding: 28px 28px 22px;
498
+ font-family: var(--font-mono);
499
+ font-size: 13px;
500
+ box-shadow: 5px 5px 0 var(--ink);
501
+ position: relative;
502
+ }
503
+
504
+ .receipt-head {
505
+ display: flex;
506
+ justify-content: space-between;
507
+ font-size: 10px;
508
+ letter-spacing: 0.18em;
509
+ text-transform: uppercase;
510
+ color: var(--ink-55);
511
+ padding-bottom: 14px;
512
+ border-bottom: 1px dashed var(--ink-30);
513
+ margin-bottom: 18px;
514
+ }
515
+
516
+ .receipt-line {
517
+ display: flex;
518
+ justify-content: space-between;
519
+ padding: 6px 0;
520
+ color: var(--ink-80);
521
+ font-variant-numeric: tabular-nums;
522
+ }
523
+ .receipt-line .x { color: var(--ink-55); }
524
+
525
+ .receipt-total {
526
+ margin-top: 14px;
527
+ padding-top: 14px;
528
+ border-top: 2px solid var(--ink);
529
+ display: flex;
530
+ justify-content: space-between;
531
+ font-weight: 500;
532
+ }
533
+
534
+ .receipt-total .t { color: var(--debit); font-size: 16px; }
535
+
536
+ .receipt::after {
537
+ content: "";
538
+ position: absolute;
539
+ left: 0; right: 0; bottom: -6px;
540
+ height: 6px;
541
+ background: repeating-linear-gradient(
542
+ -45deg,
543
+ var(--paper) 0 6px,
544
+ transparent 6px 12px
545
+ );
546
+ }
547
+
548
+ /* ------------ How it works / three tools ------------ */
549
+ .tools {
550
+ display: grid;
551
+ grid-template-columns: repeat(3, 1fr);
552
+ gap: 0;
553
+ border: 1px solid var(--ink);
554
+ background: var(--paper-deep);
555
+ }
556
+
557
+ @media (max-width: 960px) {
558
+ .tools { grid-template-columns: 1fr; }
559
+ }
560
+
561
+ .tool {
562
+ padding: 36px 32px;
563
+ border-right: 1px solid var(--ink-10);
564
+ position: relative;
565
+ transition: background 0.25s ease;
566
+ }
567
+ .tool:last-child { border-right: none; }
568
+ @media (max-width: 960px) {
569
+ .tool { border-right: none; border-bottom: 1px solid var(--ink-10); }
570
+ .tool:last-child { border-bottom: none; }
571
+ }
572
+ .tool:hover { background: var(--paper); }
573
+
574
+ .tool-name {
575
+ font-family: var(--font-mono);
576
+ font-size: 17px;
577
+ font-weight: 500;
578
+ letter-spacing: -0.01em;
579
+ margin-bottom: 12px;
580
+ display: flex;
581
+ align-items: center;
582
+ gap: 8px;
583
+ }
584
+
585
+ .tool-name .under {
586
+ color: var(--ink-55);
587
+ }
588
+
589
+ .tool-name .badge {
590
+ font-size: 9px;
591
+ font-weight: 500;
592
+ letter-spacing: 0.12em;
593
+ text-transform: uppercase;
594
+ padding: 3px 7px;
595
+ border: 1px solid var(--debit);
596
+ color: var(--debit);
597
+ margin-left: 6px;
598
+ }
599
+
600
+ .tool-desc {
601
+ font-family: var(--font-display);
602
+ font-variation-settings: "opsz" 20;
603
+ font-size: 17px;
604
+ line-height: 1.5;
605
+ color: var(--ink-80);
606
+ margin-bottom: 24px;
607
+ }
608
+
609
+ .tool-code {
610
+ font-family: var(--font-mono);
611
+ font-size: 12.5px;
612
+ line-height: 1.65;
613
+ background: var(--paper);
614
+ border: 1px solid var(--ink-10);
615
+ padding: 14px 16px;
616
+ color: var(--ink-80);
617
+ overflow-x: auto;
618
+ }
619
+
620
+ .tool-code .kw { color: var(--debit); }
621
+ .tool-code .str { color: var(--credit); }
622
+ .tool-code .com { color: var(--ink-55); font-style: italic; }
623
+
624
+ .tool-stat {
625
+ margin-top: 20px;
626
+ font-family: var(--font-mono);
627
+ font-size: 11px;
628
+ letter-spacing: 0.1em;
629
+ text-transform: uppercase;
630
+ color: var(--ink-55);
631
+ }
632
+ .tool-stat strong {
633
+ color: var(--debit);
634
+ font-weight: 500;
635
+ }
636
+
637
+ /* ------------ Architecture ------------ */
638
+ .arch {
639
+ display: grid;
640
+ grid-template-columns: repeat(3, 1fr);
641
+ gap: 24px;
642
+ align-items: center;
643
+ }
644
+
645
+ @media (max-width: 820px) {
646
+ .arch { grid-template-columns: 1fr; }
647
+ }
648
+
649
+ .arch-node {
650
+ padding: 28px 24px;
651
+ border: 1px solid var(--ink);
652
+ background: var(--paper-deep);
653
+ text-align: center;
654
+ position: relative;
655
+ }
656
+
657
+ .arch-node.core {
658
+ background: var(--ink);
659
+ color: var(--paper);
660
+ box-shadow: 6px 6px 0 var(--debit);
661
+ }
662
+
663
+ .arch-node h3 {
664
+ font-family: var(--font-display);
665
+ font-variation-settings: "opsz" 48, "SOFT" 40;
666
+ font-weight: 500;
667
+ font-size: 24px;
668
+ margin-bottom: 8px;
669
+ letter-spacing: -0.015em;
670
+ }
671
+
672
+ .arch-node p {
673
+ font-family: var(--font-mono);
674
+ font-size: 11px;
675
+ letter-spacing: 0.1em;
676
+ text-transform: uppercase;
677
+ color: var(--ink-55);
678
+ }
679
+
680
+ .arch-node.core p { color: rgba(243,234,219,0.65); }
681
+
682
+ .arch-modules {
683
+ margin-top: 16px;
684
+ font-family: var(--font-mono);
685
+ font-size: 12px;
686
+ text-align: left;
687
+ color: rgba(243,234,219,0.9);
688
+ line-height: 1.9;
689
+ }
690
+ .arch-modules .mod { color: var(--debit); margin-right: 6px; }
691
+
692
+ /* ------------ Comparison table ------------ */
693
+ .compare {
694
+ width: 100%;
695
+ border-collapse: collapse;
696
+ font-size: 15px;
697
+ border: 1px solid var(--ink);
698
+ background: var(--paper-deep);
699
+ }
700
+
701
+ .compare th, .compare td {
702
+ padding: 16px 20px;
703
+ text-align: left;
704
+ border-bottom: 1px solid var(--ink-10);
705
+ vertical-align: top;
706
+ }
707
+
708
+ .compare th {
709
+ font-family: var(--font-mono);
710
+ font-size: 11px;
711
+ font-weight: 500;
712
+ letter-spacing: 0.12em;
713
+ text-transform: uppercase;
714
+ background: var(--paper);
715
+ border-bottom: 1px solid var(--ink);
716
+ }
717
+
718
+ .compare tr:last-child td { border-bottom: none; }
719
+
720
+ .compare .feat {
721
+ font-weight: 500;
722
+ font-family: var(--font-display);
723
+ font-variation-settings: "opsz" 24;
724
+ font-size: 17px;
725
+ }
726
+
727
+ .compare .yes::before { content: "●"; color: var(--credit); margin-right: 8px; }
728
+ .compare .no::before { content: "○"; color: var(--ink-30); margin-right: 8px; }
729
+
730
+ .compare .ours { background: rgba(139, 46, 26, 0.04); }
731
+ @media (prefers-color-scheme: dark) {
732
+ .compare .ours { background: rgba(208, 107, 84, 0.08); }
733
+ }
734
+
735
+ .compare .col-price {
736
+ font-family: var(--font-mono);
737
+ font-size: 14px;
738
+ font-variant-numeric: tabular-nums;
739
+ }
740
+
741
+ /* ------------ Install ------------ */
742
+ .install-block {
743
+ margin-top: 40px;
744
+ background: var(--ink);
745
+ color: var(--paper);
746
+ padding: 32px 36px;
747
+ border: 1px solid var(--ink);
748
+ font-family: var(--font-mono);
749
+ font-size: 15px;
750
+ line-height: 1.9;
751
+ position: relative;
752
+ box-shadow: 8px 8px 0 var(--debit);
753
+ }
754
+
755
+ .install-block .c { color: var(--debit); }
756
+ .install-block .g { color: #98A789; }
757
+ .install-block .p { color: rgba(243,234,219,0.55); }
758
+
759
+ .install-block::before {
760
+ content: "◉ TERMINAL · CLAUDE CODE";
761
+ position: absolute;
762
+ top: -1px; left: -1px;
763
+ font-size: 10px;
764
+ letter-spacing: 0.16em;
765
+ padding: 4px 12px;
766
+ background: var(--debit);
767
+ color: var(--paper);
768
+ transform: translateY(-100%);
769
+ font-weight: 500;
770
+ }
771
+
772
+ .copy-btn {
773
+ position: absolute;
774
+ top: 14px; right: 14px;
775
+ background: transparent;
776
+ color: rgba(243,234,219,0.6);
777
+ border: 1px solid rgba(243,234,219,0.25);
778
+ padding: 5px 10px;
779
+ font-size: 10px;
780
+ letter-spacing: 0.12em;
781
+ text-transform: uppercase;
782
+ cursor: pointer;
783
+ font-family: var(--font-mono);
784
+ transition: all 0.2s;
785
+ }
786
+ .copy-btn:hover {
787
+ background: var(--debit);
788
+ border-color: var(--debit);
789
+ color: var(--paper);
790
+ }
791
+
792
+ .install-note {
793
+ font-family: var(--font-display);
794
+ font-variation-settings: "opsz" 18;
795
+ font-size: 16px;
796
+ color: var(--ink-55);
797
+ margin-top: 24px;
798
+ font-style: italic;
799
+ }
800
+
801
+ /* ------------ Footer ------------ */
802
+ footer {
803
+ padding: 80px 0 56px;
804
+ border-top: 1px solid var(--ink);
805
+ font-family: var(--font-mono);
806
+ font-size: 12px;
807
+ letter-spacing: 0.08em;
808
+ text-transform: uppercase;
809
+ color: var(--ink-55);
810
+ }
811
+
812
+ .footer-inner {
813
+ display: flex;
814
+ justify-content: space-between;
815
+ align-items: baseline;
816
+ flex-wrap: wrap;
817
+ gap: 16px;
818
+ }
819
+
820
+ .footer-inner a { color: var(--ink-55); }
821
+ .footer-inner a:hover { color: var(--debit); }
822
+
823
+ .colophon {
824
+ margin-top: 48px;
825
+ font-family: var(--font-display);
826
+ font-variation-settings: "opsz" 14;
827
+ font-size: 13px;
828
+ text-transform: none;
829
+ letter-spacing: 0;
830
+ color: var(--ink-30);
831
+ font-style: italic;
832
+ line-height: 1.6;
833
+ max-width: 540px;
834
+ }
835
+
836
+ /* ------------ Float margin notes on wide screens ------------ */
837
+ .margin-note {
838
+ display: none;
839
+ }
840
+
841
+ @media (min-width: 1400px) {
842
+ .margin-note {
843
+ display: block;
844
+ position: absolute;
845
+ right: calc((100vw - var(--max-w)) / 2 - 240px);
846
+ width: 200px;
847
+ font-family: var(--font-display);
848
+ font-style: italic;
849
+ font-variation-settings: "opsz" 16;
850
+ font-size: 14px;
851
+ color: var(--ink-55);
852
+ line-height: 1.4;
853
+ padding-left: 18px;
854
+ border-left: 1px solid var(--debit);
855
+ }
856
+ }
857
+
858
+ /* ------------ Page-load reveal ------------ */
859
+ .reveal {
860
+ opacity: 0;
861
+ transform: translateY(14px);
862
+ transition: opacity 0.7s cubic-bezier(0.25, 1, 0.4, 1), transform 0.7s cubic-bezier(0.25, 1, 0.4, 1);
863
+ }
864
+ .reveal.in { opacity: 1; transform: translateY(0); }
865
+
866
+ /* ------------ Utility ------------ */
867
+ .count { font-variant-numeric: tabular-nums; }
868
+ .mono { font-family: var(--font-mono); }
869
+
870
+ /* Selection */
871
+ ::selection { background: var(--ink); color: var(--paper); }
872
+
873
+ /* ------------ Terminal demo ------------ */
874
+ .demo-wrap {
875
+ margin-top: 56px;
876
+ border: 1px solid var(--ink);
877
+ background: #121212;
878
+ color: var(--paper);
879
+ font-family: var(--font-mono);
880
+ font-size: 14px;
881
+ line-height: 1.7;
882
+ box-shadow: 8px 8px 0 var(--debit);
883
+ overflow: hidden;
884
+ }
885
+
886
+ .demo-head {
887
+ padding: 12px 18px;
888
+ border-bottom: 1px solid rgba(243,234,219,0.1);
889
+ display: flex;
890
+ align-items: center;
891
+ gap: 8px;
892
+ font-size: 11px;
893
+ letter-spacing: 0.12em;
894
+ text-transform: uppercase;
895
+ color: rgba(243,234,219,0.55);
896
+ }
897
+ .demo-head .dot {
898
+ width: 11px; height: 11px; border-radius: 50%;
899
+ background: rgba(243,234,219,0.25);
900
+ display: inline-block;
901
+ }
902
+ .demo-head .dot.r { background: #D06B54; }
903
+ .demo-head .dot.y { background: #CFA960; }
904
+ .demo-head .dot.g { background: #8B9474; }
905
+ .demo-head .title { margin-left: 14px; }
906
+
907
+ .demo-body {
908
+ padding: 22px 24px 30px;
909
+ min-height: 320px;
910
+ position: relative;
911
+ }
912
+
913
+ .demo-line {
914
+ white-space: pre;
915
+ opacity: 0;
916
+ animation: demo-type var(--dur, 0.7s) steps(40, end) forwards;
917
+ animation-delay: var(--delay, 0s);
918
+ }
919
+ @keyframes demo-type {
920
+ from { opacity: 0; max-width: 0; overflow: hidden; }
921
+ 1% { opacity: 1; overflow: hidden; max-width: 0; }
922
+ to { opacity: 1; max-width: 100%; overflow: hidden; }
923
+ }
924
+
925
+ .demo-prompt { color: #8B9474; }
926
+ .demo-cmd { color: var(--paper); }
927
+ .demo-tool { color: #D06B54; }
928
+ .demo-dim { color: rgba(243,234,219,0.55); }
929
+ .demo-ok { color: #8B9474; }
930
+ .demo-save { color: #D06B54; font-weight: 500; }
931
+ .demo-caret {
932
+ display: inline-block;
933
+ width: 8px; height: 14px;
934
+ background: var(--paper);
935
+ animation: caret-blink 1s steps(2) infinite;
936
+ vertical-align: -2px;
937
+ margin-left: 2px;
938
+ }
939
+ @keyframes caret-blink { 50% { opacity: 0; } }
940
+
941
+ .demo-total {
942
+ margin-top: 22px;
943
+ padding-top: 18px;
944
+ border-top: 1px dashed rgba(243,234,219,0.2);
945
+ display: flex;
946
+ justify-content: space-between;
947
+ align-items: baseline;
948
+ opacity: 0;
949
+ animation: fade-in 0.6s ease forwards;
950
+ animation-delay: 9s;
951
+ }
952
+ @keyframes fade-in { to { opacity: 1; } }
953
+ .demo-total-l {
954
+ font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
955
+ color: rgba(243,234,219,0.55);
956
+ }
957
+ .demo-total-r {
958
+ font-size: 22px;
959
+ color: #D06B54;
960
+ font-weight: 500;
961
+ font-variant-numeric: tabular-nums;
962
+ }
963
+
964
+ /* ========== Inline hero install ========== */
965
+ .hero-install {
966
+ margin-top: 40px;
967
+ margin-bottom: 48px;
968
+ max-width: 720px;
969
+ border: 1px solid var(--ink);
970
+ background: var(--paper-deep);
971
+ padding: 18px 22px 14px;
972
+ box-shadow: 5px 5px 0 var(--ink);
973
+ position: relative;
974
+ }
975
+ .hero-install-row {
976
+ position: relative;
977
+ margin-bottom: 10px;
978
+ }
979
+ .hero-install-label {
980
+ font-family: var(--font-mono);
981
+ font-size: 9px;
982
+ letter-spacing: 0.2em;
983
+ color: var(--ink-55);
984
+ text-transform: uppercase;
985
+ position: absolute;
986
+ left: -4px;
987
+ top: -3px;
988
+ background: var(--debit);
989
+ color: var(--paper);
990
+ padding: 3px 8px;
991
+ font-weight: 500;
992
+ }
993
+ .hero-install-pre {
994
+ font-family: var(--font-mono);
995
+ font-size: 14px;
996
+ line-height: 1.7;
997
+ padding: 22px 14px 14px;
998
+ background: var(--ink);
999
+ color: var(--paper);
1000
+ border: none;
1001
+ overflow-x: auto;
1002
+ margin: 0;
1003
+ }
1004
+ .hero-install-pre .g { color: #D06B54; }
1005
+ .hero-install-pre .p { color: rgba(243,234,219,0.55); }
1006
+ .hero-install-copy {
1007
+ position: absolute;
1008
+ top: 8px; right: 8px;
1009
+ font-family: var(--font-mono);
1010
+ font-size: 10px;
1011
+ letter-spacing: 0.14em;
1012
+ text-transform: uppercase;
1013
+ padding: 4px 10px;
1014
+ background: transparent;
1015
+ color: rgba(243,234,219,0.55);
1016
+ border: 1px solid rgba(243,234,219,0.25);
1017
+ cursor: pointer;
1018
+ transition: all 0.2s;
1019
+ z-index: 2;
1020
+ }
1021
+ .hero-install-copy:hover {
1022
+ background: var(--debit);
1023
+ border-color: var(--debit);
1024
+ color: var(--paper);
1025
+ }
1026
+ .hero-install-sep {
1027
+ font-family: var(--font-display);
1028
+ font-style: italic;
1029
+ font-variation-settings: "opsz" 16;
1030
+ font-size: 14px;
1031
+ color: var(--ink-55);
1032
+ padding: 6px 2px;
1033
+ }
1034
+ .hero-install-foot {
1035
+ margin-top: 8px;
1036
+ font-family: var(--font-display);
1037
+ font-variation-settings: "opsz" 15;
1038
+ font-size: 13px;
1039
+ color: var(--ink-55);
1040
+ font-style: italic;
1041
+ }
1042
+ .hero-install-foot a {
1043
+ color: var(--debit);
1044
+ border-bottom: 1px solid currentColor;
1045
+ }
1046
+
1047
+ /* ========== Value demo · split-screen before/after ========== */
1048
+ .vd {
1049
+ margin-top: 56px;
1050
+ border: 1px solid var(--ink);
1051
+ background: var(--paper-deep);
1052
+ box-shadow: 8px 8px 0 var(--ink);
1053
+ position: relative;
1054
+ }
1055
+ .vd-head {
1056
+ display: flex;
1057
+ justify-content: space-between;
1058
+ align-items: center;
1059
+ padding: 14px 22px;
1060
+ border-bottom: 1px solid var(--ink);
1061
+ background: var(--paper);
1062
+ font-family: var(--font-mono);
1063
+ font-size: 11px;
1064
+ letter-spacing: 0.18em;
1065
+ text-transform: uppercase;
1066
+ }
1067
+ .vd-title-right { color: var(--ink-55); font-size: 11px; letter-spacing: 0.1em; }
1068
+ .vd-title-right .dot {
1069
+ display: inline-block;
1070
+ width: 6px; height: 6px;
1071
+ border-radius: 50%;
1072
+ background: var(--credit);
1073
+ margin-right: 6px;
1074
+ animation: pulse 2s infinite;
1075
+ }
1076
+ .vd-body {
1077
+ display: grid;
1078
+ grid-template-columns: 1fr 1fr;
1079
+ border-bottom: 1px solid var(--ink);
1080
+ min-height: 380px;
1081
+ }
1082
+ @media (max-width: 720px) {
1083
+ .vd-body { grid-template-columns: 1fr; }
1084
+ .vd-a { border-right: none !important; border-bottom: 1px solid var(--ink); }
1085
+ }
1086
+ .vd-col {
1087
+ position: relative;
1088
+ padding: 18px 22px 60px;
1089
+ font-family: var(--font-mono);
1090
+ font-size: 12.5px;
1091
+ line-height: 1.55;
1092
+ overflow: hidden;
1093
+ }
1094
+ .vd-a { border-right: 1px solid var(--ink); background: rgba(139, 46, 26, 0.04); }
1095
+ .vd-b { background: rgba(79, 91, 63, 0.04); }
1096
+ @media (prefers-color-scheme: dark) {
1097
+ .vd-a { background: rgba(208, 107, 84, 0.06); }
1098
+ .vd-b { background: rgba(152, 167, 137, 0.06); }
1099
+ }
1100
+ .vd-label {
1101
+ font-size: 10px;
1102
+ letter-spacing: 0.22em;
1103
+ font-weight: 500;
1104
+ color: var(--debit);
1105
+ margin-bottom: 14px;
1106
+ }
1107
+ .vd-label-b { color: var(--credit); }
1108
+
1109
+ .vd-stream {
1110
+ font-size: 12px;
1111
+ line-height: 1.55;
1112
+ color: var(--ink-80);
1113
+ max-height: 280px;
1114
+ overflow: hidden;
1115
+ position: relative;
1116
+ white-space: pre-wrap;
1117
+ word-break: break-all;
1118
+ }
1119
+ .vd-stream::after {
1120
+ content: "";
1121
+ position: absolute;
1122
+ bottom: 0; left: 0; right: 0;
1123
+ height: 36px;
1124
+ background: linear-gradient(to bottom, transparent, var(--paper-deep) 85%);
1125
+ pointer-events: none;
1126
+ }
1127
+
1128
+ .vd-line {
1129
+ opacity: 0;
1130
+ transform: translateY(4px);
1131
+ animation: vd-line-in 0.28s cubic-bezier(0.25,1,0.4,1) forwards;
1132
+ }
1133
+ @keyframes vd-line-in { to { opacity: 1; transform: translateY(0); } }
1134
+
1135
+ .vd-line.vd-prompt { color: var(--debit); font-weight: 500; margin-top: 10px; }
1136
+ .vd-line.vd-prompt-b { color: var(--credit); font-weight: 500; margin-top: 10px; }
1137
+ .vd-line.vd-cost-a { color: var(--debit); font-weight: 500; margin-top: 4px; }
1138
+ .vd-line.vd-cost-b { color: var(--credit); font-weight: 500; margin-top: 4px; }
1139
+ .vd-line.vd-dim { color: var(--ink-55); }
1140
+ .vd-line.vd-elide { color: var(--ink-55); font-style: italic; }
1141
+
1142
+ .vd-meter {
1143
+ position: absolute;
1144
+ bottom: 14px; left: 22px; right: 22px;
1145
+ display: flex;
1146
+ align-items: baseline;
1147
+ gap: 8px;
1148
+ padding-top: 10px;
1149
+ border-top: 1px dashed var(--ink-10);
1150
+ }
1151
+ .vd-meter-label {
1152
+ font-size: 10px;
1153
+ letter-spacing: 0.18em;
1154
+ text-transform: uppercase;
1155
+ color: var(--ink-55);
1156
+ }
1157
+ .vd-meter-val {
1158
+ font-size: 22px;
1159
+ font-weight: 500;
1160
+ color: var(--debit);
1161
+ font-variant-numeric: tabular-nums;
1162
+ letter-spacing: -0.01em;
1163
+ }
1164
+ .vd-meter-b .vd-meter-val { color: var(--credit); }
1165
+ .vd-meter-unit {
1166
+ font-size: 11px;
1167
+ color: var(--ink-55);
1168
+ text-transform: uppercase;
1169
+ letter-spacing: 0.1em;
1170
+ }
1171
+
1172
+ .vd-foot {
1173
+ display: flex;
1174
+ justify-content: space-between;
1175
+ align-items: center;
1176
+ padding: 16px 22px;
1177
+ background: var(--paper);
1178
+ font-family: var(--font-mono);
1179
+ }
1180
+ .vd-delta-l {
1181
+ font-size: 11px;
1182
+ letter-spacing: 0.14em;
1183
+ text-transform: uppercase;
1184
+ color: var(--ink-55);
1185
+ }
1186
+ .vd-delta-r { display: flex; align-items: baseline; gap: 10px; }
1187
+ .vd-delta-num {
1188
+ font-family: var(--font-display);
1189
+ font-weight: 300;
1190
+ font-variation-settings: "opsz" 144, "SOFT" 60, "WONK" 1;
1191
+ font-style: italic;
1192
+ font-size: 56px;
1193
+ color: var(--debit);
1194
+ letter-spacing: -0.04em;
1195
+ line-height: 1;
1196
+ display: inline-block;
1197
+ }
1198
+ .vd-delta-tag {
1199
+ font-size: 11px;
1200
+ letter-spacing: 0.2em;
1201
+ text-transform: uppercase;
1202
+ color: var(--ink-55);
1203
+ }
1204
+
1205
+ .vd-savings-cost {
1206
+ display: inline-block;
1207
+ margin-left: 8px;
1208
+ padding: 2px 8px;
1209
+ border: 1px solid var(--credit);
1210
+ color: var(--credit);
1211
+ font-size: 11px;
1212
+ font-family: var(--font-mono);
1213
+ letter-spacing: 0.06em;
1214
+ vertical-align: 2px;
1215
+ transition: opacity 0.4s ease;
1216
+ opacity: 0;
1217
+ }
1218
+ .vd-savings-cost.shown { opacity: 1; }
1219
+
1220
+ /* Ledger-stamp reveal — rotates in like an oxblood inspection mark */
1221
+ .vd-stamp {
1222
+ transform-origin: 70% 50%;
1223
+ transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1),
1224
+ text-shadow 0.45s ease;
1225
+ }
1226
+ .vd-stamp.stamping {
1227
+ animation: vd-stamp-in 0.65s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
1228
+ }
1229
+ @keyframes vd-stamp-in {
1230
+ 0% { transform: scale(0.4) rotate(-22deg); text-shadow: 0 0 0 rgba(139,46,26,0); }
1231
+ 55% { transform: scale(1.18) rotate(4deg); text-shadow: 2px 3px 0 rgba(139,46,26,0.18); }
1232
+ 100% { transform: scale(1) rotate(-3deg); text-shadow: 1px 2px 0 rgba(139,46,26,0.12); }
1233
+ }
1234
+
1235
+ /* Subtle column atmosphere — left "expensive" pulses red, right "calm" stays cool */
1236
+ .vd-a { box-shadow: inset 0 0 0 0 transparent; }
1237
+ .vd-a.heavy {
1238
+ box-shadow: inset 0 -3px 0 0 rgba(139, 46, 26, 0.45);
1239
+ transition: box-shadow 0.6s ease;
1240
+ }
1241
+ .vd-b.calm {
1242
+ box-shadow: inset 0 -3px 0 0 rgba(79, 91, 63, 0.4);
1243
+ transition: box-shadow 0.6s ease;
1244
+ }
1245
+
1246
+ @media (max-width: 560px) {
1247
+ .vd-col { padding: 14px 16px 56px; font-size: 11.5px; }
1248
+ .vd-delta-num { font-size: 40px; }
1249
+ }
1250
+
1251
+ /* ------------ Mobile overflow fixes ------------ */
1252
+ /* Guarantee block-level images/media never push past viewport */
1253
+ img, svg, iframe, video { max-width: 100%; height: auto; }
1254
+
1255
+ /* Preformatted content must contain its own overflow, not the page */
1256
+ pre {
1257
+ max-width: 100%;
1258
+ overflow-x: auto;
1259
+ word-break: break-word;
1260
+ overflow-wrap: anywhere;
1261
+ white-space: pre-wrap;
1262
+ }
1263
+ /* Keep the code-demo preformatted blocks monospace-tight but inside their container */
1264
+ .tool-code, .hero-install-pre, .install-block pre {
1265
+ white-space: pre;
1266
+ overflow-x: auto;
1267
+ word-break: normal;
1268
+ overflow-wrap: normal;
1269
+ max-width: 100%;
1270
+ }
1271
+
1272
+ /* Grid children need min-width:0 so long content (pre, long text) can shrink */
1273
+ .tools > *, .arch > *, .problem-columns > * { min-width: 0; }
1274
+
1275
+ /* Tables that list feature comparisons should scroll horizontally on narrow screens
1276
+ rather than stretch the page. Wrap with a scroll container via overflow on parent. */
1277
+ .compare { display: block; max-width: 100%; overflow-x: auto; }
1278
+ .compare thead, .compare tbody { display: table; width: 100%; table-layout: auto; }
1279
+
1280
+ @media (max-width: 720px) {
1281
+ :root { --gutter: 20px; }
1282
+
1283
+ /* Nav: smaller gap, allow wrap so it doesn't blow out horizontally */
1284
+ .header-inner { flex-wrap: wrap; gap: 12px; }
1285
+ .nav { gap: 18px; flex-wrap: wrap; font-size: 12px; }
1286
+
1287
+ /* Hero meta can stack instead of side-by-side */
1288
+ .hero-meta { flex-wrap: wrap; gap: 8px; }
1289
+
1290
+ /* Terminal demo: let long scripted lines wrap instead of forcing horizontal scroll */
1291
+ .demo-wrap { font-size: 12px; }
1292
+ .demo-body { padding: 18px 16px 24px; }
1293
+ .demo-line { white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; }
1294
+ .demo-total { flex-wrap: wrap; gap: 6px; }
1295
+
1296
+ /* Hero install block: labels, pres, copy buttons must stay inside 100% */
1297
+ .hero-install { padding: 18px 14px 14px; }
1298
+ .hero-install-pre { font-size: 12.5px; padding: 22px 12px 12px; }
1299
+
1300
+ /* Install block: prevent box-shadow from expanding layout past viewport */
1301
+ .install-block { padding: 22px 20px; font-size: 13px; box-shadow: 4px 4px 0 var(--debit); max-width: 100%; }
1302
+ .install-block pre { white-space: pre-wrap; word-break: break-word; overflow-wrap: anywhere; }
1303
+
1304
+ /* Receipt: tighten so box-shadow + padding fit */
1305
+ .receipt { padding: 20px 18px 16px; box-shadow: 4px 4px 0 var(--ink); }
1306
+
1307
+ /* Tools, ledger, value-demo — softer shadows so they don't extend layout */
1308
+ .ledger { box-shadow: 4px 4px 0 var(--ink); font-size: 12px; }
1309
+ .ledger-row { grid-template-columns: 72px 1fr 90px; padding: 10px 16px; column-gap: 10px; }
1310
+ .ledger-row .op { display: none; } /* hide op column to make room on narrow screens */
1311
+ .ledger-head, .ledger-totals { padding: 14px 16px; }
1312
+ .ledger-totals { grid-template-columns: 1fr 100px; }
1313
+
1314
+ .demo-wrap, .vd { box-shadow: 4px 4px 0 var(--debit); }
1315
+
1316
+ /* Section spacing */
1317
+ section { padding: 72px 0; }
1318
+ .hero { padding: 48px 0 64px; }
1319
+ .section-sub { margin-bottom: 40px; }
1320
+
1321
+ /* Hero CTA buttons: full-width feels better on phones */
1322
+ .hero-ctas { gap: 12px; }
1323
+ .btn { padding: 12px 18px; font-size: 12px; }
1324
+ }
1325
+
1326
+ @media (max-width: 480px) {
1327
+ .nav { gap: 14px; font-size: 11px; }
1328
+ .install-block { padding: 20px 16px; font-size: 12.5px; }
1329
+ .install-block pre { font-size: 12.5px; }
1330
+ .hero-install-label { font-size: 8px; letter-spacing: 0.16em; }
1331
+ /* Tighten split-screen value demo headers */
1332
+ .vd-head { padding: 12px 14px; font-size: 10px; letter-spacing: 0.14em; gap: 8px; }
1333
+ .vd-foot { padding: 14px 14px; flex-wrap: wrap; gap: 10px; }
1334
+ .vd-delta-num { font-size: 36px; }
1335
+ }
1336
+ </style>
1337
+ </head>
1338
+ <body>
1339
+ <!-- ================= Header ================= -->
1340
+ <header class="header">
1341
+ <div class="wrap header-inner">
1342
+ <div class="brand"><img class="brand-logo" src="/assets/logo.svg" alt="ashlr" />ashlr</div>
1343
+ <nav class="nav">
1344
+ <a href="#how">How it works</a>
1345
+ <a href="#numbers">Numbers</a>
1346
+ <a href="#vs">Compare</a>
1347
+ <a href="#install">Install</a>
1348
+ <a href="https://github.com/ashlrai/ashlr-plugin">GitHub ↗</a>
1349
+ </nav>
1350
+ </div>
1351
+ </header>
1352
+
1353
+ <!-- ================= Hero ================= -->
1354
+ <section class="hero">
1355
+ <div class="wrap">
1356
+ <div class="hero-meta">
1357
+ <span>v1.36.0 · MIT · Opt-in telemetry</span>
1358
+ <span>Entry № 006 / Apr 15, 2026</span>
1359
+ </div>
1360
+
1361
+ <div class="eyebrow">The Token Ledger</div>
1362
+
1363
+ <h1 class="display">
1364
+ Every tool call,<br />
1365
+ <span class="italic">line-itemized.</span>
1366
+ </h1>
1367
+
1368
+ <p class="lead">
1369
+ Open-source Codex and Claude Code plugin. <strong>40 MCP tools</strong> that don't
1370
+ just truncate — they <em>summarize</em> via your local LLM when output
1371
+ is large. Plus an active genome scribe loop that keeps your project's
1372
+ <span class="mono">.ashlrcode/genome/</span> current as you work.
1373
+ </p>
1374
+
1375
+ <div class="hero-ctas">
1376
+ <a href="#install" class="btn btn-primary">
1377
+ Install now <span class="caret">→</span>
1378
+ </a>
1379
+ <a href="https://github.com/ashlrai/ashlr-plugin" class="btn btn-secondary">
1380
+ Read the source <span class="caret">↗</span>
1381
+ </a>
1382
+ </div>
1383
+
1384
+ <!-- ========== Inline install — the thing you're here for ========== -->
1385
+ <div class="hero-install">
1386
+ <div class="hero-install-row">
1387
+ <span class="hero-install-label">CLAUDE TERMINAL</span>
1388
+ <button class="hero-install-copy" data-copy="hero-curl">Copy</button>
1389
+ <pre id="hero-curl" class="hero-install-pre">curl -fsSL <span class="g">plugin.ashlr.ai/install.sh</span> | bash</pre>
1390
+ </div>
1391
+ <div class="hero-install-sep">Claude Code marketplace commands:</div>
1392
+ <div class="hero-install-row">
1393
+ <span class="hero-install-label">CLAUDE CODE</span>
1394
+ <button class="hero-install-copy" data-copy="hero-slash">Copy</button>
1395
+ <pre id="hero-slash" class="hero-install-pre"><span class="p">/</span>plugin marketplace add <span class="g">ashlrai/ashlr-plugin</span>
1396
+ <span class="p">/</span>plugin install <span class="g">ashlr@ashlr-marketplace</span></pre>
1397
+ </div>
1398
+ <div class="hero-install-foot">
1399
+ Codex users: clone the repo, run <span class="mono">bun install</span>, then <span class="mono">codex plugin marketplace add ashlrai/ashlr-plugin</span>, <span class="mono">codex plugin add ashlr@ashlr-marketplace</span>, and verify with <span class="mono">bun run scripts/cli.ts codex-doctor --json</span>.
1400
+ </div>
1401
+ </div>
1402
+
1403
+ <!-- ============== Split-screen value demo ============== -->
1404
+ <div class="vd" id="vd">
1405
+ <div class="vd-head">
1406
+ <div class="vd-title">Live comparison · one session</div>
1407
+ <div class="vd-title-right"><span class="dot"></span>Looping</div>
1408
+ </div>
1409
+ <div class="vd-body">
1410
+ <!-- Column A: without ashlr -->
1411
+ <div class="vd-col vd-a">
1412
+ <div class="vd-label">WITHOUT ashlr</div>
1413
+ <div class="vd-stream" id="vd-a-stream"></div>
1414
+ <div class="vd-meter">
1415
+ <span class="vd-meter-label">session</span>
1416
+ <span class="vd-meter-val" id="vd-a-total">0</span>
1417
+ <span class="vd-meter-unit">tok</span>
1418
+ </div>
1419
+ </div>
1420
+ <!-- Column B: with ashlr -->
1421
+ <div class="vd-col vd-b">
1422
+ <div class="vd-label vd-label-b">WITH ashlr</div>
1423
+ <div class="vd-stream" id="vd-b-stream"></div>
1424
+ <div class="vd-meter vd-meter-b">
1425
+ <span class="vd-meter-label">session</span>
1426
+ <span class="vd-meter-val" id="vd-b-total">0</span>
1427
+ <span class="vd-meter-unit">tok</span>
1428
+ </div>
1429
+ </div>
1430
+ </div>
1431
+ <div class="vd-foot">
1432
+ <div class="vd-delta-l">
1433
+ same four tool calls · same task ·
1434
+ <span class="vd-savings-cost"><span id="vd-cost">$0.00</span> saved</span>
1435
+ </div>
1436
+ <div class="vd-delta-r" id="vd-delta">
1437
+ <span class="vd-delta-num vd-stamp" id="vd-delta-num">0%</span>
1438
+ <span class="vd-delta-tag">saved</span>
1439
+ </div>
1440
+ </div>
1441
+ </div>
1442
+
1443
+ <p style="margin-top: 28px; font-family: var(--font-display); font-variation-settings: 'opsz' 16; font-size: 16px; line-height: 1.5; color: var(--ink-55); max-width: 680px; font-style: italic;">
1444
+ Animation loops a real sequence of tool calls benchmarked against Codex,
1445
+ Claude Code, and MCP host workflows — see the receipts in
1446
+ <a href="./benchmarks-v2.json" style="color: var(--debit); border-bottom: 1px solid currentColor;">benchmarks-v2.json</a>.
1447
+ </p>
1448
+ </div>
1449
+ </section>
1450
+
1451
+ <!-- ================= Demo ================= -->
1452
+ <section id="demo" style="padding-top: 0; padding-bottom: 96px;">
1453
+ <div class="wrap">
1454
+ <div class="demo-wrap">
1455
+ <div class="demo-head">
1456
+ <span class="dot r"></span><span class="dot y"></span><span class="dot g"></span>
1457
+ <span class="title">claude code · session · ashlr:code active</span>
1458
+ </div>
1459
+ <div class="demo-body">
1460
+ <div class="demo-line" style="--delay: 0.2s; --dur: 0.5s;"><span class="demo-prompt">$</span> <span class="demo-dim">working in</span> ~/code/myapp</div>
1461
+ <div class="demo-line" style="--delay: 0.9s; --dur: 1.1s;"><span class="demo-prompt">&gt;</span> <span class="demo-cmd">explain how auth middleware works in this repo</span></div>
1462
+ <div class="demo-line" style="--delay: 2.3s; --dur: 0.4s;">&nbsp;</div>
1463
+ <div class="demo-line" style="--delay: 2.6s; --dur: 0.5s;"><span class="demo-tool">●</span> <span class="demo-dim">ashlr__read</span> src/middleware/auth.ts <span class="demo-ok">· saved +2,847 tok</span></div>
1464
+ <div class="demo-line" style="--delay: 3.4s; --dur: 0.5s;"><span class="demo-tool">●</span> <span class="demo-dim">ashlr__grep</span> "requireAuth" <span class="demo-ok">· saved +1,204 tok</span></div>
1465
+ <div class="demo-line" style="--delay: 4.2s; --dur: 0.5s;"><span class="demo-tool">●</span> <span class="demo-dim">ashlr__read</span> src/session/jwt.ts <span class="demo-ok">· saved +3,912 tok</span></div>
1466
+ <div class="demo-line" style="--delay: 5.0s; --dur: 0.5s;"><span class="demo-tool">●</span> <span class="demo-dim">ashlr__edit</span> src/middleware/auth.ts:42 <span class="demo-ok">· saved +1,680 tok</span></div>
1467
+ <div class="demo-line" style="--delay: 5.8s; --dur: 0.4s;">&nbsp;</div>
1468
+ <div class="demo-line" style="--delay: 6.1s; --dur: 0.9s;"><span class="demo-dim">auth middleware wraps every route with</span> <span class="demo-cmd">requireAuth()</span> <span class="demo-dim">which verifies a JWT from the</span></div>
1469
+ <div class="demo-line" style="--delay: 7.0s; --dur: 0.9s;"><span class="demo-dim">Authorization header via</span> <span class="demo-cmd">verifyToken()</span> <span class="demo-dim">in src/session/jwt.ts:18. I edited the</span></div>
1470
+ <div class="demo-line" style="--delay: 7.9s; --dur: 0.9s;"><span class="demo-dim">error branch to return 401 with a</span> <span class="demo-cmd">Retry-After</span> <span class="demo-dim">header on expired tokens.</span><span class="demo-caret"></span></div>
1471
+
1472
+ <div class="demo-total">
1473
+ <span class="demo-total-l">Session subtotal · 4 tool calls · ~79% saved</span>
1474
+ <span class="demo-total-r">+9,643 tok</span>
1475
+ </div>
1476
+ </div>
1477
+ </div>
1478
+ <p style="margin-top: 18px; font-family: var(--font-display); font-variation-settings: 'opsz' 16; font-size: 15px; color: var(--ink-55); font-style: italic;">
1479
+ Scripted animation. The numbers are real — see the <a href="#numbers" style="color: var(--debit); border-bottom: 1px solid currentColor;">benchmark</a>.
1480
+ </p>
1481
+ </div>
1482
+ </section>
1483
+
1484
+ <!-- ================= Problem ================= -->
1485
+ <section id="problem">
1486
+ <div class="wrap">
1487
+ <div class="section-label"><span class="num">01</span>The problem</div>
1488
+ <h2 class="section-head">
1489
+ Claude Code is brilliant. And <span class="italic">expensive on long days.</span>
1490
+ </h2>
1491
+ <p class="section-sub">
1492
+ Every Read returns the whole file. Every Grep returns every match. Every Edit
1493
+ ships the full before-and-after. The agent does what you asked — pays the tax
1494
+ one line-item at a time.
1495
+ </p>
1496
+
1497
+ <div class="problem-columns">
1498
+ <div class="problem-body">
1499
+ <p>
1500
+ Running a long session on a mid-sized codebase, a Claude Code Max user
1501
+ pushes past 400K tokens per context window in under an hour of real work.
1502
+ Most of that is <span class="pull">file contents the model has already
1503
+ seen</span>, tool results no one will ever re-read, and prompt scaffolding
1504
+ re-sent on every turn.
1505
+ </p>
1506
+ <p>
1507
+ Commercial plugins like <em>WOZCODE</em> ($20/week) fix this with
1508
+ proprietary MCP tools. <span class="pull">ashlr does it in the open,</span>
1509
+ using the token-efficiency library that already powers the
1510
+ <span class="mono">ashlrcode</span> CLI.
1511
+ </p>
1512
+ <p>
1513
+ Same mechanism. Open source. Free.
1514
+ </p>
1515
+ </div>
1516
+
1517
+ <!-- Right: a receipt showing example token waste -->
1518
+ <div class="receipt">
1519
+ <div class="receipt-head">
1520
+ <span>Receipt № 7411</span>
1521
+ <span>1 hour session</span>
1522
+ </div>
1523
+ <div class="receipt-line"><span class="x">12 × Read (avg 4.2K)</span><span>50,400</span></div>
1524
+ <div class="receipt-line"><span class="x">8 × Grep (raw rg)</span><span>18,200</span></div>
1525
+ <div class="receipt-line"><span class="x">4 × Edit (full before+after)</span><span>12,800</span></div>
1526
+ <div class="receipt-line"><span class="x">System prompt × 24 turns</span><span>192,000</span></div>
1527
+ <div class="receipt-line"><span class="x">Re-sent context × 24</span><span>141,600</span></div>
1528
+ <div class="receipt-total">
1529
+ <span>Session total</span>
1530
+ <span class="t">415,000 tok</span>
1531
+ </div>
1532
+ <div class="receipt-line" style="margin-top: 12px; padding-top: 8px; border-top: 1px dashed var(--ink-30);">
1533
+ <span class="x">With ashlr (typical −46%)</span>
1534
+ <span style="color: var(--credit);">224,100</span>
1535
+ </div>
1536
+ </div>
1537
+ </div>
1538
+ </div>
1539
+ </section>
1540
+
1541
+ <!-- ================= How it works (three tools) ================= -->
1542
+ <section id="how">
1543
+ <div class="wrap">
1544
+ <div class="section-label"><span class="num">02</span>How it works</div>
1545
+ <h2 class="section-head">
1546
+ Nine tools. <span class="italic">One contract.</span>
1547
+ </h2>
1548
+ <p class="section-sub">
1549
+ ashlr's MCP server exposes drop-in replacements for the file primitives Claude
1550
+ Code hits thousands of times per session. Each one composes primitives from
1551
+ <span class="mono">@ashlr/core-efficiency</span> — the shared library that also
1552
+ powers Mason's standalone <span class="mono">ashlrcode</span> CLI.
1553
+ </p>
1554
+
1555
+ <div class="tools">
1556
+ <!-- Tool 1 -->
1557
+ <div class="tool">
1558
+ <div class="tool-name"><span class="under">ashlr__</span>read<span class="badge">−46% avg</span></div>
1559
+ <p class="tool-desc">
1560
+ Reads a file, then runs <span class="mono">snipCompact</span> on
1561
+ tool-results larger than 2KB. Keeps head + tail; elides the middle.
1562
+ The agent almost never needs the middle.
1563
+ </p>
1564
+ <pre class="tool-code"><span class="com">// servers/efficiency-server.ts</span>
1565
+ <span class="kw">const</span> compact = snipCompact(msgs);
1566
+ <span class="kw">const</span> out = compact[0]
1567
+ .content[0].content;
1568
+ <span class="kw">await</span> recordSaving(
1569
+ raw.length, out.length
1570
+ );</pre>
1571
+ <div class="tool-stat">saved per call · <strong>1,800 – 8,400 tok</strong></div>
1572
+ </div>
1573
+
1574
+ <!-- Tool 2 -->
1575
+ <div class="tool">
1576
+ <div class="tool-name"><span class="under">ashlr__</span>grep<span class="badge">−62% with genome</span></div>
1577
+ <p class="tool-desc">
1578
+ If the project has a <span class="mono">.ashlrcode/genome/</span>,
1579
+ returns the top-scoring sections via TF-IDF or Ollama semantic search.
1580
+ Falls back to ripgrep otherwise — still slimmer than Grep's default.
1581
+ </p>
1582
+ <pre class="tool-code"><span class="kw">if</span> (genomeExists(cwd)) {
1583
+ <span class="kw">const</span> s = <span class="kw">await</span> retrieveSectionsV2(
1584
+ cwd, pattern, <span class="str">4000</span>
1585
+ );
1586
+ <span class="kw">return</span> formatGenomeForPrompt(s);
1587
+ }</pre>
1588
+ <div class="tool-stat">saved per call · <strong>3,000 – 12,000 tok</strong></div>
1589
+ </div>
1590
+
1591
+ <!-- Tool 3 -->
1592
+ <div class="tool">
1593
+ <div class="tool-name"><span class="under">ashlr__</span>edit<span class="badge">−31% avg</span></div>
1594
+ <p class="tool-desc">
1595
+ Sends a diff summary instead of full before-and-after contents. The
1596
+ agent already has the file in context — no need to ship it twice.
1597
+ </p>
1598
+ <pre class="tool-code"><span class="kw">const</span> summary = <span class="str">`[edit] ${path}
1599
+ - removed (${tokS} tok): ${...}
1600
+ + added (${tokR} tok): ${...}`</span>;
1601
+ <span class="kw">return</span> summary;</pre>
1602
+ <div class="tool-stat">saved per call · <strong>200 – 1,800 tok</strong></div>
1603
+ </div>
1604
+ </div>
1605
+ </div>
1606
+ </section>
1607
+
1608
+ <!-- ================= Numbers ================= -->
1609
+ <section id="numbers">
1610
+ <div class="wrap">
1611
+ <div class="section-label"><span class="num">03</span>The numbers</div>
1612
+ <h2 class="section-head">
1613
+ Benchmarked against the standard toolset. <span class="italic">Nothing theoretical.</span>
1614
+ </h2>
1615
+ <p class="section-sub">
1616
+ Run against TypeScript, Python, and Rust reference repos. Every row is
1617
+ reproducible — commands below. Raw data:
1618
+ <a style="color: var(--debit); border-bottom: 1px solid currentColor;" href="./benchmarks-v2.json">benchmarks-v2.json</a>.
1619
+ </p>
1620
+
1621
+ <table class="compare" style="margin-top: 40px;">
1622
+ <thead>
1623
+ <tr>
1624
+ <th style="width: 36%;">File · size</th>
1625
+ <th>Raw tokens</th>
1626
+ <th>ashlr tokens</th>
1627
+ <th>Saved</th>
1628
+ </tr>
1629
+ </thead>
1630
+ <tbody>
1631
+ <tr>
1632
+ <td class="feat">src/compression/priority.ts · 0.5 KB</td>
1633
+ <td class="mono">137</td>
1634
+ <td class="mono">137</td>
1635
+ <td class="mono" style="color: var(--ink-55);"> 0.0%</td>
1636
+ </tr>
1637
+ <tr>
1638
+ <td class="feat">src/tokens/index.ts · 1.5 KB</td>
1639
+ <td class="mono">379</td>
1640
+ <td class="mono">379</td>
1641
+ <td class="mono" style="color: var(--ink-55);"> 0.0%</td>
1642
+ </tr>
1643
+ <tr>
1644
+ <td class="feat">src/compression/context.ts · 5.9 KB</td>
1645
+ <td class="mono">1,517</td>
1646
+ <td class="mono">406</td>
1647
+ <td class="mono" style="color: var(--debit); font-weight: 500;">−73.2%</td>
1648
+ </tr>
1649
+ <tr>
1650
+ <td class="feat">src/genome/manifest.ts · 7.0 KB</td>
1651
+ <td class="mono">1,807</td>
1652
+ <td class="mono">406</td>
1653
+ <td class="mono" style="color: var(--debit); font-weight: 500;">−77.5%</td>
1654
+ </tr>
1655
+ <tr>
1656
+ <td class="feat">src/genome/retriever.ts · 7.8 KB</td>
1657
+ <td class="mono">2,001</td>
1658
+ <td class="mono">406</td>
1659
+ <td class="mono" style="color: var(--debit); font-weight: 500;">−79.7%</td>
1660
+ </tr>
1661
+ <tr>
1662
+ <td class="feat">src/genome/generations.ts · 11.2 KB</td>
1663
+ <td class="mono">2,878</td>
1664
+ <td class="mono">406</td>
1665
+ <td class="mono" style="color: var(--debit); font-weight: 500;">−85.9%</td>
1666
+ </tr>
1667
+ </tbody>
1668
+ </table>
1669
+
1670
+ <p style="margin-top: 28px; font-family: var(--font-display); font-variation-settings: 'opsz' 16; font-size: 16px; line-height: 1.5; color: var(--ink-55); max-width: 720px;">
1671
+ <em>Files under 2 KB see 0% savings — snipCompact only fires on tool-results
1672
+ over 2,000 chars. That's by design: there's nothing to trim on a small file.
1673
+ The story is: <strong style="color: var(--debit);">mean −57% across reference repos.</strong> Agents read big files; big files see big savings.</em>
1674
+ </p>
1675
+
1676
+ <p style="margin-top: 20px; font-family: var(--font-mono); font-size: 13px; color: var(--ink-55);">
1677
+ Reproduce: <span style="color: var(--ink);">bun run scripts/run-benchmark.ts --repo &lt;your-repo&gt;</span>
1678
+ </p>
1679
+ </div>
1680
+ </section>
1681
+
1682
+ <!-- ================= VS WOZCODE ================= -->
1683
+ <section id="vs">
1684
+ <div class="wrap">
1685
+ <div class="section-label"><span class="num">04</span>ashlr vs WOZCODE</div>
1686
+ <h2 class="section-head">
1687
+ One is $20 a week. <span class="italic">One is open-source.</span>
1688
+ </h2>
1689
+ <p class="section-sub">
1690
+ WOZCODE pioneered the Claude-Code-plugin-as-token-efficiency-layer
1691
+ pattern. ashlr rebuilds it in the open, using the token-efficiency
1692
+ library that already ships in the <span class="mono">ashlrcode</span> CLI.
1693
+ </p>
1694
+
1695
+ <table class="compare">
1696
+ <thead>
1697
+ <tr>
1698
+ <th></th>
1699
+ <th>Built-in Claude Code</th>
1700
+ <th>WOZCODE</th>
1701
+ <th class="ours">ashlr</th>
1702
+ </tr>
1703
+ </thead>
1704
+ <tbody>
1705
+ <tr>
1706
+ <td class="feat">License</td>
1707
+ <td>Anthropic proprietary</td>
1708
+ <td>Proprietary (Woz, Inc.)</td>
1709
+ <td class="ours"><strong>MIT · open source</strong></td>
1710
+ </tr>
1711
+ <tr>
1712
+ <td class="feat">Price</td>
1713
+ <td class="col-price">—</td>
1714
+ <td class="col-price">$20 / week</td>
1715
+ <td class="ours col-price"><strong>Free</strong></td>
1716
+ </tr>
1717
+ <tr>
1718
+ <td class="feat">Account required</td>
1719
+ <td><span class="no"></span>No</td>
1720
+ <td><span class="yes"></span>Woz account, OAuth login</td>
1721
+ <td class="ours"><span class="no"></span>None</td>
1722
+ </tr>
1723
+ <tr>
1724
+ <td class="feat">Token-efficient Read</td>
1725
+ <td><span class="no"></span></td>
1726
+ <td><span class="yes"></span></td>
1727
+ <td class="ours"><span class="yes"></span>snipCompact</td>
1728
+ </tr>
1729
+ <tr>
1730
+ <td class="feat">Genome-aware Grep</td>
1731
+ <td><span class="no"></span></td>
1732
+ <td><span class="no"></span>Proprietary RAG</td>
1733
+ <td class="ours"><span class="yes"></span>TF-IDF + optional Ollama</td>
1734
+ </tr>
1735
+ <tr>
1736
+ <td class="feat">Diff-format Edit</td>
1737
+ <td><span class="no"></span></td>
1738
+ <td><span class="yes"></span></td>
1739
+ <td class="ours"><span class="yes"></span></td>
1740
+ </tr>
1741
+ <tr>
1742
+ <td class="feat">Savings tracker</td>
1743
+ <td><span class="no"></span></td>
1744
+ <td><span class="yes"></span><span class="mono"> /woz-savings</span></td>
1745
+ <td class="ours"><span class="yes"></span><span class="mono"> /ashlr-savings</span></td>
1746
+ </tr>
1747
+ <tr>
1748
+ <td class="feat">Tri-agent delegation</td>
1749
+ <td><span class="no"></span></td>
1750
+ <td><span class="yes"></span> code/explore/plan</td>
1751
+ <td class="ours"><span class="yes"></span> code/explore/plan</td>
1752
+ </tr>
1753
+ <tr>
1754
+ <td class="feat">Auditable source</td>
1755
+ <td><span class="no"></span></td>
1756
+ <td><span class="no"></span> Installer only; tools compiled</td>
1757
+ <td class="ours"><span class="yes"></span> Every line, in the repo</td>
1758
+ </tr>
1759
+ <tr>
1760
+ <td class="feat">Shared efficiency library</td>
1761
+ <td><span class="no"></span></td>
1762
+ <td><span class="no"></span></td>
1763
+ <td class="ours"><span class="yes"></span><span class="mono"> @ashlr/core-efficiency</span></td>
1764
+ </tr>
1765
+ </tbody>
1766
+ </table>
1767
+
1768
+ <p style="margin-top: 28px; font-family: var(--font-display); font-style: italic; font-size: 15px; color: var(--ink-55); max-width: 600px;">
1769
+ ashlr isn't trying to be better than WOZCODE on every axis. It's trying to
1770
+ exist, honestly, for the developers who want the mechanism without the
1771
+ subscription.
1772
+ </p>
1773
+ </div>
1774
+ </section>
1775
+
1776
+ <!-- ================= Architecture ================= -->
1777
+ <section id="arch">
1778
+ <div class="wrap">
1779
+ <div class="section-label"><span class="num">05</span>The stack</div>
1780
+ <h2 class="section-head">
1781
+ One library. <span class="italic">Two consumers.</span>
1782
+ </h2>
1783
+ <p class="section-sub">
1784
+ The efficiency tech isn't locked inside the plugin. It lives in
1785
+ <span class="mono">@ashlr/core-efficiency</span> — a separate package
1786
+ consumed by both the Claude Code plugin and the standalone
1787
+ <span class="mono">ashlrcode</span> CLI. Evolution happens in one place.
1788
+ </p>
1789
+
1790
+ <div class="arch">
1791
+ <div class="arch-node">
1792
+ <h3>ashlrcode</h3>
1793
+ <p>Multi-provider AI<br/>coding agent CLI</p>
1794
+ </div>
1795
+
1796
+ <div class="arch-node core">
1797
+ <h3>@ashlr/core-efficiency</h3>
1798
+ <p>Token-efficiency library</p>
1799
+ <div class="arch-modules">
1800
+ <div><span class="mod">/genome</span> 2,342 LOC</div>
1801
+ <div><span class="mod">/compression</span> ~220 LOC</div>
1802
+ <div><span class="mod">/budget</span> ~50 LOC</div>
1803
+ <div><span class="mod">/tokens</span> ~50 LOC</div>
1804
+ </div>
1805
+ </div>
1806
+
1807
+ <div class="arch-node">
1808
+ <h3>ashlr-plugin</h3>
1809
+ <p>Codex + Claude Code plugin<br/>MCP server + skills</p>
1810
+ </div>
1811
+ </div>
1812
+ </div>
1813
+ </section>
1814
+
1815
+ <!-- ================= Install ================= -->
1816
+ <section id="install">
1817
+ <div class="wrap">
1818
+ <div class="section-label"><span class="num">06</span>Install</div>
1819
+ <h2 class="section-head">
1820
+ Two ways. <span class="italic">Pick what feels right.</span>
1821
+ </h2>
1822
+ <p class="section-sub">
1823
+ Prerequisites: <a href="https://bun.sh" style="color: var(--debit); border-bottom: 1px solid currentColor;">bun</a> ≥ 1.3 and
1824
+ Claude Code. Nothing else. No account, no login, no API key.
1825
+ </p>
1826
+
1827
+ <!-- Way 1: one-liner -->
1828
+ <div style="margin-top: 48px; display: flex; gap: 28px; align-items: baseline;">
1829
+ <div style="width: 48px; height: 48px; border: 1px solid var(--ink); display: flex; align-items: center; justify-content: center; font-family: var(--font-display); font-size: 24px; font-variation-settings: 'opsz' 48; flex-shrink: 0;">A</div>
1830
+ <div>
1831
+ <h3 style="font-family: var(--font-display); font-variation-settings: 'opsz' 48, 'SOFT' 40; font-weight: 500; font-size: 28px; letter-spacing: -0.02em; margin-bottom: 6px;">One-liner</h3>
1832
+ <p style="font-family: var(--font-display); font-variation-settings: 'opsz' 18; font-size: 17px; color: var(--ink-55); margin-bottom: 24px;">Easiest. Pre-clones the plugin + runs <span class="mono">bun install</span> for you.</p>
1833
+ </div>
1834
+ </div>
1835
+
1836
+ <div class="install-block" style="margin-top: 0;">
1837
+ <button class="copy-btn" data-copy="curl">Copy</button>
1838
+ <pre id="install-oneliner">curl -fsSL <span class="g">plugin.ashlr.ai/install.sh</span> | bash</pre>
1839
+ </div>
1840
+
1841
+ <p class="install-note" style="margin-top: 16px;">
1842
+ Then inside Claude Code, run <span class="mono">/plugin marketplace add ashlrai/ashlr-plugin</span> and
1843
+ <span class="mono">/plugin install ashlr@ashlr-marketplace</span>. Restart.
1844
+ </p>
1845
+
1846
+ <!-- Way 2: ask Claude Code -->
1847
+ <div style="margin-top: 80px; display: flex; gap: 28px; align-items: baseline;">
1848
+ <div style="width: 48px; height: 48px; border: 1px solid var(--ink); display: flex; align-items: center; justify-content: center; font-family: var(--font-display); font-size: 24px; font-variation-settings: 'opsz' 48; flex-shrink: 0;">B</div>
1849
+ <div>
1850
+ <h3 style="font-family: var(--font-display); font-variation-settings: 'opsz' 48, 'SOFT' 40; font-weight: 500; font-size: 28px; letter-spacing: -0.02em; margin-bottom: 6px;">Ask Claude Code</h3>
1851
+ <p style="font-family: var(--font-display); font-variation-settings: 'opsz' 18; font-size: 17px; color: var(--ink-55); margin-bottom: 24px;">Paste this into any Claude Code session. It runs all the steps for you.</p>
1852
+ </div>
1853
+ </div>
1854
+
1855
+ <div class="install-block" style="margin-top: 0;">
1856
+ <button class="copy-btn" data-copy="prompt">Copy</button>
1857
+ <pre id="install-prompt-text">Install the ashlr-plugin for me:
1858
+
1859
+ <span class="p">#</span> <span class="p">1. Run in a terminal</span>
1860
+ curl -fsSL https://plugin.ashlr.ai/install.sh | bash
1861
+
1862
+ <span class="p"># 2. Then inside this session, run these two slash commands:</span>
1863
+ /plugin marketplace add <span class="g">ashlrai/ashlr-plugin</span>
1864
+ /plugin install <span class="g">ashlr@ashlr-marketplace</span>
1865
+
1866
+ <span class="p"># 3. Restart this Claude Code session, then verify:</span>
1867
+ /ashlr-status</pre>
1868
+ </div>
1869
+
1870
+ <p class="install-note" style="margin-top: 16px;">
1871
+ Claude Code will execute the shell, then the slash commands, then tell you to restart.
1872
+ </p>
1873
+
1874
+ <!-- Verify -->
1875
+ <div style="margin-top: 80px; display: flex; gap: 28px; align-items: baseline;">
1876
+ <div style="width: 48px; height: 48px; border: 1px solid var(--credit); color: var(--credit); display: flex; align-items: center; justify-content: center; font-family: var(--font-display); font-size: 24px; font-variation-settings: 'opsz' 48; flex-shrink: 0;">✓</div>
1877
+ <div>
1878
+ <h3 style="font-family: var(--font-display); font-variation-settings: 'opsz' 48, 'SOFT' 40; font-weight: 500; font-size: 28px; letter-spacing: -0.02em; margin-bottom: 6px;">Verify it's working</h3>
1879
+ <p style="font-family: var(--font-display); font-variation-settings: 'opsz' 18; font-size: 17px; color: var(--ink-55); margin-bottom: 24px;">Look for <span class="mono">ashlr:code</span> on the right side of the input. Or ask:</p>
1880
+ </div>
1881
+ </div>
1882
+
1883
+ <div class="install-block" style="margin-top: 0; box-shadow: 8px 8px 0 var(--credit);">
1884
+ <button class="copy-btn" data-copy="verify">Copy</button>
1885
+ <pre id="install-verify">/ashlr-status <span class="p"># plugin + MCP server health</span>
1886
+ /ashlr-savings <span class="p"># running token-saving total</span>
1887
+ /ashlr-benchmark <span class="p"># benchmark this project's file sizes</span></pre>
1888
+ </div>
1889
+
1890
+ <p class="install-note" style="margin-top: 40px;">
1891
+ Telemetry off by default. Everything runs locally. Dependencies auto-install on first session
1892
+ via the SessionStart hook, so you can skip the manual <span class="mono">bun install</span>
1893
+ step entirely.
1894
+ </p>
1895
+ </div>
1896
+ </section>
1897
+
1898
+ <!-- ================= Footer ================= -->
1899
+ <footer>
1900
+ <div class="wrap footer-inner">
1901
+ <div>
1902
+ <span class="brand" style="font-size: 18px;"><img class="brand-logo" src="/assets/logo.svg" alt="ashlr" style="width: 22px; height: 22px;" />ashlr</span>
1903
+ &nbsp;· v1.36.0 · MIT
1904
+ </div>
1905
+ <div>
1906
+ <a href="https://github.com/ashlrai/ashlr-plugin">GitHub</a>
1907
+ &nbsp;·&nbsp;
1908
+ <a href="https://github.com/ashlrai/ashlrcode">ashlrcode CLI</a>
1909
+ &nbsp;·&nbsp;
1910
+ <a href="#install">Install</a>
1911
+ </div>
1912
+ </div>
1913
+ <div class="wrap">
1914
+ <p class="colophon">
1915
+ Set in Fraunces, IBM Plex Sans, and JetBrains Mono. Ledger aesthetic after a
1916
+ hand-kept engineering notebook. Numbers in this ledger are real and
1917
+ reproducible — see <span class="mono">scripts/run-benchmark.ts</span>. Built in one
1918
+ session using the local-ai-stack Mason set up earlier that evening. © 2026.
1919
+ </p>
1920
+ </div>
1921
+ </footer>
1922
+
1923
+ <script>
1924
+ // ---------- Split-screen value demo (v1.36.0) ----------
1925
+ // Four tool calls executed against identical tasks. Left column = built-in
1926
+ // Claude Code tools (raw output). Right column = ashlr equivalents
1927
+ // (compressed + summarized). Numbers track median savings from
1928
+ // benchmarks-v2.json. The animation loops; every cycle ends with a Fraunces-
1929
+ // italic stamp showing % saved + a $ figure (sonnet-4.5 input pricing).
1930
+ const vdSteps = [
1931
+ {
1932
+ delay: 350,
1933
+ promptA: "> Read src/agent/loop.ts",
1934
+ promptB: "> ashlr__read src/agent/loop.ts",
1935
+ bodyA: [
1936
+ 'import { Message } from "../providers/types";',
1937
+ "export async function runAgentLoop(",
1938
+ " messages: Message[],",
1939
+ " config: LoopConfig,",
1940
+ ") {",
1941
+ " const router = new ProviderRouter(config);",
1942
+ " for (let turn = 0; turn < config.maxTurns; turn++) {",
1943
+ " const stream = router.stream({",
1944
+ " systemPrompt: await buildPrompt(messages),",
1945
+ " messages, tools, maxTokens: config.maxTokens,",
1946
+ " });",
1947
+ " for await (const event of stream) {",
1948
+ " // handle text, tool_use, usage...",
1949
+ " if (event.type === 'tool_call_end') {",
1950
+ " const result = await executeTools(event.toolCall);",
1951
+ " messages = appendResult(messages, result);",
1952
+ " }",
1953
+ " }",
1954
+ " if (needsCompaction(messages, ...)) {",
1955
+ " messages = await autoCompact(messages, router);",
1956
+ " }",
1957
+ " }",
1958
+ "}",
1959
+ "// ... 382 more lines omitted in this render ...",
1960
+ ],
1961
+ bodyB: [
1962
+ 'import { Message } from "../providers/types";',
1963
+ "export async function runAgentLoop(",
1964
+ " messages: Message[],",
1965
+ " config: LoopConfig,",
1966
+ ") {",
1967
+ "",
1968
+ "[... 12,847 chars elided ...]",
1969
+ "",
1970
+ " if (needsCompaction(messages, ...)) {",
1971
+ " messages = await autoCompact(messages, router);",
1972
+ " }",
1973
+ " }",
1974
+ "}",
1975
+ ],
1976
+ costA: 11248,
1977
+ costB: 2144,
1978
+ },
1979
+ {
1980
+ delay: 2000,
1981
+ promptA: '> Grep "autopilot"',
1982
+ promptB: '> ashlr__grep "autopilot"',
1983
+ bodyA: [
1984
+ "src/agent/autopilot-loop.ts:12:import { AutopilotQueue } ...",
1985
+ "src/agent/autopilot-loop.ts:27:export async function runAutopilot(",
1986
+ "src/agent/autopilot-loop.ts:84: while (autopilot.active) {",
1987
+ "src/autopilot/queue.ts:3:export class AutopilotQueue {",
1988
+ "src/autopilot/queue.ts:19: enqueue(task: AutopilotTask) {",
1989
+ "src/autopilot/queue.ts:41: async drainAutopilot() {",
1990
+ "src/commands/autopilot.ts:6:export const autopilotCommand = ...",
1991
+ "src/commands/autopilot.ts:28: if (!isAutopilotSafe(ctx)) ...",
1992
+ "src/__tests__/autopilot-loop.test.ts:4:describe('autopilot', ...",
1993
+ "src/__tests__/autopilot-loop.test.ts:17:it('drains queue fully', ...",
1994
+ "// ... 37 more matches omitted ...",
1995
+ ],
1996
+ bodyB: [
1997
+ "[ashlr__grep] 3 genome sections retrieved",
1998
+ "",
1999
+ "strategies/autopilot-execution.md (priority 25)",
2000
+ " Autopilot drains queue in priority order, bailing",
2001
+ " when any precondition fails. See queue.ts:41.",
2002
+ "",
2003
+ "knowledge/autopilot-safety.md (priority 25)",
2004
+ " Destructive ops require isAutopilotSafe(ctx) == true.",
2005
+ ],
2006
+ costA: 3210,
2007
+ costB: 512,
2008
+ },
2009
+ {
2010
+ delay: 1500,
2011
+ promptA: "> Edit src/providers/anthropic.ts:84",
2012
+ promptB: "> ashlr__edit src/providers/anthropic.ts",
2013
+ bodyA: [
2014
+ "--- before (full file) ---",
2015
+ "export class AnthropicProvider implements Provider {",
2016
+ " name = 'anthropic';",
2017
+ " config: ProviderConfig;",
2018
+ " constructor(config: ProviderConfig) {",
2019
+ " this.config = config;",
2020
+ " }",
2021
+ " async *stream(req: ProviderRequest) {",
2022
+ " // 140 lines of streaming impl...",
2023
+ " }",
2024
+ "}",
2025
+ "--- after (full file) ---",
2026
+ "export class AnthropicProvider implements Provider {",
2027
+ " name = 'anthropic';",
2028
+ " config: ProviderConfig;",
2029
+ " constructor(config: ProviderConfig) {",
2030
+ " this.config = config;",
2031
+ " this.headers = buildRetryHeaders(config);",
2032
+ " }",
2033
+ " // ... same 140 lines re-sent ...",
2034
+ "}",
2035
+ ],
2036
+ bodyB: [
2037
+ "[ashlr__edit] src/providers/anthropic.ts · 1 of 1 hunks applied",
2038
+ " - removed (18 tok): this.config = config;",
2039
+ " + added (34 tok): this.config = config;",
2040
+ " this.headers = buildRetryHeaders(...);",
2041
+ ],
2042
+ costA: 1340,
2043
+ costB: 412,
2044
+ },
2045
+ {
2046
+ delay: 1500,
2047
+ promptA: "> Bash bun test --bail",
2048
+ promptB: "> ashlr__bash bun test --bail",
2049
+ bodyA: [
2050
+ "$ bun test --bail",
2051
+ "bun test v1.3.10",
2052
+ "",
2053
+ "src/__tests__/loop.test.ts:",
2054
+ " 12 pass · 0 fail",
2055
+ "src/__tests__/router.test.ts:",
2056
+ " 47 pass · 0 fail",
2057
+ "src/__tests__/genome.test.ts:",
2058
+ " 38 pass · 0 fail",
2059
+ "src/__tests__/scribe.test.ts:",
2060
+ " 21 pass · 0 fail",
2061
+ "src/__tests__/coordinator.test.ts:",
2062
+ " 1 fail",
2063
+ " ✗ verification round-trip > merges proposals (231ms)",
2064
+ " expected: \"applied: 3\"",
2065
+ " received: \"applied: 2\"",
2066
+ " at coordinator.test.ts:184:14",
2067
+ "[... 1,247 more lines of pass output ...]",
2068
+ "Ran 187 tests across 18 files. [11.04s]",
2069
+ "186 pass · 1 fail",
2070
+ ],
2071
+ bodyB: [
2072
+ "[ashlr__bash · summarized] bun test --bail · exit 1 · 11.04s",
2073
+ " 186 pass · 1 fail",
2074
+ " ✗ coordinator.test.ts:184 verification round-trip",
2075
+ " expected 'applied: 3' · received 'applied: 2'",
2076
+ " [LLM summary preserves the failing assertion verbatim]",
2077
+ ],
2078
+ costA: 4280,
2079
+ costB: 320,
2080
+ },
2081
+ ];
2082
+
2083
+ const streamA = document.getElementById("vd-a-stream");
2084
+ const streamB = document.getElementById("vd-b-stream");
2085
+ const totA = document.getElementById("vd-a-total");
2086
+ const totB = document.getElementById("vd-b-total");
2087
+ const deltaNum = document.getElementById("vd-delta-num");
2088
+ const costEl = document.getElementById("vd-cost");
2089
+ const colA = document.querySelector(".vd-a");
2090
+ const colB = document.querySelector(".vd-b");
2091
+ const costBadge = document.querySelector(".vd-savings-cost");
2092
+
2093
+ // Sonnet 4.5 input pricing — $3.00 per million tokens.
2094
+ function tokensToDollars(tok) {
2095
+ const n = (tok * 3) / 1_000_000;
2096
+ return n < 0.01 ? `$${n.toFixed(4)}` : `$${n.toFixed(2)}`;
2097
+ }
2098
+
2099
+ function appendLine(el, text, cls = "vd-dim", delay = 0) {
2100
+ const d = document.createElement("div");
2101
+ d.className = "vd-line " + cls;
2102
+ d.textContent = text;
2103
+ d.style.animationDelay = delay + "ms";
2104
+ el.appendChild(d);
2105
+ return d;
2106
+ }
2107
+
2108
+ function clearStream(el) {
2109
+ el.innerHTML = "";
2110
+ }
2111
+
2112
+ function animateNum(el, from, to, durationMs, color) {
2113
+ const startT = performance.now();
2114
+ if (color) el.style.color = color;
2115
+ function step(t) {
2116
+ const p = Math.min(1, (t - startT) / durationMs);
2117
+ const eased = 1 - Math.pow(1 - p, 3);
2118
+ const v = Math.floor(from + (to - from) * eased);
2119
+ el.textContent = v.toLocaleString();
2120
+ if (p < 1) requestAnimationFrame(step);
2121
+ }
2122
+ requestAnimationFrame(step);
2123
+ }
2124
+
2125
+ async function playStep(step) {
2126
+ appendLine(streamA, step.promptA, "vd-prompt");
2127
+ appendLine(streamB, step.promptB, "vd-prompt-b");
2128
+ await sleep(300);
2129
+ let d = 0;
2130
+ for (const line of step.bodyA) {
2131
+ appendLine(streamA, line, line.startsWith("//") || line.startsWith("[... ") ? "vd-elide" : "vd-dim", d);
2132
+ d += 40;
2133
+ }
2134
+ d = 0;
2135
+ for (const line of step.bodyB) {
2136
+ appendLine(streamB, line, line.startsWith("[...") ? "vd-elide" : "vd-dim", d);
2137
+ d += 60;
2138
+ }
2139
+ await sleep(Math.max(d, 800));
2140
+ appendLine(streamA, " ⏱ +" + step.costA.toLocaleString() + " tok", "vd-cost-a");
2141
+ appendLine(streamB, " ⏱ +" + step.costB.toLocaleString() + " tok", "vd-cost-b");
2142
+ }
2143
+
2144
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
2145
+
2146
+ async function runDemoOnce() {
2147
+ clearStream(streamA);
2148
+ clearStream(streamB);
2149
+ totA.textContent = "0";
2150
+ totB.textContent = "0";
2151
+ deltaNum.textContent = "0%";
2152
+ deltaNum.classList.remove("stamping");
2153
+ costEl.textContent = "$0.00";
2154
+ costBadge?.classList.remove("shown");
2155
+ colA?.classList.remove("heavy");
2156
+ colB?.classList.remove("calm");
2157
+
2158
+ let cumA = 0, cumB = 0;
2159
+ for (const step of vdSteps) {
2160
+ await sleep(step.delay);
2161
+ await playStep(step);
2162
+ const newA = cumA + step.costA;
2163
+ const newB = cumB + step.costB;
2164
+ // Make the left counter rise faster + larger than the right — visceral.
2165
+ animateNum(totA, cumA, newA, 480);
2166
+ animateNum(totB, cumB, newB, 700);
2167
+ cumA = newA; cumB = newB;
2168
+ streamA.scrollTop = streamA.scrollHeight;
2169
+ streamB.scrollTop = streamB.scrollHeight;
2170
+ }
2171
+ // Atmosphere: column underlines pulse in to match the verdict
2172
+ colA?.classList.add("heavy");
2173
+ colB?.classList.add("calm");
2174
+ await sleep(700);
2175
+
2176
+ // Final reveal: animate % counter, then stamp + reveal $ saved
2177
+ const pct = Math.round((1 - cumB / cumA) * 100);
2178
+ const dollars = tokensToDollars(cumA - cumB);
2179
+ const steps = 26;
2180
+ for (let i = 0; i <= steps; i++) {
2181
+ deltaNum.textContent = "−" + Math.round(pct * (i / steps)) + "%";
2182
+ await sleep(13);
2183
+ }
2184
+ deltaNum.textContent = "−" + pct + "%";
2185
+ // Trigger stamp animation (reflow then add class)
2186
+ void deltaNum.offsetWidth;
2187
+ deltaNum.classList.add("stamping");
2188
+ // Cost badge fades in shortly after the stamp lands
2189
+ await sleep(280);
2190
+ costEl.textContent = dollars;
2191
+ costBadge?.classList.add("shown");
2192
+ }
2193
+
2194
+ async function loopDemo() {
2195
+ // Respect reduced motion — render once, don't loop.
2196
+ const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
2197
+ while (true) {
2198
+ await runDemoOnce();
2199
+ if (reduce) break;
2200
+ await sleep(6500); // dwell on the final stamp + $ reveal
2201
+ }
2202
+ }
2203
+
2204
+ // Start once the hero is visible (don't waste cycles when user hasn't scrolled).
2205
+ const vdEl = document.getElementById("vd");
2206
+ if (vdEl && "IntersectionObserver" in window) {
2207
+ const startObs = new IntersectionObserver(
2208
+ (entries) => {
2209
+ if (entries.some((e) => e.isIntersecting)) {
2210
+ startObs.disconnect();
2211
+ loopDemo();
2212
+ }
2213
+ },
2214
+ { threshold: 0.2 }
2215
+ );
2216
+ startObs.observe(vdEl);
2217
+ } else {
2218
+ loopDemo();
2219
+ }
2220
+
2221
+ // --- Copy install commands (three blocks: oneliner, prompt, verify) ---
2222
+ const copyTargets = {
2223
+ curl: () => document.getElementById("install-oneliner").innerText,
2224
+ prompt: () => document.getElementById("install-prompt-text").innerText,
2225
+ verify: () => document.getElementById("install-verify").innerText,
2226
+ "hero-curl": () => document.getElementById("hero-curl").innerText,
2227
+ "hero-slash":() => document.getElementById("hero-slash").innerText,
2228
+ };
2229
+ document.querySelectorAll(".copy-btn").forEach((btn) => {
2230
+ btn.addEventListener("click", async () => {
2231
+ const key = btn.getAttribute("data-copy");
2232
+ const text = copyTargets[key]?.() ?? "";
2233
+ try {
2234
+ await navigator.clipboard.writeText(text);
2235
+ const prev = btn.textContent;
2236
+ btn.textContent = "Copied ✓";
2237
+ setTimeout(() => (btn.textContent = prev || "Copy"), 1600);
2238
+ } catch {
2239
+ btn.textContent = "Err";
2240
+ }
2241
+ });
2242
+ });
2243
+
2244
+ // --- Reveal-on-scroll for later sections ---
2245
+ const io = new IntersectionObserver(
2246
+ (entries) => {
2247
+ entries.forEach((e) => {
2248
+ if (e.isIntersecting) {
2249
+ e.target.classList.add("in");
2250
+ io.unobserve(e.target);
2251
+ }
2252
+ });
2253
+ },
2254
+ { threshold: 0.12 }
2255
+ );
2256
+ document.querySelectorAll("section:not(.hero)").forEach((s) => {
2257
+ s.classList.add("reveal");
2258
+ io.observe(s);
2259
+ });
2260
+ </script>
2261
+ </body>
2262
+ </html>