gitmem-mcp 1.1.3 → 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 +41 -0
- package/CLAUDE.md.template +29 -20
- package/README.md +4 -4
- package/bin/gitmem.js +33 -0
- package/copilot-instructions.template +33 -14
- 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 +14 -14
- package/dist/server.js +3 -2
- package/dist/services/display-protocol.d.ts +45 -4
- package/dist/services/display-protocol.js +114 -15
- package/dist/services/nudge-variants.d.ts +29 -0
- package/dist/services/nudge-variants.js +71 -0
- package/dist/tools/cleanup-threads.js +3 -3
- package/dist/tools/confirm-scars.js +28 -11
- package/dist/tools/definitions.js +2 -2
- package/dist/tools/list-threads.d.ts +1 -1
- package/dist/tools/list-threads.js +6 -7
- 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/search.js +3 -3
- package/dist/tools/session-close.js +105 -81
- package/dist/tools/session-start.js +32 -17
- package/package.json +1 -1
- package/windsurfrules.template +33 -14
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,51 @@ 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
|
+
|
|
10
29
|
## [1.1.3] - 2026-02-19
|
|
11
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
|
+
|
|
12
47
|
### Fixed
|
|
13
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.
|
|
14
55
|
|
|
15
56
|
## [1.1.2] - 2026-02-17
|
|
16
57
|
|
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">
|
|
@@ -136,7 +136,7 @@ Add this to your MCP client's config file:
|
|
|
136
136
|
| **Session analytics** | Spot patterns in what keeps going wrong |
|
|
137
137
|
| **Sub-agent briefing** | Hand institutional context to sub-agents automatically |
|
|
138
138
|
| **Cloud persistence** | Memory survives machine changes, shareable across team |
|
|
139
|
-
| **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) |
|
|
140
140
|
|
|
141
141
|
The free tier gives you everything for solo projects. Pro makes recall smarter and memory portable.
|
|
142
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;
|
|
@@ -46,18 +46,19 @@ Safe alternatives: `env | grep -c VARNAME` (count only), `[ -n "$VARNAME" ] && e
|
|
|
46
46
|
|
|
47
47
|
**End:** On "closing", "done for now", or "wrapping up":
|
|
48
48
|
|
|
49
|
-
1. **
|
|
50
|
-
|
|
51
|
-
- What
|
|
52
|
-
- What
|
|
53
|
-
- What
|
|
54
|
-
- What
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
1. **Write reflection directly to payload** — do NOT display the full Q&A to the human.
|
|
50
|
+
Internally answer these 9 questions and write them straight to `.gitmem/closing-payload.json`:
|
|
51
|
+
- Q1: What broke that you didn't expect?
|
|
52
|
+
- Q2: What took longer than it should have?
|
|
53
|
+
- Q3: What would you do differently next time?
|
|
54
|
+
- Q4: What pattern or approach worked well?
|
|
55
|
+
- Q5: What assumption was wrong?
|
|
56
|
+
- Q6: Which scars did you apply?
|
|
57
|
+
- Q7: What should be captured as institutional memory?
|
|
58
|
+
- Q8: How did the human prefer to work this session?
|
|
59
|
+
- Q9: What collaborative dynamic worked or didn't?
|
|
60
|
+
|
|
61
|
+
Payload schema (`.gitmem/closing-payload.json`):
|
|
61
62
|
```json
|
|
62
63
|
{
|
|
63
64
|
"closing_reflection": {
|
|
@@ -67,16 +68,34 @@ Safe alternatives: `env | grep -c VARNAME` (count only), `[ -n "$VARNAME" ] && e
|
|
|
67
68
|
"what_worked": "...",
|
|
68
69
|
"wrong_assumption": "...",
|
|
69
70
|
"scars_applied": ["scar title 1", "scar title 2"],
|
|
70
|
-
"institutional_memory_items": "..."
|
|
71
|
+
"institutional_memory_items": "...",
|
|
72
|
+
"collaborative_dynamic": "...",
|
|
73
|
+
"rapport_notes": "..."
|
|
74
|
+
},
|
|
75
|
+
"task_completion": {
|
|
76
|
+
"questions_displayed_at": "ISO timestamp (when reflection started)",
|
|
77
|
+
"reflection_completed_at": "ISO timestamp (when payload written)",
|
|
78
|
+
"human_asked_at": "ISO timestamp (when 'Corrections?' shown)",
|
|
79
|
+
"human_response_at": "ISO timestamp (when human replied)",
|
|
80
|
+
"human_response": "user's correction text or 'none'"
|
|
71
81
|
},
|
|
72
82
|
"human_corrections": "",
|
|
73
83
|
"scars_to_record": [],
|
|
84
|
+
"learnings_created": [],
|
|
74
85
|
"open_threads": [],
|
|
75
86
|
"decisions": []
|
|
76
87
|
}
|
|
77
88
|
```
|
|
78
89
|
|
|
79
|
-
|
|
90
|
+
2. **Show compact summary** (3-4 lines max):
|
|
91
|
+
```
|
|
92
|
+
Session close: [N] learnings captured, [M] scars applied, [K] threads open
|
|
93
|
+
Key lesson: [one-sentence from Q7]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
3. **Ask**: "Corrections?" — wait for response, then call `session_close`.
|
|
97
|
+
|
|
98
|
+
4. **Call `session_close`** with `session_id` and `close_type: "standard"`.
|
|
80
99
|
|
|
81
100
|
For short exploratory sessions (< 30 min, no real work), use `close_type: "quick"` — no questions needed.
|
|
82
101
|
<!-- gitmem:end -->
|
package/cursorrules.template
CHANGED
|
@@ -46,18 +46,19 @@ Safe alternatives: `env | grep -c VARNAME` (count only), `[ -n "$VARNAME" ] && e
|
|
|
46
46
|
|
|
47
47
|
On "closing", "done for now", or "wrapping up":
|
|
48
48
|
|
|
49
|
-
1. **
|
|
50
|
-
|
|
51
|
-
- What
|
|
52
|
-
- What
|
|
53
|
-
- What
|
|
54
|
-
- What
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
1. **Write reflection directly to payload** — do NOT display the full Q&A to the human.
|
|
50
|
+
Internally answer these 9 questions and write them straight to `.gitmem/closing-payload.json`:
|
|
51
|
+
- Q1: What broke that you didn't expect?
|
|
52
|
+
- Q2: What took longer than it should have?
|
|
53
|
+
- Q3: What would you do differently next time?
|
|
54
|
+
- Q4: What pattern or approach worked well?
|
|
55
|
+
- Q5: What assumption was wrong?
|
|
56
|
+
- Q6: Which scars did you apply?
|
|
57
|
+
- Q7: What should be captured as institutional memory?
|
|
58
|
+
- Q8: How did the human prefer to work this session?
|
|
59
|
+
- Q9: What collaborative dynamic worked or didn't?
|
|
60
|
+
|
|
61
|
+
Payload schema (`.gitmem/closing-payload.json`):
|
|
61
62
|
```json
|
|
62
63
|
{
|
|
63
64
|
"closing_reflection": {
|
|
@@ -68,15 +69,15 @@ On "closing", "done for now", or "wrapping up":
|
|
|
68
69
|
"wrong_assumption": "...",
|
|
69
70
|
"scars_applied": ["scar title 1", "scar title 2"],
|
|
70
71
|
"institutional_memory_items": "...",
|
|
71
|
-
"collaborative_dynamic": "
|
|
72
|
-
"rapport_notes": "
|
|
72
|
+
"collaborative_dynamic": "...",
|
|
73
|
+
"rapport_notes": "..."
|
|
73
74
|
},
|
|
74
75
|
"task_completion": {
|
|
75
|
-
"questions_displayed_at": "ISO timestamp",
|
|
76
|
-
"reflection_completed_at": "ISO timestamp",
|
|
77
|
-
"human_asked_at": "ISO timestamp",
|
|
78
|
-
"human_response_at": "ISO timestamp",
|
|
79
|
-
"human_response": "
|
|
76
|
+
"questions_displayed_at": "ISO timestamp (when reflection started)",
|
|
77
|
+
"reflection_completed_at": "ISO timestamp (when payload written)",
|
|
78
|
+
"human_asked_at": "ISO timestamp (when 'Corrections?' shown)",
|
|
79
|
+
"human_response_at": "ISO timestamp (when human replied)",
|
|
80
|
+
"human_response": "user's correction text or 'none'"
|
|
80
81
|
},
|
|
81
82
|
"human_corrections": "",
|
|
82
83
|
"scars_to_record": [],
|
|
@@ -86,7 +87,15 @@ On "closing", "done for now", or "wrapping up":
|
|
|
86
87
|
}
|
|
87
88
|
```
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
2. **Show compact summary** (3-4 lines max):
|
|
91
|
+
```
|
|
92
|
+
Session close: [N] learnings captured, [M] scars applied, [K] threads open
|
|
93
|
+
Key lesson: [one-sentence from Q7]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
3. **Ask**: "Corrections?" — wait for response, then call `session_close`.
|
|
97
|
+
|
|
98
|
+
4. **Call `session_close`** with `session_id` and `close_type: "standard"`.
|
|
90
99
|
|
|
91
100
|
For short exploratory sessions (< 30 min, no real work), use `close_type: "quick"` — no questions needed.
|
|
92
101
|
# --- gitmem:end ---
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI commands for telemetry control
|
|
3
|
+
*
|
|
4
|
+
* gitmem telemetry status
|
|
5
|
+
* gitmem telemetry enable
|
|
6
|
+
* gitmem telemetry disable
|
|
7
|
+
* gitmem telemetry show [--limit N]
|
|
8
|
+
* gitmem telemetry clear
|
|
9
|
+
*/
|
|
10
|
+
export declare function main(args: string[]): void;
|
|
11
|
+
//# sourceMappingURL=telemetry.d.ts.map
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI commands for telemetry control
|
|
3
|
+
*
|
|
4
|
+
* gitmem telemetry status
|
|
5
|
+
* gitmem telemetry enable
|
|
6
|
+
* gitmem telemetry disable
|
|
7
|
+
* gitmem telemetry show [--limit N]
|
|
8
|
+
* gitmem telemetry clear
|
|
9
|
+
*/
|
|
10
|
+
import { getTelemetry, Telemetry } from "../lib/telemetry.js";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { readFileSync } from "fs";
|
|
13
|
+
const VERSION = getPackageVersion();
|
|
14
|
+
function getPackageVersion() {
|
|
15
|
+
try {
|
|
16
|
+
const pkg = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
|
|
17
|
+
return pkg.version || "0.0.0";
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return "0.0.0";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function getGitmemDir() {
|
|
24
|
+
return join(process.cwd(), ".gitmem");
|
|
25
|
+
}
|
|
26
|
+
export function main(args) {
|
|
27
|
+
const subcommand = args[0];
|
|
28
|
+
const gitmemDir = getGitmemDir();
|
|
29
|
+
const telemetry = getTelemetry(gitmemDir, VERSION);
|
|
30
|
+
switch (subcommand) {
|
|
31
|
+
case "status":
|
|
32
|
+
cmdStatus(telemetry);
|
|
33
|
+
break;
|
|
34
|
+
case "enable":
|
|
35
|
+
cmdEnable(telemetry);
|
|
36
|
+
break;
|
|
37
|
+
case "disable":
|
|
38
|
+
cmdDisable(telemetry);
|
|
39
|
+
break;
|
|
40
|
+
case "show":
|
|
41
|
+
cmdShow(telemetry, args);
|
|
42
|
+
break;
|
|
43
|
+
case "clear":
|
|
44
|
+
cmdClear(telemetry);
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
printUsage();
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function cmdStatus(telemetry) {
|
|
52
|
+
const status = telemetry.getStatus();
|
|
53
|
+
console.log("GitMem Telemetry — Status");
|
|
54
|
+
console.log("========================\n");
|
|
55
|
+
if (status.enabled) {
|
|
56
|
+
console.log("Status: \x1b[32mEnabled\x1b[0m");
|
|
57
|
+
console.log(`Session ID: ${status.session_id} (random, not persistent)`);
|
|
58
|
+
console.log(`Events logged: ${status.event_count} (local)`);
|
|
59
|
+
if (status.consented_at) {
|
|
60
|
+
const date = new Date(status.consented_at).toLocaleDateString();
|
|
61
|
+
console.log(`Consented: ${date}`);
|
|
62
|
+
}
|
|
63
|
+
console.log("");
|
|
64
|
+
console.log("What's collected:");
|
|
65
|
+
console.log(" • Tool names (recall, session_close, etc.)");
|
|
66
|
+
console.log(" • Success/failure status");
|
|
67
|
+
console.log(" • Execution duration");
|
|
68
|
+
console.log(" • Result counts (not content)");
|
|
69
|
+
console.log(" • Platform (darwin, linux, win32)");
|
|
70
|
+
console.log(" • Version number");
|
|
71
|
+
console.log("");
|
|
72
|
+
console.log("What's NOT collected:");
|
|
73
|
+
console.log(" ✗ Queries or search terms");
|
|
74
|
+
console.log(" ✗ Scar/learning content");
|
|
75
|
+
console.log(" ✗ Project names or file paths");
|
|
76
|
+
console.log(" ✗ IP addresses or identifiers");
|
|
77
|
+
console.log("");
|
|
78
|
+
console.log("Commands:");
|
|
79
|
+
console.log(" gitmem telemetry show View pending events");
|
|
80
|
+
console.log(" gitmem telemetry disable Turn off telemetry");
|
|
81
|
+
console.log("");
|
|
82
|
+
console.log("Privacy policy: https://gitmem.ai/privacy");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.log("Status: \x1b[33mDisabled\x1b[0m");
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log("No data is being sent.");
|
|
88
|
+
console.log("");
|
|
89
|
+
console.log("To help improve GitMem:");
|
|
90
|
+
console.log(" gitmem telemetry enable");
|
|
91
|
+
console.log("");
|
|
92
|
+
console.log("Privacy policy: https://gitmem.ai/privacy");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function cmdEnable(telemetry) {
|
|
96
|
+
console.log("GitMem Telemetry — Enable");
|
|
97
|
+
console.log("=========================\n");
|
|
98
|
+
console.log("Help improve GitMem by sending anonymous usage data.\n");
|
|
99
|
+
console.log("What we collect:");
|
|
100
|
+
console.log(" ✓ Tool usage patterns (which tools are most useful)");
|
|
101
|
+
console.log(" ✓ Error rates (to prioritize fixes)");
|
|
102
|
+
console.log(" ✓ Performance metrics (duration, platform)");
|
|
103
|
+
console.log("");
|
|
104
|
+
console.log("What we DON'T collect:");
|
|
105
|
+
console.log(" ✗ Your queries or content");
|
|
106
|
+
console.log(" ✗ Scar/learning text");
|
|
107
|
+
console.log(" ✗ Project names or file paths");
|
|
108
|
+
console.log(" ✗ IP addresses or persistent IDs");
|
|
109
|
+
console.log("");
|
|
110
|
+
console.log("Transparency:");
|
|
111
|
+
console.log(" • All events logged to .gitmem/telemetry.log");
|
|
112
|
+
console.log(" • View before sending: gitmem telemetry show");
|
|
113
|
+
console.log(" • Disable anytime: gitmem telemetry disable");
|
|
114
|
+
console.log("");
|
|
115
|
+
console.log("Full privacy policy: https://gitmem.ai/privacy");
|
|
116
|
+
console.log("");
|
|
117
|
+
// Prompt for confirmation (skip if --yes flag)
|
|
118
|
+
if (!process.argv.includes("--yes") && !process.argv.includes("-y")) {
|
|
119
|
+
const readline = require("readline");
|
|
120
|
+
const rl = readline.createInterface({
|
|
121
|
+
input: process.stdin,
|
|
122
|
+
output: process.stdout,
|
|
123
|
+
});
|
|
124
|
+
rl.question("Enable telemetry? [y/N] ", (answer) => {
|
|
125
|
+
rl.close();
|
|
126
|
+
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
127
|
+
telemetry.enable();
|
|
128
|
+
console.log("");
|
|
129
|
+
console.log("\x1b[32m✓\x1b[0m Telemetry enabled");
|
|
130
|
+
console.log(" Data logged to: .gitmem/telemetry.log");
|
|
131
|
+
console.log(" Review anytime: gitmem telemetry show");
|
|
132
|
+
console.log(" Disable anytime: gitmem telemetry disable");
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
console.log("");
|
|
136
|
+
console.log("Telemetry not enabled.");
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
telemetry.enable();
|
|
142
|
+
console.log("\x1b[32m✓\x1b[0m Telemetry enabled (--yes flag)");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function cmdDisable(telemetry) {
|
|
146
|
+
telemetry.disable();
|
|
147
|
+
console.log("GitMem Telemetry — Disable");
|
|
148
|
+
console.log("==========================\n");
|
|
149
|
+
console.log("\x1b[32m✓\x1b[0m Telemetry disabled");
|
|
150
|
+
console.log(" Pending events: will not be sent");
|
|
151
|
+
console.log(" Local logs: preserved at .gitmem/telemetry.log");
|
|
152
|
+
console.log("");
|
|
153
|
+
console.log("To re-enable: gitmem telemetry enable");
|
|
154
|
+
}
|
|
155
|
+
function cmdShow(telemetry, args) {
|
|
156
|
+
const limitIdx = args.indexOf("--limit");
|
|
157
|
+
const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 100;
|
|
158
|
+
const events = telemetry.getRecentEvents(limit);
|
|
159
|
+
console.log(`GitMem Telemetry — Recent Events (last ${Math.min(limit, events.length)})`);
|
|
160
|
+
console.log("=".repeat(60));
|
|
161
|
+
console.log("");
|
|
162
|
+
if (events.length === 0) {
|
|
163
|
+
console.log("No events logged yet.");
|
|
164
|
+
console.log("");
|
|
165
|
+
console.log("Events are logged automatically as you use gitmem tools.");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
for (const eventJson of events) {
|
|
169
|
+
console.log(Telemetry.formatEvent(eventJson));
|
|
170
|
+
}
|
|
171
|
+
console.log("");
|
|
172
|
+
console.log(`Total events: ${events.length}`);
|
|
173
|
+
console.log(`Status: ${telemetry.isEnabled() ? "\x1b[32mEnabled\x1b[0m (will be sent)" : "\x1b[33mDisabled\x1b[0m (logged only)"}`);
|
|
174
|
+
}
|
|
175
|
+
function cmdClear(telemetry) {
|
|
176
|
+
const status = telemetry.getStatus();
|
|
177
|
+
const count = status.event_count;
|
|
178
|
+
telemetry.clearLog();
|
|
179
|
+
console.log("GitMem Telemetry — Clear");
|
|
180
|
+
console.log("========================\n");
|
|
181
|
+
console.log("\x1b[32m✓\x1b[0m Cleared all local telemetry logs");
|
|
182
|
+
console.log(` Events removed: ${count}`);
|
|
183
|
+
console.log("");
|
|
184
|
+
console.log("Note: Remote data (already sent) cannot be deleted.");
|
|
185
|
+
console.log(" It's already anonymous and not linked to you.");
|
|
186
|
+
}
|
|
187
|
+
function printUsage() {
|
|
188
|
+
console.log(`
|
|
189
|
+
GitMem Telemetry — Control
|
|
190
|
+
|
|
191
|
+
Usage:
|
|
192
|
+
gitmem telemetry status Show current status
|
|
193
|
+
gitmem telemetry enable Enable anonymous usage tracking
|
|
194
|
+
gitmem telemetry disable Disable usage tracking
|
|
195
|
+
gitmem telemetry show [--limit N] View recent events (default: 100)
|
|
196
|
+
gitmem telemetry clear Clear local event log
|
|
197
|
+
|
|
198
|
+
Privacy:
|
|
199
|
+
• Opt-in only (disabled by default)
|
|
200
|
+
• No PII (queries, scars, project names, IPs)
|
|
201
|
+
• Transparent (local logs before sending)
|
|
202
|
+
• Anonymous (random session IDs, not persistent)
|
|
203
|
+
|
|
204
|
+
Full policy: https://gitmem.ai/privacy
|
|
205
|
+
`);
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -23,6 +23,7 @@ export interface FormattableScar {
|
|
|
23
23
|
action_protocol?: string[];
|
|
24
24
|
self_check_criteria?: string[];
|
|
25
25
|
}
|
|
26
|
+
/** Text severity indicators — no emoji (column width is unpredictable across terminals) */
|
|
26
27
|
export declare const SEVERITY_EMOJI: Record<string, string>;
|
|
27
28
|
export declare const SEVERITY_LABEL: Record<string, string>;
|
|
28
29
|
export declare const SEVERITY_ORDER: Record<string, number>;
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* - quick-retrieve.ts (hook-invoked retrieval for auto-inject)
|
|
7
7
|
*/
|
|
8
8
|
// --- Severity Constants ---
|
|
9
|
+
/** Text severity indicators — no emoji (column width is unpredictable across terminals) */
|
|
9
10
|
export const SEVERITY_EMOJI = {
|
|
10
|
-
critical: "
|
|
11
|
-
high: "
|
|
12
|
-
medium: "
|
|
13
|
-
low: "
|
|
11
|
+
critical: "[!!]",
|
|
12
|
+
high: "[!]",
|
|
13
|
+
medium: "[~]",
|
|
14
|
+
low: "[-]",
|
|
14
15
|
};
|
|
15
16
|
export const SEVERITY_LABEL = {
|
|
16
17
|
critical: "CRITICAL",
|
|
@@ -44,7 +45,7 @@ export function formatCompact(scars, plan, maxTokens) {
|
|
|
44
45
|
const lines = [header];
|
|
45
46
|
let included = 0;
|
|
46
47
|
for (const scar of sorted) {
|
|
47
|
-
const emoji = SEVERITY_EMOJI[scar.severity] || "
|
|
48
|
+
const emoji = SEVERITY_EMOJI[scar.severity] || "[?]";
|
|
48
49
|
const label = SEVERITY_LABEL[scar.severity] || "UNKNOWN";
|
|
49
50
|
const firstSentence = scar.description.split(/\.\s/)[0].slice(0, 120);
|
|
50
51
|
const line = `${emoji} ${label}: ${scar.title} \u2014 ${firstSentence}`;
|
|
@@ -76,7 +77,7 @@ export function formatGate(scars) {
|
|
|
76
77
|
];
|
|
77
78
|
for (const scar of blockingScars) {
|
|
78
79
|
const rv = scar.required_verification;
|
|
79
|
-
lines.push(
|
|
80
|
+
lines.push(`[!!] BLOCK: ${rv.when}`);
|
|
80
81
|
if (rv.queries && rv.queries.length > 0) {
|
|
81
82
|
for (const query of rv.queries) {
|
|
82
83
|
lines.push(` RUN: ${query}`);
|