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.
Files changed (68) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +13 -56
  3. package/osint-agent-package/skills/bmad-osint-investigate/osint/SKILL.md +452 -452
  4. package/osint-agent-package/skills/bmad-osint-investigate/osint/assets/dossier-template.md +116 -116
  5. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/content-extraction.md +100 -100
  6. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/platforms.md +130 -130
  7. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/psychoprofile.md +69 -69
  8. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/tools.md +281 -281
  9. package/osint-agent-package/skills/bmad-osint-investigate/osint/scripts/mcp-client.py +136 -136
  10. package/package.json +1 -1
  11. package/readme-international/README.de.md +1 -1
  12. package/readme-international/README.es.md +1 -1
  13. package/readme-international/README.fr.md +1 -1
  14. package/tools/cli/commands/install.js +88 -59
  15. package/tools/cli/i18n.js +501 -0
  16. package/oveanet-pack/animated-website/DEPLOYMENT.md +0 -104
  17. package/oveanet-pack/animated-website/README.md +0 -63
  18. package/oveanet-pack/animated-website/agent.yaml +0 -63
  19. package/oveanet-pack/seo-audit-360/DEPLOYMENT.md +0 -115
  20. package/oveanet-pack/seo-audit-360/README.md +0 -66
  21. package/oveanet-pack/seo-audit-360/agent.yaml +0 -70
  22. package/oveanet-pack/seo-audit-360/extensions/google-analytics/EXTENSION.md +0 -79
  23. package/oveanet-pack/seo-audit-360/extensions/google-analytics/ga4_client.py +0 -200
  24. package/oveanet-pack/seo-audit-360/extensions/google-analytics/requirements.txt +0 -4
  25. package/oveanet-pack/seo-audit-360/extensions/google-search-console/EXTENSION.md +0 -109
  26. package/oveanet-pack/seo-audit-360/extensions/google-search-console/gsc_client.py +0 -186
  27. package/oveanet-pack/seo-audit-360/extensions/google-search-console/requirements.txt +0 -4
  28. package/oveanet-pack/seo-audit-360/hooks/seo-check.sh +0 -95
  29. package/oveanet-pack/seo-audit-360/requirements.txt +0 -14
  30. package/oveanet-pack/seo-audit-360/scripts/__pycache__/seo_crawl.cpython-314.pyc +0 -0
  31. package/oveanet-pack/seo-audit-360/scripts/__pycache__/seo_parse.cpython-314.pyc +0 -0
  32. package/oveanet-pack/seo-audit-360/scripts/install.ps1 +0 -53
  33. package/oveanet-pack/seo-audit-360/scripts/install.sh +0 -48
  34. package/oveanet-pack/seo-audit-360/scripts/seo_apis.py +0 -464
  35. package/oveanet-pack/seo-audit-360/scripts/seo_crawl.py +0 -282
  36. package/oveanet-pack/seo-audit-360/scripts/seo_fetch.py +0 -231
  37. package/oveanet-pack/seo-audit-360/scripts/seo_parse.py +0 -255
  38. package/oveanet-pack/seo-audit-360/scripts/seo_report.py +0 -403
  39. package/oveanet-pack/seo-audit-360/scripts/seo_screenshot.py +0 -202
  40. package/oveanet-pack/seo-audit-360/tests/__pycache__/test_crawl.cpython-314-pytest-9.0.2.pyc +0 -0
  41. package/oveanet-pack/seo-audit-360/tests/__pycache__/test_parse.cpython-314-pytest-9.0.2.pyc +0 -0
  42. package/oveanet-pack/seo-audit-360/tests/fixtures/sample_page.html +0 -62
  43. package/oveanet-pack/seo-audit-360/tests/test_apis.py +0 -75
  44. package/oveanet-pack/seo-audit-360/tests/test_crawl.py +0 -121
  45. package/oveanet-pack/seo-audit-360/tests/test_fetch.py +0 -70
  46. package/oveanet-pack/seo-audit-360/tests/test_parse.py +0 -184
  47. package/oveanet-pack/universal-backup/DEPLOYMENT.md +0 -80
  48. package/oveanet-pack/universal-backup/README.md +0 -58
  49. package/oveanet-pack/universal-backup/agent.yaml +0 -45
  50. /package/{oveanet-pack/animated-website/agent → src/bmad-plus/agents/pack-animated}/animated-website-agent.md +0 -0
  51. /package/{oveanet-pack/animated-website → src/bmad-plus/agents/pack-animated}/templates/animated-website-workflow.md +0 -0
  52. /package/{oveanet-pack/universal-backup/agent → src/bmad-plus/agents/pack-backup}/backup-agent.md +0 -0
  53. /package/{oveanet-pack/universal-backup → src/bmad-plus/agents/pack-backup}/templates/backup-workflow.md +0 -0
  54. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/SKILL.md +0 -0
  55. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/checklist.md +0 -0
  56. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/pagespeed-playbook.md +0 -0
  57. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/audit-schema.json +0 -0
  58. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/cwv-thresholds.md +0 -0
  59. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/eeat-criteria.md +0 -0
  60. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/geo-signals.md +0 -0
  61. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/hreflang-rules.md +0 -0
  62. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/quality-gates.md +0 -0
  63. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/schema-catalog.md +0 -0
  64. /package/{oveanet-pack/seo-audit-360 → src/bmad-plus/agents/pack-seo}/ref/schema-templates.json +0 -0
  65. /package/{oveanet-pack/seo-audit-360/agent → src/bmad-plus/agents/pack-seo}/seo-chief.md +0 -0
  66. /package/{oveanet-pack/seo-audit-360/agent → src/bmad-plus/agents/pack-seo}/seo-judge.md +0 -0
  67. /package/{oveanet-pack/seo-audit-360/agent → src/bmad-plus/agents/pack-seo}/seo-scout.md +0 -0
  68. /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.0",
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
- [![Version](https://img.shields.io/badge/version-0.1.0-blue.svg)](../CHANGELOG.md)
3
+ [![Version](https://img.shields.io/badge/version-0.4.2-blue.svg)](../CHANGELOG.md)
4
4
  [![Based on](https://img.shields.io/badge/based%20on-BMAD--METHOD%20v6.2.0-green.svg)](https://github.com/bmad-code-org/BMAD-METHOD)
5
5
  [![License](https://img.shields.io/badge/license-MIT-yellow.svg)](../LICENSE)
6
6
 
@@ -1,6 +1,6 @@
1
1
  # 🚀 BMAD+ — Framework de Desarrollo Impulsado por IA Aumentada
2
2
 
3
- [![Version](https://img.shields.io/badge/version-0.1.0-blue.svg)](../CHANGELOG.md)
3
+ [![Version](https://img.shields.io/badge/version-0.4.2-blue.svg)](../CHANGELOG.md)
4
4
  [![Based on](https://img.shields.io/badge/based%20on-BMAD--METHOD%20v6.2.0-green.svg)](https://github.com/bmad-code-org/BMAD-METHOD)
5
5
  [![License](https://img.shields.io/badge/license-MIT-yellow.svg)](../LICENSE)
6
6
 
@@ -1,6 +1,6 @@
1
1
  # 🚀 BMAD+ — Augmented AI-Driven Development Framework
2
2
 
3
- [![Version](https://img.shields.io/badge/version-0.1.0-blue.svg)](../CHANGELOG.md)
3
+ [![Version](https://img.shields.io/badge/version-0.4.2-blue.svg)](../CHANGELOG.md)
4
4
  [![Based on](https://img.shields.io/badge/based%20on-BMAD--METHOD%20v6.2.0-green.svg)](https://github.com/bmad-code-org/BMAD-METHOD)
5
5
  [![License](https://img.shields.io/badge/license-MIT-yellow.svg)](../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: '9-category audit for search engines + AI engines (by Oveanet)',
57
+ description: '3 agents (Scout, Chief, Judge) + 6-phase audit + PageSpeed loop',
54
58
  required: false,
55
59
  agents: [],
56
60
  skills: [],
57
- oveanetAgent: 'seo-audit-360',
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 (by Oveanet)',
66
+ description: 'Timestamped ZIP backup with smart exclusions',
63
67
  required: false,
64
68
  agents: [],
65
69
  skills: [],
66
- oveanetAgent: 'universal-backup',
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 (by Oveanet)',
75
+ description: 'Luxury scroll-driven website from video',
72
76
  required: false,
73
77
  agents: [],
74
78
  skills: [],
75
- oveanetAgent: 'animated-website',
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
- // ── Intro ──
117
- clack.intro(pc.bgCyan(pc.black(' BMAD+ Installer v0.1.0 ')));
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(`Source directory not found: ${bmadSrc}`);
122
- clack.outro(pc.red('Installation failed.'));
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(`Installing to: ${pc.cyan(projectDir)}`);
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: 'Quels packs installer ? (Core est toujours inclus)',
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 ? 'bientôt' : pack.description,
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('Installation annulée.');
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(`Packs sélectionnés: ${selectedPacks.map(p => `${PACKS[p].icon} ${PACKS[p].name}`).join(', ')}`);
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: 'Quels IDE utilises-tu ?',
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(`IDE détectés: ${detectedIDEs.map(id => IDE_CONFIGS[id].name).join(', ')}`);
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: 'Ton prénom (les agents l\'utilisent pour te saluer)',
231
+ message: i.enter_name,
212
232
  placeholder: userName,
213
233
  defaultValue: userName,
214
234
  }),
215
235
  commLang: () => clack.select({
216
- message: 'Langue de communication',
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: 'Mode d\'exécution',
240
+ message: i.exec_mode,
226
241
  options: [
227
- { value: 'manual', label: 'Manuel — Tu appelles les agents toi-même' },
228
- { value: 'autopilot', label: 'Autopilot — Nexus gère tout le pipeline' },
229
- { value: 'hybrid', label: 'Hybride — Autopilot avec checkpoints fréquents' },
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('Installation annulée.');
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('Installation des fichiers...');
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 Oveanet agent pack (SEO, Backup, Animated Website)
305
- if (pack.oveanetAgent) {
306
- const oveanetSrc = path.join(__dirname, '..', '..', '..', 'oveanet-pack', pack.oveanetAgent);
307
- const oveanetDest = path.join(targetAgentsDir, pack.oveanetAgent);
308
- if (fs.existsSync(oveanetSrc)) {
309
- fsExtra.copySync(oveanetSrc, oveanetDest, { overwrite: true });
310
- copiedSkills++;
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(`✅ ${copiedAgents} agents, ${copiedSkills} skills, ${copiedFiles} fichiers copiés`);
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('Configuration des IDE...');
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(`✅ ${detectedIDEs.length} IDE configuré(s)`);
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.1.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
- '💬 À qui parler ?',
393
+ i.guide_who,
377
394
  '',
378
- ' Discuter d\'une idée → "Atlas, j\'ai une idée de projet : [...]"',
379
- ' Créer un PRD → "Atlas, crée le PRD"',
380
- ' Architecture technique → "Forge, propose une architecture"',
381
- ' Implémenter du code → "Forge, implémente la story [X]"',
382
- ' Tester / code review → "Sentinel, review le module [X]"',
383
- ' Planifier un sprint → "Nexus, crée les epics et stories"',
384
- ' Tout automatiser → "autopilot" puis décris ton projet',
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(' Investigation OSINT → "Shadow, investigate [nom]"');
405
+ agentGuide.push(` ${i.guide_osint.padEnd(28)} → "Shadow, investigate [name]"`);
389
406
  }
390
407
 
391
408
  if (selectedPacks.includes('maker')) {
392
- agentGuide.push(' Créer un nouvel agent → "Maker, crée un agent [description]"');
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
- '🚀 Workflow recommandé:',
398
- ' 1. Atlas (idée → brief → PRD)',
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
- '⚡ Ou: "autopilot" pour tout gérer automatiquement',
431
+ i.guide_or_auto,
403
432
  '',
404
- `📁 Output: _bmad-output/discovery/ et _bmad-output/build/`,
433
+ `${i.guide_output}: _bmad-output/discovery/ & _bmad-output/build/`,
405
434
  '',
406
435
  '---',
407
- '✨ BMAD+ is created by Laurent Rochetta — github.com/lrochetta | linkedin.com/in/laurentrochetta ✨'
436
+ i.guide_credits
408
437
  );
409
438
 
410
- clack.note(agentGuide.join('\n'), '✅ Installation terminée — Comment commencer');
439
+ clack.note(agentGuide.join('\n'), i.guide_title);
411
440
 
412
- clack.outro(pc.green('BMAD+ est prêt! Parle à Atlas pour commencer 🚀'));
441
+ clack.outro(pc.green(i.guide_ready));
413
442
  },
414
443
  };
415
444