gitmem-mcp 1.5.1 → 1.6.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.
@@ -17,7 +17,7 @@ import { detectAgent } from "../services/agent-detection.js";
17
17
  import * as supabase from "../services/supabase-client.js";
18
18
  // Scar search removed from start pipeline (loads on-demand via recall)
19
19
  import { ensureInitialized } from "../services/startup.js";
20
- import { hasSupabase, getTableName } from "../services/tier.js";
20
+ import { hasSupabase, hasProInsights, getTableName } from "../services/tier.js";
21
21
  import { getStorage } from "../services/storage.js";
22
22
  import { Timer, recordMetrics, calculateContextBytes, buildPerformanceData, buildComponentPerformance, } from "../services/metrics.js";
23
23
  import { setCurrentSession, getCurrentSession, addSurfacedScars, getSurfacedScars } from "../services/session-state.js";
@@ -29,6 +29,7 @@ import { registerSession, findSessionByHostPid, pruneStale, migrateFromLegacy }
29
29
  import * as os from "os";
30
30
  import { formatDate } from "../services/timezone.js";
31
31
  import { productLine, dimText, boldText } from "../services/display-protocol.js";
32
+ import { querySessionsByDateRange, queryScarUsageByDateRange, enrichScarUsageTitles, computeLightweightSummary, } from "../services/analytics.js";
32
33
  /**
33
34
  * Closing payload schema — returned in session_start/refresh so agents
34
35
  * know the exact field names for closing-payload.json without guessing.
@@ -252,6 +253,32 @@ async function loadRecentDecisions(project, limit = 5) {
252
253
  };
253
254
  }
254
255
  }
256
+ /**
257
+ * Load lightweight analytics for Pro insights snippet.
258
+ * Reuses cached analytics queries — ~200ms on cache miss, near-instant on hit.
259
+ * Returns null on any error (never blocks session start).
260
+ */
261
+ async function loadLightweightAnalytics(project) {
262
+ if (!hasProInsights() || !hasSupabase())
263
+ return null;
264
+ try {
265
+ const endDate = new Date().toISOString();
266
+ const startDate = new Date(Date.now() - 30 * 86400000).toISOString();
267
+ const [sessions, rawUsages] = await Promise.all([
268
+ querySessionsByDateRange(startDate, endDate, project),
269
+ queryScarUsageByDateRange(startDate, endDate, project),
270
+ ]);
271
+ // Skip if not enough data for meaningful insights
272
+ if (sessions.length < 5)
273
+ return null;
274
+ const usages = await enrichScarUsageTitles(rawUsages);
275
+ return computeLightweightSummary(sessions, usages);
276
+ }
277
+ catch (error) {
278
+ console.error("[session_start] Lightweight analytics failed (non-fatal):", error);
279
+ return null;
280
+ }
281
+ }
255
282
  // loadRecentWins removed — wins available via search/log on-demand
256
283
  /**
257
284
  * Create a new session record
@@ -666,7 +693,7 @@ function writeSessionFiles(sessionId, agent, project, surfacedScars, threads, re
666
693
  function stripThreadPrefix(text) {
667
694
  return text.replace(/^t-[a-f0-9]+:\s*/i, "");
668
695
  }
669
- function formatStartDisplay(result, displayInfoMap, isFirstSession) {
696
+ function formatStartDisplay(result, displayInfoMap, isFirstSession, analytics) {
670
697
  const visual = [];
671
698
  // Line 1: branded product line + session state
672
699
  const stateLabel = result.refreshed ? "refreshed" : (result.resumed ? "resumed" : "active");
@@ -725,6 +752,16 @@ function formatStartDisplay(result, displayInfoMap, isFirstSession) {
725
752
  visual.push("");
726
753
  visual.push("No threads or decisions.");
727
754
  }
755
+ // Pro insights snippet — 30-day analytics summary
756
+ if (analytics) {
757
+ visual.push("");
758
+ const appPct = Math.round(analytics.application_rate * 100);
759
+ visual.push(boldText("Pro Insights (30d)"));
760
+ visual.push(` ${analytics.total_sessions} sessions · ${analytics.scars_surfaced} scars surfaced · ${appPct}% applied`);
761
+ if (analytics.top_blindspot) {
762
+ visual.push(` Top blindspot: "${analytics.top_blindspot.title}" (ignored ${analytics.top_blindspot.times} times)`);
763
+ }
764
+ }
728
765
  // First-session nudge — agent sees this once, internalizes to PMEM
729
766
  if (isFirstSession) {
730
767
  visual.push(FIRST_SESSION_NUDGE);
@@ -784,12 +821,13 @@ export async function sessionStart(params) {
784
821
  if (!hasSupabase()) {
785
822
  return sessionStartFree(params, env, agent, project, timer, metricsId, existingSession?.sessionId, existingSession?.startedAt || forceCarryStartedAt, priorSession ? { surfacedScars: forceCarrySurfacedScars, observations: forceCarryObservations, children: forceCarryChildren } : undefined);
786
823
  }
787
- // 2. Load last session + decisions in parallel (was sequential)
824
+ // 2. Load last session + decisions + analytics in parallel (was sequential)
788
825
  // Scars and wins removed from pipeline — load on-demand via recall/search
789
826
  // Rapport loading disabled — recording kept in session_close but not injected
790
- const [lastSessionResult, decisionsResult] = await Promise.all([
827
+ const [lastSessionResult, decisionsResult, analyticsResult] = await Promise.all([
791
828
  loadLastSession(agent, project),
792
829
  loadRecentDecisions(project, 3),
830
+ loadLightweightAnalytics(project),
793
831
  ]);
794
832
  const lastSession = lastSessionResult.session;
795
833
  const decisions = decisionsResult.decisions;
@@ -924,7 +962,7 @@ export async function sessionStart(params) {
924
962
  displayInfoMap.set(info.thread.id, info);
925
963
  }
926
964
  const isFirstSession = !isResuming && !slimLastSession;
927
- result.display = formatStartDisplay(result, displayInfoMap, isFirstSession);
965
+ result.display = formatStartDisplay(result, displayInfoMap, isFirstSession, analyticsResult);
928
966
  // Write display to per-session dir
929
967
  try {
930
968
  const sessionFilePath = getSessionPath(sessionId, "session.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitmem-mcp",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "mcpName": "io.github.gitmem-dev/gitmem",
5
5
  "description": "Persistent learning memory for AI coding agents. Memory that compounds.",
6
6
  "type": "module",