pumuki-ast-hooks 6.2.2 → 6.2.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "6.2.2",
3
+ "version": "6.2.4",
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": {
@@ -136,4 +136,4 @@
136
136
  "./skills": "./skills/skill-rules.json",
137
137
  "./hooks": "./hooks/index.js"
138
138
  }
139
- }
139
+ }
@@ -1506,3 +1506,7 @@
1506
1506
  {"timestamp":1769006619004,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
1507
1507
  {"timestamp":1769006619004,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
1508
1508
  {"timestamp":1769006619004,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
1509
+ {"timestamp":1769296106437,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
1510
+ {"timestamp":1769296106437,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
1511
+ {"timestamp":1769296106437,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
1512
+ {"timestamp":1769296106437,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
@@ -1,6 +1,5 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
- const { execSync } = require('child_process');
4
3
  const { pushFinding, SyntaxKind, platformOf, getRepoRoot } = require(path.join(__dirname, '../ast-core'));
5
4
  const { BDDTDDWorkflowRules } = require(path.join(__dirname, 'BDDTDDWorkflowRules'));
6
5
 
@@ -72,56 +71,6 @@ function hasTrackForMemoryLeaksEvidence(content) {
72
71
  return hasTrackForMemoryLeaks(content) || hasMakeSUT(content);
73
72
  }
74
73
 
75
- function loadTestFileContent(filePath) {
76
- try {
77
- const diskContent = fs.readFileSync(filePath, 'utf8');
78
- if (diskContent && diskContent.trim().length > 0) {
79
- return diskContent;
80
- }
81
- } catch (error) {
82
- if (process.env.DEBUG) {
83
- console.debug(`[ast-common] Failed to read test file content for ${filePath}: ${error.message}`);
84
- }
85
- }
86
- return null;
87
- }
88
-
89
- function resolveTestFileContent(filePath, currentContent) {
90
- const diskContent = loadTestFileContent(filePath);
91
- if (diskContent) {
92
- return diskContent;
93
- }
94
-
95
- const repoRoot = getRepoRoot();
96
- const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(repoRoot, filePath);
97
- if (resolvedPath !== filePath) {
98
- const resolvedContent = loadTestFileContent(resolvedPath);
99
- if (resolvedContent) {
100
- return resolvedContent;
101
- }
102
- }
103
-
104
- try {
105
- const relPath = path.relative(repoRoot, resolvedPath).replace(/\\/g, '/');
106
- if (relPath && !relPath.startsWith('..')) {
107
- const stagedContent = execSync(`git show :${relPath}`, {
108
- encoding: 'utf8',
109
- cwd: repoRoot,
110
- stdio: ['ignore', 'pipe', 'ignore']
111
- });
112
- if (stagedContent && stagedContent.trim().length > 0) {
113
- return stagedContent;
114
- }
115
- }
116
- } catch (error) {
117
- if (process.env.DEBUG) {
118
- console.debug(`[ast-common] Failed to read staged content for ${filePath}: ${error.message}`);
119
- }
120
- }
121
-
122
- return currentContent;
123
- }
124
-
125
74
  /**
126
75
  * Detect if a test file is a simple value type test that doesn't need makeSUT/trackForMemoryLeaks.
127
76
  * Simple tests typically:
@@ -326,7 +275,16 @@ function runCommonIntelligence(project, findings) {
326
275
  const isSwiftOrKotlinTest = ext === '.swift' || ext === '.kt' || ext === '.kts';
327
276
 
328
277
  if (isSwiftOrKotlinTest) {
329
- content = resolveTestFileContent(filePath, content);
278
+ try {
279
+ const diskContent = fs.readFileSync(filePath, 'utf8');
280
+ if (diskContent && diskContent.trim().length > 0) {
281
+ content = diskContent;
282
+ }
283
+ } catch (error) {
284
+ if (process.env.DEBUG) {
285
+ console.debug(`[ast-common] Failed to read test file content for ${filePath}: ${error.message}`);
286
+ }
287
+ }
330
288
  }
331
289
 
332
290
  if (isSwiftOrKotlinTest) {
@@ -76,7 +76,7 @@ class WorkflowRules {
76
76
  const featureName = this.extractFeatureName(content);
77
77
 
78
78
  if (featureName) {
79
- let testFiles = glob.sync(`**/*${featureName}*.{test,spec}.{ts,tsx,swift,kt}`, {
79
+ const testFiles = glob.sync(`**/*${featureName}*.{test,spec}.{ts,tsx,swift,kt}`, {
80
80
  cwd: this.projectRoot,
81
81
  absolute: true,
82
82
  nocase: true
@@ -89,17 +89,6 @@ class WorkflowRules {
89
89
  nocase: true
90
90
  });
91
91
 
92
- if (testFiles.length === 0) {
93
- const tokens = this.splitFeatureName(featureName);
94
- if (tokens.length > 1) {
95
- testFiles = tokens.flatMap(token => glob.sync(`**/*${token}*.{test,spec}.{ts,tsx,swift,kt}`, {
96
- cwd: this.projectRoot,
97
- absolute: true,
98
- nocase: true
99
- }));
100
- }
101
- }
102
-
103
92
  if (testFiles.length === 0) {
104
93
  pushFileFinding(
105
94
  'workflow.triad.feature_without_tests',
@@ -139,15 +128,6 @@ class WorkflowRules {
139
128
  });
140
129
  }
141
130
 
142
- splitFeatureName(name) {
143
- const parts = String(name).split(/And|&|_/).map(part => part.trim()).filter(Boolean);
144
- if (parts.length > 1) {
145
- return parts;
146
- }
147
- const camelParts = String(name).match(/[A-Z][a-z0-9]+/g) || [name];
148
- return camelParts.length > 0 ? camelParts : [name];
149
- }
150
-
151
131
  extractFeatureName(content) {
152
132
  const match = content.match(/Feature:\s*(.+)/);
153
133
  return match ? match[1].trim().replace(/\s+/g, '') : null;
@@ -146,7 +146,7 @@ function analyzeMemoryManagement({ content, filePath, addFinding }) {
146
146
  }
147
147
 
148
148
  function analyzeOptionals({ content, filePath, addFinding }) {
149
- const forceUnwraps = content.match(/\b\w+!/g);
149
+ const forceUnwraps = content.match(/(\w+)\s*!/g);
150
150
  if (forceUnwraps && forceUnwraps.length > 0) {
151
151
  const nonIBOutlets = forceUnwraps.filter(match => !content.includes(`@IBOutlet`));
152
152
  if (nonIBOutlets.length > 0) {
@@ -451,7 +451,7 @@ function analyzeFunctionAST(analyzer, node, filePath) {
451
451
  const ifStatements = countStatementsOfType(substructure, 'stmt.if');
452
452
  const guardStatements = countStatementsOfType(substructure, 'stmt.guard');
453
453
 
454
- const hasEarlyReturns = guardStatements > 0;
454
+ const hasEarlyReturns = (analyzer.fileContent || '').includes('return') && guardStatements > 0;
455
455
  const nestingLevel = calculateNestingDepth(substructure);
456
456
 
457
457
  if (nestingLevel >= 3 && !hasEarlyReturns) {
@@ -496,6 +496,8 @@ function analyzePropertyAST(analyzer, node, filePath) {
496
496
  }
497
497
 
498
498
  function analyzeClosuresAST(analyzer, filePath) {
499
+ const isStruct = analyzer.structs.length > 0;
500
+
499
501
  for (const closure of analyzer.closures) {
500
502
  const closureText = analyzer.safeStringify(closure);
501
503
  const hasSelfReference = closureText.includes('"self"') || closureText.includes('key.name":"self');
@@ -503,13 +505,7 @@ function analyzeClosuresAST(analyzer, filePath) {
503
505
  const parentFunc = closure._parent;
504
506
  const isEscaping = parentFunc && (parentFunc['key.typename'] || '').includes('@escaping');
505
507
 
506
- let container = closure._parent;
507
- while (container && !['source.lang.swift.decl.class', 'source.lang.swift.decl.struct'].includes(container['key.kind'])) {
508
- container = container._parent;
509
- }
510
- const isContainerStruct = container && container['key.kind'] === 'source.lang.swift.decl.struct';
511
-
512
- if (hasSelfReference && isEscaping && !isContainerStruct) {
508
+ if (hasSelfReference && isEscaping && !isStruct) {
513
509
  const offset = closure['key.offset'] || 0;
514
510
  const length = closure['key.length'] || 100;
515
511
  const closureCode = (analyzer.fileContent || '').substring(offset, offset + length);
@@ -2504,18 +2504,12 @@ if (require.main === module) {
2504
2504
  * Called ONLY after MCP handshake is complete
2505
2505
  */
2506
2506
  function startPollingLoops() {
2507
- const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
2508
- evidenceMonitor.start();
2509
-
2510
- if (process.env.DEBUG) {
2511
- process.stderr.write('[MCP] EvidenceMonitorService started with 3-min auto-refresh\n');
2512
- }
2513
-
2514
2507
  setInterval(async () => {
2515
2508
  try {
2516
2509
  const now = Date.now();
2517
2510
  const gitFlowService = getCompositionRoot().getGitFlowService();
2518
2511
  const gitQuery = getCompositionRoot().getGitQueryAdapter();
2512
+ const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
2519
2513
  const orchestrator = getCompositionRoot().getOrchestrator();
2520
2514
 
2521
2515
  const currentBranch = gitFlowService.getCurrentBranch();
@@ -1,57 +0,0 @@
1
- const { startPollingLoops } = require('./ast-intelligence-automation');
2
- const { getCompositionRoot } = require('../../../application/composition-root');
3
-
4
- jest.mock('../../../application/composition-root');
5
-
6
- describe('Polling Loops', () => {
7
- let mockEvidenceMonitor;
8
-
9
- beforeEach(() => {
10
- mockEvidenceMonitor = {
11
- start: jest.fn(),
12
- refresh: jest.fn(),
13
- isStale: jest.fn().mockReturnValue(true)
14
- };
15
-
16
- getCompositionRoot.mockReturnValue({
17
- getEvidenceMonitor: () => mockEvidenceMonitor,
18
- getGitFlowService: jest.fn(),
19
- getGitQueryAdapter: jest.fn(),
20
- getOrchestrator: jest.fn()
21
- });
22
- });
23
-
24
- it('should configure evidence monitor refresh interval to 3 minutes', () => {
25
- const originalSetInterval = setInterval;
26
- let intervalCallback;
27
- let intervalTime;
28
-
29
- global.setInterval = (callback, time) => {
30
- intervalCallback = callback;
31
- intervalTime = time;
32
- return originalSetInterval(callback, time);
33
- };
34
-
35
- startPollingLoops();
36
-
37
- expect(intervalTime).toBe(180000);
38
- expect(mockEvidenceMonitor.start).toHaveBeenCalled();
39
-
40
- intervalCallback();
41
- expect(mockEvidenceMonitor.refresh).toHaveBeenCalled();
42
-
43
- global.setInterval = originalSetInterval;
44
- });
45
-
46
- it('should handle evidence refresh errors with retry', () => {
47
- mockEvidenceMonitor.refresh.mockRejectedValue(new Error('Refresh failed'));
48
-
49
- startPollingLoops();
50
-
51
- const intervalCallback = setInterval.mock.calls[0][0];
52
- intervalCallback();
53
-
54
- expect(mockEvidenceMonitor.refresh).toHaveBeenCalled();
55
- expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 30000);
56
- });
57
- });