@pharaoh-so/mcp 0.3.59 → 0.3.61
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 +1 -1
- package/CHANGELOG.md +13 -0
- package/README.md +3 -2
- package/dist/backend.d.ts +7 -0
- package/dist/helpers.d.ts +3 -2
- package/dist/helpers.js +7 -4
- package/dist/index.js +40 -6
- package/dist/install-skills.d.ts +13 -0
- package/dist/install-skills.js +107 -2
- package/dist/proxy-server.js +2 -1
- package/hooks/route-grep.mjs +63 -0
- package/package.json +1 -1
- package/project-memory/bundle.mjs +9 -2
- package/project-memory/graph-cues.mjs +268 -0
- package/project-memory/hook-config.mjs +41 -14
- package/project-memory/hook-events.mjs +1 -1
- package/project-memory/local.mjs +30 -3
- package/project-memory/pharaoh-memory-hook.mjs +39 -7
- package/project-memory/preferences.d.mts +12 -0
- package/project-memory/preferences.mjs +70 -0
- package/project-memory/setup.d.mts +35 -9
- package/project-memory/setup.mjs +134 -10
package/project-memory/setup.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
|
4
4
|
import { bundledHook, installMemoryBundle, packagedMemoryBundle } from "./bundle.mjs";
|
|
5
5
|
import { hasLegacyHooks, hasUserHooks, updateHooks, updateUserHooks } from "./hook-config.mjs";
|
|
6
6
|
import { atomicWrite, checkout, git, hasMemoryGit } from "./local.mjs";
|
|
7
|
+
import { MEMORY_HOOK_CLIENTS, readHookPreferences, writeHookPreferences } from "./preferences.mjs";
|
|
7
8
|
import {
|
|
8
9
|
legacySettingsPath,
|
|
9
10
|
matchingMemoryHint,
|
|
@@ -13,8 +14,6 @@ import {
|
|
|
13
14
|
readHookSettings,
|
|
14
15
|
} from "./user-target.mjs";
|
|
15
16
|
|
|
16
|
-
const CLIENTS = ["claude-code", "codex"];
|
|
17
|
-
|
|
18
17
|
function userSettings(client, home, env) {
|
|
19
18
|
const codex = client === "codex";
|
|
20
19
|
const directory =
|
|
@@ -82,7 +81,7 @@ function migrateCheckout(client, target, notices) {
|
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
function syncUserHooks(client, home, env, target, hookPath, node, alreadyActive) {
|
|
84
|
+
function syncUserHooks(client, home, env, target, hookPath, node, alreadyActive, graphCues) {
|
|
86
85
|
let known = Boolean(alreadyActive);
|
|
87
86
|
try {
|
|
88
87
|
const settings = inspectSettings(userSettings(client, home, env));
|
|
@@ -95,7 +94,7 @@ function syncUserHooks(client, home, env, target, hookPath, node, alreadyActive)
|
|
|
95
94
|
hasLegacyHooks(settings.config, client, target.root);
|
|
96
95
|
writeSettings(
|
|
97
96
|
settings,
|
|
98
|
-
updateUserHooks(settings.config, client, hookPath, node),
|
|
97
|
+
updateUserHooks(settings.config, client, hookPath, node, graphCues),
|
|
99
98
|
target,
|
|
100
99
|
checkoutHook,
|
|
101
100
|
);
|
|
@@ -110,10 +109,118 @@ function writeActivation(path, active) {
|
|
|
110
109
|
else rmSync(path, { force: true });
|
|
111
110
|
}
|
|
112
111
|
|
|
112
|
+
/** Resolve explicit choices only after a supported client's MCP registration. */
|
|
113
|
+
export async function chooseMemoryHooks({
|
|
114
|
+
credentials,
|
|
115
|
+
clients,
|
|
116
|
+
requested = false,
|
|
117
|
+
ask,
|
|
118
|
+
home = homedir(),
|
|
119
|
+
env = process.env,
|
|
120
|
+
}) {
|
|
121
|
+
const choices = { accepted: [], declined: [], graphClients: [], notices: [] };
|
|
122
|
+
if (typeof process.getuid !== "function") {
|
|
123
|
+
if (requested)
|
|
124
|
+
choices.notices.push("Pharaoh: project memory hooks need a POSIX client with system Git.");
|
|
125
|
+
return choices;
|
|
126
|
+
}
|
|
127
|
+
if (credentials?.project_memory_status === "unavailable") {
|
|
128
|
+
if (requested)
|
|
129
|
+
choices.notices.push(
|
|
130
|
+
"Pharaoh: project memory eligibility could not be checked. Retry setup to enable hooks.",
|
|
131
|
+
);
|
|
132
|
+
return choices;
|
|
133
|
+
}
|
|
134
|
+
if (!projectMemoryHint(credentials).length) {
|
|
135
|
+
if (requested) choices.notices.push("Pharaoh: this session has no project memory hook grant.");
|
|
136
|
+
return credentials ? { ...choices, removeUnentitled: true } : choices;
|
|
137
|
+
}
|
|
138
|
+
if (!clients.length || !hasMemoryGit) {
|
|
139
|
+
if (requested && !hasMemoryGit)
|
|
140
|
+
choices.notices.push("Pharaoh: project memory hooks need a POSIX client with system Git.");
|
|
141
|
+
return choices;
|
|
142
|
+
}
|
|
143
|
+
const previous = readHookPreferences(home);
|
|
144
|
+
const active = readActiveMemoryClients(home);
|
|
145
|
+
const next = { ...previous };
|
|
146
|
+
for (const client of MEMORY_HOOK_CLIENTS) {
|
|
147
|
+
if (!clients.includes(client)) continue;
|
|
148
|
+
const label = client === "codex" ? "Codex" : "Claude Code";
|
|
149
|
+
let config;
|
|
150
|
+
try {
|
|
151
|
+
config = inspectSettings(userSettings(client, home, env)).config;
|
|
152
|
+
} catch {
|
|
153
|
+
// An unreadable settings file cannot establish a previous installation.
|
|
154
|
+
}
|
|
155
|
+
if (client === "claude-code" && config?.disableAllHooks === true) continue;
|
|
156
|
+
const choice = { ...previous[client] };
|
|
157
|
+
const memoryHook =
|
|
158
|
+
active[client] &&
|
|
159
|
+
hasUserHooks({ hooks: { UserPromptSubmit: config?.hooks?.UserPromptSubmit } }, client);
|
|
160
|
+
if (requested) choice.memory = true;
|
|
161
|
+
else if (choice.memory !== false && (choice.memory !== true || !memoryHook)) {
|
|
162
|
+
const answer = await ask(
|
|
163
|
+
`Enable Pharaoh project memory hooks in ${label} (recommended)? [Y/n] `,
|
|
164
|
+
true,
|
|
165
|
+
);
|
|
166
|
+
if (answer === null) continue;
|
|
167
|
+
choice.memory = answer;
|
|
168
|
+
if (!answer && choice.graph === true) delete choice.graph;
|
|
169
|
+
}
|
|
170
|
+
if (choice.memory === true) {
|
|
171
|
+
let graphEnabled =
|
|
172
|
+
choice.graph === true &&
|
|
173
|
+
["PreToolUse", "PostToolUse"].every((event) =>
|
|
174
|
+
hasUserHooks({ hooks: { [event]: config?.hooks?.[event] } }, client),
|
|
175
|
+
);
|
|
176
|
+
if (requested || choice.graph === undefined || (choice.graph === true && !graphEnabled)) {
|
|
177
|
+
const answer = await ask(
|
|
178
|
+
`Enable advisory Pharaoh worktree cues in ${label}? [y/N] `,
|
|
179
|
+
false,
|
|
180
|
+
);
|
|
181
|
+
if (answer !== null) {
|
|
182
|
+
choice.graph = answer;
|
|
183
|
+
graphEnabled = answer;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
choices.accepted.push(client);
|
|
187
|
+
if (graphEnabled) choices.graphClients.push(client);
|
|
188
|
+
} else {
|
|
189
|
+
choices.declined.push(client);
|
|
190
|
+
}
|
|
191
|
+
next[client] = choice;
|
|
192
|
+
}
|
|
193
|
+
writeHookPreferences(next, home, previous);
|
|
194
|
+
return choices;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Apply the outcome against the saved choices, which can change after the prompt. */
|
|
198
|
+
export function applyMemoryHookChoices(decision, options = {}) {
|
|
199
|
+
const { accepted, declined, graphClients, removeUnentitled } = decision;
|
|
200
|
+
const notices = [...decision.notices];
|
|
201
|
+
if (removeUnentitled)
|
|
202
|
+
return [...notices, ...syncMemoryHooks({ ...options, clients: MEMORY_HOOK_CLIENTS })];
|
|
203
|
+
|
|
204
|
+
if (declined.length) {
|
|
205
|
+
const saved = readHookPreferences(options.home);
|
|
206
|
+
const clients = declined.filter((client) => saved[client]?.memory === false);
|
|
207
|
+
if (clients.length) notices.push(...syncMemoryHooks({ ...options, clients }));
|
|
208
|
+
}
|
|
209
|
+
if (accepted.length) {
|
|
210
|
+
const saved = readHookPreferences(options.home);
|
|
211
|
+
const clients = accepted.filter((client) => saved[client]?.memory === true);
|
|
212
|
+
const cues = graphClients.filter((client) => saved[client]?.graph === true);
|
|
213
|
+
if (clients.length)
|
|
214
|
+
notices.push(...syncMemoryHooks({ ...options, clients, graphClients: cues, enable: true }));
|
|
215
|
+
}
|
|
216
|
+
return notices;
|
|
217
|
+
}
|
|
218
|
+
|
|
113
219
|
/** Run only during setup or explicit cleanup of the local memory hooks. */
|
|
114
220
|
export function syncMemoryHooks({
|
|
115
221
|
credentials,
|
|
116
|
-
clients =
|
|
222
|
+
clients = MEMORY_HOOK_CLIENTS,
|
|
223
|
+
graphClients = [],
|
|
117
224
|
enable = false,
|
|
118
225
|
home = homedir(),
|
|
119
226
|
env = process.env,
|
|
@@ -150,8 +257,8 @@ export function syncMemoryHooks({
|
|
|
150
257
|
}
|
|
151
258
|
}
|
|
152
259
|
|
|
153
|
-
for (const client of
|
|
154
|
-
if (
|
|
260
|
+
for (const client of MEMORY_HOOK_CLIENTS) {
|
|
261
|
+
if (!clients.includes(client)) continue;
|
|
155
262
|
const next = { ...active, [client]: digest };
|
|
156
263
|
try {
|
|
157
264
|
// Enable the replacement before swapping any settings, so failed
|
|
@@ -159,7 +266,16 @@ export function syncMemoryHooks({
|
|
|
159
266
|
const replacing = enable && active[client] !== digest;
|
|
160
267
|
if (replacing) writeActivation(activePath, next);
|
|
161
268
|
try {
|
|
162
|
-
syncUserHooks(
|
|
269
|
+
syncUserHooks(
|
|
270
|
+
client,
|
|
271
|
+
home,
|
|
272
|
+
env,
|
|
273
|
+
target,
|
|
274
|
+
installed,
|
|
275
|
+
node,
|
|
276
|
+
active[client],
|
|
277
|
+
graphClients.includes(client),
|
|
278
|
+
);
|
|
163
279
|
} catch (error) {
|
|
164
280
|
if (replacing) writeActivation(activePath, active);
|
|
165
281
|
throw error;
|
|
@@ -176,8 +292,16 @@ export function syncMemoryHooks({
|
|
|
176
292
|
}
|
|
177
293
|
|
|
178
294
|
// Deactivate loaded user hooks even if known settings could not be edited.
|
|
179
|
-
if (!enable
|
|
180
|
-
|
|
295
|
+
if (!enable) {
|
|
296
|
+
const remaining = Object.fromEntries(
|
|
297
|
+
Object.entries(active).filter(([client]) => !clients.includes(client)),
|
|
298
|
+
);
|
|
299
|
+
if (Object.keys(remaining).length !== Object.keys(active).length)
|
|
300
|
+
writeActivation(activePath, remaining);
|
|
301
|
+
}
|
|
302
|
+
for (const client of enable ? configured : clients) {
|
|
303
|
+
if (MEMORY_HOOK_CLIENTS.includes(client)) migrateCheckout(client, target, notices);
|
|
304
|
+
}
|
|
181
305
|
if (configured.length) {
|
|
182
306
|
notices.push(
|
|
183
307
|
"Pharaoh project memory reminders are set up for your eligible repositories. Start a new local conversation.",
|