pumuki 6.3.52 → 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(
@@ -1492,6 +1492,9 @@ const printDoctorReport = (
1492
1492
  );
1493
1493
  if (report.version.driftWarning) {
1494
1494
  writeInfo(`[pumuki] version drift: ${report.version.driftWarning}`);
1495
+ if (report.version.alignmentCommand) {
1496
+ writeInfo(`[pumuki] version remediation: ${report.version.alignmentCommand}`);
1497
+ }
1495
1498
  }
1496
1499
  writeInfo(
1497
1500
  `[pumuki] hooks path: ${report.hooksDirectory} (${report.hooksDirectoryResolution})`
@@ -2119,6 +2122,9 @@ export const runLifecycleCli = async (
2119
2122
  );
2120
2123
  if (status.version.driftWarning) {
2121
2124
  writeInfo(`[pumuki] version drift: ${status.version.driftWarning}`);
2125
+ if (status.version.alignmentCommand) {
2126
+ writeInfo(`[pumuki] version remediation: ${status.version.alignmentCommand}`);
2127
+ }
2122
2128
  }
2123
2129
  writeInfo(`[pumuki] lifecycle installed: ${status.lifecycleState.installed ?? 'false'}`);
2124
2130
  writeInfo(
@@ -33,8 +33,12 @@ export type LifecycleVersionReport = {
33
33
  driftFromRuntime: boolean;
34
34
  driftFromLifecycleInstalled: boolean;
35
35
  driftWarning: string | null;
36
+ alignmentCommand: string | null;
36
37
  };
37
38
 
39
+ export const buildLifecycleAlignmentCommand = (runtimeVersion: string): string =>
40
+ `npm install --save-exact pumuki@${runtimeVersion} && npx --yes --package pumuki@${runtimeVersion} pumuki install`;
41
+
38
42
  export const resolvePumukiVersionMetadata = (params?: { repoRoot?: string }): PumukiVersionMetadata => {
39
43
  const runtimeVersion = packageJson.version;
40
44
  const repoRoot = params?.repoRoot;
@@ -92,5 +96,7 @@ export const buildLifecycleVersionReport = (params?: {
92
96
  driftTargets.length > 0
93
97
  ? `Version drift detectado: effective=${metadata.resolvedVersion} ${driftTargets.join(' ')}.`
94
98
  : null,
99
+ alignmentCommand:
100
+ driftTargets.length > 0 ? buildLifecycleAlignmentCommand(metadata.runtimeVersion) : null,
95
101
  };
96
102
  };
@@ -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.52",
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": {