@pharaoh-so/mcp 0.3.46 → 0.3.50
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/.mcp.json +3 -1
- package/CHANGELOG.md +29 -0
- package/README.md +29 -2
- package/dist/backend.d.ts +6 -2
- package/dist/backend.js +43 -4
- package/dist/credentials.d.ts +9 -0
- package/dist/credentials.js +15 -6
- package/dist/index.js +17 -2
- package/dist/inspect.js +2 -0
- package/dist/install-skills.d.ts +63 -10
- package/dist/install-skills.js +162 -59
- package/dist/proxy-server.d.ts +4 -0
- package/dist/proxy-server.js +45 -3
- package/dist/setup.d.ts +13 -4
- package/dist/setup.js +19 -19
- package/dist/trace-resource.d.ts +3 -0
- package/dist/trace-resource.js +41 -0
- package/dist/version.d.ts +30 -0
- package/dist/version.js +66 -4
- package/inspect-tools.json +35 -10
- package/package.json +72 -69
- package/skills/pharaoh/SKILL.md +1 -1
- package/trace-app.html +296 -0
package/.mcp.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ All notable changes to `@pharaoh-so/mcp` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.50] - 2026-10-20
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Tighten Trace card spacing without shrinking text or hiding evidence.
|
|
12
|
+
- Stop requesting a duplicate ASCII panel when a usable Trace view is attached. Keep the complete insight available to the agent and preserve text-only output.
|
|
13
|
+
|
|
14
|
+
## [0.3.49] - 2026-10-20
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Publish from the standalone public source repository with npm provenance, after the reviewed source passes its required checks.
|
|
18
|
+
- Include the test runtime needed to validate the package independently.
|
|
19
|
+
|
|
20
|
+
## [0.3.48] - 2026-10-20
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Compact Trace views for supporting MCP App clients, with expandable evidence, yellow bars and green success markers. Existing text results remain available.
|
|
24
|
+
- The same static Trace resource before and after sign-in. Resource reads do not start device authentication, and view data comes only from authorized tool results.
|
|
25
|
+
|
|
26
|
+
## [Unreleased]
|
|
27
|
+
|
|
28
|
+
Security review of 2026-10-17 (findings SC-2, SC-6, SC-7, SC-8, SC-9). See README § Security for what the installer writes.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- The interactive setup allow-lists Pharaoh's six read-only tools and `pharaoh_account` by full name in `~/.claude/settings.json`, only when that file already exists, and says so. It replaces the blanket `mcp__pharaoh` rule earlier versions wrote on every start, which also auto-approved account deletion and `pharaoh_admin`. Proxy starts no longer touch permissions.
|
|
32
|
+
- Slash command files in `~/.claude/commands/` are written only when absent or Pharaoh's own (front-matter marker `# managed-by: pharaoh-mcp`, or the header and `$ARGUMENTS` footer of an earlier install). A user's file with the same name is kept and named in the output; symlinks are never followed.
|
|
33
|
+
- `~/.pharaoh` is created `0700` and set to `0700` on every start, whichever path creates it first.
|
|
34
|
+
- Every registration (`claude mcp add`, OpenClaw, the shipped `.mcp.json`) runs `npx @pharaoh-so/mcp@<the installed version>`. Upgrading is `npx @pharaoh-so/mcp@latest`; the interactive setup prints a hint when the registry has a newer version.
|
|
35
|
+
- `package.json` `repository` names the building repository. The release workflow requests npm provenance when the source repository is public; private-source releases publish without it.
|
|
36
|
+
|
|
8
37
|
## [0.1.5] - 2026-03-24
|
|
9
38
|
|
|
10
39
|
### Added
|
package/README.md
CHANGED
|
@@ -129,10 +129,10 @@ npx @pharaoh-so/mcp --logout
|
|
|
129
129
|
|
|
130
130
|
### Custom Server
|
|
131
131
|
|
|
132
|
-
For self-hosted Pharaoh instances or development, register manually:
|
|
132
|
+
For self-hosted Pharaoh instances or development, register manually, pinned to a version (`npx @pharaoh-so/mcp --version` prints the one you have):
|
|
133
133
|
|
|
134
134
|
```bash
|
|
135
|
-
claude mcp add --scope user pharaoh -- npx @pharaoh-so/mcp --server https://your-pharaoh-instance.com
|
|
135
|
+
claude mcp add --scope user pharaoh -- npx @pharaoh-so/mcp@<version> --server https://your-pharaoh-instance.com
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
### Environment Variables
|
|
@@ -155,6 +155,33 @@ claude mcp add --scope user pharaoh -- npx @pharaoh-so/mcp --server https://your
|
|
|
155
155
|
- The proxy never reads or sends your local files. Pharaoh reads your code from GitHub through its GitHub App, maps it into a knowledge graph of structural metadata (function names, file paths, dependency relationships), and deletes the clone when mapping ends.
|
|
156
156
|
- Tokens expire after 7 days and are refreshed automatically
|
|
157
157
|
- Only use trusted server URLs — the proxy sends your auth token to the configured server
|
|
158
|
+
- No telemetry. The proxy's only network calls are the device flow, token refresh, the MCP connection, and one registry lookup during interactive setup (below).
|
|
159
|
+
|
|
160
|
+
### What the installer writes on your machine
|
|
161
|
+
|
|
162
|
+
`npx @pharaoh-so/mcp` in a terminal is the interactive setup. Every later proxy start (Claude Code launching the server) repeats the plugin, skill and command steps so a new version takes effect, and nothing else.
|
|
163
|
+
|
|
164
|
+
| Path | Mode | When | What |
|
|
165
|
+
|------|------|------|------|
|
|
166
|
+
| `~/.pharaoh/` | `0700`, set again on every start | Always | Credentials and the Claude Code plugin marketplace |
|
|
167
|
+
| `~/.pharaoh/credentials.json` | `0600` | After sign-in | Access token, refresh token, expiry |
|
|
168
|
+
| `~/.pharaoh/claude-marketplace/` | — | `~/.claude/` exists | The one-plugin marketplace Claude Code installs as `pharaoh@pharaoh`: the skills and the Grep routing hook. Rewritten when the package version changes |
|
|
169
|
+
| `~/.claude/commands/plan.md`, `review.md`, `sessions.md` | — | `~/.claude/` exists | Slash commands. A file is written only when it is absent or is Pharaoh's: it carries the front-matter comment `# managed-by: pharaoh-mcp`, or has the exact header and `$ARGUMENTS` footer of a Pharaoh command from 0.3.46 or earlier. Any other file with that name is yours: it is kept, a symlink is never followed, and the setup output names it. Rename your file to keep both |
|
|
170
|
+
| `~/.claude.json` | — | Interactive setup | Removes earlier `pharaoh` entries, then `claude mcp add --scope user pharaoh -- npx @pharaoh-so/mcp@<version>` |
|
|
171
|
+
| `~/.claude/settings.json` | — | Interactive setup, and only if the file already exists | Adds to `permissions.allow`, each by full name: `mcp__pharaoh__get_codebase_map`, `mcp__pharaoh__get_module_context`, `mcp__pharaoh__search_functions`, `mcp__pharaoh__get_blast_radius`, `mcp__pharaoh__get_unused_code`, `mcp__pharaoh__pharaoh_recon`, `mcp__pharaoh__pharaoh_account`. The setup prints a line saying what it did. The proxy never creates this file and never touches it on a start |
|
|
172
|
+
| `~/.openclaw/skills/`, `~/.openclaw/openclaw.json` | — | `~/.openclaw/` exists | Skills prefixed `pharaoh-`, and an `mcpServers.pharaoh` entry running `npx @pharaoh-so/mcp@<version>`. An entry that is not an `npx @pharaoh-so/mcp` command is left alone |
|
|
173
|
+
|
|
174
|
+
Versions 0.3.46 and earlier wrote one rule, `mcp__pharaoh`, on every proxy start. That prefix auto-approved every Pharaoh tool, including account deletion and `pharaoh_admin`, so a prompt injection could run them without a human prompt. The next interactive setup replaces that rule with the list above; account deletion, `pharaoh_admin` and any tool not listed prompt before they run. To take the blanket rule out by hand, delete `mcp__pharaoh` from `permissions.allow`.
|
|
175
|
+
|
|
176
|
+
### Pinned version and upgrades
|
|
177
|
+
|
|
178
|
+
Every registration the setup writes runs the exact version that wrote it (`npx @pharaoh-so/mcp@<version>`). A new release on npm changes nothing on your machine until you upgrade:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npx @pharaoh-so/mcp@latest
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
This signs you in again, rewrites the registration with the new version and refreshes the plugin. The interactive setup asks the npm registry for the newest version and prints a one-line hint when there is one; proxy starts never check.
|
|
158
185
|
|
|
159
186
|
### Reporting Vulnerabilities
|
|
160
187
|
|
package/dist/backend.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* resolves each tool call's handler at request-arrival time, so in-flight
|
|
16
16
|
* calls complete against whichever backend started them.
|
|
17
17
|
*/
|
|
18
|
-
import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
18
|
+
import type { CallToolResult, ClientCapabilities, Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
19
19
|
import type { DeviceFlowGateway } from "./gateway.js";
|
|
20
20
|
/** The contract ProxyServer uses to delegate tool handling. */
|
|
21
21
|
export interface Backend {
|
|
@@ -32,6 +32,8 @@ export interface Backend {
|
|
|
32
32
|
* act on. Backends without a supervisor loop (AuthRequiredBackend) omit this.
|
|
33
33
|
*/
|
|
34
34
|
waitForExit?(): Promise<void>;
|
|
35
|
+
/** Return an unstarted replacement only if the negotiated UI support changed. */
|
|
36
|
+
withClientCapabilities?(capabilities: ClientCapabilities): Backend;
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
37
39
|
* Pre-auth backend. Every tool call triggers a device-flow state step via the
|
|
@@ -75,7 +77,9 @@ export declare class SseProxyBackend implements Backend {
|
|
|
75
77
|
private transport;
|
|
76
78
|
private stopped;
|
|
77
79
|
private reconnectPromise;
|
|
78
|
-
|
|
80
|
+
private readonly clientCapabilities;
|
|
81
|
+
constructor(sseUrl: string, token: string, capabilities?: ClientCapabilities);
|
|
82
|
+
withClientCapabilities(capabilities: ClientCapabilities): Backend;
|
|
79
83
|
start(): Promise<void>;
|
|
80
84
|
/**
|
|
81
85
|
* Expose the long-running reconnect loop so a supervisor can await it.
|
package/dist/backend.js
CHANGED
|
@@ -19,6 +19,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
19
19
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
20
20
|
import { loadBundledCatalog } from "./catalog.js";
|
|
21
21
|
import { classifySSEError, TenantSuspendedError, TokenExpiredError } from "./proxy.js";
|
|
22
|
+
import { TRACE_MIME_TYPE } from "./trace-resource.js";
|
|
22
23
|
import { getPackageVersion } from "./version.js";
|
|
23
24
|
/**
|
|
24
25
|
* Pre-auth backend. Every tool call triggers a device-flow state step via the
|
|
@@ -143,9 +144,23 @@ export class SseProxyBackend {
|
|
|
143
144
|
transport = null;
|
|
144
145
|
stopped = false;
|
|
145
146
|
reconnectPromise = null;
|
|
146
|
-
|
|
147
|
+
clientCapabilities;
|
|
148
|
+
constructor(sseUrl, token, capabilities = {}) {
|
|
147
149
|
this.sseUrl = sseUrl;
|
|
148
150
|
this.token = token;
|
|
151
|
+
// The proxy can deliver only this static UI extension. Advertising other
|
|
152
|
+
// capabilities (sampling, elicitation, etc.) would promise unsupported RPCs.
|
|
153
|
+
const ui = capabilities.extensions?.["io.modelcontextprotocol/ui"];
|
|
154
|
+
this.clientCapabilities =
|
|
155
|
+
Array.isArray(ui?.mimeTypes) && ui.mimeTypes.includes(TRACE_MIME_TYPE)
|
|
156
|
+
? { extensions: { "io.modelcontextprotocol/ui": { mimeTypes: [TRACE_MIME_TYPE] } } }
|
|
157
|
+
: {};
|
|
158
|
+
}
|
|
159
|
+
withClientCapabilities(capabilities) {
|
|
160
|
+
const next = new SseProxyBackend(this.sseUrl, this.token, capabilities);
|
|
161
|
+
return JSON.stringify(next.clientCapabilities) === JSON.stringify(this.clientCapabilities)
|
|
162
|
+
? this
|
|
163
|
+
: next;
|
|
149
164
|
}
|
|
150
165
|
async start() {
|
|
151
166
|
if (this.client)
|
|
@@ -169,9 +184,29 @@ export class SseProxyBackend {
|
|
|
169
184
|
return this.reconnectPromise ?? Promise.resolve();
|
|
170
185
|
}
|
|
171
186
|
async connectOnce() {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
187
|
+
const transport = createSSETransport(this.sseUrl, this.token);
|
|
188
|
+
const client = new Client({ name: "pharaoh-proxy", version: getPackageVersion() }, { capabilities: this.clientCapabilities });
|
|
189
|
+
this.transport = transport;
|
|
190
|
+
this.client = client;
|
|
191
|
+
try {
|
|
192
|
+
await client.connect(transport);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
// A failed warm capability handshake must not leave EventSource's
|
|
196
|
+
// automatic retries alive beside the previous backend.
|
|
197
|
+
await client.close().catch(() => {
|
|
198
|
+
process.stderr.write("Pharaoh: failed SSE connection cleanup\n");
|
|
199
|
+
});
|
|
200
|
+
if (this.transport === transport)
|
|
201
|
+
this.transport = null;
|
|
202
|
+
if (this.client === client)
|
|
203
|
+
this.client = null;
|
|
204
|
+
// Initial and negotiated connects need the same typed auth recovery
|
|
205
|
+
// as reconnects. Generic failures still surface to the caller.
|
|
206
|
+
if (error instanceof Error)
|
|
207
|
+
classifySSEError(error);
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
175
210
|
}
|
|
176
211
|
/** Wait for the current transport to close, then decide what to do. */
|
|
177
212
|
waitForClose() {
|
|
@@ -204,6 +239,8 @@ export class SseProxyBackend {
|
|
|
204
239
|
const delay = BACKOFF_MS[attempt - 1];
|
|
205
240
|
process.stderr.write(`Pharaoh: reconnecting in ${delay / 1000}s (attempt ${attempt}/${BACKOFF_MS.length})...\n`);
|
|
206
241
|
await sleep(delay);
|
|
242
|
+
if (this.stopped)
|
|
243
|
+
return;
|
|
207
244
|
await this.connectOnce();
|
|
208
245
|
process.stderr.write("Pharaoh: reconnected\n");
|
|
209
246
|
}
|
|
@@ -213,6 +250,8 @@ export class SseProxyBackend {
|
|
|
213
250
|
while (!this.stopped) {
|
|
214
251
|
try {
|
|
215
252
|
await this.waitForClose();
|
|
253
|
+
if (this.stopped)
|
|
254
|
+
return;
|
|
216
255
|
attempt++;
|
|
217
256
|
if (attempt > BACKOFF_MS.length) {
|
|
218
257
|
process.stderr.write("Pharaoh: max reconnect attempts reached — exiting\n");
|
package/dist/credentials.d.ts
CHANGED
|
@@ -46,6 +46,15 @@ export declare function writeCredentials(creds: Credentials, path?: string): voi
|
|
|
46
46
|
* @throws If the lock stays held for LOCK_WAIT_MS, or a stale lock can't be removed.
|
|
47
47
|
*/
|
|
48
48
|
export declare function acquireCredentialsLock(path?: string): Promise<() => void>;
|
|
49
|
+
/**
|
|
50
|
+
* Create `~/.pharaoh` with mode 0700 and set that mode again if it already
|
|
51
|
+
* exists. The one place the directory is created, whichever path gets there
|
|
52
|
+
* first (credentials or the Claude Code plugin marketplace), so it is never
|
|
53
|
+
* left world-readable (security review SC-8).
|
|
54
|
+
*
|
|
55
|
+
* @param dir - The directory (defaults to `~/.pharaoh`).
|
|
56
|
+
*/
|
|
57
|
+
export declare function ensurePharaohHome(dir?: string): void;
|
|
49
58
|
/** Delete the credential file. No-op if it doesn't exist. */
|
|
50
59
|
export declare function deleteCredentials(path?: string): void;
|
|
51
60
|
/**
|
package/dist/credentials.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* refreshes take a lock (acquireCredentialsLock) and writes are atomic.
|
|
7
7
|
*/
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
|
-
import {
|
|
9
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
12
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
@@ -147,12 +147,21 @@ function lockAgeMs(lockPath) {
|
|
|
147
147
|
return 0; // released between our create attempt and this stat — retry immediately
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Create `~/.pharaoh` with mode 0700 and set that mode again if it already
|
|
152
|
+
* exists. The one place the directory is created, whichever path gets there
|
|
153
|
+
* first (credentials or the Claude Code plugin marketplace), so it is never
|
|
154
|
+
* left world-readable (security review SC-8).
|
|
155
|
+
*
|
|
156
|
+
* @param dir - The directory (defaults to `~/.pharaoh`).
|
|
157
|
+
*/
|
|
158
|
+
export function ensurePharaohHome(dir = join(homedir(), ".pharaoh")) {
|
|
159
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
160
|
+
chmodSync(dir, 0o700);
|
|
161
|
+
}
|
|
162
|
+
/** Create the directory holding a credentials file (0700). */
|
|
151
163
|
function ensureDir(path) {
|
|
152
|
-
|
|
153
|
-
if (!existsSync(dir)) {
|
|
154
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
155
|
-
}
|
|
164
|
+
ensurePharaohHome(dirname(path));
|
|
156
165
|
}
|
|
157
166
|
/** Delete the credential file. No-op if it doesn't exist. */
|
|
158
167
|
export function deleteCredentials(path = CREDENTIALS_PATH) {
|
package/dist/index.js
CHANGED
|
@@ -17,11 +17,12 @@ import { AuthRequiredBackend, createSseBackend } from "./backend.js";
|
|
|
17
17
|
import { deleteCredentials, readCredentials, writeCredentials, } from "./credentials.js";
|
|
18
18
|
import { createGateway } from "./gateway.js";
|
|
19
19
|
import { formatTtl, parseArgs, printLines, printUsage, resolveSseUrl, tokenToCredentials, } from "./helpers.js";
|
|
20
|
-
import { runInstallSkills } from "./install-skills.js";
|
|
20
|
+
import { allowReadTools, describeAllowReadTools, formatKeptCommands, runInstallSkills, } from "./install-skills.js";
|
|
21
21
|
import { handleRefreshFailure, tryMidSessionRefresh } from "./lifecycle.js";
|
|
22
22
|
import { TenantSuspendedError, TokenExpiredError } from "./proxy.js";
|
|
23
23
|
import { startInAuthRequiredMode, startInSseMode } from "./proxy-server.js";
|
|
24
24
|
import { loadAndRefresh } from "./refresh.js";
|
|
25
|
+
import { fetchLatestVersion, getPackageVersion, isNewerVersion, UPGRADE_COMMAND, } from "./version.js";
|
|
25
26
|
const BANNER = [
|
|
26
27
|
"",
|
|
27
28
|
" ___ _ _ _ ___ _ ___ _ _ ",
|
|
@@ -90,8 +91,22 @@ async function main() {
|
|
|
90
91
|
process.stderr.write(" Setting up...");
|
|
91
92
|
const { runSetup } = await import("./setup.js");
|
|
92
93
|
runSetup({ silent: true });
|
|
93
|
-
runInstallSkills(undefined, { silent: true });
|
|
94
|
+
const { keptCommands } = runInstallSkills(undefined, { silent: true });
|
|
94
95
|
process.stderr.write(" done.\n");
|
|
96
|
+
// Two things the user must hear about: their settings.json (the only
|
|
97
|
+
// place this setup edits permissions, never a silent start) and any
|
|
98
|
+
// command file of theirs that was left alone.
|
|
99
|
+
printLines(describeAllowReadTools(allowReadTools()));
|
|
100
|
+
const kept = formatKeptCommands(keptCommands);
|
|
101
|
+
if (kept)
|
|
102
|
+
printLines(kept);
|
|
103
|
+
// The registration is pinned to this version; upgrading is a deliberate
|
|
104
|
+
// re-run. Interactive only, and quiet when the registry can't be reached.
|
|
105
|
+
const latest = await fetchLatestVersion();
|
|
106
|
+
const running = getPackageVersion();
|
|
107
|
+
if (latest && isNewerVersion(latest, running)) {
|
|
108
|
+
printLines(`Pharaoh: ${latest} is available (this is ${running}). Upgrade: ${UPGRADE_COMMAND}`);
|
|
109
|
+
}
|
|
95
110
|
printLines("", "Pharaoh is ready. Try these in a new Claude Code conversation:", "", ' "What modules does this codebase have?"', ' "Search for functions related to authentication"', ' "What breaks if I change this file?"', "");
|
|
96
111
|
process.exit(0);
|
|
97
112
|
}
|
package/dist/inspect.js
CHANGED
|
@@ -11,6 +11,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
12
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
13
13
|
import { loadBundledCatalog } from "./catalog.js";
|
|
14
|
+
import { registerTraceResource } from "./trace-resource.js";
|
|
14
15
|
import { getPackageVersion } from "./version.js";
|
|
15
16
|
const INSPECT_MSG = "This is Pharaoh's inspection server for directory listings. " +
|
|
16
17
|
"Connect to https://mcp.pharaoh.so for actual usage.";
|
|
@@ -29,5 +30,6 @@ export async function runInspect() {
|
|
|
29
30
|
server.setRequestHandler(CallToolRequestSchema, async () => ({
|
|
30
31
|
content: [{ type: "text", text: INSPECT_MSG }],
|
|
31
32
|
}));
|
|
33
|
+
registerTraceResource(server);
|
|
32
34
|
await server.connect(new StdioServerTransport());
|
|
33
35
|
}
|
package/dist/install-skills.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ClaudeCli } from "./setup.js";
|
|
1
2
|
/**
|
|
2
3
|
* Detect whether OpenClaw is installed by checking for ~/.openclaw/.
|
|
3
4
|
*
|
|
@@ -5,8 +6,6 @@
|
|
|
5
6
|
* @returns True if ~/.openclaw/ exists.
|
|
6
7
|
*/
|
|
7
8
|
export declare function detectOpenClaw(home?: string): boolean;
|
|
8
|
-
/** Runs one `claude` CLI command; true on success. */
|
|
9
|
-
type ClaudeCli = (args: string[]) => boolean;
|
|
10
9
|
/**
|
|
11
10
|
* Uninstall Pharaoh's Claude Code plugin: through the CLI, then the local
|
|
12
11
|
* marketplace directory and any pre-marketplace install.
|
|
@@ -15,6 +14,22 @@ type ClaudeCli = (args: string[]) => boolean;
|
|
|
15
14
|
* @param claude - `claude` CLI runner (defaults to the real CLI for `home`).
|
|
16
15
|
*/
|
|
17
16
|
export declare function uninstallClaudePlugin(home?: string, claude?: ClaudeCli): void;
|
|
17
|
+
/**
|
|
18
|
+
* Marker the installer writes into the front-matter of every command file it
|
|
19
|
+
* installs, as a YAML comment Claude Code ignores. A file in
|
|
20
|
+
* `~/.claude/commands/` is rewritten only when it is Pharaoh's: it carries
|
|
21
|
+
* this marker, or the exact header and `$ARGUMENTS` footer every installer
|
|
22
|
+
* before the marker shipped. Any other file with the same name is the user's
|
|
23
|
+
* and is left alone (security review SC-7).
|
|
24
|
+
*/
|
|
25
|
+
export declare const PHARAOH_COMMAND_MARKER = "# managed-by: pharaoh-mcp";
|
|
26
|
+
/**
|
|
27
|
+
* The one-line notice for command files the installer left alone, or null
|
|
28
|
+
* when there were none.
|
|
29
|
+
*
|
|
30
|
+
* @param kept - File names from `runInstallSkills().keptCommands`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatKeptCommands(kept: readonly string[]): string | null;
|
|
18
33
|
/**
|
|
19
34
|
* Install bundled skills to `~/.openclaw/skills/` with the same `pharaoh-`
|
|
20
35
|
* prefix that Claude Code installs use. Keeps naming consistent across both
|
|
@@ -28,26 +43,64 @@ export declare function uninstallClaudePlugin(home?: string, claude?: ClaudeCli)
|
|
|
28
43
|
*/
|
|
29
44
|
export declare function installSkills(home?: string): number;
|
|
30
45
|
/**
|
|
31
|
-
* Read ~/.openclaw/openclaw.json,
|
|
32
|
-
* and write it back. Creates the file
|
|
33
|
-
*
|
|
46
|
+
* Read ~/.openclaw/openclaw.json, set the Pharaoh MCP server under `mcpServers`
|
|
47
|
+
* to `npx @pharaoh-so/mcp@<this version>`, and write it back. Creates the file
|
|
48
|
+
* if it does not exist. An existing `pharaoh` entry is re-pinned when it is an
|
|
49
|
+
* `npx @pharaoh-so/mcp` command (extra arguments kept) and left alone when it
|
|
50
|
+
* is anything else.
|
|
34
51
|
*
|
|
35
52
|
* @param home - Home directory override (defaults to os.homedir()).
|
|
36
|
-
* @returns True if the config was written
|
|
53
|
+
* @returns True if the config was written, false if it was already current or is not ours.
|
|
37
54
|
*/
|
|
38
55
|
export declare function mergeOpenClawConfig(home?: string): boolean;
|
|
39
56
|
/**
|
|
40
|
-
*
|
|
57
|
+
* Rules the interactive setup adds to `permissions.allow` in
|
|
58
|
+
* ~/.claude/settings.json: the six read-only tools and `pharaoh_account`,
|
|
59
|
+
* each by full name. Never a prefix rule — `mcp__pharaoh` also auto-approves
|
|
60
|
+
* account deletion and `pharaoh_admin`, so a prompt injection could run them
|
|
61
|
+
* with no human in the loop (security review SC-6). Those tools keep prompting.
|
|
62
|
+
*/
|
|
63
|
+
export declare const PHARAOH_ALLOW_RULES: readonly string[];
|
|
64
|
+
/** What `allowReadTools` did to ~/.claude/settings.json. */
|
|
65
|
+
export type AllowReadToolsResult = "added" | "migrated" | "current" | "no-settings" | "error";
|
|
66
|
+
/**
|
|
67
|
+
* Add `PHARAOH_ALLOW_RULES` to `permissions.allow` in ~/.claude/settings.json,
|
|
68
|
+
* replacing the blanket `mcp__pharaoh` rule an earlier version wrote. Runs
|
|
69
|
+
* only from the interactive setup, which prints what happened; never on a
|
|
70
|
+
* proxy start. Never creates the file: with no settings.json the tools
|
|
71
|
+
* simply prompt on first use.
|
|
72
|
+
*
|
|
73
|
+
* @param home - Home directory override (defaults to os.homedir()).
|
|
74
|
+
* @returns "added" or "migrated" when the file was written, "current" when
|
|
75
|
+
* nothing was missing, "no-settings" when there is no file, "error" when
|
|
76
|
+
* the file is not valid JSON (left untouched).
|
|
77
|
+
*/
|
|
78
|
+
export declare function allowReadTools(home?: string): AllowReadToolsResult;
|
|
79
|
+
/**
|
|
80
|
+
* The line the interactive setup prints for an `allowReadTools` result, so
|
|
81
|
+
* the user always learns what was done to their settings.
|
|
82
|
+
*
|
|
83
|
+
* @param result - What `allowReadTools` returned.
|
|
84
|
+
*/
|
|
85
|
+
export declare function describeAllowReadTools(result: AllowReadToolsResult): string;
|
|
86
|
+
/** What `runInstallSkills` wants the caller to tell the user. */
|
|
87
|
+
export interface InstallResult {
|
|
88
|
+
/** Files in ~/.claude/commands/ left as they were because they are not Pharaoh's. */
|
|
89
|
+
keptCommands: string[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Main entry point for --install-skills, also run on every proxy start.
|
|
41
93
|
*
|
|
42
94
|
* Detection priority: Claude Code > OpenClaw > manual instructions.
|
|
43
|
-
* Installs to all detected platforms (a user may use both).
|
|
95
|
+
* Installs to all detected platforms (a user may use both). Never touches
|
|
96
|
+
* Claude Code permissions: that is `allowReadTools`, interactive setup only.
|
|
44
97
|
*
|
|
45
98
|
* @param home - Home directory override (defaults to os.homedir()).
|
|
46
99
|
* @param options.silent - Print nothing (the interactive setup prints its own summary).
|
|
47
100
|
* @param options.claude - `claude` CLI runner (defaults to the real CLI for `home`).
|
|
101
|
+
* @returns What was kept, for the caller's own summary.
|
|
48
102
|
*/
|
|
49
103
|
export declare function runInstallSkills(home?: string, options?: {
|
|
50
104
|
silent?: boolean;
|
|
51
105
|
claude?: ClaudeCli;
|
|
52
|
-
}):
|
|
53
|
-
export {};
|
|
106
|
+
}): InstallResult;
|