hiregraph 0.1.1 → 0.1.2
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/index.js +104 -78
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -355,10 +355,12 @@ var ROLE_CHOICES = [
|
|
|
355
355
|
{ name: "Founder", value: "founder" },
|
|
356
356
|
{ name: "Builder", value: "builder" }
|
|
357
357
|
];
|
|
358
|
-
|
|
358
|
+
var VALID_ROLES = ROLE_CHOICES.map((c) => c.value);
|
|
359
|
+
async function initCommand(options) {
|
|
359
360
|
header("\n HireGraph Init\n");
|
|
361
|
+
const isNonInteractive = !!(options.name && options.email);
|
|
360
362
|
const existing = await loadJson("identity.json");
|
|
361
|
-
if (existing) {
|
|
363
|
+
if (existing && !isNonInteractive) {
|
|
362
364
|
const { overwrite } = await inquirer.prompt([{
|
|
363
365
|
type: "confirm",
|
|
364
366
|
name: "overwrite",
|
|
@@ -370,87 +372,111 @@ async function initCommand() {
|
|
|
370
372
|
return;
|
|
371
373
|
}
|
|
372
374
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
375
|
+
let name;
|
|
376
|
+
let email;
|
|
377
|
+
let role;
|
|
378
|
+
let targetRoles;
|
|
379
|
+
let remotePref;
|
|
380
|
+
let minComp;
|
|
381
|
+
if (isNonInteractive) {
|
|
382
|
+
name = options.name;
|
|
383
|
+
email = options.email;
|
|
384
|
+
role = VALID_ROLES.includes(options.role || "") ? options.role : "engineer";
|
|
385
|
+
targetRoles = options.targets || "Founding Engineer, Full-Stack Engineer";
|
|
386
|
+
remotePref = options.remote || "Remote";
|
|
387
|
+
minComp = options.compensation || "";
|
|
388
|
+
} else {
|
|
389
|
+
({ name, email } = await inquirer.prompt([
|
|
390
|
+
{ type: "input", name: "name", message: "Name:" },
|
|
391
|
+
{ type: "input", name: "email", message: "Email:" }
|
|
392
|
+
]));
|
|
393
|
+
({ role } = await inquirer.prompt([{
|
|
394
|
+
type: "list",
|
|
395
|
+
name: "role",
|
|
396
|
+
message: "What describes you best?",
|
|
397
|
+
choices: ROLE_CHOICES
|
|
398
|
+
}]));
|
|
399
|
+
({ targetRoles } = await inquirer.prompt([{
|
|
400
|
+
type: "input",
|
|
401
|
+
name: "targetRoles",
|
|
402
|
+
message: "Target roles (comma separated):",
|
|
403
|
+
default: "Founding Engineer, Full-Stack Engineer"
|
|
404
|
+
}]));
|
|
405
|
+
({ remotePref } = await inquirer.prompt([{
|
|
406
|
+
type: "list",
|
|
407
|
+
name: "remotePref",
|
|
408
|
+
message: "Remote preference?",
|
|
409
|
+
choices: ["Remote", "Hybrid", "Onsite", "No preference"]
|
|
410
|
+
}]));
|
|
411
|
+
({ minComp } = await inquirer.prompt([{
|
|
412
|
+
type: "input",
|
|
413
|
+
name: "minComp",
|
|
414
|
+
message: "Min compensation (optional):",
|
|
415
|
+
default: ""
|
|
416
|
+
}]));
|
|
417
|
+
}
|
|
377
418
|
let resumeData = null;
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}]);
|
|
393
|
-
const resolved = resolve(resumePath.trim().replace(/^["']|["']$/g, ""));
|
|
394
|
-
if (existsSync2(resolved)) {
|
|
395
|
-
start("Parsing resume...");
|
|
396
|
-
try {
|
|
397
|
-
resumeData = await parseResume(resolved);
|
|
398
|
-
succeed("Resume parsed");
|
|
399
|
-
info(` Name: ${resumeData.name}`);
|
|
400
|
-
info(` Email: ${resumeData.email}`);
|
|
401
|
-
if (resumeData.work_history.length > 0) {
|
|
402
|
-
info(" Work History:");
|
|
403
|
-
for (const w of resumeData.work_history) {
|
|
404
|
-
info(` ${w.role} @ ${w.company} (${w.start_year}-${w.end_year || "present"})`);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
if (resumeData.skills.length > 0) {
|
|
408
|
-
info(` Skills: ${resumeData.skills.join(", ")}`);
|
|
409
|
-
}
|
|
410
|
-
const { looksRight } = await inquirer.prompt([{
|
|
411
|
-
type: "confirm",
|
|
412
|
-
name: "looksRight",
|
|
413
|
-
message: "Look right?",
|
|
414
|
-
default: true
|
|
415
|
-
}]);
|
|
416
|
-
if (!looksRight) {
|
|
417
|
-
info("Resume data discarded. Using manual input.");
|
|
418
|
-
resumeData = null;
|
|
419
|
+
const resumePath = options.resume;
|
|
420
|
+
if (resumePath && isApiKeyConfigured()) {
|
|
421
|
+
const resolved = resolve(resumePath.trim().replace(/^["']|["']$/g, ""));
|
|
422
|
+
if (existsSync2(resolved)) {
|
|
423
|
+
start("Parsing resume...");
|
|
424
|
+
try {
|
|
425
|
+
resumeData = await parseResume(resolved);
|
|
426
|
+
succeed("Resume parsed");
|
|
427
|
+
info(` Name: ${resumeData.name}`);
|
|
428
|
+
info(` Email: ${resumeData.email}`);
|
|
429
|
+
if (resumeData.work_history.length > 0) {
|
|
430
|
+
info(" Work History:");
|
|
431
|
+
for (const w of resumeData.work_history) {
|
|
432
|
+
info(` ${w.role} @ ${w.company} (${w.start_year}-${w.end_year || "present"})`);
|
|
419
433
|
}
|
|
420
|
-
} catch (err) {
|
|
421
|
-
fail("Failed to parse resume");
|
|
422
|
-
error(err.message);
|
|
423
|
-
resumeData = null;
|
|
424
434
|
}
|
|
435
|
+
} catch (err) {
|
|
436
|
+
fail("Failed to parse resume");
|
|
437
|
+
error(err.message);
|
|
438
|
+
resumeData = null;
|
|
439
|
+
}
|
|
440
|
+
} else {
|
|
441
|
+
warn(`Resume file not found: ${resolved}`);
|
|
442
|
+
}
|
|
443
|
+
} else if (!isNonInteractive && !resumePath) {
|
|
444
|
+
const { hasResume } = await inquirer.prompt([{
|
|
445
|
+
type: "confirm",
|
|
446
|
+
name: "hasResume",
|
|
447
|
+
message: "Do you have an existing resume? (PDF/TXT)",
|
|
448
|
+
default: true
|
|
449
|
+
}]);
|
|
450
|
+
if (hasResume) {
|
|
451
|
+
if (!isApiKeyConfigured()) {
|
|
452
|
+
warn("ANTHROPIC_API_KEY not set \u2014 skipping resume parsing. Set it in your environment for full features.");
|
|
425
453
|
} else {
|
|
426
|
-
|
|
454
|
+
const { rPath } = await inquirer.prompt([{
|
|
455
|
+
type: "input",
|
|
456
|
+
name: "rPath",
|
|
457
|
+
message: "Path to resume:"
|
|
458
|
+
}]);
|
|
459
|
+
const resolved = resolve(rPath.trim().replace(/^["']|["']$/g, ""));
|
|
460
|
+
if (existsSync2(resolved)) {
|
|
461
|
+
start("Parsing resume...");
|
|
462
|
+
try {
|
|
463
|
+
resumeData = await parseResume(resolved);
|
|
464
|
+
succeed("Resume parsed");
|
|
465
|
+
const { looksRight } = await inquirer.prompt([{
|
|
466
|
+
type: "confirm",
|
|
467
|
+
name: "looksRight",
|
|
468
|
+
message: "Look right?",
|
|
469
|
+
default: true
|
|
470
|
+
}]);
|
|
471
|
+
if (!looksRight) resumeData = null;
|
|
472
|
+
} catch (err) {
|
|
473
|
+
fail("Failed to parse resume");
|
|
474
|
+
error(err.message);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
427
477
|
}
|
|
428
478
|
}
|
|
429
479
|
}
|
|
430
|
-
const { role } = await inquirer.prompt([{
|
|
431
|
-
type: "list",
|
|
432
|
-
name: "role",
|
|
433
|
-
message: "What describes you best?",
|
|
434
|
-
choices: ROLE_CHOICES
|
|
435
|
-
}]);
|
|
436
|
-
const { targetRoles } = await inquirer.prompt([{
|
|
437
|
-
type: "input",
|
|
438
|
-
name: "targetRoles",
|
|
439
|
-
message: "Target roles (comma separated):",
|
|
440
|
-
default: "Founding Engineer, Full-Stack Engineer"
|
|
441
|
-
}]);
|
|
442
|
-
const { remotePref } = await inquirer.prompt([{
|
|
443
|
-
type: "list",
|
|
444
|
-
name: "remotePref",
|
|
445
|
-
message: "Remote preference?",
|
|
446
|
-
choices: ["Remote", "Hybrid", "Onsite", "No preference"]
|
|
447
|
-
}]);
|
|
448
|
-
const { minComp } = await inquirer.prompt([{
|
|
449
|
-
type: "input",
|
|
450
|
-
name: "minComp",
|
|
451
|
-
message: "Min compensation (optional):",
|
|
452
|
-
default: ""
|
|
453
|
-
}]);
|
|
454
480
|
const targets = targetRoles.split(",").map((r) => r.trim()).filter(Boolean);
|
|
455
481
|
const identity = resumeData ? resumeToIdentity(resumeData, role, targets, remotePref, minComp) : {
|
|
456
482
|
name,
|
|
@@ -3270,8 +3296,8 @@ function getTimeAgo2(isoDate) {
|
|
|
3270
3296
|
|
|
3271
3297
|
// src/index.ts
|
|
3272
3298
|
var program = new Command();
|
|
3273
|
-
program.name("hiregraph").description("Turn your code into job applications. Local-first CLI that scans codebases and builds skill graphs.").version("0.1.
|
|
3274
|
-
program.command("init").description("Set up your builder profile (resume upload + preferences)").action(initCommand);
|
|
3299
|
+
program.name("hiregraph").description("Turn your code into job applications. Local-first CLI that scans codebases and builds skill graphs.").version("0.1.2");
|
|
3300
|
+
program.command("init").description("Set up your builder profile (resume upload + preferences)").option("--name <name>", "Your full name").option("--email <email>", "Your email address").option("--role <role>", "Your role (engineer, pm, designer, founder, builder)").option("--targets <roles>", "Target roles, comma separated").option("--remote <pref>", "Remote preference (Remote, Hybrid, Onsite)").option("--resume <path>", "Path to resume PDF/TXT").option("--compensation <amount>", "Minimum compensation").action(initCommand);
|
|
3275
3301
|
program.command("scan").description("Scan a project and update your skill graph").argument("[path]", "Path to the project directory", ".").action(scanCommand);
|
|
3276
3302
|
program.command("status").description("Show your current skill graph summary").action(statusCommand);
|
|
3277
3303
|
program.command("jobs").description("Fetch job listings from Greenhouse, Lever, and Ashby boards").option("--refresh", "Force refresh cached jobs").option("--ats <type>", "Filter by ATS type (greenhouse, lever, ashby)").option("--limit <n>", "Show sample of N job titles", parseInt).action(jobsCommand);
|