fix: make mosaic init idempotent — detect existing config files
Some checks failed
ci/woodpecker/pr/ci Pipeline failed
ci/woodpecker/push/ci Pipeline failed

- mosaic-init bash script: detect existing SOUL.md/USER.md/TOOLS.md and
  prompt user to keep, import (re-use values as defaults), or overwrite.
  Non-interactive mode exits cleanly unless --force is passed.
  Overwrite creates timestamped backups before replacing files.

- launch.ts checkSoul(): prefer 'mosaic wizard' over legacy bash script
  when SOUL.md is missing, with fallback to mosaic-init.

- detect-install.ts: pre-populate wizard state with existing values when
  user chooses 'reconfigure', so they see current settings as defaults.

- soul-setup.ts: show existing agent name and communication style as
  defaults during reconfiguration.

- Added tests for reconfigure pre-population and reset non-population.
This commit is contained in:
Jarvis
2026-04-02 20:20:59 -05:00
parent 80e69016b0
commit 361fece023
5 changed files with 194 additions and 5 deletions

View File

@@ -87,7 +87,9 @@ export async function detectInstallStage(
],
});
if (state.installAction === 'keep') {
if (state.installAction === 'keep' || state.installAction === 'reconfigure') {
// Load existing values — for 'keep' they're final, for 'reconfigure'
// they become pre-populated defaults so the user can tweak them.
state.soul = await config.readSoul();
state.user = await config.readUser();
state.tools = await config.readTools();

View File

@@ -24,6 +24,18 @@ export async function soulSetupStage(p: WizardPrompter, state: WizardState): Pro
return undefined;
},
});
} else if (state.installAction === 'reconfigure') {
// Show existing value as default so the user can accept or change it
state.soul.agentName = await p.text({
message: 'What name should agents use?',
placeholder: state.soul.agentName,
defaultValue: state.soul.agentName,
validate: (v) => {
if (v.length === 0) return 'Name cannot be empty';
if (v.length > 50) return 'Name must be under 50 characters';
return undefined;
},
});
}
if (state.mode === 'advanced') {
@@ -38,7 +50,7 @@ export async function soulSetupStage(p: WizardPrompter, state: WizardState): Pro
state.soul.roleDescription ??= DEFAULTS.roleDescription;
}
if (!state.soul.communicationStyle) {
if (!state.soul.communicationStyle || state.installAction === 'reconfigure') {
state.soul.communicationStyle = await p.select<CommunicationStyle>({
message: 'Communication style',
options: [
@@ -46,7 +58,7 @@ export async function soulSetupStage(p: WizardPrompter, state: WizardState): Pro
{ value: 'friendly', label: 'Friendly', hint: 'Warm but efficient, conversational' },
{ value: 'formal', label: 'Formal', hint: 'Professional, structured, thorough' },
],
initialValue: 'direct',
initialValue: state.soul.communicationStyle ?? 'direct',
});
}