@pharaoh-so/mcp 0.2.2 → 0.2.4

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.
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "pharaoh",
3
+ "version": "0.2.0",
4
+ "description": "Codebase knowledge graph with 23 development workflow skills. Query architecture, dependencies, blast radius, and more instead of reading files one at a time.",
5
+ "author": {
6
+ "name": "Pharaoh",
7
+ "email": "hello@pharaoh.so",
8
+ "url": "https://pharaoh.so"
9
+ },
10
+ "homepage": "https://pharaoh.so",
11
+ "repository": "https://github.com/Pharaoh-so/pharaoh-mcp",
12
+ "license": "MIT",
13
+ "keywords": [
14
+ "code-intelligence",
15
+ "architecture",
16
+ "mcp",
17
+ "knowledge-graph",
18
+ "dependencies",
19
+ "blast-radius",
20
+ "dead-code",
21
+ "code-review",
22
+ "refactoring",
23
+ "test-coverage"
24
+ ]
25
+ }
package/.mcp.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "pharaoh": {
4
+ "command": "npx",
5
+ "args": ["@pharaoh-so/mcp"]
6
+ }
7
+ }
8
+ }
@@ -25,8 +25,8 @@ export declare function mergeOpenClawConfig(home?: string): boolean;
25
25
  /**
26
26
  * Main entry point for --install-skills.
27
27
  *
28
- * Detects OpenClaw, copies skills, merges config, and prints a summary.
29
- * If OpenClaw is not detected, prints manual installation instructions instead.
28
+ * Detection priority: Claude Code > OpenClaw > manual instructions.
29
+ * Installs to all detected platforms (a user may use both).
30
30
  *
31
31
  * @param home - Home directory override (defaults to os.homedir()).
32
32
  */
@@ -1,17 +1,29 @@
1
1
  /**
2
2
  * --install-skills implementation for the Pharaoh MCP proxy CLI.
3
3
  *
4
- * Copies bundled skill directories to ~/.openclaw/skills/ and merges
5
- * the Pharaoh MCP server entry into ~/.openclaw/openclaw.json.
6
- * If OpenClaw is not detected, prints manual installation instructions.
4
+ * Installs bundled skills as a Claude Code plugin (primary) or to
5
+ * OpenClaw's ~/.openclaw/skills/ directory (fallback). Also merges
6
+ * the Pharaoh MCP server config into the target platform.
7
7
  */
8
- import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
8
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
9
9
  import { homedir } from "node:os";
10
10
  import { dirname, join } from "node:path";
11
11
  import { fileURLToPath } from "node:url";
12
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
- /** Path to the bundled skills directory (one level up from dist/). */
14
- const BUNDLED_SKILLS_DIR = join(__dirname, "..", "skills");
13
+ /** Path to the bundled package root (one level up from dist/). */
14
+ const PKG_ROOT = join(__dirname, "..");
15
+ /** Path to the bundled skills directory. */
16
+ const BUNDLED_SKILLS_DIR = join(PKG_ROOT, "skills");
17
+ // ── Detection ───────────────────────────────────────────────────────
18
+ /**
19
+ * Detect whether Claude Code is installed by checking for ~/.claude/.
20
+ *
21
+ * @param home - Home directory override (defaults to os.homedir()).
22
+ * @returns True if ~/.claude/ exists.
23
+ */
24
+ function detectClaudeCode(home = homedir()) {
25
+ return existsSync(join(home, ".claude"));
26
+ }
15
27
  /**
16
28
  * Detect whether OpenClaw is installed by checking for ~/.openclaw/.
17
29
  *
@@ -21,6 +33,78 @@ const BUNDLED_SKILLS_DIR = join(__dirname, "..", "skills");
21
33
  export function detectOpenClaw(home = homedir()) {
22
34
  return existsSync(join(home, ".openclaw"));
23
35
  }
36
+ // ── Claude Code installer ───────────────────────────────────────────
37
+ /**
38
+ * Install Pharaoh as a Claude Code plugin by copying the full plugin
39
+ * structure (`.claude-plugin/`, `skills/`, `.mcp.json`) to a persistent
40
+ * directory under `~/.claude/plugins/data/pharaoh/`.
41
+ *
42
+ * Claude Code auto-discovers skills from the `skills/` subdirectory
43
+ * when the plugin manifest (`.claude-plugin/plugin.json`) is present.
44
+ *
45
+ * @param home - Home directory override (defaults to os.homedir()).
46
+ * @returns Number of skill directories installed, or -1 on failure.
47
+ */
48
+ function installClaudeCodePlugin(home = homedir()) {
49
+ const pluginDir = join(home, ".claude", "plugins", "data", "pharaoh");
50
+ mkdirSync(pluginDir, { recursive: true });
51
+ // Copy .claude-plugin/ manifest
52
+ const manifestSrc = join(PKG_ROOT, ".claude-plugin");
53
+ const manifestDst = join(pluginDir, ".claude-plugin");
54
+ if (existsSync(manifestSrc)) {
55
+ cpSync(manifestSrc, manifestDst, { recursive: true, force: true });
56
+ }
57
+ else {
58
+ process.stderr.write("Pharaoh: .claude-plugin/ manifest not found in package — cannot install.\n");
59
+ return -1;
60
+ }
61
+ // Copy skills/
62
+ if (existsSync(BUNDLED_SKILLS_DIR)) {
63
+ cpSync(BUNDLED_SKILLS_DIR, join(pluginDir, "skills"), { recursive: true, force: true });
64
+ }
65
+ // Copy .mcp.json
66
+ const mcpSrc = join(PKG_ROOT, ".mcp.json");
67
+ if (existsSync(mcpSrc)) {
68
+ cpSync(mcpSrc, join(pluginDir, ".mcp.json"), { force: true });
69
+ }
70
+ // Count installed skills
71
+ const entries = readdirSync(BUNDLED_SKILLS_DIR, { withFileTypes: true });
72
+ return entries.filter((e) => e.isDirectory()).length;
73
+ }
74
+ /**
75
+ * Register Pharaoh in Claude Code's installed_plugins.json.
76
+ * Does NOT overwrite an existing entry.
77
+ *
78
+ * @param home - Home directory override (defaults to os.homedir()).
79
+ * @returns True if the entry was added, false if already present.
80
+ */
81
+ function registerClaudeCodePlugin(home = homedir()) {
82
+ const registryPath = join(home, ".claude", "plugins", "installed_plugins.json");
83
+ const pluginKey = "pharaoh@pharaoh-so";
84
+ let registry = {};
85
+ if (existsSync(registryPath)) {
86
+ try {
87
+ registry = JSON.parse(readFileSync(registryPath, "utf-8"));
88
+ }
89
+ catch {
90
+ process.stderr.write("Pharaoh: installed_plugins.json exists but is not valid JSON — skipping registration.\n");
91
+ return false;
92
+ }
93
+ }
94
+ if (registry[pluginKey]) {
95
+ return false;
96
+ }
97
+ registry[pluginKey] = {
98
+ name: "pharaoh",
99
+ version: "0.2.0",
100
+ source: "npm:@pharaoh-so/mcp",
101
+ pluginRoot: join(home, ".claude", "plugins", "data", "pharaoh"),
102
+ installedAt: new Date().toISOString(),
103
+ };
104
+ writeFileSync(registryPath, JSON.stringify(registry, null, "\t"));
105
+ return true;
106
+ }
107
+ // ── OpenClaw installer ──────────────────────────────────────────────
24
108
  /**
25
109
  * Copy all bundled skill directories to ~/.openclaw/skills/.
26
110
  * Overwrites existing skill dirs on reinstall (cpSync recursive + force).
@@ -59,7 +143,6 @@ export function mergeOpenClawConfig(home = homedir()) {
59
143
  config = JSON.parse(raw);
60
144
  }
61
145
  catch {
62
- // Corrupted JSON — refuse to overwrite to avoid losing existing config
63
146
  process.stderr.write([
64
147
  "Pharaoh: ~/.openclaw/openclaw.json exists but is not valid JSON.",
65
148
  "Fix or delete it manually, then re-run --install-skills.",
@@ -71,7 +154,6 @@ export function mergeOpenClawConfig(home = homedir()) {
71
154
  if (!config.mcpServers) {
72
155
  config.mcpServers = {};
73
156
  }
74
- // Don't overwrite an existing pharaoh entry
75
157
  if (config.mcpServers.pharaoh) {
76
158
  return false;
77
159
  }
@@ -82,40 +164,65 @@ export function mergeOpenClawConfig(home = homedir()) {
82
164
  writeFileSync(configPath, JSON.stringify(config, null, "\t"));
83
165
  return true;
84
166
  }
167
+ // ── Main entry point ────────────────────────────────────────────────
85
168
  /**
86
169
  * Main entry point for --install-skills.
87
170
  *
88
- * Detects OpenClaw, copies skills, merges config, and prints a summary.
89
- * If OpenClaw is not detected, prints manual installation instructions instead.
171
+ * Detection priority: Claude Code > OpenClaw > manual instructions.
172
+ * Installs to all detected platforms (a user may use both).
90
173
  *
91
174
  * @param home - Home directory override (defaults to os.homedir()).
92
175
  */
93
176
  export function runInstallSkills(home = homedir()) {
177
+ const hasClaudeCode = detectClaudeCode(home);
94
178
  const hasOpenClaw = detectOpenClaw(home);
95
- if (!hasOpenClaw) {
179
+ let installed = false;
180
+ // ── Claude Code ──
181
+ if (hasClaudeCode) {
182
+ const count = installClaudeCodePlugin(home);
183
+ if (count >= 0) {
184
+ const registered = registerClaudeCodePlugin(home);
185
+ const regMsg = registered
186
+ ? "Plugin registered in installed_plugins.json"
187
+ : "Plugin already registered — skipped";
188
+ process.stderr.write([
189
+ `Pharaoh: installed ${count} skills as Claude Code plugin`,
190
+ ` → ~/.claude/plugins/data/pharaoh/`,
191
+ ` → ${regMsg}`,
192
+ "",
193
+ "Restart Claude Code to pick up the new skills.",
194
+ "",
195
+ ].join("\n"));
196
+ installed = true;
197
+ }
198
+ }
199
+ // ── OpenClaw ──
200
+ if (hasOpenClaw) {
201
+ const count = installSkills(home);
202
+ const configUpdated = mergeOpenClawConfig(home);
203
+ const configMsg = configUpdated
204
+ ? "Pharaoh MCP server added to ~/.openclaw/openclaw.json"
205
+ : "Pharaoh already present in ~/.openclaw/openclaw.json — skipped";
206
+ process.stderr.write([
207
+ `Pharaoh: installed ${count} skills to ~/.openclaw/skills/`,
208
+ ` → ${configMsg}`,
209
+ "",
210
+ "Restart OpenClaw to pick up the new skills.",
211
+ "",
212
+ ].join("\n"));
213
+ installed = true;
214
+ }
215
+ // ── Neither detected ──
216
+ if (!installed) {
96
217
  process.stderr.write([
97
- "Pharaoh: OpenClaw not detected (~/.openclaw/ not found).",
218
+ "Pharaoh: no supported skill platform detected.",
98
219
  "",
99
- "To install OpenClaw: https://openclaw.dev/install",
220
+ "Supported platforms:",
221
+ " • Claude Code — install from https://claude.ai/download",
222
+ " • OpenClaw — install from https://openclaw.dev/install",
100
223
  "",
101
- "Or install skills manually:",
102
- ` 1. Copy the skills/ directory from this package to ~/.openclaw/skills/`,
103
- ` 2. Add Pharaoh to ~/.openclaw/openclaw.json under mcpServers:`,
104
- ` "pharaoh": { "command": "npx", "args": ["@pharaoh-so/mcp"] }`,
224
+ "Once installed, re-run: npx @pharaoh-so/mcp --install-skills",
105
225
  "",
106
226
  ].join("\n"));
107
- return;
108
227
  }
109
- const count = installSkills(home);
110
- const configUpdated = mergeOpenClawConfig(home);
111
- const configMsg = configUpdated
112
- ? "Pharaoh MCP server added to ~/.openclaw/openclaw.json"
113
- : "Pharaoh already present in ~/.openclaw/openclaw.json — skipped";
114
- process.stderr.write([
115
- `Pharaoh: installed ${count} skills to ~/.openclaw/skills/`,
116
- configMsg,
117
- "",
118
- "Restart OpenClaw to pick up the new skills.",
119
- "",
120
- ].join("\n"));
121
228
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@pharaoh-so/mcp",
3
- "version": "0.2.2",
3
+ "mcpName": "so.pharaoh/pharaoh",
4
+ "version": "0.2.4",
4
5
  "description": "MCP proxy for Pharaoh — maps codebases into queryable knowledge graphs for AI agents. Enables Claude Code in headless environments (VPS, SSH, CI) via device flow auth.",
5
6
  "type": "module",
6
7
  "main": "dist/index.js",
@@ -11,6 +12,8 @@
11
12
  "files": [
12
13
  "dist",
13
14
  "skills",
15
+ ".claude-plugin",
16
+ ".mcp.json",
14
17
  "inspect-tools.json",
15
18
  "README.md",
16
19
  "CHANGELOG.md",