resume-parser-ats 1.1.0
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/LICENSE +21 -0
- package/README.md +294 -0
- package/bin/cli.js +368 -0
- package/dist/mcp-server/server.d.ts +18 -0
- package/dist/mcp-server/server.d.ts.map +1 -0
- package/dist/mcp-server/server.js +276 -0
- package/dist/mcp-server/server.js.map +1 -0
- package/dist/src/index.d.ts +96 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +39 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/prompts/insights-prompt.d.ts +24 -0
- package/dist/src/prompts/insights-prompt.d.ts.map +1 -0
- package/dist/src/prompts/insights-prompt.js +129 -0
- package/dist/src/prompts/insights-prompt.js.map +1 -0
- package/dist/src/prompts/parser-prompt.d.ts +11 -0
- package/dist/src/prompts/parser-prompt.d.ts.map +1 -0
- package/dist/src/prompts/parser-prompt.js +150 -0
- package/dist/src/prompts/parser-prompt.js.map +1 -0
- package/dist/src/tools/analyze-resume.d.ts +52 -0
- package/dist/src/tools/analyze-resume.d.ts.map +1 -0
- package/dist/src/tools/analyze-resume.js +286 -0
- package/dist/src/tools/analyze-resume.js.map +1 -0
- package/dist/src/tools/parse-resume.d.ts +57 -0
- package/dist/src/tools/parse-resume.d.ts.map +1 -0
- package/dist/src/tools/parse-resume.js +608 -0
- package/dist/src/tools/parse-resume.js.map +1 -0
- package/dist/src/tools/suggest-improvements.d.ts +51 -0
- package/dist/src/tools/suggest-improvements.d.ts.map +1 -0
- package/dist/src/tools/suggest-improvements.js +472 -0
- package/dist/src/tools/suggest-improvements.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dhanush Kandhan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# š Resume Parser
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>Deep resume parsing ⢠ATS compatibility scoring ⢠Actionable improvement insights</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Made with ā¤ļø by <strong>Dhanush Kandhan</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
A powerful agent skill that deeply parses resumes using the **OpenResume 4-step algorithm**, extracts structured information (Name, Email, Phone, Education, Work Experience, Skills, Projects), evaluates ATS (Applicant Tracking System) compatibility, and provides prioritized, actionable suggestions to improve your resume.
|
|
14
|
+
|
|
15
|
+
## ⨠Features
|
|
16
|
+
|
|
17
|
+
- **š Deep Parsing** ā Extracts 10+ fields from raw text or PDF using a feature-scoring engine
|
|
18
|
+
- **š ATS Scoring** ā Grades your resume A+ through F with detailed per-field confidence ratings
|
|
19
|
+
- **š” Smart Suggestions** ā Prioritized, categorized fixes (critical ā low) with before/after examples
|
|
20
|
+
- **š ļø CLI & MCP Server** ā Use interactively from the command line or as an MCP tool
|
|
21
|
+
- **āļø Configurable Strictness** ā Lenient, moderate, or strict ATS evaluation modes
|
|
22
|
+
- **š Zero Dependencies on Proprietary APIs** ā Runs entirely locally with no external calls
|
|
23
|
+
|
|
24
|
+
## š¦ Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Clone the repo
|
|
28
|
+
git clone https://github.com/dhanushk-offl/resume-parser-skill.git
|
|
29
|
+
cd resume-parser-skill
|
|
30
|
+
|
|
31
|
+
# Install dependencies
|
|
32
|
+
npm install
|
|
33
|
+
|
|
34
|
+
# Build
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## š Usage
|
|
39
|
+
|
|
40
|
+
### As a CLI Tool
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Parse a resume and output structured data
|
|
44
|
+
npx resume-parser parse resume.pdf
|
|
45
|
+
|
|
46
|
+
# Parse + analyze ATS compatibility
|
|
47
|
+
npx resume-parser analyze resume.pdf
|
|
48
|
+
|
|
49
|
+
# Full pipeline: parse + analyze + actionable suggestions
|
|
50
|
+
npx resume-parser insights resume.pdf
|
|
51
|
+
|
|
52
|
+
# Parse from raw text
|
|
53
|
+
npx resume-parser parse "John Doe\njohn@email.com\nSoftware Engineer"
|
|
54
|
+
|
|
55
|
+
# Adjust ATS strictness
|
|
56
|
+
npx resume-parser analyze resume.pdf --strictness strict
|
|
57
|
+
|
|
58
|
+
# Focus on specific areas
|
|
59
|
+
npx resume-parser insights resume.pdf --focus ats,formatting --json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### As a Library
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { parseResume, analyzeResume, suggestImprovements } from "resume-parser-ats";
|
|
66
|
+
|
|
67
|
+
// Step 1: Parse
|
|
68
|
+
const parsed = parseResume({ rawText: "..." });
|
|
69
|
+
console.log(parsed.data.profile.name); // "Jane Doe"
|
|
70
|
+
console.log(parsed.data.profile.email); // "jane@example.com"
|
|
71
|
+
console.log(parsed.data.education[0]?.school); // "MIT"
|
|
72
|
+
|
|
73
|
+
// Step 2: Analyze ATS compatibility
|
|
74
|
+
const analysis = analyzeResume({
|
|
75
|
+
rawText: "...",
|
|
76
|
+
parsedResume: parsed.data,
|
|
77
|
+
strictness: "moderate",
|
|
78
|
+
});
|
|
79
|
+
console.log(analysis.data.atsScore); // 72
|
|
80
|
+
console.log(analysis.data.atsGrade); // "B-"
|
|
81
|
+
|
|
82
|
+
// Step 3: Get improvement suggestions
|
|
83
|
+
const suggestions = suggestImprovements({
|
|
84
|
+
rawText: "...",
|
|
85
|
+
parsedResume: parsed.data,
|
|
86
|
+
analysisResult: analysis.data,
|
|
87
|
+
focusAreas: ["ats", "content", "formatting", "structure"],
|
|
88
|
+
});
|
|
89
|
+
console.log(suggestions.data.quickWins); // ["Fix your name format...", ...]
|
|
90
|
+
console.log(suggestions.data.suggestions[0].title); // "Name is not parseable by ATS"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### As an MCP Server
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm run mcp
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Starts a Model Context Protocol server exposing three tools:
|
|
100
|
+
|
|
101
|
+
| Tool | Description |
|
|
102
|
+
|------|-------------|
|
|
103
|
+
| `parse_resume` | Parse a resume PDF or raw text and return structured data |
|
|
104
|
+
| `analyze_resume` | Parse + compute ATS compatibility score with per-field confidence |
|
|
105
|
+
| `suggest_improvements` | Parse + analyze + generate prioritized improvement suggestions |
|
|
106
|
+
|
|
107
|
+
## š§ How It Works: The 4-Step Algorithm
|
|
108
|
+
|
|
109
|
+
The parser follows the **OpenResume algorithm**, a proven methodology used by real ATS systems:
|
|
110
|
+
|
|
111
|
+
### Step 1 ā Read Text Items
|
|
112
|
+
|
|
113
|
+
Extracts all text items from the resume, including:
|
|
114
|
+
- Text content
|
|
115
|
+
- X/Y positions (relative to bottom-left origin)
|
|
116
|
+
- Bold metadata
|
|
117
|
+
- Newline markers
|
|
118
|
+
|
|
119
|
+
### Step 2 ā Group Into Lines
|
|
120
|
+
|
|
121
|
+
Merges adjacent text items on the same Y-coordinate when their horizontal distance is less than the average character width. Groups by Y-coordinate to reconstruct the line-by-line reading order.
|
|
122
|
+
|
|
123
|
+
### Step 3 ā Group Into Sections
|
|
124
|
+
|
|
125
|
+
Detects section titles using two heuristics:
|
|
126
|
+
1. **Primary**: Only text item in line + bold + ALL UPPERCASE
|
|
127
|
+
2. **Fallback**: Keyword match against known headers (EDUCATION, EXPERIENCE, SKILLS, etc.)
|
|
128
|
+
|
|
129
|
+
### Step 4 ā Extract Attributes via Feature Scoring
|
|
130
|
+
|
|
131
|
+
Each attribute (Name, Email, Phone, etc.) has **feature sets** ā matching functions with positive/negative scores. The text item with the highest total score wins. This is how real ATS systems rank candidates:
|
|
132
|
+
|
|
133
|
+
| Feature (Name) | Score | Feature (Email) | Score |
|
|
134
|
+
|---|---|---|---|
|
|
135
|
+
| Contains only letters/spaces/periods | +3 | Matches email regex | +4 |
|
|
136
|
+
| Is bolded | +2 | Contains @ symbol | +4 |
|
|
137
|
+
| Is ALL UPPERCASE | +2 | Looks like a name (no @) | -1 |
|
|
138
|
+
| Contains @ (probably email) | -4 | Contains digits (no @) | -2 |
|
|
139
|
+
| Contains numbers (probably phone) | -4 | ā | ā |
|
|
140
|
+
|
|
141
|
+
## š Use Cases
|
|
142
|
+
|
|
143
|
+
### 1. šÆ Job Seeker ā ATS Optimization
|
|
144
|
+
|
|
145
|
+
> *Before applying to jobs, run your resume through the parser to see what an ATS actually extracts.*
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx resume-parser insights my-resume.pdf --strictness strict --json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Identify critical issues like a missing email, unparseable name, or sections an ATS can't detect ā and fix them *before* you apply.
|
|
152
|
+
|
|
153
|
+
### 2. š¢ Recruiter ā Bulk Resume Screening
|
|
154
|
+
|
|
155
|
+
> *Programmatically parse and score hundreds of resumes to rank candidates by ATS readability.*
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { parseResume, analyzeResume } from "resume-parser-ats";
|
|
159
|
+
import fs from "fs";
|
|
160
|
+
|
|
161
|
+
const files = fs.readdirSync("resumes/");
|
|
162
|
+
for (const file of files) {
|
|
163
|
+
const parsed = parseResume({ filePath: `resumes/${file}` });
|
|
164
|
+
const analysis = analyzeResume({
|
|
165
|
+
filePath: `resumes/${file}`,
|
|
166
|
+
parsedResume: parsed.data,
|
|
167
|
+
strictness: "moderate",
|
|
168
|
+
});
|
|
169
|
+
console.log(`${file}: ATS Score ${analysis.data.atsScore}/100 (${analysis.data.atsGrade})`);
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 3. š¤ Agent Integration ā AI-Powered Resume Coach
|
|
174
|
+
|
|
175
|
+
> *Embed the skill into an AI agent that reviews resumes and gives personalized coaching.*
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { fullPipeline } from "resume-parser-ats";
|
|
179
|
+
|
|
180
|
+
const result = fullPipeline({ rawText: resumeText, strictness: "strict" });
|
|
181
|
+
|
|
182
|
+
// result.parsed ā structured data
|
|
183
|
+
// result.analyzed ā ATS score + field analysis
|
|
184
|
+
// result.suggestions ā prioritized actions
|
|
185
|
+
|
|
186
|
+
// Feed to an LLM for natural-language coaching
|
|
187
|
+
const prompt = `You are a resume coach. Here is the analysis:
|
|
188
|
+
${JSON.stringify(result.analyzed.data)}
|
|
189
|
+
Suggest improvements in a friendly, encouraging tone.`;
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 4. š Career Platform ā Resume Health Dashboard
|
|
193
|
+
|
|
194
|
+
> *Show users a "resume health score" on your career platform dashboard.*
|
|
195
|
+
|
|
196
|
+
- Parse on upload ā store `atsScore`, `atsGrade`, and `fieldAnalyses`
|
|
197
|
+
- Display a visual dashboard with color-coded field ratings
|
|
198
|
+
- Surface `quickWins` as a checklist
|
|
199
|
+
- Track score improvements over time as users update their resumes
|
|
200
|
+
|
|
201
|
+
### 5. š University Career Center ā Student Resume Reviews
|
|
202
|
+
|
|
203
|
+
> *Automate initial resume screening for career centers at scale.*
|
|
204
|
+
|
|
205
|
+
- Batch-parse student resumes and generate summary reports
|
|
206
|
+
- Flag common issues (missing dates, non-standard section headers)
|
|
207
|
+
- Provide standardized improvement templates
|
|
208
|
+
|
|
209
|
+
### 6. š Resume Migration Tool
|
|
210
|
+
|
|
211
|
+
> *Convert resumes from one format to structured JSON for database ingestion.*
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { parseResume } from "resume-parser-ats";
|
|
215
|
+
|
|
216
|
+
const result = parseResume({ filePath: "legacy-resume.pdf" });
|
|
217
|
+
// result.data is a clean, typed JSON object ready for your database
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## šļø Architecture
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
resume-parser/
|
|
224
|
+
āāā package.json # Project metadata & scripts
|
|
225
|
+
āāā README.md # This file
|
|
226
|
+
āāā LICENSE # MIT License ā Dhanush Kandhan
|
|
227
|
+
āāā AGENTS.md # Agent-facing configuration
|
|
228
|
+
āāā SKILL.md # Skill definition for agent consumption
|
|
229
|
+
āāā src/
|
|
230
|
+
ā āāā index.ts # Main entry point + fullPipeline()
|
|
231
|
+
ā āāā tools/
|
|
232
|
+
ā ā āāā parse-resume.ts # Step 1-4 parsing engine
|
|
233
|
+
ā ā āāā analyze-resume.ts # ATS scoring & analysis
|
|
234
|
+
ā ā āāā suggest-improvements.ts # Fix suggestions generator
|
|
235
|
+
ā āāā prompts/
|
|
236
|
+
ā āāā parser-prompt.ts # Prompt templates for parsing
|
|
237
|
+
ā āāā insights-prompt.ts # Prompt templates for insights
|
|
238
|
+
āāā mcp-server/
|
|
239
|
+
ā āāā server.ts # MCP server implementation
|
|
240
|
+
āāā bin/
|
|
241
|
+
ā āāā cli.js # CLI entry point
|
|
242
|
+
āāā test/
|
|
243
|
+
āāā evals/ # Evaluation test suites
|
|
244
|
+
āāā parse-resume.test.js
|
|
245
|
+
āāā analyze-resume.test.js
|
|
246
|
+
āāā suggest-improvements.test.js
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## š§Ŗ Testing
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Run all tests
|
|
253
|
+
npm test
|
|
254
|
+
|
|
255
|
+
# Run evaluation suites
|
|
256
|
+
node --test test/evals/parse-resume.test.js
|
|
257
|
+
node --test test/evals/analyze-resume.test.js
|
|
258
|
+
node --test test/evals/suggest-improvements.test.js
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## š¤ Contributing
|
|
262
|
+
|
|
263
|
+
1. Fork the repository
|
|
264
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
265
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
266
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
267
|
+
5. Open a Pull Request
|
|
268
|
+
|
|
269
|
+
## āļø CI/CD
|
|
270
|
+
|
|
271
|
+
This project uses GitHub Actions for continuous integration and npm publishing:
|
|
272
|
+
|
|
273
|
+
| Workflow | Trigger | What it does |
|
|
274
|
+
|----------|---------|-------------|
|
|
275
|
+
| **Build & Test** | Push/PR to `master` | Lint, build, and test across Node 18/20/22 |
|
|
276
|
+
| **Publish to npm** | Tag push `v*` (e.g. `v1.0.0`) | Builds and publishes to npmjs with provenance |
|
|
277
|
+
|
|
278
|
+
To publish a new version:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npm version patch # or minor, major
|
|
282
|
+
git push --follow-tags
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## š License
|
|
286
|
+
|
|
287
|
+
MIT License ā Copyright (c) 2025 **Dhanush Kandhan**. See [LICENSE](./LICENSE) for details.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
<p align="center">
|
|
292
|
+
Made with ā¤ļø by <strong>Dhanush Kandhan</strong><br>
|
|
293
|
+
<em>If this project helped you, consider giving it a ā on GitHub!</em>
|
|
294
|
+
</p>
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resume Parser ā CLI Entry Point
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* resume-parser parse <file|text> Parse a resume and output structured data
|
|
8
|
+
* resume-parser analyze <file|text> Parse + analyze ATS compatibility
|
|
9
|
+
* resume-parser insights <file|text> Full pipeline: parse + analyze + suggestions
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require("path");
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const { parseResume } = require("../dist/src/tools/parse-resume");
|
|
15
|
+
const { analyzeResume } = require("../dist/src/tools/analyze-resume");
|
|
16
|
+
const { suggestImprovements } = require("../dist/src/tools/suggest-improvements");
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// CLI argument parsing
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
function printUsage() {
|
|
23
|
+
console.log(`
|
|
24
|
+
Resume Parser CLI ā Deep resume parsing with ATS insights
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
resume-parser <command> <input> [options]
|
|
28
|
+
|
|
29
|
+
Commands:
|
|
30
|
+
parse <file|text> Parse a resume and output structured data
|
|
31
|
+
analyze <file|text> Parse + analyze ATS compatibility
|
|
32
|
+
insights <file|text> Full pipeline: parse + analyze + suggestions
|
|
33
|
+
|
|
34
|
+
Options:
|
|
35
|
+
--strictness <level> ATS strictness: lenient, moderate, strict (default: moderate)
|
|
36
|
+
--focus <areas> Focus areas: ats,content,formatting,structure (default: all)
|
|
37
|
+
--json Output raw JSON instead of formatted report
|
|
38
|
+
--help Show this help message
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
resume-parser parse resume.pdf
|
|
42
|
+
resume-parser analyze resume.pdf --strictness strict
|
|
43
|
+
resume-parser insights resume.pdf --focus ats,formatting --json
|
|
44
|
+
resume-parser parse "John Doe\\njohn@email.com\\nSoftware Engineer"
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function main() {
|
|
49
|
+
const args = process.argv.slice(2);
|
|
50
|
+
|
|
51
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
52
|
+
printUsage();
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const command = args[0];
|
|
57
|
+
const inputArg = args[1];
|
|
58
|
+
|
|
59
|
+
if (!inputArg) {
|
|
60
|
+
console.error("Error: Please provide a file path or text input.");
|
|
61
|
+
printUsage();
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Parse options
|
|
66
|
+
const strictnessIdx = args.indexOf("--strictness");
|
|
67
|
+
const strictness = strictnessIdx >= 0 ? args[strictnessIdx + 1] : "moderate";
|
|
68
|
+
|
|
69
|
+
const focusIdx = args.indexOf("--focus");
|
|
70
|
+
const focusAreas = focusIdx >= 0
|
|
71
|
+
? args[focusIdx + 1].split(",")
|
|
72
|
+
: ["ats", "content", "formatting", "structure"];
|
|
73
|
+
|
|
74
|
+
const jsonOutput = args.includes("--json");
|
|
75
|
+
|
|
76
|
+
// Determine input type: file path or raw text
|
|
77
|
+
let filePath;
|
|
78
|
+
let rawText;
|
|
79
|
+
|
|
80
|
+
if (fs.existsSync(inputArg)) {
|
|
81
|
+
filePath = path.resolve(inputArg);
|
|
82
|
+
} else {
|
|
83
|
+
rawText = inputArg.replace(/\\n/g, "\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Execute command
|
|
87
|
+
switch (command) {
|
|
88
|
+
case "parse": {
|
|
89
|
+
const result = parseResume({ filePath, rawText });
|
|
90
|
+
if (jsonOutput) {
|
|
91
|
+
console.log(JSON.stringify(result, null, 2));
|
|
92
|
+
} else {
|
|
93
|
+
printParseResult(result);
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case "analyze": {
|
|
99
|
+
const parsed = parseResume({ filePath, rawText });
|
|
100
|
+
const analyzed = analyzeResume({
|
|
101
|
+
filePath,
|
|
102
|
+
rawText,
|
|
103
|
+
parsedResume: parsed.data,
|
|
104
|
+
strictness,
|
|
105
|
+
});
|
|
106
|
+
if (jsonOutput) {
|
|
107
|
+
console.log(JSON.stringify(analyzed, null, 2));
|
|
108
|
+
} else {
|
|
109
|
+
printAnalysisResult(analyzed);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case "insights": {
|
|
115
|
+
const parsed = parseResume({ filePath, rawText });
|
|
116
|
+
const analyzed = analyzeResume({
|
|
117
|
+
filePath,
|
|
118
|
+
rawText,
|
|
119
|
+
parsedResume: parsed.data,
|
|
120
|
+
strictness,
|
|
121
|
+
});
|
|
122
|
+
const suggestions = suggestImprovements({
|
|
123
|
+
filePath,
|
|
124
|
+
rawText,
|
|
125
|
+
parsedResume: parsed.data,
|
|
126
|
+
analysisResult: analyzed.data,
|
|
127
|
+
focusAreas,
|
|
128
|
+
});
|
|
129
|
+
if (jsonOutput) {
|
|
130
|
+
console.log(JSON.stringify(suggestions, null, 2));
|
|
131
|
+
} else {
|
|
132
|
+
printInsightsResult(suggestions);
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
default:
|
|
138
|
+
console.error("Unknown command: " + command);
|
|
139
|
+
printUsage();
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Formatted output helpers
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
function printParseResult(result) {
|
|
149
|
+
if (!result.success) {
|
|
150
|
+
console.log("ā Failed to parse resume");
|
|
151
|
+
console.log("Warnings:", result.metadata.warnings.join(", "));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const { profile, education, experience, skills, projects } = result.data;
|
|
156
|
+
|
|
157
|
+
console.log("\nš Resume Parsing Results\n");
|
|
158
|
+
console.log("ā".repeat(50));
|
|
159
|
+
|
|
160
|
+
// Profile
|
|
161
|
+
console.log("\n⨠Profile");
|
|
162
|
+
console.log("ā".repeat(30));
|
|
163
|
+
console.log(" Name: " + (profile.name || "Not parsed"));
|
|
164
|
+
console.log(" Email: " + (profile.email || "Not parsed"));
|
|
165
|
+
console.log(" Phone: " + (profile.phone || "Not parsed"));
|
|
166
|
+
console.log(" Location: " + (profile.location || "Not parsed"));
|
|
167
|
+
console.log(" Link: " + (profile.url || "Not parsed"));
|
|
168
|
+
|
|
169
|
+
if (profile.summary) {
|
|
170
|
+
console.log(" Summary: " + profile.summary);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Education
|
|
174
|
+
if (education.length > 0) {
|
|
175
|
+
console.log("\nš Education");
|
|
176
|
+
console.log("ā".repeat(30));
|
|
177
|
+
for (const edu of education) {
|
|
178
|
+
console.log(" School: " + (edu.school || "Not parsed"));
|
|
179
|
+
console.log(" Degree: " + (edu.degree || "Not parsed"));
|
|
180
|
+
console.log(" GPA: " + (edu.gpa || "Not parsed"));
|
|
181
|
+
console.log(" Date: " + (edu.date || "Not parsed"));
|
|
182
|
+
if (edu.descriptions.length > 0) {
|
|
183
|
+
console.log(" Descriptions: " + edu.descriptions.join("; "));
|
|
184
|
+
}
|
|
185
|
+
console.log();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Work Experience
|
|
190
|
+
if (experience.length > 0) {
|
|
191
|
+
console.log("š¼ Work Experience");
|
|
192
|
+
console.log("ā".repeat(30));
|
|
193
|
+
for (const exp of experience) {
|
|
194
|
+
console.log(" Company: " + (exp.company || "Not parsed"));
|
|
195
|
+
console.log(" Job Title: " + (exp.jobTitle || "Not parsed"));
|
|
196
|
+
console.log(" Date: " + (exp.date || "Not parsed"));
|
|
197
|
+
if (exp.descriptions.length > 0) {
|
|
198
|
+
console.log(" Descriptions:");
|
|
199
|
+
for (const d of exp.descriptions) {
|
|
200
|
+
console.log(" ⢠" + d);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
console.log();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Skills
|
|
208
|
+
if (skills.length > 0) {
|
|
209
|
+
console.log("š ļø Skills");
|
|
210
|
+
console.log("ā".repeat(30));
|
|
211
|
+
for (const skill of skills) {
|
|
212
|
+
for (const d of skill.descriptions) {
|
|
213
|
+
console.log(" ⢠" + d);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Projects
|
|
219
|
+
if (projects.length > 0) {
|
|
220
|
+
console.log("\nš Projects");
|
|
221
|
+
console.log("ā".repeat(30));
|
|
222
|
+
for (const proj of projects) {
|
|
223
|
+
console.log(" Name: " + (proj.name || "Not parsed"));
|
|
224
|
+
console.log(" Date: " + (proj.date || "Not parsed"));
|
|
225
|
+
if (proj.descriptions.length > 0) {
|
|
226
|
+
console.log(" Descriptions:");
|
|
227
|
+
for (const d of proj.descriptions) {
|
|
228
|
+
console.log(" ⢠" + d);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
console.log();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Metadata
|
|
236
|
+
console.log("\n" + "ā".repeat(50));
|
|
237
|
+
console.log("Steps completed: " + result.metadata.stepsCompleted.join(" ā "));
|
|
238
|
+
if (result.metadata.warnings.length > 0) {
|
|
239
|
+
console.log("Warnings: " + result.metadata.warnings.join(", "));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function printAnalysisResult(result) {
|
|
244
|
+
if (!result.success) {
|
|
245
|
+
console.log("ā Failed to analyze resume");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const { atsScore, atsGrade, fieldAnalyses, sectionAnalyses, overallNotes, formatIssues } = result.data;
|
|
250
|
+
|
|
251
|
+
console.log("\nš Resume Analysis Report\n");
|
|
252
|
+
console.log("ā".repeat(50));
|
|
253
|
+
console.log("\nšÆ ATS Compatibility Score: " + atsScore + "/100 (Grade: " + atsGrade + ")\n");
|
|
254
|
+
|
|
255
|
+
// Field analyses
|
|
256
|
+
console.log("š Field Extraction Analysis");
|
|
257
|
+
console.log("ā".repeat(40));
|
|
258
|
+
for (const field of fieldAnalyses) {
|
|
259
|
+
let icon = "ā";
|
|
260
|
+
if (field.confidence === "high") icon = "ā
";
|
|
261
|
+
else if (field.confidence === "medium") icon = "ā ļø";
|
|
262
|
+
else if (field.confidence === "low") icon = "š¶";
|
|
263
|
+
const val = field.value || "Not parsed";
|
|
264
|
+
console.log(" " + icon + " " + field.field.padEnd(12) + " | " + val.padEnd(30) + " | " + field.confidence);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Section analyses
|
|
268
|
+
console.log("\nš Section Detection");
|
|
269
|
+
console.log("ā".repeat(40));
|
|
270
|
+
for (const section of sectionAnalyses) {
|
|
271
|
+
const icon = section.detected ? "ā
" : "ā";
|
|
272
|
+
const issues = section.issues.length > 0 ? section.issues.join(", ") : "OK";
|
|
273
|
+
console.log(" " + icon + " " + section.section.padEnd(25) + " | Lines: " + section.lineCount + " | " + issues);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Format issues
|
|
277
|
+
if (formatIssues.length > 0) {
|
|
278
|
+
console.log("\nā ļø Format Issues");
|
|
279
|
+
console.log("ā".repeat(40));
|
|
280
|
+
for (const issue of formatIssues) {
|
|
281
|
+
let severityIcon = "š¢";
|
|
282
|
+
if (issue.severity === "critical") severityIcon = "š“";
|
|
283
|
+
else if (issue.severity === "high") severityIcon = "š ";
|
|
284
|
+
else if (issue.severity === "medium") severityIcon = "š”";
|
|
285
|
+
console.log(" " + severityIcon + " [" + issue.severity.toUpperCase() + "] " + issue.description);
|
|
286
|
+
console.log(" Affected: " + issue.affectedFields.join(", "));
|
|
287
|
+
console.log(" Fix: " + issue.suggestion);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Overall notes
|
|
292
|
+
console.log("\nš Overall Notes");
|
|
293
|
+
console.log("ā".repeat(40));
|
|
294
|
+
for (const note of overallNotes) {
|
|
295
|
+
console.log(" ⢠" + note);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
console.log("\n" + "ā".repeat(50));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function printInsightsResult(result) {
|
|
302
|
+
const { overallScore, overallGrade, criticalFixes, totalSuggestions, suggestions, sectionSuggestions, quickWins, longTermAdvice } = result.data;
|
|
303
|
+
|
|
304
|
+
console.log("\nš Resume Insights Report\n");
|
|
305
|
+
console.log("ā".repeat(50));
|
|
306
|
+
console.log("\nšÆ ATS Compatibility Score: " + overallScore + "/100 (Grade: " + overallGrade + ")");
|
|
307
|
+
console.log("š“ Critical fixes needed: " + criticalFixes);
|
|
308
|
+
console.log("š Total suggestions: " + totalSuggestions + "\n");
|
|
309
|
+
|
|
310
|
+
// Quick wins
|
|
311
|
+
if (quickWins.length > 0) {
|
|
312
|
+
console.log("ā” Quick Wins (fix these first):");
|
|
313
|
+
console.log("ā".repeat(40));
|
|
314
|
+
for (let i = 0; i < quickWins.length; i++) {
|
|
315
|
+
console.log(" " + (i + 1) + ". " + quickWins[i]);
|
|
316
|
+
}
|
|
317
|
+
console.log();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// All suggestions
|
|
321
|
+
if (suggestions.length > 0) {
|
|
322
|
+
console.log("š All Suggestions (sorted by priority):");
|
|
323
|
+
console.log("ā".repeat(40));
|
|
324
|
+
for (const sug of suggestions) {
|
|
325
|
+
let icon = "š¢";
|
|
326
|
+
if (sug.priority === "critical") icon = "š“";
|
|
327
|
+
else if (sug.priority === "high") icon = "š ";
|
|
328
|
+
else if (sug.priority === "medium") icon = "š”";
|
|
329
|
+
console.log("\n " + icon + " [" + sug.priority.toUpperCase() + "] " + sug.title);
|
|
330
|
+
console.log(" Field: " + sug.field);
|
|
331
|
+
console.log(" Issue: " + sug.description);
|
|
332
|
+
console.log(" Current: " + (sug.currentValue || "Not parsed"));
|
|
333
|
+
console.log(" Suggested: " + sug.suggestedValue);
|
|
334
|
+
console.log(" Why: " + sug.rationale);
|
|
335
|
+
}
|
|
336
|
+
console.log();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Section suggestions
|
|
340
|
+
console.log("š Section Status:");
|
|
341
|
+
console.log("ā".repeat(40));
|
|
342
|
+
for (const section of sectionSuggestions) {
|
|
343
|
+
const icon = section.present ? "ā
" : "ā";
|
|
344
|
+
console.log(" " + icon + " " + section.section);
|
|
345
|
+
if (section.issues.length > 0) {
|
|
346
|
+
for (const issue of section.issues) {
|
|
347
|
+
console.log(" ā ļø " + issue);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (section.recommendations.length > 0) {
|
|
351
|
+
for (const rec of section.recommendations) {
|
|
352
|
+
console.log(" š” " + rec);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Long-term advice
|
|
358
|
+
console.log("\nš Long-term Improvement Advice:");
|
|
359
|
+
console.log("ā".repeat(40));
|
|
360
|
+
for (const advice of longTermAdvice) {
|
|
361
|
+
console.log(" ⢠" + advice);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
console.log("\n" + "ā".repeat(50));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Run
|
|
368
|
+
main();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resume Parser ā MCP Server
|
|
3
|
+
*
|
|
4
|
+
* Exposes the resume parsing, analysis, and suggestion tools
|
|
5
|
+
* via the Model Context Protocol (MCP).
|
|
6
|
+
*
|
|
7
|
+
* Tools:
|
|
8
|
+
* - parse_resume: Parse a resume PDF/text and return structured data
|
|
9
|
+
* - analyze_resume: Parse + compute ATS compatibility score
|
|
10
|
+
* - suggest_improvements: Generate actionable fix suggestions
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* npm run mcp
|
|
14
|
+
*
|
|
15
|
+
* The server communicates via stdio using the MCP protocol.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../mcp-server/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
|