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.
- package/CHANGELOG.md +9 -0
- package/README.md +21 -4
- package/bin/gitmem.js +10 -0
- package/dist/commands/activate.d.ts +20 -0
- package/dist/commands/activate.js +562 -0
- package/dist/commands/deactivate.d.ts +10 -0
- package/dist/commands/deactivate.js +95 -0
- package/dist/commands/migrate-local.d.ts +53 -0
- package/dist/commands/migrate-local.js +177 -0
- package/dist/schemas/log.d.ts +2 -2
- package/dist/schemas/search.d.ts +2 -2
- package/dist/schemas/session-close.d.ts +12 -12
- package/dist/server.js +20 -2
- package/dist/services/analytics.d.ts +22 -0
- package/dist/services/analytics.js +68 -0
- package/dist/services/license.d.ts +57 -0
- package/dist/services/license.js +200 -0
- package/dist/services/supabase-client.d.ts +6 -0
- package/dist/services/supabase-client.js +75 -22
- package/dist/services/tier.d.ts +13 -3
- package/dist/services/tier.js +38 -7
- package/dist/tools/recall.js +16 -4
- package/dist/tools/session-close.js +31 -5
- package/dist/tools/session-start.js +43 -5
- package/package.json +1 -1
- package/schema/setup.sql +489 -25
|
@@ -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");
|