open-memex 0.6.0-alpha.8 → 0.6.0
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 +6 -2
- package/README.md +2 -1
- package/README.zh-CN.md +2 -1
- package/dist/doctor.js +30 -0
- package/dist/init.js +78 -15
- package/docs/V2-DESIGN.md +32 -0
- package/package.json +1 -1
- package/scripts/smoke-pure.ts +16 -1
- package/src/doctor.ts +32 -0
- package/src/init.ts +78 -14
package/AGENTS.md
CHANGED
|
@@ -78,7 +78,9 @@ recall it) so a first-time user sees what "it works" looks like (D51).
|
|
|
78
78
|
init also installs the bundled `open-memex` Agent Skill (skills/open-memex/SKILL.md,
|
|
79
79
|
teaches skill-aware agents the CLI: save/search/scope rules/outbox flow) into each
|
|
80
80
|
wired editor's user-level skills dir — ~/.copilot/skills/ (VS Code),
|
|
81
|
-
~/.cursor/skills/ (Cursor), ~/.config/opencode/skills/ (opencode
|
|
81
|
+
~/.cursor/skills/ (Cursor), ~/.config/opencode/skills/ (opencode, per-project MCP
|
|
82
|
+
mode only — with the native plugin the skill is skipped and any previously installed
|
|
83
|
+
one is removed, D55); Visual Studio
|
|
82
84
|
has no skills concept and is skipped. Copy, not symlink (Windows needs no
|
|
83
85
|
Developer Mode); existing skill kept unless --force (D54).
|
|
84
86
|
`open-memex uninstall [--client vscode|cursor|opencode|visualstudio] [--global] [--yes]`
|
|
@@ -87,7 +89,9 @@ instructions section / Agent Skill directory; memory data never touched (D48); n
|
|
|
87
89
|
with an interactive confirm, explicit --client never prompts; `--yes` only skips
|
|
88
90
|
that confirm; `--global` limits cleanup to user-level. Empty/whitespace-only
|
|
89
91
|
config files parse as `{}` and are safely populated (D49); non-JSON (JSONC)
|
|
90
|
-
files are left untouched with a printed manual snippet (D47).
|
|
92
|
+
files are left untouched with a printed manual snippet (D47). init/uninstall also sweep
|
|
93
|
+
pre-rename `my-o-memory` plugin entries and MCP server keys wherever they touch a
|
|
94
|
+
config, and `open-memex doctor` reports any remaining pre-rename leftovers (F30).
|
|
91
95
|
The published `open-memex` bin points at `dist/cli.js` (compiled at publish time).
|
|
92
96
|
From a source checkout, `npm run cli` / `npm run mcp` still run `src/` directly
|
|
93
97
|
with type-stripping — no build step needed for development.
|
package/README.md
CHANGED
|
@@ -187,7 +187,8 @@ With no `--client`, `init` **detects your installed editors and wires them all**
|
|
|
187
187
|
— user-level where the editor supports it (VS Code / Cursor MCP config, opencode
|
|
188
188
|
native plugin), so one init covers every project. Visual Studio joins in when the
|
|
189
189
|
project has a solution file. It also installs an **Agent Skill** (`open-memex`)
|
|
190
|
-
into each editor's skills folder
|
|
190
|
+
into each editor's skills folder (not opencode when the native plugin is wired —
|
|
191
|
+
the plugin already provides memory tools), so skill-aware agents can use your memory via
|
|
191
192
|
the CLI with no MCP configuration. Prefer to pick a single editor? Pass `--client`:
|
|
192
193
|
|
|
193
194
|
> **Two different "globals" — don't mix them up.**
|
package/README.zh-CN.md
CHANGED
|
@@ -179,7 +179,8 @@ npx -y open-memex init --yes
|
|
|
179
179
|
不带 `--client` 时,`init` 会**自动检测本机装了哪些编辑器,一次全接上**——
|
|
180
180
|
支持用户级的编辑器走用户级(VS Code / Cursor 的 MCP 配置、opencode 原生插件),
|
|
181
181
|
一次 init,所有项目通用;项目里有 solution 文件时 Visual Studio 也会一起配。
|
|
182
|
-
同时会给每个编辑器装一个 **Agent Skill**(`open-memex
|
|
182
|
+
同时会给每个编辑器装一个 **Agent Skill**(`open-memex`,opencode 用原生插件时除外——
|
|
183
|
+
插件已经提供了记忆工具),懂 skill 的 agent
|
|
183
184
|
不用配 MCP 也能通过 CLI 用你的记忆。
|
|
184
185
|
想只配某一个编辑器?加 `--client`:
|
|
185
186
|
|
package/dist/doctor.js
CHANGED
|
@@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { loadConfig, configSource, DEFAULT_CONFIG } from "./config.js";
|
|
11
11
|
import { paths } from "./paths.js";
|
|
12
12
|
import { resolveCwdScope } from "./scope.js";
|
|
13
|
+
import { LEGACY_PACKAGE_NAME, opencodeGlobalConfigPath, userMcpConfigPath } from "./init.js";
|
|
13
14
|
const EXPECTED_TOOLS = [
|
|
14
15
|
"memory_add",
|
|
15
16
|
"memory_search",
|
|
@@ -278,6 +279,7 @@ export async function runDoctor() {
|
|
|
278
279
|
console.log("open-memex doctor");
|
|
279
280
|
const checks = [nodeCheck(), configCheck(), scopeCheck(), storageCheck(), vscodeMcpCheck()];
|
|
280
281
|
checks.push(await mcpCheck());
|
|
282
|
+
checks.push(legacyCheck());
|
|
281
283
|
let allOk = true;
|
|
282
284
|
for (const c of checks) {
|
|
283
285
|
console.log(` ${c.ok ? "ok " : "FAIL"} ${c.name}: ${c.detail}`);
|
|
@@ -287,3 +289,31 @@ export async function runDoctor() {
|
|
|
287
289
|
console.log(allOk ? "All checks passed." : "Some checks failed — see above.");
|
|
288
290
|
return allOk;
|
|
289
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* F30: the my-o-memory → open-memex rename left configs and data behind that
|
|
294
|
+
* silently split memories across two data dirs (the exact trap: the old
|
|
295
|
+
* opencode plugin kept loading and writing to the OLD dir). Read-only.
|
|
296
|
+
*/
|
|
297
|
+
function legacyCheck() {
|
|
298
|
+
const name = "legacy my-o-memory";
|
|
299
|
+
const found = [];
|
|
300
|
+
const envHome = process.env.MY_O_MEMORY_HOME;
|
|
301
|
+
if (envHome && envHome.includes(LEGACY_PACKAGE_NAME))
|
|
302
|
+
found.push(`MY_O_MEMORY_HOME points at a pre-rename dir (${envHome}) — unset it, then merge old data with \`open-memex migrate --to-v2\``);
|
|
303
|
+
const root = paths().root;
|
|
304
|
+
const legacyDir = path.join(path.dirname(root), LEGACY_PACKAGE_NAME);
|
|
305
|
+
if (fs.existsSync(legacyDir) && fs.statSync(legacyDir).isDirectory())
|
|
306
|
+
found.push(`legacy data dir ${legacyDir} — merge it with \`open-memex migrate --to-v2\``);
|
|
307
|
+
for (const f of [opencodeGlobalConfigPath(), userMcpConfigPath("vscode"), userMcpConfigPath("cursor")]) {
|
|
308
|
+
try {
|
|
309
|
+
if (fs.existsSync(f) && fs.readFileSync(f, "utf8").includes(LEGACY_PACKAGE_NAME))
|
|
310
|
+
found.push(`stale "${LEGACY_PACKAGE_NAME}" reference in ${f} — re-run \`open-memex init --force --yes\``);
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
/* unreadable — not this check's problem */
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return found.length === 0
|
|
317
|
+
? { name, ok: true, detail: "no pre-rename leftovers found" }
|
|
318
|
+
: { name, ok: false, detail: found.join("; ") };
|
|
319
|
+
}
|
package/dist/init.js
CHANGED
|
@@ -11,6 +11,14 @@ import { markFirstRunDone, clearFirstRunMarker } from "./first-run.js";
|
|
|
11
11
|
const MARKER = "<!-- open-memex -->";
|
|
12
12
|
/** npm dist-tag carrying the 0.3.x preview line. */
|
|
13
13
|
const ALPHA_TAG = "open-memex@alpha";
|
|
14
|
+
/**
|
|
15
|
+
* F30: pre-rename package name. The my-o-memory → open-memex rename left
|
|
16
|
+
* editor configs behind that still load the OLD plugin/server — which writes
|
|
17
|
+
* to the OLD data dir, silently splitting the user's memories in two.
|
|
18
|
+
* init, uninstall and doctor treat these leftovers as stale and remove (or
|
|
19
|
+
* report) them wherever they touch a config.
|
|
20
|
+
*/
|
|
21
|
+
export const LEGACY_PACKAGE_NAME = "my-o-memory";
|
|
14
22
|
/**
|
|
15
23
|
* Resolve the MCP server command to write into client configs.
|
|
16
24
|
*
|
|
@@ -228,13 +236,18 @@ function writeServerEntryFile(file, sectionKey, entry, force, mkdir) {
|
|
|
228
236
|
doc = parsed;
|
|
229
237
|
}
|
|
230
238
|
const merged = mergeServerEntry(doc, sectionKey, entry, force);
|
|
231
|
-
|
|
239
|
+
// F30: drop a stale my-o-memory server entry — the rename left configs
|
|
240
|
+
// loading the OLD server, which writes to the OLD data dir.
|
|
241
|
+
const staleRemoved = removeServerEntry(doc, sectionKey, LEGACY_PACKAGE_NAME) === "removed";
|
|
242
|
+
if (merged === "kept" && !staleRemoved) {
|
|
232
243
|
console.log(` = ${file} already configures open-memex — left as is (use --force to overwrite)`);
|
|
233
244
|
return file;
|
|
234
245
|
}
|
|
235
246
|
if (mkdir)
|
|
236
247
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
237
248
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
249
|
+
if (staleRemoved)
|
|
250
|
+
console.log(` - ${file} (stale my-o-memory server entry removed — package renamed to open-memex)`);
|
|
238
251
|
console.log(` + ${file}`);
|
|
239
252
|
return file;
|
|
240
253
|
}
|
|
@@ -304,16 +317,25 @@ function writeOpencodeGlobalPlugin(force) {
|
|
|
304
317
|
catch {
|
|
305
318
|
console.log(` ! ${file} has comments or invalid JSON — left untouched, fix it manually.`);
|
|
306
319
|
console.log(` To enable open-memex everywhere, add "plugin": ["${url}"] to it.`);
|
|
320
|
+
console.log(` Also remove any "${LEGACY_PACKAGE_NAME}" entries from "plugin" (stale — package renamed).`);
|
|
307
321
|
return null;
|
|
308
322
|
}
|
|
309
323
|
}
|
|
310
|
-
|
|
324
|
+
// F30: the my-o-memory → open-memex rename left the OLD plugin loading
|
|
325
|
+
// alongside the new one — it writes to the OLD data dir, silently splitting
|
|
326
|
+
// memories in two. Drop it wherever init touches the plugin list.
|
|
327
|
+
const staleRemoved = removePluginEntry(doc, LEGACY_PACKAGE_NAME) === "removed";
|
|
328
|
+
const merged = mergePluginEntry(doc, url, force);
|
|
329
|
+
if (merged === "kept" && !staleRemoved) {
|
|
311
330
|
console.log(` = ${file} already loads the open-memex plugin — left as is (use --force to overwrite)`);
|
|
312
331
|
return file;
|
|
313
332
|
}
|
|
314
333
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
315
334
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
316
|
-
|
|
335
|
+
if (staleRemoved)
|
|
336
|
+
console.log(` - ${file} (stale my-o-memory plugin entry removed — package renamed to open-memex)`);
|
|
337
|
+
if (merged === "added")
|
|
338
|
+
console.log(` + ${file} (native plugin — works in every project, no per-project init needed)`);
|
|
317
339
|
return file;
|
|
318
340
|
}
|
|
319
341
|
/** opencode MCP config: project-level opencode.jsonc, `type: "local"` + command array (v1 format). */
|
|
@@ -335,7 +357,9 @@ function writeOpencodeMcpJson(root, force) {
|
|
|
335
357
|
doc = parsed;
|
|
336
358
|
}
|
|
337
359
|
const section = (doc["mcp"] ??= {});
|
|
338
|
-
|
|
360
|
+
// F30: drop the pre-rename server entry — it points at the OLD data dir.
|
|
361
|
+
const staleRemoved = removeServerEntry(doc, "mcp", LEGACY_PACKAGE_NAME) === "removed";
|
|
362
|
+
if (section["open-memex"] && !force && !staleRemoved) {
|
|
339
363
|
console.log(` = ${file} already configures open-memex — left as is (use --force to overwrite)`);
|
|
340
364
|
return file;
|
|
341
365
|
}
|
|
@@ -347,6 +371,8 @@ function writeOpencodeMcpJson(root, force) {
|
|
|
347
371
|
enabled: true,
|
|
348
372
|
};
|
|
349
373
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
374
|
+
if (staleRemoved)
|
|
375
|
+
console.log(` - ${file} (stale my-o-memory MCP entry removed — package renamed to open-memex)`);
|
|
350
376
|
console.log(` + ${file}`);
|
|
351
377
|
if (!mc.durable) {
|
|
352
378
|
console.log(` ! no durable \`open-memex\` on PATH (one-shot npx?) — wrote an npx-based command.`);
|
|
@@ -374,7 +400,9 @@ function writeVisualStudioMcpJson(root, force) {
|
|
|
374
400
|
}
|
|
375
401
|
}
|
|
376
402
|
const section = (doc["servers"] ??= {});
|
|
377
|
-
|
|
403
|
+
// F30: drop the pre-rename server entry — it points at the OLD data dir.
|
|
404
|
+
const staleRemoved = removeServerEntry(doc, "servers", LEGACY_PACKAGE_NAME) === "removed";
|
|
405
|
+
if (section["open-memex"] && !force && !staleRemoved) {
|
|
378
406
|
console.log(` = ${file} already configures open-memex — left as is (use --force to overwrite)`);
|
|
379
407
|
return file;
|
|
380
408
|
}
|
|
@@ -386,6 +414,8 @@ function writeVisualStudioMcpJson(root, force) {
|
|
|
386
414
|
args: mc.args,
|
|
387
415
|
};
|
|
388
416
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
417
|
+
if (staleRemoved)
|
|
418
|
+
console.log(` - ${file} (stale my-o-memory MCP entry removed — package renamed to open-memex)`);
|
|
389
419
|
console.log(` + ${file}`);
|
|
390
420
|
if (!mc.durable) {
|
|
391
421
|
console.log(` ! no durable \`open-memex\` on PATH (one-shot npx?) — wrote an npx-based command.`);
|
|
@@ -502,6 +532,9 @@ async function promptConfigLevel() {
|
|
|
502
532
|
// teaches skill-aware agents to use open-memex via the CLI. init copies it
|
|
503
533
|
// (not symlinks — Windows needs no Developer Mode) into each wired editor's
|
|
504
534
|
// user-level skills dir; uninstall removes only our directory.
|
|
535
|
+
// D55 exception: opencode with the native plugin (global mode) never gets the
|
|
536
|
+
// skill — the plugin already provides memory tools, and the duplicated CLI
|
|
537
|
+
// guidance only makes the agent chatty. See shouldInstallSkill.
|
|
505
538
|
// ---------------------------------------------------------------------------
|
|
506
539
|
/** Package root, two levels above this module (src/init.ts or dist/init.js). */
|
|
507
540
|
export function packageRoot() {
|
|
@@ -553,6 +586,19 @@ export function removeSkill(client, env = {}) {
|
|
|
553
586
|
fs.rmSync(target, { recursive: true, force: true });
|
|
554
587
|
return "removed";
|
|
555
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* D55: whether init installs the Agent Skill for a client. opencode with the
|
|
591
|
+
* native plugin (global mode) already gives the agent memory tools plus
|
|
592
|
+
* keyword capture and context injection — the skill's CLI guidance is pure
|
|
593
|
+
* duplication there and makes the agent chatty (two overlapping instruction
|
|
594
|
+
* sets + a "MCP or CLI?" choice on every memory action). Everywhere else the
|
|
595
|
+
* skill stays as the no-tools fallback.
|
|
596
|
+
*/
|
|
597
|
+
export function shouldInstallSkill(client, global) {
|
|
598
|
+
if (client === "opencode" && global)
|
|
599
|
+
return false;
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
556
602
|
function exists(p) {
|
|
557
603
|
try {
|
|
558
604
|
return fs.existsSync(p);
|
|
@@ -695,6 +741,12 @@ export async function initProject(opts) {
|
|
|
695
741
|
const root = projectRoot();
|
|
696
742
|
console.log(`open-memex init — project root: ${root}`);
|
|
697
743
|
for (const client of clients) {
|
|
744
|
+
// D55: opencode's native plugin supersedes the skill — don't install it
|
|
745
|
+
// there, and remove one a previous init left behind.
|
|
746
|
+
const installSkill = shouldInstallSkill(client, global);
|
|
747
|
+
if (client === "opencode" && !installSkill && removeSkill(client) === "removed") {
|
|
748
|
+
console.log(` - Agent Skill removed for opencode (superseded by the native plugin)`);
|
|
749
|
+
}
|
|
698
750
|
if (client === "vscode" || client === "cursor") {
|
|
699
751
|
if (global)
|
|
700
752
|
writeGlobalMcpJson(client, opts.force);
|
|
@@ -717,6 +769,8 @@ export async function initProject(opts) {
|
|
|
717
769
|
writeMcpJson(root, client, opts.force);
|
|
718
770
|
}
|
|
719
771
|
}
|
|
772
|
+
if (!installSkill)
|
|
773
|
+
continue;
|
|
720
774
|
// D54: Agent Skills — user-level by design (init once), alongside the MCP wiring.
|
|
721
775
|
const skill = writeSkill(client, { force: opts.force });
|
|
722
776
|
if (skill === "installed")
|
|
@@ -747,14 +801,16 @@ export async function initProject(opts) {
|
|
|
747
801
|
// instructions section. Memory data is never touched.
|
|
748
802
|
// ---------------------------------------------------------------------------
|
|
749
803
|
/**
|
|
750
|
-
* Pure removal of
|
|
804
|
+
* Pure removal of a server entry from a parsed config doc.
|
|
751
805
|
* Prunes the section when it becomes empty. Mutates `doc`.
|
|
806
|
+
* `key` defaults to "open-memex"; F30 passes LEGACY_PACKAGE_NAME to sweep
|
|
807
|
+
* the pre-rename leftovers.
|
|
752
808
|
*/
|
|
753
|
-
export function removeServerEntry(doc, sectionKey) {
|
|
809
|
+
export function removeServerEntry(doc, sectionKey, key = "open-memex") {
|
|
754
810
|
const section = doc[sectionKey];
|
|
755
|
-
if (!section || typeof section !== "object" || !(
|
|
811
|
+
if (!section || typeof section !== "object" || !(key in section))
|
|
756
812
|
return "absent";
|
|
757
|
-
delete section[
|
|
813
|
+
delete section[key];
|
|
758
814
|
if (Object.keys(section).length === 0)
|
|
759
815
|
delete doc[sectionKey];
|
|
760
816
|
return "removed";
|
|
@@ -806,11 +862,15 @@ function removeServerEntryFile(file, sectionKey) {
|
|
|
806
862
|
return null;
|
|
807
863
|
}
|
|
808
864
|
const res = removeServerEntry(doc, sectionKey);
|
|
809
|
-
|
|
865
|
+
// F30: also sweep the pre-rename leftovers — uninstalling open-memex while
|
|
866
|
+
// the OLD my-o-memory entry still loads leaves a zombie server on the old data dir.
|
|
867
|
+
const stale = removeServerEntry(doc, sectionKey, LEGACY_PACKAGE_NAME);
|
|
868
|
+
if (res === "removed" || stale === "removed") {
|
|
810
869
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
811
|
-
console.log(` - ${file} (
|
|
870
|
+
console.log(` - ${file} (memory-plugin server entries removed)`);
|
|
871
|
+
return "removed";
|
|
812
872
|
}
|
|
813
|
-
return
|
|
873
|
+
return "absent";
|
|
814
874
|
}
|
|
815
875
|
/** Remove the open-memex plugin URL from one opencode user-level config file. */
|
|
816
876
|
function removeOpencodePluginFile(file) {
|
|
@@ -823,11 +883,14 @@ function removeOpencodePluginFile(file) {
|
|
|
823
883
|
return null;
|
|
824
884
|
}
|
|
825
885
|
const res = removePluginEntry(doc, "open-memex");
|
|
826
|
-
|
|
886
|
+
// F30: also sweep the pre-rename leftover — see removeServerEntryFile.
|
|
887
|
+
const stale = removePluginEntry(doc, LEGACY_PACKAGE_NAME);
|
|
888
|
+
if (res === "removed" || stale === "removed") {
|
|
827
889
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
828
|
-
console.log(` - ${file} (
|
|
890
|
+
console.log(` - ${file} (memory-plugin entries removed)`);
|
|
891
|
+
return "removed";
|
|
829
892
|
}
|
|
830
|
-
return
|
|
893
|
+
return "absent";
|
|
831
894
|
}
|
|
832
895
|
/** Remove the open-memex section from one copilot-instructions.md file. */
|
|
833
896
|
function removeInstructionsFile(file) {
|
package/docs/V2-DESIGN.md
CHANGED
|
@@ -1103,6 +1103,38 @@ requirement: personal data never touches third-party services). Benchmarks to tr
|
|
|
1103
1103
|
configuration; the skill is the CLI-shaped complement to the MCP server.
|
|
1104
1104
|
Requested by Stone 2026-10-01 after the Agent Skills ecosystem suggestion.*
|
|
1105
1105
|
|
|
1106
|
+
- **F30** — Sweep pre-rename `my-o-memory` leftovers (0.6.0-alpha.9). The
|
|
1107
|
+
my-o-memory → open-memex rename left editor configs behind that still load
|
|
1108
|
+
the OLD plugin/server, which writes to the OLD data dir — silently splitting
|
|
1109
|
+
the user's memories in two (found live on Stone's work machine 2026-10-01:
|
|
1110
|
+
the old opencode plugin sat above the new one in `opencode.json`, so
|
|
1111
|
+
opencode and the CLI wrote to different data roots). `init` now drops stale
|
|
1112
|
+
`my-o-memory` plugin entries and `my-o-memory` MCP server keys wherever it
|
|
1113
|
+
touches a config (opencode global plugin list, per-project `opencode.jsonc`,
|
|
1114
|
+
`.mcp.json`, VS Code/Cursor `mcp.json` incl. `--global`), even on the
|
|
1115
|
+
"kept" path; JSONC files it can't parse get a manual-removal hint.
|
|
1116
|
+
`uninstall` sweeps the same leftovers. `doctor` gains a read-only
|
|
1117
|
+
`legacy my-o-memory` check: fails when a legacy data dir exists next to the
|
|
1118
|
+
data root, when a user-level config still references `my-o-memory`, or when
|
|
1119
|
+
`MY_O_MEMORY_HOME` points at a pre-rename dir. *Lesson: a package rename
|
|
1120
|
+
must clean up its own old wiring — the old entry and the new entry are never
|
|
1121
|
+
both valid. Reported 2026-10-01.*
|
|
1122
|
+
|
|
1123
|
+
- **D55** — opencode's native plugin supersedes the Agent Skill
|
|
1124
|
+
(0.6.0-alpha.10). Real-world feedback (Stone 2026-10-01): with both the
|
|
1125
|
+
native plugin's `memory_*` tools and the D54 skill installed, opencode's
|
|
1126
|
+
agent turned chatty — two overlapping instruction sets plus a "use MCP or
|
|
1127
|
+
CLI?" choice on every memory action. No data corruption (identical saves
|
|
1128
|
+
dedupe), but the UX regressed from "one tool call and done" to narration.
|
|
1129
|
+
`init` no longer installs the skill for opencode when the native plugin is
|
|
1130
|
+
wired (`--global`), and removes a previously installed one; the per-project
|
|
1131
|
+
plain-MCP mode keeps the skill as the no-tools fallback. VS Code/Cursor
|
|
1132
|
+
keep the skill — their MCP wiring is more fragile (server-start failures,
|
|
1133
|
+
policy blocks), so the fallback still earns its keep, and the skill already
|
|
1134
|
+
tells agents to prefer MCP tools when available. *Lesson: don't ship two
|
|
1135
|
+
overlapping integrations for the same editor — the agent pays the
|
|
1136
|
+
duplication cost in chatter. Reported 2026-10-01.*
|
|
1137
|
+
|
|
1106
1138
|
## Open Questions
|
|
1107
1139
|
|
|
1108
1140
|
_All resolved — see D10 (rename), D11 (type/role split), D12 (explicit pull)._
|
package/package.json
CHANGED
package/scripts/smoke-pure.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { DEFAULT_CONFIG } from "../src/config.ts";
|
|
|
8
8
|
import { resolveProjectScope, resolveCwdScope, PERSONAL_SCOPE } from "../src/scope.ts";
|
|
9
9
|
import { cjkIndexText, cjkQueryExpr, hasCjk } from "../src/retrieve/cjk.ts";
|
|
10
10
|
import { contentHash, similarity, NEAR_DUP_THRESHOLD } from "../src/store/lifecycle.ts";
|
|
11
|
-
import { userMcpConfigPath, mergeServerEntry, detectInstalledClients, mergePluginEntry, opencodeGlobalConfigPath, printManualEntryHint, parseJsonConfig, removeServerEntry, removePluginEntry, removeInstructionsSection } from "../src/init.ts";
|
|
11
|
+
import { userMcpConfigPath, mergeServerEntry, detectInstalledClients, mergePluginEntry, opencodeGlobalConfigPath, printManualEntryHint, parseJsonConfig, removeServerEntry, removePluginEntry, removeInstructionsSection, LEGACY_PACKAGE_NAME, shouldInstallSkill } from "../src/init.ts";
|
|
12
12
|
|
|
13
13
|
let fails = 0;
|
|
14
14
|
function ok(name: string, cond: boolean, info?: unknown) {
|
|
@@ -451,6 +451,12 @@ const rdoc2: Record<string, unknown> = { servers: { "open-memex": { command: "x"
|
|
|
451
451
|
ok("remove prunes empty section", removeServerEntry(rdoc2, "servers") === "removed" && !("servers" in rdoc2));
|
|
452
452
|
ok("remove absent when no entry", removeServerEntry({ servers: {} }, "servers") === "absent");
|
|
453
453
|
ok("remove absent when no section", removeServerEntry({}, "servers") === "absent");
|
|
454
|
+
// F30: pre-rename leftovers are swept by key.
|
|
455
|
+
const ldoc: Record<string, unknown> = { servers: { "my-o-memory": { command: "x" }, "open-memex": { command: "y" } } };
|
|
456
|
+
ok("remove drops legacy my-o-memory key, keeps open-memex", removeServerEntry(ldoc, "servers", LEGACY_PACKAGE_NAME) === "removed"
|
|
457
|
+
&& !("my-o-memory" in (ldoc["servers"] as Record<string, unknown>))
|
|
458
|
+
&& "open-memex" in (ldoc["servers"] as Record<string, unknown>));
|
|
459
|
+
ok("legacy package name is my-o-memory", LEGACY_PACKAGE_NAME === "my-o-memory");
|
|
454
460
|
// removePluginEntry: removes any open-memex URL, prunes empty array.
|
|
455
461
|
const rpdoc: Record<string, unknown> = { plugin: ["file:///x/open-memex/src/index.ts", "other-plugin"], theme: "dark" };
|
|
456
462
|
ok("plugin remove by substring", removePluginEntry(rpdoc, "open-memex") === "removed"
|
|
@@ -458,6 +464,10 @@ ok("plugin remove by substring", removePluginEntry(rpdoc, "open-memex") === "rem
|
|
|
458
464
|
const rpdoc3: Record<string, unknown> = { plugin: ["file:///open-memex/y"] };
|
|
459
465
|
ok("plugin remove prunes empty array", removePluginEntry(rpdoc3, "open-memex") === "removed" && !("plugin" in rpdoc3));
|
|
460
466
|
ok("plugin remove absent", removePluginEntry({ plugin: ["other"] }, "open-memex") === "absent");
|
|
467
|
+
// F30: the rename left the OLD plugin loading next to the new one — init must drop it.
|
|
468
|
+
const lpdoc: Record<string, unknown> = { plugin: ["file:///g/my-o-memory/src/index.ts", "file:///g/open-memex/src/index.ts"] };
|
|
469
|
+
ok("plugin remove drops legacy my-o-memory entry", removePluginEntry(lpdoc, LEGACY_PACKAGE_NAME) === "removed"
|
|
470
|
+
&& JSON.stringify(lpdoc["plugin"]) === JSON.stringify(["file:///g/open-memex/src/index.ts"]));
|
|
461
471
|
// removeInstructionsSection: cuts MARKER..end, "" when nothing remains.
|
|
462
472
|
ok("instructions remove keeps prior content",
|
|
463
473
|
removeInstructionsSection("# mine\n\n<!-- open-memex -->\n# OpenMemex memory\n") === "# mine\n");
|
|
@@ -488,6 +498,11 @@ fs.writeFileSync(path.join(winBin, "code.cmd"), "@echo off\n");
|
|
|
488
498
|
ok("win32 detects vscode via code.cmd",
|
|
489
499
|
detectInstalledClients({ pathEnv: winBin, home: emptyHome, platform: "win32", root: emptyHome, xdgConfigHome: path.join(emptyHome, ".config") }).includes("vscode"));
|
|
490
500
|
for (const d of [fakeHome, binDir, detHome, detRoot, emptyHome, winBin]) fs.rmSync(d, { recursive: true, force: true });
|
|
501
|
+
// D55: opencode's native plugin supersedes the skill — init must not install it there.
|
|
502
|
+
ok("opencode+global skips skill", shouldInstallSkill("opencode", true) === false);
|
|
503
|
+
ok("opencode per-project keeps skill fallback", shouldInstallSkill("opencode", false) === true);
|
|
504
|
+
ok("vscode keeps skill", shouldInstallSkill("vscode", true) === true);
|
|
505
|
+
ok("cursor keeps skill", shouldInstallSkill("cursor", false) === true);
|
|
491
506
|
|
|
492
507
|
console.log(fails === 0 ? "\nALL PASS" : `\n${fails} FAILURES`);
|
|
493
508
|
process.exit(fails === 0 ? 0 : 1);
|
package/src/doctor.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { loadConfig, configSource, DEFAULT_CONFIG } from "./config.ts";
|
|
11
11
|
import { paths } from "./paths.ts";
|
|
12
12
|
import { resolveCwdScope } from "./scope.ts";
|
|
13
|
+
import { LEGACY_PACKAGE_NAME, opencodeGlobalConfigPath, userMcpConfigPath } from "./init.ts";
|
|
13
14
|
|
|
14
15
|
interface Check {
|
|
15
16
|
name: string;
|
|
@@ -299,6 +300,7 @@ export async function runDoctor(): Promise<boolean> {
|
|
|
299
300
|
console.log("open-memex doctor");
|
|
300
301
|
const checks: Check[] = [nodeCheck(), configCheck(), scopeCheck(), storageCheck(), vscodeMcpCheck()];
|
|
301
302
|
checks.push(await mcpCheck());
|
|
303
|
+
checks.push(legacyCheck());
|
|
302
304
|
let allOk = true;
|
|
303
305
|
for (const c of checks) {
|
|
304
306
|
console.log(` ${c.ok ? "ok " : "FAIL"} ${c.name}: ${c.detail}`);
|
|
@@ -307,3 +309,33 @@ export async function runDoctor(): Promise<boolean> {
|
|
|
307
309
|
console.log(allOk ? "All checks passed." : "Some checks failed — see above.");
|
|
308
310
|
return allOk;
|
|
309
311
|
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* F30: the my-o-memory → open-memex rename left configs and data behind that
|
|
315
|
+
* silently split memories across two data dirs (the exact trap: the old
|
|
316
|
+
* opencode plugin kept loading and writing to the OLD dir). Read-only.
|
|
317
|
+
*/
|
|
318
|
+
function legacyCheck(): Check {
|
|
319
|
+
const name = "legacy my-o-memory";
|
|
320
|
+
const found: string[] = [];
|
|
321
|
+
const envHome = process.env.MY_O_MEMORY_HOME;
|
|
322
|
+
if (envHome && envHome.includes(LEGACY_PACKAGE_NAME))
|
|
323
|
+
found.push(
|
|
324
|
+
`MY_O_MEMORY_HOME points at a pre-rename dir (${envHome}) — unset it, then merge old data with \`open-memex migrate --to-v2\``,
|
|
325
|
+
);
|
|
326
|
+
const root = paths().root;
|
|
327
|
+
const legacyDir = path.join(path.dirname(root), LEGACY_PACKAGE_NAME);
|
|
328
|
+
if (fs.existsSync(legacyDir) && fs.statSync(legacyDir).isDirectory())
|
|
329
|
+
found.push(`legacy data dir ${legacyDir} — merge it with \`open-memex migrate --to-v2\``);
|
|
330
|
+
for (const f of [opencodeGlobalConfigPath(), userMcpConfigPath("vscode"), userMcpConfigPath("cursor")]) {
|
|
331
|
+
try {
|
|
332
|
+
if (fs.existsSync(f) && fs.readFileSync(f, "utf8").includes(LEGACY_PACKAGE_NAME))
|
|
333
|
+
found.push(`stale "${LEGACY_PACKAGE_NAME}" reference in ${f} — re-run \`open-memex init --force --yes\``);
|
|
334
|
+
} catch {
|
|
335
|
+
/* unreadable — not this check's problem */
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return found.length === 0
|
|
339
|
+
? { name, ok: true, detail: "no pre-rename leftovers found" }
|
|
340
|
+
: { name, ok: false, detail: found.join("; ") };
|
|
341
|
+
}
|
package/src/init.ts
CHANGED
|
@@ -14,6 +14,15 @@ const MARKER = "<!-- open-memex -->";
|
|
|
14
14
|
/** npm dist-tag carrying the 0.3.x preview line. */
|
|
15
15
|
const ALPHA_TAG = "open-memex@alpha";
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* F30: pre-rename package name. The my-o-memory → open-memex rename left
|
|
19
|
+
* editor configs behind that still load the OLD plugin/server — which writes
|
|
20
|
+
* to the OLD data dir, silently splitting the user's memories in two.
|
|
21
|
+
* init, uninstall and doctor treat these leftovers as stale and remove (or
|
|
22
|
+
* report) them wherever they touch a config.
|
|
23
|
+
*/
|
|
24
|
+
export const LEGACY_PACKAGE_NAME = "my-o-memory";
|
|
25
|
+
|
|
17
26
|
export interface McpCommand {
|
|
18
27
|
command: string;
|
|
19
28
|
args: string[];
|
|
@@ -254,12 +263,17 @@ function writeServerEntryFile(
|
|
|
254
263
|
doc = parsed;
|
|
255
264
|
}
|
|
256
265
|
const merged = mergeServerEntry(doc, sectionKey, entry, force);
|
|
257
|
-
|
|
266
|
+
// F30: drop a stale my-o-memory server entry — the rename left configs
|
|
267
|
+
// loading the OLD server, which writes to the OLD data dir.
|
|
268
|
+
const staleRemoved = removeServerEntry(doc, sectionKey, LEGACY_PACKAGE_NAME) === "removed";
|
|
269
|
+
if (merged === "kept" && !staleRemoved) {
|
|
258
270
|
console.log(` = ${file} already configures open-memex — left as is (use --force to overwrite)`);
|
|
259
271
|
return file;
|
|
260
272
|
}
|
|
261
273
|
if (mkdir) fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
262
274
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
275
|
+
if (staleRemoved)
|
|
276
|
+
console.log(` - ${file} (stale my-o-memory server entry removed — package renamed to open-memex)`);
|
|
263
277
|
console.log(` + ${file}`);
|
|
264
278
|
return file;
|
|
265
279
|
}
|
|
@@ -336,16 +350,25 @@ function writeOpencodeGlobalPlugin(force: boolean): string | null {
|
|
|
336
350
|
} catch {
|
|
337
351
|
console.log(` ! ${file} has comments or invalid JSON — left untouched, fix it manually.`);
|
|
338
352
|
console.log(` To enable open-memex everywhere, add "plugin": ["${url}"] to it.`);
|
|
353
|
+
console.log(` Also remove any "${LEGACY_PACKAGE_NAME}" entries from "plugin" (stale — package renamed).`);
|
|
339
354
|
return null;
|
|
340
355
|
}
|
|
341
356
|
}
|
|
342
|
-
|
|
357
|
+
// F30: the my-o-memory → open-memex rename left the OLD plugin loading
|
|
358
|
+
// alongside the new one — it writes to the OLD data dir, silently splitting
|
|
359
|
+
// memories in two. Drop it wherever init touches the plugin list.
|
|
360
|
+
const staleRemoved = removePluginEntry(doc, LEGACY_PACKAGE_NAME) === "removed";
|
|
361
|
+
const merged = mergePluginEntry(doc, url, force);
|
|
362
|
+
if (merged === "kept" && !staleRemoved) {
|
|
343
363
|
console.log(` = ${file} already loads the open-memex plugin — left as is (use --force to overwrite)`);
|
|
344
364
|
return file;
|
|
345
365
|
}
|
|
346
366
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
347
367
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
348
|
-
|
|
368
|
+
if (staleRemoved)
|
|
369
|
+
console.log(` - ${file} (stale my-o-memory plugin entry removed — package renamed to open-memex)`);
|
|
370
|
+
if (merged === "added")
|
|
371
|
+
console.log(` + ${file} (native plugin — works in every project, no per-project init needed)`);
|
|
349
372
|
return file;
|
|
350
373
|
}
|
|
351
374
|
|
|
@@ -368,7 +391,9 @@ function writeOpencodeMcpJson(root: string, force: boolean): string | null {
|
|
|
368
391
|
doc = parsed;
|
|
369
392
|
}
|
|
370
393
|
const section = ((doc["mcp"] ??= {}) as Record<string, unknown>);
|
|
371
|
-
|
|
394
|
+
// F30: drop the pre-rename server entry — it points at the OLD data dir.
|
|
395
|
+
const staleRemoved = removeServerEntry(doc, "mcp", LEGACY_PACKAGE_NAME) === "removed";
|
|
396
|
+
if (section["open-memex"] && !force && !staleRemoved) {
|
|
372
397
|
console.log(` = ${file} already configures open-memex — left as is (use --force to overwrite)`);
|
|
373
398
|
return file;
|
|
374
399
|
}
|
|
@@ -380,6 +405,8 @@ function writeOpencodeMcpJson(root: string, force: boolean): string | null {
|
|
|
380
405
|
enabled: true,
|
|
381
406
|
};
|
|
382
407
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
408
|
+
if (staleRemoved)
|
|
409
|
+
console.log(` - ${file} (stale my-o-memory MCP entry removed — package renamed to open-memex)`);
|
|
383
410
|
console.log(` + ${file}`);
|
|
384
411
|
if (!mc.durable) {
|
|
385
412
|
console.log(` ! no durable \`open-memex\` on PATH (one-shot npx?) — wrote an npx-based command.`);
|
|
@@ -408,7 +435,9 @@ function writeVisualStudioMcpJson(root: string, force: boolean): string | null {
|
|
|
408
435
|
}
|
|
409
436
|
}
|
|
410
437
|
const section = ((doc["servers"] ??= {}) as Record<string, unknown>);
|
|
411
|
-
|
|
438
|
+
// F30: drop the pre-rename server entry — it points at the OLD data dir.
|
|
439
|
+
const staleRemoved = removeServerEntry(doc, "servers", LEGACY_PACKAGE_NAME) === "removed";
|
|
440
|
+
if (section["open-memex"] && !force && !staleRemoved) {
|
|
412
441
|
console.log(` = ${file} already configures open-memex — left as is (use --force to overwrite)`);
|
|
413
442
|
return file;
|
|
414
443
|
}
|
|
@@ -420,6 +449,8 @@ function writeVisualStudioMcpJson(root: string, force: boolean): string | null {
|
|
|
420
449
|
args: mc.args,
|
|
421
450
|
};
|
|
422
451
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
452
|
+
if (staleRemoved)
|
|
453
|
+
console.log(` - ${file} (stale my-o-memory MCP entry removed — package renamed to open-memex)`);
|
|
423
454
|
console.log(` + ${file}`);
|
|
424
455
|
if (!mc.durable) {
|
|
425
456
|
console.log(` ! no durable \`open-memex\` on PATH (one-shot npx?) — wrote an npx-based command.`);
|
|
@@ -558,6 +589,9 @@ export interface DetectEnv {
|
|
|
558
589
|
// teaches skill-aware agents to use open-memex via the CLI. init copies it
|
|
559
590
|
// (not symlinks — Windows needs no Developer Mode) into each wired editor's
|
|
560
591
|
// user-level skills dir; uninstall removes only our directory.
|
|
592
|
+
// D55 exception: opencode with the native plugin (global mode) never gets the
|
|
593
|
+
// skill — the plugin already provides memory tools, and the duplicated CLI
|
|
594
|
+
// guidance only makes the agent chatty. See shouldInstallSkill.
|
|
561
595
|
// ---------------------------------------------------------------------------
|
|
562
596
|
|
|
563
597
|
/** Package root, two levels above this module (src/init.ts or dist/init.js). */
|
|
@@ -624,6 +658,19 @@ export function removeSkill(
|
|
|
624
658
|
return "removed";
|
|
625
659
|
}
|
|
626
660
|
|
|
661
|
+
/**
|
|
662
|
+
* D55: whether init installs the Agent Skill for a client. opencode with the
|
|
663
|
+
* native plugin (global mode) already gives the agent memory tools plus
|
|
664
|
+
* keyword capture and context injection — the skill's CLI guidance is pure
|
|
665
|
+
* duplication there and makes the agent chatty (two overlapping instruction
|
|
666
|
+
* sets + a "MCP or CLI?" choice on every memory action). Everywhere else the
|
|
667
|
+
* skill stays as the no-tools fallback.
|
|
668
|
+
*/
|
|
669
|
+
export function shouldInstallSkill(client: InitClient, global: boolean): boolean {
|
|
670
|
+
if (client === "opencode" && global) return false;
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
|
|
627
674
|
function exists(p: string): boolean {
|
|
628
675
|
try {
|
|
629
676
|
return fs.existsSync(p);
|
|
@@ -782,6 +829,12 @@ export async function initProject(opts: {
|
|
|
782
829
|
const root = projectRoot();
|
|
783
830
|
console.log(`open-memex init — project root: ${root}`);
|
|
784
831
|
for (const client of clients) {
|
|
832
|
+
// D55: opencode's native plugin supersedes the skill — don't install it
|
|
833
|
+
// there, and remove one a previous init left behind.
|
|
834
|
+
const installSkill = shouldInstallSkill(client, global);
|
|
835
|
+
if (client === "opencode" && !installSkill && removeSkill(client) === "removed") {
|
|
836
|
+
console.log(` - Agent Skill removed for opencode (superseded by the native plugin)`);
|
|
837
|
+
}
|
|
785
838
|
if (client === "vscode" || client === "cursor") {
|
|
786
839
|
if (global) writeGlobalMcpJson(client, opts.force);
|
|
787
840
|
else writeMcpJson(root, client, opts.force);
|
|
@@ -797,6 +850,7 @@ export async function initProject(opts: {
|
|
|
797
850
|
writeMcpJson(root, client, opts.force);
|
|
798
851
|
}
|
|
799
852
|
}
|
|
853
|
+
if (!installSkill) continue;
|
|
800
854
|
// D54: Agent Skills — user-level by design (init once), alongside the MCP wiring.
|
|
801
855
|
const skill = writeSkill(client, { force: opts.force });
|
|
802
856
|
if (skill === "installed") console.log(` + Agent Skill installed (${skillTargetDir(client)})`);
|
|
@@ -827,16 +881,19 @@ export async function initProject(opts: {
|
|
|
827
881
|
// ---------------------------------------------------------------------------
|
|
828
882
|
|
|
829
883
|
/**
|
|
830
|
-
* Pure removal of
|
|
884
|
+
* Pure removal of a server entry from a parsed config doc.
|
|
831
885
|
* Prunes the section when it becomes empty. Mutates `doc`.
|
|
886
|
+
* `key` defaults to "open-memex"; F30 passes LEGACY_PACKAGE_NAME to sweep
|
|
887
|
+
* the pre-rename leftovers.
|
|
832
888
|
*/
|
|
833
889
|
export function removeServerEntry(
|
|
834
890
|
doc: Record<string, unknown>,
|
|
835
891
|
sectionKey: string,
|
|
892
|
+
key = "open-memex",
|
|
836
893
|
): "removed" | "absent" {
|
|
837
894
|
const section = doc[sectionKey];
|
|
838
|
-
if (!section || typeof section !== "object" || !(
|
|
839
|
-
delete (section as Record<string, unknown>)[
|
|
895
|
+
if (!section || typeof section !== "object" || !(key in section)) return "absent";
|
|
896
|
+
delete (section as Record<string, unknown>)[key];
|
|
840
897
|
if (Object.keys(section as Record<string, unknown>).length === 0) delete doc[sectionKey];
|
|
841
898
|
return "removed";
|
|
842
899
|
}
|
|
@@ -886,11 +943,15 @@ function removeServerEntryFile(file: string, sectionKey: string): "removed" | "a
|
|
|
886
943
|
return null;
|
|
887
944
|
}
|
|
888
945
|
const res = removeServerEntry(doc, sectionKey);
|
|
889
|
-
|
|
946
|
+
// F30: also sweep the pre-rename leftovers — uninstalling open-memex while
|
|
947
|
+
// the OLD my-o-memory entry still loads leaves a zombie server on the old data dir.
|
|
948
|
+
const stale = removeServerEntry(doc, sectionKey, LEGACY_PACKAGE_NAME);
|
|
949
|
+
if (res === "removed" || stale === "removed") {
|
|
890
950
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
891
|
-
console.log(` - ${file} (
|
|
951
|
+
console.log(` - ${file} (memory-plugin server entries removed)`);
|
|
952
|
+
return "removed";
|
|
892
953
|
}
|
|
893
|
-
return
|
|
954
|
+
return "absent";
|
|
894
955
|
}
|
|
895
956
|
|
|
896
957
|
/** Remove the open-memex plugin URL from one opencode user-level config file. */
|
|
@@ -903,11 +964,14 @@ function removeOpencodePluginFile(file: string): "removed" | "absent" | null {
|
|
|
903
964
|
return null;
|
|
904
965
|
}
|
|
905
966
|
const res = removePluginEntry(doc, "open-memex");
|
|
906
|
-
|
|
967
|
+
// F30: also sweep the pre-rename leftover — see removeServerEntryFile.
|
|
968
|
+
const stale = removePluginEntry(doc, LEGACY_PACKAGE_NAME);
|
|
969
|
+
if (res === "removed" || stale === "removed") {
|
|
907
970
|
fs.writeFileSync(file, JSON.stringify(doc, null, 2) + "\n");
|
|
908
|
-
console.log(` - ${file} (
|
|
971
|
+
console.log(` - ${file} (memory-plugin entries removed)`);
|
|
972
|
+
return "removed";
|
|
909
973
|
}
|
|
910
|
-
return
|
|
974
|
+
return "absent";
|
|
911
975
|
}
|
|
912
976
|
|
|
913
977
|
/** Remove the open-memex section from one copilot-instructions.md file. */
|