@memorax/memorax-code 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +10 -0
  2. package/bin/memorax-code-codebuddy.mjs +4 -0
  3. package/bin/memorax-code-setup.mjs +182 -65
  4. package/bin/memorax-code.mjs +74 -45
  5. package/docs/configuration.md +82 -22
  6. package/docs/troubleshooting.md +69 -5
  7. package/lib/automatic-update.mjs +233 -0
  8. package/lib/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
  9. package/lib/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
  10. package/lib/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
  11. package/lib/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
  12. package/lib/memorax-code-adapter-common/src/config-utils.mjs +11 -0
  13. package/lib/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
  14. package/lib/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
  15. package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs +88 -3
  16. package/lib/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
  17. package/lib/memorax-code-backend/dist/app/backend-server.js +2 -2
  18. package/lib/memorax-code-backend/dist/app/memory-observability.js +2 -2
  19. package/lib/memorax-code-backend/dist/clients/codebuddy/jsonl-history.js +208 -0
  20. package/lib/memorax-code-backend/dist/clients/codebuddy/lifecycle.js +44 -0
  21. package/lib/memorax-code-backend/dist/clients/codebuddy/memory-hook-runtime.js +250 -0
  22. package/lib/memorax-code-backend/dist/clients/codebuddy/turn-id.js +16 -0
  23. package/lib/memorax-code-backend/dist/clients/codex/plugin-install.js +129 -7
  24. package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +52 -2
  25. package/lib/memorax-code-backend/dist/config/memorax-code.js +14 -1
  26. package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +43 -6
  27. package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
  28. package/lib/memorax-code-backend/dist/lifecycle/automatic-update-scheduler.js +133 -0
  29. package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
  30. package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +6 -4
  31. package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +48 -9
  32. package/lib/memorax-code-backend/dist/memory/cli.js +4 -2
  33. package/lib/memorax-code-backend/dist/memory/hook-command.js +31 -1
  34. package/lib/memorax-code-backend/dist/memory/reminder-trace-recorder.js +5 -1
  35. package/lib/memorax-code-backend/dist/memory/service.js +12 -0
  36. package/lib/memorax-code-backend/dist/shared/windows-cli-invocation.js +10 -1
  37. package/lib/memorax-code-backend/dist/trace/config.js +12 -0
  38. package/lib/memorax-code-backend/dist/trace/context.js +21 -1
  39. package/lib/memorax-code-backend/dist/trace/store.js +12 -1
  40. package/lib/memorax-code-backend/package.json +1 -1
  41. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  42. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  43. package/lib/memorax-code-claude-adapter/package.json +1 -1
  44. package/lib/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
  45. package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
  46. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  47. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  48. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
  49. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
  50. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
  51. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
  52. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +11 -0
  53. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
  54. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
  55. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs +88 -3
  56. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
  57. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  58. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/ensure-backend.mjs +2 -0
  59. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
  60. package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +7 -0
  61. package/lib/memorax-code-codebuddy-adapter/hooks/common-runtime.mjs +13 -0
  62. package/lib/memorax-code-codebuddy-adapter/hooks/hooks.json +38 -0
  63. package/lib/memorax-code-codebuddy-adapter/hooks/repo-memory-job.mjs +40 -0
  64. package/lib/memorax-code-codebuddy-adapter/hooks/runtime-hook.mjs +279 -0
  65. package/lib/memorax-code-codebuddy-adapter/package.json +9 -0
  66. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +85 -0
  67. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  68. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  69. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/defaults.json +12 -0
  70. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +88 -0
  71. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +93 -0
  72. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-read.md +46 -0
  73. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-write.md +120 -0
  74. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-build.md +319 -0
  75. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-read.md +103 -0
  76. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  77. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-update.md +127 -0
  78. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  79. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  80. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  81. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  82. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  83. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  84. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  85. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  86. package/lib/memorax-code-codebuddy-adapter/src/cli.mjs +58 -0
  87. package/lib/memorax-code-codebuddy-adapter/src/config.mjs +277 -0
  88. package/lib/memorax-code-codebuddy-adapter/src/hook-manifest.mjs +47 -0
  89. package/lib/memorax-code-codebuddy-adapter/src/runtime-observation.mjs +65 -0
  90. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  91. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  92. package/lib/memorax-code-codex-adapter/package.json +1 -1
  93. package/lib/memorax-code-codex-adapter/runtime-hooks/ensure-backend.mjs +4 -1
  94. package/lib/memorax-code-dsh-adapter/package.json +2 -1
  95. package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +1 -0
  96. package/lib/memorax-code-opencode-adapter/package.json +1 -1
  97. package/lib/npm-invocation.mjs +51 -8
  98. package/lib/resolve-codebuddy-command.mjs +112 -0
  99. package/lib/run-entrypoint.mjs +25 -1
  100. package/lib/windows-cli-invocation.mjs +11 -1
  101. package/lib/windows-user-path.mjs +218 -0
  102. package/package.json +5 -3
@@ -8,7 +8,8 @@ import { createBackendServer } from "../app/backend-server.js";
8
8
  import { backendDebug } from "../shared/debug-log.js";
9
9
  import { prepareClientPluginRemovalCleanup } from "../lifecycle/client-plugin-removal.js";
10
10
  import { resolveBackendInstallWatchdogConfig, startBackendInstallWatchdog, } from "../lifecycle/install-watchdog.js";
11
- import { activateCodexPlugin, installCodexPlugin } from "../clients/codex/plugin-install.js";
11
+ import { startBackendAutomaticUpdateScheduler, } from "../lifecycle/automatic-update-scheduler.js";
12
+ import { activateCodexPlugin, inspectCodexPluginRegistration, installCodexPlugin, } from "../clients/codex/plugin-install.js";
12
13
  import { inspectCodexPluginHooks, trustCodexPluginHooks } from "../clients/codex/plugin-hooks.js";
13
14
  import { collectMemoraxCodeStatus, isAdapterReady, isOptionalUnavailableDshAdapter, isOptionalUnconfiguredClaudeAdapter, restartMemoraxCodeService, startMemoraxCodeService, stopMemoraxCodeService, uninstallMemoraxCodeService, } from "../lifecycle/orchestrator.js";
14
15
  import { backendEnv } from "../config/backend-env.js";
@@ -29,9 +30,9 @@ export function runBackendCli(argv = process.argv) {
29
30
  `Usage: ${usageName} [${commands}] [--backend-url URL] [--backend-token TOKEN] [--home DIR]`,
30
31
  "[--host HOST] [--port PORT] [--rotate] [--show]",
31
32
  "[--codex-command CMD]",
32
- "[--codex-home DIR] [--claude-home DIR] [--dsh-home DIR] [--opencode-config-dir DIR]",
33
+ "[--codex-home DIR] [--claude-home DIR] [--dsh-home DIR] [--opencode-config-dir DIR] [--codebuddy-home DIR]",
33
34
  "[--dsh-command CMD] [--dsh-adapter-root DIR] [--memorax-code-command CMD]",
34
- "[--clients codex|claude|dsh|opencode|CLIENT,...|all|none]",
35
+ "[--clients codex|claude|dsh|opencode|codebuddy|CLIENT,...|all|none]",
35
36
  "[--json]",
36
37
  "[--marketplace-path FILE] [--plugin-source-path DIR] [--claude-command CMD] [--help]",
37
38
  "[--yes]",
@@ -210,12 +211,14 @@ async function startRawBackendServer(state, host, port, argv) {
210
211
  dshHome: argValue(argv, "--dsh-home"),
211
212
  dshAdapterRoot: argValue(argv, "--dsh-adapter-root"),
212
213
  openCodeConfigDir: argValue(argv, "--opencode-config-dir"),
214
+ codeBuddyHome: argValue(argv, "--codebuddy-home"),
213
215
  codexCommand: argValue(argv, "--codex-command"),
214
216
  claudeCommand: argValue(argv, "--claude-command"),
215
217
  dshCommand: argValue(argv, "--dsh-command"),
216
218
  })
217
219
  : undefined;
218
220
  const server = createBackendServer(state);
221
+ let automaticUpdateScheduler;
219
222
  let installRemovalStarted = false;
220
223
  const installWatchdog = startBackendInstallWatchdog(installWatchdogConfig, async (event) => {
221
224
  if (installRemovalStarted)
@@ -245,7 +248,10 @@ async function startRawBackendServer(state, host, port, argv) {
245
248
  });
246
249
  });
247
250
  installBackendShutdownControls(server, state);
248
- server.once("close", () => installWatchdog?.close());
251
+ server.once("close", () => {
252
+ automaticUpdateScheduler?.close();
253
+ installWatchdog?.close();
254
+ });
249
255
  server.on("error", (error) => {
250
256
  if (error.code === "EADDRINUSE") {
251
257
  console.error(`memorax-code-backend failed to listen on http://${host}:${port}: address already in use`);
@@ -258,6 +264,15 @@ async function startRawBackendServer(state, host, port, argv) {
258
264
  });
259
265
  server.listen(port, host, () => {
260
266
  console.log(`memorax-code-backend listening on http://${host}:${port}`);
267
+ if (backendEnv("INSTANCE_ID")) {
268
+ automaticUpdateScheduler = startBackendAutomaticUpdateScheduler({
269
+ env: process.env,
270
+ memoraxCodeHome: state.sessionHome,
271
+ packageRoot: process.env.MEMORAX_CODE_NPM_PACKAGE_ROOT,
272
+ packageVersion: process.env.MEMORAX_CODE_NPM_PACKAGE_VERSION,
273
+ debug: (message) => backendDebug("automatic_update.scheduler_failed", { message }),
274
+ });
275
+ }
261
276
  });
262
277
  }
263
278
  function installBackendShutdownControls(server, state) {
@@ -382,6 +397,13 @@ async function runCodexPluginCommand(argv) {
382
397
  yes: argv.includes("--yes"),
383
398
  });
384
399
  }
400
+ if (subcommand === "registration") {
401
+ return await inspectCodexPluginRegistration({
402
+ codexHome: argValue(argv, "--codex-home"),
403
+ codexCommand: argValue(argv, "--codex-command"),
404
+ workspace: argValue(argv, "--workspace"),
405
+ });
406
+ }
385
407
  if (subcommand === "hooks") {
386
408
  return await inspectCodexPluginHooks({
387
409
  codexHome: argValue(argv, "--codex-home"),
@@ -427,6 +449,15 @@ function printCodexPluginInstallResult(report) {
427
449
  console.log("backend: not started");
428
450
  return;
429
451
  }
452
+ if (report.action === "codex-plugin-registration") {
453
+ console.log(`codex home: ${report.codexHome}`);
454
+ console.log(`registered: ${report.registered ? "yes" : "no"}`);
455
+ console.log(`enabled: ${report.enabled ? "yes" : "no"}`);
456
+ if (report.version)
457
+ console.log(`version: ${report.version}`);
458
+ console.log("backend: not started");
459
+ return;
460
+ }
430
461
  const install = report.action === "codex-plugin-activate" ? report.install : report;
431
462
  console.log(`codex home: ${install.codexHome}`);
432
463
  console.log(`plugin source: ${install.pluginSourcePath}`);
@@ -490,6 +521,8 @@ function printMemoraxCodeStatus(report) {
490
521
  backendLog(`DSH adapter: ${dshAdapterStatusLine(report.dshAdapter)}`);
491
522
  if (report.opencodeAdapter)
492
523
  backendLog(`OpenCode adapter: ${adapterStatusLine(report.opencodeAdapter)}`);
524
+ if (report.codebuddyAdapter)
525
+ backendLog(`CodeBuddy adapter: ${adapterStatusLine(report.codebuddyAdapter)}`);
493
526
  if (!suppressBackendGuidance()) {
494
527
  for (const line of statusGuidance(report))
495
528
  backendLog(line);
@@ -591,6 +624,8 @@ function printLifecycleResult(report) {
591
624
  backendLog(`DSH adapter: ${dshAdapterStatusLine(report.dshAdapter)}`);
592
625
  if (report.opencodeAdapter)
593
626
  backendLog(`OpenCode adapter: ${adapterStatusLine(report.opencodeAdapter)}`);
627
+ if (report.codebuddyAdapter)
628
+ backendLog(`CodeBuddy adapter: ${adapterStatusLine(report.codebuddyAdapter)}`);
594
629
  if (report.codexPlugin) {
595
630
  const removed = report.codexPlugin.removedPaths.length;
596
631
  const marketplace = report.codexPlugin.marketplaceChanged ? " marketplace=updated" : " marketplace=unchanged";
@@ -848,11 +883,13 @@ function adapterStatusLine(report) {
848
883
  const enabled = isAdapterReady(report);
849
884
  const skillStatus = report.codexSkills?.status
850
885
  ?? report.claudeSkills?.status
851
- ?? report.opencodeSkills?.status;
886
+ ?? report.opencodeSkills?.status
887
+ ?? report.codebuddySkills?.status;
852
888
  const skills = skillStatus ? ` skills=${skillStatus}` : "";
889
+ const hooks = report.codebuddyHooks?.status ? ` hook-runtime=${report.codebuddyHooks.status}` : "";
853
890
  const changed = report.changed === true ? " changed" : "";
854
891
  const integration = report.integration ?? report.state?.integration ?? "hooks";
855
- return `${enabled ? "ok" : "not enabled"} integration=${integration}${skills}${changed}`;
892
+ return `${enabled ? "ok" : "not enabled"} integration=${integration}${skills}${hooks}${changed}`;
856
893
  }
857
894
  function selectedLifecycleClientNames(report) {
858
895
  return [
@@ -10,6 +10,8 @@ export function readActiveManagedClients(memoraxCodeHome) {
10
10
  return undefined;
11
11
  if (value.opencode !== undefined && typeof value.opencode !== "boolean")
12
12
  return undefined;
13
+ if (value.codebuddy !== undefined && typeof value.codebuddy !== "boolean")
14
+ return undefined;
13
15
  if (value.dsh !== undefined && typeof value.dsh !== "boolean")
14
16
  return undefined;
15
17
  return {
@@ -19,6 +21,7 @@ export function readActiveManagedClients(memoraxCodeHome) {
19
21
  // that the Backend was serving DSH.
20
22
  dsh: value.dsh === true,
21
23
  opencode: value.opencode === true,
24
+ ...(value.codebuddy === true ? { codebuddy: true } : {}),
22
25
  };
23
26
  }
24
27
  catch {
@@ -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 allClients;
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
- const backendOnlyStop = !clients.codex && !clients.claude && !clients.dsh && !clients.opencode;
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) {