pumuki 6.3.38 → 6.3.39
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 +1 -1
- package/docs/RELEASE_NOTES.md +26 -0
- package/docs/registro-maestro-de-seguimiento.md +3 -3
- package/docs/seguimiento-activo-pumuki-saas-supermercados.md +54 -2
- package/integrations/git/getCommitRangeFacts.ts +35 -5
- package/integrations/git/gitAtomicity.ts +1 -1
- package/integrations/git/runPlatformGate.ts +79 -0
- package/integrations/lifecycle/adapter.templates.json +20 -20
- package/integrations/lifecycle/cli.ts +6 -0
- package/integrations/lifecycle/doctor.ts +9 -1
- package/integrations/lifecycle/hookManager.ts +85 -1
- package/integrations/lifecycle/status.ts +6 -1
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.39
|
package/docs/RELEASE_NOTES.md
CHANGED
|
@@ -5,6 +5,32 @@ Detailed commit history remains available through Git history (`git log` / `git
|
|
|
5
5
|
|
|
6
6
|
## 2026-03 (enterprise hardening updates)
|
|
7
7
|
|
|
8
|
+
### 2026-03-04 (v6.3.39)
|
|
9
|
+
|
|
10
|
+
- Adapter/runtime bootstrap hardening:
|
|
11
|
+
- adapter-generated hooks/CI templates now use `npx --yes --package pumuki@latest ...` for deterministic command resolution in consumer repos.
|
|
12
|
+
- Git-range robustness:
|
|
13
|
+
- commit-range facts now guard unresolved refs (`rev-parse --verify`) and avoid ambiguous failures on repos without `HEAD`.
|
|
14
|
+
- Cross-platform critical enforcement:
|
|
15
|
+
- gate now blocks when a detected platform does not have critical (`CRITICAL/ERROR`) skills rules active/evaluated.
|
|
16
|
+
- finding id: `governance.skills.cross-platform-critical.incomplete`.
|
|
17
|
+
- Git atomicity by default:
|
|
18
|
+
- atomicity guard is enabled by default in core gate flow (`PRE_COMMIT/PRE_PUSH/CI`).
|
|
19
|
+
- keeps env/config overrides for enterprise tuning without patching source.
|
|
20
|
+
- Versioned hooks diagnostics hardening (`core.hooksPath`):
|
|
21
|
+
- lifecycle hook resolution now includes fallback to local `.git/config` (`core.hooksPath`) when `git rev-parse --git-path hooks` is unavailable.
|
|
22
|
+
- `status/doctor` now expose effective hook path metadata (`hooksDirectory`, `hooksDirectoryResolution`) and print it in human-readable mode.
|
|
23
|
+
- Validation hardening:
|
|
24
|
+
- `test:stage-gates` stabilized and green with current contracts (`1020 pass / 0 fail / 4 skip`).
|
|
25
|
+
- fixtures aligned to evidence v2.1 (`evidence_chain`, `evidence.source`) and architecture guardrail overrides updated for `integrations/lifecycle/cli.ts`.
|
|
26
|
+
- Traceability:
|
|
27
|
+
- commits: `104fc92`, `2f175ec`, `da7b073`, `2c40a4c`, `b124599`, `2aeb435`
|
|
28
|
+
- Consumer quick verification:
|
|
29
|
+
- `npx --yes --package pumuki@latest pumuki status --json`
|
|
30
|
+
- `npx --yes --package pumuki@latest pumuki doctor --json`
|
|
31
|
+
- `npm run -s typecheck`
|
|
32
|
+
- `npm run -s test:stage-gates`
|
|
33
|
+
|
|
8
34
|
### 2026-03-04 (v6.3.38)
|
|
9
35
|
|
|
10
36
|
- Blocked notification UX hardening for macOS:
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
## Estado actual
|
|
8
8
|
- Plan activo: `docs/seguimiento-activo-pumuki-saas-supermercados.md`
|
|
9
9
|
- Estado del plan: EJECUCION
|
|
10
|
-
- Última task cerrada (`✅`):
|
|
11
|
-
- Task activa (`🚧`):
|
|
12
|
-
- Nuevos pendientes añadidos (`⏳`):
|
|
10
|
+
- Última task cerrada (`✅`): PUMUKI-019 (hardening de diagnóstico para hooks versionados `core.hooksPath` con fallback robusto + cobertura de no-regresión).
|
|
11
|
+
- Task activa (`🚧`): PUMUKI-020 (publicación del siguiente corte tras cierre de PUMUKI-019).
|
|
12
|
+
- Nuevos pendientes añadidos (`⏳`): ninguno en este bloque inmediato.
|
|
13
13
|
|
|
14
14
|
## Historial resumido
|
|
15
15
|
- Bloque RuralGO cerrado: `docs/seguimiento-completo-validacion-ruralgo-03-03-2026.md`.
|
|
@@ -73,5 +73,57 @@
|
|
|
73
73
|
- ✅ Ejecutar suite de tests de regresión afectada.
|
|
74
74
|
- Evidencia (2026-03-04): `npx --yes tsx@4.21.0 --test scripts/__tests__/framework-menu-system-notifications.test.ts integrations/git/__tests__/stageRunners.test.ts integrations/lifecycle/__tests__/lifecycle.test.ts` -> `44 pass / 0 fail`.
|
|
75
75
|
- ✅ Actualizar `CHANGELOG.md` y `docs/RELEASE_NOTES.md` con fixes reales.
|
|
76
|
-
- Evidencia (2026-03-04): se documenta en `
|
|
77
|
-
-
|
|
76
|
+
- Evidencia (2026-03-04): se documenta en `6.3.38` (CHANGELOG) y en `v6.3.38` (RELEASE_NOTES) el paquete de mejoras `PUMUKI-011` + baseline test alignment.
|
|
77
|
+
- ✅ Publicar versión cuando las tareas en construcción/pending críticas estén cerradas.
|
|
78
|
+
- Evidencia (2026-03-04): `npm publish --access public` => `+ pumuki@6.3.38` y verificación remota `npm view pumuki version` => `6.3.38`.
|
|
79
|
+
|
|
80
|
+
## Fase 4. Post-release
|
|
81
|
+
|
|
82
|
+
- ✅ Monitorizar feedback de repos consumidores y registrar nuevos hallazgos canónicos.
|
|
83
|
+
- Evidencia (2026-03-04): se activa nuevo frente real en consumer repo con backlog dedicado en `/Users/juancarlosmerlosalbarracin/Developer/Projects/SAAS:APP_SUPERMERCADOS/docs/pumuki/PUMUKI_BUGS_MEJORAS.md`.
|
|
84
|
+
- ✅ Priorizar nuevos bugs/mejoras y abrir siguiente ciclo de implementación.
|
|
85
|
+
- Evidencia (2026-03-04): ciclo técnico arrancado en `ast-intelligence-hooks` con ejecución sobre bugs reales reportados desde SAAS.
|
|
86
|
+
|
|
87
|
+
## Fase 4.1 Ciclo técnico actual (core Pumuki)
|
|
88
|
+
|
|
89
|
+
- ✅ PUMUKI-012: Endurecer comandos de adapter templates para hooks/CI sin dependencia frágil de `./node_modules/.bin`.
|
|
90
|
+
- Fix: `integrations/lifecycle/adapter.templates.json` ahora usa `npx --yes --package pumuki@latest ...` en `pre_write/pre_commit/pre_push/ci`.
|
|
91
|
+
- Test: `integrations/lifecycle/__tests__/adapter.test.ts`, `integrations/lifecycle/__tests__/doctor.test.ts`, `integrations/lifecycle/__tests__/cli.test.ts`.
|
|
92
|
+
- ✅ PUMUKI-013: Blindar resolución de rango Git cuando `HEAD`/refs no son resolubles (repos sin commits o refs ambiguas).
|
|
93
|
+
- Fix: `integrations/git/getCommitRangeFacts.ts` añade guardas `rev-parse --verify` + fallback seguro sin crash.
|
|
94
|
+
- Test: `integrations/git/__tests__/getCommitRangeFacts.test.ts` (nuevo caso repo sin commits) y `integrations/git/__tests__/runPlatformGateFacts.test.ts`.
|
|
95
|
+
- ✅ PUMUKI-014: Enforcement crítico transversal por plataforma (sin huecos entre skills activas y evaluación real).
|
|
96
|
+
- Fix: `integrations/git/runPlatformGate.ts` incorpora `governance.skills.cross-platform-critical.incomplete` y bloquea cuando una plataforma detectada no tiene reglas críticas (`CRITICAL/ERROR`) activas/evaluadas.
|
|
97
|
+
- Test: `integrations/git/__tests__/runPlatformGate.test.ts` añade casos de bloqueo/allow para cobertura crítica multi-plataforma.
|
|
98
|
+
- ✅ PUMUKI-015: Ejecutar validación extendida de no-regresión (suite stage-gates focal + smoke de hooks) y cerrar trazabilidad final de este bloque crítico.
|
|
99
|
+
- Evidencia (2026-03-04): `npm run -s test:stage-gates` -> `1018 pass / 0 fail / 4 skip`.
|
|
100
|
+
- Fixes incluidos para estabilizar la suite:
|
|
101
|
+
- `integrations/lifecycle/__tests__/saasIngestionBuilder.test.ts` (fixture de evidencia v2.1 con `evidence_chain` válido).
|
|
102
|
+
- `scripts/__tests__/framework-menu-consumer-preflight.test.ts` (contrato `evidence.source` completo en fixtures).
|
|
103
|
+
- `scripts/__tests__/architecture-file-size-guardrails.test.ts` (override explícito para `integrations/lifecycle/cli.ts` en límites de líneas/imports).
|
|
104
|
+
- ✅ PUMUKI-016: Preparar release notes del siguiente corte con trazabilidad de commits y validación ejecutada.
|
|
105
|
+
- Evidencia (2026-03-04):
|
|
106
|
+
- `CHANGELOG.md` actualizado en `[Unreleased]` con `adapter hooks`, `commit-range` y `cross-platform critical enforcement`.
|
|
107
|
+
- `docs/RELEASE_NOTES.md` actualizado con bloque `next cut candidate, post v6.3.38`.
|
|
108
|
+
- ✅ PUMUKI-017: Ejecutar siguiente bug/mejora del backlog SAAS (`PUMUKI-002`: enforcement de atomicidad Git por defecto) con RED->GREEN->REFACTOR.
|
|
109
|
+
- Fix:
|
|
110
|
+
- `integrations/git/gitAtomicity.ts` activa atomicidad por defecto (`enabled: true`) manteniendo override por env/config.
|
|
111
|
+
- `integrations/git/__tests__/gitAtomicity.test.ts` actualiza contrato base a enforcement activo por defecto.
|
|
112
|
+
- Evidencia (2026-03-04):
|
|
113
|
+
- `npx --yes tsx@4.21.0 --test integrations/git/__tests__/gitAtomicity.test.ts` -> `3 pass / 0 fail`.
|
|
114
|
+
- `npx --yes tsx@4.21.0 --test integrations/git/__tests__/stageRunners.test.ts` -> `21 pass / 0 fail`.
|
|
115
|
+
- `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/cli.test.ts` -> `29 pass / 0 fail`.
|
|
116
|
+
- ✅ PUMUKI-018: Preparar cierre de corte/publicación tras validación acumulada de PUMUKI-012..017.
|
|
117
|
+
- Evidencia (2026-03-04):
|
|
118
|
+
- `npm run -s test:stage-gates` -> `1018 pass / 0 fail / 4 skip` tras ajuste de regresión en `integrations/git/__tests__/hookGateSummary.test.ts`.
|
|
119
|
+
- Smoke complementario ya validado dentro del bloque: `gitAtomicity`, `stageRunners`, `lifecycle/cli`, `typecheck`.
|
|
120
|
+
- ✅ PUMUKI-019: Ejecutar siguiente bug/mejora del backlog SAAS de prioridad media (`PUMUKI-004`: hooks versionados `core.hooksPath`).
|
|
121
|
+
- Fix:
|
|
122
|
+
- `integrations/lifecycle/hookManager.ts` añade resolución robusta de hooks con fallback a `.git/config` (`core.hooksPath`) cuando no está disponible `git rev-parse --git-path hooks`.
|
|
123
|
+
- `integrations/lifecycle/status.ts` y `integrations/lifecycle/doctor.ts` exponen metadatos de ruta efectiva (`hooksDirectory`, `hooksDirectoryResolution`).
|
|
124
|
+
- `integrations/lifecycle/cli.ts` imprime ruta efectiva de hooks en `status` y `doctor` modo texto para diagnóstico humano.
|
|
125
|
+
- Evidencia (2026-03-04):
|
|
126
|
+
- `npx --yes tsx@4.21.0 --test integrations/lifecycle/__tests__/hookManager.test.ts integrations/lifecycle/__tests__/status.test.ts integrations/lifecycle/__tests__/doctor.test.ts integrations/lifecycle/__tests__/cli.test.ts` -> `50 pass / 0 fail`.
|
|
127
|
+
- `npm run -s test:stage-gates` -> `1020 pass / 0 fail / 4 skip`.
|
|
128
|
+
- `npm run -s typecheck` -> `PASS`.
|
|
129
|
+
- 🚧 PUMUKI-020: Preparar publicación del siguiente corte cuando PUMUKI-019 quede cerrada sin regresiones.
|
|
@@ -5,6 +5,24 @@ import { GitService } from './GitService';
|
|
|
5
5
|
|
|
6
6
|
const defaultGit: IGitService = new GitService();
|
|
7
7
|
|
|
8
|
+
const isResolvableRef = (git: IGitService, ref: string): boolean => {
|
|
9
|
+
try {
|
|
10
|
+
git.runGit(['rev-parse', '--verify', ref]);
|
|
11
|
+
return true;
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const isRangeResolutionError = (error: unknown): boolean => {
|
|
18
|
+
if (!(error instanceof Error)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return /unknown revision|bad revision|ambiguous argument|fatal:\s+invalid object name/i.test(
|
|
22
|
+
error.message
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
8
26
|
export async function getFactsForCommitRange(params: {
|
|
9
27
|
fromRef: string;
|
|
10
28
|
toRef: string;
|
|
@@ -12,11 +30,23 @@ export async function getFactsForCommitRange(params: {
|
|
|
12
30
|
git?: IGitService;
|
|
13
31
|
}): Promise<ReadonlyArray<Fact>> {
|
|
14
32
|
const git = params.git ?? defaultGit;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
if (!isResolvableRef(git, params.fromRef) || !isResolvableRef(git, params.toRef)) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let diffOutput = '';
|
|
38
|
+
try {
|
|
39
|
+
diffOutput = git.runGit([
|
|
40
|
+
'diff',
|
|
41
|
+
'--name-status',
|
|
42
|
+
`${params.fromRef}..${params.toRef}`,
|
|
43
|
+
]);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (isRangeResolutionError(error)) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
20
50
|
const changes = parseNameStatus(diffOutput).filter((change) =>
|
|
21
51
|
hasAllowedExtension(change.path, params.extensions)
|
|
22
52
|
);
|
|
@@ -32,7 +32,7 @@ const DEFAULT_COMMIT_PATTERN =
|
|
|
32
32
|
'^(feat|fix|chore|refactor|docs|test|perf|build|ci|revert)(\\([^)]+\\))?:\\s.+$';
|
|
33
33
|
|
|
34
34
|
const defaultConfig: GitAtomicityConfig = {
|
|
35
|
-
enabled:
|
|
35
|
+
enabled: true,
|
|
36
36
|
maxFiles: 25,
|
|
37
37
|
maxScopes: 2,
|
|
38
38
|
enforceCommitMessagePattern: true,
|
|
@@ -235,6 +235,10 @@ const PLATFORM_REQUIRED_SKILLS_BUNDLES: Record<
|
|
|
235
235
|
frontend: ['frontend-guidelines'],
|
|
236
236
|
};
|
|
237
237
|
|
|
238
|
+
const isCriticalProfileSeverity = (severity: string): boolean => {
|
|
239
|
+
return severity === 'CRITICAL' || severity === 'ERROR';
|
|
240
|
+
};
|
|
241
|
+
|
|
238
242
|
const toNormalizedPath = (value: string): string => value.replace(/\\/g, '/').trim();
|
|
239
243
|
|
|
240
244
|
const collectObservedPathsFromFacts = (
|
|
@@ -511,6 +515,66 @@ const toPlatformSkillsCoverageBlockingFinding = (params: {
|
|
|
511
515
|
};
|
|
512
516
|
};
|
|
513
517
|
|
|
518
|
+
const toCrossPlatformCriticalEnforcementBlockingFinding = (params: {
|
|
519
|
+
stage: 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
|
|
520
|
+
detectedPlatforms: DetectedPlatforms;
|
|
521
|
+
skillsRules: SkillsRuleSetLoadResult['rules'];
|
|
522
|
+
evaluatedRuleIds: ReadonlyArray<string>;
|
|
523
|
+
}): Finding | undefined => {
|
|
524
|
+
const detectedPlatformKeys = (
|
|
525
|
+
['ios', 'android', 'backend', 'frontend'] as const
|
|
526
|
+
).filter((platform) => params.detectedPlatforms[platform]?.detected === true);
|
|
527
|
+
|
|
528
|
+
if (detectedPlatformKeys.length === 0) {
|
|
529
|
+
return undefined;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const evaluatedRuleIds = new Set(params.evaluatedRuleIds);
|
|
533
|
+
const gaps: string[] = [];
|
|
534
|
+
|
|
535
|
+
for (const platform of detectedPlatformKeys) {
|
|
536
|
+
const rulePrefix = PLATFORM_SKILLS_RULE_PREFIXES[platform];
|
|
537
|
+
const criticalSkillRules = params.skillsRules
|
|
538
|
+
.filter(
|
|
539
|
+
(rule) =>
|
|
540
|
+
rule.id.startsWith(rulePrefix) &&
|
|
541
|
+
isCriticalProfileSeverity(rule.severity)
|
|
542
|
+
)
|
|
543
|
+
.map((rule) => rule.id)
|
|
544
|
+
.sort();
|
|
545
|
+
|
|
546
|
+
if (criticalSkillRules.length === 0) {
|
|
547
|
+
gaps.push(`${platform}{critical_profile_rules=missing}`);
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const evaluatedCriticalSkillRules = criticalSkillRules.filter((ruleId) =>
|
|
552
|
+
evaluatedRuleIds.has(ruleId)
|
|
553
|
+
);
|
|
554
|
+
if (evaluatedCriticalSkillRules.length === 0) {
|
|
555
|
+
gaps.push(
|
|
556
|
+
`${platform}{critical_profile_rules=${criticalSkillRules.length}; evaluated=0}`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (gaps.length === 0) {
|
|
562
|
+
return undefined;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return {
|
|
566
|
+
ruleId: 'governance.skills.cross-platform-critical.incomplete',
|
|
567
|
+
severity: 'ERROR',
|
|
568
|
+
code: 'SKILLS_CROSS_PLATFORM_CRITICAL_INCOMPLETE_S0',
|
|
569
|
+
message:
|
|
570
|
+
`Cross-platform critical enforcement incomplete at ${params.stage}: ${gaps.join(' | ')}. ` +
|
|
571
|
+
'Ensure each detected platform has critical-profile skill rules active and evaluated.',
|
|
572
|
+
filePath: '.ai_evidence.json',
|
|
573
|
+
matchedBy: 'SkillsCrossPlatformCriticalGuard',
|
|
574
|
+
source: 'skills-cross-platform-critical',
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
|
|
514
578
|
export async function runPlatformGate(params: {
|
|
515
579
|
policy: GatePolicy;
|
|
516
580
|
auditMode?: 'gate' | 'engine';
|
|
@@ -642,6 +706,17 @@ export async function runPlatformGate(params: {
|
|
|
642
706
|
evaluatedRuleIds: coverage?.evaluatedRuleIds ?? [],
|
|
643
707
|
})
|
|
644
708
|
: undefined;
|
|
709
|
+
const crossPlatformCriticalFinding =
|
|
710
|
+
params.policy.stage === 'PRE_COMMIT' ||
|
|
711
|
+
params.policy.stage === 'PRE_PUSH' ||
|
|
712
|
+
params.policy.stage === 'CI'
|
|
713
|
+
? toCrossPlatformCriticalEnforcementBlockingFinding({
|
|
714
|
+
stage: params.policy.stage,
|
|
715
|
+
detectedPlatforms,
|
|
716
|
+
skillsRules: skillsRuleSet.rules,
|
|
717
|
+
evaluatedRuleIds: coverage?.evaluatedRuleIds ?? [],
|
|
718
|
+
})
|
|
719
|
+
: undefined;
|
|
645
720
|
const skillsScopeComplianceFinding =
|
|
646
721
|
params.policy.stage === 'PRE_COMMIT' ||
|
|
647
722
|
params.policy.stage === 'PRE_PUSH' ||
|
|
@@ -720,6 +795,7 @@ export async function runPlatformGate(params: {
|
|
|
720
795
|
...(policyAsCodeBlockingFinding ? [policyAsCodeBlockingFinding] : []),
|
|
721
796
|
...(unsupportedSkillsMappingFinding ? [unsupportedSkillsMappingFinding] : []),
|
|
722
797
|
...(platformSkillsCoverageFinding ? [platformSkillsCoverageFinding] : []),
|
|
798
|
+
...(crossPlatformCriticalFinding ? [crossPlatformCriticalFinding] : []),
|
|
723
799
|
...(skillsScopeComplianceFinding ? [skillsScopeComplianceFinding] : []),
|
|
724
800
|
...(coverageBlockingFinding ? [coverageBlockingFinding] : []),
|
|
725
801
|
...tddBddEvaluation.findings,
|
|
@@ -727,6 +803,7 @@ export async function runPlatformGate(params: {
|
|
|
727
803
|
]
|
|
728
804
|
: unsupportedSkillsMappingFinding
|
|
729
805
|
|| platformSkillsCoverageFinding
|
|
806
|
+
|| crossPlatformCriticalFinding
|
|
730
807
|
|| skillsScopeComplianceFinding
|
|
731
808
|
|| coverageBlockingFinding
|
|
732
809
|
|| policyAsCodeBlockingFinding
|
|
@@ -737,6 +814,7 @@ export async function runPlatformGate(params: {
|
|
|
737
814
|
...(policyAsCodeBlockingFinding ? [policyAsCodeBlockingFinding] : []),
|
|
738
815
|
...(unsupportedSkillsMappingFinding ? [unsupportedSkillsMappingFinding] : []),
|
|
739
816
|
...(platformSkillsCoverageFinding ? [platformSkillsCoverageFinding] : []),
|
|
817
|
+
...(crossPlatformCriticalFinding ? [crossPlatformCriticalFinding] : []),
|
|
740
818
|
...(skillsScopeComplianceFinding ? [skillsScopeComplianceFinding] : []),
|
|
741
819
|
...(coverageBlockingFinding ? [coverageBlockingFinding] : []),
|
|
742
820
|
...tddBddEvaluation.findings,
|
|
@@ -750,6 +828,7 @@ export async function runPlatformGate(params: {
|
|
|
750
828
|
policyAsCodeBlockingFinding ||
|
|
751
829
|
unsupportedSkillsMappingFinding ||
|
|
752
830
|
platformSkillsCoverageFinding ||
|
|
831
|
+
crossPlatformCriticalFinding ||
|
|
753
832
|
skillsScopeComplianceFinding ||
|
|
754
833
|
coverageBlockingFinding ||
|
|
755
834
|
hasTddBddBlockingFinding
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
"payload": {
|
|
6
6
|
"hooks": {
|
|
7
7
|
"pre_write": {
|
|
8
|
-
"command": "npx --yes pumuki-pre-write"
|
|
8
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-write"
|
|
9
9
|
},
|
|
10
10
|
"pre_commit": {
|
|
11
|
-
"command": "
|
|
11
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-commit"
|
|
12
12
|
},
|
|
13
13
|
"pre_push": {
|
|
14
|
-
"command": "
|
|
14
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-push"
|
|
15
15
|
},
|
|
16
16
|
"ci": {
|
|
17
|
-
"command": "npx --yes pumuki-ci"
|
|
17
|
+
"command": "npx --yes --package pumuki@latest pumuki-ci"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"mcp": {
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"pumuki": {
|
|
36
36
|
"hooks": {
|
|
37
37
|
"pre_write": {
|
|
38
|
-
"command": "npx --yes pumuki-pre-write"
|
|
38
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-write"
|
|
39
39
|
},
|
|
40
40
|
"pre_commit": {
|
|
41
|
-
"command": "
|
|
41
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-commit"
|
|
42
42
|
},
|
|
43
43
|
"pre_push": {
|
|
44
|
-
"command": "
|
|
44
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-push"
|
|
45
45
|
},
|
|
46
46
|
"ci": {
|
|
47
|
-
"command": "npx --yes pumuki-ci"
|
|
47
|
+
"command": "npx --yes --package pumuki@latest pumuki-ci"
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
},
|
|
@@ -97,16 +97,16 @@
|
|
|
97
97
|
},
|
|
98
98
|
"pumukiHooks": {
|
|
99
99
|
"pre_write": {
|
|
100
|
-
"command": "npx --yes pumuki-pre-write"
|
|
100
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-write"
|
|
101
101
|
},
|
|
102
102
|
"pre_commit": {
|
|
103
|
-
"command": "
|
|
103
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-commit"
|
|
104
104
|
},
|
|
105
105
|
"pre_push": {
|
|
106
|
-
"command": "
|
|
106
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-push"
|
|
107
107
|
},
|
|
108
108
|
"ci": {
|
|
109
|
-
"command": "npx --yes pumuki-ci"
|
|
109
|
+
"command": "npx --yes --package pumuki@latest pumuki-ci"
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -116,10 +116,10 @@
|
|
|
116
116
|
{
|
|
117
117
|
"path": ".codeium/adapter/hooks.json",
|
|
118
118
|
"payload": {
|
|
119
|
-
"preCommand": "npx --yes pumuki-pre-write",
|
|
120
|
-
"postCommand": "
|
|
121
|
-
"pushCommand": "
|
|
122
|
-
"ciCommand": "npx --yes pumuki-ci",
|
|
119
|
+
"preCommand": "npx --yes --package pumuki@latest pumuki-pre-write",
|
|
120
|
+
"postCommand": "npx --yes --package pumuki@latest pumuki-pre-commit",
|
|
121
|
+
"pushCommand": "npx --yes --package pumuki@latest pumuki-pre-push",
|
|
122
|
+
"ciCommand": "npx --yes --package pumuki@latest pumuki-ci",
|
|
123
123
|
"mcpCommand": "npx --yes --package pumuki@latest pumuki-mcp-enterprise-stdio"
|
|
124
124
|
}
|
|
125
125
|
},
|
|
@@ -169,16 +169,16 @@
|
|
|
169
169
|
"payload": {
|
|
170
170
|
"hooks": {
|
|
171
171
|
"pre_write": {
|
|
172
|
-
"command": "npx --yes pumuki-pre-write"
|
|
172
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-write"
|
|
173
173
|
},
|
|
174
174
|
"pre_commit": {
|
|
175
|
-
"command": "
|
|
175
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-commit"
|
|
176
176
|
},
|
|
177
177
|
"pre_push": {
|
|
178
|
-
"command": "
|
|
178
|
+
"command": "npx --yes --package pumuki@latest pumuki-pre-push"
|
|
179
179
|
},
|
|
180
180
|
"ci": {
|
|
181
|
-
"command": "npx --yes pumuki-ci"
|
|
181
|
+
"command": "npx --yes --package pumuki@latest pumuki-ci"
|
|
182
182
|
}
|
|
183
183
|
},
|
|
184
184
|
"mcp": {
|
|
@@ -975,6 +975,9 @@ const printDoctorReport = (
|
|
|
975
975
|
): void => {
|
|
976
976
|
writeInfo(`[pumuki] repo: ${report.repoRoot}`);
|
|
977
977
|
writeInfo(`[pumuki] package version: ${report.packageVersion}`);
|
|
978
|
+
writeInfo(
|
|
979
|
+
`[pumuki] hooks path: ${report.hooksDirectory} (${report.hooksDirectoryResolution})`
|
|
980
|
+
);
|
|
978
981
|
writeInfo(
|
|
979
982
|
`[pumuki] tracked node_modules paths: ${report.trackedNodeModulesPaths.length}`
|
|
980
983
|
);
|
|
@@ -1422,6 +1425,9 @@ export const runLifecycleCli = async (
|
|
|
1422
1425
|
writeInfo(`[pumuki] package version: ${status.packageVersion}`);
|
|
1423
1426
|
writeInfo(`[pumuki] lifecycle installed: ${status.lifecycleState.installed ?? 'false'}`);
|
|
1424
1427
|
writeInfo(`[pumuki] lifecycle version: ${status.lifecycleState.version ?? 'unknown'}`);
|
|
1428
|
+
writeInfo(
|
|
1429
|
+
`[pumuki] hooks path: ${status.hooksDirectory} (${status.hooksDirectoryResolution})`
|
|
1430
|
+
);
|
|
1425
1431
|
writeInfo(
|
|
1426
1432
|
`[pumuki] hooks: pre-commit=${status.hookStatus['pre-commit'].managedBlockPresent ? 'managed' : 'missing'}, pre-push=${status.hookStatus['pre-push'].managedBlockPresent ? 'managed' : 'missing'}`
|
|
1427
1433
|
);
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { readEvidenceResult } from '../evidence/readEvidence';
|
|
4
4
|
import { resolvePolicyForStage } from '../gate/stagePolicies';
|
|
5
|
-
import { getPumukiHooksStatus } from './hookManager';
|
|
5
|
+
import { getPumukiHooksStatus, resolvePumukiHooksDirectory } from './hookManager';
|
|
6
6
|
import { LifecycleGitService, type ILifecycleGitService } from './gitService';
|
|
7
7
|
import { getCurrentPumukiVersion } from './packageInfo';
|
|
8
8
|
import {
|
|
@@ -75,6 +75,8 @@ export type LifecycleDoctorReport = {
|
|
|
75
75
|
lifecycleState: LifecycleState;
|
|
76
76
|
trackedNodeModulesPaths: ReadonlyArray<string>;
|
|
77
77
|
hookStatus: ReturnType<typeof getPumukiHooksStatus>;
|
|
78
|
+
hooksDirectory: string;
|
|
79
|
+
hooksDirectoryResolution: 'git-rev-parse' | 'git-config' | 'default';
|
|
78
80
|
policyValidation: LifecyclePolicyValidationSnapshot;
|
|
79
81
|
issues: ReadonlyArray<DoctorIssue>;
|
|
80
82
|
deep?: DoctorDeepReport;
|
|
@@ -83,6 +85,7 @@ export type LifecycleDoctorReport = {
|
|
|
83
85
|
const buildDoctorIssues = (params: {
|
|
84
86
|
trackedNodeModulesPaths: ReadonlyArray<string>;
|
|
85
87
|
hookStatus: ReturnType<typeof getPumukiHooksStatus>;
|
|
88
|
+
hooksDirectory: string;
|
|
86
89
|
lifecycleState: LifecycleState;
|
|
87
90
|
}): ReadonlyArray<DoctorIssue> => {
|
|
88
91
|
const issues: DoctorIssue[] = [];
|
|
@@ -107,6 +110,7 @@ const buildDoctorIssues = (params: {
|
|
|
107
110
|
severity: 'warning',
|
|
108
111
|
message:
|
|
109
112
|
`Lifecycle state says installed=true but managed hook blocks are incomplete (${managedHooks}/${totalHooks}). ` +
|
|
113
|
+
`Effective hooks path: ${params.hooksDirectory}. ` +
|
|
110
114
|
'If you use versioned hooks via core.hooksPath, ensure those hooks include the PUMUKI MANAGED block or rerun "pumuki install".',
|
|
111
115
|
});
|
|
112
116
|
}
|
|
@@ -640,12 +644,14 @@ export const runLifecycleDoctor = (params?: {
|
|
|
640
644
|
const cwd = params?.cwd ?? process.cwd();
|
|
641
645
|
const repoRoot = git.resolveRepoRoot(cwd);
|
|
642
646
|
const trackedNodeModulesPaths = git.trackedNodeModulesPaths(repoRoot);
|
|
647
|
+
const hooksDirectory = resolvePumukiHooksDirectory(repoRoot);
|
|
643
648
|
const hookStatus = getPumukiHooksStatus(repoRoot);
|
|
644
649
|
const lifecycleState = readLifecycleState(git, repoRoot);
|
|
645
650
|
|
|
646
651
|
const issues = buildDoctorIssues({
|
|
647
652
|
trackedNodeModulesPaths,
|
|
648
653
|
hookStatus,
|
|
654
|
+
hooksDirectory: hooksDirectory.path,
|
|
649
655
|
lifecycleState,
|
|
650
656
|
});
|
|
651
657
|
const deep = params?.deep
|
|
@@ -663,6 +669,8 @@ export const runLifecycleDoctor = (params?: {
|
|
|
663
669
|
lifecycleState,
|
|
664
670
|
trackedNodeModulesPaths,
|
|
665
671
|
hookStatus,
|
|
672
|
+
hooksDirectory: hooksDirectory.path,
|
|
673
|
+
hooksDirectoryResolution: hooksDirectory.source,
|
|
666
674
|
policyValidation: readLifecyclePolicyValidationSnapshot(repoRoot),
|
|
667
675
|
issues,
|
|
668
676
|
deep,
|
|
@@ -16,6 +16,16 @@ export type HookUninstallResult = {
|
|
|
16
16
|
changedHooks: ReadonlyArray<PumukiManagedHook>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
export type PumukiHooksDirectoryResolutionSource =
|
|
20
|
+
| 'git-rev-parse'
|
|
21
|
+
| 'git-config'
|
|
22
|
+
| 'default';
|
|
23
|
+
|
|
24
|
+
export type PumukiHooksDirectoryResolution = {
|
|
25
|
+
path: string;
|
|
26
|
+
source: PumukiHooksDirectoryResolutionSource;
|
|
27
|
+
};
|
|
28
|
+
|
|
19
29
|
const HOOK_FILE_MODE = 0o755;
|
|
20
30
|
|
|
21
31
|
const resolveGitPath = (repoRoot: string, gitPathTarget: string): string | null => {
|
|
@@ -34,8 +44,82 @@ const resolveGitPath = (repoRoot: string, gitPathTarget: string): string | null
|
|
|
34
44
|
}
|
|
35
45
|
};
|
|
36
46
|
|
|
47
|
+
const readCoreHooksPathFromGitConfig = (repoRoot: string): string | null => {
|
|
48
|
+
const configPath = join(repoRoot, '.git', 'config');
|
|
49
|
+
if (!existsSync(configPath)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let contents = '';
|
|
54
|
+
try {
|
|
55
|
+
contents = readFileSync(configPath, 'utf8');
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let inCoreSection = false;
|
|
61
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
62
|
+
const line = rawLine.trim();
|
|
63
|
+
if (line.length === 0 || line.startsWith(';') || line.startsWith('#')) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (line.startsWith('[') && line.endsWith(']')) {
|
|
68
|
+
inCoreSection = /^\[core\]$/i.test(line);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!inCoreSection) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const match = /^hookspath\s*=\s*(.+)$/i.exec(line);
|
|
77
|
+
if (!match) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let hooksPath = match[1]?.trim() ?? '';
|
|
82
|
+
if (hooksPath.startsWith('"') && hooksPath.endsWith('"')) {
|
|
83
|
+
hooksPath = hooksPath.slice(1, -1);
|
|
84
|
+
}
|
|
85
|
+
if (hooksPath.length === 0) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
return hooksPath;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const resolvePumukiHooksDirectory = (
|
|
95
|
+
repoRoot: string
|
|
96
|
+
): PumukiHooksDirectoryResolution => {
|
|
97
|
+
const gitPathHooks = resolveGitPath(repoRoot, 'hooks');
|
|
98
|
+
if (gitPathHooks) {
|
|
99
|
+
return {
|
|
100
|
+
path: gitPathHooks,
|
|
101
|
+
source: 'git-rev-parse',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const hooksPathFromConfig = readCoreHooksPathFromGitConfig(repoRoot);
|
|
106
|
+
if (hooksPathFromConfig) {
|
|
107
|
+
return {
|
|
108
|
+
path: isAbsolute(hooksPathFromConfig)
|
|
109
|
+
? hooksPathFromConfig
|
|
110
|
+
: resolve(repoRoot, hooksPathFromConfig),
|
|
111
|
+
source: 'git-config',
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
path: join(repoRoot, '.git', 'hooks'),
|
|
117
|
+
source: 'default',
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
37
121
|
const resolveHooksDirectory = (repoRoot: string): string =>
|
|
38
|
-
|
|
122
|
+
resolvePumukiHooksDirectory(repoRoot).path;
|
|
39
123
|
|
|
40
124
|
const resolveHookPath = (repoRoot: string, hook: PumukiManagedHook): string =>
|
|
41
125
|
join(resolveHooksDirectory(repoRoot), hook);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getPumukiHooksStatus } from './hookManager';
|
|
1
|
+
import { getPumukiHooksStatus, resolvePumukiHooksDirectory } from './hookManager';
|
|
2
2
|
import { LifecycleGitService, type ILifecycleGitService } from './gitService';
|
|
3
3
|
import { getCurrentPumukiVersion } from './packageInfo';
|
|
4
4
|
import {
|
|
@@ -12,6 +12,8 @@ export type LifecycleStatus = {
|
|
|
12
12
|
packageVersion: string;
|
|
13
13
|
lifecycleState: LifecycleState;
|
|
14
14
|
hookStatus: ReturnType<typeof getPumukiHooksStatus>;
|
|
15
|
+
hooksDirectory: string;
|
|
16
|
+
hooksDirectoryResolution: 'git-rev-parse' | 'git-config' | 'default';
|
|
15
17
|
trackedNodeModulesCount: number;
|
|
16
18
|
policyValidation: LifecyclePolicyValidationSnapshot;
|
|
17
19
|
};
|
|
@@ -23,6 +25,7 @@ export const readLifecycleStatus = (params?: {
|
|
|
23
25
|
const git = params?.git ?? new LifecycleGitService();
|
|
24
26
|
const cwd = params?.cwd ?? process.cwd();
|
|
25
27
|
const repoRoot = git.resolveRepoRoot(cwd);
|
|
28
|
+
const hooksDirectory = resolvePumukiHooksDirectory(repoRoot);
|
|
26
29
|
const trackedNodeModulesCount = git.trackedNodeModulesPaths(repoRoot).length;
|
|
27
30
|
|
|
28
31
|
return {
|
|
@@ -30,6 +33,8 @@ export const readLifecycleStatus = (params?: {
|
|
|
30
33
|
packageVersion: getCurrentPumukiVersion({ repoRoot }),
|
|
31
34
|
lifecycleState: readLifecycleState(git, repoRoot),
|
|
32
35
|
hookStatus: getPumukiHooksStatus(repoRoot),
|
|
36
|
+
hooksDirectory: hooksDirectory.path,
|
|
37
|
+
hooksDirectoryResolution: hooksDirectory.source,
|
|
33
38
|
trackedNodeModulesCount,
|
|
34
39
|
policyValidation: readLifecyclePolicyValidationSnapshot(repoRoot),
|
|
35
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.39",
|
|
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": {
|