@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.
- package/bin/memorax-code-plugin-postinstall.mjs +88 -21
- package/docs/configuration.md +15 -0
- package/docs/troubleshooting.md +26 -0
- package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +1 -1
- package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs +38 -0
- package/lib/memorax-code-backend/dist/automatic-memory-writeback.js +7 -0
- package/lib/memorax-code-backend/dist/claude-memory-hook-runtime.js +10 -3
- package/lib/memorax-code-backend/dist/codex-memory-hook-runtime.js +10 -3
- package/lib/memorax-code-backend/dist/memorax-cli.js +2 -0
- package/lib/memorax-code-backend/dist/memory-cli.js +24 -2
- package/lib/memorax-code-backend/dist/memory-service.js +3 -1
- package/lib/memorax-code-backend/dist/memory-turn-coordinator.js +6 -3
- package/lib/memorax-code-backend/dist/memory-viewer-icon.js +1 -0
- package/lib/memorax-code-backend/dist/memory-viewer-user-html.js +6 -5
- package/lib/memorax-code-backend/dist/memory-writeback-buffer.js +29 -0
- package/lib/memorax-code-backend/dist/repository-memory-context.js +24 -3
- package/lib/memorax-code-backend/dist/repository-memory-scope.js +81 -33
- package/lib/memorax-code-backend/dist/server-cli.js +30 -7
- package/lib/memorax-code-backend/dist/server-memory-viewer.js +11 -0
- package/lib/memorax-code-backend/package.json +1 -1
- package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -0
- package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +12 -13
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +4 -0
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +3 -1
- package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +1 -1
- 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
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +12 -13
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +4 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +3 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
- package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
- package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-codex-adapter/package.json +1 -1
- package/lib/memorax-code-codex-adapter/runtime-hooks/memory-skill-reminder.mjs +6 -0
- package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +12 -13
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +4 -0
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +3 -1
- package/lib/resolve-claude-command.mjs +154 -1
- package/lib/resolve-codex-command.mjs +56 -1
- 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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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",
|
|
@@ -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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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