cowork-flow 2.1.0 → 2.3.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@
4
4
 
5
5
  ### Added
6
6
 
7
+ ## [2.3.0] - 2026-10-21
8
+
9
+ ### Added
10
+
11
+ - **`cwf project init` 宿主自动判定与 `--host` 主旗标**:省略 `--host` 时先按项目已有的宿主标记(与 `project sync` 同一套探测)判定,交互终端再回退到选择器;`--host` 可重复/逗号分隔,`--platform`、`--platforms` 保留为兼容别名。非交互且探测为空时仍明确报 `Host selection required`,不静默猜宿主。
12
+
13
+ ### Changed
14
+
15
+ - **`--developer` 省略时从 git 身份就近取值**:顺序为已有 `.developer` > 显式参数 > `git config user.name`(已有身份不被覆盖;初次运行会标注 `(from git user.name)`);无 git 身份时交互终端提示、非交互仍报错,`--dry-run` 只报告 `would-skip` 不失败。`project init` / `project sync` 的输出行由 `Platforms:` 改为 `Hosts:`。
16
+
17
+ ### Fixed
18
+
19
+ - **bootstrap 技能在非 TTY 下初始化失败**:六份共享的 `cowork-flow-bootstrap` 技能改为提示 agent 传入所在宿主(`npx cowork-flow project init --host <host>`),不再依赖交互选择器或位置参数 `.`,AI 宿主/CI 可直接完成初始化;六份载荷保持字节一致。
20
+
21
+ ## [2.2.0] - 2026-10-20
22
+
23
+ ### Changed
24
+
25
+ - **计划与任务验证段纳入规范门禁检查**:`task-planning` 的计划 `Final Verification`、任务规则与 Self-Review 要求按 `dev_type` 逐条核对 `.cowork-flow/spec/<dev_type>/` 适用规范与项目门禁——改动被规范覆盖时,把决定命令或 `规范核对:` 行写进计划的 Final Verification 与任务简报的 Verification Command,标 `pass` / `not_applicable` 并写理由;`decision-anchor` 契约的 `## 验证命令` 同步要求该检查行,并明确为 review 强提醒而非 `spec-check` 硬门禁,120 字符/10 行限制不变。
26
+ - **模板示例统一展示规范核对行**:`decision-anchor` Schema 的 `## 验证命令` 示例新增 `规范核对:` 行;`task-planning` 任务简报的 `Verification Command` 示例块后补充写法说明,避免新建计划/任务漏写规范检查项。
27
+
7
28
  ## [2.1.0] - 2026-10-19
8
29
 
9
30
  ### Added
@@ -386,7 +407,9 @@
386
407
 
387
408
  此版本未保留本地条目正文,详情见 [GitHub Release](https://github.com/fishlikewater/cowork-flow/releases/tag/v0.0.47)。
388
409
 
389
- [Unreleased]: https://github.com/fishlikewater/cowork-flow/compare/v2.1.0...HEAD
410
+ [Unreleased]: https://github.com/fishlikewater/cowork-flow/compare/v2.3.0...HEAD
411
+ [2.3.0]: https://github.com/fishlikewater/cowork-flow/compare/v2.2.0...v2.3.0
412
+ [2.2.0]: https://github.com/fishlikewater/cowork-flow/compare/v2.1.0...v2.2.0
390
413
  [2.1.0]: https://github.com/fishlikewater/cowork-flow/compare/v2.0.0...v2.1.0
391
414
  [2.0.0]: https://github.com/fishlikewater/cowork-flow/compare/v1.9.2...v2.0.0
392
415
  [1.9.2]: https://github.com/fishlikewater/cowork-flow/compare/v1.9.1...v1.9.2
package/README.en.md CHANGED
@@ -50,25 +50,33 @@ below with `npx cowork-flow` — except for `cwf self update`.
50
50
  Preview the files that will be written:
51
51
 
52
52
  ```bash
53
- cwf project init . --platform codex --developer alice --dry-run
53
+ cwf project init --host codex --developer alice --dry-run
54
54
  ```
55
55
 
56
56
  Drop `--dry-run` once the list looks right:
57
57
 
58
58
  ```bash
59
- cwf project init . --platform codex --developer alice
59
+ cwf project init --host codex --developer alice
60
60
  ```
61
61
 
62
62
  Replace `codex` with the host you use:
63
63
 
64
64
  `codex`, `claude-code`, `opencode`, `zcode`, `qoder`, `kimi-code`, `dsh`.
65
65
 
66
- For a new project, replace `.` with `./my-project` and enter that directory. Use
67
- `--platform all` only when the project really will be worked on from several hosts.
66
+ The command initializes the current directory by default. For a new project, pass the
67
+ target directory as a positional argument, for example
68
+ `cwf project init ./my-project --host codex`, then enter that directory. When `--host`
69
+ is omitted, init first looks for host markers already in the project (such as
70
+ `.codex/` or `.claude/`); an interactive terminal offers a picker when none is found,
71
+ and a non-interactive environment needs an explicit host. Use `--host all` only when
72
+ the project really will be worked on from several hosts.
68
73
 
69
74
  Initialization adds `AGENTS.md`, `.cowork-flow/`, and the matching host files. It does
70
- not touch application code. `--developer` is optional; on an existing project, always
71
- run `--dry-run` first.
75
+ not touch application code. An existing `.developer` is never overwritten; on first
76
+ initialization an explicit `--developer` wins over `git config user.name`. With no git
77
+ identity, an interactive terminal prompts and a non-interactive run fails (`--dry-run`
78
+ only reports `would-skip`). `--platform` / `--platforms` remain accepted aliases of
79
+ `--host`. On an existing project, always run `--dry-run` first.
72
80
 
73
81
  ### 3. Finish host setup
74
82
 
@@ -135,7 +143,7 @@ how the project is organized, see [architecture and extension](docs/architecture
135
143
 
136
144
  | Command | Purpose |
137
145
  |---|---|
138
- | `cwf project init [target] --platform <host> [--developer <name>] [--dry-run] [--force]` | Initialize a project |
146
+ | `cwf project init [target] --host <host> [--developer <name>] [--dry-run] [--force]` | Initialize a project |
139
147
  | `cwf project sync [target] [--dry-run] [--force]` | Update the cowork-flow-managed files in a project |
140
148
  | `cwf host add <host> [--component <name>] [--dry-run] [--force] [--prune-old] [--uninstall]` | Install a machine-level host component |
141
149
  | `cwf host remove <host> [--component <name>] [--dry-run] [--force]` | Remove a machine-level host component |
package/README.md CHANGED
@@ -44,22 +44,22 @@ cwf --version
44
44
  先预览将要写入的文件:
45
45
 
46
46
  ```bash
47
- cwf project init . --platform codex --developer alice --dry-run
47
+ cwf project init --host codex --developer alice --dry-run
48
48
  ```
49
49
 
50
50
  确认无误后去掉 `--dry-run`:
51
51
 
52
52
  ```bash
53
- cwf project init . --platform codex --developer alice
53
+ cwf project init --host codex --developer alice
54
54
  ```
55
55
 
56
56
  把 `codex` 换成你正在使用的宿主:
57
57
 
58
58
  `codex`、`claude-code`、`opencode`、`zcode`、`qoder`、`kimi-code`、`dsh`。
59
59
 
60
- 新项目可以把 `.` 换成 `./my-project`,然后进入该目录。只有确实会在多个宿主中使用时,才需要 `--platform all`。
60
+ 命令默认在当前目录初始化;新项目把目标目录作为位置参数传入,例如 `cwf project init ./my-project --host codex`,再进入该目录。省略 `--host` 时,会先按项目里已有的宿主标记(如 `.codex/`、`.claude/`)自动判定;没有标记的交互终端会让你选择,非交互环境则需要显式传入。只有确实会在多个宿主中使用时,才用 `--host all`。
61
61
 
62
- 初始化会加入 `AGENTS.md`、`.cowork-flow/` 和对应的宿主文件,不会修改业务代码。`--developer` 可以省略;已有项目建议始终先跑 `--dry-run`。
62
+ 初始化会加入 `AGENTS.md`、`.cowork-flow/` 和对应的宿主文件,不会修改业务代码。已有 `.developer` 不会被覆盖;首次初始化时显式 `--developer` 优先于 `git config user.name`。git 也没有身份时,交互终端会提示,非交互环境报错(`--dry-run` 只报告 `would-skip`,不失败)。`--platform` / `--platforms` 是 `--host` 的兼容别名。已有项目建议始终先跑 `--dry-run`。
63
63
 
64
64
  ### 3. 完成宿主设置
65
65
 
@@ -117,7 +117,7 @@ cwf project sync .
117
117
 
118
118
  | 命令 | 用途 |
119
119
  |---|---|
120
- | `cwf project init [target] --platform <host> [--developer <name>] [--dry-run] [--force]` | 初始化项目 |
120
+ | `cwf project init [target] --host <host> [--developer <name>] [--dry-run] [--force]` | 初始化项目 |
121
121
  | `cwf project sync [target] [--dry-run] [--force]` | 更新项目内由 cowork-flow 管理的文件 |
122
122
  | `cwf host add <host> [--component <name>] [--dry-run] [--force] [--prune-old] [--uninstall]` | 安装机器级宿主组件 |
123
123
  | `cwf host remove <host> [--component <name>] [--dry-run] [--force]` | 卸载机器级宿主组件 |
package/docs/direction.md CHANGED
@@ -52,7 +52,7 @@
52
52
 
53
53
  ## 6. 待决项与缓行
54
54
 
55
- - **待维护者拍板:中文面文档是否英文化 / 双语。** 英文社区是 spec-driven development 讨论的主场,与 `AGENTS.md` 的「文档语言:中文」约定冲突,需单独立项评估;契约与英文版 README 已先行英文化(`5f602c0`),待决收窄为 `docs/`、`template/AGENTS.md`、`CHANGELOG.md`。
55
+ - **已拍板(2026-10-21):详细文档维持中文,README 保持中英双语。** 英文社区是 spec-driven development 讨论的主场,但当前详细文档约 590 行、随 npm 以纯 Markdown 发布,全量双语会引入翻译漂移与文档框架成本。决定:`docs/`、`template/AGENTS.md`、`CHANGELOG.md` 维持中文;`README.md` / `README.en.md` 双语同步;不引入 VuePress/mkdocs 或目录式双语。未来若英文需求被证实,采用并列 `docs/*.en.md`,只覆盖安装面文档,并新增“英文文件与中文源文件标题集合一致”的漂移门禁后再落地。
56
56
  - 2026-10-13 澄清:「文档语言:中文」约束的是下游宿主工作时**产出文档**的语言,不要求把随包分发的产品载荷(`template/skills/`、`template/.cowork-flow/spec/contracts/`、`presets/`)翻译成中文。
57
57
  - **维持缓行**:MCP 写工具(须先重开 `fact-layer-access` 契约)、新宿主接入、自创跨 agent 协议。
58
58
  - **观察项(触发条件已到)**:机器可解析 AC 是与 Spec Kit / OpenSpec 互操作的第一块砖(spec 制品 ↔ decision-anchor 映射),符合「SDD 工具之下的状态层」定位。原定「待 agent-teams 排期落定后议」,而 1.9.0 多执行者协作主线已实现,可排入讨论。
package/docs/hosts.md CHANGED
@@ -9,7 +9,9 @@
9
9
  | 项目级文件 | 当前项目的 `AGENTS.md`、`.cowork-flow/` 和宿主目录 | `cwf project init` / `cwf project sync` |
10
10
  | 机器级组件 | 宿主的用户配置目录,如插件、预设或 hook | `cwf host add` / `cwf host remove` |
11
11
 
12
- 每个项目至少需要执行一次 `cwf project init`。是否需要 `cwf host add`,取决于宿主:
12
+ 每个项目至少需要执行一次 `cwf project init`。省略 `--host` 时,init 先按项目里已有的宿主标记(如 `.codex/`、`.claude/`)自动判定,与 `cwf project sync` 使用同一套探测;没有标记的交互终端会给出选择器,非交互环境则必须显式传 `--host`。`--platform` / `--platforms` 仍是兼容别名。
13
+
14
+ 是否需要 `cwf host add`,取决于宿主:
13
15
 
14
16
  | 宿主 | 初始化后是否需要机器级组件 | 建议命令 |
15
17
  |---|---|---|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "CLI for installing and maintaining cowork-flow collaboration templates.",
5
5
  "keywords": [
6
6
  "ai-agent",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 Claude Code。",
5
5
  "displayName": "Cowork Flow",
6
6
  "author": {
@@ -17,10 +17,12 @@ workflow entry `./.cowork-flow/run`).
17
17
 
18
18
  ## No runtime: offer the install
19
19
 
20
- Tell the user this repository has no cowork-flow runtime, then offer:
20
+ Tell the user this repository has no cowork-flow runtime, then offer (replace
21
+ `<host>` with the host you are running in: `codex`, `claude-code`, `opencode`,
22
+ `zcode`, `qoder`, `kimi-code`, or `dsh`):
21
23
 
22
24
  ```bash
23
- npx cowork-flow project init .
25
+ npx cowork-flow project init --host <host>
24
26
  ```
25
27
 
26
28
  After init the project carries its own Skills (`.agents/skills/`), the task
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 Codex。",
5
5
  "author": {
6
6
  "name": "fishlikewater",
@@ -17,10 +17,12 @@ workflow entry `./.cowork-flow/run`).
17
17
 
18
18
  ## No runtime: offer the install
19
19
 
20
- Tell the user this repository has no cowork-flow runtime, then offer:
20
+ Tell the user this repository has no cowork-flow runtime, then offer (replace
21
+ `<host>` with the host you are running in: `codex`, `claude-code`, `opencode`,
22
+ `zcode`, `qoder`, `kimi-code`, or `dsh`):
21
23
 
22
24
  ```bash
23
- npx cowork-flow project init .
25
+ npx cowork-flow project init --host <host>
24
26
  ```
25
27
 
26
28
  After init the project carries its own Skills (`.agents/skills/`), the task
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 Kimi Code。",
5
5
  "author": {
6
6
  "name": "fishlikewater",
@@ -17,10 +17,12 @@ workflow entry `./.cowork-flow/run`).
17
17
 
18
18
  ## No runtime: offer the install
19
19
 
20
- Tell the user this repository has no cowork-flow runtime, then offer:
20
+ Tell the user this repository has no cowork-flow runtime, then offer (replace
21
+ `<host>` with the host you are running in: `codex`, `claude-code`, `opencode`,
22
+ `zcode`, `qoder`, `kimi-code`, or `dsh`):
21
23
 
22
24
  ```bash
23
- npx cowork-flow project init .
25
+ npx cowork-flow project init --host <host>
24
26
  ```
25
27
 
26
28
  After init the project carries its own Skills (`.agents/skills/`), the task
@@ -17,10 +17,12 @@ workflow entry `./.cowork-flow/run`).
17
17
 
18
18
  ## No runtime: offer the install
19
19
 
20
- Tell the user this repository has no cowork-flow runtime, then offer:
20
+ Tell the user this repository has no cowork-flow runtime, then offer (replace
21
+ `<host>` with the host you are running in: `codex`, `claude-code`, `opencode`,
22
+ `zcode`, `qoder`, `kimi-code`, or `dsh`):
21
23
 
22
24
  ```bash
23
- npx cowork-flow project init .
25
+ npx cowork-flow project init --host <host>
24
26
  ```
25
27
 
26
28
  After init the project carries its own Skills (`.agents/skills/`), the task
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 Qoder。",
5
5
  "displayName": "Cowork Flow",
6
6
  "author": {
@@ -17,10 +17,12 @@ workflow entry `./.cowork-flow/run`).
17
17
 
18
18
  ## No runtime: offer the install
19
19
 
20
- Tell the user this repository has no cowork-flow runtime, then offer:
20
+ Tell the user this repository has no cowork-flow runtime, then offer (replace
21
+ `<host>` with the host you are running in: `codex`, `claude-code`, `opencode`,
22
+ `zcode`, `qoder`, `kimi-code`, or `dsh`):
21
23
 
22
24
  ```bash
23
- npx cowork-flow project init .
25
+ npx cowork-flow project init --host <host>
24
26
  ```
25
27
 
26
28
  After init the project carries its own Skills (`.agents/skills/`), the task
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 ZCode。",
5
5
  "author": {
6
6
  "name": "fishlikewater",
@@ -17,10 +17,12 @@ workflow entry `./.cowork-flow/run`).
17
17
 
18
18
  ## No runtime: offer the install
19
19
 
20
- Tell the user this repository has no cowork-flow runtime, then offer:
20
+ Tell the user this repository has no cowork-flow runtime, then offer (replace
21
+ `<host>` with the host you are running in: `codex`, `claude-code`, `opencode`,
22
+ `zcode`, `qoder`, `kimi-code`, or `dsh`):
21
23
 
22
24
  ```bash
23
- npx cowork-flow project init .
25
+ npx cowork-flow project init --host <host>
24
26
  ```
25
27
 
26
28
  After init the project carries its own Skills (`.agents/skills/`), the task
package/src/cli.js CHANGED
@@ -149,7 +149,7 @@ async function defaultSelectPlatforms({ message, choices, defaultSelected = [] }
149
149
  const action = applyPickerKey(state, text, key, choices);
150
150
  if (action?.outcome === 'cancel') {
151
151
  cleanup();
152
- reject(new Error('Platform selection cancelled'));
152
+ reject(new Error('Host selection cancelled'));
153
153
  return;
154
154
  }
155
155
  if (action?.outcome === 'confirm') {
@@ -1,9 +1,12 @@
1
+ import { execFile } from 'node:child_process';
1
2
  import { access, readFile } from 'node:fs/promises';
2
3
  import { join, resolve } from 'node:path';
4
+ import { promisify } from 'node:util';
3
5
 
4
6
  import {
5
7
  applyPlan,
6
8
  buildInitPlan,
9
+ detectInstalledPlatforms,
7
10
  summarizePlan
8
11
  } from '../lib/copy-template.js';
9
12
  import { readPackageInfo } from '../lib/package-info.js';
@@ -15,6 +18,8 @@ import {
15
18
  } from '../lib/platforms.js';
16
19
  import { parseFlags, UsageError } from '../lib/cli-flags.js';
17
20
 
21
+ const execFileAsync = promisify(execFile);
22
+
18
23
  // The manifest layer reports a plain Error; re-thrown as a
19
24
  // usage error so a bad host name exits with the
20
25
  // argv-mistake code.
@@ -29,15 +34,19 @@ function parsePlatformSelectionOrUsage(values) {
29
34
  function parseInitArgs(args) {
30
35
  const { flags, positionals } = parseFlags(args, {
31
36
  boolean: ['--dry-run', '--force'],
32
- value: ['--platform', '--platforms', '--developer'],
33
- repeatable: ['--platform', '--platforms'],
37
+ value: ['--host', '--platform', '--platforms', '--developer'],
38
+ repeatable: ['--host', '--platform', '--platforms'],
34
39
  positional: { min: 0, max: 1, name: '[target]' }
35
40
  });
36
41
  return {
37
42
  developer: flags['--developer'] ?? null,
38
43
  dryRun: Boolean(flags['--dry-run']),
39
44
  force: Boolean(flags['--force']),
40
- platforms: [...(flags['--platform'] ?? []), ...(flags['--platforms'] ?? [])],
45
+ platforms: [
46
+ ...(flags['--host'] ?? []),
47
+ ...(flags['--platform'] ?? []),
48
+ ...(flags['--platforms'] ?? [])
49
+ ],
41
50
  target: positionals[0] === undefined ? process.cwd() : resolve(positionals[0])
42
51
  };
43
52
  }
@@ -57,7 +66,7 @@ async function pathExists(path) {
57
66
  function normalizeDeveloperName(value) {
58
67
  const name = String(value ?? '').trim();
59
68
  if (!name) {
60
- throw new UsageError('Developer name required. Run: cwf project init <target> --developer <name>');
69
+ throw new UsageError('Developer name required. Run: cwf project init [target] --developer <name>');
61
70
  }
62
71
  if (/[\\/]/.test(name)) {
63
72
  throw new UsageError('Developer name must not contain path separators');
@@ -65,6 +74,32 @@ function normalizeDeveloperName(value) {
65
74
  return name;
66
75
  }
67
76
 
77
+ function normalizeGitDeveloper(value) {
78
+ const name = String(value ?? '').trim();
79
+ if (!name || /[\\/]/.test(name) || (name.startsWith('<') && name.endsWith('>'))) {
80
+ return null;
81
+ }
82
+ return name;
83
+ }
84
+
85
+ // `git config user.name` reads the local repository first and falls back to the
86
+ // global config, which is the "nearest identity" this command wants. A missing
87
+ // binary, an unset value, or a directory that vanishes under us all mean "no
88
+ // git identity", never a failed init.
89
+ async function readGitDeveloper(target) {
90
+ const cwd = await pathExists(target) ? target : process.cwd();
91
+ try {
92
+ const { stdout } = await execFileAsync('git', ['config', 'user.name'], {
93
+ cwd,
94
+ timeout: 5000,
95
+ windowsHide: true
96
+ });
97
+ return normalizeGitDeveloper(stdout);
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+
68
103
  async function readExistingDeveloper(target) {
69
104
  const developerFile = join(target, '.cowork-flow', '.developer');
70
105
  if (!await pathExists(developerFile)) {
@@ -78,19 +113,30 @@ async function readExistingDeveloper(target) {
78
113
  async function resolveDeveloperName(options, prompt) {
79
114
  const existing = await readExistingDeveloper(options.target);
80
115
  if (existing) {
81
- return { existing: true, name: existing };
116
+ return { existing: true, name: existing, source: 'existing' };
82
117
  }
83
118
 
84
- if (options.dryRun && !options.developer) {
85
- return { existing: false, name: null };
119
+ if (options.developer) {
120
+ return { existing: false, name: normalizeDeveloperName(options.developer), source: 'flag' };
86
121
  }
87
122
 
88
- let name = options.developer;
89
- if (!name && typeof prompt === 'function') {
90
- name = await prompt('Developer name: ');
123
+ const gitName = await readGitDeveloper(options.target);
124
+
125
+ if (options.dryRun) {
126
+ return { existing: false, name: gitName, source: gitName ? 'git' : null };
91
127
  }
92
128
 
93
- return { existing: false, name: normalizeDeveloperName(name) };
129
+ if (typeof prompt === 'function') {
130
+ const answer = await prompt(`Developer name${gitName ? ` [${gitName}]` : ''}: `);
131
+ const typed = String(answer ?? '').trim();
132
+ return {
133
+ existing: false,
134
+ name: normalizeDeveloperName(typed || gitName),
135
+ source: typed ? 'prompt' : 'git'
136
+ };
137
+ }
138
+
139
+ return { existing: false, name: normalizeDeveloperName(gitName), source: 'git' };
94
140
  }
95
141
 
96
142
  async function resolvePlatforms(options, selectPlatforms) {
@@ -98,10 +144,17 @@ async function resolvePlatforms(options, selectPlatforms) {
98
144
  return parsePlatformSelectionOrUsage(options.platforms);
99
145
  }
100
146
 
147
+ // Reuse the same marker-based detection `project sync` uses, so init and sync
148
+ // agree on what "this project already uses" means.
149
+ const detected = await detectInstalledPlatforms(options.target);
150
+ if (detected.length > 0) {
151
+ return detected;
152
+ }
153
+
101
154
  let selected = null;
102
155
  if (typeof selectPlatforms === 'function') {
103
156
  selected = await selectPlatforms({
104
- message: 'Select platforms to set up',
157
+ message: 'Select hosts to set up',
105
158
  choices: SUPPORTED_PLATFORMS.map((platform) => ({
106
159
  label: platformLabel(platform),
107
160
  value: platform
@@ -156,13 +209,16 @@ export async function runInitWithOptions(
156
209
 
157
210
  await applyPlan(plan, { dryRun: options.dryRun, fileSystem });
158
211
  io.writeOut(summarizePlan(plan, options.dryRun));
159
- io.writeOut(`Platforms: ${formatPlatformList(platforms)}\n`);
212
+ io.writeOut(`Hosts: ${formatPlatformList(platforms)}\n`);
160
213
 
161
214
  if (options.dryRun) {
162
215
  if (developer.existing) {
163
216
  io.writeOut(`dry-run preserve-existing=.cowork-flow/.developer for developer ${developer.name}\n`);
164
217
  } else if (developer.name) {
165
- io.writeOut(`dry-run would-create=.cowork-flow/.developer for developer ${developer.name}\n`);
218
+ const source = developer.source === 'git' ? ' (from git user.name)' : '';
219
+ io.writeOut(`dry-run would-create=.cowork-flow/.developer for developer ${developer.name}${source}\n`);
220
+ } else {
221
+ io.writeOut('dry-run would-skip=.cowork-flow/.developer (no developer identity; pass --developer)\n');
166
222
  }
167
223
  io.writeOut('Next: update AGENTS.md and .cowork-flow/config.yaml for this project.\n');
168
224
  return 0;
@@ -171,7 +227,8 @@ export async function runInitWithOptions(
171
227
  if (developer.existing) {
172
228
  io.writeOut(`Developer already initialized: ${developer.name}\n`);
173
229
  } else {
174
- io.writeOut(`Developer initialized: ${developer.name}\n`);
230
+ const source = developer.source === 'git' ? ' (from git user.name)' : '';
231
+ io.writeOut(`Developer initialized: ${developer.name}${source}\n`);
175
232
  }
176
233
  io.writeOut('Next: update AGENTS.md and .cowork-flow/config.yaml for this project.\n');
177
234
  return 0;
@@ -35,7 +35,7 @@ const FLAG_HELP = {
35
35
  const flag = (name) => ({ name, kind: 'boolean', description: FLAG_HELP[name] });
36
36
  const valueFlag = (name, valueName) => ({ name, kind: 'value', valueName, description: FLAG_HELP[name] });
37
37
 
38
- const PLATFORM_TOKENS = `${hostRegistry.platformIds.join('|')}|all`;
38
+ const HOST_TOKENS = `${hostRegistry.platformIds.join('|')}|all`;
39
39
  const HOST_NOTE = `Hosts: ${Object.entries(HOST_COMPONENTS)
40
40
  .map(([id, host]) => `${id} (${Object.keys(host.components).join(', ')})`)
41
41
  .join('; ')}.`;
@@ -51,18 +51,19 @@ export const COMMANDS = [
51
51
  {
52
52
  path: ['project', 'init'],
53
53
  summary: 'Copy the cowork-flow template into a project',
54
- usage: `project init [target] --platform <${PLATFORM_TOKENS}> [--developer <name>] [--dry-run] [--force]`,
54
+ usage: `project init [target] --host <${HOST_TOKENS}> [--developer <name>] [--dry-run] [--force]`,
55
55
  flags: [
56
- { name: '--platform', kind: 'value', valueName: 'host', repeatable: true, description: 'Host to set the project up for; repeatable and comma-separated' },
57
- { name: '--platforms', kind: 'value', valueName: 'host', repeatable: true, description: 'Alias of --platform' },
58
- { name: '--developer', kind: 'value', valueName: 'name', description: 'Developer name recorded in .cowork-flow/.developer' },
56
+ { name: '--host', kind: 'value', valueName: 'host', repeatable: true, description: 'Host to set the project up for; repeatable and comma-separated, auto-detected when omitted' },
57
+ { name: '--platform', kind: 'value', valueName: 'host', repeatable: true, description: 'Alias of --host' },
58
+ { name: '--platforms', kind: 'value', valueName: 'host', repeatable: true, description: 'Alias of --host' },
59
+ { name: '--developer', kind: 'value', valueName: 'name', description: 'Developer name recorded in .cowork-flow/.developer; falls back to git user.name' },
59
60
  flag('--dry-run'),
60
61
  flag('--force')
61
62
  ],
62
63
  positional: { min: 0, max: 1, name: '[target]' },
63
64
  examples: [
64
- 'cwf project init . --platform codex --developer alice',
65
- 'cwf project init ./my-project --platform all --dry-run'
65
+ 'cwf project init --host codex --developer alice',
66
+ 'cwf project init ./my-project --host all --dry-run'
66
67
  ],
67
68
  run: (args, context) => runInit(args, context)
68
69
  },
@@ -43,6 +43,6 @@ export async function runSync(args, { io, fileSystem }) {
43
43
  const readiness = await buildReadinessReport(plan, { fileSystem });
44
44
  io.writeOut(formatReadinessReport(readiness));
45
45
  }
46
- io.writeOut(`Platforms: ${platforms.length > 0 ? formatPlatformList(platforms) : 'none'}\n`);
46
+ io.writeOut(`Hosts: ${platforms.length > 0 ? formatPlatformList(platforms) : 'none'}\n`);
47
47
  return 0;
48
48
  }
@@ -172,7 +172,7 @@ function selectionRules(index) {
172
172
  const platformId = aliases.get(token);
173
173
  if (!platformId) {
174
174
  throw new Error(
175
- `Unsupported platform: ${token}. Supported platforms: ${platformIds.join(', ')}`
175
+ `Unsupported host: ${token}. Supported hosts: ${platformIds.join(', ')}`
176
176
  );
177
177
  }
178
178
  selected.add(platformId);
@@ -180,7 +180,7 @@ function selectionRules(index) {
180
180
  }
181
181
  if (selected.size === 0) {
182
182
  throw new Error(
183
- `Platform selection required. Run: cwf project init <target> --platform ${platformIds.join('|')}`
183
+ `Host selection required. Run: cwf project init [target] --host ${platformIds.join('|')}`
184
184
  );
185
185
  }
186
186
  return platformIds.filter((platformId) => selected.has(platformId));
@@ -1 +1 @@
1
- 2.1.0
1
+ 2.3.0
@@ -33,6 +33,7 @@ translated heading would simply stop being read.
33
33
 
34
34
  ## 验证命令
35
35
  - `<command>` → <expected>
36
+ - 规范核对:逐条核对 `.cowork-flow/spec/<dev_type>/` 适用规范 → pass / not_applicable(附理由)
36
37
  ```
37
38
 
38
39
  ## Section requirements
@@ -44,7 +45,7 @@ translated heading would simply stop being read.
44
45
  | `## 被拒方案` | optional | required for brainstorming tasks, so a rejected option is not reconsidered during execution |
45
46
  | `## 关键假设` | optional | filled in for complex tasks to surface implicit constraints; not read by the runtime parser |
46
47
  | `## 范围边界` | optional | prevents scope creep |
47
- | `## 验证命令` | optional | filled in for complex tasks, as the basis for automated verification |
48
+ | `## 验证命令` | optional | filled in for complex tasks, as the basis for automated verification; when the changed files are covered by `.cowork-flow/spec/backend/` or `frontend/` rules, it must also carry the spec/gate check for the task's `dev_type` (deciding command, or clause-by-clause `pass` / `not_applicable` with reason) |
48
49
 
49
50
  ### Machine-readable line formats (for injection)
50
51
 
@@ -54,6 +55,11 @@ fact view (`services/fact_view.parse_decision_anchor`). Line-format conventions:
54
55
  - `## 验证命令`: one command per line (an optional `- ` prefix), truncated to 120
55
56
  characters, at most 10 lines; the implementing agent runs them itself
56
57
  (self-declared, self-checked) and the reviewer verifies the execution evidence.
58
+ When the changed files are covered by `.cowork-flow/spec/<dev_type>/` rules, the
59
+ section must also carry the spec/gate check for the task's `dev_type`: a deciding
60
+ command, or a `规范核对:` line reviewed clause by clause (`pass` /
61
+ `not_applicable` + reason). This is a strong review reminder, not a `spec-check`
62
+ hard gate.
57
63
  - `## 范围边界`: only the first non-empty line is taken, truncated to 160
58
64
  characters.
59
65
 
@@ -45,7 +45,7 @@ Start with a concise brief:
45
45
  | **Task Type** | Tiny / Normal / High-risk: <reason> |
46
46
  | **Strategy** | Serial / Parallel: <why> |
47
47
  | **Success Criteria** | AC-001 / AC-002 / AC-003 |
48
- | **Final Verification** | `<command>` -> exit 0; `<command>` -> exit 0 |
48
+ | **Final Verification** | `<command>` -> exit 0; clause-by-clause check against applicable `.cowork-flow/spec/<dev_type>/` rules and project gates -> `pass` / `not_applicable` per clause |
49
49
  | **Primary Risk** | Low / Medium / High: <one sentence> |
50
50
 
51
51
  ## Goal
@@ -116,6 +116,8 @@ Then list task briefs, followed by integrated verification and completion checks
116
116
  <command>
117
117
  ```
118
118
 
119
+ When changed files are covered by `.cowork-flow/spec/<dev_type>/` rules, add the deciding command or a spec/gate check line (`pass` / `not_applicable` with reason; format example in `decision-anchor.md`) here; it is a strong review reminder, not a hard gate.
120
+
119
121
  **Completion Conditions**
120
122
  - <2-4 checkable conditions>
121
123
 
@@ -128,7 +130,7 @@ Then list task briefs, followed by integrated verification and completion checks
128
130
 
129
131
  - For Tiny tasks, keep the brief short but still name exact file boundaries, completion conditions, and verification commands.
130
132
  - For behavior changes, include a failing test before implementation when behavior can be tested.
131
- - For implementation tasks whose changed files are covered by `.cowork-flow/spec/` requirements, name the covered spec files in the plan so review can check them clause by clause.
133
+ - For implementation tasks whose changed files are covered by `.cowork-flow/spec/` requirements, name the covered spec files in the plan and add the clause check to the task's `Verification Command` and the plan's `Final Verification`: run the deciding command when one exists, otherwise check clause by clause and record `pass` / `not_applicable` with reason. This is a strong review reminder, not a separate hard gate.
132
134
  - Do not add shallow tests just to satisfy process. Avoid tests that only assert existence, mirror implementation details, count mock calls without behavior, or snapshot empty structure.
133
135
  - For complex problems, test depth first: business invariants, cross-layer contracts, state transitions, error boundaries, and real regression paths before narrow unit cases.
134
136
  - Map behavior-changing tests to stable `decision-anchor.md` acceptance IDs when useful; do not create `tdd.jsonl` or write TDD evidence records into `check.jsonl`.
@@ -190,3 +192,4 @@ Before handoff:
190
192
  3. Confirm every Normal or High-risk task has file boundaries, key symbols, implementation notes, test proof, completion conditions, and prohibited drift.
191
193
  4. Check names, paths, command syntax, and expected outputs.
192
194
  5. Confirm the plan says to stop and report back when code facts conflict with the plan, a plan-external file becomes necessary, or a test fails for an unplanned reason.
195
+ 6. Confirm Final Verification carries the applicable `.cowork-flow/spec/<dev_type>/` clause check, or states why no spec file applies.