careermate 0.0.8 → 0.0.9

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.
Files changed (50) hide show
  1. package/AGENTS.md +2 -2
  2. package/LICENSE +1 -1
  3. package/README.md +78 -81
  4. package/bin/careermate.mjs +11 -2
  5. package/dist/career-os/eop/cover-letter.md +10 -4
  6. package/dist/career-os/eop/interview-prep.md +1 -0
  7. package/dist/career-os/knowledge/cover-letter.md +4 -0
  8. package/dist/career-os/knowledge/verifiers/human-voice.md +7 -2
  9. package/dist/doctor.mjs +888 -340
  10. package/dist/mcp.mjs +904 -350
  11. package/dist/migrate.mjs +208 -16
  12. package/dist/public/i18n/en/applications.js +19 -0
  13. package/dist/public/i18n/en/common.js +6 -0
  14. package/dist/public/i18n/en/jobs.js +17 -0
  15. package/dist/public/i18n/en/settings.js +5 -2
  16. package/dist/public/i18n/ko/applications.js +19 -0
  17. package/dist/public/i18n/ko/common.js +5 -0
  18. package/dist/public/i18n/ko/jobs.js +17 -0
  19. package/dist/public/i18n/ko/settings.js +5 -2
  20. package/dist/public/lib.js +29 -0
  21. package/dist/public/pages/applications.js +86 -10
  22. package/dist/public/pages/documents.js +16 -7
  23. package/dist/public/pages/home.js +12 -0
  24. package/dist/public/pages/interview.js +11 -4
  25. package/dist/public/pages/jobs.js +71 -1
  26. package/dist/public/pages/settings.js +2 -2
  27. package/dist/public/styles.css +13 -0
  28. package/dist/web.mjs +544 -199
  29. package/docs/FAQ.md +13 -3
  30. package/docs/MCP_TOOLS.md +22 -9
  31. package/docs/career-os/eop/cover-letter.md +10 -4
  32. package/docs/career-os/eop/interview-prep.md +1 -0
  33. package/docs/career-os/knowledge/cover-letter.md +4 -0
  34. package/docs/career-os/knowledge/verifiers/human-voice.md +7 -2
  35. package/package.json +3 -1
  36. package/packages/core/src/index.ts +1 -0
  37. package/packages/core/src/jobsearch.ts +6 -2
  38. package/packages/core/src/offers.ts +41 -0
  39. package/packages/core/src/services.ts +5 -1
  40. package/packages/core/src/summary.ts +245 -19
  41. package/packages/db/src/connection.ts +70 -2
  42. package/packages/db/src/index.ts +1 -0
  43. package/packages/db/src/repositories.ts +140 -8
  44. package/packages/db/src/schema.ts +140 -8
  45. package/packages/mcp-tools/src/result.ts +9 -0
  46. package/packages/mcp-tools/src/tools.ts +143 -20
  47. package/packages/parsers/src/documents.ts +7 -1
  48. package/packages/prompts/src/humanize.ts +4 -0
  49. package/packages/shared/src/schemas.ts +134 -0
  50. package/scripts/init.ts +2 -0
package/dist/doctor.mjs CHANGED
@@ -976,8 +976,8 @@ var init_tap = __esm({
976
976
  });
977
977
 
978
978
  // node_modules/underscore/modules/toPath.js
979
- function toPath(path8) {
980
- return isArray_default(path8) ? path8 : [path8];
979
+ function toPath(path9) {
980
+ return isArray_default(path9) ? path9 : [path9];
981
981
  }
982
982
  var init_toPath = __esm({
983
983
  "node_modules/underscore/modules/toPath.js"() {
@@ -989,8 +989,8 @@ var init_toPath = __esm({
989
989
  });
990
990
 
991
991
  // node_modules/underscore/modules/_toPath.js
992
- function toPath2(path8) {
993
- return _.toPath(path8);
992
+ function toPath2(path9) {
993
+ return _.toPath(path9);
994
994
  }
995
995
  var init_toPath2 = __esm({
996
996
  "node_modules/underscore/modules/_toPath.js"() {
@@ -1001,11 +1001,11 @@ var init_toPath2 = __esm({
1001
1001
  });
1002
1002
 
1003
1003
  // node_modules/underscore/modules/_deepGet.js
1004
- function deepGet(obj2, path8) {
1005
- var length = path8.length;
1004
+ function deepGet(obj2, path9) {
1005
+ var length = path9.length;
1006
1006
  for (var i2 = 0; i2 < length; i2++) {
1007
1007
  if (obj2 == null) return void 0;
1008
- obj2 = obj2[path8[i2]];
1008
+ obj2 = obj2[path9[i2]];
1009
1009
  }
1010
1010
  return length ? obj2 : void 0;
1011
1011
  }
@@ -1016,8 +1016,8 @@ var init_deepGet = __esm({
1016
1016
  });
1017
1017
 
1018
1018
  // node_modules/underscore/modules/get.js
1019
- function get(object2, path8, defaultValue) {
1020
- var value = deepGet(object2, toPath2(path8));
1019
+ function get(object2, path9, defaultValue) {
1020
+ var value = deepGet(object2, toPath2(path9));
1021
1021
  return isUndefined(value) ? defaultValue : value;
1022
1022
  }
1023
1023
  var init_get = __esm({
@@ -1030,11 +1030,11 @@ var init_get = __esm({
1030
1030
  });
1031
1031
 
1032
1032
  // node_modules/underscore/modules/has.js
1033
- function has2(obj2, path8) {
1034
- path8 = toPath2(path8);
1035
- var length = path8.length;
1033
+ function has2(obj2, path9) {
1034
+ path9 = toPath2(path9);
1035
+ var length = path9.length;
1036
1036
  for (var i2 = 0; i2 < length; i2++) {
1037
- var key = path8[i2];
1037
+ var key = path9[i2];
1038
1038
  if (!has(obj2, key)) return false;
1039
1039
  obj2 = obj2[key];
1040
1040
  }
@@ -1074,10 +1074,10 @@ var init_matcher = __esm({
1074
1074
  });
1075
1075
 
1076
1076
  // node_modules/underscore/modules/property.js
1077
- function property(path8) {
1078
- path8 = toPath2(path8);
1077
+ function property(path9) {
1078
+ path9 = toPath2(path9);
1079
1079
  return function(obj2) {
1080
- return deepGet(obj2, path8);
1080
+ return deepGet(obj2, path9);
1081
1081
  };
1082
1082
  }
1083
1083
  var init_property = __esm({
@@ -1193,8 +1193,8 @@ var init_noop = __esm({
1193
1193
  // node_modules/underscore/modules/propertyOf.js
1194
1194
  function propertyOf(obj2) {
1195
1195
  if (obj2 == null) return noop2;
1196
- return function(path8) {
1197
- return get(obj2, path8);
1196
+ return function(path9) {
1197
+ return get(obj2, path9);
1198
1198
  };
1199
1199
  }
1200
1200
  var init_propertyOf = __esm({
@@ -1342,7 +1342,7 @@ function template(text, settings, oldSettings) {
1342
1342
  var index = 0;
1343
1343
  var source = "__p+='";
1344
1344
  text.replace(matcher2, function(match, escape2, interpolate, evaluate, offset) {
1345
- source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
1345
+ source += text.slice(index, offset).replace(escapeRegExp2, escapeChar);
1346
1346
  index = offset + match.length;
1347
1347
  if (escape2) {
1348
1348
  source += "'+\n((__t=(" + escape2 + "))==null?'':_.escape(__t))+\n'";
@@ -1377,7 +1377,7 @@ function template(text, settings, oldSettings) {
1377
1377
  template2.source = "function(" + argument + "){\n" + source + "}";
1378
1378
  return template2;
1379
1379
  }
1380
- var noMatch, escapes, escapeRegExp, bareIdentifier;
1380
+ var noMatch, escapes, escapeRegExp2, bareIdentifier;
1381
1381
  var init_template = __esm({
1382
1382
  "node_modules/underscore/modules/template.js"() {
1383
1383
  "use strict";
@@ -1393,20 +1393,20 @@ var init_template = __esm({
1393
1393
  "\u2028": "u2028",
1394
1394
  "\u2029": "u2029"
1395
1395
  };
1396
- escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
1396
+ escapeRegExp2 = /\\|'|\r|\n|\u2028|\u2029/g;
1397
1397
  bareIdentifier = /^\s*(\w|\$)+\s*$/;
1398
1398
  }
1399
1399
  });
1400
1400
 
1401
1401
  // node_modules/underscore/modules/result.js
1402
- function result(obj2, path8, fallback) {
1403
- path8 = toPath2(path8);
1404
- var length = path8.length;
1402
+ function result(obj2, path9, fallback) {
1403
+ path9 = toPath2(path9);
1404
+ var length = path9.length;
1405
1405
  if (!length) {
1406
1406
  return isFunction_default(fallback) ? fallback.call(obj2) : fallback;
1407
1407
  }
1408
1408
  for (var i2 = 0; i2 < length; i2++) {
1409
- var prop = obj2 == null ? void 0 : obj2[path8[i2]];
1409
+ var prop = obj2 == null ? void 0 : obj2[path9[i2]];
1410
1410
  if (prop === void 0) {
1411
1411
  prop = fallback;
1412
1412
  i2 = length;
@@ -2130,14 +2130,14 @@ var init_invoke = __esm({
2130
2130
  init_map();
2131
2131
  init_deepGet();
2132
2132
  init_toPath2();
2133
- invoke_default = restArguments(function(obj2, path8, args) {
2133
+ invoke_default = restArguments(function(obj2, path9, args) {
2134
2134
  var contextPath, func;
2135
- if (isFunction_default(path8)) {
2136
- func = path8;
2135
+ if (isFunction_default(path9)) {
2136
+ func = path9;
2137
2137
  } else {
2138
- path8 = toPath2(path8);
2139
- contextPath = path8.slice(0, -1);
2140
- path8 = path8[path8.length - 1];
2138
+ path9 = toPath2(path9);
2139
+ contextPath = path9.slice(0, -1);
2140
+ path9 = path9[path9.length - 1];
2141
2141
  }
2142
2142
  return map(obj2, function(context) {
2143
2143
  var method = func;
@@ -2146,7 +2146,7 @@ var init_invoke = __esm({
2146
2146
  context = deepGet(context, contextPath);
2147
2147
  }
2148
2148
  if (context == null) return void 0;
2149
- method = context[path8];
2149
+ method = context[path9];
2150
2150
  }
2151
2151
  return method == null ? method : method.apply(context, args);
2152
2152
  });
@@ -11664,8 +11664,8 @@ var require_utils2 = __commonJS({
11664
11664
  var result2 = transform[inputType][outputType](input);
11665
11665
  return result2;
11666
11666
  };
11667
- exports2.resolve = function(path8) {
11668
- var parts = path8.split("/");
11667
+ exports2.resolve = function(path9) {
11668
+ var parts = path9.split("/");
11669
11669
  var result2 = [];
11670
11670
  for (var index = 0; index < parts.length; index++) {
11671
11671
  var part = parts[index];
@@ -17518,18 +17518,18 @@ var require_object = __commonJS({
17518
17518
  var object2 = new ZipObject(name, zipObjectContent, o);
17519
17519
  this.files[name] = object2;
17520
17520
  };
17521
- var parentFolder = function(path8) {
17522
- if (path8.slice(-1) === "/") {
17523
- path8 = path8.substring(0, path8.length - 1);
17521
+ var parentFolder = function(path9) {
17522
+ if (path9.slice(-1) === "/") {
17523
+ path9 = path9.substring(0, path9.length - 1);
17524
17524
  }
17525
- var lastSlash = path8.lastIndexOf("/");
17526
- return lastSlash > 0 ? path8.substring(0, lastSlash) : "";
17525
+ var lastSlash = path9.lastIndexOf("/");
17526
+ return lastSlash > 0 ? path9.substring(0, lastSlash) : "";
17527
17527
  };
17528
- var forceTrailingSlash = function(path8) {
17529
- if (path8.slice(-1) !== "/") {
17530
- path8 += "/";
17528
+ var forceTrailingSlash = function(path9) {
17529
+ if (path9.slice(-1) !== "/") {
17530
+ path9 += "/";
17531
17531
  }
17532
- return path8;
17532
+ return path9;
17533
17533
  };
17534
17534
  var folderAdd = function(name, createFolders) {
17535
17535
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -18568,27 +18568,27 @@ var require_zipfile = __commonJS({
18568
18568
  };
18569
18569
  });
18570
18570
  }
18571
- function splitPath(path8) {
18572
- var lastIndex = path8.lastIndexOf("/");
18571
+ function splitPath(path9) {
18572
+ var lastIndex = path9.lastIndexOf("/");
18573
18573
  if (lastIndex === -1) {
18574
- return { dirname: "", basename: path8 };
18574
+ return { dirname: "", basename: path9 };
18575
18575
  } else {
18576
18576
  return {
18577
- dirname: path8.substring(0, lastIndex),
18578
- basename: path8.substring(lastIndex + 1)
18577
+ dirname: path9.substring(0, lastIndex),
18578
+ basename: path9.substring(lastIndex + 1)
18579
18579
  };
18580
18580
  }
18581
18581
  }
18582
18582
  function joinPath() {
18583
- var nonEmptyPaths = Array.prototype.filter.call(arguments, function(path8) {
18584
- return path8;
18583
+ var nonEmptyPaths = Array.prototype.filter.call(arguments, function(path9) {
18584
+ return path9;
18585
18585
  });
18586
18586
  var relevantPaths = [];
18587
- nonEmptyPaths.forEach(function(path8) {
18588
- if (/^\//.test(path8)) {
18589
- relevantPaths = [path8];
18587
+ nonEmptyPaths.forEach(function(path9) {
18588
+ if (/^\//.test(path9)) {
18589
+ relevantPaths = [path9];
18590
18590
  } else {
18591
- relevantPaths.push(path8);
18591
+ relevantPaths.push(path9);
18592
18592
  }
18593
18593
  });
18594
18594
  return relevantPaths.join("/");
@@ -26013,9 +26013,9 @@ var require_office_xml_reader = __commonJS({
26013
26013
  return collapseAlternateContent(document2)[0];
26014
26014
  });
26015
26015
  }
26016
- function readXmlFromZipFile(docxFile, path8) {
26017
- if (docxFile.exists(path8)) {
26018
- return docxFile.read(path8, "utf-8").then(stripUtf8Bom).then(read);
26016
+ function readXmlFromZipFile(docxFile, path9) {
26017
+ if (docxFile.exists(path9)) {
26018
+ return docxFile.read(path9, "utf-8").then(stripUtf8Bom).then(read);
26019
26019
  } else {
26020
26020
  return promises.resolve(null);
26021
26021
  }
@@ -27782,10 +27782,10 @@ var require_body_reader = __commonJS({
27782
27782
  }
27783
27783
  }
27784
27784
  function findEmbeddedImageFile(relationshipId) {
27785
- var path8 = uris.uriToZipEntryName("word", relationships.findTargetByRelationshipId(relationshipId));
27785
+ var path9 = uris.uriToZipEntryName("word", relationships.findTargetByRelationshipId(relationshipId));
27786
27786
  return {
27787
- path: path8,
27788
- read: docxFile.read.bind(docxFile, path8)
27787
+ path: path9,
27788
+ read: docxFile.read.bind(docxFile, path9)
27789
27789
  };
27790
27790
  }
27791
27791
  function readImage(imageFile, altText) {
@@ -28025,12 +28025,12 @@ var require_content_types_reader = __commonJS({
28025
28025
  }
28026
28026
  function contentTypes(overrides, extensionDefaults) {
28027
28027
  return {
28028
- findContentType: function(path8) {
28029
- var overrideContentType = overrides[path8];
28028
+ findContentType: function(path9) {
28029
+ var overrideContentType = overrides[path9];
28030
28030
  if (overrideContentType) {
28031
28031
  return overrideContentType;
28032
28032
  } else {
28033
- var pathParts = path8.split(".");
28033
+ var pathParts = path9.split(".");
28034
28034
  var extension = pathParts[pathParts.length - 1];
28035
28035
  if (extensionDefaults.hasOwnProperty(extension)) {
28036
28036
  return extensionDefaults[extension];
@@ -28277,12 +28277,12 @@ var require_comments_reader = __commonJS({
28277
28277
  var require_path_is_absolute = __commonJS({
28278
28278
  "node_modules/path-is-absolute/index.js"(exports2, module2) {
28279
28279
  "use strict";
28280
- function posix(path8) {
28281
- return path8.charAt(0) === "/";
28280
+ function posix(path9) {
28281
+ return path9.charAt(0) === "/";
28282
28282
  }
28283
- function win32(path8) {
28283
+ function win32(path9) {
28284
28284
  var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
28285
- var result2 = splitDeviceRe.exec(path8);
28285
+ var result2 = splitDeviceRe.exec(path9);
28286
28286
  var device = result2[1] || "";
28287
28287
  var isUnc = Boolean(device && device.charAt(1) !== ":");
28288
28288
  return Boolean(result2[2] || isUnc);
@@ -28297,7 +28297,7 @@ var require_path_is_absolute = __commonJS({
28297
28297
  var require_files = __commonJS({
28298
28298
  "node_modules/mammoth/lib/docx/files.js"(exports2) {
28299
28299
  "use strict";
28300
- var fs9 = __require("fs");
28300
+ var fs10 = __require("fs");
28301
28301
  var url = __require("url");
28302
28302
  var os3 = __require("os");
28303
28303
  var dirname = __require("path").dirname;
@@ -28317,19 +28317,19 @@ var require_files = __commonJS({
28317
28317
  }
28318
28318
  var base = options.relativeToFile ? dirname(options.relativeToFile) : null;
28319
28319
  function read(uri, encoding) {
28320
- return resolveUri(uri).then(function(path8) {
28321
- return readFile(path8, encoding).caught(function(error) {
28320
+ return resolveUri(uri).then(function(path9) {
28321
+ return readFile(path9, encoding).caught(function(error) {
28322
28322
  var message = "could not open external image: '" + uri + "' (document directory: '" + base + "')\n" + error.message;
28323
28323
  return promises.reject(new Error(message));
28324
28324
  });
28325
28325
  });
28326
28326
  }
28327
28327
  function resolveUri(uri) {
28328
- var path8 = uriToPath(uri);
28329
- if (isAbsolutePath(path8)) {
28330
- return promises.resolve(path8);
28328
+ var path9 = uriToPath(uri);
28329
+ if (isAbsolutePath(path9)) {
28330
+ return promises.resolve(path9);
28331
28331
  } else if (base) {
28332
- return promises.resolve(resolvePath(base, path8));
28332
+ return promises.resolve(resolvePath(base, path9));
28333
28333
  } else {
28334
28334
  return promises.reject(new Error("could not find external image '" + uri + "', path of input document is unknown"));
28335
28335
  }
@@ -28338,18 +28338,18 @@ var require_files = __commonJS({
28338
28338
  read
28339
28339
  };
28340
28340
  }
28341
- var readFile = promises.promisify(fs9.readFile.bind(fs9));
28341
+ var readFile = promises.promisify(fs10.readFile.bind(fs10));
28342
28342
  function uriToPath(uriString, platform) {
28343
28343
  if (!platform) {
28344
28344
  platform = os3.platform();
28345
28345
  }
28346
28346
  var uri = url.parse(uriString);
28347
28347
  if (isLocalFileUri(uri) || isRelativeUri(uri)) {
28348
- var path8 = decodeURIComponent(uri.path);
28349
- if (platform === "win32" && /^\/[a-z]:/i.test(path8)) {
28350
- return path8.slice(1);
28348
+ var path9 = decodeURIComponent(uri.path);
28349
+ if (platform === "win32" && /^\/[a-z]:/i.test(path9)) {
28350
+ return path9.slice(1);
28351
28351
  } else {
28352
- return path8;
28352
+ return path9;
28353
28353
  }
28354
28354
  } else {
28355
28355
  throw new Error("Could not convert URI to path: " + uriString);
@@ -28550,18 +28550,18 @@ var require_docx_reader = __commonJS({
28550
28550
  readElement: contentTypesReader.readContentTypesFromXml,
28551
28551
  defaultValue: contentTypesReader.defaultContentTypes
28552
28552
  });
28553
- function readNumberingFromZipFile(zipFile, path8, styles) {
28553
+ function readNumberingFromZipFile(zipFile, path9, styles) {
28554
28554
  return xmlFileReader({
28555
- filename: path8,
28555
+ filename: path9,
28556
28556
  readElement: function(element) {
28557
28557
  return numberingXml.readNumberingXml(element, { styles });
28558
28558
  },
28559
28559
  defaultValue: numberingXml.defaultNumbering
28560
28560
  })(zipFile);
28561
28561
  }
28562
- function readStylesFromZipFile(zipFile, path8) {
28562
+ function readStylesFromZipFile(zipFile, path9) {
28563
28563
  return xmlFileReader({
28564
- filename: path8,
28564
+ filename: path9,
28565
28565
  readElement: stylesReader.readStylesXml,
28566
28566
  defaultValue: stylesReader.defaultStyles
28567
28567
  })(zipFile);
@@ -28593,10 +28593,10 @@ var require_style_map = __commonJS({
28593
28593
  });
28594
28594
  }
28595
28595
  function updateRelationships(docxFile) {
28596
- var path8 = "word/_rels/document.xml.rels";
28596
+ var path9 = "word/_rels/document.xml.rels";
28597
28597
  var relationshipsUri = "http://schemas.openxmlformats.org/package/2006/relationships";
28598
28598
  var relationshipElementName = "{" + relationshipsUri + "}Relationship";
28599
- return docxFile.read(path8, "utf8").then(xml.readString).then(function(relationshipsContainer) {
28599
+ return docxFile.read(path9, "utf8").then(xml.readString).then(function(relationshipsContainer) {
28600
28600
  var relationships = relationshipsContainer.children;
28601
28601
  addOrUpdateElement(relationships, relationshipElementName, "Id", {
28602
28602
  "Id": "rMammothStyleMap",
@@ -28604,21 +28604,21 @@ var require_style_map = __commonJS({
28604
28604
  "Target": styleMapAbsolutePath
28605
28605
  });
28606
28606
  var namespaces = { "": relationshipsUri };
28607
- return docxFile.write(path8, xml.writeString(relationshipsContainer, namespaces));
28607
+ return docxFile.write(path9, xml.writeString(relationshipsContainer, namespaces));
28608
28608
  });
28609
28609
  }
28610
28610
  function updateContentTypes(docxFile) {
28611
- var path8 = "[Content_Types].xml";
28611
+ var path9 = "[Content_Types].xml";
28612
28612
  var contentTypesUri = "http://schemas.openxmlformats.org/package/2006/content-types";
28613
28613
  var overrideName = "{" + contentTypesUri + "}Override";
28614
- return docxFile.read(path8, "utf8").then(xml.readString).then(function(typesElement) {
28614
+ return docxFile.read(path9, "utf8").then(xml.readString).then(function(typesElement) {
28615
28615
  var children = typesElement.children;
28616
28616
  addOrUpdateElement(children, overrideName, "PartName", {
28617
28617
  "PartName": styleMapAbsolutePath,
28618
28618
  "ContentType": "text/prs.mammoth.style-map"
28619
28619
  });
28620
28620
  var namespaces = { "": contentTypesUri };
28621
- return docxFile.write(path8, xml.writeString(typesElement, namespaces));
28621
+ return docxFile.write(path9, xml.writeString(typesElement, namespaces));
28622
28622
  });
28623
28623
  }
28624
28624
  function addOrUpdateElement(elements, name, identifyingAttribute, attributes) {
@@ -29315,9 +29315,9 @@ var require_document_to_html = __commonJS({
29315
29315
  };
29316
29316
  var paths = [];
29317
29317
  if (run.highlight !== null) {
29318
- var path8 = findHtmlPath({ type: "highlight", color: run.highlight });
29319
- if (path8) {
29320
- paths.push(path8);
29318
+ var path9 = findHtmlPath({ type: "highlight", color: run.highlight });
29319
+ if (path9) {
29320
+ paths.push(path9);
29321
29321
  }
29322
29322
  }
29323
29323
  if (run.isSmallCaps) {
@@ -29352,15 +29352,15 @@ var require_document_to_html = __commonJS({
29352
29352
  messages.push(unrecognisedStyleWarning("run", run));
29353
29353
  }
29354
29354
  paths.push(stylePath);
29355
- paths.forEach(function(path9) {
29356
- nodes = path9.wrap.bind(path9, nodes);
29355
+ paths.forEach(function(path10) {
29356
+ nodes = path10.wrap.bind(path10, nodes);
29357
29357
  });
29358
29358
  return nodes();
29359
29359
  }
29360
29360
  function findHtmlPathForRunProperty(elementType, defaultTagName) {
29361
- var path8 = findHtmlPath({ type: elementType });
29362
- if (path8) {
29363
- return path8;
29361
+ var path9 = findHtmlPath({ type: elementType });
29362
+ if (path9) {
29363
+ return path9;
29364
29364
  } else if (defaultTagName) {
29365
29365
  return htmlPaths.element(defaultTagName, {}, { fresh: false });
29366
29366
  } else {
@@ -31080,11 +31080,11 @@ var require_options_reader = __commonJS({
31080
31080
  var require_unzip = __commonJS({
31081
31081
  "node_modules/mammoth/lib/unzip.js"(exports2) {
31082
31082
  "use strict";
31083
- var fs9 = __require("fs");
31083
+ var fs10 = __require("fs");
31084
31084
  var promises = require_promises();
31085
31085
  var zipfile = require_zipfile();
31086
31086
  exports2.openZip = openZip;
31087
- var readFile = promises.promisify(fs9.readFile);
31087
+ var readFile = promises.promisify(fs10.readFile);
31088
31088
  function openZip(options) {
31089
31089
  if (options.path) {
31090
31090
  return readFile(options.path).then(zipfile.openArrayBuffer);
@@ -31401,8 +31401,8 @@ var require_cjs = __commonJS({
31401
31401
  return module3 = {
31402
31402
  path: basedir,
31403
31403
  exports: {},
31404
- require: function(path8, base) {
31405
- return commonjsRequire(path8, base === void 0 || base === null ? module3.path : base);
31404
+ require: function(path9, base) {
31405
+ return commonjsRequire(path9, base === void 0 || base === null ? module3.path : base);
31406
31406
  }
31407
31407
  }, fn(module3, module3.exports), module3.exports;
31408
31408
  }
@@ -31809,9 +31809,9 @@ var require_cjs = __commonJS({
31809
31809
  }
31810
31810
  return o;
31811
31811
  }
31812
- var fs9;
31812
+ var fs10;
31813
31813
  function get_fs() {
31814
- return fs9 || (fs9 = require$$0__default["default"]);
31814
+ return fs10 || (fs10 = require$$0__default["default"]);
31815
31815
  }
31816
31816
  function parse2(file, options) {
31817
31817
  if (file[0] == 80 && file[1] == 75) return parse_zip(file, options);
@@ -32096,7 +32096,7 @@ var require_cjs = __commonJS({
32096
32096
  }
32097
32097
  function read_file(filename2, options) {
32098
32098
  get_fs();
32099
- return parse2(fs9.readFileSync(filename2), options);
32099
+ return parse2(fs10.readFileSync(filename2), options);
32100
32100
  }
32101
32101
  function read(blob, options) {
32102
32102
  switch (options && options.type || "base64") {
@@ -32362,7 +32362,7 @@ var require_cjs = __commonJS({
32362
32362
  while (o.l < o.length) o.write_shift(1, 0);
32363
32363
  return o;
32364
32364
  }
32365
- function find2(cfb2, path8) {
32365
+ function find2(cfb2, path9) {
32366
32366
  var UCFullPaths = cfb2.FullPaths.map(function(x2) {
32367
32367
  return x2.toUpperCase();
32368
32368
  });
@@ -32371,11 +32371,11 @@ var require_cjs = __commonJS({
32371
32371
  return y[y.length - (x2.slice(-1) == "/" ? 2 : 1)];
32372
32372
  });
32373
32373
  var k = false;
32374
- if (path8.charCodeAt(0) === 47) {
32374
+ if (path9.charCodeAt(0) === 47) {
32375
32375
  k = true;
32376
- path8 = UCFullPaths[0].slice(0, -1) + path8;
32377
- } else k = path8.indexOf("/") !== -1;
32378
- var UCPath = path8.toUpperCase();
32376
+ path9 = UCFullPaths[0].slice(0, -1) + path9;
32377
+ } else k = path9.indexOf("/") !== -1;
32378
+ var UCPath = path9.toUpperCase();
32379
32379
  var w = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
32380
32380
  if (w !== -1) return cfb2.FileIndex[w];
32381
32381
  var m = !UCPath.match(chr1);
@@ -32411,7 +32411,7 @@ var require_cjs = __commonJS({
32411
32411
  function write_file(cfb2, filename2, options) {
32412
32412
  get_fs();
32413
32413
  var o = _write(cfb2, options);
32414
- fs9.writeFileSync(filename2, o);
32414
+ fs10.writeFileSync(filename2, o);
32415
32415
  }
32416
32416
  function a2s(o) {
32417
32417
  var out = new Array(o.length);
@@ -32423,7 +32423,7 @@ var require_cjs = __commonJS({
32423
32423
  switch (options && options.type || "buffer") {
32424
32424
  case "file":
32425
32425
  get_fs();
32426
- fs9.writeFileSync(options.filename, o);
32426
+ fs10.writeFileSync(options.filename, o);
32427
32427
  return o;
32428
32428
  case "binary":
32429
32429
  return typeof o == "string" ? o : a2s(o);
@@ -37848,8 +37848,8 @@ var require_cjs = __commonJS({
37848
37848
  reader.readUInt16();
37849
37849
  var id2 = reader.readUInt16();
37850
37850
  var extension = reader.readString();
37851
- var path8 = "Root Entry/BinData/BIN".concat("".concat(id2.toString(16).toUpperCase()).padStart(4, "0"), ".").concat(extension);
37852
- var payload = cfb.find(this.container, path8).content;
37851
+ var path9 = "Root Entry/BinData/BIN".concat("".concat(id2.toString(16).toUpperCase()).padStart(4, "0"), ".").concat(extension);
37852
+ var payload = cfb.find(this.container, path9).content;
37853
37853
  this.result.binData.push(new BinData(extension, pako_1.inflate(payload, {
37854
37854
  windowBits: -15
37855
37855
  })));
@@ -39982,33 +39982,33 @@ function renderRichText({
39982
39982
  container.append(fragment);
39983
39983
  }
39984
39984
  function makePathFromDrawOPS(data) {
39985
- const path8 = new Path2D();
39985
+ const path9 = new Path2D();
39986
39986
  if (!data) {
39987
- return path8;
39987
+ return path9;
39988
39988
  }
39989
39989
  for (let i2 = 0, ii = data.length; i2 < ii; ) {
39990
39990
  switch (data[i2++]) {
39991
39991
  case DrawOPS.moveTo:
39992
- path8.moveTo(data[i2++], data[i2++]);
39992
+ path9.moveTo(data[i2++], data[i2++]);
39993
39993
  break;
39994
39994
  case DrawOPS.lineTo:
39995
- path8.lineTo(data[i2++], data[i2++]);
39995
+ path9.lineTo(data[i2++], data[i2++]);
39996
39996
  break;
39997
39997
  case DrawOPS.curveTo:
39998
- path8.bezierCurveTo(data[i2++], data[i2++], data[i2++], data[i2++], data[i2++], data[i2++]);
39998
+ path9.bezierCurveTo(data[i2++], data[i2++], data[i2++], data[i2++], data[i2++], data[i2++]);
39999
39999
  break;
40000
40000
  case DrawOPS.quadraticCurveTo:
40001
- path8.quadraticCurveTo(data[i2++], data[i2++], data[i2++], data[i2++]);
40001
+ path9.quadraticCurveTo(data[i2++], data[i2++], data[i2++], data[i2++]);
40002
40002
  break;
40003
40003
  case DrawOPS.closePath:
40004
- path8.closePath();
40004
+ path9.closePath();
40005
40005
  break;
40006
40006
  default:
40007
40007
  warn(`Unrecognized drawing path operator: ${data[i2 - 1]}`);
40008
40008
  break;
40009
40009
  }
40010
40010
  }
40011
- return path8;
40011
+ return path9;
40012
40012
  }
40013
40013
  function bindEvents(obj2, element, names) {
40014
40014
  for (const name of names) {
@@ -40116,8 +40116,8 @@ function blend(fg, bg, alpha) {
40116
40116
  return Math.round(alpha * fg + (1 - alpha) * bg);
40117
40117
  }
40118
40118
  async function node_utils_fetchData(url) {
40119
- const fs9 = process.getBuiltinModule("fs/promises");
40120
- const data = await fs9.readFile(url);
40119
+ const fs10 = process.getBuiltinModule("fs/promises");
40120
+ const data = await fs10.readFile(url);
40121
40121
  return new Uint8Array(data);
40122
40122
  }
40123
40123
  function initGPU() {
@@ -40746,11 +40746,11 @@ function network_getArrayBuffer(val) {
40746
40746
  return typeof val !== "string" ? val : stringToBytes(val).buffer;
40747
40747
  }
40748
40748
  function getReadableStream(url, opts = null) {
40749
- const fs9 = process.getBuiltinModule("fs");
40749
+ const fs10 = process.getBuiltinModule("fs");
40750
40750
  const {
40751
40751
  Readable
40752
40752
  } = process.getBuiltinModule("stream");
40753
- const readStream = fs9.createReadStream(url, opts);
40753
+ const readStream = fs10.createReadStream(url, opts);
40754
40754
  return Readable.toWeb(readStream);
40755
40755
  }
40756
40756
  function getNetworkStream(url) {
@@ -52554,11 +52554,11 @@ var init_pdf = __esm({
52554
52554
  } catch (ex) {
52555
52555
  warn(`getPathGenerator - ignoring character: "${ex}".`);
52556
52556
  }
52557
- const path8 = makePathFromDrawOPS(cmds?.path);
52557
+ const path9 = makePathFromDrawOPS(cmds?.path);
52558
52558
  if (!this.fontExtraProperties) {
52559
52559
  objs.delete(objId);
52560
52560
  }
52561
- return this.compiledGlyphs[character] = path8;
52561
+ return this.compiledGlyphs[character] = path9;
52562
52562
  }
52563
52563
  get black() {
52564
52564
  return this.#fontData.black;
@@ -54456,7 +54456,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
54456
54456
  Util.singularValueDecompose2dScale(this.baseTransform, scale);
54457
54457
  return [matrixScaleX * scale[0], matrixScaleY * scale[1]];
54458
54458
  }
54459
- drawPattern(owner, path8, useEOFill = false, [n, m], opIdx) {
54459
+ drawPattern(owner, path9, useEOFill = false, [n, m], opIdx) {
54460
54460
  const [x0, y0, x1, y1] = this.bbox;
54461
54461
  const dependencyTracker = owner.dependencyTracker;
54462
54462
  if (dependencyTracker) {
@@ -54464,9 +54464,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
54464
54464
  }
54465
54465
  owner.save();
54466
54466
  if (useEOFill) {
54467
- owner.ctx.clip(path8, "evenodd");
54467
+ owner.ctx.clip(path9, "evenodd");
54468
54468
  } else {
54469
- owner.ctx.clip(path8);
54469
+ owner.ctx.clip(path9);
54470
54470
  }
54471
54471
  owner.ctx.setTransform(...this.patternBaseMatrix);
54472
54472
  owner.ctx.translate(n * this.xstep, m * this.ystep);
@@ -55561,15 +55561,15 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55561
55561
  if (hasInnerCutout && maskX0 === layerOffsetX && maskY0 === layerOffsetY && maskX1 === layerOffsetX + layerWidth && maskY1 === layerOffsetY + layerHeight) {
55562
55562
  return;
55563
55563
  }
55564
- const path8 = new Path2D();
55565
- path8.rect(layerOffsetX, layerOffsetY, layerWidth, layerHeight);
55564
+ const path9 = new Path2D();
55565
+ path9.rect(layerOffsetX, layerOffsetY, layerWidth, layerHeight);
55566
55566
  if (hasInnerCutout) {
55567
- path8.rect(maskX0, maskY0, maskX1 - maskX0, maskY1 - maskY0);
55567
+ path9.rect(maskX0, maskY0, maskX1 - maskX0, maskY1 - maskY0);
55568
55568
  }
55569
55569
  layerCtx.save();
55570
55570
  layerCtx.globalAlpha = alpha / 255;
55571
55571
  layerCtx.setTransform(1, 0, 0, 1, 0, 0);
55572
- layerCtx.clip(path8, "evenodd");
55572
+ layerCtx.clip(path9, "evenodd");
55573
55573
  layerCtx.globalCompositeOperation = "destination-in";
55574
55574
  layerCtx.fillStyle = "#000000";
55575
55575
  layerCtx.fillRect(layerOffsetX, layerOffsetY, layerWidth, layerHeight);
@@ -55627,21 +55627,21 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55627
55627
  this._cachedGetSinglePixelWidth = null;
55628
55628
  }
55629
55629
  constructPath(opIdx, op, data, minMax) {
55630
- let [path8] = data;
55630
+ let [path9] = data;
55631
55631
  if (!minMax) {
55632
- path8 ||= data[0] = new Path2D();
55632
+ path9 ||= data[0] = new Path2D();
55633
55633
  if (op !== OPS.stroke && op !== OPS.closeStroke) {
55634
55634
  this.current.tilingPatternDims = null;
55635
55635
  }
55636
- this[op](opIdx, path8);
55636
+ this[op](opIdx, path9);
55637
55637
  return;
55638
55638
  }
55639
55639
  if (this.dependencyTracker !== null) {
55640
55640
  const outerExtraSize = op === OPS.stroke ? this.current.lineWidth / 2 : 0;
55641
55641
  this.dependencyTracker.resetBBox(opIdx).recordBBox(opIdx, this.ctx, minMax[0] - outerExtraSize, minMax[2] + outerExtraSize, minMax[1] - outerExtraSize, minMax[3] + outerExtraSize).recordDependencies(opIdx, ["transform"]);
55642
55642
  }
55643
- if (!(path8 instanceof Path2D)) {
55644
- path8 = data[0] = makePathFromDrawOPS(path8);
55643
+ if (!(path9 instanceof Path2D)) {
55644
+ path9 = data[0] = makePathFromDrawOPS(path9);
55645
55645
  }
55646
55646
  Util.axialAlignedBoundingBox(minMax, getCurrentTransform(this.ctx), this.current.minMax);
55647
55647
  const tilingDims = this.current.tilingPatternDims;
@@ -55653,13 +55653,13 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55653
55653
  this.current.fillColor.updatePatternDims(clippedBBox, tilingDims);
55654
55654
  }
55655
55655
  }
55656
- this[op](opIdx, path8);
55656
+ this[op](opIdx, path9);
55657
55657
  this._pathStartIdx = opIdx;
55658
55658
  }
55659
55659
  closePath(opIdx) {
55660
55660
  this.ctx.closePath();
55661
55661
  }
55662
- stroke(opIdx, path8, consumePath = true) {
55662
+ stroke(opIdx, path9, consumePath = true) {
55663
55663
  const started = consumePath && this.#beginKnockoutElement(this.current.strokeAlpha);
55664
55664
  const ctx = this.ctx;
55665
55665
  const strokeColor = this.current.strokeColor;
@@ -55671,26 +55671,26 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55671
55671
  ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx);
55672
55672
  if (baseTransform) {
55673
55673
  const newPath = new Path2D();
55674
- newPath.addPath(path8, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
55675
- path8 = newPath;
55674
+ newPath.addPath(path9, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
55675
+ path9 = newPath;
55676
55676
  }
55677
- this.rescaleAndStroke(path8, false);
55677
+ this.rescaleAndStroke(path9, false);
55678
55678
  ctx.restore();
55679
55679
  } else {
55680
- this.rescaleAndStroke(path8, true);
55680
+ this.rescaleAndStroke(path9, true);
55681
55681
  }
55682
55682
  }
55683
55683
  this.dependencyTracker?.recordDependencies(opIdx, Dependencies.stroke);
55684
55684
  if (consumePath) {
55685
- this.consumePath(opIdx, path8, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx)));
55685
+ this.consumePath(opIdx, path9, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx)));
55686
55686
  }
55687
55687
  ctx.globalAlpha = this.current.fillAlpha;
55688
55688
  this.#endKnockoutElement(started);
55689
55689
  }
55690
- closeStroke(opIdx, path8) {
55691
- this.stroke(opIdx, path8);
55690
+ closeStroke(opIdx, path9) {
55691
+ this.stroke(opIdx, path9);
55692
55692
  }
55693
- fill(opIdx, path8, consumePath = true) {
55693
+ fill(opIdx, path9, consumePath = true) {
55694
55694
  const started = consumePath && this.#beginKnockoutElement(this.current.fillAlpha);
55695
55695
  const ctx = this.ctx;
55696
55696
  const fillColor = this.current.fillColor;
@@ -55702,10 +55702,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55702
55702
  const dims = this.current.tilingPatternDims;
55703
55703
  const tileIdx = dims && fillColor.canSkipPatternCanvas(dims);
55704
55704
  if (tileIdx) {
55705
- fillColor.drawPattern(this, path8, this.pendingEOFill, tileIdx, opIdx);
55705
+ fillColor.drawPattern(this, path9, this.pendingEOFill, tileIdx, opIdx);
55706
55706
  this.pendingEOFill = false;
55707
55707
  if (consumePath) {
55708
- this.consumePath(opIdx, path8, intersect);
55708
+ this.consumePath(opIdx, path9, intersect);
55709
55709
  }
55710
55710
  this.current.tilingPatternDims = null;
55711
55711
  this.#endKnockoutElement(started);
@@ -55717,17 +55717,17 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55717
55717
  ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx);
55718
55718
  if (baseTransform) {
55719
55719
  const newPath = new Path2D();
55720
- newPath.addPath(path8, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
55721
- path8 = newPath;
55720
+ newPath.addPath(path9, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
55721
+ path9 = newPath;
55722
55722
  }
55723
55723
  needRestore = true;
55724
55724
  }
55725
55725
  if (this.contentVisible && intersect !== null) {
55726
55726
  if (this.pendingEOFill) {
55727
- ctx.fill(path8, "evenodd");
55727
+ ctx.fill(path9, "evenodd");
55728
55728
  this.pendingEOFill = false;
55729
55729
  } else {
55730
- ctx.fill(path8);
55730
+ ctx.fill(path9);
55731
55731
  }
55732
55732
  }
55733
55733
  if (needRestore) {
@@ -55735,38 +55735,38 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55735
55735
  this.dependencyTracker?.restore(opIdx);
55736
55736
  }
55737
55737
  if (consumePath) {
55738
- this.consumePath(opIdx, path8, intersect);
55738
+ this.consumePath(opIdx, path9, intersect);
55739
55739
  }
55740
55740
  this.#endKnockoutElement(started);
55741
55741
  }
55742
- eoFill(opIdx, path8) {
55742
+ eoFill(opIdx, path9) {
55743
55743
  this.pendingEOFill = true;
55744
- this.fill(opIdx, path8);
55744
+ this.fill(opIdx, path9);
55745
55745
  }
55746
- fillStroke(opIdx, path8) {
55746
+ fillStroke(opIdx, path9) {
55747
55747
  const started = this.#beginKnockoutElement(Math.min(this.current.fillAlpha, this.current.strokeAlpha));
55748
- this.fill(opIdx, path8, false);
55749
- this.stroke(opIdx, path8, false);
55750
- this.consumePath(opIdx, path8);
55748
+ this.fill(opIdx, path9, false);
55749
+ this.stroke(opIdx, path9, false);
55750
+ this.consumePath(opIdx, path9);
55751
55751
  this.#endKnockoutElement(started);
55752
55752
  }
55753
- eoFillStroke(opIdx, path8) {
55753
+ eoFillStroke(opIdx, path9) {
55754
55754
  this.pendingEOFill = true;
55755
- this.fillStroke(opIdx, path8);
55755
+ this.fillStroke(opIdx, path9);
55756
55756
  }
55757
- closeFillStroke(opIdx, path8) {
55758
- this.fillStroke(opIdx, path8);
55757
+ closeFillStroke(opIdx, path9) {
55758
+ this.fillStroke(opIdx, path9);
55759
55759
  }
55760
- closeEOFillStroke(opIdx, path8) {
55760
+ closeEOFillStroke(opIdx, path9) {
55761
55761
  this.pendingEOFill = true;
55762
- this.fillStroke(opIdx, path8);
55762
+ this.fillStroke(opIdx, path9);
55763
55763
  }
55764
- endPath(opIdx, path8) {
55765
- this.consumePath(opIdx, path8);
55764
+ endPath(opIdx, path9) {
55765
+ this.consumePath(opIdx, path9);
55766
55766
  }
55767
- rawFillPath(opIdx, path8) {
55767
+ rawFillPath(opIdx, path9) {
55768
55768
  const started = this.#beginKnockoutElement(this.current.fillAlpha);
55769
- this.ctx.fill(path8);
55769
+ this.ctx.fill(path9);
55770
55770
  this.dependencyTracker?.recordDependencies(opIdx, Dependencies.rawFillPath).recordOperation(opIdx);
55771
55771
  this.#endKnockoutElement(started);
55772
55772
  }
@@ -55805,12 +55805,12 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55805
55805
  x: x2,
55806
55806
  y,
55807
55807
  fontSize,
55808
- path: path8
55808
+ path: path9
55809
55809
  } of paths) {
55810
- if (!path8) {
55810
+ if (!path9) {
55811
55811
  continue;
55812
55812
  }
55813
- newPath.addPath(path8, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x2, y).scale(fontSize, -fontSize));
55813
+ newPath.addPath(path9, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x2, y).scale(fontSize, -fontSize));
55814
55814
  }
55815
55815
  ctx.clip(newPath);
55816
55816
  }
@@ -55903,9 +55903,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55903
55903
  this.moveText(opIdx, 0, this.current.leading);
55904
55904
  this.dependencyTracker?.recordIncrementalData("moveText", this.dependencyTracker.getSimpleIndex("leading") ?? opIdx);
55905
55905
  }
55906
- #getScaledPath(path8, currentTransform, transform) {
55906
+ #getScaledPath(path9, currentTransform, transform) {
55907
55907
  const newPath = new Path2D();
55908
- newPath.addPath(path8, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform));
55908
+ newPath.addPath(path9, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform));
55909
55909
  return newPath;
55910
55910
  }
55911
55911
  paintChar(opIdx, character, x2, y, patternFillTransform, patternStrokeTransform) {
@@ -55918,11 +55918,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55918
55918
  const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG);
55919
55919
  const patternFill = current.patternFill && !font.missingFile;
55920
55920
  const patternStroke = current.patternStroke && !font.missingFile;
55921
- let path8;
55921
+ let path9;
55922
55922
  if ((font.disableFontFace || isAddToPathSet || patternFill || patternStroke) && !font.missingFile) {
55923
- path8 = font.getPathGenerator(this.commonObjs, character);
55923
+ path9 = font.getPathGenerator(this.commonObjs, character);
55924
55924
  }
55925
- if (path8 && (font.disableFontFace || patternFill || patternStroke)) {
55925
+ if (path9 && (font.disableFontFace || patternFill || patternStroke)) {
55926
55926
  ctx.save();
55927
55927
  ctx.translate(x2, y);
55928
55928
  ctx.scale(fontSize, -fontSize);
@@ -55932,10 +55932,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55932
55932
  if (patternFillTransform) {
55933
55933
  currentTransform = ctx.getTransform();
55934
55934
  ctx.setTransform(...patternFillTransform);
55935
- const scaledPath = this.#getScaledPath(path8, currentTransform, patternFillTransform);
55935
+ const scaledPath = this.#getScaledPath(path9, currentTransform, patternFillTransform);
55936
55936
  ctx.fill(scaledPath);
55937
55937
  } else {
55938
- ctx.fill(path8);
55938
+ ctx.fill(path9);
55939
55939
  }
55940
55940
  }
55941
55941
  if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
@@ -55952,10 +55952,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55952
55952
  const transf = Util.transform([a, b, c, d, 0, 0], invPatternTransform);
55953
55953
  Util.singularValueDecompose2dScale(transf, XY);
55954
55954
  ctx.lineWidth *= Math.max(XY[0], XY[1]) / fontSize;
55955
- ctx.stroke(this.#getScaledPath(path8, currentTransform, patternStrokeTransform));
55955
+ ctx.stroke(this.#getScaledPath(path9, currentTransform, patternStrokeTransform));
55956
55956
  } else {
55957
55957
  ctx.lineWidth /= fontSize;
55958
- ctx.stroke(path8);
55958
+ ctx.stroke(path9);
55959
55959
  }
55960
55960
  }
55961
55961
  ctx.restore();
@@ -55978,7 +55978,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
55978
55978
  x: x2,
55979
55979
  y,
55980
55980
  fontSize,
55981
- path: path8
55981
+ path: path9
55982
55982
  });
55983
55983
  this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x2, y);
55984
55984
  }
@@ -56374,9 +56374,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
56374
56374
  const [x0, y0, x1, y1] = group2.bbox;
56375
56375
  clip.rect(x0, y0, x1 - x0, y1 - y0);
56376
56376
  if (group2.matrix) {
56377
- const path8 = new Path2D();
56378
- path8.addPath(clip, new DOMMatrix(group2.matrix));
56379
- clip = path8;
56377
+ const path9 = new Path2D();
56378
+ path9.addPath(clip, new DOMMatrix(group2.matrix));
56379
+ clip = path9;
56380
56380
  }
56381
56381
  currentCtx.clip(clip);
56382
56382
  }
@@ -56430,9 +56430,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
56430
56430
  const [x0, y0, x1, y1] = group2.bbox;
56431
56431
  clip.rect(x0, y0, x1 - x0, y1 - y0);
56432
56432
  if (group2.matrix) {
56433
- const path8 = new Path2D();
56434
- path8.addPath(clip, new DOMMatrix(group2.matrix));
56435
- clip = path8;
56433
+ const path9 = new Path2D();
56434
+ path9.addPath(clip, new DOMMatrix(group2.matrix));
56435
+ clip = path9;
56436
56436
  }
56437
56437
  groupCtx.clip(clip);
56438
56438
  }
@@ -56911,7 +56911,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
56911
56911
  }
56912
56912
  endCompat(opIdx) {
56913
56913
  }
56914
- consumePath(opIdx, path8, clipBox) {
56914
+ consumePath(opIdx, path9, clipBox) {
56915
56915
  const isEmpty2 = this.current.isEmptyClip();
56916
56916
  if (this.pendingClip) {
56917
56917
  this.current.updateClipFromPath();
@@ -56923,9 +56923,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
56923
56923
  if (this.pendingClip) {
56924
56924
  if (!isEmpty2) {
56925
56925
  if (this.pendingClip === EO_CLIP) {
56926
- ctx.clip(path8, "evenodd");
56926
+ ctx.clip(path9, "evenodd");
56927
56927
  } else {
56928
- ctx.clip(path8);
56928
+ ctx.clip(path9);
56929
56929
  }
56930
56930
  }
56931
56931
  this.pendingClip = null;
@@ -56998,7 +56998,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
56998
56998
  }
56999
56999
  return this._cachedScaleForStroking;
57000
57000
  }
57001
- rescaleAndStroke(path8, saveRestore) {
57001
+ rescaleAndStroke(path9, saveRestore) {
57002
57002
  const {
57003
57003
  ctx,
57004
57004
  current: {
@@ -57008,7 +57008,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
57008
57008
  const [scaleX, scaleY] = this.getScaleForStroking();
57009
57009
  if (scaleX === scaleY) {
57010
57010
  ctx.lineWidth = (lineWidth || 1) * scaleX;
57011
- ctx.stroke(path8);
57011
+ ctx.stroke(path9);
57012
57012
  return;
57013
57013
  }
57014
57014
  const dashes = ctx.getLineDash();
@@ -57019,7 +57019,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
57019
57019
  SCALE_MATRIX.a = 1 / scaleX;
57020
57020
  SCALE_MATRIX.d = 1 / scaleY;
57021
57021
  const newPath = new Path2D();
57022
- newPath.addPath(path8, SCALE_MATRIX);
57022
+ newPath.addPath(path9, SCALE_MATRIX);
57023
57023
  if (dashes.length > 0) {
57024
57024
  const scale = Math.max(scaleX, scaleY);
57025
57025
  ctx.setLineDash(dashes.map((x2) => x2 / scale));
@@ -57747,8 +57747,8 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
57747
57747
  url
57748
57748
  } = stream._source;
57749
57749
  this._isStreamingSupported = !disableStream;
57750
- const fs9 = process.getBuiltinModule("fs/promises");
57751
- fs9.lstat(url).then((stat) => {
57750
+ const fs10 = process.getBuiltinModule("fs/promises");
57751
+ fs10.lstat(url).then((stat) => {
57752
57752
  const readableStream = getReadableStream(url);
57753
57753
  this._reader = readableStream.getReader();
57754
57754
  const {
@@ -67942,11 +67942,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
67942
67942
  };
67943
67943
  ContourDrawOutline = class extends InkDrawOutline {
67944
67944
  toSVGPath() {
67945
- let path8 = super.toSVGPath();
67946
- if (!path8.endsWith("Z")) {
67947
- path8 += "Z";
67945
+ let path9 = super.toSVGPath();
67946
+ if (!path9.endsWith("Z")) {
67947
+ path9 += "Z";
67948
67948
  }
67949
- return path8;
67949
+ return path9;
67950
67950
  }
67951
67951
  };
67952
67952
  es_uint8_array_from_base64 = __webpack_require__(5213);
@@ -70651,7 +70651,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
70651
70651
  }
70652
70652
  const drawLayer = textLayerData.drawLayer;
70653
70653
  let div = textLayerData.selectionDiv;
70654
- let path8 = textLayerData.path;
70654
+ let path9 = textLayerData.path;
70655
70655
  if (!div) {
70656
70656
  const clipPathId = `clip_selection_${_DrawLayer.#selectionId++}`;
70657
70657
  div = document.createElement("div");
@@ -70670,18 +70670,18 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
70670
70670
  const clipPath = _DrawLayer._svgFactory.createElement("clipPath");
70671
70671
  clipPath.setAttribute("id", clipPathId);
70672
70672
  clipPath.setAttribute("clipPathUnits", "objectBoundingBox");
70673
- path8 = _DrawLayer._svgFactory.createElement("path");
70674
- clipPath.append(path8);
70673
+ path9 = _DrawLayer._svgFactory.createElement("path");
70674
+ clipPath.append(path9);
70675
70675
  svg.append(clipPath);
70676
70676
  div.append(svg);
70677
- textLayerData.path = path8;
70677
+ textLayerData.path = path9;
70678
70678
  textLayerData.selectionDiv = div;
70679
70679
  }
70680
70680
  if (!div.parentNode && drawLayer.#parent) {
70681
70681
  drawLayer.#parent.append(div);
70682
70682
  this.#selections.add(div);
70683
70683
  }
70684
- path8.setAttribute("d", boxes.join(" "));
70684
+ path9.setAttribute("d", boxes.join(" "));
70685
70685
  }
70686
70686
  }
70687
70687
  static get _svgFactory() {
@@ -70728,13 +70728,13 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
70728
70728
  const root2 = this.#createSVG();
70729
70729
  const defs = _DrawLayer._svgFactory.createElement("defs");
70730
70730
  root2.append(defs);
70731
- const path8 = _DrawLayer._svgFactory.createElement("path");
70732
- defs.append(path8);
70731
+ const path9 = _DrawLayer._svgFactory.createElement("path");
70732
+ defs.append(path9);
70733
70733
  const pathId = `path_${id2}`;
70734
- path8.setAttribute("id", pathId);
70735
- path8.setAttribute("vector-effect", "non-scaling-stroke");
70734
+ path9.setAttribute("id", pathId);
70735
+ path9.setAttribute("vector-effect", "non-scaling-stroke");
70736
70736
  if (isPathUpdatable) {
70737
- this.#toUpdate.set(id2, path8);
70737
+ this.#toUpdate.set(id2, path9);
70738
70738
  }
70739
70739
  const clipPathId = hasClip ? this.#createClipPath(defs, pathId) : null;
70740
70740
  const use = _DrawLayer._svgFactory.createElement("use");
@@ -70752,11 +70752,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
70752
70752
  const root2 = this.#createSVG();
70753
70753
  const defs = _DrawLayer._svgFactory.createElement("defs");
70754
70754
  root2.append(defs);
70755
- const path8 = _DrawLayer._svgFactory.createElement("path");
70756
- defs.append(path8);
70755
+ const path9 = _DrawLayer._svgFactory.createElement("path");
70756
+ defs.append(path9);
70757
70757
  const pathId = `path_${id2}`;
70758
- path8.setAttribute("id", pathId);
70759
- path8.setAttribute("vector-effect", "non-scaling-stroke");
70758
+ path9.setAttribute("id", pathId);
70759
+ path9.setAttribute("vector-effect", "non-scaling-stroke");
70760
70760
  let maskId;
70761
70761
  if (mustRemoveSelfIntersections) {
70762
70762
  const mask = _DrawLayer._svgFactory.createElement("mask");
@@ -70803,7 +70803,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
70803
70803
  root: root2,
70804
70804
  bbox,
70805
70805
  rootClass,
70806
- path: path8
70806
+ path: path9
70807
70807
  } = properties;
70808
70808
  const element = typeof elementOrId === "number" ? this.#mapping.get(elementOrId) : elementOrId;
70809
70809
  if (!element) {
@@ -70823,10 +70823,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
70823
70823
  classList.toggle(className, value);
70824
70824
  }
70825
70825
  }
70826
- if (path8) {
70826
+ if (path9) {
70827
70827
  const defs = element.firstElementChild;
70828
70828
  const pathElement = defs.firstElementChild;
70829
- this.#updateProperties(pathElement, path8);
70829
+ this.#updateProperties(pathElement, path9);
70830
70830
  }
70831
70831
  }
70832
70832
  updateParent(id2, layer) {
@@ -71040,7 +71040,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
71040
71040
  });
71041
71041
 
71042
71042
  // scripts/doctor.ts
71043
- import fs8 from "node:fs";
71043
+ import fs9 from "node:fs";
71044
71044
 
71045
71045
  // packages/db/src/paths.ts
71046
71046
  import os from "node:os";
@@ -71065,6 +71065,11 @@ function getInboxDir() {
71065
71065
  fs.mkdirSync(dir, { recursive: true });
71066
71066
  return dir;
71067
71067
  }
71068
+ function getBackupsDir() {
71069
+ const dir = path.join(getDataDir(), "backups");
71070
+ fs.mkdirSync(dir, { recursive: true });
71071
+ return dir;
71072
+ }
71068
71073
 
71069
71074
  // packages/db/src/runtime.ts
71070
71075
  import fs2 from "node:fs";
@@ -71090,6 +71095,8 @@ function isProcessAlive(pid) {
71090
71095
  }
71091
71096
 
71092
71097
  // packages/db/src/connection.ts
71098
+ import fs3 from "node:fs";
71099
+ import path3 from "node:path";
71093
71100
  import { DatabaseSync } from "node:sqlite";
71094
71101
 
71095
71102
  // packages/db/src/schema.ts
@@ -71408,33 +71415,155 @@ var MIGRATIONS = [
71408
71415
  // idempotent. Feeds next-round prep and consistency (lessons carry into later interviews).
71409
71416
  `
71410
71417
  ALTER TABLE interview_preps ADD COLUMN debrief TEXT NOT NULL DEFAULT '{}';
71418
+ `,
71419
+ // v7 — offer comparison (한국 총보상 필드) + company reputation research field on jobs.
71420
+ // offers: one per job (UNIQUE job_id, mirrors interview_preps). Cash pieces are numeric (만원) so
71421
+ // the server can deterministically sum an annual estimate; equity/other stay free-text (comp is
71422
+ // messy). ai_score/verdict are written by the connected AI at save time (no LLM here) and rendered
71423
+ // by the dashboard, so it never needs to call an LLM. jobs.reputation: AI-researched company
71424
+ // reputation summary (same pattern as company_overview) — feeds offer verdicts + offer-evaluation.
71425
+ `
71426
+ CREATE TABLE IF NOT EXISTS offers (
71427
+ id TEXT PRIMARY KEY,
71428
+ job_id TEXT NOT NULL UNIQUE REFERENCES jobs(id) ON DELETE CASCADE,
71429
+ base_salary INTEGER,
71430
+ bonus_amount INTEGER,
71431
+ welfare_amount INTEGER,
71432
+ signing_amount INTEGER,
71433
+ equity_note TEXT,
71434
+ comp_note TEXT,
71435
+ work_arrangement TEXT,
71436
+ contract_type TEXT,
71437
+ accept_deadline TEXT,
71438
+ ai_score INTEGER,
71439
+ verdict TEXT,
71440
+ notes TEXT,
71441
+ created_at TEXT NOT NULL,
71442
+ updated_at TEXT NOT NULL
71443
+ );
71444
+ CREATE INDEX IF NOT EXISTS idx_offers_job ON offers(job_id);
71445
+ ALTER TABLE jobs ADD COLUMN reputation TEXT;
71446
+ `,
71447
+ // v8 — offer lifecycle status. 지원 상태(전형 깔때기 = 회사의 프로세스)와 직교하는
71448
+ // "내 응답" 축: open(진행)/accepted(수락)/declined(거절). declined여도 행을 지우지
71449
+ // 않는다 — 조건(연봉·처우) 히스토리는 다음 협상의 자산이라, 대시보드가 접힌
71450
+ // '지난 오퍼'로 강등해 보여줄 뿐이다. Additive ALTER (runner가 멱등 처리).
71451
+ `
71452
+ ALTER TABLE offers ADD COLUMN offer_status TEXT NOT NULL DEFAULT 'open';
71453
+ `,
71454
+ // v9 — 한국 문항형 자소서: 회사가 지정한 문항·글자수를 공고에 구조화 보관. 한국 자소서는
71455
+ // 회사별 문항형(항목·글자수 제한)이 지배적이라, AI가 문항별로 답변을 쓰고 글자수를 점검할 때
71456
+ // 참조할 원본을 공고 옆에 둔다(별도 엔티티가 아니라 job 컨텍스트에 함께 실린다). JSON 배열을
71457
+ // TEXT에 저장(다른 JSON 필드와 동일). Additive ALTER (runner가 멱등 처리).
71458
+ `
71459
+ ALTER TABLE jobs ADD COLUMN questions TEXT NOT NULL DEFAULT '[]';
71460
+ `,
71461
+ // v10 — 탈락 회고: 어느 전형 단계에서 떨어졌는지 자가 기록. 탈락 사유를 고지하는 기업은 6%뿐이고
71462
+ // 지원자 83%가 알고 싶어하므로, 이 자가 기록이 "서류는 붙는데 면접에서 떨어진다" 같은 패턴을 로컬
71463
+ // 데이터로 드러내는 유일한 신호다. 탈락이 아니거나 미기록이면 NULL. Additive ALTER (runner가 멱등 처리).
71464
+ `
71465
+ ALTER TABLE applications ADD COLUMN rejected_stage TEXT;
71466
+ `,
71467
+ // v11 — 전형 일정: 서류마감→인적성·AI역검→1·2차 면접→발표로 이어지는 단계별 날짜를 공고에
71468
+ // 구조화 보관. 일정을 jobs에 두는 이유: 서류마감(deadline)이 이미 jobs에 있고, save_job_posting
71469
+ // 업서트를 그대로 재사용해 도구 신설 없이 갱신할 수 있다. 면접초대 메일·발표 공지를 사용자가
71470
+ // 붙여넣으면 AI가 날짜를 추출해 저장하고, 서버가 D-day·같은 날짜 충돌을 결정론으로 계산한다.
71471
+ // JSON 배열을 TEXT에 저장(다른 JSON 필드와 동일). Additive ALTER (runner가 멱등 처리).
71472
+ `
71473
+ ALTER TABLE jobs ADD COLUMN schedule TEXT NOT NULL DEFAULT '[]';
71411
71474
  `
71412
71475
  ];
71413
71476
  function migrate(db) {
71414
71477
  db.exec(`CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);`);
71415
71478
  const row = db.prepare(`SELECT value FROM _meta WHERE key = 'schema_version'`).get();
71416
71479
  const from = row ? Number(row.value) : 0;
71480
+ if (from >= MIGRATIONS.length) return { from, to: from };
71417
71481
  for (let v = from; v < MIGRATIONS.length; v++) {
71418
- db.exec(MIGRATIONS[v]);
71482
+ applyMigration(db, v);
71419
71483
  }
71420
- const to = MIGRATIONS.length;
71421
- if (to !== from) {
71422
- db.prepare(
71423
- `INSERT INTO _meta (key, value) VALUES ('schema_version', ?)
71424
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`
71425
- ).run(String(to));
71484
+ return { from, to: MIGRATIONS.length };
71485
+ }
71486
+ function stampVersion(db, version2) {
71487
+ db.prepare(
71488
+ `INSERT INTO _meta (key, value) VALUES ('schema_version', ?)
71489
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`
71490
+ ).run(String(version2));
71491
+ }
71492
+ function columnExists(db, table, column) {
71493
+ const rows = db.prepare(`PRAGMA table_info("${table.replaceAll('"', '""')}")`).all();
71494
+ return rows.some((r) => r.name === column);
71495
+ }
71496
+ var SELF_TRANSACTIONAL_MIGRATIONS = /* @__PURE__ */ new Set([1]);
71497
+ var ALTER_ADD_COLUMN_RE = /^[ \t]*ALTER TABLE\s+(\w+)\s+ADD COLUMN\s+(\w+)[^;]*;[ \t]*$/gm;
71498
+ function skipExistingColumns(db, sql) {
71499
+ return sql.replace(
71500
+ ALTER_ADD_COLUMN_RE,
71501
+ (stmt, table, column) => columnExists(db, table, column) ? "" : stmt
71502
+ );
71503
+ }
71504
+ function applyMigration(db, v) {
71505
+ const sql = MIGRATIONS[v];
71506
+ if (SELF_TRANSACTIONAL_MIGRATIONS.has(v)) {
71507
+ db.exec(sql);
71508
+ stampVersion(db, v + 1);
71509
+ return;
71510
+ }
71511
+ db.exec("BEGIN");
71512
+ try {
71513
+ db.exec(skipExistingColumns(db, sql));
71514
+ stampVersion(db, v + 1);
71515
+ db.exec("COMMIT");
71516
+ } catch (err2) {
71517
+ try {
71518
+ db.exec("ROLLBACK");
71519
+ } catch {
71520
+ }
71521
+ throw err2;
71426
71522
  }
71427
- return { from, to };
71428
71523
  }
71429
71524
 
71430
71525
  // packages/db/src/connection.ts
71431
71526
  var _db = null;
71527
+ var PRE_MIGRATE_KEEP = 5;
71528
+ function vacuumInto(db, destPath) {
71529
+ if (/[\0\n\r]/.test(destPath)) throw new Error("backup path not safe for SQL literal");
71530
+ db.exec(`VACUUM INTO '${destPath.replace(/'/g, "''")}'`);
71531
+ }
71532
+ function preMigrationBackup(db) {
71533
+ try {
71534
+ const hasMeta = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='_meta'`).get();
71535
+ if (!hasMeta) return;
71536
+ const row = db.prepare(`SELECT value FROM _meta WHERE key = 'schema_version'`).get();
71537
+ const from = row ? Number(row.value) : 0;
71538
+ if (from === 0 || from >= MIGRATIONS.length) return;
71539
+ const dir = getBackupsDir();
71540
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
71541
+ const dest = path3.join(dir, `pre-migrate-v${from}-${stamp}.sqlite`);
71542
+ try {
71543
+ vacuumInto(db, dest);
71544
+ } catch {
71545
+ fs3.copyFileSync(getDbPath(), dest);
71546
+ }
71547
+ const stale = fs3.readdirSync(dir).filter((f) => f.startsWith("pre-migrate-") && f.endsWith(".sqlite")).sort().reverse().slice(PRE_MIGRATE_KEEP);
71548
+ for (const f of stale) {
71549
+ try {
71550
+ fs3.unlinkSync(path3.join(dir, f));
71551
+ } catch {
71552
+ }
71553
+ }
71554
+ } catch (err2) {
71555
+ console.error(
71556
+ `[careermate] \uC5C5\uADF8\uB808\uC774\uB4DC \uC804 \uC790\uB3D9 \uBC31\uC5C5 \uC2E4\uD328(\uB9C8\uC774\uADF8\uB808\uC774\uC158\uC740 \uACC4\uC18D \uC9C4\uD589): ${err2 instanceof Error ? err2.message : String(err2)}`
71557
+ );
71558
+ }
71559
+ }
71432
71560
  function getDb() {
71433
71561
  if (_db) return _db;
71434
71562
  const db = new DatabaseSync(getDbPath());
71435
71563
  db.exec("PRAGMA journal_mode = WAL;");
71436
71564
  db.exec("PRAGMA foreign_keys = ON;");
71437
71565
  db.exec("PRAGMA busy_timeout = 5000;");
71566
+ preMigrationBackup(db);
71438
71567
  migrate(db);
71439
71568
  _db = db;
71440
71569
  return db;
@@ -72084,8 +72213,8 @@ function getErrorMap() {
72084
72213
 
72085
72214
  // node_modules/zod/v3/helpers/parseUtil.js
72086
72215
  var makeIssue = (params) => {
72087
- const { data, path: path8, errorMaps, issueData } = params;
72088
- const fullPath = [...path8, ...issueData.path || []];
72216
+ const { data, path: path9, errorMaps, issueData } = params;
72217
+ const fullPath = [...path9, ...issueData.path || []];
72089
72218
  const fullIssue = {
72090
72219
  ...issueData,
72091
72220
  path: fullPath
@@ -72201,11 +72330,11 @@ var errorUtil;
72201
72330
 
72202
72331
  // node_modules/zod/v3/types.js
72203
72332
  var ParseInputLazyPath = class {
72204
- constructor(parent, value, path8, key) {
72333
+ constructor(parent, value, path9, key) {
72205
72334
  this._cachedPath = [];
72206
72335
  this.parent = parent;
72207
72336
  this.data = value;
72208
- this._path = path8;
72337
+ this._path = path9;
72209
72338
  this._key = key;
72210
72339
  }
72211
72340
  get path() {
@@ -75881,6 +76010,22 @@ var CoverLetterRecordSchema = external_exports.object({
75881
76010
  current_content: external_exports.string().nullable(),
75882
76011
  versions: external_exports.array(CoverLetterVersionRecordSchema).optional()
75883
76012
  });
76013
+ var JobQuestionSchema = external_exports.object({
76014
+ no: external_exports.number().int().positive().optional().describe("\uBB38\uD56D \uBC88\uD638(\uC788\uC73C\uBA74)"),
76015
+ question: external_exports.string().max(MAX_BODY).describe("\uBB38\uD56D \uD14D\uC2A4\uD2B8(\uD68C\uC0AC\uAC00 \uC81C\uC2DC\uD55C \uC790\uC18C\uC11C \uD56D\uBAA9)"),
76016
+ min_chars: external_exports.number().int().positive().optional().describe("\uAE00\uC790\uC218 \uD558\uD55C(\uC788\uC73C\uBA74)"),
76017
+ max_chars: external_exports.number().int().positive().optional().describe("\uAE00\uC790\uC218 \uC0C1\uD55C(\uC788\uC73C\uBA74)"),
76018
+ count_mode: external_exports.enum(["with_space", "no_space", "byte"]).optional().describe("\uAE00\uC790\uC218 \uAE30\uC900: with_space=\uACF5\uBC31\uD3EC\uD568(\uAE30\uBCF8)\xB7no_space=\uACF5\uBC31\uC81C\uC678\xB7byte=UTF-8 \uBC14\uC774\uD2B8"),
76019
+ note: optLine.describe('\uBD80\uAC00 \uBA54\uBAA8(\uC608: "\uD544\uC218", "\uACBD\uD5D8 \uC911\uC2EC")')
76020
+ });
76021
+ var SCHEDULE_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
76022
+ var SCHEDULE_TIME_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
76023
+ var JobScheduleItemSchema = external_exports.object({
76024
+ label: reqLine.describe("\uC77C\uC815 \uC774\uB984 (\uC608: 1\uCC28 \uBA74\uC811\xB7AI\uC5ED\uAC80\xB7\uC11C\uB958 \uBC1C\uD45C)"),
76025
+ date: external_exports.string().regex(SCHEDULE_DATE_RE, "YYYY-MM-DD \uD615\uC2DD\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4").describe("\uC77C\uC815 \uB0A0\uC9DC YYYY-MM-DD"),
76026
+ time: external_exports.string().regex(SCHEDULE_TIME_RE, "HH:MM(24\uC2DC\uAC04) \uD615\uC2DD\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4").optional().describe("\uC2DC\uAC01 HH:MM(24\uC2DC\uAC04, \uC120\uD0DD)"),
76027
+ note: optLine.describe("\uBD80\uAC00 \uBA54\uBAA8(\uC608: \uC7A5\uC18C\xB7\uC900\uBE44\uBB3C, \uC120\uD0DD)")
76028
+ });
75884
76029
  var JobInputSchema = external_exports.object({
75885
76030
  company: reqLine,
75886
76031
  position: reqLine.describe("\uC9C1\uBB34/\uD3EC\uC9C0\uC158\uBA85"),
@@ -75894,7 +76039,10 @@ var JobInputSchema = external_exports.object({
75894
76039
  source: external_exports.string().max(MAX_LINE).optional().describe("\uCD9C\uCC98 (\uC0AC\uB78C\uC778/\uC6D0\uD2F0\uB4DC/\uC9C1\uC811 \uC785\uB825 \uB4F1)"),
75895
76040
  company_overview: optBody.describe("\uD68C\uC0AC \uC0AC\uC5C5\uAC1C\uC694\xB7\uBBF8\uC158\xB7\uC8FC\uC694 \uC81C\uD488\xB7\uCD5C\uADFC \uD65C\uB3D9 (AI\uAC00 \uB9AC\uC11C\uCE58\uD574 \uCC44\uC6C0)"),
75896
76041
  talent_profile: optBody.describe("\uD68C\uC0AC\uAC00 \uAC15\uC870\uD558\uB294 \uC778\uC7AC\uC0C1 (\uD55C\uAD6D \uC790\uC18C\uC11C\xB7\uBA74\uC811\uC5D0\uC11C \uC815\uB82C \uAE30\uC900)"),
75897
- core_values: strList.optional().describe("\uD68C\uC0AC \uD575\uC2EC\uAC00\uCE58 (\uD0A4\uC6CC\uB4DC \uBC30\uC5F4)")
76042
+ core_values: strList.optional().describe("\uD68C\uC0AC \uD575\uC2EC\uAC00\uCE58 (\uD0A4\uC6CC\uB4DC \uBC30\uC5F4)"),
76043
+ reputation: optBody.describe("\uD68C\uC0AC \uD3C9\uD310 \uC694\uC57D \u2014 \uD3C9\uC810\xB7\uC6CC\uB77C\uBC38\xB7\uBB38\uD654 \uB9AC\uC2A4\uD06C \uB4F1 (AI\uAC00 company-research \uD50C\uB808\uC774\uBD81\uC73C\uB85C \uB9AC\uC11C\uCE58\uD574 \uCC44\uC6C0; \uCD9C\uCC98\xB7\uCD5C\uADFC\uC131 \uD45C\uAE30)"),
76044
+ questions: external_exports.array(JobQuestionSchema).max(MAX_ITEMS).optional().describe("\uD68C\uC0AC \uC9C0\uC815 \uC790\uC18C\uC11C \uBB38\uD56D\xB7\uAE00\uC790\uC218 \u2014 \uC0AC\uC6A9\uC790\uAC00 \uBB38\uD56D\uC744 \uC8FC\uBA74 \uAD6C\uC870\uD654 \uC800\uC7A5"),
76045
+ schedule: external_exports.array(JobScheduleItemSchema).max(MAX_ITEMS).optional().describe("\uC804\uD615 \uC77C\uC815 \u2014 \uBA74\uC811\uCD08\uB300 \uBA54\uC77C\xB7\uBC1C\uD45C \uACF5\uC9C0\uB97C \uBC1B\uC73C\uBA74 \uAD6C\uC870\uD654 \uC800\uC7A5(label\xB7date \uD544\uC218, time \uC120\uD0DD). \uC11C\uB958 \uB9C8\uAC10\uC740 deadline \uD544\uB4DC\uB97C \uC4F0\uC138\uC694.")
75898
76046
  });
75899
76047
  var JobRecordSchema = external_exports.object({
75900
76048
  ...baseRecord,
@@ -75910,7 +76058,46 @@ var JobRecordSchema = external_exports.object({
75910
76058
  source: external_exports.string().nullable(),
75911
76059
  company_overview: external_exports.string().nullable(),
75912
76060
  talent_profile: external_exports.string().nullable(),
75913
- core_values: external_exports.array(external_exports.string())
76061
+ core_values: external_exports.array(external_exports.string()),
76062
+ reputation: external_exports.string().nullable(),
76063
+ questions: external_exports.array(JobQuestionSchema),
76064
+ schedule: external_exports.array(JobScheduleItemSchema)
76065
+ });
76066
+ var OFFER_STATUSES = ["open", "accepted", "declined"];
76067
+ var OfferInputSchema = external_exports.object({
76068
+ job_id: reqLine,
76069
+ offer_status: external_exports.enum(OFFER_STATUSES).optional().describe("\uC624\uD37C \uC0C1\uD0DC \u2014 open(\uC9C4\uD589 \uC911)/accepted(\uC218\uB77D)/declined(\uAC70\uC808). \uC0DD\uB7B5 \uC2DC \uAE30\uC874 \uAC12 \uC720\uC9C0(\uC2E0\uADDC \uC800\uC7A5\uC740 open)"),
76070
+ base_salary: external_exports.number().nonnegative().optional().describe("\uAE30\uBCF8\uAE09/\uC5F0\uBD09 (\uB9CC\uC6D0)"),
76071
+ bonus_amount: external_exports.number().nonnegative().optional().describe("\uC5F0 \uC131\uACFC\uAE09\xB7\uC0C1\uC5EC \uAE30\uB300\uC561 (\uB9CC\uC6D0)"),
76072
+ welfare_amount: external_exports.number().nonnegative().optional().describe("\uBCF5\uC9C0\uD3EC\uC778\uD2B8 \uB4F1 \uC5F0\uD658\uC0B0 (\uB9CC\uC6D0)"),
76073
+ signing_amount: external_exports.number().nonnegative().optional().describe("\uC0AC\uC774\uB2DD\uBCF4\uB108\uC2A4 \uC77C\uC2DC\uAE08 (\uB9CC\uC6D0)"),
76074
+ equity_note: optNote.describe("\uC8FC\uC2DD/\uC2A4\uD1A1\uC635\uC158 (\uC815\uC131 \uC11C\uC220 \u2014 \uAC00\uCE58\uD3C9\uAC00\uAC00 \uBAA8\uD638\uD558\uBBC0\uB85C \uD14D\uC2A4\uD2B8)"),
76075
+ comp_note: optBody.describe("\uAE30\uD0C0 \uBCF4\uC0C1 \uC790\uC720 \uC11C\uC220"),
76076
+ work_arrangement: optLine.describe("\uADFC\uBB34\uD615\uD0DC (\uC7AC\uD0DD/\uD558\uC774\uBE0C\uB9AC\uB4DC/\uCD9C\uADFC)"),
76077
+ contract_type: optLine.describe("\uACC4\uC57D\uD615\uD0DC (\uC815\uADDC/\uACC4\uC57D \uB4F1)"),
76078
+ accept_deadline: optLine.describe("\uC218\uB77D \uB9C8\uAC10\uC77C YYYY-MM-DD"),
76079
+ ai_score: external_exports.number().min(0).max(100).optional().describe("AI\uAC00 \uB9E4\uAE34 \uC885\uD569 \uC810\uC218 0~100 (\uC800\uC7A5 \uC2DC AI\uAC00 \uCC44\uC6C0)"),
76080
+ verdict: optBody.describe("\uCD5C\uC885\uD3C9 \u2014 \uC5B4\uB5A4 \uC0C1\uD669\uC774\uBA74 \uC5B4\uB514\uAC00 \uC88B\uC740\uC9C0 \uB4F1 (AI\uAC00 \uC800\uC7A5 \uC2DC \uC791\uC131)"),
76081
+ notes: optNote
76082
+ });
76083
+ var OfferRecordSchema = external_exports.object({
76084
+ ...baseRecord,
76085
+ job_id: external_exports.string(),
76086
+ offer_status: external_exports.enum(OFFER_STATUSES),
76087
+ base_salary: external_exports.number().nullable(),
76088
+ bonus_amount: external_exports.number().nullable(),
76089
+ welfare_amount: external_exports.number().nullable(),
76090
+ signing_amount: external_exports.number().nullable(),
76091
+ equity_note: external_exports.string().nullable(),
76092
+ comp_note: external_exports.string().nullable(),
76093
+ work_arrangement: external_exports.string().nullable(),
76094
+ contract_type: external_exports.string().nullable(),
76095
+ accept_deadline: external_exports.string().nullable(),
76096
+ ai_score: external_exports.number().nullable(),
76097
+ verdict: external_exports.string().nullable(),
76098
+ notes: external_exports.string().nullable(),
76099
+ /** Server-computed annual cash estimate = base + bonus + welfare (signing is one-time, excluded). Null if no numeric pieces. */
76100
+ total_comp_annual_est: external_exports.number().nullable()
75914
76101
  });
75915
76102
  var FitAnalysisInputSchema = external_exports.object({
75916
76103
  job_id: reqLine.describe("\uBD84\uC11D \uB300\uC0C1 \uACF5\uACE0 ID"),
@@ -75933,6 +76120,14 @@ var FitAnalysisRecordSchema = external_exports.object({
75933
76120
  missing_keywords: external_exports.array(external_exports.string()),
75934
76121
  recommendations: external_exports.array(external_exports.string())
75935
76122
  });
76123
+ var REJECTED_STAGES = ["document", "assessment", "interview", "final", "other"];
76124
+ var REJECTED_STAGE_LABELS = {
76125
+ document: "\uC11C\uB958",
76126
+ assessment: "\uC778\uC801\uC131\xB7AI\uC5ED\uAC80",
76127
+ interview: "\uBA74\uC811",
76128
+ final: "\uCD5C\uC885\xB7\uCC98\uC6B0",
76129
+ other: "\uAE30\uD0C0"
76130
+ };
75936
76131
  var ApplicationInputSchema = external_exports.object({
75937
76132
  job_id: reqLine,
75938
76133
  status: external_exports.enum(APPLICATION_STATUSES).optional(),
@@ -75953,7 +76148,10 @@ var ApplicationStatusUpdateSchema = external_exports.object({
75953
76148
  application_id: optLine,
75954
76149
  status: external_exports.enum(APPLICATION_STATUSES),
75955
76150
  note: optNote.describe("\uC0C1\uD0DC \uBCC0\uACBD \uC0AC\uC720/\uBA54\uBAA8"),
75956
- submission: ApplicationSubmissionSchema.optional().describe("\uC9C0\uC6D0 \uC644\uB8CC \uAE30\uB85D\uC5D0 \uB0A8\uAE38 \uC81C\uCD9C \uC790\uB8CC \uC2A4\uB0C5\uC0F7")
76151
+ submission: ApplicationSubmissionSchema.optional().describe("\uC9C0\uC6D0 \uC644\uB8CC \uAE30\uB85D\uC5D0 \uB0A8\uAE38 \uC81C\uCD9C \uC790\uB8CC \uC2A4\uB0C5\uC0F7"),
76152
+ rejected_stage: external_exports.enum(REJECTED_STAGES).optional().describe(
76153
+ "\uD0C8\uB77D \uD68C\uACE0 \u2014 'rejected'\uB85C \uBC14\uAFC0 \uB54C \uC5B4\uB290 \uB2E8\uACC4\uC5D0\uC11C \uB5A8\uC5B4\uC84C\uB294\uC9C0: \uC11C\uB958=document, \uC778\uC801\uC131\xB7AI\uC5ED\uAC80=assessment, \uBA74\uC811=interview, \uCD5C\uC885\xB7\uCC98\uC6B0=final, \uAE30\uD0C0=other. rejected\uAC00 \uC544\uB2CC \uC0C1\uD0DC\uC5D0\uB294 \uC800\uC7A5\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."
76154
+ )
75957
76155
  });
75958
76156
  var ApplicationRecordSchema = external_exports.object({
75959
76157
  ...baseRecord,
@@ -75962,7 +76160,8 @@ var ApplicationRecordSchema = external_exports.object({
75962
76160
  resume_id: external_exports.string().nullable(),
75963
76161
  cover_letter_id: external_exports.string().nullable(),
75964
76162
  applied_at: external_exports.string().nullable(),
75965
- notes: external_exports.string().nullable()
76163
+ notes: external_exports.string().nullable(),
76164
+ rejected_stage: external_exports.enum(REJECTED_STAGES).nullable()
75966
76165
  });
75967
76166
  var StarGuideSchema = external_exports.object({
75968
76167
  question: external_exports.string().max(MAX_NOTE),
@@ -76066,7 +76265,7 @@ var ApplicationContextSchema = external_exports.object({
76066
76265
  });
76067
76266
 
76068
76267
  // packages/shared/src/build-info.ts
76069
- var APP_VERSION = true ? "0.0.8" : "0.0.0-dev";
76268
+ var APP_VERSION = true ? "0.0.9" : "0.0.0-dev";
76070
76269
  var BUNDLED = true;
76071
76270
 
76072
76271
  // packages/shared/src/index.ts
@@ -76614,6 +76813,9 @@ function mapJob(r) {
76614
76813
  company_overview: r.company_overview ?? null,
76615
76814
  talent_profile: r.talent_profile ?? null,
76616
76815
  core_values: fromJson(r.core_values, []),
76816
+ reputation: r.reputation ?? null,
76817
+ questions: fromJson(r.questions, []),
76818
+ schedule: fromJson(r.schedule, []),
76617
76819
  created_at: r.created_at,
76618
76820
  updated_at: r.updated_at
76619
76821
  };
@@ -76643,7 +76845,7 @@ var jobRepo = {
76643
76845
  if (existing) {
76644
76846
  const m = { ...existing, ...input };
76645
76847
  db.prepare(
76646
- `UPDATE jobs SET company=?,position=?,url=?,location=?,employment_type=?,description=?,requirements=?,keywords=?,deadline=?,source=?,company_overview=?,talent_profile=?,core_values=?,updated_at=? WHERE id=?`
76848
+ `UPDATE jobs SET company=?,position=?,url=?,location=?,employment_type=?,description=?,requirements=?,keywords=?,deadline=?,source=?,company_overview=?,talent_profile=?,core_values=?,reputation=?,questions=?,schedule=?,updated_at=? WHERE id=?`
76647
76849
  ).run(
76648
76850
  m.company,
76649
76851
  m.position,
@@ -76658,6 +76860,12 @@ var jobRepo = {
76658
76860
  m.company_overview ?? null,
76659
76861
  m.talent_profile ?? null,
76660
76862
  toJson(m.core_values ?? []),
76863
+ m.reputation ?? null,
76864
+ // questions omitted from input → m keeps existing.questions (preserved);
76865
+ // provided → replaced. Same merge semantics as the other JSON fields.
76866
+ toJson(m.questions ?? []),
76867
+ // schedule follows the same preserve-on-omit merge (v11 전형 일정).
76868
+ toJson(m.schedule ?? []),
76661
76869
  ts,
76662
76870
  existing.id
76663
76871
  );
@@ -76665,8 +76873,8 @@ var jobRepo = {
76665
76873
  }
76666
76874
  const newJobId = id2 ?? newId("job_");
76667
76875
  db.prepare(
76668
- `INSERT INTO jobs (id,company,position,url,location,employment_type,description,requirements,keywords,deadline,source,company_overview,talent_profile,core_values,created_at,updated_at)
76669
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
76876
+ `INSERT INTO jobs (id,company,position,url,location,employment_type,description,requirements,keywords,deadline,source,company_overview,talent_profile,core_values,reputation,questions,schedule,created_at,updated_at)
76877
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
76670
76878
  ).run(
76671
76879
  newJobId,
76672
76880
  input.company,
@@ -76682,6 +76890,9 @@ var jobRepo = {
76682
76890
  input.company_overview ?? null,
76683
76891
  input.talent_profile ?? null,
76684
76892
  toJson(input.core_values ?? []),
76893
+ input.reputation ?? null,
76894
+ toJson(input.questions ?? []),
76895
+ toJson(input.schedule ?? []),
76685
76896
  ts,
76686
76897
  ts
76687
76898
  );
@@ -76695,6 +76906,7 @@ var jobRepo = {
76695
76906
  db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id2);
76696
76907
  db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id2);
76697
76908
  db.prepare(`DELETE FROM application_timeline_events WHERE job_id=?`).run(id2);
76909
+ db.prepare(`DELETE FROM offers WHERE job_id=?`).run(id2);
76698
76910
  return db.prepare(`DELETE FROM jobs WHERE id=?`).run(id2).changes > 0;
76699
76911
  });
76700
76912
  }
@@ -76769,6 +76981,8 @@ function mapApplication(r) {
76769
76981
  cover_letter_id: r.cover_letter_id,
76770
76982
  applied_at: r.applied_at,
76771
76983
  notes: r.notes,
76984
+ // 탈락 회고: 탈락이 아니거나 미기록이면 NULL(v10 이전 DB는 컬럼이 없어 undefined → null 정규화).
76985
+ rejected_stage: r.rejected_stage ?? null,
76772
76986
  created_at: r.created_at,
76773
76987
  updated_at: r.updated_at
76774
76988
  };
@@ -76805,17 +77019,19 @@ var applicationRepo = {
76805
77019
  const app = this.ensure(input.job_id, input.status ?? "draft");
76806
77020
  if (input.status && input.status !== app.status) assertStatusTransition(app.status, input.status);
76807
77021
  const m = { ...app, ...input };
77022
+ const rejectedStageValue = m.status === "rejected" ? m.rejected_stage ?? null : null;
76808
77023
  getDb().prepare(
76809
- `UPDATE applications SET status=?,resume_id=?,cover_letter_id=?,applied_at=?,notes=?,updated_at=? WHERE id=?`
76810
- ).run(m.status, m.resume_id ?? null, m.cover_letter_id ?? null, m.applied_at ?? null, m.notes ?? null, now(), app.id);
77024
+ `UPDATE applications SET status=?,resume_id=?,cover_letter_id=?,applied_at=?,notes=?,rejected_stage=?,updated_at=? WHERE id=?`
77025
+ ).run(m.status, m.resume_id ?? null, m.cover_letter_id ?? null, m.applied_at ?? null, m.notes ?? null, rejectedStageValue, now(), app.id);
76811
77026
  return this.get(app.id);
76812
77027
  },
76813
- setStatus(jobId, status, note) {
77028
+ setStatus(jobId, status, note, rejectedStage) {
76814
77029
  const app = this.ensure(jobId);
76815
77030
  assertStatusTransition(app.status, status);
76816
77031
  const appliedAt = status === "applied" && !app.applied_at ? now() : app.applied_at;
76817
77032
  const notes = note ? [app.notes, note].filter(Boolean).join("\n") : app.notes;
76818
- getDb().prepare(`UPDATE applications SET status=?, applied_at=?, notes=?, updated_at=? WHERE id=?`).run(status, appliedAt, notes, now(), app.id);
77033
+ const rejectedStageValue = status === "rejected" ? rejectedStage ?? app.rejected_stage ?? null : null;
77034
+ getDb().prepare(`UPDATE applications SET status=?, applied_at=?, notes=?, rejected_stage=?, updated_at=? WHERE id=?`).run(status, appliedAt, notes, rejectedStageValue, now(), app.id);
76819
77035
  return this.get(app.id);
76820
77036
  }
76821
77037
  };
@@ -76935,10 +77151,100 @@ var timelineRepo = {
76935
77151
  return getDb().prepare(`DELETE FROM application_timeline_events WHERE id=?`).run(id2).changes > 0;
76936
77152
  }
76937
77153
  };
77154
+ function mapOffer(r) {
77155
+ const base = r.base_salary ?? null;
77156
+ const bonus = r.bonus_amount ?? null;
77157
+ const welfare = r.welfare_amount ?? null;
77158
+ const hasAny = base != null || bonus != null || welfare != null;
77159
+ const total = hasAny ? (base ?? 0) + (bonus ?? 0) + (welfare ?? 0) : null;
77160
+ return {
77161
+ id: r.id,
77162
+ job_id: r.job_id,
77163
+ offer_status: r.offer_status ?? "open",
77164
+ base_salary: base,
77165
+ bonus_amount: bonus,
77166
+ welfare_amount: welfare,
77167
+ signing_amount: r.signing_amount ?? null,
77168
+ equity_note: r.equity_note ?? null,
77169
+ comp_note: r.comp_note ?? null,
77170
+ work_arrangement: r.work_arrangement ?? null,
77171
+ contract_type: r.contract_type ?? null,
77172
+ accept_deadline: r.accept_deadline ?? null,
77173
+ ai_score: r.ai_score ?? null,
77174
+ verdict: r.verdict ?? null,
77175
+ notes: r.notes ?? null,
77176
+ total_comp_annual_est: total,
77177
+ created_at: r.created_at,
77178
+ updated_at: r.updated_at
77179
+ };
77180
+ }
77181
+ var offerRepo = {
77182
+ getByJob(jobId) {
77183
+ const r = getDb().prepare(`SELECT * FROM offers WHERE job_id=?`).get(jobId);
77184
+ return r ? mapOffer(r) : null;
77185
+ },
77186
+ list() {
77187
+ return getDb().prepare(`SELECT * FROM offers ORDER BY ai_score IS NULL, ai_score DESC, updated_at DESC`).all().map(mapOffer);
77188
+ },
77189
+ /** Upsert by job_id (one offer per job). Missing fields are preserved on update (partial-safe). */
77190
+ save(input) {
77191
+ const db = getDb();
77192
+ const existing = this.getByJob(input.job_id);
77193
+ const ts = now();
77194
+ const n = (v, prev) => v ?? prev ?? null;
77195
+ const s = (v, prev) => v ?? prev ?? null;
77196
+ if (existing) {
77197
+ db.prepare(
77198
+ `UPDATE offers SET offer_status=?,base_salary=?,bonus_amount=?,welfare_amount=?,signing_amount=?,equity_note=?,comp_note=?,work_arrangement=?,contract_type=?,accept_deadline=?,ai_score=?,verdict=?,notes=?,updated_at=? WHERE id=?`
77199
+ ).run(
77200
+ input.offer_status ?? existing.offer_status,
77201
+ n(input.base_salary, existing.base_salary),
77202
+ n(input.bonus_amount, existing.bonus_amount),
77203
+ n(input.welfare_amount, existing.welfare_amount),
77204
+ n(input.signing_amount, existing.signing_amount),
77205
+ s(input.equity_note, existing.equity_note),
77206
+ s(input.comp_note, existing.comp_note),
77207
+ s(input.work_arrangement, existing.work_arrangement),
77208
+ s(input.contract_type, existing.contract_type),
77209
+ s(input.accept_deadline, existing.accept_deadline),
77210
+ n(input.ai_score, existing.ai_score),
77211
+ s(input.verdict, existing.verdict),
77212
+ s(input.notes, existing.notes),
77213
+ ts,
77214
+ existing.id
77215
+ );
77216
+ return this.getByJob(input.job_id);
77217
+ }
77218
+ const id2 = newId("ofr_");
77219
+ db.prepare(
77220
+ `INSERT INTO offers (id,job_id,offer_status,base_salary,bonus_amount,welfare_amount,signing_amount,equity_note,comp_note,work_arrangement,contract_type,accept_deadline,ai_score,verdict,notes,created_at,updated_at)
77221
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
77222
+ ).run(
77223
+ id2,
77224
+ input.job_id,
77225
+ input.offer_status ?? "open",
77226
+ input.base_salary ?? null,
77227
+ input.bonus_amount ?? null,
77228
+ input.welfare_amount ?? null,
77229
+ input.signing_amount ?? null,
77230
+ input.equity_note ?? null,
77231
+ input.comp_note ?? null,
77232
+ input.work_arrangement ?? null,
77233
+ input.contract_type ?? null,
77234
+ input.accept_deadline ?? null,
77235
+ input.ai_score ?? null,
77236
+ input.verdict ?? null,
77237
+ input.notes ?? null,
77238
+ ts,
77239
+ ts
77240
+ );
77241
+ return this.getByJob(input.job_id);
77242
+ }
77243
+ };
76938
77244
 
76939
77245
  // packages/mcp-tools/src/tools.ts
76940
- import fs7 from "node:fs";
76941
- import path7 from "node:path";
77246
+ import fs8 from "node:fs";
77247
+ import path8 from "node:path";
76942
77248
 
76943
77249
  // packages/core/src/onboarding.ts
76944
77250
  function profileCompleteness(p) {
@@ -77563,12 +77869,12 @@ function saveCoverLetterVersion(input) {
77563
77869
  }
77564
77870
  return { coverLetter, version: version2, verification, forced };
77565
77871
  }
77566
- function updateApplicationStatus(jobId, status, note, submission) {
77872
+ function updateApplicationStatus(jobId, status, note, submission, rejectedStage) {
77567
77873
  const job = jobRepo.get(jobId);
77568
77874
  if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
77569
77875
  const before2 = applicationRepo.getByJob(jobId);
77570
77876
  const application = tx(() => {
77571
- let application2 = applicationRepo.setStatus(jobId, status, note);
77877
+ let application2 = applicationRepo.setStatus(jobId, status, note, rejectedStage);
77572
77878
  const submittedDocs = (submission?.document_ids ?? []).map((docId) => documentRepo.get(docId)).filter((doc) => !!doc);
77573
77879
  const submittedCoverLetter = submission?.cover_letter_id ? coverLetterRepo.get(submission.cover_letter_id, true) : null;
77574
77880
  if (status === "applied" && submission) {
@@ -77604,7 +77910,9 @@ function updateApplicationStatus(jobId, status, note, submission) {
77604
77910
  status,
77605
77911
  status_label: APPLICATION_STATUS_LABELS[status],
77606
77912
  note: note ?? null,
77607
- submission: submissionPayload
77913
+ submission: submissionPayload,
77914
+ // 탈락 회고: rejected로 넘어갈 때 실제 저장된 단계(setStatus가 정합성 처리한 값)를 타임라인에도 남긴다.
77915
+ rejected_stage: application2.rejected_stage
77608
77916
  },
77609
77917
  occurred_at: submittedAtToOccurrence(submittedAt)
77610
77918
  });
@@ -77637,8 +77945,21 @@ function saveInterviewPrep(input) {
77637
77945
  // packages/core/src/summary.ts
77638
77946
  var DEADLINE_ACTIONABLE = ["draft", "planned"];
77639
77947
  var DEADLINE_SOON_DAYS = 14;
77640
- var FOLLOWUP_STALE_DAYS = 10;
77948
+ var SCHEDULE_SOON_DAYS = 7;
77641
77949
  var DAY_MS = 864e5;
77950
+ function collectFutureSchedule(jobs, now2) {
77951
+ const items = [];
77952
+ for (const job of jobs) {
77953
+ for (const s of job.schedule ?? []) {
77954
+ const d_day = daysUntilDate(s.date, now2);
77955
+ if (d_day == null || d_day < 0) continue;
77956
+ items.push({ job, label: s.label, date: s.date, time: s.time, d_day });
77957
+ }
77958
+ }
77959
+ return items.sort(
77960
+ (a, b) => a.date < b.date ? -1 : a.date > b.date ? 1 : (a.time || "99:99").localeCompare(b.time || "99:99")
77961
+ );
77962
+ }
77642
77963
  function daysUntilDate(dateStr, now2) {
77643
77964
  if (!dateStr) return null;
77644
77965
  const d = /* @__PURE__ */ new Date(`${dateStr}T00:00:00`);
@@ -77653,6 +77974,24 @@ function daysSinceTimestamp(iso, now2) {
77653
77974
  if (isNaN(d.getTime())) return null;
77654
77975
  return Math.floor((now2.getTime() - d.getTime()) / DAY_MS);
77655
77976
  }
77977
+ function businessDaysSince(iso, now2) {
77978
+ if (!iso) return null;
77979
+ const start = new Date(iso);
77980
+ if (isNaN(start.getTime())) return null;
77981
+ const s = new Date(start);
77982
+ s.setHours(0, 0, 0, 0);
77983
+ const e = new Date(now2);
77984
+ e.setHours(0, 0, 0, 0);
77985
+ if (e.getTime() <= s.getTime()) return 0;
77986
+ let count = 0;
77987
+ const cursor = new Date(s);
77988
+ while (cursor.getTime() < e.getTime()) {
77989
+ cursor.setDate(cursor.getDate() + 1);
77990
+ const dow = cursor.getDay();
77991
+ if (dow !== 0 && dow !== 6) count++;
77992
+ }
77993
+ return count;
77994
+ }
77656
77995
  function jobWithMeta(job) {
77657
77996
  const app = applicationRepo.getByJob(job.id);
77658
77997
  const fit = fitRepo.getByJob(job.id);
@@ -77664,18 +78003,36 @@ function listRecentActivity(limit = 20) {
77664
78003
  }
77665
78004
  var POST_PASS_STALE_DAYS = 7;
77666
78005
  var POST_INTERVIEW_STALE_DAYS = 7;
78006
+ var FOLLOWUP_DUE_BIZ_DAYS = 5;
78007
+ var FOLLOWUP_OVERDUE_BIZ_DAYS = 8;
78008
+ var FOLLOWUP_ONHOLD_CAL_DAYS = 45;
77667
78009
  function getActionDigest(now2 = /* @__PURE__ */ new Date()) {
77668
78010
  const applications = applicationRepo.list();
77669
78011
  const jobs = jobRepo.list();
77670
78012
  const statusByJob = new Map(applications.map((a) => [a.job_id, a]));
78013
+ const jobById = new Map(jobs.map((j) => [j.id, j]));
78014
+ const preppedJobIds = new Set(interviewRepo.list().map((p) => p.job_id));
77671
78015
  const followups = [];
77672
78016
  for (const a of applications) {
77673
- const job = jobRepo.get(a.job_id);
78017
+ const job = jobById.get(a.job_id);
77674
78018
  if (!job) continue;
77675
78019
  if (a.status === "applied") {
77676
- const days = daysSinceTimestamp(a.applied_at, now2);
77677
- if (days != null && days >= FOLLOWUP_STALE_DAYS)
77678
- followups.push({ job, status: a.status, status_label: APPLICATION_STATUS_LABELS[a.status], days_since: days, reason: `\uC9C0\uC6D0 \uD6C4 ${days}\uC77C\uC9F8 \uC751\uB2F5 \uC5C6\uC74C \u2014 \uC815\uC911\uD55C \uD6C4\uC18D \uBB38\uC758\uB97C \uACE0\uB824\uD560 \uC2DC\uC810` });
78020
+ const cal = daysSinceTimestamp(a.applied_at, now2);
78021
+ const biz = businessDaysSince(a.applied_at, now2);
78022
+ if (biz != null && biz >= FOLLOWUP_DUE_BIZ_DAYS) {
78023
+ const overdue = biz >= FOLLOWUP_OVERDUE_BIZ_DAYS;
78024
+ const item = {
78025
+ job,
78026
+ status: a.status,
78027
+ status_label: APPLICATION_STATUS_LABELS[a.status],
78028
+ days_since: cal ?? 0,
78029
+ business_days_since: biz,
78030
+ followup_timing: overdue ? "overdue" : "due",
78031
+ reason: overdue ? `\uC9C0\uC6D0 \uD6C4 ${biz}\uC601\uC5C5\uC77C \uACBD\uACFC \u2014 \uD6C4\uC18D \uBB38\uC758 \uC801\uAE30\uAC00 \uC9C0\uB0AC\uC2B5\uB2C8\uB2E4(\uBB34\uB9AC\uD55C \uB3C5\uCD09\uC740 \uC5ED\uD6A8\uACFC\uC774\uB2C8 \uC544\uC9C1 \uC548 \uD588\uB2E4\uBA74 \uC815\uC911\uD788 1\uD68C\uB9CC)` : `\uC9C0\uC6D0 \uD6C4 ${biz}\uC601\uC5C5\uC77C \u2014 \uD314\uB85C\uC5C5 \uC801\uAE30(\uBB38\uC758\uB294 \uC815\uC911\uD788 1\uD68C)`
78032
+ };
78033
+ if (cal != null && cal >= FOLLOWUP_ONHOLD_CAL_DAYS) item.suggest_on_hold = true;
78034
+ followups.push(item);
78035
+ }
77679
78036
  } else if (a.status === "document_passed") {
77680
78037
  const days = daysSinceTimestamp(a.updated_at, now2);
77681
78038
  if (days != null && days >= POST_PASS_STALE_DAYS)
@@ -77694,14 +78051,52 @@ function getActionDigest(now2 = /* @__PURE__ */ new Date()) {
77694
78051
  if (days_left == null || days_left > DEADLINE_SOON_DAYS) return null;
77695
78052
  return { job, status, status_label: APPLICATION_STATUS_LABELS[status], days_left };
77696
78053
  }).filter((x2) => x2 !== null).sort((a, b) => a.days_left - b.days_left);
77697
- const interview_todo = applications.filter((a) => INTERVIEW_UNLOCK_STATUSES.includes(a.status) && !interviewRepo.getByJob(a.job_id)).map((a) => {
77698
- const job = jobRepo.get(a.job_id);
78054
+ const interview_todo = applications.filter((a) => INTERVIEW_UNLOCK_STATUSES.includes(a.status) && !preppedJobIds.has(a.job_id)).map((a) => {
78055
+ const job = jobById.get(a.job_id);
77699
78056
  return job ? { job, status_label: APPLICATION_STATUS_LABELS[a.status] } : null;
77700
78057
  }).filter((x2) => x2 !== null);
78058
+ const futureSchedule = collectFutureSchedule(jobs, now2);
78059
+ const upcoming_schedule = futureSchedule.filter((x2) => x2.d_day <= SCHEDULE_SOON_DAYS).map((x2) => ({
78060
+ job_id: x2.job.id,
78061
+ company: x2.job.company,
78062
+ position: x2.job.position,
78063
+ label: x2.label,
78064
+ ...x2.time ? { time: x2.time } : {},
78065
+ date: x2.date,
78066
+ d_day: x2.d_day
78067
+ }));
78068
+ const byDate = /* @__PURE__ */ new Map();
78069
+ for (const x2 of futureSchedule) {
78070
+ const arr = byDate.get(x2.date) ?? [];
78071
+ arr.push(x2);
78072
+ byDate.set(x2.date, arr);
78073
+ }
78074
+ const schedule_conflicts = [];
78075
+ for (const [date, items] of byDate) {
78076
+ const distinctJobs = new Set(items.map((x2) => x2.job.id));
78077
+ if (distinctJobs.size < 2) continue;
78078
+ schedule_conflicts.push({
78079
+ date,
78080
+ items: items.map((x2) => ({
78081
+ job_id: x2.job.id,
78082
+ company: x2.job.company,
78083
+ position: x2.job.position,
78084
+ label: x2.label,
78085
+ ...x2.time ? { time: x2.time } : {}
78086
+ }))
78087
+ });
78088
+ }
78089
+ schedule_conflicts.sort((a, b) => a.date < b.date ? -1 : a.date > b.date ? 1 : 0);
77701
78090
  const today = `${now2.getFullYear()}-${String(now2.getMonth() + 1).padStart(2, "0")}-${String(now2.getDate()).padStart(2, "0")}`;
77702
- return { today, followups, deadlines, interview_todo };
78091
+ return { today, followups, deadlines, interview_todo, upcoming_schedule, schedule_conflicts };
77703
78092
  }
77704
78093
  var normToken = (s) => s.toLowerCase().trim().replace(/^[.,/()\s]+|[.,/()\s]+$/g, "");
78094
+ var ASCII_TOKEN_RE = /^[a-z0-9+#.]+$/;
78095
+ var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
78096
+ function resumeBodyHasToken(text, token) {
78097
+ if (!ASCII_TOKEN_RE.test(token)) return text.includes(token);
78098
+ return new RegExp(`(?<![a-z0-9])${escapeRegExp(token)}(?![a-z0-9])`, "i").test(text);
78099
+ }
77705
78100
  function prescreenJob(jobId) {
77706
78101
  const job = jobRepo.get(jobId);
77707
78102
  if (!job) return null;
@@ -77710,17 +78105,36 @@ function prescreenJob(jobId) {
77710
78105
  for (const e of experienceRepo.list()) for (const t of e.tech ?? []) userTokens.add(normToken(t));
77711
78106
  for (const p of projectRepo.list()) for (const t of p.tech ?? []) userTokens.add(normToken(t));
77712
78107
  userTokens.delete("");
78108
+ const resume = documentRepo.primary("resume");
78109
+ const resume_checked = resume != null;
78110
+ const resumeText = (resume?.content ?? "").toLowerCase();
77713
78111
  const keywords = (job.keywords ?? []).filter((k) => k && k.trim());
77714
78112
  const matched = [];
77715
78113
  const missing = [];
78114
+ const resume_matched = [];
77716
78115
  for (const kw of keywords) {
77717
78116
  const n = normToken(kw);
77718
- const hit = userTokens.has(n) || // substring match only for tokens ≥3 chars (avoid "c"/"r" matching everything).
78117
+ const skillHit = userTokens.has(n) || // substring match only for tokens ≥3 chars (avoid "c"/"r" matching everything).
77719
78118
  n.length >= 3 && [...userTokens].some((u) => u.length >= 3 && (u.includes(n) || n.includes(u)));
77720
- (hit ? matched : missing).push(kw);
78119
+ (skillHit ? matched : missing).push(kw);
78120
+ if (resume_checked && n.length >= 2 && resumeBodyHasToken(resumeText, n)) resume_matched.push(kw);
77721
78121
  }
77722
78122
  const coverage = keywords.length ? matched.length / keywords.length : null;
77723
- return { job_id: jobId, job_keywords: keywords, matched, missing, coverage, user_skill_count: userTokens.size };
78123
+ const combinedSet = /* @__PURE__ */ new Set([...matched, ...resume_matched]);
78124
+ const combined_matched = keywords.filter((k) => combinedSet.has(k));
78125
+ const combined_coverage = keywords.length ? combined_matched.length / keywords.length : null;
78126
+ return {
78127
+ job_id: jobId,
78128
+ job_keywords: keywords,
78129
+ matched,
78130
+ missing,
78131
+ coverage,
78132
+ user_skill_count: userTokens.size,
78133
+ resume_matched,
78134
+ combined_matched,
78135
+ combined_coverage,
78136
+ resume_checked
78137
+ };
77724
78138
  }
77725
78139
  var PROGRESSION = ["applied", "document_passed", "interview", "final_passed"];
77726
78140
  function getPipelineStats() {
@@ -77749,6 +78163,12 @@ function getPipelineStats() {
77749
78163
  }
77750
78164
  const pct = (n, d) => d > 0 ? Math.round(n / d * 1e3) / 10 : null;
77751
78165
  const cur = (s) => applications.filter((a) => a.status === s).length;
78166
+ const rejectedApps = applications.filter((a) => a.status === "rejected");
78167
+ const rejected_by_stage = REJECTED_STAGES.map((stage) => ({
78168
+ stage,
78169
+ count: rejectedApps.filter((a) => a.rejected_stage === stage).length
78170
+ })).filter((s) => s.count > 0);
78171
+ const rejected_unrecorded = rejectedApps.filter((a) => !a.rejected_stage).length;
77752
78172
  return {
77753
78173
  total: applications.length,
77754
78174
  by_status,
@@ -77760,12 +78180,14 @@ function getPipelineStats() {
77760
78180
  in_progress: cur("applied") + cur("document_passed") + cur("interview"),
77761
78181
  on_hold: cur("on_hold")
77762
78182
  },
78183
+ rejected_by_stage,
78184
+ rejected_unrecorded,
77763
78185
  note: "\uB2E8\uACC4\uBCC4 \uB3C4\uB2EC \uAE54\uB54C\uAE30\uB294 \uC0C1\uD0DC \uBCC0\uACBD \uC774\uB825(\uD0C0\uC784\uB77C\uC778)\uC73C\uB85C \uBCF5\uC6D0\uD55C \uC2E4\uCE21\uCE58\uC785\uB2C8\uB2E4(\uD604\uC7AC rejected\uC5EC\uB3C4 \uAC70\uCCD0\uAC04 \uCD5C\uACE0 \uB2E8\uACC4\uAE4C\uC9C0 \uC9D1\uACC4). \uBE44\uC728\uC740 \uC124\uBA85\uC6A9\uC774\uBA70 \uD45C\uBCF8\uC774 \uC791\uC73C\uBA74 \uC608\uCE21\uB825\uC774 \uB0AE\uC2B5\uB2C8\uB2E4 \u2014 \uCD94\uC138 \uCC38\uACE0\uB85C\uB9CC \uC4F0\uC138\uC694."
77764
78186
  };
77765
78187
  }
77766
78188
 
77767
78189
  // packages/core/src/jobsearch.ts
77768
- var UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36";
78190
+ var UA = `careermate/${APP_VERSION} (+https://careermate.life)`;
77769
78191
  function toYmd(v) {
77770
78192
  if (typeof v !== "string" || v.length < 10) return void 0;
77771
78193
  const m = v.match(/^(\d{4}-\d{2}-\d{2})/);
@@ -77855,6 +78277,24 @@ async function searchJobs(query, opts = {}) {
77855
78277
  };
77856
78278
  }
77857
78279
 
78280
+ // packages/core/src/offers.ts
78281
+ function withJob(offer, job) {
78282
+ return { ...offer, company: job?.company ?? "\u2014", position: job?.position ?? "\u2014" };
78283
+ }
78284
+ function saveOffer(input) {
78285
+ const job = jobRepo.get(input.job_id);
78286
+ if (!job) throw new Error("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 save_job_posting\uC73C\uB85C \uACF5\uACE0\uB97C \uC800\uC7A5\uD558\uC138\uC694.");
78287
+ return withJob(offerRepo.save(input), job);
78288
+ }
78289
+ function getOffer(jobId) {
78290
+ const offer = offerRepo.getByJob(jobId);
78291
+ if (!offer) return null;
78292
+ return withJob(offer, jobRepo.get(jobId));
78293
+ }
78294
+ function compareOffers() {
78295
+ return offerRepo.list().map((o) => withJob(o, jobRepo.get(o.job_id)));
78296
+ }
78297
+
77858
78298
  // packages/prompts/src/humanize.ts
77859
78299
  var HUMANIZE_WRITING_GUIDE = `# \uC0AC\uB78C\uC774 \uC4F4 \uAC83\uCC98\uB7FC: \uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uCEE4\uB9AC\uC5B4 \uAE00\uC4F0\uAE30 \uADDC\uCE59
77860
78300
 
@@ -77883,12 +78323,15 @@ var HUMANIZE_WRITING_GUIDE = `# \uC0AC\uB78C\uC774 \uC4F4 \uAC83\uCC98\uB7FC: \u
77883
78323
  3. \uAE30\uACC4\uC801 \uBCD1\uB82C\uAD6C\uC870 \u2014 \uC138 \uAC1C\uC529 \uB098\uC5F4(rule of three), \uBAA8\uB4E0 \uBB38\uB2E8\uC774 \uB611\uAC19\uC740 \uACE8\uACA9.
77884
78324
  - "\uCCAB\uC9F8\u2026\uB458\uC9F8\u2026\uC14B\uC9F8", "A\uD558\uACE0 B\uD558\uBA70 C\uD558\uB294" \uAC19\uC740 \uADE0\uC77C\uD55C \uB098\uC5F4\uC744 \uBC18\uBCF5\uD558\uC9C0 \uC54A\uB294\uB2E4.
77885
78325
  - \uBB38\uB2E8\uB9C8\uB2E4 \uAE38\uC774\uC640 \uC2DC\uC791 \uBC29\uC2DD\uC744 \uC77C\uBD80\uB7EC \uB2E4\uB974\uAC8C \uD55C\uB2E4.
78326
+ - \uAC19\uC740 \uCE90\uCE58\uD504\uB808\uC774\uC988\xB7\uBAA8\uD2F0\uD504(\uC608: "1\uC6D0\uB3C4 \uD2C0\uB9AC\uC9C0 \uC54A\uB294")\uB97C \uBB38\uC11C \uC804\uCCB4\uC5D0\uC11C **\uCD5C\uB300 2\uD68C**\uAE4C\uC9C0\uB9CC \uC4F4\uB2E4. 3\uD68C\uBD80\uD130\uB294 \uC778\uC704\uC801 \uC7A5\uCE58\uB85C \uC77D\uD600 \uC624\uD788\uB824 \uC0AC\uB78C \uB0C4\uC0C8\uB97C \uC9C0\uC6B4\uB2E4 \u2014 \uBC18\uBCF5\uD558\uACE0 \uC2F6\uC73C\uBA74 \uB9E4\uBC88 \uB2E4\uB978 \uB9D0\uB85C \uBC14\uAFD4 \uD45C\uD604\uD55C\uB2E4.
77886
78327
 
77887
78328
  4. \uC0C1\uD22C\uC801 \uC5F0\uACB0\uC5B4 \uB0A8\uBC1C \u2014 "\uB610\uD55C", "\uBFD0\uB9CC \uC544\uB2C8\uB77C", "\uB354 \uB098\uC544\uAC00", "\uC774\uB97C \uD1B5\uD574", "\uACB0\uB860\uC801\uC73C\uB85C".
77888
78329
  - \uC811\uC18D\uC5B4 \uC5C6\uC774 \uBB38\uC7A5\uC744 \uC787\uAC70\uB098, \uC0AC\uAC74\uC758 \uD750\uB984 \uC790\uCCB4\uB85C \uC5F0\uACB0\uD55C\uB2E4.
77889
78330
 
77890
78331
  5. \uADE0\uC77C\uD55C \uBB38\uC7A5 \uB9AC\uB4EC \u2014 \uBAA8\uB4E0 \uBB38\uC7A5\uC774 \uBE44\uC2B7\uD55C \uAE38\uC774.
77891
78332
  - \uC9E7\uC740 \uBB38\uC7A5\uACFC \uAE34 \uBB38\uC7A5\uC744 \uC11E\uB294\uB2E4. \uAC00\uB054 \uC544\uC8FC \uC9E7\uAC8C \uB04A\uB294\uB2E4. \uC774\uB807\uAC8C.
78333
+ - \uBE44\uC2B7\uD55C \uAE38\uC774\uC758 \uBB38\uC7A5\uC744 \uC138 \uAC1C \uC5F0\uC18D\uC73C\uB85C \uC4F0\uC9C0 \uC54A\uB294\uB2E4. \uD558\uB098\uB294 \uAE38\uAC8C \uD480\uACE0 \uB2E4\uC74C\uC740 \uD22D \uB04A\uB294 \uC2DD\uC73C\uB85C \uD638\uD761\uC744 \uBC14\uAFBC\uB2E4.
78334
+ - \uBB38\uB2E8\uB9C8\uB2E4 \uD55C \uD638\uD761\uC5D0 \uC77D\uD788\uB294 \uC9E7\uC740 \uB2E8\uD0C0 \uBB38\uC7A5(8\uB2E8\uC5B4 \uC548\uD30E \uC774\uD558)\uC744 \uCD5C\uC18C \uD558\uB098 \uB123\uB294\uB2E4.
77892
78335
 
77893
78336
  6. \uACFC\uC7A5\uB41C \uC218\uC2DD\xB7\uC218\uB3D9\uD0DC \u2014 "\uB9E4\uC6B0", "\uC815\uB9D0\uB85C", "\uD0C1\uC6D4\uD55C", "\uC131\uACF5\uC801\uC73C\uB85C", "~\uB418\uC5B4\uC84C\uC2B5\uB2C8\uB2E4".
77894
78337
  - \uBD80\uC0AC\uB97C \uBE7C\uACE0 \uB3D9\uC0AC\xB7\uBA85\uC0AC\uB97C \uAD6C\uCCB4\uD654\uD55C\uB2E4. \uB2A5\uB3D9\uD0DC\uB97C \uAE30\uBCF8\uC73C\uB85C \uD55C\uB2E4.
@@ -77916,6 +78359,7 @@ var STRUCTURED_DOC_WRITING_GUIDE = `# \uAD6C\uC870\uD615 \uBB38\uC11C(\uACBD\uB8
77916
78359
  - \uAC01 \uBD88\uB9BF\uC740 \uB3D9\uC0AC\uB098 \uC218\uCE58\uB85C \uC2DC\uC791\uD558\uACE0, \uAC80\uC99D \uAC00\uB2A5\uD55C \uC0AC\uC2E4(\uC22B\uC790\xB7\uAE30\uAC04\xB7\uB3C4\uAD6C\xB7\uC5ED\uD560\xB7\uACB0\uACFC)\uC744 \uD55C \uC904\uC5D0 \uB2F4\uB294\uB2E4.
77917
78360
  - \uBD88\uB9BF \uC548\uC758 \uBC88\uC5ED\uD22C\xB7\uD074\uB9AC\uC170\xB7\uACF5\uD5C8\uD55C \uC77C\uBC18\uB860\xB7\uC218\uB3D9\uD0DC\uB294 \uC790\uAE30\uC18C\uAC1C\uC11C\uC640 \uB611\uAC19\uC774 \uC81C\uAC70\uD55C\uB2E4. (\uC608: "\uBD84\uC11D\uC744 \uC9C4\uD589\uD558\uC600\uC2B5\uB2C8\uB2E4" \u2192 "\uBD84\uC11D\uD588\uB2E4", "\uB9CE\uC740 \uC131\uACFC\uB97C \uB0C8\uC2B5\uB2C8\uB2E4" \u2192 \uC2E4\uC81C \uC218\uCE58)
77918
78361
  - STAR\uC744 \uD55C\uB450 \uC904\uB85C \uC555\uCD95\uD55C\uB2E4: \uBB34\uC5C7\uC744 \uC65C \uD588\uACE0 \uACB0\uACFC \uC218\uCE58\uAC00 \uC5B4\uB5BB\uAC8C \uB410\uB294\uC9C0.
78362
+ - \uC0B0\uBB38\uD615 \uBD88\uB9BF\uC774 \uC5EC\uB7EC \uBB38\uC7A5\uC73C\uB85C \uAE38\uC5B4\uC9C0\uBA74, \uB9E8 \uC55E\uC5D0 "\uBC29\uBC95\u2192\uC131\uACFC"\uB97C \uC555\uCD95\uD55C \uB9AC\uB4DC \uAD6C\uB97C **\uAD75\uAC8C \uD55C \uC904** \uC5B9\uC5B4 30\uCD08 \uC2A4\uCE94\uC5D0\uC11C \uBC29\uBC95\uC774 \uBB38\uC7A5 \uC18D\uC5D0 \uBB3B\uD788\uC9C0 \uC54A\uAC8C \uD55C\uB2E4. (\uC608: **"\uC81C\uD488\uAD70\uBCC4 \uC18C\uC9C4\uC8FC\uAE30 \uCC28\uB4F1 \u2192 90\uC77C \uC7AC\uAD6C\uB9E4 18%\u219227%"** \u2014 \uC774\uC5B4\uC11C \uC0C1\uC138 \uC124\uBA85.) \uB2E8 \uC815\uC9C1 \uC7A5\uCE58(\uADC0\uC18D \uB2E8\uC11C\xB7\uACFC\uC7A5 \uC5C6\uB294 \uD558\uD5A5 \uC815\uB82C)\uB294 \uB9AC\uB4DC \uAD6C\uC5D0\uC11C\uB3C4 \uADF8\uB300\uB85C \uC720\uC9C0\uD55C\uB2E4 \u2014 \uB9AC\uB4DC \uAD6C\uAC00 \uC131\uACFC\uB97C \uBD80\uD480\uB9AC\uB294 \uD5E4\uB4DC\uB77C\uC778\uC774 \uB418\uBA74 \uC548 \uB41C\uB2E4.
77919
78363
 
77920
78364
  ## Do / Don't
77921
78365
  - Do: \uB450\uAD04\uC2DD \uBD88\uB9BF(\uACB0\uACFC\xB7\uC218\uCE58\uB97C \uC55E\uC138\uC6C0), \uC9C1\uBB34\xB7\uAE30\uAC04\xB7\uC5ED\uD560 \uAD6C\uBD84, \uC131\uACFC\uB97C \uC22B\uC790\uB85C.
@@ -78067,11 +78511,11 @@ function renderWorkflowMarkdown(id2) {
78067
78511
  }
78068
78512
 
78069
78513
  // packages/knowledge/src/index.ts
78070
- import fs3 from "node:fs";
78071
- import path3 from "node:path";
78514
+ import fs4 from "node:fs";
78515
+ import path4 from "node:path";
78072
78516
  import { fileURLToPath } from "node:url";
78073
- var here = path3.dirname(fileURLToPath(import.meta.url));
78074
- var CAREER_OS_DIR = BUNDLED ? path3.join(here, "career-os") : path3.resolve(here, "..", "..", "..", "docs", "career-os");
78517
+ var here = path4.dirname(fileURLToPath(import.meta.url));
78518
+ var CAREER_OS_DIR = BUNDLED ? path4.join(here, "career-os") : path4.resolve(here, "..", "..", "..", "docs", "career-os");
78075
78519
  var EXPERT_DOMAINS = [
78076
78520
  "resume",
78077
78521
  "ats",
@@ -78103,7 +78547,7 @@ var cache = /* @__PURE__ */ new Map();
78103
78547
  function readDoc(rel) {
78104
78548
  let cached = cache.get(rel);
78105
78549
  if (cached === void 0) {
78106
- cached = fs3.readFileSync(path3.join(CAREER_OS_DIR, rel), "utf8");
78550
+ cached = fs4.readFileSync(path4.join(CAREER_OS_DIR, rel), "utf8");
78107
78551
  cache.set(rel, cached);
78108
78552
  }
78109
78553
  return cached;
@@ -78112,7 +78556,7 @@ function getPlaybook(domain) {
78112
78556
  return readDoc(`knowledge/${domain}.md`);
78113
78557
  }
78114
78558
  function getVerifier(id2) {
78115
- return readDoc(path3.join("knowledge", "verifiers", `${id2}.md`));
78559
+ return readDoc(path4.join("knowledge", "verifiers", `${id2}.md`));
78116
78560
  }
78117
78561
  function getEop(feature) {
78118
78562
  return readDoc(`eop/${feature}.md`);
@@ -95735,16 +96179,16 @@ var replacer = ({ json, patch, patchText, context, keepOriginalStyles = true })
95735
96179
  didFindOccurrence: true
95736
96180
  };
95737
96181
  };
95738
- var goToElementFromPath = (json, path8) => {
96182
+ var goToElementFromPath = (json, path9) => {
95739
96183
  let element = json;
95740
- for (let i2 = 1; i2 < path8.length; i2++) {
95741
- const index = path8[i2];
96184
+ for (let i2 = 1; i2 < path9.length; i2++) {
96185
+ const index = path9[i2];
95742
96186
  element = element.elements[index];
95743
96187
  }
95744
96188
  return element;
95745
96189
  };
95746
- var goToParentElementFromPath = (json, path8) => goToElementFromPath(json, path8.slice(0, path8.length - 1));
95747
- var getLastElementIndexFromPath = (path8) => path8[path8.length - 1];
96190
+ var goToParentElementFromPath = (json, path9) => goToElementFromPath(json, path9.slice(0, path9.length - 1));
96191
+ var getLastElementIndexFromPath = (path9) => path9[path9.length - 1];
95748
96192
  var PatchType = {
95749
96193
  /** Replace entire file-level elements (e.g., whole paragraphs) */
95750
96194
  DOCUMENT: "file",
@@ -96304,8 +96748,8 @@ function decodeEntities(input) {
96304
96748
  }
96305
96749
 
96306
96750
  // packages/parsers/src/documents.ts
96307
- import fs4 from "node:fs";
96308
- import path4 from "node:path";
96751
+ import fs5 from "node:fs";
96752
+ import path5 from "node:path";
96309
96753
  import os2 from "node:os";
96310
96754
 
96311
96755
  // node_modules/fflate/esm/index.mjs
@@ -96796,7 +97240,7 @@ var import_mammoth = __toESM(require_lib6(), 1);
96796
97240
  var import_hwp = __toESM(require_cjs(), 1);
96797
97241
  var parseHwp = import_hwp.default?.parse ?? import_hwp.default?.default?.parse;
96798
97242
  var MAX_BYTES = 25 * 1024 * 1024;
96799
- var SENSITIVE_PATH_RE = /(^|[/\\])\.(ssh|aws|gnupg)([/\\]|$)|(^|[/\\])id_(rsa|dsa|ecdsa|ed25519)(\.[\w]+)?$|\.(pem|ppk)$|(^|[/\\])\.env(\.[\w-]+)?$|(^|[/\\])\.(npmrc|netrc|git-credentials)$/i;
97243
+ var SENSITIVE_PATH_RE = /(^|[/\\])\.(ssh|aws|gnupg|kube|docker|azure|boto)([/\\]|$)|[/\\]\.config[/\\]gcloud([/\\]|$)|(^|[/\\])id_(rsa|dsa|ecdsa|ed25519)(\.[\w]+)?$|\.(pem|ppk)$|(^|[/\\])\.env(\.[\w-]+)?$|(^|[/\\])\.(npmrc|netrc|git-credentials)$|(^|[/\\])(login data|web data|cookies|key4\.db|logins\.json|wallet\.dat)$/i;
96800
97244
  var MAX_HWPX_ENTRY_BYTES = 40 * 1024 * 1024;
96801
97245
  var MAX_HWPX_TOTAL_BYTES = 80 * 1024 * 1024;
96802
97246
  var MAX_PDF_PAGES = 300;
@@ -96810,8 +97254,8 @@ var MD_RE = /\.(md|markdown|mdown|mkd)$/i;
96810
97254
  var LEGACY_BINARY_RE = /\.(docm?|dot[xm]?|pptx?|ppt|xlsx?|xls|odt|ods|odp|rtf|pages|key|numbers)$/i;
96811
97255
  function resolveDocumentPath(input, baseDir) {
96812
97256
  let p = (input ?? "").trim().replace(/^["']|["']$/g, "");
96813
- if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) p = path4.join(os2.homedir(), p.slice(1));
96814
- if (!path4.isAbsolute(p)) p = path4.resolve(baseDir ?? process.cwd(), p);
97257
+ if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) p = path5.join(os2.homedir(), p.slice(1));
97258
+ if (!path5.isAbsolute(p)) p = path5.resolve(baseDir ?? process.cwd(), p);
96815
97259
  return p;
96816
97260
  }
96817
97261
  async function extractDocument(input, baseDir) {
@@ -96828,7 +97272,7 @@ async function extractDocument(input, baseDir) {
96828
97272
  });
96829
97273
  let realPath = filePath;
96830
97274
  try {
96831
- realPath = fs4.realpathSync.native(filePath);
97275
+ realPath = fs5.realpathSync.native(filePath);
96832
97276
  } catch {
96833
97277
  }
96834
97278
  if (SENSITIVE_PATH_RE.test(filePath) || SENSITIVE_PATH_RE.test(realPath)) {
@@ -96842,7 +97286,7 @@ async function extractDocument(input, baseDir) {
96842
97286
  }
96843
97287
  let stat;
96844
97288
  try {
96845
- stat = fs4.statSync(filePath);
97289
+ stat = fs5.statSync(filePath);
96846
97290
  } catch {
96847
97291
  return base("unsupported", { warnings: [`\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}`], unsupported: true, errored: true });
96848
97292
  }
@@ -96861,7 +97305,7 @@ async function extractDocument(input, baseDir) {
96861
97305
  warnings: ["\uD30C\uC77C\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4(0\uBC14\uC774\uD2B8). \uB0B4\uC6A9\uC774 \uC788\uB294 \uD30C\uC77C\uC744 \uC9C0\uC815\uD574 \uC8FC\uC138\uC694."],
96862
97306
  unsupported: true
96863
97307
  });
96864
- const name = path4.basename(filePath);
97308
+ const name = path5.basename(filePath);
96865
97309
  const bytes = stat.size;
96866
97310
  if (IMAGE_RE.test(name)) {
96867
97311
  return base("image", {
@@ -96875,7 +97319,7 @@ async function extractDocument(input, baseDir) {
96875
97319
  if (TEXT_RE.test(name) || MD_RE.test(name)) {
96876
97320
  let textBuf;
96877
97321
  try {
96878
- textBuf = fs4.readFileSync(filePath);
97322
+ textBuf = fs5.readFileSync(filePath);
96879
97323
  } catch (e) {
96880
97324
  return base("unsupported", { bytes, warnings: [`\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${errMsg(e)}`], unsupported: true, errored: true });
96881
97325
  }
@@ -96890,7 +97334,7 @@ async function extractDocument(input, baseDir) {
96890
97334
  }
96891
97335
  let buf;
96892
97336
  try {
96893
- buf = fs4.readFileSync(filePath);
97337
+ buf = fs5.readFileSync(filePath);
96894
97338
  } catch (e) {
96895
97339
  return base("unsupported", { bytes, warnings: [`\uD30C\uC77C\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${errMsg(e)}`], unsupported: true, errored: true });
96896
97340
  }
@@ -96902,7 +97346,7 @@ async function extractDocument(input, baseDir) {
96902
97346
  return base("unsupported", {
96903
97347
  bytes,
96904
97348
  warnings: [
96905
- `${path4.extname(name) || "\uC774 \uD615\uC2DD"}\uC740 \uC790\uB3D9 \uCD94\uCD9C\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. PDF \uB610\uB294 .docx/.hwp/.hwpx\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD558\uAC70\uB098, \uB0B4\uC6A9\uC744 \uD14D\uC2A4\uD2B8\uB85C \uBCF5\uC0AC\uD574 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.`
97349
+ `${path5.extname(name) || "\uC774 \uD615\uC2DD"}\uC740 \uC790\uB3D9 \uCD94\uCD9C\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. PDF \uB610\uB294 .docx/.hwp/.hwpx\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD558\uAC70\uB098, \uB0B4\uC6A9\uC744 \uD14D\uC2A4\uD2B8\uB85C \uBCF5\uC0AC\uD574 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.`
96906
97350
  ],
96907
97351
  unsupported: true
96908
97352
  });
@@ -97157,18 +97601,18 @@ function fail(text) {
97157
97601
 
97158
97602
  // packages/mcp-tools/src/bridge.ts
97159
97603
  import { spawn, spawnSync } from "node:child_process";
97160
- import fs5 from "node:fs";
97161
- import path5 from "node:path";
97604
+ import fs6 from "node:fs";
97605
+ import path6 from "node:path";
97162
97606
  import { fileURLToPath as fileURLToPath2 } from "node:url";
97163
- var __dirname = path5.dirname(fileURLToPath2(import.meta.url));
97164
- var REPO_ROOT = path5.resolve(__dirname, "..", "..", "..");
97607
+ var __dirname = path6.dirname(fileURLToPath2(import.meta.url));
97608
+ var REPO_ROOT = path6.resolve(__dirname, "..", "..", "..");
97165
97609
  var HEALTH_TIMEOUT_MS = 800;
97166
97610
  var pendingStart = null;
97167
97611
  function dashboardLogPath() {
97168
- return path5.join(getDataDir(), "server.log");
97612
+ return path6.join(getDataDir(), "server.log");
97169
97613
  }
97170
97614
  function launchdPlistPath() {
97171
- return path5.join(getDataDir(), "dashboard.launchd.plist");
97615
+ return path6.join(getDataDir(), "dashboard.launchd.plist");
97172
97616
  }
97173
97617
  function launchdLabel() {
97174
97618
  let hash = 5381;
@@ -97264,18 +97708,18 @@ function spawnViaLaunchd(bin, args, cwd, log) {
97264
97708
  const label = launchdLabel();
97265
97709
  const plist = launchdPlistPath();
97266
97710
  try {
97267
- fs5.writeFileSync(plist, renderLaunchdPlist(bin, args, cwd, log), "utf8");
97711
+ fs6.writeFileSync(plist, renderLaunchdPlist(bin, args, cwd, log), "utf8");
97268
97712
  spawnSync("launchctl", ["bootout", `${domain}/${label}`], { stdio: "ignore" });
97269
97713
  const res = spawnSync("launchctl", ["bootstrap", domain, plist], {
97270
97714
  encoding: "utf8",
97271
97715
  timeout: 5e3
97272
97716
  });
97273
97717
  if (res.status === 0) return true;
97274
- fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl bootstrap failed: ${res.stderr || res.stdout || res.status}
97718
+ fs6.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl bootstrap failed: ${res.stderr || res.stdout || res.status}
97275
97719
  `);
97276
97720
  } catch (e) {
97277
97721
  try {
97278
- fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl launch failed: ${e instanceof Error ? e.message : String(e)}
97722
+ fs6.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl launch failed: ${e instanceof Error ? e.message : String(e)}
97279
97723
  `);
97280
97724
  } catch {
97281
97725
  }
@@ -97301,16 +97745,16 @@ async function ensureServer(timeoutMs = 12e3) {
97301
97745
  }
97302
97746
  }
97303
97747
  async function startServerBackground(timeoutMs) {
97304
- const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path5.join(__dirname, "web.mjs")], __dirname] : [
97748
+ const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path6.join(__dirname, "web.mjs")], __dirname] : [
97305
97749
  process.execPath,
97306
- ["--no-warnings", "--experimental-sqlite", "--import", "tsx", path5.join(REPO_ROOT, "apps/web/src/index.ts")],
97750
+ ["--no-warnings", "--experimental-sqlite", "--import", "tsx", path6.join(REPO_ROOT, "apps/web/src/index.ts")],
97307
97751
  REPO_ROOT
97308
97752
  ];
97309
97753
  const log = dashboardLogPath();
97310
97754
  let logFd = null;
97311
97755
  try {
97312
- logFd = fs5.openSync(log, "a");
97313
- fs5.writeSync(logFd, `
97756
+ logFd = fs6.openSync(log, "a");
97757
+ fs6.writeSync(logFd, `
97314
97758
  [${(/* @__PURE__ */ new Date()).toISOString()}] CareerMate dashboard background launch
97315
97759
  `);
97316
97760
  } catch {
@@ -97323,7 +97767,7 @@ async function startServerBackground(timeoutMs) {
97323
97767
  } finally {
97324
97768
  if (logFd !== null) {
97325
97769
  try {
97326
- fs5.closeSync(logFd);
97770
+ fs6.closeSync(logFd);
97327
97771
  } catch {
97328
97772
  }
97329
97773
  }
@@ -97336,7 +97780,7 @@ async function startServerBackground(timeoutMs) {
97336
97780
  }
97337
97781
  if (launchedViaLaunchd) {
97338
97782
  try {
97339
- fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchd did not produce a healthy server; falling back to detached spawn
97783
+ fs6.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchd did not produce a healthy server; falling back to detached spawn
97340
97784
  `);
97341
97785
  } catch {
97342
97786
  }
@@ -97378,8 +97822,8 @@ async function resolveDashboardUrl(hash = "") {
97378
97822
  }
97379
97823
 
97380
97824
  // packages/mcp-tools/src/update.ts
97381
- import fs6 from "node:fs";
97382
- import path6 from "node:path";
97825
+ import fs7 from "node:fs";
97826
+ import path7 from "node:path";
97383
97827
  import { fileURLToPath as fileURLToPath3 } from "node:url";
97384
97828
  import { spawn as spawn2 } from "node:child_process";
97385
97829
  var REGISTRY_URL = "https://registry.npmjs.org/careermate/latest";
@@ -97387,7 +97831,7 @@ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
97387
97831
  var NULL_CACHE_TTL_MS = 30 * 60 * 1e3;
97388
97832
  var FETCH_TIMEOUT_MS = 1500;
97389
97833
  var INSTALL_TIMEOUT_MS = 12e4;
97390
- var cacheFile = () => path6.join(getDataDir(), ".update-check.json");
97834
+ var cacheFile = () => path7.join(getDataDir(), ".update-check.json");
97391
97835
  function checkDisabled() {
97392
97836
  const v = process.env.CAREERMATE_NO_UPDATE_CHECK?.trim().toLowerCase();
97393
97837
  return v === "1" || v === "true";
@@ -97435,14 +97879,14 @@ function comparePrerelease(a, b) {
97435
97879
  }
97436
97880
  function readCache() {
97437
97881
  try {
97438
- return JSON.parse(fs6.readFileSync(cacheFile(), "utf8"));
97882
+ return JSON.parse(fs7.readFileSync(cacheFile(), "utf8"));
97439
97883
  } catch {
97440
97884
  return null;
97441
97885
  }
97442
97886
  }
97443
97887
  function writeCache(c) {
97444
97888
  try {
97445
- fs6.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
97889
+ fs7.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
97446
97890
  } catch {
97447
97891
  }
97448
97892
  }
@@ -97473,13 +97917,13 @@ function refreshInBackground() {
97473
97917
  refreshing = false;
97474
97918
  });
97475
97919
  }
97476
- var __dirname2 = path6.dirname(fileURLToPath3(import.meta.url));
97920
+ var __dirname2 = path7.dirname(fileURLToPath3(import.meta.url));
97477
97921
  function installPrefix() {
97478
97922
  if (!BUNDLED) return null;
97479
- const pkgRoot = path6.resolve(__dirname2, "..");
97480
- const parent = path6.dirname(pkgRoot);
97481
- if (path6.basename(parent) !== "node_modules") return null;
97482
- return path6.dirname(parent);
97923
+ const pkgRoot = path7.resolve(__dirname2, "..");
97924
+ const parent = path7.dirname(pkgRoot);
97925
+ if (path7.basename(parent) !== "node_modules") return null;
97926
+ return path7.dirname(parent);
97483
97927
  }
97484
97928
  function manualCommand() {
97485
97929
  const prefix = installPrefix();
@@ -97612,13 +98056,13 @@ var DASHBOARD_URL = "http://127.0.0.1:4319";
97612
98056
  function listInboxFiles(dir) {
97613
98057
  let names;
97614
98058
  try {
97615
- names = fs7.readdirSync(dir);
98059
+ names = fs8.readdirSync(dir);
97616
98060
  } catch {
97617
98061
  return [];
97618
98062
  }
97619
98063
  let realDir;
97620
98064
  try {
97621
- realDir = fs7.realpathSync.native(dir);
98065
+ realDir = fs8.realpathSync.native(dir);
97622
98066
  } catch {
97623
98067
  realDir = dir;
97624
98068
  }
@@ -97626,12 +98070,12 @@ function listInboxFiles(dir) {
97626
98070
  const out = [];
97627
98071
  for (const name of names) {
97628
98072
  if (SKIP.test(name)) continue;
97629
- const p = path7.join(dir, name);
98073
+ const p = path8.join(dir, name);
97630
98074
  try {
97631
- if (!fs7.statSync(p).isFile()) continue;
97632
- const real = fs7.realpathSync.native(p);
97633
- const rel = path7.relative(realDir, real);
97634
- if (rel.startsWith("..") || path7.isAbsolute(rel)) continue;
98075
+ if (!fs8.statSync(p).isFile()) continue;
98076
+ const real = fs8.realpathSync.native(p);
98077
+ const rel = path8.relative(realDir, real);
98078
+ if (rel.startsWith("..") || path8.isAbsolute(rel)) continue;
97635
98079
  out.push({ name, path: p });
97636
98080
  } catch {
97637
98081
  }
@@ -97659,10 +98103,10 @@ function pickRoute(ctx) {
97659
98103
  return "write_cover_letter";
97660
98104
  }
97661
98105
  function writeExport(result2) {
97662
- const filePath = path7.join(getExportsDir(), result2.filename);
98106
+ const filePath = path8.join(getExportsDir(), result2.filename);
97663
98107
  try {
97664
- if (result2.bytes) fs7.writeFileSync(filePath, result2.bytes);
97665
- else fs7.writeFileSync(filePath, result2.content, "utf8");
98108
+ if (result2.bytes) fs8.writeFileSync(filePath, result2.bytes);
98109
+ else fs8.writeFileSync(filePath, result2.content, "utf8");
97666
98110
  return { path: filePath };
97667
98111
  } catch (e) {
97668
98112
  return { error: `\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}` };
@@ -97682,11 +98126,11 @@ var TOOLS = [
97682
98126
  handler: async (args) => {
97683
98127
  const r = await extractDocument(args.path);
97684
98128
  if (r.errored) {
97685
- return fail(`'${path7.basename(r.path)}'\uC744(\uB97C) \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.
98129
+ return fail(`'${path8.basename(r.path)}'\uC744(\uB97C) \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.
97686
98130
  ${r.warnings.join("\n")}`);
97687
98131
  }
97688
98132
  if (r.unsupported) {
97689
- return ok(`'${path7.basename(r.path)}'\uC740(\uB294) \uD14D\uC2A4\uD2B8\uB85C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
98133
+ return ok(`'${path8.basename(r.path)}'\uC740(\uB294) \uD14D\uC2A4\uD2B8\uB85C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
97690
98134
  ${r.warnings.join("\n")}`, {
97691
98135
  path: r.path,
97692
98136
  format: r.format,
@@ -97701,7 +98145,7 @@ ${r.warnings.join("\n")}`, {
97701
98145
  truncated = true;
97702
98146
  }
97703
98147
  const head = [
97704
- `'${path7.basename(r.path)}'\uC5D0\uC11C \uD14D\uC2A4\uD2B8\uB97C \uCD94\uCD9C\uD588\uC2B5\uB2C8\uB2E4 (${r.format.toUpperCase()}, ${text.length.toLocaleString()}\uC790${truncated ? ", \uC798\uB9BC" : ""}).`,
98148
+ `'${path8.basename(r.path)}'\uC5D0\uC11C \uD14D\uC2A4\uD2B8\uB97C \uCD94\uCD9C\uD588\uC2B5\uB2C8\uB2E4 (${r.format.toUpperCase()}, ${text.length.toLocaleString()}\uC790${truncated ? ", \uC798\uB9BC" : ""}).`,
97705
98149
  ...r.warnings,
97706
98150
  "\uC774 \uB0B4\uC6A9\uC744 \uADFC\uAC70\uB85C \uC791\uC5C5\uD55C \uB4A4 \uACB0\uACFC\uB97C \uC54C\uB9DE\uC740 CareerMate \uB3C4\uAD6C\uB85C \uC800\uC7A5\uD558\uC138\uC694."
97707
98151
  ].join("\n");
@@ -97831,6 +98275,8 @@ ${lines}`;
97831
98275
  },
97832
98276
  {
97833
98277
  name: "save_profile",
98278
+ idempotent: true,
98279
+ // 부분 업서트 — 같은 인자 재호출은 같은 결과
97834
98280
  title: "\uD504\uB85C\uD544 \uC800\uC7A5",
97835
98281
  description: "\uC0AC\uC6A9\uC790 \uD504\uB85C\uD544\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC804\uB2EC\uD55C \uD544\uB4DC\uB9CC \uAC31\uC2E0\uB418\uACE0 \uB098\uBA38\uC9C0\uB294 \uC720\uC9C0\uB429\uB2C8\uB2E4(\uBD80\uBD84 \uC800\uC7A5 \uC548\uC804). \uC774\uB825\uC11C\uB97C \uAD6C\uC870\uD654\uD55C \uB4A4 \uC774\uB984/\uD55C \uC904 \uC18C\uAC1C/\uD76C\uB9DD \uC9C1\uBB34/\uC120\uD638 \uBB38\uCCB4/\uAC15\uC870 \uD3EC\uC778\uD2B8 \uB4F1\uC744 \uCC44\uC6CC \uC800\uC7A5\uD558\uC138\uC694. \uC120\uD638 \uBB38\uCCB4(preferred_tone)\uC640 \uAC15\uC870 \uD3EC\uC778\uD2B8(emphasis_points)\uB294 \uC774\uD6C4 \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131 \uD488\uC9C8\uC5D0 \uC9C1\uC811 \uC601\uD5A5\uC744 \uC90D\uB2C8\uB2E4. \uD559\uB825(education)\xB7\uC790\uACA9\uC99D(certifications)\xB7\uC5B4\uD559\uC810\uC218(language_scores: \uD1A0\uC775\xB7\uC624\uD53D \uB4F1)\xB7\uC218\uC0C1(awards)\uC774 \uC774\uB825\uC11C\uC5D0 \uC788\uC73C\uBA74 \uAD6C\uC870\uD654\uD574 \uC800\uC7A5\uD558\uC138\uC694 \u2014 \uD55C\uAD6D \uC774\uB825\uC11C \uD575\uC2EC \uC2A4\uD399\uC774\uACE0, \uC800\uC7A5\uD558\uBA74 \uC774\uD6C4 \uC790\uC18C\uC11C\xB7\uC774\uB825\uC11C\uC5D0\uC11C \uC815\uD655\uD55C \uC218\uCE58(\uD559\uC810 3.8, \uD1A0\uC775 920)\uB97C \uC9C0\uC5B4\uB0B4\uC9C0 \uC54A\uACE0 \uADF8\uB300\uB85C \uC778\uC6A9\uD569\uB2C8\uB2E4.",
97836
98282
  inputSchema: ProfileInputSchema.shape,
@@ -97869,6 +98315,7 @@ ${lines}`;
97869
98315
  },
97870
98316
  {
97871
98317
  name: "delete_resume",
98318
+ destructive: true,
97872
98319
  title: "\uC774\uB825\uC11C/\uBB38\uC11C \uC0AD\uC81C",
97873
98320
  description: "\uC800\uC7A5\uB41C \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uB4F1 \uBB38\uC11C\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 \uBA85\uD655\uD788 \uC0AD\uC81C\uB97C \uC694\uCCAD\uD588\uC744 \uB54C\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_resumes\uB85C \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0 \uC5B4\uB5A4 \uBB38\uC11C\uC778\uC9C0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
97874
98321
  inputSchema: {
@@ -97887,6 +98334,8 @@ ${lines}`;
97887
98334
  /* ----------------------------------------------------------- experiences */
97888
98335
  {
97889
98336
  name: "add_experience",
98337
+ idempotent: true,
98338
+ // 회사·직무·입사일 키 업서트(adr_0003) — 재호출 안전
97890
98339
  title: "\uACBD\uB825 \uCD94\uAC00",
97891
98340
  description: "\uAD6C\uC870\uD654\uB41C \uACBD\uB825(\uC9C1\uC7A5 \uB2E8\uC704)\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC774\uB825\uC11C\uC5D0\uC11C \uD30C\uC545\uD55C \uACBD\uB825\uC744 experiences \uBC30\uC5F4\uC5D0 \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC74\uC774\uC5B4\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uD56D\uBAA9\uB9C8\uB2E4 \uB3C4\uAD6C\uB97C \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uC774\uB825\uC11C \uBCF8\uBB38(add_resume)\uACFC \uB2EC\uB9AC \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uAE30\uAC04\xB7\uC131\uACFC\uB97C \uD544\uB4DC\uB85C \uB098\uB220 \uC800\uC7A5\uD574 \uC801\uD569\uB3C4 \uBD84\uC11D/\uC790\uC18C\uC11C \uC791\uC131 \uC2DC \uC815\uBC00\uD558\uAC8C \uD65C\uC6A9\uB429\uB2C8\uB2E4. \uAC01 \uD56D\uBAA9\uC740 company\uAC00 \uD544\uC218\uC774\uBA70, achievements\uC5D0\uB294 \uC815\uB7C9 \uC9C0\uD45C\uAC00 \uB2F4\uAE34 \uC131\uACFC\uB97C, tech\uC5D0\uB294 \uC0AC\uC6A9 \uAE30\uC220\uC744 \uBC30\uC5F4\uB85C \uB123\uC73C\uC138\uC694. \uAC19\uC740 \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uC785\uC0AC\uC77C(start_date)\uC774 \uC77C\uCE58\uD558\uBA74 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4(get_onboarding_status)\uC758 has_experience\uB97C \uCC44\uC6B0\uB824\uBA74 1\uAC74 \uC774\uC0C1 \uCD94\uAC00\uD558\uC138\uC694.",
97892
98341
  inputSchema: ExperienceBatchInputSchema.shape,
@@ -97910,6 +98359,8 @@ ${lines}`;
97910
98359
  /* -------------------------------------------------------------- projects */
97911
98360
  {
97912
98361
  name: "add_project",
98362
+ idempotent: true,
98363
+ // 이름 키 업서트 — 재호출 안전
97913
98364
  title: "\uD504\uB85C\uC81D\uD2B8 \uCD94\uAC00",
97914
98365
  description: "\uB300\uD45C \uD504\uB85C\uC81D\uD2B8\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. projects \uBC30\uC5F4\uC5D0 \uC5EC\uB7EC \uAC1C\uB97C \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uD56D\uBAA9\uB9C8\uB2E4 \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uAC01 \uD56D\uBAA9\uC740 name\uC774 \uD544\uC218\uC774\uBA70, highlights(\uD575\uC2EC \uC131\uACFC)\uC640 tech(\uC0AC\uC6A9 \uAE30\uC220)\uB97C \uBC30\uC5F4\uB85C \uB123\uC73C\uBA74 \uC790\uC18C\uC11C\xB7\uBA74\uC811 \uC900\uBE44\uC5D0\uC11C \uADFC\uAC70\uB85C \uD65C\uC6A9\uB429\uB2C8\uB2E4. url\uC5D0 \uACB0\uACFC\uBB3C \uB9C1\uD06C\uB97C \uB123\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uAC19\uC740 \uC774\uB984\uC758 \uD504\uB85C\uC81D\uD2B8\uB294 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4.",
97915
98366
  inputSchema: ProjectBatchInputSchema.shape,
@@ -97933,6 +98384,8 @@ ${lines}`;
97933
98384
  /* ---------------------------------------------------------------- skills */
97934
98385
  {
97935
98386
  name: "add_skill",
98387
+ idempotent: true,
98388
+ // 이름 키 업서트 — 재호출 안전
97936
98389
  title: "\uAE30\uC220\uC2A4\uD0DD \uCD94\uAC00",
97937
98390
  description: "\uBCF4\uC720 \uAE30\uC220\uC2A4\uD0DD\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC5EC\uB7EC \uAC1C\uB97C skills \uBC30\uC5F4\uC5D0 \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uAE30\uC220\uB9C8\uB2E4 \uB3C4\uAD6C\uB97C \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uAC01 \uD56D\uBAA9\uC740 name\uC774 \uD544\uC218\uC774\uBA70, category(\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1)\xB7level\xB7years\uB85C \uC219\uB828\uB3C4\uB97C \uD568\uAED8 \uAE30\uB85D\uD558\uBA74 \uACF5\uACE0 \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC774 \uC815\uD655\uD574\uC9D1\uB2C8\uB2E4. \uAC19\uC740 \uC774\uB984\uC758 \uAE30\uC220\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4\uC758 has_skills\uB97C \uCC44\uC6B0\uB824\uBA74 1\uAC1C \uC774\uC0C1 \uCD94\uAC00\uD558\uC138\uC694.",
97938
98391
  inputSchema: SkillBatchInputSchema.shape,
@@ -97955,6 +98408,7 @@ ${lines}`;
97955
98408
  },
97956
98409
  {
97957
98410
  name: "delete_experience",
98411
+ destructive: true,
97958
98412
  title: "\uACBD\uB825 \uD56D\uBAA9 \uC0AD\uC81C",
97959
98413
  description: "\uAD6C\uC870\uD654\uB41C \uACBD\uB825(\uC9C1\uC7A5) \uD56D\uBAA9 \uD558\uB098\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC798\uBABB \uCD94\uCD9C/\uC785\uB825\uB41C \uACBD\uB825\uC744 \uC9C0\uC6B8 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_experiences\uB85C \uBAA9\uB85D\uACFC id\uB97C \uD655\uC778\uD558\uACE0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. (\uB0B4\uC6A9 \uC218\uC815\uC740 add_experience\uB85C \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.) confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
97960
98414
  inputSchema: {
@@ -97972,6 +98426,7 @@ ${lines}`;
97972
98426
  },
97973
98427
  {
97974
98428
  name: "delete_project",
98429
+ destructive: true,
97975
98430
  title: "\uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9 \uC0AD\uC81C",
97976
98431
  description: "\uAD6C\uC870\uD654\uB41C \uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9 \uD558\uB098\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC798\uBABB \uCD94\uCD9C/\uC785\uB825\uB41C \uD504\uB85C\uC81D\uD2B8\uB97C \uC9C0\uC6B8 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_projects\uB85C \uBAA9\uB85D\uACFC id\uB97C \uD655\uC778\uD558\uACE0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. (\uB0B4\uC6A9 \uC218\uC815\uC740 add_project\uB85C \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.) confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
97977
98432
  inputSchema: {
@@ -97989,6 +98444,7 @@ ${lines}`;
97989
98444
  },
97990
98445
  {
97991
98446
  name: "delete_skill",
98447
+ destructive: true,
97992
98448
  title: "\uAE30\uC220\uC2A4\uD0DD \uD56D\uBAA9 \uC0AD\uC81C",
97993
98449
  description: "\uAD6C\uC870\uD654\uB41C \uAE30\uC220\uC2A4\uD0DD \uD56D\uBAA9 \uD558\uB098\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC798\uBABB \uCD94\uCD9C/\uC785\uB825\uB41C \uAE30\uC220\uC744 \uC9C0\uC6B8 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_skills\uB85C \uBAA9\uB85D\uACFC id\uB97C \uD655\uC778\uD558\uACE0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. (\uB0B4\uC6A9 \uC218\uC815\uC740 add_skill\uB85C \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.) confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
97994
98450
  inputSchema: {
@@ -98037,6 +98493,7 @@ ${lines}`;
98037
98493
  },
98038
98494
  {
98039
98495
  name: "delete_cover_letter",
98496
+ destructive: true,
98040
98497
  title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC0AD\uC81C",
98041
98498
  description: "\uC0AC\uC6A9\uC790\uAC00 \uC800\uC7A5\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C \uC0AD\uC81C\uB97C \uBA85\uD655\uD788 \uC694\uCCAD\uD588\uC744 \uB54C\uB9CC \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uBA3C\uC800 get_cover_letters\uB85C \uC804\uCCB4 \uBAA9\uB85D \uB610\uB294 \uACF5\uACE0\uBCC4 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0, \uC0AD\uC81C\uD560 \uD56D\uBAA9\uC744 \uC0AC\uC6A9\uC790\uAC00 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD558\uC138\uC694. \uC0AD\uC81C\uD558\uBA74 \uD574\uB2F9 \uC790\uAE30\uC18C\uAC1C\uC11C\uC758 \uBAA8\uB4E0 \uBC84\uC804\uC774 \uD568\uAED8 \uC0AD\uC81C\uB418\uBA70 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
98042
98499
  inputSchema: {
@@ -98063,8 +98520,10 @@ ${lines}`;
98063
98520
  /* ------------------------------------------------------------------ jobs */
98064
98521
  {
98065
98522
  name: "save_job_posting",
98523
+ idempotent: true,
98524
+ // 같은 url 업서트 — 재호출 안전
98066
98525
  title: "\uCC44\uC6A9\uACF5\uACE0 \uC800\uC7A5",
98067
- description: "\uCC44\uC6A9\uACF5\uACE0\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4(\uC788\uC73C\uBA74 \uAC31\uC2E0). company\uC640 position\uC740 \uD544\uC218\uC785\uB2C8\uB2E4. \uACF5\uACE0 \uC6D0\uBB38\uC740 description\uC5D0, \uD575\uC2EC \uC790\uACA9\uC694\uAC74/\uC6B0\uB300\uC0AC\uD56D\uC740 requirements \uBC30\uC5F4\uC5D0, \uD575\uC2EC \uD0A4\uC6CC\uB4DC\uB294 keywords\uC5D0 \uC815\uB9AC\uD574 \uB123\uC73C\uBA74 \uC774\uD6C4 \uC801\uD569\uB3C4 \uBD84\uC11D/\uC790\uC18C\uC11C \uC791\uC131\uC5D0 \uD65C\uC6A9\uB429\uB2C8\uB2E4. \uD68C\uC0AC\uB97C \uB9AC\uC11C\uCE58\uD588\uB2E4\uBA74 \uC0AC\uC5C5\uAC1C\uC694\xB7\uBBF8\uC158\uC740 company_overview, \uD68C\uC0AC\uAC00 \uAC15\uC870\uD558\uB294 \uC778\uC7AC\uC0C1\uC740 talent_profile, \uD575\uC2EC\uAC00\uCE58\uB294 core_values\uC5D0 \uB123\uC73C\uC138\uC694 \u2014 \uD55C\uAD6D \uC790\uC18C\uC11C\xB7\uBA74\uC811\uC740 \uC778\uC7AC\uC0C1/\uD575\uC2EC\uAC00\uCE58 \uC815\uB82C\uC774 \uD575\uC2EC\uC774\uB77C \uC800\uC7A5\uD574\uB450\uBA74 \uC774\uD6C4 \uC791\uC131\xB7\uBA74\uC811 \uC900\uBE44\uC5D0\uC11C \uADF8\uB300\uB85C \uB04C\uC5B4\uB2E4 \uC501\uB2C8\uB2E4. \uAC19\uC740 url\uC774\uBA74 \uC911\uBCF5 \uC0DD\uC131 \uC5C6\uC774 \uAC31\uC2E0\uB429\uB2C8\uB2E4. \uC800\uC7A5 \uC2DC \uC9C0\uC6D0(application) \uD56D\uBAA9\uC774 \uC790\uB3D9\uC73C\uB85C \uC0DD\uC131\uB429\uB2C8\uB2E4.",
98526
+ description: "\uCC44\uC6A9\uACF5\uACE0\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4(\uC788\uC73C\uBA74 \uAC31\uC2E0). company\uC640 position\uC740 \uD544\uC218\uC785\uB2C8\uB2E4. \uACF5\uACE0 \uC6D0\uBB38\uC740 description\uC5D0, \uD575\uC2EC \uC790\uACA9\uC694\uAC74/\uC6B0\uB300\uC0AC\uD56D\uC740 requirements \uBC30\uC5F4\uC5D0, \uD575\uC2EC \uD0A4\uC6CC\uB4DC\uB294 keywords\uC5D0 \uC815\uB9AC\uD574 \uB123\uC73C\uBA74 \uC774\uD6C4 \uC801\uD569\uB3C4 \uBD84\uC11D/\uC790\uC18C\uC11C \uC791\uC131\uC5D0 \uD65C\uC6A9\uB429\uB2C8\uB2E4. \uD68C\uC0AC\uB97C \uB9AC\uC11C\uCE58\uD588\uB2E4\uBA74 \uC0AC\uC5C5\uAC1C\uC694\xB7\uBBF8\uC158\uC740 company_overview, \uD68C\uC0AC\uAC00 \uAC15\uC870\uD558\uB294 \uC778\uC7AC\uC0C1\uC740 talent_profile, \uD575\uC2EC\uAC00\uCE58\uB294 core_values\uC5D0 \uB123\uC73C\uC138\uC694 \u2014 \uD55C\uAD6D \uC790\uC18C\uC11C\xB7\uBA74\uC811\uC740 \uC778\uC7AC\uC0C1/\uD575\uC2EC\uAC00\uCE58 \uC815\uB82C\uC774 \uD575\uC2EC\uC774\uB77C \uC800\uC7A5\uD574\uB450\uBA74 \uC774\uD6C4 \uC791\uC131\xB7\uBA74\uC811 \uC900\uBE44\uC5D0\uC11C \uADF8\uB300\uB85C \uB04C\uC5B4\uB2E4 \uC501\uB2C8\uB2E4. \uD68C\uC0AC \uD3C9\uD310(\uD3C9\uC810\xB7\uC6CC\uB77C\uBC38\xB7\uBB38\uD654 \uB9AC\uC2A4\uD06C \uB4F1)\uC744 \uB9AC\uC11C\uCE58\uD588\uB2E4\uBA74 reputation\uC5D0 \uC694\uC57D(\uCD9C\uCC98\xB7\uCD5C\uADFC\uC131 \uD45C\uAE30)\uC744 \uB123\uC73C\uBA74 \uC624\uD37C \uD310\uB2E8\xB7\uBA74\uC811\uC5D0 \uD65C\uC6A9\uB429\uB2C8\uB2E4. \uD68C\uC0AC \uC9C0\uC815 \uC790\uC18C\uC11C \uBB38\uD56D\xB7\uAE00\uC790\uC218\uAC00 \uC788\uC73C\uBA74 questions \uBC30\uC5F4\uC5D0 \uAD6C\uC870\uD654 \uC800\uC7A5\uD558\uC138\uC694(question \uD544\uC218, max_chars/min_chars/count_mode). \uC774\uD6C4 \uC790\uC18C\uC11C \uC791\uC131 \uC2DC \uBB38\uD56D\uBCC4\uB85C validate_cover_letter(min_chars/max_chars/count_mode)\uB85C \uD55C\uB3C4\uB97C \uC810\uAC80\uD569\uB2C8\uB2E4. \uBA74\uC811\xB7\uC778\uC801\uC131\xB7\uBC1C\uD45C \uB4F1 \uC804\uD615 \uC77C\uC815\uC744 \uBC1B\uC73C\uBA74 schedule \uBC30\uC5F4\uC5D0 \uAD6C\uC870\uD654 \uC800\uC7A5\uD558\uC138\uC694(label\xB7date \uD544\uC218, time \uC120\uD0DD; \uC11C\uB958 \uB9C8\uAC10\uC740 deadline \uD544\uB4DC). \uBA74\uC811\uCD08\uB300 \uBA54\uC77C\uC744 \uC0AC\uC6A9\uC790\uAC00 \uBD99\uC5EC\uB123\uC73C\uBA74 \uB0A0\uC9DC\xB7\uC2DC\uAC04\uC744 \uCD94\uCD9C\uD574 \uC800\uC7A5\uD558\uACE0, \uAC19\uC740 \uB0A0 \uB2E4\uB978 \uC77C\uC815\uACFC \uACB9\uCE58\uBA74 \uC54C\uB824\uC8FC\uC138\uC694. \uAC19\uC740 url\uC774\uBA74 \uC911\uBCF5 \uC0DD\uC131 \uC5C6\uC774 \uAC31\uC2E0\uB429\uB2C8\uB2E4. \uC800\uC7A5 \uC2DC \uC9C0\uC6D0(application) \uD56D\uBAA9\uC774 \uC790\uB3D9\uC73C\uB85C \uC0DD\uC131\uB429\uB2C8\uB2E4.",
98068
98527
  inputSchema: JobInputSchema.shape,
98069
98528
  handler: (args) => {
98070
98529
  const { job } = saveJobPosting(args);
@@ -98110,6 +98569,7 @@ ${lines}`;
98110
98569
  },
98111
98570
  {
98112
98571
  name: "delete_job_posting",
98572
+ destructive: true,
98113
98573
  title: "\uCC44\uC6A9\uACF5\uACE0 \uC0AD\uC81C",
98114
98574
  description: "\uC0AC\uC6A9\uC790\uAC00 \uC800\uC7A5\uB41C \uCC44\uC6A9\uACF5\uACE0 \uC0AD\uC81C\uB97C \uBA85\uD655\uD788 \uC694\uCCAD\uD588\uC744 \uB54C\uB9CC \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uBA3C\uC800 list_jobs\uB85C \uC804\uCCB4 \uACF5\uACE0 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0, \uC0AD\uC81C\uD560 \uACF5\uACE0\uB97C \uC0AC\uC6A9\uC790\uAC00 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD558\uC138\uC694. \uC0AD\uC81C\uD558\uBA74 \uC5F0\uACB0\uB41C \uC9C0\uC6D0 \uC0C1\uD0DC\xB7\uC801\uD569\uB3C4 \uBD84\uC11D\xB7\uBA74\uC811 \uC900\uBE44\uAC00 \uD568\uAED8 \uC0AD\uC81C\uB429\uB2C8\uB2E4. \uC5F0\uACB0\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C\uB294 \uC0AD\uC81C\uD558\uC9C0 \uC54A\uACE0 \uACF5\uACE0 \uC5F0\uACB0\uB9CC \uD574\uC81C\uD569\uB2C8\uB2E4. confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
98115
98575
  inputSchema: {
@@ -98195,6 +98655,8 @@ ${lines}`;
98195
98655
  },
98196
98656
  {
98197
98657
  name: "save_fit_analysis",
98658
+ idempotent: true,
98659
+ // 공고당 1건 업서트
98198
98660
  title: "\uC801\uD569\uB3C4 \uBD84\uC11D \uACB0\uACFC \uC800\uC7A5",
98199
98661
  description: "\uACF5\uACE0\uC640 \uC0AC\uC6A9\uC790 \uC815\uBCF4\uB97C \uBE44\uAD50\uD574 \uB2F9\uC2E0(AI)\uC774 \uB3C4\uCD9C\uD55C \uC801\uD569\uB3C4 \uBD84\uC11D\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. job_id\uB294 \uD544\uC218\uC774\uBA70 \uBA3C\uC800 save_job_posting\uC73C\uB85C \uACF5\uACE0\uAC00 \uC800\uC7A5\uB418\uC5B4 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4. score(0~100), summary, strengths(\uAC15\uC810), gaps(\uBCF4\uC644 \uD544\uC694), matched/missing_keywords, recommendations(\uC790\uC18C\uC11C\xB7\uC9C0\uC6D0 \uC804\uB7B5 \uC81C\uC548)\uB97C \uCC44\uC6CC \uC800\uC7A5\uD558\uC138\uC694. \uAC19\uC740 \uACF5\uACE0\uC5D0 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.",
98200
98662
  inputSchema: FitAnalysisInputSchema.shape,
@@ -98239,27 +98701,41 @@ ${lines}`;
98239
98701
  {
98240
98702
  name: "update_application_status",
98241
98703
  title: "\uC9C0\uC6D0 \uC0C1\uD0DC \uBCC0\uACBD",
98242
- description: `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C \uBCC0\uACBD\uD569\uB2C8\uB2E4. \uAC00\uB2A5\uD55C \uC0C1\uD0DC: ${STATUS_LIST}. '\uC11C\uB958 \uD569\uACA9(document_passed)' \uC774\uC0C1\uC73C\uB85C \uBC14\uB00C\uBA74 \uBA74\uC811 \uC900\uBE44\uB97C \uC81C\uC548\uD558\uB294 \uD78C\uD2B8\uB97C \uD568\uAED8 \uB3CC\uB824\uC90D\uB2C8\uB2E4. note\uC5D0 \uBCC0\uACBD \uC0AC\uC720\uB97C \uB0A8\uAE38 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`,
98704
+ description: `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C \uBCC0\uACBD\uD569\uB2C8\uB2E4. \uAC00\uB2A5\uD55C \uC0C1\uD0DC: ${STATUS_LIST}. '\uC11C\uB958 \uD569\uACA9(document_passed)' \uC774\uC0C1\uC73C\uB85C \uBC14\uB00C\uBA74 \uBA74\uC811 \uC900\uBE44\uB97C \uC81C\uC548\uD558\uB294 \uD78C\uD2B8\uB97C \uD568\uAED8 \uB3CC\uB824\uC90D\uB2C8\uB2E4. note\uC5D0 \uBCC0\uACBD \uC0AC\uC720\uB97C \uB0A8\uAE38 \uC218 \uC788\uC2B5\uB2C8\uB2E4. '\uBD88\uD569\uACA9(rejected)'\uC73C\uB85C \uBC14\uAFC0 \uB54C\uB294 rejected_stage\uC5D0 \uC5B4\uB290 \uB2E8\uACC4\uC5D0\uC11C \uC544\uC27D\uAC8C \uB410\uB294\uC9C0 \uD568\uAED8 \uB0A8\uAE30\uC138\uC694 \u2014 \uC11C\uB958=document, \uC778\uC801\uC131\xB7AI\uC5ED\uAC80=assessment, \uBA74\uC811=interview, \uCD5C\uC885\xB7\uCC98\uC6B0=final, \uAE30\uD0C0=other. \uD0C8\uB77D \uC0AC\uC720\uB97C \uC54C\uB824\uC8FC\uB294 \uAE30\uC5C5\uC740 \uB4DC\uBB3C\uC5B4, \uC774 \uC790\uAC00 \uAE30\uB85D\uC774 \uC313\uC774\uBA74 '\uC11C\uB958\uB294 \uBD99\uB294\uB370 \uBA74\uC811\uC5D0\uC11C \uB5A8\uC5B4\uC9C4\uB2E4' \uAC19\uC740 \uD328\uD134\uC774 \uB85C\uCEEC \uB370\uC774\uD130\uB85C \uB4DC\uB7EC\uB0A9\uB2C8\uB2E4(get_pipeline_stats).`,
98243
98705
  inputSchema: {
98244
98706
  job_id: external_exports.string(),
98245
98707
  status: external_exports.enum(APPLICATION_STATUSES),
98246
98708
  note: external_exports.string().optional(),
98247
- submission: ApplicationSubmissionSchema.optional()
98709
+ submission: ApplicationSubmissionSchema.optional(),
98710
+ rejected_stage: external_exports.enum(REJECTED_STAGES).optional().describe(
98711
+ "\uD0C8\uB77D \uD68C\uACE0 \u2014 'rejected'\uB85C \uBC14\uAFC0 \uB54C \uC5B4\uB290 \uB2E8\uACC4\uC5D0\uC11C \uD0C8\uB77D\uD588\uB294\uC9C0: \uC11C\uB958=document, \uC778\uC801\uC131\xB7AI\uC5ED\uAC80=assessment, \uBA74\uC811=interview, \uCD5C\uC885\xB7\uCC98\uC6B0=final, \uAE30\uD0C0=other"
98712
+ )
98248
98713
  },
98249
98714
  handler: (args) => {
98250
98715
  try {
98251
- const res = updateApplicationStatus(args.job_id, args.status, args.note, args.submission);
98252
- const user_message = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.` + (res.hint ? `
98716
+ const res = updateApplicationStatus(args.job_id, args.status, args.note, args.submission, args.rejected_stage);
98717
+ const needsRetro = args.status === "rejected" && !res.application.rejected_stage;
98718
+ const retro_prompt = "\uC5B4\uB290 \uB2E8\uACC4\uC5D0\uC11C \uC544\uC27D\uAC8C \uB410\uB294\uC9C0(\uC11C\uB958/\uC778\uC801\uC131/\uBA74\uC811/\uCD5C\uC885)\uC640 \uBC30\uC6B4 \uC810\uC744 \uB0A8\uACA8\uB450\uBA74 \uB2E4\uC74C \uC9C0\uC6D0 \uC804\uB7B5\uC5D0 \uC4F8 \uC218 \uC788\uC5B4\uC694. \uAE30\uB85D\uD560\uAE4C\uC694?";
98719
+ const base = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.`;
98720
+ const user_message = base + (res.hint ? `
98721
+
98722
+ ${res.hint}` : needsRetro ? `
98723
+
98724
+ ${retro_prompt}` : "");
98725
+ const text = needsRetro ? `${user_message}
98253
98726
 
98254
- ${res.hint}` : "");
98727
+ (AI \uC548\uB0B4: \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC5B4\uB290 \uB2E8\uACC4\uC5D0\uC11C \uB5A8\uC5B4\uC84C\uB294\uC9C0\uC640 \uBC30\uC6B4 \uC810\uC744 \uBB3C\uC5B4, rejected_stage\uC640 note\uB85C \uD568\uAED8 \uAE30\uB85D\uD558\uB3C4\uB85D \uC81C\uC548\uD558\uC138\uC694.)` : user_message;
98255
98728
  return ok(
98256
- user_message,
98729
+ text,
98257
98730
  {
98258
98731
  ...res,
98259
98732
  user_message,
98260
98733
  suggested_next_action: res.hint ? {
98261
98734
  prompt: res.hint,
98262
98735
  next_workflow: "prepare_interview"
98736
+ } : needsRetro ? {
98737
+ prompt: retro_prompt,
98738
+ next_tool: "update_application_status"
98263
98739
  } : null
98264
98740
  }
98265
98741
  );
@@ -98271,6 +98747,8 @@ ${res.hint}` : "");
98271
98747
  /* ---------------------------------------------------------- interview prep */
98272
98748
  {
98273
98749
  name: "save_interview_prep",
98750
+ idempotent: true,
98751
+ // 공고당 1건 업서트(debrief는 병합)
98274
98752
  title: "\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC \uC800\uC7A5",
98275
98753
  description: "\uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38, \uAF2C\uB9AC \uC9C8\uBB38, STAR \uB2F5\uBCC0 \uAC00\uC774\uB4DC, 1\uBD84 \uC790\uAE30\uC18C\uAC1C \uCD08\uC548 \uB4F1 \uD574\uB2F9 \uACF5\uACE0 \uAE30\uC900 \uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. \uACF5\uACE0/\uC9C1\uBB34/\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uADFC\uAC70\uB85C \uB2F9\uC2E0(AI)\uC774 \uC0DD\uC131\uD55C \uC790\uB8CC\uB97C \uC800\uC7A5\uD558\uB294 \uB370 \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uC9C8\uBB38\uC740 \uB450 \uAC08\uB798\uB85C \uC900\uBE44\uD558\uC138\uC694 \u2014 \uAC01 \uC9C8\uBB38\uC758 category\uB97C 'technical'(\uAE30\uC220\xB7\uC9C1\uBB34 1\uCC28 \uBA74\uC811) \uB610\uB294 'behavioral'(\uC778\uC131\xB7\uCEEC\uCC98\uD54F\xB7\uC784\uC6D0 2\uCC28 \uBA74\uC811, \uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uD504\uB85C\uD544 \uAE30\uBC18)\uB85C \uC9C0\uC815\uD569\uB2C8\uB2E4. \uAC00\uB2A5\uD558\uBA74 \uB450 \uAC08\uB798 \uBAA8\uB450 \uCC44\uC6CC \uD55C \uBC88\uC5D0 \uC800\uC7A5\uD558\uC138\uC694(\uB300\uC2DC\uBCF4\uB4DC\uAC00 \uAE30\uC220/\uC778\uC131\xB7\uCEEC\uCC98\uD54F \uD0ED\uC73C\uB85C \uB098\uB220 \uBCF4\uC5EC\uC90D\uB2C8\uB2E4). \uBA74\uC811\uC744 \uBCF8 \uB4A4\uC5D0\uB294 \uAC19\uC740 \uB3C4\uAD6C\uC5D0 debrief(\uBC1B\uC740 \uC9C8\uBB38 asked_questions\xB7\uC798\uB41C \uC810 went_well\xB7\uAC1C\uC120\uC810 to_improve\xB7\uB2E4\uC74C \uB77C\uC6B4\uB4DC \uD3EC\uCEE4\uC2A4 next_round_focus\xB7\uCCB4\uAC10 \uBD84\uC704\uAE30 sentiment)\uB97C \uCC44\uC6CC \uC800\uC7A5\uD558\uBA74 \uB2E4\uC74C \uB77C\uC6B4\uB4DC \uC900\uBE44\uC640 \uC77C\uAD00\uC131 \uC810\uAC80\uC5D0 \uADF8\uB300\uB85C \uC774\uC5B4\uC9D1\uB2C8\uB2E4(\uBA74\uC811 \uC804 \uC900\uBE44\uC640 \uBA74\uC811 \uD6C4 \uB514\uBE0C\uB9AC\uD504\uAC00 \uD55C \uACF5\uACE0\uC5D0 \uD568\uAED8 \uC313\uC785\uB2C8\uB2E4). job_id\uB294 \uD544\uC218\uC785\uB2C8\uB2E4. \uAC19\uC740 \uACF5\uACE0\uC5D0 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4(debrief\uB294 \uAE30\uC874 \uAC12\uACFC \uBCD1\uD569\uB418\uC5B4 \uAE30\uC874 \uD544\uB4DC\uB97C \uC9C0\uC6B0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4).",
98276
98754
  inputSchema: InterviewPrepInputSchema.shape,
@@ -98404,20 +98882,22 @@ ${res.hint}` : "");
98404
98882
  {
98405
98883
  name: "get_followups",
98406
98884
  title: "\uD6C4\uC18D \uC870\uCE58 \uC54C\uB9BC(\uB193\uCE5C \uC9C0\uC6D0\xB7\uB9C8\uAC10\xB7\uBA74\uC811\uC900\uBE44)",
98407
- description: '\uC9C0\uAE08 \uCC59\uACA8\uC57C \uD560 \uAC83\uB4E4\uC744 \uD55C \uBC88\uC5D0 \uB3CC\uB824\uC90D\uB2C8\uB2E4: \u2460 \uC751\uB2F5 \uC5C6\uC774 \uC624\uB798\uB41C \uC9C0\uC6D0(\uC9C0\uC6D0 \uD6C4 \uBB34\uC751\uB2F5\xB7\uC11C\uB958\uD569\uACA9 \uD6C4 \uC77C\uC815\uB300\uAE30\xB7\uBA74\uC811 \uD6C4 \uACB0\uACFC\uB300\uAE30) \u2461 \uC784\uBC15\uD588\uAC70\uB098 \uC9C0\uB09C \uC9C0\uC6D0 \uB9C8\uAC10(\uC9C0\uC6D0 \uC804 \uB2E8\uACC4) \u2462 \uD569\uACA9\uD588\uB294\uB370 \uC544\uC9C1 \uBA74\uC811 \uC900\uBE44\uAC00 \uC5C6\uB294 \uACF5\uACE0. \uC0AC\uC6A9\uC790\uAC00 "\uB0B4 \uC9C0\uC6D0 \uD604\uD669 \uC5B4\uB54C?", "\uBB50 \uCC59\uACA8\uC57C \uD574?", "\uD6C4\uC18D \uC5F0\uB77D\uD560 \uAC70 \uC788\uC5B4?"\uB77C\uACE0 \uBB3B\uAC70\uB098, \uC138\uC158\uC744 \uC774\uC5B4\uAC00\uBA70 \uB193\uCE5C \uAC8C \uC5C6\uB294\uC9C0 \uBA3C\uC800 \uC810\uAC80\uD560 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4. \uC751\uB2F5\uC758 reason\uC740 \uC0AC\uC6A9\uC790 \uC5B8\uC5B4\uB85C \uD480\uC5B4 \uC804\uD558\uACE0, \uD6C4\uC18D \uBB38\uC758\uB294 \uAC15\uC694\uD558\uC9C0 \uB9D0\uACE0 \uC120\uD0DD\uC9C0\uB85C \uC81C\uC548\uD558\uC138\uC694(\uCC44\uC6A9 \uAD00\uD589\uC0C1 \uACFC\uD55C \uB3C5\uCD09\uC740 \uC5ED\uD6A8\uACFC).',
98885
+ description: '\uC9C0\uAE08 \uCC59\uACA8\uC57C \uD560 \uAC83\uB4E4\uC744 \uD55C \uBC88\uC5D0 \uB3CC\uB824\uC90D\uB2C8\uB2E4: \u2460 \uC751\uB2F5 \uC5C6\uC774 \uC624\uB798\uB41C \uC9C0\uC6D0(\uC9C0\uC6D0 \uD6C4 \uBB34\uC751\uB2F5\xB7\uC11C\uB958\uD569\uACA9 \uD6C4 \uC77C\uC815\uB300\uAE30\xB7\uBA74\uC811 \uD6C4 \uACB0\uACFC\uB300\uAE30) \u2461 \uC784\uBC15\uD588\uAC70\uB098 \uC9C0\uB09C \uC9C0\uC6D0 \uB9C8\uAC10(\uC9C0\uC6D0 \uC804 \uB2E8\uACC4) \u2462 \uD569\uACA9\uD588\uB294\uB370 \uC544\uC9C1 \uBA74\uC811 \uC900\uBE44\uAC00 \uC5C6\uB294 \uACF5\uACE0 \u2463 \uC624\uB298~7\uC77C \uB0B4 \uB2E4\uAC00\uC624\uB294 \uC804\uD615 \uC77C\uC815(\uBA74\uC811\xB7\uC778\uC801\uC131\xB7\uBC1C\uD45C; `upcoming_schedule`\uC5D0 \uAC01 \uC77C\uC815\uC758 D-day) \u2464 \uC11C\uB85C \uB2E4\uB978 \uACF5\uACE0\uC758 \uC77C\uC815\uC774 \uAC19\uC740 \uB0A0\uC9DC\uC5D0 \uACB9\uCE60 \uB54C(`schedule_conflicts` \u2014 \uAC19\uC740 \uACF5\uACE0\uC758 \uC5F0\uC18D \uB2E8\uACC4\uB294 \uCDA9\uB3CC\uC774 \uC544\uB2C8\uACE0, \uC11C\uB85C \uB2E4\uB978 \uACF5\uACE0 2\uAC74 \uC774\uC0C1\uC774 \uD55C \uB0A0\uC5D0 \uBAB0\uB9B4 \uB54C\uB9CC; \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC54C\uB824 \uC870\uC815 \uC81C\uC548). \uC0AC\uC6A9\uC790\uAC00 "\uB0B4 \uC9C0\uC6D0 \uD604\uD669 \uC5B4\uB54C?", "\uBB50 \uCC59\uACA8\uC57C \uD574?", "\uC774\uBC88 \uC8FC \uC77C\uC815 \uBB50 \uC788\uC5B4?", "\uD6C4\uC18D \uC5F0\uB77D\uD560 \uAC70 \uC788\uC5B4?"\uB77C\uACE0 \uBB3B\uAC70\uB098, \uC138\uC158\uC744 \uC774\uC5B4\uAC00\uBA70 \uB193\uCE5C \uAC8C \uC5C6\uB294\uC9C0 \uBA3C\uC800 \uC810\uAC80\uD560 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4. \uC9C0\uC6D0(applied) \uD56D\uBAA9\uC5D0\uB294 \uACB0\uC815\uB860 \uD314\uB85C\uC5C5 \uD0C0\uC774\uBC0D\uC774 \uBD99\uC2B5\uB2C8\uB2E4: `followup_timing="due"`\uB294 \uC9C0\uC6D0 \uD6C4 5~7\uC601\uC5C5\uC77C(\uC8FC\uB9D0 \uC81C\uC678)\uB85C \uC815\uC911\uD55C \uD6C4\uC18D \uBB38\uC758 \uC801\uAE30, `"overdue"`\uB294 8\uC601\uC5C5\uC77C \uC774\uC0C1\uC73C\uB85C \uC801\uAE30\uAC00 \uC9C0\uB0AC\uB2E4\uB294 \uB73B\uC774\uBA70, `business_days_since`\uB294 \uACBD\uACFC \uC601\uC5C5\uC77C \uC218\uC785\uB2C8\uB2E4. \uB2EC\uB825\uC77C 45\uC77C \uC774\uC0C1 \uBB34\uC751\uB2F5\uC774\uBA74 `suggest_on_hold=true`\uAC00 \uBD99\uC73C\uB2C8 "\uBCF4\uB958(on_hold)\uB85C \uC62E\uAE38\uAE4C\uC694?"\uB77C\uACE0 \uC81C\uC548\uD558\uC138\uC694. \uC751\uB2F5\uC758 reason\uC740 \uC0AC\uC6A9\uC790 \uC5B8\uC5B4\uB85C \uD480\uC5B4 \uC804\uD558\uACE0, \uD6C4\uC18D \uBB38\uC758\uB294 \uAC15\uC694\uD558\uC9C0 \uB9D0\uACE0 \uC120\uD0DD\uC9C0\uB85C \uC81C\uC548\uD558\uC138\uC694(\uCC44\uC6A9 \uAD00\uD589\uC0C1 \uACFC\uD55C \uB3C5\uCD09\uC740 \uC5ED\uD6A8\uACFC \u2014 \uBB38\uC758\uB294 \uC815\uC911\uD788 1\uD68C).',
98408
98886
  inputSchema: {},
98409
98887
  readOnly: true,
98410
98888
  handler: () => {
98411
98889
  const d = getActionDigest();
98412
- const n = d.followups.length + d.deadlines.length + d.interview_todo.length;
98413
- const msg = n === 0 ? "\uC9C0\uAE08 \uB2F9\uC7A5 \uCC59\uAE38 \uD6C4\uC18D \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(\uBB34\uC751\uB2F5 \uC9C0\uC6D0\xB7\uC784\uBC15 \uB9C8\uAC10\xB7\uBA74\uC811 \uC900\uBE44 \uB300\uAE30 \uC5C6\uC74C)." : `\uCC59\uAE38 \uD56D\uBAA9 ${n}\uAC74: \uD6C4\uC18D ${d.followups.length} \xB7 \uB9C8\uAC10 ${d.deadlines.length} \xB7 \uBA74\uC811\uC900\uBE44 ${d.interview_todo.length}`;
98890
+ const n = d.followups.length + d.deadlines.length + d.interview_todo.length + d.upcoming_schedule.length;
98891
+ const parts = [`\uD6C4\uC18D ${d.followups.length}`, `\uB9C8\uAC10 ${d.deadlines.length}`, `\uBA74\uC811\uC900\uBE44 ${d.interview_todo.length}`, `\uC77C\uC815 ${d.upcoming_schedule.length}`];
98892
+ if (d.schedule_conflicts.length) parts.push(`\uC77C\uC815\uCDA9\uB3CC ${d.schedule_conflicts.length}`);
98893
+ const msg = n === 0 && d.schedule_conflicts.length === 0 ? "\uC9C0\uAE08 \uB2F9\uC7A5 \uCC59\uAE38 \uD6C4\uC18D \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(\uBB34\uC751\uB2F5 \uC9C0\uC6D0\xB7\uC784\uBC15 \uB9C8\uAC10\xB7\uBA74\uC811 \uC900\uBE44 \uB300\uAE30\xB7\uB2E4\uAC00\uC624\uB294 \uC77C\uC815 \uC5C6\uC74C)." : `\uCC59\uAE38 \uD56D\uBAA9: ${parts.join(" \xB7 ")}`;
98414
98894
  return ok(msg, d);
98415
98895
  }
98416
98896
  },
98417
98897
  {
98418
98898
  name: "get_pipeline_stats",
98419
98899
  title: "\uC9C0\uC6D0 \uD30C\uC774\uD504\uB77C\uC778 \uBD84\uC11D(\uAE54\uB54C\uAE30\xB7\uD569\uACA9\uB960)",
98420
- description: '\uC804\uCCB4 \uC9C0\uC6D0 \uD604\uD669\uC744 \uBD84\uC11D\uD574 \uB3CC\uB824\uC90D\uB2C8\uB2E4: \uD604\uC7AC \uC0C1\uD0DC \uBD84\uD3EC(by_status), \uB2E8\uACC4\uBCC4 \uB3C4\uB2EC \uAE54\uB54C\uAE30(funnel: \uC9C0\uC6D0\u2192\uC11C\uB958\uD569\uACA9\u2192\uBA74\uC811\u2192\uCD5C\uC885, \uC0C1\uD0DC \uBCC0\uACBD \uC774\uB825\uC73C\uB85C \uBCF5\uC6D0\uD55C \uC2E4\uCE21\uCE58\uB77C \uD604\uC7AC \uBD88\uD569\uACA9\uC774\uC5B4\uB3C4 \uAC70\uCCD0\uAC04 \uCD5C\uACE0 \uB2E8\uACC4\uAE4C\uC9C0 \uC9D1\uACC4), \uD1B5\uACFC\uC728(rates: \uC11C\uB958\xB7\uBA74\uC811\xB7\uCD5C\uC885, \uC9C0\uC6D0 \uB300\uBE44 %), \uACB0\uACFC \uC694\uC57D(outcomes). \uC0AC\uC6A9\uC790\uAC00 "\uB0B4 \uD569\uACA9\uB960 \uC5B4\uB54C?", "\uC9C0\uC6D0 \uD1B5\uACC4 \uBCF4\uC5EC\uC918", "\uC5B4\uB290 \uB2E8\uACC4\uC5D0\uC11C \uB9CE\uC774 \uB5A8\uC5B4\uC838?"\uB77C\uACE0 \uBB3C\uC744 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBE44\uC728\uC740 \uC124\uBA85\uC6A9\uC774\uBA70 \uD45C\uBCF8\uC774 \uC791\uC73C\uBA74 \uC608\uCE21\uB825\uC774 \uB0AE\uB2E4\uB294 \uC810\uC744 \uD568\uAED8 \uC804\uD558\uC138\uC694(note \uCC38\uACE0). \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4.',
98900
+ description: '\uC804\uCCB4 \uC9C0\uC6D0 \uD604\uD669\uC744 \uBD84\uC11D\uD574 \uB3CC\uB824\uC90D\uB2C8\uB2E4: \uD604\uC7AC \uC0C1\uD0DC \uBD84\uD3EC(by_status), \uB2E8\uACC4\uBCC4 \uB3C4\uB2EC \uAE54\uB54C\uAE30(funnel: \uC9C0\uC6D0\u2192\uC11C\uB958\uD569\uACA9\u2192\uBA74\uC811\u2192\uCD5C\uC885, \uC0C1\uD0DC \uBCC0\uACBD \uC774\uB825\uC73C\uB85C \uBCF5\uC6D0\uD55C \uC2E4\uCE21\uCE58\uB77C \uD604\uC7AC \uBD88\uD569\uACA9\uC774\uC5B4\uB3C4 \uAC70\uCCD0\uAC04 \uCD5C\uACE0 \uB2E8\uACC4\uAE4C\uC9C0 \uC9D1\uACC4), \uD1B5\uACFC\uC728(rates: \uC11C\uB958\xB7\uBA74\uC811\xB7\uCD5C\uC885, \uC9C0\uC6D0 \uB300\uBE44 %), \uACB0\uACFC \uC694\uC57D(outcomes), \uD0C8\uB77D \uB2E8\uACC4 \uBD84\uD3EC(rejected_by_stage: \uC11C\uB958\xB7\uC778\uC801\uC131\xB7\uBA74\uC811\xB7\uCD5C\uC885\xB7\uAE30\uD0C0\uBCC4 \uD0C8\uB77D \uAC74\uC218 + rejected_unrecorded: \uB2E8\uACC4 \uBBF8\uAE30\uB85D \uC218). \uC0AC\uC6A9\uC790\uAC00 "\uB0B4 \uD569\uACA9\uB960 \uC5B4\uB54C?", "\uC9C0\uC6D0 \uD1B5\uACC4 \uBCF4\uC5EC\uC918", "\uC5B4\uB290 \uB2E8\uACC4\uC5D0\uC11C \uB9CE\uC774 \uB5A8\uC5B4\uC838?"\uB77C\uACE0 \uBB3C\uC744 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBE44\uC728\uC740 \uC124\uBA85\uC6A9\uC774\uBA70 \uD45C\uBCF8\uC774 \uC791\uC73C\uBA74 \uC608\uCE21\uB825\uC774 \uB0AE\uB2E4\uB294 \uC810\uC744 \uD568\uAED8 \uC804\uD558\uC138\uC694(note \uCC38\uACE0). \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4.',
98421
98901
  inputSchema: {},
98422
98902
  readOnly: true,
98423
98903
  handler: () => {
@@ -98425,14 +98905,19 @@ ${res.hint}` : "");
98425
98905
  if (s.total === 0) return ok("\uC544\uC9C1 \uC9C0\uC6D0 \uB0B4\uC5ED\uC774 \uC5C6\uC5B4 \uBD84\uC11D\uD560 \uD1B5\uACC4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", s);
98426
98906
  const r = s.rates;
98427
98907
  const fmt = (v) => v == null ? "\u2014" : `${v}%`;
98908
+ const rejParts = s.rejected_by_stage.map((x2) => `${REJECTED_STAGE_LABELS[x2.stage]} ${x2.count}`);
98909
+ if (s.rejected_unrecorded > 0) rejParts.push(`\uBBF8\uAE30\uB85D ${s.rejected_unrecorded}\uAC74 \u2014 \uAE30\uB85D\uD558\uBA74 \uD328\uD134\uC774 \uBCF4\uC785\uB2C8\uB2E4`);
98910
+ const rejLine = rejParts.length ? ` \xB7 \uD0C8\uB77D \uBD84\uD3EC: ${rejParts.join("\xB7")}` : "";
98428
98911
  return ok(
98429
- `\uC9C0\uC6D0 ${s.total}\uAC74 \xB7 \uAE54\uB54C\uAE30 \uC9C0\uC6D0 ${s.funnel.applied}\u2192\uC11C\uB958 ${s.funnel.document_passed}\u2192\uBA74\uC811 ${s.funnel.interview}\u2192\uCD5C\uC885 ${s.funnel.final_passed} \xB7 \uD1B5\uACFC\uC728 \uC11C\uB958 ${fmt(r.document_pass)}\xB7\uBA74\uC811 ${fmt(r.interview)}\xB7\uCD5C\uC885 ${fmt(r.offer)} (\uD45C\uBCF8 \uC791\uC73C\uBA74 \uCD94\uC138 \uCC38\uACE0\uB85C\uB9CC)`,
98912
+ `\uC9C0\uC6D0 ${s.total}\uAC74 \xB7 \uAE54\uB54C\uAE30 \uC9C0\uC6D0 ${s.funnel.applied}\u2192\uC11C\uB958 ${s.funnel.document_passed}\u2192\uBA74\uC811 ${s.funnel.interview}\u2192\uCD5C\uC885 ${s.funnel.final_passed} \xB7 \uD1B5\uACFC\uC728 \uC11C\uB958 ${fmt(r.document_pass)}\xB7\uBA74\uC811 ${fmt(r.interview)}\xB7\uCD5C\uC885 ${fmt(r.offer)}${rejLine} (\uD45C\uBCF8 \uC791\uC73C\uBA74 \uCD94\uC138 \uCC38\uACE0\uB85C\uB9CC)`,
98430
98913
  s
98431
98914
  );
98432
98915
  }
98433
98916
  },
98434
98917
  {
98435
98918
  name: "search_jobs",
98919
+ // 외부 공개 잡보드(원티드·점핏)로 검색어가 나간다 — openWorld를 정직하게 표시.
98920
+ openWorld: true,
98436
98921
  title: "\uACF5\uACE0 \uAC80\uC0C9\xB7\uAC00\uC838\uC624\uAE30(\uD0A4\uC6CC\uB4DC)",
98437
98922
  description: `\uD0A4\uC6CC\uB4DC\uB85C \uBB34\uC778\uC99D \uACF5\uAC1C \uCC44\uC6A9 \uC0AC\uC774\uD2B8(${JOB_SEARCH_SOURCES.join("\xB7")})\uC5D0\uC11C \uCC44\uC6A9 \uACF5\uACE0\uB97C \uAC00\uC838\uC635\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uBC31\uC5D4\uB4DC \uC2E0\uC785 \uACF5\uACE0 \uCC3E\uC544\uC918", "React \uACF5\uACE0 \uAC00\uC838\uC640"\uCC98\uB7FC \uACF5\uACE0\uB97C \uCC3E\uC544 \uB2EC\uB77C\uACE0 \uD560 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uACB0\uACFC(\uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uB9C1\uD06C\xB7\uC9C0\uC5ED\xB7\uACBD\uB825\xB7\uAE30\uC220\uC2A4\uD0DD\xB7\uB9C8\uAC10)\uB97C \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uAE30 \uC88B\uAC8C \uBCF4\uC5EC \uC8FC\uACE0, **\uC0AC\uC6A9\uC790\uAC00 \uAD00\uC2EC \uC788\uB2E4\uACE0 \uACE0\uB978 \uAC83\uB9CC** save_job_posting\uC73C\uB85C \uC800\uC7A5\uD558\uC138\uC694(\uC804\uBD80 \uC790\uB3D9 \uC800\uC7A5\uD558\uC9C0 \uB9C8\uC138\uC694). \uC800\uC7A5\uD55C \uACF5\uACE0\uB294 \uC774\uC5B4\uC11C get_application_context\uB85C \uBD84\uC11D\xB7\uC790\uC18C\uC11C\uB85C \uC5F0\uACB0\uB429\uB2C8\uB2E4. \uCC38\uACE0: \uC774 \uB3C4\uAD6C\uB294 \uC678\uBD80 \uACF5\uAC1C \uD398\uC774\uC9C0\uC5D0\uC11C \uACF5\uACE0\uB97C \uAC00\uC838\uC624\uAE30\uB9CC \uD558\uBA70(\uBCF4\uB0B4\uB294 \uAC83\uC740 \uAC80\uC0C9 \uD0A4\uC6CC\uB4DC\uBFD0, \uC0AC\uC6A9\uC790 \uB370\uC774\uD130\uB294 \uC808\uB300 \uC804\uC1A1\uD558\uC9C0 \uC54A\uC74C), \uBD07 \uCC28\uB2E8 \uC6B0\uD68C\uB294 \uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC0AC\uB78C\uC778\xB7\uC7A1\uCF54\uB9AC\uC544\uB294 \uD0A4 \uBC1C\uAE09 \uB610\uB294 \uC57D\uAD00 \uC81C\uC57D\uC73C\uB85C \uC5EC\uAE30 \uD3EC\uD568\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uACB0\uACFC\uAC00 \uBE44\uC5B4 \uC788\uC73C\uBA74 \uD0A4\uC6CC\uB4DC\uB97C \uBC14\uAFB8\uAC70\uB098 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uACF5\uACE0 URL/\uBCF8\uBB38\uC744 \uBC1B\uC544 save_job_posting\uC73C\uB85C \uC800\uC7A5\uD558\uC138\uC694.`,
98438
98923
  inputSchema: {
@@ -98454,16 +98939,72 @@ ${res.hint}` : "");
98454
98939
  {
98455
98940
  name: "prescreen_job",
98456
98941
  title: "\uACF5\uACE0 \uC0AC\uC804 \uD504\uB9AC\uC2A4\uD06C\uB9B0(\uD0A4\uC6CC\uB4DC \uACB9\uCE68)",
98457
- description: '\uC800\uC7A5\uB41C \uACF5\uACE0\uC758 \uD575\uC2EC \uD0A4\uC6CC\uB4DC\uB97C \uC0AC\uC6A9\uC790\uC758 \uBCF4\uC720 \uAE30\uC220\xB7\uD504\uB85C\uC81D\uD2B8/\uACBD\uB825 \uAE30\uC220\uC2A4\uD0DD\uACFC \uB300\uC870\uD574 "\uC774 \uACF5\uACE0\uB97C \uAE4A\uAC8C \uBCFC \uAC00\uCE58\uAC00 \uC788\uB294\uC9C0"\uB97C \uBE60\uB974\uAC8C \uAC00\uB2A0\uD569\uB2C8\uB2E4. LLM \uC5C6\uC774 \uC140 \uC218 \uC788\uB294 \uD0A4\uC6CC\uB4DC \uACB9\uCE68(matched/missing/coverage)\uB9CC \uB3CC\uB824\uC8FC\uB294 \uAC12\uC2FC \uC0AC\uC804 \uD544\uD130\uC774\uBA70, **\uC9C4\uC9DC \uC801\uD569\uB3C4 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 get_application_context\uB85C \uB9E5\uB77D\uC744 \uBC1B\uC544 \uC9C1\uC811 \uC218\uD589**\uD569\uB2C8\uB2E4(\uC774 \uB3C4\uAD6C\uB294 \uADF8 \uB300\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4). \uC0AC\uC6A9\uC790\uAC00 \uACF5\uACE0\uB97C \uC5EC\uB7EC \uAC1C \uD6D1\uC73C\uBA70 \uC6B0\uC120\uC21C\uC704\uB97C \uC815\uD558\uAC70\uB098 "\uC774\uAC70 \uC9C0\uC6D0\uD560 \uB9CC\uD574?"\uB77C\uACE0 \uBB3C\uC744 \uB54C, \uBCF8\uACA9 \uBD84\uC11D \uC804\uC5D0 \uAC00\uBCCD\uAC8C \uD638\uCD9C\uD558\uC138\uC694. coverage\uAC00 \uB0AE\uC544\uB3C4 \uD0A4\uC6CC\uB4DC \uBBF8\uAE30\uC7AC\uC77C \uC218 \uC788\uC73C\uB2C8 \uB2E8\uC815\uD558\uC9C0 \uB9D0\uACE0, \uD45C\uAE30\uB418\uC9C0 \uC54A\uC740 \uC5ED\uB7C9\uC740 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778\uD558\uC138\uC694. \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4.',
98942
+ description: '\uC800\uC7A5\uB41C \uACF5\uACE0\uC758 \uD575\uC2EC \uD0A4\uC6CC\uB4DC\uB97C **\uC0AC\uC6A9\uC790\uC758 \uBCF4\uC720 \uAE30\uC220\xB7\uD504\uB85C\uC81D\uD2B8/\uACBD\uB825 \uAE30\uC220\uC2A4\uD0DD(\uAD6C\uC870\uD654 \uC2A4\uD0AC)\uACFC \uB300\uD45C \uC774\uB825\uC11C \uBCF8\uBB38 \uC591\uCABD**\uC5D0 \uB300\uC870\uD574 "\uC774 \uACF5\uACE0\uB97C \uAE4A\uAC8C \uBCFC \uAC00\uCE58\uAC00 \uC788\uB294\uC9C0"\uB97C \uBE60\uB974\uAC8C \uAC00\uB2A0\uD569\uB2C8\uB2E4. LLM \uC5C6\uC774 \uC140 \uC218 \uC788\uB294 \uD0A4\uC6CC\uB4DC \uACB9\uCE68\uB9CC \uB3CC\uB824\uC8FC\uB294 \uAC12\uC2FC \uC0AC\uC804 \uD544\uD130\uC785\uB2C8\uB2E4: `matched`(\uC2A4\uD0AC \uBAA9\uB85D\uC5D0\uC11C \uBC1C\uACAC)\xB7`resume_matched`(\uC774\uB825\uC11C \uBCF8\uBB38\uC5D0\uC11C \uBC1C\uACAC)\xB7`combined_matched`(\uB458\uC758 \uD569\uC9D1\uD569)\xB7`combined_coverage`(\uD569\uC9D1\uD569 \uCEE4\uBC84\uB9AC\uC9C0 0~1)\xB7`resume_checked`(\uB300\uD45C \uC774\uB825\uC11C \uC874\uC7AC \uC5EC\uBD80). **\uC9C4\uC9DC \uC801\uD569\uB3C4 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 get_application_context\uB85C \uB9E5\uB77D\uC744 \uBC1B\uC544 \uC9C1\uC811 \uC218\uD589**\uD569\uB2C8\uB2E4(\uC774 \uB3C4\uAD6C\uB294 \uADF8 \uB300\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4). \uC0AC\uC6A9\uC790\uAC00 \uACF5\uACE0\uB97C \uC5EC\uB7EC \uAC1C \uD6D1\uC73C\uBA70 \uC6B0\uC120\uC21C\uC704\uB97C \uC815\uD558\uAC70\uB098 "\uC774\uAC70 \uC9C0\uC6D0\uD560 \uB9CC\uD574?"\uB77C\uACE0 \uBB3C\uC744 \uB54C \uBCF8\uACA9 \uBD84\uC11D \uC804\uC5D0 \uAC00\uBCCD\uAC8C \uD638\uCD9C\uD558\uC138\uC694. **combined_coverage\uAC00 \uB0AE\uC740\uB370 \uC2E4\uC81C\uB85C\uB294 \uBCF4\uC720\uD55C \uC5ED\uB7C9\uC774\uB77C\uBA74, \uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uADF8 \uD0A4\uC6CC\uB4DC\uAC00 \uC548 \uB4DC\uB7EC\uB098\uB294 \uAC83\uC774\uB2C8 \uC774\uB825\uC11C \uBCF4\uAC15(\uD574\uB2F9 \uD0A4\uC6CC\uB4DC \uB178\uCD9C)\uC744 \uC81C\uC548\uD558\uC138\uC694**(ATS \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC5D0 \uC720\uD6A8). coverage\uAC00 \uB0AE\uC544\uB3C4 \uD0A4\uC6CC\uB4DC \uBBF8\uAE30\uC7AC\uC77C \uC218 \uC788\uC73C\uB2C8 \uB2E8\uC815\uD558\uC9C0 \uB9D0\uACE0, \uD45C\uAE30\uB418\uC9C0 \uC54A\uC740 \uC5ED\uB7C9\uC740 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778\uD558\uC138\uC694. \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4.',
98458
98943
  inputSchema: { job_id: external_exports.string() },
98459
98944
  readOnly: true,
98460
98945
  handler: (args) => {
98461
98946
  const r = prescreenJob(args.job_id);
98462
98947
  if (!r) return fail("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 save_job_posting\uC73C\uB85C \uC800\uC7A5\uD558\uC138\uC694.");
98463
- if (r.coverage == null)
98948
+ if (r.combined_coverage == null)
98464
98949
  return ok("\uC774 \uACF5\uACE0\uC5D0 \uC800\uC7A5\uB41C \uD575\uC2EC \uD0A4\uC6CC\uB4DC\uAC00 \uC5C6\uC5B4 \uACB9\uCE68\uC744 \uC140 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. save_job_posting\uC758 keywords\uC5D0 \uD575\uC2EC \uD0A4\uC6CC\uB4DC\uB97C \uC815\uB9AC\uD574 \uB123\uC73C\uBA74 \uD504\uB9AC\uC2A4\uD06C\uB9B0\uC774 \uAC00\uB2A5\uD569\uB2C8\uB2E4.", r);
98465
- const pct = Math.round(r.coverage * 100);
98466
- return ok(`\uD0A4\uC6CC\uB4DC \uACB9\uCE68 ${pct}% \u2014 \uBCF4\uC720 ${r.matched.length}/${r.job_keywords.length}\uAC1C. \uBD80\uC871: ${r.missing.join(", ") || "\uC5C6\uC74C"}. (\uC140 \uC218 \uC788\uB294 \uACB9\uCE68\uC77C \uBFD0, \uC2E4\uC81C \uC801\uD569\uB3C4\uB294 \uC9C1\uC811 \uD310\uB2E8\uD558\uC138\uC694.)`, r);
98950
+ const pct = Math.round(r.combined_coverage * 100);
98951
+ const combinedMissing = r.job_keywords.filter((k) => !r.combined_matched.includes(k));
98952
+ const resumeNote = r.resume_checked ? "" : " (\uB300\uD45C \uC774\uB825\uC11C\uAC00 \uC5C6\uC5B4 \uC2A4\uD0AC \uBAA9\uB85D\uB9CC \uB300\uC870\uD588\uC5B4\uC694 \u2014 \uC774\uB825\uC11C\uB97C \uC800\uC7A5\uD558\uBA74 \uBCF8\uBB38 \uD0A4\uC6CC\uB4DC\uAE4C\uC9C0 \uB300\uC870\uD569\uB2C8\uB2E4.)";
98953
+ return ok(
98954
+ `\uD0A4\uC6CC\uB4DC \uCEE4\uBC84\uB9AC\uC9C0 ${pct}% \u2014 \uC2A4\uD0AC+\uB300\uD45C \uC774\uB825\uC11C \uBCF8\uBB38\uC5D0\uC11C ${r.combined_matched.length}/${r.job_keywords.length}\uAC1C \uD655\uC778.${resumeNote} \uC544\uC9C1 \uC548 \uB4DC\uB7EC\uB09C \uD0A4\uC6CC\uB4DC: ${combinedMissing.join(", ") || "\uC5C6\uC74C"}. (\uC140 \uC218 \uC788\uB294 \uACB9\uCE68\uC77C \uBFD0 \uC2E4\uC81C \uC801\uD569\uB3C4\uB294 \uC9C1\uC811 \uD310\uB2E8\uD558\uC138\uC694. \uBCF4\uC720 \uC5ED\uB7C9\uC778\uB370 \uC548 \uB4DC\uB7EC\uB0AC\uB2E4\uBA74 \uC774\uB825\uC11C \uBCF4\uAC15\uC744 \uC81C\uC548\uD558\uC138\uC694.)`,
98955
+ r
98956
+ );
98957
+ }
98958
+ },
98959
+ {
98960
+ name: "save_offer",
98961
+ idempotent: true,
98962
+ // 공고당 1건 업서트
98963
+ title: "\uC624\uD37C(\uC81C\uC548) \uC800\uC7A5",
98964
+ description: '\uBC1B\uC740 \uCC44\uC6A9 \uC81C\uC548(\uC624\uD37C)\uC744 \uACF5\uACE0\uC5D0 \uC5F0\uACB0\uD574 \uC800\uC7A5\uD569\uB2C8\uB2E4(\uACF5\uACE0\uB2F9 1\uAC74, \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0). \uD604\uAE08\uC131 \uD56D\uBAA9\uC740 **\uB9CC\uC6D0 \uB2E8\uC704 \uC22B\uC790**\uB85C \u2014 base_salary(\uAE30\uBCF8\uAE09/\uC5F0\uBD09)\xB7bonus_amount(\uC5F0 \uC131\uACFC\uAE09\xB7\uC0C1\uC5EC \uAE30\uB300\uC561)\xB7welfare_amount(\uBCF5\uC9C0\uD3EC\uC778\uD2B8 \uB4F1 \uC5F0\uD658\uC0B0)\xB7signing_amount(\uC0AC\uC774\uB2DD \uC77C\uC2DC\uAE08). \uC8FC\uC2DD/\uC2A4\uD1A1\uC635\uC158\uC740 \uAC00\uCE58\uD3C9\uAC00\uAC00 \uBAA8\uD638\uD558\uB2C8 equity_note\uC5D0 \uD14D\uC2A4\uD2B8\uB85C, \uADF8 \uC678 \uBCF4\uC0C1\uC740 comp_note\uC5D0. \uADFC\uBB34\uD615\uD0DC\xB7\uACC4\uC57D\uD615\uD0DC\xB7\uC218\uB77D \uB9C8\uAC10\uC77C(accept_deadline)\uB3C4 \uC800\uC7A5\uD558\uC138\uC694. **\uB2F9\uC2E0(AI)\uC774 \uC774 \uC624\uD37C\uB97C \uC885\uD569 \uD3C9\uAC00\uD574 ai_score(0~100)\uC640 verdict(\uCD5C\uC885\uD3C9 \u2014 "\uC5B4\uB5A4 \uC0C1\uD669\uC774\uBA74 \uC774 \uD68C\uC0AC\uAC00 \uC88B\uB2E4" \uC2DD \uD55C\uB450 \uBB38\uC7A5)\uB97C \uD568\uAED8 \uCC44\uC6CC \uC800\uC7A5\uD558\uC138\uC694** \u2014 CareerMate\uC5D4 LLM\uC774 \uC5C6\uC5B4 \uC774 \uC810\uC218\xB7\uD3C9\uC774 \uB300\uC2DC\uBCF4\uB4DC\uC5D0 \uADF8\uB300\uB85C \uD45C\uC2DC\uB429\uB2C8\uB2E4. \uD68C\uC0AC \uD3C9\uD310\uC774 \uD310\uB2E8\uC5D0 \uD544\uC694\uD558\uBA74 \uACF5\uACE0\uC758 reputation\uC744 \uBA3C\uC800 \uCC44\uC6CC\uB450\uC138\uC694. \uC5EC\uB7EC \uC624\uD37C\uC758 \uC815\uBC00 \uBE44\uAD50\uB294 compare_offers\uB85C \uB370\uC774\uD130\uB97C \uBC1B\uC544 \uB2F9\uC2E0\uC774 \uC9C1\uC811 \uD558\uC138\uC694(\uCD1D\uBCF4\uC0C1\xB7\uC131\uC7A5\xB7\uC548\uC815\uC131\xB7\uC6CC\uB77C\uBC38). **\uC624\uD37C\uC758 \uC9C4\uD589 \uC0C1\uD0DC\uB294 offer_status\uB85C \uAE30\uB85D\uD558\uC138\uC694** \u2014 open(\uAE30\uBCF8, \uC9C4\uD589 \uC911)/accepted(\uC218\uB77D)/declined(\uAC70\uC808). \uC0AC\uC6A9\uC790\uAC00 "\uADF8 \uC624\uD37C \uAC70\uC808\uD588\uC5B4/\uC218\uB77D\uD558\uAE30\uB85C \uD588\uC5B4"\uB77C\uACE0 \uD558\uBA74 \uC774 \uB3C4\uAD6C\uB85C offer_status\uB9CC \uAC31\uC2E0\uD558\uBA74 \uB429\uB2C8\uB2E4 \u2014 \uC0AD\uC81C\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uC5F0\uBD09\xB7\uC870\uAC74 \uAE30\uB85D\uC740 \uB2E4\uC74C \uD611\uC0C1\uC758 \uC790\uC0B0\uC774\uACE0, \uB300\uC2DC\uBCF4\uB4DC\uAC00 \uC9C0\uB09C \uC624\uD37C\uB85C \uC811\uC5B4\uC11C \uBCF4\uC5EC\uC90D\uB2C8\uB2E4). accepted\uB85C \uC800\uC7A5\uD558\uBA74 \uC9C0\uC6D0 \uC0C1\uD0DC(\uCD5C\uC885 \uD569\uACA9) \uAC31\uC2E0\uB3C4 \uD568\uAED8 \uC81C\uC548\uD558\uC138\uC694.',
98965
+ inputSchema: OfferInputSchema.shape,
98966
+ handler: (args) => {
98967
+ try {
98968
+ const offer = saveOffer(args);
98969
+ const total = offer.total_comp_annual_est;
98970
+ const statusLabel = offer.offer_status === "accepted" ? " \xB7 \uC218\uB77D" : offer.offer_status === "declined" ? " \xB7 \uAC70\uC808" : "";
98971
+ const user_message = `'${offer.company}' \uC624\uD37C\uB97C \uC800\uC7A5\uD588\uC5B4\uC694` + (offer.ai_score != null ? ` (\uC810\uC218 ${offer.ai_score})` : "") + (total != null ? ` \xB7 \uCD94\uC815 \uC5F0 \uD604\uAE08\uC131 ${total.toLocaleString()}\uB9CC\uC6D0` : "") + statusLabel + ".";
98972
+ const app = applicationRepo.getByJob(offer.job_id);
98973
+ const suggested_next_action = offer.offer_status === "accepted" && app?.status !== "final_passed" ? {
98974
+ prompt: "\uC9C0\uC6D0 \uC0C1\uD0DC\uB3C4 '\uCD5C\uC885 \uD569\uACA9'\uC73C\uB85C \uBC14\uAFC0\uAE4C\uC694?",
98975
+ next_tool: "update_application_status"
98976
+ } : null;
98977
+ const msg = suggested_next_action ? `${user_message}
98978
+
98979
+ \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '\uCD5C\uC885 \uD569\uACA9'\uC73C\uB85C \uD568\uAED8 \uBC14\uAFC0\uC9C0 \uBB3C\uC5B4\uBCF4\uC138\uC694.` : user_message;
98980
+ return ok(msg, { ...offer, user_message, suggested_next_action });
98981
+ } catch (e) {
98982
+ return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
98983
+ }
98984
+ }
98985
+ },
98986
+ {
98987
+ name: "compare_offers",
98988
+ title: "\uC624\uD37C \uBE44\uAD50",
98989
+ description: '\uC800\uC7A5\uB41C \uBAA8\uB4E0 \uC624\uD37C\uB97C **\uC810\uC218\uC21C(\uB2F9\uC2E0\uC774 \uB9E4\uAE34 ai_score)** \uC73C\uB85C \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uCD94\uC815 \uC5F0 \uD604\uAE08\uC131 \uCD1D\uC561\xB7\uADFC\uBB34\uD615\uD0DC\xB7\uC218\uB77D\uB9C8\uAC10\xB7\uCD5C\uC885\uD3C9\uACFC \uD568\uAED8 \uB3CC\uB824\uC90D\uB2C8\uB2E4. total_comp_annual_est\uB294 CareerMate\uAC00 \uACB0\uC815\uB860\uC73C\uB85C \uD569\uC0B0\uD55C \uCD94\uC815\uCE58(\uAE30\uBCF8\uAE09+\uC131\uACFC\uAE09+\uBCF5\uC9C0, \uC77C\uC2DC\uAE08 \uC81C\uC678)\uC785\uB2C8\uB2E4. \uAC01 \uC624\uD37C\uC5D0\uB294 offer_status(open/accepted/declined)\uAC00 \uD3EC\uD568\uB418\uB2C8 **\uBE44\uAD50\uB294 \uC9C4\uD589 \uC911(open) \uC624\uD37C \uC704\uC8FC\uB85C** \uD558\uACE0, \uC218\uB77D\xB7\uAC70\uC808\uB41C \uC9C0\uB09C \uC624\uD37C\uB294 \uAE30\uC900\uC810(\uACFC\uAC70 \uC870\uAC74)\uC73C\uB85C\uB9CC \uCC38\uACE0\uD558\uC138\uC694. "\uC5B4\uB290 \uC624\uD37C\uAC00 \uB098\uC740\uC9C0"\uC758 \uC815\uBC00 \uD310\uB2E8\uC740 \uC774 \uB370\uC774\uD130\uB97C \uBC1B\uC544 \uB2F9\uC2E0\uC774 offer-evaluation\xB7salary-negotiation \uAD00\uC810(\uCD1D\uBCF4\uC0C1\xB7\uC131\uC7A5\xB7\uC548\uC815\uC131\xB7\uC6CC\uB77C\uBC38\xB7\uD68C\uC0AC \uD3C9\uD310)\uC73C\uB85C \uC9C1\uC811 \uD558\uC138\uC694. \uC0AC\uC6A9\uC790\uAC00 "\uC624\uD37C \uBE44\uAD50\uD574\uC918"\uB77C\uACE0 \uD560 \uB54C \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4.',
98990
+ inputSchema: {},
98991
+ readOnly: true,
98992
+ handler: () => {
98993
+ const offers = compareOffers();
98994
+ if (offers.length === 0) return ok("\uC800\uC7A5\uB41C \uC624\uD37C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. save_offer\uB85C \uBA3C\uC800 \uC800\uC7A5\uD558\uC138\uC694.", offers);
98995
+ return ok(`\uC624\uD37C ${offers.length}\uAC74(\uC810\uC218\uC21C).`, offers);
98996
+ }
98997
+ },
98998
+ {
98999
+ name: "get_offer",
99000
+ title: "\uC624\uD37C \uC870\uD68C(\uACF5\uACE0\uBCC4)",
99001
+ description: "\uD2B9\uC815 \uACF5\uACE0\uC5D0 \uC800\uC7A5\uB41C \uC624\uD37C 1\uAC74\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4(\uC5C6\uC73C\uBA74 null). \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4.",
99002
+ inputSchema: { job_id: external_exports.string() },
99003
+ readOnly: true,
99004
+ handler: (args) => {
99005
+ const offer = getOffer(args.job_id);
99006
+ if (!offer) return ok("\uC774 \uACF5\uACE0\uC5D0 \uC800\uC7A5\uB41C \uC624\uD37C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", null);
99007
+ return ok(`'${offer.company}' \uC624\uD37C.`, offer);
98467
99008
  }
98468
99009
  },
98469
99010
  /* ------------------------------------------------------- workflow guide */
@@ -98537,7 +99078,7 @@ ${res.hint}` : "");
98537
99078
  {
98538
99079
  name: "validate_cover_letter",
98539
99080
  title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC790\uB3D9 \uC810\uAC80 (\uC800\uC7A5 \uC804 \uBBF8\uB9AC\uBCF4\uAE30)",
98540
- description: "\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C save_cover_letter_version\uC73C\uB85C \uC800\uC7A5\uD558\uAE30 \uC9C1\uC804\uC5D0 \uD638\uCD9C\uD558\uBA74, \uC800\uC7A5\uD558\uC9C0 \uC54A\uACE0 \uBBF8\uB9AC \uC810\uAC80\uB9CC \uD569\uB2C8\uB2E4. CareerMate\uAC00 LLM \uC5C6\uC774 \uACB0\uC815\uB860\uC73C\uB85C (1) \uBCF8\uBB38\uC758 \uC815\uB7C9 \uC218\uCE58\uAC00 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADFC\uAC70\uAC00 \uC788\uB294\uC9C0(\uC5C6\uC73C\uBA74 \uD658\uAC01 \uC758\uC2EC \u2192 \uC800\uC7A5 \uC2DC \uCC28\uB2E8\uB428), (2) \uACF5\uACE0\uC5D0\uC11C \uAC00\uC838\uC628 \uC218\uCE58\uB97C \uBCF8\uC778 \uC131\uACFC\uCC98\uB7FC \uC4F0\uC9C0 \uC54A\uC558\uB294\uC9C0, (3) \uBC88\uC5ED\uD22C\xB7\uC81C\uB108\uB9AD \uB3C4\uC785\xB7\uBE48 \uD615\uC6A9\uC0AC\xB7\uBE14\uB77C\uC778\uB4DC \uC704\uBC18 \uAC19\uC740 \uBB38\uCCB4 \uC2E0\uD638\uB97C \uC138\uC5B4 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uB610 (4) \uAE00\uC790\uC218\uB97C \uACF5\uBC31\uD3EC\uD568\xB7\uACF5\uBC31\uC81C\uC678\xB7UTF-8 byte \uC138 \uAE30\uC900\uC73C\uB85C \uC815\uD655\uD788 \uC138\uACE0, min_chars/max_chars\uB97C \uC8FC\uBA74 \uC0C1\xB7\uD558\uD55C \uCDA9\uC871 \uC5EC\uBD80\uAE4C\uC9C0 \uB3CC\uB824\uC90D\uB2C8\uB2E4(LLM\uC740 \uAE00\uC790\uC218\uB97C \uC815\uD655\uD788 \uBABB \uC138\uBBC0\uB85C \uC774 \uAE30\uB2A5\uC744 \uC4F0\uC138\uC694). \uC810\uAC80\uC740 \uC140 \uC218 \uC788\uB294 \uAC83\uB9CC \uBD05\uB2C8\uB2E4 \u2014 \uC0AC\uC2E4\uC131\xB7\uB3D9\uBB38\uC11C\uB2F5 \uAC19\uC740 \uC758\uBBF8 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uC9C1\uC811 \uD558\uC138\uC694. \uD55C\uAD6D \uBB38\uD56D\uD615 \uC790\uC18C\uC11C\uB294 \uBB38\uD56D\uB9C8\uB2E4 \uAE00\uC790\uC218 \uD55C\uB3C4\uAC00 \uB2E4\uB974\uB2C8 \uAC01 \uBB38\uD56D \uBCF8\uBB38\uC744 \uADF8 \uBB38\uD56D\uC758 \uD55C\uB3C4\uB85C \uB530\uB85C \uC810\uAC80\uD558\uC138\uC694. job_id\uB97C \uC8FC\uBA74 \uACF5\uACE0 \uC218\uCE58\uAE4C\uC9C0 \uBE44\uAD50\uD569\uB2C8\uB2E4.",
99081
+ description: "\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C save_cover_letter_version\uC73C\uB85C \uC800\uC7A5\uD558\uAE30 \uC9C1\uC804\uC5D0 \uD638\uCD9C\uD558\uBA74, \uC800\uC7A5\uD558\uC9C0 \uC54A\uACE0 \uBBF8\uB9AC \uC810\uAC80\uB9CC \uD569\uB2C8\uB2E4. CareerMate\uAC00 LLM \uC5C6\uC774 \uACB0\uC815\uB860\uC73C\uB85C (1) \uBCF8\uBB38\uC758 \uC815\uB7C9 \uC218\uCE58\uAC00 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADFC\uAC70\uAC00 \uC788\uB294\uC9C0(\uC5C6\uC73C\uBA74 \uD658\uAC01 \uC758\uC2EC \u2192 \uC800\uC7A5 \uC2DC \uCC28\uB2E8\uB428), (2) \uACF5\uACE0\uC5D0\uC11C \uAC00\uC838\uC628 \uC218\uCE58\uB97C \uBCF8\uC778 \uC131\uACFC\uCC98\uB7FC \uC4F0\uC9C0 \uC54A\uC558\uB294\uC9C0, (3) \uBC88\uC5ED\uD22C\xB7\uC81C\uB108\uB9AD \uB3C4\uC785\xB7\uBE48 \uD615\uC6A9\uC0AC\xB7\uBE14\uB77C\uC778\uB4DC \uC704\uBC18 \uAC19\uC740 \uBB38\uCCB4 \uC2E0\uD638\uB97C \uC138\uC5B4 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uB610 (4) \uAE00\uC790\uC218\uB97C \uACF5\uBC31\uD3EC\uD568\xB7\uACF5\uBC31\uC81C\uC678\xB7UTF-8 byte \uC138 \uAE30\uC900\uC73C\uB85C \uC815\uD655\uD788 \uC138\uACE0, min_chars/max_chars\uB97C \uC8FC\uBA74 \uC0C1\xB7\uD558\uD55C \uCDA9\uC871 \uC5EC\uBD80\uAE4C\uC9C0 \uB3CC\uB824\uC90D\uB2C8\uB2E4(LLM\uC740 \uAE00\uC790\uC218\uB97C \uC815\uD655\uD788 \uBABB \uC138\uBBC0\uB85C \uC774 \uAE30\uB2A5\uC744 \uC4F0\uC138\uC694). \uC810\uAC80\uC740 \uC140 \uC218 \uC788\uB294 \uAC83\uB9CC \uBD05\uB2C8\uB2E4 \u2014 \uC0AC\uC2E4\uC131\xB7\uB3D9\uBB38\uC11C\uB2F5 \uAC19\uC740 \uC758\uBBF8 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uC9C1\uC811 \uD558\uC138\uC694. \uD55C\uAD6D \uBB38\uD56D\uD615 \uC790\uC18C\uC11C\uB294 \uBB38\uD56D\uB9C8\uB2E4 \uAE00\uC790\uC218 \uD55C\uB3C4\uAC00 \uB2E4\uB974\uB2C8 \uAC01 \uBB38\uD56D \uBCF8\uBB38\uC744 \uADF8 \uBB38\uD56D\uC758 \uD55C\uB3C4\uB85C \uB530\uB85C \uC810\uAC80\uD558\uC138\uC694. \uACF5\uACE0\uC5D0 questions\uAC00 \uC800\uC7A5\uB3FC \uC788\uC73C\uBA74 \uADF8 \uBB38\uD56D\uC758 \uD55C\uB3C4\uB97C min_chars/max_chars/count_mode\uB85C \uB118\uACA8 \uC810\uAC80\uD558\uC138\uC694. job_id\uB97C \uC8FC\uBA74 \uACF5\uACE0 \uC218\uCE58\uAE4C\uC9C0 \uBE44\uAD50\uD569\uB2C8\uB2E4.",
98541
99082
  inputSchema: {
98542
99083
  text: external_exports.string().max(2e5).describe("\uC810\uAC80\uD560 \uC790\uAE30\uC18C\uAC1C\uC11C \uBCF8\uBB38(\uBB38\uD56D\uD615\uC774\uBA74 \uD55C \uBB38\uD56D \uBCF8\uBB38)"),
98543
99084
  job_id: external_exports.string().optional().describe("\uC5F0\uACB0\uD560 \uACF5\uACE0(\uC788\uC73C\uBA74 \uACF5\uACE0 \uC218\uCE58\uAE4C\uC9C0 \uBE44\uAD50)"),
@@ -98581,6 +99122,8 @@ ${report.disclaimer}`, {
98581
99122
  },
98582
99123
  {
98583
99124
  name: "set_verify_mode",
99125
+ idempotent: true,
99126
+ // 같은 값 재설정은 무해
98584
99127
  title: "\uC810\uAC80 \uBAA8\uB4DC \uC124\uC815 (\uAE30\uBCF8/\uC5C4\uACA9)",
98585
99128
  description: '\uC800\uC7A5 \uC804 \uC790\uB3D9 \uC810\uAC80\uC758 \uC5C4\uACA9\uB3C4\uB97C \uBC14\uAFC9\uB2C8\uB2E4. \uC5C4\uACA9 \uBAA8\uB4DC(strict=true)\uB97C \uCF1C\uBA74, \uC774\uD6C4 \uBAA8\uB4E0 \uC790\uC18C\uC11C \uC800\uC7A5\uC5D0\uC11C "\uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58"(\uAD6C\uC870\uD654 \uACBD\uB825 \uD56D\uBAA9\uC5D0\uB9CC \uC788\uB294 \uAC12)\uAE4C\uC9C0 \uCC28\uB2E8\uD569\uB2C8\uB2E4. \uAE30\uBCF8 \uBAA8\uB4DC\uB294 \uCD9C\uCC98\uAC00 \uC544\uC608 \uC5C6\uB294 \uC218\uCE58\uB9CC \uCC28\uB2E8\uD569\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uC5C4\uACA9\uD558\uAC8C \uBD10\uC918"\uB77C\uACE0 \uD558\uBA74 \uBC14\uB85C \uCF1C\uC9C0 \uB9D0\uACE0 "\uC55E\uC73C\uB85C \uD56D\uC0C1 \uC5C4\uACA9\uD558\uAC8C \uBCFC\uAE4C\uC694?"\uB77C\uACE0 \uBA3C\uC800 \uD655\uC778\uD55C \uB4A4 \uB3D9\uC758\uD558\uBA74 strict=true\uB85C \uD638\uCD9C\uD558\uC138\uC694. \uB044\uAE30(strict=false)\uB294 \uC548\uC804\uC7A5\uCE58\uB97C \uC57D\uD558\uAC8C \uB9CC\uB4DC\uB294 \uAC83\uC774\uBBC0\uB85C, \uC0AC\uC6A9\uC790\uAC00 \uBA85\uD655\uD788 \uB044\uACA0\uB2E4\uACE0 \uD560 \uB54C\uB9CC \uD638\uCD9C\uD558\uC138\uC694. \uC774\uBC88 \uD55C \uAC74\uB9CC \uC5C4\uACA9\uD558\uAC8C \uBCF4\uB824\uBA74 \uBAA8\uB4DC\uB97C \uBC14\uAFB8\uC9C0 \uB9D0\uACE0 validate_cover_letter / save_cover_letter_version\uC5D0 strict:true\uB97C \uD55C \uBC88\uB9CC \uB118\uAE30\uC138\uC694.',
98586
99129
  inputSchema: {
@@ -98619,6 +99162,8 @@ ${report.disclaimer}`, {
98619
99162
  /* ----------------------------------------------------------------- update */
98620
99163
  {
98621
99164
  name: "check_for_update",
99165
+ // npm 레지스트리로 버전 조회가 나간다(사용자 데이터 미전송).
99166
+ openWorld: true,
98622
99167
  title: "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778",
98623
99168
  description: 'CareerMate\uC5D0 \uC0C8 \uBC84\uC804\uC774 \uC788\uB294\uC9C0 npm \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0\uC11C \uC9C0\uAE08 \uD655\uC778\uD569\uB2C8\uB2E4(\uACF5\uAC1C \uBC84\uC804 \uC815\uBCF4\uB9CC GET\uD558\uBA70 \uC0AC\uC6A9\uC790 \uB370\uC774\uD130\uB294 \uC804\uC1A1\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4). \uD604\uC7AC \uBC84\uC804\xB7\uCD5C\uC2E0 \uBC84\uC804\xB7\uC5C5\uB370\uC774\uD2B8 \uAC00\uB2A5 \uC5EC\uBD80\uB97C \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uCD5C\uC2E0\uC774\uC57C?/\uC5C5\uB370\uC774\uD2B8 \uC788\uC5B4?"\uB77C\uACE0 \uBB3B\uAC70\uB098, get_onboarding_status\uAC00 \uC0C8 \uBC84\uC804\uC744 \uC54C\uB9B4 \uB54C \uD638\uCD9C\uD558\uC138\uC694. \uD655\uC778\uB9CC \uD558\uACE0 \uC124\uCE58\uB294 \uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uC5C5\uB370\uC774\uD2B8\uD574\uC918/\uCD5C\uC2E0\uC73C\uB85C \uC62C\uB824\uC918"\uCC98\uB7FC \uBA85\uC2DC\uC801\uC73C\uB85C \uC694\uCCAD\uD558\uBA74 update_careermate\uB97C \uD638\uCD9C\uD558\uC138\uC694.',
98624
99169
  inputSchema: {},
@@ -98642,6 +99187,9 @@ ${report.disclaimer}`, {
98642
99187
  },
98643
99188
  {
98644
99189
  name: "update_careermate",
99190
+ // npm에서 패키지를 내려받아 설치 폴더의 코드를 교체한다.
99191
+ openWorld: true,
99192
+ destructive: true,
98645
99193
  title: "CareerMate \uC5C5\uB370\uC774\uD2B8",
98646
99194
  description: 'CareerMate\uB97C \uCD5C\uC2E0 \uBC84\uC804\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD569\uB2C8\uB2E4(\uC124\uCE58 \uD3F4\uB354\uC5D0 npm\uC73C\uB85C careermate@latest \uC124\uCE58). \uC0AC\uC6A9\uC790\uAC00 \uBA85\uC2DC\uC801\uC73C\uB85C "\uC5C5\uB370\uC774\uD2B8\uD574\uC918", "CareerMate \uC5C5\uB370\uC774\uD2B8\uD574\uC918", "\uCD5C\uC2E0\uC73C\uB85C \uC62C\uB824\uC918"\uB77C\uACE0 \uD560 \uB54C \uD638\uCD9C\uD558\uC138\uC694. \uC774 \uB3C4\uAD6C\uAC00 \uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778\uB3C4 \uD568\uAED8 \uD558\uBBC0\uB85C \uBCC4\uB3C4 \uD655\uC778 \uC9C8\uBB38 \uC5C6\uC774 \uC2E4\uD589\uD574\uB3C4 \uB429\uB2C8\uB2E4. \uC131\uACF5\uD558\uBA74 \uC801\uC6A9\uC744 \uC704\uD574 AI \uC571(\uB610\uB294 MCP \uC5F0\uACB0) \uC7AC\uC2DC\uC791\uC774 \uD544\uC694\uD558\uBA70, \uC7AC\uC2DC\uC791 \uC2DC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uB294 \uC790\uB3D9 \uB9C8\uC774\uADF8\uB808\uC774\uC158\uB418\uC5B4 \uAE30\uC874 \uB370\uC774\uD130\uB294 \uBCF4\uC874\uB429\uB2C8\uB2E4. \uC790\uB3D9 \uC124\uCE58\uAC00 \uB9C9\uD788\uBA74(\uAC1C\uBC1C \uBAA8\uB4DC\xB7\uBE44\uD45C\uC900 \uC124\uCE58) \uC9C1\uC811 \uCE60 \uBA85\uB839\uC744 \uC548\uB0B4\uD569\uB2C8\uB2E4.',
98647
99195
  inputSchema: {},
@@ -98671,7 +99219,7 @@ async function main() {
98671
99219
  const nodeOk = checkNode();
98672
99220
  const dir = getDataDir();
98673
99221
  try {
98674
- fs8.accessSync(dir, fs8.constants.W_OK);
99222
+ fs9.accessSync(dir, fs9.constants.W_OK);
98675
99223
  ok2(`\uB370\uC774\uD130 \uD3F4\uB354 \uC4F0\uAE30 \uAC00\uB2A5: ${dir}`);
98676
99224
  } catch {
98677
99225
  warn2(`\uB370\uC774\uD130 \uD3F4\uB354\uC5D0 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dir}`);