olly-molly 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -0
  3. package/app/api/agent/execute/route.ts +157 -0
  4. package/app/api/agent/status/route.ts +38 -0
  5. package/app/api/check-api-key/route.ts +12 -0
  6. package/app/api/conversations/[id]/route.ts +35 -0
  7. package/app/api/conversations/route.ts +24 -0
  8. package/app/api/members/[id]/route.ts +37 -0
  9. package/app/api/members/route.ts +12 -0
  10. package/app/api/pm/breakdown/route.ts +142 -0
  11. package/app/api/pm/tickets/route.ts +147 -0
  12. package/app/api/projects/[id]/route.ts +56 -0
  13. package/app/api/projects/active/route.ts +15 -0
  14. package/app/api/projects/route.ts +53 -0
  15. package/app/api/tickets/[id]/logs/route.ts +16 -0
  16. package/app/api/tickets/[id]/route.ts +60 -0
  17. package/app/api/tickets/[id]/work-logs/route.ts +16 -0
  18. package/app/api/tickets/route.ts +37 -0
  19. package/app/design-system/page.tsx +242 -0
  20. package/app/favicon.ico +0 -0
  21. package/app/globals.css +318 -0
  22. package/app/layout.tsx +37 -0
  23. package/app/page.tsx +331 -0
  24. package/bin/cli.js +66 -0
  25. package/components/ThemeProvider.tsx +56 -0
  26. package/components/ThemeToggle.tsx +31 -0
  27. package/components/activity/ActivityLog.tsx +96 -0
  28. package/components/activity/index.ts +1 -0
  29. package/components/kanban/ConversationList.tsx +75 -0
  30. package/components/kanban/ConversationView.tsx +132 -0
  31. package/components/kanban/KanbanBoard.tsx +179 -0
  32. package/components/kanban/KanbanColumn.tsx +80 -0
  33. package/components/kanban/SortableTicket.tsx +58 -0
  34. package/components/kanban/TicketCard.tsx +98 -0
  35. package/components/kanban/TicketModal.tsx +510 -0
  36. package/components/kanban/TicketSidebar.tsx +448 -0
  37. package/components/kanban/index.ts +8 -0
  38. package/components/pm/PMRequestModal.tsx +196 -0
  39. package/components/pm/index.ts +1 -0
  40. package/components/project/ProjectSelector.tsx +211 -0
  41. package/components/project/index.ts +1 -0
  42. package/components/team/MemberCard.tsx +147 -0
  43. package/components/team/TeamPanel.tsx +57 -0
  44. package/components/team/index.ts +2 -0
  45. package/components/ui/ApiKeyModal.tsx +101 -0
  46. package/components/ui/Avatar.tsx +95 -0
  47. package/components/ui/Badge.tsx +59 -0
  48. package/components/ui/Button.tsx +60 -0
  49. package/components/ui/Card.tsx +64 -0
  50. package/components/ui/Input.tsx +41 -0
  51. package/components/ui/Modal.tsx +76 -0
  52. package/components/ui/ResizablePane.tsx +97 -0
  53. package/components/ui/Select.tsx +45 -0
  54. package/components/ui/Textarea.tsx +41 -0
  55. package/components/ui/index.ts +8 -0
  56. package/db/dev.sqlite +0 -0
  57. package/db/dev.sqlite-shm +0 -0
  58. package/db/dev.sqlite-wal +0 -0
  59. package/db/schema-conversations.sql +26 -0
  60. package/db/schema-projects.sql +29 -0
  61. package/db/schema.sql +94 -0
  62. package/lib/agent-jobs.ts +232 -0
  63. package/lib/db.ts +564 -0
  64. package/next.config.ts +10 -0
  65. package/package.json +80 -0
  66. package/postcss.config.mjs +7 -0
  67. package/public/app-icon.png +0 -0
  68. package/public/file.svg +1 -0
  69. package/public/globe.svg +1 -0
  70. package/public/next.svg +1 -0
  71. package/public/profiles/designer.png +0 -0
  72. package/public/profiles/dev-backend.png +0 -0
  73. package/public/profiles/dev-frontend.png +0 -0
  74. package/public/profiles/pm.png +0 -0
  75. package/public/profiles/qa.png +0 -0
  76. package/public/vercel.svg +1 -0
  77. package/public/window.svg +1 -0
  78. package/tsconfig.json +34 -0
@@ -0,0 +1,60 @@
1
+ 'use client';
2
+
3
+ import { ButtonHTMLAttributes, forwardRef } from 'react';
4
+
5
+ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
6
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
7
+ size?: 'sm' | 'md' | 'lg';
8
+ }
9
+
10
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
11
+ ({ className = '', variant = 'primary', size = 'md', children, ...props }, ref) => {
12
+ const baseStyles = `
13
+ inline-flex items-center justify-center font-medium
14
+ transition-colors duration-150
15
+ focus:outline-none focus:ring-1 focus:ring-[var(--accent-primary)] focus:ring-offset-1 focus:ring-offset-[var(--bg-primary)]
16
+ disabled:opacity-40 disabled:cursor-not-allowed
17
+ `;
18
+
19
+ const variants = {
20
+ primary: `
21
+ bg-[var(--accent-primary)] text-[var(--bg-primary)]
22
+ hover:bg-[var(--accent-secondary)]
23
+ border border-[var(--accent-primary)]
24
+ `,
25
+ secondary: `
26
+ bg-transparent text-[var(--text-primary)]
27
+ border border-[var(--border-primary)]
28
+ hover:border-[var(--text-primary)]
29
+ `,
30
+ ghost: `
31
+ bg-transparent text-[var(--text-secondary)]
32
+ border border-transparent
33
+ hover:text-[var(--text-primary)]
34
+ `,
35
+ danger: `
36
+ bg-transparent text-[var(--priority-high-text)]
37
+ border border-[var(--priority-high-text)]
38
+ hover:bg-[var(--priority-high)]
39
+ `,
40
+ };
41
+
42
+ const sizes = {
43
+ sm: 'px-3 py-1 text-xs gap-1.5',
44
+ md: 'px-4 py-1.5 text-sm gap-2',
45
+ lg: 'px-5 py-2 text-sm gap-2',
46
+ };
47
+
48
+ return (
49
+ <button
50
+ ref={ref}
51
+ className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
52
+ {...props}
53
+ >
54
+ {children}
55
+ </button>
56
+ );
57
+ }
58
+ );
59
+
60
+ Button.displayName = 'Button';
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ import { ReactNode } from 'react';
4
+
5
+ interface CardProps {
6
+ children: ReactNode;
7
+ className?: string;
8
+ onClick?: () => void;
9
+ hoverable?: boolean;
10
+ variant?: 'default' | 'bordered' | 'flat';
11
+ }
12
+
13
+ export function Card({
14
+ children,
15
+ className = '',
16
+ onClick,
17
+ hoverable = false,
18
+ variant = 'default'
19
+ }: CardProps) {
20
+ const variants = {
21
+ default: `bg-[var(--bg-card)] border-b border-[var(--border-primary)]`,
22
+ bordered: `bg-[var(--bg-card)] border border-[var(--border-primary)]`,
23
+ flat: `bg-transparent`,
24
+ };
25
+
26
+ const hoverStyles = hoverable ? `
27
+ hover:bg-[var(--bg-secondary)]
28
+ cursor-pointer
29
+ transition-colors duration-150
30
+ ` : '';
31
+
32
+ return (
33
+ <div
34
+ className={`${variants[variant]} ${hoverStyles} ${className}`}
35
+ onClick={onClick}
36
+ >
37
+ {children}
38
+ </div>
39
+ );
40
+ }
41
+
42
+ export function CardHeader({ children, className = '' }: { children: ReactNode; className?: string }) {
43
+ return (
44
+ <div className={`px-4 py-3 border-b border-[var(--border-primary)] ${className}`}>
45
+ {children}
46
+ </div>
47
+ );
48
+ }
49
+
50
+ export function CardContent({ children, className = '' }: { children: ReactNode; className?: string }) {
51
+ return (
52
+ <div className={`p-4 ${className}`}>
53
+ {children}
54
+ </div>
55
+ );
56
+ }
57
+
58
+ export function CardFooter({ children, className = '' }: { children: ReactNode; className?: string }) {
59
+ return (
60
+ <div className={`px-4 py-3 border-t border-[var(--border-primary)] ${className}`}>
61
+ {children}
62
+ </div>
63
+ );
64
+ }
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { InputHTMLAttributes, forwardRef } from 'react';
4
+
5
+ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
6
+ label?: string;
7
+ error?: string;
8
+ }
9
+
10
+ export const Input = forwardRef<HTMLInputElement, InputProps>(
11
+ ({ className = '', label, error, ...props }, ref) => {
12
+ return (
13
+ <div className="w-full">
14
+ {label && (
15
+ <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5 uppercase tracking-wide">
16
+ {label}
17
+ </label>
18
+ )}
19
+ <input
20
+ ref={ref}
21
+ className={`
22
+ w-full px-3 py-2 text-sm
23
+ bg-transparent text-[var(--text-primary)]
24
+ border-b border-[var(--border-primary)]
25
+ placeholder:text-[var(--text-muted)]
26
+ transition-colors duration-150
27
+ focus:outline-none focus:border-[var(--text-primary)]
28
+ ${error ? 'border-[var(--priority-high-text)]' : ''}
29
+ ${className}
30
+ `}
31
+ {...props}
32
+ />
33
+ {error && (
34
+ <p className="mt-1 text-xs text-[var(--priority-high-text)]">{error}</p>
35
+ )}
36
+ </div>
37
+ );
38
+ }
39
+ );
40
+
41
+ Input.displayName = 'Input';
@@ -0,0 +1,76 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, ReactNode } from 'react';
4
+
5
+ interface ModalProps {
6
+ isOpen: boolean;
7
+ onClose: () => void;
8
+ title?: string;
9
+ children: ReactNode;
10
+ size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
11
+ }
12
+
13
+ export function Modal({ isOpen, onClose, title, children, size = 'md' }: ModalProps) {
14
+ const overlayRef = useRef<HTMLDivElement>(null);
15
+
16
+ useEffect(() => {
17
+ const handleEscape = (e: KeyboardEvent) => {
18
+ if (e.key === 'Escape') onClose();
19
+ };
20
+
21
+ if (isOpen) {
22
+ document.addEventListener('keydown', handleEscape);
23
+ document.body.style.overflow = 'hidden';
24
+ }
25
+
26
+ return () => {
27
+ document.removeEventListener('keydown', handleEscape);
28
+ document.body.style.overflow = '';
29
+ };
30
+ }, [isOpen, onClose]);
31
+
32
+ if (!isOpen) return null;
33
+
34
+ const sizes = {
35
+ sm: 'max-w-sm',
36
+ md: 'max-w-md',
37
+ lg: 'max-w-lg',
38
+ xl: 'max-w-xl',
39
+ '2xl': 'max-w-2xl',
40
+ };
41
+
42
+ return (
43
+ <div
44
+ ref={overlayRef}
45
+ className="fixed inset-0 z-50 flex items-center justify-center p-4 animate-in fade-in"
46
+ onClick={(e) => e.target === overlayRef.current && onClose()}
47
+ >
48
+ {/* Minimal backdrop */}
49
+ <div className="absolute inset-0 bg-[var(--bg-primary)]/90" />
50
+
51
+ {/* Modal container */}
52
+ <div className={`
53
+ relative w-full ${sizes[size]}
54
+ bg-[var(--bg-card)]
55
+ border border-[var(--border-primary)]
56
+ animate-in zoom-in-95
57
+ max-h-[calc(100vh-4rem)] overflow-y-auto
58
+ `}>
59
+ {title && (
60
+ <div className="flex items-center justify-between px-6 py-4 border-b border-[var(--border-primary)]">
61
+ <h2 className="text-sm font-medium text-[var(--text-primary)] uppercase tracking-wide">{title}</h2>
62
+ <button
63
+ onClick={onClose}
64
+ className="p-1 text-[var(--text-muted)] hover:text-[var(--text-primary)] transition-colors"
65
+ >
66
+ <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
67
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
68
+ </svg>
69
+ </button>
70
+ </div>
71
+ )}
72
+ <div className="p-6">{children}</div>
73
+ </div>
74
+ </div>
75
+ );
76
+ }
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+
3
+ import { useState, useRef, useCallback, useEffect } from 'react';
4
+
5
+ interface ResizablePaneProps {
6
+ left: React.ReactNode;
7
+ right: React.ReactNode;
8
+ defaultLeftWidth?: number;
9
+ minLeftWidth?: number;
10
+ minRightWidth?: number;
11
+ }
12
+
13
+ export function ResizablePane({
14
+ left,
15
+ right,
16
+ defaultLeftWidth = 60, // percentage
17
+ minLeftWidth = 30,
18
+ minRightWidth = 25,
19
+ }: ResizablePaneProps) {
20
+ const [leftWidth, setLeftWidth] = useState(defaultLeftWidth);
21
+ const [isDragging, setIsDragging] = useState(false);
22
+ const containerRef = useRef<HTMLDivElement>(null);
23
+
24
+ // Update leftWidth when defaultLeftWidth changes
25
+ useEffect(() => {
26
+ setLeftWidth(defaultLeftWidth);
27
+ }, [defaultLeftWidth]);
28
+
29
+ const handleMouseDown = () => {
30
+ setIsDragging(true);
31
+ };
32
+
33
+ const handleMouseMove = useCallback(
34
+ (e: MouseEvent) => {
35
+ if (!isDragging || !containerRef.current) return;
36
+
37
+ const containerRect = containerRef.current.getBoundingClientRect();
38
+ const newLeftWidth = ((e.clientX - containerRect.left) / containerRect.width) * 100;
39
+
40
+ // Apply constraints
41
+ const constrainedWidth = Math.max(
42
+ minLeftWidth,
43
+ Math.min(100 - minRightWidth, newLeftWidth)
44
+ );
45
+
46
+ setLeftWidth(constrainedWidth);
47
+ },
48
+ [isDragging, minLeftWidth, minRightWidth]
49
+ );
50
+
51
+ const handleMouseUp = useCallback(() => {
52
+ setIsDragging(false);
53
+ }, []);
54
+
55
+ useEffect(() => {
56
+ if (isDragging) {
57
+ document.addEventListener('mousemove', handleMouseMove);
58
+ document.addEventListener('mouseup', handleMouseUp);
59
+ document.body.style.cursor = 'col-resize';
60
+ document.body.style.userSelect = 'none';
61
+
62
+ return () => {
63
+ document.removeEventListener('mousemove', handleMouseMove);
64
+ document.removeEventListener('mouseup', handleMouseUp);
65
+ document.body.style.cursor = '';
66
+ document.body.style.userSelect = '';
67
+ };
68
+ }
69
+ }, [isDragging, handleMouseMove, handleMouseUp]);
70
+
71
+ return (
72
+ <div ref={containerRef} className="flex h-full overflow-hidden">
73
+ {/* Left Pane */}
74
+ <div
75
+ style={{ width: `${leftWidth}%` }}
76
+ className="overflow-auto flex-shrink-0"
77
+ >
78
+ {left}
79
+ </div>
80
+
81
+ {/* Resizer */}
82
+ <div
83
+ onMouseDown={handleMouseDown}
84
+ className={`w-1 bg-primary hover:bg-indigo-500 cursor-col-resize flex-shrink-0 transition-colors ${isDragging ? 'bg-indigo-500' : ''
85
+ }`}
86
+ />
87
+
88
+ {/* Right Pane */}
89
+ <div
90
+ style={{ width: `${100 - leftWidth}%` }}
91
+ className="overflow-hidden flex-shrink-0"
92
+ >
93
+ {right}
94
+ </div>
95
+ </div>
96
+ );
97
+ }
@@ -0,0 +1,45 @@
1
+ 'use client';
2
+
3
+ interface SelectProps {
4
+ label?: string;
5
+ value: string;
6
+ onChange: (value: string) => void;
7
+ options: { value: string; label: string }[];
8
+ placeholder?: string;
9
+ className?: string;
10
+ }
11
+
12
+ export function Select({ label, value, onChange, options, placeholder, className = '' }: SelectProps) {
13
+ return (
14
+ <div className="w-full">
15
+ {label && (
16
+ <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5 uppercase tracking-wide">
17
+ {label}
18
+ </label>
19
+ )}
20
+ <select
21
+ value={value}
22
+ onChange={(e) => onChange(e.target.value)}
23
+ className={`
24
+ w-full px-3 py-2 text-sm
25
+ bg-transparent text-[var(--text-primary)]
26
+ border-b border-[var(--border-primary)]
27
+ transition-colors duration-150 cursor-pointer
28
+ focus:outline-none focus:border-[var(--text-primary)]
29
+ ${className}
30
+ `}
31
+ >
32
+ {placeholder && (
33
+ <option value="" disabled className="text-[var(--text-muted)]">
34
+ {placeholder}
35
+ </option>
36
+ )}
37
+ {options.map((option) => (
38
+ <option key={option.value} value={option.value} className="bg-[var(--bg-card)]">
39
+ {option.label}
40
+ </option>
41
+ ))}
42
+ </select>
43
+ </div>
44
+ );
45
+ }
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { TextareaHTMLAttributes, forwardRef } from 'react';
4
+
5
+ interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
6
+ label?: string;
7
+ error?: string;
8
+ }
9
+
10
+ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
11
+ ({ className = '', label, error, ...props }, ref) => {
12
+ return (
13
+ <div className="w-full">
14
+ {label && (
15
+ <label className="block text-xs font-medium text-[var(--text-secondary)] mb-1.5 uppercase tracking-wide">
16
+ {label}
17
+ </label>
18
+ )}
19
+ <textarea
20
+ ref={ref}
21
+ className={`
22
+ w-full px-3 py-2 text-sm
23
+ bg-transparent text-[var(--text-primary)]
24
+ border border-[var(--border-primary)]
25
+ placeholder:text-[var(--text-muted)]
26
+ transition-colors duration-150 resize-none
27
+ focus:outline-none focus:border-[var(--text-primary)]
28
+ ${error ? 'border-[var(--priority-high-text)]' : ''}
29
+ ${className}
30
+ `}
31
+ {...props}
32
+ />
33
+ {error && (
34
+ <p className="mt-1 text-xs text-[var(--priority-high-text)]">{error}</p>
35
+ )}
36
+ </div>
37
+ );
38
+ }
39
+ );
40
+
41
+ Textarea.displayName = 'Textarea';
@@ -0,0 +1,8 @@
1
+ export { Button } from './Button';
2
+ export { Card, CardHeader, CardContent, CardFooter } from './Card';
3
+ export { Modal } from './Modal';
4
+ export { Badge, StatusBadge, PriorityBadge } from './Badge';
5
+ export { Avatar } from './Avatar';
6
+ export { Input } from './Input';
7
+ export { Textarea } from './Textarea';
8
+ export { Select } from './Select';
package/db/dev.sqlite ADDED
Binary file
Binary file
Binary file
@@ -0,0 +1,26 @@
1
+ -- Conversations: Each execution of AI agent for a ticket
2
+ CREATE TABLE IF NOT EXISTS conversations (
3
+ id TEXT PRIMARY KEY,
4
+ ticket_id TEXT NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
5
+ agent_id TEXT NOT NULL REFERENCES members(id),
6
+ provider TEXT NOT NULL CHECK(provider IN ('claude', 'opencode')),
7
+ prompt TEXT,
8
+ feedback TEXT,
9
+ status TEXT DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed', 'cancelled')),
10
+ git_commit_hash TEXT,
11
+ started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
12
+ completed_at DATETIME,
13
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
14
+ );
15
+
16
+ -- Conversation Messages: Streaming log entries for each conversation
17
+ CREATE TABLE IF NOT EXISTS conversation_messages (
18
+ id TEXT PRIMARY KEY,
19
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
20
+ content TEXT NOT NULL,
21
+ message_type TEXT DEFAULT 'log' CHECK(message_type IN ('log', 'error', 'success', 'system')),
22
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
23
+ );
24
+
25
+ CREATE INDEX IF NOT EXISTS idx_conversations_ticket ON conversations(ticket_id);
26
+ CREATE INDEX IF NOT EXISTS idx_conversation_messages_conversation ON conversation_messages(conversation_id);
@@ -0,0 +1,29 @@
1
+ -- Projects table for storing target project configurations
2
+ CREATE TABLE IF NOT EXISTS projects (
3
+ id TEXT PRIMARY KEY,
4
+ name TEXT NOT NULL,
5
+ path TEXT NOT NULL UNIQUE,
6
+ description TEXT,
7
+ is_active INTEGER DEFAULT 0,
8
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
9
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
10
+ );
11
+
12
+ -- Agent work logs - detailed execution history
13
+ CREATE TABLE IF NOT EXISTS agent_work_logs (
14
+ id TEXT PRIMARY KEY,
15
+ ticket_id TEXT REFERENCES tickets(id) ON DELETE CASCADE,
16
+ agent_id TEXT REFERENCES members(id),
17
+ project_id TEXT REFERENCES projects(id),
18
+ command TEXT NOT NULL,
19
+ prompt TEXT,
20
+ output TEXT,
21
+ status TEXT DEFAULT 'RUNNING' CHECK(status IN ('RUNNING', 'SUCCESS', 'FAILED', 'CANCELLED')),
22
+ git_commit_hash TEXT,
23
+ started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
24
+ completed_at DATETIME,
25
+ duration_ms INTEGER
26
+ );
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_agent_work_logs_ticket ON agent_work_logs(ticket_id);
29
+ CREATE INDEX IF NOT EXISTS idx_agent_work_logs_agent ON agent_work_logs(agent_id);
package/db/schema.sql ADDED
@@ -0,0 +1,94 @@
1
+ -- Team Members
2
+ CREATE TABLE IF NOT EXISTS members (
3
+ id TEXT PRIMARY KEY,
4
+ role TEXT NOT NULL CHECK(role IN ('PM', 'FE_DEV', 'BACKEND_DEV', 'QA', 'DEVOPS')),
5
+ name TEXT NOT NULL,
6
+ avatar TEXT,
7
+ system_prompt TEXT NOT NULL,
8
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
9
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
10
+ );
11
+
12
+ -- Tickets
13
+ CREATE TABLE IF NOT EXISTS tickets (
14
+ id TEXT PRIMARY KEY,
15
+ title TEXT NOT NULL,
16
+ description TEXT,
17
+ status TEXT DEFAULT 'TODO' CHECK(status IN ('TODO', 'IN_PROGRESS', 'IN_REVIEW', 'NEED_FIX', 'COMPLETE', 'ON_HOLD')),
18
+ priority TEXT DEFAULT 'MEDIUM' CHECK(priority IN ('LOW', 'MEDIUM', 'HIGH', 'CRITICAL')),
19
+ assignee_id TEXT REFERENCES members(id),
20
+ project_id TEXT REFERENCES projects(id),
21
+ created_by TEXT,
22
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
23
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
24
+ );
25
+
26
+ -- Activity Logs
27
+ CREATE TABLE IF NOT EXISTS activity_logs (
28
+ id TEXT PRIMARY KEY,
29
+ ticket_id TEXT REFERENCES tickets(id) ON DELETE CASCADE,
30
+ member_id TEXT REFERENCES members(id),
31
+ action TEXT NOT NULL,
32
+ old_value TEXT,
33
+ new_value TEXT,
34
+ details TEXT,
35
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
36
+ );
37
+
38
+ -- Create indexes for better query performance
39
+ CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets(status);
40
+ CREATE INDEX IF NOT EXISTS idx_tickets_assignee ON tickets(assignee_id);
41
+ CREATE INDEX IF NOT EXISTS idx_tickets_project ON tickets(project_id);
42
+ CREATE INDEX IF NOT EXISTS idx_activity_logs_ticket ON activity_logs(ticket_id);
43
+
44
+ -- Insert default team members
45
+ INSERT OR IGNORE INTO members (id, role, name, avatar, system_prompt) VALUES
46
+ ('pm-001', 'PM', 'PM Agent', '👔', 'You are a Project Manager AI agent. Your responsibilities include:
47
+ - Creating and managing project tickets
48
+ - Assigning tasks to appropriate team members based on their expertise
49
+ - Setting priorities and deadlines
50
+ - Tracking project progress
51
+ - Facilitating communication between team members
52
+ - Making decisions about project scope and timeline
53
+
54
+ When creating tickets, analyze the task requirements and automatically assign them to the most suitable team member.'),
55
+
56
+ ('fe-001', 'FE_DEV', 'Frontend Developer', '🎨', 'You are a Frontend Developer AI agent. Your responsibilities include:
57
+ - Implementing user interfaces using React and Next.js
58
+ - Writing clean, maintainable TypeScript/JavaScript code
59
+ - Creating responsive and accessible designs
60
+ - Integrating with backend APIs
61
+ - Optimizing frontend performance
62
+ - Following best practices for component architecture
63
+
64
+ Focus on creating beautiful, user-friendly interfaces with excellent UX.'),
65
+
66
+ ('be-001', 'BACKEND_DEV', 'Backend Developer', '⚙️', 'You are a Backend Developer AI agent. Your responsibilities include:
67
+ - Designing and implementing REST APIs
68
+ - Working with databases (SQLite, PostgreSQL, etc.)
69
+ - Writing server-side logic and business rules
70
+ - Ensuring API security and performance
71
+ - Creating efficient data models
72
+ - Writing unit and integration tests
73
+
74
+ Focus on building robust, scalable backend systems.'),
75
+
76
+ ('qa-001', 'QA', 'QA Engineer', '🔍', 'You are a QA Engineer AI agent. Your responsibilities include:
77
+ - Testing features moved to "In Review" status
78
+ - Using Chrome DevTools MCP or Playwright MCP for automated testing
79
+ - Writing and executing test cases
80
+ - Reporting bugs and issues
81
+ - Verifying bug fixes
82
+ - Ensuring quality standards are met
83
+
84
+ When a ticket moves to "In Review", thoroughly test the implementation and provide detailed feedback.'),
85
+
86
+ ('devops-001', 'DEVOPS', 'DevOps Engineer', '🚀', 'You are a DevOps Engineer AI agent. Your responsibilities include:
87
+ - Setting up CI/CD pipelines
88
+ - Managing deployment processes
89
+ - Configuring infrastructure and environments
90
+ - Monitoring application performance
91
+ - Handling security and compliance
92
+ - Automating operational tasks
93
+
94
+ Focus on ensuring smooth deployments and reliable infrastructure.');