careervivid 1.12.45 → 1.12.48
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/dist/agent/instructions.d.ts.map +1 -1
- package/dist/agent/instructions.js +24 -3
- package/dist/agent/tools/coding.d.ts.map +1 -1
- package/dist/agent/tools/coding.js +49 -4
- package/dist/agent/tools/jobOpenings.js +6 -6
- package/dist/agent/tools/local-tracker.js +3 -3
- package/dist/branding.d.ts.map +1 -1
- package/dist/branding.js +14 -6
- package/dist/commands/referral.d.ts.map +1 -1
- package/dist/commands/referral.js +53 -43
- package/dist/eval/runner.js +3 -3
- package/dist/eval/storage/CsvDataLogger.d.ts +2 -2
- package/dist/eval/storage/CsvDataLogger.js +6 -6
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/agent/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,eAAO,MAAM,aAAa,QAalB,CAAC;AAMT,eAAO,MAAM,cAAc,QAcnB,CAAC;AAMT,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../src/agent/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,eAAO,MAAM,aAAa,QAalB,CAAC;AAMT,eAAO,MAAM,cAAc,QAcnB,CAAC;AAMT,eAAO,MAAM,cAAc,QAqCnB,CAAC;AAMT,eAAO,MAAM,kBAAkB,QAwCvB,CAAC;AAMT,eAAO,MAAM,YAAY,QA2CjB,CAAC;AAMT,eAAO,MAAM,iBAAiB,QAiBtB,CAAC;AAOT;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,CAkCT"}
|
|
@@ -51,11 +51,32 @@ export const CODING_SECTION = `
|
|
|
51
51
|
|
|
52
52
|
You have access to file I/O, shell execution, and codebase search tools.
|
|
53
53
|
|
|
54
|
+
### ⚠️ File Access Scope (STRICT — do not bypass)
|
|
55
|
+
|
|
56
|
+
You operate with least-privilege file access. This is enforced at the tool level and cannot be overridden.
|
|
57
|
+
|
|
58
|
+
**READ access** is limited to:
|
|
59
|
+
- \`career-vivid/\` — your job tracker data, résumé drafts, and career pipeline files
|
|
60
|
+
- \`cli/\` — the CLI source code (your own code)
|
|
61
|
+
- \`tmp/\` or \`/tmp/\` — temporary scratch files
|
|
62
|
+
|
|
63
|
+
**WRITE access** is limited to:
|
|
64
|
+
- \`career-vivid/\` — ONLY the career data directory you own
|
|
65
|
+
- \`tmp/\` or \`/tmp/\` — temporary scratch files
|
|
66
|
+
|
|
67
|
+
**NEVER attempt to read or write:**
|
|
68
|
+
- \`src/\` — web app source code (React, components, pages)
|
|
69
|
+
- \`functions/\` — Firebase Cloud Functions source
|
|
70
|
+
- \`next-app/\` — Next.js application source
|
|
71
|
+
- Any file outside your allowed prefixes
|
|
72
|
+
|
|
73
|
+
If a task requires modifying web app code (\`src/\`, \`functions/\`, etc.), tell the user to make that change in their editor. Your role is career data management, not application development.
|
|
74
|
+
|
|
54
75
|
### Workflow
|
|
55
76
|
1. Read relevant files first (read_file). Never overwrite blindly.
|
|
56
77
|
2. Emit a short "Plan:" describing files you will touch and the approach.
|
|
57
|
-
3. Write
|
|
58
|
-
4. Verify with run_command (
|
|
78
|
+
3. Write data using write_file or patch_file (career-vivid/ only).
|
|
79
|
+
4. Verify with run_command (read-only commands like cat, ls, grep).
|
|
59
80
|
5. Fix errors and loop until clean; summarise all changes made.
|
|
60
81
|
|
|
61
82
|
### Code Quality
|
|
@@ -82,7 +103,7 @@ export const JOBS_TOOLS_SECTION = `
|
|
|
82
103
|
- **openings_apply** — Mark a specific opening as Applied (requires explicit date).
|
|
83
104
|
|
|
84
105
|
### CSV Pipeline Tracker (tracker_*)
|
|
85
|
-
These tools read/write jobs.csv — the local career-
|
|
106
|
+
These tools read/write jobs.csv — the local career-vivid pipeline spreadsheet.
|
|
86
107
|
- **tracker_list_jobs** — Show the pipeline (supports tier/status filters and sort_by).
|
|
87
108
|
- **tracker_add_job** — Add a new company to the tracker.
|
|
88
109
|
- **tracker_update_job** — Update any field on a job entry (status, scores, notes, follow-up).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coding.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/coding.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"coding.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/coding.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAyGlC,iBAAS,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAG3C;AAMD,eAAO,MAAM,YAAY,EAAE,IAqC1B,CAAC;AAMF,eAAO,MAAM,aAAa,EAAE,IA0B3B,CAAC;AAMF,eAAO,MAAM,aAAa,EAAE,IA+C3B,CAAC;AAMF,eAAO,MAAM,iBAAiB,EAAE,IA8C/B,CAAC;AAMF,eAAO,MAAM,eAAe,EAAE,IA8C7B,CAAC;AAMF,eAAO,MAAM,cAAc,EAAE,IAmD5B,CAAC;AAEF,iEAAiE;AACjE,wBAAgB,yBAAyB,IAAI,IAAI,CAQhD;AAMD,eAAO,MAAM,eAAe,EAAE,IAkD7B,CAAC;AAMF,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,EAAE,IAAI,EAQlC,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -1,8 +1,53 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync } from 'fs';
|
|
3
|
-
import { resolve, dirname, relative, join } from 'path';
|
|
3
|
+
import { resolve, dirname, relative, join, normalize } from 'path';
|
|
4
4
|
import { Type } from '@google/genai';
|
|
5
5
|
// ============================================================================
|
|
6
|
+
// Path-based Access Control (Least Privilege)
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Directories the agent is ALLOWED to READ.
|
|
10
|
+
* These are relative to the repo root (process.cwd()) or absolute paths the agent owns.
|
|
11
|
+
*/
|
|
12
|
+
const READ_ALLOWED_PREFIXES = [
|
|
13
|
+
// Agent's own data & config
|
|
14
|
+
'career-vivid/', // job tracker CSV, résumé drafts, career data
|
|
15
|
+
'cli/', // CLI source — agent may read its own code
|
|
16
|
+
// Scratch / temporary outputs produced by the agent
|
|
17
|
+
'tmp/',
|
|
18
|
+
'/tmp/',
|
|
19
|
+
// User's home config file is read by loadConfig() — not by read_file tool
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Directories the agent is ALLOWED to WRITE.
|
|
23
|
+
* This is intentionally much narrower than the read list.
|
|
24
|
+
* Source code (src/, functions/, next-app/) is completely off-limits.
|
|
25
|
+
*/
|
|
26
|
+
const WRITE_ALLOWED_PREFIXES = [
|
|
27
|
+
'career-vivid/', // job tracker CSV, résumé drafts — the only structured data the agent owns
|
|
28
|
+
'tmp/',
|
|
29
|
+
'/tmp/',
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a raw path argument and check it against an allowlist.
|
|
33
|
+
* @throws if the resolved path falls outside every allowed prefix.
|
|
34
|
+
*/
|
|
35
|
+
function guardPath(rawPath, allowedPrefixes, mode) {
|
|
36
|
+
const cwd = process.cwd();
|
|
37
|
+
const absPath = normalize(resolve(rawPath));
|
|
38
|
+
const allowed = allowedPrefixes.some(prefix => {
|
|
39
|
+
const fullPrefix = prefix.startsWith('/') ? normalize(prefix) : normalize(join(cwd, prefix));
|
|
40
|
+
return absPath.startsWith(fullPrefix);
|
|
41
|
+
});
|
|
42
|
+
if (!allowed) {
|
|
43
|
+
const rel = relative(cwd, absPath);
|
|
44
|
+
throw new Error(`⛔ Access denied: ${mode} access to "${rel}" is not permitted.\n` +
|
|
45
|
+
`The agent can only ${mode} files inside: ${allowedPrefixes.join(', ')}\n` +
|
|
46
|
+
`To access this file, use your editor or the CareerVivid web app.`);
|
|
47
|
+
}
|
|
48
|
+
return absPath;
|
|
49
|
+
}
|
|
50
|
+
// ============================================================================
|
|
6
51
|
// Safe list for run_command (no confirmation required)
|
|
7
52
|
// ============================================================================
|
|
8
53
|
const SAFE_COMMAND_PREFIXES = [
|
|
@@ -77,7 +122,7 @@ export const readFileTool = {
|
|
|
77
122
|
required: ['path'],
|
|
78
123
|
},
|
|
79
124
|
execute: async (args) => {
|
|
80
|
-
const absPath =
|
|
125
|
+
const absPath = guardPath(args.path, READ_ALLOWED_PREFIXES, 'read');
|
|
81
126
|
if (!existsSync(absPath)) {
|
|
82
127
|
throw new Error(`File not found: ${absPath}`);
|
|
83
128
|
}
|
|
@@ -114,7 +159,7 @@ export const writeFileTool = {
|
|
|
114
159
|
required: ['path', 'content'],
|
|
115
160
|
},
|
|
116
161
|
execute: async (args) => {
|
|
117
|
-
const absPath =
|
|
162
|
+
const absPath = guardPath(args.path, WRITE_ALLOWED_PREFIXES, 'write');
|
|
118
163
|
mkdirSync(dirname(absPath), { recursive: true });
|
|
119
164
|
writeFileSync(absPath, args.content, 'utf-8');
|
|
120
165
|
const lines = args.content.split('\n').length;
|
|
@@ -151,7 +196,7 @@ export const patchFileTool = {
|
|
|
151
196
|
required: ['path', 'start_line', 'end_line', 'new_content'],
|
|
152
197
|
},
|
|
153
198
|
execute: async (args) => {
|
|
154
|
-
const absPath =
|
|
199
|
+
const absPath = guardPath(args.path, WRITE_ALLOWED_PREFIXES, 'write');
|
|
155
200
|
if (!existsSync(absPath))
|
|
156
201
|
throw new Error(`File not found: ${absPath}`);
|
|
157
202
|
const lines = readFileSync(absPath, 'utf-8').split('\n');
|
|
@@ -45,9 +45,9 @@ function getJobsCsvPath() {
|
|
|
45
45
|
const __dirpath = dirname(__filename);
|
|
46
46
|
// 1. Check dev repo locations (for local development)
|
|
47
47
|
const devCandidates = [
|
|
48
|
-
resolve(__dirpath, "../../../../career-
|
|
49
|
-
resolve(__dirpath, "../../../../../career-
|
|
50
|
-
resolve(process.cwd(), "career-
|
|
48
|
+
resolve(__dirpath, "../../../../career-vivid/data/jobs.csv"),
|
|
49
|
+
resolve(__dirpath, "../../../../../career-vivid/data/jobs.csv"),
|
|
50
|
+
resolve(process.cwd(), "career-vivid/data/jobs.csv"),
|
|
51
51
|
];
|
|
52
52
|
for (const p of devCandidates) {
|
|
53
53
|
if (existsSync(p))
|
|
@@ -61,9 +61,9 @@ function getOpeningsPath() {
|
|
|
61
61
|
const __dirpath = dirname(__filename);
|
|
62
62
|
// Mirror next to jobs.csv
|
|
63
63
|
const devCandidates = [
|
|
64
|
-
resolve(__dirpath, "../../../../career-
|
|
65
|
-
resolve(__dirpath, "../../../../../career-
|
|
66
|
-
resolve(process.cwd(), "career-
|
|
64
|
+
resolve(__dirpath, "../../../../career-vivid/data/job_openings.csv"),
|
|
65
|
+
resolve(__dirpath, "../../../../../career-vivid/data/job_openings.csv"),
|
|
66
|
+
resolve(process.cwd(), "career-vivid/data/job_openings.csv"),
|
|
67
67
|
];
|
|
68
68
|
for (const p of devCandidates) {
|
|
69
69
|
if (existsSync(p))
|
|
@@ -102,9 +102,9 @@ function getCsvPath() {
|
|
|
102
102
|
const __dirname = dirname(__filename);
|
|
103
103
|
// 1. Check dev repo locations first (for existing users/contributors)
|
|
104
104
|
const devCandidates = [
|
|
105
|
-
resolve(__dirname, "../../../../career-
|
|
106
|
-
resolve(__dirname, "../../../../../career-
|
|
107
|
-
resolve(process.cwd(), "career-
|
|
105
|
+
resolve(__dirname, "../../../../career-vivid/data/jobs.csv"),
|
|
106
|
+
resolve(__dirname, "../../../../../career-vivid/data/jobs.csv"),
|
|
107
|
+
resolve(process.cwd(), "career-vivid/data/jobs.csv"),
|
|
108
108
|
];
|
|
109
109
|
for (const p of devCandidates) {
|
|
110
110
|
if (existsSync(p))
|
package/dist/branding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../src/branding.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../src/branding.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,MAAM;;;;;;CAMlB,CAAC;AAMF,eAAO,MAAM,UAAU,sXAMtB,CAAC;AAEF,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAID,wBAAgB,YAAY,IAAI,IAAI,CA6BnC;AAID,wBAAgB,aAAa,IAAI,MAAM,CAOtC"}
|
package/dist/branding.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import boxen from "boxen";
|
|
3
3
|
import gradient from "gradient-string";
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import { join, dirname } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
4
9
|
// ── Colors ───────────────────────────────────────────────────────────────────
|
|
5
10
|
export const COLORS = {
|
|
6
11
|
primary: "#3b82f6", // Blue-500
|
|
@@ -26,22 +31,25 @@ export function printWelcome() {
|
|
|
26
31
|
const logo = getBrandedLogo();
|
|
27
32
|
const content = `
|
|
28
33
|
${chalk.bold("Welcome to the CareerVivid CLI!")}
|
|
29
|
-
${chalk.dim("Your
|
|
34
|
+
${chalk.dim("Your AI-powered career management command-center.")}
|
|
30
35
|
|
|
31
36
|
${chalk.white("To get started, run:")}
|
|
32
|
-
${chalk.cyan(" cv
|
|
37
|
+
${chalk.cyan(" cv login")}
|
|
33
38
|
|
|
34
39
|
${chalk.dim("Quick Commands:")}
|
|
35
|
-
${chalk.white("• cv
|
|
36
|
-
${chalk.white("• cv
|
|
37
|
-
${chalk.white("• cv
|
|
40
|
+
${chalk.white("• cv agent")} Start the AI career agent
|
|
41
|
+
${chalk.white("• cv jobs hunt")} Search & score job openings
|
|
42
|
+
${chalk.white("• cv resumes list")} View your AI-parsed resumes
|
|
43
|
+
${chalk.white("• cv referral")} View your referral dashboard
|
|
44
|
+
${chalk.white("• cv publish <file>")} Publish to CareerVivid
|
|
45
|
+
${chalk.white("• cv help")} Show all commands
|
|
38
46
|
`;
|
|
39
47
|
console.log(boxen(logo + "\n" + content, {
|
|
40
48
|
padding: 1,
|
|
41
49
|
margin: 1,
|
|
42
50
|
borderStyle: "round",
|
|
43
51
|
borderColor: COLORS.primary,
|
|
44
|
-
title: chalk.bold.blue(
|
|
52
|
+
title: chalk.bold.blue(` v${pkg?.version ?? "latest"} `),
|
|
45
53
|
titleAlignment: "right",
|
|
46
54
|
}));
|
|
47
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"referral.d.ts","sourceRoot":"","sources":["../../src/commands/referral.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"referral.d.ts","sourceRoot":"","sources":["../../src/commands/referral.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAsGvD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import boxen from "boxen";
|
|
1
2
|
import chalk from "chalk";
|
|
2
3
|
import ora from "ora";
|
|
3
4
|
import { getReferralStats, isApiError } from "../api.js";
|
|
@@ -8,75 +9,84 @@ function getProgressBar(current, total, width = 30) {
|
|
|
8
9
|
const emptyLength = width - filledLength;
|
|
9
10
|
const filledBar = '█'.repeat(filledLength);
|
|
10
11
|
const emptyBar = '░'.repeat(emptyLength);
|
|
11
|
-
return `[${chalk.
|
|
12
|
+
return `[${chalk.cyan(filledBar)}${chalk.dim(emptyBar)}]`;
|
|
12
13
|
}
|
|
13
14
|
export function registerReferralCommand(program) {
|
|
14
15
|
const referralCmd = program
|
|
15
16
|
.command("referral")
|
|
16
17
|
.description("Manage your CareerVivid referral code, track progress, and view rewards")
|
|
17
|
-
.option("--code", "
|
|
18
|
-
.option("--link", "
|
|
19
|
-
.option("--draft-message", "
|
|
20
|
-
.option("--status", "
|
|
21
|
-
.option("--list", "
|
|
18
|
+
.option("-c, --code", "Retrieve and display your unique referral code")
|
|
19
|
+
.option("-l, --link", "Generate and display your full shareable referral URL")
|
|
20
|
+
.option("-d, --draft-message", "Generate a highly-converting draft message for your network")
|
|
21
|
+
.option("-s, --status", "Display current state of active referrals (clicks, signups, progress)")
|
|
22
|
+
.option("-i, --list", "Output a detailed list of your historical referral data")
|
|
22
23
|
.option("--json", "Output raw JSON data")
|
|
23
24
|
.action(async (opts) => {
|
|
24
25
|
const isJson = opts.json ?? process.argv.includes("--json");
|
|
25
|
-
const spinner = ora(
|
|
26
|
+
const spinner = ora({
|
|
27
|
+
text: chalk.dim("Fetching referral ecosystem data..."),
|
|
28
|
+
color: "cyan"
|
|
29
|
+
}).start();
|
|
26
30
|
const result = await getReferralStats();
|
|
27
31
|
if (isApiError(result)) {
|
|
28
|
-
spinner.fail("Failed to
|
|
32
|
+
spinner.fail(chalk.red("Failed to sync referral data."));
|
|
29
33
|
printError(result.message, undefined, isJson);
|
|
30
34
|
process.exit(1);
|
|
31
35
|
}
|
|
32
|
-
spinner.
|
|
33
|
-
const baseUrl = "https://careervivid.app/
|
|
36
|
+
spinner.succeed(chalk.dim("Referral data synchronized successfully."));
|
|
37
|
+
const baseUrl = "https://careervivid.app/referral?ref=";
|
|
34
38
|
const fullLink = `${baseUrl}${result.code}`;
|
|
35
39
|
if (isJson) {
|
|
36
|
-
console.log(JSON.stringify({ ...result, fullLink }));
|
|
40
|
+
console.log(JSON.stringify({ ...result, fullLink }, null, 2));
|
|
37
41
|
return;
|
|
38
42
|
}
|
|
39
|
-
//
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
// If no specific flag is provided, show the summary dashboard
|
|
44
|
+
const showSummary = !opts.code && !opts.link && !opts.draftMessage && !opts.status && !opts.list;
|
|
45
|
+
console.log(""); // Spacer
|
|
46
|
+
// ── Flag: --code ──────────────────────────────────────────────────
|
|
47
|
+
if (opts.code) {
|
|
48
|
+
console.log(boxen(`${chalk.cyan.bold("REFERRAL CODE")}\n\n${chalk.white.bold.bgCyan(" " + result.code + " ")}`, { padding: 1, margin: 0, borderStyle: "round", borderColor: "cyan" }));
|
|
43
49
|
}
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
// ── Flag: --link ──────────────────────────────────────────────────
|
|
51
|
+
if (opts.link) {
|
|
52
|
+
console.log(boxen(`${chalk.cyan.bold("SHAREABLE LINK")}\n\n${chalk.underline.blue(fullLink)}`, { padding: 1, margin: 0, borderStyle: "round", borderColor: "blue" }));
|
|
46
53
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
console.log(
|
|
51
|
-
console.log();
|
|
52
|
-
console.log(` Use my exclusive code ${chalk.bold(result.code)} to get 2 Months of Premium for free.`);
|
|
53
|
-
console.log();
|
|
54
|
-
console.log(` Sign up here: ${fullLink}`);
|
|
55
|
-
console.log(chalk.dim(` --------------------------------------------------`));
|
|
54
|
+
// ── Flag: --draft-message ─────────────────────────────────────────
|
|
55
|
+
if (opts.draftMessage) {
|
|
56
|
+
const message = `Accelerate Your Career Path with CareerVivid! 🚀\n\nI've been using CareerVivid to automate my job tracker and resume building. It's a game-changer.\n\nUse my exclusive code ${result.code} to get 2 MONTHS of PRO for FREE.\n\nClaim it here: ${fullLink}`;
|
|
57
|
+
console.log(boxen(`${chalk.magenta.bold("HIGH-CONVERTING DRAFT")}\n\n${chalk.white(message)}`, { padding: 1, margin: 0, borderStyle: "round", borderColor: "magenta", title: "Copy & Paste" }));
|
|
56
58
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
// ── Flag: --status ───────────────────────────────────────────────
|
|
60
|
+
if (opts.status || showSummary) {
|
|
61
|
+
const remaining = Math.max(0, result.maxReferrals - result.totalReferred);
|
|
62
|
+
const statusContent = [
|
|
63
|
+
`${chalk.yellow.bold("REDEMPTION PROGRESS")}`,
|
|
64
|
+
`${getProgressBar(result.totalReferred, result.maxReferrals)} ${chalk.bold(`${result.totalReferred}/${result.maxReferrals}`)}`,
|
|
65
|
+
`${chalk.dim(`${remaining} referrals remaining to maximize rewards`)}`,
|
|
66
|
+
"",
|
|
67
|
+
`${chalk.cyan.bold("ACTIVE REWARDS")}`,
|
|
68
|
+
`${chalk.green("✔")} ${chalk.white("THEY GET:")} 2 Months Pro + 1000 AI Credits`,
|
|
69
|
+
`${chalk.green("✔")} ${chalk.white("YOU GET:")} 1 Month Pro extension per signup`
|
|
70
|
+
].join("\n");
|
|
71
|
+
console.log(boxen(statusContent, { padding: 1, borderStyle: "double", borderColor: "yellow" }));
|
|
65
72
|
}
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
// ── Flag: --list ──────────────────────────────────────────────────
|
|
74
|
+
if (opts.list) {
|
|
75
|
+
const listTitle = chalk.blue.bold("REFERRAL HISTORY");
|
|
68
76
|
if (result.referredUsers.length === 0) {
|
|
69
|
-
console.log(
|
|
77
|
+
console.log(boxen(`${listTitle}\n\n${chalk.dim("No successful referrals recorded yet.\nStart sharing to earn Pro extensions!")}`, { padding: 1, borderStyle: "round", borderColor: "blue" }));
|
|
70
78
|
}
|
|
71
79
|
else {
|
|
72
|
-
result.referredUsers.
|
|
73
|
-
const date = u.signupDate
|
|
74
|
-
|
|
75
|
-
: "Recently";
|
|
76
|
-
console.log(` ${chalk.dim(`${i + 1}.`)} ${chalk.white(u.email)} ${chalk.dim(`(Joined: ${date})`)} ${chalk.green('✓ active')}`);
|
|
80
|
+
const rows = result.referredUsers.map((u, i) => {
|
|
81
|
+
const date = u.signupDate ? new Date(u.signupDate).toLocaleDateString() : "Recently";
|
|
82
|
+
return `${chalk.dim(`${i + 1}.`)} ${chalk.white(u.email.padEnd(25))} ${chalk.dim(`[${date}]`)} ${chalk.green("● active")}`;
|
|
77
83
|
});
|
|
84
|
+
console.log(boxen(`${listTitle}\n\n${rows.join("\n")}`, { padding: 1, borderStyle: "round", borderColor: "blue" }));
|
|
78
85
|
}
|
|
79
86
|
}
|
|
80
|
-
|
|
87
|
+
if (showSummary) {
|
|
88
|
+
console.log(chalk.dim(` Use ${chalk.cyan('cv referral --help')} to see all available flags.`));
|
|
89
|
+
}
|
|
90
|
+
console.log(""); // Final spacer
|
|
81
91
|
});
|
|
82
92
|
}
|
package/dist/eval/runner.js
CHANGED
|
@@ -63,9 +63,9 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
63
63
|
const __dirname = dirname(__filename);
|
|
64
64
|
function findJobsCsvPath() {
|
|
65
65
|
const candidates = [
|
|
66
|
-
resolve(__dirname, "../../../../career-
|
|
67
|
-
resolve(__dirname, "../../../../../career-
|
|
68
|
-
resolve(process.cwd(), "career-
|
|
66
|
+
resolve(__dirname, "../../../../career-vivid/data/jobs.csv"),
|
|
67
|
+
resolve(__dirname, "../../../../../career-vivid/data/jobs.csv"),
|
|
68
|
+
resolve(process.cwd(), "career-vivid/data/jobs.csv"),
|
|
69
69
|
];
|
|
70
70
|
return candidates.find(existsSync) ?? null;
|
|
71
71
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Writes one row per EvalResult to a dated CSV file at:
|
|
5
5
|
* <outputDir>/results-YYYY-MM-DD.csv
|
|
6
6
|
*
|
|
7
|
-
* Default output directory: career-
|
|
7
|
+
* Default output directory: career-vivid/eval/
|
|
8
8
|
*
|
|
9
9
|
* Features:
|
|
10
10
|
* - Append-safe: if the file already exists from an earlier run today, new
|
|
@@ -18,7 +18,7 @@ import type { EvalResult, RunSummary } from "../types.js";
|
|
|
18
18
|
export interface CsvDataLoggerOptions {
|
|
19
19
|
/**
|
|
20
20
|
* Directory to write CSV files into.
|
|
21
|
-
* Defaults to <repo_root>/career-
|
|
21
|
+
* Defaults to <repo_root>/career-vivid/eval/
|
|
22
22
|
*/
|
|
23
23
|
outputDir?: string;
|
|
24
24
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Writes one row per EvalResult to a dated CSV file at:
|
|
5
5
|
* <outputDir>/results-YYYY-MM-DD.csv
|
|
6
6
|
*
|
|
7
|
-
* Default output directory: career-
|
|
7
|
+
* Default output directory: career-vivid/eval/
|
|
8
8
|
*
|
|
9
9
|
* Features:
|
|
10
10
|
* - Append-safe: if the file already exists from an earlier run today, new
|
|
@@ -61,17 +61,17 @@ function defaultOutputDir() {
|
|
|
61
61
|
const __dirname = dirname(__filename);
|
|
62
62
|
// From dist/eval/storage/ or src/eval/storage/, go up 4 levels to repo root
|
|
63
63
|
const candidates = [
|
|
64
|
-
resolve(__dirname, "../../../../career-
|
|
65
|
-
resolve(__dirname, "../../../../../career-
|
|
66
|
-
resolve(process.cwd(), "career-
|
|
64
|
+
resolve(__dirname, "../../../../career-vivid/eval"),
|
|
65
|
+
resolve(__dirname, "../../../../../career-vivid/eval"),
|
|
66
|
+
resolve(process.cwd(), "career-vivid/eval"),
|
|
67
67
|
];
|
|
68
68
|
// Return the first parent that exists, or the cwd-relative fallback
|
|
69
69
|
for (const c of candidates) {
|
|
70
|
-
// We only care if the career-
|
|
70
|
+
// We only care if the career-vivid dir exists — create eval/ inside it
|
|
71
71
|
if (existsSync(resolve(c, "..")))
|
|
72
72
|
return c;
|
|
73
73
|
}
|
|
74
|
-
return resolve(process.cwd(), "career-
|
|
74
|
+
return resolve(process.cwd(), "career-vivid/eval");
|
|
75
75
|
}
|
|
76
76
|
/** Format date as YYYY-MM-DD in local time. */
|
|
77
77
|
function todayStr() {
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8")
|
|
|
58
58
|
const program = new Command();
|
|
59
59
|
program
|
|
60
60
|
.name("cv")
|
|
61
|
-
.description("CareerVivid CLI —
|
|
61
|
+
.description("CareerVivid CLI — AI-powered career management: job hunting, resume building, referrals, and portfolio publishing.")
|
|
62
62
|
.version(pkg.version, "-v, --version", "Print CLI version")
|
|
63
63
|
.addHelpText("before", getHelpHeader())
|
|
64
64
|
.helpOption("-h, --help", "Show help");
|