feat: add company builder wizard with AI-powered org generation (#73)

* feat: add company builder wizard with AI-powered org generation

Adds a new "Build Your Company" step to the onboarding wizard that lets
users describe their business and generates a full agent org structure
using OpenClaw's AI. Includes company plan generation, role deduplication,
agent bootstrap with main-agent reuse, org chart preview, confetti on
success, CSS voxel running-avatar loader, amber theme unification, and
best-effort SSH workspace cleanup.

Made-with: Cursor

* fix: resolve lint errors in CompanyBuilderModal

Replace setState-in-effect pattern with a direct callback, escape
apostrophes in JSX text, and derive org chart hover state without
side effects.

Made-with: Cursor

---------

Co-authored-by: iamlukethedev <lucas.guilherme@smartwayslfl.com>
This commit is contained in:
Luke The Dev
2026-03-27 12:59:44 -05:00
committed by GitHub
parent 3da1694085
commit a953c5fda6
31 changed files with 3308 additions and 124 deletions
@@ -19,6 +19,7 @@ import { WelcomeStep } from "@/features/onboarding/components/WelcomeStep";
import { PrerequisitesStep } from "@/features/onboarding/components/PrerequisitesStep";
import { ConnectStep } from "@/features/onboarding/components/ConnectStep";
import { AgentsStep } from "@/features/onboarding/components/AgentsStep";
import { CompanyStep } from "@/features/onboarding/components/CompanyStep";
import { CompleteStep } from "@/features/onboarding/components/CompleteStep";
export type OnboardingWizardProps = {
@@ -36,6 +37,12 @@ export type OnboardingWizardProps = {
onConnect: () => void;
/** Called when the user finishes or dismisses the wizard. */
onComplete: () => void;
/** Opens the reusable company builder. */
onOpenCompanyBuilder: () => void;
initialStep?: OnboardingStepId;
initialCompletedSteps?: OnboardingStepId[];
createdCompanyName?: string | null;
companyCreated?: boolean;
/** Connection error message, if any. */
connectionError: string | null;
/** Whether we're currently connecting. */
@@ -51,12 +58,17 @@ export const OnboardingWizard = ({
onTokenChange,
onConnect,
onComplete,
onOpenCompanyBuilder,
initialStep = "welcome",
initialCompletedSteps,
createdCompanyName = null,
companyCreated = false,
connectionError,
connecting,
}: OnboardingWizardProps) => {
const [currentStep, setCurrentStep] = useState<OnboardingStepId>("welcome");
const [currentStep, setCurrentStep] = useState<OnboardingStepId>(initialStep);
const [completedSteps, setCompletedSteps] = useState<Set<OnboardingStepId>>(
new Set(),
() => new Set(initialCompletedSteps ?? []),
);
const stepIndex = useMemo(() => getStepIndex(currentStep), [currentStep]);
@@ -116,8 +128,21 @@ export const OnboardingWizard = ({
);
case "agents":
return <AgentsStep agentCount={agentCount} connected={gatewayConnected} />;
case "company":
return (
<CompanyStep
connected={gatewayConnected}
agentCount={agentCount}
onOpenCompanyBuilder={onOpenCompanyBuilder}
/>
);
case "complete":
return <CompleteStep />;
return (
<CompleteStep
companyCreated={companyCreated}
companyName={createdCompanyName}
/>
);
default:
return null;
}
@@ -125,7 +150,7 @@ export const OnboardingWizard = ({
return (
<div className="fixed inset-0 z-[100000] flex items-center justify-center bg-black/70 backdrop-blur-sm">
<div className="relative mx-4 flex w-full max-w-[560px] flex-col overflow-hidden rounded-xl border border-white/10 bg-[#0d1117] shadow-2xl">
<div className="relative mx-4 flex h-[min(92vh,640px)] w-full max-w-[560px] flex-col overflow-hidden rounded-xl border border-white/10 bg-[#0d1117] shadow-2xl">
{/* Header */}
<div className="flex items-center justify-between border-b border-white/10 px-6 py-4">
<div>
@@ -154,9 +179,9 @@ export const OnboardingWizard = ({
key={step.id}
className={`h-1 flex-1 rounded-full transition-colors ${
idx <= stepIndex
? "bg-emerald-500"
? "bg-amber-400"
: completedSteps.has(step.id)
? "bg-emerald-500/40"
? "bg-amber-400/40"
: "bg-white/10"
}`}
/>
@@ -164,7 +189,7 @@ export const OnboardingWizard = ({
</div>
{/* Step content */}
<div className="min-h-[280px] px-6 py-5">{renderStepContent()}</div>
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-4">{renderStepContent()}</div>
{/* Footer navigation */}
<div className="flex items-center justify-between border-t border-white/10 px-6 py-4">
@@ -187,7 +212,7 @@ export const OnboardingWizard = ({
{currentStep === "complete" ? (
<button
type="button"
className="inline-flex items-center gap-1.5 rounded-md bg-emerald-600 px-4 py-2 text-xs font-semibold text-white transition-colors hover:bg-emerald-500"
className="inline-flex items-center gap-1.5 rounded-md bg-amber-500 px-4 py-2 text-xs font-semibold text-[#1a1206] transition-colors hover:bg-amber-400"
onClick={onComplete}
>
Enter Office