Expand description
Pure config-building logic for agent definitions.
Ports the buildConfigFiles, buildMcpConfig, and expandTemplate
functions from frontend/app/view/agent/agent-model.ts.
Most functions here are pure — no I/O, no async. The exception is the
managed skill files manifest section near the end: real filesystem
I/O shared by the two independent RPC handlers that materialize config
files to disk (agent.open’s write_agent_config_files in
server/app_api/agent_open.rs, and writeagentconfig in
server/editor_handlers.rs — the latter is the actual “click Launch”
path used on every normal agent launch). Lives here rather than in
either handler file so the two callers can’t drift out of sync on the
manifest format or the path-traversal defense (reagent P1, PR #2322 —
writeagentconfig initially had no cleanup at all).
Structs§
- Agent
Config File - A single file to be written to the agent working directory.
- Claude
MdOwnership 🔒Marker
Constants§
- AGENTMUX_
MEMORY_ FILENAME - AgentMux-owned side file carrying the full Soul+AgentMD+Bundle+Skills
composition when the real
CLAUDE.mdis foreign — always safe to regenerate in place every launch, unlikeCLAUDE.mditself in that case. - BUNDLE_
SECTION_ HEADING - Heading of the bundle-content section in a generated
CLAUDE.md. - CLAUDE_
MD_ 🔒IMPORT_ MARKER_ COMMENT - Comment wrapping the
@importline so its origin — and how to remove it — is unambiguous to anyone reading a foreignCLAUDE.mdby hand. - CLAUDE_
MD_ MANAGED_ MARKER - First line AgentMux writes on any
CLAUDE.mdit fully owns (freshly created, or regenerated on every launch since). Its ABSENCE on an EXISTING file is what marks that file as foreign — predates AgentMux touching this working directory, or a human replaced AgentMux’s file with their own — and is never overwritten again. Seedocs/specs/SPEC_CLAUDE_MD_OWNERSHIP_PROTECTION_2026_08_22.md. - CLAUDE_
MD_ 🔒OWNERSHIP_ MARKER_ PATH - One-time marker recording whether the
@importline (below) has already been offered for this working directory. Checked INSTEAD of re-scanningCLAUDE.mdcontent on every launch — without it, a user who deliberately deletes the import line (opting out of AgentMux content entirely) would see it silently reappear on their next launch. - CLAUDE_
MD_ PRE_ ADOPT_ BACKUP - Where an adopted legacy
CLAUDE.mdis kept (is_legacy_agentmux_claude_md). - MANAGED_
MCP_ SERVERS_ MANIFEST - Names of the
mcpServersentries AgentMux wrote into a working directory’s.mcp.jsonon the last materialization — seewrite_mcp_json_respecting_user_servers. Lives besideMANAGED_SKILL_FILES_MANIFESTfor the same reason: it lets the next launch tell entries it owns from entries the user put there. - MANAGED_
SKILL_ FILES_ MANIFEST - Hidden manifest (relative to the agent working directory) tracking which
skill-derived paths (
.claude/commands/*.md,.claude/skills/*/SKILL.md) AgentMux itself wrote on the last materialization, so a subsequent one can delete ones that are no longer current without touching any user-authored.claude/commands/.claude/skillscontent. - MCP_
JSON_ WRITE_ LOCK - Held (exclusively) for the whole read-merge-write in
write_mcp_json_respecting_user_servers. See its doc comment. - SKILL_
DESCRIPTION_ 🔒MAX_ LEN - Agent Skills spec caps
descriptionat 1024 characters. - SKILL_
TYPE_ AGENT_ SKILL skill_typevalue that materializes a skill as an Agent Skills-format.claude/skills/<slug>/SKILL.mdinstead of a.claude/commands/<trigger>.mdslash command. Any other value (in practice, always"prompt"today) keeps the pre-existing slash-command behavior. Seedocs/specs/REPORT_ARMORY_BUNDLE_STANDARD_RESEARCH_2026_07_16.mdPhase 0 —skill_typealready existed end-to-end but was never branched on before this, making it the lowest-friction place to hang the format discriminator rather than adding a new column.- STARTUP_
INSTRUCTIONS_ 🔒MANAGED_ MARKER - Materialize
generated_content(the composed Soul+AgentMD+Bundle+SkillsCLAUDE.mdbodybuild_config_filesalready produced) against whatever is already on disk atbase_path/CLAUDE.md, WITHOUT ever overwriting a foreign file’s content:
Functions§
- agentmux_
sessionstart_ 🔒entry - The
SessionStarthook entry: oneagentmux-bashwrap sessionstart --part Ncommand per memory part,N= 1..=HOOK_PARTS(backend::memory_delivery). The commands run in parallel, and each prints its own part (or nothing). - back_
up_ 🔒legacy_ claude_ md - Copy a legacy
CLAUDE.mdtoCLAUDE_MD_PRE_ADOPT_BACKUPbefore it is adopted. Whole or not at all (a failed write removes its partial copy); a copy left by an earlier attempt counts only if it is this file, whole (Codex on #4131). - build_
config_ files - Build the list of config files to write to the agent working directory.
- build_
mcp_ config - Build
.mcp.jsoncontent with the auto-injected AgentMux MCP server entry. - build_
mcp_ config_ from_ refs - Build
.mcp.jsonfrom standalone McpServer ref rows (v1 composable model). - build_
settings_ with_ hooks - Build
.claude/settings.jsoncontent with the auto-injected PreToolUse Bash hook and PreCompact hooks (under the"hooks"key). PreToolUse redirects Bash invocations into the streaming wrapper (agentmux-bashwrap exec); PreCompact (two entries, matcher"manual"/"auto") pings the sidecar the instant compaction starts (agentmux-bashwrap precompact) — seedocs/specs/SPEC_COMPACTION_DETECTION_AND_HANDLING_2026_07_31.md§4.2. User-supplied settings.json (from the agent’scontent_map["settings"]) is parsed and merged at the top level; user-supplied legacy hooks content (fromcontent_map["hooks"]) is merged intosettings.hooks. - cleanup_
stale_ managed_ skill_ files - Delete skill-derived files a PREVIOUS materialization wrote (per the
on-disk manifest) but that are no longer part of
new_managed_paths– e.g. a skill’s format switched between"prompt"/"agent-skill", or it was renamed/removed. Without this, the stale file stays on disk and Claude keeps treating it as active alongside the newly selected format (reagent P1 + Codex P1/P2, PR #2322). - expand_
template - Replace
{{VARIABLE}}placeholders incontentwith values fromvars. - inject_
jekt_ signing_ keys_ into_ mcp_ json - Best-effort: mint-or-reuse
agent_slug’s jekt/LAN signing keys (SPEC_JEKT_TRUST_LAYER_COMPLETION_2026_08_13.md§2.2,SPEC_JEKT_LAN_TIER_SIGNING_2026_08_15.md§2.1) and patch them into a.mcp.jsonfile’smcpServers.agentmux.envblock. ReturnsNoneon any parse failure, missingmcpServers.agentmux.envobject, or if neither key could be ensured — callers must keep the original content in that case; this must never block writing the agent’s config. - is_
agentmux_ managed_ instructions - Does this file’s content say AgentMux wrote it?
- is_
generated_ 🔒skill_ row - A skills-index row exactly as
build_config_filesrenders it:- **<name>**, then optionally(trigger: /<trigger>), then optionally— <description>(Codex on #4131: a bare- **noteis a person’s). - is_
legacy_ agentmux_ claude_ md - Whether
contentis exactly what an older AgentMux wrote asCLAUDE.md, in its order, ignoring blank lines: the# Available Skillsheading, its usage line, at least one skill row (- **…; AgentMux wrote the section only when there were skills), then optionally the managed import (its comment and line, or the line alone). Anything else means a person wrote there, and it stays theirs. - managed_
skill_ file_ paths - Compute the subset of
filenamesthat are skill-derived managed paths (the ones tracked inMANAGED_SKILL_FILES_MANIFEST) — everything else (CLAUDE.md,.mcp.json,.claude/settings.json, …) is always fully regenerated at a fixed path every launch, so it has no staleness problem to track. - prepend_
user_ 🔒hook_ array - Merge a user-supplied
settings.json-level hook-array (PreToolUseorPreCompact) with whatever is already staged inhooks_objunderkey(our auto-injected entries, possibly already carrying legacycontent_map["hooks"]-merged user entries from the earlier pass inbuild_settings_with_hooks). User entries are PREPENDED so their matchers/gates get first refusal; AgentMux’s own entries always stay last. A non-array value is warned and dropped rather than silently discarded — same discipline as every other branch in this function (reagent P1 on PR #813). - read_
managed_ 🔒mcp_ server_ names - render_
skill_ 🔒md - Render an Agent Skills-format
SKILL.md: YAML frontmatter with the two required fields (name,description— per the spec’s six-field frontmatter; AgentMux doesn’t populate the four optional ones today:license,compatibility,metadata,allowed-tools), followed by the skill’s content as the Markdown body. See https://agentskills.io/specification. - sanitize_
trigger 🔒 - Validate a skill’s
triggeris safe to use as a single path segment in.claude/commands/<trigger>.md.triggeris free-form user input with no format validation anywhere upstream (the skill create/update RPCs and the frontend form all accept it as-is), so a trigger containing a path separator or a..segment previously let the resulting filename resolve OUTSIDE the agent’s working directory – both for this write and, once stale-file cleanup existed, for the corresponding delete (reagent P1, PR #2322). Rejects (returnsNone) anything containing/or\, or that is exactly./..; callers skip writing that skill’s command file entirely rather than silently rewriting the trigger into something the user didn’t ask for. - skill_
name_ 🔒slug - Derive a slug for an Agent Skill name that is valid per the Agent Skills
namegrammar: lowercase letters, digits, and hyphens ONLY (no underscores).derive_slugis shared with agent role-slugs, which deliberately DO permit underscores, so it isn’t spec-valid here as-is — hyphenate underscores (and re-collapse any resulting run of hyphens) rather than reusing it directly (Codex P1, PR #2322). - skills_
to_ agent_ skills - Convert standalone Skill records into the AgentSkill shape expected by
build_config_files. Used when the v1 ref tables are non-empty. - skills_
with_ 🔒their_ own_ file - For each skill, whether
build_config_filesleaves it a file of its own that Claude Code lists natively: an Agent Skill’sSKILL.md(slugs are made unique), or a command with a usable trigger that no later skill’s command overwrites. Triggers are compared case-insensitively, as a case-insensitive filesystem would. Trigger uniqueness isn’t enforced by the skill store (Codex on #4131). A non-ASCII trigger never counts as owning its file: a normalization-insensitive filesystem (macOS) folds Unicode-equivalent names together, and keeping such a skill in the index costs at most a repeated line, never a lost skill. - truncate_
utf16_ 🔒units - Truncate
sto at mostmax_unitsUTF-16 code units, on a UTF-8 char boundary. Mirrors JS.slice(0, n)semantics (JS strings are indexed in UTF-16 code units, so a Rust byte-length cap diverges from the TS mirror for any non-ASCII text — reagent P2, PR #2322). - unique_
skill_ 🔒slug - Derive a filesystem-safe, COLLISION-FREE, spec-valid slug for an Agent
Skill name within one caller-scoped
usedset.skill_name_slugalone can produce identical output for distinct names that differ only in punctuation/whitespace (e.g. “Deploy Checklist” and “Deploy!!!Checklist” both -> “deploy-checklist”), which would otherwise silently overwrite one skill’sSKILL.mdwith another’s on disk (reagent P1, PR #2322). Appends-2,-3, … until the slug is unique withinused, truncating the base first so the suffixed result never exceeds the spec’s 64-character max (Codex P2, PR #2322 — a 64-char base plus-2was previously 66 chars). - write_
claude_ md_ respecting_ ownership - Shared by
agent.open(server/app_api/agent_open.rs) and theWriteAgentConfig“click Launch” path (server/editor_handlers.rs) — per this module’s own doc comment, the two config-materializing call sites that must not drift out of sync. Seedocs/specs/SPEC_CLAUDE_MD_OWNERSHIP_PROTECTION_2026_08_22.md. - write_
managed_ 🔒mcp_ server_ names - Best-effort, like
write_managed_skill_file_manifest: losing this write only means the next launch falls back to the no-manifest rule once. - write_
managed_ skill_ file_ manifest - Record
new_managed_pathsas the manifest for the NEXT call tocleanup_stale_managed_skill_files. Best-effort: a write failure is logged, not propagated — losing this write only means the next materialization’s stale-file cleanup is skipped once, not a correctness issue for the current one. - write_
mcp_ json_ respecting_ user_ servers - Write
.mcp.jsonintobase_pathwithout destroying a user’s own MCP server entries (issue #3680). Shared byagent.openand theWriteAgentConfig“click Launch” path — the two config-materializing call sites this module keeps in sync. - write_
startup_ instructions_ respecting_ existing - Write a NON-
CLAUDE.mdstartup-instructions file (AGENTS.md,GEMINI.md,QWEN.md,.pi/APPEND_SYSTEM.md, …) WITHOUT ever overwriting a pre-existing, non-AgentMux-authored file at that path.