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,35 @@
1
+ /**
2
+ * timing-budget.ts — scale wall-clock test budgets on shared CI runners.
3
+ *
4
+ * Several tests and smoke checks assert latency bounds (e.g. router cold start
5
+ * < 300ms). Those bounds are calibrated on a developer machine. GitHub-hosted
6
+ * macOS runners are much slower and noisier: the same unchanged code has
7
+ * measured ~450ms median cold start there, failing required checks on PRs
8
+ * that touch no code.
9
+ *
10
+ * `timingBudgetMs(ms)` returns `ms` unchanged locally, so local perf checks
11
+ * keep their original strength, and scales it on CI:
12
+ * - `ASHLR_TIMING_MULTIPLIER=<n>` (n >= 1) always wins, locally or on CI;
13
+ * - otherwise, when `CI` is set: 3x on macOS, 2x elsewhere.
14
+ * Windows-specific budgets that are already loosened should stay as they are
15
+ * and not be passed through this helper.
16
+ */
17
+
18
+ export function timingMultiplier(
19
+ env: Record<string, string | undefined> = process.env,
20
+ platform: NodeJS.Platform = process.platform,
21
+ ): number {
22
+ const explicit = Number(env.ASHLR_TIMING_MULTIPLIER);
23
+ if (Number.isFinite(explicit) && explicit >= 1) return explicit;
24
+ const ci = env.CI;
25
+ if (!ci || ci === "0" || ci.toLowerCase() === "false") return 1;
26
+ return platform === "darwin" ? 3 : 2;
27
+ }
28
+
29
+ export function timingBudgetMs(
30
+ ms: number,
31
+ env: Record<string, string | undefined> = process.env,
32
+ platform: NodeJS.Platform = process.platform,
33
+ ): number {
34
+ return Math.round(ms * timingMultiplier(env, platform));
35
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * CLI wrapper around the pure status-line cell renderer at
3
+ * `servers/_status-line-cells.ts`. This module is responsible only for
4
+ * (a) re-exporting the pure helpers so existing callers keep working, and
5
+ * (b) producing ANSI-escaped strings for terminal output.
6
+ *
7
+ * The actual animation logic — gradient sweep, pulse, heartbeat, context
8
+ * pressure — lives in `_status-line-cells.ts` and is consumed in structured
9
+ * form by the Remotion hero-video renderer. Both CLI and video render from
10
+ * exactly the same cell decisions; see `docs/hero-video-script.md` for the
11
+ * video side.
12
+ */
13
+
14
+ import {
15
+ cellsToAnsi,
16
+ activityCell,
17
+ contextPressureCells,
18
+ heartbeatCell,
19
+ sparklineCells,
20
+ lerpColor,
21
+ valuesToRamp,
22
+ gradientTs,
23
+ computePulse,
24
+ sweepFactor,
25
+ detectCapability,
26
+ frameAt,
27
+ ACTIVITY_ACTIVE_MS,
28
+ ACTIVITY_GLYPH_ASCII,
29
+ ACTIVITY_GLYPH_UNICODE,
30
+ ASCII_RAMP,
31
+ BRAND_DARK,
32
+ BRAND_LIGHT,
33
+ FRAME_MS,
34
+ HEARTBEAT_ASCII_ACTIVE,
35
+ HEARTBEAT_ASCII_IDLE,
36
+ HEARTBEAT_FRAMES,
37
+ HEARTBEAT_IDLE,
38
+ PULSE_CELL,
39
+ UNICODE_RAMP,
40
+ type Capability,
41
+ type Cell,
42
+ type PulseState,
43
+ type RGB,
44
+ type SparklineInput,
45
+ } from "../servers/_status-line-cells";
46
+
47
+ // Re-export the pure API for backward compatibility with existing callers.
48
+ export {
49
+ cellsToAnsi,
50
+ activityCell,
51
+ contextPressureCells,
52
+ heartbeatCell,
53
+ sparklineCells,
54
+ lerpColor,
55
+ valuesToRamp,
56
+ gradientTs,
57
+ computePulse,
58
+ sweepFactor,
59
+ detectCapability,
60
+ frameAt,
61
+ ACTIVITY_ACTIVE_MS,
62
+ ACTIVITY_GLYPH_ASCII,
63
+ ACTIVITY_GLYPH_UNICODE,
64
+ ASCII_RAMP,
65
+ BRAND_DARK,
66
+ BRAND_LIGHT,
67
+ FRAME_MS,
68
+ HEARTBEAT_ASCII_ACTIVE,
69
+ HEARTBEAT_ASCII_IDLE,
70
+ HEARTBEAT_FRAMES,
71
+ HEARTBEAT_IDLE,
72
+ PULSE_CELL,
73
+ UNICODE_RAMP,
74
+ type Capability,
75
+ type Cell,
76
+ type PulseState,
77
+ type RGB,
78
+ type SparklineInput,
79
+ };
80
+
81
+ // ---------------------------------------------------------------------------
82
+ // ANSI string renderers (thin wrappers around the Cell[] producers)
83
+ // ---------------------------------------------------------------------------
84
+
85
+ export interface RenderSparklineInput {
86
+ values: readonly number[];
87
+ frame: number;
88
+ msSinceActive: number;
89
+ cap: Capability;
90
+ }
91
+
92
+ export function renderSparkline(input: RenderSparklineInput): string {
93
+ return cellsToAnsi(sparklineCells(input));
94
+ }
95
+
96
+ export function renderHeartbeat(frame: number, msSinceActive: number, cap: Capability): string {
97
+ return cellsToAnsi([heartbeatCell(frame, msSinceActive, cap)]);
98
+ }
99
+
100
+ export function activityIndicator(msSinceActive: number, cap: Capability): string {
101
+ const cell = activityCell(msSinceActive, cap);
102
+ return cell ? cellsToAnsi([cell]) : "";
103
+ }
104
+
105
+ export function renderContextPressure(pct: number, cap: Capability): string {
106
+ return cellsToAnsi(contextPressureCells(pct, cap));
107
+ }
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // Width helper (kept here — it only matters for CLI output)
111
+ // ---------------------------------------------------------------------------
112
+
113
+ /**
114
+ * Visible character width of a rendered string — strips ANSI escapes. Used
115
+ * by the status line to enforce its 80-column budget regardless of color.
116
+ */
117
+ export function visibleWidth(s: string): number {
118
+ const stripped = s.replace(/\x1b\[[0-9;]*m/g, "");
119
+ return Array.from(stripped).length;
120
+ }
package/scripts/ui.ts ADDED
@@ -0,0 +1,431 @@
1
+ /**
2
+ * ashlr UI helpers — zero-dependency ANSI utilities for colored terminal
3
+ * output, Unicode symbols, spinners, progress bars, and bordered boxes.
4
+ *
5
+ * All helpers respect:
6
+ * - `NO_COLOR=1` (https://no-color.org) — disables all color/animation
7
+ * - `FORCE_COLOR=1` — forces color even when !isTTY
8
+ * - `!process.stdout.isTTY` — disables color/spinners/animation in pipes/CI
9
+ *
10
+ * Designed so that ANSI escapes never leak into scraped output, tests, or
11
+ * logs. When colors are disabled, every `c.*()` helper returns the input
12
+ * string verbatim and every symbol falls back to a plain ASCII approximation
13
+ * when the environment hints that UTF-8 isn't safe.
14
+ */
15
+ /* eslint-disable @typescript-eslint/no-unused-vars */
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Capability detection
19
+ // ---------------------------------------------------------------------------
20
+
21
+ function detectColor(): boolean {
22
+ if (process.env.NO_COLOR) return false;
23
+ if (process.env.FORCE_COLOR) return true;
24
+ // stdout may not exist in some runtimes (e.g. Workers); treat as no-color.
25
+ const tty = typeof process.stdout?.isTTY === "boolean" ? process.stdout.isTTY : false;
26
+ return tty;
27
+ }
28
+
29
+ function detectStderrTTY(): boolean {
30
+ if (process.env.NO_COLOR) return false;
31
+ if (process.env.FORCE_COLOR) return true;
32
+ const tty = typeof process.stderr?.isTTY === "boolean" ? process.stderr.isTTY : false;
33
+ return tty;
34
+ }
35
+
36
+ /** Cached capability so repeated calls are cheap. */
37
+ let _colorEnabled: boolean | null = null;
38
+ /** Cached stderr TTY capability for spinners. */
39
+ let _stderrTTY: boolean | null = null;
40
+
41
+ export function isColorEnabled(): boolean {
42
+ if (_colorEnabled === null) _colorEnabled = detectColor();
43
+ return _colorEnabled;
44
+ }
45
+
46
+ export function isStderrTTY(): boolean {
47
+ if (_stderrTTY === null) _stderrTTY = detectStderrTTY();
48
+ return _stderrTTY;
49
+ }
50
+
51
+ /** Test hook — reset cached capability detection. */
52
+ export function _resetCapCache(): void {
53
+ _colorEnabled = null;
54
+ _stderrTTY = null;
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // ANSI primitives
59
+ // ---------------------------------------------------------------------------
60
+
61
+ const ESC = "\u001b[";
62
+ const RESET = `${ESC}0m`;
63
+
64
+ function wrap(open: string, close: string, s: string): string {
65
+ if (!isColorEnabled()) return s;
66
+ return `${ESC}${open}m${s}${ESC}${close}m`;
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Colors and text styles
71
+ // ---------------------------------------------------------------------------
72
+
73
+ export const c = {
74
+ // Foreground colors
75
+ black: (s: string): string => wrap("30", "39", s),
76
+ red: (s: string): string => wrap("31", "39", s),
77
+ green: (s: string): string => wrap("32", "39", s),
78
+ yellow: (s: string): string => wrap("33", "39", s),
79
+ blue: (s: string): string => wrap("34", "39", s),
80
+ magenta: (s: string): string => wrap("35", "39", s),
81
+ cyan: (s: string): string => wrap("36", "39", s),
82
+ white: (s: string): string => wrap("37", "39", s),
83
+ gray: (s: string): string => wrap("90", "39", s),
84
+
85
+ // Styles
86
+ bold: (s: string): string => wrap("1", "22", s),
87
+ dim: (s: string): string => wrap("2", "22", s),
88
+ italic: (s: string): string => wrap("3", "23", s),
89
+ underline: (s: string): string => wrap("4", "24", s),
90
+ inverse: (s: string): string => wrap("7", "27", s),
91
+
92
+ // Bright variants — useful for emphasis without `bold` side-effects.
93
+ brightGreen: (s: string): string => wrap("92", "39", s),
94
+ brightRed: (s: string): string => wrap("91", "39", s),
95
+ brightYellow: (s: string): string => wrap("93", "39", s),
96
+ brightCyan: (s: string): string => wrap("96", "39", s),
97
+ brightMagenta: (s: string): string => wrap("95", "39", s),
98
+ };
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Symbols (UTF-8 glyphs with ASCII fallbacks)
102
+ // ---------------------------------------------------------------------------
103
+
104
+ /** Best-effort UTF-8 detection. Terminals that say UTF-8 in LANG or advertise
105
+ * a UTF-8 locale get the nice glyphs; everything else falls back to ASCII. */
106
+ function supportsUnicode(): boolean {
107
+ if (process.platform === "win32") {
108
+ // Modern Windows Terminal / ConEmu handle Unicode fine; naive cmd.exe may
109
+ // not. We favor glyphs here and let users set NO_COLOR if they need pure
110
+ // ASCII.
111
+ return true;
112
+ }
113
+ const l = (process.env.LC_ALL || process.env.LC_CTYPE || process.env.LANG || "").toLowerCase();
114
+ return l.includes("utf");
115
+ }
116
+
117
+ const UTF = supportsUnicode();
118
+
119
+ export const sym = {
120
+ check: UTF ? "\u2713" : "v", // ✓
121
+ cross: UTF ? "\u2717" : "x", // ✗
122
+ warn: UTF ? "\u26A0" : "!", // ⚠
123
+ info: UTF ? "\u2139" : "i", // ℹ
124
+ arrow: UTF ? "\u2192" : "->", // →
125
+ dot: UTF ? "\u00B7" : ".", // ·
126
+ bullet: UTF ? "\u2022" : "*",// •
127
+ star: UTF ? "\u2605" : "*", // ★
128
+ circle: UTF ? "\u25CB" : "o",// ○
129
+ filled: UTF ? "\u25CF" : "@",// ●
130
+ ellipsis: UTF ? "\u2026" : "...",
131
+ // Box-drawing
132
+ tl: UTF ? "\u256D" : "+", // ╭
133
+ tr: UTF ? "\u256E" : "+", // ╮
134
+ bl: UTF ? "\u2570" : "+", // ╰
135
+ br: UTF ? "\u256F" : "+", // ╯
136
+ h: UTF ? "\u2500" : "-", // ─
137
+ v: UTF ? "\u2502" : "|", // │
138
+ };
139
+
140
+ // ---------------------------------------------------------------------------
141
+ // Spinner
142
+ // ---------------------------------------------------------------------------
143
+
144
+ const SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
145
+ // Braille spinner — same as ora's default. Smooth on any modern terminal.
146
+
147
+ const CURSOR_HIDE = `${ESC}?25l`;
148
+ const CURSOR_SHOW = `${ESC}?25h`;
149
+ const CLEAR_LINE = `${ESC}2K\r`;
150
+
151
+ export class Spinner {
152
+ private frame = 0;
153
+ private timer: ReturnType<typeof setInterval> | null = null;
154
+ private message: string;
155
+ private stream: NodeJS.WriteStream;
156
+ private enabled: boolean;
157
+ private started = false;
158
+
159
+ constructor(message = "", opts: { stream?: NodeJS.WriteStream } = {}) {
160
+ this.message = message;
161
+ this.stream = opts.stream ?? process.stderr;
162
+ // Only animate when stderr is a real TTY. No-color disables the glyph
163
+ // color but spinners themselves only make sense on a TTY anyway.
164
+ this.enabled = isStderrTTY();
165
+ }
166
+
167
+ /** Start the spinner. If already started, updates the message instead. */
168
+ start(message?: string): this {
169
+ if (message !== undefined) this.message = message;
170
+ if (!this.enabled) {
171
+ // Non-TTY: stay silent. Callers still get to drive succeed/fail/info
172
+ // messages explicitly, which render as plain prefixed lines below.
173
+ this.started = true;
174
+ return this;
175
+ }
176
+ if (this.timer) return this; // already animating
177
+ this.stream.write(CURSOR_HIDE);
178
+ this.started = true;
179
+ this.render();
180
+ this.timer = setInterval(() => {
181
+ this.frame = (this.frame + 1) % SPINNER_FRAMES.length;
182
+ this.render();
183
+ }, 80);
184
+ // Best-effort cleanup on hard exits.
185
+ const cleanup = () => this.stop();
186
+ process.once("exit", cleanup);
187
+ process.once("SIGINT", () => { cleanup(); process.exit(130); });
188
+ return this;
189
+ }
190
+
191
+ update(message: string): this {
192
+ this.message = message;
193
+ if (this.enabled && this.timer) this.render();
194
+ return this;
195
+ }
196
+
197
+ succeed(message?: string): this {
198
+ const msg = message ?? this.message;
199
+ this.stop();
200
+ if (!this.enabled) return this; // stay quiet on non-TTY
201
+ const glyph = isColorEnabled() ? c.green(sym.check) : sym.check;
202
+ this.stream.write(`${glyph} ${msg}\n`);
203
+ return this;
204
+ }
205
+
206
+ fail(message?: string): this {
207
+ const msg = message ?? this.message;
208
+ this.stop();
209
+ if (!this.enabled) {
210
+ // Failures are actionable — surface them even on non-TTY, but without
211
+ // ANSI.
212
+ this.stream.write(`${sym.cross} ${msg}\n`);
213
+ return this;
214
+ }
215
+ const glyph = isColorEnabled() ? c.red(sym.cross) : sym.cross;
216
+ this.stream.write(`${glyph} ${msg}\n`);
217
+ return this;
218
+ }
219
+
220
+ warn(message?: string): this {
221
+ const msg = message ?? this.message;
222
+ this.stop();
223
+ if (!this.enabled) return this;
224
+ const glyph = isColorEnabled() ? c.yellow(sym.warn) : sym.warn;
225
+ this.stream.write(`${glyph} ${msg}\n`);
226
+ return this;
227
+ }
228
+
229
+ info(message?: string): this {
230
+ const msg = message ?? this.message;
231
+ this.stop();
232
+ if (!this.enabled) return this;
233
+ const glyph = isColorEnabled() ? c.cyan(sym.info) : sym.info;
234
+ this.stream.write(`${glyph} ${msg}\n`);
235
+ return this;
236
+ }
237
+
238
+ /** Stop the animation without printing a final line. */
239
+ stop(): this {
240
+ if (this.timer) {
241
+ clearInterval(this.timer);
242
+ this.timer = null;
243
+ }
244
+ if (this.enabled && this.started) {
245
+ this.stream.write(CLEAR_LINE);
246
+ this.stream.write(CURSOR_SHOW);
247
+ }
248
+ this.started = false;
249
+ return this;
250
+ }
251
+
252
+ private render(): void {
253
+ const frame = SPINNER_FRAMES[this.frame]!;
254
+ const colored = isColorEnabled() ? c.cyan(frame) : frame;
255
+ this.stream.write(`${CLEAR_LINE}${colored} ${this.message}`);
256
+ }
257
+ }
258
+
259
+ // ---------------------------------------------------------------------------
260
+ // Progress bar
261
+ // ---------------------------------------------------------------------------
262
+
263
+ export interface ProgressBarOpts {
264
+ width?: number;
265
+ stream?: NodeJS.WriteStream;
266
+ }
267
+
268
+ export class ProgressBar {
269
+ private total = 0;
270
+ private current = 0;
271
+ private label = "";
272
+ private width: number;
273
+ private stream: NodeJS.WriteStream;
274
+ private enabled: boolean;
275
+ private started = false;
276
+ private lastLen = 0;
277
+
278
+ constructor(opts: ProgressBarOpts = {}) {
279
+ this.width = opts.width ?? 24;
280
+ this.stream = opts.stream ?? process.stderr;
281
+ this.enabled = isStderrTTY();
282
+ }
283
+
284
+ start(total: number, label = ""): this {
285
+ this.total = Math.max(0, total);
286
+ this.current = 0;
287
+ this.label = label;
288
+ this.started = true;
289
+ if (this.enabled) {
290
+ this.stream.write(CURSOR_HIDE);
291
+ this.render();
292
+ }
293
+ // Non-TTY: stay silent. Progress is inherently a TTY-only UX.
294
+ return this;
295
+ }
296
+
297
+ increment(n = 1): this {
298
+ this.current = Math.min(this.total, this.current + n);
299
+ if (this.enabled && this.started) this.render();
300
+ return this;
301
+ }
302
+
303
+ update(current: number): this {
304
+ this.current = Math.min(this.total, Math.max(0, current));
305
+ if (this.enabled && this.started) this.render();
306
+ return this;
307
+ }
308
+
309
+ finish(message?: string): this {
310
+ if (this.enabled && this.started) {
311
+ this.stream.write(CLEAR_LINE);
312
+ this.stream.write(CURSOR_SHOW);
313
+ }
314
+ this.started = false;
315
+ if (message && this.enabled) {
316
+ const glyph = isColorEnabled() ? c.green(sym.check) : sym.check;
317
+ this.stream.write(`${glyph} ${message}\n`);
318
+ }
319
+ return this;
320
+ }
321
+
322
+ private render(): void {
323
+ const pct = this.total > 0 ? this.current / this.total : 0;
324
+ const filled = Math.round(pct * this.width);
325
+ const empty = this.width - filled;
326
+ const bar = UTF ? "\u2588".repeat(filled) + "\u2591".repeat(empty) : "#".repeat(filled) + "-".repeat(empty);
327
+ const coloredBar = isColorEnabled() ? c.cyan(bar) : bar;
328
+ const pctStr = `${Math.round(pct * 100)}%`;
329
+ const countStr = `${this.current}/${this.total}`;
330
+ const label = this.label ? ` ${this.label}` : "";
331
+ const line = `[${coloredBar}] ${countStr} (${isColorEnabled() ? c.dim(pctStr) : pctStr})${label}`;
332
+ this.stream.write(`${CLEAR_LINE}${line}`);
333
+ this.lastLen = line.length;
334
+ }
335
+ }
336
+
337
+ // ---------------------------------------------------------------------------
338
+ // Box
339
+ // ---------------------------------------------------------------------------
340
+
341
+ export interface BoxOpts {
342
+ title?: string;
343
+ /** Color applied to the border + title. */
344
+ color?: (s: string) => string;
345
+ /** Horizontal padding inside the box. Default 1. */
346
+ padding?: number;
347
+ }
348
+
349
+ /** Strip ANSI codes so we can measure visible width. */
350
+ function stripAnsi(s: string): string {
351
+ // eslint-disable-next-line no-control-regex
352
+ return s.replace(/\u001b\[[0-9;]*m/g, "");
353
+ }
354
+
355
+ function visibleWidth(s: string): number {
356
+ // Naive: one codepoint = one column. Good enough for our ASCII+glyph usage.
357
+ return [...stripAnsi(s)].length;
358
+ }
359
+
360
+ function padRight(s: string, target: number): string {
361
+ const pad = target - visibleWidth(s);
362
+ return pad > 0 ? s + " ".repeat(pad) : s;
363
+ }
364
+
365
+ export function box(content: string, opts: BoxOpts = {}): string {
366
+ const padding = opts.padding ?? 1;
367
+ const color = opts.color ?? ((s: string) => s);
368
+
369
+ const rawLines = content.split("\n");
370
+ const titleLen = opts.title ? visibleWidth(opts.title) + 2 /* spaces */ : 0;
371
+ const contentW = Math.max(...rawLines.map(visibleWidth), titleLen);
372
+ const innerW = contentW + padding * 2;
373
+
374
+ const h = color(sym.h);
375
+ const v = color(sym.v);
376
+ const tl = color(sym.tl);
377
+ const tr = color(sym.tr);
378
+ const bl = color(sym.bl);
379
+ const br = color(sym.br);
380
+
381
+ // Top border with optional title
382
+ let top: string;
383
+ if (opts.title) {
384
+ const titleText = color(isColorEnabled() ? c.bold(opts.title) : opts.title);
385
+ const titleBlock = ` ${titleText} `;
386
+ const remaining = innerW - visibleWidth(titleBlock);
387
+ const left = Math.max(1, Math.floor(remaining / 2));
388
+ const right = Math.max(1, remaining - left);
389
+ top = tl + h.repeat(left) + titleBlock + h.repeat(right) + tr;
390
+ } else {
391
+ top = tl + h.repeat(innerW) + tr;
392
+ }
393
+
394
+ const bottom = bl + h.repeat(innerW) + br;
395
+ const pad = " ".repeat(padding);
396
+
397
+ const body = rawLines.map((line) => {
398
+ const padded = padRight(line, contentW);
399
+ return `${v}${pad}${padded}${pad}${v}`;
400
+ });
401
+
402
+ return [top, ...body, bottom].join("\n");
403
+ }
404
+
405
+ // ---------------------------------------------------------------------------
406
+ // Banner — small ASCII-art logo
407
+ // ---------------------------------------------------------------------------
408
+
409
+ /** Renders a small gradient-colored banner. `text` defaults to "ashlr". */
410
+ export function banner(text = "ashlr"): string {
411
+ // Simple ASCII-art: block-style lettering built from Unicode block chars.
412
+ // We keep this short and readable; full figlet-style ASCII art would blow
413
+ // up the vertical real estate.
414
+ const line1 = `${sym.filled} ${text}`;
415
+ const line2 = `${sym.h.repeat(Math.max(text.length + 3, 6))}`;
416
+ if (!isColorEnabled()) return `${line1}\n${line2}`;
417
+ return `${c.brightMagenta(c.bold(line1))}\n${c.dim(line2)}`;
418
+ }
419
+
420
+ // ---------------------------------------------------------------------------
421
+ // Convenience helpers
422
+ // ---------------------------------------------------------------------------
423
+
424
+ /** Colored check/cross prefix helpers for quick status lines. */
425
+ export const prefix = {
426
+ ok: (s: string): string => (isColorEnabled() ? `${c.green(sym.check)} ${s}` : `${sym.check} ${s}`),
427
+ fail: (s: string): string => (isColorEnabled() ? `${c.red(sym.cross)} ${s}` : `${sym.cross} ${s}`),
428
+ warn: (s: string): string => (isColorEnabled() ? `${c.yellow(sym.warn)} ${s}` : `${sym.warn} ${s}`),
429
+ info: (s: string): string => (isColorEnabled() ? `${c.cyan(sym.info)} ${s}` : `${sym.info} ${s}`),
430
+ arrow: (s: string): string => (isColorEnabled() ? `${c.magenta(sym.arrow)} ${s}` : `${sym.arrow} ${s}`),
431
+ };