cowork-flow 2.2.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 +16 -1
- package/README.en.md +15 -7
- package/README.md +5 -5
- package/docs/direction.md +1 -1
- package/docs/hosts.md +3 -1
- package/package.json +1 -1
- package/presets/claude-code/.claude-plugin/plugin.json +1 -1
- package/presets/claude-code/skills/cowork-flow-bootstrap/SKILL.md +4 -2
- package/presets/codex/.codex-plugin/plugin.json +1 -1
- package/presets/codex/skills/cowork-flow-bootstrap/SKILL.md +4 -2
- package/presets/kimi-code/.kimi-plugin/plugin.json +1 -1
- package/presets/kimi-code/skills/cowork-flow-bootstrap/SKILL.md +4 -2
- package/presets/opencode/cowork-flow/skills/cowork-flow-bootstrap/SKILL.md +4 -2
- package/presets/qoder/.qoder-plugin/plugin.json +1 -1
- package/presets/qoder/skills/cowork-flow-bootstrap/SKILL.md +4 -2
- package/presets/zcode/.zcode-plugin/plugin.json +1 -1
- package/presets/zcode/skills/cowork-flow-bootstrap/SKILL.md +4 -2
- package/src/cli.js +1 -1
- package/src/commands/init.js +72 -15
- package/src/commands/registry.js +8 -7
- package/src/commands/sync.js +1 -1
- package/src/lib/host-assets.js +2 -2
- package/template/.cowork-flow/.version +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@
|
|
|
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
|
+
|
|
7
21
|
## [2.2.0] - 2026-10-20
|
|
8
22
|
|
|
9
23
|
### Changed
|
|
@@ -393,7 +407,8 @@
|
|
|
393
407
|
|
|
394
408
|
此版本未保留本地条目正文,详情见 [GitHub Release](https://github.com/fishlikewater/cowork-flow/releases/tag/v0.0.47)。
|
|
395
409
|
|
|
396
|
-
[Unreleased]: https://github.com/fishlikewater/cowork-flow/compare/v2.
|
|
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
|
|
397
412
|
[2.2.0]: https://github.com/fishlikewater/cowork-flow/compare/v2.1.0...v2.2.0
|
|
398
413
|
[2.1.0]: https://github.com/fishlikewater/cowork-flow/compare/v2.0.0...v2.1.0
|
|
399
414
|
[2.0.0]: https://github.com/fishlikewater/cowork-flow/compare/v1.9.2...v2.0.0
|
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
|
|
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
|
|
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
|
-
|
|
67
|
-
|
|
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.
|
|
71
|
-
|
|
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] --
|
|
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
|
|
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
|
|
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
|
-
|
|
60
|
+
命令默认在当前目录初始化;新项目把目标目录作为位置参数传入,例如 `cwf project init ./my-project --host codex`,再进入该目录。省略 `--host` 时,会先按项目里已有的宿主标记(如 `.codex/`、`.claude/`)自动判定;没有标记的交互终端会让你选择,非交互环境则需要显式传入。只有确实会在多个宿主中使用时,才用 `--host all`。
|
|
61
61
|
|
|
62
|
-
初始化会加入 `AGENTS.md`、`.cowork-flow/`
|
|
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] --
|
|
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
|
-
-
|
|
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
|
|
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
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
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('
|
|
152
|
+
reject(new Error('Host selection cancelled'));
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
if (action?.outcome === 'confirm') {
|
package/src/commands/init.js
CHANGED
|
@@ -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: [
|
|
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
|
|
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.
|
|
85
|
-
return { existing: false, name:
|
|
119
|
+
if (options.developer) {
|
|
120
|
+
return { existing: false, name: normalizeDeveloperName(options.developer), source: 'flag' };
|
|
86
121
|
}
|
|
87
122
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
|
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(`
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
package/src/commands/registry.js
CHANGED
|
@@ -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
|
|
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] --
|
|
54
|
+
usage: `project init [target] --host <${HOST_TOKENS}> [--developer <name>] [--dry-run] [--force]`,
|
|
55
55
|
flags: [
|
|
56
|
-
{ name: '--
|
|
57
|
-
{ name: '--
|
|
58
|
-
{ name: '--
|
|
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
|
|
65
|
-
'cwf project init ./my-project --
|
|
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
|
},
|
package/src/commands/sync.js
CHANGED
|
@@ -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(`
|
|
46
|
+
io.writeOut(`Hosts: ${platforms.length > 0 ? formatPlatformList(platforms) : 'none'}\n`);
|
|
47
47
|
return 0;
|
|
48
48
|
}
|
package/src/lib/host-assets.js
CHANGED
|
@@ -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
|
|
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
|
-
`
|
|
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
|
+
2.3.0
|