pumuki-ast-hooks 5.5.35 → 5.5.37

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": "5.5.35",
3
+ "version": "5.5.37",
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": {
@@ -104,8 +104,8 @@ function getInstalledVersion() {
104
104
 
105
105
  function getLatestVersion() {
106
106
  try {
107
- // Try npm view
108
- const output = execSync('npm view @pumuki/ast-intelligence-hooks version', {
107
+ // Try npm view (correct package name: pumuki-ast-hooks)
108
+ const output = execSync('npm view pumuki-ast-hooks version', {
109
109
  encoding: 'utf-8',
110
110
  stdio: ['ignore', 'pipe', 'ignore'],
111
111
  timeout: 5000
@@ -112,11 +112,10 @@ function step2_syncDevelop() {
112
112
  log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
113
113
 
114
114
  log(COLORS.blue, 'Fetching from origin...');
115
- exec('git fetch origin', { silent: true });
115
+ exec('git fetch origin');
116
116
 
117
117
  log(COLORS.blue, 'Pulling latest changes from develop...');
118
- exec('git pull origin develop', { silent: true });
119
-
118
+ exec('git pull --ff-only origin develop');
120
119
  log(COLORS.green, '✅ Develop synced');
121
120
  }
122
121
 
@@ -125,13 +124,19 @@ function step3_syncMain() {
125
124
  log(COLORS.cyan, '🔄 Step 3: Sync Main');
126
125
  log(COLORS.cyan, '═══════════════════════════════════════════════════════════════\n');
127
126
 
127
+ const current = getCurrentBranch();
128
+
128
129
  log(COLORS.blue, 'Fetching from origin...');
129
- exec('git fetch origin', { silent: true });
130
+ exec('git fetch origin');
130
131
 
131
132
  log(COLORS.blue, 'Pulling latest changes from main...');
132
- exec('git pull origin main', { silent: true });
133
-
133
+ exec('git checkout main');
134
+ exec('git pull --ff-only origin main');
134
135
  log(COLORS.green, '✅ Main synced');
136
+
137
+ if (current && current !== 'main') {
138
+ exec(`git checkout ${current}`);
139
+ }
135
140
  }
136
141
 
137
142
  function step4_createReleasePR(prTitle, prBody) {
@@ -23,7 +23,15 @@ describe('AI_EVIDENCE.json structure validation', () => {
23
23
  question_3_clean_architecture: 'Verify that the code follows Clean Architecture and SOLID principles',
24
24
  last_answered: new Date().toISOString()
25
25
  },
26
- rules_read: {
26
+ rules_read: [
27
+ {
28
+ file: 'rulesgold.mdc',
29
+ verified: true,
30
+ summary: 'loaded (example)',
31
+ path: '/tmp/rulesgold.mdc'
32
+ }
33
+ ],
34
+ rules_read_flags: {
27
35
  backend: true,
28
36
  frontend: false,
29
37
  ios: false,
@@ -79,15 +87,25 @@ describe('AI_EVIDENCE.json structure validation', () => {
79
87
  expect(evidence.protocol_3_questions.last_answered).toBeDefined();
80
88
  });
81
89
 
82
- it('should have rules_read field tracking all platforms', () => {
90
+ it('should have rules_read field with rule load evidence entries', () => {
83
91
  const evidence = createMockEvidence();
84
92
  expect(evidence.rules_read).toBeDefined();
85
- expect(evidence.rules_read.backend).toBe(true);
86
- expect(evidence.rules_read.frontend).toBe(false);
87
- expect(evidence.rules_read.ios).toBe(false);
88
- expect(evidence.rules_read.android).toBe(false);
89
- expect(evidence.rules_read.gold).toBe(true);
90
- expect(evidence.rules_read.last_checked).toBeDefined();
93
+ expect(Array.isArray(evidence.rules_read)).toBe(true);
94
+ expect(evidence.rules_read.length).toBeGreaterThan(0);
95
+ expect(evidence.rules_read[0]).toHaveProperty('file');
96
+ expect(evidence.rules_read[0]).toHaveProperty('verified');
97
+ expect(evidence.rules_read[0]).toHaveProperty('summary');
98
+ });
99
+
100
+ it('should have rules_read_flags legacy field tracking platform flags', () => {
101
+ const evidence = createMockEvidence();
102
+ expect(evidence.rules_read_flags).toBeDefined();
103
+ expect(evidence.rules_read_flags.backend).toBe(true);
104
+ expect(evidence.rules_read_flags.frontend).toBe(false);
105
+ expect(evidence.rules_read_flags.ios).toBe(false);
106
+ expect(evidence.rules_read_flags.android).toBe(false);
107
+ expect(evidence.rules_read_flags.gold).toBe(true);
108
+ expect(evidence.rules_read_flags.last_checked).toBeDefined();
91
109
  });
92
110
 
93
111
  it('should have current_context field with branch and file info', () => {
@@ -9,6 +9,7 @@ const { TokenManager } = require('../utils/token-manager');
9
9
  const { toErrorMessage } = require('../utils/error-utils');
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
+ const DynamicRulesLoader = require('../../application/services/DynamicRulesLoader');
12
13
 
13
14
  function deriveCategoryFromRuleId(ruleId) {
14
15
  if (!ruleId || typeof ruleId !== 'string') return 'unknown';
@@ -24,6 +25,121 @@ function deriveCategoryFromRuleId(ruleId) {
24
25
  return parts[0] || 'unknown';
25
26
  }
26
27
 
28
+ function detectPlatformsFromStagedFiles(stagedFiles) {
29
+ const platforms = new Set();
30
+ const files = Array.isArray(stagedFiles) ? stagedFiles : [];
31
+
32
+ files.forEach(filePath => {
33
+ const lowerPath = String(filePath || '').toLowerCase();
34
+
35
+ if (lowerPath.includes('apps/backend/') || lowerPath.includes('/services/') || lowerPath.includes('services/') || lowerPath.includes('/functions/') || lowerPath.includes('functions/')) {
36
+ platforms.add('backend');
37
+ }
38
+ if (lowerPath.includes('apps/web-app/') || lowerPath.includes('apps/admin') || lowerPath.includes('apps/frontend/') || lowerPath.includes('frontend/')) {
39
+ platforms.add('frontend');
40
+ }
41
+ if (lowerPath.includes('apps/ios/') || lowerPath.endsWith('.swift')) {
42
+ platforms.add('ios');
43
+ }
44
+ if (lowerPath.includes('apps/android/') || lowerPath.endsWith('.kt') || lowerPath.endsWith('.kts') || lowerPath.endsWith('.java')) {
45
+ platforms.add('android');
46
+ }
47
+ });
48
+
49
+ return platforms;
50
+ }
51
+
52
+ function countViolationsByPlatform(violations) {
53
+ const counts = { backend: 0, frontend: 0, ios: 0, android: 0 };
54
+ const list = Array.isArray(violations) ? violations : [];
55
+
56
+ list.forEach(v => {
57
+ const ruleId = v.ruleId || v.rule || 'unknown';
58
+ const category = String(v.category || deriveCategoryFromRuleId(ruleId) || '').toLowerCase();
59
+ const platform = category.split('.')[0];
60
+ if (counts[platform] !== undefined) {
61
+ counts[platform] += 1;
62
+ }
63
+ });
64
+
65
+ return counts;
66
+ }
67
+
68
+ function buildPlatformsEvidence(stagedFiles, violations) {
69
+ const stagedDetected = detectPlatformsFromStagedFiles(stagedFiles);
70
+ const violationCounts = countViolationsByPlatform(violations);
71
+
72
+ const platforms = ['backend', 'frontend', 'ios', 'android'];
73
+ const result = {};
74
+
75
+ platforms.forEach(p => {
76
+ const violationsCount = violationCounts[p] || 0;
77
+ result[p] = {
78
+ detected: stagedDetected.has(p) || violationsCount > 0,
79
+ violations: violationsCount
80
+ };
81
+ });
82
+
83
+ return result;
84
+ }
85
+
86
+ function summarizeRulesContent(content) {
87
+ if (!content || typeof content !== 'string') {
88
+ return 'not found';
89
+ }
90
+ const firstNonEmpty = content
91
+ .split('\n')
92
+ .map(l => l.trim())
93
+ .find(l => l.length > 0);
94
+ const firstLine = firstNonEmpty ? firstNonEmpty.slice(0, 140) : '';
95
+ return firstLine.length > 0 ? firstLine : `loaded (${content.length} chars)`;
96
+ }
97
+
98
+ async function buildRulesReadEvidence(platformsEvidence) {
99
+ const loader = new DynamicRulesLoader();
100
+ const entries = [];
101
+
102
+ const detectedPlatforms = ['backend', 'frontend', 'ios', 'android']
103
+ .filter(p => platformsEvidence && platformsEvidence[p] && platformsEvidence[p].detected);
104
+
105
+ const uniqueFiles = ['rulesgold.mdc', ...detectedPlatforms.map(p => loader.rulesMap[p]).filter(Boolean)];
106
+
107
+ for (const file of uniqueFiles) {
108
+ let verified = false;
109
+ let summary = 'not found';
110
+ let resolvedPath = null;
111
+
112
+ try {
113
+ const content = await loader.loadRule(file);
114
+ verified = Boolean(content);
115
+ summary = summarizeRulesContent(content);
116
+ const cached = loader.cache && loader.cache.rules ? loader.cache.rules.get(file) : null;
117
+ resolvedPath = cached && cached.fullPath ? cached.fullPath : null;
118
+ } catch (error) {
119
+ summary = `error: ${toErrorMessage(error)}`;
120
+ }
121
+
122
+ entries.push({
123
+ file,
124
+ verified,
125
+ summary,
126
+ path: resolvedPath
127
+ });
128
+ }
129
+
130
+ return {
131
+ entries,
132
+ legacyFlags: {
133
+ backend: detectedPlatforms.includes('backend'),
134
+ frontend: detectedPlatforms.includes('frontend'),
135
+ ios: detectedPlatforms.includes('ios'),
136
+ android: detectedPlatforms.includes('android'),
137
+ gold: true,
138
+ last_checked: formatLocalTimestamp()
139
+ }
140
+ };
141
+ }
142
+
27
143
  function formatLocalTimestamp(date = new Date()) {
28
144
  const year = date.getFullYear();
29
145
  const month = String(date.getMonth() + 1).padStart(2, '0');
@@ -90,7 +206,7 @@ async function runIntelligentAudit() {
90
206
  const gateResult = { passed: true, exitCode: 0, blockedBy: null };
91
207
  const tokenManager = new TokenManager();
92
208
  const tokenUsage = tokenManager.estimate(enhancedAll, {});
93
- updateAIEvidence(enhancedAll, gateResult, tokenUsage);
209
+ await updateAIEvidence(enhancedAll, gateResult, tokenUsage);
94
210
  process.exit(0);
95
211
  }
96
212
 
@@ -136,7 +252,7 @@ async function runIntelligentAudit() {
136
252
 
137
253
  tokenManager.record(tokenUsage);
138
254
 
139
- updateAIEvidence(enhancedViolations, gateResult, tokenUsage);
255
+ await updateAIEvidence(enhancedViolations, gateResult, tokenUsage);
140
256
 
141
257
  saveEnhancedViolations(enhancedViolations);
142
258
 
@@ -201,7 +317,7 @@ function saveEnhancedViolations(violations) {
201
317
  fs.writeFileSync(outputPath, JSON.stringify(enhanced, null, 2));
202
318
  }
203
319
 
204
- function updateAIEvidence(violations, gateResult, tokenUsage) {
320
+ async function updateAIEvidence(violations, gateResult, tokenUsage) {
205
321
  const evidencePath = '.AI_EVIDENCE.json';
206
322
 
207
323
  if (!fs.existsSync(evidencePath)) {
@@ -320,14 +436,12 @@ function updateAIEvidence(violations, gateResult, tokenUsage) {
320
436
  last_answered: formatLocalTimestamp()
321
437
  };
322
438
 
323
- evidence.rules_read = {
324
- backend: true,
325
- frontend: false,
326
- ios: false,
327
- android: false,
328
- gold: true,
329
- last_checked: formatLocalTimestamp()
330
- };
439
+ const stagedFiles = getStagedFiles();
440
+ const platformsEvidence = buildPlatformsEvidence(stagedFiles, violations);
441
+ const rulesRead = await buildRulesReadEvidence(platformsEvidence);
442
+
443
+ evidence.rules_read = rulesRead.entries;
444
+ evidence.rules_read_flags = rulesRead.legacyFlags;
331
445
 
332
446
  evidence.current_context = {
333
447
  working_on: env.get('AUTO_EVIDENCE_SUMMARY', 'AST Intelligence Analysis'),
@@ -337,12 +451,7 @@ function updateAIEvidence(violations, gateResult, tokenUsage) {
337
451
  timestamp: formatLocalTimestamp()
338
452
  };
339
453
 
340
- evidence.platforms = {
341
- backend: { detected: true, violations: violations.filter(v => v.category && v.category.includes('backend')).length },
342
- frontend: { detected: false, violations: 0 },
343
- ios: { detected: false, violations: 0 },
344
- android: { detected: false, violations: 0 }
345
- };
454
+ evidence.platforms = platformsEvidence;
346
455
 
347
456
  evidence.session_id = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
348
457