diff --git a/packages/cli/src/project-resolver.ts b/packages/cli/src/project-resolver.ts index 9e19789add..346678e4bc 100644 --- a/packages/cli/src/project-resolver.ts +++ b/packages/cli/src/project-resolver.ts @@ -537,6 +537,19 @@ export function suggestProjectName(path: string): string { return parts[parts.length - 1] || "unnamed"; } +/** + * FNXC:TaskPrefix 2026-06-24-18:00: + * Derive a task prefix from a project name by taking the first 2-4 uppercase + * letters. Falls back to "FN" for short names. Used as the suggested default + * during project onboarding so each project gets a recognizable prefix. + */ +export function suggestTaskPrefix(projectName: string): string { + const cleaned = projectName.replace(/[^a-zA-Z]/g, "").toUpperCase(); + if (cleaned.length >= 2 && cleaned.length <= 4) return cleaned; + if (cleaned.length > 4) return cleaned.slice(0, 4); + return "FN"; +} + /** * Resolve absolute path and validate it exists. */ @@ -743,6 +756,32 @@ export async function registerProjectInteractive( // Best-effort stamp only. } + /* + FNXC:Onboarding 2026-06-24-18:00: + After registration, prompt the user to confirm a task prefix and default workflow. + The prefix defaults to the first 2-4 chars of the project name so each project gets + recognizable task IDs (e.g., "MYPR" for "my-project"). The workflow defaults to coding. + Both are persisted to config.json via the TaskStore. + */ + if (interactive) { + const { TaskStore } = await import("@fusion/core"); + const store = new TaskStore(absPath); + await store.init(); + + const suggestedPrefix = suggestTaskPrefix(name); + const rl = createInterface({ input: process.stdin, output: process.stdout }); + const prefixInput = await rl.question(`\n Task prefix [${suggestedPrefix}]: `); + rl.close(); + const prefix = prefixInput.trim().toUpperCase() || suggestedPrefix; + + await store.updateSettings({ + taskPrefix: prefix, + defaultWorkflowId: "builtin:coding", + }); + await store.close(); + console.log(` ✓ Task prefix set to "${prefix}", default workflow: coding`); + } + return createResolvedProject(project); } diff --git a/packages/core/src/settings-schema.ts b/packages/core/src/settings-schema.ts index 77eb6e7176..d57d323ccb 100644 --- a/packages/core/src/settings-schema.ts +++ b/packages/core/src/settings-schema.ts @@ -258,7 +258,7 @@ export const DEFAULT_GLOBAL_SETTINGS = { export const DEFAULT_PROJECT_SETTINGS = { globalPause: false, globalPauseReason: undefined, - defaultWorkflowId: undefined, + defaultWorkflowId: "builtin:coding", enabledBuiltinWorkflowIds: undefined, approvedWorkflowCliCommands: undefined, approvedCliAutonomyAdapters: undefined, @@ -307,7 +307,7 @@ export const DEFAULT_PROJECT_SETTINGS = { onFailure: "fail", }, worktreesDir: undefined, - taskPrefix: "FN", + taskPrefix: undefined, taskAttributionTrailerNames: ["Fusion-Task-Id"], commitMsgHookEnabled: true, includeTaskIdInCommit: true, @@ -538,7 +538,7 @@ export const DEFAULT_PROJECT_SETTINGS = { researchDefaultTimeout: 300000, researchMaxSourcesPerRun: 20, researchMaxSynthesisRounds: 2, - workspaceMode: false, + workspaceMode: undefined, } satisfies CompleteSettings; /** diff --git a/packages/dashboard/src/routes/register-project-routes.ts b/packages/dashboard/src/routes/register-project-routes.ts index 2866252493..da084ec62e 100644 --- a/packages/dashboard/src/routes/register-project-routes.ts +++ b/packages/dashboard/src/routes/register-project-routes.ts @@ -374,6 +374,28 @@ export const registerProjectRoutes: ApiRouteRegistrar = (ctx) => { // Memory bootstrap failure is non-fatal - project registration succeeded }); + /* + FNXC:Onboarding 2026-06-24-18:00: + For new registrations (not reattachments), set a derived task prefix and default + workflow via the per-project TaskStore config.json so the project is immediately + usable without manual settings configuration. + */ + if (activeProjectWithOutcome.outcome === "registered") { + try { + const { TaskStore } = await import("@fusion/core"); + const store = new TaskStore(normalizedPath); + await store.init(); + const prefix = normalizedName.replace(/[^a-zA-Z]/g, "").toUpperCase().slice(0, 4) || "FN"; + await store.updateSettings({ + taskPrefix: prefix, + defaultWorkflowId: "builtin:coding", + }); + await store.close(); + } catch { + // Non-fatal: project registration succeeded; settings can be configured later + } + } + // Notify the host (serve.ts/daemon.ts) so it can run project-setup // side-effects like installing the fusion Claude-skill into // .claude/skills/fusion when pi-claude-cli is configured. The callback