@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
|
@@ -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
|
+
}
|
|
@@ -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)
|
|
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.
|
|
3
|
+
"version": "0.1.10",
|
|
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",
|
package/lib/npm-invocation.mjs
CHANGED
|
@@ -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) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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,112 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { delimiter, join, win32 } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
commandOnPath,
|
|
5
|
+
isExecutableCommand,
|
|
6
|
+
} from "./vscode-extension-command.mjs";
|
|
7
|
+
|
|
8
|
+
const WORKBUDDY_APP_NAMES = ["WorkBuddy.app", "CodeBuddy.app"];
|
|
9
|
+
const WORKBUDDY_BUNDLED_SEGMENTS = [
|
|
10
|
+
"Contents",
|
|
11
|
+
"Resources",
|
|
12
|
+
"app.asar.unpacked",
|
|
13
|
+
"cli",
|
|
14
|
+
"bin",
|
|
15
|
+
"codebuddy",
|
|
16
|
+
];
|
|
17
|
+
const WINDOWS_WORKBUDDY_SEGMENTS = [
|
|
18
|
+
["resources", "app.asar.unpacked", "cli", "bin", "codebuddy"],
|
|
19
|
+
["resources", "app.asar.unpacked", "cli", "bin", "codebuddy.exe"],
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export function resolveCodeBuddyCommand({
|
|
23
|
+
env = process.env,
|
|
24
|
+
homeDir = homedir(),
|
|
25
|
+
platform = process.platform,
|
|
26
|
+
applicationRoots = [join(homeDir, "Applications"), "/Applications"],
|
|
27
|
+
windowsRoots = defaultWindowsRoots(env, homeDir),
|
|
28
|
+
pathExists = isExecutableCommand,
|
|
29
|
+
} = {}) {
|
|
30
|
+
const override = nonEmpty(env.MEMORAX_CODE_CODEBUDDY_COMMAND)
|
|
31
|
+
?? nonEmpty(env.CODEBUDDY_CLI_PATH)
|
|
32
|
+
?? nonEmpty(env.WORKBUDDY_CODEBUDDY_PATH);
|
|
33
|
+
if (override) return { command: override, source: "configured" };
|
|
34
|
+
|
|
35
|
+
if (commandOnPath("codebuddy", env.PATH, platform, env.PATHEXT)) {
|
|
36
|
+
return { command: findCommandOnPath("codebuddy", env.PATH, platform, env.PATHEXT), source: "path" };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (platform === "darwin") {
|
|
40
|
+
for (const root of applicationRoots) {
|
|
41
|
+
for (const appName of WORKBUDDY_APP_NAMES) {
|
|
42
|
+
const command = join(root, appName, ...WORKBUDDY_BUNDLED_SEGMENTS);
|
|
43
|
+
if (pathExists(command, platform)) return { command, source: "app-bundled" };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (platform === "win32") {
|
|
49
|
+
for (const root of windowsRoots) {
|
|
50
|
+
for (const segments of WINDOWS_WORKBUDDY_SEGMENTS) {
|
|
51
|
+
const command = win32.join(root, ...segments);
|
|
52
|
+
if (pathExists(command, platform)) return { command, source: "app-bundled" };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { command: "codebuddy", source: "unavailable" };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function findCommandOnPath(command, pathValue, platform, pathExtValue) {
|
|
61
|
+
const extensions = platform === "win32"
|
|
62
|
+
? String(pathExtValue ?? ".EXE;.CMD;.BAT;.COM").split(";")
|
|
63
|
+
: [""];
|
|
64
|
+
const pathDelimiter = platform === "win32" ? ";" : delimiter;
|
|
65
|
+
const pathJoin = platform === "win32" ? win32.join : join;
|
|
66
|
+
for (const root of String(pathValue ?? "").split(pathDelimiter)) {
|
|
67
|
+
if (!root) continue;
|
|
68
|
+
for (const extension of extensions) {
|
|
69
|
+
const candidate = pathJoin(root, `${command}${extension}`);
|
|
70
|
+
if (isExecutableCommand(candidate, platform)) return candidate;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return platform === "win32" ? "codebuddy.exe" : "codebuddy";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function ensureCodeBuddyCommandEnv(options = {}) {
|
|
77
|
+
const env = options.env ?? process.env;
|
|
78
|
+
const resolved = resolveCodeBuddyCommand({ ...options, env });
|
|
79
|
+
if (resolved.source !== "unavailable") env.MEMORAX_CODE_CODEBUDDY_COMMAND = resolved.command;
|
|
80
|
+
return resolved;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function defaultCodeBuddyHome(env = process.env, homeDir = homedir(), platform = process.platform) {
|
|
84
|
+
return nonEmpty(env.CODEBUDDY_HOME)
|
|
85
|
+
?? nonEmpty(env.WORKBUDDY_HOME)
|
|
86
|
+
?? (platform === "win32" ? win32.join(homeDir, ".codebuddy") : join(homeDir, ".workbuddy"));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function defaultWindowsRoots(env, homeDir) {
|
|
90
|
+
const roots = [];
|
|
91
|
+
const localAppData = nonEmpty(env.LOCALAPPDATA) ?? join(homeDir, "AppData", "Local");
|
|
92
|
+
const programFiles = nonEmpty(env.ProgramFiles) ?? "C:\\Program Files";
|
|
93
|
+
const programFilesX86 = nonEmpty(env["ProgramFiles(x86)"]) ?? "C:\\Program Files (x86)";
|
|
94
|
+
for (const root of [
|
|
95
|
+
win32.join(localAppData, "Programs", "WorkBuddy"),
|
|
96
|
+
win32.join(localAppData, "Programs", "CodeBuddy"),
|
|
97
|
+
win32.join(localAppData, "WorkBuddy"),
|
|
98
|
+
win32.join(localAppData, "CodeBuddy"),
|
|
99
|
+
win32.join(programFiles, "WorkBuddy"),
|
|
100
|
+
win32.join(programFiles, "CodeBuddy"),
|
|
101
|
+
win32.join(programFilesX86, "WorkBuddy"),
|
|
102
|
+
win32.join(programFilesX86, "CodeBuddy"),
|
|
103
|
+
]) {
|
|
104
|
+
if (!roots.includes(root)) roots.push(root);
|
|
105
|
+
}
|
|
106
|
+
return roots;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function nonEmpty(value) {
|
|
110
|
+
const normalized = String(value ?? "").trim();
|
|
111
|
+
return normalized || undefined;
|
|
112
|
+
}
|
package/lib/run-entrypoint.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
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
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { resolveNpmInvocation } from "./npm-invocation.mjs";
|
|
5
|
+
|
|
6
|
+
const WINDOWS_USER_PATH_SCRIPT = String.raw`
|
|
7
|
+
$ErrorActionPreference = "Stop"
|
|
8
|
+
$globalBin = $env:MEMORAX_CODE_WINDOWS_NPM_GLOBAL_BIN
|
|
9
|
+
|
|
10
|
+
function Normalize-PathEntry([string]$Value) {
|
|
11
|
+
if ([string]::IsNullOrWhiteSpace($Value)) { return "" }
|
|
12
|
+
return [Environment]::ExpandEnvironmentVariables(
|
|
13
|
+
$Value.Trim().Trim('"')
|
|
14
|
+
).TrimEnd('\')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
$normalizedGlobalBin = Normalize-PathEntry $globalBin
|
|
18
|
+
$changed = $false
|
|
19
|
+
$completed = $false
|
|
20
|
+
|
|
21
|
+
for ($attempt = 0; $attempt -lt 3; $attempt++) {
|
|
22
|
+
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
23
|
+
$entries = @($userPath -split ";" | Where-Object {
|
|
24
|
+
-not [string]::IsNullOrWhiteSpace($_)
|
|
25
|
+
})
|
|
26
|
+
$present = @($entries | Where-Object {
|
|
27
|
+
(Normalize-PathEntry $_) -ieq $normalizedGlobalBin
|
|
28
|
+
}).Count -gt 0
|
|
29
|
+
|
|
30
|
+
if ($present) {
|
|
31
|
+
$completed = $true
|
|
32
|
+
break
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
$separator = if ([string]::IsNullOrWhiteSpace($userPath) -or $userPath.TrimEnd().EndsWith(";")) { "" } else { ";" }
|
|
36
|
+
$updatedPath = "$userPath$separator$globalBin"
|
|
37
|
+
if ($updatedPath.Length -ge 32767) {
|
|
38
|
+
throw "The updated user PATH would exceed the Windows environment limit."
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
$latestUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
42
|
+
if (-not [string]::Equals([string]$latestUserPath, [string]$userPath, [StringComparison]::Ordinal)) {
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[Environment]::SetEnvironmentVariable("Path", $updatedPath, "User")
|
|
47
|
+
$changed = $true
|
|
48
|
+
$writtenUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
49
|
+
if ([string]::Equals([string]$writtenUserPath, [string]$updatedPath, [StringComparison]::Ordinal)) {
|
|
50
|
+
$completed = $true
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (-not $completed) {
|
|
56
|
+
throw "The Windows user PATH changed concurrently; retry setup."
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[Console]::Out.Write((@{ changed = $changed } | ConvertTo-Json -Compress))
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
export function ensureWindowsNpmGlobalPath(options = {}) {
|
|
63
|
+
const platform = options.platform ?? process.platform;
|
|
64
|
+
const env = options.env ?? process.env;
|
|
65
|
+
if (platform !== "win32") return result("skipped", "unsupported_platform");
|
|
66
|
+
|
|
67
|
+
let globalBin;
|
|
68
|
+
try {
|
|
69
|
+
globalBin = (options.resolveGlobalPrefix ?? resolveWindowsNpmGlobalPrefix)({
|
|
70
|
+
...options,
|
|
71
|
+
env,
|
|
72
|
+
platform,
|
|
73
|
+
});
|
|
74
|
+
} catch {
|
|
75
|
+
return result("warning", "npm_prefix_unavailable");
|
|
76
|
+
}
|
|
77
|
+
if (typeof globalBin !== "string"
|
|
78
|
+
|| globalBin.includes(";")
|
|
79
|
+
|| !path.win32.isAbsolute(globalBin.trim())) {
|
|
80
|
+
return result("warning", "npm_prefix_unavailable");
|
|
81
|
+
}
|
|
82
|
+
globalBin = path.win32.normalize(globalBin.trim());
|
|
83
|
+
|
|
84
|
+
const fileExists = options.existsSync ?? existsSync;
|
|
85
|
+
const requiredShims = ["memorax-code.cmd", "memorax-cli.cmd"];
|
|
86
|
+
if (!requiredShims.every((name) => fileExists(path.win32.join(globalBin, name)))) {
|
|
87
|
+
return result("warning", "global_shims_missing");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const pathKey = windowsPathKey(env);
|
|
91
|
+
const currentPath = typeof env[pathKey] === "string" ? env[pathKey] : "";
|
|
92
|
+
const processPathChanged = !windowsPathContains(currentPath, globalBin, env);
|
|
93
|
+
if (processPathChanged) {
|
|
94
|
+
env[pathKey] = currentPath
|
|
95
|
+
? `${currentPath}${currentPath.trimEnd().endsWith(";") ? "" : ";"}${globalBin}`
|
|
96
|
+
: globalBin;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let userPathChanged = false;
|
|
100
|
+
try {
|
|
101
|
+
const update = (options.updateUserPath ?? updateWindowsUserPath)(globalBin, {
|
|
102
|
+
...options,
|
|
103
|
+
env,
|
|
104
|
+
platform,
|
|
105
|
+
});
|
|
106
|
+
if (typeof update?.changed !== "boolean") throw new Error("invalid user PATH update result");
|
|
107
|
+
userPathChanged = update.changed;
|
|
108
|
+
} catch {
|
|
109
|
+
return result("warning", "user_path_update_failed", {
|
|
110
|
+
processPathChanged,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const changed = processPathChanged || userPathChanged;
|
|
115
|
+
return result(changed ? "updated" : "unchanged", undefined, {
|
|
116
|
+
processPathChanged,
|
|
117
|
+
userPathChanged,
|
|
118
|
+
restartRecommended: changed,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function resolveWindowsNpmGlobalPrefix(options = {}) {
|
|
123
|
+
const env = options.env ?? process.env;
|
|
124
|
+
const invocation = resolveNpmInvocation(["prefix", "-g"], options);
|
|
125
|
+
const run = options.spawnSync ?? spawnSync;
|
|
126
|
+
const resolved = run(invocation.command, invocation.args, {
|
|
127
|
+
encoding: "utf8",
|
|
128
|
+
env,
|
|
129
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
130
|
+
timeout: 10_000,
|
|
131
|
+
windowsHide: true,
|
|
132
|
+
});
|
|
133
|
+
if (resolved.error || resolved.signal || resolved.status !== 0) {
|
|
134
|
+
throw new Error("npm global prefix command failed");
|
|
135
|
+
}
|
|
136
|
+
const lines = String(resolved.stdout ?? "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
137
|
+
const prefix = lines.at(-1);
|
|
138
|
+
if (!prefix || !path.win32.isAbsolute(prefix)) throw new Error("npm global prefix is invalid");
|
|
139
|
+
return prefix;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function updateWindowsUserPath(globalBin, options = {}) {
|
|
143
|
+
const env = options.env ?? process.env;
|
|
144
|
+
const run = options.spawnSync ?? spawnSync;
|
|
145
|
+
const powerShell = windowsPowerShellCommand(env, options.existsSync ?? existsSync);
|
|
146
|
+
const encodedCommand = Buffer.from(WINDOWS_USER_PATH_SCRIPT, "utf16le").toString("base64");
|
|
147
|
+
const updated = run(powerShell, [
|
|
148
|
+
"-NoLogo",
|
|
149
|
+
"-NoProfile",
|
|
150
|
+
"-NonInteractive",
|
|
151
|
+
"-EncodedCommand",
|
|
152
|
+
encodedCommand,
|
|
153
|
+
], {
|
|
154
|
+
encoding: "utf8",
|
|
155
|
+
env: { ...env, MEMORAX_CODE_WINDOWS_NPM_GLOBAL_BIN: globalBin },
|
|
156
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
157
|
+
timeout: 10_000,
|
|
158
|
+
windowsHide: true,
|
|
159
|
+
});
|
|
160
|
+
if (updated.error || updated.signal || updated.status !== 0) {
|
|
161
|
+
throw new Error("Windows user PATH update failed");
|
|
162
|
+
}
|
|
163
|
+
let parsed;
|
|
164
|
+
try {
|
|
165
|
+
parsed = JSON.parse(String(updated.stdout ?? "").trim());
|
|
166
|
+
} catch {
|
|
167
|
+
throw new Error("Windows user PATH update returned invalid output");
|
|
168
|
+
}
|
|
169
|
+
if (typeof parsed?.changed !== "boolean") {
|
|
170
|
+
throw new Error("Windows user PATH update returned an invalid result");
|
|
171
|
+
}
|
|
172
|
+
return { changed: parsed.changed };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function windowsPathKey(env) {
|
|
176
|
+
return Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "Path";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function windowsPathContains(pathValue, expected, env) {
|
|
180
|
+
const normalizedExpected = normalizeWindowsPathEntry(expected, env);
|
|
181
|
+
return String(pathValue ?? "").split(";").some((entry) => (
|
|
182
|
+
normalizeWindowsPathEntry(entry, env) === normalizedExpected
|
|
183
|
+
));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function normalizeWindowsPathEntry(value, env) {
|
|
187
|
+
const expanded = String(value ?? "")
|
|
188
|
+
.trim()
|
|
189
|
+
.replace(/^"|"$/g, "")
|
|
190
|
+
.replace(/%([^%]+)%/g, (match, name) => environmentValue(env, name) ?? match);
|
|
191
|
+
if (!expanded) return "";
|
|
192
|
+
return path.win32.normalize(expanded).replace(/[\\/]+$/, "").toLowerCase();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function environmentValue(env, name) {
|
|
196
|
+
const key = Object.keys(env).find((candidate) => candidate.toLowerCase() === name.toLowerCase());
|
|
197
|
+
return key === undefined ? undefined : env[key];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function windowsPowerShellCommand(env, fileExists) {
|
|
201
|
+
const systemRoot = environmentValue(env, "SystemRoot") ?? environmentValue(env, "WINDIR");
|
|
202
|
+
if (systemRoot) {
|
|
203
|
+
const candidate = path.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
|
|
204
|
+
if (fileExists(candidate)) return candidate;
|
|
205
|
+
}
|
|
206
|
+
return "powershell.exe";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function result(status, reason, overrides = {}) {
|
|
210
|
+
return {
|
|
211
|
+
status,
|
|
212
|
+
...(reason ? { reason } : {}),
|
|
213
|
+
processPathChanged: false,
|
|
214
|
+
userPathChanged: false,
|
|
215
|
+
restartRecommended: false,
|
|
216
|
+
...overrides,
|
|
217
|
+
};
|
|
218
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memorax/memorax-code",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Persistent coding memory for Codex, Claude Code, DeepSeek Harness, and OpenCode.",
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"description": "Persistent coding memory for Codex, Claude Code, CodeBuddy/WorkBuddy, DeepSeek Harness, and OpenCode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/memorax-ai/memorax-code#readme",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"deepseek-harness",
|
|
19
19
|
"dsh",
|
|
20
20
|
"opencode",
|
|
21
|
+
"codebuddy",
|
|
21
22
|
"memory",
|
|
22
23
|
"memorax"
|
|
23
24
|
],
|
|
@@ -27,7 +28,8 @@
|
|
|
27
28
|
"memorax-code-backend": "bin/memorax-code-backend.mjs",
|
|
28
29
|
"memorax-code-claude": "bin/memorax-code-claude.mjs",
|
|
29
30
|
"memorax-code-codex": "bin/memorax-code-codex.mjs",
|
|
30
|
-
"memorax-code-opencode": "bin/memorax-code-opencode.mjs"
|
|
31
|
+
"memorax-code-opencode": "bin/memorax-code-opencode.mjs",
|
|
32
|
+
"memorax-code-codebuddy": "bin/memorax-code-codebuddy.mjs"
|
|
31
33
|
},
|
|
32
34
|
"files": [
|
|
33
35
|
"bin",
|