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.
- {bizy_ai-1.0.1/bizy_ai.egg-info → bizy_ai-1.1.0}/PKG-INFO +52 -2
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/README.md +44 -1
- bizy_ai-1.1.0/agent/cli.py +443 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/models.py +23 -8
- bizy_ai-1.1.0/agent/plan_manager.py +320 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/tasks.py +98 -13
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/weekly_review.py +10 -10
- {bizy_ai-1.0.1 → bizy_ai-1.1.0/bizy_ai.egg-info}/PKG-INFO +52 -2
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/SOURCES.txt +9 -2
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/requires.txt +8 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/pyproject.toml +11 -1
- bizy_ai-1.1.0/scripts/load_business_plan.py +110 -0
- bizy_ai-1.1.0/scripts/migrate_db.py +40 -0
- bizy_ai-1.1.0/scripts/scheduler.py +94 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/setup.py +1 -1
- bizy_ai-1.1.0/tests/test_models.py +137 -0
- bizy_ai-1.1.0/tests/test_plan_manager.py +73 -0
- bizy_ai-1.1.0/tests/test_planner.py +185 -0
- bizy_ai-1.1.0/tests/test_tasks.py +205 -0
- bizy_ai-1.0.1/agent/cli.py +0 -247
- bizy_ai-1.0.1/scripts/agent_cli.py +0 -232
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/.env.example +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/LICENSE +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/MANIFEST.in +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/__init__.py +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/core.py +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/evening_review.py +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/morning_brief.py +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/planner.py +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/agent/research.py +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/dependency_links.txt +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/entry_points.txt +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/bizy_ai.egg-info/top_level.txt +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/requirements.txt +0 -0
- {bizy_ai-1.0.1 → bizy_ai-1.1.0}/scripts/init_db.py +0 -0
- {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
|
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
|
-
#
|
197
|
-
|
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
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
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:
|