pumuki 6.3.72 → 6.3.75
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/docs/README.md +9 -7
- package/docs/operations/RELEASE_NOTES.md +0 -7
- package/docs/product/USAGE.md +2 -5
- package/docs/validation/README.md +3 -1
- package/integrations/evidence/buildEvidence.ts +14 -0
- package/integrations/evidence/repoState.ts +3 -0
- package/integrations/evidence/schema.ts +18 -0
- package/integrations/evidence/trackingContract.ts +146 -0
- package/integrations/evidence/writeEvidence.ts +14 -0
- package/integrations/gate/evaluateAiGate.ts +166 -3
- package/integrations/gate/governanceActionCatalog.ts +275 -0
- package/integrations/gate/remediationCatalog.ts +8 -0
- package/integrations/git/GitService.ts +0 -25
- package/integrations/git/aiGateRepoPolicyFindings.ts +4 -0
- package/integrations/git/runPlatformGate.ts +9 -1
- package/integrations/git/runPlatformGateFacts.ts +0 -7
- package/integrations/git/runPlatformGateOutput.ts +36 -27
- package/integrations/lifecycle/adapter.ts +24 -0
- package/integrations/lifecycle/bootstrapManifest.ts +248 -0
- package/integrations/lifecycle/cli.ts +45 -11
- package/integrations/lifecycle/cliSdd.ts +4 -3
- package/integrations/lifecycle/doctor.ts +49 -1
- package/integrations/lifecycle/governanceNextAction.ts +164 -0
- package/integrations/lifecycle/governanceObservationSnapshot.ts +315 -0
- package/integrations/lifecycle/install.ts +21 -0
- package/integrations/lifecycle/state.ts +8 -1
- package/integrations/lifecycle/status.ts +29 -2
- package/integrations/mcp/aiGateCheck.ts +140 -10
- package/integrations/mcp/alignedPlatformGate.ts +232 -0
- package/integrations/mcp/autoExecuteAiStart.ts +92 -85
- package/integrations/mcp/enterpriseServer.ts +6 -6
- package/integrations/mcp/preFlightCheck.ts +51 -5
- package/integrations/mcp/readMcpPrePushStdin.ts +7 -0
- package/integrations/policy/experimentalFeatures.ts +1 -1
- package/package.json +2 -4
- package/scripts/build-ruralgo-s1-evidence-pack.ts +85 -0
- package/scripts/consumer-menu-matrix-baseline-report-lib.ts +38 -13
- package/scripts/framework-menu-consumer-actions-lib.ts +4 -28
- package/scripts/framework-menu-consumer-preflight-hints.ts +2 -5
- package/scripts/framework-menu-consumer-preflight-render.ts +6 -0
- package/scripts/framework-menu-consumer-preflight-run.ts +19 -0
- package/scripts/framework-menu-consumer-preflight-types.ts +8 -0
- package/scripts/framework-menu-consumer-runtime-actions.ts +6 -86
- package/scripts/framework-menu-consumer-runtime-audit.ts +2 -36
- package/scripts/framework-menu-consumer-runtime-lib.ts +0 -2
- package/scripts/framework-menu-consumer-runtime-types.ts +1 -3
- package/scripts/framework-menu-evidence-summary-lib.ts +0 -1
- package/scripts/framework-menu-evidence-summary-read.ts +5 -57
- package/scripts/framework-menu-evidence-summary-severity.ts +1 -3
- package/scripts/framework-menu-evidence-summary-types.ts +0 -7
- package/scripts/framework-menu-gate-lib.ts +0 -9
- package/scripts/framework-menu-layout-data.ts +0 -5
- package/scripts/framework-menu-matrix-baseline-lib.ts +14 -15
- package/scripts/framework-menu-matrix-canary-lib.ts +1 -22
- package/scripts/framework-menu-matrix-evidence-lib.ts +0 -1
- package/scripts/framework-menu-matrix-evidence-types.ts +1 -13
- package/scripts/framework-menu-matrix-runner-lib.ts +0 -35
- package/scripts/framework-menu-system-notifications-macos.ts +0 -4
- package/scripts/framework-menu.ts +0 -3
- package/scripts/ruralgo-s1-evidence-pack-lib.ts +200 -0
- package/AGENTS.md +0 -269
- package/CHANGELOG.md +0 -666
- package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +0 -111
- package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +0 -140
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import type { AiGateStage } from './evaluateAiGate';
|
|
2
|
+
|
|
3
|
+
export type GovernanceCatalogNextAction = {
|
|
4
|
+
kind: 'info' | 'run_command';
|
|
5
|
+
message: string;
|
|
6
|
+
command?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type GovernanceCatalogAction = {
|
|
10
|
+
reason_code: string;
|
|
11
|
+
instruction: string;
|
|
12
|
+
next_action: GovernanceCatalogNextAction;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const PRE_WRITE_VALIDATE_COMMAND =
|
|
16
|
+
'npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json';
|
|
17
|
+
|
|
18
|
+
export const buildGovernanceValidateCommand = (stage: AiGateStage): string =>
|
|
19
|
+
PRE_WRITE_VALIDATE_COMMAND.replace('PRE_WRITE', stage);
|
|
20
|
+
|
|
21
|
+
export const buildGovernancePolicyReconcileCommand = (stage: AiGateStage): string =>
|
|
22
|
+
`npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && ${buildGovernanceValidateCommand(stage)}`;
|
|
23
|
+
|
|
24
|
+
const buildFallbackAction = (code: string): GovernanceCatalogAction => ({
|
|
25
|
+
reason_code: code,
|
|
26
|
+
instruction: 'Corrige el bloqueante primario y vuelve a ejecutar la validación del stage actual.',
|
|
27
|
+
next_action: {
|
|
28
|
+
kind: 'info',
|
|
29
|
+
message: 'Corrige el bloqueante primario y vuelve a ejecutar el mismo comando.',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const resolveGovernanceCatalogAction = (params: {
|
|
34
|
+
code: string;
|
|
35
|
+
stage?: AiGateStage;
|
|
36
|
+
fallback?: GovernanceCatalogAction;
|
|
37
|
+
}): GovernanceCatalogAction => {
|
|
38
|
+
const stage = params.stage ?? 'PRE_WRITE';
|
|
39
|
+
const validateCommand = buildGovernanceValidateCommand(stage);
|
|
40
|
+
switch (params.code) {
|
|
41
|
+
case 'READY':
|
|
42
|
+
return {
|
|
43
|
+
reason_code: 'READY',
|
|
44
|
+
instruction: 'Puedes continuar con la siguiente slice mínima y volver a validar al cerrar.',
|
|
45
|
+
next_action: {
|
|
46
|
+
kind: 'info',
|
|
47
|
+
message: 'Gate en verde. Continúa con la implementación.',
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
case 'EVIDENCE_INVALID_OR_CHAIN':
|
|
51
|
+
return {
|
|
52
|
+
reason_code: 'EVIDENCE_INVALID_OR_CHAIN',
|
|
53
|
+
instruction: 'Regenera la evidencia canónica antes de continuar.',
|
|
54
|
+
next_action: {
|
|
55
|
+
kind: 'run_command',
|
|
56
|
+
message: 'Regenera o corrige la evidencia canónica (.ai_evidence.json) y vuelve a validar governance.',
|
|
57
|
+
command: validateCommand,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
case 'AI_GATE_BLOCKED':
|
|
61
|
+
return {
|
|
62
|
+
reason_code: 'AI_GATE_BLOCKED',
|
|
63
|
+
instruction: 'Corrige primero el bloqueo principal del gate antes de continuar.',
|
|
64
|
+
next_action: {
|
|
65
|
+
kind: 'run_command',
|
|
66
|
+
message: 'Revalida el stage tras corregir la evidencia y el bloqueo principal.',
|
|
67
|
+
command: validateCommand,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
case 'SDD_SESSION_MISSING':
|
|
71
|
+
return {
|
|
72
|
+
reason_code: 'SDD_SESSION_MISSING',
|
|
73
|
+
instruction: 'Abre una sesión SDD válida antes de seguir trabajando.',
|
|
74
|
+
next_action: {
|
|
75
|
+
kind: 'run_command',
|
|
76
|
+
message: 'Abre la sesión SDD del cambio activo y vuelve a validar.',
|
|
77
|
+
command: 'npx --yes --package pumuki@latest pumuki sdd session --open --change=<id>',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
case 'SDD_SESSION_INVALID':
|
|
81
|
+
case 'SDD_SESSION_INVALID_OR_EXPIRED':
|
|
82
|
+
return {
|
|
83
|
+
reason_code: 'SDD_SESSION_INVALID_OR_EXPIRED',
|
|
84
|
+
instruction: 'Refresca o reabre la sesión SDD antes de seguir trabajando.',
|
|
85
|
+
next_action: {
|
|
86
|
+
kind: 'run_command',
|
|
87
|
+
message: 'Refresca la sesión SDD y vuelve a validar governance.',
|
|
88
|
+
command: 'npx --yes --package pumuki@latest pumuki sdd session --refresh --ttl-minutes=90',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
case 'PRE_PUSH_UPSTREAM_MISSING':
|
|
92
|
+
return {
|
|
93
|
+
reason_code: 'PRE_PUSH_UPSTREAM_MISSING',
|
|
94
|
+
instruction: 'Configura el upstream remoto antes de continuar con PRE_PUSH.',
|
|
95
|
+
next_action: {
|
|
96
|
+
kind: 'run_command',
|
|
97
|
+
message: 'Configura tracking remoto para la rama actual.',
|
|
98
|
+
command: 'git push --set-upstream origin <branch>',
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
case 'PRE_PUSH_UPSTREAM_MISALIGNED':
|
|
102
|
+
return {
|
|
103
|
+
reason_code: 'PRE_PUSH_UPSTREAM_MISALIGNED',
|
|
104
|
+
instruction: 'Alinea el upstream remoto con la rama actual antes de continuar.',
|
|
105
|
+
next_action: {
|
|
106
|
+
kind: 'run_command',
|
|
107
|
+
message: 'Reconfigura el upstream remoto de la rama actual.',
|
|
108
|
+
command: 'git branch --unset-upstream && git push --set-upstream origin <branch>',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
case 'GITFLOW_PROTECTED_BRANCH':
|
|
112
|
+
case 'GITFLOW_PROTECTED_BRANCH_CONTEXT':
|
|
113
|
+
return {
|
|
114
|
+
reason_code: 'GITFLOW_PROTECTED_BRANCH_CONTEXT',
|
|
115
|
+
instruction: 'Sal de la rama protegida y mueve el trabajo a feature/* o refactor/*.',
|
|
116
|
+
next_action: {
|
|
117
|
+
kind: 'run_command',
|
|
118
|
+
message: 'Crea una rama válida de trabajo antes de seguir.',
|
|
119
|
+
command: 'git checkout -b feature/<descripcion-kebab-case>',
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
case 'GITFLOW_BRANCH_NAMING_INVALID':
|
|
123
|
+
case 'GITFLOW_BRANCH_NAMING_INVALID_CONTEXT':
|
|
124
|
+
return {
|
|
125
|
+
reason_code: 'GITFLOW_BRANCH_NAMING_INVALID_CONTEXT',
|
|
126
|
+
instruction:
|
|
127
|
+
'Renombra o recrea la rama actual con un prefijo GitFlow válido antes de continuar.',
|
|
128
|
+
next_action: {
|
|
129
|
+
kind: 'run_command',
|
|
130
|
+
message: 'Crea una rama válida y mueve el trabajo a esa rama antes de seguir.',
|
|
131
|
+
command: 'git checkout -b feature/<descripcion-kebab-case>',
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
case 'TRACKING_CANONICAL_SOURCE_CONFLICT':
|
|
135
|
+
return {
|
|
136
|
+
reason_code: 'TRACKING_CANONICAL_SOURCE_CONFLICT',
|
|
137
|
+
instruction:
|
|
138
|
+
'Alinea AGENTS.md y los README canónicos para que todos apunten al mismo MD de seguimiento.',
|
|
139
|
+
next_action: {
|
|
140
|
+
kind: 'info',
|
|
141
|
+
message:
|
|
142
|
+
'Deja una única fuente canónica de tracking y elimina referencias legacy o contradictorias.',
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
case 'TRACKING_CANONICAL_FILE_MISSING':
|
|
146
|
+
return {
|
|
147
|
+
reason_code: 'TRACKING_CANONICAL_FILE_MISSING',
|
|
148
|
+
instruction:
|
|
149
|
+
'Crea o restaura el MD de seguimiento canónico declarado por el repo antes de continuar.',
|
|
150
|
+
next_action: {
|
|
151
|
+
kind: 'info',
|
|
152
|
+
message:
|
|
153
|
+
'Restaura el archivo canónico de tracking y vuelve a validar governance.',
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
case 'TRACKING_CANONICAL_IN_PROGRESS_INVALID':
|
|
157
|
+
return {
|
|
158
|
+
reason_code: 'TRACKING_CANONICAL_IN_PROGRESS_INVALID',
|
|
159
|
+
instruction:
|
|
160
|
+
'El tracking canónico debe tener exactamente una tarea o fase en construcción.',
|
|
161
|
+
next_action: {
|
|
162
|
+
kind: 'info',
|
|
163
|
+
message:
|
|
164
|
+
'Corrige el MD canónico para dejar exactamente una `🚧` antes de continuar.',
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
case 'POLICY_STAGE_NOT_STRICT':
|
|
168
|
+
return {
|
|
169
|
+
reason_code: 'POLICY_STAGE_NOT_STRICT',
|
|
170
|
+
instruction: 'Reconcilia policy/skills en modo estricto antes de seguir.',
|
|
171
|
+
next_action: {
|
|
172
|
+
kind: 'run_command',
|
|
173
|
+
message: 'Converge policy-as-code y revalida el stage actual.',
|
|
174
|
+
command: buildGovernancePolicyReconcileCommand(stage),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
case 'SKILLS_CONTRACT_SURFACE_INCOMPLETE':
|
|
178
|
+
case 'EVIDENCE_SKILLS_CONTRACT_INCOMPLETE':
|
|
179
|
+
return {
|
|
180
|
+
reason_code: 'SKILLS_CONTRACT_SURFACE_INCOMPLETE',
|
|
181
|
+
instruction: 'Materializa el lock/sources de skills antes de dar governance efectiva.',
|
|
182
|
+
next_action: {
|
|
183
|
+
kind: 'run_command',
|
|
184
|
+
message: 'Reconcilia policy/skills y vuelve a validar governance.',
|
|
185
|
+
command: buildGovernancePolicyReconcileCommand(stage),
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
case 'ADAPTER_WIRING_MISSING':
|
|
189
|
+
return {
|
|
190
|
+
reason_code: 'ADAPTER_WIRING_MISSING',
|
|
191
|
+
instruction: 'Instala el adaptador canónico si quieres wiring explícito de IDE/MCP.',
|
|
192
|
+
next_action: {
|
|
193
|
+
kind: 'run_command',
|
|
194
|
+
message: 'Genera `.pumuki/adapter.json` desde la instalación canónica.',
|
|
195
|
+
command: 'npx --yes --package pumuki@latest pumuki install --with-mcp',
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
case 'EVIDENCE_STALE':
|
|
199
|
+
case 'EVIDENCE_BRANCH_MISMATCH':
|
|
200
|
+
case 'EVIDENCE_SNAPSHOT_WARN':
|
|
201
|
+
return {
|
|
202
|
+
reason_code: params.code === 'EVIDENCE_SNAPSHOT_WARN' ? 'EVIDENCE_SNAPSHOT_WARN' : 'EVIDENCE_STALE',
|
|
203
|
+
instruction: 'Refresca evidencia y revisa hallazgos WARN antes de continuar.',
|
|
204
|
+
next_action: {
|
|
205
|
+
kind: 'run_command',
|
|
206
|
+
message: 'Revalida el stage actual y revisa findings WARN.',
|
|
207
|
+
command: validateCommand,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
case 'ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH':
|
|
211
|
+
case 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES':
|
|
212
|
+
return {
|
|
213
|
+
reason_code: 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES',
|
|
214
|
+
instruction: 'Reconcilia policy/skills en modo estricto y revalida el stage actual.',
|
|
215
|
+
next_action: {
|
|
216
|
+
kind: 'run_command',
|
|
217
|
+
message: 'Converge policy-as-code y vuelve a validar cobertura de active_rule_ids para reglas activas.',
|
|
218
|
+
command: buildGovernancePolicyReconcileCommand(stage),
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
case 'EVIDENCE_PLATFORM_SKILLS_SCOPE_INCOMPLETE':
|
|
222
|
+
case 'EVIDENCE_PLATFORM_SKILLS_BUNDLES_MISSING':
|
|
223
|
+
return {
|
|
224
|
+
reason_code: params.code,
|
|
225
|
+
instruction: 'Completa cobertura de skills por plataforma y vuelve a validar.',
|
|
226
|
+
next_action: {
|
|
227
|
+
kind: 'run_command',
|
|
228
|
+
message: 'Completa bundles/prefijos de skills requeridos y revalida el stage actual.',
|
|
229
|
+
command: validateCommand,
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
case 'EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING':
|
|
233
|
+
case 'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE':
|
|
234
|
+
return {
|
|
235
|
+
reason_code: params.code,
|
|
236
|
+
instruction: 'Reconcilia policy/skills en modo estricto para enforcement crítico y revalida.',
|
|
237
|
+
next_action: {
|
|
238
|
+
kind: 'run_command',
|
|
239
|
+
message: 'Materializa reglas críticas de plataforma y vuelve a validar el stage actual.',
|
|
240
|
+
command: buildGovernancePolicyReconcileCommand(stage),
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
case 'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT':
|
|
244
|
+
case 'EVIDENCE_PREWRITE_WORKTREE_WARN':
|
|
245
|
+
return {
|
|
246
|
+
reason_code: params.code,
|
|
247
|
+
instruction: 'Particiona el worktree en slices atómicos antes de continuar.',
|
|
248
|
+
next_action: {
|
|
249
|
+
kind: 'run_command',
|
|
250
|
+
message: 'Reduce el worktree pendiente y revalida el stage actual.',
|
|
251
|
+
command: `git status --short && git add -p && ${validateCommand}`,
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
case 'SKILLS_SKILLS_FRONTEND_NO_SOLID_VIOLATIONS':
|
|
255
|
+
return {
|
|
256
|
+
reason_code: 'SKILLS_SKILLS_FRONTEND_NO_SOLID_VIOLATIONS',
|
|
257
|
+
instruction: 'Aplica refactor incremental por componente/hook y vuelve a validar.',
|
|
258
|
+
next_action: {
|
|
259
|
+
kind: 'info',
|
|
260
|
+
message: 'Aplica refactor incremental: extrae 1 componente/hook por commit y vuelve a ejecutar el gate.',
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
case 'GOVERNANCE_ATTENTION':
|
|
264
|
+
return {
|
|
265
|
+
reason_code: 'GOVERNANCE_ATTENTION',
|
|
266
|
+
instruction: 'Revisa governance truth y corrige el primer hueco del contrato antes de continuar.',
|
|
267
|
+
next_action: {
|
|
268
|
+
kind: 'info',
|
|
269
|
+
message: 'Revisa governance truth, corrige el primer gap visible y vuelve a validar.',
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
default:
|
|
273
|
+
return params.fallback ?? buildFallbackAction(params.code);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
@@ -17,6 +17,14 @@ export const REMEDIATION_HINT_BY_CODE: Readonly<Record<string, string>> = {
|
|
|
17
17
|
EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES:
|
|
18
18
|
'Reconcilia policy/skills y revalida PRE_WRITE: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
|
|
19
19
|
GITFLOW_PROTECTED_BRANCH: 'Trabaja en feature/* y evita ramas protegidas.',
|
|
20
|
+
GITFLOW_BRANCH_NAMING_INVALID:
|
|
21
|
+
'Renombra o recrea la rama con un prefijo GitFlow válido (feature/*, bugfix/*, hotfix/*, release/*, chore/*, refactor/* o docs/*).',
|
|
22
|
+
TRACKING_CANONICAL_SOURCE_CONFLICT:
|
|
23
|
+
'Alinea AGENTS.md y los README canónicos para que todos apunten al mismo MD de seguimiento.',
|
|
24
|
+
TRACKING_CANONICAL_FILE_MISSING:
|
|
25
|
+
'Crea o restaura el archivo canónico de tracking declarado por el repo.',
|
|
26
|
+
TRACKING_CANONICAL_IN_PROGRESS_INVALID:
|
|
27
|
+
'Deja exactamente una tarea o fase `🚧` en el MD canónico de seguimiento antes de continuar.',
|
|
20
28
|
EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT:
|
|
21
29
|
'Reduce archivos staged/unstaged por debajo del umbral (o ajusta PUMUKI_PREWRITE_WORKTREE_*); divide el trabajo en commits más pequeños.',
|
|
22
30
|
EVIDENCE_PREWRITE_WORKTREE_WARN:
|
|
@@ -9,7 +9,6 @@ export { parseNameStatus } from './gitDiffUtils';
|
|
|
9
9
|
export interface IGitService {
|
|
10
10
|
runGit(args: ReadonlyArray<string>, cwd?: string): string;
|
|
11
11
|
getStagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
|
|
12
|
-
getUnstagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
|
|
13
12
|
getRepoFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
|
|
14
13
|
getRepoAndStagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
|
|
15
14
|
getStagedAndUnstagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact>;
|
|
@@ -45,30 +44,6 @@ export class GitService implements IGitService {
|
|
|
45
44
|
);
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
getUnstagedFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact> {
|
|
49
|
-
const nameStatus = this.runGit(['diff', '--name-status']);
|
|
50
|
-
const changes = parseNameStatus(nameStatus).filter((change) =>
|
|
51
|
-
hasAllowedExtension(change.path, extensions)
|
|
52
|
-
);
|
|
53
|
-
const untrackedPaths = this.runGit(['ls-files', '--others', '--exclude-standard'])
|
|
54
|
-
.split('\n')
|
|
55
|
-
.map((line) => line.trim())
|
|
56
|
-
.filter((line) => line.length > 0)
|
|
57
|
-
.filter((path) => hasAllowedExtension(path, extensions));
|
|
58
|
-
const unstagedPaths = new Set(changes.map((change) => change.path));
|
|
59
|
-
const untrackedChanges = untrackedPaths
|
|
60
|
-
.filter((path) => !unstagedPaths.has(path))
|
|
61
|
-
.map((path) => ({
|
|
62
|
-
path,
|
|
63
|
-
changeType: 'added' as const,
|
|
64
|
-
}));
|
|
65
|
-
const mergedChanges = [...changes, ...untrackedChanges];
|
|
66
|
-
const repoRoot = this.resolveRepoRoot();
|
|
67
|
-
return buildFactsFromChanges(mergedChanges, 'git:unstaged', (filePath) =>
|
|
68
|
-
this.readWorkingTreeFile(repoRoot, filePath)
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
47
|
getRepoFacts(extensions: ReadonlyArray<string>): ReadonlyArray<Fact> {
|
|
73
48
|
const trackedFiles = this.runGit(['ls-files'])
|
|
74
49
|
.split('\n')
|
|
@@ -6,6 +6,10 @@ const AI_GATE_STAGES = new Set<AiGateStage>(['PRE_WRITE', 'PRE_COMMIT', 'PRE_PUS
|
|
|
6
6
|
|
|
7
7
|
const REPO_POLICY_CODES = new Set<string>([
|
|
8
8
|
'GITFLOW_PROTECTED_BRANCH',
|
|
9
|
+
'GITFLOW_BRANCH_NAMING_INVALID',
|
|
10
|
+
'TRACKING_CANONICAL_SOURCE_CONFLICT',
|
|
11
|
+
'TRACKING_CANONICAL_FILE_MISSING',
|
|
12
|
+
'TRACKING_CANONICAL_IN_PROGRESS_INVALID',
|
|
9
13
|
'EVIDENCE_PREWRITE_WORKTREE_OVER_LIMIT',
|
|
10
14
|
'EVIDENCE_PREWRITE_WORKTREE_WARN',
|
|
11
15
|
]);
|
|
@@ -1372,7 +1372,15 @@ export async function runPlatformGate(params: {
|
|
|
1372
1372
|
|
|
1373
1373
|
if (gateOutcome === 'BLOCK') {
|
|
1374
1374
|
if (params.silent !== true) {
|
|
1375
|
-
|
|
1375
|
+
const renderStage =
|
|
1376
|
+
params.policy.stage === 'PRE_PUSH'
|
|
1377
|
+
? 'PRE_PUSH'
|
|
1378
|
+
: params.policy.stage === 'CI'
|
|
1379
|
+
? 'CI'
|
|
1380
|
+
: 'PRE_COMMIT';
|
|
1381
|
+
dependencies.printGateFindings(findingsWithWaiver, {
|
|
1382
|
+
stage: renderStage,
|
|
1383
|
+
});
|
|
1376
1384
|
}
|
|
1377
1385
|
return 1;
|
|
1378
1386
|
}
|
|
@@ -19,10 +19,6 @@ export type GateScope =
|
|
|
19
19
|
kind: 'workingTree';
|
|
20
20
|
extensions?: string[];
|
|
21
21
|
}
|
|
22
|
-
| {
|
|
23
|
-
kind: 'unstaged';
|
|
24
|
-
extensions?: string[];
|
|
25
|
-
}
|
|
26
22
|
| {
|
|
27
23
|
kind: 'range';
|
|
28
24
|
fromRef: string;
|
|
@@ -70,9 +66,6 @@ export const resolveFactsForGateScope = async (params: {
|
|
|
70
66
|
if (params.scope.kind === 'workingTree') {
|
|
71
67
|
return params.git.getStagedAndUnstagedFacts(extensions);
|
|
72
68
|
}
|
|
73
|
-
if (params.scope.kind === 'unstaged') {
|
|
74
|
-
return params.git.getUnstagedFacts(extensions);
|
|
75
|
-
}
|
|
76
69
|
|
|
77
70
|
return getFactsForCommitRange({
|
|
78
71
|
fromRef: params.scope.fromRef,
|
|
@@ -1,27 +1,5 @@
|
|
|
1
1
|
import type { Finding } from '../../core/gate/Finding';
|
|
2
|
-
|
|
3
|
-
const BLOCK_NEXT_ACTION_BY_CODE: Readonly<Record<string, string>> = {
|
|
4
|
-
SDD_SESSION_MISSING:
|
|
5
|
-
'npx --yes --package pumuki@latest pumuki sdd session --open --change=<id>',
|
|
6
|
-
SDD_SESSION_INVALID:
|
|
7
|
-
'npx --yes --package pumuki@latest pumuki sdd session --refresh --ttl-minutes=90',
|
|
8
|
-
PRE_PUSH_UPSTREAM_MISSING:
|
|
9
|
-
'git push --set-upstream origin <branch>',
|
|
10
|
-
PRE_PUSH_UPSTREAM_MISALIGNED:
|
|
11
|
-
'git branch --unset-upstream && git push --set-upstream origin <branch>',
|
|
12
|
-
EVIDENCE_STALE:
|
|
13
|
-
'npx --yes --package pumuki@latest pumuki-pre-commit',
|
|
14
|
-
EVIDENCE_BRANCH_MISMATCH:
|
|
15
|
-
'npx --yes --package pumuki@latest pumuki-pre-commit',
|
|
16
|
-
GIT_ATOMICITY_TOO_MANY_FILES:
|
|
17
|
-
'git restore --staged . && separa cambios en commits más pequeños',
|
|
18
|
-
GIT_ATOMICITY_TOO_MANY_SCOPES:
|
|
19
|
-
'Revisa scope_files del bloqueo y aplica: git restore --staged . && git add <scope>/ && git commit -m "<tipo>: <scope>"',
|
|
20
|
-
ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES_HIGH:
|
|
21
|
-
'Reconcilia policy/skills y reintenta PRE_COMMIT: npx --yes --package pumuki@latest pumuki policy reconcile --strict --json && npx --yes --package pumuki@latest pumuki-pre-commit',
|
|
22
|
-
SKILLS_SKILLS_FRONTEND_NO_SOLID_VIOLATIONS:
|
|
23
|
-
'Aplica refactor incremental: extrae 1 componente/hook por commit y vuelve a ejecutar PRE_COMMIT.',
|
|
24
|
-
};
|
|
2
|
+
import { resolveGovernanceCatalogAction } from '../gate/governanceActionCatalog';
|
|
25
3
|
|
|
26
4
|
const severityWeight = (severity: string): number => {
|
|
27
5
|
switch (severity.toUpperCase()) {
|
|
@@ -94,18 +72,49 @@ const formatFinding = (finding: Finding): string => {
|
|
|
94
72
|
return `[${severity}] ${finding.ruleId}: ${finding.message} -> ${location}`;
|
|
95
73
|
};
|
|
96
74
|
|
|
97
|
-
|
|
75
|
+
const resolveAtomicityFallback = (code: string): string | null => {
|
|
76
|
+
switch (code) {
|
|
77
|
+
case 'GIT_ATOMICITY_TOO_MANY_FILES':
|
|
78
|
+
return 'git restore --staged . && separa cambios en commits más pequeños';
|
|
79
|
+
case 'GIT_ATOMICITY_TOO_MANY_SCOPES':
|
|
80
|
+
return 'Revisa scope_files del bloqueo y aplica: git restore --staged . && git add <scope>/ && git commit -m "<tipo>: <scope>"';
|
|
81
|
+
default:
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const printGateFindings = (
|
|
87
|
+
findings: ReadonlyArray<Finding>,
|
|
88
|
+
params?: { stage?: 'PRE_COMMIT' | 'PRE_PUSH' | 'CI' }
|
|
89
|
+
): void => {
|
|
98
90
|
if (findings.length === 0) {
|
|
99
91
|
return;
|
|
100
92
|
}
|
|
101
93
|
const primary = resolvePrimaryFinding(findings);
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
94
|
+
const action = resolveGovernanceCatalogAction({
|
|
95
|
+
code: primary.code,
|
|
96
|
+
stage: params?.stage ?? 'PRE_COMMIT',
|
|
97
|
+
fallback: {
|
|
98
|
+
reason_code: primary.code,
|
|
99
|
+
instruction: 'Corrige el bloqueante primario y vuelve a ejecutar la validación del stage actual.',
|
|
100
|
+
next_action: {
|
|
101
|
+
kind: 'info',
|
|
102
|
+
message:
|
|
103
|
+
resolveAtomicityFallback(primary.code)
|
|
104
|
+
?? 'Corrige el bloqueante primario y vuelve a ejecutar el mismo comando.',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
const nextAction = action.next_action.command ?? action.next_action.message;
|
|
105
109
|
process.stdout.write(
|
|
106
110
|
`[pumuki][block-summary] primary=${primary.code} severity=${primary.severity.toUpperCase()} rule=${primary.ruleId}\n`
|
|
107
111
|
);
|
|
112
|
+
process.stdout.write(`[pumuki][block-summary] reason_code=${action.reason_code}\n`);
|
|
113
|
+
process.stdout.write(`[pumuki][block-summary] instruction=${action.instruction}\n`);
|
|
108
114
|
process.stdout.write(`[pumuki][block-summary] next_action=${nextAction}\n`);
|
|
115
|
+
if (action.next_action.command) {
|
|
116
|
+
process.stdout.write(`[pumuki][block-summary] command=${action.next_action.command}\n`);
|
|
117
|
+
}
|
|
109
118
|
for (const finding of findings) {
|
|
110
119
|
process.stdout.write(`${formatFinding(finding)}\n`);
|
|
111
120
|
}
|
|
@@ -2,6 +2,7 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
4
|
import { LifecycleGitService, type ILifecycleGitService } from './gitService';
|
|
5
|
+
import { writeLifecycleBootstrapManifest } from './bootstrapManifest';
|
|
5
6
|
|
|
6
7
|
export type AdapterAgent = string;
|
|
7
8
|
|
|
@@ -11,6 +12,10 @@ export type LifecycleAdapterInstallResult = {
|
|
|
11
12
|
dryRun: boolean;
|
|
12
13
|
written: boolean;
|
|
13
14
|
changedFiles: ReadonlyArray<string>;
|
|
15
|
+
bootstrapManifest: {
|
|
16
|
+
path: string;
|
|
17
|
+
changed: boolean;
|
|
18
|
+
};
|
|
14
19
|
};
|
|
15
20
|
|
|
16
21
|
type AdapterTemplate = {
|
|
@@ -154,11 +159,30 @@ export const runLifecycleAdapterInstall = (params: {
|
|
|
154
159
|
writeFileSync(absolutePath, nextContents, 'utf8');
|
|
155
160
|
}
|
|
156
161
|
|
|
162
|
+
let bootstrapManifest = {
|
|
163
|
+
path: join(repoRoot, '.pumuki', 'bootstrap-manifest.json'),
|
|
164
|
+
changed: false,
|
|
165
|
+
};
|
|
166
|
+
if (!dryRun) {
|
|
167
|
+
const manifestWrite = writeLifecycleBootstrapManifest({
|
|
168
|
+
git,
|
|
169
|
+
repoRoot,
|
|
170
|
+
});
|
|
171
|
+
bootstrapManifest = {
|
|
172
|
+
path: manifestWrite.path,
|
|
173
|
+
changed: manifestWrite.changed,
|
|
174
|
+
};
|
|
175
|
+
if (manifestWrite.changed) {
|
|
176
|
+
changedFiles.push('.pumuki/bootstrap-manifest.json');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
157
180
|
return {
|
|
158
181
|
repoRoot,
|
|
159
182
|
agent: params.agent,
|
|
160
183
|
dryRun,
|
|
161
184
|
written: !dryRun,
|
|
162
185
|
changedFiles,
|
|
186
|
+
bootstrapManifest,
|
|
163
187
|
};
|
|
164
188
|
};
|