@getkrafter/resume-toolkit 1.0.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/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +4 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/krafter/client.d.ts +54 -0
- package/dist/krafter/client.d.ts.map +1 -0
- package/dist/krafter/client.js +130 -0
- package/dist/krafter/client.js.map +1 -0
- package/dist/krafter/errors.d.ts +26 -0
- package/dist/krafter/errors.d.ts.map +1 -0
- package/dist/krafter/errors.js +45 -0
- package/dist/krafter/errors.js.map +1 -0
- package/dist/lib/ats-scorer.d.ts +12 -0
- package/dist/lib/ats-scorer.d.ts.map +1 -0
- package/dist/lib/ats-scorer.js +83 -0
- package/dist/lib/ats-scorer.js.map +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/resume-scorer.d.ts +62 -0
- package/dist/lib/resume-scorer.d.ts.map +1 -0
- package/dist/lib/resume-scorer.js +236 -0
- package/dist/lib/resume-scorer.js.map +1 -0
- package/dist/lib/resume-transformer.d.ts +13 -0
- package/dist/lib/resume-transformer.d.ts.map +1 -0
- package/dist/lib/resume-transformer.js +113 -0
- package/dist/lib/resume-transformer.js.map +1 -0
- package/dist/lib/text-utils.d.ts +57 -0
- package/dist/lib/text-utils.d.ts.map +1 -0
- package/dist/lib/text-utils.js +282 -0
- package/dist/lib/text-utils.js.map +1 -0
- package/dist/lib/types.d.ts +31 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/mcp/server.d.ts +31 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +70 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/krafter.d.ts +14 -0
- package/dist/mcp/tools/krafter.d.ts.map +1 -0
- package/dist/mcp/tools/krafter.js +228 -0
- package/dist/mcp/tools/krafter.js.map +1 -0
- package/dist/mcp/tools/scoring.d.ts +46 -0
- package/dist/mcp/tools/scoring.d.ts.map +1 -0
- package/dist/mcp/tools/scoring.js +135 -0
- package/dist/mcp/tools/scoring.js.map +1 -0
- package/package.json +67 -0
- package/skills/score/SKILL.md +185 -0
- package/skills/tailor/SKILL.md +211 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Muhammad Kasim
|
|
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,155 @@
|
|
|
1
|
+
# @getkrafter/resume-toolkit
|
|
2
|
+
|
|
3
|
+
Deterministic resume scoring, ATS keyword matching, and AI-powered resume tailoring. Works as an MCP server, a scoring library, and AI skills for Claude Code, Cursor, and Windsurf.
|
|
4
|
+
|
|
5
|
+
**Same input always produces the same score.** The AI handles parsing and explanation — the scoring is pure math.
|
|
6
|
+
|
|
7
|
+
## Quick Start — Skills (no account needed)
|
|
8
|
+
|
|
9
|
+
### Claude Code
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
/install-skill @getkrafter/resume-toolkit
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then use `/score` or `/tailor` in any conversation.
|
|
16
|
+
|
|
17
|
+
### Cursor
|
|
18
|
+
|
|
19
|
+
Add to Settings → Remote Rule:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
https://github.com/getkrafter/resume-toolkit/blob/master/skills/score/SKILL.md
|
|
23
|
+
https://github.com/getkrafter/resume-toolkit/blob/master/skills/tailor/SKILL.md
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or copy the `skills/` directory to `.cursor/skills/` in your project.
|
|
27
|
+
|
|
28
|
+
### Windsurf
|
|
29
|
+
|
|
30
|
+
Copy the `skills/` directory to `.windsurf/skills/` or `.agents/skills/` in your project.
|
|
31
|
+
|
|
32
|
+
## Quick Start — MCP Server
|
|
33
|
+
|
|
34
|
+
### Without API key (scoring tools only)
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"krafter": {
|
|
40
|
+
"command": "npx",
|
|
41
|
+
"args": ["@getkrafter/resume-toolkit"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### With API key (scoring + Krafter CRUD)
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"krafter": {
|
|
53
|
+
"command": "npx",
|
|
54
|
+
"args": ["@getkrafter/resume-toolkit"],
|
|
55
|
+
"env": {
|
|
56
|
+
"KRAFTER_API_KEY": "sk-your-key-here"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Generate an API key at [krafter.app](https://krafter.app) → Settings → AI Integrations.
|
|
64
|
+
|
|
65
|
+
## Library Usage
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install @getkrafter/resume-toolkit
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { scoreResume, scoreATS, toResumeData } from '@getkrafter/resume-toolkit';
|
|
73
|
+
|
|
74
|
+
// Score a resume
|
|
75
|
+
const result = scoreResume(
|
|
76
|
+
{ rawText: '...', bullets: ['...'], sections: ['experience', 'skills'] },
|
|
77
|
+
'Job description text...' // optional
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
console.log(result.total); // 0-100
|
|
81
|
+
console.log(result.mode); // 'with-jd' or 'without-jd'
|
|
82
|
+
console.log(result.breakdown); // per-dimension scores
|
|
83
|
+
console.log(result.flags); // diagnostic messages
|
|
84
|
+
|
|
85
|
+
// ATS keyword match only
|
|
86
|
+
const ats = scoreATS(resumeText, jdText);
|
|
87
|
+
console.log(ats?.matched); // keywords found
|
|
88
|
+
console.log(ats?.missing); // keywords missing
|
|
89
|
+
|
|
90
|
+
// Convert Krafter resume object to scoreable format
|
|
91
|
+
const resumeData = toResumeData(krafterResumeObject);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## MCP Tools
|
|
95
|
+
|
|
96
|
+
### Public (no auth)
|
|
97
|
+
|
|
98
|
+
| Tool | Description |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `score_resume` | Full quality score (0-100) across 5 dimensions with breakdown and flags |
|
|
101
|
+
| `score_ats` | ATS keyword match with bigram/unigram analysis |
|
|
102
|
+
|
|
103
|
+
### Krafter (API key required)
|
|
104
|
+
|
|
105
|
+
| Tool | Description |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `score_krafter_resume` | Fetch resume from Krafter → score in one call |
|
|
108
|
+
| `get_resume` | Fetch a resume by ID |
|
|
109
|
+
| `list_resumes` | List all your resumes |
|
|
110
|
+
| `create_resume` | Create a new resume |
|
|
111
|
+
| `update_resume` | Update an existing resume |
|
|
112
|
+
| `delete_resume` | Delete a resume |
|
|
113
|
+
| `duplicate_resume` | Clone a resume |
|
|
114
|
+
| `update_settings` | Update visual settings |
|
|
115
|
+
| `update_section` | Update a specific section |
|
|
116
|
+
| `list_templates` | List available templates |
|
|
117
|
+
| `get_resume_schema` | Get the resume data schema |
|
|
118
|
+
|
|
119
|
+
## Skills
|
|
120
|
+
|
|
121
|
+
| Skill | Description |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `/score` | Guided resume scoring — paste or provide a resume, optionally add a JD, get a detailed score breakdown with actionable advice |
|
|
124
|
+
| `/tailor` | Gap analysis against a JD — identifies missing keywords, suggests truth-preserving rewrites with before/after format |
|
|
125
|
+
|
|
126
|
+
## Scoring Dimensions
|
|
127
|
+
|
|
128
|
+
| Dimension | Weight (with JD) | What it measures |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| Quantification | 25% | Fraction of bullets with numbers/metrics |
|
|
131
|
+
| Verb Strength | 20% | Quality of action verbs (tier1 > tier2 > tier3) |
|
|
132
|
+
| ATS Match | 30% | Keyword overlap with job description |
|
|
133
|
+
| Bullet Structure | 15% | Verb + number + detail pattern |
|
|
134
|
+
| Section Completeness | 10% | Presence of expected resume sections |
|
|
135
|
+
|
|
136
|
+
When no JD is provided, the 30% ATS weight redistributes proportionally across the other dimensions.
|
|
137
|
+
|
|
138
|
+
## Contributing
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
git clone https://github.com/getkrafter/resume-toolkit.git
|
|
142
|
+
cd resume-toolkit
|
|
143
|
+
npm install
|
|
144
|
+
npm test # run tests
|
|
145
|
+
npm run build # compile TypeScript
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Commits follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
149
|
+
- `feat: ...` → minor version bump
|
|
150
|
+
- `fix: ...` → patch version bump
|
|
151
|
+
- `feat!: ...` → major version bump
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":""}
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client wrapping Krafter's `/api/mcp` tool-call endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Every public method delegates to {@link callTool} which handles
|
|
5
|
+
* authentication, timeout, and error mapping so callers get
|
|
6
|
+
* strongly-typed errors they can pattern-match on.
|
|
7
|
+
*/
|
|
8
|
+
export declare class KrafterClient {
|
|
9
|
+
private apiKey;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
private schemaCache;
|
|
12
|
+
constructor(apiKey: string, baseUrl?: string);
|
|
13
|
+
/** Fetch a single resume by id. */
|
|
14
|
+
getResume(id: string): Promise<unknown>;
|
|
15
|
+
/** List all resumes for the authenticated user. */
|
|
16
|
+
listResumes(): Promise<unknown>;
|
|
17
|
+
/** Create a new resume. */
|
|
18
|
+
createResume(data: unknown): Promise<unknown>;
|
|
19
|
+
/** Update an existing resume. */
|
|
20
|
+
updateResume(id: string, data: unknown): Promise<unknown>;
|
|
21
|
+
/** Delete a resume. */
|
|
22
|
+
deleteResume(id: string): Promise<unknown>;
|
|
23
|
+
/** Duplicate a resume. */
|
|
24
|
+
duplicateResume(id: string): Promise<unknown>;
|
|
25
|
+
/** Update resume settings (template, colors, etc.). */
|
|
26
|
+
updateSettings(id: string, settings: unknown): Promise<unknown>;
|
|
27
|
+
/** Update a specific resume section (experience, education, etc.). */
|
|
28
|
+
updateSection(id: string, type: string, items: unknown[]): Promise<unknown>;
|
|
29
|
+
/** List available templates. */
|
|
30
|
+
listTemplates(): Promise<unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Fetch the resume JSON schema.
|
|
33
|
+
*
|
|
34
|
+
* The schema is cached after the first successful call because it
|
|
35
|
+
* doesn't change during a client's lifetime.
|
|
36
|
+
*/
|
|
37
|
+
getResumeSchema(): Promise<unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Execute a single tool call against the Krafter MCP endpoint.
|
|
40
|
+
*
|
|
41
|
+
* Handles:
|
|
42
|
+
* - Bearer authentication
|
|
43
|
+
* - 10-second timeout via AbortSignal
|
|
44
|
+
* - HTTP status → typed error mapping
|
|
45
|
+
* - Network / timeout errors → KrafterNetworkError
|
|
46
|
+
*/
|
|
47
|
+
private callTool;
|
|
48
|
+
/**
|
|
49
|
+
* Map an HTTP error status code to the appropriate typed error.
|
|
50
|
+
* Always throws — the return type is `never`.
|
|
51
|
+
*/
|
|
52
|
+
private throwForStatus;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/krafter/client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAwB;gBAE/B,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAS5C,mCAAmC;IAC7B,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI7C,mDAAmD;IAC7C,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC,2BAA2B;IACrB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAInD,iCAAiC;IAC3B,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/D,uBAAuB;IACjB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIhD,0BAA0B;IACpB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAInD,uDAAuD;IACjD,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrE,sEAAsE;IAChE,aAAa,CACjB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EAAE,GACf,OAAO,CAAC,OAAO,CAAC;IAInB,gCAAgC;IAC1B,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAIvC;;;;;OAKG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAczC;;;;;;;;OAQG;YACW,QAAQ;IA8BtB;;;OAGG;IACH,OAAO,CAAC,cAAc;CAiBvB"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client wrapping Krafter's `/api/mcp` tool-call endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Every public method delegates to {@link callTool} which handles
|
|
5
|
+
* authentication, timeout, and error mapping so callers get
|
|
6
|
+
* strongly-typed errors they can pattern-match on.
|
|
7
|
+
*/
|
|
8
|
+
import { KrafterAuthError, KrafterNetworkError, KrafterNotFoundError, KrafterRateLimitError, KrafterServerError, } from './errors.js';
|
|
9
|
+
/** Default request timeout in milliseconds. */
|
|
10
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
11
|
+
export class KrafterClient {
|
|
12
|
+
apiKey;
|
|
13
|
+
baseUrl;
|
|
14
|
+
schemaCache = null;
|
|
15
|
+
constructor(apiKey, baseUrl) {
|
|
16
|
+
this.apiKey = apiKey;
|
|
17
|
+
this.baseUrl = baseUrl ?? 'https://krafter.app';
|
|
18
|
+
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Public API
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
/** Fetch a single resume by id. */
|
|
23
|
+
async getResume(id) {
|
|
24
|
+
return this.callTool('get_resume', { id });
|
|
25
|
+
}
|
|
26
|
+
/** List all resumes for the authenticated user. */
|
|
27
|
+
async listResumes() {
|
|
28
|
+
return this.callTool('list_resumes', {});
|
|
29
|
+
}
|
|
30
|
+
/** Create a new resume. */
|
|
31
|
+
async createResume(data) {
|
|
32
|
+
return this.callTool('create_resume', { data });
|
|
33
|
+
}
|
|
34
|
+
/** Update an existing resume. */
|
|
35
|
+
async updateResume(id, data) {
|
|
36
|
+
return this.callTool('update_resume', { id, data });
|
|
37
|
+
}
|
|
38
|
+
/** Delete a resume. */
|
|
39
|
+
async deleteResume(id) {
|
|
40
|
+
return this.callTool('delete_resume', { id });
|
|
41
|
+
}
|
|
42
|
+
/** Duplicate a resume. */
|
|
43
|
+
async duplicateResume(id) {
|
|
44
|
+
return this.callTool('duplicate_resume', { id });
|
|
45
|
+
}
|
|
46
|
+
/** Update resume settings (template, colors, etc.). */
|
|
47
|
+
async updateSettings(id, settings) {
|
|
48
|
+
return this.callTool('update_settings', { id, settings });
|
|
49
|
+
}
|
|
50
|
+
/** Update a specific resume section (experience, education, etc.). */
|
|
51
|
+
async updateSection(id, type, items) {
|
|
52
|
+
return this.callTool('update_section', { id, type, items });
|
|
53
|
+
}
|
|
54
|
+
/** List available templates. */
|
|
55
|
+
async listTemplates() {
|
|
56
|
+
return this.callTool('list_templates', {});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Fetch the resume JSON schema.
|
|
60
|
+
*
|
|
61
|
+
* The schema is cached after the first successful call because it
|
|
62
|
+
* doesn't change during a client's lifetime.
|
|
63
|
+
*/
|
|
64
|
+
async getResumeSchema() {
|
|
65
|
+
if (this.schemaCache !== null) {
|
|
66
|
+
return this.schemaCache;
|
|
67
|
+
}
|
|
68
|
+
const schema = await this.callTool('get_resume_schema', {});
|
|
69
|
+
this.schemaCache = schema;
|
|
70
|
+
return schema;
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Internals
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
/**
|
|
76
|
+
* Execute a single tool call against the Krafter MCP endpoint.
|
|
77
|
+
*
|
|
78
|
+
* Handles:
|
|
79
|
+
* - Bearer authentication
|
|
80
|
+
* - 10-second timeout via AbortSignal
|
|
81
|
+
* - HTTP status → typed error mapping
|
|
82
|
+
* - Network / timeout errors → KrafterNetworkError
|
|
83
|
+
*/
|
|
84
|
+
async callTool(tool, args) {
|
|
85
|
+
const url = `${this.baseUrl}/api/mcp`;
|
|
86
|
+
let response;
|
|
87
|
+
try {
|
|
88
|
+
response = await fetch(url, {
|
|
89
|
+
method: 'POST',
|
|
90
|
+
headers: {
|
|
91
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify({ tool, args }),
|
|
95
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Network failures and AbortError (timeout) both land here.
|
|
100
|
+
throw new KrafterNetworkError();
|
|
101
|
+
}
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
this.throwForStatus(response.status);
|
|
104
|
+
}
|
|
105
|
+
const json = (await response.json());
|
|
106
|
+
return json.result;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Map an HTTP error status code to the appropriate typed error.
|
|
110
|
+
* Always throws — the return type is `never`.
|
|
111
|
+
*/
|
|
112
|
+
throwForStatus(status) {
|
|
113
|
+
switch (status) {
|
|
114
|
+
case 401:
|
|
115
|
+
throw new KrafterAuthError();
|
|
116
|
+
case 404:
|
|
117
|
+
throw new KrafterNotFoundError();
|
|
118
|
+
case 429:
|
|
119
|
+
throw new KrafterRateLimitError();
|
|
120
|
+
default:
|
|
121
|
+
// Treat any 5xx (or unexpected status) as a server error.
|
|
122
|
+
if (status >= 500) {
|
|
123
|
+
throw new KrafterServerError();
|
|
124
|
+
}
|
|
125
|
+
// Fallback for unexpected 4xx codes we don't have a class for.
|
|
126
|
+
throw new KrafterServerError();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/krafter/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed error classes for the Krafter HTTP client.
|
|
3
|
+
*
|
|
4
|
+
* Each error maps to a specific HTTP failure mode so callers can
|
|
5
|
+
* pattern-match on the class (instanceof) or inspect `statusCode`.
|
|
6
|
+
*/
|
|
7
|
+
export declare class KrafterError extends Error {
|
|
8
|
+
statusCode?: number | undefined;
|
|
9
|
+
constructor(message: string, statusCode?: number | undefined);
|
|
10
|
+
}
|
|
11
|
+
export declare class KrafterAuthError extends KrafterError {
|
|
12
|
+
constructor();
|
|
13
|
+
}
|
|
14
|
+
export declare class KrafterNotFoundError extends KrafterError {
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
17
|
+
export declare class KrafterServerError extends KrafterError {
|
|
18
|
+
constructor();
|
|
19
|
+
}
|
|
20
|
+
export declare class KrafterRateLimitError extends KrafterError {
|
|
21
|
+
constructor();
|
|
22
|
+
}
|
|
23
|
+
export declare class KrafterNetworkError extends KrafterError {
|
|
24
|
+
constructor();
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/krafter/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qBAAa,YAAa,SAAQ,KAAK;IAG5B,UAAU,CAAC,EAAE,MAAM;gBAD1B,OAAO,EAAE,MAAM,EACR,UAAU,CAAC,EAAE,MAAM,YAAA;CAK7B;AAED,qBAAa,gBAAiB,SAAQ,YAAY;;CAQjD;AAED,qBAAa,oBAAqB,SAAQ,YAAY;;CAKrD;AAED,qBAAa,kBAAmB,SAAQ,YAAY;;CAKnD;AAED,qBAAa,qBAAsB,SAAQ,YAAY;;CAKtD;AAED,qBAAa,mBAAoB,SAAQ,YAAY;;CAKpD"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed error classes for the Krafter HTTP client.
|
|
3
|
+
*
|
|
4
|
+
* Each error maps to a specific HTTP failure mode so callers can
|
|
5
|
+
* pattern-match on the class (instanceof) or inspect `statusCode`.
|
|
6
|
+
*/
|
|
7
|
+
export class KrafterError extends Error {
|
|
8
|
+
statusCode;
|
|
9
|
+
constructor(message, statusCode) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.statusCode = statusCode;
|
|
12
|
+
this.name = 'KrafterError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export class KrafterAuthError extends KrafterError {
|
|
16
|
+
constructor() {
|
|
17
|
+
super('Invalid API key. Generate one at krafter.app → Settings → AI Integrations.', 401);
|
|
18
|
+
this.name = 'KrafterAuthError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export class KrafterNotFoundError extends KrafterError {
|
|
22
|
+
constructor() {
|
|
23
|
+
super('Resume not found. It may have been deleted.', 404);
|
|
24
|
+
this.name = 'KrafterNotFoundError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class KrafterServerError extends KrafterError {
|
|
28
|
+
constructor() {
|
|
29
|
+
super('Krafter service error. Try again shortly.', 500);
|
|
30
|
+
this.name = 'KrafterServerError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export class KrafterRateLimitError extends KrafterError {
|
|
34
|
+
constructor() {
|
|
35
|
+
super('Too many requests. Please wait a moment.', 429);
|
|
36
|
+
this.name = 'KrafterRateLimitError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class KrafterNetworkError extends KrafterError {
|
|
40
|
+
constructor() {
|
|
41
|
+
super('Unable to reach Krafter. Check your connection or try again.');
|
|
42
|
+
this.name = 'KrafterNetworkError';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/krafter/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAG5B;IAFT,YACE,OAAe,EACR,UAAmB;QAE1B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,eAAU,GAAV,UAAU,CAAS;QAG1B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,YAAY;IAChD;QACE,KAAK,CACH,4EAA4E,EAC5E,GAAG,CACJ,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,OAAO,oBAAqB,SAAQ,YAAY;IACpD;QACE,KAAK,CAAC,6CAA6C,EAAE,GAAG,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,YAAY;IAClD;QACE,KAAK,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IACrD;QACE,KAAK,CAAC,0CAA0C,EAAE,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,YAAY;IACnD;QACE,KAAK,CAAC,8DAA8D,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ATSResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Score a resume against a job description for ATS keyword match.
|
|
4
|
+
*
|
|
5
|
+
* Uses bigram + stemmed unigram matching with weighted scoring:
|
|
6
|
+
* - Bigram match = 1.5 points
|
|
7
|
+
* - Unigram match = 1.0 point
|
|
8
|
+
*
|
|
9
|
+
* Returns null if jdText is falsy (no JD provided).
|
|
10
|
+
*/
|
|
11
|
+
export declare function scoreATS(resumeText: string, jdText: string): ATSResult | null;
|
|
12
|
+
//# sourceMappingURL=ats-scorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ats-scorer.d.ts","sourceRoot":"","sources":["../../src/lib/ats-scorer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAiF7E"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { stem, extractTerms } from './text-utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Score a resume against a job description for ATS keyword match.
|
|
4
|
+
*
|
|
5
|
+
* Uses bigram + stemmed unigram matching with weighted scoring:
|
|
6
|
+
* - Bigram match = 1.5 points
|
|
7
|
+
* - Unigram match = 1.0 point
|
|
8
|
+
*
|
|
9
|
+
* Returns null if jdText is falsy (no JD provided).
|
|
10
|
+
*/
|
|
11
|
+
export function scoreATS(resumeText, jdText) {
|
|
12
|
+
if (!jdText)
|
|
13
|
+
return null;
|
|
14
|
+
const resumeTerms = extractTerms(resumeText);
|
|
15
|
+
const jdTerms = extractTerms(jdText);
|
|
16
|
+
// Separate JD terms into bigrams and unigrams
|
|
17
|
+
const jdBigrams = [];
|
|
18
|
+
const jdUnigrams = [];
|
|
19
|
+
for (const term of jdTerms) {
|
|
20
|
+
if (term.includes(' ')) {
|
|
21
|
+
jdBigrams.push(term);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
jdUnigrams.push(term);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Match bigrams
|
|
28
|
+
const bigramsMatched = [];
|
|
29
|
+
const bigramsMissing = [];
|
|
30
|
+
for (const bigram of jdBigrams) {
|
|
31
|
+
if (resumeTerms.has(bigram)) {
|
|
32
|
+
bigramsMatched.push(bigram);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Fallback: check if both unigrams in the bigram match individually
|
|
36
|
+
const parts = bigram.split(' ');
|
|
37
|
+
const bothMatch = parts.every((part) => resumeTerms.has(stem(part)));
|
|
38
|
+
if (bothMatch) {
|
|
39
|
+
bigramsMatched.push(bigram);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
bigramsMissing.push(bigram);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Match unigrams (stemmed)
|
|
47
|
+
const unigramsMatched = [];
|
|
48
|
+
const unigramsMissing = [];
|
|
49
|
+
// Track which stemmed resume unigrams we have for unigram matching
|
|
50
|
+
const resumeUnigrams = new Set();
|
|
51
|
+
for (const term of resumeTerms) {
|
|
52
|
+
if (!term.includes(' ')) {
|
|
53
|
+
resumeUnigrams.add(term);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const unigram of jdUnigrams) {
|
|
57
|
+
if (resumeUnigrams.has(unigram)) {
|
|
58
|
+
unigramsMatched.push(unigram);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
unigramsMissing.push(unigram);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Weighted scoring: bigram = 1.5, unigram = 1.0
|
|
65
|
+
const totalPoints = jdBigrams.length * 1.5 + jdUnigrams.length * 1.0;
|
|
66
|
+
const earnedPoints = bigramsMatched.length * 1.5 + unigramsMatched.length * 1.0;
|
|
67
|
+
const score = totalPoints === 0 ? 0 : Math.round((earnedPoints / totalPoints) * 100);
|
|
68
|
+
// Build human-readable matched/missing lists
|
|
69
|
+
const matched = [...bigramsMatched, ...unigramsMatched];
|
|
70
|
+
const missing = [...bigramsMissing, ...unigramsMissing];
|
|
71
|
+
return {
|
|
72
|
+
score,
|
|
73
|
+
matched,
|
|
74
|
+
missing,
|
|
75
|
+
details: {
|
|
76
|
+
bigramsMatched,
|
|
77
|
+
unigramsMatched,
|
|
78
|
+
bigramsMissing,
|
|
79
|
+
unigramsMissing,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=ats-scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ats-scorer.js","sourceRoot":"","sources":["../../src/lib/ats-scorer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { scoreATS } from './ats-scorer.js';
|
|
2
|
+
export { scoreResume, getVerbTier } from './resume-scorer.js';
|
|
3
|
+
export { toResumeData } from './resume-transformer.js';
|
|
4
|
+
export type { ResumeData, ATSResult, ResumeScore, ScoreDimension, ScoreMode, VerbTier, } from './types.js';
|
|
5
|
+
export { stem, tokenize, normalise, extractTerms } from './text-utils.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,YAAY,EACV,UAAU,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,SAAS,EACT,QAAQ,GACT,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Stable public API — covered by semver guarantees
|
|
2
|
+
export { scoreATS } from './ats-scorer.js';
|
|
3
|
+
export { scoreResume, getVerbTier } from './resume-scorer.js';
|
|
4
|
+
export { toResumeData } from './resume-transformer.js';
|
|
5
|
+
// Internal utilities — NOT part of the stable API, may change in minor versions
|
|
6
|
+
// Exported for advanced consumers who accept the risk
|
|
7
|
+
export { stem, tokenize, normalise, extractTerms } from './text-utils.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAUvD,gFAAgF;AAChF,sDAAsD;AACtD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resume scoring engine.
|
|
3
|
+
*
|
|
4
|
+
* Provides a deterministic, multi-dimensional resume score composed of
|
|
5
|
+
* five sub-scorers: quantification, verb strength, bullet structure,
|
|
6
|
+
* section completeness, and ATS keyword matching.
|
|
7
|
+
*
|
|
8
|
+
* Main export: `scoreResume(resumeData, jdText?)` returns a `ResumeScore`.
|
|
9
|
+
*/
|
|
10
|
+
import type { ResumeData, ResumeScore, VerbTier } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Fraction of bullets that contain at least one number or metric.
|
|
13
|
+
* Returns 0-100. Returns 0 for empty bullets array.
|
|
14
|
+
*/
|
|
15
|
+
export declare function scoreQuantification(bullets: string[]): number;
|
|
16
|
+
/**
|
|
17
|
+
* Determine the verb tier for a bullet based on its first non-stop-word token.
|
|
18
|
+
*
|
|
19
|
+
* Tokenizes the bullet, skips stop words, takes the first meaningful token,
|
|
20
|
+
* stems it, and looks it up in the pre-computed stemmed verb map.
|
|
21
|
+
*
|
|
22
|
+
* Returns 1 (strong), 2 (solid), 3 (weak), or null (unrecognized).
|
|
23
|
+
*/
|
|
24
|
+
export declare function getVerbTier(bullet: string): VerbTier;
|
|
25
|
+
/**
|
|
26
|
+
* Average verb tier quality across bullets.
|
|
27
|
+
* Scoring: tier1=100, tier2=60, tier3=20, null=40.
|
|
28
|
+
* Returns 0-100. Returns 0 for empty bullets array.
|
|
29
|
+
*/
|
|
30
|
+
export declare function scoreVerbStrength(bullets: string[]): number;
|
|
31
|
+
/**
|
|
32
|
+
* Fraction of "strong" bullets. A bullet is strong if it:
|
|
33
|
+
* - Starts with a recognized verb (tier 1, 2, or 3)
|
|
34
|
+
* - Contains a number (`/\d+/`)
|
|
35
|
+
* - Has 8+ words
|
|
36
|
+
*
|
|
37
|
+
* Returns 0-100. Returns 0 for empty bullets array.
|
|
38
|
+
*/
|
|
39
|
+
export declare function scoreBulletStructure(bullets: string[]): number;
|
|
40
|
+
/**
|
|
41
|
+
* Presence of expected resume sections.
|
|
42
|
+
*
|
|
43
|
+
* Required sections ("experience", "education", "skills") are worth 20 points each.
|
|
44
|
+
* Recommended sections ("summary", "projects", "certifications") are worth ~13.33 points each.
|
|
45
|
+
* Match by substring: a section name includes the keyword.
|
|
46
|
+
* Capped at 100.
|
|
47
|
+
*/
|
|
48
|
+
export declare function scoreSectionCompleteness(sections: string[]): number;
|
|
49
|
+
/**
|
|
50
|
+
* Score a resume across five dimensions: quantification, verb strength,
|
|
51
|
+
* ATS keyword match, bullet structure, and section completeness.
|
|
52
|
+
*
|
|
53
|
+
* When a job description is provided, the ATS dimension is active and
|
|
54
|
+
* carries 30% weight. Without a JD, the ATS weight is redistributed
|
|
55
|
+
* proportionally across the other four dimensions.
|
|
56
|
+
*
|
|
57
|
+
* @param resumeData - Parsed resume data (raw text, bullets, sections)
|
|
58
|
+
* @param jdText - Optional job description text for ATS matching
|
|
59
|
+
* @returns A `ResumeScore` with total (0-100), mode, breakdown, ATS result, and flags.
|
|
60
|
+
*/
|
|
61
|
+
export declare function scoreResume(resumeData: ResumeData, jdText?: string): ResumeScore;
|
|
62
|
+
//# sourceMappingURL=resume-scorer.d.ts.map
|