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,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sql-server-handlers — registers ashlr__sql on the shared registry.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { registerTool, type ToolCallContext, type ToolResult } from "./_tool-base";
|
|
6
|
+
import { ashlrSql, type SqlArgs } from "./sql-server";
|
|
7
|
+
|
|
8
|
+
registerTool({
|
|
9
|
+
name: "ashlr__sql",
|
|
10
|
+
description:
|
|
11
|
+
"Run SQL against SQLite or Postgres and get a compact, token-dense text " +
|
|
12
|
+
"result. Replaces the typical 3-4 Bash calls (psql / sqlite3 + parse " +
|
|
13
|
+
"stdout) with one tool call. Supports SELECT, DDL, DML, EXPLAIN, and a " +
|
|
14
|
+
"schema-introspection mode.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
query: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "SQL to run (SELECT, EXPLAIN, DDL, DML — all allowed). Required unless schema:true.",
|
|
21
|
+
},
|
|
22
|
+
connection: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description:
|
|
25
|
+
"Connection URL (postgres://…) or SQLite path. If omitted, reads $DATABASE_URL, then looks for *.db / *.sqlite files in cwd.",
|
|
26
|
+
},
|
|
27
|
+
explain: {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
description: "Wrap in EXPLAIN ANALYZE (postgres) / EXPLAIN QUERY PLAN (sqlite). Default false.",
|
|
30
|
+
},
|
|
31
|
+
limit: {
|
|
32
|
+
type: "number",
|
|
33
|
+
description: "Max rows to return in the compact output (default 20). Total row count is always reported.",
|
|
34
|
+
},
|
|
35
|
+
schema: {
|
|
36
|
+
type: "boolean",
|
|
37
|
+
description: "Skip the query and instead list tables, columns, and row counts. Cheaper than many \\d / SHOW TABLES round-trips.",
|
|
38
|
+
},
|
|
39
|
+
bypassSummary: {
|
|
40
|
+
type: "boolean",
|
|
41
|
+
description: "Skip LLM summarization of long output",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
handler: async (args: Record<string, unknown>, _ctx: ToolCallContext): Promise<ToolResult> => {
|
|
46
|
+
try {
|
|
47
|
+
const text = await ashlrSql((args ?? {}) as SqlArgs);
|
|
48
|
+
return { content: [{ type: "text", text }] };
|
|
49
|
+
} catch (err) {
|
|
50
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
51
|
+
const firstLine = message.split("\n")[0]!.slice(0, 400);
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: "text", text: `ashlr__sql error: ${firstLine}` }],
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* ashlr-sql MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Exposes a single tool, `ashlr__sql`, that runs SQL against SQLite or
|
|
6
|
+
* Postgres and returns a compact, token-dense text result. Designed to
|
|
7
|
+
* collapse the typical "shell out to psql / sqlite3 + reparse stdout" loop
|
|
8
|
+
* into one tool call.
|
|
9
|
+
*
|
|
10
|
+
* Drivers:
|
|
11
|
+
* - SQLite: bun:sqlite (built-in)
|
|
12
|
+
* - Postgres: porsager/postgres
|
|
13
|
+
* - MySQL/mssql: explicitly unsupported in v0.2 — clear error.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
17
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
|
+
import {
|
|
19
|
+
CallToolRequestSchema,
|
|
20
|
+
ListToolsRequestSchema,
|
|
21
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
22
|
+
import { Database } from "bun:sqlite";
|
|
23
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
24
|
+
import { isAbsolute, join, resolve } from "path";
|
|
25
|
+
import { summarizeIfLarge, PROMPTS, confidenceBadge, confidenceTier } from "./_summarize";
|
|
26
|
+
import { recordSaving as recordSavingCore } from "./_stats";
|
|
27
|
+
import { logEvent } from "./_events";
|
|
28
|
+
|
|
29
|
+
async function recordSaving(rawChars: number, compactChars: number): Promise<void> {
|
|
30
|
+
await recordSavingCore(rawChars, compactChars, "ashlr__sql");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Connection auto-detection
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
type Kind = "sqlite" | "postgres" | "mysql" | "mssql" | "unknown";
|
|
38
|
+
|
|
39
|
+
interface Conn {
|
|
40
|
+
kind: Kind;
|
|
41
|
+
/** Original spec (URL or path) — never logged verbatim if it has a password. */
|
|
42
|
+
raw: string;
|
|
43
|
+
/** Header-safe label with the password redacted. */
|
|
44
|
+
display: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function classify(spec: string): Conn {
|
|
48
|
+
const s = spec.trim();
|
|
49
|
+
if (/^postgres(ql)?:\/\//i.test(s)) return { kind: "postgres", raw: s, display: redactUrl(s) };
|
|
50
|
+
if (/^mysql:\/\//i.test(s)) return { kind: "mysql", raw: s, display: redactUrl(s) };
|
|
51
|
+
if (/^(mssql|sqlserver):\/\//i.test(s)) return { kind: "mssql", raw: s, display: redactUrl(s) };
|
|
52
|
+
if (/^sqlite:\/\//i.test(s)) {
|
|
53
|
+
const path = s.replace(/^sqlite:\/\//i, "");
|
|
54
|
+
return { kind: "sqlite", raw: path, display: `sqlite://${path}` };
|
|
55
|
+
}
|
|
56
|
+
// Treat as a filesystem path (relative or absolute, ":memory:" too).
|
|
57
|
+
return { kind: "sqlite", raw: s, display: `sqlite://${s}` };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function redactUrl(url: string): string {
|
|
61
|
+
// Replace the password component (between `:` and `@` after the scheme) with `***`.
|
|
62
|
+
return url.replace(/^([a-z]+:\/\/[^:/@]+):[^@/]*@/i, "$1:***@");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function autoDetectConnection(cwd: string): Conn | null {
|
|
66
|
+
const env = process.env.DATABASE_URL;
|
|
67
|
+
if (env && env.length > 0) return classify(env);
|
|
68
|
+
|
|
69
|
+
// Look for *.db / *.sqlite / *.sqlite3 in cwd (non-recursive — explicit > magic).
|
|
70
|
+
try {
|
|
71
|
+
const entries = readdirSync(cwd).filter((f) => /\.(db|sqlite3?|s3db)$/i.test(f));
|
|
72
|
+
if (entries.length > 0) {
|
|
73
|
+
// If there's exactly one, take it. If multiple, prefer one named like the
|
|
74
|
+
// dir; otherwise pick the most-recently-modified — deterministic on a
|
|
75
|
+
// given filesystem and matches what a human would do.
|
|
76
|
+
let pick = entries[0]!;
|
|
77
|
+
if (entries.length > 1) {
|
|
78
|
+
const sorted = entries
|
|
79
|
+
.map((f) => ({ f, m: statSync(join(cwd, f)).mtimeMs }))
|
|
80
|
+
.sort((a, b) => b.m - a.m);
|
|
81
|
+
pick = sorted[0]!.f;
|
|
82
|
+
}
|
|
83
|
+
return classify(join(cwd, pick));
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// unreadable cwd — fall through
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Compact output formatter
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
interface QueryResult {
|
|
96
|
+
cols: string[];
|
|
97
|
+
rows: unknown[][];
|
|
98
|
+
totalRows: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function fmtCell(v: unknown): string {
|
|
102
|
+
if (v === null || v === undefined) return "NULL";
|
|
103
|
+
if (v instanceof Date) return v.toISOString();
|
|
104
|
+
if (typeof v === "string") return v;
|
|
105
|
+
if (typeof v === "object") {
|
|
106
|
+
try {
|
|
107
|
+
return JSON.stringify(v);
|
|
108
|
+
} catch {
|
|
109
|
+
return String(v);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return String(v);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function renderTable(cols: string[], rows: unknown[][]): string {
|
|
116
|
+
if (cols.length === 0) return "";
|
|
117
|
+
const widths = cols.map((c, i) => {
|
|
118
|
+
let w = c.length;
|
|
119
|
+
for (const r of rows) {
|
|
120
|
+
const cell = fmtCell(r[i]);
|
|
121
|
+
if (cell.length > w) w = cell.length;
|
|
122
|
+
}
|
|
123
|
+
return Math.min(w, 60); // cap column width for readability
|
|
124
|
+
});
|
|
125
|
+
const pad = (s: string, w: number) => (s.length > w ? s.slice(0, w - 1) + "…" : s.padEnd(w));
|
|
126
|
+
const header = " " + cols.map((c, i) => pad(c, widths[i]!)).join(" | ");
|
|
127
|
+
const sep = " " + widths.map((w) => "─".repeat(w)).join("─┼─");
|
|
128
|
+
const body = rows
|
|
129
|
+
.map((r) => " " + cols.map((_, i) => pad(fmtCell(r[i]), widths[i]!)).join(" | "))
|
|
130
|
+
.join("\n");
|
|
131
|
+
return [header, sep, body].join("\n");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function csvBaselineBytes(cols: string[], rows: unknown[][]): number {
|
|
135
|
+
// Realistic CSV baseline: header + each row joined by commas + newlines.
|
|
136
|
+
let bytes = cols.join(",").length + 1;
|
|
137
|
+
for (const r of rows) {
|
|
138
|
+
let line = 0;
|
|
139
|
+
for (let i = 0; i < cols.length; i++) {
|
|
140
|
+
const cell = fmtCell(r[i]);
|
|
141
|
+
// Quote if contains comma/quote/newline (csv standard adds 2 chars + escapes).
|
|
142
|
+
if (/[,"\n]/.test(cell)) line += cell.length + 2 + (cell.match(/"/g)?.length ?? 0);
|
|
143
|
+
else line += cell.length;
|
|
144
|
+
if (i < cols.length - 1) line += 1;
|
|
145
|
+
}
|
|
146
|
+
bytes += line + 1;
|
|
147
|
+
}
|
|
148
|
+
return bytes;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function formatResult(
|
|
152
|
+
conn: Conn,
|
|
153
|
+
result: QueryResult,
|
|
154
|
+
elapsedSec: number,
|
|
155
|
+
limit: number,
|
|
156
|
+
): { text: string; baselineBytes: number } {
|
|
157
|
+
const { cols, rows, totalRows } = result;
|
|
158
|
+
const shown = rows.slice(0, limit);
|
|
159
|
+
const elided = totalRows - shown.length;
|
|
160
|
+
|
|
161
|
+
const header = `${conn.display} · ${elapsedSec.toFixed(3)}s · ${totalRows} row${totalRows === 1 ? "" : "s"} × ${cols.length} col${cols.length === 1 ? "" : "s"}`;
|
|
162
|
+
const body = cols.length === 0 ? " (no columns)" : renderTable(cols, shown);
|
|
163
|
+
const footer =
|
|
164
|
+
elided > 0
|
|
165
|
+
? ` [${totalRows} rows total · ${elided} elided; set limit:N to see more]`
|
|
166
|
+
: totalRows === 0
|
|
167
|
+
? " (no rows)"
|
|
168
|
+
: "";
|
|
169
|
+
|
|
170
|
+
const text = [header, "", body, footer].filter(Boolean).join("\n");
|
|
171
|
+
// Baseline = full result set as CSV (the "naive dump" the agent would otherwise pull).
|
|
172
|
+
const baselineBytes = csvBaselineBytes(cols, rows);
|
|
173
|
+
return { text, baselineBytes };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// SQLite driver
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
function runSqlite(spec: string, query: string, opts: { explain?: boolean; schema?: boolean }): {
|
|
181
|
+
result: QueryResult;
|
|
182
|
+
elapsedSec: number;
|
|
183
|
+
} {
|
|
184
|
+
// Normalize relative paths; ":memory:" passes through unchanged.
|
|
185
|
+
const path = spec === ":memory:" || isAbsolute(spec) ? spec : resolve(spec);
|
|
186
|
+
// create:true so DDL on a fresh path works; readwrite is the default behavior we want.
|
|
187
|
+
const db = new Database(path, { create: true, readwrite: true });
|
|
188
|
+
try {
|
|
189
|
+
if (opts.schema) {
|
|
190
|
+
const tables = db
|
|
191
|
+
.prepare(
|
|
192
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name",
|
|
193
|
+
)
|
|
194
|
+
.all() as { name: string }[];
|
|
195
|
+
const cols = ["table", "columns", "rows"];
|
|
196
|
+
const rows: unknown[][] = [];
|
|
197
|
+
for (const t of tables) {
|
|
198
|
+
const info = db.prepare(`PRAGMA table_info("${t.name.replace(/"/g, '""')}")`).all() as {
|
|
199
|
+
name: string;
|
|
200
|
+
type: string;
|
|
201
|
+
}[];
|
|
202
|
+
const colDesc = info.map((c) => `${c.name}:${c.type || "ANY"}`).join(", ");
|
|
203
|
+
const count = (
|
|
204
|
+
db.prepare(`SELECT COUNT(*) AS c FROM "${t.name.replace(/"/g, '""')}"`).get() as {
|
|
205
|
+
c: number;
|
|
206
|
+
}
|
|
207
|
+
).c;
|
|
208
|
+
rows.push([t.name, colDesc, count]);
|
|
209
|
+
}
|
|
210
|
+
return { result: { cols, rows, totalRows: rows.length }, elapsedSec: 0 };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const sql = opts.explain ? `EXPLAIN QUERY PLAN ${query}` : query;
|
|
214
|
+
const t0 = performance.now();
|
|
215
|
+
|
|
216
|
+
// Classify by leading keyword: row-returning vs side-effect. EXPLAIN QUERY
|
|
217
|
+
// PLAN always returns rows. Multiple statements (separated by `;`) are
|
|
218
|
+
// routed through .run() via db.run() so all are executed.
|
|
219
|
+
const head = query.replace(/^\s*(\/\*[\s\S]*?\*\/|--[^\n]*\n)*\s*/, "").slice(0, 16).toUpperCase();
|
|
220
|
+
const returnsRows =
|
|
221
|
+
opts.explain ||
|
|
222
|
+
head.startsWith("SELECT") ||
|
|
223
|
+
head.startsWith("WITH") ||
|
|
224
|
+
head.startsWith("VALUES") ||
|
|
225
|
+
head.startsWith("PRAGMA") ||
|
|
226
|
+
head.startsWith("EXPLAIN");
|
|
227
|
+
|
|
228
|
+
let cols: string[] = [];
|
|
229
|
+
let rows: unknown[][] = [];
|
|
230
|
+
|
|
231
|
+
if (returnsRows) {
|
|
232
|
+
const stmt = db.prepare(sql);
|
|
233
|
+
const out = stmt.all() as Record<string, unknown>[];
|
|
234
|
+
if (out.length > 0) {
|
|
235
|
+
cols = Object.keys(out[0]!);
|
|
236
|
+
rows = out.map((r) => cols.map((c) => r[c]));
|
|
237
|
+
} else {
|
|
238
|
+
const cnames = (stmt as unknown as { columnNames?: string[] }).columnNames;
|
|
239
|
+
if (cnames) cols = cnames;
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
// db.run handles multi-statement scripts; .changes reflects the last stmt.
|
|
243
|
+
const info = db.run(sql);
|
|
244
|
+
cols = ["changes", "lastInsertRowid"];
|
|
245
|
+
rows = [[info.changes, Number(info.lastInsertRowid)]];
|
|
246
|
+
}
|
|
247
|
+
const elapsedSec = (performance.now() - t0) / 1000;
|
|
248
|
+
return { result: { cols, rows, totalRows: rows.length }, elapsedSec };
|
|
249
|
+
} finally {
|
|
250
|
+
db.close();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
// Postgres driver
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
|
|
258
|
+
async function runPostgres(
|
|
259
|
+
spec: string,
|
|
260
|
+
query: string,
|
|
261
|
+
opts: { explain?: boolean; schema?: boolean },
|
|
262
|
+
): Promise<{ result: QueryResult; elapsedSec: number }> {
|
|
263
|
+
// Lazy-import so the module load doesn't fail if the dep is missing in some
|
|
264
|
+
// installs; the explicit message is friendlier than a cryptic resolve error.
|
|
265
|
+
let postgres: (url: string, opts?: Record<string, unknown>) => any;
|
|
266
|
+
try {
|
|
267
|
+
const mod = await import("postgres");
|
|
268
|
+
postgres = (mod as unknown as { default: typeof postgres }).default ?? (mod as unknown as typeof postgres);
|
|
269
|
+
} catch {
|
|
270
|
+
throw new Error(
|
|
271
|
+
"Postgres driver not installed. Run `bun add postgres` in the plugin directory.",
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const sql = postgres(spec, {
|
|
276
|
+
connect_timeout: 10,
|
|
277
|
+
idle_timeout: 5,
|
|
278
|
+
max: 1,
|
|
279
|
+
prepare: false, // safer for ad-hoc queries
|
|
280
|
+
onnotice: () => {},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
if (opts.schema) {
|
|
285
|
+
const tables = (await sql.unsafe(
|
|
286
|
+
`SELECT table_schema, table_name
|
|
287
|
+
FROM information_schema.tables
|
|
288
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
289
|
+
AND table_type = 'BASE TABLE'
|
|
290
|
+
ORDER BY table_schema, table_name`,
|
|
291
|
+
)) as { table_schema: string; table_name: string }[];
|
|
292
|
+
const cols = ["table", "columns", "rows"];
|
|
293
|
+
const rows: unknown[][] = [];
|
|
294
|
+
for (const t of tables) {
|
|
295
|
+
const info = (await sql.unsafe(
|
|
296
|
+
`SELECT column_name, data_type
|
|
297
|
+
FROM information_schema.columns
|
|
298
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
299
|
+
ORDER BY ordinal_position`,
|
|
300
|
+
[t.table_schema, t.table_name],
|
|
301
|
+
)) as { column_name: string; data_type: string }[];
|
|
302
|
+
const colDesc = info.map((c) => `${c.column_name}:${c.data_type}`).join(", ");
|
|
303
|
+
let count: number | string = "?";
|
|
304
|
+
try {
|
|
305
|
+
const r = (await sql.unsafe(
|
|
306
|
+
`SELECT COUNT(*)::bigint AS c FROM "${t.table_schema}"."${t.table_name}"`,
|
|
307
|
+
)) as { c: string }[];
|
|
308
|
+
count = Number(r[0]!.c);
|
|
309
|
+
} catch {
|
|
310
|
+
/* permission denied or similar — leave as "?" */
|
|
311
|
+
}
|
|
312
|
+
const label = t.table_schema === "public" ? t.table_name : `${t.table_schema}.${t.table_name}`;
|
|
313
|
+
rows.push([label, colDesc, count]);
|
|
314
|
+
}
|
|
315
|
+
return { result: { cols, rows, totalRows: rows.length }, elapsedSec: 0 };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const text = opts.explain ? `EXPLAIN ANALYZE ${query}` : query;
|
|
319
|
+
const t0 = performance.now();
|
|
320
|
+
// 60s query timeout via a Promise.race — postgres.js doesn't natively expose
|
|
321
|
+
// a per-query timeout that aborts cleanly without statement_timeout, which
|
|
322
|
+
// requires a session round-trip. Race + end() is the pragmatic move.
|
|
323
|
+
const queryPromise = sql.unsafe(text);
|
|
324
|
+
const result = (await Promise.race([
|
|
325
|
+
queryPromise,
|
|
326
|
+
new Promise((_, reject) =>
|
|
327
|
+
setTimeout(() => reject(new Error("Query timeout (60s)")), 60_000),
|
|
328
|
+
),
|
|
329
|
+
])) as Record<string, unknown>[];
|
|
330
|
+
const elapsedSec = (performance.now() - t0) / 1000;
|
|
331
|
+
|
|
332
|
+
let cols: string[] = [];
|
|
333
|
+
let rows: unknown[][] = [];
|
|
334
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
335
|
+
cols = Object.keys(result[0]!);
|
|
336
|
+
rows = result.map((r) => cols.map((c) => r[c]));
|
|
337
|
+
} else if (Array.isArray(result)) {
|
|
338
|
+
// Try to grab column descriptors from the result object (postgres.js attaches them).
|
|
339
|
+
const desc = (result as unknown as { columns?: { name: string }[] }).columns;
|
|
340
|
+
if (desc) cols = desc.map((c) => c.name);
|
|
341
|
+
}
|
|
342
|
+
return { result: { cols, rows, totalRows: rows.length }, elapsedSec };
|
|
343
|
+
} finally {
|
|
344
|
+
await sql.end({ timeout: 1 });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ---------------------------------------------------------------------------
|
|
349
|
+
// Tool entry point
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
|
|
352
|
+
export interface SqlArgs {
|
|
353
|
+
query?: string;
|
|
354
|
+
connection?: string;
|
|
355
|
+
explain?: boolean;
|
|
356
|
+
limit?: number;
|
|
357
|
+
schema?: boolean;
|
|
358
|
+
bypassSummary?: boolean;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export async function ashlrSql(input: SqlArgs): Promise<string> {
|
|
362
|
+
const limit = typeof input.limit === "number" && input.limit > 0 ? Math.floor(input.limit) : 20;
|
|
363
|
+
const explain = input.explain === true;
|
|
364
|
+
const schema = input.schema === true;
|
|
365
|
+
const query = (input.query ?? "").trim();
|
|
366
|
+
|
|
367
|
+
if (!schema && query.length === 0) {
|
|
368
|
+
throw new Error("ashlr__sql: 'query' is required (or set schema:true to introspect).");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Resolve connection.
|
|
372
|
+
const conn = input.connection
|
|
373
|
+
? classify(input.connection)
|
|
374
|
+
: autoDetectConnection(process.cwd());
|
|
375
|
+
if (!conn) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
"No connection found. Pass `connection` (postgres://… or path/to/file.db), or set $DATABASE_URL, or run from a directory containing a *.db / *.sqlite file.",
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (conn.kind === "mysql" || conn.kind === "mssql") {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`${conn.kind} is not supported in ashlr v0.2. Track support at https://github.com/ashlrai/ashlr-plugin/issues`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
if (conn.kind === "unknown") {
|
|
387
|
+
throw new Error(`Unrecognized connection: ${conn.display}`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
let runOut: { result: QueryResult; elapsedSec: number };
|
|
391
|
+
if (conn.kind === "sqlite") {
|
|
392
|
+
runOut = runSqlite(conn.raw, query, { explain, schema });
|
|
393
|
+
} else {
|
|
394
|
+
runOut = await runPostgres(conn.raw, query, { explain, schema });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// EXPLAIN: emit the plan as a single text block per row, no table chrome.
|
|
398
|
+
if (explain) {
|
|
399
|
+
const lines = runOut.result.rows.map((r) => r.map(fmtCell).join(" ")).join("\n");
|
|
400
|
+
const header = `${conn.display} · ${runOut.elapsedSec.toFixed(3)}s · EXPLAIN`;
|
|
401
|
+
const text = `${header}\n\n${lines || "(empty plan)"}`;
|
|
402
|
+
await recordSaving(csvBaselineBytes(runOut.result.cols, runOut.result.rows), text.length);
|
|
403
|
+
return text;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const { text, baselineBytes } = formatResult(conn, runOut.result, runOut.elapsedSec, limit);
|
|
407
|
+
|
|
408
|
+
// LLM summarization — only for large result sets that exceed 16KB rendered.
|
|
409
|
+
// EXPLAIN and schema modes are already structured and bail out before here.
|
|
410
|
+
let finalText = text;
|
|
411
|
+
if (
|
|
412
|
+
runOut.result.rows.length > 100 &&
|
|
413
|
+
Buffer.byteLength(finalText, "utf-8") > 16_384 &&
|
|
414
|
+
!input.bypassSummary
|
|
415
|
+
) {
|
|
416
|
+
const s = await summarizeIfLarge(finalText, {
|
|
417
|
+
toolName: "ashlr__sql",
|
|
418
|
+
systemPrompt: PROMPTS.sql,
|
|
419
|
+
bypass: false,
|
|
420
|
+
});
|
|
421
|
+
finalText = s.text;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
await recordSaving(baselineBytes, finalText.length);
|
|
425
|
+
|
|
426
|
+
const sqlBadgeOpts = {
|
|
427
|
+
toolName: "ashlr__sql",
|
|
428
|
+
rawBytes: baselineBytes,
|
|
429
|
+
outputBytes: finalText.length,
|
|
430
|
+
};
|
|
431
|
+
if (confidenceTier(sqlBadgeOpts) === "low") {
|
|
432
|
+
await logEvent("tool_low_confidence_shipped", { tool: "ashlr__sql", reason: "low-confidence" });
|
|
433
|
+
}
|
|
434
|
+
return finalText + confidenceBadge(sqlBadgeOpts);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
// MCP server wiring
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
|
|
441
|
+
const server = new Server(
|
|
442
|
+
{ name: "ashlr-sql", version: "0.2.0" },
|
|
443
|
+
{ capabilities: { tools: {} } },
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
447
|
+
tools: [
|
|
448
|
+
{
|
|
449
|
+
name: "ashlr__sql",
|
|
450
|
+
description:
|
|
451
|
+
"Run SQL against SQLite or Postgres and get a compact, token-dense text result. Replaces the typical 3-4 Bash calls (psql / sqlite3 + parse stdout) with one tool call. Supports SELECT, DDL, DML, EXPLAIN, and a schema-introspection mode.",
|
|
452
|
+
inputSchema: {
|
|
453
|
+
type: "object",
|
|
454
|
+
properties: {
|
|
455
|
+
query: {
|
|
456
|
+
type: "string",
|
|
457
|
+
description: "SQL to run (SELECT, EXPLAIN, DDL, DML — all allowed). Required unless schema:true.",
|
|
458
|
+
},
|
|
459
|
+
connection: {
|
|
460
|
+
type: "string",
|
|
461
|
+
description:
|
|
462
|
+
"Connection URL (postgres://…) or SQLite path. If omitted, reads $DATABASE_URL, then looks for *.db / *.sqlite files in cwd.",
|
|
463
|
+
},
|
|
464
|
+
explain: {
|
|
465
|
+
type: "boolean",
|
|
466
|
+
description: "Wrap in EXPLAIN ANALYZE (postgres) / EXPLAIN QUERY PLAN (sqlite). Default false.",
|
|
467
|
+
},
|
|
468
|
+
limit: {
|
|
469
|
+
type: "number",
|
|
470
|
+
description: "Max rows to return in the compact output (default 20). Total row count is always reported.",
|
|
471
|
+
},
|
|
472
|
+
schema: {
|
|
473
|
+
type: "boolean",
|
|
474
|
+
description: "Skip the query and instead list tables, columns, and row counts. Cheaper than many \\d / SHOW TABLES round-trips.",
|
|
475
|
+
},
|
|
476
|
+
bypassSummary: {
|
|
477
|
+
type: "boolean",
|
|
478
|
+
description: "Skip LLM summarization of long output",
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
}));
|
|
485
|
+
|
|
486
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
487
|
+
const { name, arguments: args } = req.params;
|
|
488
|
+
try {
|
|
489
|
+
if (name === "ashlr__sql") {
|
|
490
|
+
const text = await ashlrSql((args ?? {}) as SqlArgs);
|
|
491
|
+
return { content: [{ type: "text", text }] };
|
|
492
|
+
}
|
|
493
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
494
|
+
} catch (err) {
|
|
495
|
+
// Short, clean error — never the driver's full stack.
|
|
496
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
497
|
+
const firstLine = message.split("\n")[0]!.slice(0, 400);
|
|
498
|
+
return { content: [{ type: "text", text: `ashlr__sql error: ${firstLine}` }], isError: true };
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (import.meta.main) {
|
|
503
|
+
const transport = new StdioServerTransport();
|
|
504
|
+
await server.connect(transport);
|
|
505
|
+
}
|