@pharaoh-so/mcp 0.2.3 → 0.2.5
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/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +15 -3
- package/dist/install-skills.js +59 -23
- package/package.json +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pharaoh",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
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
5
|
"author": {
|
|
6
6
|
"name": "Pharaoh",
|
package/dist/index.js
CHANGED
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
* - Interactive (TTY): authenticate, print setup instructions, exit.
|
|
11
11
|
* - Proxy (pipe): require pre-existing credentials, bridge stdio ↔ SSE.
|
|
12
12
|
*/
|
|
13
|
-
import { printActivationPrompt, printAuthSuccess,
|
|
13
|
+
import { pollForToken, printActivationPrompt, printAuthSuccess, requestDeviceCode, } from "./auth.js";
|
|
14
14
|
import { deleteCredentials, isExpired, readCredentials, writeCredentials } from "./credentials.js";
|
|
15
15
|
import { formatIdentity, formatTtl, parseArgs, printLines, printSetupInstructions, printUsage, resolveSseUrl, tokenToCredentials, } from "./helpers.js";
|
|
16
|
-
import {
|
|
16
|
+
import { runInstallSkills } from "./install-skills.js";
|
|
17
|
+
import { startProxy, TenantSuspendedError, TokenExpiredError } from "./proxy.js";
|
|
17
18
|
async function main() {
|
|
18
19
|
const args = process.argv.slice(2);
|
|
19
20
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -46,6 +47,8 @@ async function main() {
|
|
|
46
47
|
if (isInteractive) {
|
|
47
48
|
if (creds && !isExpired(creds)) {
|
|
48
49
|
printLines(`Pharaoh: authenticated as ${formatIdentity(creds)} — token valid for ${formatTtl(creds.expires_at)}, ${creds.repos.length} repo${creds.repos.length === 1 ? "" : "s"} connected`);
|
|
50
|
+
// Ensure skills are installed/up-to-date on every interactive run
|
|
51
|
+
runInstallSkills();
|
|
49
52
|
printSetupInstructions();
|
|
50
53
|
process.exit(0);
|
|
51
54
|
}
|
|
@@ -61,6 +64,8 @@ async function main() {
|
|
|
61
64
|
const newCreds = tokenToCredentials(token, sseUrl);
|
|
62
65
|
writeCredentials(newCreds);
|
|
63
66
|
printAuthSuccess(token.github_login ?? null, token.tenant_name ?? null, token.repos?.length ?? 0);
|
|
67
|
+
// Auto-install skills to detected platforms (Claude Code, OpenClaw)
|
|
68
|
+
runInstallSkills();
|
|
64
69
|
printSetupInstructions();
|
|
65
70
|
process.exit(0);
|
|
66
71
|
}
|
|
@@ -70,7 +75,14 @@ async function main() {
|
|
|
70
75
|
printLines("Pharaoh: no valid credentials — cannot start proxy.", "Run this command first to authenticate:", " npx @pharaoh-so/mcp", "");
|
|
71
76
|
process.exit(1);
|
|
72
77
|
}
|
|
73
|
-
// Valid credentials —
|
|
78
|
+
// Valid credentials — ensure skills are installed before starting proxy
|
|
79
|
+
// Silently installs/updates on first run or package update; fast no-op if current.
|
|
80
|
+
try {
|
|
81
|
+
runInstallSkills();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// ALLOWED-CATCH: skill install failure must not block MCP proxy startup
|
|
85
|
+
}
|
|
74
86
|
printLines(`Pharaoh: token valid for ${formatTtl(creds.expires_at)} — connecting`);
|
|
75
87
|
try {
|
|
76
88
|
await startProxy(creds.sse_url, creds.access_token);
|
package/dist/install-skills.js
CHANGED
|
@@ -14,6 +14,16 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
14
14
|
const PKG_ROOT = join(__dirname, "..");
|
|
15
15
|
/** Path to the bundled skills directory. */
|
|
16
16
|
const BUNDLED_SKILLS_DIR = join(PKG_ROOT, "skills");
|
|
17
|
+
/** Read the package version from package.json (single source of truth). */
|
|
18
|
+
function getPackageVersion() {
|
|
19
|
+
try {
|
|
20
|
+
const pkg = JSON.parse(readFileSync(join(PKG_ROOT, "package.json"), "utf-8"));
|
|
21
|
+
return pkg.version ?? "0.0.0";
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return "0.0.0";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
17
27
|
// ── Detection ───────────────────────────────────────────────────────
|
|
18
28
|
/**
|
|
19
29
|
* Detect whether Claude Code is installed by checking for ~/.claude/.
|
|
@@ -72,37 +82,61 @@ function installClaudeCodePlugin(home = homedir()) {
|
|
|
72
82
|
return entries.filter((e) => e.isDirectory()).length;
|
|
73
83
|
}
|
|
74
84
|
/**
|
|
75
|
-
* Register Pharaoh in Claude Code's installed_plugins.json.
|
|
76
|
-
*
|
|
85
|
+
* Register Pharaoh in Claude Code's installed_plugins.json (v2 format).
|
|
86
|
+
* Adds a new entry or updates the version/lastUpdated of an existing one.
|
|
87
|
+
* Preserves all other plugin entries.
|
|
77
88
|
*
|
|
78
89
|
* @param home - Home directory override (defaults to os.homedir()).
|
|
79
|
-
* @returns
|
|
90
|
+
* @returns "added" if new entry created, "updated" if version bumped, "current" if already up-to-date, "error" on failure.
|
|
80
91
|
*/
|
|
81
92
|
function registerClaudeCodePlugin(home = homedir()) {
|
|
82
93
|
const registryPath = join(home, ".claude", "plugins", "installed_plugins.json");
|
|
83
94
|
const pluginKey = "pharaoh@pharaoh-so";
|
|
84
|
-
|
|
95
|
+
const pluginDir = join(home, ".claude", "plugins", "data", "pharaoh");
|
|
96
|
+
const version = getPackageVersion();
|
|
97
|
+
const now = new Date().toISOString();
|
|
98
|
+
let registry = { version: 2, plugins: {} };
|
|
85
99
|
if (existsSync(registryPath)) {
|
|
86
100
|
try {
|
|
87
|
-
|
|
101
|
+
const raw = JSON.parse(readFileSync(registryPath, "utf-8"));
|
|
102
|
+
// Handle both v2 format (with plugins key) and any other shape
|
|
103
|
+
if (raw.version === 2 && raw.plugins && typeof raw.plugins === "object") {
|
|
104
|
+
registry = raw;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// Non-v2 file — preserve as-is, add v2 structure around it
|
|
108
|
+
registry = { version: 2, plugins: {}, ...raw };
|
|
109
|
+
if (!registry.plugins)
|
|
110
|
+
registry.plugins = {};
|
|
111
|
+
}
|
|
88
112
|
}
|
|
89
113
|
catch {
|
|
90
114
|
process.stderr.write("Pharaoh: installed_plugins.json exists but is not valid JSON — skipping registration.\n");
|
|
91
|
-
return
|
|
115
|
+
return "error";
|
|
92
116
|
}
|
|
93
117
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
pluginRoot: join(home, ".claude", "plugins", "data", "pharaoh"),
|
|
102
|
-
installedAt: new Date().toISOString(),
|
|
118
|
+
const existing = registry.plugins[pluginKey];
|
|
119
|
+
const entry = {
|
|
120
|
+
scope: "user",
|
|
121
|
+
installPath: pluginDir,
|
|
122
|
+
version,
|
|
123
|
+
installedAt: now,
|
|
124
|
+
lastUpdated: now,
|
|
103
125
|
};
|
|
126
|
+
if (existing && existing.length > 0) {
|
|
127
|
+
const current = existing[0];
|
|
128
|
+
if (current.version === version) {
|
|
129
|
+
return "current";
|
|
130
|
+
}
|
|
131
|
+
// Version changed — update in place, preserve original installedAt
|
|
132
|
+
entry.installedAt = current.installedAt || now;
|
|
133
|
+
registry.plugins[pluginKey] = [entry];
|
|
134
|
+
writeFileSync(registryPath, JSON.stringify(registry, null, "\t"));
|
|
135
|
+
return "updated";
|
|
136
|
+
}
|
|
137
|
+
registry.plugins[pluginKey] = [entry];
|
|
104
138
|
writeFileSync(registryPath, JSON.stringify(registry, null, "\t"));
|
|
105
|
-
return
|
|
139
|
+
return "added";
|
|
106
140
|
}
|
|
107
141
|
// ── OpenClaw installer ──────────────────────────────────────────────
|
|
108
142
|
/**
|
|
@@ -181,17 +215,19 @@ export function runInstallSkills(home = homedir()) {
|
|
|
181
215
|
if (hasClaudeCode) {
|
|
182
216
|
const count = installClaudeCodePlugin(home);
|
|
183
217
|
if (count >= 0) {
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
: "Plugin
|
|
218
|
+
const regResult = registerClaudeCodePlugin(home);
|
|
219
|
+
const regMessages = {
|
|
220
|
+
added: "Plugin registered in installed_plugins.json",
|
|
221
|
+
updated: "Plugin version updated in installed_plugins.json",
|
|
222
|
+
current: "Plugin already registered and up-to-date",
|
|
223
|
+
error: "Could not update installed_plugins.json",
|
|
224
|
+
};
|
|
188
225
|
process.stderr.write([
|
|
189
226
|
`Pharaoh: installed ${count} skills as Claude Code plugin`,
|
|
190
227
|
` → ~/.claude/plugins/data/pharaoh/`,
|
|
191
|
-
` → ${
|
|
192
|
-
"",
|
|
193
|
-
"Restart Claude Code to pick up the new skills.",
|
|
228
|
+
` → ${regMessages[regResult]}`,
|
|
194
229
|
"",
|
|
230
|
+
...(regResult === "added" ? ["Restart Claude Code to pick up the new skills.", ""] : []),
|
|
195
231
|
].join("\n"));
|
|
196
232
|
installed = true;
|
|
197
233
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pharaoh-so/mcp",
|
|
3
|
-
"
|
|
3
|
+
"mcpName": "so.pharaoh/pharaoh",
|
|
4
|
+
"version": "0.2.5",
|
|
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",
|