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,205 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* posttooluse-native-nudge.ts — PostToolUse hook: missed-save nudge.
|
|
4
|
+
*
|
|
5
|
+
* Fires AFTER a native Read / Grep / Edit / Write / MultiEdit call completes.
|
|
6
|
+
* When the tool was called on a file inside cwd (and not inside the plugin
|
|
7
|
+
* tree) that COULD have been redirected by ashlr but wasn't (either because
|
|
8
|
+
* ASHLR_HOOK_MODE=nudge or because the cwd-clamp let it through), this hook
|
|
9
|
+
* emits a `additionalContext` nudge into the next assistant turn.
|
|
10
|
+
*
|
|
11
|
+
* Two escalation levels:
|
|
12
|
+
* 1. Basic missed-save nudge (first time, throttled to 1/min):
|
|
13
|
+
* [ashlr nudge] You called native Read on big_file.ts (8.2KB).
|
|
14
|
+
* ashlr__read would have returned ~1.6KB. Try
|
|
15
|
+
* mcp__plugin_ashlr_ashlr__ashlr__read next time.
|
|
16
|
+
*
|
|
17
|
+
* 2. Repeat-offender escalation (3+ calls of same tool in 10 min):
|
|
18
|
+
* [ashlr nudge] You've called native Grep 3 times in the last
|
|
19
|
+
* 10 minutes. ashlr__grep would have saved ~80% on each. Set
|
|
20
|
+
* ASHLR_HOOK_MODE=redirect to enforce the swap automatically:
|
|
21
|
+
* `bun ${CLAUDE_PLUGIN_ROOT}/scripts/set-hook-mode.ts redirect`.
|
|
22
|
+
*
|
|
23
|
+
* Constraints:
|
|
24
|
+
* - Only fires in ASHLR_HOOK_MODE=nudge. In redirect mode the PreToolUse
|
|
25
|
+
* hook already blocked and explained — double-talk is annoying.
|
|
26
|
+
* - Only fires for in-cwd, non-plugin-tree files (mirrors PreToolUse guard).
|
|
27
|
+
* - Throttled to ≤1 nudge per 60 seconds (see _nudge-throttle.ts).
|
|
28
|
+
* - Kill switch: ASHLR_SESSION_LOG=0.
|
|
29
|
+
* - Best-effort: any error → exit 0, no nudge.
|
|
30
|
+
*
|
|
31
|
+
* PostToolUse hook contract:
|
|
32
|
+
* stdin → JSON { tool_name, tool_input: { ... }, tool_response: { ... } }
|
|
33
|
+
* stdout → JSON { hookSpecificOutput: { hookEventName: "PostToolUse",
|
|
34
|
+
* additionalContext: "..." } } (nudge)
|
|
35
|
+
* OR empty / "{}" (no nudge)
|
|
36
|
+
* exit 0 always (never block post-tool execution)
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { basename } from "path";
|
|
40
|
+
import {
|
|
41
|
+
flushHookTimings,
|
|
42
|
+
fileSize,
|
|
43
|
+
getHookMode,
|
|
44
|
+
isInsideCwd,
|
|
45
|
+
isInsidePluginRoot,
|
|
46
|
+
nudgeVariantIndex,
|
|
47
|
+
parsePayload,
|
|
48
|
+
pluginRootFrom,
|
|
49
|
+
readStdin,
|
|
50
|
+
recordHookTiming,
|
|
51
|
+
} from "./pretooluse-common";
|
|
52
|
+
import { recordNativeCall } from "./_nudge-throttle";
|
|
53
|
+
|
|
54
|
+
const hookStartedAt = Date.now();
|
|
55
|
+
|
|
56
|
+
// Kill switch
|
|
57
|
+
if (process.env.ASHLR_SESSION_LOG === "0") {
|
|
58
|
+
process.stdout.write("{}");
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function exit(code: number, outcome: "ok" | "error" = "ok"): Promise<never> {
|
|
63
|
+
recordHookTiming({
|
|
64
|
+
hook: "posttooluse-native-nudge",
|
|
65
|
+
durationMs: Date.now() - hookStartedAt,
|
|
66
|
+
outcome,
|
|
67
|
+
});
|
|
68
|
+
await flushHookTimings();
|
|
69
|
+
process.exit(code);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** No-nudge pass-through — write empty JSON and exit 0. */
|
|
73
|
+
async function passThrough(): Promise<never> {
|
|
74
|
+
process.stdout.write("{}");
|
|
75
|
+
await exit(0);
|
|
76
|
+
// unreachable; satisfies `never` return type
|
|
77
|
+
throw new Error("unreachable");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Tool → ashlr equivalent mapping
|
|
81
|
+
const NATIVE_TO_ASHLR: Record<string, { ashlrTool: string; mcpTool: string; savingsPct: number }> = {
|
|
82
|
+
Read: { ashlrTool: "ashlr__read", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__read", savingsPct: 80 },
|
|
83
|
+
Grep: { ashlrTool: "ashlr__grep", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__grep", savingsPct: 80 },
|
|
84
|
+
Edit: { ashlrTool: "ashlr__edit", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__edit", savingsPct: 60 },
|
|
85
|
+
Write: { ashlrTool: "ashlr__edit", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__edit", savingsPct: 60 },
|
|
86
|
+
MultiEdit: { ashlrTool: "ashlr__multi_edit", mcpTool: "mcp__plugin_ashlr_ashlr__ashlr__multi_edit", savingsPct: 60 },
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const READ_THRESHOLD = 2048; // bytes — mirrors pretooluse-read.ts
|
|
90
|
+
|
|
91
|
+
// Parse stdin
|
|
92
|
+
const raw = await readStdin();
|
|
93
|
+
let payload: Record<string, unknown> = {};
|
|
94
|
+
try {
|
|
95
|
+
payload = JSON.parse(raw) as Record<string, unknown>;
|
|
96
|
+
} catch {
|
|
97
|
+
await passThrough();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const toolName = payload.tool_name as string | undefined;
|
|
101
|
+
if (!toolName) await passThrough();
|
|
102
|
+
|
|
103
|
+
const equiv = NATIVE_TO_ASHLR[toolName as string];
|
|
104
|
+
if (!equiv) await passThrough();
|
|
105
|
+
|
|
106
|
+
// Only fire in nudge mode — redirect mode already blocked + explained.
|
|
107
|
+
if (getHookMode() !== "nudge") await passThrough();
|
|
108
|
+
|
|
109
|
+
// Extract file path from tool_input
|
|
110
|
+
const toolInput = (payload.tool_input ?? {}) as Record<string, unknown>;
|
|
111
|
+
const filePath = (toolInput.file_path as string | undefined)
|
|
112
|
+
?? (toolInput.path as string | undefined)
|
|
113
|
+
?? "";
|
|
114
|
+
|
|
115
|
+
// For Read: only nudge on files above the threshold (tiny reads have no savings).
|
|
116
|
+
if (toolName === "Read") {
|
|
117
|
+
if (!filePath) await passThrough();
|
|
118
|
+
const size = fileSize(filePath);
|
|
119
|
+
if (size === null || size <= READ_THRESHOLD) await passThrough();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// For Grep: always try (no size guard needed).
|
|
123
|
+
// For Edit/Write/MultiEdit: only nudge when filePath is present.
|
|
124
|
+
if ((toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") && !filePath) {
|
|
125
|
+
await passThrough();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Safety: only nudge for in-cwd, non-plugin-tree paths.
|
|
129
|
+
const pluginRoot = pluginRootFrom(import.meta.url);
|
|
130
|
+
if (filePath) {
|
|
131
|
+
if (isInsidePluginRoot(filePath, pluginRoot)) await passThrough();
|
|
132
|
+
if (!isInsideCwd(filePath)) await passThrough();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Throttle check + repeat-offender detection.
|
|
136
|
+
const { emitNudge, emitEscalation, recentCallCount } = recordNativeCall(toolName as string);
|
|
137
|
+
|
|
138
|
+
if (!emitNudge && !emitEscalation) await passThrough();
|
|
139
|
+
|
|
140
|
+
// Build the nudge message.
|
|
141
|
+
let nudgeText: string;
|
|
142
|
+
const pluginRoot2 = process.env.CLAUDE_PLUGIN_ROOT ?? pluginRoot;
|
|
143
|
+
|
|
144
|
+
if (emitEscalation) {
|
|
145
|
+
// Escalation: 3+ same-tool calls in 10 min — give actionable set-hook-mode instruction.
|
|
146
|
+
const escalationVariants = [
|
|
147
|
+
`[ashlr nudge] ${recentCallCount}x native ${toolName} in 10 min — ${equiv.ashlrTool} saves ~${equiv.savingsPct}% each. ` +
|
|
148
|
+
`Enforce: \`bun ${pluginRoot2}/scripts/set-hook-mode.ts redirect\`.`,
|
|
149
|
+
`[ashlr nudge] You've used native ${toolName} ${recentCallCount} times (last 10 min). ` +
|
|
150
|
+
`${equiv.mcpTool} would save ~${equiv.savingsPct}% per call. ` +
|
|
151
|
+
`Auto-redirect: \`bun ${pluginRoot2}/scripts/set-hook-mode.ts redirect\`.`,
|
|
152
|
+
`[ashlr nudge] ${recentCallCount} native ${toolName} calls = missed savings. ` +
|
|
153
|
+
`Set ASHLR_HOOK_MODE=redirect or run \`bun ${pluginRoot2}/scripts/set-hook-mode.ts redirect\` once.`,
|
|
154
|
+
];
|
|
155
|
+
nudgeText = escalationVariants[nudgeVariantIndex(`${toolName}:${recentCallCount}`, escalationVariants.length)];
|
|
156
|
+
} else {
|
|
157
|
+
// Basic missed-save nudge — include exact size + MCP tool name to call.
|
|
158
|
+
if (toolName === "Read" && filePath) {
|
|
159
|
+
const size = fileSize(filePath);
|
|
160
|
+
const sizeKb = size !== null ? (size / 1024).toFixed(1) + "KB" : "?KB";
|
|
161
|
+
const compactKb = size !== null
|
|
162
|
+
? ((size * (1 - equiv.savingsPct / 100)) / 1024).toFixed(1) + "KB"
|
|
163
|
+
: "~20%";
|
|
164
|
+
const fname = basename(filePath) || filePath;
|
|
165
|
+
const variants = [
|
|
166
|
+
`[ashlr nudge] ${fname} (${sizeKb}) — ${equiv.mcpTool} returns ~${compactKb} instead. Call it next time.`,
|
|
167
|
+
`[ashlr nudge] Native Read on ${fname} (${sizeKb}) costs ${equiv.savingsPct}% extra tokens. ` +
|
|
168
|
+
`Use ${equiv.mcpTool} { "path": "${filePath}" }.`,
|
|
169
|
+
`[ashlr nudge] ${equiv.ashlrTool} on ${fname}: ${sizeKb} → ~${compactKb}. ` +
|
|
170
|
+
`${equiv.mcpTool} { "path": "${filePath}" }.`,
|
|
171
|
+
];
|
|
172
|
+
nudgeText = variants[nudgeVariantIndex(filePath, variants.length)];
|
|
173
|
+
} else if (toolName === "Grep") {
|
|
174
|
+
const pattern = (toolInput.pattern as string | undefined) ?? "<pattern>";
|
|
175
|
+
const variants = [
|
|
176
|
+
`[ashlr nudge] ${equiv.mcpTool} saves ~${equiv.savingsPct}% on Grep (genome-aware or compressed rg). ` +
|
|
177
|
+
`{ "pattern": ${JSON.stringify(pattern)} }.`,
|
|
178
|
+
`[ashlr nudge] Native Grep → raw output. ${equiv.ashlrTool} filters to relevant chunks (~${equiv.savingsPct}% smaller). ` +
|
|
179
|
+
`${equiv.mcpTool} { "pattern": ${JSON.stringify(pattern)} }.`,
|
|
180
|
+
`[ashlr nudge] Use ${equiv.mcpTool} instead of Grep — genome routing or truncated ripgrep, ~${equiv.savingsPct}% savings.`,
|
|
181
|
+
];
|
|
182
|
+
nudgeText = variants[nudgeVariantIndex(pattern, variants.length)];
|
|
183
|
+
} else {
|
|
184
|
+
// Edit / Write / MultiEdit
|
|
185
|
+
const fname = filePath ? (basename(filePath) || filePath) : "<file>";
|
|
186
|
+
const variants = [
|
|
187
|
+
`[ashlr nudge] ${equiv.mcpTool} on ${fname} returns only a diff (~${equiv.savingsPct}% less than native ${toolName}).`,
|
|
188
|
+
`[ashlr nudge] Native ${toolName} echoes the full file; ${equiv.ashlrTool} returns compact diff only. ` +
|
|
189
|
+
`Try ${equiv.mcpTool} { "path": "${filePath}" }.`,
|
|
190
|
+
`[ashlr nudge] ${fname} — ${equiv.ashlrTool} saves ~${equiv.savingsPct}% by returning a diff summary. ` +
|
|
191
|
+
`${equiv.mcpTool} { "path": "${filePath}", "search": ..., "replace": ... }.`,
|
|
192
|
+
];
|
|
193
|
+
nudgeText = variants[nudgeVariantIndex(filePath || fname, variants.length)];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const output = {
|
|
198
|
+
hookSpecificOutput: {
|
|
199
|
+
hookEventName: "PostToolUse",
|
|
200
|
+
additionalContext: nudgeText,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
process.stdout.write(JSON.stringify(output));
|
|
205
|
+
await exit(0);
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* posttooluse-stale-result.ts — Track ashlr tool results for multi-turn staleness.
|
|
4
|
+
*
|
|
5
|
+
* PostToolUse hook. Records every ashlr__read / ashlr__grep / Read / Grep result
|
|
6
|
+
* into the per-session history JSONL so the freshness tracker can later identify
|
|
7
|
+
* stale accumulation.
|
|
8
|
+
*
|
|
9
|
+
* Also fires the once-per-session adaptive nudge when stale bytes exceed 50 KB.
|
|
10
|
+
*
|
|
11
|
+
* Design:
|
|
12
|
+
* - Best-effort only — never throws, always exits 0.
|
|
13
|
+
* - Kill switch: ASHLR_SESSION_LOG=0 disables everything.
|
|
14
|
+
* - Only tracks read-class tools (Read, Grep, ashlr__read, ashlr__grep).
|
|
15
|
+
* Edit/Write results don't accumulate stale read content.
|
|
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, writeFileSync } from "fs";
|
|
24
|
+
import { homedir } from "os";
|
|
25
|
+
import { dirname, join } from "path";
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
recordResult,
|
|
29
|
+
readHistory,
|
|
30
|
+
resolveSessionId,
|
|
31
|
+
staleByteTotal,
|
|
32
|
+
STALE_BYTES_NUDGE_THRESHOLD,
|
|
33
|
+
} from "../servers/_history-tracker";
|
|
34
|
+
import { logPreCompactionNudgeEvent } from "../servers/_telemetry";
|
|
35
|
+
// logMultiTurnStaleEvent removed in v1.30 (1.5) — multi_turn_stale_estimate was never populated.
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Tools to track
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
const TRACKED_TOOLS = new Set([
|
|
42
|
+
"Read",
|
|
43
|
+
"Grep",
|
|
44
|
+
"mcp__ashlr-efficiency__ashlr__read",
|
|
45
|
+
"mcp__ashlr-efficiency__ashlr__grep",
|
|
46
|
+
"mcp__plugin_ashlr_ashlr__ashlr__read",
|
|
47
|
+
"mcp__plugin_ashlr_ashlr__ashlr__grep",
|
|
48
|
+
"ashlr__read",
|
|
49
|
+
"ashlr__grep",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Pre-compaction nudge constants
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/** Token estimate heuristic: 4 bytes per token. */
|
|
57
|
+
const BYTES_PER_TOKEN = 4;
|
|
58
|
+
|
|
59
|
+
/** Fire the pre-compaction nudge when estimated tokens exceed this. */
|
|
60
|
+
const PRECOMPACT_TOKEN_THRESHOLD = 65_000;
|
|
61
|
+
|
|
62
|
+
/** Assumed auto-compact ceiling (used for percentage display). */
|
|
63
|
+
const AUTOCOMPACT_TOKEN_CEILING = 80_000;
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Pre-compaction nudge state (once-per-session file flag)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
function precompactNudgePath(homeDir: string, sessionId: string): string {
|
|
70
|
+
return join(homeDir, ".ashlr", "precompact-fired", sessionId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function hasPrecompactNudgeFired(sessionId: string, homeDir: string): boolean {
|
|
74
|
+
try {
|
|
75
|
+
return existsSync(precompactNudgePath(homeDir, sessionId));
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function markPrecompactNudgeFired(sessionId: string, homeDir: string): void {
|
|
82
|
+
try {
|
|
83
|
+
const p = precompactNudgePath(homeDir, sessionId);
|
|
84
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
85
|
+
writeFileSync(p, String(Date.now()), "utf-8");
|
|
86
|
+
} catch {
|
|
87
|
+
// Best-effort.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Stale nudge state
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
function staleNudgePath(homeDir: string): string {
|
|
96
|
+
return join(homeDir, ".ashlr", "stale-nudge-state.json");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface StaleNudgeState {
|
|
100
|
+
sessionId: string;
|
|
101
|
+
firedAt: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function hasNudgeFiredThisSession(sessionId: string, homeDir: string): boolean {
|
|
105
|
+
try {
|
|
106
|
+
const p = staleNudgePath(homeDir);
|
|
107
|
+
if (!existsSync(p)) return false;
|
|
108
|
+
const raw = JSON.parse(readFileSync(p, "utf-8")) as Partial<StaleNudgeState>;
|
|
109
|
+
return raw.sessionId === sessionId;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function markNudgeFired(sessionId: string, homeDir: string, now: number): void {
|
|
116
|
+
try {
|
|
117
|
+
const p = staleNudgePath(homeDir);
|
|
118
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
119
|
+
const state: StaleNudgeState = { sessionId, firedAt: now };
|
|
120
|
+
writeFileSync(p, JSON.stringify(state), "utf-8");
|
|
121
|
+
} catch {
|
|
122
|
+
// Best-effort.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Result content extraction
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
function extractContent(payload: Record<string, unknown>): string {
|
|
131
|
+
const result = payload.tool_result ?? payload.tool_response ?? payload.tool_output;
|
|
132
|
+
if (result == null) return "";
|
|
133
|
+
if (typeof result === "string") return result;
|
|
134
|
+
try { return JSON.stringify(result); } catch { return ""; }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Hook output type
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
interface HookOutput {
|
|
142
|
+
hookSpecificOutput: {
|
|
143
|
+
hookEventName: "PostToolUse";
|
|
144
|
+
additionalContext?: string;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function passThrough(): HookOutput {
|
|
149
|
+
return { hookSpecificOutput: { hookEventName: "PostToolUse" } };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Main decision function (exported for tests)
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
export interface ProcessOpts {
|
|
157
|
+
home?: string;
|
|
158
|
+
sessionId?: string;
|
|
159
|
+
now?: number;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function decide(
|
|
163
|
+
payload: Record<string, unknown>,
|
|
164
|
+
opts: ProcessOpts = {},
|
|
165
|
+
): HookOutput {
|
|
166
|
+
if (process.env.ASHLR_SESSION_LOG === "0") return passThrough();
|
|
167
|
+
|
|
168
|
+
const toolName = payload?.tool_name;
|
|
169
|
+
if (typeof toolName !== "string" || !TRACKED_TOOLS.has(toolName)) {
|
|
170
|
+
return passThrough();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const homeDir =
|
|
174
|
+
opts.home ??
|
|
175
|
+
(process.env.ASHLR_HOME_OVERRIDE?.trim() || process.env.HOME || homedir());
|
|
176
|
+
const sessionId = opts.sessionId ?? resolveSessionId();
|
|
177
|
+
const now = opts.now ?? Date.now();
|
|
178
|
+
|
|
179
|
+
// Record this result in the history JSONL.
|
|
180
|
+
const content = extractContent(payload);
|
|
181
|
+
recordResult(toolName, content, sessionId, homeDir, now);
|
|
182
|
+
|
|
183
|
+
// -------------------------------------------------------------------------
|
|
184
|
+
// 2B — Pre-compaction nudge: warn once when cumulative bytes approach
|
|
185
|
+
// the auto-compact ceiling (~80K tokens).
|
|
186
|
+
// -------------------------------------------------------------------------
|
|
187
|
+
if (!hasPrecompactNudgeFired(sessionId, homeDir)) {
|
|
188
|
+
try {
|
|
189
|
+
const allEntries = readHistory(sessionId, homeDir);
|
|
190
|
+
const cumulativeBytes = allEntries.reduce((acc, e) => acc + e.sizeBytes, 0);
|
|
191
|
+
const estimatedTokens = Math.floor(cumulativeBytes / BYTES_PER_TOKEN);
|
|
192
|
+
if (estimatedTokens > PRECOMPACT_TOKEN_THRESHOLD) {
|
|
193
|
+
const pct = Math.min(100, Math.round((estimatedTokens / AUTOCOMPACT_TOKEN_CEILING) * 100));
|
|
194
|
+
markPrecompactNudgeFired(sessionId, homeDir);
|
|
195
|
+
try {
|
|
196
|
+
logPreCompactionNudgeEvent({ cumulativeBytes, estimatedTokens, pct });
|
|
197
|
+
} catch {
|
|
198
|
+
// Telemetry never blocks.
|
|
199
|
+
}
|
|
200
|
+
const precompactNudge =
|
|
201
|
+
`[ashlr] context approaching auto-compact (~${pct}% of 80K). ` +
|
|
202
|
+
`Run /ashlr-compact now to dedupe stale results before lag hits.`;
|
|
203
|
+
return {
|
|
204
|
+
hookSpecificOutput: {
|
|
205
|
+
hookEventName: "PostToolUse",
|
|
206
|
+
additionalContext: precompactNudge,
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
// Best-effort — never block tool call.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// -------------------------------------------------------------------------
|
|
216
|
+
// Stale-result nudge (original behaviour)
|
|
217
|
+
// -------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
// Check if stale bytes exceed nudge threshold.
|
|
220
|
+
if (hasNudgeFiredThisSession(sessionId, homeDir)) return passThrough();
|
|
221
|
+
|
|
222
|
+
const { staleBytes, staleResults, sessionTurnCount } = staleByteTotal(sessionId, homeDir);
|
|
223
|
+
|
|
224
|
+
if (staleBytes < STALE_BYTES_NUDGE_THRESHOLD) return passThrough();
|
|
225
|
+
|
|
226
|
+
// Fire the once-per-session adaptive nudge.
|
|
227
|
+
markNudgeFired(sessionId, homeDir, now);
|
|
228
|
+
|
|
229
|
+
const staleKb = Math.round(staleBytes / 1024);
|
|
230
|
+
const nudge =
|
|
231
|
+
`[ashlr] ~${staleKb} KB of stale tool output is accumulating in this session ` +
|
|
232
|
+
`(${staleResults} result${staleResults === 1 ? "" : "s"} from 5+ turns ago). ` +
|
|
233
|
+
`Run \`/ashlr-compact\` to surface a recompression plan.`;
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
hookSpecificOutput: {
|
|
237
|
+
hookEventName: "PostToolUse",
|
|
238
|
+
additionalContext: nudge,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// Stdin reading
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
async function readStdin(): Promise<string> {
|
|
248
|
+
const chunks: Buffer[] = [];
|
|
249
|
+
for await (const chunk of process.stdin as AsyncIterable<Buffer | string>) {
|
|
250
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
251
|
+
}
|
|
252
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Entry point
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
async function main(): Promise<void> {
|
|
260
|
+
let payload: Record<string, unknown> = {};
|
|
261
|
+
try {
|
|
262
|
+
const raw = await readStdin();
|
|
263
|
+
if (raw.trim()) payload = JSON.parse(raw) as Record<string, unknown>;
|
|
264
|
+
} catch {
|
|
265
|
+
process.stdout.write(JSON.stringify(passThrough()));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
process.stdout.write(JSON.stringify(decide(payload)));
|
|
270
|
+
} catch {
|
|
271
|
+
process.stdout.write(JSON.stringify(passThrough()));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (import.meta.main) {
|
|
276
|
+
await main();
|
|
277
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* pretooluse-bash.ts — Nudges the agent toward ashlr__bash for shell commands
|
|
4
|
+
* that produce verbose output.
|
|
5
|
+
*
|
|
6
|
+
* Unlike pretooluse-{read,grep,edit}, this hook NEVER blocks regardless of
|
|
7
|
+
* ASHLR_HOOK_MODE. Bash has no 1:1 equivalent for arbitrary commands —
|
|
8
|
+
* forcing every shell call through MCP would strand the agent on commands
|
|
9
|
+
* that aren't in the summarizer registry, and changes how output renders to
|
|
10
|
+
* the user (MCP tool result vs. inline shell output). Nudge-only keeps the
|
|
11
|
+
* UX intact while letting the model voluntarily pick ashlr__bash for the
|
|
12
|
+
* commands where compression actually pays off.
|
|
13
|
+
*
|
|
14
|
+
* The nudge fires only when `findSummarizer(command)` returns non-null OR
|
|
15
|
+
* `isLargeDiffCommand(command)` returns true — i.e., commands where
|
|
16
|
+
* ashlr__bash would actually run a structured summarizer or LLM-compress the
|
|
17
|
+
* stdout. Quiet commands (echo, pwd, mv, rm) pass through silently.
|
|
18
|
+
*
|
|
19
|
+
* `ASHLR_HOOK_MODE=off` (or the legacy `~/.ashlr/settings.json
|
|
20
|
+
* { toolRedirect: false }` killswitch) disables the nudge entirely.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
buildNudgeContext,
|
|
25
|
+
buildPassThrough,
|
|
26
|
+
flushHookTimings,
|
|
27
|
+
getHookModeFor,
|
|
28
|
+
parsePayload,
|
|
29
|
+
readStdin,
|
|
30
|
+
recordHookTiming,
|
|
31
|
+
} from "./pretooluse-common";
|
|
32
|
+
import { installHookTimeout } from "./pretooluse-common";
|
|
33
|
+
|
|
34
|
+
if (import.meta.main) installHookTimeout("pretooluse-bash");
|
|
35
|
+
|
|
36
|
+
const hookStartedAt = Date.now();
|
|
37
|
+
|
|
38
|
+
async function exit(code: number, outcome: "ok" | "bypass" | "block" | "error", tool?: string): Promise<never> {
|
|
39
|
+
recordHookTiming({ hook: "pretooluse-bash", tool, durationMs: Date.now() - hookStartedAt, outcome });
|
|
40
|
+
await flushHookTimings();
|
|
41
|
+
process.exit(code);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const raw = await readStdin();
|
|
45
|
+
const payload = parsePayload(raw);
|
|
46
|
+
if (!payload) await exit(0, "ok");
|
|
47
|
+
|
|
48
|
+
const tool = payload!.tool_name || undefined;
|
|
49
|
+
const handledTools = new Set(["Bash", "exec_command", "functions.exec_command"]);
|
|
50
|
+
if (!handledTools.has(payload!.tool_name)) await exit(0, "ok", tool);
|
|
51
|
+
if (!payload!.command) await exit(0, "ok", tool);
|
|
52
|
+
|
|
53
|
+
const mode = getHookModeFor("bash");
|
|
54
|
+
if (mode === "off") {
|
|
55
|
+
process.stdout.write(JSON.stringify(buildPassThrough()));
|
|
56
|
+
await exit(0, "ok", tool);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// buildNudgeContext("Bash", ...) returns null when the command isn't in the
|
|
60
|
+
// summarizer registry — passing through silently in that case is intentional.
|
|
61
|
+
const nudge = buildNudgeContext("Bash", { command: payload!.command });
|
|
62
|
+
process.stdout.write(JSON.stringify(nudge ?? buildPassThrough()));
|
|
63
|
+
await exit(0, "ok", tool);
|