bmad-method 6.5.1-next.8 → 6.5.1-next.9

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.5.1-next.8",
4
+ "version": "6.5.1-next.9",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -5,15 +5,11 @@ default_selected: true # This module will be selected by default for new install
5
5
 
6
6
  # Variables from Core Config inserted:
7
7
  ## user_name
8
+ ## project_name
8
9
  ## communication_language
9
10
  ## document_output_language
10
11
  ## output_folder
11
12
 
12
- project_name:
13
- prompt: "What is your project called?"
14
- default: "{directory_name}"
15
- result: "{value}"
16
-
17
13
  user_skill_level:
18
14
  prompt:
19
15
  - "What is your development experience level?"
@@ -11,6 +11,11 @@ user_name:
11
11
  default: "BMad"
12
12
  result: "{value}"
13
13
 
14
+ project_name:
15
+ prompt: "What is your project called?"
16
+ default: "{directory_name}"
17
+ result: "{value}"
18
+
14
19
  communication_language:
15
20
  prompt: "What language should agents use when chatting with you?"
16
21
  scope: user
@@ -903,7 +903,10 @@ class OfficialModules {
903
903
  try {
904
904
  const content = await fs.readFile(moduleConfigPath, 'utf8');
905
905
  const moduleConfig = yaml.parse(content);
906
- if (moduleConfig) {
906
+ // Only keep plain object parses. A corrupt config.yaml that parses
907
+ // to a scalar or array would crash later code that does `key in cfg`
908
+ // / `Object.keys(cfg)`; treat it the same as a parse error.
909
+ if (moduleConfig && typeof moduleConfig === 'object' && !Array.isArray(moduleConfig)) {
907
910
  this._existingConfig[entry.name] = moduleConfig;
908
911
  foundAny = true;
909
912
  }
@@ -914,9 +917,58 @@ class OfficialModules {
914
917
  }
915
918
  }
916
919
 
920
+ if (foundAny) {
921
+ await this._hoistCoreKeysFromLegacyModuleConfigs();
922
+ }
923
+
917
924
  return foundAny;
918
925
  }
919
926
 
927
+ /**
928
+ * Migrate prior answers when a key has moved from a non-core module to core
929
+ * (e.g. project_name moving from bmm to core in #2279). Without this, the
930
+ * partition logic in writeCentralConfig drops the value from the bmm bucket
931
+ * (because it's now a core key) without re-homing it under [core], so the
932
+ * user's prior answer silently disappears on the next install/quick-update.
933
+ */
934
+ async _hoistCoreKeysFromLegacyModuleConfigs() {
935
+ const coreSchemaPath = path.join(getSourcePath(), 'core-skills', 'module.yaml');
936
+ if (!(await fs.pathExists(coreSchemaPath))) return;
937
+
938
+ let coreSchema;
939
+ try {
940
+ coreSchema = yaml.parse(await fs.readFile(coreSchemaPath, 'utf8'));
941
+ } catch {
942
+ return;
943
+ }
944
+ if (!coreSchema || typeof coreSchema !== 'object') return;
945
+
946
+ const coreKeys = new Set(
947
+ Object.entries(coreSchema)
948
+ .filter(([, v]) => v && typeof v === 'object' && 'prompt' in v)
949
+ .map(([k]) => k),
950
+ );
951
+ if (coreKeys.size === 0) return;
952
+
953
+ // Belt-and-suspenders: loadExistingConfig already filters non-object parses,
954
+ // but anyone calling _hoistCoreKeysFromLegacyModuleConfigs in isolation (or
955
+ // future code paths populating _existingConfig directly) shouldn't be able
956
+ // to crash this with a scalar / array.
957
+ const existingCore = this._existingConfig.core;
958
+ this._existingConfig.core = existingCore && typeof existingCore === 'object' && !Array.isArray(existingCore) ? existingCore : {};
959
+
960
+ for (const [moduleName, cfg] of Object.entries(this._existingConfig)) {
961
+ if (moduleName === 'core' || !cfg || typeof cfg !== 'object' || Array.isArray(cfg)) continue;
962
+ for (const key of Object.keys(cfg)) {
963
+ if (!coreKeys.has(key)) continue;
964
+ if (!(key in this._existingConfig.core)) {
965
+ this._existingConfig.core[key] = cfg[key];
966
+ }
967
+ delete cfg[key];
968
+ }
969
+ }
970
+ }
971
+
920
972
  /**
921
973
  * Pre-scan module schemas to gather metadata for the configuration gateway prompt.
922
974
  * Returns info about which modules have configurable options.
@@ -758,6 +758,9 @@ class UI {
758
758
  const defaultUsername = safeUsername.charAt(0).toUpperCase() + safeUsername.slice(1);
759
759
  configCollector.collectedConfig.core = {
760
760
  user_name: defaultUsername,
761
+ // {directory_name} default per src/core-skills/module.yaml — matches what the
762
+ // interactive flow resolves via buildQuestion()'s {directory_name} placeholder.
763
+ project_name: path.basename(directory),
761
764
  communication_language: 'English',
762
765
  document_output_language: 'English',
763
766
  output_folder: '_bmad-output',