import { createInterface } from 'node:readline/promises'; import { stdin as input, stdout as output } from 'node:process'; import { createFrameworkMenuActions, type MenuAction } from './framework-menu-actions'; import { formatAdvancedMenuClassicView, formatAdvancedMenuView, } from './framework-menu-advanced-view-lib'; import { createConsumerMenuRuntime } from './framework-menu-consumer-runtime-lib'; import { runRangeGate, runRepoAndStagedGate, runRepoAndStagedGateSilent, runRepoGateSilent, runRepoGateSilentWithBlocked, runStagedGate, runStagedGateSilent, runStagedGateSilentWithBlocked, runWorkingTreeGateSilent, runWorkingTreeGateSilentWithBlocked, runRepoAndStagedPrePushGateSilent, runWorkingTreePrePushGateSilent, runWorkingTreePrePushGateSilentWithBlocked, runUnstagedGateSilentWithBlocked, } from './framework-menu-gate-lib'; import { createFrameworkMenuPrompts } from './framework-menu-prompts'; import { resolveDefaultRangeFrom } from './framework-menu-runners'; import { formatActiveSkillsBundles, loadAndFormatActiveSkillsBundles, } from './framework-menu-skills-lib'; import { isMenuUiV2Enabled } from './framework-menu-ui-version-lib'; export * from './framework-menu-builders'; export { formatAdvancedMenuView } from './framework-menu-advanced-view-lib'; export { buildMenuGateParams } from './framework-menu-gate-lib'; export { formatActiveSkillsBundles } from './framework-menu-skills-lib'; type MenuMode = 'consumer' | 'advanced'; const printActiveSkillsBundles = (): void => { const rendered = loadAndFormatActiveSkillsBundles(process.cwd()); output.write(`\n${rendered}\n`); }; const resolveInitialMenuMode = (): MenuMode => { if (process.env.PUMUKI_MENU_MODE === 'advanced') { return 'advanced'; } return 'consumer'; }; const menu = async (): Promise => { const rl = createInterface({ input, output }); try { let mode: MenuMode = resolveInitialMenuMode(); const prompts = createFrameworkMenuPrompts(rl); const advancedActions: ReadonlyArray = createFrameworkMenuActions({ prompts, runStaged: runStagedGate, runRange: runRangeGate, runRepoAudit: runRepoGateSilent, runRepoAndStagedAudit: runRepoAndStagedGateSilent, runStagedAndUnstagedAudit: runWorkingTreeGateSilent, resolveDefaultRangeFrom, printActiveSkillsBundles, }); const consumerRuntime = createConsumerMenuRuntime({ runRepoGate: runRepoGateSilentWithBlocked, runRepoAndStagedGate: runRepoAndStagedPrePushGateSilent, runStagedGate: runStagedGateSilentWithBlocked, runUnstagedGate: runUnstagedGateSilentWithBlocked, runWorkingTreeGate: runWorkingTreePrePushGateSilentWithBlocked, runWorkingTreePreCommitGate: runWorkingTreeGateSilentWithBlocked, write: (text) => { output.write(text); }, }); while (true) { if (mode === 'consumer') { consumerRuntime.printMenu(); } else { const currentSummary = consumerRuntime.readCurrentSummary(); if (!isMenuUiV2Enabled()) { output.write( `\n${formatAdvancedMenuClassicView(advancedActions, { evidenceSummary: currentSummary ?? undefined, })}\n` ); } else { try { output.write( `\n${formatAdvancedMenuView(advancedActions, { evidenceSummary: currentSummary ?? undefined, })}\n` ); } catch { output.write('\n[pumuki][menu-ui-v2] Render failed. Falling back to classic menu.\n'); output.write( `\n${formatAdvancedMenuClassicView(advancedActions, { evidenceSummary: currentSummary ?? undefined, })}\n` ); } } } const prompt = mode === 'consumer' ? '\nChoose an option: ' : '\nSelect option: '; const option = (await rl.question(prompt)).trim(); const normalized = option.toUpperCase(); if (mode === 'consumer' && normalized === 'A') { mode = 'advanced'; continue; } if (mode === 'advanced' && normalized === 'C') { mode = 'consumer'; continue; } if (mode === 'consumer') { const selectedConsumer = consumerRuntime.actions.find((action) => action.id === option); if (!selectedConsumer) { output.write('Invalid option.\n'); continue; } if (selectedConsumer.id === '10') { break; } try { await selectedConsumer.execute(); } catch (error) { const message = error instanceof Error ? error.message : 'Unexpected menu execution error.'; output.write(`Error: ${message}\n`); } } else { const selectedAdvanced = advancedActions.find((action) => action.id === option); if (!selectedAdvanced) { output.write('Invalid option.\n'); continue; } if (selectedAdvanced.id === '27') { break; } try { await selectedAdvanced.execute(); } catch (error) { const message = error instanceof Error ? error.message : 'Unexpected menu execution error.'; output.write(`Error: ${message}\n`); } } } } finally { rl.close(); } }; export const runFrameworkMenu = async (): Promise => { await menu(); };