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,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _edit-match.ts — Shared helpers for fuzzy search/replace matching.
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* levenshtein — used by both fuzzyTopLines (hint display) and findFuzzyMatch
|
|
6
|
+
* fuzzyTopLines — top-3 closest lines for error hints (unchanged behavior)
|
|
7
|
+
* findFuzzyMatch — two-tier fuzzy matcher, returns start/end byte offsets
|
|
8
|
+
*
|
|
9
|
+
* Design principles:
|
|
10
|
+
* - Conservative: only returns a match when confidence is HIGH and UNIQUE.
|
|
11
|
+
* - Never returns a low-confidence or ambiguous match.
|
|
12
|
+
* - Caps work on huge files (>2 MB) → returns null fast.
|
|
13
|
+
* - All errors swallowed by callers; this module must never throw.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Levenshtein distance (capped at maxDist for speed). */
|
|
17
|
+
export function levenshtein(a: string, b: string, maxDist = 256): number {
|
|
18
|
+
if (a === b) return 0;
|
|
19
|
+
if (a.length === 0) return Math.min(b.length, maxDist);
|
|
20
|
+
if (b.length === 0) return Math.min(a.length, maxDist);
|
|
21
|
+
const A = a.slice(0, 200);
|
|
22
|
+
const B = b.slice(0, 200);
|
|
23
|
+
const m = A.length, n = B.length;
|
|
24
|
+
let prev = Array.from({ length: n + 1 }, (_, i) => i);
|
|
25
|
+
let curr = new Array<number>(n + 1);
|
|
26
|
+
for (let i = 1; i <= m; i++) {
|
|
27
|
+
curr[0] = i;
|
|
28
|
+
for (let j = 1; j <= n; j++) {
|
|
29
|
+
const cost = A[i - 1] === B[j - 1] ? 0 : 1;
|
|
30
|
+
curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
|
|
31
|
+
}
|
|
32
|
+
[prev, curr] = [curr, prev];
|
|
33
|
+
}
|
|
34
|
+
return prev[n];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Return top-3 closest lines to `search` from `content`, with similarity scores. */
|
|
38
|
+
export function fuzzyTopLines(
|
|
39
|
+
search: string,
|
|
40
|
+
content: string,
|
|
41
|
+
): Array<{ lineNo: number; text: string; sim: number }> {
|
|
42
|
+
const needle = search.split("\n")[0].trim().slice(0, 200);
|
|
43
|
+
const lines = content.split("\n");
|
|
44
|
+
const results: Array<{ lineNo: number; text: string; sim: number }> = [];
|
|
45
|
+
for (let i = 0; i < lines.length; i++) {
|
|
46
|
+
const line = lines[i].trim();
|
|
47
|
+
if (!line) continue;
|
|
48
|
+
const dist = levenshtein(needle, line.slice(0, 200));
|
|
49
|
+
const maxLen = Math.max(needle.length, line.length, 1);
|
|
50
|
+
const sim = Math.round((1 - dist / maxLen) * 100) / 100;
|
|
51
|
+
results.push({ lineNo: i + 1, text: lines[i], sim });
|
|
52
|
+
}
|
|
53
|
+
results.sort((a, b) => b.sim - a.sim);
|
|
54
|
+
return results.slice(0, 3);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Result from a successful fuzzy match. */
|
|
58
|
+
export interface FuzzyMatchResult {
|
|
59
|
+
/** Byte offset of match start in `content`. */
|
|
60
|
+
start: number;
|
|
61
|
+
/** Byte offset of match end (exclusive) in `content`. */
|
|
62
|
+
end: number;
|
|
63
|
+
/** Similarity score [0..1]. */
|
|
64
|
+
score: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const FUZZY_CONTENT_LIMIT = 2 * 1024 * 1024; // 2 MB
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Try to find a fuzzy match for `search` in `content`.
|
|
71
|
+
*
|
|
72
|
+
* Tier 1 — whitespace-normalized unique match:
|
|
73
|
+
* Normalize each run of whitespace to a single space and trim; if exactly ONE
|
|
74
|
+
* normalized occurrence maps to a real offset → return it (score 1.0).
|
|
75
|
+
*
|
|
76
|
+
* Tier 2 — block Levenshtein:
|
|
77
|
+
* Slide a window of search's line count over content's lines; compute
|
|
78
|
+
* per-window normalized similarity; accept ONLY IF best >= 0.90 AND the
|
|
79
|
+
* margin over the 2nd-best is >= 0.05 (uniqueness guard).
|
|
80
|
+
*
|
|
81
|
+
* Returns null when no confident, unambiguous match is found.
|
|
82
|
+
* NEVER called for content > 2 MB (caller guards).
|
|
83
|
+
*/
|
|
84
|
+
export function findFuzzyMatch(
|
|
85
|
+
content: string,
|
|
86
|
+
search: string,
|
|
87
|
+
): FuzzyMatchResult | null {
|
|
88
|
+
if (!search || !content) return null;
|
|
89
|
+
if (content.length > FUZZY_CONTENT_LIMIT) return null;
|
|
90
|
+
|
|
91
|
+
// -------------------------------------------------------------------------
|
|
92
|
+
// Tier 1: whitespace-normalized match
|
|
93
|
+
// -------------------------------------------------------------------------
|
|
94
|
+
const normalizeWS = (s: string) =>
|
|
95
|
+
s.replace(/[ \t]+/g, " ").replace(/^ /gm, "").replace(/ $/gm, "").trimEnd();
|
|
96
|
+
|
|
97
|
+
const normContent = normalizeWS(content);
|
|
98
|
+
const normSearch = normalizeWS(search);
|
|
99
|
+
|
|
100
|
+
if (normSearch.length > 0) {
|
|
101
|
+
// Find all occurrences in normalized content.
|
|
102
|
+
const occurrences: number[] = [];
|
|
103
|
+
let pos = 0;
|
|
104
|
+
while ((pos = normContent.indexOf(normSearch, pos)) !== -1) {
|
|
105
|
+
occurrences.push(pos);
|
|
106
|
+
pos += normSearch.length;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (occurrences.length === 1) {
|
|
110
|
+
// Map normalized offset back to original content offset.
|
|
111
|
+
// We rebuild the mapping by walking both strings in tandem.
|
|
112
|
+
const normToOrig = buildNormToOrigMap(content, normContent);
|
|
113
|
+
const normStart = occurrences[0];
|
|
114
|
+
const normEnd = normStart + normSearch.length;
|
|
115
|
+
|
|
116
|
+
const origStart = normToOrig[normStart];
|
|
117
|
+
if (origStart === undefined) {
|
|
118
|
+
// Could not map back to original offset — skip Tier 1.
|
|
119
|
+
} else {
|
|
120
|
+
// For end, we use normEnd-1 to get the last char's orig position, then
|
|
121
|
+
// advance past it. Guard against out-of-bounds.
|
|
122
|
+
const origEnd =
|
|
123
|
+
normEnd > 0 && normToOrig[normEnd - 1] !== undefined
|
|
124
|
+
? normToOrig[normEnd - 1]! + 1
|
|
125
|
+
: origStart + search.length;
|
|
126
|
+
|
|
127
|
+
if (origEnd > origStart) {
|
|
128
|
+
return { start: origStart, end: origEnd, score: 1.0 };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// -------------------------------------------------------------------------
|
|
135
|
+
// Tier 2: block Levenshtein over line windows
|
|
136
|
+
// -------------------------------------------------------------------------
|
|
137
|
+
const searchLines = search.split("\n");
|
|
138
|
+
const contentLines = content.split("\n");
|
|
139
|
+
const windowSize = searchLines.length;
|
|
140
|
+
|
|
141
|
+
if (windowSize === 0 || contentLines.length < windowSize) return null;
|
|
142
|
+
|
|
143
|
+
// Build a flat string per window for distance comparison.
|
|
144
|
+
const searchFlat = searchLines.map(l => l.trim()).join("\n");
|
|
145
|
+
|
|
146
|
+
interface WindowScore { lineIdx: number; score: number }
|
|
147
|
+
let best: WindowScore | null = null;
|
|
148
|
+
let secondBest: WindowScore | null = null;
|
|
149
|
+
|
|
150
|
+
for (let i = 0; i <= contentLines.length - windowSize; i++) {
|
|
151
|
+
const windowFlat = contentLines.slice(i, i + windowSize).map(l => l.trim()).join("\n");
|
|
152
|
+
const dist = levenshtein(searchFlat, windowFlat, 512);
|
|
153
|
+
const maxLen = Math.max(searchFlat.length, windowFlat.length, 1);
|
|
154
|
+
const score = 1 - dist / maxLen;
|
|
155
|
+
|
|
156
|
+
if (!best || score > best.score) {
|
|
157
|
+
secondBest = best;
|
|
158
|
+
best = { lineIdx: i, score };
|
|
159
|
+
} else if (!secondBest || score > secondBest.score) {
|
|
160
|
+
secondBest = { lineIdx: i, score };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!best) return null;
|
|
165
|
+
|
|
166
|
+
// Confidence gate: score >= 0.90 AND margin over 2nd-best >= 0.05.
|
|
167
|
+
const margin = secondBest ? best.score - secondBest.score : 1;
|
|
168
|
+
if (best.score < 0.90 || margin < 0.05) return null;
|
|
169
|
+
|
|
170
|
+
// Map best window back to byte offsets.
|
|
171
|
+
const start = lineToOffset(content, best.lineIdx);
|
|
172
|
+
const endLineIdx = best.lineIdx + windowSize;
|
|
173
|
+
const end =
|
|
174
|
+
endLineIdx >= contentLines.length
|
|
175
|
+
? content.length
|
|
176
|
+
: lineToOffset(content, endLineIdx);
|
|
177
|
+
|
|
178
|
+
return { start, end, score: Math.round(best.score * 100) / 100 };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// Internal helpers
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build a map: normContent index → original content index.
|
|
187
|
+
* Both strings must have been produced by the same normalizeWS logic above.
|
|
188
|
+
*/
|
|
189
|
+
function buildNormToOrigMap(orig: string, norm: string): Array<number | undefined> {
|
|
190
|
+
const map: Array<number | undefined> = new Array(norm.length + 1).fill(undefined);
|
|
191
|
+
let oi = 0; // index into orig
|
|
192
|
+
let ni = 0; // index into norm
|
|
193
|
+
|
|
194
|
+
while (ni < norm.length && oi < orig.length) {
|
|
195
|
+
// Skip whitespace runs in orig that were collapsed to a single space in norm.
|
|
196
|
+
// The normalizer collapses /[ \t]+/ → " " and trims line ends, but newlines
|
|
197
|
+
// are preserved. We handle char-by-char.
|
|
198
|
+
const nc = norm[ni];
|
|
199
|
+
const oc = orig[oi];
|
|
200
|
+
|
|
201
|
+
if (nc === oc) {
|
|
202
|
+
map[ni] = oi;
|
|
203
|
+
ni++;
|
|
204
|
+
oi++;
|
|
205
|
+
} else if (nc === " " && (oc === " " || oc === "\t")) {
|
|
206
|
+
// Collapsed whitespace: norm has one space, orig has one or more spaces/tabs.
|
|
207
|
+
map[ni] = oi;
|
|
208
|
+
ni++;
|
|
209
|
+
oi++;
|
|
210
|
+
// Skip remaining contiguous spaces/tabs in orig.
|
|
211
|
+
while (oi < orig.length && (orig[oi] === " " || orig[oi] === "\t")) {
|
|
212
|
+
oi++;
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
// Mismatch not explained by WS collapse — advance orig to resync.
|
|
216
|
+
// This handles trimmed leading/trailing spaces on lines.
|
|
217
|
+
oi++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return map;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Return the byte offset of the start of line `lineIdx` (0-based) in `content`.
|
|
225
|
+
*/
|
|
226
|
+
function lineToOffset(content: string, lineIdx: number): number {
|
|
227
|
+
if (lineIdx === 0) return 0;
|
|
228
|
+
let count = 0;
|
|
229
|
+
for (let i = 0; i < content.length; i++) {
|
|
230
|
+
if (content[i] === "\n") {
|
|
231
|
+
count++;
|
|
232
|
+
if (count === lineIdx) return i + 1;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return content.length;
|
|
236
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _edit-validate.ts — Post-edit syntax validation.
|
|
3
|
+
*
|
|
4
|
+
* Wired languages: TypeScript (.ts/.tsx), JavaScript (.js/.mjs/.cjs/.jsx), JSON.
|
|
5
|
+
* YAML: no parser in deps → skipped.
|
|
6
|
+
* Everything else: skipped (returns {introducedError:false}).
|
|
7
|
+
*
|
|
8
|
+
* Design contract:
|
|
9
|
+
* - NEVER throws or rejects — all errors are caught and return safe defaults.
|
|
10
|
+
* - Only reports introducedError=true when the edit INTRODUCED the error
|
|
11
|
+
* (original was clean, updated is broken).
|
|
12
|
+
* - If the original already had parse errors → introducedError=false (don't blame the edit).
|
|
13
|
+
* - _ast-languages is dynamically imported so the WASM binary is NOT parsed at
|
|
14
|
+
* module load time. This keeps subprocess startup fast (~50ms vs ~1.5s with
|
|
15
|
+
* static import), ensuring spawned MCP servers stay within test timeouts.
|
|
16
|
+
* - The entire call is guarded by a 2s timeout. The timer is .unref()'d so it
|
|
17
|
+
* never prevents subprocess exit.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { extname, basename } from "path";
|
|
21
|
+
|
|
22
|
+
export interface ValidationResult {
|
|
23
|
+
introducedError: boolean;
|
|
24
|
+
detail: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const SAFE: ValidationResult = { introducedError: false, detail: "" };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Timeout (ms) for the entire validation call.
|
|
31
|
+
* 2s: generous for a warm parser (~microseconds), safely under typical 5s test
|
|
32
|
+
* timeouts even when the subprocess has other startup overhead.
|
|
33
|
+
*/
|
|
34
|
+
const VALIDATE_TIMEOUT_MS = 2000;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate that `updated` does not introduce a syntax error that was not
|
|
38
|
+
* already present in `original`.
|
|
39
|
+
*
|
|
40
|
+
* @param absPath Absolute path of the file (used only for extension detection).
|
|
41
|
+
* @param original Content before the edit.
|
|
42
|
+
* @param updated Content after the edit.
|
|
43
|
+
*/
|
|
44
|
+
export async function validateEdit(
|
|
45
|
+
absPath: string,
|
|
46
|
+
original: string,
|
|
47
|
+
updated: string,
|
|
48
|
+
): Promise<ValidationResult> {
|
|
49
|
+
// Wrap everything in a timeout + catch so a hung parser never blocks a write.
|
|
50
|
+
try {
|
|
51
|
+
return await Promise.race([
|
|
52
|
+
_validateInner(absPath, original, updated),
|
|
53
|
+
new Promise<ValidationResult>((resolve) => {
|
|
54
|
+
const t = setTimeout(() => resolve(SAFE), VALIDATE_TIMEOUT_MS);
|
|
55
|
+
// unref so this timer never prevents subprocess exit
|
|
56
|
+
if (t && typeof t === "object" && "unref" in t) (t as NodeJS.Timeout).unref();
|
|
57
|
+
}),
|
|
58
|
+
]);
|
|
59
|
+
} catch {
|
|
60
|
+
return SAFE;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function _validateInner(
|
|
65
|
+
absPath: string,
|
|
66
|
+
original: string,
|
|
67
|
+
updated: string,
|
|
68
|
+
): Promise<ValidationResult> {
|
|
69
|
+
const ext = extname(basename(absPath)).toLowerCase();
|
|
70
|
+
|
|
71
|
+
// -------------------------------------------------------------------------
|
|
72
|
+
// JSON
|
|
73
|
+
// -------------------------------------------------------------------------
|
|
74
|
+
if (ext === ".json") {
|
|
75
|
+
return validateJson(original, updated);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// -------------------------------------------------------------------------
|
|
79
|
+
// JS / TS via tree-sitter — dynamic import keeps WASM out of module load
|
|
80
|
+
// -------------------------------------------------------------------------
|
|
81
|
+
const isWiredTS =
|
|
82
|
+
ext === ".ts" || ext === ".tsx" || ext === ".js" ||
|
|
83
|
+
ext === ".mjs" || ext === ".cjs" || ext === ".jsx";
|
|
84
|
+
|
|
85
|
+
if (isWiredTS) {
|
|
86
|
+
return await validateAst(absPath, original, updated);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// All other extensions: skip.
|
|
90
|
+
return SAFE;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// JSON validation
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
function validateJson(original: string, updated: string): ValidationResult {
|
|
98
|
+
let originalOk = true;
|
|
99
|
+
try {
|
|
100
|
+
JSON.parse(original);
|
|
101
|
+
} catch {
|
|
102
|
+
originalOk = false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!originalOk) {
|
|
106
|
+
// Original was already broken — don't blame the edit.
|
|
107
|
+
return SAFE;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
JSON.parse(updated);
|
|
112
|
+
return SAFE;
|
|
113
|
+
} catch (err) {
|
|
114
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
115
|
+
return {
|
|
116
|
+
introducedError: true,
|
|
117
|
+
detail: `JSON parse error: ${msg.slice(0, 120)}`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// AST (tree-sitter) validation — dynamic import so WASM isn't loaded at startup
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
async function validateAst(
|
|
127
|
+
absPath: string,
|
|
128
|
+
original: string,
|
|
129
|
+
updated: string,
|
|
130
|
+
): Promise<ValidationResult> {
|
|
131
|
+
// Dynamic import: WASM binary is parsed only on first call, not at module load.
|
|
132
|
+
let resolveLanguage: (f: string) => import("./_ast-languages").Language | null;
|
|
133
|
+
let getParser: (lang: import("./_ast-languages").Language) => Promise<import("./_ast-languages").WTSParser>;
|
|
134
|
+
try {
|
|
135
|
+
const mod = await import("./_ast-languages");
|
|
136
|
+
resolveLanguage = mod.resolveLanguage;
|
|
137
|
+
getParser = mod.getParser;
|
|
138
|
+
} catch {
|
|
139
|
+
return SAFE;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const lang = resolveLanguage(absPath);
|
|
143
|
+
if (!lang || (lang !== "typescript" && lang !== "tsx" && lang !== "javascript")) {
|
|
144
|
+
return SAFE;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let parser: import("./_ast-languages").WTSParser;
|
|
148
|
+
try {
|
|
149
|
+
parser = await getParser(lang);
|
|
150
|
+
} catch {
|
|
151
|
+
// Language not wired yet (Python/Go/Rust stub) — skip gracefully.
|
|
152
|
+
return SAFE;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Parse both versions. tree-sitter is synchronous despite the async wrapper.
|
|
156
|
+
let origTree: ReturnType<typeof parser.parse>;
|
|
157
|
+
let updTree: ReturnType<typeof parser.parse>;
|
|
158
|
+
try {
|
|
159
|
+
origTree = parser.parse(original);
|
|
160
|
+
updTree = parser.parse(updated);
|
|
161
|
+
} catch {
|
|
162
|
+
return SAFE;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// If the original already had errors, don't blame the edit.
|
|
166
|
+
if (origTree.rootNode.hasError) {
|
|
167
|
+
return SAFE;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!updTree.rootNode.hasError) {
|
|
171
|
+
return SAFE;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Find the first error node and report its start line.
|
|
175
|
+
const errLine = findFirstErrorLine(updTree.rootNode);
|
|
176
|
+
const lineRef = errLine >= 0 ? ` at line ${errLine + 1}` : "";
|
|
177
|
+
return {
|
|
178
|
+
introducedError: true,
|
|
179
|
+
detail: `${lang} parse error${lineRef}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Walk the tree to find the first ERROR or MISSING node.
|
|
185
|
+
* Returns 0-based line number, or -1 if not found.
|
|
186
|
+
*/
|
|
187
|
+
function findFirstErrorLine(node: { type: string; startPosition: { row: number }; children: unknown[] }): number {
|
|
188
|
+
if (node.type === "ERROR" || node.type === "MISSING") {
|
|
189
|
+
return node.startPosition.row;
|
|
190
|
+
}
|
|
191
|
+
for (const child of node.children) {
|
|
192
|
+
const result = findFirstErrorLine(child as { type: string; startPosition: { row: number }; children: unknown[] });
|
|
193
|
+
if (result >= 0) return result;
|
|
194
|
+
}
|
|
195
|
+
return -1;
|
|
196
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _embed-calibration — embedding threshold constants and calibration helpers.
|
|
3
|
+
*
|
|
4
|
+
* Used by grep-server to gate the embedding cache and log A/B data so we
|
|
5
|
+
* can tune EMBED_HIT_THRESHOLD from real usage instead of guesses.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createHash } from "crypto";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Cosine similarity threshold for a cache hit to prepend results.
|
|
12
|
+
*
|
|
13
|
+
* Baseline 0.68 (down from 0.75 in v1.12) — calibrated against the 24-doc
|
|
14
|
+
* BM25 corpus where IDF weights are nearly flat and real similarity scores
|
|
15
|
+
* cluster lower than classic dense-embedding expectations. Override via
|
|
16
|
+
* `ASHLR_EMBED_THRESHOLD` (float in [0, 1]). A/B data flows to
|
|
17
|
+
* `~/.ashlr/embed-calibration.jsonl` (see recordEmbedCalibration below).
|
|
18
|
+
*/
|
|
19
|
+
export const EMBED_HIT_THRESHOLD = (() => {
|
|
20
|
+
const raw = process.env.ASHLR_EMBED_THRESHOLD;
|
|
21
|
+
if (!raw) return 0.68;
|
|
22
|
+
const n = Number.parseFloat(raw);
|
|
23
|
+
return Number.isFinite(n) && n >= 0 && n <= 1 ? n : 0.68;
|
|
24
|
+
})();
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Minimum BM25 corpus size before the embedding cache is consulted. Below
|
|
28
|
+
* this threshold, IDF weights are dominated by noise and nearly any query
|
|
29
|
+
* produces a spurious hit — see v1.17 incident where a 6-doc corpus emitted
|
|
30
|
+
* 0.7+ cosine hits on unrelated patterns.
|
|
31
|
+
*
|
|
32
|
+
* v1.18 Trust Pass: if docCount < THIS, the cache is skipped outright.
|
|
33
|
+
* v1.24 Track E: lowered to 10 (warm-start). The warm tier (10–49) uses a
|
|
34
|
+
* stricter cosine threshold to compensate for the thinner corpus.
|
|
35
|
+
*/
|
|
36
|
+
export const BM25_CORPUS_MIN = 50;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* v1.24 Track E — Three-tier warm-start corpus classification.
|
|
40
|
+
*
|
|
41
|
+
* cold (0–9 docs) : skip cache entirely (too few docs, high noise).
|
|
42
|
+
* warm (10–49 docs): consult cache with a stricter cosine threshold.
|
|
43
|
+
* hot (50+ docs) : full v1.23 behavior (EMBED_HIT_THRESHOLD = 0.68).
|
|
44
|
+
*/
|
|
45
|
+
export type CorpusTier = "cold" | "warm" | "hot";
|
|
46
|
+
|
|
47
|
+
/** Threshold applied at N=10 (warm entry). */
|
|
48
|
+
export const WARM_THRESHOLD_START = 0.80;
|
|
49
|
+
/** Threshold applied at N≥50 (hot entry). */
|
|
50
|
+
export const WARM_THRESHOLD_END = 0.68;
|
|
51
|
+
/** Corpus size where warm tier begins. */
|
|
52
|
+
export const WARM_CORPUS_MIN = 10;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Classify corpus into cold / warm / hot based on document count.
|
|
56
|
+
*/
|
|
57
|
+
export function computeCorpusTier(corpusSize: number): CorpusTier {
|
|
58
|
+
if (corpusSize < WARM_CORPUS_MIN) return "cold";
|
|
59
|
+
if (corpusSize < BM25_CORPUS_MIN) return "warm";
|
|
60
|
+
return "hot";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Compute the cosine similarity threshold for the given corpus size.
|
|
65
|
+
*
|
|
66
|
+
* Warm tier uses a linear gradient:
|
|
67
|
+
* threshold = max(WARM_THRESHOLD_END, WARM_THRESHOLD_START - (corpusSize - 10) * 0.003)
|
|
68
|
+
*
|
|
69
|
+
* At N=10 → 0.80 (strictest, smallest corpus)
|
|
70
|
+
* At N=50 → 0.68 (matches hot tier, smooth handoff)
|
|
71
|
+
* Hot tier → always 0.68 (EMBED_HIT_THRESHOLD)
|
|
72
|
+
* Cold tier → threshold is irrelevant (cache is not consulted)
|
|
73
|
+
*/
|
|
74
|
+
export function computeWarmThreshold(corpusSize: number): number {
|
|
75
|
+
const tier = computeCorpusTier(corpusSize);
|
|
76
|
+
if (tier === "cold") return WARM_THRESHOLD_START; // unused; cache skipped
|
|
77
|
+
if (tier === "hot") return WARM_THRESHOLD_END;
|
|
78
|
+
// warm: linear gradient
|
|
79
|
+
const raw = WARM_THRESHOLD_START - (corpusSize - WARM_CORPUS_MIN) * 0.003;
|
|
80
|
+
return Math.max(WARM_THRESHOLD_END, raw);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Stable 8-char project hash from absolute cwd path. */
|
|
84
|
+
export function projectHash(cwd: string): string {
|
|
85
|
+
return createHash("sha256").update(cwd).digest("hex").slice(0, 8);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function readCorpusDocCount(): number {
|
|
89
|
+
try {
|
|
90
|
+
const { readFileSync, existsSync } = require("fs") as typeof import("fs");
|
|
91
|
+
const { join } = require("path") as typeof import("path");
|
|
92
|
+
const { homedir } = require("os") as typeof import("os");
|
|
93
|
+
const p = join(process.env.HOME ?? homedir(), ".ashlr", "embed-corpus.json");
|
|
94
|
+
if (!existsSync(p)) return 0;
|
|
95
|
+
const raw = readFileSync(p, "utf-8");
|
|
96
|
+
const parsed = JSON.parse(raw) as { docCount?: number };
|
|
97
|
+
return typeof parsed.docCount === "number" && Number.isFinite(parsed.docCount) ? parsed.docCount : 0;
|
|
98
|
+
} catch {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Lightweight calibration log so we can tune EMBED_HIT_THRESHOLD from real
|
|
104
|
+
// data instead of guesses. Appends one JSONL record per grep call; fire-and-forget.
|
|
105
|
+
export async function recordEmbedCalibration(record: {
|
|
106
|
+
queryHashHex: string;
|
|
107
|
+
topSimilarity: number;
|
|
108
|
+
hit: boolean;
|
|
109
|
+
contentLength: number;
|
|
110
|
+
threshold: number;
|
|
111
|
+
}): Promise<void> {
|
|
112
|
+
try {
|
|
113
|
+
const { homedir } = await import("os");
|
|
114
|
+
const { appendFile, mkdir } = await import("fs/promises");
|
|
115
|
+
const { dirname, join } = await import("path");
|
|
116
|
+
const path = join(process.env.HOME ?? homedir(), ".ashlr", "embed-calibration.jsonl");
|
|
117
|
+
await mkdir(dirname(path), { recursive: true }).catch(() => undefined);
|
|
118
|
+
const line = JSON.stringify({ ts: new Date().toISOString(), ...record }) + "\n";
|
|
119
|
+
await appendFile(path, line, "utf-8");
|
|
120
|
+
} catch {
|
|
121
|
+
// Calibration log is observability, not critical path.
|
|
122
|
+
}
|
|
123
|
+
}
|