pumuki-ast-hooks 5.5.3 → 5.5.5
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/package.json +2 -2
- package/scripts/hooks-system/.audit-reports/auto-recovery.log +2 -0
- package/scripts/hooks-system/.audit-reports/install-wizard.log +8 -0
- package/scripts/hooks-system/.audit_tmp/audit.log +0 -0
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +48 -0
- package/scripts/hooks-system/.hook-system/config.json +8 -0
- package/scripts/hooks-system/application/CompositionRoot.js +31 -19
- package/scripts/hooks-system/application/factories/AdapterFactory.js +6 -6
- package/scripts/hooks-system/application/services/PredictiveHookAdvisor.js +0 -2
- package/scripts/hooks-system/application/services/RealtimeGuardService.js +146 -2
- package/scripts/hooks-system/application/services/SmartDirtyTreeAnalyzer.js +0 -1
- package/scripts/hooks-system/application/services/guard/EvidenceManager.js +31 -7
- package/scripts/hooks-system/application/services/guard/GitTreeManager.js +43 -11
- package/scripts/hooks-system/application/services/guard/GuardNotifier.js +2 -2
- package/scripts/hooks-system/application/services/installation/ConfigurationGeneratorService.js +0 -2
- package/scripts/hooks-system/application/services/installation/GitEnvironmentService.js +0 -3
- package/scripts/hooks-system/application/services/installation/InstallService.js +2 -4
- package/scripts/hooks-system/application/services/installation/McpConfigurator.js +9 -2
- package/scripts/hooks-system/application/services/logging/UnifiedLogger.js +1 -1
- package/scripts/hooks-system/application/services/monitoring/AstMonitor.js +0 -2
- package/scripts/hooks-system/application/services/monitoring/DevDocsMonitor.js +0 -2
- package/scripts/hooks-system/application/services/monitoring/GitTreeMonitorService.js +0 -3
- package/scripts/hooks-system/application/services/notification/NotificationCenterService.js +22 -0
- package/scripts/hooks-system/application/services/notification/components/NotificationRetryExecutor.js +1 -1
- package/scripts/hooks-system/application/services/recovery/AutoRecoveryManager.js +0 -1
- package/scripts/hooks-system/application/services/token/TokenMetricsService.js +1 -1
- package/scripts/hooks-system/application/services/token/TokenMonitorService.js +0 -1
- package/scripts/hooks-system/application/use-cases/AnalyzeCodebaseUseCase.js +0 -2
- package/scripts/hooks-system/application/use-cases/AnalyzeStagedFilesUseCase.js +0 -2
- package/scripts/hooks-system/application/use-cases/AutoExecuteAIStartUseCase.js +0 -1
- package/scripts/hooks-system/application/use-cases/BlockCommitUseCase.js +0 -2
- package/scripts/hooks-system/application/use-cases/GenerateAuditReportUseCase.js +0 -3
- package/scripts/hooks-system/bin/cli.js +16 -0
- package/scripts/hooks-system/domain/services/AuditResultSerializer.js +0 -5
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidASTIntelligentAnalyzer.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidASTParser.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidAnalysisOrchestrator.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidArchitectureDetector.js +5 -7
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidClassAnalyzer.js +0 -3
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidForbiddenLiteralsAnalyzer.js +0 -1
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidSOLIDAnalyzer.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/archive/ios-rules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/archive/kotlin-analyzer.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/archive/kotlin-parser.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/archive/swift-analyzer.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/backend/analyzers/BackendArchitectureDetector.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/backend/analyzers/BackendPatternDetector.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/common/BDDTDDWorkflowRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/common/rules/BDDRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/common/rules/ImplementationRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/common/rules/TDDRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/common/rules/WorkflowRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendArchitectureDetector.js +14 -16
- package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendForbiddenLiteralsAnalyzer.js +0 -5
- package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendSOLIDAnalyzer.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSASTIntelligentAnalyzer.js +17 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureDetector.js +10 -12
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSCICDRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSEnterpriseAnalyzer.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSForbiddenLiteralsAnalyzer.js +0 -1
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSNetworkingAdvancedRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSPerformanceRules.js +0 -3
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSSPMRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSSwiftUIAdvancedRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSTestingAdvancedRules.js +0 -2
- package/scripts/hooks-system/infrastructure/ast/ios/native-bridge.js +0 -1
- package/scripts/hooks-system/infrastructure/ast/ios/parsers/SourceKittenParser.js +0 -2
- package/scripts/hooks-system/infrastructure/logging/UnifiedLoggerFactory.js +1 -1
- package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +149 -57
- package/scripts/hooks-system/infrastructure/mcp/services/McpProtocolHandler.js +14 -6
- package/scripts/hooks-system/infrastructure/repositories/CursorTokenRepository.js +0 -2
- package/scripts/hooks-system/infrastructure/repositories/FileFindingsRepository.js +0 -2
- package/scripts/hooks-system/infrastructure/repositories/datasources/CursorApiDataSource.js +1 -3
- package/scripts/hooks-system/infrastructure/repositories/datasources/CursorFileDataSource.js +0 -2
- package/scripts/hooks-system/infrastructure/severity/severity-evaluator.js +0 -2
- package/scripts/hooks-system/infrastructure/telemetry/TelemetryService.js +0 -2
- package/scripts/hooks-system/infrastructure/utils/token-manager.js +0 -2
- package/scripts/hooks-system/infrastructure/validators/enforce-english-literals.js +1 -1
- package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log +9 -0
|
@@ -4,12 +4,10 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const crypto = require('crypto');
|
|
6
6
|
const env = require(path.join(__dirname, '../../../../config/env'));
|
|
7
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
8
7
|
|
|
9
8
|
class iOSASTIntelligentAnalyzer {
|
|
10
9
|
constructor(findings) {
|
|
11
10
|
this.findings = findings;
|
|
12
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
13
11
|
this.sourceKittenPath = '/opt/homebrew/bin/sourcekitten';
|
|
14
12
|
this.isAvailable = this.checkSourceKitten();
|
|
15
13
|
this.hasSwiftSyntax = this.checkSwiftSyntax();
|
|
@@ -644,6 +642,23 @@ class iOSASTIntelligentAnalyzer {
|
|
|
644
642
|
}
|
|
645
643
|
|
|
646
644
|
analyzeAdditionalRules(filePath) {
|
|
645
|
+
const hasSwiftUIViewType = (this.imports || []).some(i => i && i.name === 'SwiftUI') &&
|
|
646
|
+
(this.structs || []).some(s => (s['key.inheritedtypes'] || []).some(t => t && t['key.name'] === 'View'));
|
|
647
|
+
|
|
648
|
+
if (this.fileContent.includes('pushViewController') || this.fileContent.includes('popViewController') || this.fileContent.includes('present(')) {
|
|
649
|
+
const line = this.findLineNumber('pushViewController') || this.findLineNumber('popViewController') || this.findLineNumber('present(');
|
|
650
|
+
this.pushFinding('ios.navigation.imperative_navigation', 'critical', filePath, line,
|
|
651
|
+
'Imperative navigation detected - use event-driven navigation/coordinator');
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const swiftuiNavTokens = ['NavigationLink', 'NavigationStack', 'NavigationSplitView', '.navigationDestination'];
|
|
655
|
+
const hasSwiftUINavigation = swiftuiNavTokens.some(token => this.fileContent.includes(token));
|
|
656
|
+
if (hasSwiftUINavigation && !hasSwiftUIViewType) {
|
|
657
|
+
const line = this.findLineNumber('NavigationLink') || this.findLineNumber('NavigationStack') || this.findLineNumber('.navigationDestination');
|
|
658
|
+
this.pushFinding('ios.navigation.swiftui_navigation_outside_view', 'critical', filePath, line,
|
|
659
|
+
'SwiftUI navigation API detected outside View types');
|
|
660
|
+
}
|
|
661
|
+
|
|
647
662
|
if (filePath.includes('ViewModel') && this.fileContent.includes('NavigationLink')) {
|
|
648
663
|
const hasCoordinator = this.imports.some(i => i.name.includes('Coordinator'));
|
|
649
664
|
if (!hasCoordinator) {
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const glob = require('glob');
|
|
4
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
5
4
|
|
|
6
5
|
class iOSArchitectureDetector {
|
|
7
6
|
constructor(projectRoot) {
|
|
8
7
|
this.projectRoot = projectRoot;
|
|
9
|
-
this.auditLogger = new AuditLogger({ repoRoot: projectRoot });
|
|
10
8
|
this.patterns = {
|
|
11
|
-
featureFirstClean: 0,
|
|
9
|
+
featureFirstClean: 0,
|
|
12
10
|
mvvm: 0,
|
|
13
11
|
mvvmc: 0,
|
|
14
12
|
mvp: 0,
|
|
@@ -83,7 +81,7 @@ class iOSArchitectureDetector {
|
|
|
83
81
|
);
|
|
84
82
|
|
|
85
83
|
if (hasFeaturesFolders) {
|
|
86
|
-
this.patterns.featureFirstClean += 10;
|
|
84
|
+
this.patterns.featureFirstClean += 10;
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
if (foundCleanFolders.length >= 3) {
|
|
@@ -249,8 +247,8 @@ class iOSArchitectureDetector {
|
|
|
249
247
|
const content = this.readFile(file);
|
|
250
248
|
|
|
251
249
|
if (content.includes('protocol Coordinator') ||
|
|
252
|
-
|
|
253
|
-
|
|
250
|
+
content.includes(': Coordinator') ||
|
|
251
|
+
/func\s+start\(\)/.test(content) && /func\s+navigate/.test(content)) {
|
|
254
252
|
this.patterns.mvvmc += 2;
|
|
255
253
|
}
|
|
256
254
|
});
|
|
@@ -290,16 +288,16 @@ class iOSArchitectureDetector {
|
|
|
290
288
|
const interactorFiles = files.filter(f => /Interactor\.swift$/.test(f));
|
|
291
289
|
|
|
292
290
|
if (viewControllerFiles.length >= 2 &&
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
291
|
+
viewModelFiles.length === 0 &&
|
|
292
|
+
presenterFiles.length === 0 &&
|
|
293
|
+
interactorFiles.length === 0) {
|
|
296
294
|
|
|
297
295
|
viewControllerFiles.forEach(file => {
|
|
298
296
|
const content = this.readFile(file);
|
|
299
297
|
const lines = content.split('\n').length;
|
|
300
298
|
|
|
301
299
|
if (lines > 300) {
|
|
302
|
-
this.patterns.mvc += 3;
|
|
300
|
+
this.patterns.mvc += 3;
|
|
303
301
|
} else if (lines > 150) {
|
|
304
302
|
this.patterns.mvc += 2;
|
|
305
303
|
} else {
|
|
@@ -325,7 +323,7 @@ class iOSArchitectureDetector {
|
|
|
325
323
|
}
|
|
326
324
|
|
|
327
325
|
if (sorted.length > 1 && sorted[1][1] >= dominantScore * 0.7) {
|
|
328
|
-
return 'MIXED';
|
|
326
|
+
return 'MIXED';
|
|
329
327
|
}
|
|
330
328
|
|
|
331
329
|
return this.normalizePatternName(dominant);
|
|
@@ -333,7 +331,7 @@ class iOSArchitectureDetector {
|
|
|
333
331
|
|
|
334
332
|
normalizePatternName(pattern) {
|
|
335
333
|
const mapping = {
|
|
336
|
-
'featureFirstClean': 'FEATURE_FIRST_CLEAN_DDD',
|
|
334
|
+
'featureFirstClean': 'FEATURE_FIRST_CLEAN_DDD',
|
|
337
335
|
'mvvm': 'MVVM',
|
|
338
336
|
'mvvmc': 'MVVM-C',
|
|
339
337
|
'mvp': 'MVP',
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
const { pushFinding } = require('../../ast-core');
|
|
2
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
3
2
|
|
|
4
3
|
class iOSArchitectureRules {
|
|
5
4
|
constructor(findings, detectedPattern) {
|
|
6
5
|
this.findings = findings;
|
|
7
6
|
this.pattern = detectedPattern;
|
|
8
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
9
7
|
}
|
|
10
8
|
|
|
11
9
|
runRules(files) {
|
|
@@ -12,13 +12,11 @@ const { pushFinding } = require('../../ast-core');
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const glob = require('glob');
|
|
15
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
16
15
|
|
|
17
16
|
class iOSCICDRules {
|
|
18
17
|
constructor(findings, projectRoot) {
|
|
19
18
|
this.findings = findings;
|
|
20
19
|
this.projectRoot = projectRoot;
|
|
21
|
-
this.auditLogger = new AuditLogger({ repoRoot: projectRoot });
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
analyze() {
|
|
@@ -2,13 +2,11 @@ const path = require('path');
|
|
|
2
2
|
const fs = require('fs').promises;
|
|
3
3
|
const { SourceKittenParser } = require('../parsers/SourceKittenParser');
|
|
4
4
|
const { pushFinding, mapToLevel } = require(path.join(__dirname, '../../ast-core'));
|
|
5
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
6
5
|
|
|
7
6
|
class iOSEnterpriseAnalyzer {
|
|
8
7
|
constructor() {
|
|
9
8
|
this.parser = new SourceKittenParser();
|
|
10
9
|
this.findings = [];
|
|
11
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
12
10
|
}
|
|
13
11
|
|
|
14
12
|
async analyzeFile(filePath, findings) {
|
|
@@ -9,12 +9,10 @@
|
|
|
9
9
|
const { pushFinding } = require('../../ast-core');
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const glob = require('glob');
|
|
12
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
13
12
|
|
|
14
13
|
class iOSNetworkingAdvancedRules {
|
|
15
14
|
constructor(findings, projectRoot) {
|
|
16
15
|
this.findings = findings;
|
|
17
|
-
this.auditLogger = new AuditLogger({ repoRoot: projectRoot });
|
|
18
16
|
this.projectRoot = projectRoot;
|
|
19
17
|
}
|
|
20
18
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
2
|
-
|
|
3
1
|
class iOSPerformanceRules {
|
|
4
2
|
constructor(findings) {
|
|
5
3
|
this.findings = findings;
|
|
6
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
7
4
|
}
|
|
8
5
|
|
|
9
6
|
analyzeFile(filePath, content) {
|
|
@@ -13,13 +13,11 @@
|
|
|
13
13
|
const { pushFinding } = require('../../ast-core');
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
17
16
|
|
|
18
17
|
class iOSSPMRules {
|
|
19
18
|
constructor(findings, projectRoot) {
|
|
20
19
|
this.findings = findings;
|
|
21
20
|
this.projectRoot = projectRoot;
|
|
22
|
-
this.auditLogger = new AuditLogger({ repoRoot: projectRoot });
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
analyze() {
|
|
@@ -12,12 +12,10 @@
|
|
|
12
12
|
|
|
13
13
|
const { pushFinding } = require('../../ast-core');
|
|
14
14
|
const fs = require('fs');
|
|
15
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
16
15
|
|
|
17
16
|
class iOSSwiftUIAdvancedRules {
|
|
18
17
|
constructor(findings) {
|
|
19
18
|
this.findings = findings;
|
|
20
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
analyzeFile(filePath, ast) {
|
|
@@ -10,12 +10,10 @@ const { pushFinding } = require('../../ast-core');
|
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const glob = require('glob');
|
|
12
12
|
const path = require('path');
|
|
13
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
14
13
|
|
|
15
14
|
class iOSTestingAdvancedRules {
|
|
16
15
|
constructor(findings, projectRoot) {
|
|
17
16
|
this.findings = findings;
|
|
18
|
-
this.auditLogger = new AuditLogger({ repoRoot: projectRoot });
|
|
19
17
|
this.projectRoot = projectRoot;
|
|
20
18
|
}
|
|
21
19
|
|
|
@@ -3,7 +3,6 @@ const path = require('path');
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { pushFileFinding } = require('../ast-core');
|
|
5
5
|
const env = require(path.join(__dirname, '../../../config/env'));
|
|
6
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
7
6
|
|
|
8
7
|
function getStagedSwiftFiles(repoRoot) {
|
|
9
8
|
try {
|
|
@@ -5,7 +5,6 @@ const util = require('util');
|
|
|
5
5
|
const fs = require('fs').promises;
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const { DomainError } = require('../../../../domain/errors');
|
|
8
|
-
const AuditLogger = require('../../../../application/services/logging/AuditLogger');
|
|
9
8
|
|
|
10
9
|
const execPromise = util.promisify(exec);
|
|
11
10
|
|
|
@@ -21,7 +20,6 @@ class SourceKittenParser {
|
|
|
21
20
|
constructor() {
|
|
22
21
|
this.sourceKittenPath = '/opt/homebrew/bin/sourcekitten';
|
|
23
22
|
this.timeout = 30000;
|
|
24
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
/**
|
|
@@ -38,7 +38,10 @@ function safeGitRoot(startDir) {
|
|
|
38
38
|
});
|
|
39
39
|
const root = String(out || '').trim();
|
|
40
40
|
return root || null;
|
|
41
|
-
} catch {
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (process.env.DEBUG) {
|
|
43
|
+
process.stderr.write(`[MCP] safeGitRoot failed: ${error && error.message ? error.message : String(error)}\n`);
|
|
44
|
+
}
|
|
42
45
|
return null;
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -54,6 +57,16 @@ function resolveRepoRoot() {
|
|
|
54
57
|
|
|
55
58
|
const REPO_ROOT = resolveRepoRoot();
|
|
56
59
|
|
|
60
|
+
try {
|
|
61
|
+
if (REPO_ROOT && typeof REPO_ROOT === 'string' && fs.existsSync(REPO_ROOT)) {
|
|
62
|
+
process.chdir(REPO_ROOT);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (process.env.DEBUG) {
|
|
66
|
+
process.stderr.write(`[MCP] Failed to chdir to REPO_ROOT: ${error && error.message ? error.message : String(error)}\n`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
// NO singleton lock - Windsurf manages process lifecycle
|
|
58
71
|
// Each project gets its own independent MCP process
|
|
59
72
|
|
|
@@ -186,6 +199,34 @@ function resolveUpdateEvidenceScript() {
|
|
|
186
199
|
return null;
|
|
187
200
|
}
|
|
188
201
|
|
|
202
|
+
async function runWithTimeout(fn, timeoutMs) {
|
|
203
|
+
const ms = Number(timeoutMs);
|
|
204
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
205
|
+
try {
|
|
206
|
+
return { ok: true, timedOut: false, value: await fn() };
|
|
207
|
+
} catch (error) {
|
|
208
|
+
return { ok: false, timedOut: false, error };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let timer = null;
|
|
213
|
+
try {
|
|
214
|
+
const result = await Promise.race([
|
|
215
|
+
Promise.resolve().then(fn).then(value => ({ ok: true, timedOut: false, value })),
|
|
216
|
+
new Promise(resolve => {
|
|
217
|
+
timer = setTimeout(() => resolve({ ok: false, timedOut: true, error: new Error('timeout') }), ms);
|
|
218
|
+
})
|
|
219
|
+
]);
|
|
220
|
+
return result;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return { ok: false, timedOut: false, error };
|
|
223
|
+
} finally {
|
|
224
|
+
if (timer) {
|
|
225
|
+
clearTimeout(timer);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
189
230
|
// Lazy Loading Services
|
|
190
231
|
function getContextEngine() {
|
|
191
232
|
return getCompositionRoot().getContextDetectionEngine();
|
|
@@ -537,75 +578,123 @@ function checkBranchChangesCoherence(branchName, uncommittedChanges) {
|
|
|
537
578
|
* Returns BLOCKED or ALLOWED status with auto-fixes applied
|
|
538
579
|
*/
|
|
539
580
|
async function aiGateCheck() {
|
|
540
|
-
const
|
|
541
|
-
const
|
|
542
|
-
const
|
|
543
|
-
const
|
|
581
|
+
const startedAt = Date.now();
|
|
582
|
+
const gateTimeoutMs = Number(process.env.MCP_GATE_TIMEOUT_MS || 1200);
|
|
583
|
+
const strict = process.env.MCP_GATE_STRICT === 'true';
|
|
584
|
+
const allowEvidenceAutofix = process.env.MCP_GATE_AUTOFIX_EVIDENCE === 'true';
|
|
585
|
+
|
|
586
|
+
const core = async () => {
|
|
587
|
+
const gitFlowService = getCompositionRoot().getGitFlowService();
|
|
588
|
+
const gitQuery = getCompositionRoot().getGitQueryAdapter();
|
|
589
|
+
const currentBranch = getCurrentGitBranch(REPO_ROOT);
|
|
590
|
+
const isProtectedBranch = ['main', 'master', 'develop'].includes(currentBranch);
|
|
544
591
|
|
|
545
|
-
|
|
546
|
-
|
|
592
|
+
let uncommittedChanges = [];
|
|
593
|
+
try {
|
|
594
|
+
uncommittedChanges = gitQuery.getUncommittedChanges();
|
|
595
|
+
} catch (error) {
|
|
596
|
+
const msg = error && error.message ? error.message : String(error);
|
|
597
|
+
if (process.env.DEBUG) {
|
|
598
|
+
process.stderr.write(`[MCP] Gate gitQuery.getUncommittedChanges failed: ${msg}\n`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const hasUncommittedChanges = Array.isArray(uncommittedChanges) && uncommittedChanges.length > 0;
|
|
547
602
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
603
|
+
const violations = [];
|
|
604
|
+
const warnings = [];
|
|
605
|
+
const autoFixes = [];
|
|
606
|
+
|
|
607
|
+
const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
|
|
608
|
+
if (evidenceMonitor.isStale()) {
|
|
609
|
+
if (!allowEvidenceAutofix) {
|
|
610
|
+
violations.push('❌ EVIDENCE_STALE: Evidence is stale. Run ai-start to refresh.');
|
|
611
|
+
} else {
|
|
612
|
+
const elapsed = Date.now() - startedAt;
|
|
613
|
+
const remaining = Math.max(150, gateTimeoutMs - elapsed);
|
|
614
|
+
const refreshResult = await runWithTimeout(() => evidenceMonitor.refresh(), remaining);
|
|
615
|
+
if (refreshResult.ok) {
|
|
616
|
+
autoFixes.push('✅ Evidence was stale - AUTO-FIXED');
|
|
617
|
+
} else if (refreshResult.timedOut) {
|
|
618
|
+
violations.push('❌ EVIDENCE_STALE: Auto-fix timed out. Run ai-start to refresh.');
|
|
619
|
+
} else {
|
|
620
|
+
const msg = refreshResult.error && refreshResult.error.message ? refreshResult.error.message : String(refreshResult.error);
|
|
621
|
+
violations.push(`❌ EVIDENCE_STALE: Auto-fix failed: ${msg}`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
551
625
|
|
|
552
|
-
// 1. Evidence Freshness Check (Auto-fix included)
|
|
553
|
-
const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
|
|
554
|
-
if (evidenceMonitor.isStale()) {
|
|
555
626
|
try {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
627
|
+
const elapsed = Date.now() - startedAt;
|
|
628
|
+
const remaining = Math.max(120, gateTimeoutMs - elapsed);
|
|
629
|
+
const ghAvailable = await runWithTimeout(() => gitFlowService.isGitHubAvailable(), remaining);
|
|
630
|
+
if (!ghAvailable.ok || ghAvailable.timedOut || ghAvailable.value === false) {
|
|
631
|
+
warnings.push('⚠️ GitHub CLI not available - some automations may be limited');
|
|
632
|
+
}
|
|
633
|
+
} catch (error) {
|
|
634
|
+
warnings.push('⚠️ GitHub CLI not available - some automations may be limited');
|
|
560
635
|
}
|
|
561
|
-
}
|
|
562
636
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
637
|
+
if (isProtectedBranch) {
|
|
638
|
+
if (hasUncommittedChanges) {
|
|
639
|
+
violations.push(`❌ ON_PROTECTED_BRANCH: You are on '${currentBranch}' with uncommitted changes.`);
|
|
640
|
+
violations.push(` Required: create a feature branch first.`);
|
|
641
|
+
} else {
|
|
642
|
+
warnings.push(`⚠️ ON_PROTECTED_BRANCH: You are on '${currentBranch}'. Create a feature branch before making changes.`);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
567
645
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
646
|
+
if (strict) {
|
|
647
|
+
const elapsed = Date.now() - startedAt;
|
|
648
|
+
const remaining = Math.max(200, gateTimeoutMs - elapsed);
|
|
649
|
+
const blockCommitUseCase = getCompositionRoot().getBlockCommitUseCase();
|
|
650
|
+
const astAdapter = getCompositionRoot().getAstAdapter();
|
|
651
|
+
|
|
652
|
+
const astResult = await runWithTimeout(async () => {
|
|
653
|
+
const auditResult = await astAdapter.analyzeStagedFiles();
|
|
654
|
+
return blockCommitUseCase.execute(auditResult, { useStagedOnly: true });
|
|
655
|
+
}, remaining);
|
|
656
|
+
|
|
657
|
+
if (astResult.ok && astResult.value && astResult.value.shouldBlock) {
|
|
658
|
+
violations.push(`❌ AST_VIOLATIONS: ${astResult.value.reason}`);
|
|
659
|
+
} else if (!astResult.ok && process.env.DEBUG) {
|
|
660
|
+
const msg = astResult.error && astResult.error.message ? astResult.error.message : String(astResult.error);
|
|
661
|
+
process.stderr.write(`[MCP] Gate AST check skipped: ${astResult.timedOut ? 'timeout' : msg}\n`);
|
|
662
|
+
}
|
|
574
663
|
}
|
|
575
|
-
}
|
|
576
664
|
|
|
577
|
-
|
|
578
|
-
const blockCommitUseCase = getCompositionRoot().getBlockCommitUseCase();
|
|
579
|
-
const astAdapter = getCompositionRoot().getAstAdapter();
|
|
665
|
+
const isBlocked = violations.length > 0;
|
|
580
666
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
667
|
+
return {
|
|
668
|
+
status: isBlocked ? 'BLOCKED' : 'ALLOWED',
|
|
669
|
+
timestamp: new Date().toISOString(),
|
|
670
|
+
branch: currentBranch,
|
|
671
|
+
violations,
|
|
672
|
+
warnings,
|
|
673
|
+
autoFixes,
|
|
674
|
+
summary: isBlocked
|
|
675
|
+
? `🚫 BLOCKED: ${violations.length} violation(s). Fix before proceeding.`
|
|
676
|
+
: `🚦 ALLOWED: Gate passed.${warnings.length > 0 ? ` ${warnings.length} warning(s).` : ''}`,
|
|
677
|
+
instructions: isBlocked
|
|
678
|
+
? 'DO NOT proceed with user task. Announce violations and fix them first.'
|
|
679
|
+
: 'You may proceed with user task.'
|
|
680
|
+
};
|
|
681
|
+
};
|
|
586
682
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
} catch (err) {
|
|
591
|
-
if (process.env.DEBUG) console.error('[MCP] AST Check failed:', err.message);
|
|
683
|
+
const result = await runWithTimeout(core, gateTimeoutMs);
|
|
684
|
+
if (result.ok) {
|
|
685
|
+
return result.value;
|
|
592
686
|
}
|
|
593
687
|
|
|
594
|
-
const
|
|
595
|
-
|
|
688
|
+
const currentBranch = getCurrentGitBranch(REPO_ROOT);
|
|
596
689
|
return {
|
|
597
|
-
status:
|
|
690
|
+
status: 'BLOCKED',
|
|
598
691
|
timestamp: new Date().toISOString(),
|
|
599
692
|
branch: currentBranch,
|
|
600
|
-
violations,
|
|
601
|
-
warnings,
|
|
602
|
-
autoFixes,
|
|
603
|
-
summary:
|
|
604
|
-
|
|
605
|
-
: `🚦 ALLOWED: Gate passed.${warnings.length > 0 ? ` ${warnings.length} warning(s).` : ''}`,
|
|
606
|
-
instructions: isBlocked
|
|
607
|
-
? 'DO NOT proceed with user task. Announce violations and fix them first.'
|
|
608
|
-
: 'You may proceed with user task.'
|
|
693
|
+
violations: ['❌ GATE_TIMEOUT: AI gate check timed out. Retry or run ai-start manually.'],
|
|
694
|
+
warnings: [],
|
|
695
|
+
autoFixes: [],
|
|
696
|
+
summary: '🚫 BLOCKED: Gate check timed out.',
|
|
697
|
+
instructions: 'DO NOT proceed with user task. Retry the gate check.'
|
|
609
698
|
};
|
|
610
699
|
}
|
|
611
700
|
|
|
@@ -977,15 +1066,18 @@ protocolHandler.start(async (message) => {
|
|
|
977
1066
|
// Start polling loops ONLY after receiving 'initialized' notification from Windsurf
|
|
978
1067
|
if (!mcpInitialized && message.includes('"method":"initialized"')) {
|
|
979
1068
|
mcpInitialized = true;
|
|
980
|
-
process.
|
|
1069
|
+
if (process.env.DEBUG) {
|
|
1070
|
+
process.stderr.write(`[MCP] Received 'initialized' - starting background loops\n`);
|
|
1071
|
+
}
|
|
981
1072
|
startPollingLoops();
|
|
982
1073
|
}
|
|
983
1074
|
|
|
984
1075
|
return response;
|
|
985
1076
|
});
|
|
986
1077
|
|
|
987
|
-
|
|
988
|
-
process.stderr.write(`[MCP] Server ready for ${REPO_ROOT}\n`);
|
|
1078
|
+
if (process.env.DEBUG) {
|
|
1079
|
+
process.stderr.write(`[MCP] Server ready for ${REPO_ROOT}\n`);
|
|
1080
|
+
}
|
|
989
1081
|
|
|
990
1082
|
/**
|
|
991
1083
|
* Start polling loops for background notifications and automations
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
const MCP_VERSION = '2024-11-05';
|
|
2
2
|
const AuditLogger = require('../../../application/services/logging/AuditLogger');
|
|
3
|
+
const env = require('../../../config/env');
|
|
3
4
|
|
|
4
5
|
class McpProtocolHandler {
|
|
5
6
|
constructor(inputStream, outputStream, logger) {
|
|
6
7
|
this.inputStream = inputStream;
|
|
7
8
|
this.outputStream = outputStream;
|
|
8
9
|
this.logger = logger;
|
|
9
|
-
|
|
10
|
+
const repoRoot = (env.get('REPO_ROOT') || '').trim() || process.cwd();
|
|
11
|
+
this.auditLogger = new AuditLogger({ repoRoot, logger });
|
|
10
12
|
this.buffer = Buffer.alloc(0);
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
start(messageHandler) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
if (process.env.DEBUG) {
|
|
17
|
+
process.stderr.write('[MCP] Protocol handler starting...\n');
|
|
18
|
+
}
|
|
16
19
|
|
|
17
20
|
this.inputStream.on('data', (chunk) => {
|
|
18
21
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), 'utf8');
|
|
@@ -29,8 +32,9 @@ class McpProtocolHandler {
|
|
|
29
32
|
process.exit(1);
|
|
30
33
|
});
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
if (process.env.DEBUG) {
|
|
36
|
+
process.stderr.write('[MCP] Protocol handler ready\n');
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
async _handleChunk(chunk, messageHandler) {
|
|
@@ -115,8 +119,12 @@ class McpProtocolHandler {
|
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
const lineBuf = this.buffer.slice(0, nl);
|
|
118
|
-
this.buffer = this.buffer.slice(nl + 1);
|
|
119
122
|
const line = lineBuf.toString('utf8').trim();
|
|
123
|
+
if (/^content-length:/i.test(line)) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.buffer = this.buffer.slice(nl + 1);
|
|
120
128
|
if (line) {
|
|
121
129
|
messages.push({ body: line, framed: false });
|
|
122
130
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const env = require('../../config/env');
|
|
2
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
3
2
|
|
|
4
3
|
const path = require('path');
|
|
5
4
|
const ICursorTokenRepository = require('../../domain/repositories/ICursorTokenRepository');
|
|
@@ -20,7 +19,6 @@ class CursorTokenRepository extends ICursorTokenRepository {
|
|
|
20
19
|
this.apiDataSource = new CursorApiDataSource({ apiUrl, apiToken, fetchImpl, logger });
|
|
21
20
|
this.fileDataSource = new CursorFileDataSource({ usageFile, logger });
|
|
22
21
|
this.logger = logger;
|
|
23
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd(), logger });
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
/**
|
|
@@ -4,7 +4,6 @@ const path = require('path');
|
|
|
4
4
|
const IFindingsRepository = require('../../domain/repositories/IFindingsRepository');
|
|
5
5
|
const Finding = require('../../domain/entities/Finding');
|
|
6
6
|
const AuditResult = require('../../domain/entities/AuditResult');
|
|
7
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
8
7
|
|
|
9
8
|
class FileFindingsRepository extends IFindingsRepository {
|
|
10
9
|
constructor(basePath = '.audit_tmp') {
|
|
@@ -12,7 +11,6 @@ class FileFindingsRepository extends IFindingsRepository {
|
|
|
12
11
|
this.basePath = basePath;
|
|
13
12
|
this.findingsFile = path.join(basePath, 'findings.json');
|
|
14
13
|
this.auditResultFile = path.join(basePath, 'audit-result.json');
|
|
15
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
async ensureDirectory() {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
const env = require('
|
|
2
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
1
|
+
const env = require('../../../config/env.js');
|
|
3
2
|
|
|
4
3
|
const { DomainError } = require('../../../domain/errors');
|
|
5
4
|
|
|
@@ -14,7 +13,6 @@ class CursorApiDataSource {
|
|
|
14
13
|
this.apiToken = apiToken;
|
|
15
14
|
this.fetch = fetchImpl;
|
|
16
15
|
this.logger = logger;
|
|
17
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd(), logger });
|
|
18
16
|
this.failureCount = 0;
|
|
19
17
|
this.failureThreshold = 5;
|
|
20
18
|
this.circuitOpenUntil = null;
|
package/scripts/hooks-system/infrastructure/repositories/datasources/CursorFileDataSource.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
3
2
|
|
|
4
3
|
class CursorFileDataSource {
|
|
5
4
|
constructor({
|
|
@@ -10,7 +9,6 @@ class CursorFileDataSource {
|
|
|
10
9
|
this.usageFile = usageFile;
|
|
11
10
|
this.fs = fsModule;
|
|
12
11
|
this.logger = logger;
|
|
13
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd(), logger });
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
async readUsage() {
|
|
@@ -8,7 +8,6 @@ const { ContextBuilder } = require('./context/context-builder');
|
|
|
8
8
|
const RecommendationGenerator = require('./generators/RecommendationGenerator');
|
|
9
9
|
const ContextMultiplier = require('./scorers/ContextMultiplier');
|
|
10
10
|
const SeverityMapper = require('./mappers/SeverityMapper');
|
|
11
|
-
const AuditLogger = require('../../application/services/logging/AuditLogger');
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Main severity evaluator
|
|
@@ -23,7 +22,6 @@ class SeverityEvaluator {
|
|
|
23
22
|
this.contextBuilder = new ContextBuilder();
|
|
24
23
|
this.recommendationGenerator = new RecommendationGenerator();
|
|
25
24
|
this.contextMultiplier = new ContextMultiplier();
|
|
26
|
-
this.auditLogger = new AuditLogger({ repoRoot: process.cwd() });
|
|
27
25
|
|
|
28
26
|
this.weights = {
|
|
29
27
|
security: 0.40,
|