@vibecheckai/cli 3.9.1 → 4.0.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.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/bin/runners/context/generators/cursor-enhanced.js +99 -13
  3. package/mcp-server/.eslintrc.json +24 -0
  4. package/mcp-server/README.md +425 -135
  5. package/mcp-server/SPEC.md +583 -0
  6. package/mcp-server/configs/README.md +172 -0
  7. package/mcp-server/configs/claude-desktop-pro.json +31 -0
  8. package/mcp-server/configs/claude-desktop-with-workspace.json +25 -0
  9. package/mcp-server/configs/claude-desktop.json +19 -0
  10. package/mcp-server/configs/cursor-mcp.json +21 -0
  11. package/mcp-server/configs/windsurf-mcp.json +17 -0
  12. package/mcp-server/mcp-config.example.json +9 -0
  13. package/mcp-server/package.json +49 -34
  14. package/mcp-server/src/cli.ts +185 -0
  15. package/mcp-server/src/index.ts +85 -0
  16. package/mcp-server/src/server.ts +1933 -0
  17. package/mcp-server/src/services/cache-service.ts +466 -0
  18. package/mcp-server/src/services/cli-service.ts +345 -0
  19. package/mcp-server/src/services/context-manager.ts +717 -0
  20. package/mcp-server/src/services/firewall-service.ts +662 -0
  21. package/mcp-server/src/services/git-service.ts +671 -0
  22. package/mcp-server/src/services/index.ts +52 -0
  23. package/mcp-server/src/services/prompt-builder-service.ts +1031 -0
  24. package/mcp-server/src/services/session-service.ts +550 -0
  25. package/mcp-server/src/services/tier-service.ts +470 -0
  26. package/mcp-server/src/types.ts +351 -0
  27. package/mcp-server/tsconfig.json +16 -27
  28. package/package.json +6 -6
  29. package/mcp-server/.guardrail/audit/audit.log.jsonl +0 -2
  30. package/mcp-server/.specs/architecture.mdc +0 -90
  31. package/mcp-server/.specs/security.mdc +0 -30
  32. package/mcp-server/HARDENING_SUMMARY.md +0 -299
  33. package/mcp-server/agent-checkpoint.js +0 -364
  34. package/mcp-server/agent-firewall-interceptor.js +0 -500
  35. package/mcp-server/architect-tools.js +0 -707
  36. package/mcp-server/audit-mcp.js +0 -206
  37. package/mcp-server/authority-tools.js +0 -569
  38. package/mcp-server/codebase-architect-tools.js +0 -838
  39. package/mcp-server/conductor/conflict-resolver.js +0 -588
  40. package/mcp-server/conductor/execution-planner.js +0 -544
  41. package/mcp-server/conductor/index.js +0 -377
  42. package/mcp-server/conductor/lock-manager.js +0 -615
  43. package/mcp-server/conductor/request-queue.js +0 -550
  44. package/mcp-server/conductor/session-manager.js +0 -500
  45. package/mcp-server/conductor/tools.js +0 -510
  46. package/mcp-server/consolidated-tools.js +0 -1170
  47. package/mcp-server/deprecation-middleware.js +0 -282
  48. package/mcp-server/handlers/index.ts +0 -15
  49. package/mcp-server/handlers/tool-handler.ts +0 -593
  50. package/mcp-server/hygiene-tools.js +0 -428
  51. package/mcp-server/index-v1.js +0 -698
  52. package/mcp-server/index.js +0 -2940
  53. package/mcp-server/intelligence-tools.js +0 -664
  54. package/mcp-server/intent-drift-tools.js +0 -873
  55. package/mcp-server/intent-firewall-interceptor.js +0 -529
  56. package/mcp-server/lib/api-client.cjs +0 -13
  57. package/mcp-server/lib/cache-wrapper.cjs +0 -383
  58. package/mcp-server/lib/error-envelope.js +0 -138
  59. package/mcp-server/lib/executor.ts +0 -499
  60. package/mcp-server/lib/index.ts +0 -29
  61. package/mcp-server/lib/logger.cjs +0 -30
  62. package/mcp-server/lib/rate-limiter.js +0 -166
  63. package/mcp-server/lib/sandbox.test.ts +0 -519
  64. package/mcp-server/lib/sandbox.ts +0 -395
  65. package/mcp-server/lib/types.ts +0 -267
  66. package/mcp-server/logger.js +0 -173
  67. package/mcp-server/manifest.json +0 -473
  68. package/mcp-server/mdc-generator.js +0 -298
  69. package/mcp-server/premium-tools.js +0 -1275
  70. package/mcp-server/proof-tools.js +0 -571
  71. package/mcp-server/registry/tool-registry.js +0 -586
  72. package/mcp-server/registry/tools.json +0 -619
  73. package/mcp-server/registry.test.ts +0 -340
  74. package/mcp-server/test-mcp.js +0 -108
  75. package/mcp-server/test-tools.js +0 -36
  76. package/mcp-server/tests/tier-gating.test.js +0 -297
  77. package/mcp-server/tier-auth.js +0 -767
  78. package/mcp-server/tools/index.js +0 -72
  79. package/mcp-server/tools-reorganized.ts +0 -244
  80. package/mcp-server/tools-v3.js +0 -1004
  81. package/mcp-server/truth-context.js +0 -622
  82. package/mcp-server/truth-firewall-tools.js +0 -2183
  83. package/mcp-server/vibecheck-2.0-tools.js +0 -761
  84. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
  85. package/mcp-server/vibecheck-tools.js +0 -1075
@@ -0,0 +1,550 @@
1
+ /**
2
+ * Session Service - Comprehensive agent session management
3
+ * Tracks agent interactions, provides context continuity, and enables audit trails
4
+ */
5
+
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import * as os from 'os';
9
+ import * as crypto from 'crypto';
10
+
11
+ // ═══════════════════════════════════════════════════════════════════════════════
12
+ // Types
13
+ // ═══════════════════════════════════════════════════════════════════════════════
14
+
15
+ export interface AgentSession {
16
+ id: string;
17
+ agentId?: string;
18
+ agentName?: string;
19
+ startedAt: string;
20
+ lastActivityAt: string;
21
+ expiresAt: string;
22
+ status: 'active' | 'idle' | 'expired' | 'terminated';
23
+
24
+ // Context
25
+ workspacePath: string;
26
+ currentIntent?: SessionIntent;
27
+
28
+ // Metrics
29
+ metrics: SessionMetrics;
30
+
31
+ // History
32
+ toolCalls: ToolCallRecord[];
33
+ stateChanges: StateChange[];
34
+
35
+ // Security
36
+ securityContext: SecurityContext;
37
+ }
38
+
39
+ export interface SessionIntent {
40
+ summary: string;
41
+ constraints: string[];
42
+ setAt: string;
43
+ hash: string;
44
+ }
45
+
46
+ export interface SessionMetrics {
47
+ totalToolCalls: number;
48
+ successfulCalls: number;
49
+ failedCalls: number;
50
+ blockedCalls: number;
51
+ totalDurationMs: number;
52
+ averageLatencyMs: number;
53
+
54
+ // By category
55
+ callsByCategory: Record<string, number>;
56
+
57
+ // Firewall metrics
58
+ firewallViolations: number;
59
+ claimsVerified: number;
60
+ claimsPassed: number;
61
+ claimsFailed: number;
62
+ }
63
+
64
+ export interface ToolCallRecord {
65
+ id: string;
66
+ timestamp: string;
67
+ toolName: string;
68
+ category: 'cli' | 'firewall' | 'prompt' | 'utility';
69
+ tier: 'free' | 'pro';
70
+
71
+ // Input/Output
72
+ inputSummary: string;
73
+ outputSummary?: string;
74
+
75
+ // Result
76
+ status: 'success' | 'error' | 'blocked';
77
+ durationMs: number;
78
+ errorMessage?: string;
79
+ blockReason?: string;
80
+
81
+ // Context
82
+ intentHash?: string;
83
+ firewallMode?: string;
84
+ }
85
+
86
+ export interface StateChange {
87
+ timestamp: string;
88
+ type: 'intent_set' | 'intent_cleared' | 'mode_changed' | 'checkpoint_created' | 'fix_applied';
89
+ before: string;
90
+ after: string;
91
+ metadata?: Record<string, unknown>;
92
+ }
93
+
94
+ export interface SecurityContext {
95
+ tier: 'free' | 'pro';
96
+ firewallMode: 'off' | 'observe' | 'enforce';
97
+ hasActiveIntent: boolean;
98
+ intentHash?: string;
99
+ lastVerificationAt?: string;
100
+ trustScore: number; // 0-100
101
+ }
102
+
103
+ export interface SessionSummary {
104
+ id: string;
105
+ duration: string;
106
+ status: 'active' | 'idle' | 'expired' | 'terminated';
107
+ metrics: {
108
+ totalCalls: number;
109
+ successRate: string;
110
+ avgLatency: string;
111
+ };
112
+ intent?: string;
113
+ recentActivity: string[];
114
+ }
115
+
116
+ // ═══════════════════════════════════════════════════════════════════════════════
117
+ // Session Service Implementation
118
+ // ═══════════════════════════════════════════════════════════════════════════════
119
+
120
+ export class SessionService {
121
+ private currentSession: AgentSession | null = null;
122
+ private sessionStorePath: string;
123
+ private readonly SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
124
+ private readonly IDLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
125
+ private readonly MAX_TOOL_CALLS_STORED = 500;
126
+
127
+ constructor(workspacePath: string) {
128
+ this.sessionStorePath = path.join(os.homedir(), '.vibecheck', 'sessions');
129
+ this.ensureSessionStore();
130
+ this.currentSession = this.createSession(workspacePath);
131
+ }
132
+
133
+ private ensureSessionStore(): void {
134
+ if (!fs.existsSync(this.sessionStorePath)) {
135
+ fs.mkdirSync(this.sessionStorePath, { recursive: true });
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Create a new session
141
+ */
142
+ private createSession(workspacePath: string): AgentSession {
143
+ const now = new Date();
144
+ const session: AgentSession = {
145
+ id: this.generateSessionId(),
146
+ startedAt: now.toISOString(),
147
+ lastActivityAt: now.toISOString(),
148
+ expiresAt: new Date(now.getTime() + this.SESSION_TIMEOUT_MS).toISOString(),
149
+ status: 'active',
150
+ workspacePath,
151
+
152
+ metrics: {
153
+ totalToolCalls: 0,
154
+ successfulCalls: 0,
155
+ failedCalls: 0,
156
+ blockedCalls: 0,
157
+ totalDurationMs: 0,
158
+ averageLatencyMs: 0,
159
+ callsByCategory: {},
160
+ firewallViolations: 0,
161
+ claimsVerified: 0,
162
+ claimsPassed: 0,
163
+ claimsFailed: 0,
164
+ },
165
+
166
+ toolCalls: [],
167
+ stateChanges: [],
168
+
169
+ securityContext: {
170
+ tier: 'free',
171
+ firewallMode: 'off',
172
+ hasActiveIntent: false,
173
+ trustScore: 100,
174
+ },
175
+ };
176
+
177
+ this.saveSession(session);
178
+ return session;
179
+ }
180
+
181
+ private generateSessionId(): string {
182
+ const timestamp = Date.now().toString(36);
183
+ const random = crypto.randomBytes(4).toString('hex');
184
+ return `vcs_${timestamp}_${random}`;
185
+ }
186
+
187
+ /**
188
+ * Record a tool call
189
+ */
190
+ recordToolCall(record: Omit<ToolCallRecord, 'id' | 'timestamp'>): void {
191
+ if (!this.currentSession) return;
192
+
193
+ const fullRecord: ToolCallRecord = {
194
+ ...record,
195
+ id: `tc_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
196
+ timestamp: new Date().toISOString(),
197
+ };
198
+
199
+ // Add to history (keep last N calls)
200
+ this.currentSession.toolCalls.push(fullRecord);
201
+ if (this.currentSession.toolCalls.length > this.MAX_TOOL_CALLS_STORED) {
202
+ this.currentSession.toolCalls = this.currentSession.toolCalls.slice(-this.MAX_TOOL_CALLS_STORED);
203
+ }
204
+
205
+ // Update metrics
206
+ this.currentSession.metrics.totalToolCalls++;
207
+ this.currentSession.metrics.totalDurationMs += record.durationMs;
208
+ this.currentSession.metrics.averageLatencyMs =
209
+ this.currentSession.metrics.totalDurationMs / this.currentSession.metrics.totalToolCalls;
210
+
211
+ if (record.status === 'success') {
212
+ this.currentSession.metrics.successfulCalls++;
213
+ } else if (record.status === 'error') {
214
+ this.currentSession.metrics.failedCalls++;
215
+ } else if (record.status === 'blocked') {
216
+ this.currentSession.metrics.blockedCalls++;
217
+ }
218
+
219
+ // Update category counts
220
+ this.currentSession.metrics.callsByCategory[record.category] =
221
+ (this.currentSession.metrics.callsByCategory[record.category] || 0) + 1;
222
+
223
+ // Update activity
224
+ this.currentSession.lastActivityAt = new Date().toISOString();
225
+ this.currentSession.status = 'active';
226
+
227
+ this.saveSession(this.currentSession);
228
+ }
229
+
230
+ /**
231
+ * Record a state change
232
+ */
233
+ recordStateChange(change: Omit<StateChange, 'timestamp'>): void {
234
+ if (!this.currentSession) return;
235
+
236
+ this.currentSession.stateChanges.push({
237
+ ...change,
238
+ timestamp: new Date().toISOString(),
239
+ });
240
+
241
+ // Keep last 100 state changes
242
+ if (this.currentSession.stateChanges.length > 100) {
243
+ this.currentSession.stateChanges = this.currentSession.stateChanges.slice(-100);
244
+ }
245
+
246
+ this.saveSession(this.currentSession);
247
+ }
248
+
249
+ /**
250
+ * Update security context
251
+ */
252
+ updateSecurityContext(updates: Partial<SecurityContext>): void {
253
+ if (!this.currentSession) return;
254
+
255
+ this.currentSession.securityContext = {
256
+ ...this.currentSession.securityContext,
257
+ ...updates,
258
+ };
259
+
260
+ this.saveSession(this.currentSession);
261
+ }
262
+
263
+ /**
264
+ * Set current intent
265
+ */
266
+ setIntent(summary: string, constraints: string[]): void {
267
+ if (!this.currentSession) return;
268
+
269
+ const intent: SessionIntent = {
270
+ summary,
271
+ constraints,
272
+ setAt: new Date().toISOString(),
273
+ hash: this.hashIntent(summary, constraints),
274
+ };
275
+
276
+ const previousIntent = this.currentSession.currentIntent?.summary || 'none';
277
+
278
+ this.currentSession.currentIntent = intent;
279
+ this.currentSession.securityContext.hasActiveIntent = true;
280
+ this.currentSession.securityContext.intentHash = intent.hash;
281
+
282
+ this.recordStateChange({
283
+ type: 'intent_set',
284
+ before: previousIntent,
285
+ after: summary,
286
+ metadata: { constraints },
287
+ });
288
+ }
289
+
290
+ /**
291
+ * Clear current intent
292
+ */
293
+ clearIntent(): void {
294
+ if (!this.currentSession) return;
295
+
296
+ const previousIntent = this.currentSession.currentIntent?.summary || 'none';
297
+
298
+ this.currentSession.currentIntent = undefined;
299
+ this.currentSession.securityContext.hasActiveIntent = false;
300
+ this.currentSession.securityContext.intentHash = undefined;
301
+
302
+ this.recordStateChange({
303
+ type: 'intent_cleared',
304
+ before: previousIntent,
305
+ after: 'none',
306
+ });
307
+ }
308
+
309
+ private hashIntent(summary: string, constraints: string[]): string {
310
+ const content = `${summary}|${constraints.sort().join('|')}`;
311
+ return crypto.createHash('sha256').update(content).digest('hex').substring(0, 16);
312
+ }
313
+
314
+ /**
315
+ * Record firewall violation
316
+ */
317
+ recordFirewallViolation(): void {
318
+ if (!this.currentSession) return;
319
+
320
+ this.currentSession.metrics.firewallViolations++;
321
+ this.currentSession.securityContext.trustScore = Math.max(
322
+ 0,
323
+ this.currentSession.securityContext.trustScore - 10
324
+ );
325
+
326
+ this.saveSession(this.currentSession);
327
+ }
328
+
329
+ /**
330
+ * Record claim verification
331
+ */
332
+ recordClaimVerification(passed: boolean): void {
333
+ if (!this.currentSession) return;
334
+
335
+ this.currentSession.metrics.claimsVerified++;
336
+ if (passed) {
337
+ this.currentSession.metrics.claimsPassed++;
338
+ // Slightly increase trust score on passed verification
339
+ this.currentSession.securityContext.trustScore = Math.min(
340
+ 100,
341
+ this.currentSession.securityContext.trustScore + 2
342
+ );
343
+ } else {
344
+ this.currentSession.metrics.claimsFailed++;
345
+ // Decrease trust score on failed verification
346
+ this.currentSession.securityContext.trustScore = Math.max(
347
+ 0,
348
+ this.currentSession.securityContext.trustScore - 15
349
+ );
350
+ }
351
+
352
+ this.currentSession.securityContext.lastVerificationAt = new Date().toISOString();
353
+ this.saveSession(this.currentSession);
354
+ }
355
+
356
+ /**
357
+ * Get current session
358
+ */
359
+ getSession(): AgentSession | null {
360
+ return this.currentSession;
361
+ }
362
+
363
+ /**
364
+ * Get session summary
365
+ */
366
+ getSessionSummary(): SessionSummary | null {
367
+ if (!this.currentSession) return null;
368
+
369
+ const session = this.currentSession;
370
+ const startTime = new Date(session.startedAt);
371
+ const now = new Date();
372
+ const durationMs = now.getTime() - startTime.getTime();
373
+
374
+ const hours = Math.floor(durationMs / (1000 * 60 * 60));
375
+ const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
376
+ const seconds = Math.floor((durationMs % (1000 * 60)) / 1000);
377
+
378
+ const successRate = session.metrics.totalToolCalls > 0
379
+ ? ((session.metrics.successfulCalls / session.metrics.totalToolCalls) * 100).toFixed(1)
380
+ : '100';
381
+
382
+ const recentCalls = session.toolCalls.slice(-5).map(tc =>
383
+ `${tc.toolName} (${tc.status})`
384
+ );
385
+
386
+ return {
387
+ id: session.id,
388
+ duration: `${hours}h ${minutes}m ${seconds}s`,
389
+ status: session.status,
390
+ metrics: {
391
+ totalCalls: session.metrics.totalToolCalls,
392
+ successRate: `${successRate}%`,
393
+ avgLatency: `${Math.round(session.metrics.averageLatencyMs)}ms`,
394
+ },
395
+ intent: session.currentIntent?.summary,
396
+ recentActivity: recentCalls,
397
+ };
398
+ }
399
+
400
+ /**
401
+ * Get detailed session metrics
402
+ */
403
+ getDetailedMetrics(): SessionMetrics & { trustScore: number; sessionDuration: string } | null {
404
+ if (!this.currentSession) return null;
405
+
406
+ const startTime = new Date(this.currentSession.startedAt);
407
+ const now = new Date();
408
+ const durationMs = now.getTime() - startTime.getTime();
409
+ const hours = Math.floor(durationMs / (1000 * 60 * 60));
410
+ const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
411
+
412
+ return {
413
+ ...this.currentSession.metrics,
414
+ trustScore: this.currentSession.securityContext.trustScore,
415
+ sessionDuration: `${hours}h ${minutes}m`,
416
+ };
417
+ }
418
+
419
+ /**
420
+ * Get tool call history
421
+ */
422
+ getToolCallHistory(limit?: number): ToolCallRecord[] {
423
+ if (!this.currentSession) return [];
424
+
425
+ const calls = this.currentSession.toolCalls;
426
+ return limit ? calls.slice(-limit) : calls;
427
+ }
428
+
429
+ /**
430
+ * Get state change history
431
+ */
432
+ getStateChangeHistory(limit?: number): StateChange[] {
433
+ if (!this.currentSession) return [];
434
+
435
+ const changes = this.currentSession.stateChanges;
436
+ return limit ? changes.slice(-limit) : changes;
437
+ }
438
+
439
+ /**
440
+ * Check if session is healthy
441
+ */
442
+ isSessionHealthy(): { healthy: boolean; issues: string[] } {
443
+ if (!this.currentSession) {
444
+ return { healthy: false, issues: ['No active session'] };
445
+ }
446
+
447
+ const issues: string[] = [];
448
+ const session = this.currentSession;
449
+
450
+ // Check expiry
451
+ if (new Date(session.expiresAt) <= new Date()) {
452
+ issues.push('Session has expired');
453
+ }
454
+
455
+ // Check trust score
456
+ if (session.securityContext.trustScore < 50) {
457
+ issues.push(`Low trust score (${session.securityContext.trustScore}/100)`);
458
+ }
459
+
460
+ // Check error rate
461
+ if (session.metrics.totalToolCalls > 10) {
462
+ const errorRate = session.metrics.failedCalls / session.metrics.totalToolCalls;
463
+ if (errorRate > 0.3) {
464
+ issues.push(`High error rate (${(errorRate * 100).toFixed(1)}%)`);
465
+ }
466
+ }
467
+
468
+ // Check firewall violations
469
+ if (session.metrics.firewallViolations > 5) {
470
+ issues.push(`Multiple firewall violations (${session.metrics.firewallViolations})`);
471
+ }
472
+
473
+ return {
474
+ healthy: issues.length === 0,
475
+ issues,
476
+ };
477
+ }
478
+
479
+ /**
480
+ * Terminate session
481
+ */
482
+ terminateSession(reason?: string): void {
483
+ if (!this.currentSession) return;
484
+
485
+ this.currentSession.status = 'terminated';
486
+ this.recordStateChange({
487
+ type: 'mode_changed',
488
+ before: 'active',
489
+ after: 'terminated',
490
+ metadata: { reason },
491
+ });
492
+
493
+ this.saveSession(this.currentSession);
494
+ }
495
+
496
+ /**
497
+ * Save session to disk
498
+ */
499
+ private saveSession(session: AgentSession): void {
500
+ try {
501
+ const filePath = path.join(this.sessionStorePath, `${session.id}.json`);
502
+ fs.writeFileSync(filePath, JSON.stringify(session, null, 2));
503
+ } catch {
504
+ // Failed to save - continue anyway
505
+ }
506
+ }
507
+
508
+ /**
509
+ * Load session from disk
510
+ */
511
+ loadSession(sessionId: string): AgentSession | null {
512
+ try {
513
+ const filePath = path.join(this.sessionStorePath, `${sessionId}.json`);
514
+ if (fs.existsSync(filePath)) {
515
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
516
+ }
517
+ } catch {
518
+ // Failed to load
519
+ }
520
+ return null;
521
+ }
522
+
523
+ /**
524
+ * List recent sessions
525
+ */
526
+ listRecentSessions(limit: number = 10): { id: string; startedAt: string; status: string }[] {
527
+ try {
528
+ const files = fs.readdirSync(this.sessionStorePath)
529
+ .filter(f => f.endsWith('.json'))
530
+ .map(f => {
531
+ const filePath = path.join(this.sessionStorePath, f);
532
+ const stats = fs.statSync(filePath);
533
+ return { file: f, mtime: stats.mtime };
534
+ })
535
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime())
536
+ .slice(0, limit);
537
+
538
+ return files.map(f => {
539
+ const session = this.loadSession(f.file.replace('.json', ''));
540
+ return session ? {
541
+ id: session.id,
542
+ startedAt: session.startedAt,
543
+ status: session.status,
544
+ } : null;
545
+ }).filter((s): s is { id: string; startedAt: string; status: string } => s !== null);
546
+ } catch {
547
+ return [];
548
+ }
549
+ }
550
+ }