bmad-method 6.2.3-next.25 → 6.2.3-next.26

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-method",
4
- "version": "6.2.3-next.25",
4
+ "version": "6.2.3-next.26",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -17,7 +17,6 @@ module.exports = {
17
17
  '--tools <tools>',
18
18
  'Comma-separated list of tool/IDE IDs to configure (e.g., "claude-code,cursor"). Use "none" to skip tool configuration.',
19
19
  ],
20
- ['--custom-content <paths>', 'Comma-separated list of paths to custom modules/agents/workflows'],
21
20
  ['--action <type>', 'Action type for existing installations: install, update, or quick-update'],
22
21
  ['--user-name <name>', 'Name for agents to use (default: system username)'],
23
22
  ['--communication-language <lang>', 'Language for agent communication (default: English)'],
@@ -10,14 +10,13 @@ const { Manifest } = require('./manifest');
10
10
  class ExistingInstall {
11
11
  #version;
12
12
 
13
- constructor({ installed, version, hasCore, modules, ides, customModules }) {
13
+ constructor({ installed, version, hasCore, modules, ides }) {
14
14
  this.installed = installed;
15
15
  this.#version = version;
16
16
  this.hasCore = hasCore;
17
17
  this.modules = Object.freeze(modules.map((m) => Object.freeze({ ...m })));
18
18
  this.moduleIds = Object.freeze(this.modules.map((m) => m.id));
19
19
  this.ides = Object.freeze([...ides]);
20
- this.customModules = Object.freeze([...customModules]);
21
20
  Object.freeze(this);
22
21
  }
23
22
 
@@ -35,7 +34,6 @@ class ExistingInstall {
35
34
  hasCore: false,
36
35
  modules: [],
37
36
  ides: [],
38
- customModules: [],
39
37
  });
40
38
  }
41
39
 
@@ -53,15 +51,11 @@ class ExistingInstall {
53
51
  let hasCore = false;
54
52
  const modules = [];
55
53
  let ides = [];
56
- let customModules = [];
57
54
 
58
55
  const manifest = new Manifest();
59
56
  const manifestData = await manifest.read(bmadDir);
60
57
  if (manifestData) {
61
58
  version = manifestData.version;
62
- if (manifestData.customModules) {
63
- customModules = manifestData.customModules;
64
- }
65
59
  if (manifestData.ides) {
66
60
  ides = manifestData.ides.filter((ide) => ide && typeof ide === 'string');
67
61
  }
@@ -120,7 +114,7 @@ class ExistingInstall {
120
114
  return ExistingInstall.empty();
121
115
  }
122
116
 
123
- return new ExistingInstall({ installed, version, hasCore, modules, ides, customModules });
117
+ return new ExistingInstall({ installed, version, hasCore, modules, ides });
124
118
  }
125
119
  }
126
120
 
@@ -20,14 +20,12 @@ class InstallPaths {
20
20
 
21
21
  const configDir = path.join(bmadDir, '_config');
22
22
  const agentsDir = path.join(configDir, 'agents');
23
- const customCacheDir = path.join(configDir, 'custom');
24
23
  const coreDir = path.join(bmadDir, 'core');
25
24
 
26
25
  for (const [dir, label] of [
27
26
  [bmadDir, 'bmad directory'],
28
27
  [configDir, 'config directory'],
29
28
  [agentsDir, 'agents config directory'],
30
- [customCacheDir, 'custom modules cache'],
31
29
  [coreDir, 'core module directory'],
32
30
  ]) {
33
31
  await ensureWritableDir(dir, label);
@@ -40,7 +38,6 @@ class InstallPaths {
40
38
  bmadDir,
41
39
  configDir,
42
40
  agentsDir,
43
- customCacheDir,
44
41
  coreDir,
45
42
  isUpdate,
46
43
  });
@@ -2,7 +2,6 @@ const path = require('node:path');
2
2
  const fs = require('fs-extra');
3
3
  const { Manifest } = require('./manifest');
4
4
  const { OfficialModules } = require('../modules/official-modules');
5
- const { CustomModules } = require('../modules/custom-modules');
6
5
  const { IdeManager } = require('../ide/manager');
7
6
  const { FileOps } = require('../file-ops');
8
7
  const { Config } = require('./config');
@@ -19,7 +18,6 @@ class Installer {
19
18
  constructor() {
20
19
  this.externalModuleManager = new ExternalModuleManager();
21
20
  this.manifest = new Manifest();
22
- this.customModules = new CustomModules();
23
21
  this.ideManager = new IdeManager();
24
22
  this.fileOps = new FileOps();
25
23
  this.installedFiles = new Set(); // Track all installed files
@@ -80,8 +78,6 @@ class Installer {
80
78
  const officialModules = await OfficialModules.build(config, paths);
81
79
  const existingInstall = await ExistingInstall.detect(paths.bmadDir);
82
80
 
83
- await this.customModules.discoverPaths(originalConfig, paths);
84
-
85
81
  if (existingInstall.installed) {
86
82
  await this._removeDeselectedModules(existingInstall, config, paths);
87
83
  updateState = await this._prepareUpdateState(paths, config, existingInstall, officialModules);
@@ -121,14 +117,9 @@ class Installer {
121
117
  }
122
118
  }
123
119
 
124
- await this._cacheCustomModules(paths, addResult);
125
-
126
- // Compute module lists: official = selected minus custom, all = both
127
- const customModuleIds = new Set(this.customModules.paths.keys());
128
- const officialModuleIds = (config.modules || []).filter((m) => !customModuleIds.has(m));
129
- const allModules = [...officialModuleIds, ...[...customModuleIds].filter((id) => !officialModuleIds.includes(id))];
120
+ const allModules = config.modules || [];
130
121
 
131
- await this._installAndConfigure(config, originalConfig, paths, officialModuleIds, allModules, addResult, officialModules);
122
+ await this._installAndConfigure(config, originalConfig, paths, allModules, allModules, addResult, officialModules);
132
123
 
133
124
  await this._setupIdes(config, allModules, paths, addResult, previousSkillIds);
134
125
 
@@ -242,26 +233,6 @@ class Installer {
242
233
  }
243
234
  }
244
235
 
245
- /**
246
- * Cache custom modules into the local cache directory.
247
- * Updates this.customModules.paths in place with cached locations.
248
- */
249
- async _cacheCustomModules(paths, addResult) {
250
- if (!this.customModules.paths || this.customModules.paths.size === 0) return;
251
-
252
- const { CustomModuleCache } = require('./custom-module-cache');
253
- const customCache = new CustomModuleCache(paths.bmadDir);
254
-
255
- for (const [moduleId, sourcePath] of this.customModules.paths) {
256
- const cachedInfo = await customCache.cacheModule(moduleId, sourcePath, {
257
- sourcePath: sourcePath,
258
- });
259
- this.customModules.paths.set(moduleId, cachedInfo.cachePath);
260
- }
261
-
262
- addResult('Custom modules cached', 'ok');
263
- }
264
-
265
236
  /**
266
237
  * Install modules, create directories, generate configs and manifests.
267
238
  */
@@ -284,11 +255,6 @@ class Installer {
284
255
  installedModuleNames,
285
256
  });
286
257
 
287
- await this._installCustomModules(config, paths, addResult, officialModules, {
288
- message,
289
- installedModuleNames,
290
- });
291
-
292
258
  return `${allModules.length} module(s) ${isQuickUpdate ? 'updated' : 'installed'}`;
293
259
  },
294
260
  });
@@ -515,48 +481,7 @@ class Installer {
515
481
  }
516
482
 
517
483
  /**
518
- * Scan the custom module cache directory and register any cached custom modules
519
- * that aren't already known from the manifest or external module list.
520
- * @param {Object} paths - InstallPaths instance
521
- */
522
- async _scanCachedCustomModules(paths) {
523
- const cacheDir = paths.customCacheDir;
524
- if (!(await fs.pathExists(cacheDir))) {
525
- return;
526
- }
527
-
528
- const cachedModules = await fs.readdir(cacheDir, { withFileTypes: true });
529
-
530
- for (const cachedModule of cachedModules) {
531
- const moduleId = cachedModule.name;
532
- const cachedPath = path.join(cacheDir, moduleId);
533
-
534
- // Skip if path doesn't exist (broken symlink, deleted dir) - avoids lstat ENOENT
535
- if (!(await fs.pathExists(cachedPath)) || !cachedModule.isDirectory()) {
536
- continue;
537
- }
538
-
539
- // Skip if we already have this module from manifest
540
- if (this.customModules.paths.has(moduleId)) {
541
- continue;
542
- }
543
-
544
- // Check if this is an external official module - skip cache for those
545
- const isExternal = await this.externalModuleManager.hasModule(moduleId);
546
- if (isExternal) {
547
- continue;
548
- }
549
-
550
- // Check if this is actually a custom module (has module.yaml)
551
- const moduleYamlPath = path.join(cachedPath, 'module.yaml');
552
- if (await fs.pathExists(moduleYamlPath)) {
553
- this.customModules.paths.set(moduleId, cachedPath);
554
- }
555
- }
556
- }
557
-
558
- /**
559
- * Common update preparation: detect files, preserve core config, scan cache, back up.
484
+ * Common update preparation: detect files, preserve core config, back up.
560
485
  * @param {Object} paths - InstallPaths instance
561
486
  * @param {Object} config - Clean config (may have coreConfig updated)
562
487
  * @param {Object} existingInstall - Detection result
@@ -584,8 +509,6 @@ class Installer {
584
509
  }
585
510
  }
586
511
 
587
- await this._scanCachedCustomModules(paths);
588
-
589
512
  const backupDirs = await this._backupUserFiles(paths, customFiles, modifiedFiles);
590
513
 
591
514
  return {
@@ -677,38 +600,6 @@ class Installer {
677
600
  }
678
601
  }
679
602
 
680
- /**
681
- * Install custom modules using CustomModules.install().
682
- * Source paths come from this.customModules.paths (populated by discoverPaths).
683
- */
684
- async _installCustomModules(config, paths, addResult, officialModules, ctx) {
685
- const { message, installedModuleNames } = ctx;
686
- const isQuickUpdate = config.isQuickUpdate();
687
-
688
- for (const [moduleName, sourcePath] of this.customModules.paths) {
689
- if (installedModuleNames.has(moduleName)) continue;
690
- installedModuleNames.add(moduleName);
691
-
692
- message(`${isQuickUpdate ? 'Updating' : 'Installing'} ${moduleName}...`);
693
-
694
- const collectedModuleConfig = officialModules.moduleConfigs[moduleName] || {};
695
- const result = await this.customModules.install(moduleName, paths.bmadDir, (filePath) => this.installedFiles.add(filePath), {
696
- moduleConfig: collectedModuleConfig,
697
- });
698
-
699
- // Generate runtime config.yaml with merged values
700
- await this.generateModuleConfigs(paths.bmadDir, {
701
- [moduleName]: { ...config.coreConfig, ...result.moduleConfig, ...collectedModuleConfig },
702
- });
703
-
704
- // Get display name from source module.yaml; version from marketplace.json
705
- const moduleInfo = await officialModules.getModuleInfo(sourcePath, moduleName, '');
706
- const displayName = moduleInfo?.name || moduleName;
707
- const version = await this._getMarketplaceVersion(sourcePath);
708
- addResult(displayName, 'ok', '', { moduleCode: moduleName, newVersion: version });
709
- }
710
- }
711
-
712
603
  /**
713
604
  * Read files-manifest.csv
714
605
  * @param {string} bmadDir - BMAD installation directory
@@ -1253,16 +1144,9 @@ class Installer {
1253
1144
  const configuredIdes = existingInstall.ides;
1254
1145
  const projectRoot = path.dirname(bmadDir);
1255
1146
 
1256
- const customModuleSources = await this.customModules.assembleQuickUpdateSources(
1257
- config,
1258
- existingInstall,
1259
- bmadDir,
1260
- this.externalModuleManager,
1261
- );
1262
-
1263
1147
  // Get available modules (what we have source for)
1264
1148
  const availableModulesData = await new OfficialModules().listAvailable();
1265
- const availableModules = [...availableModulesData.modules, ...availableModulesData.customModules];
1149
+ const availableModules = [...availableModulesData.modules];
1266
1150
 
1267
1151
  // Add external official modules to available modules
1268
1152
  const externalModules = await this.externalModuleManager.listAvailable();
@@ -1277,52 +1161,12 @@ class Installer {
1277
1161
  }
1278
1162
  }
1279
1163
 
1280
- // Add custom modules from manifest if their sources exist
1281
- for (const [moduleId, customModule] of customModuleSources) {
1282
- const sourcePath = customModule.sourcePath;
1283
- if (sourcePath && (await fs.pathExists(sourcePath)) && !availableModules.some((m) => m.id === moduleId)) {
1284
- availableModules.push({
1285
- id: moduleId,
1286
- name: customModule.name || moduleId,
1287
- path: sourcePath,
1288
- isCustom: true,
1289
- fromManifest: true,
1290
- });
1291
- }
1292
- }
1293
-
1294
- // Handle missing custom module sources
1295
- const customModuleResult = await this.handleMissingCustomSources(
1296
- customModuleSources,
1297
- bmadDir,
1298
- projectRoot,
1299
- 'update',
1300
- installedModules,
1301
- config.skipPrompts || false,
1302
- );
1303
-
1304
- const { validCustomModules, keptModulesWithoutSources } = customModuleResult;
1305
-
1306
- const customModulesFromManifest = validCustomModules.map((m) => ({
1307
- ...m,
1308
- isCustom: true,
1309
- hasUpdate: true,
1310
- }));
1311
-
1312
- const allAvailableModules = [...availableModules, ...customModulesFromManifest];
1313
- const availableModuleIds = new Set(allAvailableModules.map((m) => m.id));
1164
+ const availableModuleIds = new Set(availableModules.map((m) => m.id));
1314
1165
 
1315
1166
  // Only update modules that are BOTH installed AND available (we have source for)
1316
1167
  const modulesToUpdate = installedModules.filter((id) => availableModuleIds.has(id));
1317
1168
  const skippedModules = installedModules.filter((id) => !availableModuleIds.has(id));
1318
1169
 
1319
- // Add custom modules that were kept without sources to the skipped modules
1320
- for (const keptModule of keptModulesWithoutSources) {
1321
- if (!skippedModules.includes(keptModule)) {
1322
- skippedModules.push(keptModule);
1323
- }
1324
- }
1325
-
1326
1170
  if (skippedModules.length > 0) {
1327
1171
  await prompts.log.warn(`Skipping ${skippedModules.length} module(s) - no source available: ${skippedModules.join(', ')}`);
1328
1172
  }
@@ -1367,9 +1211,7 @@ class Installer {
1367
1211
  actionType: 'install',
1368
1212
  _quickUpdate: true,
1369
1213
  _preserveModules: skippedModules,
1370
- _customModuleSources: customModuleSources,
1371
1214
  _existingModules: installedModules,
1372
- customContent: config.customContent,
1373
1215
  };
1374
1216
 
1375
1217
  await this.install(installConfig);
@@ -1504,239 +1346,6 @@ class Installer {
1504
1346
  return this._readOutputFolder(bmadDir);
1505
1347
  }
1506
1348
 
1507
- /**
1508
- * Handle missing custom module sources interactively
1509
- * @param {Map} customModuleSources - Map of custom module ID to info
1510
- * @param {string} bmadDir - BMAD directory
1511
- * @param {string} projectRoot - Project root directory
1512
- * @param {string} operation - Current operation ('update', 'compile', etc.)
1513
- * @param {Array} installedModules - Array of installed module IDs (will be modified)
1514
- * @param {boolean} [skipPrompts=false] - Skip interactive prompts and keep all modules with missing sources
1515
- * @returns {Object} Object with validCustomModules array and keptModulesWithoutSources array
1516
- */
1517
- async handleMissingCustomSources(customModuleSources, bmadDir, projectRoot, operation, installedModules, skipPrompts = false) {
1518
- const validCustomModules = [];
1519
- const keptModulesWithoutSources = []; // Track modules kept without sources
1520
- const customModulesWithMissingSources = [];
1521
-
1522
- // Check which sources exist
1523
- for (const [moduleId, customInfo] of customModuleSources) {
1524
- if (await fs.pathExists(customInfo.sourcePath)) {
1525
- validCustomModules.push({
1526
- id: moduleId,
1527
- name: customInfo.name,
1528
- path: customInfo.sourcePath,
1529
- info: customInfo,
1530
- });
1531
- } else {
1532
- // For cached modules that are missing, we just skip them without prompting
1533
- if (customInfo.cached) {
1534
- // Skip cached modules without prompting
1535
- keptModulesWithoutSources.push({
1536
- id: moduleId,
1537
- name: customInfo.name,
1538
- cached: true,
1539
- });
1540
- } else {
1541
- customModulesWithMissingSources.push({
1542
- id: moduleId,
1543
- name: customInfo.name,
1544
- sourcePath: customInfo.sourcePath,
1545
- relativePath: customInfo.relativePath,
1546
- info: customInfo,
1547
- });
1548
- }
1549
- }
1550
- }
1551
-
1552
- // If no missing sources, return immediately
1553
- if (customModulesWithMissingSources.length === 0) {
1554
- return {
1555
- validCustomModules,
1556
- keptModulesWithoutSources: [],
1557
- };
1558
- }
1559
-
1560
- // Non-interactive mode: keep all modules with missing sources
1561
- if (skipPrompts) {
1562
- for (const missing of customModulesWithMissingSources) {
1563
- keptModulesWithoutSources.push(missing.id);
1564
- }
1565
- return { validCustomModules, keptModulesWithoutSources };
1566
- }
1567
-
1568
- await prompts.log.warn(`Found ${customModulesWithMissingSources.length} custom module(s) with missing sources:`);
1569
-
1570
- let keptCount = 0;
1571
- let updatedCount = 0;
1572
- let removedCount = 0;
1573
-
1574
- for (const missing of customModulesWithMissingSources) {
1575
- await prompts.log.message(
1576
- `${missing.name} (${missing.id})\n Original source: ${missing.relativePath}\n Full path: ${missing.sourcePath}`,
1577
- );
1578
-
1579
- const choices = [
1580
- {
1581
- name: 'Keep installed (will not be processed)',
1582
- value: 'keep',
1583
- hint: 'Keep',
1584
- },
1585
- {
1586
- name: 'Specify new source location',
1587
- value: 'update',
1588
- hint: 'Update',
1589
- },
1590
- ];
1591
-
1592
- // Only add remove option if not just compiling agents
1593
- if (operation !== 'compile-agents') {
1594
- choices.push({
1595
- name: '⚠️ REMOVE module completely (destructive!)',
1596
- value: 'remove',
1597
- hint: 'Remove',
1598
- });
1599
- }
1600
-
1601
- const action = await prompts.select({
1602
- message: `How would you like to handle "${missing.name}"?`,
1603
- choices,
1604
- });
1605
-
1606
- switch (action) {
1607
- case 'update': {
1608
- // Use sync validation because @clack/prompts doesn't support async validate
1609
- const newSourcePath = await prompts.text({
1610
- message: 'Enter the new path to the custom module:',
1611
- default: missing.sourcePath,
1612
- validate: (input) => {
1613
- if (!input || input.trim() === '') {
1614
- return 'Please enter a path';
1615
- }
1616
- const expandedPath = path.resolve(input.trim());
1617
- if (!fs.pathExistsSync(expandedPath)) {
1618
- return 'Path does not exist';
1619
- }
1620
- // Check if it looks like a valid module
1621
- const moduleYamlPath = path.join(expandedPath, 'module.yaml');
1622
- const agentsPath = path.join(expandedPath, 'agents');
1623
- const workflowsPath = path.join(expandedPath, 'workflows');
1624
-
1625
- if (!fs.pathExistsSync(moduleYamlPath) && !fs.pathExistsSync(agentsPath) && !fs.pathExistsSync(workflowsPath)) {
1626
- return 'Path does not appear to contain a valid custom module';
1627
- }
1628
- return; // clack expects undefined for valid input
1629
- },
1630
- });
1631
-
1632
- // Defensive: handleCancel should have exited, but guard against symbol propagation
1633
- if (typeof newSourcePath !== 'string') {
1634
- keptCount++;
1635
- keptModulesWithoutSources.push(missing.id);
1636
- continue;
1637
- }
1638
-
1639
- // Update the source in manifest
1640
- const resolvedPath = path.resolve(newSourcePath.trim());
1641
- missing.info.sourcePath = resolvedPath;
1642
- // Remove relativePath - we only store absolute sourcePath now
1643
- delete missing.info.relativePath;
1644
- await this.manifest.addCustomModule(bmadDir, missing.info);
1645
-
1646
- validCustomModules.push({
1647
- id: missing.id,
1648
- name: missing.name,
1649
- path: resolvedPath,
1650
- info: missing.info,
1651
- });
1652
-
1653
- updatedCount++;
1654
- await prompts.log.success('Updated source location');
1655
-
1656
- break;
1657
- }
1658
- case 'remove': {
1659
- // Extra confirmation for destructive remove
1660
- await prompts.log.error(
1661
- `WARNING: This will PERMANENTLY DELETE "${missing.name}" and all its files!\n Module location: ${path.join(bmadDir, missing.id)}`,
1662
- );
1663
-
1664
- const confirmDelete = await prompts.confirm({
1665
- message: 'Are you absolutely sure you want to delete this module?',
1666
- default: false,
1667
- });
1668
-
1669
- if (confirmDelete) {
1670
- const typedConfirm = await prompts.text({
1671
- message: 'Type "DELETE" to confirm permanent deletion:',
1672
- validate: (input) => {
1673
- if (input !== 'DELETE') {
1674
- return 'You must type "DELETE" exactly to proceed';
1675
- }
1676
- return; // clack expects undefined for valid input
1677
- },
1678
- });
1679
-
1680
- if (typedConfirm === 'DELETE') {
1681
- // Remove the module from filesystem and manifest
1682
- const modulePath = path.join(bmadDir, missing.id);
1683
- if (await fs.pathExists(modulePath)) {
1684
- const fsExtra = require('fs-extra');
1685
- await fsExtra.remove(modulePath);
1686
- await prompts.log.warn(`Deleted module directory: ${path.relative(projectRoot, modulePath)}`);
1687
- }
1688
-
1689
- await this.manifest.removeModule(bmadDir, missing.id);
1690
- await this.manifest.removeCustomModule(bmadDir, missing.id);
1691
- await prompts.log.warn('Removed from manifest');
1692
-
1693
- // Also remove from installedModules list
1694
- if (installedModules && installedModules.includes(missing.id)) {
1695
- const index = installedModules.indexOf(missing.id);
1696
- if (index !== -1) {
1697
- installedModules.splice(index, 1);
1698
- }
1699
- }
1700
-
1701
- removedCount++;
1702
- await prompts.log.error(`"${missing.name}" has been permanently removed`);
1703
- } else {
1704
- await prompts.log.message('Removal cancelled - module will be kept');
1705
- keptCount++;
1706
- }
1707
- } else {
1708
- await prompts.log.message('Removal cancelled - module will be kept');
1709
- keptCount++;
1710
- }
1711
-
1712
- break;
1713
- }
1714
- case 'keep': {
1715
- keptCount++;
1716
- keptModulesWithoutSources.push(missing.id);
1717
- await prompts.log.message('Module will be kept as-is');
1718
-
1719
- break;
1720
- }
1721
- // No default
1722
- }
1723
- }
1724
-
1725
- // Show summary
1726
- if (keptCount > 0 || updatedCount > 0 || removedCount > 0) {
1727
- let summary = 'Summary for custom modules with missing sources:';
1728
- if (keptCount > 0) summary += `\n • ${keptCount} module(s) kept as-is`;
1729
- if (updatedCount > 0) summary += `\n • ${updatedCount} module(s) updated with new sources`;
1730
- if (removedCount > 0) summary += `\n • ${removedCount} module(s) permanently deleted`;
1731
- await prompts.log.message(summary);
1732
- }
1733
-
1734
- return {
1735
- validCustomModules,
1736
- keptModulesWithoutSources,
1737
- };
1738
- }
1739
-
1740
1349
  /**
1741
1350
  * Find the bmad installation directory in a project
1742
1351
  * Always uses the standard _bmad folder name
@@ -375,8 +375,6 @@ class ManifestGenerator {
375
375
  // Read existing manifest to preserve install date
376
376
  let existingInstallDate = null;
377
377
  const existingModulesMap = new Map();
378
- let existingCustomModules = [];
379
-
380
378
  if (await fs.pathExists(manifestPath)) {
381
379
  try {
382
380
  const existingContent = await fs.readFile(manifestPath, 'utf8');
@@ -397,12 +395,6 @@ class ManifestGenerator {
397
395
  }
398
396
  }
399
397
  }
400
-
401
- if (existingManifest.customModules && Array.isArray(existingManifest.customModules)) {
402
- // We filter here so manifest regeneration preserves source metadata only for custom modules that
403
- // are still installed. Without that, customModules can retain stale entries for modules that were removed.
404
- existingCustomModules = existingManifest.customModules.filter((customModule) => installedModuleSet.has(customModule?.id));
405
- }
406
398
  } catch {
407
399
  // If we can't read existing manifest, continue with defaults
408
400
  }
@@ -438,7 +430,6 @@ class ManifestGenerator {
438
430
  lastUpdated: new Date().toISOString(),
439
431
  },
440
432
  modules: updatedModules,
441
- customModules: existingCustomModules,
442
433
  ides: this.selectedIdes,
443
434
  };
444
435