careervivid 1.11.5 → 1.12.1

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.
package/README.md CHANGED
@@ -146,15 +146,24 @@ cv profile export --format gdoc
146
146
 
147
147
  ### `cv jobs`
148
148
 
149
- Automate your job application tracking.
149
+ Automate your job application tracking with AI.
150
150
 
151
151
  | Subcommand | Description |
152
152
  |---|---|
153
- | `cv jobs sync-gmail` | Scan Gmail for applications and sync to a Google Sheet |
153
+ | `cv jobs hunt` | AI-powered job search scored against your resume auto-saves to /job-tracker |
154
+ | `cv jobs update` | Interactively update a job application status on your Kanban board |
155
+ | `cv jobs list` | View your current job tracker board |
156
+ | `cv jobs sync-gmail` | [Legacy] Scan Gmail for applications and sync to a Google Sheet |
154
157
 
155
158
  ```bash
156
- # Sync recent applications to Google Sheets
157
- cv jobs sync-gmail
159
+ # AI-powered job search scored against your resume (uses configuration keys)
160
+ cv jobs hunt --role "Software Engineer" --score 60
161
+
162
+ # View your job tracking board
163
+ cv jobs list
164
+
165
+ # Update status of an application
166
+ cv jobs update
158
167
  ```
159
168
 
160
169
  ---
@@ -202,10 +211,16 @@ View and modify CLI configuration stored at `~/.careervividrc.json`.
202
211
  | `cv config get <key>` | Print a single config value |
203
212
  | `cv config set <key> <value>` | Update a config value |
204
213
 
214
+ **Available Keys:**
215
+ - \`apiKey\`: Your CareerVivid API key.
216
+ - \`geminiKey\`: Your Gemini API key for local AI-powered job application tracking and parsing.
217
+ - \`targetCompanies\`: Comma-separated list of target organizations to focus search on ATS boards (used by \`cv jobs hunt\`).
218
+ - \`apiUrl\`: Optional API endpoint override (default: \`https://careervivid.app/api\`).
219
+
205
220
  ```bash
206
221
  cv config show
207
- cv config get apiKey
208
- cv config set apiUrl https://careervivid.app/api/publish
222
+ cv config get targetCompanies
223
+ cv config set targetCompanies "OpenAI, Google, Vercel"
209
224
  ```
210
225
 
211
226
  ---
@@ -1 +1 @@
1
- {"version":3,"file":"QueryEngine.d.ts","sourceRoot":"","sources":["../../src/agent/QueryEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,uBAAuB,EAAE,OAAO,EAAQ,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;AAMrD,eAAO,MAAM,0BAA0B,QA8B/B,CAAC;AAMT,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACzD,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAyCD;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,GAAE,kBAAuB;IAyBrC,UAAU,IAAI,OAAO,EAAE;IAIvB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE;IAIpC,8CAA8C;IACvC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE;IAW7B,OAAO,CAAC,mBAAmB;YAcb,YAAY;YAOZ,gBAAgB;IA6DjB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IA8E5E;;;;OAIG;IACU,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;CA2EtF"}
1
+ {"version":3,"file":"QueryEngine.d.ts","sourceRoot":"","sources":["../../src/agent/QueryEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,uBAAuB,EAAE,OAAO,EAAQ,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,IAAI,EAAsB,MAAM,WAAW,CAAC;AAMrD,eAAO,MAAM,0BAA0B,QA8B/B,CAAC;AAMT,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACzD,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAyCD;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,GAAE,kBAAuB;IAyBrC,UAAU,IAAI,OAAO,EAAE;IAIvB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE;IAIpC,8CAA8C;IACvC,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE;IAW7B,OAAO,CAAC,mBAAmB;YAcb,YAAY;YAOZ,gBAAgB;IA6DjB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IA8E5E;;;;OAIG;IACU,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;CAqFtF"}
@@ -286,8 +286,18 @@ export class QueryEngine {
286
286
  let accumulatedText = '';
287
287
  let accumulatedFunctionCalls = [];
288
288
  for await (const chunk of streamResponse) {
289
- // Text chunk
290
- const chunkText = chunk.text;
289
+ // Manually extract text to avoid the SDK's "there are non-text parts" warning
290
+ let chunkText = '';
291
+ const parts = chunk.candidates?.[0]?.content?.parts || [];
292
+ if (parts.length > 0) {
293
+ chunkText = parts
294
+ .filter(p => p.text && !p.functionCall && !p.thought)
295
+ .map(p => p.text)
296
+ .join('');
297
+ }
298
+ else if (chunk.text && !chunk.text.includes('there are non-text parts')) {
299
+ chunkText = chunk.text;
300
+ }
291
301
  if (chunkText) {
292
302
  accumulatedText += chunkText;
293
303
  if (hooks?.onChunk)
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Job-hunting tools for the CareerVivid Job Agent.
3
+ *
4
+ * These tools wrap the existing jobs API methods so the Gemini agent can
5
+ * autonomously hunt for jobs, save them to the tracker, and update statuses
6
+ * using natural language instructions from the user.
7
+ */
8
+ import { Tool } from "../Tool.js";
9
+ export declare const SearchJobsTool: Tool;
10
+ export declare const SaveJobTool: Tool;
11
+ export declare const ListJobsTool: Tool;
12
+ export declare const UpdateJobStatusTool: Tool;
13
+ export declare const GetResumeTool: Tool;
14
+ export declare const ListResumesTool: Tool;
15
+ export declare const ALL_JOB_TOOLS: Tool[];
16
+ //# sourceMappingURL=jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/jobs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAiBlC,eAAO,MAAM,cAAc,EAAE,IA0F5B,CAAC;AAMF,eAAO,MAAM,WAAW,EAAE,IA4EzB,CAAC;AAMF,eAAO,MAAM,YAAY,EAAE,IAgE1B,CAAC;AAMF,eAAO,MAAM,mBAAmB,EAAE,IAwDjC,CAAC;AAMF,eAAO,MAAM,aAAa,EAAE,IA8B3B,CAAC;AAMF,eAAO,MAAM,eAAe,EAAE,IA6B7B,CAAC;AAMF,eAAO,MAAM,aAAa,EAAE,IAAI,EAO/B,CAAC"}
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Job-hunting tools for the CareerVivid Job Agent.
3
+ *
4
+ * These tools wrap the existing jobs API methods so the Gemini agent can
5
+ * autonomously hunt for jobs, save them to the tracker, and update statuses
6
+ * using natural language instructions from the user.
7
+ */
8
+ import { Type } from "@google/genai";
9
+ import { jobsHunt, jobsCreate, jobsList, jobsUpdate, resumeGet, resumesList, isApiError, } from "../../api.js";
10
+ // ---------------------------------------------------------------------------
11
+ // Tool: search_jobs
12
+ // ---------------------------------------------------------------------------
13
+ export const SearchJobsTool = {
14
+ name: "search_jobs",
15
+ description: `Search for jobs that match a given role and location.
16
+ The search automatically scores results against the user's CareerVivid resume.
17
+ Returns a list of scored job opportunities with AI summaries and missing skills.
18
+ Use this when the user asks to "find jobs", "search for roles", "look for positions", etc.`,
19
+ parameters: {
20
+ type: Type.OBJECT,
21
+ properties: {
22
+ role: {
23
+ type: Type.STRING,
24
+ description: 'The job title or role to search for. Examples: "Software Engineer", "Product Manager", "Data Scientist".',
25
+ },
26
+ location: {
27
+ type: Type.STRING,
28
+ description: 'Optional. Preferred job location. Examples: "San Francisco, CA", "Remote", "New York". Default is remote-friendly if omitted.',
29
+ },
30
+ count: {
31
+ type: Type.NUMBER,
32
+ description: "Optional. Number of job results to return. Default is 10. Maximum is 20.",
33
+ },
34
+ min_score: {
35
+ type: Type.NUMBER,
36
+ description: "Optional. Minimum match score (0-100) to include in results. Use 60 for good matches, 80 for excellent matches only.",
37
+ },
38
+ resume_id: {
39
+ type: Type.STRING,
40
+ description: "Optional. Specific resume ID to score against. If omitted, uses the user's latest resume.",
41
+ },
42
+ },
43
+ required: ["role"],
44
+ },
45
+ execute: async (args) => {
46
+ // Optionally fetch resume content to include in the scoring request
47
+ let resumeContent;
48
+ try {
49
+ const resumeResult = await resumeGet(args.resume_id);
50
+ if (!isApiError(resumeResult)) {
51
+ resumeContent = resumeResult.cvMarkdown;
52
+ }
53
+ }
54
+ catch {
55
+ // Proceed without resume — backend will use stored resume
56
+ }
57
+ const result = await jobsHunt({
58
+ resumeContent,
59
+ role: args.role,
60
+ location: args.location,
61
+ count: args.count ?? 10,
62
+ minScore: args.min_score,
63
+ });
64
+ if (isApiError(result)) {
65
+ return `Error searching jobs: ${result.message}`;
66
+ }
67
+ if (result.jobs.length === 0) {
68
+ return `No jobs found matching "${args.role}"${args.location ? ` in ${args.location}` : ""}. Try broadening your search or adjusting the location.`;
69
+ }
70
+ const jobList = result.jobs
71
+ .map((job, i) => {
72
+ const scoreBar = "█".repeat(Math.round(job.score / 10)) + "░".repeat(10 - Math.round(job.score / 10));
73
+ const missing = job.missingSkills.length > 0 ? `\n Missing skills: ${job.missingSkills.join(", ")}` : "";
74
+ return (`${i + 1}. [${job.scoreLabel.toUpperCase()} ${job.score}/100] ${job.title} @ ${job.company}\n` +
75
+ ` Score: ${scoreBar} ${job.score}%\n` +
76
+ ` Location: ${job.location || "Not specified"}${job.salary ? ` | Salary: ${job.salary}` : ""}\n` +
77
+ ` Summary: ${job.aiSummary}${missing}\n` +
78
+ ` URL: ${job.url}\n` +
79
+ ` Job ID: ${job.id}`);
80
+ })
81
+ .join("\n\n");
82
+ return (`Found ${result.total} jobs for "${args.role}"${args.location ? ` in ${args.location}` : ""}.\n` +
83
+ `Showing top ${result.jobs.length} results:\n\n${jobList}`);
84
+ },
85
+ };
86
+ // ---------------------------------------------------------------------------
87
+ // Tool: save_job
88
+ // ---------------------------------------------------------------------------
89
+ export const SaveJobTool = {
90
+ name: "save_job",
91
+ description: `Save a job to the user's CareerVivid job tracker Kanban board.
92
+ The job will appear in the "To Apply" column of the /job-tracker page.
93
+ Use this after finding interesting jobs via search_jobs, or when the user asks to "save", "add", or "track" a specific job.`,
94
+ parameters: {
95
+ type: Type.OBJECT,
96
+ properties: {
97
+ job_title: {
98
+ type: Type.STRING,
99
+ description: "The job title / position name.",
100
+ },
101
+ company_name: {
102
+ type: Type.STRING,
103
+ description: "The name of the company.",
104
+ },
105
+ location: {
106
+ type: Type.STRING,
107
+ description: "Optional. Job location (city, state, or 'Remote').",
108
+ },
109
+ job_url: {
110
+ type: Type.STRING,
111
+ description: "Optional. URL to the job posting.",
112
+ },
113
+ job_description: {
114
+ type: Type.STRING,
115
+ description: "Optional. Brief description or key details of the role.",
116
+ },
117
+ ai_score: {
118
+ type: Type.NUMBER,
119
+ description: "Optional. AI match score (0-100) from search_jobs results.",
120
+ },
121
+ ai_summary: {
122
+ type: Type.STRING,
123
+ description: "Optional. AI-generated summary of the job from search_jobs results.",
124
+ },
125
+ notes: {
126
+ type: Type.STRING,
127
+ description: "Optional. Personal notes about this job opportunity.",
128
+ },
129
+ },
130
+ required: ["job_title", "company_name"],
131
+ },
132
+ execute: async (args) => {
133
+ const result = await jobsCreate({
134
+ jobTitle: args.job_title,
135
+ companyName: args.company_name,
136
+ location: args.location,
137
+ jobPostURL: args.job_url,
138
+ jobDescription: args.job_description,
139
+ aiScore: args.ai_score,
140
+ aiSummary: args.ai_summary,
141
+ notes: args.notes,
142
+ });
143
+ if (isApiError(result)) {
144
+ return `Error saving job: ${result.message}`;
145
+ }
146
+ return (`✅ Saved "${args.job_title}" at ${args.company_name} to your job tracker!\n` +
147
+ ` Status: To Apply\n` +
148
+ ` Job ID: ${result.id}\n` +
149
+ ` View it at: https://careervivid.app/job-tracker\n\n` +
150
+ `Agent Instruction: Please tell the user that the job is saved and remind them that they need to apply for it. Also let them know that you can help them update the job application status (To Apply, Applied, Interviewing, Offered, Rejected) whenever they are ready.`);
151
+ },
152
+ };
153
+ // ---------------------------------------------------------------------------
154
+ // Tool: list_jobs
155
+ // ---------------------------------------------------------------------------
156
+ export const ListJobsTool = {
157
+ name: "list_jobs",
158
+ description: `List the user's jobs from their CareerVivid job tracker Kanban board.
159
+ Can filter by status. Use this when the user asks "what jobs do I have?", "show my tracker",
160
+ "what's in my job pipeline?", "check my interviews", etc.`,
161
+ parameters: {
162
+ type: Type.OBJECT,
163
+ properties: {
164
+ status: {
165
+ type: Type.STRING,
166
+ description: 'Optional. Filter by application status. One of: "To Apply", "Applied", "Interviewing", "Offered", "Rejected". If omitted, returns all jobs.',
167
+ },
168
+ },
169
+ },
170
+ execute: async (args) => {
171
+ const validStatuses = [
172
+ "To Apply", "Applied", "Interviewing", "Offered", "Rejected",
173
+ ];
174
+ const status = validStatuses.includes(args.status)
175
+ ? args.status
176
+ : undefined;
177
+ const result = await jobsList(status);
178
+ if (isApiError(result)) {
179
+ return `Error fetching job tracker: ${result.message}`;
180
+ }
181
+ if (result.jobs.length === 0) {
182
+ const statusMsg = status ? ` with status "${status}"` : "";
183
+ return `Your job tracker is empty${statusMsg}. Use search_jobs to find opportunities and save_job to add them!`;
184
+ }
185
+ // Group by status for a clear overview
186
+ const grouped = {};
187
+ for (const job of result.jobs) {
188
+ const s = job.applicationStatus;
189
+ if (!grouped[s])
190
+ grouped[s] = [];
191
+ grouped[s].push(job);
192
+ }
193
+ const statusOrder = ["To Apply", "Applied", "Interviewing", "Offered", "Rejected"];
194
+ const lines = [`Your Job Tracker (${result.total} total):\n`];
195
+ for (const s of statusOrder) {
196
+ const jobs = grouped[s];
197
+ if (!jobs || jobs.length === 0)
198
+ continue;
199
+ lines.push(`── ${s.toUpperCase()} (${jobs.length}) ──`);
200
+ for (const job of jobs) {
201
+ const score = job.aiScore !== null ? ` [${job.aiScore}%]` : "";
202
+ const updated = job.updatedAt
203
+ ? ` | Updated: ${new Date(job.updatedAt).toLocaleDateString()}`
204
+ : "";
205
+ lines.push(` • ${job.jobTitle} @ ${job.companyName}${score} | ${job.location || "N/A"}${updated}`);
206
+ lines.push(` ID: ${job.id} | URL: ${job.jobPostURL || "N/A"}`);
207
+ if (job.notes)
208
+ lines.push(` Notes: ${job.notes}`);
209
+ }
210
+ lines.push("");
211
+ }
212
+ return lines.join("\n");
213
+ },
214
+ };
215
+ // ---------------------------------------------------------------------------
216
+ // Tool: update_job_status
217
+ // ---------------------------------------------------------------------------
218
+ export const UpdateJobStatusTool = {
219
+ name: "update_job_status",
220
+ description: `Move a job to a different status on the CareerVivid Kanban board.
221
+ Use this when the user mentions: "I got an interview", "I applied to X", "I got an offer",
222
+ "X rejected me", "move Y to applied", etc. The change will be visible in the /job-tracker UI.`,
223
+ parameters: {
224
+ type: Type.OBJECT,
225
+ properties: {
226
+ job_id: {
227
+ type: Type.STRING,
228
+ description: "The job ID to update. Get this from list_jobs or from a previous save_job call.",
229
+ },
230
+ new_status: {
231
+ type: Type.STRING,
232
+ description: 'The new application status. Must be one of: "To Apply", "Applied", "Interviewing", "Offered", "Rejected".',
233
+ },
234
+ notes: {
235
+ type: Type.STRING,
236
+ description: "Optional. Add or update notes about this status change (e.g. interview date, interviewer name, feedback).",
237
+ },
238
+ },
239
+ required: ["job_id", "new_status"],
240
+ },
241
+ execute: async (args) => {
242
+ const validStatuses = [
243
+ "To Apply", "Applied", "Interviewing", "Offered", "Rejected",
244
+ ];
245
+ if (!validStatuses.includes(args.new_status)) {
246
+ return (`Invalid status "${args.new_status}". ` +
247
+ `Valid options are: ${validStatuses.join(", ")}.`);
248
+ }
249
+ const result = await jobsUpdate({
250
+ jobId: args.job_id,
251
+ status: args.new_status,
252
+ notes: args.notes,
253
+ });
254
+ if (isApiError(result)) {
255
+ return `Error updating job: ${result.message}`;
256
+ }
257
+ const notesMsg = args.notes ? `\n Notes: "${args.notes}"` : "";
258
+ return (`✅ Job tracker updated!\n` +
259
+ ` Job ID: ${result.jobId}\n` +
260
+ ` New Status: ${result.newStatus}${notesMsg}\n` +
261
+ ` View your board at: https://careervivid.app/job-tracker`);
262
+ },
263
+ };
264
+ // ---------------------------------------------------------------------------
265
+ // Tool: get_resume
266
+ // ---------------------------------------------------------------------------
267
+ export const GetResumeTool = {
268
+ name: "get_resume",
269
+ description: `Fetch the user's resume from their CareerVivid profile.
270
+ Use this to understand the user's background, skills, and experience when giving
271
+ personalized job advice, tailoring cover letters, or analyzing job fit.`,
272
+ parameters: {
273
+ type: Type.OBJECT,
274
+ properties: {
275
+ resume_id: {
276
+ type: Type.STRING,
277
+ description: "Optional. Specific resume ID to retrieve. If omitted, fetches the latest resume.",
278
+ },
279
+ },
280
+ },
281
+ execute: async (args) => {
282
+ const result = await resumeGet(args.resume_id);
283
+ if (isApiError(result)) {
284
+ return `Error fetching resume: ${result.message}`;
285
+ }
286
+ const updated = result.updatedAt
287
+ ? `\nLast updated: ${new Date(result.updatedAt).toLocaleDateString()}`
288
+ : "";
289
+ return (`Resume: "${result.title}" (ID: ${result.resumeId})${updated}\n\n` +
290
+ `--- RESUME CONTENT ---\n${result.cvMarkdown}\n--- END OF RESUME ---`);
291
+ },
292
+ };
293
+ // ---------------------------------------------------------------------------
294
+ // Tool: list_resumes
295
+ // ---------------------------------------------------------------------------
296
+ export const ListResumesTool = {
297
+ name: "list_resumes",
298
+ description: `List all of the user's resumes on CareerVivid.
299
+ Use this when the user asks "how many resumes do I have?", "list my resumes", or "show my resumes".
300
+ This returns lightweight metadata including resume ID and title, which can be useful when calling search_jobs or get_resume.`,
301
+ parameters: {
302
+ type: Type.OBJECT,
303
+ properties: {},
304
+ },
305
+ execute: async () => {
306
+ const result = await resumesList();
307
+ if (isApiError(result)) {
308
+ return `Error fetching resumes: ${result.message}`;
309
+ }
310
+ if (result.total === 0 || !result.resumes || result.resumes.length === 0) {
311
+ return "You don't have any resumes uploaded yet. Visit CareerVivid to upload one!";
312
+ }
313
+ const lines = [`You have ${result.total} resume(s) on CareerVivid:\n`];
314
+ for (const res of result.resumes) {
315
+ const updated = res.updatedAt ? ` (Updated: ${new Date(res.updatedAt).toLocaleDateString()})` : "";
316
+ lines.push(`- "${res.title}" [ID: ${res.id}]${updated}`);
317
+ }
318
+ lines.push("\nUse get_resume with a specific ID to view its content.");
319
+ return lines.join("\n");
320
+ },
321
+ };
322
+ // ---------------------------------------------------------------------------
323
+ // All job tools export
324
+ // ---------------------------------------------------------------------------
325
+ export const ALL_JOB_TOOLS = [
326
+ GetResumeTool,
327
+ ListResumesTool,
328
+ SearchJobsTool,
329
+ SaveJobTool,
330
+ ListJobsTool,
331
+ UpdateJobStatusTool,
332
+ ];
package/dist/api.d.ts CHANGED
@@ -72,4 +72,90 @@ export declare function uploadPortfolioAsset(image: string, path: string, mimeTy
72
72
  downloadUrl: string;
73
73
  } | ApiError>;
74
74
  export declare function isApiError(v: unknown): v is ApiError;
75
+ export type ApplicationStatus = "To Apply" | "Applied" | "Interviewing" | "Offered" | "Rejected";
76
+ export interface ScoredJob {
77
+ id: string;
78
+ title: string;
79
+ company: string;
80
+ location: string;
81
+ description: string;
82
+ url: string;
83
+ salary?: string;
84
+ score: number;
85
+ scoreLabel: "Excellent" | "Good" | "Fair" | "Low";
86
+ aiSummary: string;
87
+ missingSkills: string[];
88
+ }
89
+ export interface JobTrackerItem {
90
+ id: string;
91
+ jobTitle: string;
92
+ companyName: string;
93
+ location: string;
94
+ applicationStatus: ApplicationStatus;
95
+ aiScore: number | null;
96
+ jobPostURL: string;
97
+ updatedAt: string | null;
98
+ notes: string;
99
+ }
100
+ export interface ResumeResult {
101
+ resumeId: string;
102
+ title: string;
103
+ cvMarkdown: string;
104
+ updatedAt: string | null;
105
+ }
106
+ export interface ResumesListResult {
107
+ resumes: {
108
+ id: string;
109
+ title: string;
110
+ updatedAt: string | null;
111
+ }[];
112
+ total: number;
113
+ }
114
+ /** Fetch the user's latest resume from CareerVivid via API key */
115
+ export declare function resumeGet(resumeId?: string): Promise<ResumeResult | ApiError>;
116
+ /** Fetch a lightweight list of the user's resumes */
117
+ export declare function resumesList(): Promise<ResumesListResult | ApiError>;
118
+ /** Run an agentic job search scored against the resume */
119
+ export declare function jobsHunt(payload: {
120
+ resumeContent?: string;
121
+ role: string;
122
+ location?: string;
123
+ count?: number;
124
+ minScore?: number;
125
+ targetOrgs?: string[];
126
+ }): Promise<{
127
+ jobs: ScoredJob[];
128
+ total: number;
129
+ } | ApiError>;
130
+ /** Add a job to the user's Kanban tracker */
131
+ export declare function jobsCreate(payload: {
132
+ jobTitle: string;
133
+ companyName: string;
134
+ location?: string;
135
+ jobPostURL?: string;
136
+ jobDescription?: string;
137
+ aiScore?: number;
138
+ aiSummary?: string;
139
+ notes?: string;
140
+ }): Promise<{
141
+ success: boolean;
142
+ id: string;
143
+ message: string;
144
+ } | ApiError>;
145
+ /** Move a job to a new status on the Kanban board */
146
+ export declare function jobsUpdate(payload: {
147
+ jobId: string;
148
+ status: ApplicationStatus;
149
+ notes?: string;
150
+ }): Promise<{
151
+ success: boolean;
152
+ jobId: string;
153
+ newStatus: string;
154
+ message: string;
155
+ } | ApiError>;
156
+ /** List jobs currently in the user's tracker */
157
+ export declare function jobsList(status?: ApplicationStatus): Promise<{
158
+ jobs: JobTrackerItem[];
159
+ total: number;
160
+ } | ApiError>;
75
161
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/api.js CHANGED
@@ -151,3 +151,73 @@ export async function uploadPortfolioAsset(image, path, mimeType) {
151
151
  export function isApiError(v) {
152
152
  return typeof v === "object" && v !== null && v.isError === true;
153
153
  }
154
+ // ── Cloud Function base URL ───────────────────────────────────────────────────
155
+ // The CLI job-hunt endpoints are deployed as standalone Cloud Functions,
156
+ // not via the /api proxy used by publishPost. This mirrors the verifyAuth pattern.
157
+ const CLI_FUNCTIONS_BASE = process.env.CV_FUNCTIONS_URL ||
158
+ "https://us-west1-jastalk-firebase.cloudfunctions.net";
159
+ async function cfRequest(method, functionName, body, queryParams) {
160
+ const apiKey = requireApiKey();
161
+ let url = `${CLI_FUNCTIONS_BASE}/${functionName}`;
162
+ if (queryParams) {
163
+ const qs = new URLSearchParams(queryParams).toString();
164
+ if (qs)
165
+ url += `?${qs}`;
166
+ }
167
+ const response = await fetch(url, {
168
+ method,
169
+ headers: {
170
+ "Content-Type": "application/json",
171
+ "x-api-key": apiKey,
172
+ "User-Agent": `careervivid-cli/${CLI_VERSION}`,
173
+ },
174
+ body: body ? JSON.stringify(body) : undefined,
175
+ });
176
+ const text = await response.text();
177
+ let parsed = {};
178
+ try {
179
+ parsed = JSON.parse(text);
180
+ }
181
+ catch {
182
+ parsed = { message: text };
183
+ }
184
+ if (!response.ok) {
185
+ return {
186
+ isError: true,
187
+ statusCode: response.status,
188
+ message: parsed.error || parsed.message || `HTTP ${response.status}`,
189
+ };
190
+ }
191
+ return parsed;
192
+ }
193
+ // ── Job Hunt API Methods ──────────────────────────────────────────────────────
194
+ /** Fetch the user's latest resume from CareerVivid via API key */
195
+ export async function resumeGet(resumeId) {
196
+ const params = {};
197
+ if (resumeId)
198
+ params.resumeId = resumeId;
199
+ return cfRequest("GET", "cliResumeGet", undefined, Object.keys(params).length ? params : undefined);
200
+ }
201
+ /** Fetch a lightweight list of the user's resumes */
202
+ export async function resumesList() {
203
+ return cfRequest("GET", "cliResumesList");
204
+ }
205
+ /** Run an agentic job search scored against the resume */
206
+ export async function jobsHunt(payload) {
207
+ return cfRequest("POST", "cliJobsHunt", payload);
208
+ }
209
+ /** Add a job to the user's Kanban tracker */
210
+ export async function jobsCreate(payload) {
211
+ return cfRequest("POST", "cliJobsCreate", payload);
212
+ }
213
+ /** Move a job to a new status on the Kanban board */
214
+ export async function jobsUpdate(payload) {
215
+ return cfRequest("POST", "cliJobsUpdate", payload);
216
+ }
217
+ /** List jobs currently in the user's tracker */
218
+ export async function jobsList(status) {
219
+ const params = {};
220
+ if (status)
221
+ params.status = status;
222
+ return cfRequest("GET", "cliJobsList", undefined, Object.keys(params).length ? params : undefined);
223
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyFpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA2OpD"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2FpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA2SpD"}