pumuki-ast-hooks 5.5.47 → 5.5.49
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/docs/CODE_STANDARDS.md +5 -0
- package/docs/VIOLATIONS_RESOLUTION_PLAN.md +5 -140
- package/package.json +1 -1
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +96 -0
- package/scripts/hooks-system/application/services/installation/VSCodeTaskConfigurator.js +3 -1
- package/scripts/hooks-system/bin/gitflow-cycle.js +0 -0
- package/scripts/hooks-system/config/project.config.json +1 -1
- package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidSOLIDAnalyzer.js +11 -255
- package/scripts/hooks-system/infrastructure/ast/android/detectors/android-solid-detectors.js +227 -0
- package/scripts/hooks-system/infrastructure/ast/ast-core.js +12 -3
- package/scripts/hooks-system/infrastructure/ast/ast-intelligence.js +36 -13
- package/scripts/hooks-system/infrastructure/ast/backend/ast-backend.js +10 -83
- package/scripts/hooks-system/infrastructure/ast/backend/detectors/god-class-detector.js +83 -0
- package/scripts/hooks-system/infrastructure/ast/common/ast-common.js +17 -2
- package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendArchitectureDetector.js +12 -142
- package/scripts/hooks-system/infrastructure/ast/frontend/detectors/frontend-architecture-strategies.js +126 -0
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSASTIntelligentAnalyzer.js +30 -783
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureDetector.js +21 -224
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureRules.js +18 -605
- package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSModernPracticesRules.js +4 -1
- package/scripts/hooks-system/infrastructure/ast/ios/ast-ios.js +4 -1
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-architecture-rules-strategies.js +595 -0
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-architecture-strategies.js +192 -0
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-ast-intelligent-strategies.js +789 -0
- package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-god-class-detector.js +79 -0
- package/scripts/hooks-system/infrastructure/ast/ios/native-bridge.js +4 -1
- package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +24 -13
- package/skills/android-guidelines/SKILL.md +1 -0
- package/skills/backend-guidelines/SKILL.md +1 -0
- package/skills/frontend-guidelines/SKILL.md +1 -0
- package/skills/ios-guidelines/SKILL.md +1 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const { SyntaxKind } = require('../ast-core');
|
|
2
|
+
|
|
3
|
+
function analyzeGodClasses(sourceFile, findings, pushFinding, godClassBaseline) {
|
|
4
|
+
if (!godClassBaseline) return;
|
|
5
|
+
|
|
6
|
+
sourceFile.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
|
|
7
|
+
const className = cls.getName() || '';
|
|
8
|
+
const isValueObject = /DTO$|ViewModel$|State$|Props$|Entity$/.test(className);
|
|
9
|
+
const isTestClass = /Spec$|Test$|Mock/.test(className);
|
|
10
|
+
if (isValueObject || isTestClass) return;
|
|
11
|
+
|
|
12
|
+
const methodsCount = cls.getMethods().length;
|
|
13
|
+
const propertiesCount = cls.getProperties().length;
|
|
14
|
+
const startLine = cls.getStartLineNumber();
|
|
15
|
+
const endLine = cls.getEndLineNumber();
|
|
16
|
+
const lineCount = Math.max(0, endLine - startLine);
|
|
17
|
+
|
|
18
|
+
const decisionKinds = [
|
|
19
|
+
SyntaxKind.IfStatement,
|
|
20
|
+
SyntaxKind.ForStatement,
|
|
21
|
+
SyntaxKind.ForInStatement,
|
|
22
|
+
SyntaxKind.ForOfStatement,
|
|
23
|
+
SyntaxKind.WhileStatement,
|
|
24
|
+
SyntaxKind.DoStatement,
|
|
25
|
+
SyntaxKind.SwitchStatement,
|
|
26
|
+
SyntaxKind.ConditionalExpression,
|
|
27
|
+
SyntaxKind.TryStatement,
|
|
28
|
+
SyntaxKind.CatchClause
|
|
29
|
+
];
|
|
30
|
+
const complexity = decisionKinds.reduce((acc, kind) => acc + cls.getDescendantsOfKind(kind).length, 0);
|
|
31
|
+
|
|
32
|
+
const clsText = cls.getFullText();
|
|
33
|
+
const concerns = new Set();
|
|
34
|
+
if (/URLSession|URLRequest|Alamofire|HTTP/i.test(clsText)) concerns.add('network');
|
|
35
|
+
if (/CoreData|Realm|SQLite|Persistence/i.test(clsText)) concerns.add('persistence');
|
|
36
|
+
if (/DispatchQueue|asyncAfter|Timer/i.test(clsText)) concerns.add('async');
|
|
37
|
+
if (/CryptoKit|Keychain|JWT|token|bearer/i.test(clsText)) concerns.add('security');
|
|
38
|
+
if (/NotificationCenter|push|local notification/i.test(clsText)) concerns.add('notifications');
|
|
39
|
+
if (/UIKit|SwiftUI|View/i.test(clsText)) concerns.add('ui');
|
|
40
|
+
if (/Logger|print\(/i.test(clsText)) concerns.add('logging');
|
|
41
|
+
const concernCount = concerns.size;
|
|
42
|
+
|
|
43
|
+
const methodsZ = godClassBaseline.robustZ(methodsCount, godClassBaseline.med.methodsCount, godClassBaseline.mad.methodsCount);
|
|
44
|
+
const propsZ = godClassBaseline.robustZ(propertiesCount, godClassBaseline.med.propertiesCount, godClassBaseline.mad.propertiesCount);
|
|
45
|
+
const linesZ = godClassBaseline.robustZ(lineCount, godClassBaseline.med.lineCount, godClassBaseline.mad.lineCount);
|
|
46
|
+
const complexityZ = godClassBaseline.robustZ(complexity, godClassBaseline.med.complexity, godClassBaseline.mad.complexity);
|
|
47
|
+
|
|
48
|
+
const sizeOutlier =
|
|
49
|
+
methodsZ >= godClassBaseline.thresholds.outlier.methodsCountZ ||
|
|
50
|
+
propsZ >= godClassBaseline.thresholds.outlier.propertiesCountZ ||
|
|
51
|
+
linesZ >= godClassBaseline.thresholds.outlier.lineCountZ;
|
|
52
|
+
const complexityOutlier = complexityZ >= godClassBaseline.thresholds.outlier.complexityZ;
|
|
53
|
+
const concernOutlier = concernCount >= godClassBaseline.thresholds.outlier.concerns;
|
|
54
|
+
|
|
55
|
+
const isMassiveFile = lineCount > 500;
|
|
56
|
+
const isAbsoluteGod = lineCount > 900 ||
|
|
57
|
+
(lineCount > 450 && complexity > 50) ||
|
|
58
|
+
(lineCount > 450 && methodsCount > 20) ||
|
|
59
|
+
(lineCount > 550 && methodsCount > 25 && complexity > 70);
|
|
60
|
+
const isUnderThreshold = lineCount < 280 && methodsCount < 15 && complexity < 28;
|
|
61
|
+
|
|
62
|
+
let signalCount = 0;
|
|
63
|
+
if (sizeOutlier) signalCount++;
|
|
64
|
+
if (complexityOutlier) signalCount++;
|
|
65
|
+
if (concernOutlier) signalCount++;
|
|
66
|
+
if (isMassiveFile) signalCount++;
|
|
67
|
+
|
|
68
|
+
if (!isUnderThreshold && (signalCount >= 2 || isAbsoluteGod)) {
|
|
69
|
+
pushFinding("backend.antipattern.god_classes", "critical", sourceFile, cls,
|
|
70
|
+
`God class detected: ${methodsCount} methods, ${propertiesCount} properties, ${lineCount} lines, complexity ${complexity}, concerns ${concernCount} - VIOLATES SRP`,
|
|
71
|
+
findings
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
analyzeGodClasses,
|
|
79
|
+
};
|
|
@@ -16,7 +16,10 @@ function getStagedSwiftFiles(repoRoot) {
|
|
|
16
16
|
.map(s => s.trim())
|
|
17
17
|
.filter(Boolean)
|
|
18
18
|
.filter(p => p.endsWith('.swift'));
|
|
19
|
-
} catch {
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (process.env.DEBUG) {
|
|
21
|
+
console.debug(`[iOS NativeBridge] Failed to read staged swift files: ${error.message}`);
|
|
22
|
+
}
|
|
20
23
|
return [];
|
|
21
24
|
}
|
|
22
25
|
}
|
|
@@ -235,6 +235,28 @@ function toRepoRelativePath(filePath) {
|
|
|
235
235
|
return normalized;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
function isAuditTmpPath(repoRelativePath) {
|
|
239
|
+
const normalized = normalizePathForMatch(repoRelativePath);
|
|
240
|
+
return normalized.startsWith('.audit_tmp/') || normalized.includes('/.audit_tmp/');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function isViolationInStagedFiles(violationPath, stagedSet) {
|
|
244
|
+
if (!violationPath) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const repoRelative = toRepoRelativePath(violationPath);
|
|
249
|
+
if (!repoRelative) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (isAuditTmpPath(repoRelative)) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return stagedSet.has(repoRelative);
|
|
258
|
+
}
|
|
259
|
+
|
|
238
260
|
function resolveAuditTmpDir() {
|
|
239
261
|
const configured = (env.get('AUDIT_TMP', '') || '').trim();
|
|
240
262
|
if (configured.length > 0) {
|
|
@@ -273,18 +295,7 @@ async function runIntelligentAudit() {
|
|
|
273
295
|
|
|
274
296
|
const stagedViolations = rawViolations.filter(v => {
|
|
275
297
|
const violationPath = toRepoRelativePath(v.filePath || v.file || '');
|
|
276
|
-
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
if (stagedSet.has(violationPath)) {
|
|
280
|
-
return true;
|
|
281
|
-
}
|
|
282
|
-
for (const sf of stagedSet) {
|
|
283
|
-
if (sf && (violationPath === sf || violationPath.endsWith('/' + sf) || violationPath.includes('/' + sf))) {
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return false;
|
|
298
|
+
return isViolationInStagedFiles(violationPath, stagedSet);
|
|
288
299
|
});
|
|
289
300
|
|
|
290
301
|
console.log(`[Intelligent Audit] Gate scope: STAGING (${stagedFiles.length} files)`);
|
|
@@ -683,4 +694,4 @@ if (require.main === module) {
|
|
|
683
694
|
});
|
|
684
695
|
}
|
|
685
696
|
|
|
686
|
-
module.exports = { runIntelligentAudit };
|
|
697
|
+
module.exports = { runIntelligentAudit, isViolationInStagedFiles, toRepoRelativePath };
|
|
@@ -34,6 +34,7 @@ Automatically activates when working on:
|
|
|
34
34
|
- [ ] **ViewModel**: Presentation/ui/{feature}/ViewModel
|
|
35
35
|
- [ ] **Tests**: Unit + integration tests
|
|
36
36
|
- [ ] **Error Handling**: Sealed class Result<T>
|
|
37
|
+
- [ ] **No empty catch**: Prohibido silenciar errores en tooling (AST: common.error.empty_catch)
|
|
37
38
|
|
|
38
39
|
### New Module Checklist
|
|
39
40
|
|
|
@@ -33,6 +33,7 @@ Automatically activates when working on:
|
|
|
33
33
|
- [ ] **Entity**: Domain model in domain/entities/
|
|
34
34
|
- [ ] **Tests**: Unit + integration tests
|
|
35
35
|
- [ ] **Error Handling**: Custom exceptions
|
|
36
|
+
- [ ] **No empty catch**: Prohibido `catch` vacío (AST: common.error.empty_catch)
|
|
36
37
|
|
|
37
38
|
### New Module Checklist
|
|
38
39
|
|
|
@@ -36,6 +36,7 @@ Creating a component? Follow this checklist:
|
|
|
36
36
|
- [ ] **i18n** - Use translation hooks, no hardcoded strings
|
|
37
37
|
- [ ] **Performance** - useMemo, useCallback when appropriate
|
|
38
38
|
- [ ] **Testing** - React Testing Library tests
|
|
39
|
+
- [ ] **No empty catch** - Prohibido silenciar errores (AST: common.error.empty_catch)
|
|
39
40
|
|
|
40
41
|
### New Feature Checklist
|
|
41
42
|
|
|
@@ -34,6 +34,7 @@ Automatically activates when working on:
|
|
|
34
34
|
- [ ] **ViewModel**: Application/ViewModels/ (if needed)
|
|
35
35
|
- [ ] **Tests**: Unit + integration tests
|
|
36
36
|
- [ ] **Error Handling**: Custom Error enum
|
|
37
|
+
- [ ] **No empty catch**: Prohibido `catch` vacío (AST: common.error.empty_catch)
|
|
37
38
|
|
|
38
39
|
### New Module Checklist
|
|
39
40
|
|