| 1 | import Link from "next/link"; |
| 2 | import { getCachedRoadmap, type RoadmapItem } from "@/lib/roadmap-feed"; |
| 3 | import { getEnv } from "@/lib/kv"; |
| 4 | import { buildPageMetadata } from "@/lib/page-meta"; |
| 5 | |
| 6 | export const revalidate = 1800; |
| 7 | |
| 8 | export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) { |
| 9 | const { locale } = await params; |
| 10 | const isZh = locale === "zh"; |
| 11 | return buildPageMetadata({ |
| 12 | path: "/roadmap", |
| 13 | locale, |
| 14 | title: isZh ? "路线图 · Codewhale" : "Roadmap · Codewhale", |
| 15 | description: isZh |
| 16 | ? "Codewhale 已完成、进行中、考虑中和明确不在范围内的工作。" |
| 17 | : "Current Codewhale work grouped by shipped, underway, considered, and deliberately out-of-scope directions.", |
| 18 | }); |
| 19 | } |
| 20 | |
| 21 | const tracksEn = [ |
| 22 | { |
| 23 | title: "Shipped", |
| 24 | items: [ |
| 25 | { title: "Canonical action tools", note: "Bash, File, Git, and Run; agent, remember, tasks, update_plan, work_update, and the synthetic tool_search complete the ten-name default-active policy. remember is present when built-in memory is enabled; Web is deferred and conditional." }, |
| 26 | { title: "Sub-agent parallel execution", note: "agent; 64 concurrent sessions by default, configurable to 128, with bounded result handles" }, |
| 27 | { title: "RLM batched processing", note: "Persistent sandboxed Python REPL with 1–16 cheap parallel children for long-input analysis" }, |
| 28 | { title: "Three operating modes", note: "Plan (read-only), Act (execution), Operate (Fleet/Workflow orchestration); orthogonal Ask / Auto-Review / Full Access posture" }, |
| 29 | { title: "OS command sandbox", note: "Seatbelt on macOS when available; opt-in bubblewrap on Linux when installed. Windows currently reports no OS sandbox." }, |
| 30 | { title: "Durable sessions + tasks", note: "Save, resume, rollback; background task queue with replayable timelines under ~/.codewhale/tasks/" }, |
| 31 | { title: "Bidirectional MCP", note: "Consume tools from external servers; expose as server via `codewhale mcp`; ~/.codewhale/mcp.json" }, |
| 32 | { title: "Skills + unified slash palette", note: "~/.codewhale/skills/ auto-loading; /help, /mode, /status, /config, /trust, /feedback" }, |
| 33 | { title: "OpenRouter provider", note: "OpenRouter integration with 300+ models across dozens of providers" }, |
| 34 | { title: "OpenAI-compatible & local runtimes", note: "Generic `openai` route for any OpenAI-compatible gateway, plus vLLM, SGLang, and Ollama against your own localhost endpoints — no key required" }, |
| 35 | { title: "Multi-provider support", note: "Hot-swap between providers (DeepSeek, OpenAI, Anthropic, OpenRouter) per session" }, |
| 36 | { title: "Local web client", note: "Implemented in the v0.9.1 source candidate: `codewhale web` is a loopback-only browser client over the Runtime API behind a one-time bootstrap session boundary; approvals and user input recover across page reloads (#4423)" }, |
| 37 | { title: "Opt-in product telemetry", note: "Off by default, and unanswered until a first-run notice asks — nothing is collected, and the telemetry directory is not even created, until you answer Enable. An enabled session posts counts and closed enums to the first-party endpoint https://telemetry.codewhale.net/v1/telemetry, a Cloudflare Worker whose full source is in the repo under telemetry-ingest/. Its storage has no IP, country, or geo column — that is structural, not a setting — nothing is logged, and Cloudflare's retention is a fixed three months. Set telemetry_endpoint = \"\" to stay enabled and contact nobody: batches then go to $CODEWHALE_HOME/telemetry/dryrun.jsonl and you can read exactly what would have been sent. Never prompts, completions, tool arguments, file contents, paths, repo or branch names, model ids, or credentials; the full schema is docs/TELEMETRY.md. Turn it off with `codewhale config set telemetry false` or CODEWHALE_TELEMETRY=0, which also deletes the random install id and every buffered event." }, |
| 38 | ], |
| 39 | }, |
| 40 | { |
| 41 | title: "Underway", |
| 42 | items: [ |
| 43 | { title: "VS Code extension", note: "The repository ships a Phase 0 local-runtime companion: terminal launch, health checks, read-only thread summaries, and restore-point browsing. Full chat and editor actions are not part of this slice." }, |
| 44 | { title: "Memory typed store", note: "The native store has shipped: Markdown is the durable source of truth, with a rebuildable SQLite FTS5 index over it. Graph-structured agent memory and multi-signal recall are still ahead (#534–#536)" }, |
| 45 | { title: "Feishu / Lark bot", note: "First long-connection bridge over the runtime API shipped; richer chat features underway (#757)" }, |
| 46 | { title: "Chinese-market & i18n", note: "Locale-aware UI, platform refinements, region-specific search backends (#755)" }, |
| 47 | { title: "Hugging Face model discovery + Model Lab", note: "Browse, download, and manage models from Hugging Face Hub directly in the TUI" }, |
| 48 | ], |
| 49 | }, |
| 50 | { |
| 51 | title: "Considered", |
| 52 | items: [ |
| 53 | { title: "Workrooms", note: "Durable, addressable agent-work threads over the Runtime API and user surfaces (#3209, docs/WORKROOM_ARCHITECTURE.md)" }, |
| 54 | { title: "Exa web-search backend", note: "Bundled alternative to the existing DDG + Bing path (#431)" }, |
| 55 | { title: "Homebrew core formula", note: "Tap exists; pursuing homebrew-core inclusion" }, |
| 56 | { title: "Native Windows installer", note: "MSI / WinGet; Scoop manifest already ships" }, |
| 57 | { title: "Unsloth / NeMo / Arcee fine-tune integration", note: "One-click fine-tuning workflows backed by Unsloth, NVIDIA NeMo, and Arcee toolkits" }, |
| 58 | ], |
| 59 | }, |
| 60 | { |
| 61 | title: "Ruled out", |
| 62 | items: [ |
| 63 | { title: "Always-on or silent product telemetry", note: "There is now a first-party telemetry endpoint (https://telemetry.codewhale.net/v1/telemetry) and it is the shipped default — but it decides where an already-enabled session sends, never whether one collects. Product telemetry stays opt-in and off by default; nothing is collected until the first-run notice is answered with Enable, and CODEWHALE_TELEMETRY=0, telemetry = false, and a recorded decline are all hard floors. What stays ruled out: an always-on mode, per-keystroke or per-tool-call phone-home, silent collection with no notice, and any third-party ad or analytics SDK in the runtime binary. A selected hosted provider still receives the context required for its model turn; loopback routes can keep inference local." }, |
| 64 | { title: "Mandatory hosted relay for local sessions", note: "The local runtime and bring-your-own-provider routes continue to work without sending sessions through a Codewhale service" }, |
| 65 | { title: "Required account for the local runtime", note: "Installing and running Codewhale locally requires no account" }, |
| 66 | { title: "Sponsored model promotion", note: "Model picker stays neutral — no paid placement" }, |
| 67 | { title: "Public share links for local sessions", note: "The retired share-link direction (#471/#481) is not coming back; any future sharing design starts fresh." }, |
| 68 | ], |
| 69 | }, |
| 70 | { |
| 71 | title: "Open model platform", |
| 72 | items: [ |
| 73 | { title: "Community model registry", note: "Discover, share, and rate community fine-tuned models with reproducible recipes" }, |
| 74 | { title: "One-click deploy", note: "Deploy any model to RunPod, Replicate, or your own infra with a single command" }, |
| 75 | { title: "Model evaluation dashboard", note: "Transparent, reproducible comparisons across providers, quantization levels, and hardware" }, |
| 76 | ], |
| 77 | }, |
| 78 | ]; |
| 79 | |
| 80 | const tracksZh = [ |
| 81 | { |
| 82 | title: "已完成", |
| 83 | items: [ |
| 84 | { title: "Canonical action 工具", note: "Bash、File、Git、Run、agent、remember、tasks、update_plan、work_update 与 synthetic tool_search 构成十个默认启用名称;remember 在启用内置记忆时出现,Web 按条件延迟加载" }, |
| 85 | { title: "子 Agent 并行执行", note: "agent;默认 64 个并发会话,可配置到 128 个,通过 var_handle 有界读取结果" }, |
| 86 | { title: "RLM 批量处理", note: "持久沙箱 Python REPL,支持 1–16 路廉价并行子调用,处理长文本分析" }, |
| 87 | { title: "三种运行模式", note: "Plan(只读调查)、Act(执行)与 Operate(Fleet / Workflow 编排);Ask、Auto-Review 与 Full Access 权限姿态独立设置" }, |
| 88 | { title: "OS 命令沙箱", note: "macOS 在可用时使用 Seatbelt;Linux 在安装后可显式启用 bubblewrap。Windows 当前报告无 OS 沙箱。" }, |
| 89 | { title: "持久化会话 + 后台任务", note: "保存、恢复、回滚;后台任务队列,可回放时间线,位于 ~/.codewhale/tasks/" }, |
| 90 | { title: "双向 MCP 协议", note: "消费外部服务器工具;通过 `codewhale mcp` 暴露为服务器;~/.codewhale/mcp.json" }, |
| 91 | { title: "技能 + 统一命令面板", note: "~/.codewhale/skills/ 自动加载;/help、/mode、/status、/config、/trust、/feedback" }, |
| 92 | { title: "OpenRouter 提供商", note: "原生集成 OpenRouter,支持 300+ 模型,覆盖数十个提供商" }, |
| 93 | { title: "OpenAI 兼容与本地运行时", note: "通用 `openai` 路由可接入任意 OpenAI 兼容网关;vLLM、SGLang、Ollama 直连本地端点,无需密钥" }, |
| 94 | { title: "多提供商支持", note: "按会话动态切换提供商(DeepSeek、OpenAI、Anthropic、OpenRouter)" }, |
| 95 | { title: "本地 Web 客户端", note: "v0.9.1 源码候选版已实现:`codewhale web` 是基于 Runtime API 与一次性引导会话边界的回环地址浏览器客户端;审批与用户输入可在页面刷新后恢复(#4423)" }, |
| 96 | { title: "可选加入的产品遥测", note: "默认关闭,首次运行的提示未回答前不会收集任何内容——在你回答“启用”之前,连遥测目录都不会被创建。启用后的会话会把计数与封闭枚举 POST 到第一方端点 https://telemetry.codewhale.net/v1/telemetry,那是一个 Cloudflare Worker,完整源码就在仓库的 telemetry-ingest/ 目录里。它的存储中没有 IP、国家或任何地理位置列——这是结构上不存在,而不是某个可以改回来的开关——不写任何日志,Cloudflare 的保留期固定为三个月。若想保持启用但不联系任何服务器,设置 telemetry_endpoint = \"\":批次会写入 $CODEWHALE_HOME/telemetry/dryrun.jsonl,你可以直接查看本应发送的全部内容。永远不收集 prompt、模型回复、工具参数、文件内容、路径、仓库或分支名、模型 id 与凭据;完整 schema 见 docs/TELEMETRY.md。用 `codewhale config set telemetry false` 或 CODEWHALE_TELEMETRY=0 关闭,同时会删除随机安装 id 与所有已缓冲事件。" }, |
| 97 | ], |
| 98 | }, |
| 99 | { |
| 100 | title: "进行中", |
| 101 | items: [ |
| 102 | { title: "VS Code 扩展", note: "仓库已提供 Phase 0 本地 Runtime 配套扩展:终端启动、健康检查、只读线程摘要和还原点浏览;完整聊天与编辑器操作尚未包含在此版本中。" }, |
| 103 | { title: "记忆类型化存储", note: "原生存储已发布:以 Markdown 作为持久化事实来源,并在其上建立可重建的 SQLite FTS5 索引。图结构 Agent 记忆与多信号召回仍在规划中(#534–#536)" }, |
| 104 | { title: "飞书 / Lark 机器人", note: "基于 runtime API 的长连接桥接已发布首版;更丰富的对话能力进行中(#757)" }, |
| 105 | { title: "中国市场与国际化改进", note: "本地化 UI、平台优化、区域搜索引擎(#755)" }, |
| 106 | { title: "Hugging Face 模型发现 + 模型实验室", note: "在 TUI 中直接浏览、下载和管理 Hugging Face Hub 上的模型" }, |
| 107 | ], |
| 108 | }, |
| 109 | { |
| 110 | title: "考虑中", |
| 111 | items: [ |
| 112 | { title: "Workrooms 工作间", note: "基于 Runtime API 与用户界面的持久、可寻址 Agent 工作线程(#3209,docs/WORKROOM_ARCHITECTURE.md)" }, |
| 113 | { title: "Exa 网页搜索后端", note: "内建替代 DDG + Bing 的搜索路由(#431)" }, |
| 114 | { title: "Homebrew 核心仓库", note: "Tap 已有;正在争取进入 homebrew-core" }, |
| 115 | { title: "Windows 原生安装器", note: "MSI / WinGet;Scoop 清单已发布" }, |
| 116 | { title: "Unsloth / NeMo / Arcee 微调集成", note: "一键微调工作流,由 Unsloth、NVIDIA NeMo 和 Arcee 工具链驱动" }, |
| 117 | ], |
| 118 | }, |
| 119 | { |
| 120 | title: "暂不考虑", |
| 121 | items: [ |
| 122 | { title: "默认开启或静默的产品遥测", note: "现在确实存在第一方遥测端点(https://telemetry.codewhale.net/v1/telemetry),并且它是随产品发布的默认值——但它决定的只是「已启用的会话把批次发到哪里」,而不是「是否收集」。产品遥测依然是可选加入且默认关闭的:在首次运行提示被回答为“启用”之前不会收集任何内容,而 CODEWHALE_TELEMETRY=0、telemetry = false 以及已记录的“拒绝”都是硬性下限。仍被排除在外的是:常开模式、按键级或按工具调用级的回传、没有任何提示的静默收集,以及运行时二进制中的任何第三方广告或分析 SDK。选用托管 provider 时仍会发送本轮所需上下文,回环地址路由可让推理保持本地。" }, |
| 123 | { title: "本地会话强制经过托管中继", note: "本地 Runtime 与自带提供商路由继续工作,无需把会话发送到 Codewhale 服务" }, |
| 124 | { title: "本地 Runtime 强制注册账户", note: "本地安装和运行 Codewhale 不需要账户" }, |
| 125 | { title: "赞助商模型推广", note: "模型选择器保持中立——无付费推荐位" }, |
| 126 | { title: "本地会话的公开分享链接", note: "已停用的分享链接方向(#471/#481)不会恢复;未来的分享设计将重新开始。" }, |
| 127 | ], |
| 128 | }, |
| 129 | { |
| 130 | title: "开放模型平台", |
| 131 | items: [ |
| 132 | { title: "社区模型注册中心", note: "发现、分享和评价社区微调模型,附带可复现的配方" }, |
| 133 | { title: "一键部署", note: "一条命令将任意模型部署到 RunPod、Replicate 或自有基础设施" }, |
| 134 | { title: "模型评测面板", note: "跨提供商、量化级别和硬件的透明、可复现对比" }, |
| 135 | ], |
| 136 | }, |
| 137 | ]; |
| 138 | |
| 139 | const roadmapText = (text: string) => |
| 140 | text.replace(/^>\s*/, "").replaceAll("**", "").replaceAll("CodeWhale", "Codewhale"); |
| 141 | |
| 142 | export default async function RoadmapPage({ params }: { params: Promise<{ locale: string }> }) { |
| 143 | const { locale } = await params; |
| 144 | const isZh = locale === "zh"; |
| 145 | const baseTracks = isZh ? tracksZh : tracksEn; |
| 146 | |
| 147 | // Live feed: shipped from GitHub Releases; underway/considered/ruled-out from issue labels. |
| 148 | // Per-category fallback to the static items so unlabeled categories stay populated. |
| 149 | let tracks = baseTracks; |
| 150 | try { |
| 151 | const env = await getEnv(); |
| 152 | const feed = await getCachedRoadmap(env.CURATED_KV, env.GITHUB_TOKEN); |
| 153 | if (feed) { |
| 154 | const liveByCategory: Record<string, RoadmapItem[]> = { |
| 155 | Shipped: feed.shipped, |
| 156 | Underway: feed.underway, |
| 157 | Considered: feed.considered, |
| 158 | "Ruled out": feed.ruledOut, |
| 159 | 已完成: feed.shipped, |
| 160 | 进行中: feed.underway, |
| 161 | 考虑中: feed.considered, |
| 162 | 暂不考虑: feed.ruledOut, |
| 163 | }; |
| 164 | tracks = baseTracks.map((t) => { |
| 165 | const live = liveByCategory[t.title]; |
| 166 | if (live && live.length > 0) { |
| 167 | return { ...t, items: live.map((it) => ({ title: it.title, note: it.note })) }; |
| 168 | } |
| 169 | return t; |
| 170 | }); |
| 171 | } |
| 172 | } catch { |
| 173 | /* keep static fallback */ |
| 174 | } |
| 175 | |
| 176 | const copy = isZh |
| 177 | ? { |
| 178 | eyebrow: "项目路线图", |
| 179 | title: "路线图", |
| 180 | introduction: "这里将已完成的仓库工作、正在推进的工作、仍在评估的方案和明确不在范围内的方向分开列出。路线图的“已完成”可包含已在源码候选版中实现的工作;安装页与首页另行标明最新已发布包。发布记录和 GitHub issues 会在可用时更新这些分类。", |
| 181 | sectionLabel: "当前状态", |
| 182 | sectionTitle: "按状态查看工作", |
| 183 | browseIssues: "浏览 open issues ↗", |
| 184 | count: (value: number) => `${value} 项`, |
| 185 | contributeLabel: "参与贡献", |
| 186 | contributeTitle: "路线图决策公开进行。", |
| 187 | contributeBody: "Bug 和范围明确的功能请求请使用 issues;尚在形成中的想法可以先在 Discussions 讨论;已有具体实现时,欢迎发送带测试或文档的 pull request。来自不同语言、平台和提供商的验证结果都能帮助维护者判断优先级。", |
| 188 | links: [ |
| 189 | { title: "Issues", detail: "报告问题,或提出范围明确的工作。", href: "https://github.com/Hmbown/CodeWhale/issues" }, |
| 190 | { title: "Discussions", detail: "在开始实现前讨论尚未成熟的想法。", href: "https://github.com/Hmbown/CodeWhale/discussions/new?category=ideas" }, |
| 191 | { title: "Pull requests", detail: "审查现有改动,或发送一个范围清楚的补丁。", href: "https://github.com/Hmbown/CodeWhale/pulls" }, |
| 192 | ], |
| 193 | } |
| 194 | : { |
| 195 | eyebrow: "Project roadmap", |
| 196 | title: "Roadmap", |
| 197 | introduction: "This page separates completed repository work from work in progress, proposals still being evaluated, and directions intentionally kept out of scope. Roadmap Shipped can include work implemented in a source candidate; the install page and homepage separately identify the latest published package. Release records and GitHub issues refresh these categories when available.", |
| 198 | sectionLabel: "Current status", |
| 199 | sectionTitle: "Work grouped by status", |
| 200 | browseIssues: "Browse open issues ↗", |
| 201 | count: (value: number) => `${value} ${value === 1 ? "item" : "items"}`, |
| 202 | contributeLabel: "Contribute", |
| 203 | contributeTitle: "Keep roadmap decisions in the open.", |
| 204 | contributeBody: "Use issues for bugs and well-scoped feature requests, Discussions for ideas that need shaping, and pull requests for concrete changes with tests or documentation. Verification across languages, platforms, and providers helps maintainers judge priority.", |
| 205 | links: [ |
| 206 | { title: "Issues", detail: "Report a problem or propose scoped work.", href: "https://github.com/Hmbown/CodeWhale/issues" }, |
| 207 | { title: "Discussions", detail: "Explore an early idea before implementation.", href: "https://github.com/Hmbown/CodeWhale/discussions/new?category=ideas" }, |
| 208 | { title: "Pull requests", detail: "Review existing work or send a focused change.", href: "https://github.com/Hmbown/CodeWhale/pulls" }, |
| 209 | ], |
| 210 | }; |
| 211 | |
| 212 | return ( |
| 213 | <div className="roadmap-page"> |
| 214 | <section className="community-welcome"> |
| 215 | <div className="portal-current" aria-hidden="true" /> |
| 216 | <div className="portal-container community-welcome-inner"> |
| 217 | <div className="eyebrow">{copy.eyebrow}</div> |
| 218 | <h1>{copy.title}</h1> |
| 219 | <p>{copy.introduction}</p> |
| 220 | </div> |
| 221 | </section> |
| 222 | |
| 223 | <section className="portal-section"> |
| 224 | <div className="portal-container"> |
| 225 | <div className="portal-docs-heading"> |
| 226 | <div> |
| 227 | <span>{copy.sectionLabel}</span> |
| 228 | <h2>{copy.sectionTitle}</h2> |
| 229 | </div> |
| 230 | <Link href="https://github.com/Hmbown/CodeWhale/issues">{copy.browseIssues}</Link> |
| 231 | </div> |
| 232 | {tracks.map((track) => ( |
| 233 | <section key={track.title} className="portal-section-grid py-10 hairline-t"> |
| 234 | <div className="portal-section-copy"> |
| 235 | <span>{copy.count(track.items.length)}</span> |
| 236 | <h2>{track.title}</h2> |
| 237 | </div> |
| 238 | <ul className="hairline-t"> |
| 239 | {track.items.map((item) => ( |
| 240 | <li key={`${item.title}-${item.note}`} className="py-4 hairline-b"> |
| 241 | <h3 className="font-display text-base">{roadmapText(item.title)}</h3> |
| 242 | <p className={`mt-1 text-sm text-ink-soft ${isZh ? "leading-[1.9] tracking-wide" : "leading-relaxed"}`}> |
| 243 | {roadmapText(item.note)} |
| 244 | </p> |
| 245 | </li> |
| 246 | ))} |
| 247 | </ul> |
| 248 | </section> |
| 249 | ))} |
| 250 | </div> |
| 251 | </section> |
| 252 | |
| 253 | <section className="portal-section portal-section-muted"> |
| 254 | <div className="portal-container portal-section-grid"> |
| 255 | <div className="portal-section-copy"> |
| 256 | <span>{copy.contributeLabel}</span> |
| 257 | <h2>{copy.contributeTitle}</h2> |
| 258 | <p>{copy.contributeBody}</p> |
| 259 | </div> |
| 260 | <div className="portal-topic-list"> |
| 261 | {copy.links.map((link) => ( |
| 262 | <Link key={link.title} href={link.href}> |
| 263 | <strong>{link.title}</strong> |
| 264 | <span>{link.detail}</span> |
| 265 | <span aria-hidden="true">↗</span> |
| 266 | </Link> |
| 267 | ))} |
| 268 | </div> |
| 269 | </div> |
| 270 | </section> |
| 271 | </div> |
| 272 | ); |
| 273 | } |
| 274 |