feat: unified first-run flow — merge wizard + gateway install (IUH-M03)
Collapse `mosaic wizard` and `mosaic gateway install` into a single cohesive first-run experience. Gateway config and admin bootstrap now run as terminal stages of `runWizard`, sharing `WizardState` with the framework stages and eliminating the fragile 10-minute `$XDG_RUNTIME_DIR/mosaic-install-state.json` session-file bridge. - Extract `gatewayConfigStage` and `gatewayBootstrapStage` as first-class wizard stages with full spec coverage (headless + interactive paths). - `mosaic gateway install` becomes a thin wrapper that invokes the same two stages — the CLI entry point is preserved for operators who only need to (re)configure the daemon. - Honor explicit `--port` override even on resume: when the override differs from the saved GATEWAY_PORT, force a config regeneration so `.env` and `meta.json` cannot drift. - Honor `state.hooks.accepted === false` in the finalize stage and in `mosaic-link-runtime-assets`: declined hooks are now actually opted-out, with a stable `mosaic-managed: true` marker in the template so cleanup survives template updates without touching user-owned configs. - Headless rerun of an already-bootstrapped gateway with no local token cache is a successful no-op (no more false-positive install failures). - `tools/install.sh` calls `mosaic wizard` only — the follow-up `mosaic gateway install` auto-launch is removed. Closes mosaicstack/mosaic-stack#427. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -70,11 +70,45 @@ for p in "${legacy_paths[@]}"; do
|
||||
done
|
||||
|
||||
# Claude-specific runtime files (settings, hooks — NOT CLAUDE.md which is now a thin pointer)
|
||||
# When MOSAIC_SKIP_CLAUDE_HOOKS=1 is set (user declined hooks in the wizard
|
||||
# preview stage), skip hooks-config.json but still copy the other runtime
|
||||
# files so Claude still gets CLAUDE.md/settings.json/context7 guidance.
|
||||
for runtime_file in \
|
||||
CLAUDE.md \
|
||||
settings.json \
|
||||
hooks-config.json \
|
||||
context7-integration.md; do
|
||||
if [[ "$runtime_file" == "hooks-config.json" ]] && [[ "${MOSAIC_SKIP_CLAUDE_HOOKS:-0}" == "1" ]]; then
|
||||
echo "[mosaic-link] Skipping hooks-config.json (user declined in wizard)"
|
||||
# An existing ~/.claude/hooks-config.json that we previously installed
|
||||
# is identified by one of:
|
||||
# 1. It's a symlink (legacy symlink-mode install)
|
||||
# 2. It contains the `mosaic-managed` marker string we embed in the
|
||||
# template (survives template updates unlike byte-equality)
|
||||
# 3. It is byte-identical to the current Mosaic template (fallback
|
||||
# for templates that pre-date the marker)
|
||||
# Anything else is user-owned and we must leave it alone.
|
||||
existing_hooks="$HOME/.claude/hooks-config.json"
|
||||
mosaic_hooks_src="$MOSAIC_HOME/runtime/claude/hooks-config.json"
|
||||
if [[ -L "$existing_hooks" ]]; then
|
||||
rm -f "$existing_hooks"
|
||||
echo "[mosaic-link] Removed previously-linked Mosaic hooks-config.json (was symlink)"
|
||||
elif [[ -f "$existing_hooks" ]]; then
|
||||
is_mosaic_managed=0
|
||||
if grep -q 'mosaic-managed' "$existing_hooks" 2>/dev/null; then
|
||||
is_mosaic_managed=1
|
||||
elif [[ -f "$mosaic_hooks_src" ]] && cmp -s "$existing_hooks" "$mosaic_hooks_src"; then
|
||||
is_mosaic_managed=1
|
||||
fi
|
||||
if [[ "$is_mosaic_managed" == "1" ]]; then
|
||||
mv "$existing_hooks" "${existing_hooks}.mosaic-bak-${backup_stamp}"
|
||||
echo "[mosaic-link] Removed previously-linked Mosaic hooks-config.json (backup at ${existing_hooks}.mosaic-bak-${backup_stamp})"
|
||||
else
|
||||
echo "[mosaic-link] Leaving existing non-Mosaic hooks-config.json in place"
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
src="$MOSAIC_HOME/runtime/claude/$runtime_file"
|
||||
[[ -f "$src" ]] || continue
|
||||
copy_file_managed "$src" "$HOME/.claude/$runtime_file"
|
||||
|
||||
Reference in New Issue
Block a user