@quantiya/codevibe-gemini-plugin 1.0.6 → 1.0.7

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.
@@ -1,324 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.TranscriptWatcher = void 0;
37
- const fs = __importStar(require("fs"));
38
- const path = __importStar(require("path"));
39
- const crypto = __importStar(require("crypto"));
40
- const chokidar = __importStar(require("chokidar"));
41
- const events_1 = require("events");
42
- const logger_1 = require("./logger");
43
- /**
44
- * Watches Gemini CLI transcript files for changes and emits events for new messages.
45
- * This is a workaround since Gemini CLI hooks are not fully implemented yet.
46
- */
47
- class TranscriptWatcher extends events_1.EventEmitter {
48
- constructor(workingDirectory) {
49
- super();
50
- this.watcher = null;
51
- this.sentMessageIds = new Set();
52
- this.currentSessionId = null;
53
- this.currentTranscriptPath = null;
54
- this.projectHash = null;
55
- this.workingDirectory = workingDirectory;
56
- this.geminiDir = path.join(process.env.HOME || '', '.gemini', 'tmp');
57
- this.projectHash = this.computeProjectHash(workingDirectory);
58
- this.watcherStartTime = new Date();
59
- logger_1.logger.info('TranscriptWatcher initialized', {
60
- workingDirectory,
61
- geminiDir: this.geminiDir,
62
- projectHash: this.projectHash,
63
- watcherStartTime: this.watcherStartTime.toISOString(),
64
- });
65
- }
66
- /**
67
- * Compute the project hash the same way Gemini CLI does
68
- * Based on the working directory path
69
- */
70
- computeProjectHash(dir) {
71
- return crypto.createHash('sha256').update(dir).digest('hex');
72
- }
73
- /**
74
- * Start watching for transcript changes
75
- */
76
- start() {
77
- if (this.watcher) {
78
- logger_1.logger.warn('TranscriptWatcher already started');
79
- return;
80
- }
81
- // Watch the project-specific chats directory
82
- const chatsDir = path.join(this.geminiDir, this.projectHash, 'chats');
83
- logger_1.logger.info('Starting TranscriptWatcher', { chatsDir });
84
- // Check if directory exists, if not watch parent and wait for it
85
- if (!fs.existsSync(chatsDir)) {
86
- logger_1.logger.info('Chats directory does not exist yet, watching parent directory');
87
- // Watch the entire gemini tmp dir for new project directories
88
- this.watchForSessionCreation();
89
- return;
90
- }
91
- this.watchChatsDirectory(chatsDir);
92
- }
93
- /**
94
- * Watch for the chats directory to be created
95
- */
96
- watchForSessionCreation() {
97
- const projectDir = path.join(this.geminiDir, this.projectHash);
98
- // Create watcher for the project directory or parent
99
- const watchPath = fs.existsSync(projectDir) ? projectDir : this.geminiDir;
100
- this.watcher = chokidar.watch(watchPath, {
101
- persistent: true,
102
- ignoreInitial: true, // Only watch for NEW files, not existing ones
103
- depth: 3,
104
- awaitWriteFinish: {
105
- stabilityThreshold: 200, // Reduced from 500ms for lower latency
106
- pollInterval: 50
107
- }
108
- });
109
- this.watcher.on('add', (filePath) => {
110
- // Look for session files matching pattern
111
- if (filePath.includes(this.projectHash) && filePath.includes('/chats/session-') && filePath.endsWith('.json')) {
112
- logger_1.logger.info('Session file discovered', { filePath });
113
- this.handleSessionFile(filePath);
114
- }
115
- });
116
- this.watcher.on('change', (filePath) => {
117
- if (filePath.includes(this.projectHash) && filePath.includes('/chats/session-') && filePath.endsWith('.json')) {
118
- if (filePath === this.currentTranscriptPath) {
119
- this.handleTranscriptChange(filePath);
120
- }
121
- else {
122
- // This might be a resumed session - check and switch if needed
123
- logger_1.logger.info('Session file changed (possible resume)', { filePath, currentPath: this.currentTranscriptPath });
124
- this.handleSessionFile(filePath);
125
- }
126
- }
127
- });
128
- this.watcher.on('error', (err) => {
129
- const error = err instanceof Error ? err : new Error(String(err));
130
- logger_1.logger.error('Watcher error', { error: error.message });
131
- this.emit('error', error);
132
- });
133
- }
134
- /**
135
- * Watch a specific chats directory for session files
136
- */
137
- watchChatsDirectory(chatsDir) {
138
- // DON'T scan for existing files - only react to NEW files or changes
139
- // This prevents picking up stale session files that won't be used.
140
- // When user types in Gemini CLI, it will either:
141
- // 1. Create a NEW session file (triggers 'add' event)
142
- // 2. Update existing session file (triggers 'change' event)
143
- // Either way, we'll detect the ACTIVE session correctly.
144
- this.watcher = chokidar.watch(chatsDir, {
145
- persistent: true,
146
- ignoreInitial: true, // Don't re-emit 'add' for files we already scanned
147
- awaitWriteFinish: {
148
- stabilityThreshold: 200, // Reduced from 500ms for lower latency
149
- pollInterval: 50
150
- }
151
- });
152
- this.watcher.on('add', (filePath) => {
153
- if (filePath.endsWith('.json') && filePath.includes('session-')) {
154
- logger_1.logger.info('New session file detected', { filePath });
155
- this.handleSessionFile(filePath);
156
- }
157
- });
158
- this.watcher.on('change', (filePath) => {
159
- if (filePath.endsWith('.json') && filePath.includes('session-')) {
160
- // Handle changes to any session file (for resume scenarios)
161
- if (filePath === this.currentTranscriptPath) {
162
- this.handleTranscriptChange(filePath);
163
- }
164
- else {
165
- // This might be a resumed session - check and switch if needed
166
- logger_1.logger.info('Session file changed (possible resume)', { filePath, currentPath: this.currentTranscriptPath });
167
- this.handleSessionFile(filePath);
168
- }
169
- }
170
- });
171
- this.watcher.on('error', (err) => {
172
- const error = err instanceof Error ? err : new Error(String(err));
173
- logger_1.logger.error('Watcher error', { error: error.message });
174
- this.emit('error', error);
175
- });
176
- }
177
- /**
178
- * Scan for RECENT session files in the chats directory (modified within last 5 minutes)
179
- * This allows picking up sessions where Gemini CLI is already running,
180
- * while avoiding old session files from previous days that would load stale events.
181
- */
182
- scanForRecentSession(chatsDir) {
183
- const RECENT_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
184
- try {
185
- if (!fs.existsSync(chatsDir)) {
186
- logger_1.logger.debug('Chats directory does not exist yet', { chatsDir });
187
- return;
188
- }
189
- const now = Date.now();
190
- const files = fs.readdirSync(chatsDir)
191
- .filter(f => f.startsWith('session-') && f.endsWith('.json'))
192
- .map(f => ({
193
- name: f,
194
- path: path.join(chatsDir, f),
195
- mtime: fs.statSync(path.join(chatsDir, f)).mtime
196
- }))
197
- .filter(f => (now - f.mtime.getTime()) < RECENT_THRESHOLD_MS) // Only recent files
198
- .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
199
- if (files.length > 0) {
200
- const ageSeconds = Math.round((now - files[0].mtime.getTime()) / 1000);
201
- logger_1.logger.info('Found recent session file', {
202
- name: files[0].name,
203
- ageSeconds,
204
- totalFiles: fs.readdirSync(chatsDir).filter(f => f.startsWith('session-')).length,
205
- });
206
- this.handleSessionFile(files[0].path);
207
- }
208
- else {
209
- const totalFiles = fs.readdirSync(chatsDir).filter(f => f.startsWith('session-')).length;
210
- logger_1.logger.info('No recent session files found (all older than 5 minutes)', {
211
- totalFiles,
212
- thresholdMinutes: RECENT_THRESHOLD_MS / 60000,
213
- });
214
- }
215
- }
216
- catch (error) {
217
- logger_1.logger.error('Error scanning for sessions', { error: error.message });
218
- }
219
- }
220
- /**
221
- * Handle a new or updated session file
222
- */
223
- handleSessionFile(filePath) {
224
- try {
225
- const content = fs.readFileSync(filePath, 'utf-8');
226
- const transcript = JSON.parse(content);
227
- // If this is a new session, emit session-discovered
228
- if (this.currentSessionId !== transcript.sessionId) {
229
- logger_1.logger.info('New session discovered', {
230
- sessionId: transcript.sessionId,
231
- projectHash: transcript.projectHash
232
- });
233
- this.currentSessionId = transcript.sessionId;
234
- this.currentTranscriptPath = filePath;
235
- this.sentMessageIds.clear(); // Reset for new session
236
- this.emit('session-discovered', transcript.sessionId, transcript.projectHash, filePath);
237
- }
238
- // Process messages
239
- this.processMessages(transcript);
240
- }
241
- catch (err) {
242
- logger_1.logger.error('Error reading session file', {
243
- filePath,
244
- error: err instanceof Error ? err.message : String(err)
245
- });
246
- }
247
- }
248
- /**
249
- * Handle transcript file changes
250
- */
251
- handleTranscriptChange(filePath) {
252
- logger_1.logger.debug('Transcript changed', { filePath });
253
- this.handleSessionFile(filePath);
254
- }
255
- /**
256
- * Process messages and emit events for new ones
257
- * Only processes messages created after the watcher started to prevent
258
- * loading historical messages from the transcript file.
259
- */
260
- processMessages(transcript) {
261
- for (const message of transcript.messages) {
262
- // Skip messages we've already processed (within this watcher instance)
263
- if (this.sentMessageIds.has(message.id)) {
264
- continue;
265
- }
266
- // Skip messages that were created before the watcher started
267
- // This prevents loading old messages from the transcript file on startup
268
- const messageTime = new Date(message.timestamp);
269
- if (messageTime < this.watcherStartTime) {
270
- // Mark as processed so we don't check again
271
- this.sentMessageIds.add(message.id);
272
- continue;
273
- }
274
- this.sentMessageIds.add(message.id);
275
- logger_1.logger.debug('New message detected', {
276
- id: message.id,
277
- type: message.type,
278
- contentLength: message.content.length
279
- });
280
- this.emit('new-message', transcript.sessionId, message);
281
- }
282
- }
283
- /**
284
- * Get the current session ID
285
- */
286
- getSessionId() {
287
- return this.currentSessionId;
288
- }
289
- /**
290
- * Get the project hash for the working directory
291
- */
292
- getProjectHash() {
293
- return this.projectHash;
294
- }
295
- /**
296
- * Manually set the session ID (e.g., when passed from wrapper)
297
- */
298
- setSessionId(sessionId) {
299
- this.currentSessionId = sessionId;
300
- // Try to find the transcript file for this session
301
- if (this.projectHash) {
302
- const chatsDir = path.join(this.geminiDir, this.projectHash, 'chats');
303
- if (fs.existsSync(chatsDir)) {
304
- const files = fs.readdirSync(chatsDir).filter(f => f.includes(sessionId));
305
- if (files.length > 0) {
306
- this.currentTranscriptPath = path.join(chatsDir, files[0]);
307
- logger_1.logger.info('Found transcript for session', { sessionId, path: this.currentTranscriptPath });
308
- }
309
- }
310
- }
311
- }
312
- /**
313
- * Stop watching
314
- */
315
- stop() {
316
- if (this.watcher) {
317
- this.watcher.close();
318
- this.watcher = null;
319
- logger_1.logger.info('TranscriptWatcher stopped');
320
- }
321
- }
322
- }
323
- exports.TranscriptWatcher = TranscriptWatcher;
324
- //# sourceMappingURL=transcript-watcher.js.map
package/dist/types.js DELETED
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_TOOL_APPROVAL_OPTIONS = exports.DeliveryStatus = exports.AgentType = exports.SessionStatus = exports.EventSource = exports.EventType = void 0;
4
- // Re-export shared types from codevibe-core
5
- var codevibe_core_1 = require("@quantiya/codevibe-core");
6
- Object.defineProperty(exports, "EventType", { enumerable: true, get: function () { return codevibe_core_1.EventType; } });
7
- Object.defineProperty(exports, "EventSource", { enumerable: true, get: function () { return codevibe_core_1.EventSource; } });
8
- Object.defineProperty(exports, "SessionStatus", { enumerable: true, get: function () { return codevibe_core_1.SessionStatus; } });
9
- Object.defineProperty(exports, "AgentType", { enumerable: true, get: function () { return codevibe_core_1.AgentType; } });
10
- Object.defineProperty(exports, "DeliveryStatus", { enumerable: true, get: function () { return codevibe_core_1.DeliveryStatus; } });
11
- exports.DEFAULT_TOOL_APPROVAL_OPTIONS = [
12
- { number: '1', text: 'Allow once' },
13
- { number: '2', text: 'Allow for this session' },
14
- { number: '3', text: 'Deny' },
15
- ];
16
- //# sourceMappingURL=types.js.map