diff --git a/.gitignore b/.gitignore index d1f67b7c83..e3690f0419 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,13 @@ apps/backend/bin/ # Go binaries (built locally) apps/backend/kandev apps/backend/agentctl + +# Next.js +.next/ +out/ +node_modules/ +pnpm-lock.yaml + +# TypeScript +*.tsbuildinfo +apps/landing/next-env.d.ts diff --git a/Makefile b/Makefile index c383202f97..85524af6ad 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,11 @@ # Directories BACKEND_DIR := apps/backend WEB_DIR := apps/web +LANDING_DIR := apps/landing +APPS_DIR := apps # Tools -NPM := npm +PNPM := pnpm MAKE := make # Default target @@ -23,17 +25,19 @@ help: @echo "Development Commands:" @echo " dev-backend Run backend in development mode (port 8080)" @echo " dev-web Run web app in development mode (port 3000)" + @echo " dev-landing Run landing page in development mode (port 3001)" @echo " dev Note: Run dev-backend and dev-web in separate terminals" @echo "" @echo "Build Commands:" - @echo " build Build both backend and web app" + @echo " build Build backend, web app, and landing page" @echo " build-backend Build backend binary" @echo " build-web Build web app for production" + @echo " build-landing Build landing page for production" @echo "" @echo "Installation:" - @echo " install Install all dependencies (backend + web)" + @echo " install Install all dependencies (backend + web + landing)" @echo " install-backend Install backend dependencies" - @echo " install-web Install web dependencies" + @echo " install-web Install web dependencies (uses pnpm workspace)" @echo "" @echo "Testing:" @echo " test Run all tests (backend + web)" @@ -71,7 +75,7 @@ dev: @echo "Backend: http://localhost:8080" @echo "Web app: http://localhost:3000" @echo "" - @$(MAKE) -C $(BACKEND_DIR) run & cd $(WEB_DIR) && $(NPM) run dev + @$(MAKE) -C $(BACKEND_DIR) run & cd $(APPS_DIR) && $(PNPM) --filter @kandev/web dev .PHONY: dev-backend dev-backend: @@ -81,18 +85,24 @@ dev-backend: .PHONY: dev-web dev-web: @echo "Starting web app on http://localhost:3000" - @cd $(WEB_DIR) && $(NPM) run dev + @cd $(APPS_DIR) && $(PNPM) --filter @kandev/web dev + +.PHONY: dev-landing +dev-landing: + @echo "Starting landing page on http://localhost:3001" + @cd $(APPS_DIR) && $(PNPM) --filter @kandev/landing dev # # Build # .PHONY: build -build: build-backend build-web +build: build-backend build-web build-landing @echo "" @echo "✓ Build complete!" @echo " Backend binary: $(BACKEND_DIR)/bin/kandev" @echo " Web app: $(WEB_DIR)/.next" + @echo " Landing page: $(LANDING_DIR)/.next" .PHONY: build-backend build-backend: @@ -102,7 +112,12 @@ build-backend: .PHONY: build-web build-web: @echo "Building web app..." - @cd $(WEB_DIR) && $(NPM) run build + @cd $(APPS_DIR) && $(PNPM) --filter @kandev/web build + +.PHONY: build-landing +build-landing: + @echo "Building landing page..." + @cd $(APPS_DIR) && $(PNPM) --filter @kandev/landing build # # Installation @@ -120,8 +135,8 @@ install-backend: .PHONY: install-web install-web: - @echo "Installing web dependencies..." - @cd $(WEB_DIR) && $(NPM) install + @echo "Installing web and landing dependencies..." + @cd $(APPS_DIR) && $(PNPM) install # # Testing @@ -140,7 +155,7 @@ test-backend: .PHONY: test-web test-web: @echo "Running web app tests..." - @cd $(WEB_DIR) && $(NPM) test + @cd $(APPS_DIR) && $(PNPM) --filter @kandev/web test # # Code Quality @@ -159,7 +174,7 @@ lint-backend: .PHONY: lint-web lint-web: @echo "Linting web app..." - @cd $(WEB_DIR) && $(NPM) run lint + @cd $(APPS_DIR) && $(PNPM) --filter @kandev/web lint .PHONY: fmt fmt: fmt-backend fmt-web @@ -174,7 +189,7 @@ fmt-backend: .PHONY: fmt-web fmt-web: @echo "Formatting web code..." - @cd $(WEB_DIR) && $(NPM) run lint -- --fix || true + @cd $(APPS_DIR) && $(PNPM) --filter @kandev/web lint -- --fix || true # # Cleanup @@ -192,8 +207,9 @@ clean-backend: .PHONY: clean-web clean-web: - @echo "Cleaning web artifacts..." - @rm -rf $(WEB_DIR)/.next $(WEB_DIR)/node_modules + @echo "Cleaning web and landing artifacts..." + @rm -rf $(WEB_DIR)/.next $(LANDING_DIR)/.next $(APPS_DIR)/node_modules + @rm -rf $(APPS_DIR)/packages/*/node_modules .PHONY: clean-db clean-db: diff --git a/apps/landing/app/about/page.tsx b/apps/landing/app/about/page.tsx new file mode 100644 index 0000000000..b436f2329d --- /dev/null +++ b/apps/landing/app/about/page.tsx @@ -0,0 +1,105 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@kandev/ui/card'; +import { IconTarget, IconRocket, IconHeartHandshake } from '@tabler/icons-react'; + +const values = [ + { + icon: IconTarget, + title: 'Developer First', + description: 'Built by developers, for developers. We understand your workflow because we live it every day.', + }, + { + icon: IconRocket, + title: 'Ship Fast, Ship Safe', + description: 'Speed without sacrificing security. AI-powered automation with human oversight and approval.', + }, + { + icon: IconHeartHandshake, + title: 'Open & Transparent', + description: 'Open source at heart. We believe in building in public and learning from the community.', + }, +]; + +const timeline = [ + { + year: '2026', + title: 'The Beginning', + description: 'kandev.ai is born from the need to combine visual task management with AI-assisted development.', + }, +]; + +export default function AboutPage() { + return ( +
+
+

About kandev.ai

+

+ We're building the future of development workflows. +

+
+ +
+

Our Mission

+

+ kandev.ai exists to make software development more visual, more collaborative, and more efficient. + We believe that combining Kanban methodology with AI-assisted coding creates a workflow that's + greater than the sum of its parts. +

+

+ Traditional development tools force you to context switch between project management, coding, + and deployment. We're building a unified experience where your Kanban board is the control center + for AI agents working in isolated, secure environments. +

+
+ +
+

Our Values

+
+ {values.map((value) => { + const Icon = value.icon; + return ( + + + + {value.title} + + + {value.description} + + + ); + })} +
+
+ +
+

Our Journey

+
+ {timeline.map((item) => ( + + +
{item.year}
+ {item.title} +
+ +

{item.description}

+
+
+ ))} +
+
+ +
+

Why Kanban + Development + AI?

+

+ Kanban boards provide visual clarity. Development tools provide execution. AI provides automation. + Together, they create a workflow where you can see all your work, execute it in parallel, and + automate the routine parts while maintaining full control. +

+

+ We're just getting started. As we grow, we'll continue to focus on making development more + intuitive, more powerful, and more enjoyable. +

+
+
+ ); +} diff --git a/apps/landing/app/blog/page.tsx b/apps/landing/app/blog/page.tsx new file mode 100644 index 0000000000..5bfa3e4265 --- /dev/null +++ b/apps/landing/app/blog/page.tsx @@ -0,0 +1,50 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@kandev/ui/card'; +import Link from 'next/link'; + +const blogPosts = [ + { + title: 'Introducing kandev.ai: AI-Powered Kanban for Developers', + description: 'Learn how kandev.ai combines Kanban boards with AI agents, isolated worktrees, and Docker containers to accelerate your development workflow.', + date: 'January 10, 2026', + slug: 'introducing-kandev', + author: 'Kandev Team', + }, + { + title: 'Running Multiple AI Agents in Parallel: Best Practices', + description: 'Discover how to effectively manage multiple AI coding agents working simultaneously on different tasks using isolated environments and approval workflows.', + date: 'January 5, 2026', + slug: 'parallel-ai-agents', + author: 'Kandev Team', + }, +]; + +export default function BlogPage() { + return ( +
+

Blog

+

+ Insights and updates from the kandev.ai team. +

+ +
+ {blogPosts.map((post) => ( + + + +
+ {post.date} + • + {post.author} +
+ {post.title} +
+ + {post.description} + +
+ + ))} +
+
+ ); +} diff --git a/apps/landing/app/docs/layout.tsx b/apps/landing/app/docs/layout.tsx new file mode 100644 index 0000000000..ec1602ac03 --- /dev/null +++ b/apps/landing/app/docs/layout.tsx @@ -0,0 +1,10 @@ +import { DocsSidebar } from '@/components/docs-sidebar'; + +export default function DocsLayout({ children }: { children: React.ReactNode }) { + return ( +
+ +
{children}
+
+ ); +} diff --git a/apps/landing/app/docs/page.tsx b/apps/landing/app/docs/page.tsx new file mode 100644 index 0000000000..8d9e025f3c --- /dev/null +++ b/apps/landing/app/docs/page.tsx @@ -0,0 +1,149 @@ +export default function DocsPage() { + return ( +
+
+

Getting Started with kandev.ai

+

+ kandev.ai combines Kanban task management with AI-powered development workflows. Manage tasks visually, + execute them with AI agents in isolated environments, and ship faster with confidence. +

+
+ +
+

What is kandev.ai?

+

+ kandev.ai is a development tool that bridges project management and AI-assisted coding. Each task on your + Kanban board can be executed by AI agents working in isolated git worktrees and Docker containers. +

+
+ +

Key Features

+
+
+

Visual Task Management

+

Organize development work with Kanban boards

+
+
+

Isolated Worktrees

+

Each task gets its own git worktree for parallel development

+
+
+

Local Docker Execution

+

AI agents run in secure Docker containers on your machine

+
+
+

Multiple AI Agents

+

Support for Claude Code, OpenAI Codex, Augment, and more

+
+
+

Approval Workflows

+

Review and approve agent changes before merging

+
+
+

Multiple Workspaces

+

Organize projects by client, team, or product

+
+
+ +

How It Works

+
+
+
1
+
+

Create a Task

+

Add a task to your Kanban board with a description of what needs to be done

+
+
+
+
2
+
+

Configure Repository

+

Select a repository and base branch for the task

+
+
+
+
3
+
+

Choose AI Agent

+

Select which AI agent should work on the task

+
+
+
+
4
+
+

Isolated Environment

+

kandev creates an isolated git worktree and Docker container

+
+
+
+
5
+
+

AI Execution

+

The AI agent executes the task in the secure environment

+
+
+
+
6
+
+

Review & Approve

+

Review the changes and approve them for merging

+
+
+
+ +
+

Why kandev.ai?

+

+ Traditional development workflows require constant context switching. kandev.ai eliminates this by: +

+ +
+ +
+

Security & Control

+

+ Every operation performed by AI agents is logged and requires approval. Agents run in isolated Docker + containers with restricted permissions, ensuring your codebase and system remain secure. +

+
+ +
+

Ready to Get Started?

+

+ Check out our guides to install and configure kandev.ai +

+
+ + Installation Guide + + + Quick Start Tutorial + +
+
+
+ ); +} diff --git a/apps/landing/app/global-error.tsx b/apps/landing/app/global-error.tsx new file mode 100644 index 0000000000..6841c83961 --- /dev/null +++ b/apps/landing/app/global-error.tsx @@ -0,0 +1,62 @@ +'use client'; + +export default function GlobalError({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + return ( + + + Error - kandev.ai + + +
+

+ Something went wrong! +

+

+ We're sorry, but something unexpected happened. +

+ +
+ + + ); +} diff --git a/apps/landing/app/globals.css b/apps/landing/app/globals.css new file mode 100644 index 0000000000..2591006301 --- /dev/null +++ b/apps/landing/app/globals.css @@ -0,0 +1,155 @@ +@import "tailwindcss"; +@import "tw-animate-css"; +@import "shadcn/tailwind.css"; +@import "@kandev/theme/fonts.css"; + +@source "../components/**/*.{js,ts,jsx,tsx}"; +@source "../../packages/ui/src/**/*.{js,ts,jsx,tsx}"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-sans); + --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: var(--radius); + --radius-md: var(--radius); + --radius-lg: var(--radius); + --radius-xl: var(--radius); + --radius-2xl: var(--radius); + --radius-3xl: var(--radius); + --radius-4xl: var(--radius); +} + +:root { + --font-geist-sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; + --font-sans: "Figtree", "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", + sans-serif; + --font-geist-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.51 0.23 277); + --primary-foreground: oklch(0.96 0.02 272); + --secondary: oklch(0.967 0.001 286.375); + --secondary-foreground: oklch(0.21 0.006 285.885); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.68 0.18 276); + --accent-foreground: oklch(0.98 0 0); + --destructive: oklch(0.58 0.22 27); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.79 0.10 275); + --chart-2: oklch(0.68 0.16 277); + --chart-3: oklch(0.59 0.20 277); + --chart-4: oklch(0.51 0.23 277); + --chart-5: oklch(0.46 0.21 277); + --radius: 0.375rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.51 0.23 277); + --sidebar-primary-foreground: oklch(0.96 0.02 272); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.59 0.20 277); + --primary-foreground: oklch(0.96 0.02 272); + --secondary: oklch(0.274 0.006 286.033); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.62 0.18 276); + --accent-foreground: oklch(0.98 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.79 0.10 275); + --chart-2: oklch(0.68 0.16 277); + --chart-3: oklch(0.59 0.20 277); + --chart-4: oklch(0.51 0.23 277); + --chart-5: oklch(0.46 0.21 277); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.68 0.16 277); + --sidebar-primary-foreground: oklch(0.96 0.02 272); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + html { + scrollbar-gutter: stable; + } + body { + @apply bg-background text-foreground; + } +} + +@layer utilities { + .animate-pulse-slow { + animation: pulse-slow 8s ease-in-out infinite; + } +} + +@keyframes pulse-slow { + 0%, 100% { + transform: scale(1); + opacity: 0.6; + } + 50% { + transform: scale(1.1); + opacity: 0.8; + } +} diff --git a/apps/landing/app/layout.tsx b/apps/landing/app/layout.tsx new file mode 100644 index 0000000000..b2ac53504c --- /dev/null +++ b/apps/landing/app/layout.tsx @@ -0,0 +1,32 @@ +import type { Metadata } from 'next'; +import './globals.css'; +import { ThemeProvider } from '@/components/theme-provider'; +import { Navbar } from '@/components/navbar'; +import { Footer } from '@/components/footer'; + +export const dynamic = 'force-dynamic'; + +export const metadata: Metadata = { + title: 'kandev.ai - AI-powered Kanban for Developers', + description: 'Kanban meets AI development. Manage tasks with isolated worktrees, Docker containers, and AI agents.', +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + +
+ {children} +
+