@paigy/mcp 0.40.25 → 0.40.27

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.
@@ -17,7 +17,7 @@ import {
17
17
  acknowledge,
18
18
  agentName,
19
19
  authToken,
20
- claimGoal,
20
+ checkActivity,
21
21
  claimSessionEnds,
22
22
  claimSessions,
23
23
  contact,
@@ -40,6 +40,7 @@ import {
40
40
  pollToken,
41
41
  reach,
42
42
  reachAs,
43
+ readDelivery,
43
44
  readSlot,
44
45
  readToken,
45
46
  receive,
@@ -61,12 +62,10 @@ import {
61
62
  slotName,
62
63
  submitTriage,
63
64
  subscribeWake,
64
- updateGoal,
65
65
  updateSlot,
66
66
  whoAmI,
67
- whoIsWorking,
68
67
  withCodexEnv
69
- } from "./chunk-EJYXBEWN.js";
68
+ } from "./chunk-4C6SDQ7A.js";
70
69
  export {
71
70
  AGENT_TOOLS,
72
71
  AGENT_TOOL_NAMES,
@@ -86,7 +85,7 @@ export {
86
85
  acknowledge,
87
86
  agentName,
88
87
  authToken,
89
- claimGoal,
88
+ checkActivity,
90
89
  claimSessionEnds,
91
90
  claimSessions,
92
91
  contact,
@@ -109,6 +108,7 @@ export {
109
108
  pollToken,
110
109
  reach,
111
110
  reachAs,
111
+ readDelivery,
112
112
  readSlot,
113
113
  readToken,
114
114
  receive,
@@ -130,9 +130,7 @@ export {
130
130
  slotName,
131
131
  submitTriage,
132
132
  subscribeWake,
133
- updateGoal,
134
133
  updateSlot,
135
134
  whoAmI,
136
- whoIsWorking,
137
135
  withCodexEnv
138
136
  };
package/dist/enable.js CHANGED
@@ -3,9 +3,9 @@ import {
3
3
  PAIGY_TOOL_IDS,
4
4
  enablePaigyTools,
5
5
  installSessionListening
6
- } from "./chunk-UAVXYNLV.js";
7
- import "./chunk-GHSB2VP6.js";
8
- import "./chunk-EJYXBEWN.js";
6
+ } from "./chunk-A5FYZK6P.js";
7
+ import "./chunk-X3GTDW5G.js";
8
+ import "./chunk-4C6SDQ7A.js";
9
9
 
10
10
  // src/enable.ts
11
11
  function main() {
@@ -16,14 +16,14 @@ function main() {
16
16
  if (arg === "-h" || arg === "--help") {
17
17
  console.log("usage: paigy-enable-tools [--scope user|project]");
18
18
  console.log("");
19
- console.log("Allowlist Paigy's notify/await tools in Claude Code so they run without an");
19
+ console.log("Allowlist Paigy's tools in Claude Code so they run without an");
20
20
  console.log("approval prompt each time \u2014 an unattended session can't stall on a dialog.");
21
21
  console.log("");
22
22
  console.log(" --scope user ~/.claude/settings.json (default) \u2014 every project");
23
23
  console.log(" --scope project ./.claude/settings.json \u2014 just this repo");
24
24
  console.log("");
25
25
  console.log(`Adds: ${PAIGY_TOOL_IDS.join(", ")}`);
26
- console.log("Leaves pair/unpair out, so they stay human-approved. Safe to re-run.");
26
+ console.log("Safe to re-run.");
27
27
  return;
28
28
  }
29
29
  if (arg === "--scope") {
package/dist/index.js CHANGED
@@ -9,22 +9,22 @@ import {
9
9
  resolvePairing,
10
10
  startPairing,
11
11
  suggestedAgentName
12
- } from "./chunk-2ROD77AW.js";
12
+ } from "./chunk-UQDA7X3W.js";
13
13
  import {
14
+ ConfigureSchema,
14
15
  ENABLE_COMMAND,
15
- OnboardSchema,
16
16
  PAIGY_TOOL_IDS,
17
- PairSchema,
18
17
  SERVER_INSTRUCTIONS,
19
18
  TOOLS,
20
19
  paigyToolsAllowlisted
21
- } from "./chunk-UAVXYNLV.js";
20
+ } from "./chunk-A5FYZK6P.js";
22
21
  import {
23
22
  decideListen,
24
23
  existingListener,
24
+ harnessDriven,
25
25
  listenerKey,
26
26
  wakeStatusPath
27
- } from "./chunk-GHSB2VP6.js";
27
+ } from "./chunk-X3GTDW5G.js";
28
28
  import {
29
29
  clearSurface,
30
30
  writeSurface
@@ -45,7 +45,7 @@ import {
45
45
  setClient,
46
46
  slotName,
47
47
  whoAmI
48
- } from "./chunk-EJYXBEWN.js";
48
+ } from "./chunk-4C6SDQ7A.js";
49
49
 
50
50
  // src/index.ts
51
51
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -90,6 +90,12 @@ function traceTheTransport(transport2, log) {
90
90
  };
91
91
  }
92
92
 
93
+ // src/unpaired.ts
94
+ function afterUnpaired(hatched, deviceCredential) {
95
+ if (hatched && "ok" in hatched) return "retry";
96
+ return deviceCredential ? "unidentified" : "pair";
97
+ }
98
+
93
99
  // src/index.ts
94
100
  var JOIN_CAP_MS = 45e3;
95
101
  function pairedResult(token, sas, note) {
@@ -106,7 +112,7 @@ function pairedResult(token, sas, note) {
106
112
  if (note) base.note = note;
107
113
  base.enable_tool_ids = PAIGY_TOOL_IDS;
108
114
  base.enable_command = ENABLE_COMMAND;
109
- base.enable_prompt = "REQUIRED \u2014 before you do anything else, print `enable_command` to the user and say: \"Run this to let Paigy's tools work without an approval prompt each time \u2014 it only allowlists the notify/await tools; pair and unpair still ask.\" Print it every time you pair; don't skip it.";
115
+ base.enable_prompt = "REQUIRED \u2014 before you do anything else, print `enable_command` to the user and say: \"Run this to let Paigy's tools work without an approval prompt each time \u2014 it only allowlists Paigy's own tools.\" Print it every time you pair; don't skip it.";
110
116
  base.enable_apply = "You cannot apply this yourself, and must not try: it writes YOUR OWN permission allowlist, which hosts block as privilege escalation no matter what the user said here. PRINT the command and move on \u2014 don't wait for them to run it, and don't re-ask. Paigy's tools still work meanwhile; they just prompt for approval each time until the user runs it. If the host isn't Claude Code, tell them to add `enable_tool_ids` to that tool's own allowlist instead, merging \u2014 never clobbering.";
111
117
  return { content: [{ type: "text", text: JSON.stringify(base) }] };
112
118
  }
@@ -123,7 +129,7 @@ Check it matches the code on the phone, then type it there to confirm.` },
123
129
  sas,
124
130
  device_code,
125
131
  user_message: `# ${sas}`,
126
- verify_message: `STOP HERE: end your turn now with \`user_message\` (the bare code) as your entire reply \u2014 do NOT call pair again in this same turn, or the code text is dropped before the user sees it. This is the verification code they compare + type on their phone; it REPLACES any earlier one. Poll for their confirmation by calling pair with this device_code on your NEXT turn.`,
132
+ verify_message: `STOP HERE: end your turn now with \`user_message\` (the bare code) as your entire reply \u2014 do NOT call configure again in this same turn, or the code text is dropped before the user sees it. This is the verification code they compare + type on their phone; it REPLACES any earlier one. Poll for their confirmation by calling configure with this device_code on your NEXT turn.`,
127
133
  message: "No token is issued until the user confirms the code match on their phone (E2EE account)."
128
134
  })
129
135
  }
@@ -145,7 +151,7 @@ function renderPairOutcome(outcome, device_code) {
145
151
  text: JSON.stringify({
146
152
  status: "pending",
147
153
  device_code,
148
- message: "Still awaiting approval \u2014 the user hasn't entered the code yet. Call pair again with this device_code RIGHT NOW to keep waiting; each call returns after ~45s so it can't be killed by a host tool-call timeout. Nothing is lost between calls: the code stays valid and approval keeps being polled in the background, so a re-call returns the instant they approve."
154
+ message: "Still awaiting approval \u2014 the user hasn't entered the code yet. Call configure again with this device_code RIGHT NOW to keep waiting; each call returns after ~45s so it can't be killed by a host tool-call timeout. Nothing is lost between calls: the code stays valid and approval keeps being polled in the background, so a re-call returns the instant they approve."
149
155
  })
150
156
  }]
151
157
  };
@@ -155,7 +161,7 @@ function unidentifiedResult() {
155
161
  return { isError: true, content: [{ type: "text", text: JSON.stringify({
156
162
  error: "no_identity",
157
163
  slot: agentName(),
158
- message: `This session has no Paigy identity yet, so nothing was sent \u2014 it will not speak as another session. This machine holds a device credential but hatching under it just failed (it may have been revoked), so call \`pair\` (no arguments) to set this session up, then retry \u2014 it re-hatches if the credential recovered and runs the code ceremony if it didn't. To reuse an existing identity instead, start the session with PAIGY_AGENT set to its slot (${listSlots().filter((s) => s !== "Desktop").join(", ") || "none yet"}).`
164
+ message: `This session has no Paigy identity yet, so nothing was sent \u2014 it will not speak as another session. This machine holds a device credential but hatching under it just failed (it may have been revoked), so call \`configure\` (no arguments) to set this session up, then retry \u2014 it re-hatches if the credential recovered and runs the code ceremony if it didn't. To reuse an existing identity instead, start the session with PAIGY_AGENT set to its slot (${listSlots().filter((s) => s !== "Desktop").join(", ") || "none yet"}).`
159
165
  }) }] };
160
166
  }
161
167
  function pairStartResult(start, loggedOut) {
@@ -197,7 +203,7 @@ This session isn't on a connected computer, so it asks by code.`;
197
203
  expires_in: start.expiresIn,
198
204
  qr: ascii,
199
205
  user_message: userMessage,
200
- message: "REQUIRED: print user_message for the user \u2014 the LINK is the primary action, the code is the fallback, and `qr` is there for a phone when they ask. Then call pair again with device_code to collect approval. Do not open a browser yourself."
206
+ message: "REQUIRED: print user_message for the user \u2014 the LINK is the primary action, the code is the fallback, and `qr` is there for a phone when they ask. Then call configure again with device_code to collect approval. Do not open a browser yourself."
201
207
  }) }
202
208
  ] };
203
209
  }
@@ -234,7 +240,7 @@ async function runPair(device_code, name, voice, note) {
234
240
  if ("ok" in dt) return pairedResult(
235
241
  dt,
236
242
  void 0,
237
- "Hatched instantly under this device's credential \u2014 no code needed. " + (dt.message ? `${dt.message} Set it with onboard({ name }).` : name ? "" : `You were given a default name ("${dt.name}") \u2014 the user never chose it, so don't announce it as their agent's identity. They can rename it in the app, or you can re-call pair with { name, voice }.`)
243
+ "Hatched instantly under this device's credential \u2014 no code needed. " + (dt.message ? `${dt.message} Set it with configure({ name }).` : name ? "" : `You were given a default name ("${dt.name}") \u2014 the user never chose it, so don't announce it as their agent's identity. They can rename it in the app, or you can call configure with { name, voice }.`)
238
244
  );
239
245
  miss = dt;
240
246
  }
@@ -253,10 +259,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
253
259
  return await handleTool(request, extra?.signal);
254
260
  } catch (e) {
255
261
  if (e instanceof UnpairedError) {
256
- const IDENTITY_TOOLS = /* @__PURE__ */ new Set(["pair", "onboard", "unpair", "listen"]);
262
+ const IDENTITY_TOOLS = /* @__PURE__ */ new Set(["configure"]);
257
263
  const hatched = IDENTITY_TOOLS.has(request.params.name) ? null : await hatchUnderDevice();
258
- if (hatched) return await handleTool(request, extra?.signal);
259
- if (listSlots().includes("Desktop")) return unidentifiedResult();
264
+ const next = afterUnpaired(hatched, listSlots().includes("Desktop"));
265
+ if (next === "retry") return await handleTool(request, extra?.signal);
266
+ if (next === "unidentified") return unidentifiedResult();
260
267
  return pairStartResult(await startPairing(suggestedAgentName2()));
261
268
  }
262
269
  throw e;
@@ -265,26 +272,46 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
265
272
  function suggestedAgentName2() {
266
273
  return suggestedAgentName(server.getClientVersion?.()?.name);
267
274
  }
275
+ async function logOutResult() {
276
+ const token = readToken();
277
+ let revoked = false;
278
+ if (token) {
279
+ try {
280
+ revoked = await revokeToken(token);
281
+ } catch {
282
+ revoked = false;
283
+ }
284
+ }
285
+ const removed = deleteToken();
286
+ clearPairing();
287
+ return {
288
+ content: [{
289
+ type: "text",
290
+ text: JSON.stringify({
291
+ ok: true,
292
+ revoked,
293
+ removed,
294
+ message: token ? `Logged out. Server token ${revoked ? "revoked" : "revoke not confirmed"}; local token ${removed ? "deleted" : "was already absent"}.` : "No token found \u2014 already logged out."
295
+ })
296
+ }]
297
+ };
298
+ }
268
299
  async function handleTool(request, signal) {
269
300
  switch (request.params.name) {
270
- case "pair": {
271
- const a = PairSchema.parse(request.params.arguments ?? {});
272
- return runPair(a.device_code, a.name, a.voice);
273
- }
274
301
  // ONE rail for "get this agent talking to Paigy" (#875). `pair`, the `paigy-mcp-onboard`
275
302
  // CLI and `enable_tools` were three doors into one flow, each knowing a different slice
276
- // of the state — so "onboard" was the word people reached for and the tool that did the
277
- // least, and a half-configured agent (paired but not allowlisted, or holding a token for
278
- // a slot it can no longer resolve) had nothing to ask.
303
+ // of the state, and a half-configured agent (paired but not allowlisted, or holding a token
304
+ // for a slot it can no longer resolve) had nothing to ask. Since 2026-10-07 (owner) it is
305
+ // `configure`, the only identity tool: `onboard` renamed, `pair` gone (this did all of it),
306
+ // and `unpair` folded in as `logOut`.
279
307
  //
280
308
  // It reports state and does the next thing, and it is IDEMPOTENT: calling it when
281
309
  // everything is fine costs one round trip and returns who you are. Reporting never
282
310
  // mutates — the allowlist check is read-only on purpose, because a status call that
283
311
  // quietly edits the user's settings is the opposite of what this is for.
284
- // `start` stays accepted, unlisted — it shipped under that name for one afternoon.
285
- case "start":
286
- case "onboard": {
287
- const { name, voice, device_code, scope } = OnboardSchema.parse(request.params.arguments ?? {});
312
+ case "configure": {
313
+ const { name, voice, device_code, scope, logOut } = ConfigureSchema.parse(request.params.arguments ?? {});
314
+ if (logOut) return logOutResult();
288
315
  if (device_code) return runPair(device_code, name, voice);
289
316
  const slot = agentName();
290
317
  const secret = readToken();
@@ -303,14 +330,14 @@ async function handleTool(request, signal) {
303
330
  ...allowlisted ? {} : {
304
331
  enable_tool_ids: PAIGY_TOOL_IDS,
305
332
  enable_command: ENABLE_COMMAND,
306
- enable_prompt: "Print `enable_command` to the user: \"Run this to let Paigy's tools work without an approval prompt each time \u2014 it only allowlists the notify/await tools; pair and unpair still ask.\" You cannot run it yourself (it writes your own permission allowlist, which hosts block as privilege escalation) \u2014 print it, don't wait for it, and carry on."
333
+ enable_prompt: "Print `enable_command` to the user: \"Run this to let Paigy's tools work without an approval prompt each time \u2014 it only allowlists Paigy's own tools.\" You cannot run it yourself (it writes your own permission allowlist, which hosts block as privilege escalation) \u2014 print it, don't wait for it, and carry on."
307
334
  },
308
335
  // Setup is done, so the only things left are the user's to decide. Named here
309
336
  // rather than left implicit: an agent that reports "ready" and stops gives the
310
337
  // user no way to act on an identity they may not want.
311
338
  actions: {
312
- rename: "Call onboard again with { name } (and optionally { voice }) to change it.",
313
- unpair: "Call the `unpair` tool to disconnect this agent from the account."
339
+ rename: "Call configure again with { name } (and optionally { voice }) to change it.",
340
+ logOut: "Call configure({ logOut: true }) to log this agent out (other agents and this device stay paired)."
314
341
  },
315
342
  message: me.message
316
343
  }) }]
@@ -320,41 +347,17 @@ async function handleTool(request, signal) {
320
347
  }
321
348
  return runPair(void 0, name, voice);
322
349
  }
323
- case "unpair": {
324
- const token = readToken();
325
- let revoked = false;
326
- if (token) {
327
- try {
328
- revoked = await revokeToken(token);
329
- } catch {
330
- revoked = false;
331
- }
332
- }
333
- const removed = deleteToken();
334
- clearPairing();
335
- return {
336
- content: [{
337
- type: "text",
338
- text: JSON.stringify({
339
- ok: true,
340
- revoked,
341
- removed,
342
- message: token ? `Unpaired. Server token ${revoked ? "revoked" : "revoke not confirmed"}; local token ${removed ? "deleted" : "was already absent"}.` : "No token found \u2014 already unpaired."
343
- })
344
- }]
345
- };
346
- }
347
350
  // AM I LISTENING? (#2265) Decides and hands over — `listening.ts` owns the rule and the four
348
351
  // answers. The facts are read here, once: the harness's session mark, a token to listen as,
349
352
  // a live listener's mark for this slot, and where THIS server's node and daemon live. The
350
353
  // daemon is the `listen.js` beside our own entry, so the command never resolves a package.
351
354
  case "listen": {
355
+ if (!harnessDriven(process.env) && !readToken()) throw new UnpairedError();
352
356
  const slot = agentName();
353
357
  const key = listenerKey(slot, process.env);
354
358
  const pid = existingListener(slot, process.env);
355
359
  const decision = decideListen({
356
360
  env: process.env,
357
- token: !!readToken(),
358
361
  pidAlive: pid !== null,
359
362
  wake: readWakeStatus(wakeStatusPath(key)),
360
363
  ...pid !== null ? { pid } : {},
package/dist/listen.js CHANGED
@@ -3,6 +3,7 @@ import {
3
3
  WakeBridge
4
4
  } from "./chunk-RK5LT7NH.js";
5
5
  import {
6
+ entryUnit,
6
7
  entryWords,
7
8
  existingListener,
8
9
  harnessDriven,
@@ -10,16 +11,16 @@ import {
10
11
  removeListenMark,
11
12
  wakeStatusPath,
12
13
  writeListenMark
13
- } from "./chunk-GHSB2VP6.js";
14
+ } from "./chunk-X3GTDW5G.js";
14
15
  import {
15
16
  agentName,
16
- contact,
17
17
  getGoal,
18
18
  heartbeat,
19
+ readDelivery,
19
20
  receive,
20
21
  slotName,
21
22
  subscribeWake
22
- } from "./chunk-EJYXBEWN.js";
23
+ } from "./chunk-4C6SDQ7A.js";
23
24
 
24
25
  // src/listen.ts
25
26
  import { createHash } from "crypto";
@@ -134,7 +135,7 @@ var emit = (obj) => line(JSON.stringify(obj));
134
135
  var listeningAs = () => slotName(agentName()) ?? agentName();
135
136
  var SWEEP_EVERY_MS = Number(process.env.PAIGY_SWEEP_MS) || 9e4;
136
137
  function saidIn(delivery) {
137
- return ("entries" in delivery ? delivery.entries : []).filter((entry) => entry.kind === "contribution").map((entry) => entryWords(entry)).filter(Boolean).join("\n");
138
+ return ("entries" in delivery ? delivery.entries : []).filter((entry) => entry.kind === "contribution" && !entryUnit(entry)).map((entry) => entryWords(entry)).filter(Boolean).join("\n");
138
139
  }
139
140
  function leadOf(deliveries, goalId) {
140
141
  return (goalId ? deliveries.find((d) => d.goalIds.includes(goalId)) : void 0) ?? deliveries[0];
@@ -144,9 +145,9 @@ function brief(waiting, reason, as, goalId) {
144
145
  const stalled = waiting.stalled?.length ? ` \xB7 ${waiting.stalled.length} of your Goals stalled 3+ days: update, finish or cancel each` : "";
145
146
  const review = waiting.review ?? [];
146
147
  const named = (goalId ? review.find((g) => g.goalId === goalId) : void 0) ?? review[0];
147
- const fresh = review.length ? ` \xB7 new on ${review.length === 1 ? "your Goal" : `${review.length} of your Goals`}, first ${named.goalId} ("${named.title}"): claim_goal to read it` : "";
148
+ const fresh = review.length ? ` \xB7 new on ${review.length === 1 ? "your Goal" : `${review.length} of your Goals`}, first ${named.goalId} ("${named.title}"): get_goal to read it` : "";
148
149
  const assigned = waiting.assigned ?? [];
149
- const mine = assigned.length ? ` \xB7 assigned to you: ${assigned.length === 1 ? "1 Goal" : `${assigned.length} Goals`}, first ${assigned[0].goalId} ("${assigned[0].title}"): claim_goal({goalId}) to start it` : "";
150
+ const mine = assigned.length ? ` \xB7 assigned to you: ${assigned.length === 1 ? "1 Goal" : `${assigned.length} Goals`}, first ${assigned[0].goalId} ("${assigned[0].title}"): get_goal({goalId}) to read it; your first write starts it` : "";
150
151
  if (!lead) return `Paigy \xB7 ${reason === "boot" ? `listening as ${as}` : reason}${mine}${fresh || (mine ? "" : " \xB7 nothing waiting")}${stalled}`;
151
152
  const said = saidIn(lead) || ("about" in lead ? lead.about : "");
152
153
  const shown = said.length > 160 ? `${said.slice(0, 160)}\u2026` : said;
@@ -171,7 +172,7 @@ async function sweep(reason, goalId, bridge = new WakeBridge(process.env, wakeSt
171
172
  });
172
173
  const { deliveries: rows, stalled, review, assigned } = await receive();
173
174
  const lead = leadOf(rows, goalId);
174
- const full = lead ? await contact({ deliveryId: lead.deliveryId }, { waits: false }).then((d) => "sent" in d ? null : d).catch(() => null) : null;
175
+ const full = lead ? await readDelivery(lead.deliveryId).catch(() => null) : null;
175
176
  const deliveries = rows.map((d) => d === lead && full ? full : d);
176
177
  const leadGoalId = lead?.goalIds[0];
177
178
  const goal = leadGoalId ? await getGoal(leadGoalId).then((g) => ({ goalId: leadGoalId, progress: g.progress })).catch(() => null) : null;
package/dist/onboard.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  resolvePairing,
4
4
  startPairing,
5
5
  suggestedAgentName
6
- } from "./chunk-2ROD77AW.js";
6
+ } from "./chunk-UQDA7X3W.js";
7
7
  import {
8
8
  autoConfigureClients,
9
9
  claudeInstallHint,
10
10
  openBrowser
11
- } from "./chunk-UAVXYNLV.js";
12
- import "./chunk-GHSB2VP6.js";
11
+ } from "./chunk-A5FYZK6P.js";
12
+ import "./chunk-X3GTDW5G.js";
13
13
  import {
14
14
  TOKEN_PATH,
15
15
  agentName,
@@ -20,7 +20,7 @@ import {
20
20
  saveToken,
21
21
  setIdentity,
22
22
  whoAmI
23
- } from "./chunk-EJYXBEWN.js";
23
+ } from "./chunk-4C6SDQ7A.js";
24
24
 
25
25
  // src/onboard.ts
26
26
  function reportRegistered() {
@@ -58,8 +58,8 @@ Paigy MCP onboarding (agent: "${agentName()}")
58
58
  console.log(`Already set up as "${who.name}"${who.voice ? ` (voice: ${who.voice})` : ""}.
59
59
  `);
60
60
  console.log("Nothing to do. What's left is yours to decide:");
61
- console.log(' rename \u2014 PAIGY_NAME="New name" paigy-mcp-onboard (or the `onboard` MCP tool)');
62
- console.log(" unpair \u2014 the MCP `unpair` tool, or delete this slot from ~/.paigy/token.json\n");
61
+ console.log(' rename \u2014 PAIGY_NAME="New name" paigy-mcp-onboard (or the `configure` MCP tool)');
62
+ console.log(" log out \u2014 the MCP `configure` tool with { logOut: true }, or delete this slot from ~/.paigy/token.json\n");
63
63
  return;
64
64
  }
65
65
  console.log("The saved token is no longer valid \u2014 re-pairing.\n");
package/dist/slot.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  agentName
4
- } from "./chunk-EJYXBEWN.js";
4
+ } from "./chunk-4C6SDQ7A.js";
5
5
 
6
6
  // src/slot.ts
7
7
  process.stdout.write(agentName());
package/dist/stalled.js CHANGED
@@ -15,7 +15,7 @@ function stopReply(stalled, event = "Stop") {
15
15
  if (!stalled.length) return null;
16
16
  const list = stalled.slice(0, 8).map((g) => `"${g.title}" (${g.goalId})`).join(", ");
17
17
  const more = stalled.length > 8 ? ` and ${stalled.length - 8} more` : "";
18
- const reason = `Before you stop: ${stalled.length} of your Paigy Goals have had no progress for 3 days: ${list}${more}. For each, update_goal with progress, or finish or cancel it with manage_goals.`;
18
+ const reason = `Before you stop: ${stalled.length} of your Paigy Goals have had no progress for 3 days: ${list}${more}. For each, report where it stands with a contact update, or finish or cancel it with manage_goals.`;
19
19
  return event === "AfterAgent" ? { decision: "block", reason, systemMessage: reason } : { decision: "block", reason };
20
20
  }
21
21
  async function main() {
@@ -29,7 +29,7 @@ async function main() {
29
29
  const dir = join(homedir(), ".paigy", "stalled-reminded");
30
30
  const mark = join(dir, `${session || "session"}-${day}`);
31
31
  if (existsSync(mark)) return;
32
- const { receive } = await import("./dist-NMHU2UNG.js");
32
+ const { receive } = await import("./dist-N6ZJUJV7.js");
33
33
  const reply = stopReply((await receive()).stalled ?? [], input.hook_event_name ?? "Stop");
34
34
  if (!reply) return;
35
35
  mkdirSync(dir, { recursive: true });
@@ -7,7 +7,7 @@ import {
7
7
  readToken,
8
8
  sessionSlot,
9
9
  slotName
10
- } from "./chunk-EJYXBEWN.js";
10
+ } from "./chunk-4C6SDQ7A.js";
11
11
 
12
12
  // src/statusline.ts
13
13
  import { realpathSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paigy/mcp",
3
- "version": "0.40.25",
3
+ "version": "0.40.27",
4
4
  "description": "Paigy MCP server — the AI agent harness that calls you. Lets an agent notify a user and await their reply.",
5
5
  "license": "MIT",
6
6
  "type": "module",