fix(landing): sync first-run narrative

This commit is contained in:
ZacharyZhang-NY committed 2026-04-22 15:30:10 -04:00
1 parent f8ed2aa666
commit e3eb45b08d
8 files changed
+526 -79

No files matched your search

+7 -3
View File
@@ -114,9 +114,13 @@ pnpm dev
### First run
1. Onboarding probes your shell PATH for `node` (agents call the local HTTP API via `node -e`).
2. Settings → Providers. Sign in or drop a key.
3. Create a company: name, mission, goal, mode, workspace.
1. Requirements. Welcome screen, then a Node.js check. CLI runtimes call the local API through `node -e`.
2. Providers. Connect at least one runtime.
3. Company. Enter name, description, and workspace.
4. Team Models. Enable the models available to the CEO and workers.
5. Mode. Default is `ai_ceo`; `ai_cofounder` keeps key decisions human-reviewed.
6. Mission. Capture the first goal and acceptance description.
7. Launch. AgentCompany creates the company, CEO agent, workspace, kickoff task, and initial run handoff.
---
+321
View File
@@ -0,0 +1,321 @@
# Phase 5 — Design System & Desktop Flows
> Visual + interaction system for the rebuild. Anchors: desktop-native, two-mode (AI CEO / AI Co-Founder), thread-runtime first, knowledge-backed. No web-dashboard-in-a-shell affordances.
---
## 1. Design intent
A control-plane for an autonomous company. Calm. Information-dense. Keyboard-first. Pause moments are intentional — when AI Co-Founder mode pauses for review, the UI **feels** like a checkpoint, not a delay.
The aesthetic baseline already lives in `landing/` and `screenshots/Dashboard.jpeg`: dusty navy (`#4a5d7a`), graphite text, off-white surfaces, restrained accent. We keep that palette and tighten the IA.
## 2. Information architecture
### 2.1 Top-level navigation (left rail)
```
┌─────────────────────────────────────┐
│ ⌂ Dashboard │
│ 📥 Inbox ⓘ <wait-on-you count>│
│ 🧵 Threads │
│ ✓ Tasks & Approvals │
│ 💼 Workspaces │
│ 📚 Knowledge │
│ ── │
│ ⚙ Settings │
│ 📊 Diagnostics │
└─────────────────────────────────────┘
```
Sidebar collapses to icons at < 1100 px. Active item shows a subtle vertical accent bar.
### 2.2 Top bar
Per company: company picker · execution-mode pill (AI CEO / AI Co-Founder) · provider chip with active connector + cost-this-month · search · profile.
The mode pill is interactive: clicking opens a small panel explaining the difference, with a switch.
### 2.3 Onboarding (first run)
Welcome opens the flow. The setup panel then runs 7 sections:
1. **Requirements** — checks Node.js because CLI runtimes call the local API through `node -e`.
2. **Providers** — connects at least one runtime.
3. **Company** — captures company name, description, and workspace.
4. **Team Models** — enables the models available to the CEO and workers.
5. **Mode** — defaults to `ai_ceo`; `ai_cofounder` keeps key decisions human-reviewed.
6. **Mission** — captures the first goal and acceptance description.
7. **Launch** — creates the company, CEO agent, workspace, kickoff task, and initial run handoff.
The user lands in Control Center after launch.
### 2.4 Dashboard
Top: 4 KPI tiles (Active agents, Tasks in review, Spend month-to-date, Waiting-on-you).
Below: 3 columns —
- "What's happening now" (live runs with brief status)
- "Decisions waiting for you" (approval queue mirrors Inbox)
- "Recent deliverables" (documents/work-products with reviewer)
Empty states explain what each panel will hold once activity begins.
### 2.5 Inbox
A single ordered feed of `inbox_items`:
| Type | Source | Action |
|---|---|---|
| Wait-on-you (approval) | `thread_wait_states` `waiting_for='human'` + linked approval | Approve / Deny inline |
| @mention | `agent_messages.body` mention scan | Reply |
| Subscribed thread update | `thread_subscriptions` + `conversation_events` | Open |
| Deliverable for review | `tasks.requires_user_review` true | Open task |
Counts shown in sidebar mirror the unread/wait totals.
### 2.6 Threads
A two-pane view: left = thread list (filter: All / Subscribed / Wait / Recent); right = thread transcript with composer.
Transcript items: agent messages, tool executions (collapsible), tool results (collapsible), human posts, wait-state markers ("Waiting on @you to approve `bash` call …").
Composer: posts a `user` message into the active thread → triggers `wakeup_request` for the assigned agent.
### 2.7 Tasks & Approvals
Existing task/approval surfaces preserved; added:
- "Pending tool approvals" section grouped by run (each item shows tool, args summary, "Approve in thread" deep link).
### 2.8 Workspaces
Existing surface preserved. Adds workspace lock indicator and a "Reveal in Finder/Explorer/Files" button (uses `openPath`).
### 2.9 Knowledge
Existing surface preserved. Adds "Inject into agent" affordance — moves a knowledge entry into the prompt-builder context for the next run.
### 2.10 Settings
Sub-pages:
```
Settings
• Profile
• Providers ← new (the key addition)
• Execution defaults (mode default, thinking level, retry counts)
• Budgets
• Workspaces
• Skills
• Updates
• Advanced (logs path, profile dir, vault info)
```
### 2.11 Diagnostics
Read-only:
- Recent runs with status colours
- Recent IPC errors
- Vault backend label (e.g. "macOS Keychain" or "encrypted file")
- Current pi-mono package versions
- Open log directory button
## 3. Settings → Providers panel (the central new surface)
### 3.1 Layout
```
┌──────────────────────────────────────────────────────────────────────┐
│ Providers │
│ Search [ ] View: [Configured ▾] [+ Add provider]│
│ │
│ ── Configured (4) ──────────────────────────────────────────────── │
│ ● Claude Pro/Max OAuth · claude.pro@gmail.com [Re-login][⋯] │
│ ● Anthropic API Key · sk-ant-…abcd [Edit] [⋯] │
│ ● AWS Bedrock AWS · profile=prod region=us-east-1 [Edit] │
│ ! Gemini CLI OAuth · refresh failed [Re-login] │
│ │
│ ── Available (21) ─────────────────────────────────────────────── │
│ ○ ChatGPT Plus/Pro (Codex) OAuth [Login] │
│ ○ GitHub Copilot OAuth [Login] │
│ ○ Antigravity OAuth [Login] │
│ ○ OpenAI API Key [Add] │
│ … │
└──────────────────────────────────────────────────────────────────────┘
```
Status dot colours:
- `●` green — `active`
- `◐` amber — `expired` (refresh failed)
- `!` red — `error`
- `○` neutral — `not configured`
### 3.2 Forms
Five hand-written forms (no universal generator):
1. **OAuthRow** — "Login" button, optional GHE domain prompt for GitHub Copilot (renders `OAuthPromptDialog`), shows progress (`onProgress` messages) inline as a tiny status line.
2. **ApiKeyForm** — single password input (masked) + "Reveal" toggle + dropdown "Source: Inline value | Env var name". Save button.
3. **AzureForm** — fields: `baseUrl`, `resourceName`, `apiVersion`, `deploymentMap` (key/value editor), `apiKey`. Save button.
4. **AwsForm** — radio: "AWS profile" / "IAM keys" / "Bearer token", each revealing fields. Region (default `us-east-1`). Advanced toggles for `forceCache`, `endpointUrl`, `skipAuth`, `forceHttp1`.
5. **VertexForm** — fields: `project`, `location`, `credentialsPath` (file picker, optional), "Use ADC" checkbox.
Each form has a "Test connection" button that performs `streamSimple(... { maxTokens: 1 })` against the connector's default model with a 1-character prompt. Outcome: ✅ "OK — used $0.0001" / ❌ "<error message>".
### 3.3 OAuthPromptDialog
Modal triggered by `oauth:prompt-request` (e.g. GHE domain) and `oauth:manual-code-request` (fallback when callback server is blocked):
```
┌─────────────────────────────────────┐
│ GitHub Copilot │
│ │
│ Enter your GitHub Enterprise domain. │
│ Leave blank for github.com. │
│ │
│ [github.com_______________________] │
│ │
│ [Cancel] [Submit →] │
└─────────────────────────────────────┘
```
## 4. Mode UX
Mode pill in top bar:
- **AI CEO** — solid green pill. Tooltip: "Tools execute without review. Approvals only for explicit policy gates."
- **AI Co-Founder** — outlined navy pill. Tooltip: "Agents pause at write-side tools (bash, edit, write). You approve in Inbox."
Switching modes is per-company. The switch action writes `companies.execution_mode` and a confirmation toast.
### Pause state in a thread
When a run pauses at `beforeToolCall`, the thread shows:
```
┌───────────────────────────────────────────┐
│ ⟳ engineer-01 wants to run │
│ │
│ [bash] npm install │
│ in workspaces/web-frontend │
│ │
│ [Approve] [Deny] [Approve & don't ask]│
└───────────────────────────────────────────┘
```
"Approve & don't ask" sets a per-task auto-approval for the same tool with the same arg shape (writes `agent_permissions.permissions_json`).
## 5. Visual system
### 5.1 Tokens (Tailwind config)
```
colors:
navy.700 #2c3a4f
navy.600 #4a5d7a ← brand accent
navy.500 #6f8198
graphite #1f1f23 ← primary text
ash #56575a ← secondary text
fog #efeff1 ← surface alt
paper #f8f8f9 ← surface
accent #c98c5a ← warm accent (warnings, awaiting human)
ok #4a8d6f
warn #c79a45
err #b75252
font:
sans Inter, ui-sans-serif
mono "JetBrains Mono", ui-monospace
radius:
sm 4px / md 8px / lg 12px
shadow:
card 0 1px 2px rgba(0,0,0,0.06), 0 0 0 1px rgba(0,0,0,0.04)
```
Dark mode: invert paper/fog/graphite axis; navy.600 preserved as accent.
### 5.2 Spacing scale
`4 / 8 / 12 / 16 / 24 / 32 / 48 / 64` px. No off-grid spacing.
### 5.3 Components reused
`src/renderer/components/ui/` already exposes primitives, layout, feedback, hooks. We extend it (no rewrite) with:
- `<StatusDot status="active|expired|error|none" />`
- `<MaskedSecret value="sk-ant-…" />`
- `<KeyValueEditor />` (Azure deployment map, custom env)
- `<ModePill mode="ai_ceo" | "ai_co_founder" interactive />`
- `<WaitMarker reason="awaiting human" tool="bash" />`
### 5.4 Density + responsive
- Default density: comfortable. Toggle to compact in Settings → Profile.
- Window min size: `1024 × 720`. Sidebar collapses < 1100. Right panels collapse < 1280.
- Drag-and-drop affordances:
- Knowledge entries → agent context (drag onto agent card).
- Files into Workspaces (already supported by underlying tooling; UI affordance added).
### 5.5 Keyboard
| Shortcut | Action |
|---|---|
| `⌘/Ctrl + K` | Quick open (companies, threads, tasks) |
| `⌘/Ctrl + 1..9` | Switch sidebar item |
| `⌘/Ctrl + Enter` | Submit composer / Approve highlighted item |
| `Esc` | Close dialogs |
| `?` | Show shortcuts overlay |
## 6. Empty + loading + error states (mandatory per screen)
Every new screen ships:
- Empty illustration + 1-line explanation + 1-CTA.
- Loading: skeleton blocks (no spinners except > 2 s).
- Error: a `<FailureCard>` with an action button (retry / open logs).
No placeholder copy. No "Coming soon" text on any provider — every provider has either an active form or an inactive form with a clear "Configure" path.
## 7. Microcopy rules
- Lead with verbs ("Approve", "Deny", "Re-login").
- Status text uses past tense ("Refreshed 2 min ago").
- Cost expressed as `$0.012` (3 decimals when < $1, otherwise 2).
- Token counts use grouping `12,345`.
- Times use locale-aware format with absolute hover tooltip.
## 8. Thread runtime UX bindings
| Renderer concept | Binding |
|---|---|
| "Wait card" in thread | `thread_wait_states` row resolved == null |
| "Read receipt" | (not in this rebuild) |
| "Subscribed" badge | `thread_subscriptions` row exists |
| "Mention" highlight | `agent_messages.body` mentions parsed at render time (existing helper) |
| "Inject knowledge" | calls `agentCompany.addThreadEvent` with `event_type: 'knowledge_injection'`, then `wakeup_request` |
## 9. Onboarding mode-explainer copy
```
AI CEO
The CEO agent runs without pausing. Tools execute autonomously.
Best for: research sprints, content batches, long-running automation.
AI Co-Founder
The CEO agent pauses before any tool that writes to your filesystem,
modifies code, or runs a shell command. You approve in your Inbox.
Best for: code in your real workspaces, anything you'd want to review first.
You can switch any company between modes from the top bar.
```
## 10. Done criteria for Phase 5
- Every screen is named.
- Every form is a dedicated component.
- Every state (empty/loading/error) has a rule.
- Every status surface has a token.
- The Providers panel is fully specified per the contract.
- The mode UX is concrete, not aspirational.
+14
View File
@@ -0,0 +1,14 @@
# AgentCompany Landing
Next.js landing page for the AgentCompany desktop release lane.
## Copy Sources
| Surface | Source |
|---|---|
| Download cards | `landing/components/release-links.generated.ts` |
| Platform FAQ | `landing/lib/platform-support.ts` |
| Hero, How It Works, software JSON-LD features | `landing/lib/product-narrative.ts` |
| Runtime download URLs | generated from `scripts/generate-landing-release-links.mjs` |
The public story mirrors the first-run desktop flow: Welcome, Requirements, Providers, Company, Team Models, Mode, Mission, Launch.
+16 -36
View File
@@ -1,6 +1,10 @@
import type { Metadata } from "next";
import "./globals.css";
import { ThemeProvider } from "../components/theme-provider";
import {
landingHowItWorksNarrative,
landingStructuredDataNarrative,
} from "../lib/product-narrative";
import { platformSupportSummary } from "../lib/platform-support";
const description =
@@ -8,7 +12,7 @@ const description =
export const metadata: Metadata = {
metadataBase: new URL("https://agent-company.dev"),
title: "AgentCompany — Launch an Autonomous AI Company in Minutes",
title: "AgentCompany - Launch an Autonomous AI Company from Your Desktop",
description,
keywords: [
"AI company",
@@ -32,7 +36,7 @@ export const metadata: Metadata = {
"AI company builder",
],
openGraph: {
title: "AgentCompany — Launch an Autonomous AI Company in Minutes",
title: "AgentCompany - Launch an Autonomous AI Company from Your Desktop",
description,
url: "https://agent-company.dev/",
siteName: "AgentCompany",
@@ -49,7 +53,7 @@ export const metadata: Metadata = {
},
twitter: {
card: "summary_large_image",
title: "AgentCompany — Launch an Autonomous AI Company in Minutes",
title: "AgentCompany - Launch an Autonomous AI Company from Your Desktop",
description,
images: ["/screenshots/Dashboard.jpeg"],
},
@@ -76,19 +80,9 @@ const softwareJsonLd = {
url: "https://agent-company.dev",
downloadUrl: "https://agent-company.dev/#download",
screenshot: "https://agent-company.dev/screenshots/Dashboard.jpeg",
datePublished: "2026-03-01",
softwareVersion: "1.0",
softwareVersion: landingStructuredDataNarrative.softwareVersion,
description,
featureList: [
"One-click AI company creation",
"Self-organizing AI agent teams",
"Autonomous task management",
"Real-time agent communication",
"Human-in-the-loop approvals",
"AI document generation",
"Multi-connector support (Claude, Codex, Gemini)",
"Cross-platform (macOS, Windows, Linux)",
],
featureList: landingStructuredDataNarrative.featureList,
author: { "@type": "Organization", name: "AgentCompany", url: "https://agent-company.dev" },
};
@@ -104,27 +98,13 @@ const howToJsonLd = {
"@context": "https://schema.org",
"@type": "HowTo",
name: "How to Launch an Autonomous AI Company with AgentCompany",
description: "Create a fully autonomous AI company in three steps using the AgentCompany desktop app.",
step: [
{
"@type": "HowToStep",
position: 1,
name: "Set Your Goal",
text: "Describe the company, its mission, and what you want delivered. Connect your preferred AI runtime.",
},
{
"@type": "HowToStep",
position: 2,
name: "Agents Self-Organize",
text: "A CEO agent analyzes your goal, hires specialists, creates departments, and delegates the work autonomously.",
},
{
"@type": "HowToStep",
position: 3,
name: "Collect Results",
text: "Review deliverables, approve critical decisions, and download finished work including code, content, and research.",
},
],
description: "Complete the AgentCompany desktop first-run flow and queue the first mission.",
step: landingHowItWorksNarrative.steps.map((step) => ({
"@type": "HowToStep",
position: step.number,
name: step.title,
text: step.description,
})),
};
const faqJsonLd = {
+6 -6
View File
@@ -2,6 +2,7 @@
import { motion } from "framer-motion";
import { AsciiBackground } from "./ascii-background";
import { landingHeroNarrative } from "../lib/product-narrative";
const sharedEase: [number, number, number, number] = [0.16, 1, 0.3, 1];
@@ -54,7 +55,7 @@ export function Hero() {
<div className="flex flex-col items-center text-center">
<motion.div {...heroMotion(0.05)}>
<span className="inline-flex items-center px-4 py-1.5 rounded-full text-xs font-semibold tracking-wide bg-[var(--accent-soft)] text-[var(--accent)] dark:text-[var(--muted-strong)] border border-[var(--border)]">
Agent Company
{landingHeroNarrative.eyebrow}
</span>
</motion.div>
@@ -62,16 +63,15 @@ export function Hero() {
{...heroMotion(0.15)}
className="mt-8 font-serif text-[clamp(2.75rem,2rem+3.8vw,5rem)] font-normal leading-[1.0] tracking-[-0.03em] text-[var(--text)]"
>
Launch an AI Company <br className="hidden sm:block" />
<span className="text-[var(--accent)]">in Minutes</span>
{landingHeroNarrative.titleLine1} <br className="hidden sm:block" />
<span className="text-[var(--accent)]">{landingHeroNarrative.titleLine2}</span>
</motion.h1>
<motion.p
{...heroMotion(0.25)}
className="mt-7 max-w-2xl text-lg sm:text-xl leading-relaxed text-[var(--muted)]"
>
Describe your company&apos;s mission. A CEO agent recruits the team, delegates work, and delivers real
results — code, content, research, and more. You&apos;re the founder.
{landingHeroNarrative.description}
</motion.p>
<motion.div {...heroMotion(0.35)} className="mt-12">
@@ -104,7 +104,7 @@ export function Hero() {
<WindowsIcon />
<LinuxIcon />
</div>
<span>Available on macOS, Windows & Linux</span>
<span>{landingHeroNarrative.availability}</span>
</motion.div>
</div>
+11 -34
View File
@@ -2,32 +2,19 @@
import { motion, useInView } from "framer-motion";
import { useEffect, useRef, useState } from "react";
import { landingHowItWorksNarrative } from "../lib/product-narrative";
const demoMessages = [
{ agent: "You", role: "system", text: "Build a SaaS landing page with pricing, blog, and auth.", delay: 0 },
{ agent: "CEO", role: "ceo", text: "Analyzing goal... Creating departments: Engineering, Design, Content.", delay: 800 },
{ agent: "CEO", role: "ceo", text: "Hiring 4 specialists for this mission.", delay: 1600 },
{ agent: "Lead Engineer", role: "engineer", text: "Picked up task: Set up Next.js project with auth routes.", delay: 2400 },
{ agent: "UI Designer", role: "designer", text: "Picked up task: Design landing page and pricing components.", delay: 3000 },
{ agent: "Researcher", role: "researcher", text: "Picked up task: Competitive analysis of SaaS pricing models.", delay: 3600 },
{ agent: "Lead Engineer", role: "engineer", text: "Completed: Auth scaffolding with JWT + session middleware.", delay: 4400 },
{ agent: "UI Designer", role: "designer", text: "Completed: Responsive landing page with 3-tier pricing grid.", delay: 5200 },
{ agent: "CEO", role: "ceo", text: "All tasks delivered. 3 documents ready for review.", delay: 6000 },
];
const demoMessages = landingHowItWorksNarrative.demoMessages;
const logTone: Record<(typeof demoMessages)[number]["role"], string> = {
system: "text-[var(--muted-strong)]",
ceo: "text-[#ca8a04]",
engineer: "text-[var(--accent)]",
designer: "text-[#8b5cf6]",
researcher: "text-[#059669]",
};
const steps = [
{ number: 1, title: "Set Your Goal", description: "Describe the company, its mission, and what you want delivered." },
{ number: 2, title: "Agents Self-Organize", description: "A CEO analyzes your goal, hires specialists, and delegates the work." },
{ number: 3, title: "Collect Results", description: "Review deliverables, approve decisions, and download finished work." },
];
const dotTone: Record<(typeof demoMessages)[number]["role"], string> = {
system: "bg-[var(--muted-strong)]",
};
const steps = landingHowItWorksNarrative.steps;
function DemoPanel() {
const anchorRef = useRef<HTMLDivElement | null>(null);
@@ -72,17 +59,7 @@ function DemoPanel() {
>
<span className={`shrink-0 font-semibold inline-flex items-center gap-1.5 ${logTone[message.role]}`}>
<span
className={`w-1.5 h-1.5 rounded-full ${
message.role === "system"
? "bg-[var(--muted-strong)]"
: message.role === "ceo"
? "bg-[#ca8a04]"
: message.role === "engineer"
? "bg-[var(--accent)]"
: message.role === "designer"
? "bg-[#8b5cf6]"
: "bg-[#059669]"
}`}
className={`w-1.5 h-1.5 rounded-full ${dotTone[message.role]}`}
aria-hidden="true"
/>
{message.agent}
@@ -106,7 +83,7 @@ function DemoPanel() {
<path d="m9 12 2 2 4-4" />
<circle cx="12" cy="12" r="10" />
</svg>
<span className="font-semibold">Company run complete. All deliverables ready.</span>
<span className="font-semibold">{landingHowItWorksNarrative.completionText}</span>
</motion.div>
)}
@@ -126,7 +103,7 @@ export function HowItWorks() {
How It Works
</p>
<h2 className="font-serif text-[clamp(2rem,1.5rem+2.5vw,3.25rem)] tracking-[-0.01em] text-[var(--text)]">
Watch a Company Come to Life
{landingHowItWorksNarrative.heading}
</h2>
</div>
@@ -134,7 +111,7 @@ export function HowItWorks() {
<div className="lg:col-span-2 relative">
<div className="absolute left-[15px] top-10 bottom-4 w-px bg-[var(--border)] hidden lg:block" aria-hidden="true" />
<div className="space-y-10">
<div className="space-y-7">
{steps.map((step, index) => (
<motion.div
key={step.number}
+80
View File
@@ -0,0 +1,80 @@
import { releaseVersion } from "../components/release-links.generated";
export const landingHeroNarrative = {
eyebrow: "AgentCompany Desktop",
titleLine1: "Launch an Autonomous AI Company",
titleLine2: "from Your Desktop",
description:
"Run AI CEO by default, switch to AI Co-Founder for human-reviewed decisions, and launch the first mission from a local desktop setup.",
availability: "Available on macOS, Windows, and Linux",
} as const;
export const onboardingFlowNarrative = {
prelude: "Welcome",
sections: [
{
number: 1,
title: "Requirements",
description: "Check Node.js because CLI runtimes call the local API through node -e.",
},
{
number: 2,
title: "Providers",
description: "Connect at least one runtime before company creation continues.",
},
{
number: 3,
title: "Company",
description: "Enter company name, description, and workspace.",
},
{
number: 4,
title: "Team Models",
description: "Enable the models available to the CEO and workers.",
},
{
number: 5,
title: "Mode",
description: "Use AI CEO by default, or choose AI Co-Founder for human-reviewed decisions.",
},
{
number: 6,
title: "Mission",
description: "Capture the first goal and acceptance description.",
},
{
number: 7,
title: "Launch",
description: "Create the company, CEO agent, workspace, kickoff task, and initial run handoff.",
},
],
} as const;
export const landingHowItWorksNarrative = {
heading: "Follow the Desktop First-Run Flow",
steps: onboardingFlowNarrative.sections,
completionText: "Initial run queued. Open Control Center.",
demoMessages: [
{ agent: "Requirements", role: "system", text: "Node.js ready. CLI runtimes can call the local API.", delay: 0 },
{ agent: "Providers", role: "system", text: "Runtime connected.", delay: 700 },
{ agent: "Company", role: "system", text: "Company profile and workspace saved.", delay: 1400 },
{ agent: "Team Models", role: "system", text: "Model pool enabled for CEO and workers.", delay: 2100 },
{ agent: "Mode", role: "system", text: "AI CEO mode selected.", delay: 2800 },
{ agent: "Mission", role: "system", text: "First goal and acceptance description captured.", delay: 3500 },
{ agent: "Launch", role: "system", text: "Company, CEO agent, kickoff task, and initial run created.", delay: 4400 },
],
} as const;
export const landingStructuredDataNarrative = {
softwareVersion: releaseVersion,
featureList: [
"Guided first-run setup",
"Node.js readiness check for CLI runtimes",
"Provider connection and model-pool selection",
"Multi-connector support (Claude Code, Codex CLI, Gemini CLI)",
"AI CEO autonomous operating mode",
"AI Co-Founder human-reviewed operating mode",
"Local-first SQLite storage",
"Cross-platform desktop releases for macOS, Windows, and Linux",
],
};
@@ -0,0 +1,71 @@
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { releaseVersion } from "../../landing/components/release-links.generated";
import {
landingHeroNarrative,
landingHowItWorksNarrative,
landingStructuredDataNarrative,
onboardingFlowNarrative,
} from "../../landing/lib/product-narrative";
describe("landing product narrative", () => {
it("uses the generated release version for software structured data", () => {
expect(releaseVersion).toMatch(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/);
expect(landingStructuredDataNarrative.softwareVersion).toBe(releaseVersion);
});
it("describes the current first-run setup flow", () => {
expect(landingHeroNarrative).toMatchObject({
eyebrow: "AgentCompany Desktop",
titleLine1: "Launch an Autonomous AI Company",
titleLine2: "from Your Desktop",
});
expect(landingHeroNarrative.description).toContain("AI CEO by default");
expect(landingHeroNarrative.description).toContain("AI Co-Founder for human-reviewed decisions");
expect(landingHowItWorksNarrative.steps.map((step) => step.title)).toEqual([
"Requirements",
"Providers",
"Company",
"Team Models",
"Mode",
"Mission",
"Launch",
]);
expect(landingHowItWorksNarrative.demoMessages.map((message) => message.text)).toEqual([
"Node.js ready. CLI runtimes can call the local API.",
"Runtime connected.",
"Company profile and workspace saved.",
"Model pool enabled for CEO and workers.",
"AI CEO mode selected.",
"First goal and acceptance description captured.",
"Company, CEO agent, kickoff task, and initial run created.",
]);
expect(landingStructuredDataNarrative.featureList).toContain(
"Multi-connector support (Claude Code, Codex CLI, Gemini CLI)",
);
});
it("keeps first-run documentation ordered by the same setup sections", () => {
const files = [
"README.md",
"docs/rebuild/05-design-system-and-flows.md",
"landing/README.md",
];
for (const file of files) {
const text = readFileSync(file, "utf8");
const positions = [];
let searchOffset = 0;
for (const step of onboardingFlowNarrative.sections) {
const position = text.indexOf(step.title, searchOffset);
positions.push(position);
searchOffset = position + step.title.length;
}
expect(positions.every((position) => position >= 0)).toBe(true);
expect(positions).toEqual([...positions].sort((left, right) => left - right));
}
});
});