pumuki 6.3.269 → 6.3.270
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/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.270
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import type { Finding } from '../../core/gate/Finding';
|
|
4
|
+
import type { GateStage } from '../../core/gate/GateStage';
|
|
5
|
+
|
|
6
|
+
export type ContextGateStatus = 'applied' | 'blocked' | 'disabled';
|
|
7
|
+
|
|
8
|
+
export type ContextGateResult = {
|
|
9
|
+
status: ContextGateStatus;
|
|
10
|
+
finding?: Finding;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const CONTEXT_FILE = '.pumuki/context/context.json';
|
|
14
|
+
const CONTEXT_GATE_ENV = 'PUMUKI_CONTEXT_GATE';
|
|
15
|
+
const CONTEXT_SCHEMA_VERSION = 1;
|
|
16
|
+
|
|
17
|
+
const isStrictContextGate = (): boolean => {
|
|
18
|
+
const normalized = process.env[CONTEXT_GATE_ENV]?.trim().toLowerCase();
|
|
19
|
+
return normalized !== 'off' && normalized !== '0' && normalized !== 'false' && normalized !== 'disabled';
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type LocalContextDocument = {
|
|
23
|
+
schema_version: number;
|
|
24
|
+
repo_root: string;
|
|
25
|
+
generated_at: string;
|
|
26
|
+
contract: {
|
|
27
|
+
status: 'applied';
|
|
28
|
+
required_before_gate: true;
|
|
29
|
+
agent_instruction: string;
|
|
30
|
+
user_remediation: string;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ContextLifecycleResult = {
|
|
35
|
+
status: 'ok' | 'blocked';
|
|
36
|
+
path: string;
|
|
37
|
+
message: string;
|
|
38
|
+
document?: LocalContextDocument;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const buildContextDocument = (repoRoot: string): LocalContextDocument => ({
|
|
42
|
+
schema_version: CONTEXT_SCHEMA_VERSION,
|
|
43
|
+
repo_root: repoRoot,
|
|
44
|
+
generated_at: new Date().toISOString(),
|
|
45
|
+
contract: {
|
|
46
|
+
status: 'applied',
|
|
47
|
+
required_before_gate: true,
|
|
48
|
+
agent_instruction:
|
|
49
|
+
'STOP si este contexto no se puede leer o validar antes de editar, auditar, commitear o pushear.',
|
|
50
|
+
user_remediation:
|
|
51
|
+
'Ejecuta `npx pumuki context repair` y reejecuta el gate antes de continuar.',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const contextPathForRepo = (repoRoot: string): string => join(repoRoot, CONTEXT_FILE);
|
|
56
|
+
|
|
57
|
+
const parseContextDocument = (repoRoot: string): LocalContextDocument => {
|
|
58
|
+
const contextPath = contextPathForRepo(repoRoot);
|
|
59
|
+
const parsed: unknown = JSON.parse(readFileSync(contextPath, 'utf8'));
|
|
60
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
61
|
+
throw new Error(`${CONTEXT_FILE} no contiene un objeto JSON valido`);
|
|
62
|
+
}
|
|
63
|
+
const candidate = parsed as Partial<LocalContextDocument>;
|
|
64
|
+
if (candidate.schema_version !== CONTEXT_SCHEMA_VERSION) {
|
|
65
|
+
throw new Error(`${CONTEXT_FILE} tiene schema_version invalido`);
|
|
66
|
+
}
|
|
67
|
+
if (candidate.repo_root !== repoRoot) {
|
|
68
|
+
throw new Error(`${CONTEXT_FILE} pertenece a otro repo_root`);
|
|
69
|
+
}
|
|
70
|
+
if (
|
|
71
|
+
!candidate.contract ||
|
|
72
|
+
candidate.contract.status !== 'applied' ||
|
|
73
|
+
candidate.contract.required_before_gate !== true ||
|
|
74
|
+
typeof candidate.contract.agent_instruction !== 'string' ||
|
|
75
|
+
candidate.contract.agent_instruction.trim().length === 0 ||
|
|
76
|
+
typeof candidate.contract.user_remediation !== 'string' ||
|
|
77
|
+
candidate.contract.user_remediation.trim().length === 0
|
|
78
|
+
) {
|
|
79
|
+
throw new Error(`${CONTEXT_FILE} no declara contrato aplicado para usuario y agente IA`);
|
|
80
|
+
}
|
|
81
|
+
return candidate as LocalContextDocument;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const writeLocalContext = (repoRoot: string): ContextLifecycleResult => {
|
|
85
|
+
const contextPath = contextPathForRepo(repoRoot);
|
|
86
|
+
const document = buildContextDocument(repoRoot);
|
|
87
|
+
mkdirSync(join(repoRoot, '.pumuki/context'), { recursive: true });
|
|
88
|
+
writeFileSync(contextPath, `${JSON.stringify(document, null, 2)}\n`, 'utf8');
|
|
89
|
+
return {
|
|
90
|
+
status: 'ok',
|
|
91
|
+
path: contextPath,
|
|
92
|
+
message: 'Contexto local Pumuki inicializado y aplicado.',
|
|
93
|
+
document,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const readLocalContextStatus = (repoRoot: string): ContextLifecycleResult => {
|
|
98
|
+
const contextPath = contextPathForRepo(repoRoot);
|
|
99
|
+
if (!existsSync(contextPath)) {
|
|
100
|
+
return {
|
|
101
|
+
status: 'blocked',
|
|
102
|
+
path: contextPath,
|
|
103
|
+
message:
|
|
104
|
+
`Pumuki bloqueado: falta ${CONTEXT_FILE}. Ejecuta ` +
|
|
105
|
+
'`npx pumuki context init` o `npx pumuki context repair`.',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const document = parseContextDocument(repoRoot);
|
|
110
|
+
return {
|
|
111
|
+
status: 'ok',
|
|
112
|
+
path: contextPath,
|
|
113
|
+
message: 'Contexto local Pumuki leido, validado y aplicado.',
|
|
114
|
+
document,
|
|
115
|
+
};
|
|
116
|
+
} catch (error) {
|
|
117
|
+
const reason = error instanceof Error ? error.message : 'context validation error';
|
|
118
|
+
return {
|
|
119
|
+
status: 'blocked',
|
|
120
|
+
path: contextPath,
|
|
121
|
+
message:
|
|
122
|
+
`Pumuki bloqueado: contexto local invalido. Causa: ${reason}. Ejecuta ` +
|
|
123
|
+
'`npx pumuki context repair`.',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const repairLocalContext = (repoRoot: string): ContextLifecycleResult =>
|
|
129
|
+
writeLocalContext(repoRoot);
|
|
130
|
+
|
|
131
|
+
const contextBlockedFinding = (params: {
|
|
132
|
+
stage: GateStage;
|
|
133
|
+
code: string;
|
|
134
|
+
cause: string;
|
|
135
|
+
evidence: string;
|
|
136
|
+
}): Finding => ({
|
|
137
|
+
ruleId: 'governance.context.local-context-not-applied',
|
|
138
|
+
severity: 'ERROR',
|
|
139
|
+
code: params.code,
|
|
140
|
+
message:
|
|
141
|
+
`Pumuki bloqueado: contexto local obligatorio no aplicado en ${params.stage}. ` +
|
|
142
|
+
`Causa: ${params.cause}. ` +
|
|
143
|
+
`Evidencia: ${params.evidence}. ` +
|
|
144
|
+
'Usuario: ejecuta `npx pumuki context init` si el repo no tiene contexto, o `npx pumuki context repair` si existe pero no valida. ' +
|
|
145
|
+
'Agente IA: STOP. No edites archivos ni continues la tarea hasta leer, validar y aplicar el contexto local del repo.',
|
|
146
|
+
filePath: CONTEXT_FILE,
|
|
147
|
+
matchedBy: 'PumukiContextGate',
|
|
148
|
+
source: 'pumuki-context-store',
|
|
149
|
+
why: 'Pumuki no puede garantizar continuidad entre sesiones ni aplicar el contrato activo del repo sin contexto local validado.',
|
|
150
|
+
impact: 'El agente y el usuario podrian trabajar con task, contrato o evidencia stale.',
|
|
151
|
+
expected_fix:
|
|
152
|
+
'Inicializa o repara el contexto local: `npx pumuki context init` o `npx pumuki context repair`, y reejecuta el gate.',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
export const evaluateContextGate = (params: {
|
|
156
|
+
repoRoot: string;
|
|
157
|
+
stage: GateStage;
|
|
158
|
+
}): ContextGateResult => {
|
|
159
|
+
if (!isStrictContextGate()) {
|
|
160
|
+
return { status: 'disabled' };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const contextPath = contextPathForRepo(params.repoRoot);
|
|
164
|
+
if (!existsSync(contextPath)) {
|
|
165
|
+
return {
|
|
166
|
+
status: 'blocked',
|
|
167
|
+
finding: contextBlockedFinding({
|
|
168
|
+
stage: params.stage,
|
|
169
|
+
code: 'CONTEXT_NOT_APPLIED',
|
|
170
|
+
cause: `No existe ${CONTEXT_FILE}`,
|
|
171
|
+
evidence: contextPath,
|
|
172
|
+
}),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
parseContextDocument(params.repoRoot);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const reason = error instanceof Error ? error.message : 'JSON parse error';
|
|
180
|
+
return {
|
|
181
|
+
status: 'blocked',
|
|
182
|
+
finding: contextBlockedFinding({
|
|
183
|
+
stage: params.stage,
|
|
184
|
+
code: 'CONTEXT_INVALID',
|
|
185
|
+
cause: reason,
|
|
186
|
+
evidence: contextPath,
|
|
187
|
+
}),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { status: 'applied' };
|
|
192
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.270",
|
|
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": {
|
|
@@ -239,6 +239,7 @@
|
|
|
239
239
|
"core/rules/presets/*.ts",
|
|
240
240
|
"core/rules/presets/heuristics/*.ts",
|
|
241
241
|
"integrations/config/*.ts",
|
|
242
|
+
"integrations/context/*.ts",
|
|
242
243
|
"integrations/evidence/*.ts",
|
|
243
244
|
"integrations/gate/*.ts",
|
|
244
245
|
"integrations/git/*.ts",
|
|
@@ -9,6 +9,7 @@ export const REQUIRED_PACKAGE_PATHS = [
|
|
|
9
9
|
'core/facts/detectors/typescript/index.ts',
|
|
10
10
|
'core/rules/presets/heuristics/typescript.ts',
|
|
11
11
|
'scripts/package-install-smoke.ts',
|
|
12
|
+
'integrations/context/contextGate.ts',
|
|
12
13
|
'integrations/git/runPlatformGate.ts',
|
|
13
14
|
'integrations/policy/policyProfiles.ts',
|
|
14
15
|
'integrations/policy/experimentalFeatures.ts',
|