145 lines
3.9 KiB
Go
145 lines
3.9 KiB
Go
package tui
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
tea "charm.land/bubbletea/v2"
|
|
|
|
"superwork-tui/internal/auth"
|
|
"superwork-tui/internal/cc"
|
|
"superwork-tui/internal/config"
|
|
"superwork-tui/internal/files"
|
|
"superwork-tui/internal/git"
|
|
"superwork-tui/internal/gitea"
|
|
"superwork-tui/internal/issue"
|
|
)
|
|
|
|
type branchSyncStatusMsg struct {
|
|
status git.BranchSyncStatus
|
|
err error
|
|
}
|
|
|
|
type branchSyncDoneMsg struct {
|
|
devBranch string
|
|
autoBuildBranch string
|
|
err error
|
|
}
|
|
|
|
type envLockDoneMsg struct {
|
|
locked bool
|
|
result files.EnvLockResult
|
|
err error
|
|
}
|
|
|
|
type prDiffResultMsg struct {
|
|
issueNumber int
|
|
prDiffFile string
|
|
err error
|
|
}
|
|
|
|
// branchSyncCheckCmd checks branch sync status and returns branchSyncStatusMsg.
|
|
func branchSyncCheckCmd() tea.Cmd {
|
|
return func() tea.Msg {
|
|
ctx := context.Background()
|
|
root, err := workspaceRootFn()
|
|
if err != nil {
|
|
return branchSyncStatusMsg{err: err}
|
|
}
|
|
settings, err := config.Load()
|
|
if err != nil {
|
|
return branchSyncStatusMsg{err: fmt.Errorf("load settings: %w", err)}
|
|
}
|
|
// Empty autoBuildBranch means "follow devBranch"; resolving here makes
|
|
// the check fall into the same-branch (Unavailable) path, matching TS.
|
|
autoBuildBranch := settings.AutoBuildBranch
|
|
if autoBuildBranch == "" {
|
|
autoBuildBranch = settings.DevBranch
|
|
}
|
|
status, err := git.CheckBranchSync(ctx, git.BranchSyncOpts{
|
|
WorkspaceRoot: root,
|
|
DevBranch: settings.DevBranch,
|
|
AutoBuildBranch: autoBuildBranch,
|
|
})
|
|
return branchSyncStatusMsg{status: status, err: err}
|
|
}
|
|
}
|
|
|
|
// branchSyncRunCmd pushes devBranch to autoBuildBranch, resolving workspace root internally.
|
|
func branchSyncRunCmd(devBranch, autoBuildBranch string) tea.Cmd {
|
|
return func() tea.Msg {
|
|
ctx := context.Background()
|
|
root, err := workspaceRootFn()
|
|
if err != nil {
|
|
return branchSyncDoneMsg{devBranch: devBranch, autoBuildBranch: autoBuildBranch, err: err}
|
|
}
|
|
err = git.RunBranchSync(ctx, git.BranchSyncOpts{
|
|
WorkspaceRoot: root,
|
|
DevBranch: devBranch,
|
|
AutoBuildBranch: autoBuildBranch,
|
|
})
|
|
return branchSyncDoneMsg{devBranch: devBranch, autoBuildBranch: autoBuildBranch, err: err}
|
|
}
|
|
}
|
|
|
|
// envLockToggleCmd locks or unlocks .env files based on current state.
|
|
func envLockToggleCmd(currentlyLocked bool) tea.Cmd {
|
|
return func() tea.Msg {
|
|
root, err := workspaceRootFn()
|
|
if err != nil {
|
|
return envLockDoneMsg{err: err}
|
|
}
|
|
var result files.EnvLockResult
|
|
if currentlyLocked {
|
|
result, err = files.UnlockEnvFiles(root)
|
|
return envLockDoneMsg{locked: false, result: result, err: err}
|
|
}
|
|
result, err = files.LockEnvFiles(root)
|
|
return envLockDoneMsg{locked: true, result: result, err: err}
|
|
}
|
|
}
|
|
|
|
// prDiffSummaryCmd asks Claude to summarize the PR diff and write it to docs/pr-diff/<branch>.md.
|
|
func prDiffSummaryCmd(iss issue.Issue) tea.Cmd {
|
|
return func() tea.Msg {
|
|
ctx := context.Background()
|
|
root, err := workspaceRootFn()
|
|
if err != nil {
|
|
return prDiffResultMsg{issueNumber: iss.Number, err: err}
|
|
}
|
|
|
|
safeBranch := strings.ReplaceAll(iss.Branch, "/", "-")
|
|
outFile := filepath.Join("docs", "pr-diff", safeBranch+".md")
|
|
|
|
prompt := fmt.Sprintf(
|
|
"请阅读 PR #%s(分支 %s)的变更,并将综合摘要写入 %s。"+
|
|
"摘要应包括:改动内容、改动原因、关键技术决策以及测试方案。",
|
|
iss.PR, iss.Branch, outFile,
|
|
)
|
|
|
|
_, spawnErr := cc.SpawnClaude(ctx, cc.Opts{
|
|
Prompt: prompt,
|
|
Cwd: root,
|
|
})
|
|
if spawnErr != nil {
|
|
return prDiffResultMsg{issueNumber: iss.Number, err: spawnErr}
|
|
}
|
|
|
|
// Persist prDiffFile to Gitea state JSON (best-effort).
|
|
remote, rErr := git.DetectRepo(root)
|
|
if rErr == nil {
|
|
token, tErr := auth.ResolveGiteaToken(remote.Host)
|
|
if tErr == nil {
|
|
client := gitea.New(remote.Host, token)
|
|
_ = issue.MergeStateJSON(ctx, client, remote.Owner, remote.Repo, iss.Number, map[string]any{
|
|
"prDiffFile": outFile,
|
|
})
|
|
}
|
|
}
|
|
|
|
return prDiffResultMsg{issueNumber: iss.Number, prDiffFile: outFile}
|
|
}
|
|
}
|