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,149 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* ashlr-http MCP server — compressed HTTP fetch.
|
|
4
|
+
*
|
|
5
|
+
* Exposes a single tool `ashlr__http` that fetches a URL and returns a
|
|
6
|
+
* compressed representation (default for HTML: extract main content; JSON:
|
|
7
|
+
* pretty + array-elide; raw: no compression beyond byte cap; headers: just
|
|
8
|
+
* response headers). Tracks savings in ~/.ashlr/stats.json.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import {
|
|
14
|
+
CallToolRequestSchema,
|
|
15
|
+
ListToolsRequestSchema,
|
|
16
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
+
import { confidenceBadge, confidenceTier, summarizeIfLarge, PROMPTS } from "./_summarize";
|
|
18
|
+
import { recordSavingAccurate } from "./_accounting";
|
|
19
|
+
import { logEvent } from "./_events";
|
|
20
|
+
export { isPrivateHost, compressHtml, compressJson } from "./_http-helpers";
|
|
21
|
+
import { compressHtml, compressJson, safeFetch } from "./_http-helpers";
|
|
22
|
+
|
|
23
|
+
// ---------- fetch ----------
|
|
24
|
+
|
|
25
|
+
export interface HttpArgs {
|
|
26
|
+
url: string;
|
|
27
|
+
method?: string;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
body?: string;
|
|
30
|
+
mode?: "readable" | "raw" | "json" | "headers";
|
|
31
|
+
maxBytes?: number;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function doFetch(args: HttpArgs): Promise<string> {
|
|
36
|
+
const { url, method = "GET", headers = {}, body, mode: reqMode, maxBytes = 2_000_000, timeoutMs = 15_000 } = args;
|
|
37
|
+
|
|
38
|
+
const ctl = new AbortController();
|
|
39
|
+
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
40
|
+
let res: Response;
|
|
41
|
+
try {
|
|
42
|
+
// safeFetch handles URL validation + scheme + isPrivateHost + SSRF-safe
|
|
43
|
+
// manual redirect validation (each hop re-checked against isPrivateHost).
|
|
44
|
+
res = await safeFetch(url, {
|
|
45
|
+
method,
|
|
46
|
+
headers: { "user-agent": "ashlr-plugin/0.9.2 (+https://plugin.ashlr.ai)", ...headers },
|
|
47
|
+
body,
|
|
48
|
+
signal: ctl.signal,
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
clearTimeout(t);
|
|
52
|
+
throw new Error(`fetch failed: ${(err as Error).message}`);
|
|
53
|
+
}
|
|
54
|
+
clearTimeout(t);
|
|
55
|
+
|
|
56
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
57
|
+
|
|
58
|
+
if (reqMode === "headers") {
|
|
59
|
+
const wanted = ["content-type", "content-length", "etag", "last-modified", "location", "cache-control"];
|
|
60
|
+
const lines = [`${method} ${url} · ${res.status}`];
|
|
61
|
+
for (const h of wanted) {
|
|
62
|
+
const v = res.headers.get(h);
|
|
63
|
+
if (v) lines.push(` ${h}: ${v}`);
|
|
64
|
+
}
|
|
65
|
+
await recordSavingAccurate({ rawBytes: 2000, compactBytes: lines.join("\n").length, toolName: "ashlr__http", cacheHit: false });
|
|
66
|
+
return lines.join("\n");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const buf = await res.arrayBuffer();
|
|
70
|
+
const raw = new TextDecoder().decode(buf.slice(0, maxBytes));
|
|
71
|
+
|
|
72
|
+
let extracted: string;
|
|
73
|
+
const mode = reqMode ?? (ct.includes("json") ? "json" : ct.includes("html") ? "readable" : "raw");
|
|
74
|
+
switch (mode) {
|
|
75
|
+
case "readable": extracted = compressHtml(raw); break;
|
|
76
|
+
case "json": extracted = compressJson(raw); break;
|
|
77
|
+
case "raw": extracted = raw; break;
|
|
78
|
+
default: extracted = raw;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// LLM summarization path for large responses.
|
|
82
|
+
const summResult = await summarizeIfLarge(extracted, {
|
|
83
|
+
toolName: "ashlr__http",
|
|
84
|
+
systemPrompt: PROMPTS.http,
|
|
85
|
+
});
|
|
86
|
+
const compact = (summResult.summarized || summResult.wasCached) ? summResult.text : extracted;
|
|
87
|
+
|
|
88
|
+
await recordSavingAccurate({
|
|
89
|
+
rawBytes: raw.length,
|
|
90
|
+
compactBytes: compact.length,
|
|
91
|
+
toolName: "ashlr__http",
|
|
92
|
+
cacheHit: summResult.wasCached,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const header = `${method} ${url} · ${res.status} · ${ct || "?"} · ${(raw.length / 1024).toFixed(1)} KB → ${(compact.length / 1024).toFixed(1)} KB`;
|
|
96
|
+
const httpBadgeOpts = {
|
|
97
|
+
toolName: "ashlr__http",
|
|
98
|
+
rawBytes: raw.length,
|
|
99
|
+
outputBytes: compact.length,
|
|
100
|
+
};
|
|
101
|
+
if (confidenceTier(httpBadgeOpts) === "low") {
|
|
102
|
+
await logEvent("tool_low_confidence_shipped", { tool: "ashlr__http", reason: "low-confidence" });
|
|
103
|
+
}
|
|
104
|
+
return header + "\n\n" + compact + confidenceBadge(httpBadgeOpts);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------- MCP wiring ----------
|
|
108
|
+
|
|
109
|
+
const server = new Server(
|
|
110
|
+
{ name: "ashlr-http", version: "0.5.0" },
|
|
111
|
+
{ capabilities: { tools: {} } },
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
115
|
+
tools: [{
|
|
116
|
+
name: "ashlr__http",
|
|
117
|
+
description: "HTTP fetch with compressed output. Readable-extracts main content from HTML, pretty-prints + array-elides JSON, bounded byte cap. Refuses non-http/https schemes and private hosts by default.",
|
|
118
|
+
inputSchema: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
url: { type: "string" },
|
|
122
|
+
method: { type: "string", description: "HTTP method (default GET)" },
|
|
123
|
+
headers: { type: "object", description: "Request headers" },
|
|
124
|
+
body: { type: "string", description: "Request body for POST/PUT" },
|
|
125
|
+
mode: { type: "string", description: "'readable' (HTML→main content) | 'raw' | 'json' | 'headers'" },
|
|
126
|
+
maxBytes: { type: "number", description: "Response body cap before compression (default 2_000_000)" },
|
|
127
|
+
timeoutMs: { type: "number", description: "Request timeout (default 15000)" },
|
|
128
|
+
},
|
|
129
|
+
required: ["url"],
|
|
130
|
+
},
|
|
131
|
+
}],
|
|
132
|
+
}));
|
|
133
|
+
|
|
134
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
135
|
+
if (req.params.name !== "ashlr__http") {
|
|
136
|
+
return { content: [{ type: "text", text: `Unknown tool: ${req.params.name}` }], isError: true };
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const text = await doFetch(req.params.arguments as unknown as HttpArgs);
|
|
140
|
+
return { content: [{ type: "text", text }] };
|
|
141
|
+
} catch (err) {
|
|
142
|
+
return { content: [{ type: "text", text: `ashlr__http error: ${(err as Error).message}` }], isError: true };
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (import.meta.main) {
|
|
147
|
+
const transport = new StdioServerTransport();
|
|
148
|
+
await server.connect(transport);
|
|
149
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* logs-server-handlers — registers ashlr__logs on the shared registry.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { registerTool, type ToolCallContext, type ToolResult } from "./_tool-base";
|
|
6
|
+
import { ashlrLogs, type LogsArgs } from "./logs-server";
|
|
7
|
+
|
|
8
|
+
registerTool({
|
|
9
|
+
name: "ashlr__logs",
|
|
10
|
+
description:
|
|
11
|
+
"Tail a log file efficiently. Reads the last N lines (default 200), " +
|
|
12
|
+
"detects severity from bracketed tags, bare prefixes, JSON/logfmt level " +
|
|
13
|
+
"fields, and Python tracebacks, filters by level and/or ISO timestamp, " +
|
|
14
|
+
"and collapses runs of identical consecutive lines into '(42x) ...'. " +
|
|
15
|
+
"Supports glob paths. Lines with no parseable timestamp are skipped " +
|
|
16
|
+
"(never fatal) when 'since' is set.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
path: { type: "string", description: "Log file path (supports glob)" },
|
|
21
|
+
lines: { type: "number", description: "Tail window (default 200)" },
|
|
22
|
+
level: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "Filter: 'all' (default) | 'error' | 'warn' | 'error+warn'",
|
|
25
|
+
},
|
|
26
|
+
since: { type: "string", description: "ISO timestamp — only lines after this" },
|
|
27
|
+
dedupe: {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
description: "Collapse repeated consecutive lines with count (default: true)",
|
|
30
|
+
},
|
|
31
|
+
bypassSummary: {
|
|
32
|
+
type: "boolean",
|
|
33
|
+
description: "If true, skip LLM summarization and return the full rendered log tail.",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ["path"],
|
|
37
|
+
},
|
|
38
|
+
handler: async (args: Record<string, unknown>, _ctx: ToolCallContext): Promise<ToolResult> => {
|
|
39
|
+
const text = await ashlrLogs(args as unknown as LogsArgs);
|
|
40
|
+
return { content: [{ type: "text", text }] };
|
|
41
|
+
},
|
|
42
|
+
});
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* ashlr-logs MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Exposes ashlr__logs — a token-efficient log tail. Reads the last N lines
|
|
6
|
+
* of a log file (or glob), detects severity with a suite of common patterns
|
|
7
|
+
* (bracketed [ERROR], bare "error:", Python tracebacks, JSON {"level":...}),
|
|
8
|
+
* optionally filters by level and/or timestamp, and collapses runs of
|
|
9
|
+
* identical lines into "(42x) same message".
|
|
10
|
+
*
|
|
11
|
+
* Savings are persisted to the shared ~/.ashlr/stats.json.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
15
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
16
|
+
import {
|
|
17
|
+
CallToolRequestSchema,
|
|
18
|
+
ListToolsRequestSchema,
|
|
19
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
import { existsSync, readFileSync, statSync } from "fs";
|
|
21
|
+
import { glob } from "fs/promises";
|
|
22
|
+
import { join, isAbsolute } from "path";
|
|
23
|
+
import { summarizeIfLarge, PROMPTS, confidenceBadge, confidenceTier } from "./_summarize";
|
|
24
|
+
import { recordSaving as recordSavingCore } from "./_stats";
|
|
25
|
+
import { logEvent } from "./_events";
|
|
26
|
+
|
|
27
|
+
async function recordSaving(
|
|
28
|
+
rawBytes: number,
|
|
29
|
+
compactBytes: number,
|
|
30
|
+
tool: "ashlr__logs",
|
|
31
|
+
): Promise<number> {
|
|
32
|
+
return recordSavingCore(rawBytes, compactBytes, tool);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Last-N-lines reader. Seeks from end of file and reads 64KB chunks backward
|
|
37
|
+
// until we've accumulated enough newlines or we hit BOF.
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
function readLastLines(absPath: string, nLines: number): string[] {
|
|
41
|
+
const fd = require("fs").openSync(absPath, "r");
|
|
42
|
+
try {
|
|
43
|
+
const size = statSync(absPath).size;
|
|
44
|
+
const chunk = 64 * 1024;
|
|
45
|
+
let offset = size;
|
|
46
|
+
let buf = Buffer.alloc(0);
|
|
47
|
+
let newlines = 0;
|
|
48
|
+
while (offset > 0 && newlines <= nLines) {
|
|
49
|
+
const toRead = Math.min(chunk, offset);
|
|
50
|
+
offset -= toRead;
|
|
51
|
+
const b = Buffer.alloc(toRead);
|
|
52
|
+
require("fs").readSync(fd, b, 0, toRead, offset);
|
|
53
|
+
buf = Buffer.concat([b, buf]);
|
|
54
|
+
for (let i = 0; i < b.length; i++) if (b[i] === 10) newlines++;
|
|
55
|
+
}
|
|
56
|
+
const text = buf.toString("utf-8");
|
|
57
|
+
const all = text.split("\n");
|
|
58
|
+
// Trim trailing empty (file ended with \n).
|
|
59
|
+
if (all.length && all[all.length - 1] === "") all.pop();
|
|
60
|
+
if (all.length <= nLines) return all;
|
|
61
|
+
return all.slice(all.length - nLines);
|
|
62
|
+
} finally {
|
|
63
|
+
try { require("fs").closeSync(fd); } catch { /* ignore */ }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Level detection
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
type Level = "error" | "warn" | "info" | "debug" | "unknown";
|
|
72
|
+
|
|
73
|
+
// Ordered from most-specific to most-generic. The JSON-log detector runs
|
|
74
|
+
// first because logfmt/JSON lines can contain the word "error" as data and
|
|
75
|
+
// we want the explicit `level` field to win.
|
|
76
|
+
const LEVEL_PATTERNS: Array<{ level: Level; re: RegExp }> = [
|
|
77
|
+
// JSON logs: "level":"error" or 'level':'error'
|
|
78
|
+
{ level: "error", re: /"level"\s*:\s*"(?:error|fatal|critical)"/i },
|
|
79
|
+
{ level: "warn", re: /"level"\s*:\s*"(?:warn(?:ing)?)"/i },
|
|
80
|
+
{ level: "info", re: /"level"\s*:\s*"info"/i },
|
|
81
|
+
{ level: "debug", re: /"level"\s*:\s*"(?:debug|trace)"/i },
|
|
82
|
+
// logfmt: level=error
|
|
83
|
+
{ level: "error", re: /\blevel=(?:error|fatal|critical)\b/i },
|
|
84
|
+
{ level: "warn", re: /\blevel=warn(?:ing)?\b/i },
|
|
85
|
+
{ level: "info", re: /\blevel=info\b/i },
|
|
86
|
+
{ level: "debug", re: /\blevel=(?:debug|trace)\b/i },
|
|
87
|
+
// Bracketed: [ERROR] [WARN] [INFO]
|
|
88
|
+
{ level: "error", re: /\[(?:ERROR|ERR|FATAL|CRITICAL)\]/ },
|
|
89
|
+
{ level: "warn", re: /\[(?:WARN|WARNING)\]/ },
|
|
90
|
+
{ level: "info", re: /\[INFO\]/ },
|
|
91
|
+
{ level: "debug", re: /\[(?:DEBUG|TRACE)\]/ },
|
|
92
|
+
// Python tracebacks always imply an error context.
|
|
93
|
+
{ level: "error", re: /^Traceback \(most recent call last\):/ },
|
|
94
|
+
// Bare colon-prefixed: ERROR: / error: (at line start or after timestamp).
|
|
95
|
+
{ level: "error", re: /(?:^|\s)(?:ERROR|ERR|FATAL|CRITICAL):/ },
|
|
96
|
+
{ level: "warn", re: /(?:^|\s)(?:WARN|WARNING):/ },
|
|
97
|
+
{ level: "info", re: /(?:^|\s)INFO:/ },
|
|
98
|
+
// Lowercased variants common in many frameworks.
|
|
99
|
+
{ level: "error", re: /(?:^|\s)error:/ },
|
|
100
|
+
{ level: "warn", re: /(?:^|\s)warn(?:ing)?:/ },
|
|
101
|
+
// Isolated whole-word uppercase token — weakest signal, last resort.
|
|
102
|
+
{ level: "error", re: /(?<![A-Za-z])ERROR(?![A-Za-z])/ },
|
|
103
|
+
{ level: "warn", re: /(?<![A-Za-z])WARN(?:ING)?(?![A-Za-z])/ },
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
function detectLevel(line: string): Level {
|
|
107
|
+
for (const { level, re } of LEVEL_PATTERNS) {
|
|
108
|
+
if (re.test(line)) return level;
|
|
109
|
+
}
|
|
110
|
+
return "unknown";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Timestamp parsing (best-effort). Returns ms-epoch or null.
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
const TS_PATTERNS: RegExp[] = [
|
|
118
|
+
// ISO 8601: 2026-04-14T12:34:56(.789)?(Z|+00:00)?
|
|
119
|
+
/\b(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)\b/,
|
|
120
|
+
// Slashy: 2026/04/14 12:34:56
|
|
121
|
+
/\b(\d{4}\/\d{2}\/\d{2}\s+\d{2}:\d{2}:\d{2})\b/,
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
function parseTimestamp(line: string): number | null {
|
|
125
|
+
for (const re of TS_PATTERNS) {
|
|
126
|
+
const m = line.match(re);
|
|
127
|
+
if (m) {
|
|
128
|
+
const iso = m[1]!.replace(" ", "T").replace(/\//g, "-");
|
|
129
|
+
const t = Date.parse(iso);
|
|
130
|
+
if (!Number.isNaN(t)) return t;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Main
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
export interface LogsArgs {
|
|
141
|
+
path: string;
|
|
142
|
+
lines?: number;
|
|
143
|
+
level?: "all" | "error" | "warn" | "error+warn";
|
|
144
|
+
since?: string;
|
|
145
|
+
dedupe?: boolean;
|
|
146
|
+
cwd?: string;
|
|
147
|
+
bypassSummary?: boolean;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function resolvePath(input: string, cwd: string): Promise<string[]> {
|
|
151
|
+
// Detect glob characters.
|
|
152
|
+
if (/[*?\[]/.test(input)) {
|
|
153
|
+
const results: string[] = [];
|
|
154
|
+
for await (const f of glob(input, { cwd })) {
|
|
155
|
+
results.push(join(cwd, f));
|
|
156
|
+
}
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
const abs = isAbsolute(input) ? input : join(cwd, input);
|
|
160
|
+
return [abs];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function ashlrLogs(args: LogsArgs): Promise<string> {
|
|
164
|
+
if (!args.path) throw new Error("'path' is required");
|
|
165
|
+
const cwd = args.cwd ?? process.cwd();
|
|
166
|
+
const nLines = typeof args.lines === "number" && args.lines > 0 ? args.lines : 200;
|
|
167
|
+
const levelFilter = args.level ?? "all";
|
|
168
|
+
const dedupe = args.dedupe !== false;
|
|
169
|
+
|
|
170
|
+
const sinceMs = args.since ? Date.parse(args.since) : null;
|
|
171
|
+
if (args.since && Number.isNaN(sinceMs as number)) {
|
|
172
|
+
throw new Error(`'since' is not a parseable ISO timestamp: ${args.since}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const paths = await resolvePath(args.path, cwd);
|
|
176
|
+
const existing = paths.filter((p) => existsSync(p));
|
|
177
|
+
if (existing.length === 0) {
|
|
178
|
+
throw new Error(`no files matched: ${args.path}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const blocks: string[] = [];
|
|
182
|
+
let totalRawBytes = 0;
|
|
183
|
+
let totalScanned = 0;
|
|
184
|
+
let totalErrors = 0;
|
|
185
|
+
let totalWarnings = 0;
|
|
186
|
+
|
|
187
|
+
for (const p of existing) {
|
|
188
|
+
let lines: string[];
|
|
189
|
+
try {
|
|
190
|
+
lines = readLastLines(p, nLines);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
blocks.push(`logs \u00b7 ${p} \u00b7 read error: ${(err as Error).message}`);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const rawBytesForFile = lines.reduce((s, l) => s + l.length + 1, 0);
|
|
196
|
+
totalRawBytes += rawBytesForFile;
|
|
197
|
+
totalScanned += lines.length;
|
|
198
|
+
|
|
199
|
+
// Filter + level-tag pass.
|
|
200
|
+
interface Tagged { raw: string; level: Level; ts: number | null }
|
|
201
|
+
const tagged: Tagged[] = [];
|
|
202
|
+
let errCount = 0;
|
|
203
|
+
let warnCount = 0;
|
|
204
|
+
for (const raw of lines) {
|
|
205
|
+
let level: Level;
|
|
206
|
+
let ts: number | null = null;
|
|
207
|
+
try {
|
|
208
|
+
level = detectLevel(raw);
|
|
209
|
+
} catch {
|
|
210
|
+
level = "unknown";
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
ts = parseTimestamp(raw);
|
|
214
|
+
} catch {
|
|
215
|
+
ts = null;
|
|
216
|
+
}
|
|
217
|
+
if (level === "error") errCount++;
|
|
218
|
+
else if (level === "warn") warnCount++;
|
|
219
|
+
|
|
220
|
+
if (sinceMs !== null) {
|
|
221
|
+
// Skip lines with no parseable ts (don't fail the whole call).
|
|
222
|
+
if (ts === null) continue;
|
|
223
|
+
if (ts < (sinceMs as number)) continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (levelFilter === "error" && level !== "error") continue;
|
|
227
|
+
if (levelFilter === "warn" && level !== "warn") continue;
|
|
228
|
+
if (levelFilter === "error+warn" && level !== "error" && level !== "warn") continue;
|
|
229
|
+
|
|
230
|
+
tagged.push({ raw, level, ts });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
totalErrors += errCount;
|
|
234
|
+
totalWarnings += warnCount;
|
|
235
|
+
|
|
236
|
+
// Dedupe consecutive identical lines.
|
|
237
|
+
const out: string[] = [];
|
|
238
|
+
if (dedupe) {
|
|
239
|
+
let i = 0;
|
|
240
|
+
while (i < tagged.length) {
|
|
241
|
+
let j = i + 1;
|
|
242
|
+
while (j < tagged.length && tagged[j]!.raw === tagged[i]!.raw) j++;
|
|
243
|
+
const run = j - i;
|
|
244
|
+
if (run > 1) {
|
|
245
|
+
// Find a reasonable place to inject the multiplier prefix.
|
|
246
|
+
// We prepend "(NxX) " after any timestamp/level prefix we can detect.
|
|
247
|
+
out.push(`(${run}\u00d7) ${tagged[i]!.raw}`);
|
|
248
|
+
} else {
|
|
249
|
+
out.push(tagged[i]!.raw);
|
|
250
|
+
}
|
|
251
|
+
i = j;
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
for (const t of tagged) out.push(t.raw);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const header = `logs \u00b7 ${p} \u00b7 ${lines.length} lines scanned \u00b7 ${errCount} errors \u00b7 ${warnCount} warnings`;
|
|
258
|
+
blocks.push(header + "\n\n" + out.join("\n"));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let body = blocks.join("\n\n---\n\n");
|
|
262
|
+
|
|
263
|
+
// If the emitted body is still bigger than the raw tail, we didn't save
|
|
264
|
+
// anything — just surface the raw. But keep the header block either way.
|
|
265
|
+
void totalErrors;
|
|
266
|
+
void totalWarnings;
|
|
267
|
+
void totalScanned;
|
|
268
|
+
|
|
269
|
+
// LLM summarization: trigger when raw tail is large OR many errors detected.
|
|
270
|
+
const trigger = totalRawBytes > 16_384 || totalErrors > 5;
|
|
271
|
+
if (trigger && !args.bypassSummary) {
|
|
272
|
+
const s = await summarizeIfLarge(body, {
|
|
273
|
+
toolName: "ashlr__logs",
|
|
274
|
+
systemPrompt: PROMPTS.logs,
|
|
275
|
+
bypass: false,
|
|
276
|
+
// Force summarization on trigger even if body is below default threshold
|
|
277
|
+
// (e.g. many short error lines exceed errorCount but not 16KB).
|
|
278
|
+
thresholdBytes: 1,
|
|
279
|
+
});
|
|
280
|
+
body = s.text;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const footer = `\n\n[ashlr__logs \u00b7 ${existing.length} file${existing.length === 1 ? "" : "s"}]`;
|
|
284
|
+
const text = body + footer;
|
|
285
|
+
|
|
286
|
+
const logsRawBytes = Math.max(totalRawBytes, body.length);
|
|
287
|
+
await recordSaving(logsRawBytes, text.length, "ashlr__logs");
|
|
288
|
+
|
|
289
|
+
const logsBadgeOpts = {
|
|
290
|
+
toolName: "ashlr__logs",
|
|
291
|
+
rawBytes: logsRawBytes,
|
|
292
|
+
outputBytes: text.length,
|
|
293
|
+
};
|
|
294
|
+
if (confidenceTier(logsBadgeOpts) === "low") {
|
|
295
|
+
await logEvent("tool_low_confidence_shipped", { tool: "ashlr__logs", reason: "low-confidence" });
|
|
296
|
+
}
|
|
297
|
+
return text + confidenceBadge(logsBadgeOpts);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
// MCP wiring
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
|
|
304
|
+
const server = new Server(
|
|
305
|
+
{ name: "ashlr-logs", version: "0.1.0" },
|
|
306
|
+
{ capabilities: { tools: {} } },
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
310
|
+
tools: [
|
|
311
|
+
{
|
|
312
|
+
name: "ashlr__logs",
|
|
313
|
+
description:
|
|
314
|
+
"Tail a log file efficiently. Reads the last N lines (default 200), detects severity from bracketed tags, bare prefixes, JSON/logfmt level fields, and Python tracebacks, filters by level and/or ISO timestamp, and collapses runs of identical consecutive lines into '(42x) ...'. Supports glob paths. Lines with no parseable timestamp are skipped (never fatal) when 'since' is set.",
|
|
315
|
+
inputSchema: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {
|
|
318
|
+
path: { type: "string", description: "Log file path (supports glob)" },
|
|
319
|
+
lines: { type: "number", description: "Tail window (default 200)" },
|
|
320
|
+
level: {
|
|
321
|
+
type: "string",
|
|
322
|
+
description: "Filter: 'all' (default) | 'error' | 'warn' | 'error+warn'",
|
|
323
|
+
},
|
|
324
|
+
since: { type: "string", description: "ISO timestamp \u2014 only lines after this" },
|
|
325
|
+
dedupe: {
|
|
326
|
+
type: "boolean",
|
|
327
|
+
description: "Collapse repeated consecutive lines with count (default: true)",
|
|
328
|
+
},
|
|
329
|
+
bypassSummary: {
|
|
330
|
+
type: "boolean",
|
|
331
|
+
description: "If true, skip LLM summarization and return the full rendered log tail.",
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
required: ["path"],
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
}));
|
|
339
|
+
|
|
340
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
341
|
+
const { name, arguments: args } = req.params;
|
|
342
|
+
try {
|
|
343
|
+
if (name === "ashlr__logs") {
|
|
344
|
+
const text = await ashlrLogs((args ?? {}) as unknown as LogsArgs);
|
|
345
|
+
return { content: [{ type: "text", text }] };
|
|
346
|
+
}
|
|
347
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
348
|
+
} catch (err) {
|
|
349
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
350
|
+
return { content: [{ type: "text", text: `ashlr__logs error: ${message}` }], isError: true };
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
if (import.meta.main) {
|
|
355
|
+
const transport = new StdioServerTransport();
|
|
356
|
+
await server.connect(transport);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
void readFileSync;
|