@getkrafter/resume-toolkit 1.0.5 → 1.1.1
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/bin/cli.js +79 -1
- package/dist/bin/cli.js.map +1 -1
- package/package.json +1 -1
- package/skills/score/SKILL.md +9 -17
- package/skills/tailor/SKILL.md +8 -16
package/dist/bin/cli.js
CHANGED
|
@@ -1,4 +1,82 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { startServer } from '../mcp/server.js';
|
|
3
|
-
|
|
3
|
+
import { scoreResume } from '../lib/resume-scorer.js';
|
|
4
|
+
import { scoreATS } from '../lib/ats-scorer.js';
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const command = args[0];
|
|
7
|
+
if (command === 'score') {
|
|
8
|
+
const resumeFile = getFlag(args, '--resume');
|
|
9
|
+
const jdFile = getFlag(args, '--jd');
|
|
10
|
+
if (!resumeFile) {
|
|
11
|
+
console.error('Usage: resume-toolkit score --resume <file> [--jd <file>]');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const fs = await import('node:fs');
|
|
15
|
+
const resumeText = fs.readFileSync(resumeFile, 'utf-8');
|
|
16
|
+
const jdText = jdFile ? fs.readFileSync(jdFile, 'utf-8') : undefined;
|
|
17
|
+
const resumeData = parseRawText(resumeText);
|
|
18
|
+
const result = scoreResume(resumeData, jdText);
|
|
19
|
+
console.log(JSON.stringify(result, null, 2));
|
|
20
|
+
}
|
|
21
|
+
else if (command === 'ats') {
|
|
22
|
+
const resumeFile = getFlag(args, '--resume');
|
|
23
|
+
const jdFile = getFlag(args, '--jd');
|
|
24
|
+
if (!resumeFile || !jdFile) {
|
|
25
|
+
console.error('Usage: resume-toolkit ats --resume <file> --jd <file>');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const fs = await import('node:fs');
|
|
29
|
+
const resumeText = fs.readFileSync(resumeFile, 'utf-8');
|
|
30
|
+
const jdText = fs.readFileSync(jdFile, 'utf-8');
|
|
31
|
+
const result = scoreATS(resumeText, jdText);
|
|
32
|
+
console.log(JSON.stringify(result, null, 2));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Default: start MCP server
|
|
36
|
+
startServer();
|
|
37
|
+
}
|
|
38
|
+
function getFlag(args, flag) {
|
|
39
|
+
const idx = args.indexOf(flag);
|
|
40
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
|
|
41
|
+
}
|
|
42
|
+
function parseRawText(text) {
|
|
43
|
+
// Strip markdown formatting for rawText
|
|
44
|
+
const cleanText = text.replace(/^#+\s+/gm, '').replace(/\*+([^*]+)\*+/g, '$1');
|
|
45
|
+
const lines = text.split('\n');
|
|
46
|
+
const bullets = [];
|
|
47
|
+
const sections = [];
|
|
48
|
+
let currentSection = '';
|
|
49
|
+
for (const line of lines) {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
if (!trimmed)
|
|
52
|
+
continue;
|
|
53
|
+
// Detect markdown headings as sections
|
|
54
|
+
const headingMatch = trimmed.match(/^#{1,3}\s+(.+)/);
|
|
55
|
+
if (headingMatch) {
|
|
56
|
+
const heading = headingMatch[1].replace(/\|.*$/, '').trim().toLowerCase();
|
|
57
|
+
sections.push(heading);
|
|
58
|
+
currentSection = heading;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// Detect ALL CAPS or Title Case lines as sections (plain text resumes)
|
|
62
|
+
if (trimmed.length < 50 &&
|
|
63
|
+
!(/^\s*[-*•]/.test(line) || /^\s*\d+[.)]/.test(line)) &&
|
|
64
|
+
(trimmed === trimmed.toUpperCase() && /[A-Z]/.test(trimmed))) {
|
|
65
|
+
sections.push(trimmed.toLowerCase());
|
|
66
|
+
currentSection = trimmed.toLowerCase();
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Detect bullet points — but only in work/project sections, not skills/education
|
|
70
|
+
const isSkillSection = /skill|competenc|certification|course|education|article/i.test(currentSection);
|
|
71
|
+
if (/^\s*[-*•]\s+/.test(line) || /^\s*\d+[.)]\s+/.test(line)) {
|
|
72
|
+
const content = trimmed.replace(/^[-*•]\s+/, '').replace(/^\d+[.)]\s+/, '');
|
|
73
|
+
// Only count as a resume bullet if it's long enough to be an achievement
|
|
74
|
+
// (not a short skill name) AND not in a skills/education section
|
|
75
|
+
if (!isSkillSection && content.length > 30) {
|
|
76
|
+
bullets.push(content);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { rawText: cleanText, bullets, sections };
|
|
81
|
+
}
|
|
4
82
|
//# sourceMappingURL=cli.js.map
|
package/dist/bin/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skills/score/SKILL.md
CHANGED
|
@@ -91,29 +91,21 @@ Or for Krafter resumes, call `score_krafter_resume`:
|
|
|
91
91
|
}
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
**Option B —
|
|
94
|
+
**Option B — CLI** (no MCP server needed):
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
1. Write the resume text to a temp file (e.g., `/tmp/resume.txt`).
|
|
97
|
+
2. If a JD was provided, write it to another temp file (e.g., `/tmp/jd.txt`).
|
|
98
|
+
3. Run:
|
|
97
99
|
|
|
98
100
|
```bash
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
# Without JD:
|
|
102
|
+
npx @getkrafter/resume-toolkit score --resume /tmp/resume.txt
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const lines = resumeText.split('\n');
|
|
106
|
-
const bullets = lines.filter(l => /^\s*[-*•]|\d+[.)]/.test(l)).map(l => l.replace(/^\s*[-*•]\s*|\d+[.)]\s*/, '').trim());
|
|
107
|
-
const sections = lines.filter(l => l.trim().length < 50 && l.trim().length > 0 && !(/^\s*[-*•]|\d+[.)]/.test(l)) && (l.trim() === l.trim().toUpperCase() || /^[A-Z][a-z]/.test(l.trim()))).map(l => l.trim().toLowerCase());
|
|
108
|
-
|
|
109
|
-
const result = scoreResume({ rawText: resumeText, bullets, sections }, jdText || undefined);
|
|
110
|
-
console.log(JSON.stringify(result, null, 2));
|
|
111
|
-
" <<< ""
|
|
104
|
+
# With JD:
|
|
105
|
+
npx @getkrafter/resume-toolkit score --resume /tmp/resume.txt --jd /tmp/jd.txt
|
|
112
106
|
```
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
If `@getkrafter/resume-toolkit` is not installed as a dependency, install it first: `npm install @getkrafter/resume-toolkit`
|
|
108
|
+
The output is a JSON `ResumeScore` object. Parse it and present results per Step 6.
|
|
117
109
|
|
|
118
110
|
---
|
|
119
111
|
|
package/skills/tailor/SKILL.md
CHANGED
|
@@ -67,28 +67,20 @@ For pasted/file resumes:
|
|
|
67
67
|
For Krafter resumes:
|
|
68
68
|
1. `score_krafter_resume` with `{ id, jdText }` -- returns the full `ResumeScore` including the embedded `ATSResult`.
|
|
69
69
|
|
|
70
|
-
**Option B —
|
|
70
|
+
**Option B — CLI** (no MCP server needed):
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
1. Write the resume text to `/tmp/resume.txt` and the JD to `/tmp/jd.txt`.
|
|
73
|
+
2. Run both commands:
|
|
73
74
|
|
|
74
75
|
```bash
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
# Full score with ATS:
|
|
77
|
+
npx @getkrafter/resume-toolkit score --resume /tmp/resume.txt --jd /tmp/jd.txt
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const lines = resumeText.split('\n');
|
|
82
|
-
const bullets = lines.filter(l => /^\s*[-*•]|\d+[.)]/.test(l)).map(l => l.replace(/^\s*[-*•]\s*|\d+[.)]\s*/, '').trim());
|
|
83
|
-
const sections = lines.filter(l => l.trim().length < 50 && l.trim().length > 0 && !(/^\s*[-*•]|\d+[.)]/.test(l)) && (l.trim() === l.trim().toUpperCase() || /^[A-Z][a-z]/.test(l.trim()))).map(l => l.trim().toLowerCase());
|
|
84
|
-
|
|
85
|
-
const atsResult = scoreATS(resumeText, jdText);
|
|
86
|
-
const scoreResult = scoreResume({ rawText: resumeText, bullets, sections }, jdText);
|
|
87
|
-
console.log(JSON.stringify({ atsResult, scoreResult }, null, 2));
|
|
88
|
-
" <<< ""
|
|
79
|
+
# Standalone ATS analysis:
|
|
80
|
+
npx @getkrafter/resume-toolkit ats --resume /tmp/resume.txt --jd /tmp/jd.txt
|
|
89
81
|
```
|
|
90
82
|
|
|
91
|
-
|
|
83
|
+
Parse the JSON output from each command and continue to Step 5.
|
|
92
84
|
|
|
93
85
|
### Step 5 -- Gap Analysis
|
|
94
86
|
|