pumuki 6.3.115 → 6.3.117
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/CHANGELOG.md +17 -0
- package/VERSION +1 -1
- package/docs/operations/RELEASE_NOTES.md +6 -0
- package/integrations/lifecycle/audit.ts +105 -8
- package/integrations/lifecycle/cli.ts +8 -8
- package/integrations/lifecycle/dependencyInventory.ts +92 -0
- package/integrations/lifecycle/doctor.ts +7 -0
- package/integrations/lifecycle/status.ts +7 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [6.3.117] - 2026-04-28
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`pumuki audit --json` queda accionable cuando bloquea:** la salida ahora expone `findings_count`, `blocking_findings_count` y `findings`; si el gate bloquea sin findings persistidos, emite un blocker sintético `AUDIT_BLOCKED_WITHOUT_FINDINGS` para evitar JSON no accionable.
|
|
14
|
+
- **Auditoría previa a edición:** `pumuki audit --stage=PRE_WRITE --json` queda soportado para dar a consumers como RuralGo un contrato machine-friendly antes de continuar feature work.
|
|
15
|
+
- **Tracking externo prioritario:** el reset interno queda alineado con el bloqueo vivo de RuralGo `PUMUKI-INC-109`/`PUMUKI-INC-110`.
|
|
16
|
+
|
|
17
|
+
## [6.3.116] - 2026-04-25
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Inventario real de dependencia local en `status` y `doctor`:** la línea publicada diferencia por fin la señal de seguridad Git (`trackedNodeModulesCount` / `trackedNodeModulesPaths`) del estado real de instalación local (`dependencyInventory`).
|
|
22
|
+
- **Diagnóstico consumer-facing de `pumuki`:** `status --json` y `doctor --json` exponen si existe `package.json`, lockfile, `node_modules`, declaración de `pumuki`, versión instalada y binario local.
|
|
23
|
+
- **Salida humana alineada:** `status` y `doctor` imprimen una línea compacta `dependency pumuki` para que el operador no confunda ausencia de `node_modules` trackeados con ausencia de instalación local.
|
|
24
|
+
- **Cobertura de regresión de `PUMUKI-INC-088`:** nuevas pruebas fijan el inventario de dependencia en ambas superficies lifecycle sin arrastrar el delta amplio de `develop`.
|
|
25
|
+
|
|
9
26
|
## [6.3.115] - 2026-04-24
|
|
10
27
|
|
|
11
28
|
### Fixed
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.117
|
|
@@ -6,6 +6,12 @@ This file keeps only the operational highlights and rollout notes that matter wh
|
|
|
6
6
|
|
|
7
7
|
## 2026-04 (CLI stability and macOS notifications)
|
|
8
8
|
|
|
9
|
+
### 2026-04-25 (v6.3.116)
|
|
10
|
+
|
|
11
|
+
- **Inventario local real de dependencias:** `status` y `doctor` conservan `trackedNodeModules*` como señal estricta de seguridad Git y añaden `dependencyInventory` como fuente de verdad de instalación local.
|
|
12
|
+
- **Cierre útil de `PUMUKI-INC-088`:** los consumers pueden ver si `pumuki` está declarado, instalado, con qué versión y si el binario local existe, sin inferirlo desde `git ls-files node_modules`.
|
|
13
|
+
- **Rollout recomendado:** publicar `pumuki@6.3.116`, repin inmediato en `RuralGo` y revalidar `status --json` / `doctor --json` comprobando `dependencyInventory.pumuki.installedVersion`.
|
|
14
|
+
|
|
9
15
|
### 2026-04-24 (v6.3.115)
|
|
10
16
|
|
|
11
17
|
- **`issues` canónicos también para `WARN`:** `status` y `doctor` ya no dejan `issues=[]` cuando la evidencia operativa real está en atención (`WARN`) pero aún no bloquea el gate.
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import { readEvidence } from '../evidence/readEvidence';
|
|
2
|
-
import {
|
|
2
|
+
import type { AiEvidenceV2_1, SnapshotFinding } from '../evidence/schema';
|
|
3
|
+
import { GitService, type IGitService } from '../git/GitService';
|
|
3
4
|
import { hasAllowedExtension } from '../git/gitDiffUtils';
|
|
4
5
|
import { runPlatformGate } from '../git/runPlatformGate';
|
|
5
6
|
import { evaluatePlatformGateFindings } from '../git/runPlatformGateEvaluation';
|
|
6
7
|
import { DEFAULT_FACT_FILE_EXTENSIONS } from '../git/runPlatformGateFacts';
|
|
7
|
-
import { resolvePolicyForStage } from '../gate/stagePolicies';
|
|
8
|
+
import { resolvePolicyForStage, type ResolvedStagePolicy } from '../gate/stagePolicies';
|
|
8
9
|
|
|
9
|
-
export type LifecycleAuditStage = 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
|
|
10
|
+
export type LifecycleAuditStage = 'PRE_WRITE' | 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
|
|
11
|
+
|
|
12
|
+
export type LifecycleAuditFinding = {
|
|
13
|
+
ruleId: string;
|
|
14
|
+
severity: string;
|
|
15
|
+
code: string;
|
|
16
|
+
message: string;
|
|
17
|
+
file: string;
|
|
18
|
+
lines?: SnapshotFinding['lines'];
|
|
19
|
+
blocking: boolean;
|
|
20
|
+
};
|
|
10
21
|
|
|
11
22
|
export type LifecycleAuditResult = {
|
|
12
23
|
command: 'pumuki audit';
|
|
@@ -18,14 +29,24 @@ export type LifecycleAuditResult = {
|
|
|
18
29
|
files_scanned: number | null;
|
|
19
30
|
untracked_matching_extensions_count: number;
|
|
20
31
|
snapshot_outcome: string | null;
|
|
32
|
+
findings_count: number;
|
|
33
|
+
blocking_findings_count: number;
|
|
34
|
+
findings: ReadonlyArray<LifecycleAuditFinding>;
|
|
21
35
|
policy_reconcile_hint: string;
|
|
22
36
|
};
|
|
23
37
|
|
|
38
|
+
type LifecycleAuditDependencies = {
|
|
39
|
+
git: IGitService;
|
|
40
|
+
readEvidence: typeof readEvidence;
|
|
41
|
+
resolvePolicyForStage: typeof resolvePolicyForStage;
|
|
42
|
+
runPlatformGate: typeof runPlatformGate;
|
|
43
|
+
};
|
|
44
|
+
|
|
24
45
|
const POLICY_RECONCILE_HINT =
|
|
25
46
|
'If .pumuki/policy-as-code.json signatures drift after a pumuki upgrade, run: pumuki policy reconcile --apply';
|
|
26
47
|
|
|
27
48
|
const countUntrackedMatchingExtensions = (
|
|
28
|
-
git:
|
|
49
|
+
git: Pick<IGitService, 'resolveRepoRoot' | 'runGit'>,
|
|
29
50
|
extensions: ReadonlyArray<string>
|
|
30
51
|
): number => {
|
|
31
52
|
const repoRoot = git.resolveRepoRoot();
|
|
@@ -37,13 +58,80 @@ const countUntrackedMatchingExtensions = (
|
|
|
37
58
|
.filter((path) => hasAllowedExtension(path, extensions)).length;
|
|
38
59
|
};
|
|
39
60
|
|
|
61
|
+
const isFindingBlocking = (finding: SnapshotFinding): boolean => {
|
|
62
|
+
if (typeof finding.blocking === 'boolean') {
|
|
63
|
+
return finding.blocking;
|
|
64
|
+
}
|
|
65
|
+
return finding.severity === 'CRITICAL' || finding.severity === 'ERROR';
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const toLifecycleAuditFinding = (finding: SnapshotFinding): LifecycleAuditFinding => ({
|
|
69
|
+
ruleId: finding.ruleId,
|
|
70
|
+
severity: finding.severity,
|
|
71
|
+
code: finding.code,
|
|
72
|
+
message: finding.message,
|
|
73
|
+
file: finding.file,
|
|
74
|
+
...(typeof finding.lines !== 'undefined' ? { lines: finding.lines } : {}),
|
|
75
|
+
blocking: isFindingBlocking(finding),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const buildBlockedWithoutFindingsFallback = (params: {
|
|
79
|
+
stage: LifecycleAuditStage;
|
|
80
|
+
gateExitCode: number;
|
|
81
|
+
snapshotOutcome: string | null;
|
|
82
|
+
}): LifecycleAuditFinding => ({
|
|
83
|
+
ruleId: 'audit.gate.blocked-without-findings',
|
|
84
|
+
severity: 'ERROR',
|
|
85
|
+
code: 'AUDIT_BLOCKED_WITHOUT_FINDINGS',
|
|
86
|
+
message:
|
|
87
|
+
`Audit ${params.stage} exited ${params.gateExitCode} with outcome=${params.snapshotOutcome ?? 'unknown'} ` +
|
|
88
|
+
'but produced no machine-readable findings. Re-run with this Pumuki version and inspect policy/SDD output; this fallback keeps JSON actionable.',
|
|
89
|
+
file: '.ai_evidence.json',
|
|
90
|
+
blocking: true,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const extractAuditFindings = (params: {
|
|
94
|
+
evidence: AiEvidenceV2_1 | undefined;
|
|
95
|
+
gateExitCode: number;
|
|
96
|
+
stage: LifecycleAuditStage;
|
|
97
|
+
snapshotOutcome: string | null;
|
|
98
|
+
}): ReadonlyArray<LifecycleAuditFinding> => {
|
|
99
|
+
const findings = Array.isArray(params.evidence?.snapshot.findings)
|
|
100
|
+
? params.evidence.snapshot.findings.map(toLifecycleAuditFinding)
|
|
101
|
+
: [];
|
|
102
|
+
if (findings.length > 0) {
|
|
103
|
+
return findings;
|
|
104
|
+
}
|
|
105
|
+
if (params.gateExitCode !== 0 || params.snapshotOutcome === 'BLOCK') {
|
|
106
|
+
return [
|
|
107
|
+
buildBlockedWithoutFindingsFallback({
|
|
108
|
+
stage: params.stage,
|
|
109
|
+
gateExitCode: params.gateExitCode,
|
|
110
|
+
snapshotOutcome: params.snapshotOutcome,
|
|
111
|
+
}),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
return [];
|
|
115
|
+
};
|
|
116
|
+
|
|
40
117
|
export const runLifecycleAudit = async (params: {
|
|
41
118
|
stage: LifecycleAuditStage;
|
|
42
119
|
auditMode: 'gate' | 'engine';
|
|
120
|
+
dependencies?: Partial<LifecycleAuditDependencies>;
|
|
43
121
|
}): Promise<LifecycleAuditResult> => {
|
|
44
|
-
const
|
|
122
|
+
const activeDependencies: LifecycleAuditDependencies = {
|
|
123
|
+
git: new GitService(),
|
|
124
|
+
readEvidence,
|
|
125
|
+
resolvePolicyForStage,
|
|
126
|
+
runPlatformGate,
|
|
127
|
+
...params.dependencies,
|
|
128
|
+
};
|
|
129
|
+
const git = activeDependencies.git;
|
|
45
130
|
const repoRoot = git.resolveRepoRoot();
|
|
46
|
-
const resolved = resolvePolicyForStage(
|
|
131
|
+
const resolved: ResolvedStagePolicy = activeDependencies.resolvePolicyForStage(
|
|
132
|
+
params.stage,
|
|
133
|
+
repoRoot
|
|
134
|
+
);
|
|
47
135
|
const extensions = DEFAULT_FACT_FILE_EXTENSIONS;
|
|
48
136
|
const untrackedMatchingExtensionsCount = countUntrackedMatchingExtensions(git, extensions);
|
|
49
137
|
|
|
@@ -76,8 +164,8 @@ export const runLifecycleAudit = async (params: {
|
|
|
76
164
|
auditMode: 'gate' as const,
|
|
77
165
|
};
|
|
78
166
|
|
|
79
|
-
const gateExitCode = await runPlatformGate(gateParams);
|
|
80
|
-
const evidence = readEvidence(repoRoot);
|
|
167
|
+
const gateExitCode = await activeDependencies.runPlatformGate(gateParams);
|
|
168
|
+
const evidence = activeDependencies.readEvidence(repoRoot);
|
|
81
169
|
const filesScanned =
|
|
82
170
|
typeof evidence?.snapshot.files_scanned === 'number' &&
|
|
83
171
|
Number.isFinite(evidence.snapshot.files_scanned)
|
|
@@ -85,6 +173,12 @@ export const runLifecycleAudit = async (params: {
|
|
|
85
173
|
: null;
|
|
86
174
|
const snapshotOutcome =
|
|
87
175
|
typeof evidence?.snapshot.outcome === 'string' ? evidence.snapshot.outcome : null;
|
|
176
|
+
const findings = extractAuditFindings({
|
|
177
|
+
evidence,
|
|
178
|
+
gateExitCode,
|
|
179
|
+
stage: params.stage,
|
|
180
|
+
snapshotOutcome,
|
|
181
|
+
});
|
|
88
182
|
|
|
89
183
|
return {
|
|
90
184
|
command: 'pumuki audit',
|
|
@@ -96,6 +190,9 @@ export const runLifecycleAudit = async (params: {
|
|
|
96
190
|
files_scanned: filesScanned,
|
|
97
191
|
untracked_matching_extensions_count: untrackedMatchingExtensionsCount,
|
|
98
192
|
snapshot_outcome: snapshotOutcome,
|
|
193
|
+
findings_count: findings.length,
|
|
194
|
+
blocking_findings_count: findings.filter((finding) => finding.blocking).length,
|
|
195
|
+
findings,
|
|
99
196
|
policy_reconcile_hint: POLICY_RECONCILE_HINT,
|
|
100
197
|
};
|
|
101
198
|
};
|
|
@@ -188,7 +188,7 @@ Pumuki lifecycle commands:
|
|
|
188
188
|
pumuki remove
|
|
189
189
|
pumuki update [--latest|--spec=<package-spec>]
|
|
190
190
|
pumuki doctor [--remote-checks] [--deep] [--parity] [--json]
|
|
191
|
-
pumuki audit [--stage=PRE_COMMIT|PRE_PUSH|CI] [--engine] [--json]
|
|
191
|
+
pumuki audit [--stage=PRE_WRITE|PRE_COMMIT|PRE_PUSH|CI] [--engine] [--json]
|
|
192
192
|
pumuki status [--json] [--remote-checks]
|
|
193
193
|
pumuki watch [--stage=PRE_COMMIT|PRE_PUSH|CI] [--scope=workingTree|staged|repoAndStaged|repo] [--severity=critical|high|medium|low] [--interval-ms=<n>] [--notify-cooldown-ms=<n>] [--no-notify] [--once|--iterations=<n>] [--json]
|
|
194
194
|
pumuki loop run --objective=<text> [--max-attempts=<n>] [--json]
|
|
@@ -266,13 +266,7 @@ const parseSddStage = (value?: string): SddStage => {
|
|
|
266
266
|
};
|
|
267
267
|
|
|
268
268
|
const parseAuditStage = (value?: string): LifecycleAuditStage => {
|
|
269
|
-
|
|
270
|
-
if (stage === 'PRE_WRITE') {
|
|
271
|
-
throw new Error(
|
|
272
|
-
'PRE_WRITE is not supported for "pumuki audit". Use PRE_COMMIT, PRE_PUSH or CI (aliases GREEN, REFACTOR, CLOSE).'
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
return stage;
|
|
269
|
+
return parseSddStage(value);
|
|
276
270
|
};
|
|
277
271
|
|
|
278
272
|
const parseSddEvidencePath = (value: string): string => {
|
|
@@ -1555,6 +1549,9 @@ const printDoctorReport = (
|
|
|
1555
1549
|
writeInfo(
|
|
1556
1550
|
`[pumuki] tracked node_modules paths: ${report.trackedNodeModulesPaths.length}`
|
|
1557
1551
|
);
|
|
1552
|
+
writeInfo(
|
|
1553
|
+
`[pumuki] dependency pumuki: declared=${report.dependencyInventory.pumuki.declared ? 'yes' : 'no'}, installed=${report.dependencyInventory.pumuki.installed ? 'yes' : 'no'}, version=${report.dependencyInventory.pumuki.installedVersion ?? 'unknown'}, bin=${report.dependencyInventory.pumuki.binPresent ? 'present' : 'missing'}`
|
|
1554
|
+
);
|
|
1558
1555
|
writeInfo(
|
|
1559
1556
|
`[pumuki] hook pre-commit: ${report.hookStatus['pre-commit'].managedBlockPresent ? 'managed' : 'missing'}`
|
|
1560
1557
|
);
|
|
@@ -2428,6 +2425,9 @@ export const runLifecycleCli = async (
|
|
|
2428
2425
|
writeInfo(
|
|
2429
2426
|
`[pumuki] tracked node_modules paths: ${status.trackedNodeModulesCount}`
|
|
2430
2427
|
);
|
|
2428
|
+
writeInfo(
|
|
2429
|
+
`[pumuki] dependency pumuki: declared=${status.dependencyInventory.pumuki.declared ? 'yes' : 'no'}, installed=${status.dependencyInventory.pumuki.installed ? 'yes' : 'no'}, version=${status.dependencyInventory.pumuki.installedVersion ?? 'unknown'}, bin=${status.dependencyInventory.pumuki.binPresent ? 'present' : 'missing'}`
|
|
2430
|
+
);
|
|
2431
2431
|
writeInfo(
|
|
2432
2432
|
`[pumuki] policy-as-code: PRE_COMMIT=${status.policyValidation.stages.PRE_COMMIT.validationCode ?? 'n/a'} strict=${status.policyValidation.stages.PRE_COMMIT.strict ? 'yes' : 'no'} ` +
|
|
2433
2433
|
`PRE_PUSH=${status.policyValidation.stages.PRE_PUSH.validationCode ?? 'n/a'} strict=${status.policyValidation.stages.PRE_PUSH.strict ? 'yes' : 'no'} ` +
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import packageJson from '../../package.json';
|
|
4
|
+
|
|
5
|
+
export type LifecycleDependencyInventory = {
|
|
6
|
+
nodeModulesPresent: boolean;
|
|
7
|
+
packageJsonPresent: boolean;
|
|
8
|
+
lockfilePresent: boolean;
|
|
9
|
+
pumuki: {
|
|
10
|
+
declared: boolean;
|
|
11
|
+
declaredRange: string | null;
|
|
12
|
+
installed: boolean;
|
|
13
|
+
installedVersion: string | null;
|
|
14
|
+
packageJsonPath: string | null;
|
|
15
|
+
binPath: string | null;
|
|
16
|
+
binPresent: boolean;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const readJsonRecord = (path: string): Record<string, unknown> | null => {
|
|
21
|
+
if (!existsSync(path)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown;
|
|
27
|
+
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
|
|
28
|
+
? (parsed as Record<string, unknown>)
|
|
29
|
+
: null;
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const readDependencyRange = (
|
|
36
|
+
manifest: Record<string, unknown> | null,
|
|
37
|
+
dependencyName: string
|
|
38
|
+
): string | null => {
|
|
39
|
+
if (!manifest) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const section of ['dependencies', 'devDependencies', 'optionalDependencies'] as const) {
|
|
44
|
+
const dependencies = manifest[section];
|
|
45
|
+
if (typeof dependencies !== 'object' || dependencies === null || Array.isArray(dependencies)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const value = (dependencies as Record<string, unknown>)[dependencyName];
|
|
50
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
51
|
+
return value.trim();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const readInstalledVersion = (installedManifest: Record<string, unknown> | null): string | null => {
|
|
59
|
+
const version = installedManifest?.version;
|
|
60
|
+
return typeof version === 'string' && version.trim().length > 0 ? version.trim() : null;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const readLifecycleDependencyInventory = (
|
|
64
|
+
repoRoot: string
|
|
65
|
+
): LifecycleDependencyInventory => {
|
|
66
|
+
const manifestPath = join(repoRoot, 'package.json');
|
|
67
|
+
const nodeModulesPath = join(repoRoot, 'node_modules');
|
|
68
|
+
const lockfilePath = join(repoRoot, 'package-lock.json');
|
|
69
|
+
const installedPackageJsonPath = join(nodeModulesPath, packageJson.name, 'package.json');
|
|
70
|
+
const binName = process.platform === 'win32' ? `${packageJson.name}.cmd` : packageJson.name;
|
|
71
|
+
const binPath = join(nodeModulesPath, '.bin', binName);
|
|
72
|
+
const manifest = readJsonRecord(manifestPath);
|
|
73
|
+
const installedManifest = readJsonRecord(installedPackageJsonPath);
|
|
74
|
+
const declaredRange = readDependencyRange(manifest, packageJson.name);
|
|
75
|
+
const installedVersion = readInstalledVersion(installedManifest);
|
|
76
|
+
const binPresent = existsSync(binPath);
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
nodeModulesPresent: existsSync(nodeModulesPath),
|
|
80
|
+
packageJsonPresent: existsSync(manifestPath),
|
|
81
|
+
lockfilePresent: existsSync(lockfilePath),
|
|
82
|
+
pumuki: {
|
|
83
|
+
declared: declaredRange !== null,
|
|
84
|
+
declaredRange,
|
|
85
|
+
installed: installedVersion !== null,
|
|
86
|
+
installedVersion,
|
|
87
|
+
packageJsonPath: installedVersion !== null ? `node_modules/${packageJson.name}/package.json` : null,
|
|
88
|
+
binPath: binPresent ? `node_modules/.bin/${binName}` : null,
|
|
89
|
+
binPresent,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
};
|
|
@@ -16,6 +16,10 @@ import {
|
|
|
16
16
|
evaluateOpenSpecCompatibility,
|
|
17
17
|
isOpenSpecProjectInitialized,
|
|
18
18
|
} from '../sdd/openSpecCli';
|
|
19
|
+
import {
|
|
20
|
+
readLifecycleDependencyInventory,
|
|
21
|
+
type LifecycleDependencyInventory,
|
|
22
|
+
} from './dependencyInventory';
|
|
19
23
|
|
|
20
24
|
export type DoctorIssueSeverity = 'warning' | 'error';
|
|
21
25
|
|
|
@@ -91,6 +95,7 @@ export type LifecycleDoctorReport = {
|
|
|
91
95
|
packageVersion: string;
|
|
92
96
|
version: ReturnType<typeof buildLifecycleVersionReport>;
|
|
93
97
|
lifecycleState: LifecycleState;
|
|
98
|
+
dependencyInventory: LifecycleDependencyInventory;
|
|
94
99
|
trackedNodeModulesPaths: ReadonlyArray<string>;
|
|
95
100
|
hookStatus: ReturnType<typeof getPumukiHooksStatus>;
|
|
96
101
|
hooksDirectory: string;
|
|
@@ -842,6 +847,7 @@ export const runLifecycleDoctor = (params?: {
|
|
|
842
847
|
const cwd = params?.cwd ?? process.cwd();
|
|
843
848
|
const repoRoot = git.resolveRepoRoot(cwd);
|
|
844
849
|
const trackedNodeModulesPaths = git.trackedNodeModulesPaths(repoRoot);
|
|
850
|
+
const dependencyInventory = readLifecycleDependencyInventory(repoRoot);
|
|
845
851
|
const hooksDirectory = resolvePumukiHooksDirectory(repoRoot);
|
|
846
852
|
const hookStatus = getPumukiHooksStatus(repoRoot);
|
|
847
853
|
const lifecycleState = readLifecycleState(git, repoRoot);
|
|
@@ -886,6 +892,7 @@ export const runLifecycleDoctor = (params?: {
|
|
|
886
892
|
packageVersion: version.effective,
|
|
887
893
|
version,
|
|
888
894
|
lifecycleState,
|
|
895
|
+
dependencyInventory,
|
|
889
896
|
trackedNodeModulesPaths,
|
|
890
897
|
hookStatus,
|
|
891
898
|
hooksDirectory: hooksDirectory.path,
|
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
} from './policyValidationSnapshot';
|
|
14
14
|
import { readLifecycleState, type LifecycleState } from './state';
|
|
15
15
|
import type { DoctorIssue } from './doctor';
|
|
16
|
+
import {
|
|
17
|
+
readLifecycleDependencyInventory,
|
|
18
|
+
type LifecycleDependencyInventory,
|
|
19
|
+
} from './dependencyInventory';
|
|
16
20
|
|
|
17
21
|
export type LifecycleStatus = {
|
|
18
22
|
repoRoot: string;
|
|
@@ -23,6 +27,7 @@ export type LifecycleStatus = {
|
|
|
23
27
|
hooksDirectory: string;
|
|
24
28
|
hooksDirectoryResolution: 'git-rev-parse' | 'git-config' | 'default';
|
|
25
29
|
trackedNodeModulesCount: number;
|
|
30
|
+
dependencyInventory: LifecycleDependencyInventory;
|
|
26
31
|
policyValidation: LifecyclePolicyValidationSnapshot;
|
|
27
32
|
experimentalFeatures: LifecycleExperimentalFeaturesSnapshot;
|
|
28
33
|
issues: ReadonlyArray<DoctorIssue>;
|
|
@@ -79,6 +84,7 @@ export const readLifecycleStatus = (params?: {
|
|
|
79
84
|
const repoRoot = git.resolveRepoRoot(cwd);
|
|
80
85
|
const hooksDirectory = resolvePumukiHooksDirectory(repoRoot);
|
|
81
86
|
const trackedNodeModulesCount = git.trackedNodeModulesPaths(repoRoot).length;
|
|
87
|
+
const dependencyInventory = readLifecycleDependencyInventory(repoRoot);
|
|
82
88
|
const lifecycleState = readLifecycleState(git, repoRoot);
|
|
83
89
|
const version = buildLifecycleVersionReport({
|
|
84
90
|
repoRoot,
|
|
@@ -97,6 +103,7 @@ export const readLifecycleStatus = (params?: {
|
|
|
97
103
|
hooksDirectory: hooksDirectory.path,
|
|
98
104
|
hooksDirectoryResolution: hooksDirectory.source,
|
|
99
105
|
trackedNodeModulesCount,
|
|
106
|
+
dependencyInventory,
|
|
100
107
|
policyValidation,
|
|
101
108
|
experimentalFeatures,
|
|
102
109
|
issues,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.117",
|
|
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": {
|