diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..59e075e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: ci + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v7 + with: + node-version: '24' + package-manager-cache: false + - run: npm ci + - run: npm run lint + - run: npm test + - name: Verify publish contents + run: npm pack --dry-run diff --git a/CHANGELOG.md b/CHANGELOG.md index eaa6700..ab4664c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,25 @@ # Changelog -All notable changes to `pilot-mcp` documented here. Format follows +All notable changes to the `pilotprotocol-mcp` npm adapter are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [0.2.11] - 2026-08-07 + +### Fixed +- The exact obsolete `heartbeat --claude` command now exits successfully and + silently while current setup removes it. Machines carrying settings from + releases <=0.2.5 therefore no longer reject every Claude Code prompt before + the harness is restarted. +- Security and internals documentation now describe the current pre/post tool + enforcement boundary and the separate `pilotprotocol-mcp` npm package. +- Generated hook and MCP commands now pin `pilotprotocol-mcp@0.2.11`. +- The compatibility setup path now checksum-pins the installer-compatible + public core runtime `managed-runtime-v0.1.5` instead of the superseded + `v0.1.2` bundle. + ## [0.2.10] - 2026-08-07 ### Fixed diff --git a/README.md b/README.md index bc0151c..1f5ed9c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# pilot-mcp +# pilotprotocol-mcp + +The npm package is **`pilotprotocol-mcp`**. Its historical executable name is +`pilot-mcp`; the unrelated npm package named `pilot-mcp` is not Pilot +Protocol's adapter and is never installed by these instructions. **Your agent's overlay network — local or hosted, your choice.** 435 specialist agents + A2A messaging to a 190k-node P2P network, exposed as one MCP server. diff --git a/SECURITY.md b/SECURITY.md index b1f2584..7579803 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,10 +11,11 @@ advisory on the GitHub Security Advisories tab once a fix is available. ## Scope In scope: -- `pilot-mcp` npm package and its platform subpackages +- `pilotprotocol-mcp` npm package and its published adapter distributions - The `pilot-mcp setup` auto-config flow (config file writes, daemon install) -- The MCP server implementations (stdio + HTTP) -- The Go binaries (`pilot-daemon`, `pilotctl`) shipped in the platform subpackages +- The stdio MCP server and native harness pre/post adapters +- Runtime discovery and the checksum-verified public core bootstrap retained + for compatibility with existing setup flows Out of scope (report upstream): - Vulnerabilities in the Pilot Protocol itself → security@pilotprotocol.network @@ -27,9 +28,11 @@ Notable classes we explicitly defend against: 1. **Hook-execution attack via config write.** `pilot-mcp setup` writes to harness config files. We do not write arbitrary executable commands — - only `npx -y pilotprotocol-mcp` and `pilot-mcp heartbeat --`, both stable - audited entrypoints. We never accept user-influenced strings into hook - command fields. + only version-pinned `pilotprotocol-mcp` stdio and native `hook --harness + --phase pre|post` commands. The exact historical `heartbeat --claude` + invocation is retained only as a silent, non-networked compatibility shim + while setup removes obsolete settings. We never accept user-influenced + strings into hook command fields. 2. **Supply chain.** Releases are tagged + signed; npm publishes use `--provenance`; Docker images are SBOM + Sigstore signed via the @@ -37,9 +40,8 @@ Notable classes we explicitly defend against: registry (never raw GitHub Releases) for corporate-proxy compatibility. 3. **Daemon socket trust.** `pilot-mcp` talks to the local daemon via Unix - socket / named pipe. We rely on OS process boundary for auth; HTTP mode - uses a per-install bearer token in `~/.pilot/mcp-token` bound to loopback - by default. + socket / named pipe. We rely on the OS process boundary for local socket + access. The adapter does not expose an HTTP transport. 4. **Identity exfiltration.** The user's Ed25519 private key (`~/.pilot/identity.json`) is never read by `pilot-mcp` directly — only the daemon touches it. diff --git a/cli.js b/cli.js index 6c76a94..b57c111 100755 --- a/cli.js +++ b/cli.js @@ -8,6 +8,7 @@ // pilot-mcp attach [flags] → configure harness hooks around an already-adopted core node // pilot-mcp doctor → diagnose daemon/registry/harness state // pilot-mcp hook --harness → native pre/post tool enforcement bridge +// pilot-mcp heartbeat --claude → silent compatibility shim for obsolete hooks // pilot-mcp tour → first-run guided demo (one specialist call) // pilot-mcp export-identity → write identity to portable file // pilot-mcp import-identity → load identity from portable file @@ -72,6 +73,17 @@ async function main() { await runHook(parseFlags(args.slice(1))); break; } + case 'heartbeat': { + // Releases <=0.2.5 installed this command in Claude Code's + // UserPromptSubmit hook. Current setup removes that hook because prompt + // submission is not a tool-action enforcement boundary, but an existing + // Claude process can keep the old settings loaded until it restarts. + // Keep the exact legacy invocation as a silent allow so an obsolete + // config cannot reject every prompt while migration catches up. + const { runLegacyHeartbeat } = await import('./src/hooks/legacy-heartbeat.js'); + await runLegacyHeartbeat(args.slice(1)); + break; + } case 'picoclaw-hook': { const { runPicoClawRPC } = await import('./src/hooks/picoclaw-rpc.js'); await runPicoClawRPC(); diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index d851abf..e96da43 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -54,13 +54,21 @@ from (in order): If none exist, we surface an MCP error pointing at `pilot-mcp setup`. -## Per-turn injection is NOT MCP's job +## Native action hooks complement MCP -MCP only fires on `tools/call`. It cannot inject context into the system or -user prompt on every turn. For the "always check pilot before web_search" -directive to land every turn, we need per-harness hooks: -- Claude Code: `UserPromptSubmit` hook in `~/.claude/settings.json` - (`pilot-mcp heartbeat --claude` returns `additionalContext`) +MCP governs only calls routed through Pilot's MCP tools. Where a harness offers +native interception, setup also installs a pre/post action boundary so Pilot +can evaluate the exact tool input before execution and retain the result after +execution. In particular, Claude Code uses `PreToolUse`, `PostToolUse`, and +`PostToolUseFailure` in `~/.claude/settings.json`. + +Releases <=0.2.5 briefly installed a `UserPromptSubmit` heartbeat command that +never existed. Setup now removes only that obsolete Pilot entry, preserving +unrelated prompt hooks. The CLI retains the exact historical +`heartbeat --claude` spelling as a silent compatibility shim so a running +Claude process with cached settings cannot reject prompts before restart. + +Other harness boundaries include: - OpenHands: Claude-compatible hook JSON, discovered per repository from `.openhands/hooks.json`; a user-home hook is not fleet-wide enforcement - PicoClaw: `hooks.processes.PreMessage` with `inject_output: true` — but ONLY diff --git a/package-lock.json b/package-lock.json index 6a77daa..19fef4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pilotprotocol-mcp", - "version": "0.2.10", + "version": "0.2.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pilotprotocol-mcp", - "version": "0.2.10", + "version": "0.2.11", "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", diff --git a/package.json b/package.json index 504ba02..efbe5ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pilotprotocol-mcp", - "version": "0.2.10", + "version": "0.2.11", "mcpName": "io.github.pilot-protocol/pilot-mcp", "description": "Your agent's overlay network. MCP server exposing 436 Pilot specialist agents + P2P A2A messaging. One install configures every harness on your machine.", "type": "module", diff --git a/server.json b/server.json index 9dd2121..99ee918 100644 --- a/server.json +++ b/server.json @@ -7,13 +7,13 @@ "url": "https://github.com/pilot-protocol/pilot-mcp", "source": "github" }, - "version": "0.2.10", + "version": "0.2.11", "websiteUrl": "https://pilotprotocol.network", "packages": [ { "registryType": "npm", "identifier": "pilotprotocol-mcp", - "version": "0.2.10", + "version": "0.2.11", "transport": { "type": "stdio" } } ] diff --git a/src/hooks/legacy-heartbeat.js b/src/hooks/legacy-heartbeat.js new file mode 100644 index 0000000..70a2de4 --- /dev/null +++ b/src/hooks/legacy-heartbeat.js @@ -0,0 +1,8 @@ +// Claude Code treats exit 0 with no stdout as "allow" for UserPromptSubmit. +// Do not print JSON or context here: this command exists only to make the +// obsolete <=0.2.5 hook harmless until setup removes it from settings.json. +export async function runLegacyHeartbeat(args) { + if (args.length !== 1 || args[0] !== '--claude') { + throw new Error('heartbeat is a legacy compatibility command; only --claude is supported'); + } +} diff --git a/src/setup/harnesses/claude.js b/src/setup/harnesses/claude.js index 623d4f4..4ac40a4 100644 --- a/src/setup/harnesses/claude.js +++ b/src/setup/harnesses/claude.js @@ -35,7 +35,7 @@ async function registerMcp() { current.mcpServers = current.mcpServers ?? {}; current.mcpServers.pilot = { command: 'npx', - args: ['-y', 'pilotprotocol-mcp@0.2.10'], + args: ['-y', 'pilotprotocol-mcp@0.2.11'], }; writeFileSync(SETTINGS, JSON.stringify(current, null, 2)); } diff --git a/src/setup/harnesses/cline.js b/src/setup/harnesses/cline.js index 0a05082..822d019 100644 --- a/src/setup/harnesses/cline.js +++ b/src/setup/harnesses/cline.js @@ -23,7 +23,7 @@ export async function configure() { mkdirSync(dirname(settings), { recursive: true }); const current = existsSync(settings) ? JSON.parse(readFileSync(settings, 'utf8')) : {}; current.mcpServers = current.mcpServers ?? {}; - current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] }; + current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] }; writeFileSync(settings, JSON.stringify(current, null, 2)); installNativeHook('PreToolUse', 'pre'); installNativeHook('PostToolUse', 'post'); diff --git a/src/setup/harnesses/codex.js b/src/setup/harnesses/codex.js index 9de155b..20641e2 100644 --- a/src/setup/harnesses/codex.js +++ b/src/setup/harnesses/codex.js @@ -15,7 +15,7 @@ const HOOKS = join(HOME, '.codex', 'hooks.json'); const BLOCK = ` [mcp_servers.pilot] command = "npx" -args = ["-y", "pilotprotocol-mcp@0.2.10"] +args = ["-y", "pilotprotocol-mcp@0.2.11"] `; export async function configure() { diff --git a/src/setup/harnesses/continue.js b/src/setup/harnesses/continue.js index 960ddd2..e56be6e 100644 --- a/src/setup/harnesses/continue.js +++ b/src/setup/harnesses/continue.js @@ -13,7 +13,7 @@ schema: v1 mcpServers: - name: pilot command: npx - args: ["-y", "pilotprotocol-mcp@0.2.10"] + args: ["-y", "pilotprotocol-mcp@0.2.11"] `; export async function configure() { diff --git a/src/setup/harnesses/copilot.js b/src/setup/harnesses/copilot.js index ab797fe..cf77fd0 100644 --- a/src/setup/harnesses/copilot.js +++ b/src/setup/harnesses/copilot.js @@ -23,7 +23,7 @@ export async function configure() { mkdirSync(dirname(settings), { recursive: true }); const current = existsSync(settings) ? JSON.parse(readFileSync(settings, 'utf8')) : {}; current['github.copilot.chat.mcp.servers'] = current['github.copilot.chat.mcp.servers'] ?? {}; - current['github.copilot.chat.mcp.servers'].pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] }; + current['github.copilot.chat.mcp.servers'].pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] }; writeFileSync(settings, JSON.stringify(current, null, 2)); installHooks(); } diff --git a/src/setup/harnesses/cursor.js b/src/setup/harnesses/cursor.js index 74a11b5..b3ecf6d 100644 --- a/src/setup/harnesses/cursor.js +++ b/src/setup/harnesses/cursor.js @@ -17,7 +17,7 @@ export async function configure() { mkdirSync(dirname(MCP_JSON), { recursive: true }); const current = existsSync(MCP_JSON) ? JSON.parse(readFileSync(MCP_JSON, 'utf8')) : {}; current.mcpServers = current.mcpServers ?? {}; - current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] }; + current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] }; writeFileSync(MCP_JSON, JSON.stringify(current, null, 2)); const hooks = existsSync(HOOKS_JSON) ? JSON.parse(readFileSync(HOOKS_JSON, 'utf8')) : { version: 1, hooks: {} }; diff --git a/src/setup/harnesses/gemini.js b/src/setup/harnesses/gemini.js index 8be2e5f..e79d0df 100644 --- a/src/setup/harnesses/gemini.js +++ b/src/setup/harnesses/gemini.js @@ -11,7 +11,7 @@ export async function configure() { mkdirSync(dirname(SETTINGS), { recursive: true }); const current = existsSync(SETTINGS) ? JSON.parse(readFileSync(SETTINGS, 'utf8')) : {}; current.mcpServers = current.mcpServers ?? {}; - current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] }; + current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] }; current.hooksConfig = current.hooksConfig ?? {}; if (current.hooksConfig.enabled === undefined) current.hooksConfig.enabled = true; current.hooks = current.hooks ?? {}; diff --git a/src/setup/harnesses/hermes.js b/src/setup/harnesses/hermes.js index 749f480..e57852d 100644 --- a/src/setup/harnesses/hermes.js +++ b/src/setup/harnesses/hermes.js @@ -21,7 +21,7 @@ export async function configure() { } document.setIn(['mcp_servers', 'pilot'], { command: 'npx', - args: ['-y', 'pilotprotocol-mcp@0.2.10'], + args: ['-y', 'pilotprotocol-mcp@0.2.11'], }); installHook(document, 'pre_tool_call', 'pre'); installHook(document, 'post_tool_call', 'post'); diff --git a/src/setup/harnesses/junie.js b/src/setup/harnesses/junie.js index a138e5d..083d77b 100644 --- a/src/setup/harnesses/junie.js +++ b/src/setup/harnesses/junie.js @@ -13,11 +13,11 @@ const CLI_CONFIG = join(HOME, '.junie', 'config.json'); export async function configure() { if (!existsSync(CLI_CONFIG)) { mkdirSync(dirname(CLI_CONFIG), { recursive: true }); - writeFileSync(CLI_CONFIG, JSON.stringify({ mcpServers: { pilot: { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] } } }, null, 2)); + writeFileSync(CLI_CONFIG, JSON.stringify({ mcpServers: { pilot: { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] } } }, null, 2)); return; } const current = JSON.parse(readFileSync(CLI_CONFIG, 'utf8')); current.mcpServers = current.mcpServers ?? {}; - current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] }; + current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] }; writeFileSync(CLI_CONFIG, JSON.stringify(current, null, 2)); } diff --git a/src/setup/harnesses/openclaw.js b/src/setup/harnesses/openclaw.js index 68835b5..e2e8e80 100644 --- a/src/setup/harnesses/openclaw.js +++ b/src/setup/harnesses/openclaw.js @@ -23,7 +23,7 @@ export async function configure() { if (!existsSync(CONFIG)) return; // OpenClaw not configured yet — caller already skipped const current = JSON.parse(readFileSync(CONFIG, 'utf8')); current.mcpServers = current.mcpServers ?? {}; - current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] }; + current.mcpServers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] }; writeFileSync(CONFIG, JSON.stringify(current, null, 2)); mkdirSync(join(HOME, '.pilot', 'integrations'), { recursive: true }); cpSync(SOURCE_PLUGIN, INSTALLED_PLUGIN, { recursive: true, force: true }); diff --git a/src/setup/harnesses/openhands.js b/src/setup/harnesses/openhands.js index 98812fd..20df813 100644 --- a/src/setup/harnesses/openhands.js +++ b/src/setup/harnesses/openhands.js @@ -17,7 +17,7 @@ const CONFIG = join(HOME, '.openhands', 'config.toml'); const MCP_BLOCK = ` [mcp.stdio_servers.pilot] command = "npx" -args = ["-y", "pilotprotocol-mcp@0.2.10"] +args = ["-y", "pilotprotocol-mcp@0.2.11"] `; export async function configure(options = {}) { diff --git a/src/setup/harnesses/picoclaw.js b/src/setup/harnesses/picoclaw.js index 377847a..75f3b6d 100644 --- a/src/setup/harnesses/picoclaw.js +++ b/src/setup/harnesses/picoclaw.js @@ -16,7 +16,7 @@ export async function configure() { current.tools = current.tools ?? {}; current.tools.mcp = current.tools.mcp ?? {}; current.tools.mcp.servers = current.tools.mcp.servers ?? {}; - current.tools.mcp.servers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.10'] }; + current.tools.mcp.servers.pilot = { command: 'npx', args: ['-y', 'pilotprotocol-mcp@0.2.11'] }; current.hooks = current.hooks ?? {}; current.hooks.enabled = true; current.hooks.defaults = current.hooks.defaults ?? {}; @@ -26,7 +26,7 @@ export async function configure() { enabled: true, priority: 10, transport: 'stdio', - command: ['npx', '-y', 'pilotprotocol-mcp@0.2.10', 'picoclaw-hook'], + command: ['npx', '-y', 'pilotprotocol-mcp@0.2.11', 'picoclaw-hook'], intercept: ['before_tool', 'after_tool'], }; writeFileSync(CONFIG, JSON.stringify(current, null, 2)); diff --git a/src/setup/harnesses/runtime.js b/src/setup/harnesses/runtime.js index fc7fc98..e3b1c18 100644 --- a/src/setup/harnesses/runtime.js +++ b/src/setup/harnesses/runtime.js @@ -2,7 +2,7 @@ // Requiring a separate global install makes onboarding appear successful while // the first real tool call fails with "pilot-mcp: command not found". -export const PILOT_RUNNER = 'npx -y pilotprotocol-mcp@0.2.10'; +export const PILOT_RUNNER = 'npx -y pilotprotocol-mcp@0.2.11'; export function hookCommand(harness, phase) { return `${PILOT_RUNNER} hook --harness ${harness} --phase ${phase}`; diff --git a/src/setup/runtime.js b/src/setup/runtime.js index 5ca7c0a..cf5ce90 100644 --- a/src/setup/runtime.js +++ b/src/setup/runtime.js @@ -20,23 +20,23 @@ import { pilotctlBinaryPath } from '../daemon-bridge.js'; const DEFAULT_MANIFEST = 'https://pilotprotocol.network/.well-known/latest.json'; const MAX_RUNTIME_ARCHIVE_BYTES = 128 * 1024 * 1024; const MANAGED_RUNTIME = Object.freeze({ - tag: 'managed-runtime-v0.1.2', + tag: 'managed-runtime-v0.1.5', platforms: Object.freeze({ 'darwin-amd64': Object.freeze({ - url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.2/pilot-darwin-amd64.tar.gz', - sha256: '878f5e029424a3726f6ebec4195da619794844bf36487e1c3c8e223cc64a90ff', + url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.5/pilot-darwin-amd64.tar.gz', + sha256: '0d8bbda818bbfa1df9544fe114353eca097d3f7b7e356c4c5ceac1eaaf3f6d63', }), 'darwin-arm64': Object.freeze({ - url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.2/pilot-darwin-arm64.tar.gz', - sha256: '889217ebcc6ecbef77dd694d0ca553c45688183e1c4db739f215bad0c4f3a2a4', + url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.5/pilot-darwin-arm64.tar.gz', + sha256: '9df13081c5340f24c18c7941ad964c7c8c67b64d0f7a7fe98eee5043ee2fe915', }), 'linux-amd64': Object.freeze({ - url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.2/pilot-linux-amd64.tar.gz', - sha256: '94f46dff66bd1032d5ce8930d917a7ed9583a03deba4609d9de5f9049acf4fd8', + url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.5/pilot-linux-amd64.tar.gz', + sha256: '432c04cd27b66e422b5d50778a7f7c220cd20b248348d2fcbad7e0a13e4de498', }), 'linux-arm64': Object.freeze({ - url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.2/pilot-linux-arm64.tar.gz', - sha256: 'e79140e684506b2a244a7e2e0564bee085f5c826bcfbe918ea5e1bb926286c0c', + url: 'https://github.com/pilot-protocol/pilotprotocol/releases/download/managed-runtime-v0.1.5/pilot-linux-arm64.tar.gz', + sha256: 'ce873cc9838a7845956028050358f66a33f87af7c8983d2e63065d89104c2181', }), }), }); diff --git a/test/cli-help.test.js b/test/cli-help.test.js index 0ecbd39..11d4e08 100644 --- a/test/cli-help.test.js +++ b/test/cli-help.test.js @@ -30,6 +30,27 @@ test('serve exits non-zero and says the HTTP transport is unavailable', async () ); }); +test('obsolete Claude heartbeat hook is a silent non-blocking compatibility shim', async () => { + const { stdout, stderr } = await run( + process.execPath, + [cli, 'heartbeat', '--claude'], + { input: JSON.stringify({ hook_event_name: 'UserPromptSubmit', prompt: 'hello' }) }, + ); + assert.strictEqual(stdout, ''); + assert.strictEqual(stderr, ''); +}); + +test('legacy heartbeat rejects unsupported invocations', async () => { + await assert.rejects( + run(process.execPath, [cli, 'heartbeat', '--cursor'], { input: '{}' }), + (err) => { + assert.strictEqual(err.code, 1); + assert.match(err.stderr, /only --claude is supported/i); + return true; + }, + ); +}); + test('no manifest or source string claims bearer-token auth for the HTTP transport', () => { for (const rel of ['src/mcp-http.js', 'cli.js', 'server.json']) { const text = readFileSync(join(root, rel), 'utf8'); diff --git a/test/hermes-setup.test.js b/test/hermes-setup.test.js index c53e3e8..c475701 100644 --- a/test/hermes-setup.test.js +++ b/test/hermes-setup.test.js @@ -23,7 +23,7 @@ test('Hermes setup merges native pre/post hooks without replacing existing YAML' assert.equal(result.model, 'gemini/example'); assert.equal(result.hooks.on_session_start[0].command, 'existing-hook'); assert.equal(result.hooks.pre_tool_call.length, 1); - assert.equal(result.hooks.pre_tool_call[0].command, 'npx -y pilotprotocol-mcp@0.2.10 hook --harness hermes --phase pre'); + assert.equal(result.hooks.pre_tool_call[0].command, 'npx -y pilotprotocol-mcp@0.2.11 hook --harness hermes --phase pre'); assert.equal(result.hooks.post_tool_call.length, 1); - assert.deepEqual(result.mcp_servers.pilot.args, ['-y', 'pilotprotocol-mcp@0.2.10']); + assert.deepEqual(result.mcp_servers.pilot.args, ['-y', 'pilotprotocol-mcp@0.2.11']); }); diff --git a/test/native-harness-setup.test.js b/test/native-harness-setup.test.js index 9cb8fe9..477a129 100644 --- a/test/native-harness-setup.test.js +++ b/test/native-harness-setup.test.js @@ -20,7 +20,7 @@ test('Cursor setup installs an idempotent fail-closed native tool boundary', () const hooks = JSON.parse(readFileSync(join(home, '.cursor', 'hooks.json'), 'utf8')); assert.equal(hooks.hooks.preToolUse.length, 1); assert.equal(hooks.hooks.preToolUse[0].failClosed, true); - assert.match(hooks.hooks.preToolUse[0].command, /^npx -y pilotprotocol-mcp@0\.2\.10 /); + assert.match(hooks.hooks.preToolUse[0].command, /^npx -y pilotprotocol-mcp@0\.2\.11 /); assert.match(hooks.hooks.preToolUse[0].command, /--harness cursor --phase pre/); }); @@ -29,7 +29,7 @@ test('Cline setup installs executable global pre/post hook shims without replaci configureInHome('cline', home); const pre = readFileSync(join(home, 'Documents', 'Cline', 'Hooks', 'PreToolUse'), 'utf8'); const post = readFileSync(join(home, 'Documents', 'Cline', 'Hooks', 'PostToolUse'), 'utf8'); - assert.match(pre, /exec npx -y pilotprotocol-mcp@0\.2\.10 hook/); + assert.match(pre, /exec npx -y pilotprotocol-mcp@0\.2\.11 hook/); assert.match(pre, /--harness cline --phase pre/); assert.match(post, /--harness cline --phase post/); @@ -48,7 +48,7 @@ test('Copilot setup writes the documented cross-platform command-hook fields', ( const pre = hooks.hooks.preToolUse[0]; assert.equal(pre.type, 'command'); assert.equal(pre.bash, pre.powershell); - assert.match(pre.bash, /^npx -y pilotprotocol-mcp@0\.2\.10 /); + assert.match(pre.bash, /^npx -y pilotprotocol-mcp@0\.2\.11 /); assert.equal(pre.command, undefined); }); @@ -60,7 +60,7 @@ test('PicoClaw setup attaches the native process hook as a fixed argv array', () writeFileSync(target, '{}'); }); const config = JSON.parse(readFileSync(join(home, '.picoclaw', 'config.json'), 'utf8')); - assert.deepEqual(config.hooks.processes.pilot.command, ['npx', '-y', 'pilotprotocol-mcp@0.2.10', 'picoclaw-hook']); + assert.deepEqual(config.hooks.processes.pilot.command, ['npx', '-y', 'pilotprotocol-mcp@0.2.11', 'picoclaw-hook']); assert.deepEqual(config.hooks.processes.pilot.intercept, ['before_tool', 'after_tool']); }); diff --git a/test/runtime-setup.test.js b/test/runtime-setup.test.js index de70ace..3c467f9 100644 --- a/test/runtime-setup.test.js +++ b/test/runtime-setup.test.js @@ -32,7 +32,7 @@ test('runtime manifest rejects cross-repository and cross-version substitution', test('managed setup selects a release asset pinned in the published installer', () => { const release = managedRuntimeRelease('linux', 'x64'); - assert.equal(release.tag, 'managed-runtime-v0.1.2'); + assert.equal(release.tag, 'managed-runtime-v0.1.5'); assert.match(release.url, /pilot-linux-amd64\.tar\.gz$/); assert.match(release.sha256, /^[a-f0-9]{64}$/); });