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,986 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * ashlr genome-init — initialize a `.ashlrcode/genome/` directory in a project.
4
+ *
5
+ * Reuses @ashlr/core-efficiency's `initGenome` for the base scaffold, then
6
+ * layers on ashlr-plugin-specific customizations:
7
+ * - ADR-0000 placeholder in knowledge/decisions.md
8
+ * - Auto-populated knowledge/architecture.md from the baseline scanner
9
+ * - Auto-populated knowledge/conventions.md from detected config files
10
+ *
11
+ * Usage:
12
+ * bun run scripts/genome-init.ts --dir <path> [--force] [--minimal]
13
+ */
14
+
15
+ import { existsSync, readdirSync, readFileSync } from "fs";
16
+ import { rm } from "fs/promises";
17
+ import { basename, join, resolve } from "path";
18
+ import { execSync } from "child_process";
19
+ import { initGenome } from "@ashlr/core-efficiency/genome";
20
+ import {
21
+ genomeDir,
22
+ genomeExists,
23
+ loadManifest,
24
+ writeSection,
25
+ } from "@ashlr/core-efficiency/genome";
26
+ import { scan, type Baseline } from "./baseline-scan.ts";
27
+ import { Spinner, ProgressBar, c, sym, isColorEnabled } from "./ui.ts";
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Args
31
+ // ---------------------------------------------------------------------------
32
+
33
+ export interface CliArgs {
34
+ dir?: string;
35
+ force: boolean;
36
+ minimal: boolean;
37
+ summarize: boolean; // use local model (Ollama) for CLAUDE.md summaries
38
+ }
39
+
40
+ export function parseArgs(argv: string[]): CliArgs {
41
+ let dir: string | undefined;
42
+ let force = false;
43
+ let minimal = false;
44
+ let summarize = false;
45
+ for (let i = 0; i < argv.length; i++) {
46
+ const a = argv[i];
47
+ if (a === "--dir" && argv[i + 1]) dir = argv[++i];
48
+ else if (a === "--force") force = true;
49
+ else if (a === "--minimal") minimal = true;
50
+ else if (a === "--summarize") summarize = true;
51
+ }
52
+ return { dir, force, minimal, summarize };
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Convention detection
57
+ // ---------------------------------------------------------------------------
58
+
59
+ export interface DetectedConventions {
60
+ detected: string[]; // human-readable bullet lines
61
+ files: string[]; // filenames found
62
+ }
63
+
64
+ export function detectConventions(dir: string): DetectedConventions {
65
+ const files: string[] = [];
66
+ const detected: string[] = [];
67
+
68
+ // biome.json
69
+ if (existsSync(join(dir, "biome.json")) || existsSync(join(dir, "biome.jsonc"))) {
70
+ files.push("biome.json");
71
+ detected.push("Biome is configured — lint + format via `biome check`.");
72
+ }
73
+
74
+ // eslint
75
+ const eslintCandidates = [
76
+ ".eslintrc",
77
+ ".eslintrc.js",
78
+ ".eslintrc.cjs",
79
+ ".eslintrc.json",
80
+ ".eslintrc.yaml",
81
+ ".eslintrc.yml",
82
+ "eslint.config.js",
83
+ "eslint.config.mjs",
84
+ "eslint.config.ts",
85
+ ];
86
+ for (const name of eslintCandidates) {
87
+ if (existsSync(join(dir, name))) {
88
+ files.push(name);
89
+ detected.push(`ESLint is configured (${name}).`);
90
+ break;
91
+ }
92
+ }
93
+
94
+ // prettier
95
+ const prettierCandidates = [
96
+ ".prettierrc",
97
+ ".prettierrc.json",
98
+ ".prettierrc.js",
99
+ ".prettierrc.cjs",
100
+ ".prettierrc.yaml",
101
+ ".prettierrc.yml",
102
+ "prettier.config.js",
103
+ "prettier.config.cjs",
104
+ "prettier.config.mjs",
105
+ ];
106
+ for (const name of prettierCandidates) {
107
+ if (existsSync(join(dir, name))) {
108
+ files.push(name);
109
+ detected.push(`Prettier is configured (${name}).`);
110
+ break;
111
+ }
112
+ }
113
+
114
+ // editorconfig
115
+ if (existsSync(join(dir, ".editorconfig"))) {
116
+ files.push(".editorconfig");
117
+ detected.push(".editorconfig present — respect indent/newline conventions.");
118
+ }
119
+
120
+ // tsconfig strict settings
121
+ const tsconfigPath = join(dir, "tsconfig.json");
122
+ if (existsSync(tsconfigPath)) {
123
+ files.push("tsconfig.json");
124
+ try {
125
+ const raw = readFileSync(tsconfigPath, "utf-8");
126
+ // Strip comments for JSONC safety
127
+ const cleaned = raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
128
+ const ts = JSON.parse(cleaned);
129
+ const co = (ts && typeof ts === "object" && ts.compilerOptions) || {};
130
+ const strictFlags: string[] = [];
131
+ for (const key of [
132
+ "strict",
133
+ "noImplicitAny",
134
+ "strictNullChecks",
135
+ "noUncheckedIndexedAccess",
136
+ "noImplicitReturns",
137
+ "noFallthroughCasesInSwitch",
138
+ ]) {
139
+ if (co[key] === true) strictFlags.push(key);
140
+ }
141
+ if (strictFlags.length > 0) {
142
+ detected.push(
143
+ `TypeScript strict settings enabled: ${strictFlags.join(", ")}.`,
144
+ );
145
+ } else {
146
+ detected.push("TypeScript is used (tsconfig.json present).");
147
+ }
148
+ } catch {
149
+ detected.push("TypeScript is used (tsconfig.json present, but unparseable).");
150
+ }
151
+ }
152
+
153
+ // package.json scripts
154
+ const pkgPath = join(dir, "package.json");
155
+ if (existsSync(pkgPath)) {
156
+ files.push("package.json");
157
+ try {
158
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
159
+ const scripts = (pkg && pkg.scripts) || {};
160
+ const names = Object.keys(scripts);
161
+ if (names.length > 0) {
162
+ const notable = names
163
+ .filter((n) =>
164
+ ["test", "lint", "typecheck", "build", "format", "check"].includes(n),
165
+ )
166
+ .slice(0, 8);
167
+ if (notable.length > 0) {
168
+ detected.push(
169
+ `package.json scripts: ${notable.map((n) => `\`${n}\``).join(", ")}.`,
170
+ );
171
+ } else {
172
+ detected.push(
173
+ `package.json scripts present: ${names.slice(0, 6).map((n) => `\`${n}\``).join(", ")}.`,
174
+ );
175
+ }
176
+ }
177
+ } catch {
178
+ /* ignore */
179
+ }
180
+ }
181
+
182
+ if (detected.length === 0) {
183
+ detected.push("No standard lint/format configs detected.");
184
+ }
185
+
186
+ return { detected, files };
187
+ }
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Project discovery — scan child directories for related projects
191
+ // ---------------------------------------------------------------------------
192
+
193
+ export interface DiscoveredProject {
194
+ name: string;
195
+ path: string;
196
+ isGitRepo: boolean;
197
+ remoteUrl?: string; // origin remote URL
198
+ org?: string; // GitHub org/owner extracted from remote
199
+ repoName?: string; // repo name extracted from remote
200
+ hasClaudeMd: boolean;
201
+ claudeMdSummary?: string; // first ~500 chars of CLAUDE.md
202
+ hasClaudeDir: boolean;
203
+ hasGenome: boolean;
204
+ }
205
+
206
+ export interface WorkspaceGraph {
207
+ rootDir: string;
208
+ projects: DiscoveredProject[];
209
+ orgs: Map<string, DiscoveredProject[]>; // grouped by GitHub org
210
+ ollamaModel?: string; // set if Ollama was actually used for summaries
211
+ }
212
+
213
+ // ---------------------------------------------------------------------------
214
+ // Local model summarization (Ollama)
215
+ // ---------------------------------------------------------------------------
216
+
217
+ const OLLAMA_BASE_URL = "http://localhost:11434";
218
+ const OLLAMA_TIMEOUT_MS = 2000;
219
+ const OLLAMA_GENERATE_TIMEOUT_MS = 30000;
220
+ /** Max chars of CLAUDE.md to pass to the LLM. Avoids loading huge files. */
221
+ const OLLAMA_MAX_INPUT_CHARS = 3000;
222
+
223
+ /** Preferred models in order — use the first one available. */
224
+ const PREFERRED_MODELS = ["llama3.2:3b", "llama3.2:1b", "gemma4:12b", "gemma4:26b", "qwen2.5:3b"];
225
+
226
+ let resolvedModel: string | null = null;
227
+
228
+ /**
229
+ * Check if Ollama is running and find the best available model.
230
+ * Returns the model name or null if unavailable.
231
+ */
232
+ async function resolveOllamaModel(): Promise<string | null> {
233
+ if (resolvedModel !== null) return resolvedModel;
234
+ try {
235
+ const controller = new AbortController();
236
+ const timer = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT_MS);
237
+ const res = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {
238
+ signal: controller.signal,
239
+ });
240
+ clearTimeout(timer);
241
+ if (!res.ok) return null;
242
+ const data = (await res.json()) as { models?: Array<{ name: string }> };
243
+ const available = new Set((data.models ?? []).map((m) => m.name));
244
+ // Pick first preferred model that's available
245
+ for (const model of PREFERRED_MODELS) {
246
+ if (available.has(model)) {
247
+ resolvedModel = model;
248
+ return model;
249
+ }
250
+ }
251
+ // Fall back to first available model
252
+ const first = data.models?.[0]?.name;
253
+ resolvedModel = first ?? null;
254
+ return resolvedModel;
255
+ } catch {
256
+ return null;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Summarize text using a local Ollama model.
262
+ * Returns null on failure — caller falls back to truncation.
263
+ */
264
+ async function summarizeWithOllama(
265
+ text: string,
266
+ projectName: string,
267
+ model: string,
268
+ ): Promise<string | null> {
269
+ try {
270
+ const controller = new AbortController();
271
+ const timer = setTimeout(() => controller.abort(), OLLAMA_GENERATE_TIMEOUT_MS);
272
+ const res = await fetch(`${OLLAMA_BASE_URL}/api/chat`, {
273
+ method: "POST",
274
+ headers: { "Content-Type": "application/json" },
275
+ body: JSON.stringify({
276
+ model,
277
+ messages: [
278
+ {
279
+ role: "system",
280
+ content:
281
+ "You are a concise technical summarizer. Respond with exactly 2-3 sentences. No headers, no bullet points, no markdown.",
282
+ },
283
+ {
284
+ role: "user",
285
+ content: `Summarize this CLAUDE.md project file for "${projectName}". What is the project, what's the tech stack, and what's its current status?\n\n${text}`,
286
+ },
287
+ ],
288
+ stream: false,
289
+ options: { num_predict: 200, temperature: 0.1 },
290
+ }),
291
+ signal: controller.signal,
292
+ });
293
+ clearTimeout(timer);
294
+ if (!res.ok) return null;
295
+ const data = (await res.json()) as { message?: { content?: string } };
296
+ return data.message?.content?.trim() ?? null;
297
+ } catch {
298
+ return null;
299
+ }
300
+ }
301
+
302
+ /** Extract org and repo name from a GitHub remote URL. */
303
+ export function parseGitRemote(url: string): { org: string; repo: string } | null {
304
+ // https://github.com/owner/repo.git or git@github.com:owner/repo.git
305
+ const httpsMatch = url.match(/github\.com\/([^/]+)\/([^/.]+)/);
306
+ if (httpsMatch) return { org: httpsMatch[1], repo: httpsMatch[2] };
307
+ const sshMatch = url.match(/github\.com:([^/]+)\/([^/.]+)/);
308
+ if (sshMatch) return { org: sshMatch[1], repo: sshMatch[2] };
309
+ return null;
310
+ }
311
+
312
+ /** Read the first ~500 chars of a CLAUDE.md as a summary. */
313
+ function readClaudeMdSummary(filePath: string): string {
314
+ try {
315
+ const raw = readFileSync(filePath, "utf-8");
316
+ // Strip frontmatter if present
317
+ let content = raw;
318
+ if (content.startsWith("---")) {
319
+ const end = content.indexOf("---", 3);
320
+ if (end !== -1) content = content.slice(end + 3).trimStart();
321
+ }
322
+ // Take first ~500 chars, break at last newline
323
+ if (content.length > 500) {
324
+ const cut = content.lastIndexOf("\n", 500);
325
+ content = content.slice(0, cut > 200 ? cut : 500) + "\n…";
326
+ }
327
+ return content.trim();
328
+ } catch {
329
+ return "";
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Scan immediate child directories for git repos, CLAUDE.md files, and genomes.
335
+ * Depth-limited to 1 level. Skips hidden dirs, node_modules, etc.
336
+ * When `summarize` is true and Ollama is available, uses a local model
337
+ * to generate concise CLAUDE.md summaries instead of truncating.
338
+ */
339
+ export interface DiscoverProgress {
340
+ /** Called once after the directory scan, with the number of child dirs found. */
341
+ onScanned?: (total: number) => void;
342
+ /** Called before starting Ollama summarization, with the total docs to summarize. */
343
+ onSummarizeStart?: (total: number, model: string) => void;
344
+ /** Called after each summarization attempt (success or fallback). */
345
+ onSummarized?: (projectName: string, usedModel: boolean) => void;
346
+ /** Called when the full discovery is done. */
347
+ onDone?: (graph: WorkspaceGraph) => void;
348
+ }
349
+
350
+ export async function discoverProjects(
351
+ dir: string,
352
+ opts: { summarize?: boolean; progress?: DiscoverProgress } = {},
353
+ ): Promise<WorkspaceGraph> {
354
+ const skipDirs = new Set([
355
+ "node_modules", "dist", "build", "coverage", ".Trash",
356
+ ".git", "__pycache__", ".next", ".turbo",
357
+ ]);
358
+
359
+ // Resolve Ollama model up front; warn once if requested but unavailable.
360
+ const ollamaModel = opts.summarize ? await resolveOllamaModel() : null;
361
+ if (opts.summarize && !ollamaModel) {
362
+ process.stderr.write(
363
+ "ashlr genome-init: --summarize requested but Ollama is not running or has no models. Falling back to truncation.\n",
364
+ );
365
+ }
366
+
367
+ let entries: import("fs").Dirent[];
368
+ try {
369
+ entries = readdirSync(dir, { withFileTypes: true, encoding: "utf-8" }) as import("fs").Dirent[];
370
+ } catch {
371
+ return { rootDir: dir, projects: [], orgs: new Map() };
372
+ }
373
+
374
+ // Walk immediate children once, collecting metadata and the path to
375
+ // CLAUDE.md (if any) so we can summarize in a second pass.
376
+ const projects: DiscoveredProject[] = [];
377
+ const claudeMdPaths = new Map<string, string>(); // project.name -> CLAUDE.md path
378
+
379
+ for (const entry of entries) {
380
+ // Skip symlinks to avoid traversing outside the workspace root.
381
+ if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
382
+ if (entry.name.startsWith(".") || skipDirs.has(entry.name)) continue;
383
+
384
+ const childPath = join(dir, entry.name);
385
+ const isGitRepo = existsSync(join(childPath, ".git"));
386
+
387
+ let remoteUrl: string | undefined;
388
+ let org: string | undefined;
389
+ let repoName: string | undefined;
390
+ if (isGitRepo) {
391
+ try {
392
+ remoteUrl = execSync("git remote get-url origin", {
393
+ cwd: childPath,
394
+ timeout: 3000,
395
+ encoding: "utf-8",
396
+ stdio: ["pipe", "pipe", "pipe"],
397
+ }).trim();
398
+ const parsed = parseGitRemote(remoteUrl);
399
+ if (parsed) {
400
+ org = parsed.org;
401
+ repoName = parsed.repo;
402
+ }
403
+ } catch {
404
+ // No remote or git error — skip
405
+ }
406
+ }
407
+
408
+ const claudeMdPath = join(childPath, "CLAUDE.md");
409
+ const hasClaudeMd = existsSync(claudeMdPath);
410
+ if (hasClaudeMd) claudeMdPaths.set(entry.name, claudeMdPath);
411
+
412
+ projects.push({
413
+ name: entry.name,
414
+ path: childPath,
415
+ isGitRepo,
416
+ remoteUrl,
417
+ org,
418
+ repoName,
419
+ hasClaudeMd,
420
+ hasClaudeDir: existsSync(join(childPath, ".claude")),
421
+ hasGenome: existsSync(join(childPath, ".ashlrcode", "genome", "manifest.json")),
422
+ });
423
+ }
424
+
425
+ // Notify listeners that the initial directory scan is complete.
426
+ opts.progress?.onScanned?.(projects.length);
427
+
428
+ // Fill in CLAUDE.md summaries. With Ollama we summarize in parallel
429
+ // batches; without it we just truncate synchronously.
430
+ const PARALLEL_BATCH = 4;
431
+ const projectsWithClaudeMd = projects.filter((p) => claudeMdPaths.has(p.name));
432
+
433
+ // Track whether any Ollama summary actually succeeded.
434
+ let ollamaUsed = false;
435
+
436
+ if (ollamaModel) {
437
+ opts.progress?.onSummarizeStart?.(projectsWithClaudeMd.length, ollamaModel);
438
+ for (let i = 0; i < projectsWithClaudeMd.length; i += PARALLEL_BATCH) {
439
+ const batch = projectsWithClaudeMd.slice(i, i + PARALLEL_BATCH);
440
+ await Promise.all(
441
+ batch.map(async (p) => {
442
+ const path = claudeMdPaths.get(p.name)!;
443
+ // Cap read at OLLAMA_MAX_INPUT_CHARS to avoid loading huge files into memory.
444
+ const raw = readFileSync(path, "utf-8").slice(0, OLLAMA_MAX_INPUT_CHARS);
445
+ const llm = await summarizeWithOllama(raw, p.name, ollamaModel);
446
+ if (llm) {
447
+ ollamaUsed = true;
448
+ p.claudeMdSummary = llm;
449
+ opts.progress?.onSummarized?.(p.name, true);
450
+ } else {
451
+ p.claudeMdSummary = readClaudeMdSummary(path);
452
+ opts.progress?.onSummarized?.(p.name, false);
453
+ }
454
+ }),
455
+ );
456
+ }
457
+ } else {
458
+ for (const p of projectsWithClaudeMd) {
459
+ p.claudeMdSummary = readClaudeMdSummary(claudeMdPaths.get(p.name)!);
460
+ }
461
+ }
462
+
463
+ // Group by org for the workspace renderer.
464
+ const orgs = new Map<string, DiscoveredProject[]>();
465
+ for (const p of projects) {
466
+ if (!p.org) continue;
467
+ const list = orgs.get(p.org) ?? [];
468
+ list.push(p);
469
+ orgs.set(p.org, list);
470
+ }
471
+
472
+ const graph: WorkspaceGraph = {
473
+ rootDir: dir,
474
+ projects,
475
+ orgs,
476
+ ollamaModel: ollamaUsed ? ollamaModel ?? undefined : undefined,
477
+ };
478
+ opts.progress?.onDone?.(graph);
479
+ return graph;
480
+ }
481
+
482
+ // ---------------------------------------------------------------------------
483
+ // Workspace markdown renderer
484
+ // ---------------------------------------------------------------------------
485
+
486
+ export function renderWorkspaceMd(graph: WorkspaceGraph): string {
487
+ const lines: string[] = [];
488
+ lines.push("# Workspace");
489
+ lines.push("");
490
+ lines.push(
491
+ "> Auto-discovered from child directories at genome init. This helps the",
492
+ "> agent understand which projects live in this workspace and how they relate.",
493
+ );
494
+ lines.push("");
495
+
496
+ // Summary
497
+ const gitRepos = graph.projects.filter((p) => p.isGitRepo);
498
+ const withContext = graph.projects.filter((p) => p.hasClaudeMd || p.hasClaudeDir);
499
+ const withGenome = graph.projects.filter((p) => p.hasGenome);
500
+ lines.push(`- **Total directories:** ${graph.projects.length}`);
501
+ lines.push(`- **Git repositories:** ${gitRepos.length}`);
502
+ lines.push(`- **Projects with CLAUDE.md or .claude/:** ${withContext.length}`);
503
+ if (withGenome.length > 0) {
504
+ lines.push(`- **Projects with genome:** ${withGenome.length}`);
505
+ }
506
+ lines.push("");
507
+
508
+ // Org grouping
509
+ if (graph.orgs.size > 0) {
510
+ lines.push("## Organizations");
511
+ lines.push("");
512
+ for (const [orgName, orgProjects] of [...graph.orgs.entries()].sort()) {
513
+ lines.push(`### ${orgName} (${orgProjects.length} repos)`);
514
+ lines.push("");
515
+ for (const p of orgProjects.sort((a, b) => a.name.localeCompare(b.name))) {
516
+ const badges: string[] = [];
517
+ if (p.hasClaudeMd) badges.push("CLAUDE.md");
518
+ if (p.hasGenome) badges.push("genome");
519
+ const suffix = badges.length > 0 ? ` — ${badges.join(", ")}` : "";
520
+ lines.push(`- **${p.name}** → \`${p.repoName || p.name}\`${suffix}`);
521
+ }
522
+ lines.push("");
523
+ }
524
+ }
525
+
526
+ // Ungrouped / no-remote projects
527
+ const ungrouped = graph.projects.filter((p) => !p.org);
528
+ if (ungrouped.length > 0) {
529
+ lines.push("## Local-only (no GitHub remote)");
530
+ lines.push("");
531
+ for (const p of ungrouped.sort((a, b) => a.name.localeCompare(b.name))) {
532
+ const badges: string[] = [];
533
+ if (p.isGitRepo) badges.push("git");
534
+ if (p.hasClaudeMd) badges.push("CLAUDE.md");
535
+ if (p.hasGenome) badges.push("genome");
536
+ const suffix = badges.length > 0 ? ` [${badges.join(", ")}]` : "";
537
+ lines.push(`- **${p.name}**${suffix}`);
538
+ }
539
+ lines.push("");
540
+ }
541
+
542
+ // CLAUDE.md summaries for projects that have them
543
+ const withSummaries = graph.projects.filter(
544
+ (p) => p.claudeMdSummary && p.claudeMdSummary.length > 0,
545
+ );
546
+ if (withSummaries.length > 0) {
547
+ lines.push("## Project Context (from CLAUDE.md files)");
548
+ lines.push("");
549
+ for (const p of withSummaries.sort((a, b) => a.name.localeCompare(b.name))) {
550
+ lines.push(`### ${p.name}`);
551
+ lines.push("");
552
+ // Cap at 500 chars — LLM summaries are short (~200-400 chars) and pass through;
553
+ // truncated raw content may be longer and gets capped here.
554
+ const summary = p.claudeMdSummary!;
555
+ if (summary.length > 500) {
556
+ lines.push(summary.slice(0, 500) + "…");
557
+ } else {
558
+ lines.push(summary);
559
+ }
560
+ lines.push("");
561
+ }
562
+ }
563
+
564
+ return lines.join("\n");
565
+ }
566
+
567
+ // ---------------------------------------------------------------------------
568
+ // Architecture from baseline scan
569
+ // ---------------------------------------------------------------------------
570
+
571
+ export function renderArchitectureMd(b: Baseline): string {
572
+ const lines: string[] = [];
573
+ lines.push("# Architecture");
574
+ lines.push("");
575
+ lines.push(
576
+ "> Auto-populated from an ashlr baseline scan at genome init. Edit freely to",
577
+ "> capture intent and tradeoffs that a scanner cannot see.",
578
+ );
579
+ lines.push("");
580
+ lines.push("## Snapshot");
581
+ lines.push("");
582
+ lines.push(`- **Files scanned:** ${b.fileCount}${b.truncated ? " (truncated)" : ""}`);
583
+ lines.push(`- **Runtime:** ${b.runtime.name}`);
584
+ if (b.runtime.notes.length > 0) {
585
+ lines.push(`- **Runtime notes:** ${b.runtime.notes.join("; ")}`);
586
+ }
587
+ if (b.topExtensions.length > 0) {
588
+ lines.push(
589
+ `- **Top extensions:** ${b.topExtensions.map((e) => `${e.ext} (${e.count})`).join(", ")}`,
590
+ );
591
+ }
592
+ if (b.entryPoints.length > 0) {
593
+ lines.push(`- **Entry points:** ${b.entryPoints.slice(0, 5).join(", ")}`);
594
+ }
595
+ if (b.tests.count > 0) {
596
+ lines.push(
597
+ `- **Tests:** ${b.tests.count} files${b.tests.framework ? ` via ${b.tests.framework}` : ""}`,
598
+ );
599
+ }
600
+ if (b.largestFiles.length > 0) {
601
+ lines.push("");
602
+ lines.push("## Largest source files");
603
+ lines.push("");
604
+ for (const f of b.largestFiles) {
605
+ lines.push(`- \`${f.path}\` — ${f.loc} LOC`);
606
+ }
607
+ }
608
+ lines.push("");
609
+ lines.push("## Top-level layout");
610
+ lines.push("");
611
+ lines.push("```");
612
+ lines.push(renderTopLevelTree(b));
613
+ lines.push("```");
614
+ lines.push("");
615
+ lines.push("## Notes");
616
+ lines.push("");
617
+ lines.push("- Replace this section with an intent-level description: why does each top-level dir exist, what does it own, and what crosses its boundary?");
618
+ lines.push("");
619
+ return lines.join("\n");
620
+ }
621
+
622
+ function renderTopLevelTree(b: Baseline): string {
623
+ // Baseline doesn't carry the full file list, so supplement with a shallow
624
+ // readdir of the top level to sketch the project skeleton.
625
+ const topDirs: string[] = [];
626
+ const rootFiles: string[] = [];
627
+ const skip = new Set(["node_modules", "dist", "build", "coverage"]);
628
+ try {
629
+ for (const e of readdirSync(b.dir, { withFileTypes: true })) {
630
+ if (e.name.startsWith(".") && e.name !== ".ashlrcode") continue;
631
+ if (skip.has(e.name)) continue;
632
+ if (e.isDirectory()) topDirs.push(e.name + "/");
633
+ else rootFiles.push(e.name);
634
+ }
635
+ } catch {
636
+ /* ignore — renderer stays best-effort */
637
+ }
638
+
639
+ const lines: string[] = [`${basename(b.dir)}/`];
640
+ for (const d of topDirs.sort()) lines.push(`├── ${d}`);
641
+ for (const f of rootFiles.sort()) lines.push(`├── ${f}`);
642
+ return lines.join("\n");
643
+ }
644
+
645
+ // ---------------------------------------------------------------------------
646
+ // Conventions markdown
647
+ // ---------------------------------------------------------------------------
648
+
649
+ export function renderConventionsMd(det: DetectedConventions): string {
650
+ const lines: string[] = [];
651
+ lines.push("# Conventions");
652
+ lines.push("");
653
+ lines.push(
654
+ "> Auto-populated from detected config files at genome init. Replace with",
655
+ "> the conventions your team actually enforces in review.",
656
+ );
657
+ lines.push("");
658
+ lines.push("## Detected");
659
+ lines.push("");
660
+ for (const d of det.detected) lines.push(`- ${d}`);
661
+ if (det.files.length > 0) {
662
+ lines.push("");
663
+ lines.push("## Source files");
664
+ lines.push("");
665
+ for (const f of det.files) lines.push(`- \`${f}\``);
666
+ }
667
+ lines.push("");
668
+ lines.push("## Team conventions");
669
+ lines.push("");
670
+ lines.push("- _Add conventions that aren't captured in config: naming, file layout, commit style, PR protocol._");
671
+ lines.push("");
672
+ return lines.join("\n");
673
+ }
674
+
675
+ // ---------------------------------------------------------------------------
676
+ // ADR stub
677
+ // ---------------------------------------------------------------------------
678
+
679
+ export function renderDecisionsMd(): string {
680
+ return [
681
+ "# Architectural Decision Records",
682
+ "",
683
+ "> Each non-obvious decision gets an ADR entry. Append — do not rewrite history.",
684
+ "",
685
+ "---",
686
+ "",
687
+ "## ADR-0000: Initialize genome",
688
+ "",
689
+ "- **Status:** Accepted",
690
+ "- **Date:** " + new Date().toISOString().slice(0, 10),
691
+ "- **Context:** This project now uses an ashlr genome so the agent can route",
692
+ " grep/recall through retrieval instead of re-reading files (~-84% token",
693
+ " savings on repeated queries).",
694
+ "- **Decision:** Store durable context in `.ashlrcode/genome/` keyed by the",
695
+ " manifest so retrieval stays cheap and deterministic.",
696
+ "- **Consequences:** Agents must keep the genome current as the project evolves.",
697
+ " Stale knowledge sections degrade retrieval quality.",
698
+ "",
699
+ "---",
700
+ "",
701
+ "## ADR-NNNN: _Template_",
702
+ "",
703
+ "- **Status:** Proposed | Accepted | Superseded",
704
+ "- **Date:** YYYY-MM-DD",
705
+ "- **Context:** …",
706
+ "- **Decision:** …",
707
+ "- **Consequences:** …",
708
+ "",
709
+ ].join("\n");
710
+ }
711
+
712
+ // ---------------------------------------------------------------------------
713
+ // Main init
714
+ // ---------------------------------------------------------------------------
715
+
716
+ export interface InitResult {
717
+ dir: string;
718
+ genomePath: string;
719
+ sectionsCreated: number;
720
+ autoPopulated: string[]; // names of auto-populated files
721
+ minimal: boolean;
722
+ usedOllama: boolean; // true iff an Ollama summary actually succeeded
723
+ ollamaModel?: string; // name of the model that produced summaries
724
+ }
725
+
726
+ export async function runInit(args: CliArgs): Promise<InitResult> {
727
+ if (!args.dir) {
728
+ throw new Error("--dir <path> is required");
729
+ }
730
+ const cwd = resolve(args.dir);
731
+ if (!existsSync(cwd)) {
732
+ throw new Error(`Directory does not exist: ${cwd}`);
733
+ }
734
+
735
+ if (genomeExists(cwd)) {
736
+ if (!args.force) {
737
+ throw new Error(
738
+ `Genome already exists at ${genomeDir(cwd)}. Re-run with --force to overwrite.`,
739
+ );
740
+ }
741
+ // Wipe and recreate.
742
+ await rm(genomeDir(cwd), { recursive: true, force: true });
743
+ }
744
+
745
+ const project = basename(cwd) || "project";
746
+
747
+ // Use core-efficiency's initGenome for the base scaffold.
748
+ await initGenome(cwd, {
749
+ project,
750
+ vision:
751
+ "_Describe the ultimate end-state of this project in one or two sentences. Edit this file — the agent reads it before every significant task._",
752
+ milestone: "Initial setup",
753
+ });
754
+
755
+ // Always overwrite knowledge/decisions.md with our richer ADR stub.
756
+ await writeSection(cwd, "knowledge/decisions.md", renderDecisionsMd(), {
757
+ title: "Decisions",
758
+ summary: "Architectural decision records with rationale",
759
+ tags: ["knowledge", "decisions", "adr", "rationale"],
760
+ });
761
+
762
+ const autoPopulated: string[] = [];
763
+ let actualOllamaModel: string | undefined;
764
+
765
+ if (!args.minimal) {
766
+ // Architecture — from baseline scan. Write to knowledge/architecture.md
767
+ // (core-efficiency already creates vision/architecture.md; we layer a
768
+ // concrete, scanned version under knowledge/ per the plugin's spec).
769
+ try {
770
+ const baseline = scan({ dir: cwd, noCache: true });
771
+ await writeSection(
772
+ cwd,
773
+ "knowledge/architecture.md",
774
+ renderArchitectureMd(baseline),
775
+ {
776
+ title: "Architecture (scanned)",
777
+ summary: "Auto-populated architecture snapshot from baseline scan",
778
+ tags: ["knowledge", "architecture", "scanned", "structure"],
779
+ },
780
+ );
781
+ autoPopulated.push("architecture.md (from baseline scan)");
782
+ } catch (e) {
783
+ // Degrade gracefully — still write a stub so the section exists.
784
+ await writeSection(
785
+ cwd,
786
+ "knowledge/architecture.md",
787
+ `# Architecture\n\n> Baseline scan failed: ${String(e)}. Fill this in manually.\n`,
788
+ {
789
+ title: "Architecture",
790
+ summary: "Architecture (scan failed, manual fill required)",
791
+ tags: ["knowledge", "architecture", "stub"],
792
+ },
793
+ );
794
+ }
795
+
796
+ // Conventions
797
+ const det = detectConventions(cwd);
798
+ await writeSection(cwd, "knowledge/conventions.md", renderConventionsMd(det), {
799
+ title: "Conventions",
800
+ summary: "Auto-populated conventions detected from config files",
801
+ tags: ["knowledge", "conventions", "style", "lint"],
802
+ });
803
+ autoPopulated.push("conventions.md (from config files)");
804
+
805
+ // Workspace discovery — scan child dirs for related projects
806
+ try {
807
+ // Drive a spinner (scan phase) and progress bar (Ollama summaries phase)
808
+ // from the outside so discoverProjects stays testable/silent by default.
809
+ const spinner = new Spinner(
810
+ `Discovering projects in ${basename(cwd)}${sym.ellipsis}`,
811
+ );
812
+ spinner.start();
813
+ let bar: ProgressBar | null = null;
814
+
815
+ const graph = await discoverProjects(cwd, {
816
+ summarize: args.summarize,
817
+ progress: {
818
+ onScanned: (total) => {
819
+ spinner.update(
820
+ `Scanned ${total} ${total === 1 ? "directory" : "directories"}, analyzing context${sym.ellipsis}`,
821
+ );
822
+ },
823
+ onSummarizeStart: (total, model) => {
824
+ spinner.stop();
825
+ if (total > 0) {
826
+ bar = new ProgressBar({ width: 20 });
827
+ bar.start(total, `Summarizing via ${model}`);
828
+ }
829
+ },
830
+ onSummarized: () => {
831
+ bar?.increment();
832
+ },
833
+ },
834
+ });
835
+ if (bar) {
836
+ (bar as ProgressBar).finish();
837
+ } else {
838
+ spinner.succeed(
839
+ `Discovered ${graph.projects.length} ${graph.projects.length === 1 ? "project" : "projects"}`,
840
+ );
841
+ }
842
+ actualOllamaModel = graph.ollamaModel;
843
+ if (graph.projects.length > 0) {
844
+ const gitCount = graph.projects.filter((p) => p.isGitRepo).length;
845
+ const claudeCount = graph.projects.filter((p) => p.hasClaudeMd || p.hasClaudeDir).length;
846
+
847
+ await writeSection(
848
+ cwd,
849
+ "knowledge/workspace.md",
850
+ renderWorkspaceMd(graph),
851
+ {
852
+ title: "Workspace (discovered)",
853
+ summary: `${gitCount} git repos and ${claudeCount} projects with CLAUDE.md discovered in workspace`,
854
+ tags: [
855
+ "knowledge", "workspace", "projects", "related",
856
+ // Include org names as tags for retrieval
857
+ ...[...graph.orgs.keys()],
858
+ ],
859
+ },
860
+ );
861
+ autoPopulated.push(
862
+ `workspace.md (${gitCount} repos, ${claudeCount} with context)`,
863
+ );
864
+ }
865
+ } catch {
866
+ // Workspace discovery is best-effort — don't fail init
867
+ }
868
+ } else {
869
+ // Minimal mode: still create stubs so the 6-section contract holds.
870
+ await writeSection(
871
+ cwd,
872
+ "knowledge/architecture.md",
873
+ "# Architecture\n\n_Describe the high-level structure of this project._\n",
874
+ {
875
+ title: "Architecture",
876
+ summary: "Architecture stub (minimal init)",
877
+ tags: ["knowledge", "architecture", "stub"],
878
+ },
879
+ );
880
+ await writeSection(
881
+ cwd,
882
+ "knowledge/conventions.md",
883
+ "# Conventions\n\n_Capture lint/format/test conventions and team norms._\n",
884
+ {
885
+ title: "Conventions",
886
+ summary: "Conventions stub (minimal init)",
887
+ tags: ["knowledge", "conventions", "stub"],
888
+ },
889
+ );
890
+ }
891
+
892
+ const manifest = await loadManifest(cwd);
893
+ const sectionsCreated = manifest ? manifest.sections.length : 0;
894
+
895
+ // Q1 Genome 2.0: opt-in post-commit hook for live commit-diff awareness.
896
+ // Best-effort — skipped silently when cwd isn't a git repo.
897
+ try {
898
+ const { installPostCommitHook } = await import("./install-genome-hooks.ts");
899
+ installPostCommitHook({ cwd, force: false });
900
+ } catch {
901
+ /* best-effort */
902
+ }
903
+
904
+ return {
905
+ dir: cwd,
906
+ genomePath: genomeDir(cwd),
907
+ sectionsCreated,
908
+ autoPopulated,
909
+ minimal: args.minimal,
910
+ usedOllama: !!actualOllamaModel,
911
+ ollamaModel: actualOllamaModel,
912
+ };
913
+ }
914
+
915
+ // ---------------------------------------------------------------------------
916
+ // CLI
917
+ // ---------------------------------------------------------------------------
918
+
919
+ export function formatResult(r: InitResult): string {
920
+ // Always emit the "\u2713 Initialized genome at …/" first line the tests
921
+ // rely on. In a TTY we layer color + a summary box on top; in non-TTY
922
+ // environments (pipes, tests, CI) we emit plain ASCII identical to the
923
+ // previous contract.
924
+ const lines: string[] = [];
925
+
926
+ // Headline — colored check + "Initialized genome at …" path.
927
+ const check = isColorEnabled() ? c.green(sym.check) : sym.check;
928
+ const pathStr = isColorEnabled() ? c.cyan(`${r.genomePath}/`) : `${r.genomePath}/`;
929
+ lines.push(`${check} Initialized genome at ${pathStr}`);
930
+
931
+ // Detail rows — dim labels, bold/colored values.
932
+ const label = (s: string): string => (isColorEnabled() ? c.dim(s) : s);
933
+ const num = (n: number): string => (isColorEnabled() ? c.bold(c.brightGreen(String(n))) : String(n));
934
+
935
+ lines.push(
936
+ ` ${label("sections created:")} ${num(r.sectionsCreated)} ${isColorEnabled() ? c.dim("(vision, strategies, knowledge, milestones, meta)") : "(vision, strategies, knowledge, milestones, meta)"}`,
937
+ );
938
+ if (r.autoPopulated.length > 0) {
939
+ const value = isColorEnabled() ? c.yellow(r.autoPopulated.join(", ")) : r.autoPopulated.join(", ");
940
+ lines.push(` ${label("auto-populated: ")} ${value}`);
941
+ } else {
942
+ lines.push(` ${label("auto-populated: ")} ${isColorEnabled() ? c.dim("(minimal — stubs only)") : "(minimal — stubs only)"}`);
943
+ }
944
+ if (r.usedOllama && r.ollamaModel) {
945
+ const model = isColorEnabled() ? c.magenta(r.ollamaModel) : r.ollamaModel;
946
+ lines.push(` ${label("summaries: ")} via local model (${model})`);
947
+ }
948
+ const hasWorkspace = r.autoPopulated.some((s) => s.startsWith("workspace.md"));
949
+ const arrow = isColorEnabled() ? c.magenta(sym.arrow) : sym.arrow;
950
+
951
+ // "Next steps" block.
952
+ lines.push("");
953
+ lines.push(isColorEnabled() ? c.bold("Next:") : "Next:");
954
+ lines.push(
955
+ ` ${arrow} edit ${isColorEnabled() ? c.cyan(`${r.genomePath}/vision/north-star.md`) : `${r.genomePath}/vision/north-star.md`} with your project's north star`,
956
+ );
957
+ if (hasWorkspace) {
958
+ lines.push(
959
+ ` ${arrow} review ${isColorEnabled() ? c.cyan(`${r.genomePath}/knowledge/workspace.md`) : `${r.genomePath}/knowledge/workspace.md`} for discovered projects`,
960
+ );
961
+ }
962
+ const highlight = isColorEnabled()
963
+ ? `${c.bold(c.brightGreen("ashlr__grep"))} ${c.dim("— routes through genome RAG for ~-84% savings")}`
964
+ : "ashlr__grep — routes through genome RAG for ~-84% savings";
965
+ lines.push(` ${arrow} use ${highlight}`);
966
+
967
+ return lines.join("\n");
968
+ }
969
+
970
+ async function main(): Promise<void> {
971
+ const args = parseArgs(process.argv.slice(2));
972
+ try {
973
+ const result = await runInit(args);
974
+ process.stdout.write(formatResult(result) + "\n");
975
+ process.exit(0);
976
+ } catch (e) {
977
+ const msg = e instanceof Error ? e.message : String(e);
978
+ const prefix = isColorEnabled() ? `${c.red(sym.cross)} ${c.bold("ashlr genome-init:")}` : "ashlr genome-init:";
979
+ process.stderr.write(`${prefix} ${msg}\n`);
980
+ process.exit(1);
981
+ }
982
+ }
983
+
984
+ if (import.meta.main) {
985
+ await main();
986
+ }