pumuki-ast-hooks 5.5.36 → 5.5.38
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.
|
|
3
|
+
"version": "5.5.38",
|
|
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": {
|
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,183 @@ 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
|
+
function buildAutoContextFrontmatter(detectedPlatforms) {
|
|
99
|
+
const platforms = Array.isArray(detectedPlatforms) ? detectedPlatforms.filter(Boolean) : [];
|
|
100
|
+
const generated = formatLocalTimestamp();
|
|
101
|
+
const platformStr = platforms.length > 0 ? platforms.join(', ') : 'none';
|
|
102
|
+
return `---\nalwaysApply: true\ndescription: Auto-generated context for detected platforms\nplatforms: ${platformStr}\ngenerated: ${generated}\n---\n\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function buildAutoContextContent(platformsEvidence) {
|
|
106
|
+
const loader = new DynamicRulesLoader();
|
|
107
|
+
const detectedPlatforms = ['backend', 'frontend', 'ios', 'android']
|
|
108
|
+
.filter(p => platformsEvidence && platformsEvidence[p] && platformsEvidence[p].detected);
|
|
109
|
+
|
|
110
|
+
const files = ['rulesgold.mdc', ...detectedPlatforms.map(p => loader.rulesMap[p]).filter(Boolean)];
|
|
111
|
+
const uniqueFiles = Array.from(new Set(files));
|
|
112
|
+
|
|
113
|
+
let content = buildAutoContextFrontmatter(detectedPlatforms);
|
|
114
|
+
|
|
115
|
+
for (const file of uniqueFiles) {
|
|
116
|
+
let ruleContent = null;
|
|
117
|
+
try {
|
|
118
|
+
ruleContent = await loader.loadRule(file);
|
|
119
|
+
} catch {
|
|
120
|
+
ruleContent = null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
content += `## Source: ${file}\n\n`;
|
|
124
|
+
content += ruleContent ? `${ruleContent}\n\n` : `not found\n\n`;
|
|
125
|
+
content += `---\n\n`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return content;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function writeAutoContextFiles(platformsEvidence) {
|
|
132
|
+
try {
|
|
133
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
134
|
+
if (fs.existsSync(pkgPath)) {
|
|
135
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
136
|
+
if (pkg && pkg.name === 'pumuki-ast-hooks') {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
process.stderr.write(`[Intelligent Audit] ⚠️ Failed to inspect package.json for auto-context skip logic (${toErrorMessage(error)})\n`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const content = await buildAutoContextContent(platformsEvidence);
|
|
145
|
+
const targets = [
|
|
146
|
+
path.join(process.cwd(), '.cursor', 'rules', 'auto-context.mdc'),
|
|
147
|
+
path.join(process.cwd(), '.windsurf', 'rules', 'auto-context.mdc')
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
for (const target of targets) {
|
|
151
|
+
try {
|
|
152
|
+
await fs.promises.mkdir(path.dirname(target), { recursive: true });
|
|
153
|
+
await fs.promises.writeFile(target, content, 'utf-8');
|
|
154
|
+
} catch (error) {
|
|
155
|
+
process.stderr.write(`[Intelligent Audit] ⚠️ Failed to write auto-context: ${target} (${toErrorMessage(error)})\n`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function buildRulesReadEvidence(platformsEvidence) {
|
|
161
|
+
const loader = new DynamicRulesLoader();
|
|
162
|
+
const entries = [];
|
|
163
|
+
|
|
164
|
+
const detectedPlatforms = ['backend', 'frontend', 'ios', 'android']
|
|
165
|
+
.filter(p => platformsEvidence && platformsEvidence[p] && platformsEvidence[p].detected);
|
|
166
|
+
|
|
167
|
+
const uniqueFiles = ['rulesgold.mdc', ...detectedPlatforms.map(p => loader.rulesMap[p]).filter(Boolean)];
|
|
168
|
+
|
|
169
|
+
for (const file of uniqueFiles) {
|
|
170
|
+
let verified = false;
|
|
171
|
+
let summary = 'not found';
|
|
172
|
+
let resolvedPath = null;
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const content = await loader.loadRule(file);
|
|
176
|
+
verified = Boolean(content);
|
|
177
|
+
summary = summarizeRulesContent(content);
|
|
178
|
+
const cached = loader.cache && loader.cache.rules ? loader.cache.rules.get(file) : null;
|
|
179
|
+
resolvedPath = cached && cached.fullPath ? cached.fullPath : null;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
summary = `error: ${toErrorMessage(error)}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
entries.push({
|
|
185
|
+
file,
|
|
186
|
+
verified,
|
|
187
|
+
summary,
|
|
188
|
+
path: resolvedPath
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
entries,
|
|
194
|
+
legacyFlags: {
|
|
195
|
+
backend: detectedPlatforms.includes('backend'),
|
|
196
|
+
frontend: detectedPlatforms.includes('frontend'),
|
|
197
|
+
ios: detectedPlatforms.includes('ios'),
|
|
198
|
+
android: detectedPlatforms.includes('android'),
|
|
199
|
+
gold: true,
|
|
200
|
+
last_checked: formatLocalTimestamp()
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
27
205
|
function formatLocalTimestamp(date = new Date()) {
|
|
28
206
|
const year = date.getFullYear();
|
|
29
207
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
@@ -90,7 +268,7 @@ async function runIntelligentAudit() {
|
|
|
90
268
|
const gateResult = { passed: true, exitCode: 0, blockedBy: null };
|
|
91
269
|
const tokenManager = new TokenManager();
|
|
92
270
|
const tokenUsage = tokenManager.estimate(enhancedAll, {});
|
|
93
|
-
updateAIEvidence(enhancedAll, gateResult, tokenUsage);
|
|
271
|
+
await updateAIEvidence(enhancedAll, gateResult, tokenUsage);
|
|
94
272
|
process.exit(0);
|
|
95
273
|
}
|
|
96
274
|
|
|
@@ -136,7 +314,7 @@ async function runIntelligentAudit() {
|
|
|
136
314
|
|
|
137
315
|
tokenManager.record(tokenUsage);
|
|
138
316
|
|
|
139
|
-
updateAIEvidence(enhancedViolations, gateResult, tokenUsage);
|
|
317
|
+
await updateAIEvidence(enhancedViolations, gateResult, tokenUsage);
|
|
140
318
|
|
|
141
319
|
saveEnhancedViolations(enhancedViolations);
|
|
142
320
|
|
|
@@ -201,7 +379,7 @@ function saveEnhancedViolations(violations) {
|
|
|
201
379
|
fs.writeFileSync(outputPath, JSON.stringify(enhanced, null, 2));
|
|
202
380
|
}
|
|
203
381
|
|
|
204
|
-
function updateAIEvidence(violations, gateResult, tokenUsage) {
|
|
382
|
+
async function updateAIEvidence(violations, gateResult, tokenUsage) {
|
|
205
383
|
const evidencePath = '.AI_EVIDENCE.json';
|
|
206
384
|
|
|
207
385
|
if (!fs.existsSync(evidencePath)) {
|
|
@@ -320,14 +498,14 @@ function updateAIEvidence(violations, gateResult, tokenUsage) {
|
|
|
320
498
|
last_answered: formatLocalTimestamp()
|
|
321
499
|
};
|
|
322
500
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
501
|
+
const stagedFiles = getStagedFiles();
|
|
502
|
+
const platformsEvidence = buildPlatformsEvidence(stagedFiles, violations);
|
|
503
|
+
const rulesRead = await buildRulesReadEvidence(platformsEvidence);
|
|
504
|
+
|
|
505
|
+
await writeAutoContextFiles(platformsEvidence);
|
|
506
|
+
|
|
507
|
+
evidence.rules_read = rulesRead.entries;
|
|
508
|
+
evidence.rules_read_flags = rulesRead.legacyFlags;
|
|
331
509
|
|
|
332
510
|
evidence.current_context = {
|
|
333
511
|
working_on: env.get('AUTO_EVIDENCE_SUMMARY', 'AST Intelligence Analysis'),
|
|
@@ -337,12 +515,7 @@ function updateAIEvidence(violations, gateResult, tokenUsage) {
|
|
|
337
515
|
timestamp: formatLocalTimestamp()
|
|
338
516
|
};
|
|
339
517
|
|
|
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
|
-
};
|
|
518
|
+
evidence.platforms = platformsEvidence;
|
|
346
519
|
|
|
347
520
|
evidence.session_id = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
348
521
|
|