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,438 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* ashlr handoff-pack — generates a compact context primer so the next
|
|
4
|
+
* session can resume cold without re-exploring.
|
|
5
|
+
*
|
|
6
|
+
* Writes to .ashlr/handoffs/YYYY-MM-DD-HHMMSS-<rand>.md
|
|
7
|
+
*
|
|
8
|
+
* CLI flags:
|
|
9
|
+
* --session <id> override which session to pack (default: current)
|
|
10
|
+
* --last re-print the most recent handoff (no new write)
|
|
11
|
+
* --dir <path> override output directory
|
|
12
|
+
* --days N how many days of log to scan (default: 7)
|
|
13
|
+
*
|
|
14
|
+
* Exported surface:
|
|
15
|
+
* buildHandoffPack(opts?) -> { path: string; content: string }, never throws.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync } from "fs";
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
import { join, basename, relative } from "path";
|
|
21
|
+
import { randomBytes } from "crypto";
|
|
22
|
+
import { currentSessionId, readCurrentSession, readStats } from "../servers/_stats.ts";
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Schema (matches session-log-report.ts)
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
interface LogRecord {
|
|
29
|
+
ts: string;
|
|
30
|
+
agent: string;
|
|
31
|
+
event: string;
|
|
32
|
+
tool: string;
|
|
33
|
+
cwd: string;
|
|
34
|
+
session: string;
|
|
35
|
+
input_size: number;
|
|
36
|
+
output_size: number;
|
|
37
|
+
calls?: number;
|
|
38
|
+
tokens_saved?: number;
|
|
39
|
+
started_at?: string;
|
|
40
|
+
reason?: string;
|
|
41
|
+
// TodoWrite input payload (serialized JSON string or object)
|
|
42
|
+
input?: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Options
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
export interface BuildHandoffPackOpts {
|
|
50
|
+
/** Override home dir — used by tests. */
|
|
51
|
+
home?: string;
|
|
52
|
+
/** Override session id to pack. Default: currentSessionId(). */
|
|
53
|
+
sessionId?: string;
|
|
54
|
+
/** Override output directory. Default: <home>/.ashlr/handoffs/ */
|
|
55
|
+
outDir?: string;
|
|
56
|
+
/** Injected now in ms — tests pin this. */
|
|
57
|
+
now?: number;
|
|
58
|
+
/** Max lines per file. */
|
|
59
|
+
limitLines?: number;
|
|
60
|
+
/** Random suffix override — lets tests force collision-safe filenames. */
|
|
61
|
+
randSuffix?: string;
|
|
62
|
+
/** Skip writing to disk and return content only (for tests). */
|
|
63
|
+
dryRun?: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// JSONL helpers
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
function readLines(path: string, limit?: number): string[] {
|
|
71
|
+
try {
|
|
72
|
+
if (!existsSync(path)) return [];
|
|
73
|
+
const raw = readFileSync(path, "utf-8");
|
|
74
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
75
|
+
return limit ? lines.slice(0, limit) : lines;
|
|
76
|
+
} catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function parseRecords(lines: string[]): LogRecord[] {
|
|
82
|
+
const out: LogRecord[] = [];
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
try {
|
|
85
|
+
const r = JSON.parse(line) as Partial<LogRecord>;
|
|
86
|
+
if (typeof r.ts !== "string" || typeof r.event !== "string") continue;
|
|
87
|
+
out.push({
|
|
88
|
+
ts: r.ts,
|
|
89
|
+
agent: r.agent ?? "unknown",
|
|
90
|
+
event: r.event,
|
|
91
|
+
tool: r.tool ?? "unknown",
|
|
92
|
+
cwd: r.cwd ?? "",
|
|
93
|
+
session: r.session ?? "",
|
|
94
|
+
input_size: typeof r.input_size === "number" ? r.input_size : 0,
|
|
95
|
+
output_size: typeof r.output_size === "number" ? r.output_size : 0,
|
|
96
|
+
calls: r.calls,
|
|
97
|
+
tokens_saved: r.tokens_saved,
|
|
98
|
+
started_at: r.started_at,
|
|
99
|
+
reason: r.reason,
|
|
100
|
+
input: r.input,
|
|
101
|
+
});
|
|
102
|
+
} catch {
|
|
103
|
+
// skip malformed
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Timestamp filename helper
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
function makeFilename(now: number, randSuffix?: string): string {
|
|
114
|
+
const d = new Date(now);
|
|
115
|
+
const pad = (n: number, w = 2) => String(n).padStart(w, "0");
|
|
116
|
+
const date =
|
|
117
|
+
`${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}`;
|
|
118
|
+
const time =
|
|
119
|
+
`${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}`;
|
|
120
|
+
const suffix = randSuffix ?? randomBytes(3).toString("hex");
|
|
121
|
+
return `${date}-${time}-${suffix}.md`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Section builders
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
function fmtTokens(n: number): string {
|
|
129
|
+
if (n < 1000) return `${n}`;
|
|
130
|
+
return `${(n / 1000).toFixed(1)}K`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function buildSessionSummarySection(
|
|
134
|
+
sessionId: string,
|
|
135
|
+
statsHome?: string,
|
|
136
|
+
): Promise<string> {
|
|
137
|
+
try {
|
|
138
|
+
// readCurrentSession reads from the real stats.json path — we can't
|
|
139
|
+
// easily redirect it in tests. For test isolation, callers use dryRun +
|
|
140
|
+
// a synthetic sessionId that won't be found, so we gracefully degrade.
|
|
141
|
+
const session = await readCurrentSession(sessionId);
|
|
142
|
+
const lines: string[] = ["## Session Summary", ""];
|
|
143
|
+
|
|
144
|
+
lines.push(`- Session ID: ${sessionId}`);
|
|
145
|
+
lines.push(`- Started at: ${session.startedAt}`);
|
|
146
|
+
lines.push(`- Calls: ${session.calls}`);
|
|
147
|
+
lines.push(`- Tokens saved: ${fmtTokens(session.tokensSaved)}`);
|
|
148
|
+
|
|
149
|
+
// Dominant tools — top 3 by calls
|
|
150
|
+
const toolEntries = Object.entries(session.byTool)
|
|
151
|
+
.sort((a, b) => b[1].calls - a[1].calls)
|
|
152
|
+
.slice(0, 3);
|
|
153
|
+
|
|
154
|
+
if (toolEntries.length > 0) {
|
|
155
|
+
lines.push(`- Dominant tools:`);
|
|
156
|
+
for (const [tool, stat] of toolEntries) {
|
|
157
|
+
lines.push(` - ${tool}: ${stat.calls} calls, ${fmtTokens(stat.tokensSaved)} tok saved`);
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
lines.push(`- Dominant tools: (none recorded)`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return lines.join("\n");
|
|
164
|
+
} catch {
|
|
165
|
+
return [
|
|
166
|
+
"## Session Summary",
|
|
167
|
+
"",
|
|
168
|
+
`- Session ID: ${sessionId}`,
|
|
169
|
+
"- Stats unavailable (no stats.json or session not found).",
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const READ_TOOLS = new Set([
|
|
175
|
+
"Read", "Edit", "MultiEdit", "ashlr__read", "ashlr__edit", "ashlr__multi_edit",
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
function buildRecentFilesSection(
|
|
179
|
+
records: LogRecord[],
|
|
180
|
+
sessionId: string,
|
|
181
|
+
cwd: string,
|
|
182
|
+
): string {
|
|
183
|
+
const sessionRecords = sessionId
|
|
184
|
+
? records.filter((r) => r.session === sessionId)
|
|
185
|
+
: records;
|
|
186
|
+
|
|
187
|
+
const pathCounts = new Map<string, number>();
|
|
188
|
+
|
|
189
|
+
for (const r of sessionRecords) {
|
|
190
|
+
if (!READ_TOOLS.has(r.tool)) continue;
|
|
191
|
+
// We don't have the file path in the log. Use cwd as a coarse proxy, but
|
|
192
|
+
// more usefully track input_size as a stand-in identifier. Since the path
|
|
193
|
+
// isn't logged, we emit the cwd + tool combination as context.
|
|
194
|
+
// Best we can do: emit unique (cwd, tool) pairs.
|
|
195
|
+
const key = r.cwd || cwd;
|
|
196
|
+
pathCounts.set(key, (pathCounts.get(key) ?? 0) + 1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const lines: string[] = ["## Recent Files Touched", ""];
|
|
200
|
+
|
|
201
|
+
if (pathCounts.size === 0) {
|
|
202
|
+
lines.push("(no file operations recorded for this session)");
|
|
203
|
+
return lines.join("\n");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const sorted = [...pathCounts.entries()]
|
|
207
|
+
.sort((a, b) => b[1] - a[1])
|
|
208
|
+
.slice(0, 10);
|
|
209
|
+
|
|
210
|
+
for (const [p, count] of sorted) {
|
|
211
|
+
// Show last two path components to keep lines short but identifiable.
|
|
212
|
+
// Split on both separators so Windows cwds (C:\foo\bar) render correctly.
|
|
213
|
+
const parts = p.replace(/[\\/]+$/, "").split(/[\\/]/).filter(Boolean);
|
|
214
|
+
const display = parts.length >= 2
|
|
215
|
+
? parts.slice(-2).join("/")
|
|
216
|
+
: parts[0] ?? p;
|
|
217
|
+
lines.push(`- ${display} (${count} ops)`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return lines.join("\n");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function buildGenomeSection(cwd: string): string {
|
|
224
|
+
const lines: string[] = ["## Genome Status", ""];
|
|
225
|
+
|
|
226
|
+
if (!cwd) {
|
|
227
|
+
lines.push("(no working directory)");
|
|
228
|
+
return lines.join("\n");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const manifestPath = join(cwd, ".ashlrcode", "genome", "manifest.json");
|
|
232
|
+
if (!existsSync(manifestPath)) {
|
|
233
|
+
lines.push("No genome found. Run /ashlr-genome-init to initialize.");
|
|
234
|
+
return lines.join("\n");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")) as {
|
|
239
|
+
sections?: unknown[];
|
|
240
|
+
updatedAt?: string;
|
|
241
|
+
[k: string]: unknown;
|
|
242
|
+
};
|
|
243
|
+
const sectionCount = Array.isArray(manifest.sections) ? manifest.sections.length : "?";
|
|
244
|
+
const updatedAt = manifest.updatedAt ?? "unknown";
|
|
245
|
+
lines.push(`- Sections: ${sectionCount}`);
|
|
246
|
+
lines.push(`- Last updated: ${updatedAt}`);
|
|
247
|
+
} catch {
|
|
248
|
+
lines.push("Manifest exists but could not be parsed.");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return lines.join("\n");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function buildOpenTodosSection(records: LogRecord[], sessionId: string): string {
|
|
255
|
+
const lines: string[] = ["## Open Todos", ""];
|
|
256
|
+
|
|
257
|
+
const todoRecords = records.filter(
|
|
258
|
+
(r) =>
|
|
259
|
+
r.tool === "TodoWrite" &&
|
|
260
|
+
(sessionId ? r.session === sessionId : true),
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
if (todoRecords.length === 0) {
|
|
264
|
+
lines.push("(no TodoWrite calls in this session)");
|
|
265
|
+
return lines.join("\n");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Use the latest TodoWrite record's input
|
|
269
|
+
const latest = todoRecords[todoRecords.length - 1]!;
|
|
270
|
+
const input = latest.input;
|
|
271
|
+
|
|
272
|
+
if (!input) {
|
|
273
|
+
lines.push("(TodoWrite called but input not recorded)");
|
|
274
|
+
return lines.join("\n");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
// input may be a JSON string or already an object
|
|
279
|
+
const parsed: unknown =
|
|
280
|
+
typeof input === "string" ? JSON.parse(input) : input;
|
|
281
|
+
|
|
282
|
+
// TodoWrite input shape: { todos: Array<{ content, status, priority, id }> }
|
|
283
|
+
const todos =
|
|
284
|
+
parsed !== null &&
|
|
285
|
+
typeof parsed === "object" &&
|
|
286
|
+
"todos" in (parsed as object) &&
|
|
287
|
+
Array.isArray((parsed as { todos: unknown }).todos)
|
|
288
|
+
? (parsed as { todos: Array<{ content?: string; status?: string; priority?: string }> }).todos
|
|
289
|
+
: Array.isArray(parsed)
|
|
290
|
+
? (parsed as Array<{ content?: string; status?: string; priority?: string }>)
|
|
291
|
+
: null;
|
|
292
|
+
|
|
293
|
+
if (!todos) {
|
|
294
|
+
lines.push("(could not parse todo list)");
|
|
295
|
+
return lines.join("\n");
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
for (const todo of todos) {
|
|
299
|
+
const status = todo.status ?? "pending";
|
|
300
|
+
const content = todo.content ?? "(no content)";
|
|
301
|
+
const priority = todo.priority ? ` [${todo.priority}]` : "";
|
|
302
|
+
lines.push(`- [${status}]${priority} ${content}`);
|
|
303
|
+
}
|
|
304
|
+
} catch {
|
|
305
|
+
lines.push("(could not parse TodoWrite input)");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return lines.join("\n");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// Public API
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
|
|
315
|
+
export interface HandoffResult {
|
|
316
|
+
path: string;
|
|
317
|
+
content: string;
|
|
318
|
+
/** True when --last was used and no prior handoffs exist. */
|
|
319
|
+
empty?: boolean;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export async function buildHandoffPack(
|
|
323
|
+
opts: BuildHandoffPackOpts = {},
|
|
324
|
+
): Promise<HandoffResult> {
|
|
325
|
+
const homeDir = opts.home ?? homedir();
|
|
326
|
+
const now = opts.now ?? Date.now();
|
|
327
|
+
const outDir = opts.outDir ?? join(homeDir, ".ashlr", "handoffs");
|
|
328
|
+
const sessionId = opts.sessionId ?? currentSessionId();
|
|
329
|
+
|
|
330
|
+
const logPath = join(homeDir, ".ashlr", "session-log.jsonl");
|
|
331
|
+
const rotatedPath = join(homeDir, ".ashlr", "session-log.jsonl.1");
|
|
332
|
+
|
|
333
|
+
const lines1 = readLines(rotatedPath, opts.limitLines);
|
|
334
|
+
const lines2 = readLines(logPath, opts.limitLines);
|
|
335
|
+
const allRecords = parseRecords([...lines1, ...lines2]);
|
|
336
|
+
|
|
337
|
+
// Best-guess cwd: most common cwd in session records
|
|
338
|
+
const sessionRecords = allRecords.filter((r) => r.session === sessionId);
|
|
339
|
+
const cwdCounts = new Map<string, number>();
|
|
340
|
+
for (const r of sessionRecords) {
|
|
341
|
+
if (r.cwd) cwdCounts.set(r.cwd, (cwdCounts.get(r.cwd) ?? 0) + 1);
|
|
342
|
+
}
|
|
343
|
+
let cwd = "";
|
|
344
|
+
let cwdMax = 0;
|
|
345
|
+
for (const [c, n] of cwdCounts) {
|
|
346
|
+
if (n > cwdMax) { cwdMax = n; cwd = c; }
|
|
347
|
+
}
|
|
348
|
+
// Fall back to process.cwd() if no session records found
|
|
349
|
+
if (!cwd) cwd = process.cwd();
|
|
350
|
+
|
|
351
|
+
const sections: string[] = [];
|
|
352
|
+
|
|
353
|
+
sections.push(await buildSessionSummarySection(sessionId));
|
|
354
|
+
sections.push(buildRecentFilesSection(allRecords, sessionId, cwd));
|
|
355
|
+
sections.push(buildGenomeSection(cwd));
|
|
356
|
+
sections.push(buildOpenTodosSection(allRecords, sessionId));
|
|
357
|
+
|
|
358
|
+
const filename = makeFilename(now, opts.randSuffix);
|
|
359
|
+
const outPath = join(outDir, filename);
|
|
360
|
+
|
|
361
|
+
const footerNote =
|
|
362
|
+
`---\n` +
|
|
363
|
+
`Paste the contents of ${outPath} into your next session to resume context.`;
|
|
364
|
+
|
|
365
|
+
const content = [
|
|
366
|
+
`# ashlr handoff — ${new Date(now).toISOString()}`,
|
|
367
|
+
"",
|
|
368
|
+
...sections.map((s) => s + "\n"),
|
|
369
|
+
footerNote,
|
|
370
|
+
].join("\n");
|
|
371
|
+
|
|
372
|
+
if (!opts.dryRun) {
|
|
373
|
+
mkdirSync(outDir, { recursive: true });
|
|
374
|
+
writeFileSync(outPath, content, "utf-8");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return { path: outPath, content };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Return the most recent existing handoff file, or null. */
|
|
381
|
+
export function findLastHandoff(outDir: string): string | null {
|
|
382
|
+
try {
|
|
383
|
+
if (!existsSync(outDir)) return null;
|
|
384
|
+
const files = readdirSync(outDir)
|
|
385
|
+
.filter((f) => f.endsWith(".md"))
|
|
386
|
+
.sort() // lexicographic == chronological for YYYY-MM-DD-HHMMSS prefix
|
|
387
|
+
.reverse();
|
|
388
|
+
return files.length > 0 ? join(outDir, files[0]!) : null;
|
|
389
|
+
} catch {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ---------------------------------------------------------------------------
|
|
395
|
+
// CLI entry
|
|
396
|
+
// ---------------------------------------------------------------------------
|
|
397
|
+
|
|
398
|
+
if (import.meta.main) {
|
|
399
|
+
const args = process.argv.slice(2);
|
|
400
|
+
|
|
401
|
+
const getFlag = (flag: string): string | undefined => {
|
|
402
|
+
const idx = args.indexOf(flag);
|
|
403
|
+
return idx !== -1 ? args[idx + 1] : undefined;
|
|
404
|
+
};
|
|
405
|
+
const hasFlag = (flag: string): boolean => args.includes(flag);
|
|
406
|
+
|
|
407
|
+
const sessionFlag = getFlag("--session");
|
|
408
|
+
const dirFlag = getFlag("--dir");
|
|
409
|
+
const lastFlag = hasFlag("--last");
|
|
410
|
+
|
|
411
|
+
const homeDir = homedir();
|
|
412
|
+
const outDir = dirFlag ?? join(homeDir, ".ashlr", "handoffs");
|
|
413
|
+
|
|
414
|
+
if (lastFlag) {
|
|
415
|
+
const last = findLastHandoff(outDir);
|
|
416
|
+
if (!last) {
|
|
417
|
+
process.stdout.write("(no handoffs yet)\n");
|
|
418
|
+
} else {
|
|
419
|
+
const preview = readFileSync(last, "utf-8").split("\n").slice(0, 5).join("\n");
|
|
420
|
+
process.stdout.write(`Last handoff: ${last}\n\n${preview}\n...\n`);
|
|
421
|
+
}
|
|
422
|
+
process.exit(0);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
buildHandoffPack({
|
|
426
|
+
sessionId: sessionFlag,
|
|
427
|
+
outDir,
|
|
428
|
+
})
|
|
429
|
+
.then(({ path, content }) => {
|
|
430
|
+
const preview = content.split("\n").slice(0, 5).join("\n");
|
|
431
|
+
process.stdout.write(`Handoff written: ${path}\n\n${preview}\n...\n`);
|
|
432
|
+
})
|
|
433
|
+
.catch((err) => {
|
|
434
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
435
|
+
process.stdout.write(`handoff-pack failed: ${msg}\n`);
|
|
436
|
+
})
|
|
437
|
+
.finally(() => process.exit(0));
|
|
438
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* hook-bootstrap.mjs — node trampoline for every hook in hooks/hooks.json.
|
|
4
|
+
*
|
|
5
|
+
* Mirror of scripts/bootstrap.mjs but for the Claude Code hooks surface.
|
|
6
|
+
* Claude Code spawns hook commands with its *own* parent-process PATH, which
|
|
7
|
+
* doesn't include ~/.bun/bin right after a first-run auto-install. Without
|
|
8
|
+
* this trampoline, every hook silently dies with "bun: not found" until the
|
|
9
|
+
* user restarts Claude Code.
|
|
10
|
+
*
|
|
11
|
+
* Flow:
|
|
12
|
+
* 1. If bun is already on PATH: spawn it directly.
|
|
13
|
+
* 2. Otherwise, try prepending ~/.bun/bin (populated by a prior
|
|
14
|
+
* bootstrap.mjs auto-install from the MCP server spawn).
|
|
15
|
+
* 3. If bun is STILL unreachable: exit 0 silently. Hooks are decoration,
|
|
16
|
+
* never a gate on the harness — a missing bun here just means the hook
|
|
17
|
+
* no-ops for this session, not that the user's workflow breaks.
|
|
18
|
+
*
|
|
19
|
+
* Critical: this trampoline NEVER auto-installs bun. Hooks fire concurrently
|
|
20
|
+
* (multiple PreToolUse + PostToolUse in parallel); racing installers would
|
|
21
|
+
* be catastrophic. Only scripts/bootstrap.mjs (MCP server spawn — single
|
|
22
|
+
* producer) is allowed to trigger the installer.
|
|
23
|
+
*
|
|
24
|
+
* ASHLR_NO_AUTO_INSTALL only gates *installation*; PATH *resolution* always
|
|
25
|
+
* runs — we never refuse to look at a bin dir the user already has on disk.
|
|
26
|
+
*
|
|
27
|
+
* Usage (wired from hooks/hooks.json):
|
|
28
|
+
* node "${CODEX_PLUGIN_ROOT}/scripts/hook-bootstrap.mjs" \
|
|
29
|
+
* "${CODEX_PLUGIN_ROOT}/hooks/<hook>.ts" [args...]
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { spawnSync } from "node:child_process";
|
|
33
|
+
import { hasBun, prependBunToPath } from "./bun-resolve.mjs";
|
|
34
|
+
|
|
35
|
+
const hookPath = process.argv[2];
|
|
36
|
+
if (!hookPath) {
|
|
37
|
+
process.stderr.write("[ashlr:hook-bootstrap] missing hook path argument\n");
|
|
38
|
+
process.exit(0); // still don't gate the harness
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const looksLikeCodexPluginHook =
|
|
42
|
+
Boolean(process.env.CODEX_PLUGIN_ROOT) ||
|
|
43
|
+
Boolean(process.env.ASHLR_CODEX_HOOK_MODE) ||
|
|
44
|
+
(Boolean(process.env.CLAUDE_PLUGIN_ROOT) &&
|
|
45
|
+
!process.env.CLAUDE_SESSION_ID &&
|
|
46
|
+
!process.env.CLAUDE_CODE_MODEL);
|
|
47
|
+
|
|
48
|
+
if (looksLikeCodexPluginHook && !process.env.ASHLR_MCP_HOST) {
|
|
49
|
+
process.env.ASHLR_MCP_HOST = "codex-cli";
|
|
50
|
+
}
|
|
51
|
+
if (process.env.ASHLR_MCP_HOST === "codex-cli" && !process.env.ASHLR_HOOK_MODE) {
|
|
52
|
+
process.env.ASHLR_HOOK_MODE = process.env.ASHLR_CODEX_HOOK_MODE || "nudge";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!hasBun()) {
|
|
56
|
+
prependBunToPath();
|
|
57
|
+
if (!hasBun()) {
|
|
58
|
+
// Bun genuinely unreachable. Exit 0 — hooks must never fail the harness.
|
|
59
|
+
// The MCP server's bootstrap.mjs will install bun on its next spawn if it
|
|
60
|
+
// hasn't already; this hook simply no-ops for now.
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const forwarded = process.argv.slice(3);
|
|
66
|
+
const result = spawnSync("bun", ["run", hookPath, ...forwarded], {
|
|
67
|
+
stdio: "inherit",
|
|
68
|
+
env: process.env,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (result.error) {
|
|
72
|
+
process.stderr.write(`[ashlr:hook-bootstrap] ${result.error.message}\n`);
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
process.exit(result.status ?? 0);
|