@memorax/memorax-code 0.1.8 → 0.1.10
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/README.md +10 -0
- package/bin/memorax-code-codebuddy.mjs +4 -0
- package/bin/memorax-code-setup.mjs +182 -65
- package/bin/memorax-code.mjs +74 -45
- package/docs/configuration.md +82 -22
- package/docs/troubleshooting.md +69 -5
- package/lib/automatic-update.mjs +233 -0
- package/lib/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
- package/lib/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
- package/lib/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
- package/lib/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
- package/lib/memorax-code-adapter-common/src/config-utils.mjs +11 -0
- package/lib/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
- package/lib/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
- package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs +88 -3
- package/lib/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
- package/lib/memorax-code-backend/dist/app/backend-server.js +2 -2
- package/lib/memorax-code-backend/dist/app/memory-observability.js +2 -2
- package/lib/memorax-code-backend/dist/clients/codebuddy/jsonl-history.js +208 -0
- package/lib/memorax-code-backend/dist/clients/codebuddy/lifecycle.js +44 -0
- package/lib/memorax-code-backend/dist/clients/codebuddy/memory-hook-runtime.js +250 -0
- package/lib/memorax-code-backend/dist/clients/codebuddy/turn-id.js +16 -0
- package/lib/memorax-code-backend/dist/clients/codex/plugin-install.js +129 -7
- package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +52 -2
- package/lib/memorax-code-backend/dist/config/memorax-code.js +14 -1
- package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +43 -6
- package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
- package/lib/memorax-code-backend/dist/lifecycle/automatic-update-scheduler.js +133 -0
- package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
- package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +6 -4
- package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +48 -9
- package/lib/memorax-code-backend/dist/memory/cli.js +4 -2
- package/lib/memorax-code-backend/dist/memory/hook-command.js +31 -1
- package/lib/memorax-code-backend/dist/memory/reminder-trace-recorder.js +5 -1
- package/lib/memorax-code-backend/dist/memory/service.js +12 -0
- package/lib/memorax-code-backend/dist/shared/windows-cli-invocation.js +10 -1
- package/lib/memorax-code-backend/dist/trace/config.js +12 -0
- package/lib/memorax-code-backend/dist/trace/context.js +21 -1
- package/lib/memorax-code-backend/dist/trace/store.js +12 -1
- package/lib/memorax-code-backend/package.json +1 -1
- package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
- package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +11 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs +88 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
- package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +7 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/common-runtime.mjs +13 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/hooks.json +38 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/repo-memory-job.mjs +40 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/runtime-hook.mjs +279 -0
- package/lib/memorax-code-codebuddy-adapter/package.json +9 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +85 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/claude.yaml +10 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/openai.yaml +7 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/defaults.json +12 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +88 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +93 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-read.md +46 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-write.md +120 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-build.md +319 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-read.md +103 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-templates.md +390 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-update.md +127 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
- package/lib/memorax-code-codebuddy-adapter/src/cli.mjs +58 -0
- package/lib/memorax-code-codebuddy-adapter/src/config.mjs +277 -0
- package/lib/memorax-code-codebuddy-adapter/src/hook-manifest.mjs +47 -0
- package/lib/memorax-code-codebuddy-adapter/src/runtime-observation.mjs +65 -0
- package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
- package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-codex-adapter/package.json +1 -1
- package/lib/memorax-code-codex-adapter/runtime-hooks/ensure-backend.mjs +4 -1
- package/lib/memorax-code-dsh-adapter/package.json +2 -1
- package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +1 -0
- package/lib/memorax-code-opencode-adapter/package.json +1 -1
- package/lib/npm-invocation.mjs +51 -8
- package/lib/resolve-codebuddy-command.mjs +112 -0
- package/lib/run-entrypoint.mjs +25 -1
- package/lib/windows-cli-invocation.mjs +11 -1
- package/lib/windows-user-path.mjs +218 -0
- package/package.json +5 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { basename, delimiter, join } from "node:path";
|
|
2
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { basename, delimiter, isAbsolute, join } from "node:path";
|
|
4
4
|
import {
|
|
5
5
|
localBackendRecoveryArguments,
|
|
6
6
|
resolveBackendConnection,
|
|
@@ -16,8 +16,17 @@ export async function runEnsureBackendHook(options) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export async function ensureBackendAvailable(options, input = {}) {
|
|
19
|
-
if (isRepoMemoryJobWorker()
|
|
19
|
+
if (isRepoMemoryJobWorker()) return;
|
|
20
20
|
const homes = options.resolveHomes(input);
|
|
21
|
+
const metadata = packageMetadata(options.pluginRoot);
|
|
22
|
+
const command = memoraxCodeCommandInfo(
|
|
23
|
+
options.memoraxCodeCommand,
|
|
24
|
+
metadata,
|
|
25
|
+
options.platform,
|
|
26
|
+
);
|
|
27
|
+
if (ensureDisabled(options.ensureBackendValue)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
21
30
|
let connection;
|
|
22
31
|
try {
|
|
23
32
|
connection = options.backendConnection
|
|
@@ -34,19 +43,19 @@ export async function ensureBackendAvailable(options, input = {}) {
|
|
|
34
43
|
|
|
35
44
|
const recoveryArguments = localBackendRecoveryArguments(connection);
|
|
36
45
|
if (recoveryArguments === undefined) return;
|
|
37
|
-
const command = memoraxCodeCommandInfo(options.memoraxCodeCommand, options.pluginRoot);
|
|
38
46
|
if (!command.value || command.removed === true || !memoraxCodeCommandAvailable(command.value)) return;
|
|
39
47
|
const result = await runMemoraxCode(
|
|
40
48
|
command.value,
|
|
41
49
|
options.buildStartArgs(homes, recoveryArguments),
|
|
42
50
|
parsePositiveInt(options.startTimeoutValue, DEFAULT_ENSURE_BACKEND_START_TIMEOUT_MS),
|
|
43
51
|
options.nodePath,
|
|
44
|
-
options.recoveryEnv,
|
|
52
|
+
recoveryEnvironment(options.recoveryEnv, metadata),
|
|
45
53
|
);
|
|
46
54
|
if (result.code !== 0) {
|
|
47
55
|
options.debug?.(
|
|
48
56
|
`MemoraX Code backend start failed with code ${result.code}${result.stderr ? `: ${result.stderr}` : ""}`,
|
|
49
57
|
);
|
|
58
|
+
return;
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
|
|
@@ -79,22 +88,48 @@ async function backendHealthy(connection, timeoutMs) {
|
|
|
79
88
|
}
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
function memoraxCodeCommandInfo(explicitCommand,
|
|
91
|
+
function memoraxCodeCommandInfo(explicitCommand, metadata, platform = process.platform) {
|
|
83
92
|
const explicit = stringValue(explicitCommand);
|
|
84
93
|
if (explicit) return { value: explicit, removed: pathLooksRemoved(explicit) };
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
94
|
+
const command = stringValue(metadata?.memoraxCodeCommand);
|
|
95
|
+
if (command) return { value: command, removed: pathLooksRemoved(command) };
|
|
96
|
+
if (platform === "win32") return { value: undefined, removed: false };
|
|
87
97
|
return { value: "memorax-code", removed: false };
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
function
|
|
100
|
+
function packageMetadata(pluginRoot) {
|
|
91
101
|
const root = stringValue(pluginRoot);
|
|
92
102
|
if (!root) return undefined;
|
|
93
103
|
const path = join(root, ".memorax-code-package.json");
|
|
94
104
|
if (!existsSync(path)) return undefined;
|
|
95
105
|
try {
|
|
96
106
|
const metadata = JSON.parse(readFileSync(path, "utf8"));
|
|
97
|
-
return
|
|
107
|
+
return metadata && typeof metadata === "object" && !Array.isArray(metadata)
|
|
108
|
+
? metadata
|
|
109
|
+
: undefined;
|
|
110
|
+
} catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function recoveryEnvironment(recoveryEnv, metadata) {
|
|
116
|
+
if (stringValue(recoveryEnv?.MEMORAX_CODE_NPM_EXEC_PATH)
|
|
117
|
+
|| stringValue(process.env.MEMORAX_CODE_NPM_EXEC_PATH)) {
|
|
118
|
+
return recoveryEnv;
|
|
119
|
+
}
|
|
120
|
+
const npmExecPath = metadataNpmExecPath(metadata);
|
|
121
|
+
return npmExecPath
|
|
122
|
+
? { ...(recoveryEnv ?? {}), MEMORAX_CODE_NPM_EXEC_PATH: npmExecPath }
|
|
123
|
+
: recoveryEnv;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function metadataNpmExecPath(metadata) {
|
|
127
|
+
const npmExecPath = stringValue(metadata?.npmExecPath);
|
|
128
|
+
if (!npmExecPath || !isAbsolute(npmExecPath) || !/\.(?:cjs|js|mjs)$/i.test(npmExecPath)) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
return statSync(npmExecPath).isFile() ? npmExecPath : undefined;
|
|
98
133
|
} catch {
|
|
99
134
|
return undefined;
|
|
100
135
|
}
|
|
@@ -21,7 +21,7 @@ let requestedSignal;
|
|
|
21
21
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
22
22
|
process.on(signal, () => {
|
|
23
23
|
requestedSignal = signal;
|
|
24
|
-
activeChild
|
|
24
|
+
if (activeChild) terminateClient(activeChild, signal);
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -56,12 +56,16 @@ async function main(args) {
|
|
|
56
56
|
runId: workerContext.runId,
|
|
57
57
|
});
|
|
58
58
|
if (initial.status !== "started") throw new Error(`repo memory job cannot start from status ${initial.status}`);
|
|
59
|
+
const clientTimeoutMs = repoMemoryClientTimeoutMs();
|
|
60
|
+
const clientKillGraceMs = repoMemoryClientKillGraceMs();
|
|
59
61
|
let state = writeJobState(request.jobPath, {
|
|
60
62
|
...initial,
|
|
61
63
|
status: "running",
|
|
62
64
|
pid: process.pid,
|
|
63
65
|
workerPid: process.pid,
|
|
64
66
|
workerStartedAt: new Date().toISOString(),
|
|
67
|
+
clientTimeoutMs,
|
|
68
|
+
clientKillGraceMs,
|
|
65
69
|
});
|
|
66
70
|
|
|
67
71
|
if (requestedSignal) {
|
|
@@ -85,9 +89,18 @@ async function main(args) {
|
|
|
85
89
|
});
|
|
86
90
|
},
|
|
87
91
|
finalMessagePath: state.finalMessagePath,
|
|
92
|
+
timeoutMs: clientTimeoutMs,
|
|
93
|
+
killGraceMs: clientKillGraceMs,
|
|
88
94
|
});
|
|
89
95
|
state = readJobState(request.jobPath);
|
|
90
96
|
|
|
97
|
+
if (childResult.timedOut) {
|
|
98
|
+
return finishFailed(request, state, workerContext, `${runner}_timeout`, {
|
|
99
|
+
timeoutMs: childResult.timeoutMs,
|
|
100
|
+
elapsedMs: childResult.elapsedMs,
|
|
101
|
+
signal: childResult.signal,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
91
104
|
if (childResult.error) {
|
|
92
105
|
return finishFailed(request, state, workerContext, `${runner}_spawn_failed`, {
|
|
93
106
|
error: childResult.error.message,
|
|
@@ -213,6 +226,11 @@ async function waitForOwnedMarker(input) {
|
|
|
213
226
|
function runClient(command, options) {
|
|
214
227
|
return new Promise((resolveResult) => {
|
|
215
228
|
let settled = false;
|
|
229
|
+
let timeoutHandle;
|
|
230
|
+
let killHandle;
|
|
231
|
+
let timedOut = false;
|
|
232
|
+
let timeoutMs;
|
|
233
|
+
const startedAt = Date.now();
|
|
216
234
|
let invocation;
|
|
217
235
|
try {
|
|
218
236
|
invocation = process.platform === "win32" && /\.[cm]?js$/i.test(command[0])
|
|
@@ -228,6 +246,7 @@ function runClient(command, options) {
|
|
|
228
246
|
const child = spawn(invocation.command, invocation.args, {
|
|
229
247
|
cwd: options.cwd,
|
|
230
248
|
env: options.env,
|
|
249
|
+
detached: process.platform !== "win32",
|
|
231
250
|
stdio: options.captureStdout ? ["ignore", "pipe", "inherit"] : "inherit",
|
|
232
251
|
});
|
|
233
252
|
activeChild = child;
|
|
@@ -243,8 +262,10 @@ function runClient(command, options) {
|
|
|
243
262
|
const finish = (result) => {
|
|
244
263
|
if (settled) return;
|
|
245
264
|
settled = true;
|
|
265
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
266
|
+
if (killHandle) clearTimeout(killHandle);
|
|
246
267
|
activeChild = undefined;
|
|
247
|
-
resolveResult(result);
|
|
268
|
+
resolveResult({ ...result, timedOut, timeoutMs, elapsedMs: Date.now() - startedAt });
|
|
248
269
|
};
|
|
249
270
|
child.once("error", (error) => finish({ code: undefined, signal: undefined, error }));
|
|
250
271
|
child.once("close", (code, signal) => {
|
|
@@ -258,10 +279,74 @@ function runClient(command, options) {
|
|
|
258
279
|
}
|
|
259
280
|
finish({ code, signal, error: undefined });
|
|
260
281
|
});
|
|
261
|
-
if (
|
|
282
|
+
if (Number.isFinite(options.timeoutMs) && options.timeoutMs > 0) {
|
|
283
|
+
timeoutHandle = setTimeout(() => {
|
|
284
|
+
timeoutMs = options.timeoutMs;
|
|
285
|
+
timedOut = true;
|
|
286
|
+
const terminated = terminateClient(child, "SIGTERM");
|
|
287
|
+
// A broken or ignored CLI must not leave a detached job behind forever.
|
|
288
|
+
// The grace period is deliberately short and only targets this child.
|
|
289
|
+
killHandle = setTimeout(() => {
|
|
290
|
+
if (!settled) terminateClient(child, "SIGKILL");
|
|
291
|
+
}, options.killGraceMs);
|
|
292
|
+
if (!terminated && !settled) {
|
|
293
|
+
finish({
|
|
294
|
+
code: undefined,
|
|
295
|
+
signal: "SIGTERM",
|
|
296
|
+
error: undefined,
|
|
297
|
+
timedOut: true,
|
|
298
|
+
timeoutMs,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}, options.timeoutMs);
|
|
302
|
+
}
|
|
303
|
+
if (requestedSignal) terminateClient(child, requestedSignal);
|
|
262
304
|
});
|
|
263
305
|
}
|
|
264
306
|
|
|
307
|
+
function terminateClient(child, signal) {
|
|
308
|
+
if (!child || !Number.isInteger(child.pid) || child.pid <= 0) return false;
|
|
309
|
+
if (process.platform !== "win32") {
|
|
310
|
+
try {
|
|
311
|
+
process.kill(-child.pid, signal);
|
|
312
|
+
return true;
|
|
313
|
+
} catch {
|
|
314
|
+
// The process group may have exited between the timeout and this call.
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (process.platform === "win32") {
|
|
318
|
+
try {
|
|
319
|
+
const result = spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
|
|
320
|
+
encoding: "utf8",
|
|
321
|
+
windowsHide: true,
|
|
322
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
323
|
+
});
|
|
324
|
+
if (result.status === 0) return true;
|
|
325
|
+
} catch {
|
|
326
|
+
// Fall through to the direct child termination below.
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
return child.kill(signal);
|
|
331
|
+
} catch {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function repoMemoryClientTimeoutMs(env = process.env) {
|
|
337
|
+
return positiveEnvInteger(env.MEMORAX_CODE_REPO_MEMORY_JOB_TIMEOUT_MS, 10 * 60 * 1000);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function repoMemoryClientKillGraceMs(env = process.env) {
|
|
341
|
+
return positiveEnvInteger(env.MEMORAX_CODE_REPO_MEMORY_JOB_KILL_GRACE_MS, 5000);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function positiveEnvInteger(value, fallback) {
|
|
345
|
+
if (typeof value !== "string" || !/^\d+$/.test(value.trim())) return fallback;
|
|
346
|
+
const parsed = Number(value.trim());
|
|
347
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
348
|
+
}
|
|
349
|
+
|
|
265
350
|
function validateBundle(repo, validator) {
|
|
266
351
|
const python = process.env.MEMORAX_CODE_REPO_MEMORY_PYTHON_COMMAND || "python3";
|
|
267
352
|
process.stdout.write("\n[repo-memory-worker] validating generated repo memory\n");
|
|
@@ -24,6 +24,9 @@ export function resolveWindowsCliInvocation(command, args, options = {}) {
|
|
|
24
24
|
}
|
|
25
25
|
resolvedCommand ??= command;
|
|
26
26
|
if (/\.(?:exe|com)$/i.test(resolvedCommand)) return { command: resolvedCommand, args };
|
|
27
|
+
if (isBareCodeBuddyEntrypoint(resolvedCommand, pathApi) && fileExists(resolvedCommand)) {
|
|
28
|
+
return { command: options.nodePath ?? process.execPath, args: [resolvedCommand, ...args] };
|
|
29
|
+
}
|
|
27
30
|
if (!/\.(?:cmd|bat)$/i.test(resolvedCommand)) {
|
|
28
31
|
throw new Error(`refusing to execute unsupported Windows command shim: ${resolvedCommand}`);
|
|
29
32
|
}
|
|
@@ -104,9 +107,16 @@ export function selectWindowsCommandCandidate(command, output) {
|
|
|
104
107
|
.map((value) => value.trim())
|
|
105
108
|
.filter(Boolean);
|
|
106
109
|
const selected = candidates.find((value) => /\.(?:cmd|bat)$/i.test(value))
|
|
107
|
-
?? candidates.find((value) => /\.(?:exe|com)$/i.test(value))
|
|
110
|
+
?? candidates.find((value) => /\.(?:exe|com)$/i.test(value))
|
|
111
|
+
?? (path.win32.basename(command).toLowerCase() === "codebuddy"
|
|
112
|
+
? candidates.find((value) => isBareCodeBuddyEntrypoint(value, path.win32))
|
|
113
|
+
: undefined);
|
|
108
114
|
if (!selected) {
|
|
109
115
|
throw new Error(`where.exe did not return a safe executable or command shim for ${command}`);
|
|
110
116
|
}
|
|
111
117
|
return selected;
|
|
112
118
|
}
|
|
119
|
+
|
|
120
|
+
function isBareCodeBuddyEntrypoint(value, pathApi) {
|
|
121
|
+
return pathApi.basename(value).toLowerCase() === "codebuddy" && pathApi.extname(value) === "";
|
|
122
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { readClientHookPluginRoot } from "../memorax-code-adapter-common/src/config-utils.mjs";
|
|
5
6
|
import {
|
|
6
7
|
runEnsureBackendHook,
|
|
7
8
|
stringValue,
|
|
@@ -31,6 +32,7 @@ process.exit(0);
|
|
|
31
32
|
function pluginRoot() {
|
|
32
33
|
return stringValue(process.env.CLAUDE_PLUGIN_ROOT)
|
|
33
34
|
?? stringValue(process.env.PLUGIN_ROOT)
|
|
35
|
+
?? readClientHookPluginRoot()
|
|
34
36
|
?? dirname(dirname(fileURLToPath(import.meta.url)));
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -32,6 +32,7 @@ const STABLE_SHELL_REQUIRED_FILES = Object.freeze([
|
|
|
32
32
|
"memorax-code-adapter-common/src/hooks/capture-cwd-hook.mjs",
|
|
33
33
|
"memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs",
|
|
34
34
|
"memorax-code-adapter-common/src/config-utils.mjs",
|
|
35
|
+
"memorax-code-adapter-common/src/setup-completion.mjs",
|
|
35
36
|
"memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs",
|
|
36
37
|
"memorax-code-adapter-common/src/hooks/hook-runtime-generation.mjs",
|
|
37
38
|
"memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs",
|
|
@@ -226,10 +227,12 @@ export function ensureClaudePluginInstalled(options = {}) {
|
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
function writeInstalledPluginMetadata(installPath, claudeCommand) {
|
|
230
|
+
const npmExecPath = stringOption(process.env.MEMORAX_CODE_NPM_EXEC_PATH);
|
|
229
231
|
atomicWriteJson(join(installPath, ".memorax-code-package.json"), {
|
|
230
232
|
version: 1,
|
|
231
233
|
memoraxCodeCommand: process.argv[1],
|
|
232
234
|
claudeCommand,
|
|
235
|
+
...(npmExecPath ? { npmExecPath } : {}),
|
|
233
236
|
writtenAt: new Date().toISOString(),
|
|
234
237
|
});
|
|
235
238
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
export function resolveCommonSourceRoot(pluginRoot) {
|
|
5
|
+
const candidates = [
|
|
6
|
+
join(pluginRoot, "memorax-code-adapter-common", "src"),
|
|
7
|
+
join(pluginRoot, "..", "memorax-code-adapter-common", "src"),
|
|
8
|
+
join(pluginRoot, "..", "..", "memorax-code-adapter-common", "src"),
|
|
9
|
+
];
|
|
10
|
+
const root = candidates.find((candidate) => existsSync(join(candidate, "repo-memory", "repo-memory-job-supervisor.mjs")));
|
|
11
|
+
if (!root) throw new Error(`MemoraX Code shared runtime is unavailable for CodeBuddy plugin: ${pluginRoot}`);
|
|
12
|
+
return root;
|
|
13
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup|resume|clear|compact",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node \"${CODEBUDDY_PLUGIN_ROOT}/hooks/runtime-hook.mjs\" turn",
|
|
10
|
+
"timeout": 35
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"UserPromptSubmit": [
|
|
16
|
+
{
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "node \"${CODEBUDDY_PLUGIN_ROOT}/hooks/runtime-hook.mjs\" turn",
|
|
21
|
+
"timeout": 15
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"Stop": [
|
|
27
|
+
{
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "node \"${CODEBUDDY_PLUGIN_ROOT}/hooks/runtime-hook.mjs\" turn",
|
|
32
|
+
"timeout": 15
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { resolveCommonSourceRoot } from "./common-runtime.mjs";
|
|
6
|
+
|
|
7
|
+
const hookDir = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pluginRoot = dirname(hookDir);
|
|
9
|
+
const commonRoot = resolveCommonSourceRoot(pluginRoot);
|
|
10
|
+
const { resolveHookCodeBuddyCommand } = await import(pathToFileURL(join(commonRoot, "clients", "codebuddy-command.mjs")).href);
|
|
11
|
+
const { runRepoMemoryJob } = await import(pathToFileURL(join(commonRoot, "repo-memory", "repo-memory-job-supervisor.mjs")).href);
|
|
12
|
+
const { evaluateRepository } = await import(pathToFileURL(join(commonRoot, "repo-memory", "repo-memory-update-policy-evaluator.mjs")).href);
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const payload = runRepoMemoryJob(process.argv.slice(2), {
|
|
16
|
+
runner: "codebuddy",
|
|
17
|
+
finalMessageSource: "stdout",
|
|
18
|
+
memorySkillInvocation: "the `memorax-code` skill",
|
|
19
|
+
validatorPath: resolve(pluginRoot, "skills/memorax-code/scripts/validate_memory.py"),
|
|
20
|
+
evaluateRepository,
|
|
21
|
+
createCommand({ prompt }) {
|
|
22
|
+
const codeBuddy = resolveHookCodeBuddyCommand({
|
|
23
|
+
pluginRoot,
|
|
24
|
+
});
|
|
25
|
+
return [
|
|
26
|
+
codeBuddy,
|
|
27
|
+
"--print",
|
|
28
|
+
"--output-format",
|
|
29
|
+
"text",
|
|
30
|
+
"--dangerously-skip-permissions",
|
|
31
|
+
"--no-session-persistence",
|
|
32
|
+
prompt,
|
|
33
|
+
];
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|