import React, { useEffect, useRef, useState } from "react"; import { Box, Text } from "ink"; import { Stepper } from "../components/Stepper"; import { useTerminalTheme } from "../hooks/useTerminalTheme"; import { resolveRepositoryPath } from "../../core/repository"; import { buildMoveCodeInput } from "../../commands/git.command"; import { runMoveCodeWorkflow } from "../../core/git/git-move-code-workflow"; import { InteractionCancelledError } from "../../core/interaction/interaction-service"; import type { InkInteractionService } from "../services/ink-interaction-service"; import type { TWorkflowContext } from "../../core/git/git-types"; import type { TNotification } from "../../core/interaction/interaction-service"; const STEP_LABELS = [ "Fetch branches", "Search commits", "Select commits", "Create release branch", "Cherry-pick", "Build", "Trace dependencies", "Summary", ]; /** * The flagship migrated workflow: same `runMoveCodeWorkflow`/`buildMoveCodeInput` * business logic the traditional `smdg git move-code` command calls, fed by an * InkInteractionService instead of PlainCliInteractionService. Every * select/input/confirm/multiSelect touchpoint renders via * (mounted alongside this screen by SmdgTerminalApp); every notify() call * lands in the shell's permanent scrollback — that stream is this screen's * source of truth for what happened, so there's no separate summary to keep * in sync. */ export function GitMoveCodeScreen(props: { service: InkInteractionService; onDone: (success: boolean) => void }) { const theme = useTerminalTheme(); const [stepIndex, setStepIndex] = useState(0); const startedRef = useRef(false); useEffect(() => { const onNotify = (notification: TNotification) => { if (notification.level === "step" && notification.current !== undefined) { setStepIndex(notification.current - 1); } }; props.service.on("notify", onNotify); return () => { props.service.off("notify", onNotify); }; }, [props.service]); useEffect(() => { if (startedRef.current) return; startedRef.current = true; const ctx: TWorkflowContext = { interaction: props.service, signal: props.service.signal }; void (async () => { try { const repositoryPath = await resolveRepositoryPath(process.cwd()); const input = await buildMoveCodeInput({}, ctx); const results = await runMoveCodeWorkflow(input, [repositoryPath], ctx); const failed = results.some((result) => result.status === "CONFLICT" || result.status === "ABORTED"); props.onDone(!failed); } catch (error) { if (!(error instanceof InteractionCancelledError) && !props.service.signal.aborted) { props.service.notify({ level: "error", message: error instanceof Error ? error.message : String(error) }); } props.onDone(false); } })(); // Intentionally runs once per mount — a fresh GitMoveCodeScreen instance // (and a fresh InkInteractionService/AbortController) is created per launch. // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( Move Code Assistant ); }