-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.py
More file actions
303 lines (231 loc) · 10.1 KB
/
cli.py
File metadata and controls
303 lines (231 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#!/usr/bin/env python3
"""
Command-line interface for Job Agent.
Production-grade CLI with rich formatting.
"""
import click
import json
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.progress import Progress
from rich import print as rprint
from pathlib import Path
from main import create_orchestrator
console = Console()
@click.group()
def cli():
"""AI Job Application Agent - Your autonomous job hunting team"""
pass
@cli.command()
@click.option('--interactive', '-i', is_flag=True, help='Interactive profile creation')
def init_profile(interactive):
"""Initialize or update user profile"""
console.print("\n[bold cyan]Create Your Profile[/bold cyan]\n")
if interactive:
# Interactive mode
profile = {
"user_id": "default_user",
"name": click.prompt("Full Name"),
"skills": click.prompt("Skills (comma-separated)").split(","),
"experience_level": click.prompt(
"Experience Level",
type=click.Choice(['Entry', 'Mid', 'Senior', 'Lead'])
),
"preferred_roles": click.prompt("Preferred Roles (comma-separated)").split(","),
"preferred_locations": click.prompt("Preferred Locations (comma-separated)").split(","),
"job_goal": click.prompt("Job Goal (e.g., 'interviews', 'fast placement', 'high pay')"),
"resume_text": click.prompt("Paste your resume (press Enter twice when done)",
type=str, default="")
}
else:
# Load from example
profile = {
"user_id": "default_user",
"name": "Alex Johnson",
"skills": ["Python", "React", "AWS", "Docker", "PostgreSQL"],
"experience_level": "Mid",
"preferred_roles": ["Software Engineer", "Backend Engineer", "Full Stack Developer"],
"preferred_locations": ["Remote", "San Francisco", "New York"],
"job_goal": "interviews",
"resume_text": """
Alex Johnson
Software Engineer
EXPERIENCE
Senior Software Engineer at TechCo (2021-2024)
- Built scalable microservices handling 1M+ requests/day
- Led migration to Kubernetes, reducing infrastructure costs 40%
- Mentored 3 junior engineers
Software Engineer at StartupXYZ (2019-2021)
- Developed REST APIs using Python/Flask
- Implemented CI/CD pipelines with GitHub Actions
- Improved test coverage from 30% to 85%
EDUCATION
BS Computer Science, State University (2019)
SKILLS
Languages: Python, JavaScript, TypeScript, SQL
Frameworks: React, Node.js, Flask, FastAPI
Cloud: AWS (EC2, S3, Lambda), Docker, Kubernetes
Databases: PostgreSQL, MongoDB, Redis
""".strip()
}
# Save profile
orchestrator = create_orchestrator()
orchestrator.create_user_profile(profile)
console.print("\n[green]✓ Profile created successfully![/green]\n")
console.print(Panel(f"""
[bold]Profile Summary[/bold]
Name: {profile['name']}
Experience: {profile['experience_level']}
Skills: {', '.join(profile['skills'][:5])}
Roles: {', '.join(profile['preferred_roles'])}
Goal: {profile['job_goal']}
""", title="Your Profile"))
@cli.command()
@click.option('--max-jobs', '-n', default=50, help='Maximum jobs to discover')
@click.option('--user', '-u', default='default_user', help='User ID')
def discover(max_jobs, user):
"""Discover relevant job opportunities"""
console.print(f"\n[bold cyan]Discovering Jobs[/bold cyan] (max: {max_jobs})\n")
orchestrator = create_orchestrator()
with Progress() as progress:
task = progress.add_task("[cyan]Discovering jobs...", total=100)
result = orchestrator.run_discovery(user, max_jobs)
progress.update(task, completed=100)
console.print(f"\n[green]✓ Discovery complete![/green]\n")
# Display results
table = Table(title="Discovery Results")
table.add_column("Metric", style="cyan")
table.add_column("Value", style="green")
table.add_row("Jobs Discovered", str(result['jobs_discovered']))
table.add_row("Average Score", f"{result['average_score']:.2f}")
table.add_row("High Quality (>0.8)", str(result['high_quality_matches']))
console.print(table)
if result.get('top_job'):
top = result['top_job']
console.print(Panel(f"""
[bold]{top['title']}[/bold] at {top['company']}
Location: {top['location']}
Score: {top['relevance_score']:.2f}
""", title="Top Match"))
@cli.command()
@click.option('--auto', is_flag=True, help='Auto-apply without confirmation')
@click.option('--min-score', '-s', default=0.65, help='Minimum relevance score')
@click.option('--max-apps', '-n', default=None, type=int, help='Max applications')
@click.option('--user', '-u', default='default_user', help='User ID')
def apply(auto, min_score, max_apps, user):
"""Apply to jobs automatically"""
console.print(f"\n[bold cyan]Submitting Applications[/bold cyan]\n")
if auto:
console.print("[yellow]⚠ Auto-apply mode enabled[/yellow]")
orchestrator = create_orchestrator()
with Progress() as progress:
task = progress.add_task("[cyan]Applying to jobs...", total=100)
result = orchestrator.run_applications(
user,
min_score=min_score,
auto_mode=auto,
max_applications=max_apps
)
progress.update(task, completed=100)
console.print(f"\n[green]✓ Applications submitted![/green]\n")
# Display results
console.print(Panel(f"""
[bold]Application Summary[/bold]
Applications Submitted: {result['applications_submitted']}
Total Applications: {result['total_applications']}
Remaining Today: {result['remaining_today']}
""", title="Results"))
@cli.command()
@click.option('--dry-run', is_flag=True, help='Generate messages without sending')
def followup(dry_run):
"""Send follow-up messages"""
console.print("\n[bold cyan]Processing Follow-ups[/bold cyan]\n")
if dry_run:
console.print("[yellow]Dry run mode - messages will not be sent[/yellow]\n")
orchestrator = create_orchestrator()
result = orchestrator.run_followups(dry_run=dry_run)
console.print(f"\n[green]✓ Follow-ups processed![/green]\n")
console.print(f"Messages generated: {result['followups_sent']}")
@cli.command()
@click.option('--user', '-u', default='default_user', help='User ID')
def status(user):
"""Show current status and statistics"""
console.print("\n[bold cyan]System Status[/bold cyan]\n")
orchestrator = create_orchestrator()
status_data = orchestrator.get_status(user)
# User info
console.print(Panel(f"""
[bold]User Profile[/bold]
Name: {status_data['user']['name']}
Goal: {status_data['user']['job_goal']}
""", title="Profile"))
# Jobs table
jobs_table = Table(title="Jobs")
jobs_table.add_column("Metric", style="cyan")
jobs_table.add_column("Value", style="green")
jobs_table.add_row("Total Discovered", str(status_data['jobs']['total_discovered']))
jobs_table.add_row("High Quality", str(status_data['jobs']['high_quality_matches']))
jobs_table.add_row("Average Score", f"{status_data['jobs']['average_score']:.2f}")
console.print(jobs_table)
# Applications table
apps_table = Table(title="Applications")
apps_table.add_column("Metric", style="cyan")
apps_table.add_column("Value", style="green")
apps_table.add_row("Total", str(status_data['applications']['total']))
apps_table.add_row("Pending", str(status_data['applications']['pending']))
apps_table.add_row("Responses", str(status_data['applications']['responses_received']))
apps_table.add_row("Response Rate", f"{status_data['applications']['response_rate']:.1%}")
console.print(apps_table)
# Suggestions
if status_data.get('suggestions'):
console.print("\n[bold yellow]Suggestions for Improvement:[/bold yellow]")
for i, suggestion in enumerate(status_data['suggestions'], 1):
console.print(f" {i}. {suggestion}")
@cli.command()
@click.option('--max-jobs', '-n', default=50, help='Max jobs to discover')
@click.option('--min-score', '-s', default=0.7, help='Min score to apply')
@click.option('--auto', is_flag=True, help='Enable auto-apply')
@click.option('--user', '-u', default='default_user', help='User ID')
def run(max_jobs, min_score, auto, user):
"""Run full cycle: discover → apply → follow-up"""
console.print("\n[bold cyan]Running Full Cycle[/bold cyan]\n")
orchestrator = create_orchestrator()
with Progress() as progress:
task = progress.add_task("[cyan]Running agents...", total=100)
results = orchestrator.run_full_cycle(
user,
discovery_max=max_jobs,
apply_min_score=min_score,
auto_apply=auto
)
progress.update(task, completed=100)
console.print("\n[green]✓ Full cycle complete![/green]\n")
# Display results
if 'discovery' in results:
console.print(f"Jobs discovered: {results['discovery'].get('jobs_discovered', 0)}")
if 'applications' in results:
console.print(f"Applications submitted: {results['applications'].get('applications_submitted', 0)}")
if 'followups' in results:
console.print(f"Follow-ups sent: {results['followups'].get('followups_sent', 0)}")
# Performance summary
if 'performance' in results:
perf = results['performance']
console.print(Panel(f"""
[bold]Performance Summary[/bold]
Total Applications: {perf.get('total_applications', 0)}
Response Rate: {perf.get('response_rate', 0):.1%}
Recent Activity: {perf.get('applications_last_7_days', 0)} in last 7 days
""", title="Performance"))
@cli.command()
def costs():
"""Show LLM API cost summary"""
from core.llm import get_llm_client
llm = get_llm_client()
total = llm.get_total_cost()
console.print(Panel(f"""
[bold]API Cost Summary[/bold]
Total Spent: ${total:.4f}
""", title="Costs"))
if __name__ == '__main__':
cli()