@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.
Files changed (102) hide show
  1. package/README.md +10 -0
  2. package/bin/memorax-code-codebuddy.mjs +4 -0
  3. package/bin/memorax-code-setup.mjs +182 -65
  4. package/bin/memorax-code.mjs +74 -45
  5. package/docs/configuration.md +82 -22
  6. package/docs/troubleshooting.md +69 -5
  7. package/lib/automatic-update.mjs +233 -0
  8. package/lib/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
  9. package/lib/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
  10. package/lib/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
  11. package/lib/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
  12. package/lib/memorax-code-adapter-common/src/config-utils.mjs +11 -0
  13. package/lib/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
  14. package/lib/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
  15. package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs +88 -3
  16. package/lib/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
  17. package/lib/memorax-code-backend/dist/app/backend-server.js +2 -2
  18. package/lib/memorax-code-backend/dist/app/memory-observability.js +2 -2
  19. package/lib/memorax-code-backend/dist/clients/codebuddy/jsonl-history.js +208 -0
  20. package/lib/memorax-code-backend/dist/clients/codebuddy/lifecycle.js +44 -0
  21. package/lib/memorax-code-backend/dist/clients/codebuddy/memory-hook-runtime.js +250 -0
  22. package/lib/memorax-code-backend/dist/clients/codebuddy/turn-id.js +16 -0
  23. package/lib/memorax-code-backend/dist/clients/codex/plugin-install.js +129 -7
  24. package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +52 -2
  25. package/lib/memorax-code-backend/dist/config/memorax-code.js +14 -1
  26. package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +43 -6
  27. package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
  28. package/lib/memorax-code-backend/dist/lifecycle/automatic-update-scheduler.js +133 -0
  29. package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
  30. package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +6 -4
  31. package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +48 -9
  32. package/lib/memorax-code-backend/dist/memory/cli.js +4 -2
  33. package/lib/memorax-code-backend/dist/memory/hook-command.js +31 -1
  34. package/lib/memorax-code-backend/dist/memory/reminder-trace-recorder.js +5 -1
  35. package/lib/memorax-code-backend/dist/memory/service.js +12 -0
  36. package/lib/memorax-code-backend/dist/shared/windows-cli-invocation.js +10 -1
  37. package/lib/memorax-code-backend/dist/trace/config.js +12 -0
  38. package/lib/memorax-code-backend/dist/trace/context.js +21 -1
  39. package/lib/memorax-code-backend/dist/trace/store.js +12 -1
  40. package/lib/memorax-code-backend/package.json +1 -1
  41. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  42. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  43. package/lib/memorax-code-claude-adapter/package.json +1 -1
  44. package/lib/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
  45. package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
  46. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  47. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  48. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
  49. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
  50. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
  51. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
  52. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +11 -0
  53. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
  54. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
  55. 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
  56. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
  57. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  58. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
  59. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
  60. package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +7 -0
  61. package/lib/memorax-code-codebuddy-adapter/hooks/common-runtime.mjs +13 -0
  62. package/lib/memorax-code-codebuddy-adapter/hooks/hooks.json +38 -0
  63. package/lib/memorax-code-codebuddy-adapter/hooks/repo-memory-job.mjs +40 -0
  64. package/lib/memorax-code-codebuddy-adapter/hooks/runtime-hook.mjs +279 -0
  65. package/lib/memorax-code-codebuddy-adapter/package.json +9 -0
  66. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +85 -0
  67. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  68. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  69. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/defaults.json +12 -0
  70. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +88 -0
  71. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +93 -0
  72. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-read.md +46 -0
  73. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-write.md +120 -0
  74. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-build.md +319 -0
  75. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-read.md +103 -0
  76. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  77. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-update.md +127 -0
  78. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  79. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  80. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  81. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  82. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  83. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  84. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  85. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  86. package/lib/memorax-code-codebuddy-adapter/src/cli.mjs +58 -0
  87. package/lib/memorax-code-codebuddy-adapter/src/config.mjs +277 -0
  88. package/lib/memorax-code-codebuddy-adapter/src/hook-manifest.mjs +47 -0
  89. package/lib/memorax-code-codebuddy-adapter/src/runtime-observation.mjs +65 -0
  90. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  91. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  92. package/lib/memorax-code-codex-adapter/package.json +1 -1
  93. package/lib/memorax-code-codex-adapter/runtime-hooks/ensure-backend.mjs +4 -1
  94. package/lib/memorax-code-dsh-adapter/package.json +2 -1
  95. package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +1 -0
  96. package/lib/memorax-code-opencode-adapter/package.json +1 -1
  97. package/lib/npm-invocation.mjs +51 -8
  98. package/lib/resolve-codebuddy-command.mjs +112 -0
  99. package/lib/run-entrypoint.mjs +25 -1
  100. package/lib/windows-cli-invocation.mjs +11 -1
  101. package/lib/windows-user-path.mjs +218 -0
  102. 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() || ensureDisabled(options.ensureBackendValue)) return;
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, pluginRoot) {
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 metadata = metadataCommand(pluginRoot);
86
- if (metadata) return { value: metadata, removed: pathLooksRemoved(metadata) };
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 metadataCommand(pluginRoot) {
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 stringValue(metadata.memoraxCodeCommand);
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?.kill(signal);
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 (requestedSignal) child.kill(requestedSignal);
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
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorax-code/claude-adapter",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Claude Code Hook adapter for MemoraX Code memory services.",
@@ -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,7 @@
1
+ {
2
+ "name": "memorax-code-codebuddy-adapter",
3
+ "version": "0.1.10",
4
+ "description": "MemoraX Code memory integration for CodeBuddy and WorkBuddy.",
5
+ "hooks": "./hooks/hooks.json",
6
+ "skills": ["./skills/memorax-code"]
7
+ }
@@ -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
+ }