Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// The paths and defaults are generated from schemas/settings/source/agent-guidance.json
// (Schema/SettingsSchema+AgentGuidance.generated.swift); this file only parses.

/// `agents.guidance.*` in cmux.json: whether new agent chats get cmux's built-in guidance
/// (per harness family) and the person's own skills from `~/.config/cmux/skills`. The acpmux
/// daemon reads the same file when it creates a chat (`agent_guidance.rs`); the app shows and
/// validates them.
public nonisolated struct AgentGuidanceSettings: Sendable, Equatable {
public var claude = AgentGuidanceSettings.guidanceClaudeDefault
public var codex = AgentGuidanceSettings.guidanceCodexDefault
public var otherAgents = AgentGuidanceSettings.guidanceOtherAgentsDefault
public var userSkills = AgentGuidanceSettings.guidanceUserSkillsDefault

public init() {}

static func parse(_ root: JSONValue, diagnostics: inout [SettingsDiagnostic]) -> Self {
var settings = Self()
guard var reader = ConfigFieldReader(root, at: ["agents", "guidance"], diagnostics: &diagnostics) else { return settings }
if let value = reader.bool("claude") { settings.claude = value }
if let value = reader.bool("codex") { settings.codex = value }
if let value = reader.bool("otherAgents") { settings.otherAgents = value }
if let value = reader.bool("userSkills") { settings.userSkills = value }
diagnostics += reader.diagnostics
return settings
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ public struct CmuxConfigSnapshot: Sendable, Equatable {
public var sessions = SessionSettings()
/// `agents.subsessions.*`: sub-session limits; the acpmux daemon enforces them.
public var subsessions = SubsessionSettings()
public var agentGuidance = AgentGuidanceSettings()
/// `agents.autoRetry.*`: the retry loop after a rate limit or lost connection; the acpmux
/// daemon runs it.
public var agentRetry = AgentRetrySettings()
Expand Down Expand Up @@ -470,6 +471,7 @@ public struct CmuxConfigSnapshot: Sendable, Equatable {
snapshot.freeModel = FreeModelSettings.parse(root, diagnostics: &snapshot.diagnostics)
snapshot.sessions = SessionSettings.parse(root, diagnostics: &snapshot.diagnostics)
snapshot.subsessions = SubsessionSettings.parse(root, diagnostics: &snapshot.diagnostics)
snapshot.agentGuidance = AgentGuidanceSettings.parse(root, diagnostics: &snapshot.diagnostics)
snapshot.agentRetry = AgentRetrySettings.parse(root, diagnostics: &snapshot.diagnostics)
snapshot.trust = TrustSettings.parse(root, diagnostics: &snapshot.diagnostics)
snapshot.mobile = MobileSettings.parse(root, diagnostics: &snapshot.diagnostics)
Expand Down
243 changes: 243 additions & 0 deletions Packages/macOS/CmuxNext/Sources/CmuxNextSettings/Localizable.xcstrings

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ public nonisolated struct SettingsPageLayout: Sendable {
group("settings.group.sessions"),
// Whether agent sessions may start sub-sessions, how many and how deep.
group("settings.group.subsessions"),
// cmux's guidance for the agents it runs and the person's own skills.
group("settings.group.agentGuidance"),
// Whether a turn a rate limit or a lost connection ended is tried again, how often.
group("settings.group.agentRetry"),
// Folders that start an agent without the trust question.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// GENERATED by scripts/cmux-next/gen-settings-sources.py from
// schemas/settings/source/agent-guidance.json. Do not edit; edit the JSON and rerun.

extension AgentGuidanceSettings {
/// `agents.guidance.claude`: On: a new Claude Code chat gets the cmux:cmux-chat skill's first-turn pointer (agent_guidance.rs CLAUDE_GUIDANCE). The skill itself stays listed either way.
public nonisolated static let guidanceClaudePath = ["agents", "guidance", "claude"]
public nonisolated static let guidanceClaudeDefault = true
/// `agents.guidance.codex`: On: a new Codex chat gets cmux's chat guidance as first-turn context (agent_guidance.rs AGENT_GUIDANCE), the channel Codex uses for AGENTS.md.
public nonisolated static let guidanceCodexPath = ["agents", "guidance", "codex"]
public nonisolated static let guidanceCodexDefault = true
/// `agents.guidance.otherAgents`: On: every other harness (Gemini, OpenCode, pi, your own) gets the same first-turn guidance as Codex.
public nonisolated static let guidanceOtherAgentsPath = ["agents", "guidance", "otherAgents"]
public nonisolated static let guidanceOtherAgentsDefault = true
/// `agents.guidance.userSkills`: On: the skills in ~/.config/cmux/skills/<name>/SKILL.md reach every new chat: Claude Code as the cmux-user plugin, other agents as an index of names, descriptions and paths in the first-turn context.
public nonisolated static let guidanceUserSkillsPath = ["agents", "guidance", "userSkills"]
public nonisolated static let guidanceUserSkillsDefault = true
}

/// One group of `SettingsSchema` rows (its own type: the schema type's line budget is per type).
nonisolated enum AgentGuidanceSettingsSchema {
/// Settings > Agents > Guidance and Skills (Leo, 2026-11-05: "cmux-next should add a few and we can think about extending them as well and letting other users define stuff too!"). Each switch applies to chats created after the change.
static var descriptors: [SettingDescriptor] {
let group = SettingsText.keyed("settings.group.agentGuidance", "Guidance and Skills")
return [
SettingDescriptor(
AgentGuidanceSettings.guidanceClaudePath,
section: .general,
group: group,
title: SettingsText.keyed("settings.agents.guidance.claude", "cmux Guidance for Claude Code"),
help: SettingsText.keyed("settings.agents.guidance.claude.help", "New Claude Code chats are told how cmux shows their work: the plan checklist, side sessions, session links and the closing recap."),
kind: .toggle,
default: .bool(AgentGuidanceSettings.guidanceClaudeDefault),
keywords: ["guidance", "skills", "system prompt", "instructions", "agents", "claude"]
),
SettingDescriptor(
AgentGuidanceSettings.guidanceCodexPath,
section: .general,
group: group,
title: SettingsText.keyed("settings.agents.guidance.codex", "cmux Guidance for Codex"),
help: SettingsText.keyed("settings.agents.guidance.codex.help", "New Codex chats are told how cmux shows their work: the plan checklist, side sessions, session links and the closing recap."),
kind: .toggle,
default: .bool(AgentGuidanceSettings.guidanceCodexDefault),
keywords: ["guidance", "skills", "system prompt", "instructions", "agents", "codex"]
),
SettingDescriptor(
AgentGuidanceSettings.guidanceOtherAgentsPath,
section: .general,
group: group,
title: SettingsText.keyed("settings.agents.guidance.otherAgents", "cmux Guidance for Other Agents"),
help: SettingsText.keyed("settings.agents.guidance.otherAgents.help", "Other agents (Gemini, OpenCode and the ones you added) get the same guidance about how cmux shows their work."),
kind: .toggle,
default: .bool(AgentGuidanceSettings.guidanceOtherAgentsDefault),
keywords: ["guidance", "skills", "system prompt", "instructions", "agents"]
),
SettingDescriptor(
AgentGuidanceSettings.guidanceUserSkillsPath,
section: .general,
group: group,
title: SettingsText.keyed("settings.agents.guidance.userSkills", "Your Skills in Every Chat"),
help: SettingsText.keyed("settings.agents.guidance.userSkills.help", "Skills you put in ~/.config/cmux/skills, one folder each with a SKILL.md, reach every new agent chat."),
kind: .toggle,
default: .bool(AgentGuidanceSettings.guidanceUserSkillsDefault),
keywords: ["guidance", "skills", "system prompt", "instructions", "agents", "custom", "user skills"]
),
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,12 @@ extension SettingsSchema {
"agents.subsessions.maxChildren": .userOnly,
"agents.subsessions.maxRunning": .userOnly,
"agents.subsessions.maxDepth": .userOnly,
// What every agent is told, and which of the person's skills reach it, is the person's
// call: an agent must not switch off the guidance or the skills it was given.
"agents.guidance.claude": .userOnly,
"agents.guidance.codex": .userOnly,
"agents.guidance.otherAgents": .userOnly,
"agents.guidance.userSkills": .userOnly,
// Whether and how often a failed turn is sent again spends the person's usage: their call,
// as `sessions.resumeAgents` is.
"agents.autoRetry.enabled": .userOnly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public nonisolated enum SettingsSchema {
/// reads it twice before the first window (about 20 ms in all on a cold launch, bd cx-z9kz0).
public static let all: [SettingDescriptor] = {
let next = general + ShortcutHintsSettingsSchema.descriptors + UpdateSettingsSchema.descriptors + UpdateSettingsSchema.announcements
+ ComputerUseSettingsSchema.descriptors + FreeModelSettingsSchema.descriptors + SessionSettingsSchema.descriptors + SessionSettingsSchema.phoneDescriptors + [MachineColorsSetting.descriptor, SSHLinkHostsSetting.descriptor] + CloudNewWorkspaceSetting.descriptors + CloudLoginCopyConsentSetting.descriptors + CloudCodexSignInConsentSetting.descriptors + CloudAgentStartSetting.descriptors + SubsessionSettingsSchema.descriptors + AgentRetrySettingsSchema.descriptors + TrustSettingsSchema.descriptors + MobileSettingsSchema.descriptors + ColumnLayoutSettingsSchema.descriptors
+ ComputerUseSettingsSchema.descriptors + FreeModelSettingsSchema.descriptors + SessionSettingsSchema.descriptors + SessionSettingsSchema.phoneDescriptors + [MachineColorsSetting.descriptor, SSHLinkHostsSetting.descriptor] + CloudNewWorkspaceSetting.descriptors + CloudLoginCopyConsentSetting.descriptors + CloudCodexSignInConsentSetting.descriptors + CloudAgentStartSetting.descriptors + SubsessionSettingsSchema.descriptors + AgentGuidanceSettingsSchema.descriptors + AgentRetrySettingsSchema.descriptors + TrustSettingsSchema.descriptors + MobileSettingsSchema.descriptors + ColumnLayoutSettingsSchema.descriptors
+ PanePlacementSettingsSchema.descriptors + WorkspaceListSettingsSchema.descriptors + PaletteSettingsSchema.descriptors
+ ChatSettingsSchema.descriptors + PickerSettingsSchema.descriptors + ScreenshotsSettingsSchema.descriptors + AutoArchiveSettingsSchema.descriptors + WorkspaceHibernationSettingsSchema.descriptors + TaskSettingsSchema.descriptors + appearance + TerminalSettingsSchema.descriptors + EditorSettingsSchema.descriptors
+ SidebarSectionSettingsSchema.descriptors + HoverCardSettingsSchema.descriptors + NowPlayingSettingsSchema.descriptors + NowPlayingSettingsSchema.newTabDescriptors + NowPlayingSettingsSchema.pictureInPictureDescriptors + NewTabWidgetSettingsSchema.descriptors + NewTabWidgetSettings.editDescriptors + ColorTransitionSettingsSchema.descriptors + WorkspaceRowSetting.descriptors() + BrowserSettingsSchema.descriptors + HomeSettingsSchema.descriptors + ChiefReactionSettingsSchema.descriptors + ChiefRoutingSettingsSchema.descriptors
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import CmuxNextSettings
import Testing

/// `agents.guidance.*`: every switch is on by default, and each turns off on its own, as the
/// acpmux daemon reads them when it creates a chat (`agent_guidance.rs`).
struct AgentGuidanceSettingsTests {
private static func snapshot(_ root: JSONValue) -> CmuxConfigSnapshot {
CmuxConfigSnapshot.parse(root, validDensities: [], validMetrics: [])
}

@Test func allOnByDefault() {
let settings = Self.snapshot(.object([:])).agentGuidance
#expect(settings.claude && settings.codex && settings.otherAgents && settings.userSkills)
}

@Test func eachSwitchTurnsOffAlone() {
let root: JSONValue = .object([
"agents": .object(["guidance": .object(["codex": .bool(false), "userSkills": .bool(false)])]),
])
let settings = Self.snapshot(root).agentGuidance
#expect(settings.claude && !settings.codex && settings.otherAgents && !settings.userSkills)
}

@Test func everyKeyIsInTheSchema() {
let keys = Set(SettingsSchema.all.map(\.id))
for key in ["agents.guidance.claude", "agents.guidance.codex", "agents.guidance.otherAgents", "agents.guidance.userSkills"] {
#expect(keys.contains(key), "\(key)")
}
}
}
80 changes: 80 additions & 0 deletions cmux-tui/crates/acpmux/skills/cmux-chat/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
name: cmux-chat
description: "How a cmux chat shows your work: the plan list it draws, side sessions and offered sessions, links to other sessions, the closing recap, and editing cmux itself live. Read it at the start of a task with more than one step, before starting or suggesting another session, and before ending a turn that changed files."
---

# Working in a cmux chat

cmux NEXT draws your chat as a pane: your plan as a checklist, your sub-sessions in a
tray under the chat, session links as chips, offers as cards. These rules say when to
use each. They are cmux's defaults; the person's own instructions win.

## Plan list

Use it when the task has more than one step (edits plus a test, an investigation then a
fix, anything the person listed). Skip it for a one-command answer or plain talk.

- Claude Code: `TodoWrite`. Codex: `update_plan`. Other agents: their plan tool.
cmux draws the newest list as a checklist and keeps earlier lists as history.
- Write the whole plan in the first tool step, short imperative items (at most about
7 words), one item in progress, the rest pending.
- Mark an item done when it is done, in the same step that starts the next one.
Rewrite the whole list when the plan changes, so it never shows abandoned work.
- Never also write the plan as a numbered list in your reply: the checklist is the plan.

## Side sessions, sub-sessions and subagents

These tools exist only when cmux lists them (the person can turn sub-sessions off, and a
chat outside the cmux app shows no offers); skip what you do not have.

A sub-session is a new agent chat the person can open, steer and stop. Use one only for
work with its own result (a branch, a PR, a separate investigation) that can run while
you go on, never for a step you need the answer to right now: do that yourself or in your
harness's own subagent (Claude Code's Task/Agent tool), whose result comes back to you.

- `start_session` (server `cmux-subsessions`): starts a child now. Use it when the person
asked for parallel or background work. Pass `worktree: true` when it edits files that
yours may also touch. Its prompt carries the whole task: it starts with none of your
context. Read results with `wait_for_sessions` or `get_session`; steer with
`send_message`.
- `offer_session` (server `cmux-sessions`): one suggestion mid-turn for work you noticed
outside the task (a bug, a cleanup). The person starts it or not; do not do that work here.
- `suggest_tasks` (server `cmux-sessions`): at the end of a turn, up to 5 follow-ups the
person may want, drawn as one card.
- Not for: quick lookups, a handful of tool calls, or pieces of one change. Never start a
session the person did not ask for; offer it instead.
- To show a session's chat, `app_agent_open` (server `cmux`) with its sessionId.

## Linking sessions

When you mention another session (one you started, one that messaged you, one in a PR
footer), write a reference cmux turns into a clickable chip, in inline code:

- its `HOST/NAME`, as `list_sessions` or `acpmux ls` shows it (`huge-blue/tests`), or
- its agent link: `cmux://agent/<harness>/<sessionId>` or the
`https://cmux.com/deeplink/agent?...` link a PR footer carries.

Never invent an id. A name you only guessed stays plain text.

## Closing recap

End every turn that changed files, ran commands or finished multi-step work with a
short recap, the last thing you write:

- what is now true or changed, in two or three sentences;
- what you verified and how, and what is not done or not verified;
- the one decision the person must make next, if any, as a single question.

Skip it for a short answer or plain conversation. Do not restate the plan or the diff.

## Editing cmux live

When the person asks to change how cmux itself looks or behaves (a panel, a view, a
setting) and the `cmux-live` skill is listed, read it first: it edits the running app
and reloads it in place. For settings and a person's own views, use `cmux-customization`.

## The person's own skills

The person can add skills in `~/.config/cmux/skills/<name>/SKILL.md`. Claude Code lists
them as `cmux-user:<name>`; other agents get their names, descriptions and paths in the
chat's first message. Read one when its description matches the task.
Loading
Loading