open-memex 0.6.0-alpha.5 → 0.6.0-alpha.8

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 CHANGED
@@ -75,9 +75,15 @@ TTY (usage as before when non-interactive); init/uninstall maintain a
75
75
  `.init.json` first-run marker at the data root so the offer is asked once (D50);
76
76
  init ends with a one-line next-step hint (`open-memex add` + ask the agent to
77
77
  recall it) so a first-time user sees what "it works" looks like (D51).
78
+ init also installs the bundled `open-memex` Agent Skill (skills/open-memex/SKILL.md,
79
+ teaches skill-aware agents the CLI: save/search/scope rules/outbox flow) into each
80
+ wired editor's user-level skills dir — ~/.copilot/skills/ (VS Code),
81
+ ~/.cursor/skills/ (Cursor), ~/.config/opencode/skills/ (opencode); Visual Studio
82
+ has no skills concept and is skipped. Copy, not symlink (Windows needs no
83
+ Developer Mode); existing skill kept unless --force (D54).
78
84
  `open-memex uninstall [--client vscode|cursor|opencode|visualstudio] [--global] [--yes]`
79
85
  reverses init — removes the MCP server entry / opencode plugin line / Copilot
80
- instructions section; memory data never touched (D48); no --client → auto-detect
86
+ instructions section / Agent Skill directory; memory data never touched (D48); no --client → auto-detect
81
87
  with an interactive confirm, explicit --client never prompts; `--yes` only skips
82
88
  that confirm; `--global` limits cleanup to user-level. Empty/whitespace-only
83
89
  config files parse as `{}` and are safely populated (D49); non-JSON (JSONC)
package/README.md CHANGED
@@ -113,6 +113,12 @@ This installs the `0.5.1` stable release.
113
113
  npm install -g open-memex@alpha
114
114
  ```
115
115
 
116
+ ```sh
117
+ open-memex init
118
+ ```
119
+
120
+ The install isn't complete until you run `open-memex init` — it wires up your editors (VS Code, Cursor, opencode, and Visual Studio for solution projects).
121
+
116
122
  See what's published:
117
123
 
118
124
  ```sh
@@ -180,7 +186,9 @@ npx -y open-memex init --yes
180
186
  With no `--client`, `init` **detects your installed editors and wires them all**
181
187
  — user-level where the editor supports it (VS Code / Cursor MCP config, opencode
182
188
  native plugin), so one init covers every project. Visual Studio joins in when the
183
- project has a solution file. Prefer to pick a single editor? Pass `--client`:
189
+ project has a solution file. It also installs an **Agent Skill** (`open-memex`)
190
+ into each editor's skills folder, so skill-aware agents can use your memory via
191
+ the CLI with no MCP configuration. Prefer to pick a single editor? Pass `--client`:
184
192
 
185
193
  > **Two different "globals" — don't mix them up.**
186
194
  > - `npm install -g open-memex` installs the *package* globally: it puts the
package/README.zh-CN.md CHANGED
@@ -108,6 +108,12 @@ npm install -g open-memex
108
108
  npm install -g open-memex@alpha
109
109
  ```
110
110
 
111
+ ```sh
112
+ open-memex init
113
+ ```
114
+
115
+ 不跑 `open-memex init` 把编辑器接上,安装就不算完成(支持 VS Code、Cursor、opencode,有 .sln 的项目还支持 Visual Studio)。
116
+
111
117
  查看已发布版本:
112
118
 
113
119
  ```sh
@@ -173,6 +179,8 @@ npx -y open-memex init --yes
173
179
  不带 `--client` 时,`init` 会**自动检测本机装了哪些编辑器,一次全接上**——
174
180
  支持用户级的编辑器走用户级(VS Code / Cursor 的 MCP 配置、opencode 原生插件),
175
181
  一次 init,所有项目通用;项目里有 solution 文件时 Visual Studio 也会一起配。
182
+ 同时会给每个编辑器装一个 **Agent Skill**(`open-memex`),懂 skill 的 agent
183
+ 不用配 MCP 也能通过 CLI 用你的记忆。
176
184
  想只配某一个编辑器?加 `--client`:
177
185
 
178
186
  > **两个"全局"不是一回事,别搞混。**
package/dist/cli.js CHANGED
@@ -516,30 +516,31 @@ async function main() {
516
516
  usage(0);
517
517
  return;
518
518
  }
519
- if (cmd === "--help" || cmd === "-h" || cmd === "help")
520
- usage(0);
521
- if (cmd === "--version" || cmd === "-v") {
522
- // package.json sits two levels above this file in both layouts
523
- // (src/cli.ts and dist/cli.js).
524
- const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
525
- const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
526
- console.log(`open-memex ${pkg.version}`);
527
- return;
528
- }
529
519
  // F28: npm runs lifecycle scripts in the background and swallows their
530
520
  // stdout (unless --foreground-scripts), so the D50 postinstall pointer
531
521
  // never reaches the user. Every CLI entry point therefore carries a
532
522
  // one-line nudge on stderr until init has run or been declined — stderr
533
523
  // keeps the MCP stdio protocol (stdout) intact, and the .init.json marker
534
524
  // makes it once-ever. `init`/`uninstall` are excluded (already there /
535
- // nothing to wire).
525
+ // nothing to wire). Placed before the --help/--version early returns:
526
+ // `-v` is the first thing people run after installing.
536
527
  if (cmd !== "init" && cmd !== "uninstall") {
537
528
  const { isFirstRun } = await import("./first-run.js");
538
529
  if (isFirstRun()) {
539
530
  console.error("open-memex: first run? `open-memex init` wires it into your editors " +
540
- "(auto-detects VS Code, Cursor, opencode).");
531
+ "(auto-detects VS Code, Cursor, opencode — and Visual Studio for solution projects).");
541
532
  }
542
533
  }
534
+ if (cmd === "--help" || cmd === "-h" || cmd === "help")
535
+ usage(0);
536
+ if (cmd === "--version" || cmd === "-v") {
537
+ // package.json sits two levels above this file in both layouts
538
+ // (src/cli.ts and dist/cli.js).
539
+ const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
540
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
541
+ console.log(`open-memex ${pkg.version}`);
542
+ return;
543
+ }
543
544
  // Per-command help: `open-memex <command> --help`. Checked before loadConfig()
544
545
  // so it works even when the environment is broken.
545
546
  if (rest.includes("--help") || rest.includes("-h")) {
package/dist/first-run.js CHANGED
@@ -64,7 +64,7 @@ export async function offerFirstRunInit(runInit) {
64
64
  if (!shouldOfferFirstRun())
65
65
  return "skipped";
66
66
  console.log(`It looks like open-memex hasn't been set up on this machine yet.\n` +
67
- "`open-memex init` wires it into your editors (auto-detects VS Code, Cursor and opencode).\n");
67
+ "`open-memex init` wires it into your editors (auto-detects VS Code, Cursor and opencode — and Visual Studio for solution projects).\n");
68
68
  if (await askYesNo("Run it now? [Y/n] ")) {
69
69
  await runInit();
70
70
  return "initialized";
package/dist/init.js CHANGED
@@ -497,6 +497,62 @@ async function promptConfigLevel() {
497
497
  rl.close();
498
498
  }
499
499
  }
500
+ // ---------------------------------------------------------------------------
501
+ // D54: Agent Skills. The bundled open-memex skill (skills/open-memex/SKILL.md)
502
+ // teaches skill-aware agents to use open-memex via the CLI. init copies it
503
+ // (not symlinks — Windows needs no Developer Mode) into each wired editor's
504
+ // user-level skills dir; uninstall removes only our directory.
505
+ // ---------------------------------------------------------------------------
506
+ /** Package root, two levels above this module (src/init.ts or dist/init.js). */
507
+ export function packageRoot() {
508
+ return path.dirname(path.dirname(fileURLToPath(import.meta.url)));
509
+ }
510
+ /** Where the bundled skill lives inside the installed package. */
511
+ export function skillSourceDir(pkgRoot = packageRoot()) {
512
+ return path.join(pkgRoot, "skills", "open-memex");
513
+ }
514
+ /**
515
+ * D54: user-level Agent Skills directory for the open-memex skill, per client.
516
+ * null = the client has no skills concept (Visual Studio).
517
+ */
518
+ export function skillTargetDir(client, env = {}) {
519
+ const home = env.home ?? os.homedir();
520
+ switch (client) {
521
+ case "vscode":
522
+ return path.join(home, ".copilot", "skills", "open-memex");
523
+ case "cursor":
524
+ return path.join(home, ".cursor", "skills", "open-memex");
525
+ case "opencode":
526
+ return path.join(opencodeConfigDir(home, env.xdgConfigHome ?? process.env.XDG_CONFIG_HOME), "skills", "open-memex");
527
+ case "visualstudio":
528
+ return null;
529
+ }
530
+ }
531
+ /**
532
+ * Install the bundled skill for one client. Skips when already present unless
533
+ * force (a customized skill is never clobbered silently).
534
+ */
535
+ export function writeSkill(client, opts = {}) {
536
+ const target = skillTargetDir(client, opts);
537
+ if (!target)
538
+ return "unsupported";
539
+ const source = skillSourceDir(opts.pkgRoot ?? packageRoot());
540
+ if (!exists(path.join(source, "SKILL.md")))
541
+ return "missing-source";
542
+ if (exists(target) && !opts.force)
543
+ return "skipped";
544
+ fs.rmSync(target, { recursive: true, force: true });
545
+ fs.cpSync(source, target, { recursive: true });
546
+ return "installed";
547
+ }
548
+ /** Remove the open-memex skill installed by init for one client. */
549
+ export function removeSkill(client, env = {}) {
550
+ const target = skillTargetDir(client, env);
551
+ if (!target || !exists(target))
552
+ return "absent";
553
+ fs.rmSync(target, { recursive: true, force: true });
554
+ return "removed";
555
+ }
500
556
  function exists(p) {
501
557
  try {
502
558
  return fs.existsSync(p);
@@ -661,6 +717,12 @@ export async function initProject(opts) {
661
717
  writeMcpJson(root, client, opts.force);
662
718
  }
663
719
  }
720
+ // D54: Agent Skills — user-level by design (init once), alongside the MCP wiring.
721
+ const skill = writeSkill(client, { force: opts.force });
722
+ if (skill === "installed")
723
+ console.log(` + Agent Skill installed (${skillTargetDir(client)})`);
724
+ else if (skill === "skipped")
725
+ console.log(` - Agent Skill already present for ${client} (use --force to refresh)`);
664
726
  }
665
727
  if (clients.length === 0) {
666
728
  console.log(" - editor setup skipped");
@@ -855,6 +917,8 @@ export async function uninstallProject(opts) {
855
917
  // Solution-level only — --global is meaningless, same as init.
856
918
  bump(removeServerEntryFile(path.join(root, ".mcp.json"), "servers"));
857
919
  }
920
+ // D54: remove the Agent Skill installed by init.
921
+ bump(removeSkill(client));
858
922
  }
859
923
  // Copilot instructions: init may have written personal (default) or project.
860
924
  if (clients.some((c) => c !== "opencode")) {
package/dist/tools/ops.js CHANGED
@@ -40,7 +40,7 @@ export const TOOL_DESCRIPTIONS = {
40
40
  memory_list: "List memories in a scope, newest first. Useful for browsing what is remembered, or verifying that a save landed.",
41
41
  memory_supersede: "Replace an existing memory with a newer version. The old memory is kept as history (status: superseded) and retrieval returns the new one. Use when a saved fact becomes outdated and should be replaced rather than duplicated.",
42
42
  memory_forget: "Delete a memory by id. Use when the user asks to forget something.",
43
- memory_status: "Show the project memory sync pipeline: drafts waiting in the outbox (appdata), memories in the repo awaiting review or published, and any repo files not yet committed. Call this at session start and at task checkpoints, then ask the user which drafts to sync. The user may also trigger this flow by saying 'sync memory' (or '同步记忆').",
43
+ memory_status: "Show the project memory sync pipeline: drafts waiting in the outbox (appdata), memories in the repo awaiting review or published, and any repo files not yet committed. Call this at session start, when the server reports drafts waiting for review, or when the user says 'sync memory' (or '同步记忆'); then ask the user which drafts to sync.",
44
44
  memory_submit: "Move outbox drafts into the repo memory dir for review: copies the drafts in as proposed (or keeps a local approval), commits locally on the current branch, and moves the outbox originals out. Never creates a branch on its own — pass branch= only with the user's explicit approval for the full chain. Prints the push and PR commands — those need the user's explicit approval and are never run automatically.",
45
45
  memory_propose: "Copy personal memories into the project outbox as review drafts. The personal originals stay put.",
46
46
  memory_promote: "Advance a project memory one step up the review ladder (proposed → approved → published), or reject it with a note. Rejected memories are never deleted — they can be revised and resubmitted.",
@@ -63,7 +63,7 @@ export const memoryAddArgs = {
63
63
  source: z
64
64
  .string()
65
65
  .optional()
66
- .describe("Where this memory came from. Default: tool. Pass 'inference' for agent-proposed captures at checkpoints (V2-DESIGN §3.5)."),
66
+ .describe("Where this memory came from. Default: tool. Pass 'inference' for agent-proposed captures (V2-DESIGN §3.5)."),
67
67
  };
68
68
  export const memorySearchArgs = {
69
69
  query: z
package/docs/V2-DESIGN.md CHANGED
@@ -1073,6 +1073,36 @@ requirement: personal data never touches third-party services). Benchmarks to tr
1073
1073
  channel the project controls — the CLI — not in npm script output.
1074
1074
  Reported 2026-10-01.*
1075
1075
 
1076
+ - **F29** — Copilot review fixes on PR #11 (0.6.0-alpha.7). (a) The lockfile
1077
+ carried a stray `"version": "0.6.0-alpha.1"` key as a direct child of
1078
+ `packages` (left by the D50 version bump; hand-edited bumps preserved it) —
1079
+ `npm ls --package-lock-only` failed on it. Removed; version bumps now go
1080
+ through `npm pkg set` so npm owns the lockfile format. (b) D53 missed the
1081
+ shared `TOOL_DESCRIPTIONS.memory_status`: it still told agents to call the
1082
+ tool "at session start and at task checkpoints" — the polling this change
1083
+ retires. Now: session start, server-reported drafts, or explicit "sync
1084
+ memory". Same staleness removed from the `source` field hint. (c) Onboarding
1085
+ strings (postinstall note, first-run nudge, bare-CLI offer) now mention
1086
+ Visual Studio auto-detection for solution projects instead of listing only
1087
+ three editors. *Lesson: never hand-edit version fields in package-lock.json.
1088
+ Reported 2026-10-01.*
1089
+
1090
+ - **D54** — Agent Skills support (0.6.0-alpha.8). Ship a bundled
1091
+ `open-memex` skill (`skills/open-memex/SKILL.md`: frontmatter + CLI guide —
1092
+ proactive save, search, scope routing, outbox→submit flow) inside the npm
1093
+ package. `init` copies it (not symlinks — Windows needs no Developer Mode)
1094
+ into each wired editor's user-level skills dir: VS Code →
1095
+ `~/.copilot/skills/open-memex/`, Cursor → `~/.cursor/skills/open-memex/`,
1096
+ opencode → `~/.config/opencode/skills/open-memex/`; Visual Studio has no
1097
+ skills concept and is skipped. User-level by design (D46: init once). An
1098
+ existing skill is never clobbered silently — kept unless `--force`.
1099
+ `uninstall` removes only the `open-memex` skill directory. The skill tells
1100
+ agents to prefer MCP tools (`memory_add` etc.) when available and fall back
1101
+ to the CLI otherwise (with the `npx -y open-memex@latest` prefix when the
1102
+ CLI isn't on PATH). *Rationale: skill-aware agents get memory with zero MCP
1103
+ configuration; the skill is the CLI-shaped complement to the MCP server.
1104
+ Requested by Stone 2026-10-01 after the Agent Skills ecosystem suggestion.*
1105
+
1076
1106
  ## Open Questions
1077
1107
 
1078
1108
  _All resolved — see D10 (rename), D11 (type/role split), D12 (explicit pull)._
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-memex",
3
- "version": "0.6.0-alpha.5",
3
+ "version": "0.6.0-alpha.8",
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",
@@ -5,5 +5,5 @@
5
5
  // prints the pointer. Plain JS, no dependencies.
6
6
  console.log(
7
7
  "\nopen-memex installed. Run `open-memex init` to wire it into your editors — " +
8
- "it auto-detects VS Code, Cursor and opencode.\n",
8
+ "it auto-detects VS Code, Cursor and opencode (and Visual Studio for solution projects).\n",
9
9
  );
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: open-memex
3
+ description: Persistent local-first memory for the user and their projects. Use when the user shares facts, preferences, or decisions worth remembering across sessions, or when you need to recall past context before answering.
4
+ ---
5
+
6
+ # open-memex — local memory
7
+
8
+ open-memex gives you persistent memory across sessions. Memories live in local
9
+ markdown files (SQLite FTS index); the `personal` scope never leaves this machine.
10
+
11
+ If `open-memex` MCP tools (`memory_add`, `memory_search`, …) are available in
12
+ this session, prefer them. Otherwise use the CLI below. If `open-memex` is not
13
+ on PATH, prefix commands with `npx -y open-memex@latest`.
14
+
15
+ ## Save — be proactive
16
+
17
+ When the user shares something worth remembering — a fact, preference,
18
+ decision, convention, or error fix — save it without being asked:
19
+
20
+ ```sh
21
+ open-memex add "standup is at 9:30" # current project scope
22
+ open-memex add "I prefer concise diffs" --scope personal # applies everywhere
23
+ ```
24
+
25
+ Keep each memory one self-contained statement. Scope routing: facts about the
26
+ user ("I"/"me") → `personal`; everything else → the current project.
27
+
28
+ ## Recall
29
+
30
+ ```sh
31
+ open-memex search "deployment steps" # keyword search across scopes
32
+ open-memex list # recent memories in the current project
33
+ ```
34
+
35
+ Search before asking the user about past decisions or preferences they may
36
+ have told you before.
37
+
38
+ ## Project outbox → repo (the sync flow)
39
+
40
+ New project memories land in a local outbox (not in git). Review and publish:
41
+
42
+ ```sh
43
+ open-memex sync-status # drafts waiting, repo review state, uncommitted files
44
+ open-memex submit <id> # publish drafts to .ai/open-memex/ + local commit
45
+ ```
46
+
47
+ If any command output reports drafts waiting in the project outbox, run
48
+ `open-memex sync-status` and ask the user which drafts to sync. The user may
49
+ also trigger this flow by saying "sync memory".
50
+
51
+ Run `open-memex <command> --help` for full usage of any command.
package/src/cli.ts CHANGED
@@ -577,16 +577,6 @@ async function main() {
577
577
  if (outcome !== "initialized") usage(0);
578
578
  return;
579
579
  }
580
- if (cmd === "--help" || cmd === "-h" || cmd === "help") usage(0);
581
-
582
- if (cmd === "--version" || cmd === "-v") {
583
- // package.json sits two levels above this file in both layouts
584
- // (src/cli.ts and dist/cli.js).
585
- const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
586
- const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
587
- console.log(`open-memex ${pkg.version}`);
588
- return;
589
- }
590
580
 
591
581
  // F28: npm runs lifecycle scripts in the background and swallows their
592
582
  // stdout (unless --foreground-scripts), so the D50 postinstall pointer
@@ -594,17 +584,29 @@ async function main() {
594
584
  // one-line nudge on stderr until init has run or been declined — stderr
595
585
  // keeps the MCP stdio protocol (stdout) intact, and the .init.json marker
596
586
  // makes it once-ever. `init`/`uninstall` are excluded (already there /
597
- // nothing to wire).
587
+ // nothing to wire). Placed before the --help/--version early returns:
588
+ // `-v` is the first thing people run after installing.
598
589
  if (cmd !== "init" && cmd !== "uninstall") {
599
590
  const { isFirstRun } = await import("./first-run.ts");
600
591
  if (isFirstRun()) {
601
592
  console.error(
602
593
  "open-memex: first run? `open-memex init` wires it into your editors " +
603
- "(auto-detects VS Code, Cursor, opencode).",
594
+ "(auto-detects VS Code, Cursor, opencode — and Visual Studio for solution projects).",
604
595
  );
605
596
  }
606
597
  }
607
598
 
599
+ if (cmd === "--help" || cmd === "-h" || cmd === "help") usage(0);
600
+
601
+ if (cmd === "--version" || cmd === "-v") {
602
+ // package.json sits two levels above this file in both layouts
603
+ // (src/cli.ts and dist/cli.js).
604
+ const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
605
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
606
+ console.log(`open-memex ${pkg.version}`);
607
+ return;
608
+ }
609
+
608
610
  // Per-command help: `open-memex <command> --help`. Checked before loadConfig()
609
611
  // so it works even when the environment is broken.
610
612
  if (rest.includes("--help") || rest.includes("-h")) {
package/src/first-run.ts CHANGED
@@ -84,7 +84,7 @@ export async function offerFirstRunInit(
84
84
  if (!shouldOfferFirstRun()) return "skipped";
85
85
  console.log(
86
86
  `It looks like open-memex hasn't been set up on this machine yet.\n` +
87
- "`open-memex init` wires it into your editors (auto-detects VS Code, Cursor and opencode).\n",
87
+ "`open-memex init` wires it into your editors (auto-detects VS Code, Cursor and opencode — and Visual Studio for solution projects).\n",
88
88
  );
89
89
  if (await askYesNo("Run it now? [Y/n] ")) {
90
90
  await runInit();
package/src/init.ts CHANGED
@@ -553,6 +553,77 @@ export interface DetectEnv {
553
553
  xdgConfigHome?: string;
554
554
  }
555
555
 
556
+ // ---------------------------------------------------------------------------
557
+ // D54: Agent Skills. The bundled open-memex skill (skills/open-memex/SKILL.md)
558
+ // teaches skill-aware agents to use open-memex via the CLI. init copies it
559
+ // (not symlinks — Windows needs no Developer Mode) into each wired editor's
560
+ // user-level skills dir; uninstall removes only our directory.
561
+ // ---------------------------------------------------------------------------
562
+
563
+ /** Package root, two levels above this module (src/init.ts or dist/init.js). */
564
+ export function packageRoot(): string {
565
+ return path.dirname(path.dirname(fileURLToPath(import.meta.url)));
566
+ }
567
+
568
+ /** Where the bundled skill lives inside the installed package. */
569
+ export function skillSourceDir(pkgRoot: string = packageRoot()): string {
570
+ return path.join(pkgRoot, "skills", "open-memex");
571
+ }
572
+
573
+ /**
574
+ * D54: user-level Agent Skills directory for the open-memex skill, per client.
575
+ * null = the client has no skills concept (Visual Studio).
576
+ */
577
+ export function skillTargetDir(
578
+ client: InitClient,
579
+ env: DetectEnv = {},
580
+ ): string | null {
581
+ const home = env.home ?? os.homedir();
582
+ switch (client) {
583
+ case "vscode":
584
+ return path.join(home, ".copilot", "skills", "open-memex");
585
+ case "cursor":
586
+ return path.join(home, ".cursor", "skills", "open-memex");
587
+ case "opencode":
588
+ return path.join(
589
+ opencodeConfigDir(home, env.xdgConfigHome ?? process.env.XDG_CONFIG_HOME),
590
+ "skills",
591
+ "open-memex",
592
+ );
593
+ case "visualstudio":
594
+ return null;
595
+ }
596
+ }
597
+
598
+ /**
599
+ * Install the bundled skill for one client. Skips when already present unless
600
+ * force (a customized skill is never clobbered silently).
601
+ */
602
+ export function writeSkill(
603
+ client: InitClient,
604
+ opts: { force?: boolean; pkgRoot?: string } & DetectEnv = {},
605
+ ): "installed" | "skipped" | "unsupported" | "missing-source" {
606
+ const target = skillTargetDir(client, opts);
607
+ if (!target) return "unsupported";
608
+ const source = skillSourceDir(opts.pkgRoot ?? packageRoot());
609
+ if (!exists(path.join(source, "SKILL.md"))) return "missing-source";
610
+ if (exists(target) && !opts.force) return "skipped";
611
+ fs.rmSync(target, { recursive: true, force: true });
612
+ fs.cpSync(source, target, { recursive: true });
613
+ return "installed";
614
+ }
615
+
616
+ /** Remove the open-memex skill installed by init for one client. */
617
+ export function removeSkill(
618
+ client: InitClient,
619
+ env: DetectEnv = {},
620
+ ): "removed" | "absent" {
621
+ const target = skillTargetDir(client, env);
622
+ if (!target || !exists(target)) return "absent";
623
+ fs.rmSync(target, { recursive: true, force: true });
624
+ return "removed";
625
+ }
626
+
556
627
  function exists(p: string): boolean {
557
628
  try {
558
629
  return fs.existsSync(p);
@@ -726,6 +797,11 @@ export async function initProject(opts: {
726
797
  writeMcpJson(root, client, opts.force);
727
798
  }
728
799
  }
800
+ // D54: Agent Skills — user-level by design (init once), alongside the MCP wiring.
801
+ const skill = writeSkill(client, { force: opts.force });
802
+ if (skill === "installed") console.log(` + Agent Skill installed (${skillTargetDir(client)})`);
803
+ else if (skill === "skipped")
804
+ console.log(` - Agent Skill already present for ${client} (use --force to refresh)`);
729
805
  }
730
806
  if (clients.length === 0) {
731
807
  console.log(" - editor setup skipped");
@@ -918,6 +994,8 @@ export async function uninstallProject(opts: {
918
994
  // Solution-level only — --global is meaningless, same as init.
919
995
  bump(removeServerEntryFile(path.join(root, ".mcp.json"), "servers"));
920
996
  }
997
+ // D54: remove the Agent Skill installed by init.
998
+ bump(removeSkill(client));
921
999
  }
922
1000
  // Copilot instructions: init may have written personal (default) or project.
923
1001
  if (clients.some((c) => c !== "opencode")) {
package/src/tools/ops.ts CHANGED
@@ -71,7 +71,7 @@ export const TOOL_DESCRIPTIONS = {
71
71
  "Replace an existing memory with a newer version. The old memory is kept as history (status: superseded) and retrieval returns the new one. Use when a saved fact becomes outdated and should be replaced rather than duplicated.",
72
72
  memory_forget: "Delete a memory by id. Use when the user asks to forget something.",
73
73
  memory_status:
74
- "Show the project memory sync pipeline: drafts waiting in the outbox (appdata), memories in the repo awaiting review or published, and any repo files not yet committed. Call this at session start and at task checkpoints, then ask the user which drafts to sync. The user may also trigger this flow by saying 'sync memory' (or '同步记忆').",
74
+ "Show the project memory sync pipeline: drafts waiting in the outbox (appdata), memories in the repo awaiting review or published, and any repo files not yet committed. Call this at session start, when the server reports drafts waiting for review, or when the user says 'sync memory' (or '同步记忆'); then ask the user which drafts to sync.",
75
75
  memory_submit:
76
76
  "Move outbox drafts into the repo memory dir for review: copies the drafts in as proposed (or keeps a local approval), commits locally on the current branch, and moves the outbox originals out. Never creates a branch on its own — pass branch= only with the user's explicit approval for the full chain. Prints the push and PR commands — those need the user's explicit approval and are never run automatically.",
77
77
  memory_propose:
@@ -104,7 +104,7 @@ export const memoryAddArgs = {
104
104
  .string()
105
105
  .optional()
106
106
  .describe(
107
- "Where this memory came from. Default: tool. Pass 'inference' for agent-proposed captures at checkpoints (V2-DESIGN §3.5).",
107
+ "Where this memory came from. Default: tool. Pass 'inference' for agent-proposed captures (V2-DESIGN §3.5).",
108
108
  ),
109
109
  };
110
110
  export type MemoryAddArgs = z.infer<z.ZodObject<typeof memoryAddArgs>>;