feat(KB-504): add migration and first-run experience for multi-project support

- Add MigrationOrchestrator with filesystem scanning and auto-registration

- Add FirstRunExperience with setup wizard state management

- CLI integration with automatic migration hook on first run

- Backward-compatible single-project mode support

- \"--project\" flag support for CLI multi-project targeting

- KB_SKIP_MIGRATION environment variable for recovery

- Graceful fallback when central database unavailable

- New fn init command to initialize kb projects

- Interactive first-run setup wizard API in dashboard
This commit is contained in:
gsxdsm
2026-04-01 08:17:30 -07:00
parent 5681c354e2
commit 017c1bb8d5
9 changed files with 286 additions and 441 deletions

View File

@@ -76,7 +76,7 @@ import {
fetchTasks,
type ProjectInfo,
type DetectedProject,
} from "../../app/api";
} from "../../app/api.js";
function mockFetchResponse(
ok: boolean,

View File

@@ -1075,18 +1075,28 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
*/
router.get("/setup-state", async (_req, res) => {
try {
const { FirstRunDetector } = await import("@fusion/core");
const { CentralCore } = await import("@fusion/core");
const { createFirstRunExperience, CentralCore } = await import("@fusion/core");
const { createMigrationOrchestrator } = await import("@fusion/core");
const detector = new FirstRunDetector();
const state = await detector.detectFirstRunState();
const detectedProjects = await detector.detectExistingProjects(process.cwd());
const central = new CentralCore();
await central.init();
res.json({
state,
detectedProjects,
hasCentralDb: detector.hasCentralDb(),
});
try {
const migration = createMigrationOrchestrator(central);
const firstRun = createFirstRunExperience(central);
const needsMigration = await migration.needsMigration();
const state = await firstRun.getSetupState();
const detectedProjects = state.detectedProjects || [];
res.json({
state: needsMigration ? "needs-migration" : state.isFirstRun ? "setup-wizard" : "normal-operation",
detectedProjects,
hasCentralDb: true,
});
} finally {
await central.close();
}
} catch (err: any) {
res.status(500).json({ error: err.message });
}
@@ -1106,19 +1116,19 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
return;
}
const { CentralCore, MigrationCoordinator } = await import("@fusion/core");
const { CentralCore, createFirstRunExperience } = await import("@fusion/core");
const central = new CentralCore();
await central.init();
try {
const coordinator = new MigrationCoordinator(central);
const result = await coordinator.completeSetup(projects);
const firstRun = createFirstRunExperience(central);
const result = await firstRun.completeSetup(projects);
res.json({
success: result.success,
registered: result.projectsRegistered,
errors: result.errors,
registered: result.projects.map(p => p.id),
errors: [],
});
} finally {
await central.close();