Skip to main content

Module render

Module render 

Source
Expand description

Render a loaded compose into on-disk artifacts.

Outputs under <root>/state/:

  • envs/<project>-<agent>.env — env vars for the agent wrapper.
  • mcp/<project>-<agent>.json — MCP stdio config for the runtime.
  • claude/<project>-<agent>.json — wrapper-managed Claude Code settings (currently a PreToolUse deny hook for synchronous-prompt tools that strand a headless pane). Claude-code agents only.
  • role_prompts/<project>-<agent>.md (multi-file role_prompt only) — the ordered concatenation of every source file declared in the role’s role_prompt: [...] list. Re-materialized on every render so any source-file edit lands in the agent’s prompt at next boot.

systemd / launchd unit rendering lives behind a feature flag when those back-ends are enabled via supervisor.type.

Functions§

agent_scope_dir
Absolute path to the per-agent scope directory passed to Claude Code via --add-dir (#383 Phase 3b). render materializes <this>/.claude/skills/<name> symlinks to each declared skill; the wrapper adds --add-dir <this> so the agent discovers them on top of the project .claude/skills/. The directory is materialized only when the agent declares skills:; the wrapper’s [ -d ] guard decides whether the flag is passed.
boot_script_path
Absolute path to the shared boot-context hook script (#430). Wired into every claude-code agent’s SessionStart hook and rewritten by teamctl up (see ensure_wrapper_and_dirs), so it sits beside the agent wrapper in bin/ rather than under per-agent state/. One script serves all agents — it reads the wake source from stdin, so it needs no per-agent identity baked into the path.
claude_settings_path
Absolute path to the wrapper-managed Claude Code settings file. The file carries the default PreToolUse deny hook for synchronous-prompt tools (AskUserQuestion, EnterPlanMode, ExitPlanMode) so a headless agent doesn’t strand on a picker no one will answer. The wrapper applies it via claude --settings <path> for every claude-code agent except those in permission_mode: attended.
env_path
Absolute path to the rendered env file for a given agent.
heartbeat_path
Absolute path to the per-agent activity heartbeat marker (#428). The PreToolUse/UserPromptSubmit hooks touch it on activity and the Stop/StopFailure hooks rm it at turn-end; the TUI stats its mtime at the 1s refresh and classifies the agent Working (touched within 15s) or Idle. NOT JSON — a bare marker whose mtime is the whole signal. Compound <project>-<agent> like every sibling helper, so agents that share a name across projects never collide on one marker.
lastseen_path
Per-agent “last seen” marker, a sibling of heartbeat_path (#439). The boot-context hook touches it at clean turn-end — alongside rm-ing the heartbeat marker — so a freshly woken session can compute how long the agent was down. Unlike the heartbeat marker (removed at every turn-end, so present at boot only after an unclean shutdown), this one persists across the gap, making its mtime the agent’s last activity. Same compound <project>-<agent> stem as every sibling so cross-project name clashes can’t collide, with a .lastseen suffix so it never shadows the marker the TUI stats for Working/Idle.
mcp_path
Absolute path to the rendered MCP config for a given agent.
render_agent
Rendered env + MCP content for a single agent.
render_claude_settings
Wrapper-managed Claude Code settings JSON for a single agent. Returns Some(json) for claude-code runtime regardless of permission_mode — the wrapper decides whether to apply it. Returns None for runtimes that don’t read Claude settings (codex, gemini, …).
render_subagents
#383 Phase 3a: build Claude Code’s --agents inline JSON for one agent from its declared subagents: list. Each list entry is a compose-root-relative markdown file with standard sub-agent frontmatter (name, description, optional tools, model) and a body that becomes the sub-agent’s system prompt. The result is the { "<name>": { description, prompt, [tools], [model] } } object the --agents flag consumes — the only cwd-stationary way to scope sub-agents per agent (no arbitrary-path flag exists; see the Phase-1 spike). Returns Ok(None) when none are declared (→ no --agents flag) or the runtime isn’t claude-code (logs an “unsupported” warning, claude-only v1); Err if a source is unreadable or its frontmatter is invalid, so a typo fails the apply loudly rather than dropping a sub-agent silently.
role_prompt_concat_path
Absolute path to the materialized concatenation of a multi-file role_prompt list. Only ever written for the list form — single-file role_prompt keeps pointing at its source path directly.
subagents_json_path
Absolute path to the rendered Claude Code --agents JSON for one agent (#383 Phase 3a). Lives beside the settings file under state/claude/ and is written only when the agent declares subagents:; the wrapper passes it via --agents "$(cat <path>)" when the file exists.
system_prompt_path
Resolve the absolute path that SYSTEM_PROMPT_PATH will point at.
write_agent_skills
Materialize (or clear) the per-agent skills scope for one agent (#383 Phase 3b). For a claude-code agent declaring skills:, this creates state/agent-scope/<project>-<agent>/.claude/skills/ and symlinks each declared skill directory into it (link name = the skill dir’s basename), so claude --add-dir <scope> surfaces them additively atop the project .claude/skills/. Mirrors write_subagents_json: the scoped + full render paths both call it, and the skills dir is rebuilt from scratch every render so a renamed or dropped skill never lingers. When the agent declares no skills (or isn’t claude-code) the scope dir is removed if present.
write_role_prompt_concat
Materialize the multi-file role_prompt concatenation for one agent.
write_subagents_json
Write (or clear) the per-agent --agents JSON file. Mirrors write_role_prompt_concat: the scoped + full render paths both call it so a subagents: edit flows into the agent at the next render. When the agent declares no sub-agents (or isn’t claude-code) the file is removed if present, so a stale --agents set never lingers across a reload that dropped them.