grov 0.1.0
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/LICENSE +190 -0
- package/README.md +211 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +106 -0
- package/dist/commands/capture.d.ts +6 -0
- package/dist/commands/capture.js +324 -0
- package/dist/commands/drift-test.d.ts +7 -0
- package/dist/commands/drift-test.js +177 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +27 -0
- package/dist/commands/inject.d.ts +5 -0
- package/dist/commands/inject.js +88 -0
- package/dist/commands/prompt-inject.d.ts +4 -0
- package/dist/commands/prompt-inject.js +451 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.js +51 -0
- package/dist/commands/unregister.d.ts +1 -0
- package/dist/commands/unregister.js +22 -0
- package/dist/lib/anchor-extractor.d.ts +30 -0
- package/dist/lib/anchor-extractor.js +296 -0
- package/dist/lib/correction-builder.d.ts +10 -0
- package/dist/lib/correction-builder.js +226 -0
- package/dist/lib/debug.d.ts +24 -0
- package/dist/lib/debug.js +34 -0
- package/dist/lib/drift-checker.d.ts +66 -0
- package/dist/lib/drift-checker.js +341 -0
- package/dist/lib/hooks.d.ts +27 -0
- package/dist/lib/hooks.js +258 -0
- package/dist/lib/jsonl-parser.d.ts +87 -0
- package/dist/lib/jsonl-parser.js +281 -0
- package/dist/lib/llm-extractor.d.ts +50 -0
- package/dist/lib/llm-extractor.js +408 -0
- package/dist/lib/session-parser.d.ts +44 -0
- package/dist/lib/session-parser.js +256 -0
- package/dist/lib/store.d.ts +248 -0
- package/dist/lib/store.js +793 -0
- package/dist/lib/utils.d.ts +31 -0
- package/dist/lib/utils.js +76 -0
- package/package.json +67 -0
|
@@ -0,0 +1,256 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import type { ClaudeAction } from './session-parser.js';
|
|
3
|
+
export type TaskStatus = 'complete' | 'question' | 'partial' | 'abandoned';
|
|
4
|
+
export interface Task {
|
|
5
|
+
id: string;
|
|
6
|
+
project_path: string;
|
|
7
|
+
user?: string;
|
|
8
|
+
original_query: string;
|
|
9
|
+
goal?: string;
|
|
10
|
+
reasoning_trace: string[];
|
|
11
|
+
files_touched: string[];
|
|
12
|
+
status: TaskStatus;
|
|
13
|
+
linked_commit?: string;
|
|
14
|
+
parent_task_id?: string;
|
|
15
|
+
turn_number?: number;
|
|
16
|
+
tags: string[];
|
|
17
|
+
created_at: string;
|
|
18
|
+
}
|
|
19
|
+
export interface CreateTaskInput {
|
|
20
|
+
project_path: string;
|
|
21
|
+
user?: string;
|
|
22
|
+
original_query: string;
|
|
23
|
+
goal?: string;
|
|
24
|
+
reasoning_trace?: string[];
|
|
25
|
+
files_touched?: string[];
|
|
26
|
+
status: TaskStatus;
|
|
27
|
+
linked_commit?: string;
|
|
28
|
+
parent_task_id?: string;
|
|
29
|
+
turn_number?: number;
|
|
30
|
+
tags?: string[];
|
|
31
|
+
}
|
|
32
|
+
export type SessionStatus = 'active' | 'completed' | 'abandoned';
|
|
33
|
+
export interface SessionState {
|
|
34
|
+
session_id: string;
|
|
35
|
+
user_id?: string;
|
|
36
|
+
project_path: string;
|
|
37
|
+
original_goal?: string;
|
|
38
|
+
actions_taken: string[];
|
|
39
|
+
files_explored: string[];
|
|
40
|
+
current_intent?: string;
|
|
41
|
+
drift_warnings: string[];
|
|
42
|
+
start_time: string;
|
|
43
|
+
last_update: string;
|
|
44
|
+
status: SessionStatus;
|
|
45
|
+
expected_scope: string[];
|
|
46
|
+
constraints: string[];
|
|
47
|
+
success_criteria: string[];
|
|
48
|
+
keywords: string[];
|
|
49
|
+
last_drift_score?: number;
|
|
50
|
+
escalation_count: number;
|
|
51
|
+
pending_recovery_plan?: RecoveryPlan;
|
|
52
|
+
drift_history: DriftEvent[];
|
|
53
|
+
last_checked_at: number;
|
|
54
|
+
}
|
|
55
|
+
export interface RecoveryPlan {
|
|
56
|
+
steps: Array<{
|
|
57
|
+
file?: string;
|
|
58
|
+
action: string;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export interface DriftEvent {
|
|
62
|
+
timestamp: string;
|
|
63
|
+
score: number;
|
|
64
|
+
level: string;
|
|
65
|
+
prompt_summary: string;
|
|
66
|
+
}
|
|
67
|
+
export interface CreateSessionStateInput {
|
|
68
|
+
session_id: string;
|
|
69
|
+
user_id?: string;
|
|
70
|
+
project_path: string;
|
|
71
|
+
original_goal?: string;
|
|
72
|
+
expected_scope?: string[];
|
|
73
|
+
constraints?: string[];
|
|
74
|
+
success_criteria?: string[];
|
|
75
|
+
keywords?: string[];
|
|
76
|
+
}
|
|
77
|
+
export type ChangeType = 'read' | 'write' | 'edit' | 'create' | 'delete';
|
|
78
|
+
export interface FileReasoning {
|
|
79
|
+
id: string;
|
|
80
|
+
task_id?: string;
|
|
81
|
+
file_path: string;
|
|
82
|
+
anchor?: string;
|
|
83
|
+
line_start?: number;
|
|
84
|
+
line_end?: number;
|
|
85
|
+
code_hash?: string;
|
|
86
|
+
change_type?: ChangeType;
|
|
87
|
+
reasoning: string;
|
|
88
|
+
created_at: string;
|
|
89
|
+
}
|
|
90
|
+
export interface CreateFileReasoningInput {
|
|
91
|
+
task_id?: string;
|
|
92
|
+
file_path: string;
|
|
93
|
+
anchor?: string;
|
|
94
|
+
line_start?: number;
|
|
95
|
+
line_end?: number;
|
|
96
|
+
code_hash?: string;
|
|
97
|
+
change_type?: ChangeType;
|
|
98
|
+
reasoning: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Initialize the database connection and create tables
|
|
102
|
+
*/
|
|
103
|
+
export declare function initDatabase(): Database.Database;
|
|
104
|
+
/**
|
|
105
|
+
* Close the database connection
|
|
106
|
+
*/
|
|
107
|
+
export declare function closeDatabase(): void;
|
|
108
|
+
/**
|
|
109
|
+
* Create a new task
|
|
110
|
+
*/
|
|
111
|
+
export declare function createTask(input: CreateTaskInput): Task;
|
|
112
|
+
/**
|
|
113
|
+
* Get tasks for a project
|
|
114
|
+
*/
|
|
115
|
+
export declare function getTasksForProject(projectPath: string, options?: {
|
|
116
|
+
status?: TaskStatus;
|
|
117
|
+
limit?: number;
|
|
118
|
+
}): Task[];
|
|
119
|
+
export declare function getTasksByFiles(projectPath: string, files: string[], options?: {
|
|
120
|
+
status?: TaskStatus;
|
|
121
|
+
limit?: number;
|
|
122
|
+
}): Task[];
|
|
123
|
+
/**
|
|
124
|
+
* Get a task by ID
|
|
125
|
+
*/
|
|
126
|
+
export declare function getTaskById(id: string): Task | null;
|
|
127
|
+
/**
|
|
128
|
+
* Update a task's status
|
|
129
|
+
*/
|
|
130
|
+
export declare function updateTaskStatus(id: string, status: TaskStatus): void;
|
|
131
|
+
/**
|
|
132
|
+
* Get task count for a project
|
|
133
|
+
*/
|
|
134
|
+
export declare function getTaskCount(projectPath: string): number;
|
|
135
|
+
/**
|
|
136
|
+
* Create a new session state.
|
|
137
|
+
* FIXED: Uses INSERT OR IGNORE to handle race conditions safely.
|
|
138
|
+
*/
|
|
139
|
+
export declare function createSessionState(input: CreateSessionStateInput): SessionState;
|
|
140
|
+
/**
|
|
141
|
+
* Get a session state by ID
|
|
142
|
+
*/
|
|
143
|
+
export declare function getSessionState(sessionId: string): SessionState | null;
|
|
144
|
+
/**
|
|
145
|
+
* Update a session state.
|
|
146
|
+
* SECURITY: Uses transaction for atomic updates to prevent race conditions.
|
|
147
|
+
*/
|
|
148
|
+
export declare function updateSessionState(sessionId: string, updates: Partial<Omit<SessionState, 'session_id' | 'start_time'>>): void;
|
|
149
|
+
/**
|
|
150
|
+
* Delete a session state
|
|
151
|
+
*/
|
|
152
|
+
export declare function deleteSessionState(sessionId: string): void;
|
|
153
|
+
/**
|
|
154
|
+
* Get active sessions for a project
|
|
155
|
+
*/
|
|
156
|
+
export declare function getActiveSessionsForProject(projectPath: string): SessionState[];
|
|
157
|
+
/**
|
|
158
|
+
* Correction level types for drift detection
|
|
159
|
+
*/
|
|
160
|
+
export type CorrectionLevel = 'nudge' | 'correct' | 'intervene' | 'halt';
|
|
161
|
+
/**
|
|
162
|
+
* Update session drift metrics after a prompt check
|
|
163
|
+
*/
|
|
164
|
+
export declare function updateSessionDrift(sessionId: string, driftScore: number, correctionLevel: CorrectionLevel | null, promptSummary: string, recoveryPlan?: RecoveryPlan): void;
|
|
165
|
+
/**
|
|
166
|
+
* Check if a session should be flagged for review
|
|
167
|
+
* Returns true if: status=drifted OR warnings>=3 OR avg_score<6
|
|
168
|
+
*/
|
|
169
|
+
export declare function shouldFlagForReview(sessionId: string): boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Get drift summary for a session (used by capture)
|
|
172
|
+
*/
|
|
173
|
+
export declare function getDriftSummary(sessionId: string): {
|
|
174
|
+
totalEvents: number;
|
|
175
|
+
resolved: boolean;
|
|
176
|
+
finalScore: number | null;
|
|
177
|
+
hadHalt: boolean;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Create a new file reasoning entry
|
|
181
|
+
*/
|
|
182
|
+
export declare function createFileReasoning(input: CreateFileReasoningInput): FileReasoning;
|
|
183
|
+
/**
|
|
184
|
+
* Get file reasoning entries for a task
|
|
185
|
+
*/
|
|
186
|
+
export declare function getFileReasoningForTask(taskId: string): FileReasoning[];
|
|
187
|
+
/**
|
|
188
|
+
* Get file reasoning entries by file path
|
|
189
|
+
*/
|
|
190
|
+
export declare function getFileReasoningByPath(filePath: string, limit?: number): FileReasoning[];
|
|
191
|
+
/**
|
|
192
|
+
* Get file reasoning entries matching a pattern (for files in a project).
|
|
193
|
+
* SECURITY: Uses escaped LIKE patterns to prevent injection.
|
|
194
|
+
*/
|
|
195
|
+
export declare function getFileReasoningByPathPattern(pathPattern: string, limit?: number): FileReasoning[];
|
|
196
|
+
/**
|
|
197
|
+
* Get the database path
|
|
198
|
+
*/
|
|
199
|
+
export declare function getDatabasePath(): string;
|
|
200
|
+
/**
|
|
201
|
+
* Step record - a single Claude action stored in DB
|
|
202
|
+
*/
|
|
203
|
+
export interface StepRecord {
|
|
204
|
+
id: string;
|
|
205
|
+
session_id: string;
|
|
206
|
+
action_type: string;
|
|
207
|
+
files: string[];
|
|
208
|
+
folders: string[];
|
|
209
|
+
command?: string;
|
|
210
|
+
reasoning?: string;
|
|
211
|
+
drift_score: number;
|
|
212
|
+
is_key_decision: boolean;
|
|
213
|
+
keywords: string[];
|
|
214
|
+
timestamp: number;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Save a Claude action as a step
|
|
218
|
+
*/
|
|
219
|
+
export declare function saveStep(sessionId: string, action: ClaudeAction, driftScore: number, isKeyDecision?: boolean, keywords?: string[]): void;
|
|
220
|
+
/**
|
|
221
|
+
* Get recent steps for a session (most recent first)
|
|
222
|
+
*/
|
|
223
|
+
export declare function getRecentSteps(sessionId: string, limit?: number): StepRecord[];
|
|
224
|
+
/**
|
|
225
|
+
* Update last_checked_at timestamp for a session
|
|
226
|
+
*/
|
|
227
|
+
export declare function updateLastChecked(sessionId: string, timestamp: number): void;
|
|
228
|
+
/**
|
|
229
|
+
* Get steps that touched specific files
|
|
230
|
+
*/
|
|
231
|
+
export declare function getStepsByFiles(sessionId: string, files: string[], limit?: number): StepRecord[];
|
|
232
|
+
/**
|
|
233
|
+
* Get steps that touched specific folders
|
|
234
|
+
*/
|
|
235
|
+
export declare function getStepsByFolders(sessionId: string, folders: string[], limit?: number): StepRecord[];
|
|
236
|
+
/**
|
|
237
|
+
* Get steps matching keywords
|
|
238
|
+
*/
|
|
239
|
+
export declare function getStepsByKeywords(sessionId: string, keywords: string[], limit?: number): StepRecord[];
|
|
240
|
+
/**
|
|
241
|
+
* Get key decision steps
|
|
242
|
+
*/
|
|
243
|
+
export declare function getKeyDecisionSteps(sessionId: string, limit?: number): StepRecord[];
|
|
244
|
+
/**
|
|
245
|
+
* Combined retrieval: runs all 4 queries and deduplicates
|
|
246
|
+
* Priority: key decisions > files > folders > keywords
|
|
247
|
+
*/
|
|
248
|
+
export declare function getRelevantSteps(sessionId: string, currentFiles: string[], currentFolders: string[], keywords: string[], limit?: number): StepRecord[];
|