dream-wf 0.1.1 → 0.1.2

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,44 +1,93 @@
1
1
  # DREAM-WORKFLOW
2
2
 
3
- 面向 Cursor、Claude Code 和 OpenCode 的个人 Trellis workflow patch 安装器。
3
+ 面向 Codex、Claude Code、OpenCodeCursor Workflow patch 安装聚合器。
4
4
 
5
- `dream-wf` 不替代 Trellis。它是在 Trellis 之上安装一组项目级个人 workflow 约束:
5
+ `dream-wf` 不替代 Trellis。它是在 Trellis 之上安装一组项目级个人 workflow 约束,同时聚合配置 MCP servers 和 skills:
6
6
 
7
- - PRD 澄清自动采用 grill-me 风格,用户不需要显式提到 `dream-wf`。
8
- - Trellis 原生的任务生命周期、spec、hooks、skills、sub-agents、checks finish-work 保持不变。
9
- - 初始 spec 候选内容来自用户回答、PRD 决策和已验证的项目事实。
10
- - 代码语义检索优先使用 `fast-context-mcp`。
11
- - 外部文档和实时网络检索优先使用 `grok-search-mcp`。
12
- - strict 模式会阻止无活跃任务或 PRD 未确认时的实现类操作。
7
+ - 平台选择:Cursor / Claude Code / OpenCode / Codex
8
+ - Skill 安装:`dream-wf-grill-prd`(Trellis patch · grill-me 风格 PRD)、`dream-wf-mcp-policy`(MCP 优先级策略)
9
+ - MCP 配置:`fast-context-mcp`(代码语义检索)、`grok-search-mcp`(外部文档/实时网络检索)
10
+ - 交互式 TUI:上下选择、space 选中、enter 下一步/安装
11
+ - PRD 澄清自动采用 grill-me 风格,用户不需要显式提到 `dream-wf`
12
+ - Trellis 原生的任务生命周期、spec、hooks、skills、sub-agents、checks finish-work 保持不变
13
+ - strict 模式会阻止无活跃任务或 PRD 未确认时的实现类操作
13
14
 
14
15
  ## 安装
15
16
 
17
+ ### 交互式 TUI(推荐)
18
+
19
+ ```bash
20
+ npx dream-wf
21
+ ```
22
+
23
+ 无参数时自动进入 TUI:
24
+
25
+ 1. 第一步选择平台(claude code / codex / opencode / cursor)
26
+ 2. 第二步选择要安装的 skills(默认全选)
27
+ 3. 第三步选择要配置的 MCP servers(默认全选)
28
+ 4. enter 确认安装
29
+
30
+ 操作键:
31
+
32
+ - `↑/↓` 移动光标
33
+ - `space` 切换选中
34
+ - `a` 全选/全不选
35
+ - `enter` 下一步/确认
36
+ - `ctrl+c` 退出
37
+
38
+ ### 命令行模式
39
+
16
40
  ```bash
17
41
  npx dream-wf init -p cursor
18
42
  npx dream-wf init -p claude
19
43
  npx dream-wf init -p opencode
44
+ npx dream-wf init -p codex
20
45
  ```
21
46
 
22
- `-p` 是必填参数。默认安装范围是项目级,默认模式是 `strict`。
47
+ `-p` 是必填参数。默认安装范围是项目级,默认模式是 `strict`,默认安装全部 skills 和 MCPs。
23
48
 
24
49
  ## 命令
25
50
 
26
51
  ```bash
27
- npx dream-wf init -p cursor
28
- npx dream-wf doctor -p cursor
29
- npx dream-wf update -p cursor
52
+ npx dream-wf # 交互式 TUI
53
+ npx dream-wf interactive # 同上
54
+ npx dream-wf init -p <platform> [options]
55
+ npx dream-wf doctor -p <platform>
56
+ npx dream-wf update -p <platform>
30
57
  ```
31
58
 
32
59
  参数:
33
60
 
34
61
  ```bash
35
- -p cursor|claude|opencode
36
- --mode strict|advisory
37
- --install-deps
38
- --developer <name>
62
+ -p cursor|claude|opencode|codex # 必填
63
+ --mode strict|advisory # 默认 strict
64
+ --skills <id,id,...> # 指定 skill id,默认全部
65
+ --mcps <id,id,...> # 指定 mcp id,默认全部
66
+ --skip-skills # 不安装任何 skill
67
+ --skip-mcps # 不配置任何 mcp
68
+ --install-deps --developer <name> # 自动初始化 Trellis
39
69
  ```
40
70
 
41
- ## Trellis 依赖
71
+ Skill ids:
72
+
73
+ - `trellis-dream-wf-patch`(dream-wf-grill-prd)
74
+ - `dream-wf-mcp-policy`
75
+
76
+ MCP ids:
77
+
78
+ - `fast-context`(fast-context-mcp)
79
+ - `grok-search`(grok-search-mcp)
80
+
81
+ ## 平台支持
82
+
83
+ | 平台 | 入口规则 | Skills 目录 | Hook 类型 | MCP 配置文件 |
84
+ |------|---------|------------|----------|-------------|
85
+ | Cursor | `.cursor/rules/dream-wf.mdc` | `.cursor/skills/` | `preToolUse` (python) | `.cursor/mcp.json` |
86
+ | Claude Code | `CLAUDE.md` | `.claude/skills/` | `PreToolUse` (python) | `.mcp.json` |
87
+ | OpenCode | `AGENTS.md` | `.opencode/skills/` | `tool.execute.before` plugin (js) | `opencode.json` |
88
+ | Codex | `AGENTS.md` | `.codex/skills/` | `PreToolUse` (python, hooks.json) | `.codex/config.toml` |
89
+
90
+ ## Trellis
42
91
 
43
92
  来源:https://github.com/mindfold-ai/trellis
44
93
 
@@ -49,12 +98,13 @@ npm install -g @mindfoldhq/trellis@latest
49
98
  先初始化 Trellis,或者让 `dream-wf` 输出对应的初始化命令:
50
99
 
51
100
  ```bash
52
- trellis init -u your-name --cursor
53
- trellis init -u your-name --claude
54
- trellis init -u your-name --opencode
101
+ trellis init -u your-name --cursor --yes
102
+ trellis init -u your-name --claude --yes
103
+ trellis init -u your-name --opencode --yes
104
+ trellis init -u your-name --codex --yes
55
105
  ```
56
106
 
57
- ## Grill Me 依赖
107
+ ## Grill Me
58
108
 
59
109
  来源:https://github.com/mattpocock/skills/blob/main/skills/productivity/grill-me/SKILL.md
60
110
 
@@ -119,8 +169,6 @@ MCP server 配置示例:
119
169
  }
120
170
  ```
121
171
 
122
- 不要把真实 API key、token 或 MCP secret 提交到项目文件中。
123
-
124
172
  ## 生成文件
125
173
 
126
174
  Cursor:
@@ -130,6 +178,7 @@ Cursor:
130
178
  - `.cursor/skills/dream-wf-mcp-policy/SKILL.md`
131
179
  - `.cursor/hooks/dream-wf-guard.py`
132
180
  - `.cursor/hooks.json`
181
+ - `.cursor/mcp.json`
133
182
 
134
183
  Claude Code:
135
184
 
@@ -138,6 +187,7 @@ Claude Code:
138
187
  - `.claude/skills/dream-wf-mcp-policy/SKILL.md`
139
188
  - `.claude/hooks/dream-wf-guard.py`
140
189
  - `.claude/settings.json`
190
+ - `.mcp.json`
141
191
 
142
192
  OpenCode:
143
193
 
@@ -145,6 +195,16 @@ OpenCode:
145
195
  - `.opencode/skills/dream-wf-grill-prd/SKILL.md`
146
196
  - `.opencode/skills/dream-wf-mcp-policy/SKILL.md`
147
197
  - `.opencode/plugins/dream-wf-guard.js`
198
+ - `opencode.json`
199
+
200
+ Codex:
201
+
202
+ - `AGENTS.md` dream-wf entry block
203
+ - `.codex/skills/dream-wf-grill-prd/SKILL.md`
204
+ - `.codex/skills/dream-wf-mcp-policy/SKILL.md`
205
+ - `.codex/hooks/dream-wf-guard.py`
206
+ - `.codex/hooks.json`
207
+ - `.codex/config.toml`(含 `[features] hooks = true` 和 `[mcp_servers.*]`)
148
208
 
149
209
  Trellis:
150
210
 
@@ -173,6 +233,13 @@ status: confirmed
173
233
 
174
234
  ```bash
175
235
  npx dream-wf doctor -p cursor
236
+ npx dream-wf doctor -p codex
176
237
  ```
177
238
 
178
- Doctor 会检查明显的 MCP secret 模式,并提示可疑内容。
239
+ doctor 会检查:
240
+
241
+ - 必需二进制(node、python3、trellis、uvx)
242
+ - Trellis 项目目录和 workflow.md
243
+ - 平台对应的规则、skills、hook 文件
244
+ - MCP 配置文件存在性和默认 MCP 条目完整性
245
+ - 项目文件中的密钥泄露扫描
@@ -12,6 +12,7 @@ Use grill-me behavior during Trellis planning. This replaces only the interview
12
12
  - If a question can be answered by exploring the codebase, docs, config, existing specs, or task history, explore first instead of asking the user.
13
13
  - After each answer, update `prd.md` immediately.
14
14
  - Keep open questions explicit until they are answered or intentionally deferred.
15
+ - **Before requesting PRD confirmation, perform a Knowledge Verification pass.** Use `grok-search-mcp` (`web_search`, `web_fetch`) to verify technical assumptions that could be outdated or wrong. Record results in the `## Knowledge Verification` section of `prd.md`. Correct any outdated assumptions. Move unverified points to `Open Questions`. Add `knowledge verified` to the PRD after verification is complete.
15
16
  - Do not start implementation until the PRD is reviewed and confirmed.
16
17
 
17
18
  ## Required PRD Sections
@@ -23,6 +24,7 @@ Use grill-me behavior during Trellis planning. This replaces only the interview
23
24
  - Acceptance criteria
24
25
  - Decisions
25
26
  - Technical notes
27
+ - Knowledge verification
26
28
  - Open questions
27
29
  - Spec candidates
28
30
 
@@ -24,6 +24,7 @@ Keep the native Trellis flow:
24
24
  ## Dream WF Patch Points
25
25
 
26
26
  - Use `dream-wf-grill-prd` for PRD clarification instead of open-ended brainstorm interviewing.
27
+ - **Before PRD confirmation, perform a Knowledge Verification pass**: use `grok-search-mcp` (`web_search`, `web_fetch`) to verify technical assumptions that could be outdated or wrong. Record results in the `## Knowledge Verification` section of `prd.md`. Correct outdated assumptions. Add `knowledge verified` to the PRD after verification is complete.
27
28
  - Generate initial spec candidates from user answers, PRD decisions, and verified project facts.
28
29
  - Prefer `fast-context-mcp` for codebase semantic search.
29
30
  - Prefer `grok-search-mcp` for external docs, live technical information, and webpage fetching.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "dream-wf",
3
- "version": "0.1.1",
4
- "description": "Personal Trellis workflow patch installer for Cursor, Claude Code, and OpenCode.",
3
+ "version": "0.1.2",
4
+ "description": "Trellis workflow patch installer and MCP/skill aggregator for Cursor, Claude Code, OpenCode and Codex.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "dream-wf": "bin/dream-wf.js"
8
8
  },
9
9
  "scripts": {
10
- "check": "node --check bin/dream-wf.js && node --check src/cli/index.js && node --check src/lib/files.js && node --check src/lib/platforms.js && node --check src/lib/trellis.js && node --check src/lib/json.js && node --check src/doctor/index.js && node --check src/deps/index.js && node --check src/platforms/shared.js && node --check src/platforms/cursor/index.js && node --check src/platforms/claude-code/index.js && node --check src/platforms/opencode/index.js && node --check templates/hooks/opencode/dream-wf-guard.js"
10
+ "check": "node --check bin/dream-wf.js && node --check src/cli/index.js && node --check src/lib/files.js && node --check src/lib/platforms.js && node --check src/lib/trellis.js && node --check src/lib/json.js && node --check src/lib/catalog.js && node --check src/lib/mcp.js && node --check src/doctor/index.js && node --check src/deps/index.js && node --check src/platforms/shared.js && node --check src/platforms/cursor/index.js && node --check src/platforms/claude-code/index.js && node --check src/platforms/opencode/index.js && node --check src/platforms/codex/index.js && node --check src/tui/index.js && node --check templates/hooks/opencode/dream-wf-guard.js"
11
11
  },
12
12
  "keywords": [
13
13
  "trellis",
@@ -15,6 +15,7 @@
15
15
  "cursor",
16
16
  "claude",
17
17
  "opencode",
18
+ "codex",
18
19
  "mcp"
19
20
  ],
20
21
  "license": "MIT",
package/src/cli/index.js CHANGED
@@ -1,91 +1,157 @@
1
- import process from 'node:process';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- import { assertSupportedPlatform, normalizePlatform } from '../lib/platforms.js';
5
- import { ensureTrellisInitialized, installTrellisProfile } from '../lib/trellis.js';
6
- import { formatRelative } from '../lib/files.js';
7
- import { installCursor } from '../platforms/cursor/index.js';
8
- import { installClaudeCode } from '../platforms/claude-code/index.js';
9
- import { installOpenCode } from '../platforms/opencode/index.js';
10
- import { runDoctor, formatDoctorReport } from '../doctor/index.js';
11
-
12
- const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
1
+ import process from "node:process";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import {
5
+ assertSupportedPlatform,
6
+ normalizePlatform,
7
+ } from "../lib/platforms.js";
8
+ import {
9
+ ensureTrellisInitialized,
10
+ installTrellisProfile,
11
+ } from "../lib/trellis.js";
12
+ import { formatRelative } from "../lib/files.js";
13
+ import {
14
+ resolveSkills,
15
+ resolveMcps,
16
+ defaultSkillIds,
17
+ defaultMcpIds,
18
+ } from "../lib/catalog.js";
19
+ import { installCursor } from "../platforms/cursor/index.js";
20
+ import { installClaudeCode } from "../platforms/claude-code/index.js";
21
+ import { installOpenCode } from "../platforms/opencode/index.js";
22
+ import { installCodex } from "../platforms/codex/index.js";
23
+ import { runDoctor, formatDoctorReport } from "../doctor/index.js";
24
+ import { runInteractive } from "../tui/index.js";
25
+
26
+ const packageRoot = path.resolve(
27
+ path.dirname(fileURLToPath(import.meta.url)),
28
+ "..",
29
+ "..",
30
+ );
13
31
 
14
32
  export async function run(argv) {
33
+ // 无参数或仅 --help 以外无 subcommand 时,进入交互式 TUI。
34
+ if (argv.length === 0) {
35
+ const interactive = await runInteractive();
36
+ if (!interactive) {
37
+ return;
38
+ }
39
+ await init(process.cwd(), interactive);
40
+ return;
41
+ }
42
+
43
+ // 优先处理全局 help 标志,避免被当作 command 或要求 -p。
44
+ if (argv.includes("--help") || argv.includes("-h")) {
45
+ writeOutput(helpText());
46
+ return;
47
+ }
48
+
15
49
  const { command, options } = parseArgs(argv);
16
50
 
17
- if (options.help || command === 'help') {
51
+ if (!command || command === "help") {
18
52
  writeOutput(helpText());
19
53
  return;
20
54
  }
21
55
 
22
- if (!command) {
23
- throw new Error(`Missing command.\n\n${helpText()}`);
56
+ if (command === "interactive" || command === "tui") {
57
+ const interactive = await runInteractive();
58
+ if (!interactive) {
59
+ return;
60
+ }
61
+ await init(process.cwd(), interactive);
62
+ return;
24
63
  }
25
64
 
26
65
  const platform = normalizePlatform(options.platform);
27
66
  assertSupportedPlatform(platform);
28
67
 
29
68
  const rootDir = process.cwd();
30
- const mode = options.mode ?? 'strict';
31
- if (!['strict', 'advisory'].includes(mode)) {
32
- throw new Error('Invalid --mode. Use strict or advisory.');
69
+ const mode = options.mode ?? "strict";
70
+ if (!["strict", "advisory"].includes(mode)) {
71
+ throw new Error("Invalid --mode. Use strict or advisory.");
33
72
  }
34
73
 
35
- if (command === 'init') {
74
+ if (command === "init") {
36
75
  await init(rootDir, { ...options, platform, mode });
37
76
  return;
38
77
  }
39
78
 
40
- if (command === 'doctor') {
79
+ if (command === "doctor") {
41
80
  const report = await runDoctor(rootDir, platform);
42
81
  writeOutput(formatDoctorReport(report));
43
82
  return;
44
83
  }
45
84
 
46
- if (command === 'update') {
85
+ if (command === "update") {
47
86
  await init(rootDir, { ...options, platform, mode });
48
87
  return;
49
88
  }
50
89
 
51
- if (command === 'uninstall') {
52
- throw new Error('uninstall is planned but not implemented in this MVP. Remove dream-wf generated files manually if needed.');
90
+ if (command === "uninstall") {
91
+ throw new Error(
92
+ "uninstall is planned but not implemented in this MVP. Remove dream-wf generated files manually if needed.",
93
+ );
53
94
  }
54
95
 
55
96
  throw new Error(`Unknown command "${command}".\n\n${helpText()}`);
56
97
  }
57
98
 
58
99
  async function init(rootDir, options) {
100
+ // 来自 TUI 的 options 已带 skills/mcps;来自 CLI 的 options 需要解析。
101
+ const platform = options.platform;
102
+ const mode = options.mode ?? "strict";
103
+
104
+ const skillIds = options.skillIds ?? defaultSkillIds();
105
+ const mcpIds = options.mcpIds ?? defaultMcpIds();
106
+ const skills = resolveSkills(
107
+ options.skills ? options.skills.map((s) => s.id) : skillIds,
108
+ );
109
+ const mcps = resolveMcps(
110
+ options.mcps ? options.mcps.map((m) => m.id) : mcpIds,
111
+ );
112
+
113
+ const initOptions = { ...options, platform, mode, skills, mcps };
114
+
59
115
  writeOutput(formatBanner());
60
116
 
61
117
  const results = [];
62
- const trellis = await ensureTrellisInitialized(rootDir, options);
118
+ const trellis = await ensureTrellisInitialized(rootDir, initOptions);
63
119
 
64
120
  if (!trellis.initialized) {
65
- writeOutput([
66
- 'Trellis is not initialized in this project.',
67
- `Run: ${trellis.initCommand}`,
68
- 'Then rerun dream-wf init.'
69
- ].join('\n'));
121
+ writeOutput(
122
+ [
123
+ "Trellis is not initialized in this project.",
124
+ `Run: ${trellis.initCommand}`,
125
+ "Then rerun dream-wf init.",
126
+ ].join("\n"),
127
+ );
70
128
  return;
71
129
  }
72
130
 
73
131
  results.push(await installTrellisProfile(rootDir));
74
132
 
75
- if (options.platform === 'cursor') {
76
- results.push(...await installCursor(packageRoot, rootDir, options));
133
+ if (platform === "cursor") {
134
+ results.push(...(await installCursor(packageRoot, rootDir, initOptions)));
135
+ }
136
+
137
+ if (platform === "claude") {
138
+ results.push(
139
+ ...(await installClaudeCode(packageRoot, rootDir, initOptions)),
140
+ );
77
141
  }
78
142
 
79
- if (options.platform === 'claude') {
80
- results.push(...await installClaudeCode(packageRoot, rootDir, options));
143
+ if (platform === "opencode") {
144
+ results.push(...(await installOpenCode(packageRoot, rootDir, initOptions)));
81
145
  }
82
146
 
83
- if (options.platform === 'opencode') {
84
- results.push(...await installOpenCode(packageRoot, rootDir, options));
147
+ if (platform === "codex") {
148
+ results.push(...(await installCodex(packageRoot, rootDir, initOptions)));
85
149
  }
86
150
 
87
- const report = await runDoctor(rootDir, options.platform);
88
- writeOutput(`${formatInstallReport(rootDir, results)}\n\n${formatDoctorReport(report)}`);
151
+ const report = await runDoctor(rootDir, platform);
152
+ writeOutput(
153
+ `${formatInstallReport(rootDir, results)}\n\n${formatDoctorReport(report)}`,
154
+ );
89
155
  }
90
156
 
91
157
  function parseArgs(argv) {
@@ -95,55 +161,85 @@ function parseArgs(argv) {
95
161
  for (let index = 0; index < rest.length; index += 1) {
96
162
  const arg = rest[index];
97
163
 
98
- if (arg === '--help' || arg === '-h') {
164
+ if (arg === "--help" || arg === "-h") {
99
165
  options.help = true;
100
166
  continue;
101
167
  }
102
168
 
103
- if (arg === '--install-deps') {
169
+ if (arg === "--install-deps") {
104
170
  options.installDeps = true;
105
171
  continue;
106
172
  }
107
173
 
108
- if (arg === '--skip-deps') {
174
+ if (arg === "--skip-deps") {
109
175
  options.installDeps = false;
110
176
  continue;
111
177
  }
112
178
 
113
- if (arg === '--yes' || arg === '-y') {
179
+ if (arg === "--yes" || arg === "-y") {
114
180
  options.yes = true;
115
181
  continue;
116
182
  }
117
183
 
118
- if (arg === '-p') {
184
+ if (arg === "--skip-skills") {
185
+ options.skillIds = [];
186
+ continue;
187
+ }
188
+
189
+ if (arg === "--skip-mcps") {
190
+ options.mcpIds = [];
191
+ continue;
192
+ }
193
+
194
+ if (arg === "-p") {
119
195
  const value = rest[index + 1];
120
- if (!value || value.startsWith('-')) {
121
- throw new Error('Missing value for -p. Use -p <cursor|claude|opencode>.');
196
+ if (!value || value.startsWith("-")) {
197
+ throw new Error(
198
+ "Missing value for -p. Use -p <cursor|claude|opencode|codex>.",
199
+ );
122
200
  }
123
201
  options.platform = value;
124
202
  index += 1;
125
203
  continue;
126
204
  }
127
205
 
128
- if (arg === '--mode') {
206
+ if (arg === "--mode") {
129
207
  options.mode = readOptionValue(arg, rest, index);
130
208
  index += 1;
131
209
  continue;
132
210
  }
133
211
 
134
- if (arg.startsWith('--mode=')) {
135
- options.mode = arg.slice('--mode='.length);
212
+ if (arg.startsWith("--mode=")) {
213
+ options.mode = arg.slice("--mode=".length);
136
214
  continue;
137
215
  }
138
216
 
139
- if (arg === '--developer') {
217
+ if (arg === "--developer") {
140
218
  options.developer = readOptionValue(arg, rest, index);
141
219
  index += 1;
142
220
  continue;
143
221
  }
144
222
 
145
- if (arg.startsWith('--developer=')) {
146
- options.developer = arg.slice('--developer='.length);
223
+ if (arg.startsWith("--developer=")) {
224
+ options.developer = arg.slice("--developer=".length);
225
+ continue;
226
+ }
227
+
228
+ if (arg === "--skills") {
229
+ options.skillIds = readOptionValue(arg, rest, index)
230
+ .split(",")
231
+ .map((s) => s.trim())
232
+ .filter(Boolean);
233
+ index += 1;
234
+ continue;
235
+ }
236
+
237
+ if (arg === "--mcps") {
238
+ options.mcpIds = readOptionValue(arg, rest, index)
239
+ .split(",")
240
+ .map((s) => s.trim())
241
+ .filter(Boolean);
242
+ index += 1;
147
243
  continue;
148
244
  }
149
245
 
@@ -155,19 +251,21 @@ function parseArgs(argv) {
155
251
 
156
252
  function readOptionValue(arg, rest, index) {
157
253
  const value = rest[index + 1];
158
- if (!value || value.startsWith('-')) {
254
+ if (!value || value.startsWith("-")) {
159
255
  throw new Error(`Missing value for ${arg}.`);
160
256
  }
161
257
  return value;
162
258
  }
163
259
 
164
260
  function formatInstallReport(rootDir, results) {
165
- const lines = ['dream-wf install report:'];
261
+ const lines = ["dream-wf install report:"];
166
262
  for (const result of results.flat().filter(Boolean)) {
167
- const suffix = result.reason ? ` (${result.reason})` : '';
168
- lines.push(`- ${result.action}: ${formatRelative(rootDir, result.path)}${suffix}`);
263
+ const suffix = result.reason ? ` (${result.reason})` : "";
264
+ lines.push(
265
+ `- ${result.action}: ${formatRelative(rootDir, result.path)}${suffix}`,
266
+ );
169
267
  }
170
- return lines.join('\n');
268
+ return lines.join("\n");
171
269
  }
172
270
 
173
271
  function writeOutput(message) {
@@ -176,15 +274,14 @@ function writeOutput(message) {
176
274
 
177
275
  function formatBanner() {
178
276
  const banner = [
179
- '██████╗ ██████╗ ███████╗ █████╗ ███╗ ███╗',
180
- '██╔══██╗██╔══██╗██╔════╝██╔══██╗████╗ ████║',
181
- '██║ ██║██████╔╝█████╗ ███████║██╔████╔██║',
182
- '██║ ██║██╔══██╗██╔══╝ ██╔══██║██║╚██╔╝██║',
183
- '██████╔╝██║ ██║███████╗██║ ██║██║ ╚═╝ ██║',
184
- '╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝',
185
- '',
186
- ' Dream WF · Trellis workflow profile installer'
187
- ].join('\n');
277
+ "███████╗ ███████╗ ███████╗ ███████╗ ███╗ ███╗",
278
+ "██╔═══██╗ ██╔═══██╗ ██╔═════╝ ██╔═══██╗ ████╗ ████║",
279
+ "██║ ██║██████╔═╝ ███████╗ ███████╔╝ ██╔████╔██║",
280
+ "██║ ██║██╔═══██╗ ██╔════╝ ██╔═══██╗ ██║╚██╔╝██║",
281
+ "███████╔╝ ██║ ██║ ███████╗ ██║ ██║ ██║ ╚═╝ ██║",
282
+ "╚══════╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝",
283
+ " Dream WorkFlow v0.1.2",
284
+ ].join("\n");
188
285
 
189
286
  if (!process.stdout.isTTY || process.env.NO_COLOR) {
190
287
  return banner;
@@ -194,5 +291,35 @@ function formatBanner() {
194
291
  }
195
292
 
196
293
  function helpText() {
197
- return `dream-wf\n\nUsage:\n dream-wf init -p <cursor|claude|opencode> [--mode strict|advisory] [--install-deps --developer <name>]\n dream-wf doctor -p <cursor|claude|opencode>\n dream-wf update -p <cursor|claude|opencode>\n\nDefaults:\n --mode strict\n project-level install\n\nExamples:\n npx dream-wf init -p cursor\n npx dream-wf init -p claude --install-deps --developer ashe\n npx dream-wf doctor -p opencode`;
294
+ return [
295
+ "dream-wf v0.1.2 · Trellis workflow 安装聚合器",
296
+ "",
297
+ "Usage:",
298
+ " dream-wf # 交互式 TUI(推荐)",
299
+ " dream-wf interactive # 同上",
300
+ " dream-wf init -p <cursor|claude|opencode|codex> [options]",
301
+ " dream-wf doctor -p <cursor|claude|opencode|codex>",
302
+ " dream-wf update -p <cursor|claude|opencode|codex>",
303
+ "",
304
+ "Options:",
305
+ " -p <platform> cursor|claude|opencode|codex",
306
+ " --mode strict|advisory 默认 strict",
307
+ " --skills <id,id,...> 指定要安装的 skill id(默认全部)",
308
+ " --mcps <id,id,...> 指定要配置的 mcp id(默认全部)",
309
+ " --skip-skills 不安装任何 skill",
310
+ " --skip-mcps 不配置任何 mcp",
311
+ " --install-deps --developer <n> 自动初始化 Trellis",
312
+ "",
313
+ "Skill ids:",
314
+ " trellis-dream-wf-patch, dream-wf-mcp-policy",
315
+ "",
316
+ "MCP ids:",
317
+ " fast-context, grok-search",
318
+ "",
319
+ "Examples:",
320
+ " npx dream-wf",
321
+ " npx dream-wf init -p cursor",
322
+ " npx dream-wf init -p claude --skills trellis-dream-wf-patch --mcps fast-context",
323
+ " npx dream-wf doctor -p codex",
324
+ ].join("\n");
198
325
  }