bmad-plus 0.7.0 → 0.7.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 +19 -0
- package/README.md +1 -1
- 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/bmad-plus-cli.js +22 -0
- package/tools/cli/commands/install.js +35 -2
- package/tools/cli/commands/memory.js +194 -0
- package/tools/cli/commands/scan.js +335 -0
- package/tools/cli/i18n.js +30 -10
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to BMAD+ will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.1] — 2026-05-17
|
|
9
|
+
|
|
10
|
+
### 🛠️ CLI Commands & Guardrails Injection
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`npx bmad-plus scan [path]`** — Interactive project scanner
|
|
14
|
+
- Recursive directory scan with stack auto-detection (Node.js, Rust, Python, Go, PHP, Ruby, Java)
|
|
15
|
+
- Framework detection (Next.js, Nuxt, React, Vue, Svelte, Express, Electron, Tauri)
|
|
16
|
+
- Status tracking (active/paused/archived based on last modified)
|
|
17
|
+
- Interactive validation: index all, select, or skip
|
|
18
|
+
- Auto-generates `projects-index.md` in global brain
|
|
19
|
+
- **`npx bmad-plus memory status`** — Memory health report
|
|
20
|
+
- Shows project memory + global brain status
|
|
21
|
+
- Entry counts, last modified dates, health score
|
|
22
|
+
- Brain link detection
|
|
23
|
+
- **`npx bmad-plus memory export`** — Export brain as portable Markdown archive
|
|
24
|
+
- **Karpathy Guardrails injection** — 4 principles auto-injected into CLAUDE.md/GEMINI.md/AGENTS.md when Memory pack is installed
|
|
25
|
+
- **i18n** — Memory + Dev Studio keys for EN and FR (other languages use fallback)
|
|
26
|
+
|
|
8
27
|
## [0.7.0] — 2026-05-17
|
|
9
28
|
|
|
10
29
|
### 🧠 Pack Memory — Persistent Brain
|
package/README.md
CHANGED
|
@@ -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
|
|
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.7.
|
|
4
|
+
"version": "0.7.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
|
|
|
@@ -25,6 +25,8 @@ const install = require('./commands/install');
|
|
|
25
25
|
const uninstall = require('./commands/uninstall');
|
|
26
26
|
const update = require('./commands/update');
|
|
27
27
|
const doctor = require('./commands/doctor');
|
|
28
|
+
const scan = require('./commands/scan');
|
|
29
|
+
const memory = require('./commands/memory');
|
|
28
30
|
|
|
29
31
|
program
|
|
30
32
|
.version(packageJson.version)
|
|
@@ -66,6 +68,26 @@ for (const option of doctor.options || []) {
|
|
|
66
68
|
}
|
|
67
69
|
doctorCmd.action(doctor.action);
|
|
68
70
|
|
|
71
|
+
// Scan command
|
|
72
|
+
const scanCmd = program
|
|
73
|
+
.command('scan')
|
|
74
|
+
.description('Scan directories to discover and index projects in the global brain');
|
|
75
|
+
|
|
76
|
+
for (const option of scan.options || []) {
|
|
77
|
+
scanCmd.option(...option);
|
|
78
|
+
}
|
|
79
|
+
scanCmd.action(scan.action);
|
|
80
|
+
|
|
81
|
+
// Memory command
|
|
82
|
+
const memoryCmd = program
|
|
83
|
+
.command('memory [subcommand]')
|
|
84
|
+
.description('Manage persistent brain (status, export)');
|
|
85
|
+
|
|
86
|
+
for (const option of memory.options || []) {
|
|
87
|
+
memoryCmd.option(...option);
|
|
88
|
+
}
|
|
89
|
+
memoryCmd.action(memory.action);
|
|
90
|
+
|
|
69
91
|
program.parse(process.argv);
|
|
70
92
|
|
|
71
93
|
if (process.argv.slice(2).length === 0) {
|
|
@@ -56,7 +56,7 @@ const PACKS = {
|
|
|
56
56
|
'dev-studio': {
|
|
57
57
|
name: 'Dev Studio — Full SDLC',
|
|
58
58
|
icon: '🏗️',
|
|
59
|
-
description: '
|
|
59
|
+
description: 'Full SDLC pipeline: brainstorm → PRD → architecture → TDD → code review → deploy',
|
|
60
60
|
required: false,
|
|
61
61
|
agents: [],
|
|
62
62
|
skills: [],
|
|
@@ -655,6 +655,39 @@ function generateIDEConfig(userName, language, packs) {
|
|
|
655
655
|
agents.push('- **Zecher** (זכר) — Memory Archivist — Consolidation, project scanning, context recall');
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
+
// Build memory section if memory pack is installed
|
|
659
|
+
let memorySection = '';
|
|
660
|
+
if (packs.includes('memory')) {
|
|
661
|
+
memorySection = [
|
|
662
|
+
'',
|
|
663
|
+
'## Memory Protocol (Karpathy Guardrails)',
|
|
664
|
+
'',
|
|
665
|
+
'Agents MUST follow these behavioral principles:',
|
|
666
|
+
'',
|
|
667
|
+
'### G1 — Think Before Coding',
|
|
668
|
+
'- State assumptions explicitly. If uncertain, ask.',
|
|
669
|
+
'- Check `.agents/memory/decisions.md` for prior decisions before re-deciding.',
|
|
670
|
+
'',
|
|
671
|
+
'### G2 — Simplicity First',
|
|
672
|
+
'- Minimum code that solves the problem. Nothing speculative.',
|
|
673
|
+
'- Check `.agents/memory/patterns.md` for existing solutions.',
|
|
674
|
+
'',
|
|
675
|
+
'### G3 — Surgical Changes',
|
|
676
|
+
'- Touch only what you must. Match existing style.',
|
|
677
|
+
'- Log surprises in `.agents/memory/lessons.md`.',
|
|
678
|
+
'',
|
|
679
|
+
'### G4 — Goal-Driven Execution',
|
|
680
|
+
'- Define success criteria before implementing.',
|
|
681
|
+
'- Log non-obvious decisions in `.agents/memory/decisions.md`.',
|
|
682
|
+
'',
|
|
683
|
+
'### Memory Files',
|
|
684
|
+
'- `.agents/memory/decisions.md` — Read at session start, write when making decisions',
|
|
685
|
+
'- `.agents/memory/lessons.md` — Write when something unexpected happens',
|
|
686
|
+
'- `.agents/memory/patterns.md` — Write when a reusable pattern is validated',
|
|
687
|
+
'- `.agents/memory/context.md` — Update at session end with project state',
|
|
688
|
+
].join('\n');
|
|
689
|
+
}
|
|
690
|
+
|
|
658
691
|
return `# BMAD+ — AI Agent Configuration
|
|
659
692
|
|
|
660
693
|
## Project Context
|
|
@@ -678,7 +711,7 @@ ${agents.join('\n')}
|
|
|
678
711
|
## Communication
|
|
679
712
|
- User name: ${userName}
|
|
680
713
|
- Default language: ${language} for user-facing content, English for code and technical docs.
|
|
681
|
-
`;
|
|
714
|
+
${memorySection}`;
|
|
682
715
|
}
|
|
683
716
|
|
|
684
717
|
function generateConfigYaml(userName, language, projectDir) {
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BMAD+ Memory Command
|
|
3
|
+
* Manage persistent brain: status, export, consolidate
|
|
4
|
+
*
|
|
5
|
+
* Author: Laurent Rochetta
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const os = require('node:os');
|
|
11
|
+
const clack = require('@clack/prompts');
|
|
12
|
+
const pc = require('picocolors');
|
|
13
|
+
|
|
14
|
+
function countEntries(filePath) {
|
|
15
|
+
if (!fs.existsSync(filePath)) return { exists: false, count: 0, lastModified: null };
|
|
16
|
+
try {
|
|
17
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
18
|
+
const entries = content.match(/^### /gm);
|
|
19
|
+
const stat = fs.statSync(filePath);
|
|
20
|
+
return {
|
|
21
|
+
exists: true,
|
|
22
|
+
count: entries ? entries.length : 0,
|
|
23
|
+
lastModified: stat.mtime.toISOString().slice(0, 10),
|
|
24
|
+
sizeKB: (stat.size / 1024).toFixed(1),
|
|
25
|
+
};
|
|
26
|
+
} catch {
|
|
27
|
+
return { exists: true, count: 0, lastModified: null };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function countSessions(dir) {
|
|
32
|
+
if (!fs.existsSync(dir)) return 0;
|
|
33
|
+
try {
|
|
34
|
+
return fs.readdirSync(dir).filter(f => f.endsWith('.md')).length;
|
|
35
|
+
} catch { return 0; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
command: 'memory',
|
|
40
|
+
description: 'Manage BMAD+ persistent brain (status, export)',
|
|
41
|
+
options: [
|
|
42
|
+
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
43
|
+
],
|
|
44
|
+
subcommands: {
|
|
45
|
+
status: 'Show memory health report',
|
|
46
|
+
export: 'Export brain as portable archive',
|
|
47
|
+
},
|
|
48
|
+
action: async (subcommand, options) => {
|
|
49
|
+
const projectDir = path.resolve(options?.directory || process.cwd());
|
|
50
|
+
const cmd = subcommand || 'status';
|
|
51
|
+
|
|
52
|
+
clack.intro(pc.bgMagenta(pc.white(' 🧠 BMAD+ Memory Manager ')));
|
|
53
|
+
|
|
54
|
+
if (cmd === 'status') {
|
|
55
|
+
// ── Project Memory ──
|
|
56
|
+
const memoryDir = path.join(projectDir, '.agents', 'memory');
|
|
57
|
+
const hasProjectMemory = fs.existsSync(memoryDir);
|
|
58
|
+
|
|
59
|
+
clack.log.info(pc.bold('\n📁 Project Memory') + pc.dim(` (${projectDir})`));
|
|
60
|
+
|
|
61
|
+
if (hasProjectMemory) {
|
|
62
|
+
const decisions = countEntries(path.join(memoryDir, 'decisions.md'));
|
|
63
|
+
const lessons = countEntries(path.join(memoryDir, 'lessons.md'));
|
|
64
|
+
const patterns = countEntries(path.join(memoryDir, 'patterns.md'));
|
|
65
|
+
const context = countEntries(path.join(memoryDir, 'context.md'));
|
|
66
|
+
const sessions = countSessions(path.join(memoryDir, 'sessions'));
|
|
67
|
+
const brainLink = fs.existsSync(path.join(memoryDir, '.brain-link'));
|
|
68
|
+
|
|
69
|
+
clack.log.info(` decisions.md ${decisions.exists ? pc.green('✓') : pc.red('✗')} ${decisions.count} entries ${pc.dim(decisions.lastModified || '')}`);
|
|
70
|
+
clack.log.info(` lessons.md ${lessons.exists ? pc.green('✓') : pc.red('✗')} ${lessons.count} entries ${pc.dim(lessons.lastModified || '')}`);
|
|
71
|
+
clack.log.info(` patterns.md ${patterns.exists ? pc.green('✓') : pc.red('✗')} ${patterns.count} entries ${pc.dim(patterns.lastModified || '')}`);
|
|
72
|
+
clack.log.info(` context.md ${context.exists ? pc.green('✓') : pc.red('✗')} ${pc.dim(context.lastModified || '')}`);
|
|
73
|
+
clack.log.info(` sessions/ ${sessions > 0 ? pc.green('✓') : pc.dim('·')} ${sessions} handoff(s)`);
|
|
74
|
+
|
|
75
|
+
if (brainLink) {
|
|
76
|
+
try {
|
|
77
|
+
const link = JSON.parse(fs.readFileSync(path.join(memoryDir, '.brain-link'), 'utf8'));
|
|
78
|
+
clack.log.info(` ${pc.cyan('🔗 Linked brain')}: ${link.linked_brain}`);
|
|
79
|
+
} catch {
|
|
80
|
+
clack.log.info(` ${pc.cyan('🔗 Brain link exists')}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
clack.log.warn(' Not installed. Run: npx bmad-plus install (select Memory pack)');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Global Brain ──
|
|
88
|
+
const globalBrainDir = path.join(os.homedir(), '.bmad-plus', 'brain');
|
|
89
|
+
const hasGlobalBrain = fs.existsSync(globalBrainDir);
|
|
90
|
+
|
|
91
|
+
clack.log.info(pc.bold('\n🌐 Global Brain') + pc.dim(` (${globalBrainDir})`));
|
|
92
|
+
|
|
93
|
+
if (hasGlobalBrain) {
|
|
94
|
+
const identity = fs.existsSync(path.join(globalBrainDir, 'identity.yaml'));
|
|
95
|
+
const gDecisions = countEntries(path.join(globalBrainDir, 'decisions.md'));
|
|
96
|
+
const gLessons = countEntries(path.join(globalBrainDir, 'lessons.md'));
|
|
97
|
+
const gPatterns = countEntries(path.join(globalBrainDir, 'patterns.md'));
|
|
98
|
+
|
|
99
|
+
const projectsDir = path.join(globalBrainDir, 'projects');
|
|
100
|
+
let projectCount = 0;
|
|
101
|
+
if (fs.existsSync(projectsDir)) {
|
|
102
|
+
projectCount = fs.readdirSync(projectsDir).filter(f => f.endsWith('.yaml')).length;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const indexExists = fs.existsSync(path.join(globalBrainDir, 'projects-index.md'));
|
|
106
|
+
|
|
107
|
+
clack.log.info(` identity.yaml ${identity ? pc.green('✓') : pc.yellow('✗ missing')}`);
|
|
108
|
+
clack.log.info(` decisions.md ${gDecisions.exists ? pc.green('✓') : pc.dim('·')} ${gDecisions.count} entries`);
|
|
109
|
+
clack.log.info(` lessons.md ${gLessons.exists ? pc.green('✓') : pc.dim('·')} ${gLessons.count} entries`);
|
|
110
|
+
clack.log.info(` patterns.md ${gPatterns.exists ? pc.green('✓') : pc.dim('·')} ${gPatterns.count} entries`);
|
|
111
|
+
clack.log.info(` projects/ ${pc.green('✓')} ${projectCount} project(s) indexed`);
|
|
112
|
+
clack.log.info(` projects-index ${indexExists ? pc.green('✓') : pc.dim('·')}`);
|
|
113
|
+
} else {
|
|
114
|
+
clack.log.warn(' Not initialized. Run: npx bmad-plus install (select Memory pack)');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Health Score ──
|
|
118
|
+
let score = 0;
|
|
119
|
+
let maxScore = 0;
|
|
120
|
+
|
|
121
|
+
if (hasProjectMemory) {
|
|
122
|
+
const checks = [
|
|
123
|
+
fs.existsSync(path.join(memoryDir, 'decisions.md')),
|
|
124
|
+
fs.existsSync(path.join(memoryDir, 'lessons.md')),
|
|
125
|
+
fs.existsSync(path.join(memoryDir, 'patterns.md')),
|
|
126
|
+
fs.existsSync(path.join(memoryDir, 'context.md')),
|
|
127
|
+
countSessions(path.join(memoryDir, 'sessions')) > 0,
|
|
128
|
+
];
|
|
129
|
+
maxScore += checks.length;
|
|
130
|
+
score += checks.filter(Boolean).length;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (hasGlobalBrain) {
|
|
134
|
+
const gChecks = [
|
|
135
|
+
fs.existsSync(path.join(globalBrainDir, 'identity.yaml')),
|
|
136
|
+
fs.existsSync(path.join(globalBrainDir, 'decisions.md')),
|
|
137
|
+
fs.existsSync(path.join(globalBrainDir, 'lessons.md')),
|
|
138
|
+
fs.existsSync(path.join(globalBrainDir, 'patterns.md')),
|
|
139
|
+
fs.existsSync(path.join(globalBrainDir, 'projects')),
|
|
140
|
+
];
|
|
141
|
+
maxScore += gChecks.length;
|
|
142
|
+
score += gChecks.filter(Boolean).length;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (maxScore > 0) {
|
|
146
|
+
const pct = Math.round((score / maxScore) * 100);
|
|
147
|
+
const color = pct >= 80 ? pc.green : pct >= 50 ? pc.yellow : pc.red;
|
|
148
|
+
clack.log.info('');
|
|
149
|
+
clack.log.info(pc.bold(`🏥 Health: ${color(`${pct}%`)} (${score}/${maxScore} checks)`));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
clack.log.info('');
|
|
153
|
+
} else if (cmd === 'export') {
|
|
154
|
+
// Export brain as text dump
|
|
155
|
+
const globalBrainDir = path.join(os.homedir(), '.bmad-plus', 'brain');
|
|
156
|
+
if (!fs.existsSync(globalBrainDir)) {
|
|
157
|
+
clack.log.error('Global brain not found. Nothing to export.');
|
|
158
|
+
clack.outro(pc.red('Export failed.'));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const exportPath = path.join(projectDir, `bmad-brain-export-${new Date().toISOString().slice(0, 10)}.md`);
|
|
163
|
+
const sections = [];
|
|
164
|
+
|
|
165
|
+
sections.push('# BMAD+ Brain Export', `> Exported: ${new Date().toISOString()}`, '');
|
|
166
|
+
|
|
167
|
+
for (const file of ['identity.yaml', 'decisions.md', 'lessons.md', 'patterns.md']) {
|
|
168
|
+
const fp = path.join(globalBrainDir, file);
|
|
169
|
+
if (fs.existsSync(fp)) {
|
|
170
|
+
sections.push(`## ${file}`, '```', fs.readFileSync(fp, 'utf8').trim(), '```', '');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Project index
|
|
175
|
+
const projectsDir = path.join(globalBrainDir, 'projects');
|
|
176
|
+
if (fs.existsSync(projectsDir)) {
|
|
177
|
+
sections.push('## Projects', '');
|
|
178
|
+
for (const f of fs.readdirSync(projectsDir)) {
|
|
179
|
+
if (f.endsWith('.yaml')) {
|
|
180
|
+
sections.push(`### ${f}`, '```yaml', fs.readFileSync(path.join(projectsDir, f), 'utf8').trim(), '```', '');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
fs.writeFileSync(exportPath, sections.join('\n'), 'utf8');
|
|
186
|
+
clack.log.success(`📦 Brain exported to: ${exportPath}`);
|
|
187
|
+
} else {
|
|
188
|
+
clack.log.error(`Unknown subcommand: ${cmd}`);
|
|
189
|
+
clack.log.info('Available: status, export');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
clack.outro(pc.green('Done! 🧠'));
|
|
193
|
+
},
|
|
194
|
+
};
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BMAD+ Scan Command
|
|
3
|
+
* Scan directories to discover projects, detect stacks, and index them in the global brain.
|
|
4
|
+
* Interactive validation — user confirms each project before indexing.
|
|
5
|
+
*
|
|
6
|
+
* Author: Laurent Rochetta
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
const fs = require('node:fs');
|
|
11
|
+
const os = require('node:os');
|
|
12
|
+
const crypto = require('node:crypto');
|
|
13
|
+
const clack = require('@clack/prompts');
|
|
14
|
+
const pc = require('picocolors');
|
|
15
|
+
|
|
16
|
+
// Project detection markers (priority order)
|
|
17
|
+
const PROJECT_MARKERS = [
|
|
18
|
+
{ file: 'package.json', stack: 'Node.js', detect: (dir) => {
|
|
19
|
+
try {
|
|
20
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
21
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
22
|
+
if (deps['next']) return 'Next.js';
|
|
23
|
+
if (deps['nuxt']) return 'Nuxt';
|
|
24
|
+
if (deps['react']) return 'React';
|
|
25
|
+
if (deps['vue']) return 'Vue.js';
|
|
26
|
+
if (deps['svelte']) return 'Svelte';
|
|
27
|
+
if (deps['express']) return 'Express';
|
|
28
|
+
if (deps['fastify']) return 'Fastify';
|
|
29
|
+
if (deps['electron']) return 'Electron';
|
|
30
|
+
if (deps['tauri']) return 'Tauri';
|
|
31
|
+
return 'Node.js';
|
|
32
|
+
} catch { return 'Node.js'; }
|
|
33
|
+
}},
|
|
34
|
+
{ file: 'Cargo.toml', stack: 'Rust' },
|
|
35
|
+
{ file: 'pyproject.toml', stack: 'Python' },
|
|
36
|
+
{ file: 'requirements.txt', stack: 'Python' },
|
|
37
|
+
{ file: 'go.mod', stack: 'Go' },
|
|
38
|
+
{ file: 'composer.json', stack: 'PHP' },
|
|
39
|
+
{ file: 'Gemfile', stack: 'Ruby' },
|
|
40
|
+
{ file: 'pom.xml', stack: 'Java' },
|
|
41
|
+
{ file: 'build.gradle', stack: 'Java/Kotlin' },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// Directories to skip during scanning
|
|
45
|
+
const SKIP_DIRS = new Set([
|
|
46
|
+
'node_modules', '.git', 'vendor', '__pycache__', 'dist', 'build',
|
|
47
|
+
'.next', '.nuxt', '.svelte-kit', 'target', '.venv', 'venv',
|
|
48
|
+
'.cache', '.output', 'coverage', '.turbo', '.angular',
|
|
49
|
+
'$RECYCLE.BIN', 'System Volume Information', 'Windows',
|
|
50
|
+
'Program Files', 'Program Files (x86)', 'ProgramData',
|
|
51
|
+
'AppData', 'Recovery', 'PerfLogs',
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
function getProjectStatus(dir) {
|
|
55
|
+
try {
|
|
56
|
+
const stat = fs.statSync(dir);
|
|
57
|
+
const daysSince = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60 * 24);
|
|
58
|
+
if (daysSince < 30) return 'active';
|
|
59
|
+
if (daysSince < 180) return 'paused';
|
|
60
|
+
return 'archived';
|
|
61
|
+
} catch { return 'unknown'; }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getStatusIcon(status) {
|
|
65
|
+
switch (status) {
|
|
66
|
+
case 'active': return pc.green('●');
|
|
67
|
+
case 'paused': return pc.yellow('◐');
|
|
68
|
+
case 'archived': return pc.dim('○');
|
|
69
|
+
default: return pc.dim('?');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getProjectName(dir) {
|
|
74
|
+
try {
|
|
75
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
76
|
+
if (fs.existsSync(pkgPath)) {
|
|
77
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
78
|
+
if (pkg.name) return pkg.name;
|
|
79
|
+
}
|
|
80
|
+
} catch {}
|
|
81
|
+
return path.basename(dir);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function hasBmadInstalled(dir) {
|
|
85
|
+
return fs.existsSync(path.join(dir, '.agents')) ||
|
|
86
|
+
fs.existsSync(path.join(dir, '_bmad'));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function scanDirectory(rootDir, maxDepth = 4, currentDepth = 0) {
|
|
90
|
+
const projects = [];
|
|
91
|
+
|
|
92
|
+
if (currentDepth > maxDepth) return projects;
|
|
93
|
+
|
|
94
|
+
let entries;
|
|
95
|
+
try {
|
|
96
|
+
entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
|
97
|
+
} catch {
|
|
98
|
+
return projects; // Permission denied or inaccessible
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check if current dir is a project
|
|
102
|
+
for (const marker of PROJECT_MARKERS) {
|
|
103
|
+
if (fs.existsSync(path.join(rootDir, marker.file))) {
|
|
104
|
+
const stack = marker.detect ? marker.detect(rootDir) : marker.stack;
|
|
105
|
+
projects.push({
|
|
106
|
+
path: rootDir,
|
|
107
|
+
name: getProjectName(rootDir),
|
|
108
|
+
stack,
|
|
109
|
+
status: getProjectStatus(rootDir),
|
|
110
|
+
bmad: hasBmadInstalled(rootDir),
|
|
111
|
+
hasAgentsMd: fs.existsSync(path.join(rootDir, 'AGENTS.md')),
|
|
112
|
+
hasGit: fs.existsSync(path.join(rootDir, '.git')),
|
|
113
|
+
});
|
|
114
|
+
return projects; // Don't recurse into project subdirs
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Also detect by .git alone (any project with version control)
|
|
119
|
+
if (fs.existsSync(path.join(rootDir, '.git')) && currentDepth > 0) {
|
|
120
|
+
projects.push({
|
|
121
|
+
path: rootDir,
|
|
122
|
+
name: getProjectName(rootDir),
|
|
123
|
+
stack: 'Unknown',
|
|
124
|
+
status: getProjectStatus(rootDir),
|
|
125
|
+
bmad: hasBmadInstalled(rootDir),
|
|
126
|
+
hasAgentsMd: fs.existsSync(path.join(rootDir, 'AGENTS.md')),
|
|
127
|
+
hasGit: true,
|
|
128
|
+
});
|
|
129
|
+
return projects;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Recurse into subdirectories
|
|
133
|
+
for (const entry of entries) {
|
|
134
|
+
if (!entry.isDirectory()) continue;
|
|
135
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
136
|
+
if (entry.name.startsWith('.') && entry.name !== '.git') continue;
|
|
137
|
+
|
|
138
|
+
const subPath = path.join(rootDir, entry.name);
|
|
139
|
+
const subProjects = scanDirectory(subPath, maxDepth, currentDepth + 1);
|
|
140
|
+
projects.push(...subProjects);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return projects;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = {
|
|
147
|
+
command: 'scan',
|
|
148
|
+
description: 'Scan directories to discover and index projects in the global brain',
|
|
149
|
+
options: [
|
|
150
|
+
['-d, --directory <path>', 'Directory to scan (default: current directory)'],
|
|
151
|
+
['--depth <n>', 'Max depth to scan (default: 4)', '4'],
|
|
152
|
+
['-y, --yes', 'Index all projects without prompting'],
|
|
153
|
+
],
|
|
154
|
+
action: async (options) => {
|
|
155
|
+
const scanDir = path.resolve(options.directory || process.cwd());
|
|
156
|
+
const maxDepth = parseInt(options.depth) || 4;
|
|
157
|
+
|
|
158
|
+
clack.intro(pc.bgMagenta(pc.white(' 🧠 BMAD+ Project Scanner ')));
|
|
159
|
+
|
|
160
|
+
// Verify directory exists
|
|
161
|
+
if (!fs.existsSync(scanDir)) {
|
|
162
|
+
clack.log.error(`Directory not found: ${scanDir}`);
|
|
163
|
+
clack.outro(pc.red('Scan failed.'));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Scan
|
|
168
|
+
const spinner = clack.spinner();
|
|
169
|
+
spinner.start(`Scanning ${scanDir} (depth: ${maxDepth})...`);
|
|
170
|
+
|
|
171
|
+
const projects = scanDirectory(scanDir, maxDepth);
|
|
172
|
+
|
|
173
|
+
if (projects.length === 0) {
|
|
174
|
+
spinner.stop('No projects found.');
|
|
175
|
+
clack.outro('Try scanning a different directory or increasing --depth');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
spinner.stop(`Found ${pc.bold(projects.length)} project(s)`);
|
|
180
|
+
|
|
181
|
+
// Display table
|
|
182
|
+
clack.log.info('');
|
|
183
|
+
clack.log.info(pc.bold(' # Status BMAD+ Stack Name Path'));
|
|
184
|
+
clack.log.info(pc.dim(' ' + '─'.repeat(90)));
|
|
185
|
+
|
|
186
|
+
projects.forEach((p, i) => {
|
|
187
|
+
const num = String(i + 1).padStart(3);
|
|
188
|
+
const status = getStatusIcon(p.status) + ' ' + p.status.padEnd(8);
|
|
189
|
+
const bmad = p.bmad ? pc.green('✓') : pc.dim('·');
|
|
190
|
+
const stack = p.stack.padEnd(16);
|
|
191
|
+
const name = p.name.substring(0, 20).padEnd(20);
|
|
192
|
+
const projPath = p.path.length > 40 ? '...' + p.path.slice(-37) : p.path;
|
|
193
|
+
clack.log.info(` ${num} ${status} ${bmad} ${stack} ${name} ${pc.dim(projPath)}`);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
clack.log.info('');
|
|
197
|
+
|
|
198
|
+
// Interactive validation
|
|
199
|
+
const globalBrainDir = path.join(os.homedir(), '.bmad-plus', 'brain', 'projects');
|
|
200
|
+
|
|
201
|
+
if (options.yes) {
|
|
202
|
+
// Auto-index all
|
|
203
|
+
const fsExtra = require('fs-extra');
|
|
204
|
+
fsExtra.ensureDirSync(globalBrainDir);
|
|
205
|
+
|
|
206
|
+
let indexed = 0;
|
|
207
|
+
for (const proj of projects) {
|
|
208
|
+
const hash = crypto.createHash('sha256').update(proj.path).digest('hex').slice(0, 8);
|
|
209
|
+
const meta = {
|
|
210
|
+
path: proj.path,
|
|
211
|
+
name: proj.name,
|
|
212
|
+
hash,
|
|
213
|
+
stack: proj.stack,
|
|
214
|
+
status: proj.status,
|
|
215
|
+
bmad_installed: proj.bmad,
|
|
216
|
+
has_git: proj.hasGit,
|
|
217
|
+
last_scanned: new Date().toISOString().slice(0, 10),
|
|
218
|
+
};
|
|
219
|
+
fs.writeFileSync(
|
|
220
|
+
path.join(globalBrainDir, `${hash}.yaml`),
|
|
221
|
+
Object.entries(meta).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n'),
|
|
222
|
+
'utf8'
|
|
223
|
+
);
|
|
224
|
+
indexed++;
|
|
225
|
+
}
|
|
226
|
+
clack.log.success(`✅ ${indexed} project(s) indexed in ${globalBrainDir}`);
|
|
227
|
+
} else {
|
|
228
|
+
// Interactive mode
|
|
229
|
+
const action = await clack.select({
|
|
230
|
+
message: `${projects.length} project(s) found. What to do?`,
|
|
231
|
+
options: [
|
|
232
|
+
{ value: 'all', label: `✅ Index all ${projects.length} projects` },
|
|
233
|
+
{ value: 'select', label: '✏️ Select which to index' },
|
|
234
|
+
{ value: 'none', label: '⏭️ Skip — don\'t index anything' },
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (clack.isCancel(action) || action === 'none') {
|
|
239
|
+
clack.cancel('Scan cancelled.');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const fsExtra = require('fs-extra');
|
|
244
|
+
fsExtra.ensureDirSync(globalBrainDir);
|
|
245
|
+
|
|
246
|
+
let toIndex = projects;
|
|
247
|
+
|
|
248
|
+
if (action === 'select') {
|
|
249
|
+
const selected = await clack.multiselect({
|
|
250
|
+
message: 'Select projects to index:',
|
|
251
|
+
options: projects.map((p, i) => ({
|
|
252
|
+
value: i,
|
|
253
|
+
label: `${p.name} (${p.stack})`,
|
|
254
|
+
hint: `${p.status} — ${p.path}`,
|
|
255
|
+
})),
|
|
256
|
+
required: false,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (clack.isCancel(selected)) {
|
|
260
|
+
clack.cancel('Scan cancelled.');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
toIndex = selected.map(i => projects[i]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let indexed = 0;
|
|
268
|
+
for (const proj of toIndex) {
|
|
269
|
+
const hash = crypto.createHash('sha256').update(proj.path).digest('hex').slice(0, 8);
|
|
270
|
+
const meta = {
|
|
271
|
+
path: proj.path,
|
|
272
|
+
name: proj.name,
|
|
273
|
+
hash,
|
|
274
|
+
stack: proj.stack,
|
|
275
|
+
status: proj.status,
|
|
276
|
+
bmad_installed: proj.bmad,
|
|
277
|
+
has_git: proj.hasGit,
|
|
278
|
+
last_scanned: new Date().toISOString().slice(0, 10),
|
|
279
|
+
};
|
|
280
|
+
fs.writeFileSync(
|
|
281
|
+
path.join(globalBrainDir, `${hash}.yaml`),
|
|
282
|
+
Object.entries(meta).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n'),
|
|
283
|
+
'utf8'
|
|
284
|
+
);
|
|
285
|
+
indexed++;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
clack.log.success(`✅ ${indexed} project(s) indexed in global brain`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Generate human-readable index
|
|
292
|
+
const indexPath = path.join(os.homedir(), '.bmad-plus', 'brain', 'projects-index.md');
|
|
293
|
+
const existingProjects = [];
|
|
294
|
+
if (fs.existsSync(globalBrainDir)) {
|
|
295
|
+
for (const f of fs.readdirSync(globalBrainDir)) {
|
|
296
|
+
if (!f.endsWith('.yaml')) continue;
|
|
297
|
+
try {
|
|
298
|
+
const content = fs.readFileSync(path.join(globalBrainDir, f), 'utf8');
|
|
299
|
+
const meta = {};
|
|
300
|
+
for (const line of content.split('\n')) {
|
|
301
|
+
const m = line.match(/^(\w+):\s*(.+)$/);
|
|
302
|
+
if (m) {
|
|
303
|
+
try { meta[m[1]] = JSON.parse(m[2]); } catch { meta[m[1]] = m[2]; }
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
existingProjects.push(meta);
|
|
307
|
+
} catch {}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const indexContent = [
|
|
312
|
+
'---',
|
|
313
|
+
'title: Project Index',
|
|
314
|
+
`last_updated: "${new Date().toISOString().slice(0, 10)}"`,
|
|
315
|
+
`total_projects: ${existingProjects.length}`,
|
|
316
|
+
'---',
|
|
317
|
+
'',
|
|
318
|
+
'# Project Index',
|
|
319
|
+
'',
|
|
320
|
+
`> Auto-generated by \`npx bmad-plus scan\` — ${new Date().toISOString().slice(0, 10)}`,
|
|
321
|
+
'',
|
|
322
|
+
'| Status | Name | Stack | BMAD+ | Path |',
|
|
323
|
+
'|--------|------|-------|-------|------|',
|
|
324
|
+
...existingProjects.map(p =>
|
|
325
|
+
`| ${p.status || '?'} | ${p.name || '?'} | ${p.stack || '?'} | ${p.bmad_installed ? '✓' : '·'} | \`${p.path || '?'}\` |`
|
|
326
|
+
),
|
|
327
|
+
'',
|
|
328
|
+
];
|
|
329
|
+
|
|
330
|
+
fs.writeFileSync(indexPath, indexContent.join('\n'), 'utf8');
|
|
331
|
+
clack.log.info(`📋 Project index updated: ${indexPath}`);
|
|
332
|
+
|
|
333
|
+
clack.outro(pc.green('Scan complete! 🧠'));
|
|
334
|
+
},
|
|
335
|
+
};
|
package/tools/cli/i18n.js
CHANGED
|
@@ -10,7 +10,7 @@ const LANGUAGES = {
|
|
|
10
10
|
flag: '🇬🇧',
|
|
11
11
|
name: 'English',
|
|
12
12
|
locale: 'en',
|
|
13
|
-
installer_title: ' BMAD+ Installer v0.7.
|
|
13
|
+
installer_title: ' BMAD+ Installer v0.7.1 ',
|
|
14
14
|
select_language: 'Select your language',
|
|
15
15
|
installing_to: 'Installing to',
|
|
16
16
|
select_packs: 'Which packs to install? (Core is always included)',
|
|
@@ -76,13 +76,23 @@ const LANGUAGES = {
|
|
|
76
76
|
guide_example_backup: '🗂️ Backup: "/backup create" → ZIP timestamped',
|
|
77
77
|
guide_example_animated: '🎬 Animated: "/animated build hero.mp4"',
|
|
78
78
|
guide_example_osint: '🔍 OSINT: "Shadow, investigate John Doe"',
|
|
79
|
+
guide_memory: '🧠 Persistent Brain',
|
|
80
|
+
guide_dev_studio: '🏗️ Dev Studio',
|
|
81
|
+
guide_example_memory_1: '🧠 Memory: "Zecher, scan projects in D:\\DEV"',
|
|
82
|
+
guide_example_memory_2: '🧠 Memory: "Zecher, where were we?"',
|
|
83
|
+
guide_example_memory_3: '🧠 Memory: "Zecher, consolidate memory"',
|
|
84
|
+
guide_example_dev_studio_1: '🏗️ Dev Studio: "Miriam, brainstorm a productivity app"',
|
|
85
|
+
guide_example_dev_studio_2: '🏗️ Dev Studio: "Bezalel, design the architecture"',
|
|
86
|
+
guide_example_dev_studio_3: '🏗️ Dev Studio: "Oholiab, implement story S1"',
|
|
87
|
+
brain_detected: 'Existing brain detected',
|
|
88
|
+
brain_created: 'Global brain created',
|
|
79
89
|
},
|
|
80
90
|
|
|
81
91
|
fr: {
|
|
82
92
|
flag: '🇫🇷',
|
|
83
93
|
name: 'Français',
|
|
84
94
|
locale: 'fr',
|
|
85
|
-
installer_title: ' BMAD+ Installeur v0.7.
|
|
95
|
+
installer_title: ' BMAD+ Installeur v0.7.1 ',
|
|
86
96
|
select_language: 'Choisissez votre langue',
|
|
87
97
|
installing_to: 'Installation dans',
|
|
88
98
|
select_packs: 'Quels packs installer ? (Core est toujours inclus)',
|
|
@@ -146,13 +156,23 @@ const LANGUAGES = {
|
|
|
146
156
|
guide_example_backup: '🗂️ Backup : "/backup create" → ZIP horodaté',
|
|
147
157
|
guide_example_animated: '🎬 Animé : "/animated build hero.mp4"',
|
|
148
158
|
guide_example_osint: '🔍 OSINT : "Shadow, investigate John Doe"',
|
|
159
|
+
guide_memory: '🧠 Cerveau persistant',
|
|
160
|
+
guide_dev_studio: '🏗️ Dev Studio',
|
|
161
|
+
guide_example_memory_1: '🧠 Mémoire : "Zecher, scanne les projets dans D:\\DEV"',
|
|
162
|
+
guide_example_memory_2: '🧠 Mémoire : "Zecher, où en étions-nous ?"',
|
|
163
|
+
guide_example_memory_3: '🧠 Mémoire : "Zecher, consolide la mémoire"',
|
|
164
|
+
guide_example_dev_studio_1: '🏗️ Dev Studio : "Miriam, brainstorme une app de productivité"',
|
|
165
|
+
guide_example_dev_studio_2: '🏗️ Dev Studio : "Bezalel, conçois l\'architecture"',
|
|
166
|
+
guide_example_dev_studio_3: '🏗️ Dev Studio : "Oholiab, implémente la story S1"',
|
|
167
|
+
brain_detected: 'Cerveau existant détecté',
|
|
168
|
+
brain_created: 'Cerveau global créé',
|
|
149
169
|
},
|
|
150
170
|
|
|
151
171
|
es: {
|
|
152
172
|
flag: '🇪🇸',
|
|
153
173
|
name: 'Español',
|
|
154
174
|
locale: 'es',
|
|
155
|
-
installer_title: ' BMAD+ Instalador v0.7.
|
|
175
|
+
installer_title: ' BMAD+ Instalador v0.7.1 ',
|
|
156
176
|
select_language: 'Seleccione su idioma',
|
|
157
177
|
installing_to: 'Instalando en',
|
|
158
178
|
select_packs: '¿Qué packs instalar? (Core siempre está incluido)',
|
|
@@ -222,7 +242,7 @@ const LANGUAGES = {
|
|
|
222
242
|
flag: '🇩🇪',
|
|
223
243
|
name: 'Deutsch',
|
|
224
244
|
locale: 'de',
|
|
225
|
-
installer_title: ' BMAD+ Installer v0.7.
|
|
245
|
+
installer_title: ' BMAD+ Installer v0.7.1 ',
|
|
226
246
|
select_language: 'Wählen Sie Ihre Sprache',
|
|
227
247
|
installing_to: 'Installiere in',
|
|
228
248
|
select_packs: 'Welche Packs installieren? (Core ist immer enthalten)',
|
|
@@ -292,7 +312,7 @@ const LANGUAGES = {
|
|
|
292
312
|
flag: '🇧🇷',
|
|
293
313
|
name: 'Português (Brasil)',
|
|
294
314
|
locale: 'pt-BR',
|
|
295
|
-
installer_title: ' BMAD+ Instalador v0.7.
|
|
315
|
+
installer_title: ' BMAD+ Instalador v0.7.1 ',
|
|
296
316
|
select_language: 'Selecione seu idioma',
|
|
297
317
|
installing_to: 'Instalando em',
|
|
298
318
|
select_packs: 'Quais packs instalar? (Core sempre está incluído)',
|
|
@@ -362,7 +382,7 @@ const LANGUAGES = {
|
|
|
362
382
|
flag: '🇷🇺',
|
|
363
383
|
name: 'Русский',
|
|
364
384
|
locale: 'ru',
|
|
365
|
-
installer_title: ' BMAD+ Установщик v0.7.
|
|
385
|
+
installer_title: ' BMAD+ Установщик v0.7.1 ',
|
|
366
386
|
select_language: 'Выберите язык',
|
|
367
387
|
installing_to: 'Установка в',
|
|
368
388
|
select_packs: 'Какие пакеты установить? (Core всегда включён)',
|
|
@@ -432,7 +452,7 @@ const LANGUAGES = {
|
|
|
432
452
|
flag: '🇨🇳',
|
|
433
453
|
name: '中文 (简体)',
|
|
434
454
|
locale: 'zh-CN',
|
|
435
|
-
installer_title: ' BMAD+ 安装程序 v0.7.
|
|
455
|
+
installer_title: ' BMAD+ 安装程序 v0.7.1 ',
|
|
436
456
|
select_language: '选择您的语言',
|
|
437
457
|
installing_to: '安装到',
|
|
438
458
|
select_packs: '安装哪些包?(Core 始终包含)',
|
|
@@ -502,7 +522,7 @@ const LANGUAGES = {
|
|
|
502
522
|
flag: '🇮🇱',
|
|
503
523
|
name: 'עברית',
|
|
504
524
|
locale: 'he',
|
|
505
|
-
installer_title: ' BMAD+ מתקין v0.7.
|
|
525
|
+
installer_title: ' BMAD+ מתקין v0.7.1 ',
|
|
506
526
|
select_language: 'בחר את השפה שלך',
|
|
507
527
|
installing_to: 'מתקין ב',
|
|
508
528
|
select_packs: 'אילו חבילות להתקין? (Core תמיד כלול)',
|
|
@@ -572,7 +592,7 @@ const LANGUAGES = {
|
|
|
572
592
|
flag: '🇯🇵',
|
|
573
593
|
name: '日本語',
|
|
574
594
|
locale: 'ja',
|
|
575
|
-
installer_title: ' BMAD+ インストーラー v0.7.
|
|
595
|
+
installer_title: ' BMAD+ インストーラー v0.7.1 ',
|
|
576
596
|
select_language: '言語を選択してください',
|
|
577
597
|
installing_to: 'インストール先',
|
|
578
598
|
select_packs: 'どのパックをインストールしますか?(Coreは常に含まれます)',
|
|
@@ -642,7 +662,7 @@ const LANGUAGES = {
|
|
|
642
662
|
flag: '🇮🇹',
|
|
643
663
|
name: 'Italiano',
|
|
644
664
|
locale: 'it',
|
|
645
|
-
installer_title: ' BMAD+ Installatore v0.7.
|
|
665
|
+
installer_title: ' BMAD+ Installatore v0.7.1 ',
|
|
646
666
|
select_language: 'Seleziona la tua lingua',
|
|
647
667
|
installing_to: 'Installazione in',
|
|
648
668
|
select_packs: 'Quali pack installare? (Core è sempre incluso)',
|