agentleFS
amntoppoamntoppo/MasterDeck-Terminal/skills/master/SKILL.md

master

Skill4 starsChanged 25 days agoMITView on GitHub

Use when the user types /master, asks what work is pending across their Claude sessions, wants issues/PRs/meeting action items delegated to sessions, asks to message or spawn a session for an issue, asks to start a session (from scratch with a task, or for board tickets: "start sessions for #42 and api#12", "pick up everything in Ready For Dev assigned to me"), when a /loop sweep fires, or when a `master watch` Monitor event arrives. Runs only in the master session (configured masterName, default master-agent).

5 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

1 rule. 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 0

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Run ListAgents. The first line says this session's name.

    line 31

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

master — delegate the user's work to their Claude sessions

This session reads the user's GitHub issues and PRs, their meeting action items (optional, if a Fathom MCP connector is available), and the list of live Claude sessions. It turns them into proposals, and it only messages or spawns a session after the user says yes to that proposal.

The CLI is ~/.claude/skills/master/master (called master below). The ledger is ~/.claude/master/ledger.json. Never edit the ledger by hand. Every write goes through the CLI so dedup holds.

Configuration

Everything user-specific lives in one file, ~/.claude/master/config.json, created by MasterDeck's Setup screen or by master config save. master config show prints it; master config get <field> prints one field. Fields:

  • owner — the GitHub org or user that owns the repos.
  • issueRepo — the primary repo holding the issues.
  • repos — every selected repo (owner/name), primary first; allRepos: true means any repo counts. projects — every selected board, each with its own status field, columns and statuses.
  • project — GitHub Projects v2 number of the (first) board (0 = no board).

Ticket references. A ticket is a repo and a number. In the primary repo it is written #N (and stored as a bare number, as before); in another repo it is name#N (for example api#12), or owner/name#N in full. Wherever this skill says #N, read "the ticket's label": #N or name#N. Sessions report with that label (api#12: done). master add / master draft-assign take --issue as 12, api#12 or acme/api#12; master list shows a repo field on proposals for other repos. When matching a report to a proposal, match both the number and the repo (no repo field means the primary repo).

  • columns — the board's status names, in order.
  • statuses.inProgress (default In Dev), statuses.prRaised (default PR Raised), statuses.devDone (default Dev Done), and the lists statuses.done, statuses.assignable, statuses.resumable.
  • workspace — the folder master runs in, and where the user's repositories are cloned. An account in accounts may carry its own workspace. A spawned session's cwd is the checkout of the ticket's repository found in its account's workspace, else that workspace: master fills it in (ASSIGN proposals, and master add without --cwd for an ASSIGN or PRREVIEW of a real issue; any other kind, and issue 0, start in the workspace), so do not pass --cwd unless the user named a folder. master checkout <owner/name> prints where a repository's sessions start.
  • masterName — this session's name (default master-agent).

Below, master-agent stands for the configured masterName; substitute it if the user changed it. "The user" is the person who owns this config.

Guard

Run ListAgents. The first line says this session's name. If it is not the configured masterName (master config get masterName, default master-agent), stop and say: "Run /master from the session named <masterName> (/rename <masterName>)."

Watching the app — keep this armed

The MasterBar app (and master say in any shell) writes to master's inbox and approves proposals. master watch turns both into events.

  • On the first /master of a session, and whenever a Monitor notice says the master inbox and approvals watch expired, arm it: Monitor with command ~/.claude/skills/master/master watch, description master inbox and approvals, timeout_ms 1800000. Only one watch at a time.
  • Its first events are the backlog: everything queued while master was closed. Handle them in order.
  • inbox N say: <text> → handle it as a chat message (next section).
  • inbox N sweep → run the sweep below, then
    master reply N - <<'MASTER_EOF_7f3a'
    <the batch text, or 'no new proposals'>
    MASTER_EOF_7f3a
    master ack N
  • approved N … → check that N is still listed by master list --status approved. If it is, dispatch it exactly as in "The user's answer — dispatch" step 2. If it isn't, ignore the event: it was already dispatched.
  • inbox N send → NAME: text → this is the user messaging NAME directly (from the MasterBar app or master say --to), and that counts as their yes. Do this:
    1. master add --kind CHAT --issue <the issue of NAME's latest proposal, or 0> --source chat:N --session NAME \
        --message - <<'MASTER_EOF_7f3a'
      <text>
      
      (From the user via master-agent. Reply to master-agent with SendMessage; if it is not reachable, do not message any other session — answer the user here.)
      MASTER_EOF_7f3a
    2. master approve <id>, then master mark <id> sent.
    3. SendMessage to NAME with that message, notify_when_idle: true. As soon as it succeeds, run master mark <id> done --note delivered — a CHAT never sits in sent, so it cannot mask the session's real work. (If it fails, mark it held as in dispatch step 2.)
    4. Find NAME's latest non-CHAT proposal. If it is question or blocked, run master mark <that id> sent — the user has answered it (blocked → sent is allowed). If it is done and this message gives NAME new work — not just a question like "any new comments?" — also run master mark <that id> sent (done → sent is allowed) so the app shows the session working again; a question alone leaves a done proposal as done. Say in the reply which of these happened, if either.
    5. master reply N with sent to NAME (proposal <id>), then master ack N.

Chat from the app

A chat message is the user talking, the same as typing here. Always answer, then ack:

master reply N - <<'MASTER_EOF_7f3a'
<text>
MASTER_EOF_7f3a
master ack N

⏎ in the text marks a line break.

  • An instruction that names what to send and to whom ("tell paywall to skip the web half") is the user's yes. Run
    master add --kind CHAT --issue <the issue, or 0 if none applies> --source chat:N --session <name> \
      --message - <<'MASTER_EOF_7f3a'
    <the message, first line self-contained>
    MASTER_EOF_7f3a
    then master approve <id>, dispatch it, and reply sent to <name> (proposal <id>).
  • A request to start a session ("start a session in MasterDeck-Terminal to fix the flaky tests", "pick up #42 and #57", "start everything in Ready For Dev assigned to me"): see "Start sessions on demand" below; reply with the plan master start printed.
  • An open-ended request ("deal with the paywall stuff") becomes a CHAT proposal. Add it the same way but do not approve it, and reply proposal <id> is waiting for your approval.
  • A question ("what is paywall doing?") gets an answer in the reply. Use master status, ListAgents and the transcripts as needed.
  • An approval or rejection in words ("approve 3 and 5") → master approve / master reject, and dispatch as usual.

/master or /master sweep — find work

  1. Run master sweep. It prints no new proposals or a numbered batch. It also prints sources missing: … if gh, the board, or claude agents failed. Always pass that line on to the user.
  2. Meetings (optional). Only if a Fathom MCP connector is available in this session; otherwise skip this step without comment. Run master cursor to get meetings_since. It is null on the first run; use 24 hours ago in that case. Call Fathom list_meetings since then. For each meeting, call get_meeting_summary. Take only action items that name the user or an issue number (#N, <issueRepo>#N), or that clearly match the title of an in-scope issue (master snapshot lists them).
    • Item matches an issue that has an owner session: compute a collision-free source key from the item text (verbatim, not truncated), then add the proposal with the message piped in:
      master key <<'MASTER_EOF_7f3a'
      <item text>
      MASTER_EOF_7f3a
      master add --kind MEETING --issue N --source fathom:<recording_id>:<key from master key> \
        --session <owner> --message - <<'MASTER_EOF_7f3a'
      #N: from <meeting title> <date> — <item>
      
      <one or two sentences of context from the summary>
      
      When you are done, blocked or have a question, tell master-agent with SendMessage. First line: '#N: done', '#N: blocked — <reason>' or '#N: question — <question>'. When you have a question, ALSO ask the user directly in this session (so they see it here too), and wait for the answer from either place. If the user answers you here, tell master-agent '#N: answered — <answer>'. If SendMessage says master-agent is not reachable, do not send it to any other session (none of them is master-agent, whatever its name); ask the user here instead.
      MASTER_EOF_7f3a
    • Item matches an issue with no owner: do not add it. Mention it under the batch; ASSIGN covers spawning.
    • Item matches no issue: list it for the user under "unmatched action items". Never create an issue.
    • Fetch a transcript (get_meeting_transcript) only when the summary is too vague to write the message.
    • When done, move the cursor to the start time of the latest meeting you fully processed — never past a meeting whose summary is not ready yet; leave that one for the next sweep. master cursor --meetings-since <that meeting's start time, UTC, %Y-%m-%dT%H:%M:%SZ>.
    • If the Fathom connector is present but fails, say "meetings: unavailable (<error>)" and skip this step. Do not move the cursor.
  3. Show the user one combined batch. Use the sweep output, plus a line per added MEETING in the same format (master list --status proposed gives the ids). Then stop and wait for the user's answer. Do not dispatch anything before the user answers.
  4. If there is nothing to report and this was a /loop fire, say nothing beyond one line: sweep: nothing new.

Start sessions on demand

master start turns "start a session for X" into proposals: it reads what it needs, records each start in the ledger and prints the plan (id, ticket, session name, folder, model, permission mode, account, warnings) and what it refused. Every start is a real claude --bg session, listed by claude agents and in MasterDeck the moment it starts, and it keeps running when this session exits.

  • From scratch (no ticket): the task goes in --prompt (- reads stdin, use the heredoc):
    master start --prompt - [--repo owner/name | --cwd <folder>] [--name <name>] [--worktree | --worktree-name <name>] \
      [--model <model>] [--permission-mode plan|acceptEdits|auto|dontAsk|manual] [--account <login>] <<'MASTER_EOF_7f3a'
    <the task, in the user's words>
    MASTER_EOF_7f3a
    It becomes a START proposal (issue 0). The name comes from the task unless --name; the folder is the repository's checkout in the account's workspace (--repo), else the workspace; --worktree starts it in a new git worktree (claude --worktree) named after the session, --worktree-name <name> names it (one start only). The session reports with its name as the label (<name>: done).
  • Board tickets by reference: master start 42 api#12 acme/web#7 [--prompt - <<…instructions…] (also comma separated).
  • Board tickets by filter: master start --column "Ready For Dev" --assignee @me [--label bug] [--sprint @current|<title>|none|any] [--limit 10]. Column and labels match without case; --assignee takes a login, @me or none. Finished columns are left out unless --column names one.
  • A ticket's start is an ASSIGN proposal: it starts in the ticket's repository checkout (as the sweep's ASSIGN does), its first prompt is master's ASSIGN prompt plus the ticket's description and sub-issues (as context: the session still stops and asks the user, unless the user gave instructions with --prompt, which it follows), and MasterDeck links the session to the ticket when it shows up (Details, PR watch and board moves work; the card moves to In Dev) and assigns the ticket to the user (--no-assign leaves the assignees alone). A sweep proposal still waiting for the same ticket, or a start of it held (for example refused for its folder), is replaced (rejected) by it. --worktree with tickets gives each session its own worktree.
  • Options for both: --model, --permission-mode (never bypassPermissions), --account (two or more accounts), --cwd (only when the user named a folder), --name (one start only), --dry-run (show the plan, add nothing).
  • Refused, and said under "not started:" (never added): a ticket not found, closed, a pull request, in a repository not selected in Setup, one that already has a running session (its name and claude attach id are given), or one whose proposal is already approved. Warnings (added, said on the start's line): no checkout of the repository was found (it starts in the workspace), Claude Code was never allowed to work in the folder (the start would be refused until the user runs claude there and accepts its prompt; MasterDeck's Needs you has Open Claude there…), an earlier session of the ticket stopped (its ORPHAN would resume it instead). Pass every line of the output on to the user.
  • The yes. Show the plan and wait: the user approves all, some ids, or drops some ("start 14 and 16, not 15"): master approve / master reject those ids and dispatch as below. A batch is one list; ids the user did not keep are rejected. Only when the user's own words already say to start now ("start it now", "go ahead and start …") add --yes: it approves and spawns at once and prints a started <name> (proposal N) — claude attach <id> line per session, or why one did not start (left held; nothing is retried until the user says retry).

The user's answer — dispatch

The user answers in free text, for example 1 3 yes, 2 no, all yes, or 2 yes but tell it to skip the web half.

  1. master approve <ids> and master reject <ids>. Ids the user didn't mention stay proposed. Ask about them once, at the end.
  2. For each approved proposal, master list --status approved gives its target and message:
    • Session target: run master mark <id> sent first, so the watch never offers it twice. If master mark <id> sent fails, do not send. Then SendMessage to target.session with message exactly as stored, plus any extra instruction the user gave, appended as a final paragraph. Pass notify_when_idle: true. If the proposal's kind is CHAT, run master mark <id> done --note delivered right after SendMessage succeeds.
    • Spawn target: master spawn <id>. On success it prints started <name> (proposal <id>) — claude attach <id>. The new session appears in ListAgents under its name. On failure it prints the error and the proposal becomes held with a note. Never retry on your own; tell the user, and run master spawn <id> again only when they say retry. If the error says the workspace is not trusted, Claude Code was never allowed to work in that folder: tell the user that MasterDeck's Needs you has this start with Open Claude there… and Try again (or to run claude in that folder once and accept its prompt). Never answer that prompt or edit ~/.claude.json yourself.
    • If SendMessage fails or says the name doesn't match a live session, run:
      master mark <id> held --note - <<'MASTER_EOF_7f3a'
      <why>
      MASTER_EOF_7f3a
      and tell the user. sent → held is allowed.
  3. Report in one short list: what was sent to whom, what spawned (with claude attach <id>), and what failed.

Messages and notices from sessions

  • A session started from scratch (START, issue 0) reports with its own name in place of #N (flaky-tests: done): match it to the START proposal whose spawn name is that session, and handle it as below.
  • A <cross-session-message from="X"> whose first line is #N: done or #N: blocked — …: find the sent proposal for that session and issue (master list --status sent), then master mark <id> done or:
    master mark <id> blocked --note - <<'MASTER_EOF_7f3a'
    <reason>
    MASTER_EOF_7f3a
    Tell the user in one line. For blocked, include the reason.
  • A <cross-session-message from="X"> whose first line is #N: question — …: find that session's latest non-CHAT proposal for issue N in any of sent / question / done / blocked (master list --status sent, then --status question, then --status done, then --status blocked — a second question before an answer replaces the first, and done → question reopens a finished one), then
    master mark <id> question --note - <<'MASTER_EOF_7f3a'
    <the question>
    MASTER_EOF_7f3a
    and tell the user the question in one line. It then appears in MasterBar automatically.
  • A <cross-session-message from="X"> whose first line is #N: answered — …: the session is reporting that the user answered its question directly, in that session — find the same proposal (as above) and run
    master mark <id> sent --note - <<'MASTER_EOF_7f3a'
    <the answer>
    MASTER_EOF_7f3a
    tell the user the answer in one line, and do not run master reply — there is no inbox entry to answer, this only came in as a cross-session message.
  • Any other message from a session: tell the user what it said in one or two lines. Reply only if they ask you to.
  • A [Cross-session delivery notice] saying delivery was held or refused: run
    master mark <id> held --note - <<'MASTER_EOF_7f3a'
    <notice>
    MASTER_EOF_7f3a
    and tell the user. Never treat silence as agreement.
  • A [Cross-session idle notice] for a session with a sent non-CHAT proposal and no reply yet: send one message, "#N: master-agent here — you went idle; are you done, blocked or stuck on a question? Reply with '#N: done', '#N: blocked — <reason>' or '#N: question — <question>'. When you have a question, ALSO ask the user directly in this session (so they see it here too), and wait for the answer from either place. If the user answers you here, tell master-agent '#N: answered — <answer>'.", with notify_when_idle: true. CHAT proposals are marked done on delivery, so idle notices never apply to them. If the next idle notice also arrives with no reply, tell the user: "<session> went idle twice without reporting on #N."

/master status

Run master status. Show the roster as printed. Sessions with status blocked show "needs input — claude attach <id>" — that command reattaches to them. Then list proposals that are sent, held or blocked (master list --status …).

/master loop

Start the sweep loop: invoke the loop skill with 20m /master sweep. Tell the user it stops when this session closes and expires after 7 days.

When sessions stop

Background sessions run under Claude Code's daemon, not under the terminal or process that started them, so closing those does not stop them. They stop when the machine restarts or crashes (every process ends), when someone runs claude stop, or when the daemon retires one that sat idle for hours. The conversation is always kept on disk; claude --bg --resume <session-id> continues it in the background under the same id — with no other flag: any flag (-n, --model, --settings) starts a copy and leaves the old session listed, so never add one by hand (master spawn adds --settings only when the proposal names another account than the session was recorded as; it never removes a session, and it holds a resume when claude agents fails, since it cannot tell whether the session already runs).

So never tell the user a session is "gone" or that sessions "don't survive Claude Code exiting". Say it stopped, why if the roster shows it (e.g. every session stopped at once: the machine restarted), and how to get it back: MasterDeck's Resume all banner after a restart, or approving the ORPHAN proposal the sweep makes for a stopped session whose issue is still in progress. master spawn does not resume a session that is already running again.

Rules that never bend

  • Nothing is sent or spawned without the user's yes on that specific proposal id.
  • The CLI refuses write commands from any Claude session other than the master session by design; never work around it (no editing the ledger by hand, no faking CLAUDE_CODE_SESSION_ID).
  • Never create issues, comment on GitHub, move board status, assign anyone, push, or merge from this session. Sessions (and MasterDeck, for board moves, a master start ticket's assignee and PR watching) do that, after the user gives them instructions.
  • master start --yes only on the user's own words to start now, for that request; never for a batch the user has not seen.
  • Never pass --dangerously-skip-permissions or --allow-dangerously-skip-permissions to anything.
  • Never ask a session to do something that was denied or blocked in this session.
  • Send message text inline. @file references attach nothing across sessions.
  • For ad-hoc GitHub reads in this session, use ghc (same arguments as gh) instead of gh. It shares one cache and one rate-limit pause with the sweep, MasterDeck and the babysit skills (when installed). ghc --status shows whether GitHub is paused.
  • Never store meeting transcripts. The ledger gets only the meeting id and the action item text.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.