fix(landing): sync first-run narrative
This commit is contained in:
1 parent
f8ed2aa666
commit
e3eb45b08d
8 files changed
+526
-79
No files matched your search
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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
@@ -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 = {
|
||||
|
||||
@@ -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's mission. A CEO agent recruits the team, delegates work, and delivers real
|
||||
results — code, content, research, and more. You'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>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user