@vibecheckai/cli 3.9.0 → 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 (88) hide show
  1. package/README.md +1 -1
  2. package/bin/runners/context/generators/cursor-enhanced.js +99 -13
  3. package/bin/runners/lib/unified-cli-output.js +16 -0
  4. package/bin/runners/runCI.js +353 -0
  5. package/bin/runners/runCheckpoint.js +2 -2
  6. package/mcp-server/.eslintrc.json +24 -0
  7. package/mcp-server/README.md +425 -135
  8. package/mcp-server/SPEC.md +583 -0
  9. package/mcp-server/configs/README.md +172 -0
  10. package/mcp-server/configs/claude-desktop-pro.json +31 -0
  11. package/mcp-server/configs/claude-desktop-with-workspace.json +25 -0
  12. package/mcp-server/configs/claude-desktop.json +19 -0
  13. package/mcp-server/configs/cursor-mcp.json +21 -0
  14. package/mcp-server/configs/windsurf-mcp.json +17 -0
  15. package/mcp-server/mcp-config.example.json +9 -0
  16. package/mcp-server/package.json +49 -34
  17. package/mcp-server/src/cli.ts +185 -0
  18. package/mcp-server/src/index.ts +85 -0
  19. package/mcp-server/src/server.ts +1933 -0
  20. package/mcp-server/src/services/cache-service.ts +466 -0
  21. package/mcp-server/src/services/cli-service.ts +345 -0
  22. package/mcp-server/src/services/context-manager.ts +717 -0
  23. package/mcp-server/src/services/firewall-service.ts +662 -0
  24. package/mcp-server/src/services/git-service.ts +671 -0
  25. package/mcp-server/src/services/index.ts +52 -0
  26. package/mcp-server/src/services/prompt-builder-service.ts +1031 -0
  27. package/mcp-server/src/services/session-service.ts +550 -0
  28. package/mcp-server/src/services/tier-service.ts +470 -0
  29. package/mcp-server/src/types.ts +351 -0
  30. package/mcp-server/tsconfig.json +16 -27
  31. package/package.json +6 -6
  32. package/mcp-server/.guardrail/audit/audit.log.jsonl +0 -2
  33. package/mcp-server/.specs/architecture.mdc +0 -90
  34. package/mcp-server/.specs/security.mdc +0 -30
  35. package/mcp-server/HARDENING_SUMMARY.md +0 -299
  36. package/mcp-server/agent-checkpoint.js +0 -364
  37. package/mcp-server/agent-firewall-interceptor.js +0 -500
  38. package/mcp-server/architect-tools.js +0 -707
  39. package/mcp-server/audit-mcp.js +0 -206
  40. package/mcp-server/authority-tools.js +0 -569
  41. package/mcp-server/codebase-architect-tools.js +0 -838
  42. package/mcp-server/conductor/conflict-resolver.js +0 -588
  43. package/mcp-server/conductor/execution-planner.js +0 -544
  44. package/mcp-server/conductor/index.js +0 -377
  45. package/mcp-server/conductor/lock-manager.js +0 -615
  46. package/mcp-server/conductor/request-queue.js +0 -550
  47. package/mcp-server/conductor/session-manager.js +0 -500
  48. package/mcp-server/conductor/tools.js +0 -510
  49. package/mcp-server/consolidated-tools.js +0 -1170
  50. package/mcp-server/deprecation-middleware.js +0 -282
  51. package/mcp-server/handlers/index.ts +0 -15
  52. package/mcp-server/handlers/tool-handler.ts +0 -593
  53. package/mcp-server/hygiene-tools.js +0 -428
  54. package/mcp-server/index-v1.js +0 -698
  55. package/mcp-server/index.js +0 -2940
  56. package/mcp-server/intelligence-tools.js +0 -664
  57. package/mcp-server/intent-drift-tools.js +0 -873
  58. package/mcp-server/intent-firewall-interceptor.js +0 -529
  59. package/mcp-server/lib/api-client.cjs +0 -13
  60. package/mcp-server/lib/cache-wrapper.cjs +0 -383
  61. package/mcp-server/lib/error-envelope.js +0 -138
  62. package/mcp-server/lib/executor.ts +0 -499
  63. package/mcp-server/lib/index.ts +0 -29
  64. package/mcp-server/lib/logger.cjs +0 -30
  65. package/mcp-server/lib/rate-limiter.js +0 -166
  66. package/mcp-server/lib/sandbox.test.ts +0 -519
  67. package/mcp-server/lib/sandbox.ts +0 -395
  68. package/mcp-server/lib/types.ts +0 -267
  69. package/mcp-server/logger.js +0 -173
  70. package/mcp-server/manifest.json +0 -473
  71. package/mcp-server/mdc-generator.js +0 -298
  72. package/mcp-server/premium-tools.js +0 -1275
  73. package/mcp-server/proof-tools.js +0 -571
  74. package/mcp-server/registry/tool-registry.js +0 -586
  75. package/mcp-server/registry/tools.json +0 -619
  76. package/mcp-server/registry.test.ts +0 -340
  77. package/mcp-server/test-mcp.js +0 -108
  78. package/mcp-server/test-tools.js +0 -36
  79. package/mcp-server/tests/tier-gating.test.js +0 -297
  80. package/mcp-server/tier-auth.js +0 -767
  81. package/mcp-server/tools/index.js +0 -72
  82. package/mcp-server/tools-reorganized.ts +0 -244
  83. package/mcp-server/tools-v3.js +0 -1004
  84. package/mcp-server/truth-context.js +0 -622
  85. package/mcp-server/truth-firewall-tools.js +0 -2183
  86. package/mcp-server/vibecheck-2.0-tools.js +0 -761
  87. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
  88. package/mcp-server/vibecheck-tools.js +0 -1075
@@ -0,0 +1,662 @@
1
+ /**
2
+ * Enhanced Firewall Service for MCP Server
3
+ * Provides agent-aware firewall with intent tracking, claim verification, and action gating
4
+ */
5
+
6
+ import { CliService } from './cli-service.js';
7
+ import type {
8
+ FirewallMode,
9
+ FirewallStatus,
10
+ FirewallVerdict,
11
+ Intent,
12
+ IntentTemplate,
13
+ ShieldCheckResult,
14
+ ClaimVerificationRequest,
15
+ ClaimVerificationResult,
16
+ ClaimEvidence,
17
+ } from '../types.js';
18
+
19
+ // Pre-defined intent templates for common tasks
20
+ const INTENT_TEMPLATES: IntentTemplate[] = [
21
+ {
22
+ name: 'Add Auth',
23
+ summary: 'Add authentication feature',
24
+ constraints: ['Use existing auth middleware', 'No new environment variables', 'Do not change billing code'],
25
+ description: 'Safe for adding authentication without touching sensitive areas',
26
+ },
27
+ {
28
+ name: 'Add Route',
29
+ summary: 'Add new API route',
30
+ constraints: ['No new env vars unless declared', 'No auth changes', 'Follow existing route patterns'],
31
+ description: 'For adding new API endpoints with existing patterns',
32
+ },
33
+ {
34
+ name: 'Bug Fix',
35
+ summary: 'Fix a specific bug',
36
+ constraints: ['Minimal code changes', 'No new dependencies', 'No refactoring unrelated code'],
37
+ description: 'Tightly scoped bug fixes only',
38
+ },
39
+ {
40
+ name: 'Refactor',
41
+ summary: 'Refactor existing code',
42
+ constraints: ['No behavior changes', 'Preserve all tests', 'No new features'],
43
+ description: 'Code quality improvements without behavior changes',
44
+ },
45
+ {
46
+ name: 'Add Feature',
47
+ summary: 'Add new feature',
48
+ constraints: ['Use existing patterns', 'Add tests for new code', 'Update documentation'],
49
+ description: 'Feature additions with proper testing',
50
+ },
51
+ {
52
+ name: 'Payment Flow',
53
+ summary: 'Modify payment/billing code',
54
+ constraints: ['No auth changes', 'Preserve existing integrations', 'Add audit logging'],
55
+ description: 'Sensitive payment code changes with audit trail',
56
+ },
57
+ {
58
+ name: 'Database Migration',
59
+ summary: 'Modify database schema',
60
+ constraints: ['Create rollback migration', 'No data deletion', 'Test in staging first'],
61
+ description: 'Safe database schema changes',
62
+ },
63
+ {
64
+ name: 'Dependency Update',
65
+ summary: 'Update project dependencies',
66
+ constraints: ['Update one major version at a time', 'Run full test suite', 'Check for breaking changes'],
67
+ description: 'Controlled dependency updates',
68
+ },
69
+ ];
70
+
71
+ // Action categories for gating
72
+ type ActionCategory = 'read' | 'write' | 'execute' | 'network' | 'sensitive';
73
+
74
+ interface ActionGateResult {
75
+ allowed: boolean;
76
+ reason: string;
77
+ requiresIntent: boolean;
78
+ suggestedIntent?: IntentTemplate;
79
+ }
80
+
81
+ export class FirewallService {
82
+ private cliService: CliService;
83
+ private mode: FirewallMode = 'off';
84
+ private currentIntent: Intent | null = null;
85
+ private violationCount: number = 0;
86
+ private blockedCount: number = 0;
87
+ private sessionId: string;
88
+ private actionLog: ActionLogEntry[] = [];
89
+
90
+ constructor(cliService: CliService) {
91
+ this.cliService = cliService;
92
+ this.sessionId = this.generateSessionId();
93
+ this.initialize();
94
+ }
95
+
96
+ private async initialize(): Promise<void> {
97
+ const status = await this.getStatus();
98
+ this.mode = status.mode;
99
+ if (status.currentIntent) {
100
+ this.currentIntent = status.currentIntent;
101
+ }
102
+ }
103
+
104
+ private generateSessionId(): string {
105
+ return `mcp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
106
+ }
107
+
108
+ // ═══════════════════════════════════════════════════════════════════════════════
109
+ // Status & Mode Management
110
+ // ═══════════════════════════════════════════════════════════════════════════════
111
+
112
+ /**
113
+ * Get firewall status
114
+ */
115
+ async getStatus(): Promise<FirewallStatus> {
116
+ const result = await this.cliService.shieldStatus();
117
+
118
+ if (result.success && result.data) {
119
+ const data = result.data as Record<string, unknown>;
120
+ this.mode = (data.mode as FirewallMode) || 'off';
121
+ return {
122
+ mode: this.mode,
123
+ enabled: this.mode !== 'off',
124
+ violationCount: (data.violationCount as number) || this.violationCount,
125
+ blockedCount: (data.blockedCount as number) || this.blockedCount,
126
+ lastCheck: data.lastCheck as string | undefined,
127
+ currentIntent: this.currentIntent || undefined,
128
+ };
129
+ }
130
+
131
+ return {
132
+ mode: this.mode,
133
+ enabled: this.mode !== 'off',
134
+ violationCount: this.violationCount,
135
+ blockedCount: this.blockedCount,
136
+ currentIntent: this.currentIntent || undefined,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Set firewall mode
142
+ */
143
+ async setMode(mode: FirewallMode): Promise<boolean> {
144
+ const result = await this.cliService.shieldSetMode(mode);
145
+ if (result.success) {
146
+ this.mode = mode;
147
+ return true;
148
+ }
149
+ return false;
150
+ }
151
+
152
+ /**
153
+ * Get current mode
154
+ */
155
+ getMode(): FirewallMode {
156
+ return this.mode;
157
+ }
158
+
159
+ /**
160
+ * Check if firewall is enabled
161
+ */
162
+ isEnabled(): boolean {
163
+ return this.mode !== 'off';
164
+ }
165
+
166
+ // ═══════════════════════════════════════════════════════════════════════════════
167
+ // Intent Management (Enhanced for MCP)
168
+ // ═══════════════════════════════════════════════════════════════════════════════
169
+
170
+ /**
171
+ * Get current intent
172
+ */
173
+ async getIntent(): Promise<Intent | null> {
174
+ const result = await this.cliService.intentShow();
175
+
176
+ if (result.success && result.data) {
177
+ const data = result.data as Record<string, unknown>;
178
+ const intent: Intent = {
179
+ summary: (data.summary as string) || (data.intent as string) || '',
180
+ constraints: (data.constraints as string[]) || [],
181
+ timestamp: data.timestamp as string | undefined,
182
+ sessionId: data.sessionId as string | undefined,
183
+ hash: data.hash as string | undefined,
184
+ };
185
+
186
+ if (intent.summary) {
187
+ this.currentIntent = intent;
188
+ return intent;
189
+ }
190
+ }
191
+
192
+ // Try parsing non-JSON output
193
+ if (result.output && result.output.includes('Intent:')) {
194
+ const summaryMatch = result.output.match(/Intent:\s*(.+)/);
195
+ if (summaryMatch) {
196
+ const intent: Intent = {
197
+ summary: summaryMatch[1].trim(),
198
+ constraints: [],
199
+ };
200
+ this.currentIntent = intent;
201
+ return intent;
202
+ }
203
+ }
204
+
205
+ this.currentIntent = null;
206
+ return null;
207
+ }
208
+
209
+ /**
210
+ * Set intent with summary and constraints
211
+ */
212
+ async setIntent(summary: string, constraints: string[] = []): Promise<boolean> {
213
+ const result = await this.cliService.intentSet(summary, constraints);
214
+
215
+ if (result.success) {
216
+ this.currentIntent = {
217
+ summary,
218
+ constraints,
219
+ timestamp: new Date().toISOString(),
220
+ sessionId: this.sessionId,
221
+ };
222
+
223
+ // Auto-enable observe mode if off
224
+ if (this.mode === 'off') {
225
+ await this.setMode('observe');
226
+ }
227
+
228
+ this.logAction('intent_set', { summary, constraints });
229
+ return true;
230
+ }
231
+
232
+ return false;
233
+ }
234
+
235
+ /**
236
+ * Set intent from a template
237
+ */
238
+ async setIntentFromTemplate(templateName: string, customSummary?: string): Promise<boolean> {
239
+ const template = INTENT_TEMPLATES.find(t =>
240
+ t.name.toLowerCase() === templateName.toLowerCase()
241
+ );
242
+
243
+ if (!template) {
244
+ return false;
245
+ }
246
+
247
+ const summary = customSummary || template.summary;
248
+ return this.setIntent(summary, template.constraints);
249
+ }
250
+
251
+ /**
252
+ * Clear current intent
253
+ */
254
+ async clearIntent(): Promise<boolean> {
255
+ const result = await this.cliService.intentClear();
256
+
257
+ if (result.success) {
258
+ this.currentIntent = null;
259
+ this.logAction('intent_cleared', {});
260
+ return true;
261
+ }
262
+
263
+ return false;
264
+ }
265
+
266
+ /**
267
+ * Get cached intent (no CLI call)
268
+ */
269
+ getCurrentIntent(): Intent | null {
270
+ return this.currentIntent;
271
+ }
272
+
273
+ /**
274
+ * Check if intent is set
275
+ */
276
+ hasIntent(): boolean {
277
+ return this.currentIntent !== null && this.currentIntent.summary.length > 0;
278
+ }
279
+
280
+ /**
281
+ * Get all intent templates
282
+ */
283
+ getTemplates(): IntentTemplate[] {
284
+ return INTENT_TEMPLATES;
285
+ }
286
+
287
+ /**
288
+ * Get a specific template by name
289
+ */
290
+ getTemplate(name: string): IntentTemplate | undefined {
291
+ return INTENT_TEMPLATES.find(t =>
292
+ t.name.toLowerCase() === name.toLowerCase()
293
+ );
294
+ }
295
+
296
+ // ═══════════════════════════════════════════════════════════════════════════════
297
+ // Shield Check & Verification
298
+ // ═══════════════════════════════════════════════════════════════════════════════
299
+
300
+ /**
301
+ * Run comprehensive shield check
302
+ */
303
+ async check(): Promise<ShieldCheckResult | null> {
304
+ const result = await this.cliService.shieldCheck();
305
+
306
+ if (result.success && result.data) {
307
+ const data = result.data as Record<string, unknown>;
308
+ const checkResult: ShieldCheckResult = {
309
+ passed: (data.passed as boolean) ?? data.verdict === 'SHIP',
310
+ score: (data.score as number) || 0,
311
+ verdict: (data.verdict as 'SHIP' | 'WARN' | 'BLOCK') || 'WARN',
312
+ findings: (data.findings as ShieldCheckResult['findings']) || [],
313
+ truthpack: data.truthpack as ShieldCheckResult['truthpack'],
314
+ };
315
+
316
+ this.logAction('shield_check', { verdict: checkResult.verdict, score: checkResult.score });
317
+ return checkResult;
318
+ }
319
+
320
+ return null;
321
+ }
322
+
323
+ /**
324
+ * Verify AI claims against current state
325
+ */
326
+ async verify(claims?: string): Promise<FirewallVerdict | null> {
327
+ const result = await this.cliService.shieldVerify(claims);
328
+
329
+ if (result.success && result.data) {
330
+ const data = result.data as Record<string, unknown>;
331
+ const verdict: FirewallVerdict = {
332
+ allowed: (data.allowed as boolean) ?? true,
333
+ verdict: (data.verdict as 'ALLOW' | 'WARN' | 'BLOCK') || 'ALLOW',
334
+ violations: (data.violations as FirewallVerdict['violations']) || [],
335
+ unblockPlan: data.unblockPlan as FirewallVerdict['unblockPlan'],
336
+ };
337
+
338
+ if (!verdict.allowed) {
339
+ this.violationCount++;
340
+ if (verdict.verdict === 'BLOCK') {
341
+ this.blockedCount++;
342
+ }
343
+ }
344
+
345
+ this.logAction('verify', { verdict: verdict.verdict, allowed: verdict.allowed });
346
+ return verdict;
347
+ }
348
+
349
+ return null;
350
+ }
351
+
352
+ // ═══════════════════════════════════════════════════════════════════════════════
353
+ // Enhanced MCP-Specific Features
354
+ // ═══════════════════════════════════════════════════════════════════════════════
355
+
356
+ /**
357
+ * Verify a specific claim made by an AI agent
358
+ * This is an enhanced verification that analyzes the claim semantically
359
+ */
360
+ async verifyClaim(request: ClaimVerificationRequest): Promise<ClaimVerificationResult> {
361
+ // First, check if we have an intent set
362
+ if (this.mode === 'enforce' && !this.hasIntent()) {
363
+ return {
364
+ verified: false,
365
+ confidence: 0,
366
+ verdict: 'REJECTED',
367
+ reasons: ['No intent is set. In enforce mode, all actions require an intent.'],
368
+ };
369
+ }
370
+
371
+ // Run shield verification
372
+ const shieldResult = await this.verify(request.claim);
373
+
374
+ // Build evidence array
375
+ const evidence: ClaimEvidence[] = [];
376
+
377
+ // Check claim against intent constraints
378
+ const constraintViolations: string[] = [];
379
+ if (this.currentIntent) {
380
+ for (const constraint of this.currentIntent.constraints) {
381
+ // Simple heuristic check for constraint violations
382
+ const claimLower = request.claim.toLowerCase();
383
+ const constraintLower = constraint.toLowerCase();
384
+
385
+ // Check for obvious violations
386
+ if (constraintLower.includes('no new') && claimLower.includes('add')) {
387
+ if (constraintLower.includes('env') && claimLower.includes('env')) {
388
+ constraintViolations.push(`Constraint violation: "${constraint}"`);
389
+ }
390
+ if (constraintLower.includes('dependencies') && claimLower.includes('install')) {
391
+ constraintViolations.push(`Constraint violation: "${constraint}"`);
392
+ }
393
+ }
394
+ if (constraintLower.includes('no auth') && claimLower.includes('auth')) {
395
+ constraintViolations.push(`Constraint violation: "${constraint}"`);
396
+ }
397
+ if (constraintLower.includes('no behavior') && claimLower.includes('change')) {
398
+ constraintViolations.push(`Constraint violation: "${constraint}"`);
399
+ }
400
+ }
401
+ }
402
+
403
+ // Calculate confidence based on findings
404
+ let confidence = 100;
405
+ const reasons: string[] = [];
406
+
407
+ if (shieldResult) {
408
+ if (!shieldResult.allowed) {
409
+ confidence -= 50;
410
+ reasons.push('Shield verification failed');
411
+ }
412
+ for (const violation of shieldResult.violations || []) {
413
+ confidence -= 10;
414
+ reasons.push(violation.message);
415
+ evidence.push({
416
+ type: 'behavior',
417
+ content: violation.message,
418
+ relevance: 0.8,
419
+ });
420
+ }
421
+ }
422
+
423
+ for (const violation of constraintViolations) {
424
+ confidence -= 20;
425
+ reasons.push(violation);
426
+ }
427
+
428
+ // Determine verdict
429
+ let verdict: 'VERIFIED' | 'SUSPICIOUS' | 'REJECTED';
430
+ if (confidence >= 80) {
431
+ verdict = 'VERIFIED';
432
+ } else if (confidence >= 50) {
433
+ verdict = 'SUSPICIOUS';
434
+ } else {
435
+ verdict = 'REJECTED';
436
+ }
437
+
438
+ const result: ClaimVerificationResult = {
439
+ verified: confidence >= 80,
440
+ confidence: Math.max(0, confidence),
441
+ verdict,
442
+ reasons: reasons.length > 0 ? reasons : ['Claim appears valid'],
443
+ evidence: evidence.length > 0 ? evidence : undefined,
444
+ };
445
+
446
+ this.logAction('claim_verification', {
447
+ claim: request.claim.substring(0, 100),
448
+ verdict,
449
+ confidence,
450
+ });
451
+
452
+ return result;
453
+ }
454
+
455
+ /**
456
+ * Gate an action based on current firewall state and intent
457
+ */
458
+ gateAction(action: string, category: ActionCategory): ActionGateResult {
459
+ // If firewall is off, allow everything
460
+ if (this.mode === 'off') {
461
+ return {
462
+ allowed: true,
463
+ reason: 'Firewall is disabled',
464
+ requiresIntent: false,
465
+ };
466
+ }
467
+
468
+ // Check if intent is required for this action category
469
+ const sensitiveCategories: ActionCategory[] = ['write', 'execute', 'sensitive'];
470
+ const requiresIntent = sensitiveCategories.includes(category);
471
+
472
+ // In observe mode, allow but warn
473
+ if (this.mode === 'observe') {
474
+ if (requiresIntent && !this.hasIntent()) {
475
+ return {
476
+ allowed: true,
477
+ reason: 'Action allowed (observe mode) but intent is recommended',
478
+ requiresIntent: true,
479
+ suggestedIntent: this.suggestIntentForAction(action),
480
+ };
481
+ }
482
+ return {
483
+ allowed: true,
484
+ reason: 'Action allowed (observe mode)',
485
+ requiresIntent: false,
486
+ };
487
+ }
488
+
489
+ // In enforce mode, block sensitive actions without intent
490
+ if (this.mode === 'enforce') {
491
+ if (requiresIntent && !this.hasIntent()) {
492
+ this.blockedCount++;
493
+ return {
494
+ allowed: false,
495
+ reason: 'Intent required for this action in enforce mode',
496
+ requiresIntent: true,
497
+ suggestedIntent: this.suggestIntentForAction(action),
498
+ };
499
+ }
500
+
501
+ // Check if action matches intent
502
+ if (this.currentIntent) {
503
+ const matchResult = this.matchActionToIntent(action);
504
+ if (!matchResult.matches) {
505
+ this.violationCount++;
506
+ return {
507
+ allowed: false,
508
+ reason: `Action does not match current intent: ${matchResult.reason}`,
509
+ requiresIntent: true,
510
+ };
511
+ }
512
+ }
513
+
514
+ return {
515
+ allowed: true,
516
+ reason: 'Action allowed (matches intent)',
517
+ requiresIntent: false,
518
+ };
519
+ }
520
+
521
+ return {
522
+ allowed: true,
523
+ reason: 'Action allowed',
524
+ requiresIntent: false,
525
+ };
526
+ }
527
+
528
+ /**
529
+ * Suggest an intent template based on the action
530
+ */
531
+ private suggestIntentForAction(action: string): IntentTemplate | undefined {
532
+ const actionLower = action.toLowerCase();
533
+
534
+ if (actionLower.includes('auth') || actionLower.includes('login')) {
535
+ return this.getTemplate('Add Auth');
536
+ }
537
+ if (actionLower.includes('route') || actionLower.includes('api') || actionLower.includes('endpoint')) {
538
+ return this.getTemplate('Add Route');
539
+ }
540
+ if (actionLower.includes('fix') || actionLower.includes('bug')) {
541
+ return this.getTemplate('Bug Fix');
542
+ }
543
+ if (actionLower.includes('refactor') || actionLower.includes('clean')) {
544
+ return this.getTemplate('Refactor');
545
+ }
546
+ if (actionLower.includes('payment') || actionLower.includes('billing') || actionLower.includes('stripe')) {
547
+ return this.getTemplate('Payment Flow');
548
+ }
549
+ if (actionLower.includes('database') || actionLower.includes('migration') || actionLower.includes('schema')) {
550
+ return this.getTemplate('Database Migration');
551
+ }
552
+ if (actionLower.includes('update') || actionLower.includes('upgrade') || actionLower.includes('dependency')) {
553
+ return this.getTemplate('Dependency Update');
554
+ }
555
+
556
+ return this.getTemplate('Add Feature');
557
+ }
558
+
559
+ /**
560
+ * Check if an action matches the current intent
561
+ */
562
+ private matchActionToIntent(action: string): { matches: boolean; reason: string } {
563
+ if (!this.currentIntent) {
564
+ return { matches: false, reason: 'No intent set' };
565
+ }
566
+
567
+ const actionLower = action.toLowerCase();
568
+ const intentLower = this.currentIntent.summary.toLowerCase();
569
+
570
+ // Check constraints
571
+ for (const constraint of this.currentIntent.constraints) {
572
+ const constraintLower = constraint.toLowerCase();
573
+
574
+ // "No new X" constraints
575
+ if (constraintLower.startsWith('no new') || constraintLower.startsWith('no ')) {
576
+ const forbidden = constraintLower.replace(/^no (new )?/, '').trim();
577
+ if (actionLower.includes(forbidden)) {
578
+ return { matches: false, reason: `Violates constraint: ${constraint}` };
579
+ }
580
+ }
581
+
582
+ // "Do not X" constraints
583
+ if (constraintLower.startsWith('do not')) {
584
+ const forbidden = constraintLower.replace('do not', '').trim();
585
+ if (actionLower.includes(forbidden)) {
586
+ return { matches: false, reason: `Violates constraint: ${constraint}` };
587
+ }
588
+ }
589
+ }
590
+
591
+ return { matches: true, reason: 'Action matches intent constraints' };
592
+ }
593
+
594
+ // ═══════════════════════════════════════════════════════════════════════════════
595
+ // Action Logging (Audit Trail for MCP)
596
+ // ═══════════════════════════════════════════════════════════════════════════════
597
+
598
+ private logAction(action: string, details: Record<string, unknown>): void {
599
+ this.actionLog.push({
600
+ timestamp: new Date().toISOString(),
601
+ sessionId: this.sessionId,
602
+ action,
603
+ details,
604
+ mode: this.mode,
605
+ hasIntent: this.hasIntent(),
606
+ });
607
+
608
+ // Keep only last 1000 entries
609
+ if (this.actionLog.length > 1000) {
610
+ this.actionLog = this.actionLog.slice(-1000);
611
+ }
612
+ }
613
+
614
+ /**
615
+ * Get action log
616
+ */
617
+ getActionLog(limit?: number): ActionLogEntry[] {
618
+ const entries = limit ? this.actionLog.slice(-limit) : this.actionLog;
619
+ return entries;
620
+ }
621
+
622
+ /**
623
+ * Get session ID
624
+ */
625
+ getSessionId(): string {
626
+ return this.sessionId;
627
+ }
628
+
629
+ /**
630
+ * Get statistics
631
+ */
632
+ getStats(): FirewallStats {
633
+ return {
634
+ sessionId: this.sessionId,
635
+ mode: this.mode,
636
+ violationCount: this.violationCount,
637
+ blockedCount: this.blockedCount,
638
+ actionLogSize: this.actionLog.length,
639
+ hasIntent: this.hasIntent(),
640
+ currentIntent: this.currentIntent?.summary,
641
+ };
642
+ }
643
+ }
644
+
645
+ interface ActionLogEntry {
646
+ timestamp: string;
647
+ sessionId: string;
648
+ action: string;
649
+ details: Record<string, unknown>;
650
+ mode: FirewallMode;
651
+ hasIntent: boolean;
652
+ }
653
+
654
+ interface FirewallStats {
655
+ sessionId: string;
656
+ mode: FirewallMode;
657
+ violationCount: number;
658
+ blockedCount: number;
659
+ actionLogSize: number;
660
+ hasIntent: boolean;
661
+ currentIntent?: string;
662
+ }