# git-init 架构 本文说明组件如何分工、密钥放在哪、各条 Git 路径怎么走。使用步骤见 [使用文档.md](使用文档.md)。 ## 设计目标 - **对话里只出现 Gitea 仓库 URL 和 SSH/GPG 选择**,不出现 Token - **同一套本机档案**服务于初始化以及之后每一次提交、推送、克隆、拉取 - **默认远程为 Gitea**(自建域名与自定义端口);脚本按 URL 解析 host/port - **Windows PowerShell** 可执行;不用 `eval "$(ssh-agent -s)"` - **可复用**:源码在仓库 `.cursor/skills/git-init`,`install.ps1` 同步到个人 Skill 目录 ## 逻辑架构 ```mermaid 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/GPG;stdout 不含 Token 3. **本机秘密**(`%USERPROFILE%\.git-skills\`):与任何项目仓库隔离 ## 目录与职责 ```mermaid 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` | 仅 Token,DPAPI `CurrentUser` | Agent **可以**读 `profile.json`(经 `profile-status.ps1`)。Agent **不得**读 `token.dpapi`。 ## 信任边界 ```mermaid 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`、私钥 ## 脚本调用关系 ```mermaid 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 | ## 主流程 ### 总路由 ```mermaid 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 ``` ### 初始化 ```mermaid 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] ``` ### 每次提交 ```mermaid 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] ``` ### 每次推送 / 克隆 / 拉取 ```mermaid 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 ``` HTTPS:Token 解密后仅进入 `git credential approve` 的 stdin。SSH:用已有 `id_ed25519`,日常路径不生成新密钥、不打印公钥。 ## Agent 控制流 ```mermaid 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](../相关文档.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 与凭据 ```mermaid 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](../.cursor/skills/git-init/gitea.md)。 ## Gitea 密钥验证(官方行为) 依据 [docs.gitea.com/administration/signing](https://docs.gitea.com/administration/signing/):灰色开锁 = 数据库中找不到可校验密钥。用户密钥须在 `/user/settings/keys` **验证**后,提交签名才能被识别。 ```mermaid 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 主动询问(一次性,不落盘)。