bmad-plus 0.3.3 → 0.4.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.
- package/CHANGELOG.md +34 -0
- package/README.md +12 -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 +74 -46
- 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/animated-website-agent.md +0 -325
- package/oveanet-pack/animated-website/agent.yaml +0 -63
- package/oveanet-pack/animated-website/templates/animated-website-workflow.md +0 -55
- 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/SKILL.md +0 -171
- package/oveanet-pack/seo-audit-360/agent/seo-chief.md +0 -294
- package/oveanet-pack/seo-audit-360/agent/seo-judge.md +0 -241
- package/oveanet-pack/seo-audit-360/agent/seo-scout.md +0 -171
- package/oveanet-pack/seo-audit-360/agent.yaml +0 -70
- package/oveanet-pack/seo-audit-360/checklist.md +0 -140
- package/oveanet-pack/seo-audit-360/hooks/seo-check.sh +0 -95
- package/oveanet-pack/seo-audit-360/pagespeed-playbook.md +0 -320
- package/oveanet-pack/seo-audit-360/ref/audit-schema.json +0 -187
- package/oveanet-pack/seo-audit-360/ref/cwv-thresholds.md +0 -87
- package/oveanet-pack/seo-audit-360/ref/eeat-criteria.md +0 -123
- package/oveanet-pack/seo-audit-360/ref/geo-signals.md +0 -167
- package/oveanet-pack/seo-audit-360/ref/hreflang-rules.md +0 -153
- package/oveanet-pack/seo-audit-360/ref/quality-gates.md +0 -133
- package/oveanet-pack/seo-audit-360/ref/schema-catalog.md +0 -91
- package/oveanet-pack/seo-audit-360/ref/schema-templates.json +0 -356
- 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/templates/seo-audit-workflow.md +0 -241
- 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/backup-agent.md +0 -71
- package/oveanet-pack/universal-backup/agent.yaml +0 -45
- package/oveanet-pack/universal-backup/templates/backup-workflow.md +0 -51
|
@@ -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
|
+
"version": "0.4.1",
|
|
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 = {
|
|
@@ -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.1
|
|
120
|
+
// ── Step 0: Language Selection ──
|
|
121
|
+
clack.intro(pc.bgCyan(pc.black(' BMAD+ Installer v0.4.1 ')));
|
|
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');
|
|
@@ -325,12 +340,12 @@ module.exports = {
|
|
|
325
340
|
copiedFiles++;
|
|
326
341
|
}
|
|
327
342
|
|
|
328
|
-
spinner.stop(
|
|
343
|
+
spinner.stop(i.installed_summary(copiedAgents, copiedSkills, copiedFiles));
|
|
329
344
|
|
|
330
345
|
// ── Step 5: Generate IDE Configs ──
|
|
331
346
|
if (detectedIDEs.length > 0) {
|
|
332
347
|
const ideSpinner = clack.spinner();
|
|
333
|
-
ideSpinner.start(
|
|
348
|
+
ideSpinner.start(i.configuring_ides);
|
|
334
349
|
|
|
335
350
|
const configContent = generateIDEConfig(userName, commLang, selectedPacks);
|
|
336
351
|
|
|
@@ -342,7 +357,7 @@ module.exports = {
|
|
|
342
357
|
fs.writeFileSync(configPath, configContent, 'utf8');
|
|
343
358
|
}
|
|
344
359
|
|
|
345
|
-
ideSpinner.stop(
|
|
360
|
+
ideSpinner.stop(i.ide_configured(detectedIDEs.length));
|
|
346
361
|
}
|
|
347
362
|
|
|
348
363
|
// ── Step 6: Create config.yaml ──
|
|
@@ -358,7 +373,8 @@ module.exports = {
|
|
|
358
373
|
|
|
359
374
|
// ── Step 8: Write install manifest ──
|
|
360
375
|
const manifest = {
|
|
361
|
-
version: '0.
|
|
376
|
+
version: '0.4.0',
|
|
377
|
+
uiLanguage: lang,
|
|
362
378
|
installed: new Date().toISOString(),
|
|
363
379
|
packs: selectedPacks,
|
|
364
380
|
ides: detectedIDEs,
|
|
@@ -373,43 +389,55 @@ module.exports = {
|
|
|
373
389
|
|
|
374
390
|
// ── Summary — Contextual Getting Started ──
|
|
375
391
|
const agentGuide = [
|
|
376
|
-
|
|
392
|
+
i.guide_who,
|
|
377
393
|
'',
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
394
|
+
` ${i.guide_idea.padEnd(28)} → "Atlas, [...]"`,
|
|
395
|
+
` ${i.guide_prd.padEnd(28)} → "Atlas, create PRD"`,
|
|
396
|
+
` ${i.guide_arch.padEnd(28)} → "Forge, propose architecture"`,
|
|
397
|
+
` ${i.guide_code.padEnd(28)} → "Forge, implement story [X]"`,
|
|
398
|
+
` ${i.guide_test.padEnd(28)} → "Sentinel, review module [X]"`,
|
|
399
|
+
` ${i.guide_sprint.padEnd(28)} → "Nexus, create epics"`,
|
|
400
|
+
` ${i.guide_auto.padEnd(28)} → "autopilot"`,
|
|
385
401
|
];
|
|
386
402
|
|
|
387
403
|
if (selectedPacks.includes('osint')) {
|
|
388
|
-
agentGuide.push(
|
|
404
|
+
agentGuide.push(` ${i.guide_osint.padEnd(28)} → "Shadow, investigate [name]"`);
|
|
389
405
|
}
|
|
390
406
|
|
|
391
407
|
if (selectedPacks.includes('maker')) {
|
|
392
|
-
agentGuide.push(
|
|
408
|
+
agentGuide.push(` ${i.guide_maker.padEnd(28)} → "Maker, create agent [desc]"`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (selectedPacks.includes('seo')) {
|
|
412
|
+
agentGuide.push(` ${i.guide_seo.padEnd(28)} → "/seo audit <url>"`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (selectedPacks.includes('backup')) {
|
|
416
|
+
agentGuide.push(` ${i.guide_backup.padEnd(28)} → "/backup create"`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (selectedPacks.includes('animated')) {
|
|
420
|
+
agentGuide.push(` ${i.guide_animated.padEnd(28)} → "/animated build <video>"`);
|
|
393
421
|
}
|
|
394
422
|
|
|
395
423
|
agentGuide.push(
|
|
396
424
|
'',
|
|
397
|
-
|
|
398
|
-
' 1. Atlas (
|
|
425
|
+
i.guide_workflow,
|
|
426
|
+
' 1. Atlas (idea → brief → PRD)',
|
|
399
427
|
' 2. Forge (architecture → code)',
|
|
400
428
|
' 3. Sentinel (tests → review)',
|
|
401
429
|
'',
|
|
402
|
-
|
|
430
|
+
i.guide_or_auto,
|
|
403
431
|
'',
|
|
404
|
-
|
|
432
|
+
`${i.guide_output}: _bmad-output/discovery/ & _bmad-output/build/`,
|
|
405
433
|
'',
|
|
406
434
|
'---',
|
|
407
|
-
|
|
435
|
+
i.guide_credits
|
|
408
436
|
);
|
|
409
437
|
|
|
410
|
-
clack.note(agentGuide.join('\n'),
|
|
438
|
+
clack.note(agentGuide.join('\n'), i.guide_title);
|
|
411
439
|
|
|
412
|
-
clack.outro(pc.green(
|
|
440
|
+
clack.outro(pc.green(i.guide_ready));
|
|
413
441
|
},
|
|
414
442
|
};
|
|
415
443
|
|