myshell-tools 2.15.0 → 3.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 +83 -1
- package/README.md +39 -44
- package/dist/cli.js +33 -22
- package/dist/cli.js.map +1 -1
- package/dist/commands/cost.js +44 -21
- package/dist/commands/cost.js.map +1 -1
- package/dist/commands/doctor.d.ts +17 -4
- package/dist/commands/doctor.js +62 -35
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/login.d.ts +18 -10
- package/dist/commands/login.js +81 -109
- package/dist/commands/login.js.map +1 -1
- package/dist/core/json-envelope.js +31 -3
- package/dist/core/json-envelope.js.map +1 -1
- package/dist/core/native-session.d.ts +57 -0
- package/dist/core/native-session.js +68 -0
- package/dist/core/native-session.js.map +1 -0
- package/dist/core/orchestrate.js +106 -43
- package/dist/core/orchestrate.js.map +1 -1
- package/dist/core/types.d.ts +26 -0
- package/dist/infra/atomic.d.ts +9 -1
- package/dist/infra/atomic.js +12 -2
- package/dist/infra/atomic.js.map +1 -1
- package/dist/infra/config.d.ts +8 -0
- package/dist/infra/config.js.map +1 -1
- package/dist/infra/credentials.d.ts +69 -3
- package/dist/infra/credentials.js +118 -9
- package/dist/infra/credentials.js.map +1 -1
- package/dist/infra/health.d.ts +57 -0
- package/dist/infra/health.js +96 -0
- package/dist/infra/health.js.map +1 -0
- package/dist/infra/insights.d.ts +14 -0
- package/dist/infra/insights.js +31 -0
- package/dist/infra/insights.js.map +1 -1
- package/dist/interface/menu.d.ts +70 -5
- package/dist/interface/menu.js +292 -88
- package/dist/interface/menu.js.map +1 -1
- package/dist/interface/render.js +20 -13
- package/dist/interface/render.js.map +1 -1
- package/dist/providers/claude.d.ts +24 -8
- package/dist/providers/claude.js +77 -15
- package/dist/providers/claude.js.map +1 -1
- package/dist/providers/codex-parse.js +14 -2
- package/dist/providers/codex-parse.js.map +1 -1
- package/dist/providers/codex.d.ts +13 -1
- package/dist/providers/codex.js +19 -8
- package/dist/providers/codex.js.map +1 -1
- package/dist/providers/detect.js +22 -1
- package/dist/providers/detect.js.map +1 -1
- package/dist/providers/port.d.ts +15 -0
- package/dist/providers/registry.d.ts +8 -4
- package/dist/providers/registry.js +7 -6
- package/dist/providers/registry.js.map +1 -1
- package/dist/ui/help.d.ts +17 -0
- package/dist/ui/help.js +106 -0
- package/dist/ui/help.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,7 +9,89 @@ and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.
|
|
|
9
9
|
### Pending
|
|
10
10
|
- Live cross-vendor review demonstration (requires an authenticated Codex CLI).
|
|
11
11
|
- Cross-OS CI execution (requires a public remote).
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
## [3.2.0]
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **Tokens, not dollars, on the everyday UI.** myshell-tools drives your *subscription* CLIs (flat fee), so per-task dollar figures were misleading — they don't map to subscription billing and read as bloat. The control-panel status line and the live per-task output now show **real, measured token counts**; the always-on money meter ("Today: $… · session so far: $…") is gone. The `tier-done` and final-summary lines show tokens; the control-panel line shows tasks + tokens.
|
|
17
|
+
- **`cost` reframed as "usage & efficiency".** It now leads with real tokens (overall + per model) and a **billing-agnostic routing-efficiency ratio** ("routing picked cheaper-tier models — ~N× less than always-flagship"), which is honest under a subscription. The dollar estimate is demoted to a clearly-captioned section: **"Estimated cost — API-equivalent (list price), not your subscription bill"** — and both the routed and always-flagship figures use the **same basis** (list price × tokens), so they're apples-to-apples and internally consistent (routed never exceeds flagship). The previous mix of a provider-reported total against a list-price counterfactual could read as a contradiction; that's resolved. New pure `formatTokens` helper; `SpendSummary` gained `todayTokens`/`totalTokens`; the `tier-done` event carries real `inputTokens`/`outputTokens`.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **Version status in the header** — the control-panel title now always tells you where you stand: `myshell-tools v3.2.0 (latest)` when current, or `myshell-tools v3.2.0 → 3.3.0 available` when a newer release exists. No more guessing whether you're up to date. (`versionStatusLabel`, pure + tested.)
|
|
21
|
+
- **npx-awareness** — when run via `npx myshell-tools`, the tool now detects it (`isRunningUnderNpx`) and is honest about updates: instead of silently running a global install that the next `npx` invocation would ignore (npx re-serves its own cache), it shows `Install globally to stay current: npm install -g myshell-tools@latest`. Silent auto-update and the `[u]` key are suppressed under npx because they cannot persist there.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- The update banner and `[u]` Update-now action appear **only when a newer version is genuinely available** (unchanged), and are now also gated on the update being able to persist (not under npx).
|
|
25
|
+
|
|
26
|
+
### Performance
|
|
27
|
+
- **Control-panel menu no longer re-parses the ledger on every keystroke.** The spend summary is computed once and cached, and only refreshed after a task actually runs (the only time the ledger changes). Previously each keypress re-read and re-parsed the unbounded `ledger.jsonl`; on an active ledger that was tens-to-hundreds of ms of avoidable latency per keystroke. The menu hot path is now O(1) in ledger size.
|
|
28
|
+
|
|
29
|
+
### Added (experimental, opt-in)
|
|
30
|
+
- **Native session continuity for Claude and Codex (`nativeSessions`, default OFF).** The default path replays a compacted history block into every turn's prompt so stateless `-p`/`exec` calls have context — correct and provider-portable, but it re-sends prior context each turn. When enabled, a conversation that stays on one provider reuses that provider's *native* session and the replayed history is skipped — better context fidelity and less re-sent context (matters most for subscription rate-limit headroom; it isn't a dollar saving on a flat plan). Two id models, handled transparently:
|
|
31
|
+
- **Claude** — we choose the id (the conversation id): `--session-id` to establish, `--resume` to continue.
|
|
32
|
+
- **Codex** — Codex generates its own thread id; myshell-tools captures it from the `thread.started` event, persists it on the turn in the conversation log, and resumes via `codex exec resume <thread-id>` on the next Codex turn.
|
|
33
|
+
|
|
34
|
+
If a turn routes to a provider with no active native session, it transparently falls back to history replay — so switching providers never loses context. Enable via Settings → `[4] Native sessions` or `"nativeSessions": true`.
|
|
35
|
+
- **Verification:** the planning logic, the Claude/Codex CLI-arg construction, the Codex thread-id capture (contract test against the recorded transcript), and the persist-on-the-turn behavior are all unit/contract-tested. The one thing only a live CLI can prove — that resuming actually carries context — is a **gated integration test** you run with your own authenticated CLIs: `MYSHELL_NATIVE_SESSION_E2E=1 npm run test:integration` (covers both Claude and Codex). Off until you opt in; the feature defaults off until you've confirmed it on your setup. opencode has no documented resume flag yet, so it stays on history replay.
|
|
36
|
+
- **Per-command help.** `myshell-tools <command> --help` now shows focused, command-specific help (e.g. `login --help` explains `--code`/`--browser` and the container flow; `cost --help` is honest about subscription-vs-API billing) instead of the generic command list. Bare `--help` still shows the global list. Pure `commandHelpText`, unit-tested.
|
|
37
|
+
- **Self-health, surfaced automatically — no command to run.** The control panel now evaluates its own environment at startup (Node version, state-directory writability, pricing-table staleness) and shows a short, actionable warning **only when something is actually wrong**. No problems → nothing shown. Pure `evaluateHealth` (fully unit-tested) + a one-shot `probeStateWritable`. The diagnostics that were already visible in the header (provider install/auth, Claude-token expiry) are not duplicated.
|
|
38
|
+
- **`doctor --fix` offers to refresh an expiring Claude token.** When Claude is signed in and the stored `sk-ant-oat…` token is expired or inside the 14-day warning window, the fix pass offers a one-keypress re-login — closing the gap where the expiry was *reported* but never *actionable*.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- **Retired the `doctor` name from the user-facing surface.** "Doctor" was borrowed jargon, and *requiring* a diagnostic command is itself friction — health now surfaces on its own (see above). The command still exists as a hidden, scriptable health check for support/CI, reachable as `status`, `check`, or `doctor` (the old name still works for muscle-memory and existing scripts); it's just no longer advertised in `--help`. Its report header now reads "environment health" rather than "doctor".
|
|
42
|
+
|
|
43
|
+
### Why
|
|
44
|
+
- Users running the convenience `npx` path were landing on a stale cached version (e.g. 2.8.0) and could not understand why "auto-update" never advanced them — npx ignores the global install our updater performs. The tool now names that situation and points to the durable fix instead of failing silently.
|
|
45
|
+
|
|
46
|
+
## [3.1.0]
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- **Claude token lifetime awareness** — `claude setup-token` mints an `sk-ant-oat…` token valid ~1 year; the tool now records when it was captured and surfaces the remaining lifetime. `doctor` shows `token: expires ~YYYY-MM-DD (NNN days left)`, and the control-panel header shows a concise warning only when the token is near expiry or already expired (`claudeTokenStatus`, pure + tested). No nagging when the token is healthy.
|
|
50
|
+
|
|
51
|
+
## [3.0.0]
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- **opencode contract test** — parser pinned to a recorded real `opencode run --format json` transcript.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
- **Wizard polish** — first-run prompts are simple `(y/n)` / `(Y/n)` instead of requiring the user to type `yes`.
|
|
58
|
+
- **Chat feels like a chat** — the conversation prompt is a bare `> ` (not `myshell-tools>`).
|
|
59
|
+
- **Claude token env-scoping** — the stored OAuth token is injected only into the provider child process env (and only when not already present), never globally exported.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
- **Browser→code login retry** — when the browser/localhost OAuth flow can't work (containers/SSH), the login offers an interactive `--code` retry.
|
|
63
|
+
- **Auth-aware routing** — `route()` prefers authenticated + available providers; auth errors short-circuit (no wasted failover/escalation).
|
|
64
|
+
|
|
65
|
+
## [2.17.0]
|
|
66
|
+
|
|
67
|
+
### Security
|
|
68
|
+
- Credentials file (~/.myshell-tools/credentials.json) is now created with mode 0600 and its directory 0700 from the first write (atomicWrite gained an optional mode), closing a brief world-readable window on shared systems.
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
- opencode now advertises a model per tier so routing never picks a model it does not have.
|
|
72
|
+
- run (one-shot) now respects your Settings mode (cost-saver/balanced/quality-first) instead of always using the default policy.
|
|
73
|
+
- Environment detection runs once per launch (was duplicated: 6 provider --version spawns, now 3).
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
- Honest labels: opencode shows ready (free models) (not signed in); doctor shows free models (no sign-in needed); the cost total is labeled provider-reported where available, otherwise estimated from list prices (Codex costs are estimates).
|
|
77
|
+
- README de-staled (auto-update default-on + (Y/n) + MYSHELL_NO_UPDATE documented; alpha roadmap table removed; brittle test-count claims replaced).
|
|
78
|
+
- Architecture guards strengthened (no-orphan logic; core purity now also forbids new Date(/node:os/node:crypto) and two weak tests made assertive.
|
|
79
|
+
|
|
80
|
+
## [2.16.0]
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
- **Login no longer garbles the screen**: the Claude code-method sign-in now runs claude setup-token with inherited stdio (clean native animation) and uses the robust paste prompt, instead of piping/echoing the output (which turned the spinner into a scroll of repeated banners). Honest error on non-zero exit.
|
|
84
|
+
- **Auth errors short-circuit**: a not-signed-in provider no longer burns failover + tier escalation (3 failed attempts) before surfacing — it stops after one attempt and the conversation offers inline re-login immediately.
|
|
85
|
+
- **Orchestrator edge cases**: reviewer escalate at the top tier no longer loops silently; a failover event is never shown when no attempt remains to honor it; reviewer revise notes are now applied on retries at any tier; the JSON-envelope scanner is string-aware (braces inside string values no longer break confidence/verdict parsing).
|
|
86
|
+
- **Ctrl+C x2 returns to menu even mid-task** (was dropped while a task was running).
|
|
87
|
+
- **No dead-end runs**: starting a task with no signed-in provider now prompts you to sign in instead of failing through the tiers.
|
|
88
|
+
- **Inline re-login retries with refreshed auth** (was reusing stale provider state).
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
- **Conversation feels like a chat**: the per-message prompt is now a plain > (not myshell-tools>), and the verbose Classified: routing line is hidden unless MYSHELL_DEBUG is set — the model's reply is the focus.
|
|
92
|
+
|
|
93
|
+
### Security
|
|
94
|
+
- Credentials file is created with 0600 and its dir 0700 from the start (no world-readable race) — see 2.16.x.
|
|
13
95
|
|
|
14
96
|
## [2.15.0]
|
|
15
97
|
|
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**Hierarchical, multi-provider AI orchestration for your shell — over the CLIs you already use.**
|
|
4
4
|
|
|
5
|
-
`myshell-tools` routes each task to the *cheapest* model likely to succeed, runs it on your real codebase, optionally has a **different vendor** review the result, and shows you exactly what it did and
|
|
5
|
+
`myshell-tools` routes each task to the *cheapest* model likely to succeed, runs it on your real codebase, optionally has a **different vendor** review the result, and shows you exactly what it did and how many real tokens it used — with **no fabricated data, ever**.
|
|
6
6
|
|
|
7
|
-
> **Status: `2.
|
|
7
|
+
> **Status: `3.2.0` — honest, tested, and real.** Claude, Codex, and opencode (experimental) all work, provider auth is detected for real, and the header always shows whether you're on the latest version (`(latest)` or `→ x.y.z available`).
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -38,13 +38,14 @@ myshell-tools
|
|
|
38
38
|
|
|
39
39
|
Using one frontier model for everything is wasteful (renaming a variable doesn't need Opus) and single‑model output has blind spots. `myshell-tools` addresses both, honestly:
|
|
40
40
|
|
|
41
|
-
- **Cost‑aware routing** — trivial work goes to the cheap tier (Haiku / GPT‑5 mini), real implementation to the mid tier, hard calls to the flagship.
|
|
41
|
+
- **Cost‑aware routing** — trivial work goes to the cheap tier (Haiku / GPT‑5 mini), real implementation to the mid tier, hard calls to the flagship. The efficiency shows up as a billing‑agnostic ratio (how many flagship tokens you avoided), not a dollar figure — because you're on a subscription, not metered API billing.
|
|
42
42
|
- **Efficiency modes** — three policy presets control the cost/quality trade-off:
|
|
43
43
|
- `cost-saver` — routes to the cheapest capable model for each tier; only runs the cross-vendor review pass on *critical*-risk tasks (not every IC call). An optional `maxCostUsd` cap halts further escalation/review once spend reaches the limit.
|
|
44
44
|
- `balanced` — the default; routes intelligently and reviews high-risk work.
|
|
45
45
|
- `quality-first` — always reviews IC output for high/critical tasks, regardless of cost.
|
|
46
46
|
- **Cross‑vendor adversarial review** — a *different vendor* checks the first model's output (Codex reviewing Claude, or vice‑versa). Different families, different blind spots. Review gating depends on mode; see Efficiency modes above.
|
|
47
47
|
- **Multi-turn context continuity** — follow-up messages carry real context. Prior conversation turns are compacted into a bounded history block (~6 k chars, most recent 12 turns) and replayed to the model, so it actually knows what was said earlier. Confidence envelopes are stripped before replay to save tokens.
|
|
48
|
+
- **Native session continuity** *(experimental, opt-in, default off)* — when enabled, a conversation that stays on one provider reuses that provider's *native* session instead of replaying the history block, for better fidelity and less re-sent context. **Claude** uses a session id we choose (the conversation id); **Codex** has its generated thread id captured (from `thread.started`), persisted on the turn, and resumed via `codex exec resume`. Falls back to history replay automatically when a turn routes to a provider without an active session. Because the live behavior depends on your CLI, it ships off with a gated verification test covering both providers — `MYSHELL_NATIVE_SESSION_E2E=1 npm run test:integration` — and you enable it (Settings → Native sessions) only after it passes on your setup.
|
|
48
49
|
- **Container / SSH sign-in** — `myshell-tools login` auto-detects headless and cloud-IDE environments (Replit, Codespaces, Gitpod, SSH sessions) and switches to a no-localhost sign-in flow automatically. Force either flow with `--code` or `--browser`. For Claude, the tool prompts you to paste the `sk-ant-oat…` OAuth token printed by `claude setup-token`, stores it locally (mode 0600), and injects it at startup — no manual env-var wiring needed. For Codex, a device-code flow is used.
|
|
49
50
|
- **opencode provider (experimental)** — auto-detected; works instantly with free hosted models (no keys). `[o]` is always visible in the Auth section: it installs `opencode-ai` (with consent) if missing, then runs `opencode auth login`. Route to it explicitly or let the policy fall back to it automatically.
|
|
50
51
|
- **Routing prefers advertised models** — detection passes each provider's actual model list to `route()`, which picks the cheapest model the CLI *actually has*, not just the cheapest in the pricing table. Falls back gracefully if the advertised list doesn't match any pricing entry.
|
|
@@ -75,7 +76,7 @@ You need **one** to start; install **both** claude and codex to unlock cross‑v
|
|
|
75
76
|
|
|
76
77
|
### `npx` vs. `npm install -g` — which to choose?
|
|
77
78
|
|
|
78
|
-
`npx myshell-tools` is convenient for a one-off run but **caches the downloaded version** — subsequent invocations reuse the cache and **will not pick up new releases** automatically.
|
|
79
|
+
`npx myshell-tools` is convenient for a one-off run but **caches the downloaded version** — subsequent invocations reuse the cache and **will not pick up new releases** automatically. The tool detects when it's running under npx and, if a newer version exists, tells you exactly that instead of pretending to self-update (a global install run from an npx process is ignored by the next `npx` invocation). If you're stuck on an old version via npx, clear the cache (`rm -rf ~/.npm/_npx`) or — better — install globally:
|
|
79
80
|
|
|
80
81
|
For day-to-day use, a global install is recommended:
|
|
81
82
|
|
|
@@ -92,11 +93,13 @@ The globally-installed CLI includes the **update notifier**: it checks the npm r
|
|
|
92
93
|
|
|
93
94
|
Press `u` to install the update in-place (`npm install -g myshell-tools@latest`). No relaunch is forced — restart the CLI when you're ready.
|
|
94
95
|
|
|
95
|
-
You can also enable **auto-update** so the CLI updates and relaunches itself silently at startup
|
|
96
|
+
You can also enable **auto-update** so the CLI updates and relaunches itself silently at startup. Auto-update is **on by default** — the first-run prompt asks `Keep myshell-tools up to date automatically? (Y/n)` and defaults to yes.
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
98
|
+
To opt out:
|
|
99
|
+
- During first-run setup: answer `n` to the `Keep myshell-tools up to date automatically? (Y/n)` prompt.
|
|
100
|
+
- In the control panel: `[s] Settings → [3] Auto-update: on → off`.
|
|
101
|
+
- Or set `"autoUpdate": false` in `~/.myshell-tools/config.json`.
|
|
102
|
+
- Or set `MYSHELL_NO_UPDATE=1` in your environment to disable auto-update permanently without changing config.
|
|
100
103
|
|
|
101
104
|
To update manually at any time:
|
|
102
105
|
|
|
@@ -128,7 +131,6 @@ Commands:
|
|
|
128
131
|
run <task...> Run a one-shot task and exit
|
|
129
132
|
repl Plain line REPL (no menu)
|
|
130
133
|
login [prov] Sign in to a provider (claude or codex) via its own OAuth
|
|
131
|
-
doctor Check providers, auth, environment
|
|
132
134
|
cost Show real spend + the cost-routing counterfactual
|
|
133
135
|
|
|
134
136
|
Options:
|
|
@@ -142,19 +144,26 @@ These are **actual, unedited** outputs (your costs/timings will differ):
|
|
|
142
144
|
|
|
143
145
|
```text
|
|
144
146
|
$ myshell-tools run "what is 2 plus 2"
|
|
145
|
-
Classified: worker tier, low risk — tier: worker keyword 'what is'; risk: defaulting to low
|
|
146
147
|
▶ WORKER (claude/claude-haiku-4-5) attempt 1
|
|
147
148
|
2 plus 2 equals 4.
|
|
148
|
-
✓ tier done — confidence: 100%,
|
|
149
|
-
Success — tier: worker,
|
|
149
|
+
✓ tier done — confidence: 100%, 312 tokens, duration: 5648ms
|
|
150
|
+
Success — tier: worker, 312 tokens, attempts: 1, session: 0dbfe2e3-…
|
|
150
151
|
```
|
|
151
152
|
|
|
152
|
-
The confidence (`100%`) is **parsed from the model's own structured reply**, not invented. The
|
|
153
|
+
The confidence (`100%`) is **parsed from the model's own structured reply**, not invented. The token count is the **CLI's own reported usage** — real and measured. Because myshell-tools drives your *subscription* CLIs (not metered API keys), the hot path shows tokens, not dollars; a per-task dollar figure wouldn't map to flat subscription billing. If you want a rough API-equivalent cost estimate, it lives in `myshell-tools cost`.
|
|
153
154
|
|
|
154
|
-
### Health
|
|
155
|
+
### Health — automatic, no command needed
|
|
156
|
+
|
|
157
|
+
The control panel checks its own environment on every launch (Node version, whether the state directory is writable, pricing freshness) and shows a short, actionable warning **only when something is actually wrong**. When everything is fine, it stays quiet — you never run a "doctor" command.
|
|
158
|
+
|
|
159
|
+
If you want the full report explicitly (handy for support threads or CI), it's still there as a hidden, scriptable command — `status`, `check`, or `doctor`:
|
|
155
160
|
|
|
156
161
|
```text
|
|
157
|
-
$ myshell-tools
|
|
162
|
+
$ myshell-tools status
|
|
163
|
+
myshell-tools — environment health
|
|
164
|
+
Platform: linux
|
|
165
|
+
Node: v22.19.0
|
|
166
|
+
...
|
|
158
167
|
Providers
|
|
159
168
|
✓ claude — installed, version: 2.1.157 (Claude Code)
|
|
160
169
|
auth: signed in (pro)
|
|
@@ -163,21 +172,25 @@ Providers
|
|
|
163
172
|
Ready — at least one provider is available.
|
|
164
173
|
```
|
|
165
174
|
|
|
166
|
-
###
|
|
175
|
+
### Usage & efficiency (`cost`)
|
|
176
|
+
|
|
177
|
+
This is a *subscription* tool, so the everyday UI shows **real, measured tokens** — never per-task dollars, which wouldn't map to flat subscription billing. The on-demand `cost` view leads with tokens and a **billing-agnostic routing-efficiency ratio**, then offers a clearly-captioned API-equivalent dollar estimate for anyone who wants the magnitude:
|
|
167
178
|
|
|
168
179
|
```text
|
|
169
180
|
$ myshell-tools cost
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
181
|
+
myshell-tools — usage & efficiency
|
|
182
|
+
Tasks run: 3
|
|
183
|
+
Tokens used: 12.4k (real, measured)
|
|
184
|
+
Per-model usage
|
|
185
|
+
claude-haiku-4-5: 2 tasks, 4.1k tokens
|
|
186
|
+
claude-sonnet-4-6: 1 task, 8.3k tokens
|
|
187
|
+
Routing efficiency
|
|
188
|
+
Routing picked cheaper-tier models where it could — ~6.3× less than sending every task to the flagship (claude-opus-4-7).
|
|
189
|
+
Estimated cost — API-equivalent (list price), not your subscription bill
|
|
190
|
+
Routed: ~$0.0020 · always-flagship: ~$0.0126
|
|
178
191
|
```
|
|
179
192
|
|
|
180
|
-
The
|
|
193
|
+
The **efficiency ratio is honest under a subscription** (it compares flagship tokens you avoided, not dollars you were charged). The dollar figures are explicitly labeled an *API-equivalent estimate* — not your actual bill — and both use the **same basis** (list price × tokens), so "routed vs always-flagship" is apples-to-apples and internally consistent.
|
|
181
194
|
|
|
182
195
|
---
|
|
183
196
|
|
|
@@ -204,7 +217,7 @@ classify ─▶ route(cheapest tier) ─▶ run ─▶ assess
|
|
|
204
217
|
This is a ground‑up rebuild whose first principle is: **the tool never shows fabricated, mocked, or randomized data as if it were real.** It's enforced, not promised:
|
|
205
218
|
|
|
206
219
|
- **Architecture guard tests** fail the build if the UI/command layers contain hardcoded "AI responses", fake metrics, or a digit‑then‑`%` literal; if the orchestration core touches the filesystem, clock, or RNG directly; or if any module other than the entry point can terminate the process.
|
|
207
|
-
- **
|
|
220
|
+
- **An extensive unit + architecture-guard suite** (plus contract tests with parsers pinned to *recorded real transcripts*), with `tsc --strict`, ESLint, and a clean `npm pack` checked in CI across Windows / macOS / Linux.
|
|
208
221
|
|
|
209
222
|
---
|
|
210
223
|
|
|
@@ -239,24 +252,6 @@ npm run build # tsc → dist/
|
|
|
239
252
|
|
|
240
253
|
---
|
|
241
254
|
|
|
242
|
-
## Status & roadmap
|
|
243
|
-
|
|
244
|
-
Honest snapshot of `2.0.0-alpha.0`:
|
|
245
|
-
|
|
246
|
-
| Area | State |
|
|
247
|
-
| --- | --- |
|
|
248
|
-
| Core routing + escalation + cross‑vendor review loop | ✅ implemented & unit‑proven |
|
|
249
|
-
| Claude adapter | ✅ live, validated end‑to‑end on real models |
|
|
250
|
-
| Codex adapter | ✅ built; auto‑activates once `codex` is installed + authed |
|
|
251
|
-
| opencode adapter (experimental) | ✅ auto-detected; free models work without keys |
|
|
252
|
-
| Routing prefers advertised models (never routes to unavailable model) | ✅ |
|
|
253
|
-
| `doctor` / `cost` / REPL / streaming UI | ✅ |
|
|
254
|
-
| Live cross‑vendor demonstration | ⏳ pending Codex auth |
|
|
255
|
-
| Cross‑OS CI run | ⏳ pending a public remote |
|
|
256
|
-
| npm publish | ⏳ alpha |
|
|
257
|
-
|
|
258
|
-
---
|
|
259
|
-
|
|
260
255
|
## License
|
|
261
256
|
|
|
262
257
|
MIT — see [LICENSE](LICENSE).
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import { execa } from 'execa';
|
|
|
10
10
|
import { systemClock } from './infra/clock.js';
|
|
11
11
|
import { createSessionWriter } from './infra/session.js';
|
|
12
12
|
import { createLedger } from './infra/ledger.js';
|
|
13
|
-
import { DEFAULT_POLICY } from './core/policy.js';
|
|
13
|
+
import { DEFAULT_POLICY, POLICY_PRESETS } from './core/policy.js';
|
|
14
14
|
import { runTask } from './interface/run.js';
|
|
15
15
|
import { startRepl } from './interface/repl.js';
|
|
16
16
|
import { startMenu } from './interface/menu.js';
|
|
@@ -19,14 +19,16 @@ import { detectEnvironment } from './providers/detect.js';
|
|
|
19
19
|
import { createFileConversationStore } from './infra/conversations.js';
|
|
20
20
|
import { loadConfig } from './infra/config.js';
|
|
21
21
|
import { checkForUpdate } from './infra/update-check.js';
|
|
22
|
+
import { evaluateHealth, probeStateWritable } from './infra/health.js';
|
|
23
|
+
import { isPricingStale } from './infra/pricing.js';
|
|
22
24
|
import { runDoctor } from './commands/doctor.js';
|
|
23
25
|
import { runCost } from './commands/cost.js';
|
|
24
26
|
import { runLogin } from './commands/login.js';
|
|
25
27
|
import { runInstall } from './commands/install.js';
|
|
26
28
|
import { banner } from './ui/banner.js';
|
|
29
|
+
import { commandHelpText } from './ui/help.js';
|
|
27
30
|
import { createSpinner } from './ui/spinner.js';
|
|
28
31
|
import { dim } from './ui/theme.js';
|
|
29
|
-
import { applyStoredCredentials } from './infra/credentials.js';
|
|
30
32
|
const require = createRequire(import.meta.url);
|
|
31
33
|
const pkg = require('../package.json');
|
|
32
34
|
const version = pkg.version;
|
|
@@ -47,9 +49,6 @@ Commands:
|
|
|
47
49
|
Add --code to use the no-localhost flow (paste a code for
|
|
48
50
|
claude, device code for codex) — best inside containers /
|
|
49
51
|
over SSH. Add --browser to force the localhost flow.
|
|
50
|
-
doctor [--fix] Check provider installation, auth, and environment health.
|
|
51
|
-
Add --fix to interactively install missing providers and
|
|
52
|
-
sign in to unauthenticated ones.
|
|
53
52
|
cost Show real spend from the ledger with a per-model breakdown
|
|
54
53
|
install Write a guarded startup hook to your shell rc file so new
|
|
55
54
|
interactive shells launch myshell-tools automatically
|
|
@@ -63,12 +62,9 @@ Examples:
|
|
|
63
62
|
|
|
64
63
|
Repository: https://github.com/hey-vera/myshell-tools
|
|
65
64
|
`;
|
|
66
|
-
/** Build the orchestration dependencies
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
buildProviders(cwd),
|
|
70
|
-
detectEnvironment(),
|
|
71
|
-
]);
|
|
65
|
+
/** Build the orchestration dependencies from a pre-detected EnvironmentStatus. */
|
|
66
|
+
function buildDeps(cwd, env, policy = DEFAULT_POLICY) {
|
|
67
|
+
const providers = buildProviders(cwd, env);
|
|
72
68
|
// Populate advertised model lists from detection so route() can prefer a
|
|
73
69
|
// model the provider CLI actually has. Only include installed providers.
|
|
74
70
|
const availableModels = {};
|
|
@@ -94,7 +90,7 @@ async function buildDeps(cwd) {
|
|
|
94
90
|
clock: systemClock,
|
|
95
91
|
session: createSessionWriter({ cwd, id: systemClock.uuid() }),
|
|
96
92
|
ledger: createLedger({ cwd }),
|
|
97
|
-
policy
|
|
93
|
+
policy,
|
|
98
94
|
providers,
|
|
99
95
|
cwd,
|
|
100
96
|
sandbox: 'workspace-write',
|
|
@@ -114,13 +110,12 @@ function welcome(deps, color) {
|
|
|
114
110
|
'(diagnose: myshell-tools doctor)', color);
|
|
115
111
|
}
|
|
116
112
|
async function main() {
|
|
117
|
-
// Inject any previously-saved Claude OAuth token into process.env before any
|
|
118
|
-
// detection or provider spawn so `claude auth status` and `claude -p …` both
|
|
119
|
-
// see it without the user needing to export CLAUDE_CODE_OAUTH_TOKEN manually.
|
|
120
|
-
await applyStoredCredentials(process.env);
|
|
121
113
|
const args = process.argv.slice(2);
|
|
122
114
|
if (args.includes('--help') || args.includes('-h')) {
|
|
123
|
-
|
|
115
|
+
// Focused per-command help (e.g. `login --help`) when the first arg is a
|
|
116
|
+
// known command; otherwise the global command list.
|
|
117
|
+
const cmdHelp = args[0] !== undefined ? commandHelpText(args[0]) : null;
|
|
118
|
+
process.stdout.write(cmdHelp ?? HELP);
|
|
124
119
|
process.exit(0);
|
|
125
120
|
}
|
|
126
121
|
if (args.includes('--version') || args.includes('-v')) {
|
|
@@ -148,7 +143,11 @@ async function main() {
|
|
|
148
143
|
const provider = rest.find((a) => !a.startsWith('-'));
|
|
149
144
|
process.exit(await runLogin(out, provider, method !== undefined ? { method } : undefined));
|
|
150
145
|
}
|
|
151
|
-
|
|
146
|
+
// Health check — surfaced automatically in the control panel, so this is no
|
|
147
|
+
// longer an advertised command. Kept as a hidden, scriptable entry point for
|
|
148
|
+
// support/CI; `status` and `check` are friendlier aliases for the old
|
|
149
|
+
// `doctor` name (which still works for muscle-memory / existing scripts).
|
|
150
|
+
if (args[0] === 'doctor' || args[0] === 'status' || args[0] === 'check') {
|
|
152
151
|
const fix = args.includes('--fix');
|
|
153
152
|
process.exit(await runDoctor(out, fix ? { fix: true } : undefined));
|
|
154
153
|
}
|
|
@@ -168,7 +167,9 @@ async function main() {
|
|
|
168
167
|
process.stderr.write('myshell-tools run: expected a task description\n');
|
|
169
168
|
process.exit(1);
|
|
170
169
|
}
|
|
171
|
-
const
|
|
170
|
+
const [env, config] = await Promise.all([detectEnvironment(), loadConfig()]);
|
|
171
|
+
const policy = POLICY_PRESETS[config.mode ?? 'balanced'];
|
|
172
|
+
const deps = buildDeps(cwd, env, policy);
|
|
172
173
|
const result = await runTask(taskParts.join(' '), deps, out, new AbortController().signal);
|
|
173
174
|
process.exit(result.code);
|
|
174
175
|
}
|
|
@@ -176,12 +177,20 @@ async function main() {
|
|
|
176
177
|
if (args.length === 0) {
|
|
177
178
|
const spinner = createSpinner(out);
|
|
178
179
|
spinner.start('Detecting providers…');
|
|
179
|
-
const [
|
|
180
|
-
buildProviders(cwd),
|
|
180
|
+
const [env, config, stateWritable] = await Promise.all([
|
|
181
181
|
detectEnvironment(),
|
|
182
182
|
loadConfig(),
|
|
183
|
+
probeStateWritable(cwd),
|
|
183
184
|
]);
|
|
185
|
+
const providers = buildProviders(cwd, env);
|
|
184
186
|
spinner.stop();
|
|
187
|
+
// Evaluate non-provider environment health once at startup. Surfaced in the
|
|
188
|
+
// menu only when a problem exists — the user never runs a health command.
|
|
189
|
+
const healthIssues = evaluateHealth({
|
|
190
|
+
nodeVersion: process.version,
|
|
191
|
+
stateWritable,
|
|
192
|
+
pricingStale: isPricingStale(),
|
|
193
|
+
});
|
|
185
194
|
const store = createFileConversationStore({ clock: systemClock });
|
|
186
195
|
const ledger = createLedger({ cwd });
|
|
187
196
|
const menuCtx = {
|
|
@@ -195,6 +204,7 @@ async function main() {
|
|
|
195
204
|
cwd,
|
|
196
205
|
sandbox: 'workspace-write',
|
|
197
206
|
timeoutMs: 120000,
|
|
207
|
+
healthIssues,
|
|
198
208
|
checkForUpdate: () => checkForUpdate({ currentVersion: version, now: Date.now() }),
|
|
199
209
|
updateSelf: async (updateOut) => {
|
|
200
210
|
try {
|
|
@@ -230,7 +240,8 @@ async function main() {
|
|
|
230
240
|
out.write(banner(version, out.color) + '\n');
|
|
231
241
|
const spinner = createSpinner(out);
|
|
232
242
|
spinner.start('Detecting providers…');
|
|
233
|
-
const
|
|
243
|
+
const env = await detectEnvironment();
|
|
244
|
+
const deps = buildDeps(cwd, env);
|
|
234
245
|
spinner.stop();
|
|
235
246
|
out.write(welcome(deps, out.color) + '\n\n');
|
|
236
247
|
await startRepl(deps, out);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/cost.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* fabricated. If the ledger is empty the command says so and returns 0.
|
|
20
20
|
*/
|
|
21
21
|
import { readLedger, summarizeLedger } from '../infra/ledger.js';
|
|
22
|
+
import { formatTokens } from '../infra/insights.js';
|
|
22
23
|
import { getCheapestForTier, calculateCost, getModelPricing } from '../infra/pricing.js';
|
|
23
24
|
import { bold, dim, cyan, divider, label } from '../ui/theme.js';
|
|
24
25
|
// ---------------------------------------------------------------------------
|
|
@@ -32,7 +33,7 @@ import { bold, dim, cyan, divider, label } from '../ui/theme.js';
|
|
|
32
33
|
*/
|
|
33
34
|
export function formatCostReport(entries, color = false) {
|
|
34
35
|
if (entries.length === 0) {
|
|
35
|
-
return ['No usage recorded yet
|
|
36
|
+
return ['No usage recorded yet. Run a task first, e.g. myshell-tools run "summarize this repo"'];
|
|
36
37
|
}
|
|
37
38
|
const summary = summarizeLedger(entries);
|
|
38
39
|
// Counterfactual uses the flagship from paid providers only (claude/codex).
|
|
@@ -42,9 +43,15 @@ export function formatCostReport(entries, color = false) {
|
|
|
42
43
|
// Apples-to-apples: price BOTH the models actually used AND the flagship on the
|
|
43
44
|
// SAME basis (list price × tokens). We do NOT compare the billed total (which
|
|
44
45
|
// includes caching/discounts) against a list-price flagship estimate.
|
|
46
|
+
// Also accumulate REAL token totals (overall + per model) — the honest signal.
|
|
45
47
|
let routedListUsd = 0;
|
|
46
48
|
let flagshipListUsd = 0;
|
|
49
|
+
let totalTokens = 0;
|
|
50
|
+
const tokensByModel = {};
|
|
47
51
|
for (const entry of entries) {
|
|
52
|
+
const entryTokens = entry.inputTokens + entry.outputTokens;
|
|
53
|
+
totalTokens += entryTokens;
|
|
54
|
+
tokensByModel[entry.model] = (tokensByModel[entry.model] ?? 0) + entryTokens;
|
|
48
55
|
const entryPricing = getModelPricing(entry.provider, entry.model);
|
|
49
56
|
if (entryPricing) {
|
|
50
57
|
routedListUsd += calculateCost(entry.inputTokens, entry.outputTokens, entryPricing);
|
|
@@ -52,37 +59,53 @@ export function formatCostReport(entries, color = false) {
|
|
|
52
59
|
flagshipListUsd += calculateCost(entry.inputTokens, entry.outputTokens, flagship);
|
|
53
60
|
}
|
|
54
61
|
const lines = [];
|
|
55
|
-
lines.push(bold('myshell-tools
|
|
62
|
+
lines.push(bold('myshell-tools — usage & efficiency', color));
|
|
56
63
|
lines.push(divider(color));
|
|
57
|
-
// ----
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
lines.push(`${label('
|
|
64
|
+
// ---- Usage (REAL, measured) -----------------------------------------------
|
|
65
|
+
// Tokens are measured, not estimated — the trustworthy primary figure.
|
|
66
|
+
lines.push(`${label('Tasks run', color)}: ${summary.calls}`);
|
|
67
|
+
lines.push(`${label('Tokens used', color)}: ${formatTokens(totalTokens)} ${dim('(real, measured)', color)}`);
|
|
61
68
|
lines.push(divider(color));
|
|
62
|
-
|
|
63
|
-
lines.push(bold('Per-model breakdown', color));
|
|
69
|
+
lines.push(bold('Per-model usage', color));
|
|
64
70
|
for (const [model, ms] of Object.entries(summary.byModel)) {
|
|
65
|
-
const callStr = ms.calls === 1 ? '1
|
|
66
|
-
lines.push(` ${cyan(model, color)}: ${callStr},
|
|
71
|
+
const callStr = ms.calls === 1 ? '1 task' : `${ms.calls} tasks`;
|
|
72
|
+
lines.push(` ${cyan(model, color)}: ${callStr}, ${formatTokens(tokensByModel[model] ?? 0)} tokens`);
|
|
67
73
|
}
|
|
68
74
|
lines.push(divider(color));
|
|
69
|
-
// ----
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
lines.push(`${label('Routed (models used)', color)}: $${routedListUsd.toFixed(4)}`);
|
|
75
|
-
lines.push(`${label('Always-flagship', color)}: $${flagshipListUsd.toFixed(4)}`);
|
|
75
|
+
// ---- Routing efficiency (billing-agnostic ratio) --------------------------
|
|
76
|
+
// The savings RATIO is honest regardless of billing model: it compares how
|
|
77
|
+
// many flagship tokens you avoided by routing to cheaper tiers. This is the
|
|
78
|
+
// value-prop number, and it holds true on a subscription too.
|
|
79
|
+
lines.push(bold('Routing efficiency', color));
|
|
76
80
|
if (routedListUsd > 0 && flagshipListUsd > routedListUsd) {
|
|
77
81
|
const multiplier = flagshipListUsd / routedListUsd;
|
|
78
|
-
lines.push(`Routing
|
|
79
|
-
|
|
82
|
+
lines.push(`Routing picked cheaper-tier models where it could — ` +
|
|
83
|
+
`${cyan(`~${multiplier.toFixed(1)}× less`, color)} than sending every task to ` +
|
|
84
|
+
`the flagship (${flagship.model}).`);
|
|
80
85
|
}
|
|
81
86
|
else if (routedListUsd > 0) {
|
|
82
|
-
lines.push('Every
|
|
87
|
+
lines.push(dim('Every task already used a flagship-tier (or pricier) model — no routing savings to show.', color));
|
|
83
88
|
}
|
|
84
89
|
else {
|
|
85
|
-
lines.push(dim('No priced models in the ledger — cannot compute a
|
|
90
|
+
lines.push(dim('No priced models in the ledger — cannot compute a routing comparison.', color));
|
|
91
|
+
}
|
|
92
|
+
// ---- Estimated cost (API-equivalent, NOT your subscription bill) ----------
|
|
93
|
+
// myshell-tools drives your SUBSCRIPTION CLIs — you pay a flat fee, not per
|
|
94
|
+
// token. This is a rough "if this were metered API usage" estimate, shown for
|
|
95
|
+
// devs who want the magnitude; it is not what you are billed. Both figures use
|
|
96
|
+
// the SAME basis (list price × tokens) so "routed vs always-flagship" is
|
|
97
|
+
// apples-to-apples and internally consistent (routed never exceeds flagship).
|
|
98
|
+
lines.push(divider(color));
|
|
99
|
+
lines.push(bold('Estimated cost', color) + dim(' — API-equivalent (list price), not your subscription bill', color));
|
|
100
|
+
if (routedListUsd > 0) {
|
|
101
|
+
let line = `${label('Routed', color)}: ~$${routedListUsd.toFixed(4)}`;
|
|
102
|
+
if (flagshipListUsd > routedListUsd) {
|
|
103
|
+
line += ` · ${dim(`always-flagship: ~$${flagshipListUsd.toFixed(4)}`, color)}`;
|
|
104
|
+
}
|
|
105
|
+
lines.push(line);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
lines.push(dim('No priced models in the ledger — no estimate available.', color));
|
|
86
109
|
}
|
|
87
110
|
return lines;
|
|
88
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../../src/commands/cost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEjE,8EAA8E;AAC9E,+DAA+D;AAC/D,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAsB,EAAE,KAAK,GAAG,KAAK;IACpE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../../src/commands/cost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEjE,8EAA8E;AAC9E,+DAA+D;AAC/D,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAsB,EAAE,KAAK,GAAG,KAAK;IACpE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,wFAAwF,CAAC,CAAC;IACpG,CAAC;IAED,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,4EAA4E;IAC5E,6EAA6E;IAC7E,wCAAwC;IACxC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAEpE,gFAAgF;IAChF,8EAA8E;IAC9E,sEAAsE;IACtE,+EAA+E;IAC/E,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,MAAM,aAAa,GAA2B,EAAE,CAAC;IACjD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,YAAY,CAAC;QAC3D,WAAW,IAAI,WAAW,CAAC;QAC3B,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC;QAE7E,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAClE,IAAI,YAAY,EAAE,CAAC;YACjB,aAAa,IAAI,aAAa,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QACtF,CAAC;QACD,eAAe,IAAI,aAAa,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC9D,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAE3B,8EAA8E;IAC9E,uEAAuE;IACvE,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,YAAY,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IAE7G,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC,CAAC;IAC3C,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC;QAChE,KAAK,CAAC,IAAI,CACR,KAAK,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,OAAO,KAAK,YAAY,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,CACzF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAE3B,8EAA8E;IAC9E,2EAA2E;IAC3E,4EAA4E;IAC5E,8DAA8D;IAC9D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC,CAAC;IAC9C,IAAI,aAAa,GAAG,CAAC,IAAI,eAAe,GAAG,aAAa,EAAE,CAAC;QACzD,MAAM,UAAU,GAAG,eAAe,GAAG,aAAa,CAAC;QACnD,KAAK,CAAC,IAAI,CACR,sDAAsD;YACpD,GAAG,IAAI,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,8BAA8B;YAC/E,iBAAiB,QAAQ,CAAC,KAAK,IAAI,CACtC,CAAC;IACJ,CAAC;SAAM,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CACR,GAAG,CAAC,0FAA0F,EAAE,KAAK,CAAC,CACvG,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CACR,GAAG,CAAC,uEAAuE,EAAE,KAAK,CAAC,CACpF,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,8EAA8E;IAC9E,+EAA+E;IAC/E,yEAAyE;IACzE,8EAA8E;IAC9E,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,CAAC,GAAG,GAAG,CAAC,6DAA6D,EAAE,KAAK,CAAC,CAAC,CAAC;IACtH,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,IAAI,IAAI,GAAG,GAAG,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,IAAI,eAAe,GAAG,aAAa,EAAE,CAAC;YACpC,IAAI,IAAI,UAAU,GAAG,CAAC,sBAAsB,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;QACrF,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,yDAAyD,EAAE,KAAK,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,GAAe;IACxD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,GAAG,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|