pumuki 6.3.51 → 6.3.53

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/README.md CHANGED
@@ -34,6 +34,7 @@ Install and bootstrap:
34
34
  npm install --save-exact pumuki
35
35
  npx --yes pumuki bootstrap --enterprise --agent=codex
36
36
  npx --yes pumuki status
37
+ npx --yes pumuki doctor --json
37
38
  ```
38
39
 
39
40
  Fallback (equivalent in pasos separados):
@@ -80,6 +81,16 @@ Expected behavior:
80
81
  - `PRE_PUSH`: blocks if branch has no upstream tracking reference.
81
82
  - `CI`: requires a valid diff range context (not `HEAD..HEAD` with ambiguous range).
82
83
 
84
+ Version drift quick check:
85
+
86
+ - `status --json` and `doctor --json` expose `version.effective`, `version.runtime`, `version.consumerInstalled`, `version.lifecycleInstalled`, `version.driftWarning`, and `version.alignmentCommand`.
87
+ - If `driftWarning` is not `null`, align the consumer with:
88
+
89
+ ```bash
90
+ npm install --save-exact pumuki@6.3.52
91
+ npx --yes pumuki update --latest
92
+ ```
93
+
83
94
  ## Why Pumuki
84
95
 
85
96
  Modern teams need fast feedback with strict governance. Pumuki combines:
@@ -173,6 +173,10 @@ const computeBundleHash = (rules: ReadonlyArray<SkillsCompiledRule>): string =>
173
173
  confidence: rule.confidence ?? null,
174
174
  locked: rule.locked ?? false,
175
175
  evaluationMode: rule.evaluationMode ?? null,
176
+ ast_node_ids:
177
+ rule.astNodeIds && rule.astNodeIds.length > 0
178
+ ? [...new Set(rule.astNodeIds)].sort()
179
+ : null,
176
180
  origin: rule.origin ?? null,
177
181
  sourceSkill: rule.sourceSkill,
178
182
  sourcePath: rule.sourcePath,
@@ -128,6 +128,7 @@ export const captureRepoState = (repoRoot: string): RepoState => {
128
128
  const statusLines = toStatusLines(safeRunGit(repoRoot, ['status', '--short']) ?? '');
129
129
  const staged = statusLines.filter((line) => line[0] && line[0] !== '?' && line[0] !== ' ').length;
130
130
  const unstaged = statusLines.filter((line) => line[1] && line[1] !== ' ').length;
131
+ const pendingChanges = statusLines.length;
131
132
  const { ahead, behind } = toAheadBehind(repoRoot, upstream);
132
133
  const lifecycle = readLifecycleStatusSafe(repoRoot);
133
134
  const versionMetadata = resolvePumukiVersionMetadata({ repoRoot });
@@ -146,6 +147,7 @@ export const captureRepoState = (repoRoot: string): RepoState => {
146
147
  dirty: statusLines.length > 0,
147
148
  staged: toCount(staged),
148
149
  unstaged: toCount(unstaged),
150
+ pending_changes: toCount(pendingChanges),
149
151
  },
150
152
  lifecycle: {
151
153
  installed: lifecycle.lifecycleState.installed === 'true',
@@ -164,6 +164,7 @@ export type RepoState = {
164
164
  dirty: boolean;
165
165
  staged: number;
166
166
  unstaged: number;
167
+ pending_changes?: number;
167
168
  };
168
169
  lifecycle: {
169
170
  installed: boolean;
@@ -770,7 +770,9 @@ const collectPreWriteCoherenceViolations = (params: {
770
770
  }
771
771
 
772
772
  if (params.preWriteWorktreeHygiene.enabled && params.repoState.git.available) {
773
- const pendingChanges = params.repoState.git.staged + params.repoState.git.unstaged;
773
+ const pendingChanges =
774
+ params.repoState.git.pending_changes
775
+ ?? (params.repoState.git.staged + params.repoState.git.unstaged);
774
776
  if (pendingChanges >= params.preWriteWorktreeHygiene.blockThreshold) {
775
777
  violations.push(
776
778
  toErrorViolation(
@@ -1482,7 +1482,20 @@ const printDoctorReport = (
1482
1482
  remoteCiDiagnostics?: RemoteCiDiagnostics
1483
1483
  ): void => {
1484
1484
  writeInfo(`[pumuki] repo: ${report.repoRoot}`);
1485
- writeInfo(`[pumuki] package version: ${report.packageVersion}`);
1485
+ writeInfo(`[pumuki] effective version: ${report.version.effective}`);
1486
+ writeInfo(`[pumuki] runtime version: ${report.version.runtime}`);
1487
+ writeInfo(
1488
+ `[pumuki] consumer installed version: ${report.version.consumerInstalled ?? 'unknown'}`
1489
+ );
1490
+ writeInfo(
1491
+ `[pumuki] lifecycle installed version: ${report.version.lifecycleInstalled ?? 'unknown'}`
1492
+ );
1493
+ if (report.version.driftWarning) {
1494
+ writeInfo(`[pumuki] version drift: ${report.version.driftWarning}`);
1495
+ if (report.version.alignmentCommand) {
1496
+ writeInfo(`[pumuki] version remediation: ${report.version.alignmentCommand}`);
1497
+ }
1498
+ }
1486
1499
  writeInfo(
1487
1500
  `[pumuki] hooks path: ${report.hooksDirectory} (${report.hooksDirectoryResolution})`
1488
1501
  );
@@ -2099,9 +2112,21 @@ export const runLifecycleCli = async (
2099
2112
  );
2100
2113
  } else {
2101
2114
  writeInfo(`[pumuki] repo: ${status.repoRoot}`);
2102
- writeInfo(`[pumuki] package version: ${status.packageVersion}`);
2115
+ writeInfo(`[pumuki] effective version: ${status.version.effective}`);
2116
+ writeInfo(`[pumuki] runtime version: ${status.version.runtime}`);
2117
+ writeInfo(
2118
+ `[pumuki] consumer installed version: ${status.version.consumerInstalled ?? 'unknown'}`
2119
+ );
2120
+ writeInfo(
2121
+ `[pumuki] lifecycle installed version: ${status.version.lifecycleInstalled ?? 'unknown'}`
2122
+ );
2123
+ if (status.version.driftWarning) {
2124
+ writeInfo(`[pumuki] version drift: ${status.version.driftWarning}`);
2125
+ if (status.version.alignmentCommand) {
2126
+ writeInfo(`[pumuki] version remediation: ${status.version.alignmentCommand}`);
2127
+ }
2128
+ }
2103
2129
  writeInfo(`[pumuki] lifecycle installed: ${status.lifecycleState.installed ?? 'false'}`);
2104
- writeInfo(`[pumuki] lifecycle version: ${status.lifecycleState.version ?? 'unknown'}`);
2105
2130
  writeInfo(
2106
2131
  `[pumuki] hooks path: ${status.hooksDirectory} (${status.hooksDirectoryResolution})`
2107
2132
  );
@@ -4,7 +4,7 @@ import { readEvidenceResult } from '../evidence/readEvidence';
4
4
  import { resolvePolicyForStage } from '../gate/stagePolicies';
5
5
  import { getPumukiHooksStatus, resolvePumukiHooksDirectory } from './hookManager';
6
6
  import { LifecycleGitService, type ILifecycleGitService } from './gitService';
7
- import { getCurrentPumukiVersion } from './packageInfo';
7
+ import { buildLifecycleVersionReport, getCurrentPumukiVersion } from './packageInfo';
8
8
  import {
9
9
  readLifecyclePolicyValidationSnapshot,
10
10
  type LifecyclePolicyValidationSnapshot,
@@ -72,6 +72,7 @@ export type DoctorCompatibilityContract = {
72
72
  export type LifecycleDoctorReport = {
73
73
  repoRoot: string;
74
74
  packageVersion: string;
75
+ version: ReturnType<typeof buildLifecycleVersionReport>;
75
76
  lifecycleState: LifecycleState;
76
77
  trackedNodeModulesPaths: ReadonlyArray<string>;
77
78
  hookStatus: ReturnType<typeof getPumukiHooksStatus>;
@@ -724,10 +725,15 @@ export const runLifecycleDoctor = (params?: {
724
725
  hookStatus,
725
726
  })
726
727
  : undefined;
728
+ const version = buildLifecycleVersionReport({
729
+ repoRoot,
730
+ lifecycleVersion: lifecycleState.version,
731
+ });
727
732
 
728
733
  return {
729
734
  repoRoot,
730
- packageVersion: getCurrentPumukiVersion({ repoRoot }),
735
+ packageVersion: version.effective,
736
+ version,
731
737
  lifecycleState,
732
738
  trackedNodeModulesPaths,
733
739
  hookStatus,
@@ -24,6 +24,21 @@ export type PumukiVersionMetadata = {
24
24
  source: 'consumer-node-modules' | 'runtime-package';
25
25
  };
26
26
 
27
+ export type LifecycleVersionReport = {
28
+ effective: string;
29
+ runtime: string;
30
+ consumerInstalled: string | null;
31
+ lifecycleInstalled: string | null;
32
+ source: PumukiVersionMetadata['source'];
33
+ driftFromRuntime: boolean;
34
+ driftFromLifecycleInstalled: boolean;
35
+ driftWarning: string | null;
36
+ alignmentCommand: string | null;
37
+ };
38
+
39
+ export const buildLifecycleAlignmentCommand = (runtimeVersion: string): string =>
40
+ `npm install --save-exact pumuki@${runtimeVersion} && npx --yes --package pumuki@${runtimeVersion} pumuki install`;
41
+
27
42
  export const resolvePumukiVersionMetadata = (params?: { repoRoot?: string }): PumukiVersionMetadata => {
28
43
  const runtimeVersion = packageJson.version;
29
44
  const repoRoot = params?.repoRoot;
@@ -51,3 +66,37 @@ export const getCurrentPumukiVersion = (params?: { repoRoot?: string }): string
51
66
  };
52
67
 
53
68
  export const getCurrentPumukiPackageName = (): string => packageJson.name;
69
+
70
+ export const buildLifecycleVersionReport = (params?: {
71
+ repoRoot?: string;
72
+ lifecycleVersion?: string | null | undefined;
73
+ }): LifecycleVersionReport => {
74
+ const metadata = resolvePumukiVersionMetadata({ repoRoot: params?.repoRoot });
75
+ const lifecycleInstalled =
76
+ typeof params?.lifecycleVersion === 'string' && params.lifecycleVersion.trim().length > 0
77
+ ? params.lifecycleVersion.trim()
78
+ : null;
79
+ const driftFromRuntime = metadata.resolvedVersion !== metadata.runtimeVersion;
80
+ const driftFromLifecycleInstalled =
81
+ lifecycleInstalled !== null && metadata.resolvedVersion !== lifecycleInstalled;
82
+ const driftTargets = [
83
+ driftFromRuntime ? `runtime=${metadata.runtimeVersion}` : null,
84
+ driftFromLifecycleInstalled ? `lifecycle=${lifecycleInstalled}` : null,
85
+ ].filter((value): value is string => value !== null);
86
+
87
+ return {
88
+ effective: metadata.resolvedVersion,
89
+ runtime: metadata.runtimeVersion,
90
+ consumerInstalled: metadata.consumerInstalledVersion,
91
+ lifecycleInstalled,
92
+ source: metadata.source,
93
+ driftFromRuntime,
94
+ driftFromLifecycleInstalled,
95
+ driftWarning:
96
+ driftTargets.length > 0
97
+ ? `Version drift detectado: effective=${metadata.resolvedVersion} ${driftTargets.join(' ')}.`
98
+ : null,
99
+ alignmentCommand:
100
+ driftTargets.length > 0 ? buildLifecycleAlignmentCommand(metadata.runtimeVersion) : null,
101
+ };
102
+ };
@@ -1,6 +1,6 @@
1
1
  import { getPumukiHooksStatus, resolvePumukiHooksDirectory } from './hookManager';
2
2
  import { LifecycleGitService, type ILifecycleGitService } from './gitService';
3
- import { getCurrentPumukiVersion } from './packageInfo';
3
+ import { buildLifecycleVersionReport } from './packageInfo';
4
4
  import {
5
5
  readLifecyclePolicyValidationSnapshot,
6
6
  type LifecyclePolicyValidationSnapshot,
@@ -10,6 +10,7 @@ import { readLifecycleState, type LifecycleState } from './state';
10
10
  export type LifecycleStatus = {
11
11
  repoRoot: string;
12
12
  packageVersion: string;
13
+ version: ReturnType<typeof buildLifecycleVersionReport>;
13
14
  lifecycleState: LifecycleState;
14
15
  hookStatus: ReturnType<typeof getPumukiHooksStatus>;
15
16
  hooksDirectory: string;
@@ -27,11 +28,17 @@ export const readLifecycleStatus = (params?: {
27
28
  const repoRoot = git.resolveRepoRoot(cwd);
28
29
  const hooksDirectory = resolvePumukiHooksDirectory(repoRoot);
29
30
  const trackedNodeModulesCount = git.trackedNodeModulesPaths(repoRoot).length;
31
+ const lifecycleState = readLifecycleState(git, repoRoot);
32
+ const version = buildLifecycleVersionReport({
33
+ repoRoot,
34
+ lifecycleVersion: lifecycleState.version,
35
+ });
30
36
 
31
37
  return {
32
38
  repoRoot,
33
- packageVersion: getCurrentPumukiVersion({ repoRoot }),
34
- lifecycleState: readLifecycleState(git, repoRoot),
39
+ packageVersion: version.effective,
40
+ version,
41
+ lifecycleState,
35
42
  hookStatus: getPumukiHooksStatus(repoRoot),
36
43
  hooksDirectory: hooksDirectory.path,
37
44
  hooksDirectoryResolution: hooksDirectory.source,
@@ -9,7 +9,7 @@ import { resolveFactsForGateScope } from '../git/runPlatformGateFacts';
9
9
  import { resolvePolicyForStage, type ResolvedStagePolicy } from '../gate/stagePolicies';
10
10
  import { readEvidence } from '../evidence/readEvidence';
11
11
  import type { AiEvidenceV2_1, SnapshotFinding } from '../evidence/schema';
12
- import { GitService } from '../git/GitService';
12
+ import { GitService, type IGitService } from '../git/GitService';
13
13
  import type { Fact } from '../../core/facts/Fact';
14
14
  import { ensureRuntimeArtifactsIgnored } from './artifacts';
15
15
  import {
@@ -17,7 +17,11 @@ import {
17
17
  emitGateBlockedNotification,
18
18
  } from '../notifications/emitAuditSummaryNotification';
19
19
  import { runPolicyReconcile } from './policyReconcile';
20
- import { resolvePumukiVersionMetadata, type PumukiVersionMetadata } from './packageInfo';
20
+ import {
21
+ buildLifecycleAlignmentCommand,
22
+ resolvePumukiVersionMetadata,
23
+ type PumukiVersionMetadata,
24
+ } from './packageInfo';
21
25
 
22
26
  export type LifecycleWatchStage = 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
23
27
  export type LifecycleWatchScope = 'workingTree' | 'staged' | 'repoAndStaged' | 'repo';
@@ -59,6 +63,7 @@ export type LifecycleWatchResult = {
59
63
  source: PumukiVersionMetadata['source'];
60
64
  driftFromRuntime: boolean;
61
65
  driftWarning: string | null;
66
+ alignmentCommand: string | null;
62
67
  };
63
68
  stage: LifecycleWatchStage;
64
69
  scope: LifecycleWatchScope;
@@ -91,6 +96,20 @@ type LifecycleWatchDependencies = {
91
96
 
92
97
  const defaultGitService = new GitService();
93
98
 
99
+ class RepoScopedGitService extends GitService implements IGitService {
100
+ constructor(private readonly repoRoot: string) {
101
+ super();
102
+ }
103
+
104
+ override runGit(args: ReadonlyArray<string>, cwd?: string): string {
105
+ return super.runGit(args, cwd ?? this.repoRoot);
106
+ }
107
+
108
+ override resolveRepoRoot(): string {
109
+ return this.repoRoot;
110
+ }
111
+ }
112
+
94
113
  const defaultDependencies: LifecycleWatchDependencies = {
95
114
  resolveRepoRoot: () => defaultGitService.resolveRepoRoot(),
96
115
  readChangeToken: (repoRoot) =>
@@ -301,6 +320,7 @@ export const runLifecycleWatch = async (
301
320
  ...dependencies,
302
321
  };
303
322
  const repoRoot = params?.repoRoot ?? activeDependencies.resolveRepoRoot();
323
+ const repoGitService = new RepoScopedGitService(repoRoot);
304
324
  const versionMetadata = activeDependencies.resolvePumukiVersionMetadata({ repoRoot });
305
325
  const driftFromRuntime = versionMetadata.resolvedVersion !== versionMetadata.runtimeVersion;
306
326
  const driftWarning = driftFromRuntime
@@ -368,7 +388,7 @@ export const runLifecycleWatch = async (
368
388
  const gateScope = toGateScope(scope);
369
389
  const facts = await activeDependencies.resolveFactsForGateScope({
370
390
  scope: gateScope,
371
- git: defaultGitService,
391
+ git: repoGitService,
372
392
  });
373
393
  const changedFiles = collectChangedFilesFromFacts(facts);
374
394
  const evaluatedFiles = collectEvaluatedFilesFromFacts(facts);
@@ -389,6 +409,9 @@ export const runLifecycleWatch = async (
389
409
  policyTrace: resolvedPolicy.trace,
390
410
  scope: gateScope,
391
411
  silent: true,
412
+ services: {
413
+ git: repoGitService,
414
+ },
392
415
  });
393
416
  const evidence = activeDependencies.readEvidence(repoRoot);
394
417
  const allFindingsBase = evidence?.snapshot.findings ?? [];
@@ -566,6 +589,9 @@ export const runLifecycleWatch = async (
566
589
  source: versionMetadata.source,
567
590
  driftFromRuntime,
568
591
  driftWarning,
592
+ alignmentCommand: driftFromRuntime
593
+ ? buildLifecycleAlignmentCommand(versionMetadata.runtimeVersion)
594
+ : null,
569
595
  },
570
596
  stage,
571
597
  scope,
@@ -31,6 +31,7 @@ const toHumanMessage = (params: {
31
31
  const isEvidenceCode = (code: string): boolean =>
32
32
  code === 'EVIDENCE_MISSING'
33
33
  || code === 'EVIDENCE_INVALID'
34
+ || code === 'EVIDENCE_CHAIN_INVALID'
34
35
  || code === 'EVIDENCE_STALE'
35
36
  || code === 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES'
36
37
  || code === 'EVIDENCE_SKILLS_CONTRACT_INCOMPLETE'
@@ -67,10 +68,11 @@ const nextActionFromViolation = (
67
68
  switch (violation.code) {
68
69
  case 'EVIDENCE_MISSING':
69
70
  case 'EVIDENCE_INVALID':
71
+ case 'EVIDENCE_CHAIN_INVALID':
70
72
  case 'EVIDENCE_STALE':
71
73
  return {
72
74
  kind: 'run_command',
73
- message: 'Refresca evidencia y vuelve a evaluar PRE_WRITE.',
75
+ message: 'Regenera o refresca evidencia y vuelve a evaluar PRE_WRITE.',
74
76
  command: 'npx --yes --package pumuki@latest pumuki sdd validate --stage=PRE_WRITE --json',
75
77
  };
76
78
  case 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.51",
3
+ "version": "6.3.53",
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": {