async function handleShellActions(event, target, action, repository) { if (action === "navigate") { ui.currentView = target.dataset.view; ui.modal = null; render(); if (ui.currentView === "deployments" && (ui.boot?.state?.servers || []).length && !(ui.serverDiscovery || []).length) { setLoading(true, "Reading Docker, Compose and DockerMan inventory from Unraid…"); await refreshDeploymentTruth(true); setLoading(false); } } else if (action === "select-repo") selectRepository(target.dataset.id); else if (action === "open-deployment-link") { if (!repository) return true; selectRepository(repository.id, false); ui.selectedProfileId = target.dataset.profileId || selectedProfile(repository)?.id || null; ui.repositoryTab = "deployments"; ui.currentView = "repository"; render(); } else if (action === "select-deployment-profile") { ui.selectedProfileId = target.dataset.profileId || null; ui.repositoryTab = "deployments"; render(); } else if (action === "refresh") { await refreshRepositories(true); await refreshActiveOperations(false); await refreshDeploymentTruth(false); } else if (action === "refresh-operations") { setLoading(true, "Refreshing deployment operations and live server state…"); await refreshActiveOperations(); await refreshDeploymentTruth(true); setLoading(false); } else if (action === "toggle-theme") { const appearance = document.documentElement.dataset.theme === "dark" ? "light" : "dark"; applyTheme(appearance); ui.boot.state = await window.forgeflow.setAppearance(appearance); render(); } else if (action === "open-palette") { ui.paletteQuery = ""; ui.modal = { type: "command-palette" }; render(); } else if (action === "repo-tab") { ui.repositoryTab = target.dataset.tab; if (ui.repositoryTab === "gittools" && !ui.branches.length) await loadGitTools(repository); else if (ui.repositoryTab === "validator" && !ui.gitValidation) { setLoading(true, "Validating Git and Gitea best practices…"); try { ui.gitValidation = await window.forgeflow.gitValidatorScan( repository.fullName, ); } catch (error) { showToast("Git Validator failed", error.message, "error"); } finally { setLoading(false); } render(); } else if (ui.repositoryTab === "settings") { try { ui.pullRequests = await window.forgeflow.pullRequests( repository.fullName, "open", ); } catch (error) { ui.pullRequests = []; showToast("Could not load pull requests", error.message, "error"); } render(); } else render(); } else if (action === "git-validator-scan") { setLoading(true, "Validating Git and Gitea best practices…"); try { ui.gitValidation = await window.forgeflow.gitValidatorScan( repository.fullName, ); showToast( "Git validation complete", `${ui.gitValidation.score}/100 · ${ui.gitValidation.grade}`, ui.gitValidation.summary.errors ? "error" : "success", ); } catch (error) { showToast("Git Validator failed", error.message, "error"); } finally { setLoading(false); } } else if (action === "git-validator-repair") { const check = ui.gitValidation?.checks?.[Number(target.dataset.checkIndex)]; if (!check?.fixAction) return; let preview; try { preview = await window.forgeflow.gitValidatorPreviewRepair(repository.fullName, check); } catch (error) { showToast("Preview failed", error.message, "error"); return; } if (!confirm(`${check.confirmation || `Apply ${check.title}?`}\n\nReviewable change:\n${preview.diff}\n\nNothing will be committed or pushed.`)) return; setLoading(true, `Repairing ${check.title}…`); try { await window.forgeflow.gitValidatorRepair(repository.fullName, check); await refreshRepositories(false, true); ui.gitValidation = await window.forgeflow.gitValidatorScan( repository.fullName, ); showToast("Git best practice repaired", check.title, "success"); } catch (error) { showToast("Repair failed", error.message, "error"); } finally { setLoading(false); } } else if (action === "git-validator-policy") { setLoading(true, "Applying assurance policy…"); try { await window.forgeflow.gitValidatorSetPolicy(repository.fullName, { id: target.value }); ui.gitValidation = await window.forgeflow.gitValidatorScan(repository.fullName); showToast("Policy updated", ui.gitValidation.policy.label, "success"); } catch (error) { showToast("Policy update failed", error.message, "error"); } finally { setLoading(false); } } else if (action === "git-validator-export") { try { const exported = await window.forgeflow.gitValidatorExport(repository.fullName, target.dataset.format || "markdown"); const url = URL.createObjectURL(new Blob([exported.content], { type: exported.mimeType })); const link = document.createElement("a"); link.href = url; link.download = `${repository.name || "repository"}-git-assurance.${exported.extension}`; link.click(); URL.revokeObjectURL(url); showToast("Report exported", link.download, "success"); } catch (error) { showToast("Export failed", error.message, "error"); } } else if (action === "git-validator-suppress") { const check = ui.gitValidation?.checks?.[Number(target.dataset.checkIndex)]; if (!check) return; const reason = prompt("Reason for this temporary exception (minimum 10 characters):", "Accepted temporarily while remediation is tracked."); if (!reason) return; const author = prompt("Exception owner:", ui.boot?.state?.gitea?.user?.login || ""); if (!author) return; const ticket = prompt("Ticket reference (optional):", "") || ""; const expiresAt = new Date(Date.now() + 7 * 86_400_000).toISOString(); try { await window.forgeflow.gitValidatorSuppress(repository.fullName, { checkId: check.id, reason, author, ticket, expiresAt, scope: "repository", evidence: `${ui.gitValidation.commitSha || "unknown"}:${check.id}:${check.status}` }); ui.gitValidation = await window.forgeflow.gitValidatorScan(repository.fullName); showToast("Exception documented", `Expires ${formatDate(expiresAt)}.`, "success"); } catch (error) { showToast("Exception rejected", error.message, "error"); } } else if (action === "toggle-favorite") { ui.boot.state = await window.forgeflow.favoriteRepository( repository.fullName, !repository.favorite, ); repository.favorite = !repository.favorite; render(); } else if (action === "select-file") { if (event.target.matches("input[type=checkbox]")) return; ui.selectedFile = target.dataset.path; await loadDiff(repository, ui.selectedFile); } else if (action === "toggle-all-files") { const files = repository.localStatus?.files || []; ui.selectedFiles = ui.selectedFiles.size === files.length ? new Set() : new Set(files.map((file) => file.path)); render(); } else if (action === "copy-diff") { await navigator.clipboard.writeText(ui.diff || ""); showToast("Copied", "Diff copied to clipboard.", "success"); } else if (action === "open-hunk-staging") { if (ui.diffHunks?.partialSupported) { ui.modal = { type: "hunk-staging" }; render(); } } else if (action === "stage-chosen-hunks") { const indexes = [ ...document.querySelectorAll("[data-hunk-index]:checked"), ].map((input) => Number(input.dataset.hunkIndex)); if (!indexes.length) return; const result = await runOperation( "Staging selected hunks…", () => window.forgeflow.stageHunks( repository.localPath, ui.selectedFile, indexes, ), "Selected hunks staged.", ); if (result) { ui.modal = null; await loadDiff(selectedRepository(), ui.selectedFile); } } else if (action === "open-file-editor") { await window.forgeflow .openEditor(repository.localPath, ui.selectedFile) .catch((error) => showToast("Could not open editor", error.message, "error"), ); } else if (action === "open-editor") { await window.forgeflow .openEditor(repository.localPath) .catch((error) => showToast("Could not open editor", error.message, "error"), ); } else if (action === "open-terminal") { await window.forgeflow .openTerminal(repository.localPath) .catch((error) => showToast("Could not open terminal", error.message, "error"), ); } else if (action === "load-conflicts") { ui.conflictState = await window.forgeflow.conflictState( repository.localPath, ); ui.modal = { type: "conflict-guide" }; render(); } else if (action === "resolve-conflict") { if ( !ui.selectedFile || !confirm(`Apply “${target.dataset.resolution}” to ${ui.selectedFile}?`) ) return; ui.conflictState = await window.forgeflow.resolveConflict( repository.localPath, ui.selectedFile, target.dataset.resolution, ); await refreshRepositories(false); ui.modal = { type: "conflict-guide" }; render(); } else if (action === "open-conflict-file") { await window.forgeflow.openEditor( repository.localPath, target.dataset.path, ); } else if (action === "continue-git-operation") { ui.conflictState = await window.forgeflow.continueGitOperation( repository.localPath, ); ui.modal = null; await refreshRepositories(false); showToast( "Git operation continued", "The repository operation completed.", "success", ); } else if (action === "abort-git-operation") { if ( !confirm( "Abort the active Git operation? Conflict-resolution work may be discarded.", ) ) return; await window.forgeflow.abortGitOperation(repository.localPath); ui.modal = null; await refreshRepositories(false); } else if (action === "check-branch-protection") { ui.branchProtection = await window.forgeflow.branchProtection( repository.fullName, repository.localStatus.branch.head, ); showToast( ui.branchProtection.protected ? "Protected branch" : "Branch is not protected", ui.branchProtection.protected ? `${ui.branchProtection.requiredApprovals} approval(s) required.` : "Direct pushes are permitted by the reported branch rule.", ui.branchProtection.protected ? "info" : "success", ); render(); } else if (action === "open-pull-request") { const subject = ui.history[0]?.subject || repository.localStatus.branch.head; ui.modal = { type: "pull-request", title: subject, body: `## Summary\n\nChanges from ${repository.localStatus.branch.head}.`, }; render(); } else if (action === "load-pull-requests") { try { ui.pullRequests = await window.forgeflow.pullRequests( repository.fullName, "open", ); render(); } catch (error) { showToast("Could not load pull requests", error.message, "error"); } } else if (action === "open-pull-request-url") { if (target.dataset.url) await window.forgeflow.openExternal(target.dataset.url); } else if (action === "create-pull-request") { setLoading(true, "Creating pull request…"); try { const pull = await window.forgeflow.createPullRequest( repository.fullName, document.querySelector("#pr-title").value, document.querySelector("#pr-body").value, document.querySelector("#pr-base").value, ); ui.modal = null; ui.pullRequests = await window.forgeflow .pullRequests(repository.fullName, "open") .catch(() => ui.pullRequests); showToast("Pull request created", `#${pull.number}`, "success"); if (pull.html_url) await window.forgeflow.openExternal(pull.html_url); } catch (error) { showToast("Could not create pull request", error.message, "error"); } setLoading(false); } else if (action === "copy-logs") { const text = (ui.activeDeployment?.logs || []).join("\n"); await navigator.clipboard.writeText(text); showToast( "Copied", "Safe operation output copied. Open Gitea for raw runner logs.", "success", ); } else if (action === "stage-selected") { if (!repository?.localPath || !ui.selectedFiles.size) return; await runOperation( "Staging selected files…", () => window.forgeflow.stageFiles(repository.localPath, [ ...ui.selectedFiles, ]), "Files staged.", ); } else if (action === "unstage-selected") { if (!repository?.localPath || !ui.selectedFiles.size) return; await runOperation( "Unstaging selected files…", () => window.forgeflow.unstageFiles(repository.localPath, [ ...ui.selectedFiles, ]), "Files unstaged.", ); } else if (action === "commit-push" || action === "commit-only") { if ( !repository?.localPath || !ui.commitMessage.trim() || (!ui.selectedFiles.size && !repository.localStatus?.counts?.staged) ) return; const selected = [...ui.selectedFiles]; const result = await runOperation( action === "commit-push" ? "Committing and pushing…" : "Creating local commit…", () => action === "commit-push" ? selected.length ? window.forgeflow.commitAndPush( repository.localPath, ui.commitMessage, selected, ) : window.forgeflow.commitStagedAndPush( repository.localPath, ui.commitMessage, ) : selected.length ? window.forgeflow.commit( repository.localPath, ui.commitMessage, selected, ) : window.forgeflow.commitStaged( repository.localPath, ui.commitMessage, ), action === "commit-push" ? "Changes committed and pushed to Gitea." : "Local commit created.", ); if (result) { ui.commitMessage = ""; ui.selectedFiles.clear(); ui.selectedFile = null; ui.diff = ""; } } else if (action === "push") await runOperation( "Pushing local commits…", () => window.forgeflow.push(repository.localPath), "Push completed.", ); else if (action === "fetch") await runOperation( "Fetching from Gitea…", () => window.forgeflow.fetch(repository.localPath), "Remote state refreshed.", ); else if (action === "pull") await runOperation( "Synchronizing from Gitea…", () => window.forgeflow.pull(repository.localPath), "Local branch fast-forwarded.", ); else if (action === "load-history") { setLoading(true, "Loading commit history…"); try { ui.history = await window.forgeflow.history(repository.localPath, 50); } catch (error) { showToast("History unavailable", error.message, "error"); } setLoading(false); } else if (action === "load-git-tools") await loadGitTools(repository); else if (action === "create-branch") { const branch = document.querySelector("#new-branch-name")?.value.trim(); if (branch) await runOperation( `Creating ${branch}…`, () => window.forgeflow.createBranch(repository.localPath, branch), `Switched to ${branch}.`, ); await loadGitTools(selectedRepository()); } else if (action === "checkout-branch") { await runOperation( `Switching to ${target.dataset.branch}…`, () => window.forgeflow.checkoutBranch( repository.localPath, target.dataset.branch, ), `Switched to ${target.dataset.branch}.`, ); await loadGitTools(selectedRepository()); } else if (action === "stash-changes") { const result = await runOperation( "Stashing local changes…", () => window.forgeflow.stash( repository.localPath, `ForgeFlow ${new Date().toLocaleString()}`, ), "Local changes stashed.", ); if (result) ui.stashes = result.stashes; } else if (action === "pop-stash") { const result = await runOperation( `Applying ${target.dataset.stashRef}…`, () => window.forgeflow.popStash( repository.localPath, target.dataset.stashRef, ), "Stash applied.", ); if (result) ui.stashes = result.stashes; } else if (action === "open-path") await window.forgeflow .openPath(repository.localPath) .catch((error) => showToast("Could not open folder", error.message, "error"), ); else if (action === "open-gitea") await window.forgeflow .openExternal(repository.htmlUrl) .catch((error) => showToast("Could not open Gitea", error.message, "error"), ); else if (action === "link-repo") { const localPath = await window.forgeflow.selectDirectory({ title: `Link local folder for ${repository.name}`, }); if (localPath) { ui.repositories = (await runOperation( "Linking local repository…", () => window.forgeflow.linkRepository(repository.fullName, localPath), "Local folder linked.", { refresh: false }, )) || ui.repositories; selectRepository(repository.id); } } else if (action === "unlink-repo") { ui.repositories = (await runOperation( "Removing local link…", () => window.forgeflow.unlinkRepository(repository.fullName), "Repository link removed.", { refresh: false }, )) || ui.repositories; selectRepository(repository.id); } else if (action === "clone-repo" || action === "clone-repo-custom") { const mode = action === "clone-repo-custom" ? "custom" : "default"; const clone = await runOperation( mode === "custom" ? "Choosing location and cloning repository…" : `Cloning ${repository.name} into the default project root…`, () => window.forgeflow.cloneRepository(repository.fullName, mode), null, { refresh: false }, ); if (clone?.cancelled) return; if (clone?.target) { if (clone.state) ui.boot.state = clone.state; ui.repositories = clone.repositories || (await window.forgeflow.refreshRepositories()); selectRepository(repository.id); showToast( clone.reused ? "Existing repository linked" : "Repository cloned", clone.target, "success", ); } } else return false; return true; }