pumuki 6.3.353 → 6.3.355

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.
@@ -4,6 +4,7 @@ import { captureRepoState } from '../evidence/repoState';
4
4
  import type { RepoState } from '../evidence/schema';
5
5
  import {
6
6
  extractEvidenceBlockingCauses,
7
+ type EvidenceBlockingCause,
7
8
  formatEvidenceBlockingCause,
8
9
  } from '../evidence/blockingCauses';
9
10
  import { resolvePolicyForStage } from './stagePolicies';
@@ -31,6 +32,11 @@ export type AiGateViolation = {
31
32
  code: string;
32
33
  message: string;
33
34
  severity: 'ERROR' | 'WARN';
35
+ ruleId?: string;
36
+ file?: string;
37
+ line?: number;
38
+ lines?: readonly number[];
39
+ remediation?: string;
34
40
  };
35
41
 
36
42
  type PreWriteWorktreeHygienePolicy = {
@@ -198,6 +204,50 @@ const toWarnViolation = (code: string, message: string): AiGateViolation => ({
198
204
  message,
199
205
  });
200
206
 
207
+ const normalizeEvidenceCauseLines = (
208
+ lines: EvidenceBlockingCause['lines']
209
+ ): Pick<AiGateViolation, 'line' | 'lines'> => {
210
+ if (typeof lines === 'number' && Number.isFinite(lines) && lines > 0) {
211
+ return { line: Math.floor(lines) };
212
+ }
213
+ if (Array.isArray(lines)) {
214
+ const normalized = Array.from(
215
+ new Set(
216
+ lines
217
+ .filter((line): line is number => typeof line === 'number' && Number.isFinite(line) && line > 0)
218
+ .map((line) => Math.floor(line))
219
+ )
220
+ ).sort((left, right) => left - right);
221
+ return normalized.length > 0 ? { lines: normalized } : {};
222
+ }
223
+ if (typeof lines === 'string') {
224
+ const normalized = Array.from(
225
+ new Set(
226
+ lines
227
+ .split(',')
228
+ .map((line) => Number.parseInt(line.trim(), 10))
229
+ .filter((line) => Number.isFinite(line) && line > 0)
230
+ )
231
+ ).sort((left, right) => left - right);
232
+ return normalized.length > 0 ? { lines: normalized } : {};
233
+ }
234
+ return {};
235
+ };
236
+
237
+ const toEvidenceGateBlockedViolation = (
238
+ severity: AiGateViolation['severity'],
239
+ message: string,
240
+ cause?: EvidenceBlockingCause
241
+ ): AiGateViolation => ({
242
+ code: 'EVIDENCE_GATE_BLOCKED',
243
+ severity,
244
+ message,
245
+ ruleId: cause?.ruleId,
246
+ file: cause?.file,
247
+ remediation: cause?.remediation,
248
+ ...normalizeEvidenceCauseLines(cause?.lines),
249
+ });
250
+
201
251
  const toSkillsViolation = (
202
252
  resolution: SkillsEnforcementResolution,
203
253
  code: string,
@@ -1327,6 +1377,7 @@ const collectEvidenceViolations = (
1327
1377
  }
1328
1378
 
1329
1379
  if (result.evidence.ai_gate.status === 'BLOCKED') {
1380
+ const blockingCause = extractEvidenceBlockingCauses(result.evidence)[0];
1330
1381
  const gateBlockedMessage = buildEvidenceGateBlockedMessage(result);
1331
1382
  violations.push(
1332
1383
  !hasEvidenceBlockingCause(result) && ageSeconds <= maxAgeSeconds
@@ -1339,7 +1390,7 @@ const collectEvidenceViolations = (
1339
1390
  'EVIDENCE_GATE_BLOCKED',
1340
1391
  `${gateBlockedMessage} Advisory because the current slice only contains documentation/render/tooling artifacts and evidence is fresh.`
1341
1392
  )
1342
- : toErrorViolation('EVIDENCE_GATE_BLOCKED', gateBlockedMessage)
1393
+ : toEvidenceGateBlockedViolation('ERROR', gateBlockedMessage, blockingCause)
1343
1394
  );
1344
1395
  }
1345
1396
 
@@ -30,6 +30,8 @@ type NotificationBlockingCause = {
30
30
  message: string;
31
31
  ruleId?: string;
32
32
  file?: string;
33
+ line?: number;
34
+ lines?: ReadonlyArray<number>;
33
35
  remediation?: string;
34
36
  };
35
37
 
@@ -83,6 +85,48 @@ const extractMessageField = (message: string, field: string): string | undefined
83
85
  return value && value.length > 0 ? value : undefined;
84
86
  };
85
87
 
88
+ const normalizeEvidenceLinesForNotification = (
89
+ lines: unknown
90
+ ): Pick<NotificationBlockingCause, 'line' | 'lines'> => {
91
+ if (typeof lines === 'number' && Number.isFinite(lines) && lines > 0) {
92
+ return {
93
+ line: Math.floor(lines),
94
+ };
95
+ }
96
+ if (Array.isArray(lines)) {
97
+ const normalized = Array.from(
98
+ new Set(
99
+ lines
100
+ .filter((line): line is number => typeof line === 'number' && Number.isFinite(line) && line > 0)
101
+ .map((line) => Math.floor(line))
102
+ )
103
+ ).sort((left, right) => left - right);
104
+ return normalized.length > 0 ? { lines: normalized } : {};
105
+ }
106
+ if (typeof lines === 'string') {
107
+ const normalized = Array.from(
108
+ new Set(
109
+ lines
110
+ .split(',')
111
+ .map((line) => Number.parseInt(line.trim(), 10))
112
+ .filter((line) => Number.isFinite(line) && line > 0)
113
+ )
114
+ ).sort((left, right) => left - right);
115
+ return normalized.length > 0 ? { lines: normalized } : {};
116
+ }
117
+ return {};
118
+ };
119
+
120
+ const extractStructuredLinesFromMessage = (
121
+ message: string
122
+ ): Pick<NotificationBlockingCause, 'line' | 'lines'> => {
123
+ const raw = message.match(/\blines?=([0-9][0-9,\-\s]*)\b/i)?.[1];
124
+ if (!raw) {
125
+ return {};
126
+ }
127
+ return normalizeEvidenceLinesForNotification(raw);
128
+ };
129
+
86
130
  const extractFirstNestedSkillSegment = (message: string): string =>
87
131
  message
88
132
  .replace(/^.*?\bBlocking causes:\s*1\)\s*/isu, '')
@@ -121,6 +165,11 @@ const extractNestedSkillCauseFromWrapper = (cause: NotificationBlockingCause): N
121
165
  code: normalizeRuleCode(ruleId),
122
166
  ruleId,
123
167
  file: nestedFile ?? cause.file,
168
+ ...(
169
+ cause.line || cause.lines
170
+ ? {}
171
+ : extractStructuredLinesFromMessage(normalizedNestedMessage)
172
+ ),
124
173
  message: `rule=${ruleId} ${normalizedNestedMessage}`,
125
174
  remediation:
126
175
  cause.remediation && !/corrige la violaci[oó]n indicada|corrige la causa bloqueante/i.test(cause.remediation)
@@ -163,6 +212,7 @@ export const toAuditSummaryEventFromEvidence = (
163
212
  code: cause.code,
164
213
  ruleId: cause.ruleId,
165
214
  file: cause.file,
215
+ ...normalizeEvidenceLinesForNotification(cause.lines),
166
216
  message: formatEvidenceBlockingCause(cause),
167
217
  remediation: cause.remediation,
168
218
  })
@@ -204,6 +254,7 @@ export const toAuditSummaryEventFromAiGate = (params: {
204
254
  ruleId: violation.code.startsWith('skills.') ? violation.code : undefined,
205
255
  message: violation.message,
206
256
  remediation: 'Corrige la violación indicada y vuelve a intentar el commit.',
257
+ ...extractStructuredLinesFromMessage(violation.message),
207
258
  })
208
259
  ),
209
260
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.353",
3
+ "version": "6.3.355",
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": {