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,233 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * genome-key.ts — CLI for managing team genome encryption keys.
4
+ *
5
+ * Usage:
6
+ * bun run scripts/genome-key.ts generate <genomeId>
7
+ * bun run scripts/genome-key.ts export <genomeId>
8
+ * bun run scripts/genome-key.ts import <genomeId> <base32>
9
+ * bun run scripts/genome-key.ts rotate <genomeId>
10
+ *
11
+ * Keys are stored at ~/.ashlr/team-keys/<genomeId>.key (mode 0600, 32 raw bytes).
12
+ * Export/import uses base32 (RFC 4648) for human-friendly sharing over Signal,
13
+ * 1Password, etc.
14
+ *
15
+ * Key rotation procedure:
16
+ * 1. Reads all sections from the remote genome via the API.
17
+ * 2. Decrypts each section with the current key.
18
+ * 3. Generates a new key, encrypts all sections with it.
19
+ * 4. Pushes all re-encrypted sections to the remote.
20
+ * 5. Saves the new key file.
21
+ * After rotation, share the new key with all team members via a secure channel.
22
+ * Members who do not receive the new key will be unable to decrypt future sections.
23
+ */
24
+
25
+ import { join } from "path";
26
+ import { homedir } from "os";
27
+ import {
28
+ generateKey,
29
+ loadKey,
30
+ saveKey,
31
+ encryptSection,
32
+ decryptSection,
33
+ serializeBlob,
34
+ parseBlob,
35
+ encodeBase32,
36
+ decodeBase32,
37
+ } from "../servers/_genome-crypto.ts";
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Helpers
41
+ // ---------------------------------------------------------------------------
42
+
43
+ function die(msg: string): never {
44
+ process.stderr.write(`[genome-key] ERROR: ${msg}\n`);
45
+ process.exit(1);
46
+ }
47
+
48
+ function info(msg: string): void {
49
+ process.stdout.write(`${msg}\n`);
50
+ }
51
+
52
+ function keyFilePath(genomeId: string): string {
53
+ return join(homedir(), ".ashlr", "team-keys", `${genomeId}.key`);
54
+ }
55
+
56
+ async function promptConfirm(question: string): Promise<boolean> {
57
+ process.stdout.write(`${question} [y/N] `);
58
+ // Use process.stdin — works on Windows, macOS, and Linux without /dev/tty.
59
+ return new Promise((resolve) => {
60
+ const chunks: Buffer[] = [];
61
+ const onData = (c: Buffer) => {
62
+ chunks.push(c);
63
+ const s = Buffer.concat(chunks).toString("utf-8");
64
+ if (s.includes("\n") || s.includes("\r")) {
65
+ process.stdin.removeListener("data", onData);
66
+ process.stdin.pause();
67
+ const answer = s.trim().toLowerCase();
68
+ resolve(answer === "y" || answer === "yes");
69
+ }
70
+ };
71
+ process.stdin.resume();
72
+ process.stdin.setEncoding("utf-8");
73
+ process.stdin.on("data", onData);
74
+ process.stdin.once("end", () => resolve(false));
75
+ });
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Commands
80
+ // ---------------------------------------------------------------------------
81
+
82
+ async function cmdGenerate(genomeId: string): Promise<void> {
83
+ const existing = await loadKey(genomeId);
84
+ if (existing) {
85
+ const ok = await promptConfirm(
86
+ `A key already exists at ${keyFilePath(genomeId)}. Overwrite?`,
87
+ );
88
+ if (!ok) die("Aborted.");
89
+ }
90
+ const key = generateKey();
91
+ await saveKey(genomeId, key);
92
+ info(`Key generated and saved to ${keyFilePath(genomeId)} (mode 0600).`);
93
+ info(`Share it with team members using: bun run scripts/genome-key.ts export ${genomeId}`);
94
+ }
95
+
96
+ async function cmdExport(genomeId: string): Promise<void> {
97
+ const key = await loadKey(genomeId);
98
+ if (!key) die(`No key found at ${keyFilePath(genomeId)}. Run 'generate' first.`);
99
+
100
+ const ok = await promptConfirm(
101
+ "This will print your team encryption key to stdout. Only share over a secure channel (Signal, 1Password, etc.). Continue?",
102
+ );
103
+ if (!ok) die("Aborted.");
104
+
105
+ const encoded = encodeBase32(key);
106
+ info("");
107
+ info("=== TEAM GENOME KEY (keep secret) ===");
108
+ info(encoded);
109
+ info("=====================================");
110
+ info("");
111
+ info("Share this with team members. They import it with:");
112
+ info(` bun run scripts/genome-key.ts import ${genomeId} <key>`);
113
+ }
114
+
115
+ async function cmdImport(genomeId: string, base32Key: string): Promise<void> {
116
+ let key: Buffer;
117
+ try {
118
+ key = decodeBase32(base32Key);
119
+ } catch (err) {
120
+ die(`Invalid base32 key: ${String(err)}`);
121
+ }
122
+ if (key.length !== 32) {
123
+ die(`Key must be 32 bytes when decoded (got ${key.length}). Make sure you copied the full key.`);
124
+ }
125
+ await saveKey(genomeId, key);
126
+ info(`Key imported and saved to ${keyFilePath(genomeId)} (mode 0600).`);
127
+ }
128
+
129
+ async function cmdRotate(genomeId: string): Promise<void> {
130
+ const apiUrl = process.env["ASHLR_API_URL"] ?? "https://api.ashlr.ai";
131
+ const proToken = process.env["ASHLR_PRO_TOKEN"] ?? "";
132
+ if (!proToken) die("ASHLR_PRO_TOKEN is not set. Cannot pull/push sections for rotation.");
133
+
134
+ const currentKey = await loadKey(genomeId);
135
+ if (!currentKey) die(`No key found at ${keyFilePath(genomeId)}. Cannot rotate without the current key.`);
136
+
137
+ info(`Rotating key for genome ${genomeId}...`);
138
+ info("Step 1/4: Fetching all sections from remote...");
139
+
140
+ const pullRes = await fetch(`${apiUrl}/genome/${genomeId}/pull?since=0`, {
141
+ headers: { Authorization: `Bearer ${proToken}` },
142
+ });
143
+ if (!pullRes.ok) die(`Pull failed: ${pullRes.status} ${await pullRes.text()}`);
144
+
145
+ const pullData = await pullRes.json() as {
146
+ sections: Array<{ path: string; content: string; content_encrypted: boolean; vclock: Record<string, number> }>;
147
+ };
148
+
149
+ info(`Step 2/4: Decrypting ${pullData.sections.length} section(s) with current key...`);
150
+ const decrypted: Array<{ path: string; plaintext: string; vclock: Record<string, number> }> = [];
151
+ for (const sec of pullData.sections) {
152
+ if (!sec.content_encrypted) {
153
+ // Plaintext section — carry forward as-is
154
+ decrypted.push({ path: sec.path, plaintext: sec.content, vclock: sec.vclock });
155
+ continue;
156
+ }
157
+ try {
158
+ const blob = parseBlob(sec.content);
159
+ const plaintext = decryptSection(blob, currentKey);
160
+ decrypted.push({ path: sec.path, plaintext, vclock: sec.vclock });
161
+ } catch {
162
+ process.stderr.write(`[genome-key] WARNING: failed to decrypt ${sec.path} — skipping\n`);
163
+ }
164
+ }
165
+
166
+ info("Step 3/4: Generating new key and re-encrypting sections...");
167
+ const newKey = generateKey();
168
+ const reencrypted = decrypted.map((sec) => ({
169
+ path: sec.path,
170
+ content: serializeBlob(encryptSection(sec.plaintext, newKey)),
171
+ vclock: sec.vclock,
172
+ }));
173
+
174
+ if (reencrypted.length > 0) {
175
+ info(`Step 4/4: Pushing ${reencrypted.length} re-encrypted section(s)...`);
176
+ const clientId = `genome-key-rotate-${process.pid}`;
177
+ // Push in batches of 10 (rate limit)
178
+ for (let i = 0; i < reencrypted.length; i += 10) {
179
+ const batch = reencrypted.slice(i, i + 10);
180
+ const pushRes = await fetch(`${apiUrl}/genome/${genomeId}/push`, {
181
+ method: "POST",
182
+ headers: { Authorization: `Bearer ${proToken}`, "Content-Type": "application/json" },
183
+ body: JSON.stringify({ clientId, sections: batch }),
184
+ });
185
+ if (!pushRes.ok) die(`Push failed on batch ${i / 10 + 1}: ${pushRes.status} ${await pushRes.text()}`);
186
+ }
187
+ } else {
188
+ info("Step 4/4: No sections to push.");
189
+ }
190
+
191
+ await saveKey(genomeId, newKey);
192
+ info(`\nRotation complete. New key saved to ${keyFilePath(genomeId)} (mode 0600).`);
193
+ info("IMPORTANT: Share the new key with all active team members:");
194
+ info(` bun run scripts/genome-key.ts export ${genomeId}`);
195
+ info("Team members who do not receive the new key will be unable to read future encrypted sections.");
196
+ }
197
+
198
+ // ---------------------------------------------------------------------------
199
+ // Entry point
200
+ // ---------------------------------------------------------------------------
201
+
202
+ const [,, command, genomeId, ...rest] = process.argv;
203
+
204
+ if (!command || !genomeId) {
205
+ process.stderr.write(
206
+ "Usage:\n" +
207
+ " bun run scripts/genome-key.ts generate <genomeId>\n" +
208
+ " bun run scripts/genome-key.ts export <genomeId>\n" +
209
+ " bun run scripts/genome-key.ts import <genomeId> <base32>\n" +
210
+ " bun run scripts/genome-key.ts rotate <genomeId>\n",
211
+ );
212
+ process.exit(1);
213
+ }
214
+
215
+ switch (command) {
216
+ case "generate":
217
+ await cmdGenerate(genomeId);
218
+ break;
219
+ case "export":
220
+ await cmdExport(genomeId);
221
+ break;
222
+ case "import": {
223
+ const b32 = rest[0];
224
+ if (!b32) die("Missing base32 key argument.");
225
+ await cmdImport(genomeId, b32);
226
+ break;
227
+ }
228
+ case "rotate":
229
+ await cmdRotate(genomeId);
230
+ break;
231
+ default:
232
+ die(`Unknown command: ${command}. Use generate | export | import | rotate.`);
233
+ }
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * genome-keygen.ts — generate an X25519 keypair for team-cloud-genome v2 and
4
+ * upload the public half to the Ashlr server.
5
+ *
6
+ * Invoked by /ashlr-genome-keygen. Idempotent: if a keypair already exists
7
+ * on disk and the server already has the matching pubkey, it prints "already
8
+ * registered" and exits 0.
9
+ *
10
+ * Flow:
11
+ * 1. Read ~/.ashlr/pro-token (from /ashlr-upgrade).
12
+ * 2. GET /user/me → userId.
13
+ * 3. Check local ~/.ashlr/member-keys/<userId>.json + server /user/genome-pubkey:
14
+ * - both present + same pubkey → no-op.
15
+ * - local absent OR server absent → regenerate + upload.
16
+ * 4. Generate X25519 keypair.
17
+ * 5. Save locally (mode 0600).
18
+ * 6. POST /user/genome-pubkey with { pubkey, alg: "x25519-v1" }.
19
+ *
20
+ * Flags:
21
+ * --force regenerate even if a keypair already exists (rotates it)
22
+ * --endpoint <url> override default https://api.ashlr.ai
23
+ * --dry-run print what would happen; no network, no file writes
24
+ */
25
+
26
+ import { existsSync, readFileSync } from "fs";
27
+ import { homedir } from "os";
28
+ import { join } from "path";
29
+
30
+ import {
31
+ generateKeyPair,
32
+ loadKeypair,
33
+ memberKeyPath,
34
+ saveKeypair,
35
+ type StoredKeypair,
36
+ } from "../servers/_genome-crypto-v2";
37
+
38
+ const DEFAULT_API = process.env.ASHLR_API_URL ?? "https://api.ashlr.ai";
39
+ const ALG = "x25519-v1";
40
+
41
+ function proTokenPath(): string {
42
+ return join(process.env.HOME ?? homedir(), ".ashlr", "pro-token");
43
+ }
44
+
45
+ function readProToken(): string | null {
46
+ const path = proTokenPath();
47
+ if (!existsSync(path)) return null;
48
+ const t = readFileSync(path, "utf-8").trim();
49
+ return t || null;
50
+ }
51
+
52
+ interface Args {
53
+ force: boolean;
54
+ dryRun: boolean;
55
+ endpoint: string;
56
+ help: boolean;
57
+ }
58
+
59
+ function parseArgs(argv: string[]): Args {
60
+ const out: Args = { force: false, dryRun: false, endpoint: DEFAULT_API, help: false };
61
+ for (let i = 0; i < argv.length; i++) {
62
+ const a = argv[i];
63
+ if (a === "-h" || a === "--help") out.help = true;
64
+ else if (a === "--force") out.force = true;
65
+ else if (a === "--dry-run") out.dryRun = true;
66
+ else if (a === "--endpoint") out.endpoint = argv[++i] ?? out.endpoint;
67
+ }
68
+ return out;
69
+ }
70
+
71
+ function err(m: string): void { process.stderr.write(m + "\n"); }
72
+ function out(m: string): void { process.stdout.write(m + "\n"); }
73
+
74
+ async function api<T>(method: string, url: string, token: string, body?: unknown): Promise<{ status: number; body: T | null }> {
75
+ const headers: Record<string, string> = {
76
+ authorization: `Bearer ${token}`,
77
+ };
78
+ const init: RequestInit = { method, headers };
79
+ if (body !== undefined) {
80
+ headers["content-type"] = "application/json";
81
+ init.body = JSON.stringify(body);
82
+ }
83
+ const res = await fetch(url, init);
84
+ let j: T | null = null;
85
+ try { j = (await res.json()) as T; } catch { /* non-JSON / 404 body */ }
86
+ return { status: res.status, body: j };
87
+ }
88
+
89
+ export async function run(argv: string[]): Promise<number> {
90
+ const args = parseArgs(argv);
91
+ if (args.help) {
92
+ err("Usage: /ashlr-genome-keygen [--force] [--dry-run] [--endpoint <url>]");
93
+ return 0;
94
+ }
95
+
96
+ const token = readProToken();
97
+ if (!token) {
98
+ err("No Ashlr Pro token found. Run /ashlr-upgrade first.");
99
+ return 2;
100
+ }
101
+
102
+ // Resolve user id.
103
+ const me = await api<{ userId: string; email: string }>("GET", `${args.endpoint}/user/me`, token);
104
+ if (me.status !== 200 || !me.body) {
105
+ err(`Couldn't resolve your Ashlr account (status ${me.status}).`);
106
+ return 3;
107
+ }
108
+ const userId = me.body.userId;
109
+
110
+ // Current state.
111
+ const localKp = loadKeypair(userId);
112
+ const serverPk = await api<{ pubkey: string; alg: string }>(
113
+ "GET", `${args.endpoint}/user/genome-pubkey`, token,
114
+ );
115
+
116
+ const haveLocal = localKp !== null;
117
+ const haveServer = serverPk.status === 200 && !!serverPk.body;
118
+ const inSync = haveLocal && haveServer && localKp!.publicKey === serverPk.body!.pubkey;
119
+
120
+ if (inSync && !args.force) {
121
+ out(`Already registered.`);
122
+ out(` pubkey: ${localKp!.publicKey}`);
123
+ out(` alg: ${localKp!.alg}`);
124
+ out(` path: ${memberKeyPath(userId)}`);
125
+ return 0;
126
+ }
127
+
128
+ if (args.dryRun) {
129
+ err("--dry-run: would generate a fresh X25519 keypair and upload the public half.");
130
+ return 0;
131
+ }
132
+
133
+ // Generate.
134
+ const { publicKey, privateKey } = generateKeyPair();
135
+ const stored: StoredKeypair = {
136
+ userId,
137
+ publicKey,
138
+ privateKey,
139
+ alg: ALG,
140
+ createdAt: new Date().toISOString(),
141
+ };
142
+ const path = saveKeypair(stored);
143
+
144
+ // Upload.
145
+ const up = await api<{ ok: boolean }>(
146
+ "POST", `${args.endpoint}/user/genome-pubkey`, token,
147
+ { pubkey: publicKey, alg: ALG },
148
+ );
149
+ if (up.status !== 200) {
150
+ err(`Upload failed: HTTP ${up.status}`);
151
+ return 3;
152
+ }
153
+
154
+ out(`Generated X25519 keypair.`);
155
+ out(` pubkey: ${publicKey}`);
156
+ out(` alg: ${ALG}`);
157
+ out(` path: ${path}`);
158
+ out(``);
159
+ out(`Your public key is now on file with Ashlr. Keep ${path} private — it is`);
160
+ out(`the only way to decrypt team-cloud genomes shared with you.`);
161
+ return 0;
162
+ }
163
+
164
+ if (import.meta.main) {
165
+ run(process.argv.slice(2))
166
+ .then((code) => process.exit(code))
167
+ .catch((e) => { err(`fatal: ${(e as Error).message}`); process.exit(1); });
168
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * genome-link — walk up parent directories looking for a genome.
3
+ *
4
+ * When a project has no `.ashlrcode/genome/` of its own, ashlr tools can fall
5
+ * back to a genome in an ancestor directory (e.g. a workspace-level genome at
6
+ * `~/Desktop/.ashlrcode/genome/`). This module locates that ancestor.
7
+ *
8
+ * The walk stops at whichever comes first:
9
+ * - a directory containing `.ashlrcode/genome/manifest.json`
10
+ * - `$HOME` (we never look at `$HOME` itself or above — user-level genomes
11
+ * are out of scope; we want *workspace* genomes)
12
+ * - the filesystem root
13
+ * - `maxDepth` parents (default 4)
14
+ */
15
+ import { existsSync } from "fs";
16
+ import { homedir } from "os";
17
+ import { dirname, join, resolve, sep } from "path";
18
+
19
+ const GENOME_MANIFEST_REL = ".ashlrcode/genome/manifest.json";
20
+ const DEFAULT_MAX_DEPTH = 4;
21
+
22
+ function hasGenome(dir: string): boolean {
23
+ return existsSync(join(dir, GENOME_MANIFEST_REL));
24
+ }
25
+
26
+ /**
27
+ * Walk up from `startDir` looking for the first ancestor that contains
28
+ * `.ashlrcode/genome/manifest.json`. Returns that directory's absolute path,
29
+ * or `null` if none is found within `maxDepth` steps.
30
+ *
31
+ * The walk skips `startDir` itself — callers should check the current dir
32
+ * separately via `genomeExists(cwd)`. We stop before reaching `$HOME` so we
33
+ * don't accidentally attach a user-level genome to an unrelated project.
34
+ */
35
+ export function findParentGenome(
36
+ startDir: string,
37
+ maxDepth: number = DEFAULT_MAX_DEPTH,
38
+ ): string | null {
39
+ const home = resolve(homedir());
40
+ let current = resolve(startDir);
41
+ let depth = 0;
42
+
43
+ while (depth < maxDepth) {
44
+ const parent = dirname(current);
45
+ // Stop at filesystem root (dirname of "/" is "/") to avoid infinite loop.
46
+ if (parent === current) return null;
47
+ // Stop when we'd cross into or above $HOME — workspace genomes only,
48
+ // user-level (~/.ashlrcode) is out of scope.
49
+ if (parent === home) return null;
50
+ // Safety: if we somehow walked outside the $HOME subtree, bail.
51
+ if (!parent.startsWith(home + sep)) return null;
52
+
53
+ if (hasGenome(parent)) return parent;
54
+
55
+ current = parent;
56
+ depth++;
57
+ }
58
+ return null;
59
+ }