resuml 1.13.1 → 1.13.2

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.
@@ -1,87 +1,3 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/mcp/server.ts
31
- var server_exports = {};
32
- __export(server_exports, {
33
- startMcpServer: () => startMcpServer
34
- });
35
- module.exports = __toCommonJS(server_exports);
36
-
37
- // node_modules/tsup/assets/cjs_shims.js
38
- var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
39
- var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
40
-
41
- // src/mcp/server.ts
42
- var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
43
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
44
- var import_zod = require("zod");
45
-
46
- // src/core.ts
47
- var import_yaml = require("yaml");
48
- var import_lodash = __toESM(require("lodash.merge"), 1);
49
- var import_schema = require("@jsonresume/schema");
50
- async function processResumeData(yamlContents) {
51
- if (yamlContents.length === 0) {
52
- throw new Error("No YAML content provided for processing.");
53
- }
54
- const dataObjects = yamlContents.map((content) => {
55
- try {
56
- return (0, import_yaml.parse)(content);
57
- } catch (error) {
58
- console.warn("Failed to parse YAML content:", error);
59
- return null;
60
- }
61
- }).filter((data) => typeof data === "object" && data !== null);
62
- if (dataObjects.length === 0) {
63
- throw new Error("No valid YAML content found after parsing.");
64
- }
65
- const customizer = (objValue, srcValue) => {
66
- if (Array.isArray(objValue)) {
67
- return objValue.concat(srcValue);
68
- }
69
- return void 0;
70
- };
71
- const mergedData = dataObjects.reduce((acc, data) => (0, import_lodash.default)(acc, data, customizer), {});
72
- return new Promise((resolve, reject) => {
73
- (0, import_schema.validate)(mergedData, (errors, isValid) => {
74
- if (!isValid) {
75
- reject(
76
- new Error(`Resume data failed schema validation: ${JSON.stringify(errors, null, 2)}`)
77
- );
78
- } else {
79
- resolve(mergedData);
80
- }
81
- });
82
- });
83
- }
84
-
85
1
  // src/ats/i18n/en.ts
86
2
  var en = {
87
3
  actionVerbs: [
@@ -1741,59 +1657,6 @@ function analyzeAts(resume, options = {}) {
1741
1657
  };
1742
1658
  }
1743
1659
 
1744
- // src/utils/themeLoader.ts
1745
- var import_child_process = require("child_process");
1746
- var import_module = require("module");
1747
- var require2 = (0, import_module.createRequire)(importMetaUrl);
1748
- function installTheme(packageName) {
1749
- try {
1750
- (0, import_child_process.execFileSync)("npm", ["install", packageName], {
1751
- stdio: ["inherit", "pipe", "pipe"],
1752
- encoding: "utf8"
1753
- });
1754
- } catch (error) {
1755
- throw new Error(`Failed to install ${packageName}: ${error.message}`);
1756
- }
1757
- }
1758
- function loadTheme(themeName, options) {
1759
- let jsonResumeThemeName;
1760
- let nativeThemeName;
1761
- const autoInstall = options?.autoInstall !== false;
1762
- try {
1763
- jsonResumeThemeName = themeName.startsWith("jsonresume-theme-") ? themeName : `jsonresume-theme-${themeName}`;
1764
- try {
1765
- return require2(jsonResumeThemeName);
1766
- } catch (_jsonResumeError) {
1767
- nativeThemeName = `@resuml/theme-${themeName}`;
1768
- try {
1769
- return require2(nativeThemeName);
1770
- } catch (_nativeError) {
1771
- if (!autoInstall) {
1772
- throw new Error(
1773
- `Theme package ${jsonResumeThemeName} or ${nativeThemeName} not found in node_modules.
1774
- Please install the theme package manually.`
1775
- );
1776
- }
1777
- console.log(`\u{1F4E6} Theme ${jsonResumeThemeName} not found. Installing...`);
1778
- try {
1779
- installTheme(jsonResumeThemeName);
1780
- console.log(`\u2705 Successfully installed ${jsonResumeThemeName}`);
1781
- return require2(jsonResumeThemeName);
1782
- } catch (installError) {
1783
- throw new Error(
1784
- `Failed to auto-install theme ${jsonResumeThemeName}: ${installError.message}`
1785
- );
1786
- }
1787
- }
1788
- }
1789
- } catch (error) {
1790
- if (error instanceof Error && error.message.includes("Failed to auto-install")) {
1791
- throw error;
1792
- }
1793
- throw new Error(`Theme package ${themeName} not found`);
1794
- }
1795
- }
1796
-
1797
1660
  // src/utils/resumeTemplate.ts
1798
1661
  function generateResumeYaml(name, email, label) {
1799
1662
  return `# =============================================================================
@@ -1935,7 +1798,7 @@ skills:
1935
1798
  }
1936
1799
 
1937
1800
  // src/utils/themeInfo.ts
1938
- var import_module2 = require("module");
1801
+ import { createRequire } from "module";
1939
1802
  var KNOWN_THEMES = [
1940
1803
  { name: "stackoverflow", pkg: "jsonresume-theme-stackoverflow", description: "Stack Overflow inspired theme" },
1941
1804
  { name: "elegant", pkg: "jsonresume-theme-elegant", description: "Elegant and professional" },
@@ -1952,7 +1815,7 @@ var KNOWN_THEMES = [
1952
1815
  ];
1953
1816
  function isThemeInstalled(pkg) {
1954
1817
  try {
1955
- const req = (0, import_module2.createRequire)(process.cwd() + "/");
1818
+ const req = createRequire(process.cwd() + "/");
1956
1819
  req.resolve(pkg);
1957
1820
  return true;
1958
1821
  } catch {
@@ -1961,7 +1824,7 @@ function isThemeInstalled(pkg) {
1961
1824
  }
1962
1825
  function getInstalledVersion(pkg) {
1963
1826
  try {
1964
- const req = (0, import_module2.createRequire)(process.cwd() + "/");
1827
+ const req = createRequire(process.cwd() + "/");
1965
1828
  const pkgJson = req(`${pkg}/package.json`);
1966
1829
  return pkgJson.version ?? null;
1967
1830
  } catch {
@@ -1969,619 +1832,11 @@ function getInstalledVersion(pkg) {
1969
1832
  }
1970
1833
  }
1971
1834
 
1972
- // src/mcp/server.ts
1973
- var originalLog = console.log;
1974
- var originalWarn = console.warn;
1975
- function suppressStdout() {
1976
- console.log = (...args) => {
1977
- console.error("[resuml]", ...args);
1978
- };
1979
- console.warn = (...args) => {
1980
- console.error("[resuml]", ...args);
1981
- };
1982
- }
1983
- function restoreStdout() {
1984
- console.log = originalLog;
1985
- console.warn = originalWarn;
1986
- }
1987
- var JSON_RESUME_SCHEMA_REFERENCE = `# JSON Resume Schema Reference
1988
-
1989
- The JSON Resume schema defines the structure for resume data. resuml uses YAML as the input format.
1990
-
1991
- ## Top-level sections
1992
-
1993
- | Section | Required | Description |
1994
- |---------|----------|-------------|
1995
- | basics | Yes | Name, label, email, phone, url, summary, location, profiles |
1996
- | work | Recommended | Work experience entries |
1997
- | education | Recommended | Education entries |
1998
- | skills | Recommended | Skill categories with keywords |
1999
- | projects | Optional | Project entries |
2000
- | volunteer | Optional | Volunteer experience |
2001
- | awards | Optional | Awards and honors |
2002
- | certificates | Optional | Professional certifications |
2003
- | publications | Optional | Published works |
2004
- | languages | Optional | Language proficiencies |
2005
- | interests | Optional | Personal interests |
2006
- | references | Optional | Professional references |
2007
-
2008
- ## Section schemas
2009
-
2010
- ### basics
2011
- \`\`\`yaml
2012
- basics:
2013
- name: "Full Name" # required
2014
- label: "Professional Title"
2015
- email: "email@example.com"
2016
- phone: "+1-555-123-4567"
2017
- url: "https://website.com"
2018
- summary: "2-4 sentence professional summary"
2019
- location:
2020
- city: "City"
2021
- countryCode: "US"
2022
- region: "State"
2023
- profiles:
2024
- - network: "LinkedIn"
2025
- username: "username"
2026
- url: "https://linkedin.com/in/username"
2027
- \`\`\`
2028
-
2029
- ### work
2030
- \`\`\`yaml
2031
- work:
2032
- - name: "Company Name"
2033
- position: "Job Title"
2034
- url: "https://company.com"
2035
- startDate: "2020-01-01" # ISO 8601
2036
- endDate: "2023-12-31" # omit for current position
2037
- summary: "Role description"
2038
- highlights:
2039
- - "Achievement with measurable result"
2040
- \`\`\`
2041
-
2042
- ### education
2043
- \`\`\`yaml
2044
- education:
2045
- - institution: "University"
2046
- area: "Field of Study"
2047
- studyType: "Degree Type" # e.g. Bachelor, Master, PhD
2048
- startDate: "2014-09-01"
2049
- endDate: "2018-06-01"
2050
- \`\`\`
2051
-
2052
- ### skills
2053
- \`\`\`yaml
2054
- skills:
2055
- - name: "Category"
2056
- level: "Expert" # Master, Expert, Advanced, Intermediate, Beginner
2057
- keywords: ["Skill1", "Skill2"]
2058
- \`\`\`
2059
-
2060
- ### projects
2061
- \`\`\`yaml
2062
- projects:
2063
- - name: "Project Name"
2064
- description: "What it does"
2065
- highlights: ["Key achievement"]
2066
- keywords: ["Tech1", "Tech2"]
2067
- startDate: "2023-01-01"
2068
- url: "https://github.com/..."
2069
- \`\`\`
2070
-
2071
- ### certificates
2072
- \`\`\`yaml
2073
- certificates:
2074
- - name: "Certificate Name"
2075
- date: "2023-01-01"
2076
- issuer: "Issuing Organization"
2077
- url: "https://credential-url.com"
2078
- \`\`\`
2079
-
2080
- ### languages
2081
- \`\`\`yaml
2082
- languages:
2083
- - language: "English"
2084
- fluency: "Native speaker" # Native speaker, Fluent, Advanced, Intermediate, Elementary
2085
- \`\`\`
2086
-
2087
- ## Formatting rules
2088
- - Dates: ISO 8601 format (YYYY-MM-DD or YYYY-MM)
2089
- - Start highlights with action verbs: Developed, Implemented, Led, Optimized, Reduced, Built, Designed
2090
- - Include numbers in 50%+ of highlights (e.g., "Reduced latency by 40%")
2091
- - Never use first person (I, my, me, we)
2092
- - Summary: 2-4 sentences positioning the candidate for the specific role
2093
- `;
2094
- var ATS_SCORING_RUBRIC = `# ATS Scoring Rubric
2095
-
2096
- resuml performs deterministic, offline ATS (Applicant Tracking System) analysis.
2097
-
2098
- ## Scoring system
2099
-
2100
- ### Rating scale
2101
- | Score | Rating | Description |
2102
- |-------|--------|-------------|
2103
- | 90-100 | Excellent | Resume is well-optimized for ATS |
2104
- | 75-89 | Good | Resume passes most ATS checks |
2105
- | 60-74 | Needs Work | Several improvements recommended |
2106
- | 0-59 | Poor | Significant issues found |
2107
-
2108
- ### Weight system
2109
- Each check has a weight that affects the final score:
2110
- - **High weight (3x)**: Critical checks that significantly impact ATS parsing
2111
- - **Medium weight (2x)**: Important but not critical checks
2112
- - **Low weight (1x)**: Nice-to-have improvements
2113
-
2114
- ### Combined scoring (with job description)
2115
- When a job description is provided:
2116
- - Generic checks: 60% of final score
2117
- - Keyword match: 40% of final score
2118
-
2119
- ## Checks performed
2120
-
2121
- ### Contact Information (category: contact)
2122
- | Check | Weight | What it verifies |
2123
- |-------|--------|-----------------|
2124
- | contact-complete | High | Name, email, phone, and city are all present |
2125
- | has-linkedin | Medium | LinkedIn profile exists in profiles section |
2126
-
2127
- ### Content Quality (category: content)
2128
- | Check | Weight | What it verifies |
2129
- |-------|--------|-----------------|
2130
- | has-summary | High | Professional summary exists (15-100 words) |
2131
- | work-highlights | High | Each work entry has at least 2 highlights |
2132
- | action-verbs | Medium | Highlights start with action verbs |
2133
- | quantified-impact | Medium | 50%+ of highlights include numbers/metrics |
2134
- | no-first-person | Low | No first-person pronouns (I, my, me, we) |
2135
-
2136
- ### Resume Structure (category: structure)
2137
- | Check | Weight | What it verifies |
2138
- |-------|--------|-----------------|
2139
- | date-consistency | Medium | All dates are valid ISO 8601 format |
2140
- | skills-populated | Medium | At least 3 skill categories defined |
2141
- | education-complete | Medium | Education section has institution and area |
2142
- | essential-sections | High | Work, education, and skills sections present |
2143
-
2144
- ## Job description matching
2145
- When a job description is provided, resuml extracts keywords using TF-based extraction
2146
- and matches them against the resume using stem matching. Results include:
2147
- - **matched**: Keywords found in the resume
2148
- - **missing**: Keywords not found (add these to improve score)
2149
- - **matchPercentage**: Percentage of JD keywords found in resume
2150
-
2151
- ## Fit Assessment
2152
- When a job description is provided, a \`fitAssessment\` field is included in the result:
2153
- | Match % | Level | Meaning |
2154
- |---------|-------|---------|
2155
- | >= 70% | strong | Resume aligns well with the job description |
2156
- | 50-69% | partial | Some alignment; emphasize transferable skills |
2157
- | < 50% | weak | Significant skill gaps; role may not match profile |
2158
-
2159
- The assessment includes the top 5 missing keywords as specific gaps to address.
2160
- Use this to advise users whether to apply or focus effort elsewhere.
2161
-
2162
- ## Tips for improving ATS score
2163
- 1. Include all contact information (name, email, phone, city)
2164
- 2. Add a LinkedIn profile URL
2165
- 3. Write a 2-4 sentence professional summary
2166
- 4. Use action verbs to start each highlight
2167
- 5. Quantify achievements with numbers (%, $, time saved, team size)
2168
- 6. Include at least 3 skill categories with relevant keywords
2169
- 7. When targeting a job, mirror exact terminology from the job description
2170
- `;
2171
- function createServer() {
2172
- const server = new import_mcp.McpServer({
2173
- name: "resuml",
2174
- version: "1.0.0"
2175
- });
2176
- server.registerResource(
2177
- "json-resume-schema",
2178
- "resuml://schema/json-resume",
2179
- {
2180
- description: "JSON Resume schema reference with all sections, field types, and formatting rules",
2181
- mimeType: "text/markdown"
2182
- },
2183
- () => ({
2184
- contents: [{
2185
- uri: "resuml://schema/json-resume",
2186
- mimeType: "text/markdown",
2187
- text: JSON_RESUME_SCHEMA_REFERENCE
2188
- }]
2189
- })
2190
- );
2191
- server.registerResource(
2192
- "ats-scoring-rubric",
2193
- "resuml://docs/ats-scoring",
2194
- {
2195
- description: "ATS scoring rubric: checks performed, weight system, rating scale, and tips for improving score",
2196
- mimeType: "text/markdown"
2197
- },
2198
- () => ({
2199
- contents: [{
2200
- uri: "resuml://docs/ats-scoring",
2201
- mimeType: "text/markdown",
2202
- text: ATS_SCORING_RUBRIC
2203
- }]
2204
- })
2205
- );
2206
- server.registerResource(
2207
- "theme-catalog",
2208
- "resuml://themes/catalog",
2209
- {
2210
- description: "Available resume themes with descriptions and installation status",
2211
- mimeType: "application/json"
2212
- },
2213
- () => {
2214
- const themes = KNOWN_THEMES.map((t) => ({
2215
- name: t.name,
2216
- package: t.pkg,
2217
- description: t.description,
2218
- installed: isThemeInstalled(t.pkg),
2219
- version: getInstalledVersion(t.pkg)
2220
- }));
2221
- return {
2222
- contents: [{
2223
- uri: "resuml://themes/catalog",
2224
- mimeType: "application/json",
2225
- text: JSON.stringify({ themes, totalCount: themes.length }, null, 2)
2226
- }]
2227
- };
2228
- }
2229
- );
2230
- server.registerPrompt(
2231
- "tailor-resume-to-jd",
2232
- {
2233
- title: "Tailor Resume to Job Description",
2234
- description: "Generate a tailored resume YAML optimized for a specific job description",
2235
- argsSchema: {
2236
- jobDescription: import_zod.z.string().describe("The full job description text"),
2237
- candidateName: import_zod.z.string().optional().describe("Candidate full name"),
2238
- candidateEmail: import_zod.z.string().optional().describe("Candidate email address"),
2239
- candidateBackground: import_zod.z.string().optional().describe("Brief summary of the candidate background, skills, and experience to incorporate")
2240
- }
2241
- },
2242
- ({ jobDescription, candidateName, candidateEmail, candidateBackground }) => ({
2243
- messages: [{
2244
- role: "user",
2245
- content: {
2246
- type: "text",
2247
- text: `Create a tailored resume in YAML format optimized for the following job description.
2248
-
2249
- ## Job Description
2250
- ${jobDescription}
2251
-
2252
- ${candidateBackground ? `## Candidate Background
2253
- ${candidateBackground}
2254
- ` : ""}
2255
- ## Instructions
2256
-
2257
- 1. Analyze the job description to identify required skills, technologies, experience level, and industry terms
2258
- 2. Generate a complete resume YAML following the JSON Resume schema (read the resuml://schema/json-resume resource for the full schema)
2259
- 3. Mirror exact terminology from the job description in skills and highlights
2260
- 4. Start every highlight with an action verb (Developed, Implemented, Led, Optimized, Reduced, Built, Designed)
2261
- 5. Include numbers in 50%+ of highlights (e.g., "Reduced latency by 40%", "Managed team of 8")
2262
- 6. Never use "I", "my", "me", "we"
2263
- 7. Write a 2-4 sentence summary positioning the candidate for this specific role
2264
- 8. Use ISO 8601 dates (YYYY-MM-DD or YYYY-MM)
2265
- ${candidateName ? `9. Use candidate name: ${candidateName}` : ""}
2266
- ${candidateEmail ? `10. Use candidate email: ${candidateEmail}` : ""}
2267
-
2268
- ## Workflow
2269
- After generating the YAML:
2270
- 1. Use \`resuml_validate\` to check schema compliance
2271
- 2. Use \`resuml_ats_check\` with the job description text. Target: score >= 75, keyword match >= 70%
2272
- 3. If ATS score is low, revise the YAML and re-check
2273
- 4. Use \`resuml_render\` with theme "even" for the final output
2274
-
2275
- Output the resume YAML first, then run the validation and ATS check tools.`
2276
- }
2277
- }]
2278
- })
2279
- );
2280
- server.registerPrompt(
2281
- "optimize-ats-score",
2282
- {
2283
- title: "Optimize ATS Score",
2284
- description: "Analyze and improve an existing resume YAML to maximize its ATS score",
2285
- argsSchema: {
2286
- resumeYaml: import_zod.z.string().describe("The current resume YAML content"),
2287
- jobDescription: import_zod.z.string().optional().describe("Optional job description to optimize against"),
2288
- targetScore: import_zod.z.string().optional().describe("Target ATS score (default: 85)")
2289
- }
2290
- },
2291
- ({ resumeYaml, jobDescription, targetScore }) => ({
2292
- messages: [{
2293
- role: "user",
2294
- content: {
2295
- type: "text",
2296
- text: `Optimize this resume YAML to maximize its ATS score${targetScore ? ` (target: ${targetScore})` : " (target: 85+)"}.
2297
-
2298
- ## Current Resume YAML
2299
- \`\`\`yaml
2300
- ${resumeYaml}
2301
- \`\`\`
2302
-
2303
- ${jobDescription ? `## Job Description
2304
- ${jobDescription}
2305
- ` : ""}
2306
- ## Instructions
2307
-
2308
- 1. First, run \`resuml_ats_check\` on the current YAML${jobDescription ? " with the job description" : ""} to get the baseline score
2309
- 2. Read the ATS scoring rubric (resuml://docs/ats-scoring) to understand what checks are performed
2310
- 3. Review each failed or low-scoring check and fix the issues:
2311
- - Missing contact info \u2192 add it
2312
- - No summary \u2192 write a 2-4 sentence professional summary
2313
- - Weak highlights \u2192 rewrite with action verbs and quantified metrics
2314
- - Missing keywords \u2192 incorporate them naturally into skills and highlights
2315
- - Structural issues \u2192 ensure all essential sections are present
2316
- 4. Run \`resuml_ats_check\` again to verify improvement
2317
- 5. Repeat until the target score is reached
2318
-
2319
- Output the improved YAML with a summary of changes made.`
2320
- }
2321
- }]
2322
- })
2323
- );
2324
- server.registerPrompt(
2325
- "review-resume",
2326
- {
2327
- title: "Review Resume",
2328
- description: "Comprehensive review of a resume YAML with ATS analysis and improvement suggestions",
2329
- argsSchema: {
2330
- resumeYaml: import_zod.z.string().describe("The resume YAML content to review")
2331
- }
2332
- },
2333
- ({ resumeYaml }) => ({
2334
- messages: [{
2335
- role: "user",
2336
- content: {
2337
- type: "text",
2338
- text: `Perform a comprehensive review of this resume.
2339
-
2340
- ## Resume YAML
2341
- \`\`\`yaml
2342
- ${resumeYaml}
2343
- \`\`\`
2344
-
2345
- ## Review steps
2346
-
2347
- 1. Run \`resuml_validate\` to check schema compliance
2348
- 2. Run \`resuml_ats_check\` for ATS analysis
2349
- 3. Review content quality:
2350
- - Is the summary compelling and role-specific?
2351
- - Do highlights use strong action verbs?
2352
- - Are achievements quantified with metrics?
2353
- - Are skills well-organized and comprehensive?
2354
- - Is the work history clear and impactful?
2355
- 4. Provide a structured review with:
2356
- - **ATS Score**: Current score and rating
2357
- - **Schema Issues**: Any validation errors
2358
- - **Strengths**: What the resume does well
2359
- - **Improvements**: Specific, actionable suggestions
2360
- - **Revised YAML**: An improved version if significant changes are recommended`
2361
- }
2362
- }]
2363
- })
2364
- );
2365
- server.registerTool(
2366
- "resuml_init_resume",
2367
- {
2368
- title: "Init Resume",
2369
- description: "Generate a starter resume YAML template following the JSON Resume schema",
2370
- inputSchema: {
2371
- name: import_zod.z.string().optional().describe("Full name for the resume"),
2372
- title: import_zod.z.string().optional().describe("Professional title/label"),
2373
- email: import_zod.z.string().optional().describe("Email address")
2374
- }
2375
- },
2376
- ({ name, title, email }) => {
2377
- const yaml = generateResumeYaml(
2378
- name ?? "Your Name",
2379
- email ?? "email@example.com",
2380
- title ?? "Professional Title"
2381
- );
2382
- return { content: [{ type: "text", text: yaml }] };
2383
- }
2384
- );
2385
- server.registerTool(
2386
- "resuml_validate",
2387
- {
2388
- title: "Validate Resume",
2389
- description: "Validate resume YAML against the JSON Resume schema",
2390
- inputSchema: {
2391
- yaml: import_zod.z.string().describe("Resume content in YAML format")
2392
- }
2393
- },
2394
- async ({ yaml }) => {
2395
- suppressStdout();
2396
- try {
2397
- await processResumeData([yaml]);
2398
- restoreStdout();
2399
- return {
2400
- content: [{ type: "text", text: JSON.stringify({ valid: true, errors: [] }, null, 2) }]
2401
- };
2402
- } catch (e) {
2403
- restoreStdout();
2404
- const message = e instanceof Error ? e.message : String(e);
2405
- return {
2406
- content: [{ type: "text", text: JSON.stringify({ valid: false, errors: [message] }, null, 2) }]
2407
- };
2408
- }
2409
- }
2410
- );
2411
- server.registerTool(
2412
- "resuml_ats_check",
2413
- {
2414
- title: "ATS Check",
2415
- description: "Run ATS (Applicant Tracking System) analysis on a resume, optionally matching against a job description",
2416
- inputSchema: {
2417
- yaml: import_zod.z.string().describe("Resume content in YAML format"),
2418
- jobDescription: import_zod.z.string().optional().describe("Job description text to match keywords against"),
2419
- language: import_zod.z.enum(["en", "de"]).optional().describe("Language for analysis (default: en)")
2420
- }
2421
- },
2422
- async ({ yaml, jobDescription, language }) => {
2423
- suppressStdout();
2424
- try {
2425
- const resume = await processResumeData([yaml]);
2426
- const result = analyzeAts(resume, {
2427
- language: language ?? "en",
2428
- jobDescription
2429
- });
2430
- restoreStdout();
2431
- return {
2432
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2433
- };
2434
- } catch (e) {
2435
- restoreStdout();
2436
- return {
2437
- content: [{ type: "text", text: JSON.stringify({ error: e instanceof Error ? e.message : String(e) }) }],
2438
- isError: true
2439
- };
2440
- }
2441
- }
2442
- );
2443
- server.registerTool(
2444
- "resuml_render",
2445
- {
2446
- title: "Render Resume",
2447
- description: "Render a resume to HTML using a specified theme",
2448
- inputSchema: {
2449
- yaml: import_zod.z.string().describe("Resume content in YAML format"),
2450
- theme: import_zod.z.string().default("even").describe("Theme name (e.g. even, stackoverflow, elegant, paper, kendall)"),
2451
- locale: import_zod.z.string().optional().describe("Locale for theme rendering (e.g. en, de)")
2452
- }
2453
- },
2454
- async (args) => {
2455
- const { yaml, theme } = args;
2456
- const locale = args["locale"];
2457
- suppressStdout();
2458
- try {
2459
- const resume = await processResumeData([yaml]);
2460
- const themeModule = loadTheme(theme, { autoInstall: false });
2461
- const renderOptions = {};
2462
- if (locale) renderOptions["locale"] = locale;
2463
- const html = await themeModule.render(resume, renderOptions);
2464
- restoreStdout();
2465
- return { content: [{ type: "text", text: html }] };
2466
- } catch (e) {
2467
- restoreStdout();
2468
- const message = e instanceof Error ? e.message : String(e);
2469
- const hint = message.includes("Cannot find module") ? `. Install with: resuml themes --install ${theme}` : "";
2470
- return {
2471
- content: [{ type: "text", text: JSON.stringify({ error: message + hint }) }],
2472
- isError: true
2473
- };
2474
- }
2475
- }
2476
- );
2477
- server.registerTool(
2478
- "resuml_list_themes",
2479
- {
2480
- title: "List Themes",
2481
- description: "List available resume themes with their installation status"
2482
- },
2483
- () => {
2484
- const themes = KNOWN_THEMES.map((t) => ({
2485
- name: t.name,
2486
- package: t.pkg,
2487
- description: t.description,
2488
- installed: isThemeInstalled(t.pkg),
2489
- version: getInstalledVersion(t.pkg)
2490
- }));
2491
- return {
2492
- content: [{ type: "text", text: JSON.stringify({ themes }, null, 2) }]
2493
- };
2494
- }
2495
- );
2496
- server.registerTool(
2497
- "resuml_export_pdf",
2498
- {
2499
- title: "Export PDF",
2500
- description: "Export a resume as PDF (requires Playwright to be installed)",
2501
- inputSchema: {
2502
- yaml: import_zod.z.string().describe("Resume content in YAML format"),
2503
- theme: import_zod.z.string().default("even").describe("Theme name"),
2504
- format: import_zod.z.enum(["A4", "Letter"]).default("A4").describe("Paper format"),
2505
- locale: import_zod.z.string().optional().describe("Locale for theme rendering (e.g. en, de)"),
2506
- margin: import_zod.z.string().optional().describe('Page margins. Single value (e.g. "10mm") for all sides, two values (e.g. "10mm,15mm") for vertical/horizontal, or four values (e.g. "10mm,15mm,10mm,15mm") for top/right/bottom/left')
2507
- }
2508
- },
2509
- async (args) => {
2510
- const { yaml, theme, format } = args;
2511
- const locale = args["locale"];
2512
- const margin = args["margin"];
2513
- suppressStdout();
2514
- try {
2515
- const resume = await processResumeData([yaml]);
2516
- const themeModule = loadTheme(theme, { autoInstall: false });
2517
- const renderOptions = {};
2518
- if (locale) renderOptions["locale"] = locale;
2519
- const html = await themeModule.render(resume, renderOptions);
2520
- let chromium;
2521
- try {
2522
- const pw = await import("playwright");
2523
- chromium = pw.chromium;
2524
- } catch {
2525
- restoreStdout();
2526
- return {
2527
- content: [{ type: "text", text: JSON.stringify({ error: "Playwright is not installed. Run: npm install playwright" }) }],
2528
- isError: true
2529
- };
2530
- }
2531
- const parsedMargin = parseMargin(margin);
2532
- const browser = await chromium.launch({ headless: true });
2533
- const page = await browser.newPage();
2534
- await page.setContent(html, { waitUntil: "networkidle" });
2535
- const pdfBuffer = await page.pdf({
2536
- format,
2537
- printBackground: true,
2538
- margin: parsedMargin
2539
- });
2540
- await browser.close();
2541
- restoreStdout();
2542
- return {
2543
- content: [{
2544
- type: "text",
2545
- text: JSON.stringify({
2546
- pdf: Buffer.from(pdfBuffer).toString("base64"),
2547
- encoding: "base64",
2548
- format
2549
- })
2550
- }]
2551
- };
2552
- } catch (e) {
2553
- restoreStdout();
2554
- return {
2555
- content: [{ type: "text", text: JSON.stringify({ error: e instanceof Error ? e.message : String(e) }) }],
2556
- isError: true
2557
- };
2558
- }
2559
- }
2560
- );
2561
- return server;
2562
- }
2563
- function parseMargin(margin) {
2564
- const defaultMargin = { top: "10mm", right: "10mm", bottom: "10mm", left: "10mm" };
2565
- if (!margin) return defaultMargin;
2566
- const parts = margin.split(",").map((s) => s.trim());
2567
- if (parts.length === 1 && parts[0]) {
2568
- return { top: parts[0], right: parts[0], bottom: parts[0], left: parts[0] };
2569
- }
2570
- if (parts.length === 2 && parts[0] && parts[1]) {
2571
- return { top: parts[0], right: parts[1], bottom: parts[0], left: parts[1] };
2572
- }
2573
- if (parts.length === 4 && parts[0] && parts[1] && parts[2] && parts[3]) {
2574
- return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[3] };
2575
- }
2576
- return defaultMargin;
2577
- }
2578
- async function startMcpServer() {
2579
- const server = createServer();
2580
- const transport = new import_stdio.StdioServerTransport();
2581
- await server.connect(transport);
2582
- }
2583
- // Annotate the CommonJS export names for ESM import in node:
2584
- 0 && (module.exports = {
2585
- startMcpServer
2586
- });
2587
- //# sourceMappingURL=server.cjs.map
1835
+ export {
1836
+ analyzeAts,
1837
+ generateResumeYaml,
1838
+ KNOWN_THEMES,
1839
+ isThemeInstalled,
1840
+ getInstalledVersion
1841
+ };
1842
+ //# sourceMappingURL=chunk-SZAL46QV.js.map