@memorax/memorax-code 0.1.9 → 0.1.11

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 (101) 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 +85 -22
  6. package/docs/troubleshooting.md +72 -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/config/memorax-code.js +14 -1
  25. package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +43 -6
  26. package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
  27. package/lib/memorax-code-backend/dist/lifecycle/automatic-update-scheduler.js +133 -0
  28. package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
  29. package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +6 -4
  30. package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +48 -9
  31. package/lib/memorax-code-backend/dist/memory/cli.js +4 -2
  32. package/lib/memorax-code-backend/dist/memory/hook-command.js +31 -1
  33. package/lib/memorax-code-backend/dist/memory/reminder-trace-recorder.js +5 -1
  34. package/lib/memorax-code-backend/dist/memory/service.js +12 -0
  35. package/lib/memorax-code-backend/dist/shared/windows-cli-invocation.js +10 -1
  36. package/lib/memorax-code-backend/dist/trace/config.js +12 -0
  37. package/lib/memorax-code-backend/dist/trace/context.js +21 -1
  38. package/lib/memorax-code-backend/dist/trace/store.js +12 -1
  39. package/lib/memorax-code-backend/package.json +1 -1
  40. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  41. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  42. package/lib/memorax-code-claude-adapter/package.json +1 -1
  43. package/lib/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
  44. package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
  45. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  46. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  47. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
  48. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
  49. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
  50. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
  51. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +11 -0
  52. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
  53. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
  54. 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
  55. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
  56. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  57. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
  58. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
  59. package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +7 -0
  60. package/lib/memorax-code-codebuddy-adapter/hooks/common-runtime.mjs +13 -0
  61. package/lib/memorax-code-codebuddy-adapter/hooks/hooks.json +38 -0
  62. package/lib/memorax-code-codebuddy-adapter/hooks/repo-memory-job.mjs +40 -0
  63. package/lib/memorax-code-codebuddy-adapter/hooks/runtime-hook.mjs +289 -0
  64. package/lib/memorax-code-codebuddy-adapter/package.json +9 -0
  65. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +85 -0
  66. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  67. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  68. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/defaults.json +12 -0
  69. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +88 -0
  70. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +93 -0
  71. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-read.md +46 -0
  72. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-write.md +120 -0
  73. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-build.md +319 -0
  74. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-read.md +103 -0
  75. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  76. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-update.md +127 -0
  77. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  78. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  79. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  80. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  81. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  82. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  83. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  84. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  85. package/lib/memorax-code-codebuddy-adapter/src/cli.mjs +58 -0
  86. package/lib/memorax-code-codebuddy-adapter/src/config.mjs +340 -0
  87. package/lib/memorax-code-codebuddy-adapter/src/hook-manifest.mjs +47 -0
  88. package/lib/memorax-code-codebuddy-adapter/src/runtime-observation.mjs +65 -0
  89. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  90. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  91. package/lib/memorax-code-codex-adapter/package.json +1 -1
  92. package/lib/memorax-code-codex-adapter/runtime-hooks/ensure-backend.mjs +4 -1
  93. package/lib/memorax-code-dsh-adapter/package.json +2 -1
  94. package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +1 -0
  95. package/lib/memorax-code-opencode-adapter/package.json +1 -1
  96. package/lib/npm-invocation.mjs +51 -8
  97. package/lib/resolve-codebuddy-command.mjs +123 -0
  98. package/lib/run-entrypoint.mjs +25 -1
  99. package/lib/windows-cli-invocation.mjs +11 -1
  100. package/lib/windows-user-path.mjs +218 -0
  101. package/package.json +5 -3
@@ -0,0 +1,47 @@
1
+ import { readFile, stat, writeFile } from "node:fs/promises";
2
+ import { join, win32 } from "node:path";
3
+
4
+ const REQUIRED_EVENTS = ["SessionStart", "UserPromptSubmit", "Stop"];
5
+ const PORTABLE_COMMAND = 'node "${CODEBUDDY_PLUGIN_ROOT}/hooks/runtime-hook.mjs" turn';
6
+
7
+ export function codeBuddyHookCommand(pluginRoot, platform = process.platform) {
8
+ if (platform !== "win32") return PORTABLE_COMMAND;
9
+ return `node "${win32.join(pluginRoot, "hooks", "runtime-hook.mjs").replaceAll("\\", "/")}" turn`;
10
+ }
11
+
12
+ export async function materializeCodeBuddyHookManifest(pluginRoot, platform = process.platform) {
13
+ if (platform !== "win32") return;
14
+ const path = join(pluginRoot, "hooks", "hooks.json");
15
+ const manifest = JSON.parse(await readFile(path, "utf8"));
16
+ const command = codeBuddyHookCommand(pluginRoot, platform);
17
+ for (const event of REQUIRED_EVENTS) {
18
+ const hooks = commandHooks(manifest, event);
19
+ if (hooks.length === 0) throw new Error(`CodeBuddy Hook manifest is missing ${event}`);
20
+ for (const hook of hooks) hook.command = command;
21
+ }
22
+ await writeFile(path, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
23
+ }
24
+
25
+ export async function codeBuddyHookManifestConfigured(pluginRoot, platform = process.platform) {
26
+ try {
27
+ await stat(join(pluginRoot, "hooks", "runtime-hook.mjs"));
28
+ const manifest = JSON.parse(await readFile(join(pluginRoot, "hooks", "hooks.json"), "utf8"));
29
+ const expected = codeBuddyHookCommand(pluginRoot, platform);
30
+ return REQUIRED_EVENTS.every((event) => {
31
+ const hooks = commandHooks(manifest, event);
32
+ return hooks.length > 0 && hooks.every((hook) => hook.command === expected);
33
+ });
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+
39
+ function commandHooks(manifest, event) {
40
+ const matchers = manifest?.hooks?.[event];
41
+ if (!Array.isArray(matchers)) return [];
42
+ return matchers.flatMap((matcher) => (
43
+ Array.isArray(matcher?.hooks)
44
+ ? matcher.hooks.filter((hook) => hook?.type === "command" && typeof hook.command === "string")
45
+ : []
46
+ ));
47
+ }
@@ -0,0 +1,65 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+
4
+ export function codeBuddyRuntimeObservationPath(memoraxCodeHome) {
5
+ return join(memoraxCodeHome, "adapters", "codebuddy", "runtime-observed.json");
6
+ }
7
+
8
+ export async function writeCodeBuddyRuntimeObservation({
9
+ memoraxCodeHome,
10
+ codeBuddyHome,
11
+ pluginRoot,
12
+ }) {
13
+ const pluginVersion = await readPluginVersion(pluginRoot);
14
+ const path = codeBuddyRuntimeObservationPath(memoraxCodeHome);
15
+ const record = {
16
+ version: 1,
17
+ pluginVersion,
18
+ codeBuddyHome,
19
+ observedAt: new Date().toISOString(),
20
+ };
21
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
22
+ const temp = `${path}.tmp-${process.pid}-${Date.now()}`;
23
+ await writeFile(temp, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600 });
24
+ await rename(temp, path);
25
+ return record;
26
+ }
27
+
28
+ export async function readCodeBuddyRuntimeObservation(memoraxCodeHome) {
29
+ try {
30
+ const value = JSON.parse(await readFile(codeBuddyRuntimeObservationPath(memoraxCodeHome), "utf8"));
31
+ if (!isRecord(value) || !hasExactKeys(value, ["codeBuddyHome", "observedAt", "pluginVersion", "version"])) {
32
+ return undefined;
33
+ }
34
+ if (value.version !== 1
35
+ || !stringValue(value.pluginVersion)
36
+ || !stringValue(value.codeBuddyHome)
37
+ || !validTimestamp(value.observedAt)) return undefined;
38
+ return value;
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ }
43
+
44
+ async function readPluginVersion(pluginRoot) {
45
+ const manifest = JSON.parse(await readFile(join(pluginRoot, ".codebuddy-plugin", "plugin.json"), "utf8"));
46
+ const version = stringValue(manifest?.version);
47
+ if (!version) throw new Error("CodeBuddy plugin manifest has no version");
48
+ return version;
49
+ }
50
+
51
+ function isRecord(value) {
52
+ return value && typeof value === "object" && !Array.isArray(value);
53
+ }
54
+
55
+ function hasExactKeys(value, expected) {
56
+ return Object.keys(value).sort().join("\n") === [...expected].sort().join("\n");
57
+ }
58
+
59
+ function stringValue(value) {
60
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
61
+ }
62
+
63
+ function validTimestamp(value) {
64
+ return typeof value === "string" && Number.isFinite(Date.parse(value));
65
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorax-code-codex-adapter",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Codex adapter, memory skill, and local hooks for MemoraX Code.",
5
5
  "author": {
6
6
  "name": "MemoraX AI"
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "version": 1,
3
- "shellVersion": "0.1.9",
3
+ "shellVersion": "0.1.11",
4
4
  "runtimeAbi": 1
5
5
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorax-code/codex-adapter",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Codex Hook and memory integration for MemoraX Code.",
@@ -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,
@@ -56,7 +57,9 @@ async function reconcileAdapter(homes, backendUrl) {
56
57
  }
57
58
 
58
59
  function pluginRoot() {
59
- return stringValue(process.env.PLUGIN_ROOT) ?? dirname(dirname(fileURLToPath(import.meta.url)));
60
+ return stringValue(process.env.PLUGIN_ROOT)
61
+ ?? readClientHookPluginRoot()
62
+ ?? dirname(dirname(fileURLToPath(import.meta.url)));
60
63
  }
61
64
 
62
65
  function hookHomes(input = {}) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorax-code/dsh-memorax-code",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "DeepSeek Harness native memory adapter for MemoraX Code.",
@@ -44,6 +44,7 @@
44
44
  "src/runtime-state.mjs",
45
45
  "memorax-code-adapter-common/src/backend-connection.mjs",
46
46
  "memorax-code-adapter-common/src/config-utils.mjs",
47
+ "memorax-code-adapter-common/src/setup-completion.mjs",
47
48
  "memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs",
48
49
  "memorax-code-adapter-common/src/hooks/memory-skill-reminder-policy.mjs",
49
50
  "memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs",
@@ -100,6 +100,7 @@ const PROFILE_BUNDLE_FILES = Object.freeze([
100
100
  "src/runtime-state.mjs",
101
101
  "memorax-code-adapter-common/src/backend-connection.mjs",
102
102
  "memorax-code-adapter-common/src/config-utils.mjs",
103
+ "memorax-code-adapter-common/src/setup-completion.mjs",
103
104
  "memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs",
104
105
  "memorax-code-adapter-common/src/hooks/memory-skill-reminder-policy.mjs",
105
106
  "memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorax-code/opencode-adapter",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "OpenCode plugin adapter for MemoraX Code memory services.",
@@ -1,12 +1,26 @@
1
+ import { spawn } from "node:child_process";
1
2
  import { existsSync } from "node:fs";
3
+ import { homedir } from "node:os";
2
4
  import path from "node:path";
3
5
 
4
6
  export function resolveNpmInvocation(npmArgs, options = {}) {
7
+ const platform = options.platform ?? process.platform;
8
+ if (platform !== "win32") return { command: "npm", args: npmArgs };
9
+ const nodePath = options.nodePath ?? process.execPath;
10
+ const npmCli = resolveNpmExecPath(options);
11
+ if (npmCli) return { command: nodePath, args: [npmCli, ...npmArgs] };
12
+ throw new Error(
13
+ "npm CLI JavaScript entrypoint was not found; set MEMORAX_CODE_NPM_EXEC_PATH, "
14
+ + "npm_execpath, or NPM_CLI_JS before running memorax-code update",
15
+ );
16
+ }
17
+
18
+ export function resolveNpmExecPath(options = {}) {
5
19
  const env = options.env ?? process.env;
6
20
  const platform = options.platform ?? process.platform;
21
+ if (platform !== "win32") return undefined;
7
22
  const nodePath = options.nodePath ?? process.execPath;
8
23
  const fileExists = options.existsSync ?? existsSync;
9
- if (platform !== "win32") return { command: "npm", args: npmArgs };
10
24
  const pathApi = path.win32;
11
25
  const candidates = [
12
26
  env.MEMORAX_CODE_NPM_EXEC_PATH,
@@ -21,11 +35,40 @@ export function resolveNpmInvocation(npmArgs, options = {}) {
21
35
  "bin",
22
36
  "npm-cli.js",
23
37
  ),
24
- ].filter((candidate) => typeof candidate === "string" && /\.(?:cjs|js|mjs)$/i.test(candidate));
25
- const npmCli = candidates.find((candidate) => fileExists(candidate));
26
- if (npmCli) return { command: nodePath, args: [npmCli, ...npmArgs] };
27
- throw new Error(
28
- "npm CLI JavaScript entrypoint was not found; set MEMORAX_CODE_NPM_EXEC_PATH, "
29
- + "npm_execpath, or NPM_CLI_JS before running memorax-code update",
30
- );
38
+ ].filter((candidate) => (
39
+ typeof candidate === "string"
40
+ && pathApi.isAbsolute(candidate)
41
+ && /\.(?:cjs|js|mjs)$/i.test(candidate)
42
+ ));
43
+ return candidates.find((candidate) => fileExists(candidate));
44
+ }
45
+
46
+ export function npmCommandCwd(env = process.env) {
47
+ for (const candidate of [env.HOME, homedir(), "/"]) {
48
+ if (candidate && existsSync(candidate)) return candidate;
49
+ }
50
+ return "/";
51
+ }
52
+
53
+ export async function runNpmCommand(npmArgs, options = {}) {
54
+ const env = options.env ?? process.env;
55
+ const invocation = resolveNpmInvocation(npmArgs, options);
56
+ const cwd = options.cwd ?? npmCommandCwd(env);
57
+ const child = (options.spawnProcess ?? spawn)(invocation.command, invocation.args, {
58
+ cwd,
59
+ env: { ...env, PWD: cwd },
60
+ stdio: options.stdio ?? "inherit",
61
+ windowsHide: options.windowsHide ?? false,
62
+ });
63
+ return await waitForChildProcess(child);
64
+ }
65
+
66
+ export async function waitForChildProcess(child) {
67
+ return await new Promise((resolve, reject) => {
68
+ child.once("error", reject);
69
+ child.once("close", (code, signal) => resolve({
70
+ exitCode: signal ? 1 : (code ?? 1),
71
+ signal,
72
+ }));
73
+ });
31
74
  }
@@ -0,0 +1,123 @@
1
+ import { existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { delimiter, join, win32 } from "node:path";
4
+ import {
5
+ commandOnPath,
6
+ isExecutableCommand,
7
+ } from "./vscode-extension-command.mjs";
8
+
9
+ const WORKBUDDY_APP_NAMES = ["WorkBuddy.app", "CodeBuddy.app"];
10
+ const WORKBUDDY_BUNDLED_SEGMENTS = [
11
+ "Contents",
12
+ "Resources",
13
+ "app.asar.unpacked",
14
+ "cli",
15
+ "bin",
16
+ "codebuddy",
17
+ ];
18
+ const WINDOWS_WORKBUDDY_SEGMENTS = [
19
+ ["resources", "app.asar.unpacked", "cli", "bin", "codebuddy"],
20
+ ["resources", "app.asar.unpacked", "cli", "bin", "codebuddy.exe"],
21
+ ];
22
+
23
+ export function resolveCodeBuddyCommand({
24
+ env = process.env,
25
+ homeDir = homedir(),
26
+ platform = process.platform,
27
+ applicationRoots = [join(homeDir, "Applications"), "/Applications"],
28
+ windowsRoots = defaultWindowsRoots(env, homeDir),
29
+ pathExists = isExecutableCommand,
30
+ } = {}) {
31
+ const override = nonEmpty(env.MEMORAX_CODE_CODEBUDDY_COMMAND)
32
+ ?? nonEmpty(env.CODEBUDDY_CLI_PATH)
33
+ ?? nonEmpty(env.WORKBUDDY_CODEBUDDY_PATH);
34
+ if (override) return { command: override, source: "configured" };
35
+
36
+ if (commandOnPath("codebuddy", env.PATH, platform, env.PATHEXT)) {
37
+ return { command: findCommandOnPath("codebuddy", env.PATH, platform, env.PATHEXT), source: "path" };
38
+ }
39
+
40
+ if (platform === "darwin") {
41
+ for (const root of applicationRoots) {
42
+ for (const appName of WORKBUDDY_APP_NAMES) {
43
+ const command = join(root, appName, ...WORKBUDDY_BUNDLED_SEGMENTS);
44
+ if (pathExists(command, platform)) return { command, source: "app-bundled" };
45
+ }
46
+ }
47
+ }
48
+
49
+ if (platform === "win32") {
50
+ for (const root of windowsRoots) {
51
+ for (const segments of WINDOWS_WORKBUDDY_SEGMENTS) {
52
+ const command = win32.join(root, ...segments);
53
+ if (pathExists(command, platform)) return { command, source: "app-bundled" };
54
+ }
55
+ }
56
+ }
57
+
58
+ return { command: "codebuddy", source: "unavailable" };
59
+ }
60
+
61
+ function findCommandOnPath(command, pathValue, platform, pathExtValue) {
62
+ const extensions = platform === "win32"
63
+ ? String(pathExtValue ?? ".EXE;.CMD;.BAT;.COM").split(";")
64
+ : [""];
65
+ const pathDelimiter = platform === "win32" ? ";" : delimiter;
66
+ const pathJoin = platform === "win32" ? win32.join : join;
67
+ for (const root of String(pathValue ?? "").split(pathDelimiter)) {
68
+ if (!root) continue;
69
+ for (const extension of extensions) {
70
+ const candidate = pathJoin(root, `${command}${extension}`);
71
+ if (isExecutableCommand(candidate, platform)) return candidate;
72
+ }
73
+ }
74
+ return platform === "win32" ? "codebuddy.exe" : "codebuddy";
75
+ }
76
+
77
+ export function ensureCodeBuddyCommandEnv(options = {}) {
78
+ const env = options.env ?? process.env;
79
+ const resolved = resolveCodeBuddyCommand({ ...options, env });
80
+ if (resolved.source !== "unavailable") env.MEMORAX_CODE_CODEBUDDY_COMMAND = resolved.command;
81
+ return resolved;
82
+ }
83
+
84
+ export function defaultCodeBuddyHome(
85
+ env = process.env,
86
+ homeDir = homedir(),
87
+ platform = process.platform,
88
+ pathExists = existsSync,
89
+ ) {
90
+ const configured = nonEmpty(env.CODEBUDDY_HOME) ?? nonEmpty(env.WORKBUDDY_HOME);
91
+ if (configured) return configured;
92
+ if (platform !== "win32") return join(homeDir, ".workbuddy");
93
+ const workBuddyHome = win32.join(homeDir, ".workbuddy");
94
+ const legacyCodeBuddyHome = win32.join(homeDir, ".codebuddy");
95
+ return pathExists(workBuddyHome) || !pathExists(legacyCodeBuddyHome)
96
+ ? workBuddyHome
97
+ : legacyCodeBuddyHome;
98
+ }
99
+
100
+ function defaultWindowsRoots(env, homeDir) {
101
+ const roots = [];
102
+ const localAppData = nonEmpty(env.LOCALAPPDATA) ?? join(homeDir, "AppData", "Local");
103
+ const programFiles = nonEmpty(env.ProgramFiles) ?? "C:\\Program Files";
104
+ const programFilesX86 = nonEmpty(env["ProgramFiles(x86)"]) ?? "C:\\Program Files (x86)";
105
+ for (const root of [
106
+ win32.join(localAppData, "Programs", "WorkBuddy"),
107
+ win32.join(localAppData, "Programs", "CodeBuddy"),
108
+ win32.join(localAppData, "WorkBuddy"),
109
+ win32.join(localAppData, "CodeBuddy"),
110
+ win32.join(programFiles, "WorkBuddy"),
111
+ win32.join(programFiles, "CodeBuddy"),
112
+ win32.join(programFilesX86, "WorkBuddy"),
113
+ win32.join(programFilesX86, "CodeBuddy"),
114
+ ]) {
115
+ if (!roots.includes(root)) roots.push(root);
116
+ }
117
+ return roots;
118
+ }
119
+
120
+ function nonEmpty(value) {
121
+ const normalized = String(value ?? "").trim();
122
+ return normalized || undefined;
123
+ }
@@ -1,8 +1,11 @@
1
- import { delimiter, dirname, join } from "node:path";
1
+ import { readFileSync } from "node:fs";
2
+ import { delimiter, dirname, join, resolve } from "node:path";
2
3
  import { fileURLToPath, pathToFileURL } from "node:url";
3
4
  import { unsupportedNodeVersionMessage } from "./node-version.mjs";
5
+ import { resolveNpmExecPath } from "./npm-invocation.mjs";
4
6
  import { ensureClaudeCommandEnv } from "./resolve-claude-command.mjs";
5
7
  import { ensureCodexCommandEnv } from "./resolve-codex-command.mjs";
8
+ import { ensureCodeBuddyCommandEnv } from "./resolve-codebuddy-command.mjs";
6
9
 
7
10
  const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
11
 
@@ -41,13 +44,27 @@ export function ensureInstallWatchdogEnv(root = packageRoot) {
41
44
  }
42
45
  }
43
46
 
47
+ export function ensureNpmPackageRuntimeEnv(root = packageRoot, options = {}) {
48
+ const env = options.env ?? process.env;
49
+ const resolvedRoot = resolve(root);
50
+ const metadata = JSON.parse(readFileSync(join(resolvedRoot, "package.json"), "utf8"));
51
+ const version = typeof metadata.version === "string" ? metadata.version.trim() : "";
52
+ if (!version) throw new Error("MemoraX Code package version is missing");
53
+ env.MEMORAX_CODE_NPM_PACKAGE_ROOT = resolvedRoot;
54
+ env.MEMORAX_CODE_NPM_PACKAGE_VERSION = version;
55
+ const npmExecPath = resolveNpmExecPath({ ...options, env });
56
+ if (npmExecPath) env.MEMORAX_CODE_NPM_EXEC_PATH = npmExecPath;
57
+ }
58
+
44
59
  export async function runBackendEntrypoint(relativeEntrypoint) {
45
60
  if (!ensureSupportedNodeRuntime()) return;
46
61
  ensureCodexCommandEnv();
47
62
  ensureClaudeCommandEnv();
63
+ ensureCodeBuddyCommandEnv();
48
64
  ensureBundledSkillEnv();
49
65
  ensureClaudeMarketplaceEnv();
50
66
  ensureInstallWatchdogEnv();
67
+ ensureNpmPackageRuntimeEnv();
51
68
  const entrypoint = join(packageRoot, "lib", "memorax-code-backend", "dist", relativeEntrypoint);
52
69
  const previousArgv1 = process.argv[1];
53
70
  process.argv[1] = entrypoint;
@@ -80,6 +97,13 @@ export async function runOpenCodeAdapterCli() {
80
97
  await import(pathToFileURL(entrypoint).href);
81
98
  }
82
99
 
100
+ export async function runCodeBuddyAdapterCli() {
101
+ if (!ensureSupportedNodeRuntime()) return;
102
+ ensureCodeBuddyCommandEnv();
103
+ const entrypoint = join(packageRoot, "lib", "memorax-code-codebuddy-adapter", "src", "cli.mjs");
104
+ await import(pathToFileURL(entrypoint).href);
105
+ }
106
+
83
107
  function ensureSupportedNodeRuntime() {
84
108
  const message = unsupportedNodeVersionMessage();
85
109
  if (!message) return true;
@@ -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
  }
@@ -92,9 +95,16 @@ export function selectWindowsCommandCandidate(command, output) {
92
95
  .map((value) => value.trim())
93
96
  .filter(Boolean);
94
97
  const selected = candidates.find((value) => /\.(?:cmd|bat)$/i.test(value))
95
- ?? candidates.find((value) => /\.(?:exe|com)$/i.test(value));
98
+ ?? candidates.find((value) => /\.(?:exe|com)$/i.test(value))
99
+ ?? (path.win32.basename(command).toLowerCase() === "codebuddy"
100
+ ? candidates.find((value) => isBareCodeBuddyEntrypoint(value, path.win32))
101
+ : undefined);
96
102
  if (!selected) {
97
103
  throw new Error(`where.exe did not return a safe executable or command shim for ${command}`);
98
104
  }
99
105
  return selected;
100
106
  }
107
+
108
+ function isBareCodeBuddyEntrypoint(value, pathApi) {
109
+ return pathApi.basename(value).toLowerCase() === "codebuddy" && pathApi.extname(value) === "";
110
+ }