pumuki-ast-hooks 6.0.0 → 6.0.2

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/README.md CHANGED
@@ -27,19 +27,19 @@ npx ast-hooks
27
27
 
28
28
  ### Pre‑write enforcement (block before writing)
29
29
 
30
- ![Pre-write hook output](./assets/Hook_02.png)
30
+ ![Pre-write hook output](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/Hook_02.png)
31
31
 
32
32
  ### AI Gate (blocked example)
33
33
 
34
- ![AI Gate blocked example](./assets/ai_gate.png)
34
+ ![AI Gate blocked example](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ai_gate.png)
35
35
 
36
36
  ### ai-start (bootstrap + evidence refresh)
37
37
 
38
- ![ai-start output](./assets/ai-start.png)
38
+ ![ai-start output](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ai-start.png)
39
39
 
40
40
  ### Pre-flight check (in-memory validation)
41
41
 
42
- ![pre-flight-check output](./assets/pre-flight-check.png)
42
+ ![pre-flight-check output](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/pre-flight-check.png)
43
43
 
44
44
  ### Interactive menu (orchestrator overview)
45
45
 
@@ -58,15 +58,15 @@ Documentation:
58
58
 
59
59
  ## Visual Overview
60
60
 
61
- ![AST Intelligence System Overview](./assets/ast_intelligence_01.svg)
61
+ ![AST Intelligence System Overview](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_01.svg)
62
62
 
63
- ![AST Intelligence Workflow](./assets/ast_intelligence_02.svg)
63
+ ![AST Intelligence Workflow](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_02.svg)
64
64
 
65
- ![AST Intelligence Audit - Part 1](./assets/ast_intelligence_03.svg)
65
+ ![AST Intelligence Audit - Part 1](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_03.svg)
66
66
 
67
- ![AST Intelligence Audit - Part 2](./assets/ast_intelligence_04.svg)
67
+ ![AST Intelligence Audit - Part 2](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_04.svg)
68
68
 
69
- ![AST Intelligence Audit - Part 3](./assets/ast_intelligence_05.svg)
69
+ ![AST Intelligence Audit - Part 3](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_05.svg)
70
70
 
71
71
  ---
72
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "6.0.0",
3
+ "version": "6.0.2",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -14,3 +14,5 @@
14
14
  {"timestamp":"2026-01-11T21:44:57.015Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
15
15
  {"timestamp":"2026-01-12T08:29:42.539Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
16
16
  {"timestamp":"2026-01-12T08:44:03.829Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
17
+ {"timestamp":"2026-01-12T18:40:40.786Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
18
+ {"timestamp":"2026-01-12T19:29:17.037Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
@@ -66,3 +66,11 @@
66
66
  {"timestamp":"2026-01-12T08:44:03.895Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
67
67
  {"timestamp":"2026-01-12T08:44:03.895Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
68
68
  {"timestamp":"2026-01-12T08:44:03.895Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
69
+ {"timestamp":"2026-01-12T18:40:40.839Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
70
+ {"timestamp":"2026-01-12T18:40:40.845Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
71
+ {"timestamp":"2026-01-12T18:40:40.846Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
72
+ {"timestamp":"2026-01-12T18:40:40.846Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
73
+ {"timestamp":"2026-01-12T19:29:17.091Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
74
+ {"timestamp":"2026-01-12T19:29:17.097Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
75
+ {"timestamp":"2026-01-12T19:29:17.098Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
76
+ {"timestamp":"2026-01-12T19:29:17.098Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
@@ -626,3 +626,59 @@
626
626
  {"timestamp":1768207443829,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
627
627
  {"timestamp":1768207443829,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
628
628
  {"timestamp":1768207443829,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
629
+ {"timestamp":1768243240785,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
630
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
631
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
632
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
633
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
634
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
635
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
636
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
637
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
638
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
639
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
640
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
641
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
642
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
643
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
644
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
645
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
646
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
647
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
648
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
649
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
650
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
651
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
652
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
653
+ {"timestamp":1768246157036,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
654
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
655
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
656
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
657
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
658
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
659
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
660
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
661
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
662
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
663
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
664
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
665
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
666
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
667
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
668
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
669
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
670
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
671
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
672
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
673
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
674
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
675
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
676
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
677
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
678
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
679
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
680
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
681
+ {"timestamp":1768247690240,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
682
+ {"timestamp":1768247690240,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
683
+ {"timestamp":1768247690240,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
684
+ {"timestamp":1768247690241,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
@@ -5,7 +5,7 @@
5
5
  "platforms": [
6
6
  "backend"
7
7
  ],
8
- "created": "2026-01-06T11:37:47.558Z"
8
+ "created": "2026-01-12T08:54:59.529Z"
9
9
  },
10
10
  "architecture": {
11
11
  "pattern": "FEATURE_FIRST_CLEAN_DDD",
@@ -176,5 +176,17 @@ describe('network-resilience-analyzer', () => {
176
176
 
177
177
  expect(findings.filter(f => f.ruleId === 'common.network.missing_connection_pooling')).toHaveLength(0);
178
178
  });
179
+
180
+ it('should ignore non-js sources', () => {
181
+ const project = createMockProject([{
182
+ path: '/app/services/KeychainHelper.swift',
183
+ content: 'struct KeychainHelper { }'
184
+ }]);
185
+ const findings = [];
186
+
187
+ analyzeNetworkResilience(project, findings);
188
+
189
+ expect(findings.filter(f => f.ruleId === 'common.network.missing_connection_pooling')).toHaveLength(0);
190
+ });
179
191
  });
180
192
  });
@@ -149,7 +149,8 @@ function checkConnectionPooling(sf, content, findings) {
149
149
  const filePath = sf.getFilePath();
150
150
  const isAnalyzer = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(filePath);
151
151
  const isTestFile = /\.(spec|test)\.(js|ts)$/i.test(filePath);
152
- if (isAnalyzer || isTestFile) {
152
+ const isSupportedSource = /\.(jsx?|tsx?)$/i.test(filePath);
153
+ if (isAnalyzer || isTestFile || !isSupportedSource) {
153
154
  return;
154
155
  }
155
156
 
@@ -31,4 +31,18 @@ describe('ios.imports.unused', () => {
31
31
 
32
32
  expect(analyzer.findings.find(f => f.ruleId === 'ios.imports.unused')).toBeUndefined();
33
33
  });
34
+
35
+ it('does not report unused Foundation when Foundation types are used in file content', () => {
36
+ const filePath = '/tmp/Bar.swift';
37
+ const analyzer = makeAnalyzer({
38
+ fileContent: 'import Foundation\n\nstruct Bar { let createdAt: Date }',
39
+ allNodes: []
40
+ });
41
+
42
+ analyzer.imports = [{ name: 'Foundation', line: 1 }];
43
+
44
+ analyzeImportsAST(analyzer, filePath);
45
+
46
+ expect(analyzer.findings.find(f => f.ruleId === 'ios.imports.unused')).toBeUndefined();
47
+ });
34
48
  });
@@ -87,6 +87,7 @@ function analyzeImportsAST(analyzer, filePath) {
87
87
  if (filePath.includes('Tests')) {
88
88
  return;
89
89
  }
90
+ const content = analyzer.fileContent || '';
90
91
  const importNames = analyzer.imports.map((i) => i.name);
91
92
 
92
93
  const hasUIKit = importNames.includes('UIKit');
@@ -117,14 +118,18 @@ function analyzeImportsAST(analyzer, filePath) {
117
118
 
118
119
  const unusedImportAllowlist = new Set(['Foundation', 'SwiftUI', 'UIKit', 'Combine']);
119
120
 
121
+ const foundationTypeUsage = /\b(Data|Date|URL|UUID|Decimal|NSNumber|NSDecimalNumber|NSSet|NSDictionary|NSArray|IndexPath|Notification|FileManager|Bundle|Locale|TimeZone|Calendar|DateComponents|URLRequest|URLSession)\b/;
120
122
  for (const imp of analyzer.imports) {
121
123
  if (!unusedImportAllowlist.has(imp.name)) continue;
122
124
 
123
- const isUsed = analyzer.allNodes.some((n) => {
125
+ let isUsed = analyzer.allNodes.some((n) => {
124
126
  const typename = n['key.typename'] || '';
125
127
  const name = n['key.name'] || '';
126
128
  return typename.includes(imp.name) || name.includes(imp.name);
127
129
  });
130
+ if (!isUsed && imp.name === 'Foundation') {
131
+ isUsed = foundationTypeUsage.test(content);
132
+ }
128
133
 
129
134
  if (!isUsed) {
130
135
  analyzer.pushFinding('ios.imports.unused', 'low', filePath, imp.line, `Unused import: ${imp.name}`);
@@ -1,4 +1,7 @@
1
1
  const { runTextScanner } = require('../text-scanner');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
2
5
 
3
6
  describe('Text Scanner Module', () => {
4
7
  describe('runTextScanner', () => {
@@ -17,4 +20,50 @@ describe('Text Scanner Module', () => {
17
20
  expect(mod.runTextScanner).toBeDefined();
18
21
  });
19
22
  });
23
+
24
+ describe('ios rules', () => {
25
+ it('does not flag missing DI when Package.swift has empty dependencies', () => {
26
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-text-'));
27
+ const packagePath = path.join(root, 'Package.swift');
28
+ const content = [
29
+ '// swift-tools-version: 5.9',
30
+ 'import PackageDescription',
31
+ '',
32
+ 'let package = Package(',
33
+ ' name: "Demo",',
34
+ ' dependencies: [],',
35
+ ' targets: [',
36
+ ' .target(name: "Demo")',
37
+ ' ]',
38
+ ')'
39
+ ].join('\n');
40
+
41
+ fs.writeFileSync(packagePath, content, 'utf8');
42
+
43
+ const findings = [];
44
+ runTextScanner(root, findings);
45
+
46
+ const diFinding = findings.find(f => f.ruleId === 'ios.spm.dependency_injection');
47
+ expect(diFinding).toBeUndefined();
48
+ });
49
+
50
+ it('does not flag Any usage when Security context is present', () => {
51
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-text-'));
52
+ const filePath = path.join(root, 'KeychainHelper.swift');
53
+ const content = [
54
+ 'import Security',
55
+ 'struct KeychainHelper {',
56
+ ' func baseQuery() -> [String: Any] { [:] }',
57
+ '}'
58
+ ].join('\n');
59
+
60
+ fs.writeFileSync(filePath, content, 'utf8');
61
+
62
+ const findings = [];
63
+ runTextScanner(root, findings);
64
+
65
+ const typeSafety = findings.find(f => f.ruleId === 'ios.optionals.type_safety');
66
+ expect(typeSafety).toBeUndefined();
67
+ });
68
+ });
20
69
  });
@@ -561,7 +561,8 @@ function runTextScanner(root, findings) {
561
561
  if (targetCount < 2) {
562
562
  pushFileFinding('ios.spm.modular_architecture', 'low', file, 1, 1, 'Package.swift without multiple targets (weak modular architecture)', findings);
563
563
  }
564
- if (!/Swinject|Needle|Resolver|Factory\b/.test(content)) {
564
+ const hasEmptyDependencies = /dependencies\s*:\s*\[\s*\]/.test(content);
565
+ if (!hasEmptyDependencies && !/Swinject|Needle|Resolver|Factory\b/.test(content)) {
565
566
  pushFileFinding('ios.spm.dependency_injection', 'low', file, 1, 1, 'No DI library hint found in Package.swift', findings);
566
567
  }
567
568
  }
@@ -601,7 +602,10 @@ function runTextScanner(root, findings) {
601
602
  pushFileFinding('ios.optionals.optional_binding', 'medium', file, 1, 1, 'Optional check != nil (prefer optional binding)', findings);
602
603
  }
603
604
  if (/:\s*Any\b|\bas\s*Any\b/.test(content)) {
604
- pushFileFinding('ios.optionals.type_safety', 'medium', file, 1, 1, 'Usage of Any reduces type safety', findings);
605
+ const hasSecurityContext = /import\s+Security\b/.test(content) || /\bkSec[A-Za-z0-9_]+\b/.test(content);
606
+ if (!hasSecurityContext) {
607
+ pushFileFinding('ios.optionals.type_safety', 'medium', file, 1, 1, 'Usage of Any reduces type safety', findings);
608
+ }
605
609
  }
606
610
  if (/\.sink\s*\(/.test(content) && !/receiveCompletion\s*:\s*/.test(content)) {
607
611
  pushFileFinding('ios.combine.error_handling', 'medium', file, 1, 1, 'Combine sink without receiveCompletion handler', findings);
@@ -43,3 +43,9 @@
43
43
  {"timestamp":"2026-01-12T08:44:05.598Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
44
44
  {"timestamp":"2026-01-12T08:44:05.599Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
45
45
  {"timestamp":"2026-01-12T08:44:05.600Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
46
+ {"timestamp":"2026-01-12T18:40:42.667Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
47
+ {"timestamp":"2026-01-12T18:40:42.668Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
48
+ {"timestamp":"2026-01-12T18:40:42.669Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
49
+ {"timestamp":"2026-01-12T19:29:18.617Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
50
+ {"timestamp":"2026-01-12T19:29:18.618Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
51
+ {"timestamp":"2026-01-12T19:29:18.618Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}