@hasna/microservices 0.0.4 → 0.0.6

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.
Files changed (57) hide show
  1. package/bin/index.js +9 -1
  2. package/bin/mcp.js +9 -1
  3. package/dist/index.js +9 -1
  4. package/microservices/microservice-ads/src/cli/index.ts +198 -0
  5. package/microservices/microservice-ads/src/db/campaigns.ts +304 -0
  6. package/microservices/microservice-ads/src/mcp/index.ts +160 -0
  7. package/microservices/microservice-company/package.json +27 -0
  8. package/microservices/microservice-company/src/cli/index.ts +1126 -0
  9. package/microservices/microservice-company/src/db/company.ts +854 -0
  10. package/microservices/microservice-company/src/db/database.ts +93 -0
  11. package/microservices/microservice-company/src/db/migrations.ts +214 -0
  12. package/microservices/microservice-company/src/db/workflow-migrations.ts +44 -0
  13. package/microservices/microservice-company/src/index.ts +60 -0
  14. package/microservices/microservice-company/src/lib/audit.ts +168 -0
  15. package/microservices/microservice-company/src/lib/finance.ts +299 -0
  16. package/microservices/microservice-company/src/lib/settings.ts +85 -0
  17. package/microservices/microservice-company/src/lib/workflows.ts +698 -0
  18. package/microservices/microservice-company/src/mcp/index.ts +991 -0
  19. package/microservices/microservice-contracts/src/cli/index.ts +410 -23
  20. package/microservices/microservice-contracts/src/db/contracts.ts +430 -1
  21. package/microservices/microservice-contracts/src/db/migrations.ts +83 -0
  22. package/microservices/microservice-contracts/src/mcp/index.ts +312 -3
  23. package/microservices/microservice-domains/src/cli/index.ts +673 -0
  24. package/microservices/microservice-domains/src/db/domains.ts +613 -0
  25. package/microservices/microservice-domains/src/index.ts +21 -0
  26. package/microservices/microservice-domains/src/lib/brandsight.ts +285 -0
  27. package/microservices/microservice-domains/src/lib/godaddy.ts +328 -0
  28. package/microservices/microservice-domains/src/lib/namecheap.ts +474 -0
  29. package/microservices/microservice-domains/src/lib/registrar.ts +355 -0
  30. package/microservices/microservice-domains/src/mcp/index.ts +413 -0
  31. package/microservices/microservice-hiring/src/cli/index.ts +318 -8
  32. package/microservices/microservice-hiring/src/db/hiring.ts +503 -0
  33. package/microservices/microservice-hiring/src/db/migrations.ts +21 -0
  34. package/microservices/microservice-hiring/src/index.ts +29 -0
  35. package/microservices/microservice-hiring/src/lib/scoring.ts +206 -0
  36. package/microservices/microservice-hiring/src/mcp/index.ts +245 -0
  37. package/microservices/microservice-payments/src/cli/index.ts +255 -3
  38. package/microservices/microservice-payments/src/db/migrations.ts +18 -0
  39. package/microservices/microservice-payments/src/db/payments.ts +552 -0
  40. package/microservices/microservice-payments/src/mcp/index.ts +223 -0
  41. package/microservices/microservice-payroll/src/cli/index.ts +269 -0
  42. package/microservices/microservice-payroll/src/db/migrations.ts +26 -0
  43. package/microservices/microservice-payroll/src/db/payroll.ts +636 -0
  44. package/microservices/microservice-payroll/src/mcp/index.ts +246 -0
  45. package/microservices/microservice-shipping/src/cli/index.ts +211 -3
  46. package/microservices/microservice-shipping/src/db/migrations.ts +8 -0
  47. package/microservices/microservice-shipping/src/db/shipping.ts +453 -3
  48. package/microservices/microservice-shipping/src/mcp/index.ts +149 -1
  49. package/microservices/microservice-social/src/cli/index.ts +244 -2
  50. package/microservices/microservice-social/src/db/migrations.ts +33 -0
  51. package/microservices/microservice-social/src/db/social.ts +378 -4
  52. package/microservices/microservice-social/src/mcp/index.ts +221 -1
  53. package/microservices/microservice-subscriptions/src/cli/index.ts +315 -0
  54. package/microservices/microservice-subscriptions/src/db/migrations.ts +68 -0
  55. package/microservices/microservice-subscriptions/src/db/subscriptions.ts +567 -3
  56. package/microservices/microservice-subscriptions/src/mcp/index.ts +267 -1
  57. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import { Command } from "commander";
4
+ import { readFileSync } from "node:fs";
4
5
  import {
5
6
  createJob,
6
7
  getJob,
@@ -17,7 +18,17 @@ import {
17
18
  createInterview,
18
19
  listInterviews,
19
20
  addInterviewFeedback,
21
+ bulkImportApplicants,
22
+ generateOffer,
23
+ getHiringForecast,
24
+ submitStructuredFeedback,
25
+ bulkReject,
26
+ getReferralStats,
27
+ saveJobAsTemplate,
28
+ createJobFromTemplate,
29
+ listJobTemplates,
20
30
  } from "../db/hiring.js";
31
+ import { scoreApplicant, rankApplicants } from "../lib/scoring.js";
21
32
 
22
33
  const program = new Command();
23
34
 
@@ -291,6 +302,142 @@ applicantCmd
291
302
  }
292
303
  });
293
304
 
305
+ // --- Bulk Import ---
306
+
307
+ applicantCmd
308
+ .command("bulk-import")
309
+ .description("Bulk import applicants from a CSV file")
310
+ .requiredOption("--file <path>", "Path to CSV file (name,email,phone,job_id,source,resume_url)")
311
+ .option("--json", "Output as JSON", false)
312
+ .action((opts) => {
313
+ const csvData = readFileSync(opts.file, "utf-8");
314
+ const result = bulkImportApplicants(csvData);
315
+
316
+ if (opts.json) {
317
+ console.log(JSON.stringify(result, null, 2));
318
+ } else {
319
+ console.log(`Imported: ${result.imported}`);
320
+ console.log(`Skipped: ${result.skipped}`);
321
+ if (result.errors.length > 0) {
322
+ console.log("Errors:");
323
+ for (const e of result.errors) {
324
+ console.log(` - ${e}`);
325
+ }
326
+ }
327
+ }
328
+ });
329
+
330
+ // --- AI Scoring ---
331
+
332
+ applicantCmd
333
+ .command("score")
334
+ .description("AI-score an applicant against job requirements")
335
+ .argument("<id>", "Applicant ID")
336
+ .option("--json", "Output as JSON", false)
337
+ .action(async (id, opts) => {
338
+ try {
339
+ const score = await scoreApplicant(id);
340
+
341
+ if (opts.json) {
342
+ console.log(JSON.stringify(score, null, 2));
343
+ } else {
344
+ console.log(`Match: ${score.match_pct}%`);
345
+ console.log(`Recommendation: ${score.recommendation}`);
346
+ if (score.strengths.length) console.log(`Strengths: ${score.strengths.join(", ")}`);
347
+ if (score.gaps.length) console.log(`Gaps: ${score.gaps.join(", ")}`);
348
+ }
349
+ } catch (err) {
350
+ console.error(err instanceof Error ? err.message : String(err));
351
+ process.exit(1);
352
+ }
353
+ });
354
+
355
+ // --- AI Bulk Ranking ---
356
+
357
+ applicantCmd
358
+ .command("rank")
359
+ .description("AI-rank all applicants for a job by fit score")
360
+ .requiredOption("--job <id>", "Job ID")
361
+ .option("--json", "Output as JSON", false)
362
+ .action(async (opts) => {
363
+ try {
364
+ const ranked = await rankApplicants(opts.job);
365
+
366
+ if (opts.json) {
367
+ console.log(JSON.stringify(ranked, null, 2));
368
+ } else {
369
+ if (ranked.length === 0) {
370
+ console.log("No applicants to rank.");
371
+ return;
372
+ }
373
+ console.log("Ranking:");
374
+ for (let i = 0; i < ranked.length; i++) {
375
+ const { applicant, score } = ranked[i];
376
+ console.log(` ${i + 1}. ${applicant.name} — ${score.match_pct}% (${score.recommendation})`);
377
+ }
378
+ }
379
+ } catch (err) {
380
+ console.error(err instanceof Error ? err.message : String(err));
381
+ process.exit(1);
382
+ }
383
+ });
384
+
385
+ // --- Offer Letter ---
386
+
387
+ applicantCmd
388
+ .command("offer")
389
+ .description("Generate a Markdown offer letter")
390
+ .argument("<id>", "Applicant ID")
391
+ .requiredOption("--salary <amount>", "Annual salary")
392
+ .requiredOption("--start-date <date>", "Start date (YYYY-MM-DD)")
393
+ .option("--title <title>", "Position title override")
394
+ .option("--department <dept>", "Department override")
395
+ .option("--benefits <text>", "Benefits description")
396
+ .option("--equity <text>", "Equity details")
397
+ .option("--signing-bonus <amount>", "Signing bonus")
398
+ .option("--json", "Output as JSON", false)
399
+ .action((id, opts) => {
400
+ try {
401
+ const letter = generateOffer(id, {
402
+ salary: parseInt(opts.salary),
403
+ start_date: opts.startDate,
404
+ position_title: opts.title,
405
+ department: opts.department,
406
+ benefits: opts.benefits,
407
+ equity: opts.equity,
408
+ signing_bonus: opts.signingBonus ? parseInt(opts.signingBonus) : undefined,
409
+ });
410
+
411
+ if (opts.json) {
412
+ console.log(JSON.stringify({ offer_letter: letter }, null, 2));
413
+ } else {
414
+ console.log(letter);
415
+ }
416
+ } catch (err) {
417
+ console.error(err instanceof Error ? err.message : String(err));
418
+ process.exit(1);
419
+ }
420
+ });
421
+
422
+ // --- Bulk Rejection ---
423
+
424
+ applicantCmd
425
+ .command("reject-batch")
426
+ .description("Bulk reject applicants for a job by status")
427
+ .requiredOption("--job <id>", "Job ID")
428
+ .requiredOption("--status <status>", "Status to reject (applied/screening/etc.)")
429
+ .option("--reason <reason>", "Rejection reason")
430
+ .option("--json", "Output as JSON", false)
431
+ .action((opts) => {
432
+ const result = bulkReject(opts.job, opts.status, opts.reason);
433
+
434
+ if (opts.json) {
435
+ console.log(JSON.stringify(result, null, 2));
436
+ } else {
437
+ console.log(`Rejected ${result.rejected} applicant(s)`);
438
+ }
439
+ });
440
+
294
441
  // --- Interviews ---
295
442
 
296
443
  const interviewCmd = program
@@ -356,17 +503,46 @@ interviewCmd
356
503
 
357
504
  interviewCmd
358
505
  .command("feedback")
359
- .description("Add feedback to an interview")
506
+ .description("Add feedback to an interview (supports structured scoring dimensions)")
360
507
  .argument("<id>", "Interview ID")
361
- .requiredOption("--feedback <text>", "Feedback text")
362
- .option("--rating <n>", "Rating (1-5)")
508
+ .option("--feedback <text>", "Feedback text")
509
+ .option("--rating <n>", "Overall rating (1-5)")
510
+ .option("--technical <n>", "Technical score (1-5)")
511
+ .option("--communication <n>", "Communication score (1-5)")
512
+ .option("--culture-fit <n>", "Culture fit score (1-5)")
513
+ .option("--problem-solving <n>", "Problem solving score (1-5)")
514
+ .option("--leadership <n>", "Leadership score (1-5)")
363
515
  .option("--json", "Output as JSON", false)
364
516
  .action((id, opts) => {
365
- const interview = addInterviewFeedback(
366
- id,
367
- opts.feedback,
368
- opts.rating ? parseInt(opts.rating) : undefined
369
- );
517
+ const hasStructured = opts.technical || opts.communication || opts.cultureFit ||
518
+ opts.problemSolving || opts.leadership;
519
+
520
+ let interview;
521
+ if (hasStructured) {
522
+ interview = submitStructuredFeedback(
523
+ id,
524
+ {
525
+ technical: opts.technical ? parseInt(opts.technical) : undefined,
526
+ communication: opts.communication ? parseInt(opts.communication) : undefined,
527
+ culture_fit: opts.cultureFit ? parseInt(opts.cultureFit) : undefined,
528
+ problem_solving: opts.problemSolving ? parseInt(opts.problemSolving) : undefined,
529
+ leadership: opts.leadership ? parseInt(opts.leadership) : undefined,
530
+ overall: opts.rating ? parseInt(opts.rating) : undefined,
531
+ },
532
+ opts.feedback
533
+ );
534
+ } else {
535
+ if (!opts.feedback) {
536
+ console.error("Either --feedback or structured scores (--technical, --communication, etc.) are required.");
537
+ process.exit(1);
538
+ }
539
+ interview = addInterviewFeedback(
540
+ id,
541
+ opts.feedback,
542
+ opts.rating ? parseInt(opts.rating) : undefined
543
+ );
544
+ }
545
+
370
546
  if (!interview) {
371
547
  console.error(`Interview '${id}' not found.`);
372
548
  process.exit(1);
@@ -428,4 +604,138 @@ program
428
604
  }
429
605
  });
430
606
 
607
+ // --- Referral Stats ---
608
+
609
+ const statsCmd = program
610
+ .command("stats-referrals")
611
+ .description("Show referral/source conversion rates")
612
+ .option("--json", "Output as JSON", false)
613
+ .action((opts) => {
614
+ const stats = getReferralStats();
615
+
616
+ if (opts.json) {
617
+ console.log(JSON.stringify(stats, null, 2));
618
+ } else {
619
+ if (stats.length === 0) {
620
+ console.log("No applicant source data.");
621
+ return;
622
+ }
623
+ console.log("Referral Stats:");
624
+ for (const s of stats) {
625
+ console.log(` ${s.source}: ${s.total} total, ${s.hired} hired, ${s.conversion_rate}% conversion`);
626
+ }
627
+ }
628
+ });
629
+
630
+ // --- Forecast ---
631
+
632
+ program
633
+ .command("forecast")
634
+ .description("Estimate days-to-fill based on pipeline velocity")
635
+ .argument("<job-id>", "Job ID")
636
+ .option("--json", "Output as JSON", false)
637
+ .action((jobId, opts) => {
638
+ try {
639
+ const forecast = getHiringForecast(jobId);
640
+
641
+ if (opts.json) {
642
+ console.log(JSON.stringify(forecast, null, 2));
643
+ } else {
644
+ console.log(`Forecast for: ${forecast.job_title}`);
645
+ console.log(` Total applicants: ${forecast.total_applicants}`);
646
+ console.log(` Estimated days to fill: ${forecast.estimated_days_to_fill ?? "N/A"}`);
647
+
648
+ if (Object.keys(forecast.avg_days_per_stage).length) {
649
+ console.log(" Avg days per transition:");
650
+ for (const [stage, days] of Object.entries(forecast.avg_days_per_stage)) {
651
+ console.log(` ${stage}: ${days} days`);
652
+ }
653
+ }
654
+
655
+ if (Object.keys(forecast.conversion_rates).length) {
656
+ console.log(" Conversion rates:");
657
+ for (const [stage, rate] of Object.entries(forecast.conversion_rates)) {
658
+ console.log(` ${stage}: ${rate}%`);
659
+ }
660
+ }
661
+ }
662
+ } catch (err) {
663
+ console.error(err instanceof Error ? err.message : String(err));
664
+ process.exit(1);
665
+ }
666
+ });
667
+
668
+ // --- Job Templates ---
669
+
670
+ jobCmd
671
+ .command("save-template")
672
+ .description("Save a job as a reusable template")
673
+ .argument("<id>", "Job ID")
674
+ .requiredOption("--name <name>", "Template name")
675
+ .option("--json", "Output as JSON", false)
676
+ .action((id, opts) => {
677
+ try {
678
+ const template = saveJobAsTemplate(id, opts.name);
679
+
680
+ if (opts.json) {
681
+ console.log(JSON.stringify(template, null, 2));
682
+ } else {
683
+ console.log(`Saved template: ${template.name} (${template.id})`);
684
+ }
685
+ } catch (err) {
686
+ console.error(err instanceof Error ? err.message : String(err));
687
+ process.exit(1);
688
+ }
689
+ });
690
+
691
+ jobCmd
692
+ .command("from-template")
693
+ .description("Create a job from a template")
694
+ .requiredOption("--template <name>", "Template name")
695
+ .option("--title <title>", "Override title")
696
+ .option("--department <dept>", "Override department")
697
+ .option("--location <loc>", "Override location")
698
+ .option("--salary-range <range>", "Override salary range")
699
+ .option("--json", "Output as JSON", false)
700
+ .action((opts) => {
701
+ try {
702
+ const job = createJobFromTemplate(opts.template, {
703
+ title: opts.title,
704
+ department: opts.department,
705
+ location: opts.location,
706
+ salary_range: opts.salaryRange,
707
+ });
708
+
709
+ if (opts.json) {
710
+ console.log(JSON.stringify(job, null, 2));
711
+ } else {
712
+ console.log(`Created job from template: ${job.title} (${job.id})`);
713
+ }
714
+ } catch (err) {
715
+ console.error(err instanceof Error ? err.message : String(err));
716
+ process.exit(1);
717
+ }
718
+ });
719
+
720
+ jobCmd
721
+ .command("templates")
722
+ .description("List all job templates")
723
+ .option("--json", "Output as JSON", false)
724
+ .action((opts) => {
725
+ const templates = listJobTemplates();
726
+
727
+ if (opts.json) {
728
+ console.log(JSON.stringify(templates, null, 2));
729
+ } else {
730
+ if (templates.length === 0) {
731
+ console.log("No templates found.");
732
+ return;
733
+ }
734
+ for (const t of templates) {
735
+ console.log(` ${t.name} — ${t.title} (${t.id})`);
736
+ }
737
+ console.log(`\n${templates.length} template(s)`);
738
+ }
739
+ });
740
+
431
741
  program.parse(process.argv);