bmad-method 4.35.2 → 4.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/discord.yaml +16 -0
- package/CHANGELOG.md +14 -2
- package/README.md +42 -9
- package/dist/agents/dev.txt +9 -9
- package/dist/teams/team-all.txt +9 -9
- package/dist/teams/team-ide-minimal.txt +9 -9
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +102 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash copy.txt +13 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.1 Google Cloud Project Setup/1.1.1 - Initial Project Configuration - bash.txt +13 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.2 Agent Development Kit Installation/1.2.2 - Basic Project Structure - txt.txt +25 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.1 - settings.py +34 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.3 Core Configuration Files/1.3.2 - main.py - Base Application.py +70 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.4 Deployment Configuration/1.4.2 - cloudbuild.yaml +26 -0
- package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +109 -0
- package/package.json +2 -2
- package/tools/flattener/aggregate.js +76 -0
- package/tools/flattener/binary.js +53 -0
- package/tools/flattener/discovery.js +70 -0
- package/tools/flattener/files.js +35 -0
- package/tools/flattener/ignoreRules.js +176 -0
- package/tools/flattener/main.js +113 -466
- package/tools/flattener/projectRoot.js +45 -0
- package/tools/flattener/prompts.js +44 -0
- package/tools/flattener/stats.js +30 -0
- package/tools/flattener/xml.js +86 -0
- package/tools/installer/lib/installer.js +63 -1
- package/tools/installer/package.json +1 -1
- package/tools/shared/bannerArt.js +105 -0
- package/tools/installer/package-lock.json +0 -906
- /package/{bmad-core → docs}/enhanced-ide-development-workflow.md +0 -0
- /package/{bmad-core → docs}/user-guide.md +0 -0
- /package/{bmad-core → docs}/working-in-the-brownfield.md +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Attempt to find the project root by walking up from startDir
|
|
6
|
+
* Looks for common project markers like .git, package.json, pyproject.toml, etc.
|
|
7
|
+
* @param {string} startDir
|
|
8
|
+
* @returns {Promise<string|null>} project root directory or null if not found
|
|
9
|
+
*/
|
|
10
|
+
async function findProjectRoot(startDir) {
|
|
11
|
+
try {
|
|
12
|
+
let dir = path.resolve(startDir);
|
|
13
|
+
const root = path.parse(dir).root;
|
|
14
|
+
const markers = [
|
|
15
|
+
".git",
|
|
16
|
+
"package.json",
|
|
17
|
+
"pnpm-workspace.yaml",
|
|
18
|
+
"yarn.lock",
|
|
19
|
+
"pnpm-lock.yaml",
|
|
20
|
+
"pyproject.toml",
|
|
21
|
+
"requirements.txt",
|
|
22
|
+
"go.mod",
|
|
23
|
+
"Cargo.toml",
|
|
24
|
+
"composer.json",
|
|
25
|
+
".hg",
|
|
26
|
+
".svn",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
while (true) {
|
|
30
|
+
const exists = await Promise.all(
|
|
31
|
+
markers.map((m) => fs.pathExists(path.join(dir, m))),
|
|
32
|
+
);
|
|
33
|
+
if (exists.some(Boolean)) {
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
if (dir === root) break;
|
|
37
|
+
dir = path.dirname(dir);
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { findProjectRoot };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const os = require("node:os");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const readline = require("node:readline");
|
|
4
|
+
const process = require("node:process");
|
|
5
|
+
|
|
6
|
+
function expandHome(p) {
|
|
7
|
+
if (!p) return p;
|
|
8
|
+
if (p.startsWith("~")) return path.join(os.homedir(), p.slice(1));
|
|
9
|
+
return p;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createRl() {
|
|
13
|
+
return readline.createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function promptQuestion(question) {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const rl = createRl();
|
|
22
|
+
rl.question(question, (answer) => {
|
|
23
|
+
rl.close();
|
|
24
|
+
resolve(answer);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function promptYesNo(question, defaultYes = true) {
|
|
30
|
+
const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
|
|
31
|
+
const ans = (await promptQuestion(`${question}${suffix}`)).trim().toLowerCase();
|
|
32
|
+
if (!ans) return defaultYes;
|
|
33
|
+
if (["y", "yes"].includes(ans)) return true;
|
|
34
|
+
if (["n", "no"].includes(ans)) return false;
|
|
35
|
+
return promptYesNo(question, defaultYes);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function promptPath(question, defaultValue) {
|
|
39
|
+
const prompt = `${question}${defaultValue ? ` (default: ${defaultValue})` : ""}: `;
|
|
40
|
+
const ans = (await promptQuestion(prompt)).trim();
|
|
41
|
+
return expandHome(ans || defaultValue);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { promptYesNo, promptPath, promptQuestion, expandHome };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
function calculateStatistics(aggregatedContent, xmlFileSize) {
|
|
2
|
+
const { textFiles, binaryFiles, errors } = aggregatedContent;
|
|
3
|
+
|
|
4
|
+
const totalTextSize = textFiles.reduce((sum, file) => sum + file.size, 0);
|
|
5
|
+
const totalBinarySize = binaryFiles.reduce((sum, file) => sum + file.size, 0);
|
|
6
|
+
const totalSize = totalTextSize + totalBinarySize;
|
|
7
|
+
|
|
8
|
+
const totalLines = textFiles.reduce((sum, file) => sum + file.lines, 0);
|
|
9
|
+
|
|
10
|
+
const estimatedTokens = Math.ceil(xmlFileSize / 4);
|
|
11
|
+
|
|
12
|
+
const formatSize = (bytes) => {
|
|
13
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
14
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
15
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
totalFiles: textFiles.length + binaryFiles.length,
|
|
20
|
+
textFiles: textFiles.length,
|
|
21
|
+
binaryFiles: binaryFiles.length,
|
|
22
|
+
errorFiles: errors.length,
|
|
23
|
+
totalSize: formatSize(totalSize),
|
|
24
|
+
xmlSize: formatSize(xmlFileSize),
|
|
25
|
+
totalLines,
|
|
26
|
+
estimatedTokens: estimatedTokens.toLocaleString(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { calculateStatistics };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
|
|
3
|
+
function escapeXml(str) {
|
|
4
|
+
if (typeof str !== "string") {
|
|
5
|
+
return String(str);
|
|
6
|
+
}
|
|
7
|
+
return str
|
|
8
|
+
.replace(/&/g, "&")
|
|
9
|
+
.replace(/</g, "<")
|
|
10
|
+
.replace(/'/g, "'");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function indentFileContent(content) {
|
|
14
|
+
if (typeof content !== "string") {
|
|
15
|
+
return String(content);
|
|
16
|
+
}
|
|
17
|
+
return content.split("\n").map((line) => ` ${line}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function generateXMLOutput(aggregatedContent, outputPath) {
|
|
21
|
+
const { textFiles } = aggregatedContent;
|
|
22
|
+
const writeStream = fs.createWriteStream(outputPath, { encoding: "utf8" });
|
|
23
|
+
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
writeStream.on("error", reject);
|
|
26
|
+
writeStream.on("finish", resolve);
|
|
27
|
+
|
|
28
|
+
writeStream.write('<?xml version="1.0" encoding="UTF-8"?>\n');
|
|
29
|
+
writeStream.write("<files>\n");
|
|
30
|
+
|
|
31
|
+
// Sort files by path for deterministic order
|
|
32
|
+
const filesSorted = [...textFiles].sort((a, b) =>
|
|
33
|
+
a.path.localeCompare(b.path)
|
|
34
|
+
);
|
|
35
|
+
let index = 0;
|
|
36
|
+
|
|
37
|
+
const writeNext = () => {
|
|
38
|
+
if (index >= filesSorted.length) {
|
|
39
|
+
writeStream.write("</files>\n");
|
|
40
|
+
writeStream.end();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const file = filesSorted[index++];
|
|
45
|
+
const p = escapeXml(file.path);
|
|
46
|
+
const content = typeof file.content === "string" ? file.content : "";
|
|
47
|
+
|
|
48
|
+
if (content.length === 0) {
|
|
49
|
+
writeStream.write(`\t<file path='${p}'/>\n`);
|
|
50
|
+
setTimeout(writeNext, 0);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const needsCdata = content.includes("<") || content.includes("&") ||
|
|
55
|
+
content.includes("]]>");
|
|
56
|
+
if (needsCdata) {
|
|
57
|
+
// Open tag and CDATA on their own line with tab indent; content lines indented with two tabs
|
|
58
|
+
writeStream.write(`\t<file path='${p}'><![CDATA[\n`);
|
|
59
|
+
// Safely split any occurrences of "]]>" inside content, trim trailing newlines, indent each line with two tabs
|
|
60
|
+
const safe = content.replace(/]]>/g, "]]]]><![CDATA[>");
|
|
61
|
+
const trimmed = safe.replace(/[\r\n]+$/, "");
|
|
62
|
+
const indented = trimmed.length > 0
|
|
63
|
+
? trimmed.split("\n").map((line) => `\t\t${line}`).join("\n")
|
|
64
|
+
: "";
|
|
65
|
+
writeStream.write(indented);
|
|
66
|
+
// Close CDATA and attach closing tag directly after the last content line
|
|
67
|
+
writeStream.write("]]></file>\n");
|
|
68
|
+
} else {
|
|
69
|
+
// Write opening tag then newline; indent content with two tabs; attach closing tag directly after last content char
|
|
70
|
+
writeStream.write(`\t<file path='${p}'>\n`);
|
|
71
|
+
const trimmed = content.replace(/[\r\n]+$/, "");
|
|
72
|
+
const indented = trimmed.length > 0
|
|
73
|
+
? trimmed.split("\n").map((line) => `\t\t${line}`).join("\n")
|
|
74
|
+
: "";
|
|
75
|
+
writeStream.write(indented);
|
|
76
|
+
writeStream.write(`</file>\n`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setTimeout(writeNext, 0);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
writeNext();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { generateXMLOutput };
|
|
@@ -237,6 +237,10 @@ class Installer {
|
|
|
237
237
|
// Copy common/ items to .bmad-core
|
|
238
238
|
spinner.text = "Copying common utilities...";
|
|
239
239
|
await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
240
|
+
|
|
241
|
+
// Copy documentation files from docs/ to .bmad-core
|
|
242
|
+
spinner.text = "Copying documentation files...";
|
|
243
|
+
await this.copyDocsItems(installDir, ".bmad-core", spinner);
|
|
240
244
|
|
|
241
245
|
// Get list of all files for manifest
|
|
242
246
|
const foundFiles = await resourceLocator.findFiles("**/*", {
|
|
@@ -308,6 +312,11 @@ class Installer {
|
|
|
308
312
|
spinner.text = "Copying common utilities...";
|
|
309
313
|
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
310
314
|
files.push(...commonFiles);
|
|
315
|
+
|
|
316
|
+
// Copy documentation files from docs/ to .bmad-core
|
|
317
|
+
spinner.text = "Copying documentation files...";
|
|
318
|
+
const docFiles = await this.copyDocsItems(installDir, ".bmad-core", spinner);
|
|
319
|
+
files.push(...docFiles);
|
|
311
320
|
} else if (config.installType === "team") {
|
|
312
321
|
// Team installation
|
|
313
322
|
spinner.text = `Installing ${config.team} team...`;
|
|
@@ -353,6 +362,11 @@ class Installer {
|
|
|
353
362
|
spinner.text = "Copying common utilities...";
|
|
354
363
|
const commonFiles = await this.copyCommonItems(installDir, ".bmad-core", spinner);
|
|
355
364
|
files.push(...commonFiles);
|
|
365
|
+
|
|
366
|
+
// Copy documentation files from docs/ to .bmad-core
|
|
367
|
+
spinner.text = "Copying documentation files...";
|
|
368
|
+
const docFiles = await this.copyDocsItems(installDir, ".bmad-core", spinner);
|
|
369
|
+
files.push(...docFiles);
|
|
356
370
|
} else if (config.installType === "expansion-only") {
|
|
357
371
|
// Expansion-only installation - DO NOT create .bmad-core
|
|
358
372
|
// Only install expansion packs
|
|
@@ -896,7 +910,7 @@ class Installer {
|
|
|
896
910
|
}
|
|
897
911
|
|
|
898
912
|
// Important notice to read the user guide
|
|
899
|
-
console.log(chalk.red.bold("\n📖 IMPORTANT: Please read the user guide installed at .bmad-core/user-guide.md"));
|
|
913
|
+
console.log(chalk.red.bold("\n📖 IMPORTANT: Please read the user guide at docs/user-guide.md (also installed at .bmad-core/user-guide.md)"));
|
|
900
914
|
console.log(chalk.red("This guide contains essential information about the BMad workflow and how to use the agents effectively."));
|
|
901
915
|
}
|
|
902
916
|
|
|
@@ -1557,6 +1571,54 @@ class Installer {
|
|
|
1557
1571
|
return copiedFiles;
|
|
1558
1572
|
}
|
|
1559
1573
|
|
|
1574
|
+
async copyDocsItems(installDir, targetSubdir, spinner) {
|
|
1575
|
+
const fs = require('fs').promises;
|
|
1576
|
+
const sourceBase = path.dirname(path.dirname(path.dirname(path.dirname(__filename)))); // Go up to project root
|
|
1577
|
+
const docsPath = path.join(sourceBase, 'docs');
|
|
1578
|
+
const targetPath = path.join(installDir, targetSubdir);
|
|
1579
|
+
const copiedFiles = [];
|
|
1580
|
+
|
|
1581
|
+
// Specific documentation files to copy
|
|
1582
|
+
const docFiles = [
|
|
1583
|
+
'enhanced-ide-development-workflow.md',
|
|
1584
|
+
'user-guide.md',
|
|
1585
|
+
'working-in-the-brownfield.md'
|
|
1586
|
+
];
|
|
1587
|
+
|
|
1588
|
+
// Check if docs/ exists
|
|
1589
|
+
if (!(await fileManager.pathExists(docsPath))) {
|
|
1590
|
+
console.warn('Warning: docs/ folder not found');
|
|
1591
|
+
return copiedFiles;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// Copy specific documentation files from docs/ to target
|
|
1595
|
+
for (const docFile of docFiles) {
|
|
1596
|
+
const sourcePath = path.join(docsPath, docFile);
|
|
1597
|
+
const destPath = path.join(targetPath, docFile);
|
|
1598
|
+
|
|
1599
|
+
// Check if the source file exists
|
|
1600
|
+
if (await fileManager.pathExists(sourcePath)) {
|
|
1601
|
+
// Read the file content
|
|
1602
|
+
const content = await fs.readFile(sourcePath, 'utf8');
|
|
1603
|
+
|
|
1604
|
+
// Replace {root} with the target subdirectory
|
|
1605
|
+
const updatedContent = content.replace(/\{root\}/g, targetSubdir);
|
|
1606
|
+
|
|
1607
|
+
// Ensure directory exists
|
|
1608
|
+
await fileManager.ensureDirectory(path.dirname(destPath));
|
|
1609
|
+
|
|
1610
|
+
// Write the updated content
|
|
1611
|
+
await fs.writeFile(destPath, updatedContent, 'utf8');
|
|
1612
|
+
copiedFiles.push(path.join(targetSubdir, docFile));
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
if (copiedFiles.length > 0) {
|
|
1617
|
+
console.log(chalk.dim(` Added ${copiedFiles.length} documentation files`));
|
|
1618
|
+
}
|
|
1619
|
+
return copiedFiles;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1560
1622
|
async detectExpansionPacks(installDir) {
|
|
1561
1623
|
const expansionPacks = {};
|
|
1562
1624
|
const glob = require("glob");
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// ASCII banner art definitions extracted from banners.js to separate art from logic
|
|
2
|
+
|
|
3
|
+
const BMAD_TITLE = "BMAD-METHOD";
|
|
4
|
+
const FLATTENER_TITLE = "FLATTENER";
|
|
5
|
+
const INSTALLER_TITLE = "INSTALLER";
|
|
6
|
+
|
|
7
|
+
// Large ASCII blocks (block-style fonts)
|
|
8
|
+
const BMAD_LARGE = `
|
|
9
|
+
██████╗ ███╗ ███╗ █████╗ ██████╗ ███╗ ███╗███████╗████████╗██╗ ██╗ ██████╗ ██████╗
|
|
10
|
+
██╔══██╗████╗ ████║██╔══██╗██╔══██╗ ████╗ ████║██╔════╝╚══██╔══╝██║ ██║██╔═══██╗██╔══██╗
|
|
11
|
+
██████╔╝██╔████╔██║███████║██║ ██║█████╗██╔████╔██║█████╗ ██║ ███████║██║ ██║██║ ██║
|
|
12
|
+
██╔══██╗██║╚██╔╝██║██╔══██║██║ ██║╚════╝██║╚██╔╝██║██╔══╝ ██║ ██╔══██║██║ ██║██║ ██║
|
|
13
|
+
██████╔╝██║ ╚═╝ ██║██║ ██║██████╔╝ ██║ ╚═╝ ██║███████╗ ██║ ██║ ██║╚██████╔╝██████╔╝
|
|
14
|
+
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const FLATTENER_LARGE = `
|
|
18
|
+
███████╗██╗ █████╗ ████████╗████████╗███████╗███╗ ██╗███████╗██████╗
|
|
19
|
+
██╔════╝██║ ██╔══██╗╚══██╔══╝╚══██╔══╝██╔════╝████╗ ██║██╔════╝██╔══██╗
|
|
20
|
+
█████╗ ██║ ███████║ ██║ ██║ █████╗ ██╔██╗ ██║█████╗ ██████╔╝
|
|
21
|
+
██╔══╝ ██║ ██╔══██║ ██║ ██║ ██╔══╝ ██║╚██╗██║██╔══╝ ██╔══██╗
|
|
22
|
+
██║ ███████║██║ ██║ ██║ ██║ ███████╗██║ ╚████║███████╗██║ ██║
|
|
23
|
+
╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const INSTALLER_LARGE = `
|
|
27
|
+
██╗███╗ ██╗███████╗████████╗ █████╗ ██╗ ██╗ ███████╗██████╗
|
|
28
|
+
██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██║ ██║ ██╔════╝██╔══██╗
|
|
29
|
+
██║██╔██╗ ██║███████╗ ██║ ███████║██║ ██║ █████╗ ██████╔╝
|
|
30
|
+
██║██║╚██╗██║╚════██║ ██║ ██╔══██║██║ ██║ ██╔══╝ ██╔══██╗
|
|
31
|
+
██║██║ ╚████║███████║ ██║ ██║ ██║███████╗███████╗███████╗██║ ██║
|
|
32
|
+
╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
// Curated medium/small/tiny variants (fixed art, no runtime scaling)
|
|
36
|
+
// Medium: bold framed title with heavy fill (high contrast, compact)
|
|
37
|
+
const BMAD_MEDIUM = `
|
|
38
|
+
███╗ █╗ █╗ ██╗ ███╗ █╗ █╗███╗█████╗█╗ █╗ ██╗ ███╗
|
|
39
|
+
█╔═█╗██╗ ██║█╔═█╗█╔═█╗ ██╗ ██║█╔═╝╚═█╔═╝█║ █║█╔═█╗█╔═█╗
|
|
40
|
+
███╔╝█╔███╔█║████║█║ █║██╗█╔███╔█║██╗ █║ ████║█║ █║█║ █║
|
|
41
|
+
█╔═█╗█║ █╔╝█║█╔═█║█║ █║╚═╝█║ █╔╝█║█╔╝ █║ █╔═█║█║ █║█║ █║
|
|
42
|
+
███╔╝█║ ╚╝ █║█║ █║███╔╝ █║ ╚╝ █║███╗ █║ █║ █║╚██╔╝███╔╝
|
|
43
|
+
╚══╝ ╚╝ ╚╝╚╝ ╚╝╚══╝ ╚╝ ╚╝╚══╝ ╚╝ ╚╝ ╚╝ ╚═╝ ╚══╝
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const FLATTENER_MEDIUM = `
|
|
47
|
+
███╗█╗ ██╗ █████╗█████╗███╗█╗ █╗███╗███╗
|
|
48
|
+
█╔═╝█║ █╔═█╗╚═█╔═╝╚═█╔═╝█╔═╝██╗ █║█╔═╝█╔═█╗
|
|
49
|
+
██╗ █║ ████║ █║ █║ ██╗ █╔█╗█║██╗ ███╔╝
|
|
50
|
+
█╔╝ █║ █╔═█║ █║ █║ █╔╝ █║ ██║█╔╝ █╔═█╗
|
|
51
|
+
█║ ███║█║ █║ █║ █║ ███╗█║ █║███╗█║ █║
|
|
52
|
+
╚╝ ╚══╝╚╝ ╚╝ ╚╝ ╚╝ ╚══╝╚╝ ╚╝╚══╝╚╝ ╚╝
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
const INSTALLER_MEDIUM = `
|
|
56
|
+
█╗█╗ █╗████╗█████╗ ██╗ █╗ █╗ ███╗███╗
|
|
57
|
+
█║██╗ █║█╔══╝╚═█╔═╝█╔═█╗█║ █║ █╔═╝█╔═█╗
|
|
58
|
+
█║█╔█╗█║████╗ █║ ████║█║ █║ ██╗ ███╔╝
|
|
59
|
+
█║█║ ██║╚══█║ █║ █╔═█║█║ █║ █╔╝ █╔═█╗
|
|
60
|
+
█║█║ █║████║ █║ █║ █║███╗███╗███╗█║ █║
|
|
61
|
+
╚╝╚╝ ╚╝╚═══╝ ╚╝ ╚╝ ╚╝╚══╝╚══╝╚══╝╚╝ ╚╝
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
// Small: rounded box with bold rule
|
|
65
|
+
// Width: 30 columns total (28 inner)
|
|
66
|
+
const BMAD_SMALL = `
|
|
67
|
+
╭──────────────────────────╮
|
|
68
|
+
│ BMAD-METHOD │
|
|
69
|
+
╰──────────────────────────╯
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
const FLATTENER_SMALL = `
|
|
73
|
+
╭──────────────────────────╮
|
|
74
|
+
│ FLATTENER │
|
|
75
|
+
╰──────────────────────────╯
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
const INSTALLER_SMALL = `
|
|
79
|
+
╭──────────────────────────╮
|
|
80
|
+
│ INSTALLER │
|
|
81
|
+
╰──────────────────────────╯
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
// Tiny (compact brackets)
|
|
85
|
+
const BMAD_TINY = `[ BMAD-METHOD ]`;
|
|
86
|
+
const FLATTENER_TINY = `[ FLATTENER ]`;
|
|
87
|
+
const INSTALLER_TINY = `[ INSTALLER ]`;
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
BMAD_TITLE,
|
|
91
|
+
FLATTENER_TITLE,
|
|
92
|
+
INSTALLER_TITLE,
|
|
93
|
+
BMAD_LARGE,
|
|
94
|
+
FLATTENER_LARGE,
|
|
95
|
+
INSTALLER_LARGE,
|
|
96
|
+
BMAD_MEDIUM,
|
|
97
|
+
FLATTENER_MEDIUM,
|
|
98
|
+
INSTALLER_MEDIUM,
|
|
99
|
+
BMAD_SMALL,
|
|
100
|
+
FLATTENER_SMALL,
|
|
101
|
+
INSTALLER_SMALL,
|
|
102
|
+
BMAD_TINY,
|
|
103
|
+
FLATTENER_TINY,
|
|
104
|
+
INSTALLER_TINY,
|
|
105
|
+
};
|