feat(web): import oh-my-openagent-web Next.js + Cloudflare Workers site
Imports the public marketing site previously living in ../oh-my-opencode-web. Independent of the npm plugin: own package.json, bun.lock, tsconfig.json. Not included in the published package — root files: array still only ships dist/, bin/, postinstall.mjs. Stack: - Next.js 15.5 App Router + RSC, deployed to Cloudflare Workers via @opennextjs/cloudflare (build target .open-next/worker.js). - Tailwind v4 + shadcn/ui primitives. - next-intl with 4 locales (en/ja/ko/zh) under app/[locale]/. - Playwright e2e tests under web/e2e/. - Custom domains ohmyopenagent.com (primary) and ohmyopencode.org (legacy alias) declared in web/wrangler.toml. Source files were re-formatted via `bun run format` to bring them in line with the existing .prettierrc (singleQuote: false). Functional code unchanged.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import type { Metadata } from "next"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Documentation",
|
||||
description:
|
||||
"Configuration reference for Oh My OpenAgent. Agents, categories, skills, hooks, MCPs, and more.",
|
||||
}
|
||||
|
||||
export default function DocsLayout({ children }: { children: React.ReactNode }) {
|
||||
return children
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
import { getTranslations } from "next-intl/server"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion"
|
||||
import { CodeBlock } from "@/components/ui/code-block"
|
||||
import { OptionTable } from "@/components/ui/option-table"
|
||||
import { DocsShell } from "@/components/docs/docs-shell"
|
||||
import { DOC_SECTION_IDS, DOC_SECTION_TITLE_KEYS } from "@/lib/docs-sections"
|
||||
|
||||
export default async function DocsPage() {
|
||||
const t = await getTranslations("docs")
|
||||
const sections = DOC_SECTION_IDS.map((id) => ({
|
||||
id,
|
||||
title: t(`sections.${DOC_SECTION_TITLE_KEYS[id]}`),
|
||||
}))
|
||||
|
||||
return (
|
||||
<DocsShell
|
||||
mobileHeader={t("mobileHeader")}
|
||||
searchPlaceholder={t("searchPlaceholder")}
|
||||
sections={sections}
|
||||
>
|
||||
<section id="overview" className="scroll-mt-24 space-y-4">
|
||||
<h1 className="text-4xl font-bold tracking-tight">{t("overview.title")}</h1>
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{t("overview.description", { command: "bunx oh-my-openagent install" })}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="quick-start" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("quickStart.title")}</h2>
|
||||
<CodeBlock
|
||||
code={`{
|
||||
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/master/assets/oh-my-openagent.schema.json",
|
||||
"agents": {
|
||||
"oracle": { "model": "openai/gpt-5.4", "variant": "high" },
|
||||
"explore": { "model": "github-copilot/grok-code-fast-1" }
|
||||
},
|
||||
"categories": {
|
||||
"quick": { "model": "opencode/gpt-5-nano" },
|
||||
"visual-engineering": { "model": "google/gemini-3.1-pro" }
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="config-locations" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("configLocations.title")}</h2>
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<ul className="text-muted-foreground list-disc space-y-2 pl-5">
|
||||
<li>
|
||||
<code className="text-foreground font-mono">.opencode/oh-my-openagent.json</code>{" "}
|
||||
{t("configLocations.projectLevel")}
|
||||
</li>
|
||||
<li>
|
||||
<code className="text-foreground font-mono">
|
||||
~/.config/opencode/oh-my-openagent.json
|
||||
</code>{" "}
|
||||
{t("configLocations.userLevel")}
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-muted-foreground mt-4 text-sm">{t("configLocations.jsonc")}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
<section id="agents" className="scroll-mt-24 space-y-6">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("agentsSection.title")}</h2>
|
||||
<p className="text-muted-foreground">{t("agentsSection.description")}</p>
|
||||
|
||||
<h3 className="text-xl font-semibold">{t("agentsSection.overrideOptions")}</h3>
|
||||
<OptionTable
|
||||
options={[
|
||||
{ name: "model", type: "string", description: t("agentsSection.options.model") },
|
||||
{ name: "variant", type: "string", description: t("agentsSection.options.variant") },
|
||||
{ name: "category", type: "string", description: t("agentsSection.options.category") },
|
||||
{
|
||||
name: "temperature",
|
||||
type: "number",
|
||||
description: t("agentsSection.options.temperature"),
|
||||
},
|
||||
{ name: "top_p", type: "number", description: t("agentsSection.options.topP") },
|
||||
{ name: "prompt", type: "string", description: t("agentsSection.options.prompt") },
|
||||
{
|
||||
name: "prompt_append",
|
||||
type: "string",
|
||||
description: t("agentsSection.options.promptAppend"),
|
||||
},
|
||||
{ name: "tools", type: "Record", description: t("agentsSection.options.tools") },
|
||||
{
|
||||
name: "disable",
|
||||
type: "boolean",
|
||||
default: "false",
|
||||
description: t("agentsSection.options.disable"),
|
||||
},
|
||||
{
|
||||
name: "maxTokens",
|
||||
type: "number",
|
||||
description: t("agentsSection.options.maxTokens"),
|
||||
},
|
||||
{ name: "thinking", type: "object", description: t("agentsSection.options.thinking") },
|
||||
{
|
||||
name: "reasoningEffort",
|
||||
type: "string",
|
||||
description: t("agentsSection.options.reasoningEffort"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<h3 className="text-xl font-semibold">{t("agentsSection.permissions")}</h3>
|
||||
<div className="border-border overflow-hidden rounded-lg border">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-muted/50 text-left font-medium">
|
||||
<tr>
|
||||
<th className="p-3">Permission</th>
|
||||
<th className="p-3">Values</th>
|
||||
<th className="p-3">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-border divide-y">
|
||||
{[
|
||||
{ name: "edit", descKey: "edit" as const },
|
||||
{ name: "bash", descKey: "bash" as const },
|
||||
{ name: "webfetch", descKey: "webfetch" as const },
|
||||
{ name: "doom_loop", descKey: "doomLoop" as const },
|
||||
{ name: "external_directory", descKey: "externalDirectory" as const },
|
||||
].map((p) => (
|
||||
<tr key={p.name}>
|
||||
<td className="p-3 font-mono font-medium">{p.name}</td>
|
||||
<td className="text-muted-foreground p-3 font-mono">
|
||||
{t("agentsSection.permissionValues")}
|
||||
</td>
|
||||
<td className="text-muted-foreground p-3">
|
||||
{t(`agentsSection.permissionDescriptions.${p.descKey}`)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="categories" className="scroll-mt-24 space-y-6">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("categoriesSection.title")}</h2>
|
||||
<p className="text-muted-foreground">{t("categoriesSection.description")}</p>
|
||||
|
||||
<div className="border-border overflow-hidden rounded-lg border">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-muted/50 text-left font-medium">
|
||||
<tr>
|
||||
<th className="p-3">Category</th>
|
||||
<th className="p-3">Default Model</th>
|
||||
<th className="p-3">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-border divide-y">
|
||||
{[
|
||||
{
|
||||
name: "visual-engineering",
|
||||
model: "gemini-3.1-pro (high)",
|
||||
descKey: "visualEngineering" as const,
|
||||
},
|
||||
{
|
||||
name: "ultrabrain",
|
||||
model: "gpt-5.3-codex (xhigh)",
|
||||
descKey: "ultrabrain" as const,
|
||||
},
|
||||
{ name: "deep", model: "gpt-5.3-codex (medium)", descKey: "deep" as const },
|
||||
{ name: "artistry", model: "gemini-3.1-pro (high)", descKey: "artistry" as const },
|
||||
{ name: "quick", model: "claude-haiku-4-5", descKey: "quick" as const },
|
||||
{
|
||||
name: "unspecified-low",
|
||||
model: "claude-sonnet-4-6",
|
||||
descKey: "unspecifiedLow" as const,
|
||||
},
|
||||
{
|
||||
name: "unspecified-high",
|
||||
model: "gpt-5.4 (high)",
|
||||
descKey: "unspecifiedHigh" as const,
|
||||
},
|
||||
{ name: "writing", model: "gemini-3-flash", descKey: "writing" as const },
|
||||
].map((c) => (
|
||||
<tr key={c.name}>
|
||||
<td className="text-primary p-3 font-mono font-medium">{c.name}</td>
|
||||
<td className="text-muted-foreground p-3 font-mono">{c.model}</td>
|
||||
<td className="text-muted-foreground p-3">
|
||||
{t(`categoriesSection.categories.${c.descKey}`)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("categoriesSection.availableOptions", {
|
||||
options:
|
||||
"model, variant, temperature, top_p, maxTokens, thinking, reasoningEffort, textVerbosity, tools, prompt_append, is_unstable_agent",
|
||||
})}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="skills" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("skillsSection.title")}</h2>
|
||||
<p className="text-muted-foreground">
|
||||
{t("skillsSection.description", {
|
||||
playwright: "playwright",
|
||||
agentBrowser: "agent-browser",
|
||||
gitMaster: "git-master",
|
||||
})}
|
||||
</p>
|
||||
<CodeBlock
|
||||
code={`"skills": {
|
||||
"my-custom-skill": {
|
||||
"description": "A custom skill for specific tasks",
|
||||
"instructions": "Always use this skill when..."
|
||||
}
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="background-tasks" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
{t("backgroundTasksSection.title")}
|
||||
</h2>
|
||||
<OptionTable
|
||||
options={[
|
||||
{
|
||||
name: "defaultConcurrency",
|
||||
type: "number",
|
||||
description: t("backgroundTasksSection.options.defaultConcurrency"),
|
||||
},
|
||||
{
|
||||
name: "staleTimeoutMs",
|
||||
type: "number",
|
||||
description: t("backgroundTasksSection.options.staleTimeoutMs"),
|
||||
},
|
||||
{
|
||||
name: "providerConcurrency",
|
||||
type: "number",
|
||||
description: t("backgroundTasksSection.options.providerConcurrency"),
|
||||
},
|
||||
{
|
||||
name: "modelConcurrency",
|
||||
type: "number",
|
||||
description: t("backgroundTasksSection.options.modelConcurrency"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div className="text-muted-foreground flex items-center gap-2 text-sm">
|
||||
<span className="font-semibold">{t("backgroundTasksSection.priority")}</span>
|
||||
<Badge variant="secondary">modelConcurrency</Badge> >
|
||||
<Badge variant="secondary">providerConcurrency</Badge> >
|
||||
<Badge variant="secondary">defaultConcurrency</Badge>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="hooks" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("hooksSection.title")}</h2>
|
||||
<p className="text-muted-foreground mb-4">{t("hooksSection.description")}</p>
|
||||
<div className="grid grid-cols-1 gap-2 md:grid-cols-2 lg:grid-cols-3">
|
||||
{[
|
||||
"agent-usage-reminder",
|
||||
"anthropic-context-window-limit-recovery",
|
||||
"anthropic-effort",
|
||||
"atlas",
|
||||
"auto-slash-command",
|
||||
"auto-update-checker",
|
||||
"background-notification",
|
||||
"category-skill-reminder",
|
||||
"claude-code-hooks",
|
||||
"comment-checker",
|
||||
"compaction-context-injector",
|
||||
"compaction-todo-preserver",
|
||||
"delegate-task-retry",
|
||||
"directory-agents-injector",
|
||||
"directory-readme-injector",
|
||||
"edit-error-recovery",
|
||||
"interactive-bash-session",
|
||||
"keyword-detector",
|
||||
"non-interactive-env",
|
||||
"prometheus-md-only",
|
||||
"question-label-truncator",
|
||||
"ralph-loop",
|
||||
"rules-injector",
|
||||
"session-recovery",
|
||||
"sisyphus-junior-notepad",
|
||||
"start-work",
|
||||
"stop-continuation-guard",
|
||||
"subagent-question-blocker",
|
||||
"task-reminder",
|
||||
"task-resume-info",
|
||||
"tasks-todowrite-disabler",
|
||||
"think-mode",
|
||||
"thinking-block-validator",
|
||||
"unstable-agent-babysitter",
|
||||
"write-existing-file-guard",
|
||||
].map((hook) => (
|
||||
<div
|
||||
key={hook}
|
||||
className="border-border bg-card text-muted-foreground rounded border p-2 font-mono text-xs transition-colors"
|
||||
>
|
||||
{hook}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="mcps" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("mcpsSection.title")}</h2>
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">{t("mcpsSection.websearch.title")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("mcpsSection.websearch.description")}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">{t("mcpsSection.context7.title")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("mcpsSection.context7.description")}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">{t("mcpsSection.grepApp.title")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("mcpsSection.grepApp.description")}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="browser-automation" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
{t("browserAutomationSection.title")}
|
||||
</h2>
|
||||
<div className="border-border overflow-hidden rounded-lg border">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-muted/50 text-left font-medium">
|
||||
<tr>
|
||||
<th className="p-3">Tool</th>
|
||||
<th className="p-3">Description</th>
|
||||
<th className="p-3">Use Case</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-border divide-y">
|
||||
<tr>
|
||||
<td className="text-primary p-3 font-mono font-medium">
|
||||
{t("browserAutomationSection.playwright.tool")}
|
||||
</td>
|
||||
<td className="text-muted-foreground p-3">
|
||||
{t("browserAutomationSection.playwright.description")}
|
||||
</td>
|
||||
<td className="text-muted-foreground p-3">
|
||||
{t("browserAutomationSection.playwright.useCase")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="text-primary p-3 font-mono font-medium">
|
||||
{t("browserAutomationSection.agentBrowser.tool")}
|
||||
</td>
|
||||
<td className="text-muted-foreground p-3">
|
||||
{t("browserAutomationSection.agentBrowser.description")}
|
||||
</td>
|
||||
<td className="text-muted-foreground p-3">
|
||||
{t("browserAutomationSection.agentBrowser.useCase")}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="tmux" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("tmuxSection.title")}</h2>
|
||||
<OptionTable
|
||||
options={[
|
||||
{ name: "enabled", type: "boolean", description: t("tmuxSection.options.enabled") },
|
||||
{ name: "layout", type: "string", description: t("tmuxSection.options.layout") },
|
||||
{
|
||||
name: "main_pane_size",
|
||||
type: "string",
|
||||
description: t("tmuxSection.options.mainPaneSize"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="git-master" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("gitMasterSection.title")}</h2>
|
||||
<OptionTable
|
||||
options={[
|
||||
{
|
||||
name: "commit_footer",
|
||||
type: "string",
|
||||
description: t("gitMasterSection.options.commitFooter"),
|
||||
},
|
||||
{
|
||||
name: "include_co_authored_by",
|
||||
type: "boolean",
|
||||
description: t("gitMasterSection.options.includeCoAuthoredBy"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="comment-checker" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
{t("commentCheckerSection.title")}
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
{t("commentCheckerSection.description", { placeholder: "{{comments}}" })}
|
||||
</p>
|
||||
<CodeBlock
|
||||
code={`"comment-checker": {
|
||||
"custom_prompt": "Review these comments: {{comments}}"
|
||||
}`}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="experimental" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("experimentalSection.title")}</h2>
|
||||
<OptionTable
|
||||
options={[
|
||||
{
|
||||
name: "aggressive_truncation",
|
||||
type: "boolean",
|
||||
description: t("experimentalSection.options.aggressiveTruncation"),
|
||||
},
|
||||
{
|
||||
name: "auto_resume",
|
||||
type: "boolean",
|
||||
description: t("experimentalSection.options.autoResume"),
|
||||
},
|
||||
{
|
||||
name: "preemptive_compaction",
|
||||
type: "boolean",
|
||||
description: t("experimentalSection.options.preemptiveCompaction"),
|
||||
},
|
||||
{
|
||||
name: "truncate_all_tool_outputs",
|
||||
type: "boolean",
|
||||
description: t("experimentalSection.options.truncateAllToolOutputs"),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="dynamic-pruning">
|
||||
<AccordionTrigger>{t("experimentalSection.dynamicPruning.trigger")}</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p className="text-muted-foreground mb-2 text-sm">
|
||||
{t("experimentalSection.dynamicPruning.description")}
|
||||
</p>
|
||||
<CodeBlock
|
||||
code={`"dynamic_context_pruning": {
|
||||
"enabled": true,
|
||||
"strategy": "smart",
|
||||
"max_tokens": 10000
|
||||
}`}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</section>
|
||||
|
||||
<section id="lsp" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("lspSection.title")}</h2>
|
||||
<OptionTable
|
||||
options={[
|
||||
{ name: "command", type: "string", description: t("lspSection.options.command") },
|
||||
{ name: "extensions", type: "array", description: t("lspSection.options.extensions") },
|
||||
{ name: "priority", type: "number", description: t("lspSection.options.priority") },
|
||||
{ name: "env", type: "object", description: t("lspSection.options.env") },
|
||||
{
|
||||
name: "initialization",
|
||||
type: "object",
|
||||
description: t("lspSection.options.initialization"),
|
||||
},
|
||||
{ name: "disabled", type: "boolean", description: t("lspSection.options.disabled") },
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section id="env-vars" className="scroll-mt-24 space-y-4">
|
||||
<h2 className="text-2xl font-semibold tracking-tight">{t("envVarsSection.title")}</h2>
|
||||
<div className="border-border rounded-lg border p-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h3 className="text-primary font-mono font-bold">
|
||||
{t("envVarsSection.opencodeConfigDir.name")}
|
||||
</h3>
|
||||
<p className="text-muted-foreground mt-1 text-sm">
|
||||
{t("envVarsSection.opencodeConfigDir.description")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Separator className="my-12" />
|
||||
|
||||
<footer className="text-muted-foreground text-sm">
|
||||
<p>{t("footer", { year: new Date().getFullYear().toString() })}</p>
|
||||
</footer>
|
||||
</DocsShell>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { Metadata } from "next"
|
||||
import type { JSX } from "react"
|
||||
import { notFound } from "next/navigation"
|
||||
import { hasLocale } from "next-intl"
|
||||
import { setRequestLocale } from "next-intl/server"
|
||||
import { LocalizedPageShell } from "@/app/_components/localized-page-shell"
|
||||
import { routing } from "@/i18n/routing"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
description:
|
||||
"Meet Sisyphus: The batteries-included agent that codes like you. Multi-model orchestration, background agents, 40+ lifecycle hooks.",
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return routing.locales.map((locale) => ({ locale }))
|
||||
}
|
||||
|
||||
export default async function LocaleLayout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
params: Promise<{ locale: string }>
|
||||
}): Promise<JSX.Element> {
|
||||
const { locale } = await params
|
||||
|
||||
if (!hasLocale(routing.locales, locale)) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
setRequestLocale(locale)
|
||||
|
||||
return <LocalizedPageShell locale={locale}>{children}</LocalizedPageShell>
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { Metadata } from "next"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Ultrawork Manifesto",
|
||||
description:
|
||||
"The philosophy of high-output engineering. Why human developers should be architects, not spell-checkers.",
|
||||
}
|
||||
|
||||
export default function ManifestoLayout({ children }: { children: React.ReactNode }) {
|
||||
return children
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
import { getTranslations } from "next-intl/server"
|
||||
import Image from "next/image"
|
||||
import { ArrowRight, Check, Terminal, Zap } from "lucide-react"
|
||||
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Section } from "@/components/ui/section"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { Link } from "@/i18n/routing"
|
||||
|
||||
async function ManifestoPage() {
|
||||
const t = await getTranslations("manifesto")
|
||||
|
||||
const painPointKeys = ["fixing", "syntax", "copyPasting", "reviewing"] as const
|
||||
const indistinguishableKeys = [
|
||||
"patterns",
|
||||
"errorHandling",
|
||||
"tests",
|
||||
"noSlop",
|
||||
"comments",
|
||||
] as const
|
||||
const ultraworkStepKeys = ["analyze", "breakdown", "execute", "verify", "commit"] as const
|
||||
|
||||
const coreLoopKeys = [
|
||||
"prometheus",
|
||||
"metis",
|
||||
"momus",
|
||||
"orchestrator",
|
||||
"todoContinuation",
|
||||
"categorySystem",
|
||||
"backgroundAgents",
|
||||
"wisdomAccumulation",
|
||||
] as const
|
||||
|
||||
const futureKeys = ["focus", "quality", "complexity", "promptEngineering"] as const
|
||||
|
||||
return (
|
||||
<main className="bg-background text-foreground min-h-screen overflow-x-hidden">
|
||||
<section className="relative flex min-h-[80vh] flex-col items-center justify-center overflow-hidden px-6 pt-20 text-center">
|
||||
<div className="absolute inset-0 z-0 opacity-20">
|
||||
<Image
|
||||
src="/images/core-loop.png"
|
||||
alt="Background"
|
||||
fill
|
||||
className="object-cover object-center"
|
||||
priority
|
||||
/>
|
||||
<div className="from-background/80 via-background/90 to-background absolute inset-0 bg-gradient-to-b" />
|
||||
</div>
|
||||
|
||||
<div className="animate-in fade-in slide-in-from-bottom-2 relative z-10 mx-auto max-w-4xl space-y-6 duration-500">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-primary/50 text-primary mb-4 px-4 py-1 text-sm"
|
||||
>
|
||||
{t("badge")}
|
||||
</Badge>
|
||||
<h1 className="from-foreground to-foreground/60 bg-gradient-to-b bg-clip-text text-5xl font-bold tracking-tight text-transparent md:text-7xl">
|
||||
{t("hero.title")}
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-xl font-light tracking-wide md:text-2xl">
|
||||
{t("hero.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Section className="mx-auto max-w-3xl">
|
||||
<div className="space-y-12">
|
||||
<div className="text-primary/90 border-primary/20 bg-primary/5 border-y py-8 text-center font-mono text-lg md:text-xl">
|
||||
{t("bottleneck")}
|
||||
</div>
|
||||
|
||||
<div className="prose prose-invert prose-lg max-w-none">
|
||||
<p>{t("autonomousCar")}</p>
|
||||
|
||||
<h2 className="mt-8 mb-4 text-2xl font-bold">{t("whyDifferent")}</h2>
|
||||
|
||||
<p>{t("micromanagement")}</p>
|
||||
|
||||
<ul className="my-6 list-none space-y-4 pl-0">
|
||||
{painPointKeys.map((key) => (
|
||||
<li key={key} className="flex items-start gap-3">
|
||||
<span className="mt-1 text-red-500">✕</span>
|
||||
<span>{t(`painPoints.${key}`)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<p className="my-8 border-l-4 border-red-500 bg-red-500/5 py-2 pl-6 text-xl font-semibold">
|
||||
{t("notCollaboration")}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<Link href="/" className="text-primary underline-offset-4 hover:underline">
|
||||
{t("premiseLinkText")}
|
||||
</Link>{" "}
|
||||
{t("premise", { linkText: "" })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Separator className="mx-auto max-w-4xl opacity-20" />
|
||||
|
||||
<Section className="mx-auto max-w-3xl">
|
||||
<h2 className="mb-8 text-3xl font-bold md:text-4xl">{t("indistinguishable.title")}</h2>
|
||||
|
||||
<p className="text-muted-foreground mb-8 text-xl">{t("indistinguishable.subtitle")}</p>
|
||||
|
||||
<div className="mb-10 grid gap-6">
|
||||
{indistinguishableKeys.map((key) => (
|
||||
<div
|
||||
key={key}
|
||||
className="bg-secondary/30 border-border/50 flex items-start gap-4 rounded-lg border p-4"
|
||||
>
|
||||
<Check className="h-6 w-6 shrink-0 text-green-500" />
|
||||
<span>{t(`indistinguishable.items.${key}`)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<blockquote className="border-primary bg-primary/5 rounded-r-lg border-l-4 py-4 pl-6 text-2xl font-light italic">
|
||||
{t("indistinguishable.quote")}
|
||||
</blockquote>
|
||||
</Section>
|
||||
|
||||
<Section className="mx-auto max-w-4xl">
|
||||
<div className="grid items-center gap-12 md:grid-cols-2">
|
||||
<div>
|
||||
<h2 className="mb-6 text-3xl font-bold md:text-4xl">{t("tokenCost.title")}</h2>
|
||||
<p className="text-muted-foreground mb-6 text-lg">{t("tokenCost.description")}</p>
|
||||
<ul className="mb-8 space-y-3">
|
||||
<li className="flex items-center gap-2">
|
||||
<Zap className="h-5 w-5 text-yellow-500" />
|
||||
<span>{t("tokenCost.parallelAgents")}</span>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<Zap className="h-5 w-5 text-yellow-500" />
|
||||
<span>{t("tokenCost.completeWork")}</span>
|
||||
</li>
|
||||
<li className="flex items-center gap-2">
|
||||
<Zap className="h-5 w-5 text-yellow-500" />
|
||||
<span>{t("tokenCost.selfVerification")}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="bg-secondary/20 border-border/50 rounded-xl border p-8">
|
||||
<h3 className="text-primary mb-4 text-xl font-semibold">{t("tokenCost.however")}</h3>
|
||||
<p className="text-muted-foreground mb-4">{t("tokenCost.optimizeDescription")}</p>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li className="text-muted-foreground flex items-center gap-2">
|
||||
<div className="bg-primary h-1.5 w-1.5 rounded-full" />
|
||||
{t("tokenCost.cheaperModels")}
|
||||
</li>
|
||||
<li className="text-muted-foreground flex items-center gap-2">
|
||||
<div className="bg-primary h-1.5 w-1.5 rounded-full" />
|
||||
{t("tokenCost.avoidingRedundant")}
|
||||
</li>
|
||||
<li className="text-muted-foreground flex items-center gap-2">
|
||||
<div className="bg-primary h-1.5 w-1.5 rounded-full" />
|
||||
{t("tokenCost.intelligentCaching")}
|
||||
</li>
|
||||
<li className="text-muted-foreground flex items-center gap-2">
|
||||
<div className="bg-primary h-1.5 w-1.5 rounded-full" />
|
||||
{t("tokenCost.stoppingExactly")}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section className="mx-auto max-w-5xl">
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="mb-4 text-3xl font-bold md:text-4xl">{t("cognitiveLoad.title")}</h2>
|
||||
<p className="text-muted-foreground mx-auto max-w-2xl text-xl">
|
||||
{t("cognitiveLoad.subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:grid-cols-2">
|
||||
<Card className="from-background to-primary/5 border-primary/20 relative overflow-hidden bg-gradient-to-br">
|
||||
<div className="absolute top-0 right-0 p-4 opacity-10">
|
||||
<Terminal className="h-24 w-24" />
|
||||
</div>
|
||||
<CardHeader>
|
||||
<Badge className="mb-2 w-fit">{t("cognitiveLoad.ultrawork.badge")}</Badge>
|
||||
<CardTitle className="text-2xl">{t("cognitiveLoad.ultrawork.title")}</CardTitle>
|
||||
<p className="text-muted-foreground">{t("cognitiveLoad.ultrawork.subtitle")}</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="border-primary/20 relative ml-2 space-y-6 border-l pl-4">
|
||||
{ultraworkStepKeys.map((key) => (
|
||||
<div key={key} className="relative">
|
||||
<div className="bg-primary border-background absolute top-1.5 -left-[21px] h-3 w-3 rounded-full border-2" />
|
||||
<p className="text-sm">{t(`cognitiveLoad.ultrawork.steps.${key}`)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="border-border/50 text-primary mt-8 border-t pt-6 text-center font-bold">
|
||||
{t("cognitiveLoad.ultrawork.footer")}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-secondary/10 border-border/50">
|
||||
<CardHeader>
|
||||
<Badge variant="secondary" className="mb-2 w-fit">
|
||||
{t("cognitiveLoad.prometheus.badge")}
|
||||
</Badge>
|
||||
<CardTitle className="text-2xl">{t("cognitiveLoad.prometheus.title")}</CardTitle>
|
||||
<p className="text-muted-foreground">{t("cognitiveLoad.prometheus.subtitle")}</p>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div className="bg-background/50 border-border/50 rounded-lg border p-4">
|
||||
<h3 className="text-primary mb-1 font-semibold">
|
||||
{t("cognitiveLoad.prometheus.prometheusTitle")}
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("cognitiveLoad.prometheus.prometheusDescription")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<ArrowRight className="text-muted-foreground/50 rotate-90 md:rotate-0" />
|
||||
</div>
|
||||
<div className="bg-background/50 border-border/50 rounded-lg border p-4">
|
||||
<h3 className="text-primary mb-1 font-semibold">
|
||||
{t("cognitiveLoad.prometheus.atlasTitle")}
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("cognitiveLoad.prometheus.atlasDescription")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-border/50 text-muted-foreground mt-4 border-t pt-6 text-center font-bold">
|
||||
{t("cognitiveLoad.prometheus.footer")}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section className="mx-auto max-w-6xl">
|
||||
<div className="grid gap-8 md:grid-cols-3">
|
||||
{(["predictable", "continuous", "delegatable"] as const).map((key) => (
|
||||
<div
|
||||
key={key}
|
||||
className="bg-secondary/10 border-border/30 rounded-xl border p-6 text-center transition-colors"
|
||||
>
|
||||
<div className="mb-4 flex justify-center">
|
||||
<Image
|
||||
src={`/images/${key}.png`}
|
||||
alt={key}
|
||||
width={64}
|
||||
height={64}
|
||||
className="rounded-lg"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="mb-3 text-xl font-bold">{t(`principles.${key}.title`)}</h3>
|
||||
<p className="text-muted-foreground">{t(`principles.${key}.description`)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Separator className="mx-auto max-w-4xl opacity-20" />
|
||||
|
||||
<Section className="mx-auto max-w-5xl">
|
||||
<h2 className="mb-12 text-center text-3xl font-bold md:text-4xl">{t("coreLoop.title")}</h2>
|
||||
|
||||
<div className="bg-background border-border/50 mb-16 rounded-xl border p-6 shadow-lg">
|
||||
<div className="flex flex-wrap items-center justify-center gap-4 py-8 md:gap-8">
|
||||
<div className="rounded-lg border-2 border-white bg-black px-6 py-3 text-sm font-semibold text-white md:text-base">
|
||||
Human Intent
|
||||
</div>
|
||||
<svg
|
||||
className="text-muted-foreground h-8 w-8 shrink-0"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7" />
|
||||
</svg>
|
||||
<div className="rounded-lg border-2 border-zinc-600 bg-zinc-900 px-6 py-3 text-sm font-semibold text-white md:text-base">
|
||||
Agent Execution
|
||||
</div>
|
||||
<svg
|
||||
className="text-muted-foreground h-8 w-8 shrink-0"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M5 12h14M12 5l7 7-7 7" />
|
||||
</svg>
|
||||
<div className="rounded-lg border-2 border-cyan-500 bg-black px-6 py-3 text-sm font-semibold text-cyan-400 md:text-base">
|
||||
Verified Result
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-muted-foreground mt-2 text-center text-xs">↻ Minimum Intervention</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
{coreLoopKeys.map((key) => (
|
||||
<Card key={key} className="bg-secondary/5 border-border/40 transition-colors">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-primary text-lg">
|
||||
{t(`coreLoop.features.${key}.feature`)}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t(`coreLoop.features.${key}.purpose`)}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section className="mx-auto max-w-3xl text-center">
|
||||
<h2 className="mb-8 text-3xl font-bold md:text-4xl">{t("future.title")}</h2>
|
||||
|
||||
<div className="mx-auto mb-12 max-w-2xl space-y-4 text-left">
|
||||
{futureKeys.map((key) => (
|
||||
<div key={key} className="flex items-center gap-3">
|
||||
<div className="bg-primary h-2 w-2 shrink-0 rounded-full" />
|
||||
<span className="text-lg">{t(`future.items.${key}`)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<p className="text-2xl font-light">{t("future.quote1")}</p>
|
||||
<p className="text-primary text-3xl font-bold">{t("future.quote2")}</p>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<section className="from-primary/10 to-background bg-gradient-to-t px-6 py-32 text-center">
|
||||
<div className="space-y-8">
|
||||
<h2 className="text-foreground text-6xl font-black tracking-tighter md:text-8xl">
|
||||
{t("finalCta.title")}
|
||||
</h2>
|
||||
|
||||
<Button size="lg" className="rounded-full px-8 py-6 text-lg" asChild>
|
||||
<Link href="https://github.com/code-yeongyu/oh-my-openagent" target="_blank">
|
||||
{t("finalCta.button")} <ArrowRight className="ml-2 h-5 w-5" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
export default ManifestoPage
|
||||
@@ -0,0 +1,17 @@
|
||||
export { landingMetadata as metadata } from "@/app/_components/landing-page"
|
||||
|
||||
import type { JSX } from "react"
|
||||
import { setRequestLocale } from "next-intl/server"
|
||||
import { LandingPage } from "@/app/_components/landing-page"
|
||||
|
||||
export default async function LocaleLandingPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>
|
||||
}): Promise<JSX.Element> {
|
||||
const { locale } = await params
|
||||
|
||||
setRequestLocale(locale)
|
||||
|
||||
return <LandingPage />
|
||||
}
|
||||
Reference in New Issue
Block a user