bmad-plus 0.4.0 → 0.4.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/CHANGELOG.md +29 -0
- package/README.md +13 -56
- package/osint-agent-package/skills/bmad-osint-investigate/osint/SKILL.md +452 -452
- package/osint-agent-package/skills/bmad-osint-investigate/osint/assets/dossier-template.md +116 -116
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/content-extraction.md +100 -100
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/platforms.md +130 -130
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/psychoprofile.md +69 -69
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/tools.md +281 -281
- package/osint-agent-package/skills/bmad-osint-investigate/osint/scripts/mcp-client.py +136 -136
- package/package.json +1 -1
- package/readme-international/README.de.md +1 -1
- package/readme-international/README.es.md +1 -1
- package/readme-international/README.fr.md +1 -1
- package/tools/cli/commands/install.js +88 -59
- package/tools/cli/i18n.js +501 -0
- package/oveanet-pack/animated-website/DEPLOYMENT.md +0 -104
- package/oveanet-pack/animated-website/README.md +0 -63
- package/oveanet-pack/animated-website/agent.yaml +0 -63
- package/oveanet-pack/seo-audit-360/DEPLOYMENT.md +0 -115
- package/oveanet-pack/seo-audit-360/README.md +0 -66
- package/oveanet-pack/seo-audit-360/agent.yaml +0 -70
- package/oveanet-pack/seo-audit-360/extensions/google-analytics/EXTENSION.md +0 -79
- package/oveanet-pack/seo-audit-360/extensions/google-analytics/ga4_client.py +0 -200
- package/oveanet-pack/seo-audit-360/extensions/google-analytics/requirements.txt +0 -4
- package/oveanet-pack/seo-audit-360/extensions/google-search-console/EXTENSION.md +0 -109
- package/oveanet-pack/seo-audit-360/extensions/google-search-console/gsc_client.py +0 -186
- package/oveanet-pack/seo-audit-360/extensions/google-search-console/requirements.txt +0 -4
- package/oveanet-pack/seo-audit-360/hooks/seo-check.sh +0 -95
- package/oveanet-pack/seo-audit-360/requirements.txt +0 -14
- package/oveanet-pack/seo-audit-360/scripts/__pycache__/seo_crawl.cpython-314.pyc +0 -0
- package/oveanet-pack/seo-audit-360/scripts/__pycache__/seo_parse.cpython-314.pyc +0 -0
- package/oveanet-pack/seo-audit-360/scripts/install.ps1 +0 -53
- package/oveanet-pack/seo-audit-360/scripts/install.sh +0 -48
- package/oveanet-pack/seo-audit-360/scripts/seo_apis.py +0 -464
- package/oveanet-pack/seo-audit-360/scripts/seo_crawl.py +0 -282
- package/oveanet-pack/seo-audit-360/scripts/seo_fetch.py +0 -231
- package/oveanet-pack/seo-audit-360/scripts/seo_parse.py +0 -255
- package/oveanet-pack/seo-audit-360/scripts/seo_report.py +0 -403
- package/oveanet-pack/seo-audit-360/scripts/seo_screenshot.py +0 -202
- package/oveanet-pack/seo-audit-360/tests/__pycache__/test_crawl.cpython-314-pytest-9.0.2.pyc +0 -0
- package/oveanet-pack/seo-audit-360/tests/__pycache__/test_parse.cpython-314-pytest-9.0.2.pyc +0 -0
- package/oveanet-pack/seo-audit-360/tests/fixtures/sample_page.html +0 -62
- package/oveanet-pack/seo-audit-360/tests/test_apis.py +0 -75
- package/oveanet-pack/seo-audit-360/tests/test_crawl.py +0 -121
- package/oveanet-pack/seo-audit-360/tests/test_fetch.py +0 -70
- package/oveanet-pack/seo-audit-360/tests/test_parse.py +0 -184
- package/oveanet-pack/universal-backup/DEPLOYMENT.md +0 -80
- package/oveanet-pack/universal-backup/README.md +0 -58
- package/oveanet-pack/universal-backup/agent.yaml +0 -45
- /package/{oveanet-pack/animated-website/agent → src/bmad-plus/agents/pack-animated}/animated-website-agent.md +0 -0
- /package/{oveanet-pack/animated-website → src/bmad-plus/agents/pack-animated}/templates/animated-website-workflow.md +0 -0
- /package/{oveanet-pack/universal-backup/agent → src/bmad-plus/agents/pack-backup}/backup-agent.md +0 -0
- /package/{oveanet-pack/universal-backup → src/bmad-plus/agents/pack-backup}/templates/backup-workflow.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/SKILL.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/checklist.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/pagespeed-playbook.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/audit-schema.json +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/cwv-thresholds.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/eeat-criteria.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/geo-signals.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/hreflang-rules.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/quality-gates.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/schema-catalog.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/schema-templates.json +0 -0
- /package/{oveanet-pack/seo-audit-360/agent → src/bmad-plus/agents/pack-seo}/seo-chief.md +0 -0
- /package/{oveanet-pack/seo-audit-360/agent → src/bmad-plus/agents/pack-seo}/seo-judge.md +0 -0
- /package/{oveanet-pack/seo-audit-360/agent → src/bmad-plus/agents/pack-seo}/seo-scout.md +0 -0
- /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/templates/seo-audit-workflow.md +0 -0
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Lightweight MCP client for Streamable HTTP/SSE transport.
|
|
3
|
-
|
|
4
|
-
Usage:
|
|
5
|
-
python3 mcp-client.py <mcp_url> --list-tools
|
|
6
|
-
python3 mcp-client.py <mcp_url> <tool_name> '<json_args>'
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import sys
|
|
11
|
-
import http.client
|
|
12
|
-
from urllib.parse import urlparse, urlencode
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def mcp_request(url: str, method: str, params: dict | None = None,
|
|
16
|
-
req_id: int = 1, session_id: str | None = None) -> tuple[dict, str | None]:
|
|
17
|
-
"""Send MCP JSON-RPC request and parse SSE response. Returns (result, session_id)."""
|
|
18
|
-
parsed = urlparse(url)
|
|
19
|
-
|
|
20
|
-
payload = {
|
|
21
|
-
"jsonrpc": "2.0",
|
|
22
|
-
"id": req_id,
|
|
23
|
-
"method": method,
|
|
24
|
-
}
|
|
25
|
-
if params:
|
|
26
|
-
payload["params"] = params
|
|
27
|
-
|
|
28
|
-
data = json.dumps(payload).encode()
|
|
29
|
-
|
|
30
|
-
headers = {
|
|
31
|
-
"Content-Type": "application/json",
|
|
32
|
-
"Accept": "application/json, text/event-stream",
|
|
33
|
-
}
|
|
34
|
-
if session_id:
|
|
35
|
-
headers["Mcp-Session-Id"] = session_id
|
|
36
|
-
|
|
37
|
-
conn = http.client.HTTPSConnection(parsed.hostname, timeout=120)
|
|
38
|
-
path = parsed.path
|
|
39
|
-
if parsed.query:
|
|
40
|
-
path += "?" + parsed.query
|
|
41
|
-
|
|
42
|
-
conn.request("POST", path, body=data, headers=headers)
|
|
43
|
-
resp = conn.getresponse()
|
|
44
|
-
|
|
45
|
-
# Get session ID from response headers
|
|
46
|
-
new_session_id = resp.getheader("Mcp-Session-Id") or session_id
|
|
47
|
-
|
|
48
|
-
body = resp.read().decode()
|
|
49
|
-
conn.close()
|
|
50
|
-
|
|
51
|
-
if resp.status >= 400:
|
|
52
|
-
return {"error": f"HTTP {resp.status}: {body[:200]}"}, new_session_id
|
|
53
|
-
|
|
54
|
-
# Parse SSE: look for data: lines
|
|
55
|
-
for line in body.split("\n"):
|
|
56
|
-
if line.startswith("data: "):
|
|
57
|
-
try:
|
|
58
|
-
return json.loads(line[6:]), new_session_id
|
|
59
|
-
except json.JSONDecodeError:
|
|
60
|
-
continue
|
|
61
|
-
|
|
62
|
-
# Try direct JSON
|
|
63
|
-
try:
|
|
64
|
-
return json.loads(body), new_session_id
|
|
65
|
-
except json.JSONDecodeError:
|
|
66
|
-
return {"raw": body[:500]}, new_session_id
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def init_and_call(url: str, method: str, params: dict | None = None) -> dict:
|
|
70
|
-
"""Initialize session then make a call."""
|
|
71
|
-
# Step 1: Initialize
|
|
72
|
-
init_result, session_id = mcp_request(url, "initialize", {
|
|
73
|
-
"protocolVersion": "2024-11-05",
|
|
74
|
-
"capabilities": {},
|
|
75
|
-
"clientInfo": {"name": "osint-skill", "version": "3.1"},
|
|
76
|
-
}, req_id=1)
|
|
77
|
-
|
|
78
|
-
if "error" in init_result:
|
|
79
|
-
return init_result
|
|
80
|
-
|
|
81
|
-
# Step 2: Send initialized notification (optional but polite)
|
|
82
|
-
# Step 3: Make actual call
|
|
83
|
-
result, _ = mcp_request(url, method, params, req_id=2, session_id=session_id)
|
|
84
|
-
return result
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def list_tools(url: str) -> list:
|
|
88
|
-
"""List available tools on MCP server."""
|
|
89
|
-
result = init_and_call(url, "tools/list")
|
|
90
|
-
tools = result.get("result", {}).get("tools", [])
|
|
91
|
-
return tools
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def call_tool(url: str, tool_name: str, arguments: dict) -> dict:
|
|
95
|
-
"""Call a specific tool on MCP server."""
|
|
96
|
-
result = init_and_call(url, "tools/call", {
|
|
97
|
-
"name": tool_name,
|
|
98
|
-
"arguments": arguments,
|
|
99
|
-
})
|
|
100
|
-
return result
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def main():
|
|
104
|
-
if len(sys.argv) < 3:
|
|
105
|
-
print(__doc__)
|
|
106
|
-
sys.exit(1)
|
|
107
|
-
|
|
108
|
-
url = sys.argv[1]
|
|
109
|
-
|
|
110
|
-
if sys.argv[2] == "--list-tools":
|
|
111
|
-
tools = list_tools(url)
|
|
112
|
-
if not tools:
|
|
113
|
-
print("No tools found or error occurred")
|
|
114
|
-
return
|
|
115
|
-
for t in tools:
|
|
116
|
-
desc = t.get("description", "")[:100]
|
|
117
|
-
print(f" {t['name']}: {desc}")
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
tool_name = sys.argv[2]
|
|
121
|
-
args = json.loads(sys.argv[3]) if len(sys.argv) > 3 else {}
|
|
122
|
-
|
|
123
|
-
result = call_tool(url, tool_name, args)
|
|
124
|
-
|
|
125
|
-
# Extract content from MCP response
|
|
126
|
-
content = result.get("result", {}).get("content", [])
|
|
127
|
-
if content:
|
|
128
|
-
for item in content:
|
|
129
|
-
if item.get("type") == "text":
|
|
130
|
-
print(item["text"])
|
|
131
|
-
else:
|
|
132
|
-
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if __name__ == "__main__":
|
|
136
|
-
main()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Lightweight MCP client for Streamable HTTP/SSE transport.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 mcp-client.py <mcp_url> --list-tools
|
|
6
|
+
python3 mcp-client.py <mcp_url> <tool_name> '<json_args>'
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
import http.client
|
|
12
|
+
from urllib.parse import urlparse, urlencode
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def mcp_request(url: str, method: str, params: dict | None = None,
|
|
16
|
+
req_id: int = 1, session_id: str | None = None) -> tuple[dict, str | None]:
|
|
17
|
+
"""Send MCP JSON-RPC request and parse SSE response. Returns (result, session_id)."""
|
|
18
|
+
parsed = urlparse(url)
|
|
19
|
+
|
|
20
|
+
payload = {
|
|
21
|
+
"jsonrpc": "2.0",
|
|
22
|
+
"id": req_id,
|
|
23
|
+
"method": method,
|
|
24
|
+
}
|
|
25
|
+
if params:
|
|
26
|
+
payload["params"] = params
|
|
27
|
+
|
|
28
|
+
data = json.dumps(payload).encode()
|
|
29
|
+
|
|
30
|
+
headers = {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
"Accept": "application/json, text/event-stream",
|
|
33
|
+
}
|
|
34
|
+
if session_id:
|
|
35
|
+
headers["Mcp-Session-Id"] = session_id
|
|
36
|
+
|
|
37
|
+
conn = http.client.HTTPSConnection(parsed.hostname, timeout=120)
|
|
38
|
+
path = parsed.path
|
|
39
|
+
if parsed.query:
|
|
40
|
+
path += "?" + parsed.query
|
|
41
|
+
|
|
42
|
+
conn.request("POST", path, body=data, headers=headers)
|
|
43
|
+
resp = conn.getresponse()
|
|
44
|
+
|
|
45
|
+
# Get session ID from response headers
|
|
46
|
+
new_session_id = resp.getheader("Mcp-Session-Id") or session_id
|
|
47
|
+
|
|
48
|
+
body = resp.read().decode()
|
|
49
|
+
conn.close()
|
|
50
|
+
|
|
51
|
+
if resp.status >= 400:
|
|
52
|
+
return {"error": f"HTTP {resp.status}: {body[:200]}"}, new_session_id
|
|
53
|
+
|
|
54
|
+
# Parse SSE: look for data: lines
|
|
55
|
+
for line in body.split("\n"):
|
|
56
|
+
if line.startswith("data: "):
|
|
57
|
+
try:
|
|
58
|
+
return json.loads(line[6:]), new_session_id
|
|
59
|
+
except json.JSONDecodeError:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
# Try direct JSON
|
|
63
|
+
try:
|
|
64
|
+
return json.loads(body), new_session_id
|
|
65
|
+
except json.JSONDecodeError:
|
|
66
|
+
return {"raw": body[:500]}, new_session_id
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def init_and_call(url: str, method: str, params: dict | None = None) -> dict:
|
|
70
|
+
"""Initialize session then make a call."""
|
|
71
|
+
# Step 1: Initialize
|
|
72
|
+
init_result, session_id = mcp_request(url, "initialize", {
|
|
73
|
+
"protocolVersion": "2024-11-05",
|
|
74
|
+
"capabilities": {},
|
|
75
|
+
"clientInfo": {"name": "osint-skill", "version": "3.1"},
|
|
76
|
+
}, req_id=1)
|
|
77
|
+
|
|
78
|
+
if "error" in init_result:
|
|
79
|
+
return init_result
|
|
80
|
+
|
|
81
|
+
# Step 2: Send initialized notification (optional but polite)
|
|
82
|
+
# Step 3: Make actual call
|
|
83
|
+
result, _ = mcp_request(url, method, params, req_id=2, session_id=session_id)
|
|
84
|
+
return result
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def list_tools(url: str) -> list:
|
|
88
|
+
"""List available tools on MCP server."""
|
|
89
|
+
result = init_and_call(url, "tools/list")
|
|
90
|
+
tools = result.get("result", {}).get("tools", [])
|
|
91
|
+
return tools
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def call_tool(url: str, tool_name: str, arguments: dict) -> dict:
|
|
95
|
+
"""Call a specific tool on MCP server."""
|
|
96
|
+
result = init_and_call(url, "tools/call", {
|
|
97
|
+
"name": tool_name,
|
|
98
|
+
"arguments": arguments,
|
|
99
|
+
})
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def main():
|
|
104
|
+
if len(sys.argv) < 3:
|
|
105
|
+
print(__doc__)
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
|
|
108
|
+
url = sys.argv[1]
|
|
109
|
+
|
|
110
|
+
if sys.argv[2] == "--list-tools":
|
|
111
|
+
tools = list_tools(url)
|
|
112
|
+
if not tools:
|
|
113
|
+
print("No tools found or error occurred")
|
|
114
|
+
return
|
|
115
|
+
for t in tools:
|
|
116
|
+
desc = t.get("description", "")[:100]
|
|
117
|
+
print(f" {t['name']}: {desc}")
|
|
118
|
+
return
|
|
119
|
+
|
|
120
|
+
tool_name = sys.argv[2]
|
|
121
|
+
args = json.loads(sys.argv[3]) if len(sys.argv) > 3 else {}
|
|
122
|
+
|
|
123
|
+
result = call_tool(url, tool_name, args)
|
|
124
|
+
|
|
125
|
+
# Extract content from MCP response
|
|
126
|
+
content = result.get("result", {}).get("content", [])
|
|
127
|
+
if content:
|
|
128
|
+
for item in content:
|
|
129
|
+
if item.get("type") == "text":
|
|
130
|
+
print(item["text"])
|
|
131
|
+
else:
|
|
132
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
main()
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "bmad-plus",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.2",
|
|
5
5
|
"description": "BMAD+ — Augmented AI-Driven Development Framework with multi-role agents, autopilot, and parallel execution",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bmad",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🚀 BMAD+ — Erweitertes KI-gestütztes Entwicklungs-Framework
|
|
2
2
|
|
|
3
|
-
[](../CHANGELOG.md)
|
|
4
4
|
[](https://github.com/bmad-code-org/BMAD-METHOD)
|
|
5
5
|
[](../LICENSE)
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🚀 BMAD+ — Framework de Desarrollo Impulsado por IA Aumentada
|
|
2
2
|
|
|
3
|
-
[](../CHANGELOG.md)
|
|
4
4
|
[](https://github.com/bmad-code-org/BMAD-METHOD)
|
|
5
5
|
[](../LICENSE)
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🚀 BMAD+ — Augmented AI-Driven Development Framework
|
|
2
2
|
|
|
3
|
-
[](../CHANGELOG.md)
|
|
4
4
|
[](https://github.com/bmad-code-org/BMAD-METHOD)
|
|
5
5
|
[](../LICENSE)
|
|
6
6
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* BMAD+ Install Command
|
|
3
3
|
* Installs agents, skills, and IDE configs into the current project
|
|
4
|
+
* Supports 9 languages: EN, FR, ES, DE, PT-BR, RU, ZH, HE, JA
|
|
5
|
+
*
|
|
6
|
+
* Author: Laurent Rochetta
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
9
|
const path = require('node:path');
|
|
@@ -8,6 +11,7 @@ const fs = require('node:fs');
|
|
|
8
11
|
const fsExtra = require('fs-extra');
|
|
9
12
|
const clack = require('@clack/prompts');
|
|
10
13
|
const pc = require('picocolors');
|
|
14
|
+
const { t, getLanguageOptions, getCommLanguageOptions } = require('../i18n');
|
|
11
15
|
|
|
12
16
|
// Pack definitions
|
|
13
17
|
const PACKS = {
|
|
@@ -50,29 +54,29 @@ const PACKS = {
|
|
|
50
54
|
seo: {
|
|
51
55
|
name: 'SEO Audit 360',
|
|
52
56
|
icon: '🔍',
|
|
53
|
-
description: '
|
|
57
|
+
description: '3 agents (Scout, Chief, Judge) + 6-phase audit + PageSpeed loop',
|
|
54
58
|
required: false,
|
|
55
59
|
agents: [],
|
|
56
60
|
skills: [],
|
|
57
|
-
|
|
61
|
+
packDir: 'pack-seo',
|
|
58
62
|
},
|
|
59
63
|
backup: {
|
|
60
64
|
name: 'Universal Backup',
|
|
61
65
|
icon: '🗂️',
|
|
62
|
-
description: 'Timestamped ZIP backup with smart exclusions
|
|
66
|
+
description: 'Timestamped ZIP backup with smart exclusions',
|
|
63
67
|
required: false,
|
|
64
68
|
agents: [],
|
|
65
69
|
skills: [],
|
|
66
|
-
|
|
70
|
+
packDir: 'pack-backup',
|
|
67
71
|
},
|
|
68
72
|
animated: {
|
|
69
73
|
name: 'Animated Website',
|
|
70
74
|
icon: '🎬',
|
|
71
|
-
description: 'Luxury scroll-driven website from video
|
|
75
|
+
description: 'Luxury scroll-driven website from video',
|
|
72
76
|
required: false,
|
|
73
77
|
agents: [],
|
|
74
78
|
skills: [],
|
|
75
|
-
|
|
79
|
+
packDir: 'pack-animated',
|
|
76
80
|
},
|
|
77
81
|
};
|
|
78
82
|
|
|
@@ -113,17 +117,33 @@ module.exports = {
|
|
|
113
117
|
const projectDir = path.resolve(options.directory || process.cwd());
|
|
114
118
|
const bmadSrc = path.join(__dirname, '..', '..', '..', 'src', 'bmad-plus');
|
|
115
119
|
|
|
116
|
-
// ──
|
|
117
|
-
clack.intro(pc.bgCyan(pc.black(' BMAD+ Installer v0.
|
|
120
|
+
// ── Step 0: Language Selection ──
|
|
121
|
+
clack.intro(pc.bgCyan(pc.black(' BMAD+ Installer v0.4.2 ')));
|
|
122
|
+
|
|
123
|
+
let lang = 'en';
|
|
124
|
+
if (!options.yes) {
|
|
125
|
+
const langChoice = await clack.select({
|
|
126
|
+
message: '🌐 Select your language / Choisissez votre langue / 选择语言',
|
|
127
|
+
options: getLanguageOptions(),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (clack.isCancel(langChoice)) {
|
|
131
|
+
clack.cancel('Installation cancelled.');
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
lang = langChoice;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const i = t(lang); // Get translations for selected language
|
|
118
138
|
|
|
119
139
|
// Verify source exists
|
|
120
140
|
if (!fs.existsSync(bmadSrc)) {
|
|
121
|
-
clack.log.error(
|
|
122
|
-
clack.outro(pc.red(
|
|
141
|
+
clack.log.error(`${i.source_not_found}: ${bmadSrc}`);
|
|
142
|
+
clack.outro(pc.red(i.failed));
|
|
123
143
|
process.exit(1);
|
|
124
144
|
}
|
|
125
145
|
|
|
126
|
-
clack.log.info(
|
|
146
|
+
clack.log.info(`${i.installing_to}: ${pc.cyan(projectDir)}`);
|
|
127
147
|
|
|
128
148
|
// ── Step 1: Pack Selection ──
|
|
129
149
|
let selectedPacks = ['core']; // Core always included
|
|
@@ -137,27 +157,27 @@ module.exports = {
|
|
|
137
157
|
}
|
|
138
158
|
} else if (!options.yes) {
|
|
139
159
|
const packChoice = await clack.multiselect({
|
|
140
|
-
message:
|
|
160
|
+
message: i.select_packs,
|
|
141
161
|
options: Object.entries(PACKS)
|
|
142
162
|
.filter(([, p]) => !p.required)
|
|
143
163
|
.map(([key, pack]) => ({
|
|
144
164
|
value: key,
|
|
145
165
|
label: `${pack.icon} ${pack.name}`,
|
|
146
|
-
hint: pack.disabled ?
|
|
166
|
+
hint: pack.disabled ? i.soon : pack.description,
|
|
147
167
|
disabled: pack.disabled,
|
|
148
168
|
})),
|
|
149
169
|
required: false,
|
|
150
170
|
});
|
|
151
171
|
|
|
152
172
|
if (clack.isCancel(packChoice)) {
|
|
153
|
-
clack.cancel(
|
|
173
|
+
clack.cancel(i.cancelled);
|
|
154
174
|
process.exit(0);
|
|
155
175
|
}
|
|
156
176
|
|
|
157
177
|
selectedPacks = [...new Set(['core', ...packChoice])];
|
|
158
178
|
}
|
|
159
179
|
|
|
160
|
-
clack.log.success(
|
|
180
|
+
clack.log.success(`${i.selected_packs}: ${selectedPacks.map(p => `${PACKS[p].icon} ${PACKS[p].name}`).join(', ')}`);
|
|
161
181
|
|
|
162
182
|
// ── Step 2: IDE Detection ──
|
|
163
183
|
let detectedIDEs = [];
|
|
@@ -178,7 +198,7 @@ module.exports = {
|
|
|
178
198
|
// If nothing detected, ask
|
|
179
199
|
if (detectedIDEs.length === 0 && !options.yes) {
|
|
180
200
|
const ideChoice = await clack.multiselect({
|
|
181
|
-
message:
|
|
201
|
+
message: i.select_ide,
|
|
182
202
|
options: Object.entries(IDE_CONFIGS).map(([key, ide]) => ({
|
|
183
203
|
value: key,
|
|
184
204
|
label: ide.name,
|
|
@@ -198,7 +218,7 @@ module.exports = {
|
|
|
198
218
|
}
|
|
199
219
|
|
|
200
220
|
if (detectedIDEs.length > 0) {
|
|
201
|
-
clack.log.info(
|
|
221
|
+
clack.log.info(`${i.detected_ides}: ${detectedIDEs.map(id => IDE_CONFIGS[id].name).join(', ')}`);
|
|
202
222
|
}
|
|
203
223
|
|
|
204
224
|
// ── Step 3: User Config ──
|
|
@@ -208,31 +228,26 @@ module.exports = {
|
|
|
208
228
|
if (!options.yes) {
|
|
209
229
|
const userConfig = await clack.group({
|
|
210
230
|
userName: () => clack.text({
|
|
211
|
-
message:
|
|
231
|
+
message: i.enter_name,
|
|
212
232
|
placeholder: userName,
|
|
213
233
|
defaultValue: userName,
|
|
214
234
|
}),
|
|
215
235
|
commLang: () => clack.select({
|
|
216
|
-
message:
|
|
217
|
-
options:
|
|
218
|
-
{ value: 'French', label: '🇫🇷 Français' },
|
|
219
|
-
{ value: 'English', label: '🇬🇧 English' },
|
|
220
|
-
{ value: 'German', label: '🇩🇪 Deutsch' },
|
|
221
|
-
{ value: 'Spanish', label: '🇪🇸 Español' },
|
|
222
|
-
],
|
|
236
|
+
message: i.comm_language,
|
|
237
|
+
options: getCommLanguageOptions(),
|
|
223
238
|
}),
|
|
224
239
|
execMode: () => clack.select({
|
|
225
|
-
message:
|
|
240
|
+
message: i.exec_mode,
|
|
226
241
|
options: [
|
|
227
|
-
{ value: 'manual', label:
|
|
228
|
-
{ value: 'autopilot', label:
|
|
229
|
-
{ value: 'hybrid', label:
|
|
242
|
+
{ value: 'manual', label: i.exec_manual },
|
|
243
|
+
{ value: 'autopilot', label: i.exec_autopilot },
|
|
244
|
+
{ value: 'hybrid', label: i.exec_hybrid },
|
|
230
245
|
],
|
|
231
246
|
}),
|
|
232
247
|
});
|
|
233
248
|
|
|
234
249
|
if (clack.isCancel(userConfig)) {
|
|
235
|
-
clack.cancel(
|
|
250
|
+
clack.cancel(i.cancelled);
|
|
236
251
|
process.exit(0);
|
|
237
252
|
}
|
|
238
253
|
|
|
@@ -242,7 +257,7 @@ module.exports = {
|
|
|
242
257
|
|
|
243
258
|
// ── Step 4: Install Files ──
|
|
244
259
|
const spinner = clack.spinner();
|
|
245
|
-
spinner.start(
|
|
260
|
+
spinner.start(i.installing_files);
|
|
246
261
|
|
|
247
262
|
const targetAgentsDir = path.join(projectDir, '.agents', 'skills');
|
|
248
263
|
const targetDataDir = path.join(projectDir, '.agents', 'data');
|
|
@@ -301,13 +316,14 @@ module.exports = {
|
|
|
301
316
|
}
|
|
302
317
|
}
|
|
303
318
|
|
|
304
|
-
// Copy
|
|
305
|
-
if (pack.
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
if (fs.existsSync(
|
|
309
|
-
fsExtra.copySync(
|
|
310
|
-
|
|
319
|
+
// Copy pack directory (SEO, Backup, Animated Website)
|
|
320
|
+
if (pack.packDir) {
|
|
321
|
+
const packSrc = path.join(bmadSrc, 'agents', pack.packDir);
|
|
322
|
+
const packDest = path.join(targetAgentsDir, pack.packDir);
|
|
323
|
+
if (fs.existsSync(packSrc)) {
|
|
324
|
+
fsExtra.copySync(packSrc, packDest, { overwrite: true });
|
|
325
|
+
copiedAgents++;
|
|
326
|
+
copiedFiles++;
|
|
311
327
|
}
|
|
312
328
|
}
|
|
313
329
|
}
|
|
@@ -325,12 +341,12 @@ module.exports = {
|
|
|
325
341
|
copiedFiles++;
|
|
326
342
|
}
|
|
327
343
|
|
|
328
|
-
spinner.stop(
|
|
344
|
+
spinner.stop(i.installed_summary(copiedAgents, copiedSkills, copiedFiles));
|
|
329
345
|
|
|
330
346
|
// ── Step 5: Generate IDE Configs ──
|
|
331
347
|
if (detectedIDEs.length > 0) {
|
|
332
348
|
const ideSpinner = clack.spinner();
|
|
333
|
-
ideSpinner.start(
|
|
349
|
+
ideSpinner.start(i.configuring_ides);
|
|
334
350
|
|
|
335
351
|
const configContent = generateIDEConfig(userName, commLang, selectedPacks);
|
|
336
352
|
|
|
@@ -342,7 +358,7 @@ module.exports = {
|
|
|
342
358
|
fs.writeFileSync(configPath, configContent, 'utf8');
|
|
343
359
|
}
|
|
344
360
|
|
|
345
|
-
ideSpinner.stop(
|
|
361
|
+
ideSpinner.stop(i.ide_configured(detectedIDEs.length));
|
|
346
362
|
}
|
|
347
363
|
|
|
348
364
|
// ── Step 6: Create config.yaml ──
|
|
@@ -358,7 +374,8 @@ module.exports = {
|
|
|
358
374
|
|
|
359
375
|
// ── Step 8: Write install manifest ──
|
|
360
376
|
const manifest = {
|
|
361
|
-
version: '0.
|
|
377
|
+
version: '0.4.0',
|
|
378
|
+
uiLanguage: lang,
|
|
362
379
|
installed: new Date().toISOString(),
|
|
363
380
|
packs: selectedPacks,
|
|
364
381
|
ides: detectedIDEs,
|
|
@@ -373,43 +390,55 @@ module.exports = {
|
|
|
373
390
|
|
|
374
391
|
// ── Summary — Contextual Getting Started ──
|
|
375
392
|
const agentGuide = [
|
|
376
|
-
|
|
393
|
+
i.guide_who,
|
|
377
394
|
'',
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
395
|
+
` ${i.guide_idea.padEnd(28)} → "Atlas, [...]"`,
|
|
396
|
+
` ${i.guide_prd.padEnd(28)} → "Atlas, create PRD"`,
|
|
397
|
+
` ${i.guide_arch.padEnd(28)} → "Forge, propose architecture"`,
|
|
398
|
+
` ${i.guide_code.padEnd(28)} → "Forge, implement story [X]"`,
|
|
399
|
+
` ${i.guide_test.padEnd(28)} → "Sentinel, review module [X]"`,
|
|
400
|
+
` ${i.guide_sprint.padEnd(28)} → "Nexus, create epics"`,
|
|
401
|
+
` ${i.guide_auto.padEnd(28)} → "autopilot"`,
|
|
385
402
|
];
|
|
386
403
|
|
|
387
404
|
if (selectedPacks.includes('osint')) {
|
|
388
|
-
agentGuide.push(
|
|
405
|
+
agentGuide.push(` ${i.guide_osint.padEnd(28)} → "Shadow, investigate [name]"`);
|
|
389
406
|
}
|
|
390
407
|
|
|
391
408
|
if (selectedPacks.includes('maker')) {
|
|
392
|
-
agentGuide.push(
|
|
409
|
+
agentGuide.push(` ${i.guide_maker.padEnd(28)} → "Maker, create agent [desc]"`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (selectedPacks.includes('seo')) {
|
|
413
|
+
agentGuide.push(` ${i.guide_seo.padEnd(28)} → "/seo audit <url>"`);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (selectedPacks.includes('backup')) {
|
|
417
|
+
agentGuide.push(` ${i.guide_backup.padEnd(28)} → "/backup create"`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (selectedPacks.includes('animated')) {
|
|
421
|
+
agentGuide.push(` ${i.guide_animated.padEnd(28)} → "/animated build <video>"`);
|
|
393
422
|
}
|
|
394
423
|
|
|
395
424
|
agentGuide.push(
|
|
396
425
|
'',
|
|
397
|
-
|
|
398
|
-
' 1. Atlas (
|
|
426
|
+
i.guide_workflow,
|
|
427
|
+
' 1. Atlas (idea → brief → PRD)',
|
|
399
428
|
' 2. Forge (architecture → code)',
|
|
400
429
|
' 3. Sentinel (tests → review)',
|
|
401
430
|
'',
|
|
402
|
-
|
|
431
|
+
i.guide_or_auto,
|
|
403
432
|
'',
|
|
404
|
-
|
|
433
|
+
`${i.guide_output}: _bmad-output/discovery/ & _bmad-output/build/`,
|
|
405
434
|
'',
|
|
406
435
|
'---',
|
|
407
|
-
|
|
436
|
+
i.guide_credits
|
|
408
437
|
);
|
|
409
438
|
|
|
410
|
-
clack.note(agentGuide.join('\n'),
|
|
439
|
+
clack.note(agentGuide.join('\n'), i.guide_title);
|
|
411
440
|
|
|
412
|
-
clack.outro(pc.green(
|
|
441
|
+
clack.outro(pc.green(i.guide_ready));
|
|
413
442
|
},
|
|
414
443
|
};
|
|
415
444
|
|