diff --git a/.changeset/design-review-controls.md b/.changeset/design-review-controls.md
new file mode 100644
index 000000000000..5a877c6e6b6b
--- /dev/null
+++ b/.changeset/design-review-controls.md
@@ -0,0 +1,5 @@
+---
+"@reddb-io/redcode": patch
+---
+
+Restore a compact Design review surface with cyan actions, system light/dark themes, a viewport-sized preview, and keyboard-accessible Review, Assets and Details panels. Keep control styles inside Shadow DOM and prototype styles inside their sandboxed iframe, without adding Tailwind or a global reset.
diff --git a/.changeset/lsp-node-recovery.md b/.changeset/lsp-node-recovery.md
new file mode 100644
index 000000000000..a1126278659f
--- /dev/null
+++ b/.changeset/lsp-node-recovery.md
@@ -0,0 +1,5 @@
+---
+"@reddb-io/redcode": patch
+---
+
+Fix LSP recovery from rejected NODE_OPTIONS, including Biome startup with --user-system-ca. Keep retries isolated per server, preserve quoted options, and monitor the successfully restarted process instead of the exited original.
diff --git a/.changeset/restore-design-tui.md b/.changeset/restore-design-tui.md
new file mode 100644
index 000000000000..aaf16ba3b5b8
--- /dev/null
+++ b/.changeset/restore-design-tui.md
@@ -0,0 +1,5 @@
+---
+"@reddb-io/redcode": patch
+---
+
+Restore Design as the cyan third mode in the fullscreen TUI, alongside Build and Plan. Keep prototyping, browser feedback, image assets, SVG-to-GIF exports and approved Plan handoffs attached to the same conversation. Reopen pre-0.22 prototypes without overwriting their sources, and retain the new Design revision and rendering services.
diff --git a/README.md b/README.md
index f22cd1d19b73..5dd5c822da7b 100644
--- a/README.md
+++ b/README.md
@@ -246,14 +246,15 @@ HTTP endpoint. Set `REDCODE_RPC_URL` to the printed URL. It reuses
## Modes
-Build, Plan and Design define what the agent may change. The full-screen TUI uses its existing
-session runtime for Build and Plan; `Tab` switches between available agents. Design now starts in
-its own SessionV2 terminal with `redcode design`, or in a SessionV2 session in the web app. The
-TUI's `/design` command shows how to open that terminal. Existing conversations are preserved;
-a legacy session ID cannot be adopted as a V2 session.
+Build, Plan and Design are the three primary modes in the full-screen TUI. Press `Tab`
+to cycle between them: Build is red, Plan is gold and Design is cyan. `/design` selects
+Design in the current conversation; `/design-review` reopens its browser review. Prototype
+changes, generated assets, browser feedback and approved handoffs remain in that same
+conversation. Design edits only its prototype work directory.
-In `redcode design`, use `/mode design|plan|build` to change the active mode explicitly. Mode
-changes keep the current SessionV2 history; they do not expand an existing Goal's scope.
+The web app and the optional `redcode design` terminal also use the shared Design
+storage and rendering services. Existing TUI sessions keep their history and execution
+runtime; using Design does not require moving to another terminal.
@@ -275,18 +276,18 @@ reuse application components and design-system evidence through authorized reads
## Design Mode
-Design combines a resumable terminal session with a browser review surface. It supports HTML,
+Design combines the full-screen TUI conversation with a browser review surface. It supports HTML,
React and Solid prototypes, versioned assets, editable SVG-to-GIF exports, and recorded approval.
### Start
```sh
-redcode design "Explore the settings screen"
-redcode design --model provider/model
-redcode design --session ses_existing_v2
+redcode
+# Press Tab to select Design, or use /design.
+# Use /design-review to reopen the current conversation’s browser review.
```
-Use `/review` inside the terminal to open the browser. Choose a starting point, the target
+Use `/design-review` inside the terminal to open the browser. Choose a starting point, the target
application, an engine and an objective. The agent publishes revisions with `design_preview`.
The web app opens the same review implementation in its **Design** tab.
@@ -339,10 +340,12 @@ streaming and migration of the full-screen TUI renderer remain future work.
| The plan's marked Design section | Reviewed scope and evidence for the implementation handoff |
| `DESIGN.md` or `.red/DESIGN.md` | Project design guidance used as source evidence |
-Version 0.22 moves Design off the legacy agents and `/design` HTTP routes. Existing V1 Design
-state and `design.json` files are not migrated automatically. Keep old artifacts when upgrading;
-new reviews use `/api/session/:sessionID/design/review`. Legacy Design configuration options do
-not configure the new review surface.
+Design is available in the existing full-screen TUI again. New documents use the shared
+revision and asset store. When continuing a pre-0.22 prototype, `design_preview` still
+accepts its original `path`: it imports the source into a new document, keeps private
+review files out of the published snapshot, and preserves the original directory.
+TUI feedback and approvals return through `/design/session/:sessionID`; web-app sessions
+use `/api/session/:sessionID/design`.
See [Design Studio](specs/design/studio.md) for storage, permissions, exports and MCP configuration,
and [Design terminal](specs/design/terminal.md) for connection and interaction commands.
diff --git a/bun.lock b/bun.lock
index 650a4890a00d..be364d279b6b 100644
--- a/bun.lock
+++ b/bun.lock
@@ -635,7 +635,7 @@
},
"packages/redcode": {
"name": "@reddb-io/redcode",
- "version": "0.21.2",
+ "version": "0.22.1",
"bin": {
"redcode": "./bin/redcode",
},
@@ -685,6 +685,7 @@
"@pierre/diffs": "catalog:",
"@reddb-io/redcode-client": "workspace:*",
"@reddb-io/redcode-codemode": "workspace:*",
+ "@reddb-io/redcode-design": "workspace:*",
"@reddb-io/redcode-llm": "workspace:*",
"@reddb-io/redcode-plugin": "workspace:*",
"@reddb-io/redcode-protocol": "workspace:*",
diff --git a/packages/app/src/i18n/design-goal-br.ts b/packages/app/src/i18n/design-goal-br.ts
index 3a2de5c4d056..1995ee5783bd 100644
--- a/packages/app/src/i18n/design-goal-br.ts
+++ b/packages/app/src/i18n/design-goal-br.ts
@@ -1,5 +1,6 @@
export const designGoalPortuguese: Record = {
"session.design.studio.title": "Design",
+ "session.design.studio.details": "Detalhes",
"session.design.studio.create": "Criar design",
"session.design.studio.name": "Nome",
"session.design.studio.journey": "Ponto de partida",
diff --git a/packages/app/src/i18n/design-goal.test.ts b/packages/app/src/i18n/design-goal.test.ts
index 2df2a1dfa747..88c9cd1dc686 100644
--- a/packages/app/src/i18n/design-goal.test.ts
+++ b/packages/app/src/i18n/design-goal.test.ts
@@ -14,7 +14,7 @@ import { DESKTOP_NATIVE_LOCALES, DESKTOP_NATIVE_LOCALE_TAGS } from "./desktop-na
describe("Design and Goal localization coverage", () => {
test("declares English fallback separately from completed translations", () => {
expect(designGoalLocales).toEqual(["en", "br"])
- expect(designGoalKeys).toHaveLength(95)
+ expect(designGoalKeys).toHaveLength(96)
expect(designGoalCoverage("en")).toEqual({ sourceLocale: "en", translated: [], fallback: [] })
expect(designGoalCoverage("br")).toEqual({ sourceLocale: "br", translated: designGoalKeys, fallback: [] })
for (const locale of DESKTOP_NATIVE_LOCALES.filter((locale) => locale !== "en" && locale !== "br")) {
diff --git a/packages/core/src/design/files.ts b/packages/core/src/design/files.ts
index a48315cd3242..b80679b77bcd 100644
--- a/packages/core/src/design/files.ts
+++ b/packages/core/src/design/files.ts
@@ -45,7 +45,7 @@ export async function review(storage: string, design: string, id: string, index:
await atomic(path.join(directory, `${id}-${index}.excalidraw`), scene)
}
-export async function snapshot(root: string, blobs: string) {
+export async function snapshot(root: string, blobs: string, read?: (file: string) => Promise) {
const files: Record = {}
const budget = { bytes: 0 }
const walk = async (directory: string) => {
@@ -62,7 +62,9 @@ export async function snapshot(root: string, blobs: string) {
if (!entry.isFile()) continue
if (Object.keys(files).length >= 2000)
throw new Design.Error({ code: "invalid", message: "Design contains more than 2000 files" })
- const source = Bun.file(await resolve(root, file))
+ const resolved = await resolve(root, file)
+ await read?.(resolved)
+ const source = Bun.file(resolved)
budget.bytes += source.size
if (source.size > 25 * 1024 * 1024 || budget.bytes > 100 * 1024 * 1024)
throw new Design.Error({ code: "invalid", message: "Snapshot limits: 25 MB per file and 100 MB in total" })
diff --git a/packages/core/src/v1/config/mcp.ts b/packages/core/src/v1/config/mcp.ts
index 0a2aeff12fb0..7cc22c9f8c5c 100644
--- a/packages/core/src/v1/config/mcp.ts
+++ b/packages/core/src/v1/config/mcp.ts
@@ -3,7 +3,17 @@ export * as ConfigMCPV1 from "./mcp"
import { Schema } from "effect"
import { PositiveInt } from "../../schema"
+const Media = Schema.Record(
+ Schema.String,
+ Schema.Struct({
+ operations: Schema.mutable(Schema.Array(Schema.Literals(["generate", "edit", "reference"]))),
+ formats: Schema.mutable(Schema.Array(Schema.String)),
+ transparency: Schema.Boolean,
+ }),
+)
+
export const Local = Schema.Struct({
+ media: Schema.optional(Media),
type: Schema.Literal("local").annotate({ description: "Type of MCP server connection" }),
command: Schema.mutable(Schema.Array(Schema.String)).annotate({
description: "Command and arguments to run the MCP server",
@@ -42,6 +52,7 @@ export const OAuth = Schema.Struct({
export type OAuth = Schema.Schema.Type
export const Remote = Schema.Struct({
+ media: Schema.optional(Media),
type: Schema.Literal("remote").annotate({ description: "Type of MCP server connection" }),
url: Schema.String.annotate({ description: "URL of the remote MCP server" }),
enabled: Schema.optional(Schema.Boolean).annotate({
diff --git a/packages/design/src/copy.ts b/packages/design/src/copy.ts
index 71ba68e40c11..3f28c316f4c1 100644
--- a/packages/design/src/copy.ts
+++ b/packages/design/src/copy.ts
@@ -1,5 +1,6 @@
export const reviewCopy = {
title: "Design",
+ details: "Details",
create: "Create design",
name: "Name",
journey: "Starting point",
diff --git a/packages/design/src/review.ts b/packages/design/src/review.ts
index 520316ed9593..2e457bc6366c 100644
--- a/packages/design/src/review.ts
+++ b/packages/design/src/review.ts
@@ -3,6 +3,7 @@ import type { ReviewCopy } from "./copy"
export interface ReviewOptions {
base: string
+ endpoint?: string
sessionID: string
copy: ReviewCopy
request?: (url: string, init?: RequestInit) => Promise
@@ -13,7 +14,8 @@ export function mountReview(host: HTMLElement, options: ReviewOptions) {
const copy = { ...options.copy }
const request = options.request ?? fetch
const root = host.attachShadow({ mode: "open" })
- const endpoint = `${options.base.replace(/\/$/, "")}/api/session/${encodeURIComponent(options.sessionID)}/design`
+ const endpoint =
+ options.endpoint ?? `${options.base.replace(/\/$/, "")}/api/session/${encodeURIComponent(options.sessionID)}/design`
const state = {
creating: false,
design: undefined as Design.Info | undefined,
@@ -76,19 +78,47 @@ export function mountReview(host: HTMLElement, options: ReviewOptions) {
return pending
}
root.innerHTML = `