resuml 1.3.0 → 1.4.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.
@@ -39,7 +39,7 @@ var getImportMetaUrl, importMetaUrl;
39
39
  var init_cjs_shims = __esm({
40
40
  "node_modules/tsup/assets/cjs_shims.js"() {
41
41
  "use strict";
42
- getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
42
+ getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
43
43
  importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
44
44
  }
45
45
  });
@@ -181,7 +181,7 @@ var require_brace_expansion = __commonJS({
181
181
  var isSequence = isNumericSequence || isAlphaSequence;
182
182
  var isOptions = m.body.indexOf(",") >= 0;
183
183
  if (!isSequence && !isOptions) {
184
- if (m.post.match(/,(?!,).*\}/)) {
184
+ if (m.post.match(/,.*\}/)) {
185
185
  str = m.pre + "{" + m.body + escClose + m.post;
186
186
  return expand2(str);
187
187
  }
@@ -258,6 +258,7 @@ var require_brace_expansion = __commonJS({
258
258
  // src/index.ts
259
259
  var index_exports = {};
260
260
  __export(index_exports, {
261
+ analyzeAts: () => analyzeAts,
261
262
  loadResumeFiles: () => loadResumeFiles,
262
263
  loadTheme: () => loadTheme,
263
264
  processResumeData: () => processResumeData,
@@ -267,17 +268,18 @@ __export(index_exports, {
267
268
  module.exports = __toCommonJS(index_exports);
268
269
  init_cjs_shims();
269
270
  var import_commander = require("commander");
270
- var import_path3 = __toESM(require("path"));
271
- var import_fs7 = __toESM(require("fs"));
271
+ var import_path3 = __toESM(require("path"), 1);
272
+ var import_fs8 = __toESM(require("fs"), 1);
272
273
  var import_url = require("url");
273
274
 
274
275
  // src/commands/validate.ts
275
276
  init_cjs_shims();
277
+ var import_fs2 = __toESM(require("fs"), 1);
276
278
 
277
279
  // src/core.ts
278
280
  init_cjs_shims();
279
281
  var import_yaml = require("yaml");
280
- var import_lodash = __toESM(require("lodash.merge"));
282
+ var import_lodash = __toESM(require("lodash.merge"), 1);
281
283
  var import_schema = require("@jsonresume/schema");
282
284
  async function processResumeData(yamlContents) {
283
285
  if (yamlContents.length === 0) {
@@ -316,13 +318,13 @@ async function processResumeData(yamlContents) {
316
318
 
317
319
  // src/utils/loadResume.ts
318
320
  init_cjs_shims();
319
- var import_promises3 = __toESM(require("fs/promises"));
320
- var import_yaml2 = __toESM(require("yaml"));
321
+ var import_promises3 = __toESM(require("fs/promises"), 1);
322
+ var import_yaml2 = __toESM(require("yaml"), 1);
321
323
 
322
324
  // src/utils/fileUtils.ts
323
325
  init_cjs_shims();
324
- var import_promises2 = __toESM(require("fs/promises"));
325
- var import_path = __toESM(require("path"));
326
+ var import_promises2 = __toESM(require("fs/promises"), 1);
327
+ var import_path = __toESM(require("path"), 1);
326
328
 
327
329
  // node_modules/glob/dist/esm/index.js
328
330
  init_cjs_shims();
@@ -1012,7 +1014,7 @@ var path = {
1012
1014
  };
1013
1015
  var sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
1014
1016
  minimatch.sep = sep;
1015
- var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
1017
+ var GLOBSTAR = Symbol("globstar **");
1016
1018
  minimatch.GLOBSTAR = GLOBSTAR;
1017
1019
  var qmark2 = "[^/]";
1018
1020
  var star2 = qmark2 + "*?";
@@ -1717,6 +1719,7 @@ if (typeof AC === "undefined") {
1717
1719
  };
1718
1720
  }
1719
1721
  var shouldWarn = (code) => !warned.has(code);
1722
+ var TYPE = Symbol("type");
1720
1723
  var isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
1721
1724
  var getUintArray = (max) => !isPosInt(max) ? null : max <= Math.pow(2, 8) ? Uint8Array : max <= Math.pow(2, 16) ? Uint16Array : max <= Math.pow(2, 32) ? Uint32Array : max <= Number.MAX_SAFE_INTEGER ? ZeroArray : null;
1722
1725
  var ZeroArray = class extends Array {
@@ -3061,37 +3064,37 @@ var isStream = (s) => !!s && typeof s === "object" && (s instanceof Minipass ||
3061
3064
  var isReadable = (s) => !!s && typeof s === "object" && s instanceof import_node_events.EventEmitter && typeof s.pipe === "function" && // node core Writable streams have a pipe() method, but it throws
3062
3065
  s.pipe !== import_node_stream.default.Writable.prototype.pipe;
3063
3066
  var isWritable = (s) => !!s && typeof s === "object" && s instanceof import_node_events.EventEmitter && typeof s.write === "function" && typeof s.end === "function";
3064
- var EOF = /* @__PURE__ */ Symbol("EOF");
3065
- var MAYBE_EMIT_END = /* @__PURE__ */ Symbol("maybeEmitEnd");
3066
- var EMITTED_END = /* @__PURE__ */ Symbol("emittedEnd");
3067
- var EMITTING_END = /* @__PURE__ */ Symbol("emittingEnd");
3068
- var EMITTED_ERROR = /* @__PURE__ */ Symbol("emittedError");
3069
- var CLOSED = /* @__PURE__ */ Symbol("closed");
3070
- var READ = /* @__PURE__ */ Symbol("read");
3071
- var FLUSH = /* @__PURE__ */ Symbol("flush");
3072
- var FLUSHCHUNK = /* @__PURE__ */ Symbol("flushChunk");
3073
- var ENCODING = /* @__PURE__ */ Symbol("encoding");
3074
- var DECODER = /* @__PURE__ */ Symbol("decoder");
3075
- var FLOWING = /* @__PURE__ */ Symbol("flowing");
3076
- var PAUSED = /* @__PURE__ */ Symbol("paused");
3077
- var RESUME = /* @__PURE__ */ Symbol("resume");
3078
- var BUFFER = /* @__PURE__ */ Symbol("buffer");
3079
- var PIPES = /* @__PURE__ */ Symbol("pipes");
3080
- var BUFFERLENGTH = /* @__PURE__ */ Symbol("bufferLength");
3081
- var BUFFERPUSH = /* @__PURE__ */ Symbol("bufferPush");
3082
- var BUFFERSHIFT = /* @__PURE__ */ Symbol("bufferShift");
3083
- var OBJECTMODE = /* @__PURE__ */ Symbol("objectMode");
3084
- var DESTROYED = /* @__PURE__ */ Symbol("destroyed");
3085
- var ERROR = /* @__PURE__ */ Symbol("error");
3086
- var EMITDATA = /* @__PURE__ */ Symbol("emitData");
3087
- var EMITEND = /* @__PURE__ */ Symbol("emitEnd");
3088
- var EMITEND2 = /* @__PURE__ */ Symbol("emitEnd2");
3089
- var ASYNC = /* @__PURE__ */ Symbol("async");
3090
- var ABORT = /* @__PURE__ */ Symbol("abort");
3091
- var ABORTED = /* @__PURE__ */ Symbol("aborted");
3092
- var SIGNAL = /* @__PURE__ */ Symbol("signal");
3093
- var DATALISTENERS = /* @__PURE__ */ Symbol("dataListeners");
3094
- var DISCARDED = /* @__PURE__ */ Symbol("discarded");
3067
+ var EOF = Symbol("EOF");
3068
+ var MAYBE_EMIT_END = Symbol("maybeEmitEnd");
3069
+ var EMITTED_END = Symbol("emittedEnd");
3070
+ var EMITTING_END = Symbol("emittingEnd");
3071
+ var EMITTED_ERROR = Symbol("emittedError");
3072
+ var CLOSED = Symbol("closed");
3073
+ var READ = Symbol("read");
3074
+ var FLUSH = Symbol("flush");
3075
+ var FLUSHCHUNK = Symbol("flushChunk");
3076
+ var ENCODING = Symbol("encoding");
3077
+ var DECODER = Symbol("decoder");
3078
+ var FLOWING = Symbol("flowing");
3079
+ var PAUSED = Symbol("paused");
3080
+ var RESUME = Symbol("resume");
3081
+ var BUFFER = Symbol("buffer");
3082
+ var PIPES = Symbol("pipes");
3083
+ var BUFFERLENGTH = Symbol("bufferLength");
3084
+ var BUFFERPUSH = Symbol("bufferPush");
3085
+ var BUFFERSHIFT = Symbol("bufferShift");
3086
+ var OBJECTMODE = Symbol("objectMode");
3087
+ var DESTROYED = Symbol("destroyed");
3088
+ var ERROR = Symbol("error");
3089
+ var EMITDATA = Symbol("emitData");
3090
+ var EMITEND = Symbol("emitEnd");
3091
+ var EMITEND2 = Symbol("emitEnd2");
3092
+ var ASYNC = Symbol("async");
3093
+ var ABORT = Symbol("abort");
3094
+ var ABORTED = Symbol("aborted");
3095
+ var SIGNAL = Symbol("signal");
3096
+ var DATALISTENERS = Symbol("dataListeners");
3097
+ var DISCARDED = Symbol("discarded");
3095
3098
  var defer = (fn) => Promise.resolve().then(fn);
3096
3099
  var nodefer = (fn) => fn();
3097
3100
  var isEndish = (ev) => ev === "end" || ev === "finish" || ev === "prefinish";
@@ -3130,7 +3133,7 @@ var PipeProxyErrors = class extends Pipe {
3130
3133
  }
3131
3134
  constructor(src, dest, opts) {
3132
3135
  super(src, dest, opts);
3133
- this.proxyErrors = (er) => dest.emit("error", er);
3136
+ this.proxyErrors = (er) => this.dest.emit("error", er);
3134
3137
  src.on("error", this.proxyErrors);
3135
3138
  }
3136
3139
  };
@@ -3844,6 +3847,8 @@ var Minipass = class extends import_node_events.EventEmitter {
3844
3847
  return: stop,
3845
3848
  [Symbol.asyncIterator]() {
3846
3849
  return this;
3850
+ },
3851
+ [Symbol.asyncDispose]: async () => {
3847
3852
  }
3848
3853
  };
3849
3854
  }
@@ -3879,6 +3884,8 @@ var Minipass = class extends import_node_events.EventEmitter {
3879
3884
  return: stop,
3880
3885
  [Symbol.iterator]() {
3881
3886
  return this;
3887
+ },
3888
+ [Symbol.dispose]: () => {
3882
3889
  }
3883
3890
  };
3884
3891
  }
@@ -4004,7 +4011,7 @@ var ChildrenCache = class extends LRUCache {
4004
4011
  });
4005
4012
  }
4006
4013
  };
4007
- var setAsCwd = /* @__PURE__ */ Symbol("PathScurry setAsCwd");
4014
+ var setAsCwd = Symbol("PathScurry setAsCwd");
4008
4015
  var PathBase = class {
4009
4016
  /**
4010
4017
  * the basename of this path
@@ -5044,8 +5051,8 @@ var PathScurryBase = class {
5044
5051
  *
5045
5052
  * @internal
5046
5053
  */
5047
- constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs9 = defaultFS } = {}) {
5048
- this.#fs = fsFromOption(fs9);
5054
+ constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs10 = defaultFS } = {}) {
5055
+ this.#fs = fsFromOption(fs10);
5049
5056
  if (cwd instanceof URL || cwd.startsWith("file://")) {
5050
5057
  cwd = (0, import_node_url.fileURLToPath)(cwd);
5051
5058
  }
@@ -5603,8 +5610,8 @@ var PathScurryWin32 = class extends PathScurryBase {
5603
5610
  /**
5604
5611
  * @internal
5605
5612
  */
5606
- newRoot(fs9) {
5607
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
5613
+ newRoot(fs10) {
5614
+ return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs10 });
5608
5615
  }
5609
5616
  /**
5610
5617
  * Return true if the provided path string is an absolute path
@@ -5632,8 +5639,8 @@ var PathScurryPosix = class extends PathScurryBase {
5632
5639
  /**
5633
5640
  * @internal
5634
5641
  */
5635
- newRoot(fs9) {
5636
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
5642
+ newRoot(fs10) {
5643
+ return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs10 });
5637
5644
  }
5638
5645
  /**
5639
5646
  * Return true if the provided path string is an absolute path
@@ -6816,12 +6823,1011 @@ function handleCommandError(error, command, debug = false) {
6816
6823
  }
6817
6824
  }
6818
6825
  }
6819
- if (process.env.NODE_ENV !== "test") {
6826
+ if (process.env["NODE_ENV"] !== "test") {
6820
6827
  process.exit(1);
6821
6828
  }
6822
6829
  }
6823
6830
 
6831
+ // src/ats/index.ts
6832
+ init_cjs_shims();
6833
+
6834
+ // src/ats/genericChecks.ts
6835
+ init_cjs_shims();
6836
+
6837
+ // src/ats/i18n/index.ts
6838
+ init_cjs_shims();
6839
+
6840
+ // src/ats/i18n/en.ts
6841
+ init_cjs_shims();
6842
+ var en = {
6843
+ actionVerbs: [
6844
+ // Leadership & Management
6845
+ "achieved",
6846
+ "administered",
6847
+ "advanced",
6848
+ "allocated",
6849
+ "approved",
6850
+ "assigned",
6851
+ "authorized",
6852
+ "chaired",
6853
+ "consolidated",
6854
+ "coordinated",
6855
+ "delegated",
6856
+ "directed",
6857
+ "established",
6858
+ "executed",
6859
+ "headed",
6860
+ "hired",
6861
+ "hosted",
6862
+ "led",
6863
+ "managed",
6864
+ "mentored",
6865
+ "motivated",
6866
+ "orchestrated",
6867
+ "organized",
6868
+ "oversaw",
6869
+ "planned",
6870
+ "presided",
6871
+ "prioritized",
6872
+ "produced",
6873
+ "recruited",
6874
+ "spearheaded",
6875
+ "supervised",
6876
+ // Technical & Engineering
6877
+ "architected",
6878
+ "automated",
6879
+ "built",
6880
+ "coded",
6881
+ "configured",
6882
+ "debugged",
6883
+ "deployed",
6884
+ "designed",
6885
+ "developed",
6886
+ "devised",
6887
+ "engineered",
6888
+ "implemented",
6889
+ "installed",
6890
+ "integrated",
6891
+ "launched",
6892
+ "maintained",
6893
+ "migrated",
6894
+ "modernized",
6895
+ "optimized",
6896
+ "overhauled",
6897
+ "programmed",
6898
+ "prototyped",
6899
+ "refactored",
6900
+ "reengineered",
6901
+ "resolved",
6902
+ "restructured",
6903
+ "revamped",
6904
+ "scaled",
6905
+ "standardized",
6906
+ "streamlined",
6907
+ "tested",
6908
+ "troubleshot",
6909
+ "upgraded",
6910
+ // Achievement & Impact
6911
+ "accelerated",
6912
+ "accomplished",
6913
+ "boosted",
6914
+ "completed",
6915
+ "contributed",
6916
+ "converted",
6917
+ "decreased",
6918
+ "delivered",
6919
+ "doubled",
6920
+ "earned",
6921
+ "eliminated",
6922
+ "exceeded",
6923
+ "expanded",
6924
+ "expedited",
6925
+ "generated",
6926
+ "grew",
6927
+ "improved",
6928
+ "increased",
6929
+ "maximized",
6930
+ "minimized",
6931
+ "outperformed",
6932
+ "pioneered",
6933
+ "recovered",
6934
+ "reduced",
6935
+ "saved",
6936
+ "simplified",
6937
+ "solved",
6938
+ "surpassed",
6939
+ "transformed",
6940
+ "tripled",
6941
+ // Communication & Collaboration
6942
+ "advised",
6943
+ "advocated",
6944
+ "briefed",
6945
+ "collaborated",
6946
+ "communicated",
6947
+ "consulted",
6948
+ "convinced",
6949
+ "counseled",
6950
+ "defined",
6951
+ "demonstrated",
6952
+ "documented",
6953
+ "educated",
6954
+ "facilitated",
6955
+ "guided",
6956
+ "influenced",
6957
+ "informed",
6958
+ "instructed",
6959
+ "liaised",
6960
+ "negotiated",
6961
+ "partnered",
6962
+ "persuaded",
6963
+ "presented",
6964
+ "promoted",
6965
+ "proposed",
6966
+ "published",
6967
+ "recommended",
6968
+ "represented",
6969
+ "trained",
6970
+ // Analysis & Research
6971
+ "analyzed",
6972
+ "assessed",
6973
+ "audited",
6974
+ "benchmarked",
6975
+ "calculated",
6976
+ "compared",
6977
+ "compiled",
6978
+ "conducted",
6979
+ "discovered",
6980
+ "evaluated",
6981
+ "examined",
6982
+ "explored",
6983
+ "forecasted",
6984
+ "identified",
6985
+ "inspected",
6986
+ "interpreted",
6987
+ "investigated",
6988
+ "mapped",
6989
+ "measured",
6990
+ "modeled",
6991
+ "monitored",
6992
+ "quantified",
6993
+ "researched",
6994
+ "reviewed",
6995
+ "surveyed",
6996
+ "synthesized",
6997
+ "tracked",
6998
+ "validated",
6999
+ "verified",
7000
+ // Creation & Innovation
7001
+ "conceptualized",
7002
+ "crafted",
7003
+ "created",
7004
+ "customized",
7005
+ "formulated",
7006
+ "founded",
7007
+ "initiated",
7008
+ "innovated",
7009
+ "introduced",
7010
+ "invented",
7011
+ "originated",
7012
+ "shaped"
7013
+ ],
7014
+ pronouns: ["i", "me", "my", "mine", "myself", "we", "our", "ours"],
7015
+ stopWords: [
7016
+ "a",
7017
+ "an",
7018
+ "the",
7019
+ "and",
7020
+ "or",
7021
+ "but",
7022
+ "in",
7023
+ "on",
7024
+ "at",
7025
+ "to",
7026
+ "for",
7027
+ "of",
7028
+ "with",
7029
+ "by",
7030
+ "from",
7031
+ "is",
7032
+ "was",
7033
+ "are",
7034
+ "were",
7035
+ "be",
7036
+ "been",
7037
+ "being",
7038
+ "have",
7039
+ "has",
7040
+ "had",
7041
+ "do",
7042
+ "does",
7043
+ "did",
7044
+ "will",
7045
+ "would",
7046
+ "could",
7047
+ "should",
7048
+ "may",
7049
+ "might",
7050
+ "shall",
7051
+ "can",
7052
+ "this",
7053
+ "that",
7054
+ "these",
7055
+ "those",
7056
+ "it",
7057
+ "its",
7058
+ "as",
7059
+ "if",
7060
+ "not",
7061
+ "no",
7062
+ "so",
7063
+ "up",
7064
+ "out",
7065
+ "about",
7066
+ "into",
7067
+ "over",
7068
+ "after",
7069
+ "before",
7070
+ "between",
7071
+ "under",
7072
+ "above",
7073
+ "below",
7074
+ "all",
7075
+ "each",
7076
+ "every",
7077
+ "both",
7078
+ "few",
7079
+ "more",
7080
+ "most",
7081
+ "other",
7082
+ "some",
7083
+ "such",
7084
+ "than",
7085
+ "too",
7086
+ "very"
7087
+ ]
7088
+ };
7089
+ var en_default = en;
7090
+
7091
+ // src/ats/i18n/de.ts
7092
+ init_cjs_shims();
7093
+ var de = {
7094
+ actionVerbs: [
7095
+ // Führung & Management
7096
+ "geleitet",
7097
+ "gef\xFChrt",
7098
+ "koordiniert",
7099
+ "organisiert",
7100
+ "verwaltet",
7101
+ "delegiert",
7102
+ "beaufsichtigt",
7103
+ "betreut",
7104
+ "eingestellt",
7105
+ "motiviert",
7106
+ "verantwortet",
7107
+ "gesteuert",
7108
+ "\xFCberwacht",
7109
+ "priorisiert",
7110
+ "geplant",
7111
+ // Technik & Entwicklung
7112
+ "entwickelt",
7113
+ "implementiert",
7114
+ "programmiert",
7115
+ "konfiguriert",
7116
+ "automatisiert",
7117
+ "deployt",
7118
+ "gebaut",
7119
+ "entworfen",
7120
+ "integriert",
7121
+ "migriert",
7122
+ "modernisiert",
7123
+ "optimiert",
7124
+ "refaktoriert",
7125
+ "skaliert",
7126
+ "standardisiert",
7127
+ "getestet",
7128
+ "aufgebaut",
7129
+ "eingef\xFChrt",
7130
+ "bereitgestellt",
7131
+ "umgesetzt",
7132
+ // Leistung & Ergebnisse
7133
+ "verbessert",
7134
+ "gesteigert",
7135
+ "reduziert",
7136
+ "beschleunigt",
7137
+ "erreicht",
7138
+ "\xFCbertroffen",
7139
+ "erweitert",
7140
+ "vereinfacht",
7141
+ "gel\xF6st",
7142
+ "transformiert",
7143
+ "erh\xF6ht",
7144
+ "verdoppelt",
7145
+ "verdreifacht",
7146
+ "generiert",
7147
+ "gespart",
7148
+ "maximiert",
7149
+ "minimiert",
7150
+ "eliminiert",
7151
+ "geliefert",
7152
+ "abgeschlossen",
7153
+ // Kommunikation & Zusammenarbeit
7154
+ "beraten",
7155
+ "pr\xE4sentiert",
7156
+ "dokumentiert",
7157
+ "geschult",
7158
+ "trainiert",
7159
+ "vermittelt",
7160
+ "kommuniziert",
7161
+ "verhandelt",
7162
+ "zusammengearbeitet",
7163
+ "unterst\xFCtzt",
7164
+ "gef\xF6rdert",
7165
+ "empfohlen",
7166
+ "vorgestellt",
7167
+ "publiziert",
7168
+ // Analyse & Forschung
7169
+ "analysiert",
7170
+ "bewertet",
7171
+ "evaluiert",
7172
+ "untersucht",
7173
+ "erforscht",
7174
+ "identifiziert",
7175
+ "gemessen",
7176
+ "\xFCberwacht",
7177
+ "validiert",
7178
+ "verifiziert",
7179
+ "gepr\xFCft",
7180
+ "verglichen",
7181
+ "recherchiert",
7182
+ "quantifiziert",
7183
+ // Kreation & Innovation
7184
+ "konzipiert",
7185
+ "erstellt",
7186
+ "gestaltet",
7187
+ "initiiert",
7188
+ "innoviert",
7189
+ "eingef\xFChrt",
7190
+ "gegr\xFCndet",
7191
+ "formuliert"
7192
+ ],
7193
+ pronouns: ["ich", "mich", "mir", "mein", "meine", "meinem", "meiner", "meines", "wir", "unser", "unsere"],
7194
+ stopWords: [
7195
+ "ein",
7196
+ "eine",
7197
+ "einer",
7198
+ "eines",
7199
+ "einem",
7200
+ "der",
7201
+ "die",
7202
+ "das",
7203
+ "den",
7204
+ "dem",
7205
+ "des",
7206
+ "und",
7207
+ "oder",
7208
+ "aber",
7209
+ "in",
7210
+ "an",
7211
+ "auf",
7212
+ "zu",
7213
+ "f\xFCr",
7214
+ "von",
7215
+ "mit",
7216
+ "bei",
7217
+ "aus",
7218
+ "ist",
7219
+ "war",
7220
+ "sind",
7221
+ "waren",
7222
+ "wird",
7223
+ "wurde",
7224
+ "werden",
7225
+ "hat",
7226
+ "hatte",
7227
+ "haben",
7228
+ "hatten",
7229
+ "sein",
7230
+ "kann",
7231
+ "k\xF6nnte",
7232
+ "soll",
7233
+ "sollte",
7234
+ "muss",
7235
+ "musste",
7236
+ "darf",
7237
+ "diese",
7238
+ "dieser",
7239
+ "dieses",
7240
+ "diesem",
7241
+ "diesen",
7242
+ "als",
7243
+ "wenn",
7244
+ "nicht",
7245
+ "kein",
7246
+ "keine",
7247
+ "so",
7248
+ "auch",
7249
+ "noch",
7250
+ "schon",
7251
+ "nach",
7252
+ "vor",
7253
+ "\xFCber",
7254
+ "unter",
7255
+ "zwischen",
7256
+ "durch",
7257
+ "ohne",
7258
+ "um",
7259
+ "bis",
7260
+ "alle",
7261
+ "jede",
7262
+ "jeder",
7263
+ "jedes",
7264
+ "mehr",
7265
+ "viel",
7266
+ "sehr"
7267
+ ]
7268
+ };
7269
+ var de_default = de;
7270
+
7271
+ // src/ats/i18n/index.ts
7272
+ var languages = { en: en_default, de: de_default };
7273
+ function getLanguageData(language) {
7274
+ return languages[language] ?? languages["en"] ?? en_default;
7275
+ }
7276
+
7277
+ // src/ats/genericChecks.ts
7278
+ function wordCount(text) {
7279
+ return text.trim().split(/\s+/).filter(Boolean).length;
7280
+ }
7281
+ function getFirstWord(text) {
7282
+ return text.trim().split(/\s+/)[0]?.toLowerCase().replace(/[^a-zA-ZäöüßÄÖÜàáâãéèêëíìîïóòôõúùûüñç]/g, "") || "";
7283
+ }
7284
+ var contactComplete = (resume) => {
7285
+ const b = resume.basics;
7286
+ const hasName = !!b?.name;
7287
+ const hasEmail = !!b?.email;
7288
+ const hasPhone = !!b?.phone;
7289
+ const hasCity = !!b?.location?.city;
7290
+ const fields = [hasName, hasEmail, hasPhone, hasCity];
7291
+ const presentCount = fields.filter(Boolean).length;
7292
+ const passed = presentCount === fields.length;
7293
+ const missing = [];
7294
+ if (!hasName) missing.push("name");
7295
+ if (!hasEmail) missing.push("email");
7296
+ if (!hasPhone) missing.push("phone");
7297
+ if (!hasCity) missing.push("location.city");
7298
+ return {
7299
+ id: "contact-complete",
7300
+ category: "contact",
7301
+ weight: "high",
7302
+ passed,
7303
+ score: Math.round(presentCount / fields.length * 100),
7304
+ message: passed ? "Contact information is complete." : `Missing contact fields: ${missing.join(", ")}.`,
7305
+ suggestion: passed ? void 0 : `Add the following to your basics section: ${missing.join(", ")}.`
7306
+ };
7307
+ };
7308
+ var hasSummary = (resume) => {
7309
+ const summary = resume.basics?.summary?.trim();
7310
+ if (!summary) {
7311
+ return {
7312
+ id: "has-summary",
7313
+ category: "content",
7314
+ weight: "high",
7315
+ passed: false,
7316
+ score: 0,
7317
+ message: "No professional summary found.",
7318
+ suggestion: "Add a 2-4 sentence professional summary to your basics section highlighting your experience and key skills."
7319
+ };
7320
+ }
7321
+ const words = wordCount(summary);
7322
+ const tooShort = words < 15;
7323
+ const tooLong = words > 100;
7324
+ const passed = !tooShort && !tooLong;
7325
+ let score = 100;
7326
+ if (tooShort) score = Math.round(words / 15 * 60);
7327
+ if (tooLong) score = Math.max(60, 100 - (words - 100));
7328
+ return {
7329
+ id: "has-summary",
7330
+ category: "content",
7331
+ weight: "high",
7332
+ passed,
7333
+ score,
7334
+ message: tooShort ? `Summary is too short (${words} words). Aim for 15-100 words.` : tooLong ? `Summary is too long (${words} words). Aim for 15-100 words.` : `Summary length is good (${words} words).`,
7335
+ suggestion: tooShort ? "Expand your summary to describe your experience, expertise, and career goals in 15-100 words." : tooLong ? "Shorten your summary to the most impactful 15-100 words. Focus on key achievements and expertise." : void 0
7336
+ };
7337
+ };
7338
+ var hasLinkedin = (resume) => {
7339
+ const profiles = resume.basics?.profiles || [];
7340
+ const linkedin = profiles.find(
7341
+ (p) => p.network?.toLowerCase() === "linkedin" || p.url?.toLowerCase().includes("linkedin.com")
7342
+ );
7343
+ return {
7344
+ id: "has-linkedin",
7345
+ category: "contact",
7346
+ weight: "medium",
7347
+ passed: !!linkedin,
7348
+ score: linkedin ? 100 : 0,
7349
+ message: linkedin ? "LinkedIn profile found." : "No LinkedIn profile found.",
7350
+ suggestion: linkedin ? void 0 : "Add a LinkedIn profile to your basics.profiles section."
7351
+ };
7352
+ };
7353
+ var workHighlights = (resume) => {
7354
+ const work = resume.work || [];
7355
+ if (work.length === 0) {
7356
+ return {
7357
+ id: "work-highlights",
7358
+ category: "content",
7359
+ weight: "high",
7360
+ passed: false,
7361
+ score: 0,
7362
+ message: "No work experience entries found.",
7363
+ suggestion: "Add work experience entries with highlights describing your accomplishments."
7364
+ };
7365
+ }
7366
+ const minHighlights = 2;
7367
+ const entriesWithEnough = work.filter((w) => (w.highlights?.length || 0) >= minHighlights);
7368
+ const passed = entriesWithEnough.length === work.length;
7369
+ const score = Math.round(entriesWithEnough.length / work.length * 100);
7370
+ const lacking = work.filter((w) => (w.highlights?.length || 0) < minHighlights).map((w) => `"${w.position || "Unknown"} at ${w.name || "Unknown"}" (${w.highlights?.length || 0} highlights)`);
7371
+ return {
7372
+ id: "work-highlights",
7373
+ category: "content",
7374
+ weight: "high",
7375
+ passed,
7376
+ score,
7377
+ message: passed ? "All work entries have sufficient highlights." : `Some work entries need more highlights: ${lacking.join("; ")}.`,
7378
+ suggestion: passed ? void 0 : `Add at least ${minHighlights} bullet-point highlights to each work entry describing specific accomplishments.`
7379
+ };
7380
+ };
7381
+ var actionVerbs = (resume, language) => {
7382
+ const langData = getLanguageData(language);
7383
+ const verbs = new Set(langData.actionVerbs);
7384
+ const allHighlights = [];
7385
+ for (const w of resume.work || []) {
7386
+ for (const h of w.highlights || []) {
7387
+ allHighlights.push({ text: h, source: `${w.position || ""} at ${w.name || ""}` });
7388
+ }
7389
+ }
7390
+ for (const p of resume.projects || []) {
7391
+ for (const h of p.highlights || []) {
7392
+ allHighlights.push({ text: h, source: `project "${p.name || ""}"` });
7393
+ }
7394
+ }
7395
+ for (const v of resume.volunteer || []) {
7396
+ for (const h of v.highlights || []) {
7397
+ allHighlights.push({ text: h, source: `volunteer at ${v.organization || ""}` });
7398
+ }
7399
+ }
7400
+ if (allHighlights.length === 0) {
7401
+ return {
7402
+ id: "action-verbs",
7403
+ category: "content",
7404
+ weight: "high",
7405
+ passed: false,
7406
+ score: 0,
7407
+ message: "No highlights found to check for action verbs.",
7408
+ suggestion: "Add highlights to your work, project, or volunteer entries starting with strong action verbs."
7409
+ };
7410
+ }
7411
+ const withActionVerb = allHighlights.filter((h) => verbs.has(getFirstWord(h.text)));
7412
+ const withoutActionVerb = allHighlights.filter((h) => !verbs.has(getFirstWord(h.text)));
7413
+ const passed = withoutActionVerb.length === 0;
7414
+ const score = Math.round(withActionVerb.length / allHighlights.length * 100);
7415
+ const examples = withoutActionVerb.slice(0, 3).map(
7416
+ (h) => `"${h.text.substring(0, 60)}${h.text.length > 60 ? "..." : ""}" (${h.source})`
7417
+ );
7418
+ return {
7419
+ id: "action-verbs",
7420
+ category: "content",
7421
+ weight: "high",
7422
+ passed,
7423
+ score,
7424
+ message: passed ? "All highlights start with action verbs." : `${withoutActionVerb.length} of ${allHighlights.length} highlights don't start with an action verb.`,
7425
+ suggestion: passed ? void 0 : `Start each highlight with a strong action verb (e.g., "Developed", "Implemented", "Led"). Fix: ${examples.join("; ")}.`
7426
+ };
7427
+ };
7428
+ var quantifiedImpact = (resume) => {
7429
+ const quantPattern = /\d+%?|\$[\d,]+|[\d,]+\+?\s*(users|clients|customers|people|team|members|projects|applications|servers|services|endpoints|requests|transactions)/i;
7430
+ const allHighlights = [];
7431
+ for (const w of resume.work || []) {
7432
+ allHighlights.push(...w.highlights || []);
7433
+ }
7434
+ for (const p of resume.projects || []) {
7435
+ allHighlights.push(...p.highlights || []);
7436
+ }
7437
+ if (allHighlights.length === 0) {
7438
+ return {
7439
+ id: "quantified-impact",
7440
+ category: "content",
7441
+ weight: "medium",
7442
+ passed: false,
7443
+ score: 0,
7444
+ message: "No highlights found to check for quantified impact.",
7445
+ suggestion: 'Add measurable results to your highlights (e.g., "Reduced load time by 40%", "Managed team of 8").'
7446
+ };
7447
+ }
7448
+ const quantified = allHighlights.filter((h) => quantPattern.test(h));
7449
+ const ratio = quantified.length / allHighlights.length;
7450
+ const passed = ratio >= 0.5;
7451
+ const score = Math.min(100, Math.round(ratio * 200));
7452
+ return {
7453
+ id: "quantified-impact",
7454
+ category: "content",
7455
+ weight: "medium",
7456
+ passed,
7457
+ score,
7458
+ message: passed ? `${quantified.length} of ${allHighlights.length} highlights include quantified results.` : `Only ${quantified.length} of ${allHighlights.length} highlights include numbers or metrics.`,
7459
+ suggestion: passed ? void 0 : 'Add specific numbers, percentages, or metrics to your highlights (e.g., "Improved performance by 30%", "Managed $2M budget").'
7460
+ };
7461
+ };
7462
+ var dateConsistency = (resume) => {
7463
+ const work = resume.work || [];
7464
+ if (work.length < 2) {
7465
+ return {
7466
+ id: "date-consistency",
7467
+ category: "structure",
7468
+ weight: "medium",
7469
+ passed: true,
7470
+ score: 100,
7471
+ message: "Date consistency check passed (fewer than 2 work entries)."
7472
+ };
7473
+ }
7474
+ const issues = [];
7475
+ for (const w of work) {
7476
+ if (!w.startDate) {
7477
+ issues.push(`"${w.position || "Unknown"} at ${w.name || "Unknown"}" is missing a start date.`);
7478
+ }
7479
+ }
7480
+ const sorted = [...work].filter((w) => Boolean(w.startDate)).sort((a, b) => a.startDate > b.startDate ? 1 : -1);
7481
+ for (let i = 0; i < sorted.length - 1; i++) {
7482
+ const current = sorted[i];
7483
+ const next = sorted[i + 1];
7484
+ const endDate = current.endDate ?? current.startDate;
7485
+ const gapMs = new Date(next.startDate).getTime() - new Date(endDate).getTime();
7486
+ const gapMonths = gapMs / (1e3 * 60 * 60 * 24 * 30);
7487
+ if (gapMonths > 6) {
7488
+ issues.push(
7489
+ `Gap of ~${Math.round(gapMonths)} months between "${current.name ?? "Unknown"}" (ended ${endDate}) and "${next.name ?? "Unknown"}" (started ${next.startDate}).`
7490
+ );
7491
+ }
7492
+ }
7493
+ const passed = issues.length === 0;
7494
+ return {
7495
+ id: "date-consistency",
7496
+ category: "structure",
7497
+ weight: "medium",
7498
+ passed,
7499
+ score: passed ? 100 : Math.max(0, 100 - issues.length * 25),
7500
+ message: passed ? "Work experience dates are consistent with no major gaps." : `Date issues found: ${issues.join(" ")}`,
7501
+ suggestion: passed ? void 0 : "Ensure all work entries have start dates. If there are employment gaps, consider adding freelance, volunteer, or education entries to fill them."
7502
+ };
7503
+ };
7504
+ var skillsPopulated = (resume) => {
7505
+ const skills = resume.skills || [];
7506
+ const minCategories = 3;
7507
+ const categoriesWithKeywords = skills.filter((s) => s.keywords && s.keywords.length > 0);
7508
+ const passed = categoriesWithKeywords.length >= minCategories;
7509
+ const score = Math.min(100, Math.round(categoriesWithKeywords.length / minCategories * 100));
7510
+ return {
7511
+ id: "skills-populated",
7512
+ category: "structure",
7513
+ weight: "medium",
7514
+ passed,
7515
+ score,
7516
+ message: passed ? `${categoriesWithKeywords.length} skill categories with keywords found.` : `Only ${categoriesWithKeywords.length} skill categories with keywords found (need at least ${minCategories}).`,
7517
+ suggestion: passed ? void 0 : `Add at least ${minCategories} skill categories with specific keywords (e.g., "Languages: TypeScript, Python", "Frameworks: React, Node.js").`
7518
+ };
7519
+ };
7520
+ var educationComplete = (resume) => {
7521
+ const education = resume.education || [];
7522
+ if (education.length === 0) {
7523
+ return {
7524
+ id: "education-complete",
7525
+ category: "structure",
7526
+ weight: "low",
7527
+ passed: false,
7528
+ score: 0,
7529
+ message: "No education entries found.",
7530
+ suggestion: "Add at least one education entry with institution, area, and studyType."
7531
+ };
7532
+ }
7533
+ const complete = education.filter((e) => e.institution && e.area && e.studyType);
7534
+ const passed = complete.length === education.length;
7535
+ const score = Math.round(complete.length / education.length * 100);
7536
+ const incomplete = education.filter((e) => !e.institution || !e.area || !e.studyType).map((e) => {
7537
+ const missing = [];
7538
+ if (!e.institution) missing.push("institution");
7539
+ if (!e.area) missing.push("area");
7540
+ if (!e.studyType) missing.push("studyType");
7541
+ return `"${e.institution || "Unknown"}" missing: ${missing.join(", ")}`;
7542
+ });
7543
+ return {
7544
+ id: "education-complete",
7545
+ category: "structure",
7546
+ weight: "low",
7547
+ passed,
7548
+ score,
7549
+ message: passed ? "All education entries are complete." : `Incomplete education entries: ${incomplete.join("; ")}.`,
7550
+ suggestion: passed ? void 0 : "Ensure each education entry has institution, area (field of study), and studyType (degree type)."
7551
+ };
7552
+ };
7553
+ var noPronouns = (resume, language) => {
7554
+ const langData = getLanguageData(language);
7555
+ const pronounSet = new Set(langData.pronouns);
7556
+ const textBlocks = [];
7557
+ if (resume.basics?.summary) {
7558
+ textBlocks.push({ text: resume.basics.summary, source: "summary" });
7559
+ }
7560
+ for (const w of resume.work || []) {
7561
+ if (w.summary) textBlocks.push({ text: w.summary, source: `work summary (${w.name || "Unknown"})` });
7562
+ for (const h of w.highlights || []) {
7563
+ textBlocks.push({ text: h, source: `work highlight (${w.name || "Unknown"})` });
7564
+ }
7565
+ }
7566
+ for (const p of resume.projects || []) {
7567
+ if (p.description) textBlocks.push({ text: p.description, source: `project (${p.name || "Unknown"})` });
7568
+ for (const h of p.highlights || []) {
7569
+ textBlocks.push({ text: h, source: `project highlight (${p.name || "Unknown"})` });
7570
+ }
7571
+ }
7572
+ const found = [];
7573
+ for (const block of textBlocks) {
7574
+ const words = block.text.toLowerCase().split(/\s+/);
7575
+ for (const word of words) {
7576
+ const clean = word.replace(/[^a-zA-ZäöüßÄÖÜ]/g, "");
7577
+ if (pronounSet.has(clean)) {
7578
+ found.push({ pronoun: clean, source: block.source });
7579
+ }
7580
+ }
7581
+ }
7582
+ const passed = found.length === 0;
7583
+ const uniquePronouns = [...new Set(found.map((f) => f.pronoun))];
7584
+ const examples = found.slice(0, 3).map((f) => `"${f.pronoun}" in ${f.source}`);
7585
+ return {
7586
+ id: "no-pronouns",
7587
+ category: "content",
7588
+ weight: "medium",
7589
+ passed,
7590
+ score: passed ? 100 : Math.max(0, 100 - found.length * 15),
7591
+ message: passed ? "No first-person pronouns found in resume content." : `Found ${found.length} first-person pronoun(s): ${uniquePronouns.join(", ")}.`,
7592
+ suggestion: passed ? void 0 : `Remove first-person pronouns from your resume. Instead of "I managed a team", write "Managed a team". Found: ${examples.join("; ")}.`
7593
+ };
7594
+ };
7595
+ var sectionCompleteness = (resume) => {
7596
+ const required = ["basics", "work", "education", "skills"];
7597
+ const present = required.filter((section) => {
7598
+ const value = resume[section];
7599
+ if (Array.isArray(value)) return value.length > 0;
7600
+ return value !== void 0;
7601
+ });
7602
+ const missing = required.filter((s) => !present.includes(s));
7603
+ const passed = missing.length === 0;
7604
+ return {
7605
+ id: "section-completeness",
7606
+ category: "structure",
7607
+ weight: "low",
7608
+ passed,
7609
+ score: Math.round(present.length / required.length * 100),
7610
+ message: passed ? "All essential resume sections are present." : `Missing essential sections: ${missing.join(", ")}.`,
7611
+ suggestion: passed ? void 0 : `Add the following sections to your resume: ${missing.join(", ")}.`
7612
+ };
7613
+ };
7614
+ var allChecks = [
7615
+ contactComplete,
7616
+ hasSummary,
7617
+ hasLinkedin,
7618
+ workHighlights,
7619
+ actionVerbs,
7620
+ quantifiedImpact,
7621
+ dateConsistency,
7622
+ skillsPopulated,
7623
+ educationComplete,
7624
+ noPronouns,
7625
+ sectionCompleteness
7626
+ ];
7627
+ function runGenericChecks(resume, language) {
7628
+ return allChecks.map((check) => check(resume, language));
7629
+ }
7630
+
7631
+ // src/ats/jdMatcher.ts
7632
+ init_cjs_shims();
7633
+ function tokenize(text, stopWords) {
7634
+ return text.toLowerCase().replace(/[^a-zA-Z0-9äöüßÄÖÜàáâãéèêëíìîïóòôõúùûüñç\s-]/g, " ").split(/\s+/).filter((word) => word.length > 2 && !stopWords.has(word));
7635
+ }
7636
+ function simpleStem(word, language) {
7637
+ if (language === "de") {
7638
+ return word.replace(/(ung|heit|keit|schaft|lich|isch|iert|ieren|tion|ment)$/, "").replace(/(en|er|es|em|te|st)$/, "");
7639
+ }
7640
+ return word.replace(/(ment|ness|tion|sion|ance|ence|ity|ing|ous|ive|able|ible|ful|less)$/, "").replace(/(ies)$/, "y").replace(/(es|ed|s)$/, "");
7641
+ }
7642
+ function extractResumeText(resume) {
7643
+ const parts = [];
7644
+ if (resume.basics?.summary) parts.push(resume.basics.summary);
7645
+ if (resume.basics?.label) parts.push(resume.basics.label);
7646
+ for (const w of resume.work || []) {
7647
+ if (w.position) parts.push(w.position);
7648
+ if (w.summary) parts.push(w.summary);
7649
+ parts.push(...w.highlights || []);
7650
+ }
7651
+ for (const s of resume.skills || []) {
7652
+ if (s.name) parts.push(s.name);
7653
+ parts.push(...s.keywords || []);
7654
+ }
7655
+ for (const p of resume.projects || []) {
7656
+ if (p.name) parts.push(p.name);
7657
+ if (p.description) parts.push(p.description);
7658
+ parts.push(...p.highlights || []);
7659
+ }
7660
+ for (const e of resume.education || []) {
7661
+ if (e.area) parts.push(e.area);
7662
+ if (e.studyType) parts.push(e.studyType);
7663
+ parts.push(...e.courses || []);
7664
+ }
7665
+ for (const c of resume.certificates || []) {
7666
+ if (c.name) parts.push(c.name);
7667
+ }
7668
+ return parts.join(" ");
7669
+ }
7670
+ function buildTfMap(tokens) {
7671
+ const tf = /* @__PURE__ */ new Map();
7672
+ for (const token of tokens) {
7673
+ tf.set(token, (tf.get(token) || 0) + 1);
7674
+ }
7675
+ return tf;
7676
+ }
7677
+ function extractKeywords(text, language, maxKeywords = 30) {
7678
+ const langData = getLanguageData(language);
7679
+ const stopWords = new Set(langData.stopWords);
7680
+ const tokens = tokenize(text, stopWords);
7681
+ const stemmed = tokens.map((t) => simpleStem(t, language));
7682
+ const tf = buildTfMap(stemmed);
7683
+ const stemToOriginal = /* @__PURE__ */ new Map();
7684
+ for (let i = 0; i < tokens.length; i++) {
7685
+ const stem = stemmed[i] ?? "";
7686
+ if (!stemToOriginal.has(stem)) {
7687
+ stemToOriginal.set(stem, tokens[i] ?? "");
7688
+ }
7689
+ }
7690
+ return [...tf.entries()].filter(([stem]) => stem.length > 2).sort((a, b) => b[1] - a[1]).slice(0, maxKeywords).map(([stem]) => stemToOriginal.get(stem) || stem);
7691
+ }
7692
+ function matchJobDescription(resume, jobDescription, language = "en") {
7693
+ const langData = getLanguageData(language);
7694
+ const stopWords = new Set(langData.stopWords);
7695
+ const jdKeywords = extractKeywords(jobDescription, language);
7696
+ const resumeText = extractResumeText(resume);
7697
+ const resumeTokens = tokenize(resumeText, stopWords);
7698
+ const resumeStems = new Set(resumeTokens.map((t) => simpleStem(t, language)));
7699
+ const resumeTokenSet = new Set(resumeTokens);
7700
+ const matched = [];
7701
+ const missing = [];
7702
+ for (const keyword of jdKeywords) {
7703
+ const stem = simpleStem(keyword, language);
7704
+ if (resumeStems.has(stem) || resumeTokenSet.has(keyword.toLowerCase())) {
7705
+ matched.push(keyword);
7706
+ } else {
7707
+ missing.push(keyword);
7708
+ }
7709
+ }
7710
+ const matchPercentage = jdKeywords.length > 0 ? Math.round(matched.length / jdKeywords.length * 100) : 0;
7711
+ return { matched, missing, matchPercentage };
7712
+ }
7713
+
7714
+ // src/ats/scoring.ts
7715
+ init_cjs_shims();
7716
+ var weightMultiplier = {
7717
+ high: 3,
7718
+ medium: 2,
7719
+ low: 1
7720
+ };
7721
+ function calculateScore(checks) {
7722
+ if (checks.length === 0) return 0;
7723
+ let weightedSum = 0;
7724
+ let totalWeight = 0;
7725
+ for (const check of checks) {
7726
+ const mult = weightMultiplier[check.weight] || 1;
7727
+ weightedSum += check.score * mult;
7728
+ totalWeight += 100 * mult;
7729
+ }
7730
+ return totalWeight > 0 ? Math.round(weightedSum / totalWeight * 100) : 0;
7731
+ }
7732
+ function calculateCombinedScore(genericScore, jdMatchPercentage) {
7733
+ if (jdMatchPercentage === void 0) return genericScore;
7734
+ return Math.round(genericScore * 0.6 + jdMatchPercentage * 0.4);
7735
+ }
7736
+ function scoreToRating(score) {
7737
+ if (score >= 90) return "excellent";
7738
+ if (score >= 75) return "good";
7739
+ if (score >= 60) return "needs-work";
7740
+ return "poor";
7741
+ }
7742
+ function generateSummary(score, rating, hasJd) {
7743
+ const ratingLabel = {
7744
+ excellent: "Excellent",
7745
+ good: "Good",
7746
+ "needs-work": "Needs Work",
7747
+ poor: "Poor"
7748
+ }[rating];
7749
+ const base = `ATS Score: ${score}/100 (${ratingLabel})`;
7750
+ if (hasJd) {
7751
+ return `${base} \u2014 includes job description keyword matching.`;
7752
+ }
7753
+ return `${base} \u2014 based on resume structure and content best practices.`;
7754
+ }
7755
+
7756
+ // src/ats/index.ts
7757
+ function analyzeAts(resume, options = {}) {
7758
+ const language = options.language || "en";
7759
+ const checks = runGenericChecks(resume, language);
7760
+ const genericScore = calculateScore(checks);
7761
+ let keywords;
7762
+ if (options.jobDescription) {
7763
+ keywords = matchJobDescription(resume, options.jobDescription, language);
7764
+ }
7765
+ const finalScore = calculateCombinedScore(genericScore, keywords?.matchPercentage);
7766
+ const rating = scoreToRating(finalScore);
7767
+ const summary = generateSummary(finalScore, rating, !!keywords);
7768
+ return {
7769
+ score: finalScore,
7770
+ rating,
7771
+ checks,
7772
+ keywords,
7773
+ summary
7774
+ };
7775
+ }
7776
+
6824
7777
  // src/commands/validate.ts
7778
+ function formatAtsReport(result, debug, chalk6) {
7779
+ const scoreColor = result.score >= 75 ? chalk6.green : result.score >= 60 ? chalk6.yellow : chalk6.red;
7780
+ console.log("");
7781
+ console.log(chalk6.bold("\u2550\u2550\u2550 ATS Analysis Report \u2550\u2550\u2550"));
7782
+ console.log("");
7783
+ console.log(` Score: ${scoreColor(chalk6.bold(`${result.score}/100`))} (${result.rating.replace("-", " ")})`);
7784
+ console.log(` ${result.summary}`);
7785
+ console.log("");
7786
+ const categories = {};
7787
+ for (const check of result.checks) {
7788
+ const list = categories[check.category];
7789
+ if (!list) {
7790
+ categories[check.category] = [check];
7791
+ } else {
7792
+ list.push(check);
7793
+ }
7794
+ }
7795
+ const categoryLabels = {
7796
+ contact: "Contact Information",
7797
+ content: "Content Quality",
7798
+ structure: "Resume Structure",
7799
+ keywords: "Keywords"
7800
+ };
7801
+ for (const [cat, checks] of Object.entries(categories)) {
7802
+ const label = categoryLabels[cat] || cat;
7803
+ console.log(chalk6.bold(` ${label}`));
7804
+ for (const check of checks) {
7805
+ if (!debug && check.passed) continue;
7806
+ const icon = check.passed ? chalk6.green("\u2713") : chalk6.red("\u2717");
7807
+ const scoreText = chalk6.dim(`[${check.score}]`);
7808
+ console.log(` ${icon} ${check.message} ${scoreText}`);
7809
+ if (!check.passed && check.suggestion) {
7810
+ console.log(chalk6.dim(` \u2192 ${check.suggestion}`));
7811
+ }
7812
+ }
7813
+ console.log("");
7814
+ }
7815
+ if (result.keywords) {
7816
+ console.log(chalk6.bold(" Job Description Match"));
7817
+ const kw = result.keywords;
7818
+ const matchColor = kw.matchPercentage >= 70 ? chalk6.green : kw.matchPercentage >= 50 ? chalk6.yellow : chalk6.red;
7819
+ console.log(` Match: ${matchColor(`${kw.matchPercentage}%`)} (${kw.matched.length}/${kw.matched.length + kw.missing.length} keywords)`);
7820
+ if (kw.matched.length > 0) {
7821
+ console.log(chalk6.green(` \u2713 Matched: ${kw.matched.join(", ")}`));
7822
+ }
7823
+ if (kw.missing.length > 0) {
7824
+ console.log(chalk6.red(` \u2717 Missing: ${kw.missing.join(", ")}`));
7825
+ console.log(chalk6.dim(" \u2192 Consider incorporating these keywords into your resume where relevant."));
7826
+ }
7827
+ console.log("");
7828
+ }
7829
+ console.log(chalk6.dim("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
7830
+ }
6825
7831
  async function validateAction(options) {
6826
7832
  const chalk6 = (await import("chalk")).default;
6827
7833
  console.log(chalk6.blue("Starting resuml validate..."));
@@ -6829,13 +7835,41 @@ async function validateAction(options) {
6829
7835
  const inputPath = options.resume;
6830
7836
  const { yamlContents } = await loadResumeFiles(inputPath);
6831
7837
  console.log(chalk6.blue("Validating resume data..."));
7838
+ let resumeData;
6832
7839
  try {
6833
- await processResumeData(yamlContents);
7840
+ resumeData = await processResumeData(yamlContents);
6834
7841
  console.log(chalk6.green("\u2713 Resume data is valid against the schema!"));
6835
7842
  } catch (error) {
6836
7843
  handleCommandError(error, "validate", options.debug);
6837
7844
  return;
6838
7845
  }
7846
+ if (options.ats) {
7847
+ console.log(chalk6.blue("Running ATS analysis..."));
7848
+ let jobDescription;
7849
+ if (options.jd) {
7850
+ try {
7851
+ jobDescription = import_fs2.default.readFileSync(options.jd, "utf8");
7852
+ } catch {
7853
+ console.error(chalk6.red(`Failed to read job description file: ${options.jd}`));
7854
+ return;
7855
+ }
7856
+ }
7857
+ const result = analyzeAts(resumeData, {
7858
+ language: "en",
7859
+ jobDescription
7860
+ });
7861
+ if (options.format === "json") {
7862
+ console.log(JSON.stringify(result, null, 2));
7863
+ } else {
7864
+ formatAtsReport(result, !!options.debug, chalk6);
7865
+ }
7866
+ const threshold = options.atsThreshold ? parseInt(options.atsThreshold, 10) : void 0;
7867
+ if (threshold !== void 0 && result.score < threshold) {
7868
+ console.error(chalk6.red(`
7869
+ ATS score ${result.score} is below threshold ${threshold}.`));
7870
+ process.exit(1);
7871
+ }
7872
+ }
6839
7873
  } catch (error) {
6840
7874
  handleCommandError(error, "validate", options.debug);
6841
7875
  }
@@ -6843,7 +7877,7 @@ async function validateAction(options) {
6843
7877
 
6844
7878
  // src/commands/tojson.ts
6845
7879
  init_cjs_shims();
6846
- var import_fs2 = __toESM(require("fs"));
7880
+ var import_fs3 = __toESM(require("fs"), 1);
6847
7881
  async function toJsonAction(options) {
6848
7882
  const chalk6 = (await import("chalk")).default;
6849
7883
  console.log(chalk6.blue("Starting resuml tojson..."));
@@ -6854,7 +7888,7 @@ async function toJsonAction(options) {
6854
7888
  const resumeData = await processResumeData(yamlContents);
6855
7889
  console.log(chalk6.green("Processing and validation successful!"));
6856
7890
  const jsonOutput = JSON.stringify(resumeData, null, 2);
6857
- import_fs2.default.writeFileSync(options.output, jsonOutput, "utf8");
7891
+ import_fs3.default.writeFileSync(options.output, jsonOutput, "utf8");
6858
7892
  console.log(chalk6.green(`Successfully wrote output to ${options.output}`));
6859
7893
  } catch (error) {
6860
7894
  handleCommandError(error, "tojson", options.debug);
@@ -6863,15 +7897,15 @@ async function toJsonAction(options) {
6863
7897
 
6864
7898
  // src/commands/render.ts
6865
7899
  init_cjs_shims();
6866
- var import_fs3 = __toESM(require("fs"));
6867
- var import_node_path2 = __toESM(require("path"));
7900
+ var import_fs4 = __toESM(require("fs"), 1);
7901
+ var import_node_path2 = __toESM(require("path"), 1);
6868
7902
 
6869
7903
  // src/utils/themeLoader.ts
6870
7904
  init_cjs_shims();
6871
7905
  var import_child_process = require("child_process");
6872
7906
  var import_module = require("module");
6873
7907
  var require2 = (0, import_module.createRequire)(importMetaUrl);
6874
- async function installTheme(packageName) {
7908
+ function installTheme(packageName) {
6875
7909
  try {
6876
7910
  (0, import_child_process.execFileSync)("npm", ["install", packageName], {
6877
7911
  stdio: ["inherit", "pipe", "pipe"],
@@ -6881,7 +7915,7 @@ async function installTheme(packageName) {
6881
7915
  throw new Error(`Failed to install ${packageName}: ${error.message}`);
6882
7916
  }
6883
7917
  }
6884
- async function loadTheme(themeName, options) {
7918
+ function loadTheme(themeName, options) {
6885
7919
  let jsonResumeThemeName;
6886
7920
  let nativeThemeName;
6887
7921
  const autoInstall = options?.autoInstall !== false;
@@ -6902,7 +7936,7 @@ Please install the theme package manually.`
6902
7936
  }
6903
7937
  console.log(`\u{1F4E6} Theme ${jsonResumeThemeName} not found. Installing...`);
6904
7938
  try {
6905
- await installTheme(jsonResumeThemeName);
7939
+ installTheme(jsonResumeThemeName);
6906
7940
  console.log(`\u2705 Successfully installed ${jsonResumeThemeName}`);
6907
7941
  return require2(jsonResumeThemeName);
6908
7942
  } catch (installError) {
@@ -6921,7 +7955,7 @@ Please install the theme package manually.`
6921
7955
  }
6922
7956
 
6923
7957
  // src/commands/render.ts
6924
- var import_chalk = __toESM(require("chalk"));
7958
+ var import_chalk = __toESM(require("chalk"), 1);
6925
7959
  async function renderAction(options) {
6926
7960
  if (!options.theme) {
6927
7961
  throw new Error(
@@ -6935,15 +7969,39 @@ async function renderAction(options) {
6935
7969
  console.log(import_chalk.default.blue("Processing and validating resume data..."));
6936
7970
  const resumeData = await processResumeData(yamlContents);
6937
7971
  console.log(import_chalk.default.green("Resume data processing and validation successful!"));
6938
- const theme = await loadTheme(options.theme);
7972
+ const theme = loadTheme(options.theme);
6939
7973
  const htmlOutput = await theme.render(resumeData, {
6940
7974
  locale: options.language
6941
7975
  });
6942
- const outputHtmlPath = options.output || "resume.html";
6943
- console.log(import_chalk.default.blue(`Writing HTML output to ${outputHtmlPath}...`));
6944
- import_fs3.default.mkdirSync(import_node_path2.default.dirname(outputHtmlPath), { recursive: true });
6945
- import_fs3.default.writeFileSync(outputHtmlPath, htmlOutput, "utf8");
6946
- console.log(import_chalk.default.green(`Successfully wrote HTML output to ${outputHtmlPath}`));
7976
+ const defaultExtension = options.format;
7977
+ const defaultFilename = `resume.${defaultExtension}`;
7978
+ const outputPath = options.output || defaultFilename;
7979
+ if (options.format === "pdf") {
7980
+ console.log(import_chalk.default.blue(`Generating PDF output at ${outputPath}...`));
7981
+ const { chromium } = await import("playwright");
7982
+ const browser = await chromium.launch();
7983
+ const page = await browser.newPage();
7984
+ await page.setContent(htmlOutput, { waitUntil: "networkidle" });
7985
+ const pdfBuffer = await page.pdf({
7986
+ path: outputPath,
7987
+ format: "A4",
7988
+ printBackground: true,
7989
+ margin: {
7990
+ top: "1cm",
7991
+ right: "1cm",
7992
+ bottom: "1cm",
7993
+ left: "1cm"
7994
+ }
7995
+ });
7996
+ await browser.close();
7997
+ import_fs4.default.writeFileSync(outputPath, pdfBuffer);
7998
+ console.log(import_chalk.default.green(`Successfully wrote PDF output to ${outputPath}`));
7999
+ } else {
8000
+ console.log(import_chalk.default.blue(`Writing HTML output to ${outputPath}...`));
8001
+ import_fs4.default.mkdirSync(import_node_path2.default.dirname(outputPath), { recursive: true });
8002
+ import_fs4.default.writeFileSync(outputPath, htmlOutput, "utf8");
8003
+ console.log(import_chalk.default.green(`Successfully wrote HTML output to ${outputPath}`));
8004
+ }
6947
8005
  } catch (error) {
6948
8006
  handleCommandError(error, "render", options.debug);
6949
8007
  }
@@ -6951,9 +8009,9 @@ async function renderAction(options) {
6951
8009
 
6952
8010
  // src/commands/dev.ts
6953
8011
  init_cjs_shims();
6954
- var import_fs4 = __toESM(require("fs"));
6955
- var import_node_path3 = __toESM(require("path"));
6956
- var import_chalk2 = __toESM(require("chalk"));
8012
+ var import_fs5 = __toESM(require("fs"), 1);
8013
+ var import_node_path3 = __toESM(require("path"), 1);
8014
+ var import_chalk2 = __toESM(require("chalk"), 1);
6957
8015
  async function devAction(options) {
6958
8016
  if (!options.theme) {
6959
8017
  throw new Error(
@@ -6970,10 +8028,14 @@ async function devAction(options) {
6970
8028
  await renderResume(options);
6971
8029
  console.log(import_chalk2.default.green(`\u{1F680} Development server running at http://localhost:${port}`));
6972
8030
  console.log(import_chalk2.default.blue("Watching for file changes..."));
6973
- if (import_fs4.default.existsSync(inputPath) && import_fs4.default.statSync(inputPath).isDirectory()) {
6974
- watchDirectory(inputPath, () => renderResume(options));
6975
- } else if (import_fs4.default.existsSync(inputPath)) {
6976
- watchFile(inputPath, () => renderResume(options));
8031
+ if (import_fs5.default.existsSync(inputPath) && import_fs5.default.statSync(inputPath).isDirectory()) {
8032
+ watchDirectory(inputPath, () => {
8033
+ void renderResume(options);
8034
+ });
8035
+ } else if (import_fs5.default.existsSync(inputPath)) {
8036
+ watchFile(inputPath, () => {
8037
+ void renderResume(options);
8038
+ });
6977
8039
  }
6978
8040
  await startDevServer(port);
6979
8041
  } catch (error) {
@@ -6989,20 +8051,20 @@ async function renderResume(options) {
6989
8051
  const { yamlContents } = await loadResumeFiles(inputPath);
6990
8052
  console.log(import_chalk2.default.blue("\u{1F504} Processing resume data..."));
6991
8053
  const resumeData = await processResumeData(yamlContents);
6992
- const theme = await loadTheme(options.theme);
8054
+ const theme = loadTheme(options.theme ?? "stackoverflow");
6993
8055
  const htmlOutput = await theme.render(resumeData, {
6994
8056
  locale: options.language
6995
8057
  });
6996
8058
  const outputPath = import_node_path3.default.join(process.cwd(), ".resuml-dev", "index.html");
6997
- import_fs4.default.mkdirSync(import_node_path3.default.dirname(outputPath), { recursive: true });
6998
- import_fs4.default.writeFileSync(outputPath, htmlOutput, "utf8");
8059
+ import_fs5.default.mkdirSync(import_node_path3.default.dirname(outputPath), { recursive: true });
8060
+ import_fs5.default.writeFileSync(outputPath, htmlOutput, "utf8");
6999
8061
  console.log(import_chalk2.default.green("\u2705 Resume updated!"));
7000
8062
  } catch (error) {
7001
8063
  console.error(import_chalk2.default.red("\u274C Error rendering resume:"), error.message);
7002
8064
  }
7003
8065
  }
7004
8066
  function watchDirectory(dirPath, callback) {
7005
- import_fs4.default.watch(dirPath, { recursive: true }, (eventType, filename) => {
8067
+ import_fs5.default.watch(dirPath, { recursive: true }, (_eventType, filename) => {
7006
8068
  if (filename && (filename.endsWith(".yaml") || filename.endsWith(".yml"))) {
7007
8069
  console.log(import_chalk2.default.blue(`\u{1F4C1} File changed: ${filename}`));
7008
8070
  callback();
@@ -7010,7 +8072,7 @@ function watchDirectory(dirPath, callback) {
7010
8072
  });
7011
8073
  }
7012
8074
  function watchFile(filePath, callback) {
7013
- import_fs4.default.watch(filePath, (eventType) => {
8075
+ import_fs5.default.watch(filePath, (eventType) => {
7014
8076
  if (eventType === "change") {
7015
8077
  console.log(import_chalk2.default.blue(`\u{1F4C4} File changed: ${import_node_path3.default.basename(filePath)}`));
7016
8078
  callback();
@@ -7025,8 +8087,8 @@ async function startDevServer(port) {
7025
8087
  const pathname = parsedUrl.pathname || "/";
7026
8088
  if (pathname === "/" || pathname === "/index.html") {
7027
8089
  const htmlPath = import_node_path3.default.join(process.cwd(), ".resuml-dev", "index.html");
7028
- if (import_fs4.default.existsSync(htmlPath)) {
7029
- const html = import_fs4.default.readFileSync(htmlPath, "utf8");
8090
+ if (import_fs5.default.existsSync(htmlPath)) {
8091
+ const html = import_fs5.default.readFileSync(htmlPath, "utf8");
7030
8092
  const liveReloadScript = `
7031
8093
  <script>
7032
8094
  setInterval(() => {
@@ -7060,10 +8122,10 @@ async function startDevServer(port) {
7060
8122
 
7061
8123
  // src/commands/init.ts
7062
8124
  init_cjs_shims();
7063
- var import_fs5 = __toESM(require("fs"));
7064
- var import_path2 = __toESM(require("path"));
7065
- var import_readline = __toESM(require("readline"));
7066
- var import_chalk3 = __toESM(require("chalk"));
8125
+ var import_fs6 = __toESM(require("fs"), 1);
8126
+ var import_path2 = __toESM(require("path"), 1);
8127
+ var import_readline = __toESM(require("readline"), 1);
8128
+ var import_chalk3 = __toESM(require("chalk"), 1);
7067
8129
  function createReadlineInterface() {
7068
8130
  return import_readline.default.createInterface({
7069
8131
  input: process.stdin,
@@ -7221,7 +8283,7 @@ async function initAction(options) {
7221
8283
  const fullPath = import_path2.default.resolve(outputPath);
7222
8284
  const rl = createReadlineInterface();
7223
8285
  try {
7224
- if (import_fs5.default.existsSync(fullPath)) {
8286
+ if (import_fs6.default.existsSync(fullPath)) {
7225
8287
  const overwrite = await ask(
7226
8288
  rl,
7227
8289
  `${import_chalk3.default.yellow("\u26A0")} ${outputPath} already exists. Overwrite? (y/N)`,
@@ -7237,8 +8299,8 @@ async function initAction(options) {
7237
8299
  const email = await ask(rl, "Email address", "john@example.com");
7238
8300
  const label = await ask(rl, "Professional title/label", "Software Engineer");
7239
8301
  const yaml = generateResumeYaml(name, email, label);
7240
- import_fs5.default.mkdirSync(import_path2.default.dirname(fullPath), { recursive: true });
7241
- import_fs5.default.writeFileSync(fullPath, yaml, "utf8");
8302
+ import_fs6.default.mkdirSync(import_path2.default.dirname(fullPath), { recursive: true });
8303
+ import_fs6.default.writeFileSync(fullPath, yaml, "utf8");
7242
8304
  console.log(import_chalk3.default.green(`
7243
8305
  \u2705 Created ${outputPath}`));
7244
8306
  console.log(import_chalk3.default.blue("\nNext steps:"));
@@ -7252,18 +8314,17 @@ async function initAction(options) {
7252
8314
 
7253
8315
  // src/commands/pdf.ts
7254
8316
  init_cjs_shims();
7255
- var import_fs6 = __toESM(require("fs"));
7256
- var import_node_path4 = __toESM(require("path"));
7257
- var import_chalk4 = __toESM(require("chalk"));
7258
- async function loadPuppeteer() {
8317
+ var import_fs7 = __toESM(require("fs"), 1);
8318
+ var import_node_path4 = __toESM(require("path"), 1);
8319
+ var import_chalk4 = __toESM(require("chalk"), 1);
8320
+ async function loadPlaywright() {
7259
8321
  try {
7260
- const puppeteer = await import("puppeteer");
7261
- return puppeteer.default || puppeteer;
8322
+ const { chromium } = await import("playwright");
8323
+ return chromium;
7262
8324
  } catch {
7263
8325
  throw new Error(
7264
- `Puppeteer is required for PDF export but is not installed.
7265
- Install it with: ${import_chalk4.default.cyan("npm install puppeteer")}
7266
- Or for a smaller download: ${import_chalk4.default.cyan("npm install puppeteer-core")}`
8326
+ `Playwright is required for PDF export but is not installed.
8327
+ Install it with: ${import_chalk4.default.cyan("npm install playwright")}`
7267
8328
  );
7268
8329
  }
7269
8330
  }
@@ -7271,13 +8332,13 @@ function parseMargin(margin) {
7271
8332
  const defaultMargin = { top: "10mm", right: "10mm", bottom: "10mm", left: "10mm" };
7272
8333
  if (!margin) return defaultMargin;
7273
8334
  const parts = margin.split(",").map((s) => s.trim());
7274
- if (parts.length === 1) {
8335
+ if (parts.length === 1 && parts[0]) {
7275
8336
  return { top: parts[0], right: parts[0], bottom: parts[0], left: parts[0] };
7276
8337
  }
7277
- if (parts.length === 2) {
8338
+ if (parts.length === 2 && parts[0] && parts[1]) {
7278
8339
  return { top: parts[0], right: parts[1], bottom: parts[0], left: parts[1] };
7279
8340
  }
7280
- if (parts.length === 4) {
8341
+ if (parts.length === 4 && parts[0] && parts[1] && parts[2] && parts[3]) {
7281
8342
  return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[3] };
7282
8343
  }
7283
8344
  return defaultMargin;
@@ -7295,28 +8356,28 @@ async function pdfAction(options) {
7295
8356
  console.log(import_chalk4.default.blue("Processing and validating resume data..."));
7296
8357
  const resumeData = await processResumeData(yamlContents);
7297
8358
  console.log(import_chalk4.default.green("Resume data processing and validation successful!"));
7298
- const theme = await loadTheme(options.theme);
8359
+ const theme = loadTheme(options.theme);
7299
8360
  const htmlOutput = await theme.render(resumeData, {
7300
8361
  locale: options.language
7301
8362
  });
7302
- console.log(import_chalk4.default.blue("Loading Puppeteer..."));
7303
- const puppeteer = await loadPuppeteer();
8363
+ console.log(import_chalk4.default.blue("Loading Playwright..."));
8364
+ const chromium = await loadPlaywright();
7304
8365
  const outputPath = options.output || "resume.pdf";
7305
8366
  const format = options.format === "Letter" ? "Letter" : "A4";
7306
8367
  const margin = parseMargin(options.margin);
7307
8368
  console.log(import_chalk4.default.blue(`Generating PDF (${format} format)...`));
7308
- const browser = await puppeteer.launch({ headless: true });
8369
+ const browser = await chromium.launch({ headless: true });
7309
8370
  try {
7310
8371
  const page = await browser.newPage();
7311
- await page.setContent(htmlOutput, { waitUntil: "networkidle0" });
8372
+ await page.setContent(htmlOutput, { waitUntil: "networkidle" });
7312
8373
  const pdfBuffer = await page.pdf({
7313
8374
  format,
7314
8375
  margin,
7315
8376
  printBackground: true,
7316
8377
  preferCSSPageSize: true
7317
8378
  });
7318
- import_fs6.default.mkdirSync(import_node_path4.default.dirname(import_node_path4.default.resolve(outputPath)), { recursive: true });
7319
- import_fs6.default.writeFileSync(outputPath, pdfBuffer);
8379
+ import_fs7.default.mkdirSync(import_node_path4.default.dirname(import_node_path4.default.resolve(outputPath)), { recursive: true });
8380
+ import_fs7.default.writeFileSync(outputPath, pdfBuffer);
7320
8381
  console.log(import_chalk4.default.green(`\u2705 Successfully generated ${outputPath}`));
7321
8382
  } finally {
7322
8383
  await browser.close();
@@ -7328,7 +8389,7 @@ async function pdfAction(options) {
7328
8389
 
7329
8390
  // src/commands/themes.ts
7330
8391
  init_cjs_shims();
7331
- var import_chalk5 = __toESM(require("chalk"));
8392
+ var import_chalk5 = __toESM(require("chalk"), 1);
7332
8393
  var import_child_process2 = require("child_process");
7333
8394
  var import_module2 = require("module");
7334
8395
  var KNOWN_THEMES = [
@@ -7358,7 +8419,7 @@ function getInstalledVersion(pkg) {
7358
8419
  try {
7359
8420
  const require3 = (0, import_module2.createRequire)(process.cwd() + "/");
7360
8421
  const pkgJson = require3(`${pkg}/package.json`);
7361
- return pkgJson.version || null;
8422
+ return pkgJson.version ?? null;
7362
8423
  } catch {
7363
8424
  return null;
7364
8425
  }
@@ -7407,7 +8468,7 @@ Use it with: ${import_chalk5.default.cyan(`resuml render --theme ${known?.name |
7407
8468
  `));
7408
8469
  }
7409
8470
  }
7410
- async function themesAction(options) {
8471
+ function themesAction(options) {
7411
8472
  if (options.install) {
7412
8473
  installTheme2(options.install);
7413
8474
  } else {
@@ -7472,10 +8533,10 @@ function injectCss(html, css) {
7472
8533
  var currentDir = import_path3.default.dirname((0, import_url.fileURLToPath)(importMetaUrl));
7473
8534
  function getCliVersion() {
7474
8535
  const packageJsonPath = import_path3.default.resolve(currentDir, "../package.json");
7475
- if (import_fs7.default.existsSync(packageJsonPath)) {
8536
+ if (import_fs8.default.existsSync(packageJsonPath)) {
7476
8537
  try {
7477
- const packageJson = JSON.parse(import_fs7.default.readFileSync(packageJsonPath, "utf8"));
7478
- return packageJson.version || "0.0.0";
8538
+ const packageJson = JSON.parse(import_fs8.default.readFileSync(packageJsonPath, "utf8"));
8539
+ return packageJson.version ?? "0.0.0";
7479
8540
  } catch {
7480
8541
  return "0.0.0";
7481
8542
  }
@@ -7484,29 +8545,30 @@ function getCliVersion() {
7484
8545
  }
7485
8546
  var program = new import_commander.Command();
7486
8547
  program.name("resuml").description("CLI tool for managing resuml resume files.").version(getCliVersion());
7487
- program.command("validate").description("Validates resume data against the schema.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("--debug", "Show detailed validation errors.").action(validateAction);
8548
+ program.command("validate").description("Validates resume data against the schema.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("--debug", "Show detailed validation errors.").option("--ats", "Run ATS (Applicant Tracking System) compatibility analysis.").option("--jd <path>", "Path to a job description file for keyword matching (requires --ats).").option("--ats-threshold <score>", "Minimum ATS score (0-100). Exit with code 1 if below threshold.").option("--format <type>", "Output format for ATS results (text or json).", "text").action(validateAction);
7488
8549
  program.command("tojson").description("Converts YAML resume data to JSON format.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-o, --output <file>", "Output JSON file path.", "resume.json").option("--debug", "Show detailed validation and processing information.").action(toJsonAction);
7489
- program.command("render").description("Renders the resume data using a specified theme.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("-o, --output <file>", "Output HTML file path (defaults to resume.html).").option("--language <code>", "Language code for localization.", "en").option("--debug", "Show detailed validation and processing information.").action(renderAction);
8550
+ program.command("render").description("Renders the resume data using a specified theme.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("-o, --output <file>", "Output file path.").option("--format <type>", "Output format (html or pdf).", "html").option("--language <code>", "Language code for localization.", "en").option("--debug", "Show detailed validation and processing information.").action(renderAction);
7490
8551
  program.command("dev").description("Start development server with hot-reload.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("--port <number>", "Port for development server.", "3000").option("--language <code>", "Language code for localization.", "en").option("--debug", "Show detailed validation and processing information.").action(devAction);
7491
8552
  program.command("init").description("Scaffold a starter resume.yaml file with all sections.").option("-o, --output <file>", "Output YAML file path.", "resume.yaml").action(initAction);
7492
- program.command("pdf").description("Export resume as PDF using Puppeteer.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("-o, --output <file>", "Output PDF file path.", "resume.pdf").option("--language <code>", "Language code for localization.", "en").option("--format <size>", "Page format: A4 or Letter.", "A4").option("--margin <values>", 'Page margins (e.g., "10mm" or "10mm,15mm,10mm,15mm").').option("--debug", "Show detailed validation and processing information.").action(pdfAction);
8553
+ program.command("pdf").description("Export resume as PDF using Playwright.").option("-r, --resume <path>", "Input YAML file, directory, or glob pattern.").option("-t, --theme <name>", "Theme name (e.g., stackoverflow, react).").option("-o, --output <file>", "Output PDF file path.", "resume.pdf").option("--language <code>", "Language code for localization.", "en").option("--format <size>", "Page format: A4 or Letter.", "A4").option("--margin <values>", 'Page margins (e.g., "10mm" or "10mm,15mm,10mm,15mm").').option("--debug", "Show detailed validation and processing information.").action(pdfAction);
7493
8554
  program.command("themes").description("List available JSON Resume themes and install them.").option("--install <name>", "Install a theme by name (e.g., stackoverflow, elegant).").action(themesAction);
7494
- if (process.env.NODE_ENV !== "test") {
7495
- (async () => {
8555
+ if (process.env["NODE_ENV"] !== "test") {
8556
+ void (async () => {
7496
8557
  try {
7497
8558
  await program.parseAsync(process.argv);
7498
8559
  } catch (e) {
7499
- console.error("Command line error:", e?.message);
8560
+ console.error("Command line error:", e.message);
7500
8561
  process.exit(1);
7501
8562
  }
7502
8563
  })();
7503
8564
  }
7504
8565
  // Annotate the CommonJS export names for ESM import in node:
7505
8566
  0 && (module.exports = {
8567
+ analyzeAts,
7506
8568
  loadResumeFiles,
7507
8569
  loadTheme,
7508
8570
  processResumeData,
7509
8571
  program,
7510
8572
  themeRender
7511
8573
  });
7512
- //# sourceMappingURL=index.js.map
8574
+ //# sourceMappingURL=index.cjs.map