@popoverinstall/cli 0.8.1 → 0.9.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 +147 -78
- package/LICENSE +21 -21
- package/README.md +142 -141
- package/dist/config-command.d.ts +2 -0
- package/dist/config-command.d.ts.map +1 -0
- package/dist/config-command.js +80 -0
- package/dist/config-command.js.map +1 -0
- package/dist/cursor-hooks.d.ts +18 -0
- package/dist/cursor-hooks.d.ts.map +1 -0
- package/dist/cursor-hooks.js +105 -0
- package/dist/cursor-hooks.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -25
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +10 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +44 -0
- package/dist/keys.js.map +1 -0
- package/dist/repo-scan.d.ts +130 -0
- package/dist/repo-scan.d.ts.map +1 -0
- package/dist/repo-scan.js +281 -0
- package/dist/repo-scan.js.map +1 -0
- package/dist/repos.d.ts +180 -0
- package/dist/repos.d.ts.map +1 -0
- package/dist/repos.js +1002 -0
- package/dist/repos.js.map +1 -0
- package/dist/snapshot.d.ts +35 -0
- package/dist/snapshot.d.ts.map +1 -1
- package/dist/snapshot.js +16 -16
- package/dist/snapshot.js.map +1 -1
- package/dist/vaults.d.ts +276 -0
- package/dist/vaults.d.ts.map +1 -0
- package/dist/vaults.js +1224 -0
- package/dist/vaults.js.map +1 -0
- package/package.json +47 -47
- package/plugin/.claude-plugin/plugin.json +19 -19
- package/plugin/.mcp.json +9 -9
- package/plugin/README.md +84 -77
- package/plugin/commands/ask.md +65 -65
- package/plugin/commands/fork.md +119 -119
- package/plugin/commands/repos.md +107 -0
- package/plugin/commands/team.md +60 -60
- package/plugin/commands/tell.md +66 -66
- package/plugin/commands/vault.md +173 -0
- package/plugin/hooks/hooks.json +111 -111
- package/plugin/mcp/index.mjs +585 -355
- package/plugin/scripts/_ipc.mjs +146 -146
- package/plugin/scripts/announce-roster.mjs +141 -141
- package/plugin/scripts/deliver-messages.mjs +77 -77
- package/plugin/scripts/emit-event.mjs +44 -44
- package/plugin/scripts/ensure-daemon.mjs +156 -156
- package/plugin/scripts/roster.mjs +52 -52
- package/plugin/skills/popover/SKILL.md +175 -168
package/README.md
CHANGED
|
@@ -1,141 +1,142 @@
|
|
|
1
|
-
# popover
|
|
2
|
-
|
|
3
|
-
**Ask your teammates' agents, not your teammates.**
|
|
4
|
-
|
|
5
|
-
When a teammate's agent has already worked out why the retry logic lives where it does, you
|
|
6
|
-
should be able to ask it — without interrupting your teammate. popover shows you everyone's
|
|
7
|
-
running Claude Code agents in the repo you are working in, and answers from a read-only fork
|
|
8
|
-
of the agent's full accumulated context.
|
|
9
|
-
|
|
10
|
-
Full documentation: **[popover.to/docs](https://www.popover.to/docs)**.
|
|
11
|
-
|
|
12
|
-
## Install
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install -g @popoverinstall/cli && popover welcome
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Requires **Node.js 20+** and **Claude Code 2.1.229+**. The same two commands on macOS, Linux
|
|
19
|
-
and Windows.
|
|
20
|
-
|
|
21
|
-
`npm install -g` only places files. `popover welcome` is the rest of a first run: it registers
|
|
22
|
-
the Claude Code plugin, records where the daemon lives, and offers to connect this machine. It
|
|
23
|
-
prints a short code to confirm in your browser; the device token it stores is generated locally
|
|
24
|
-
and never transmitted.
|
|
25
|
-
|
|
26
|
-
Both halves are available on their own — `popover setup` for the wiring, `popover login` for the
|
|
27
|
-
connection — and `welcome` is re-runnable, so it is also the way back if either needs redoing.
|
|
28
|
-
|
|
29
|
-
## Use
|
|
30
|
-
|
|
31
|
-
Inside Claude Code, `/popover:team` prints the roster: every agent your team is running in the
|
|
32
|
-
repo you are in, and what each one is doing right now. Each gets a short handle.
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
*You*
|
|
36
|
-
A1 ad pipeline idle 34m — Wiring Kling into the ad pipeline
|
|
37
|
-
A2 popover editing main.py — Team agent communication
|
|
38
|
-
|
|
39
|
-
*Bob Chen*
|
|
40
|
-
B1 api-server reading config.json — Fixing OAuth token refresh
|
|
41
|
-
B2 api-server creating plan.md — Postgres connection pooling
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Three things to do with one:
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
/popover:ask B1 why did you rule out redis?
|
|
48
|
-
/popover:tell B1 the migration is applied on prod
|
|
49
|
-
/popover:fork create
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
**Ask** is answered by a read-only fork of that agent, running on its owner's machine. Their
|
|
53
|
-
session is never interrupted and their source never leaves their laptop — only the answer
|
|
54
|
-
does. The fork has no tools, so a question about something the agent never looked at gets
|
|
55
|
-
"I don't know" rather than an investigation, and the answer is billed to their account.
|
|
56
|
-
|
|
57
|
-
**Tell** drops a heads-up into a running agent's context before its next prompt. No answer
|
|
58
|
-
comes back. It is for facts that would otherwise cause a collision, and it is the one thing
|
|
59
|
-
here that reaches a live session: the database caps a sender at ten messages an hour to any
|
|
60
|
-
one recipient, and the recipient's human is always notified.
|
|
61
|
-
|
|
62
|
-
**Fork** hands over the whole conversation. `/popover:fork create` returns a code that works
|
|
63
|
-
for 24 hours; whoever you send it to runs `popover fork open <code>` and carries on in their
|
|
64
|
-
own local chat. Opening one needs no popover account — `npx @popoverinstall/cli fork open
|
|
65
|
-
<code>` is enough.
|
|
66
|
-
|
|
67
|
-
Claude Code namespaces plugin commands, so it is `/popover:team`, not `/team`.
|
|
68
|
-
|
|
69
|
-
## Who can see what
|
|
70
|
-
|
|
71
|
-
Agents are only visible to each other **inside the same repo**, so the directory you run from
|
|
72
|
-
is what scopes the roster. Repos are identified by their git remotes, so two clones match
|
|
73
|
-
however they were cloned; a directory with no shared remote is private to you. The boundary is
|
|
74
|
-
enforced by row-level security in Postgres, not by the client.
|
|
75
|
-
|
|
76
|
-
## Commands
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
popover welcome Set up and log in — the whole of a first run
|
|
80
|
-
popover login / logout Connect this machine, or forget its credentials
|
|
81
|
-
popover setup Re-register the Claude Code plugin
|
|
82
|
-
popover status This machine's sessions and connection
|
|
83
|
-
popover team Print the roster for the repo you are in
|
|
84
|
-
popover
|
|
85
|
-
popover
|
|
86
|
-
popover
|
|
87
|
-
popover
|
|
88
|
-
popover
|
|
89
|
-
popover
|
|
90
|
-
popover
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
- **the
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
- [
|
|
139
|
-
- [
|
|
140
|
-
|
|
141
|
-
|
|
1
|
+
# popover
|
|
2
|
+
|
|
3
|
+
**Ask your teammates' agents, not your teammates.**
|
|
4
|
+
|
|
5
|
+
When a teammate's agent has already worked out why the retry logic lives where it does, you
|
|
6
|
+
should be able to ask it — without interrupting your teammate. popover shows you everyone's
|
|
7
|
+
running Claude Code agents in the repo you are working in, and answers from a read-only fork
|
|
8
|
+
of the agent's full accumulated context.
|
|
9
|
+
|
|
10
|
+
Full documentation: **[popover.to/docs](https://www.popover.to/docs)**.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @popoverinstall/cli && popover welcome
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires **Node.js 20+** and **Claude Code 2.1.229+**. The same two commands on macOS, Linux
|
|
19
|
+
and Windows.
|
|
20
|
+
|
|
21
|
+
`npm install -g` only places files. `popover welcome` is the rest of a first run: it registers
|
|
22
|
+
the Claude Code plugin, records where the daemon lives, and offers to connect this machine. It
|
|
23
|
+
prints a short code to confirm in your browser; the device token it stores is generated locally
|
|
24
|
+
and never transmitted.
|
|
25
|
+
|
|
26
|
+
Both halves are available on their own — `popover setup` for the wiring, `popover login` for the
|
|
27
|
+
connection — and `welcome` is re-runnable, so it is also the way back if either needs redoing.
|
|
28
|
+
|
|
29
|
+
## Use
|
|
30
|
+
|
|
31
|
+
Inside Claude Code, `/popover:team` prints the roster: every agent your team is running in the
|
|
32
|
+
repo you are in, and what each one is doing right now. Each gets a short handle.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
*You*
|
|
36
|
+
A1 ad pipeline idle 34m — Wiring Kling into the ad pipeline
|
|
37
|
+
A2 popover editing main.py — Team agent communication
|
|
38
|
+
|
|
39
|
+
*Bob Chen*
|
|
40
|
+
B1 api-server reading config.json — Fixing OAuth token refresh
|
|
41
|
+
B2 api-server creating plan.md — Postgres connection pooling
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Three things to do with one:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
/popover:ask B1 why did you rule out redis?
|
|
48
|
+
/popover:tell B1 the migration is applied on prod
|
|
49
|
+
/popover:fork create
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Ask** is answered by a read-only fork of that agent, running on its owner's machine. Their
|
|
53
|
+
session is never interrupted and their source never leaves their laptop — only the answer
|
|
54
|
+
does. The fork has no tools, so a question about something the agent never looked at gets
|
|
55
|
+
"I don't know" rather than an investigation, and the answer is billed to their account.
|
|
56
|
+
|
|
57
|
+
**Tell** drops a heads-up into a running agent's context before its next prompt. No answer
|
|
58
|
+
comes back. It is for facts that would otherwise cause a collision, and it is the one thing
|
|
59
|
+
here that reaches a live session: the database caps a sender at ten messages an hour to any
|
|
60
|
+
one recipient, and the recipient's human is always notified.
|
|
61
|
+
|
|
62
|
+
**Fork** hands over the whole conversation. `/popover:fork create` returns a code that works
|
|
63
|
+
for 24 hours; whoever you send it to runs `popover fork open <code>` and carries on in their
|
|
64
|
+
own local chat. Opening one needs no popover account — `npx @popoverinstall/cli fork open
|
|
65
|
+
<code>` is enough.
|
|
66
|
+
|
|
67
|
+
Claude Code namespaces plugin commands, so it is `/popover:team`, not `/team`.
|
|
68
|
+
|
|
69
|
+
## Who can see what
|
|
70
|
+
|
|
71
|
+
Agents are only visible to each other **inside the same repo**, so the directory you run from
|
|
72
|
+
is what scopes the roster. Repos are identified by their git remotes, so two clones match
|
|
73
|
+
however they were cloned; a directory with no shared remote is private to you. The boundary is
|
|
74
|
+
enforced by row-level security in Postgres, not by the client.
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
popover welcome Set up and log in — the whole of a first run
|
|
80
|
+
popover login / logout Connect this machine, or forget its credentials
|
|
81
|
+
popover setup Re-register the Claude Code plugin
|
|
82
|
+
popover status This machine's sessions and connection
|
|
83
|
+
popover team Print the roster for the repo you are in
|
|
84
|
+
popover rename <who> [name] Name one of your own agents, or clear its name
|
|
85
|
+
popover fork <create|open|launch|list|revoke>
|
|
86
|
+
popover log Questions and messages your agents received
|
|
87
|
+
popover changelog [--all] What the last update changed
|
|
88
|
+
popover update Upgrade and restart the daemon
|
|
89
|
+
popover doctor Diagnose setup problems
|
|
90
|
+
popover daemon <start|stop|restart|status|logs>
|
|
91
|
+
popover uninstall Stop the daemon, unhook Claude Code
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Asking and telling are not in that list on purpose: they live in the session, where the agent
|
|
95
|
+
doing them has the context to write a question worth answering and to do something with the
|
|
96
|
+
answer.
|
|
97
|
+
|
|
98
|
+
## How it works
|
|
99
|
+
|
|
100
|
+
Three pieces install together:
|
|
101
|
+
|
|
102
|
+
- **the CLI** — the `popover` command
|
|
103
|
+
- **the daemon** — a detached background process that publishes this machine's sessions and
|
|
104
|
+
answers teammates' questions. It stores state in `~/.popover/`.
|
|
105
|
+
- **the Claude Code plugin** — provides `/popover:team`, `/popover:fork`, and the MCP tools
|
|
106
|
+
agents use to reach each other. Registered by `popover setup`.
|
|
107
|
+
|
|
108
|
+
## What leaves your machine
|
|
109
|
+
|
|
110
|
+
- An **ask** exports one answer. A teammate can learn whatever is already in that agent's
|
|
111
|
+
context, including the contents of files it read earlier in the session — that is what it
|
|
112
|
+
answers from. Every ask is logged with its cost.
|
|
113
|
+
- A **fork** exports the entire conversation: every message, every tool result, the contents
|
|
114
|
+
of every file read into it. It is sealed before it leaves, with a key derived from the code,
|
|
115
|
+
so the server holds something it cannot read — but anyone holding the code can open it.
|
|
116
|
+
Treat it the way you would treat pasting the transcript into a chat window.
|
|
117
|
+
- The device token is generated here and never transmitted; the server only ever sees its
|
|
118
|
+
SHA-256.
|
|
119
|
+
|
|
120
|
+
## Troubleshooting
|
|
121
|
+
|
|
122
|
+
`popover doctor` diagnoses setup problems in root-cause order and is the right first move for
|
|
123
|
+
most things. `popover status` says whether this machine is connected; `popover daemon logs` is
|
|
124
|
+
the detail underneath. A teammate missing from the roster is usually running a version too old
|
|
125
|
+
to publish a repo identity — the roster counts them and says so.
|
|
126
|
+
|
|
127
|
+
## Uninstall
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
popover uninstall # stop the daemon, unhook Claude Code
|
|
131
|
+
npm uninstall -g @popoverinstall/cli
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Credentials and logs are left in `~/.popover/`; delete the directory to remove those too.
|
|
135
|
+
|
|
136
|
+
## Links
|
|
137
|
+
|
|
138
|
+
- [Documentation](https://www.popover.to/docs)
|
|
139
|
+
- [Changelog](https://www.popover.to/changelog)
|
|
140
|
+
- [popover.to](https://www.popover.to) — dashboard, team and activity
|
|
141
|
+
|
|
142
|
+
MIT · Quo Labs, Inc.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-command.d.ts","sourceRoot":"","sources":["../src/config-command.ts"],"names":[],"mappings":"AAmDA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAqDpD"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { DEFAULT_CONFIG, WorkspaceScanSchema, configPath, loadConfig, saveConfig, } from "@popoverinstall/shared";
|
|
2
|
+
import { bad, c, dim, info, ok } from "./ui.js";
|
|
3
|
+
const SETTINGS = [
|
|
4
|
+
{
|
|
5
|
+
name: "workspace-scan",
|
|
6
|
+
key: "workspaceScan",
|
|
7
|
+
describe: "Whether a directory that is not itself a repo, but contains repos, publishes them",
|
|
8
|
+
values: WorkspaceScanSchema.options,
|
|
9
|
+
validate: (raw) => WorkspaceScanSchema.safeParse(raw).success,
|
|
10
|
+
effect: (value) => value === "children"
|
|
11
|
+
? "An agent in a folder above several repos is visible to teammates in each of them."
|
|
12
|
+
: "An agent in a folder that is not itself a repo is private to you again.",
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
export function configCommand(args) {
|
|
16
|
+
const [name, value] = args;
|
|
17
|
+
if (!name || name === "--help" || name === "-h" || name === "help") {
|
|
18
|
+
return list();
|
|
19
|
+
}
|
|
20
|
+
const setting = SETTINGS.find((s) => s.name === name);
|
|
21
|
+
if (!setting) {
|
|
22
|
+
bad(`No setting called \`${name}\`.`);
|
|
23
|
+
dim(`Known settings: ${SETTINGS.map((s) => s.name).join(", ")}`);
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
// No value: report this one rather than assuming the user meant to clear it. A settings
|
|
27
|
+
// command where `config foo` unsets `foo` is a settings command that loses data to a
|
|
28
|
+
// typo.
|
|
29
|
+
if (value === undefined) {
|
|
30
|
+
const current = loadConfig()[setting.key];
|
|
31
|
+
info(`${c.bold(setting.name)} ${current}`);
|
|
32
|
+
dim(`${setting.describe}.`);
|
|
33
|
+
dim(`Values: ${setting.values.join(", ")} (default ${DEFAULT_CONFIG[setting.key]})`);
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
if (!setting.validate(value)) {
|
|
37
|
+
bad(`\`${value}\` is not a valid ${setting.name}.`);
|
|
38
|
+
dim(`Values: ${setting.values.join(", ")}`);
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
const before = loadConfig()[setting.key];
|
|
42
|
+
if (before === value) {
|
|
43
|
+
// Said plainly rather than reported as a change, because "it was already that" and "I
|
|
44
|
+
// just changed it" should not read identically when someone is checking their work.
|
|
45
|
+
ok(`${setting.name} is already ${value}.`);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
saveConfig({ [setting.key]: value });
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
bad(`Could not write ${configPath()}.`);
|
|
53
|
+
dim(err instanceof Error ? err.message : String(err));
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
ok(`${setting.name}: ${before} → ${value}`);
|
|
57
|
+
dim(setting.effect(value));
|
|
58
|
+
// The daemon re-reads this file rather than caching it, which is what makes the promise
|
|
59
|
+
// below true. See `loadConfig` for why it is deliberately not memoized.
|
|
60
|
+
dim("The daemon picks this up within a minute; no restart needed.");
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
function list() {
|
|
64
|
+
const current = loadConfig();
|
|
65
|
+
console.log(`
|
|
66
|
+
${c.bold("popover config")} — settings for this machine
|
|
67
|
+
|
|
68
|
+
${c.dim(configPath())}
|
|
69
|
+
`);
|
|
70
|
+
for (const setting of SETTINGS) {
|
|
71
|
+
info(`${c.bold(setting.name.padEnd(16))} ${current[setting.key]}`);
|
|
72
|
+
dim(`${setting.describe}.`);
|
|
73
|
+
dim(`Values: ${setting.values.join(", ")} (default ${DEFAULT_CONFIG[setting.key]})`);
|
|
74
|
+
console.log();
|
|
75
|
+
}
|
|
76
|
+
dim("popover config <setting> show one");
|
|
77
|
+
dim("popover config <setting> <value> change it");
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=config-command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-command.js","sourceRoot":"","sources":["../src/config-command.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,UAAU,EACV,UAAU,EACV,UAAU,GACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AA6BhD,MAAM,QAAQ,GAAuB;IACnC;QACE,IAAI,EAAE,gBAAgB;QACtB,GAAG,EAAE,eAAe;QACpB,QAAQ,EACN,mFAAmF;QACrF,MAAM,EAAE,mBAAmB,CAAC,OAAO;QACnC,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO;QAC7D,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAChB,KAAK,KAAK,UAAU;YAClB,CAAC,CAAC,mFAAmF;YACrF,CAAC,CAAC,yEAAyE;KAChF;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,IAAc;IAC1C,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;IAE3B,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACnE,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,GAAG,CAAC,uBAAuB,IAAI,KAAK,CAAC,CAAC;QACtC,GAAG,CAAC,mBAAmB,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjE,OAAO,CAAC,CAAC;IACX,CAAC;IAED,wFAAwF;IACxF,qFAAqF;IACrF,QAAQ;IACR,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC;QAC5C,GAAG,CAAC,GAAG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QAC5B,GAAG,CAAC,WAAW,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,KAAK,KAAK,qBAAqB,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;QACpD,GAAG,CAAC,WAAW,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACrB,sFAAsF;QACtF,oFAAoF;QACpF,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,eAAe,KAAK,GAAG,CAAC,CAAC;QAC3C,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,CAAC;QACH,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,EAAsC,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,mBAAmB,UAAU,EAAE,GAAG,CAAC,CAAC;QACxC,GAAG,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,MAAM,MAAM,KAAK,EAAE,CAAC,CAAC;IAC5C,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3B,wFAAwF;IACxF,wEAAwE;IACxE,GAAG,CAAC,8DAA8D,CAAC,CAAC;IACpE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,IAAI;IACX,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC;EACZ,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;;IAEtB,CAAC,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;CACtB,CAAC,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnE,GAAG,CAAC,GAAG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QAC5B,GAAG,CAAC,WAAW,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrF,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IACD,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAClD,GAAG,CAAC,6CAA6C,CAAC,CAAC;IACnD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add popover's Cursor hooks to `~/.cursor/hooks.json`, without touching anything already
|
|
3
|
+
* there.
|
|
4
|
+
*
|
|
5
|
+
* Unlike Claude Code's plugin-scoped hook file, `~/.cursor/hooks.json` is one file shared
|
|
6
|
+
* by every tool a user has configured Cursor hooks for — so this always reads the existing
|
|
7
|
+
* file, appends only popover's own entries (skipping any it already added, so re-running
|
|
8
|
+
* `setup`/`update` is idempotent), and never removes or replaces anything else in it.
|
|
9
|
+
*
|
|
10
|
+
* Whether Cursor actually *runs* every entry in a multi-entry event array, as opposed to
|
|
11
|
+
* only the first, is not yet confirmed — see the note above `setup()`. This writes the
|
|
12
|
+
* structurally-correct merge regardless; that question only affects whether Cursor honors
|
|
13
|
+
* it, not whether this code is right to write it this way.
|
|
14
|
+
*
|
|
15
|
+
* A no-op, not an error, if Cursor isn't installed on this machine (no `~/.cursor`).
|
|
16
|
+
*/
|
|
17
|
+
export declare function installCursorHooks(): number;
|
|
18
|
+
//# sourceMappingURL=cursor-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-hooks.d.ts","sourceRoot":"","sources":["../src/cursor-hooks.ts"],"names":[],"mappings":"AA8CA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAmD3C"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { bad, dim, info, ok } from "./ui.js";
|
|
6
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
/**
|
|
8
|
+
* Which Cursor hook event runs which of our scripts.
|
|
9
|
+
*
|
|
10
|
+
* A single source of truth kept in code rather than a template JSON file: unlike Claude
|
|
11
|
+
* Code's `${CLAUDE_PLUGIN_ROOT}`-templated `hooks/hooks.json`, Cursor's hook config has no
|
|
12
|
+
* confirmed placeholder-substitution mechanism of its own, so *we* have to resolve the
|
|
13
|
+
* scripts' absolute path and merge it in ourselves — a JS map does that more directly than
|
|
14
|
+
* a JSON file that would still need string substitution before use.
|
|
15
|
+
*
|
|
16
|
+
* `stop` carries two entries deliberately: detection (status) and tell delivery
|
|
17
|
+
* (followup_message), confirmed as the right event for both empirically — see the plan.
|
|
18
|
+
*/
|
|
19
|
+
const EVENT_SCRIPTS = {
|
|
20
|
+
sessionStart: ["ensure-daemon.mjs"],
|
|
21
|
+
beforeSubmitPrompt: ["cursor-emit-event.mjs"],
|
|
22
|
+
preToolUse: ["cursor-emit-event.mjs"],
|
|
23
|
+
postToolUse: ["cursor-emit-event.mjs"],
|
|
24
|
+
stop: ["cursor-emit-event.mjs", "cursor-deliver-messages.mjs"],
|
|
25
|
+
sessionEnd: ["cursor-emit-event.mjs"],
|
|
26
|
+
};
|
|
27
|
+
function cursorHooksPath() {
|
|
28
|
+
return path.join(os.homedir(), ".cursor", "hooks.json");
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The directory holding the plugin's `scripts/`, resolved the same way
|
|
32
|
+
* `resolveMarketplaceRoot` in setup.ts finds the plugin root — published layout first,
|
|
33
|
+
* dev-checkout fallback second.
|
|
34
|
+
*/
|
|
35
|
+
function resolveScriptsDir() {
|
|
36
|
+
const candidates = [
|
|
37
|
+
path.join(HERE, "..", "plugin", "scripts"), // <pkg>/plugin/scripts — published layout
|
|
38
|
+
path.join(HERE, "..", "..", "..", "plugin", "scripts"), // repo root — dev checkout
|
|
39
|
+
];
|
|
40
|
+
return candidates.find((d) => existsSync(d)) ?? null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Add popover's Cursor hooks to `~/.cursor/hooks.json`, without touching anything already
|
|
44
|
+
* there.
|
|
45
|
+
*
|
|
46
|
+
* Unlike Claude Code's plugin-scoped hook file, `~/.cursor/hooks.json` is one file shared
|
|
47
|
+
* by every tool a user has configured Cursor hooks for — so this always reads the existing
|
|
48
|
+
* file, appends only popover's own entries (skipping any it already added, so re-running
|
|
49
|
+
* `setup`/`update` is idempotent), and never removes or replaces anything else in it.
|
|
50
|
+
*
|
|
51
|
+
* Whether Cursor actually *runs* every entry in a multi-entry event array, as opposed to
|
|
52
|
+
* only the first, is not yet confirmed — see the note above `setup()`. This writes the
|
|
53
|
+
* structurally-correct merge regardless; that question only affects whether Cursor honors
|
|
54
|
+
* it, not whether this code is right to write it this way.
|
|
55
|
+
*
|
|
56
|
+
* A no-op, not an error, if Cursor isn't installed on this machine (no `~/.cursor`).
|
|
57
|
+
*/
|
|
58
|
+
export function installCursorHooks() {
|
|
59
|
+
const cursorHome = path.join(os.homedir(), ".cursor");
|
|
60
|
+
if (!existsSync(cursorHome)) {
|
|
61
|
+
dim("Cursor not found on this machine; skipping its hooks.");
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
const scriptsDir = resolveScriptsDir();
|
|
65
|
+
if (!scriptsDir) {
|
|
66
|
+
bad("Could not find the bundled Cursor hook scripts.");
|
|
67
|
+
dim("This install looks incomplete — reinstall with: npm install -g @popoverinstall/cli");
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
const file = cursorHooksPath();
|
|
71
|
+
let config = { version: 1, hooks: {} };
|
|
72
|
+
if (existsSync(file)) {
|
|
73
|
+
try {
|
|
74
|
+
config = JSON.parse(readFileSync(file, "utf8"));
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
bad(`Could not parse ${file} — leaving it untouched.`);
|
|
78
|
+
dim(err instanceof Error ? err.message : String(err));
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
config.hooks ??= {};
|
|
83
|
+
config.version ??= 1;
|
|
84
|
+
let added = 0;
|
|
85
|
+
for (const [event, scripts] of Object.entries(EVENT_SCRIPTS)) {
|
|
86
|
+
const existing = (config.hooks[event] ??= []);
|
|
87
|
+
for (const script of scripts) {
|
|
88
|
+
const command = `node "${path.join(scriptsDir, script)}"`;
|
|
89
|
+
const alreadyPresent = existing.some((h) => typeof h === "object" && h !== null && h.command === command);
|
|
90
|
+
if (alreadyPresent)
|
|
91
|
+
continue;
|
|
92
|
+
existing.push({ command, timeout: script === "ensure-daemon.mjs" ? 30 : 5 });
|
|
93
|
+
added += 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (added === 0) {
|
|
97
|
+
ok("Cursor hooks already installed.");
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
mkdirSync(cursorHome, { recursive: true });
|
|
101
|
+
writeFileSync(file, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
102
|
+
info(`Added ${added} Cursor hook${added === 1 ? "" : "s"} to ${file}.`);
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=cursor-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-hooks.js","sourceRoot":"","sources":["../src/cursor-hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAyCA;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,MAEb,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,12 @@ import { execTool } from "./exec.js";
|
|
|
12
12
|
import { askDaemon, daemonIsRunning } from "./ipc-client.js";
|
|
13
13
|
import { login } from "./login.js";
|
|
14
14
|
import { rename } from "./rename.js";
|
|
15
|
+
import { reposCommand } from "./repos.js";
|
|
16
|
+
import { configCommand } from "./config-command.js";
|
|
15
17
|
import { setup } from "./setup.js";
|
|
16
18
|
import { forkCommand } from "./snapshot.js";
|
|
17
19
|
import { bad, c, dim, info, ok, warn } from "./ui.js";
|
|
20
|
+
import { vaultCommand } from "./vaults.js";
|
|
18
21
|
import { welcome } from "./welcome.js";
|
|
19
22
|
/**
|
|
20
23
|
* This package's own version, read from the manifest npm installed beside us.
|
|
@@ -55,10 +58,25 @@ async function main() {
|
|
|
55
58
|
return team();
|
|
56
59
|
case "rename":
|
|
57
60
|
return rename(rest);
|
|
61
|
+
// Deliberately takes no repo argument at any depth: which repo it means comes from the
|
|
62
|
+
// working directory, because registering one is a claim of possession rather than a
|
|
63
|
+
// name. See packages/cli/src/repos.ts.
|
|
64
|
+
case "repos":
|
|
65
|
+
return reposCommand(rest);
|
|
66
|
+
// Machine-local settings, not team ones. The only key today changes who can see an
|
|
67
|
+
// agent, so it needs a way to be read and turned off that is not hand-editing JSON.
|
|
68
|
+
case "config":
|
|
69
|
+
return configCommand(rest);
|
|
58
70
|
case "daemon":
|
|
59
71
|
return daemonCommand(rest[0] ?? "status");
|
|
60
72
|
case "fork":
|
|
61
73
|
return forkCommand(rest);
|
|
74
|
+
// A vault is the other half of `fork`, and the difference is worth knowing before you
|
|
75
|
+
// reach for either: a fork goes to one named person, expires in a day, and is carried on
|
|
76
|
+
// as their own session; a vault is published to a team, never expires, is never carried
|
|
77
|
+
// on, and is readable by the service. See packages/cli/src/vaults.ts and docs/vaults.md.
|
|
78
|
+
case "vault":
|
|
79
|
+
return vaultCommand(rest);
|
|
62
80
|
case "log":
|
|
63
81
|
return showLog(Number(flag(rest, "--lines") ?? 20));
|
|
64
82
|
case "uninstall":
|
|
@@ -128,35 +146,38 @@ function gettingStarted() {
|
|
|
128
146
|
? ` ${c.green(marker)} ${c.dim(name)}${c.dim(what)}`
|
|
129
147
|
: ` ${c.bold(marker)} ${c.bold(name)}${what}`;
|
|
130
148
|
});
|
|
131
|
-
return ` ${c.bold("Finish installing")} — npm placed the files, these connect them:
|
|
132
|
-
|
|
133
|
-
${lines.join("\n")}
|
|
134
|
-
|
|
135
|
-
${c.dim("Then run")} ${c.cyan("/popover:team")} ${c.dim("inside Claude Code to see your teammates' agents.")}
|
|
149
|
+
return ` ${c.bold("Finish installing")} — npm placed the files, these connect them:
|
|
150
|
+
|
|
151
|
+
${lines.join("\n")}
|
|
152
|
+
|
|
153
|
+
${c.dim("Then run")} ${c.cyan("/popover:team")} ${c.dim("inside Claude Code to see your teammates' agents.")}
|
|
136
154
|
`;
|
|
137
155
|
}
|
|
138
156
|
function usage() {
|
|
139
157
|
const next = gettingStarted();
|
|
140
|
-
console.log(`
|
|
141
|
-
${c.bold("popover")} — connect this machine, and see what your team's agents are doing
|
|
142
|
-
${next ? `\n${next}` : ""}
|
|
143
|
-
Asking and telling happen inside a session, where the agent doing them has the
|
|
144
|
-
context to be worth reading: ${c.bold("/popover:ask B1 …")} and ${c.bold("/popover:tell B1 …")}
|
|
145
|
-
|
|
146
|
-
${c.bold("popover login")} [--api URL] Connect this machine to your team
|
|
147
|
-
${c.bold("popover logout")} Forget this machine's credentials
|
|
148
|
-
${c.bold("popover setup")} [--force] Re-register the Claude Code plugin
|
|
149
|
-
${c.bold("popover welcome")} Set up and log in — the whole of a first run
|
|
150
|
-
${c.bold("popover update")} Upgrade to the newest version and restart the daemon
|
|
151
|
-
${c.bold("popover changelog")} [--all] What the last update changed
|
|
152
|
-
${c.bold("popover status")} Show this machine's sessions and connection
|
|
153
|
-
${c.bold("popover team")} Print the team roster
|
|
154
|
-
${c.bold("popover rename")} [handle] Name an agent, so your team can tell them apart
|
|
155
|
-
${c.bold("popover
|
|
156
|
-
${c.bold("popover
|
|
157
|
-
${c.bold("popover
|
|
158
|
-
${c.bold("popover
|
|
159
|
-
${c.bold("popover
|
|
158
|
+
console.log(`
|
|
159
|
+
${c.bold("popover")} — connect this machine, and see what your team's agents are doing
|
|
160
|
+
${next ? `\n${next}` : ""}
|
|
161
|
+
Asking and telling happen inside a session, where the agent doing them has the
|
|
162
|
+
context to be worth reading: ${c.bold("/popover:ask B1 …")} and ${c.bold("/popover:tell B1 …")}
|
|
163
|
+
|
|
164
|
+
${c.bold("popover login")} [--api URL] Connect this machine to your team
|
|
165
|
+
${c.bold("popover logout")} Forget this machine's credentials
|
|
166
|
+
${c.bold("popover setup")} [--force] Re-register the Claude Code plugin
|
|
167
|
+
${c.bold("popover welcome")} Set up and log in — the whole of a first run
|
|
168
|
+
${c.bold("popover update")} Upgrade to the newest version and restart the daemon
|
|
169
|
+
${c.bold("popover changelog")} [--all] What the last update changed
|
|
170
|
+
${c.bold("popover status")} Show this machine's sessions and connection
|
|
171
|
+
${c.bold("popover team")} Print the team roster
|
|
172
|
+
${c.bold("popover rename")} [handle] Name an agent, so your team can tell them apart
|
|
173
|
+
${c.bold("popover repos")} <add|rm> Which repos your team treats as one product
|
|
174
|
+
${c.bold("popover config")} [key] [value] Settings for this machine
|
|
175
|
+
${c.bold("popover fork")} <create|open|launch|list|revoke>
|
|
176
|
+
${c.bold("popover vault")} <create|list|ask|note|archive>
|
|
177
|
+
${c.bold("popover doctor")} Diagnose setup problems
|
|
178
|
+
${c.bold("popover daemon")} <start|stop|restart|status|logs>
|
|
179
|
+
${c.bold("popover log")} [--lines N] Recent questions and messages your agents received
|
|
180
|
+
${c.bold("popover uninstall")} Remove popover from this machine
|
|
160
181
|
`);
|
|
161
182
|
}
|
|
162
183
|
/**
|