@paigy/mcp 0.40.11 → 0.40.15
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/README.md +35 -9
- package/dist/{chunk-FQVK7SO4.js → chunk-37FS33UP.js} +56 -5
- package/dist/{chunk-H5GGWCPS.js → chunk-PHTQITTC.js} +835 -498
- package/dist/{chunk-I5QINUN6.js → chunk-SE2JCKDI.js} +593 -454
- package/dist/{chunk-XIHVHYDU.js → chunk-X6G7XXCP.js} +1 -1
- package/dist/dist-DKP3DJQJ.js +110 -0
- package/dist/enable.js +9 -4
- package/dist/index.js +29 -5
- package/dist/listen.js +48 -16
- package/dist/onboard.js +4 -4
- package/dist/slot.js +1 -1
- package/dist/stalled.js +35 -0
- package/dist/statusline.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -117,8 +117,8 @@ Then pair: `PAIGY_AGENT=gemini npx -p @paigy/mcp@latest paigy-mcp-onboard`.
|
|
|
117
117
|
## Tools
|
|
118
118
|
|
|
119
119
|
One catalog, on every transport (2026-09-11): the agent tools are `AGENT_TOOLS` in
|
|
120
|
-
`packages/schema/src/tools.ts` — contact, check_replies,
|
|
121
|
-
|
|
120
|
+
`packages/schema/src/tools.ts` — contact, check_replies, create_goal, claim_goal,
|
|
121
|
+
get_goal, update_goal — and both this server and the hosted MCP
|
|
122
122
|
(`apps/api/src/mcp`) publish that list and dispatch it through the SDK's one `runTool`.
|
|
123
123
|
This server adds only onboard, pair and unpair, the tools that mint and delete the token
|
|
124
124
|
file this machine holds. See the [SDK contract](../../packages/sdk/README.md) for the
|
|
@@ -171,18 +171,17 @@ To launch an agent — any agent, not just Claude — when work arrives, set
|
|
|
171
171
|
| `PAIGY_PARENT_ID` | the thread to continue on (`PAIGY_THREAD_ID` is the same value, for existing scripts) |
|
|
172
172
|
| `PAIGY_DELIVERY_ID` | the Delivery being acted on — `contact({deliveryId})` rereads it |
|
|
173
173
|
| `PAIGY_GOAL_ID` | the Goal it belongs to — `claim_goal` it **first** |
|
|
174
|
-
| `PAIGY_CONTEXT_THREAD_ID` | a past conversation this Goal names — `get_thread` it **first** |
|
|
175
174
|
| `PAIGY_TEXT` | what the person said, in prose |
|
|
176
175
|
|
|
177
176
|
Hand `$PAIGY_WORK` to a harness that can read JSON and decide for itself; use the
|
|
178
177
|
scalars for a plain shell launcher that shouldn't need `jq`. They describe **one**
|
|
179
178
|
item — the oldest open Delivery — because the contract is one thread at a time. An
|
|
180
|
-
absent fact is unset rather than empty, so `${
|
|
181
|
-
|
|
179
|
+
absent fact is unset rather than empty, so `${PAIGY_GOAL_ID:-}` distinguishes "no
|
|
180
|
+
Goal" from "an empty id".
|
|
182
181
|
|
|
183
182
|
```sh
|
|
184
183
|
# Claude Code — hand it everything and let it plan:
|
|
185
|
-
PAIGY_ON_WAKE='claude -p "Handle the Paigy work in $PAIGY_WORK — claim_goal first, and
|
|
184
|
+
PAIGY_ON_WAKE='claude -p "Handle the Paigy work in $PAIGY_WORK — claim_goal first, and read a Goal you do not recognize with get_goal."' \
|
|
186
185
|
npx -y -p @paigy/mcp paigy-listen --install
|
|
187
186
|
|
|
188
187
|
# Codex (or any CLI harness) — the scalars are enough for a one-liner:
|
|
@@ -190,6 +189,22 @@ PAIGY_ON_WAKE='codex exec "Claim Paigy goal $PAIGY_GOAL_ID and continue thread $
|
|
|
190
189
|
npx -y -p @paigy/mcp paigy-listen --install
|
|
191
190
|
```
|
|
192
191
|
|
|
192
|
+
### A session that starts listening (`listen`, #2265)
|
|
193
|
+
|
|
194
|
+
Nothing pushes an answer into a session started in a terminal — it waits for that
|
|
195
|
+
session's next `check_replies`. What can reach a running session mid-turn is a watched
|
|
196
|
+
background task, so a terminal session runs `paigy-listen --brief` as its own watched task
|
|
197
|
+
(one human line per event: `Paigy · wake:reply · 2 waiting · lead <deliveryId> on Goal
|
|
198
|
+
<goalId>: "…"`). The `listen` tool is how a session asked to "start listening" gets there
|
|
199
|
+
in one call: it decides and hands over — `listening` (nothing to do), `unpaired` (call
|
|
200
|
+
`onboard`), or `start` with the exact command (this server's own node and `listen.js`,
|
|
201
|
+
`PAIGY_AGENT` and `PAIGY_SESSION_ID` set to this session's slot and id — a session-bound
|
|
202
|
+
identity is refused from any other) for the agent to run as its background task in the
|
|
203
|
+
same turn. It never subscribes, spawns or writes settings. "Already running" is a fact: the
|
|
204
|
+
daemon marks its pid under `~/.paigy/listen/<slot>.pid` while it runs. A session started by
|
|
205
|
+
the Paigy harness (`paigy-harness host` / `run`) needs none of this — its pump types each
|
|
206
|
+
answer in as it lands, and `listen` says so.
|
|
207
|
+
|
|
193
208
|
`$PAIGY_TEXT` is whatever the user said, expanded inside a shell command — keep it
|
|
194
209
|
quoted, as above. For anything longer than a one-liner, point `PAIGY_ON_WAKE` at a
|
|
195
210
|
script and branch on `$PAIGY_EVENT` there:
|
|
@@ -197,12 +212,23 @@ script and branch on `$PAIGY_EVENT` there:
|
|
|
197
212
|
```sh
|
|
198
213
|
#!/bin/sh
|
|
199
214
|
# ~/.paigy/on-wake.sh — chmod +x, then PAIGY_ON_WAKE=~/.paigy/on-wake.sh
|
|
200
|
-
|
|
201
|
-
|
|
215
|
+
codex exec "Paigy goal $PAIGY_GOAL_ID. claim_goal it first. The user said: $PAIGY_TEXT. Reply with contact when done."
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Stalled work (Claude Code stop hook)
|
|
202
219
|
|
|
203
|
-
|
|
220
|
+
An agent that goes quiet leaves its Goals behind. Three things hand it that work back, all
|
|
221
|
+
reading one rule (no progress for 3 days, `COLD_AFTER_MS`): `check_replies` lists it under
|
|
222
|
+
`stalled`, `paigy-listen` carries it in `PAIGY_WORK` (and launches for it on boot), and this
|
|
223
|
+
hook holds a Claude Code session's stop once a day to list it:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "npx -y -p @paigy/mcp paigy-stalled" }] }] } }
|
|
204
227
|
```
|
|
205
228
|
|
|
229
|
+
It is silent when nothing is stalled, when the stop is already being continued by a hook, and
|
|
230
|
+
after it has reminded that session once today.
|
|
231
|
+
|
|
206
232
|
## Statusline (Claude Code)
|
|
207
233
|
|
|
208
234
|
`paigy-statusline` prints which Paigy identity this Claude Code session is, for the
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGENT_TOOLS,
|
|
3
3
|
mcpInputSchema,
|
|
4
|
-
serverInstructions
|
|
5
|
-
|
|
4
|
+
serverInstructions,
|
|
5
|
+
sessionStartHook,
|
|
6
|
+
withSessionStartHook
|
|
7
|
+
} from "./chunk-SE2JCKDI.js";
|
|
6
8
|
import {
|
|
7
9
|
agentName
|
|
8
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-PHTQITTC.js";
|
|
9
11
|
|
|
10
12
|
// src/toolset.ts
|
|
11
13
|
var ONBOARD_DESCRIPTION = "Get this agent talking to Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call, and it does whatever the situation needs: NOT SET UP \u2192 hatches an identity instantly if this machine holds a device credential (the user ran the Paigy desktop app or harness), otherwise starts the code ceremony; ALREADY SET UP \u2192 returns your current identity and offers the two things left to decide, renaming it or unpairing; TOKEN NO LONGER VALID \u2192 says so, then re-pairs. Pass { name, voice } to choose who you are when hatching, or to RENAME yourself when already set up (voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings \u2014 the tool-allowlist state it reports is read-only. If it returns a `user_code`, print it to the user immediately and call onboard again with the `device_code`. If it returns `enable_command`, PRINT that command for the user to run \u2014 you cannot apply it yourself (it writes your own permission allowlist, which hosts block as privilege escalation), so print it, don't wait for it, and carry on.";
|
|
@@ -44,6 +46,15 @@ var IDENTITY_TOOLS = [
|
|
|
44
46
|
name: "unpair",
|
|
45
47
|
description: "Log out / unpair this agent from the user's Paigy account: revokes the token server-side (it stops working everywhere) and deletes the local ~/.paigy/token.json. Takes no arguments. After this, contact won't work until the user pairs again with the pair tool.",
|
|
46
48
|
inputSchema: mcpInputSchema(z.object({}))
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
// A session asked to "start listening" can, in one call (#2265). Nothing can push an
|
|
52
|
+
// answer into a session started in a terminal — an answer waits for its next check_replies —
|
|
53
|
+
// and the only thing that reaches a running session mid-turn is a watched background task.
|
|
54
|
+
// So this tool decides and hands over: it never subscribes, spawns or writes settings.
|
|
55
|
+
name: "listen",
|
|
56
|
+
description: "Am I listening for answers, and if not, how do I start? Call it when the user asks you to start listening (or to stay reachable), and whenever you are unsure whether you are. Takes no arguments. Returns `status`: `listening` \u2014 nothing to do (a session started by the Paigy harness is ALWAYS listening: its pump types each answer in as it lands; or your own paigy-listen is already running, with its pid); `unpaired` \u2014 call onboard first; `start` \u2014 a `command` you MUST run NOW, in this same turn, as a watched BACKGROUND task (your harness's background-task/monitor tool), and leave running for the session. Each line that task prints is a Delivery waiting for you: claim_goal the Goal it names, read it, answer with contact. Start it once. This tool never subscribes or starts anything itself \u2014 the listener has to be YOUR watched task, or its lines reach no one.",
|
|
57
|
+
inputSchema: mcpInputSchema(z.object({}))
|
|
47
58
|
}
|
|
48
59
|
];
|
|
49
60
|
var TOOLS = [...AGENT_TOOLS, ...IDENTITY_TOOLS];
|
|
@@ -52,7 +63,7 @@ var HIDDEN_ALIASES = [];
|
|
|
52
63
|
|
|
53
64
|
// src/clients.ts
|
|
54
65
|
import { execFile } from "child_process";
|
|
55
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
66
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
56
67
|
import { homedir, platform } from "os";
|
|
57
68
|
import { join, dirname } from "path";
|
|
58
69
|
function openBrowser(url) {
|
|
@@ -208,6 +219,45 @@ function enablePaigyTools(scope = "user", cwd = process.cwd()) {
|
|
|
208
219
|
already: PAIGY_TOOL_IDS.filter((t) => prevAllow.includes(t))
|
|
209
220
|
};
|
|
210
221
|
}
|
|
222
|
+
var HOOK_CONFIGS = [
|
|
223
|
+
{ harness: "Claude Code", path: () => join(homedir(), ".claude", "settings.json") },
|
|
224
|
+
{ harness: "Codex", path: () => join(homedir(), ".codex", "hooks.json") }
|
|
225
|
+
];
|
|
226
|
+
function installSessionListening() {
|
|
227
|
+
const script = join(homedir(), ".paigy", "hooks", "session-start.sh");
|
|
228
|
+
try {
|
|
229
|
+
mkdirSync(dirname(script), { recursive: true });
|
|
230
|
+
writeFileSync(script, sessionStartHook(), { mode: 493 });
|
|
231
|
+
chmodSync(script, 493);
|
|
232
|
+
} catch (e) {
|
|
233
|
+
return [{ harness: "all", path: script, ok: false, why: e.message }];
|
|
234
|
+
}
|
|
235
|
+
const out = [];
|
|
236
|
+
for (const { harness, path: at } of HOOK_CONFIGS) {
|
|
237
|
+
const path = at();
|
|
238
|
+
if (!existsSync(dirname(path))) continue;
|
|
239
|
+
let existing = null;
|
|
240
|
+
try {
|
|
241
|
+
existing = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
242
|
+
} catch (e) {
|
|
243
|
+
out.push({ harness, path, ok: false, why: e.message });
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const already = existing?.includes(script) ?? false;
|
|
247
|
+
const updated = withSessionStartHook(existing, script);
|
|
248
|
+
if (updated === null) {
|
|
249
|
+
out.push({ harness, path, ok: false, why: `${path} didn't parse as JSON \u2014 not overwriting it` });
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
writeFileSync(path, updated + "\n");
|
|
254
|
+
out.push({ harness, path, ok: true, already });
|
|
255
|
+
} catch (e) {
|
|
256
|
+
out.push({ harness, path, ok: false, why: e.message });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
211
261
|
|
|
212
262
|
export {
|
|
213
263
|
SERVER_INSTRUCTIONS,
|
|
@@ -220,5 +270,6 @@ export {
|
|
|
220
270
|
ENABLE_COMMAND,
|
|
221
271
|
PAIGY_TOOL_IDS,
|
|
222
272
|
paigyToolsAllowlisted,
|
|
223
|
-
enablePaigyTools
|
|
273
|
+
enablePaigyTools,
|
|
274
|
+
installSessionListening
|
|
224
275
|
};
|