gitmem-mcp 1.0.2 → 1.0.4

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 (299) hide show
  1. package/CHANGELOG.md +20 -6
  2. package/README.md +30 -9
  3. package/dist/commands/check.d.ts +1 -1
  4. package/dist/commands/check.js +4 -3
  5. package/dist/diagnostics/anonymizer.d.ts +1 -1
  6. package/dist/diagnostics/anonymizer.js +1 -1
  7. package/dist/diagnostics/channels.d.ts +1 -1
  8. package/dist/diagnostics/channels.js +1 -1
  9. package/dist/diagnostics/collector.d.ts +1 -1
  10. package/dist/diagnostics/collector.js +1 -1
  11. package/dist/diagnostics/index.d.ts +1 -1
  12. package/dist/diagnostics/index.js +1 -1
  13. package/dist/hooks/quick-retrieve.js +2 -1
  14. package/dist/index.js +0 -0
  15. package/dist/schemas/active-sessions.d.ts +9 -9
  16. package/dist/schemas/active-sessions.js +1 -1
  17. package/dist/schemas/common.d.ts +2 -5
  18. package/dist/schemas/common.js +13 -7
  19. package/dist/schemas/create-learning.d.ts +4 -4
  20. package/dist/schemas/create-learning.js +1 -1
  21. package/dist/schemas/session-close.d.ts +5 -8
  22. package/dist/schemas/session-close.js +7 -3
  23. package/dist/schemas/session-start.d.ts +4 -4
  24. package/dist/schemas/session-start.js +1 -1
  25. package/dist/schemas/thread.d.ts +1 -1
  26. package/dist/schemas/thread.js +1 -1
  27. package/dist/server.d.ts +2 -2
  28. package/dist/server.js +21 -10
  29. package/dist/services/active-sessions.js +3 -2
  30. package/dist/services/agent-briefing.d.ts +12 -0
  31. package/dist/services/agent-briefing.js +81 -0
  32. package/dist/services/agent-detection.d.ts +2 -1
  33. package/dist/services/agent-detection.js +23 -14
  34. package/dist/services/analytics.d.ts +1 -1
  35. package/dist/services/analytics.js +3 -3
  36. package/dist/services/behavioral-decay.js +2 -2
  37. package/dist/services/cache.d.ts +1 -1
  38. package/dist/services/cache.js +1 -1
  39. package/dist/services/compliance-validator.d.ts +1 -1
  40. package/dist/services/compliance-validator.js +5 -5
  41. package/dist/services/config.d.ts +1 -1
  42. package/dist/services/config.js +1 -1
  43. package/dist/services/file-lock.js +12 -0
  44. package/dist/services/gitmem-dir.d.ts +17 -4
  45. package/dist/services/gitmem-dir.js +43 -9
  46. package/dist/services/local-file-storage.d.ts +1 -1
  47. package/dist/services/local-file-storage.js +16 -10
  48. package/dist/services/local-vector-search.d.ts +1 -1
  49. package/dist/services/local-vector-search.js +2 -2
  50. package/dist/services/metrics.d.ts +6 -6
  51. package/dist/services/metrics.js +8 -8
  52. package/dist/services/session-state.d.ts +7 -7
  53. package/dist/services/session-state.js +19 -7
  54. package/dist/services/startup.d.ts +1 -1
  55. package/dist/services/startup.js +3 -2
  56. package/dist/services/supabase-client.d.ts +17 -6
  57. package/dist/services/supabase-client.js +44 -8
  58. package/dist/services/thread-manager.d.ts +1 -1
  59. package/dist/services/thread-manager.js +5 -5
  60. package/dist/services/thread-supabase.d.ts +1 -1
  61. package/dist/services/thread-supabase.js +2 -2
  62. package/dist/services/tier.d.ts +2 -0
  63. package/dist/services/tier.js +4 -0
  64. package/dist/services/transcript-chunker.d.ts +1 -1
  65. package/dist/services/transcript-chunker.js +1 -1
  66. package/dist/services/triple-writer.d.ts +4 -4
  67. package/dist/services/triple-writer.js +11 -20
  68. package/dist/services/variant-assignment.d.ts +6 -6
  69. package/dist/services/variant-assignment.js +9 -8
  70. package/dist/tools/analyze.d.ts +2 -2
  71. package/dist/tools/analyze.js +2 -2
  72. package/dist/tools/archive-learning.js +36 -22
  73. package/dist/tools/confirm-scars.js +4 -0
  74. package/dist/tools/create-decision.d.ts +1 -1
  75. package/dist/tools/create-decision.js +2 -2
  76. package/dist/tools/create-learning.d.ts +1 -1
  77. package/dist/tools/create-learning.js +4 -4
  78. package/dist/tools/create-linear-issue.d.ts +18 -0
  79. package/dist/tools/create-linear-issue.js +197 -0
  80. package/dist/tools/create-thread.d.ts +1 -1
  81. package/dist/tools/create-thread.js +2 -2
  82. package/dist/tools/definitions.d.ts +280 -58
  83. package/dist/tools/definitions.js +127 -87
  84. package/dist/tools/get-transcript.d.ts +1 -1
  85. package/dist/tools/get-transcript.js +1 -1
  86. package/dist/tools/graph-traverse.d.ts +1 -1
  87. package/dist/tools/graph-traverse.js +20 -17
  88. package/dist/tools/list-threads.d.ts +2 -2
  89. package/dist/tools/list-threads.js +4 -4
  90. package/dist/tools/log.d.ts +1 -1
  91. package/dist/tools/log.js +1 -1
  92. package/dist/tools/prepare-context.d.ts +1 -1
  93. package/dist/tools/prepare-context.js +2 -2
  94. package/dist/tools/recall.d.ts +5 -4
  95. package/dist/tools/recall.js +37 -28
  96. package/dist/tools/record-scar-usage-batch.js +2 -2
  97. package/dist/tools/record-scar-usage.d.ts +1 -1
  98. package/dist/tools/record-scar-usage.js +3 -3
  99. package/dist/tools/resolve-thread.d.ts +2 -2
  100. package/dist/tools/resolve-thread.js +3 -3
  101. package/dist/tools/save-transcript.d.ts +1 -1
  102. package/dist/tools/save-transcript.js +1 -1
  103. package/dist/tools/search.d.ts +1 -1
  104. package/dist/tools/search.js +1 -1
  105. package/dist/tools/session-close.d.ts +1 -1
  106. package/dist/tools/session-close.js +58 -57
  107. package/dist/tools/session-start.d.ts +5 -5
  108. package/dist/tools/session-start.js +63 -61
  109. package/dist/types/index.d.ts +17 -13
  110. package/hooks/.claude-plugin/plugin.json +1 -1
  111. package/hooks/scripts/post-tool-use.sh +1 -1
  112. package/hooks/scripts/recall-check.sh +1 -1
  113. package/hooks/scripts/session-close-check.sh +1 -1
  114. package/hooks/scripts/session-start.sh +1 -1
  115. package/package.json +6 -3
  116. package/schema/starter-scars.json +3 -153
  117. package/dist/commands/check.d.ts.map +0 -1
  118. package/dist/commands/check.js.map +0 -1
  119. package/dist/constants/closing-questions.d.ts.map +0 -1
  120. package/dist/constants/closing-questions.js.map +0 -1
  121. package/dist/diagnostics/anonymizer.d.ts.map +0 -1
  122. package/dist/diagnostics/anonymizer.js.map +0 -1
  123. package/dist/diagnostics/channels.d.ts.map +0 -1
  124. package/dist/diagnostics/channels.js.map +0 -1
  125. package/dist/diagnostics/collector.d.ts.map +0 -1
  126. package/dist/diagnostics/collector.js.map +0 -1
  127. package/dist/diagnostics/index.d.ts.map +0 -1
  128. package/dist/diagnostics/index.js.map +0 -1
  129. package/dist/hooks/format-utils.d.ts.map +0 -1
  130. package/dist/hooks/format-utils.js.map +0 -1
  131. package/dist/hooks/quick-retrieve.d.ts.map +0 -1
  132. package/dist/hooks/quick-retrieve.js.map +0 -1
  133. package/dist/index.d.ts.map +0 -1
  134. package/dist/index.js.map +0 -1
  135. package/dist/schemas/absorb-observations.d.ts.map +0 -1
  136. package/dist/schemas/absorb-observations.js.map +0 -1
  137. package/dist/schemas/active-sessions.d.ts.map +0 -1
  138. package/dist/schemas/active-sessions.js.map +0 -1
  139. package/dist/schemas/analyze.d.ts.map +0 -1
  140. package/dist/schemas/analyze.js.map +0 -1
  141. package/dist/schemas/common.d.ts.map +0 -1
  142. package/dist/schemas/common.js.map +0 -1
  143. package/dist/schemas/create-decision.d.ts.map +0 -1
  144. package/dist/schemas/create-decision.js.map +0 -1
  145. package/dist/schemas/create-learning.d.ts.map +0 -1
  146. package/dist/schemas/create-learning.js.map +0 -1
  147. package/dist/schemas/get-transcript.d.ts.map +0 -1
  148. package/dist/schemas/get-transcript.js.map +0 -1
  149. package/dist/schemas/index.d.ts.map +0 -1
  150. package/dist/schemas/index.js.map +0 -1
  151. package/dist/schemas/log.d.ts.map +0 -1
  152. package/dist/schemas/log.js.map +0 -1
  153. package/dist/schemas/prepare-context.d.ts.map +0 -1
  154. package/dist/schemas/prepare-context.js.map +0 -1
  155. package/dist/schemas/recall.d.ts.map +0 -1
  156. package/dist/schemas/recall.js.map +0 -1
  157. package/dist/schemas/record-scar-usage-batch.d.ts.map +0 -1
  158. package/dist/schemas/record-scar-usage-batch.js.map +0 -1
  159. package/dist/schemas/record-scar-usage.d.ts.map +0 -1
  160. package/dist/schemas/record-scar-usage.js.map +0 -1
  161. package/dist/schemas/registry.d.ts.map +0 -1
  162. package/dist/schemas/registry.js.map +0 -1
  163. package/dist/schemas/save-transcript.d.ts.map +0 -1
  164. package/dist/schemas/save-transcript.js.map +0 -1
  165. package/dist/schemas/search-transcripts.d.ts.map +0 -1
  166. package/dist/schemas/search-transcripts.js.map +0 -1
  167. package/dist/schemas/search.d.ts.map +0 -1
  168. package/dist/schemas/search.js.map +0 -1
  169. package/dist/schemas/session-close.d.ts.map +0 -1
  170. package/dist/schemas/session-close.js.map +0 -1
  171. package/dist/schemas/session-start.d.ts.map +0 -1
  172. package/dist/schemas/session-start.js.map +0 -1
  173. package/dist/schemas/thread.d.ts.map +0 -1
  174. package/dist/schemas/thread.js.map +0 -1
  175. package/dist/server.d.ts.map +0 -1
  176. package/dist/server.js.map +0 -1
  177. package/dist/services/active-sessions.d.ts.map +0 -1
  178. package/dist/services/active-sessions.js.map +0 -1
  179. package/dist/services/agent-detection.d.ts.map +0 -1
  180. package/dist/services/agent-detection.js.map +0 -1
  181. package/dist/services/analytics.d.ts.map +0 -1
  182. package/dist/services/analytics.js.map +0 -1
  183. package/dist/services/behavioral-decay.d.ts.map +0 -1
  184. package/dist/services/behavioral-decay.js.map +0 -1
  185. package/dist/services/bm25.d.ts.map +0 -1
  186. package/dist/services/bm25.js.map +0 -1
  187. package/dist/services/cache.d.ts.map +0 -1
  188. package/dist/services/cache.js.map +0 -1
  189. package/dist/services/cache.test.d.ts +0 -8
  190. package/dist/services/cache.test.d.ts.map +0 -1
  191. package/dist/services/cache.test.js +0 -267
  192. package/dist/services/cache.test.js.map +0 -1
  193. package/dist/services/compliance-validator.d.ts.map +0 -1
  194. package/dist/services/compliance-validator.js.map +0 -1
  195. package/dist/services/config.d.ts.map +0 -1
  196. package/dist/services/config.js.map +0 -1
  197. package/dist/services/display-protocol.d.ts.map +0 -1
  198. package/dist/services/display-protocol.js.map +0 -1
  199. package/dist/services/effect-tracker.d.ts.map +0 -1
  200. package/dist/services/effect-tracker.js.map +0 -1
  201. package/dist/services/embedding.d.ts.map +0 -1
  202. package/dist/services/embedding.js.map +0 -1
  203. package/dist/services/file-lock.d.ts.map +0 -1
  204. package/dist/services/file-lock.js.map +0 -1
  205. package/dist/services/gitmem-dir.d.ts.map +0 -1
  206. package/dist/services/gitmem-dir.js.map +0 -1
  207. package/dist/services/local-file-storage.d.ts.map +0 -1
  208. package/dist/services/local-file-storage.js.map +0 -1
  209. package/dist/services/local-vector-search.d.ts.map +0 -1
  210. package/dist/services/local-vector-search.js.map +0 -1
  211. package/dist/services/metrics.d.ts.map +0 -1
  212. package/dist/services/metrics.js.map +0 -1
  213. package/dist/services/session-state.d.ts.map +0 -1
  214. package/dist/services/session-state.js.map +0 -1
  215. package/dist/services/startup.d.ts.map +0 -1
  216. package/dist/services/startup.js.map +0 -1
  217. package/dist/services/storage.d.ts.map +0 -1
  218. package/dist/services/storage.js.map +0 -1
  219. package/dist/services/supabase-client.d.ts.map +0 -1
  220. package/dist/services/supabase-client.js.map +0 -1
  221. package/dist/services/thread-dedup.d.ts.map +0 -1
  222. package/dist/services/thread-dedup.js.map +0 -1
  223. package/dist/services/thread-manager.d.ts.map +0 -1
  224. package/dist/services/thread-manager.js.map +0 -1
  225. package/dist/services/thread-suggestions.d.ts.map +0 -1
  226. package/dist/services/thread-suggestions.js.map +0 -1
  227. package/dist/services/thread-supabase.d.ts.map +0 -1
  228. package/dist/services/thread-supabase.js.map +0 -1
  229. package/dist/services/thread-vitality.d.ts.map +0 -1
  230. package/dist/services/thread-vitality.js.map +0 -1
  231. package/dist/services/tier.d.ts.map +0 -1
  232. package/dist/services/tier.js.map +0 -1
  233. package/dist/services/timezone.d.ts.map +0 -1
  234. package/dist/services/timezone.js.map +0 -1
  235. package/dist/services/transcript-chunker.d.ts.map +0 -1
  236. package/dist/services/transcript-chunker.js.map +0 -1
  237. package/dist/services/triple-writer.d.ts.map +0 -1
  238. package/dist/services/triple-writer.js.map +0 -1
  239. package/dist/services/variant-assignment.d.ts.map +0 -1
  240. package/dist/services/variant-assignment.js.map +0 -1
  241. package/dist/services/variant-generation.d.ts.map +0 -1
  242. package/dist/services/variant-generation.js.map +0 -1
  243. package/dist/tools/absorb-observations.d.ts.map +0 -1
  244. package/dist/tools/absorb-observations.js.map +0 -1
  245. package/dist/tools/analyze.d.ts.map +0 -1
  246. package/dist/tools/analyze.js.map +0 -1
  247. package/dist/tools/archive-learning.d.ts.map +0 -1
  248. package/dist/tools/archive-learning.js.map +0 -1
  249. package/dist/tools/cleanup-threads.d.ts.map +0 -1
  250. package/dist/tools/cleanup-threads.js.map +0 -1
  251. package/dist/tools/confirm-scars.d.ts.map +0 -1
  252. package/dist/tools/confirm-scars.js.map +0 -1
  253. package/dist/tools/create-decision.d.ts.map +0 -1
  254. package/dist/tools/create-decision.js.map +0 -1
  255. package/dist/tools/create-learning.d.ts.map +0 -1
  256. package/dist/tools/create-learning.js.map +0 -1
  257. package/dist/tools/create-thread.d.ts.map +0 -1
  258. package/dist/tools/create-thread.js.map +0 -1
  259. package/dist/tools/definitions.d.ts.map +0 -1
  260. package/dist/tools/definitions.js.map +0 -1
  261. package/dist/tools/dismiss-suggestion.d.ts.map +0 -1
  262. package/dist/tools/dismiss-suggestion.js.map +0 -1
  263. package/dist/tools/get-transcript.d.ts.map +0 -1
  264. package/dist/tools/get-transcript.js.map +0 -1
  265. package/dist/tools/graph-traverse.d.ts.map +0 -1
  266. package/dist/tools/graph-traverse.js.map +0 -1
  267. package/dist/tools/list-threads.d.ts.map +0 -1
  268. package/dist/tools/list-threads.js.map +0 -1
  269. package/dist/tools/log.d.ts.map +0 -1
  270. package/dist/tools/log.js.map +0 -1
  271. package/dist/tools/prepare-context.d.ts.map +0 -1
  272. package/dist/tools/prepare-context.js.map +0 -1
  273. package/dist/tools/promote-suggestion.d.ts.map +0 -1
  274. package/dist/tools/promote-suggestion.js.map +0 -1
  275. package/dist/tools/recall.d.ts.map +0 -1
  276. package/dist/tools/recall.js.map +0 -1
  277. package/dist/tools/recall.test.d.ts +0 -5
  278. package/dist/tools/recall.test.d.ts.map +0 -1
  279. package/dist/tools/recall.test.js +0 -155
  280. package/dist/tools/recall.test.js.map +0 -1
  281. package/dist/tools/record-scar-usage-batch.d.ts.map +0 -1
  282. package/dist/tools/record-scar-usage-batch.js.map +0 -1
  283. package/dist/tools/record-scar-usage.d.ts.map +0 -1
  284. package/dist/tools/record-scar-usage.js.map +0 -1
  285. package/dist/tools/resolve-thread.d.ts.map +0 -1
  286. package/dist/tools/resolve-thread.js.map +0 -1
  287. package/dist/tools/save-transcript.d.ts.map +0 -1
  288. package/dist/tools/save-transcript.js.map +0 -1
  289. package/dist/tools/search-transcripts.d.ts.map +0 -1
  290. package/dist/tools/search-transcripts.js.map +0 -1
  291. package/dist/tools/search.d.ts.map +0 -1
  292. package/dist/tools/search.js.map +0 -1
  293. package/dist/tools/session-close.d.ts.map +0 -1
  294. package/dist/tools/session-close.js.map +0 -1
  295. package/dist/tools/session-start.d.ts.map +0 -1
  296. package/dist/tools/session-start.js.map +0 -1
  297. package/dist/types/index.d.ts.map +0 -1
  298. package/dist/types/index.js.map +0 -1
  299. package/hooks/tests/test-hooks.sh +0 -577
package/dist/server.js CHANGED
@@ -43,7 +43,7 @@ import { validateToolArgs } from "./schemas/registry.js";
43
43
  export function createServer() {
44
44
  const server = new Server({
45
45
  name: "gitmem-mcp",
46
- version: "0.1.0",
46
+ version: "1.0.3",
47
47
  }, {
48
48
  capabilities: {
49
49
  tools: {},
@@ -207,6 +207,7 @@ export function createServer() {
207
207
  { alias: "gitmem-r", full: "recall", description: "Check scars before taking action" },
208
208
  { alias: "gitmem-cs", full: "confirm_scars", description: "Confirm recalled scars (APPLYING/N_A/REFUTED)" },
209
209
  { alias: "gitmem-ss", full: "session_start", description: "Initialize session with context" },
210
+ { alias: "gitmem-sr", full: "session_refresh", description: "Refresh context for active session" },
210
211
  { alias: "gitmem-sc", full: "session_close", description: "Close session with compliance validation" },
211
212
  { alias: "gitmem-cl", full: "create_learning", description: "Create scar/win/pattern entry" },
212
213
  { alias: "gitmem-cd", full: "create_decision", description: "Log architectural/operational decision" },
@@ -223,6 +224,7 @@ export function createServer() {
223
224
  { alias: "gitmem-cleanup", full: "cleanup_threads", description: "Triage threads by lifecycle health" },
224
225
  { alias: "gitmem-health", full: "health", description: "Show write health for fire-and-forget operations" },
225
226
  { alias: "gitmem-al", full: "archive_learning", description: "Archive a scar/win/pattern (is_active=false)" },
227
+ { alias: "gitmem-graph", full: "graph_traverse", description: "Traverse knowledge graph over institutional memory" },
226
228
  ];
227
229
  if (hasBatchOperations()) {
228
230
  commands.push({ alias: "gitmem-rsb", full: "record_scar_usage_batch", description: "Track multiple scars (batch)" });
@@ -233,10 +235,12 @@ export function createServer() {
233
235
  if (hasCacheManagement()) {
234
236
  commands.push({ alias: "gitmem-cache-status", full: "cache_status", description: "Show cache status" }, { alias: "gitmem-cache-health", full: "cache_health", description: "Compare local vs remote" }, { alias: "gitmem-cache-flush", full: "cache_flush", description: "Force reload from Supabase" });
235
237
  }
238
+ // Filter to only show commands whose canonical tool is actually registered
239
+ const visibleCommands = commands.filter(c => registeredToolNames.has(c.full));
236
240
  // Build command table for display
237
- const cmdLines = commands.map(c => ` ${c.alias.padEnd(22)} ${c.description}`).join("\n");
241
+ const cmdLines = visibleCommands.map(c => ` ${c.alias.padEnd(22)} ${c.description}`).join("\n");
238
242
  const display = [
239
- `gitmem v0.1.0 · ${tier} · ${registeredTools.length} tools · ${hasSupabase() ? "supabase" : "local (.gitmem/)"}`,
243
+ `gitmem v1.0.3 · ${tier} · ${registeredTools.length} tools · ${hasSupabase() ? "supabase" : "local (.gitmem/)"}`,
240
244
  "Memory that compounds.",
241
245
  "",
242
246
  cmdLines,
@@ -248,11 +252,11 @@ export function createServer() {
248
252
  "Tool results are collapsed in the CLI — the user cannot see them unless you echo them.",
249
253
  ].join("\n");
250
254
  result = {
251
- version: "0.1.0",
255
+ version: "1.0.3",
252
256
  tier,
253
257
  tools_registered: registeredTools.length,
254
258
  storage: hasSupabase() ? "supabase" : "local (.gitmem/)",
255
- commands,
259
+ commands: visibleCommands,
256
260
  display,
257
261
  };
258
262
  break;
@@ -277,7 +281,7 @@ export function createServer() {
277
281
  case "gm-graph":
278
282
  result = await graphTraverse(toolArgs);
279
283
  break;
280
- // Cache management tools (OD-473)
284
+ // Cache management tools
281
285
  case "gitmem-cache-status":
282
286
  case "gm-cache-s":
283
287
  result = getCacheStatus(toolArgs.project || getProject() || "default");
@@ -319,12 +323,19 @@ export function createServer() {
319
323
  };
320
324
  }
321
325
  catch (error) {
322
- const message = error instanceof Error ? error.message : String(error);
326
+ const rawMessage = error instanceof Error ? error.message : String(error);
327
+ // Redact internal details: file paths, SQL errors, stack traces
328
+ const safeMessage = rawMessage
329
+ .replace(/\/[^\s:]+/g, "[path]") // redact file paths
330
+ .replace(/\b\d{5}\b/g, "[code]") // redact PG error codes
331
+ .replace(/at\s+\S+\s+\(.+\)/g, "") // strip stack frames
332
+ .slice(0, 200); // cap length
333
+ console.error(`[server] Tool error:`, rawMessage);
323
334
  return {
324
335
  content: [
325
336
  {
326
337
  type: "text",
327
- text: JSON.stringify({ error: message }),
338
+ text: JSON.stringify({ error: safeMessage }),
328
339
  },
329
340
  ],
330
341
  isError: true,
@@ -336,8 +347,8 @@ export function createServer() {
336
347
  /**
337
348
  * Run the server with stdio transport
338
349
  *
339
- * OD-473: Initializes local vector search in background for fast startup.
340
- * OD-489: Uses direct Supabase queries to get embeddings for local cache.
350
+ * Initializes local vector search in background for fast startup.
351
+ * Uses direct Supabase queries to get embeddings for local cache.
341
352
  *
342
353
  * Server starts immediately; cache loads in background.
343
354
  * First few queries may use Supabase fallback until cache is ready.
@@ -13,7 +13,7 @@
13
13
  import * as fs from "fs";
14
14
  import * as path from "path";
15
15
  import * as os from "os";
16
- import { getGitmemDir, getSessionPath } from "./gitmem-dir.js";
16
+ import { getGitmemDir, getSessionPath, sanitizePathComponent } from "./gitmem-dir.js";
17
17
  import { ActiveSessionsRegistrySchema } from "../schemas/active-sessions.js";
18
18
  import { withLockSync } from "./file-lock.js";
19
19
  const REGISTRY_FILENAME = "active-sessions.json";
@@ -276,6 +276,7 @@ function pruneOrphanedDirs(gitmemDir, registry) {
276
276
  */
277
277
  function cleanupSessionDir(gitmemDir, sessionId) {
278
278
  try {
279
+ sanitizePathComponent(sessionId, "sessionId");
279
280
  const sessionDir = path.join(gitmemDir, "sessions", sessionId);
280
281
  if (fs.existsSync(sessionDir)) {
281
282
  fs.rmSync(sessionDir, { recursive: true, force: true });
@@ -328,7 +329,7 @@ export function migrateFromLegacy() {
328
329
  // 2. Create registry with single entry
329
330
  const entry = {
330
331
  session_id: old.session_id,
331
- agent: old.agent || "CLI",
332
+ agent: old.agent || "cli",
332
333
  started_at: old.started_at || new Date().toISOString(),
333
334
  hostname: old.hostname || os.hostname(),
334
335
  pid: old.pid || process.pid,
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Agent Briefing Generator
3
+ *
4
+ * Generates `.gitmem/agent-briefing.md` at session close with a
5
+ * compact summary of memory state. Designed for inclusion in
6
+ * MEMORY.md to bridge PMEM <-> GitMem.
7
+ */
8
+ /**
9
+ * Generate and write agent-briefing.md to .gitmem/
10
+ */
11
+ export declare function writeAgentBriefing(newLearnings: number, newDecisions: number): Promise<void>;
12
+ //# sourceMappingURL=agent-briefing.d.ts.map
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Agent Briefing Generator
3
+ *
4
+ * Generates `.gitmem/agent-briefing.md` at session close with a
5
+ * compact summary of memory state. Designed for inclusion in
6
+ * MEMORY.md to bridge PMEM <-> GitMem.
7
+ */
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import { getGitmemDir } from "./gitmem-dir.js";
11
+ import { getStorage } from "./storage.js";
12
+ /**
13
+ * Generate and write agent-briefing.md to .gitmem/
14
+ */
15
+ export async function writeAgentBriefing(newLearnings, newDecisions) {
16
+ try {
17
+ const gitmemDir = getGitmemDir();
18
+ if (!gitmemDir || !fs.existsSync(gitmemDir))
19
+ return;
20
+ const storage = getStorage();
21
+ const sessions = await storage.query("sessions");
22
+ const learnings = await storage.query("learnings");
23
+ const threads = await storage.query("threads");
24
+ // Count by severity
25
+ const severityCounts = {};
26
+ const domainCounts = {};
27
+ for (const l of learnings) {
28
+ const sev = l.severity || "medium";
29
+ severityCounts[sev] = (severityCounts[sev] || 0) + 1;
30
+ const domains = l.domain || [];
31
+ for (const d of domains) {
32
+ domainCounts[d] = (domainCounts[d] || 0) + 1;
33
+ }
34
+ }
35
+ // Active threads
36
+ const activeThreads = threads.filter((t) => t.status === "open").length;
37
+ // Top domains (top 5)
38
+ const topDomains = Object.entries(domainCounts)
39
+ .sort((a, b) => b[1] - a[1])
40
+ .slice(0, 5)
41
+ .map(([d]) => d);
42
+ // Severity breakdown
43
+ const sevParts = [];
44
+ if (severityCounts.critical)
45
+ sevParts.push(`${severityCounts.critical} critical`);
46
+ if (severityCounts.high)
47
+ sevParts.push(`${severityCounts.high} high`);
48
+ if (severityCounts.medium)
49
+ sevParts.push(`${severityCounts.medium} medium`);
50
+ if (severityCounts.low)
51
+ sevParts.push(`${severityCounts.low} low`);
52
+ // Recent activity
53
+ const recentParts = [];
54
+ if (newLearnings > 0)
55
+ recentParts.push(`+${newLearnings} scars`);
56
+ if (newDecisions > 0)
57
+ recentParts.push(`+${newDecisions} decisions`);
58
+ const today = new Date().toISOString().split("T")[0];
59
+ const lines = [
60
+ "<!-- Generated by GitMem at session close. Include in MEMORY.md for cross-session context. -->",
61
+ "## GitMem Status",
62
+ `- **Sessions:** ${sessions.length} total, last: ${today}`,
63
+ `- **Active threads:** ${activeThreads}`,
64
+ `- **Scars earned:** ${learnings.length}${sevParts.length > 0 ? ` (${sevParts.join(", ")})` : ""}`,
65
+ ];
66
+ if (recentParts.length > 0) {
67
+ lines.push(`- **Recent:** ${recentParts.join(", ")} this session`);
68
+ }
69
+ if (topDomains.length > 0) {
70
+ lines.push(`- **Top domains:** ${topDomains.join(", ")}`);
71
+ }
72
+ const briefingPath = path.join(gitmemDir, "agent-briefing.md");
73
+ fs.writeFileSync(briefingPath, lines.join("\n") + "\n");
74
+ console.error(`[agent-briefing] Written to ${briefingPath}`);
75
+ }
76
+ catch (error) {
77
+ // Non-fatal — don't break session close
78
+ console.warn("[agent-briefing] Failed to write:", error);
79
+ }
80
+ }
81
+ //# sourceMappingURL=agent-briefing.js.map
@@ -8,7 +8,7 @@
8
8
  * | ENTRYPOINT | Docker | Hostname | Identity |
9
9
  * |------------|--------|----------|----------|
10
10
  * | cli | YES | (any) | CLI |
11
- * | cli | NO | orchestra.nteg.com | CODA-1 |
11
+ * | cli | NO | (server hostname) | CODA-1 |
12
12
  * | claude-desktop | NO | (any) | DAC |
13
13
  * | (empty) | NO | (local) | Brain_Local |
14
14
  * | (empty) | NO | (no fs) | Brain_Cloud |
@@ -18,6 +18,7 @@ import type { AgentIdentity, DetectedEnvironment } from "../types/index.js";
18
18
  * Detect the current agent identity based on environment
19
19
  */
20
20
  export declare function detectAgent(): DetectedEnvironment;
21
+ export declare function normalizeAgent(input: string): AgentIdentity;
21
22
  /**
22
23
  * Get just the agent identity (convenience function)
23
24
  */
@@ -8,7 +8,7 @@
8
8
  * | ENTRYPOINT | Docker | Hostname | Identity |
9
9
  * |------------|--------|----------|----------|
10
10
  * | cli | YES | (any) | CLI |
11
- * | cli | NO | orchestra.nteg.com | CODA-1 |
11
+ * | cli | NO | (server hostname) | CODA-1 |
12
12
  * | claude-desktop | NO | (any) | DAC |
13
13
  * | (empty) | NO | (local) | Brain_Local |
14
14
  * | (empty) | NO | (no fs) | Brain_Cloud |
@@ -48,33 +48,27 @@ export function detectAgent() {
48
48
  let agent;
49
49
  if (entrypoint === "cli") {
50
50
  if (docker) {
51
- // CLI in Docker container
52
- agent = "CLI";
51
+ agent = "cli";
53
52
  }
54
- else if (hostname === "orchestra.nteg.com" || hostname === "cyrus") {
55
- // CLI on orchestra server = CODA-1
56
- agent = "CODA-1";
53
+ else if (process.env.GITMEM_AGENT_HOSTNAME && (hostname === process.env.GITMEM_AGENT_HOSTNAME)) {
54
+ agent = "autonomous";
57
55
  }
58
56
  else {
59
- // CLI elsewhere (fallback)
60
- agent = "CLI";
57
+ agent = "cli";
61
58
  }
62
59
  }
63
60
  else if (entrypoint === "claude-desktop") {
64
- // Desktop app code tab
65
- agent = "DAC";
61
+ agent = "desktop";
66
62
  }
67
63
  else if (!entrypoint) {
68
- // No entrypoint - could be Brain Local or Brain Cloud
69
64
  if (hasFilesystemAccess()) {
70
- agent = "Brain_Local";
65
+ agent = "local";
71
66
  }
72
67
  else {
73
- agent = "Brain_Cloud";
68
+ agent = "cloud";
74
69
  }
75
70
  }
76
71
  else {
77
- // Unknown entrypoint
78
72
  agent = "Unknown";
79
73
  }
80
74
  return {
@@ -84,6 +78,21 @@ export function detectAgent() {
84
78
  agent,
85
79
  };
86
80
  }
81
+ /**
82
+ * Normalize legacy agent names to new generic names.
83
+ * Accepts both old (CLI, DAC, CODA-1, Brain_Local, Brain_Cloud)
84
+ * and new (cli, desktop, autonomous, local, cloud) formats.
85
+ */
86
+ const LEGACY_MAP = {
87
+ "CLI": "cli",
88
+ "DAC": "desktop",
89
+ "CODA-1": "autonomous",
90
+ "Brain_Local": "local",
91
+ "Brain_Cloud": "cloud",
92
+ };
93
+ export function normalizeAgent(input) {
94
+ return LEGACY_MAP[input] || input;
95
+ }
87
96
  /**
88
97
  * Get just the agent identity (convenience function)
89
98
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Analytics Service (OD-567)
2
+ * Analytics Service
3
3
  *
4
4
  * Shared analytics engine for session insights. Powers both the
5
5
  * gitmem-analyze MCP tool (CLI) and the GitMem Console dashboard.
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Analytics Service (OD-567)
2
+ * Analytics Service
3
3
  *
4
4
  * Shared analytics engine for session insights. Powers both the
5
5
  * gitmem-analyze MCP tool (CLI) and the GitMem Console dashboard.
6
6
  *
7
7
  * Uses directQuery for raw Supabase REST access (no ww-mcp dependency).
8
8
  */
9
- import { directQuery, directQueryAll } from "./supabase-client.js";
9
+ import { directQuery, directQueryAll, safeInFilter } from "./supabase-client.js";
10
10
  import { getCache } from "./cache.js";
11
11
  // --- Query Layer ---
12
12
  /**
@@ -97,7 +97,7 @@ export async function enrichScarUsageTitles(usages) {
97
97
  const learnings = await directQuery("orchestra_learnings", {
98
98
  select: "id,title,severity",
99
99
  filters: {
100
- id: `in.(${ids.join(",")})`,
100
+ id: safeInFilter(ids),
101
101
  },
102
102
  });
103
103
  // Build lookup map
@@ -10,7 +10,7 @@
10
10
  * 2. fetchDismissalCounts() — queries scar_usage for inline archival hints
11
11
  * (called from recall to annotate frequently-dismissed scars)
12
12
  */
13
- import { isConfigured } from "./supabase-client.js";
13
+ import { isConfigured, safeInFilter } from "./supabase-client.js";
14
14
  const SUPABASE_URL = process.env.SUPABASE_URL || "";
15
15
  const SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_KEY || "";
16
16
  const SUPABASE_REST_URL = SUPABASE_URL ? `${SUPABASE_URL}/rest/v1` : "";
@@ -76,7 +76,7 @@ export async function fetchDismissalCounts(scarIds) {
76
76
  // Query scar_usage for the given scar IDs (last 90 days)
77
77
  const url = new URL(`${SUPABASE_REST_URL}/scar_usage`);
78
78
  url.searchParams.set("select", "scar_id,reference_type");
79
- url.searchParams.set("scar_id", `in.(${scarIds.join(",")})`);
79
+ url.searchParams.set("scar_id", safeInFilter(scarIds));
80
80
  url.searchParams.set("surfaced_at", `gte.${new Date(Date.now() - 90 * 86400000).toISOString()}`);
81
81
  url.searchParams.set("limit", "1000");
82
82
  const response = await fetch(url.toString(), {
@@ -5,7 +5,7 @@
5
5
  * Caches search results to avoid repeated ww-mcp calls.
6
6
  *
7
7
  * Design: docs/systems/gitmem-caching.md
8
- * Issue: OD-473
8
+ *
9
9
  */
10
10
  declare const TTL: {
11
11
  readonly SCAR_SEARCH: number;
@@ -5,7 +5,7 @@
5
5
  * Caches search results to avoid repeated ww-mcp calls.
6
6
  *
7
7
  * Design: docs/systems/gitmem-caching.md
8
- * Issue: OD-473
8
+ *
9
9
  */
10
10
  import { createHash } from "crypto";
11
11
  import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync, statSync } from "fs";
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Validates session close compliance based on close type.
5
5
  *
6
- * Standard close requires (OD-491):
6
+ * Standard close requires:
7
7
  * - task_completion object with timestamps proving each step was done
8
8
  * - All 6 closing questions answered
9
9
  * - human_corrections field present (even if empty string)
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Validates session close compliance based on close type.
5
5
  *
6
- * Standard close requires (OD-491):
6
+ * Standard close requires:
7
7
  * - task_completion object with timestamps proving each step was done
8
8
  * - All 6 closing questions answered
9
9
  * - human_corrections field present (even if empty string)
@@ -17,7 +17,7 @@ import { CLOSING_QUESTIONS } from "../constants/closing-questions.js";
17
17
  /** Minimum time (ms) between asking human and receiving response */
18
18
  const MIN_HUMAN_RESPONSE_GAP_MS = 3000;
19
19
  /**
20
- * Validate task_completion timestamps (OD-491)
20
+ * Validate task_completion timestamps
21
21
  *
22
22
  * Ensures:
23
23
  * 1. All timestamps are valid ISO strings
@@ -83,14 +83,14 @@ function validateTaskCompletion(tc) {
83
83
  /**
84
84
  * Validate standard close parameters
85
85
  *
86
- * OD-491: Now requires task_completion with timestamps proving each step was done.
86
+ * Now requires task_completion with timestamps proving each step was done.
87
87
  */
88
88
  function validateStandardClose(params) {
89
89
  const errors = [];
90
90
  const warnings = [];
91
- // OD-491: task_completion is REQUIRED for standard close
91
+ // task_completion is REQUIRED for standard close
92
92
  if (!params.task_completion) {
93
- errors.push("Standard close requires task_completion object (OD-491). " +
93
+ errors.push("Standard close requires task_completion object. " +
94
94
  "You must complete each step: display questions → answer → ask human → wait for response");
95
95
  }
96
96
  else {
@@ -10,7 +10,7 @@
10
10
  * - GITMEM_STALE_CHECK: "true" | "false" - Check for stale data (default: "true")
11
11
  * - SUPABASE_URL: Supabase project URL
12
12
  *
13
- * Issue: OD-473
13
+ *
14
14
  */
15
15
  import type { GitMemTier } from "./tier.js";
16
16
  export type SearchMode = "local" | "remote" | "auto";
@@ -10,7 +10,7 @@
10
10
  * - GITMEM_STALE_CHECK: "true" | "false" - Check for stale data (default: "true")
11
11
  * - SUPABASE_URL: Supabase project URL
12
12
  *
13
- * Issue: OD-473
13
+ *
14
14
  */
15
15
  import { getTier, hasSupabase, getTablePrefix } from "./tier.js";
16
16
  // Private IP patterns for on-prem detection
@@ -66,6 +66,18 @@ export function acquireLockSync(lockPath, timeoutMs = DEFAULT_TIMEOUT_MS, retryM
66
66
  if (error.code !== "EEXIST") {
67
67
  throw new Error(`[file-lock] Failed to create lock file ${lockPath}: ${error.message}`);
68
68
  }
69
+ // Lock file exists — check if we already hold it (reentrance detection)
70
+ try {
71
+ const raw = fs.readFileSync(lockPath, "utf-8");
72
+ const holder = JSON.parse(raw);
73
+ if (holder.pid === process.pid && holder.hostname === os.hostname()) {
74
+ // Same process already holds the lock — reentrant call, allow through
75
+ return;
76
+ }
77
+ }
78
+ catch {
79
+ // Can't read lock — fall through to stale check
80
+ }
69
81
  // Lock file exists — check if stale
70
82
  if (isLockStale(lockPath)) {
71
83
  try {
@@ -2,20 +2,33 @@
2
2
  * Resolved .gitmem directory path
3
3
  *
4
4
  * Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
5
- * but .gitmem/ was created in the project root (e.g., /workspace/orchestra/).
5
+ * but .gitmem/ was created in the project root.
6
6
  * The MCP server is long-running, so we resolve the path once and cache it.
7
7
  *
8
8
  * Resolution order:
9
- * 1. Cached path from session_start (most reliable — session_start created the directory)
10
- * 2. Walk up from process.cwd() looking for existing .gitmem/ sentinels
11
- * 3. Fall back to process.cwd()/.gitmem (original behavior)
9
+ * 1. GITMEM_DIR env var (explicit override)
10
+ * 2. Cached path from session_start (most reliable session_start created the directory)
11
+ * 3. Walk up from process.cwd() looking for existing .gitmem/ sentinels (backward compat)
12
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
12
13
  */
14
+ /**
15
+ * Validate a string intended for use as a single path component (directory name or filename).
16
+ * Rejects path traversal sequences, directory separators, and null bytes.
17
+ * Throws on invalid input — callers should validate before reaching this layer.
18
+ */
19
+ export declare function sanitizePathComponent(value: string, label: string): string;
13
20
  /**
14
21
  * Set the .gitmem directory path (called by session_start after creating it)
15
22
  */
16
23
  export declare function setGitmemDir(dir: string): void;
17
24
  /**
18
25
  * Get the resolved .gitmem directory path
26
+ *
27
+ * Resolution order:
28
+ * 1. GITMEM_DIR env var (explicit override)
29
+ * 2. Cached path from session_start (most reliable)
30
+ * 3. Walk up from CWD looking for existing .gitmem/ sentinels (backward compat)
31
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
19
32
  */
20
33
  export declare function getGitmemDir(): string;
21
34
  /**
@@ -2,16 +2,32 @@
2
2
  * Resolved .gitmem directory path
3
3
  *
4
4
  * Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
5
- * but .gitmem/ was created in the project root (e.g., /workspace/orchestra/).
5
+ * but .gitmem/ was created in the project root.
6
6
  * The MCP server is long-running, so we resolve the path once and cache it.
7
7
  *
8
8
  * Resolution order:
9
- * 1. Cached path from session_start (most reliable — session_start created the directory)
10
- * 2. Walk up from process.cwd() looking for existing .gitmem/ sentinels
11
- * 3. Fall back to process.cwd()/.gitmem (original behavior)
9
+ * 1. GITMEM_DIR env var (explicit override)
10
+ * 2. Cached path from session_start (most reliable session_start created the directory)
11
+ * 3. Walk up from process.cwd() looking for existing .gitmem/ sentinels (backward compat)
12
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
12
13
  */
13
14
  import * as path from "path";
14
15
  import * as fs from "fs";
16
+ import * as os from "os";
17
+ /**
18
+ * Validate a string intended for use as a single path component (directory name or filename).
19
+ * Rejects path traversal sequences, directory separators, and null bytes.
20
+ * Throws on invalid input — callers should validate before reaching this layer.
21
+ */
22
+ export function sanitizePathComponent(value, label) {
23
+ if (!value || typeof value !== "string") {
24
+ throw new Error(`${label} must be a non-empty string`);
25
+ }
26
+ if (value.includes("..") || value.includes("/") || value.includes("\\") || value.includes("\0")) {
27
+ throw new Error(`${label} contains invalid characters (path traversal rejected)`);
28
+ }
29
+ return value;
30
+ }
15
31
  let cachedGitmemDir = null;
16
32
  /**
17
33
  * Set the .gitmem directory path (called by session_start after creating it)
@@ -22,13 +38,29 @@ export function setGitmemDir(dir) {
22
38
  }
23
39
  /**
24
40
  * Get the resolved .gitmem directory path
41
+ *
42
+ * Resolution order:
43
+ * 1. GITMEM_DIR env var (explicit override)
44
+ * 2. Cached path from session_start (most reliable)
45
+ * 3. Walk up from CWD looking for existing .gitmem/ sentinels (backward compat)
46
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
25
47
  */
26
48
  export function getGitmemDir() {
27
- // 1. Use cached path from session_start
49
+ // 1. GITMEM_DIR env var (explicit override, highest priority)
50
+ const envDir = process.env.GITMEM_DIR;
51
+ if (envDir) {
52
+ if (!cachedGitmemDir || cachedGitmemDir !== envDir) {
53
+ cachedGitmemDir = envDir;
54
+ console.error(`[gitmem-dir] Using GITMEM_DIR env var: ${envDir}`);
55
+ }
56
+ return envDir;
57
+ }
58
+ // 2. Use cached path from session_start
28
59
  if (cachedGitmemDir && fs.existsSync(cachedGitmemDir)) {
29
60
  return cachedGitmemDir;
30
61
  }
31
- // 2. Walk up from CWD looking for existing .gitmem directory
62
+ // 3. Walk up from CWD looking for existing .gitmem directory
63
+ // Backward compat: finds project-scoped .gitmem/ from older installations.
32
64
  // Sentinel files checked in priority order:
33
65
  // - active-sessions.json (multi-session registry, GIT-19)
34
66
  // - config.json (project-level gitmem config)
@@ -46,9 +78,9 @@ export function getGitmemDir() {
46
78
  }
47
79
  dir = path.dirname(dir);
48
80
  }
49
- // 3. Fall back to CWD (original behavior)
50
- const fallback = path.join(process.cwd(), ".gitmem");
51
- console.error(`[gitmem-dir] Falling back to CWD: ${fallback}`);
81
+ // 4. Fall back to ~/.gitmem (developer-scoped — survives across projects and containers)
82
+ const fallback = path.join(os.homedir(), ".gitmem");
83
+ console.error(`[gitmem-dir] Falling back to home dir: ${fallback}`);
52
84
  return fallback;
53
85
  }
54
86
  /**
@@ -62,6 +94,7 @@ export function getGitmemPath(filename) {
62
94
  * Creates the directory if it doesn't exist.
63
95
  */
64
96
  export function getSessionDir(sessionId) {
97
+ sanitizePathComponent(sessionId, "sessionId");
65
98
  const sessionsDir = path.join(getGitmemDir(), "sessions", sessionId);
66
99
  if (!fs.existsSync(sessionsDir)) {
67
100
  fs.mkdirSync(sessionsDir, { recursive: true });
@@ -73,6 +106,7 @@ export function getSessionDir(sessionId) {
73
106
  * Get a file path within a per-session directory.
74
107
  */
75
108
  export function getSessionPath(sessionId, filename) {
109
+ sanitizePathComponent(filename, "filename");
76
110
  return path.join(getSessionDir(sessionId), filename);
77
111
  }
78
112
  /**
@@ -2,7 +2,7 @@
2
2
  * Local File Storage — Free Tier Backend
3
3
  *
4
4
  * Stores scars, sessions, decisions, and scar usage as JSON files
5
- * in the .gitmem/ directory of the current project.
5
+ * in the .gitmem/ directory (defaults to ~/.gitmem, overridable via GITMEM_DIR).
6
6
  *
7
7
  * Provides keyword-based search (no embeddings needed).
8
8
  */