bizy-ai 1.0.2__tar.gz → 1.1.1__tar.gz

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 (36) hide show
  1. {bizy_ai-1.0.2/bizy_ai.egg-info → bizy_ai-1.1.1}/PKG-INFO +52 -2
  2. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/README.md +44 -1
  3. bizy_ai-1.1.1/agent/cli.py +456 -0
  4. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/core.py +4 -4
  5. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/models.py +25 -10
  6. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/plan_manager.py +14 -3
  7. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/tasks.py +120 -22
  8. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/weekly_review.py +10 -10
  9. {bizy_ai-1.0.2 → bizy_ai-1.1.1/bizy_ai.egg-info}/PKG-INFO +52 -2
  10. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/bizy_ai.egg-info/SOURCES.txt +8 -2
  11. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/bizy_ai.egg-info/requires.txt +8 -0
  12. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/pyproject.toml +11 -1
  13. bizy_ai-1.1.1/scripts/load_business_plan.py +110 -0
  14. bizy_ai-1.1.1/scripts/migrate_db.py +40 -0
  15. bizy_ai-1.1.1/scripts/scheduler.py +94 -0
  16. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/setup.py +1 -1
  17. bizy_ai-1.1.1/tests/test_models.py +137 -0
  18. bizy_ai-1.1.1/tests/test_plan_manager.py +73 -0
  19. bizy_ai-1.1.1/tests/test_planner.py +185 -0
  20. bizy_ai-1.1.1/tests/test_tasks.py +345 -0
  21. bizy_ai-1.0.2/agent/cli.py +0 -271
  22. bizy_ai-1.0.2/scripts/agent_cli.py +0 -232
  23. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/.env.example +0 -0
  24. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/LICENSE +0 -0
  25. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/MANIFEST.in +0 -0
  26. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/__init__.py +0 -0
  27. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/evening_review.py +0 -0
  28. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/morning_brief.py +0 -0
  29. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/planner.py +0 -0
  30. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/agent/research.py +0 -0
  31. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/bizy_ai.egg-info/dependency_links.txt +0 -0
  32. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/bizy_ai.egg-info/entry_points.txt +0 -0
  33. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/bizy_ai.egg-info/top_level.txt +0 -0
  34. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/requirements.txt +0 -0
  35. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/scripts/init_db.py +0 -0
  36. {bizy_ai-1.0.2 → bizy_ai-1.1.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bizy-ai
3
- Version: 1.0.2
3
+ Version: 1.1.1
4
4
  Summary: AI-powered business planning and execution agent
5
5
  Author: Reid Chatham
6
6
  License: MIT
@@ -37,6 +37,13 @@ Requires-Dist: rich>=13.0.0
37
37
  Requires-Dist: python-dateutil>=2.8.2
38
38
  Requires-Dist: click>=8.1.0
39
39
  Requires-Dist: jinja2>=3.1.2
40
+ Provides-Extra: dev
41
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
42
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
43
+ Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
44
+ Requires-Dist: pytest-watch>=4.2.0; extra == "dev"
45
+ Requires-Dist: black>=23.0.0; extra == "dev"
46
+ Requires-Dist: flake8>=6.1.0; extra == "dev"
40
47
  Dynamic: license-file
41
48
  Dynamic: requires-python
42
49
 
@@ -88,7 +95,50 @@ pip install -e .
88
95
  ./setup.sh
89
96
  ```
90
97
 
91
- See **[INSTALL.md](INSTALL.md)** for detailed installation options.
98
+ See **[INSTALL.md](docs/INSTALL.md)** for detailed installation options.
99
+
100
+ ---
101
+
102
+ ## Development
103
+
104
+ ### Test-Driven Development (TDD)
105
+
106
+ This project follows TDD principles. All new features must:
107
+
108
+ 1. **Write tests first** - Define expected behavior
109
+ 2. **Run tests (they should fail)** - Red phase
110
+ 3. **Implement feature** - Green phase
111
+ 4. **Refactor** - Clean up code
112
+
113
+ ### Running Tests
114
+
115
+ ```bash
116
+ # Run all tests with coverage
117
+ make test
118
+
119
+ # Run tests in watch mode
120
+ make test-watch
121
+
122
+ # Run specific test file
123
+ pytest tests/test_tasks.py -v
124
+ ```
125
+
126
+ ### Database Environments
127
+
128
+ - **Production**: `~/.business-agent/tasks.db` - Your actual business data 🔒
129
+ - **Development**: `~/.business-agent/dev_tasks.db` - Safe for experimentation ⚙️
130
+ - **Test**: In-memory - Isolated, clean for each test 🧪
131
+
132
+ ```bash
133
+ # Use development database
134
+ export BIZY_ENV=development
135
+ make dev
136
+
137
+ # Use production database (default)
138
+ export BIZY_ENV=production
139
+ ```
140
+
141
+ See **[CONTRIBUTING.md](CONTRIBUTING.md)** for detailed development guidelines.
92
142
 
93
143
  ---
94
144
 
@@ -46,7 +46,50 @@ pip install -e .
46
46
  ./setup.sh
47
47
  ```
48
48
 
49
- See **[INSTALL.md](INSTALL.md)** for detailed installation options.
49
+ See **[INSTALL.md](docs/INSTALL.md)** for detailed installation options.
50
+
51
+ ---
52
+
53
+ ## Development
54
+
55
+ ### Test-Driven Development (TDD)
56
+
57
+ This project follows TDD principles. All new features must:
58
+
59
+ 1. **Write tests first** - Define expected behavior
60
+ 2. **Run tests (they should fail)** - Red phase
61
+ 3. **Implement feature** - Green phase
62
+ 4. **Refactor** - Clean up code
63
+
64
+ ### Running Tests
65
+
66
+ ```bash
67
+ # Run all tests with coverage
68
+ make test
69
+
70
+ # Run tests in watch mode
71
+ make test-watch
72
+
73
+ # Run specific test file
74
+ pytest tests/test_tasks.py -v
75
+ ```
76
+
77
+ ### Database Environments
78
+
79
+ - **Production**: `~/.business-agent/tasks.db` - Your actual business data 🔒
80
+ - **Development**: `~/.business-agent/dev_tasks.db` - Safe for experimentation ⚙️
81
+ - **Test**: In-memory - Isolated, clean for each test 🧪
82
+
83
+ ```bash
84
+ # Use development database
85
+ export BIZY_ENV=development
86
+ make dev
87
+
88
+ # Use production database (default)
89
+ export BIZY_ENV=production
90
+ ```
91
+
92
+ See **[CONTRIBUTING.md](CONTRIBUTING.md)** for detailed development guidelines.
50
93
 
51
94
  ---
52
95
 
@@ -0,0 +1,456 @@
1
+ #!/usr/bin/env python3
2
+ """Business Agent CLI Tool"""
3
+
4
+ import click
5
+ import os
6
+ import sys
7
+ from agent.tasks import TaskManager
8
+ from agent.planner import BusinessPlanner
9
+ from agent.research import ResearchAgent
10
+ from rich.console import Console
11
+ from rich.table import Table
12
+ from rich.panel import Panel
13
+ from rich.markdown import Markdown
14
+ from rich.prompt import Prompt, Confirm
15
+ from datetime import datetime, timedelta
16
+ from dotenv import load_dotenv
17
+
18
+ load_dotenv()
19
+ console = Console()
20
+
21
+ @click.group()
22
+ def cli():
23
+ """Business Agent CLI - Manage your business from the command line"""
24
+ pass
25
+
26
+ # TASK COMMANDS
27
+ @cli.group()
28
+ def task():
29
+ """Manage tasks"""
30
+ pass
31
+
32
+ @task.command()
33
+ @click.argument('title')
34
+ @click.option('--description', '-d', help='Task description')
35
+ @click.option('--priority', '-p', type=int, default=3)
36
+ @click.option('--category', '-c', help='Task category')
37
+ @click.option('--hours', '-h', type=float, help='Estimated hours')
38
+ @click.option('--goal', '-g', type=int, help='Goal ID to assign task to')
39
+ def add(title, description, priority, category, hours, goal):
40
+ """Add a new task"""
41
+ task_mgr = TaskManager()
42
+ planner = BusinessPlanner()
43
+
44
+ goal_id = goal
45
+
46
+ # If no goal specified, prompt user to select or create one
47
+ if goal_id is None:
48
+ goals = planner.get_active_goals()
49
+
50
+ if goals:
51
+ console.print("\n[bold]📋 Available Goals:[/bold]")
52
+ goals_table = Table(show_header=True, header_style="bold cyan")
53
+ goals_table.add_column("ID", style="dim", width=6)
54
+ goals_table.add_column("Goal")
55
+ goals_table.add_column("Progress", justify="right", width=15)
56
+
57
+ for g in goals[:10]: # Show max 10 goals
58
+ progress_bar = "█" * int(g.progress_percentage / 10) + "░" * (10 - int(g.progress_percentage / 10))
59
+ goals_table.add_row(
60
+ str(g.id),
61
+ g.title[:50],
62
+ f"{progress_bar} {g.progress_percentage:.0f}%"
63
+ )
64
+ console.print(goals_table)
65
+
66
+ # Prompt user
67
+ console.print("\n[bold]Options:[/bold]")
68
+ console.print(" • Enter [cyan]goal ID[/cyan] to assign task to that goal")
69
+ console.print(" • Press [dim]Enter[/dim] to skip (create task without goal)")
70
+ console.print(" • Type [yellow]'new'[/yellow] to create a new goal")
71
+
72
+ choice = Prompt.ask("\n[bold]Your choice[/bold]", default="")
73
+
74
+ if choice.lower() == 'new':
75
+ # Create new goal interactively
76
+ console.print("\n[bold cyan]Creating New Goal[/bold cyan]")
77
+ goal_title = Prompt.ask("Goal title")
78
+ goal_horizon = Prompt.ask(
79
+ "Horizon",
80
+ choices=["daily", "weekly", "monthly", "quarterly", "yearly"],
81
+ default="monthly"
82
+ )
83
+
84
+ try:
85
+ new_goal = planner.create_goal(
86
+ title=goal_title,
87
+ horizon=goal_horizon
88
+ )
89
+ goal_id = new_goal.id
90
+ console.print(f"[green]✓[/green] Created goal: {new_goal.title} (ID: {new_goal.id})\n")
91
+ except Exception as e:
92
+ console.print(f"[red]✗[/red] Error creating goal: {e}")
93
+ console.print("[yellow]Creating task without goal assignment[/yellow]\n")
94
+ goal_id = None
95
+ elif choice and choice.isdigit():
96
+ goal_id = int(choice)
97
+ # Validate goal exists
98
+ goal_obj = planner.get_goal(goal_id)
99
+ if not goal_obj:
100
+ console.print(f"[yellow]⚠[/yellow] Goal #{goal_id} not found. Creating task without goal.\n")
101
+ goal_id = None
102
+ else:
103
+ console.print("\n[yellow]No active goals found.[/yellow]")
104
+ if Confirm.ask("Would you like to create a new goal?", default=False):
105
+ console.print("\n[bold cyan]Creating New Goal[/bold cyan]")
106
+ goal_title = Prompt.ask("Goal title")
107
+ goal_horizon = Prompt.ask(
108
+ "Horizon",
109
+ choices=["daily", "weekly", "monthly", "quarterly", "yearly"],
110
+ default="monthly"
111
+ )
112
+
113
+ try:
114
+ new_goal = planner.create_goal(
115
+ title=goal_title,
116
+ horizon=goal_horizon
117
+ )
118
+ goal_id = new_goal.id
119
+ console.print(f"[green]✓[/green] Created goal: {new_goal.title} (ID: {new_goal.id})\n")
120
+ except Exception as e:
121
+ console.print(f"[red]✗[/red] Error creating goal: {e}\n")
122
+ goal_id = None
123
+
124
+ # Create task with goal assignment
125
+ task = task_mgr.create_task(
126
+ title=title,
127
+ description=description,
128
+ priority=priority,
129
+ category=category,
130
+ estimated_hours=hours,
131
+ parent_goal_id=goal_id
132
+ )
133
+
134
+ console.print(f"[green]✓[/green] Task created: {task.title} (ID: {task.id})")
135
+ if goal_id:
136
+ console.print(f"[dim] Assigned to goal #{goal_id}[/dim]")
137
+ # Recalculate goal progress
138
+ planner.calculate_goal_progress(goal_id)
139
+
140
+ task_mgr.close()
141
+ planner.close()
142
+
143
+ @task.command()
144
+ @click.option('--filter', '-f', type=click.Choice(['all', 'pending', 'completed', 'today']), default='all', help='Filter tasks')
145
+ @click.option('--goal', '-g', type=int, help='Filter by goal ID')
146
+ def list(filter, goal):
147
+ """List tasks with optional filters"""
148
+ task_mgr = TaskManager()
149
+
150
+ # Get tasks based on filter
151
+ if filter == 'all':
152
+ from agent.models import Task
153
+ if goal:
154
+ tasks = task_mgr.session.query(Task).filter_by(parent_goal_id=goal).all()
155
+ else:
156
+ tasks = task_mgr.session.query(Task).all()
157
+ elif filter == 'completed':
158
+ from agent.models import Task
159
+ query = task_mgr.session.query(Task).filter_by(status='completed')
160
+ if goal:
161
+ query = query.filter_by(parent_goal_id=goal)
162
+ tasks = query.all()
163
+ elif filter == 'pending':
164
+ from agent.models import Task
165
+ query = task_mgr.session.query(Task).filter(Task.status.in_(['pending', 'in_progress']))
166
+ if goal:
167
+ query = query.filter_by(parent_goal_id=goal)
168
+ tasks = query.all()
169
+ else: # today
170
+ tasks = task_mgr.get_tasks_for_today()
171
+
172
+ if not tasks:
173
+ console.print(f"[yellow]No {filter} tasks found[/yellow]")
174
+ task_mgr.close()
175
+ return
176
+
177
+ # Build title
178
+ title_parts = ["📋 Your Tasks"]
179
+ if filter != 'all':
180
+ title_parts.append(f"({filter.title()})")
181
+ if goal:
182
+ title_parts.append(f"for Goal #{goal}")
183
+
184
+ table = Table(title=" ".join(title_parts), show_header=True, header_style="bold cyan")
185
+ table.add_column("ID", style="dim")
186
+ table.add_column("Status", justify="center")
187
+ table.add_column("Priority", justify="center")
188
+ table.add_column("Title")
189
+ table.add_column("Category", style="cyan")
190
+ table.add_column("Due Date", style="dim")
191
+
192
+ for task in tasks:
193
+ status_icon = "✓" if task.status == 'completed' else "○"
194
+ priority_str = "🔴" if task.priority == 1 else "🟡" if task.priority == 2 else "🟢"
195
+ due_date = task.due_date.strftime('%Y-%m-%d') if task.due_date else "-"
196
+
197
+ table.add_row(
198
+ str(task.id),
199
+ status_icon,
200
+ priority_str,
201
+ task.title[:50],
202
+ task.category or "-",
203
+ due_date
204
+ )
205
+
206
+ console.print(table)
207
+ task_mgr.close()
208
+
209
+ @task.command()
210
+ @click.argument('task_id', type=int)
211
+ def complete(task_id):
212
+ """Mark a task as complete"""
213
+ task_mgr = TaskManager()
214
+ task = task_mgr.complete_task(task_id)
215
+ if task:
216
+ console.print(f"[green]✓[/green] Completed: {task.title}")
217
+
218
+ # Update goal progress if task is associated with a goal
219
+ if task.parent_goal_id:
220
+ from agent.planner import BusinessPlanner
221
+ planner = BusinessPlanner()
222
+ progress = planner.calculate_goal_progress(task.parent_goal_id)
223
+ console.print(f"[dim]Goal progress updated: {progress:.1f}%[/dim]")
224
+ planner.close()
225
+ else:
226
+ console.print(f"[red]✗[/red] Task {task_id} not found")
227
+ task_mgr.close()
228
+
229
+ # GOAL COMMANDS
230
+ @cli.group()
231
+ def goal():
232
+ """Manage goals"""
233
+ pass
234
+
235
+ @goal.command()
236
+ @click.argument('title')
237
+ @click.option('--description', '-d', help='Goal description')
238
+ @click.option('--horizon', '-h', type=click.Choice(['weekly', 'monthly', 'quarterly', 'yearly']), default='monthly')
239
+ @click.option('--target', '-t', help='Target date (YYYY-MM-DD)')
240
+ def add(title, description, horizon, target):
241
+ """Add a new goal"""
242
+ planner = BusinessPlanner()
243
+ target_date = None
244
+ if target:
245
+ target_date = datetime.strptime(target, '%Y-%m-%d')
246
+
247
+ goal = planner.create_goal(
248
+ title=title,
249
+ description=description,
250
+ horizon=horizon,
251
+ target_date=target_date
252
+ )
253
+ console.print(f"[green]✓[/green] Goal created: {goal.title} (ID: {goal.id})")
254
+ planner.close()
255
+
256
+ @goal.command()
257
+ def list():
258
+ """List all active goals"""
259
+ planner = BusinessPlanner()
260
+ goals = planner.get_active_goals()
261
+
262
+ if not goals:
263
+ console.print("[yellow]No active goals[/yellow]")
264
+ planner.close()
265
+ return
266
+
267
+ # Recalculate progress for all goals
268
+ for goal in goals:
269
+ planner.calculate_goal_progress(goal.id)
270
+
271
+ # Refresh goals after recalculation
272
+ goals = planner.get_active_goals()
273
+
274
+ table = Table(title="🎯 Your Goals", show_header=True, header_style="bold cyan")
275
+ table.add_column("ID", style="dim")
276
+ table.add_column("Title")
277
+ table.add_column("Horizon", style="cyan")
278
+ table.add_column("Progress", justify="right")
279
+
280
+ for goal in goals:
281
+ progress_bar = "█" * int(goal.progress_percentage / 10) + "░" * (10 - int(goal.progress_percentage / 10))
282
+ table.add_row(
283
+ str(goal.id),
284
+ goal.title[:40],
285
+ goal.horizon,
286
+ f"{progress_bar} {goal.progress_percentage:.0f}%"
287
+ )
288
+
289
+ console.print(table)
290
+ planner.close()
291
+
292
+ @goal.command()
293
+ @click.argument('goal_id', type=int)
294
+ def breakdown(goal_id):
295
+ """Break down a goal into tasks using AI"""
296
+ planner = BusinessPlanner()
297
+ console.print(f"[cyan]Breaking down goal {goal_id}...[/cyan]")
298
+ tasks = planner.break_down_goal(goal_id)
299
+
300
+ if tasks:
301
+ console.print(f"[green]✓[/green] Created {len(tasks)} tasks")
302
+ for task in tasks:
303
+ console.print(f" • {task.title}")
304
+ else:
305
+ console.print("[red]✗[/red] Failed to break down goal")
306
+ planner.close()
307
+
308
+ # RESEARCH COMMANDS
309
+ @cli.group()
310
+ def research():
311
+ """Conduct research"""
312
+ pass
313
+
314
+ @research.command()
315
+ @click.argument('topic')
316
+ @click.option('--goal', '-g', help='Business goal')
317
+ def topic(topic, goal):
318
+ """Research a topic"""
319
+ researcher = ResearchAgent()
320
+ console.print(f"[cyan]Researching: {topic}...[/cyan]\n")
321
+
322
+ result = researcher.research_topic(
323
+ topic=topic,
324
+ business_goal=goal or "General research",
325
+ depth="standard"
326
+ )
327
+
328
+ if 'error' in result:
329
+ console.print(f"[red]✗ Error:[/red] {result['error']}")
330
+ else:
331
+ console.print(Panel(
332
+ Markdown(result['findings']),
333
+ title=f"🔍 Research: {topic}",
334
+ border_style="blue"
335
+ ))
336
+ console.print(f"\n[dim]Saved as research ID: {result['research_id']}[/dim]")
337
+ researcher.close()
338
+
339
+ @research.command()
340
+ @click.argument('domain')
341
+ @click.argument('offering')
342
+ def competitors(domain, offering):
343
+ """Research competitors"""
344
+ researcher = ResearchAgent()
345
+ console.print(f"[cyan]Analyzing competitive landscape...[/cyan]\n")
346
+
347
+ result = researcher.research_competitors(domain, offering)
348
+
349
+ if 'error' in result:
350
+ console.print(f"[red]✗ Error:[/red] {result['error']}")
351
+ else:
352
+ console.print(Panel(
353
+ Markdown(result['findings']),
354
+ title="🏆 Competitive Analysis",
355
+ border_style="blue"
356
+ ))
357
+ researcher.close()
358
+
359
+ # STATS COMMAND
360
+ @cli.command()
361
+ def stats():
362
+ """Show statistics"""
363
+ task_mgr = TaskManager()
364
+ weekly_stats = task_mgr.get_weekly_task_stats()
365
+ today_summary = task_mgr.get_daily_summary()
366
+ yesterday_summary = task_mgr.get_yesterday_summary()
367
+ velocity = task_mgr.get_task_velocity(days=7) # Use 7-day velocity to match weekly context
368
+ today_tasks = task_mgr.get_tasks_for_today()
369
+
370
+ console.print("\n[bold cyan]📊 Your Statistics[/bold cyan]\n")
371
+
372
+ # Today's stats
373
+ console.print("[bold]Today:[/bold]")
374
+ console.print(f" • Tasks Completed: {today_summary['tasks_completed']}")
375
+ console.print(f" • Tasks Scheduled: {len(today_tasks)}")
376
+
377
+ # Yesterday's stats
378
+ console.print(f"\n[bold]Yesterday:[/bold]")
379
+ console.print(f" • Tasks Completed: {yesterday_summary['tasks_completed']}")
380
+
381
+ # Weekly stats
382
+ console.print(f"\n[bold]This Week:[/bold]")
383
+ console.print(f" • Tasks Completed: {weekly_stats['tasks_completed_this_week']}")
384
+ console.print(f" • Tasks Created: {weekly_stats['tasks_created_this_week']}")
385
+ console.print(f" • Completion Rate: {weekly_stats['completion_rate']:.1f}%")
386
+ console.print(f" • Total Hours (Estimated): {weekly_stats['total_estimated_hours']:.1f}h")
387
+ if weekly_stats['total_actual_hours'] > 0:
388
+ console.print(f" • Total Hours (Actual): {weekly_stats['total_actual_hours']:.1f}h")
389
+
390
+ console.print(f"\n[bold]Velocity:[/bold] {velocity:.1f} tasks/day\n")
391
+
392
+ # Show breakdown by category if available
393
+ if weekly_stats['categories']:
394
+ console.print("[bold]By Category (This Week):[/bold]")
395
+ for category, count in sorted(weekly_stats['categories'].items(), key=lambda x: x[1], reverse=True):
396
+ console.print(f" • {category}: {count} task(s)")
397
+ console.print()
398
+
399
+ task_mgr.close()
400
+
401
+ # DAILY/WEEKLY REVIEWS
402
+ @cli.command()
403
+ def brief():
404
+ """Generate morning briefing"""
405
+ from agent.morning_brief import run_morning_briefing
406
+ run_morning_briefing()
407
+
408
+ @cli.command()
409
+ def review():
410
+ """Generate evening review"""
411
+ from agent.evening_review import run_evening_review
412
+ run_evening_review()
413
+
414
+ @cli.command()
415
+ def weekly():
416
+ """Generate weekly review"""
417
+ from agent.weekly_review import run_weekly_review
418
+ run_weekly_review()
419
+
420
+ # BUSINESS PLAN MANAGEMENT
421
+ @cli.group()
422
+ def plan():
423
+ """Manage business plan"""
424
+ pass
425
+
426
+ @plan.command()
427
+ def show():
428
+ """Review current business plan and goals"""
429
+ from agent.plan_manager import review_business_plan
430
+ review_business_plan()
431
+
432
+ @plan.command()
433
+ def create():
434
+ """Create new business plan"""
435
+ from agent.plan_manager import create_business_plan
436
+ create_business_plan()
437
+
438
+ @plan.command()
439
+ def update():
440
+ """Update existing business plan"""
441
+ from agent.plan_manager import update_business_plan
442
+ update_business_plan()
443
+
444
+ @plan.command()
445
+ @click.argument('file_path', required=False)
446
+ @click.option('--name', '-n', help='Business plan name')
447
+ def load(file_path, name):
448
+ """Load business plan from YAML file"""
449
+ # Import and run the standalone script
450
+ repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
451
+ sys.path.insert(0, repo_root)
452
+ from scripts.load_business_plan import load_business_plan
453
+ load_business_plan(file_path, name)
454
+
455
+ if __name__ == "__main__":
456
+ cli()
@@ -127,10 +127,10 @@ Be supportive but honest. Keep it concise."""
127
127
  WEEK ENDING: {datetime.now().strftime('%B %d, %Y')}
128
128
 
129
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)}
130
+ - Total Tasks Completed: {weekly_stats.get('tasks_completed_this_week', 0)}
131
+ - Total Tasks Created: {weekly_stats.get('tasks_created_this_week', 0)}
132
+ - Completion Rate: {weekly_stats.get('completion_rate', 0):.1f}%
133
+ - Total Hours (Estimated): {weekly_stats.get('total_estimated_hours', 0):.1f}h
134
134
 
135
135
  GOAL PROGRESS:
136
136
  {goals_progress}
@@ -18,8 +18,8 @@ class Task(Base):
18
18
  estimated_hours = Column(Float)
19
19
  actual_hours = Column(Float)
20
20
  due_date = Column(DateTime)
21
- created_at = Column(DateTime, default=datetime.utcnow)
22
- completed_at = Column(DateTime)
21
+ created_at = Column(DateTime, default=datetime.now) # LOCAL time for consistency
22
+ completed_at = Column(DateTime) # LOCAL time, set by complete_task()
23
23
  parent_goal_id = Column(Integer) # Links to goals
24
24
  dependencies = Column(JSON) # List of task IDs this depends on
25
25
  notes = Column(Text)
@@ -158,8 +158,9 @@ class BusinessMetric(Base):
158
158
 
159
159
  class BusinessPlan(Base):
160
160
  __tablename__ = 'business_plans'
161
-
161
+
162
162
  id = Column(Integer, primary_key=True)
163
+ name = Column(String(255))
163
164
  version = Column(String(50))
164
165
  vision = Column(Text)
165
166
  mission = Column(Text)
@@ -171,6 +172,7 @@ class BusinessPlan(Base):
171
172
  key_partnerships = Column(JSON)
172
173
  cost_structure = Column(JSON)
173
174
  created_at = Column(DateTime, default=datetime.utcnow)
175
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
174
176
  is_active = Column(Boolean, default=True)
175
177
 
176
178
  def to_dict(self):
@@ -193,15 +195,28 @@ class BusinessPlan(Base):
193
195
  # Database setup
194
196
  def get_engine(db_path=None):
195
197
  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
+ # Determine database path based on environment
199
+ env = os.getenv('BIZY_ENV', 'production')
200
+
201
+ if env == 'test':
202
+ # Test environment uses in-memory database (overridden by conftest.py)
203
+ return create_engine('sqlite:///:memory:', echo=False)
204
+ elif env == 'development':
205
+ # Development environment uses separate database
206
+ db_path = os.getenv('BUSINESS_AGENT_DB', os.path.expanduser('~/.business-agent/dev_tasks.db'))
207
+ else: # production
208
+ # Production environment uses main database
209
+ db_path = os.getenv('BUSINESS_AGENT_DB', os.path.expanduser('~/.business-agent/tasks.db'))
198
210
 
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)
211
+ # Create directory if it doesn't exist (not needed for in-memory)
212
+ if db_path and db_path != ':memory:':
213
+ db_dir = os.path.dirname(db_path)
214
+ if db_dir:
215
+ os.makedirs(db_dir, exist_ok=True)
216
+ return create_engine(f'sqlite:///{db_path}', echo=False)
203
217
 
204
- return create_engine(f'sqlite:///{db_path}', echo=False)
218
+ # In-memory database
219
+ return create_engine('sqlite:///:memory:', echo=False)
205
220
 
206
221
  def get_session(engine=None):
207
222
  if engine is None: