@ashlr/hub 2.2.0
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/CHANGELOG.md +1158 -0
- package/LICENSE +21 -0
- package/README.md +833 -0
- package/bin/ashlr +8 -0
- package/dist/api/core.d.ts +28 -0
- package/dist/api/core.js +42 -0
- package/dist/api/core.js.map +1 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/index.js +9 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/plugin.d.ts +12 -0
- package/dist/api/plugin.js +13 -0
- package/dist/api/plugin.js.map +1 -0
- package/dist/api/types.d.ts +9 -0
- package/dist/api/types.js +9 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli/args.d.ts +20 -0
- package/dist/cli/args.js +23 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/ask.d.ts +22 -0
- package/dist/cli/ask.js +240 -0
- package/dist/cli/ask.js.map +1 -0
- package/dist/cli/audit.d.ts +26 -0
- package/dist/cli/audit.js +199 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/backlog.d.ts +26 -0
- package/dist/cli/backlog.js +322 -0
- package/dist/cli/backlog.js.map +1 -0
- package/dist/cli/completions.d.ts +18 -0
- package/dist/cli/completions.js +156 -0
- package/dist/cli/completions.js.map +1 -0
- package/dist/cli/daemon.d.ts +31 -0
- package/dist/cli/daemon.js +334 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/demo-sandbox.d.ts +78 -0
- package/dist/cli/demo-sandbox.js +227 -0
- package/dist/cli/demo-sandbox.js.map +1 -0
- package/dist/cli/demo.d.ts +76 -0
- package/dist/cli/demo.js +285 -0
- package/dist/cli/demo.js.map +1 -0
- package/dist/cli/digest.d.ts +46 -0
- package/dist/cli/digest.js +352 -0
- package/dist/cli/digest.js.map +1 -0
- package/dist/cli/doctor-init.d.ts +28 -0
- package/dist/cli/doctor-init.js +335 -0
- package/dist/cli/doctor-init.js.map +1 -0
- package/dist/cli/genome.d.ts +62 -0
- package/dist/cli/genome.js +1061 -0
- package/dist/cli/genome.js.map +1 -0
- package/dist/cli/gh.d.ts +27 -0
- package/dist/cli/gh.js +322 -0
- package/dist/cli/gh.js.map +1 -0
- package/dist/cli/goals.d.ts +55 -0
- package/dist/cli/goals.js +667 -0
- package/dist/cli/goals.js.map +1 -0
- package/dist/cli/health.d.ts +56 -0
- package/dist/cli/health.js +407 -0
- package/dist/cli/health.js.map +1 -0
- package/dist/cli/help.d.ts +51 -0
- package/dist/cli/help.js +436 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/inbox.d.ts +28 -0
- package/dist/cli/inbox.js +589 -0
- package/dist/cli/inbox.js.map +1 -0
- package/dist/cli/index.d.ts +41 -0
- package/dist/cli/index.js +1208 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/knowledge.d.ts +33 -0
- package/dist/cli/knowledge.js +575 -0
- package/dist/cli/knowledge.js.map +1 -0
- package/dist/cli/mcp.d.ts +26 -0
- package/dist/cli/mcp.js +487 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/models.d.ts +26 -0
- package/dist/cli/models.js +435 -0
- package/dist/cli/models.js.map +1 -0
- package/dist/cli/new.d.ts +32 -0
- package/dist/cli/new.js +565 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/notify.d.ts +20 -0
- package/dist/cli/notify.js +188 -0
- package/dist/cli/notify.js.map +1 -0
- package/dist/cli/onboard.d.ts +85 -0
- package/dist/cli/onboard.js +305 -0
- package/dist/cli/onboard.js.map +1 -0
- package/dist/cli/open.d.ts +38 -0
- package/dist/cli/open.js +86 -0
- package/dist/cli/open.js.map +1 -0
- package/dist/cli/orient.d.ts +14 -0
- package/dist/cli/orient.js +110 -0
- package/dist/cli/orient.js.map +1 -0
- package/dist/cli/picker.d.ts +21 -0
- package/dist/cli/picker.js +177 -0
- package/dist/cli/picker.js.map +1 -0
- package/dist/cli/plugins.d.ts +21 -0
- package/dist/cli/plugins.js +394 -0
- package/dist/cli/plugins.js.map +1 -0
- package/dist/cli/preflight.d.ts +29 -0
- package/dist/cli/preflight.js +131 -0
- package/dist/cli/preflight.js.map +1 -0
- package/dist/cli/pulse.d.ts +26 -0
- package/dist/cli/pulse.js +482 -0
- package/dist/cli/pulse.js.map +1 -0
- package/dist/cli/reflect.d.ts +42 -0
- package/dist/cli/reflect.js +381 -0
- package/dist/cli/reflect.js.map +1 -0
- package/dist/cli/run.d.ts +29 -0
- package/dist/cli/run.js +687 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/sandbox.d.ts +20 -0
- package/dist/cli/sandbox.js +345 -0
- package/dist/cli/sandbox.js.map +1 -0
- package/dist/cli/seams.d.ts +41 -0
- package/dist/cli/seams.js +167 -0
- package/dist/cli/seams.js.map +1 -0
- package/dist/cli/serve.d.ts +25 -0
- package/dist/cli/serve.js +249 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/ship.d.ts +33 -0
- package/dist/cli/ship.js +433 -0
- package/dist/cli/ship.js.map +1 -0
- package/dist/cli/spec.d.ts +19 -0
- package/dist/cli/spec.js +478 -0
- package/dist/cli/spec.js.map +1 -0
- package/dist/cli/swarm.d.ts +42 -0
- package/dist/cli/swarm.js +1365 -0
- package/dist/cli/swarm.js.map +1 -0
- package/dist/cli/telemetry.d.ts +26 -0
- package/dist/cli/telemetry.js +364 -0
- package/dist/cli/telemetry.js.map +1 -0
- package/dist/cli/tui.d.ts +17 -0
- package/dist/cli/tui.js +52 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/cli/ui.d.ts +53 -0
- package/dist/cli/ui.js +66 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/cli/update.d.ts +38 -0
- package/dist/cli/update.js +682 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/vercel.d.ts +20 -0
- package/dist/cli/vercel.js +193 -0
- package/dist/cli/vercel.js.map +1 -0
- package/dist/cli/verify-safety.d.ts +96 -0
- package/dist/cli/verify-safety.js +509 -0
- package/dist/cli/verify-safety.js.map +1 -0
- package/dist/cli/wire.d.ts +22 -0
- package/dist/cli/wire.js +205 -0
- package/dist/cli/wire.js.map +1 -0
- package/dist/core/classify.d.ts +51 -0
- package/dist/core/classify.js +450 -0
- package/dist/core/classify.js.map +1 -0
- package/dist/core/config.d.ts +63 -0
- package/dist/core/config.js +474 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/daemon/loop.d.ts +74 -0
- package/dist/core/daemon/loop.js +618 -0
- package/dist/core/daemon/loop.js.map +1 -0
- package/dist/core/daemon/state.d.ts +66 -0
- package/dist/core/daemon/state.js +197 -0
- package/dist/core/daemon/state.js.map +1 -0
- package/dist/core/dashboard.d.ts +40 -0
- package/dist/core/dashboard.js +463 -0
- package/dist/core/dashboard.js.map +1 -0
- package/dist/core/digest/build.d.ts +51 -0
- package/dist/core/digest/build.js +230 -0
- package/dist/core/digest/build.js.map +1 -0
- package/dist/core/digest/deliver.d.ts +47 -0
- package/dist/core/digest/deliver.js +230 -0
- package/dist/core/digest/deliver.js.map +1 -0
- package/dist/core/digest/store.d.ts +57 -0
- package/dist/core/digest/store.js +223 -0
- package/dist/core/digest/store.js.map +1 -0
- package/dist/core/doctor-fix.d.ts +21 -0
- package/dist/core/doctor-fix.js +440 -0
- package/dist/core/doctor-fix.js.map +1 -0
- package/dist/core/doctor.d.ts +18 -0
- package/dist/core/doctor.js +806 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/env-bridge.d.ts +64 -0
- package/dist/core/env-bridge.js +103 -0
- package/dist/core/env-bridge.js.map +1 -0
- package/dist/core/genome/capture.d.ts +49 -0
- package/dist/core/genome/capture.js +352 -0
- package/dist/core/genome/capture.js.map +1 -0
- package/dist/core/genome/consolidate.d.ts +38 -0
- package/dist/core/genome/consolidate.js +426 -0
- package/dist/core/genome/consolidate.js.map +1 -0
- package/dist/core/genome/export.d.ts +29 -0
- package/dist/core/genome/export.js +102 -0
- package/dist/core/genome/export.js.map +1 -0
- package/dist/core/genome/playbook.d.ts +33 -0
- package/dist/core/genome/playbook.js +320 -0
- package/dist/core/genome/playbook.js.map +1 -0
- package/dist/core/genome/recall.d.ts +45 -0
- package/dist/core/genome/recall.js +293 -0
- package/dist/core/genome/recall.js.map +1 -0
- package/dist/core/genome/store.d.ts +62 -0
- package/dist/core/genome/store.js +711 -0
- package/dist/core/genome/store.js.map +1 -0
- package/dist/core/git.d.ts +34 -0
- package/dist/core/git.js +115 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/goals/advance.d.ts +82 -0
- package/dist/core/goals/advance.js +263 -0
- package/dist/core/goals/advance.js.map +1 -0
- package/dist/core/goals/planner.d.ts +58 -0
- package/dist/core/goals/planner.js +233 -0
- package/dist/core/goals/planner.js.map +1 -0
- package/dist/core/goals/store.d.ts +128 -0
- package/dist/core/goals/store.js +442 -0
- package/dist/core/goals/store.js.map +1 -0
- package/dist/core/inbox/apply.d.ts +36 -0
- package/dist/core/inbox/apply.js +407 -0
- package/dist/core/inbox/apply.js.map +1 -0
- package/dist/core/inbox/notify-proposal.d.ts +13 -0
- package/dist/core/inbox/notify-proposal.js +21 -0
- package/dist/core/inbox/notify-proposal.js.map +1 -0
- package/dist/core/inbox/store.d.ts +67 -0
- package/dist/core/inbox/store.js +258 -0
- package/dist/core/inbox/store.js.map +1 -0
- package/dist/core/index-engine.d.ts +62 -0
- package/dist/core/index-engine.js +486 -0
- package/dist/core/index-engine.js.map +1 -0
- package/dist/core/integrations/desktop-notify.d.ts +18 -0
- package/dist/core/integrations/desktop-notify.js +42 -0
- package/dist/core/integrations/desktop-notify.js.map +1 -0
- package/dist/core/integrations/editors.d.ts +50 -0
- package/dist/core/integrations/editors.js +216 -0
- package/dist/core/integrations/editors.js.map +1 -0
- package/dist/core/integrations/github.d.ts +66 -0
- package/dist/core/integrations/github.js +332 -0
- package/dist/core/integrations/github.js.map +1 -0
- package/dist/core/integrations/identity.d.ts +29 -0
- package/dist/core/integrations/identity.js +359 -0
- package/dist/core/integrations/identity.js.map +1 -0
- package/dist/core/integrations/notify.d.ts +23 -0
- package/dist/core/integrations/notify.js +96 -0
- package/dist/core/integrations/notify.js.map +1 -0
- package/dist/core/integrations/vercel.d.ts +37 -0
- package/dist/core/integrations/vercel.js +192 -0
- package/dist/core/integrations/vercel.js.map +1 -0
- package/dist/core/knowledge/ask.d.ts +34 -0
- package/dist/core/knowledge/ask.js +325 -0
- package/dist/core/knowledge/ask.js.map +1 -0
- package/dist/core/knowledge/graph.d.ts +51 -0
- package/dist/core/knowledge/graph.js +564 -0
- package/dist/core/knowledge/graph.js.map +1 -0
- package/dist/core/knowledge/index.d.ts +74 -0
- package/dist/core/knowledge/index.js +586 -0
- package/dist/core/knowledge/index.js.map +1 -0
- package/dist/core/learn/playbooks.d.ts +84 -0
- package/dist/core/learn/playbooks.js +241 -0
- package/dist/core/learn/playbooks.js.map +1 -0
- package/dist/core/learn/reflect.d.ts +87 -0
- package/dist/core/learn/reflect.js +435 -0
- package/dist/core/learn/reflect.js.map +1 -0
- package/dist/core/learn/store.d.ts +51 -0
- package/dist/core/learn/store.js +165 -0
- package/dist/core/learn/store.js.map +1 -0
- package/dist/core/learn/tuning.d.ts +48 -0
- package/dist/core/learn/tuning.js +201 -0
- package/dist/core/learn/tuning.js.map +1 -0
- package/dist/core/lifecycle/scaffold.d.ts +43 -0
- package/dist/core/lifecycle/scaffold.js +260 -0
- package/dist/core/lifecycle/scaffold.js.map +1 -0
- package/dist/core/lifecycle/ship.d.ts +48 -0
- package/dist/core/lifecycle/ship.js +513 -0
- package/dist/core/lifecycle/ship.js.map +1 -0
- package/dist/core/lifecycle/templates.d.ts +20 -0
- package/dist/core/lifecycle/templates.js +605 -0
- package/dist/core/lifecycle/templates.js.map +1 -0
- package/dist/core/mcp-gateway.d.ts +59 -0
- package/dist/core/mcp-gateway.js +385 -0
- package/dist/core/mcp-gateway.js.map +1 -0
- package/dist/core/mcp-native.d.ts +53 -0
- package/dist/core/mcp-native.js +507 -0
- package/dist/core/mcp-native.js.map +1 -0
- package/dist/core/mcp-registry.d.ts +36 -0
- package/dist/core/mcp-registry.js +180 -0
- package/dist/core/mcp-registry.js.map +1 -0
- package/dist/core/observability/budget-alert.d.ts +18 -0
- package/dist/core/observability/budget-alert.js +90 -0
- package/dist/core/observability/budget-alert.js.map +1 -0
- package/dist/core/observability/estimate.d.ts +27 -0
- package/dist/core/observability/estimate.js +188 -0
- package/dist/core/observability/estimate.js.map +1 -0
- package/dist/core/observability/forecast.d.ts +19 -0
- package/dist/core/observability/forecast.js +101 -0
- package/dist/core/observability/forecast.js.map +1 -0
- package/dist/core/observability/governance.d.ts +28 -0
- package/dist/core/observability/governance.js +101 -0
- package/dist/core/observability/governance.js.map +1 -0
- package/dist/core/observability/otlp.d.ts +88 -0
- package/dist/core/observability/otlp.js +217 -0
- package/dist/core/observability/otlp.js.map +1 -0
- package/dist/core/observability/rollup.d.ts +35 -0
- package/dist/core/observability/rollup.js +311 -0
- package/dist/core/observability/rollup.js.map +1 -0
- package/dist/core/observability/telemetry-sink.d.ts +63 -0
- package/dist/core/observability/telemetry-sink.js +350 -0
- package/dist/core/observability/telemetry-sink.js.map +1 -0
- package/dist/core/observability/usage-source.d.ts +60 -0
- package/dist/core/observability/usage-source.js +347 -0
- package/dist/core/observability/usage-source.js.map +1 -0
- package/dist/core/onboard.d.ts +27 -0
- package/dist/core/onboard.js +288 -0
- package/dist/core/onboard.js.map +1 -0
- package/dist/core/orient.d.ts +23 -0
- package/dist/core/orient.js +135 -0
- package/dist/core/orient.js.map +1 -0
- package/dist/core/phantom.d.ts +23 -0
- package/dist/core/phantom.js +279 -0
- package/dist/core/phantom.js.map +1 -0
- package/dist/core/plugins/host-api.d.ts +29 -0
- package/dist/core/plugins/host-api.js +113 -0
- package/dist/core/plugins/host-api.js.map +1 -0
- package/dist/core/plugins/integrity.d.ts +36 -0
- package/dist/core/plugins/integrity.js +73 -0
- package/dist/core/plugins/integrity.js.map +1 -0
- package/dist/core/plugins/manifest.d.ts +31 -0
- package/dist/core/plugins/manifest.js +316 -0
- package/dist/core/plugins/manifest.js.map +1 -0
- package/dist/core/plugins/registry.d.ts +87 -0
- package/dist/core/plugins/registry.js +415 -0
- package/dist/core/plugins/registry.js.map +1 -0
- package/dist/core/plugins/types.d.ts +182 -0
- package/dist/core/plugins/types.js +40 -0
- package/dist/core/plugins/types.js.map +1 -0
- package/dist/core/plugins/wrappers.d.ts +40 -0
- package/dist/core/plugins/wrappers.js +229 -0
- package/dist/core/plugins/wrappers.js.map +1 -0
- package/dist/core/portfolio/backlog.d.ts +40 -0
- package/dist/core/portfolio/backlog.js +177 -0
- package/dist/core/portfolio/backlog.js.map +1 -0
- package/dist/core/portfolio/scanners.d.ts +21 -0
- package/dist/core/portfolio/scanners.js +600 -0
- package/dist/core/portfolio/scanners.js.map +1 -0
- package/dist/core/providers.d.ts +34 -0
- package/dist/core/providers.js +250 -0
- package/dist/core/providers.js.map +1 -0
- package/dist/core/quality/conventions.d.ts +35 -0
- package/dist/core/quality/conventions.js +267 -0
- package/dist/core/quality/conventions.js.map +1 -0
- package/dist/core/quality/fixes.d.ts +56 -0
- package/dist/core/quality/fixes.js +209 -0
- package/dist/core/quality/fixes.js.map +1 -0
- package/dist/core/quality/health.d.ts +69 -0
- package/dist/core/quality/health.js +350 -0
- package/dist/core/quality/health.js.map +1 -0
- package/dist/core/quality/store.d.ts +56 -0
- package/dist/core/quality/store.js +195 -0
- package/dist/core/quality/store.js.map +1 -0
- package/dist/core/readiness.d.ts +112 -0
- package/dist/core/readiness.js +431 -0
- package/dist/core/readiness.js.map +1 -0
- package/dist/core/run/agent-loop.d.ts +40 -0
- package/dist/core/run/agent-loop.js +291 -0
- package/dist/core/run/agent-loop.js.map +1 -0
- package/dist/core/run/budget.d.ts +47 -0
- package/dist/core/run/budget.js +113 -0
- package/dist/core/run/budget.js.map +1 -0
- package/dist/core/run/engines.d.ts +75 -0
- package/dist/core/run/engines.js +199 -0
- package/dist/core/run/engines.js.map +1 -0
- package/dist/core/run/model-manager.d.ts +64 -0
- package/dist/core/run/model-manager.js +339 -0
- package/dist/core/run/model-manager.js.map +1 -0
- package/dist/core/run/orchestrator.d.ts +100 -0
- package/dist/core/run/orchestrator.js +1515 -0
- package/dist/core/run/orchestrator.js.map +1 -0
- package/dist/core/run/provider-client.d.ts +46 -0
- package/dist/core/run/provider-client.js +796 -0
- package/dist/core/run/provider-client.js.map +1 -0
- package/dist/core/run/retry.d.ts +19 -0
- package/dist/core/run/retry.js +68 -0
- package/dist/core/run/retry.js.map +1 -0
- package/dist/core/run/router.d.ts +50 -0
- package/dist/core/run/router.js +257 -0
- package/dist/core/run/router.js.map +1 -0
- package/dist/core/run/self-heal.d.ts +52 -0
- package/dist/core/run/self-heal.js +181 -0
- package/dist/core/run/self-heal.js.map +1 -0
- package/dist/core/run/streaming.d.ts +31 -0
- package/dist/core/run/streaming.js +122 -0
- package/dist/core/run/streaming.js.map +1 -0
- package/dist/core/run/verify.d.ts +30 -0
- package/dist/core/run/verify.js +204 -0
- package/dist/core/run/verify.js.map +1 -0
- package/dist/core/sandbox/audit.d.ts +31 -0
- package/dist/core/sandbox/audit.js +169 -0
- package/dist/core/sandbox/audit.js.map +1 -0
- package/dist/core/sandbox/policy.d.ts +61 -0
- package/dist/core/sandbox/policy.js +211 -0
- package/dist/core/sandbox/policy.js.map +1 -0
- package/dist/core/sandbox/worktree.d.ts +175 -0
- package/dist/core/sandbox/worktree.js +673 -0
- package/dist/core/sandbox/worktree.js.map +1 -0
- package/dist/core/seams/backlog.d.ts +47 -0
- package/dist/core/seams/backlog.js +47 -0
- package/dist/core/seams/backlog.js.map +1 -0
- package/dist/core/seams/daemon-coordinator.d.ts +72 -0
- package/dist/core/seams/daemon-coordinator.js +76 -0
- package/dist/core/seams/daemon-coordinator.js.map +1 -0
- package/dist/core/seams/genome.d.ts +45 -0
- package/dist/core/seams/genome.js +53 -0
- package/dist/core/seams/genome.js.map +1 -0
- package/dist/core/seams/identity.d.ts +40 -0
- package/dist/core/seams/identity.js +44 -0
- package/dist/core/seams/identity.js.map +1 -0
- package/dist/core/seams/inbox.d.ts +60 -0
- package/dist/core/seams/inbox.js +66 -0
- package/dist/core/seams/inbox.js.map +1 -0
- package/dist/core/seams/index.d.ts +20 -0
- package/dist/core/seams/index.js +21 -0
- package/dist/core/seams/index.js.map +1 -0
- package/dist/core/seams/portfolio.d.ts +50 -0
- package/dist/core/seams/portfolio.js +61 -0
- package/dist/core/seams/portfolio.js.map +1 -0
- package/dist/core/seams/registry.d.ts +42 -0
- package/dist/core/seams/registry.js +128 -0
- package/dist/core/seams/registry.js.map +1 -0
- package/dist/core/seams/run-swarm.d.ts +66 -0
- package/dist/core/seams/run-swarm.js +74 -0
- package/dist/core/seams/run-swarm.js.map +1 -0
- package/dist/core/seams/types.d.ts +123 -0
- package/dist/core/seams/types.js +35 -0
- package/dist/core/seams/types.js.map +1 -0
- package/dist/core/spec/spec-store.d.ts +62 -0
- package/dist/core/spec/spec-store.js +359 -0
- package/dist/core/spec/spec-store.js.map +1 -0
- package/dist/core/swarm/gate.d.ts +45 -0
- package/dist/core/swarm/gate.js +114 -0
- package/dist/core/swarm/gate.js.map +1 -0
- package/dist/core/swarm/planner.d.ts +32 -0
- package/dist/core/swarm/planner.js +293 -0
- package/dist/core/swarm/planner.js.map +1 -0
- package/dist/core/swarm/rollback.d.ts +56 -0
- package/dist/core/swarm/rollback.js +266 -0
- package/dist/core/swarm/rollback.js.map +1 -0
- package/dist/core/swarm/runner.d.ts +62 -0
- package/dist/core/swarm/runner.js +1263 -0
- package/dist/core/swarm/runner.js.map +1 -0
- package/dist/core/swarm/sign.d.ts +71 -0
- package/dist/core/swarm/sign.js +362 -0
- package/dist/core/swarm/sign.js.map +1 -0
- package/dist/core/swarm/store.d.ts +52 -0
- package/dist/core/swarm/store.js +195 -0
- package/dist/core/swarm/store.js.map +1 -0
- package/dist/core/tidy.d.ts +32 -0
- package/dist/core/tidy.js +354 -0
- package/dist/core/tidy.js.map +1 -0
- package/dist/core/tools-registry.d.ts +16 -0
- package/dist/core/tools-registry.js +308 -0
- package/dist/core/tools-registry.js.map +1 -0
- package/dist/core/types.d.ts +2545 -0
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/web/api.d.ts +53 -0
- package/dist/core/web/api.js +698 -0
- package/dist/core/web/api.js.map +1 -0
- package/dist/core/web/public/app.js +1906 -0
- package/dist/core/web/public/index.html +721 -0
- package/dist/core/web/public/styles.css +2007 -0
- package/dist/core/web/server.d.ts +18 -0
- package/dist/core/web/server.js +122 -0
- package/dist/core/web/server.js.map +1 -0
- package/dist/core/web/static.d.ts +18 -0
- package/dist/core/web/static.js +122 -0
- package/dist/core/web/static.js.map +1 -0
- package/dist/tui/app.d.ts +33 -0
- package/dist/tui/app.js +350 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/render.d.ts +20 -0
- package/dist/tui/render.js +558 -0
- package/dist/tui/render.js.map +1 -0
- package/package.json +80 -0
- package/schema/config.schema.json +223 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* usage-source.ts — local-first UsageEvent collector (M5 observability).
|
|
3
|
+
*
|
|
4
|
+
* PRIVACY GUARDRAILS: reads ONLY token counts, model id, timestamp, and the
|
|
5
|
+
* project path (decoded from the encoded dir name). Never reads or retains
|
|
6
|
+
* message content, tool args/results, prompts, or completions.
|
|
7
|
+
*
|
|
8
|
+
* PERFORMANCE: streams line-by-line; skips files whose mtime is older than
|
|
9
|
+
* sinceMs; tolerates malformed lines; never throws.
|
|
10
|
+
*/
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import * as os from "os";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Public helpers
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/** Absolute path to ~/.claude/projects (honors $HOME). */
|
|
18
|
+
export function claudeProjectsDir() {
|
|
19
|
+
return path.join(os.homedir(), ".claude", "projects");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Decode an encoded Claude Code project dir name back to an absolute path.
|
|
23
|
+
*
|
|
24
|
+
* Claude encodes the absolute path by replacing the leading `/` with a `-`
|
|
25
|
+
* and then replacing every subsequent `/` with `-`. For example:
|
|
26
|
+
* '-Users-you-Desktop-foo' -> '/Users/you/Desktop/foo'
|
|
27
|
+
*
|
|
28
|
+
* This is lossy for path segments that naturally contain `-`, but we do
|
|
29
|
+
* the best-effort reconstruction by:
|
|
30
|
+
* 1. Stripping the leading `-`
|
|
31
|
+
* 2. Re-prefixing with `/`
|
|
32
|
+
* 3. Replacing remaining `-` with `/`
|
|
33
|
+
*
|
|
34
|
+
* The result is a plausible absolute path suitable for display/grouping.
|
|
35
|
+
*/
|
|
36
|
+
export function decodeProjectPath(dirName) {
|
|
37
|
+
// Remove leading dash (represents the leading `/` of an absolute path)
|
|
38
|
+
const withoutLeadingDash = dirName.startsWith("-")
|
|
39
|
+
? dirName.slice(1)
|
|
40
|
+
: dirName;
|
|
41
|
+
// Re-add leading slash and replace dashes with path separators
|
|
42
|
+
return "/" + withoutLeadingDash.replace(/-/g, "/");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Collapse a real absolute path to the dash-normalized form Claude uses to
|
|
46
|
+
* encode project dir names: every `/` AND every `-` becomes `-`.
|
|
47
|
+
*
|
|
48
|
+
* Both the real path '/Users/x/dev-tools/ashlr-hub' and its lossy decode
|
|
49
|
+
* '/Users/x/dev/tools/ashlr/hub' normalize to the SAME string
|
|
50
|
+
* '-Users-x-dev-tools-ashlr-hub'. That makes dashNormalize a reliable join key
|
|
51
|
+
* between the index's REAL paths and the transcript's MANGLED decoded paths,
|
|
52
|
+
* which recovers correct grouping/labels for any repo whose name has a dash.
|
|
53
|
+
*/
|
|
54
|
+
export function dashNormalize(absPath) {
|
|
55
|
+
return absPath.replace(/[/-]/g, "-");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a (possibly mangled) decoded project path against known real paths
|
|
59
|
+
* from the index. Returns the matching real path when the dash-normalized
|
|
60
|
+
* forms agree, otherwise returns the input unchanged (best-effort decode).
|
|
61
|
+
*
|
|
62
|
+
* `realByNorm` maps dashNormalize(realPath) -> realPath and is built once by
|
|
63
|
+
* the caller so this stays O(1) per event.
|
|
64
|
+
*/
|
|
65
|
+
export function reconcileProjectPath(decodedPath, realByNorm) {
|
|
66
|
+
const match = realByNorm.get(dashNormalize(decodedPath));
|
|
67
|
+
return match ?? decodedPath;
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Internal helpers
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
/** Chunk size for the streaming reader (64 KiB). */
|
|
73
|
+
const READ_CHUNK = 64 * 1024;
|
|
74
|
+
/**
|
|
75
|
+
* Hard cap on bytes scanned per transcript (256 MiB). Transcripts can be
|
|
76
|
+
* hundreds of MB; this bounds worst-case work while still covering any
|
|
77
|
+
* realistic session. Peak memory stays ~one chunk + one line regardless.
|
|
78
|
+
*/
|
|
79
|
+
const MAX_BYTES_PER_FILE = 256 * 1024 * 1024;
|
|
80
|
+
/**
|
|
81
|
+
* Read a file line-by-line with BOUNDED memory, calling cb for each non-empty
|
|
82
|
+
* line. Instead of readFileSync + split (which loads the whole file as a string
|
|
83
|
+
* AND a full line array — pathological for 100+ MB transcripts), we pull fixed
|
|
84
|
+
* 64 KiB chunks via fs.readSync and split on newlines incrementally, carrying a
|
|
85
|
+
* partial-line buffer across chunk boundaries. Peak memory is ~one chunk plus
|
|
86
|
+
* one line, not the whole file. Never throws.
|
|
87
|
+
*/
|
|
88
|
+
function eachLine(filePath, cb) {
|
|
89
|
+
let fd;
|
|
90
|
+
try {
|
|
91
|
+
fd = fs.openSync(filePath, "r");
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return; // unreadable — skip silently
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const chunk = Buffer.allocUnsafe(READ_CHUNK);
|
|
98
|
+
let pending = ""; // partial line carried across chunk boundaries
|
|
99
|
+
let bytesRead = 0;
|
|
100
|
+
for (;;) {
|
|
101
|
+
let n;
|
|
102
|
+
try {
|
|
103
|
+
n = fs.readSync(fd, chunk, 0, READ_CHUNK, null);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
break; // read error — stop, keep whatever we collected
|
|
107
|
+
}
|
|
108
|
+
if (n <= 0)
|
|
109
|
+
break; // EOF
|
|
110
|
+
bytesRead += n;
|
|
111
|
+
pending += chunk.toString("utf8", 0, n);
|
|
112
|
+
let nl;
|
|
113
|
+
while ((nl = pending.indexOf("\n")) !== -1) {
|
|
114
|
+
const line = pending.slice(0, nl).trim();
|
|
115
|
+
pending = pending.slice(nl + 1);
|
|
116
|
+
if (line.length > 0)
|
|
117
|
+
cb(line);
|
|
118
|
+
}
|
|
119
|
+
// Bound total work per file.
|
|
120
|
+
if (bytesRead >= MAX_BYTES_PER_FILE)
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
// Flush any trailing line without a newline.
|
|
124
|
+
const last = pending.trim();
|
|
125
|
+
if (last.length > 0)
|
|
126
|
+
cb(last);
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
try {
|
|
130
|
+
fs.closeSync(fd);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// ignore
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** Extract a UsageEvent from a parsed JSONL line, or null if not applicable. */
|
|
138
|
+
function extractClaudeEvent(raw, project, sinceMs) {
|
|
139
|
+
if (typeof raw !== "object" || raw === null)
|
|
140
|
+
return null;
|
|
141
|
+
const obj = raw;
|
|
142
|
+
// Only process assistant-type events
|
|
143
|
+
if (obj["type"] !== "assistant")
|
|
144
|
+
return null;
|
|
145
|
+
// Timestamp lives at top level
|
|
146
|
+
const ts = typeof obj["timestamp"] === "string" ? obj["timestamp"] : null;
|
|
147
|
+
if (!ts)
|
|
148
|
+
return null;
|
|
149
|
+
// Filter by window early
|
|
150
|
+
try {
|
|
151
|
+
if (new Date(ts).getTime() < sinceMs)
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
// Model and usage are nested under `message`
|
|
158
|
+
const message = obj["message"];
|
|
159
|
+
if (typeof message !== "object" || message === null)
|
|
160
|
+
return null;
|
|
161
|
+
const msg = message;
|
|
162
|
+
const model = typeof msg["model"] === "string" ? msg["model"] : "";
|
|
163
|
+
if (!model)
|
|
164
|
+
return null;
|
|
165
|
+
const usage = msg["usage"];
|
|
166
|
+
if (typeof usage !== "object" || usage === null)
|
|
167
|
+
return null;
|
|
168
|
+
const u = usage;
|
|
169
|
+
// PRIVACY: extract ONLY the four numeric token counts — nothing else
|
|
170
|
+
const tokensIn = typeof u["input_tokens"] === "number" ? u["input_tokens"] : 0;
|
|
171
|
+
const tokensOut = typeof u["output_tokens"] === "number" ? u["output_tokens"] : 0;
|
|
172
|
+
const cacheRead = typeof u["cache_read_input_tokens"] === "number"
|
|
173
|
+
? u["cache_read_input_tokens"]
|
|
174
|
+
: 0;
|
|
175
|
+
const cacheWrite = typeof u["cache_creation_input_tokens"] === "number"
|
|
176
|
+
? u["cache_creation_input_tokens"]
|
|
177
|
+
: 0;
|
|
178
|
+
// Skip events with zero useful usage (e.g. pure-tool events)
|
|
179
|
+
if (tokensIn === 0 && tokensOut === 0)
|
|
180
|
+
return null;
|
|
181
|
+
return {
|
|
182
|
+
ts,
|
|
183
|
+
project,
|
|
184
|
+
model,
|
|
185
|
+
source: "claude",
|
|
186
|
+
tokensIn,
|
|
187
|
+
tokensOut,
|
|
188
|
+
cacheRead,
|
|
189
|
+
cacheWrite,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Claude Code transcript ingestion
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
function collectClaudeEvents(sinceMs) {
|
|
196
|
+
const events = [];
|
|
197
|
+
const projectsRoot = claudeProjectsDir();
|
|
198
|
+
let projectDirs;
|
|
199
|
+
try {
|
|
200
|
+
projectDirs = fs.readdirSync(projectsRoot, { withFileTypes: true })
|
|
201
|
+
.filter((d) => d.isDirectory())
|
|
202
|
+
.map((d) => d.name);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return events; // ~/.claude/projects doesn't exist or isn't readable
|
|
206
|
+
}
|
|
207
|
+
for (const dirName of projectDirs) {
|
|
208
|
+
const dirPath = path.join(projectsRoot, dirName);
|
|
209
|
+
const project = decodeProjectPath(dirName);
|
|
210
|
+
let files;
|
|
211
|
+
try {
|
|
212
|
+
files = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
for (const entry of files) {
|
|
218
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl"))
|
|
219
|
+
continue;
|
|
220
|
+
const filePath = path.join(dirPath, entry.name);
|
|
221
|
+
// Performance: skip files that haven't been modified since our window
|
|
222
|
+
try {
|
|
223
|
+
const stat = fs.statSync(filePath);
|
|
224
|
+
if (stat.mtimeMs < sinceMs)
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// Stream line-by-line; PRIVACY: parse only; never retain content
|
|
231
|
+
eachLine(filePath, (line) => {
|
|
232
|
+
let parsed;
|
|
233
|
+
try {
|
|
234
|
+
parsed = JSON.parse(line);
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return; // malformed line — skip silently
|
|
238
|
+
}
|
|
239
|
+
const event = extractClaudeEvent(parsed, project, sinceMs);
|
|
240
|
+
if (event)
|
|
241
|
+
events.push(event);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return events;
|
|
246
|
+
}
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// M4 run record ingestion
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
function collectRunEvents(sinceMs) {
|
|
251
|
+
const events = [];
|
|
252
|
+
const runsDir = path.join(os.homedir(), ".ashlr", "runs");
|
|
253
|
+
let files;
|
|
254
|
+
try {
|
|
255
|
+
files = fs.readdirSync(runsDir);
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return events; // no runs dir — fine
|
|
259
|
+
}
|
|
260
|
+
for (const name of files) {
|
|
261
|
+
if (!name.endsWith(".json"))
|
|
262
|
+
continue;
|
|
263
|
+
const filePath = path.join(runsDir, name);
|
|
264
|
+
// Performance: skip files older than window
|
|
265
|
+
try {
|
|
266
|
+
const stat = fs.statSync(filePath);
|
|
267
|
+
if (stat.mtimeMs < sinceMs)
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
let raw;
|
|
274
|
+
try {
|
|
275
|
+
raw = fs.readFileSync(filePath, "utf8");
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
let run;
|
|
281
|
+
try {
|
|
282
|
+
run = JSON.parse(raw);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
continue; // malformed — skip
|
|
286
|
+
}
|
|
287
|
+
if (typeof run !== "object" || run === null)
|
|
288
|
+
continue;
|
|
289
|
+
const r = run;
|
|
290
|
+
// Validate createdAt is within window
|
|
291
|
+
const ts = typeof r["createdAt"] === "string" ? r["createdAt"] : null;
|
|
292
|
+
if (!ts)
|
|
293
|
+
continue;
|
|
294
|
+
try {
|
|
295
|
+
if (new Date(ts).getTime() < sinceMs)
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const model = typeof r["provider"] === "string" ? r["provider"] : "unknown";
|
|
302
|
+
// Extract ONLY the numeric usage fields
|
|
303
|
+
const usage = r["usage"];
|
|
304
|
+
if (typeof usage !== "object" || usage === null)
|
|
305
|
+
continue;
|
|
306
|
+
const u = usage;
|
|
307
|
+
const tokensIn = typeof u["tokensIn"] === "number" ? u["tokensIn"] : 0;
|
|
308
|
+
const tokensOut = typeof u["tokensOut"] === "number" ? u["tokensOut"] : 0;
|
|
309
|
+
if (tokensIn === 0 && tokensOut === 0)
|
|
310
|
+
continue;
|
|
311
|
+
events.push({
|
|
312
|
+
ts,
|
|
313
|
+
project: null,
|
|
314
|
+
model,
|
|
315
|
+
source: "run",
|
|
316
|
+
tokensIn,
|
|
317
|
+
tokensOut,
|
|
318
|
+
cacheRead: 0,
|
|
319
|
+
cacheWrite: 0,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
return events;
|
|
323
|
+
}
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
// Public API
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
/**
|
|
328
|
+
* Collect normalized UsageEvents from all local sources with ts >= sinceMs.
|
|
329
|
+
*
|
|
330
|
+
* Sources:
|
|
331
|
+
* (a) ~/.claude/projects/<encoded-dir>/*.jsonl — METADATA ONLY
|
|
332
|
+
* (b) ~/.ashlr/runs/*.json — M4 RunState records
|
|
333
|
+
*
|
|
334
|
+
* Streams line-by-line; skips files older than sinceMs by mtime; tolerates
|
|
335
|
+
* malformed lines; never reads message content. Never throws.
|
|
336
|
+
*/
|
|
337
|
+
export function collectUsageEvents(sinceMs) {
|
|
338
|
+
try {
|
|
339
|
+
const claude = collectClaudeEvents(sinceMs);
|
|
340
|
+
const runs = collectRunEvents(sinceMs);
|
|
341
|
+
return [...claude, ...runs];
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return []; // belt-and-suspenders: top-level guard
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=usage-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-source.js","sourceRoot":"","sources":["../../../src/core/observability/usage-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/onboard.ts — one-command idempotent onboarding (M20).
|
|
3
|
+
*
|
|
4
|
+
* onboard(cfg, opts): detect + report + safe ensures in a single, NON-TTY-safe
|
|
5
|
+
* call. Never hangs on a prompt. Mutating steps are gated:
|
|
6
|
+
* - editor wiring: only when opts.wire
|
|
7
|
+
* - model downloads: NEVER (pull stays explicit)
|
|
8
|
+
* - shell-profile edits: NEVER
|
|
9
|
+
*
|
|
10
|
+
* Returns an OnboardResult with one OnboardStep per step, a ready flag, and
|
|
11
|
+
* crisp nextSteps guidance.
|
|
12
|
+
*/
|
|
13
|
+
import type { AshlrConfig, OnboardResult } from './types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Run the complete idempotent onboarding sequence.
|
|
16
|
+
*
|
|
17
|
+
* Guaranteed to:
|
|
18
|
+
* - Never throw (degrades to 'manual' steps on any error).
|
|
19
|
+
* - Never hang (no interactive prompts; safe in non-TTY environments).
|
|
20
|
+
* - Never auto-download models.
|
|
21
|
+
* - Never touch secrets, shell profiles, or anything outward/network.
|
|
22
|
+
* - Be idempotent (safe to re-run).
|
|
23
|
+
*/
|
|
24
|
+
export declare function onboard(cfg: AshlrConfig, opts: {
|
|
25
|
+
wire: boolean;
|
|
26
|
+
yes: boolean;
|
|
27
|
+
}): Promise<OnboardResult>;
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core/onboard.ts — one-command idempotent onboarding (M20).
|
|
3
|
+
*
|
|
4
|
+
* onboard(cfg, opts): detect + report + safe ensures in a single, NON-TTY-safe
|
|
5
|
+
* call. Never hangs on a prompt. Mutating steps are gated:
|
|
6
|
+
* - editor wiring: only when opts.wire
|
|
7
|
+
* - model downloads: NEVER (pull stays explicit)
|
|
8
|
+
* - shell-profile edits: NEVER
|
|
9
|
+
*
|
|
10
|
+
* Returns an OnboardResult with one OnboardStep per step, a ready flag, and
|
|
11
|
+
* crisp nextSteps guidance.
|
|
12
|
+
*/
|
|
13
|
+
import { existsSync, mkdirSync, symlinkSync, readlinkSync } from 'node:fs';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { join, resolve, dirname } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { saveConfig, defaultConfig } from './config.js';
|
|
18
|
+
import { listLocalModels, ollamaInstalled } from './run/model-manager.js';
|
|
19
|
+
import { detectEditors, wireEditor } from './integrations/editors.js';
|
|
20
|
+
import { getPhantomStatus } from './phantom.js';
|
|
21
|
+
import { runDoctor } from './doctor.js';
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/** Produce a passing step — never throws. */
|
|
26
|
+
function step(name, status, detail) {
|
|
27
|
+
return { name, status, detail };
|
|
28
|
+
}
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Step implementations — each is async, catches all errors, never throws
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/** Step 1: ensure ~/.ashlr/config.json exists. */
|
|
33
|
+
async function stepConfig() {
|
|
34
|
+
try {
|
|
35
|
+
// Resolve the config path from homedir() at call time so a moving HOME is
|
|
36
|
+
// honored and matches the path saveConfig()/runDoctor() actually use. The
|
|
37
|
+
// exported CONFIG_PATH constant is resolved once at module load and would
|
|
38
|
+
// point at a stale home (breaking onboarding under a relocated/test HOME).
|
|
39
|
+
const configPath = join(homedir(), '.ashlr', 'config.json');
|
|
40
|
+
if (existsSync(configPath)) {
|
|
41
|
+
return step('config', 'ok', `Config present at ${configPath}`);
|
|
42
|
+
}
|
|
43
|
+
// Create from defaults via saveConfig (loadConfig already does this, but we
|
|
44
|
+
// want explicit ensure-only semantics here — never overwrite existing).
|
|
45
|
+
const defaults = defaultConfig();
|
|
46
|
+
saveConfig(defaults);
|
|
47
|
+
return step('config', 'ok', `Created default config at ${configPath}`);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51
|
+
return step('config', 'manual', `Could not ensure config: ${msg}. Run: ashlr doctor --fix`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Step 2: detect local models — report only, NEVER auto-download. */
|
|
55
|
+
async function stepModels(cfg) {
|
|
56
|
+
try {
|
|
57
|
+
const models = await listLocalModels(cfg);
|
|
58
|
+
if (models.length > 0) {
|
|
59
|
+
const names = models.map((m) => `${m.provider}/${m.name}`).join(', ');
|
|
60
|
+
return step('models', 'detected', `Local models found: ${names}`);
|
|
61
|
+
}
|
|
62
|
+
const ollamaPresent = ollamaInstalled();
|
|
63
|
+
if (ollamaPresent) {
|
|
64
|
+
return step('models', 'manual', 'Ollama is installed but no models are loaded. Run: ashlr models pull <model> (e.g. llama3:8b)');
|
|
65
|
+
}
|
|
66
|
+
return step('models', 'manual', 'No local models detected (Ollama/LM Studio not running or not installed). Run: ashlr models pull <model>');
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
70
|
+
return step('models', 'manual', `Could not probe local models: ${msg}. Run: ashlr models pull <model>`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Step 3: detect editors; wire them when opts.wire. */
|
|
74
|
+
async function stepEditors(opts) {
|
|
75
|
+
try {
|
|
76
|
+
const editors = detectEditors();
|
|
77
|
+
if (editors.length === 0) {
|
|
78
|
+
return step('editors', 'skipped', 'No supported editors detected (claude / codex / cursor)');
|
|
79
|
+
}
|
|
80
|
+
if (!opts.wire) {
|
|
81
|
+
return step('editors', 'detected', `Detected: ${editors.join(', ')}. Re-run with --wire to register the ashlr MCP gateway.`);
|
|
82
|
+
}
|
|
83
|
+
// Wire each detected editor — backup-first + idempotent (M18).
|
|
84
|
+
const results = [];
|
|
85
|
+
for (const editor of editors) {
|
|
86
|
+
const target = editor;
|
|
87
|
+
const r = await wireEditor(target, {});
|
|
88
|
+
results.push(`${editor}: ${r.detail}`);
|
|
89
|
+
}
|
|
90
|
+
return step('editors', 'wired', results.join('; '));
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
94
|
+
return step('editors', 'manual', `Editor detection/wiring failed: ${msg}. Run: ashlr wire`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Step 4: ensure the ashlr → ~/.local/bin/ashlr symlink.
|
|
99
|
+
*
|
|
100
|
+
* Safe ensure: create only when (a) the symlink is missing AND (b) the source
|
|
101
|
+
* bin/ashlr file can be resolved from this module's location. Never modifies
|
|
102
|
+
* shell profiles. If source cannot be resolved → manual guidance.
|
|
103
|
+
*/
|
|
104
|
+
async function stepSymlink() {
|
|
105
|
+
const localBin = join(homedir(), '.local', 'bin');
|
|
106
|
+
const symlinkDest = join(localBin, 'ashlr');
|
|
107
|
+
try {
|
|
108
|
+
// Check if already present and correct.
|
|
109
|
+
if (existsSync(symlinkDest)) {
|
|
110
|
+
let target;
|
|
111
|
+
try {
|
|
112
|
+
target = readlinkSync(symlinkDest);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Not a symlink (could be a real file installed by npm) — that's fine.
|
|
116
|
+
return step('symlink', 'ok', `ashlr present at ${symlinkDest}`);
|
|
117
|
+
}
|
|
118
|
+
return step('symlink', 'ok', `Symlink present: ${symlinkDest} → ${target}`);
|
|
119
|
+
}
|
|
120
|
+
// Attempt to resolve the bin/ashlr source from this module's directory tree.
|
|
121
|
+
// __dirname equivalent for ESM: walk up from src/core → project root → bin/ashlr.
|
|
122
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
123
|
+
// src/core/onboard.ts → src/core → src → project root
|
|
124
|
+
const projectRoot = resolve(dirname(thisFile), '..', '..');
|
|
125
|
+
const binSrc = join(projectRoot, 'bin', 'ashlr');
|
|
126
|
+
if (!existsSync(binSrc)) {
|
|
127
|
+
return step('symlink', 'manual', `ashlr not yet in ~/.local/bin. Run: ./install.sh (from the ashlr-hub repo)`);
|
|
128
|
+
}
|
|
129
|
+
// Create ~/.local/bin if absent, then create the symlink.
|
|
130
|
+
mkdirSync(localBin, { recursive: true });
|
|
131
|
+
symlinkSync(binSrc, symlinkDest);
|
|
132
|
+
return step('symlink', 'ok', `Created symlink: ${symlinkDest} → ${binSrc}`);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
136
|
+
return step('symlink', 'manual', `Could not ensure symlink: ${msg}. Run: ./install.sh (from the ashlr-hub repo)`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Step 5: ensure the genome directory exists (~/.ashlr/genome).
|
|
141
|
+
* mkdir-only, never seeds/edits entries.
|
|
142
|
+
*/
|
|
143
|
+
async function stepGenome() {
|
|
144
|
+
const genomeDir = join(homedir(), '.ashlr', 'genome');
|
|
145
|
+
try {
|
|
146
|
+
if (existsSync(genomeDir)) {
|
|
147
|
+
return step('genome', 'ok', `Genome dir present at ${genomeDir}`);
|
|
148
|
+
}
|
|
149
|
+
mkdirSync(genomeDir, { recursive: true });
|
|
150
|
+
return step('genome', 'ok', `Created genome dir at ${genomeDir}`);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
154
|
+
return step('genome', 'manual', `Could not create genome dir: ${msg}. Run: ashlr doctor --fix`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Step 6: phantom status — report only, never touches secrets. */
|
|
158
|
+
async function stepPhantom() {
|
|
159
|
+
try {
|
|
160
|
+
const status = getPhantomStatus();
|
|
161
|
+
if (!status.installed) {
|
|
162
|
+
return step('phantom', 'manual', 'Phantom not installed. Install from https://phantom.sh to enable secrets management.');
|
|
163
|
+
}
|
|
164
|
+
if (!status.initialized) {
|
|
165
|
+
return step('phantom', 'detected', `Phantom v${status.version ?? 'unknown'} installed but vault not initialized. Run: phantom init`);
|
|
166
|
+
}
|
|
167
|
+
const count = status.secretNames.length;
|
|
168
|
+
return step('phantom', 'ok', `Phantom v${status.version ?? 'unknown'} — vault initialized (${count} secret${count !== 1 ? 's' : ''})`);
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
172
|
+
return step('phantom', 'manual', `Phantom status unavailable: ${msg}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Doctor checks that genuinely block the hub from running. A `fail` on one of
|
|
177
|
+
* these means setup is NOT ready.
|
|
178
|
+
*
|
|
179
|
+
* This set is intentionally EMPTY: config presence — the one true readiness
|
|
180
|
+
* prerequisite — is gated authoritatively by onboard's own `configStep` (which
|
|
181
|
+
* resolves the config path at call time), not by doctor's `config` check (which
|
|
182
|
+
* compares a module-load-time CONFIG_PATH constant and can therefore disagree
|
|
183
|
+
* under a relocated/test HOME). Every other doctor `fail` (git/ashlr-on-PATH,
|
|
184
|
+
* provider reachability, MCP wiring, etc.) is environmental/advisory: you can
|
|
185
|
+
* still `ashlr run` with cloud fallback, install a model later, or fix PATH
|
|
186
|
+
* later. Those are surfaced as guidance in nextSteps, never a hard readiness
|
|
187
|
+
* blocker. This matches the contract's `ready` = "config present + doctor has
|
|
188
|
+
* no fail that blocks running".
|
|
189
|
+
*/
|
|
190
|
+
const BLOCKING_FAIL_IDS = new Set();
|
|
191
|
+
/** Step 7: run doctor and fold roll-up into final OnboardStep. */
|
|
192
|
+
async function stepDoctor(cfg) {
|
|
193
|
+
try {
|
|
194
|
+
const report = await runDoctor(cfg);
|
|
195
|
+
const { pass, warn, fail } = report.summary;
|
|
196
|
+
const failChecks = report.checks.filter((c) => c.status === 'fail');
|
|
197
|
+
const blockingFails = failChecks.filter((c) => BLOCKING_FAIL_IDS.has(c.id));
|
|
198
|
+
const hasBlockingFail = blockingFails.length > 0;
|
|
199
|
+
const detail = `doctor: ${pass} pass, ${warn} warn, ${fail} fail`;
|
|
200
|
+
if (hasBlockingFail) {
|
|
201
|
+
const failLabels = blockingFails.map((c) => c.label).join(', ');
|
|
202
|
+
return {
|
|
203
|
+
doctorStep: step('doctor', 'manual', `${detail}. Blocking: ${failLabels}. Run: ashlr doctor --fix`),
|
|
204
|
+
hasBlockingFail: true,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// Non-blocking fails are advisory — report them but don't gate readiness.
|
|
208
|
+
if (failChecks.length > 0) {
|
|
209
|
+
const failLabels = failChecks.map((c) => c.label).join(', ');
|
|
210
|
+
return {
|
|
211
|
+
doctorStep: step('doctor', 'detected', `${detail}. Needs attention: ${failLabels}. Run: ashlr doctor --fix`),
|
|
212
|
+
hasBlockingFail: false,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
doctorStep: step('doctor', warn > 0 ? 'detected' : 'ok', detail),
|
|
217
|
+
hasBlockingFail: false,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
222
|
+
return {
|
|
223
|
+
doctorStep: step('doctor', 'manual', `Doctor check failed: ${msg}. Run: ashlr doctor`),
|
|
224
|
+
hasBlockingFail: false, // unknown state — don't block if doctor itself errors
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
// Public API
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
/**
|
|
232
|
+
* Run the complete idempotent onboarding sequence.
|
|
233
|
+
*
|
|
234
|
+
* Guaranteed to:
|
|
235
|
+
* - Never throw (degrades to 'manual' steps on any error).
|
|
236
|
+
* - Never hang (no interactive prompts; safe in non-TTY environments).
|
|
237
|
+
* - Never auto-download models.
|
|
238
|
+
* - Never touch secrets, shell profiles, or anything outward/network.
|
|
239
|
+
* - Be idempotent (safe to re-run).
|
|
240
|
+
*/
|
|
241
|
+
export async function onboard(cfg, opts) {
|
|
242
|
+
// Run all steps sequentially so each can feed into ready computation.
|
|
243
|
+
// Steps that are independent in principle are still sequential here to avoid
|
|
244
|
+
// interleaved output and to keep the order deterministic and predictable.
|
|
245
|
+
const configStep = await stepConfig();
|
|
246
|
+
const modelsStep = await stepModels(cfg);
|
|
247
|
+
const editorsStep = await stepEditors(opts);
|
|
248
|
+
const symlinkStep = await stepSymlink();
|
|
249
|
+
const genomeStep = await stepGenome();
|
|
250
|
+
const phantomStep = await stepPhantom();
|
|
251
|
+
const { doctorStep, hasBlockingFail } = await stepDoctor(cfg);
|
|
252
|
+
const steps = [
|
|
253
|
+
configStep,
|
|
254
|
+
modelsStep,
|
|
255
|
+
editorsStep,
|
|
256
|
+
symlinkStep,
|
|
257
|
+
genomeStep,
|
|
258
|
+
phantomStep,
|
|
259
|
+
doctorStep,
|
|
260
|
+
];
|
|
261
|
+
// ready = config is ok AND no doctor blocking failures.
|
|
262
|
+
const configOk = configStep.status === 'ok';
|
|
263
|
+
const ready = configOk && !hasBlockingFail;
|
|
264
|
+
// Build nextSteps guidance.
|
|
265
|
+
const nextSteps = [];
|
|
266
|
+
if (!configOk) {
|
|
267
|
+
nextSteps.push('Fix config first: ashlr doctor --fix');
|
|
268
|
+
}
|
|
269
|
+
if (modelsStep.status === 'manual') {
|
|
270
|
+
nextSteps.push('Pull a local model: ashlr models pull llama3:8b');
|
|
271
|
+
}
|
|
272
|
+
if (editorsStep.status === 'detected' && !opts.wire) {
|
|
273
|
+
nextSteps.push('Wire your editor: ashlr init --wire');
|
|
274
|
+
}
|
|
275
|
+
if (symlinkStep.status === 'manual') {
|
|
276
|
+
nextSteps.push('Install the CLI symlink: ./install.sh (from the ashlr-hub repo)');
|
|
277
|
+
}
|
|
278
|
+
// Surface doctor guidance for any remaining issues (blocking or advisory) —
|
|
279
|
+
// doctorStep is 'manual' on a blocking fail and 'detected' when there are
|
|
280
|
+
// non-blocking fails/warns that still warrant a `doctor --fix` pass.
|
|
281
|
+
if (doctorStep.status === 'manual' || hasBlockingFail) {
|
|
282
|
+
nextSteps.push('Fix doctor issues: ashlr doctor --fix');
|
|
283
|
+
}
|
|
284
|
+
// Always end with the crisp "you're set up" guidance.
|
|
285
|
+
nextSteps.push('try: ashlr run / ashlr swarm / ashlr tui');
|
|
286
|
+
return { steps, ready, nextSteps };
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=onboard.js.map
|