grov 0.5.3 → 0.5.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.
- package/README.md +15 -3
- package/dist/lib/llm-extractor.d.ts +0 -1
- package/dist/lib/llm-extractor.js +1 -3
- package/dist/proxy/server.js +28 -20
- package/package.json +3 -1
- package/postinstall.js +19 -0
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<a href="https://www.npmjs.com/package/grov"><img src="https://img.shields.io/npm/v/grov" alt="npm version"></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/grov"><img src="https://img.shields.io/npm/dm/grov" alt="npm downloads"></a>
|
|
12
12
|
<a href="https://github.com/TonyStef/Grov/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue" alt="license"></a>
|
|
13
|
+
<a href="https://app.grov.dev"><img src="https://img.shields.io/badge/Dashboard-app.grov.dev-22c55e" alt="dashboard"></a>
|
|
13
14
|
</p>
|
|
14
15
|
|
|
15
16
|
<p align="center">
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
<a href="#contributing">Contributing</a>
|
|
21
22
|
</p>
|
|
22
23
|
|
|
23
|
-
Grov
|
|
24
|
+
Grov gives your team a shared AI memory.
|
|
24
25
|
|
|
25
26
|
## The Problem
|
|
26
27
|
|
|
@@ -89,6 +90,17 @@ grov disable # Disable grov
|
|
|
89
90
|
- **Per-project:** Context is filtered by project path
|
|
90
91
|
- **Local by default:** Memories stay on your machine unless you enable team sync
|
|
91
92
|
|
|
93
|
+
### Why Sync to Dashboard?
|
|
94
|
+
|
|
95
|
+
Local memories work great for solo use. The dashboard unlocks:
|
|
96
|
+
|
|
97
|
+
- **Search across all sessions** - Hybrid semantic + keyword search
|
|
98
|
+
- **Team sharing** - What one dev's AI learns, everyone's AI knows
|
|
99
|
+
- **Cross-device sync** - Switch machines without losing context
|
|
100
|
+
- **Browse & manage** - Visual interface for all captured reasoning
|
|
101
|
+
|
|
102
|
+
[Open Dashboard](https://app.grov.dev)
|
|
103
|
+
|
|
92
104
|
## Team Sync
|
|
93
105
|
|
|
94
106
|
Share memories across your engineering team with the cloud dashboard.
|
|
@@ -100,7 +112,7 @@ grov sync --enable --team ID # Enable sync for a team
|
|
|
100
112
|
|
|
101
113
|
Once enabled, memories automatically sync to [app.grov.dev](https://app.grov.dev) where your team can:
|
|
102
114
|
- Browse all captured reasoning
|
|
103
|
-
-
|
|
115
|
+
- **Hybrid search** - semantic (AI understands meaning) + lexical (keyword matching)
|
|
104
116
|
- Invite team members
|
|
105
117
|
- See who learned what
|
|
106
118
|
|
|
@@ -209,7 +221,7 @@ YOU MAY SKIP EXPLORE AGENTS for files mentioned above.
|
|
|
209
221
|
- [x] Anti-drift detection & correction
|
|
210
222
|
- [x] Team sync (cloud backend)
|
|
211
223
|
- [x] Web dashboard
|
|
212
|
-
- [
|
|
224
|
+
- [x] Hybrid search (semantic + lexical)
|
|
213
225
|
- [ ] VS Code extension
|
|
214
226
|
|
|
215
227
|
## Contributing
|
|
@@ -32,7 +32,6 @@ export interface TaskAnalysis {
|
|
|
32
32
|
task_type: 'information' | 'planning' | 'implementation';
|
|
33
33
|
action: 'continue' | 'new_task' | 'subtask' | 'parallel_task' | 'task_complete' | 'subtask_complete';
|
|
34
34
|
task_id: string;
|
|
35
|
-
current_goal: string;
|
|
36
35
|
parent_task_id?: string;
|
|
37
36
|
reasoning: string;
|
|
38
37
|
step_reasoning?: string;
|
|
@@ -311,7 +311,6 @@ Return a JSON object with these fields:
|
|
|
311
311
|
- task_type: one of "information", "planning", or "implementation"
|
|
312
312
|
- action: one of "continue", "task_complete", "new_task", or "subtask_complete"
|
|
313
313
|
- task_id: existing session_id "${currentSession?.session_id || 'NEW'}" or "NEW" for new task
|
|
314
|
-
- current_goal: the goal based on the latest user message
|
|
315
314
|
- reasoning: brief explanation of why you made this decision${compressionInstruction}
|
|
316
315
|
</output>
|
|
317
316
|
|
|
@@ -481,7 +480,7 @@ RESPONSE RULES:
|
|
|
481
480
|
if (!needsCompression && assistantResponse.length > 0) {
|
|
482
481
|
analysis.step_reasoning = assistantResponse.substring(0, 1000);
|
|
483
482
|
}
|
|
484
|
-
debugLLM('analyzeTaskContext', `Result: task_type=${analysis.task_type}, action=${analysis.action},
|
|
483
|
+
debugLLM('analyzeTaskContext', `Result: task_type=${analysis.task_type}, action=${analysis.action}, reasoning="${analysis.reasoning?.substring(0, 150) || 'none'}"`);
|
|
485
484
|
return analysis;
|
|
486
485
|
}
|
|
487
486
|
catch (parseError) {
|
|
@@ -491,7 +490,6 @@ RESPONSE RULES:
|
|
|
491
490
|
task_type: 'implementation',
|
|
492
491
|
action: currentSession ? 'continue' : 'new_task',
|
|
493
492
|
task_id: currentSession?.session_id || 'NEW',
|
|
494
|
-
current_goal: latestUserMessage.substring(0, 200),
|
|
495
493
|
reasoning: 'Fallback due to parse error',
|
|
496
494
|
step_reasoning: assistantResponse.substring(0, 1000),
|
|
497
495
|
};
|
package/dist/proxy/server.js
CHANGED
|
@@ -406,10 +406,21 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
406
406
|
else if (!activeSession) {
|
|
407
407
|
// First request, create session without task analysis
|
|
408
408
|
const newSessionId = randomUUID();
|
|
409
|
+
// Extract clean goal summary instead of using raw text
|
|
410
|
+
let goalSummary = latestUserMessage.substring(0, 500) || 'Task in progress';
|
|
411
|
+
if (isIntentExtractionAvailable() && latestUserMessage.length > 10) {
|
|
412
|
+
try {
|
|
413
|
+
const intentData = await extractIntent(latestUserMessage);
|
|
414
|
+
goalSummary = intentData.goal;
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
// Keep fallback goalSummary
|
|
418
|
+
}
|
|
419
|
+
}
|
|
409
420
|
activeSession = createSessionState({
|
|
410
421
|
session_id: newSessionId,
|
|
411
422
|
project_path: sessionInfo.projectPath,
|
|
412
|
-
original_goal:
|
|
423
|
+
original_goal: goalSummary,
|
|
413
424
|
task_type: 'main',
|
|
414
425
|
});
|
|
415
426
|
activeSessionId = newSessionId;
|
|
@@ -432,7 +443,6 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
432
443
|
msg: 'Task analysis',
|
|
433
444
|
action: taskAnalysis.action,
|
|
434
445
|
task_type: taskAnalysis.task_type,
|
|
435
|
-
goal: taskAnalysis.current_goal?.substring(0, 50),
|
|
436
446
|
reasoning: taskAnalysis.reasoning,
|
|
437
447
|
});
|
|
438
448
|
// TASK LOG: Analysis result
|
|
@@ -440,7 +450,6 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
440
450
|
sessionId: sessionInfo.sessionId,
|
|
441
451
|
action: taskAnalysis.action,
|
|
442
452
|
task_type: taskAnalysis.task_type,
|
|
443
|
-
goal: taskAnalysis.current_goal || '',
|
|
444
453
|
reasoning: taskAnalysis.reasoning || '',
|
|
445
454
|
userMessage: latestUserMessage.substring(0, 80),
|
|
446
455
|
hasCurrentSession: !!sessionInfo.currentSession,
|
|
@@ -464,22 +473,11 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
464
473
|
if (sessionInfo.currentSession) {
|
|
465
474
|
activeSessionId = sessionInfo.currentSession.session_id;
|
|
466
475
|
activeSession = sessionInfo.currentSession;
|
|
467
|
-
// Update goal if Haiku detected a new instruction from user
|
|
468
|
-
// (same task/topic, but new specific instruction)
|
|
469
|
-
if (taskAnalysis.current_goal &&
|
|
470
|
-
taskAnalysis.current_goal !== activeSession.original_goal &&
|
|
471
|
-
latestUserMessage.length > 30) {
|
|
472
|
-
updateSessionState(activeSessionId, {
|
|
473
|
-
original_goal: taskAnalysis.current_goal,
|
|
474
|
-
});
|
|
475
|
-
activeSession.original_goal = taskAnalysis.current_goal;
|
|
476
|
-
}
|
|
477
476
|
// TASK LOG: Continue existing session
|
|
478
477
|
taskLog('ORCHESTRATION_CONTINUE', {
|
|
479
478
|
sessionId: activeSessionId,
|
|
480
479
|
source: 'current_session',
|
|
481
480
|
goal: activeSession.original_goal,
|
|
482
|
-
goalUpdated: taskAnalysis.current_goal !== activeSession.original_goal,
|
|
483
481
|
});
|
|
484
482
|
}
|
|
485
483
|
else if (sessionInfo.completedSession) {
|
|
@@ -488,7 +486,6 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
488
486
|
activeSession = sessionInfo.completedSession;
|
|
489
487
|
updateSessionState(activeSessionId, {
|
|
490
488
|
status: 'active',
|
|
491
|
-
original_goal: taskAnalysis.current_goal || activeSession.original_goal,
|
|
492
489
|
});
|
|
493
490
|
activeSession.status = 'active';
|
|
494
491
|
activeSessions.set(activeSessionId, {
|
|
@@ -513,7 +510,7 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
513
510
|
}
|
|
514
511
|
// Extract full intent for new task (goal, scope, constraints, keywords)
|
|
515
512
|
let intentData = {
|
|
516
|
-
goal:
|
|
513
|
+
goal: latestUserMessage.substring(0, 500),
|
|
517
514
|
expected_scope: [],
|
|
518
515
|
constraints: [],
|
|
519
516
|
keywords: [],
|
|
@@ -600,7 +597,7 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
600
597
|
case 'subtask': {
|
|
601
598
|
// Extract intent for subtask
|
|
602
599
|
let intentData = {
|
|
603
|
-
goal:
|
|
600
|
+
goal: latestUserMessage.substring(0, 500),
|
|
604
601
|
expected_scope: [],
|
|
605
602
|
constraints: [],
|
|
606
603
|
keywords: [],
|
|
@@ -650,7 +647,7 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
650
647
|
case 'parallel_task': {
|
|
651
648
|
// Extract intent for parallel task
|
|
652
649
|
let intentData = {
|
|
653
|
-
goal:
|
|
650
|
+
goal: latestUserMessage.substring(0, 500),
|
|
654
651
|
expected_scope: [],
|
|
655
652
|
constraints: [],
|
|
656
653
|
keywords: [],
|
|
@@ -748,10 +745,21 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
748
745
|
// Example: user asks clarification question, answer is provided in single turn
|
|
749
746
|
try {
|
|
750
747
|
const newSessionId = randomUUID();
|
|
748
|
+
// Extract clean goal summary instead of using raw text
|
|
749
|
+
let goalSummary = latestUserMessage.substring(0, 500);
|
|
750
|
+
if (isIntentExtractionAvailable() && latestUserMessage.length > 10) {
|
|
751
|
+
try {
|
|
752
|
+
const intentData = await extractIntent(latestUserMessage);
|
|
753
|
+
goalSummary = intentData.goal;
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
756
|
+
// Keep fallback goalSummary
|
|
757
|
+
}
|
|
758
|
+
}
|
|
751
759
|
const instantSession = createSessionState({
|
|
752
760
|
session_id: newSessionId,
|
|
753
761
|
project_path: sessionInfo.projectPath,
|
|
754
|
-
original_goal:
|
|
762
|
+
original_goal: goalSummary,
|
|
755
763
|
task_type: 'main',
|
|
756
764
|
});
|
|
757
765
|
// Set final_response for reasoning extraction
|
|
@@ -764,7 +772,7 @@ async function postProcessResponse(response, sessionInfo, requestBody, logger, e
|
|
|
764
772
|
// TASK LOG: Instant complete (new task that finished in one turn)
|
|
765
773
|
taskLog('ORCHESTRATION_TASK_COMPLETE', {
|
|
766
774
|
sessionId: newSessionId,
|
|
767
|
-
goal:
|
|
775
|
+
goal: goalSummary,
|
|
768
776
|
source: 'instant_complete',
|
|
769
777
|
});
|
|
770
778
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grov",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Collective AI memory for Claude Code - captures reasoning from sessions and injects context into future sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"dist/**/*.d.ts",
|
|
14
14
|
"!dist/**/*.test.js",
|
|
15
15
|
"!dist/**/*.test.d.ts",
|
|
16
|
+
"postinstall.js",
|
|
16
17
|
"README.md",
|
|
17
18
|
"LICENSE"
|
|
18
19
|
],
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"test:watch": "vitest",
|
|
35
36
|
"test:all": "turbo run test",
|
|
36
37
|
"typecheck": "turbo run typecheck",
|
|
38
|
+
"postinstall": "node postinstall.js",
|
|
37
39
|
"prepublishOnly": "npm run build && npm test && npm run security:scan",
|
|
38
40
|
"prepare": "husky",
|
|
39
41
|
"security:scan": "./scripts/scan-secrets.sh"
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const green = '\x1b[32m';
|
|
4
|
+
const cyan = '\x1b[36m';
|
|
5
|
+
const dim = '\x1b[2m';
|
|
6
|
+
const reset = '\x1b[0m';
|
|
7
|
+
const bold = '\x1b[1m';
|
|
8
|
+
|
|
9
|
+
console.log(`
|
|
10
|
+
${green}✓${reset} ${bold}grov installed successfully${reset}
|
|
11
|
+
|
|
12
|
+
${dim}Sync your AI memories across your team:${reset}
|
|
13
|
+
${cyan}https://app.grov.dev${reset}
|
|
14
|
+
|
|
15
|
+
${dim}Quick start:${reset}
|
|
16
|
+
${green}grov init${reset} Configure proxy
|
|
17
|
+
${green}grov proxy${reset} Start capturing
|
|
18
|
+
${green}grov login${reset} Connect to dashboard
|
|
19
|
+
`);
|