msdevflow 0.7.4 → 0.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +39 -40
  2. package/lib/bootstrap.js +485 -103
  3. package/package.json +2 -2
  4. package/skill/{msdevflow → msd}/README.md +54 -39
  5. package/skill/{msdevflow → msd}/SKILL.md +26 -35
  6. package/skill/{msdevflow → msd}/references/ci-and-review.md +2 -2
  7. package/skill/{msdevflow → msd}/references/command-capabilities.md +1 -1
  8. package/skill/{msdevflow → msd}/references/create-issue.md +1 -1
  9. package/skill/{msdevflow → msd}/references/design-and-development.md +1 -1
  10. package/skill/{msdevflow → msd}/references/openlibing-ci.md +9 -2
  11. package/skill/{msdevflow → msd}/references/pr-and-ci.md +2 -2
  12. package/skill/{msdevflow → msd}/references/recovery.md +1 -1
  13. package/skill/{msdevflow → msd}/references/review-and-merge.md +2 -2
  14. package/skill/{msdevflow → msd}/references/setup-and-issue.md +2 -2
  15. package/skill/{msdevflow → msd}/references/state-and-safety.md +1 -1
  16. package/skill/{msdevflow → msd}/scripts/openlibing_ci.py +62 -21
  17. package/skill/msd-ci/SKILL.md +15 -0
  18. package/skill/msd-code-review/SKILL.md +15 -0
  19. package/skill/msd-create-issue/SKILL.md +15 -0
  20. package/skill/msd-develop/SKILL.md +15 -0
  21. package/skill/msd-discover/SKILL.md +15 -0
  22. package/skill/msd-feedback/SKILL.md +15 -0
  23. package/skill/msd-issue/SKILL.md +15 -0
  24. package/skill/msd-merge/SKILL.md +15 -0
  25. package/skill/msd-openlibing-auth/SKILL.md +15 -0
  26. package/skill/msd-pr/SKILL.md +15 -0
  27. /package/skill/{msdevflow → msd}/references/code-review.md +0 -0
  28. /package/skill/{msdevflow → msd}/scripts/requirements.txt +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msdevflow",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
4
4
  "description": "Install the msdevflow GitCode skill and its runtime dependencies",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "bin/",
11
11
  "lib/",
12
12
  "skill/",
13
- "!skill/msdevflow/scripts/test_openlibing_ci.py",
13
+ "!skill/**/scripts/test_openlibing_ci.py",
14
14
  "!skill/**/__pycache__/**",
15
15
  "!skill/**/*.pyc"
16
16
  ],
@@ -1,22 +1,25 @@
1
- # msdevflow
1
+ # msd
2
2
 
3
- 通用 GitCode Issue 到 PR 合入 workflow skill。它保留一个统一 skill:显式 `action=<name>` 直接执行单项能力;未提供 action 时先通过客户端原生选择菜单路由,再读取仓库或访问 GitCode
3
+ 通用 GitCode Issue 到 PR 合入 workflow Skill 套件。业务规则只存在于核心 `msd`;十个 `msd-<action>` 薄路由 Skill Claude Code、Codex 和 OpenCode 在输入阶段展示并补全可用 action
4
4
 
5
5
  ## 1. Action 模型
6
6
 
7
- action 调用的菜单为:
7
+ 输入 `/msd-`(Claude Code/OpenCode)或 `$msd-`(Codex)即可筛选:
8
8
 
9
9
  ```text
10
- 选择运行方式
11
- ├─ 完整端到端流程
12
- ├─ 作者工作流
13
- │ ├─ Issue 管理 -> discover | create-issue | issue
14
- │ ├─ 开发与交付 -> develop | pr | ci
15
- │ └─ PR 后续 -> feedback | merge
16
- └─ 独立工具 -> openlibing-auth | code-review
10
+ msd-discover
11
+ msd-create-issue
12
+ msd-issue
13
+ msd-develop
14
+ msd-pr
15
+ msd-ci
16
+ msd-openlibing-auth
17
+ msd-feedback
18
+ msd-code-review
19
+ msd-merge
17
20
  ```
18
21
 
19
- Claude Code 使用 `AskUserQuestion`,Codex 使用 `request_user_input`,OpenCode 使用 `question`。每次只显示当前层且不超过 3 项;没有可用选择工具时只输出当前层编号选项并等待回复。菜单选择前不探测仓库、不调用 GitCode、不读写远端,也不加载 action reference。选中叶子后等同于显式 action,且保留原调用正文中的 URL、路径、仓库和模式。
22
+ 每个薄路由只固定唯一 action、保留调用正文,并从相对路径 `../msd/SKILL.md` 加载同一 skills 根目录中的核心,不复制业务规则;核心 references 继续以 `../msd/` 为基准。直接调用核心 `msd` 会运行完整作者 E2E,不会在提交后弹出 Question 菜单;`/msd action=<name>`、`$msd action=<name>` 仍作为兼容调用,但参数本身没有跨客户端枚举下拉。
20
23
 
21
24
  支持:
22
25
 
@@ -42,7 +45,7 @@ Claude Code 使用 `AskUserQuestion`,Codex 使用 `request_user_input`,OpenC
42
45
 
43
46
  ## 2. 完整作者 E2E
44
47
 
45
- action 菜单选中“完整端到端流程”后,从环境、CLI 能力和仓库画像开始运行完整流程:
48
+ 直接调用核心 `msd` 且不提供 `action=` 时,从环境、CLI 能力和仓库画像开始运行完整流程:
46
49
 
47
50
  ```text
48
51
  discover -> issue -> develop -> pr -> ci
@@ -69,12 +72,12 @@ discover -> issue -> develop -> pr -> ci
69
72
 
70
73
  ## 4. 调用示例
71
74
 
72
- 以下示例使用 Claude Code 的 `/msdevflow` 形式。Codex 使用 `$msdevflow` 或从 `/skills` 选择;OpenCode 使用 `/skills` 选择 `msdevflow`,不要用 `@` 查找 Skill。没有 `action=` 时,提交后先出现分级菜单;显式 `action=<name>` 则不显示菜单,直接路由。无论客户端入口如何,正文中的 action 和其余任务参数保持相同。
75
+ 以下示例使用 Claude Code 的 `/msd` 形式。Codex 使用 `$msd`,OpenCode 可从 `/skills` 选择 `msd`;OpenCode `@` 不用于 Skill。单项能力优先使用各客户端输入补全中的 `msd-<action>`,正文中的任务参数保持不变。
73
76
 
74
- ### 通过菜单选择完整 E2E
77
+ ### 运行完整 E2E
75
78
 
76
79
  ```text
77
- /msdevflow
80
+ /msd
78
81
  处理 https://gitcode.com/Ascend/example/issues/123。
79
82
  本地仓库:D:\work\example
80
83
  canonical:Ascend/example
@@ -85,7 +88,7 @@ Fork:myname/example
85
88
  ### 只发现候选 Issue
86
89
 
87
90
  ```text
88
- /msdevflow action=discover
91
+ /msd-discover
89
92
  查询 Ascend/example 中分配给当前账号的开放 Issue,列出仍需处理的候选。
90
93
  ```
91
94
 
@@ -94,7 +97,7 @@ Fork:myname/example
94
97
  ### 只创建 Issue
95
98
 
96
99
  ```text
97
- /msdevflow action=create-issue
100
+ /msd-create-issue
98
101
  在 Ascend/example 提一个 bug:升级后首次启动失败,错误信息和环境如下……
99
102
  使用 guided 模式,先给我完整预览,确认后再创建。
100
103
  ```
@@ -106,7 +109,7 @@ Fork:myname/example
106
109
  ### 只接取和核验 Issue
107
110
 
108
111
  ```text
109
- /msdevflow action=issue
112
+ /msd-issue
110
113
  处理 https://gitcode.com/Ascend/example/issues/123。
111
114
  本地仓库:D:\work\example
112
115
  ```
@@ -116,7 +119,7 @@ Fork:myname/example
116
119
  ### 只开发和本地验证
117
120
 
118
121
  ```text
119
- /msdevflow action=develop
122
+ /msd-develop
120
123
  Issue:https://gitcode.com/Ascend/example/issues/123
121
124
  本地仓库:D:\work\example
122
125
  ```
@@ -126,7 +129,7 @@ Issue:https://gitcode.com/Ascend/example/issues/123
126
129
  ### 只创建 PR
127
130
 
128
131
  ```text
129
- /msdevflow action=pr
132
+ /msd-pr
130
133
  Issue:https://gitcode.com/Ascend/example/issues/123
131
134
  本地仓库:D:\work\example
132
135
  canonical:Ascend/example
@@ -138,28 +141,28 @@ source:myname/example
138
141
  ### 只处理 CI
139
142
 
140
143
  ```text
141
- /msdevflow action=ci
144
+ /msd-ci
142
145
  PR:https://gitcode.com/Ascend/example/pulls/456
143
146
  本地仓库:D:\work\example
144
147
  ```
145
148
 
146
- CI 通过后立即停止,不处理 feedback 或 merge。识别为 openLiBing 且只读 API 返回 401/403 时,可自动安装 Python Playwright 依赖并打开已有的系统 Chrome/Edge 或 Playwright Chromium,认证成功后继续本 action;不会自动下载浏览器,浏览器不可用时输出可复制的 OAuth 链接并停止为 `blocked: browser-required`。可用 `autonomous-ci` 只授权同一 PR 上根因明确的最小 CI 修复循环。
149
+ CI 通过后立即停止,不处理 feedback 或 merge。识别为 openLiBing 且只读 API 返回 401/403 时,使用 setup 创建的受管 Python 运行时和其中的 Playwright 打开已有的系统 Chrome/Edge 或 Playwright Chromium,认证成功后继续本 action;运行时不可用时停止并要求运行 `npx msdevflow@latest setup`,不会在 action 内调用 pip,也不会自动下载浏览器。浏览器不可用时输出可复制的 OAuth 链接并停止为 `blocked: browser-required`。可用 `autonomous-ci` 只授权同一 PR 上根因明确的最小 CI 修复循环。
147
150
 
148
151
  ### 只验证 openLiBing OAuth
149
152
 
150
153
  ```text
151
- /msdevflow action=openlibing-auth
154
+ /msd-openlibing-auth
152
155
  本地仓库:D:\work\example
153
156
  ```
154
157
 
155
- 本 action 从当前工作区唯一识别 canonical,只在该仓库内按“当前分支关联 PR → 最近开放 PR → 最近合入 PR”的顺序选择一个包含 openLiBing run 的验证目标,不要求用户提供 PR。它可自动安装 Python Playwright 依赖,并使用已有的系统 Chrome/Edge 或 Playwright Chromium打开可见窗口;不会自动下载浏览器。用户亲自完成 GitCode 登录和授权。浏览器不可用时输出可复制的 OAuth 链接并停止为 `blocked: browser-required`;人工浏览器登录不等于验证完成。只有 OAuth 后对固定 run 的真实只读请求成功才完成,随后立即停止,不进入 `ci`。
158
+ 本 action 从当前工作区唯一识别 canonical,只在该仓库内按“当前分支关联 PR → 最近开放 PR → 最近合入 PR”的顺序选择一个包含 openLiBing run 的验证目标,不要求用户提供 PR。它使用 setup 创建的受管 Python 运行时和其中的 Playwright,并使用已有的系统 Chrome/Edge 或 Playwright Chromium 打开可见窗口;运行时不可用时要求运行 `npx msdevflow@latest setup`,不会在 action 内调用 pip,也不会自动下载浏览器。用户亲自完成 GitCode 登录和授权。浏览器不可用时输出可复制的 OAuth 链接并停止为 `blocked: browser-required`;人工浏览器登录不等于验证完成。只有 OAuth 后对固定 run 的真实只读请求成功才完成,随后立即停止,不进入 `ci`。
156
159
 
157
160
  它不读取或输出 GitCode Token。openLiBing Token 只驻留当前 Python 进程,固定显示为 `redacted`;持久化的只有受管专用 profile 中的 GitCode 浏览器登录态。
158
161
 
159
162
  ### 只处理自己 PR 的意见
160
163
 
161
164
  ```text
162
- /msdevflow action=feedback
165
+ /msd-feedback
163
166
  PR:https://gitcode.com/Ascend/example/pulls/456
164
167
  本地仓库:D:\work\example
165
168
  ```
@@ -169,7 +172,7 @@ PR:https://gitcode.com/Ascend/example/pulls/456
169
172
  ### 检视他人的 PR
170
173
 
171
174
  ```text
172
- /msdevflow action=code-review
175
+ /msd-code-review
173
176
  PR:https://gitcode.com/Ascend/example/pulls/789
174
177
  ```
175
178
 
@@ -180,7 +183,7 @@ PR:https://gitcode.com/Ascend/example/pulls/789
180
183
  ### 只核验并合入
181
184
 
182
185
  ```text
183
- /msdevflow action=merge
186
+ /msd-merge
184
187
  PR:https://gitcode.com/Ascend/example/pulls/456
185
188
  ```
186
189
 
@@ -224,7 +227,7 @@ GitCode CLI 会打开官方页面让用户创建 Token,再在 CLI 自己的终
224
227
 
225
228
  ## 6. 安装
226
229
 
227
- npm 包同时携带 `msdevflow` skill setup 程序。首次安装:
230
+ npm `msdevflow` 同时携带 `msd` Skill 套件和 setup 程序。首次安装:
228
231
 
229
232
  ```bash
230
233
  npx msdevflow setup
@@ -238,26 +241,38 @@ npx msdevflow@latest setup
238
241
 
239
242
  `setup` 是幂等的:内容一致时保持 `current`,内容不同时原子更新。
240
243
 
241
- `setup` 默认检测 PATH 中的 Claude Code、Codex 和 OpenCode,先展示客户端、物理 skill 目标、安装或更新状态、GitCode CLI 归属、registry、安装模式、Python 依赖命令和认证方式;用户确认后,在同一次运行中:
244
+ `setup` 默认检测 PATH 中的 Claude Code、Codex 和 OpenCode,先展示客户端、物理 skill 目标、安装或更新状态、GitCode CLI 归属、registry、安装模式、受管 Python 运行时、依赖命令和认证方式;用户确认后,在同一次运行中:
242
245
 
243
- 1. npm 包内唯一源码 `skill/msdevflow` 原子安装或更新所需受管副本;
244
- 2. 安装或升级官方 GitCode npm CLI;
245
- 3. 安装内置 `scripts/requirements.txt` 中的 Python Playwright 包;
246
+ 1. 创建、修复或复用 msdevflow 受管 Python venv;
247
+ 2. 用受管 Python 安装内置 `scripts/requirements.txt` 中的 Playwright 包;
248
+ 3. 安装或升级官方 GitCode npm CLI;
246
249
  4. 验收 workflow 所需 schema/API;
247
- 5. CLI 未认证时运行选定命令的 `auth login --web`。
250
+ 5. CLI 未认证时运行选定命令的 `auth login --web`;
251
+ 6. 从 npm 包内的 `skill/msd` 核心和十个 `skill/msd-<action>` 路由器原子安装或更新整套入口。
252
+
253
+ 受管运行时默认位于:
254
+
255
+ ```text
256
+ Windows: %LOCALAPPDATA%\msdevflow\python
257
+ macOS/Linux: ${XDG_DATA_HOME:-$HOME/.local/share}/msdevflow/python
258
+ ```
259
+
260
+ 可用 `MSDEVFLOW_PYTHON_DIR` 覆盖,值必须是绝对路径或以 `~` 开头;setup 与后续 action 必须使用同一配置。`venv` 属于 CPython 标准库,但精简发行版可能缺失;Debian/Ubuntu 通常需要用户自行安装 `python3-venv`。setup 不执行 sudo,不回退到全局 pip,也不使用 `--break-system-packages`。来源不明或 marker 损坏的运行时目录不会被接管。
248
261
 
249
262
  客户端到物理目录的映射:
250
263
 
251
264
  ```text
252
- Claude Code -> ~/.claude/skills/msdevflow
253
- Codex -> ~/.agents/skills/msdevflow
254
- 仅 OpenCode -> ~/.agents/skills/msdevflow
265
+ Claude Code -> ~/.claude/skills/{msd,msd-*}
266
+ Codex -> ~/.agents/skills/{msd,msd-*}
267
+ 仅 OpenCode -> ~/.agents/skills/{msd,msd-*}
255
268
  OpenCode + Claude Code -> 复用 Claude 目标
256
269
  OpenCode + Codex -> 复用 Agent Skills 目标
257
- Claude Code + Codex -> 两个受管副本
258
- 三者同时安装 -> 两个受管副本,并提示 OpenCode 双路径发现
270
+ Claude Code + Codex -> 两套受管副本
271
+ 三者同时安装 -> 两套受管副本,并提示 OpenCode 双路径发现
259
272
  ```
260
273
 
274
+ 每个物理目标都包含一个 `msd` 核心和十个 `msd-<action>` 路由器;业务规则仍只有一份。
275
+
261
276
  显式选择客户端、只读预检或自定义目录:
262
277
 
263
278
  ```bash
@@ -277,7 +292,7 @@ npm 包尚未发布时,可从当前源码/发布目录运行等价入口:
277
292
  node ./msdevflow/bin/msdevflow.js setup
278
293
  ```
279
294
 
280
- `setup` 不下载或启动 Chromium。GitCode Token 只由 GitCode CLI 自己的终端提示接收并验证;setup 父进程不读取、输出或转存 Token,也不要求用户把 Token 传给 Agent。每个已有 skill 目标只有 manifest 明确标识为 `msdevflow` 时才允许更新,来源不明的目录、文件或符号链接会被拒绝覆盖。安装后重启或重新加载对应客户端。
295
+ `setup` 不下载或启动 Chromium。GitCode Token 只由 GitCode CLI 自己的终端提示接收并验证;setup 父进程不读取、输出或转存 Token,也不要求用户把 Token 传给 Agent。每个已有 `msd` `msd-<action>` entry 的 manifest 都必须精确匹配自身名称才允许更新;来源不明的目录、文件或符号链接会阻断整套安装。旧 `msdevflow` 目录只有在 manifest 精确证明由本包管理时,才会在新套件全部切换并验证后安全迁移;失败时回滚整套变更。安装后重启或重新加载对应客户端。
281
296
 
282
297
  ## 7. 仓库画像和 PR 模板
283
298
 
@@ -1,52 +1,43 @@
1
1
  ---
2
- name: msdevflow
2
+ name: msd
3
3
  description: >
4
- 通用 GitCode Issue 到 PR 合入工作流。无 action 调用时通过宿主原生选择菜单路由到完整作者流程或独立能力;也支持通过 action=discover、create-issue、issue、develop、pr、ci、openlibing-auth、feedback、code-review 或 merge 直接执行单个能力。适用于发现、新建或接取 GitCode Issue、开发并创建 PR、处理 CI、验证 openLiBing OAuth、处理自己 PR 的检视意见、检视他人 PR,以及在独立审批和显式确认后合入。仅适用于已授权的 GitCode 仓库。
4
+ 通用 GitCode Issue 到 PR 合入工作流核心。直接调用 msd 时运行完整作者端到端流程;也接受 action=discover、create-issue、issue、develop、pr、ci、openlibing-auth、feedback、code-review 或 merge 兼容参数。推荐通过独立的 msd-<action> Skill 在输入时发现并选择单项能力。适用于发现、新建或接取 GitCode Issue、开发并创建 PR、处理 CI、验证 openLiBing OAuth、处理自己 PR 的检视意见、检视他人 PR,以及在独立审批和显式确认后合入。仅适用于已授权的 GitCode 仓库。
5
5
  compatibility: Requires Git, Node.js >=18, Python >=3.10, GitCode access, and the setup-installed GitCode CLI. Supports Claude Code, Codex, and OpenCode.
6
6
  metadata:
7
- version: 2.1.0
7
+ version: 3.0.0
8
8
  source: msdevflow
9
+ role: core
9
10
  ---
10
11
 
11
- # msdevflow:GitCode Issue 到 PR 合入
12
+ # msd:GitCode Issue 到 PR 合入
12
13
 
13
- 把 GitCode 工作拆成可显式调用的 action;无 action 调用先通过选择菜单路由到完整作者 E2E 或独立能力。选定流程后再探测仓库规范和 CLI 能力,不硬编码组织、默认分支、CI、分支命名、合并方式或机器人协议。
14
+ 把 GitCode 工作拆成可独立发现和调用的 action,不硬编码组织、默认分支、CI、分支命名、合并方式或机器人协议。安装套件同时提供核心 `msd` 和十个薄路由 Skill:Claude Code OpenCode 输入 `/msd-`、Codex 输入 `$msd-`,即可在客户端的 Skill 补全中查看 action。
14
15
 
15
16
  ## Action 路由
16
17
 
17
- 识别显式参数 `action=<name>`;显式 action 直接路由,不显示菜单。未知 action 停止并列出有效值,不做远端写入。仅当用户调用本 skill 且没有提供 `action=` 时,必须先执行下述菜单路由,不得在选择完成前探测仓库、调用 GitCode、读写远端或加载 action reference。
18
+ 推荐入口为 `msd-<action>`。薄路由 Skill 只固定一个 action、保留调用正文并加载本核心,不复制业务规则。核心也兼容字面量参数 `action=<name>`;显式 action 直接路由,未知 action 停止并列出有效值,不做远端写入。
18
19
 
19
- ### action 菜单路由
20
+ 直接调用核心 `msd` 且没有 `action=` 时,运行完整作者 E2E,不显示运行时 Question 菜单,也不根据自然语言改成某个独立 action。完整 E2E 内部按权威证据定位当前阶段并渐进加载规则。
20
21
 
21
- 优先使用当前宿主提供的原生单选工具:Claude Code 使用 `AskUserQuestion`,Codex 使用 `request_user_input`,OpenCode 使用 `question`;不要仅因工具名称不同而跳过菜单。每次只问一个问题,选项不超过 3 个,等待用户选择后再进入下一层:
22
+ 若薄路由 Skill 固定的 action 与调用正文中的 `action=` 冲突,停止为 `blocked: action-conflict`,不得猜测或执行远端写入。
23
+
24
+ 可发现入口:
22
25
 
23
26
  ```text
24
- 选择运行方式
25
- ├─ 完整端到端流程 -> full-e2e
26
- ├─ 作者工作流
27
- │ ├─ Issue 管理
28
- │ │ ├─ discover
29
- │ │ ├─ create-issue
30
- │ │ └─ issue
31
- │ ├─ 开发与交付
32
- │ │ ├─ develop
33
- │ │ ├─ pr
34
- │ │ └─ ci
35
- │ └─ PR 后续
36
- │ ├─ feedback
37
- │ └─ merge
38
- └─ 独立工具
39
- ├─ openlibing-auth
40
- └─ code-review
27
+ msd
28
+ msd-discover
29
+ msd-create-issue
30
+ msd-issue
31
+ msd-develop
32
+ msd-pr
33
+ msd-ci
34
+ msd-openlibing-auth
35
+ msd-feedback
36
+ msd-code-review
37
+ msd-merge
41
38
  ```
42
39
 
43
- 菜单规则:
44
-
45
- 1. 顶层“完整端到端流程”不是 action;选中后按“完整作者 E2E”执行,并在内部将本次路由记为 `full-e2e`。
46
- 2. 每级原生菜单都把推荐项放在首位,并按宿主约定标明推荐:顶层推荐“完整端到端流程”;作者工作流分类推荐“Issue 管理”;各叶子层分别推荐 `discover`、`develop`、`feedback` 和 `openlibing-auth`。不得为了改变推荐项而提前探测仓库或远端。
47
- 3. 选中叶子 action 后,等同于用户显式提供了对应 `action=<name>`,严格遵守显式 action 契约;保留调用正文中已经给出的 URL、路径、仓库和模式等参数。
48
- 4. 宿主没有可用选择工具、选择工具在当前模式被禁用,或处于非交互运行时,只输出当前层的编号文本选项并停止等待用户回复;不得自行选择默认项,也不得把多层菜单一次性展开。
49
- 5. 只要调用中没有字面量 `action=<name>`,即使自然语言看似指向某个流程,也必须从顶层菜单开始;不得根据意图猜测跳过菜单。菜单选择属于当前 skill 调用,选定后立即继续,不要求用户重新输入 `/msdevflow`。
40
+ 薄路由选定后,等同于向本核心显式提供对应 `action=<name>`,并严格遵守显式 action 契约。
50
41
 
51
42
  | action | 目标 | 主要输入 | 完成后立即停止于 | 必读文件 |
52
43
  |---|---|---|---|---|
@@ -61,7 +52,7 @@ metadata:
61
52
  | `code-review` | 检视他人的 PR,发布 finding 或 `/lgtm` | 他人 PR | `review-findings`、`review-passed`、`review-incomplete`、`waiting` 或 `blocked` | [references/code-review.md](references/code-review.md) |
62
53
  | `merge` | 核验门禁并在最终确认后合入 | canonical PR | `merged`、`waiting` 或 `blocked` | [references/review-and-merge.md](references/review-and-merge.md) |
63
54
 
64
- `code-review` 只能通过显式 `action=code-review` 或“独立工具”菜单叶子独立调用,永不进入作者 E2E,不得检视或批准当前账号自己的 PR。它固定当前 head,完整检视权威 diff、必要上下文和既有 discussions:有高置信度问题时发布经确认且逐字回读的 finding,不发送 `/lgtm`;没有问题、没有有效未解决意见且覆盖完整时,在针对当前 PR/head 明确确认后先发布带尾签摘要,再原样发送 `/lgtm`。它不发送 `/approve`、`/merge`,不修改作者代码。
55
+ `code-review` 只能通过 `msd-code-review` 或兼容参数 `action=code-review` 独立调用,永不进入作者 E2E,不得检视或批准当前账号自己的 PR。它固定当前 head,完整检视权威 diff、必要上下文和既有 discussions:有高置信度问题时发布经确认且逐字回读的 finding,不发送 `/lgtm`;没有问题、没有有效未解决意见且覆盖完整时,在针对当前 PR/head 明确确认后先发布带尾签摘要,再原样发送 `/lgtm`。它不发送 `/approve`、`/merge`,不修改作者代码。
65
56
 
66
57
  ## 显式 action 契约
67
58
 
@@ -75,7 +66,7 @@ metadata:
75
66
 
76
67
  ## 完整作者 E2E
77
68
 
78
- 只有无 action 菜单选中 `full-e2e` 后,才从 Phase 0 环境、CLI 能力和仓库画像开始运行完整流程;调用正文可以提供完整 E2E 所需的 URL、路径、仓库和模式,但不能替代菜单选择。“从头开始”只表示重新读取事实,不表示重复创建资源或重复评论。按证据幂等跳过已完成步骤:
69
+ 直接调用核心 `msd` 且未提供 `action=` 时,从 Phase 0 环境、CLI 能力和仓库画像开始运行完整流程;调用正文可以提供完整 E2E 所需的 URL、路径、仓库和模式。“从头开始”只表示重新读取事实,不表示重复创建资源或重复评论。按证据幂等跳过已完成步骤:
79
70
 
80
71
  ```text
81
72
  discover -> issue -> develop -> pr -> ci
@@ -97,7 +88,7 @@ discover -> issue -> develop -> pr -> ci
97
88
  1. 固定 `gitcode_command`:无 Python 同名工具时为 `gitcode`;有 Python `gitcode` 时保留它并使用 `gitcode-npm`。来源不明时停止,不覆盖。
98
89
  2. 检查当前目录;若不是 Git 仓库,自动探测直接子目录中的 Git 仓库及 remotes。
99
90
  3. 确认 canonical repository、source repository、operation target、当前账号和授权范围。
100
- 4. 除纯只读 `discover` 外,显式 action 先从 [references/setup-and-issue.md](references/setup-and-issue.md) 只读取公共环境、仓库上下文和最小充分仓库画像,不执行其中的 Issue 接取业务;随后只加载该 action 对应 reference 及实际触发的深层规则。`create-issue` 的模板选择和写入只按 [references/create-issue.md](references/create-issue.md),不执行候选发现或既有 Issue 核验。`code-review` 只使用公共只读画像和 [references/code-review.md](references/code-review.md),不加载作者 feedback/merge 规则。`full-e2e` 也只按当前位置渐进加载。
91
+ 4. 除纯只读 `discover` 外,显式 action 先从 [references/setup-and-issue.md](references/setup-and-issue.md) 只读取公共环境、仓库上下文和最小充分仓库画像,不执行其中的 Issue 接取业务;随后只加载该 action 对应 reference 及实际触发的深层规则。`create-issue` 的模板选择和写入只按 [references/create-issue.md](references/create-issue.md),不执行候选发现或既有 Issue 核验。`code-review` 只使用公共只读画像和 [references/code-review.md](references/code-review.md),不加载作者 feedback/merge 规则。完整作者 E2E 也只按当前位置渐进加载。
101
92
  5. CLI 缺失、命令不兼容或版本不足时读 [references/command-capabilities.md](references/command-capabilities.md),停止当前 action 并提示用户运行独立 setup;setup 成功后恢复原 action。
102
93
  6. 进入 autonomous、远端写、危险动作或恢复时读 [references/state-and-safety.md](references/state-and-safety.md)。
103
94
  7. 中断恢复、写结果不确定或最终报告时读 [references/recovery.md](references/recovery.md)。
@@ -11,7 +11,7 @@
11
11
 
12
12
  不要把一个仓库的 `compile`、label 或机器人协议迁移到另一个仓库。
13
13
 
14
- 若评论/文档明确为 openLiBing,按需加载 [openlibing-ci.md](openlibing-ci.md);先用 GitCode 评论定位 run/job,再尝试只读 detail/log API。401/403 时,显式 `action=ci` 和完整 E2E 都直接进入安全 OAuth 子流程,允许自动安装 Python Playwright 依赖并打开已有的系统 Chrome/Edge 或 Playwright Chromium,但不自动下载浏览器;成功后恢复原 CI 诊断,浏览器不可用时输出 OAuth 链接并停止为 `blocked: browser-required`。不得把 GitCode Actions 当作 openLiBing。
14
+ 若评论/文档明确为 openLiBing,按需加载 [openlibing-ci.md](openlibing-ci.md);先用 GitCode 评论定位 run/job,再尝试只读 detail/log API。401/403 时,显式 `action=ci` 和完整 E2E 都直接进入安全 OAuth 子流程,使用 setup 创建的受管 Python 运行时和其中的 Playwright,并打开已有的系统 Chrome/Edge 或 Playwright Chromium;action 内不调用 pip,也不自动下载浏览器。受管运行时不可用时要求运行 `npx msdevflow@latest setup`;成功后恢复原 CI 诊断,浏览器不可用时输出 OAuth 链接并停止为 `blocked: browser-required`。不得把 GitCode Actions 当作 openLiBing。
15
15
 
16
16
  记录适配器:
17
17
 
@@ -107,7 +107,7 @@ commit/head SHA
107
107
  - 不采纳:spec/代码/测试依据;
108
108
  6. 统一换行为 `\n` 后逐字比较完整远端回复与预期正文,并验证末尾恰好存在一次尾签;发现 `?`、乱码、截断、尾签缺失、重复或其他不一致时先原地修复,未修复前不得 resolve;
109
109
  7. 正文回读通过后,仓库规则允许且权限具备时 resolve,再回读确认 `resolved=true`;
110
- 8. 产生新 head 时,显式 `action=feedback` 停止并建议 `action=ci`;菜单选中的完整作者 E2E(`full-e2e`) 由编排器随后路由到 `ci`。
110
+ 8. 产生新 head 时,显式 `action=feedback` 停止并建议 `action=ci`;核心 `msd` 启动的完整作者 E2E 由编排器随后路由到 `ci`。
111
111
 
112
112
  不要只发一条总体总结替代逐条回复。汇总应在逐条闭环后发布。
113
113
 
@@ -42,7 +42,7 @@ CLI 缺失、版本低于推荐下限或所需 schema 不存在时,当前 acti
42
42
  npx msdevflow setup
43
43
  ```
44
44
 
45
- 独立 setup 会在同一次确认后安装或更新 npm 包内置的 `msdevflow` skill,安装或升级官方 npm 包 `@gitcode-cli/cli@latest`,保留已有 Python `gitcode`,默认安装内置 `scripts/requirements.txt` 中的 Playwright,并验收 workflow 所需 schema/API。它不下载 Playwright Chromium。若 CLI 未认证,setup 会启动 `<gitcode-command> auth login --web` 的官方浏览器流程;凭证由 GitCode CLI 自己接收和保存,setup 不读取、打印或转存 Token,也不要求用户把 Token 传给 Agent。workflow 不在 action 内隐式安装依赖。setup 成功后重新探测环境并恢复原 action;不要 fallback 到可能安装旧实现的 PyPI `gitcode-cli`。
45
+ 独立 setup 会在同一次确认后安装或更新 npm 包内置的 `msd` Skill 套件,安装或升级官方 npm 包 `@gitcode-cli/cli@latest`,保留已有 Python `gitcode`,创建或修复 msdevflow 受管 Python venv,在其中安装 `scripts/requirements.txt` Playwright,并验收 workflow 所需 schema/API。它不修改系统 Python、不使用 `--break-system-packages`、不回退到全局 pip,也不下载 Playwright Chromium。若 Python 发行版缺少 `venv`,setup 明确阻断;Debian/Ubuntu 通常需要用户自行安装 `python3-venv`。若 CLI 未认证,setup 会启动 `<gitcode-command> auth login --web` 的官方浏览器流程;凭证由 GitCode CLI 自己接收和保存,setup 不读取、打印或转存 Token,也不要求用户把 Token 传给 Agent。workflow 不在 action 内隐式安装依赖。setup 成功后重新探测环境并恢复原 action;不要 fallback 到可能安装旧实现的 PyPI `gitcode-cli`。
46
46
 
47
47
  ## 能力降级顺序
48
48
 
@@ -1,6 +1,6 @@
1
1
  # Action `create-issue`
2
2
 
3
- 显式 `action=create-issue` 用于在一个已授权的 canonical repository 中创建一个 GitCode Issue。它是独立 action:创建并回读确认后立即停止,不执行 `discover`、`issue`、接取、查重、代码核验、开发或 PR 流程,也不进入菜单选中的完整作者 E2E(`full-e2e`)。
3
+ 显式 `action=create-issue` 用于在一个已授权的 canonical repository 中创建一个 GitCode Issue。它是独立 action:创建并回读确认后立即停止,不执行 `discover`、`issue`、接取、查重、代码核验、开发或 PR 流程,也不进入核心 `msd` 启动的完整作者 E2E
4
4
 
5
5
  ## 输入和边界
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Action `develop`:分析、设计与开发
2
2
 
3
- 只在显式 `action=develop`,或菜单选中的完整作者 E2E(`full-e2e`) 已达到 `verified` 时读取。显式 action 启动时先只读确认 Issue 已核验且没有重复实现;前置不足时返回 `blocked` 并建议 `action=issue`,不得自动接取或补跑核验。
3
+ 只在显式 `action=develop`,或核心 `msd` 启动的完整作者 E2E 已达到 `verified` 时读取。显式 action 启动时先只读确认 Issue 已核验且没有重复实现;前置不足时返回 `blocked` 并建议 `action=issue`,不得自动接取或补跑核验。
4
4
 
5
5
  ## Phase 3:需求分析与设计决策树
6
6
 
@@ -20,7 +20,14 @@
20
20
 
21
21
  ### 执行和完成证据
22
22
 
23
- setup 默认安装 `scripts/requirements.txt` 中的 Python Playwright 依赖;脚本在依赖缺失时也可从同一清单受控恢复,但不下载 Playwright Chromium。脚本优先使用系统 Chrome/Edge,其次使用当前 Playwright 环境中已经存在的 Chromium。然后运行:
23
+ setup 在所有平台创建或修复一个 msdevflow 受管 Python venv,并在其中安装 `scripts/requirements.txt` Playwright;脚本进入 OAuth 流程时自动重启到该受管解释器。受管运行时或 Playwright 不可用时要求运行 `npx msdevflow@latest setup`,脚本自身不调用 pip,不修改系统 Python,也不下载 Playwright Chromium。脚本优先使用系统 Chrome/Edge,其次使用受管 Playwright 环境中已经存在的 Chromium。然后运行:
24
+
25
+ ```text
26
+ Windows: %LOCALAPPDATA%\msdevflow\python
27
+ macOS/Linux: ${XDG_DATA_HOME:-$HOME/.local/share}/msdevflow/python
28
+ ```
29
+
30
+ 可用 `MSDEVFLOW_PYTHON_DIR` 覆盖,值必须是绝对路径或以 `~` 开头;setup 与运行 action 时必须保持一致。
24
31
 
25
32
  ```bash
26
33
  python <skill-dir>/scripts/openlibing_ci.py login-check \
@@ -85,7 +92,7 @@ python <skill-dir>/scripts/openlibing_ci.py diagnose \
85
92
  python <skill-dir>/scripts/openlibing_ci.py diagnose ... --oauth
86
93
  ```
87
94
 
88
- `--oauth` 需要 Python Playwright,以及已有的系统 Chrome/Edge 或当前 Playwright 环境中已经存在的 Chromium。脚本可从 `scripts/requirements.txt` 自动安装受审依赖,但不会执行 `playwright install chromium`;浏览器可用时打开本机可见窗口让用户自己完成 GitCode 登录/授权,浏览器不可用时只输出可复制的 OAuth 链接并停止为 `blocked: browser-required`。捕获的 openLiBing token 只驻留当前 Python 进程内,不打印、不落盘。
95
+ `--oauth` 需要 setup 已准备好的受管 Python 运行时及其中的 Playwright,以及已有的系统 Chrome/Edge 或受管 Playwright 环境中已经存在的 Chromium。脚本不调用 pip,也不会执行 `playwright install chromium`;受管运行时或依赖缺失时停止并要求运行 `npx msdevflow@latest setup`。浏览器可用时打开本机可见窗口让用户自己完成 GitCode 登录/授权,浏览器不可用时只输出可复制的 OAuth 链接并停止为 `blocked: browser-required`。捕获的 openLiBing token 只驻留当前 Python 进程内,不打印、不落盘。
89
96
 
90
97
  默认使用专用持久浏览器 profile:
91
98
 
@@ -1,6 +1,6 @@
1
1
  # Action `pr` 与 `ci`
2
2
 
3
- 只在显式 `action=pr`、`action=ci`,或菜单选中的完整作者 E2E(`full-e2e`) 路由到对应位置时读取。任何远端写前同时读取 [state-and-safety.md](state-and-safety.md)。CI 触发或失败时再读取 [ci-and-review.md](ci-and-review.md),不要提前加载。
3
+ 只在显式 `action=pr`、`action=ci`,或核心 `msd` 启动的完整作者 E2E 路由到对应位置时读取。任何远端写前同时读取 [state-and-safety.md](state-and-safety.md)。CI 触发或失败时再读取 [ci-and-review.md](ci-and-review.md),不要提前加载。
4
4
 
5
5
  ## Action `pr`:Commit、Push、普通 PR
6
6
 
@@ -90,7 +90,7 @@ Suggested next action: ci
90
90
  4. 分类当前改动、连带失败、canonical 基线、基础设施、权限或证据不足;
91
91
  5. guided 模式确认修复;`autonomous-ci` 只在已授权最小范围内自动修改、验证、新 commit、push 和重触发;
92
92
  6. 新 push 后获取新 head,旧 head 结果失效;
93
- 7. openLiBing detail/log 返回 401/403 时,按 [openlibing-ci.md](openlibing-ci.md) 自动安装缺失的 Python Playwright 依赖,并使用已有的系统 Chrome/Edge 或 Playwright Chromium 打开可见浏览器;不自动下载浏览器。用户完成 GitCode 登录/授权且固定 run 验证成功后恢复当前 CI 诊断;浏览器不可用时输出 OAuth 链接并停止为 `blocked: browser-required`;
93
+ 7. openLiBing detail/log 返回 401/403 时,按 [openlibing-ci.md](openlibing-ci.md) 使用 setup 创建的受管 Python 运行时和其中的 Playwright,并用已有的系统 Chrome/Edge 或 Playwright Chromium 打开可见浏览器;action 内不调用 pip,也不自动下载浏览器。受管运行时不可用时要求运行 `npx msdevflow@latest setup`;用户完成 GitCode 登录/授权且固定 run 验证成功后恢复当前 CI 诊断,浏览器不可用时输出 OAuth 链接并停止为 `blocked: browser-required`;
94
94
  8. 循环至当前 head 全绿或形成证据充分的 blocker。
95
95
 
96
96
  重试不能代替根因分析。CI 修复不得扩大 Issue 范围;发现产品实现缺失或方案错误时返回 `blocked` 并建议 `action=develop`,不得把 CI action 变成补开发流程。
@@ -14,7 +14,7 @@
14
14
  4. 根据 action 完成条件识别最后一个有证据的状态;不能只信先前摘要或旧 run 产物。
15
15
  5. 验证本地分支与远端 source head 是否一致。
16
16
  6. 显式 action 只判断自身前置和完成条件:前置缺失时 `blocked`,不得运行上游;已完成时报告证据并立即停止。
17
- 7. 菜单选中的完整作者 E2E(`full-e2e`) 从 Phase 0 重建仓库画像,再路由到下一未完成作者 action;不重复已完成的写操作。
17
+ 7. 核心 `msd` 启动的完整作者 E2E 从 Phase 0 重建仓库画像,再路由到下一未完成作者 action;不重复已完成的写操作。
18
18
  8. `create-issue` 不执行产品查重;仅在本次创建结果不确定时按目标仓库、当前账号、启动时间、标题和完整正文做有界恢复,无法唯一证明时停止。`openlibing-auth` 的历史 profile 或旧认证摘要不证明当前授权有效;必须用本次固定 run 做真实只读请求。
19
19
  9. `code-review` 不参与作者 E2E,也不从作者 feedback 状态恢复。恢复时重新确认 reviewer、author、PR 状态和当前 head;旧 `review_head_sha` 的分析、摘要、finding 或 `/lgtm` 不证明当前 head 已检视。
20
20
  10. 当前 head 等于旧 `review_head_sha` 时,回读结构化 comments/discussions,并按 reviewer、head、path/position 和完整正文恢复已发布 finding;检视通过必须同时回读到绑定当前 head 的带尾签摘要和同一 reviewer 的精确 `/lgtm`。
@@ -1,6 +1,6 @@
1
1
  # Action `feedback` 与 `merge`
2
2
 
3
- 只在显式 `action=feedback`、`action=merge`,或菜单选中的完整作者 E2E(`full-e2e`) 路由到对应位置时读取。开始时同时读取 [state-and-safety.md](state-and-safety.md);处理结构化 discussion 或 CI 时按需读取 [ci-and-review.md](ci-and-review.md)。本文件不定义 `action=code-review`。
3
+ 只在显式 `action=feedback`、`action=merge`,或核心 `msd` 启动的完整作者 E2E 路由到对应位置时读取。开始时同时读取 [state-and-safety.md](state-and-safety.md);处理结构化 discussion 或 CI 时按需读取 [ci-and-review.md](ci-and-review.md)。本文件不定义 `action=code-review`。
4
4
 
5
5
  ## Action `feedback`:处理自己 PR 的检视意见
6
6
 
@@ -31,7 +31,7 @@ PR 不存在时返回 `blocked` 并建议 `action=pr`;source branch 不可写
31
31
  6. 创建新 commit 并 push,不 amend、不 force push;
32
32
  7. 每条 discussion 在线程内回复:已修复引用 commit;部分采纳、不采纳或延期说明依据;每条回复按 [state-and-safety.md](state-and-safety.md) 使用 UTF-8 安全通道并附加唯一尾签,逐字回读完整正文;
33
33
  8. 只有回复正文和尾签回读一致后,仓库规则和权限允许时才 resolve;乱码、`?`、截断或不一致时先原地修复;
34
- 9. 产生新 head 时记录旧 CI 结果已失效,停止后建议 `action=ci`;不得在显式 `feedback` 内触发或监控远端 CI。菜单选中的完整作者 E2E(`full-e2e`) 由编排器随后路由到 `ci`。
34
+ 9. 产生新 head 时记录旧 CI 结果已失效,停止后建议 `action=ci`;不得在显式 `feedback` 内触发或监控远端 CI。核心 `msd` 启动的完整作者 E2E 由编排器随后路由到 `ci`。
35
35
 
36
36
  不要以一条总体评论替代逐条回复。处理完成后不请求重新检视、不触发检视机器人;外部 reviewer 是否重新检视由仓库和人员流程决定。
37
37
 
@@ -1,6 +1,6 @@
1
1
  # Action `discover` 与 `issue`
2
2
 
3
- 在显式 `action=discover`、`action=issue`,或菜单选中的完整作者 E2E(`full-e2e`) 启动时读取;显式 `action=create-issue` 只读取本文件的公共环境、仓库上下文和最小充分画像,模板和写入业务转到 [create-issue.md](create-issue.md),不得执行本文件的发现、接取或重复实现核验。`discover` 全程只读并在候选报告后立即停止;`issue` 只接取和核验指定 Issue,达到 `verified` 后立即停止,不进入开发。
3
+ 在显式 `action=discover`、`action=issue`,或核心 `msd` 启动的完整作者 E2E 启动时读取;显式 `action=create-issue` 只读取本文件的公共环境、仓库上下文和最小充分画像,模板和写入业务转到 [create-issue.md](create-issue.md),不得执行本文件的发现、接取或重复实现核验。`discover` 全程只读并在候选报告后立即停止;`issue` 只接取和核验指定 Issue,达到 `verified` 后立即停止,不进入开发。
4
4
 
5
5
  显式 `action=issue` 必须提供 Issue URL 或编号。缺少目标时只做确认输入缺失所需的只读核验,随后返回 `blocked` 并建议先运行 `action=discover`;不得在本 action 内查询候选、让用户选择 Issue 或执行 `discover` 的业务。
6
6
 
@@ -98,7 +98,7 @@ pr_number(如已有)
98
98
 
99
99
  ## Phase 1:发现与接取
100
100
 
101
- 显式 `action=issue` 直接读取输入中的 Issue URL/编号;缺少目标时按本文件入口规则返回 `blocked`。显式 `action=discover` 或菜单选中的完整作者 E2E(`full-e2e`) 尚无目标时,才对“无目标发现”确认出的每个 canonical 查询当前用户负责的开放 Issue:
101
+ 显式 `action=issue` 直接读取输入中的 Issue URL/编号;缺少目标时按本文件入口规则返回 `blocked`。显式 `action=discover` 或核心 `msd` 启动的完整作者 E2E 尚无目标时,才对“无目标发现”确认出的每个 canonical 查询当前用户负责的开放 Issue:
102
102
 
103
103
  ```bash
104
104
  <gitcode-command> issue list -R <canonical> --state open --assignee <username> --json
@@ -100,7 +100,7 @@ Agent 创建或修改的 Issue/PR 正文,以及发布的 Issue 评论、PR 普
100
100
 
101
101
  `autonomous-ci` 只授权在同一 PR 的 CI 修复循环中:修改与根因直接相关的代码、运行门禁、创建新 commit、push 同一 source branch、重触发 CI。它不授权扩大需求、force push、降低测试、处理 feedback、review、approve 或 merge。
102
102
 
103
- 显式 `openlibing-auth`、显式 `ci` 和完整 E2E 均授权在需要 openLiBing OAuth 时自动安装配套 Python Playwright 依赖并打开可见浏览器,但不授权下载 Playwright Chromium。优先使用系统 Chrome/Edge,其次使用已存在的 Playwright Chromium;均不可用或 Playwright 无法启动时输出可复制的 OAuth 链接并停止为 `blocked: browser-required`。该链接只供人工浏览器访问,不能证明当前进程已认证或固定 run 已验证。该授权不允许 Agent 代替用户填写 GitCode 凭证、点击授权同意、读取浏览器 Cookie 值或导出任何 Token。
103
+ 显式 `openlibing-auth`、显式 `ci` 和完整 E2E 均授权在需要 openLiBing OAuth 时使用 setup 创建的受管 Python 运行时和其中的 Playwright 打开可见浏览器,但不授权在 action 内调用 pip,也不授权下载 Playwright Chromium。受管运行时不可用时要求用户运行 `npx msdevflow@latest setup`。优先使用系统 Chrome/Edge,其次使用已存在的 Playwright Chromium;均不可用或 Playwright 无法启动时输出可复制的 OAuth 链接并停止为 `blocked: browser-required`。该链接只供人工浏览器访问,不能证明当前进程已认证或固定 run 已验证。该授权不允许 Agent 代替用户填写 GitCode 凭证、点击授权同意、读取浏览器 Cookie 值或导出任何 Token。
104
104
 
105
105
  ## Guided 检查点
106
106
 
@@ -8,7 +8,6 @@ import os
8
8
  import re
9
9
  import shutil
10
10
  import stat
11
- import subprocess
12
11
  import sys
13
12
  import time
14
13
  import urllib.error
@@ -79,6 +78,59 @@ LEGACY_PROFILE_MARKERS = {
79
78
  },
80
79
  }
81
80
  PROFILE_METADATA = "session.json"
81
+ PYTHON_RUNTIME_MARKER = ".msdevflow-python-runtime"
82
+ PYTHON_RUNTIME_MARKER_CONTENT = "managed-by=msdevflow\n"
83
+
84
+
85
+ def configured_directory(value: str, variable: str) -> Path:
86
+ selected = Path(value).expanduser()
87
+ if not selected.is_absolute():
88
+ raise OpenLibingAuthRequired(
89
+ f"{variable} 必须是绝对路径或以 ~ 开头。请运行 npx msdevflow@latest setup。"
90
+ )
91
+ return selected
92
+
93
+
94
+ def default_python_runtime_dir() -> Path:
95
+ configured = os.getenv("MSDEVFLOW_PYTHON_DIR")
96
+ if configured:
97
+ return configured_directory(configured, "MSDEVFLOW_PYTHON_DIR")
98
+ if os.name == "nt":
99
+ local_app_data = os.getenv("LOCALAPPDATA")
100
+ root = (
101
+ configured_directory(local_app_data, "LOCALAPPDATA")
102
+ if local_app_data
103
+ else Path.home() / "AppData" / "Local"
104
+ )
105
+ return root / "msdevflow" / "python"
106
+ data_home = os.getenv("XDG_DATA_HOME")
107
+ root = (
108
+ configured_directory(data_home, "XDG_DATA_HOME")
109
+ if data_home
110
+ else Path.home() / ".local" / "share"
111
+ )
112
+ return root / "msdevflow" / "python"
113
+
114
+
115
+ def managed_python_executable(runtime_dir: Path | None = None) -> Path:
116
+ selected = (runtime_dir or default_python_runtime_dir()).expanduser()
117
+ executable = selected / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
118
+ marker = selected / PYTHON_RUNTIME_MARKER
119
+ try:
120
+ owned = (
121
+ selected.is_dir()
122
+ and not selected.is_symlink()
123
+ and marker.is_file()
124
+ and not marker.is_symlink()
125
+ and marker.read_text(encoding="utf-8") == PYTHON_RUNTIME_MARKER_CONTENT
126
+ )
127
+ except OSError:
128
+ owned = False
129
+ if not owned or not executable.is_file():
130
+ raise OpenLibingAuthRequired(
131
+ f"msdevflow 受管 Python 运行时不可用:{selected}。请运行 npx msdevflow@latest setup。"
132
+ )
133
+ return executable
82
134
 
83
135
 
84
136
  def profile_marker(profile_dir: Path) -> Path:
@@ -181,29 +233,18 @@ def clear_openlibing_storage(context: Any, page: Any, base_url: str) -> None:
181
233
  raise OpenLibingAuthRequired("无法从持久 profile 清除 openLiBing Cookie,已拒绝保存会话。")
182
234
 
183
235
 
184
- def oauth_requirements_file() -> Path:
185
- return Path(__file__).with_name("requirements.txt")
186
-
187
-
188
236
  def ensure_playwright() -> None:
237
+ executable = managed_python_executable()
238
+ active_prefix = os.path.normcase(str(Path(sys.prefix).resolve()))
239
+ managed_prefix = os.path.normcase(str(default_python_runtime_dir().resolve()))
240
+ if active_prefix != managed_prefix:
241
+ os.execv(str(executable), [str(executable), str(Path(__file__).resolve()), *sys.argv[1:]])
189
242
  try:
190
243
  importlib.import_module("playwright.sync_api")
191
- return
192
- except ImportError:
193
- requirements = oauth_requirements_file()
194
- if not requirements.is_file():
195
- raise OpenLibingAuthRequired("缺少 openLiBing OAuth 依赖清单,无法自动安装 Playwright。")
196
- result = subprocess.run(
197
- [sys.executable, "-m", "pip", "install", "-r", str(requirements)],
198
- check=False,
199
- )
200
- if result.returncode != 0:
201
- raise OpenLibingAuthRequired("自动安装 Playwright 失败。")
202
- importlib.invalidate_caches()
203
- try:
204
- importlib.import_module("playwright.sync_api")
205
- except ImportError as error:
206
- raise OpenLibingAuthRequired("Playwright 安装完成但当前 Python 无法导入。") from error
244
+ except ImportError as error:
245
+ raise OpenLibingAuthRequired(
246
+ "受管 Python 运行时缺少 Playwright。请运行 npx msdevflow@latest setup。"
247
+ ) from error
207
248
 
208
249
 
209
250
  def playwright_chromium_path() -> Path: