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.
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md.template +29 -20
- package/README.md +36 -20
- package/bin/gitmem.js +33 -0
- package/bin/init-wizard.js +147 -21
- package/copilot-instructions.template +101 -0
- package/cursorrules.template +29 -20
- package/dist/commands/telemetry.d.ts +11 -0
- package/dist/commands/telemetry.js +207 -0
- package/dist/hooks/format-utils.d.ts +1 -0
- package/dist/hooks/format-utils.js +7 -6
- package/dist/lib/telemetry.d.ts +101 -0
- package/dist/lib/telemetry.js +272 -0
- package/dist/schemas/session-close.d.ts +40 -40
- package/dist/server.js +15 -4
- package/dist/services/analytics.js +1 -1
- package/dist/services/display-protocol.d.ts +45 -4
- package/dist/services/display-protocol.js +114 -15
- package/dist/services/enforcement.d.ts +24 -0
- package/dist/services/enforcement.js +126 -0
- package/dist/services/nudge-variants.d.ts +29 -0
- package/dist/services/nudge-variants.js +71 -0
- package/dist/tools/cleanup-threads.js +9 -5
- package/dist/tools/confirm-scars.js +28 -11
- package/dist/tools/definitions.js +7 -7
- package/dist/tools/list-threads.d.ts +1 -1
- package/dist/tools/list-threads.js +8 -17
- package/dist/tools/log.js +3 -3
- package/dist/tools/prepare-context.js +7 -10
- package/dist/tools/recall.js +7 -17
- package/dist/tools/record-scar-usage-batch.js +7 -2
- package/dist/tools/record-scar-usage.js +7 -2
- package/dist/tools/search.js +3 -3
- package/dist/tools/session-close.js +105 -81
- package/dist/tools/session-start.js +73 -14
- package/dist/types/index.d.ts +2 -0
- package/package.json +13 -3
- 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
|
package/CLAUDE.md.template
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. **
|
|
55
|
-
|
|
56
|
-
- What
|
|
57
|
-
- What
|
|
58
|
-
- What
|
|
59
|
-
- What
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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": "
|
|
77
|
-
"rapport_notes": "
|
|
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": "
|
|
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
|
-
|
|
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=
|
|
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=
|
|
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%
|
|
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
|
-
|
|
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
|
|
34
|
-
- `.mcp.json
|
|
35
|
-
- `CLAUDE.md
|
|
36
|
-
-
|
|
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
|
|
44
|
-
npx gitmem-mcp init --dry-run
|
|
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` (
|
|
84
|
-
| **
|
|
85
|
-
| **
|
|
86
|
-
| **
|
|
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** |
|
|
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;
|
package/bin/init-wizard.js
CHANGED
|
@@ -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
|
|
91
|
-
console.error(` Error: Unknown client "${clientFlag}". Use --client
|
|
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}
|
|
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
|
-
|
|
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
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
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();
|