@memorax/memorax-code 0.1.1 → 0.1.2

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 (48) hide show
  1. package/bin/memorax-code-plugin-postinstall.mjs +88 -21
  2. package/docs/configuration.md +15 -0
  3. package/docs/troubleshooting.md +26 -0
  4. package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +1 -1
  5. package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs +38 -0
  6. package/lib/memorax-code-backend/dist/automatic-memory-writeback.js +7 -0
  7. package/lib/memorax-code-backend/dist/claude-memory-hook-runtime.js +10 -3
  8. package/lib/memorax-code-backend/dist/codex-memory-hook-runtime.js +10 -3
  9. package/lib/memorax-code-backend/dist/memorax-cli.js +2 -0
  10. package/lib/memorax-code-backend/dist/memory-cli.js +24 -2
  11. package/lib/memorax-code-backend/dist/memory-service.js +3 -1
  12. package/lib/memorax-code-backend/dist/memory-turn-coordinator.js +6 -3
  13. package/lib/memorax-code-backend/dist/memory-viewer-icon.js +1 -0
  14. package/lib/memorax-code-backend/dist/memory-viewer-user-html.js +6 -5
  15. package/lib/memorax-code-backend/dist/memory-writeback-buffer.js +29 -0
  16. package/lib/memorax-code-backend/dist/repository-memory-context.js +24 -3
  17. package/lib/memorax-code-backend/dist/repository-memory-scope.js +81 -33
  18. package/lib/memorax-code-backend/dist/server-cli.js +30 -7
  19. package/lib/memorax-code-backend/dist/server-memory-viewer.js +11 -0
  20. package/lib/memorax-code-backend/package.json +1 -1
  21. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  22. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  23. package/lib/memorax-code-claude-adapter/package.json +1 -1
  24. package/lib/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -0
  25. package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +12 -13
  26. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +4 -0
  27. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +3 -1
  28. package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
  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/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +1 -1
  32. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs +38 -0
  33. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  34. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -0
  35. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +12 -13
  36. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +4 -0
  37. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +3 -1
  38. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
  39. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  40. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  41. package/lib/memorax-code-codex-adapter/package.json +1 -1
  42. package/lib/memorax-code-codex-adapter/runtime-hooks/memory-skill-reminder.mjs +6 -0
  43. package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +12 -13
  44. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +4 -0
  45. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +3 -1
  46. package/lib/resolve-claude-command.mjs +154 -1
  47. package/lib/resolve-codex-command.mjs +56 -1
  48. package/package.json +1 -1
@@ -1,21 +1,20 @@
1
1
  ---
2
2
  name: memorax-code
3
3
  description: >-
4
- Use this skill as the single entry point for persistent coding and
5
- repository-local memory work, regardless of type, wording, or operation. Use
6
- it whenever a request may involve information that should persist beyond the
7
- current task,
8
- knowledge remembered from prior work, repository memory,
9
- reusable procedures or working rules, or durable user profile and interaction
10
- preferences. This applies even when the user does not call it memory,
11
- including naturally stated habits, preferences,
4
+ Use this skill as the single router for persistent coding and repository-local
5
+ memory. Invoke it whenever a request may involve prior-work knowledge,
6
+ repository memory, reusable procedures or rules, durable profile or
7
+ interaction preferences, or information worth retaining beyond the current
8
+ task. This applies without memory wording, including habits, preferences,
12
9
  checklists, action sequences, prerequisites, gates, exceptions, validation
13
10
  rules, communication style, preferred language, or result presentation.
14
- Determine whether the request belongs to MemoraX Code coding memory,
15
- repository memory, personal procedure memory, personal profile
16
- memory, or no persistent memory, then route it to the appropriate operation.
17
- Prefer this router over any underlying memory workflow. Ask one focused
18
- question only when the memory authority remains ambiguous after routing.
11
+ Classify the request as coding memory, repository memory, personal procedure
12
+ memory, personal profile memory, or no persistent memory, then route it to the
13
+ matching operation. Invoking this router does not require coding-memory
14
+ search. Reuse a relevant coding-memory result already retrieved in this
15
+ conversation; otherwise let the matching operation decide on search. Prefer
16
+ this router over underlying memory workflows. Ask one focused question only when
17
+ memory authority remains ambiguous.
19
18
  ---
20
19
 
21
20
  # MemoraX Code
@@ -71,6 +71,10 @@ anchors: stable/source/path' \
71
71
 
72
72
  If add fails, report the exact failure and do not retry automatically, bypass the CLI, or call MemoraX directly.
73
73
 
74
+ If a successful Add result reports `workspaceScopeFallbackReason: git_metadata_invalid`, malformed or incomplete metadata inside a direct `.git` directory was downgraded to the normalized local folder scope. Add has already been submitted with the reported `effectiveUserId`. Present its `userNotice` once without pausing the current task or asking the user to repair Git first, then continue the current task. After the repository or `.git` metadata is repaired, later Search, Add, and automatic writeback in the same client session automatically use the restored Git repository scope.
75
+
76
+ If `memorax-cli add` reports `workspace_scope_mismatch` or `workspace_scope_unavailable`, do not bypass the scope. Do not change the CLI working directory and retry. Tell the user that the memory was not submitted and no request was sent to MemoraX, then present the CLI's `userAction` in natural language. Continue the current task using only live code and documentation.
77
+
74
78
  ## Exclusions
75
79
 
76
80
  Do not add secrets, credentials, private URLs, raw authorization headers, exact patches, target commits, hidden tests, target diffs, vulnerability details, exploit steps, copied source, long logs, stack traces, raw transcripts, temporary errors, or facts directly recoverable from current files and git history.
@@ -8,7 +8,9 @@ Run the CLI from the active task workspace. The installed Hook and session bindi
8
8
 
9
9
  Coding memory uses `<MemoraX base user ID>@<normalized repository name>` for Git workspaces and the normalized folder name for genuine non-Git directories. MemoraX Code resolves `.git`, `gitdir`, and `commondir` without executing Git. Linked worktrees share one repository scope; another clone, repository, or non-Git directory retains a different local session key even when its readable name matches.
10
10
 
11
- Require a readable active workspace binding. A CLI command from a linked worktree of the bound repository is valid. If the CLI reports `workspace_scope_mismatch`, ask the user to start a task in the target repository or local workspace. Do not retry from an unrelated scope or fall back to an unscoped user id.
11
+ If a successful Search result reports `workspaceScopeFallbackReason: git_metadata_invalid`, malformed or incomplete metadata inside a direct `.git` directory was downgraded to the normalized local folder scope. Search has already run with the reported `effectiveUserId`. Present its `userNotice` once without pausing the current task or asking the user to repair Git first, then continue with the returned memory and live evidence. After the repository or `.git` metadata is repaired, later Search, Add, and automatic writeback in the same client session automatically use the restored Git repository scope.
12
+
13
+ Require a readable active workspace binding. A CLI command from a linked worktree of the bound repository is valid. If `memorax-cli search` reports `workspace_scope_mismatch` or `workspace_scope_unavailable`, do not bypass the scope or fall back to an unscoped user id. Do not change the CLI working directory and retry. Tell the user that memory search was not executed and no request was sent to MemoraX, then present the CLI's `userAction` in natural language. Continue the current task using only live code and documentation.
12
14
 
13
15
  If `memorax-cli` is not on `PATH`, or memory is disabled, unconfigured, or unavailable, report that briefly and continue with live code or documentation. Authenticate through MemoraX Code configuration; never recover credentials from shell history or place tokens in prompts. Treat injected memory as a hypothesis and verify it against the current checkout.
14
16
 
@@ -35,6 +35,7 @@ const STABLE_SHELL_REQUIRED_FILES = Object.freeze([
35
35
  "memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs",
36
36
  "memorax-code-adapter-common/src/hooks/hook-runtime-generation.mjs",
37
37
  "memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs",
38
+ "memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs",
38
39
  "memorax-code-adapter-common/src/repo-memory/repo-memory-job-context.mjs",
39
40
  "memorax-code-adapter-common/src/repo-memory/repo-procedure-memory-context.mjs",
40
41
  "memorax-code-adapter-common/src/repo-memory/repo-user-profile-context.mjs",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorax-code-codex-adapter",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Codex adapter, memory skill, and local hooks for MemoraX Code.",
5
5
  "author": {
6
6
  "name": "MemoraX AI"
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "version": 1,
3
- "shellVersion": "0.1.1",
3
+ "shellVersion": "0.1.2",
4
4
  "runtimeAbi": 1
5
5
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorax-code/codex-adapter",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "Codex Hook and memory integration for MemoraX Code.",
@@ -5,6 +5,7 @@ import {
5
5
  } from "../../memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs";
6
6
  import { resolveBackendConnection } from "../../memorax-code-adapter-common/src/backend-connection.mjs";
7
7
  import { readStdinJson, stringOption } from "../../memorax-code-adapter-common/src/config-utils.mjs";
8
+ import { scheduleMissingRepoMemoryBuild } from "../../memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs";
8
9
  import { isRepoMemoryJobWorker } from "../../memorax-code-adapter-common/src/repo-memory/repo-memory-job-context.mjs";
9
10
  import { buildRepoProcedureMemoryContext } from "../../memorax-code-adapter-common/src/repo-memory/repo-procedure-memory-context.mjs";
10
11
  import { buildRepoUserProfilePreferencesContext } from "../../memorax-code-adapter-common/src/repo-memory/repo-user-profile-context.mjs";
@@ -25,6 +26,10 @@ try {
25
26
  const turnStart = turnStartBody(input);
26
27
  if (turnStart) {
27
28
  const turnStartResult = await recordTurnStart(turnStart);
29
+ scheduleMissingRepoMemoryBuild(turnStartResult.repoMemoryWorktree, {
30
+ debugEnv: "MEMORAX_CODE_CODEX_HOOK_DEBUG",
31
+ pluginRoot: process.env.PLUGIN_ROOT,
32
+ });
28
33
  const normalizedInput = turnStart.workspaceKind
29
34
  ? { ...input, workspaceKind: turnStart.workspaceKind }
30
35
  : input;
@@ -69,6 +74,7 @@ async function recordTurnStart(body) {
69
74
  return {
70
75
  recorded: true,
71
76
  additionalContext: stringValue(response?.additionalContext),
77
+ repoMemoryWorktree: stringValue(response?.repoMemoryWorktree),
72
78
  };
73
79
  } catch (error) {
74
80
  debugHookError(error);
@@ -1,21 +1,20 @@
1
1
  ---
2
2
  name: memorax-code
3
3
  description: >-
4
- Use this skill as the single entry point for persistent coding and
5
- repository-local memory work, regardless of type, wording, or operation. Use
6
- it whenever a request may involve information that should persist beyond the
7
- current task,
8
- knowledge remembered from prior work, repository memory,
9
- reusable procedures or working rules, or durable user profile and interaction
10
- preferences. This applies even when the user does not call it memory,
11
- including naturally stated habits, preferences,
4
+ Use this skill as the single router for persistent coding and repository-local
5
+ memory. Invoke it whenever a request may involve prior-work knowledge,
6
+ repository memory, reusable procedures or rules, durable profile or
7
+ interaction preferences, or information worth retaining beyond the current
8
+ task. This applies without memory wording, including habits, preferences,
12
9
  checklists, action sequences, prerequisites, gates, exceptions, validation
13
10
  rules, communication style, preferred language, or result presentation.
14
- Determine whether the request belongs to MemoraX Code coding memory,
15
- repository memory, personal procedure memory, personal profile
16
- memory, or no persistent memory, then route it to the appropriate operation.
17
- Prefer this router over any underlying memory workflow. Ask one focused
18
- question only when the memory authority remains ambiguous after routing.
11
+ Classify the request as coding memory, repository memory, personal procedure
12
+ memory, personal profile memory, or no persistent memory, then route it to the
13
+ matching operation. Invoking this router does not require coding-memory
14
+ search. Reuse a relevant coding-memory result already retrieved in this
15
+ conversation; otherwise let the matching operation decide on search. Prefer
16
+ this router over underlying memory workflows. Ask one focused question only when
17
+ memory authority remains ambiguous.
19
18
  ---
20
19
 
21
20
  # MemoraX Code
@@ -71,6 +71,10 @@ anchors: stable/source/path' \
71
71
 
72
72
  If add fails, report the exact failure and do not retry automatically, bypass the CLI, or call MemoraX directly.
73
73
 
74
+ If a successful Add result reports `workspaceScopeFallbackReason: git_metadata_invalid`, malformed or incomplete metadata inside a direct `.git` directory was downgraded to the normalized local folder scope. Add has already been submitted with the reported `effectiveUserId`. Present its `userNotice` once without pausing the current task or asking the user to repair Git first, then continue the current task. After the repository or `.git` metadata is repaired, later Search, Add, and automatic writeback in the same client session automatically use the restored Git repository scope.
75
+
76
+ If `memorax-cli add` reports `workspace_scope_mismatch` or `workspace_scope_unavailable`, do not bypass the scope. Do not change the CLI working directory and retry. Tell the user that the memory was not submitted and no request was sent to MemoraX, then present the CLI's `userAction` in natural language. Continue the current task using only live code and documentation.
77
+
74
78
  ## Exclusions
75
79
 
76
80
  Do not add secrets, credentials, private URLs, raw authorization headers, exact patches, target commits, hidden tests, target diffs, vulnerability details, exploit steps, copied source, long logs, stack traces, raw transcripts, temporary errors, or facts directly recoverable from current files and git history.
@@ -8,7 +8,9 @@ Run the CLI from the active task workspace. The installed Hook and session bindi
8
8
 
9
9
  Coding memory uses `<MemoraX base user ID>@<normalized repository name>` for Git workspaces and the normalized folder name for genuine non-Git directories. MemoraX Code resolves `.git`, `gitdir`, and `commondir` without executing Git. Linked worktrees share one repository scope; another clone, repository, or non-Git directory retains a different local session key even when its readable name matches.
10
10
 
11
- Require a readable active workspace binding. A CLI command from a linked worktree of the bound repository is valid. If the CLI reports `workspace_scope_mismatch`, ask the user to start a task in the target repository or local workspace. Do not retry from an unrelated scope or fall back to an unscoped user id.
11
+ If a successful Search result reports `workspaceScopeFallbackReason: git_metadata_invalid`, malformed or incomplete metadata inside a direct `.git` directory was downgraded to the normalized local folder scope. Search has already run with the reported `effectiveUserId`. Present its `userNotice` once without pausing the current task or asking the user to repair Git first, then continue with the returned memory and live evidence. After the repository or `.git` metadata is repaired, later Search, Add, and automatic writeback in the same client session automatically use the restored Git repository scope.
12
+
13
+ Require a readable active workspace binding. A CLI command from a linked worktree of the bound repository is valid. If `memorax-cli search` reports `workspace_scope_mismatch` or `workspace_scope_unavailable`, do not bypass the scope or fall back to an unscoped user id. Do not change the CLI working directory and retry. Tell the user that memory search was not executed and no request was sent to MemoraX, then present the CLI's `userAction` in natural language. Continue the current task using only live code and documentation.
12
14
 
13
15
  If `memorax-cli` is not on `PATH`, or memory is disabled, unconfigured, or unavailable, report that briefly and continue with live code or documentation. Authenticate through MemoraX Code configuration; never recover credentials from shell history or place tokens in prompts. Treat injected memory as a hypothesis and verify it against the current checkout.
14
16
 
@@ -1,17 +1,30 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { readdirSync } from "node:fs";
1
3
  import { homedir } from "node:os";
2
- import { join } from "node:path";
4
+ import { join, win32 } from "node:path";
3
5
  import {
4
6
  commandOnPath,
5
7
  defaultVsCodeExtensionRoots,
6
8
  findVsCodeExtensionCommand,
9
+ isExecutableCommand,
7
10
  } from "./vscode-extension-command.mjs";
8
11
 
12
+ const CLAUDE_DESKTOP_MACOS_RUNTIME_SEGMENTS = ["claude.app", "Contents", "MacOS", "claude"];
13
+ const CLAUDE_DESKTOP_CODE_PROBE_TIMEOUT_MS = 10_000;
14
+ const CLAUDE_DESKTOP_WINDOWS_PACKAGE_NAME = "Claude";
15
+ const CLAUDE_DESKTOP_WINDOWS_QUERY_TIMEOUT_MS = 10_000;
16
+ const CLAUDE_DESKTOP_WINDOWS_RUNTIME_SEGMENTS = ["claude.exe"];
17
+
9
18
  export function resolveClaudeCommand({
10
19
  env = process.env,
11
20
  homeDir = homedir(),
12
21
  platform = process.platform,
13
22
  arch = process.arch,
23
+ desktopCodeRoots = defaultClaudeDesktopCodeRoots(env, homeDir, platform),
24
+ desktopCodeProbe = spawnSync,
14
25
  vscodeExtensionRoots = defaultVsCodeExtensionRoots(homeDir),
26
+ windowsAppPackageFamilies,
27
+ windowsAppQuery = spawnSync,
15
28
  } = {}) {
16
29
  const configured = nonEmpty(env.MEMORAX_CODE_CLAUDE_COMMAND);
17
30
  if (configured) return { command: configured, source: "configured" };
@@ -20,6 +33,24 @@ export function resolveClaudeCommand({
20
33
  return { command: "claude", source: "path" };
21
34
  }
22
35
 
36
+ const desktopCommand = findClaudeDesktopCodeCommand(
37
+ desktopCodeRoots,
38
+ platform,
39
+ env,
40
+ desktopCodeProbe,
41
+ );
42
+ if (desktopCommand) return { command: desktopCommand, source: "app-bundled" };
43
+
44
+ const windowsDesktopCommand = resolveWindowsClaudeDesktopCodeCommand({
45
+ env,
46
+ homeDir,
47
+ packageFamilies: windowsAppPackageFamilies,
48
+ platform,
49
+ desktopCodeProbe,
50
+ spawnSyncImpl: windowsAppQuery,
51
+ });
52
+ if (windowsDesktopCommand) return { command: windowsDesktopCommand, source: "app-bundled" };
53
+
23
54
  const vscodeCommand = findVsCodeExtensionCommand({
24
55
  extensionId: "anthropic.claude-code",
25
56
  extensionRoots: vscodeExtensionRoots,
@@ -41,6 +72,128 @@ export function ensureClaudeCommandEnv(options = {}) {
41
72
  return resolved;
42
73
  }
43
74
 
75
+ export function resolveWindowsClaudeDesktopCodeCommand({
76
+ env = process.env,
77
+ homeDir = homedir(),
78
+ packageFamilies,
79
+ platform = process.platform,
80
+ desktopCodeProbe = spawnSync,
81
+ spawnSyncImpl = spawnSync,
82
+ } = {}) {
83
+ if (platform !== "win32") return undefined;
84
+ const localAppData = nonEmpty(env.LOCALAPPDATA) ?? join(homeDir, "AppData", "Local");
85
+ const families = packageFamilies ?? queryWindowsClaudeAppPackageFamilies(env, spawnSyncImpl);
86
+ const roots = families.map((family) => join(
87
+ localAppData,
88
+ "Packages",
89
+ family,
90
+ "LocalCache",
91
+ "Roaming",
92
+ "Claude",
93
+ "claude-code",
94
+ ));
95
+ return findClaudeDesktopCodeCommand(roots, platform, env, desktopCodeProbe);
96
+ }
97
+
98
+ function findClaudeDesktopCodeCommand(roots, platform, env, spawnSyncImpl) {
99
+ const runtimeSegments = claudeDesktopRuntimeSegments(platform);
100
+ if (!runtimeSegments) return undefined;
101
+ for (const root of new Set(roots)) {
102
+ let versions;
103
+ try {
104
+ versions = readdirSync(root, { withFileTypes: true })
105
+ .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
106
+ .map((entry) => entry.name)
107
+ .sort((left, right) => right.localeCompare(left, "en", { numeric: true, sensitivity: "base" }));
108
+ } catch {
109
+ continue;
110
+ }
111
+ for (const version of versions) {
112
+ const command = join(root, version, ...runtimeSegments);
113
+ if (
114
+ isExecutableCommand(command, platform)
115
+ && claudeDesktopCodeCommandIsRunnable(command, env, spawnSyncImpl)
116
+ ) {
117
+ return command;
118
+ }
119
+ }
120
+ }
121
+ return undefined;
122
+ }
123
+
124
+ function claudeDesktopCodeCommandIsRunnable(command, env, spawnSyncImpl) {
125
+ let result;
126
+ try {
127
+ result = spawnSyncImpl(
128
+ command,
129
+ ["--version"],
130
+ {
131
+ encoding: "utf8",
132
+ env,
133
+ stdio: ["ignore", "pipe", "pipe"],
134
+ timeout: CLAUDE_DESKTOP_CODE_PROBE_TIMEOUT_MS,
135
+ windowsHide: true,
136
+ },
137
+ );
138
+ } catch {
139
+ return false;
140
+ }
141
+ return result.status === 0 && !result.error && !result.signal;
142
+ }
143
+
144
+ function defaultClaudeDesktopCodeRoots(env, homeDir, platform) {
145
+ if (platform === "darwin") {
146
+ return [join(homeDir, "Library", "Application Support", "Claude", "claude-code")];
147
+ }
148
+ if (platform === "win32") {
149
+ const appData = nonEmpty(env.APPDATA) ?? join(homeDir, "AppData", "Roaming");
150
+ return [join(appData, "Claude", "claude-code")];
151
+ }
152
+ return [];
153
+ }
154
+
155
+ function claudeDesktopRuntimeSegments(platform) {
156
+ if (platform === "darwin") return CLAUDE_DESKTOP_MACOS_RUNTIME_SEGMENTS;
157
+ if (platform === "win32") return CLAUDE_DESKTOP_WINDOWS_RUNTIME_SEGMENTS;
158
+ return undefined;
159
+ }
160
+
161
+ function queryWindowsClaudeAppPackageFamilies(env, spawnSyncImpl) {
162
+ const powershell = windowsPowerShellCommand(env);
163
+ const script = [
164
+ "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)",
165
+ `Get-AppxPackage -Name '${CLAUDE_DESKTOP_WINDOWS_PACKAGE_NAME}' -ErrorAction SilentlyContinue | ForEach-Object { $_.PackageFamilyName }`,
166
+ ].join("; ");
167
+ let result;
168
+ try {
169
+ result = spawnSyncImpl(
170
+ powershell,
171
+ ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", script],
172
+ {
173
+ encoding: "utf8",
174
+ env,
175
+ stdio: ["ignore", "pipe", "pipe"],
176
+ timeout: CLAUDE_DESKTOP_WINDOWS_QUERY_TIMEOUT_MS,
177
+ windowsHide: true,
178
+ },
179
+ );
180
+ } catch {
181
+ return [];
182
+ }
183
+ if (result.status !== 0 || result.error || result.signal) return [];
184
+ return [...new Set(String(result.stdout ?? "")
185
+ .split(/\r?\n/)
186
+ .map((value) => value.replace(/^\uFEFF/, "").trim())
187
+ .filter((value) => /^[A-Za-z0-9][A-Za-z0-9.-]*_[A-Za-z0-9]+$/.test(value)))];
188
+ }
189
+
190
+ function windowsPowerShellCommand(env) {
191
+ const systemRoot = nonEmpty(env.SystemRoot) ?? nonEmpty(env.SYSTEMROOT);
192
+ return systemRoot
193
+ ? win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
194
+ : "powershell.exe";
195
+ }
196
+
44
197
  function nonEmpty(value) {
45
198
  const normalized = String(value ?? "").trim();
46
199
  return normalized || undefined;
@@ -1,6 +1,7 @@
1
+ import { spawnSync } from "node:child_process";
1
2
  import { readdirSync } from "node:fs";
2
3
  import { homedir } from "node:os";
3
- import { join } from "node:path";
4
+ import { join, win32 } from "node:path";
4
5
  import {
5
6
  commandOnPath,
6
7
  defaultVsCodeExtensionRoots,
@@ -9,6 +10,8 @@ import {
9
10
  } from "./vscode-extension-command.mjs";
10
11
 
11
12
  const APP_BUNDLE_NAMES = ["ChatGPT.app", "Codex.app"];
13
+ const WINDOWS_CODEX_APP_PROBE_TIMEOUT_MS = 10_000;
14
+ const WINDOWS_CODEX_APP_RUNTIME_SEGMENTS = ["plugins", ".plugin-appserver", "codex.exe"];
12
15
 
13
16
  export function resolveCodexCommand({
14
17
  env = process.env,
@@ -17,6 +20,9 @@ export function resolveCodexCommand({
17
20
  arch = process.arch,
18
21
  applicationRoots = [join(homeDir, "Applications"), "/Applications"],
19
22
  vscodeExtensionRoots = defaultVsCodeExtensionRoots(homeDir),
23
+ windowsAppProbe = spawnSync,
24
+ windowsAppRuntimePaths,
25
+ windowsPathExists = isExecutableCommand,
20
26
  } = {}) {
21
27
  const npmOverride = nonEmpty(env.MEMORAX_CODE_CODEX_COMMAND);
22
28
  if (npmOverride) return { command: npmOverride, source: "npm-override" };
@@ -37,6 +43,16 @@ export function resolveCodexCommand({
37
43
  }
38
44
  }
39
45
 
46
+ const windowsAppCommand = resolveWindowsCodexAppCommand({
47
+ env,
48
+ homeDir,
49
+ pathExists: windowsPathExists,
50
+ platform,
51
+ runtimePaths: windowsAppRuntimePaths,
52
+ spawnSyncImpl: windowsAppProbe,
53
+ });
54
+ if (windowsAppCommand) return { command: windowsAppCommand, source: "app-bundled" };
55
+
40
56
  const vscodeCommand = findVsCodeExtensionCommand({
41
57
  extensionId: "openai.chatgpt",
42
58
  extensionRoots: vscodeExtensionRoots,
@@ -49,6 +65,25 @@ export function resolveCodexCommand({
49
65
  return { command: "codex", source: "unavailable" };
50
66
  }
51
67
 
68
+ export function resolveWindowsCodexAppCommand({
69
+ env = process.env,
70
+ homeDir = homedir(),
71
+ pathExists = isExecutableCommand,
72
+ platform = process.platform,
73
+ runtimePaths,
74
+ spawnSyncImpl = spawnSync,
75
+ } = {}) {
76
+ if (platform !== "win32") return undefined;
77
+ const codexHome = nonEmpty(env.CODEX_HOME) ?? win32.join(homeDir, ".codex");
78
+ const candidates = runtimePaths ?? [win32.join(codexHome, ...WINDOWS_CODEX_APP_RUNTIME_SEGMENTS)];
79
+ for (const command of candidates) {
80
+ if (pathExists(command, platform) && windowsCodexCommandIsRunnable(command, env, spawnSyncImpl)) {
81
+ return command;
82
+ }
83
+ }
84
+ return undefined;
85
+ }
86
+
52
87
  export function ensureCodexCommandEnv(options = {}) {
53
88
  const env = options.env ?? process.env;
54
89
  const resolved = resolveCodexCommand({ ...options, env });
@@ -88,6 +123,26 @@ function codexPlatformDirectories(platform, arch) {
88
123
  return [];
89
124
  }
90
125
 
126
+ function windowsCodexCommandIsRunnable(command, env, spawnSyncImpl) {
127
+ let result;
128
+ try {
129
+ result = spawnSyncImpl(
130
+ command,
131
+ ["--version"],
132
+ {
133
+ encoding: "utf8",
134
+ env,
135
+ stdio: ["ignore", "pipe", "pipe"],
136
+ timeout: WINDOWS_CODEX_APP_PROBE_TIMEOUT_MS,
137
+ windowsHide: true,
138
+ },
139
+ );
140
+ } catch {
141
+ return false;
142
+ }
143
+ return result.status === 0 && !result.error && !result.signal;
144
+ }
145
+
91
146
  function nonEmpty(value) {
92
147
  const normalized = String(value ?? "").trim();
93
148
  return normalized || undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memorax/memorax-code",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "MemoraX Code CLI, local memory backend, Codex and Claude Code adapters, skills, and Memory Viewer.",
5
5
  "license": "MIT",
6
6
  "type": "module",