gitmem-mcp 1.1.2 → 1.2.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/CLAUDE.md.template +29 -20
  3. package/README.md +36 -20
  4. package/bin/gitmem.js +33 -0
  5. package/bin/init-wizard.js +147 -21
  6. package/copilot-instructions.template +101 -0
  7. package/cursorrules.template +29 -20
  8. package/dist/commands/telemetry.d.ts +11 -0
  9. package/dist/commands/telemetry.js +207 -0
  10. package/dist/hooks/format-utils.d.ts +1 -0
  11. package/dist/hooks/format-utils.js +7 -6
  12. package/dist/lib/telemetry.d.ts +101 -0
  13. package/dist/lib/telemetry.js +272 -0
  14. package/dist/schemas/session-close.d.ts +40 -40
  15. package/dist/server.js +15 -4
  16. package/dist/services/analytics.js +1 -1
  17. package/dist/services/display-protocol.d.ts +45 -4
  18. package/dist/services/display-protocol.js +114 -15
  19. package/dist/services/enforcement.d.ts +24 -0
  20. package/dist/services/enforcement.js +126 -0
  21. package/dist/services/nudge-variants.d.ts +29 -0
  22. package/dist/services/nudge-variants.js +71 -0
  23. package/dist/tools/cleanup-threads.js +9 -5
  24. package/dist/tools/confirm-scars.js +28 -11
  25. package/dist/tools/definitions.js +7 -7
  26. package/dist/tools/list-threads.d.ts +1 -1
  27. package/dist/tools/list-threads.js +8 -17
  28. package/dist/tools/log.js +3 -3
  29. package/dist/tools/prepare-context.js +7 -10
  30. package/dist/tools/recall.js +7 -17
  31. package/dist/tools/record-scar-usage-batch.js +7 -2
  32. package/dist/tools/record-scar-usage.js +7 -2
  33. package/dist/tools/search.js +3 -3
  34. package/dist/tools/session-close.js +105 -81
  35. package/dist/tools/session-start.js +73 -14
  36. package/dist/types/index.d.ts +2 -0
  37. package/package.json +13 -3
  38. package/windsurfrules.template +101 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.0] - 2026-02-20
11
+
12
+ ### Added
13
+ - **Telemetry CLI**: `npx gitmem-mcp telemetry` command for viewing scar effectiveness metrics and recall statistics.
14
+ - **Confirm-scars prefix matching**: `confirm_scars` now accepts 8-character ID prefixes instead of requiring full UUIDs — faster agent workflows.
15
+ - **Session-close timing**: `session_close` now tracks and reports ceremony duration for performance visibility.
16
+
17
+ ### Fixed
18
+ - **Test assertion alignment**: Updated smoke and E2E test assertions to match current CLI output format (branded `((●))` display, lowercase identifiers).
19
+ - **No-console-log allowlist**: CLI commands correctly excluded from console.log lint rule.
20
+
21
+ ## [1.1.4] - 2026-02-20
22
+
23
+ ### Changed
24
+ - **Recall default switched to c-review**: Production nudge header changed from "INSTITUTIONAL MEMORY ACTIVATED" to "N scars to review". Nudge-bench testing (54 runs × 3 models) showed 89% scar reference rate vs 44% — a 2x improvement across Opus, Sonnet, and Haiku.
25
+
26
+ ### Fixed
27
+ - **Thread display cleanup**: Removed internal thread IDs from `list_threads` output. Threads now show `# | Thread | Active` — IDs were implementation detail with no user value.
28
+
29
+ ## [1.1.3] - 2026-02-19
30
+
31
+ ### Added
32
+ - **Multi-client init wizard**: `npx gitmem-mcp init` now supports VS Code, Windsurf, and generic MCP clients in addition to Claude Code and Cursor.
33
+ - **Server-side enforcement layer**: Universal compliance enforcement that works across all MCP clients — recall before consequential actions, scar confirmation gates.
34
+ - **Scar framing guidance**: `create_learning` tool now guides agents to frame scars as "what we now know" (factual discovery) rather than "what I did wrong" (self-criticism).
35
+ - **Auto-detect agent and session**: Scar usage tracking automatically detects the current agent identity and session context.
36
+ - **Closing payload schema**: Session close payload schema now ships with `init` and `session_start` for client reference.
37
+ - **npm discoverability keywords**: Added `mcp-server`, `claude-code`, `ai-memory`, `ai-agent` keywords for npm search.
38
+ - **Documentation site**:
39
+ - Restored Fumadocs source for gitmem.ai/docs with emerald theme.
40
+ - Redesigned docs landing page with improved messaging and branding.
41
+ - Added FAQ page with 11 questions.
42
+ - Added MCP one-liner explainer for new users.
43
+ - Added 3 docs examples (scar stories): credential leak, phantom deploy, and first scar.
44
+ - Inline mailing list signup form in docs pages.
45
+ - Rich installation page with multi-client instructions.
46
+
47
+ ### Fixed
48
+ - **Thread display output**: `list_threads` and `cleanup_threads` replaced ASCII box-drawing tables with markdown tables. Thread text truncation increased from 40-48 to 60 characters. Output now renders cleanly in all MCP clients instead of clipping on narrow terminals.
49
+ - **Version reporting**: Server now reads version from `package.json` instead of hardcoded `1.0.3`.
50
+ - **Log header clarity**: `gitmem log` header now says "most recent learnings" instead of ambiguous label.
51
+ - **Analyze output**: Relabeled misleading "Open Threads" to "Threads Referenced" in analyze output.
52
+ - **Stale thread cleanup**: Drop stale local-only threads on `session_start` when Supabase is authoritative source.
53
+ - **Package name in docs**: Corrected to `npx gitmem-mcp init` (was `npx gitmem init`).
54
+ - **Docs fixes**: Removed duplicate h1 headers, fixed sidebar nav duplicate entry, corrected GitHub URLs after org migration.
55
+
10
56
  ## [1.1.2] - 2026-02-17
11
57
 
12
58
  ### Changed
@@ -51,18 +51,19 @@ A PreToolUse hook blocks consequential actions until all recalled scars are conf
51
51
 
52
52
  On "closing", "done for now", or "wrapping up":
53
53
 
54
- 1. **Answer these reflection questions** and display to the human:
55
- - What broke that you didn't expect?
56
- - What took longer than it should have?
57
- - What would you do differently next time?
58
- - What pattern or approach worked well?
59
- - What assumption was wrong?
60
- - Which scars did you apply?
61
- - What should be captured as institutional memory?
62
-
63
- 2. **Ask the human**: "Any corrections or additions?" Wait for their response.
64
-
65
- 3. **Write payload** to `.gitmem/closing-payload.json`:
54
+ 1. **Write reflection directly to payload** do NOT display the full Q&A to the human.
55
+ Internally answer these 9 questions and write them straight to `.gitmem/closing-payload.json`:
56
+ - Q1: What broke that you didn't expect?
57
+ - Q2: What took longer than it should have?
58
+ - Q3: What would you do differently next time?
59
+ - Q4: What pattern or approach worked well?
60
+ - Q5: What assumption was wrong?
61
+ - Q6: Which scars did you apply?
62
+ - Q7: What should be captured as institutional memory?
63
+ - Q8: How did the human prefer to work this session?
64
+ - Q9: What collaborative dynamic worked or didn't?
65
+
66
+ Payload schema (`.gitmem/closing-payload.json`):
66
67
  ```json
67
68
  {
68
69
  "closing_reflection": {
@@ -73,15 +74,15 @@ On "closing", "done for now", or "wrapping up":
73
74
  "wrong_assumption": "...",
74
75
  "scars_applied": ["scar title 1", "scar title 2"],
75
76
  "institutional_memory_items": "...",
76
- "collaborative_dynamic": "Q8: How human preferred to work",
77
- "rapport_notes": "Q9: What collaborative dynamic worked"
77
+ "collaborative_dynamic": "...",
78
+ "rapport_notes": "..."
78
79
  },
79
80
  "task_completion": {
80
- "questions_displayed_at": "ISO timestamp",
81
- "reflection_completed_at": "ISO timestamp",
82
- "human_asked_at": "ISO timestamp",
83
- "human_response_at": "ISO timestamp",
84
- "human_response": "human's correction text or 'Looks good'"
81
+ "questions_displayed_at": "ISO timestamp (when reflection started)",
82
+ "reflection_completed_at": "ISO timestamp (when payload written)",
83
+ "human_asked_at": "ISO timestamp (when 'Corrections?' shown)",
84
+ "human_response_at": "ISO timestamp (when human replied)",
85
+ "human_response": "user's correction text or 'none'"
85
86
  },
86
87
  "human_corrections": "",
87
88
  "scars_to_record": [],
@@ -91,7 +92,15 @@ On "closing", "done for now", or "wrapping up":
91
92
  }
92
93
  ```
93
94
 
94
- 4. **Call `session_close`** with `session_id` and `close_type: "standard"`
95
+ 2. **Show compact summary** (3-4 lines max):
96
+ ```
97
+ Session close: [N] learnings captured, [M] scars applied, [K] threads open
98
+ Key lesson: [one-sentence from Q7]
99
+ ```
100
+
101
+ 3. **Ask**: "Corrections?" — wait for response, then call `session_close`.
102
+
103
+ 4. **Call `session_close`** with `session_id` and `close_type: "standard"`.
95
104
 
96
105
  For short exploratory sessions (< 30 min, no real work), use `close_type: "quick"` — no questions needed.
97
106
  <!-- gitmem:end -->
package/README.md CHANGED
@@ -3,11 +3,11 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <a href="https://www.npmjs.com/package/gitmem-mcp"><img src="https://img.shields.io/npm/v/gitmem-mcp?style=flat-square&color=ed1e25&label=npm" alt="npm version" /></a>
6
+ <a href="https://www.npmjs.com/package/gitmem-mcp"><img src="https://img.shields.io/npm/v/gitmem-mcp?style=flat-square&color=c41920&label=npm" alt="npm version" /></a>
7
7
  <a href="https://www.npmjs.com/package/gitmem-mcp"><img src="https://img.shields.io/npm/dm/gitmem-mcp?style=flat-square&color=333333&label=downloads" alt="npm downloads" /></a>
8
- <a href="https://github.com/gitmem-dev/gitmem/blob/main/LICENSE"><img src="https://img.shields.io/github/license/gitmem-dev/gitmem?style=flat-square&color=ed1e25" alt="MIT License" /></a>
8
+ <a href="https://github.com/gitmem-dev/gitmem/blob/main/LICENSE"><img src="https://img.shields.io/github/license/gitmem-dev/gitmem?style=flat-square&color=c41920" alt="MIT License" /></a>
9
9
  <a href="https://github.com/gitmem-dev/gitmem/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/gitmem-dev/gitmem/ci.yml?style=flat-square&color=333333&label=build" alt="Build" /></a>
10
- <img src="https://img.shields.io/badge/node-%3E%3D22-ed1e25?style=flat-square" alt="Node.js >= 22" />
10
+ <img src="https://img.shields.io/badge/node-%3E%3D18-c41920?style=flat-square" alt="Node.js >= 18" />
11
11
  </p>
12
12
 
13
13
  <p align="center">
@@ -21,7 +21,9 @@
21
21
 
22
22
  GitMem is an [MCP server](https://modelcontextprotocol.io/) that gives your AI coding agent **persistent memory across sessions**. It remembers mistakes (scars), successes (wins), and decisions — so your agent learns from experience instead of starting from scratch every time.
23
23
 
24
- Works with **Claude Code**, **Claude Desktop**, **Cursor**, and any MCP-compatible client.
24
+ > **What's MCP?** [Model Context Protocol](https://modelcontextprotocol.io/) is how AI coding tools connect to external capabilities. GitMem is an MCP server — install it once and your agent gains persistent memory.
25
+
26
+ Works with **Claude Code**, **Cursor**, **VS Code (Copilot)**, **Windsurf**, and any MCP-compatible client.
25
27
 
26
28
  ## Quick Start
27
29
 
@@ -29,19 +31,19 @@ Works with **Claude Code**, **Claude Desktop**, **Cursor**, and any MCP-compatib
29
31
  npx gitmem-mcp init
30
32
  ```
31
33
 
32
- One command. The wizard sets up everything:
33
- - `.gitmem/` directory with 3 starter scars
34
- - `.mcp.json` with gitmem server entry
35
- - `CLAUDE.md` with memory protocol instructions
36
- - `.claude/settings.json` with tool permissions
37
- - Lifecycle hooks for automatic session management
34
+ One command. The wizard auto-detects your IDE and sets up everything:
35
+ - `.gitmem/` directory with starter scars
36
+ - MCP server config (`.mcp.json`, `.vscode/mcp.json`, `.cursor/mcp.json`, etc.)
37
+ - Instructions file (`CLAUDE.md`, `.cursorrules`, `.windsurfrules`, `.github/copilot-instructions.md`)
38
+ - Lifecycle hooks (where supported)
38
39
  - `.gitignore` updated
39
40
 
40
41
  Already have existing config? The wizard merges without destroying anything. Re-running is safe.
41
42
 
42
43
  ```bash
43
- npx gitmem-mcp init --yes # Non-interactive
44
- npx gitmem-mcp init --dry-run # Preview changes
44
+ npx gitmem-mcp init --yes # Non-interactive
45
+ npx gitmem-mcp init --dry-run # Preview changes
46
+ npx gitmem-mcp init --client vscode # Force specific client
45
47
  ```
46
48
 
47
49
  ## How It Works
@@ -78,16 +80,22 @@ Every scar includes **counter-arguments** — reasons why someone might reasonab
78
80
 
79
81
  ## Supported Clients
80
82
 
81
- | Client | Setup |
82
- |--------|-------|
83
- | **Claude Code** | `npx gitmem-mcp init` (auto-detected) |
84
- | **Claude Desktop** | `npx gitmem-mcp init` or add to `claude_desktop_config.json` |
85
- | **Cursor** | `npx gitmem-mcp init` or add to `.cursor/mcp.json` |
86
- | **Any MCP client** | Add `npx -y gitmem-mcp` as an MCP server |
83
+ | Client | Setup | Hooks |
84
+ |--------|-------|-------|
85
+ | **Claude Code** | `npx gitmem-mcp init` | Full (session, recall, credential guard) |
86
+ | **Cursor** | `npx gitmem-mcp init --client cursor` | Partial (session, recall) |
87
+ | **VS Code (Copilot)** | `npx gitmem-mcp init --client vscode` | Instructions-based |
88
+ | **Windsurf** | `npx gitmem-mcp init --client windsurf` | Instructions-based |
89
+ | **Claude Desktop** | Add to `claude_desktop_config.json` | Manual |
90
+ | **Any MCP client** | `npx gitmem-mcp init --client generic` | Instructions-based |
91
+
92
+ The wizard auto-detects your IDE. Use `--client` to override.
87
93
 
88
94
  <details>
89
95
  <summary><strong>Manual MCP configuration</strong></summary>
90
96
 
97
+ Add this to your MCP client's config file:
98
+
91
99
  ```json
92
100
  {
93
101
  "mcpServers": {
@@ -99,13 +107,21 @@ Every scar includes **counter-arguments** — reasons why someone might reasonab
99
107
  }
100
108
  ```
101
109
 
110
+ | Client | Config file |
111
+ |--------|-------------|
112
+ | Claude Code | `.mcp.json` |
113
+ | Cursor | `.cursor/mcp.json` |
114
+ | VS Code | `.vscode/mcp.json` |
115
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
116
+
102
117
  </details>
103
118
 
104
119
  ## CLI Commands
105
120
 
106
121
  | Command | Description |
107
122
  |---------|-------------|
108
- | `npx gitmem-mcp init` | Interactive setup wizard |
123
+ | `npx gitmem-mcp init` | Interactive setup wizard (auto-detects IDE) |
124
+ | `npx gitmem-mcp init --client <name>` | Setup for specific client (`claude`, `cursor`, `vscode`, `windsurf`, `generic`) |
109
125
  | `npx gitmem-mcp init --yes` | Non-interactive setup |
110
126
  | `npx gitmem-mcp init --dry-run` | Preview changes |
111
127
  | `npx gitmem-mcp uninstall` | Clean removal (preserves `.gitmem/` data) |
@@ -120,7 +136,7 @@ Every scar includes **counter-arguments** — reasons why someone might reasonab
120
136
  | **Session analytics** | Spot patterns in what keeps going wrong |
121
137
  | **Sub-agent briefing** | Hand institutional context to sub-agents automatically |
122
138
  | **Cloud persistence** | Memory survives machine changes, shareable across team |
123
- | **A/B testing** | Optimize scar phrasing based on what actually changes behavior |
139
+ | **A/B testing analytics** | Measure which scar phrasings actually change agent behavior (free tier includes `GITMEM_NUDGE_VARIANT` for manual testing) |
124
140
 
125
141
  The free tier gives you everything for solo projects. Pro makes recall smarter and memory portable.
126
142
 
package/bin/gitmem.js CHANGED
@@ -51,6 +51,9 @@ Other commands:
51
51
  npx gitmem-mcp check --full Full diagnostic with benchmarks
52
52
  npx gitmem-mcp install-hooks Install hooks (standalone)
53
53
  npx gitmem-mcp uninstall-hooks Remove hooks (standalone)
54
+ npx gitmem-mcp telemetry status Check telemetry settings
55
+ npx gitmem-mcp telemetry enable Enable anonymous usage tracking (opt-in)
56
+ npx gitmem-mcp telemetry disable Disable usage tracking
54
57
  npx gitmem-mcp server Start MCP server (default)
55
58
  npx gitmem-mcp help Show this help message
56
59
 
@@ -200,6 +203,10 @@ async function cmdInit() {
200
203
  console.log("Add .gitmem/ to your .gitignore:");
201
204
  console.log(" echo '.gitmem/' >> .gitignore");
202
205
  console.log("");
206
+
207
+ // Prompt for telemetry (optional, non-blocking)
208
+ promptTelemetryOptIn();
209
+
203
210
  console.log("To upgrade to Pro tier (semantic search + Supabase persistence):");
204
211
  console.log(" Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY, then run init again.");
205
212
  return;
@@ -686,6 +693,29 @@ function cmdInstallHooks() {
686
693
  console.log(" npx gitmem-mcp install-hooks --force");
687
694
  }
688
695
 
696
+ /**
697
+ * Prompt user to opt-in to telemetry (non-blocking, async)
698
+ */
699
+ function promptTelemetryOptIn() {
700
+ console.log("─────────────────────────────────────────────");
701
+ console.log("");
702
+ console.log("Help improve GitMem? (Optional)");
703
+ console.log("");
704
+ console.log("Send anonymous usage data to help us understand:");
705
+ console.log(" • Which features are most useful");
706
+ console.log(" • Where errors occur");
707
+ console.log(" • Performance patterns");
708
+ console.log("");
709
+ console.log("✓ No queries, scars, or project content");
710
+ console.log("✓ Randomized ID (not linked to you)");
711
+ console.log("✓ View all data before it's sent");
712
+ console.log("✓ Disable anytime");
713
+ console.log("");
714
+ console.log("To enable: npx gitmem-mcp telemetry enable");
715
+ console.log("Privacy policy: https://gitmem.ai/privacy");
716
+ console.log("");
717
+ }
718
+
689
719
  /**
690
720
  * Uninstall gitmem hooks.
691
721
  *
@@ -858,6 +888,9 @@ switch (command) {
858
888
  case "check":
859
889
  import("../dist/commands/check.js").then((m) => m.main(process.argv.slice(3)));
860
890
  break;
891
+ case "telemetry":
892
+ import("../dist/commands/telemetry.js").then((m) => m.main(process.argv.slice(3)));
893
+ break;
861
894
  case "install-hooks":
862
895
  cmdInstallHooks();
863
896
  break;
@@ -6,7 +6,7 @@
6
6
  * Interactive setup that detects existing config, prompts, and merges.
7
7
  * Supports Claude Code and Cursor IDE.
8
8
  *
9
- * Usage: npx gitmem-mcp init [--yes] [--dry-run] [--project <name>] [--client <claude|cursor>]
9
+ * Usage: npx gitmem-mcp init [--yes] [--dry-run] [--project <name>] [--client <claude|cursor|vscode|windsurf|generic>]
10
10
  */
11
11
 
12
12
  import {
@@ -35,11 +35,15 @@ const clientFlag = clientIdx !== -1 ? args[clientIdx + 1]?.toLowerCase() : null;
35
35
 
36
36
  // ── Client Configuration ──
37
37
 
38
+ // Resolve user home directory for clients that use user-level config
39
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "~";
40
+
38
41
  const CLIENT_CONFIGS = {
39
42
  claude: {
40
43
  name: "Claude Code",
41
44
  mcpConfigPath: join(cwd, ".mcp.json"),
42
45
  mcpConfigName: ".mcp.json",
46
+ mcpConfigScope: "project",
43
47
  instructionsFile: join(cwd, "CLAUDE.md"),
44
48
  instructionsName: "CLAUDE.md",
45
49
  templateFile: join(__dirname, "..", "CLAUDE.md.template"),
@@ -50,12 +54,14 @@ const CLIENT_CONFIGS = {
50
54
  settingsLocalFile: join(cwd, ".claude", "settings.local.json"),
51
55
  hasPermissions: true,
52
56
  hooksInSettings: true,
57
+ hasHooks: true,
53
58
  completionMsg: "Setup complete! Start Claude Code \u2014 memory is active.",
54
59
  },
55
60
  cursor: {
56
61
  name: "Cursor",
57
62
  mcpConfigPath: join(cwd, ".cursor", "mcp.json"),
58
63
  mcpConfigName: ".cursor/mcp.json",
64
+ mcpConfigScope: "project",
59
65
  instructionsFile: join(cwd, ".cursorrules"),
60
66
  instructionsName: ".cursorrules",
61
67
  templateFile: join(__dirname, "..", "cursorrules.template"),
@@ -66,10 +72,66 @@ const CLIENT_CONFIGS = {
66
72
  settingsLocalFile: null,
67
73
  hasPermissions: false,
68
74
  hooksInSettings: false,
75
+ hasHooks: true,
69
76
  hooksFile: join(cwd, ".cursor", "hooks.json"),
70
77
  hooksFileName: ".cursor/hooks.json",
71
78
  completionMsg: "Setup complete! Open Cursor (Agent mode) \u2014 memory is active.",
72
79
  },
80
+ vscode: {
81
+ name: "VS Code (Copilot)",
82
+ mcpConfigPath: join(cwd, ".vscode", "mcp.json"),
83
+ mcpConfigName: ".vscode/mcp.json",
84
+ mcpConfigScope: "project",
85
+ instructionsFile: join(cwd, ".github", "copilot-instructions.md"),
86
+ instructionsName: ".github/copilot-instructions.md",
87
+ templateFile: join(__dirname, "..", "copilot-instructions.template"),
88
+ startMarker: "<!-- gitmem:start -->",
89
+ endMarker: "<!-- gitmem:end -->",
90
+ configDir: join(cwd, ".vscode"),
91
+ settingsFile: null,
92
+ settingsLocalFile: null,
93
+ hasPermissions: false,
94
+ hooksInSettings: false,
95
+ hasHooks: false,
96
+ completionMsg: "Setup complete! Open VS Code \u2014 memory is active via Copilot.",
97
+ },
98
+ windsurf: {
99
+ name: "Windsurf",
100
+ mcpConfigPath: join(homeDir, ".codeium", "windsurf", "mcp_config.json"),
101
+ mcpConfigName: "~/.codeium/windsurf/mcp_config.json",
102
+ mcpConfigScope: "user",
103
+ instructionsFile: join(cwd, ".windsurfrules"),
104
+ instructionsName: ".windsurfrules",
105
+ templateFile: join(__dirname, "..", "windsurfrules.template"),
106
+ startMarker: "# --- gitmem:start ---",
107
+ endMarker: "# --- gitmem:end ---",
108
+ configDir: null,
109
+ settingsFile: null,
110
+ settingsLocalFile: null,
111
+ hasPermissions: false,
112
+ hooksInSettings: false,
113
+ hasHooks: false,
114
+ completionMsg: "Setup complete! Open Windsurf \u2014 memory is active.",
115
+ },
116
+ generic: {
117
+ name: "Generic MCP Client",
118
+ mcpConfigPath: join(cwd, ".mcp.json"),
119
+ mcpConfigName: ".mcp.json",
120
+ mcpConfigScope: "project",
121
+ instructionsFile: join(cwd, "CLAUDE.md"),
122
+ instructionsName: "CLAUDE.md",
123
+ templateFile: join(__dirname, "..", "CLAUDE.md.template"),
124
+ startMarker: "<!-- gitmem:start -->",
125
+ endMarker: "<!-- gitmem:end -->",
126
+ configDir: null,
127
+ settingsFile: null,
128
+ settingsLocalFile: null,
129
+ hasPermissions: false,
130
+ hooksInSettings: false,
131
+ hasHooks: false,
132
+ completionMsg:
133
+ "Setup complete! Configure your MCP client to use the gitmem server from .mcp.json.",
134
+ },
73
135
  };
74
136
 
75
137
  // Shared paths (client-agnostic)
@@ -84,33 +146,49 @@ let cc; // shorthand for CLIENT_CONFIGS[client]
84
146
 
85
147
  // ── Client Detection ──
86
148
 
149
+ const VALID_CLIENTS = Object.keys(CLIENT_CONFIGS);
150
+
87
151
  function detectClient() {
88
152
  // Explicit flag takes priority
89
153
  if (clientFlag) {
90
- if (clientFlag !== "claude" && clientFlag !== "cursor") {
91
- console.error(` Error: Unknown client "${clientFlag}". Use --client claude or --client cursor.`);
154
+ if (!VALID_CLIENTS.includes(clientFlag)) {
155
+ console.error(` Error: Unknown client "${clientFlag}". Use --client ${VALID_CLIENTS.join("|")}.`);
92
156
  process.exit(1);
93
157
  }
94
158
  return clientFlag;
95
159
  }
96
160
 
97
- // Auto-detect based on directory presence
161
+ // Auto-detect based on directory/file presence
98
162
  const hasCursorDir = existsSync(join(cwd, ".cursor"));
99
163
  const hasClaudeDir = existsSync(join(cwd, ".claude"));
100
164
  const hasMcpJson = existsSync(join(cwd, ".mcp.json"));
101
165
  const hasClaudeMd = existsSync(join(cwd, "CLAUDE.md"));
102
166
  const hasCursorRules = existsSync(join(cwd, ".cursorrules"));
103
167
  const hasCursorMcp = existsSync(join(cwd, ".cursor", "mcp.json"));
168
+ const hasVscodeDir = existsSync(join(cwd, ".vscode"));
169
+ const hasVscodeMcp = existsSync(join(cwd, ".vscode", "mcp.json"));
170
+ const hasCopilotInstructions = existsSync(join(cwd, ".github", "copilot-instructions.md"));
171
+ const hasWindsurfRules = existsSync(join(cwd, ".windsurfrules"));
172
+ const hasWindsurfMcp = existsSync(
173
+ join(homeDir, ".codeium", "windsurf", "mcp_config.json")
174
+ );
104
175
 
105
176
  // Strong Cursor signals
106
177
  if (hasCursorDir && !hasClaudeDir && !hasMcpJson && !hasClaudeMd) return "cursor";
107
- if (hasCursorRules && !hasClaudeMd) return "cursor";
108
- if (hasCursorMcp && !hasMcpJson) return "cursor";
178
+ if (hasCursorRules && !hasClaudeMd && !hasCopilotInstructions) return "cursor";
179
+ if (hasCursorMcp && !hasMcpJson && !hasVscodeMcp) return "cursor";
109
180
 
110
181
  // Strong Claude signals
111
- if (hasClaudeDir && !hasCursorDir) return "claude";
112
- if (hasMcpJson && !hasCursorMcp) return "claude";
113
- if (hasClaudeMd && !hasCursorRules) return "claude";
182
+ if (hasClaudeDir && !hasCursorDir && !hasVscodeDir) return "claude";
183
+ if (hasMcpJson && !hasCursorMcp && !hasVscodeMcp) return "claude";
184
+ if (hasClaudeMd && !hasCursorRules && !hasCopilotInstructions) return "claude";
185
+
186
+ // VS Code signals
187
+ if (hasVscodeMcp && !hasMcpJson && !hasCursorMcp) return "vscode";
188
+ if (hasCopilotInstructions && !hasClaudeMd && !hasCursorRules) return "vscode";
189
+
190
+ // Windsurf signals
191
+ if (hasWindsurfRules && !hasClaudeMd && !hasCursorRules && !hasCopilotInstructions) return "windsurf";
114
192
 
115
193
  // Default to Claude Code (most common)
116
194
  return "claude";
@@ -428,6 +506,36 @@ async function stepMemoryStore() {
428
506
  }
429
507
  }
430
508
 
509
+ // Closing payload template — agents read this before writing closing-payload.json
510
+ const templatePath = join(gitmemDir, "closing-payload-template.json");
511
+ if (!existsSync(templatePath)) {
512
+ writeJson(templatePath, {
513
+ closing_reflection: {
514
+ what_broke: "",
515
+ what_took_longer: "",
516
+ do_differently: "",
517
+ what_worked: "",
518
+ wrong_assumption: "",
519
+ scars_applied: [],
520
+ institutional_memory_items: "",
521
+ collaborative_dynamic: "",
522
+ rapport_notes: ""
523
+ },
524
+ task_completion: {
525
+ questions_displayed_at: "ISO-8601 timestamp",
526
+ reflection_completed_at: "ISO-8601 timestamp",
527
+ human_asked_at: "ISO-8601 timestamp",
528
+ human_response_at: "ISO-8601 timestamp",
529
+ human_response: "no corrections | actual corrections text"
530
+ },
531
+ human_corrections: "",
532
+ scars_to_record: [],
533
+ learnings_created: [],
534
+ open_threads: [],
535
+ decisions: []
536
+ });
537
+ }
538
+
431
539
  console.log(
432
540
  ` Created .gitmem/ with ${starterScars.length} starter scars` +
433
541
  (added < starterScars.length
@@ -439,6 +547,7 @@ async function stepMemoryStore() {
439
547
  async function stepMcpServer() {
440
548
  const mcpPath = cc.mcpConfigPath;
441
549
  const mcpName = cc.mcpConfigName;
550
+ const isUserLevel = cc.mcpConfigScope === "user";
442
551
 
443
552
  const existing = readJson(mcpPath);
444
553
  const hasGitmem =
@@ -453,9 +562,10 @@ async function stepMcpServer() {
453
562
  ? Object.keys(existing.mcpServers).length
454
563
  : 0;
455
564
  const tierLabel = process.env.SUPABASE_URL ? "pro" : "free";
565
+ const scopeNote = isUserLevel ? " (user-level config)" : "";
456
566
  const prompt = existing
457
- ? `Add gitmem to ${mcpName}? (${serverCount} existing server${serverCount !== 1 ? "s" : ""} preserved)`
458
- : `Create ${mcpName} with gitmem server?`;
567
+ ? `Add gitmem to ${mcpName}?${scopeNote} (${serverCount} existing server${serverCount !== 1 ? "s" : ""} preserved)`
568
+ : `Create ${mcpName} with gitmem server?${scopeNote}`;
459
569
 
460
570
  if (!(await confirm(prompt))) {
461
571
  console.log(" Skipped.");
@@ -463,11 +573,11 @@ async function stepMcpServer() {
463
573
  }
464
574
 
465
575
  if (dryRun) {
466
- console.log(` [dry-run] Would add gitmem entry to ${mcpName} (${tierLabel} tier)`);
576
+ console.log(` [dry-run] Would add gitmem entry to ${mcpName} (${tierLabel} tier${scopeNote})`);
467
577
  return;
468
578
  }
469
579
 
470
- // Ensure parent directory exists (for .cursor/mcp.json)
580
+ // Ensure parent directory exists (for .cursor/mcp.json, .vscode/mcp.json, ~/.codeium/windsurf/)
471
581
  const parentDir = dirname(mcpPath);
472
582
  if (!existsSync(parentDir)) {
473
583
  mkdirSync(parentDir, { recursive: true });
@@ -481,7 +591,8 @@ async function stepMcpServer() {
481
591
  console.log(
482
592
  ` Added gitmem entry to ${mcpName} (${tierLabel} tier` +
483
593
  (process.env.SUPABASE_URL ? " \u2014 Supabase detected" : " \u2014 local storage") +
484
- ")"
594
+ ")" +
595
+ (isUserLevel ? " [user-level]" : "")
485
596
  );
486
597
  }
487
598
 
@@ -525,6 +636,12 @@ async function stepInstructions() {
525
636
  block = `${cc.startMarker}\n${block}\n${cc.endMarker}`;
526
637
  }
527
638
 
639
+ // Ensure parent directory exists (for .github/copilot-instructions.md)
640
+ const instrParentDir = dirname(instrPath);
641
+ if (!existsSync(instrParentDir)) {
642
+ mkdirSync(instrParentDir, { recursive: true });
643
+ }
644
+
528
645
  if (exists) {
529
646
  content = content.trimEnd() + "\n\n" + block + "\n";
530
647
  } else {
@@ -598,6 +715,11 @@ function copyHookScripts() {
598
715
  }
599
716
 
600
717
  async function stepHooks() {
718
+ if (!cc.hasHooks) {
719
+ console.log(` ${cc.name} does not support lifecycle hooks. Skipping.`);
720
+ console.log(" Enforcement relies on system prompt instructions instead.");
721
+ return;
722
+ }
601
723
  if (cc.hooksInSettings) {
602
724
  return stepHooksClaude();
603
725
  }
@@ -821,7 +943,7 @@ async function main() {
821
943
  );
822
944
  }
823
945
 
824
- if (!cc.hooksInSettings && cc.hooksFile && existsSync(cc.hooksFile)) {
946
+ if (!cc.hooksInSettings && cc.hasHooks && cc.hooksFile && existsSync(cc.hooksFile)) {
825
947
  const hooks = readJson(cc.hooksFile);
826
948
  const hookCount = hooks?.hooks
827
949
  ? Object.values(hooks.hooks).flat().length
@@ -850,8 +972,10 @@ async function main() {
850
972
  );
851
973
  console.log("");
852
974
 
853
- // Run steps — step count depends on client
854
- const stepCount = cc.hasPermissions ? 6 : 5;
975
+ // Run steps — step count depends on client capabilities
976
+ let stepCount = 4; // memory store + mcp server + instructions + gitignore
977
+ if (cc.hasPermissions) stepCount++;
978
+ if (cc.hasHooks) stepCount++;
855
979
  let step = 1;
856
980
 
857
981
  console.log(` Step ${step}/${stepCount} \u2014 Memory Store`);
@@ -876,10 +1000,12 @@ async function main() {
876
1000
  step++;
877
1001
  }
878
1002
 
879
- console.log(` Step ${step}/${stepCount} \u2014 Lifecycle Hooks`);
880
- await stepHooks();
881
- console.log("");
882
- step++;
1003
+ if (cc.hasHooks) {
1004
+ console.log(` Step ${step}/${stepCount} \u2014 Lifecycle Hooks`);
1005
+ await stepHooks();
1006
+ console.log("");
1007
+ step++;
1008
+ }
883
1009
 
884
1010
  console.log(` Step ${step}/${stepCount} \u2014 Gitignore`);
885
1011
  await stepGitignore();