bmad-method 4.35.3 → 4.36.1

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.
Files changed (26) hide show
  1. package/.github/workflows/discord.yaml +16 -0
  2. package/.github/workflows/release.yaml +1 -1
  3. package/CHANGELOG.md +14 -2
  4. package/README.md +36 -3
  5. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/Complete AI Agent System - Flowchart.svg +102 -0
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/README.md +109 -0
  13. package/package.json +2 -2
  14. package/tools/flattener/aggregate.js +76 -0
  15. package/tools/flattener/binary.js +53 -0
  16. package/tools/flattener/discovery.js +70 -0
  17. package/tools/flattener/files.js +35 -0
  18. package/tools/flattener/ignoreRules.js +176 -0
  19. package/tools/flattener/main.js +113 -466
  20. package/tools/flattener/projectRoot.js +45 -0
  21. package/tools/flattener/prompts.js +44 -0
  22. package/tools/flattener/stats.js +30 -0
  23. package/tools/flattener/xml.js +86 -0
  24. package/tools/installer/package.json +1 -1
  25. package/tools/shared/bannerArt.js +105 -0
  26. package/tools/installer/package-lock.json +0 -906
@@ -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, "&amp;")
9
+ .replace(/</g, "&lt;")
10
+ .replace(/'/g, "&apos;");
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 };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmad-method",
3
- "version": "4.35.3",
3
+ "version": "4.36.1",
4
4
  "description": "BMad Method installer - AI-powered Agile development framework",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {
@@ -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
+ };