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,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* savings-report-extras.ts
|
|
3
|
+
*
|
|
4
|
+
* Three additional sections for the ashlr__savings renderer.
|
|
5
|
+
* Called from renderSavings() in servers/efficiency-server.ts.
|
|
6
|
+
* All functions are tolerant of missing files/data and never throw.
|
|
7
|
+
*
|
|
8
|
+
* Sections:
|
|
9
|
+
* 1. renderPerProjectSection — top-5 projects by call count (from session-log)
|
|
10
|
+
* 2. renderBestDaySection — single best day overall + its relative position
|
|
11
|
+
* 3. renderCalibrationLine — one-line calibration confidence note
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, readFileSync } from "fs";
|
|
15
|
+
import { homedir } from "os";
|
|
16
|
+
import { join, basename } from "path";
|
|
17
|
+
import type { LifetimeBucket } from "../servers/_stats.ts";
|
|
18
|
+
import {
|
|
19
|
+
CALIBRATION_PATH,
|
|
20
|
+
DEFAULT_MULTIPLIER,
|
|
21
|
+
getCalibrationMultiplier,
|
|
22
|
+
type CalibrationFile,
|
|
23
|
+
} from "./read-calibration.ts";
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Types
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export interface ProjectInfo {
|
|
30
|
+
name: string;
|
|
31
|
+
calls: number;
|
|
32
|
+
toolVariety: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ExtraContext {
|
|
36
|
+
/** Top 5 projects from session-log, pre-computed. */
|
|
37
|
+
topProjects?: ProjectInfo[];
|
|
38
|
+
/** Mean ratio from calibration — undefined means file is absent. */
|
|
39
|
+
calibrationRatio?: number;
|
|
40
|
+
/** True when calibration.json actually exists on disk. */
|
|
41
|
+
calibrationPresent?: boolean;
|
|
42
|
+
/** Pre-computed nudge telemetry summary (shown / clicked / dismissed). */
|
|
43
|
+
nudgeSummary?: NudgeSummary;
|
|
44
|
+
/** True when a local pro-token is present — suppresses the "try Pro" nudge stats
|
|
45
|
+
* in contexts where showing the nudge no longer applies. */
|
|
46
|
+
proUser?: boolean;
|
|
47
|
+
/** Opportunity hints context for renderTopOpportunitySection (Track GG). */
|
|
48
|
+
opportunity?: OpportunityContext;
|
|
49
|
+
/** Lifetime dollar cost saved (pre-computed). Used for Pro upsell threshold (Track JJ). */
|
|
50
|
+
lifetimeDollarsSaved?: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface OpportunityContext {
|
|
54
|
+
/** True when .ashlrcode/genome/manifest.json does NOT exist in cwd. */
|
|
55
|
+
noGenome: boolean;
|
|
56
|
+
/** How many grep calls occurred this week (from session-log). */
|
|
57
|
+
weeklyGrepCalls: number;
|
|
58
|
+
/** Active hook mode ("redirect" | "nudge" | "off"). */
|
|
59
|
+
hookMode: string;
|
|
60
|
+
/** Adoption funnel conversion pct (0–100). 0 when no data. */
|
|
61
|
+
conversionPct: number;
|
|
62
|
+
/** How many tool_fallback events recorded (snipCompact fallbacks). */
|
|
63
|
+
fallbackCount: number;
|
|
64
|
+
/** True when an LLM provider key is set (ANTHROPIC_API_KEY present). */
|
|
65
|
+
hasLlmProvider: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Pro upsell hint (Track JJ)
|
|
70
|
+
// Threshold: $20 lifetime saved. At sonnet-4.6 pricing ($2.50/MTok input)
|
|
71
|
+
// that is 8 million tokens — a legitimate signal of regular engagement.
|
|
72
|
+
// Pro at $12/mo is a clear value proposition once someone is saving this much.
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
/** Threshold at which the Pro upsell hint is shown (lifetime dollars saved). */
|
|
76
|
+
export const PRO_UPSELL_THRESHOLD_DOLLARS = 20;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Returns a one-line Pro upsell hint when:
|
|
80
|
+
* - lifetime cost saved >= PRO_UPSELL_THRESHOLD_DOLLARS, AND
|
|
81
|
+
* - the user is NOT already on Pro/Team (proUser=false).
|
|
82
|
+
*
|
|
83
|
+
* Returns empty string otherwise (caller must check before appending).
|
|
84
|
+
*/
|
|
85
|
+
export function renderProUpsellHint(
|
|
86
|
+
lifetimeDollarsSaved: number,
|
|
87
|
+
proUser: boolean,
|
|
88
|
+
lifetimeDollarsFmt?: string,
|
|
89
|
+
): string {
|
|
90
|
+
if (proUser) return "";
|
|
91
|
+
if (lifetimeDollarsSaved < PRO_UPSELL_THRESHOLD_DOLLARS) return "";
|
|
92
|
+
const dollarStr = lifetimeDollarsFmt ?? `$${lifetimeDollarsSaved.toFixed(0)}`;
|
|
93
|
+
return (
|
|
94
|
+
`You've saved ${dollarStr} lifetime on Free. Pro adds cross-machine sync + cloud genome. ` +
|
|
95
|
+
`Try /ashlr-upgrade.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface NudgeSummary {
|
|
100
|
+
shown: number;
|
|
101
|
+
clicked: number;
|
|
102
|
+
dismissed: number;
|
|
103
|
+
conversionPct: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// Session-log reader
|
|
108
|
+
// Reuses parse logic aligned with session-log-report.ts; kept local to avoid
|
|
109
|
+
// circular imports (session-log-report imports savings-status-line).
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
interface LogRecord {
|
|
113
|
+
ts: string;
|
|
114
|
+
event: string;
|
|
115
|
+
tool: string;
|
|
116
|
+
cwd: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function readLogLines(home: string): string[] {
|
|
120
|
+
const lines: string[] = [];
|
|
121
|
+
for (const fname of ["session-log.jsonl.1", "session-log.jsonl"]) {
|
|
122
|
+
const p = join(home, ".ashlr", fname);
|
|
123
|
+
if (!existsSync(p)) continue;
|
|
124
|
+
try {
|
|
125
|
+
lines.push(
|
|
126
|
+
...readFileSync(p, "utf-8")
|
|
127
|
+
.split("\n")
|
|
128
|
+
.filter((l) => l.trim().length > 0),
|
|
129
|
+
);
|
|
130
|
+
} catch {
|
|
131
|
+
// tolerate read errors
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return lines;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseLogRecords(lines: string[]): LogRecord[] {
|
|
138
|
+
const out: LogRecord[] = [];
|
|
139
|
+
for (const line of lines) {
|
|
140
|
+
try {
|
|
141
|
+
const r = JSON.parse(line) as Partial<LogRecord>;
|
|
142
|
+
if (typeof r.ts !== "string" || typeof r.event !== "string") continue;
|
|
143
|
+
if (r.event === "session_end") continue;
|
|
144
|
+
out.push({
|
|
145
|
+
ts: r.ts,
|
|
146
|
+
event: r.event,
|
|
147
|
+
tool: r.tool ?? "unknown",
|
|
148
|
+
cwd: r.cwd ?? "",
|
|
149
|
+
});
|
|
150
|
+
} catch {
|
|
151
|
+
// skip malformed lines
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Build top-5 project list from session-log.
|
|
159
|
+
* Exported so tests can call directly.
|
|
160
|
+
*/
|
|
161
|
+
export function buildTopProjects(home?: string): ProjectInfo[] {
|
|
162
|
+
const h = home ?? homedir();
|
|
163
|
+
const lines = readLogLines(h);
|
|
164
|
+
if (lines.length === 0) return [];
|
|
165
|
+
|
|
166
|
+
const records = parseLogRecords(lines);
|
|
167
|
+
const projectMap = new Map<string, { calls: number; tools: Set<string> }>();
|
|
168
|
+
|
|
169
|
+
for (const r of records) {
|
|
170
|
+
if (!r.cwd) continue;
|
|
171
|
+
let ps = projectMap.get(r.cwd);
|
|
172
|
+
if (!ps) {
|
|
173
|
+
ps = { calls: 0, tools: new Set() };
|
|
174
|
+
projectMap.set(r.cwd, ps);
|
|
175
|
+
}
|
|
176
|
+
ps.calls++;
|
|
177
|
+
ps.tools.add(r.tool);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return [...projectMap.entries()]
|
|
181
|
+
.sort((a, b) => b[1].calls - a[1].calls)
|
|
182
|
+
.slice(0, 5)
|
|
183
|
+
.map(([path, stat]) => ({
|
|
184
|
+
name: basename(path) || path,
|
|
185
|
+
calls: stat.calls,
|
|
186
|
+
toolVariety: stat.tools.size,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Gather calibration state: returns ratio (actual or default) + presence flag.
|
|
192
|
+
* Uses getCalibrationMultiplier from read-calibration.ts (reuse, don't dupe).
|
|
193
|
+
*/
|
|
194
|
+
export function readCalibrationState(calibPath?: string): {
|
|
195
|
+
ratio: number;
|
|
196
|
+
present: boolean;
|
|
197
|
+
} {
|
|
198
|
+
const p = calibPath ?? CALIBRATION_PATH;
|
|
199
|
+
const present = existsSync(p);
|
|
200
|
+
const ratio = getCalibrationMultiplier(p);
|
|
201
|
+
return { ratio, present };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Section renderers
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Section 1: per-project breakdown — top 5 by call count.
|
|
210
|
+
* Returns empty string when no data.
|
|
211
|
+
*/
|
|
212
|
+
export function renderPerProjectSection(projects: ProjectInfo[]): string {
|
|
213
|
+
if (projects.length === 0) return "";
|
|
214
|
+
const lines: string[] = [];
|
|
215
|
+
lines.push("top projects (by call count):");
|
|
216
|
+
for (const p of projects) {
|
|
217
|
+
const name = p.name.length > 32 ? "..." + p.name.slice(-29) : p.name;
|
|
218
|
+
const namePad = name.padEnd(35);
|
|
219
|
+
const callStr = `${p.calls} call${p.calls === 1 ? " " : "s"}`.padEnd(12);
|
|
220
|
+
const toolStr = `${p.toolVariety} tool${p.toolVariety === 1 ? "" : "s"}`;
|
|
221
|
+
lines.push(` ${namePad}${callStr}${toolStr}`);
|
|
222
|
+
}
|
|
223
|
+
return lines.join("\n");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Section 2: best day overall + its relative position.
|
|
228
|
+
* Scans lifetime.byDay for the single best day.
|
|
229
|
+
* Returns empty string when no data.
|
|
230
|
+
*/
|
|
231
|
+
export function renderBestDaySection(lifetime: LifetimeBucket): string {
|
|
232
|
+
const entries = Object.entries(lifetime.byDay).filter(
|
|
233
|
+
([, v]) => v.tokensSaved > 0,
|
|
234
|
+
);
|
|
235
|
+
if (entries.length === 0) return "";
|
|
236
|
+
|
|
237
|
+
entries.sort((a, b) => b[1].tokensSaved - a[1].tokensSaved);
|
|
238
|
+
const [bestDate, bestVal] = entries[0]!;
|
|
239
|
+
|
|
240
|
+
// Compute average from up to 6 other recent days for context.
|
|
241
|
+
const recentOthers = entries.slice(1, 7);
|
|
242
|
+
const avgTok =
|
|
243
|
+
recentOthers.length > 0
|
|
244
|
+
? Math.round(
|
|
245
|
+
recentOthers.reduce((s, [, v]) => s + v.tokensSaved, 0) /
|
|
246
|
+
recentOthers.length,
|
|
247
|
+
)
|
|
248
|
+
: 0;
|
|
249
|
+
|
|
250
|
+
const lines: string[] = [];
|
|
251
|
+
lines.push("best day:");
|
|
252
|
+
lines.push(
|
|
253
|
+
` ${bestDate} ${bestVal.tokensSaved.toLocaleString()} tok saved` +
|
|
254
|
+
` ${bestVal.calls} call${bestVal.calls === 1 ? "" : "s"}`,
|
|
255
|
+
);
|
|
256
|
+
if (avgTok > 0) {
|
|
257
|
+
const mult = (bestVal.tokensSaved / avgTok).toFixed(1);
|
|
258
|
+
lines.push(
|
|
259
|
+
` (${mult}x your recent avg of ${avgTok.toLocaleString()} tok/day)`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
return lines.join("\n");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Section 3: calibration confidence — one line.
|
|
267
|
+
* present=true → empirical measurement exists
|
|
268
|
+
* present=false → only the default 4x estimate
|
|
269
|
+
*
|
|
270
|
+
* When the calibration file exists, we read it directly to get quality
|
|
271
|
+
* breakdown (measured vs synthetic) and warn when >50% of samples are
|
|
272
|
+
* synthetic (meaning the genome didn't match any pattern).
|
|
273
|
+
*/
|
|
274
|
+
export function renderCalibrationLine(
|
|
275
|
+
ratio: number,
|
|
276
|
+
present: boolean,
|
|
277
|
+
calibPath: string = CALIBRATION_PATH,
|
|
278
|
+
): string {
|
|
279
|
+
if (!present) {
|
|
280
|
+
return `calibration: grep baseline is estimated (${DEFAULT_MULTIPLIER}x -- run calibrate-grep.ts)`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const headline = `calibration: grep baseline is empirical (mean ratio ${ratio.toFixed(1)}x)`;
|
|
284
|
+
|
|
285
|
+
// Optionally append a short quality note from the calibration file. Skip if
|
|
286
|
+
// appending would exceed 80 chars or anything goes wrong reading the file.
|
|
287
|
+
// The path is threaded from the caller so tests using a non-default path
|
|
288
|
+
// get a deterministic note tied to the file they wrote, not the user's home.
|
|
289
|
+
try {
|
|
290
|
+
if (existsSync(calibPath)) {
|
|
291
|
+
const raw = readFileSync(calibPath, "utf-8");
|
|
292
|
+
const file = JSON.parse(raw) as CalibrationFile;
|
|
293
|
+
const total = file.samples?.length ?? 0;
|
|
294
|
+
const measured = file.measuredCount ?? 0;
|
|
295
|
+
const coreFb = file.coreFallbackCount ?? 0;
|
|
296
|
+
if (total > 0 && measured < total) {
|
|
297
|
+
let note: string;
|
|
298
|
+
if (measured === 0 && coreFb === 0) {
|
|
299
|
+
note = ` (all ${total} synthetic)`;
|
|
300
|
+
} else if (measured === 0) {
|
|
301
|
+
note = ` (${coreFb}/${total} core-fallback)`;
|
|
302
|
+
} else {
|
|
303
|
+
note = ` (${measured}/${total} measured)`;
|
|
304
|
+
}
|
|
305
|
+
if (headline.length + note.length <= 80) return headline + note;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
// Fall through to the bare headline.
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return headline;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Section 4: nudge telemetry — "Shown X times, clicked Y (Z%)".
|
|
317
|
+
*
|
|
318
|
+
* Returns empty string when no events have been recorded. When the user is
|
|
319
|
+
* on Pro/Team we still print historical stats for curiosity — we just
|
|
320
|
+
* relabel the header so it reads as past-tense rather than a live prompt.
|
|
321
|
+
*/
|
|
322
|
+
export function renderNudgeSection(summary: NudgeSummary | undefined, proUser: boolean): string {
|
|
323
|
+
if (!summary || summary.shown === 0) return "";
|
|
324
|
+
const header = proUser ? "pro upgrade (historical nudge stats):" : "pro upgrade nudge:";
|
|
325
|
+
const lines: string[] = [];
|
|
326
|
+
lines.push(header);
|
|
327
|
+
const conv = summary.conversionPct.toFixed(1);
|
|
328
|
+
lines.push(
|
|
329
|
+
` shown ${summary.shown} · clicked ${summary.clicked} · rate ${conv}%`,
|
|
330
|
+
);
|
|
331
|
+
if (summary.dismissed > 0) {
|
|
332
|
+
lines.push(` dismissed (session ended, no click): ${summary.dismissed}`);
|
|
333
|
+
}
|
|
334
|
+
return lines.join("\n");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Section 5: top opportunity hints — surfaced when /ashlr-savings is run.
|
|
339
|
+
*
|
|
340
|
+
* Shows at most 2 items to avoid overwhelming the user. Priority:
|
|
341
|
+
* 1. Genome missing + heavy grep → "Run /ashlr-genome-init"
|
|
342
|
+
* 2. No LLM provider + many fallbacks → "Install ONNX or set ANTHROPIC_API_KEY"
|
|
343
|
+
* 3. Nudge mode + low conversion → "Switch to redirect mode"
|
|
344
|
+
*
|
|
345
|
+
* Returns empty string when there are no actionable hints.
|
|
346
|
+
*/
|
|
347
|
+
export function renderTopOpportunitySection(ctx: OpportunityContext | undefined): string {
|
|
348
|
+
if (!ctx) return "";
|
|
349
|
+
|
|
350
|
+
const hints: string[] = [];
|
|
351
|
+
|
|
352
|
+
// Opportunity 1: genome missing + grep-heavy
|
|
353
|
+
if (ctx.noGenome && ctx.weeklyGrepCalls > 5) {
|
|
354
|
+
hints.push(
|
|
355
|
+
` genome not initialised (${ctx.weeklyGrepCalls} grep calls this week). ` +
|
|
356
|
+
`Run \`/ashlr-genome-init\` to enable RAG-mode grep (+20% more savings).`,
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Opportunity 2: no LLM provider + many snipCompact fallbacks
|
|
361
|
+
if (!ctx.hasLlmProvider && ctx.fallbackCount > 10) {
|
|
362
|
+
hints.push(
|
|
363
|
+
` no LLM provider (${ctx.fallbackCount} snipCompact fallbacks). ` +
|
|
364
|
+
`Run \`bun run install-onnx-model\` for offline summarization, ` +
|
|
365
|
+
`or set ANTHROPIC_API_KEY for Haiku-quality summaries.`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Opportunity 3: nudge mode + low conversion.
|
|
370
|
+
// Suppressed when hookMode is "redirect" (already done) or "off" (user opted out).
|
|
371
|
+
// Only fires for "nudge" with meaningful historical signal (< 30% conversion).
|
|
372
|
+
if (ctx.hookMode === "nudge" && ctx.conversionPct < 30) {
|
|
373
|
+
hints.push(
|
|
374
|
+
` hook mode is "nudge" at ${ctx.conversionPct.toFixed(0)}% conversion. ` +
|
|
375
|
+
`Set ASHLR_HOOK_MODE=redirect for automatic enforcement: ` +
|
|
376
|
+
`\`bun run scripts/set-hook-mode.ts redirect\`.`,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (hints.length === 0) return "";
|
|
381
|
+
|
|
382
|
+
const lines: string[] = [];
|
|
383
|
+
lines.push("top opportunities:");
|
|
384
|
+
// Cap at 2.
|
|
385
|
+
for (const h of hints.slice(0, 2)) {
|
|
386
|
+
lines.push(h);
|
|
387
|
+
}
|
|
388
|
+
return lines.join("\n");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---------------------------------------------------------------------------
|
|
392
|
+
// Opportunity context builder — called by savings-server.ts
|
|
393
|
+
// ---------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Read the weekly grep call count from session-log for the past 7 days.
|
|
397
|
+
* Best-effort — returns 0 on any error.
|
|
398
|
+
*/
|
|
399
|
+
export function countWeeklyGrepCalls(home?: string): number {
|
|
400
|
+
const h = home ?? homedir();
|
|
401
|
+
const lines = readLogLines(h);
|
|
402
|
+
if (lines.length === 0) return 0;
|
|
403
|
+
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
404
|
+
let count = 0;
|
|
405
|
+
for (const line of lines) {
|
|
406
|
+
try {
|
|
407
|
+
const r = JSON.parse(line) as Partial<{ ts: string; tool: string }>;
|
|
408
|
+
if (typeof r.ts !== "string") continue;
|
|
409
|
+
if (new Date(r.ts).getTime() < cutoff) continue;
|
|
410
|
+
if (typeof r.tool === "string" && r.tool.includes("grep")) count++;
|
|
411
|
+
} catch {
|
|
412
|
+
// skip
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return count;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Count tool_fallback events in session-log (proxy for snipCompact fallbacks).
|
|
420
|
+
* Best-effort — returns 0 on any error.
|
|
421
|
+
*/
|
|
422
|
+
export function countFallbackEvents(home?: string): number {
|
|
423
|
+
const h = home ?? homedir();
|
|
424
|
+
const lines = readLogLines(h);
|
|
425
|
+
let count = 0;
|
|
426
|
+
for (const line of lines) {
|
|
427
|
+
try {
|
|
428
|
+
const r = JSON.parse(line) as Partial<{ event: string }>;
|
|
429
|
+
if (r.event === "tool_fallback") count++;
|
|
430
|
+
} catch {
|
|
431
|
+
// skip
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return count;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* True when .ashlrcode/genome/manifest.json does NOT exist in cwd.
|
|
439
|
+
*/
|
|
440
|
+
export function isGenomeMissing(cwd?: string): boolean {
|
|
441
|
+
try {
|
|
442
|
+
const dir = cwd ?? process.cwd();
|
|
443
|
+
return !existsSync(join(dir, ".ashlrcode", "genome", "manifest.json"));
|
|
444
|
+
} catch {
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
}
|