grov 0.1.2 → 0.2.3
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 +73 -88
- package/dist/cli.js +23 -37
- package/dist/commands/capture.js +1 -1
- package/dist/commands/disable.d.ts +1 -0
- package/dist/commands/disable.js +14 -0
- package/dist/commands/drift-test.js +56 -68
- package/dist/commands/init.js +29 -17
- package/dist/commands/proxy-status.d.ts +1 -0
- package/dist/commands/proxy-status.js +32 -0
- package/dist/commands/unregister.js +7 -1
- package/dist/lib/correction-builder-proxy.d.ts +16 -0
- package/dist/lib/correction-builder-proxy.js +125 -0
- package/dist/lib/correction-builder.js +1 -1
- package/dist/lib/drift-checker-proxy.d.ts +63 -0
- package/dist/lib/drift-checker-proxy.js +373 -0
- package/dist/lib/drift-checker.js +1 -1
- package/dist/lib/hooks.d.ts +11 -0
- package/dist/lib/hooks.js +33 -0
- package/dist/lib/llm-extractor.d.ts +60 -11
- package/dist/lib/llm-extractor.js +431 -98
- package/dist/lib/settings.d.ts +19 -0
- package/dist/lib/settings.js +63 -0
- package/dist/lib/store.d.ts +201 -43
- package/dist/lib/store.js +653 -90
- package/dist/proxy/action-parser.d.ts +58 -0
- package/dist/proxy/action-parser.js +196 -0
- package/dist/proxy/config.d.ts +26 -0
- package/dist/proxy/config.js +67 -0
- package/dist/proxy/forwarder.d.ts +24 -0
- package/dist/proxy/forwarder.js +119 -0
- package/dist/proxy/index.d.ts +1 -0
- package/dist/proxy/index.js +30 -0
- package/dist/proxy/request-processor.d.ts +12 -0
- package/dist/proxy/request-processor.js +120 -0
- package/dist/proxy/response-processor.d.ts +14 -0
- package/dist/proxy/response-processor.js +138 -0
- package/dist/proxy/server.d.ts +9 -0
- package/dist/proxy/server.js +904 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="landing/public/images/logos/grov-nobg.png" alt="grov logo" width="120">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
<h1 align="center">grov</h1>
|
|
2
6
|
|
|
3
7
|
<p align="center"><strong>Collective AI memory for engineering teams.</strong></p>
|
|
4
8
|
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.npmjs.com/package/grov"><img src="https://img.shields.io/npm/v/grov" alt="npm version"></a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/grov"><img src="https://img.shields.io/npm/dm/grov" alt="npm downloads"></a>
|
|
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
|
+
</p>
|
|
14
|
+
|
|
5
15
|
<p align="center">
|
|
6
16
|
<a href="https://grov.dev">Website</a> •
|
|
7
17
|
<a href="#quick-start">Quick Start</a> •
|
|
8
|
-
<a href="#
|
|
18
|
+
<a href="#advanced-features">Advanced</a> •
|
|
9
19
|
<a href="#contributing">Contributing</a>
|
|
10
20
|
</p>
|
|
11
21
|
|
|
12
|
-
Grov
|
|
22
|
+
Grov captures reasoning from your Claude Code sessions and injects it into future sessions. Your AI remembers what it learned.
|
|
13
23
|
|
|
14
24
|
## The Problem
|
|
15
25
|
|
|
@@ -19,13 +29,31 @@ Every time you start a new Claude Code session:
|
|
|
19
29
|
- It rediscovers patterns you've already established
|
|
20
30
|
- You burn tokens on redundant exploration
|
|
21
31
|
|
|
22
|
-
**Measured impact:** A typical task takes 10+ minutes, 7%+ token usage, and 3+ explore agents just to understand the codebase
|
|
32
|
+
**Measured impact:** A typical task takes 10+ minutes, 7%+ token usage, and 3+ explore agents just to understand the codebase.*
|
|
23
33
|
|
|
24
34
|
## The Solution
|
|
25
35
|
|
|
26
36
|
Grov captures what Claude learns and injects it back on the next session.
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
### What Gets Captured
|
|
41
|
+
|
|
42
|
+
Real reasoning, not just file lists:
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
*Architectural decisions, patterns, and rationale - automatically extracted.*
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g grov # Install
|
|
52
|
+
grov init # Configure (one-time)
|
|
53
|
+
grov proxy # Start (keep running)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then use Claude Code normally in another terminal. That's it.
|
|
29
57
|
|
|
30
58
|
## How It Works
|
|
31
59
|
|
|
@@ -42,83 +70,60 @@ Session 2: User asks about related feature
|
|
|
42
70
|
Claude skips exploration, reads files directly
|
|
43
71
|
```
|
|
44
72
|
|
|
45
|
-
**Zero friction.** You don't change anything about how you use Claude Code.
|
|
46
|
-
|
|
47
|
-
## Quick Start
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
# Install globally
|
|
51
|
-
npm install -g grov
|
|
52
|
-
|
|
53
|
-
# One-time setup (registers hooks in Claude Code)
|
|
54
|
-
grov init
|
|
55
|
-
|
|
56
|
-
# Done. Use Claude Code normally.
|
|
57
|
-
claude
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
That's it. Grov works invisibly in the background.
|
|
61
|
-
|
|
62
73
|
## Commands
|
|
63
74
|
|
|
64
75
|
```bash
|
|
65
|
-
grov init
|
|
66
|
-
grov
|
|
67
|
-
grov status
|
|
68
|
-
grov
|
|
69
|
-
grov
|
|
76
|
+
grov init # Configure proxy URL (one-time)
|
|
77
|
+
grov proxy # Start the proxy (required)
|
|
78
|
+
grov proxy-status # Show active sessions
|
|
79
|
+
grov status # Show captured tasks
|
|
80
|
+
grov disable # Disable grov
|
|
70
81
|
```
|
|
71
82
|
|
|
72
|
-
##
|
|
83
|
+
## Data Storage
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
- **Database:** `~/.grov/memory.db` (SQLite)
|
|
86
|
+
- **Per-project:** Context is filtered by project path
|
|
87
|
+
- **Local only:** Nothing leaves your machine
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
- Monitors Claude's actions (files touched, commands run)
|
|
80
|
-
- Detects scope drift and injects corrections if needed
|
|
81
|
-
- Smart filtering skips simple prompts ("yes", "ok", "continue")
|
|
89
|
+
## Requirements
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
- Node.js 18+
|
|
92
|
+
- Claude Code
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
- Grov parses the session's JSONL file
|
|
87
|
-
- Extracts reasoning via LLM (Claude Haiku 4.5)
|
|
88
|
-
- Stores structured summary in SQLite
|
|
94
|
+
---
|
|
89
95
|
|
|
90
|
-
|
|
96
|
+
## Advanced Features
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
### Anti-Drift Detection
|
|
93
99
|
|
|
94
|
-
Grov monitors what Claude **does** (not what you ask) and
|
|
100
|
+
Grov monitors what Claude **does** (not what you ask) and corrects if it drifts from your goal.
|
|
95
101
|
|
|
96
|
-
**How it works:**
|
|
97
102
|
- Extracts your intent from the first prompt
|
|
98
103
|
- Monitors Claude's actions (file edits, commands, explorations)
|
|
99
|
-
- Uses Claude Haiku
|
|
100
|
-
- Injects corrections at
|
|
101
|
-
|
|
102
|
-
**Key principle:** You can explore freely. Grov watches Claude's actions, not your prompts.
|
|
104
|
+
- Uses Claude Haiku to score alignment (1-10)
|
|
105
|
+
- Injects corrections at 4 levels: nudge → correct → intervene → halt
|
|
103
106
|
|
|
104
107
|
```bash
|
|
105
|
-
# Test drift detection
|
|
108
|
+
# Test drift detection
|
|
106
109
|
grov drift-test "refactor the auth system" --goal "fix login bug"
|
|
107
110
|
```
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
### Environment Variables
|
|
110
113
|
|
|
111
114
|
```bash
|
|
112
115
|
# Required for drift detection and LLM extraction
|
|
113
116
|
export ANTHROPIC_API_KEY=sk-ant-...
|
|
114
117
|
|
|
115
|
-
# Optional
|
|
116
|
-
export GROV_DRIFT_MODEL=claude-sonnet-4-20250514
|
|
118
|
+
# Optional
|
|
119
|
+
export GROV_DRIFT_MODEL=claude-sonnet-4-20250514 # Override model
|
|
120
|
+
export PROXY_HOST=127.0.0.1 # Proxy host
|
|
121
|
+
export PROXY_PORT=8080 # Proxy port
|
|
117
122
|
```
|
|
118
123
|
|
|
119
|
-
Without an API key, grov uses basic extraction
|
|
124
|
+
Without an API key, grov uses basic extraction and disables drift detection.
|
|
120
125
|
|
|
121
|
-
|
|
126
|
+
### What Gets Stored
|
|
122
127
|
|
|
123
128
|
```json
|
|
124
129
|
{
|
|
@@ -130,12 +135,11 @@ Without an API key, grov uses basic extraction (files touched, tool usage counts
|
|
|
130
135
|
"Found refresh window was too short",
|
|
131
136
|
"Extended from 5min to 15min"
|
|
132
137
|
],
|
|
133
|
-
"status": "complete"
|
|
134
|
-
"tags": ["auth", "session", "token"]
|
|
138
|
+
"status": "complete"
|
|
135
139
|
}
|
|
136
140
|
```
|
|
137
141
|
|
|
138
|
-
|
|
142
|
+
### What Gets Injected
|
|
139
143
|
|
|
140
144
|
```
|
|
141
145
|
VERIFIED CONTEXT FROM PREVIOUS SESSIONS:
|
|
@@ -146,26 +150,24 @@ VERIFIED CONTEXT FROM PREVIOUS SESSIONS:
|
|
|
146
150
|
- Reason: Users were getting logged out during long forms
|
|
147
151
|
|
|
148
152
|
YOU MAY SKIP EXPLORE AGENTS for files mentioned above.
|
|
149
|
-
Read them directly if relevant to the current task.
|
|
150
153
|
```
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
### How the Proxy Works
|
|
153
156
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
-
|
|
157
|
+
1. **`grov init`** sets `ANTHROPIC_BASE_URL=http://127.0.0.1:8080` in Claude's settings
|
|
158
|
+
2. **`grov proxy`** intercepts all API calls and:
|
|
159
|
+
- Extracts intent from first prompt
|
|
160
|
+
- Injects context from team memory
|
|
161
|
+
- Tracks actions and detects drift
|
|
162
|
+
- Saves reasoning when tasks complete
|
|
157
163
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- Node.js 18+
|
|
161
|
-
- Claude Code v2.0+
|
|
164
|
+
---
|
|
162
165
|
|
|
163
166
|
## Roadmap
|
|
164
167
|
|
|
165
168
|
- [x] Local capture & inject
|
|
166
|
-
- [x] LLM-powered extraction
|
|
167
|
-
- [x]
|
|
168
|
-
- [x] Per-prompt context injection
|
|
169
|
+
- [x] LLM-powered extraction
|
|
170
|
+
- [x] Local proxy with real-time monitoring
|
|
169
171
|
- [x] Anti-drift detection & correction
|
|
170
172
|
- [ ] Team sync (cloud backend)
|
|
171
173
|
- [ ] Web dashboard
|
|
@@ -173,34 +175,17 @@ Read them directly if relevant to the current task.
|
|
|
173
175
|
|
|
174
176
|
## Contributing
|
|
175
177
|
|
|
176
|
-
We welcome contributions! Here's how to get started:
|
|
177
|
-
|
|
178
178
|
1. **Fork the repo** and clone locally
|
|
179
179
|
2. **Install dependencies:** `npm install`
|
|
180
180
|
3. **Build:** `npm run build`
|
|
181
181
|
4. **Test locally:** `node dist/cli.js --help`
|
|
182
182
|
|
|
183
|
-
### Development
|
|
184
|
-
|
|
185
183
|
```bash
|
|
186
|
-
# Watch mode
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
# Test the CLI
|
|
190
|
-
node dist/cli.js init
|
|
191
|
-
node dist/cli.js status
|
|
184
|
+
npm run dev # Watch mode
|
|
185
|
+
node dist/cli.js init # Test CLI
|
|
192
186
|
```
|
|
193
187
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
- Keep PRs focused on a single change
|
|
197
|
-
- Follow existing code style
|
|
198
|
-
- Update tests if applicable
|
|
199
|
-
- Update docs if adding features
|
|
200
|
-
|
|
201
|
-
### Reporting Issues
|
|
202
|
-
|
|
203
|
-
Found a bug or have a feature request? [Open an issue](https://github.com/TonyStef/Grov/issues).
|
|
188
|
+
Found a bug? [Open an issue](https://github.com/TonyStef/Grov/issues).
|
|
204
189
|
|
|
205
190
|
## License
|
|
206
191
|
|
package/dist/cli.js
CHANGED
|
@@ -36,43 +36,21 @@ program
|
|
|
36
36
|
.name('grov')
|
|
37
37
|
.description('Collective AI memory for engineering teams')
|
|
38
38
|
.version('0.1.0');
|
|
39
|
-
// grov init -
|
|
39
|
+
// grov init - Configure Claude Code to use grov proxy
|
|
40
40
|
program
|
|
41
41
|
.command('init')
|
|
42
|
-
.description('
|
|
42
|
+
.description('Configure Claude Code to use grov proxy (run once)')
|
|
43
43
|
.action(safeAction(async () => {
|
|
44
44
|
const { init } = await import('./commands/init.js');
|
|
45
45
|
await init();
|
|
46
46
|
}));
|
|
47
|
-
// grov
|
|
47
|
+
// grov disable - Remove proxy configuration
|
|
48
48
|
program
|
|
49
|
-
.command('
|
|
50
|
-
.description('
|
|
51
|
-
.option('--session-dir <path>', 'Path to session directory')
|
|
52
|
-
.action(safeAction(async (options) => {
|
|
53
|
-
// SECURITY: Validate session-dir doesn't contain path traversal
|
|
54
|
-
if (options.sessionDir && options.sessionDir.includes('..')) {
|
|
55
|
-
throw new Error('Invalid session directory path');
|
|
56
|
-
}
|
|
57
|
-
const { capture } = await import('./commands/capture.js');
|
|
58
|
-
await capture(options);
|
|
59
|
-
}));
|
|
60
|
-
// grov inject - Called by SessionStart hook, outputs context JSON
|
|
61
|
-
program
|
|
62
|
-
.command('inject')
|
|
63
|
-
.description('Inject relevant context for new session (called by SessionStart hook)')
|
|
64
|
-
.option('--task <description>', 'Task description from user prompt')
|
|
65
|
-
.action(safeAction(async (options) => {
|
|
66
|
-
const { inject } = await import('./commands/inject.js');
|
|
67
|
-
await inject(options);
|
|
68
|
-
}));
|
|
69
|
-
// grov prompt-inject - Called by UserPromptSubmit hook, outputs context JSON per-turn
|
|
70
|
-
program
|
|
71
|
-
.command('prompt-inject')
|
|
72
|
-
.description('Inject context before each prompt (called by UserPromptSubmit hook)')
|
|
49
|
+
.command('disable')
|
|
50
|
+
.description('Disable grov and restore direct Anthropic connection')
|
|
73
51
|
.action(safeAction(async () => {
|
|
74
|
-
const {
|
|
75
|
-
await
|
|
52
|
+
const { disable } = await import('./commands/disable.js');
|
|
53
|
+
await disable();
|
|
76
54
|
}));
|
|
77
55
|
// grov status - Show stored reasoning for current project
|
|
78
56
|
program
|
|
@@ -83,14 +61,6 @@ program
|
|
|
83
61
|
const { status } = await import('./commands/status.js');
|
|
84
62
|
await status(options);
|
|
85
63
|
}));
|
|
86
|
-
// grov unregister - Remove hooks from Claude Code
|
|
87
|
-
program
|
|
88
|
-
.command('unregister')
|
|
89
|
-
.description('Remove grov hooks from Claude Code settings')
|
|
90
|
-
.action(safeAction(async () => {
|
|
91
|
-
const { unregister } = await import('./commands/unregister.js');
|
|
92
|
-
await unregister();
|
|
93
|
-
}));
|
|
94
64
|
// grov drift-test - Test drift detection on a prompt
|
|
95
65
|
program
|
|
96
66
|
.command('drift-test')
|
|
@@ -103,4 +73,20 @@ program
|
|
|
103
73
|
const { driftTest } = await import('./commands/drift-test.js');
|
|
104
74
|
await driftTest(prompt, options);
|
|
105
75
|
});
|
|
76
|
+
// grov proxy - Start the proxy server
|
|
77
|
+
program
|
|
78
|
+
.command('proxy')
|
|
79
|
+
.description('Start the Grov proxy server (intercepts Claude API calls)')
|
|
80
|
+
.action(async () => {
|
|
81
|
+
const { startServer } = await import('./proxy/server.js');
|
|
82
|
+
await startServer();
|
|
83
|
+
});
|
|
84
|
+
// grov proxy-status - Show active proxy sessions
|
|
85
|
+
program
|
|
86
|
+
.command('proxy-status')
|
|
87
|
+
.description('Show active proxy sessions')
|
|
88
|
+
.action(safeAction(async () => {
|
|
89
|
+
const { proxyStatus } = await import('./commands/proxy-status.js');
|
|
90
|
+
await proxyStatus();
|
|
91
|
+
}));
|
|
106
92
|
program.parse();
|
package/dist/commands/capture.js
CHANGED
|
@@ -114,7 +114,7 @@ export async function capture(options) {
|
|
|
114
114
|
if (sessionState) {
|
|
115
115
|
updateSessionState(sessionId, {
|
|
116
116
|
status: finalStatus === 'complete' ? 'completed' : 'abandoned',
|
|
117
|
-
files_explored: [...new Set([...sessionState.files_explored, ...filesTouched])],
|
|
117
|
+
files_explored: [...new Set([...(sessionState.files_explored || []), ...filesTouched])],
|
|
118
118
|
original_goal: goal,
|
|
119
119
|
});
|
|
120
120
|
debugCapture('Updated session state: %s...', sessionId.substring(0, 8));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function disable(): Promise<void>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// grov disable - Remove proxy configuration and restore direct Anthropic connection
|
|
2
|
+
import { setProxyEnv, getSettingsPath } from '../lib/settings.js';
|
|
3
|
+
export async function disable() {
|
|
4
|
+
const result = setProxyEnv(false);
|
|
5
|
+
if (result.action === 'removed') {
|
|
6
|
+
console.log('Grov disabled.');
|
|
7
|
+
console.log(' - ANTHROPIC_BASE_URL removed from settings');
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
console.log('Grov was not configured.');
|
|
11
|
+
}
|
|
12
|
+
console.log(`\nSettings file: ${getSettingsPath()}`);
|
|
13
|
+
console.log('\nClaude will now connect directly to Anthropic.');
|
|
14
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// grov drift-test - Debug command for testing drift detection
|
|
2
2
|
// Usage: grov drift-test "your prompt here" [--session <id>] [--goal "original goal"]
|
|
3
3
|
//
|
|
4
|
-
// NOTE: This command creates mock
|
|
5
|
-
// In real usage,
|
|
4
|
+
// NOTE: This command creates mock STEPS from the prompt for testing.
|
|
5
|
+
// In real usage, steps are tracked by the proxy from Claude's actions.
|
|
6
6
|
import 'dotenv/config';
|
|
7
7
|
import { getSessionState, createSessionState } from '../lib/store.js';
|
|
8
|
-
import { extractIntent
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { extractIntent } from '../lib/llm-extractor.js';
|
|
9
|
+
import { checkDrift, checkDriftBasic, isDriftCheckAvailable, scoreToCorrectionLevel } from '../lib/drift-checker-proxy.js';
|
|
10
|
+
import { buildCorrection, formatCorrectionForInjection } from '../lib/correction-builder-proxy.js';
|
|
11
11
|
export async function driftTest(prompt, options) {
|
|
12
12
|
console.log('=== GROV DRIFT TEST ===\n');
|
|
13
13
|
// Check API availability
|
|
14
|
-
const llmAvailable =
|
|
14
|
+
const llmAvailable = isDriftCheckAvailable();
|
|
15
15
|
console.log(`Anthropic API: ${llmAvailable ? 'AVAILABLE' : 'NOT AVAILABLE (using fallback)'}`);
|
|
16
16
|
console.log('');
|
|
17
17
|
// Get or create session state
|
|
@@ -27,52 +27,21 @@ export async function driftTest(prompt, options) {
|
|
|
27
27
|
console.log(`Constraints: ${intent.constraints.join(', ') || 'none'}`);
|
|
28
28
|
console.log(`Keywords: ${intent.keywords.join(', ')}`);
|
|
29
29
|
console.log('');
|
|
30
|
-
// Create temporary session state
|
|
31
|
-
sessionState = {
|
|
32
|
-
session_id: options.session || 'test-session',
|
|
30
|
+
// Create temporary session state
|
|
31
|
+
sessionState = createSessionState({
|
|
32
|
+
session_id: options.session || 'test-session-' + Date.now(),
|
|
33
33
|
project_path: process.cwd(),
|
|
34
34
|
original_goal: intent.goal,
|
|
35
|
-
actions_taken: [],
|
|
36
|
-
files_explored: [],
|
|
37
|
-
current_intent: undefined,
|
|
38
|
-
drift_warnings: [],
|
|
39
|
-
start_time: new Date().toISOString(),
|
|
40
|
-
last_update: new Date().toISOString(),
|
|
41
|
-
status: 'active',
|
|
42
35
|
expected_scope: intent.expected_scope,
|
|
43
36
|
constraints: intent.constraints,
|
|
44
|
-
success_criteria: intent.success_criteria,
|
|
45
37
|
keywords: intent.keywords,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
pending_recovery_plan: undefined,
|
|
49
|
-
drift_history: [],
|
|
50
|
-
last_checked_at: 0 // New field for action tracking
|
|
51
|
-
};
|
|
52
|
-
// Persist if session ID was provided
|
|
53
|
-
if (options.session) {
|
|
54
|
-
try {
|
|
55
|
-
createSessionState({
|
|
56
|
-
session_id: options.session,
|
|
57
|
-
project_path: process.cwd(),
|
|
58
|
-
original_goal: intent.goal,
|
|
59
|
-
expected_scope: intent.expected_scope,
|
|
60
|
-
constraints: intent.constraints,
|
|
61
|
-
success_criteria: intent.success_criteria,
|
|
62
|
-
keywords: intent.keywords
|
|
63
|
-
});
|
|
64
|
-
console.log(`Session state persisted: ${options.session}`);
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
// Might already exist, ignore
|
|
68
|
-
}
|
|
69
|
-
}
|
|
38
|
+
task_type: 'main',
|
|
39
|
+
});
|
|
70
40
|
}
|
|
71
41
|
else {
|
|
72
42
|
console.log(`Using existing session: ${options.session}`);
|
|
73
43
|
console.log(`Original goal: ${sessionState.original_goal}`);
|
|
74
44
|
console.log(`Escalation count: ${sessionState.escalation_count}`);
|
|
75
|
-
console.log(`Drift history: ${sessionState.drift_history.length} events`);
|
|
76
45
|
console.log('');
|
|
77
46
|
}
|
|
78
47
|
// Ensure sessionState is not null at this point
|
|
@@ -80,23 +49,42 @@ export async function driftTest(prompt, options) {
|
|
|
80
49
|
console.error('Failed to create session state');
|
|
81
50
|
process.exit(1);
|
|
82
51
|
}
|
|
83
|
-
// Create mock
|
|
84
|
-
// In real usage, actions are parsed from Claude's JSONL session file
|
|
52
|
+
// Create mock steps from prompt for testing
|
|
85
53
|
const mockFiles = extractFilesFromPrompt(prompt);
|
|
86
|
-
const
|
|
54
|
+
const mockSteps = mockFiles.length > 0
|
|
87
55
|
? mockFiles.map((file, i) => ({
|
|
88
|
-
|
|
56
|
+
id: `step-${i}`,
|
|
57
|
+
session_id: sessionState.session_id,
|
|
58
|
+
action_type: 'edit',
|
|
89
59
|
files: [file],
|
|
90
|
-
|
|
60
|
+
folders: [],
|
|
61
|
+
timestamp: Date.now() + i * 1000,
|
|
62
|
+
is_validated: true,
|
|
63
|
+
is_key_decision: false,
|
|
64
|
+
keywords: [],
|
|
91
65
|
}))
|
|
92
|
-
: [{
|
|
93
|
-
|
|
66
|
+
: [{
|
|
67
|
+
id: 'step-0',
|
|
68
|
+
session_id: sessionState.session_id,
|
|
69
|
+
action_type: 'edit',
|
|
70
|
+
files: ['mock-file.ts'],
|
|
71
|
+
folders: [],
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
is_validated: true,
|
|
74
|
+
is_key_decision: false,
|
|
75
|
+
keywords: [],
|
|
76
|
+
}];
|
|
77
|
+
console.log('--- Mock Steps (from prompt) ---');
|
|
94
78
|
console.log(`Files detected: ${mockFiles.join(', ') || 'none (using mock-file.ts)'}`);
|
|
95
79
|
console.log('');
|
|
96
|
-
// Build drift check input
|
|
97
|
-
const driftInput =
|
|
80
|
+
// Build drift check input
|
|
81
|
+
const driftInput = {
|
|
82
|
+
sessionState,
|
|
83
|
+
recentSteps: mockSteps,
|
|
84
|
+
latestUserMessage: prompt,
|
|
85
|
+
};
|
|
98
86
|
console.log('--- Drift Check Input ---');
|
|
99
|
-
console.log(`
|
|
87
|
+
console.log(`Steps: ${mockSteps.map(s => `${s.action_type}:${s.files.join(',')}`).join(' | ')}`);
|
|
100
88
|
console.log('');
|
|
101
89
|
// Run drift check
|
|
102
90
|
console.log('--- Running Drift Check ---');
|
|
@@ -112,37 +100,37 @@ export async function driftTest(prompt, options) {
|
|
|
112
100
|
console.log('');
|
|
113
101
|
console.log('--- Drift Check Result ---');
|
|
114
102
|
console.log(`Score: ${result.score}/10`);
|
|
115
|
-
console.log(`Type: ${result.
|
|
103
|
+
console.log(`Type: ${result.driftType}`);
|
|
116
104
|
console.log(`Diagnostic: ${result.diagnostic}`);
|
|
117
|
-
if (result.
|
|
118
|
-
console.log(`
|
|
105
|
+
if (result.suggestedAction) {
|
|
106
|
+
console.log(`Suggested Action: ${result.suggestedAction}`);
|
|
119
107
|
}
|
|
120
|
-
if (result.
|
|
108
|
+
if (result.recoverySteps && result.recoverySteps.length > 0) {
|
|
121
109
|
console.log('Recovery steps:');
|
|
122
|
-
for (const step of result.
|
|
123
|
-
|
|
124
|
-
console.log(` - ${file}${step.action}`);
|
|
110
|
+
for (const step of result.recoverySteps) {
|
|
111
|
+
console.log(` - ${step}`);
|
|
125
112
|
}
|
|
126
113
|
}
|
|
127
114
|
console.log('');
|
|
128
115
|
// Determine correction level
|
|
129
|
-
const level =
|
|
116
|
+
const level = scoreToCorrectionLevel(result.score);
|
|
130
117
|
console.log('--- Correction Level ---');
|
|
131
118
|
console.log(`Level: ${level || 'NONE (no correction needed)'}`);
|
|
132
119
|
console.log('');
|
|
133
120
|
// Show thresholds
|
|
134
|
-
console.log('--- Thresholds
|
|
135
|
-
console.log(`>=
|
|
136
|
-
console.log(
|
|
137
|
-
console.log(
|
|
138
|
-
console.log(
|
|
139
|
-
console.log(`<
|
|
121
|
+
console.log('--- Thresholds ---');
|
|
122
|
+
console.log(`>= 8: No correction`);
|
|
123
|
+
console.log(`= 7: Nudge`);
|
|
124
|
+
console.log(`5-6: Correct`);
|
|
125
|
+
console.log(`3-4: Intervene`);
|
|
126
|
+
console.log(`< 3: Halt`);
|
|
140
127
|
console.log('');
|
|
141
128
|
// Build and show correction if applicable
|
|
142
129
|
if (level) {
|
|
143
130
|
console.log('--- Correction Output ---');
|
|
144
131
|
const correction = buildCorrection(result, sessionState, level);
|
|
145
|
-
|
|
132
|
+
const formatted = formatCorrectionForInjection(correction);
|
|
133
|
+
console.log(formatted);
|
|
146
134
|
}
|
|
147
135
|
else {
|
|
148
136
|
console.log('No correction needed for this prompt.');
|
|
@@ -150,7 +138,7 @@ export async function driftTest(prompt, options) {
|
|
|
150
138
|
console.log('\n=== END DRIFT TEST ===');
|
|
151
139
|
}
|
|
152
140
|
/**
|
|
153
|
-
* Extract file paths from a prompt for mock
|
|
141
|
+
* Extract file paths from a prompt for mock step creation
|
|
154
142
|
*/
|
|
155
143
|
function extractFilesFromPrompt(prompt) {
|
|
156
144
|
const patterns = [
|
package/dist/commands/init.js
CHANGED
|
@@ -1,27 +1,39 @@
|
|
|
1
|
-
// grov init -
|
|
2
|
-
import {
|
|
1
|
+
// grov init - Configure Claude Code to use grov proxy
|
|
2
|
+
import { setProxyEnv, getSettingsPath } from '../lib/settings.js';
|
|
3
3
|
export async function init() {
|
|
4
|
-
console.log('
|
|
4
|
+
console.log('Configuring grov...\n');
|
|
5
5
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
// Set up proxy URL in settings so users just type 'claude'
|
|
7
|
+
const result = setProxyEnv(true);
|
|
8
|
+
if (result.action === 'added') {
|
|
9
|
+
console.log(' + ANTHROPIC_BASE_URL → http://127.0.0.1:8080');
|
|
10
10
|
}
|
|
11
|
-
if (
|
|
12
|
-
console.log('
|
|
13
|
-
alreadyExists.forEach(hook => console.log(` = ${hook}`));
|
|
11
|
+
else if (result.action === 'unchanged') {
|
|
12
|
+
console.log(' = ANTHROPIC_BASE_URL already configured');
|
|
14
13
|
}
|
|
15
14
|
console.log(`\nSettings file: ${getSettingsPath()}`);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
// Check for API key and provide helpful instructions
|
|
16
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
17
|
+
console.log('\n' + '='.repeat(50));
|
|
18
|
+
console.log(' ANTHROPIC_API_KEY not found');
|
|
19
|
+
console.log('='.repeat(50));
|
|
20
|
+
console.log('\nTo enable drift detection and smart extraction:\n');
|
|
21
|
+
console.log(' 1. Get your API key at:');
|
|
22
|
+
console.log(' https://console.anthropic.com/settings/keys\n');
|
|
23
|
+
console.log(' 2. Add to your shell profile (~/.zshrc or ~/.bashrc):');
|
|
24
|
+
console.log(' export ANTHROPIC_API_KEY=sk-ant-...\n');
|
|
25
|
+
console.log(' 3. Restart terminal or run: source ~/.zshrc\n');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.log('\n ANTHROPIC_API_KEY found');
|
|
29
|
+
}
|
|
30
|
+
console.log('\n--- Next Steps ---');
|
|
31
|
+
console.log('1. Terminal 1: grov proxy');
|
|
32
|
+
console.log('2. Terminal 2: claude');
|
|
33
|
+
console.log('\nGrov will automatically capture reasoning and inject context.');
|
|
21
34
|
}
|
|
22
35
|
catch (error) {
|
|
23
|
-
|
|
24
|
-
console.error('Failed to register hooks:', error instanceof Error ? error.message : 'Unknown error');
|
|
36
|
+
console.error('Failed to configure grov:', error instanceof Error ? error.message : 'Unknown error');
|
|
25
37
|
process.exit(1);
|
|
26
38
|
}
|
|
27
39
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function proxyStatus(): Promise<void>;
|