@memorax/memorax-code 0.1.9 → 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/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,133 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { AUTOMATIC_UPDATE_RETRY_INTERVAL_MS, readAutomaticUpdateState, } from "../../../memorax-code-adapter-common/src/automatic-update-state.mjs";
|
|
5
|
+
import { readSetupCompletionRecord, } from "../../../memorax-code-adapter-common/src/setup-completion.mjs";
|
|
6
|
+
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
7
|
+
export function startBackendAutomaticUpdateScheduler(options, runtime = {}) {
|
|
8
|
+
const env = options.env ?? process.env;
|
|
9
|
+
if (automaticUpdateDisabled(env.MEMORAX_CODE_AUTO_UPDATE))
|
|
10
|
+
return undefined;
|
|
11
|
+
const packageRoot = nonEmptyString(options.packageRoot);
|
|
12
|
+
const installedVersion = nonEmptyString(options.packageVersion);
|
|
13
|
+
if (!packageRoot || !installedVersion)
|
|
14
|
+
return undefined;
|
|
15
|
+
const memoraxCodeHome = resolve(options.memoraxCodeHome);
|
|
16
|
+
const memoraxCodeCommand = join(resolve(packageRoot), "bin", "memorax-code.mjs");
|
|
17
|
+
if (!(runtime.existsSync ?? existsSync)(memoraxCodeCommand))
|
|
18
|
+
return undefined;
|
|
19
|
+
const readCompletion = runtime.readSetupCompletionRecord ?? readSetupCompletionRecord;
|
|
20
|
+
const readUpdateState = runtime.readAutomaticUpdateState ?? readAutomaticUpdateState;
|
|
21
|
+
const spawnProcess = runtime.spawnProcess ?? spawn;
|
|
22
|
+
const now = runtime.now ?? Date.now;
|
|
23
|
+
const setTimer = runtime.setTimeout
|
|
24
|
+
?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
25
|
+
const clearTimer = runtime.clearTimeout
|
|
26
|
+
?? ((handle) => clearTimeout(handle));
|
|
27
|
+
let closed = false;
|
|
28
|
+
let childRunning = false;
|
|
29
|
+
let timer;
|
|
30
|
+
let suppressImmediate = env.MEMORAX_CODE_AUTOMATIC_UPDATE_PROCESS === "1";
|
|
31
|
+
const debug = (message) => options.debug?.(message);
|
|
32
|
+
const scheduleAfter = (delayMs) => {
|
|
33
|
+
if (closed)
|
|
34
|
+
return;
|
|
35
|
+
if (timer !== undefined)
|
|
36
|
+
clearTimer(timer);
|
|
37
|
+
timer = setTimer(() => {
|
|
38
|
+
timer = undefined;
|
|
39
|
+
reconcile();
|
|
40
|
+
}, Math.max(1, Math.min(MAX_TIMER_DELAY_MS, Math.trunc(delayMs))));
|
|
41
|
+
timer.unref?.();
|
|
42
|
+
};
|
|
43
|
+
const scheduleRetry = () => scheduleAfter(AUTOMATIC_UPDATE_RETRY_INTERVAL_MS);
|
|
44
|
+
const launch = () => {
|
|
45
|
+
let child;
|
|
46
|
+
try {
|
|
47
|
+
child = spawnProcess(process.execPath, [memoraxCodeCommand, "update", "--automatic", "--home", memoraxCodeHome], {
|
|
48
|
+
detached: true,
|
|
49
|
+
env: {
|
|
50
|
+
...env,
|
|
51
|
+
MEMORAX_CODE_AUTOMATIC_UPDATE_PROCESS: "1",
|
|
52
|
+
MEMORAX_CODE_HOME: memoraxCodeHome,
|
|
53
|
+
},
|
|
54
|
+
stdio: "ignore",
|
|
55
|
+
windowsHide: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
debug(`MemoraX Code automatic update could not start: ${errorMessage(error)}`);
|
|
60
|
+
scheduleRetry();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
childRunning = true;
|
|
64
|
+
let settled = false;
|
|
65
|
+
const finish = (successful) => {
|
|
66
|
+
if (settled)
|
|
67
|
+
return;
|
|
68
|
+
settled = true;
|
|
69
|
+
childRunning = false;
|
|
70
|
+
if (!closed)
|
|
71
|
+
reconcile(!successful);
|
|
72
|
+
};
|
|
73
|
+
child.once("error", (error) => {
|
|
74
|
+
debug(`MemoraX Code automatic update could not start: ${error.message}`);
|
|
75
|
+
finish(false);
|
|
76
|
+
});
|
|
77
|
+
child.once("close", (code, signal) => finish(signal === null && code === 0));
|
|
78
|
+
child.unref();
|
|
79
|
+
};
|
|
80
|
+
function reconcile(deferDue = false) {
|
|
81
|
+
if (closed || childRunning)
|
|
82
|
+
return;
|
|
83
|
+
const suppressDue = suppressImmediate;
|
|
84
|
+
suppressImmediate = false;
|
|
85
|
+
try {
|
|
86
|
+
const completion = readCompletion(memoraxCodeHome);
|
|
87
|
+
const state = readUpdateState(memoraxCodeHome);
|
|
88
|
+
const nowMs = Number(now());
|
|
89
|
+
if (!Number.isFinite(nowMs))
|
|
90
|
+
throw new TypeError("automatic update scheduler clock is invalid");
|
|
91
|
+
if (completion.status !== "valid") {
|
|
92
|
+
scheduleRetry();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (state.status === "valid" && state.record.installedVersion === installedVersion) {
|
|
96
|
+
const remainingMs = Date.parse(state.record.nextCheckAt) - nowMs;
|
|
97
|
+
if (remainingMs > 0) {
|
|
98
|
+
scheduleAfter(remainingMs);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
debug(`MemoraX Code automatic update scheduling failed: ${errorMessage(error)}`);
|
|
105
|
+
scheduleRetry();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (suppressDue || deferDue)
|
|
109
|
+
scheduleRetry();
|
|
110
|
+
else
|
|
111
|
+
launch();
|
|
112
|
+
}
|
|
113
|
+
reconcile();
|
|
114
|
+
return {
|
|
115
|
+
close() {
|
|
116
|
+
if (closed)
|
|
117
|
+
return;
|
|
118
|
+
closed = true;
|
|
119
|
+
if (timer !== undefined)
|
|
120
|
+
clearTimer(timer);
|
|
121
|
+
timer = undefined;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function automaticUpdateDisabled(value) {
|
|
126
|
+
return ["0", "false", "no", "off", "disabled"].includes(String(value ?? "").trim().toLowerCase());
|
|
127
|
+
}
|
|
128
|
+
function nonEmptyString(value) {
|
|
129
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
130
|
+
}
|
|
131
|
+
function errorMessage(error) {
|
|
132
|
+
return error instanceof Error ? error.message : String(error);
|
|
133
|
+
}
|
|
@@ -8,6 +8,7 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
|
|
|
8
8
|
const claudePluginInstaller = await loadClaudePluginInstaller();
|
|
9
9
|
const dshProfileLifecycle = await loadDshProfileLifecycle();
|
|
10
10
|
const openCodePluginInstaller = await loadOpenCodePluginInstaller();
|
|
11
|
+
const codeBuddyPluginInstaller = await loadCodeBuddyPluginInstaller();
|
|
11
12
|
const home = resolveHome(options.homeDir);
|
|
12
13
|
const memoraxCodeHome = resolve(options.memoraxCodeHome ?? process.env.MEMORAX_CODE_HOME ?? join(home, ".memorax-code"));
|
|
13
14
|
const claudeState = await readJsonRecord(join(memoraxCodeHome, "adapters", "claude-code", "state.json"));
|
|
@@ -15,7 +16,7 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
|
|
|
15
16
|
return async () => {
|
|
16
17
|
try {
|
|
17
18
|
return await withBackendLifecycleLock({ home: memoraxCodeHome }, async () => {
|
|
18
|
-
const [codexPlugin, claudePlugin, dshPlugin, opencodePlugin] = await Promise.all([
|
|
19
|
+
const [codexPlugin, claudePlugin, dshPlugin, opencodePlugin, codebuddyPlugin] = await Promise.all([
|
|
19
20
|
cleanupCodexAfterBackendRemoval({
|
|
20
21
|
memoraxCodeHome,
|
|
21
22
|
homeDir: home,
|
|
@@ -45,14 +46,21 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
|
|
|
45
46
|
: {}),
|
|
46
47
|
}))
|
|
47
48
|
.catch((error) => removalFailure("opencode-plugin-remove", error)),
|
|
49
|
+
Promise.resolve()
|
|
50
|
+
.then(() => codeBuddyPluginInstaller.removeCodeBuddyPluginInstallation({
|
|
51
|
+
memoraxCodeHome,
|
|
52
|
+
...(options.codeBuddyHome ? { codeBuddyHome: options.codeBuddyHome } : {}),
|
|
53
|
+
}))
|
|
54
|
+
.catch((error) => removalFailure("codebuddy-plugin-remove", error)),
|
|
48
55
|
]);
|
|
49
56
|
return {
|
|
50
|
-
ok: codexPlugin.ok && claudePlugin.ok && dshPlugin.ok && opencodePlugin.ok,
|
|
57
|
+
ok: codexPlugin.ok && claudePlugin.ok && dshPlugin.ok && opencodePlugin.ok && codebuddyPlugin.ok,
|
|
51
58
|
action: "client-plugin-removal-cleanup",
|
|
52
59
|
codexPlugin,
|
|
53
60
|
claudePlugin,
|
|
54
61
|
dshPlugin,
|
|
55
62
|
opencodePlugin,
|
|
63
|
+
codebuddyPlugin,
|
|
56
64
|
};
|
|
57
65
|
});
|
|
58
66
|
}
|
|
@@ -65,6 +73,7 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
|
|
|
65
73
|
claudePlugin: failure,
|
|
66
74
|
dshPlugin: failure,
|
|
67
75
|
opencodePlugin: failure,
|
|
76
|
+
codebuddyPlugin: failure,
|
|
68
77
|
};
|
|
69
78
|
}
|
|
70
79
|
};
|
|
@@ -75,6 +84,9 @@ async function loadClaudePluginInstaller() {
|
|
|
75
84
|
async function loadOpenCodePluginInstaller() {
|
|
76
85
|
return await import(new URL("../../../memorax-code-opencode-adapter/src/plugin-install.mjs", import.meta.url).href);
|
|
77
86
|
}
|
|
87
|
+
async function loadCodeBuddyPluginInstaller() {
|
|
88
|
+
return await import(new URL("../../../memorax-code-codebuddy-adapter/src/config.mjs", import.meta.url).href);
|
|
89
|
+
}
|
|
78
90
|
async function loadDshProfileLifecycle() {
|
|
79
91
|
return await import(new URL("../../../memorax-code-dsh-adapter/src/profile-lifecycle.mjs", import.meta.url).href);
|
|
80
92
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadLifecycleMemoraxCodeConfig } from "../config/memorax-code.js";
|
|
2
|
-
const allClients = Object.freeze({ codex: true, claude: true, dsh: true, opencode: true });
|
|
2
|
+
const allClients = Object.freeze({ codex: true, claude: true, dsh: true, opencode: true, codebuddy: true });
|
|
3
3
|
export function resolveManagedClients(argv, config = {}) {
|
|
4
4
|
const explicit = argValue(argv, "--clients");
|
|
5
5
|
if (explicit !== undefined)
|
|
@@ -12,9 +12,10 @@ export function resolveManagedClients(argv, config = {}) {
|
|
|
12
12
|
// automatic local-Harness discovery enabled for existing installations.
|
|
13
13
|
dsh: config.clients.dsh !== false,
|
|
14
14
|
opencode: config.clients.opencode === true,
|
|
15
|
+
...(config.clients.codebuddy === true ? { codebuddy: true } : {}),
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
|
-
return
|
|
18
|
+
return { codex: true, claude: true, dsh: true, opencode: true };
|
|
18
19
|
}
|
|
19
20
|
export function parseManagedClients(value) {
|
|
20
21
|
const normalized = value.trim().toLowerCase();
|
|
@@ -23,14 +24,15 @@ export function parseManagedClients(value) {
|
|
|
23
24
|
if (normalized === "none")
|
|
24
25
|
return { codex: false, claude: false, dsh: false, opencode: false };
|
|
25
26
|
const names = normalized.split(",").map((name) => name.trim()).filter(Boolean);
|
|
26
|
-
if (names.length === 0 || names.some((name) => (name !== "codex" && name !== "claude" && name !== "dsh" && name !== "opencode"))) {
|
|
27
|
-
throw new Error(`invalid --clients value: ${value}; expected a comma-separated subset of codex, claude, dsh, opencode, or all or none`);
|
|
27
|
+
if (names.length === 0 || names.some((name) => (name !== "codex" && name !== "claude" && name !== "dsh" && name !== "opencode" && name !== "codebuddy"))) {
|
|
28
|
+
throw new Error(`invalid --clients value: ${value}; expected a comma-separated subset of codex, claude, dsh, opencode, codebuddy, or all or none`);
|
|
28
29
|
}
|
|
29
30
|
return {
|
|
30
31
|
codex: names.includes("codex"),
|
|
31
32
|
claude: names.includes("claude"),
|
|
32
33
|
dsh: names.includes("dsh"),
|
|
33
34
|
opencode: names.includes("opencode"),
|
|
35
|
+
...(names.includes("codebuddy") ? { codebuddy: true } : {}),
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
export function loadManagedClientsConfig(memoraxCodeHome) {
|
|
@@ -17,6 +17,7 @@ import { codexAdapterLifecycle } from "../clients/codex/lifecycle.js";
|
|
|
17
17
|
import { claudeAdapterLifecycle } from "../clients/claude/lifecycle.js";
|
|
18
18
|
import { collectDshAdapterLifecycleStatus, withDshAdapterLifecycleLock, } from "../clients/dsh/lifecycle.js";
|
|
19
19
|
import { openCodeAdapterLifecycle } from "../clients/opencode/lifecycle.js";
|
|
20
|
+
import { codeBuddyAdapterLifecycle } from "../clients/codebuddy/lifecycle.js";
|
|
20
21
|
const PACKAGE_REPLACEMENT_ENV = "MEMORAX_CODE_PACKAGE_REPLACEMENT";
|
|
21
22
|
const DSH_OPTIONAL_ENV = "MEMORAX_CODE_DSH_ADAPTER_OPTIONAL";
|
|
22
23
|
const DSH_RECOVERY_ENV = "MEMORAX_CODE_DSH_ADAPTER_RECOVERY";
|
|
@@ -58,15 +59,20 @@ export async function collectMemoraxCodeStatus(backendUrl, backendToken, service
|
|
|
58
59
|
const opencodeAdapter = clients.opencode
|
|
59
60
|
? await openCodeAdapterLifecycle.status({ argv, serviceOptions, backendUrl })
|
|
60
61
|
: undefined;
|
|
62
|
+
const codebuddyAdapter = clients.codebuddy
|
|
63
|
+
? await codeBuddyAdapterLifecycle.status({ argv, serviceOptions, backendUrl })
|
|
64
|
+
: undefined;
|
|
61
65
|
const codexReady = codexAdapter ? isAdapterReady(codexAdapter) : true;
|
|
62
66
|
const claudeReady = claudeAdapter ? isAdapterReady(claudeAdapter) : true;
|
|
63
67
|
const dshReady = dshAdapter ? isAdapterReady(dshAdapter) : true;
|
|
64
68
|
const opencodeReady = opencodeAdapter ? isAdapterReady(opencodeAdapter) : true;
|
|
69
|
+
const codebuddyReady = codebuddyAdapter ? isAdapterReady(codebuddyAdapter) : true;
|
|
65
70
|
const optionalDshUnavailable = isOptionalUnavailableDshAdapter(dshAdapter);
|
|
66
71
|
return {
|
|
67
72
|
ok: backend.ok
|
|
68
73
|
&& codexReady
|
|
69
74
|
&& opencodeReady
|
|
75
|
+
&& codebuddyReady
|
|
70
76
|
&& (isOptionalUnconfiguredClaudeAdapter(claudeAdapter, codexAdapter) || claudeReady)
|
|
71
77
|
&& (optionalDshUnavailable || dshReady),
|
|
72
78
|
action: "status",
|
|
@@ -76,6 +82,7 @@ export async function collectMemoraxCodeStatus(backendUrl, backendToken, service
|
|
|
76
82
|
...(claudeAdapter ? { claudeAdapter } : {}),
|
|
77
83
|
...(dshAdapter ? { dshAdapter } : {}),
|
|
78
84
|
...(opencodeAdapter ? { opencodeAdapter } : {}),
|
|
85
|
+
...(codebuddyAdapter ? { codebuddyAdapter } : {}),
|
|
79
86
|
};
|
|
80
87
|
}
|
|
81
88
|
export async function startMemoraxCodeService(serviceOptions, argv, commitReadyState) {
|
|
@@ -193,10 +200,14 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
193
200
|
const deselectedOpenCode = previousClients?.opencode && !clients.opencode
|
|
194
201
|
? await openCodeAdapterLifecycle.disable({ argv, serviceOptions })
|
|
195
202
|
: undefined;
|
|
203
|
+
const deselectedCodeBuddy = previousClients?.codebuddy && !clients.codebuddy
|
|
204
|
+
? await codeBuddyAdapterLifecycle.disable({ argv, serviceOptions })
|
|
205
|
+
: undefined;
|
|
196
206
|
if (deselectedCodex?.ok === false
|
|
197
207
|
|| deselectedClaude?.ok === false
|
|
198
208
|
|| deselectedDsh?.ok === false
|
|
199
|
-
|| deselectedOpenCode?.ok === false
|
|
209
|
+
|| deselectedOpenCode?.ok === false
|
|
210
|
+
|| deselectedCodeBuddy?.ok === false) {
|
|
200
211
|
const recovery = await recoverPreparationFailure("adapter_disable_failed");
|
|
201
212
|
return {
|
|
202
213
|
ok: false,
|
|
@@ -208,6 +219,7 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
208
219
|
? { dshAdapter: recovery.dshAdapter ?? deselectedDsh }
|
|
209
220
|
: {}),
|
|
210
221
|
...(deselectedOpenCode ? { opencodeAdapter: deselectedOpenCode } : {}),
|
|
222
|
+
...(deselectedCodeBuddy ? { codebuddyAdapter: deselectedCodeBuddy } : {}),
|
|
211
223
|
};
|
|
212
224
|
}
|
|
213
225
|
// The marker is a conservative cleanup scope, not a readiness signal. Persist
|
|
@@ -247,6 +259,9 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
247
259
|
const opencodeAdapter = clients.opencode
|
|
248
260
|
? await openCodeAdapterLifecycle.prepareEnable({ argv, serviceOptions, backendUrl })
|
|
249
261
|
: undefined;
|
|
262
|
+
const codebuddyAdapter = clients.codebuddy
|
|
263
|
+
? await codeBuddyAdapterLifecycle.prepareEnable({ argv, serviceOptions, backendUrl })
|
|
264
|
+
: undefined;
|
|
250
265
|
if (opencodeAdapter?.ok === false) {
|
|
251
266
|
const recovery = await recoverPreparationFailure("opencode_adapter_enable_failed");
|
|
252
267
|
return {
|
|
@@ -257,6 +272,7 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
257
272
|
...(claudeAdapter ? { claudeAdapter } : {}),
|
|
258
273
|
...(recovery.dshAdapter ? { dshAdapter: recovery.dshAdapter } : {}),
|
|
259
274
|
opencodeAdapter,
|
|
275
|
+
...(codebuddyAdapter ? { codebuddyAdapter } : {}),
|
|
260
276
|
};
|
|
261
277
|
}
|
|
262
278
|
const preparedDshAdapter = markOptionalDshAdapter(quiescedDsh?.ok === false
|
|
@@ -274,6 +290,7 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
274
290
|
...(claudeAdapter ? { claudeAdapter } : {}),
|
|
275
291
|
dshAdapter: recovery.dshAdapter ?? preparedDshAdapter,
|
|
276
292
|
...(opencodeAdapter ? { opencodeAdapter } : {}),
|
|
293
|
+
...(codebuddyAdapter ? { codebuddyAdapter } : {}),
|
|
277
294
|
};
|
|
278
295
|
}
|
|
279
296
|
const backend = await startBackendService(serviceOptions);
|
|
@@ -284,6 +301,9 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
284
301
|
const disabledOpenCode = opencodeAdapter
|
|
285
302
|
? await openCodeAdapterLifecycle.disable({ argv, serviceOptions })
|
|
286
303
|
: undefined;
|
|
304
|
+
const disabledCodeBuddy = codebuddyAdapter
|
|
305
|
+
? await codeBuddyAdapterLifecycle.disable({ argv, serviceOptions })
|
|
306
|
+
: undefined;
|
|
287
307
|
return {
|
|
288
308
|
ok: false,
|
|
289
309
|
action: "start",
|
|
@@ -292,6 +312,7 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
292
312
|
...(claudeAdapter ? { claudeAdapter } : {}),
|
|
293
313
|
...(preparedDshAdapter ? { dshAdapter: preparedDshAdapter } : {}),
|
|
294
314
|
...(disabledOpenCode ? { opencodeAdapter: disabledOpenCode } : {}),
|
|
315
|
+
...(disabledCodeBuddy ? { codebuddyAdapter: disabledCodeBuddy } : {}),
|
|
295
316
|
};
|
|
296
317
|
}
|
|
297
318
|
const dshAdapter = markOptionalDshAdapter(preparedDshAdapter?.installed === true
|
|
@@ -308,6 +329,7 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
308
329
|
...(claudeAdapter ? { claudeAdapter } : {}),
|
|
309
330
|
...(dshAdapter ? { dshAdapter } : {}),
|
|
310
331
|
...(opencodeAdapter ? { opencodeAdapter } : {}),
|
|
332
|
+
...(codebuddyAdapter ? { codebuddyAdapter } : {}),
|
|
311
333
|
};
|
|
312
334
|
}
|
|
313
335
|
function expectedBackendUrl(serviceOptions) {
|
|
@@ -350,13 +372,15 @@ async function executeMemoraxCodeStop(serviceOptions, argv, context, dshLifecycl
|
|
|
350
372
|
claude: activeClients.claude && !clients.claude,
|
|
351
373
|
dsh: activeClients.dsh && !clients.dsh,
|
|
352
374
|
opencode: activeClients.opencode && !clients.opencode,
|
|
375
|
+
codebuddy: activeClients.codebuddy && !clients.codebuddy,
|
|
353
376
|
}
|
|
354
377
|
: undefined;
|
|
355
378
|
const hasRemainingClients = remaining?.codex === true
|
|
356
379
|
|| remaining?.claude === true
|
|
357
380
|
|| remaining?.dsh === true
|
|
358
|
-
|| remaining?.opencode === true
|
|
359
|
-
|
|
381
|
+
|| remaining?.opencode === true
|
|
382
|
+
|| remaining?.codebuddy === true;
|
|
383
|
+
const backendOnlyStop = !clients.codex && !clients.claude && !clients.dsh && !clients.opencode && !clients.codebuddy;
|
|
360
384
|
const packageReplacement = isPackageReplacement();
|
|
361
385
|
const needsBackendStop = packageReplacement || backendOnlyStop || !hasRemainingClients;
|
|
362
386
|
const quiescedDsh = clients.dsh && dshLifecycle
|
|
@@ -405,10 +429,14 @@ async function executeMemoraxCodeStop(serviceOptions, argv, context, dshLifecycl
|
|
|
405
429
|
const opencodeAdapter = clients.opencode
|
|
406
430
|
? await openCodeAdapterLifecycle.disable({ argv, serviceOptions })
|
|
407
431
|
: undefined;
|
|
432
|
+
const codebuddyAdapter = clients.codebuddy
|
|
433
|
+
? await codeBuddyAdapterLifecycle.disable({ argv, serviceOptions })
|
|
434
|
+
: undefined;
|
|
408
435
|
const adaptersOk = codexAdapter?.ok !== false
|
|
409
436
|
&& claudeAdapter?.ok !== false
|
|
410
437
|
&& dshAdapter?.ok !== false
|
|
411
|
-
&& opencodeAdapter?.ok !== false
|
|
438
|
+
&& opencodeAdapter?.ok !== false
|
|
439
|
+
&& codebuddyAdapter?.ok !== false;
|
|
412
440
|
const backend = stoppedBackend
|
|
413
441
|
?? (adaptersOk
|
|
414
442
|
? preservedBackendResult(serviceOptions, "active_clients_remaining")
|
|
@@ -417,7 +445,8 @@ async function executeMemoraxCodeStop(serviceOptions, argv, context, dshLifecycl
|
|
|
417
445
|
&& codexAdapter?.ok !== false
|
|
418
446
|
&& claudeAdapter?.ok !== false
|
|
419
447
|
&& dshAdapter?.ok !== false
|
|
420
|
-
&& opencodeAdapter?.ok !== false
|
|
448
|
+
&& opencodeAdapter?.ok !== false
|
|
449
|
+
&& codebuddyAdapter?.ok !== false;
|
|
421
450
|
return {
|
|
422
451
|
report: {
|
|
423
452
|
ok,
|
|
@@ -427,6 +456,7 @@ async function executeMemoraxCodeStop(serviceOptions, argv, context, dshLifecycl
|
|
|
427
456
|
...(claudeAdapter ? { claudeAdapter } : {}),
|
|
428
457
|
...(dshAdapter ? { dshAdapter } : {}),
|
|
429
458
|
...(opencodeAdapter ? { opencodeAdapter } : {}),
|
|
459
|
+
...(codebuddyAdapter ? { codebuddyAdapter } : {}),
|
|
430
460
|
},
|
|
431
461
|
remainingClients: packageReplacement
|
|
432
462
|
? activeClients
|
|
@@ -535,10 +565,14 @@ async function executeMemoraxCodeUninstall(serviceOptions, argv, context, dshLif
|
|
|
535
565
|
const opencodePlugin = clients.opencode
|
|
536
566
|
? await openCodeAdapterLifecycle.remove({ argv, serviceOptions })
|
|
537
567
|
: undefined;
|
|
568
|
+
const codebuddyPlugin = clients.codebuddy
|
|
569
|
+
? await codeBuddyAdapterLifecycle.remove({ argv, serviceOptions })
|
|
570
|
+
: undefined;
|
|
538
571
|
const pluginCleanupOk = codexPlugin?.ok !== false
|
|
539
572
|
&& claudePlugin?.ok !== false
|
|
540
573
|
&& dshPlugin?.ok !== false
|
|
541
|
-
&& opencodePlugin?.ok !== false
|
|
574
|
+
&& opencodePlugin?.ok !== false
|
|
575
|
+
&& codebuddyPlugin?.ok !== false;
|
|
542
576
|
const npmPackageRemoval = !pluginCleanupOk
|
|
543
577
|
? skippedNpmPackageRemoval("plugin_cleanup_failed")
|
|
544
578
|
: canRemoveSharedPackage
|
|
@@ -562,11 +596,13 @@ async function executeMemoraxCodeUninstall(serviceOptions, argv, context, dshLif
|
|
|
562
596
|
...(claudeAdapter ? { claudeAdapter } : {}),
|
|
563
597
|
...(dshAdapter ? { dshAdapter } : {}),
|
|
564
598
|
...(opencodeAdapter ? { opencodeAdapter } : {}),
|
|
599
|
+
...(codebuddyPlugin ? { codebuddyPlugin } : {}),
|
|
565
600
|
npmPackageRemoval,
|
|
566
601
|
removesPlugin: codexPlugin?.ok === true
|
|
567
602
|
|| claudePlugin?.ok === true
|
|
568
603
|
|| (dshPlugin?.ok === true && dshPlugin.skipped !== true)
|
|
569
|
-
|| opencodePlugin?.ok === true
|
|
604
|
+
|| opencodePlugin?.ok === true
|
|
605
|
+
|| codebuddyPlugin?.ok === true,
|
|
570
606
|
removesUserState: false,
|
|
571
607
|
};
|
|
572
608
|
}
|
|
@@ -691,7 +727,8 @@ function includesManagedClients(selection, required) {
|
|
|
691
727
|
return (!required.codex || selection.codex)
|
|
692
728
|
&& (!required.claude || selection.claude)
|
|
693
729
|
&& (!required.dsh || selection.dsh)
|
|
694
|
-
&& (!required.opencode || selection.opencode)
|
|
730
|
+
&& (!required.opencode || selection.opencode)
|
|
731
|
+
&& (!required.codebuddy || selection.codebuddy === true);
|
|
695
732
|
}
|
|
696
733
|
function isPackageReplacement() {
|
|
697
734
|
return truthyEnv(process.env[PACKAGE_REPLACEMENT_ENV]);
|
|
@@ -819,7 +856,9 @@ export function isAdapterReady(report) {
|
|
|
819
856
|
&& report.backendUrlMatches !== false
|
|
820
857
|
&& report.codexSkills?.ok !== false
|
|
821
858
|
&& report.claudeSkills?.ok !== false
|
|
822
|
-
&& report.opencodeSkills?.ok !== false
|
|
859
|
+
&& report.opencodeSkills?.ok !== false
|
|
860
|
+
&& report.codebuddyHooks?.ok !== false
|
|
861
|
+
&& report.codebuddySkills?.ok !== false;
|
|
823
862
|
}
|
|
824
863
|
export function isOptionalUnavailableDshAdapter(report) {
|
|
825
864
|
return Boolean(report?.optional === true && !isAdapterReady(report));
|
|
@@ -238,9 +238,9 @@ async function resolveMemoryCliRepositoryMemory(options) {
|
|
|
238
238
|
}
|
|
239
239
|
function memoryCliRepositoryFailure(action, failure, fields = {}) {
|
|
240
240
|
const userAction = failure.reason === "workspace_scope_mismatch"
|
|
241
|
-
? "Start a new Codex, Claude Code, DSH, or OpenCode session from the target repository or local workspace."
|
|
241
|
+
? "Start a new Codex, Claude Code, WorkBuddy, DSH, or OpenCode session from the target repository or local workspace."
|
|
242
242
|
: failure.reason === "workspace_scope_unavailable"
|
|
243
|
-
? "Start a new Codex, Claude Code, DSH, or OpenCode session from the target repository or local workspace. If the problem continues, make sure its .git metadata is readable and valid."
|
|
243
|
+
? "Start a new Codex, Claude Code, WorkBuddy, DSH, or OpenCode session from the target repository or local workspace. If the problem continues, make sure its .git metadata is readable and valid."
|
|
244
244
|
: undefined;
|
|
245
245
|
return {
|
|
246
246
|
ok: false,
|
|
@@ -336,6 +336,8 @@ function traceClientLabel(client) {
|
|
|
336
336
|
return "DSH";
|
|
337
337
|
if (client === "opencode")
|
|
338
338
|
return "OpenCode";
|
|
339
|
+
if (client === "codebuddy")
|
|
340
|
+
return "WorkBuddy";
|
|
339
341
|
return client === "codex" ? "Codex" : "coding agent";
|
|
340
342
|
}
|
|
341
343
|
function memoryAddContentOptions(args) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isRecord } from "../shared/record.js";
|
|
2
|
+
import { codeBuddyPromptDigest, parseCodeBuddyTurnId } from "../clients/codebuddy/turn-id.js";
|
|
2
3
|
export const MEMORY_HOOK_COMMAND_VERSION = 1;
|
|
3
4
|
export const INVALID_MEMORY_HOOK_COMMAND = "invalid memory Hook command";
|
|
4
5
|
const BASE_COMMAND_KEYS = [
|
|
@@ -13,6 +14,7 @@ const TURN_START_KEYS = {
|
|
|
13
14
|
"claude-code": new Set([...BASE_COMMAND_KEYS, "promptId", "prompt", "transcriptPath"]),
|
|
14
15
|
opencode: new Set([...BASE_COMMAND_KEYS, "userMessageId", "prompt"]),
|
|
15
16
|
dsh: new Set(["version", "client", "sessionId", "turn", "startSeq", "cwd", "prompt"]),
|
|
17
|
+
codebuddy: new Set([...BASE_COMMAND_KEYS, "turnId", "prompt", "transcriptPath"]),
|
|
16
18
|
};
|
|
17
19
|
const WRITEBACK_KEYS = {
|
|
18
20
|
codex: new Set([...BASE_COMMAND_KEYS, "turnId", "lastAssistantMessage", "transcriptPath"]),
|
|
@@ -39,12 +41,14 @@ const WRITEBACK_KEYS = {
|
|
|
39
41
|
"sessionHeader",
|
|
40
42
|
"events",
|
|
41
43
|
]),
|
|
44
|
+
codebuddy: new Set([...BASE_COMMAND_KEYS, "turnId", "transcriptPath"]),
|
|
42
45
|
};
|
|
43
46
|
const SKILL_REMINDER_KEYS = {
|
|
44
47
|
codex: new Set([...BASE_COMMAND_KEYS, "turnId", "transcriptPath", "content", "triggers"]),
|
|
45
48
|
"claude-code": new Set([...BASE_COMMAND_KEYS, "promptId", "transcriptPath", "content", "triggers"]),
|
|
46
49
|
dsh: new Set(["version", "client", "sessionId", "turn", "cwd", "content", "triggers"]),
|
|
47
50
|
opencode: new Set([...BASE_COMMAND_KEYS, "userMessageId", "content", "triggers"]),
|
|
51
|
+
codebuddy: new Set([...BASE_COMMAND_KEYS, "turnId", "transcriptPath", "content", "triggers"]),
|
|
48
52
|
};
|
|
49
53
|
export function parseTurnStartCommand(value) {
|
|
50
54
|
if (!isRecord(value))
|
|
@@ -102,6 +106,13 @@ export function parseTurnStartCommand(value) {
|
|
|
102
106
|
},
|
|
103
107
|
};
|
|
104
108
|
}
|
|
109
|
+
if (base.client === "codebuddy") {
|
|
110
|
+
const turnId = requiredStringField(value, "turnId");
|
|
111
|
+
const transcriptPath = requiredStringField(value, "transcriptPath");
|
|
112
|
+
if (!turnId || !transcriptPath || !validCodeBuddyTurnId(turnId, base.sessionId, prompt))
|
|
113
|
+
return invalidCommand();
|
|
114
|
+
return { ok: true, command: { ...base, client: "codebuddy", turnId, prompt, transcriptPath } };
|
|
115
|
+
}
|
|
105
116
|
const promptId = requiredStringField(value, "promptId");
|
|
106
117
|
const transcriptPath = requiredStringField(value, "transcriptPath");
|
|
107
118
|
if (!promptId || !transcriptPath)
|
|
@@ -166,6 +177,13 @@ export function parseWritebackCommand(value) {
|
|
|
166
177
|
},
|
|
167
178
|
};
|
|
168
179
|
}
|
|
180
|
+
if (base.client === "codebuddy") {
|
|
181
|
+
const turnId = requiredStringField(value, "turnId");
|
|
182
|
+
const transcriptPath = requiredStringField(value, "transcriptPath");
|
|
183
|
+
if (!turnId || !transcriptPath || !validCodeBuddyTurnId(turnId, base.sessionId))
|
|
184
|
+
return invalidCommand();
|
|
185
|
+
return { ok: true, command: { ...base, client: "codebuddy", turnId, transcriptPath } };
|
|
186
|
+
}
|
|
169
187
|
const lastAssistantMessage = requiredStringField(value, "lastAssistantMessage");
|
|
170
188
|
if (!lastAssistantMessage)
|
|
171
189
|
return invalidCommand();
|
|
@@ -241,6 +259,13 @@ export function parseSkillReminderCommand(value) {
|
|
|
241
259
|
},
|
|
242
260
|
};
|
|
243
261
|
}
|
|
262
|
+
if (base.client === "codebuddy") {
|
|
263
|
+
const turnId = requiredStringField(value, "turnId");
|
|
264
|
+
const transcriptPath = requiredStringField(value, "transcriptPath");
|
|
265
|
+
if (!turnId || !transcriptPath || !validCodeBuddyTurnId(turnId, base.sessionId))
|
|
266
|
+
return invalidCommand();
|
|
267
|
+
return { ok: true, command: { ...base, client: "codebuddy", turnId, transcriptPath, content, triggers } };
|
|
268
|
+
}
|
|
244
269
|
const transcriptPath = requiredStringField(value, "transcriptPath");
|
|
245
270
|
if (!transcriptPath)
|
|
246
271
|
return invalidCommand();
|
|
@@ -284,7 +309,8 @@ function parseCommandBase(value, allowedKeys) {
|
|
|
284
309
|
if (client !== "codex"
|
|
285
310
|
&& client !== "claude-code"
|
|
286
311
|
&& client !== "opencode"
|
|
287
|
-
&& client !== "dsh"
|
|
312
|
+
&& client !== "dsh"
|
|
313
|
+
&& client !== "codebuddy")
|
|
288
314
|
return undefined;
|
|
289
315
|
const clientKeys = allowedKeys[client];
|
|
290
316
|
if (!clientKeys || Object.keys(value).some((key) => !clientKeys.has(key)))
|
|
@@ -342,6 +368,10 @@ function optionalStringField(value, key) {
|
|
|
342
368
|
const field = requiredStringField(value, key);
|
|
343
369
|
return field ? { ok: true, value: field } : { ok: false };
|
|
344
370
|
}
|
|
371
|
+
function validCodeBuddyTurnId(turnId, sessionId, prompt) {
|
|
372
|
+
const identity = parseCodeBuddyTurnId({ sessionId, turnId });
|
|
373
|
+
return Boolean(identity && (prompt === undefined || identity.promptDigest === codeBuddyPromptDigest(prompt)));
|
|
374
|
+
}
|
|
345
375
|
function invalidCommand() {
|
|
346
376
|
return { ok: false, error: INVALID_MEMORY_HOOK_COMMAND };
|
|
347
377
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseSkillReminderCommand, } from "./hook-command.js";
|
|
2
|
-
import { traceContextFromClaudeHookBody, traceContextFromDshSkillReminder, traceContextFromHookBody, traceContextFromOpenCodeHookBody, } from "../trace/context.js";
|
|
2
|
+
import { traceContextFromClaudeHookBody, traceContextFromDshSkillReminder, traceContextFromHookBody, traceContextFromOpenCodeHookBody, traceContextFromCodeBuddyHookBody, } from "../trace/context.js";
|
|
3
3
|
import { recordTraceEvent } from "../trace/store.js";
|
|
4
4
|
export function createMemoryReminderTraceRecorder(options = {}) {
|
|
5
5
|
return {
|
|
@@ -47,6 +47,8 @@ function traceContextForReminder(command) {
|
|
|
47
47
|
return traceContextFromClaudeHookBody(command);
|
|
48
48
|
if (command.client === "dsh")
|
|
49
49
|
return traceContextFromDshSkillReminder(command);
|
|
50
|
+
if (command.client === "codebuddy")
|
|
51
|
+
return traceContextFromCodeBuddyHookBody(command);
|
|
50
52
|
return traceContextFromOpenCodeHookBody(command);
|
|
51
53
|
}
|
|
52
54
|
function reminderSource(command) {
|
|
@@ -56,6 +58,8 @@ function reminderSource(command) {
|
|
|
56
58
|
return "claude-hook";
|
|
57
59
|
if (command.client === "dsh")
|
|
58
60
|
return "dsh-cordis";
|
|
61
|
+
if (command.client === "codebuddy")
|
|
62
|
+
return "codebuddy-hook";
|
|
59
63
|
return "opencode-plugin";
|
|
60
64
|
}
|
|
61
65
|
async function recordTraceBestEffort(label, promise, diagnosticLogger) {
|
|
@@ -3,6 +3,7 @@ import { createCodexMemoryHookRuntime, } from "../clients/codex/memory-hook-runt
|
|
|
3
3
|
import { createClaudeMemoryHookRuntime, } from "../clients/claude/memory-hook-runtime.js";
|
|
4
4
|
import { createDshMemoryHookRuntime, } from "../clients/dsh/memory-hook-runtime.js";
|
|
5
5
|
import { createOpenCodeMemoryHookRuntime, } from "../clients/opencode/memory-hook-runtime.js";
|
|
6
|
+
import { createCodeBuddyMemoryHookRuntime } from "../clients/codebuddy/memory-hook-runtime.js";
|
|
6
7
|
import { createMemoryTurnCoordinator } from "./turn-coordinator.js";
|
|
7
8
|
import { createRepositoryMemorySessionRuntime, } from "./repository-session.js";
|
|
8
9
|
import { createPendingQuotaNoticeRuntime } from "./quota-notice.js";
|
|
@@ -49,6 +50,12 @@ export function createMemoryService(options = {}) {
|
|
|
49
50
|
repositoryMemorySession,
|
|
50
51
|
turnCoordinator,
|
|
51
52
|
});
|
|
53
|
+
const codeBuddyHook = createCodeBuddyMemoryHookRuntime({
|
|
54
|
+
...options,
|
|
55
|
+
pendingQuotaNotice,
|
|
56
|
+
repositoryMemorySession,
|
|
57
|
+
turnCoordinator,
|
|
58
|
+
});
|
|
52
59
|
let closed = false;
|
|
53
60
|
return {
|
|
54
61
|
async recordTurnStart(command) {
|
|
@@ -61,6 +68,8 @@ export function createMemoryService(options = {}) {
|
|
|
61
68
|
return await openCodeHook.recordTurnStart(command);
|
|
62
69
|
case "dsh":
|
|
63
70
|
return await dshHook.recordTurnStart(command);
|
|
71
|
+
case "codebuddy":
|
|
72
|
+
return await codeBuddyHook.recordTurnStart(command);
|
|
64
73
|
}
|
|
65
74
|
return unsupportedMemoryHookCommand(command);
|
|
66
75
|
},
|
|
@@ -74,6 +83,8 @@ export function createMemoryService(options = {}) {
|
|
|
74
83
|
return await openCodeHook.writeback(command);
|
|
75
84
|
case "dsh":
|
|
76
85
|
return await dshHook.writeback(command);
|
|
86
|
+
case "codebuddy":
|
|
87
|
+
return await codeBuddyHook.writeback(command);
|
|
77
88
|
}
|
|
78
89
|
return unsupportedMemoryHookCommand(command);
|
|
79
90
|
},
|
|
@@ -88,6 +99,7 @@ export function createMemoryService(options = {}) {
|
|
|
88
99
|
claudeHook.close();
|
|
89
100
|
openCodeHook.close();
|
|
90
101
|
dshHook.close();
|
|
102
|
+
codeBuddyHook.close();
|
|
91
103
|
turnCoordinator.close();
|
|
92
104
|
repositoryMemorySession.close();
|
|
93
105
|
automaticWriteback.close();
|
|
@@ -25,6 +25,9 @@ export function resolveWindowsCliInvocation(command, args, options = {}) {
|
|
|
25
25
|
resolvedCommand ??= command;
|
|
26
26
|
if (/\.(?:exe|com)$/i.test(resolvedCommand))
|
|
27
27
|
return { command: resolvedCommand, args };
|
|
28
|
+
if (isBareCodeBuddyEntrypoint(resolvedCommand, pathApi) && fileExists(resolvedCommand)) {
|
|
29
|
+
return { command: options.nodePath ?? process.execPath, args: [resolvedCommand, ...args] };
|
|
30
|
+
}
|
|
28
31
|
if (!/\.(?:cmd|bat)$/i.test(resolvedCommand)) {
|
|
29
32
|
throw new Error(`refusing to execute unsupported Windows command shim: ${resolvedCommand}`);
|
|
30
33
|
}
|
|
@@ -87,12 +90,18 @@ export function selectWindowsCommandCandidate(command, output) {
|
|
|
87
90
|
.map((value) => value.trim())
|
|
88
91
|
.filter(Boolean);
|
|
89
92
|
const selected = candidates.find((value) => /\.(?:cmd|bat)$/i.test(value))
|
|
90
|
-
?? candidates.find((value) => /\.(?:exe|com)$/i.test(value))
|
|
93
|
+
?? candidates.find((value) => /\.(?:exe|com)$/i.test(value))
|
|
94
|
+
?? (path.win32.basename(command).toLowerCase() === "codebuddy"
|
|
95
|
+
? candidates.find((value) => isBareCodeBuddyEntrypoint(value, path.win32))
|
|
96
|
+
: undefined);
|
|
91
97
|
if (!selected) {
|
|
92
98
|
throw new Error(`where.exe did not return a safe executable or command shim for ${command}`);
|
|
93
99
|
}
|
|
94
100
|
return selected;
|
|
95
101
|
}
|
|
102
|
+
function isBareCodeBuddyEntrypoint(value, pathApi) {
|
|
103
|
+
return pathApi.basename(value).toLowerCase() === "codebuddy" && pathApi.extname(value) === "";
|
|
104
|
+
}
|
|
96
105
|
export function resolveWindowsNpmInvocation(args, env, options = {}) {
|
|
97
106
|
const platform = options.platform ?? process.platform;
|
|
98
107
|
if (platform !== "win32")
|