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 CHANGED
@@ -355,10 +355,12 @@ var ROLE_CHOICES = [
355
355
  { name: "Founder", value: "founder" },
356
356
  { name: "Builder", value: "builder" }
357
357
  ];
358
- async function initCommand() {
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
- const { name, email } = await inquirer.prompt([
374
- { type: "input", name: "name", message: "Name:" },
375
- { type: "input", name: "email", message: "Email:" }
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 { hasResume } = await inquirer.prompt([{
379
- type: "confirm",
380
- name: "hasResume",
381
- message: "Do you have an existing resume? (PDF/TXT)",
382
- default: true
383
- }]);
384
- if (hasResume) {
385
- if (!isApiKeyConfigured()) {
386
- warn("ANTHROPIC_API_KEY not set \u2014 skipping resume parsing. Set it in your environment for full features.");
387
- } else {
388
- const { resumePath } = await inquirer.prompt([{
389
- type: "input",
390
- name: "resumePath",
391
- message: "Path to resume:"
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
- warn("File not found. Continuing without resume.");
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.0");
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);