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,394 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* scripts/weekly-digest.ts — Weekly digest renderer.
|
|
4
|
+
*
|
|
5
|
+
* Wraps servers/_weekly-digest.ts to produce a markdown report to stdout.
|
|
6
|
+
*
|
|
7
|
+
* Sections (Pro users):
|
|
8
|
+
* - Top 5 tools used this week (count + share %)
|
|
9
|
+
* - Genome learnings (new section count; top 3 by token impact if available)
|
|
10
|
+
* - Streak status (current streak, vs last week)
|
|
11
|
+
* - Total tokens + $ saved this week
|
|
12
|
+
*
|
|
13
|
+
* Free-tier users see a 3-line teaser instead of the full digest.
|
|
14
|
+
*
|
|
15
|
+
* Integration with /ashlr-resume: buildResumeWithDigest() prepends the
|
|
16
|
+
* digest when 7+ days have elapsed since last_digest_shown_ts.
|
|
17
|
+
*
|
|
18
|
+
* Email delivery: deferred to v1.32. This file ships markdown-to-stdout only.
|
|
19
|
+
* (Decision: ship markdown render first; email route server/src/emails/weekly-digest.tsx
|
|
20
|
+
* can ride v1.32 once we verify engagement with the text version.)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
24
|
+
import { homedir } from "os";
|
|
25
|
+
import { join } from "path";
|
|
26
|
+
import {
|
|
27
|
+
buildDigestBanner,
|
|
28
|
+
isoWeekKey,
|
|
29
|
+
priorWeekKey,
|
|
30
|
+
weekDayKeys,
|
|
31
|
+
type DigestStats,
|
|
32
|
+
} from "../servers/_weekly-digest.ts";
|
|
33
|
+
import { readStreaks } from "../servers/_streaks.ts";
|
|
34
|
+
import { costFor } from "../servers/_pricing.ts";
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Types
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export interface WeeklyDigestOpts {
|
|
41
|
+
/** Override home dir — used by tests. */
|
|
42
|
+
home?: string;
|
|
43
|
+
/** Injected now — used by tests. */
|
|
44
|
+
now?: Date;
|
|
45
|
+
/** Override Pro status detection — used by tests. */
|
|
46
|
+
isPro?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* When true, the digest runs even if shown less than 7 days ago.
|
|
49
|
+
* Used by force-render calls and tests.
|
|
50
|
+
*/
|
|
51
|
+
force?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* When true, does not update last_digest_shown_ts in state.
|
|
54
|
+
* Used by tests to stay isolated.
|
|
55
|
+
*/
|
|
56
|
+
suppressStateWrite?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface WeeklyDigestResult {
|
|
60
|
+
/** Rendered markdown string, or null if digest should not fire. */
|
|
61
|
+
markdown: string | null;
|
|
62
|
+
/** True if the digest fired (new content). */
|
|
63
|
+
fired: boolean;
|
|
64
|
+
/** True if user is on Pro. */
|
|
65
|
+
isPro: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Paths
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
export function digestResumePath(homeDir: string = homedir()): string {
|
|
73
|
+
return join(homeDir, ".ashlr", "digest-resume-state.json");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Pro detection
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* True when ~/.ashlr/pro-token exists and is non-empty.
|
|
82
|
+
* Same check used by the status-line.
|
|
83
|
+
*/
|
|
84
|
+
export function isProUser(homeDir: string = homedir()): boolean {
|
|
85
|
+
try {
|
|
86
|
+
const path = join(homeDir, ".ashlr", "pro-token");
|
|
87
|
+
const st = statSync(path);
|
|
88
|
+
return st.isFile() && st.size > 0;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Resume state — tracks when digest was last shown in /ashlr-resume
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
interface DigestResumeState {
|
|
99
|
+
last_digest_shown_ts: string | null; // ISO timestamp
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function readDigestResumeState(homeDir: string = homedir()): DigestResumeState {
|
|
103
|
+
try {
|
|
104
|
+
const p = digestResumePath(homeDir);
|
|
105
|
+
if (!existsSync(p)) return { last_digest_shown_ts: null };
|
|
106
|
+
const raw = JSON.parse(readFileSync(p, "utf-8")) as Partial<DigestResumeState>;
|
|
107
|
+
return {
|
|
108
|
+
last_digest_shown_ts:
|
|
109
|
+
typeof raw.last_digest_shown_ts === "string" ? raw.last_digest_shown_ts : null,
|
|
110
|
+
};
|
|
111
|
+
} catch {
|
|
112
|
+
return { last_digest_shown_ts: null };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function writeDigestResumeState(
|
|
117
|
+
state: DigestResumeState,
|
|
118
|
+
homeDir: string = homedir(),
|
|
119
|
+
): void {
|
|
120
|
+
try {
|
|
121
|
+
mkdirSync(join(homeDir, ".ashlr"), { recursive: true });
|
|
122
|
+
writeFileSync(digestResumePath(homeDir), JSON.stringify(state, null, 2));
|
|
123
|
+
} catch {
|
|
124
|
+
/* best-effort */
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** True if 7+ days have elapsed since the last digest was shown in resume. */
|
|
129
|
+
export function shouldShowDigestInResume(
|
|
130
|
+
homeDir: string = homedir(),
|
|
131
|
+
nowMs: number = Date.now(),
|
|
132
|
+
): boolean {
|
|
133
|
+
const state = readDigestResumeState(homeDir);
|
|
134
|
+
if (!state.last_digest_shown_ts) return true;
|
|
135
|
+
const lastMs = Date.parse(state.last_digest_shown_ts);
|
|
136
|
+
if (!Number.isFinite(lastMs)) return true;
|
|
137
|
+
return nowMs - lastMs >= 7 * 24 * 60 * 60_000;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Stats reader
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
interface StatsFile {
|
|
145
|
+
lifetime?: {
|
|
146
|
+
tokensSaved?: number;
|
|
147
|
+
calls?: number;
|
|
148
|
+
byTool?: Record<string, { tokensSaved?: number; calls?: number }>;
|
|
149
|
+
byDay?: Record<string, { tokensSaved?: number; calls?: number }>;
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function readStats(homeDir: string): StatsFile | null {
|
|
154
|
+
try {
|
|
155
|
+
const p = join(homeDir, ".ashlr", "stats.json");
|
|
156
|
+
if (!existsSync(p)) return null;
|
|
157
|
+
return JSON.parse(readFileSync(p, "utf-8")) as StatsFile;
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Formatting helpers
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
function fmtTokens(n: number): string {
|
|
168
|
+
if (!Number.isFinite(n) || n < 0) return "0";
|
|
169
|
+
if (n < 1000) return String(Math.floor(n));
|
|
170
|
+
if (n < 1_000_000) return (n / 1000).toFixed(1) + "K";
|
|
171
|
+
return (n / 1_000_000).toFixed(1) + "M";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function fmtDollars(n: number): string {
|
|
175
|
+
if (!Number.isFinite(n) || n < 0.01) return "$0.00";
|
|
176
|
+
if (n < 100) return `$${n.toFixed(2)}`;
|
|
177
|
+
return `$${Math.round(n)}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Digest section renderers
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Render top 5 tools by lifetime tokens saved. Returns "" when no data.
|
|
186
|
+
*
|
|
187
|
+
* Note: byTool is a lifetime accumulator — share % is computed over the sum
|
|
188
|
+
* of reported entries (not weekly tokens), so percentages reflect each tool's
|
|
189
|
+
* contribution to all-time savings, not weekly savings.
|
|
190
|
+
*/
|
|
191
|
+
export function renderTopToolsSection(
|
|
192
|
+
byTool: Record<string, { tokensSaved?: number; calls?: number }> | undefined,
|
|
193
|
+
): string {
|
|
194
|
+
if (!byTool) return "";
|
|
195
|
+
const entries = Object.entries(byTool)
|
|
196
|
+
.map(([name, v]) => ({ name, tokens: v?.tokensSaved ?? 0, calls: v?.calls ?? 0 }))
|
|
197
|
+
.filter((e) => e.tokens > 0)
|
|
198
|
+
.sort((a, b) => b.tokens - a.tokens);
|
|
199
|
+
|
|
200
|
+
if (entries.length === 0) return "";
|
|
201
|
+
|
|
202
|
+
const totalTokens = entries.reduce((sum, e) => sum + e.tokens, 0);
|
|
203
|
+
const top = entries.slice(0, 5);
|
|
204
|
+
|
|
205
|
+
const lines: string[] = ["### Top tools (all time)"];
|
|
206
|
+
for (const e of top) {
|
|
207
|
+
const pct = totalTokens > 0 ? Math.round((e.tokens / totalTokens) * 100) : 0;
|
|
208
|
+
lines.push(`- **${e.name}**: ${e.calls} calls · ${fmtTokens(e.tokens)} tokens saved (${pct}%)`);
|
|
209
|
+
}
|
|
210
|
+
return lines.join("\n");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Render genome learnings section. Returns "" when no genome is present. */
|
|
214
|
+
export function renderGenomeLearningsSection(homeDir: string): string {
|
|
215
|
+
try {
|
|
216
|
+
const genomePath = join(homeDir, ".ashlrcode", "genome");
|
|
217
|
+
if (!existsSync(genomePath)) return "";
|
|
218
|
+
// Count .md files in genome knowledge dir as a proxy for section count.
|
|
219
|
+
const knowledgePath = join(genomePath, "knowledge");
|
|
220
|
+
if (!existsSync(knowledgePath)) return "";
|
|
221
|
+
const { readdirSync } = require("fs") as typeof import("fs");
|
|
222
|
+
const files = readdirSync(knowledgePath).filter((f: string) => f.endsWith(".md"));
|
|
223
|
+
if (files.length === 0) return "";
|
|
224
|
+
return [
|
|
225
|
+
"### Genome learnings",
|
|
226
|
+
`- ${files.length} knowledge section(s) in your project genome`,
|
|
227
|
+
"- Run `/ashlr-genome-init` on a new project to start capturing learnings",
|
|
228
|
+
].join("\n");
|
|
229
|
+
} catch {
|
|
230
|
+
return "";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Render streak section. Returns "" when streak < 1. */
|
|
235
|
+
export function renderStreakSection(homeDir: string): string {
|
|
236
|
+
try {
|
|
237
|
+
const streak = readStreaks(homeDir);
|
|
238
|
+
if (streak.currentStreak < 1) return "";
|
|
239
|
+
const lines: string[] = ["### Streak"];
|
|
240
|
+
lines.push(`- Current streak: **${streak.currentStreak}d**`);
|
|
241
|
+
if (streak.longestStreak > streak.currentStreak) {
|
|
242
|
+
lines.push(`- Longest streak: ${streak.longestStreak}d`);
|
|
243
|
+
}
|
|
244
|
+
return lines.join("\n");
|
|
245
|
+
} catch {
|
|
246
|
+
return "";
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// Free-tier teaser
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
export function renderProTeaser(): string {
|
|
255
|
+
return [
|
|
256
|
+
"---",
|
|
257
|
+
"**Weekly digest is a Pro feature.**",
|
|
258
|
+
"Pro unlocks: weekly digest with team genome learnings, shared knowledge base, and cross-machine stats.",
|
|
259
|
+
"Run `/ashlr-tier` to see what Pro adds for $12/mo.",
|
|
260
|
+
].join("\n");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// Full digest renderer
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Compute token savings for the prior ISO week from byDay data.
|
|
269
|
+
*/
|
|
270
|
+
export function computeWeekTokens(
|
|
271
|
+
byDay: Record<string, { tokensSaved?: number }> | undefined,
|
|
272
|
+
priorWeek: string,
|
|
273
|
+
): number {
|
|
274
|
+
if (!byDay) return 0;
|
|
275
|
+
const keys = weekDayKeys(priorWeek);
|
|
276
|
+
return keys.reduce((sum, k) => sum + (byDay[k]?.tokensSaved ?? 0), 0);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Render the full weekly digest markdown. Does NOT check the 7-day gate or
|
|
281
|
+
* update state — that's the caller's responsibility.
|
|
282
|
+
*/
|
|
283
|
+
export function renderWeeklyDigest(opts: WeeklyDigestOpts = {}): string {
|
|
284
|
+
const homeDir = opts.home ?? homedir();
|
|
285
|
+
const now = opts.now ?? new Date();
|
|
286
|
+
|
|
287
|
+
const stats = readStats(homeDir);
|
|
288
|
+
const prior = priorWeekKey(now);
|
|
289
|
+
const weekTokens = computeWeekTokens(stats?.lifetime?.byDay, prior);
|
|
290
|
+
const weekDollars = costFor(weekTokens);
|
|
291
|
+
|
|
292
|
+
const sections: string[] = [];
|
|
293
|
+
|
|
294
|
+
// Header
|
|
295
|
+
sections.push(`## ashlr Weekly Digest — week of ${prior}`);
|
|
296
|
+
sections.push("");
|
|
297
|
+
|
|
298
|
+
// Savings summary
|
|
299
|
+
sections.push("### Savings this week");
|
|
300
|
+
if (weekTokens > 0) {
|
|
301
|
+
sections.push(`- **${fmtTokens(weekTokens)}** tokens saved`);
|
|
302
|
+
sections.push(`- **${fmtDollars(weekDollars)}** estimated value`);
|
|
303
|
+
} else {
|
|
304
|
+
sections.push("- No savings recorded last week");
|
|
305
|
+
}
|
|
306
|
+
sections.push("");
|
|
307
|
+
|
|
308
|
+
// Top tools
|
|
309
|
+
const topTools = renderTopToolsSection(stats?.lifetime?.byTool);
|
|
310
|
+
if (topTools) {
|
|
311
|
+
sections.push(topTools);
|
|
312
|
+
sections.push("");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Genome learnings
|
|
316
|
+
const genome = renderGenomeLearningsSection(homeDir);
|
|
317
|
+
if (genome) {
|
|
318
|
+
sections.push(genome);
|
|
319
|
+
sections.push("");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Streak
|
|
323
|
+
const streakSection = renderStreakSection(homeDir);
|
|
324
|
+
if (streakSection) {
|
|
325
|
+
sections.push(streakSection);
|
|
326
|
+
sections.push("");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return sections.join("\n").trimEnd();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
// Public API
|
|
334
|
+
// ---------------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Build the weekly digest. Respects the 7-day gate unless force=true.
|
|
338
|
+
* Marks the digest as shown (updates state) unless suppressStateWrite=true.
|
|
339
|
+
*/
|
|
340
|
+
export function buildWeeklyDigest(opts: WeeklyDigestOpts = {}): WeeklyDigestResult {
|
|
341
|
+
const homeDir = opts.home ?? homedir();
|
|
342
|
+
const nowMs = opts.now ? opts.now.getTime() : Date.now();
|
|
343
|
+
|
|
344
|
+
const proUser = opts.isPro ?? isProUser(homeDir);
|
|
345
|
+
|
|
346
|
+
// 7-day gate (unless force=true).
|
|
347
|
+
if (!opts.force && !shouldShowDigestInResume(homeDir, nowMs)) {
|
|
348
|
+
return { markdown: null, fired: false, isPro: proUser };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
let markdown: string;
|
|
352
|
+
if (proUser) {
|
|
353
|
+
markdown = renderWeeklyDigest(opts);
|
|
354
|
+
} else {
|
|
355
|
+
markdown = renderProTeaser();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Update state.
|
|
359
|
+
if (!opts.suppressStateWrite) {
|
|
360
|
+
writeDigestResumeState(
|
|
361
|
+
{ last_digest_shown_ts: new Date(nowMs).toISOString() },
|
|
362
|
+
homeDir,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return { markdown, fired: true, isPro: proUser };
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Integration point for /ashlr-resume: prepend the digest when 7+ days
|
|
371
|
+
* have elapsed. Returns the digest markdown (or null if gated/not ready).
|
|
372
|
+
*/
|
|
373
|
+
export function getDigestForResume(
|
|
374
|
+
homeDir: string = homedir(),
|
|
375
|
+
nowMs: number = Date.now(),
|
|
376
|
+
): string | null {
|
|
377
|
+
if (!shouldShowDigestInResume(homeDir, nowMs)) return null;
|
|
378
|
+
const result = buildWeeklyDigest({ home: homeDir, now: new Date(nowMs) });
|
|
379
|
+
return result.markdown;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ---------------------------------------------------------------------------
|
|
383
|
+
// CLI
|
|
384
|
+
// ---------------------------------------------------------------------------
|
|
385
|
+
|
|
386
|
+
if (import.meta.main) {
|
|
387
|
+
const digest = buildWeeklyDigest({ force: process.argv.includes("--force") });
|
|
388
|
+
if (digest.markdown) {
|
|
389
|
+
process.stdout.write(digest.markdown + "\n");
|
|
390
|
+
} else {
|
|
391
|
+
process.stdout.write("No digest available yet (shown less than 7 days ago). Use --force to override.\n");
|
|
392
|
+
}
|
|
393
|
+
process.exit(0);
|
|
394
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accurate savings accounting — v1.18 "Trust Pass" graduates this from the
|
|
3
|
+
* Day-1 pass-through into the correct cache-hit math.
|
|
4
|
+
*
|
|
5
|
+
* Background: `recordSaving(raw, compact, tool)` records `raw - compact` as
|
|
6
|
+
* the delta. For a first-time call that's correct: Claude *would have*
|
|
7
|
+
* received `raw` bytes of tool output, but we compressed it to `compact`.
|
|
8
|
+
*
|
|
9
|
+
* Cache-hit case is different: on a hit we served the cached compact
|
|
10
|
+
* result, so Claude received exactly `compact` bytes again. But the value
|
|
11
|
+
* proposition of the hit is that Claude *didn't have to pay* for the
|
|
12
|
+
* counterfactual re-fetch — so the savings are the full `rawBytes` of the
|
|
13
|
+
* source payload, not `raw - compact`.
|
|
14
|
+
*
|
|
15
|
+
* Concretely, the existing math on the first call already saved
|
|
16
|
+
* `raw - compact`. On each subsequent hit, reporting only `raw - compact`
|
|
17
|
+
* *again* under-counts — we should report `rawBytes` worth of work that
|
|
18
|
+
* the cache shortcut entirely. We do this by recording the hit as
|
|
19
|
+
* `(rawBytes, 0)` so `recordSaving` attributes the full `rawBytes` of
|
|
20
|
+
* counterfactual cost to savings.
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* import { recordSavingAccurate } from "./_accounting";
|
|
24
|
+
* await recordSavingAccurate({ rawBytes, compactBytes, toolName, cacheHit });
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { recordSaving } from "./_stats";
|
|
28
|
+
import { logEvent } from "./_events";
|
|
29
|
+
|
|
30
|
+
export interface RecordSavingAccurateOpts {
|
|
31
|
+
/** Raw bytes before compression / summarization. */
|
|
32
|
+
rawBytes: number;
|
|
33
|
+
/** Compact bytes after compression / summarization. */
|
|
34
|
+
compactBytes: number;
|
|
35
|
+
/** Tool name for stats bucketing (e.g. "ashlr__webfetch"). */
|
|
36
|
+
toolName: string;
|
|
37
|
+
/**
|
|
38
|
+
* True if the result was served from the SHA-256 summary cache.
|
|
39
|
+
* On a hit the accounting records `(rawBytes, 0)` — the full source
|
|
40
|
+
* payload is saved because the cache shortcut skipped the re-fetch.
|
|
41
|
+
*/
|
|
42
|
+
cacheHit: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Record a savings event with cache-hit awareness.
|
|
47
|
+
*
|
|
48
|
+
* Miss: forwards `(rawBytes, compactBytes)` unchanged — normal delta math.
|
|
49
|
+
* Hit: forwards `(rawBytes, 0)` — the full source payload's token cost was
|
|
50
|
+
* avoided because the cache served a compact copy at near-zero cost.
|
|
51
|
+
*
|
|
52
|
+
* Also emits an `accounting_cache_hit` event so we can measure hit rates
|
|
53
|
+
* over time and validate the correction.
|
|
54
|
+
*/
|
|
55
|
+
export async function recordSavingAccurate(opts: RecordSavingAccurateOpts): Promise<void> {
|
|
56
|
+
const { rawBytes, compactBytes, toolName, cacheHit } = opts;
|
|
57
|
+
|
|
58
|
+
if (cacheHit) {
|
|
59
|
+
// Emit telemetry first (never blocks the stats write on the event log).
|
|
60
|
+
await logEvent("accounting_cache_hit", {
|
|
61
|
+
tool: toolName,
|
|
62
|
+
extra: {
|
|
63
|
+
rawBytes,
|
|
64
|
+
compactBytes,
|
|
65
|
+
// Savings attributed on this call — for a hit, the full source bytes.
|
|
66
|
+
savedTokens: Math.max(0, Math.ceil(rawBytes / 4)),
|
|
67
|
+
},
|
|
68
|
+
}).catch(() => undefined);
|
|
69
|
+
|
|
70
|
+
// Cache hit: full rawBytes of counterfactual cost avoided. Pass 0 as
|
|
71
|
+
// the compact size so recordSaving credits the full source.
|
|
72
|
+
await recordSaving(rawBytes, 0, toolName);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Miss: standard delta.
|
|
77
|
+
await recordSaving(rawBytes, compactBytes, toolName);
|
|
78
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _ask-router — pure routing decision logic for ashlr__ask.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of tool-server imports so ask-server can eventually be registered
|
|
5
|
+
* on the shared router (see `_router.ts`) without creating a module-load
|
|
6
|
+
* circular dependency. `ask-server.ts` dispatch today still calls each
|
|
7
|
+
* underlying tool directly; Phase 2 of the router migration swaps that to a
|
|
8
|
+
* registry lookup once efficiency + orient + tree + glob hold registered
|
|
9
|
+
* handlers.
|
|
10
|
+
*
|
|
11
|
+
* Only dependency: `_text-helpers.ts` for `extractKeywords`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { extractKeywords } from "./_text-helpers";
|
|
15
|
+
|
|
16
|
+
export type RoutedTool =
|
|
17
|
+
| "ashlr__read"
|
|
18
|
+
| "ashlr__grep"
|
|
19
|
+
| "ashlr__orient"
|
|
20
|
+
| "ashlr__tree"
|
|
21
|
+
| "ashlr__glob";
|
|
22
|
+
|
|
23
|
+
export interface RouteDecision {
|
|
24
|
+
tool: RoutedTool;
|
|
25
|
+
reason: string;
|
|
26
|
+
/** Extracted value used for the underlying call (path, keyword, pattern). */
|
|
27
|
+
extracted?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Glob-shaped token: contains * or ? and looks like a file pattern. */
|
|
31
|
+
const GLOB_RE = /(?:^|\s)((?:\*\*\/|[\w.\-]+\/)*[\w.*?\-]+\*[\w.*?\-/]*|[\w.*?\-/]*\*[\w.*?\-/]*\.[\w]+)(?:\s|$)/;
|
|
32
|
+
|
|
33
|
+
/** Path-like token: starts with / or ./ or contains a file extension, or is a dotfile like .env */
|
|
34
|
+
const PATH_RE = /(?:^|\s)((?:\/|\.\/|~\/|[\w\-]+\/)+[\w.\-]+|\.[\w]+|[\w\-]+\.(?:ts|js|tsx|jsx|py|go|rs|rb|java|md|json|yaml|yml|sh|toml|lock|txt|env|sql|graphql|proto|css|html|xml))(?:\s|$)/;
|
|
35
|
+
|
|
36
|
+
const READ_VERBS_RE = /\b(read|show\s+me|what'?s?\s+in|contents?\s+of|display|print|open)\b/i;
|
|
37
|
+
const GREP_VERBS_RE = /^(grep|find|search|where\s+is|where\s+are|which\s+file|look\s+for|locate)\b/i;
|
|
38
|
+
const STRUCTURAL_RE = /\b(how\s+does|how\s+do(?:es)?\s+we|explain|walk\s+me\s+through|why\s+does|how\s+is|what\s+is\s+the\s+(?:flow|pattern|architecture)|how\s+(?:does|do|is|are)\s+(?:the\s+)?(?:\w+\s+){0,3}work)\b/i;
|
|
39
|
+
const TREE_VERBS_RE = /\b(list|show|tree|structure|directory|layout|overview|scaffold|outline)\b/i;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Decide which ashlr tool should handle a natural-language question.
|
|
43
|
+
*
|
|
44
|
+
* Pure function. No I/O, no env, no tool imports. First match wins; falls
|
|
45
|
+
* back to `ashlr__orient` for the empty/ambiguous case since orient handles
|
|
46
|
+
* multi-file synthesis best.
|
|
47
|
+
*/
|
|
48
|
+
export function routeQuestion(question: string): RouteDecision {
|
|
49
|
+
const q = question.trim();
|
|
50
|
+
|
|
51
|
+
// 1. Glob-shaped token — highest priority so "find **/*.ts" → glob, not grep.
|
|
52
|
+
const globMatch = GLOB_RE.exec(q);
|
|
53
|
+
if (globMatch) {
|
|
54
|
+
return { tool: "ashlr__glob", reason: "glob-pattern token", extracted: globMatch[1]!.trim() };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. Read verbs + path token.
|
|
58
|
+
if (READ_VERBS_RE.test(q)) {
|
|
59
|
+
const pathMatch = PATH_RE.exec(q);
|
|
60
|
+
if (pathMatch) {
|
|
61
|
+
return { tool: "ashlr__read", reason: "read verb + path token", extracted: pathMatch[1]!.trim() };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 3. Grep/search verbs (anchored at start for precision).
|
|
66
|
+
if (GREP_VERBS_RE.test(q)) {
|
|
67
|
+
const keywords = extractKeywords(q);
|
|
68
|
+
const kw = keywords[0] ?? q.split(/\s+/).slice(1, 3).join(" ");
|
|
69
|
+
return { tool: "ashlr__grep", reason: "search verb", extracted: kw };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Structural / explanatory questions.
|
|
73
|
+
if (STRUCTURAL_RE.test(q)) {
|
|
74
|
+
return { tool: "ashlr__orient", reason: "structural query" };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 5. List/tree/structure with no specific pattern.
|
|
78
|
+
if (TREE_VERBS_RE.test(q) && !PATH_RE.test(q)) {
|
|
79
|
+
return { tool: "ashlr__tree", reason: "structural listing request" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fallback: orient handles multi-file synthesis best.
|
|
83
|
+
return { tool: "ashlr__orient", reason: "fallback — no rule matched" };
|
|
84
|
+
}
|