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,614 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* ashlr session-resume — reads ~/.ashlr/session-log.jsonl (+ rotated .1/.2)
|
|
4
|
+
* and produces a compact "pick up where you left off" summary.
|
|
5
|
+
*
|
|
6
|
+
* Exported surface:
|
|
7
|
+
* buildResume(opts?) → formatted string, never throws.
|
|
8
|
+
* SessionSummary — the data structure for tests to inspect.
|
|
9
|
+
*
|
|
10
|
+
* CLI usage:
|
|
11
|
+
* bun run scripts/session-resume.ts [<branch>]
|
|
12
|
+
*
|
|
13
|
+
* Limitations:
|
|
14
|
+
* - The session log records tool names + cwd, NOT individual file paths or
|
|
15
|
+
* grep patterns. "Files touched" is approximated from read/edit tool call
|
|
16
|
+
* counts grouped by cwd directory; "patterns" and "bash commands" are
|
|
17
|
+
* reconstructed from tool-call frequency only (no argument capture).
|
|
18
|
+
* - Branch detection uses git log timestamps — requires git in PATH and the
|
|
19
|
+
* cwd to be a git repo at resume time.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { existsSync, readFileSync } from "fs";
|
|
23
|
+
import { homedir } from "os";
|
|
24
|
+
import { join, basename } from "path";
|
|
25
|
+
import { execSync } from "child_process";
|
|
26
|
+
import { costFor } from "../servers/_pricing.ts";
|
|
27
|
+
import { getDigestForResume } from "./weekly-digest.ts";
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Schema
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
interface LogRecord {
|
|
34
|
+
ts: string;
|
|
35
|
+
agent?: string;
|
|
36
|
+
event: string;
|
|
37
|
+
tool: string;
|
|
38
|
+
cwd: string;
|
|
39
|
+
session: string;
|
|
40
|
+
input_size: number;
|
|
41
|
+
output_size: number;
|
|
42
|
+
calls?: number;
|
|
43
|
+
tokens_saved?: number;
|
|
44
|
+
started_at?: string;
|
|
45
|
+
reason?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Options
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
export interface BuildResumeOpts {
|
|
53
|
+
/** Override home dir — used by tests. */
|
|
54
|
+
home?: string;
|
|
55
|
+
/** Filter to sessions on this branch (cross-refs via git log timestamps). */
|
|
56
|
+
branch?: string;
|
|
57
|
+
/** Injected now in ms — tests pin this. */
|
|
58
|
+
now?: number;
|
|
59
|
+
/** Max recent sessions to show (default: 1). */
|
|
60
|
+
maxSessions?: number;
|
|
61
|
+
/** Max lines to read per log file (default: unlimited). */
|
|
62
|
+
limitLines?: number;
|
|
63
|
+
/** Git cwd for branch detection (default: process.cwd()). */
|
|
64
|
+
gitCwd?: string;
|
|
65
|
+
/**
|
|
66
|
+
* When true, suppress the weekly digest prepend even if 7+ days have
|
|
67
|
+
* elapsed. Used by tests that don't want digest output mixed in.
|
|
68
|
+
*/
|
|
69
|
+
suppressDigest?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Per-session summary data (exported for tests)
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
export interface FileStat {
|
|
77
|
+
cwd: string;
|
|
78
|
+
readCalls: number;
|
|
79
|
+
editCalls: number;
|
|
80
|
+
totalCalls: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface ToolCallStat {
|
|
84
|
+
tool: string;
|
|
85
|
+
calls: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface SessionSummary {
|
|
89
|
+
sessionId: string;
|
|
90
|
+
startTs: number; // ms
|
|
91
|
+
endTs: number; // ms
|
|
92
|
+
totalCalls: number;
|
|
93
|
+
tokensSaved: number; // from session_end if present, else 0
|
|
94
|
+
/** Top N cwds touched by read/edit tools, sorted by call count. */
|
|
95
|
+
topFiles: FileStat[];
|
|
96
|
+
/** Top N tools used, sorted by call count — for pattern extraction. */
|
|
97
|
+
topTools: ToolCallStat[];
|
|
98
|
+
/** Top N bash-family tool calls. */
|
|
99
|
+
topBash: ToolCallStat[];
|
|
100
|
+
/** Branch active during session (if detectable). */
|
|
101
|
+
branch?: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Tool classification
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
const READ_TOOLS = new Set([
|
|
109
|
+
"Read", "ashlr__read", "ashlr__diff", "ashlr__diff_semantic",
|
|
110
|
+
"ashlr__tree", "ashlr__ls", "ashlr__glob",
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const EDIT_TOOLS = new Set([
|
|
114
|
+
"Edit", "MultiEdit", "Write",
|
|
115
|
+
"ashlr__edit", "ashlr__multi_edit", "ashlr__edit_structural",
|
|
116
|
+
"ashlr__search_replace_regex", "ashlr__rename_file",
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
const GREP_TOOLS = new Set([
|
|
120
|
+
"Grep", "ashlr__grep",
|
|
121
|
+
]);
|
|
122
|
+
|
|
123
|
+
const BASH_TOOLS = new Set([
|
|
124
|
+
"Bash", "ashlr__bash", "ashlr__bash_list", "ashlr__bash_start",
|
|
125
|
+
"ashlr__bash_stop", "ashlr__bash_tail",
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// JSONL reader
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
function readLines(path: string, limit?: number): string[] {
|
|
133
|
+
try {
|
|
134
|
+
if (!existsSync(path)) return [];
|
|
135
|
+
const raw = readFileSync(path, "utf-8");
|
|
136
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
137
|
+
return limit ? lines.slice(0, limit) : lines;
|
|
138
|
+
} catch {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function parseRecords(lines: string[]): LogRecord[] {
|
|
144
|
+
const out: LogRecord[] = [];
|
|
145
|
+
for (const line of lines) {
|
|
146
|
+
try {
|
|
147
|
+
const r = JSON.parse(line) as Partial<LogRecord>;
|
|
148
|
+
if (typeof r.ts !== "string" || typeof r.event !== "string") continue;
|
|
149
|
+
out.push({
|
|
150
|
+
ts: r.ts,
|
|
151
|
+
agent: r.agent ?? "unknown",
|
|
152
|
+
event: r.event,
|
|
153
|
+
tool: r.tool ?? "unknown",
|
|
154
|
+
cwd: r.cwd ?? "",
|
|
155
|
+
session: r.session ?? "",
|
|
156
|
+
input_size: typeof r.input_size === "number" ? r.input_size : 0,
|
|
157
|
+
output_size: typeof r.output_size === "number" ? r.output_size : 0,
|
|
158
|
+
calls: r.calls,
|
|
159
|
+
tokens_saved: r.tokens_saved,
|
|
160
|
+
started_at: r.started_at,
|
|
161
|
+
reason: r.reason,
|
|
162
|
+
});
|
|
163
|
+
} catch {
|
|
164
|
+
// skip malformed
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Session grouping
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
interface SessionGroup {
|
|
175
|
+
sessionId: string;
|
|
176
|
+
records: LogRecord[];
|
|
177
|
+
sessionEnd?: LogRecord;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function groupBySessions(records: LogRecord[]): Map<string, SessionGroup> {
|
|
181
|
+
const groups = new Map<string, SessionGroup>();
|
|
182
|
+
|
|
183
|
+
for (const r of records) {
|
|
184
|
+
const sid = r.session || "unknown";
|
|
185
|
+
let g = groups.get(sid);
|
|
186
|
+
if (!g) {
|
|
187
|
+
g = { sessionId: sid, records: [] };
|
|
188
|
+
groups.set(sid, g);
|
|
189
|
+
}
|
|
190
|
+
if (r.event === "session_end") {
|
|
191
|
+
g.sessionEnd = r;
|
|
192
|
+
} else {
|
|
193
|
+
g.records.push(r);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return groups;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Branch detection via git log
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
function detectBranchForTimeRange(
|
|
205
|
+
startMs: number,
|
|
206
|
+
endMs: number,
|
|
207
|
+
cwd: string,
|
|
208
|
+
): string | undefined {
|
|
209
|
+
try {
|
|
210
|
+
// Use git log to find the branch that was active around the session time.
|
|
211
|
+
// Strategy: ask git for refs that have commits in the session window.
|
|
212
|
+
const since = new Date(startMs - 300_000).toISOString(); // 5m before
|
|
213
|
+
const until = new Date(endMs + 300_000).toISOString(); // 5m after
|
|
214
|
+
|
|
215
|
+
const result = execSync(
|
|
216
|
+
`git log --all --oneline --format="%D" --after="${since}" --before="${until}" 2>/dev/null | head -5`,
|
|
217
|
+
{ cwd, encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] },
|
|
218
|
+
).trim();
|
|
219
|
+
|
|
220
|
+
if (!result) return undefined;
|
|
221
|
+
|
|
222
|
+
// Parse "HEAD -> main, origin/main" style refs
|
|
223
|
+
for (const line of result.split("\n")) {
|
|
224
|
+
const headArrow = line.match(/HEAD -> ([^\s,]+)/);
|
|
225
|
+
if (headArrow) return headArrow[1];
|
|
226
|
+
// fallback: first local branch name
|
|
227
|
+
const local = line.match(/(?:^|, )([^\s,/]+\/[^\s,]+|[a-zA-Z][^\s,]*)/);
|
|
228
|
+
if (local && !local[1].startsWith("origin/")) return local[1];
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
} catch {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
// Session analysis
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
function analyzeSession(
|
|
241
|
+
group: SessionGroup,
|
|
242
|
+
branch?: string,
|
|
243
|
+
gitCwd?: string,
|
|
244
|
+
now?: number,
|
|
245
|
+
): SessionSummary {
|
|
246
|
+
const { sessionId, records, sessionEnd } = group;
|
|
247
|
+
|
|
248
|
+
// Timestamps
|
|
249
|
+
const timestamps = records
|
|
250
|
+
.map((r) => Date.parse(r.ts))
|
|
251
|
+
.filter(Number.isFinite);
|
|
252
|
+
|
|
253
|
+
const startTs = timestamps.length > 0 ? Math.min(...timestamps) : (now ?? Date.now());
|
|
254
|
+
const endTs = timestamps.length > 0 ? Math.max(...timestamps) : startTs;
|
|
255
|
+
|
|
256
|
+
// Tokens saved — prefer session_end, else 0
|
|
257
|
+
const tokensSaved = sessionEnd?.tokens_saved ?? 0;
|
|
258
|
+
|
|
259
|
+
// Total calls (tool_call events only)
|
|
260
|
+
const toolCalls = records.filter((r) =>
|
|
261
|
+
r.event === "tool_call" || r.event === "tool_escalate" || r.event === "tool_fallback"
|
|
262
|
+
);
|
|
263
|
+
const totalCalls = toolCalls.length;
|
|
264
|
+
|
|
265
|
+
// Files touched: group by cwd, count read vs edit calls
|
|
266
|
+
const cwdStats = new Map<string, { read: number; edit: number; grep: number; bash: number }>();
|
|
267
|
+
const toolFreq = new Map<string, number>();
|
|
268
|
+
const bashFreq = new Map<string, number>();
|
|
269
|
+
|
|
270
|
+
for (const r of toolCalls) {
|
|
271
|
+
const tool = r.tool;
|
|
272
|
+
|
|
273
|
+
// Tool frequency (all tools)
|
|
274
|
+
toolFreq.set(tool, (toolFreq.get(tool) ?? 0) + 1);
|
|
275
|
+
|
|
276
|
+
// Bash frequency
|
|
277
|
+
if (BASH_TOOLS.has(tool)) {
|
|
278
|
+
bashFreq.set(tool, (bashFreq.get(tool) ?? 0) + 1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// CWD-level file stats
|
|
282
|
+
if (READ_TOOLS.has(tool) || EDIT_TOOLS.has(tool) || GREP_TOOLS.has(tool)) {
|
|
283
|
+
const cwd = r.cwd || "unknown";
|
|
284
|
+
let s = cwdStats.get(cwd);
|
|
285
|
+
if (!s) {
|
|
286
|
+
s = { read: 0, edit: 0, grep: 0, bash: 0 };
|
|
287
|
+
cwdStats.set(cwd, s);
|
|
288
|
+
}
|
|
289
|
+
if (READ_TOOLS.has(tool)) s.read++;
|
|
290
|
+
else if (EDIT_TOOLS.has(tool)) s.edit++;
|
|
291
|
+
else if (GREP_TOOLS.has(tool)) s.grep++;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Top 5 files (cwds) by total call count
|
|
296
|
+
const topFiles: FileStat[] = [...cwdStats.entries()]
|
|
297
|
+
.map(([cwd, s]) => ({
|
|
298
|
+
cwd,
|
|
299
|
+
readCalls: s.read,
|
|
300
|
+
editCalls: s.edit,
|
|
301
|
+
totalCalls: s.read + s.edit + s.grep,
|
|
302
|
+
}))
|
|
303
|
+
.sort((a, b) => b.totalCalls - a.totalCalls)
|
|
304
|
+
.slice(0, 5);
|
|
305
|
+
|
|
306
|
+
// Top 3 grep/search tools + top N all tools (for "patterns" context)
|
|
307
|
+
const topTools: ToolCallStat[] = [...toolFreq.entries()]
|
|
308
|
+
.map(([tool, calls]) => ({ tool, calls }))
|
|
309
|
+
.sort((a, b) => b.calls - a.calls)
|
|
310
|
+
.slice(0, 5);
|
|
311
|
+
|
|
312
|
+
// Top 5 bash tools
|
|
313
|
+
const topBash: ToolCallStat[] = [...bashFreq.entries()]
|
|
314
|
+
.map(([tool, calls]) => ({ tool, calls }))
|
|
315
|
+
.sort((a, b) => b.calls - a.calls)
|
|
316
|
+
.slice(0, 5);
|
|
317
|
+
|
|
318
|
+
// Branch detection
|
|
319
|
+
let detectedBranch = branch;
|
|
320
|
+
if (!detectedBranch && gitCwd && timestamps.length > 0) {
|
|
321
|
+
detectedBranch = detectBranchForTimeRange(startTs, endTs, gitCwd);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
sessionId,
|
|
326
|
+
startTs,
|
|
327
|
+
endTs,
|
|
328
|
+
totalCalls,
|
|
329
|
+
tokensSaved,
|
|
330
|
+
topFiles,
|
|
331
|
+
topTools,
|
|
332
|
+
topBash,
|
|
333
|
+
branch: detectedBranch,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// Formatting helpers
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
|
|
341
|
+
function fmtDurationRelative(ms: number, now: number): string {
|
|
342
|
+
const diffMs = now - ms;
|
|
343
|
+
if (diffMs < 0) return "just now";
|
|
344
|
+
const mins = Math.floor(diffMs / 60_000);
|
|
345
|
+
const hours = Math.floor(mins / 60);
|
|
346
|
+
const days = Math.floor(hours / 24);
|
|
347
|
+
if (days >= 2) return `${days} days ago`;
|
|
348
|
+
if (days === 1) return "yesterday";
|
|
349
|
+
if (hours >= 1) return `${hours}h ago`;
|
|
350
|
+
if (mins >= 1) return `${mins}m ago`;
|
|
351
|
+
return "just now";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function fmtDuration(ms: number): string {
|
|
355
|
+
if (ms <= 0) return "< 1m";
|
|
356
|
+
const mins = Math.floor(ms / 60_000);
|
|
357
|
+
const hours = Math.floor(mins / 60);
|
|
358
|
+
if (hours >= 1) return `${hours}h ${mins % 60}m`;
|
|
359
|
+
if (mins >= 1) return `${mins}m`;
|
|
360
|
+
return "< 1m";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function fmtTokens(n: number): string {
|
|
364
|
+
if (!Number.isFinite(n) || n < 0) return "0";
|
|
365
|
+
if (n < 1000) return String(Math.floor(n));
|
|
366
|
+
if (n < 1_000_000) return (n / 1000).toFixed(1) + "K";
|
|
367
|
+
return (n / 1_000_000).toFixed(1) + "M";
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function fmtCost(tokens: number): string {
|
|
371
|
+
const dollars = costFor(tokens);
|
|
372
|
+
return `$${dollars.toFixed(2)}`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function shortId(id: string): string {
|
|
376
|
+
if (!id || id.length <= 8) return id || "?";
|
|
377
|
+
return id.slice(0, 8);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
381
|
+
// Render
|
|
382
|
+
// ---------------------------------------------------------------------------
|
|
383
|
+
|
|
384
|
+
function renderSession(
|
|
385
|
+
summary: SessionSummary,
|
|
386
|
+
now: number,
|
|
387
|
+
index: number,
|
|
388
|
+
): string {
|
|
389
|
+
const lines: string[] = [];
|
|
390
|
+
|
|
391
|
+
const ago = fmtDurationRelative(summary.startTs, now);
|
|
392
|
+
const dur = fmtDuration(summary.endTs - summary.startTs);
|
|
393
|
+
const tokStr = fmtTokens(summary.tokensSaved);
|
|
394
|
+
const costStr = summary.tokensSaved > 0 ? ` ≈${fmtCost(summary.tokensSaved)}` : "";
|
|
395
|
+
const saved = summary.tokensSaved > 0
|
|
396
|
+
? ` — saved ${tokStr} tokens${costStr}`
|
|
397
|
+
: "";
|
|
398
|
+
|
|
399
|
+
const label = index === 0 ? "Last session" : `Session ${index + 1}`;
|
|
400
|
+
lines.push(`${label} (${ago}, ${dur}${saved}):`);
|
|
401
|
+
|
|
402
|
+
if (summary.branch) {
|
|
403
|
+
lines.push(` Branch: ${summary.branch}`);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (summary.topFiles.length > 0) {
|
|
407
|
+
const first = summary.topFiles[0]!;
|
|
408
|
+
const dirName = basename(first.cwd) || first.cwd;
|
|
409
|
+
lines.push(
|
|
410
|
+
` Work dir: ${dirName} (${first.readCalls} reads, ${first.editCalls} edits)`,
|
|
411
|
+
);
|
|
412
|
+
for (const f of summary.topFiles.slice(1)) {
|
|
413
|
+
const d = basename(f.cwd) || f.cwd;
|
|
414
|
+
lines.push(
|
|
415
|
+
` ${d} (${f.readCalls} reads, ${f.editCalls} edits)`,
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
lines.push(" Work dir: (not recorded)");
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Tool patterns: highlight grep, search, read calls
|
|
423
|
+
const searchTools = summary.topTools.filter((t) => GREP_TOOLS.has(t.tool));
|
|
424
|
+
if (searchTools.length > 0) {
|
|
425
|
+
const top3 = searchTools.slice(0, 3);
|
|
426
|
+
lines.push(` Patterns: ${top3.map((t) => `${t.tool} ×${t.calls}`).join(", ")}`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Bash calls
|
|
430
|
+
if (summary.topBash.length > 0) {
|
|
431
|
+
const top5 = summary.topBash.slice(0, 5);
|
|
432
|
+
lines.push(` Bash: ${top5.map((t) => `${t.tool} ×${t.calls}`).join(", ")}`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
lines.push(` Calls: ${summary.totalCalls} tool invocations`);
|
|
436
|
+
|
|
437
|
+
return lines.join("\n");
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function renderSuggestions(summary: SessionSummary): string {
|
|
441
|
+
const lines: string[] = [];
|
|
442
|
+
lines.push("");
|
|
443
|
+
lines.push("Resume? Suggested next steps based on the trail:");
|
|
444
|
+
|
|
445
|
+
if (summary.topFiles.length > 0) {
|
|
446
|
+
const top = summary.topFiles[0]!;
|
|
447
|
+
const dirName = basename(top.cwd) || top.cwd;
|
|
448
|
+
if (top.editCalls > 0) {
|
|
449
|
+
lines.push(` - Re-open ${dirName} (last edits in this session)`);
|
|
450
|
+
} else {
|
|
451
|
+
lines.push(` - Review ${dirName} (most activity in this session)`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (summary.topBash.length > 0) {
|
|
456
|
+
const topBash = summary.topBash[0]!;
|
|
457
|
+
lines.push(` - Re-run: ${topBash.tool} (used ×${topBash.calls} last session)`);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (summary.branch) {
|
|
461
|
+
lines.push(` - Continue on branch: ${summary.branch}`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (lines.length === 2) {
|
|
465
|
+
// Only the header was added
|
|
466
|
+
lines.push(" - Check git status and recent commits to orient");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return lines.join("\n");
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ---------------------------------------------------------------------------
|
|
473
|
+
// Branch filter: match sessions whose timestamp range overlaps a git branch
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
|
|
476
|
+
function getGitBranchCommitRange(
|
|
477
|
+
branch: string,
|
|
478
|
+
gitCwd: string,
|
|
479
|
+
): { earliest: number; latest: number } | undefined {
|
|
480
|
+
try {
|
|
481
|
+
const result = execSync(
|
|
482
|
+
`git log "${branch}" --format="%ci" --max-count=50 2>/dev/null`,
|
|
483
|
+
{ cwd: gitCwd, encoding: "utf-8", timeout: 3000, stdio: ["ignore", "pipe", "ignore"] },
|
|
484
|
+
).trim();
|
|
485
|
+
|
|
486
|
+
if (!result) return undefined;
|
|
487
|
+
|
|
488
|
+
const timestamps = result
|
|
489
|
+
.split("\n")
|
|
490
|
+
.map((l) => Date.parse(l.trim()))
|
|
491
|
+
.filter(Number.isFinite);
|
|
492
|
+
|
|
493
|
+
if (timestamps.length === 0) return undefined;
|
|
494
|
+
|
|
495
|
+
return {
|
|
496
|
+
earliest: Math.min(...timestamps),
|
|
497
|
+
latest: Math.max(...timestamps),
|
|
498
|
+
};
|
|
499
|
+
} catch {
|
|
500
|
+
return undefined;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ---------------------------------------------------------------------------
|
|
505
|
+
// Public API
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
507
|
+
|
|
508
|
+
export function buildResume(opts: BuildResumeOpts = {}): string {
|
|
509
|
+
const home = opts.home ?? homedir();
|
|
510
|
+
const now = opts.now ?? Date.now();
|
|
511
|
+
const maxSessions = opts.maxSessions ?? 1;
|
|
512
|
+
const gitCwd = opts.gitCwd ?? process.cwd();
|
|
513
|
+
|
|
514
|
+
const logPath = join(home, ".ashlr", "session-log.jsonl");
|
|
515
|
+
const rotated1 = logPath + ".1";
|
|
516
|
+
const rotated2 = logPath + ".2";
|
|
517
|
+
|
|
518
|
+
const lines1 = readLines(rotated2, opts.limitLines);
|
|
519
|
+
const lines2 = readLines(rotated1, opts.limitLines);
|
|
520
|
+
const lines3 = readLines(logPath, opts.limitLines);
|
|
521
|
+
const allLines = [...lines1, ...lines2, ...lines3];
|
|
522
|
+
|
|
523
|
+
if (allLines.length === 0) {
|
|
524
|
+
return [
|
|
525
|
+
"No prior sessions found — you're starting fresh.",
|
|
526
|
+
"",
|
|
527
|
+
"Once you use ashlr__read, ashlr__grep, or ashlr__edit, activity will",
|
|
528
|
+
`be recorded to: ${logPath}`,
|
|
529
|
+
].join("\n");
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const records = parseRecords(allLines);
|
|
533
|
+
const groups = groupBySessions(records);
|
|
534
|
+
|
|
535
|
+
if (groups.size === 0) {
|
|
536
|
+
return "No prior sessions found — you're starting fresh.";
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Compute summaries, sorted by most recent first
|
|
540
|
+
let summaries: SessionSummary[] = [...groups.values()]
|
|
541
|
+
.map((g) => analyzeSession(g, undefined, gitCwd, now))
|
|
542
|
+
.filter((s) => s.totalCalls > 0)
|
|
543
|
+
.sort((a, b) => b.endTs - a.endTs);
|
|
544
|
+
|
|
545
|
+
if (summaries.length === 0) {
|
|
546
|
+
return "No activity recorded yet — you're starting fresh.";
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Branch filter
|
|
550
|
+
if (opts.branch) {
|
|
551
|
+
const range = getGitBranchCommitRange(opts.branch, gitCwd);
|
|
552
|
+
if (range) {
|
|
553
|
+
const branchSummaries = summaries.filter(
|
|
554
|
+
(s) => s.startTs <= range.latest + 24 * 60 * 60_000 &&
|
|
555
|
+
s.endTs >= range.earliest - 24 * 60 * 60_000,
|
|
556
|
+
);
|
|
557
|
+
if (branchSummaries.length > 0) {
|
|
558
|
+
summaries = branchSummaries.map((s) => ({ ...s, branch: opts.branch }));
|
|
559
|
+
} else {
|
|
560
|
+
// No sessions match the branch — fall back to most recent with a note
|
|
561
|
+
summaries = summaries.slice(0, maxSessions);
|
|
562
|
+
const parts = summaries
|
|
563
|
+
.map((s, i) => renderSession(s, now, i))
|
|
564
|
+
.join("\n\n");
|
|
565
|
+
return [
|
|
566
|
+
`No sessions found on branch "${opts.branch}".`,
|
|
567
|
+
"Showing most recent session instead:",
|
|
568
|
+
"",
|
|
569
|
+
parts,
|
|
570
|
+
renderSuggestions(summaries[0]!),
|
|
571
|
+
].join("\n");
|
|
572
|
+
}
|
|
573
|
+
} else {
|
|
574
|
+
// Can't determine branch range — tag matching sessions with branch name
|
|
575
|
+
summaries = summaries.slice(0, maxSessions).map((s) => ({
|
|
576
|
+
...s,
|
|
577
|
+
branch: opts.branch,
|
|
578
|
+
}));
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const toShow = summaries.slice(0, maxSessions);
|
|
583
|
+
const parts = toShow.map((s, i) => renderSession(s, now, i)).join("\n\n");
|
|
584
|
+
const suggestions = renderSuggestions(toShow[0]!);
|
|
585
|
+
|
|
586
|
+
// Weekly digest — prepend when 7+ days have elapsed since last shown.
|
|
587
|
+
// Suppressed when suppressDigest=true (test isolation) or on error.
|
|
588
|
+
let digestPrefix = "";
|
|
589
|
+
if (!opts.suppressDigest) {
|
|
590
|
+
try {
|
|
591
|
+
const digest = getDigestForResume(home, now);
|
|
592
|
+
if (digest) digestPrefix = digest + "\n\n---\n\n";
|
|
593
|
+
} catch {
|
|
594
|
+
/* digest is decoration — never break resume */
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return digestPrefix + [parts, suggestions].join("\n");
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ---------------------------------------------------------------------------
|
|
602
|
+
// CLI entry
|
|
603
|
+
// ---------------------------------------------------------------------------
|
|
604
|
+
|
|
605
|
+
if (import.meta.main) {
|
|
606
|
+
try {
|
|
607
|
+
const branch = process.argv[2]; // optional branch arg
|
|
608
|
+
process.stdout.write(buildResume({ branch }) + "\n");
|
|
609
|
+
} catch (err) {
|
|
610
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
611
|
+
process.stdout.write(`session-resume failed: ${msg}\n`);
|
|
612
|
+
}
|
|
613
|
+
process.exit(0);
|
|
614
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* set-hook-mode.ts — Flip ~/.ashlr/config.json::hookMode.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* bun run scripts/set-hook-mode.ts redirect
|
|
7
|
+
* bun run scripts/set-hook-mode.ts nudge
|
|
8
|
+
* bun run scripts/set-hook-mode.ts off
|
|
9
|
+
*
|
|
10
|
+
* Prints: "ashlr hook mode set to <mode>. Restart Claude Code to apply."
|
|
11
|
+
* Exits 1 with an error message on invalid input.
|
|
12
|
+
*
|
|
13
|
+
* Design:
|
|
14
|
+
* - Reads existing config.json to preserve other keys.
|
|
15
|
+
* - Writes atomically (write to tmp, then rename — avoids corruption if
|
|
16
|
+
* killed mid-write).
|
|
17
|
+
* - Creates ~/.ashlr/ if it doesn't exist.
|
|
18
|
+
* - Never touches settings.json or any other file.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
22
|
+
import { homedir } from "os";
|
|
23
|
+
import { dirname, join } from "path";
|
|
24
|
+
|
|
25
|
+
export type HookMode = "redirect" | "nudge" | "off";
|
|
26
|
+
|
|
27
|
+
const VALID_MODES: HookMode[] = ["redirect", "nudge", "off"];
|
|
28
|
+
|
|
29
|
+
function configPath(home: string = process.env.HOME ?? homedir()): string {
|
|
30
|
+
return join(home, ".ashlr", "config.json");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set the hook mode in ~/.ashlr/config.json.
|
|
35
|
+
* Returns the path written to — exported so tests can call directly.
|
|
36
|
+
*/
|
|
37
|
+
export function setHookMode(mode: HookMode, home?: string): string {
|
|
38
|
+
const p = configPath(home);
|
|
39
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
40
|
+
|
|
41
|
+
// Read existing config, preserve other keys.
|
|
42
|
+
let existing: Record<string, unknown> = {};
|
|
43
|
+
if (existsSync(p)) {
|
|
44
|
+
try {
|
|
45
|
+
existing = JSON.parse(readFileSync(p, "utf-8")) as Record<string, unknown>;
|
|
46
|
+
} catch {
|
|
47
|
+
// Overwrite corrupt config.
|
|
48
|
+
existing = {};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
existing.hookMode = mode;
|
|
53
|
+
|
|
54
|
+
// Atomic write via tmp + rename.
|
|
55
|
+
const tmp = p + ".tmp." + process.pid;
|
|
56
|
+
writeFileSync(tmp, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
57
|
+
renameSync(tmp, p);
|
|
58
|
+
|
|
59
|
+
return p;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// CLI entry point
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
if (import.meta.main) {
|
|
67
|
+
const arg = process.argv[2];
|
|
68
|
+
|
|
69
|
+
if (!arg || !(VALID_MODES as string[]).includes(arg)) {
|
|
70
|
+
process.stderr.write(
|
|
71
|
+
`Error: invalid hook mode "${arg ?? ""}". ` +
|
|
72
|
+
`Valid values: redirect, nudge, off.\n` +
|
|
73
|
+
`Usage: bun run scripts/set-hook-mode.ts <redirect|nudge|off>\n`,
|
|
74
|
+
);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const mode = arg as HookMode;
|
|
79
|
+
setHookMode(mode);
|
|
80
|
+
process.stdout.write(
|
|
81
|
+
`ashlr hook mode set to ${mode}. Restart Claude Code to apply.\n`,
|
|
82
|
+
);
|
|
83
|
+
}
|