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 +1 -1
- package/scripts/hooks-system/bin/check-version.js +2 -2
- package/scripts/hooks-system/bin/gitflow-release.js +11 -6
- package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +26 -8
- package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +126 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki-ast-hooks",
|
|
3
|
-
"version": "5.5.
|
|
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
|
|
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'
|
|
115
|
+
exec('git fetch origin');
|
|
116
116
|
|
|
117
117
|
log(COLORS.blue, 'Pulling latest changes from develop...');
|
|
118
|
-
exec('git pull origin develop'
|
|
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'
|
|
130
|
+
exec('git fetch origin');
|
|
130
131
|
|
|
131
132
|
log(COLORS.blue, 'Pulling latest changes from main...');
|
|
132
|
-
exec('git
|
|
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) {
|
package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js
CHANGED
|
@@ -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
|
|
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
|
|
86
|
-
expect(evidence.rules_read.
|
|
87
|
-
expect(evidence.rules_read
|
|
88
|
-
expect(evidence.rules_read
|
|
89
|
-
expect(evidence.rules_read
|
|
90
|
-
|
|
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
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
|