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.
- package/dist/cli/agents/registry.d.ts +17 -0
- package/dist/cli/agents/registry.js +132 -0
- package/dist/cli/commands/agents.d.ts +1 -0
- package/dist/cli/commands/agents.js +48 -0
- package/dist/cli/commands/disable.d.ts +1 -0
- package/dist/cli/commands/disable.js +179 -0
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +157 -0
- package/dist/{commands → cli/commands}/drift-test.js +39 -26
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +90 -0
- package/dist/{commands → cli/commands}/login.js +19 -18
- package/dist/{commands → cli/commands}/logout.js +1 -1
- package/dist/{commands → cli/commands}/proxy-status.js +1 -1
- package/dist/cli/commands/setup.d.ts +6 -0
- package/dist/cli/commands/setup.js +309 -0
- package/dist/{commands → cli/commands}/status.js +1 -1
- package/dist/{commands → cli/commands}/sync.d.ts +1 -0
- package/dist/{commands → cli/commands}/sync.js +59 -4
- package/dist/{commands → cli/commands}/uninstall.js +2 -2
- package/dist/cli/index.js +270 -0
- package/dist/{lib → core/cloud}/cloud-sync.d.ts +3 -3
- package/dist/{lib → core/cloud}/cloud-sync.js +10 -10
- package/dist/{lib → core/extraction}/correction-builder-proxy.d.ts +1 -1
- package/dist/{lib → core/extraction}/correction-builder-proxy.js +0 -4
- package/dist/{lib → core/extraction}/drift-checker-proxy.d.ts +13 -9
- package/dist/core/extraction/drift-checker-proxy.js +510 -0
- package/dist/{lib → core/extraction}/llm-extractor.d.ts +8 -38
- package/dist/{lib → core/extraction}/llm-extractor.js +132 -220
- package/dist/{lib → core}/store/sessions.js +3 -19
- package/dist/core/store/store.d.ts +1 -0
- package/dist/{lib → core/store}/store.js +1 -1
- package/dist/{lib → core}/store/types.d.ts +0 -4
- package/dist/integrations/mcp/cache.d.ts +27 -0
- package/dist/integrations/mcp/cache.js +106 -0
- package/dist/integrations/mcp/capture/antigravity-parser.d.ts +26 -0
- package/dist/integrations/mcp/capture/antigravity-parser.js +272 -0
- package/dist/integrations/mcp/capture/antigravity-scanner.d.ts +24 -0
- package/dist/integrations/mcp/capture/antigravity-scanner.js +153 -0
- package/dist/integrations/mcp/capture/antigravity-sync-tracker.d.ts +29 -0
- package/dist/integrations/mcp/capture/antigravity-sync-tracker.js +115 -0
- package/dist/integrations/mcp/capture/cli-extractor.d.ts +18 -0
- package/dist/integrations/mcp/capture/cli-extractor.js +258 -0
- package/dist/integrations/mcp/capture/cli-synced.d.ts +4 -0
- package/dist/integrations/mcp/capture/cli-synced.js +62 -0
- package/dist/integrations/mcp/capture/cli-transform.d.ts +30 -0
- package/dist/integrations/mcp/capture/cli-transform.js +62 -0
- package/dist/integrations/mcp/capture/cli-watcher.d.ts +31 -0
- package/dist/integrations/mcp/capture/cli-watcher.js +106 -0
- package/dist/integrations/mcp/capture/hook-handler.d.ts +2 -0
- package/dist/integrations/mcp/capture/hook-handler.js +157 -0
- package/dist/integrations/mcp/capture/sqlite-reader.d.ts +35 -0
- package/dist/integrations/mcp/capture/sqlite-reader.js +388 -0
- package/dist/integrations/mcp/capture/sync-tracker.d.ts +16 -0
- package/dist/integrations/mcp/capture/sync-tracker.js +102 -0
- package/dist/integrations/mcp/clients/cursor/rules-installer.d.ts +19 -0
- package/dist/integrations/mcp/clients/cursor/rules-installer.js +123 -0
- package/dist/integrations/mcp/index.d.ts +1 -0
- package/dist/integrations/mcp/index.js +94 -0
- package/dist/integrations/mcp/logger.d.ts +8 -0
- package/dist/integrations/mcp/logger.js +50 -0
- package/dist/integrations/mcp/server.d.ts +5 -0
- package/dist/integrations/mcp/server.js +58 -0
- package/dist/integrations/mcp/tools/expand.d.ts +1 -0
- package/dist/integrations/mcp/tools/expand.js +53 -0
- package/dist/integrations/mcp/tools/preview.d.ts +1 -0
- package/dist/integrations/mcp/tools/preview.js +64 -0
- package/dist/integrations/proxy/agents/base.d.ts +43 -0
- package/dist/integrations/proxy/agents/base.js +13 -0
- package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.d.ts +4 -8
- package/dist/{proxy/utils → integrations/proxy/agents/claude}/extractors.js +4 -33
- package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.d.ts +1 -1
- package/dist/{proxy → integrations/proxy/agents/claude}/forwarder.js +22 -6
- package/dist/integrations/proxy/agents/claude/index.d.ts +43 -0
- package/dist/integrations/proxy/agents/claude/index.js +386 -0
- package/dist/{proxy/action-parser.d.ts → integrations/proxy/agents/claude/parser.d.ts} +1 -1
- package/dist/integrations/proxy/agents/codex/extractors.d.ts +6 -0
- package/dist/integrations/proxy/agents/codex/extractors.js +49 -0
- package/dist/integrations/proxy/agents/codex/forwarder.d.ts +9 -0
- package/dist/integrations/proxy/agents/codex/forwarder.js +125 -0
- package/dist/integrations/proxy/agents/codex/index.d.ts +44 -0
- package/dist/integrations/proxy/agents/codex/index.js +371 -0
- package/dist/integrations/proxy/agents/codex/parser.d.ts +11 -0
- package/dist/integrations/proxy/agents/codex/parser.js +104 -0
- package/dist/integrations/proxy/agents/codex/patch.d.ts +12 -0
- package/dist/integrations/proxy/agents/codex/patch.js +40 -0
- package/dist/integrations/proxy/agents/codex/settings.d.ts +18 -0
- package/dist/integrations/proxy/agents/codex/settings.js +73 -0
- package/dist/integrations/proxy/agents/codex/types.d.ts +59 -0
- package/dist/integrations/proxy/agents/codex/types.js +2 -0
- package/dist/integrations/proxy/agents/index.d.ts +11 -0
- package/dist/integrations/proxy/agents/index.js +25 -0
- package/dist/integrations/proxy/agents/types.d.ts +77 -0
- package/dist/integrations/proxy/agents/types.js +2 -0
- package/dist/{proxy → integrations/proxy/cache}/extended-cache.js +2 -6
- package/dist/{proxy → integrations/proxy}/config.js +1 -1
- package/dist/{proxy → integrations/proxy}/handlers/preprocess.d.ts +3 -3
- package/dist/integrations/proxy/handlers/preprocess.js +194 -0
- package/dist/integrations/proxy/index.js +20 -0
- package/dist/integrations/proxy/injection/memory-injection.d.ts +56 -0
- package/dist/integrations/proxy/injection/memory-injection.js +252 -0
- package/dist/integrations/proxy/orchestrator.d.ts +30 -0
- package/dist/integrations/proxy/orchestrator.js +954 -0
- package/dist/integrations/proxy/request-processor.d.ts +14 -0
- package/dist/integrations/proxy/request-processor.js +68 -0
- package/dist/{proxy → integrations/proxy}/response-processor.d.ts +4 -3
- package/dist/{proxy → integrations/proxy}/response-processor.js +51 -43
- package/dist/{proxy → integrations/proxy}/server.d.ts +0 -1
- package/dist/integrations/proxy/server.js +146 -0
- package/dist/{proxy → integrations/proxy}/types.d.ts +4 -0
- package/dist/{proxy → integrations/proxy}/utils/logging.d.ts +1 -0
- package/dist/{proxy → integrations/proxy}/utils/logging.js +5 -0
- package/package.json +31 -10
- package/postinstall.js +62 -6
- package/dist/cli.js +0 -149
- package/dist/commands/capture.d.ts +0 -6
- package/dist/commands/capture.js +0 -324
- package/dist/commands/disable.d.ts +0 -1
- package/dist/commands/disable.js +0 -14
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/doctor.js +0 -89
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.js +0 -52
- package/dist/commands/inject.d.ts +0 -5
- package/dist/commands/inject.js +0 -88
- package/dist/commands/prompt-inject.d.ts +0 -4
- package/dist/commands/prompt-inject.js +0 -451
- package/dist/commands/unregister.d.ts +0 -1
- package/dist/commands/unregister.js +0 -28
- package/dist/lib/anchor-extractor.d.ts +0 -30
- package/dist/lib/anchor-extractor.js +0 -296
- package/dist/lib/correction-builder.d.ts +0 -10
- package/dist/lib/correction-builder.js +0 -226
- package/dist/lib/drift-checker-proxy.js +0 -373
- package/dist/lib/drift-checker.d.ts +0 -66
- package/dist/lib/drift-checker.js +0 -341
- package/dist/lib/hooks.d.ts +0 -38
- package/dist/lib/hooks.js +0 -291
- package/dist/lib/jsonl-parser.d.ts +0 -87
- package/dist/lib/jsonl-parser.js +0 -281
- package/dist/lib/session-parser.d.ts +0 -44
- package/dist/lib/session-parser.js +0 -256
- package/dist/lib/store.d.ts +0 -1
- package/dist/proxy/cache.d.ts +0 -32
- package/dist/proxy/cache.js +0 -47
- package/dist/proxy/handlers/preprocess.js +0 -186
- package/dist/proxy/index.js +0 -30
- package/dist/proxy/injection/delta-tracking.d.ts +0 -11
- package/dist/proxy/injection/delta-tracking.js +0 -94
- package/dist/proxy/injection/injectors.d.ts +0 -7
- package/dist/proxy/injection/injectors.js +0 -139
- package/dist/proxy/request-processor.d.ts +0 -27
- package/dist/proxy/request-processor.js +0 -233
- package/dist/proxy/server.js +0 -1289
- /package/dist/{commands → cli/commands}/drift-test.d.ts +0 -0
- /package/dist/{commands → cli/commands}/login.d.ts +0 -0
- /package/dist/{commands → cli/commands}/logout.d.ts +0 -0
- /package/dist/{commands → cli/commands}/proxy-status.d.ts +0 -0
- /package/dist/{commands → cli/commands}/status.d.ts +0 -0
- /package/dist/{commands → cli/commands}/uninstall.d.ts +0 -0
- /package/dist/{cli.d.ts → cli/index.d.ts} +0 -0
- /package/dist/{lib → core/cloud}/api-client.d.ts +0 -0
- /package/dist/{lib → core/cloud}/api-client.js +0 -0
- /package/dist/{lib → core/cloud}/credentials.d.ts +0 -0
- /package/dist/{lib → core/cloud}/credentials.js +0 -0
- /package/dist/{lib → core}/store/convenience.d.ts +0 -0
- /package/dist/{lib → core}/store/convenience.js +0 -0
- /package/dist/{lib → core}/store/database.d.ts +0 -0
- /package/dist/{lib → core}/store/database.js +0 -0
- /package/dist/{lib → core}/store/drift.d.ts +0 -0
- /package/dist/{lib → core}/store/drift.js +0 -0
- /package/dist/{lib → core}/store/index.d.ts +0 -0
- /package/dist/{lib → core}/store/index.js +0 -0
- /package/dist/{lib → core}/store/sessions.d.ts +0 -0
- /package/dist/{lib → core}/store/steps.d.ts +0 -0
- /package/dist/{lib → core}/store/steps.js +0 -0
- /package/dist/{lib → core}/store/tasks.d.ts +0 -0
- /package/dist/{lib → core}/store/tasks.js +0 -0
- /package/dist/{lib → core}/store/types.js +0 -0
- /package/dist/{proxy/action-parser.js → integrations/proxy/agents/claude/parser.js} +0 -0
- /package/dist/{lib → integrations/proxy/agents/claude}/settings.d.ts +0 -0
- /package/dist/{lib → integrations/proxy/agents/claude}/settings.js +0 -0
- /package/dist/{proxy → integrations/proxy/cache}/extended-cache.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/config.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/index.d.ts +0 -0
- /package/dist/{proxy → integrations/proxy}/types.js +0 -0
- /package/dist/{lib → utils}/debug.d.ts +0 -0
- /package/dist/{lib → utils}/debug.js +0 -0
- /package/dist/{lib → utils}/utils.d.ts +0 -0
- /package/dist/{lib → utils}/utils.js +0 -0
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
interface JsonlEntry {
|
|
2
|
-
type: 'user' | 'assistant' | 'system';
|
|
3
|
-
message?: {
|
|
4
|
-
role: string;
|
|
5
|
-
content: string | ContentBlock[];
|
|
6
|
-
};
|
|
7
|
-
timestamp?: string;
|
|
8
|
-
session_id?: string;
|
|
9
|
-
[key: string]: unknown;
|
|
10
|
-
}
|
|
11
|
-
interface ContentBlock {
|
|
12
|
-
type: string;
|
|
13
|
-
text?: string;
|
|
14
|
-
name?: string;
|
|
15
|
-
input?: unknown;
|
|
16
|
-
[key: string]: unknown;
|
|
17
|
-
}
|
|
18
|
-
export interface ParsedSession {
|
|
19
|
-
sessionId: string;
|
|
20
|
-
projectPath: string;
|
|
21
|
-
startTime: string;
|
|
22
|
-
endTime: string;
|
|
23
|
-
userMessages: string[];
|
|
24
|
-
assistantMessages: string[];
|
|
25
|
-
toolCalls: ToolCall[];
|
|
26
|
-
filesRead: string[];
|
|
27
|
-
filesWritten: string[];
|
|
28
|
-
rawEntries: JsonlEntry[];
|
|
29
|
-
}
|
|
30
|
-
export interface ToolCall {
|
|
31
|
-
name: string;
|
|
32
|
-
input: unknown;
|
|
33
|
-
timestamp?: string;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Encode a project path the same way Claude Code does
|
|
37
|
-
* /Users/dev/myapp -> -Users-dev-myapp
|
|
38
|
-
*/
|
|
39
|
-
export declare function encodeProjectPath(projectPath: string): string;
|
|
40
|
-
/**
|
|
41
|
-
* Decode an encoded project path back to the original.
|
|
42
|
-
* SECURITY: Validates that the decoded path doesn't contain traversal sequences.
|
|
43
|
-
* @throws Error if path contains traversal sequences
|
|
44
|
-
*/
|
|
45
|
-
export declare function decodeProjectPath(encoded: string): string;
|
|
46
|
-
/**
|
|
47
|
-
* Validate that a file path is within the expected project boundary.
|
|
48
|
-
* SECURITY: Prevents path traversal outside project directory.
|
|
49
|
-
*/
|
|
50
|
-
export declare function isPathWithinProject(projectPath: string, filePath: string): boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Get the directory where Claude stores sessions for a project
|
|
53
|
-
*/
|
|
54
|
-
export declare function getProjectSessionsDir(projectPath: string): string;
|
|
55
|
-
/**
|
|
56
|
-
* Extract session ID from a JSONL file path
|
|
57
|
-
* ~/.claude/projects/-Users-dev-myapp/abc123def456.jsonl -> abc123def456
|
|
58
|
-
* SECURITY: Validates session ID format to prevent path confusion attacks.
|
|
59
|
-
*/
|
|
60
|
-
export declare function getSessionIdFromPath(jsonlPath: string): string;
|
|
61
|
-
/**
|
|
62
|
-
* Get the current session ID for a project (from the most recent session file)
|
|
63
|
-
*/
|
|
64
|
-
export declare function getCurrentSessionId(projectPath: string): string | null;
|
|
65
|
-
/**
|
|
66
|
-
* Find the most recent session file for a project.
|
|
67
|
-
* SECURITY: Handles TOCTOU race conditions gracefully.
|
|
68
|
-
*/
|
|
69
|
-
export declare function findLatestSessionFile(projectPath: string): string | null;
|
|
70
|
-
/**
|
|
71
|
-
* List all session files for a project
|
|
72
|
-
*/
|
|
73
|
-
export declare function listSessionFiles(projectPath: string): string[];
|
|
74
|
-
/**
|
|
75
|
-
* Parse a JSONL file into entries.
|
|
76
|
-
* SECURITY: Limits entries to prevent memory exhaustion attacks.
|
|
77
|
-
*/
|
|
78
|
-
export declare function parseJsonlFile(filePath: string): JsonlEntry[];
|
|
79
|
-
/**
|
|
80
|
-
* Parse a session file and extract structured data
|
|
81
|
-
*/
|
|
82
|
-
export declare function parseSession(filePath: string): ParsedSession;
|
|
83
|
-
/**
|
|
84
|
-
* Get a summary of the session for LLM extraction
|
|
85
|
-
*/
|
|
86
|
-
export declare function getSessionSummary(session: ParsedSession): string;
|
|
87
|
-
export {};
|
package/dist/lib/jsonl-parser.js
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
// Parse Claude Code session JSONL files from ~/.claude/projects/
|
|
2
|
-
import { readFileSync, readdirSync, statSync, existsSync } from 'fs';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
|
-
import { join, basename, resolve, normalize } from 'path';
|
|
5
|
-
import { debugParser } from './debug.js';
|
|
6
|
-
const CLAUDE_PROJECTS_DIR = join(homedir(), '.claude', 'projects');
|
|
7
|
-
/**
|
|
8
|
-
* Encode a project path the same way Claude Code does
|
|
9
|
-
* /Users/dev/myapp -> -Users-dev-myapp
|
|
10
|
-
*/
|
|
11
|
-
export function encodeProjectPath(projectPath) {
|
|
12
|
-
// Normalize the path first to prevent encoding malicious paths
|
|
13
|
-
const normalized = projectPath.replace(/\.\.+/g, '.');
|
|
14
|
-
return normalized.replace(/\//g, '-');
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Decode an encoded project path back to the original.
|
|
18
|
-
* SECURITY: Validates that the decoded path doesn't contain traversal sequences.
|
|
19
|
-
* @throws Error if path contains traversal sequences
|
|
20
|
-
*/
|
|
21
|
-
export function decodeProjectPath(encoded) {
|
|
22
|
-
// First char is always '-' representing the root '/'
|
|
23
|
-
const decoded = encoded.replace(/-/g, '/');
|
|
24
|
-
// SECURITY: Prevent path traversal attacks
|
|
25
|
-
if (decoded.includes('..')) {
|
|
26
|
-
throw new Error('Invalid path: traversal sequence detected');
|
|
27
|
-
}
|
|
28
|
-
return decoded;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Validate that a file path is within the expected project boundary.
|
|
32
|
-
* SECURITY: Prevents path traversal outside project directory.
|
|
33
|
-
*/
|
|
34
|
-
export function isPathWithinProject(projectPath, filePath) {
|
|
35
|
-
const resolvedProject = resolve(normalize(projectPath));
|
|
36
|
-
const resolvedFile = resolve(normalize(filePath));
|
|
37
|
-
return resolvedFile.startsWith(resolvedProject);
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Get the directory where Claude stores sessions for a project
|
|
41
|
-
*/
|
|
42
|
-
export function getProjectSessionsDir(projectPath) {
|
|
43
|
-
const encoded = encodeProjectPath(projectPath);
|
|
44
|
-
return join(CLAUDE_PROJECTS_DIR, encoded);
|
|
45
|
-
}
|
|
46
|
-
// SECURITY: Valid session ID pattern (hex chars, dashes for UUIDs)
|
|
47
|
-
const SESSION_ID_PATTERN = /^[a-f0-9-]+$/i;
|
|
48
|
-
/**
|
|
49
|
-
* Extract session ID from a JSONL file path
|
|
50
|
-
* ~/.claude/projects/-Users-dev-myapp/abc123def456.jsonl -> abc123def456
|
|
51
|
-
* SECURITY: Validates session ID format to prevent path confusion attacks.
|
|
52
|
-
*/
|
|
53
|
-
export function getSessionIdFromPath(jsonlPath) {
|
|
54
|
-
const sessionId = basename(jsonlPath, '.jsonl');
|
|
55
|
-
// SECURITY: Validate session ID contains only safe characters
|
|
56
|
-
if (!SESSION_ID_PATTERN.test(sessionId)) {
|
|
57
|
-
debugParser('Invalid session ID format: %s', sessionId.substring(0, 20));
|
|
58
|
-
throw new Error('Invalid session ID format');
|
|
59
|
-
}
|
|
60
|
-
return sessionId;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Get the current session ID for a project (from the most recent session file)
|
|
64
|
-
*/
|
|
65
|
-
export function getCurrentSessionId(projectPath) {
|
|
66
|
-
const latestFile = findLatestSessionFile(projectPath);
|
|
67
|
-
return latestFile ? getSessionIdFromPath(latestFile) : null;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Find the most recent session file for a project.
|
|
71
|
-
* SECURITY: Handles TOCTOU race conditions gracefully.
|
|
72
|
-
*/
|
|
73
|
-
export function findLatestSessionFile(projectPath) {
|
|
74
|
-
const sessionsDir = getProjectSessionsDir(projectPath);
|
|
75
|
-
if (!existsSync(sessionsDir)) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
// SECURITY: Handle TOCTOU - files may be deleted between list and stat
|
|
79
|
-
const files = [];
|
|
80
|
-
try {
|
|
81
|
-
const entries = readdirSync(sessionsDir).filter(f => f.endsWith('.jsonl'));
|
|
82
|
-
for (const f of entries) {
|
|
83
|
-
const filePath = join(sessionsDir, f);
|
|
84
|
-
try {
|
|
85
|
-
// SECURITY: Wrap stat in try-catch to handle deleted files
|
|
86
|
-
const stat = statSync(filePath);
|
|
87
|
-
files.push({
|
|
88
|
-
name: f,
|
|
89
|
-
path: filePath,
|
|
90
|
-
mtime: stat.mtime
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// File was deleted between readdir and stat - skip it
|
|
95
|
-
debugParser('File disappeared during listing: %s', f);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
// Directory may have been deleted
|
|
101
|
-
debugParser('Could not read sessions directory: %s', sessionsDir);
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
files.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
105
|
-
return files.length > 0 ? files[0].path : null;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* List all session files for a project
|
|
109
|
-
*/
|
|
110
|
-
export function listSessionFiles(projectPath) {
|
|
111
|
-
const sessionsDir = getProjectSessionsDir(projectPath);
|
|
112
|
-
if (!existsSync(sessionsDir)) {
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
return readdirSync(sessionsDir)
|
|
116
|
-
.filter(f => f.endsWith('.jsonl'))
|
|
117
|
-
.map(f => join(sessionsDir, f));
|
|
118
|
-
}
|
|
119
|
-
// SECURITY: Maximum entries to prevent memory exhaustion
|
|
120
|
-
const MAX_JSONL_ENTRIES = 10000;
|
|
121
|
-
/**
|
|
122
|
-
* Parse a JSONL file into entries.
|
|
123
|
-
* SECURITY: Limits entries to prevent memory exhaustion attacks.
|
|
124
|
-
*/
|
|
125
|
-
export function parseJsonlFile(filePath) {
|
|
126
|
-
let content;
|
|
127
|
-
try {
|
|
128
|
-
content = readFileSync(filePath, 'utf-8');
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
// File may have been deleted/moved between finding and reading
|
|
132
|
-
debugParser('Could not read file: %s', filePath);
|
|
133
|
-
return [];
|
|
134
|
-
}
|
|
135
|
-
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
136
|
-
const entries = [];
|
|
137
|
-
for (const line of lines) {
|
|
138
|
-
// SECURITY: Limit entries to prevent memory exhaustion
|
|
139
|
-
if (entries.length >= MAX_JSONL_ENTRIES) {
|
|
140
|
-
debugParser('Entry limit reached (%d), stopping parse', MAX_JSONL_ENTRIES);
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
try {
|
|
144
|
-
const entry = JSON.parse(line);
|
|
145
|
-
entries.push(entry);
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
// Skip malformed lines
|
|
149
|
-
debugParser('Skipping malformed JSONL line');
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return entries;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Extract text content from a message content array
|
|
156
|
-
*/
|
|
157
|
-
function extractTextContent(content) {
|
|
158
|
-
if (typeof content === 'string') {
|
|
159
|
-
return content;
|
|
160
|
-
}
|
|
161
|
-
return content
|
|
162
|
-
.filter(block => block.type === 'text' && block.text)
|
|
163
|
-
.map(block => block.text)
|
|
164
|
-
.join('\n');
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Extract tool calls from assistant message content
|
|
168
|
-
*/
|
|
169
|
-
function extractToolCalls(content, timestamp) {
|
|
170
|
-
if (typeof content === 'string') {
|
|
171
|
-
return [];
|
|
172
|
-
}
|
|
173
|
-
return content
|
|
174
|
-
.filter(block => block.type === 'tool_use' && block.name)
|
|
175
|
-
.map(block => ({
|
|
176
|
-
name: block.name,
|
|
177
|
-
input: block.input,
|
|
178
|
-
timestamp
|
|
179
|
-
}));
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Parse a session file and extract structured data
|
|
183
|
-
*/
|
|
184
|
-
export function parseSession(filePath) {
|
|
185
|
-
const entries = parseJsonlFile(filePath);
|
|
186
|
-
const sessionId = basename(filePath, '.jsonl');
|
|
187
|
-
const userMessages = [];
|
|
188
|
-
const assistantMessages = [];
|
|
189
|
-
const toolCalls = [];
|
|
190
|
-
const filesRead = new Set();
|
|
191
|
-
const filesWritten = new Set();
|
|
192
|
-
let startTime = '';
|
|
193
|
-
let endTime = '';
|
|
194
|
-
let projectPath = '';
|
|
195
|
-
for (const entry of entries) {
|
|
196
|
-
// Track timestamps
|
|
197
|
-
if (entry.timestamp) {
|
|
198
|
-
if (!startTime)
|
|
199
|
-
startTime = entry.timestamp;
|
|
200
|
-
endTime = entry.timestamp;
|
|
201
|
-
}
|
|
202
|
-
// Extract user messages
|
|
203
|
-
if (entry.type === 'user' && entry.message?.content) {
|
|
204
|
-
const text = extractTextContent(entry.message.content);
|
|
205
|
-
if (text)
|
|
206
|
-
userMessages.push(text);
|
|
207
|
-
}
|
|
208
|
-
// Extract assistant messages and tool calls
|
|
209
|
-
if (entry.type === 'assistant' && entry.message?.content) {
|
|
210
|
-
const text = extractTextContent(entry.message.content);
|
|
211
|
-
if (text)
|
|
212
|
-
assistantMessages.push(text);
|
|
213
|
-
const tools = extractToolCalls(entry.message.content, entry.timestamp);
|
|
214
|
-
toolCalls.push(...tools);
|
|
215
|
-
// Track files from tool calls
|
|
216
|
-
for (const tool of tools) {
|
|
217
|
-
const input = tool.input;
|
|
218
|
-
if (input?.file_path && typeof input.file_path === 'string') {
|
|
219
|
-
if (tool.name === 'Read') {
|
|
220
|
-
filesRead.add(input.file_path);
|
|
221
|
-
}
|
|
222
|
-
else if (tool.name === 'Write' || tool.name === 'Edit') {
|
|
223
|
-
filesWritten.add(input.file_path);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
// Try to infer project path from file paths
|
|
230
|
-
const allFiles = [...filesRead, ...filesWritten];
|
|
231
|
-
if (allFiles.length > 0) {
|
|
232
|
-
// Find common prefix
|
|
233
|
-
const firstFile = allFiles[0];
|
|
234
|
-
const parts = firstFile.split('/');
|
|
235
|
-
// Assume project is a few levels deep (e.g., /Users/dev/project)
|
|
236
|
-
if (parts.length >= 4) {
|
|
237
|
-
projectPath = parts.slice(0, 4).join('/');
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return {
|
|
241
|
-
sessionId,
|
|
242
|
-
projectPath,
|
|
243
|
-
startTime,
|
|
244
|
-
endTime,
|
|
245
|
-
userMessages,
|
|
246
|
-
assistantMessages,
|
|
247
|
-
toolCalls,
|
|
248
|
-
filesRead: [...filesRead],
|
|
249
|
-
filesWritten: [...filesWritten],
|
|
250
|
-
rawEntries: entries
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Get a summary of the session for LLM extraction
|
|
255
|
-
*/
|
|
256
|
-
export function getSessionSummary(session) {
|
|
257
|
-
const lines = [];
|
|
258
|
-
lines.push(`Session ID: ${session.sessionId}`);
|
|
259
|
-
lines.push(`Time: ${session.startTime} to ${session.endTime}`);
|
|
260
|
-
lines.push('');
|
|
261
|
-
lines.push('## User Messages');
|
|
262
|
-
session.userMessages.forEach((msg, i) => {
|
|
263
|
-
lines.push(`[${i + 1}] ${msg.substring(0, 500)}${msg.length > 500 ? '...' : ''}`);
|
|
264
|
-
});
|
|
265
|
-
lines.push('');
|
|
266
|
-
lines.push('## Files Read');
|
|
267
|
-
session.filesRead.forEach(f => lines.push(` - ${f}`));
|
|
268
|
-
lines.push('');
|
|
269
|
-
lines.push('## Files Written/Edited');
|
|
270
|
-
session.filesWritten.forEach(f => lines.push(` - ${f}`));
|
|
271
|
-
lines.push('');
|
|
272
|
-
lines.push('## Tool Calls');
|
|
273
|
-
const toolSummary = session.toolCalls.reduce((acc, t) => {
|
|
274
|
-
acc[t.name] = (acc[t.name] || 0) + 1;
|
|
275
|
-
return acc;
|
|
276
|
-
}, {});
|
|
277
|
-
Object.entries(toolSummary).forEach(([name, count]) => {
|
|
278
|
-
lines.push(` - ${name}: ${count}x`);
|
|
279
|
-
});
|
|
280
|
-
return lines.join('\n');
|
|
281
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Claude's action extracted from session JSONL
|
|
3
|
-
*/
|
|
4
|
-
export interface ClaudeAction {
|
|
5
|
-
type: 'edit' | 'write' | 'bash' | 'read' | 'delete' | 'grep' | 'glob' | 'multiedit';
|
|
6
|
-
files: string[];
|
|
7
|
-
command?: string;
|
|
8
|
-
timestamp: number;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Find session JSONL path from session_id and project path.
|
|
12
|
-
*
|
|
13
|
-
* Claude Code stores sessions in:
|
|
14
|
-
* ~/.claude/projects/<encoded-path>/<session_id>.jsonl
|
|
15
|
-
*
|
|
16
|
-
* The encoded path uses a specific encoding (not standard URL encoding).
|
|
17
|
-
*/
|
|
18
|
-
export declare function findSessionFile(sessionId: string, projectPath: string): string | null;
|
|
19
|
-
/**
|
|
20
|
-
* Parse JSONL and extract ALL Claude's tool calls
|
|
21
|
-
*/
|
|
22
|
-
export declare function parseSessionActions(sessionPath: string): ClaudeAction[];
|
|
23
|
-
/**
|
|
24
|
-
* Get only NEW actions since last check timestamp.
|
|
25
|
-
* This is the main function used by prompt-inject.
|
|
26
|
-
*/
|
|
27
|
-
export declare function getNewActions(sessionPath: string, lastCheckedTimestamp: number): ClaudeAction[];
|
|
28
|
-
/**
|
|
29
|
-
* Get actions that MODIFY files (not reads).
|
|
30
|
-
* Use this for drift detection - reads are exploration, not drift.
|
|
31
|
-
*/
|
|
32
|
-
export declare function getModifyingActions(actions: ClaudeAction[]): ClaudeAction[];
|
|
33
|
-
/**
|
|
34
|
-
* Extract all unique files touched by actions
|
|
35
|
-
*/
|
|
36
|
-
export declare function extractFilesFromActions(actions: ClaudeAction[]): string[];
|
|
37
|
-
/**
|
|
38
|
-
* Extract unique folders from actions
|
|
39
|
-
*/
|
|
40
|
-
export declare function extractFoldersFromActions(actions: ClaudeAction[]): string[];
|
|
41
|
-
/**
|
|
42
|
-
* Extract keywords from an action (for step storage)
|
|
43
|
-
*/
|
|
44
|
-
export declare function extractKeywordsFromAction(action: ClaudeAction): string[];
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
// Session JSONL parser for anti-drift system
|
|
2
|
-
// Parses Claude Code session files to extract Claude's ACTIONS (tool calls)
|
|
3
|
-
//
|
|
4
|
-
// CRITICAL: We monitor Claude's ACTIONS, NOT user prompts.
|
|
5
|
-
// User can explore freely. We check what CLAUDE DOES.
|
|
6
|
-
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
7
|
-
import { homedir } from 'os';
|
|
8
|
-
import { join, dirname } from 'path';
|
|
9
|
-
import { debugInject } from './debug.js';
|
|
10
|
-
// ============================================
|
|
11
|
-
// MAIN FUNCTIONS
|
|
12
|
-
// ============================================
|
|
13
|
-
/**
|
|
14
|
-
* Find session JSONL path from session_id and project path.
|
|
15
|
-
*
|
|
16
|
-
* Claude Code stores sessions in:
|
|
17
|
-
* ~/.claude/projects/<encoded-path>/<session_id>.jsonl
|
|
18
|
-
*
|
|
19
|
-
* The encoded path uses a specific encoding (not standard URL encoding).
|
|
20
|
-
*/
|
|
21
|
-
export function findSessionFile(sessionId, projectPath) {
|
|
22
|
-
const claudeProjectsDir = join(homedir(), '.claude', 'projects');
|
|
23
|
-
if (!existsSync(claudeProjectsDir)) {
|
|
24
|
-
debugInject('Claude projects dir not found: %s', claudeProjectsDir);
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
// Try to find the project folder
|
|
28
|
-
// Claude Code uses a specific encoding for the path
|
|
29
|
-
// We'll search for folders that might contain our session
|
|
30
|
-
const projectFolders = readdirSync(claudeProjectsDir, { withFileTypes: true })
|
|
31
|
-
.filter(d => d.isDirectory())
|
|
32
|
-
.map(d => d.name);
|
|
33
|
-
// First, try URL-encoded path
|
|
34
|
-
const urlEncoded = encodeURIComponent(projectPath);
|
|
35
|
-
if (projectFolders.includes(urlEncoded)) {
|
|
36
|
-
const sessionPath = join(claudeProjectsDir, urlEncoded, `${sessionId}.jsonl`);
|
|
37
|
-
if (existsSync(sessionPath)) {
|
|
38
|
-
debugInject('Found session via URL encoding: %s', sessionPath);
|
|
39
|
-
return sessionPath;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
// Try custom Claude encoding (%2F for /, etc.)
|
|
43
|
-
const customEncoded = projectPath
|
|
44
|
-
.replace(/\//g, '%2F')
|
|
45
|
-
.replace(/:/g, '%3A');
|
|
46
|
-
if (projectFolders.includes(customEncoded)) {
|
|
47
|
-
const sessionPath = join(claudeProjectsDir, customEncoded, `${sessionId}.jsonl`);
|
|
48
|
-
if (existsSync(sessionPath)) {
|
|
49
|
-
debugInject('Found session via custom encoding: %s', sessionPath);
|
|
50
|
-
return sessionPath;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Search in all project folders for the session
|
|
54
|
-
for (const folder of projectFolders) {
|
|
55
|
-
const sessionPath = join(claudeProjectsDir, folder, `${sessionId}.jsonl`);
|
|
56
|
-
if (existsSync(sessionPath)) {
|
|
57
|
-
debugInject('Found session by scanning: %s', sessionPath);
|
|
58
|
-
return sessionPath;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
debugInject('Session file not found for: %s in %s', sessionId, projectPath);
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Parse JSONL and extract ALL Claude's tool calls
|
|
66
|
-
*/
|
|
67
|
-
export function parseSessionActions(sessionPath) {
|
|
68
|
-
if (!existsSync(sessionPath)) {
|
|
69
|
-
debugInject('Session file does not exist: %s', sessionPath);
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
const content = readFileSync(sessionPath, 'utf-8');
|
|
73
|
-
const lines = content.trim().split('\n').filter(Boolean);
|
|
74
|
-
const actions = [];
|
|
75
|
-
for (const line of lines) {
|
|
76
|
-
try {
|
|
77
|
-
const entry = JSON.parse(line);
|
|
78
|
-
// Only process assistant messages (Claude's responses)
|
|
79
|
-
if (entry.type !== 'assistant')
|
|
80
|
-
continue;
|
|
81
|
-
const timestamp = new Date(entry.timestamp).getTime();
|
|
82
|
-
// Extract tool calls from content array
|
|
83
|
-
for (const block of entry.message?.content || []) {
|
|
84
|
-
if (block.type !== 'tool_use')
|
|
85
|
-
continue;
|
|
86
|
-
const action = parseToolCall(block, timestamp);
|
|
87
|
-
if (action) {
|
|
88
|
-
actions.push(action);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
catch {
|
|
93
|
-
// Skip malformed lines silently
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
debugInject('Parsed %d actions from session', actions.length);
|
|
98
|
-
return actions;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Get only NEW actions since last check timestamp.
|
|
102
|
-
* This is the main function used by prompt-inject.
|
|
103
|
-
*/
|
|
104
|
-
export function getNewActions(sessionPath, lastCheckedTimestamp) {
|
|
105
|
-
const allActions = parseSessionActions(sessionPath);
|
|
106
|
-
const newActions = allActions.filter(a => a.timestamp > lastCheckedTimestamp);
|
|
107
|
-
debugInject('Found %d new actions since %d', newActions.length, lastCheckedTimestamp);
|
|
108
|
-
return newActions;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Get actions that MODIFY files (not reads).
|
|
112
|
-
* Use this for drift detection - reads are exploration, not drift.
|
|
113
|
-
*/
|
|
114
|
-
export function getModifyingActions(actions) {
|
|
115
|
-
return actions.filter(a => a.type !== 'read' && a.type !== 'grep' && a.type !== 'glob');
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Extract all unique files touched by actions
|
|
119
|
-
*/
|
|
120
|
-
export function extractFilesFromActions(actions) {
|
|
121
|
-
const files = new Set();
|
|
122
|
-
for (const action of actions) {
|
|
123
|
-
for (const file of action.files) {
|
|
124
|
-
files.add(file);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return [...files];
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Extract unique folders from actions
|
|
131
|
-
*/
|
|
132
|
-
export function extractFoldersFromActions(actions) {
|
|
133
|
-
const folders = new Set();
|
|
134
|
-
for (const action of actions) {
|
|
135
|
-
for (const file of action.files) {
|
|
136
|
-
const folder = dirname(file);
|
|
137
|
-
if (folder && folder !== '.') {
|
|
138
|
-
folders.add(folder);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return [...folders];
|
|
143
|
-
}
|
|
144
|
-
// ============================================
|
|
145
|
-
// HELPERS
|
|
146
|
-
// ============================================
|
|
147
|
-
/**
|
|
148
|
-
* Parse a single tool call block into ClaudeAction
|
|
149
|
-
*/
|
|
150
|
-
function parseToolCall(block, timestamp) {
|
|
151
|
-
const name = block.name?.toLowerCase();
|
|
152
|
-
const input = block.input || {};
|
|
153
|
-
switch (name) {
|
|
154
|
-
case 'edit':
|
|
155
|
-
return {
|
|
156
|
-
type: 'edit',
|
|
157
|
-
files: [input.file_path].filter(Boolean),
|
|
158
|
-
timestamp
|
|
159
|
-
};
|
|
160
|
-
case 'multiedit':
|
|
161
|
-
return {
|
|
162
|
-
type: 'multiedit',
|
|
163
|
-
files: [input.file_path].filter(Boolean),
|
|
164
|
-
timestamp
|
|
165
|
-
};
|
|
166
|
-
case 'write':
|
|
167
|
-
return {
|
|
168
|
-
type: 'write',
|
|
169
|
-
files: [input.file_path].filter(Boolean),
|
|
170
|
-
timestamp
|
|
171
|
-
};
|
|
172
|
-
case 'bash':
|
|
173
|
-
return {
|
|
174
|
-
type: 'bash',
|
|
175
|
-
files: extractFilesFromCommand(input.command || ''),
|
|
176
|
-
command: input.command,
|
|
177
|
-
timestamp
|
|
178
|
-
};
|
|
179
|
-
case 'read':
|
|
180
|
-
return {
|
|
181
|
-
type: 'read',
|
|
182
|
-
files: [input.file_path].filter(Boolean),
|
|
183
|
-
timestamp
|
|
184
|
-
};
|
|
185
|
-
case 'grep':
|
|
186
|
-
return {
|
|
187
|
-
type: 'grep',
|
|
188
|
-
files: [input.path].filter(Boolean),
|
|
189
|
-
timestamp
|
|
190
|
-
};
|
|
191
|
-
case 'glob':
|
|
192
|
-
return {
|
|
193
|
-
type: 'glob',
|
|
194
|
-
files: [input.path].filter(Boolean),
|
|
195
|
-
timestamp
|
|
196
|
-
};
|
|
197
|
-
default:
|
|
198
|
-
// Ignore other tools (Task, WebFetch, etc.)
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Extract file paths from a bash command.
|
|
204
|
-
* Basic extraction - not perfect but catches common patterns.
|
|
205
|
-
*/
|
|
206
|
-
function extractFilesFromCommand(command) {
|
|
207
|
-
if (!command)
|
|
208
|
-
return [];
|
|
209
|
-
const files = [];
|
|
210
|
-
const patterns = [
|
|
211
|
-
// Absolute paths: /path/to/file.ts
|
|
212
|
-
/(?:^|\s)(\/[\w\-\.\/]+\.\w+)/g,
|
|
213
|
-
// Relative paths with ./: ./src/file.ts
|
|
214
|
-
/(?:^|\s)(\.\/[\w\-\.\/]+\.\w+)/g,
|
|
215
|
-
// Relative paths: src/file.ts
|
|
216
|
-
/(?:^|\s)([\w\-]+\/[\w\-\.\/]+\.\w+)/g,
|
|
217
|
-
];
|
|
218
|
-
for (const pattern of patterns) {
|
|
219
|
-
for (const match of command.matchAll(pattern)) {
|
|
220
|
-
const file = match[1];
|
|
221
|
-
// Filter out common non-files
|
|
222
|
-
if (file && !file.startsWith('http') && !file.match(/^\d+\.\d+/)) {
|
|
223
|
-
files.push(file);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return [...new Set(files)];
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Extract keywords from an action (for step storage)
|
|
231
|
-
*/
|
|
232
|
-
export function extractKeywordsFromAction(action) {
|
|
233
|
-
const keywords = [];
|
|
234
|
-
// Extract from file names
|
|
235
|
-
for (const file of action.files) {
|
|
236
|
-
const fileName = file.split('/').pop() || '';
|
|
237
|
-
const baseName = fileName.replace(/\.\w+$/, '');
|
|
238
|
-
// Split camelCase and kebab-case
|
|
239
|
-
const parts = baseName
|
|
240
|
-
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
241
|
-
.replace(/[-_]/g, ' ')
|
|
242
|
-
.toLowerCase()
|
|
243
|
-
.split(/\s+/)
|
|
244
|
-
.filter(p => p.length > 2);
|
|
245
|
-
keywords.push(...parts);
|
|
246
|
-
}
|
|
247
|
-
// Extract from bash command
|
|
248
|
-
if (action.command) {
|
|
249
|
-
const commandParts = action.command
|
|
250
|
-
.toLowerCase()
|
|
251
|
-
.split(/\s+/)
|
|
252
|
-
.filter(p => p.length > 3 && !p.startsWith('-'));
|
|
253
|
-
keywords.push(...commandParts.slice(0, 5));
|
|
254
|
-
}
|
|
255
|
-
return [...new Set(keywords)];
|
|
256
|
-
}
|
package/dist/lib/store.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './store/index.js';
|