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,523 @@
|
|
|
1
|
+
# ashlr Pro Backend Architecture
|
|
2
|
+
|
|
3
|
+
_Target: Ship Phase 1 in 12 weeks. TypeScript + Bun + Postgres + Hono._
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Overview
|
|
8
|
+
|
|
9
|
+
The ashlr pro-tier backend solves three structural problems the self-hosted plugin cannot: (1) team genome sync with vclock-based last-write-wins and E2E encryption (with manual conflict resolution via `/ashlr-genome-conflicts`; CRDT auto-merge is a v2 evolution), (2) cross-device stats aggregation and public leaderboards, and (3) managed LLM inference without local hardware. The backend is a constellation of stateless HTTP services (one per concern) backed by Postgres, S3, and Redis. Each service owns its API surface and data; clients gate pro calls on `ASHLR_PRO_TOKEN` presence.
|
|
10
|
+
|
|
11
|
+
**Architecture**: Request → ALB → Bun app (stateless, auto-scales) → Postgres (relational + append-only logs) / S3 (section bodies, policy YAML) / Redis (rate limits, session cache). Clients use `ASHLR_API_URL` to override the default `https://api.ashlr.ai`.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Stack Choices
|
|
16
|
+
|
|
17
|
+
**Language: TypeScript (Hono).**
|
|
18
|
+
- Shares types with the plugin; avoids .proto/.graphql overhead.
|
|
19
|
+
- Hono is minimal (6KB), fast, and works on Bun, Node, Cloudflare Workers, Deno—future-proofs library portability.
|
|
20
|
+
|
|
21
|
+
**Runtime: Bun 1.1+.**
|
|
22
|
+
- Native TypeScript execution avoids build-step friction during development.
|
|
23
|
+
- Startup time <50ms; HTTP perf competitive with Deno.
|
|
24
|
+
- Plugin already uses Bun; aligns team tooling.
|
|
25
|
+
- Node.js 22 acceptable fallback if Bun stability concerns arise later.
|
|
26
|
+
|
|
27
|
+
**Framework: Hono.**
|
|
28
|
+
- Lightweight, portable, zero-magic middleware.
|
|
29
|
+
- Same mental model as the plugin's existing HTTP layer.
|
|
30
|
+
- Easy rate-limit + auth middleware composition.
|
|
31
|
+
|
|
32
|
+
**Database: Postgres (primary) + S3/R2 (blobs) + Redis (cache/limits).**
|
|
33
|
+
- Postgres handles relational metadata (users, orgs, genome sections, stats, audit logs).
|
|
34
|
+
- S3/Cloudflare R2 stores section bodies (~10–50KB each); cheaper than Postgres for large blobs, simpler cache busting.
|
|
35
|
+
- Redis for rate limits, session cache, leaderboard pre-compute.
|
|
36
|
+
|
|
37
|
+
**Auth: Clerk.**
|
|
38
|
+
- Native JWT support (client-side validation of `ASHLR_PRO_TOKEN`).
|
|
39
|
+
- Built-in passwordless + social; low ops overhead.
|
|
40
|
+
- Pricing aligns with usage (pay-per-auth, not per-seat); fits lean startup model.
|
|
41
|
+
|
|
42
|
+
**Hosting: Railway + Neon.**
|
|
43
|
+
- Railway abstracts container + secret management; plug in Neon Postgres, auto-scales.
|
|
44
|
+
- Neon's serverless Postgres is cost-efficient for bursty stats-sync workloads.
|
|
45
|
+
- S3 is S3 (AWS, Wasabi, or Backblaze B2 are equivalent; pick by region/cost).
|
|
46
|
+
|
|
47
|
+
**Payments: Stripe.**
|
|
48
|
+
- Checkout for one-time + recurring subscriptions.
|
|
49
|
+
- Billing Portal for self-serve upgrades, cancel.
|
|
50
|
+
- Webhooks for trial expiry, dunning, churn.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. API Surface
|
|
55
|
+
|
|
56
|
+
Each endpoint requires `Authorization: Bearer <ASHLR_PRO_TOKEN>` header. Rate limits applied via Redis + sliding-window count-and-sleep.
|
|
57
|
+
|
|
58
|
+
### 3.1 Genome Sync
|
|
59
|
+
|
|
60
|
+
Clients sync `.ashlrcode/genome/` sections and receive merged diffs.
|
|
61
|
+
|
|
62
|
+
- **POST /genome/sync** — Upload section mutations and fetch latest merged state.
|
|
63
|
+
- Body: `{ org_id, user_id, device_id, sections: [{ name, version, hash, body }] }`
|
|
64
|
+
- Response: `{ merged: [{ name, version, body, merged_by, timestamp }], your_changes_applied: bool }`
|
|
65
|
+
- Rate limit: 10/min per user.
|
|
66
|
+
- Notes: Uses vector clocks for conflict detection; `merged_by` field reveals which peer won the merge.
|
|
67
|
+
|
|
68
|
+
- **GET /genome/:org_id/:section_name** — Poll for latest section (long-poll, 30s timeout).
|
|
69
|
+
- Response: `{ version, hash, body, updated_by_user_id, timestamp }`
|
|
70
|
+
- Rate limit: 30/min per user (cheap).
|
|
71
|
+
|
|
72
|
+
- **POST /genome/propose** — Queue a proposal without merging (for async review).
|
|
73
|
+
- Body: `{ org_id, user_id, section_name, body, rationale }`
|
|
74
|
+
- Response: `{ proposal_id, queued_at }`
|
|
75
|
+
- Rate limit: 5/min per user.
|
|
76
|
+
|
|
77
|
+
### 3.2 Cross-Machine Stats Sync
|
|
78
|
+
|
|
79
|
+
Upload periodic snapshots; server aggregates and publishes leaderboard + badges.
|
|
80
|
+
|
|
81
|
+
- **POST /stats/upload** — Client uploads local `~/.ashlr/stats.json` snapshot (opt-in).
|
|
82
|
+
- Body: `{ user_id, machine_id, timestamp, stats: { tools: { [tool_name]: { calls, tokens_saved } }, sessions: int } }`
|
|
83
|
+
- Response: `{ received_at, next_sync_in_ms }`
|
|
84
|
+
- Rate limit: 1/hour per machine.
|
|
85
|
+
- Privacy: No paths, cwds, or file contents; only tool counts and token sums.
|
|
86
|
+
|
|
87
|
+
- **GET /stats/user/:user_id** — User's own aggregated stats (7d, 30d, all-time).
|
|
88
|
+
- Response: `{ user_id, machines: int, lifetime_tokens_saved, rank_percentile, badges }`
|
|
89
|
+
- Rate limit: 60/min per user.
|
|
90
|
+
|
|
91
|
+
- **GET /stats/leaderboard?limit=100&period=7d** — Public anonymous leaderboard.
|
|
92
|
+
- Response: `[{ rank, tokens_saved, is_you: bool }]` (user_id hashed; ties broken by earliest timestamp).
|
|
93
|
+
- Rate limit: 60/min (global).
|
|
94
|
+
|
|
95
|
+
- **POST /badge/refresh** — Trigger manual badge SVG update (normally auto-triggered on stats/upload).
|
|
96
|
+
- Response: `{ badge_url, next_refresh_in_ms }`
|
|
97
|
+
- Rate limit: 5/min per user.
|
|
98
|
+
|
|
99
|
+
### 3.3 Cloud LLM Summarizer
|
|
100
|
+
|
|
101
|
+
Stateless wrapper around inference endpoints.
|
|
102
|
+
|
|
103
|
+
- **POST /llm/summarize** — Summarize a code diff, log tail, or arbitrary text.
|
|
104
|
+
- Body: `{ user_id, model: "haiku" | "gemini-flash" | "local", prompt, context, max_tokens }`
|
|
105
|
+
- Response: `{ text, model_used, tokens_in, tokens_out, cached: bool }`
|
|
106
|
+
- Rate limit: 100/hour per user (based on model choice + token cost).
|
|
107
|
+
- Notes: Routes to Anthropic Haiku → Gemini Flash → local Ollama (fallback), based on availability + user tier.
|
|
108
|
+
|
|
109
|
+
### 3.4 Hosted Badge
|
|
110
|
+
|
|
111
|
+
Auto-updating SVG at `https://ashlr.ai/u/<user>/badge.svg`.
|
|
112
|
+
|
|
113
|
+
- **GET /u/:user_hash/badge.svg** — CDN-cached SVG (5-min TTL) showing tokens saved + rank.
|
|
114
|
+
- Response: SVG with lifetime tokens saved, rank percentile, updated timestamp.
|
|
115
|
+
- Rate limit: none (static content, CDN-served).
|
|
116
|
+
|
|
117
|
+
### 3.5 Policy Packs
|
|
118
|
+
|
|
119
|
+
Teams upload allow/deny lists; server pushes to members via polling endpoint.
|
|
120
|
+
|
|
121
|
+
- **POST /policies/upload** — Org admin uploads YAML policy pack.
|
|
122
|
+
- Body: `{ org_id, admin_id, policies: { allow_tools: [...], deny_paths: [...], enforce_genome_review: bool } }`
|
|
123
|
+
- Response: `{ policy_id, version, members_notified_at }`
|
|
124
|
+
- Rate limit: 5/day per org.
|
|
125
|
+
|
|
126
|
+
- **GET /policies/for-org/:org_id** — Fetch latest policy pack for org.
|
|
127
|
+
- Response: `{ policy_id, version, policies: {...}, updated_by, timestamp }`
|
|
128
|
+
- Rate limit: 20/min per user.
|
|
129
|
+
|
|
130
|
+
### 3.6 Audit Log
|
|
131
|
+
|
|
132
|
+
Immutable append-only log of non-read ashlr tool invocations.
|
|
133
|
+
|
|
134
|
+
- **POST /audit/log** — Append a tool invocation event (client-initiated; optional).
|
|
135
|
+
- Body: `{ user_id, org_id, tool_name, args_hash, git_sha, status: "ok" | "error", tokens }`
|
|
136
|
+
- Response: `{ event_id, committed_at }`
|
|
137
|
+
- Rate limit: none (dropped if burst > 100/sec; client-side buffering ok).
|
|
138
|
+
|
|
139
|
+
- **GET /audit/org/:org_id** — Org admin exports audit log (CSV, JSON, parquet).
|
|
140
|
+
- Query: `?start_date=ISO&end_date=ISO&tool_filter=grep&format=json`
|
|
141
|
+
- Response: `{ events: [...], total_tokens, date_range }`
|
|
142
|
+
- Rate limit: 5/hour per org (export is expensive).
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 4. Data Model
|
|
147
|
+
|
|
148
|
+
Core Postgres tables (DDL):
|
|
149
|
+
|
|
150
|
+
```sql
|
|
151
|
+
CREATE TABLE users (
|
|
152
|
+
id UUID PRIMARY KEY,
|
|
153
|
+
clerk_id TEXT UNIQUE NOT NULL,
|
|
154
|
+
email TEXT UNIQUE NOT NULL,
|
|
155
|
+
pro_tier TEXT DEFAULT 'free', -- 'free' | 'team' | 'enterprise'
|
|
156
|
+
stripe_customer_id TEXT UNIQUE,
|
|
157
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
158
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
CREATE TABLE orgs (
|
|
162
|
+
id UUID PRIMARY KEY,
|
|
163
|
+
owner_id UUID NOT NULL REFERENCES users(id),
|
|
164
|
+
name TEXT NOT NULL UNIQUE,
|
|
165
|
+
stripe_subscription_id TEXT UNIQUE,
|
|
166
|
+
tier TEXT DEFAULT 'team',
|
|
167
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
CREATE TABLE memberships (
|
|
171
|
+
id UUID PRIMARY KEY,
|
|
172
|
+
org_id UUID NOT NULL REFERENCES orgs(id),
|
|
173
|
+
user_id UUID NOT NULL REFERENCES users(id),
|
|
174
|
+
role TEXT DEFAULT 'member', -- 'owner' | 'admin' | 'member'
|
|
175
|
+
invited_by UUID REFERENCES users(id),
|
|
176
|
+
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
|
177
|
+
UNIQUE(org_id, user_id)
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
CREATE TABLE genome_sections (
|
|
181
|
+
id UUID PRIMARY KEY,
|
|
182
|
+
org_id UUID NOT NULL REFERENCES orgs(id),
|
|
183
|
+
section_name TEXT NOT NULL,
|
|
184
|
+
version BIGINT DEFAULT 0,
|
|
185
|
+
body_s3_key TEXT NOT NULL,
|
|
186
|
+
hash TEXT NOT NULL,
|
|
187
|
+
merged_by_user_id UUID REFERENCES users(id),
|
|
188
|
+
merged_at TIMESTAMPTZ DEFAULT NOW(),
|
|
189
|
+
UNIQUE(org_id, section_name)
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
CREATE TABLE genome_versions (
|
|
193
|
+
id UUID PRIMARY KEY,
|
|
194
|
+
section_id UUID NOT NULL REFERENCES genome_sections(id),
|
|
195
|
+
version BIGINT NOT NULL,
|
|
196
|
+
author_user_id UUID NOT NULL REFERENCES users(id),
|
|
197
|
+
author_device_id TEXT NOT NULL,
|
|
198
|
+
body_s3_key TEXT NOT NULL,
|
|
199
|
+
vector_clock JSONB NOT NULL, -- { [device_id]: version_num, ... }
|
|
200
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
201
|
+
UNIQUE(section_id, version)
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
CREATE TABLE stats_uploads (
|
|
205
|
+
id UUID PRIMARY KEY,
|
|
206
|
+
user_id UUID NOT NULL REFERENCES users(id),
|
|
207
|
+
machine_id TEXT NOT NULL,
|
|
208
|
+
tool_stats JSONB NOT NULL, -- { 'tool_name': { calls: int, tokens_saved: int }, ... }
|
|
209
|
+
session_count INT DEFAULT 0,
|
|
210
|
+
uploaded_at TIMESTAMPTZ DEFAULT NOW(),
|
|
211
|
+
UNIQUE(user_id, machine_id, uploaded_at)
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
CREATE TABLE policy_packs (
|
|
215
|
+
id UUID PRIMARY KEY,
|
|
216
|
+
org_id UUID NOT NULL REFERENCES orgs(id),
|
|
217
|
+
admin_id UUID NOT NULL REFERENCES users(id),
|
|
218
|
+
policies JSONB NOT NULL, -- { allow_tools: [...], deny_paths: [...], ... }
|
|
219
|
+
version BIGINT DEFAULT 1,
|
|
220
|
+
published_at TIMESTAMPTZ DEFAULT NOW()
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
CREATE TABLE audit_events (
|
|
224
|
+
id UUID PRIMARY KEY,
|
|
225
|
+
org_id UUID NOT NULL REFERENCES orgs(id),
|
|
226
|
+
user_id UUID NOT NULL REFERENCES users(id),
|
|
227
|
+
tool_name TEXT NOT NULL,
|
|
228
|
+
args_hash TEXT NOT NULL,
|
|
229
|
+
git_sha TEXT,
|
|
230
|
+
status TEXT DEFAULT 'ok',
|
|
231
|
+
tokens INT DEFAULT 0,
|
|
232
|
+
logged_at TIMESTAMPTZ DEFAULT NOW()
|
|
233
|
+
);
|
|
234
|
+
-- WORM: append-only, no deletes. Partition by month for retention.
|
|
235
|
+
|
|
236
|
+
CREATE TABLE badges (
|
|
237
|
+
id UUID PRIMARY KEY,
|
|
238
|
+
user_id UUID NOT NULL REFERENCES users(id) UNIQUE,
|
|
239
|
+
tokens_saved_lifetime INT DEFAULT 0,
|
|
240
|
+
rank_percentile FLOAT DEFAULT 0.0,
|
|
241
|
+
badge_html TEXT NOT NULL,
|
|
242
|
+
generated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
243
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
CREATE INDEX idx_stats_uploads_user_ts ON stats_uploads(user_id, uploaded_at DESC);
|
|
247
|
+
CREATE INDEX idx_audit_events_org_ts ON audit_events(org_id, logged_at DESC);
|
|
248
|
+
CREATE INDEX idx_genome_sections_org ON genome_sections(org_id);
|
|
249
|
+
CREATE INDEX idx_memberships_org ON memberships(org_id);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 5. Client Integration
|
|
255
|
+
|
|
256
|
+
The plugin uses three new environment variables:
|
|
257
|
+
|
|
258
|
+
- `ASHLR_PRO_TOKEN` — Clerk JWT token. If absent, all pro features are disabled.
|
|
259
|
+
- `ASHLR_API_URL` — Optional override; defaults to `https://api.ashlr.ai`.
|
|
260
|
+
- `ASHLR_STATS_OPT_IN` — Boolean; controls whether stats are uploaded (default false, explicit opt-in required).
|
|
261
|
+
|
|
262
|
+
**Gate logic** (in plugin's HTTP client):
|
|
263
|
+
```typescript
|
|
264
|
+
if (!process.env.ASHLR_PRO_TOKEN) {
|
|
265
|
+
// Fall back to local-only mode: no genome sync, no stats, no cloud LLM.
|
|
266
|
+
return fallbackToLocal();
|
|
267
|
+
}
|
|
268
|
+
const baseURL = process.env.ASHLR_API_URL || 'https://api.ashlr.ai';
|
|
269
|
+
const headers = { Authorization: `Bearer ${process.env.ASHLR_PRO_TOKEN}` };
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Genome sync integration**: Before consolidating local proposals, call `POST /genome/sync` with pending sections. Merge remote changes into the local genome before applying hooks. If sync fails (auth or network), warn and fall back to local-only merge.
|
|
273
|
+
|
|
274
|
+
**Stats sync integration**: Background task (1/hour, opt-in) uploads snapshot of `~/.ashlr/stats.json` to `POST /stats/upload`. On success, update `stats.json` with `last_sync_at`. No blocking; failures are logged to `~/.ashlr/errors.jsonl` but don't halt the CLI.
|
|
275
|
+
|
|
276
|
+
**Cloud LLM integration**: In `_summarize.ts`, detect `ASHLR_PRO_TOKEN` presence. If set, call `POST /llm/summarize` instead of spawning local Ollama. Fallback to local if cloud call fails or returns 429.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 6. Privacy + Security
|
|
281
|
+
|
|
282
|
+
**Genome sync**: Sections are encrypted client-side (AES-256-GCM) using a user-held key before upload. Server stores encrypted bodies in S3. Only the org members who hold the key can decrypt. No key material ever sent to the server; the `hash` field lets the server detect replays and version skew without decrypting.
|
|
283
|
+
|
|
284
|
+
**Stats sync**:
|
|
285
|
+
- Never log paths, cwds, git repos, or filenames.
|
|
286
|
+
- Only upload tool names + call counts + token totals.
|
|
287
|
+
- User_id is visible to the user's own account; on leaderboard, user_id is hashed (SHA256) and never tied to email.
|
|
288
|
+
- Machines are tracked by `machine_id` (UUID generated at first sync); supports multi-device use without tracking physical identity.
|
|
289
|
+
|
|
290
|
+
**Audit log**:
|
|
291
|
+
- Append-only (no deletes, no updates). Dropped events are logged separately.
|
|
292
|
+
- Per-org isolation; only org members with `admin` role can export.
|
|
293
|
+
- Git SHA linkage lets auditors trace a tool invocation to a commit.
|
|
294
|
+
- Exports are available in JSON, CSV, and Parquet (via local DuckDB on demand).
|
|
295
|
+
|
|
296
|
+
**PII**:
|
|
297
|
+
- No PII collected except email (Clerk-managed, used only for Stripe invoicing and password reset).
|
|
298
|
+
- Stripe invoices are generated server-side; customer never sees an API key.
|
|
299
|
+
- Session logs and stats uploads are anonymous by design.
|
|
300
|
+
|
|
301
|
+
**Threat model**:
|
|
302
|
+
- **SSRF**: Cloud LLM endpoint is hardcoded; no user-supplied URLs. Proxy runs in a VPC with egress filtering.
|
|
303
|
+
- **SQL injection**: Hono + standard parameterized queries (no string interpolation).
|
|
304
|
+
- **Tenant isolation**: Org-based queries always include `org_id` filter; no cross-org data leakage.
|
|
305
|
+
- **Rate limits**: Redis sliding-window counter; 429 on limit exceeded.
|
|
306
|
+
- **Replay attacks**: Vector clocks + hashes prevent genome version replays. Nonce in stats uploads prevents duplicate-count inflation.
|
|
307
|
+
- **Credential disclosure**: `ASHLR_PRO_TOKEN` is a short-lived Clerk JWT (15 min TTL); client auto-refreshes. No long-lived API keys.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 7. Cost Model
|
|
312
|
+
|
|
313
|
+
**Assumptions**: 100 / 1K / 10K active users, average usage 50 tool calls/session, 10 sessions/month, 10KB avg genome section size.
|
|
314
|
+
|
|
315
|
+
**Per-user infra cost**:
|
|
316
|
+
| Layer | 100 users | 1K users | 10K users |
|
|
317
|
+
|-------|-----------|----------|-----------|
|
|
318
|
+
| Postgres (Neon, serverless) | ~$0.50 | ~$5 | ~$30 |
|
|
319
|
+
| S3 (genome bodies, stats backups) | ~$0.10 | ~$1 | ~$8 |
|
|
320
|
+
| Redis (rate limits, cache) | $20/mo shared | $20/mo shared | $50/mo |
|
|
321
|
+
| Cloud LLM (Haiku, 50 summaries/user/mo @ $0.80/1M tokens) | ~$0.20 | ~$2 | ~$20 |
|
|
322
|
+
| Bandwidth / misc | ~$0.20 | ~$2 | ~$15 |
|
|
323
|
+
| **Total / user** | ~$0.81/user | ~$0.10/user | ~$0.012/user |
|
|
324
|
+
|
|
325
|
+
**Team tier pricing** (20–500 user seats): $500–2K/month flat-rate + $5/seat for seats over 20. At 100 users, profit margin ~40%.
|
|
326
|
+
|
|
327
|
+
**Enterprise tier**: Custom metering (audit log export, policy enforcement, SSO). Assumes 3–5 large customers at $10K–50K/year each.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## 8. Build Phases
|
|
332
|
+
|
|
333
|
+
**Phase 1: Badge service + stats sync (1 week).**
|
|
334
|
+
- MVP with `POST /stats/upload`, `GET /stats/user`, `GET /stats/leaderboard`.
|
|
335
|
+
- Auto-refreshing badge SVG in Neon + Redis.
|
|
336
|
+
- Plugin integration: background stats sync every 1 hour.
|
|
337
|
+
- Risk: Low. Validation: public leaderboard has >50 users; badge renders on plugin landing page.
|
|
338
|
+
|
|
339
|
+
**Phase 2: Cloud LLM summarizer (1 week).**
|
|
340
|
+
- Routing wrapper around Haiku, Gemini Flash, local Ollama.
|
|
341
|
+
- Rate limiting per token cost (not per call).
|
|
342
|
+
- Risk: Vendor API changes. Validation: 99% uptime SLO; fallback to local Ollama works.
|
|
343
|
+
|
|
344
|
+
**Phase 3: Genome sync with vclock LWW (2 weeks).**
|
|
345
|
+
- S3 storage for section bodies.
|
|
346
|
+
- Vector clock comparison; LWW when clocks are comparable, conflict-pair recording when they're not.
|
|
347
|
+
- Manual conflict resolution surfaced via `/ashlr-genome-conflicts` (CRDT auto-merge is deferred to v2).
|
|
348
|
+
- Client encryption (AES-256-GCM).
|
|
349
|
+
- Risk: Medium (vclock edge cases, key rotation). Validation: Team of 5 syncs genome across 10 devices; LWW applies cleanly when clocks dominate, concurrent edits surface as conflicts and resolve via the CLI.
|
|
350
|
+
|
|
351
|
+
**Phase 4: Policy packs + audit log (1 week).**
|
|
352
|
+
- YAML parsing, WORM storage.
|
|
353
|
+
- Org admin export.
|
|
354
|
+
- Risk: Moderate (WORM constraints, retention). Validation: Export audit log; verify no data loss across 1-month run.
|
|
355
|
+
|
|
356
|
+
**Phase 5: Leaderboard UI + badge embeds (3 days).**
|
|
357
|
+
- Static HTML page at `ashlr.ai/leaderboard`.
|
|
358
|
+
- Markdown embed snippet for GitHub profiles, blogs.
|
|
359
|
+
- Risk: Low. Validation: Leaderboard loads in <1s; 100K requests/day without CDN tuning.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## 9. Open Questions
|
|
364
|
+
|
|
365
|
+
1. **Genome sync encryption**: Client-side AES-256-GCM vs. server-side with customer-managed keys (CMK in AWS KMS)? CMK is more durable but adds latency and ops complexity.
|
|
366
|
+
|
|
367
|
+
2. **Stats retention**: Keep all stats uploads forever (cost ~$1/10K users/month in Postgres)? Archive to cold storage (Glacier) after 1 year? Or aggregate daily and drop raw data?
|
|
368
|
+
|
|
369
|
+
3. **Audit log SLA**: Is append-only WORM required for regulatory (SOC 2)? If yes, adds replication/backup burden. If optional, simpler to design.
|
|
370
|
+
|
|
371
|
+
4. **LLM fallback**: What happens if Haiku quota is exhausted and Gemini is also down? Serve a cached summary from Redis, or fail the request?
|
|
372
|
+
|
|
373
|
+
5. **Cross-org genome**: Can orgs share genome sections (e.g., public reference docs)? Adds multi-tenancy complexity; deferred to Phase 3.1.
|
|
374
|
+
|
|
375
|
+
6. **Policy pack versioning**: When an org updates policies, do existing jobs (e.g., autonomous agents) re-run under the new policy? Or only new jobs? Needs clarification from product.
|
|
376
|
+
|
|
377
|
+
7. **Badge CSS**: SVG vs. HTML+CSS vs. Markdown embed with image tag? SVG is simplest; Markdown link is most portable.
|
|
378
|
+
|
|
379
|
+
8. **Free tier rate limits**: Do free-tier users get stats sync at all? Current design assumes opt-in pro token, so free tier is local-only by default.
|
|
380
|
+
|
|
381
|
+
9. **Stripe dunning**: After trial expiry or payment failure, what's the grace period before genome/stats sync is disabled? 7 days? 14 days?
|
|
382
|
+
|
|
383
|
+
10. **Multitenancy overhead**: One Postgres instance per org, or one instance with `org_id` sharding? Shared instance is cheaper; per-org is simpler compliance. Recommend shared for Phase 1–2, re-evaluate at Phase 4.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 10. Hardest Architectural Decisions
|
|
388
|
+
|
|
389
|
+
### Decision 1: Shared Postgres vs. Per-Org Instance
|
|
390
|
+
**Options**: (A) One Postgres with `org_id` sharding, (B) One instance per org, (C) Postgres for metadata, per-org blob-store silo.
|
|
391
|
+
|
|
392
|
+
**Recommendation**: (A) — Shared Postgres with `org_id` index on every table.
|
|
393
|
+
|
|
394
|
+
**Rationale**:
|
|
395
|
+
- At 100–1K users, per-org instances would cost 3–10x more.
|
|
396
|
+
- Shared instance is sufficient for row-level security; Postgres policies can enforce `org_id` checks at the query level.
|
|
397
|
+
- Migration to (B) later is tractable (shard by org_id at application layer, then split at DB layer).
|
|
398
|
+
- Georeplication is easier on one instance; hot-standby failover is automatic with Neon.
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
### Decision 2: CRDT vs. Last-Write-Wins for Genome Merge
|
|
403
|
+
**Options**: (A) Vector clocks + CRDT auto-merge algorithm (e.g., YATA / Yjs), (B) Vector clocks + last-write-wins with user-visible conflict resolution, (C) Server-authoritative with client lock.
|
|
404
|
+
|
|
405
|
+
**Recommendation**: (B) for v1 — vclock-based LWW with concurrent edits surfaced as conflict pairs and resolved manually via `/ashlr-genome-conflicts`. CRDT auto-merge (A) is on the v2 roadmap.
|
|
406
|
+
|
|
407
|
+
**Rationale**:
|
|
408
|
+
- Genome is semi-structured (JSON sections), and most of those sections are short, infrequently-edited natural-language documents — concurrent edits are rare in practice. A simple vclock + LWW path with a CLI for the rare conflict ships in weeks; a CRDT (A) is a multi-month engineering investment with a hard correctness bar.
|
|
409
|
+
- Vclock comparison gives us the same correctness primitive as a CRDT for the dominating-clock case (the common case); only the incomparable-clocks case differs in handling.
|
|
410
|
+
- LWW (B) avoids CRDT-implementation bug risk and is much easier to audit, especially in combination with E2E encryption (the server only sees opaque envelopes).
|
|
411
|
+
- Server-lock (C) would create a leader bottleneck and defeat the purpose of decentralized team editing; rejected.
|
|
412
|
+
- Risk: users see conflicts when they edit concurrently. Mitigation: section-level granularity is already coarse, so most concurrent edits don't actually overlap; when they do, the CLI resolution is one prompt.
|
|
413
|
+
- v2 evolution: when usage data justifies it, replace LWW with a true CRDT auto-merge (Yjs or hand-rolled per-section operations) and keep the conflict CLI as a fallback for unresolvable cases.
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
### Decision 3: Client-Side Encryption vs. Server-Side with CMK
|
|
418
|
+
**Options**: (A) AES-256-GCM, client holds key (no key escrow), (B) AES-256-GCM, server-side key rotation via AWS CMK + audit log, (C) TLS-in-transit only (no rest encryption).
|
|
419
|
+
|
|
420
|
+
**Recommendation**: (A) — Client-side AES-256-GCM, key derived from Clerk JWT + org_id.
|
|
421
|
+
|
|
422
|
+
**Rationale**:
|
|
423
|
+
- Simpler to audit: no server-side key material, no key rotation ceremony.
|
|
424
|
+
- Clerk JWT is already encrypted; using it as a key-derivation input avoids a new secret store.
|
|
425
|
+
- Server never decrypts sections; only hashes are visible (enables dedup, version detection).
|
|
426
|
+
- Trade-off: If user loses Clerk JWT, they lose access to encrypted sections. Mitigate with recovery code + offline backup.
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
### Decision 4: Append-Only Audit Log vs. Aggregated Events
|
|
431
|
+
**Options**: (A) WORM append-only (each tool call = one row; immutable), (B) Daily aggregates (tokens + tool counts, mutable), (C) Hybrid (last 30 days append-only, older data aggregated).
|
|
432
|
+
|
|
433
|
+
**Recommendation**: (A) — Full WORM append-only; add retention policy (delete rows older than 7 years).
|
|
434
|
+
|
|
435
|
+
**Rationale**:
|
|
436
|
+
- Audit log is a compliance artifact; WORM is the standard.
|
|
437
|
+
- Aggregates hide information (e.g., which user ran which tool at what time); defeats compliance value.
|
|
438
|
+
- Cost is acceptable (one row per tool invocation; 10–100K rows/day for 1K users).
|
|
439
|
+
- Partition by month and archive old partitions to cold storage (Glacier) for retention.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
### Decision 5: Polling vs. WebSocket for Genome Sync
|
|
444
|
+
**Options**: (A) Long-poll (HTTP GET with 30s timeout), (B) WebSocket duplex, (C) Server-sent events (SSE).
|
|
445
|
+
|
|
446
|
+
**Recommendation**: (A) — Long-poll with exponential backoff.
|
|
447
|
+
|
|
448
|
+
**Rationale**:
|
|
449
|
+
- Stateless: Long-poll doesn't require server to maintain connection state; scales to 10K+ concurrent users easily.
|
|
450
|
+
- Simple: Aligns with Hono's request-response model; no special handling needed.
|
|
451
|
+
- Mobile-friendly: Long-poll works over cellular with retries; WebSocket connection drops are expensive.
|
|
452
|
+
- SSE is a middle ground, but browsers have fewer SSE libraries and CDN support is weaker than HTTP.
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
### Decision 6: Stripe Checkout vs. Metered Billing
|
|
457
|
+
**Options**: (A) Checkout for subscription (flat monthly or per-seat), (B) Metered billing (post-usage aggregation), (C) Hybrid (flat + metered overage).
|
|
458
|
+
|
|
459
|
+
**Recommendation**: (A) — Subscription (flat $500–2K/month + $5/seat) for team tier; separate enterprise contract for metered custom features.
|
|
460
|
+
|
|
461
|
+
**Rationale**:
|
|
462
|
+
- Team tier is SMB-focused; predictable per-seat cost is easier to sell.
|
|
463
|
+
- Metered (B) introduces invoice surprise and requires real-time usage sync; adds complexity.
|
|
464
|
+
- Hybrid (C) is a third option but creates edge cases (what if usage is 2x over?).
|
|
465
|
+
- Enterprise (custom) tier handles edge cases (high-volume policy enforcement, audit log queries).
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
### Decision 7: Redis vs. Postgres for Rate Limits
|
|
470
|
+
**Options**: (A) Redis sliding-window counter, (B) Postgres with advisory locks, (C) In-memory token bucket per server (no shared state).
|
|
471
|
+
|
|
472
|
+
**Recommendation**: (A) — Redis sliding-window.
|
|
473
|
+
|
|
474
|
+
**Rationale**:
|
|
475
|
+
- Redis is O(1) per limit check; Postgres + locks adds latency.
|
|
476
|
+
- Sliding window is more accurate than fixed buckets; edge case: request at limit boundary is handled fairly.
|
|
477
|
+
- In-memory (C) doesn't work for distributed app; requires load-balancer affinity or consistent hashing.
|
|
478
|
+
- Redis ttl auto-cleanup reduces memory bleed.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
### Decision 8: S3 vs. Postgres for Genome Section Bodies
|
|
483
|
+
**Options**: (A) Store full section bodies in Postgres BYTEA, (B) Store in S3, reference via `body_s3_key`, (C) Hybrid (small bodies in Postgres, large in S3).
|
|
484
|
+
|
|
485
|
+
**Recommendation**: (B) — S3 for all sections.
|
|
486
|
+
|
|
487
|
+
**Rationale**:
|
|
488
|
+
- Sections can be large (50KB+); storing in Postgres inflates WAL and backup size.
|
|
489
|
+
- S3 is cheaper per GB ($0.023/month) than Postgres ($0.50+/month).
|
|
490
|
+
- Cache busting is simpler: `body_s3_key` includes a hash; old versions are automatically unreferenced (S3 lifecycle policies delete after 30 days).
|
|
491
|
+
- Integrity: ETag on S3 object matches hash in `genome_sections.hash`; prevents corruption.
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
### Decision 9: Single Stripe Account vs. Reseller Model
|
|
496
|
+
**Options**: (A) ashlr.ai holds Stripe account; users pay ashlr directly, (B) Orgs hold their own Stripe accounts (reseller/whitelabel), (C) Hybrid (ashlr standard, opt-in self-billing for enterprise).
|
|
497
|
+
|
|
498
|
+
**Recommendation**: (A) — Single Stripe account owned by ashlr.ai.
|
|
499
|
+
|
|
500
|
+
**Rationale**:
|
|
501
|
+
- Simpler to implement; no certificate signing or connected-account complexity.
|
|
502
|
+
- Pricing is transparent and centralized.
|
|
503
|
+
- Orgs don't have to manage Stripe; reduces friction at signup.
|
|
504
|
+
- Enterprise tier (C) can be added later if customers demand white-label invoicing.
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
### Decision 10: Real-Time Badge Refresh vs. Batch Hourly
|
|
509
|
+
**Options**: (A) Update badge SVG immediately on stats upload (slow path in request), (B) Batch job (every 1 hour), (C) Lazy (update on first request after stats upload).
|
|
510
|
+
|
|
511
|
+
**Recommendation**: (B) — Batch job (cron) every 15 minutes.
|
|
512
|
+
|
|
513
|
+
**Rationale**:
|
|
514
|
+
- Real-time (A) adds latency to stats-upload request (slow path); stats are non-critical, so users won't tolerate >100ms delay.
|
|
515
|
+
- Batch (B) is predictable; `redis-del` old badge, compute new badge, `redis-set` with 15m expiry. Leaderboard changes are visible within 15m.
|
|
516
|
+
- Lazy (C) is fine but requires stats-upload request to notify the batch job; adds coupling.
|
|
517
|
+
- 15 min is a compromise: frequent enough for GitHub embeds to feel live, infrequent enough to not thrash Redis.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## End Notes
|
|
522
|
+
|
|
523
|
+
This architecture is defensible for 10K active users and < $50K/month infra spend. Beyond that, shard by org, add read-replicas, and consider denormalization (aggregate tables) for leaderboard queries. The vclock/LWW sync and encryption plumbing are the highest-risk items (and the CRDT auto-merge upgrade in v2 will be the next big lift); build Phase 1 (badge + stats) to validate the basic backend infrastructure before committing to Phase 3.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# ProductHunt Launch Draft
|
|
2
|
+
|
|
3
|
+
## Tagline (60 char max)
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Open-source Codex + Claude Code token ledger.
|
|
7
|
+
```
|
|
8
|
+
(50 chars)
|
|
9
|
+
|
|
10
|
+
## Description (260 char max)
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
ashlr replaces high-volume Read/Grep/Edit/Bash workflows with compressed alternatives for Codex and Claude Code. 40 MCP tools, Codex skills, measured −57% token savings on real codebases. MIT free forever.
|
|
14
|
+
```
|
|
15
|
+
(205 chars)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## First comment (from the maker)
|
|
20
|
+
|
|
21
|
+
I started building ashlr in April after watching Claude Code blow through a context window on what should have been a simple refactor. The culprit was obvious once I looked: every native Read call was shipping the full file — 10,000 tokens for a file I needed three lines from.
|
|
22
|
+
|
|
23
|
+
v0.6 was a rough proof of concept: three MCP tools, a basic snip-compactor, a status line that mostly worked. The numbers were promising but not measured — I was estimating savings, not counting them.
|
|
24
|
+
|
|
25
|
+
Between v0.6 and v1.36 I built out the full accounting layer: per-session token ledger, Codex-native plugin packaging, an animated Claude Code status line, a genome-aware grep path that uses retrieval, and a reproducible cross-repo benchmark. The honest −57% number comes from TypeScript, Python, and Rust reference repos — including workloads where the plugin is less flattering than a single self-repo benchmark. I kept those caveats in because the methodology has to be auditable.
|
|
26
|
+
|
|
27
|
+
A few things broke along the way that are worth being honest about. The session counter showed `+0` for weeks because Claude Code doesn't forward CLAUDE_SESSION_ID into MCP subprocesses. The `$` interpolation bug silently corrupted multi-edits containing template literals. An unrelated zombie-process issue made `/reload-plugins` look like it worked when it didn't. All three are fixed in the changelog with root causes.
|
|
28
|
+
|
|
29
|
+
860 tests pass. 2 are skipped for documented reasons. The benchmark runs weekly in CI.
|
|
30
|
+
|
|
31
|
+
If you use Claude Code on a real codebase, install the free tier and run `/ashlr-benchmark`. The number will either justify itself or it won't — either way it's your number, not mine.
|
|
32
|
+
|
|
33
|
+
Feedback welcome, especially on the genome-init workflow and anything that looks wrong about the benchmark methodology.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Gallery captions (5 items)
|
|
38
|
+
|
|
39
|
+
1. **Status line in action**
|
|
40
|
+
Terminal screenshot showing `ashlr · 7d ▁▂▃▅▇█ · session ↑+48.2K · lifetime +2.1M` with the animated gradient sweep and activity pulse indicator. Run `/ashlr-savings` to reproduce.
|
|
41
|
+
|
|
42
|
+
2. **ASCII dashboard**
|
|
43
|
+
Terminal screenshot of `/ashlr-dashboard` — three CountUp tiles (session / lifetime / best day), per-tool horizontal bar chart, 7-day and 30-day sparklines, projected annual savings. Run `/ashlr-dashboard` on any active session.
|
|
44
|
+
|
|
45
|
+
3. **Benchmarks page**
|
|
46
|
+
Browser screenshot of `plugin.ashlr.ai/benchmarks` showing the −57% cross-language number, per-language breakdown, and the methodology panel. Numbers are reproducible from the repo.
|
|
47
|
+
|
|
48
|
+
4. **Before / after token comparison**
|
|
49
|
+
Side-by-side showing the same `ashlr__read` call: raw file (10,846 bytes / 2,709 tokens) vs ashlr output (1,623 bytes / 406 tokens). The 85% reduction on `server/tests/auth.test.ts` from `docs/benchmarks-v2.json`.
|
|
50
|
+
|
|
51
|
+
5. **Pricing**
|
|
52
|
+
Browser screenshot of `plugin.ashlr.ai/pricing` — three-tier layout (Free / Pro / Team) with the feature comparison table. Emphasizes the free tier is not crippled.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Router Migration Guide (v1.24)
|
|
2
|
+
|
|
3
|
+
## What changed
|
|
4
|
+
|
|
5
|
+
Starting with v1.24, ashlr runs as a **single MCP process** hosting all 40
|
|
6
|
+
tools instead of spawning one child process per tool. The canonical entry point
|
|
7
|
+
is `servers/_router.ts`.
|
|
8
|
+
|
|
9
|
+
The old architecture spawned up to 12 separate `mcpServers` entries in
|
|
10
|
+
`plugin.json`. Each entry was its own Bun process, each with its own SQLite
|
|
11
|
+
handle, summarizer pool, and genome LRU cache. Cold-start summed up across all
|
|
12
|
+
of them; memory usage scaled linearly with the number of tools in flight.
|
|
13
|
+
|
|
14
|
+
The router consolidates this: one process, one SQLite handle, one LLM pool,
|
|
15
|
+
one startup cost. Measured cold-start went from ~200-400ms (aggregate N
|
|
16
|
+
processes) to ~75ms (single process with all 40 handlers).
|
|
17
|
+
|
|
18
|
+
## plugin.json already updated
|
|
19
|
+
|
|
20
|
+
As of v1.24, `.claude-plugin/plugin.json` contains a single entry:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"ashlr": {
|
|
26
|
+
"command": "node",
|
|
27
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/scripts/bootstrap.mjs", "servers/_router.ts"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Fresh installs get this automatically.
|
|
34
|
+
|
|
35
|
+
## Users with legacy multi-entry config
|
|
36
|
+
|
|
37
|
+
Before v1.24, `plugin.json` listed one `mcpServers` entry per server
|
|
38
|
+
(efficiency-server, grep-server, etc.). If a user installed ashlr before v1.24
|
|
39
|
+
and has not re-run `/ashlr-allow`, their `~/.claude/settings.json` may still
|
|
40
|
+
contain the old entries alongside (or instead of) the new single entry.
|
|
41
|
+
|
|
42
|
+
**Detection:** The orient-nudge hook (`hooks/orient-server.ts`) detects the
|
|
43
|
+
legacy multi-MCP config on session start and emits a `legacy_mcp_config`
|
|
44
|
+
nudge. When this fires, the user sees:
|
|
45
|
+
|
|
46
|
+
> "ashlr: multiple MCP server entries detected (v1.23 config). Run
|
|
47
|
+
> `/ashlr-allow` to refresh to the single-router config and reduce process
|
|
48
|
+
> count."
|
|
49
|
+
|
|
50
|
+
**Migration steps (manual):**
|
|
51
|
+
|
|
52
|
+
1. Open `~/.claude/settings.json`.
|
|
53
|
+
2. Under `mcpServers`, delete any entries whose `args` array references a
|
|
54
|
+
per-server file (e.g., `servers/efficiency-server.ts`, `servers/grep-server.ts`).
|
|
55
|
+
3. Keep (or add) the single entry:
|
|
56
|
+
```json
|
|
57
|
+
"ashlr": {
|
|
58
|
+
"command": "node",
|
|
59
|
+
"args": ["${CLAUDE_PLUGIN_ROOT}/scripts/bootstrap.mjs", "servers/_router.ts"]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
4. Reload Claude Code (`/reset` or restart the app).
|
|
63
|
+
|
|
64
|
+
**Migration steps (automatic):**
|
|
65
|
+
|
|
66
|
+
Run `/ashlr-allow` — the onboarding wizard detects the legacy config and
|
|
67
|
+
offers to collapse it to the single entry.
|
|
68
|
+
|
|
69
|
+
## Legacy per-server entrypoints remain functional
|
|
70
|
+
|
|
71
|
+
`efficiency-server.ts`, `grep-server.ts`, and every other per-server file keep
|
|
72
|
+
their `if (import.meta.main)` guards. They still start correctly when invoked
|
|
73
|
+
directly:
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
bun run servers/efficiency-server.ts
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This means:
|
|
80
|
+
|
|
81
|
+
- Users who keep the old `mcpServers` config continue to work without any
|
|
82
|
+
hard break. Their tools call the per-server processes; those processes use
|
|
83
|
+
the same handlers (via the shared registry in `_tool-base.ts`).
|
|
84
|
+
- Smoke tests that exercise individual servers directly continue to pass.
|
|
85
|
+
- No tools were changed — this is purely structural.
|
|
86
|
+
|
|
87
|
+
## Verification
|
|
88
|
+
|
|
89
|
+
After migrating, confirm a single ashlr process is running:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
ps aux | grep ashlr
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
You should see one process referencing `_router.ts` (plus the Claude Code
|
|
96
|
+
parent). Before migration you would have seen one per tool.
|
|
97
|
+
|
|
98
|
+
Check cold-start:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
bun run scripts/measure-cold-start.ts --runs 3
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Expected output: p95 under 100ms.
|
|
105
|
+
|
|
106
|
+
Run the full test suite to confirm no regressions:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
ASHLR_STATS_SYNC=1 bun test __tests__/router-dispatch.test.ts __tests__/router-cold-start.test.ts __tests__/router.test.ts
|
|
110
|
+
```
|