dream-wf 0.1.0 → 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 +122 -55
- package/core/grill-prd-policy.md +2 -0
- package/core/workflow-profile.md +1 -0
- package/package.json +4 -3
- package/src/cli/index.js +192 -65
- package/src/deps/index.js +50 -0
- package/src/lib/catalog.js +82 -0
- package/src/lib/mcp.js +305 -0
- package/src/lib/platforms.js +13 -3
- package/src/lib/trellis.js +25 -2
- package/src/platforms/claude-code/index.js +7 -3
- package/src/platforms/codex/index.js +83 -0
- package/src/platforms/cursor/index.js +7 -3
- package/src/platforms/opencode/index.js +7 -3
- package/src/platforms/shared.js +11 -0
- package/src/tui/index.js +445 -0
- package/templates/hooks/claude-code/__pycache__/dream-wf-guard.cpython-314.pyc +0 -0
- package/templates/hooks/claude-code/dream-wf-guard.py +33 -11
- package/templates/hooks/codex/__pycache__/dream-wf-guard.cpython-314.pyc +0 -0
- package/templates/hooks/codex/dream-wf-guard.py +150 -0
- package/templates/hooks/cursor/__pycache__/dream-wf-guard.cpython-314.pyc +0 -0
- package/templates/hooks/cursor/dream-wf-guard.py +30 -11
- package/templates/hooks/opencode/dream-wf-guard.js +28 -10
- package/templates/rules/claude-code/dream-wf-block.md +20 -0
- package/templates/rules/codex/dream-wf-block.md +43 -0
- package/templates/rules/cursor/dream-wf.mdc +20 -1
- package/templates/rules/opencode/dream-wf-block.md +20 -0
- package/templates/skills/dream-wf-grill-prd/SKILL.md +54 -2
- package/templates/spec/guides/dream-wf-mcp-policy.md +6 -0
- package/templates/spec/guides/dream-wf-prd-policy.md +17 -0
package/src/cli/index.js
CHANGED
|
@@ -1,91 +1,157 @@
|
|
|
1
|
-
import process from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 (
|
|
51
|
+
if (!command || command === "help") {
|
|
18
52
|
writeOutput(helpText());
|
|
19
53
|
return;
|
|
20
54
|
}
|
|
21
55
|
|
|
22
|
-
if (
|
|
23
|
-
|
|
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 ??
|
|
31
|
-
if (![
|
|
32
|
-
throw new Error(
|
|
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 ===
|
|
74
|
+
if (command === "init") {
|
|
36
75
|
await init(rootDir, { ...options, platform, mode });
|
|
37
76
|
return;
|
|
38
77
|
}
|
|
39
78
|
|
|
40
|
-
if (command ===
|
|
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 ===
|
|
85
|
+
if (command === "update") {
|
|
47
86
|
await init(rootDir, { ...options, platform, mode });
|
|
48
87
|
return;
|
|
49
88
|
}
|
|
50
89
|
|
|
51
|
-
if (command ===
|
|
52
|
-
throw new Error(
|
|
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,
|
|
118
|
+
const trellis = await ensureTrellisInitialized(rootDir, initOptions);
|
|
63
119
|
|
|
64
120
|
if (!trellis.initialized) {
|
|
65
|
-
writeOutput(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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 (
|
|
76
|
-
results.push(...await installCursor(packageRoot, rootDir,
|
|
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 (
|
|
80
|
-
results.push(...await
|
|
143
|
+
if (platform === "opencode") {
|
|
144
|
+
results.push(...(await installOpenCode(packageRoot, rootDir, initOptions)));
|
|
81
145
|
}
|
|
82
146
|
|
|
83
|
-
if (
|
|
84
|
-
results.push(...await
|
|
147
|
+
if (platform === "codex") {
|
|
148
|
+
results.push(...(await installCodex(packageRoot, rootDir, initOptions)));
|
|
85
149
|
}
|
|
86
150
|
|
|
87
|
-
const report = await runDoctor(rootDir,
|
|
88
|
-
writeOutput(
|
|
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 ===
|
|
164
|
+
if (arg === "--help" || arg === "-h") {
|
|
99
165
|
options.help = true;
|
|
100
166
|
continue;
|
|
101
167
|
}
|
|
102
168
|
|
|
103
|
-
if (arg ===
|
|
169
|
+
if (arg === "--install-deps") {
|
|
104
170
|
options.installDeps = true;
|
|
105
171
|
continue;
|
|
106
172
|
}
|
|
107
173
|
|
|
108
|
-
if (arg ===
|
|
174
|
+
if (arg === "--skip-deps") {
|
|
109
175
|
options.installDeps = false;
|
|
110
176
|
continue;
|
|
111
177
|
}
|
|
112
178
|
|
|
113
|
-
if (arg ===
|
|
179
|
+
if (arg === "--yes" || arg === "-y") {
|
|
114
180
|
options.yes = true;
|
|
115
181
|
continue;
|
|
116
182
|
}
|
|
117
183
|
|
|
118
|
-
if (arg ===
|
|
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(
|
|
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 ===
|
|
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(
|
|
135
|
-
options.mode = arg.slice(
|
|
212
|
+
if (arg.startsWith("--mode=")) {
|
|
213
|
+
options.mode = arg.slice("--mode=".length);
|
|
136
214
|
continue;
|
|
137
215
|
}
|
|
138
216
|
|
|
139
|
-
if (arg ===
|
|
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(
|
|
146
|
-
options.developer = arg.slice(
|
|
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 = [
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
}
|
package/src/deps/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { pathExists, readTextIfExists } from '../lib/files.js';
|
|
4
4
|
import { commandExists } from '../lib/trellis.js';
|
|
5
|
+
import { readMcpServers, mcpConfigExists } from '../lib/mcp.js';
|
|
6
|
+
import { MCP_CATALOG } from '../lib/catalog.js';
|
|
5
7
|
|
|
6
8
|
export async function checkDependencies(rootDir, platform) {
|
|
7
9
|
const checks = [];
|
|
@@ -18,18 +20,31 @@ export async function checkDependencies(rootDir, platform) {
|
|
|
18
20
|
checks.push(await fileCheck(path.join(rootDir, '.cursor', 'rules', 'dream-wf.mdc'), 'Cursor dream-wf always-on rule'));
|
|
19
21
|
checks.push(await fileCheck(path.join(rootDir, '.cursor', 'skills', 'dream-wf-grill-prd', 'SKILL.md'), 'Cursor dream-wf grill PRD skill'));
|
|
20
22
|
checks.push(await fileCheck(path.join(rootDir, '.cursor', 'skills', 'dream-wf-mcp-policy', 'SKILL.md'), 'Cursor dream-wf MCP policy skill'));
|
|
23
|
+
checks.push(await mcpConfigCheck(rootDir, 'cursor'));
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
if (platform === 'claude') {
|
|
24
27
|
checks.push(await contentCheck(path.join(rootDir, 'CLAUDE.md'), '<!-- DREAM-WF:START -->', 'Claude Code dream-wf entry block'));
|
|
25
28
|
checks.push(await fileCheck(path.join(rootDir, '.claude', 'skills', 'dream-wf-grill-prd', 'SKILL.md'), 'Claude Code dream-wf grill PRD skill'));
|
|
26
29
|
checks.push(await fileCheck(path.join(rootDir, '.claude', 'skills', 'dream-wf-mcp-policy', 'SKILL.md'), 'Claude Code dream-wf MCP policy skill'));
|
|
30
|
+
checks.push(await mcpConfigCheck(rootDir, 'claude'));
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
if (platform === 'opencode') {
|
|
30
34
|
checks.push(await contentCheck(path.join(rootDir, 'AGENTS.md'), '<!-- DREAM-WF:START -->', 'OpenCode dream-wf entry block'));
|
|
31
35
|
checks.push(await fileCheck(path.join(rootDir, '.opencode', 'skills', 'dream-wf-grill-prd', 'SKILL.md'), 'OpenCode dream-wf grill PRD skill'));
|
|
32
36
|
checks.push(await fileCheck(path.join(rootDir, '.opencode', 'skills', 'dream-wf-mcp-policy', 'SKILL.md'), 'OpenCode dream-wf MCP policy skill'));
|
|
37
|
+
checks.push(await mcpConfigCheck(rootDir, 'opencode'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (platform === 'codex') {
|
|
41
|
+
checks.push(await contentCheck(path.join(rootDir, 'AGENTS.md'), '<!-- DREAM-WF:START -->', 'Codex dream-wf entry block'));
|
|
42
|
+
checks.push(await fileCheck(path.join(rootDir, '.codex', 'skills', 'dream-wf-grill-prd', 'SKILL.md'), 'Codex dream-wf grill PRD skill'));
|
|
43
|
+
checks.push(await fileCheck(path.join(rootDir, '.codex', 'skills', 'dream-wf-mcp-policy', 'SKILL.md'), 'Codex dream-wf MCP policy skill'));
|
|
44
|
+
checks.push(await fileCheck(path.join(rootDir, '.codex', 'hooks', 'dream-wf-guard.py'), 'Codex dream-wf guard hook'));
|
|
45
|
+
checks.push(await contentCheck(path.join(rootDir, '.codex', 'hooks.json'), 'dream-wf-guard.py', 'Codex hooks.json registration'));
|
|
46
|
+
checks.push(await contentCheck(path.join(rootDir, '.codex', 'config.toml'), 'hooks = true', 'Codex hooks feature enabled'));
|
|
47
|
+
checks.push(await mcpConfigCheck(rootDir, 'codex'));
|
|
33
48
|
}
|
|
34
49
|
|
|
35
50
|
checks.push(await secretScan(rootDir));
|
|
@@ -37,6 +52,41 @@ export async function checkDependencies(rootDir, platform) {
|
|
|
37
52
|
return checks;
|
|
38
53
|
}
|
|
39
54
|
|
|
55
|
+
// 检查 MCP 配置文件存在性以及是否包含 catalog 里的默认 MCP 条目。
|
|
56
|
+
async function mcpConfigCheck(rootDir, platform) {
|
|
57
|
+
const configPaths = {
|
|
58
|
+
cursor: '.cursor/mcp.json',
|
|
59
|
+
claude: '.mcp.json',
|
|
60
|
+
opencode: 'opencode.json',
|
|
61
|
+
codex: '.codex/config.toml'
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const exists = await mcpConfigExists(rootDir, platform);
|
|
65
|
+
if (!exists) {
|
|
66
|
+
return {
|
|
67
|
+
name: `MCP config (${configPaths[platform]})`,
|
|
68
|
+
ok: false,
|
|
69
|
+
hint: `Missing ${configPaths[platform]}. Run dream-wf init or TUI to configure MCP servers.`
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const servers = await readMcpServers(rootDir, platform);
|
|
74
|
+
const missing = MCP_CATALOG.filter((entry) => !servers[entry.name]).map((entry) => entry.name);
|
|
75
|
+
if (missing.length > 0) {
|
|
76
|
+
return {
|
|
77
|
+
name: `MCP config (${configPaths[platform]})`,
|
|
78
|
+
ok: false,
|
|
79
|
+
hint: `Missing MCP servers: ${missing.join(', ')}.`
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
name: `MCP config (${configPaths[platform]})`,
|
|
85
|
+
ok: true,
|
|
86
|
+
hint: `MCP config OK (${MCP_CATALOG.map((entry) => entry.name).join(', ')}).`
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
40
90
|
function binaryCheck(command, hint) {
|
|
41
91
|
return {
|
|
42
92
|
name: command,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// 安装聚合器的可选 skill 和 mcp 条目。
|
|
2
|
+
// 每个 skill 对应一个模板目录 templates/skills/<name>/,由 shared.installSkill 装入各平台 skills 目录。
|
|
3
|
+
// 每个 mcp 对应一个独立 server 配置,由 mcp.installMcpServer 写入各平台 mcp 配置文件。
|
|
4
|
+
|
|
5
|
+
export const SKILL_CATALOG = [
|
|
6
|
+
{
|
|
7
|
+
id: 'trellis-dream-wf-patch',
|
|
8
|
+
name: 'dream-wf-grill-prd',
|
|
9
|
+
label: 'dream-wf-grill-prd (Trellis patch · grill-me style PRD)',
|
|
10
|
+
description: 'grill-me 风格的 PRD 澄清 skill,dream-wf 的核心 patch。',
|
|
11
|
+
templateDir: 'dream-wf-grill-prd',
|
|
12
|
+
default: true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'dream-wf-mcp-policy',
|
|
16
|
+
name: 'dream-wf-mcp-policy',
|
|
17
|
+
label: 'dream-wf-mcp-policy (MCP 优先级策略 skill)',
|
|
18
|
+
description: '强制 fast-context-mcp / grok-search-mcp 优先级的策略 skill。',
|
|
19
|
+
templateDir: 'dream-wf-mcp-policy',
|
|
20
|
+
default: true
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export const MCP_CATALOG = [
|
|
25
|
+
{
|
|
26
|
+
id: 'fast-context',
|
|
27
|
+
name: 'fast-context',
|
|
28
|
+
label: 'fast-context-mcp (代码语义检索)',
|
|
29
|
+
description: '代码库语义理解优先 MCP,来源 SammySnake-d/fast-context-mcp。',
|
|
30
|
+
server: {
|
|
31
|
+
command: 'npx',
|
|
32
|
+
args: ['-y', '--prefer-online', 'fast-context-mcp@latest'],
|
|
33
|
+
env: {
|
|
34
|
+
WINDSURF_API_KEY: 'devin-session-xx'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
default: true,
|
|
38
|
+
requires: {
|
|
39
|
+
binaries: ['npx']
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'grok-search',
|
|
44
|
+
name: 'grok-search',
|
|
45
|
+
label: 'grok-search-mcp (外部文档/实时网络检索)',
|
|
46
|
+
description: '外部文档和实时网络检索优先 MCP,来源 GuDaStudio/GrokSearch。',
|
|
47
|
+
server: {
|
|
48
|
+
type: 'stdio',
|
|
49
|
+
command: 'uvx',
|
|
50
|
+
args: ['--from', 'git+https://github.com/GuDaStudio/GrokSearch@grok-with-tavily', 'grok-search'],
|
|
51
|
+
env: {
|
|
52
|
+
GROK_API_URL: 'https://your-api-endpoint.com/v1',
|
|
53
|
+
GROK_API_KEY: 'your-grok-api-key',
|
|
54
|
+
GROK_MODEL: 'your-model-name',
|
|
55
|
+
TAVILY_API_KEY: 'optional-tavily-key',
|
|
56
|
+
TAVILY_API_URL: 'https://api.tavily.com'
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
default: true,
|
|
60
|
+
requires: {
|
|
61
|
+
binaries: ['uvx']
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
export function defaultSkillIds() {
|
|
67
|
+
return SKILL_CATALOG.filter((item) => item.default).map((item) => item.id);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function defaultMcpIds() {
|
|
71
|
+
return MCP_CATALOG.filter((item) => item.default).map((item) => item.id);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function resolveSkills(ids) {
|
|
75
|
+
const set = new Set(ids);
|
|
76
|
+
return SKILL_CATALOG.filter((item) => set.has(item.id));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function resolveMcps(ids) {
|
|
80
|
+
const set = new Set(ids);
|
|
81
|
+
return MCP_CATALOG.filter((item) => set.has(item.id));
|
|
82
|
+
}
|