Eine Definition.Dreizehn Plattformen.
next-vibe macht aus einer einzigen TypeScript-Definition dreizehn Plattformen gleichzeitig - maßgeschneiderte Web-UI, CLI-Befehl, MCP-Tool, Mobile-Screen, Cron-Job, WebSocket, Admin-Panel und mehr. Vollständig typsicher, ohne Drift, ohne Wiederholung.
typed endpoints
runtime type errors
platforms per endpoint
files required
Du hast dasselbe dreizehnmal gebaut.
Jedes Feature braucht eine eigene Web-UI, einen CLI-Befehl, ein MCP-Tool, einen Mobile-Screen, Cron-Job, WebSocket-Handler, Admin-Panel und mehr. Gleiche Validierung, gleiche i18n, gleiche Fehlerbehandlung - nur anders verkleidet. Jedes Mal.
next-vibe baut alle dreizehn aus einer Datei.
Zwei Dateien erforderlich. Jede Plattform.
Jedes Feature lebt in einem Ordner. Nur definition.ts und route.ts sind Pflicht - alles andere ist optional.
definition.ts - der Vertrag
Felder, Zod-Schemas, Labels, Fehlertypen und Beispiele einmal deklarieren. Diese Datei ist die einzige Quelle der Wahrheit - das Framework liest sie zur Laufzeit auf jeder Plattform.
1const { POST } = createEndpoint({
2 scopedTranslation,
3 method: Methods.POST,
4 path: ["explain", "to-my-boss"],
5 title: "post.title" as const,
6 description: "post.description" as const,
7 icon: "sparkles",
8 category: "endpointCategories.ai",
9 allowedRoles: [UserRole.CUSTOMER, UserRole.ADMIN] as const,
10 fields: customWidgetObject({
11 render: ExplainContainer,
12 usage: { request: "data", response: true } as const,
13 children: {
14 decision: requestField(scopedTranslation, {
15 type: WidgetType.FORM_FIELD,
16 fieldType: FieldDataType.TEXTAREA,
17 label: "post.decision.label" as const,
18 columns: 12,
19 schema: z.string().min(10),
20 }),
21 justification: responseField(scopedTranslation, {
22 type: WidgetType.TEXT,
23 content: "post.justification.content" as const,
24 schema: z.string(),
25 }),
26 },
27 }),
28 errorTypes: {
29 [EndpointErrorTypes.UNAUTHORIZED]: { title: "post.errors.unauthorized.title" as const, description: "post.errors.unauthorized.description" as const },
30 [EndpointErrorTypes.SERVER_ERROR]: { title: "post.errors.serverError.title" as const, description: "post.errors.serverError.description" as const },
31 // ... other error types
32 },
33 successTypes: { title: "post.success.title" as const, description: "post.success.description" as const },
34 examples: { requests: { default: { decision: "Alles auf TypeScript umschreiben" } }, responses: { default: { justification: "..." } } },
35});
36
37export type ExplainResponseOutput = typeof POST.types.ResponseOutput;
38const definitions = { POST } as const;
39export default definitions;repository.ts - die Logik
Business-Logik lebt hier - DB-Abfragen, Auth-Prüfungen, Fehlerbehandlung mit success()/fail(). Die route.ts ist nur ein dünner Delegator; Validierung, Logging und Plattform-Registrierung laufen automatisch.
1import "server-only";
2
3export class ExplainRepository {
4 static async explainToMyBoss(
5 data: ExplainRequestOutput,
6 user: JwtPayloadType,
7 logger: EndpointLogger,
8 t: ExplainT,
9 ): Promise<ResponseType<ExplainResponseOutput>> {
10 if (!user.id) {
11 return fail({ message: t("post.errors.unauthorized.title"), errorType: ErrorResponseTypes.UNAUTHORIZED });
12 }
13 try {
14 const [saved] = await db
15 .insert(explainResults)
16 .values({ userId: user.id, decision: data.decision })
17 .returning();
18 logger.info("Saved decision", { userId: user.id, id: saved.id });
19 return success({ justification: saved.justification ?? "" });
20 } catch (error) {
21 logger.error("Failed to save decision", parseError(error));
22 return fail({ message: t("post.errors.serverError.title"), errorType: ErrorResponseTypes.INTERNAL_ERROR });
23 }
24 }
25}widget.tsx - die UI (optional)
Ohne Widget rendert das Framework deine Felder überall automatisch. Füge widget.tsx hinzu, um eine vollständig angepasste interaktive UI auszuliefern - dieselbe Komponente rendert in Admin-Panels, eingebetteten Widgets und Mobile-Screens.
1"use client";
2
3interface CustomWidgetProps {
4 field: {
5 value: ExplainResponseOutput | null | undefined;
6 } & (typeof definition.POST)["fields"];
7}
8
9export function ExplainContainer({ field }: CustomWidgetProps): JSX.Element {
10 const children = field.children;
11 const emptyField = useMemo(() => ({}), []);
12
13 return (
14 <Div className="flex flex-col gap-4 p-4">
15 <TextareaFieldWidget fieldName="decision" field={children.decision} />
16 <SubmitButtonWidget<typeof definition.POST>
17 field={{
18 text: "Explain to my boss",
19 loadingText: "Explaining...",
20 icon: "sparkles",
21 variant: "primary",
22 }}
23 />
24 <FormAlertWidget field={emptyField} />
25 <AlertWidget
26 fieldName="justification"
27 field={withValue(children.justification, field.value?.justification, null)}
28 />
29 </Div>
30 );
31}Ordner löschen. Das Feature verschwindet sofort von jeder Plattform.
Eine Definition. Dreizehn Plattformen.
Wenn du ein Feature zu next-vibe hinzufügst, wird es nicht nur ein API-Endpoint. Es läuft überall auf einmal.
REST-Endpoint, vollständig validiert und typisiert
Maßgeschneiderte interaktive UI - kein JSX nötig
Jeder Endpoint ist ein Befehl mit automatisch generierten Flags
Function-Calling-Schema automatisch generiert
Verbinde Claude Desktop, Cursor, jeden MCP-Client
iOS- und Android-Screens aus derselben Definition
Jeden Endpoint nach Cron-Ausdruck planen
Updates nach Abschluss an verbundene Clients senden
Native Desktop-App über dieselben Endpoint-Verträge
Automatisch generiertes Admin-UI - kein dedizierter Code
Einbettbares iframe-Widget für jede Seite
Von KI-Agenten als strukturierter Skill aufrufbar
Knoten in einem Live-Datenflussgraphen - derselbe Endpoint
Kein any. Kein unknown. Kein throw.
Typen müssen vollständig übereinstimmen - keine Ausnahmen. Das ist keine Stilpräferenz. Es ist eine Strukturregel, zur Build-Zeit durch vibe check durchgesetzt.
Durch echtes typisiertes Interface ersetzen. Wenn du nach any greifst, hat deine Architektur ein Loch.
Gleiche Regel. unknown ist nur any mit extra Schritten. Typ definieren.
Nacktes object ist bedeutungslos. Schreibe die Form, die du tatsächlich erwartest.
Typzusicherungen sind Lügen an den Compiler. Behebe stattdessen die Architektur.
Verwende ResponseType<T> mit success(data) oder fail({message, errorType}). Fehler sind Daten.
Jeder String braucht einen Übersetzungsschlüssel. Der Checker erkennt nicht übersetzte Literale.
vibe check führt Oxlint (Rust), ESLint und TypeScript-Typüberprüfung parallel aus. Null Fehler erforderlich vor dem Deployen.
Forken, fragen, deployen.
Vom ersten Tag an auf unbottled.ai-Niveau.
Auf GitHub forken, dann den Fork lokal klonen.
1git clone https://github.com/YOUR_USERNAME/next-vibe
2cd next-vibe && bun installFür lokale Entwicklung startet vibe dev PostgreSQL in Docker, führt Migrationen durch, befüllt Daten und gibt Hot Reload. Für Produktion erledigt vibe build && vibe start den ersten Deploy. Danach nutzt du vibe rebuild, um Produktion mit Zero Downtime zu aktualisieren.
1# development
2vibe dev
3
4# production - erster Start
5vibe build && vibe start
6
7# production - nach Änderungen
8vibe rebuildApp öffnen und "Als Admin einloggen" klicken - der Setup-Assistent führt durch API-Keys und Admin-Passwort.
unbottled.ai-Chat oder Claude Code öffnen und das gewünschte Feature beschreiben. Die KI schreibt alle Dateien - Definition, Route, Widget, i18n - und führt vibe check automatisch aus.
1# In unbottled.ai-Chat oder Claude Code:
2"Bau mir ein Feature, das Entscheidungen meinem Chef erklärt"
3# KI schreibt alle Dateien und führt vibe check automatisch ausBaust du etwas Großes?
Wir helfen Teams bei Setup, individuellen Integrationen, Architektur-Review und laufender Entwicklungsunterstützung. Dieselbe Codebasis, dein Deployment.