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.
- package/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +39 -0
- package/.claude-plugin/plugin.json +36 -0
- package/.codex/agents/README.md +9 -0
- package/.codex/agents/ashlr-explorer.md +36 -0
- package/.codex/agents/ashlr-worker.md +38 -0
- package/.codex-plugin/plugin.json +45 -0
- package/.mcp.json +14 -0
- package/LICENSE +21 -0
- package/README.md +311 -0
- package/agents/ashlr-code.md +69 -0
- package/agents/ashlr-explore.md +44 -0
- package/agents/ashlr-plan.md +54 -0
- package/bench/fixtures/notebook-large.ipynb +660 -0
- package/bench/fixtures/notebook-small.ipynb +39 -0
- package/bench/fixtures/tasklist-closed.json +62 -0
- package/bench/fixtures/tasklist-large-mixed.json +27 -0
- package/bench/fixtures/tasklist-sprint-open.json +79 -0
- package/bench/fixtures/websearch-bun-runtime.json +32 -0
- package/bench/fixtures/websearch-react-hooks-patterns.json +32 -0
- package/bench/fixtures/websearch-typescript-generics.json +38 -0
- package/bench/refs/node-sdk/.refrev +1 -0
- package/bench/refs/node-sdk/README.md +17 -0
- package/bench/refs/node-sdk/src/agent/agent.ts +163 -0
- package/bench/refs/node-sdk/src/agent/create-agent-ui-stream.ts +83 -0
- package/bench/refs/node-sdk/src/agent/tool-loop-agent.ts +238 -0
- package/bench/refs/node-sdk/src/embed/embed-many.ts +409 -0
- package/bench/refs/node-sdk/src/embed/embed-result.ts +50 -0
- package/bench/refs/node-sdk/src/embed/embed.ts +257 -0
- package/bench/refs/node-sdk/src/error/invalid-argument-error.ts +34 -0
- package/bench/refs/node-sdk/src/error/invalid-tool-input-error.ts +33 -0
- package/bench/refs/node-sdk/src/error/no-object-generated-error.ts +70 -0
- package/bench/refs/node-sdk/src/generate-image/generate-image-result.ts +42 -0
- package/bench/refs/node-sdk/src/generate-image/generate-image.ts +362 -0
- package/bench/refs/node-sdk/src/generate-object/generate-object-result.ts +67 -0
- package/bench/refs/node-sdk/src/generate-object/generate-object.ts +501 -0
- package/bench/refs/node-sdk/src/generate-object/inject-json-instruction.ts +30 -0
- package/bench/refs/node-sdk/src/generate-object/output-strategy.ts +415 -0
- package/bench/refs/node-sdk/src/generate-object/repair-text.ts +12 -0
- package/bench/refs/node-sdk/src/generate-object/stream-object.ts +960 -0
- package/bench/refs/node-sdk/src/generate-speech/generate-speech.ts +191 -0
- package/bench/refs/node-sdk/src/generate-text/generate-text.ts +1484 -0
- package/bench/refs/node-sdk/src/generate-text/stream-text.ts +2708 -0
- package/bench/refs/node-sdk/src/generate-text/tool-call.ts +59 -0
- package/bench/refs/node-sdk/src/generate-text/tool-result.ts +36 -0
- package/bench/refs/node-sdk/src/telemetry/create-telemetry-dispatcher.ts +146 -0
- package/bench/refs/node-sdk/src/telemetry/telemetry-registry.ts +15 -0
- package/bench/refs/node-sdk/src/telemetry/telemetry.ts +223 -0
- package/bench/refs/node-sdk/src/util/async-iterable-stream.ts +94 -0
- package/bench/refs/node-sdk/src/util/consume-stream.ts +31 -0
- package/bench/refs/node-sdk/src/util/create-stitchable-stream.ts +112 -0
- package/bench/refs/node-sdk/src/util/download/download.ts +74 -0
- package/bench/refs/node-sdk/src/util/error-handler.ts +1 -0
- package/bench/refs/node-sdk/src/util/fix-json.ts +401 -0
- package/bench/refs/node-sdk/src/util/merge-objects.ts +79 -0
- package/bench/refs/python-lib/.refrev +1 -0
- package/bench/refs/python-lib/README.md +18 -0
- package/bench/refs/python-lib/pandas/core/_numba/executor.py +249 -0
- package/bench/refs/python-lib/pandas/core/_numba/extensions.py +586 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/mean_.py +198 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/min_max_.py +178 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/sum_.py +255 -0
- package/bench/refs/python-lib/pandas/core/_numba/kernels/var_.py +251 -0
- package/bench/refs/python-lib/pandas/core/accessor.py +520 -0
- package/bench/refs/python-lib/pandas/core/api.py +138 -0
- package/bench/refs/python-lib/pandas/core/array_algos/masked_accumulations.py +97 -0
- package/bench/refs/python-lib/pandas/core/array_algos/masked_reductions.py +252 -0
- package/bench/refs/python-lib/pandas/core/array_algos/putmask.py +150 -0
- package/bench/refs/python-lib/pandas/core/array_algos/quantile.py +232 -0
- package/bench/refs/python-lib/pandas/core/array_algos/replace.py +159 -0
- package/bench/refs/python-lib/pandas/core/array_algos/take.py +609 -0
- package/bench/refs/python-lib/pandas/core/arraylike.py +534 -0
- package/bench/refs/python-lib/pandas/core/arrays/_arrow_string_mixins.py +434 -0
- package/bench/refs/python-lib/pandas/core/arrays/_mixins.py +630 -0
- package/bench/refs/python-lib/pandas/core/arrays/_ranges.py +276 -0
- package/bench/refs/python-lib/pandas/core/arrays/_utils.py +77 -0
- package/bench/refs/python-lib/pandas/core/arrays/arrow/accessors.py +521 -0
- package/bench/refs/python-lib/pandas/core/arrays/arrow/extension_types.py +174 -0
- package/bench/refs/python-lib/pandas/core/arrays/boolean.py +454 -0
- package/bench/refs/python-lib/pandas/core/arrays/floating.py +192 -0
- package/bench/refs/python-lib/pandas/core/arrays/integer.py +296 -0
- package/bench/refs/python-lib/pandas/core/arrays/masked.py +2140 -0
- package/bench/refs/python-lib/pandas/core/arrays/numeric.py +305 -0
- package/bench/refs/python-lib/pandas/core/arrays/numpy_.py +701 -0
- package/bench/refs/python-lib/pandas/core/arrays/period.py +1586 -0
- package/bench/refs/python-lib/pandas/core/arrays/sparse/accessor.py +505 -0
- package/bench/refs/python-lib/pandas/core/arrays/sparse/scipy_sparse.py +208 -0
- package/bench/refs/rust-project/.refrev +1 -0
- package/bench/refs/rust-project/README.md +17 -0
- package/bench/refs/rust-project/tokio/src/doc/os.rs +68 -0
- package/bench/refs/rust-project/tokio/src/fs/dir_builder.rs +129 -0
- package/bench/refs/rust-project/tokio/src/fs/file.rs +1113 -0
- package/bench/refs/rust-project/tokio/src/fs/mocks.rs +176 -0
- package/bench/refs/rust-project/tokio/src/fs/mod.rs +324 -0
- package/bench/refs/rust-project/tokio/src/fs/open_options.rs +856 -0
- package/bench/refs/rust-project/tokio/src/fs/read.rs +80 -0
- package/bench/refs/rust-project/tokio/src/fs/read_dir.rs +357 -0
- package/bench/refs/rust-project/tokio/src/fs/write.rs +99 -0
- package/bench/refs/rust-project/tokio/src/future/maybe_done.rs +123 -0
- package/bench/refs/rust-project/tokio/src/future/try_join.rs +82 -0
- package/bench/refs/rust-project/tokio/src/io/async_buf_read.rs +117 -0
- package/bench/refs/rust-project/tokio/src/io/async_read.rs +133 -0
- package/bench/refs/rust-project/tokio/src/io/async_seek.rs +96 -0
- package/bench/refs/rust-project/tokio/src/io/async_write.rs +402 -0
- package/bench/refs/rust-project/tokio/src/io/blocking.rs +339 -0
- package/bench/refs/rust-project/tokio/src/io/interest.rs +345 -0
- package/bench/refs/rust-project/tokio/src/io/join.rs +130 -0
- package/bench/refs/rust-project/tokio/src/io/mod.rs +308 -0
- package/bench/refs/rust-project/tokio/src/io/poll_evented.rs +319 -0
- package/bench/refs/rust-project/tokio/src/io/read_buf.rs +344 -0
- package/bench/refs/rust-project/tokio/src/io/ready.rs +322 -0
- package/bench/refs/rust-project/tokio/src/io/split.rs +166 -0
- package/bench/refs/rust-project/tokio/src/io/stderr.rs +148 -0
- package/bench/refs/rust-project/tokio/src/io/stdin.rs +98 -0
- package/bench/refs/rust-project/tokio/src/io/stdio_common.rs +222 -0
- package/bench/refs/rust-project/tokio/src/io/stdout.rs +197 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_reader.rs +311 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_stream.rs +207 -0
- package/bench/refs/rust-project/tokio/src/io/util/buf_writer.rs +310 -0
- package/bench/refs/rust-project/tokio/src/io/util/chain.rs +144 -0
- package/commands/_spawn-patterns.json +32 -0
- package/commands/ashlr-allow.md +21 -0
- package/commands/ashlr-benchmark.md +53 -0
- package/commands/ashlr-brief.md +97 -0
- package/commands/ashlr-budget.md +76 -0
- package/commands/ashlr-compact.md +54 -0
- package/commands/ashlr-dashboard.md +37 -0
- package/commands/ashlr-doctor.md +56 -0
- package/commands/ashlr-eco-mode.md +92 -0
- package/commands/ashlr-genome-init.md +24 -0
- package/commands/ashlr-genome-keygen.md +36 -0
- package/commands/ashlr-genome-loop.md +118 -0
- package/commands/ashlr-genome-push.md +36 -0
- package/commands/ashlr-genome-rewrap.md +48 -0
- package/commands/ashlr-genome-team-init.md +67 -0
- package/commands/ashlr-handoff.md +23 -0
- package/commands/ashlr-help.md +86 -0
- package/commands/ashlr-hook-timings.md +30 -0
- package/commands/ashlr-leaderboard.md +50 -0
- package/commands/ashlr-ollama-setup.md +39 -0
- package/commands/ashlr-orchestrate-status.md +72 -0
- package/commands/ashlr-orchestrate.md +83 -0
- package/commands/ashlr-parallelize.md +63 -0
- package/commands/ashlr-report-crash.md +33 -0
- package/commands/ashlr-resume.md +72 -0
- package/commands/ashlr-savings.md +24 -0
- package/commands/ashlr-settings.md +50 -0
- package/commands/ashlr-spawn.md +61 -0
- package/commands/ashlr-start.md +60 -0
- package/commands/ashlr-status.md +64 -0
- package/commands/ashlr-team-invite.md +47 -0
- package/commands/ashlr-tier.md +122 -0
- package/commands/ashlr-tour.md +60 -0
- package/commands/ashlr-update.md +155 -0
- package/commands/ashlr-upgrade.md +96 -0
- package/commands/goal.md +58 -0
- package/commands/loop.md +40 -0
- package/docs/CNAME +1 -0
- package/docs/PRO_TIER.md +104 -0
- package/docs/README.md +21 -0
- package/docs/architecture.md +415 -0
- package/docs/ashlr-pipe.md +161 -0
- package/docs/assets/logo.svg +19 -0
- package/docs/assets/og.png +0 -0
- package/docs/assets/og.svg +41 -0
- package/docs/benchmarks-v2.json +355 -0
- package/docs/benchmarks.json +102 -0
- package/docs/benchmarks.md +211 -0
- package/docs/billing.md +185 -0
- package/docs/cloud-genome.md +155 -0
- package/docs/competitive-wozcode.md +92 -0
- package/docs/deploy.md +325 -0
- package/docs/email-templates.md +113 -0
- package/docs/embed-tuning.md +103 -0
- package/docs/genome.md +120 -0
- package/docs/github-oauth-onboarding.md +127 -0
- package/docs/hero-video-script.md +129 -0
- package/docs/index.html +2262 -0
- package/docs/install-prompt.md +78 -0
- package/docs/install-windows.md +132 -0
- package/docs/install.ps1 +171 -0
- package/docs/install.sh +146 -0
- package/docs/launch/README.md +114 -0
- package/docs/launch/hn.md +49 -0
- package/docs/launch/reddit.md +45 -0
- package/docs/launch/x.md +65 -0
- package/docs/launch-post.md +112 -0
- package/docs/legal.md +78 -0
- package/docs/llm-providers.md +99 -0
- package/docs/multi-host-mcp.md +217 -0
- package/docs/multi-turn-architecture.md +108 -0
- package/docs/operations/deployment.md +108 -0
- package/docs/operations/error-monitoring.md +70 -0
- package/docs/operations/weekly-digest.md +70 -0
- package/docs/operations.md +494 -0
- package/docs/platform-support.md +133 -0
- package/docs/policy-packs.md +188 -0
- package/docs/press-kit/fact-sheet.md +78 -0
- package/docs/press-kit/founder-bio.md +3 -0
- package/docs/press-kit/logo.svg +33 -0
- package/docs/press-kit/screenshots.md +146 -0
- package/docs/pricing.md +251 -0
- package/docs/pro-backend-architecture.md +523 -0
- package/docs/producthunt.md +52 -0
- package/docs/router-migration.md +110 -0
- package/docs/session-log-schema.md +68 -0
- package/docs/social.md +95 -0
- package/docs/team-genome.md +528 -0
- package/docs/telemetry.md +194 -0
- package/docs/test-isolation.md +100 -0
- package/docs/upgrade.md +159 -0
- package/hooks/_hook-errors.ts +87 -0
- package/hooks/_hook-perf-emit.ts +113 -0
- package/hooks/_hook-timing-ledger.ts +1133 -0
- package/hooks/_nudge-throttle.ts +127 -0
- package/hooks/_recent-blocks.ts +127 -0
- package/hooks/audit-upload.ts +86 -0
- package/hooks/codex-hooks.json +171 -0
- package/hooks/commit-attribution.ts +179 -0
- package/hooks/edit-batching-nudge.ts +154 -0
- package/hooks/genome-scribe-hook.ts +219 -0
- package/hooks/hooks.json +294 -0
- package/hooks/orient-nudge-hook.ts +242 -0
- package/hooks/policy-enforce.ts +214 -0
- package/hooks/post-tool-use-embedding.ts +93 -0
- package/hooks/post-tool-use-genome.sh +37 -0
- package/hooks/post-tool-use-genome.ts +69 -0
- package/hooks/posttooluse-correlate.ts +122 -0
- package/hooks/posttooluse-dedupe-output.ts +241 -0
- package/hooks/posttooluse-genome-refresh.ts +223 -0
- package/hooks/posttooluse-native-nudge.ts +205 -0
- package/hooks/posttooluse-stale-result.ts +277 -0
- package/hooks/pretooluse-bash.ts +63 -0
- package/hooks/pretooluse-budget-guard.ts +318 -0
- package/hooks/pretooluse-common.ts +985 -0
- package/hooks/pretooluse-eco-router.ts +196 -0
- package/hooks/pretooluse-edit.sh +93 -0
- package/hooks/pretooluse-edit.ts +197 -0
- package/hooks/pretooluse-glob.ts +70 -0
- package/hooks/pretooluse-grep.sh +85 -0
- package/hooks/pretooluse-grep.ts +96 -0
- package/hooks/pretooluse-notebookedit.ts +121 -0
- package/hooks/pretooluse-read.sh +118 -0
- package/hooks/pretooluse-read.ts +118 -0
- package/hooks/pretooluse-task.ts +130 -0
- package/hooks/pretooluse-webfetch.ts +66 -0
- package/hooks/pretooluse-websearch.ts +85 -0
- package/hooks/pretooluse-write.ts +128 -0
- package/hooks/pulse-emit.ts +197 -0
- package/hooks/session-end-consolidate.sh +35 -0
- package/hooks/session-end-consolidate.ts +188 -0
- package/hooks/session-end-stats-gc.ts +80 -0
- package/hooks/session-log-append.sh +94 -0
- package/hooks/session-log-append.ts +116 -0
- package/hooks/session-start.ts +993 -0
- package/hooks/sessionend-hook-health-nudge.ts +451 -0
- package/hooks/sessionend-savings-nudge.ts +318 -0
- package/hooks/sessionend-status-line-nudge.ts +296 -0
- package/hooks/sessionstart-brief.ts +140 -0
- package/hooks/sessionstart-cost-refactor.ts +95 -0
- package/hooks/sessionstart-efficient.ts +110 -0
- package/hooks/sessionstart-genome-author.ts +93 -0
- package/hooks/sessionstart-lean-tools.ts +98 -0
- package/hooks/sessionstart-search.ts +97 -0
- package/hooks/stop-accounting.ts +152 -0
- package/hooks/subagent-stop-rollup.ts +162 -0
- package/hooks/userpromptsubmit-brief-trigger.ts +156 -0
- package/package.json +56 -0
- package/ports/README.md +108 -0
- package/ports/cursor/mcp.json +11 -0
- package/ports/goose/recipe.yaml +26 -0
- package/scripts/ashlr-mcp.ts +40 -0
- package/scripts/auto-update.ts +177 -0
- package/scripts/baseline-scan.ts +831 -0
- package/scripts/bench-orchestrate.ts +377 -0
- package/scripts/benchmark-refs.ts +292 -0
- package/scripts/bootstrap.mjs +100 -0
- package/scripts/brief-eval.ts +234 -0
- package/scripts/bump-version.ts +82 -0
- package/scripts/bun-resolve.d.mts +11 -0
- package/scripts/bun-resolve.mjs +59 -0
- package/scripts/calibrate-grep.ts +467 -0
- package/scripts/capture-grep-workload.ts +260 -0
- package/scripts/check-tier.ts +61 -0
- package/scripts/check-version-sync.ts +97 -0
- package/scripts/cli-orchestrate-status.ts +386 -0
- package/scripts/cli-orchestrate.ts +303 -0
- package/scripts/cli.ts +478 -0
- package/scripts/cloud-smoke-test.ts +313 -0
- package/scripts/coach-report.ts +373 -0
- package/scripts/context-db-migrate.ts +137 -0
- package/scripts/context-status.ts +43 -0
- package/scripts/demo-run.ts +255 -0
- package/scripts/deploy-smoke.sh +140 -0
- package/scripts/doctor.ts +992 -0
- package/scripts/embed-file-worker.ts +104 -0
- package/scripts/embed-tune.ts +308 -0
- package/scripts/errors-report.ts +428 -0
- package/scripts/find-test-leak.ts +69 -0
- package/scripts/generate-badge.ts +357 -0
- package/scripts/genome-auto-consolidate.ts +533 -0
- package/scripts/genome-auto-propose.ts +578 -0
- package/scripts/genome-cloud-pull.ts +363 -0
- package/scripts/genome-cloud-push.ts +382 -0
- package/scripts/genome-cloud-sync.ts +587 -0
- package/scripts/genome-commit-watcher.ts +295 -0
- package/scripts/genome-init.ts +986 -0
- package/scripts/genome-key.ts +233 -0
- package/scripts/genome-keygen.ts +168 -0
- package/scripts/genome-link.ts +59 -0
- package/scripts/genome-refresh-worker.ts +364 -0
- package/scripts/genome-rewrap.ts +90 -0
- package/scripts/genome-synthesizer.ts +679 -0
- package/scripts/genome-team-init.ts +312 -0
- package/scripts/handoff-pack.ts +438 -0
- package/scripts/hook-bootstrap.mjs +75 -0
- package/scripts/hook-timing-reader.ts +371 -0
- package/scripts/hook-timings-report.ts +554 -0
- package/scripts/install-genome-hooks.ts +196 -0
- package/scripts/install-onnx-model.ts +154 -0
- package/scripts/install-permissions.ts +276 -0
- package/scripts/install-status-line.ts +150 -0
- package/scripts/mcp-entrypoint.sh +78 -0
- package/scripts/mcp-entrypoint.ts +130 -0
- package/scripts/mdx-angle-bracket-lint.ts +152 -0
- package/scripts/measure-cold-start.ts +126 -0
- package/scripts/migrate-stats-to-sqlite.ts +86 -0
- package/scripts/ollama-setup.ts +498 -0
- package/scripts/onboarding-wizard.ts +1691 -0
- package/scripts/orchestrate-emit-telemetry.ts +177 -0
- package/scripts/orchestrate-expand.ts +517 -0
- package/scripts/orchestrate-run.ts +744 -0
- package/scripts/print-legend.ts +50 -0
- package/scripts/product-metadata.ts +38 -0
- package/scripts/prune-old-memories.ts +208 -0
- package/scripts/publish.sh +88 -0
- package/scripts/read-calibration.ts +122 -0
- package/scripts/report-crash.ts +226 -0
- package/scripts/run-benchmark.ts +750 -0
- package/scripts/run-root-tests.ts +28 -0
- package/scripts/savings-dashboard.ts +1258 -0
- package/scripts/savings-report-extras.ts +447 -0
- package/scripts/savings-status-line.ts +873 -0
- package/scripts/session-event-emit.ts +266 -0
- package/scripts/session-greet.ts +686 -0
- package/scripts/session-log-report.ts +551 -0
- package/scripts/session-resume.ts +614 -0
- package/scripts/set-hook-mode.ts +83 -0
- package/scripts/smoke-cross-platform.ts +117 -0
- package/scripts/smoke-realtime.ts +257 -0
- package/scripts/smoke-tools.ts +21 -0
- package/scripts/stats-cloud-pull.ts +213 -0
- package/scripts/stats-cloud-sync.ts +261 -0
- package/scripts/telemetry-flush.ts +160 -0
- package/scripts/telemetry-status.ts +266 -0
- package/scripts/timing-budget.ts +35 -0
- package/scripts/ui-animation.ts +120 -0
- package/scripts/ui.ts +431 -0
- package/scripts/upgrade-flow.ts +775 -0
- package/scripts/weekly-digest.ts +394 -0
- package/servers/_accounting.ts +78 -0
- package/servers/_ask-router.ts +84 -0
- package/servers/_ast-chunker.ts +434 -0
- package/servers/_ast-helpers.ts +183 -0
- package/servers/_ast-languages.ts +153 -0
- package/servers/_ast-refactor/_shared.ts +221 -0
- package/servers/_ast-refactor/cross-file-rename.ts +560 -0
- package/servers/_ast-refactor/extract-function.ts +357 -0
- package/servers/_ast-refactor/file-local-rename.ts +172 -0
- package/servers/_ast-refactor.ts +41 -0
- package/servers/_ast-skeleton.ts +107 -0
- package/servers/_bash-summarizers-registry.ts +1096 -0
- package/servers/_crash-dump.ts +210 -0
- package/servers/_cwd-clamp.ts +342 -0
- package/servers/_edit-log.ts +28 -0
- package/servers/_edit-match.ts +236 -0
- package/servers/_edit-validate.ts +196 -0
- package/servers/_embed-calibration.ts +123 -0
- package/servers/_embedding-cache.ts +401 -0
- package/servers/_embedding-model.ts +329 -0
- package/servers/_events.ts +113 -0
- package/servers/_genome-cache.ts +154 -0
- package/servers/_genome-commits.ts +172 -0
- package/servers/_genome-crypto-v2.ts +242 -0
- package/servers/_genome-crypto.ts +259 -0
- package/servers/_genome-discoveries.ts +140 -0
- package/servers/_genome-embed-populator.ts +216 -0
- package/servers/_genome-freshness.ts +134 -0
- package/servers/_genome-issues.ts +171 -0
- package/servers/_genome-live.ts +263 -0
- package/servers/_genome-prs.ts +188 -0
- package/servers/_genome-search.ts +479 -0
- package/servers/_genome-sync.ts +297 -0
- package/servers/_history-tracker.ts +279 -0
- package/servers/_http-helpers.ts +151 -0
- package/servers/_identity-hash.ts +151 -0
- package/servers/_llm-providers/anthropic.ts +132 -0
- package/servers/_llm-providers/cloud.ts +195 -0
- package/servers/_llm-providers/index.ts +350 -0
- package/servers/_llm-providers/local.ts +127 -0
- package/servers/_llm-providers/onnx.ts +393 -0
- package/servers/_llm-providers/types.ts +27 -0
- package/servers/_manifest-v2.ts +447 -0
- package/servers/_mcp-host.ts +93 -0
- package/servers/_nudge-events.ts +451 -0
- package/servers/_orchestrate-executor.ts +316 -0
- package/servers/_prefetch.ts +435 -0
- package/servers/_pricing.ts +256 -0
- package/servers/_pro.ts +381 -0
- package/servers/_read-cache.ts +44 -0
- package/servers/_router-handlers.ts +46 -0
- package/servers/_router.ts +55 -0
- package/servers/_run-with-timeout.ts +173 -0
- package/servers/_savings-render.ts +214 -0
- package/servers/_stats-sqlite.ts +640 -0
- package/servers/_stats.ts +1183 -0
- package/servers/_status-line-cells.ts +469 -0
- package/servers/_streaks.ts +162 -0
- package/servers/_summarize.ts +208 -0
- package/servers/_task-graph-render.ts +217 -0
- package/servers/_task-graph.ts +545 -0
- package/servers/_telemetry.ts +719 -0
- package/servers/_test-parsers.ts +530 -0
- package/servers/_test-watch.ts +626 -0
- package/servers/_text-helpers.ts +37 -0
- package/servers/_token-measure.ts +160 -0
- package/servers/_tool-base.ts +248 -0
- package/servers/_weekly-digest.ts +391 -0
- package/servers/ask-server-handlers.ts +49 -0
- package/servers/ask-server.ts +174 -0
- package/servers/bash-server-handlers.ts +138 -0
- package/servers/bash-server.ts +1136 -0
- package/servers/bench.ts +186 -0
- package/servers/diff-semantic-server-handlers.ts +39 -0
- package/servers/diff-semantic-server.ts +566 -0
- package/servers/diff-server-handlers.ts +317 -0
- package/servers/diff-server.ts +20 -0
- package/servers/edit-server-handlers.ts +31 -0
- package/servers/edit-server.ts +209 -0
- package/servers/edit-structural-server-handlers.ts +322 -0
- package/servers/efficiency-server-handlers.ts +161 -0
- package/servers/efficiency-server.ts +1135 -0
- package/servers/flush-server-handlers.ts +21 -0
- package/servers/flush-server.ts +19 -0
- package/servers/genome-server-handlers.ts +118 -0
- package/servers/genome-server.ts +397 -0
- package/servers/github-server-handlers.ts +187 -0
- package/servers/github-server.ts +856 -0
- package/servers/glob-server-handlers.ts +233 -0
- package/servers/glob-server.ts +20 -0
- package/servers/grep-server-handlers.ts +61 -0
- package/servers/grep-server.ts +564 -0
- package/servers/http-server-handlers.ts +36 -0
- package/servers/http-server.ts +149 -0
- package/servers/logs-server-handlers.ts +42 -0
- package/servers/logs-server.ts +359 -0
- package/servers/ls-server-handlers.ts +166 -0
- package/servers/ls-server.ts +20 -0
- package/servers/multi-edit-server-handlers.ts +51 -0
- package/servers/multi-edit-server.ts +422 -0
- package/servers/notebook-edit-server-handlers.ts +34 -0
- package/servers/notebook-edit-server.ts +181 -0
- package/servers/orient-server-handlers.ts +52 -0
- package/servers/orient-server.ts +436 -0
- package/servers/pipe-server-handlers.ts +68 -0
- package/servers/pipe-server.ts +300 -0
- package/servers/read-server-handlers.ts +42 -0
- package/servers/read-server.ts +206 -0
- package/servers/rename-file-server-handlers.ts +67 -0
- package/servers/rename-file-server.ts +699 -0
- package/servers/savings-server-handlers.ts +27 -0
- package/servers/savings-server.ts +54 -0
- package/servers/search-replace-regex-server-handlers.ts +96 -0
- package/servers/search-replace-regex-server.ts +751 -0
- package/servers/sql-server-handlers.ts +58 -0
- package/servers/sql-server.ts +505 -0
- package/servers/task-server-handlers.ts +358 -0
- package/servers/task-server.ts +24 -0
- package/servers/test-server-handlers.ts +351 -0
- package/servers/test-server.ts +20 -0
- package/servers/tree-server-handlers.ts +503 -0
- package/servers/tree-server.ts +20 -0
- package/servers/webfetch-server-handlers.ts +166 -0
- package/servers/webfetch-server.ts +20 -0
- package/servers/websearch-server-handlers.ts +215 -0
- package/servers/websearch-server.ts +19 -0
- package/servers/write-server-handlers.ts +27 -0
- package/servers/write-server.ts +97 -0
- package/skills/ashlr-brief/SKILL.md +80 -0
- package/skills/ashlr-codex-doctor/SKILL.md +21 -0
- package/skills/ashlr-codex-efficient-tools/SKILL.md +17 -0
- package/skills/ashlr-codex-genome-refresh/SKILL.md +20 -0
- package/skills/ashlr-codex-parallel-explore/SKILL.md +20 -0
- package/skills/ashlr-codex-savings/SKILL.md +17 -0
- package/skills/ashlr-cost-refactor/SKILL.md +50 -0
- package/skills/ashlr-efficient/SKILL.md +64 -0
- package/skills/ashlr-genome-author/SKILL.md +43 -0
- package/skills/ashlr-lean-tools/SKILL.md +43 -0
- package/skills/ashlr-search/SKILL.md +46 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* posttooluse-correlate.ts — Correlate ashlr__* calls with prior native-tool
|
|
4
|
+
* blocks to measure redirect conversion.
|
|
5
|
+
*
|
|
6
|
+
* PostToolUse hook. When an ashlr__read / ashlr__grep / ashlr__edit /
|
|
7
|
+
* ashlr__multi_edit call completes, this hook reads the recent-blocks log
|
|
8
|
+
* written by the pretooluse-* hooks. For every block within the 10-second
|
|
9
|
+
* window, it emits a `tool_called_after_block` event with correlation metadata
|
|
10
|
+
* and then prunes the consumed entries from the log.
|
|
11
|
+
*
|
|
12
|
+
* Design:
|
|
13
|
+
* - Best-effort only. Any error → silent continue, exit 0.
|
|
14
|
+
* - Kill switch: ASHLR_SESSION_LOG=0 disables all telemetry.
|
|
15
|
+
* - Window: 10 seconds (configurable via ASHLR_BLOCK_WINDOW_MS).
|
|
16
|
+
* - Only fires for tools that are known ashlr equivalents.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { join, dirname } from "path";
|
|
20
|
+
import { fileURLToPath } from "url";
|
|
21
|
+
import { flushHookTimings, recordHookTiming } from "./pretooluse-common";
|
|
22
|
+
import { readRecentBlocks, pruneOldBlocks } from "./_recent-blocks";
|
|
23
|
+
|
|
24
|
+
const hookStartedAt = Date.now();
|
|
25
|
+
|
|
26
|
+
async function exit(code: number, outcome: "ok" | "error" = "ok"): Promise<never> {
|
|
27
|
+
recordHookTiming({
|
|
28
|
+
hook: "posttooluse-correlate",
|
|
29
|
+
durationMs: Date.now() - hookStartedAt,
|
|
30
|
+
outcome,
|
|
31
|
+
});
|
|
32
|
+
await flushHookTimings();
|
|
33
|
+
process.exit(code);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Kill switch
|
|
37
|
+
if (process.env.ASHLR_SESSION_LOG === "0") await exit(0);
|
|
38
|
+
|
|
39
|
+
// Correlation window (default 10s)
|
|
40
|
+
const WINDOW_MS = (() => {
|
|
41
|
+
const raw = process.env.ASHLR_BLOCK_WINDOW_MS;
|
|
42
|
+
if (!raw) return 10_000;
|
|
43
|
+
const n = parseInt(raw, 10);
|
|
44
|
+
return Number.isFinite(n) && n > 0 ? n : 10_000;
|
|
45
|
+
})();
|
|
46
|
+
|
|
47
|
+
// Map from ashlr tool → native tool it replaces (for correlation)
|
|
48
|
+
const ASHLR_TO_NATIVE: Record<string, string[]> = {
|
|
49
|
+
"ashlr__read": ["Read"],
|
|
50
|
+
"ashlr__grep": ["Grep"],
|
|
51
|
+
"ashlr__edit": ["Edit", "Write"],
|
|
52
|
+
"ashlr__multi_edit": ["MultiEdit"],
|
|
53
|
+
// Wave-1 tools (Track B/C) — reference them but they may not exist yet.
|
|
54
|
+
"ashlr__websearch": ["WebSearch"],
|
|
55
|
+
"ashlr__notebook_edit": ["NotebookEdit"],
|
|
56
|
+
"ashlr__write": ["Write"],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Read stdin
|
|
60
|
+
const chunks: Buffer[] = [];
|
|
61
|
+
process.stdin.on("data", (c: Buffer) => chunks.push(c));
|
|
62
|
+
process.stdin.on("end", async () => {
|
|
63
|
+
let payload: Record<string, unknown> = {};
|
|
64
|
+
try {
|
|
65
|
+
payload = JSON.parse(Buffer.concat(chunks).toString("utf-8")) as Record<string, unknown>;
|
|
66
|
+
} catch {
|
|
67
|
+
await exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const toolName = payload.tool_name as string | undefined;
|
|
71
|
+
if (!toolName) await exit(0);
|
|
72
|
+
// After the guard above, toolName is narrowed to string within the
|
|
73
|
+
// same block — but TypeScript can't narrow across the async exit(0)
|
|
74
|
+
// boundary. Use a local const to carry the narrowed type forward.
|
|
75
|
+
const resolvedToolName: string = toolName!;
|
|
76
|
+
|
|
77
|
+
// Only process known ashlr tools
|
|
78
|
+
const nativeEquivalents = ASHLR_TO_NATIVE[resolvedToolName];
|
|
79
|
+
if (!nativeEquivalents) await exit(0);
|
|
80
|
+
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
const cutoff = now - WINDOW_MS;
|
|
83
|
+
|
|
84
|
+
// After nativeEquivalents guard, TypeScript can't narrow across async boundary.
|
|
85
|
+
const resolvedNative: string[] = nativeEquivalents!;
|
|
86
|
+
|
|
87
|
+
// Read recent blocks and find matches within window
|
|
88
|
+
const blocks = readRecentBlocks();
|
|
89
|
+
const matches = blocks.filter(
|
|
90
|
+
(b) => b.ts >= cutoff && resolvedNative.includes(b.toolName)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (matches.length === 0) {
|
|
94
|
+
// Still prune stale entries
|
|
95
|
+
pruneOldBlocks(WINDOW_MS * 2);
|
|
96
|
+
await exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Emit one event per matched block
|
|
100
|
+
try {
|
|
101
|
+
const { logEvent } = await import("../servers/_events");
|
|
102
|
+
for (const match of matches) {
|
|
103
|
+
await logEvent("tool_called_after_block", {
|
|
104
|
+
tool: resolvedToolName,
|
|
105
|
+
extra: {
|
|
106
|
+
nativeToolBlocked: match.toolName,
|
|
107
|
+
blockTs: match.ts,
|
|
108
|
+
latencyMs: now - match.ts,
|
|
109
|
+
pattern: match.pattern,
|
|
110
|
+
filePath: match.filePath,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// Telemetry must never surface errors.
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Prune old entries (keep any that are still within the window but unmatched)
|
|
119
|
+
pruneOldBlocks(WINDOW_MS * 2);
|
|
120
|
+
|
|
121
|
+
await exit(0);
|
|
122
|
+
});
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* posttooluse-dedupe-output.ts — Elide duplicate tool results across turns.
|
|
4
|
+
*
|
|
5
|
+
* PostToolUse hook for Read / Grep / ashlr__read / ashlr__grep.
|
|
6
|
+
*
|
|
7
|
+
* When the same content (identified by contentSha8) was already seen from the
|
|
8
|
+
* same tool class (Read↔ashlr__read, Grep↔ashlr__grep) within the last 8
|
|
9
|
+
* turns of the current session, the result is elided in additionalContext so
|
|
10
|
+
* the model doesn't re-ingest redundant bytes.
|
|
11
|
+
*
|
|
12
|
+
* Design:
|
|
13
|
+
* - Best-effort — never throws, always exits 0.
|
|
14
|
+
* - Kill switch: ASHLR_SESSION_LOG=0 disables everything.
|
|
15
|
+
* - Uses the same session-history JSONL written by posttooluse-stale-result.
|
|
16
|
+
*
|
|
17
|
+
* Hook contract (PostToolUse):
|
|
18
|
+
* stdin → { tool_name, tool_input?, tool_result? }
|
|
19
|
+
* stdout → { hookSpecificOutput: { hookEventName: "PostToolUse",
|
|
20
|
+
* additionalContext?: string } }
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { existsSync, mkdirSync, readFileSync, appendFileSync } from "fs";
|
|
24
|
+
import { homedir } from "os";
|
|
25
|
+
import { dirname, join } from "path";
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
readHistory,
|
|
29
|
+
resolveSessionId,
|
|
30
|
+
sha8,
|
|
31
|
+
historyDir,
|
|
32
|
+
historyPath,
|
|
33
|
+
readCurrentTurn,
|
|
34
|
+
type HistoryEntry,
|
|
35
|
+
} from "../servers/_history-tracker";
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Constants
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/** How many recent turns to scan for duplicates. */
|
|
42
|
+
const DEDUP_LOOKBACK_TURNS = 8;
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Tool class normalisation — Read↔ashlr__read, Grep↔ashlr__grep
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
type ToolClass = "read" | "grep" | "other";
|
|
49
|
+
|
|
50
|
+
const TRACKED_TOOLS = new Set([
|
|
51
|
+
"Read",
|
|
52
|
+
"Grep",
|
|
53
|
+
"mcp__ashlr-efficiency__ashlr__read",
|
|
54
|
+
"mcp__ashlr-efficiency__ashlr__grep",
|
|
55
|
+
"mcp__plugin_ashlr_ashlr__ashlr__read",
|
|
56
|
+
"mcp__plugin_ashlr_ashlr__ashlr__grep",
|
|
57
|
+
"ashlr__read",
|
|
58
|
+
"ashlr__grep",
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
function toolClass(toolName: string): ToolClass {
|
|
62
|
+
const t = toolName.toLowerCase();
|
|
63
|
+
if (t === "read" || t.endsWith("ashlr__read")) return "read";
|
|
64
|
+
if (t === "grep" || t.endsWith("ashlr__grep")) return "grep";
|
|
65
|
+
return "other";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Content extraction
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
function extractContent(payload: Record<string, unknown>): string {
|
|
73
|
+
const result = payload.tool_result ?? payload.tool_response ?? payload.tool_output;
|
|
74
|
+
if (result == null) return "";
|
|
75
|
+
if (typeof result === "string") return result;
|
|
76
|
+
try { return JSON.stringify(result); } catch { return ""; }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Hook output type
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
interface HookOutput {
|
|
84
|
+
hookSpecificOutput: {
|
|
85
|
+
hookEventName: "PostToolUse";
|
|
86
|
+
additionalContext?: string;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function passThrough(): HookOutput {
|
|
91
|
+
return { hookSpecificOutput: { hookEventName: "PostToolUse" } };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Dedup stats path (for bytesSaved accumulation)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
export function dedupStatsPath(homeDir: string): string {
|
|
99
|
+
return join(homeDir, ".ashlr", "dedupe-stats.json");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface DedupeStats {
|
|
103
|
+
bytesSaved: number;
|
|
104
|
+
dedupeCount: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function loadDedupeStats(homeDir: string): DedupeStats {
|
|
108
|
+
try {
|
|
109
|
+
const p = dedupStatsPath(homeDir);
|
|
110
|
+
if (!existsSync(p)) return { bytesSaved: 0, dedupeCount: 0 };
|
|
111
|
+
return JSON.parse(readFileSync(p, "utf-8")) as DedupeStats;
|
|
112
|
+
} catch {
|
|
113
|
+
return { bytesSaved: 0, dedupeCount: 0 };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function saveDedupeStats(stats: DedupeStats, homeDir: string): void {
|
|
118
|
+
try {
|
|
119
|
+
const p = dedupStatsPath(homeDir);
|
|
120
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
121
|
+
const { writeFileSync } = require("fs") as typeof import("fs");
|
|
122
|
+
writeFileSync(p, JSON.stringify(stats), "utf-8");
|
|
123
|
+
} catch {
|
|
124
|
+
// Best-effort.
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function accumulateBytesSaved(bytes: number, homeDir: string): void {
|
|
129
|
+
try {
|
|
130
|
+
const stats = loadDedupeStats(homeDir);
|
|
131
|
+
stats.bytesSaved += bytes;
|
|
132
|
+
stats.dedupeCount += 1;
|
|
133
|
+
saveDedupeStats(stats, homeDir);
|
|
134
|
+
} catch {
|
|
135
|
+
// Best-effort.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Main decision function (exported for tests)
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
export interface DecideOpts {
|
|
144
|
+
home?: string;
|
|
145
|
+
sessionId?: string;
|
|
146
|
+
now?: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function decide(
|
|
150
|
+
payload: Record<string, unknown>,
|
|
151
|
+
opts: DecideOpts = {},
|
|
152
|
+
): HookOutput {
|
|
153
|
+
if (process.env.ASHLR_SESSION_LOG === "0") return passThrough();
|
|
154
|
+
|
|
155
|
+
const toolName = payload?.tool_name;
|
|
156
|
+
if (typeof toolName !== "string" || !TRACKED_TOOLS.has(toolName)) {
|
|
157
|
+
return passThrough();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const klass = toolClass(toolName);
|
|
161
|
+
if (klass === "other") return passThrough();
|
|
162
|
+
|
|
163
|
+
const homeDir =
|
|
164
|
+
opts.home ??
|
|
165
|
+
(process.env.ASHLR_HOME_OVERRIDE?.trim() || process.env.HOME || homedir());
|
|
166
|
+
const sessionId = opts.sessionId ?? resolveSessionId();
|
|
167
|
+
|
|
168
|
+
const content = extractContent(payload);
|
|
169
|
+
if (!content) return passThrough();
|
|
170
|
+
|
|
171
|
+
const contentHash = sha8(content);
|
|
172
|
+
const contentBytes = Buffer.byteLength(content, "utf-8");
|
|
173
|
+
|
|
174
|
+
// Read last DEDUP_LOOKBACK_TURNS entries from session history.
|
|
175
|
+
const history = readHistory(sessionId, homeDir);
|
|
176
|
+
if (history.length === 0) return passThrough();
|
|
177
|
+
|
|
178
|
+
const currentTurn = history.length;
|
|
179
|
+
const lookbackStart = Math.max(0, currentTurn - DEDUP_LOOKBACK_TURNS);
|
|
180
|
+
const recentEntries = history.slice(lookbackStart);
|
|
181
|
+
|
|
182
|
+
// Find a prior turn with matching tool class AND same contentSha8.
|
|
183
|
+
const match = recentEntries.find(
|
|
184
|
+
(e: HistoryEntry) =>
|
|
185
|
+
toolClass(e.tool) === klass &&
|
|
186
|
+
e.contentSha8 === contentHash &&
|
|
187
|
+
e.turn < currentTurn,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (!match) return passThrough();
|
|
191
|
+
|
|
192
|
+
// Duplicate found — elide and report.
|
|
193
|
+
accumulateBytesSaved(contentBytes, homeDir);
|
|
194
|
+
|
|
195
|
+
const elision =
|
|
196
|
+
`[ashlr-dedupe] identical to turn ${match.turn} result ` +
|
|
197
|
+
`(sha8=${contentHash}); content elided to save ${contentBytes}B`;
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
hookSpecificOutput: {
|
|
201
|
+
hookEventName: "PostToolUse",
|
|
202
|
+
additionalContext: elision,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Stdin reading
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
async function readStdin(): Promise<string> {
|
|
212
|
+
const chunks: Buffer[] = [];
|
|
213
|
+
for await (const chunk of process.stdin as AsyncIterable<Buffer | string>) {
|
|
214
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
215
|
+
}
|
|
216
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// Entry point
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
async function main(): Promise<void> {
|
|
224
|
+
let payload: Record<string, unknown> = {};
|
|
225
|
+
try {
|
|
226
|
+
const raw = await readStdin();
|
|
227
|
+
if (raw.trim()) payload = JSON.parse(raw) as Record<string, unknown>;
|
|
228
|
+
} catch {
|
|
229
|
+
process.stdout.write(JSON.stringify(passThrough()));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
process.stdout.write(JSON.stringify(decide(payload)));
|
|
234
|
+
} catch {
|
|
235
|
+
process.stdout.write(JSON.stringify(passThrough()));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (import.meta.main) {
|
|
240
|
+
await main();
|
|
241
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* posttooluse-genome-refresh.ts — PostToolUse hook that records edited file
|
|
4
|
+
* paths to ~/.ashlr/pending-genome-refresh.txt for incremental genome refresh.
|
|
5
|
+
*
|
|
6
|
+
* Triggers on Edit, Write, MultiEdit, NotebookEdit, and all ashlr MCP edit
|
|
7
|
+
* variants. Appends the absolute file path (deduped) so the genome-refresh-
|
|
8
|
+
* worker can process them at session end.
|
|
9
|
+
*
|
|
10
|
+
* Design rules:
|
|
11
|
+
* - Never throws, never blocks. Best-effort fire-and-forget.
|
|
12
|
+
* - Exits 0 always.
|
|
13
|
+
* - No stdout — must not pollute the hook channel.
|
|
14
|
+
* - Kill switch: ASHLR_GENOME_AUTO=0
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
18
|
+
import { homedir } from "os";
|
|
19
|
+
import { join, dirname, resolve, isAbsolute } from "path";
|
|
20
|
+
|
|
21
|
+
import { noteHookError } from "./_hook-errors";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Constants
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
export const PENDING_FILE_NAME = "pending-genome-refresh.txt";
|
|
28
|
+
|
|
29
|
+
function pendingPath(home: string = homedir()): string {
|
|
30
|
+
return join(home, ".ashlr", PENDING_FILE_NAME);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Tool name detection — mirrors genome-scribe-hook.ts EDIT_TOOL_NAMES
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
export const WRITE_TOOL_NAMES = new Set<string>([
|
|
38
|
+
"Edit",
|
|
39
|
+
"Write",
|
|
40
|
+
"MultiEdit",
|
|
41
|
+
"NotebookEdit",
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
export function isWriteTool(name: string | undefined): boolean {
|
|
45
|
+
if (!name) return false;
|
|
46
|
+
if (WRITE_TOOL_NAMES.has(name)) return true;
|
|
47
|
+
// mcp__plugin_ashlr_ashlr__ashlr__edit etc.
|
|
48
|
+
if (!name.startsWith("mcp__")) return false;
|
|
49
|
+
return (
|
|
50
|
+
name.endsWith("ashlr__edit") ||
|
|
51
|
+
name.endsWith("ashlr__write") ||
|
|
52
|
+
name.endsWith("ashlr__multi_edit") ||
|
|
53
|
+
name.endsWith("ashlr__notebook_edit") ||
|
|
54
|
+
name.endsWith("ashlr__edit_structural") ||
|
|
55
|
+
name.endsWith("ashlr__rename_file")
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Path extraction from tool_input
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
interface ToolInput {
|
|
64
|
+
file_path?: unknown;
|
|
65
|
+
path?: unknown;
|
|
66
|
+
// rename_file uses old_path / new_path
|
|
67
|
+
old_path?: unknown;
|
|
68
|
+
new_path?: unknown;
|
|
69
|
+
// MultiEdit uses edits[]
|
|
70
|
+
edits?: Array<{ file_path?: unknown }>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function extractFilePaths(toolInput: unknown): string[] {
|
|
74
|
+
if (!toolInput || typeof toolInput !== "object") return [];
|
|
75
|
+
const inp = toolInput as ToolInput;
|
|
76
|
+
const paths: string[] = [];
|
|
77
|
+
|
|
78
|
+
// Single file: file_path or path
|
|
79
|
+
for (const key of ["file_path", "path", "old_path", "new_path"] as const) {
|
|
80
|
+
const v = inp[key];
|
|
81
|
+
if (typeof v === "string" && v.trim()) paths.push(v.trim());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// MultiEdit: edits[].file_path
|
|
85
|
+
if (Array.isArray(inp.edits)) {
|
|
86
|
+
for (const e of inp.edits) {
|
|
87
|
+
if (e && typeof e === "object") {
|
|
88
|
+
const fp = (e as { file_path?: unknown }).file_path;
|
|
89
|
+
if (typeof fp === "string" && fp.trim()) paths.push(fp.trim());
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Deduplicate within this payload
|
|
95
|
+
return [...new Set(paths)];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Pending-list writer
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Append absolute paths to the pending file, deduped.
|
|
104
|
+
* Returns true if any paths were added.
|
|
105
|
+
*/
|
|
106
|
+
export function appendToPending(paths: string[], home?: string): boolean {
|
|
107
|
+
if (paths.length === 0) return false;
|
|
108
|
+
const file = pendingPath(home);
|
|
109
|
+
try {
|
|
110
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Read existing set
|
|
116
|
+
const existing = new Set<string>();
|
|
117
|
+
if (existsSync(file)) {
|
|
118
|
+
try {
|
|
119
|
+
for (const line of readFileSync(file, "utf-8").split("\n")) {
|
|
120
|
+
const t = line.trim();
|
|
121
|
+
if (t) existing.add(t);
|
|
122
|
+
}
|
|
123
|
+
} catch (e) {
|
|
124
|
+
noteHookError("posttooluse-genome-refresh", "read-pending-list", e);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Resolve to absolute paths and filter new-only
|
|
129
|
+
const toAdd: string[] = [];
|
|
130
|
+
for (const p of paths) {
|
|
131
|
+
const abs = isAbsolute(p) ? p : resolve(p);
|
|
132
|
+
if (!existing.has(abs)) {
|
|
133
|
+
existing.add(abs);
|
|
134
|
+
toAdd.push(abs);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (toAdd.length === 0) return false;
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const content = [...existing].filter(Boolean).join("\n") + "\n";
|
|
142
|
+
writeFileSync(file, content, "utf-8");
|
|
143
|
+
return true;
|
|
144
|
+
} catch (e) {
|
|
145
|
+
noteHookError("posttooluse-genome-refresh", "write-pending-list", e);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Payload type
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
interface PostToolUsePayload {
|
|
155
|
+
tool_name?: string;
|
|
156
|
+
tool_input?: unknown;
|
|
157
|
+
tool_response?: Record<string, unknown>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function editSucceeded(toolResponse: Record<string, unknown> | undefined): boolean {
|
|
161
|
+
if (!toolResponse) return true;
|
|
162
|
+
if (toolResponse.isError === true) return false;
|
|
163
|
+
if (toolResponse.is_error === true) return false;
|
|
164
|
+
if (typeof toolResponse.success === "boolean") return toolResponse.success;
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Hook output (pass-through — we never block)
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
interface HookOutput {
|
|
173
|
+
hookSpecificOutput: { hookEventName: "PostToolUse"; additionalContext?: string };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function passThrough(): HookOutput {
|
|
177
|
+
return { hookSpecificOutput: { hookEventName: "PostToolUse" } };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Core logic
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
export function handle(payload: PostToolUsePayload, home?: string): HookOutput {
|
|
185
|
+
try {
|
|
186
|
+
if (process.env.ASHLR_GENOME_AUTO === "0") return passThrough();
|
|
187
|
+
if (!isWriteTool(payload?.tool_name)) return passThrough();
|
|
188
|
+
if (!editSucceeded(payload.tool_response)) return passThrough();
|
|
189
|
+
|
|
190
|
+
const paths = extractFilePaths(payload.tool_input);
|
|
191
|
+
appendToPending(paths, home ?? homedir());
|
|
192
|
+
} catch (e) {
|
|
193
|
+
noteHookError("posttooluse-genome-refresh", "handle", e);
|
|
194
|
+
}
|
|
195
|
+
return passThrough();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Stdio entry point
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
async function main(): Promise<void> {
|
|
203
|
+
const chunks: Buffer[] = [];
|
|
204
|
+
for await (const chunk of process.stdin as AsyncIterable<Buffer | string>) {
|
|
205
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
206
|
+
}
|
|
207
|
+
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
208
|
+
|
|
209
|
+
let payload: PostToolUsePayload = {};
|
|
210
|
+
if (raw) {
|
|
211
|
+
try {
|
|
212
|
+
payload = JSON.parse(raw) as PostToolUsePayload;
|
|
213
|
+
} catch {
|
|
214
|
+
/* bad input — pass-through */
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
process.stdout.write(JSON.stringify(handle(payload)));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (import.meta.main) {
|
|
222
|
+
await main();
|
|
223
|
+
}
|