bizy-ai 1.0.0__py3-none-any.whl

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.
agent/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """
2
+ Business Agent - AI-Powered Business Execution Assistant
3
+ """
4
+
5
+ __version__ = "1.0.0"
agent/cli.py ADDED
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env python3
2
+ """Business Agent CLI Tool"""
3
+
4
+ import click
5
+ from agent.tasks import TaskManager
6
+ from agent.planner import BusinessPlanner
7
+ from agent.research import ResearchAgent
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ from rich.panel import Panel
11
+ from rich.markdown import Markdown
12
+ from datetime import datetime, timedelta
13
+ from dotenv import load_dotenv
14
+
15
+ load_dotenv()
16
+ console = Console()
17
+
18
+ @click.group()
19
+ def cli():
20
+ """Business Agent CLI - Manage your business from the command line"""
21
+ pass
22
+
23
+ # TASK COMMANDS
24
+ @cli.group()
25
+ def task():
26
+ """Manage tasks"""
27
+ pass
28
+
29
+ @task.command()
30
+ @click.argument('title')
31
+ @click.option('--description', '-d', help='Task description')
32
+ @click.option('--priority', '-p', type=int, default=3)
33
+ @click.option('--category', '-c', help='Task category')
34
+ @click.option('--hours', '-h', type=float, help='Estimated hours')
35
+ def add(title, description, priority, category, hours):
36
+ """Add a new task"""
37
+ task_mgr = TaskManager()
38
+ task = task_mgr.create_task(
39
+ title=title,
40
+ description=description,
41
+ priority=priority,
42
+ category=category,
43
+ estimated_hours=hours
44
+ )
45
+ console.print(f"[green]✓[/green] Task created: {task.title} (ID: {task.id})")
46
+ task_mgr.close()
47
+
48
+ @task.command()
49
+ def list():
50
+ """List all pending tasks"""
51
+ task_mgr = TaskManager()
52
+ tasks = task_mgr.get_tasks_for_today()
53
+
54
+ if not tasks:
55
+ console.print("[yellow]No pending tasks[/yellow]")
56
+ return
57
+
58
+ table = Table(title="📋 Your Tasks", show_header=True, header_style="bold cyan")
59
+ table.add_column("ID", style="dim")
60
+ table.add_column("Priority", justify="center")
61
+ table.add_column("Title")
62
+ table.add_column("Category", style="cyan")
63
+
64
+ for task in tasks:
65
+ priority_str = "🔴" if task.priority == 1 else "🟡" if task.priority == 3 else "🟢"
66
+ table.add_row(
67
+ str(task.id),
68
+ priority_str,
69
+ task.title[:50],
70
+ task.category or "-"
71
+ )
72
+
73
+ console.print(table)
74
+ task_mgr.close()
75
+
76
+ @task.command()
77
+ @click.argument('task_id', type=int)
78
+ def complete(task_id):
79
+ """Mark a task as complete"""
80
+ task_mgr = TaskManager()
81
+ task = task_mgr.complete_task(task_id)
82
+ if task:
83
+ console.print(f"[green]✓[/green] Completed: {task.title}")
84
+ else:
85
+ console.print(f"[red]✗[/red] Task {task_id} not found")
86
+ task_mgr.close()
87
+
88
+ # GOAL COMMANDS
89
+ @cli.group()
90
+ def goal():
91
+ """Manage goals"""
92
+ pass
93
+
94
+ @goal.command()
95
+ @click.argument('title')
96
+ @click.option('--description', '-d', help='Goal description')
97
+ @click.option('--horizon', '-h', type=click.Choice(['weekly', 'monthly', 'quarterly', 'yearly']), default='monthly')
98
+ @click.option('--target', '-t', help='Target date (YYYY-MM-DD)')
99
+ def add(title, description, horizon, target):
100
+ """Add a new goal"""
101
+ planner = BusinessPlanner()
102
+ target_date = None
103
+ if target:
104
+ target_date = datetime.strptime(target, '%Y-%m-%d')
105
+
106
+ goal = planner.create_goal(
107
+ title=title,
108
+ description=description,
109
+ horizon=horizon,
110
+ target_date=target_date
111
+ )
112
+ console.print(f"[green]✓[/green] Goal created: {goal.title} (ID: {goal.id})")
113
+ planner.close()
114
+
115
+ @goal.command()
116
+ def list():
117
+ """List all active goals"""
118
+ planner = BusinessPlanner()
119
+ goals = planner.get_active_goals()
120
+
121
+ if not goals:
122
+ console.print("[yellow]No active goals[/yellow]")
123
+ return
124
+
125
+ table = Table(title="🎯 Your Goals", show_header=True, header_style="bold cyan")
126
+ table.add_column("ID", style="dim")
127
+ table.add_column("Title")
128
+ table.add_column("Horizon", style="cyan")
129
+ table.add_column("Progress", justify="right")
130
+
131
+ for goal in goals:
132
+ progress_bar = "█" * int(goal.progress_percentage / 10) + "░" * (10 - int(goal.progress_percentage / 10))
133
+ table.add_row(
134
+ str(goal.id),
135
+ goal.title[:40],
136
+ goal.horizon,
137
+ f"{progress_bar} {goal.progress_percentage:.0f}%"
138
+ )
139
+
140
+ console.print(table)
141
+ planner.close()
142
+
143
+ @goal.command()
144
+ @click.argument('goal_id', type=int)
145
+ def breakdown(goal_id):
146
+ """Break down a goal into tasks using AI"""
147
+ planner = BusinessPlanner()
148
+ console.print(f"[cyan]Breaking down goal {goal_id}...[/cyan]")
149
+ tasks = planner.break_down_goal(goal_id)
150
+
151
+ if tasks:
152
+ console.print(f"[green]✓[/green] Created {len(tasks)} tasks")
153
+ for task in tasks:
154
+ console.print(f" • {task.title}")
155
+ else:
156
+ console.print("[red]✗[/red] Failed to break down goal")
157
+ planner.close()
158
+
159
+ # RESEARCH COMMANDS
160
+ @cli.group()
161
+ def research():
162
+ """Conduct research"""
163
+ pass
164
+
165
+ @research.command()
166
+ @click.argument('topic')
167
+ @click.option('--goal', '-g', help='Business goal')
168
+ def topic(topic, goal):
169
+ """Research a topic"""
170
+ researcher = ResearchAgent()
171
+ console.print(f"[cyan]Researching: {topic}...[/cyan]\n")
172
+
173
+ result = researcher.research_topic(
174
+ topic=topic,
175
+ business_goal=goal or "General research",
176
+ depth="standard"
177
+ )
178
+
179
+ if 'error' in result:
180
+ console.print(f"[red]✗ Error:[/red] {result['error']}")
181
+ else:
182
+ console.print(Panel(
183
+ Markdown(result['findings']),
184
+ title=f"🔍 Research: {topic}",
185
+ border_style="blue"
186
+ ))
187
+ console.print(f"\n[dim]Saved as research ID: {result['research_id']}[/dim]")
188
+ researcher.close()
189
+
190
+ @research.command()
191
+ @click.argument('domain')
192
+ @click.argument('offering')
193
+ def competitors(domain, offering):
194
+ """Research competitors"""
195
+ researcher = ResearchAgent()
196
+ console.print(f"[cyan]Analyzing competitive landscape...[/cyan]\n")
197
+
198
+ result = researcher.research_competitors(domain, offering)
199
+
200
+ if 'error' in result:
201
+ console.print(f"[red]✗ Error:[/red] {result['error']}")
202
+ else:
203
+ console.print(Panel(
204
+ Markdown(result['findings']),
205
+ title="🏆 Competitive Analysis",
206
+ border_style="blue"
207
+ ))
208
+ researcher.close()
209
+
210
+ # STATS COMMAND
211
+ @cli.command()
212
+ def stats():
213
+ """Show statistics"""
214
+ task_mgr = TaskManager()
215
+ weekly_stats = task_mgr.get_weekly_stats()
216
+ velocity = task_mgr.get_task_velocity()
217
+ today_tasks = task_mgr.get_tasks_for_today()
218
+
219
+ console.print("\n[bold cyan]📊 Your Statistics[/bold cyan]\n")
220
+ console.print("[bold]This Week:[/bold]")
221
+ console.print(f" • Tasks Completed: {weekly_stats['total_tasks_completed']}")
222
+ console.print(f" • Completion Rate: {weekly_stats['average_completion_rate']:.0%}")
223
+ console.print(f"\n[bold]Velocity:[/bold] {velocity:.1f} tasks/day")
224
+ console.print(f"\n[bold]Today:[/bold] {len(today_tasks)} tasks scheduled\n")
225
+ task_mgr.close()
226
+
227
+ if __name__ == "__main__":
228
+ cli()
agent/core.py ADDED
@@ -0,0 +1,205 @@
1
+ import anthropic
2
+ import os
3
+ from datetime import datetime, timedelta
4
+ from rich.console import Console
5
+ from rich.panel import Panel
6
+ from rich.markdown import Markdown
7
+ import json
8
+
9
+ console = Console()
10
+
11
+ class BusinessAgent:
12
+ def __init__(self):
13
+ api_key = os.getenv('ANTHROPIC_API_KEY')
14
+ if not api_key:
15
+ raise ValueError("ANTHROPIC_API_KEY environment variable not set")
16
+
17
+ self.client = anthropic.Anthropic(api_key=api_key)
18
+ self.model = "claude-sonnet-4-20250514"
19
+
20
+ def morning_briefing(self, tasks_today, yesterday_summary, business_context, goals=None):
21
+ """Generate morning briefing with AI"""
22
+
23
+ tasks_str = self._format_tasks_for_prompt(tasks_today)
24
+ goals_str = self._format_goals_for_prompt(goals) if goals else "No active goals set"
25
+
26
+ prompt = f"""You are my business execution assistant. Generate an energizing and focused morning briefing for today.
27
+
28
+ TODAY'S DATE: {datetime.now().strftime('%A, %B %d, %Y')}
29
+
30
+ YESTERDAY'S SUMMARY:
31
+ Tasks Completed: {yesterday_summary.get('tasks_completed', 0)} of {yesterday_summary.get('tasks_due', 0)}
32
+ Completion Rate: {yesterday_summary.get('completion_rate', 0):.0%}
33
+
34
+ ACTIVE GOALS:
35
+ {goals_str}
36
+
37
+ TODAY'S SCHEDULED TASKS:
38
+ {tasks_str}
39
+
40
+ Create a morning briefing with these sections:
41
+
42
+ ## 🌅 Good Morning!
43
+ Brief motivational opener (1-2 sentences)
44
+
45
+ ## 📊 Yesterday's Recap
46
+ Quick summary of what was accomplished and what it means
47
+
48
+ ## 🎯 Today's Mission
49
+ Top 3 priorities for today with estimated time for each
50
+ Explain why each matters for the bigger picture
51
+
52
+ ## ⚠️ Watch Out For
53
+ Any potential blockers, risks, or important deadlines
54
+
55
+ ## 💡 Pro Tip
56
+ One specific, actionable suggestion to move the business forward today
57
+
58
+ Keep it concise, energizing, and actionable. Use markdown formatting."""
59
+
60
+ try:
61
+ message = self.client.messages.create(
62
+ model=self.model,
63
+ max_tokens=2000,
64
+ messages=[{"role": "user", "content": prompt}]
65
+ )
66
+ return message.content[0].text
67
+ except Exception as e:
68
+ return f"Error generating briefing: {e}"
69
+
70
+ def evening_review_analysis(self, completed_tasks, planned_tasks, wins, blockers, learnings, energy_level):
71
+ """Analyze the day's work and provide insights"""
72
+
73
+ completed_str = "\n".join([f"- {t.title} ({t.category or 'uncategorized'})" for t in completed_tasks])
74
+ planned_str = "\n".join([f"- {t.title} ({t.status})" for t in planned_tasks])
75
+
76
+ completion_rate = len(completed_tasks) / len(planned_tasks) if planned_tasks else 0
77
+
78
+ prompt = f"""Analyze today's business execution and provide insights:
79
+
80
+ DATE: {datetime.now().strftime('%A, %B %d, %Y')}
81
+
82
+ COMPLETION RATE: {completion_rate:.0%} ({len(completed_tasks)}/{len(planned_tasks)} tasks)
83
+
84
+ COMPLETED TASKS:
85
+ {completed_str or 'None'}
86
+
87
+ PLANNED BUT NOT COMPLETED:
88
+ {planned_str or 'All completed!'}
89
+
90
+ USER'S REFLECTION:
91
+ - Wins: {wins or 'Not specified'}
92
+ - Blockers: {blockers or 'None mentioned'}
93
+ - Learnings: {learnings or 'Not specified'}
94
+ - Energy Level: {energy_level or 'Not specified'}
95
+
96
+ Provide:
97
+
98
+ ## 📈 Day Analysis
99
+ Honest assessment of today's productivity (2-3 sentences)
100
+
101
+ ## 🎯 What Worked
102
+ Highlight positive patterns or wins
103
+
104
+ ## 🔄 What to Adjust
105
+ Constructive suggestions for improvement
106
+
107
+ ## 🌟 Momentum Builder
108
+ One specific thing to carry into tomorrow
109
+
110
+ Be supportive but honest. Keep it concise."""
111
+
112
+ try:
113
+ message = self.client.messages.create(
114
+ model=self.model,
115
+ max_tokens=1500,
116
+ messages=[{"role": "user", "content": prompt}]
117
+ )
118
+ return message.content[0].text
119
+ except Exception as e:
120
+ return f"Error generating analysis: {e}"
121
+
122
+ def weekly_review(self, weekly_stats, goals_progress, key_events):
123
+ """Generate comprehensive weekly review"""
124
+
125
+ prompt = f"""Generate a comprehensive weekly review for my business:
126
+
127
+ WEEK ENDING: {datetime.now().strftime('%B %d, %Y')}
128
+
129
+ STATS:
130
+ - Total Tasks Completed: {weekly_stats.get('total_tasks_completed', 0)}
131
+ - Total Tasks Planned: {weekly_stats.get('total_tasks_planned', 0)}
132
+ - Average Completion Rate: {weekly_stats.get('average_completion_rate', 0):.0%}
133
+ - Days Logged: {weekly_stats.get('days_logged', 0)}
134
+
135
+ GOAL PROGRESS:
136
+ {goals_progress}
137
+
138
+ KEY EVENTS/NOTES:
139
+ {key_events or 'None logged'}
140
+
141
+ Create a weekly review with:
142
+
143
+ ## 📊 Week in Review
144
+ High-level summary of the week's productivity
145
+
146
+ ## 🎯 Goal Progress
147
+ Assessment of progress toward key goals
148
+
149
+ ## 📈 Trends & Patterns
150
+ What patterns do you notice? (velocity, energy, blockers)
151
+
152
+ ## 🏆 Wins & Achievements
153
+ Celebrate what went well
154
+
155
+ ## 🔧 Areas for Improvement
156
+ What could be better next week?
157
+
158
+ ## 📋 Next Week's Focus
159
+ Top 3 priorities for the coming week
160
+
161
+ ## 💡 Strategic Insight
162
+ One key insight or recommendation for the business
163
+
164
+ Be thorough but scannable. Use bullet points where appropriate."""
165
+
166
+ try:
167
+ message = self.client.messages.create(
168
+ model=self.model,
169
+ max_tokens=3000,
170
+ messages=[{"role": "user", "content": prompt}]
171
+ )
172
+ return message.content[0].text
173
+ except Exception as e:
174
+ return f"Error generating weekly review: {e}"
175
+
176
+ # Helper methods
177
+
178
+ def _format_tasks_for_prompt(self, tasks):
179
+ """Format tasks for inclusion in prompts"""
180
+ if not tasks:
181
+ return "No tasks scheduled"
182
+
183
+ formatted = []
184
+ for task in tasks[:10]: # Limit to top 10
185
+ due_str = task.due_date.strftime('%m/%d') if task.due_date else 'No due date'
186
+ est_str = f"{task.estimated_hours}h" if task.estimated_hours else "?"
187
+ formatted.append(
188
+ f"- [{task.priority}] {task.title} (Est: {est_str}, Due: {due_str})"
189
+ )
190
+
191
+ return "\n".join(formatted)
192
+
193
+ def _format_goals_for_prompt(self, goals):
194
+ """Format goals for inclusion in prompts"""
195
+ if not goals:
196
+ return "No active goals"
197
+
198
+ formatted = []
199
+ for goal in goals[:5]: # Top 5 goals
200
+ target_str = goal.target_date.strftime('%m/%d/%Y') if goal.target_date else 'No target'
201
+ formatted.append(
202
+ f"- [{goal.horizon.upper()}] {goal.title} - {goal.progress_percentage:.0f}% (Target: {target_str})"
203
+ )
204
+
205
+ return "\n".join(formatted)
agent/models.py ADDED
@@ -0,0 +1,216 @@
1
+ from sqlalchemy import create_engine, Column, Integer, String, DateTime, Boolean, Text, Float, JSON
2
+ from sqlalchemy.ext.declarative import declarative_base
3
+ from sqlalchemy.orm import sessionmaker
4
+ from datetime import datetime
5
+ import os
6
+
7
+ Base = declarative_base()
8
+
9
+ class Task(Base):
10
+ __tablename__ = 'tasks'
11
+
12
+ id = Column(Integer, primary_key=True)
13
+ title = Column(String(500), nullable=False)
14
+ description = Column(Text)
15
+ priority = Column(Integer, default=3) # 1=highest, 5=lowest
16
+ status = Column(String(50), default='pending') # pending, in_progress, completed, blocked
17
+ category = Column(String(100)) # development, marketing, operations, finance, etc.
18
+ estimated_hours = Column(Float)
19
+ actual_hours = Column(Float)
20
+ due_date = Column(DateTime)
21
+ created_at = Column(DateTime, default=datetime.utcnow)
22
+ completed_at = Column(DateTime)
23
+ parent_goal_id = Column(Integer) # Links to goals
24
+ dependencies = Column(JSON) # List of task IDs this depends on
25
+ notes = Column(Text)
26
+ tags = Column(JSON) # List of tags for filtering
27
+
28
+ def to_dict(self):
29
+ return {
30
+ 'id': self.id,
31
+ 'title': self.title,
32
+ 'description': self.description,
33
+ 'priority': self.priority,
34
+ 'status': self.status,
35
+ 'category': self.category,
36
+ 'estimated_hours': self.estimated_hours,
37
+ 'actual_hours': self.actual_hours,
38
+ 'due_date': self.due_date.isoformat() if self.due_date else None,
39
+ 'created_at': self.created_at.isoformat() if self.created_at else None,
40
+ 'completed_at': self.completed_at.isoformat() if self.completed_at else None,
41
+ 'parent_goal_id': self.parent_goal_id,
42
+ 'dependencies': self.dependencies or [],
43
+ 'notes': self.notes,
44
+ 'tags': self.tags or []
45
+ }
46
+
47
+ class Goal(Base):
48
+ __tablename__ = 'goals'
49
+
50
+ id = Column(Integer, primary_key=True)
51
+ title = Column(String(500), nullable=False)
52
+ description = Column(Text)
53
+ horizon = Column(String(50)) # yearly, quarterly, monthly, weekly
54
+ target_date = Column(DateTime)
55
+ status = Column(String(50), default='active') # active, completed, on_hold, cancelled
56
+ progress_percentage = Column(Float, default=0.0)
57
+ success_criteria = Column(Text)
58
+ parent_goal_id = Column(Integer)
59
+ created_at = Column(DateTime, default=datetime.utcnow)
60
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
61
+ metrics = Column(JSON) # Key metrics to track
62
+
63
+ def to_dict(self):
64
+ return {
65
+ 'id': self.id,
66
+ 'title': self.title,
67
+ 'description': self.description,
68
+ 'horizon': self.horizon,
69
+ 'target_date': self.target_date.isoformat() if self.target_date else None,
70
+ 'status': self.status,
71
+ 'progress_percentage': self.progress_percentage,
72
+ 'success_criteria': self.success_criteria,
73
+ 'parent_goal_id': self.parent_goal_id,
74
+ 'created_at': self.created_at.isoformat() if self.created_at else None,
75
+ 'metrics': self.metrics or {}
76
+ }
77
+
78
+ class DailyLog(Base):
79
+ __tablename__ = 'daily_logs'
80
+
81
+ id = Column(Integer, primary_key=True)
82
+ date = Column(DateTime, nullable=False, unique=True)
83
+ tasks_completed = Column(Integer, default=0)
84
+ tasks_planned = Column(Integer, default=0)
85
+ completion_rate = Column(Float)
86
+ energy_level = Column(String(20)) # high, medium, low
87
+ blockers = Column(Text)
88
+ wins = Column(Text)
89
+ learnings = Column(Text)
90
+ tomorrow_focus = Column(Text)
91
+ mood = Column(String(50))
92
+ notes = Column(Text)
93
+
94
+ def to_dict(self):
95
+ return {
96
+ 'id': self.id,
97
+ 'date': self.date.isoformat() if self.date else None,
98
+ 'tasks_completed': self.tasks_completed,
99
+ 'tasks_planned': self.tasks_planned,
100
+ 'completion_rate': self.completion_rate,
101
+ 'energy_level': self.energy_level,
102
+ 'blockers': self.blockers,
103
+ 'wins': self.wins,
104
+ 'learnings': self.learnings,
105
+ 'tomorrow_focus': self.tomorrow_focus,
106
+ 'mood': self.mood,
107
+ 'notes': self.notes
108
+ }
109
+
110
+ class ResearchItem(Base):
111
+ __tablename__ = 'research'
112
+
113
+ id = Column(Integer, primary_key=True)
114
+ title = Column(String(500))
115
+ summary = Column(Text)
116
+ source_url = Column(String(1000))
117
+ category = Column(String(100)) # competitor, trend, opportunity, threat, technology
118
+ relevance_score = Column(Float)
119
+ date_found = Column(DateTime, default=datetime.utcnow)
120
+ action_items = Column(Text)
121
+ tags = Column(JSON)
122
+ raw_data = Column(JSON) # Store full research data
123
+
124
+ def to_dict(self):
125
+ return {
126
+ 'id': self.id,
127
+ 'title': self.title,
128
+ 'summary': self.summary,
129
+ 'source_url': self.source_url,
130
+ 'category': self.category,
131
+ 'relevance_score': self.relevance_score,
132
+ 'date_found': self.date_found.isoformat() if self.date_found else None,
133
+ 'action_items': self.action_items,
134
+ 'tags': self.tags or []
135
+ }
136
+
137
+ class BusinessMetric(Base):
138
+ __tablename__ = 'metrics'
139
+
140
+ id = Column(Integer, primary_key=True)
141
+ metric_name = Column(String(200))
142
+ value = Column(Float)
143
+ date = Column(DateTime, default=datetime.utcnow)
144
+ notes = Column(Text)
145
+ target_value = Column(Float)
146
+ category = Column(String(100)) # revenue, growth, customer, product, etc.
147
+
148
+ def to_dict(self):
149
+ return {
150
+ 'id': self.id,
151
+ 'metric_name': self.metric_name,
152
+ 'value': self.value,
153
+ 'date': self.date.isoformat() if self.date else None,
154
+ 'notes': self.notes,
155
+ 'target_value': self.target_value,
156
+ 'category': self.category
157
+ }
158
+
159
+ class BusinessPlan(Base):
160
+ __tablename__ = 'business_plans'
161
+
162
+ id = Column(Integer, primary_key=True)
163
+ version = Column(String(50))
164
+ vision = Column(Text)
165
+ mission = Column(Text)
166
+ value_proposition = Column(Text)
167
+ target_market = Column(Text)
168
+ revenue_model = Column(Text)
169
+ key_resources = Column(JSON)
170
+ key_activities = Column(JSON)
171
+ key_partnerships = Column(JSON)
172
+ cost_structure = Column(JSON)
173
+ created_at = Column(DateTime, default=datetime.utcnow)
174
+ is_active = Column(Boolean, default=True)
175
+
176
+ def to_dict(self):
177
+ return {
178
+ 'id': self.id,
179
+ 'version': self.version,
180
+ 'vision': self.vision,
181
+ 'mission': self.mission,
182
+ 'value_proposition': self.value_proposition,
183
+ 'target_market': self.target_market,
184
+ 'revenue_model': self.revenue_model,
185
+ 'key_resources': self.key_resources or {},
186
+ 'key_activities': self.key_activities or [],
187
+ 'key_partnerships': self.key_partnerships or [],
188
+ 'cost_structure': self.cost_structure or {},
189
+ 'created_at': self.created_at.isoformat() if self.created_at else None,
190
+ 'is_active': self.is_active
191
+ }
192
+
193
+ # Database setup
194
+ def get_engine(db_path=None):
195
+ if db_path is None:
196
+ # Use environment variable or default to home directory
197
+ db_path = os.getenv('BUSINESS_AGENT_DB', os.path.expanduser('~/.business-agent/tasks.db'))
198
+
199
+ # Create directory if it doesn't exist
200
+ db_dir = os.path.dirname(db_path)
201
+ if db_dir:
202
+ os.makedirs(db_dir, exist_ok=True)
203
+
204
+ return create_engine(f'sqlite:///{db_path}', echo=False)
205
+
206
+ def get_session(engine=None):
207
+ if engine is None:
208
+ engine = get_engine()
209
+ Session = sessionmaker(bind=engine)
210
+ return Session()
211
+
212
+ def init_database(db_path=None):
213
+ """Initialize the database with all tables"""
214
+ engine = get_engine(db_path)
215
+ Base.metadata.create_all(engine)
216
+ return engine