✨ feat(spx): 按 git origin 支持 GitHub 仓库,state 评论改成隐藏注释格式,新增 issue view / pr view / pr create

This commit is contained in:
cruldra committed 2026-10-19 07:41:13 +11:00
1 parent 64d5961b62
commit c90d84d7f2
17 files changed
+2683 -400

No files matched your search

+83
View File
@@ -0,0 +1,83 @@
package forge
import (
"errors"
"fmt"
"os"
"os/exec"
"strings"
"github.com/cruldra/superpowers-vscode/cli/internal/repo"
"github.com/cruldra/superpowers-vscode/cli/internal/tea"
)
// Env is where [Open] gets credentials from; tests swap in canned answers, real runs use [SystemEnv].
type Env struct {
// GitHubToken returns the token the GitHub CLI holds for `host`.
GitHubToken func(host string) (string, error)
// TeaLogin returns the tea login whose URL points at `host`, or `nil` when there is none.
TeaLogin func(host string) (*tea.TeaLogin, error)
// Getenv reads an environment variable.
Getenv func(key string) string
}
// SystemEnv reads credentials from this machine: `gh auth token`, the tea config file and the process environment.
func SystemEnv() Env {
return Env{
GitHubToken: ghAuthToken,
TeaLogin: func(host string) (*tea.TeaLogin, error) {
path, err := tea.DefaultConfigPath()
if err != nil {
return nil, err
}
return tea.LoginForHost(path, host)
},
Getenv: os.Getenv,
}
}
// ghAuthToken asks the GitHub CLI for the token it stores for `host`.
func ghAuthToken(host string) (string, error) {
out, err := exec.Command("gh", "auth", "token", "--hostname", host).Output()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) && len(exitErr.Stderr) > 0 {
return "", fmt.Errorf("gh auth token --hostname %s 失败: %w: %s", host, err, strings.TrimSpace(string(exitErr.Stderr)))
}
return "", fmt.Errorf("gh auth token --hostname %s 失败: %w", host, err)
}
return string(out), nil
}
// Open connects to the repo at `o` on the forge its host names, picking credentials the way that forge expects.
//
// GitHub takes the token from `gh auth token`. Gitea takes the token of the tea login for the same host, and its
// URL as the API root; with no such login, or one without a token, it falls back to `GITEA_TOKEN` and the origin's web root.
func Open(o repo.Origin, env Env) (Forge, error) {
if KindForHost(o.Host) == KindGitHub {
token, err := env.GitHubToken(o.Host)
if err != nil {
return nil, fmt.Errorf("取 GitHub token 失败,请先运行 `gh auth login`: %w", err)
}
token = strings.TrimSpace(token)
if token == "" {
return nil, fmt.Errorf("gh 没有 %s 的 token,请先运行 `gh auth login`", o.Host)
}
return NewGitHub(GitHubAPIBaseURL, token, o.Owner, o.Repo), nil
}
login, err := env.TeaLogin(o.Host)
if err != nil {
return nil, err
}
baseURL, token := o.WebURL, ""
if login != nil {
baseURL, token = login.URL, login.Token
}
if token == "" {
token = env.Getenv("GITEA_TOKEN")
}
if token == "" {
return nil, fmt.Errorf("没有 %s 的 Gitea token:tea config 里没有该 host 的 login 或其 token 为空,GITEA_TOKEN 也没设置;请运行 `tea login add` 或设置 GITEA_TOKEN", o.Host)
}
return NewGitea(baseURL, token, o.Owner, o.Repo), nil
}