Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/deploy-catalog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Deploy terminal catalog

on:
push:
branches: [main]
paths:
- ".github/workflows/deploy-catalog.yml"
- "bun.lock"
- "package.json"
- "packages/drive/**"
- "apps/catalog/**"
workflow_dispatch:

concurrency:
group: deploy-terminal-catalog-${{ github.ref_name }}
cancel-in-progress: false

permissions:
contents: read

jobs:
deploy:
if: github.repository == 'anomalyco/opencode-drive' && github.ref_name == 'main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.14"

- run: sudo apt-get update && sudo apt-get install --yes ffmpeg
- run: bun install --frozen-lockfile

- name: Validate
run: |
bun run --cwd packages/drive check
bun run --cwd packages/drive test
bun run --cwd apps/catalog check

- name: Deploy
working-directory: apps/catalog
run: bun run deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
15 changes: 8 additions & 7 deletions apps/catalog/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Executable states have canonical `<flow-id>/<state-id>` addresses. The catalog v
Resolve and reproduce an executable state with:

```bash
bun run catalog:reproduce -- patch-success-lifecycle/permission-prompt \
bun run reproduce -- patch-success-lifecycle/permission-prompt \
--opencode /path/to/opencode
```

Expand Down Expand Up @@ -76,10 +76,11 @@ Variants are independent capture environments, not duplicated screen definitions

```bash
bun run capture -- \
--variant baseline=/path/to/opencode \
--theme baseline=opencode \
--variant candidate=/path/to/opencode \
--theme candidate=rosepine
--opencode /path/to/opencode \
--revision v2~1 \
--revision v2 \
--theme opencode \
--theme rosepine
```

Each variant must have:
Expand Down Expand Up @@ -113,9 +114,9 @@ If OpenCode UI copy drifts, inspect the current target checkout and update exact

## Scope

- The annotation system was deliberately removed. Do not restore annotation routes, storage, KV bindings, or markup UI without an explicit product decision.
- Annotations are URL-fragment/localStorage state only; GitHub remains the durable issue store. Do not add accounts, OAuth, uploads, or anonymous writes without an explicit product decision.
- OpenCode Drive is the generic published package in this monorepo (`packages/drive`); this app owns everything OpenCode-catalog-specific, and the package must not import the app.
- The catalog repository is private because its state suite and review workflow are Anomaly application concerns.
- The catalog app is private and deployed only as internal developer tooling under `dev.opencode.ai/lab`.

## Verification

Expand Down
17 changes: 0 additions & 17 deletions apps/catalog/NOTES.md

This file was deleted.

10 changes: 5 additions & 5 deletions apps/catalog/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# OpenCode Terminal Catalog

Production: <https://catalog.kitlangton.dev>
Internal deployment: <https://dev.opencode.ai/lab/catalog>

Capture a reproducible catalog of OpenCode terminal states from local checkouts, browse every state in one web app, and flip between themes or branches without changing the selected screen.

The catalog currently contains 63 scripted states covering navigation, sessions, assistant responses, questions, subagents, shell, file patches, reads, project search, web tools, permissions, toasts, and review surfaces.
The generated manifest is the source of truth for the current state count. It covers navigation, sessions, assistant responses, questions, subagents, shell, file patches, reads, project search, web tools, permissions, notifications, and review surfaces.

## Prerequisites

Expand Down Expand Up @@ -95,7 +95,7 @@ Capture any refs available in one OpenCode checkout:
```bash
bun run capture -- \
--opencode $HOME/code/opencode \
--revision main \
--revision origin/v2 \
--revision v2

bun run generate
Expand All @@ -118,7 +118,7 @@ Themes and checkout comparisons can be combined:
```bash
bun run capture -- \
--opencode $HOME/code/opencode \
--revision main \
--revision origin/v2 \
--revision v2 \
--theme nord \
--theme rosepine
Expand Down Expand Up @@ -214,7 +214,7 @@ The application deploys as a Cloudflare Worker with static assets:
bun run deploy
```

Current deployment: https://catalog.kitlangton.dev
Current deployment: https://dev.opencode.ai/lab/catalog

## Troubleshooting

Expand Down
8 changes: 1 addition & 7 deletions apps/catalog/catalog/authored/flows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,6 @@ export const flowGroups = defineFlows(screens, {
},
review: {
label: "Review",
flows: {
"reviewing-a-diff": {
title: "Reviewing a diff",
description: "Inspect the working tree and verify whether files have changed.",
steps: [{ capture: "diff-viewer", title: "Open the diff viewer", trigger: "Run /diff" }],
},
},
flows: executableFlowDefinitions("review"),
},
})
82 changes: 66 additions & 16 deletions apps/catalog/catalog/authored/screens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const screens = defineScreens(taxonomies, {
surfaces: "modal",
patterns: "palette",
features: ["commands", "navigation"],
states: "default",
states: "populated",
},
"model-picker": {
title: "Model picker",
Expand All @@ -31,7 +31,7 @@ export const screens = defineScreens(taxonomies, {
surfaces: "modal",
patterns: "picker",
features: "model",
states: "default",
states: "populated",
},
"agent-picker": {
title: "Agent picker",
Expand All @@ -41,7 +41,7 @@ export const screens = defineScreens(taxonomies, {
surfaces: "modal",
patterns: "picker",
features: "agent",
states: "default",
states: "populated",
},
"integration-picker": {
title: "Integrations",
Expand All @@ -51,7 +51,7 @@ export const screens = defineScreens(taxonomies, {
surfaces: "modal",
patterns: "picker",
features: "integration",
states: "default",
states: "populated",
},
"theme-picker": {
title: "Theme picker",
Expand All @@ -61,7 +61,7 @@ export const screens = defineScreens(taxonomies, {
surfaces: "modal",
patterns: "picker",
features: "theme",
states: "default",
states: "populated",
},
"mcp-list": {
title: "MCPs",
Expand Down Expand Up @@ -131,7 +131,67 @@ export const screens = defineScreens(taxonomies, {
surfaces: "modal",
patterns: "picker",
features: "skill",
states: "default",
states: "populated",
},
"command-palette-empty": {
title: "Command palette (no results)",
category: "navigation",
screenLabels: ["command-navigation"],
uiElements: ["command-palette", "search-field", "list", "keyboard-hints", "dialog", "empty-state"],
surfaces: "modal",
patterns: "palette",
features: ["commands", "navigation"],
states: "empty",
},
"model-picker-empty": {
title: "Model picker (no results)",
category: "picker",
screenLabels: ["model-selection"],
uiElements: ["picker", "search-field", "single-select", "list", "dialog", "keyboard-hints", "empty-state"],
surfaces: "modal",
patterns: "picker",
features: "model",
states: "empty",
},
"agent-picker-empty": {
title: "Agent picker (no results)",
category: "picker",
screenLabels: ["agent-selection"],
uiElements: ["picker", "search-field", "single-select", "list", "dialog", "keyboard-hints", "empty-state"],
surfaces: "modal",
patterns: "picker",
features: "agent",
states: "empty",
},
"integration-picker-empty": {
title: "Integrations (no results)",
category: "picker",
screenLabels: ["integration-setup"],
uiElements: ["picker", "search-field", "single-select", "list", "dialog", "empty-state"],
surfaces: "modal",
patterns: "picker",
features: "integration",
states: "empty",
},
"theme-picker-empty": {
title: "Theme picker (no results)",
category: "picker",
screenLabels: ["theme-selection"],
uiElements: ["picker", "search-field", "single-select", "list", "dialog", "empty-state"],
surfaces: "modal",
patterns: "picker",
features: "theme",
states: "empty",
},
"skill-picker-empty": {
title: "Skill picker (no results)",
category: "session",
screenLabels: ["skill-selection"],
uiElements: ["picker", "search-field", "single-select", "list", "dialog", "keyboard-hints", "empty-state"],
surfaces: "modal",
patterns: "picker",
features: "skill",
states: "empty",
},
...executableScreens,
"session-rename": {
Expand Down Expand Up @@ -214,16 +274,6 @@ export const screens = defineScreens(taxonomies, {
features: "notification",
states: "error",
},
"diff-viewer": {
title: "Diff viewer",
category: "session",
screenLabels: ["diff-review"],
uiElements: ["full-screen-view", "list", "keyboard-hints", "empty-state"],
surfaces: "full-screen",
patterns: "list",
features: ["diff", "session"],
states: "empty",
},
})

export type CaptureId = ScreenId<typeof screens>
60 changes: 34 additions & 26 deletions apps/catalog/catalog/authoring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,30 @@ import type { DriveManifest } from "./schema"
const manifest: DriveManifest = {
format: "opencode-terminal-frame-captures-v1",
generatedBy: "scripts/capture-opencode-drive.ts",
variants: [{
id: "baseline",
label: "Baseline",
source: "/tmp/opencode",
revision: "abc123",
ref: "v2",
committedAt: "2026-07-17T10:17:51Z",
theme: "opencode",
}],
variants: [
{
id: "baseline",
label: "Baseline",
source: "/tmp/opencode",
revision: "abc123",
ref: "v2",
committedAt: "2026-07-17T10:17:51Z",
theme: "opencode",
},
],
captures: [
{
id: "home",
title: "Home",
category: "system",
frames: [{
variantId: "baseline",
src: "captures/baseline/home.frame.json",
cols: 118,
rows: 34,
}],
frames: [
{
variantId: "baseline",
src: "captures/baseline/home.frame.json",
cols: 118,
rows: 34,
},
],
},
],
}
Expand Down Expand Up @@ -136,17 +140,21 @@ describe("catalog authoring", () => {
const error = await Effect.runPromise(
compileCatalog(definition, {
...manifest,
captures: [{
id: "home",
title: "Home",
category: "system",
frames: [{
variantId: "unknown",
src: "captures/unknown/home.frame.json",
cols: 118,
rows: 34,
}],
}],
captures: [
{
id: "home",
title: "Home",
category: "system",
frames: [
{
variantId: "unknown",
src: "captures/unknown/home.frame.json",
cols: 118,
rows: 34,
},
],
},
],
}).pipe(Effect.flip),
)

Expand Down
Loading