pumuki 6.3.113 → 6.3.114

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.
Files changed (99) hide show
  1. package/CHANGELOG.md +51 -5
  2. package/README.md +4 -2
  3. package/VERSION +1 -1
  4. package/core/facts/detectors/typescript/index.test.ts +0 -229
  5. package/core/facts/detectors/typescript/index.ts +0 -278
  6. package/core/facts/extractHeuristicFacts.ts +0 -4
  7. package/core/rules/presets/heuristics/typescript.test.ts +1 -21
  8. package/core/rules/presets/heuristics/typescript.ts +0 -72
  9. package/docs/README.md +13 -9
  10. package/docs/codex-skills/backend-enterprise-rules.md +3 -3
  11. package/docs/operations/RELEASE_NOTES.md +40 -4
  12. package/docs/product/API_REFERENCE.md +1 -1
  13. package/docs/product/HOW_IT_WORKS.md +6 -0
  14. package/docs/product/INSTALLATION.md +1 -1
  15. package/docs/product/USAGE.md +42 -5
  16. package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +100 -44
  17. package/docs/validation/README.md +6 -3
  18. package/integrations/config/skillsDetectorRegistry.ts +0 -24
  19. package/integrations/config/skillsMarkdownRules.ts +0 -57
  20. package/integrations/evidence/buildEvidence.ts +0 -24
  21. package/integrations/evidence/repoState.ts +0 -3
  22. package/integrations/evidence/schema.ts +0 -18
  23. package/integrations/evidence/writeEvidence.ts +0 -24
  24. package/integrations/gate/evaluateAiGate.ts +8 -251
  25. package/integrations/gate/remediationCatalog.ts +0 -8
  26. package/integrations/git/GitService.ts +44 -5
  27. package/integrations/git/aiGateRepoPolicyFindings.ts +86 -17
  28. package/integrations/git/runPlatformGate.ts +1 -9
  29. package/integrations/git/runPlatformGateFacts.ts +19 -1
  30. package/integrations/git/runPlatformGateOutput.ts +41 -42
  31. package/integrations/lifecycle/adapter.templates.json +1 -0
  32. package/integrations/lifecycle/adapter.ts +0 -24
  33. package/integrations/lifecycle/audit.ts +101 -0
  34. package/integrations/lifecycle/cli.ts +120 -99
  35. package/integrations/lifecycle/cliSdd.ts +4 -26
  36. package/integrations/lifecycle/doctor.ts +40 -102
  37. package/integrations/lifecycle/index.ts +2 -0
  38. package/integrations/lifecycle/install.ts +0 -21
  39. package/integrations/lifecycle/packageInfo.ts +1 -118
  40. package/integrations/lifecycle/state.ts +1 -8
  41. package/integrations/lifecycle/status.ts +40 -59
  42. package/integrations/lifecycle/watch.ts +1 -1
  43. package/integrations/mcp/aiGateCheck.ts +10 -194
  44. package/integrations/mcp/autoExecuteAiStart.ts +116 -92
  45. package/integrations/mcp/enterpriseServer.ts +7 -23
  46. package/integrations/mcp/enterpriseStdioServer.cli.ts +4 -31
  47. package/integrations/mcp/preFlightCheck.ts +5 -67
  48. package/integrations/platform/detectPlatforms.ts +37 -0
  49. package/integrations/sdd/policy.ts +28 -20
  50. package/package.json +1 -1
  51. package/scripts/check-tracking-single-active.sh +1 -1
  52. package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
  53. package/scripts/consumer-postinstall-resolve-args.cjs +44 -0
  54. package/scripts/consumer-postinstall.cjs +76 -21
  55. package/scripts/framework-menu-advanced-view-lib.ts +0 -49
  56. package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
  57. package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
  58. package/scripts/framework-menu-consumer-preflight-render.ts +0 -10
  59. package/scripts/framework-menu-consumer-preflight-run.ts +0 -23
  60. package/scripts/framework-menu-consumer-preflight-types.ts +0 -12
  61. package/scripts/framework-menu-consumer-runtime-actions.ts +87 -17
  62. package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
  63. package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
  64. package/scripts/framework-menu-consumer-runtime-lib.ts +2 -38
  65. package/scripts/framework-menu-consumer-runtime-menu.ts +4 -31
  66. package/scripts/framework-menu-consumer-runtime-types.ts +3 -5
  67. package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
  68. package/scripts/framework-menu-evidence-summary-read.ts +57 -5
  69. package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
  70. package/scripts/framework-menu-evidence-summary-types.ts +7 -0
  71. package/scripts/framework-menu-gate-lib.ts +9 -0
  72. package/scripts/framework-menu-layout-data.ts +5 -0
  73. package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
  74. package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
  75. package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
  76. package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
  77. package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
  78. package/scripts/framework-menu-system-notifications-cause.ts +0 -3
  79. package/scripts/framework-menu-system-notifications-macos-swift-source.ts +24 -204
  80. package/scripts/framework-menu-system-notifications-macos.ts +4 -0
  81. package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
  82. package/scripts/framework-menu-system-notifications-text.ts +1 -7
  83. package/scripts/framework-menu.ts +3 -24
  84. package/scripts/package-install-smoke-consumer-git-repo-lib.ts +1 -10
  85. package/scripts/package-install-smoke-consumer-npm-lib.ts +9 -46
  86. package/scripts/pumuki-full-surface-smoke-lib.ts +37 -0
  87. package/scripts/pumuki-full-surface-smoke.ts +346 -0
  88. package/scripts/pumuki-smoke-installed-wrapper.cjs +31 -0
  89. package/integrations/evidence/trackingContract.ts +0 -17
  90. package/integrations/gate/governanceActionCatalog.ts +0 -275
  91. package/integrations/lifecycle/bootstrapManifest.ts +0 -248
  92. package/integrations/lifecycle/cliGovernanceConsole.ts +0 -69
  93. package/integrations/lifecycle/governanceNextAction.ts +0 -171
  94. package/integrations/lifecycle/governanceObservationSnapshot.ts +0 -369
  95. package/integrations/lifecycle/trackingState.ts +0 -403
  96. package/integrations/mcp/alignedPlatformGate.ts +0 -232
  97. package/integrations/mcp/readMcpPrePushStdin.ts +0 -7
  98. package/scripts/build-ruralgo-s1-evidence-pack.ts +0 -85
  99. package/scripts/ruralgo-s1-evidence-pack-lib.ts +0 -200
@@ -1,16 +1,11 @@
1
- import {
2
- resolveGovernanceCatalogAction,
3
- type GovernanceCatalogNextAction,
4
- } from '../gate/governanceActionCatalog';
5
1
  import { evaluateAiGate, type AiGateStage, type AiGateViolation } from '../gate/evaluateAiGate';
6
2
  import { collectWorktreeAtomicSlices } from '../git/worktreeAtomicSlices';
7
- import { readLifecyclePolicyValidationSnapshot } from '../lifecycle/policyValidationSnapshot';
8
3
  import { resolveLearningContextExperimentalFeature } from '../policy/experimentalFeatures';
9
- import { resolvePreWriteEnforcement } from '../policy/preWriteEnforcement';
10
4
  import { readSddLearningContext, type SddLearningContext } from '../sdd/learningInsights';
11
5
 
12
6
  const ACTIONABLE_HINTS_BY_CODE: Readonly<Record<string, string>> = {
13
- EVIDENCE_MISSING: 'Ejecuta una auditoría (1/2/3/4) para regenerar .ai_evidence.json.',
7
+ EVIDENCE_MISSING:
8
+ 'Ejecuta una auditoría (1/2/3/4 u opciones de motor 11–14) para regenerar .ai_evidence.json.',
14
9
  EVIDENCE_INVALID: 'Regenera .ai_evidence.json desde una opción de auditoría.',
15
10
  EVIDENCE_INTEGRITY_MISSING: 'Refresca evidencia para regenerar metadatos de integridad.',
16
11
  EVIDENCE_ACTIVE_RULE_IDS_EMPTY_FOR_CODE_CHANGES:
@@ -44,28 +39,6 @@ const ACTIONABLE_HINTS_BY_CODE: Readonly<Record<string, string>> = {
44
39
  'Mapea todas las reglas AUTO a detectores AST antes de continuar.',
45
40
  EVIDENCE_TIMESTAMP_FUTURE: 'Corrige la hora del sistema y regenera evidencia.',
46
41
  GITFLOW_PROTECTED_BRANCH: 'Evita trabajo directo en ramas protegidas (usa feature/*).',
47
- GITFLOW_BRANCH_NAMING_INVALID:
48
- 'La rama actual no cumple GitFlow. Usa feature/*, bugfix/*, hotfix/*, release/*, chore/*, refactor/* o docs/*.',
49
- TRACKING_CANONICAL_SOURCE_CONFLICT:
50
- 'AGENTS.md y los README del repo no apuntan a la misma fuente canónica de tracking.',
51
- TRACKING_CANONICAL_FILE_MISSING:
52
- 'El repo declara un MD de tracking canónico que ahora mismo no existe.',
53
- TRACKING_CANONICAL_IN_PROGRESS_INVALID:
54
- 'El MD canónico de tracking debe dejar exactamente una task o fase en construcción.',
55
- };
56
-
57
- const normalizeGovernanceCatalogCode = (code: string): string => {
58
- switch (code) {
59
- case 'EVIDENCE_INVALID':
60
- case 'EVIDENCE_CHAIN_INVALID':
61
- return 'EVIDENCE_INVALID_OR_CHAIN';
62
- case 'GITFLOW_PROTECTED_BRANCH':
63
- return 'GITFLOW_PROTECTED_BRANCH_CONTEXT';
64
- case 'GITFLOW_BRANCH_NAMING_INVALID':
65
- return 'GITFLOW_BRANCH_NAMING_INVALID_CONTEXT';
66
- default:
67
- return code;
68
- }
69
42
  };
70
43
 
71
44
  const buildPreFlightHints = (params: {
@@ -142,14 +115,6 @@ export type EnterprisePreFlightCheckResult = {
142
115
  phase: 'GREEN' | 'RED';
143
116
  message: string;
144
117
  instruction: string;
145
- reason_code: string;
146
- prewrite_effective: {
147
- mode: ReturnType<typeof resolvePreWriteEnforcement>['mode'];
148
- source: ReturnType<typeof resolvePreWriteEnforcement>['source'];
149
- blocking: boolean;
150
- strict_policy: boolean;
151
- };
152
- next_action: GovernanceCatalogNextAction;
153
118
  stage: ReturnType<typeof evaluateAiGate>['stage'];
154
119
  policy: ReturnType<typeof evaluateAiGate>['policy'];
155
120
  violations: ReturnType<typeof evaluateAiGate>['violations'];
@@ -180,8 +145,6 @@ export const runEnterprisePreFlightCheck = (params: {
180
145
  : readSddLearningContext({
181
146
  repoRoot: params.repoRoot,
182
147
  });
183
- const preWriteEnforcement = resolvePreWriteEnforcement();
184
- const policyValidation = readLifecyclePolicyValidationSnapshot(params.repoRoot);
185
148
 
186
149
  const hints = buildPreFlightHints({
187
150
  repoRoot: params.repoRoot,
@@ -191,30 +154,13 @@ export const runEnterprisePreFlightCheck = (params: {
191
154
  upstream: evaluation.repo_state.git.upstream,
192
155
  learningContext,
193
156
  });
194
- const firstViolation = evaluation.violations[0];
195
- const reasonCode = firstViolation?.code ?? 'READY';
196
- const governanceAction = firstViolation
197
- ? resolveGovernanceCatalogAction({
198
- code: normalizeGovernanceCatalogCode(firstViolation.code),
199
- stage: evaluation.stage,
200
- })
201
- : resolveGovernanceCatalogAction({
202
- code: 'READY',
203
- stage: evaluation.stage,
204
- });
205
157
  const phase: 'GREEN' | 'RED' = evaluation.allowed ? 'GREEN' : 'RED';
206
158
  const message = evaluation.allowed
207
159
  ? '✅ Pre-flight aprobado: puedes continuar con la implementación.'
208
- : `🔴 Pre-flight bloqueado: corrige ${reasonCode} y vuelve a ejecutar.`;
209
- const instruction = !firstViolation && evaluation.allowed
160
+ : `🔴 Pre-flight bloqueado: corrige ${evaluation.violations[0]?.code ?? 'la causa'} y vuelve a ejecutar.`;
161
+ const instruction = evaluation.allowed
210
162
  ? 'Implementa el cambio mínimo para pasar en verde y vuelve a validar.'
211
- : governanceAction.instruction;
212
- const nextAction: GovernanceCatalogNextAction = evaluation.allowed
213
- ? {
214
- kind: 'info',
215
- message: governanceAction.next_action.message,
216
- }
217
- : governanceAction.next_action;
163
+ : hints[0] ?? 'Corrige la causa bloqueante y vuelve a ejecutar el pre-flight.';
218
164
 
219
165
  return {
220
166
  tool: 'pre_flight_check',
@@ -227,14 +173,6 @@ export const runEnterprisePreFlightCheck = (params: {
227
173
  phase,
228
174
  message,
229
175
  instruction,
230
- reason_code: reasonCode,
231
- prewrite_effective: {
232
- mode: preWriteEnforcement.mode,
233
- source: preWriteEnforcement.source,
234
- blocking: preWriteEnforcement.blocking,
235
- strict_policy: policyValidation.stages.PRE_WRITE.strict,
236
- },
237
- next_action: nextAction,
238
176
  stage: evaluation.stage,
239
177
  policy: evaluation.policy,
240
178
  violations: evaluation.violations,
@@ -51,6 +51,38 @@ const collectFilePaths = (facts: ReadonlyArray<Fact>): string[] => {
51
51
  .filter((path): path is string => typeof path === 'string');
52
52
  };
53
53
 
54
+ const ALLOWED_PIN_KEYS = new Set(['ios', 'android', 'backend', 'frontend']);
55
+
56
+ const readPinnedPlatformsFromEnv = (): ReadonlySet<keyof DetectedPlatforms> | null => {
57
+ const raw = process.env.PUMUKI_PIN_PLATFORMS?.trim().toLowerCase();
58
+ if (!raw) {
59
+ return null;
60
+ }
61
+ const tokens = raw
62
+ .split(',')
63
+ .map((token) => token.trim())
64
+ .filter((token) => token.length > 0)
65
+ .filter((token) => ALLOWED_PIN_KEYS.has(token)) as Array<keyof DetectedPlatforms>;
66
+ if (tokens.length === 0) {
67
+ return null;
68
+ }
69
+ return new Set(tokens);
70
+ };
71
+
72
+ const applyPinnedPlatformsFilter = (
73
+ detected: DetectedPlatforms,
74
+ pin: ReadonlySet<keyof DetectedPlatforms>
75
+ ): DetectedPlatforms => {
76
+ const next: DetectedPlatforms = {};
77
+ for (const key of pin) {
78
+ const state = detected[key];
79
+ if (state) {
80
+ next[key] = state;
81
+ }
82
+ }
83
+ return next;
84
+ };
85
+
54
86
  export const detectPlatformsFromFacts = (
55
87
  facts: ReadonlyArray<Fact>
56
88
  ): DetectedPlatforms => {
@@ -102,5 +134,10 @@ export const detectPlatformsFromFacts = (
102
134
  };
103
135
  }
104
136
 
137
+ const pin = readPinnedPlatformsFromEnv();
138
+ if (pin && pin.size > 0) {
139
+ return applyPinnedPlatformsFilter(result, pin);
140
+ }
141
+
105
142
  return result;
106
143
  };
@@ -14,6 +14,7 @@ import {
14
14
  } from './sessionStore';
15
15
  import type { ILifecycleGitService } from '../lifecycle/gitService';
16
16
  import { resolveDegradedMode } from '../gate/degradedMode';
17
+ import { getCurrentPumukiVersion } from '../lifecycle/packageInfo';
17
18
  import { resolveSddCompletenessEnforcement } from '../policy/sddCompletenessEnforcement';
18
19
  import { resolveSddExperimentalFeature } from '../policy/experimentalFeatures';
19
20
  import type {
@@ -23,16 +24,20 @@ import type {
23
24
  SddStatusPayload,
24
25
  } from './types';
25
26
 
26
- const SDD_SESSION_REFRESH_COMMAND =
27
- 'npx --yes --package pumuki@latest pumuki sdd session --refresh --ttl-minutes=90';
28
- const SDD_SESSION_OPEN_AUTO_COMMAND =
29
- 'npx --yes --package pumuki@latest pumuki sdd session --open --change=auto';
30
- const SDD_SESSION_OPEN_EXPLICIT_COMMAND =
31
- 'npx --yes --package pumuki@latest pumuki sdd session --open --change=<id>';
32
27
  const SDD_COMPLETENESS_CONTRACT_VERSION = '1.0';
33
28
 
34
- const buildSddExperimentalEnableCommand = (stage: SddStage): string =>
35
- `PUMUKI_EXPERIMENTAL_SDD=advisory npx --yes --package pumuki@latest pumuki sdd validate --stage=${stage} --json`;
29
+ const buildPinnedPumukiNpxCommand = (version: string): string =>
30
+ `npx --yes --package pumuki@${version} pumuki`;
31
+ const buildSddSessionRefreshCommand = (repoRoot: string): string =>
32
+ `${buildPinnedPumukiNpxCommand(getCurrentPumukiVersion({ repoRoot }))} sdd session --refresh --ttl-minutes=90`;
33
+ const buildSddSessionOpenAutoCommand = (repoRoot: string): string =>
34
+ `${buildPinnedPumukiNpxCommand(getCurrentPumukiVersion({ repoRoot }))} sdd session --open --change=auto`;
35
+ const buildSddSessionOpenExplicitCommand = (repoRoot: string): string =>
36
+ `${buildPinnedPumukiNpxCommand(getCurrentPumukiVersion({ repoRoot }))} sdd session --open --change=<id>`;
37
+ const buildSddExperimentalEnableCommand = (stage: SddStage, repoRoot: string): string =>
38
+ `PUMUKI_EXPERIMENTAL_SDD=advisory ${buildPinnedPumukiNpxCommand(
39
+ getCurrentPumukiVersion({ repoRoot })
40
+ )} sdd validate --stage=${stage} --json`;
36
41
 
37
42
  const resolveMissingSessionGuidance = (repoRoot: string): {
38
43
  message: string;
@@ -41,15 +46,17 @@ const resolveMissingSessionGuidance = (repoRoot: string): {
41
46
  suggestedChangeId?: string;
42
47
  availableChangeIds: ReadonlyArray<string>;
43
48
  } => {
49
+ const sessionOpenAutoCommand = buildSddSessionOpenAutoCommand(repoRoot);
50
+ const sessionOpenExplicitCommand = buildSddSessionOpenExplicitCommand(repoRoot);
44
51
  const availableChangeIds = listActiveOpenSpecChangeIds(repoRoot);
45
52
  if (availableChangeIds.length === 1) {
46
53
  const suggestedChangeId = availableChangeIds[0] ?? '';
47
54
  const command =
48
- `npx --yes --package pumuki@latest pumuki sdd session --open --change=${suggestedChangeId}`;
55
+ `${buildPinnedPumukiNpxCommand(getCurrentPumukiVersion({ repoRoot }))} sdd session --open --change=${suggestedChangeId}`;
49
56
  return {
50
57
  message: `SDD session is not active. Run \`${command}\` and retry.`,
51
58
  command,
52
- fallbackCommand: SDD_SESSION_OPEN_AUTO_COMMAND,
59
+ fallbackCommand: sessionOpenAutoCommand,
53
60
  suggestedChangeId,
54
61
  availableChangeIds,
55
62
  };
@@ -57,17 +64,17 @@ const resolveMissingSessionGuidance = (repoRoot: string): {
57
64
  if (availableChangeIds.length > 1) {
58
65
  return {
59
66
  message:
60
- `SDD session is not active. Run \`${SDD_SESSION_OPEN_EXPLICIT_COMMAND}\` ` +
67
+ `SDD session is not active. Run \`${sessionOpenExplicitCommand}\` ` +
61
68
  `with one active change id. Active changes: ${availableChangeIds.join(', ')}.`,
62
- command: SDD_SESSION_OPEN_EXPLICIT_COMMAND,
63
- fallbackCommand: SDD_SESSION_OPEN_EXPLICIT_COMMAND,
69
+ command: sessionOpenExplicitCommand,
70
+ fallbackCommand: sessionOpenExplicitCommand,
64
71
  availableChangeIds,
65
72
  };
66
73
  }
67
74
  return {
68
- message: `SDD session is not active. Run \`${SDD_SESSION_OPEN_AUTO_COMMAND}\` and retry.`,
69
- command: SDD_SESSION_OPEN_AUTO_COMMAND,
70
- fallbackCommand: SDD_SESSION_OPEN_AUTO_COMMAND,
75
+ message: `SDD session is not active. Run \`${sessionOpenAutoCommand}\` and retry.`,
76
+ command: sessionOpenAutoCommand,
77
+ fallbackCommand: sessionOpenAutoCommand,
71
78
  availableChangeIds,
72
79
  };
73
80
  };
@@ -182,8 +189,9 @@ const evaluateSessionRequirements = (params: {
182
189
  );
183
190
  }
184
191
  if (!status.session.valid || !status.session.changeId) {
192
+ const refreshCommand = buildSddSessionRefreshCommand(params.repoRoot);
185
193
  const details: Record<string, string | boolean> = {
186
- command: SDD_SESSION_REFRESH_COMMAND,
194
+ command: refreshCommand,
187
195
  autoRefreshEnabled: params.autoRefreshEnabled,
188
196
  autoRefreshAttempted: params.autoRefreshAttempted,
189
197
  };
@@ -192,8 +200,8 @@ const evaluateSessionRequirements = (params: {
192
200
  }
193
201
  const message =
194
202
  params.autoRefreshAttempted && params.autoRefreshError
195
- ? `SDD session is invalid or expired. Auto-refresh failed (${params.autoRefreshError}). Run \`${SDD_SESSION_REFRESH_COMMAND}\` or reopen it.`
196
- : `SDD session is invalid or expired. Run \`${SDD_SESSION_REFRESH_COMMAND}\` or reopen it.`;
203
+ ? `SDD session is invalid or expired. Auto-refresh failed (${params.autoRefreshError}). Run \`${refreshCommand}\` or reopen it.`
204
+ : `SDD session is invalid or expired. Run \`${refreshCommand}\` or reopen it.`;
197
205
  return blocked(
198
206
  'SDD_SESSION_INVALID',
199
207
  message,
@@ -327,7 +335,7 @@ export const evaluateSddPolicy = (params: {
327
335
  experimentalSource: sddExperimentalFeature.source,
328
336
  activation_env: sddExperimentalFeature.activationVariable,
329
337
  legacy_activation_env: sddExperimentalFeature.legacyActivationVariable,
330
- activation_command: buildSddExperimentalEnableCommand(params.stage),
338
+ activation_command: buildSddExperimentalEnableCommand(params.stage, params.repoRoot),
331
339
  },
332
340
  },
333
341
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki",
3
- "version": "6.3.113",
3
+ "version": "6.3.114",
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": {
@@ -31,7 +31,7 @@ for FILE in "${FILES[@]}"; do
31
31
  fi
32
32
 
33
33
  if [[ "${FILE}" == "PUMUKI-RESET-MASTER-PLAN.md" ]]; then
34
- ACTIVE_PATTERN='(^- Estado:\s*🚧(?:\s|$))|(^`?\[\s*🚧\s*\]\s*-`?)|(^\|\s*[^|]+\|\s*`?\[\s*🚧\s*\]\s*-`?)|(^\|\s*🚧(?:\s|\|))'
34
+ ACTIVE_PATTERN='^- Estado: 🚧'
35
35
  else
36
36
  ACTIVE_PATTERN='^- 🚧 (`?P[0-9A-Za-z.-]+`?)'
37
37
  fi
@@ -4,10 +4,11 @@ import type {
4
4
  ConsumerMenuMatrixReport,
5
5
  MatrixOptionId,
6
6
  } from './framework-menu-matrix-baseline-lib';
7
+ import { MATRIX_MENU_OPTION_IDS } from './framework-menu-matrix-evidence-lib';
7
8
  import type { DoctorDeepCheckLayer, LifecycleDoctorReport } from '../integrations/lifecycle/doctor';
8
9
  import type { LifecycleStatus } from '../integrations/lifecycle/status';
9
10
 
10
- const OPTION_IDS: ReadonlyArray<MatrixOptionId> = ['1', '2', '3', '4', '9'];
11
+ const OPTION_IDS: ReadonlyArray<MatrixOptionId> = [...MATRIX_MENU_OPTION_IDS];
11
12
  const DOCTOR_LAYERS: ReadonlyArray<DoctorDeepCheckLayer> = [
12
13
  'core',
13
14
  'operational',
@@ -51,45 +52,19 @@ export type ConsumerMenuMatrixBaselineSnapshot = {
51
52
  };
52
53
  };
53
54
 
55
+ const UNKNOWN_OPTION_REPORT = {
56
+ stage: 'UNKNOWN',
57
+ outcome: 'UNKNOWN',
58
+ filesScanned: 0,
59
+ totalViolations: 0,
60
+ diagnosis: 'unknown' as const,
61
+ };
62
+
54
63
  const buildEmptyRound = (): ConsumerMenuMatrixReport => {
55
64
  return {
56
- byOption: {
57
- '1': {
58
- stage: 'UNKNOWN',
59
- outcome: 'UNKNOWN',
60
- filesScanned: 0,
61
- totalViolations: 0,
62
- diagnosis: 'unknown',
63
- },
64
- '2': {
65
- stage: 'UNKNOWN',
66
- outcome: 'UNKNOWN',
67
- filesScanned: 0,
68
- totalViolations: 0,
69
- diagnosis: 'unknown',
70
- },
71
- '3': {
72
- stage: 'UNKNOWN',
73
- outcome: 'UNKNOWN',
74
- filesScanned: 0,
75
- totalViolations: 0,
76
- diagnosis: 'unknown',
77
- },
78
- '4': {
79
- stage: 'UNKNOWN',
80
- outcome: 'UNKNOWN',
81
- filesScanned: 0,
82
- totalViolations: 0,
83
- diagnosis: 'unknown',
84
- },
85
- '9': {
86
- stage: 'UNKNOWN',
87
- outcome: 'UNKNOWN',
88
- filesScanned: 0,
89
- totalViolations: 0,
90
- diagnosis: 'unknown',
91
- },
92
- },
65
+ byOption: Object.fromEntries(
66
+ MATRIX_MENU_OPTION_IDS.map((id) => [id, { ...UNKNOWN_OPTION_REPORT }])
67
+ ) as ConsumerMenuMatrixReport['byOption'],
93
68
  };
94
69
  };
95
70
 
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const KNOWN_MCP_AGENTS = new Set(['cursor', 'codex', 'claude', 'repo']);
4
+
5
+ const normalizeExplicitAgent = (raw) => {
6
+ const trimmed = (raw ?? '').trim();
7
+ if (!trimmed) {
8
+ return '';
9
+ }
10
+ const lower = trimmed.toLowerCase();
11
+ if (lower === '0' || lower === 'none' || lower === 'false') {
12
+ return '';
13
+ }
14
+ if (!KNOWN_MCP_AGENTS.has(lower)) {
15
+ return '';
16
+ }
17
+ return lower;
18
+ };
19
+
20
+ const resolveConsumerPostinstallInstallExtras = (_consumerRoot, env = process.env) => {
21
+ if (env.PUMUKI_POSTINSTALL_SKIP_MCP === '1') {
22
+ return { extras: [], reason: 'skip_mcp' };
23
+ }
24
+ const withMcpFlag =
25
+ env.PUMUKI_POSTINSTALL_WITH_MCP === '1' ||
26
+ env.PUMUKI_POSTINSTALL_WITH_MCP?.toLowerCase() === 'true';
27
+ const explicit = normalizeExplicitAgent(env.PUMUKI_POSTINSTALL_MCP_AGENT);
28
+ if (explicit) {
29
+ return { extras: ['--with-mcp', `--agent=${explicit}`], reason: 'explicit_agent' };
30
+ }
31
+ if (withMcpFlag) {
32
+ return { extras: ['--with-mcp', '--agent=repo'], reason: 'explicit_repo_flag' };
33
+ }
34
+ return {
35
+ extras: [],
36
+ reason: 'disabled_default',
37
+ };
38
+ };
39
+
40
+ module.exports = {
41
+ KNOWN_MCP_AGENTS,
42
+ resolveConsumerPostinstallInstallExtras,
43
+ normalizeExplicitAgent,
44
+ };
@@ -4,56 +4,111 @@
4
4
  const { existsSync } = require('node:fs');
5
5
  const { join, resolve } = require('node:path');
6
6
  const { spawnSync } = require('node:child_process');
7
+ const { resolveConsumerPostinstallInstallExtras } = require('./consumer-postinstall-resolve-args.cjs');
7
8
 
8
- const skipReason = () => {
9
- if (process.env.PUMUKI_SKIP_POSTINSTALL === '1') {
9
+ const skipReason = (env = process.env) => {
10
+ if (env.PUMUKI_SKIP_POSTINSTALL === '1') {
10
11
  return 'PUMUKI_SKIP_POSTINSTALL=1';
11
12
  }
12
- if (process.env.CI === 'true' || process.env.CI === '1') {
13
+ if (env.CI === 'true' || env.CI === '1') {
13
14
  return 'CI';
14
15
  }
15
- if (process.env.npm_config_ignore_scripts === 'true') {
16
+ if (env.npm_config_ignore_scripts === 'true') {
16
17
  return 'npm ignore-scripts';
17
18
  }
18
19
  return '';
19
20
  };
20
21
 
21
- const main = () => {
22
- const reason = skipReason();
22
+ const normalizePumukiInstallResult = (result) => {
23
+ if (result.error) {
24
+ return {
25
+ code: 1,
26
+ errorMessage: result.error.message ?? String(result.error),
27
+ };
28
+ }
29
+
30
+ return { code: typeof result.status === 'number' ? result.status : 1, errorMessage: null };
31
+ };
32
+
33
+ const runPostinstall = ({
34
+ spawnSyncFn = spawnSync,
35
+ packageRoot = resolve(__dirname, '..'),
36
+ consumerRoot = process.env.INIT_CWD || process.cwd(),
37
+ env = process.env,
38
+ logger = console,
39
+ } = {}) => {
40
+ const reason = skipReason(env);
23
41
  if (reason) {
24
42
  return 0;
25
43
  }
26
44
 
27
- const consumerRoot = process.env.INIT_CWD || process.cwd();
28
45
  if (!existsSync(join(consumerRoot, '.git'))) {
29
46
  return 0;
30
47
  }
31
48
 
32
- const packageRoot = resolve(__dirname, '..');
33
49
  const pumukiCli = join(packageRoot, 'bin', 'pumuki.js');
34
50
  if (!existsSync(pumukiCli)) {
35
51
  return 0;
36
52
  }
37
53
 
38
- const env = {
39
- ...process.env,
54
+ const postinstallEnv = {
55
+ ...env,
40
56
  PUMUKI_AUTO_POSTINSTALL: '1',
41
- PUMUKI_SKIP_OPENSPEC_BOOTSTRAP: process.env.PUMUKI_SKIP_OPENSPEC_BOOTSTRAP ?? '1',
57
+ PUMUKI_SKIP_OPENSPEC_BOOTSTRAP: env.PUMUKI_SKIP_OPENSPEC_BOOTSTRAP ?? '1',
42
58
  };
43
59
 
44
- const result = spawnSync(process.execPath, [pumukiCli, 'install'], {
45
- cwd: consumerRoot,
46
- env,
47
- stdio: 'inherit',
48
- });
60
+ const { extras: installExtras, reason: mcpReason } = resolveConsumerPostinstallInstallExtras(
61
+ consumerRoot,
62
+ postinstallEnv
63
+ );
64
+ if (installExtras.length > 0 && postinstallEnv.PUMUKI_VERBOSE_INSTALL === '1') {
65
+ logger.debug(
66
+ `[pumuki] postinstall: mcp wiring (${mcpReason}): ${installExtras.join(' ')}`
67
+ );
68
+ }
69
+
70
+ if (installExtras.length === 0 && postinstallEnv.PUMUKI_VERBOSE_INSTALL === '1') {
71
+ logger.debug('[pumuki] postinstall: no extra args; running baseline install only.');
72
+ }
73
+
74
+ const result = normalizePumukiInstallResult(
75
+ spawnSyncFn(process.execPath, [pumukiCli, 'install', ...installExtras], {
76
+ cwd: consumerRoot,
77
+ env: postinstallEnv,
78
+ stdio: 'inherit',
79
+ })
80
+ );
49
81
 
50
- const code = typeof result.status === 'number' ? result.status : 1;
51
- if (code !== 0) {
52
- console.error(
53
- '[pumuki] postinstall: `pumuki install` exited non-zero; npm install will still succeed. Run `npx pumuki install` or `npx pumuki doctor` from the repo root.'
82
+ if (result.code !== 0) {
83
+ const scope = installExtras.length > 0 ? 'postinstall+extras' : 'postinstall';
84
+ const severityHint =
85
+ installExtras.length > 0
86
+ ? 'Run `pumuki install` manually with the desired options to investigar y resolver el fallo.'
87
+ : 'Run `pumuki install` o `pumuki doctor` desde la raíz del repo.';
88
+ logger.error(
89
+ `[pumuki] postinstall: install exited non-zero (scope=${scope}, reason=${mcpReason}). ${severityHint}`
54
90
  );
91
+ if (result.errorMessage) {
92
+ logger.error(`[pumuki] postinstall: ${result.errorMessage}`);
93
+ }
55
94
  }
56
- return 0;
95
+
96
+ return result.code;
97
+ };
98
+
99
+ const main = () => {
100
+ return runPostinstall({
101
+ spawnSyncFn: spawnSync,
102
+ packageRoot: resolve(__dirname, '..'),
103
+ consumerRoot: process.env.INIT_CWD || process.cwd(),
104
+ env: process.env,
105
+ logger: console,
106
+ });
57
107
  };
58
108
 
59
109
  process.exitCode = main();
110
+
111
+ module.exports = {
112
+ runPostinstall,
113
+ skipReason,
114
+ };
@@ -19,47 +19,20 @@ import {
19
19
  renderActionRow,
20
20
  renderBadge,
21
21
  } from './framework-menu-ui-components-lib';
22
- import {
23
- buildGovernanceConsoleSummaryLines,
24
- type GovernanceConsoleSnapshot,
25
- } from '../integrations/lifecycle/cliGovernanceConsole';
26
- import type { ConsumerPreflightResult } from './framework-menu-consumer-preflight-types';
27
22
 
28
23
  export const formatAdvancedMenuView = (
29
24
  actions: ReadonlyArray<MenuAction>,
30
25
  options?: {
31
26
  evidenceSummary?: FrameworkMenuEvidenceSummary;
32
- preflight?: ConsumerPreflightResult | null;
33
- governanceConsole?: GovernanceConsoleSnapshot | null;
34
27
  }
35
28
  ): string => {
36
29
  const evidenceSummary = options?.evidenceSummary ?? readEvidenceSummaryForMenu(process.cwd());
37
30
  const tokens = buildCliDesignTokens();
38
31
  const statusBadge = resolveAdvancedStatusBadge(evidenceSummary, tokens);
39
32
  const groupedActions = resolveAdvancedMenuLayout(actions);
40
- const governanceConsole = options?.preflight
41
- ? {
42
- governanceObservation: options.preflight.governanceObservation,
43
- governanceNextAction: options.preflight.governanceNextAction,
44
- policyValidation: options.preflight.policyValidation,
45
- experimentalFeatures: options.preflight.experimentalFeatures,
46
- }
47
- : (options?.governanceConsole ?? null);
48
33
  const lines = [
49
34
  'Pumuki Framework Menu (Advanced)',
50
35
  `Status: ${statusBadge}`,
51
- ...(governanceConsole
52
- ? [
53
- 'Governance Console',
54
- ...buildGovernanceConsoleSummaryLines({
55
- governanceObservation: governanceConsole.governanceObservation,
56
- governanceNextAction: governanceConsole.governanceNextAction,
57
- policyValidation: governanceConsole.policyValidation,
58
- experimentalFeatures: governanceConsole.experimentalFeatures,
59
- }).map((line) => ` ${line}`),
60
- '',
61
- ]
62
- : []),
63
36
  'C. Switch to consumer menu',
64
37
  '',
65
38
  ...groupedActions.flatMap((group, groupIndex) => [
@@ -87,33 +60,11 @@ export const formatAdvancedMenuClassicView = (
87
60
  actions: ReadonlyArray<MenuAction>,
88
61
  options?: {
89
62
  evidenceSummary?: FrameworkMenuEvidenceSummary;
90
- preflight?: ConsumerPreflightResult | null;
91
- governanceConsole?: GovernanceConsoleSnapshot | null;
92
63
  }
93
64
  ): string => {
94
65
  const evidenceSummary = options?.evidenceSummary ?? readEvidenceSummaryForMenu(process.cwd());
95
- const governanceConsole = options?.preflight
96
- ? {
97
- governanceObservation: options.preflight.governanceObservation,
98
- governanceNextAction: options.preflight.governanceNextAction,
99
- policyValidation: options.preflight.policyValidation,
100
- experimentalFeatures: options.preflight.experimentalFeatures,
101
- }
102
- : (options?.governanceConsole ?? null);
103
66
  const lines = [
104
67
  'Pumuki Framework Menu (Advanced)',
105
- ...(governanceConsole
106
- ? [
107
- 'Governance Console',
108
- ...buildGovernanceConsoleSummaryLines({
109
- governanceObservation: governanceConsole.governanceObservation,
110
- governanceNextAction: governanceConsole.governanceNextAction,
111
- policyValidation: governanceConsole.policyValidation,
112
- experimentalFeatures: governanceConsole.experimentalFeatures,
113
- }).map((line) => ` ${line}`),
114
- '',
115
- ]
116
- : []),
117
68
  'C. Switch to consumer menu',
118
69
  ...actions.map((action) => {
119
70
  const help = ADVANCED_MENU_HELP[action.id];