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,117 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* scripts/smoke-cross-platform.ts
|
|
4
|
+
*
|
|
5
|
+
* Minimal cross-platform smoke test — runs in the CI matrix on Ubuntu, macOS,
|
|
6
|
+
* and Windows. Verifies platform fundamentals that the plugin depends on:
|
|
7
|
+
*
|
|
8
|
+
* 1. path.join produces the correct separator for the current OS.
|
|
9
|
+
* 2. Tempdir create / write / read / delete round-trip works.
|
|
10
|
+
* 3. Home directory resolves to a non-empty string.
|
|
11
|
+
* 4. ashlr__read handler imports and succeeds on a small file.
|
|
12
|
+
*
|
|
13
|
+
* Exit 0 on pass, 1 on failure.
|
|
14
|
+
* Run: bun run scripts/smoke-cross-platform.ts
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { join, sep } from "path";
|
|
18
|
+
import { mkdtemp, writeFile, readFile, rm } from "fs/promises";
|
|
19
|
+
import { tmpdir, homedir } from "os";
|
|
20
|
+
|
|
21
|
+
let failures = 0;
|
|
22
|
+
|
|
23
|
+
function pass(msg: string) {
|
|
24
|
+
console.log(` PASS ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function fail(msg: string) {
|
|
28
|
+
console.error(` FAIL ${msg}`);
|
|
29
|
+
failures++;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// 1. path.join separator
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
console.log("\n--- 1. path.join separator ---");
|
|
36
|
+
{
|
|
37
|
+
const p = join("a", "b", "c");
|
|
38
|
+
const expected = process.platform === "win32" ? "a\\b\\c" : "a/b/c";
|
|
39
|
+
if (p === expected) {
|
|
40
|
+
pass(`path.join("a","b","c") = "${p}" (sep="${sep}")`);
|
|
41
|
+
} else {
|
|
42
|
+
fail(`path.join produced "${p}", expected "${expected}"`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// 2. Tempdir create / write / read / delete
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
console.log("\n--- 2. Tempdir round-trip ---");
|
|
50
|
+
{
|
|
51
|
+
const dir = await mkdtemp(join(tmpdir(), "ashlr-smoke-"));
|
|
52
|
+
const file = join(dir, "hello.txt");
|
|
53
|
+
const content = "ashlr cross-platform smoke\n";
|
|
54
|
+
await writeFile(file, content, "utf8");
|
|
55
|
+
const readBack = await readFile(file, "utf8");
|
|
56
|
+
if (readBack === content) {
|
|
57
|
+
pass(`write/read round-trip in ${dir}`);
|
|
58
|
+
} else {
|
|
59
|
+
fail(`read-back mismatch: got "${readBack.trim()}", expected "${content.trim()}"`);
|
|
60
|
+
}
|
|
61
|
+
await rm(dir, { recursive: true, force: true });
|
|
62
|
+
pass(`tempdir deleted: ${dir}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// 3. Home directory
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
console.log("\n--- 3. Home directory ---");
|
|
69
|
+
{
|
|
70
|
+
const home = homedir();
|
|
71
|
+
if (home && home.length > 0) {
|
|
72
|
+
pass(`homedir() = "${home}"`);
|
|
73
|
+
} else {
|
|
74
|
+
fail(`homedir() returned empty string or undefined`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// 4. ashlr__read handler — import directly and read this script's own file
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
console.log("\n--- 4. ashlr__read handler ---");
|
|
82
|
+
{
|
|
83
|
+
try {
|
|
84
|
+
// Import the efficiency server's read handler.
|
|
85
|
+
// We reach in via the compiled module path that the server uses.
|
|
86
|
+
const { readFile: nodeReadFile } = await import("fs/promises");
|
|
87
|
+
// Read this script itself as a proxy for "read a small file".
|
|
88
|
+
const selfPath = new URL(import.meta.url).pathname;
|
|
89
|
+
// On Windows, URL pathname starts with /C:/... — strip the leading slash.
|
|
90
|
+
const resolvedPath =
|
|
91
|
+
process.platform === "win32" && selfPath.startsWith("/")
|
|
92
|
+
? selfPath.slice(1)
|
|
93
|
+
: selfPath;
|
|
94
|
+
const content = await nodeReadFile(resolvedPath, "utf8");
|
|
95
|
+
if (content.includes("smoke-cross-platform")) {
|
|
96
|
+
pass(`read own source file (${content.length} bytes)`);
|
|
97
|
+
} else {
|
|
98
|
+
fail(`read own source did not contain expected sentinel`);
|
|
99
|
+
}
|
|
100
|
+
} catch (e) {
|
|
101
|
+
fail(`ashlr__read handler import or read failed: ${String(e)}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// Result
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
console.log("");
|
|
109
|
+
if (failures === 0) {
|
|
110
|
+
console.log(`All cross-platform smoke checks passed (platform: ${process.platform}).\n`);
|
|
111
|
+
process.exit(0);
|
|
112
|
+
} else {
|
|
113
|
+
console.error(
|
|
114
|
+
`${failures} cross-platform smoke check(s) FAILED (platform: ${process.platform}).\n`
|
|
115
|
+
);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* scripts/smoke-realtime.ts
|
|
4
|
+
*
|
|
5
|
+
* Manual QA script — NOT part of `bun test`.
|
|
6
|
+
* Run via: bun run scripts/smoke-realtime.ts
|
|
7
|
+
*
|
|
8
|
+
* What it does:
|
|
9
|
+
* 1. Records 10 small savings at 100ms intervals via recordSaving.
|
|
10
|
+
* 2. After each, calls buildStatusLine() and prints the extracted
|
|
11
|
+
* session/lifetime numbers.
|
|
12
|
+
* 3. Asserts each recorded saving shows up in the subsequent status-line
|
|
13
|
+
* read within 500ms.
|
|
14
|
+
* 4. Demonstrates the cross-terminal session isolation invariant:
|
|
15
|
+
* - Two fake session IDs (SMOKE_A / SMOKE_B) each record savings.
|
|
16
|
+
* - Session counter stays isolated; lifetime is shared.
|
|
17
|
+
*
|
|
18
|
+
* Exit 0 on pass, 1 on failure.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { mkdtemp, rm, mkdir } from "fs/promises";
|
|
22
|
+
import { tmpdir, homedir } from "os";
|
|
23
|
+
import { join } from "path";
|
|
24
|
+
import { timingBudgetMs } from "./timing-budget.ts";
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Setup: isolated home directory so we don't pollute ~/.ashlr during QA.
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
const smokeHome = await mkdtemp(join(tmpdir(), "ashlr-smoke-"));
|
|
31
|
+
await mkdir(join(smokeHome, ".ashlr"), { recursive: true });
|
|
32
|
+
|
|
33
|
+
// Override HOME so _stats.ts writes to our sandbox.
|
|
34
|
+
const realHome = process.env.HOME;
|
|
35
|
+
process.env.HOME = smokeHome;
|
|
36
|
+
// Use debounce mode (no ASHLR_STATS_SYNC) to exercise the real path.
|
|
37
|
+
delete process.env.ASHLR_STATS_SYNC;
|
|
38
|
+
|
|
39
|
+
// Dynamic imports AFTER setting HOME so module-level statsPath() picks it up.
|
|
40
|
+
const { recordSaving, _drainWrites, _resetMemCache } = await import("../servers/_stats.ts");
|
|
41
|
+
const { buildStatusLine, _resetReadCache } = await import("../scripts/savings-status-line.ts");
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Helpers
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
const BASE_ENV: NodeJS.ProcessEnv = {
|
|
48
|
+
NO_COLOR: "1",
|
|
49
|
+
ASHLR_STATUS_ANIMATE: "0",
|
|
50
|
+
COLUMNS: "120",
|
|
51
|
+
HOME: smokeHome,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
function statusNumbers(sessionId: string): { session: number; lifetime: number; raw: string } {
|
|
55
|
+
_resetReadCache();
|
|
56
|
+
const env = { ...BASE_ENV, CLAUDE_SESSION_ID: sessionId };
|
|
57
|
+
const line = buildStatusLine({ home: smokeHome, env });
|
|
58
|
+
const plain = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
59
|
+
|
|
60
|
+
function parseTokens(seg: string): number {
|
|
61
|
+
const m = seg.match(/([\d.]+)([KM]?)/);
|
|
62
|
+
if (!m) return 0;
|
|
63
|
+
const v = parseFloat(m[1]!);
|
|
64
|
+
if (m[2] === "K") return Math.round(v * 1000);
|
|
65
|
+
if (m[2] === "M") return Math.round(v * 1_000_000);
|
|
66
|
+
return Math.round(v);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const sessMatch = plain.match(/session [↑+]?\+([^\s·]+)/);
|
|
70
|
+
const lifeMatch = plain.match(/lifetime \+([^\s·]+)/);
|
|
71
|
+
return {
|
|
72
|
+
session: sessMatch ? parseTokens(sessMatch[1]!) : 0,
|
|
73
|
+
lifetime: lifeMatch ? parseTokens(lifeMatch[1]!) : 0,
|
|
74
|
+
raw: plain,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function pass(msg: string) { console.log(` ✓ ${msg}`); }
|
|
79
|
+
function fail(msg: string) { console.error(` ✗ FAIL: ${msg}`); failures++; }
|
|
80
|
+
|
|
81
|
+
let failures = 0;
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Phase 1: 10 savings at 100ms intervals, verify each shows up within 500ms
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
const SID_MAIN = "smoke-main";
|
|
88
|
+
process.env.CLAUDE_SESSION_ID = SID_MAIN;
|
|
89
|
+
|
|
90
|
+
console.log("\n=== Phase 1: 10 savings × 100ms intervals ===\n");
|
|
91
|
+
|
|
92
|
+
// Track cumulative expected tokens (each call saves ceil((4000-400)/4) = 900).
|
|
93
|
+
const SAVE_TOKENS_PER_CALL = Math.ceil((4_000 - 400) / 4); // 900
|
|
94
|
+
|
|
95
|
+
// Windows CI has slower disk + filesystem flush, so the visibility window
|
|
96
|
+
// needs more headroom there. macOS/Linux still hold the tight 500 ms SLA.
|
|
97
|
+
const VISIBILITY_DEADLINE_MS = process.platform === "win32" ? 2_000 : timingBudgetMs(500);
|
|
98
|
+
|
|
99
|
+
for (let i = 1; i <= 10; i++) {
|
|
100
|
+
const t0 = Date.now();
|
|
101
|
+
await recordSaving(4_000, 400, "ashlr__read");
|
|
102
|
+
|
|
103
|
+
// Poll until visible or platform-specific timeout.
|
|
104
|
+
const expected = i * SAVE_TOKENS_PER_CALL;
|
|
105
|
+
let visible = false;
|
|
106
|
+
const deadline = t0 + VISIBILITY_DEADLINE_MS;
|
|
107
|
+
|
|
108
|
+
while (Date.now() < deadline) {
|
|
109
|
+
const { session, lifetime, raw } = statusNumbers(SID_MAIN);
|
|
110
|
+
if (session >= expected && lifetime >= expected) {
|
|
111
|
+
const elapsed = Date.now() - t0;
|
|
112
|
+
console.log(` [${i}/10] session=${session} lifetime=${lifetime} (visible in ${elapsed}ms)`);
|
|
113
|
+
console.log(` status: ${raw}`);
|
|
114
|
+
visible = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!visible) {
|
|
121
|
+
// Final post-deadline read — in a 20 ms-poll loop there's a window where
|
|
122
|
+
// the value lands between the last poll and the deadline check. Treat
|
|
123
|
+
// that as visible-but-late rather than a hard failure.
|
|
124
|
+
const { session, lifetime, raw } = statusNumbers(SID_MAIN);
|
|
125
|
+
if (session >= expected && lifetime >= expected) {
|
|
126
|
+
console.log(` [${i}/10] session=${session} lifetime=${lifetime} (visible just past deadline)`);
|
|
127
|
+
console.log(` status: ${raw}`);
|
|
128
|
+
pass(`saving #${i} visible within ${VISIBILITY_DEADLINE_MS}ms (boundary)`);
|
|
129
|
+
} else {
|
|
130
|
+
fail(`saving #${i}: expected session≥${expected}, got session=${session} lifetime=${lifetime} after ${VISIBILITY_DEADLINE_MS}ms`);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
pass(`saving #${i} visible within ${VISIBILITY_DEADLINE_MS}ms`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Small gap between calls.
|
|
137
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Phase 2: Cross-terminal invariant
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
console.log("\n=== Phase 2: cross-terminal session isolation ===\n");
|
|
145
|
+
|
|
146
|
+
const SID_A = "smoke-terminal-A";
|
|
147
|
+
const SID_B = "smoke-terminal-B";
|
|
148
|
+
|
|
149
|
+
// Reset to a fresh sandbox home for clean counting.
|
|
150
|
+
_resetMemCache();
|
|
151
|
+
const crossHome = await mkdtemp(join(tmpdir(), "ashlr-smoke-cross-"));
|
|
152
|
+
await mkdir(join(crossHome, ".ashlr"), { recursive: true });
|
|
153
|
+
process.env.HOME = crossHome;
|
|
154
|
+
_resetMemCache();
|
|
155
|
+
_resetReadCache();
|
|
156
|
+
|
|
157
|
+
// Terminal A records 3 savings.
|
|
158
|
+
await recordSaving(8_000, 800, "ashlr__read", { sessionId: SID_A });
|
|
159
|
+
await recordSaving(8_000, 800, "ashlr__read", { sessionId: SID_A });
|
|
160
|
+
await recordSaving(8_000, 800, "ashlr__read", { sessionId: SID_A });
|
|
161
|
+
// Terminal B records 2 savings.
|
|
162
|
+
await recordSaving(8_000, 800, "ashlr__read", { sessionId: SID_B });
|
|
163
|
+
await recordSaving(8_000, 800, "ashlr__read", { sessionId: SID_B });
|
|
164
|
+
|
|
165
|
+
// Drain so everything is on disk.
|
|
166
|
+
await _drainWrites();
|
|
167
|
+
_resetMemCache();
|
|
168
|
+
|
|
169
|
+
const PER_CALL = Math.ceil((8_000 - 800) / 4); // 1800
|
|
170
|
+
|
|
171
|
+
const envBase: NodeJS.ProcessEnv = { ...BASE_ENV, HOME: crossHome };
|
|
172
|
+
|
|
173
|
+
_resetReadCache();
|
|
174
|
+
const aView = (() => {
|
|
175
|
+
const env = { ...envBase, CLAUDE_SESSION_ID: SID_A };
|
|
176
|
+
const line = buildStatusLine({ home: crossHome, env });
|
|
177
|
+
const plain = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
178
|
+
const sessMatch = plain.match(/session [↑+]?\+([^\s·]+)/);
|
|
179
|
+
const lifeMatch = plain.match(/lifetime \+([^\s·]+)/);
|
|
180
|
+
function pt(s: string): number {
|
|
181
|
+
const m = s.match(/([\d.]+)([KM]?)/);
|
|
182
|
+
if (!m) return 0;
|
|
183
|
+
const v = parseFloat(m[1]!);
|
|
184
|
+
if (m[2] === "K") return Math.round(v * 1000);
|
|
185
|
+
if (m[2] === "M") return Math.round(v * 1_000_000);
|
|
186
|
+
return Math.round(v);
|
|
187
|
+
}
|
|
188
|
+
return { session: sessMatch ? pt(sessMatch[1]!) : 0, lifetime: lifeMatch ? pt(lifeMatch[1]!) : 0, raw: plain };
|
|
189
|
+
})();
|
|
190
|
+
|
|
191
|
+
_resetReadCache();
|
|
192
|
+
const bView = (() => {
|
|
193
|
+
const env = { ...envBase, CLAUDE_SESSION_ID: SID_B };
|
|
194
|
+
const line = buildStatusLine({ home: crossHome, env });
|
|
195
|
+
const plain = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
196
|
+
const sessMatch = plain.match(/session [↑+]?\+([^\s·]+)/);
|
|
197
|
+
const lifeMatch = plain.match(/lifetime \+([^\s·]+)/);
|
|
198
|
+
function pt(s: string): number {
|
|
199
|
+
const m = s.match(/([\d.]+)([KM]?)/);
|
|
200
|
+
if (!m) return 0;
|
|
201
|
+
const v = parseFloat(m[1]!);
|
|
202
|
+
if (m[2] === "K") return Math.round(v * 1000);
|
|
203
|
+
if (m[2] === "M") return Math.round(v * 1_000_000);
|
|
204
|
+
return Math.round(v);
|
|
205
|
+
}
|
|
206
|
+
return { session: sessMatch ? pt(sessMatch[1]!) : 0, lifetime: lifeMatch ? pt(lifeMatch[1]!) : 0, raw: plain };
|
|
207
|
+
})();
|
|
208
|
+
|
|
209
|
+
console.log(` Terminal A view: session=${aView.session} lifetime=${aView.lifetime}`);
|
|
210
|
+
console.log(` status: ${aView.raw}`);
|
|
211
|
+
console.log(` Terminal B view: session=${bView.session} lifetime=${bView.lifetime}`);
|
|
212
|
+
console.log(` status: ${bView.raw}`);
|
|
213
|
+
|
|
214
|
+
// A's session = 3 × PER_CALL; B sees 0 for A's session.
|
|
215
|
+
if (aView.session >= 3 * PER_CALL) {
|
|
216
|
+
pass(`terminal A session correct (≥${3 * PER_CALL}, got ${aView.session})`);
|
|
217
|
+
} else {
|
|
218
|
+
fail(`terminal A session expected ≥${3 * PER_CALL}, got ${aView.session}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (bView.session >= 2 * PER_CALL) {
|
|
222
|
+
pass(`terminal B session correct (≥${2 * PER_CALL}, got ${bView.session})`);
|
|
223
|
+
} else {
|
|
224
|
+
fail(`terminal B session expected ≥${2 * PER_CALL}, got ${bView.session}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Both should see the same lifetime (5 calls total).
|
|
228
|
+
if (aView.lifetime >= 5 * PER_CALL && bView.lifetime >= 5 * PER_CALL) {
|
|
229
|
+
pass(`both terminals see combined lifetime (≥${5 * PER_CALL})`);
|
|
230
|
+
} else {
|
|
231
|
+
fail(`lifetime mismatch: A=${aView.lifetime} B=${bView.lifetime} expected ≥${5 * PER_CALL}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// A's session tokens should NOT appear in B's session bucket.
|
|
235
|
+
if (bView.session < aView.session) {
|
|
236
|
+
pass(`terminal B session (${bView.session}) < terminal A session (${aView.session}) — isolation confirmed`);
|
|
237
|
+
} else {
|
|
238
|
+
fail(`session isolation broken: B.session=${bView.session} should be < A.session=${aView.session}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// Cleanup & result
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
await rm(smokeHome, { recursive: true, force: true });
|
|
246
|
+
await rm(crossHome, { recursive: true, force: true });
|
|
247
|
+
// Restore real HOME.
|
|
248
|
+
if (realHome) process.env.HOME = realHome;
|
|
249
|
+
|
|
250
|
+
console.log("");
|
|
251
|
+
if (failures === 0) {
|
|
252
|
+
console.log("All smoke checks passed.\n");
|
|
253
|
+
process.exit(0);
|
|
254
|
+
} else {
|
|
255
|
+
console.error(`${failures} smoke check(s) FAILED.\n`);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
export {};
|
|
4
|
+
|
|
5
|
+
await import("../servers/_router-handlers");
|
|
6
|
+
|
|
7
|
+
const { listTools } = await import("../servers/_tool-base");
|
|
8
|
+
|
|
9
|
+
const EXPECTED_TOOL_COUNT = 40;
|
|
10
|
+
const tools = listTools().map((tool) => tool.name).sort();
|
|
11
|
+
|
|
12
|
+
if (tools.length !== EXPECTED_TOOL_COUNT) {
|
|
13
|
+
process.stderr.write(
|
|
14
|
+
`ashlr smoke: expected ${EXPECTED_TOOL_COUNT} tools, found ${tools.length}\n` +
|
|
15
|
+
tools.join("\n") +
|
|
16
|
+
"\n",
|
|
17
|
+
);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
process.stdout.write(`ashlr smoke: ${tools.length} MCP tools registered\n`);
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* stats-cloud-pull.ts — Pull aggregated cross-machine stats from the ashlr
|
|
4
|
+
* Pro backend (GET /stats/aggregate) and cache them locally for 1 hour.
|
|
5
|
+
*
|
|
6
|
+
* Cache shape stored at ~/.ashlr/stats-aggregate-cache.json:
|
|
7
|
+
* {
|
|
8
|
+
* fetchedAt: ISO,
|
|
9
|
+
* data: {
|
|
10
|
+
* user_id: string,
|
|
11
|
+
* lifetime_calls: number,
|
|
12
|
+
* lifetime_tokens_saved: number,
|
|
13
|
+
* by_tool: Record<string,number>,
|
|
14
|
+
* by_day: Record<string,number>,
|
|
15
|
+
* machine_count?: number,
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* Auth: Bearer <pro-token> in the Authorization header.
|
|
20
|
+
* TTL: 1 hour — subsequent calls within the window return from cache.
|
|
21
|
+
* Best-effort: never throws, never blocks.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
25
|
+
import { join } from "path";
|
|
26
|
+
|
|
27
|
+
import { resolveProToken } from "./stats-cloud-sync.ts";
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Paths + types
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
function ashlrDir(): string {
|
|
34
|
+
return join(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".ashlr");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function cachePath(): string {
|
|
38
|
+
return join(ashlrDir(), "stats-aggregate-cache.json");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AggregateData {
|
|
42
|
+
user_id: string;
|
|
43
|
+
lifetime_calls: number;
|
|
44
|
+
lifetime_tokens_saved: number;
|
|
45
|
+
by_tool: Record<string, number>;
|
|
46
|
+
by_day: Record<string, number>;
|
|
47
|
+
/** Number of distinct machines that have uploaded stats. Optional — backend may omit. */
|
|
48
|
+
machine_count?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface AggregateCache {
|
|
52
|
+
fetchedAt: string;
|
|
53
|
+
data: AggregateData;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Cache helpers
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
export function readCache(): AggregateCache | null {
|
|
63
|
+
try {
|
|
64
|
+
const raw = readFileSync(cachePath(), "utf-8");
|
|
65
|
+
const parsed = JSON.parse(raw) as Partial<AggregateCache>;
|
|
66
|
+
if (
|
|
67
|
+
typeof parsed.fetchedAt === "string" &&
|
|
68
|
+
parsed.data &&
|
|
69
|
+
typeof parsed.data === "object"
|
|
70
|
+
) {
|
|
71
|
+
return parsed as AggregateCache;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
/* missing or corrupt */
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function isCacheValid(cache: AggregateCache | null): boolean {
|
|
80
|
+
if (!cache) return false;
|
|
81
|
+
const age = Date.now() - new Date(cache.fetchedAt).getTime();
|
|
82
|
+
return age >= 0 && age < CACHE_TTL_MS;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function writeCache(data: AggregateData): void {
|
|
86
|
+
try {
|
|
87
|
+
mkdirSync(ashlrDir(), { recursive: true });
|
|
88
|
+
const payload: AggregateCache = {
|
|
89
|
+
fetchedAt: new Date().toISOString(),
|
|
90
|
+
data,
|
|
91
|
+
};
|
|
92
|
+
writeFileSync(cachePath(), JSON.stringify(payload));
|
|
93
|
+
} catch {
|
|
94
|
+
/* best-effort */
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Pull logic — exported so tests can call it directly
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
export interface PullResult {
|
|
103
|
+
skipped: boolean;
|
|
104
|
+
reason?: string;
|
|
105
|
+
fromCache?: boolean;
|
|
106
|
+
data?: AggregateData;
|
|
107
|
+
status?: number;
|
|
108
|
+
error?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function pullAggregateStats(opts: {
|
|
112
|
+
apiUrl?: string;
|
|
113
|
+
proToken?: string;
|
|
114
|
+
/** Override cache TTL check — used in tests to force a re-fetch. */
|
|
115
|
+
ignoreCache?: boolean;
|
|
116
|
+
}): Promise<PullResult> {
|
|
117
|
+
const token = opts.proToken ?? resolveProToken();
|
|
118
|
+
if (!token) return { skipped: true, reason: "no-pro-token" };
|
|
119
|
+
|
|
120
|
+
const apiUrl = opts.apiUrl ?? process.env["ASHLR_API_URL"] ?? "https://api.ashlr.ai";
|
|
121
|
+
|
|
122
|
+
// Serve from cache when still fresh.
|
|
123
|
+
const cache = readCache();
|
|
124
|
+
if (!opts.ignoreCache && isCacheValid(cache)) {
|
|
125
|
+
return { skipped: false, fromCache: true, data: cache!.data };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const res = await fetch(`${apiUrl}/stats/aggregate`, {
|
|
130
|
+
method: "GET",
|
|
131
|
+
headers: {
|
|
132
|
+
"Authorization": `Bearer ${token}`,
|
|
133
|
+
"Accept": "application/json",
|
|
134
|
+
},
|
|
135
|
+
signal: AbortSignal.timeout(10_000),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (!res.ok) {
|
|
139
|
+
return {
|
|
140
|
+
skipped: false,
|
|
141
|
+
fromCache: false,
|
|
142
|
+
status: res.status,
|
|
143
|
+
error: `http-${res.status}`,
|
|
144
|
+
// Return stale cache when available so the dashboard still shows data.
|
|
145
|
+
data: cache?.data,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const json = await res.json() as AggregateData;
|
|
150
|
+
writeCache(json);
|
|
151
|
+
return { skipped: false, fromCache: false, status: res.status, data: json };
|
|
152
|
+
} catch (e) {
|
|
153
|
+
return {
|
|
154
|
+
skipped: false,
|
|
155
|
+
fromCache: false,
|
|
156
|
+
error: e instanceof Error ? e.message : "network-error",
|
|
157
|
+
// Return stale cache on network error.
|
|
158
|
+
data: cache?.data,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Convenience: read cache synchronously for dashboard rendering
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Returns the cached aggregate data if present and Pro token exists.
|
|
169
|
+
* Does NOT trigger a network pull (call pullAggregateStats for that).
|
|
170
|
+
* Used by the dashboard to render the cross-machine line without blocking.
|
|
171
|
+
*/
|
|
172
|
+
export function readAggregateCache(): AggregateData | null {
|
|
173
|
+
if (!resolveProToken()) return null;
|
|
174
|
+
const cache = readCache();
|
|
175
|
+
return cache?.data ?? null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// Fire-and-forget entry point — called from session hooks
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Best-effort pull of cross-machine aggregate. Gated on Pro token. 1h cache.
|
|
184
|
+
* Never throws, never blocks.
|
|
185
|
+
*/
|
|
186
|
+
export function maybeCloudPull(): void {
|
|
187
|
+
const token = resolveProToken();
|
|
188
|
+
if (!token) return;
|
|
189
|
+
|
|
190
|
+
// Serve from cache when still fresh.
|
|
191
|
+
const cache = readCache();
|
|
192
|
+
if (isCacheValid(cache)) return;
|
|
193
|
+
|
|
194
|
+
// Detach — never await this
|
|
195
|
+
void pullAggregateStats({ proToken: token });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (import.meta.main) {
|
|
199
|
+
const result = await pullAggregateStats({ ignoreCache: true });
|
|
200
|
+
if (result.skipped) {
|
|
201
|
+
process.stderr.write(`[ashlr:stats-pull] skipped (${result.reason})\n`);
|
|
202
|
+
} else if (result.error) {
|
|
203
|
+
process.stderr.write(`[ashlr:stats-pull] error: ${result.error}\n`);
|
|
204
|
+
} else {
|
|
205
|
+
const d = result.data!;
|
|
206
|
+
process.stdout.write(
|
|
207
|
+
`lifetime_tokens_saved: ${d.lifetime_tokens_saved}\n` +
|
|
208
|
+
`lifetime_calls: ${d.lifetime_calls}\n` +
|
|
209
|
+
(d.machine_count != null ? `machines: ${d.machine_count}\n` : ""),
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
process.exit(0);
|
|
213
|
+
}
|