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 +11 -0
- package/integrations/config/skillsCustomRules.ts +4 -0
- package/integrations/evidence/repoState.ts +2 -0
- package/integrations/evidence/schema.ts +1 -0
- package/integrations/gate/evaluateAiGate.ts +3 -1
- package/integrations/lifecycle/cli.ts +28 -3
- package/integrations/lifecycle/doctor.ts +8 -2
- package/integrations/lifecycle/packageInfo.ts +49 -0
- package/integrations/lifecycle/status.ts +10 -3
- package/integrations/lifecycle/watch.ts +29 -3
- package/integrations/mcp/autoExecuteAiStart.ts +3 -1
- package/package.json +1 -1
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',
|
|
@@ -770,7 +770,9 @@ const collectPreWriteCoherenceViolations = (params: {
|
|
|
770
770
|
}
|
|
771
771
|
|
|
772
772
|
if (params.preWriteWorktreeHygiene.enabled && params.repoState.git.available) {
|
|
773
|
-
const pendingChanges =
|
|
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]
|
|
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]
|
|
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:
|
|
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 {
|
|
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:
|
|
34
|
-
|
|
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 {
|
|
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:
|
|
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: '
|
|
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.
|
|
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": {
|