#!/bin/sh
set -e
echo "=== Creating demo git repository ==="
BARE_REPO="/workspace/demo-repo.git"
TEMP_DIR=$(mktemp -d)
# Initialize bare repo
git init --bare "$BARE_REPO"
cd "$TEMP_DIR"
git init -b main
git remote add origin "$BARE_REPO"
# ─── main branch: initial task manager app ───
cat > README.md << 'READMEEOF'
# Task Manager
A modern task management application built with React and TypeScript.
## Features
- Create, update, and delete tasks
- Filter by status and priority
- Real-time updates
- Responsive design
## Getting Started
```bash
npm install
npm run dev
```
READMEEOF
cat > package.json << 'PKGEOF'
{
"name": "task-manager",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}
PKGEOF
mkdir -p src/components src/hooks src/lib
cat > src/app.tsx << 'APPEOF'
import { useState } from 'react';
import { TaskList } from './components/TaskList';
import { Sidebar } from './components/Sidebar';
export function App() {
const [filter, setFilter] = useState('all');
return (
Task Manager
);
}
APPEOF
cat > src/components/TaskList.tsx << 'TLEOF'
import { useTasks } from '../hooks/useTasks';
interface TaskListProps {
filter: string;
}
export function TaskList({ filter }: TaskListProps) {
const { tasks, loading } = useTasks(filter);
if (loading) return Loading...
;
return (
{tasks.map(task => (
-
{task.title}
{task.status}
))}
);
}
TLEOF
cat > src/components/Sidebar.tsx << 'SBEOF'
interface SidebarProps {
filter: string;
onFilterChange: (filter: string) => void;
}
export function Sidebar({ filter, onFilterChange }: SidebarProps) {
const filters = ['all', 'active', 'completed'];
return (
);
}
SBEOF
cat > src/hooks/useTasks.ts << 'HTEOF'
import { useState, useEffect } from 'react';
import { fetchTasks } from '../lib/api';
export function useTasks(filter: string) {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchTasks(filter).then(data => {
setTasks(data);
setLoading(false);
});
}, [filter]);
return { tasks, loading };
}
HTEOF
cat > src/lib/api.ts << 'APIEOF'
const API_URL = '/api';
export async function fetchTasks(filter: string) {
const res = await fetch(`${API_URL}/tasks?filter=${filter}`);
return res.json();
}
export async function createTask(title: string) {
const res = await fetch(`${API_URL}/tasks`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title }),
});
return res.json();
}
APIEOF
git add -A
git commit -m "feat: initial task manager setup"
git push origin main
# ─── initiative branch (same as main, no extra commits) ───
git checkout -b cw/task-manager-redesign
git push origin cw/task-manager-redesign
# ─── phase branch: UI overhaul with 3 commits ───
git checkout -b cw/task-manager-redesign-phase-ui-overhaul
# Commit 1: responsive header with search and notifications
mkdir -p src/components
cat > src/components/Header.tsx << 'HDREOF'
import { useState } from 'react';
interface HeaderProps {
onSearch: (query: string) => void;
}
export function Header({ onSearch }: HeaderProps) {
const [query, setQuery] = useState('');
const [notifications] = useState([
{ id: 1, text: 'Task "Deploy v2" completed', unread: true },
{ id: 2, text: 'New comment on "API redesign"', unread: true },
{ id: 3, text: 'Sprint review tomorrow', unread: false },
]);
const unreadCount = notifications.filter(n => n.unread).length;
return (
);
}
HDREOF
cat > src/components/TaskFilters.tsx << 'TFEOF'
interface TaskFiltersProps {
activeFilter: string;
onFilterChange: (filter: string) => void;
activePriority: string;
onPriorityChange: (priority: string) => void;
}
export function TaskFilters({
activeFilter,
onFilterChange,
activePriority,
onPriorityChange,
}: TaskFiltersProps) {
const statuses = ['all', 'active', 'completed', 'blocked'];
const priorities = ['all', 'high', 'medium', 'low'];
return (
{statuses.map(s => (
))}
{priorities.map(p => (
))}
);
}
TFEOF
# Update app.tsx to use Header
cat > src/app.tsx << 'APPEOF2'
import { useState } from 'react';
import { Header } from './components/Header';
import { TaskList } from './components/TaskList';
import { TaskFilters } from './components/TaskFilters';
import { Sidebar } from './components/Sidebar';
export function App() {
const [filter, setFilter] = useState('all');
const [priority, setPriority] = useState('all');
const [searchQuery, setSearchQuery] = useState('');
return (
);
}
APPEOF2
git add -A
git commit -m "feat: add responsive header with search and notifications"
# Commit 2: enhance task list with status icons and priorities
cat > src/components/TaskList.tsx << 'TL2EOF'
import { useTasks } from '../hooks/useTasks';
interface Task {
id: string;
title: string;
status: 'active' | 'completed' | 'blocked';
priority: 'high' | 'medium' | 'low';
assignee?: string;
dueDate?: string;
}
interface TaskListProps {
filter: string;
}
const STATUS_ICONS: Record = {
active: '🔵',
completed: '✅',
blocked: '🔴',
};
const PRIORITY_COLORS: Record = {
high: 'priority-high',
medium: 'priority-medium',
low: 'priority-low',
};
export function TaskList({ filter }: TaskListProps) {
const { tasks, loading } = useTasks(filter);
if (loading) {
return (
{[1, 2, 3].map(i => (
))}
);
}
if (tasks.length === 0) {
return (
No tasks found
);
}
return (
Task
Priority
Assignee
Due Date
Status
{(tasks as Task[]).map(task => (
{STATUS_ICONS[task.status]}
{task.title}
{task.priority}
{task.assignee ? (
{task.assignee.slice(0, 2).toUpperCase()}
) : (
—
)}
{task.dueDate ?? '—'}
{task.status}
))}
);
}
TL2EOF
git add -A
git commit -m "refactor: enhance task list with status icons and priorities"
# Commit 3: type-safe API client with error handling
cat > src/lib/api.ts << 'API2EOF'
interface ApiError {
message: string;
status: number;
}
interface Task {
id: string;
title: string;
status: 'active' | 'completed' | 'blocked';
priority: 'high' | 'medium' | 'low';
assignee?: string;
dueDate?: string;
}
interface CreateTaskInput {
title: string;
priority?: Task['priority'];
assignee?: string;
dueDate?: string;
}
class ApiClient {
private baseUrl: string;
constructor(baseUrl = '/api') {
this.baseUrl = baseUrl;
}
private async request(path: string, options?: RequestInit): Promise {
const response = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
const error: ApiError = {
message: `Request failed: ${response.statusText}`,
status: response.status,
};
throw error;
}
return response.json();
}
async getTasks(filter?: string): Promise {
const params = filter && filter !== 'all' ? `?filter=${filter}` : '';
return this.request(`/tasks${params}`);
}
async getTask(id: string): Promise {
return this.request(`/tasks/${id}`);
}
async createTask(input: CreateTaskInput): Promise {
return this.request('/tasks', {
method: 'POST',
body: JSON.stringify(input),
});
}
async updateTask(id: string, updates: Partial): Promise {
return this.request(`/tasks/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates),
});
}
async deleteTask(id: string): Promise {
await this.request(`/tasks/${id}`, { method: 'DELETE' });
}
}
export const api = new ApiClient();
API2EOF
cat > src/hooks/useTasks.ts << 'HT2EOF'
import { useState, useEffect, useCallback } from 'react';
import { api } from '../lib/api';
interface Task {
id: string;
title: string;
status: 'active' | 'completed' | 'blocked';
priority: 'high' | 'medium' | 'low';
assignee?: string;
dueDate?: string;
}
interface UseTasksResult {
tasks: Task[];
loading: boolean;
error: string | null;
refresh: () => void;
createTask: (title: string) => Promise;
deleteTask: (id: string) => Promise;
}
export function useTasks(filter: string): UseTasksResult {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const loadTasks = useCallback(async () => {
try {
setLoading(true);
setError(null);
const data = await api.getTasks(filter);
setTasks(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load tasks');
} finally {
setLoading(false);
}
}, [filter]);
useEffect(() => {
loadTasks();
}, [loadTasks]);
const createTask = useCallback(async (title: string) => {
await api.createTask({ title });
await loadTasks();
}, [loadTasks]);
const deleteTask = useCallback(async (id: string) => {
await api.deleteTask(id);
await loadTasks();
}, [loadTasks]);
return { tasks, loading, error, refresh: loadTasks, createTask, deleteTask };
}
HT2EOF
git add -A
git commit -m "refactor: type-safe API client with error handling"
# Push all branches
git push origin cw/task-manager-redesign-phase-ui-overhaul
# Clean up
cd /
rm -rf "$TEMP_DIR"
echo "=== Demo git repository created at $BARE_REPO ==="
# ─── Step 2: Run Node.js seed ───
echo "=== Running database seed ==="
PROJECT_ID=$(CW_DB_PATH=/workspace/.cw/cw.db node /app/apps/server/dist/preview-seed.js)
echo "=== Seed complete, project ID: $PROJECT_ID ==="
# ─── Step 3: Set up local branches in the server's clone dir ───
CLONE_DIR="/workspace/repos/demo-app-${PROJECT_ID}"
echo "=== Cloning demo repo to $CLONE_DIR ==="
mkdir -p /workspace/repos
git clone "$BARE_REPO" "$CLONE_DIR"
echo "=== Setting up local branches in $CLONE_DIR ==="
cd "$CLONE_DIR"
git checkout -b cw/task-manager-redesign origin/cw/task-manager-redesign
git checkout -b cw/task-manager-redesign-phase-ui-overhaul origin/cw/task-manager-redesign-phase-ui-overhaul
git checkout main
echo "=== Preview seed complete ==="