pumuki 6.3.129 → 6.3.130
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/AGENTS.md +16 -1
- package/CHANGELOG.md +9 -0
- package/README.md +14 -10
- package/VERSION +1 -1
- package/core/facts/detectors/text/android.test.ts +2583 -24
- package/core/facts/detectors/text/android.ts +4633 -34
- package/core/facts/detectors/typescript/index.test.ts +3639 -73
- package/core/facts/detectors/typescript/index.ts +4819 -270
- package/core/facts/extractHeuristicFacts.ts +271 -6
- package/core/rules/presets/heuristics/android.test.ts +314 -1
- package/core/rules/presets/heuristics/android.ts +1220 -50
- package/core/rules/presets/heuristics/typescript.test.ts +158 -2
- package/core/rules/presets/heuristics/typescript.ts +508 -0
- package/docs/README.md +3 -3
- package/docs/operations/RELEASE_NOTES.md +7 -1
- package/docs/operations/framework-menu-consumer-walkthrough.md +18 -15
- package/docs/product/API_REFERENCE.md +1 -1
- package/docs/product/USAGE.md +1 -1
- package/docs/validation/README.md +3 -1
- package/integrations/config/skillsCompilerTemplates.test.ts +131 -0
- package/integrations/config/skillsCompilerTemplates.ts +953 -7
- package/integrations/config/skillsDetectorRegistry.ts +451 -8
- package/integrations/config/skillsMarkdownRules.ts +884 -2
- package/integrations/evidence/buildEvidence.ts +29 -1
- package/integrations/evidence/platformSummary.test.ts +73 -9
- package/integrations/evidence/platformSummary.ts +165 -7
- package/integrations/evidence/repoState.ts +3 -0
- package/integrations/evidence/schema.ts +18 -0
- package/integrations/evidence/trackingContract.ts +17 -0
- package/integrations/evidence/writeEvidence.test.ts +3 -0
- package/integrations/evidence/writeEvidence.ts +29 -1
- package/integrations/gate/evaluateAiGate.ts +251 -8
- package/integrations/gate/governanceActionCatalog.ts +275 -0
- package/integrations/gate/remediationCatalog.ts +8 -0
- package/integrations/git/runPlatformGate.ts +9 -1
- package/integrations/lifecycle/adapter.ts +24 -0
- package/integrations/lifecycle/bootstrapManifest.ts +248 -0
- package/integrations/lifecycle/cliGovernanceConsole.ts +69 -0
- package/integrations/lifecycle/governanceNextAction.ts +171 -0
- package/integrations/lifecycle/governanceObservationSnapshot.ts +369 -0
- package/integrations/lifecycle/packageInfo.ts +118 -1
- package/integrations/lifecycle/state.ts +8 -1
- package/integrations/lifecycle/trackingState.ts +403 -0
- package/integrations/lifecycle/watch.ts +1 -1
- package/integrations/mcp/aiGateCheck.ts +194 -10
- package/integrations/mcp/alignedPlatformGate.ts +232 -0
- package/integrations/mcp/enterpriseServer.ts +19 -3
- package/integrations/mcp/preFlightCheck.ts +66 -3
- package/integrations/mcp/readMcpPrePushStdin.ts +7 -0
- package/package.json +1 -1
- package/scripts/build-ruralgo-s1-evidence-pack.ts +85 -0
- package/scripts/check-tracking-single-active.sh +1 -1
- package/scripts/framework-menu-advanced-view-lib.ts +49 -0
- package/scripts/framework-menu-consumer-actions-lib.ts +32 -32
- package/scripts/framework-menu-consumer-preflight-render.ts +10 -0
- package/scripts/framework-menu-consumer-preflight-run.ts +23 -0
- package/scripts/framework-menu-consumer-preflight-types.ts +12 -0
- package/scripts/framework-menu-consumer-runtime-actions.ts +11 -5
- package/scripts/framework-menu-consumer-runtime-audit.ts +0 -28
- package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +118 -42
- package/scripts/framework-menu-consumer-runtime-lib.ts +38 -0
- package/scripts/framework-menu-consumer-runtime-menu.ts +55 -15
- package/scripts/framework-menu-consumer-runtime-types.ts +4 -0
- package/scripts/framework-menu-evidence-summary-read.ts +17 -1
- package/scripts/framework-menu-evidence-summary-types.ts +3 -0
- package/scripts/framework-menu-layout-data.ts +3 -23
- package/scripts/framework-menu-system-notifications-macos-applescript-dialog.ts +1 -1
- package/scripts/framework-menu-system-notifications-macos-dialog-payload.ts +14 -2
- package/scripts/framework-menu-system-notifications-macos-swift-source.ts +1 -1
- package/scripts/framework-menu-system-notifications-payloads-blocked.ts +128 -4
- package/scripts/framework-menu-system-notifications-payloads.ts +8 -1
- package/scripts/framework-menu-system-notifications-text.ts +7 -1
- package/scripts/framework-menu.ts +37 -2
- package/scripts/package-install-smoke-consumer-git-repo-lib.ts +10 -1
- package/scripts/package-install-smoke-consumer-npm-lib.ts +46 -9
- package/scripts/ruralgo-s1-evidence-pack-lib.ts +200 -0
- package/skills.lock.json +613 -698
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
renderBadge,
|
|
14
14
|
} from './framework-menu-ui-components-lib';
|
|
15
15
|
import { isMenuUiV2Enabled } from './framework-menu-ui-version-lib';
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from './framework-menu-consumer-runtime-types';
|
|
16
|
+
import { buildGovernanceConsoleSummaryLines } from '../integrations/lifecycle/cliGovernanceConsole';
|
|
17
|
+
import type { GovernanceConsoleSnapshot } from '../integrations/lifecycle/cliGovernanceConsole';
|
|
18
|
+
import type { ConsumerPreflightResult } from './framework-menu-consumer-preflight-types';
|
|
19
|
+
import type { ConsumerAction, ConsumerRuntimeWrite } from './framework-menu-consumer-runtime-types';
|
|
20
20
|
|
|
21
21
|
const buildConsumerRuntimeMenuStatus = (
|
|
22
22
|
menuSummary: FrameworkMenuEvidenceSummary
|
|
@@ -32,24 +32,35 @@ const buildConsumerRuntimeMenuStatus = (
|
|
|
32
32
|
: { level: 'info', label: (menuSummary.outcome ?? 'UNKNOWN').trim().toUpperCase() };
|
|
33
33
|
|
|
34
34
|
export const renderConsumerRuntimeClassicMenu = (
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
params: {
|
|
36
|
+
actions: ReadonlyArray<ConsumerAction>;
|
|
37
|
+
repoRoot: string;
|
|
38
|
+
useColor: () => boolean;
|
|
39
|
+
}
|
|
37
40
|
): string => {
|
|
38
|
-
const
|
|
41
|
+
const menuSummary = readEvidenceSummaryForMenu(params.repoRoot);
|
|
42
|
+
const menuStatus = buildConsumerRuntimeMenuStatus(menuSummary);
|
|
43
|
+
const tokens = buildCliDesignTokens({
|
|
44
|
+
width: resolveLegacyPanelOuterWidth(),
|
|
45
|
+
color: params.useColor(),
|
|
46
|
+
});
|
|
47
|
+
const groupedActions = resolveConsumerMenuLayout(params.actions);
|
|
39
48
|
const lines = [
|
|
40
49
|
'PUMUKI — Hook-System (run: npx ast-hooks)',
|
|
41
50
|
'AST Intelligence System Overview',
|
|
42
|
-
'A. Switch to advanced menu',
|
|
43
51
|
'',
|
|
44
52
|
...groupedActions.flatMap((group) => [
|
|
45
|
-
group.title,
|
|
46
|
-
...group.items.map((item) =>
|
|
53
|
+
...(group.title.trim().length > 0 ? [group.title] : []),
|
|
54
|
+
...group.items.map((item) => renderActionRow({
|
|
55
|
+
id: item.id,
|
|
56
|
+
label: item.action.label,
|
|
57
|
+
})),
|
|
47
58
|
'',
|
|
48
59
|
]),
|
|
49
60
|
];
|
|
50
61
|
return renderLegacyPanel(lines, {
|
|
51
62
|
width: resolveLegacyPanelOuterWidth(),
|
|
52
|
-
color: useColor(),
|
|
63
|
+
color: params.useColor(),
|
|
53
64
|
});
|
|
54
65
|
};
|
|
55
66
|
|
|
@@ -58,6 +69,8 @@ export const renderConsumerRuntimeModernMenu = (
|
|
|
58
69
|
actions: ReadonlyArray<ConsumerAction>;
|
|
59
70
|
repoRoot: string;
|
|
60
71
|
useColor: () => boolean;
|
|
72
|
+
preflight?: ConsumerPreflightResult | null;
|
|
73
|
+
governanceConsole?: GovernanceConsoleSnapshot | null;
|
|
61
74
|
}
|
|
62
75
|
): string => {
|
|
63
76
|
const menuSummary = readEvidenceSummaryForMenu(params.repoRoot);
|
|
@@ -67,14 +80,33 @@ export const renderConsumerRuntimeModernMenu = (
|
|
|
67
80
|
color: params.useColor(),
|
|
68
81
|
});
|
|
69
82
|
const groupedActions = resolveConsumerMenuLayout(params.actions);
|
|
83
|
+
const governanceConsole =
|
|
84
|
+
params.preflight
|
|
85
|
+
? {
|
|
86
|
+
governanceObservation: params.preflight.governanceObservation,
|
|
87
|
+
governanceNextAction: params.preflight.governanceNextAction,
|
|
88
|
+
policyValidation: params.preflight.policyValidation,
|
|
89
|
+
experimentalFeatures: params.preflight.experimentalFeatures,
|
|
90
|
+
}
|
|
91
|
+
: params.governanceConsole;
|
|
70
92
|
const lines = [
|
|
71
93
|
'PUMUKI — Hook-System (run: npx ast-hooks)',
|
|
72
94
|
'AST Intelligence System Overview',
|
|
73
|
-
|
|
74
|
-
|
|
95
|
+
...(governanceConsole
|
|
96
|
+
? [
|
|
97
|
+
'',
|
|
98
|
+
'Governance Console',
|
|
99
|
+
...buildGovernanceConsoleSummaryLines({
|
|
100
|
+
governanceObservation: governanceConsole.governanceObservation,
|
|
101
|
+
governanceNextAction: governanceConsole.governanceNextAction,
|
|
102
|
+
policyValidation: governanceConsole.policyValidation,
|
|
103
|
+
experimentalFeatures: governanceConsole.experimentalFeatures,
|
|
104
|
+
}).map((line) => ` ${line}`),
|
|
105
|
+
]
|
|
106
|
+
: []),
|
|
75
107
|
'',
|
|
76
108
|
...groupedActions.flatMap((group) => [
|
|
77
|
-
group.title,
|
|
109
|
+
...(group.title.trim().length > 0 ? [group.title] : []),
|
|
78
110
|
...group.items.map((item) => renderActionRow({
|
|
79
111
|
id: item.id,
|
|
80
112
|
label: item.action.label,
|
|
@@ -93,8 +125,14 @@ export const printConsumerRuntimeMenu = (params: {
|
|
|
93
125
|
repoRoot: string;
|
|
94
126
|
useColor: () => boolean;
|
|
95
127
|
write: ConsumerRuntimeWrite;
|
|
128
|
+
preflight?: ConsumerPreflightResult | null;
|
|
129
|
+
governanceConsole?: GovernanceConsoleSnapshot | null;
|
|
96
130
|
}): void => {
|
|
97
|
-
const classicMenu = renderConsumerRuntimeClassicMenu(
|
|
131
|
+
const classicMenu = renderConsumerRuntimeClassicMenu({
|
|
132
|
+
actions: params.actions,
|
|
133
|
+
repoRoot: params.repoRoot,
|
|
134
|
+
useColor: params.useColor,
|
|
135
|
+
});
|
|
98
136
|
if (!isMenuUiV2Enabled()) {
|
|
99
137
|
params.write(`\n${classicMenu}\n`);
|
|
100
138
|
return;
|
|
@@ -105,6 +143,8 @@ export const printConsumerRuntimeMenu = (params: {
|
|
|
105
143
|
actions: params.actions,
|
|
106
144
|
repoRoot: params.repoRoot,
|
|
107
145
|
useColor: params.useColor,
|
|
146
|
+
preflight: params.preflight,
|
|
147
|
+
governanceConsole: params.governanceConsole,
|
|
108
148
|
})}\n`);
|
|
109
149
|
} catch {
|
|
110
150
|
params.write('\n[pumuki][menu-ui-v2] Render failed. Falling back to classic menu.\n');
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { FrameworkMenuEvidenceSummary } from './framework-menu-evidence-summary-lib';
|
|
2
|
+
import type { ConsumerPreflightResult } from './framework-menu-consumer-preflight-types';
|
|
2
3
|
import type {
|
|
3
4
|
PumukiCriticalNotificationEvent,
|
|
4
5
|
SystemNotificationEmitResult,
|
|
5
6
|
} from './framework-menu-system-notifications-lib';
|
|
7
|
+
import type { GovernanceConsoleSnapshot } from '../integrations/lifecycle/cliGovernanceConsole';
|
|
6
8
|
|
|
7
9
|
export type ConsumerAction = {
|
|
8
10
|
id: string;
|
|
@@ -40,6 +42,7 @@ export type ConsumerMenuRuntimeParams = {
|
|
|
40
42
|
stage: 'PRE_COMMIT' | 'PRE_PUSH'
|
|
41
43
|
) => Promise<string | void> | string | void;
|
|
42
44
|
emitSystemNotification?: ConsumerRuntimeEmitNotification;
|
|
45
|
+
readGovernanceConsole?: () => GovernanceConsoleSnapshot | null;
|
|
43
46
|
write: ConsumerRuntimeWrite;
|
|
44
47
|
};
|
|
45
48
|
|
|
@@ -47,6 +50,7 @@ export type ConsumerMenuRuntime = {
|
|
|
47
50
|
actions: ReadonlyArray<ConsumerAction>;
|
|
48
51
|
printMenu: () => void;
|
|
49
52
|
readCurrentSummary: () => FrameworkMenuEvidenceSummary | null;
|
|
53
|
+
readLastPreflight: () => ConsumerPreflightResult | null;
|
|
50
54
|
};
|
|
51
55
|
|
|
52
56
|
export type ConsumerRuntimeScope = 'staged' | 'unstaged' | 'workingTree';
|
|
@@ -43,6 +43,13 @@ const sumLegacySeverityBand = (row: { by_severity?: unknown }): number => {
|
|
|
43
43
|
return total;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
const optionalNonNegativeInt = (value: unknown): number | undefined => {
|
|
47
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return Math.max(0, Math.trunc(value));
|
|
51
|
+
};
|
|
52
|
+
|
|
46
53
|
const parsePlatformAuditRows = (
|
|
47
54
|
snapshot: EvidenceSnapshot
|
|
48
55
|
): ReadonlyArray<FrameworkMenuEvidencePlatformRow> => {
|
|
@@ -57,7 +64,16 @@ const parsePlatformAuditRows = (
|
|
|
57
64
|
}
|
|
58
65
|
const platform = toStringOrNull((entry as { platform?: unknown }).platform) ?? 'Other';
|
|
59
66
|
const violations = sumLegacySeverityBand(entry as { by_severity?: unknown });
|
|
60
|
-
|
|
67
|
+
const activeRules = optionalNonNegativeInt((entry as { active_rules?: unknown }).active_rules);
|
|
68
|
+
const evaluatedRules = optionalNonNegativeInt(
|
|
69
|
+
(entry as { evaluated_rules?: unknown }).evaluated_rules
|
|
70
|
+
);
|
|
71
|
+
rows.push({
|
|
72
|
+
platform,
|
|
73
|
+
violations,
|
|
74
|
+
...(typeof activeRules === 'number' ? { activeRules } : {}),
|
|
75
|
+
...(typeof evaluatedRules === 'number' ? { evaluatedRules } : {}),
|
|
76
|
+
});
|
|
61
77
|
}
|
|
62
78
|
return rows;
|
|
63
79
|
};
|
|
@@ -19,6 +19,7 @@ export type EvidenceSnapshot = {
|
|
|
19
19
|
outcome?: unknown;
|
|
20
20
|
findings?: unknown;
|
|
21
21
|
platforms?: unknown;
|
|
22
|
+
rules_coverage?: unknown;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
export type EvidenceSeverityMetrics = {
|
|
@@ -28,6 +29,8 @@ export type EvidenceSeverityMetrics = {
|
|
|
28
29
|
export type FrameworkMenuEvidencePlatformRow = {
|
|
29
30
|
platform: string;
|
|
30
31
|
violations: number;
|
|
32
|
+
activeRules?: number;
|
|
33
|
+
evaluatedRules?: number;
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
export type FrameworkMenuEvidenceSummary = {
|
|
@@ -2,29 +2,9 @@ import type { MenuLayoutGroup } from './framework-menu-layout-types';
|
|
|
2
2
|
|
|
3
3
|
export const CONSUMER_MENU_LAYOUT: ReadonlyArray<MenuLayoutGroup> = [
|
|
4
4
|
{
|
|
5
|
-
key: '
|
|
6
|
-
title: '
|
|
7
|
-
itemIds: ['1', '2', '3', '4'],
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
key: 'engine-working-tree-no-preflight',
|
|
11
|
-
title: 'Engine · working tree (no preflight)',
|
|
12
|
-
itemIds: ['11', '12', '13', '14'],
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
key: 'export',
|
|
16
|
-
title: 'Legacy Read-Only Export',
|
|
17
|
-
itemIds: ['8'],
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
key: 'legacy-read-only-diagnostics',
|
|
21
|
-
title: 'Legacy Read-Only Diagnostics',
|
|
22
|
-
itemIds: ['5', '6', '7', '9'],
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
key: 'system',
|
|
26
|
-
title: 'System',
|
|
27
|
-
itemIds: ['10'],
|
|
5
|
+
key: 'ast-intelligence-legacy',
|
|
6
|
+
title: '',
|
|
7
|
+
itemIds: ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
|
|
28
8
|
},
|
|
29
9
|
];
|
|
30
10
|
|
|
@@ -13,6 +13,6 @@ export const buildDisplayDialogScript = (params: {
|
|
|
13
13
|
const title = escapeAppleScriptString(params.title);
|
|
14
14
|
const cause = escapeAppleScriptString(params.cause);
|
|
15
15
|
const remediation = escapeAppleScriptString(params.remediation);
|
|
16
|
-
const message = escapeAppleScriptString(
|
|
16
|
+
const message = escapeAppleScriptString(`${cause}\n\n${remediation}`);
|
|
17
17
|
return `display dialog "${message}" with title "${title}" buttons {"${BLOCKED_DIALOG_DISABLE}", "${BLOCKED_DIALOG_MUTE_30}", "${BLOCKED_DIALOG_KEEP}"} default button "${BLOCKED_DIALOG_KEEP}" with icon stop giving up after 15`;
|
|
18
18
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { PumukiCriticalNotificationEvent } from './framework-menu-system-notifications-types';
|
|
2
2
|
import {
|
|
3
3
|
resolveBlockedCauseSummary,
|
|
4
|
+
resolveBlockedCommand,
|
|
5
|
+
resolveBlockedImpactSummary,
|
|
4
6
|
resolveBlockedRemediation,
|
|
5
7
|
resolveProjectLabel,
|
|
6
8
|
} from './framework-menu-system-notifications-payloads';
|
|
@@ -17,8 +19,18 @@ export const buildBlockedDialogPayload = (params: {
|
|
|
17
19
|
env: NodeJS.ProcessEnv;
|
|
18
20
|
}): BlockedDialogPayload => {
|
|
19
21
|
const causeCode = params.event.causeCode ?? 'GATE_BLOCKED';
|
|
20
|
-
const cause =
|
|
21
|
-
|
|
22
|
+
const cause = [
|
|
23
|
+
`Causa: ${resolveBlockedCauseSummary(params.event, causeCode)}`,
|
|
24
|
+
`Impacto: ${resolveBlockedImpactSummary(params.event, causeCode)}`,
|
|
25
|
+
].join('\n');
|
|
26
|
+
const remediation = [
|
|
27
|
+
`Comando: ${resolveBlockedCommand({
|
|
28
|
+
event: params.event,
|
|
29
|
+
repoRoot: params.repoRoot,
|
|
30
|
+
causeCode,
|
|
31
|
+
})}`,
|
|
32
|
+
`Siguiente acción: ${resolveBlockedRemediation(params.event, causeCode)}`,
|
|
33
|
+
].join('\n');
|
|
22
34
|
const projectLabel = resolveProjectLabel({
|
|
23
35
|
repoRoot: params.repoRoot,
|
|
24
36
|
projectLabel: params.env.PUMUKI_PROJECT_LABEL,
|
|
@@ -41,7 +41,7 @@ final class DialogAppDelegate: NSObject, NSApplicationDelegate {
|
|
|
41
41
|
|
|
42
42
|
let alert = NSAlert()
|
|
43
43
|
alert.messageText = config.title
|
|
44
|
-
alert.informativeText = "
|
|
44
|
+
alert.informativeText = "\(config.cause)\n\n\(config.remediation)"
|
|
45
45
|
alert.alertStyle = .critical
|
|
46
46
|
alert.addButton(withTitle: config.keepButton)
|
|
47
47
|
alert.addButton(withTitle: config.muteButton)
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
PumukiCriticalNotificationEvent,
|
|
3
3
|
SystemNotificationPayload,
|
|
4
4
|
} from './framework-menu-system-notifications-types';
|
|
5
|
+
import { getCurrentPumukiVersion } from '../integrations/lifecycle/packageInfo';
|
|
5
6
|
import { resolveBlockedCauseSummary } from './framework-menu-system-notifications-cause';
|
|
6
7
|
import { resolveBlockedRemediation } from './framework-menu-system-notifications-remediation';
|
|
7
8
|
import { truncateNotificationText } from './framework-menu-system-notifications-text';
|
|
@@ -13,20 +14,143 @@ export {
|
|
|
13
14
|
resolveBlockedRemediation,
|
|
14
15
|
} from './framework-menu-system-notifications-remediation';
|
|
15
16
|
|
|
17
|
+
export const resolveBlockedImpactSummary = (
|
|
18
|
+
event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>,
|
|
19
|
+
causeCode: string
|
|
20
|
+
): string => {
|
|
21
|
+
if (causeCode === 'EVIDENCE_GATE_BLOCKED') {
|
|
22
|
+
return 'El gate no deja avanzar hasta revalidar la evidencia y la gobernanza del repo.';
|
|
23
|
+
}
|
|
24
|
+
if (causeCode === 'TRACKING_CANONICAL_IN_PROGRESS_INVALID') {
|
|
25
|
+
return 'El tracking canónico bloquea el avance hasta dejar una única tarea activa real.';
|
|
26
|
+
}
|
|
27
|
+
if (causeCode === 'TRACKING_CANONICAL_SOURCE_CONFLICT') {
|
|
28
|
+
return 'Las fuentes de tracking no coinciden y el loop enterprise no puede decidir autoridad.';
|
|
29
|
+
}
|
|
30
|
+
if (causeCode === 'PRE_PUSH_UPSTREAM_MISSING') {
|
|
31
|
+
return 'No puedes publicar el cambio hasta fijar upstream en la rama actual.';
|
|
32
|
+
}
|
|
33
|
+
if (causeCode === 'EVIDENCE_STALE') {
|
|
34
|
+
return `La evidencia de ${event.stage} ya no describe el estado real del repo.`;
|
|
35
|
+
}
|
|
36
|
+
return `No se puede continuar en ${event.stage} hasta resolver el bloqueo detectado.`;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const buildPinnedPumukiCommand = (params: {
|
|
40
|
+
repoRoot: string;
|
|
41
|
+
executableAndArgs: string;
|
|
42
|
+
}): string =>
|
|
43
|
+
`npx --yes --package pumuki@${getCurrentPumukiVersion({ repoRoot: params.repoRoot })} ${params.executableAndArgs}`;
|
|
44
|
+
|
|
45
|
+
export const resolveBlockedCommand = (params: {
|
|
46
|
+
event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>;
|
|
47
|
+
repoRoot: string;
|
|
48
|
+
causeCode: string;
|
|
49
|
+
}): string => {
|
|
50
|
+
switch (params.causeCode) {
|
|
51
|
+
case 'EVIDENCE_MISSING':
|
|
52
|
+
case 'EVIDENCE_INVALID':
|
|
53
|
+
case 'EVIDENCE_CHAIN_INVALID':
|
|
54
|
+
case 'EVIDENCE_STALE':
|
|
55
|
+
return buildPinnedPumukiCommand({
|
|
56
|
+
repoRoot: params.repoRoot,
|
|
57
|
+
executableAndArgs: `pumuki sdd validate --stage=${params.event.stage} --json`,
|
|
58
|
+
});
|
|
59
|
+
case 'EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES':
|
|
60
|
+
case 'EVIDENCE_SKILLS_CONTRACT_INCOMPLETE':
|
|
61
|
+
case 'EVIDENCE_PLATFORM_SKILLS_SCOPE_INCOMPLETE':
|
|
62
|
+
case 'EVIDENCE_PLATFORM_SKILLS_BUNDLES_MISSING':
|
|
63
|
+
case 'EVIDENCE_PLATFORM_CRITICAL_SKILLS_RULES_MISSING':
|
|
64
|
+
case 'EVIDENCE_CROSS_PLATFORM_CRITICAL_ENFORCEMENT_INCOMPLETE':
|
|
65
|
+
return `${buildPinnedPumukiCommand({
|
|
66
|
+
repoRoot: params.repoRoot,
|
|
67
|
+
executableAndArgs: 'pumuki policy reconcile --strict --json',
|
|
68
|
+
})} && ${buildPinnedPumukiCommand({
|
|
69
|
+
repoRoot: params.repoRoot,
|
|
70
|
+
executableAndArgs: `pumuki sdd validate --stage=${params.event.stage} --json`,
|
|
71
|
+
})}`;
|
|
72
|
+
case 'TRACKING_CANONICAL_IN_PROGRESS_INVALID':
|
|
73
|
+
case 'TRACKING_CANONICAL_SOURCE_CONFLICT':
|
|
74
|
+
return 'Editar PUMUKI-RESET-MASTER-PLAN.md y dejar una sola tarea activa válida.';
|
|
75
|
+
case 'PRE_PUSH_UPSTREAM_MISSING':
|
|
76
|
+
return 'git push --set-upstream origin <branch>';
|
|
77
|
+
case 'GITFLOW_PROTECTED_BRANCH':
|
|
78
|
+
return 'git checkout -b feature/<descripcion-kebab-case>';
|
|
79
|
+
case 'EVIDENCE_GATE_BLOCKED':
|
|
80
|
+
return `${buildPinnedPumukiCommand({
|
|
81
|
+
repoRoot: params.repoRoot,
|
|
82
|
+
executableAndArgs: 'pumuki policy reconcile --strict --json',
|
|
83
|
+
})} && ${buildPinnedPumukiCommand({
|
|
84
|
+
repoRoot: params.repoRoot,
|
|
85
|
+
executableAndArgs: `pumuki sdd validate --stage=${params.event.stage} --json`,
|
|
86
|
+
})}`;
|
|
87
|
+
default:
|
|
88
|
+
return resolveBlockedRemediation(params.event, params.causeCode);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const buildBlockedActionableMessage = (params: {
|
|
93
|
+
event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>;
|
|
94
|
+
repoRoot: string;
|
|
95
|
+
causeCode: string;
|
|
96
|
+
}): string => {
|
|
97
|
+
const cause = resolveBlockedCauseSummary(params.event, params.causeCode);
|
|
98
|
+
const impact = resolveBlockedImpactSummary(params.event, params.causeCode);
|
|
99
|
+
const command = resolveBlockedCommand(params);
|
|
100
|
+
const nextAction = resolveBlockedRemediation(params.event, params.causeCode);
|
|
101
|
+
return [
|
|
102
|
+
`Causa: ${cause}`,
|
|
103
|
+
`Impacto: ${impact}`,
|
|
104
|
+
`Comando: ${command}`,
|
|
105
|
+
`Siguiente acción: ${nextAction}`,
|
|
106
|
+
].join('\n');
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type BlockedPayloadContext =
|
|
110
|
+
| string
|
|
111
|
+
| {
|
|
112
|
+
projectPrefix: string;
|
|
113
|
+
repoRoot: string;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const normalizeBlockedPayloadContext = (
|
|
117
|
+
context: BlockedPayloadContext
|
|
118
|
+
): {
|
|
119
|
+
projectPrefix: string;
|
|
120
|
+
repoRoot: string;
|
|
121
|
+
} => {
|
|
122
|
+
if (typeof context === 'string') {
|
|
123
|
+
return {
|
|
124
|
+
projectPrefix: context,
|
|
125
|
+
repoRoot: '',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
projectPrefix: context.projectPrefix,
|
|
131
|
+
repoRoot: context.repoRoot,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
16
135
|
export const buildGateBlockedPayload = (
|
|
17
136
|
event: Extract<PumukiCriticalNotificationEvent, { kind: 'gate.blocked' }>,
|
|
18
|
-
|
|
137
|
+
context: BlockedPayloadContext
|
|
19
138
|
): SystemNotificationPayload => {
|
|
139
|
+
const params = normalizeBlockedPayloadContext(context);
|
|
20
140
|
const causeCode = event.causeCode ?? 'GATE_BLOCKED';
|
|
21
141
|
const causeSummary = truncateNotificationText(
|
|
22
142
|
resolveBlockedCauseSummary(event, causeCode),
|
|
23
143
|
72
|
|
24
144
|
);
|
|
25
|
-
const
|
|
145
|
+
const actionableMessage = buildBlockedActionableMessage({
|
|
146
|
+
event,
|
|
147
|
+
repoRoot: params.repoRoot,
|
|
148
|
+
causeCode,
|
|
149
|
+
});
|
|
26
150
|
return {
|
|
27
151
|
title: '🔴 Pumuki bloqueado',
|
|
28
|
-
subtitle: `${projectPrefix}${event.stage} · ${causeSummary}`,
|
|
29
|
-
message:
|
|
152
|
+
subtitle: `${params.projectPrefix}${event.stage} · ${causeSummary}`,
|
|
153
|
+
message: actionableMessage,
|
|
30
154
|
soundName: 'Basso',
|
|
31
155
|
};
|
|
32
156
|
};
|
|
@@ -10,6 +10,8 @@ import { buildAuditSummaryPayload } from './framework-menu-system-notifications-
|
|
|
10
10
|
import {
|
|
11
11
|
buildGateBlockedPayload,
|
|
12
12
|
resolveBlockedCauseSummary,
|
|
13
|
+
resolveBlockedImpactSummary,
|
|
14
|
+
resolveBlockedCommand,
|
|
13
15
|
resolveBlockedRemediation,
|
|
14
16
|
} from './framework-menu-system-notifications-payloads-blocked';
|
|
15
17
|
import {
|
|
@@ -20,6 +22,8 @@ import {
|
|
|
20
22
|
export { resolveProjectLabel } from './framework-menu-system-notifications-payloads-context';
|
|
21
23
|
export {
|
|
22
24
|
resolveBlockedCauseSummary,
|
|
25
|
+
resolveBlockedImpactSummary,
|
|
26
|
+
resolveBlockedCommand,
|
|
23
27
|
resolveBlockedRemediation,
|
|
24
28
|
} from './framework-menu-system-notifications-payloads-blocked';
|
|
25
29
|
export {
|
|
@@ -46,7 +50,10 @@ export const buildSystemNotificationPayload = (
|
|
|
46
50
|
return buildAuditSummaryPayload(event);
|
|
47
51
|
}
|
|
48
52
|
if (event.kind === 'gate.blocked') {
|
|
49
|
-
return buildGateBlockedPayload(event,
|
|
53
|
+
return buildGateBlockedPayload(event, {
|
|
54
|
+
projectPrefix,
|
|
55
|
+
repoRoot: context?.repoRoot ?? '',
|
|
56
|
+
});
|
|
50
57
|
}
|
|
51
58
|
if (event.kind === 'evidence.stale') {
|
|
52
59
|
return buildEvidenceStalePayload(event);
|
|
@@ -7,7 +7,13 @@ export const truncateNotificationText = (value: string, maxLength: number): stri
|
|
|
7
7
|
if (value.length <= maxLength) {
|
|
8
8
|
return value;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
const limit = Math.max(1, maxLength - 1);
|
|
11
|
+
const trimmed = value.slice(0, limit).trimEnd();
|
|
12
|
+
const boundary = trimmed.lastIndexOf(' ');
|
|
13
|
+
const shortened = boundary >= Math.floor(limit * 0.6)
|
|
14
|
+
? trimmed.slice(0, boundary).trimEnd()
|
|
15
|
+
: trimmed;
|
|
16
|
+
return `${shortened}…`;
|
|
11
17
|
};
|
|
12
18
|
|
|
13
19
|
export const resolveProjectLabel = (params: {
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
loadAndFormatActiveSkillsBundles,
|
|
26
26
|
} from './framework-menu-skills-lib';
|
|
27
27
|
import { isMenuUiV2Enabled } from './framework-menu-ui-version-lib';
|
|
28
|
+
import { readLifecycleStatus } from '../integrations/lifecycle/status';
|
|
29
|
+
import type { GovernanceConsoleSnapshot } from '../integrations/lifecycle/cliGovernanceConsole';
|
|
28
30
|
export * from './framework-menu-builders';
|
|
29
31
|
export { formatAdvancedMenuView } from './framework-menu-advanced-view-lib';
|
|
30
32
|
export { buildMenuGateParams } from './framework-menu-gate-lib';
|
|
@@ -43,6 +45,20 @@ const resolveInitialMenuMode = (): MenuMode => {
|
|
|
43
45
|
return 'consumer';
|
|
44
46
|
};
|
|
45
47
|
|
|
48
|
+
const readAdvancedGovernanceConsoleSnapshot = (): GovernanceConsoleSnapshot | null => {
|
|
49
|
+
try {
|
|
50
|
+
const status = readLifecycleStatus({ cwd: process.cwd() });
|
|
51
|
+
return {
|
|
52
|
+
governanceObservation: status.governanceObservation,
|
|
53
|
+
governanceNextAction: status.governanceNextAction,
|
|
54
|
+
policyValidation: status.policyValidation,
|
|
55
|
+
experimentalFeatures: status.experimentalFeatures,
|
|
56
|
+
};
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
46
62
|
const menu = async (): Promise<void> => {
|
|
47
63
|
const rl = createInterface({ input, output });
|
|
48
64
|
|
|
@@ -76,10 +92,14 @@ const menu = async (): Promise<void> => {
|
|
|
76
92
|
consumerRuntime.printMenu();
|
|
77
93
|
} else {
|
|
78
94
|
const currentSummary = consumerRuntime.readCurrentSummary();
|
|
95
|
+
const lastPreflight = consumerRuntime.readLastPreflight();
|
|
96
|
+
const governanceConsole = lastPreflight ? null : readAdvancedGovernanceConsoleSnapshot();
|
|
79
97
|
if (!isMenuUiV2Enabled()) {
|
|
80
98
|
output.write(
|
|
81
99
|
`\n${formatAdvancedMenuClassicView(advancedActions, {
|
|
82
100
|
evidenceSummary: currentSummary ?? undefined,
|
|
101
|
+
preflight: lastPreflight ?? undefined,
|
|
102
|
+
governanceConsole: governanceConsole ?? undefined,
|
|
83
103
|
})}\n`
|
|
84
104
|
);
|
|
85
105
|
} else {
|
|
@@ -87,6 +107,8 @@ const menu = async (): Promise<void> => {
|
|
|
87
107
|
output.write(
|
|
88
108
|
`\n${formatAdvancedMenuView(advancedActions, {
|
|
89
109
|
evidenceSummary: currentSummary ?? undefined,
|
|
110
|
+
preflight: lastPreflight ?? undefined,
|
|
111
|
+
governanceConsole: governanceConsole ?? undefined,
|
|
90
112
|
})}\n`
|
|
91
113
|
);
|
|
92
114
|
} catch {
|
|
@@ -94,12 +116,25 @@ const menu = async (): Promise<void> => {
|
|
|
94
116
|
output.write(
|
|
95
117
|
`\n${formatAdvancedMenuClassicView(advancedActions, {
|
|
96
118
|
evidenceSummary: currentSummary ?? undefined,
|
|
119
|
+
preflight: lastPreflight ?? undefined,
|
|
120
|
+
governanceConsole: governanceConsole ?? undefined,
|
|
97
121
|
})}\n`
|
|
98
122
|
);
|
|
99
123
|
}
|
|
100
124
|
}
|
|
101
125
|
}
|
|
102
|
-
|
|
126
|
+
let option: string;
|
|
127
|
+
try {
|
|
128
|
+
option = (await rl.question('\nSelect option: ')).trim();
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const code = error instanceof Error && 'code' in error
|
|
131
|
+
? String((error as { code?: unknown }).code)
|
|
132
|
+
: '';
|
|
133
|
+
if (code === 'ABORT_ERR' || code === 'ERR_USE_AFTER_CLOSE') {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
103
138
|
const normalized = option.toUpperCase();
|
|
104
139
|
|
|
105
140
|
if (mode === 'consumer' && normalized === 'A') {
|
|
@@ -118,7 +153,7 @@ const menu = async (): Promise<void> => {
|
|
|
118
153
|
output.write('Invalid option.\n');
|
|
119
154
|
continue;
|
|
120
155
|
}
|
|
121
|
-
if (selectedConsumer.id === '
|
|
156
|
+
if (selectedConsumer.id === '9') {
|
|
122
157
|
break;
|
|
123
158
|
}
|
|
124
159
|
try {
|
|
@@ -39,8 +39,17 @@ export const configureRemoteAndFeatureBranch = (
|
|
|
39
39
|
workspace.tmpRoot
|
|
40
40
|
);
|
|
41
41
|
runGitStep(workspace, ['remote', 'add', 'origin', workspace.bareRemote], 'git remote add origin');
|
|
42
|
-
runGitStep(workspace, ['push', '-u', 'origin', 'main'], 'git push origin main');
|
|
43
42
|
runGitStep(workspace, ['checkout', '-b', 'feature/package-smoke'], 'git checkout feature branch');
|
|
43
|
+
runGitStep(
|
|
44
|
+
workspace,
|
|
45
|
+
['push', '-u', 'origin', 'feature/package-smoke'],
|
|
46
|
+
'git push origin feature branch'
|
|
47
|
+
);
|
|
48
|
+
runGitStep(
|
|
49
|
+
workspace,
|
|
50
|
+
['push', 'origin', 'HEAD:refs/heads/main'],
|
|
51
|
+
'git push origin main from feature branch'
|
|
52
|
+
);
|
|
44
53
|
runGitStep(
|
|
45
54
|
workspace,
|
|
46
55
|
['branch', '--set-upstream-to=origin/main', 'feature/package-smoke'],
|