@popoverinstall/cli 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +90 -19
  3. package/dist/changelog.d.ts.map +1 -1
  4. package/dist/changelog.js +8 -1
  5. package/dist/changelog.js.map +1 -1
  6. package/dist/daemon-lock.d.ts +102 -0
  7. package/dist/daemon-lock.d.ts.map +1 -0
  8. package/dist/daemon-lock.js +321 -0
  9. package/dist/daemon-lock.js.map +1 -0
  10. package/dist/doctor.d.ts.map +1 -1
  11. package/dist/doctor.js +13 -5
  12. package/dist/doctor.js.map +1 -1
  13. package/dist/exec.d.ts +49 -0
  14. package/dist/exec.d.ts.map +1 -0
  15. package/dist/exec.js +58 -0
  16. package/dist/exec.js.map +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +154 -35
  19. package/dist/index.js.map +1 -1
  20. package/dist/login.d.ts.map +1 -1
  21. package/dist/login.js +38 -5
  22. package/dist/login.js.map +1 -1
  23. package/dist/rename.d.ts +18 -0
  24. package/dist/rename.d.ts.map +1 -0
  25. package/dist/rename.js +252 -0
  26. package/dist/rename.js.map +1 -0
  27. package/dist/setup.d.ts.map +1 -1
  28. package/dist/setup.js +4 -8
  29. package/dist/setup.js.map +1 -1
  30. package/dist/snapshot.js +24 -4
  31. package/dist/snapshot.js.map +1 -1
  32. package/dist/theme.d.ts +5 -3
  33. package/dist/theme.d.ts.map +1 -1
  34. package/dist/theme.js +7 -5
  35. package/dist/theme.js.map +1 -1
  36. package/package.json +3 -3
  37. package/plugin/.claude-plugin/plugin.json +1 -1
  38. package/plugin/README.md +10 -2
  39. package/plugin/commands/ask.md +65 -0
  40. package/plugin/commands/fork.md +1 -1
  41. package/plugin/commands/team.md +37 -64
  42. package/plugin/commands/tell.md +66 -0
  43. package/plugin/scripts/announce-roster.mjs +5 -1
  44. package/plugin/scripts/emit-event.mjs +17 -0
  45. package/plugin/scripts/ensure-daemon.mjs +41 -1
  46. package/plugin/scripts/roster.mjs +5 -0
  47. package/plugin/skills/popover/SKILL.md +23 -8
package/plugin/README.md CHANGED
@@ -4,15 +4,23 @@ Installed into Claude Code; see the repo root README for the whole system.
4
4
 
5
5
  ```
6
6
  .claude-plugin/plugin.json metadata only
7
- commands/team.md the /team command
7
+ commands/team.md the /team command — the roster
8
+ commands/ask.md the /ask command
9
+ commands/tell.md the /tell command
8
10
  commands/fork.md the /fork command
11
+ skills/popover/SKILL.md when to reach for any of them unprompted
9
12
  hooks/hooks.json status feed (auto-discovered)
10
13
  .mcp.json bundled MCP server (auto-discovered)
11
14
  mcp/index.mjs team_list + team_ask + team_tell, zero dependencies
12
15
  scripts/ hook handlers, zero dependencies
13
16
  ```
14
17
 
15
- `/team` reaches the daemon through the MCP server. `/fork` deliberately does not: it shells
18
+ `/team`, `/ask` and `/tell` are one command per thing spent the roster costs nothing, an ask
19
+ costs the teammate's tokens, a tell costs their attention. They were one command with a
20
+ subword until 0.8.0, which put the cheapest and the most intrusive behind the same first word;
21
+ `/team ask …` and `/team tell …` still work and say so once.
22
+
23
+ They reach the daemon through the MCP server. `/fork` deliberately does not: it shells
16
24
  out to the `popover` binary instead, because opening a fork has to work for someone who is
17
25
  not signed in and may have no daemon running at all. That also means it adds nothing to the
18
26
  IPC protocol, and cannot regress asks, tells, or the roster.
@@ -0,0 +1,65 @@
1
+ ---
2
+ description: Ask a teammate's agent a question and wait for the answer — it runs on their machine, at their cost
3
+ # Quoted because an unquoted value starting with `[` is parsed as a YAML flow sequence,
4
+ # which fails and silently drops EVERY field here, allowed-tools included.
5
+ argument-hint: "[agent-handle] <question>"
6
+ allowed-tools: mcp__plugin_popover_popover__team_list, mcp__plugin_popover_popover__team_ask, AskUserQuestion
7
+ ---
8
+
9
+ # Ask a teammate's agent
10
+
11
+ The user ran `/popover:ask`. Arguments, which may be empty: **$ARGUMENTS**
12
+
13
+ Only agents working in **this repo** are reachable. A teammate running Claude Code in a
14
+ different repository is neither listed nor askable, so "nobody to ask" means nobody else is
15
+ working *here* — not that the team is idle. Say it that way if it comes up.
16
+
17
+ ## No arguments
18
+
19
+ Call `mcp__plugin_popover_popover__team_list`, print what it returns **verbatim** in a fenced
20
+ block, and show the form:
21
+
22
+ `/popover:ask B1 why did you rule out redis?`
23
+
24
+ Stop there. Do not ask anything on the user's behalf.
25
+
26
+ ## A handle and a question
27
+
28
+ Call `mcp__plugin_popover_popover__team_ask` with:
29
+
30
+ - `target`: the handle or name they used, exactly as typed
31
+ - `question`: the rest of their message, as a **self-contained** question. The answering agent
32
+ has none of this conversation — it cannot see the file you are looking at or the error you
33
+ just read. Expand every pronoun and vague reference ("that bug" → "the token refresh bug in
34
+ the auth service"), name files by path, and say what the user is trying to do rather than
35
+ only what they want to know. One well-formed question, not three exploratory ones: you get
36
+ one round trip for the price.
37
+
38
+ When the answer comes back, show it clearly attributed:
39
+
40
+ > **B1** (Bob Chen, api-server): …answer…
41
+
42
+ Then say how it bears on what you are working on, if it does.
43
+
44
+ ## A question with no handle
45
+
46
+ Call `mcp__plugin_popover_popover__team_list` first. Then:
47
+
48
+ - If exactly one agent plausibly fits, use it and say which one you chose and why.
49
+ - If several fit, call `AskUserQuestion` with up to 4 of them as options (label = the handle,
50
+ description = owner, repo, and what it is doing). Then use the chosen one.
51
+ - If none fit, say so and show the roster instead.
52
+
53
+ ## Rules
54
+
55
+ - **Never invent a handle.** Only use ones `team_list` returned. A handle from earlier in this
56
+ conversation may since have gone out of scope; if the tool says it does not match, relay that
57
+ rather than guessing at another agent.
58
+ - **One agent per question.** Never fan out across the roster to see who answers best.
59
+ - An answer comes from a **read-only copy** of their session. Their live session is not
60
+ interrupted and cannot be modified by an ask. It costs the teammate tokens on their own
61
+ machine, and nothing else.
62
+ - If the tool errors or times out, relay it plainly. Do not silently re-ask, and do not retry
63
+ a different agent unless the user asks.
64
+ - If the user is trying to hand a colleague *work* rather than get an answer, say plainly that
65
+ popover does not do that — the copy that answers cannot act — and let them message the human.
@@ -110,7 +110,7 @@ asked for in as many words.
110
110
  - Never print a code the user has not seen; it is the entire credential and the decryption
111
111
  key for the conversation behind it.
112
112
  - A fork is one-way. If the user seems to want an answer rather than to hand over the whole
113
- conversation, say that `/popover:team ask` is the cheaper thing and offer it instead.
113
+ conversation, say that `/popover:ask` is the cheaper thing and offer it instead.
114
114
  - Do not run `create` and then immediately `open` the result to "test" it. Every open counts
115
115
  against what the sharer sees, and the fork is already verified by the command exiting
116
116
  cleanly.
@@ -1,87 +1,60 @@
1
1
  ---
2
- description: See your team's active Claude Code agents in this repo, ask one a question, or tell one something
3
- # Quoted because an unquoted value starting with `[` is parsed as a YAML flow sequence,
4
- # which fails and silently drops EVERY field here, allowed-tools included.
5
- argument-hint: "[ask|tell] [agent-handle] [question or message]"
2
+ description: Show which of your teammates' Claude Code agents are active in this repo right now
3
+ # No argument-hint: this command takes none. The ask/tell tools are still allowed below
4
+ # because `/popover:team ask …` was the only form before 0.8.0 and is still carried out.
6
5
  allowed-tools: mcp__plugin_popover_popover__team_list, mcp__plugin_popover_popover__team_ask, mcp__plugin_popover_popover__team_tell, AskUserQuestion
7
6
  ---
8
7
 
9
8
  # Team agents
10
9
 
11
- The user ran `/popover:team`. Arguments, which may be empty: **$ARGUMENTS**
10
+ The user ran `/popover:team`. Arguments, which are usually empty: **$ARGUMENTS**
12
11
 
13
- Only agents working in **this repo** are visible. A teammate running Claude Code in a
14
- different repository is neither listed nor reachable, so an empty roster means nobody else is
15
- working *here* — not that the team is idle. Say it that way if it comes up.
12
+ This command shows the roster. Asking and telling are their own commands now `/popover:ask`
13
+ and `/popover:tell` because they cost different things and deserve to be chosen, not reached
14
+ by a subword.
16
15
 
17
- The first word of the arguments chooses what to do. Follow exactly one of these paths.
18
-
19
- ## 1. No arguments — show the roster
16
+ ## The roster
20
17
 
21
18
  Call `mcp__plugin_popover_popover__team_list`, then print what it returns **verbatim** in a
22
19
  fenced block. Do not reformat it, re-sort it, or add agents that are not in it.
23
20
 
24
- Then add one short line showing both forms:
25
- `/popover:team ask B1 why did you rule out redis?` · `/popover:team tell B1 the migration is applied`
26
-
27
- Stop there. Do not ask or tell anything on the user's behalf.
28
-
29
- ## 2. `ask <handle> <question>` — ask it and wait
30
-
31
- Call `mcp__plugin_popover_popover__team_ask` with:
32
-
33
- - `target`: the handle or name they used, exactly as typed
34
- - `question`: the rest of their message, as a self-contained question — the answering
35
- agent has none of this conversation's context, so expand pronouns and vague references
36
- ("that bug" → "the token refresh bug in the auth service")
21
+ Only agents working in **this repo** are visible. A teammate running Claude Code in a different
22
+ repository is neither listed nor reachable, so an empty roster means nobody else is working
23
+ *here* — not that the team is idle. Say it that way; the tool's own text already knows the
24
+ difference, so relay it rather than paraphrasing it into "your team is idle".
37
25
 
38
- When the answer comes back, show it clearly attributed:
26
+ Then add one short line showing where to go next:
39
27
 
40
- > **B1** (Bob Chen, api-server): …answer…
28
+ `/popover:ask B1 why did you rule out redis?` · `/popover:tell B1 the migration is applied`
41
29
 
42
- Then say how it bears on what you are working on, if it does. If the tool returns an
43
- error, relay it plainly — do not retry a different agent unless the user asks.
44
-
45
- ## 3. `tell <handle> <message>` — send it, expect nothing back
46
-
47
- Call `mcp__plugin_popover_popover__team_tell` with:
48
-
49
- - `target`: the handle or name they used, exactly as typed
50
- - `message`: the rest of their message, made self-contained the same way — it arrives with
51
- no context of its own, so name the thing rather than referring to it
52
-
53
- There is no answer. Confirm what was sent and to whom, in one line, and stop:
54
-
55
- > Told **B1** (Bob Chen): the repo_keys migration is applied on prod.
30
+ Stop there. Do not ask or tell anything on the user's behalf.
56
31
 
57
- A tell is for facts about shared state that would otherwise cause a collision. If the user
58
- is trying to give another agent instructions or hand it work, say that a tell does not do
59
- that — the receiving agent is told to treat it as information rather than a directive — and
60
- offer to ask instead.
32
+ ## If they typed `ask` or `tell` anyway
61
33
 
62
- ## 4. A question or message with no clear target pick one
34
+ `/popover:team ask …` and `/popover:team tell …` were the only forms before 0.8.0, so they are
35
+ still muscle memory. Do not answer with a correction and nothing else — carry it out, then
36
+ mention the shorter command once at the end in a single line. The user gets what they asked for
37
+ and learns the new name.
63
38
 
64
- Call `mcp__plugin_popover_popover__team_list` first. Then:
39
+ **`ask <handle> <question>`** — call `mcp__plugin_popover_popover__team_ask` with `target` set
40
+ to the handle exactly as typed and `question` set to the rest, rewritten to stand alone: the
41
+ answering agent has none of this conversation, so expand every pronoun and vague reference
42
+ ("that bug" → "the token refresh bug in the auth service") and name files by path. Show the
43
+ answer attributed — `> **B1** (Bob Chen, api-server): …` — then say how it bears on the work in
44
+ hand, if it does.
65
45
 
66
- - If exactly one agent plausibly fits, use it and say which one you chose and why.
67
- - If several fit, call `AskUserQuestion` with up to 4 of them as options (label = the
68
- handle, description = owner, repo, and what it is doing). Then use the chosen one.
69
- - If none fit, say so and show the roster instead.
46
+ **`tell <handle> <message>`** call `mcp__plugin_popover_popover__team_tell` with `target` and
47
+ `message`, made self-contained the same way. Nothing comes back; confirm in one line what was
48
+ sent and to whom, and stop. A tell is information, not a directive — if the user is trying to
49
+ hand over work, say so and offer an ask instead.
70
50
 
71
- If it is unclear whether the user meant to ask or to tell, prefer **ask** it interrupts
72
- nobody and costs only the answering machine some tokens, whereas a tell puts text into a
73
- colleague's context and notifies them.
51
+ If the tool errors or times out, relay it plainly rather than retrying or trying another agent.
74
52
 
75
53
  ## Rules
76
54
 
77
- - Never invent a handle. Only use ones `team_list` returned. A handle from earlier in this
78
- conversation may since have gone out of scope; if the tool says it does not match, relay
79
- that rather than guessing at another agent.
80
- - Asking costs the *teammate* money and runs on *their* machine. Telling costs their
81
- attention and notifies them. One per request either way; do not fan out to several agents
82
- unless explicitly asked.
83
- - An answer comes from a read-only copy of their session. Their live session is not
84
- interrupted and cannot be modified by an ask.
85
- - A tell **is** delivered into their live session, before its next prompt. That is the one
86
- thing in popover that reaches a running agent, so treat it as something the user should have
87
- meant to do.
55
+ - Never invent a handle. Only use ones `team_list` returned.
56
+ - Asking costs the *teammate* money and runs on *their* machine. Telling costs their attention
57
+ and notifies them. One agent per request either way.
58
+ - An answer comes from a read-only copy of their session. Their live session is not interrupted
59
+ and cannot be modified by an ask. A tell **is** delivered into their live session, before its
60
+ next prompt — that is the one thing in popover that reaches a running agent.
@@ -0,0 +1,66 @@
1
+ ---
2
+ description: Send a short heads-up into a teammate's live session — no answer comes back, and they are notified
3
+ # Quoted because an unquoted value starting with `[` is parsed as a YAML flow sequence,
4
+ # which fails and silently drops EVERY field here, allowed-tools included.
5
+ argument-hint: "[agent-handle] <message>"
6
+ allowed-tools: mcp__plugin_popover_popover__team_list, mcp__plugin_popover_popover__team_tell, AskUserQuestion
7
+ ---
8
+
9
+ # Tell a teammate's agent
10
+
11
+ The user ran `/popover:tell`. Arguments, which may be empty: **$ARGUMENTS**
12
+
13
+ A tell is the one thing in popover that reaches a **running** agent: it is delivered into a
14
+ teammate's live session before its next prompt, and their user is notified. It costs a
15
+ colleague's attention rather than their tokens, which is why it is its own command and why the
16
+ bar for sending one is higher than for an ask.
17
+
18
+ Only agents working in **this repo** are reachable.
19
+
20
+ ## No arguments
21
+
22
+ Call `mcp__plugin_popover_popover__team_list`, print what it returns **verbatim** in a fenced
23
+ block, and show the form:
24
+
25
+ `/popover:tell B1 the repo_keys migration is applied on prod`
26
+
27
+ Stop there. Do not send anything on the user's behalf.
28
+
29
+ ## A handle and a message
30
+
31
+ Call `mcp__plugin_popover_popover__team_tell` with:
32
+
33
+ - `target`: the handle or name they used, exactly as typed
34
+ - `message`: the rest of their message, made **self-contained** — it arrives with no context of
35
+ its own, so name the thing rather than referring to it. One or two sentences.
36
+
37
+ Nothing comes back. Confirm what was sent and to whom, in one line, and stop:
38
+
39
+ > Told **B1** (Bob Chen): the repo_keys migration is applied on prod.
40
+
41
+ ## A message with no handle
42
+
43
+ Call `mcp__plugin_popover_popover__team_list`, then **confirm the recipient with
44
+ `AskUserQuestion` before sending** — up to 4 candidates, label = the handle, description =
45
+ owner, repo, and what it is doing. Do this even when only one agent plausibly fits.
46
+
47
+ Naming a handle is how the user says *which colleague to interrupt*; if they did not name one,
48
+ that choice has not been made yet and it is not yours to make. This is the one place a tell
49
+ differs from an ask, which may pick its own target because the only thing it spends is tokens.
50
+
51
+ If none fit, show the roster instead. Never broadcast to several agents.
52
+
53
+ ## Rules
54
+
55
+ - **What a tell is for:** facts about shared state that would otherwise cause a collision — a
56
+ migration you just applied, a branch you force-pushed, a file you are about to rewrite that
57
+ their agent is working in right now.
58
+ - **What it is not for:** instructions or handing over work. The receiving agent is explicitly
59
+ told to treat the message as information rather than a directive. If the user is trying to
60
+ give another agent a task, say so and offer `/popover:ask` instead — or suggest they message
61
+ the human.
62
+ - **Never invent a handle**, and do not fan out to several agents unless asked in as many words.
63
+ - Rate limited to 10 messages an hour to any one agent, which is a hint about its intended
64
+ frequency. If the limit is hit, relay that rather than retrying.
65
+ - If it is genuinely ambiguous whether the user meant to ask or tell, prefer **`/popover:ask`**
66
+ — it interrupts nobody.
@@ -37,8 +37,12 @@ try {
37
37
  // `refresh: false` is what makes the tight timeout survivable — this reads whatever the
38
38
  // daemon already has, so it costs one socket round trip and no network. A hook sitting
39
39
  // between a keystroke and the model must never be the thing that calls the cloud.
40
+ // `cwd` matters most here of anywhere: this fires on the first prompt of a session, which
41
+ // is exactly when the daemon may not yet have resolved that session's repo — and the line
42
+ // it prints claims the agents are "in this repo". Without the hint an unplaceable caller
43
+ // is scoped by recency across the whole machine, so the claim could be false.
40
44
  const reply = await request(
41
- { t: "roster", id: "hook", refresh: false, fromSessionId: sessionId },
45
+ { t: "roster", id: "hook", refresh: false, fromSessionId: sessionId, cwd: process.cwd() },
42
46
  { timeoutMs: 250 },
43
47
  );
44
48
 
@@ -13,6 +13,23 @@
13
13
 
14
14
  import { readStdin, sendFireAndForget } from "./_ipc.mjs";
15
15
 
16
+ /*
17
+ * Stay silent inside a fork — the same guard deliver-messages.mjs and announce-roster.mjs
18
+ * carry, and for a sharper reason than either of them.
19
+ *
20
+ * A fork is a real Claude Code session with its own session id, and it runs the plugin's
21
+ * hooks like any other. Without this, every answered ask reported PreToolUse, PostToolUse and
22
+ * Stop for that throwaway session, and the daemon created a session for it — so every ask put
23
+ * a ghost agent on the team roster, in the answering machine's repo, that nobody could reach
24
+ * and that never went away until it was reaped.
25
+ *
26
+ * Worse, it poisoned attribution. `SessionStore.resolveCaller` infers who is calling from the
27
+ * highest `lastEventAt`, and a fork that has just finished answering is by construction the
28
+ * most recent thing to have emitted an event on this machine — so a tell sent moments after
29
+ * answering an ask was recorded as coming from the fork.
30
+ */
31
+ if (process.env.CLAUDE_CODE_ENTRYPOINT === "popover-fork") process.exit(0);
32
+
16
33
  try {
17
34
  const raw = await readStdin();
18
35
  const payload = JSON.parse(raw);
@@ -12,6 +12,16 @@ import { daemonAddress, popoverHome, readStdin, request, sendFireAndForget } fro
12
12
 
13
13
  const HERE = path.dirname(fileURLToPath(import.meta.url));
14
14
 
15
+ /*
16
+ * A fork has nothing to do here, and doing it is actively harmful.
17
+ *
18
+ * The daemon spawns forks itself, so a daemon demonstrably exists — and registering the
19
+ * fork's session id would put a ghost agent on the team roster for every ask answered. Same
20
+ * guard, same reason, as emit-event.mjs; see its header for what the ghost then does to
21
+ * attribution.
22
+ */
23
+ if (process.env.CLAUDE_CODE_ENTRYPOINT === "popover-fork") process.exit(0);
24
+
15
25
  async function main() {
16
26
  const raw = await readStdin().catch(() => "");
17
27
  let payload = null;
@@ -22,7 +32,12 @@ async function main() {
22
32
  }
23
33
 
24
34
  const alive = await ping();
25
- if (!alive) {
35
+ // `updateInProgress()` is the interlock, not an optimisation. `popover update` stops the
36
+ // daemon and hands npm up to five minutes to replace the global install; a daemon started
37
+ // in that window runs out of the tree being replaced and holds its files open, so npm's
38
+ // rename fails EPERM/EBUSY on Windows and the update aborts half-done. Every session on the
39
+ // machine runs this hook, so without the check one of them wins that race almost every time.
40
+ if (!alive && !updateInProgress()) {
26
41
  const entry = resolveDaemonEntry();
27
42
  if (entry) {
28
43
  launch(entry);
@@ -95,6 +110,31 @@ function resolveDaemonEntry() {
95
110
  return null;
96
111
  }
97
112
 
113
+ /**
114
+ * Whether `popover update` is replacing the install tree right now.
115
+ *
116
+ * Written by `beginUpdateLock` in packages/cli/src/daemon-lock.ts, which holds the readable
117
+ * version of why this exists. Duplicated here rather than imported for the same reason
118
+ * `resolveDaemonEntry` duplicates its path: this file must resolve no imports beyond the ones
119
+ * it already has, because a hook that throws breaks every session on the machine. Keep the
120
+ * two in step.
121
+ *
122
+ * Every branch fails towards starting the daemon. A marker with no deadline, or one whose
123
+ * deadline has passed, is a crashed update rather than a running one, and refusing to start
124
+ * on that basis would leave a machine permanently without a daemon and nothing on screen to
125
+ * explain it. Only a marker that is present, readable and unexpired holds us back.
126
+ */
127
+ function updateInProgress() {
128
+ try {
129
+ const file = path.join(popoverHome(), "update.lock");
130
+ if (!existsSync(file)) return false;
131
+ const deadline = Number(JSON.parse(readFileSync(file, "utf8")).deadline);
132
+ return Number.isFinite(deadline) && Date.now() < deadline;
133
+ } catch {
134
+ return false;
135
+ }
136
+ }
137
+
98
138
  function launch(entry) {
99
139
  try {
100
140
  // Detached with stdio to a log file: the daemon must outlive this hook, this session,
@@ -18,6 +18,11 @@ const reply = await request(
18
18
  id: "cli",
19
19
  refresh: true,
20
20
  ...(callerSessionId() ? { fromSessionId: callerSessionId() } : {}),
21
+ // Fallback for the window where the daemon has not yet resolved this session's repo —
22
+ // the same hint the MCP server sends. Without it an unplaceable caller falls through to
23
+ // pure recency across every session on the machine, so `/popover:team` run in one repo
24
+ // could print the roster for another, under a heading claiming it was this one.
25
+ cwd: process.cwd(),
21
26
  },
22
27
  { timeoutMs: 8000 },
23
28
  );
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: popover
3
- description: Reach a teammate's Claude Code agent working in this same repo. Use when a question is about what a colleague decided, already ruled out, or is changing right now — why the code is the way it is when the repo does not say, whether work you are about to start is already in flight, or why something you did not touch is broken. Also covers handing this whole conversation to a teammate as a fork.
3
+ description: "Judgment for reaching a teammate's Claude Code agent *without being asked to* — when it is worth spending their tokens, and which of /popover:team, /popover:ask, /popover:tell and /popover:fork fits. Nothing to invoke by hand: use it when a question turns on what a colleague decided, ruled out, or is changing right now — why the code is the way it is when the repo does not say, whether work about to start is already in flight, or why something nobody touched is broken."
4
4
  ---
5
5
 
6
6
  # popover
@@ -10,10 +10,21 @@ accumulated context you do not have: the alternatives they rejected, the constra
10
10
  forced an awkward shape, what they changed twenty minutes ago and have not pushed. popover
11
11
  lets you reach those agents.
12
12
 
13
- The reason this skill exists is that the moment to use popover rarely announces itself. The
13
+ **This skill is not a command, and there is nothing here for a user to invoke.** The four
14
+ commands are the things a user types:
15
+
16
+ | | |
17
+ | --- | --- |
18
+ | `/popover:team` | who is working in this repo right now |
19
+ | `/popover:ask` | one question to one agent, and you wait for the answer |
20
+ | `/popover:tell` | a heads-up into a teammate's live session; nothing comes back |
21
+ | `/popover:fork` | hand this whole conversation to a teammate |
22
+
23
+ This file is the judgment around them: when reaching for one is worth it, which one fits, and
24
+ what each one spends. It exists because the moment to use popover rarely announces itself. The
14
25
  user asks why something is the way it is, and the honest answer is that the repo does not
15
- record it — but a colleague's agent worked it out this morning. Nobody will type
16
- `/popover:team` for you. Noticing is your job.
26
+ record it — but a colleague's agent worked it out this morning. Nobody will type `/popover:ask`
27
+ for you. Noticing is your job.
17
28
 
18
29
  ## The three tools
19
30
 
@@ -139,7 +150,11 @@ The tools return errors as text; relay them rather than retrying with different
139
150
 
140
151
  ## The user-facing commands
141
152
 
142
- `/popover:team` and `/popover:fork` carry the full flows for when the user drives this
143
- explicitly, including how to present a roster and how to take delivery of a fork. Do not
144
- restate their steps here or work around them when the user invokes a command, follow it.
145
- This skill is for the case where nobody invoked anything and you noticed the moment yourself.
153
+ The four commands carry the full flows for when the user drives this explicitly — how to
154
+ present a roster, how to word an ask, how to take delivery of a fork. Do not restate their
155
+ steps here or work around them: **when the user invokes a command, follow that command.** This
156
+ skill is for the other case, where nobody invoked anything and you noticed the moment yourself.
157
+
158
+ When you act on your own, prefer naming the command you are standing in for — "I'll run the
159
+ equivalent of `/popover:ask B1`" — so the user learns the surface they can drive directly next
160
+ time.