grov 0.5.4 → 0.5.6
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 +147 -114
- package/dist/cli.js +8 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +88 -0
- package/dist/commands/login.js +66 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center"><strong>Collective AI memory for engineering teams.</strong></p>
|
|
8
8
|
|
|
9
|
+
<p align="center"><em>When one dev's Claude figures something out, every dev's Claude knows it.</em></p>
|
|
10
|
+
|
|
9
11
|
<p align="center">
|
|
10
12
|
<a href="https://www.npmjs.com/package/grov"><img src="https://img.shields.io/npm/v/grov" alt="npm version"></a>
|
|
11
13
|
<a href="https://www.npmjs.com/package/grov"><img src="https://img.shields.io/npm/dm/grov" alt="npm downloads"></a>
|
|
@@ -17,35 +19,47 @@
|
|
|
17
19
|
<a href="https://grov.dev">Website</a> •
|
|
18
20
|
<a href="https://app.grov.dev">Dashboard</a> •
|
|
19
21
|
<a href="#quick-start">Quick Start</a> •
|
|
20
|
-
<a href="#
|
|
22
|
+
<a href="#features">Features</a> •
|
|
21
23
|
<a href="#contributing">Contributing</a>
|
|
22
24
|
</p>
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
---
|
|
25
27
|
|
|
26
28
|
## The Problem
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
- Claude re-explores your codebase from scratch
|
|
30
|
-
- It reads the same files again
|
|
31
|
-
- It rediscovers patterns you've already established
|
|
32
|
-
- You burn tokens on redundant exploration
|
|
33
|
-
|
|
34
|
-
**Measured impact:** A typical task takes 10+ minutes, 7%+ token usage, and 3+ explore agents just to understand the codebase.*
|
|
30
|
+
Your team's AI agents are learning in silos.
|
|
35
31
|
|
|
36
|
-
|
|
32
|
+
- Dev A's Claude spends 10 minutes understanding your auth system
|
|
33
|
+
- Dev B's Claude does the exact same exploration the next day
|
|
34
|
+
- Dev C asks a question that was already answered last week
|
|
35
|
+
- Every new session starts from zero
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
**The waste:** Redundant exploration, duplicate token spend, knowledge that disappears when sessions end.
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
## The Solution
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
Grov captures what your team's AI learns and shares it automatically.
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
```
|
|
44
|
+
Dev A: "How does auth work in this codebase?"
|
|
45
|
+
↓
|
|
46
|
+
Claude investigates, figures it out
|
|
47
|
+
↓
|
|
48
|
+
Grov captures the reasoning + decisions
|
|
49
|
+
↓
|
|
50
|
+
Syncs to team dashboard
|
|
51
|
+
↓
|
|
52
|
+
Dev B: "Should we add password salting?"
|
|
53
|
+
↓
|
|
54
|
+
Claude already knows: "Based on verified team knowledge,
|
|
55
|
+
no - this codebase uses OAuth-only, no passwords stored"
|
|
56
|
+
↓
|
|
57
|
+
No exploration needed. Instant expert answer.
|
|
58
|
+
```
|
|
45
59
|
|
|
46
|
-
|
|
60
|
+
**Measured impact:** Tasks drop from 10+ minutes to 1-2 minutes when team context is available.
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
---
|
|
49
63
|
|
|
50
64
|
## Quick Start
|
|
51
65
|
|
|
@@ -57,83 +71,60 @@ grov proxy # Start (keep running)
|
|
|
57
71
|
|
|
58
72
|
Then use Claude Code normally in another terminal. That's it.
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
Session 1: Claude learns about your auth system
|
|
64
|
-
↓
|
|
65
|
-
grov captures: "Auth tokens refresh in middleware/token.ts:45,
|
|
66
|
-
using 15-min window to handle long forms"
|
|
67
|
-
↓
|
|
68
|
-
Session 2: User asks about related feature
|
|
69
|
-
↓
|
|
70
|
-
grov injects: Previous context about auth
|
|
71
|
-
↓
|
|
72
|
-
Claude skips exploration, reads files directly
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Commands
|
|
76
|
-
|
|
74
|
+
For team sync:
|
|
77
75
|
```bash
|
|
78
|
-
grov
|
|
79
|
-
grov
|
|
80
|
-
grov proxy-status # Show active sessions
|
|
81
|
-
grov status # Show captured tasks
|
|
82
|
-
grov login # Login to cloud dashboard
|
|
83
|
-
grov sync # Sync memories to team dashboard
|
|
84
|
-
grov disable # Disable grov
|
|
76
|
+
grov login # Authenticate via GitHub
|
|
77
|
+
grov sync --enable --team ID # Enable sync for your team
|
|
85
78
|
```
|
|
86
79
|
|
|
87
|
-
|
|
80
|
+
**Free for individuals and teams up to 3 developers.**
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
- **Per-project:** Context is filtered by project path
|
|
91
|
-
- **Local by default:** Memories stay on your machine unless you enable team sync
|
|
82
|
+
---
|
|
92
83
|
|
|
93
|
-
|
|
84
|
+
## What Gets Captured
|
|
94
85
|
|
|
95
|
-
|
|
86
|
+
Real reasoning, not just file lists:
|
|
96
87
|
|
|
97
|
-
|
|
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
|
|
88
|
+

|
|
101
89
|
|
|
102
|
-
|
|
90
|
+
*Architectural decisions, patterns, and rationale - automatically extracted and synced to your team.*
|
|
103
91
|
|
|
104
|
-
|
|
92
|
+
Every captured memory includes:
|
|
93
|
+
- **Reasoning trace** - The WHY behind decisions (CONCLUSION/INSIGHT pairs)
|
|
94
|
+
- **Key decisions** - What was chosen and why alternatives were rejected
|
|
95
|
+
- **Files touched** - Which parts of the codebase are relevant
|
|
96
|
+
- **Constraints discovered** - What can't break, what must stay compatible
|
|
105
97
|
|
|
106
|
-
|
|
98
|
+
---
|
|
107
99
|
|
|
108
|
-
|
|
109
|
-
grov login # Authenticate via GitHub
|
|
110
|
-
grov sync --enable --team ID # Enable sync for a team
|
|
111
|
-
```
|
|
100
|
+
## What Your Team Gets
|
|
112
101
|
|
|
113
|
-
|
|
114
|
-
- Browse all captured reasoning
|
|
115
|
-
- **Hybrid search** - semantic (AI understands meaning) + lexical (keyword matching)
|
|
116
|
-
- Invite team members
|
|
117
|
-
- See who learned what
|
|
102
|
+
When a teammate asks a related question, Claude already knows:
|
|
118
103
|
|
|
119
|
-
|
|
104
|
+

|
|
120
105
|
|
|
121
|
-
|
|
106
|
+
*No exploration. No re-investigation. Instant expert answers from team memory.*
|
|
122
107
|
|
|
123
|
-
-
|
|
124
|
-
- Claude Code
|
|
108
|
+
Claude receives verified context and skips the exploration phase entirely - no "let me investigate" or "I'll need to look at the codebase."
|
|
125
109
|
|
|
126
110
|
---
|
|
127
111
|
|
|
128
|
-
##
|
|
112
|
+
## Features
|
|
129
113
|
|
|
130
|
-
###
|
|
114
|
+
### Team Knowledge Sharing
|
|
115
|
+
The core value: what one dev's AI learns, everyone's AI knows.
|
|
116
|
+
|
|
117
|
+
- **Automatic capture** - Reasoning extracted when tasks complete
|
|
118
|
+
- **Automatic sync** - Memories sync to your team in real-time
|
|
119
|
+
- **Automatic injection** - Relevant context injected into new sessions
|
|
120
|
+
- **Hybrid search** - Semantic (AI understands meaning) + lexical (keyword matching)
|
|
131
121
|
|
|
132
|
-
|
|
122
|
+
### Anti-Drift Detection
|
|
123
|
+
Grov monitors what Claude **does** (not what you ask) and corrects when it goes off-track.
|
|
133
124
|
|
|
134
125
|
- Extracts your intent from the first prompt
|
|
135
126
|
- Monitors Claude's actions (file edits, commands, explorations)
|
|
136
|
-
-
|
|
127
|
+
- Scores alignment (1-10) using Claude Haiku
|
|
137
128
|
- Injects corrections at 4 levels: nudge → correct → intervene → halt
|
|
138
129
|
|
|
139
130
|
```bash
|
|
@@ -141,25 +132,85 @@ Grov monitors what Claude **does** (not what you ask) and corrects if it drifts
|
|
|
141
132
|
grov drift-test "refactor the auth system" --goal "fix login bug"
|
|
142
133
|
```
|
|
143
134
|
|
|
144
|
-
### Extended Cache
|
|
145
|
-
|
|
146
|
-
Anthropic's prompt cache expires after 5 minutes of inactivity. If you pause to think between prompts, the cache expires and must be recreated (costs more, takes longer).
|
|
135
|
+
### Extended Cache
|
|
136
|
+
Anthropic's prompt cache expires after 5 minutes of inactivity. Grov keeps it warm.
|
|
147
137
|
|
|
148
138
|
```bash
|
|
149
139
|
grov proxy --extended-cache
|
|
150
140
|
```
|
|
151
141
|
|
|
152
|
-
|
|
142
|
+
- Sends minimal keep-alive requests (~$0.002 each) during idle periods
|
|
143
|
+
- Saves ~$0.18 per idle period by avoiding cache recreation
|
|
144
|
+
- Your next prompt is faster and cheaper
|
|
145
|
+
|
|
146
|
+
**Opt-in only.** By using `--extended-cache`, you consent to Grov making API requests on your behalf.
|
|
147
|
+
|
|
148
|
+
### Auto-Compaction
|
|
149
|
+
When your context window fills up, Grov automatically compacts while preserving what matters.
|
|
150
|
+
|
|
151
|
+
- Pre-computes summary at 85% capacity
|
|
152
|
+
- Preserves: original goal, key decisions with reasoning, current state, next steps
|
|
153
|
+
- Drops: verbose exploration, redundant file reads, superseded reasoning
|
|
154
|
+
- Claude continues seamlessly without losing important context
|
|
155
|
+
|
|
156
|
+
No manual `/compact` needed. No lost reasoning.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Commands
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
grov init # Configure proxy URL (one-time)
|
|
164
|
+
grov proxy # Start the proxy (required)
|
|
165
|
+
grov proxy-status # Show active sessions
|
|
166
|
+
grov status # Show captured tasks
|
|
167
|
+
grov login # Login to cloud dashboard
|
|
168
|
+
grov sync # Sync memories to team dashboard
|
|
169
|
+
grov disable # Disable grov
|
|
170
|
+
grov drift-test # Test drift detection
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
153
174
|
|
|
154
|
-
|
|
155
|
-
- Use your Anthropic API key
|
|
156
|
-
- Are sent automatically during idle periods (every ~4 minutes)
|
|
157
|
-
- Cost approximately $0.002 per keep-alive
|
|
158
|
-
- Are discarded (not added to your conversation)
|
|
175
|
+
## How It Works
|
|
159
176
|
|
|
160
|
-
|
|
177
|
+
```
|
|
178
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
179
|
+
│ Claude Code │
|
|
180
|
+
│ │ │
|
|
181
|
+
│ ▼ │
|
|
182
|
+
│ Grov Proxy (localhost:8080) │
|
|
183
|
+
│ │ │
|
|
184
|
+
│ ├──► Inject team memory from past sessions │
|
|
185
|
+
│ ├──► Forward to Anthropic API │
|
|
186
|
+
│ ├──► Monitor for drift, inject corrections │
|
|
187
|
+
│ ├──► Track context usage, auto-compact if needed │
|
|
188
|
+
│ └──► Capture reasoning when task completes │
|
|
189
|
+
│ │ │
|
|
190
|
+
│ ▼ │
|
|
191
|
+
│ Team Dashboard (app.grov.dev) │
|
|
192
|
+
│ │ │
|
|
193
|
+
│ ▼ │
|
|
194
|
+
│ Available to entire team │
|
|
195
|
+
└─────────────────────────────────────────────────────────────┘
|
|
196
|
+
```
|
|
161
197
|
|
|
162
|
-
|
|
198
|
+
**Local by default:** Memories stay on your machine in `~/.grov/memory.db` (SQLite) unless you enable team sync.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Dashboard
|
|
203
|
+
|
|
204
|
+
Browse, search, and manage your team's AI knowledge at [app.grov.dev](https://app.grov.dev).
|
|
205
|
+
|
|
206
|
+
- **Search across all sessions** - Hybrid semantic + keyword search
|
|
207
|
+
- **Browse reasoning traces** - See the WHY behind every decision
|
|
208
|
+
- **Team visibility** - See who learned what, when
|
|
209
|
+
- **Invite teammates** - Share knowledge automatically
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Environment Variables
|
|
163
214
|
|
|
164
215
|
```bash
|
|
165
216
|
# Required for drift detection and LLM extraction
|
|
@@ -171,45 +222,21 @@ export PROXY_HOST=127.0.0.1 # Proxy host
|
|
|
171
222
|
export PROXY_PORT=8080 # Proxy port
|
|
172
223
|
```
|
|
173
224
|
|
|
174
|
-
Without an API key,
|
|
175
|
-
|
|
176
|
-
### What Gets Stored
|
|
177
|
-
|
|
178
|
-
```json
|
|
179
|
-
{
|
|
180
|
-
"task": "Fix auth logout bug",
|
|
181
|
-
"goal": "Prevent random user logouts",
|
|
182
|
-
"files_touched": ["src/auth/session.ts", "src/middleware/token.ts"],
|
|
183
|
-
"reasoning_trace": [
|
|
184
|
-
"Investigated token refresh logic",
|
|
185
|
-
"Found refresh window was too short",
|
|
186
|
-
"Extended from 5min to 15min"
|
|
187
|
-
],
|
|
188
|
-
"status": "complete"
|
|
189
|
-
}
|
|
190
|
-
```
|
|
225
|
+
Without an API key, Grov uses basic extraction and disables drift detection.
|
|
191
226
|
|
|
192
|
-
|
|
227
|
+
---
|
|
193
228
|
|
|
194
|
-
|
|
195
|
-
VERIFIED CONTEXT FROM PREVIOUS SESSIONS:
|
|
229
|
+
## Requirements
|
|
196
230
|
|
|
197
|
-
|
|
198
|
-
-
|
|
199
|
-
- Extended token refresh window from 5min to 15min
|
|
200
|
-
- Reason: Users were getting logged out during long forms
|
|
231
|
+
- Node.js 18+
|
|
232
|
+
- Claude Code
|
|
201
233
|
|
|
202
|
-
|
|
203
|
-
```
|
|
234
|
+
---
|
|
204
235
|
|
|
205
|
-
|
|
236
|
+
## Pricing
|
|
206
237
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
- Extracts intent from first prompt
|
|
210
|
-
- Injects context from team memory
|
|
211
|
-
- Tracks actions and detects drift
|
|
212
|
-
- Saves reasoning when tasks complete
|
|
238
|
+
- **Free** - Individuals and teams up to 3 developers
|
|
239
|
+
- **Team** - Larger teams with additional features (coming soon)
|
|
213
240
|
|
|
214
241
|
---
|
|
215
242
|
|
|
@@ -222,8 +249,12 @@ YOU MAY SKIP EXPLORE AGENTS for files mentioned above.
|
|
|
222
249
|
- [x] Team sync (cloud backend)
|
|
223
250
|
- [x] Web dashboard
|
|
224
251
|
- [x] Hybrid search (semantic + lexical)
|
|
252
|
+
- [x] Extended cache (keep prompt cache warm)
|
|
253
|
+
- [x] Auto-compaction with reasoning preservation
|
|
225
254
|
- [ ] VS Code extension
|
|
226
255
|
|
|
256
|
+
---
|
|
257
|
+
|
|
227
258
|
## Contributing
|
|
228
259
|
|
|
229
260
|
1. **Fork the repo** and clone locally
|
|
@@ -238,6 +269,8 @@ node dist/cli.js init # Test CLI
|
|
|
238
269
|
|
|
239
270
|
Found a bug? [Open an issue](https://github.com/TonyStef/Grov/issues).
|
|
240
271
|
|
|
272
|
+
---
|
|
273
|
+
|
|
241
274
|
## License
|
|
242
275
|
|
|
243
276
|
Apache License 2.0 - see [LICENSE](LICENSE) file for details.
|
package/dist/cli.js
CHANGED
|
@@ -127,4 +127,12 @@ program
|
|
|
127
127
|
const { sync } = await import('./commands/sync.js');
|
|
128
128
|
await sync(options);
|
|
129
129
|
}));
|
|
130
|
+
// grov doctor - Diagnose setup issues
|
|
131
|
+
program
|
|
132
|
+
.command('doctor')
|
|
133
|
+
.description('Check grov setup and diagnose issues')
|
|
134
|
+
.action(safeAction(async () => {
|
|
135
|
+
const { doctor } = await import('./commands/doctor.js');
|
|
136
|
+
await doctor();
|
|
137
|
+
}));
|
|
130
138
|
program.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function doctor(): Promise<void>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// grov doctor - Check setup and diagnose issues
|
|
2
|
+
import { existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { request } from 'undici';
|
|
6
|
+
import { readCredentials, getSyncStatus } from '../lib/credentials.js';
|
|
7
|
+
import { initDatabase } from '../lib/store/database.js';
|
|
8
|
+
const CLAUDE_SETTINGS_PATH = join(homedir(), '.claude', 'settings.json');
|
|
9
|
+
const DB_PATH = join(homedir(), '.grov', 'memory.db');
|
|
10
|
+
export async function doctor() {
|
|
11
|
+
console.log('\nGrov Doctor');
|
|
12
|
+
console.log('===========\n');
|
|
13
|
+
// Check proxy
|
|
14
|
+
const proxyRunning = await checkProxy();
|
|
15
|
+
printCheck('Proxy', proxyRunning, 'Running on port 8080', 'Not running', 'grov proxy');
|
|
16
|
+
// Check Claude settings
|
|
17
|
+
const baseUrlConfigured = checkBaseUrl();
|
|
18
|
+
printCheck('ANTHROPIC_BASE_URL', baseUrlConfigured, 'Configured for proxy', 'Not configured', 'grov init');
|
|
19
|
+
// Check API key
|
|
20
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
21
|
+
const hasApiKey = !!(apiKey && apiKey.length > 10);
|
|
22
|
+
const apiKeyFix = process.platform === 'win32'
|
|
23
|
+
? 'set ANTHROPIC_API_KEY=sk-ant-...'
|
|
24
|
+
: 'export ANTHROPIC_API_KEY=sk-ant-... (add to shell profile)';
|
|
25
|
+
printCheck('ANTHROPIC_API_KEY', hasApiKey, 'Set', 'Not set (task detection disabled)', apiKeyFix);
|
|
26
|
+
// Check login
|
|
27
|
+
const creds = readCredentials();
|
|
28
|
+
printCheck('Login', !!creds, creds ? `Logged in as ${creds.email}` : 'Not logged in', 'Not logged in', 'grov login');
|
|
29
|
+
// Check sync
|
|
30
|
+
const syncStatus = getSyncStatus();
|
|
31
|
+
const syncOk = syncStatus?.enabled && syncStatus?.teamId;
|
|
32
|
+
const syncMsg = syncOk
|
|
33
|
+
? `Team ${syncStatus.teamId.substring(0, 8)}...`
|
|
34
|
+
: syncStatus?.teamId ? 'Disabled' : 'No team';
|
|
35
|
+
printCheck('Cloud Sync', !!syncOk, syncMsg, syncMsg, 'grov sync --enable --team <id>');
|
|
36
|
+
// Check database
|
|
37
|
+
const dbStats = checkDatabase();
|
|
38
|
+
const dbOk = dbStats.tasks > 0 || dbStats.sessions > 0;
|
|
39
|
+
const dbMsg = `${dbStats.tasks} tasks, ${dbStats.unsynced} unsynced, ${dbStats.sessions} active`;
|
|
40
|
+
printCheck('Local Database', dbOk, dbMsg, 'Empty', 'Use Claude Code with proxy running');
|
|
41
|
+
console.log('');
|
|
42
|
+
}
|
|
43
|
+
async function checkProxy() {
|
|
44
|
+
try {
|
|
45
|
+
const res = await request('http://127.0.0.1:8080/health', {
|
|
46
|
+
headersTimeout: 2000,
|
|
47
|
+
bodyTimeout: 2000
|
|
48
|
+
});
|
|
49
|
+
return res.statusCode === 200;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function checkBaseUrl() {
|
|
56
|
+
if (!existsSync(CLAUDE_SETTINGS_PATH))
|
|
57
|
+
return false;
|
|
58
|
+
try {
|
|
59
|
+
const settings = JSON.parse(readFileSync(CLAUDE_SETTINGS_PATH, 'utf-8'));
|
|
60
|
+
return settings.env?.ANTHROPIC_BASE_URL === 'http://127.0.0.1:8080';
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function checkDatabase() {
|
|
67
|
+
if (!existsSync(DB_PATH)) {
|
|
68
|
+
return { tasks: 0, unsynced: 0, sessions: 0 };
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const db = initDatabase();
|
|
72
|
+
const tasks = db.prepare('SELECT COUNT(*) as c FROM tasks').get().c;
|
|
73
|
+
const unsynced = db.prepare('SELECT COUNT(*) as c FROM tasks WHERE synced_at IS NULL').get().c;
|
|
74
|
+
const sessions = db.prepare("SELECT COUNT(*) as c FROM session_states WHERE status = 'active'").get().c;
|
|
75
|
+
return { tasks, unsynced, sessions };
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return { tasks: 0, unsynced: 0, sessions: 0 };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function printCheck(name, ok, successMsg, failMsg, fix) {
|
|
82
|
+
const icon = ok ? '\x1b[32m✓\x1b[0m' : '\x1b[31m✗\x1b[0m';
|
|
83
|
+
const msg = ok ? successMsg : failMsg;
|
|
84
|
+
console.log(`${icon} ${name}: ${msg}`);
|
|
85
|
+
if (!ok) {
|
|
86
|
+
console.log(` \x1b[90m→ ${fix}\x1b[0m`);
|
|
87
|
+
}
|
|
88
|
+
}
|
package/dist/commands/login.js
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
// Login command - Device authorization flow
|
|
2
2
|
// Authenticates CLI with Grov cloud using OAuth-like device flow
|
|
3
3
|
import open from 'open';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
4
|
+
import * as readline from 'readline';
|
|
5
|
+
import { writeCredentials, isAuthenticated, readCredentials, setTeamId, setSyncEnabled } from '../lib/credentials.js';
|
|
6
|
+
import { startDeviceFlow, pollDeviceFlow, sleep, fetchTeams } from '../lib/api-client.js';
|
|
7
|
+
/**
|
|
8
|
+
* Prompt user for input
|
|
9
|
+
*/
|
|
10
|
+
function prompt(question) {
|
|
11
|
+
const rl = readline.createInterface({
|
|
12
|
+
input: process.stdin,
|
|
13
|
+
output: process.stdout,
|
|
14
|
+
});
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
rl.question(question, (answer) => {
|
|
17
|
+
rl.close();
|
|
18
|
+
resolve(answer.trim().toLowerCase());
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
6
22
|
/**
|
|
7
23
|
* Decode JWT payload to extract user info
|
|
8
24
|
*/
|
|
@@ -91,16 +107,54 @@ export async function login() {
|
|
|
91
107
|
email: userInfo.email,
|
|
92
108
|
sync_enabled: false,
|
|
93
109
|
});
|
|
94
|
-
console.log('
|
|
95
|
-
|
|
96
|
-
console.log('
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
console.log('\n✓ Logged in as:', userInfo.email);
|
|
111
|
+
// Auto-setup: Fetch teams and configure sync
|
|
112
|
+
console.log('\nSetting up cloud sync...');
|
|
113
|
+
try {
|
|
114
|
+
const teams = await fetchTeams();
|
|
115
|
+
if (teams.length === 0) {
|
|
116
|
+
console.log('\n⚠ No teams found.');
|
|
117
|
+
console.log('Create one at: https://app.grov.dev/team');
|
|
118
|
+
console.log('Then run: grov sync --enable --team <team-id>\n');
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
let selectedTeam = teams[0];
|
|
122
|
+
// If multiple teams, let user choose
|
|
123
|
+
if (teams.length > 1) {
|
|
124
|
+
console.log('\nYour teams:');
|
|
125
|
+
teams.forEach((team, i) => {
|
|
126
|
+
console.log(` ${i + 1}. ${team.name} (${team.slug})`);
|
|
127
|
+
});
|
|
128
|
+
const choice = await prompt(`\nSelect team [1-${teams.length}] (default: 1): `);
|
|
129
|
+
const index = parseInt(choice, 10) - 1;
|
|
130
|
+
if (index >= 0 && index < teams.length) {
|
|
131
|
+
selectedTeam = teams[index];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Ask to enable sync (default yes)
|
|
135
|
+
const enableSync = await prompt(`Enable cloud sync to "${selectedTeam.name}"? [Y/n]: `);
|
|
136
|
+
if (enableSync !== 'n' && enableSync !== 'no') {
|
|
137
|
+
setTeamId(selectedTeam.id);
|
|
138
|
+
setSyncEnabled(true);
|
|
139
|
+
console.log('\n╔═════════════════════════════════════════╗');
|
|
140
|
+
console.log('║ ║');
|
|
141
|
+
console.log('║ ✓ Cloud sync enabled! ║');
|
|
142
|
+
console.log('║ ║');
|
|
143
|
+
console.log('╚═════════════════════════════════════════╝');
|
|
144
|
+
console.log(`\nSyncing to: ${selectedTeam.name}`);
|
|
145
|
+
console.log('\nYou\'re all set! Your AI sessions will now be saved.');
|
|
146
|
+
console.log('View them at: https://app.grov.dev/memories\n');
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.log('\n✓ Logged in. Sync not enabled.');
|
|
150
|
+
console.log('Run "grov sync --enable" later to start syncing.\n');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
console.log('\n⚠ Could not auto-configure sync.');
|
|
155
|
+
console.log('Run "grov sync --enable --team <team-id>" manually.');
|
|
156
|
+
console.log('Find your team ID at: https://app.grov.dev/team\n');
|
|
157
|
+
}
|
|
104
158
|
return;
|
|
105
159
|
}
|
|
106
160
|
if (status === 'expired') {
|
package/package.json
CHANGED