Skip to content

feat(launch): lazy-MCP loading, token accounting, R015 lint, startup banners#94

Merged
NagyVikt merged 11 commits into
mainfrom
feat/lazy-mcp-loading
Jun 30, 2026
Merged

feat(launch): lazy-MCP loading, token accounting, R015 lint, startup banners#94
NagyVikt merged 11 commits into
mainfrom
feat/lazy-mcp-loading

Conversation

@NagyVikt

Copy link
Copy Markdown
Contributor

Summary

Launch/loading improvements for cue, built on feat/lazy-mcp-loading:

  • lazy-MCP loading + opt-in prune (profile + global) — MCPs load lazily; prune is gated behind an explicit marker (never skill-reference inference).
  • honest MCP token accounting + --budget CI gate (cue cost).
  • R015 security lint rule — flags dangerous markdown + invisible chars.
  • uvx installer hardening — collapse the install firehose to a salient error line; --force retry on stale uv entrypoint collision.
  • startup banners — compact one-line banners, capped off by an always-on one-line identity banner on every real launch (▸ claude · 🏭 gstack +4 · 96 skills · 7 MCPs · 🔴 ~23K always-on · → cue cost).

Test plan

  • bun test — no new failures vs main (baseline-red repo: prove the failing set is unchanged).
  • tsc --noEmit clean.
  • Independent AI review of the full diff — no CRITICAL/HIGH.
  • Manual: banner renders on a fresh-shell launch; absent from --dry-run/--rematerialize.

NagyVikt and others added 8 commits June 29, 2026 23:29
…code

- picker: render "N skills" instead of "+N skills" in the combine multiselect
  (formatTallyDelta + per-row headline) so counts read as plain numbers
- /goal: add TL;DR + Arguments section, make auto-vs-interactive mode explicit,
  de-duplicate the continuation-cap guardrail
- prune provably-dead code flagged by biome (REGISTRY_PATH, REPO_ROOT, conf,
  padVisual, body, ctxEndBefore, unused stderr destructure)

lint: 1 error + 12 warnings → 0 errors, 4 warnings; tsc clean; touched-module
tests green. Excludes unrelated in-progress picker layout work and other WIP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cue cost estimated each MCP server's cost from its config-entry byte length
(JSON.stringify(server)/200), which has no relation to tool count or schema
size — undercounting the dominant always-on cost ~50x (e.g. ~400 tok reported
for servers whose schemas cost ~19k).

Replace with cache-backed per-server accounting (src/lib/mcp-token-estimate.ts):
a repo seed (resources/mcp-token-estimates.json, flagged estimate) overridden
per-id by probed runtime measurements. Add `--budget N` to `cue cost` and
`cue cost --compare`: exits 1 when a profile exceeds the budget (human + --json),
for use as a CI token-budget gate. Uncached servers are flagged, not faked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prune a profile's MCP servers to what its skills actually need so unused tool
schemas don't burn always-on context. Interactive launches keep the existing
opt-out picker + remembered per-dir overrides; non-interactive launches stay
fail-open (keep all) unless CUE_PRUNE_MCPS opts in:

  CUE_PRUNE_MCPS=auto  drop unused PROFILE-declared MCPs (user-global MCPs
                       stay untouched — the existing tested invariant holds)
  CUE_PRUNE_MCPS=all   also drop unused GLOBAL servers present in the runtime
                       .claude.json (heavy ones a coding profile never calls);
                       a louder opt-in because it removes globally-set config

Precedence: --disable-mcp > remembered override > CUE_PRUNE_MCPS > keep-all.
disabledMcpIds (incl globals under =all) flow to the materializer, which evicts
exactly those keys from .claude.json and never touches an unnamed user MCP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse the verbose pre-agent startup output. The token-overhead banner
goes from 6 lines to 2 (formatTokenWarning) with the full per-profile /
heaviest-bodies / drop-hint breakdown moving to `cue cost`. The doctor,
skill-description-lint, and missing-MCP blocks each collapse to a single
line ending in a `-> command` pointer (cue doctor --fix / cue validate /
cue mcps add). A heavy composite launch drops from ~12 lines to <=5.

Tests rewritten to pin the new compact shapes.
…ars)

A SKILL.md is untrusted content loaded into the model's context, so it
carries the markdown->exfiltration risk that vercel-labs/markdown-sanitizers
hardens against. R015 flags, as errors:

- dangerous URL schemes (javascript:/vbscript:/data:text/html, data:image/svg)
  and raw <script>/<iframe>/<object>/<form>/on*= handlers, in PROSE only
  (reuses R009's code-fence split, so fenced examples are exempt);
- invisible / bidirectional control chars anywhere (zero-width, word-joiner,
  RLO/LRO, BOM, soft hyphen) -- the markdown sanitizer's own blind spot.

Invisible chars are auto-fixable (stripped); dangerous URLs/HTML are not.
The on*= check is tag-scoped (<... on*=) so prose like "once = 'daily'"
doesn't false-positive, and quote-optional so unquoted <img onerror=fn()>
is caught. Rides the existing cue lint-skill + skill-md-lint-action gate.

Verified: 80 tests pass; 0 findings across all 1028 skill .md.
uv tool install aborts when a tool's ~/.local/bin entrypoint symlinks are
left dangling (tool dir wiped). existsSync reports the binary missing, so
cue reinstalls, but uv refuses to overwrite the leftover symlink without
--force — looping the full install + failure on every launch. Detect the
collision and retry once with --force to relink in place.
Replace the conditional 4-line formatTokenWarning block with a single
identity line printed on every real launch (stderr): agent, profile
(composites collapsed to primary +N), skill/MCP counts, and always-on
token cost. Token segment drops under the 2K floor; the cue cost pointer
appears only for heavy (>=10K) profiles. alwaysOnForBadge (tmux badge)
preserved; banner stays out of --dry-run/--rematerialize. Tests rewritten
to pin the new shape.
@NagyVikt NagyVikt marked this pull request as draft June 30, 2026 10:00
NagyVikt added 3 commits June 30, 2026 12:05
Verified this session (112 tests pass, typecheck clean, independent review
DONE_WITH_CONCERNS — concern resolved):

- merge(M1): renderMerged static mode now emits non-core rules/commands/hooks
  (previously computed in the preview but never rendered → silent data loss)
- merge(M2): --optimize dedupe is real — collapses same-slug skills last-wins
  to mirror the runtime materializer's flat-symlink override (was a no-op)
- merge(M4): estTokens uses the honest shared estimators (skillAlwaysOnTokens
  + sumMcpTokens), consistent with `cue cost`, replacing flat per-skill guesses
- picker(A1): companion pre-check set is conflict-consistent — a companion that
  conflicts with an already-checked one starts unchecked instead of being
  silently pruned at confirm (WYSIWYG)

Also bundles unrelated in-progress working-tree work (profiles, personas,
hooks, evals harness, legal/ros2 profiles, shell/status/loader edits) at the
user's request. Submodules (resources/skills, resources/mcps) intentionally
NOT bumped — their commits aren't pushed yet.
# Conflicts:
#	README.md
#	profiles/_types.ts
#	profiles/schema.json
#	src/commands/ai.ts
#	src/commands/cost.ts
#	src/commands/launch.ts
#	src/commands/score.ts
#	src/lib/mcp-overrides.test.ts
#	src/lib/mcp-overrides.ts
#	src/lib/mcp-token-estimate.test.ts
#	src/lib/mcp-token-estimate.ts
#	src/lib/pair-suggestions.ts
#	src/lib/profile-loader.ts
#	src/lib/profile-merge.ts
#	src/lib/skill-dependencies.getNeeded.test.ts
#	src/lib/skill-dependencies.ts
handleHookSource correctly returns not-found for a declared-but-unmaterialized
hook script; the test picked the first hook with any scriptPath, so it failed in
environments (incl. main) where a settings.json hook references a script not on
disk. Require existsSync before asserting the source serves.
@NagyVikt NagyVikt marked this pull request as ready for review June 30, 2026 10:39
@NagyVikt NagyVikt merged commit bbf3dba into main Jun 30, 2026
2 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant