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.
Files changed (39) hide show
  1. package/README.md +73 -88
  2. package/dist/cli.js +23 -37
  3. package/dist/commands/capture.js +1 -1
  4. package/dist/commands/disable.d.ts +1 -0
  5. package/dist/commands/disable.js +14 -0
  6. package/dist/commands/drift-test.js +56 -68
  7. package/dist/commands/init.js +29 -17
  8. package/dist/commands/proxy-status.d.ts +1 -0
  9. package/dist/commands/proxy-status.js +32 -0
  10. package/dist/commands/unregister.js +7 -1
  11. package/dist/lib/correction-builder-proxy.d.ts +16 -0
  12. package/dist/lib/correction-builder-proxy.js +125 -0
  13. package/dist/lib/correction-builder.js +1 -1
  14. package/dist/lib/drift-checker-proxy.d.ts +63 -0
  15. package/dist/lib/drift-checker-proxy.js +373 -0
  16. package/dist/lib/drift-checker.js +1 -1
  17. package/dist/lib/hooks.d.ts +11 -0
  18. package/dist/lib/hooks.js +33 -0
  19. package/dist/lib/llm-extractor.d.ts +60 -11
  20. package/dist/lib/llm-extractor.js +431 -98
  21. package/dist/lib/settings.d.ts +19 -0
  22. package/dist/lib/settings.js +63 -0
  23. package/dist/lib/store.d.ts +201 -43
  24. package/dist/lib/store.js +653 -90
  25. package/dist/proxy/action-parser.d.ts +58 -0
  26. package/dist/proxy/action-parser.js +196 -0
  27. package/dist/proxy/config.d.ts +26 -0
  28. package/dist/proxy/config.js +67 -0
  29. package/dist/proxy/forwarder.d.ts +24 -0
  30. package/dist/proxy/forwarder.js +119 -0
  31. package/dist/proxy/index.d.ts +1 -0
  32. package/dist/proxy/index.js +30 -0
  33. package/dist/proxy/request-processor.d.ts +12 -0
  34. package/dist/proxy/request-processor.js +120 -0
  35. package/dist/proxy/response-processor.d.ts +14 -0
  36. package/dist/proxy/response-processor.js +138 -0
  37. package/dist/proxy/server.d.ts +9 -0
  38. package/dist/proxy/server.js +904 -0
  39. 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="#roadmap">Roadmap</a> •
18
+ <a href="#advanced-features">Advanced</a> •
9
19
  <a href="#contributing">Contributing</a>
10
20
  </p>
11
21
 
12
- Grov automatically captures reasoning from your Claude Code sessions and injects relevant context into future sessions. Your AI remembers what it learned.
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
- **With grov:** Same task takes ~1-2 minutes, <2% tokens, 0 explore agents. Claude reads files directly because it already has context.
38
+ ![grov status](docs/grov-status.jpeg)
39
+
40
+ ### What Gets Captured
41
+
42
+ Real reasoning, not just file lists:
43
+
44
+ ![captured reasoning](docs/reasoning-output.jpeg)
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 # Register hooks (run once)
66
- grov status # Show captured tasks for current project
67
- grov status -a # Show all tasks (including partial/abandoned)
68
- grov unregister # Disable grov
69
- grov drift-test # Test drift detection (debug)
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
- ## How It Actually Works
83
+ ## Data Storage
73
84
 
74
- 1. **SessionStart hook** fires when you run `claude`
75
- - Grov queries its database for relevant past reasoning
76
- - Outputs context that Claude Code injects into the session
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
- 2. **UserPromptSubmit hook** fires on every prompt
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
- 3. **You work normally** with Claude Code
91
+ - Node.js 18+
92
+ - Claude Code
84
93
 
85
- 4. **Stop hook** fires when the session ends
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
- 5. **Next session**, Claude has context and skips re-exploration
96
+ ## Advanced Features
91
97
 
92
- ## Anti-Drift Detection
98
+ ### Anti-Drift Detection
93
99
 
94
- Grov monitors what Claude **does** (not what you ask) and warns if it drifts from your original goal.
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 4.5 to score alignment (1-10)
100
- - Injects corrections at 5 levels: nudge → correct → intervene → halt
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 manually
108
+ # Test drift detection
106
109
  grov drift-test "refactor the auth system" --goal "fix login bug"
107
110
  ```
108
111
 
109
- ## Environment Variables
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: Override drift model (default: claude-haiku-4-5)
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 (files touched, tool usage counts) and disables drift detection.
124
+ Without an API key, grov uses basic extraction and disables drift detection.
120
125
 
121
- ## What Gets Stored
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
- ## What Gets Injected
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
- ## Data Storage
155
+ ### How the Proxy Works
153
156
 
154
- - **Database:** `~/.grov/memory.db` (SQLite)
155
- - **Per-project:** Context is filtered by project path
156
- - **Local only:** Nothing leaves your machine (unless you add cloud sync)
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
- ## Requirements
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 (Claude Haiku 4.5)
167
- - [x] Zero-friction hooks
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 for development
187
- npm run dev
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
- ### Guidelines
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 - Register hooks in Claude Code
39
+ // grov init - Configure Claude Code to use grov proxy
40
40
  program
41
41
  .command('init')
42
- .description('Register grov hooks in Claude Code settings')
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 capture - Called by Stop hook, extracts and stores reasoning
47
+ // grov disable - Remove proxy configuration
48
48
  program
49
- .command('capture')
50
- .description('Capture reasoning from current session (called by Stop hook)')
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 { promptInject } = await import('./commands/prompt-inject.js');
75
- await promptInject({});
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();
@@ -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 ACTIONS from the prompt for testing.
5
- // In real usage, actions are parsed from Claude's JSONL session file.
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, isAnthropicAvailable } from '../lib/llm-extractor.js';
9
- import { buildDriftCheckInput, checkDrift, checkDriftBasic, DRIFT_CONFIG } from '../lib/drift-checker.js';
10
- import { determineCorrectionLevel, buildCorrection } from '../lib/correction-builder.js';
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 = isAnthropicAvailable();
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 in memory (not persisted unless session ID provided)
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
- last_drift_score: undefined,
47
- escalation_count: 0,
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 actions from prompt for testing
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 mockActions = mockFiles.length > 0
54
+ const mockSteps = mockFiles.length > 0
87
55
  ? mockFiles.map((file, i) => ({
88
- type: 'edit',
56
+ id: `step-${i}`,
57
+ session_id: sessionState.session_id,
58
+ action_type: 'edit',
89
59
  files: [file],
90
- timestamp: Date.now() + i * 1000
60
+ folders: [],
61
+ timestamp: Date.now() + i * 1000,
62
+ is_validated: true,
63
+ is_key_decision: false,
64
+ keywords: [],
91
65
  }))
92
- : [{ type: 'edit', files: ['mock-file.ts'], timestamp: Date.now() }];
93
- console.log('--- Mock Actions (from prompt) ---');
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 using ACTIONS (not prompt!)
97
- const driftInput = buildDriftCheckInput(mockActions, sessionState.session_id, sessionState);
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(`Actions: ${mockActions.map(a => `${a.type}:${a.files.join(',')}`).join(' | ')}`);
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.type}`);
103
+ console.log(`Type: ${result.driftType}`);
116
104
  console.log(`Diagnostic: ${result.diagnostic}`);
117
- if (result.boundaries.length > 0) {
118
- console.log(`Boundaries: ${result.boundaries.join(', ')}`);
105
+ if (result.suggestedAction) {
106
+ console.log(`Suggested Action: ${result.suggestedAction}`);
119
107
  }
120
- if (result.recoveryPlan?.steps) {
108
+ if (result.recoverySteps && result.recoverySteps.length > 0) {
121
109
  console.log('Recovery steps:');
122
- for (const step of result.recoveryPlan.steps) {
123
- const file = step.file ? `[${step.file}] ` : '';
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 = determineCorrectionLevel(result.score, sessionState.escalation_count);
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 (with escalation=%d) ---', sessionState.escalation_count);
135
- console.log(`>= ${DRIFT_CONFIG.SCORE_NO_INJECTION - sessionState.escalation_count}: No correction`);
136
- console.log(`>= ${DRIFT_CONFIG.SCORE_NUDGE - sessionState.escalation_count}: Nudge`);
137
- console.log(`>= ${DRIFT_CONFIG.SCORE_CORRECT - sessionState.escalation_count}: Correct`);
138
- console.log(`>= ${DRIFT_CONFIG.SCORE_INTERVENE - sessionState.escalation_count}: Intervene`);
139
- console.log(`< ${DRIFT_CONFIG.SCORE_INTERVENE - sessionState.escalation_count}: Halt`);
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
- console.log(correction);
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 action creation
141
+ * Extract file paths from a prompt for mock step creation
154
142
  */
155
143
  function extractFilesFromPrompt(prompt) {
156
144
  const patterns = [
@@ -1,27 +1,39 @@
1
- // grov init - Register hooks in Claude Code settings
2
- import { registerGrovHooks, getSettingsPath } from '../lib/hooks.js';
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('Registering grov hooks in Claude Code...\n');
4
+ console.log('Configuring grov...\n');
5
5
  try {
6
- const { added, alreadyExists } = registerGrovHooks();
7
- if (added.length > 0) {
8
- console.log('Added hooks:');
9
- added.forEach(hook => console.log(` + ${hook}`));
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 (alreadyExists.length > 0) {
12
- console.log('Already registered:');
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
- console.log('\nGrov is now active! Your Claude Code sessions will automatically:');
17
- console.log(' - Capture reasoning after each task (Stop hook)');
18
- console.log(' - Inject relevant context at session start (SessionStart hook)');
19
- console.log(' - Inject targeted context before each prompt (UserPromptSubmit hook)');
20
- console.log('\nJust use Claude Code normally. Grov works in the background.');
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
- // SECURITY: Only show error message, not full stack trace with paths
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>;