open-memex 0.7.0-alpha.2 → 0.7.0-alpha.3
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/AGENTS.md +2 -2
- package/CHANGELOG.md +4 -0
- package/README.md +3 -2
- package/README.zh-CN.md +1 -1
- package/dist/config.js +7 -2
- package/dist/doctor.js +12 -4
- package/dist/paths.js +16 -2
- package/docs/V2-DESIGN.md +14 -3
- package/package.json +1 -1
- package/scripts/smoke-pure.ts +34 -0
- package/src/config.ts +6 -1
- package/src/doctor.ts +13 -4
- package/src/paths.ts +16 -1
package/AGENTS.md
CHANGED
|
@@ -102,7 +102,7 @@ The published `open-memex` bin points at `dist/cli.js` (compiled at publish time
|
|
|
102
102
|
From a source checkout, `npm run cli` / `npm run mcp` still run `src/` directly
|
|
103
103
|
with type-stripping — no build step needed for development.
|
|
104
104
|
|
|
105
|
-
There is **no `npm test`** and no CI. Verification loop is: `npm run typecheck` + `smoke-pure.ts` + (if touching sqlite) `npm run cli -- reindex` against a scratch `
|
|
105
|
+
There is **no `npm test`** and no CI. Verification loop is: `npm run typecheck` + `smoke-pure.ts` + (if touching sqlite) `npm run cli -- reindex` against a scratch `OPEN_MEMEX_HOME`.
|
|
106
106
|
|
|
107
107
|
To load the plugin in opencode locally, `~/.config/opencode/opencode.jsonc` must have:
|
|
108
108
|
```
|
|
@@ -115,7 +115,7 @@ Restart opencode after any change — the plugin is not hot-reloaded.
|
|
|
115
115
|
Storage root:
|
|
116
116
|
- Windows: `%APPDATA%\open-memex\`
|
|
117
117
|
- Linux/macOS: `$XDG_DATA_HOME/open-memex/` (fallback `~/.local/share/open-memex/`)
|
|
118
|
-
- Override with `
|
|
118
|
+
- Override with `OPEN_MEMEX_HOME` (use this for tests to avoid clobbering real data). The pre-rename `MY_O_MEMORY_HOME` is still honored as a fallback (D62).
|
|
119
119
|
|
|
120
120
|
Layout: `memories/<scope_key>/<id>.md` (YAML frontmatter + body) + `index.db` (SQLite FTS5).
|
|
121
121
|
|
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,10 @@ the `@alpha` tag are development snapshots and are not listed individually.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## Environment variable names (D62)
|
|
13
|
+
|
|
14
|
+
- The override variables now answer to `OPEN_MEMEX_HOME` (storage root) and `OPEN_MEMEX_CONFIG` (config path). The pre-rename `MY_O_MEMORY_HOME` / `MY_O_MEMORY_CONFIG` names keep working as fallbacks — nothing to migrate, the data root itself does not move. `doctor` notes when the storage root came from the legacy variable.
|
|
15
|
+
|
|
12
16
|
## Proactive capture: checkpoint proposals and aliases (D61)
|
|
13
17
|
|
|
14
18
|
- At task checkpoints the agent now proposes instead of waiting to be asked: when a task wraps up, it offers 1–3 short candidate memories distilled from the session (decisions and their reasons, conventions, gotchas, approaches tried and abandoned) and saves only the ones you approve — approved inferences are marked `source: "inference"`. One memorability rubric and one search discipline (break the question into concepts, try several phrasings, check both scopes) are written into every guidance surface — the bundled skill, the AGENTS.md memory-hygiene footer, the MCP handshake, and the tool descriptions — so behavior is consistent across editors. Nothing is auto-drafted in the background; that posture waits on memory visibility.
|
package/README.md
CHANGED
|
@@ -578,8 +578,9 @@ branches and PRs like any other file.
|
|
|
578
578
|
|
|
579
579
|
Settings live in `~/.config/opencode/open-memex.jsonc` — the `opencode` in the
|
|
580
580
|
path is historical; this one file is shared by every client. Override the config
|
|
581
|
-
path with `
|
|
582
|
-
(
|
|
581
|
+
path with `OPEN_MEMEX_CONFIG` and the storage root with `OPEN_MEMEX_HOME`
|
|
582
|
+
(the pre-rename `MY_O_MEMORY_CONFIG` / `MY_O_MEMORY_HOME` names are still
|
|
583
|
+
honored as fallbacks).
|
|
583
584
|
|
|
584
585
|
Defaults:
|
|
585
586
|
|
package/README.zh-CN.md
CHANGED
|
@@ -391,7 +391,7 @@ npm install -g open-memex@latest # 或 @alpha
|
|
|
391
391
|
|
|
392
392
|
## 配置
|
|
393
393
|
|
|
394
|
-
设置放在 `~/.config/opencode/open-memex.jsonc`——路径里的 `opencode` 是历史名;这一个文件由所有客户端共享。用 `
|
|
394
|
+
设置放在 `~/.config/opencode/open-memex.jsonc`——路径里的 `opencode` 是历史名;这一个文件由所有客户端共享。用 `OPEN_MEMEX_CONFIG` 覆盖配置路径,用 `OPEN_MEMEX_HOME` 覆盖存储根目录(改名之前的旧变量名 `MY_O_MEMORY_CONFIG` / `MY_O_MEMORY_HOME` 仍然有效,作为回退)。
|
|
395
395
|
|
|
396
396
|
默认值:
|
|
397
397
|
|
package/dist/config.js
CHANGED
|
@@ -46,9 +46,13 @@ function stripJsonComments(raw) {
|
|
|
46
46
|
out = out.replace(/,(\s*[}\]])/g, "$1");
|
|
47
47
|
return out;
|
|
48
48
|
}
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Where `config set` / interactive `init` persist. Respects OPEN_MEMEX_CONFIG
|
|
51
|
+
* (D62); MY_O_MEMORY_CONFIG is the pre-rename fallback, still honored.
|
|
52
|
+
*/
|
|
50
53
|
export function configFilePath() {
|
|
51
|
-
return (process.env.
|
|
54
|
+
return (process.env.OPEN_MEMEX_CONFIG ??
|
|
55
|
+
process.env.MY_O_MEMORY_CONFIG ??
|
|
52
56
|
path.join(os.homedir(), ".config", "opencode", "open-memex.jsonc"));
|
|
53
57
|
}
|
|
54
58
|
function toBool(v) {
|
|
@@ -111,6 +115,7 @@ export function saveConfig(patch) {
|
|
|
111
115
|
/** Path of the config file in effect, or null when using built-in defaults. */
|
|
112
116
|
export function configSource() {
|
|
113
117
|
const candidates = [
|
|
118
|
+
process.env.OPEN_MEMEX_CONFIG,
|
|
114
119
|
process.env.MY_O_MEMORY_CONFIG,
|
|
115
120
|
path.join(os.homedir(), ".config", "opencode", "open-memex.jsonc"),
|
|
116
121
|
path.join(os.homedir(), ".config", "opencode", "open-memex.json"),
|
package/dist/doctor.js
CHANGED
|
@@ -8,7 +8,7 @@ import path from "node:path";
|
|
|
8
8
|
import { spawn, execFileSync } from "node:child_process";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { loadConfig, configSource, DEFAULT_CONFIG } from "./config.js";
|
|
11
|
-
import { paths } from "./paths.js";
|
|
11
|
+
import { paths, homeOverride } from "./paths.js";
|
|
12
12
|
import { resolveCwdScope } from "./scope.js";
|
|
13
13
|
import { db, backendName } from "./store/db.js";
|
|
14
14
|
import { LEGACY_PACKAGE_NAME, opencodeGlobalConfigPath, userMcpConfigPath } from "./init.js";
|
|
@@ -54,7 +54,13 @@ function storageCheck() {
|
|
|
54
54
|
try {
|
|
55
55
|
const p = paths();
|
|
56
56
|
fs.accessSync(p.memories, fs.constants.W_OK);
|
|
57
|
-
|
|
57
|
+
// D62: when the root came from the legacy env var, say so (still ok —
|
|
58
|
+
// the fallback is supported, doctor just names the current one).
|
|
59
|
+
const via = homeOverride()?.via;
|
|
60
|
+
const note = via === "MY_O_MEMORY_HOME"
|
|
61
|
+
? " (via legacy MY_O_MEMORY_HOME — OPEN_MEMEX_HOME is the current name)"
|
|
62
|
+
: "";
|
|
63
|
+
return { name: "storage", ok: true, detail: `${p.root} (writable)${note}` };
|
|
58
64
|
}
|
|
59
65
|
catch (err) {
|
|
60
66
|
return { name: "storage", ok: false, detail: String(err) };
|
|
@@ -559,9 +565,11 @@ export async function runDoctor() {
|
|
|
559
565
|
function legacyCheck() {
|
|
560
566
|
const name = "legacy my-o-memory";
|
|
561
567
|
const found = [];
|
|
562
|
-
|
|
568
|
+
// D62: the override may arrive via either env var name; check the value
|
|
569
|
+
// in effect, not just the legacy one.
|
|
570
|
+
const envHome = homeOverride()?.dir;
|
|
563
571
|
if (envHome && envHome.includes(LEGACY_PACKAGE_NAME))
|
|
564
|
-
found.push(`
|
|
572
|
+
found.push(`memory home points at a pre-rename dir (${envHome}) — unset it, then merge old data with \`open-memex migrate --to-v2\``);
|
|
565
573
|
const root = paths().root;
|
|
566
574
|
const legacyDir = path.join(path.dirname(root), LEGACY_PACKAGE_NAME);
|
|
567
575
|
if (fs.existsSync(legacyDir) && fs.statSync(legacyDir).isDirectory())
|
package/dist/paths.js
CHANGED
|
@@ -2,9 +2,23 @@ import path from "node:path";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* D62: the data-root override env var. `OPEN_MEMEX_HOME` is the current
|
|
7
|
+
* name; `MY_O_MEMORY_HOME` (pre-rename package name) is still honored as a
|
|
8
|
+
* fallback so existing setups and scripts keep working. New name wins
|
|
9
|
+
* when both are set.
|
|
10
|
+
*/
|
|
11
|
+
export function homeOverride() {
|
|
12
|
+
if (process.env.OPEN_MEMEX_HOME)
|
|
13
|
+
return { dir: path.resolve(process.env.OPEN_MEMEX_HOME), via: "OPEN_MEMEX_HOME" };
|
|
6
14
|
if (process.env.MY_O_MEMORY_HOME)
|
|
7
|
-
return path.resolve(process.env.MY_O_MEMORY_HOME);
|
|
15
|
+
return { dir: path.resolve(process.env.MY_O_MEMORY_HOME), via: "MY_O_MEMORY_HOME" };
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
function dataRoot() {
|
|
19
|
+
const override = homeOverride();
|
|
20
|
+
if (override)
|
|
21
|
+
return override.dir;
|
|
8
22
|
if (process.platform === "win32") {
|
|
9
23
|
const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
10
24
|
return path.join(appData, "open-memex");
|
package/docs/V2-DESIGN.md
CHANGED
|
@@ -1304,13 +1304,24 @@ requirement: personal data never touches third-party services). Benchmarks to tr
|
|
|
1304
1304
|
distillation over host transcripts stays its own group; it will
|
|
1305
1305
|
reuse this rubric.
|
|
1306
1306
|
|
|
1307
|
+
- **D62 — environment variable names grow current-name aliases.** The
|
|
1308
|
+
data-root and config-path overrides predate the rename and kept the
|
|
1309
|
+
old package's name (`MY_O_MEMORY_HOME`, `MY_O_MEMORY_CONFIG`) — a
|
|
1310
|
+
stale-looking name is a trap for users and agents scripting against
|
|
1311
|
+
the current one. `OPEN_MEMEX_HOME` / `OPEN_MEMEX_CONFIG` are now the
|
|
1312
|
+
preferred names; the legacy names still work as fallbacks (new name
|
|
1313
|
+
wins when both are set), so existing setups and scripts never break.
|
|
1314
|
+
The data root itself does not move — only the variable names change.
|
|
1315
|
+
`doctor`'s storage check notes when the root arrived via the legacy
|
|
1316
|
+
variable, and its pre-rename-dir leftovers check reads whichever
|
|
1317
|
+
variable is in effect.
|
|
1318
|
+
|
|
1307
1319
|
## Open Questions
|
|
1308
1320
|
|
|
1309
1321
|
_All resolved — see D10 (rename), D11 (type/role split), D12 (explicit pull)._
|
|
1322
|
+
- **Checkpoint proactivity for capture (open, 2026-10-02):** D53 retired checkpoint *polling* — the server pushes draft counts instead. But agent proactivity at task checkpoints genuinely helps knowledge capture (§3.5 distillation), so the open question is the degree: proactive enough to catch distillable moments, without nagging the user or burning tokens at every checkpoint. Candidate directions (server-pushed hints in tool results, client-side heuristics, or a middle setting) to be evaluated in a later version; default posture TBD. **Update (2026-10-03): decided in D61 — propose posture** (agent proposes 1–3 candidates at task checkpoints, saves only on approval; auto-draft deferred until memory visibility ships, see the MEMORY_VISIBILITY question below).
|
|
1310
1323
|
|
|
1311
|
-
- **
|
|
1312
|
-
|
|
1313
|
-
- **Retrieval robustness: phrasing, synonyms, extra words (open, 2026-10-03):** keyword search matches word forms, not meaning — the asker phrases it differently, adds a word, or uses a synonym and the memory is missed. The current FTS5 setup (porter stemming + pre-tokenized CJK bigrams) already covers morphology and substrings; it cannot bridge semantics. Layered candidate directions, cheapest first: (1) query construction — never feed a raw sentence to FTS; extract content terms, drop stopwords, OR them and let bm25 rank, so extra words hurt ranking instead of killing the match; (2) agent-side query expansion as an explicit protocol in the skill/instructions (decompose the question into concepts, generate 2–3 synonym variants per concept, run several searches before giving up — the LLM is the synonym dictionary, no embedding model needed); (3) capture-time aliasing — when a memory is written, the agent attaches keyword/synonym aliases so future phrasings can hit it. Embeddings are the design's reserved optional capability: real semantic matching, but they require a design decision and a local model to distribute, so they stay deferred unless (1)–(3) prove insufficient. Note (3) pairs naturally with the proactive-capture question below: the same capture pass that proposes a memory can attach its aliases. **Status (2026-10-03): layer (1) shipped (D56, 0.6.1)** — query construction now filters EN/CJK function words, dedupes, and caps terms. Layers (2) agent-side expansion and (3) capture-time aliasing remain open; embeddings stay deferred.
|
|
1324
|
+
- **Retrieval robustness: phrasing, synonyms, extra words (open, 2026-10-03):** keyword search matches word forms, not meaning — the asker phrases it differently, adds a word, or uses a synonym and the memory is missed. The current FTS5 setup (porter stemming + pre-tokenized CJK bigrams) already covers morphology and substrings; it cannot bridge semantics. Layered candidate directions, cheapest first: (1) query construction — never feed a raw sentence to FTS; extract content terms, drop stopwords, OR them and let bm25 rank, so extra words hurt ranking instead of killing the match; (2) agent-side query expansion as an explicit protocol in the skill/instructions (decompose the question into concepts, generate 2–3 synonym variants per concept, run several searches before giving up — the LLM is the synonym dictionary, no embedding model needed); (3) capture-time aliasing — when a memory is written, the agent attaches keyword/synonym aliases so future phrasings can hit it. Embeddings are the design's reserved optional capability: real semantic matching, but they require a design decision and a local model to distribute, so they stay deferred unless (1)–(3) prove insufficient. Note (3) pairs naturally with the proactive-capture question below: the same capture pass that proposes a memory can attach its aliases. **Status (2026-10-03): layer (1) shipped (D56, 0.6.1)** — query construction now filters EN/CJK function words, dedupes, and caps terms. Layers (2) agent-side expansion and (3) capture-time aliasing remain open; embeddings stay deferred. **Update (2026-10-03): layers (2) and (3) shipped in D61** — agent-side expansion as guidance text on every surface, capture-time aliases indexed in their own FTS column behind the `captureAliases` flag. Embeddings stay deferred.
|
|
1314
1325
|
|
|
1315
1326
|
- **Proactive capture: agent judgment of what is worth remembering (open, 2026-10-03):** companion to the checkpoint-proactivity question above — that one asks *when* to prompt; this one asks *what qualifies*. Agent hosts with good long-term memory (e.g. Muse) get their results from explicit memorability criteria plus checkpoint consolidation, not from silent background magic; open-memex can adopt both halves. Candidate criteria to teach via the skill/instructions: decisions **and their rationale**, preferences, conventions, gotchas, and abandoned paths ("we tried X, it failed because…") are worth proposing; one-off task details and anything re-derivable from the code are not. Two postures to evaluate: propose-at-checkpoint (agent asks "worth saving?", user vetoes, then `memory_add`) versus auto-draft (agent writes outbox drafts unasked and surfaces them in a batch at the next checkpoint — drafts are git-invisible and deletable, so the cost of a wrong guess is low). Hard boundaries either way: project scope never auto-submits, and no cross-session background consolidation is possible — the plugin lives inside sessions, so consolidation happens at in-session checkpoints and wrap-up passes only. This question also decides how much aliasing (retrieval question above) the capture pass should generate.
|
|
1316
1327
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-memex",
|
|
3
|
-
"version": "0.7.0-alpha.
|
|
3
|
+
"version": "0.7.0-alpha.3",
|
|
4
4
|
"description": "Local-first memory layer and protocol for AI coding agents. Markdown source of truth, SQLite FTS5 index, zero cloud.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
package/scripts/smoke-pure.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { contentHash, similarity, NEAR_DUP_THRESHOLD } from "../src/store/lifecy
|
|
|
12
12
|
import { userMcpConfigPath, mergeServerEntry, detectInstalledClients, mergePluginEntry, mergeV2PluginEntry, removeV2PluginEntry, opencodeGlobalConfigPath, printManualEntryHint, parseJsonConfig, removeServerEntry, removePluginEntry, removeInstructionsSection, LEGACY_PACKAGE_NAME, shouldInstallSkill } from "../src/init.ts";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { memoryAddArgs } from "../src/tools/ops.ts";
|
|
15
|
+
import { dataRootPath, homeOverride } from "../src/paths.ts";
|
|
16
|
+
import { configFilePath } from "../src/config.ts";
|
|
15
17
|
|
|
16
18
|
let fails = 0;
|
|
17
19
|
function ok(name: string, cond: boolean, info?: unknown) {
|
|
@@ -423,6 +425,38 @@ ok("failed backup leaves legacy dir for retry", fs.existsSync(legacyRoot2));
|
|
|
423
425
|
delete process.env.MY_O_MEMORY_HOME;
|
|
424
426
|
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
425
427
|
|
|
428
|
+
console.log("== env var names (D62) ==");
|
|
429
|
+
{
|
|
430
|
+
const saved = {
|
|
431
|
+
n: process.env.OPEN_MEMEX_HOME,
|
|
432
|
+
l: process.env.MY_O_MEMORY_HOME,
|
|
433
|
+
nc: process.env.OPEN_MEMEX_CONFIG,
|
|
434
|
+
lc: process.env.MY_O_MEMORY_CONFIG,
|
|
435
|
+
};
|
|
436
|
+
const restore = () => {
|
|
437
|
+
const set = (k: string, v: string | undefined) => (v === undefined ? delete process.env[k] : (process.env[k] = v));
|
|
438
|
+
set("OPEN_MEMEX_HOME", saved.n);
|
|
439
|
+
set("MY_O_MEMORY_HOME", saved.l);
|
|
440
|
+
set("OPEN_MEMEX_CONFIG", saved.nc);
|
|
441
|
+
set("MY_O_MEMORY_CONFIG", saved.lc);
|
|
442
|
+
};
|
|
443
|
+
try {
|
|
444
|
+
delete process.env.OPEN_MEMEX_HOME;
|
|
445
|
+
delete process.env.MY_O_MEMORY_HOME;
|
|
446
|
+
ok("no override -> homeOverride null", homeOverride() === null);
|
|
447
|
+
process.env.MY_O_MEMORY_HOME = "/tmp/legacy-home";
|
|
448
|
+
ok("legacy fallback honored", dataRootPath().endsWith("legacy-home") && homeOverride()?.via === "MY_O_MEMORY_HOME");
|
|
449
|
+
process.env.OPEN_MEMEX_HOME = "/tmp/new-home";
|
|
450
|
+
ok("new name wins over legacy", dataRootPath().endsWith("new-home") && homeOverride()?.via === "OPEN_MEMEX_HOME");
|
|
451
|
+
process.env.MY_O_MEMORY_CONFIG = "/tmp/legacy.jsonc";
|
|
452
|
+
ok("config legacy fallback", configFilePath().endsWith("legacy.jsonc"));
|
|
453
|
+
process.env.OPEN_MEMEX_CONFIG = "/tmp/new.jsonc";
|
|
454
|
+
ok("config new name wins", configFilePath().endsWith("new.jsonc"));
|
|
455
|
+
} finally {
|
|
456
|
+
restore();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
426
460
|
console.log("== lifecycle pure: contentHash / similarity ==");
|
|
427
461
|
ok("hash deterministic + whitespace-insensitive", contentHash("hello world\n") === contentHash("hello world"));
|
|
428
462
|
ok("hash differs on content", contentHash("hello world") !== contentHash("hello mars"));
|
package/src/config.ts
CHANGED
|
@@ -84,9 +84,13 @@ function stripJsonComments(raw: string): string {
|
|
|
84
84
|
return out;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* Where `config set` / interactive `init` persist. Respects OPEN_MEMEX_CONFIG
|
|
89
|
+
* (D62); MY_O_MEMORY_CONFIG is the pre-rename fallback, still honored.
|
|
90
|
+
*/
|
|
88
91
|
export function configFilePath(): string {
|
|
89
92
|
return (
|
|
93
|
+
process.env.OPEN_MEMEX_CONFIG ??
|
|
90
94
|
process.env.MY_O_MEMORY_CONFIG ??
|
|
91
95
|
path.join(os.homedir(), ".config", "opencode", "open-memex.jsonc")
|
|
92
96
|
);
|
|
@@ -156,6 +160,7 @@ export function saveConfig(patch: Record<string, unknown>): string {
|
|
|
156
160
|
/** Path of the config file in effect, or null when using built-in defaults. */
|
|
157
161
|
export function configSource(): string | null {
|
|
158
162
|
const candidates = [
|
|
163
|
+
process.env.OPEN_MEMEX_CONFIG,
|
|
159
164
|
process.env.MY_O_MEMORY_CONFIG,
|
|
160
165
|
path.join(os.homedir(), ".config", "opencode", "open-memex.jsonc"),
|
|
161
166
|
path.join(os.homedir(), ".config", "opencode", "open-memex.json"),
|
package/src/doctor.ts
CHANGED
|
@@ -8,7 +8,7 @@ import path from "node:path";
|
|
|
8
8
|
import { spawn, execFileSync } from "node:child_process";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { loadConfig, configSource, DEFAULT_CONFIG } from "./config.ts";
|
|
11
|
-
import { paths } from "./paths.ts";
|
|
11
|
+
import { paths, homeOverride } from "./paths.ts";
|
|
12
12
|
import { resolveCwdScope } from "./scope.ts";
|
|
13
13
|
import { db, backendName } from "./store/db.ts";
|
|
14
14
|
import { LEGACY_PACKAGE_NAME, opencodeGlobalConfigPath, userMcpConfigPath } from "./init.ts";
|
|
@@ -66,7 +66,14 @@ function storageCheck(): Check {
|
|
|
66
66
|
try {
|
|
67
67
|
const p = paths();
|
|
68
68
|
fs.accessSync(p.memories, fs.constants.W_OK);
|
|
69
|
-
|
|
69
|
+
// D62: when the root came from the legacy env var, say so (still ok —
|
|
70
|
+
// the fallback is supported, doctor just names the current one).
|
|
71
|
+
const via = homeOverride()?.via;
|
|
72
|
+
const note =
|
|
73
|
+
via === "MY_O_MEMORY_HOME"
|
|
74
|
+
? " (via legacy MY_O_MEMORY_HOME — OPEN_MEMEX_HOME is the current name)"
|
|
75
|
+
: "";
|
|
76
|
+
return { name: "storage", ok: true, detail: `${p.root} (writable)${note}` };
|
|
70
77
|
} catch (err) {
|
|
71
78
|
return { name: "storage", ok: false, detail: String(err) };
|
|
72
79
|
}
|
|
@@ -579,10 +586,12 @@ export async function runDoctor(): Promise<boolean> {
|
|
|
579
586
|
function legacyCheck(): Check {
|
|
580
587
|
const name = "legacy my-o-memory";
|
|
581
588
|
const found: string[] = [];
|
|
582
|
-
|
|
589
|
+
// D62: the override may arrive via either env var name; check the value
|
|
590
|
+
// in effect, not just the legacy one.
|
|
591
|
+
const envHome = homeOverride()?.dir;
|
|
583
592
|
if (envHome && envHome.includes(LEGACY_PACKAGE_NAME))
|
|
584
593
|
found.push(
|
|
585
|
-
`
|
|
594
|
+
`memory home points at a pre-rename dir (${envHome}) — unset it, then merge old data with \`open-memex migrate --to-v2\``,
|
|
586
595
|
);
|
|
587
596
|
const root = paths().root;
|
|
588
597
|
const legacyDir = path.join(path.dirname(root), LEGACY_PACKAGE_NAME);
|
package/src/paths.ts
CHANGED
|
@@ -3,8 +3,23 @@ import fs from "node:fs";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* D62: the data-root override env var. `OPEN_MEMEX_HOME` is the current
|
|
8
|
+
* name; `MY_O_MEMORY_HOME` (pre-rename package name) is still honored as a
|
|
9
|
+
* fallback so existing setups and scripts keep working. New name wins
|
|
10
|
+
* when both are set.
|
|
11
|
+
*/
|
|
12
|
+
export function homeOverride(): { dir: string; via: "OPEN_MEMEX_HOME" | "MY_O_MEMORY_HOME" } | null {
|
|
13
|
+
if (process.env.OPEN_MEMEX_HOME)
|
|
14
|
+
return { dir: path.resolve(process.env.OPEN_MEMEX_HOME), via: "OPEN_MEMEX_HOME" };
|
|
15
|
+
if (process.env.MY_O_MEMORY_HOME)
|
|
16
|
+
return { dir: path.resolve(process.env.MY_O_MEMORY_HOME), via: "MY_O_MEMORY_HOME" };
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
function dataRoot(): string {
|
|
7
|
-
|
|
21
|
+
const override = homeOverride();
|
|
22
|
+
if (override) return override.dir;
|
|
8
23
|
if (process.platform === "win32") {
|
|
9
24
|
const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
10
25
|
return path.join(appData, "open-memex");
|