@popoverinstall/cli 0.4.1 → 0.6.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.
- package/CHANGELOG.md +158 -0
- package/dist/aardvark.d.ts +30 -0
- package/dist/aardvark.d.ts.map +1 -0
- package/dist/aardvark.js +97 -0
- package/dist/aardvark.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +140 -52
- package/dist/index.js.map +1 -1
- package/dist/login.d.ts +8 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +51 -23
- package/dist/login.js.map +1 -1
- package/dist/next-steps.d.ts +18 -0
- package/dist/next-steps.d.ts.map +1 -0
- package/dist/next-steps.js +73 -0
- package/dist/next-steps.js.map +1 -0
- package/dist/scene.d.ts +43 -0
- package/dist/scene.d.ts.map +1 -0
- package/dist/scene.js +276 -0
- package/dist/scene.js.map +1 -0
- package/dist/snapshot.d.ts +2 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +828 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/terminal.d.ts +58 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +139 -0
- package/dist/terminal.js.map +1 -0
- package/dist/theme.d.ts +123 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +257 -0
- package/dist/theme.js.map +1 -0
- package/dist/welcome.d.ts +23 -0
- package/dist/welcome.d.ts.map +1 -0
- package/dist/welcome.js +128 -0
- package/dist/welcome.js.map +1 -0
- package/package.json +5 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +11 -1
- package/plugin/commands/fork.md +118 -0
- package/plugin/commands/team.md +44 -20
- package/plugin/hooks/hooks.json +6 -0
- package/plugin/mcp/index.mjs +69 -0
- package/plugin/scripts/deliver-messages.mjs +76 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first screen anyone sees: what the installer hands off to the moment it finishes.
|
|
3
|
+
*
|
|
4
|
+
* The aardvark, a box saying what this is, and a single question — log in? — that Enter
|
|
5
|
+
* answers. Everything past that point is the existing device flow in `login.ts`; this
|
|
6
|
+
* module owns the framing and the one keypress, not the protocol.
|
|
7
|
+
*
|
|
8
|
+
* Two things it must never do:
|
|
9
|
+
*
|
|
10
|
+
* 1. **Block a non-interactive install.** The installer only hands off when it has a
|
|
11
|
+
* terminal, but `popover welcome` is a real command and can be piped or run in CI. With no
|
|
12
|
+
* TTY it prints the same next steps the installer used to print and exits 0.
|
|
13
|
+
* 2. **Fail the install.** A machine with popover on it and no login is a fine outcome — the
|
|
14
|
+
* user runs `popover login` later. Every exit here is 0 unless the login itself was
|
|
15
|
+
* attempted and failed.
|
|
16
|
+
*/
|
|
17
|
+
/** Re-exported from where they now live, alongside the send-off that uses them. */
|
|
18
|
+
export { BRAND, TAGLINE, TAGLINE_SECONDARY } from "./next-steps.js";
|
|
19
|
+
export interface WelcomeOptions {
|
|
20
|
+
apiUrl: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function welcome({ apiUrl }: WelcomeOptions): Promise<number>;
|
|
23
|
+
//# sourceMappingURL=welcome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../src/welcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,mFAAmF;AACnF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAoDzE"}
|
package/dist/welcome.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first screen anyone sees: what the installer hands off to the moment it finishes.
|
|
3
|
+
*
|
|
4
|
+
* The aardvark, a box saying what this is, and a single question — log in? — that Enter
|
|
5
|
+
* answers. Everything past that point is the existing device flow in `login.ts`; this
|
|
6
|
+
* module owns the framing and the one keypress, not the protocol.
|
|
7
|
+
*
|
|
8
|
+
* Two things it must never do:
|
|
9
|
+
*
|
|
10
|
+
* 1. **Block a non-interactive install.** The installer only hands off when it has a
|
|
11
|
+
* terminal, but `popover welcome` is a real command and can be piped or run in CI. With no
|
|
12
|
+
* TTY it prints the same next steps the installer used to print and exits 0.
|
|
13
|
+
* 2. **Fail the install.** A machine with popover on it and no login is a fine outcome — the
|
|
14
|
+
* user runs `popover login` later. Every exit here is 0 unless the login itself was
|
|
15
|
+
* attempted and failed.
|
|
16
|
+
*/
|
|
17
|
+
import { loadCredentials } from "@popoverinstall/shared";
|
|
18
|
+
import { aardvarkLines } from "./aardvark.js";
|
|
19
|
+
import { sceneLines } from "./scene.js";
|
|
20
|
+
import { login } from "./login.js";
|
|
21
|
+
import { BRAND, TAGLINE, TAGLINE_SECONDARY, nextSteps } from "./next-steps.js";
|
|
22
|
+
import { badge, box, glyph, line, step, style, trunk, wrap } from "./theme.js";
|
|
23
|
+
/** Re-exported from where they now live, alongside the send-off that uses them. */
|
|
24
|
+
export { BRAND, TAGLINE, TAGLINE_SECONDARY } from "./next-steps.js";
|
|
25
|
+
export async function welcome({ apiUrl }) {
|
|
26
|
+
const alreadyConnected = Boolean(loadCredentials());
|
|
27
|
+
console.log("");
|
|
28
|
+
// The full scene where there is room for it, the aardvark on its own where there is not.
|
|
29
|
+
for (const row of sceneLines() ?? aardvarkLines())
|
|
30
|
+
console.log(row);
|
|
31
|
+
// Wrapped before styling, and to the window rather than to a fixed number: the second
|
|
32
|
+
// line is long enough that an 80-column terminal is the tight case, not the roomy one.
|
|
33
|
+
const room = Math.max(28, (process.stdout.columns ?? 80) - 8);
|
|
34
|
+
box({
|
|
35
|
+
title: badge(`Welcome to ${BRAND}`),
|
|
36
|
+
lines: [
|
|
37
|
+
"",
|
|
38
|
+
...wrap(TAGLINE, room).map(style.bold),
|
|
39
|
+
...wrap(TAGLINE_SECONDARY, room).map(style.dim),
|
|
40
|
+
"",
|
|
41
|
+
],
|
|
42
|
+
// Keeps the box at least as wide as the aardvark above it, so the two read as one block.
|
|
43
|
+
minWidth: 36,
|
|
44
|
+
});
|
|
45
|
+
if (alreadyConnected) {
|
|
46
|
+
trunk();
|
|
47
|
+
step("This machine is already connected.");
|
|
48
|
+
nextSteps();
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
// No terminal to ask into: say what to run and get out of the way.
|
|
52
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
53
|
+
trunk();
|
|
54
|
+
step(`Log in to ${BRAND}:`);
|
|
55
|
+
line(style.bold("popover login"));
|
|
56
|
+
nextSteps();
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
trunk();
|
|
60
|
+
const answer = await ask(`Log in to ${BRAND} ${style.dim("(opens browser)")}`);
|
|
61
|
+
trunk();
|
|
62
|
+
if (answer === "skip") {
|
|
63
|
+
step("Skipped. Run this when you are ready:");
|
|
64
|
+
line(style.bold("popover login"));
|
|
65
|
+
nextSteps();
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
const code = await login(apiUrl, { framed: true });
|
|
69
|
+
if (code === 0)
|
|
70
|
+
nextSteps();
|
|
71
|
+
return code;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The one question in the flow.
|
|
75
|
+
*
|
|
76
|
+
* Enter is the answer — the prompt is phrased as the action rather than a yes/no, so a bare
|
|
77
|
+
* Enter means "do it". `s`, `n` and `q` skip, which covers "skip", "no" and "quit" without a
|
|
78
|
+
* menu. Ctrl-C, Escape and a closed stdin all resolve to a skip rather than throwing,
|
|
79
|
+
* because the installer is the parent process and a half-finished login is not a failure.
|
|
80
|
+
*
|
|
81
|
+
* Read raw rather than through readline, for two reasons. A single keypress is what "just
|
|
82
|
+
* hit enter" means — with readline, `s` would need an Enter after it. And readline owns the
|
|
83
|
+
* cursor on the line it prompted on: it repositions after the echoed newline, so the next
|
|
84
|
+
* thing printed lands on the prompt line instead of below it.
|
|
85
|
+
*/
|
|
86
|
+
function ask(question) {
|
|
87
|
+
step(style.bold(question));
|
|
88
|
+
// The one instruction on screen, so it is the one thing not dimmed: bright white against a
|
|
89
|
+
// flow that is otherwise deliberately quiet.
|
|
90
|
+
const hint = `${style.white("press enter")}${style.dim(" to continue")}` +
|
|
91
|
+
`${style.dim(` ${glyph.dot} s to skip`)}`;
|
|
92
|
+
process.stdout.write(`${style.dim(glyph.trunk)} ${hint} `);
|
|
93
|
+
const stdin = process.stdin;
|
|
94
|
+
const wasRaw = stdin.isRaw;
|
|
95
|
+
return new Promise((resolve) => {
|
|
96
|
+
let settled = false;
|
|
97
|
+
const done = (result) => {
|
|
98
|
+
if (settled)
|
|
99
|
+
return;
|
|
100
|
+
settled = true;
|
|
101
|
+
stdin.removeListener("data", onData);
|
|
102
|
+
stdin.removeListener("end", onEnd);
|
|
103
|
+
stdin.setRawMode?.(wasRaw ?? false);
|
|
104
|
+
stdin.pause();
|
|
105
|
+
// Raw mode swallowed the echo of whatever ended this, so close the line ourselves.
|
|
106
|
+
console.log("");
|
|
107
|
+
resolve(result);
|
|
108
|
+
};
|
|
109
|
+
const onData = (chunk) => {
|
|
110
|
+
for (const key of chunk) {
|
|
111
|
+
if (key === "\r" || key === "\n")
|
|
112
|
+
return done("go");
|
|
113
|
+
// Ctrl-C, Ctrl-D and Escape: in raw mode these arrive as bytes, not as signals.
|
|
114
|
+
if (key === "\x03" || key === "\x04" || key === "\x1b")
|
|
115
|
+
return done("skip");
|
|
116
|
+
if (/[nsq]/i.test(key))
|
|
117
|
+
return done("skip");
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const onEnd = () => done("skip");
|
|
121
|
+
stdin.setRawMode?.(true);
|
|
122
|
+
stdin.setEncoding("utf8");
|
|
123
|
+
stdin.resume();
|
|
124
|
+
stdin.on("data", onData);
|
|
125
|
+
stdin.on("end", onEnd);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=welcome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"welcome.js","sourceRoot":"","sources":["../src/welcome.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@popoverinstall/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "See your teammates' active Claude Code agents and ask them questions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -33,14 +33,15 @@
|
|
|
33
33
|
"dist",
|
|
34
34
|
"plugin",
|
|
35
35
|
".claude-plugin",
|
|
36
|
-
"README.md"
|
|
36
|
+
"README.md",
|
|
37
|
+
"CHANGELOG.md"
|
|
37
38
|
],
|
|
38
39
|
"scripts": {
|
|
39
40
|
"build": "tsc -b",
|
|
40
41
|
"prepack": "node ../../scripts/stage-plugin.mjs && tsc -b"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"@popoverinstall/daemon": "0.
|
|
44
|
-
"@popoverinstall/shared": "0.
|
|
44
|
+
"@popoverinstall/daemon": "0.6.0",
|
|
45
|
+
"@popoverinstall/shared": "0.6.0"
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "popover",
|
|
4
4
|
"displayName": "Popover",
|
|
5
5
|
"description": "See your teammates' active Claude Code agents and ask them questions. A read-only fork answers from the agent's full context without interrupting your teammate.",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.6.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Quolabs"
|
|
9
9
|
},
|
package/plugin/README.md
CHANGED
|
@@ -5,12 +5,22 @@ Installed into Claude Code; see the repo root README for the whole system.
|
|
|
5
5
|
```
|
|
6
6
|
.claude-plugin/plugin.json metadata only
|
|
7
7
|
commands/team.md the /team command
|
|
8
|
+
commands/fork.md the /fork command
|
|
8
9
|
hooks/hooks.json status feed (auto-discovered)
|
|
9
10
|
.mcp.json bundled MCP server (auto-discovered)
|
|
10
|
-
mcp/index.mjs team_list + team_ask, zero dependencies
|
|
11
|
+
mcp/index.mjs team_list + team_ask + team_tell, zero dependencies
|
|
11
12
|
scripts/ hook handlers, zero dependencies
|
|
12
13
|
```
|
|
13
14
|
|
|
15
|
+
`/team` reaches the daemon through the MCP server. `/fork` deliberately does not: it shells
|
|
16
|
+
out to the `popover` binary instead, because opening a fork has to work for someone who is
|
|
17
|
+
not signed in and may have no daemon running at all. That also means it adds nothing to the
|
|
18
|
+
IPC protocol, and cannot regress asks, tells, or the roster.
|
|
19
|
+
|
|
20
|
+
It is the one command that needs to know which session it is in, which it gets from
|
|
21
|
+
`${CLAUDE_SESSION_ID}` — substituted into the command body by Claude Code, so there is no
|
|
22
|
+
guessing at "the newest transcript in this directory".
|
|
23
|
+
|
|
14
24
|
## Three things that will bite you
|
|
15
25
|
|
|
16
26
|
**Do not declare `hooks` or `mcpServers` in `plugin.json`.** Both `hooks/hooks.json` and
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Share this conversation as a fork a teammate can carry on, or open one you were sent
|
|
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: "[create|open] [code]"
|
|
6
|
+
allowed-tools: Bash(popover fork *), AskUserQuestion
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Fork this conversation
|
|
10
|
+
|
|
11
|
+
The user ran `/popover:fork`. Arguments, which may be empty: **$ARGUMENTS**
|
|
12
|
+
|
|
13
|
+
This session's id is `${CLAUDE_SESSION_ID}`. You will need it for `create`.
|
|
14
|
+
|
|
15
|
+
A fork is not an ask. An ask sends a teammate's question to a read-only copy of someone's
|
|
16
|
+
session and brings back one answer. A fork hands over **the whole conversation**, frozen at
|
|
17
|
+
the moment it was made, and the person who opens it gets their own local chat that continues
|
|
18
|
+
from there. Nothing they do in it comes back here.
|
|
19
|
+
|
|
20
|
+
The first word of the arguments chooses what to do. Follow exactly one of these paths.
|
|
21
|
+
|
|
22
|
+
## 1. `create` — freeze this conversation and get a code
|
|
23
|
+
|
|
24
|
+
Run:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
popover fork create --session ${CLAUDE_SESSION_ID} --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
On `ok: true`, show the user the code on its own line so it is easy to copy, then tell them,
|
|
31
|
+
briefly and in this order:
|
|
32
|
+
|
|
33
|
+
- Who it is for and how they open it: `popover fork open <code>`, or
|
|
34
|
+
`npx @popoverinstall/cli fork open <code>` if they do not have popover yet.
|
|
35
|
+
- That the code stops working in 24 hours, but a chat someone has already opened does not —
|
|
36
|
+
it is theirs from then on.
|
|
37
|
+
- That it is frozen at `last_turn_at`, so nothing said after this point is included. If they
|
|
38
|
+
keep working and want the newer state shared, they run `create` again for a new code.
|
|
39
|
+
- That this sends the entire conversation, including the contents of every file read into
|
|
40
|
+
it. Say it plainly, once. It is a much larger disclosure than an ask and the user should
|
|
41
|
+
make it deliberately.
|
|
42
|
+
|
|
43
|
+
On `ok: false`, relay `message` and the `hints` verbatim. Do not retry with different flags.
|
|
44
|
+
|
|
45
|
+
## 2. `open <code>` — take delivery of a fork
|
|
46
|
+
|
|
47
|
+
Run:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
popover fork open <the code they typed> --json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
On `ok: false`, relay `message` and `hints` and stop.
|
|
54
|
+
|
|
55
|
+
On `ok: true`, tell them what arrived: who shared it, the title if there is one,
|
|
56
|
+
`entry_count` messages, when it was frozen, and which repo it came from. Then surface
|
|
57
|
+
whatever the output flags:
|
|
58
|
+
|
|
59
|
+
- `repo_match: "different"` — say which repo it came from and which one they are in, and
|
|
60
|
+
that the conversation will refer to files they may not have.
|
|
61
|
+
- `repo_match: "unknown"` — they are not in a git repository, so paths in it may not resolve.
|
|
62
|
+
- `version_skew` — relay it as a note, not a blocker.
|
|
63
|
+
- `claude_problem` — this one *is* a blocker. Relay it and stop.
|
|
64
|
+
|
|
65
|
+
Then explain why this needs a window of its own, and offer the choice.
|
|
66
|
+
|
|
67
|
+
**A fork cannot open inside this chat.** A running Claude Code session cannot become a
|
|
68
|
+
different session — one chat holds one conversation, and this one already has its own. So
|
|
69
|
+
the fork starts as a second session in a new window. If this conversation has real context
|
|
70
|
+
in it already, say that plainly; if the user only just started here, one short line is
|
|
71
|
+
enough.
|
|
72
|
+
|
|
73
|
+
Then call `AskUserQuestion` with exactly these two options, so Esc cancels:
|
|
74
|
+
|
|
75
|
+
- **Open a new window** — "Start the fork in its own terminal window"
|
|
76
|
+
- **Print the command** — "Show the command and let me run it myself"
|
|
77
|
+
|
|
78
|
+
If they choose to open a new window, run:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
popover fork launch <stage_id> --json
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then report what happened: `opened: true` means a window is on its way, and it is worth
|
|
85
|
+
adding that the fork may take a moment to appear. `opened: false` is not a failure — it
|
|
86
|
+
happens over SSH, in a container, and anywhere there is no window to open — so show them
|
|
87
|
+
`command` and the `cwd` to run it in.
|
|
88
|
+
|
|
89
|
+
If they choose to print the command, run the same thing with `--here` instead of no flag,
|
|
90
|
+
and show them the `command` it prints. `--here` writes the fork without trying to open
|
|
91
|
+
anything.
|
|
92
|
+
|
|
93
|
+
If they press Esc, stop. Nothing has been written to their machine yet, and there is nothing
|
|
94
|
+
to clean up.
|
|
95
|
+
|
|
96
|
+
## 3. No arguments, or anything else
|
|
97
|
+
|
|
98
|
+
Say what the two halves do in two lines, and stop:
|
|
99
|
+
|
|
100
|
+
`/popover:fork create` · `/popover:fork open PQRS-2345-BCDF-7892`
|
|
101
|
+
|
|
102
|
+
Do not create a fork on the user's behalf just because they ran the command with no
|
|
103
|
+
arguments. Sharing a conversation is a disclosure, and it should always be something they
|
|
104
|
+
asked for in as many words.
|
|
105
|
+
|
|
106
|
+
## Rules
|
|
107
|
+
|
|
108
|
+
- Never invent or guess at a code. If what they typed is rejected, relay that and let them
|
|
109
|
+
paste it again — a code is 16 characters in four groups and is easy to truncate.
|
|
110
|
+
- Never print a code the user has not seen; it is the entire credential and the decryption
|
|
111
|
+
key for the conversation behind it.
|
|
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.
|
|
114
|
+
- Do not run `create` and then immediately `open` the result to "test" it. Every open counts
|
|
115
|
+
against what the sharer sees, and the fork is already verified by the command exiting
|
|
116
|
+
cleanly.
|
|
117
|
+
- `popover fork revoke <code>` destroys a snapshot early. It cannot recall a copy someone
|
|
118
|
+
has already opened; if the user is worried about that, say so rather than reassuring them.
|
package/plugin/commands/team.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: See your team's active Claude Code agents in this repo
|
|
2
|
+
description: See your team's active Claude Code agents in this repo, ask one a question, or tell one something
|
|
3
3
|
# Quoted because an unquoted value starting with `[` is parsed as a YAML flow sequence,
|
|
4
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
|
|
5
|
+
argument-hint: "[ask|tell] [agent-handle] [question or message]"
|
|
6
|
+
allowed-tools: mcp__plugin_popover_popover__team_list, mcp__plugin_popover_popover__team_ask, mcp__plugin_popover_popover__team_tell, AskUserQuestion
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Team agents
|
|
@@ -11,25 +11,24 @@ allowed-tools: mcp__plugin_popover_popover__team_list, mcp__plugin_popover_popov
|
|
|
11
11
|
The user ran `/popover:team`. Arguments, which may be empty: **$ARGUMENTS**
|
|
12
12
|
|
|
13
13
|
Only agents working in **this repo** are visible. A teammate running Claude Code in a
|
|
14
|
-
different repository is neither listed nor
|
|
14
|
+
different repository is neither listed nor reachable, so an empty roster means nobody else is
|
|
15
15
|
working *here* — not that the team is idle. Say it that way if it comes up.
|
|
16
16
|
|
|
17
|
-
Follow exactly one of these
|
|
17
|
+
The first word of the arguments chooses what to do. Follow exactly one of these paths.
|
|
18
18
|
|
|
19
19
|
## 1. No arguments — show the roster
|
|
20
20
|
|
|
21
21
|
Call `mcp__plugin_popover_popover__team_list`, then print what it returns **verbatim** in a
|
|
22
22
|
fenced block. Do not reformat it, re-sort it, or add agents that are not in it.
|
|
23
23
|
|
|
24
|
-
Then add one short line
|
|
25
|
-
`/popover:team B1 why did you rule out redis?`
|
|
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
26
|
|
|
27
|
-
Stop there. Do not ask
|
|
27
|
+
Stop there. Do not ask or tell anything on the user's behalf.
|
|
28
28
|
|
|
29
|
-
## 2.
|
|
29
|
+
## 2. `ask <handle> <question>` — ask it and wait
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
by a question, call `mcp__plugin_popover_popover__team_ask` with:
|
|
31
|
+
Call `mcp__plugin_popover_popover__team_ask` with:
|
|
33
32
|
|
|
34
33
|
- `target`: the handle or name they used, exactly as typed
|
|
35
34
|
- `question`: the rest of their message, as a self-contained question — the answering
|
|
@@ -43,21 +42,46 @@ When the answer comes back, show it clearly attributed:
|
|
|
43
42
|
Then say how it bears on what you are working on, if it does. If the tool returns an
|
|
44
43
|
error, relay it plainly — do not retry a different agent unless the user asks.
|
|
45
44
|
|
|
46
|
-
## 3.
|
|
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.
|
|
56
|
+
|
|
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.
|
|
61
|
+
|
|
62
|
+
## 4. A question or message with no clear target — pick one
|
|
47
63
|
|
|
48
64
|
Call `mcp__plugin_popover_popover__team_list` first. Then:
|
|
49
65
|
|
|
50
|
-
- If exactly one agent plausibly fits,
|
|
66
|
+
- If exactly one agent plausibly fits, use it and say which one you chose and why.
|
|
51
67
|
- If several fit, call `AskUserQuestion` with up to 4 of them as options (label = the
|
|
52
|
-
handle, description = owner, repo, and what it is doing). Then
|
|
68
|
+
handle, description = owner, repo, and what it is doing). Then use the chosen one.
|
|
53
69
|
- If none fit, say so and show the roster instead.
|
|
54
70
|
|
|
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.
|
|
74
|
+
|
|
55
75
|
## Rules
|
|
56
76
|
|
|
57
77
|
- Never invent a handle. Only use ones `team_list` returned. A handle from earlier in this
|
|
58
|
-
conversation may since have gone out of scope; if
|
|
59
|
-
|
|
60
|
-
- Asking costs the *teammate* money and runs on *their* machine.
|
|
61
|
-
request; do not fan out to several agents
|
|
62
|
-
|
|
63
|
-
|
|
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.
|
package/plugin/hooks/hooks.json
CHANGED
package/plugin/mcp/index.mjs
CHANGED
|
@@ -23,6 +23,7 @@ const SERVER_INFO = {
|
|
|
23
23
|
).version,
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
|
|
26
27
|
const TOOLS = [
|
|
27
28
|
{
|
|
28
29
|
name: "team_list",
|
|
@@ -73,6 +74,40 @@ const TOOLS = [
|
|
|
73
74
|
},
|
|
74
75
|
annotations: { readOnlyHint: true, openWorldHint: true, title: "Ask a teammate's agent" },
|
|
75
76
|
},
|
|
77
|
+
{
|
|
78
|
+
name: "team_tell",
|
|
79
|
+
description:
|
|
80
|
+
"Send a short heads-up to a teammate's agent working in this repo. Unlike team_ask " +
|
|
81
|
+
"there is no answer: the message is delivered into that agent's context before its " +
|
|
82
|
+
"next prompt, and nothing comes back. Use it for facts about shared state that would " +
|
|
83
|
+
"otherwise cause a collision — \"the migration is applied on prod\", \"I am rewriting " +
|
|
84
|
+
"globals.css, leave it alone\". Do not use it to give another agent instructions or " +
|
|
85
|
+
"assign it work: the receiving agent is told to treat your message as information " +
|
|
86
|
+
"from outside its conversation, not as a directive, and its user is notified that you " +
|
|
87
|
+
"sent it. Only agents in the same repository can be told, and you are limited to 10 " +
|
|
88
|
+
"messages an hour to any one agent.",
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
target: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description:
|
|
95
|
+
"Which agent to tell: a handle from team_list such as 'B1', or a teammate's " +
|
|
96
|
+
"name, or a repo name.",
|
|
97
|
+
},
|
|
98
|
+
message: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description:
|
|
101
|
+
"The heads-up, in one or two sentences. It arrives with no context of its own, " +
|
|
102
|
+
"so name the thing you are talking about rather than referring to it.",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
required: ["target", "message"],
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
},
|
|
108
|
+
// Not read-only: this writes into someone else's context and notifies a person.
|
|
109
|
+
annotations: { readOnlyHint: false, openWorldHint: true, title: "Tell a teammate's agent" },
|
|
110
|
+
},
|
|
76
111
|
];
|
|
77
112
|
|
|
78
113
|
// ---------------------------------------------------------------------------
|
|
@@ -157,6 +192,39 @@ async function callTeamAsk(args) {
|
|
|
157
192
|
return text(`Answer from ${attribution}:\n\n${answer.answer}${cost}`);
|
|
158
193
|
}
|
|
159
194
|
|
|
195
|
+
async function callTeamTell(args) {
|
|
196
|
+
const target = typeof args?.target === "string" ? args.target.trim() : "";
|
|
197
|
+
const message = typeof args?.message === "string" ? args.message.trim() : "";
|
|
198
|
+
|
|
199
|
+
if (!target) return errorText("`target` is required — call team_list to see the options.");
|
|
200
|
+
if (!message) return errorText("`message` is required.");
|
|
201
|
+
|
|
202
|
+
const reply = await request(
|
|
203
|
+
{
|
|
204
|
+
t: "tell",
|
|
205
|
+
id: rpcId(),
|
|
206
|
+
req: {
|
|
207
|
+
target,
|
|
208
|
+
message,
|
|
209
|
+
...(callerSessionId() ? { fromSessionId: callerSessionId() } : {}),
|
|
210
|
+
},
|
|
211
|
+
cwd: process.cwd(),
|
|
212
|
+
},
|
|
213
|
+
{ timeoutMs: 15000 },
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
if (!reply) return errorText(daemonDownMessage());
|
|
217
|
+
if (reply.t === "error") return errorText(explain(reply));
|
|
218
|
+
if (reply.t !== "tell.ok") return errorText("The popover daemon returned an unexpected response.");
|
|
219
|
+
|
|
220
|
+
// Says delivered-to, never read-by: a sender is deliberately told nothing about what
|
|
221
|
+
// happened to their message afterwards.
|
|
222
|
+
return text(
|
|
223
|
+
`Sent to ${reply.handle} (${reply.ownerName}). It will reach that agent before its next ` +
|
|
224
|
+
`prompt; there is no reply.`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
160
228
|
function explain(errorReply) {
|
|
161
229
|
switch (errorReply.code) {
|
|
162
230
|
// The daemon knows whether credentials are missing or merely unusable; relaying its
|
|
@@ -247,6 +315,7 @@ async function handle(message) {
|
|
|
247
315
|
try {
|
|
248
316
|
if (name === "team_list") return reply(id, await callTeamList());
|
|
249
317
|
if (name === "team_ask") return reply(id, await callTeamAsk(params?.arguments ?? {}));
|
|
318
|
+
if (name === "team_tell") return reply(id, await callTeamTell(params?.arguments ?? {}));
|
|
250
319
|
return replyError(id, -32602, `Unknown tool: ${name}`);
|
|
251
320
|
} catch (err) {
|
|
252
321
|
// A thrown handler must not kill the server; report it as a failed tool call.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Delivers tells into a live session.
|
|
3
|
+
//
|
|
4
|
+
// This is the one hook in the plugin that writes to stdout on purpose. Every other one is
|
|
5
|
+
// registered `async: true` and forbidden from printing, because for UserPromptSubmit stdout
|
|
6
|
+
// is injected into the model's context — see the header of emit-event.mjs. That is precisely
|
|
7
|
+
// the channel a tell needs, so this hook is synchronous and its output is the message.
|
|
8
|
+
//
|
|
9
|
+
// Which makes it the riskiest script here, and it is written accordingly:
|
|
10
|
+
//
|
|
11
|
+
// - It runs before every prompt the user submits, so it must be fast and must fail open.
|
|
12
|
+
// A missed heads-up is survivable; a prompt that hangs waiting on a dead daemon is not.
|
|
13
|
+
// - It prints only what the daemon hands back. The envelope is rendered on this machine
|
|
14
|
+
// from columns the database filled in, so a sender can neither forge the attribution nor
|
|
15
|
+
// strip the framing off their own message.
|
|
16
|
+
// - It stays silent inside a fork. Forks run the plugin's hooks like any other session, and
|
|
17
|
+
// a fork answering a teammate's question must never be handed a third party's text — its
|
|
18
|
+
// output goes back to whoever asked.
|
|
19
|
+
|
|
20
|
+
import { callerSessionId, request } from "./_ipc.mjs";
|
|
21
|
+
|
|
22
|
+
// The fork runner sets this when it spawns a read-only answer session.
|
|
23
|
+
if (process.env.CLAUDE_CODE_ENTRYPOINT === "popover-fork") process.exit(0);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// The payload carries session_id; the environment is the fallback, and one of the two is
|
|
27
|
+
// always present in a real session. Without a session there is no inbox to drain.
|
|
28
|
+
let sessionId = callerSessionId();
|
|
29
|
+
try {
|
|
30
|
+
const raw = await readStdinQuickly();
|
|
31
|
+
if (raw) sessionId = JSON.parse(raw)?.session_id || sessionId;
|
|
32
|
+
} catch {
|
|
33
|
+
// A malformed payload is not a reason to skip delivery; the env var still identifies us.
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (sessionId) {
|
|
37
|
+
const reply = await request(
|
|
38
|
+
{ t: "pending", id: "hook", fromSessionId: sessionId, limit: 3 },
|
|
39
|
+
{ timeoutMs: 300 },
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (reply?.t === "pending.ok" && reply.rendered) {
|
|
43
|
+
// The whole point of the script. Everything above exists to make this line safe.
|
|
44
|
+
process.stdout.write(`${reply.rendered}\n`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Fail open, always. Whatever went wrong, the user's prompt must still go through.
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
process.exit(0);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Read the hook payload, but never wait long for it.
|
|
55
|
+
*
|
|
56
|
+
* `readStdin` in _ipc.mjs allows two seconds, which is right for a fire-and-forget reporter
|
|
57
|
+
* and far too long for something sitting between a keystroke and the model.
|
|
58
|
+
*/
|
|
59
|
+
function readStdinQuickly(timeoutMs = 150) {
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
let data = "";
|
|
62
|
+
const timer = setTimeout(() => resolve(data), timeoutMs);
|
|
63
|
+
process.stdin.setEncoding("utf8");
|
|
64
|
+
process.stdin.on("data", (chunk) => {
|
|
65
|
+
data += chunk;
|
|
66
|
+
});
|
|
67
|
+
process.stdin.on("end", () => {
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
resolve(data);
|
|
70
|
+
});
|
|
71
|
+
process.stdin.on("error", () => {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
resolve(data);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|