@paigy/mcp 0.40.39 → 0.40.40

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.
@@ -1,18 +1,19 @@
1
1
  import {
2
2
  AGENT_TOOLS,
3
+ ENABLE_LISTEN_COMMAND,
3
4
  mcpInputSchema,
4
5
  serverInstructions,
5
6
  sessionStartHook,
6
7
  withSessionStartHook
7
- } from "./chunk-QE33A5XK.js";
8
+ } from "./chunk-H2EBILBE.js";
8
9
  import {
9
10
  CODEX_ENV,
10
11
  agentName,
11
12
  withCodexEnv
12
- } from "./chunk-X4UIGUEX.js";
13
+ } from "./chunk-V4OVLFMP.js";
13
14
 
14
15
  // src/toolset.ts
15
- var CONFIGURE_DESCRIPTION = "Set this agent up with Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call does whatever the situation needs: NOT SET UP \u2192 if this machine holds a device credential (the user ran the Paigy desktop app or harness) it hatches an identity INSTANTLY, no code and no approval; otherwise it starts the code ceremony. ALREADY SET UP \u2192 returns your current identity and the two things left to decide, renaming it or logging out. 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 (pick your own; 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.\n\nTHE CODE CEREMONY does not open a browser; the user enters the code in the Paigy app (or scans `qr`). A result with { user_code, device_code, qr, user_message } has already started polling for approval in the background: you MUST immediately print the `user_message` (the bare code) to the user, AND in that same turn call configure again with that `device_code` to collect the result. It returns the moment the user approves; on { status:'pending' } call again to keep waiting; on { status:'awaiting_confirmation' } (E2EE) show the bare `user_message` verify code and call again to finish. The leading text block of every result states the code plainly, so it shows even if you emit no prose.\n\nOn { status:'paired' }, or whenever it returns `enable_command`, ALWAYS follow the `enable_prompt`: PRINT that command for the user to run, so Paigy's tools stop prompting each time. 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.\n\nLOG OUT with { logOut: true }: it logs out THIS agent only \u2014 its token is revoked server-side and its local entry removed. Other agents and this device's credential are untouched, and a later configure re-hatches instantly while the device credential exists. Disconnecting the device itself is not an agent tool.";
16
+ var CONFIGURE_DESCRIPTION = "Set this agent up with Paigy \u2014 call it FIRST, before contact, and any time you're unsure who you are. One call does whatever the situation needs: NOT SET UP \u2192 if this machine holds a device credential (the user ran the Paigy desktop app or harness) it hatches an identity INSTANTLY, no code and no approval; otherwise it starts the code ceremony. ALREADY SET UP \u2192 returns your current identity and the two things left to decide, renaming it or logging out. 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 (pick your own; voices: rachel, george, jessica, brian, lily). Safe to call any time: idempotent, and it never writes settings.\n\nTHE CODE CEREMONY does not open a browser; the user enters the code in the Paigy app (or scans `qr`). A result with { user_code, device_code, qr, user_message } has already started polling for approval in the background: you MUST immediately print the `user_message` (the bare code) to the user, AND in that same turn call configure again with that `device_code` to collect the result. It returns the moment the user approves; on { status:'pending' } call again to keep waiting; on { status:'awaiting_confirmation' } (E2EE) show the bare `user_message` verify code and call again to finish. Every result's first text block states the code plainly.\n\nOn { status:'paired' }, or whenever it returns `enable_command`, ALWAYS follow the `enable_prompt`: PRINT that command for the user to run, so Paigy's tools stop prompting each time. 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.\n\nLOG OUT with { logOut: true }: it logs out THIS agent only \u2014 its token is revoked server-side and its local entry removed. Other agents and this device's credential are untouched, and a later configure re-hatches instantly while the device credential exists. Disconnecting the device itself is not an agent tool.";
16
17
  var SERVER_INSTRUCTIONS = serverInstructions({ waits: true });
17
18
 
18
19
  // src/tools.ts
@@ -106,36 +107,45 @@ function withPaigyStopHookToml(existing, agent) {
106
107
  ].join("\n");
107
108
  return existing + entry;
108
109
  }
109
- function withPaigyStopHookJson(existing, agent) {
110
- let config = {};
111
- if (existing) {
110
+ function withoutPaigyStopHookJson(existing) {
111
+ const ours = (hook) => hook?.name === "paigy-stalled";
112
+ let config;
113
+ try {
114
+ config = JSON.parse(existing);
115
+ } catch {
116
+ return null;
117
+ }
118
+ if (!config || typeof config !== "object" || Array.isArray(config)) return null;
119
+ const hooks = config.hooks;
120
+ if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) return null;
121
+ const groups = hooks.AfterAgent;
122
+ if (!Array.isArray(groups) || !groups.every((g) => g && typeof g === "object" && !Array.isArray(g))) return null;
123
+ const held = (g) => Array.isArray(g.hooks) && g.hooks.some(ours);
124
+ if (!groups.some(held)) return null;
125
+ const kept = groups.flatMap((g) => {
126
+ if (!held(g)) return [g];
127
+ const left = g.hooks.filter((hook) => !ours(hook));
128
+ return left.length ? [{ ...g, hooks: left }] : [];
129
+ });
130
+ const { AfterAgent: _old, ...rest } = hooks;
131
+ const next = kept.length ? { ...rest, AfterAgent: kept } : rest;
132
+ const { hooks: _hooks, ...others } = config;
133
+ return JSON.stringify(Object.keys(next).length ? { ...others, hooks: next } : others, null, 2);
134
+ }
135
+ function removeOldGeminiHooks() {
136
+ const home = homedir();
137
+ const changed = [];
138
+ for (const path of [join(home, ".gemini", "settings.json"), join(home, ".gemini", "antigravity-cli", "settings.json")]) {
112
139
  try {
113
- config = JSON.parse(existing);
140
+ if (!existsSync(path)) continue;
141
+ const updated = withoutPaigyStopHookJson(readFileSync(path, "utf8"));
142
+ if (updated === null) continue;
143
+ writeFileSync(path, updated);
144
+ changed.push(path);
114
145
  } catch {
115
- return null;
116
146
  }
117
147
  }
118
- const hooks = config.hooks ?? {};
119
- const kept = (hooks.AfterAgent ?? []).map((group) => ({ ...group, hooks: (group.hooks ?? []).filter((hook) => hook.name !== "paigy-stalled") })).filter((group) => group.hooks.length > 0);
120
- config.hooks = {
121
- ...hooks,
122
- AfterAgent: [
123
- ...kept,
124
- {
125
- matcher: "*",
126
- hooks: [
127
- {
128
- name: "paigy-stalled",
129
- type: "command",
130
- command: stalledHookCommand(agent),
131
- timeout: 6e4
132
- // milliseconds, in Gemini CLI
133
- }
134
- ]
135
- }
136
- ]
137
- };
138
- return JSON.stringify(config, null, 2);
148
+ return changed;
139
149
  }
140
150
  function autoConfigureClients(skip = agentName()) {
141
151
  const home = homedir();
@@ -156,20 +166,6 @@ function autoConfigureClients(skip = agentName()) {
156
166
  } catch {
157
167
  }
158
168
  }
159
- for (const path of [
160
- join(home, ".gemini", "settings.json"),
161
- join(home, ".gemini", "antigravity-cli", "settings.json")
162
- ]) {
163
- try {
164
- if (!existsSync(dirname(path))) continue;
165
- const existing = existsSync(path) ? readFileSync(path, "utf8") : null;
166
- const updated = withPaigyStopHookJson(existing, "antigravity");
167
- if (updated === null) continue;
168
- writeFileSync(path, updated);
169
- registered.push(`Paigy's turn-end reminder (${path})`);
170
- } catch {
171
- }
172
- }
173
169
  }
174
170
  if (skip !== "codex") {
175
171
  const codexConfig = join(home, ".codex", "config.toml");
@@ -204,7 +200,7 @@ function claudeInstallHint(skip = agentName()) {
204
200
  }
205
201
  return "Claude Code detected \u2014 to add Paigy there, run /plugin marketplace add paigy-ai/mcp then /plugin install paigy (pairing carries over; no need to pair again). Note: If you run this inside a live session, type /reload-plugins afterward so the agent connects to the new tools.";
206
202
  }
207
- var ENABLE_COMMAND = "npx -y -p @paigy/mcp@latest paigy-enable-tools";
203
+ var ENABLE_COMMAND = ENABLE_LISTEN_COMMAND;
208
204
  var PAIGY_TOOL_IDS = [
209
205
  ...TOOL_NAMES,
210
206
  ...HIDDEN_ALIASES
@@ -321,6 +317,7 @@ export {
321
317
  ConfigureSchema,
322
318
  TOOLS,
323
319
  openBrowser,
320
+ removeOldGeminiHooks,
324
321
  autoConfigureClients,
325
322
  claudeInstallHint,
326
323
  ENABLE_COMMAND,