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,566 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * ashlr-diff-semantic MCP server.
4
+ *
5
+ * Exposes ashlr__diff_semantic — AST-aware (heuristic) diff summarization.
6
+ * A 200-line rename-refactor across 20 files renders as one line.
7
+ *
8
+ * Semantic analysis passes (in order, applied to each file's hunks):
9
+ * 1. Rename detection — if symbol X appears in deletions and Y appears at
10
+ * the same position in additions across >= 3 files, emit
11
+ * "renamed X -> Y (N occurrences across K files)"
12
+ * 2. Signature change — function/method declaration line changed but body
13
+ * lines are identical
14
+ * 3. Formatting-only — diff reduces to whitespace changes only
15
+ * 4. Other — falls through to a compact summary (same output
16
+ * as ashlr__diff summary mode)
17
+ *
18
+ * Savings are persisted to the shared ~/.ashlr/stats.json.
19
+ */
20
+
21
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
22
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
+ import {
24
+ CallToolRequestSchema,
25
+ ListToolsRequestSchema,
26
+ } from "@modelcontextprotocol/sdk/types.js";
27
+ import { existsSync } from "fs";
28
+ import { runWithTimeout } from "./_run-with-timeout";
29
+ import { recordSaving as recordSavingCore } from "./_stats";
30
+ import { summarizeIfLarge, PROMPTS } from "./_summarize";
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Savings wrapper
34
+ // ---------------------------------------------------------------------------
35
+
36
+ async function recordSaving(rawBytes: number, compactBytes: number): Promise<number> {
37
+ return recordSavingCore(rawBytes, compactBytes, "ashlr__diff_semantic");
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Git helpers (mirrors diff-server.ts — no shared dependency to keep servers
42
+ // standalone)
43
+ // ---------------------------------------------------------------------------
44
+
45
+ async function runGit(
46
+ cwd: string,
47
+ args: string[],
48
+ ): Promise<{ ok: boolean; stdout: string; stderr: string }> {
49
+ const res = await runWithTimeout({
50
+ command: "git",
51
+ args: ["-C", cwd, ...args],
52
+ timeoutMs: 15_000,
53
+ });
54
+ return {
55
+ ok: res.exitCode === 0,
56
+ stdout: res.stdout || "",
57
+ stderr: res.stderr || "",
58
+ };
59
+ }
60
+
61
+ async function isGitRepo(cwd: string): Promise<boolean> {
62
+ const r = await runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
63
+ return r.ok && r.stdout.trim() === "true";
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Input
68
+ // ---------------------------------------------------------------------------
69
+
70
+ export interface SemanticDiffArgs {
71
+ /** Skip LLM summarization of long semantic-diff output. */
72
+ bypassSummary?: boolean;
73
+ cwd?: string;
74
+ range?: string;
75
+ staged?: boolean;
76
+ }
77
+
78
+ function buildGitDiffArgs(args: SemanticDiffArgs): string[] {
79
+ if (args.staged) return ["--cached"];
80
+ if (args.range) return [args.range];
81
+ // Default: unstaged working tree
82
+ return [];
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Diff splitting
87
+ // ---------------------------------------------------------------------------
88
+
89
+ interface FileDiff {
90
+ path: string;
91
+ raw: string;
92
+ hunks: Hunk[];
93
+ }
94
+
95
+ interface Hunk {
96
+ header: string;
97
+ lines: string[]; // raw diff lines (+ / - / context), excluding hunk header
98
+ }
99
+
100
+ function splitDiffIntoFiles(raw: string): FileDiff[] {
101
+ const files: FileDiff[] = [];
102
+ if (!raw.trim()) return files;
103
+
104
+ const parts = raw.split(/^diff --git /m);
105
+ for (const part of parts) {
106
+ if (!part.trim()) continue;
107
+ const firstLine = part.split("\n", 1)[0] ?? "";
108
+ const m = firstLine.match(/ b\/(.+)$/);
109
+ const path = m ? m[1]! : firstLine.trim();
110
+ const block = "diff --git " + part;
111
+ files.push({ path, raw: block, hunks: parseHunks(block) });
112
+ }
113
+ return files;
114
+ }
115
+
116
+ function parseHunks(fileBlock: string): Hunk[] {
117
+ const hunks: Hunk[] = [];
118
+ const lines = fileBlock.split("\n");
119
+ let cur: Hunk | null = null;
120
+ for (const ln of lines) {
121
+ if (ln.startsWith("@@")) {
122
+ if (cur) hunks.push(cur);
123
+ cur = { header: ln, lines: [] };
124
+ } else if (cur) {
125
+ cur.lines.push(ln);
126
+ }
127
+ }
128
+ if (cur) hunks.push(cur);
129
+ return hunks;
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Heuristic analysis
134
+ // ---------------------------------------------------------------------------
135
+
136
+ // -- 1. Formatting-only detection -------------------------------------------
137
+
138
+ /**
139
+ * Returns true when every changed line (+ or -) is identical after stripping
140
+ * all whitespace — i.e. the diff is purely a reformatting.
141
+ */
142
+ function isFormattingOnly(hunks: Hunk[]): boolean {
143
+ if (hunks.length === 0) return false;
144
+ const added: string[] = [];
145
+ const deleted: string[] = [];
146
+ for (const h of hunks) {
147
+ for (const ln of h.lines) {
148
+ if (ln.startsWith("+") && !ln.startsWith("+++")) {
149
+ added.push(ln.slice(1).replace(/\s/g, ""));
150
+ } else if (ln.startsWith("-") && !ln.startsWith("---")) {
151
+ deleted.push(ln.slice(1).replace(/\s/g, ""));
152
+ }
153
+ }
154
+ }
155
+ if (added.length === 0 && deleted.length === 0) return false;
156
+ if (added.length !== deleted.length) return false;
157
+ for (let i = 0; i < added.length; i++) {
158
+ if (added[i] !== deleted[i]) return false;
159
+ }
160
+ return true;
161
+ }
162
+
163
+ // -- 2. Signature-change detection ------------------------------------------
164
+
165
+ // Patterns that identify function/method/class declaration lines.
166
+ const DECL_RE =
167
+ /^\s*(export\s+)?(default\s+)?(async\s+)?(function\s+\w|class\s+\w|\w+\s*[=:]\s*(async\s+)?\(|def\s+\w|fn\s+\w|func\s+\w|public\s+|private\s+|protected\s+|static\s+)/;
168
+
169
+ interface SignatureChange {
170
+ file: string;
171
+ from: string;
172
+ to: string;
173
+ }
174
+
175
+ function detectSignatureChanges(files: FileDiff[]): SignatureChange[] {
176
+ const changes: SignatureChange[] = [];
177
+ for (const file of files) {
178
+ for (const hunk of file.hunks) {
179
+ const dels = hunk.lines.filter(
180
+ (l) => l.startsWith("-") && !l.startsWith("---"),
181
+ );
182
+ const adds = hunk.lines.filter(
183
+ (l) => l.startsWith("+") && !l.startsWith("+++"),
184
+ );
185
+ if (dels.length !== adds.length) continue;
186
+ // All deleted lines must be declaration lines; body lines must match
187
+ for (let i = 0; i < dels.length; i++) {
188
+ const delLine = dels[i]!.slice(1);
189
+ const addLine = adds[i]!.slice(1);
190
+ if (!DECL_RE.test(delLine) && !DECL_RE.test(addLine)) continue;
191
+ if (delLine.replace(/\s/g, "") === addLine.replace(/\s/g, "")) continue;
192
+ // Check context (non-+/- lines) are purely body — no extra changes
193
+ const contextLines = hunk.lines.filter(
194
+ (l) => !l.startsWith("+") && !l.startsWith("-"),
195
+ );
196
+ const changedLines = hunk.lines.filter(
197
+ (l) =>
198
+ (l.startsWith("+") && !l.startsWith("+++")) ||
199
+ (l.startsWith("-") && !l.startsWith("---")),
200
+ );
201
+ // Signature-only: only one pair of changed lines per hunk, rest is context
202
+ if (changedLines.length === 2 && contextLines.length > 0) {
203
+ changes.push({
204
+ file: file.path,
205
+ from: delLine.trim(),
206
+ to: addLine.trim(),
207
+ });
208
+ }
209
+ }
210
+ }
211
+ }
212
+ return changes;
213
+ }
214
+
215
+ // -- 3. Rename detection -----------------------------------------------------
216
+
217
+ /**
218
+ * Rename detection heuristic:
219
+ *
220
+ * For each hunk, extract deleted tokens and added tokens (identifiers only —
221
+ * [A-Za-z_][A-Za-z0-9_]*). Build a positional diff: for each position i,
222
+ * if deleted[i] != added[i], record the pair (deleted[i], added[i]).
223
+ *
224
+ * Then, across ALL files, if the same (from, to) pair appears in >= 3 files,
225
+ * it's considered a rename. We pick the most-frequent pair and emit it.
226
+ *
227
+ * False-positive scenarios addressed:
228
+ * - Common English words / short tokens (< 3 chars) are skipped.
229
+ * - Pairs where `from === to` are ignored (no-op).
230
+ * - We require >= 3 *files* (not just occurrences) to call it a rename.
231
+ * - A single giant churn that replaces many unrelated tokens won't fire
232
+ * because positional matching limits scope to tokens at the same index.
233
+ */
234
+
235
+ interface RenameCandidate {
236
+ from: string;
237
+ to: string;
238
+ files: Set<string>;
239
+ occurrences: number;
240
+ }
241
+
242
+ const IDENT_RE = /[A-Za-z_][A-Za-z0-9_]*/g;
243
+ const MIN_TOKEN_LEN = 3;
244
+ const MIN_FILES_FOR_RENAME = 3;
245
+
246
+ function extractTokens(line: string): string[] {
247
+ return Array.from(line.matchAll(IDENT_RE), (m) => m[0]).filter(
248
+ (t) => t.length >= MIN_TOKEN_LEN,
249
+ );
250
+ }
251
+
252
+ function detectRenames(files: FileDiff[]): RenameCandidate[] {
253
+ // Map: `${from}\0${to}` -> { from, to, files, occurrences }
254
+ const pairMap = new Map<string, RenameCandidate>();
255
+
256
+ for (const file of files) {
257
+ // Track pairs seen in this file to avoid double-counting per-file
258
+ const filePairs = new Set<string>();
259
+
260
+ for (const hunk of file.hunks) {
261
+ const delLines = hunk.lines.filter(
262
+ (l) => l.startsWith("-") && !l.startsWith("---"),
263
+ );
264
+ const addLines = hunk.lines.filter(
265
+ (l) => l.startsWith("+") && !l.startsWith("+++"),
266
+ );
267
+
268
+ // Only process hunks where line counts match (substitutions, not pure insertions/deletions)
269
+ if (delLines.length === 0 || addLines.length === 0) continue;
270
+ const pairCount = Math.min(delLines.length, addLines.length);
271
+
272
+ for (let i = 0; i < pairCount; i++) {
273
+ const delTokens = extractTokens(delLines[i]!.slice(1));
274
+ const addTokens = extractTokens(addLines[i]!.slice(1));
275
+
276
+ // Positional matching: same index, different token
277
+ const limit = Math.min(delTokens.length, addTokens.length);
278
+ for (let j = 0; j < limit; j++) {
279
+ const from = delTokens[j]!;
280
+ const to = addTokens[j]!;
281
+ if (from === to) continue;
282
+ const key = `${from}\0${to}`;
283
+ filePairs.add(key);
284
+ }
285
+ }
286
+ }
287
+
288
+ // Now credit each unique pair to this file
289
+ for (const key of filePairs) {
290
+ const [from, to] = key.split("\0") as [string, string];
291
+ let entry = pairMap.get(key);
292
+ if (!entry) {
293
+ entry = { from, to, files: new Set(), occurrences: 0 };
294
+ pairMap.set(key, entry);
295
+ }
296
+ entry.files.add(file.path);
297
+ entry.occurrences++;
298
+ }
299
+ }
300
+
301
+ // Filter: must appear in >= MIN_FILES_FOR_RENAME distinct files
302
+ return Array.from(pairMap.values())
303
+ .filter((c) => c.files.size >= MIN_FILES_FOR_RENAME)
304
+ .sort((a, b) => b.files.size - a.files.size || b.occurrences - a.occurrences);
305
+ }
306
+
307
+ // ---------------------------------------------------------------------------
308
+ // Numstat helpers (for footer)
309
+ // ---------------------------------------------------------------------------
310
+
311
+ interface FileStat {
312
+ added: number;
313
+ deleted: number;
314
+ path: string;
315
+ }
316
+
317
+ function parseNumstat(raw: string): FileStat[] {
318
+ const out: FileStat[] = [];
319
+ for (const line of raw.split("\n")) {
320
+ if (!line) continue;
321
+ const parts = line.split("\t");
322
+ if (parts.length < 3) continue;
323
+ const [a, d, ...rest] = parts;
324
+ out.push({
325
+ added: a === "-" ? 0 : Number(a) || 0,
326
+ deleted: d === "-" ? 0 : Number(d) || 0,
327
+ path: rest.join("\t"),
328
+ });
329
+ }
330
+ return out;
331
+ }
332
+
333
+ function fmtKB(bytes: number): string {
334
+ return (bytes / 1024).toFixed(1) + " KB";
335
+ }
336
+
337
+ // ---------------------------------------------------------------------------
338
+ // Compact fallback summary (mirrors diff-server.ts summary mode logic)
339
+ // ---------------------------------------------------------------------------
340
+
341
+ function compactSummary(files: FileStat[], rawDiff: string): string {
342
+ if (files.length === 0) return "(no changes)";
343
+ const totalAdd = files.reduce((s, f) => s + f.added, 0);
344
+ const totalDel = files.reduce((s, f) => s + f.deleted, 0);
345
+ const lines: string[] = [
346
+ `${files.length} file${files.length === 1 ? "" : "s"} changed · +${totalAdd} -${totalDel} lines`,
347
+ ];
348
+ // Show up to 5 files with their line counts
349
+ for (const f of files.slice(0, 5)) {
350
+ lines.push(` ${f.path} +${f.added} -${f.deleted}`);
351
+ }
352
+ if (files.length > 5) lines.push(` ... and ${files.length - 5} more file(s)`);
353
+ void rawDiff;
354
+ return lines.join("\n");
355
+ }
356
+
357
+ // ---------------------------------------------------------------------------
358
+ // Main semantic analysis
359
+ // ---------------------------------------------------------------------------
360
+
361
+ export async function ashlrDiffSemantic(args: SemanticDiffArgs): Promise<string> {
362
+ const cwd = args.cwd ?? process.cwd();
363
+ if (!existsSync(cwd)) throw new Error(`cwd does not exist: ${cwd}`);
364
+ if (!await isGitRepo(cwd)) throw new Error(`not a git repository: ${cwd}`);
365
+
366
+ const diffArgs = buildGitDiffArgs(args);
367
+
368
+ // Raw diff — what Claude Code would have read without us
369
+ const rawResult = await runGit(cwd, ["diff", ...diffArgs]);
370
+ if (!rawResult.ok) {
371
+ throw new Error(`git diff failed: ${rawResult.stderr.trim() || "unknown error"}`);
372
+ }
373
+ const rawDiff = rawResult.stdout;
374
+ const rawBytes = Buffer.byteLength(rawDiff, "utf-8");
375
+
376
+ // Numstat for summary fallback and footer
377
+ const numstatResult = await runGit(cwd, ["diff", "--numstat", ...diffArgs]);
378
+ const fileStats = numstatResult.ok ? parseNumstat(numstatResult.stdout) : [];
379
+
380
+ // Parse diff into per-file structures
381
+ const fileDiffs = splitDiffIntoFiles(rawDiff);
382
+
383
+ // -- Analysis passes -------------------------------------------------------
384
+
385
+ const formattingFiles: string[] = [];
386
+ const otherFiles: FileStat[] = [];
387
+
388
+ for (const fd of fileDiffs) {
389
+ if (isFormattingOnly(fd.hunks)) {
390
+ formattingFiles.push(fd.path);
391
+ }
392
+ }
393
+
394
+ // Files that are NOT formatting-only go through other passes
395
+ const nonFormattingDiffs = fileDiffs.filter(
396
+ (fd) => !formattingFiles.includes(fd.path),
397
+ );
398
+
399
+ const renames = detectRenames(nonFormattingDiffs);
400
+ const sigChanges = detectSignatureChanges(nonFormattingDiffs);
401
+
402
+ // Build the set of files "explained" by rename/signature analysis
403
+ const explainedPaths = new Set<string>();
404
+ for (const r of renames) {
405
+ for (const p of r.files) explainedPaths.add(p);
406
+ }
407
+ for (const s of sigChanges) {
408
+ explainedPaths.add(s.file);
409
+ }
410
+
411
+ // Remaining files with actual changes not explained above
412
+ for (const fs of fileStats) {
413
+ if (!formattingFiles.includes(fs.path) && !explainedPaths.has(fs.path)) {
414
+ otherFiles.push(fs);
415
+ }
416
+ }
417
+
418
+ const hasSemanticContent =
419
+ renames.length > 0 ||
420
+ sigChanges.length > 0 ||
421
+ formattingFiles.length > 0;
422
+
423
+ // -- Render output ---------------------------------------------------------
424
+
425
+ const sections: string[] = [];
426
+
427
+ if (renames.length > 0) {
428
+ const lines: string[] = [`renames: ${renames.length} detected`];
429
+ for (const r of renames) {
430
+ lines.push(
431
+ ` ${r.from} -> ${r.to} (${r.occurrences} occurrence${r.occurrences === 1 ? "" : "s"} across ${r.files.size} file${r.files.size === 1 ? "" : "s"})`,
432
+ );
433
+ }
434
+ sections.push(lines.join("\n"));
435
+ }
436
+
437
+ if (sigChanges.length > 0) {
438
+ const lines: string[] = [`signature changes: ${sigChanges.length}`];
439
+ for (const sc of sigChanges) {
440
+ // Truncate long signatures for compactness
441
+ const fromShort =
442
+ sc.from.length > 80 ? sc.from.slice(0, 77) + "..." : sc.from;
443
+ const toShort = sc.to.length > 80 ? sc.to.slice(0, 77) + "..." : sc.to;
444
+ lines.push(` ${sc.file}: ${fromShort} -> ${toShort}`);
445
+ }
446
+ sections.push(lines.join("\n"));
447
+ }
448
+
449
+ if (formattingFiles.length > 0) {
450
+ sections.push(
451
+ `formatting-only: ${formattingFiles.length} file${formattingFiles.length === 1 ? "" : "s"}\n` +
452
+ formattingFiles.map((f) => ` ${f}`).join("\n"),
453
+ );
454
+ }
455
+
456
+ let body: string;
457
+
458
+ if (!hasSemanticContent || otherFiles.length > 0) {
459
+ const otherSummary =
460
+ otherFiles.length > 0 ? compactSummary(otherFiles, rawDiff) : null;
461
+
462
+ if (!hasSemanticContent) {
463
+ // Full degradation: behave exactly like ashlr__diff compact output
464
+ body = compactSummary(fileStats, rawDiff);
465
+ } else {
466
+ // Mixed: semantic sections + compact summary for remainder
467
+ if (otherSummary) sections.push(`other changes:\n${otherSummary}`);
468
+ body = sections.join("\n\n");
469
+ }
470
+ } else {
471
+ body = sections.join("\n\n");
472
+ }
473
+
474
+ // -- Footer ----------------------------------------------------------------
475
+
476
+ const compactBytes = Buffer.byteLength(body, "utf-8");
477
+ const footerRaw = fmtKB(rawBytes);
478
+ const footerCompact = fmtKB(compactBytes);
479
+ const footer = `\n\n[ashlr__diff_semantic] raw: ${footerRaw} · compact: ${footerCompact} · ${renames.length} rename${renames.length === 1 ? "" : "s"} detected`;
480
+
481
+ const fullOutput = body + footer;
482
+
483
+ // Pipe large semantic-diff output through the shared LLM summarizer so a
484
+ // 300-file refactor doesn't hand Claude 40KB of rename rows. Opt out via
485
+ // bypassSummary so tests and power users can still see the raw body.
486
+ const DIFF_SEMANTIC_LLM_THRESHOLD = 8 * 1024;
487
+ const summResult = await summarizeIfLarge(fullOutput, {
488
+ toolName: "ashlr__diff_semantic",
489
+ systemPrompt: PROMPTS.diff_semantic,
490
+ thresholdBytes: DIFF_SEMANTIC_LLM_THRESHOLD,
491
+ bypass: args.bypassSummary === true,
492
+ });
493
+ const outputText = summResult.text;
494
+
495
+ await recordSaving(rawBytes, Buffer.byteLength(outputText, "utf-8"));
496
+
497
+ return outputText;
498
+ }
499
+
500
+ // ---------------------------------------------------------------------------
501
+ // MCP wiring
502
+ // ---------------------------------------------------------------------------
503
+
504
+ const server = new Server(
505
+ { name: "ashlr-diff-semantic", version: "0.1.0" },
506
+ { capabilities: { tools: {} } },
507
+ );
508
+
509
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
510
+ tools: [
511
+ {
512
+ name: "ashlr__diff_semantic",
513
+ description:
514
+ "Semantic git diff with AST-aware summarization. Use instead of ashlr__diff when " +
515
+ "reviewing refactors, renames, or large reformatting commits — a 200-line rename " +
516
+ "across 20 files renders as one line. Compresses by detecting symbol renames (>=3 " +
517
+ "files), signature-only changes, and formatting-only diffs; typical savings 85–99% " +
518
+ "on refactor commits. Degrades gracefully to compact diff when no semantic patterns " +
519
+ "are found. For ordinary diff viewing use ashlr__diff instead.",
520
+ inputSchema: {
521
+ type: "object",
522
+ properties: {
523
+ cwd: { type: "string", description: "Working directory (default: process.cwd())" },
524
+ range: {
525
+ type: "string",
526
+ description:
527
+ "Git range to diff, e.g. 'HEAD~1..HEAD'. Default: unstaged working tree changes.",
528
+ },
529
+ staged: {
530
+ type: "boolean",
531
+ description: "If true, diff staged changes (--cached). Overrides range.",
532
+ },
533
+ bypassSummary: {
534
+ type: "boolean",
535
+ description: "Skip LLM summarization of long semantic-diff output (default: false).",
536
+ },
537
+ },
538
+ },
539
+ },
540
+ ],
541
+ }));
542
+
543
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
544
+ const { name, arguments: args } = req.params;
545
+ try {
546
+ if (name === "ashlr__diff_semantic") {
547
+ const text = await ashlrDiffSemantic((args ?? {}) as SemanticDiffArgs);
548
+ return { content: [{ type: "text", text }] };
549
+ }
550
+ return {
551
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
552
+ isError: true,
553
+ };
554
+ } catch (err) {
555
+ const message = err instanceof Error ? err.message : String(err);
556
+ return {
557
+ content: [{ type: "text", text: `ashlr__diff_semantic error: ${message}` }],
558
+ isError: true,
559
+ };
560
+ }
561
+ });
562
+
563
+ if (import.meta.main) {
564
+ const transport = new StdioServerTransport();
565
+ await server.connect(transport);
566
+ }