bizy-ai 1.0.2__py3-none-any.whl → 1.1.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/cli.py +184 -12
- agent/models.py +23 -8
- agent/plan_manager.py +14 -3
- agent/tasks.py +98 -13
- agent/weekly_review.py +10 -10
- {bizy_ai-1.0.2.dist-info → bizy_ai-1.1.0.dist-info}/METADATA +52 -2
- bizy_ai-1.1.0.dist-info/RECORD +21 -0
- scripts/load_business_plan.py +110 -0
- scripts/migrate_db.py +40 -0
- scripts/scheduler.py +94 -0
- bizy_ai-1.0.2.dist-info/RECORD +0 -19
- scripts/agent_cli.py +0 -232
- {bizy_ai-1.0.2.dist-info → bizy_ai-1.1.0.dist-info}/WHEEL +0 -0
- {bizy_ai-1.0.2.dist-info → bizy_ai-1.1.0.dist-info}/entry_points.txt +0 -0
- {bizy_ai-1.0.2.dist-info → bizy_ai-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {bizy_ai-1.0.2.dist-info → bizy_ai-1.1.0.dist-info}/top_level.txt +0 -0
agent/cli.py
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
"""Business Agent CLI Tool"""
|
3
3
|
|
4
4
|
import click
|
5
|
+
import os
|
6
|
+
import sys
|
5
7
|
from agent.tasks import TaskManager
|
6
8
|
from agent.planner import BusinessPlanner
|
7
9
|
from agent.research import ResearchAgent
|
@@ -9,6 +11,7 @@ from rich.console import Console
|
|
9
11
|
from rich.table import Table
|
10
12
|
from rich.panel import Panel
|
11
13
|
from rich.markdown import Markdown
|
14
|
+
from rich.prompt import Prompt, Confirm
|
12
15
|
from datetime import datetime, timedelta
|
13
16
|
from dotenv import load_dotenv
|
14
17
|
|
@@ -32,42 +35,172 @@ def task():
|
|
32
35
|
@click.option('--priority', '-p', type=int, default=3)
|
33
36
|
@click.option('--category', '-c', help='Task category')
|
34
37
|
@click.option('--hours', '-h', type=float, help='Estimated hours')
|
35
|
-
|
38
|
+
@click.option('--goal', '-g', type=int, help='Goal ID to assign task to')
|
39
|
+
def add(title, description, priority, category, hours, goal):
|
36
40
|
"""Add a new task"""
|
37
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
|
38
125
|
task = task_mgr.create_task(
|
39
126
|
title=title,
|
40
127
|
description=description,
|
41
128
|
priority=priority,
|
42
129
|
category=category,
|
43
|
-
estimated_hours=hours
|
130
|
+
estimated_hours=hours,
|
131
|
+
parent_goal_id=goal_id
|
44
132
|
)
|
133
|
+
|
45
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
|
+
|
46
140
|
task_mgr.close()
|
141
|
+
planner.close()
|
47
142
|
|
48
143
|
@task.command()
|
49
|
-
|
50
|
-
|
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"""
|
51
148
|
task_mgr = TaskManager()
|
52
|
-
|
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()
|
53
171
|
|
54
172
|
if not tasks:
|
55
|
-
console.print("[yellow]No
|
173
|
+
console.print(f"[yellow]No {filter} tasks found[/yellow]")
|
174
|
+
task_mgr.close()
|
56
175
|
return
|
57
176
|
|
58
|
-
|
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")
|
59
185
|
table.add_column("ID", style="dim")
|
186
|
+
table.add_column("Status", justify="center")
|
60
187
|
table.add_column("Priority", justify="center")
|
61
188
|
table.add_column("Title")
|
62
189
|
table.add_column("Category", style="cyan")
|
190
|
+
table.add_column("Due Date", style="dim")
|
63
191
|
|
64
192
|
for task in tasks:
|
65
|
-
|
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
|
+
|
66
197
|
table.add_row(
|
67
198
|
str(task.id),
|
199
|
+
status_icon,
|
68
200
|
priority_str,
|
69
201
|
task.title[:50],
|
70
|
-
task.category or "-"
|
202
|
+
task.category or "-",
|
203
|
+
due_date
|
71
204
|
)
|
72
205
|
|
73
206
|
console.print(table)
|
@@ -81,6 +214,14 @@ def complete(task_id):
|
|
81
214
|
task = task_mgr.complete_task(task_id)
|
82
215
|
if task:
|
83
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()
|
84
225
|
else:
|
85
226
|
console.print(f"[red]✗[/red] Task {task_id} not found")
|
86
227
|
task_mgr.close()
|
@@ -120,8 +261,16 @@ def list():
|
|
120
261
|
|
121
262
|
if not goals:
|
122
263
|
console.print("[yellow]No active goals[/yellow]")
|
264
|
+
planner.close()
|
123
265
|
return
|
124
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
|
+
|
125
274
|
table = Table(title="🎯 Your Goals", show_header=True, header_style="bold cyan")
|
126
275
|
table.add_column("ID", style="dim")
|
127
276
|
table.add_column("Title")
|
@@ -212,16 +361,28 @@ def competitors(domain, offering):
|
|
212
361
|
def stats():
|
213
362
|
"""Show statistics"""
|
214
363
|
task_mgr = TaskManager()
|
215
|
-
weekly_stats = task_mgr.
|
364
|
+
weekly_stats = task_mgr.get_weekly_task_stats()
|
216
365
|
velocity = task_mgr.get_task_velocity()
|
217
366
|
today_tasks = task_mgr.get_tasks_for_today()
|
218
367
|
|
219
368
|
console.print("\n[bold cyan]📊 Your Statistics[/bold cyan]\n")
|
220
369
|
console.print("[bold]This Week:[/bold]")
|
221
|
-
console.print(f" • Tasks Completed: {weekly_stats['
|
222
|
-
console.print(f" •
|
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")
|
223
376
|
console.print(f"\n[bold]Velocity:[/bold] {velocity:.1f} tasks/day")
|
224
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
|
+
|
225
386
|
task_mgr.close()
|
226
387
|
|
227
388
|
# DAILY/WEEKLY REVIEWS
|
@@ -267,5 +428,16 @@ def update():
|
|
267
428
|
from agent.plan_manager import update_business_plan
|
268
429
|
update_business_plan()
|
269
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
|
+
|
270
442
|
if __name__ == "__main__":
|
271
443
|
cli()
|
agent/models.py
CHANGED
@@ -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:
|
agent/plan_manager.py
CHANGED
@@ -6,11 +6,13 @@ Review and update business plans, goals, and tasks
|
|
6
6
|
|
7
7
|
import sys
|
8
8
|
import os
|
9
|
+
import yaml
|
9
10
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
10
11
|
|
11
12
|
from agent.core import BusinessAgent
|
12
13
|
from agent.planner import BusinessPlanner
|
13
14
|
from agent.tasks import TaskManager
|
15
|
+
from agent.models import get_session, BusinessPlan
|
14
16
|
from rich.console import Console
|
15
17
|
from rich.panel import Panel
|
16
18
|
from rich.table import Table
|
@@ -64,6 +66,13 @@ def review_business_plan():
|
|
64
66
|
console.print("\n[bold]🎯 Goals Overview[/bold]\n")
|
65
67
|
all_goals = planner.get_active_goals()
|
66
68
|
|
69
|
+
# Recalculate progress for all goals
|
70
|
+
for goal in all_goals:
|
71
|
+
planner.calculate_goal_progress(goal.id)
|
72
|
+
|
73
|
+
# Refresh goals after recalculation
|
74
|
+
all_goals = planner.get_active_goals()
|
75
|
+
|
67
76
|
if all_goals:
|
68
77
|
goals_table = Table(show_header=True, header_style="bold cyan")
|
69
78
|
goals_table.add_column("Horizon")
|
@@ -87,11 +96,11 @@ def review_business_plan():
|
|
87
96
|
|
88
97
|
# Get task summary
|
89
98
|
console.print("\n[bold]📋 Task Summary[/bold]\n")
|
90
|
-
weekly_stats = task_mgr.
|
99
|
+
weekly_stats = task_mgr.get_weekly_task_stats()
|
91
100
|
today_tasks = task_mgr.get_tasks_for_today()
|
92
101
|
overdue = task_mgr.get_overdue_tasks()
|
93
102
|
|
94
|
-
console.print(f" • This Week: {weekly_stats['
|
103
|
+
console.print(f" • This Week: {weekly_stats['tasks_completed_this_week']} completed ({weekly_stats['completion_rate']:.1f}% completion rate)")
|
95
104
|
console.print(f" • Today: {len(today_tasks)} tasks scheduled")
|
96
105
|
console.print(f" • Overdue: {len(overdue)} tasks")
|
97
106
|
|
@@ -114,7 +123,9 @@ GOALS PROGRESS:
|
|
114
123
|
{goals_summary}
|
115
124
|
|
116
125
|
EXECUTION METRICS:
|
117
|
-
-
|
126
|
+
- Tasks Completed This Week: {weekly_stats['tasks_completed_this_week']}
|
127
|
+
- Tasks Created This Week: {weekly_stats['tasks_created_this_week']}
|
128
|
+
- Completion Rate: {weekly_stats['completion_rate']:.1f}%
|
118
129
|
- Tasks Overdue: {len(overdue)}
|
119
130
|
|
120
131
|
Provide:
|
agent/tasks.py
CHANGED
@@ -233,19 +233,104 @@ class TaskManager:
|
|
233
233
|
}
|
234
234
|
|
235
235
|
def get_task_velocity(self, days=30):
|
236
|
-
"""
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
if not
|
244
|
-
return 0
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
236
|
+
"""
|
237
|
+
Calculate average tasks completed per day over a period.
|
238
|
+
Uses actual completed_at timestamps for accurate velocity calculation.
|
239
|
+
"""
|
240
|
+
# Get tasks completed in the specified period
|
241
|
+
completed_tasks = self.get_completed_tasks_this_week(days=days)
|
242
|
+
|
243
|
+
if not completed_tasks:
|
244
|
+
return 0.0
|
245
|
+
|
246
|
+
# Calculate velocity as tasks per day
|
247
|
+
velocity = len(completed_tasks) / days
|
248
|
+
return velocity
|
249
|
+
|
250
|
+
def get_completed_tasks_this_week(self, days=7):
|
251
|
+
"""Get tasks completed in the last N days based on completed_at timestamp"""
|
252
|
+
# Use UTC time to match database timestamps
|
253
|
+
now = datetime.utcnow()
|
254
|
+
start_date = now - timedelta(days=days)
|
255
|
+
|
256
|
+
completed_tasks = self.session.query(Task).filter(
|
257
|
+
and_(
|
258
|
+
Task.status == 'completed',
|
259
|
+
Task.completed_at >= start_date,
|
260
|
+
Task.completed_at <= now
|
261
|
+
)
|
262
|
+
).order_by(Task.completed_at.desc()).all()
|
263
|
+
|
264
|
+
return completed_tasks
|
265
|
+
|
266
|
+
def get_created_tasks_this_week(self, days=7):
|
267
|
+
"""Get tasks created in the last N days based on created_at timestamp"""
|
268
|
+
# Use UTC time to match database timestamps
|
269
|
+
now = datetime.utcnow()
|
270
|
+
start_date = now - timedelta(days=days)
|
271
|
+
|
272
|
+
created_tasks = self.session.query(Task).filter(
|
273
|
+
and_(
|
274
|
+
Task.created_at >= start_date,
|
275
|
+
Task.created_at <= now
|
276
|
+
)
|
277
|
+
).order_by(Task.created_at.desc()).all()
|
278
|
+
|
279
|
+
return created_tasks
|
280
|
+
|
281
|
+
def get_weekly_task_stats(self, days=7):
|
282
|
+
"""
|
283
|
+
Get weekly statistics based on actual task completion dates (completed_at).
|
284
|
+
This is more accurate than DailyLog-based stats as it reflects actual work completed.
|
285
|
+
"""
|
286
|
+
completed_tasks = self.get_completed_tasks_this_week(days)
|
287
|
+
created_tasks = self.get_created_tasks_this_week(days)
|
288
|
+
|
289
|
+
# Calculate statistics
|
290
|
+
tasks_completed = len(completed_tasks)
|
291
|
+
tasks_created = len(created_tasks)
|
292
|
+
|
293
|
+
# Calculate completion rate (completed vs created this week)
|
294
|
+
completion_rate = (tasks_completed / tasks_created * 100) if tasks_created > 0 else 0
|
295
|
+
|
296
|
+
# Calculate total estimated hours
|
297
|
+
total_estimated_hours = sum(
|
298
|
+
task.estimated_hours or 0 for task in completed_tasks
|
299
|
+
)
|
300
|
+
|
301
|
+
# Calculate total actual hours
|
302
|
+
total_actual_hours = sum(
|
303
|
+
task.actual_hours or 0 for task in completed_tasks
|
304
|
+
)
|
305
|
+
|
306
|
+
# Break down by category
|
307
|
+
categories = {}
|
308
|
+
for task in completed_tasks:
|
309
|
+
category = task.category or 'uncategorized'
|
310
|
+
if category not in categories:
|
311
|
+
categories[category] = 0
|
312
|
+
categories[category] += 1
|
313
|
+
|
314
|
+
# Break down by priority
|
315
|
+
priorities = {}
|
316
|
+
for task in completed_tasks:
|
317
|
+
priority = task.priority
|
318
|
+
if priority not in priorities:
|
319
|
+
priorities[priority] = 0
|
320
|
+
priorities[priority] += 1
|
321
|
+
|
322
|
+
return {
|
323
|
+
'tasks_completed_this_week': tasks_completed,
|
324
|
+
'tasks_created_this_week': tasks_created,
|
325
|
+
'completion_rate': completion_rate,
|
326
|
+
'total_estimated_hours': total_estimated_hours,
|
327
|
+
'total_actual_hours': total_actual_hours,
|
328
|
+
'completed_tasks': [task.to_dict() for task in completed_tasks],
|
329
|
+
'categories': categories,
|
330
|
+
'priorities': priorities,
|
331
|
+
'period_days': days
|
332
|
+
}
|
333
|
+
|
249
334
|
def close(self):
|
250
335
|
"""Close the database session"""
|
251
336
|
self.session.close()
|
agent/weekly_review.py
CHANGED
@@ -25,22 +25,22 @@ def run_weekly_review():
|
|
25
25
|
task_mgr = TaskManager()
|
26
26
|
planner = BusinessPlanner()
|
27
27
|
|
28
|
-
# Get weekly statistics
|
29
|
-
weekly_stats = task_mgr.
|
30
|
-
|
28
|
+
# Get weekly statistics based on actual task completions
|
29
|
+
weekly_stats = task_mgr.get_weekly_task_stats()
|
30
|
+
|
31
31
|
# Get goal progress
|
32
32
|
active_goals = planner.get_active_goals()
|
33
33
|
goals_progress = "\n".join([
|
34
34
|
f"- {g.title}: {g.progress_percentage:.0f}% complete"
|
35
35
|
for g in active_goals[:5]
|
36
36
|
]) if active_goals else "No active goals"
|
37
|
-
|
38
|
-
# Get key events
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
37
|
+
|
38
|
+
# Get key events from completed tasks
|
39
|
+
completed_tasks = weekly_stats.get('completed_tasks', [])
|
40
|
+
key_events_str = "\n".join([
|
41
|
+
f"✓ {task['title']}" + (f" ({task['category']})" if task['category'] else "")
|
42
|
+
for task in completed_tasks[:10]
|
43
|
+
]) if completed_tasks else "No tasks completed this week"
|
44
44
|
|
45
45
|
# Generate review
|
46
46
|
console.print("[dim]Generating comprehensive analysis...[/dim]\n")
|
@@ -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
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
agent/__init__.py,sha256=pHeIDi-ibuwZqaNqxdIDRvzedWtPywssbaYIDawMcvQ,88
|
2
|
+
agent/cli.py,sha256=J-TlTArbSD2eHkn93Kt7Y7YXZb84NJPGj3XDRgUPsZw,15186
|
3
|
+
agent/core.py,sha256=UILN_DjsSr91KN3YyQaD_cZgF9vW8m827-W0pZMTUnM,6561
|
4
|
+
agent/evening_review.py,sha256=Y4d-t62zfaufM8mnSvkhBBZ0wMw1R8jtj4cN8r30t4E,4929
|
5
|
+
agent/models.py,sha256=Et7GDIW8E-S444j0AbqoFrvNXL4hJ9iWPM989CS34E0,8727
|
6
|
+
agent/morning_brief.py,sha256=zcu4nUOnIQtoZTiK1XlEjuudiUAABrps3PvAwt2Avmk,4732
|
7
|
+
agent/plan_manager.py,sha256=7ipJBW-QS308dSpULjJGcDPjsXKA-yVh1mljFejCv_U,12225
|
8
|
+
agent/planner.py,sha256=-KlyZ-2jMQTUy0QVZCH_NuubENU-UrPXVhat3ju54ew,13114
|
9
|
+
agent/research.py,sha256=HvqGHENQ0v1lJadGYNuf3eb6fZoR6qzzIPzqcsnhFSE,6429
|
10
|
+
agent/tasks.py,sha256=PzisO_2a7Hgplxf3CxnGYLG-Fg8O9ylHYC5193cHyZo,12155
|
11
|
+
agent/weekly_review.py,sha256=83fYwe3hKEhqAQfMlj7WmvvMhTNHtUnqr1PFXCHRGWo,2394
|
12
|
+
bizy_ai-1.1.0.dist-info/licenses/LICENSE,sha256=__BSNgmbeWQ1IA57XKyGhQajUNcF-pZjvBAY268fCWM,1069
|
13
|
+
scripts/init_db.py,sha256=lF1rJAuaeOX19dYQKURzePYWmjkjLPH_4L0D2OiRgQA,3376
|
14
|
+
scripts/load_business_plan.py,sha256=gb4_NPL39PX5yie7zENYgViPBoTqHaLYbTGXPdZU9wM,3560
|
15
|
+
scripts/migrate_db.py,sha256=b51nlLn62nThDGRNqPbxT3jNM2Zdg56hKyymYFTcrUk,1220
|
16
|
+
scripts/scheduler.py,sha256=4LDL7Mb8ZWeOFpy6i8LuMff4HWjFwm10XrcMpLkZmUY,3459
|
17
|
+
bizy_ai-1.1.0.dist-info/METADATA,sha256=9zDRI0w3sQuFbGFPFbZC97Gq0kY-vbeyFMjk99OWjxo,13726
|
18
|
+
bizy_ai-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
19
|
+
bizy_ai-1.1.0.dist-info/entry_points.txt,sha256=yDZc2xFUlCOPuHtAaNissB16AZFzOnOL8xeStkDujAg,39
|
20
|
+
bizy_ai-1.1.0.dist-info/top_level.txt,sha256=k5ce4bNe_tK9tse1lxY4b8nPSipbtgoA28GHmM2ojwk,14
|
21
|
+
bizy_ai-1.1.0.dist-info/RECORD,,
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""Script to load business plan from YAML into the database"""
|
3
|
+
|
4
|
+
import yaml
|
5
|
+
import sys
|
6
|
+
import os
|
7
|
+
from datetime import datetime
|
8
|
+
from agent.models import get_session, BusinessPlan
|
9
|
+
from rich.console import Console
|
10
|
+
from rich.panel import Panel
|
11
|
+
from rich.prompt import Prompt
|
12
|
+
|
13
|
+
console = Console()
|
14
|
+
|
15
|
+
def load_business_plan(file_path=None, plan_name=None):
|
16
|
+
"""Load a business plan from YAML into database"""
|
17
|
+
|
18
|
+
console.print()
|
19
|
+
|
20
|
+
# If no file path provided, request one
|
21
|
+
if not file_path:
|
22
|
+
file_path = Prompt.ask("📁 [bold]Enter path to business plan YAML file[/bold]")
|
23
|
+
|
24
|
+
# Expand user path if needed
|
25
|
+
file_path = os.path.expanduser(file_path)
|
26
|
+
|
27
|
+
# Check if file exists
|
28
|
+
if not os.path.exists(file_path):
|
29
|
+
console.print(f"[bold red]❌ Error: File not found: {file_path}[/bold red]")
|
30
|
+
return False
|
31
|
+
|
32
|
+
# Read the YAML file
|
33
|
+
try:
|
34
|
+
with open(file_path, 'r') as f:
|
35
|
+
file_content = f.read()
|
36
|
+
plan_data = yaml.safe_load(file_content)
|
37
|
+
except Exception as e:
|
38
|
+
console.print(f"[bold red]❌ Error reading YAML file: {e}[/bold red]")
|
39
|
+
return False
|
40
|
+
|
41
|
+
session = get_session()
|
42
|
+
|
43
|
+
try:
|
44
|
+
# Get plan name: CLI option > YAML 'name' field > first # header > prompt user
|
45
|
+
if not plan_name:
|
46
|
+
# Try to get name from YAML 'name' field
|
47
|
+
plan_name = plan_data.get('name')
|
48
|
+
|
49
|
+
if not plan_name:
|
50
|
+
# Try to extract from first # header in file
|
51
|
+
for line in file_content.split('\n'):
|
52
|
+
line = line.strip()
|
53
|
+
if line.startswith('# ') and not line.startswith('##'):
|
54
|
+
plan_name = line[2:].strip()
|
55
|
+
break
|
56
|
+
|
57
|
+
if not plan_name:
|
58
|
+
# Prompt user for name
|
59
|
+
plan_name = Prompt.ask(f"📝 [bold]Enter business plan name[/bold]")
|
60
|
+
|
61
|
+
# Deactivate any existing active plans
|
62
|
+
active_plans = session.query(BusinessPlan).filter_by(is_active=True).all()
|
63
|
+
for plan in active_plans:
|
64
|
+
plan.is_active = False
|
65
|
+
console.print(f"[yellow]⚠ Deactivated existing plan: {plan.name}[/yellow]")
|
66
|
+
|
67
|
+
# Create new business plan
|
68
|
+
new_plan = BusinessPlan(
|
69
|
+
name=plan_name,
|
70
|
+
version=plan_data.get('version', '1.0'),
|
71
|
+
vision=plan_data.get('vision', ''),
|
72
|
+
mission=plan_data.get('mission', ''),
|
73
|
+
value_proposition=plan_data.get('value_proposition', ''),
|
74
|
+
target_market=plan_data.get('target_market', ''),
|
75
|
+
revenue_model=plan_data.get('revenue_model', ''),
|
76
|
+
is_active=True
|
77
|
+
)
|
78
|
+
|
79
|
+
session.add(new_plan)
|
80
|
+
session.commit()
|
81
|
+
|
82
|
+
console.print(Panel(
|
83
|
+
f"[green]✅ Business Plan Loaded Successfully[/green]\n\n"
|
84
|
+
f"Name: {new_plan.name}\n"
|
85
|
+
f"Version: {new_plan.version}\n"
|
86
|
+
f"ID: {new_plan.id}\n"
|
87
|
+
f"Created: {new_plan.created_at.strftime('%Y-%m-%d %H:%M:%S')}\n"
|
88
|
+
f"Active: {'Yes' if new_plan.is_active else 'No'}",
|
89
|
+
title="Success",
|
90
|
+
border_style="green"
|
91
|
+
))
|
92
|
+
|
93
|
+
console.print()
|
94
|
+
return True
|
95
|
+
|
96
|
+
except Exception as e:
|
97
|
+
console.print(f"[bold red]❌ Error creating business plan: {e}[/bold red]")
|
98
|
+
session.rollback()
|
99
|
+
return False
|
100
|
+
finally:
|
101
|
+
session.close()
|
102
|
+
|
103
|
+
if __name__ == "__main__":
|
104
|
+
# Check for command line argument
|
105
|
+
if len(sys.argv) > 1:
|
106
|
+
file_path = sys.argv[1]
|
107
|
+
else:
|
108
|
+
file_path = None
|
109
|
+
|
110
|
+
load_business_plan(file_path)
|
scripts/migrate_db.py
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""Migrate database to add name and updated_at columns to business_plans table"""
|
3
|
+
|
4
|
+
import sqlite3
|
5
|
+
import os
|
6
|
+
|
7
|
+
db_path = os.path.expanduser('~/.business-agent/tasks.db')
|
8
|
+
|
9
|
+
print(f"Migrating database at: {db_path}")
|
10
|
+
|
11
|
+
conn = sqlite3.connect(db_path)
|
12
|
+
cursor = conn.cursor()
|
13
|
+
|
14
|
+
try:
|
15
|
+
# Check if name column exists
|
16
|
+
cursor.execute("PRAGMA table_info(business_plans)")
|
17
|
+
columns = [row[1] for row in cursor.fetchall()]
|
18
|
+
|
19
|
+
if 'name' not in columns:
|
20
|
+
print("Adding 'name' column to business_plans table...")
|
21
|
+
cursor.execute("ALTER TABLE business_plans ADD COLUMN name VARCHAR(255)")
|
22
|
+
print("✅ Added 'name' column")
|
23
|
+
else:
|
24
|
+
print("✓ 'name' column already exists")
|
25
|
+
|
26
|
+
if 'updated_at' not in columns:
|
27
|
+
print("Adding 'updated_at' column to business_plans table...")
|
28
|
+
cursor.execute("ALTER TABLE business_plans ADD COLUMN updated_at DATETIME")
|
29
|
+
print("✅ Added 'updated_at' column")
|
30
|
+
else:
|
31
|
+
print("✓ 'updated_at' column already exists")
|
32
|
+
|
33
|
+
conn.commit()
|
34
|
+
print("\n✅ Database migration completed successfully!")
|
35
|
+
|
36
|
+
except Exception as e:
|
37
|
+
print(f"❌ Error during migration: {e}")
|
38
|
+
conn.rollback()
|
39
|
+
finally:
|
40
|
+
conn.close()
|
scripts/scheduler.py
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Business Agent Main Scheduler
|
4
|
+
Runs automated daily tasks on schedule
|
5
|
+
"""
|
6
|
+
|
7
|
+
import schedule
|
8
|
+
import time
|
9
|
+
import subprocess
|
10
|
+
import sys
|
11
|
+
from datetime import datetime
|
12
|
+
from rich.console import Console
|
13
|
+
from dotenv import load_dotenv
|
14
|
+
|
15
|
+
load_dotenv()
|
16
|
+
|
17
|
+
console = Console()
|
18
|
+
|
19
|
+
def run_script(script_name, description):
|
20
|
+
"""Run a Python script and handle errors"""
|
21
|
+
console.print(f"\n[bold cyan]Running {description}...[/bold cyan]")
|
22
|
+
console.print(f"[dim]{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}[/dim]\n")
|
23
|
+
|
24
|
+
try:
|
25
|
+
result = subprocess.run(
|
26
|
+
[sys.executable, f"scripts/{script_name}"],
|
27
|
+
capture_output=False,
|
28
|
+
text=True
|
29
|
+
)
|
30
|
+
|
31
|
+
if result.returncode == 0:
|
32
|
+
console.print(f"[green]✓ {description} completed successfully[/green]\n")
|
33
|
+
else:
|
34
|
+
console.print(f"[yellow]⚠ {description} finished with warnings[/yellow]\n")
|
35
|
+
|
36
|
+
except Exception as e:
|
37
|
+
console.print(f"[red]✗ Error running {description}: {e}[/red]\n")
|
38
|
+
|
39
|
+
def run_morning_brief():
|
40
|
+
"""Run morning briefing"""
|
41
|
+
run_script("morning_brief.py", "Morning Briefing")
|
42
|
+
|
43
|
+
def run_evening_review():
|
44
|
+
"""Run evening review"""
|
45
|
+
run_script("evening_review.py", "Evening Review")
|
46
|
+
|
47
|
+
def run_weekly_review():
|
48
|
+
"""Run weekly review"""
|
49
|
+
run_script("weekly_review.py", "Weekly Review")
|
50
|
+
|
51
|
+
def main():
|
52
|
+
console.print("\n[bold cyan]═══════════════════════════════════════════[/bold cyan]")
|
53
|
+
console.print("[bold cyan] 🤖 Business Agent Scheduler Started[/bold cyan]")
|
54
|
+
console.print("[bold cyan]═══════════════════════════════════════════[/bold cyan]\n")
|
55
|
+
|
56
|
+
# Schedule jobs
|
57
|
+
# Morning briefing: Monday-Friday at 8:00 AM
|
58
|
+
schedule.every().monday.at("08:00").do(run_morning_brief)
|
59
|
+
schedule.every().tuesday.at("08:00").do(run_morning_brief)
|
60
|
+
schedule.every().wednesday.at("08:00").do(run_morning_brief)
|
61
|
+
schedule.every().thursday.at("08:00").do(run_morning_brief)
|
62
|
+
schedule.every().friday.at("08:00").do(run_morning_brief)
|
63
|
+
|
64
|
+
# Evening review: Monday-Friday at 6:00 PM
|
65
|
+
schedule.every().monday.at("18:00").do(run_evening_review)
|
66
|
+
schedule.every().tuesday.at("18:00").do(run_evening_review)
|
67
|
+
schedule.every().wednesday.at("18:00").do(run_evening_review)
|
68
|
+
schedule.every().thursday.at("18:00").do(run_evening_review)
|
69
|
+
schedule.every().friday.at("18:00").do(run_evening_review)
|
70
|
+
|
71
|
+
# Weekly review: Sunday at 7:00 PM
|
72
|
+
schedule.every().sunday.at("19:00").do(run_weekly_review)
|
73
|
+
|
74
|
+
console.print("[bold]📅 Scheduled Tasks:[/bold]")
|
75
|
+
console.print(" • Morning Briefings: Mon-Fri at 8:00 AM")
|
76
|
+
console.print(" • Evening Reviews: Mon-Fri at 6:00 PM")
|
77
|
+
console.print(" • Weekly Review: Sunday at 7:00 PM")
|
78
|
+
console.print()
|
79
|
+
console.print("[dim]Press Ctrl+C to stop the scheduler[/dim]")
|
80
|
+
console.print()
|
81
|
+
|
82
|
+
# Keep the scheduler running
|
83
|
+
try:
|
84
|
+
while True:
|
85
|
+
schedule.run_pending()
|
86
|
+
time.sleep(60) # Check every minute
|
87
|
+
|
88
|
+
except KeyboardInterrupt:
|
89
|
+
console.print("\n\n[yellow]Scheduler stopped by user[/yellow]")
|
90
|
+
console.print("[dim]All scheduled tasks have been cancelled[/dim]\n")
|
91
|
+
sys.exit(0)
|
92
|
+
|
93
|
+
if __name__ == "__main__":
|
94
|
+
main()
|
bizy_ai-1.0.2.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
agent/__init__.py,sha256=pHeIDi-ibuwZqaNqxdIDRvzedWtPywssbaYIDawMcvQ,88
|
2
|
-
agent/cli.py,sha256=HZ9u3j2NU4lSBS-buP7xGZs_8dwiVLhSV8zJwMrf8bw,7821
|
3
|
-
agent/core.py,sha256=UILN_DjsSr91KN3YyQaD_cZgF9vW8m827-W0pZMTUnM,6561
|
4
|
-
agent/evening_review.py,sha256=Y4d-t62zfaufM8mnSvkhBBZ0wMw1R8jtj4cN8r30t4E,4929
|
5
|
-
agent/models.py,sha256=6bDSLXRxztF50J0AoTi5PX3ckf74oPhyQXfOccfVGdk,7937
|
6
|
-
agent/morning_brief.py,sha256=zcu4nUOnIQtoZTiK1XlEjuudiUAABrps3PvAwt2Avmk,4732
|
7
|
-
agent/plan_manager.py,sha256=x216QjRe9wd9tdBcqBdXX3idU77SqtYeUGN5nxZjrXQ,11832
|
8
|
-
agent/planner.py,sha256=-KlyZ-2jMQTUy0QVZCH_NuubENU-UrPXVhat3ju54ew,13114
|
9
|
-
agent/research.py,sha256=HvqGHENQ0v1lJadGYNuf3eb6fZoR6qzzIPzqcsnhFSE,6429
|
10
|
-
agent/tasks.py,sha256=PaQzRcnzp5HPWVlQKG3KLwdNBNqHTsqCuoGmRwRh8dA,9041
|
11
|
-
agent/weekly_review.py,sha256=ljwy0Aq6yFE_gs8TQjJKCO9Ob59fXsu8L_gDPiRQdmc,2298
|
12
|
-
bizy_ai-1.0.2.dist-info/licenses/LICENSE,sha256=__BSNgmbeWQ1IA57XKyGhQajUNcF-pZjvBAY268fCWM,1069
|
13
|
-
scripts/agent_cli.py,sha256=sG-iRmFZCzm5SkqDtVV1KzZ293SEtvFpY8A1_b69dJU,6971
|
14
|
-
scripts/init_db.py,sha256=lF1rJAuaeOX19dYQKURzePYWmjkjLPH_4L0D2OiRgQA,3376
|
15
|
-
bizy_ai-1.0.2.dist-info/METADATA,sha256=qlo6EBdzrXVemHs64kMvD_YKRs6WJG1hzGI4QfeTM8s,12480
|
16
|
-
bizy_ai-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
17
|
-
bizy_ai-1.0.2.dist-info/entry_points.txt,sha256=yDZc2xFUlCOPuHtAaNissB16AZFzOnOL8xeStkDujAg,39
|
18
|
-
bizy_ai-1.0.2.dist-info/top_level.txt,sha256=k5ce4bNe_tK9tse1lxY4b8nPSipbtgoA28GHmM2ojwk,14
|
19
|
-
bizy_ai-1.0.2.dist-info/RECORD,,
|
scripts/agent_cli.py
DELETED
@@ -1,232 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""Business Agent CLI Tool"""
|
3
|
-
|
4
|
-
import sys
|
5
|
-
import os
|
6
|
-
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
7
|
-
|
8
|
-
import click
|
9
|
-
from agent.tasks import TaskManager
|
10
|
-
from agent.planner import BusinessPlanner
|
11
|
-
from agent.research import ResearchAgent
|
12
|
-
from rich.console import Console
|
13
|
-
from rich.table import Table
|
14
|
-
from rich.panel import Panel
|
15
|
-
from rich.markdown import Markdown
|
16
|
-
from datetime import datetime, timedelta
|
17
|
-
from dotenv import load_dotenv
|
18
|
-
|
19
|
-
load_dotenv()
|
20
|
-
console = Console()
|
21
|
-
|
22
|
-
@click.group()
|
23
|
-
def cli():
|
24
|
-
"""Business Agent CLI - Manage your business from the command line"""
|
25
|
-
pass
|
26
|
-
|
27
|
-
# TASK COMMANDS
|
28
|
-
@cli.group()
|
29
|
-
def task():
|
30
|
-
"""Manage tasks"""
|
31
|
-
pass
|
32
|
-
|
33
|
-
@task.command()
|
34
|
-
@click.argument('title')
|
35
|
-
@click.option('--description', '-d', help='Task description')
|
36
|
-
@click.option('--priority', '-p', type=int, default=3)
|
37
|
-
@click.option('--category', '-c', help='Task category')
|
38
|
-
@click.option('--hours', '-h', type=float, help='Estimated hours')
|
39
|
-
def add(title, description, priority, category, hours):
|
40
|
-
"""Add a new task"""
|
41
|
-
task_mgr = TaskManager()
|
42
|
-
task = task_mgr.create_task(
|
43
|
-
title=title,
|
44
|
-
description=description,
|
45
|
-
priority=priority,
|
46
|
-
category=category,
|
47
|
-
estimated_hours=hours
|
48
|
-
)
|
49
|
-
console.print(f"[green]✓[/green] Task created: {task.title} (ID: {task.id})")
|
50
|
-
task_mgr.close()
|
51
|
-
|
52
|
-
@task.command()
|
53
|
-
def list():
|
54
|
-
"""List all pending tasks"""
|
55
|
-
task_mgr = TaskManager()
|
56
|
-
tasks = task_mgr.get_tasks_for_today()
|
57
|
-
|
58
|
-
if not tasks:
|
59
|
-
console.print("[yellow]No pending tasks[/yellow]")
|
60
|
-
return
|
61
|
-
|
62
|
-
table = Table(title="📋 Your Tasks", show_header=True, header_style="bold cyan")
|
63
|
-
table.add_column("ID", style="dim")
|
64
|
-
table.add_column("Priority", justify="center")
|
65
|
-
table.add_column("Title")
|
66
|
-
table.add_column("Category", style="cyan")
|
67
|
-
|
68
|
-
for task in tasks:
|
69
|
-
priority_str = "🔴" if task.priority == 1 else "🟡" if task.priority == 3 else "🟢"
|
70
|
-
table.add_row(
|
71
|
-
str(task.id),
|
72
|
-
priority_str,
|
73
|
-
task.title[:50],
|
74
|
-
task.category or "-"
|
75
|
-
)
|
76
|
-
|
77
|
-
console.print(table)
|
78
|
-
task_mgr.close()
|
79
|
-
|
80
|
-
@task.command()
|
81
|
-
@click.argument('task_id', type=int)
|
82
|
-
def complete(task_id):
|
83
|
-
"""Mark a task as complete"""
|
84
|
-
task_mgr = TaskManager()
|
85
|
-
task = task_mgr.complete_task(task_id)
|
86
|
-
if task:
|
87
|
-
console.print(f"[green]✓[/green] Completed: {task.title}")
|
88
|
-
else:
|
89
|
-
console.print(f"[red]✗[/red] Task {task_id} not found")
|
90
|
-
task_mgr.close()
|
91
|
-
|
92
|
-
# GOAL COMMANDS
|
93
|
-
@cli.group()
|
94
|
-
def goal():
|
95
|
-
"""Manage goals"""
|
96
|
-
pass
|
97
|
-
|
98
|
-
@goal.command()
|
99
|
-
@click.argument('title')
|
100
|
-
@click.option('--description', '-d', help='Goal description')
|
101
|
-
@click.option('--horizon', '-h', type=click.Choice(['weekly', 'monthly', 'quarterly', 'yearly']), default='monthly')
|
102
|
-
@click.option('--target', '-t', help='Target date (YYYY-MM-DD)')
|
103
|
-
def add(title, description, horizon, target):
|
104
|
-
"""Add a new goal"""
|
105
|
-
planner = BusinessPlanner()
|
106
|
-
target_date = None
|
107
|
-
if target:
|
108
|
-
target_date = datetime.strptime(target, '%Y-%m-%d')
|
109
|
-
|
110
|
-
goal = planner.create_goal(
|
111
|
-
title=title,
|
112
|
-
description=description,
|
113
|
-
horizon=horizon,
|
114
|
-
target_date=target_date
|
115
|
-
)
|
116
|
-
console.print(f"[green]✓[/green] Goal created: {goal.title} (ID: {goal.id})")
|
117
|
-
planner.close()
|
118
|
-
|
119
|
-
@goal.command()
|
120
|
-
def list():
|
121
|
-
"""List all active goals"""
|
122
|
-
planner = BusinessPlanner()
|
123
|
-
goals = planner.get_active_goals()
|
124
|
-
|
125
|
-
if not goals:
|
126
|
-
console.print("[yellow]No active goals[/yellow]")
|
127
|
-
return
|
128
|
-
|
129
|
-
table = Table(title="🎯 Your Goals", show_header=True, header_style="bold cyan")
|
130
|
-
table.add_column("ID", style="dim")
|
131
|
-
table.add_column("Title")
|
132
|
-
table.add_column("Horizon", style="cyan")
|
133
|
-
table.add_column("Progress", justify="right")
|
134
|
-
|
135
|
-
for goal in goals:
|
136
|
-
progress_bar = "█" * int(goal.progress_percentage / 10) + "░" * (10 - int(goal.progress_percentage / 10))
|
137
|
-
table.add_row(
|
138
|
-
str(goal.id),
|
139
|
-
goal.title[:40],
|
140
|
-
goal.horizon,
|
141
|
-
f"{progress_bar} {goal.progress_percentage:.0f}%"
|
142
|
-
)
|
143
|
-
|
144
|
-
console.print(table)
|
145
|
-
planner.close()
|
146
|
-
|
147
|
-
@goal.command()
|
148
|
-
@click.argument('goal_id', type=int)
|
149
|
-
def breakdown(goal_id):
|
150
|
-
"""Break down a goal into tasks using AI"""
|
151
|
-
planner = BusinessPlanner()
|
152
|
-
console.print(f"[cyan]Breaking down goal {goal_id}...[/cyan]")
|
153
|
-
tasks = planner.break_down_goal(goal_id)
|
154
|
-
|
155
|
-
if tasks:
|
156
|
-
console.print(f"[green]✓[/green] Created {len(tasks)} tasks")
|
157
|
-
for task in tasks:
|
158
|
-
console.print(f" • {task.title}")
|
159
|
-
else:
|
160
|
-
console.print("[red]✗[/red] Failed to break down goal")
|
161
|
-
planner.close()
|
162
|
-
|
163
|
-
# RESEARCH COMMANDS
|
164
|
-
@cli.group()
|
165
|
-
def research():
|
166
|
-
"""Conduct research"""
|
167
|
-
pass
|
168
|
-
|
169
|
-
@research.command()
|
170
|
-
@click.argument('topic')
|
171
|
-
@click.option('--goal', '-g', help='Business goal')
|
172
|
-
def topic(topic, goal):
|
173
|
-
"""Research a topic"""
|
174
|
-
researcher = ResearchAgent()
|
175
|
-
console.print(f"[cyan]Researching: {topic}...[/cyan]\n")
|
176
|
-
|
177
|
-
result = researcher.research_topic(
|
178
|
-
topic=topic,
|
179
|
-
business_goal=goal or "General research",
|
180
|
-
depth="standard"
|
181
|
-
)
|
182
|
-
|
183
|
-
if 'error' in result:
|
184
|
-
console.print(f"[red]✗ Error:[/red] {result['error']}")
|
185
|
-
else:
|
186
|
-
console.print(Panel(
|
187
|
-
Markdown(result['findings']),
|
188
|
-
title=f"🔍 Research: {topic}",
|
189
|
-
border_style="blue"
|
190
|
-
))
|
191
|
-
console.print(f"\n[dim]Saved as research ID: {result['research_id']}[/dim]")
|
192
|
-
researcher.close()
|
193
|
-
|
194
|
-
@research.command()
|
195
|
-
@click.argument('domain')
|
196
|
-
@click.argument('offering')
|
197
|
-
def competitors(domain, offering):
|
198
|
-
"""Research competitors"""
|
199
|
-
researcher = ResearchAgent()
|
200
|
-
console.print(f"[cyan]Analyzing competitive landscape...[/cyan]\n")
|
201
|
-
|
202
|
-
result = researcher.research_competitors(domain, offering)
|
203
|
-
|
204
|
-
if 'error' in result:
|
205
|
-
console.print(f"[red]✗ Error:[/red] {result['error']}")
|
206
|
-
else:
|
207
|
-
console.print(Panel(
|
208
|
-
Markdown(result['findings']),
|
209
|
-
title="🏆 Competitive Analysis",
|
210
|
-
border_style="blue"
|
211
|
-
))
|
212
|
-
researcher.close()
|
213
|
-
|
214
|
-
# STATS COMMAND
|
215
|
-
@cli.command()
|
216
|
-
def stats():
|
217
|
-
"""Show statistics"""
|
218
|
-
task_mgr = TaskManager()
|
219
|
-
weekly_stats = task_mgr.get_weekly_stats()
|
220
|
-
velocity = task_mgr.get_task_velocity()
|
221
|
-
today_tasks = task_mgr.get_tasks_for_today()
|
222
|
-
|
223
|
-
console.print("\n[bold cyan]📊 Your Statistics[/bold cyan]\n")
|
224
|
-
console.print("[bold]This Week:[/bold]")
|
225
|
-
console.print(f" • Tasks Completed: {weekly_stats['total_tasks_completed']}")
|
226
|
-
console.print(f" • Completion Rate: {weekly_stats['average_completion_rate']:.0%}")
|
227
|
-
console.print(f"\n[bold]Velocity:[/bold] {velocity:.1f} tasks/day")
|
228
|
-
console.print(f"\n[bold]Today:[/bold] {len(today_tasks)} tasks scheduled\n")
|
229
|
-
task_mgr.close()
|
230
|
-
|
231
|
-
if __name__ == "__main__":
|
232
|
-
cli()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|