name: updating-llm-client-model-lists description: >- Synchronizes model configurations across Zed and Crush from the Plexus proxy and Hyper. Use when the user asks to update model lists, sync models, refresh available models, or mentions Plexus or Hyper model availability. user-invocable: true license: LicenseRef-MutuaL-1.2 metadata: author: Amolith amolith@secluded.site
Updates LLM client configs from two sources:
- Plexus (self-hosted at
harp-willow.snowy-egret.ts.net:4000) β feeds both Zed (language_models.openai_compatible.plexus) and Crush (providers.plexus.models). - Charm Hyper (
hyper.charm.land) and Hyper Staging (hyper.charmcli.dev) β feed Zed only (language_models.openai_compatible.hyperandlanguage_models.openai_compatible.hyper-stagingrespectively). Crush manages its own Hyper models; do not touch them.
Scripts
Two reusable, typed Python scripts live in scripts/ alongside this skill. They
fetch and normalise model metadata into a uniform TSV or JSON format. Run them
with --json when piping into a config-apply helper, or --tsv (default) for
review.
| Script | Source | Auth | Notes |
|---|---|---|---|
scripts/plexus.py |
Plexus management + metadata APIs | 1Password (op read) |
Unsets OP_SERVICE_ACCOUNT_TOKEN internally, retrieves admin key, does two-step alias β metadata lookup. Excludes nomic-embed-text-v1.5, diff-apply, fix-json, and any alias without a metadata block. Uses floor(context_length * 0.2) for max_completion_tokens. |
scripts/hyper.py |
Hyper /v1/models |
None | Pass --staging to target Hyper Staging. Uses max_output_tokens verbatim (no 20% rule). |
Both scripts share scripts/models.py (the Model dataclass, display-name
cleaner, and TSV/JSON emitters). All scripts are executable and use
#!/usr/bin/env python3.
Running the scripts
SKILL_DIR="$HOME/.agents/skills/updating-llm-client-model-lists/scripts"
# Plexus β TSV for review
"$SKILL_DIR/plexus.py" --tsv | column -t -s$'\t'
# Plexus β JSON for config apply
"$SKILL_DIR/plexus.py" --json
# Hyper β TSV
"$SKILL_DIR/hyper.py" --tsv | column -t -s$'\t'
# Hyper Staging β JSON
"$SKILL_DIR/hyper.py" --staging --json
The TSV/JSON output is the single source of truth for model IDs, display names, context windows, and capability flags. Review the display names in the table, and if they need modification, make the modification while editing the configs.
jq
The jq on PATH inside Crush is a shell function, not the real binary. Always
invoke the real one via mise exec jq -- jq β¦.
JSONC
Zed's settings.json is JSONC (allows trailing commas and comments), so stdlib
jq cannot parse it. Use Python with the json5 module instead:
import json, json5, pathlib
src = pathlib.Path("/home/amolith/.config/zed/settings.json")
data = json5.loads(src.read_text())
# β¦mutate dataβ¦
out = json.dumps(data, indent=4, ensure_ascii=False).replace(" ", "\t")
src.write_text(out)
Network reachability
- Ping
harp-willow.snowy-egret.ts.netonce. - If unreachable and Tailscale DNS is disabled, enable it:
tailscale set --accept-dns=true - Test ping again. If still unreachable, STOP and inform the user.
Remember to disable after finishing the whole process if it was previously disabled:
tailscale set --accept-dns=false
max_completion_tokens
Different sources use different rules:
- Plexus: use
floor(context_length * 0.2). Do NOT use thetop_provider.max_completion_tokensfield β some models report their entire context window, which is not a sensible default.scripts/plexus.pyapplies this automatically. - Hyper / Hyper Staging: use the
max_output_tokensfield verbatim.scripts/hyper.pyuses this directly (no 20% rule).
Rules
Exclusions (Plexus)
- Always exclude
nomic-embed-text-v1.5(embedding model, not supported by any client) - Exclude
diff-applyandfix-jsonfrom all client model lists - Exclude models with no
context_lengthfield (e.g. voxtral-small, mistral-ocr) unless the user provides values manually - Only aliases with a
metadatablock are fetched; aliases without metadata (likediff-apply,fix-json,memri,smol) are implicitly excluded.
Capabilities
- Assume every Plexus-listed model supports tools, regardless of whether
Plexus reports
toolsinsupported_parameters. - For Hyper and Hyper Staging models, every entry supports tools and
interleaved reasoning; set
tools: true,parallel_tool_calls: true, andinterleaved_reasoning: trueon every Hyper entry.
Display names
scripts/models.py:clean_name() handles normalisation for both sources:
- Strip redundant vendor prefixes (
DeepSeek: DeepSeek V4 ProβDeepSeek V4 Pro) - Remove parentheses (
(fast)βfast) - Replace hyphens with spaces
- Split brand-version compounds (
Qwen3.6βQwen 3.6) - Normalise capitalisation for known brands and acronyms (DeepSeek, GLM, GPT, MiMo, MiniMax, Qwen, OSS, FP8, INT4, etc.)
When a Plexus alias ID ends in -h (indicating a hosted variant), the
display name from Plexus already reflects the correct form; no special
suffix handling is needed beyond clean_name().
Update Zed config
File: ~/.config/zed/settings.json (JSONC β edit via Python json5, see
above).
Plexus
Update language_models.openai_compatible.plexus.available_models[]. Each
entry:
{
"name": "<id>",
"display_name": "<name>",
"max_tokens": <context_length>,
"max_completion_tokens": <max_completion_tokens>,
"capabilities": {
"chat_completions": true,
"prompt_cache_key": false,
"tools": true,
"parallel_tool_calls": true,
"images": <supports_image>
}
}
Hyper
Update language_models.openai_compatible.hyper.available_models[]. Each
entry:
{
"name": "<id>",
"display_name": "<name>",
"max_tokens": <context_window>,
"max_completion_tokens": <max_output_tokens>,
"capabilities": {
"chat_completions": false,
"prompt_cache_key": false,
"tools": true,
"parallel_tool_calls": true,
"interleaved_reasoning": true,
"images": <supports_attachments>
}
}
Hyper Staging
Update language_models.openai_compatible.hyper-staging.available_models[]
with the same schema as Hyper. The api_url is
https://hyper.charmcli.dev/v1.
Example apply helper for all three Zed providers:
import json, json5, pathlib, subprocess
SKILL_DIR = "$HOME/.agents/skills/updating-llm-client-model-lists/scripts"
src = pathlib.Path.home() / ".config/zed/settings.json"
data = json5.loads(src.read_text())
om = data["language_models"]["openai_compatible"]
# Plexus
plexus = json.loads(subprocess.check_output([SKILL_DIR + "/plexus.py", "--json"]))
om["plexus"]["available_models"] = [{
"name": m["id"], "display_name": m["name"],
"max_tokens": m["context_length"],
"max_completion_tokens": m["max_completion_tokens"],
"capabilities": {"chat_completions": True, "prompt_cache_key": False,
"tools": True, "parallel_tool_calls": True, "images": m["supports_image"]},
} for m in plexus]
# Hyper
def hyper_entries(staging=False):
cmd = [SKILL_DIR + "/hyper.py", "--json"]
if staging: cmd.append("--staging")
models = json.loads(subprocess.check_output(cmd))
return [{
"name": m["id"], "display_name": m["name"],
"max_tokens": m["context_length"],
"max_completion_tokens": m["max_completion_tokens"],
"capabilities": {"chat_completions": False, "prompt_cache_key": False,
"tools": True, "parallel_tool_calls": True,
"interleaved_reasoning": True, "images": m["supports_image"]},
} for m in models]
om["hyper"]["available_models"] = hyper_entries()
om["hyper-staging"] = {
"api_url": "https://hyper.charmcli.dev/v1",
"available_models": hyper_entries(staging=True),
}
out = json.dumps(data, indent=4, ensure_ascii=False).replace(" ", "\t")
src.write_text(out)
Update Crush config
File: ~/.local/share/chezmoi/dot_config/crush/crush.json
Update providers.plexus.models[]. Each entry:
{
"id": "<id>",
"name": "<name>",
"context_window": <context_length>,
"default_max_tokens": <max_completion_tokens>,
"can_reason": <supports_reasoning>,
"supports_attachments": <supports_image>
}
This file is strict JSON β safe to edit with mise exec jq directly:
"$SKILL_DIR/plexus.py" --json > /tmp/plexus_models.json
mise exec jq -- jq --tab --slurpfile new /tmp/plexus_models.json \
'.providers.plexus.models = [$new[0][] | {
"id": .id, "name": .name,
"context_window": .context_length,
"default_max_tokens": .max_completion_tokens,
"can_reason": .supports_reasoning,
"supports_attachments": .supports_image
}]' \
~/.local/share/chezmoi/dot_config/crush/crush.json > /tmp/crush_new.json
mv /tmp/crush_new.json ~/.local/share/chezmoi/dot_config/crush/crush.json
After editing, run chezmoi apply ~/.config/crush/crush.json. STOP if chezmoi
has any output other than success. Do not touch providers.hyper β Crush
manages its own Hyper models.