Files
2026-08-20 23:42:02 +08:00

10 KiB
Raw Permalink Blame History

git-init 架构

本文说明组件如何分工、密钥放在哪、各条 Git 路径怎么走。使用步骤见 使用文档.md

设计目标

  • 对话里只出现 Gitea 仓库 URL 和 SSH/GPG 选择,不出现 Token
  • 同一套本机档案服务于初始化以及之后每一次提交、推送、克隆、拉取
  • 默认远程为 Gitea(自建域名与自定义端口);脚本按 URL 解析 host/port
  • Windows PowerShell 可执行;不用 eval "$(ssh-agent -s)"
  • 可复用:源码在仓库 .cursor/skills/git-initinstall.ps1 同步到个人 Skill 目录

逻辑架构

flowchart LR
  user[UserCursorChat]
  agent[AgentPlusSkill]
  scripts[PowerShellScripts]
  profile[ProfileJson]
  tokenFile[TokenDpapi]
  gcm[GitCredentialManager]
  sshAgent[WindowsSshAgent]
  gpg[GpgKeyring]
  git[GitRepo]
  remote[GiteaHost]

  user --> agent
  agent --> scripts
  scripts --> profile
  scripts --> tokenFile
  tokenFile -->|"decrypt CurrentUser"| gcm
  scripts --> sshAgent
  scripts --> gpg
  agent --> git
  gcm --> remote
  sshAgent --> remote
  git --> remote

三层:

  1. Skill 指令SKILL.md):路由到 Init / Commit / Push / Clone 等,并列出安全红线
  2. 脚本scripts/):状态检查、加密存取、注入凭据、SSH/GPGstdout 不含 Token
  3. 本机秘密%USERPROFILE%\.git-skills\):与任何项目仓库隔离

目录与职责

flowchart TB
  subgraph repo [git-skillsRepo]
    skillMd[SKILL.md]
    workflows[workflows.md]
    reference[reference.md]
    examples[examples.md]
    scriptsDir[scripts]
    installPs[install.ps1]
    docsDir[docs]
  end

  subgraph personal [UserHome]
    personalSkill[".cursor/skills/git-init"]
    secrets[".git-skills"]
  end

  installPs -->|"copy skill not secrets"| personalSkill
  scriptsDir --> secrets
路径 职责
.cursor/skills/git-init/SKILL.md Agent 入口:路由、红线、Init/Commit/Push
workflows.md Clone / Pull / Branch / Merge / Tag / Stash / Inspect
reference.md 中文提交规范、Windows SSH/GPG、故障表
examples.md 触发例句
scripts/*.ps1 可执行步骤;失败用退出码和 key=value
install.ps1 复制 Skill 到 ~/.cursor/skills/git-init
%USERPROFILE%\.git-skills\profile.json 姓名、邮箱、HTTPS 用户名、SSH 路径
%USERPROFILE%\.git-skills\token.dpapi 仅 TokenDPAPI CurrentUser

Agent 可以profile.json(经 profile-status.ps1)。Agent 不得token.dpapi

信任边界

flowchart TB
  subgraph chat [CursorChat]
    urlIn[RemoteUrl]
    talk[NoTokenInChat]
  end

  subgraph machine [ThisWindowsUser]
    dpapi[DPAPI]
    gcm[GCM]
    sshKeys[SSHPrivateKey]
    gpgKeys[GPGSecretKey]
  end

  subgraph project [GitWorkTree]
    gitDir[.git]
    ignore[.gitignore]
  end

  subgraph host [Gitea]
    origin[origin]
  end

  urlIn --> gitDir
  dpapi -->|"git credential approve stdin"| gcm
  gcm --> origin
  sshKeys --> origin
  gitDir --> origin
  ignore -->|"block secrets"| gitDir
  • 解密只在当前 Windows 用户下有效;换用户或换机器无法读 Token
  • 注入走 stdin 给 git credential approve,不写进命令行参数、不写进仓库
  • 项目 .gitignore 忽略 .git-skills/*.dpapi、私钥

脚本调用关系

flowchart TB
  storeProfile[store-profile.ps1]
  storeToken[store-token.ps1]
  profileStatus[profile-status.ps1]
  tokenStatus[token-status.ps1]
  prepareCommit[prepare-commit.ps1]
  prepareAuth[prepare-auth.ps1]
  preparePush[prepare-push.ps1]
  inject[inject-credential.ps1]
  ensureSsh[ensure-ssh.ps1]
  ensureGpg[ensure-gpg.ps1]
  assertSecrets[assert-no-secrets.ps1]
  repoStatus[repo-status.ps1]
  common[common.ps1]

  storeProfile --> common
  storeToken --> common
  profileStatus --> common
  tokenStatus --> common
  prepareCommit --> common
  prepareAuth --> inject
  prepareAuth --> ensureSsh
  preparePush --> prepareAuth
  inject --> common
  ensureSsh --> common
  ensureGpg --> common
  assertSecrets --> common
  repoStatus --> common
脚本 谁跑 作用
store-profile.ps1 / store-token.ps1 用户本机终端 交互写入档案 / 加密 Token
profile-status.ps1 / token-status.ps1 Agent present / missing,无密钥明文
prepare-commit.ps1 Agent 本地作者 + commit.gpgsign
prepare-auth.ps1 Agent 按 URL 注入 GCM 或 ssh-add(克隆可用)
prepare-push.ps1 Agent origin 再调 prepare-auth
ensure-ssh.ps1 / ensure-gpg.ps1 Agent 复用或生成密钥;日常推送 -NoGenerate -Quiet
assert-no-secrets.ps1 Agent 提交前拦截密钥路径
repo-status.ps1 Agent 分支、脏否、打码后的 origin

主流程

总路由

flowchart TD
  start[UserRequest]
  start --> route{Intent}
  route -->|init| initPath[Init]
  route -->|commit| commitPath[Commit]
  route -->|push| pushPath[Push]
  route -->|commitAndPush| commitPath
  commitPath -->|ifAskedPush| pushPath
  route -->|clone| clonePath[Clone]
  route -->|pullOrFetch| syncPath[Sync]
  route -->|branchMergeTagStash| localPath[LocalGit]
  route -->|statusLogDiff| inspectPath[Inspect]
  route -->|commitButNotRepo| initPath

初始化

flowchart TD
  detect[DetectRepoAndRemote]
  detect --> profile{ProfileAndToken}
  profile -->|missing| stopStore[StopTellUserRunStoreScripts]
  profile -->|ok| askUrl[NeedRemoteUrl]
  askUrl --> initGit[git init plus local author]
  initGit --> addOrigin[remote add origin]
  addOrigin --> inject[inject-credential]
  inject --> ssh[ensure-ssh plus ssh test]
  ssh --> gpg[ensure-gpg plus gpgsign]
  gpg --> hygiene[gitignore and README]
  hygiene --> firstCommit[Commit path]
  firstCommit --> firstPush[Push main]
  firstPush --> verify[status remote log signature]

每次提交

flowchart TD
  prep[prepare-commit]
  prep --> signing{signing present}
  signing -->|missing| gpg[ensure-gpg]
  gpg --> prep
  signing -->|ok| inspect[status diff log]
  inspect --> secrets[assert-no-secrets]
  secrets -->|blocked| unstage[Unstage secret paths]
  secrets -->|ok| stage[Stage related files]
  stage --> msg[Chinese type colon description]
  msg --> commit[git commit -S]
  commit --> check[log show-signature]
  check --> maybePush{User asked push}
  maybePush -->|yes| pushPath[Push]
  maybePush -->|no| done[Stop]

每次推送 / 克隆 / 拉取

flowchart TD
  needAuth[NeedNetworkAuth]
  needAuth --> hasRepo{Inside work tree}
  hasRepo -->|yes| prepPush[prepare-push]
  hasRepo -->|no clone| prepAuth[prepare-auth with URL]
  prepPush --> proto{Protocol}
  prepAuth --> proto
  proto -->|https| gcm[GCM uses injected token]
  proto -->|ssh| sshAdd[ssh-add Quiet]
  gcm --> gitNet[git push or clone or pull]
  sshAdd --> gitNet

HTTPSToken 解密后仅进入 git credential approve 的 stdin。SSH:用已有 id_ed25519,日常路径不生成新密钥、不打印公钥。

Agent 控制流

sequenceDiagram
  participant U as User
  participant A as Agent
  participant S as Scripts
  participant G as Git
  participant H as GitHost

  U->>A: 提交并推送
  A->>S: profile-status token-status repo-status
  S-->>A: present plus branch info
  A->>S: prepare-commit
  A->>S: assert-no-secrets
  A->>G: git commit -S
  A->>S: prepare-push
  S->>G: credential approve or ssh-add
  A->>G: git push
  G->>H: objects
  A-->>U: 作者签名与推送结果无 Token

与原始任务的对应

相关文档.md 中的步骤映射:

文档章节 实现
一 init git init -b main
二 用户信息 profile.json + 仓库 local user.name / user.email
三 远程 git remote add origin(已有则不改)
四 Token DPAPI + GCM,禁止写入代码
五 SSH ensure-ssh.ps1 + Windows ssh-agent
六 GPG ensure-gpg.ps1 + commit.gpgsign
七 / 十二 中文提交 每次 Commit 路径强制 类型: 描述
八 README 缺失才生成
九 / 十 首次提交推送 Init 末尾走 Commit + Push
十一 验收 status / remote -v / log --show-signature

Gitea URL 与凭据

flowchart TD
  url[GiteaRemoteUrl]
  url --> parse[Parse-GitRemoteUrl]
  parse --> httpsPath{Protocol}
  httpsPath -->|https or http| credHost[CredentialHost may include port]
  httpsPath -->|ssh| sshHost[Host plus optional Port]
  credHost --> inject[inject-credential to GCM]
  sshHost --> sshTest[test-ssh with ssh -p]
  inject --> pushHttps[git push HTTPS]
  sshTest --> pushSsh[git push SSH]
URL 示例 CredentialHost SSH 测试
https://git.example.com/a/b.git git.example.com 不适用
https://git.example.com:3000/a/b.git git.example.com:3000 不适用
git@git.example.com:a/b.git 主机名(供日后 HTTPS ssh -T git@...
ssh://git@git.example.com:2222/a/b.git 主机名(不含 2222 ssh -p 2222 -T git@...

细节见 gitea.md

Gitea 密钥验证(官方行为)

依据 docs.gitea.com/administration/signing:灰色开锁 = 数据库中找不到可校验密钥。用户密钥须在 /user/settings/keys 验证后,提交签名才能被识别。

flowchart TD
  addKey[AddPubkeyOnGitea]
  addKey --> verifyClick[ClickVerifyOnSettings]
  verifyClick --> challenge[PageShowsToken]
  challenge --> localSign[verify-gitea-keys with tokens from chat]
  localSign --> paste[AgentShowsSignatureBlocks]
  paste --> verified[KeyMarkedVerified]
  verified --> newCommit[NewSignedCommitShowsVerified]
脚本 对齐官方 UI
verify-gitea-keys.ps1 编排:可缺令牌时提示 need_token;有令牌则调下面两个脚本
verify-gpg-challenge.ps1 echo "TOKEN" | gpg -a --default-key KEY --detach-sig(有换行)
verify-ssh-challenge.ps1 echo -n 'TOKEN' | ssh-keygen -Y sign -n gitea -f KEY(无换行)

HTTPS 访问令牌禁止进对话。页面 验证挑战令牌可由 Agent 主动询问(一次性,不落盘)。