zcf 3.6.1 → 3.6.3
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 +12 -6
- package/dist/chunks/api-providers.mjs +39 -4
- package/dist/chunks/simple-config.mjs +101 -25
- package/dist/i18n/locales/en/configuration.json +3 -0
- package/dist/i18n/locales/zh-CN/configuration.json +3 -0
- package/package.json +1 -1
- package/templates/claude-code/common/settings.json +12 -2
- package/templates/claude-code/en/workflow/bmad/commands/bmad-init.md +225 -115
- package/templates/claude-code/zh-CN/workflow/bmad/commands/bmad-init.md +214 -111
package/README.md
CHANGED
|
@@ -24,18 +24,19 @@
|
|
|
24
24
|
|
|
25
25
|
## ♥️ Sponsor AI API
|
|
26
26
|
|
|
27
|
-
[](https://share.302.ai/gAT9VG)
|
|
28
|
-
[302.AI](https://share.302.ai/gAT9VG) is a pay-as-you-go enterprise AI resource hub that offers the latest and most comprehensive AI models and APIs on the market, along with a variety of ready-to-use online AI applications.
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
27
|
[](https://z.ai/subscribe?ic=8JVLJQFSKB)
|
|
28
|
+
|
|
33
29
|
This project is sponsored by Z.ai, supporting us with their GLM CODING PLAN.
|
|
34
|
-
GLM CODING PLAN is a subscription service designed for AI coding, starting at just $
|
|
30
|
+
GLM CODING PLAN is a subscription service designed for AI coding, starting at just $10/month. It provides access to their flagship GLM-4.7 & (GLM-5 Only Available for Pro Users)model across 10+ popular AI coding tools (Claude Code, Cline, Roo Code, etc.), offering developers top-tier, fast, and stable coding experiences.
|
|
35
31
|
Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB
|
|
36
32
|
|
|
37
33
|
---
|
|
38
34
|
|
|
35
|
+
[](https://share.302.ai/gAT9VG)
|
|
36
|
+
[302.AI](https://share.302.ai/gAT9VG) is a pay-as-you-go enterprise AI resource hub that offers the latest and most comprehensive AI models and APIs on the market, along with a variety of ready-to-use online AI applications.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
39
40
|
<table>
|
|
40
41
|
<tbody>
|
|
41
42
|
<tr>
|
|
@@ -46,6 +47,10 @@ Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB
|
|
|
46
47
|
<td width="180"><a href="https://www.aicodemirror.com/register?invitecode=ZCFZCF"><img src="./src/assets/AICodeMirror.jpg" alt="AICodeMirror" width="150"></a></td>
|
|
47
48
|
<td>Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code/Codex/Gemini CLI, supporting enterprise-level high concurrency, fast invoicing, and 7x24 dedicated technical support. Official channels for Claude Code/Codex/Gemini at discounts as low as 38%/2%/10.9% off, with additional discounts on top-ups! AICodeMirror offers special benefits for ZCF users: users who register through <a href="https://www.aicodemirror.com/register?invitecode=ZCFZCF">this link</a> can enjoy 20% off on first top-up, and enterprise customers can get up to 25% off!</td>
|
|
48
49
|
</tr>
|
|
50
|
+
<tr>
|
|
51
|
+
<td width="180"><a href="https://crazyrouter.com/?utm_source=github&utm_medium=sponsor&utm_campaign=zcf&aff=yJFo"><img src="./src/assets/crazyrouter.svg" alt="Crazyrouter" width="150"></a></td>
|
|
52
|
+
<td>Thanks to Crazyrouter for sponsoring this project! Crazyrouter is a high-performance AI API aggregation gateway — one API key for 300+ models (GPT, Claude, Gemini, DeepSeek, and more). All models at 55% of official pricing with auto-failover, smart routing, and unlimited concurrency. Fully OpenAI-compatible, works seamlessly with Claude Code, Codex, and Gemini CLI. Crazyrouter offers an exclusive deal for ZCF users: register via <a href="https://crazyrouter.com/?utm_source=github&utm_medium=sponsor&utm_campaign=zcf&aff=yJFo">this link</a> to get $2 free credit instantly!</td>
|
|
53
|
+
</tr>
|
|
49
54
|
<tbody>
|
|
50
55
|
</table>
|
|
51
56
|
|
|
@@ -114,6 +119,7 @@ A huge thank you to all our sponsors for their generous support!
|
|
|
114
119
|
- [PackyCode](https://www.packyapi.com/register?aff=zcf) (first API proxy service sponsor 🧝🏻♀️)
|
|
115
120
|
- [AICodeMirror](https://www.aicodemirror.com/register?invitecode=ZCFZCF) (official high-stability relay service sponsor 🪞)
|
|
116
121
|
- [UUCode](https://www.uucode.org/auth?ref=JQ2DJ1T8) (sponsored $100 proxy credits 💰)
|
|
122
|
+
- [Crazyrouter](https://crazyrouter.com/?utm_source=github&utm_medium=sponsor&utm_campaign=zcf&aff=yJFo) (AI API aggregation gateway sponsor 🚀)
|
|
117
123
|
|
|
118
124
|
【Individual Sponsors】
|
|
119
125
|
|
|
@@ -56,8 +56,22 @@ const API_PROVIDER_PRESETS = [
|
|
|
56
56
|
description: "AICodeMirror China Optimized Line"
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
|
-
id: "
|
|
60
|
-
name: "
|
|
59
|
+
id: "crazyrouter",
|
|
60
|
+
name: "Crazyrouter",
|
|
61
|
+
supportedCodeTools: ["claude-code", "codex"],
|
|
62
|
+
claudeCode: {
|
|
63
|
+
baseUrl: "https://crazyrouter.com",
|
|
64
|
+
authType: "api_key"
|
|
65
|
+
},
|
|
66
|
+
codex: {
|
|
67
|
+
baseUrl: "https://crazyrouter.com/v1",
|
|
68
|
+
wireApi: "responses"
|
|
69
|
+
},
|
|
70
|
+
description: "Crazyrouter AI API aggregation gateway"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "glm-cn",
|
|
74
|
+
name: "GLM CN",
|
|
61
75
|
supportedCodeTools: ["claude-code"],
|
|
62
76
|
claudeCode: {
|
|
63
77
|
baseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
@@ -65,6 +79,27 @@ const API_PROVIDER_PRESETS = [
|
|
|
65
79
|
},
|
|
66
80
|
description: "GLM (\u667A\u8C31AI)"
|
|
67
81
|
},
|
|
82
|
+
{
|
|
83
|
+
id: "z-ai",
|
|
84
|
+
name: "Z.ai",
|
|
85
|
+
supportedCodeTools: ["claude-code"],
|
|
86
|
+
claudeCode: {
|
|
87
|
+
baseUrl: "https://api.z.ai/api/anthropic",
|
|
88
|
+
authType: "auth_token"
|
|
89
|
+
},
|
|
90
|
+
description: "Z.ai API Service"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "bailian-coding",
|
|
94
|
+
name: "Bailian Coding",
|
|
95
|
+
supportedCodeTools: ["claude-code"],
|
|
96
|
+
claudeCode: {
|
|
97
|
+
baseUrl: "https://coding.dashscope.aliyuncs.com/apps/anthropic",
|
|
98
|
+
authType: "auth_token",
|
|
99
|
+
defaultModels: ["GLM-5"]
|
|
100
|
+
},
|
|
101
|
+
description: "Bailian Coding API Service"
|
|
102
|
+
},
|
|
68
103
|
{
|
|
69
104
|
id: "minimax",
|
|
70
105
|
name: "MiniMax",
|
|
@@ -77,8 +112,8 @@ const API_PROVIDER_PRESETS = [
|
|
|
77
112
|
description: "MiniMax API Service"
|
|
78
113
|
},
|
|
79
114
|
{
|
|
80
|
-
id: "kimi",
|
|
81
|
-
name: "Kimi",
|
|
115
|
+
id: "kimi-coding",
|
|
116
|
+
name: "Kimi Coding",
|
|
82
117
|
supportedCodeTools: ["claude-code"],
|
|
83
118
|
claudeCode: {
|
|
84
119
|
baseUrl: "https://api.kimi.com/coding/",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as nodeFs from 'node:fs';
|
|
2
|
-
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, rmSync, rmdirSync, readdirSync,
|
|
2
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, lstatSync, statSync, rmSync, rmdirSync, readdirSync, unlinkSync, renameSync } from 'node:fs';
|
|
3
3
|
import process from 'node:process';
|
|
4
4
|
import ansis from 'ansis';
|
|
5
5
|
import inquirer from 'inquirer';
|
|
@@ -18,7 +18,7 @@ import { rm, mkdir, copyFile as copyFile$1 } from 'node:fs/promises';
|
|
|
18
18
|
import i18next from 'i18next';
|
|
19
19
|
import Backend from 'i18next-fs-backend';
|
|
20
20
|
|
|
21
|
-
const version = "3.6.
|
|
21
|
+
const version = "3.6.3";
|
|
22
22
|
const homepage = "https://github.com/UfoMiao/zcf";
|
|
23
23
|
|
|
24
24
|
const i18n = i18next.createInstance();
|
|
@@ -947,10 +947,35 @@ function copyDir(src, dest, options = {}) {
|
|
|
947
947
|
for (const entry of entries) {
|
|
948
948
|
const srcPath = `${src}/${entry}`;
|
|
949
949
|
const destPath = `${dest}/${entry}`;
|
|
950
|
-
|
|
950
|
+
let stats;
|
|
951
|
+
try {
|
|
952
|
+
stats = lstatSync(srcPath);
|
|
953
|
+
} catch (error) {
|
|
954
|
+
if (error.code === "ENOENT") {
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
throw error;
|
|
958
|
+
}
|
|
951
959
|
if (filter && !filter(srcPath, stats)) {
|
|
952
960
|
continue;
|
|
953
961
|
}
|
|
962
|
+
if (stats.isSymbolicLink()) {
|
|
963
|
+
try {
|
|
964
|
+
existsSync(srcPath);
|
|
965
|
+
const targetStats = statSync(srcPath);
|
|
966
|
+
if (targetStats.isDirectory()) {
|
|
967
|
+
copyDir(srcPath, destPath, options);
|
|
968
|
+
} else {
|
|
969
|
+
if (!overwrite && exists(destPath)) {
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
copyFile(srcPath, destPath);
|
|
973
|
+
}
|
|
974
|
+
} catch {
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
954
979
|
if (stats.isDirectory()) {
|
|
955
980
|
copyDir(srcPath, destPath, options);
|
|
956
981
|
} else {
|
|
@@ -3048,7 +3073,7 @@ function migrateFromJsonConfig(jsonConfig) {
|
|
|
3048
3073
|
claudeCode: {
|
|
3049
3074
|
enabled: jsonConfig.codeToolType === "claude-code",
|
|
3050
3075
|
outputStyles: jsonConfig.outputStyles || defaultConfig.claudeCode.outputStyles,
|
|
3051
|
-
defaultOutputStyle: jsonConfig.defaultOutputStyle
|
|
3076
|
+
defaultOutputStyle: jsonConfig.defaultOutputStyle ?? defaultConfig.claudeCode.defaultOutputStyle,
|
|
3052
3077
|
installType: claudeCodeInstallType,
|
|
3053
3078
|
currentProfile: jsonConfig.currentProfileId || defaultConfig.claudeCode.currentProfile,
|
|
3054
3079
|
profiles: jsonConfig.claudeCode?.profiles || {}
|
|
@@ -3912,8 +3937,9 @@ function backupCodexFiles() {
|
|
|
3912
3937
|
return cachedSkipPromptBackup;
|
|
3913
3938
|
const timestamp = dayjs().format("YYYY-MM-DD_HH-mm-ss");
|
|
3914
3939
|
const backupDir = createBackupDirectory(timestamp);
|
|
3940
|
+
const tmpDir = join(CODEX_DIR, "tmp");
|
|
3915
3941
|
const filter = (path) => {
|
|
3916
|
-
return !path.includes("/backup");
|
|
3942
|
+
return !path.includes("/backup") && !path.startsWith(tmpDir);
|
|
3917
3943
|
};
|
|
3918
3944
|
copyDir(CODEX_DIR, backupDir, { filter });
|
|
3919
3945
|
if (process.env.ZCF_CODEX_SKIP_PROMPT_SINGLE_BACKUP === "true")
|
|
@@ -5365,6 +5391,11 @@ function setGlobalDefaultOutputStyle(styleId) {
|
|
|
5365
5391
|
};
|
|
5366
5392
|
writeJsonConfig(SETTINGS_FILE, updatedSettings);
|
|
5367
5393
|
}
|
|
5394
|
+
function clearGlobalOutputStyle() {
|
|
5395
|
+
const existingSettings = readJsonConfig(SETTINGS_FILE) || {};
|
|
5396
|
+
const { outputStyle: _, ...rest } = existingSettings;
|
|
5397
|
+
writeJsonConfig(SETTINGS_FILE, rest);
|
|
5398
|
+
}
|
|
5368
5399
|
function hasLegacyPersonalityFiles() {
|
|
5369
5400
|
return LEGACY_FILES.some((filename) => exists(join(CLAUDE_DIR, filename)));
|
|
5370
5401
|
}
|
|
@@ -5390,25 +5421,15 @@ async function configureOutputStyle(preselectedStyles, preselectedDefault) {
|
|
|
5390
5421
|
description: i18n.t("configuration:outputStyles.engineer-professional.description")
|
|
5391
5422
|
},
|
|
5392
5423
|
{
|
|
5393
|
-
id: "
|
|
5394
|
-
name: i18n.t("configuration:outputStyles.
|
|
5395
|
-
description: i18n.t("configuration:outputStyles.
|
|
5424
|
+
id: "nekomata-engineer",
|
|
5425
|
+
name: i18n.t("configuration:outputStyles.nekomata-engineer.name"),
|
|
5426
|
+
description: i18n.t("configuration:outputStyles.nekomata-engineer.description")
|
|
5396
5427
|
},
|
|
5397
5428
|
{
|
|
5398
5429
|
id: "laowang-engineer",
|
|
5399
5430
|
name: i18n.t("configuration:outputStyles.laowang-engineer.name"),
|
|
5400
5431
|
description: i18n.t("configuration:outputStyles.laowang-engineer.description")
|
|
5401
5432
|
},
|
|
5402
|
-
{
|
|
5403
|
-
id: "learning",
|
|
5404
|
-
name: i18n.t("configuration:outputStyles.learning.name"),
|
|
5405
|
-
description: i18n.t("configuration:outputStyles.learning.description")
|
|
5406
|
-
},
|
|
5407
|
-
{
|
|
5408
|
-
id: "nekomata-engineer",
|
|
5409
|
-
name: i18n.t("configuration:outputStyles.nekomata-engineer.name"),
|
|
5410
|
-
description: i18n.t("configuration:outputStyles.nekomata-engineer.description")
|
|
5411
|
-
},
|
|
5412
5433
|
{
|
|
5413
5434
|
id: "ojousama-engineer",
|
|
5414
5435
|
name: i18n.t("configuration:outputStyles.ojousama-engineer.name"),
|
|
@@ -5423,6 +5444,16 @@ async function configureOutputStyle(preselectedStyles, preselectedDefault) {
|
|
|
5423
5444
|
id: "rem-engineer",
|
|
5424
5445
|
name: i18n.t("configuration:outputStyles.rem-engineer.name"),
|
|
5425
5446
|
description: i18n.t("configuration:outputStyles.rem-engineer.description")
|
|
5447
|
+
},
|
|
5448
|
+
{
|
|
5449
|
+
id: "explanatory",
|
|
5450
|
+
name: i18n.t("configuration:outputStyles.explanatory.name"),
|
|
5451
|
+
description: i18n.t("configuration:outputStyles.explanatory.description")
|
|
5452
|
+
},
|
|
5453
|
+
{
|
|
5454
|
+
id: "learning",
|
|
5455
|
+
name: i18n.t("configuration:outputStyles.learning.name"),
|
|
5456
|
+
description: i18n.t("configuration:outputStyles.learning.description")
|
|
5426
5457
|
}
|
|
5427
5458
|
];
|
|
5428
5459
|
const availableStyles = getAvailableOutputStyles();
|
|
@@ -5458,14 +5489,59 @@ async function configureOutputStyle(preselectedStyles, preselectedDefault) {
|
|
|
5458
5489
|
checked: true
|
|
5459
5490
|
// Default select all custom styles
|
|
5460
5491
|
};
|
|
5461
|
-
}))
|
|
5462
|
-
|
|
5492
|
+
}))
|
|
5493
|
+
// Allow empty selection - user can choose to not install any custom styles
|
|
5463
5494
|
});
|
|
5464
|
-
|
|
5465
|
-
|
|
5495
|
+
selectedStyles = promptedStyles || [];
|
|
5496
|
+
if (selectedStyles.length === 0) {
|
|
5497
|
+
const builtinStyles = availableStyles.filter((style) => !style.isCustom);
|
|
5498
|
+
const noneOption = { name: i18n.t("configuration:noOutputStyle"), description: i18n.t("configuration:noOutputStyleDesc") };
|
|
5499
|
+
const { defaultStyle: promptedDefault2 } = await inquirer.prompt({
|
|
5500
|
+
type: "list",
|
|
5501
|
+
name: "defaultStyle",
|
|
5502
|
+
message: i18n.t("configuration:selectDefaultOutputStyle"),
|
|
5503
|
+
choices: addNumbersToChoices([
|
|
5504
|
+
// Show "none" option first
|
|
5505
|
+
{
|
|
5506
|
+
name: `${noneOption.name} - ${ansis.gray(noneOption.description)}`,
|
|
5507
|
+
value: "__none__",
|
|
5508
|
+
short: noneOption.name
|
|
5509
|
+
},
|
|
5510
|
+
// Then show built-in styles
|
|
5511
|
+
...builtinStyles.map((style) => {
|
|
5512
|
+
const styleInfo = outputStyleList.find((s) => s.id === style.id);
|
|
5513
|
+
return {
|
|
5514
|
+
name: `${styleInfo?.name || style.id} - ${ansis.gray(styleInfo?.description || "")}`,
|
|
5515
|
+
value: style.id,
|
|
5516
|
+
short: styleInfo?.name || style.id
|
|
5517
|
+
};
|
|
5518
|
+
})
|
|
5519
|
+
]),
|
|
5520
|
+
default: "__none__"
|
|
5521
|
+
});
|
|
5522
|
+
if (!promptedDefault2) {
|
|
5523
|
+
console.log(ansis.yellow(i18n.t("common:cancelled")));
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
if (promptedDefault2 === "__none__") {
|
|
5527
|
+
clearGlobalOutputStyle();
|
|
5528
|
+
updateZcfConfig({
|
|
5529
|
+
outputStyles: [],
|
|
5530
|
+
defaultOutputStyle: "none"
|
|
5531
|
+
});
|
|
5532
|
+
console.log(ansis.green(`\u2714 ${i18n.t("configuration:outputStyleCleared")}`));
|
|
5533
|
+
return;
|
|
5534
|
+
}
|
|
5535
|
+
defaultStyle = promptedDefault2;
|
|
5536
|
+
setGlobalDefaultOutputStyle(defaultStyle);
|
|
5537
|
+
updateZcfConfig({
|
|
5538
|
+
outputStyles: [],
|
|
5539
|
+
defaultOutputStyle: defaultStyle
|
|
5540
|
+
});
|
|
5541
|
+
console.log(ansis.green(`\u2714 ${i18n.t("configuration:outputStyleInstalled")}`));
|
|
5542
|
+
console.log(ansis.gray(` ${i18n.t("configuration:defaultStyle")}: ${defaultStyle}`));
|
|
5466
5543
|
return;
|
|
5467
5544
|
}
|
|
5468
|
-
selectedStyles = promptedStyles;
|
|
5469
5545
|
const { defaultStyle: promptedDefault } = await inquirer.prompt({
|
|
5470
5546
|
type: "list",
|
|
5471
5547
|
name: "defaultStyle",
|
|
@@ -5480,8 +5556,8 @@ async function configureOutputStyle(preselectedStyles, preselectedDefault) {
|
|
|
5480
5556
|
short: styleInfo?.name || styleId
|
|
5481
5557
|
};
|
|
5482
5558
|
}),
|
|
5483
|
-
// Then show all built-in styles (always available)
|
|
5484
|
-
...availableStyles.filter((style) => !style.isCustom).map((style) => {
|
|
5559
|
+
// Then show all built-in styles (always available), with default first
|
|
5560
|
+
...availableStyles.filter((style) => !style.isCustom).sort((a, b) => a.id === "default" ? -1 : b.id === "default" ? 1 : 0).map((style) => {
|
|
5485
5561
|
const styleInfo = outputStyleList.find((s) => s.id === style.id);
|
|
5486
5562
|
return {
|
|
5487
5563
|
name: `${styleInfo?.name || style.id} - ${ansis.gray(styleInfo?.description || "")}`,
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
"openingSettingsJson": "Opening settings.json...",
|
|
42
42
|
"opusModelOption": "Opus - Only use opus, high token consumption, use with caution",
|
|
43
43
|
"sonnet1mModelOption": "Sonnet 1M - 1M context version",
|
|
44
|
+
"noOutputStyle": "No output style",
|
|
45
|
+
"noOutputStyleDesc": "Do not set any output style, use Claude Code default behavior",
|
|
46
|
+
"outputStyleCleared": "Output style settings cleared",
|
|
44
47
|
"outputStyleInstalled": "Output styles installed successfully",
|
|
45
48
|
"outputStyles.default.description": "Claude completes coding tasks efficiently and provides concise responses (Claude Code built-in)",
|
|
46
49
|
"outputStyles.default.name": "Default",
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
"openingSettingsJson": "正在打开 settings.json...",
|
|
42
42
|
"opusModelOption": "Opus - 只用opus,token消耗高,慎用",
|
|
43
43
|
"sonnet1mModelOption": "Sonnet 1M - 1M上下文版本",
|
|
44
|
+
"noOutputStyle": "不使用输出风格",
|
|
45
|
+
"noOutputStyleDesc": "不设置任何输出风格,使用 Claude Code 原始行为",
|
|
46
|
+
"outputStyleCleared": "已清除输出风格设置",
|
|
44
47
|
"outputStyleInstalled": "输出风格安装成功",
|
|
45
48
|
"outputStyles.default.description": "完成编码任务时高效且提供简洁响应 (Claude Code自带)",
|
|
46
49
|
"outputStyles.default.name": "默认风格",
|
package/package.json
CHANGED
|
@@ -10,16 +10,26 @@
|
|
|
10
10
|
"permissions": {
|
|
11
11
|
"allow": [
|
|
12
12
|
"Bash",
|
|
13
|
-
"BashOutput",
|
|
14
13
|
"Edit",
|
|
15
14
|
"Glob",
|
|
16
15
|
"Grep",
|
|
17
16
|
"KillShell",
|
|
17
|
+
"LS",
|
|
18
|
+
"LSP",
|
|
19
|
+
"MultiEdit",
|
|
18
20
|
"NotebookEdit",
|
|
21
|
+
"NotebookRead",
|
|
19
22
|
"Read",
|
|
20
|
-
"
|
|
23
|
+
"Skill",
|
|
21
24
|
"Task",
|
|
25
|
+
"TaskCreate",
|
|
26
|
+
"TaskGet",
|
|
27
|
+
"TaskList",
|
|
28
|
+
"TaskOutput",
|
|
29
|
+
"TaskStop",
|
|
30
|
+
"TaskUpdate",
|
|
22
31
|
"TodoWrite",
|
|
32
|
+
"ToolSearch",
|
|
23
33
|
"WebFetch",
|
|
24
34
|
"WebSearch",
|
|
25
35
|
"Write",
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# /bmad-init Command
|
|
2
2
|
|
|
3
|
-
This command initializes BMad
|
|
3
|
+
This command initializes or updates BMad-Method (V6) in your project.
|
|
4
4
|
|
|
5
5
|
## When this command is invoked:
|
|
6
6
|
|
|
7
|
-
1. Check if BMad is already installed
|
|
8
|
-
2.
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
7
|
+
1. Check if `_bmad/` directory exists to determine if BMad V6 is already installed
|
|
8
|
+
2. Check for legacy V4 installations (`.bmad-core` or `.bmad-method` directories)
|
|
9
|
+
3. Fresh install executes: `npx bmad-method install --directory . --modules bmm --tools claude-code --communication-language English --document-output-language English --yes`
|
|
10
|
+
4. Existing install executes: `npx bmad-method install --directory . --action quick-update --yes`
|
|
11
|
+
5. Fix installer bug: rename `{output_folder}` to `_bmad-output` (Beta known issue)
|
|
12
|
+
6. Automatically update `.gitignore` (remove V4 entries, add V6 entries)
|
|
13
|
+
7. Display installation results and prompt user for next steps
|
|
11
14
|
|
|
12
15
|
## Implementation
|
|
13
16
|
|
|
@@ -16,150 +19,257 @@ const { execSync } = require('node:child_process')
|
|
|
16
19
|
const fs = require('node:fs')
|
|
17
20
|
const path = require('node:path')
|
|
18
21
|
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
}
|
|
22
|
+
// Legacy entries to clean from .gitignore
|
|
23
|
+
const LEGACY_GITIGNORE_ENTRIES = [
|
|
24
|
+
'.bmad-core',
|
|
25
|
+
'.bmad-method',
|
|
26
|
+
'.claude/commands/BMad',
|
|
27
|
+
'{output_folder}', // v6.0.0-Beta.8 bug artifact
|
|
28
|
+
]
|
|
28
29
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
send "1\\r"
|
|
35
|
-
expect "How would you like to proceed?"
|
|
36
|
-
send "1\\r"
|
|
37
|
-
expect eof
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
execSync(`expect -c '${expectScript}'`, {
|
|
41
|
-
stdio: 'inherit',
|
|
42
|
-
cwd: process.cwd(),
|
|
43
|
-
shell: true
|
|
44
|
-
})
|
|
45
|
-
}
|
|
30
|
+
// V6 .gitignore entries
|
|
31
|
+
const V6_GITIGNORE_ENTRIES = [
|
|
32
|
+
'_bmad/',
|
|
33
|
+
'_bmad-output/',
|
|
34
|
+
]
|
|
46
35
|
|
|
47
|
-
//
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log(' 1. Choose "Upgrade BMad core" when prompted')
|
|
52
|
-
console.log(' 2. Choose "Backup and overwrite modified files" when prompted')
|
|
53
|
-
console.log('')
|
|
54
|
-
|
|
55
|
-
execSync('npx bmad-method@latest install -f -d . -i claude-code', {
|
|
56
|
-
stdio: 'inherit',
|
|
57
|
-
cwd: process.cwd(),
|
|
58
|
-
shell: true
|
|
59
|
-
})
|
|
60
|
-
}
|
|
36
|
+
// Fix installer bug: {output_folder} not resolved to _bmad-output (v6.0.0-Beta.8)
|
|
37
|
+
function fixOutputFolderBug(cwd) {
|
|
38
|
+
const buggyPath = path.join(cwd, '{output_folder}')
|
|
39
|
+
const correctPath = path.join(cwd, '_bmad-output')
|
|
61
40
|
|
|
62
|
-
|
|
63
|
-
// Check if already installed and get version
|
|
64
|
-
const manifestPath = path.join(process.cwd(), '.bmad-core', 'install-manifest.yaml')
|
|
65
|
-
let needsInstall = true
|
|
66
|
-
let currentVersion = null
|
|
41
|
+
if (!fs.existsSync(buggyPath)) return false
|
|
67
42
|
|
|
68
|
-
if (fs.existsSync(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
43
|
+
if (!fs.existsSync(correctPath)) {
|
|
44
|
+
// _bmad-output doesn't exist, simply rename
|
|
45
|
+
fs.renameSync(buggyPath, correctPath)
|
|
46
|
+
console.log(' ✅ {output_folder} → _bmad-output/ (renamed)')
|
|
47
|
+
} else {
|
|
48
|
+
// _bmad-output already exists, merge subdirectories then delete
|
|
49
|
+
const entries = fs.readdirSync(buggyPath, { withFileTypes: true })
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const src = path.join(buggyPath, entry.name)
|
|
52
|
+
const dest = path.join(correctPath, entry.name)
|
|
53
|
+
if (!fs.existsSync(dest)) {
|
|
54
|
+
fs.renameSync(src, dest)
|
|
55
|
+
console.log(` ✅ Moved ${entry.name} → _bmad-output/`)
|
|
76
56
|
}
|
|
57
|
+
}
|
|
58
|
+
fs.rmSync(buggyPath, { recursive: true, force: true })
|
|
59
|
+
console.log(' ✅ Removed redundant {output_folder}/')
|
|
60
|
+
}
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
77
63
|
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
function updateGitignore(cwd) {
|
|
65
|
+
const gitignorePath = path.join(cwd, '.gitignore')
|
|
66
|
+
let content = ''
|
|
67
|
+
let exists = false
|
|
80
68
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
69
|
+
if (fs.existsSync(gitignorePath)) {
|
|
70
|
+
content = fs.readFileSync(gitignorePath, 'utf8')
|
|
71
|
+
exists = true
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const lines = content.split('\n')
|
|
75
|
+
let changed = false
|
|
76
|
+
|
|
77
|
+
// Remove V4 legacy entries
|
|
78
|
+
const filtered = lines.filter(line => {
|
|
79
|
+
const trimmed = line.trim()
|
|
80
|
+
const isLegacy = LEGACY_GITIGNORE_ENTRIES.some(entry =>
|
|
81
|
+
trimmed === entry || trimmed === entry + '/' || trimmed === '/' + entry
|
|
82
|
+
)
|
|
83
|
+
if (isLegacy) {
|
|
84
|
+
console.log(` 🗑️ Removing legacy entry: ${trimmed}`)
|
|
85
|
+
changed = true
|
|
89
86
|
}
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
return !isLegacy
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// Add V6 entries
|
|
91
|
+
const newEntries = []
|
|
92
|
+
for (const entry of V6_GITIGNORE_ENTRIES) {
|
|
93
|
+
const entryBase = entry.replace(/\/$/, '')
|
|
94
|
+
const alreadyExists = filtered.some(line => {
|
|
95
|
+
const trimmed = line.trim()
|
|
96
|
+
return trimmed === entry || trimmed === entryBase || trimmed === '/' + entryBase
|
|
97
|
+
})
|
|
98
|
+
if (!alreadyExists) {
|
|
99
|
+
newEntries.push(entry)
|
|
100
|
+
console.log(` ✅ Adding new entry: ${entry}`)
|
|
101
|
+
changed = true
|
|
92
102
|
}
|
|
93
103
|
}
|
|
94
104
|
|
|
95
|
-
if (
|
|
105
|
+
if (!changed) {
|
|
106
|
+
console.log(' ℹ️ .gitignore is up to date, no changes needed')
|
|
96
107
|
return
|
|
97
108
|
}
|
|
98
109
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
console.log('📋 Using automated installation (expect tool available)')
|
|
107
|
-
installWithExpect()
|
|
108
|
-
} else {
|
|
109
|
-
fallbackInstallation()
|
|
110
|
+
// Build new content
|
|
111
|
+
let result = filtered.join('\n')
|
|
112
|
+
|
|
113
|
+
if (newEntries.length > 0) {
|
|
114
|
+
// Ensure trailing newline, then add BMad section
|
|
115
|
+
if (result.length > 0 && !result.endsWith('\n')) {
|
|
116
|
+
result += '\n'
|
|
110
117
|
}
|
|
118
|
+
result += '\n# BMad Method V6\n'
|
|
119
|
+
result += newEntries.join('\n') + '\n'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fs.writeFileSync(gitignorePath, result, 'utf8')
|
|
123
|
+
console.log(` 📝 .gitignore ${exists ? 'updated' : 'created'}`)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function initBmad() {
|
|
127
|
+
const cwd = process.cwd()
|
|
128
|
+
const bmadV6Path = path.join(cwd, '_bmad')
|
|
129
|
+
const legacyCorePath = path.join(cwd, '.bmad-core')
|
|
130
|
+
const legacyMethodPath = path.join(cwd, '.bmad-method')
|
|
131
|
+
|
|
132
|
+
// Check for legacy V4 installation
|
|
133
|
+
const hasLegacyCore = fs.existsSync(legacyCorePath)
|
|
134
|
+
const hasLegacyMethod = fs.existsSync(legacyMethodPath)
|
|
111
135
|
|
|
136
|
+
if (hasLegacyCore || hasLegacyMethod) {
|
|
137
|
+
console.log('⚠️ Legacy BMad V4 installation detected:')
|
|
138
|
+
if (hasLegacyCore) console.log(' • .bmad-core/ (V4 core directory)')
|
|
139
|
+
if (hasLegacyMethod) console.log(' • .bmad-method/ (V4 method directory)')
|
|
112
140
|
console.log('')
|
|
113
|
-
console.log('
|
|
141
|
+
console.log('📌 The V6 installer will handle legacy migration automatically. Follow the prompts during installation.')
|
|
142
|
+
console.log(' Details: https://bmad-code-org.github.io/BMAD-METHOD/docs/how-to/upgrade-to-v6')
|
|
143
|
+
console.log('')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check if V6 is already installed
|
|
147
|
+
const hasV6 = fs.existsSync(bmadV6Path)
|
|
148
|
+
|
|
149
|
+
// Build non-interactive install command
|
|
150
|
+
let installCmd
|
|
151
|
+
if (hasV6) {
|
|
152
|
+
console.log('🔄 Existing BMad V6 installation detected, performing quick update...')
|
|
153
|
+
console.log('')
|
|
154
|
+
installCmd = [
|
|
155
|
+
'npx bmad-method install',
|
|
156
|
+
'--directory .',
|
|
157
|
+
'--action quick-update',
|
|
158
|
+
'--yes',
|
|
159
|
+
].join(' ')
|
|
160
|
+
} else {
|
|
161
|
+
console.log('🚀 Initializing BMad-Method V6...')
|
|
162
|
+
console.log('')
|
|
163
|
+
installCmd = [
|
|
164
|
+
'npx bmad-method install',
|
|
165
|
+
'--directory .',
|
|
166
|
+
'--modules bmm',
|
|
167
|
+
'--tools claude-code',
|
|
168
|
+
'--communication-language English',
|
|
169
|
+
'--document-output-language English',
|
|
170
|
+
'--yes',
|
|
171
|
+
].join(' ')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Execute installation
|
|
175
|
+
try {
|
|
176
|
+
console.log(`📋 Executing: ${installCmd}`)
|
|
177
|
+
console.log('')
|
|
178
|
+
execSync(installCmd, {
|
|
179
|
+
stdio: 'inherit',
|
|
180
|
+
cwd: cwd,
|
|
181
|
+
shell: true
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
console.log('')
|
|
185
|
+
console.log('✅ BMad-Method V6 installation/update complete!')
|
|
114
186
|
console.log('')
|
|
115
187
|
console.log('═══════════════════════════════════════════════════════════════')
|
|
116
|
-
console.log('📌 IMPORTANT: Please restart
|
|
188
|
+
console.log('📌 IMPORTANT: Please restart your AI IDE to load BMad extensions')
|
|
117
189
|
console.log('═══════════════════════════════════════════════════════════════')
|
|
118
190
|
console.log('')
|
|
119
|
-
|
|
120
|
-
console.log('
|
|
121
|
-
|
|
191
|
+
// Fix {output_folder} bug (v6.0.0-Beta.8)
|
|
192
|
+
console.log('🔧 Checking for known installer issues...')
|
|
193
|
+
try {
|
|
194
|
+
const fixed = fixOutputFolderBug(cwd)
|
|
195
|
+
if (!fixed) console.log(' ℹ️ No fixes needed')
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.log(` ⚠️ Failed to fix {output_folder}: ${err.message}`)
|
|
198
|
+
console.log(' Please manually rename {output_folder}/ to _bmad-output/')
|
|
199
|
+
}
|
|
122
200
|
console.log('')
|
|
123
|
-
|
|
124
|
-
console.log('
|
|
125
|
-
console.log('
|
|
126
|
-
console.log('
|
|
127
|
-
console.log(' • docs/')
|
|
201
|
+
|
|
202
|
+
console.log('📂 V6 Directory Structure:')
|
|
203
|
+
console.log(' • _bmad/ — agents, workflows, tasks, and configuration')
|
|
204
|
+
console.log(' • _bmad-output/ — generated artifact output directory')
|
|
128
205
|
console.log('')
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
console.log('
|
|
132
|
-
|
|
133
|
-
|
|
206
|
+
|
|
207
|
+
// Automatically update .gitignore
|
|
208
|
+
console.log('🔧 Updating .gitignore...')
|
|
209
|
+
try {
|
|
210
|
+
updateGitignore(cwd)
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.log(' ⚠️ Failed to automatically update .gitignore, please manually add _bmad/ and _bmad-output/')
|
|
213
|
+
}
|
|
134
214
|
console.log('')
|
|
135
|
-
console.log('
|
|
136
|
-
console.log('
|
|
215
|
+
console.log('🚀 Quick Start:')
|
|
216
|
+
console.log(' 1. Restart your AI IDE')
|
|
217
|
+
console.log(' 2. Run /bmad-help for guidance and next step suggestions')
|
|
218
|
+
console.log(' 3. Type /bmad and use autocomplete to browse available commands')
|
|
219
|
+
console.log('')
|
|
220
|
+
console.log('💡 Common Workflows:')
|
|
221
|
+
console.log(' • /bmad-help — Interactive help')
|
|
222
|
+
console.log(' • /bmad-bmm-create-prd — Create Product Requirements Document')
|
|
223
|
+
console.log(' • /bmad-bmm-create-architecture — Create Architecture Document')
|
|
224
|
+
console.log(' • /bmad-bmm-create-epics-and-stories — Create Epics and User Stories')
|
|
225
|
+
console.log(' • /bmad-bmm-sprint-planning — Initialize Sprint Planning')
|
|
226
|
+
console.log(' • /bmad-bmm-dev-story — Implement User Story')
|
|
227
|
+
|
|
228
|
+
// Legacy V4 IDE command cleanup reminder
|
|
229
|
+
const legacyClaudeAgents = path.join(cwd, '.claude', 'commands', 'BMad', 'agents')
|
|
230
|
+
const legacyClaudeTasks = path.join(cwd, '.claude', 'commands', 'BMad', 'tasks')
|
|
231
|
+
if (fs.existsSync(legacyClaudeAgents) || fs.existsSync(legacyClaudeTasks)) {
|
|
232
|
+
console.log('')
|
|
233
|
+
console.log('⚠️ Legacy V4 IDE commands detected, consider removing manually:')
|
|
234
|
+
if (fs.existsSync(legacyClaudeAgents)) console.log(' • .claude/commands/BMad/agents/')
|
|
235
|
+
if (fs.existsSync(legacyClaudeTasks)) console.log(' • .claude/commands/BMad/tasks/')
|
|
236
|
+
console.log(' New V6 commands are installed under .claude/commands/bmad/')
|
|
237
|
+
}
|
|
137
238
|
}
|
|
138
239
|
catch (error) {
|
|
139
240
|
console.error('❌ Installation failed:', error.message)
|
|
140
241
|
console.log('')
|
|
141
242
|
console.log('🛠️ Manual Installation Guide:')
|
|
142
|
-
console.log('
|
|
143
|
-
console.log('
|
|
243
|
+
console.log(' 1. Ensure Node.js 20+ is installed')
|
|
244
|
+
console.log(' 2. Non-interactive install:')
|
|
245
|
+
console.log(' npx bmad-method install --directory . --modules bmm --tools claude-code --communication-language English --document-output-language English --yes')
|
|
246
|
+
console.log(' 3. Quick update existing installation:')
|
|
247
|
+
console.log(' npx bmad-method install --directory . --action quick-update --yes')
|
|
248
|
+
console.log(' 4. Or interactive install:')
|
|
249
|
+
console.log(' npx bmad-method install')
|
|
144
250
|
console.log('')
|
|
145
|
-
console.log('
|
|
146
|
-
console.log('
|
|
147
|
-
console.log(' 2. When asked "How would you like to proceed?", choose "Backup and overwrite"')
|
|
148
|
-
console.log('')
|
|
149
|
-
console.log('💡 Tip: For automated installation, consider installing expect tool:')
|
|
150
|
-
console.log(' • macOS: brew install expect')
|
|
151
|
-
console.log(' • Ubuntu: sudo apt-get install expect')
|
|
152
|
-
console.log(' • CentOS: sudo yum install expect')
|
|
251
|
+
console.log('📖 Documentation:')
|
|
252
|
+
console.log(' https://bmad-code-org.github.io/BMAD-METHOD/docs/how-to/install-bmad')
|
|
153
253
|
}
|
|
154
254
|
}
|
|
155
255
|
|
|
156
|
-
// Execute
|
|
256
|
+
// Execute initialization
|
|
157
257
|
initBmad()
|
|
158
258
|
```
|
|
159
259
|
|
|
160
|
-
##
|
|
260
|
+
## Usage
|
|
261
|
+
|
|
262
|
+
Simply type in Claude Code:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
/bmad-init
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
This command will:
|
|
161
269
|
|
|
162
|
-
|
|
163
|
-
-
|
|
164
|
-
|
|
165
|
-
|
|
270
|
+
1. Detect current installation status (V6 / V4 legacy / not installed)
|
|
271
|
+
2. Fresh install: non-interactively execute `npx bmad-method install --directory . --modules bmm --tools claude-code --communication-language English --document-output-language English --yes`
|
|
272
|
+
3. Existing install: execute `npx bmad-method install --directory . --action quick-update --yes`
|
|
273
|
+
4. Fix `{output_folder}` → `_bmad-output` installer bug
|
|
274
|
+
5. Automatically update `.gitignore` (clean up legacy entries, add V6 entries)
|
|
275
|
+
6. Provide next step suggestions
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# /bmad-init 命令
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
此命令在您的项目中初始化或更新 BMad-Method (V6)。
|
|
4
4
|
|
|
5
5
|
## 当调用此命令时:
|
|
6
6
|
|
|
7
|
-
1. 检查
|
|
8
|
-
2.
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
7
|
+
1. 检查 `_bmad/` 目录是否存在,判断 BMad V6 是否已安装
|
|
8
|
+
2. 检查是否存在旧版 V4 安装(`.bmad-core` 或 `.bmad-method` 目录)
|
|
9
|
+
3. 新安装执行:`npx bmad-method install --directory . --modules bmm --tools claude-code --communication-language Chinese --document-output-language Chinese --yes`
|
|
10
|
+
4. 已安装则执行:`npx bmad-method install --directory . --action quick-update --yes`
|
|
11
|
+
5. 修复安装器 bug:将 `{output_folder}` 重命名为 `_bmad-output`(Beta 已知问题)
|
|
12
|
+
6. 自动更新 `.gitignore`(移除 V4 条目,添加 V6 条目)
|
|
13
|
+
7. 显示安装结果并提示用户后续操作
|
|
11
14
|
|
|
12
15
|
## 实现
|
|
13
16
|
|
|
@@ -16,140 +19,237 @@ const { execSync } = require('node:child_process')
|
|
|
16
19
|
const fs = require('node:fs')
|
|
17
20
|
const path = require('node:path')
|
|
18
21
|
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
}
|
|
22
|
+
// 需要从 .gitignore 清理的旧条目
|
|
23
|
+
const LEGACY_GITIGNORE_ENTRIES = [
|
|
24
|
+
'.bmad-core',
|
|
25
|
+
'.bmad-method',
|
|
26
|
+
'.claude/commands/BMad',
|
|
27
|
+
'{output_folder}', // v6.0.0-Beta.8 bug 产物
|
|
28
|
+
]
|
|
28
29
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
send "1\\r"
|
|
35
|
-
expect "How would you like to proceed?"
|
|
36
|
-
send "1\\r"
|
|
37
|
-
expect eof
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
execSync(`expect -c '${expectScript}'`, {
|
|
41
|
-
stdio: 'inherit',
|
|
42
|
-
cwd: process.cwd(),
|
|
43
|
-
shell: true
|
|
44
|
-
})
|
|
45
|
-
}
|
|
30
|
+
// V6 新版 .gitignore 条目
|
|
31
|
+
const V6_GITIGNORE_ENTRIES = [
|
|
32
|
+
'_bmad/',
|
|
33
|
+
'_bmad-output/',
|
|
34
|
+
]
|
|
46
35
|
|
|
47
|
-
//
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log(' 1. 选择 "Upgrade BMad core" (升级 BMad 核心)')
|
|
52
|
-
console.log(' 2. 选择 "Backup and overwrite modified files" (备份并覆盖修改的文件)')
|
|
53
|
-
console.log('')
|
|
54
|
-
|
|
55
|
-
execSync('npx bmad-method@latest install -f -d . -i claude-code', {
|
|
56
|
-
stdio: 'inherit',
|
|
57
|
-
cwd: process.cwd(),
|
|
58
|
-
shell: true
|
|
59
|
-
})
|
|
60
|
-
}
|
|
36
|
+
// 修复安装器 bug: {output_folder} 未解析为 _bmad-output (v6.0.0-Beta.8)
|
|
37
|
+
function fixOutputFolderBug(cwd) {
|
|
38
|
+
const buggyPath = path.join(cwd, '{output_folder}')
|
|
39
|
+
const correctPath = path.join(cwd, '_bmad-output')
|
|
61
40
|
|
|
62
|
-
|
|
63
|
-
// 检查是否已安装并获取版本
|
|
64
|
-
const manifestPath = path.join(process.cwd(), '.bmad-core', 'install-manifest.yaml')
|
|
65
|
-
let needsInstall = true
|
|
66
|
-
let currentVersion = null
|
|
41
|
+
if (!fs.existsSync(buggyPath)) return false
|
|
67
42
|
|
|
68
|
-
if (fs.existsSync(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
43
|
+
if (!fs.existsSync(correctPath)) {
|
|
44
|
+
// _bmad-output 不存在,直接重命名
|
|
45
|
+
fs.renameSync(buggyPath, correctPath)
|
|
46
|
+
console.log(' ✅ {output_folder} → _bmad-output/ (重命名)')
|
|
47
|
+
} else {
|
|
48
|
+
// _bmad-output 已存在,合并子目录后删除
|
|
49
|
+
const entries = fs.readdirSync(buggyPath, { withFileTypes: true })
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
const src = path.join(buggyPath, entry.name)
|
|
52
|
+
const dest = path.join(correctPath, entry.name)
|
|
53
|
+
if (!fs.existsSync(dest)) {
|
|
54
|
+
fs.renameSync(src, dest)
|
|
55
|
+
console.log(` ✅ 移动 ${entry.name} → _bmad-output/`)
|
|
76
56
|
}
|
|
57
|
+
}
|
|
58
|
+
fs.rmSync(buggyPath, { recursive: true, force: true })
|
|
59
|
+
console.log(' ✅ 已删除多余的 {output_folder}/')
|
|
60
|
+
}
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
77
63
|
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
function updateGitignore(cwd) {
|
|
65
|
+
const gitignorePath = path.join(cwd, '.gitignore')
|
|
66
|
+
let content = ''
|
|
67
|
+
let exists = false
|
|
80
68
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
69
|
+
if (fs.existsSync(gitignorePath)) {
|
|
70
|
+
content = fs.readFileSync(gitignorePath, 'utf8')
|
|
71
|
+
exists = true
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const lines = content.split('\n')
|
|
75
|
+
let changed = false
|
|
76
|
+
|
|
77
|
+
// 移除 V4 旧条目
|
|
78
|
+
const filtered = lines.filter(line => {
|
|
79
|
+
const trimmed = line.trim()
|
|
80
|
+
const isLegacy = LEGACY_GITIGNORE_ENTRIES.some(entry =>
|
|
81
|
+
trimmed === entry || trimmed === entry + '/' || trimmed === '/' + entry
|
|
82
|
+
)
|
|
83
|
+
if (isLegacy) {
|
|
84
|
+
console.log(` 🗑️ 移除旧条目: ${trimmed}`)
|
|
85
|
+
changed = true
|
|
89
86
|
}
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
return !isLegacy
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// 添加 V6 新条目
|
|
91
|
+
const newEntries = []
|
|
92
|
+
for (const entry of V6_GITIGNORE_ENTRIES) {
|
|
93
|
+
const entryBase = entry.replace(/\/$/, '')
|
|
94
|
+
const alreadyExists = filtered.some(line => {
|
|
95
|
+
const trimmed = line.trim()
|
|
96
|
+
return trimmed === entry || trimmed === entryBase || trimmed === '/' + entryBase
|
|
97
|
+
})
|
|
98
|
+
if (!alreadyExists) {
|
|
99
|
+
newEntries.push(entry)
|
|
100
|
+
console.log(` ✅ 添加新条目: ${entry}`)
|
|
101
|
+
changed = true
|
|
92
102
|
}
|
|
93
103
|
}
|
|
94
104
|
|
|
95
|
-
if (
|
|
105
|
+
if (!changed) {
|
|
106
|
+
console.log(' ℹ️ .gitignore 已是最新,无需更新')
|
|
96
107
|
return
|
|
97
108
|
}
|
|
98
109
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
console.log('📋 使用自动化安装 (expect 工具可用)')
|
|
107
|
-
installWithExpect()
|
|
108
|
-
} else {
|
|
109
|
-
fallbackInstallation()
|
|
110
|
+
// 构建新内容
|
|
111
|
+
let result = filtered.join('\n')
|
|
112
|
+
|
|
113
|
+
if (newEntries.length > 0) {
|
|
114
|
+
// 确保末尾有换行,然后添加 BMad 区块
|
|
115
|
+
if (result.length > 0 && !result.endsWith('\n')) {
|
|
116
|
+
result += '\n'
|
|
110
117
|
}
|
|
118
|
+
result += '\n# BMad Method V6\n'
|
|
119
|
+
result += newEntries.join('\n') + '\n'
|
|
120
|
+
}
|
|
111
121
|
|
|
122
|
+
fs.writeFileSync(gitignorePath, result, 'utf8')
|
|
123
|
+
console.log(` 📝 .gitignore 已${exists ? '更新' : '创建'}`)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function initBmad() {
|
|
127
|
+
const cwd = process.cwd()
|
|
128
|
+
const bmadV6Path = path.join(cwd, '_bmad')
|
|
129
|
+
const legacyCorePath = path.join(cwd, '.bmad-core')
|
|
130
|
+
const legacyMethodPath = path.join(cwd, '.bmad-method')
|
|
131
|
+
|
|
132
|
+
// 检查旧版 V4 安装
|
|
133
|
+
const hasLegacyCore = fs.existsSync(legacyCorePath)
|
|
134
|
+
const hasLegacyMethod = fs.existsSync(legacyMethodPath)
|
|
135
|
+
|
|
136
|
+
if (hasLegacyCore || hasLegacyMethod) {
|
|
137
|
+
console.log('⚠️ 检测到旧版 BMad V4 安装:')
|
|
138
|
+
if (hasLegacyCore) console.log(' • .bmad-core/ (V4 核心目录)')
|
|
139
|
+
if (hasLegacyMethod) console.log(' • .bmad-method/ (V4 方法目录)')
|
|
140
|
+
console.log('')
|
|
141
|
+
console.log('📌 V6 安装器会自动处理旧版迁移,请在安装过程中按提示操作。')
|
|
142
|
+
console.log(' 详情参考:https://bmad-code-org.github.io/BMAD-METHOD/docs/how-to/upgrade-to-v6')
|
|
143
|
+
console.log('')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 检查 V6 是否已安装
|
|
147
|
+
const hasV6 = fs.existsSync(bmadV6Path)
|
|
148
|
+
|
|
149
|
+
// 构建非交互式安装命令
|
|
150
|
+
let installCmd
|
|
151
|
+
if (hasV6) {
|
|
152
|
+
console.log('🔄 检测到已有 BMad V6 安装,将执行快速更新...')
|
|
153
|
+
console.log('')
|
|
154
|
+
installCmd = [
|
|
155
|
+
'npx bmad-method install',
|
|
156
|
+
'--directory .',
|
|
157
|
+
'--action quick-update',
|
|
158
|
+
'--yes',
|
|
159
|
+
].join(' ')
|
|
160
|
+
} else {
|
|
161
|
+
console.log('🚀 正在初始化 BMad-Method V6...')
|
|
162
|
+
console.log('')
|
|
163
|
+
installCmd = [
|
|
164
|
+
'npx bmad-method install',
|
|
165
|
+
'--directory .',
|
|
166
|
+
'--modules bmm',
|
|
167
|
+
'--tools claude-code',
|
|
168
|
+
'--communication-language Chinese',
|
|
169
|
+
'--document-output-language Chinese',
|
|
170
|
+
'--yes',
|
|
171
|
+
].join(' ')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 执行安装
|
|
175
|
+
try {
|
|
176
|
+
console.log(`📋 执行: ${installCmd}`)
|
|
112
177
|
console.log('')
|
|
113
|
-
|
|
178
|
+
execSync(installCmd, {
|
|
179
|
+
stdio: 'inherit',
|
|
180
|
+
cwd: cwd,
|
|
181
|
+
shell: true
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
console.log('')
|
|
185
|
+
console.log('✅ BMad-Method V6 安装/更新完成!')
|
|
114
186
|
console.log('')
|
|
115
187
|
console.log('═══════════════════════════════════════════════════════════════')
|
|
116
|
-
console.log('📌 重要提示:请重启
|
|
188
|
+
console.log('📌 重要提示:请重启 AI IDE 以加载 BMad 扩展')
|
|
117
189
|
console.log('═══════════════════════════════════════════════════════════════')
|
|
118
190
|
console.log('')
|
|
119
|
-
|
|
120
|
-
console.log('
|
|
121
|
-
|
|
191
|
+
// 修复 {output_folder} bug (v6.0.0-Beta.8)
|
|
192
|
+
console.log('🔧 检查安装器已知问题...')
|
|
193
|
+
try {
|
|
194
|
+
const fixed = fixOutputFolderBug(cwd)
|
|
195
|
+
if (!fixed) console.log(' ℹ️ 无需修复')
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.log(` ⚠️ 修复 {output_folder} 失败: ${err.message}`)
|
|
198
|
+
console.log(' 请手动将 {output_folder}/ 重命名为 _bmad-output/')
|
|
199
|
+
}
|
|
200
|
+
console.log('')
|
|
201
|
+
|
|
202
|
+
console.log('📂 V6 目录结构:')
|
|
203
|
+
console.log(' • _bmad/ — agents、workflows、tasks 和配置')
|
|
204
|
+
console.log(' • _bmad-output/ — 生成的工件输出目录')
|
|
122
205
|
console.log('')
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
console.log('
|
|
126
|
-
|
|
127
|
-
|
|
206
|
+
|
|
207
|
+
// 自动更新 .gitignore
|
|
208
|
+
console.log('🔧 更新 .gitignore...')
|
|
209
|
+
try {
|
|
210
|
+
updateGitignore(cwd)
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.log(' ⚠️ 自动更新 .gitignore 失败,请手动添加 _bmad/ 和 _bmad-output/')
|
|
213
|
+
}
|
|
128
214
|
console.log('')
|
|
129
215
|
console.log('🚀 快速开始:')
|
|
130
|
-
console.log(' 1. 重启
|
|
131
|
-
console.log(' 2.
|
|
132
|
-
console.log('
|
|
133
|
-
console.log(' 这将启动 BMad 工作流引导系统')
|
|
216
|
+
console.log(' 1. 重启 AI IDE')
|
|
217
|
+
console.log(' 2. 运行 /bmad-help 获取引导和下一步建议')
|
|
218
|
+
console.log(' 3. 输入 /bmad 并使用自动补全浏览可用命令')
|
|
134
219
|
console.log('')
|
|
135
|
-
console.log('💡
|
|
136
|
-
console.log('
|
|
220
|
+
console.log('💡 常用工作流:')
|
|
221
|
+
console.log(' • /bmad-help — 交互式帮助')
|
|
222
|
+
console.log(' • /bmad-bmm-create-prd — 创建产品需求文档')
|
|
223
|
+
console.log(' • /bmad-bmm-create-architecture — 创建架构文档')
|
|
224
|
+
console.log(' • /bmad-bmm-create-epics-and-stories — 创建史诗和用户故事')
|
|
225
|
+
console.log(' • /bmad-bmm-sprint-planning — 初始化 Sprint 计划')
|
|
226
|
+
console.log(' • /bmad-bmm-dev-story — 实现用户故事')
|
|
227
|
+
|
|
228
|
+
// 清理旧版 V4 IDE 命令提醒
|
|
229
|
+
const legacyClaudeAgents = path.join(cwd, '.claude', 'commands', 'BMad', 'agents')
|
|
230
|
+
const legacyClaudeTasks = path.join(cwd, '.claude', 'commands', 'BMad', 'tasks')
|
|
231
|
+
if (fs.existsSync(legacyClaudeAgents) || fs.existsSync(legacyClaudeTasks)) {
|
|
232
|
+
console.log('')
|
|
233
|
+
console.log('⚠️ 检测到旧版 V4 IDE 命令,建议手动删除:')
|
|
234
|
+
if (fs.existsSync(legacyClaudeAgents)) console.log(' • .claude/commands/BMad/agents/')
|
|
235
|
+
if (fs.existsSync(legacyClaudeTasks)) console.log(' • .claude/commands/BMad/tasks/')
|
|
236
|
+
console.log(' 新的 V6 命令已安装在 .claude/commands/bmad/ 下')
|
|
237
|
+
}
|
|
137
238
|
}
|
|
138
239
|
catch (error) {
|
|
139
240
|
console.error('❌ 安装失败:', error.message)
|
|
140
241
|
console.log('')
|
|
141
242
|
console.log('🛠️ 手动安装指南:')
|
|
142
|
-
console.log('
|
|
143
|
-
console.log('
|
|
144
|
-
console.log('')
|
|
145
|
-
console.log('
|
|
146
|
-
console.log('
|
|
147
|
-
console.log('
|
|
243
|
+
console.log(' 1. 确保已安装 Node.js 20+')
|
|
244
|
+
console.log(' 2. 非交互式安装:')
|
|
245
|
+
console.log(' npx bmad-method install --directory . --modules bmm --tools claude-code --communication-language Chinese --document-output-language Chinese --yes')
|
|
246
|
+
console.log(' 3. 快速更新已有安装:')
|
|
247
|
+
console.log(' npx bmad-method install --directory . --action quick-update --yes')
|
|
248
|
+
console.log(' 4. 或交互式安装:')
|
|
249
|
+
console.log(' npx bmad-method install')
|
|
148
250
|
console.log('')
|
|
149
|
-
console.log('
|
|
150
|
-
console.log('
|
|
151
|
-
console.log(' • Ubuntu: sudo apt-get install expect')
|
|
152
|
-
console.log(' • CentOS: sudo yum install expect')
|
|
251
|
+
console.log('📖 详细文档:')
|
|
252
|
+
console.log(' https://bmad-code-org.github.io/BMAD-METHOD/docs/how-to/install-bmad')
|
|
153
253
|
}
|
|
154
254
|
}
|
|
155
255
|
|
|
@@ -167,6 +267,9 @@ initBmad()
|
|
|
167
267
|
|
|
168
268
|
此命令将:
|
|
169
269
|
|
|
170
|
-
1.
|
|
171
|
-
2.
|
|
172
|
-
3.
|
|
270
|
+
1. 检测现有安装状态(V6 / V4 旧版 / 未安装)
|
|
271
|
+
2. 新安装:`npx bmad-method install --directory . --modules bmm --tools claude-code --communication-language Chinese --document-output-language Chinese --yes`
|
|
272
|
+
3. 已安装:`npx bmad-method install --directory . --action quick-update --yes`
|
|
273
|
+
4. 修复 `{output_folder}` → `_bmad-output` 安装器 bug
|
|
274
|
+
5. 自动更新 `.gitignore`(清理旧条目,添加 V6 条目)
|
|
275
|
+
6. 提供后续步骤建议
|