grov 0.5.11 → 0.6.13

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 (190) hide show
  1. package/dist/cli/agents/registry.d.ts +17 -0
  2. package/dist/cli/agents/registry.js +132 -0
  3. package/dist/cli/commands/agents.d.ts +1 -0
  4. package/dist/cli/commands/agents.js +48 -0
  5. package/dist/cli/commands/disable.d.ts +1 -0
  6. package/dist/cli/commands/disable.js +179 -0
  7. package/dist/cli/commands/doctor.d.ts +1 -0
  8. package/dist/cli/commands/doctor.js +157 -0
  9. package/dist/{commands → cli/commands}/drift-test.js +39 -26
  10. package/dist/cli/commands/init.d.ts +1 -0
  11. package/dist/cli/commands/init.js +90 -0
  12. package/dist/{commands → cli/commands}/login.js +19 -18
  13. package/dist/{commands → cli/commands}/logout.js +1 -1
  14. package/dist/{commands → cli/commands}/proxy-status.js +1 -1
  15. package/dist/cli/commands/setup.d.ts +6 -0
  16. package/dist/cli/commands/setup.js +309 -0
  17. package/dist/{commands → cli/commands}/status.js +1 -1
  18. package/dist/{commands → cli/commands}/sync.d.ts +1 -0
  19. package/dist/{commands → cli/commands}/sync.js +59 -4
  20. package/dist/{commands → cli/commands}/uninstall.js +2 -2
  21. package/dist/cli/index.js +270 -0
  22. package/dist/{lib → core/cloud}/cloud-sync.d.ts +3 -3
  23. package/dist/{lib → core/cloud}/cloud-sync.js +10 -10
  24. package/dist/{lib → core/extraction}/correction-builder-proxy.d.ts +1 -1
  25. package/dist/{lib → core/extraction}/correction-builder-proxy.js +0 -4
  26. package/dist/{lib → core/extraction}/drift-checker-proxy.d.ts +13 -9
  27. package/dist/core/extraction/drift-checker-proxy.js +510 -0
  28. package/dist/{lib → core/extraction}/llm-extractor.d.ts +8 -38
  29. package/dist/{lib → core/extraction}/llm-extractor.js +132 -220
  30. package/dist/{lib → core}/store/sessions.js +3 -19
  31. package/dist/core/store/store.d.ts +1 -0
  32. package/dist/{lib → core/store}/store.js +1 -1
  33. package/dist/{lib → core}/store/types.d.ts +0 -4
  34. package/dist/integrations/mcp/cache.d.ts +27 -0
  35. package/dist/integrations/mcp/cache.js +106 -0
  36. package/dist/integrations/mcp/capture/antigravity-parser.d.ts +26 -0
  37. package/dist/integrations/mcp/capture/antigravity-parser.js +272 -0
  38. package/dist/integrations/mcp/capture/antigravity-scanner.d.ts +24 -0
  39. package/dist/integrations/mcp/capture/antigravity-scanner.js +153 -0
  40. package/dist/integrations/mcp/capture/antigravity-sync-tracker.d.ts +29 -0
  41. package/dist/integrations/mcp/capture/antigravity-sync-tracker.js +115 -0
  42. package/dist/integrations/mcp/capture/cli-extractor.d.ts +18 -0
  43. package/dist/integrations/mcp/capture/cli-extractor.js +258 -0
  44. package/dist/integrations/mcp/capture/cli-synced.d.ts +4 -0
  45. package/dist/integrations/mcp/capture/cli-synced.js +62 -0
  46. package/dist/integrations/mcp/capture/cli-transform.d.ts +30 -0
  47. package/dist/integrations/mcp/capture/cli-transform.js +62 -0
  48. package/dist/integrations/mcp/capture/cli-watcher.d.ts +31 -0
  49. package/dist/integrations/mcp/capture/cli-watcher.js +106 -0
  50. package/dist/integrations/mcp/capture/hook-handler.d.ts +2 -0
  51. package/dist/integrations/mcp/capture/hook-handler.js +157 -0
  52. package/dist/integrations/mcp/capture/sqlite-reader.d.ts +35 -0
  53. package/dist/integrations/mcp/capture/sqlite-reader.js +388 -0
  54. package/dist/integrations/mcp/capture/sync-tracker.d.ts +16 -0
  55. package/dist/integrations/mcp/capture/sync-tracker.js +102 -0
  56. package/dist/integrations/mcp/clients/cursor/rules-installer.d.ts +19 -0
  57. package/dist/integrations/mcp/clients/cursor/rules-installer.js +123 -0
  58. package/dist/integrations/mcp/index.d.ts +1 -0
  59. package/dist/integrations/mcp/index.js +94 -0
  60. package/dist/integrations/mcp/logger.d.ts +8 -0
  61. package/dist/integrations/mcp/logger.js +50 -0
  62. package/dist/integrations/mcp/server.d.ts +5 -0
  63. package/dist/integrations/mcp/server.js +58 -0
  64. package/dist/integrations/mcp/tools/expand.d.ts +1 -0
  65. package/dist/integrations/mcp/tools/expand.js +53 -0
  66. package/dist/integrations/mcp/tools/preview.d.ts +1 -0
  67. package/dist/integrations/mcp/tools/preview.js +64 -0
  68. package/dist/integrations/proxy/agents/base.d.ts +43 -0
  69. package/dist/integrations/proxy/agents/base.js +13 -0
  70. package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.d.ts +4 -8
  71. package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.js +4 -33
  72. package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.d.ts +1 -1
  73. package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.js +22 -6
  74. package/dist/integrations/proxy/agents/claude/index.d.ts +43 -0
  75. package/dist/integrations/proxy/agents/claude/index.js +386 -0
  76. package/dist/{proxy/action-parser.d.ts → integrations/proxy/agents/claude/parser.d.ts} +1 -1
  77. package/dist/integrations/proxy/agents/codex/extractors.d.ts +6 -0
  78. package/dist/integrations/proxy/agents/codex/extractors.js +49 -0
  79. package/dist/integrations/proxy/agents/codex/forwarder.d.ts +9 -0
  80. package/dist/integrations/proxy/agents/codex/forwarder.js +125 -0
  81. package/dist/integrations/proxy/agents/codex/index.d.ts +44 -0
  82. package/dist/integrations/proxy/agents/codex/index.js +371 -0
  83. package/dist/integrations/proxy/agents/codex/parser.d.ts +11 -0
  84. package/dist/integrations/proxy/agents/codex/parser.js +104 -0
  85. package/dist/integrations/proxy/agents/codex/patch.d.ts +12 -0
  86. package/dist/integrations/proxy/agents/codex/patch.js +40 -0
  87. package/dist/integrations/proxy/agents/codex/settings.d.ts +18 -0
  88. package/dist/integrations/proxy/agents/codex/settings.js +73 -0
  89. package/dist/integrations/proxy/agents/codex/types.d.ts +59 -0
  90. package/dist/integrations/proxy/agents/codex/types.js +2 -0
  91. package/dist/integrations/proxy/agents/index.d.ts +11 -0
  92. package/dist/integrations/proxy/agents/index.js +25 -0
  93. package/dist/integrations/proxy/agents/types.d.ts +77 -0
  94. package/dist/integrations/proxy/agents/types.js +2 -0
  95. package/dist/{proxy → integrations/proxy/cache}/extended-cache.js +2 -6
  96. package/dist/{proxy → integrations/proxy}/config.js +1 -1
  97. package/dist/{proxy → integrations/proxy}/handlers/preprocess.d.ts +3 -3
  98. package/dist/integrations/proxy/handlers/preprocess.js +194 -0
  99. package/dist/integrations/proxy/index.js +20 -0
  100. package/dist/integrations/proxy/injection/memory-injection.d.ts +56 -0
  101. package/dist/integrations/proxy/injection/memory-injection.js +252 -0
  102. package/dist/integrations/proxy/orchestrator.d.ts +30 -0
  103. package/dist/integrations/proxy/orchestrator.js +954 -0
  104. package/dist/integrations/proxy/request-processor.d.ts +14 -0
  105. package/dist/integrations/proxy/request-processor.js +68 -0
  106. package/dist/{proxy → integrations/proxy}/response-processor.d.ts +4 -3
  107. package/dist/{proxy → integrations/proxy}/response-processor.js +51 -43
  108. package/dist/{proxy → integrations/proxy}/server.d.ts +0 -1
  109. package/dist/integrations/proxy/server.js +146 -0
  110. package/dist/{proxy → integrations/proxy}/types.d.ts +4 -0
  111. package/dist/{proxy → integrations/proxy}/utils/logging.d.ts +1 -0
  112. package/dist/{proxy → integrations/proxy}/utils/logging.js +5 -0
  113. package/package.json +31 -10
  114. package/postinstall.js +62 -6
  115. package/dist/cli.js +0 -149
  116. package/dist/commands/capture.d.ts +0 -6
  117. package/dist/commands/capture.js +0 -324
  118. package/dist/commands/disable.d.ts +0 -1
  119. package/dist/commands/disable.js +0 -14
  120. package/dist/commands/doctor.d.ts +0 -1
  121. package/dist/commands/doctor.js +0 -89
  122. package/dist/commands/init.d.ts +0 -1
  123. package/dist/commands/init.js +0 -52
  124. package/dist/commands/inject.d.ts +0 -5
  125. package/dist/commands/inject.js +0 -88
  126. package/dist/commands/prompt-inject.d.ts +0 -4
  127. package/dist/commands/prompt-inject.js +0 -451
  128. package/dist/commands/unregister.d.ts +0 -1
  129. package/dist/commands/unregister.js +0 -28
  130. package/dist/lib/anchor-extractor.d.ts +0 -30
  131. package/dist/lib/anchor-extractor.js +0 -296
  132. package/dist/lib/correction-builder.d.ts +0 -10
  133. package/dist/lib/correction-builder.js +0 -226
  134. package/dist/lib/drift-checker-proxy.js +0 -373
  135. package/dist/lib/drift-checker.d.ts +0 -66
  136. package/dist/lib/drift-checker.js +0 -341
  137. package/dist/lib/hooks.d.ts +0 -38
  138. package/dist/lib/hooks.js +0 -291
  139. package/dist/lib/jsonl-parser.d.ts +0 -87
  140. package/dist/lib/jsonl-parser.js +0 -281
  141. package/dist/lib/session-parser.d.ts +0 -44
  142. package/dist/lib/session-parser.js +0 -256
  143. package/dist/lib/store.d.ts +0 -1
  144. package/dist/proxy/cache.d.ts +0 -32
  145. package/dist/proxy/cache.js +0 -47
  146. package/dist/proxy/handlers/preprocess.js +0 -186
  147. package/dist/proxy/index.js +0 -30
  148. package/dist/proxy/injection/delta-tracking.d.ts +0 -11
  149. package/dist/proxy/injection/delta-tracking.js +0 -94
  150. package/dist/proxy/injection/injectors.d.ts +0 -7
  151. package/dist/proxy/injection/injectors.js +0 -139
  152. package/dist/proxy/request-processor.d.ts +0 -27
  153. package/dist/proxy/request-processor.js +0 -233
  154. package/dist/proxy/server.js +0 -1289
  155. /package/dist/{commands → cli/commands}/drift-test.d.ts +0 -0
  156. /package/dist/{commands → cli/commands}/login.d.ts +0 -0
  157. /package/dist/{commands → cli/commands}/logout.d.ts +0 -0
  158. /package/dist/{commands → cli/commands}/proxy-status.d.ts +0 -0
  159. /package/dist/{commands → cli/commands}/status.d.ts +0 -0
  160. /package/dist/{commands → cli/commands}/uninstall.d.ts +0 -0
  161. /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
  162. /package/dist/{lib → core/cloud}/api-client.d.ts +0 -0
  163. /package/dist/{lib → core/cloud}/api-client.js +0 -0
  164. /package/dist/{lib → core/cloud}/credentials.d.ts +0 -0
  165. /package/dist/{lib → core/cloud}/credentials.js +0 -0
  166. /package/dist/{lib → core}/store/convenience.d.ts +0 -0
  167. /package/dist/{lib → core}/store/convenience.js +0 -0
  168. /package/dist/{lib → core}/store/database.d.ts +0 -0
  169. /package/dist/{lib → core}/store/database.js +0 -0
  170. /package/dist/{lib → core}/store/drift.d.ts +0 -0
  171. /package/dist/{lib → core}/store/drift.js +0 -0
  172. /package/dist/{lib → core}/store/index.d.ts +0 -0
  173. /package/dist/{lib → core}/store/index.js +0 -0
  174. /package/dist/{lib → core}/store/sessions.d.ts +0 -0
  175. /package/dist/{lib → core}/store/steps.d.ts +0 -0
  176. /package/dist/{lib → core}/store/steps.js +0 -0
  177. /package/dist/{lib → core}/store/tasks.d.ts +0 -0
  178. /package/dist/{lib → core}/store/tasks.js +0 -0
  179. /package/dist/{lib → core}/store/types.js +0 -0
  180. /package/dist/{proxy/action-parser.js → integrations/proxy/agents/claude/parser.js} +0 -0
  181. /package/dist/{lib → integrations/proxy/agents/claude}/settings.d.ts +0 -0
  182. /package/dist/{lib → integrations/proxy/agents/claude}/settings.js +0 -0
  183. /package/dist/{proxy → integrations/proxy/cache}/extended-cache.d.ts +0 -0
  184. /package/dist/{proxy → integrations/proxy}/config.d.ts +0 -0
  185. /package/dist/{proxy → integrations/proxy}/index.d.ts +0 -0
  186. /package/dist/{proxy → integrations/proxy}/types.js +0 -0
  187. /package/dist/{lib → utils}/debug.d.ts +0 -0
  188. /package/dist/{lib → utils}/debug.js +0 -0
  189. /package/dist/{lib → utils}/utils.d.ts +0 -0
  190. /package/dist/{lib → utils}/utils.js +0 -0
@@ -1,32 +0,0 @@
1
- /**
2
- * Global team memory cache
3
- * - Calculated ONCE on first request, reused for ALL subsequent requests
4
- * - Invalidated only on: sync completion (in .then() callback), proxy restart
5
- * - Ensures system prompt prefix stays CONSTANT for Anthropic cache preservation
6
- */
7
- export declare let globalTeamMemoryCache: {
8
- projectPath: string;
9
- content: string;
10
- } | null;
11
- /**
12
- * Invalidate the global team memory cache
13
- * Called after successful sync to cloud (in .then() callback)
14
- * This ensures cache is only invalidated AFTER data is in cloud
15
- */
16
- export declare function invalidateTeamMemoryCache(): void;
17
- /**
18
- * Set the global team memory cache
19
- * @param projectPath - Project path for cache key
20
- * @param content - Formatted team memory content
21
- */
22
- export declare function setTeamMemoryCache(projectPath: string, content: string): void;
23
- /**
24
- * Get the current cache content if it matches the project path
25
- * @param projectPath - Project path to check
26
- * @returns Cached content or null if not cached/different project
27
- */
28
- export declare function getTeamMemoryCache(projectPath: string): string | null;
29
- /**
30
- * Check if cache exists for a specific project
31
- */
32
- export declare function hasCacheForProject(projectPath: string): boolean;
@@ -1,47 +0,0 @@
1
- // Phase 0 verified
2
- // Cache management for team memory injection
3
- // Shared between server.ts and response-processor.ts to avoid circular dependencies
4
- /**
5
- * Global team memory cache
6
- * - Calculated ONCE on first request, reused for ALL subsequent requests
7
- * - Invalidated only on: sync completion (in .then() callback), proxy restart
8
- * - Ensures system prompt prefix stays CONSTANT for Anthropic cache preservation
9
- */
10
- export let globalTeamMemoryCache = null;
11
- /**
12
- * Invalidate the global team memory cache
13
- * Called after successful sync to cloud (in .then() callback)
14
- * This ensures cache is only invalidated AFTER data is in cloud
15
- */
16
- export function invalidateTeamMemoryCache() {
17
- globalTeamMemoryCache = null;
18
- // DEBUG: Commented out for cleaner terminal - uncomment when debugging
19
- // console.log('[CACHE] Team memory cache invalidated');
20
- }
21
- /**
22
- * Set the global team memory cache
23
- * @param projectPath - Project path for cache key
24
- * @param content - Formatted team memory content
25
- */
26
- export function setTeamMemoryCache(projectPath, content) {
27
- globalTeamMemoryCache = { projectPath, content };
28
- // DEBUG: Commented out for cleaner terminal - uncomment when debugging
29
- // console.log(`[CACHE] Team memory cache set for project: ${projectPath} (${content.length} chars)`);
30
- }
31
- /**
32
- * Get the current cache content if it matches the project path
33
- * @param projectPath - Project path to check
34
- * @returns Cached content or null if not cached/different project
35
- */
36
- export function getTeamMemoryCache(projectPath) {
37
- if (globalTeamMemoryCache && globalTeamMemoryCache.projectPath === projectPath) {
38
- return globalTeamMemoryCache.content;
39
- }
40
- return null;
41
- }
42
- /**
43
- * Check if cache exists for a specific project
44
- */
45
- export function hasCacheForProject(projectPath) {
46
- return globalTeamMemoryCache?.projectPath === projectPath;
47
- }
@@ -1,186 +0,0 @@
1
- // Pre-process requests before forwarding to Anthropic
2
- import { config } from '../config.js';
3
- import { extractLastUserPrompt, extractFilesFromMessages, buildTeamMemoryContextCloud } from '../request-processor.js';
4
- import { getSessionState, updateSessionState, markCleared, } from '../../lib/store.js';
5
- import { isSyncEnabled, getSyncTeamId } from '../../lib/cloud-sync.js';
6
- import { globalTeamMemoryCache, setTeamMemoryCache, invalidateTeamMemoryCache } from '../cache.js';
7
- import { buildDynamicInjection, clearSessionTracking } from '../injection/delta-tracking.js';
8
- import { appendToSystemPrompt } from '../injection/injectors.js';
9
- // Pending plan summary state - triggers CLEAR-like reset after planning task completes
10
- let pendingPlanClear = null;
11
- export function getPendingPlanClear() {
12
- return pendingPlanClear;
13
- }
14
- export function setPendingPlanClear(value) {
15
- pendingPlanClear = value;
16
- }
17
- export function clearPendingPlan() {
18
- pendingPlanClear = null;
19
- }
20
- export async function preProcessRequest(body, sessionInfo, logger, detectRequestType) {
21
- const modified = { ...body };
22
- // Skip warmup requests - Claude Code sends "Warmup" as health check
23
- // No need to do semantic search or cache operations for these
24
- const earlyUserPrompt = extractLastUserPrompt(modified.messages || []);
25
- if (earlyUserPrompt === 'Warmup') {
26
- // DEBUG: Commented out for cleaner terminal - uncomment when debugging
27
- // console.log('[INJECT] Skipping warmup request (no search, no cache)');
28
- return modified;
29
- }
30
- // Detect request type: first, continuation, or retry
31
- const requestType = detectRequestType(modified.messages || [], sessionInfo.projectPath);
32
- // === NEW ARCHITECTURE: Separate static and dynamic injection ===
33
- //
34
- // STATIC (system prompt, cached):
35
- // - Team memory from PAST sessions only
36
- // - CLEAR summary when triggered
37
- // -> Uses __grovInjection + injectIntoRawBody()
38
- //
39
- // DYNAMIC (user message, delta only):
40
- // - Files edited in current session
41
- // - Key decisions with reasoning
42
- // - Drift correction, forced recovery
43
- // -> Uses __grovUserMsgInjection + appendToLastUserMessage()
44
- // Get session state
45
- const sessionState = getSessionState(sessionInfo.sessionId);
46
- // === PLANNING CLEAR: Reset after planning task completes ===
47
- // This ensures implementation phase starts fresh with planning context from team memory
48
- if (pendingPlanClear && pendingPlanClear.projectPath === sessionInfo.projectPath) {
49
- // 1. Extract context BEFORE emptying messages (for hybrid search)
50
- const mentionedFiles = extractFilesFromMessages(modified.messages || []);
51
- const userPrompt = extractLastUserPrompt(modified.messages || []);
52
- // 2. Empty messages array (fresh start)
53
- modified.messages = [];
54
- // 3. Inject planning summary into system prompt
55
- appendToSystemPrompt(modified, pendingPlanClear.summary);
56
- // 4. Rebuild team memory NOW (includes the just-saved planning task)
57
- // Use cloud-first approach if sync is enabled
58
- let teamContext = null;
59
- const teamId = getSyncTeamId();
60
- if (isSyncEnabled() && teamId) {
61
- // DEBUG: Commented out for cleaner terminal - uncomment when debugging
62
- // console.log(`[INJECT] PLANNING_CLEAR: Using cloud team memory (teamId=${teamId.substring(0, 8)}...)`);
63
- teamContext = await buildTeamMemoryContextCloud(teamId, sessionInfo.projectPath, mentionedFiles, userPrompt // For hybrid semantic search
64
- );
65
- }
66
- else {
67
- // Sync not enabled - no injection (cloud-first approach)
68
- // console.log('[INJECT] Sync not enabled. Enable sync for team memory injection.');
69
- teamContext = null;
70
- }
71
- if (teamContext) {
72
- modified.__grovInjection = teamContext;
73
- modified.__grovInjectionCached = false;
74
- // Update cache with fresh team memory
75
- setTeamMemoryCache(sessionInfo.projectPath, teamContext);
76
- }
77
- // 4. Clear the pending plan (one-time use)
78
- pendingPlanClear = null;
79
- // 5. Clear tracking (fresh start)
80
- clearSessionTracking(sessionInfo.sessionId);
81
- return modified; // Skip other injections - this is a complete reset
82
- }
83
- // === CLEAR MODE (100% threshold) ===
84
- // If token count exceeds threshold AND we have a pre-computed summary, apply CLEAR
85
- if (sessionState) {
86
- const currentTokenCount = sessionState.token_count || 0;
87
- // DEBUG: Commented out for cleaner terminal - uncomment when debugging
88
- // console.log('[CLEAR-CHECK] ═══════════════════════════════════════════');
89
- // console.log('[CLEAR-CHECK] currentTokenCount:', currentTokenCount);
90
- // console.log('[CLEAR-CHECK] threshold:', config.TOKEN_CLEAR_THRESHOLD);
91
- // console.log('[CLEAR-CHECK] exceedsThreshold:', currentTokenCount > config.TOKEN_CLEAR_THRESHOLD);
92
- // console.log('[CLEAR-CHECK] hasPendingSummary:', !!sessionState.pending_clear_summary);
93
- // console.log('[CLEAR-CHECK] summaryLength:', sessionState.pending_clear_summary?.length || 0);
94
- // console.log('[CLEAR-CHECK] shouldClear:', currentTokenCount > config.TOKEN_CLEAR_THRESHOLD && !!sessionState.pending_clear_summary);
95
- // console.log('[CLEAR-CHECK] ═══════════════════════════════════════════');
96
- if (currentTokenCount > config.TOKEN_CLEAR_THRESHOLD &&
97
- sessionState.pending_clear_summary) {
98
- // console.log('[CLEAR-CHECK] >>> CLEAR MODE TRIGGERED! <<<');
99
- logger.info({
100
- msg: 'CLEAR MODE ACTIVATED - resetting conversation',
101
- tokenCount: currentTokenCount,
102
- threshold: config.TOKEN_CLEAR_THRESHOLD,
103
- summaryLength: sessionState.pending_clear_summary.length,
104
- });
105
- // 1. Empty messages array (fundamental reset)
106
- modified.messages = [];
107
- // 2. Inject summary into system prompt (this will cause cache miss - intentional)
108
- appendToSystemPrompt(modified, sessionState.pending_clear_summary);
109
- // 3. Mark session as cleared
110
- markCleared(sessionInfo.sessionId);
111
- console.log(`[CLEAR] Context reset (${sessionState.pending_clear_summary?.length || 0} chars summary)`);
112
- // 4. Clear pending summary and invalidate GLOBAL team memory cache (new baseline)
113
- updateSessionState(sessionInfo.sessionId, { pending_clear_summary: undefined });
114
- invalidateTeamMemoryCache(); // Force recalculation on next request (CLEAR mode)
115
- // 5. Clear tracking (fresh start after CLEAR)
116
- clearSessionTracking(sessionInfo.sessionId);
117
- logger.info({ msg: 'CLEAR complete - conversation reset with summary' });
118
- return modified; // Skip other injections - this is a complete reset
119
- }
120
- }
121
- // === STATIC INJECTION: Team memory (PAST sessions only) ===
122
- // Cached per session - identical across all requests for cache preservation
123
- // GLOBAL cache: same team memory for ALL requests (regardless of sessionId changes)
124
- // Only recalculate on: first request ever, CLEAR/Summary, project change, proxy restart
125
- const isSameProject = globalTeamMemoryCache?.projectPath === sessionInfo.projectPath;
126
- if (globalTeamMemoryCache && isSameProject) {
127
- // Reuse GLOBAL cached team memory (constant for entire conversation)
128
- modified.__grovInjection = globalTeamMemoryCache.content;
129
- modified.__grovInjectionCached = true;
130
- // Using cached team memory
131
- }
132
- else {
133
- // First request OR project changed OR cache was invalidated: compute team memory
134
- const mentionedFiles = extractFilesFromMessages(modified.messages || []);
135
- const userPrompt = extractLastUserPrompt(modified.messages || []);
136
- // Use cloud-first approach if sync is enabled
137
- let teamContext = null;
138
- const teamId = getSyncTeamId();
139
- if (isSyncEnabled() && teamId) {
140
- teamContext = await buildTeamMemoryContextCloud(teamId, sessionInfo.projectPath, mentionedFiles, userPrompt // For hybrid semantic search
141
- );
142
- }
143
- else {
144
- // Sync not enabled - no injection (cloud-first approach)
145
- // console.log('[INJECT] Sync not enabled. Enable sync for team memory injection.');
146
- teamContext = null;
147
- }
148
- // console.log(`[CACHE] Computing team memory (first/new), files=${mentionedFiles.length}, result=${teamContext ? teamContext.length : 'null'}`);
149
- if (teamContext) {
150
- modified.__grovInjection = teamContext;
151
- modified.__grovInjectionCached = false;
152
- // Store in GLOBAL cache - stays constant until CLEAR or restart
153
- setTeamMemoryCache(sessionInfo.projectPath, teamContext);
154
- }
155
- else {
156
- // No team memory available - clear global cache for this project
157
- // DEBUG: Commented out for cleaner terminal - uncomment when debugging
158
- // console.log('[CACHE-INVALIDATE] teamContext is NULL - why?');
159
- // console.log('[CACHE-INVALIDATE] isSameProject:', isSameProject);
160
- if (isSameProject) {
161
- // console.log('[CACHE-INVALIDATE] >>> INVALIDATING CACHE (teamContext null) <<<');
162
- invalidateTeamMemoryCache();
163
- }
164
- }
165
- }
166
- // SKIP dynamic injection for retries and continuations
167
- if (requestType !== 'first') {
168
- return modified;
169
- }
170
- // === DYNAMIC INJECTION: User message (delta only) ===
171
- // Includes: edited files, key decisions, drift correction, forced recovery
172
- // This goes into the LAST user message, not system prompt
173
- const dynamicInjection = buildDynamicInjection(sessionInfo.sessionId, sessionState, logger);
174
- if (dynamicInjection) {
175
- modified.__grovUserMsgInjection = dynamicInjection;
176
- logger.info({ msg: 'Dynamic injection ready for user message', size: dynamicInjection.length });
177
- // Clear pending corrections after building injection
178
- if (sessionState?.pending_correction || sessionState?.pending_forced_recovery) {
179
- updateSessionState(sessionInfo.sessionId, {
180
- pending_correction: undefined,
181
- pending_forced_recovery: undefined,
182
- });
183
- }
184
- }
185
- return modified;
186
- }
@@ -1,30 +0,0 @@
1
- // Grov Proxy CLI entry point
2
- // Load .env file for API keys
3
- import { config } from 'dotenv';
4
- import { join } from 'path';
5
- import { homedir } from 'os';
6
- import { existsSync } from 'fs';
7
- // Load from current directory .env first
8
- config();
9
- // Also load ~/.grov/.env as fallback
10
- const grovEnvPath = join(homedir(), '.grov', '.env');
11
- if (existsSync(grovEnvPath)) {
12
- config({ path: grovEnvPath });
13
- }
14
- import { startServer } from './server.js';
15
- // Check for API key before starting proxy
16
- if (!process.env.ANTHROPIC_API_KEY) {
17
- console.error('Error: ANTHROPIC_API_KEY is required to run the proxy.\n');
18
- console.error('To set it up:\n');
19
- console.error(' 1. Get your API key at:');
20
- console.error(' https://console.anthropic.com/settings/keys\n');
21
- console.error(' 2. Add to ~/.zshrc (or ~/.bashrc):');
22
- console.error(' export ANTHROPIC_API_KEY=sk-ant-...\n');
23
- console.error(' 3. Restart terminal or run: source ~/.zshrc\n');
24
- console.error('Then try again: grov proxy');
25
- process.exit(1);
26
- }
27
- startServer().catch((err) => {
28
- console.error('Proxy failed:', err);
29
- process.exit(1);
30
- });
@@ -1,11 +0,0 @@
1
- import type { SessionState } from '../../lib/store.js';
2
- export interface SessionInjectionTracking {
3
- files: Set<string>;
4
- decisionIds: Set<string>;
5
- reasonings: Set<string>;
6
- }
7
- export declare function getOrCreateTracking(sessionId: string): SessionInjectionTracking;
8
- export declare function clearSessionTracking(sessionId: string): void;
9
- export declare function buildDynamicInjection(sessionId: string, sessionState: SessionState | null, logger?: {
10
- info: (data: Record<string, unknown>) => void;
11
- }): string | null;
@@ -1,94 +0,0 @@
1
- // Delta tracking - avoid duplicate injections across requests
2
- import { getEditedFiles, getKeyDecisions } from '../../lib/store.js';
3
- import { smartTruncate } from '../../lib/utils.js';
4
- const sessionInjectionTracking = new Map();
5
- export function getOrCreateTracking(sessionId) {
6
- if (!sessionInjectionTracking.has(sessionId)) {
7
- sessionInjectionTracking.set(sessionId, {
8
- files: new Set(),
9
- decisionIds: new Set(),
10
- reasonings: new Set(),
11
- });
12
- }
13
- return sessionInjectionTracking.get(sessionId);
14
- }
15
- export function clearSessionTracking(sessionId) {
16
- sessionInjectionTracking.delete(sessionId);
17
- }
18
- export function buildDynamicInjection(sessionId, sessionState, logger) {
19
- const tracking = getOrCreateTracking(sessionId);
20
- const parts = [];
21
- const debugInfo = {};
22
- // 1. Get edited files (delta - not already injected)
23
- const allEditedFiles = getEditedFiles(sessionId);
24
- const newFiles = allEditedFiles.filter(f => !tracking.files.has(f));
25
- debugInfo.totalEditedFiles = allEditedFiles.length;
26
- debugInfo.newEditedFiles = newFiles.length;
27
- debugInfo.alreadyTrackedFiles = tracking.files.size;
28
- if (newFiles.length > 0) {
29
- // Track and add to injection
30
- newFiles.forEach(f => tracking.files.add(f));
31
- const fileNames = newFiles.slice(0, 5).map(f => f.split('/').pop());
32
- parts.push(`[EDITED: ${fileNames.join(', ')}]`);
33
- debugInfo.editedFilesInjected = fileNames;
34
- }
35
- // 2. Get key decisions with reasoning (delta - not already injected)
36
- const keyDecisions = getKeyDecisions(sessionId, 5);
37
- debugInfo.totalKeyDecisions = keyDecisions.length;
38
- debugInfo.alreadyTrackedDecisions = tracking.decisionIds.size;
39
- const newDecisions = keyDecisions.filter(d => !tracking.decisionIds.has(d.id) &&
40
- d.reasoning &&
41
- !tracking.reasonings.has(d.reasoning));
42
- debugInfo.newKeyDecisions = newDecisions.length;
43
- for (const decision of newDecisions.slice(0, 3)) {
44
- tracking.decisionIds.add(decision.id);
45
- tracking.reasonings.add(decision.reasoning);
46
- const truncated = smartTruncate(decision.reasoning, 120);
47
- parts.push(`[DECISION: ${truncated}]`);
48
- // Log the original and truncated reasoning for debugging
49
- if (logger) {
50
- logger.info({
51
- msg: 'Key decision reasoning extracted',
52
- originalLength: decision.reasoning.length,
53
- truncatedLength: truncated.length,
54
- original: decision.reasoning.substring(0, 200) + (decision.reasoning.length > 200 ? '...' : ''),
55
- truncated,
56
- });
57
- }
58
- }
59
- debugInfo.decisionsInjected = newDecisions.slice(0, 3).length;
60
- // 3. Add drift correction if pending
61
- if (sessionState?.pending_correction) {
62
- parts.push(`[DRIFT: ${sessionState.pending_correction}]`);
63
- debugInfo.hasDriftCorrection = true;
64
- debugInfo.driftCorrectionLength = sessionState.pending_correction.length;
65
- console.log(`[DRIFT] Correction injected (${sessionState.pending_correction.length} chars)`);
66
- }
67
- // 4. Add forced recovery if pending
68
- if (sessionState?.pending_forced_recovery) {
69
- parts.push(`[RECOVERY: ${sessionState.pending_forced_recovery}]`);
70
- debugInfo.hasForcedRecovery = true;
71
- debugInfo.forcedRecoveryLength = sessionState.pending_forced_recovery.length;
72
- }
73
- // Log debug info
74
- if (logger) {
75
- logger.info({
76
- msg: 'Dynamic injection build details',
77
- ...debugInfo,
78
- partsCount: parts.length,
79
- });
80
- }
81
- if (parts.length === 0) {
82
- return null;
83
- }
84
- const injection = '---\n[GROV CONTEXT]\n' + parts.join('\n');
85
- // Log final injection content
86
- if (logger) {
87
- logger.info({
88
- msg: 'Dynamic injection content',
89
- size: injection.length,
90
- content: injection,
91
- });
92
- }
93
- return injection;
94
- }
@@ -1,7 +0,0 @@
1
- import type { MessagesRequestBody } from '../types.js';
2
- export declare function appendToLastUserMessage(rawBody: string, injection: string): string;
3
- export declare function appendToSystemPrompt(body: MessagesRequestBody, textToAppend: string): void;
4
- export declare function injectIntoRawBody(rawBody: string, injectionText: string): {
5
- modified: string;
6
- success: boolean;
7
- };
@@ -1,139 +0,0 @@
1
- // Injection helpers for modifying request bodies
2
- export function appendToLastUserMessage(rawBody, injection) {
3
- // Find the last occurrence of "role":"user" followed by content
4
- // We need to find the content field of the last user message and append to it
5
- // Strategy: Find all user messages, get the last one, append to its content
6
- // This is tricky because content can be string or array
7
- // Simpler approach: Find the last user message's closing content
8
- // Look for pattern: "role":"user","content":"..." or "role":"user","content":[...]
9
- // Find last "role":"user"
10
- const userRolePattern = /"role"\s*:\s*"user"/g;
11
- let lastUserMatch = null;
12
- let match;
13
- while ((match = userRolePattern.exec(rawBody)) !== null) {
14
- lastUserMatch = match;
15
- }
16
- if (!lastUserMatch) {
17
- // No user message found, can't inject
18
- return rawBody;
19
- }
20
- // From lastUserMatch position, find the content field
21
- const afterRole = rawBody.slice(lastUserMatch.index);
22
- // Find "content" field after role
23
- const contentMatch = afterRole.match(/"content"\s*:\s*/);
24
- if (!contentMatch || contentMatch.index === undefined) {
25
- return rawBody;
26
- }
27
- const contentStartGlobal = lastUserMatch.index + contentMatch.index + contentMatch[0].length;
28
- const afterContent = rawBody.slice(contentStartGlobal);
29
- // Determine if content is string or array
30
- if (afterContent.startsWith('"')) {
31
- // String content - find closing quote (handling escapes)
32
- let i = 1; // Skip opening quote
33
- while (i < afterContent.length) {
34
- if (afterContent[i] === '\\') {
35
- i += 2; // Skip escaped char
36
- }
37
- else if (afterContent[i] === '"') {
38
- // Found closing quote
39
- const insertPos = contentStartGlobal + i;
40
- // Insert before closing quote, escape the injection for JSON
41
- const escapedInjection = injection
42
- .replace(/\\/g, '\\\\')
43
- .replace(/"/g, '\\"')
44
- .replace(/\n/g, '\\n');
45
- return rawBody.slice(0, insertPos) + '\\n\\n' + escapedInjection + rawBody.slice(insertPos);
46
- }
47
- else {
48
- i++;
49
- }
50
- }
51
- }
52
- else if (afterContent.startsWith('[')) {
53
- // Array content - find last text block and append, or add new text block
54
- // Find the closing ] of the content array
55
- let depth = 1;
56
- let i = 1;
57
- while (i < afterContent.length && depth > 0) {
58
- const char = afterContent[i];
59
- if (char === '[')
60
- depth++;
61
- else if (char === ']')
62
- depth--;
63
- else if (char === '"') {
64
- // Skip string
65
- i++;
66
- while (i < afterContent.length && afterContent[i] !== '"') {
67
- if (afterContent[i] === '\\')
68
- i++;
69
- i++;
70
- }
71
- }
72
- i++;
73
- }
74
- if (depth === 0) {
75
- // Found closing bracket at position i-1
76
- const insertPos = contentStartGlobal + i - 1;
77
- // Add new text block before closing bracket
78
- const escapedInjection = injection
79
- .replace(/\\/g, '\\\\')
80
- .replace(/"/g, '\\"')
81
- .replace(/\n/g, '\\n');
82
- const newBlock = `,{"type":"text","text":"\\n\\n${escapedInjection}"}`;
83
- return rawBody.slice(0, insertPos) + newBlock + rawBody.slice(insertPos);
84
- }
85
- }
86
- // Fallback: couldn't parse, return unchanged
87
- return rawBody;
88
- }
89
- export function appendToSystemPrompt(body, textToAppend) {
90
- if (typeof body.system === 'string') {
91
- body.system = body.system + textToAppend;
92
- }
93
- else if (Array.isArray(body.system)) {
94
- // Append as new text block WITHOUT cache_control
95
- // Anthropic allows max 4 cache blocks - Claude Code already uses 2+
96
- // Grov's injections are small (~2KB) so uncached is fine
97
- body.system.push({
98
- type: 'text',
99
- text: textToAppend,
100
- });
101
- }
102
- else {
103
- // No system prompt yet, create as string
104
- body.system = textToAppend;
105
- }
106
- }
107
- export function injectIntoRawBody(rawBody, injectionText) {
108
- // Find the system array in the raw JSON
109
- // Pattern: "system": [....]
110
- const systemMatch = rawBody.match(/"system"\s*:\s*\[/);
111
- if (!systemMatch || systemMatch.index === undefined) {
112
- return { modified: rawBody, success: false };
113
- }
114
- // Find the matching closing bracket for the system array
115
- const startIndex = systemMatch.index + systemMatch[0].length;
116
- let bracketCount = 1;
117
- let endIndex = startIndex;
118
- for (let i = startIndex; i < rawBody.length && bracketCount > 0; i++) {
119
- const char = rawBody[i];
120
- if (char === '[')
121
- bracketCount++;
122
- else if (char === ']')
123
- bracketCount--;
124
- if (bracketCount === 0) {
125
- endIndex = i;
126
- break;
127
- }
128
- }
129
- if (bracketCount !== 0) {
130
- return { modified: rawBody, success: false };
131
- }
132
- // Escape the injection text for JSON
133
- const escapedText = JSON.stringify(injectionText).slice(1, -1); // Remove outer quotes
134
- // Create the new block (without cache_control - will be cache_creation)
135
- const newBlock = `,{"type":"text","text":"${escapedText}"}`;
136
- // Insert before the closing bracket
137
- const modified = rawBody.slice(0, endIndex) + newBlock + rawBody.slice(endIndex);
138
- return { modified, success: true };
139
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Build context from CLOUD team memory for injection
3
- * Fetches memories from Supabase via API (cloud-first approach)
4
- * Uses hybrid search (semantic + lexical) when userPrompt is provided
5
- *
6
- * @param teamId - Team UUID from sync configuration
7
- * @param projectPath - Project path to filter by
8
- * @param mentionedFiles - Files mentioned in user messages (for boost)
9
- * @param userPrompt - User's prompt for semantic search (optional)
10
- * @returns Formatted context string or null if no memories found
11
- */
12
- export declare function buildTeamMemoryContextCloud(teamId: string, projectPath: string, mentionedFiles: string[], userPrompt?: string): Promise<string | null>;
13
- /**
14
- * Extract file paths from messages (user messages only, clean text)
15
- */
16
- export declare function extractFilesFromMessages(messages: Array<{
17
- role: string;
18
- content: unknown;
19
- }>): string[];
20
- /**
21
- * Extract the last user prompt from messages for semantic search
22
- * Returns clean text without system tags
23
- */
24
- export declare function extractLastUserPrompt(messages: Array<{
25
- role: string;
26
- content: unknown;
27
- }>): string | undefined;