msdevflow 0.6.1 → 0.7.1

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.
package/README.md CHANGED
@@ -1,58 +1,81 @@
1
1
  # msdevflow
2
2
 
3
- `msdevflow` 是一个用于 GitCode 的 Claude Code Agent Skill,可以把已授权的 Issue 安全推进到代码实现、普通 PR、CI、检视意见闭环和最终合入,也可以通过显式 `action` 只执行其中一个能力。
3
+ `msdevflow` 是一个用于 GitCode 的 Agent Skill,可以把已授权的 Issue 安全推进到代码实现、普通 PR、CI、检视意见闭环和最终合入,也可以通过显式 `action` 只执行其中一个能力。正式支持 Claude Code、Codex 和 OpenCode;Trae 的 Skills 安装与兼容机制尚未完成可靠验证,因此当前不宣称支持。
4
4
 
5
5
  ## 安装
6
6
 
7
7
  环境要求:
8
8
 
9
- - Claude Code,且支持 Agent Skills;
9
+ - Claude Code、Codex OpenCode 中至少一个;
10
10
  - Node.js `>=18` 和 npm;
11
11
  - Git;
12
12
  - Python `>=3.10` 和 pip;
13
13
  - GitCode 账号;
14
14
  - Chrome 或 Edge(推荐,用于 openLiBing OAuth)。
15
15
 
16
- 运行一步安装:
16
+ 首次安装:
17
17
 
18
18
  ```bash
19
19
  npx msdevflow setup
20
20
  ```
21
21
 
22
- `setup` 会先展示完整计划,确认后在同一次运行中:
22
+ 后续升级无需卸载旧版本,显式使用 `@latest` 获取 npm 上的最新版:
23
23
 
24
- 1. 安装或更新 `msdevflow` skill;
24
+ ```bash
25
+ npx msdevflow@latest setup
26
+ ```
27
+
28
+ `setup` 是幂等的:内容一致时保持 `current`,内容不同时原子更新。
29
+
30
+ `setup` 默认检测 PATH 中的 Claude Code、Codex 和 OpenCode,展示完整计划并在确认后:
31
+
32
+ 1. 把同一份 `msdevflow` skill 原子安装或更新到所需目标;
25
33
  2. 安装或升级官方 `@gitcode-cli/cli@latest`;
26
34
  3. 安装受审的 Python Playwright 依赖;
27
35
  4. 验收 Issue、PR、结构化评论、行内评论、JSON、显式仓库和正文文件等 CLI 能力;
28
36
  5. GitCode CLI 未认证时打开官方浏览器登录。
29
37
 
30
- 可选的只读预检:
38
+ 显式选择客户端或只读预检:
31
39
 
32
40
  ```bash
33
- npx msdevflow setup --dry-run
41
+ npx msdevflow setup --targets claude
42
+ npx msdevflow setup --targets codex,opencode
43
+ npx msdevflow setup --targets all
44
+ npx msdevflow setup --dry-run --targets all
34
45
  ```
35
46
 
36
- 自定义 Claude Code skills 目录:
47
+ 安装目录映射:
48
+
49
+ ```text
50
+ Claude Code -> ~/.claude/skills/msdevflow
51
+ Codex -> ~/.agents/skills/msdevflow
52
+ 仅 OpenCode -> ~/.agents/skills/msdevflow
53
+ OpenCode + Claude Code -> 复用 ~/.claude/skills/msdevflow
54
+ OpenCode + Codex -> 复用 ~/.agents/skills/msdevflow
55
+ Claude Code + Codex -> 两个受管副本
56
+ 三者同时安装 -> 两个受管副本,并提示 OpenCode 可从两处发现同名 skill
57
+ ```
58
+
59
+ 自定义目录:
37
60
 
38
61
  ```bash
39
- npx msdevflow setup --skills-dir "<skills-directory>"
62
+ npx msdevflow setup --skills-dir "<claude-skills-directory>"
63
+ npx msdevflow setup --agents-skills-dir "<shared-agent-skills-directory>"
40
64
  ```
41
65
 
42
- 安装完成后重新启动 Claude Code 会话。如果机器已有 Python 版本的 `gitcode`,setup 会保留它,并为 npm CLI 创建 `gitcode-npm` 命令;否则使用 `gitcode`。
66
+ `--skills-dir` 未配合 `--targets` 使用时保留旧语义,只安装 Claude Code 目标。没有检测到客户端时,交互运行会要求选择目标;非交互运行必须提供 `--targets`。安装完成后重启或重新加载对应客户端。如果机器已有 Python 版本的 `gitcode`,setup 会保留它,并为 npm CLI 创建 `gitcode-npm` 命令;否则使用 `gitcode`。
43
67
 
44
68
  ## 完整端到端流程
45
69
 
46
- 从目标仓库目录启动 Claude Code
70
+ 从目标仓库目录启动已安装目标中的任一客户端,然后调用 skill
47
71
 
48
- ```bash
49
- claude
50
- ```
72
+ - Claude Code:输入 `/msdevflow`;
73
+ - Codex:使用 `$msdevflow`、`/skills` 选择,或让 Codex 按描述隐式匹配;
74
+ - OpenCode:通过其 skill 工具选择,或让 OpenCode 按描述隐式匹配。
51
75
 
52
- 然后输入:
76
+ 客户端 UI 和命令入口可能随版本变化,但传给 skill 的任务正文相同:
53
77
 
54
78
  ```text
55
- /msdevflow
56
79
  处理 https://gitcode.com/<canonical-owner>/<repo>/issues/<number>。
57
80
  本地仓库:<local-repository-absolute-path>
58
81
  canonical:<canonical-owner>/<repo>
@@ -160,14 +183,18 @@ Agent 写入的 PR/Issue 正文、评论和 discussion 回复末尾会附加:
160
183
 
161
184
  ## 常见阻塞
162
185
 
163
- ### `/msdevflow` 不存在
186
+ ### 客户端找不到 `msdevflow`
164
187
 
165
- 确认以下文件存在,然后重启 Claude Code:
188
+ 按目标确认至少一个受支持的 manifest 存在,然后重启或重新加载客户端:
166
189
 
167
190
  ```text
168
- ~/.claude/skills/msdevflow/SKILL.md
191
+ Claude Code: ~/.claude/skills/msdevflow/SKILL.md
192
+ Codex: ~/.agents/skills/msdevflow/SKILL.md
193
+ OpenCode: 上述任一路径(按 setup 计划)
169
194
  ```
170
195
 
196
+ 可运行 `npx msdevflow setup --dry-run --targets <clients>` 检查目标映射。
197
+
171
198
  ### `blocked: bootstrap-required`
172
199
 
173
200
  重新运行:
package/lib/bootstrap.js CHANGED
@@ -25,6 +25,7 @@ const REGISTRY = "https://registry.npmjs.org";
25
25
  const GITCODE_PACKAGE = "@gitcode-cli/cli@latest";
26
26
  const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
27
27
  const BUNDLED_SKILL_DIR = path.join(PACKAGE_ROOT, "skill", "msdevflow");
28
+ const SUPPORTED_CLIENTS = ["claude", "codex", "opencode"];
28
29
  const REQUIRED_SCHEMAS = {
29
30
  "auth login": ["web"],
30
31
  "auth status": ["json"],
@@ -56,11 +57,18 @@ export function usage() {
56
57
  msdevflow setup [options]
57
58
 
58
59
  Options:
59
- --skills-dir <path> Installed skills directory (default: ~/.claude/skills)
60
- --dry-run Diagnose and print the plan without changing the environment
61
- --yes Apply the displayed plan without an interactive confirmation
62
- --json Print the final result as JSON
63
- -h, --help Show this help
60
+ --targets <clients> Comma-separated: claude,codex,opencode,all
61
+ --skills-dir <path> Claude skills directory (default: ~/.claude/skills)
62
+ --agents-skills-dir <path> Shared Agent Skills directory (default: ~/.agents/skills)
63
+ --dry-run Diagnose and print the plan without changing the environment
64
+ --yes Apply the displayed plan without an interactive confirmation
65
+ --json Print the final result as JSON
66
+ -h, --help Show this help
67
+
68
+ Without --targets, setup detects Claude Code, Codex, and OpenCode on PATH. If no
69
+ client is detected, interactive setup asks which clients to install; non-interactive
70
+ setup requires --targets. OpenCode reuses a Claude or shared Agent Skills target
71
+ instead of creating a third copy.
64
72
 
65
73
  The setup installs or updates the bundled msdevflow skill, installs or upgrades
66
74
  the official GitCode npm CLI, and installs all reviewed Python runtime
@@ -69,6 +77,24 @@ authenticated, it starts the CLI's official browser login; setup never reads or
69
77
  prints a token.`;
70
78
  }
71
79
 
80
+ export function parseTargets(value) {
81
+ const requested = value.split(",").map((target) => target.trim().toLowerCase()).filter(Boolean);
82
+ if (requested.length === 0) {
83
+ throw new BootstrapError("--targets requires at least one client.", 2);
84
+ }
85
+ const unsupported = requested.filter(
86
+ (target) => target !== "all" && !SUPPORTED_CLIENTS.includes(target),
87
+ );
88
+ if (unsupported.length) {
89
+ throw new BootstrapError(
90
+ `Unsupported client target: ${unsupported.join(", ")}. Supported targets: ${SUPPORTED_CLIENTS.join(", ")}, all.`,
91
+ 2,
92
+ );
93
+ }
94
+ const expanded = requested.includes("all") ? SUPPORTED_CLIENTS : requested;
95
+ return [...new Set(expanded)];
96
+ }
97
+
72
98
  export function parseArgs(argv) {
73
99
  if (argv.includes("--help") || argv.includes("-h")) {
74
100
  return { help: true };
@@ -78,13 +104,25 @@ export function parseArgs(argv) {
78
104
  }
79
105
 
80
106
  const options = {
107
+ targets: [],
108
+ targetsSpecified: false,
81
109
  skillsDir: "",
110
+ agentsSkillsDir: "",
82
111
  dryRun: false,
83
112
  yes: false,
84
113
  json: false,
85
114
  };
86
115
  for (let index = 1; index < argv.length; index += 1) {
87
116
  const argument = argv[index];
117
+ if (argument === "--targets") {
118
+ index += 1;
119
+ if (!argv[index]) {
120
+ throw new BootstrapError("--targets requires a comma-separated client list.", 2);
121
+ }
122
+ options.targets = parseTargets(argv[index]);
123
+ options.targetsSpecified = true;
124
+ continue;
125
+ }
88
126
  if (argument === "--skills-dir") {
89
127
  index += 1;
90
128
  if (!argv[index]) {
@@ -93,6 +131,14 @@ export function parseArgs(argv) {
93
131
  options.skillsDir = argv[index];
94
132
  continue;
95
133
  }
134
+ if (argument === "--agents-skills-dir") {
135
+ index += 1;
136
+ if (!argv[index]) {
137
+ throw new BootstrapError("--agents-skills-dir requires a path.", 2);
138
+ }
139
+ options.agentsSkillsDir = argv[index];
140
+ continue;
141
+ }
96
142
  if (argument === "--dry-run") {
97
143
  options.dryRun = true;
98
144
  continue;
@@ -110,16 +156,25 @@ export function parseArgs(argv) {
110
156
  return options;
111
157
  }
112
158
 
113
- function defaultSkillsDir(environment) {
114
- return environment.CLAUDE_SKILLS_DIR || path.join(homedir(), ".claude", "skills");
159
+ function defaultClaudeSkillsDir(options, environment, platform) {
160
+ return options.skillsDir
161
+ || environment.CLAUDE_SKILLS_DIR
162
+ || path.join(environmentHome(environment, platform), ".claude", "skills");
115
163
  }
116
164
 
117
- export function resolveLayout(options, environment = process.env, bundledSkillDir = BUNDLED_SKILL_DIR) {
118
- const skillsDir = path.resolve(options.skillsDir || defaultSkillsDir(environment));
119
- const workflowDir = path.join(skillsDir, "msdevflow");
165
+ function defaultAgentsSkillsDir(options, environment, platform) {
166
+ return options.agentsSkillsDir
167
+ || path.join(environmentHome(environment, platform), ".agents", "skills");
168
+ }
169
+
170
+ function createSkillLayout(kind, clients, skillsDir, bundledSkillDir) {
171
+ const resolvedSkillsDir = path.resolve(skillsDir);
172
+ const workflowDir = path.join(resolvedSkillsDir, "msdevflow");
120
173
  const bundledWorkflowDir = path.resolve(bundledSkillDir);
121
174
  return {
122
- skillsDir,
175
+ kind,
176
+ clients,
177
+ skillsDir: resolvedSkillsDir,
123
178
  workflowDir,
124
179
  workflowManifest: path.join(workflowDir, "SKILL.md"),
125
180
  pythonRequirements: path.join(workflowDir, "scripts", "requirements.txt"),
@@ -129,6 +184,58 @@ export function resolveLayout(options, environment = process.env, bundledSkillDi
129
184
  };
130
185
  }
131
186
 
187
+ export function resolveLayout(options, environment = process.env, bundledSkillDir = BUNDLED_SKILL_DIR) {
188
+ return createSkillLayout(
189
+ "claude",
190
+ ["claude"],
191
+ defaultClaudeSkillsDir(options, environment, process.platform),
192
+ bundledSkillDir,
193
+ );
194
+ }
195
+
196
+ export function resolveLayouts(
197
+ options,
198
+ clients,
199
+ environment = process.env,
200
+ bundledSkillDir = BUNDLED_SKILL_DIR,
201
+ platform = process.platform,
202
+ ) {
203
+ const layouts = [];
204
+ const openCodeUsesClaude = clients.includes("opencode") && clients.includes("claude");
205
+ if (clients.includes("claude")) {
206
+ layouts.push(createSkillLayout(
207
+ "claude",
208
+ openCodeUsesClaude ? ["claude", "opencode"] : ["claude"],
209
+ defaultClaudeSkillsDir(options, environment, platform),
210
+ bundledSkillDir,
211
+ ));
212
+ }
213
+ if (clients.includes("codex") || (clients.includes("opencode") && !openCodeUsesClaude)) {
214
+ const sharedClients = clients.includes("codex") ? ["codex"] : [];
215
+ if (clients.includes("opencode")) {
216
+ sharedClients.push("opencode");
217
+ }
218
+ layouts.push(createSkillLayout(
219
+ "agents",
220
+ sharedClients,
221
+ defaultAgentsSkillsDir(options, environment, platform),
222
+ bundledSkillDir,
223
+ ));
224
+ }
225
+ const unique = new Map();
226
+ for (const layout of layouts) {
227
+ const key = normalizedPath(layout.workflowDir, platform);
228
+ const existing = unique.get(key);
229
+ if (existing) {
230
+ existing.clients = [...new Set([...existing.clients, ...layout.clients])];
231
+ existing.kind = "shared";
232
+ } else {
233
+ unique.set(key, layout);
234
+ }
235
+ }
236
+ return [...unique.values()];
237
+ }
238
+
132
239
  function namedCommand(command, args, platform) {
133
240
  return executableCommand(platform === "win32" && command === "npm" ? "npm.cmd" : command, args, platform);
134
241
  }
@@ -386,6 +493,51 @@ export function resolveExecutable(name, run, platform) {
386
493
  return result.status === 0 ? firstOutputLine(result.stdout) : "";
387
494
  }
388
495
 
496
+ export function detectClients(run, platform) {
497
+ return SUPPORTED_CLIENTS.filter((client) => Boolean(resolveExecutable(client, run, platform)));
498
+ }
499
+
500
+ async function selectClientTargets(input, output, promptOutput = output) {
501
+ if (!input.isTTY || !output.isTTY) {
502
+ throw new BootstrapError(
503
+ "No supported agent client was detected. Non-interactive setup requires --targets <clients>.",
504
+ 2,
505
+ );
506
+ }
507
+ promptOutput.write("No supported agent client was detected. Available targets: claude, codex, opencode, all.\n");
508
+ const terminal = createInterface({ input, output: promptOutput });
509
+ try {
510
+ const answer = await terminal.question("Install msdevflow for: ");
511
+ return parseTargets(answer);
512
+ } finally {
513
+ terminal.close();
514
+ }
515
+ }
516
+
517
+ async function resolveClientTargets(options, run, platform, selectClients) {
518
+ if (options.targetsSpecified) {
519
+ return { clients: options.targets, source: "explicit", detected: [] };
520
+ }
521
+ if (options.skillsDir) {
522
+ return { clients: ["claude"], source: "legacy-skills-dir", detected: [] };
523
+ }
524
+ const detected = detectClients(run, platform);
525
+ if (detected.length) {
526
+ return { clients: detected, source: "detected", detected };
527
+ }
528
+ const clients = await selectClients();
529
+ return { clients, source: "selected", detected: [] };
530
+ }
531
+
532
+ function clientTargetWarnings(clients, layouts) {
533
+ if (clients.includes("opencode") && layouts.length > 1) {
534
+ return [
535
+ "OpenCode can discover msdevflow from both installed paths; keep both copies managed by this setup to prevent version drift.",
536
+ ];
537
+ }
538
+ return [];
539
+ }
540
+
389
541
  function normalizedPath(value, platform) {
390
542
  const normalized = value.replaceAll("\\", "/").replace(/\/+$/, "");
391
543
  return platform === "win32" ? normalized.toLowerCase() : normalized;
@@ -677,10 +829,16 @@ function safeAuthStatus(output) {
677
829
 
678
830
  function printPlan(plan, write) {
679
831
  write("Setup plan (no changes made yet):");
680
- write(` Skills directory: ${plan.layout.skillsDir}`);
681
- write(` Bundled skill: ${plan.skillInstall.source}`);
682
- write(` Skill target: ${plan.skillInstall.target}`);
683
- write(` Skill action: ${plan.skillInstall.status}`);
832
+ write(` Client targets: ${plan.clientTargets.clients.join(", ")} (${plan.clientTargets.source})`);
833
+ write(` Bundled skill: ${plan.skillInstalls[0].details.source}`);
834
+ for (const { layout, details } of plan.skillInstalls) {
835
+ write(` Skill target: ${details.target}`);
836
+ write(` Clients: ${layout.clients.join(", ")}`);
837
+ write(` Action: ${details.status}`);
838
+ }
839
+ for (const warning of plan.clientTargets.warnings) {
840
+ write(` Warning: ${warning}`);
841
+ }
684
842
  write(` Existing gitcode: ${plan.diagnosis.classification}`);
685
843
  if (plan.diagnosis.existingExecutable) {
686
844
  write(` Existing executable: ${plan.diagnosis.existingExecutable}`);
@@ -781,7 +939,13 @@ function authenticateGitcode(cliExecutable, authentication, run, platform, write
781
939
 
782
940
  function textResult(result, write) {
783
941
  write("Setup completed:");
784
- write(` Skill: ${result.skill.status} at ${result.skill.target}`);
942
+ write(` Client targets: ${result.clients.join(", ")}`);
943
+ for (const skill of result.skills) {
944
+ write(` Skill: ${skill.status} at ${skill.target} (${skill.clients.join(", ")})`);
945
+ }
946
+ for (const warning of result.warnings) {
947
+ write(` Warning: ${warning}`);
948
+ }
785
949
  write(` Workflow CLI command: ${result.gitcode.command}`);
786
950
  write(` GitCode CLI: ${result.gitcode.version}`);
787
951
  write(` Required capabilities: ${result.gitcode.capabilities}`);
@@ -800,8 +964,31 @@ export async function runSetup(options, dependencies = {}) {
800
964
  const run = dependencies.run || execute;
801
965
  const write = dependencies.write || ((line) => console.log(line));
802
966
  const writePlan = dependencies.writePlan || write;
803
- const layout = resolveLayout(options, environment, dependencies.bundledSkillDir);
804
- const skillInstall = skillInstallDetails(layout);
967
+ const targetSelection = await resolveClientTargets(
968
+ options,
969
+ run,
970
+ platform,
971
+ dependencies.selectClients || (() => selectClientTargets(
972
+ process.stdin,
973
+ process.stdout,
974
+ options.json ? process.stderr : process.stdout,
975
+ )),
976
+ );
977
+ const layouts = resolveLayouts(
978
+ options,
979
+ targetSelection.clients,
980
+ environment,
981
+ dependencies.bundledSkillDir,
982
+ platform,
983
+ );
984
+ const skillInstalls = layouts.map((layout) => ({
985
+ layout,
986
+ details: skillInstallDetails(layout),
987
+ }));
988
+ const clientTargets = {
989
+ ...targetSelection,
990
+ warnings: clientTargetWarnings(targetSelection.clients, layouts),
991
+ };
805
992
 
806
993
  if (Number(process.versions.node.split(".")[0]) < 18) {
807
994
  throw new BootstrapError(`Node.js >=18 is required; found ${process.version}.`, 2);
@@ -830,11 +1017,12 @@ export async function runSetup(options, dependencies = {}) {
830
1017
  run({ command: python.executable, args: ["-m", "pip", "--version"] });
831
1018
  const pipInvocation = {
832
1019
  command: python.executable,
833
- args: ["-m", "pip", "install", "-r", layout.bundledPythonRequirements],
1020
+ args: ["-m", "pip", "install", "-r", layouts[0].bundledPythonRequirements],
834
1021
  };
835
1022
  const plan = {
836
- layout,
837
- skillInstall,
1023
+ clientTargets,
1024
+ layouts,
1025
+ skillInstalls,
838
1026
  diagnosis,
839
1027
  installInvocation,
840
1028
  gitcodeInstall,
@@ -872,14 +1060,19 @@ export async function runSetup(options, dependencies = {}) {
872
1060
  options.json ? writePlan : write,
873
1061
  options.json,
874
1062
  );
875
- const skillStatus = installBundledSkill(skillInstall);
876
- return {
1063
+ const skills = skillInstalls.map(({ layout, details }) => ({
1064
+ kind: layout.kind,
1065
+ clients: layout.clients,
1066
+ status: installBundledSkill(details),
1067
+ target: layout.workflowDir,
1068
+ }));
1069
+ const result = {
877
1070
  state: "ready",
878
1071
  git: gitVersion,
879
- skill: {
880
- status: skillStatus,
881
- target: layout.workflowDir,
882
- },
1072
+ clients: clientTargets.clients,
1073
+ targetSource: clientTargets.source,
1074
+ warnings: clientTargets.warnings,
1075
+ skills,
883
1076
  gitcode,
884
1077
  python: {
885
1078
  executable: python.executable,
@@ -888,6 +1081,10 @@ export async function runSetup(options, dependencies = {}) {
888
1081
  chromium: "not-downloaded",
889
1082
  },
890
1083
  };
1084
+ if (skills.length === 1) {
1085
+ result.skill = skills[0];
1086
+ }
1087
+ return result;
891
1088
  }
892
1089
 
893
1090
  export async function main(argv, dependencies = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msdevflow",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Install the msdevflow GitCode skill and its runtime dependencies",
5
5
  "type": "module",
6
6
  "bin": {
@@ -55,6 +55,8 @@ discover -> issue -> develop -> pr -> ci
55
55
 
56
56
  ## 4. 调用示例
57
57
 
58
+ 以下示例使用 Claude Code 的 `/msdevflow` 形式。Codex 使用 `$msdevflow`、`/skills` 或隐式匹配;OpenCode 使用 skill 工具或隐式匹配。无论客户端入口如何,正文中的 `action=<name>` 和其余任务参数保持相同。
59
+
58
60
  ### 未指定 action:完整 E2E
59
61
 
60
62
  ```text
@@ -174,7 +176,7 @@ PR:https://gitcode.com/Ascend/example/pulls/456
174
176
 
175
177
  需要:
176
178
 
177
- - Claude Code 或其他兼容 Agent Skills 的客户端;
179
+ - Claude Code、Codex OpenCode;
178
180
  - `git`;
179
181
  - GitCode 官方 npm CLI;
180
182
  - GitCode CLI 已认证;
@@ -208,39 +210,60 @@ git --version
208
210
 
209
211
  ## 6. 安装
210
212
 
211
- npm 包同时携带 `msdevflow` skill 和 setup 程序。标准安装只需要:
213
+ npm 包同时携带 `msdevflow` skill 和 setup 程序。首次安装:
212
214
 
213
215
  ```bash
214
216
  npx msdevflow setup
215
217
  ```
216
218
 
217
- `setup` 先展示 skill 目标、安装或更新状态、GitCode CLI 归属、registry、安装模式、Python 依赖命令和认证方式;用户确认后,在同一次运行中:
219
+ 后续升级无需卸载旧版本,显式使用 `@latest` 获取 npm 上的最新版:
220
+
221
+ ```bash
222
+ npx msdevflow@latest setup
223
+ ```
224
+
225
+ `setup` 是幂等的:内容一致时保持 `current`,内容不同时原子更新。
218
226
 
219
- 1. 安装或更新 `~/.claude/skills/msdevflow`;
227
+ `setup` 默认检测 PATH 中的 Claude Code、Codex 和 OpenCode,先展示客户端、物理 skill 目标、安装或更新状态、GitCode CLI 归属、registry、安装模式、Python 依赖命令和认证方式;用户确认后,在同一次运行中:
228
+
229
+ 1. 从 npm 包内唯一源码 `skill/msdevflow` 原子安装或更新所需受管副本;
220
230
  2. 安装或升级官方 GitCode npm CLI;
221
231
  3. 安装内置 `scripts/requirements.txt` 中的 Python Playwright 包;
222
232
  4. 验收 workflow 所需 schema/API;
223
233
  5. CLI 未认证时运行选定命令的 `auth login --web`。
224
234
 
225
- 可选只读预检:
235
+ 客户端到物理目录的映射:
226
236
 
227
- ```bash
228
- npx msdevflow setup --dry-run
237
+ ```text
238
+ Claude Code -> ~/.claude/skills/msdevflow
239
+ Codex -> ~/.agents/skills/msdevflow
240
+ 仅 OpenCode -> ~/.agents/skills/msdevflow
241
+ OpenCode + Claude Code -> 复用 Claude 目标
242
+ OpenCode + Codex -> 复用 Agent Skills 目标
243
+ Claude Code + Codex -> 两个受管副本
244
+ 三者同时安装 -> 两个受管副本,并提示 OpenCode 双路径发现
229
245
  ```
230
246
 
231
- 自定义 skills 目录时使用:
247
+ 显式选择客户端、只读预检或自定义目录:
232
248
 
233
249
  ```bash
234
- npx msdevflow setup --skills-dir "<skills-directory>"
250
+ npx msdevflow setup --targets claude
251
+ npx msdevflow setup --targets codex,opencode
252
+ npx msdevflow setup --targets all
253
+ npx msdevflow setup --dry-run --targets all
254
+ npx msdevflow setup --skills-dir "<claude-skills-directory>"
255
+ npx msdevflow setup --agents-skills-dir "<shared-agent-skills-directory>"
235
256
  ```
236
257
 
258
+ 没有检测到客户端时,交互运行要求选择目标,非交互运行要求 `--targets`。`--skills-dir` 单独使用时保留旧语义,只安装 Claude Code 目标。Trae 的目录与兼容细节尚未可靠验证,因此当前不属于支持目标。
259
+
237
260
  npm 包尚未发布时,可从当前源码/发布目录运行等价入口:
238
261
 
239
262
  ```bash
240
263
  node ./msdevflow/bin/msdevflow.js setup
241
264
  ```
242
265
 
243
- `setup` 不下载或启动 Chromium。凭证只由 GitCode CLI 和官方浏览器流程接收与保存;setup 不读取、输出或转存 Token,也不要求用户把 Token 传给 Agent。已有 skill 目标只有 manifest 明确标识为 `msdevflow` 时才允许更新,来源不明的目录、文件或符号链接会被拒绝覆盖。安装后建议重新启动 Claude Code 会话。
266
+ `setup` 不下载或启动 Chromium。凭证只由 GitCode CLI 和官方浏览器流程接收与保存;setup 不读取、输出或转存 Token,也不要求用户把 Token 传给 Agent。每个已有 skill 目标只有 manifest 明确标识为 `msdevflow` 时才允许更新,来源不明的目录、文件或符号链接会被拒绝覆盖。安装后重启或重新加载对应客户端。
244
267
 
245
268
  ## 7. 仓库画像和 PR 模板
246
269
 
@@ -2,8 +2,9 @@
2
2
  name: msdevflow
3
3
  description: >
4
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 仓库。
5
+ compatibility: Requires Git, Node.js >=18, Python >=3.10, GitCode access, and the setup-installed GitCode CLI. Supports Claude Code, Codex, and OpenCode.
5
6
  metadata:
6
- version: 1.9.0
7
+ version: 2.0.0
7
8
  source: msdevflow
8
9
  ---
9
10