@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.
- package/bin/index.js +9 -1
- package/bin/mcp.js +9 -1
- package/dist/index.js +9 -1
- package/microservices/microservice-ads/src/cli/index.ts +198 -0
- package/microservices/microservice-ads/src/db/campaigns.ts +304 -0
- package/microservices/microservice-ads/src/mcp/index.ts +160 -0
- package/microservices/microservice-company/package.json +27 -0
- package/microservices/microservice-company/src/cli/index.ts +1126 -0
- package/microservices/microservice-company/src/db/company.ts +854 -0
- package/microservices/microservice-company/src/db/database.ts +93 -0
- package/microservices/microservice-company/src/db/migrations.ts +214 -0
- package/microservices/microservice-company/src/db/workflow-migrations.ts +44 -0
- package/microservices/microservice-company/src/index.ts +60 -0
- package/microservices/microservice-company/src/lib/audit.ts +168 -0
- package/microservices/microservice-company/src/lib/finance.ts +299 -0
- package/microservices/microservice-company/src/lib/settings.ts +85 -0
- package/microservices/microservice-company/src/lib/workflows.ts +698 -0
- package/microservices/microservice-company/src/mcp/index.ts +991 -0
- package/microservices/microservice-contracts/src/cli/index.ts +410 -23
- package/microservices/microservice-contracts/src/db/contracts.ts +430 -1
- package/microservices/microservice-contracts/src/db/migrations.ts +83 -0
- package/microservices/microservice-contracts/src/mcp/index.ts +312 -3
- package/microservices/microservice-domains/src/cli/index.ts +673 -0
- package/microservices/microservice-domains/src/db/domains.ts +613 -0
- package/microservices/microservice-domains/src/index.ts +21 -0
- package/microservices/microservice-domains/src/lib/brandsight.ts +285 -0
- package/microservices/microservice-domains/src/lib/godaddy.ts +328 -0
- package/microservices/microservice-domains/src/lib/namecheap.ts +474 -0
- package/microservices/microservice-domains/src/lib/registrar.ts +355 -0
- package/microservices/microservice-domains/src/mcp/index.ts +413 -0
- package/microservices/microservice-hiring/src/cli/index.ts +318 -8
- package/microservices/microservice-hiring/src/db/hiring.ts +503 -0
- package/microservices/microservice-hiring/src/db/migrations.ts +21 -0
- package/microservices/microservice-hiring/src/index.ts +29 -0
- package/microservices/microservice-hiring/src/lib/scoring.ts +206 -0
- package/microservices/microservice-hiring/src/mcp/index.ts +245 -0
- package/microservices/microservice-payments/src/cli/index.ts +255 -3
- package/microservices/microservice-payments/src/db/migrations.ts +18 -0
- package/microservices/microservice-payments/src/db/payments.ts +552 -0
- package/microservices/microservice-payments/src/mcp/index.ts +223 -0
- package/microservices/microservice-payroll/src/cli/index.ts +269 -0
- package/microservices/microservice-payroll/src/db/migrations.ts +26 -0
- package/microservices/microservice-payroll/src/db/payroll.ts +636 -0
- package/microservices/microservice-payroll/src/mcp/index.ts +246 -0
- package/microservices/microservice-shipping/src/cli/index.ts +211 -3
- package/microservices/microservice-shipping/src/db/migrations.ts +8 -0
- package/microservices/microservice-shipping/src/db/shipping.ts +453 -3
- package/microservices/microservice-shipping/src/mcp/index.ts +149 -1
- package/microservices/microservice-social/src/cli/index.ts +244 -2
- package/microservices/microservice-social/src/db/migrations.ts +33 -0
- package/microservices/microservice-social/src/db/social.ts +378 -4
- package/microservices/microservice-social/src/mcp/index.ts +221 -1
- package/microservices/microservice-subscriptions/src/cli/index.ts +315 -0
- package/microservices/microservice-subscriptions/src/db/migrations.ts +68 -0
- package/microservices/microservice-subscriptions/src/db/subscriptions.ts +567 -3
- package/microservices/microservice-subscriptions/src/mcp/index.ts +267 -1
- 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
|
-
.
|
|
362
|
-
.option("--rating <n>", "
|
|
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
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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);
|