diff --git a/apps/marketing/src/components/Footer.astro b/apps/marketing/src/components/Footer.astro new file mode 100644 index 000000000..24bbc08aa --- /dev/null +++ b/apps/marketing/src/components/Footer.astro @@ -0,0 +1,34 @@ + diff --git a/apps/marketing/src/components/Nav.astro b/apps/marketing/src/components/Nav.astro new file mode 100644 index 000000000..34b5a5717 --- /dev/null +++ b/apps/marketing/src/components/Nav.astro @@ -0,0 +1,66 @@ +--- +interface Props { + currentPage?: "blog" | "roadmap"; +} + +const { currentPage } = Astro.props; + +const linkClass = (page?: string) => + page === currentPage + ? "text-ice-50" + : "text-ice-100 hover:text-ice-50"; +--- + + diff --git a/apps/marketing/src/components/PageScripts.astro b/apps/marketing/src/components/PageScripts.astro new file mode 100644 index 000000000..360dec312 --- /dev/null +++ b/apps/marketing/src/components/PageScripts.astro @@ -0,0 +1,27 @@ + diff --git a/apps/marketing/src/content.config.ts b/apps/marketing/src/content.config.ts index c16e06018..8cae23293 100644 --- a/apps/marketing/src/content.config.ts +++ b/apps/marketing/src/content.config.ts @@ -12,4 +12,14 @@ const roadmap = defineCollection({ }), }); -export const collections = { roadmap }; +const blog = defineCollection({ + loader: glob({ pattern: "**/*.md", base: "./src/content/blog" }), + schema: z.object({ + title: z.string(), + description: z.string(), + date: z.coerce.date(), + author: z.string(), + }), +}); + +export const collections = { roadmap, blog }; diff --git a/apps/marketing/src/content/blog/introducing-fabro.md b/apps/marketing/src/content/blog/introducing-fabro.md new file mode 100644 index 000000000..819f118fc --- /dev/null +++ b/apps/marketing/src/content/blog/introducing-fabro.md @@ -0,0 +1,22 @@ +--- +title: "Introducing Fabro" +description: "Fabro is an open-source workflow orchestration platform that lets expert engineers define their process as a graph and let AI agents execute it." +date: 2026-03-16 +author: "Fabro Team" +--- + +Most AI coding tools give you a chat window and hope for the best. Fabro takes a different approach: you define your engineering process as a workflow graph, and Fabro executes it — with verification gates, human checkpoints, and full observability at every step. + +## Why workflows? + +A chat-based agent is a single loop: prompt, act, repeat. That works for small tasks, but it falls apart when you need structure — when the plan should be approved before implementation begins, when tests must pass before the PR is opened, when a second model should cross-review the first. + +Fabro workflows are DOT graphs. Each node is a stage with a specific role: planning, coding, reviewing, testing. Edges define the flow. Human-in-the-loop gates let you intervene where it matters and step back where it doesn't. + +## Multi-model by design + +Not every stage needs a frontier model. Fabro's CSS-like model stylesheets let you assign models by stage ID, class, or shape — so you can use fast models for triage and frontier models for the work that matters. Swap providers without changing your workflow. + +## What's next + +We're just getting started. Check the [roadmap](/roadmap) to see what we're building, and join us on [Discord](/discord) to shape what comes next. diff --git a/apps/marketing/src/layouts/Layout.astro b/apps/marketing/src/layouts/Layout.astro index b21ea7aba..1f8581c37 100644 --- a/apps/marketing/src/layouts/Layout.astro +++ b/apps/marketing/src/layouts/Layout.astro @@ -3,9 +3,10 @@ import "../styles/global.css"; interface Props { title: string; + description?: string; } -const { title } = Astro.props; +const { title, description = "Fabro is the open source dark software factory for expert engineers. Define your process as a workflow graph, let AI agents execute it, and intervene only where it matters." } = Astro.props; --- @@ -13,7 +14,7 @@ const { title } = Astro.props; - + @@ -21,7 +22,7 @@ const { title } = Astro.props; - + @@ -31,7 +32,7 @@ const { title } = Astro.props; - + diff --git a/apps/marketing/src/pages/blog/[slug].astro b/apps/marketing/src/pages/blog/[slug].astro new file mode 100644 index 000000000..4cd03cf02 --- /dev/null +++ b/apps/marketing/src/pages/blog/[slug].astro @@ -0,0 +1,64 @@ +--- +import Layout from "../../layouts/Layout.astro"; +import Nav from "../../components/Nav.astro"; +import Footer from "../../components/Footer.astro"; +import PageScripts from "../../components/PageScripts.astro"; +import { getCollection, render } from "astro:content"; + +export async function getStaticPaths() { + const posts = await getCollection("blog"); + return posts.map((post) => ({ + params: { slug: post.id }, + props: { post }, + })); +} + +const { post } = Astro.props; +const { Content } = await render(post); + +const formatDate = (date: Date) => + date.toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }); + +const wordCount = post.body?.split(/\s+/).length ?? 0; +const readingTime = Math.max(1, Math.round(wordCount / 250)); +--- + + +