bmad-method 6.2.1-next.35 → 6.2.1-next.36

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.1-next.35",
4
+ "version": "6.2.1-next.36",
5
5
  "description": "Breakthrough Method of Agile AI-driven Development",
6
6
  "keywords": [
7
7
  "agile",
@@ -954,29 +954,121 @@ class ConfigCollector {
954
954
  return match;
955
955
  }
956
956
 
957
- // Look for the config value in allAnswers (already answered questions)
958
- let configValue = this.allAnswers[configKey] || this.allAnswers[`core_${configKey}`];
959
-
960
- // Check in already collected config
961
- if (!configValue) {
962
- for (const mod of Object.keys(this.collectedConfig)) {
963
- if (mod !== '_meta' && this.collectedConfig[mod] && this.collectedConfig[mod][configKey]) {
964
- configValue = this.collectedConfig[mod][configKey];
965
- break;
966
- }
957
+ const configValue = this.resolveConfigValue(configKey, currentModule, moduleConfig);
958
+
959
+ return configValue || match;
960
+ });
961
+ }
962
+
963
+ /**
964
+ * Clean a stored path-like value for prompt display/input reuse.
965
+ * @param {*} value - Stored value
966
+ * @returns {*} Cleaned value
967
+ */
968
+ cleanPromptValue(value) {
969
+ if (typeof value === 'string' && value.startsWith('{project-root}/')) {
970
+ return value.replace('{project-root}/', '');
971
+ }
972
+
973
+ return value;
974
+ }
975
+
976
+ /**
977
+ * Resolve a config key from answers, collected config, existing config, or schema defaults.
978
+ * @param {string} configKey - Config key to resolve
979
+ * @param {string} currentModule - Current module name
980
+ * @param {Object} moduleConfig - Current module config schema
981
+ * @returns {*} Resolved value
982
+ */
983
+ resolveConfigValue(configKey, currentModule = null, moduleConfig = null) {
984
+ // Look for the config value in allAnswers (already answered questions)
985
+ let configValue = this.allAnswers?.[configKey] || this.allAnswers?.[`core_${configKey}`];
986
+
987
+ if (!configValue && this.allAnswers) {
988
+ for (const [answerKey, answerValue] of Object.entries(this.allAnswers)) {
989
+ if (answerKey.endsWith(`_${configKey}`)) {
990
+ configValue = answerValue;
991
+ break;
967
992
  }
968
993
  }
994
+ }
969
995
 
970
- // If still not found and we're in the same module, use the default from the config schema
971
- if (!configValue && currentModule && moduleConfig && moduleConfig[configKey]) {
972
- const referencedItem = moduleConfig[configKey];
973
- if (referencedItem && referencedItem.default !== undefined) {
974
- configValue = referencedItem.default;
996
+ // Prefer the current module's persisted value when re-prompting an existing install
997
+ if (!configValue && currentModule && this.existingConfig?.[currentModule]?.[configKey] !== undefined) {
998
+ configValue = this.existingConfig[currentModule][configKey];
999
+ }
1000
+
1001
+ // Check in already collected config
1002
+ if (!configValue) {
1003
+ for (const mod of Object.keys(this.collectedConfig)) {
1004
+ if (mod !== '_meta' && this.collectedConfig[mod] && this.collectedConfig[mod][configKey]) {
1005
+ configValue = this.collectedConfig[mod][configKey];
1006
+ break;
975
1007
  }
976
1008
  }
1009
+ }
977
1010
 
978
- return configValue || match;
979
- });
1011
+ // Fall back to other existing module config values
1012
+ if (!configValue && this.existingConfig) {
1013
+ for (const mod of Object.keys(this.existingConfig)) {
1014
+ if (mod !== '_meta' && this.existingConfig[mod] && this.existingConfig[mod][configKey]) {
1015
+ configValue = this.existingConfig[mod][configKey];
1016
+ break;
1017
+ }
1018
+ }
1019
+ }
1020
+
1021
+ // If still not found and we're in the same module, use the default from the config schema
1022
+ if (!configValue && currentModule && moduleConfig && moduleConfig[configKey]) {
1023
+ const referencedItem = moduleConfig[configKey];
1024
+ if (referencedItem && referencedItem.default !== undefined) {
1025
+ configValue = referencedItem.default;
1026
+ }
1027
+ }
1028
+
1029
+ return this.cleanPromptValue(configValue);
1030
+ }
1031
+
1032
+ /**
1033
+ * Convert an existing stored value back into the prompt-facing value for templated fields.
1034
+ * For example, "{test_artifacts}/{value}" + "_bmad-output/test-artifacts/test-design"
1035
+ * becomes "test-design" so the template is not applied twice on modify.
1036
+ * @param {*} existingValue - Stored config value
1037
+ * @param {string} moduleName - Module name
1038
+ * @param {Object} item - Config item definition
1039
+ * @param {Object} moduleConfig - Current module config schema
1040
+ * @returns {*} Prompt-facing default value
1041
+ */
1042
+ normalizeExistingValueForPrompt(existingValue, moduleName, item, moduleConfig = null) {
1043
+ const cleanedValue = this.cleanPromptValue(existingValue);
1044
+
1045
+ if (typeof cleanedValue !== 'string' || typeof item?.result !== 'string' || !item.result.includes('{value}')) {
1046
+ return cleanedValue;
1047
+ }
1048
+
1049
+ const [prefixTemplate = '', suffixTemplate = ''] = item.result.split('{value}');
1050
+ const prefix = this.cleanPromptValue(this.replacePlaceholders(prefixTemplate, moduleName, moduleConfig));
1051
+ const suffix = this.cleanPromptValue(this.replacePlaceholders(suffixTemplate, moduleName, moduleConfig));
1052
+
1053
+ if ((prefix && !cleanedValue.startsWith(prefix)) || (suffix && !cleanedValue.endsWith(suffix))) {
1054
+ return cleanedValue;
1055
+ }
1056
+
1057
+ const startIndex = prefix.length;
1058
+ const endIndex = suffix ? cleanedValue.length - suffix.length : cleanedValue.length;
1059
+ if (endIndex < startIndex) {
1060
+ return cleanedValue;
1061
+ }
1062
+
1063
+ let promptValue = cleanedValue.slice(startIndex, endIndex);
1064
+ if (promptValue.startsWith('/')) {
1065
+ promptValue = promptValue.slice(1);
1066
+ }
1067
+ if (promptValue.endsWith('/')) {
1068
+ promptValue = promptValue.slice(0, -1);
1069
+ }
1070
+
1071
+ return promptValue || cleanedValue;
980
1072
  }
981
1073
 
982
1074
  /**
@@ -993,12 +1085,7 @@ class ConfigCollector {
993
1085
  let existingValue = null;
994
1086
  if (this.existingConfig && this.existingConfig[moduleName]) {
995
1087
  existingValue = this.existingConfig[moduleName][key];
996
-
997
- // Clean up existing value - remove {project-root}/ prefix if present
998
- // This prevents duplication when the result template adds it back
999
- if (typeof existingValue === 'string' && existingValue.startsWith('{project-root}/')) {
1000
- existingValue = existingValue.replace('{project-root}/', '');
1001
- }
1088
+ existingValue = this.normalizeExistingValueForPrompt(existingValue, moduleName, item, moduleConfig);
1002
1089
  }
1003
1090
 
1004
1091
  // Special handling for user_name: default to system user