@memorax/memorax-code 0.1.10 → 0.1.12

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 (84) hide show
  1. package/README.md +7 -4
  2. package/bin/memorax-code-setup.mjs +136 -34
  3. package/bin/memorax-code-trae.mjs +4 -0
  4. package/docs/configuration.md +111 -54
  5. package/docs/troubleshooting.md +50 -8
  6. package/lib/memorax-code-backend/dist/clients/claude/transcript-turn.js +2 -2
  7. package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +1 -1
  8. package/lib/memorax-code-backend/dist/clients/trae/lifecycle.js +53 -0
  9. package/lib/memorax-code-backend/dist/clients/trae/memory-hook-runtime.js +302 -0
  10. package/lib/memorax-code-backend/dist/clients/trae/turn-id.js +16 -0
  11. package/lib/memorax-code-backend/dist/config/memorax-code.js +14 -1
  12. package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +22 -4
  13. package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
  14. package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
  15. package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +5 -3
  16. package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +62 -9
  17. package/lib/memorax-code-backend/dist/memory/hook-command.js +34 -2
  18. package/lib/memorax-code-backend/dist/memory/reminder-trace-recorder.js +5 -1
  19. package/lib/memorax-code-backend/dist/memory/service.js +12 -0
  20. package/lib/memorax-code-backend/dist/trace/config.js +12 -1
  21. package/lib/memorax-code-backend/dist/trace/context.js +26 -1
  22. package/lib/memorax-code-backend/package.json +1 -1
  23. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  24. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  25. package/lib/memorax-code-claude-adapter/package.json +1 -1
  26. package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +3 -1
  27. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +16 -3
  28. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +15 -2
  29. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  30. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  31. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  32. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +3 -1
  33. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +16 -3
  34. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +15 -2
  35. package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +1 -1
  36. package/lib/memorax-code-codebuddy-adapter/hooks/runtime-hook.mjs +11 -1
  37. package/lib/memorax-code-codebuddy-adapter/package.json +1 -1
  38. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +3 -1
  39. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +16 -3
  40. package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +15 -2
  41. package/lib/memorax-code-codebuddy-adapter/src/config.mjs +86 -23
  42. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  43. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  44. package/lib/memorax-code-codex-adapter/package.json +1 -1
  45. package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +3 -1
  46. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +16 -3
  47. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +15 -2
  48. package/lib/memorax-code-dsh-adapter/package.json +1 -1
  49. package/lib/memorax-code-dsh-adapter/skills/memorax-code/SKILL.md +3 -1
  50. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +16 -3
  51. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +15 -2
  52. package/lib/memorax-code-opencode-adapter/package.json +1 -1
  53. package/lib/memorax-code-opencode-adapter/skills/memorax-code/SKILL.md +3 -1
  54. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +16 -3
  55. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +15 -2
  56. package/lib/memorax-code-trae-adapter/hooks/runtime-hook.mjs +340 -0
  57. package/lib/memorax-code-trae-adapter/package.json +9 -0
  58. package/lib/memorax-code-trae-adapter/skills/memorax-code/SKILL.md +87 -0
  59. package/lib/memorax-code-trae-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  60. package/lib/memorax-code-trae-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  61. package/lib/memorax-code-trae-adapter/skills/memorax-code/defaults.json +12 -0
  62. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/memorax-add.md +101 -0
  63. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/memorax-search.md +106 -0
  64. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/personal-read.md +46 -0
  65. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/personal-write.md +120 -0
  66. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-build.md +319 -0
  67. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-read.md +103 -0
  68. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  69. package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-update.md +127 -0
  70. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  71. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  72. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  73. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  74. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  75. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  76. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  77. package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  78. package/lib/memorax-code-trae-adapter/src/adapter-paths.mjs +69 -0
  79. package/lib/memorax-code-trae-adapter/src/cli.mjs +59 -0
  80. package/lib/memorax-code-trae-adapter/src/config.mjs +630 -0
  81. package/lib/memorax-code-trae-adapter/src/runtime-observation.mjs +55 -0
  82. package/lib/resolve-codebuddy-command.mjs +15 -4
  83. package/lib/run-entrypoint.mjs +6 -0
  84. package/package.json +5 -3
@@ -0,0 +1,302 @@
1
+ import { createAutomaticMemoryWritebackRuntime, } from "../../memory/automatic-writeback.js";
2
+ import { retrieveAutomaticMemoryContext } from "../../memory/automatic-retrieval.js";
3
+ import { claimQuotaNotice, createPendingQuotaNoticeRuntime, } from "../../memory/quota-notice.js";
4
+ import { createMemoryTurnCoordinator, } from "../../memory/turn-coordinator.js";
5
+ import { createRepositoryMemorySessionRuntime, resolvedRepoMemoryWorktree, } from "../../memory/repository-session.js";
6
+ import { traceContextFromTraeHookBody } from "../../trace/context.js";
7
+ import { markCurrentTraceTurnOutcome, recordTraceEvent, traceTurnEventId, writeCurrentTraceTurn, } from "../../trace/store.js";
8
+ const TRAE_MEMORY_TURN_CLIENT = "trae";
9
+ export function createTraeMemoryHookRuntime(options = {}) {
10
+ const now = options.now ?? (() => Date.now());
11
+ const pendingQuotaNotice = options.pendingQuotaNotice ?? createPendingQuotaNoticeRuntime({
12
+ claimQuotaNotice: options.claimQuotaNotice,
13
+ diagnosticLogger: options.diagnosticLogger,
14
+ env: options.env,
15
+ });
16
+ const ownsPendingQuotaNotice = options.pendingQuotaNotice === undefined;
17
+ const automaticWritebackRuntime = options.turnCoordinator
18
+ ? undefined
19
+ : options.automaticWriteback
20
+ ? { enqueue: options.automaticWriteback }
21
+ : createAutomaticMemoryWritebackRuntime({
22
+ diagnosticLogger: options.diagnosticLogger,
23
+ queueQuotaNotice: pendingQuotaNotice.queue,
24
+ });
25
+ const turnCoordinator = options.turnCoordinator ?? createMemoryTurnCoordinator({
26
+ automaticWriteback: automaticWritebackRuntime.enqueue,
27
+ now,
28
+ ttlMs: options.ttlMs,
29
+ maxEntries: options.maxEntries,
30
+ cleanupIntervalMs: options.cleanupIntervalMs,
31
+ });
32
+ const ownsTurnCoordinator = options.turnCoordinator === undefined;
33
+ const repositoryMemorySession = options.repositoryMemorySession ?? createRepositoryMemorySessionRuntime({
34
+ onScopeUpgrade: automaticWritebackRuntime?.discardForScopeUpgrade,
35
+ });
36
+ const ownsRepositoryMemorySession = options.repositoryMemorySession === undefined;
37
+ const retrievalTurns = new Set();
38
+ const interruptedTurns = new Set();
39
+ const activeTurns = new Map();
40
+ const turnStartOperations = new Map();
41
+ const runtimeTurnLimit = positiveInteger(options.maxEntries, 256);
42
+ const runtime = {
43
+ async recordTurnStart(command) {
44
+ turnCoordinator.pruneExpired();
45
+ const commandKey = traeRuntimeTurnKey(command.sessionId, command.turnId);
46
+ if (interruptedTurns.has(commandKey))
47
+ return { ok: true };
48
+ const previous = activeTurns.get(command.sessionId) ?? turnCoordinator.latestTurn({
49
+ client: TRAE_MEMORY_TURN_CLIENT,
50
+ sessionId: command.sessionId,
51
+ excludeClientTurnId: command.turnId,
52
+ });
53
+ if (previous && previous.clientTurnId !== command.turnId) {
54
+ await interruptPreviousTurn(turnCoordinator, previous, options, now);
55
+ rememberBounded(interruptedTurns, traeRuntimeTurnKey(previous.sessionId, previous.clientTurnId), runtimeTurnLimit);
56
+ }
57
+ const createdAt = now();
58
+ const traceContext = traceContextFromTraeHookBody(command, new Date(createdAt).toISOString());
59
+ const repositoryMemory = await resolveHookRepositoryMemory(command, options, repositoryMemorySession);
60
+ const repoMemoryWorktree = resolvedRepoMemoryWorktree(repositoryMemory);
61
+ const turn = turnCoordinator.recordTurnStart({
62
+ client: TRAE_MEMORY_TURN_CLIENT,
63
+ sessionId: command.sessionId,
64
+ clientTurnId: command.turnId,
65
+ cwd: command.cwd,
66
+ workspaceKind: command.workspaceKind,
67
+ createdAt,
68
+ traceContext,
69
+ repositoryMemory,
70
+ });
71
+ activeTurns.delete(command.sessionId);
72
+ activeTurns.set(command.sessionId, turn);
73
+ while (activeTurns.size > runtimeTurnLimit) {
74
+ const oldestSessionId = activeTurns.keys().next().value;
75
+ if (typeof oldestSessionId !== "string")
76
+ break;
77
+ activeTurns.delete(oldestSessionId);
78
+ }
79
+ await recordTraceBestEffort("trae_memory.turn_start_event", recordTraceEvent({
80
+ eventId: traceTurnEventId(traceContext, "turn_start"),
81
+ memoraxCodeHome: options.memoraxCodeHome,
82
+ env: options.env,
83
+ traceContext,
84
+ type: "turn_start",
85
+ source: "trae-hook",
86
+ operation: "query",
87
+ ok: true,
88
+ request: { prompt: command.prompt, cwd: command.cwd },
89
+ }), options.diagnosticLogger);
90
+ await recordTraceBestEffort("trae_memory.current_turn_write", writeCurrentTraceTurn(traceContext, {
91
+ client: "trae",
92
+ memoraxCodeHome: options.memoraxCodeHome,
93
+ env: options.env,
94
+ now: () => new Date(now()),
95
+ }), options.diagnosticLogger);
96
+ const retrievalKey = JSON.stringify([command.sessionId, command.turnId]);
97
+ if (retrievalTurns.has(retrievalKey)) {
98
+ return { ok: true, ...(repoMemoryWorktree ? { repoMemoryWorktree } : {}) };
99
+ }
100
+ retrievalTurns.add(retrievalKey);
101
+ trimBounded(retrievalTurns, runtimeTurnLimit);
102
+ const pendingUserNotice = repositoryMemory.ok
103
+ ? await pendingQuotaNotice.claim(repositoryMemory.memory.config)
104
+ : undefined;
105
+ const retrieval = await retrieveAutomaticMemoryContext({
106
+ diagnosticLogger: options.diagnosticLogger,
107
+ claimQuotaNotice: options.claimQuotaNotice ?? claimQuotaNotice,
108
+ env: options.env ?? process.env,
109
+ fetchImpl: options.fetchImpl,
110
+ memoryObservability: options.memoryObservability,
111
+ memoryObservabilitySource: "trae_hook_retrieval",
112
+ query: command.prompt,
113
+ repositoryMemory,
114
+ sessionKey: command.sessionId,
115
+ traceContext,
116
+ });
117
+ const userNotice = [pendingUserNotice, retrieval.userNotice].filter(Boolean).join("\n");
118
+ return {
119
+ ok: true,
120
+ ...(repoMemoryWorktree ? { repoMemoryWorktree } : {}),
121
+ ...(retrieval.context ? { additionalContext: retrieval.context } : {}),
122
+ ...(userNotice ? { userNotice } : {}),
123
+ };
124
+ },
125
+ async writeback(command) {
126
+ turnCoordinator.pruneExpired();
127
+ const commandKey = traeRuntimeTurnKey(command.sessionId, command.turnId);
128
+ const activeTurn = activeTurns.get(command.sessionId);
129
+ if (interruptedTurns.has(commandKey)
130
+ || (activeTurn && activeTurn.clientTurnId !== command.turnId)) {
131
+ rememberBounded(interruptedTurns, commandKey, runtimeTurnLimit);
132
+ return { ok: true, scheduled: false, reason: "interrupted" };
133
+ }
134
+ const key = traeTurnKey(command.sessionId, command.turnId);
135
+ const entry = turnCoordinator.getTurn(key) ?? activeTurn;
136
+ const traceContext = entry?.traceContext ?? traceContextFromTraeHookBody(command);
137
+ const repositoryMemory = await resolveHookRepositoryMemory(command, options, repositoryMemorySession);
138
+ await recordCompletedTurn(traceContext, command, options, now);
139
+ const completed = await turnCoordinator.completeMaterializedTurn({
140
+ key,
141
+ metadata: entry,
142
+ resolveRepositoryMemory: async () => repositoryMemory,
143
+ userText: command.prompt,
144
+ assistantText: command.lastAssistantMessage,
145
+ writeback: {
146
+ client: "trae",
147
+ sessionKey: command.sessionId,
148
+ env: options.env ?? process.env,
149
+ fetchImpl: options.fetchImpl,
150
+ memoryObservability: options.memoryObservability,
151
+ memoryObservabilitySource: "trae_hook_writeback",
152
+ traceContext,
153
+ },
154
+ });
155
+ await recordMaterializedTurn(traceContext, command, options);
156
+ if (activeTurn?.clientTurnId === command.turnId)
157
+ activeTurns.delete(command.sessionId);
158
+ options.diagnosticLogger?.("trae_memory.writeback", {
159
+ scheduled: completed.scheduled,
160
+ ...(!completed.scheduled ? { reason: completed.reason } : {}),
161
+ sessionId: command.sessionId,
162
+ turnId: command.turnId,
163
+ metadataDisposition: completed.metadataDisposition,
164
+ });
165
+ return completed.scheduled
166
+ ? { ok: true, scheduled: true }
167
+ : { ok: true, scheduled: false, reason: completed.reason };
168
+ },
169
+ size() {
170
+ return turnCoordinator.size(TRAE_MEMORY_TURN_CLIENT);
171
+ },
172
+ close() {
173
+ retrievalTurns.clear();
174
+ interruptedTurns.clear();
175
+ activeTurns.clear();
176
+ turnStartOperations.clear();
177
+ if (ownsTurnCoordinator)
178
+ turnCoordinator.close();
179
+ if (ownsRepositoryMemorySession)
180
+ repositoryMemorySession.close();
181
+ automaticWritebackRuntime?.close?.();
182
+ if (ownsPendingQuotaNotice)
183
+ pendingQuotaNotice.close();
184
+ },
185
+ };
186
+ return {
187
+ ...runtime,
188
+ recordTurnStart(command) {
189
+ return queueSessionTurnStart(turnStartOperations, command.sessionId, () => runtime.recordTurnStart(command));
190
+ },
191
+ };
192
+ }
193
+ async function interruptPreviousTurn(coordinator, previous, options, now) {
194
+ await recordTraceBestEffort("trae_memory.interrupted_turn_end", recordTraceEvent({
195
+ eventId: traceTurnEventId(previous.traceContext, "turn_end"),
196
+ memoraxCodeHome: options.memoraxCodeHome,
197
+ env: options.env,
198
+ traceContext: previous.traceContext,
199
+ type: "turn_end",
200
+ source: "trae-hook",
201
+ operation: "writeback",
202
+ ok: true,
203
+ outcome: "interrupted",
204
+ }), options.diagnosticLogger);
205
+ await recordTraceBestEffort("trae_memory.interrupted_current_turn", markCurrentTraceTurnOutcome(previous.traceContext, "interrupted", {
206
+ client: "trae",
207
+ memoraxCodeHome: options.memoraxCodeHome,
208
+ env: options.env,
209
+ now: () => new Date(now()),
210
+ }), options.diagnosticLogger);
211
+ coordinator.discardTurn(previous, "interrupted");
212
+ }
213
+ async function recordCompletedTurn(traceContext, command, options, now) {
214
+ await recordTraceBestEffort("trae_memory.turn_end", recordTraceEvent({
215
+ eventId: traceTurnEventId(traceContext, "turn_end"),
216
+ memoraxCodeHome: options.memoraxCodeHome,
217
+ env: options.env,
218
+ traceContext,
219
+ type: "turn_end",
220
+ source: "trae-hook",
221
+ operation: "writeback",
222
+ ok: true,
223
+ outcome: "completed",
224
+ response: { assistant: command.lastAssistantMessage },
225
+ }), options.diagnosticLogger);
226
+ await recordTraceBestEffort("trae_memory.current_turn_close", markCurrentTraceTurnOutcome(traceContext, "completed", {
227
+ client: "trae",
228
+ memoraxCodeHome: options.memoraxCodeHome,
229
+ env: options.env,
230
+ now: () => new Date(now()),
231
+ }), options.diagnosticLogger);
232
+ }
233
+ async function recordMaterializedTurn(traceContext, command, options) {
234
+ await recordTraceBestEffort("trae_memory.turn_materialized", recordTraceEvent({
235
+ eventId: traceTurnEventId(traceContext, "turn_materialized"),
236
+ memoraxCodeHome: options.memoraxCodeHome,
237
+ env: options.env,
238
+ traceContext,
239
+ type: "turn_materialized",
240
+ source: "trae-hook",
241
+ operation: "writeback",
242
+ ok: true,
243
+ request: { prompt: command.prompt },
244
+ response: { assistant: command.lastAssistantMessage },
245
+ }), options.diagnosticLogger);
246
+ }
247
+ async function resolveHookRepositoryMemory(command, options, repository) {
248
+ return await repository.resolve({
249
+ client: "trae",
250
+ sessionId: command.sessionId,
251
+ workspaceRoot: command.cwd,
252
+ workspaceKind: command.workspaceKind,
253
+ memoraxCodeHome: options.memoraxCodeHome ?? options.env?.MEMORAX_CODE_HOME,
254
+ env: options.env,
255
+ });
256
+ }
257
+ function traeTurnKey(sessionId, turnId) {
258
+ return { client: "trae", sessionId, clientTurnId: turnId };
259
+ }
260
+ function traeRuntimeTurnKey(sessionId, turnId) {
261
+ return JSON.stringify([sessionId, turnId]);
262
+ }
263
+ function rememberBounded(values, value, limit) {
264
+ values.add(value);
265
+ trimBounded(values, limit);
266
+ }
267
+ function trimBounded(values, limit) {
268
+ while (values.size > limit) {
269
+ const oldest = values.values().next().value;
270
+ if (typeof oldest !== "string")
271
+ return;
272
+ values.delete(oldest);
273
+ }
274
+ }
275
+ async function recordTraceBestEffort(label, promise, diagnosticLogger) {
276
+ try {
277
+ await promise;
278
+ }
279
+ catch (error) {
280
+ diagnosticLogger?.("trae_trace.write_failed", {
281
+ label,
282
+ error: error instanceof Error ? error.message : String(error),
283
+ });
284
+ }
285
+ }
286
+ function queueSessionTurnStart(operations, sessionId, operation) {
287
+ const previous = operations.get(sessionId) ?? Promise.resolve();
288
+ const queued = previous
289
+ .catch(() => undefined)
290
+ .then(operation);
291
+ operations.set(sessionId, queued);
292
+ const clear = () => {
293
+ if (operations.get(sessionId) === queued)
294
+ operations.delete(sessionId);
295
+ };
296
+ void queued.then(clear, clear);
297
+ return queued;
298
+ }
299
+ function positiveInteger(value, fallback) {
300
+ const parsed = Number(value);
301
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
302
+ }
@@ -0,0 +1,16 @@
1
+ import { createHash } from "node:crypto";
2
+ export function traePromptDigest(prompt) {
3
+ return createHash("sha256").update(prompt.trim()).digest("hex");
4
+ }
5
+ export function parseTraeTurnId(input) {
6
+ const prefix = `${input.sessionId}:`;
7
+ if (!input.turnId.startsWith(prefix))
8
+ return undefined;
9
+ const match = /^([1-9]\d*):([a-f0-9]{64})$/.exec(input.turnId.slice(prefix.length));
10
+ if (!match)
11
+ return undefined;
12
+ const createdAt = Number(match[1]);
13
+ if (!Number.isSafeInteger(createdAt))
14
+ return undefined;
15
+ return { createdAt, promptDigest: match[2] };
16
+ }
@@ -74,6 +74,10 @@ export function renderDefaultMemoraxCodeConfig() {
74
74
  "enabled = true # Enable local CodeBuddy session memory trace collection.",
75
75
  "capture_content = true # Store content in local CodeBuddy trace events.",
76
76
  "",
77
+ "[trace.trae]",
78
+ "enabled = true # Enable local Trae session memory trace collection.",
79
+ "capture_content = true # Store content in local Trae trace events.",
80
+ "",
77
81
  ].join("\n");
78
82
  }
79
83
  export async function seedMissingMemoraxCodeConfig(memoraxCodeHome = defaultMemoraxCodeHome(process.env)) {
@@ -151,6 +155,7 @@ function normalizeMemoraxCodeConfig(value) {
151
155
  const traceDsh = recordValue(trace?.dsh);
152
156
  const traceOpenCode = recordValue(trace?.opencode);
153
157
  const traceCodeBuddy = recordValue(trace?.codebuddy);
158
+ const traceTrae = recordValue(trace?.trae);
154
159
  return (prune({
155
160
  clients: prune({
156
161
  codex: booleanField(clients, "codex"),
@@ -158,6 +163,7 @@ function normalizeMemoraxCodeConfig(value) {
158
163
  dsh: booleanField(clients, "dsh"),
159
164
  opencode: booleanField(clients, "opencode"),
160
165
  codebuddy: booleanField(clients, "codebuddy"),
166
+ trae: booleanField(clients, "trae"),
161
167
  }),
162
168
  memorax: prune({
163
169
  endpoint: stringField(memorax, "endpoint"),
@@ -243,6 +249,13 @@ function normalizeMemoraxCodeConfig(value) {
243
249
  max_event_chars: numberField(traceCodeBuddy, "max_event_chars"),
244
250
  max_file_bytes: numberField(traceCodeBuddy, "max_file_bytes"),
245
251
  }),
252
+ trae: prune({
253
+ enabled: booleanField(traceTrae, "enabled"),
254
+ capture_content: booleanField(traceTrae, "capture_content"),
255
+ retention_days: numberField(traceTrae, "retention_days"),
256
+ max_event_chars: numberField(traceTrae, "max_event_chars"),
257
+ max_file_bytes: numberField(traceTrae, "max_file_bytes"),
258
+ }),
246
259
  }),
247
260
  }) ?? {});
248
261
  }
@@ -255,7 +268,7 @@ function validateRawLifecycleConfig(value, path) {
255
268
  const clients = tableValue(rawClients);
256
269
  if (!clients)
257
270
  throw invalidLifecycleConfig(path, "clients must be a table");
258
- for (const field of ["codex", "claude", "dsh", "opencode", "codebuddy"]) {
271
+ for (const field of ["codex", "claude", "dsh", "opencode", "codebuddy", "trae"]) {
259
272
  if (clients[field] !== undefined && typeof clients[field] !== "boolean") {
260
273
  throw invalidLifecycleConfig(path, `clients.${field} must be a boolean`);
261
274
  }
@@ -30,9 +30,9 @@ export function runBackendCli(argv = process.argv) {
30
30
  `Usage: ${usageName} [${commands}] [--backend-url URL] [--backend-token TOKEN] [--home DIR]`,
31
31
  "[--host HOST] [--port PORT] [--rotate] [--show]",
32
32
  "[--codex-command CMD]",
33
- "[--codex-home DIR] [--claude-home DIR] [--dsh-home DIR] [--opencode-config-dir DIR] [--codebuddy-home DIR]",
33
+ "[--codex-home DIR] [--claude-home DIR] [--dsh-home DIR] [--opencode-config-dir DIR] [--codebuddy-home DIR] [--trae-home DIR]",
34
34
  "[--dsh-command CMD] [--dsh-adapter-root DIR] [--memorax-code-command CMD]",
35
- "[--clients codex|claude|dsh|opencode|codebuddy|CLIENT,...|all|none]",
35
+ "[--clients codex|claude|dsh|opencode|codebuddy|trae|CLIENT,...|all|none]",
36
36
  "[--json]",
37
37
  "[--marketplace-path FILE] [--plugin-source-path DIR] [--claude-command CMD] [--help]",
38
38
  "[--yes]",
@@ -212,6 +212,7 @@ async function startRawBackendServer(state, host, port, argv) {
212
212
  dshAdapterRoot: argValue(argv, "--dsh-adapter-root"),
213
213
  openCodeConfigDir: argValue(argv, "--opencode-config-dir"),
214
214
  codeBuddyHome: argValue(argv, "--codebuddy-home"),
215
+ traeHome: argValue(argv, "--trae-home"),
215
216
  codexCommand: argValue(argv, "--codex-command"),
216
217
  claudeCommand: argValue(argv, "--claude-command"),
217
218
  dshCommand: argValue(argv, "--dsh-command"),
@@ -523,6 +524,8 @@ function printMemoraxCodeStatus(report) {
523
524
  backendLog(`OpenCode adapter: ${adapterStatusLine(report.opencodeAdapter)}`);
524
525
  if (report.codebuddyAdapter)
525
526
  backendLog(`CodeBuddy adapter: ${adapterStatusLine(report.codebuddyAdapter)}`);
527
+ if (report.traeAdapter)
528
+ backendLog(`Trae adapter: ${adapterStatusLine(report.traeAdapter)}`);
526
529
  if (!suppressBackendGuidance()) {
527
530
  for (const line of statusGuidance(report))
528
531
  backendLog(line);
@@ -626,6 +629,8 @@ function printLifecycleResult(report) {
626
629
  backendLog(`OpenCode adapter: ${adapterStatusLine(report.opencodeAdapter)}`);
627
630
  if (report.codebuddyAdapter)
628
631
  backendLog(`CodeBuddy adapter: ${adapterStatusLine(report.codebuddyAdapter)}`);
632
+ if (report.traeAdapter)
633
+ backendLog(`Trae adapter: ${adapterStatusLine(report.traeAdapter)}`);
629
634
  if (report.codexPlugin) {
630
635
  const removed = report.codexPlugin.removedPaths.length;
631
636
  const marketplace = report.codexPlugin.marketplaceChanged ? " marketplace=updated" : " marketplace=unchanged";
@@ -795,6 +800,9 @@ function statusGuidance(report) {
795
800
  }
796
801
  return [
797
802
  green("MemoraX Code is ready; sessions with the stable plugin shell use the active Hook runtime on their next user prompt."),
803
+ ...(report.traeAdapter?.globalHooksActivationRequired
804
+ ? ["Trae requires one manual step: open Trae Settings and enable Global Hooks, then start a new Trae session."]
805
+ : []),
798
806
  ...(optionalDshUnavailable
799
807
  ? [`DSH integration is unavailable: ${report.dshAdapter?.reason ?? "not available"}.`]
800
808
  : []),
@@ -841,6 +849,12 @@ function statusGuidance(report) {
841
849
  "Run `memorax-code start`, then restart or refresh OpenCode.",
842
850
  ];
843
851
  }
852
+ if (report.traeAdapter && !isAdapterReady(report.traeAdapter)) {
853
+ return [
854
+ red("Trae adapter is not enabled."),
855
+ "Run `memorax-code start --clients trae`, then enable Global Hooks in Trae Settings if this is the first setup.",
856
+ ];
857
+ }
844
858
  return [
845
859
  red("MemoraX Code needs attention."),
846
860
  "Run `memorax-code status` and `memorax-code logs` for details.",
@@ -884,9 +898,11 @@ function adapterStatusLine(report) {
884
898
  const skillStatus = report.codexSkills?.status
885
899
  ?? report.claudeSkills?.status
886
900
  ?? report.opencodeSkills?.status
887
- ?? report.codebuddySkills?.status;
901
+ ?? report.codebuddySkills?.status
902
+ ?? report.traeSkills?.status;
888
903
  const skills = skillStatus ? ` skills=${skillStatus}` : "";
889
- const hooks = report.codebuddyHooks?.status ? ` hook-runtime=${report.codebuddyHooks.status}` : "";
904
+ const hookStatus = report.codebuddyHooks?.status ?? report.traeHooks?.status;
905
+ const hooks = hookStatus ? ` hook-runtime=${hookStatus}` : "";
890
906
  const changed = report.changed === true ? " changed" : "";
891
907
  const integration = report.integration ?? report.state?.integration ?? "hooks";
892
908
  return `${enabled ? "ok" : "not enabled"} integration=${integration}${skills}${hooks}${changed}`;
@@ -897,6 +913,8 @@ function selectedLifecycleClientNames(report) {
897
913
  report.claudeAdapter ? "Claude Code" : undefined,
898
914
  report.dshAdapter ? "DSH" : undefined,
899
915
  report.opencodeAdapter ? "OpenCode" : undefined,
916
+ report.codebuddyAdapter ? "CodeBuddy/WorkBuddy" : undefined,
917
+ report.traeAdapter ? "Trae" : undefined,
900
918
  ].filter((name) => name !== undefined);
901
919
  }
902
920
  function lifecycleClientName(report) {
@@ -12,6 +12,8 @@ export function readActiveManagedClients(memoraxCodeHome) {
12
12
  return undefined;
13
13
  if (value.codebuddy !== undefined && typeof value.codebuddy !== "boolean")
14
14
  return undefined;
15
+ if (value.trae !== undefined && typeof value.trae !== "boolean")
16
+ return undefined;
15
17
  if (value.dsh !== undefined && typeof value.dsh !== "boolean")
16
18
  return undefined;
17
19
  return {
@@ -22,6 +24,7 @@ export function readActiveManagedClients(memoraxCodeHome) {
22
24
  dsh: value.dsh === true,
23
25
  opencode: value.opencode === true,
24
26
  ...(value.codebuddy === true ? { codebuddy: true } : {}),
27
+ ...(value.trae === true ? { trae: true } : {}),
25
28
  };
26
29
  }
27
30
  catch {
@@ -9,14 +9,17 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
9
9
  const dshProfileLifecycle = await loadDshProfileLifecycle();
10
10
  const openCodePluginInstaller = await loadOpenCodePluginInstaller();
11
11
  const codeBuddyPluginInstaller = await loadCodeBuddyPluginInstaller();
12
+ const traePluginInstaller = await loadTraePluginInstaller();
12
13
  const home = resolveHome(options.homeDir);
13
14
  const memoraxCodeHome = resolve(options.memoraxCodeHome ?? process.env.MEMORAX_CODE_HOME ?? join(home, ".memorax-code"));
14
15
  const claudeState = await readJsonRecord(join(memoraxCodeHome, "adapters", "claude-code", "state.json"));
15
16
  const claudeHome = options.claudeHome ?? stringField(claudeState, "claudeHome");
17
+ const traeState = await readJsonRecord(join(memoraxCodeHome, "adapters", "trae", "state.json"));
18
+ const traeHome = options.traeHome ?? stringField(traeState, "traeHome");
16
19
  return async () => {
17
20
  try {
18
21
  return await withBackendLifecycleLock({ home: memoraxCodeHome }, async () => {
19
- const [codexPlugin, claudePlugin, dshPlugin, opencodePlugin, codebuddyPlugin] = await Promise.all([
22
+ const [codexPlugin, claudePlugin, dshPlugin, opencodePlugin, codebuddyPlugin, traePlugin] = await Promise.all([
20
23
  cleanupCodexAfterBackendRemoval({
21
24
  memoraxCodeHome,
22
25
  homeDir: home,
@@ -52,15 +55,20 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
52
55
  ...(options.codeBuddyHome ? { codeBuddyHome: options.codeBuddyHome } : {}),
53
56
  }))
54
57
  .catch((error) => removalFailure("codebuddy-plugin-remove", error)),
58
+ traePluginInstaller.removeTraeAdapterInstallation({
59
+ memoraxCodeHome,
60
+ ...(traeHome ? { traeHome } : {}),
61
+ }).catch((error) => removalFailure("trae-adapter-remove", error)),
55
62
  ]);
56
63
  return {
57
- ok: codexPlugin.ok && claudePlugin.ok && dshPlugin.ok && opencodePlugin.ok && codebuddyPlugin.ok,
64
+ ok: codexPlugin.ok && claudePlugin.ok && dshPlugin.ok && opencodePlugin.ok && codebuddyPlugin.ok && traePlugin.ok,
58
65
  action: "client-plugin-removal-cleanup",
59
66
  codexPlugin,
60
67
  claudePlugin,
61
68
  dshPlugin,
62
69
  opencodePlugin,
63
70
  codebuddyPlugin,
71
+ traePlugin,
64
72
  };
65
73
  });
66
74
  }
@@ -74,6 +82,7 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
74
82
  dshPlugin: failure,
75
83
  opencodePlugin: failure,
76
84
  codebuddyPlugin: failure,
85
+ traePlugin: failure,
77
86
  };
78
87
  }
79
88
  };
@@ -87,6 +96,9 @@ async function loadOpenCodePluginInstaller() {
87
96
  async function loadCodeBuddyPluginInstaller() {
88
97
  return await import(new URL("../../../memorax-code-codebuddy-adapter/src/config.mjs", import.meta.url).href);
89
98
  }
99
+ async function loadTraePluginInstaller() {
100
+ return await import(new URL("../../../memorax-code-trae-adapter/src/config.mjs", import.meta.url).href);
101
+ }
90
102
  async function loadDshProfileLifecycle() {
91
103
  return await import(new URL("../../../memorax-code-dsh-adapter/src/profile-lifecycle.mjs", import.meta.url).href);
92
104
  }
@@ -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, codebuddy: true });
2
+ const allClients = Object.freeze({ codex: true, claude: true, dsh: true, opencode: true, codebuddy: true, trae: true });
3
3
  export function resolveManagedClients(argv, config = {}) {
4
4
  const explicit = argValue(argv, "--clients");
5
5
  if (explicit !== undefined)
@@ -13,6 +13,7 @@ export function resolveManagedClients(argv, config = {}) {
13
13
  dsh: config.clients.dsh !== false,
14
14
  opencode: config.clients.opencode === true,
15
15
  ...(config.clients.codebuddy === true ? { codebuddy: true } : {}),
16
+ ...(config.clients.trae === true ? { trae: true } : {}),
16
17
  };
17
18
  }
18
19
  return { codex: true, claude: true, dsh: true, opencode: true };
@@ -24,8 +25,8 @@ export function parseManagedClients(value) {
24
25
  if (normalized === "none")
25
26
  return { codex: false, claude: false, dsh: false, opencode: false };
26
27
  const names = normalized.split(",").map((name) => name.trim()).filter(Boolean);
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
+ if (names.length === 0 || names.some((name) => (name !== "codex" && name !== "claude" && name !== "dsh" && name !== "opencode" && name !== "codebuddy" && name !== "trae"))) {
29
+ throw new Error(`invalid --clients value: ${value}; expected a comma-separated subset of codex, claude, dsh, opencode, codebuddy, trae, or all or none`);
29
30
  }
30
31
  return {
31
32
  codex: names.includes("codex"),
@@ -33,6 +34,7 @@ export function parseManagedClients(value) {
33
34
  dsh: names.includes("dsh"),
34
35
  opencode: names.includes("opencode"),
35
36
  ...(names.includes("codebuddy") ? { codebuddy: true } : {}),
37
+ ...(names.includes("trae") ? { trae: true } : {}),
36
38
  };
37
39
  }
38
40
  export function loadManagedClientsConfig(memoraxCodeHome) {