From 25c39f2aadc79ba94ecd0cd2a60ce22d2b044acb Mon Sep 17 00:00:00 2001 From: xarmian Date: Tue, 20 Oct 2026 00:10:46 +0000 Subject: [PATCH] fix(plugin): a Pad MCP tool wakes the session whatever the server is called (TASK-3970) Codex r3: the matcher required "pad" in the server part of the tool name, so a Pad server registered under another name (work-tracker) was missed. It now matches any mcp____pad_* tool. The tests table Pad tools under four server names and four tools that are not Pad's, and add ./pad and ../bin/pad to the Bash shapes. Claude-Session: https://claude.ai/code/session_01EZ6hZzQiytCeBxXrtCWcVd --- plugin/hooks/register.js | 4 +++- plugin/tests/watch.test.ts | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/plugin/hooks/register.js b/plugin/hooks/register.js index ad94c7692..2f4a23d14 100644 --- a/plugin/hooks/register.js +++ b/plugin/hooks/register.js @@ -1105,7 +1105,9 @@ function wake($) { // shape; the shapes are tabled in watch.test.ts rather than listed here. function usesPad(e) { if (e.tool === 'Bash') return /(^|[^\w.-])pad($|[^\w.-])/.test(String(e.command || '')) - return /^mcp__.*pad.*__pad_/i.test(String(e.tool || '')) + // Any server name: Pad's tools are pad_* whatever the person called the server + // (Codex r3: `.*pad.*` in the server part missed a server named otherwise). + return /^mcp__.+__pad_[a-z_]+$/i.test(String(e.tool || '')) } // Silent by construction: every failure here leaves the session as it was. diff --git a/plugin/tests/watch.test.ts b/plugin/tests/watch.test.ts index f09576a8d..72147c02e 100644 --- a/plugin/tests/watch.test.ts +++ b/plugin/tests/watch.test.ts @@ -114,7 +114,7 @@ const RUNS_PAD = [ 'pad bootstrap', 'pad', ' pad item list', 'cd /w && pad item list', 'ls; pad project next', 'a || pad x', 'a | pad x', 'bash -c "pad item list"', `sh -c 'pad project next'`, 'bash -c "pad"', `sh -c 'pad'`, 'echo $(pad)', 'X="$(pad auth whoami)"', 'echo `pad auth whoami`', 'pad;ls', 'pad&&ls', '\\pad item list', 'PAD_AGENT=x pad bootstrap', '~/.local/bin/pad project next', - '/usr/local/bin/pad item show TASK-5', 'env pad bootstrap', 'time pad x', '(pad x)', '{ pad x; }', 'pad\titem list', + '/usr/local/bin/pad item show TASK-5', './pad item list', '../bin/pad x', 'env pad bootstrap', 'time pad x', '(pad x)', '{ pad x; }', 'pad\titem list', ] const NOT_PAD = ['ls -la && git status', 'cat ~/.padrc', 'echo spad', 'padd --version', 'ls pad-monitor.sh', 'grep -r ipad .', 'cat notepad.txt', 'echo pad_x'] @@ -143,6 +143,27 @@ test('a command that does not run pad wakes nothing', async ($, on) => { expect(s.runs).toEqual([]) }) +// Pad MCP tools under any server name, and tools that are not Pad's. +const PAD_MCP = ['mcp__pad__pad_item', 'mcp__claude_ai_Pad__pad_project', 'mcp__plugin_pad_pad__pad_search', 'mcp__work-tracker__pad_collection'] +const NOT_PAD_MCP = ['mcp__pad__other', 'mcp__ipad__notes', 'mcp__github__create_issue', 'mcp__notepad__pad'] + +test('a Pad MCP tool under any server name wakes the session; other tools do not', async ($, on) => { + mock.clock(on) + const s = stubs(on, { armed: false }) + await $.session.start({ cwd: '/work/demo' }) + for (const tool of NOT_PAD_MCP) await $.tool.call({ tool } as any) + await settle() + expect(s.runs).toEqual([]) + for (const tool of PAD_MCP) { + const before = s.atCall.length + await $.tool.call({ tool } as any) + await settle() + expect([tool, s.atCall[before].includes('pad session register')]).toEqual([tool, true]) + await $.session.end({ reason: 'prompt_input_exit' }) + s.runs.length = 0 + } +}) + test('a Pad MCP tool call wakes the session too', async ($, on) => { mock.clock(on) const s = stubs(on, { armed: true })