bizy-ai 1.0.1__tar.gz → 1.1.0__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.1/bizy_ai.egg-info → bizy_ai-1.1.0}/PKG-INFO +52 -2
  2. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/README.md +44 -1
  3. bizy_ai-1.1.0/agent/cli.py +443 -0
  4. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/models.py +23 -8
  5. bizy_ai-1.1.0/agent/plan_manager.py +320 -0
  6. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/tasks.py +98 -13
  7. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/weekly_review.py +10 -10
  8. {bizy_ai-1.0.1 → bizy_ai-1.1.0/bizy_ai.egg-info}/PKG-INFO +52 -2
  9. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/SOURCES.txt +9 -2
  10. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/requires.txt +8 -0
  11. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/pyproject.toml +11 -1
  12. bizy_ai-1.1.0/scripts/load_business_plan.py +110 -0
  13. bizy_ai-1.1.0/scripts/migrate_db.py +40 -0
  14. bizy_ai-1.1.0/scripts/scheduler.py +94 -0
  15. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/setup.py +1 -1
  16. bizy_ai-1.1.0/tests/test_models.py +137 -0
  17. bizy_ai-1.1.0/tests/test_plan_manager.py +73 -0
  18. bizy_ai-1.1.0/tests/test_planner.py +185 -0
  19. bizy_ai-1.1.0/tests/test_tasks.py +205 -0
  20. bizy_ai-1.0.1/agent/cli.py +0 -247
  21. bizy_ai-1.0.1/scripts/agent_cli.py +0 -232
  22. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/.env.example +0 -0
  23. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/LICENSE +0 -0
  24. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/MANIFEST.in +0 -0
  25. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/__init__.py +0 -0
  26. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/core.py +0 -0
  27. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/evening_review.py +0 -0
  28. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/morning_brief.py +0 -0
  29. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/planner.py +0 -0
  30. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/research.py +0 -0
  31. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/dependency_links.txt +0 -0
  32. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/entry_points.txt +0 -0
  33. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/top_level.txt +0 -0
  34. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/requirements.txt +0 -0
  35. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/scripts/init_db.py +0 -0
  36. {bizy_ai-1.0.1 → bizy_ai-1.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bizy-ai
3
- Version: 1.0.1
3
+ Version: 1.1.0
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,443 @@
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
+ velocity = task_mgr.get_task_velocity()
366
+ today_tasks = task_mgr.get_tasks_for_today()
367
+
368
+ console.print("\n[bold cyan]📊 Your Statistics[/bold cyan]\n")
369
+ console.print("[bold]This Week:[/bold]")
370
+ console.print(f" • Tasks Completed: {weekly_stats['tasks_completed_this_week']}")
371
+ console.print(f" • Tasks Created: {weekly_stats['tasks_created_this_week']}")
372
+ console.print(f" • Completion Rate: {weekly_stats['completion_rate']:.1f}%")
373
+ console.print(f" • Total Hours (Estimated): {weekly_stats['total_estimated_hours']:.1f}h")
374
+ if weekly_stats['total_actual_hours'] > 0:
375
+ console.print(f" • Total Hours (Actual): {weekly_stats['total_actual_hours']:.1f}h")
376
+ console.print(f"\n[bold]Velocity:[/bold] {velocity:.1f} tasks/day")
377
+ console.print(f"\n[bold]Today:[/bold] {len(today_tasks)} tasks scheduled\n")
378
+
379
+ # Show breakdown by category if available
380
+ if weekly_stats['categories']:
381
+ console.print("[bold]By Category:[/bold]")
382
+ for category, count in sorted(weekly_stats['categories'].items(), key=lambda x: x[1], reverse=True):
383
+ console.print(f" • {category}: {count} task(s)")
384
+ console.print()
385
+
386
+ task_mgr.close()
387
+
388
+ # DAILY/WEEKLY REVIEWS
389
+ @cli.command()
390
+ def brief():
391
+ """Generate morning briefing"""
392
+ from agent.morning_brief import run_morning_briefing
393
+ run_morning_briefing()
394
+
395
+ @cli.command()
396
+ def review():
397
+ """Generate evening review"""
398
+ from agent.evening_review import run_evening_review
399
+ run_evening_review()
400
+
401
+ @cli.command()
402
+ def weekly():
403
+ """Generate weekly review"""
404
+ from agent.weekly_review import run_weekly_review
405
+ run_weekly_review()
406
+
407
+ # BUSINESS PLAN MANAGEMENT
408
+ @cli.group()
409
+ def plan():
410
+ """Manage business plan"""
411
+ pass
412
+
413
+ @plan.command()
414
+ def show():
415
+ """Review current business plan and goals"""
416
+ from agent.plan_manager import review_business_plan
417
+ review_business_plan()
418
+
419
+ @plan.command()
420
+ def create():
421
+ """Create new business plan"""
422
+ from agent.plan_manager import create_business_plan
423
+ create_business_plan()
424
+
425
+ @plan.command()
426
+ def update():
427
+ """Update existing business plan"""
428
+ from agent.plan_manager import update_business_plan
429
+ update_business_plan()
430
+
431
+ @plan.command()
432
+ @click.argument('file_path', required=False)
433
+ @click.option('--name', '-n', help='Business plan name')
434
+ def load(file_path, name):
435
+ """Load business plan from YAML file"""
436
+ # Import and run the standalone script
437
+ repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
438
+ sys.path.insert(0, repo_root)
439
+ from scripts.load_business_plan import load_business_plan
440
+ load_business_plan(file_path, name)
441
+
442
+ if __name__ == "__main__":
443
+ cli()
@@ -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: