bmad-plus 0.12.2 → 0.14.0
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 +47 -0
- package/README.md +96 -461
- package/package.json +12 -5
- package/readme-international/README.de.md +42 -9
- package/readme-international/README.es.md +43 -10
- package/readme-international/README.fr.md +42 -9
- package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +5 -3
- package/src/bmad-plus/module.yaml +270 -220
- package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
- package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +52 -426
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +22 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +60 -482
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
- package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
- package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
- package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
- package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
- package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
- package/src/bmad-plus/packs/pack-memory/README.md +6 -0
- package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +12 -5
- package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
- package/src/bmad-plus/packs/pack-seo/scripts/seo_apis.py +8 -8
- package/src/bmad-plus/packs/pack-seo/scripts/seo_fetch.py +1 -2
- package/src/bmad-plus/packs/pack-seo/scripts/seo_report.py +0 -1
- package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
- package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
- package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
- package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +4 -4
- package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +5 -3
- package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
- package/tools/bmad-plus-npx.js +4 -2
- package/tools/build/adapters.config.js +60 -51
- package/tools/build/check-counts.js +52 -54
- package/tools/build/check-install-contract.js +450 -0
- package/tools/build/generate-adapters.js +252 -56
- package/tools/build/generate.js +364 -14
- package/tools/build/generated-adapters/.codex/AGENTS.md +20 -7
- package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +20 -7
- package/tools/build/generated-adapters/.opencode/AGENTS.md +20 -7
- package/tools/build/generated-adapters/AGENTS.md +20 -7
- package/tools/build/generated-adapters/CLAUDE.md +20 -7
- package/tools/build/generated-adapters/CONVENTIONS.md +20 -7
- package/tools/build/generated-adapters/GEMINI.md +20 -7
- package/tools/build/module.template.yaml +82 -0
- package/tools/build/pack-delivery.js +78 -0
- package/tools/cli/bmad-plus-cli.js +16 -1
- package/tools/cli/commands/doctor.js +49 -217
- package/tools/cli/commands/install.js +109 -164
- package/tools/cli/commands/studio.js +68 -0
- package/tools/cli/commands/uninstall.js +173 -65
- package/tools/cli/commands/update-check.js +31 -0
- package/tools/cli/commands/update-policy.js +39 -0
- package/tools/cli/commands/update.js +102 -113
- package/tools/cli/i18n.js +60 -0
- package/tools/cli/lib/ide-config.js +4 -261
- package/tools/cli/lib/install-manifest.js +17 -0
- package/tools/cli/lib/installation-health.js +360 -0
- package/tools/cli/lib/installed-adapters.js +89 -0
- package/tools/cli/lib/npm-runner.js +177 -0
- package/tools/cli/lib/pack-copy.js +62 -66
- package/tools/cli/lib/packs.js +546 -3
- package/tools/cli/lib/python-health.js +233 -0
- package/tools/cli/lib/python-provision.js +2 -2
- package/tools/cli/lib/studio.js +310 -0
- package/tools/cli/lib/update-check.js +153 -0
- package/tools/cli/lib/update-dispatch.js +182 -0
- package/tools/cli/lib/update-policy.js +90 -0
- package/tools/cli/lib/update-transaction.js +334 -0
- package/tools/release/publication-content.js +901 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Inspect installed Dev Studio routes and prepare actual host instructions. */
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { installedPack, validatePack, prepare } = require('../lib/studio');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
command: 'studio <action> [workflow]',
|
|
7
|
+
description: 'List Dev Studio workflows or prepare read-only execution context',
|
|
8
|
+
options: [
|
|
9
|
+
['-d, --directory <path>', 'Installed project directory'],
|
|
10
|
+
['--request <text>', 'Task scope for the host assistant'],
|
|
11
|
+
[
|
|
12
|
+
'--input <file>',
|
|
13
|
+
'Project-relative input file (repeatable)',
|
|
14
|
+
(file, files) => [...files, file],
|
|
15
|
+
[],
|
|
16
|
+
],
|
|
17
|
+
['--json', 'Print machine-readable context and evidence'],
|
|
18
|
+
],
|
|
19
|
+
action: (action, workflow, options = {}) => {
|
|
20
|
+
try {
|
|
21
|
+
const projectDir = path.resolve(options.directory || process.cwd());
|
|
22
|
+
let result;
|
|
23
|
+
if (action === 'list' && !workflow) {
|
|
24
|
+
const { catalog, resources } = validatePack(installedPack(projectDir));
|
|
25
|
+
result = {
|
|
26
|
+
schemaVersion: 1,
|
|
27
|
+
execution: 'host-managed',
|
|
28
|
+
...catalog,
|
|
29
|
+
validatedResources: resources.length,
|
|
30
|
+
};
|
|
31
|
+
} else if (action === 'prepare' && workflow) {
|
|
32
|
+
result = prepare({ projectDir, workflow, request: options.request, inputs: options.input });
|
|
33
|
+
} else throw new Error('Use studio list or studio prepare WORKFLOW.');
|
|
34
|
+
process.exitCode = result.status === 'needs-input' ? 2 : 0;
|
|
35
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
36
|
+
else if (action === 'list')
|
|
37
|
+
console.log(
|
|
38
|
+
result.workflows.map((w) => `${w.id} (${w.agent}; input: ${w.input})`).join('\n')
|
|
39
|
+
);
|
|
40
|
+
else
|
|
41
|
+
console.log(
|
|
42
|
+
[
|
|
43
|
+
`${result.workflow}: ${result.status} — prepared for the host, not executed.`,
|
|
44
|
+
`Project: ${result.projectDir}`,
|
|
45
|
+
`Request: ${result.request || '(none supplied)'}`,
|
|
46
|
+
`Resolved configuration:\n${JSON.stringify(result.config.values, null, 2)}`,
|
|
47
|
+
`Defaulted fields: ${result.config.defaultsUsed.join(', ') || '(none)'}`,
|
|
48
|
+
`Proposed report: ${result.outputPath}`,
|
|
49
|
+
...result.missingInputs,
|
|
50
|
+
...result.instructions.map(
|
|
51
|
+
(item) => `\n--- ${item.path} (${item.sha256}) ---\n${item.content}`
|
|
52
|
+
),
|
|
53
|
+
...result.inputs.map(
|
|
54
|
+
(item) => `\n--- Project input: ${item.path} (${item.sha256}) ---\n${item.content}`
|
|
55
|
+
),
|
|
56
|
+
result.previousReport
|
|
57
|
+
? `\n--- Continue existing report ---\n${result.previousReport.content}`
|
|
58
|
+
: '',
|
|
59
|
+
].join('\n')
|
|
60
|
+
);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
if (options.json)
|
|
64
|
+
console.log(JSON.stringify({ schemaVersion: 1, status: 'error', error: error.message }));
|
|
65
|
+
else console.error('Dev Studio: ' + error.message);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -1,101 +1,209 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* BMAD+ Uninstall Command
|
|
3
|
-
* Removes BMAD+
|
|
4
|
-
* Internationalized — uses i18n system
|
|
5
|
-
*
|
|
3
|
+
* Removes identifiable BMAD+ files while preserving project memory and edits.
|
|
6
4
|
* Author: Laurent Rochetta
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
const path = require('node:path');
|
|
10
8
|
const fs = require('node:fs');
|
|
11
|
-
const fsExtra = require('fs-extra');
|
|
12
9
|
const clack = require('@clack/prompts');
|
|
13
10
|
const pc = require('picocolors');
|
|
14
11
|
const { t } = require('../i18n');
|
|
12
|
+
const { PACKS, DERIVED } = require('../lib/packs');
|
|
13
|
+
const { readInstallManifest } = require('../lib/install-manifest');
|
|
14
|
+
const { generateUserFiles, USER_CONFIG_MARKER } = require('../../build/generate-adapters');
|
|
15
|
+
const { contentHash } = require('../lib/installed-adapters');
|
|
16
|
+
|
|
17
|
+
// Never follow a junction/symlink in a path used for deletion, including parents.
|
|
18
|
+
function safeStat(projectDir, target) {
|
|
19
|
+
const relative = path.relative(projectDir, target);
|
|
20
|
+
if (!relative || relative === '..' || relative.startsWith('..' + path.sep) || path.isAbsolute(relative)) {
|
|
21
|
+
throw new Error('Path outside the project: ' + target);
|
|
22
|
+
}
|
|
23
|
+
let current = projectDir;
|
|
24
|
+
let stat;
|
|
25
|
+
for (const part of relative.split(path.sep)) {
|
|
26
|
+
current = path.join(current, part);
|
|
27
|
+
try {
|
|
28
|
+
stat = fs.lstatSync(current);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.code === 'ENOENT') return undefined;
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
if (stat.isSymbolicLink()) {
|
|
34
|
+
const error = new Error('Symbolic link or junction: ' + current);
|
|
35
|
+
error.code = 'BMAD_UNSAFE_LINK';
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return stat;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function removalPlan(projectDir, manifest, manifestPath) {
|
|
43
|
+
const packageRoot = path.join(__dirname, '..', '..', '..');
|
|
44
|
+
const sourceRoot = path.join(packageRoot, 'src', 'bmad-plus');
|
|
45
|
+
const files = new Map();
|
|
46
|
+
const directories = new Set();
|
|
47
|
+
const targetPath = (...parts) => path.join(projectDir, ...parts);
|
|
48
|
+
|
|
49
|
+
// Old manifests contain pack IDs, not file hashes. Match shipped files byte for
|
|
50
|
+
// byte; anything unknown or modified stays, including files inside a pack.
|
|
51
|
+
function matchSource(source, target) {
|
|
52
|
+
const targetStat = safeStat(projectDir, target);
|
|
53
|
+
if (!targetStat || !fs.existsSync(source)) return;
|
|
54
|
+
const sourceStat = fs.lstatSync(source);
|
|
55
|
+
if (sourceStat.isDirectory() && targetStat.isDirectory()) {
|
|
56
|
+
directories.add(target);
|
|
57
|
+
for (const name of fs.readdirSync(source)) matchSource(path.join(source, name), path.join(target, name));
|
|
58
|
+
} else if (sourceStat.isFile() && targetStat.isFile()) {
|
|
59
|
+
const expected = fs.readFileSync(source);
|
|
60
|
+
if (fs.readFileSync(target).equals(expected)) files.set(target, expected);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const relative of ['.agents', '.agents/skills', '.agents/data', '_bmad', '_bmad-output']) {
|
|
65
|
+
const target = targetPath(relative);
|
|
66
|
+
if (safeStat(projectDir, target)?.isDirectory()) directories.add(target);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const packId of manifest.packs) {
|
|
70
|
+
if (!Object.hasOwn(PACKS, packId)) throw new Error('Unknown installed pack: ' + packId);
|
|
71
|
+
const pack = PACKS[packId];
|
|
72
|
+
for (const name of pack.agents || []) matchSource(path.join(sourceRoot, 'agents', name), targetPath('.agents', 'skills', name));
|
|
73
|
+
for (const name of pack.skills || []) matchSource(path.join(sourceRoot, 'skills', name), targetPath('.agents', 'skills', name));
|
|
74
|
+
for (const name of pack.data || []) matchSource(path.join(sourceRoot, 'data', name), targetPath('.agents', 'data', name));
|
|
75
|
+
if (pack.packDir) matchSource(path.join(sourceRoot, pack.packSrcDir || 'agents', pack.packDir), targetPath('.agents', 'skills', pack.packDir));
|
|
76
|
+
if (pack.externalPackage) matchSource(path.join(packageRoot, pack.externalPackage, 'skills'), targetPath('.agents', 'skills'));
|
|
77
|
+
}
|
|
78
|
+
for (const name of ['module.yaml', 'module-help.csv']) matchSource(path.join(sourceRoot, name), targetPath('_bmad', name));
|
|
79
|
+
const renderOptions = {
|
|
80
|
+
packs: manifest.packs,
|
|
81
|
+
userName: typeof manifest.user === 'string' ? manifest.user : 'Developer',
|
|
82
|
+
language: typeof manifest.language === 'string' ? manifest.language : 'English',
|
|
83
|
+
};
|
|
84
|
+
const expectedFiles = new Map(generateUserFiles(DERIVED, renderOptions).map(entry => [entry.file, entry.content]));
|
|
85
|
+
const hashes = manifest.adapterHashes && typeof manifest.adapterHashes === 'object' && !Array.isArray(manifest.adapterHashes)
|
|
86
|
+
? manifest.adapterHashes : {};
|
|
87
|
+
// Only registry-declared paths can become deletion candidates, even when the
|
|
88
|
+
// manifest contains arbitrary hash keys. Keep previously owned tool targets.
|
|
89
|
+
const claimed = new Set(Object.keys(hashes).filter(file => expectedFiles.has(file)));
|
|
90
|
+
if (Array.isArray(manifest.ides)) {
|
|
91
|
+
const tools = manifest.ides.filter(id => DERIVED.targets.adapters.some(adapter => adapter.tool === id));
|
|
92
|
+
if (tools.length) {
|
|
93
|
+
for (const entry of generateUserFiles(DERIVED, { ...renderOptions, tools })) {
|
|
94
|
+
claimed.add(entry.file);
|
|
95
|
+
expectedFiles.set(entry.file, entry.content);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const legacyFallback = !Array.isArray(manifest.ides) && Object.keys(hashes).length === 0;
|
|
100
|
+
for (const [file, rendered] of expectedFiles) {
|
|
101
|
+
if (!legacyFallback && !claimed.has(file)) continue;
|
|
102
|
+
const target = targetPath(file);
|
|
103
|
+
let stat;
|
|
104
|
+
try {
|
|
105
|
+
stat = safeStat(projectDir, target);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
// A link with no ownership evidence is user content, not an uninstall target.
|
|
108
|
+
if (legacyFallback && error.code === 'BMAD_UNSAFE_LINK') continue;
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
if (stat?.isFile()) {
|
|
112
|
+
const content = fs.readFileSync(target);
|
|
113
|
+
const text = content.toString('utf8');
|
|
114
|
+
const expectedHash = hashes[file];
|
|
115
|
+
const matches = expectedHash ? contentHash(text) === expectedHash : text === rendered;
|
|
116
|
+
if (text.includes(USER_CONFIG_MARKER) && matches) {
|
|
117
|
+
files.set(target, content);
|
|
118
|
+
let parent = path.dirname(target);
|
|
119
|
+
while (parent !== projectDir) {
|
|
120
|
+
directories.add(parent);
|
|
121
|
+
parent = path.dirname(parent);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Keep config.yaml: it contains the user's settings and has no ownership hash.
|
|
127
|
+
files.set(manifestPath, fs.readFileSync(manifestPath));
|
|
128
|
+
return { files, directories };
|
|
129
|
+
}
|
|
15
130
|
|
|
16
131
|
module.exports = {
|
|
17
132
|
command: 'uninstall',
|
|
18
133
|
description: 'Remove BMAD+ from your project',
|
|
19
134
|
options: [
|
|
135
|
+
['-y, --yes', 'Confirm removal without prompting'],
|
|
20
136
|
['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it'],
|
|
21
137
|
],
|
|
22
138
|
action: async (options) => {
|
|
23
|
-
const projectDir = process.cwd();
|
|
24
|
-
|
|
139
|
+
const projectDir = fs.realpathSync(process.cwd());
|
|
140
|
+
let i = t(options.lang || 'en');
|
|
25
141
|
clack.intro(pc.bgRed(pc.white(' BMAD+ Uninstaller ')));
|
|
26
142
|
|
|
27
|
-
// Check if installed
|
|
28
143
|
const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
144
|
+
let manifest;
|
|
145
|
+
try {
|
|
146
|
+
if (!safeStat(projectDir, manifestPath)) {
|
|
147
|
+
clack.log.warn(i.uninstall_not_installed);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
manifest = readInstallManifest(manifestPath);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
clack.log.error(i.manifest_invalid(err.message));
|
|
153
|
+
clack.outro(pc.red(i.manifest_repair));
|
|
154
|
+
process.exitCode = 1;
|
|
32
155
|
return;
|
|
33
156
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let lang = options.lang || manifest.uiLanguage || 'en';
|
|
39
|
-
const i = t(lang);
|
|
40
|
-
|
|
41
|
-
clack.log.info(`BMAD+ v${manifest.version} (${i.installed_on || 'installed'} ${manifest.installed.split('T')[0]})`);
|
|
42
|
-
clack.log.info(`${i.selected_packs}: ${(manifest.packs || ['core']).join(', ')}`);
|
|
43
|
-
|
|
44
|
-
const confirm = await clack.confirm({
|
|
45
|
-
message: i.uninstall_confirm || 'Remove BMAD+ from this project?',
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (!confirm || clack.isCancel(confirm)) {
|
|
49
|
-
clack.cancel(i.cancelled);
|
|
157
|
+
i = t(options.lang || manifest.uiLanguage || 'en');
|
|
158
|
+
if (!options.yes && !process.stdin.isTTY) {
|
|
159
|
+
clack.log.error(i.noninteractive_requires_yes('uninstall'));
|
|
160
|
+
process.exitCode = 1;
|
|
50
161
|
return;
|
|
51
162
|
}
|
|
52
163
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (fs.existsSync(agentsDir)) {
|
|
61
|
-
fsExtra.removeSync(agentsDir);
|
|
62
|
-
removed++;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Remove _bmad/ config
|
|
66
|
-
const bmadDir = path.join(projectDir, '_bmad');
|
|
67
|
-
if (fs.existsSync(bmadDir)) {
|
|
68
|
-
fsExtra.removeSync(bmadDir);
|
|
69
|
-
removed++;
|
|
164
|
+
let plan;
|
|
165
|
+
try {
|
|
166
|
+
plan = removalPlan(projectDir, manifest, manifestPath);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
clack.log.error(i.uninstall_aborted(err.message));
|
|
169
|
+
process.exitCode = 1;
|
|
170
|
+
return;
|
|
70
171
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
removed++;
|
|
79
|
-
} else {
|
|
80
|
-
clack.log.info(i.uninstall_output_kept || '📁 _bmad-output/ kept (contains files)');
|
|
172
|
+
clack.log.info('BMAD+ v' + manifest.version + ' (' + i.installed_on + ' ' + manifest.installed.split('T')[0] + ')');
|
|
173
|
+
clack.log.info(i.selected_packs + ': ' + manifest.packs.join(', '));
|
|
174
|
+
if (!options.yes) {
|
|
175
|
+
const confirm = await clack.confirm({ message: i.uninstall_confirm });
|
|
176
|
+
if (!confirm || clack.isCancel(confirm)) {
|
|
177
|
+
clack.cancel(i.cancelled);
|
|
178
|
+
return;
|
|
81
179
|
}
|
|
82
180
|
}
|
|
83
181
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
fs.unlinkSync(
|
|
182
|
+
const spinner = clack.spinner();
|
|
183
|
+
spinner.start(i.uninstall_removing);
|
|
184
|
+
let removed = 0;
|
|
185
|
+
try {
|
|
186
|
+
// Revalidate after the prompt, before deleting any file.
|
|
187
|
+
for (const target of [...plan.files.keys(), ...plan.directories]) safeStat(projectDir, target);
|
|
188
|
+
for (const [target, expected] of plan.files) {
|
|
189
|
+
if (safeStat(projectDir, target)?.isFile() && fs.readFileSync(target).equals(expected)) {
|
|
190
|
+
fs.unlinkSync(target);
|
|
93
191
|
removed++;
|
|
94
192
|
}
|
|
95
193
|
}
|
|
194
|
+
// No recursive deletion: hidden files, nested user content, and memory stay.
|
|
195
|
+
for (const target of [...plan.directories].sort((a, b) => b.length - a.length)) {
|
|
196
|
+
if (safeStat(projectDir, target)?.isDirectory() && fs.readdirSync(target).length === 0) {
|
|
197
|
+
fs.rmdirSync(target);
|
|
198
|
+
removed++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} catch (err) {
|
|
202
|
+
spinner.stop(i.uninstall_aborted(err.message));
|
|
203
|
+
process.exitCode = 1;
|
|
204
|
+
return;
|
|
96
205
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
clack.outro(pc.green(i.guide_ready ? '👋 Done!' : 'Done!'));
|
|
206
|
+
spinner.stop(i.uninstall_done(removed));
|
|
207
|
+
clack.outro(i.uninstall_preserved);
|
|
100
208
|
},
|
|
101
209
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Report release status without applying updates. */
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { checkForUpdate } = require('../lib/update-check');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
command: 'update-check',
|
|
7
|
+
description: 'Check the published BMAD+ release without applying it',
|
|
8
|
+
options: [
|
|
9
|
+
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
10
|
+
['--refresh', 'Bypass the release cache and retry backoff'],
|
|
11
|
+
['--offline', 'Report local state without network access'],
|
|
12
|
+
['--json', 'Print machine-readable JSON'],
|
|
13
|
+
],
|
|
14
|
+
action: async (options = {}) => {
|
|
15
|
+
const result = await checkForUpdate({
|
|
16
|
+
projectDir: path.resolve(options.directory || process.cwd()),
|
|
17
|
+
runningVersion: require('../../../package.json').version,
|
|
18
|
+
refresh: Boolean(options.refresh), offline: Boolean(options.offline),
|
|
19
|
+
checkReadiness: ({ projectDir }) => {
|
|
20
|
+
const { evaluateUpdateReadiness } = require('../lib/update-transaction');
|
|
21
|
+
return evaluateUpdateReadiness({ projectDir });
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
if (result.status.startsWith('invalid-')) process.exitCode = 1;
|
|
25
|
+
if (options.json) console.log(JSON.stringify(result));
|
|
26
|
+
else {
|
|
27
|
+
console.log(`Installed: ${result.installedVersion || 'unknown'}; running: ${result.runningVersion}; channel ${result.channel || 'unknown'}: ${result.targetVersion || 'unknown'}`);
|
|
28
|
+
console.log(`${result.status}: ${result.reason}${result.stale ? ' (cached result is stale)' : ''}`);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Show or explicitly configure the local update policy; never updates packages. */
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const { readUpdatePolicy, writeUpdatePolicy } = require('../lib/update-policy');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
command: 'update-policy',
|
|
7
|
+
description: 'Show or configure the project update policy',
|
|
8
|
+
options: [
|
|
9
|
+
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
10
|
+
['--mode <mode>', 'off, notify, or auto (auto requires --range)'],
|
|
11
|
+
['--range <range>', 'Explicit semantic-version range for automatic updates'],
|
|
12
|
+
['--channel <tag>', 'npm release tag (default: latest)'],
|
|
13
|
+
['--allow-prerelease', 'Explicitly allow prerelease targets'],
|
|
14
|
+
['--stable-only', 'Disable prerelease targets'],
|
|
15
|
+
['--json', 'Print machine-readable JSON'],
|
|
16
|
+
],
|
|
17
|
+
action: async (options = {}) => {
|
|
18
|
+
try {
|
|
19
|
+
if (options.allowPrerelease && options.stableOnly) throw new Error('Choose --allow-prerelease or --stable-only.');
|
|
20
|
+
if (options.mode === 'auto' && !options.range) throw new Error('--mode auto requires an explicit --range.');
|
|
21
|
+
const projectDir = path.resolve(options.directory || process.cwd());
|
|
22
|
+
const policy = readUpdatePolicy(projectDir);
|
|
23
|
+
const changing = options.mode !== undefined || options.range !== undefined ||
|
|
24
|
+
options.channel !== undefined || options.allowPrerelease || options.stableOnly;
|
|
25
|
+
if (options.mode !== undefined) policy.mode = options.mode;
|
|
26
|
+
if (options.range !== undefined) policy.allowedRange = options.range;
|
|
27
|
+
if (options.channel !== undefined) policy.channel = options.channel;
|
|
28
|
+
if (options.allowPrerelease) policy.allowPrerelease = true;
|
|
29
|
+
if (options.stableOnly) policy.allowPrerelease = false;
|
|
30
|
+
const saved = changing ? writeUpdatePolicy(projectDir, policy) : policy;
|
|
31
|
+
if (options.json) console.log(JSON.stringify({ policy: saved, changed: Boolean(changing) }));
|
|
32
|
+
else console.log(`Update policy${changing ? ' saved' : ''}: ${saved.mode}; channel=${saved.channel}; range=${saved.allowedRange || 'none'}; prereleases=${saved.allowPrerelease}`);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
if (options.json) console.log(JSON.stringify({ error: error.message }));
|
|
36
|
+
else console.error(error.message);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -1,134 +1,123 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* BMAD+ Update Command
|
|
3
|
-
* Updates agents and skills while preserving user config
|
|
4
|
-
*
|
|
2
|
+
* BMAD+ Update Command — planned updates with recoverable backups.
|
|
5
3
|
* Author: Laurent Rochetta
|
|
6
4
|
*/
|
|
7
|
-
|
|
8
5
|
const path = require('node:path');
|
|
9
6
|
const fs = require('node:fs');
|
|
10
|
-
const
|
|
7
|
+
const semver = require('semver');
|
|
11
8
|
const clack = require('@clack/prompts');
|
|
12
9
|
const pc = require('picocolors');
|
|
13
10
|
const { t } = require('../i18n');
|
|
14
|
-
const {
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
const { safeTarget } = require('../../build/generate-adapters');
|
|
12
|
+
const { readInstallManifest } = require('../lib/install-manifest');
|
|
13
|
+
const { isExactVersion } = require('../lib/update-policy');
|
|
14
|
+
const { planUpdate, applyPlan, restoreUpdate, MANIFEST } = require('../lib/update-transaction');
|
|
15
|
+
|
|
16
|
+
function authorizeAuto(projectDir, version) {
|
|
17
|
+
const { readUpdatePolicy, authorizeTarget } = require('../lib/update-policy');
|
|
18
|
+
const decision = authorizeTarget(readUpdatePolicy(projectDir), version);
|
|
19
|
+
if (!decision.allowed) throw new Error('Automatic update refused: ' + decision.reason);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function confirmAction(options, message, lang = 'en') {
|
|
23
|
+
if (options.yes || options.auto) return true;
|
|
24
|
+
const i = t(lang);
|
|
25
|
+
if (!process.stdin.isTTY) throw new Error(i.noninteractive_requires_yes('update'));
|
|
26
|
+
const confirmed = await clack.confirm({ message });
|
|
27
|
+
if (!confirmed || clack.isCancel(confirmed)) {
|
|
28
|
+
clack.cancel(i.cancelled);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
18
33
|
|
|
19
34
|
module.exports = {
|
|
20
35
|
command: 'update',
|
|
21
|
-
description: 'Update BMAD+ agents and skills (preserves
|
|
36
|
+
description: 'Update BMAD+ agents and skills (preserves local changes)',
|
|
22
37
|
options: [
|
|
23
38
|
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
39
|
+
['-y, --yes', 'Confirm update without prompting'],
|
|
24
40
|
['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it'],
|
|
41
|
+
['--latest', 'Resolve and run the exact latest compatible published package'],
|
|
42
|
+
['--expected-version <version>', 'Require this exact executing package version'],
|
|
43
|
+
['--auto', 'Apply only when automatic update policy and ownership allow it'],
|
|
44
|
+
['--restore <receipt-id>', 'Restore files from an update receipt'],
|
|
25
45
|
],
|
|
26
46
|
action: async (options) => {
|
|
27
47
|
const projectDir = path.resolve(options.directory || process.cwd());
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
clack.intro(pc.bgMagenta(pc.white(` BMAD+ Updater v${packageJson.version} `)));
|
|
32
|
-
|
|
33
|
-
// Check if installed
|
|
34
|
-
const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
|
|
35
|
-
if (!fs.existsSync(manifestPath)) {
|
|
36
|
-
clack.log.error('BMAD+ is not installed in this directory.');
|
|
37
|
-
clack.log.info('Run `npx bmad-plus install` first.');
|
|
38
|
-
clack.outro(pc.red('Update aborted.'));
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let manifest;
|
|
48
|
+
const version = require('../../../package.json').version;
|
|
49
|
+
let spinner;
|
|
43
50
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
if (options.restore && (options.latest || options.expectedVersion)) {
|
|
52
|
+
throw new Error('--restore cannot be combined with --latest or --expected-version.');
|
|
53
|
+
}
|
|
54
|
+
// The dispatcher resolves the target package; this local package may be older.
|
|
55
|
+
if (options.latest) {
|
|
56
|
+
const result = await require('../lib/update-dispatch').runLatestUpdate({
|
|
57
|
+
projectDir, auto: Boolean(options.auto), yes: Boolean(options.yes),
|
|
58
|
+
});
|
|
59
|
+
clack.log.info(result.reason || result.status);
|
|
60
|
+
if (result.output) clack.log.info(result.output);
|
|
61
|
+
if (result.targetVersion) clack.log.info('Target: v' + result.targetVersion);
|
|
62
|
+
if (result.reloadInstructions) clack.log.info('Reload your agent instructions or start a new session.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
clack.intro(pc.bgMagenta(pc.white(' BMAD+ Updater v' + version + ' ')));
|
|
67
|
+
if (options.restore) {
|
|
68
|
+
if (options.auto) throw new Error('Recovery requires manual approval.');
|
|
69
|
+
if (!await confirmAction(options, 'Restore the files saved in receipt ' + options.restore + '?', options.lang)) return;
|
|
70
|
+
const restored = restoreUpdate({ projectDir, receiptId: options.restore });
|
|
71
|
+
clack.log.success('Restored BMAD+ v' + restored.restoredVersion);
|
|
72
|
+
clack.log.info('Receipt: ' + restored.receiptPath);
|
|
73
|
+
clack.outro('Reload your agent instructions or start a new session.');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (options.auto && !options.expectedVersion) throw new Error('Automatic update requires --expected-version.');
|
|
78
|
+
if (options.expectedVersion && options.expectedVersion !== version) {
|
|
79
|
+
throw new Error('Executing package version ' + version + ' does not match expected version ' + options.expectedVersion + '.');
|
|
80
|
+
}
|
|
81
|
+
const manifestPath = safeTarget(projectDir, MANIFEST);
|
|
82
|
+
if (!fs.existsSync(manifestPath)) throw new Error('BMAD+ is not installed in this directory. Run bmad-plus install first.');
|
|
83
|
+
let manifest;
|
|
84
|
+
try { manifest = readInstallManifest(manifestPath); } catch (error) {
|
|
85
|
+
throw new Error('Install manifest is unreadable or corrupt: ' + error.message, { cause: error });
|
|
86
|
+
}
|
|
87
|
+
if (!isExactVersion(version) || !isExactVersion(manifest.version)) throw new Error('Update versions must be strict semantic versions.');
|
|
88
|
+
if (semver.lt(version, manifest.version)) throw new Error('Downgrade refused: ' + manifest.version + ' -> ' + version);
|
|
89
|
+
const lang = options.lang || manifest.uiLanguage || 'en';
|
|
90
|
+
clack.log.info('Installed: v' + manifest.version + ' -> Executing package: v' + version);
|
|
91
|
+
if (manifest.version === version) {
|
|
92
|
+
clack.log.success('The installed version matches this executing package. Use --latest to check the registry.');
|
|
93
|
+
clack.outro(pc.green('Nothing to update.'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const plan = planUpdate({ projectDir, manifest, version, auto: Boolean(options.auto) });
|
|
97
|
+
if (options.auto) {
|
|
98
|
+
authorizeAuto(projectDir, version);
|
|
99
|
+
const fresh = await require('../lib/update-check').checkForUpdate({ projectDir, runningVersion: version, refresh: true });
|
|
100
|
+
if (fresh.source !== 'registry' || fresh.stale || !fresh.versionEligible || fresh.targetVersion !== options.expectedVersion) {
|
|
101
|
+
throw new Error('Automatic update requires a fresh, eligible registry target matching --expected-version.');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
clack.log.info('Planned changes: ' + (plan.files.length - 1) + ' files; preserved conflicts: ' + plan.conflicts.length + '.');
|
|
106
|
+
if (plan.legacy) clack.log.warn('Legacy installation: original pack files will be backed up before replacement because ownership hashes are unavailable. Review the receipt to recover local edits.');
|
|
107
|
+
for (const conflict of plan.conflicts) clack.log.warn(conflict.file + ': preserved (' + conflict.reason + ').');
|
|
108
|
+
if (!await confirmAction(options, 'Apply this update from v' + manifest.version + ' to v' + version + '?', lang)) return;
|
|
109
|
+
|
|
110
|
+
spinner = clack.spinner();
|
|
111
|
+
spinner.start('Updating managed files...');
|
|
112
|
+
const result = applyPlan(plan, { reauthorize: () => authorizeAuto(projectDir, version) });
|
|
113
|
+
spinner.stop(result.changedFiles.length + ' files updated; ' + result.conflicts.length + ' local conflicts preserved.');
|
|
114
|
+
spinner = null;
|
|
115
|
+
clack.log.info('Receipt and backups: ' + result.receiptPath);
|
|
116
|
+
clack.outro(pc.green('BMAD+ v' + version + (result.conflicts.length ? ' applied partially.' : ' installed.') + ' Reload your agent instructions or start a new session.'));
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (spinner) spinner.stop('Update failed.');
|
|
119
|
+
clack.log.error(error.message);
|
|
120
|
+
process.exitCode = 1;
|
|
50
121
|
}
|
|
51
|
-
if (!manifest || typeof manifest !== 'object') {
|
|
52
|
-
clack.log.error('Install manifest is malformed (not an object).');
|
|
53
|
-
clack.log.info('Re-run `npx bmad-plus install` to repair the installation.');
|
|
54
|
-
clack.outro(pc.red('Update aborted.'));
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const lang = options.lang || manifest.uiLanguage || 'en';
|
|
58
|
-
const i = t(lang);
|
|
59
|
-
|
|
60
|
-
clack.log.info(`📦 Installed: v${manifest.version} → Available: v${packageJson.version}`);
|
|
61
|
-
|
|
62
|
-
if (manifest.version === packageJson.version) {
|
|
63
|
-
clack.log.success(i.update_current || '✅ Already up to date!');
|
|
64
|
-
clack.outro(pc.green('Nothing to update.'));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Guard against a missing/malformed `packs` field (NODE-04): coerce to an array.
|
|
69
|
-
const selectedPacks = Array.isArray(manifest.packs) && manifest.packs.length > 0
|
|
70
|
-
? manifest.packs
|
|
71
|
-
: ['core'];
|
|
72
|
-
clack.log.info(`${i.selected_packs}: ${selectedPacks.join(', ')}`);
|
|
73
|
-
|
|
74
|
-
const confirm = await clack.confirm({
|
|
75
|
-
message: i.update_confirm || `Update from v${manifest.version} to v${packageJson.version}?`,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
if (!confirm || clack.isCancel(confirm)) {
|
|
79
|
-
clack.cancel(i.cancelled);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const spinner = clack.spinner();
|
|
84
|
-
spinner.start(i.update_updating || 'Updating agents and skills...');
|
|
85
|
-
|
|
86
|
-
const targetAgentsDir = path.join(projectDir, '.agents', 'skills');
|
|
87
|
-
const targetDataDir = path.join(projectDir, '.agents', 'data');
|
|
88
|
-
|
|
89
|
-
fsExtra.ensureDirSync(targetAgentsDir);
|
|
90
|
-
fsExtra.ensureDirSync(targetDataDir);
|
|
91
|
-
|
|
92
|
-
let updated = 0;
|
|
93
|
-
|
|
94
|
-
const projectRoot = path.join(bmadSrc, '..', '..');
|
|
95
|
-
|
|
96
|
-
for (const packId of selectedPacks) {
|
|
97
|
-
const pack = PACKS[packId];
|
|
98
|
-
if (!pack) continue;
|
|
99
|
-
|
|
100
|
-
const result = copyPackFiles({
|
|
101
|
-
bmadSrc,
|
|
102
|
-
targetAgentsDir,
|
|
103
|
-
targetDataDir,
|
|
104
|
-
projectRoot,
|
|
105
|
-
pack,
|
|
106
|
-
});
|
|
107
|
-
updated += result.copiedAgents + result.copiedSkills + result.copiedFiles;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Update module config (always)
|
|
111
|
-
const moduleYaml = path.join(bmadSrc, 'module.yaml');
|
|
112
|
-
const targetBmadDir = path.join(projectDir, '_bmad');
|
|
113
|
-
if (fs.existsSync(moduleYaml)) {
|
|
114
|
-
fsExtra.copySync(moduleYaml, path.join(targetBmadDir, 'module.yaml'));
|
|
115
|
-
updated++;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const helpCsv = path.join(bmadSrc, 'module-help.csv');
|
|
119
|
-
if (fs.existsSync(helpCsv)) {
|
|
120
|
-
fsExtra.copySync(helpCsv, path.join(targetBmadDir, 'module-help.csv'));
|
|
121
|
-
updated++;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Update manifest version (preserve everything else)
|
|
125
|
-
manifest.version = packageJson.version;
|
|
126
|
-
manifest.lastUpdated = new Date().toISOString();
|
|
127
|
-
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
128
|
-
|
|
129
|
-
spinner.stop(i.update_done ? i.update_done(updated) : `✅ ${updated} files updated to v${packageJson.version}`);
|
|
130
|
-
|
|
131
|
-
clack.log.info('📋 Config preserved: config.yaml, IDE configs, output directories');
|
|
132
|
-
clack.outro(pc.green(i.update_ready || `BMAD+ v${packageJson.version} is ready! 🚀`));
|
|
133
122
|
},
|
|
134
123
|
};
|