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/mcp.mjs CHANGED
@@ -3244,8 +3244,8 @@ var require_utils = __commonJS({
3244
3244
  }
3245
3245
  return ind;
3246
3246
  }
3247
- function removeDotSegments(path8) {
3248
- let input = path8;
3247
+ function removeDotSegments(path9) {
3248
+ let input = path9;
3249
3249
  const output = [];
3250
3250
  let nextSlash = -1;
3251
3251
  let len = 0;
@@ -3497,8 +3497,8 @@ var require_schemes = __commonJS({
3497
3497
  wsComponent.secure = void 0;
3498
3498
  }
3499
3499
  if (wsComponent.resourceName) {
3500
- const [path8, query] = wsComponent.resourceName.split("?");
3501
- wsComponent.path = path8 && path8 !== "/" ? path8 : void 0;
3500
+ const [path9, query] = wsComponent.resourceName.split("?");
3501
+ wsComponent.path = path9 && path9 !== "/" ? path9 : void 0;
3502
3502
  wsComponent.query = query;
3503
3503
  wsComponent.resourceName = void 0;
3504
3504
  }
@@ -6891,12 +6891,12 @@ var require_dist = __commonJS({
6891
6891
  throw new Error(`Unknown format "${name}"`);
6892
6892
  return f;
6893
6893
  };
6894
- function addFormats(ajv, list, fs8, exportName2) {
6894
+ function addFormats(ajv, list, fs9, exportName2) {
6895
6895
  var _a2;
6896
6896
  var _b2;
6897
6897
  (_a2 = (_b2 = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b2.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName2}`;
6898
6898
  for (const f of list)
6899
- ajv.addFormat(f, fs8[f]);
6899
+ ajv.addFormat(f, fs9[f]);
6900
6900
  }
6901
6901
  module2.exports = exports2 = formatsPlugin;
6902
6902
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -7832,8 +7832,8 @@ var init_tap = __esm({
7832
7832
  });
7833
7833
 
7834
7834
  // node_modules/underscore/modules/toPath.js
7835
- function toPath(path8) {
7836
- return isArray_default(path8) ? path8 : [path8];
7835
+ function toPath(path9) {
7836
+ return isArray_default(path9) ? path9 : [path9];
7837
7837
  }
7838
7838
  var init_toPath = __esm({
7839
7839
  "node_modules/underscore/modules/toPath.js"() {
@@ -7845,8 +7845,8 @@ var init_toPath = __esm({
7845
7845
  });
7846
7846
 
7847
7847
  // node_modules/underscore/modules/_toPath.js
7848
- function toPath2(path8) {
7849
- return _.toPath(path8);
7848
+ function toPath2(path9) {
7849
+ return _.toPath(path9);
7850
7850
  }
7851
7851
  var init_toPath2 = __esm({
7852
7852
  "node_modules/underscore/modules/_toPath.js"() {
@@ -7857,11 +7857,11 @@ var init_toPath2 = __esm({
7857
7857
  });
7858
7858
 
7859
7859
  // node_modules/underscore/modules/_deepGet.js
7860
- function deepGet(obj2, path8) {
7861
- var length = path8.length;
7860
+ function deepGet(obj2, path9) {
7861
+ var length = path9.length;
7862
7862
  for (var i2 = 0; i2 < length; i2++) {
7863
7863
  if (obj2 == null) return void 0;
7864
- obj2 = obj2[path8[i2]];
7864
+ obj2 = obj2[path9[i2]];
7865
7865
  }
7866
7866
  return length ? obj2 : void 0;
7867
7867
  }
@@ -7872,8 +7872,8 @@ var init_deepGet = __esm({
7872
7872
  });
7873
7873
 
7874
7874
  // node_modules/underscore/modules/get.js
7875
- function get(object4, path8, defaultValue) {
7876
- var value = deepGet(object4, toPath2(path8));
7875
+ function get(object4, path9, defaultValue) {
7876
+ var value = deepGet(object4, toPath2(path9));
7877
7877
  return isUndefined(value) ? defaultValue : value;
7878
7878
  }
7879
7879
  var init_get = __esm({
@@ -7886,11 +7886,11 @@ var init_get = __esm({
7886
7886
  });
7887
7887
 
7888
7888
  // node_modules/underscore/modules/has.js
7889
- function has2(obj2, path8) {
7890
- path8 = toPath2(path8);
7891
- var length = path8.length;
7889
+ function has2(obj2, path9) {
7890
+ path9 = toPath2(path9);
7891
+ var length = path9.length;
7892
7892
  for (var i2 = 0; i2 < length; i2++) {
7893
- var key = path8[i2];
7893
+ var key = path9[i2];
7894
7894
  if (!has(obj2, key)) return false;
7895
7895
  obj2 = obj2[key];
7896
7896
  }
@@ -7930,10 +7930,10 @@ var init_matcher = __esm({
7930
7930
  });
7931
7931
 
7932
7932
  // node_modules/underscore/modules/property.js
7933
- function property(path8) {
7934
- path8 = toPath2(path8);
7933
+ function property(path9) {
7934
+ path9 = toPath2(path9);
7935
7935
  return function(obj2) {
7936
- return deepGet(obj2, path8);
7936
+ return deepGet(obj2, path9);
7937
7937
  };
7938
7938
  }
7939
7939
  var init_property = __esm({
@@ -8049,8 +8049,8 @@ var init_noop = __esm({
8049
8049
  // node_modules/underscore/modules/propertyOf.js
8050
8050
  function propertyOf(obj2) {
8051
8051
  if (obj2 == null) return noop2;
8052
- return function(path8) {
8053
- return get(obj2, path8);
8052
+ return function(path9) {
8053
+ return get(obj2, path9);
8054
8054
  };
8055
8055
  }
8056
8056
  var init_propertyOf = __esm({
@@ -8198,7 +8198,7 @@ function template(text, settings, oldSettings) {
8198
8198
  var index = 0;
8199
8199
  var source = "__p+='";
8200
8200
  text.replace(matcher2, function(match, escape2, interpolate, evaluate, offset) {
8201
- source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
8201
+ source += text.slice(index, offset).replace(escapeRegExp2, escapeChar);
8202
8202
  index = offset + match.length;
8203
8203
  if (escape2) {
8204
8204
  source += "'+\n((__t=(" + escape2 + "))==null?'':_.escape(__t))+\n'";
@@ -8233,7 +8233,7 @@ function template(text, settings, oldSettings) {
8233
8233
  template2.source = "function(" + argument + "){\n" + source + "}";
8234
8234
  return template2;
8235
8235
  }
8236
- var noMatch, escapes, escapeRegExp, bareIdentifier;
8236
+ var noMatch, escapes, escapeRegExp2, bareIdentifier;
8237
8237
  var init_template = __esm({
8238
8238
  "node_modules/underscore/modules/template.js"() {
8239
8239
  "use strict";
@@ -8249,20 +8249,20 @@ var init_template = __esm({
8249
8249
  "\u2028": "u2028",
8250
8250
  "\u2029": "u2029"
8251
8251
  };
8252
- escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
8252
+ escapeRegExp2 = /\\|'|\r|\n|\u2028|\u2029/g;
8253
8253
  bareIdentifier = /^\s*(\w|\$)+\s*$/;
8254
8254
  }
8255
8255
  });
8256
8256
 
8257
8257
  // node_modules/underscore/modules/result.js
8258
- function result(obj2, path8, fallback) {
8259
- path8 = toPath2(path8);
8260
- var length = path8.length;
8258
+ function result(obj2, path9, fallback) {
8259
+ path9 = toPath2(path9);
8260
+ var length = path9.length;
8261
8261
  if (!length) {
8262
8262
  return isFunction_default(fallback) ? fallback.call(obj2) : fallback;
8263
8263
  }
8264
8264
  for (var i2 = 0; i2 < length; i2++) {
8265
- var prop = obj2 == null ? void 0 : obj2[path8[i2]];
8265
+ var prop = obj2 == null ? void 0 : obj2[path9[i2]];
8266
8266
  if (prop === void 0) {
8267
8267
  prop = fallback;
8268
8268
  i2 = length;
@@ -8986,14 +8986,14 @@ var init_invoke = __esm({
8986
8986
  init_map();
8987
8987
  init_deepGet();
8988
8988
  init_toPath2();
8989
- invoke_default = restArguments(function(obj2, path8, args) {
8989
+ invoke_default = restArguments(function(obj2, path9, args) {
8990
8990
  var contextPath, func;
8991
- if (isFunction_default(path8)) {
8992
- func = path8;
8991
+ if (isFunction_default(path9)) {
8992
+ func = path9;
8993
8993
  } else {
8994
- path8 = toPath2(path8);
8995
- contextPath = path8.slice(0, -1);
8996
- path8 = path8[path8.length - 1];
8994
+ path9 = toPath2(path9);
8995
+ contextPath = path9.slice(0, -1);
8996
+ path9 = path9[path9.length - 1];
8997
8997
  }
8998
8998
  return map(obj2, function(context) {
8999
8999
  var method = func;
@@ -9002,7 +9002,7 @@ var init_invoke = __esm({
9002
9002
  context = deepGet(context, contextPath);
9003
9003
  }
9004
9004
  if (context == null) return void 0;
9005
- method = context[path8];
9005
+ method = context[path9];
9006
9006
  }
9007
9007
  return method == null ? method : method.apply(context, args);
9008
9008
  });
@@ -18520,8 +18520,8 @@ var require_utils3 = __commonJS({
18520
18520
  var result2 = transform2[inputType][outputType](input);
18521
18521
  return result2;
18522
18522
  };
18523
- exports2.resolve = function(path8) {
18524
- var parts = path8.split("/");
18523
+ exports2.resolve = function(path9) {
18524
+ var parts = path9.split("/");
18525
18525
  var result2 = [];
18526
18526
  for (var index = 0; index < parts.length; index++) {
18527
18527
  var part = parts[index];
@@ -24374,18 +24374,18 @@ var require_object = __commonJS({
24374
24374
  var object4 = new ZipObject(name, zipObjectContent, o);
24375
24375
  this.files[name] = object4;
24376
24376
  };
24377
- var parentFolder = function(path8) {
24378
- if (path8.slice(-1) === "/") {
24379
- path8 = path8.substring(0, path8.length - 1);
24377
+ var parentFolder = function(path9) {
24378
+ if (path9.slice(-1) === "/") {
24379
+ path9 = path9.substring(0, path9.length - 1);
24380
24380
  }
24381
- var lastSlash = path8.lastIndexOf("/");
24382
- return lastSlash > 0 ? path8.substring(0, lastSlash) : "";
24381
+ var lastSlash = path9.lastIndexOf("/");
24382
+ return lastSlash > 0 ? path9.substring(0, lastSlash) : "";
24383
24383
  };
24384
- var forceTrailingSlash = function(path8) {
24385
- if (path8.slice(-1) !== "/") {
24386
- path8 += "/";
24384
+ var forceTrailingSlash = function(path9) {
24385
+ if (path9.slice(-1) !== "/") {
24386
+ path9 += "/";
24387
24387
  }
24388
- return path8;
24388
+ return path9;
24389
24389
  };
24390
24390
  var folderAdd = function(name, createFolders) {
24391
24391
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -25424,27 +25424,27 @@ var require_zipfile = __commonJS({
25424
25424
  };
25425
25425
  });
25426
25426
  }
25427
- function splitPath(path8) {
25428
- var lastIndex = path8.lastIndexOf("/");
25427
+ function splitPath(path9) {
25428
+ var lastIndex = path9.lastIndexOf("/");
25429
25429
  if (lastIndex === -1) {
25430
- return { dirname: "", basename: path8 };
25430
+ return { dirname: "", basename: path9 };
25431
25431
  } else {
25432
25432
  return {
25433
- dirname: path8.substring(0, lastIndex),
25434
- basename: path8.substring(lastIndex + 1)
25433
+ dirname: path9.substring(0, lastIndex),
25434
+ basename: path9.substring(lastIndex + 1)
25435
25435
  };
25436
25436
  }
25437
25437
  }
25438
25438
  function joinPath() {
25439
- var nonEmptyPaths = Array.prototype.filter.call(arguments, function(path8) {
25440
- return path8;
25439
+ var nonEmptyPaths = Array.prototype.filter.call(arguments, function(path9) {
25440
+ return path9;
25441
25441
  });
25442
25442
  var relevantPaths = [];
25443
- nonEmptyPaths.forEach(function(path8) {
25444
- if (/^\//.test(path8)) {
25445
- relevantPaths = [path8];
25443
+ nonEmptyPaths.forEach(function(path9) {
25444
+ if (/^\//.test(path9)) {
25445
+ relevantPaths = [path9];
25446
25446
  } else {
25447
- relevantPaths.push(path8);
25447
+ relevantPaths.push(path9);
25448
25448
  }
25449
25449
  });
25450
25450
  return relevantPaths.join("/");
@@ -32869,9 +32869,9 @@ var require_office_xml_reader = __commonJS({
32869
32869
  return collapseAlternateContent(document2)[0];
32870
32870
  });
32871
32871
  }
32872
- function readXmlFromZipFile(docxFile, path8) {
32873
- if (docxFile.exists(path8)) {
32874
- return docxFile.read(path8, "utf-8").then(stripUtf8Bom).then(read);
32872
+ function readXmlFromZipFile(docxFile, path9) {
32873
+ if (docxFile.exists(path9)) {
32874
+ return docxFile.read(path9, "utf-8").then(stripUtf8Bom).then(read);
32875
32875
  } else {
32876
32876
  return promises.resolve(null);
32877
32877
  }
@@ -34638,10 +34638,10 @@ var require_body_reader = __commonJS({
34638
34638
  }
34639
34639
  }
34640
34640
  function findEmbeddedImageFile(relationshipId) {
34641
- var path8 = uris.uriToZipEntryName("word", relationships.findTargetByRelationshipId(relationshipId));
34641
+ var path9 = uris.uriToZipEntryName("word", relationships.findTargetByRelationshipId(relationshipId));
34642
34642
  return {
34643
- path: path8,
34644
- read: docxFile.read.bind(docxFile, path8)
34643
+ path: path9,
34644
+ read: docxFile.read.bind(docxFile, path9)
34645
34645
  };
34646
34646
  }
34647
34647
  function readImage(imageFile, altText) {
@@ -34881,12 +34881,12 @@ var require_content_types_reader = __commonJS({
34881
34881
  }
34882
34882
  function contentTypes(overrides, extensionDefaults) {
34883
34883
  return {
34884
- findContentType: function(path8) {
34885
- var overrideContentType = overrides[path8];
34884
+ findContentType: function(path9) {
34885
+ var overrideContentType = overrides[path9];
34886
34886
  if (overrideContentType) {
34887
34887
  return overrideContentType;
34888
34888
  } else {
34889
- var pathParts = path8.split(".");
34889
+ var pathParts = path9.split(".");
34890
34890
  var extension = pathParts[pathParts.length - 1];
34891
34891
  if (extensionDefaults.hasOwnProperty(extension)) {
34892
34892
  return extensionDefaults[extension];
@@ -35133,12 +35133,12 @@ var require_comments_reader = __commonJS({
35133
35133
  var require_path_is_absolute = __commonJS({
35134
35134
  "node_modules/path-is-absolute/index.js"(exports2, module2) {
35135
35135
  "use strict";
35136
- function posix(path8) {
35137
- return path8.charAt(0) === "/";
35136
+ function posix(path9) {
35137
+ return path9.charAt(0) === "/";
35138
35138
  }
35139
- function win32(path8) {
35139
+ function win32(path9) {
35140
35140
  var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
35141
- var result2 = splitDeviceRe.exec(path8);
35141
+ var result2 = splitDeviceRe.exec(path9);
35142
35142
  var device = result2[1] || "";
35143
35143
  var isUnc = Boolean(device && device.charAt(1) !== ":");
35144
35144
  return Boolean(result2[2] || isUnc);
@@ -35153,7 +35153,7 @@ var require_path_is_absolute = __commonJS({
35153
35153
  var require_files = __commonJS({
35154
35154
  "node_modules/mammoth/lib/docx/files.js"(exports2) {
35155
35155
  "use strict";
35156
- var fs8 = __require("fs");
35156
+ var fs9 = __require("fs");
35157
35157
  var url = __require("url");
35158
35158
  var os3 = __require("os");
35159
35159
  var dirname = __require("path").dirname;
@@ -35173,19 +35173,19 @@ var require_files = __commonJS({
35173
35173
  }
35174
35174
  var base = options.relativeToFile ? dirname(options.relativeToFile) : null;
35175
35175
  function read(uri, encoding) {
35176
- return resolveUri(uri).then(function(path8) {
35177
- return readFile(path8, encoding).caught(function(error2) {
35176
+ return resolveUri(uri).then(function(path9) {
35177
+ return readFile(path9, encoding).caught(function(error2) {
35178
35178
  var message = "could not open external image: '" + uri + "' (document directory: '" + base + "')\n" + error2.message;
35179
35179
  return promises.reject(new Error(message));
35180
35180
  });
35181
35181
  });
35182
35182
  }
35183
35183
  function resolveUri(uri) {
35184
- var path8 = uriToPath(uri);
35185
- if (isAbsolutePath(path8)) {
35186
- return promises.resolve(path8);
35184
+ var path9 = uriToPath(uri);
35185
+ if (isAbsolutePath(path9)) {
35186
+ return promises.resolve(path9);
35187
35187
  } else if (base) {
35188
- return promises.resolve(resolvePath(base, path8));
35188
+ return promises.resolve(resolvePath(base, path9));
35189
35189
  } else {
35190
35190
  return promises.reject(new Error("could not find external image '" + uri + "', path of input document is unknown"));
35191
35191
  }
@@ -35194,18 +35194,18 @@ var require_files = __commonJS({
35194
35194
  read
35195
35195
  };
35196
35196
  }
35197
- var readFile = promises.promisify(fs8.readFile.bind(fs8));
35197
+ var readFile = promises.promisify(fs9.readFile.bind(fs9));
35198
35198
  function uriToPath(uriString, platform) {
35199
35199
  if (!platform) {
35200
35200
  platform = os3.platform();
35201
35201
  }
35202
35202
  var uri = url.parse(uriString);
35203
35203
  if (isLocalFileUri(uri) || isRelativeUri(uri)) {
35204
- var path8 = decodeURIComponent(uri.path);
35205
- if (platform === "win32" && /^\/[a-z]:/i.test(path8)) {
35206
- return path8.slice(1);
35204
+ var path9 = decodeURIComponent(uri.path);
35205
+ if (platform === "win32" && /^\/[a-z]:/i.test(path9)) {
35206
+ return path9.slice(1);
35207
35207
  } else {
35208
- return path8;
35208
+ return path9;
35209
35209
  }
35210
35210
  } else {
35211
35211
  throw new Error("Could not convert URI to path: " + uriString);
@@ -35406,18 +35406,18 @@ var require_docx_reader = __commonJS({
35406
35406
  readElement: contentTypesReader.readContentTypesFromXml,
35407
35407
  defaultValue: contentTypesReader.defaultContentTypes
35408
35408
  });
35409
- function readNumberingFromZipFile(zipFile, path8, styles) {
35409
+ function readNumberingFromZipFile(zipFile, path9, styles) {
35410
35410
  return xmlFileReader({
35411
- filename: path8,
35411
+ filename: path9,
35412
35412
  readElement: function(element) {
35413
35413
  return numberingXml.readNumberingXml(element, { styles });
35414
35414
  },
35415
35415
  defaultValue: numberingXml.defaultNumbering
35416
35416
  })(zipFile);
35417
35417
  }
35418
- function readStylesFromZipFile(zipFile, path8) {
35418
+ function readStylesFromZipFile(zipFile, path9) {
35419
35419
  return xmlFileReader({
35420
- filename: path8,
35420
+ filename: path9,
35421
35421
  readElement: stylesReader.readStylesXml,
35422
35422
  defaultValue: stylesReader.defaultStyles
35423
35423
  })(zipFile);
@@ -35449,10 +35449,10 @@ var require_style_map = __commonJS({
35449
35449
  });
35450
35450
  }
35451
35451
  function updateRelationships(docxFile) {
35452
- var path8 = "word/_rels/document.xml.rels";
35452
+ var path9 = "word/_rels/document.xml.rels";
35453
35453
  var relationshipsUri = "http://schemas.openxmlformats.org/package/2006/relationships";
35454
35454
  var relationshipElementName = "{" + relationshipsUri + "}Relationship";
35455
- return docxFile.read(path8, "utf8").then(xml.readString).then(function(relationshipsContainer) {
35455
+ return docxFile.read(path9, "utf8").then(xml.readString).then(function(relationshipsContainer) {
35456
35456
  var relationships = relationshipsContainer.children;
35457
35457
  addOrUpdateElement(relationships, relationshipElementName, "Id", {
35458
35458
  "Id": "rMammothStyleMap",
@@ -35460,21 +35460,21 @@ var require_style_map = __commonJS({
35460
35460
  "Target": styleMapAbsolutePath
35461
35461
  });
35462
35462
  var namespaces = { "": relationshipsUri };
35463
- return docxFile.write(path8, xml.writeString(relationshipsContainer, namespaces));
35463
+ return docxFile.write(path9, xml.writeString(relationshipsContainer, namespaces));
35464
35464
  });
35465
35465
  }
35466
35466
  function updateContentTypes(docxFile) {
35467
- var path8 = "[Content_Types].xml";
35467
+ var path9 = "[Content_Types].xml";
35468
35468
  var contentTypesUri = "http://schemas.openxmlformats.org/package/2006/content-types";
35469
35469
  var overrideName = "{" + contentTypesUri + "}Override";
35470
- return docxFile.read(path8, "utf8").then(xml.readString).then(function(typesElement) {
35470
+ return docxFile.read(path9, "utf8").then(xml.readString).then(function(typesElement) {
35471
35471
  var children = typesElement.children;
35472
35472
  addOrUpdateElement(children, overrideName, "PartName", {
35473
35473
  "PartName": styleMapAbsolutePath,
35474
35474
  "ContentType": "text/prs.mammoth.style-map"
35475
35475
  });
35476
35476
  var namespaces = { "": contentTypesUri };
35477
- return docxFile.write(path8, xml.writeString(typesElement, namespaces));
35477
+ return docxFile.write(path9, xml.writeString(typesElement, namespaces));
35478
35478
  });
35479
35479
  }
35480
35480
  function addOrUpdateElement(elements, name, identifyingAttribute, attributes) {
@@ -36171,9 +36171,9 @@ var require_document_to_html = __commonJS({
36171
36171
  };
36172
36172
  var paths = [];
36173
36173
  if (run.highlight !== null) {
36174
- var path8 = findHtmlPath({ type: "highlight", color: run.highlight });
36175
- if (path8) {
36176
- paths.push(path8);
36174
+ var path9 = findHtmlPath({ type: "highlight", color: run.highlight });
36175
+ if (path9) {
36176
+ paths.push(path9);
36177
36177
  }
36178
36178
  }
36179
36179
  if (run.isSmallCaps) {
@@ -36208,15 +36208,15 @@ var require_document_to_html = __commonJS({
36208
36208
  messages.push(unrecognisedStyleWarning("run", run));
36209
36209
  }
36210
36210
  paths.push(stylePath);
36211
- paths.forEach(function(path9) {
36212
- nodes = path9.wrap.bind(path9, nodes);
36211
+ paths.forEach(function(path10) {
36212
+ nodes = path10.wrap.bind(path10, nodes);
36213
36213
  });
36214
36214
  return nodes();
36215
36215
  }
36216
36216
  function findHtmlPathForRunProperty(elementType, defaultTagName) {
36217
- var path8 = findHtmlPath({ type: elementType });
36218
- if (path8) {
36219
- return path8;
36217
+ var path9 = findHtmlPath({ type: elementType });
36218
+ if (path9) {
36219
+ return path9;
36220
36220
  } else if (defaultTagName) {
36221
36221
  return htmlPaths.element(defaultTagName, {}, { fresh: false });
36222
36222
  } else {
@@ -37936,11 +37936,11 @@ var require_options_reader = __commonJS({
37936
37936
  var require_unzip = __commonJS({
37937
37937
  "node_modules/mammoth/lib/unzip.js"(exports2) {
37938
37938
  "use strict";
37939
- var fs8 = __require("fs");
37939
+ var fs9 = __require("fs");
37940
37940
  var promises = require_promises();
37941
37941
  var zipfile = require_zipfile();
37942
37942
  exports2.openZip = openZip;
37943
- var readFile = promises.promisify(fs8.readFile);
37943
+ var readFile = promises.promisify(fs9.readFile);
37944
37944
  function openZip(options) {
37945
37945
  if (options.path) {
37946
37946
  return readFile(options.path).then(zipfile.openArrayBuffer);
@@ -38257,8 +38257,8 @@ var require_cjs = __commonJS({
38257
38257
  return module3 = {
38258
38258
  path: basedir,
38259
38259
  exports: {},
38260
- require: function(path8, base) {
38261
- return commonjsRequire(path8, base === void 0 || base === null ? module3.path : base);
38260
+ require: function(path9, base) {
38261
+ return commonjsRequire(path9, base === void 0 || base === null ? module3.path : base);
38262
38262
  }
38263
38263
  }, fn(module3, module3.exports), module3.exports;
38264
38264
  }
@@ -38665,9 +38665,9 @@ var require_cjs = __commonJS({
38665
38665
  }
38666
38666
  return o;
38667
38667
  }
38668
- var fs8;
38668
+ var fs9;
38669
38669
  function get_fs() {
38670
- return fs8 || (fs8 = require$$0__default["default"]);
38670
+ return fs9 || (fs9 = require$$0__default["default"]);
38671
38671
  }
38672
38672
  function parse4(file, options) {
38673
38673
  if (file[0] == 80 && file[1] == 75) return parse_zip(file, options);
@@ -38952,7 +38952,7 @@ var require_cjs = __commonJS({
38952
38952
  }
38953
38953
  function read_file(filename2, options) {
38954
38954
  get_fs();
38955
- return parse4(fs8.readFileSync(filename2), options);
38955
+ return parse4(fs9.readFileSync(filename2), options);
38956
38956
  }
38957
38957
  function read(blob, options) {
38958
38958
  switch (options && options.type || "base64") {
@@ -39218,7 +39218,7 @@ var require_cjs = __commonJS({
39218
39218
  while (o.l < o.length) o.write_shift(1, 0);
39219
39219
  return o;
39220
39220
  }
39221
- function find2(cfb2, path8) {
39221
+ function find2(cfb2, path9) {
39222
39222
  var UCFullPaths = cfb2.FullPaths.map(function(x2) {
39223
39223
  return x2.toUpperCase();
39224
39224
  });
@@ -39227,11 +39227,11 @@ var require_cjs = __commonJS({
39227
39227
  return y[y.length - (x2.slice(-1) == "/" ? 2 : 1)];
39228
39228
  });
39229
39229
  var k = false;
39230
- if (path8.charCodeAt(0) === 47) {
39230
+ if (path9.charCodeAt(0) === 47) {
39231
39231
  k = true;
39232
- path8 = UCFullPaths[0].slice(0, -1) + path8;
39233
- } else k = path8.indexOf("/") !== -1;
39234
- var UCPath = path8.toUpperCase();
39232
+ path9 = UCFullPaths[0].slice(0, -1) + path9;
39233
+ } else k = path9.indexOf("/") !== -1;
39234
+ var UCPath = path9.toUpperCase();
39235
39235
  var w = k === true ? UCFullPaths.indexOf(UCPath) : UCPaths.indexOf(UCPath);
39236
39236
  if (w !== -1) return cfb2.FileIndex[w];
39237
39237
  var m = !UCPath.match(chr1);
@@ -39267,7 +39267,7 @@ var require_cjs = __commonJS({
39267
39267
  function write_file(cfb2, filename2, options) {
39268
39268
  get_fs();
39269
39269
  var o = _write(cfb2, options);
39270
- fs8.writeFileSync(filename2, o);
39270
+ fs9.writeFileSync(filename2, o);
39271
39271
  }
39272
39272
  function a2s(o) {
39273
39273
  var out = new Array(o.length);
@@ -39279,7 +39279,7 @@ var require_cjs = __commonJS({
39279
39279
  switch (options && options.type || "buffer") {
39280
39280
  case "file":
39281
39281
  get_fs();
39282
- fs8.writeFileSync(options.filename, o);
39282
+ fs9.writeFileSync(options.filename, o);
39283
39283
  return o;
39284
39284
  case "binary":
39285
39285
  return typeof o == "string" ? o : a2s(o);
@@ -44704,8 +44704,8 @@ var require_cjs = __commonJS({
44704
44704
  reader.readUInt16();
44705
44705
  var id2 = reader.readUInt16();
44706
44706
  var extension = reader.readString();
44707
- var path8 = "Root Entry/BinData/BIN".concat("".concat(id2.toString(16).toUpperCase()).padStart(4, "0"), ".").concat(extension);
44708
- var payload = cfb.find(this.container, path8).content;
44707
+ var path9 = "Root Entry/BinData/BIN".concat("".concat(id2.toString(16).toUpperCase()).padStart(4, "0"), ".").concat(extension);
44708
+ var payload = cfb.find(this.container, path9).content;
44709
44709
  this.result.binData.push(new BinData(extension, pako_1.inflate(payload, {
44710
44710
  windowBits: -15
44711
44711
  })));
@@ -46838,33 +46838,33 @@ function renderRichText({
46838
46838
  container.append(fragment);
46839
46839
  }
46840
46840
  function makePathFromDrawOPS(data) {
46841
- const path8 = new Path2D();
46841
+ const path9 = new Path2D();
46842
46842
  if (!data) {
46843
- return path8;
46843
+ return path9;
46844
46844
  }
46845
46845
  for (let i2 = 0, ii = data.length; i2 < ii; ) {
46846
46846
  switch (data[i2++]) {
46847
46847
  case DrawOPS.moveTo:
46848
- path8.moveTo(data[i2++], data[i2++]);
46848
+ path9.moveTo(data[i2++], data[i2++]);
46849
46849
  break;
46850
46850
  case DrawOPS.lineTo:
46851
- path8.lineTo(data[i2++], data[i2++]);
46851
+ path9.lineTo(data[i2++], data[i2++]);
46852
46852
  break;
46853
46853
  case DrawOPS.curveTo:
46854
- path8.bezierCurveTo(data[i2++], data[i2++], data[i2++], data[i2++], data[i2++], data[i2++]);
46854
+ path9.bezierCurveTo(data[i2++], data[i2++], data[i2++], data[i2++], data[i2++], data[i2++]);
46855
46855
  break;
46856
46856
  case DrawOPS.quadraticCurveTo:
46857
- path8.quadraticCurveTo(data[i2++], data[i2++], data[i2++], data[i2++]);
46857
+ path9.quadraticCurveTo(data[i2++], data[i2++], data[i2++], data[i2++]);
46858
46858
  break;
46859
46859
  case DrawOPS.closePath:
46860
- path8.closePath();
46860
+ path9.closePath();
46861
46861
  break;
46862
46862
  default:
46863
46863
  warn(`Unrecognized drawing path operator: ${data[i2 - 1]}`);
46864
46864
  break;
46865
46865
  }
46866
46866
  }
46867
- return path8;
46867
+ return path9;
46868
46868
  }
46869
46869
  function bindEvents(obj2, element, names) {
46870
46870
  for (const name of names) {
@@ -46972,8 +46972,8 @@ function blend(fg, bg, alpha) {
46972
46972
  return Math.round(alpha * fg + (1 - alpha) * bg);
46973
46973
  }
46974
46974
  async function node_utils_fetchData(url) {
46975
- const fs8 = process.getBuiltinModule("fs/promises");
46976
- const data = await fs8.readFile(url);
46975
+ const fs9 = process.getBuiltinModule("fs/promises");
46976
+ const data = await fs9.readFile(url);
46977
46977
  return new Uint8Array(data);
46978
46978
  }
46979
46979
  function initGPU() {
@@ -47602,11 +47602,11 @@ function network_getArrayBuffer(val) {
47602
47602
  return typeof val !== "string" ? val : stringToBytes(val).buffer;
47603
47603
  }
47604
47604
  function getReadableStream(url, opts = null) {
47605
- const fs8 = process.getBuiltinModule("fs");
47605
+ const fs9 = process.getBuiltinModule("fs");
47606
47606
  const {
47607
47607
  Readable
47608
47608
  } = process.getBuiltinModule("stream");
47609
- const readStream = fs8.createReadStream(url, opts);
47609
+ const readStream = fs9.createReadStream(url, opts);
47610
47610
  return Readable.toWeb(readStream);
47611
47611
  }
47612
47612
  function getNetworkStream(url) {
@@ -59410,11 +59410,11 @@ var init_pdf = __esm({
59410
59410
  } catch (ex) {
59411
59411
  warn(`getPathGenerator - ignoring character: "${ex}".`);
59412
59412
  }
59413
- const path8 = makePathFromDrawOPS(cmds?.path);
59413
+ const path9 = makePathFromDrawOPS(cmds?.path);
59414
59414
  if (!this.fontExtraProperties) {
59415
59415
  objs.delete(objId);
59416
59416
  }
59417
- return this.compiledGlyphs[character] = path8;
59417
+ return this.compiledGlyphs[character] = path9;
59418
59418
  }
59419
59419
  get black() {
59420
59420
  return this.#fontData.black;
@@ -61312,7 +61312,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
61312
61312
  Util.singularValueDecompose2dScale(this.baseTransform, scale);
61313
61313
  return [matrixScaleX * scale[0], matrixScaleY * scale[1]];
61314
61314
  }
61315
- drawPattern(owner, path8, useEOFill = false, [n, m], opIdx) {
61315
+ drawPattern(owner, path9, useEOFill = false, [n, m], opIdx) {
61316
61316
  const [x0, y0, x1, y1] = this.bbox;
61317
61317
  const dependencyTracker = owner.dependencyTracker;
61318
61318
  if (dependencyTracker) {
@@ -61320,9 +61320,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
61320
61320
  }
61321
61321
  owner.save();
61322
61322
  if (useEOFill) {
61323
- owner.ctx.clip(path8, "evenodd");
61323
+ owner.ctx.clip(path9, "evenodd");
61324
61324
  } else {
61325
- owner.ctx.clip(path8);
61325
+ owner.ctx.clip(path9);
61326
61326
  }
61327
61327
  owner.ctx.setTransform(...this.patternBaseMatrix);
61328
61328
  owner.ctx.translate(n * this.xstep, m * this.ystep);
@@ -62417,15 +62417,15 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62417
62417
  if (hasInnerCutout && maskX0 === layerOffsetX && maskY0 === layerOffsetY && maskX1 === layerOffsetX + layerWidth && maskY1 === layerOffsetY + layerHeight) {
62418
62418
  return;
62419
62419
  }
62420
- const path8 = new Path2D();
62421
- path8.rect(layerOffsetX, layerOffsetY, layerWidth, layerHeight);
62420
+ const path9 = new Path2D();
62421
+ path9.rect(layerOffsetX, layerOffsetY, layerWidth, layerHeight);
62422
62422
  if (hasInnerCutout) {
62423
- path8.rect(maskX0, maskY0, maskX1 - maskX0, maskY1 - maskY0);
62423
+ path9.rect(maskX0, maskY0, maskX1 - maskX0, maskY1 - maskY0);
62424
62424
  }
62425
62425
  layerCtx.save();
62426
62426
  layerCtx.globalAlpha = alpha / 255;
62427
62427
  layerCtx.setTransform(1, 0, 0, 1, 0, 0);
62428
- layerCtx.clip(path8, "evenodd");
62428
+ layerCtx.clip(path9, "evenodd");
62429
62429
  layerCtx.globalCompositeOperation = "destination-in";
62430
62430
  layerCtx.fillStyle = "#000000";
62431
62431
  layerCtx.fillRect(layerOffsetX, layerOffsetY, layerWidth, layerHeight);
@@ -62483,21 +62483,21 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62483
62483
  this._cachedGetSinglePixelWidth = null;
62484
62484
  }
62485
62485
  constructPath(opIdx, op, data, minMax) {
62486
- let [path8] = data;
62486
+ let [path9] = data;
62487
62487
  if (!minMax) {
62488
- path8 ||= data[0] = new Path2D();
62488
+ path9 ||= data[0] = new Path2D();
62489
62489
  if (op !== OPS.stroke && op !== OPS.closeStroke) {
62490
62490
  this.current.tilingPatternDims = null;
62491
62491
  }
62492
- this[op](opIdx, path8);
62492
+ this[op](opIdx, path9);
62493
62493
  return;
62494
62494
  }
62495
62495
  if (this.dependencyTracker !== null) {
62496
62496
  const outerExtraSize = op === OPS.stroke ? this.current.lineWidth / 2 : 0;
62497
62497
  this.dependencyTracker.resetBBox(opIdx).recordBBox(opIdx, this.ctx, minMax[0] - outerExtraSize, minMax[2] + outerExtraSize, minMax[1] - outerExtraSize, minMax[3] + outerExtraSize).recordDependencies(opIdx, ["transform"]);
62498
62498
  }
62499
- if (!(path8 instanceof Path2D)) {
62500
- path8 = data[0] = makePathFromDrawOPS(path8);
62499
+ if (!(path9 instanceof Path2D)) {
62500
+ path9 = data[0] = makePathFromDrawOPS(path9);
62501
62501
  }
62502
62502
  Util.axialAlignedBoundingBox(minMax, getCurrentTransform(this.ctx), this.current.minMax);
62503
62503
  const tilingDims = this.current.tilingPatternDims;
@@ -62509,13 +62509,13 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62509
62509
  this.current.fillColor.updatePatternDims(clippedBBox, tilingDims);
62510
62510
  }
62511
62511
  }
62512
- this[op](opIdx, path8);
62512
+ this[op](opIdx, path9);
62513
62513
  this._pathStartIdx = opIdx;
62514
62514
  }
62515
62515
  closePath(opIdx) {
62516
62516
  this.ctx.closePath();
62517
62517
  }
62518
- stroke(opIdx, path8, consumePath = true) {
62518
+ stroke(opIdx, path9, consumePath = true) {
62519
62519
  const started = consumePath && this.#beginKnockoutElement(this.current.strokeAlpha);
62520
62520
  const ctx = this.ctx;
62521
62521
  const strokeColor = this.current.strokeColor;
@@ -62527,26 +62527,26 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62527
62527
  ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx);
62528
62528
  if (baseTransform) {
62529
62529
  const newPath = new Path2D();
62530
- newPath.addPath(path8, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
62531
- path8 = newPath;
62530
+ newPath.addPath(path9, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
62531
+ path9 = newPath;
62532
62532
  }
62533
- this.rescaleAndStroke(path8, false);
62533
+ this.rescaleAndStroke(path9, false);
62534
62534
  ctx.restore();
62535
62535
  } else {
62536
- this.rescaleAndStroke(path8, true);
62536
+ this.rescaleAndStroke(path9, true);
62537
62537
  }
62538
62538
  }
62539
62539
  this.dependencyTracker?.recordDependencies(opIdx, Dependencies.stroke);
62540
62540
  if (consumePath) {
62541
- this.consumePath(opIdx, path8, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx)));
62541
+ this.consumePath(opIdx, path9, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx)));
62542
62542
  }
62543
62543
  ctx.globalAlpha = this.current.fillAlpha;
62544
62544
  this.#endKnockoutElement(started);
62545
62545
  }
62546
- closeStroke(opIdx, path8) {
62547
- this.stroke(opIdx, path8);
62546
+ closeStroke(opIdx, path9) {
62547
+ this.stroke(opIdx, path9);
62548
62548
  }
62549
- fill(opIdx, path8, consumePath = true) {
62549
+ fill(opIdx, path9, consumePath = true) {
62550
62550
  const started = consumePath && this.#beginKnockoutElement(this.current.fillAlpha);
62551
62551
  const ctx = this.ctx;
62552
62552
  const fillColor = this.current.fillColor;
@@ -62558,10 +62558,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62558
62558
  const dims = this.current.tilingPatternDims;
62559
62559
  const tileIdx = dims && fillColor.canSkipPatternCanvas(dims);
62560
62560
  if (tileIdx) {
62561
- fillColor.drawPattern(this, path8, this.pendingEOFill, tileIdx, opIdx);
62561
+ fillColor.drawPattern(this, path9, this.pendingEOFill, tileIdx, opIdx);
62562
62562
  this.pendingEOFill = false;
62563
62563
  if (consumePath) {
62564
- this.consumePath(opIdx, path8, intersect);
62564
+ this.consumePath(opIdx, path9, intersect);
62565
62565
  }
62566
62566
  this.current.tilingPatternDims = null;
62567
62567
  this.#endKnockoutElement(started);
@@ -62573,17 +62573,17 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62573
62573
  ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx);
62574
62574
  if (baseTransform) {
62575
62575
  const newPath = new Path2D();
62576
- newPath.addPath(path8, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
62577
- path8 = newPath;
62576
+ newPath.addPath(path9, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
62577
+ path9 = newPath;
62578
62578
  }
62579
62579
  needRestore = true;
62580
62580
  }
62581
62581
  if (this.contentVisible && intersect !== null) {
62582
62582
  if (this.pendingEOFill) {
62583
- ctx.fill(path8, "evenodd");
62583
+ ctx.fill(path9, "evenodd");
62584
62584
  this.pendingEOFill = false;
62585
62585
  } else {
62586
- ctx.fill(path8);
62586
+ ctx.fill(path9);
62587
62587
  }
62588
62588
  }
62589
62589
  if (needRestore) {
@@ -62591,38 +62591,38 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62591
62591
  this.dependencyTracker?.restore(opIdx);
62592
62592
  }
62593
62593
  if (consumePath) {
62594
- this.consumePath(opIdx, path8, intersect);
62594
+ this.consumePath(opIdx, path9, intersect);
62595
62595
  }
62596
62596
  this.#endKnockoutElement(started);
62597
62597
  }
62598
- eoFill(opIdx, path8) {
62598
+ eoFill(opIdx, path9) {
62599
62599
  this.pendingEOFill = true;
62600
- this.fill(opIdx, path8);
62600
+ this.fill(opIdx, path9);
62601
62601
  }
62602
- fillStroke(opIdx, path8) {
62602
+ fillStroke(opIdx, path9) {
62603
62603
  const started = this.#beginKnockoutElement(Math.min(this.current.fillAlpha, this.current.strokeAlpha));
62604
- this.fill(opIdx, path8, false);
62605
- this.stroke(opIdx, path8, false);
62606
- this.consumePath(opIdx, path8);
62604
+ this.fill(opIdx, path9, false);
62605
+ this.stroke(opIdx, path9, false);
62606
+ this.consumePath(opIdx, path9);
62607
62607
  this.#endKnockoutElement(started);
62608
62608
  }
62609
- eoFillStroke(opIdx, path8) {
62609
+ eoFillStroke(opIdx, path9) {
62610
62610
  this.pendingEOFill = true;
62611
- this.fillStroke(opIdx, path8);
62611
+ this.fillStroke(opIdx, path9);
62612
62612
  }
62613
- closeFillStroke(opIdx, path8) {
62614
- this.fillStroke(opIdx, path8);
62613
+ closeFillStroke(opIdx, path9) {
62614
+ this.fillStroke(opIdx, path9);
62615
62615
  }
62616
- closeEOFillStroke(opIdx, path8) {
62616
+ closeEOFillStroke(opIdx, path9) {
62617
62617
  this.pendingEOFill = true;
62618
- this.fillStroke(opIdx, path8);
62618
+ this.fillStroke(opIdx, path9);
62619
62619
  }
62620
- endPath(opIdx, path8) {
62621
- this.consumePath(opIdx, path8);
62620
+ endPath(opIdx, path9) {
62621
+ this.consumePath(opIdx, path9);
62622
62622
  }
62623
- rawFillPath(opIdx, path8) {
62623
+ rawFillPath(opIdx, path9) {
62624
62624
  const started = this.#beginKnockoutElement(this.current.fillAlpha);
62625
- this.ctx.fill(path8);
62625
+ this.ctx.fill(path9);
62626
62626
  this.dependencyTracker?.recordDependencies(opIdx, Dependencies.rawFillPath).recordOperation(opIdx);
62627
62627
  this.#endKnockoutElement(started);
62628
62628
  }
@@ -62661,12 +62661,12 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62661
62661
  x: x2,
62662
62662
  y,
62663
62663
  fontSize,
62664
- path: path8
62664
+ path: path9
62665
62665
  } of paths) {
62666
- if (!path8) {
62666
+ if (!path9) {
62667
62667
  continue;
62668
62668
  }
62669
- newPath.addPath(path8, new DOMMatrix(transform2).preMultiplySelf(invTransf).translate(x2, y).scale(fontSize, -fontSize));
62669
+ newPath.addPath(path9, new DOMMatrix(transform2).preMultiplySelf(invTransf).translate(x2, y).scale(fontSize, -fontSize));
62670
62670
  }
62671
62671
  ctx.clip(newPath);
62672
62672
  }
@@ -62759,9 +62759,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62759
62759
  this.moveText(opIdx, 0, this.current.leading);
62760
62760
  this.dependencyTracker?.recordIncrementalData("moveText", this.dependencyTracker.getSimpleIndex("leading") ?? opIdx);
62761
62761
  }
62762
- #getScaledPath(path8, currentTransform, transform2) {
62762
+ #getScaledPath(path9, currentTransform, transform2) {
62763
62763
  const newPath = new Path2D();
62764
- newPath.addPath(path8, new DOMMatrix(transform2).invertSelf().multiplySelf(currentTransform));
62764
+ newPath.addPath(path9, new DOMMatrix(transform2).invertSelf().multiplySelf(currentTransform));
62765
62765
  return newPath;
62766
62766
  }
62767
62767
  paintChar(opIdx, character, x2, y, patternFillTransform, patternStrokeTransform) {
@@ -62774,11 +62774,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62774
62774
  const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG);
62775
62775
  const patternFill = current.patternFill && !font.missingFile;
62776
62776
  const patternStroke = current.patternStroke && !font.missingFile;
62777
- let path8;
62777
+ let path9;
62778
62778
  if ((font.disableFontFace || isAddToPathSet || patternFill || patternStroke) && !font.missingFile) {
62779
- path8 = font.getPathGenerator(this.commonObjs, character);
62779
+ path9 = font.getPathGenerator(this.commonObjs, character);
62780
62780
  }
62781
- if (path8 && (font.disableFontFace || patternFill || patternStroke)) {
62781
+ if (path9 && (font.disableFontFace || patternFill || patternStroke)) {
62782
62782
  ctx.save();
62783
62783
  ctx.translate(x2, y);
62784
62784
  ctx.scale(fontSize, -fontSize);
@@ -62788,10 +62788,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62788
62788
  if (patternFillTransform) {
62789
62789
  currentTransform = ctx.getTransform();
62790
62790
  ctx.setTransform(...patternFillTransform);
62791
- const scaledPath = this.#getScaledPath(path8, currentTransform, patternFillTransform);
62791
+ const scaledPath = this.#getScaledPath(path9, currentTransform, patternFillTransform);
62792
62792
  ctx.fill(scaledPath);
62793
62793
  } else {
62794
- ctx.fill(path8);
62794
+ ctx.fill(path9);
62795
62795
  }
62796
62796
  }
62797
62797
  if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
@@ -62808,10 +62808,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62808
62808
  const transf = Util.transform([a, b, c, d, 0, 0], invPatternTransform);
62809
62809
  Util.singularValueDecompose2dScale(transf, XY);
62810
62810
  ctx.lineWidth *= Math.max(XY[0], XY[1]) / fontSize;
62811
- ctx.stroke(this.#getScaledPath(path8, currentTransform, patternStrokeTransform));
62811
+ ctx.stroke(this.#getScaledPath(path9, currentTransform, patternStrokeTransform));
62812
62812
  } else {
62813
62813
  ctx.lineWidth /= fontSize;
62814
- ctx.stroke(path8);
62814
+ ctx.stroke(path9);
62815
62815
  }
62816
62816
  }
62817
62817
  ctx.restore();
@@ -62834,7 +62834,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
62834
62834
  x: x2,
62835
62835
  y,
62836
62836
  fontSize,
62837
- path: path8
62837
+ path: path9
62838
62838
  });
62839
62839
  this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x2, y);
62840
62840
  }
@@ -63230,9 +63230,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
63230
63230
  const [x0, y0, x1, y1] = group2.bbox;
63231
63231
  clip.rect(x0, y0, x1 - x0, y1 - y0);
63232
63232
  if (group2.matrix) {
63233
- const path8 = new Path2D();
63234
- path8.addPath(clip, new DOMMatrix(group2.matrix));
63235
- clip = path8;
63233
+ const path9 = new Path2D();
63234
+ path9.addPath(clip, new DOMMatrix(group2.matrix));
63235
+ clip = path9;
63236
63236
  }
63237
63237
  currentCtx.clip(clip);
63238
63238
  }
@@ -63286,9 +63286,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
63286
63286
  const [x0, y0, x1, y1] = group2.bbox;
63287
63287
  clip.rect(x0, y0, x1 - x0, y1 - y0);
63288
63288
  if (group2.matrix) {
63289
- const path8 = new Path2D();
63290
- path8.addPath(clip, new DOMMatrix(group2.matrix));
63291
- clip = path8;
63289
+ const path9 = new Path2D();
63290
+ path9.addPath(clip, new DOMMatrix(group2.matrix));
63291
+ clip = path9;
63292
63292
  }
63293
63293
  groupCtx.clip(clip);
63294
63294
  }
@@ -63767,7 +63767,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
63767
63767
  }
63768
63768
  endCompat(opIdx) {
63769
63769
  }
63770
- consumePath(opIdx, path8, clipBox) {
63770
+ consumePath(opIdx, path9, clipBox) {
63771
63771
  const isEmpty2 = this.current.isEmptyClip();
63772
63772
  if (this.pendingClip) {
63773
63773
  this.current.updateClipFromPath();
@@ -63779,9 +63779,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
63779
63779
  if (this.pendingClip) {
63780
63780
  if (!isEmpty2) {
63781
63781
  if (this.pendingClip === EO_CLIP) {
63782
- ctx.clip(path8, "evenodd");
63782
+ ctx.clip(path9, "evenodd");
63783
63783
  } else {
63784
- ctx.clip(path8);
63784
+ ctx.clip(path9);
63785
63785
  }
63786
63786
  }
63787
63787
  this.pendingClip = null;
@@ -63854,7 +63854,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
63854
63854
  }
63855
63855
  return this._cachedScaleForStroking;
63856
63856
  }
63857
- rescaleAndStroke(path8, saveRestore) {
63857
+ rescaleAndStroke(path9, saveRestore) {
63858
63858
  const {
63859
63859
  ctx,
63860
63860
  current: {
@@ -63864,7 +63864,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
63864
63864
  const [scaleX, scaleY] = this.getScaleForStroking();
63865
63865
  if (scaleX === scaleY) {
63866
63866
  ctx.lineWidth = (lineWidth || 1) * scaleX;
63867
- ctx.stroke(path8);
63867
+ ctx.stroke(path9);
63868
63868
  return;
63869
63869
  }
63870
63870
  const dashes = ctx.getLineDash();
@@ -63875,7 +63875,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
63875
63875
  SCALE_MATRIX.a = 1 / scaleX;
63876
63876
  SCALE_MATRIX.d = 1 / scaleY;
63877
63877
  const newPath = new Path2D();
63878
- newPath.addPath(path8, SCALE_MATRIX);
63878
+ newPath.addPath(path9, SCALE_MATRIX);
63879
63879
  if (dashes.length > 0) {
63880
63880
  const scale = Math.max(scaleX, scaleY);
63881
63881
  ctx.setLineDash(dashes.map((x2) => x2 / scale));
@@ -64603,8 +64603,8 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
64603
64603
  url
64604
64604
  } = stream._source;
64605
64605
  this._isStreamingSupported = !disableStream;
64606
- const fs8 = process.getBuiltinModule("fs/promises");
64607
- fs8.lstat(url).then((stat) => {
64606
+ const fs9 = process.getBuiltinModule("fs/promises");
64607
+ fs9.lstat(url).then((stat) => {
64608
64608
  const readableStream = getReadableStream(url);
64609
64609
  this._reader = readableStream.getReader();
64610
64610
  const {
@@ -74798,11 +74798,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
74798
74798
  };
74799
74799
  ContourDrawOutline = class extends InkDrawOutline {
74800
74800
  toSVGPath() {
74801
- let path8 = super.toSVGPath();
74802
- if (!path8.endsWith("Z")) {
74803
- path8 += "Z";
74801
+ let path9 = super.toSVGPath();
74802
+ if (!path9.endsWith("Z")) {
74803
+ path9 += "Z";
74804
74804
  }
74805
- return path8;
74805
+ return path9;
74806
74806
  }
74807
74807
  };
74808
74808
  es_uint8_array_from_base64 = __webpack_require__(5213);
@@ -77507,7 +77507,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
77507
77507
  }
77508
77508
  const drawLayer = textLayerData.drawLayer;
77509
77509
  let div = textLayerData.selectionDiv;
77510
- let path8 = textLayerData.path;
77510
+ let path9 = textLayerData.path;
77511
77511
  if (!div) {
77512
77512
  const clipPathId = `clip_selection_${_DrawLayer.#selectionId++}`;
77513
77513
  div = document.createElement("div");
@@ -77526,18 +77526,18 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
77526
77526
  const clipPath = _DrawLayer._svgFactory.createElement("clipPath");
77527
77527
  clipPath.setAttribute("id", clipPathId);
77528
77528
  clipPath.setAttribute("clipPathUnits", "objectBoundingBox");
77529
- path8 = _DrawLayer._svgFactory.createElement("path");
77530
- clipPath.append(path8);
77529
+ path9 = _DrawLayer._svgFactory.createElement("path");
77530
+ clipPath.append(path9);
77531
77531
  svg.append(clipPath);
77532
77532
  div.append(svg);
77533
- textLayerData.path = path8;
77533
+ textLayerData.path = path9;
77534
77534
  textLayerData.selectionDiv = div;
77535
77535
  }
77536
77536
  if (!div.parentNode && drawLayer.#parent) {
77537
77537
  drawLayer.#parent.append(div);
77538
77538
  this.#selections.add(div);
77539
77539
  }
77540
- path8.setAttribute("d", boxes.join(" "));
77540
+ path9.setAttribute("d", boxes.join(" "));
77541
77541
  }
77542
77542
  }
77543
77543
  static get _svgFactory() {
@@ -77584,13 +77584,13 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
77584
77584
  const root2 = this.#createSVG();
77585
77585
  const defs = _DrawLayer._svgFactory.createElement("defs");
77586
77586
  root2.append(defs);
77587
- const path8 = _DrawLayer._svgFactory.createElement("path");
77588
- defs.append(path8);
77587
+ const path9 = _DrawLayer._svgFactory.createElement("path");
77588
+ defs.append(path9);
77589
77589
  const pathId = `path_${id2}`;
77590
- path8.setAttribute("id", pathId);
77591
- path8.setAttribute("vector-effect", "non-scaling-stroke");
77590
+ path9.setAttribute("id", pathId);
77591
+ path9.setAttribute("vector-effect", "non-scaling-stroke");
77592
77592
  if (isPathUpdatable) {
77593
- this.#toUpdate.set(id2, path8);
77593
+ this.#toUpdate.set(id2, path9);
77594
77594
  }
77595
77595
  const clipPathId = hasClip ? this.#createClipPath(defs, pathId) : null;
77596
77596
  const use = _DrawLayer._svgFactory.createElement("use");
@@ -77608,11 +77608,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
77608
77608
  const root2 = this.#createSVG();
77609
77609
  const defs = _DrawLayer._svgFactory.createElement("defs");
77610
77610
  root2.append(defs);
77611
- const path8 = _DrawLayer._svgFactory.createElement("path");
77612
- defs.append(path8);
77611
+ const path9 = _DrawLayer._svgFactory.createElement("path");
77612
+ defs.append(path9);
77613
77613
  const pathId = `path_${id2}`;
77614
- path8.setAttribute("id", pathId);
77615
- path8.setAttribute("vector-effect", "non-scaling-stroke");
77614
+ path9.setAttribute("id", pathId);
77615
+ path9.setAttribute("vector-effect", "non-scaling-stroke");
77616
77616
  let maskId;
77617
77617
  if (mustRemoveSelfIntersections) {
77618
77618
  const mask = _DrawLayer._svgFactory.createElement("mask");
@@ -77659,7 +77659,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
77659
77659
  root: root2,
77660
77660
  bbox,
77661
77661
  rootClass,
77662
- path: path8
77662
+ path: path9
77663
77663
  } = properties;
77664
77664
  const element = typeof elementOrId === "number" ? this.#mapping.get(elementOrId) : elementOrId;
77665
77665
  if (!element) {
@@ -77679,10 +77679,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
77679
77679
  classList.toggle(className, value);
77680
77680
  }
77681
77681
  }
77682
- if (path8) {
77682
+ if (path9) {
77683
77683
  const defs = element.firstElementChild;
77684
77684
  const pathElement = defs.firstElementChild;
77685
- this.#updateProperties(pathElement, path8);
77685
+ this.#updateProperties(pathElement, path9);
77686
77686
  }
77687
77687
  }
77688
77688
  updateParent(id2, layer) {
@@ -78373,8 +78373,8 @@ function getErrorMap() {
78373
78373
 
78374
78374
  // node_modules/zod/v3/helpers/parseUtil.js
78375
78375
  var makeIssue = (params) => {
78376
- const { data, path: path8, errorMaps, issueData } = params;
78377
- const fullPath = [...path8, ...issueData.path || []];
78376
+ const { data, path: path9, errorMaps, issueData } = params;
78377
+ const fullPath = [...path9, ...issueData.path || []];
78378
78378
  const fullIssue = {
78379
78379
  ...issueData,
78380
78380
  path: fullPath
@@ -78490,11 +78490,11 @@ var errorUtil;
78490
78490
 
78491
78491
  // node_modules/zod/v3/types.js
78492
78492
  var ParseInputLazyPath = class {
78493
- constructor(parent, value, path8, key) {
78493
+ constructor(parent, value, path9, key) {
78494
78494
  this._cachedPath = [];
78495
78495
  this.parent = parent;
78496
78496
  this.data = value;
78497
- this._path = path8;
78497
+ this._path = path9;
78498
78498
  this._key = key;
78499
78499
  }
78500
78500
  get path() {
@@ -82131,10 +82131,10 @@ function assignProp(target, prop, value) {
82131
82131
  configurable: true
82132
82132
  });
82133
82133
  }
82134
- function getElementAtPath(obj2, path8) {
82135
- if (!path8)
82134
+ function getElementAtPath(obj2, path9) {
82135
+ if (!path9)
82136
82136
  return obj2;
82137
- return path8.reduce((acc, key) => acc?.[key], obj2);
82137
+ return path9.reduce((acc, key) => acc?.[key], obj2);
82138
82138
  }
82139
82139
  function promiseAllObject(promisesObj) {
82140
82140
  const keys2 = Object.keys(promisesObj);
@@ -82454,11 +82454,11 @@ function aborted(x2, startIndex = 0) {
82454
82454
  }
82455
82455
  return false;
82456
82456
  }
82457
- function prefixIssues(path8, issues) {
82457
+ function prefixIssues(path9, issues) {
82458
82458
  return issues.map((iss) => {
82459
82459
  var _a2;
82460
82460
  (_a2 = iss).path ?? (_a2.path = []);
82461
- iss.path.unshift(path8);
82461
+ iss.path.unshift(path9);
82462
82462
  return iss;
82463
82463
  });
82464
82464
  }
@@ -92130,6 +92130,11 @@ function getInboxDir() {
92130
92130
  fs.mkdirSync(dir, { recursive: true });
92131
92131
  return dir;
92132
92132
  }
92133
+ function getBackupsDir() {
92134
+ const dir = path.join(getDataDir(), "backups");
92135
+ fs.mkdirSync(dir, { recursive: true });
92136
+ return dir;
92137
+ }
92133
92138
 
92134
92139
  // packages/db/src/runtime.ts
92135
92140
  import fs2 from "node:fs";
@@ -92155,6 +92160,8 @@ function isProcessAlive(pid) {
92155
92160
  }
92156
92161
 
92157
92162
  // packages/db/src/connection.ts
92163
+ import fs3 from "node:fs";
92164
+ import path3 from "node:path";
92158
92165
  import { DatabaseSync } from "node:sqlite";
92159
92166
 
92160
92167
  // packages/db/src/schema.ts
@@ -92473,33 +92480,155 @@ var MIGRATIONS = [
92473
92480
  // idempotent. Feeds next-round prep and consistency (lessons carry into later interviews).
92474
92481
  `
92475
92482
  ALTER TABLE interview_preps ADD COLUMN debrief TEXT NOT NULL DEFAULT '{}';
92483
+ `,
92484
+ // v7 — offer comparison (한국 총보상 필드) + company reputation research field on jobs.
92485
+ // offers: one per job (UNIQUE job_id, mirrors interview_preps). Cash pieces are numeric (만원) so
92486
+ // the server can deterministically sum an annual estimate; equity/other stay free-text (comp is
92487
+ // messy). ai_score/verdict are written by the connected AI at save time (no LLM here) and rendered
92488
+ // by the dashboard, so it never needs to call an LLM. jobs.reputation: AI-researched company
92489
+ // reputation summary (same pattern as company_overview) — feeds offer verdicts + offer-evaluation.
92490
+ `
92491
+ CREATE TABLE IF NOT EXISTS offers (
92492
+ id TEXT PRIMARY KEY,
92493
+ job_id TEXT NOT NULL UNIQUE REFERENCES jobs(id) ON DELETE CASCADE,
92494
+ base_salary INTEGER,
92495
+ bonus_amount INTEGER,
92496
+ welfare_amount INTEGER,
92497
+ signing_amount INTEGER,
92498
+ equity_note TEXT,
92499
+ comp_note TEXT,
92500
+ work_arrangement TEXT,
92501
+ contract_type TEXT,
92502
+ accept_deadline TEXT,
92503
+ ai_score INTEGER,
92504
+ verdict TEXT,
92505
+ notes TEXT,
92506
+ created_at TEXT NOT NULL,
92507
+ updated_at TEXT NOT NULL
92508
+ );
92509
+ CREATE INDEX IF NOT EXISTS idx_offers_job ON offers(job_id);
92510
+ ALTER TABLE jobs ADD COLUMN reputation TEXT;
92511
+ `,
92512
+ // v8 — offer lifecycle status. 지원 상태(전형 깔때기 = 회사의 프로세스)와 직교하는
92513
+ // "내 응답" 축: open(진행)/accepted(수락)/declined(거절). declined여도 행을 지우지
92514
+ // 않는다 — 조건(연봉·처우) 히스토리는 다음 협상의 자산이라, 대시보드가 접힌
92515
+ // '지난 오퍼'로 강등해 보여줄 뿐이다. Additive ALTER (runner가 멱등 처리).
92516
+ `
92517
+ ALTER TABLE offers ADD COLUMN offer_status TEXT NOT NULL DEFAULT 'open';
92518
+ `,
92519
+ // v9 — 한국 문항형 자소서: 회사가 지정한 문항·글자수를 공고에 구조화 보관. 한국 자소서는
92520
+ // 회사별 문항형(항목·글자수 제한)이 지배적이라, AI가 문항별로 답변을 쓰고 글자수를 점검할 때
92521
+ // 참조할 원본을 공고 옆에 둔다(별도 엔티티가 아니라 job 컨텍스트에 함께 실린다). JSON 배열을
92522
+ // TEXT에 저장(다른 JSON 필드와 동일). Additive ALTER (runner가 멱등 처리).
92523
+ `
92524
+ ALTER TABLE jobs ADD COLUMN questions TEXT NOT NULL DEFAULT '[]';
92525
+ `,
92526
+ // v10 — 탈락 회고: 어느 전형 단계에서 떨어졌는지 자가 기록. 탈락 사유를 고지하는 기업은 6%뿐이고
92527
+ // 지원자 83%가 알고 싶어하므로, 이 자가 기록이 "서류는 붙는데 면접에서 떨어진다" 같은 패턴을 로컬
92528
+ // 데이터로 드러내는 유일한 신호다. 탈락이 아니거나 미기록이면 NULL. Additive ALTER (runner가 멱등 처리).
92529
+ `
92530
+ ALTER TABLE applications ADD COLUMN rejected_stage TEXT;
92531
+ `,
92532
+ // v11 — 전형 일정: 서류마감→인적성·AI역검→1·2차 면접→발표로 이어지는 단계별 날짜를 공고에
92533
+ // 구조화 보관. 일정을 jobs에 두는 이유: 서류마감(deadline)이 이미 jobs에 있고, save_job_posting
92534
+ // 업서트를 그대로 재사용해 도구 신설 없이 갱신할 수 있다. 면접초대 메일·발표 공지를 사용자가
92535
+ // 붙여넣으면 AI가 날짜를 추출해 저장하고, 서버가 D-day·같은 날짜 충돌을 결정론으로 계산한다.
92536
+ // JSON 배열을 TEXT에 저장(다른 JSON 필드와 동일). Additive ALTER (runner가 멱등 처리).
92537
+ `
92538
+ ALTER TABLE jobs ADD COLUMN schedule TEXT NOT NULL DEFAULT '[]';
92476
92539
  `
92477
92540
  ];
92478
92541
  function migrate(db) {
92479
92542
  db.exec(`CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);`);
92480
92543
  const row = db.prepare(`SELECT value FROM _meta WHERE key = 'schema_version'`).get();
92481
92544
  const from = row ? Number(row.value) : 0;
92545
+ if (from >= MIGRATIONS.length) return { from, to: from };
92482
92546
  for (let v = from; v < MIGRATIONS.length; v++) {
92483
- db.exec(MIGRATIONS[v]);
92547
+ applyMigration(db, v);
92548
+ }
92549
+ return { from, to: MIGRATIONS.length };
92550
+ }
92551
+ function stampVersion(db, version3) {
92552
+ db.prepare(
92553
+ `INSERT INTO _meta (key, value) VALUES ('schema_version', ?)
92554
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value`
92555
+ ).run(String(version3));
92556
+ }
92557
+ function columnExists(db, table, column) {
92558
+ const rows = db.prepare(`PRAGMA table_info("${table.replaceAll('"', '""')}")`).all();
92559
+ return rows.some((r) => r.name === column);
92560
+ }
92561
+ var SELF_TRANSACTIONAL_MIGRATIONS = /* @__PURE__ */ new Set([1]);
92562
+ var ALTER_ADD_COLUMN_RE = /^[ \t]*ALTER TABLE\s+(\w+)\s+ADD COLUMN\s+(\w+)[^;]*;[ \t]*$/gm;
92563
+ function skipExistingColumns(db, sql) {
92564
+ return sql.replace(
92565
+ ALTER_ADD_COLUMN_RE,
92566
+ (stmt, table, column) => columnExists(db, table, column) ? "" : stmt
92567
+ );
92568
+ }
92569
+ function applyMigration(db, v) {
92570
+ const sql = MIGRATIONS[v];
92571
+ if (SELF_TRANSACTIONAL_MIGRATIONS.has(v)) {
92572
+ db.exec(sql);
92573
+ stampVersion(db, v + 1);
92574
+ return;
92484
92575
  }
92485
- const to = MIGRATIONS.length;
92486
- if (to !== from) {
92487
- db.prepare(
92488
- `INSERT INTO _meta (key, value) VALUES ('schema_version', ?)
92489
- ON CONFLICT(key) DO UPDATE SET value = excluded.value`
92490
- ).run(String(to));
92576
+ db.exec("BEGIN");
92577
+ try {
92578
+ db.exec(skipExistingColumns(db, sql));
92579
+ stampVersion(db, v + 1);
92580
+ db.exec("COMMIT");
92581
+ } catch (err2) {
92582
+ try {
92583
+ db.exec("ROLLBACK");
92584
+ } catch {
92585
+ }
92586
+ throw err2;
92491
92587
  }
92492
- return { from, to };
92493
92588
  }
92494
92589
 
92495
92590
  // packages/db/src/connection.ts
92496
92591
  var _db = null;
92592
+ var PRE_MIGRATE_KEEP = 5;
92593
+ function vacuumInto(db, destPath) {
92594
+ if (/[\0\n\r]/.test(destPath)) throw new Error("backup path not safe for SQL literal");
92595
+ db.exec(`VACUUM INTO '${destPath.replace(/'/g, "''")}'`);
92596
+ }
92597
+ function preMigrationBackup(db) {
92598
+ try {
92599
+ const hasMeta = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='_meta'`).get();
92600
+ if (!hasMeta) return;
92601
+ const row = db.prepare(`SELECT value FROM _meta WHERE key = 'schema_version'`).get();
92602
+ const from = row ? Number(row.value) : 0;
92603
+ if (from === 0 || from >= MIGRATIONS.length) return;
92604
+ const dir = getBackupsDir();
92605
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
92606
+ const dest = path3.join(dir, `pre-migrate-v${from}-${stamp}.sqlite`);
92607
+ try {
92608
+ vacuumInto(db, dest);
92609
+ } catch {
92610
+ fs3.copyFileSync(getDbPath(), dest);
92611
+ }
92612
+ const stale = fs3.readdirSync(dir).filter((f) => f.startsWith("pre-migrate-") && f.endsWith(".sqlite")).sort().reverse().slice(PRE_MIGRATE_KEEP);
92613
+ for (const f of stale) {
92614
+ try {
92615
+ fs3.unlinkSync(path3.join(dir, f));
92616
+ } catch {
92617
+ }
92618
+ }
92619
+ } catch (err2) {
92620
+ console.error(
92621
+ `[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)}`
92622
+ );
92623
+ }
92624
+ }
92497
92625
  function getDb() {
92498
92626
  if (_db) return _db;
92499
92627
  const db = new DatabaseSync(getDbPath());
92500
92628
  db.exec("PRAGMA journal_mode = WAL;");
92501
92629
  db.exec("PRAGMA foreign_keys = ON;");
92502
92630
  db.exec("PRAGMA busy_timeout = 5000;");
92631
+ preMigrationBackup(db);
92503
92632
  migrate(db);
92504
92633
  _db = db;
92505
92634
  return db;
@@ -92911,6 +93040,22 @@ var CoverLetterRecordSchema = external_exports.object({
92911
93040
  current_content: external_exports.string().nullable(),
92912
93041
  versions: external_exports.array(CoverLetterVersionRecordSchema).optional()
92913
93042
  });
93043
+ var JobQuestionSchema = external_exports.object({
93044
+ no: external_exports.number().int().positive().optional().describe("\uBB38\uD56D \uBC88\uD638(\uC788\uC73C\uBA74)"),
93045
+ question: external_exports.string().max(MAX_BODY).describe("\uBB38\uD56D \uD14D\uC2A4\uD2B8(\uD68C\uC0AC\uAC00 \uC81C\uC2DC\uD55C \uC790\uC18C\uC11C \uD56D\uBAA9)"),
93046
+ min_chars: external_exports.number().int().positive().optional().describe("\uAE00\uC790\uC218 \uD558\uD55C(\uC788\uC73C\uBA74)"),
93047
+ max_chars: external_exports.number().int().positive().optional().describe("\uAE00\uC790\uC218 \uC0C1\uD55C(\uC788\uC73C\uBA74)"),
93048
+ 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"),
93049
+ note: optLine.describe('\uBD80\uAC00 \uBA54\uBAA8(\uC608: "\uD544\uC218", "\uACBD\uD5D8 \uC911\uC2EC")')
93050
+ });
93051
+ var SCHEDULE_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
93052
+ var SCHEDULE_TIME_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
93053
+ var JobScheduleItemSchema = external_exports.object({
93054
+ label: reqLine.describe("\uC77C\uC815 \uC774\uB984 (\uC608: 1\uCC28 \uBA74\uC811\xB7AI\uC5ED\uAC80\xB7\uC11C\uB958 \uBC1C\uD45C)"),
93055
+ 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"),
93056
+ 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)"),
93057
+ note: optLine.describe("\uBD80\uAC00 \uBA54\uBAA8(\uC608: \uC7A5\uC18C\xB7\uC900\uBE44\uBB3C, \uC120\uD0DD)")
93058
+ });
92914
93059
  var JobInputSchema = external_exports.object({
92915
93060
  company: reqLine,
92916
93061
  position: reqLine.describe("\uC9C1\uBB34/\uD3EC\uC9C0\uC158\uBA85"),
@@ -92924,7 +93069,10 @@ var JobInputSchema = external_exports.object({
92924
93069
  source: external_exports.string().max(MAX_LINE).optional().describe("\uCD9C\uCC98 (\uC0AC\uB78C\uC778/\uC6D0\uD2F0\uB4DC/\uC9C1\uC811 \uC785\uB825 \uB4F1)"),
92925
93070
  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)"),
92926
93071
  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)"),
92927
- core_values: strList.optional().describe("\uD68C\uC0AC \uD575\uC2EC\uAC00\uCE58 (\uD0A4\uC6CC\uB4DC \uBC30\uC5F4)")
93072
+ core_values: strList.optional().describe("\uD68C\uC0AC \uD575\uC2EC\uAC00\uCE58 (\uD0A4\uC6CC\uB4DC \uBC30\uC5F4)"),
93073
+ 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)"),
93074
+ 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"),
93075
+ 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.")
92928
93076
  });
92929
93077
  var JobRecordSchema = external_exports.object({
92930
93078
  ...baseRecord,
@@ -92940,7 +93088,46 @@ var JobRecordSchema = external_exports.object({
92940
93088
  source: external_exports.string().nullable(),
92941
93089
  company_overview: external_exports.string().nullable(),
92942
93090
  talent_profile: external_exports.string().nullable(),
92943
- core_values: external_exports.array(external_exports.string())
93091
+ core_values: external_exports.array(external_exports.string()),
93092
+ reputation: external_exports.string().nullable(),
93093
+ questions: external_exports.array(JobQuestionSchema),
93094
+ schedule: external_exports.array(JobScheduleItemSchema)
93095
+ });
93096
+ var OFFER_STATUSES = ["open", "accepted", "declined"];
93097
+ var OfferInputSchema = external_exports.object({
93098
+ job_id: reqLine,
93099
+ 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)"),
93100
+ base_salary: external_exports.number().nonnegative().optional().describe("\uAE30\uBCF8\uAE09/\uC5F0\uBD09 (\uB9CC\uC6D0)"),
93101
+ bonus_amount: external_exports.number().nonnegative().optional().describe("\uC5F0 \uC131\uACFC\uAE09\xB7\uC0C1\uC5EC \uAE30\uB300\uC561 (\uB9CC\uC6D0)"),
93102
+ welfare_amount: external_exports.number().nonnegative().optional().describe("\uBCF5\uC9C0\uD3EC\uC778\uD2B8 \uB4F1 \uC5F0\uD658\uC0B0 (\uB9CC\uC6D0)"),
93103
+ signing_amount: external_exports.number().nonnegative().optional().describe("\uC0AC\uC774\uB2DD\uBCF4\uB108\uC2A4 \uC77C\uC2DC\uAE08 (\uB9CC\uC6D0)"),
93104
+ 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)"),
93105
+ comp_note: optBody.describe("\uAE30\uD0C0 \uBCF4\uC0C1 \uC790\uC720 \uC11C\uC220"),
93106
+ work_arrangement: optLine.describe("\uADFC\uBB34\uD615\uD0DC (\uC7AC\uD0DD/\uD558\uC774\uBE0C\uB9AC\uB4DC/\uCD9C\uADFC)"),
93107
+ contract_type: optLine.describe("\uACC4\uC57D\uD615\uD0DC (\uC815\uADDC/\uACC4\uC57D \uB4F1)"),
93108
+ accept_deadline: optLine.describe("\uC218\uB77D \uB9C8\uAC10\uC77C YYYY-MM-DD"),
93109
+ 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)"),
93110
+ 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)"),
93111
+ notes: optNote
93112
+ });
93113
+ var OfferRecordSchema = external_exports.object({
93114
+ ...baseRecord,
93115
+ job_id: external_exports.string(),
93116
+ offer_status: external_exports.enum(OFFER_STATUSES),
93117
+ base_salary: external_exports.number().nullable(),
93118
+ bonus_amount: external_exports.number().nullable(),
93119
+ welfare_amount: external_exports.number().nullable(),
93120
+ signing_amount: external_exports.number().nullable(),
93121
+ equity_note: external_exports.string().nullable(),
93122
+ comp_note: external_exports.string().nullable(),
93123
+ work_arrangement: external_exports.string().nullable(),
93124
+ contract_type: external_exports.string().nullable(),
93125
+ accept_deadline: external_exports.string().nullable(),
93126
+ ai_score: external_exports.number().nullable(),
93127
+ verdict: external_exports.string().nullable(),
93128
+ notes: external_exports.string().nullable(),
93129
+ /** Server-computed annual cash estimate = base + bonus + welfare (signing is one-time, excluded). Null if no numeric pieces. */
93130
+ total_comp_annual_est: external_exports.number().nullable()
92944
93131
  });
92945
93132
  var FitAnalysisInputSchema = external_exports.object({
92946
93133
  job_id: reqLine.describe("\uBD84\uC11D \uB300\uC0C1 \uACF5\uACE0 ID"),
@@ -92963,6 +93150,14 @@ var FitAnalysisRecordSchema = external_exports.object({
92963
93150
  missing_keywords: external_exports.array(external_exports.string()),
92964
93151
  recommendations: external_exports.array(external_exports.string())
92965
93152
  });
93153
+ var REJECTED_STAGES = ["document", "assessment", "interview", "final", "other"];
93154
+ var REJECTED_STAGE_LABELS = {
93155
+ document: "\uC11C\uB958",
93156
+ assessment: "\uC778\uC801\uC131\xB7AI\uC5ED\uAC80",
93157
+ interview: "\uBA74\uC811",
93158
+ final: "\uCD5C\uC885\xB7\uCC98\uC6B0",
93159
+ other: "\uAE30\uD0C0"
93160
+ };
92966
93161
  var ApplicationInputSchema = external_exports.object({
92967
93162
  job_id: reqLine,
92968
93163
  status: external_exports.enum(APPLICATION_STATUSES).optional(),
@@ -92983,7 +93178,10 @@ var ApplicationStatusUpdateSchema = external_exports.object({
92983
93178
  application_id: optLine,
92984
93179
  status: external_exports.enum(APPLICATION_STATUSES),
92985
93180
  note: optNote.describe("\uC0C1\uD0DC \uBCC0\uACBD \uC0AC\uC720/\uBA54\uBAA8"),
92986
- submission: ApplicationSubmissionSchema.optional().describe("\uC9C0\uC6D0 \uC644\uB8CC \uAE30\uB85D\uC5D0 \uB0A8\uAE38 \uC81C\uCD9C \uC790\uB8CC \uC2A4\uB0C5\uC0F7")
93181
+ submission: ApplicationSubmissionSchema.optional().describe("\uC9C0\uC6D0 \uC644\uB8CC \uAE30\uB85D\uC5D0 \uB0A8\uAE38 \uC81C\uCD9C \uC790\uB8CC \uC2A4\uB0C5\uC0F7"),
93182
+ rejected_stage: external_exports.enum(REJECTED_STAGES).optional().describe(
93183
+ "\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."
93184
+ )
92987
93185
  });
92988
93186
  var ApplicationRecordSchema = external_exports.object({
92989
93187
  ...baseRecord,
@@ -92992,7 +93190,8 @@ var ApplicationRecordSchema = external_exports.object({
92992
93190
  resume_id: external_exports.string().nullable(),
92993
93191
  cover_letter_id: external_exports.string().nullable(),
92994
93192
  applied_at: external_exports.string().nullable(),
92995
- notes: external_exports.string().nullable()
93193
+ notes: external_exports.string().nullable(),
93194
+ rejected_stage: external_exports.enum(REJECTED_STAGES).nullable()
92996
93195
  });
92997
93196
  var StarGuideSchema = external_exports.object({
92998
93197
  question: external_exports.string().max(MAX_NOTE),
@@ -93096,7 +93295,7 @@ var ApplicationContextSchema = external_exports.object({
93096
93295
  });
93097
93296
 
93098
93297
  // packages/shared/src/build-info.ts
93099
- var APP_VERSION = true ? "0.0.8" : "0.0.0-dev";
93298
+ var APP_VERSION = true ? "0.0.9" : "0.0.0-dev";
93100
93299
  var BUNDLED = true;
93101
93300
 
93102
93301
  // packages/shared/src/index.ts
@@ -93644,6 +93843,9 @@ function mapJob(r) {
93644
93843
  company_overview: r.company_overview ?? null,
93645
93844
  talent_profile: r.talent_profile ?? null,
93646
93845
  core_values: fromJson(r.core_values, []),
93846
+ reputation: r.reputation ?? null,
93847
+ questions: fromJson(r.questions, []),
93848
+ schedule: fromJson(r.schedule, []),
93647
93849
  created_at: r.created_at,
93648
93850
  updated_at: r.updated_at
93649
93851
  };
@@ -93673,7 +93875,7 @@ var jobRepo = {
93673
93875
  if (existing) {
93674
93876
  const m = { ...existing, ...input };
93675
93877
  db.prepare(
93676
- `UPDATE jobs SET company=?,position=?,url=?,location=?,employment_type=?,description=?,requirements=?,keywords=?,deadline=?,source=?,company_overview=?,talent_profile=?,core_values=?,updated_at=? WHERE id=?`
93878
+ `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=?`
93677
93879
  ).run(
93678
93880
  m.company,
93679
93881
  m.position,
@@ -93688,6 +93890,12 @@ var jobRepo = {
93688
93890
  m.company_overview ?? null,
93689
93891
  m.talent_profile ?? null,
93690
93892
  toJson(m.core_values ?? []),
93893
+ m.reputation ?? null,
93894
+ // questions omitted from input → m keeps existing.questions (preserved);
93895
+ // provided → replaced. Same merge semantics as the other JSON fields.
93896
+ toJson(m.questions ?? []),
93897
+ // schedule follows the same preserve-on-omit merge (v11 전형 일정).
93898
+ toJson(m.schedule ?? []),
93691
93899
  ts,
93692
93900
  existing.id
93693
93901
  );
@@ -93695,8 +93903,8 @@ var jobRepo = {
93695
93903
  }
93696
93904
  const newJobId = id2 ?? newId("job_");
93697
93905
  db.prepare(
93698
- `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)
93699
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
93906
+ `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)
93907
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
93700
93908
  ).run(
93701
93909
  newJobId,
93702
93910
  input.company,
@@ -93712,6 +93920,9 @@ var jobRepo = {
93712
93920
  input.company_overview ?? null,
93713
93921
  input.talent_profile ?? null,
93714
93922
  toJson(input.core_values ?? []),
93923
+ input.reputation ?? null,
93924
+ toJson(input.questions ?? []),
93925
+ toJson(input.schedule ?? []),
93715
93926
  ts,
93716
93927
  ts
93717
93928
  );
@@ -93725,6 +93936,7 @@ var jobRepo = {
93725
93936
  db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id2);
93726
93937
  db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id2);
93727
93938
  db.prepare(`DELETE FROM application_timeline_events WHERE job_id=?`).run(id2);
93939
+ db.prepare(`DELETE FROM offers WHERE job_id=?`).run(id2);
93728
93940
  return db.prepare(`DELETE FROM jobs WHERE id=?`).run(id2).changes > 0;
93729
93941
  });
93730
93942
  }
@@ -93799,6 +94011,8 @@ function mapApplication(r) {
93799
94011
  cover_letter_id: r.cover_letter_id,
93800
94012
  applied_at: r.applied_at,
93801
94013
  notes: r.notes,
94014
+ // 탈락 회고: 탈락이 아니거나 미기록이면 NULL(v10 이전 DB는 컬럼이 없어 undefined → null 정규화).
94015
+ rejected_stage: r.rejected_stage ?? null,
93802
94016
  created_at: r.created_at,
93803
94017
  updated_at: r.updated_at
93804
94018
  };
@@ -93835,17 +94049,19 @@ var applicationRepo = {
93835
94049
  const app = this.ensure(input.job_id, input.status ?? "draft");
93836
94050
  if (input.status && input.status !== app.status) assertStatusTransition(app.status, input.status);
93837
94051
  const m = { ...app, ...input };
94052
+ const rejectedStageValue = m.status === "rejected" ? m.rejected_stage ?? null : null;
93838
94053
  getDb().prepare(
93839
- `UPDATE applications SET status=?,resume_id=?,cover_letter_id=?,applied_at=?,notes=?,updated_at=? WHERE id=?`
93840
- ).run(m.status, m.resume_id ?? null, m.cover_letter_id ?? null, m.applied_at ?? null, m.notes ?? null, now(), app.id);
94054
+ `UPDATE applications SET status=?,resume_id=?,cover_letter_id=?,applied_at=?,notes=?,rejected_stage=?,updated_at=? WHERE id=?`
94055
+ ).run(m.status, m.resume_id ?? null, m.cover_letter_id ?? null, m.applied_at ?? null, m.notes ?? null, rejectedStageValue, now(), app.id);
93841
94056
  return this.get(app.id);
93842
94057
  },
93843
- setStatus(jobId, status, note) {
94058
+ setStatus(jobId, status, note, rejectedStage) {
93844
94059
  const app = this.ensure(jobId);
93845
94060
  assertStatusTransition(app.status, status);
93846
94061
  const appliedAt = status === "applied" && !app.applied_at ? now() : app.applied_at;
93847
94062
  const notes = note ? [app.notes, note].filter(Boolean).join("\n") : app.notes;
93848
- getDb().prepare(`UPDATE applications SET status=?, applied_at=?, notes=?, updated_at=? WHERE id=?`).run(status, appliedAt, notes, now(), app.id);
94063
+ const rejectedStageValue = status === "rejected" ? rejectedStage ?? app.rejected_stage ?? null : null;
94064
+ getDb().prepare(`UPDATE applications SET status=?, applied_at=?, notes=?, rejected_stage=?, updated_at=? WHERE id=?`).run(status, appliedAt, notes, rejectedStageValue, now(), app.id);
93849
94065
  return this.get(app.id);
93850
94066
  }
93851
94067
  };
@@ -93965,10 +94181,100 @@ var timelineRepo = {
93965
94181
  return getDb().prepare(`DELETE FROM application_timeline_events WHERE id=?`).run(id2).changes > 0;
93966
94182
  }
93967
94183
  };
94184
+ function mapOffer(r) {
94185
+ const base = r.base_salary ?? null;
94186
+ const bonus = r.bonus_amount ?? null;
94187
+ const welfare = r.welfare_amount ?? null;
94188
+ const hasAny = base != null || bonus != null || welfare != null;
94189
+ const total = hasAny ? (base ?? 0) + (bonus ?? 0) + (welfare ?? 0) : null;
94190
+ return {
94191
+ id: r.id,
94192
+ job_id: r.job_id,
94193
+ offer_status: r.offer_status ?? "open",
94194
+ base_salary: base,
94195
+ bonus_amount: bonus,
94196
+ welfare_amount: welfare,
94197
+ signing_amount: r.signing_amount ?? null,
94198
+ equity_note: r.equity_note ?? null,
94199
+ comp_note: r.comp_note ?? null,
94200
+ work_arrangement: r.work_arrangement ?? null,
94201
+ contract_type: r.contract_type ?? null,
94202
+ accept_deadline: r.accept_deadline ?? null,
94203
+ ai_score: r.ai_score ?? null,
94204
+ verdict: r.verdict ?? null,
94205
+ notes: r.notes ?? null,
94206
+ total_comp_annual_est: total,
94207
+ created_at: r.created_at,
94208
+ updated_at: r.updated_at
94209
+ };
94210
+ }
94211
+ var offerRepo = {
94212
+ getByJob(jobId) {
94213
+ const r = getDb().prepare(`SELECT * FROM offers WHERE job_id=?`).get(jobId);
94214
+ return r ? mapOffer(r) : null;
94215
+ },
94216
+ list() {
94217
+ return getDb().prepare(`SELECT * FROM offers ORDER BY ai_score IS NULL, ai_score DESC, updated_at DESC`).all().map(mapOffer);
94218
+ },
94219
+ /** Upsert by job_id (one offer per job). Missing fields are preserved on update (partial-safe). */
94220
+ save(input) {
94221
+ const db = getDb();
94222
+ const existing = this.getByJob(input.job_id);
94223
+ const ts = now();
94224
+ const n = (v, prev) => v ?? prev ?? null;
94225
+ const s = (v, prev) => v ?? prev ?? null;
94226
+ if (existing) {
94227
+ db.prepare(
94228
+ `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=?`
94229
+ ).run(
94230
+ input.offer_status ?? existing.offer_status,
94231
+ n(input.base_salary, existing.base_salary),
94232
+ n(input.bonus_amount, existing.bonus_amount),
94233
+ n(input.welfare_amount, existing.welfare_amount),
94234
+ n(input.signing_amount, existing.signing_amount),
94235
+ s(input.equity_note, existing.equity_note),
94236
+ s(input.comp_note, existing.comp_note),
94237
+ s(input.work_arrangement, existing.work_arrangement),
94238
+ s(input.contract_type, existing.contract_type),
94239
+ s(input.accept_deadline, existing.accept_deadline),
94240
+ n(input.ai_score, existing.ai_score),
94241
+ s(input.verdict, existing.verdict),
94242
+ s(input.notes, existing.notes),
94243
+ ts,
94244
+ existing.id
94245
+ );
94246
+ return this.getByJob(input.job_id);
94247
+ }
94248
+ const id2 = newId("ofr_");
94249
+ db.prepare(
94250
+ `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)
94251
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
94252
+ ).run(
94253
+ id2,
94254
+ input.job_id,
94255
+ input.offer_status ?? "open",
94256
+ input.base_salary ?? null,
94257
+ input.bonus_amount ?? null,
94258
+ input.welfare_amount ?? null,
94259
+ input.signing_amount ?? null,
94260
+ input.equity_note ?? null,
94261
+ input.comp_note ?? null,
94262
+ input.work_arrangement ?? null,
94263
+ input.contract_type ?? null,
94264
+ input.accept_deadline ?? null,
94265
+ input.ai_score ?? null,
94266
+ input.verdict ?? null,
94267
+ input.notes ?? null,
94268
+ ts,
94269
+ ts
94270
+ );
94271
+ return this.getByJob(input.job_id);
94272
+ }
94273
+ };
93968
94274
 
93969
94275
  // packages/mcp-tools/src/tools.ts
93970
- import fs7 from "node:fs";
93971
- import path7 from "node:path";
94276
+ import fs8 from "node:fs";
94277
+ import path8 from "node:path";
93972
94278
 
93973
94279
  // packages/core/src/onboarding.ts
93974
94280
  function profileCompleteness(p) {
@@ -94593,12 +94899,12 @@ function saveCoverLetterVersion(input) {
94593
94899
  }
94594
94900
  return { coverLetter, version: version3, verification, forced };
94595
94901
  }
94596
- function updateApplicationStatus(jobId, status, note, submission) {
94902
+ function updateApplicationStatus(jobId, status, note, submission, rejectedStage) {
94597
94903
  const job = jobRepo.get(jobId);
94598
94904
  if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
94599
94905
  const before2 = applicationRepo.getByJob(jobId);
94600
94906
  const application = tx(() => {
94601
- let application2 = applicationRepo.setStatus(jobId, status, note);
94907
+ let application2 = applicationRepo.setStatus(jobId, status, note, rejectedStage);
94602
94908
  const submittedDocs = (submission?.document_ids ?? []).map((docId) => documentRepo.get(docId)).filter((doc) => !!doc);
94603
94909
  const submittedCoverLetter = submission?.cover_letter_id ? coverLetterRepo.get(submission.cover_letter_id, true) : null;
94604
94910
  if (status === "applied" && submission) {
@@ -94634,7 +94940,9 @@ function updateApplicationStatus(jobId, status, note, submission) {
94634
94940
  status,
94635
94941
  status_label: APPLICATION_STATUS_LABELS[status],
94636
94942
  note: note ?? null,
94637
- submission: submissionPayload
94943
+ submission: submissionPayload,
94944
+ // 탈락 회고: rejected로 넘어갈 때 실제 저장된 단계(setStatus가 정합성 처리한 값)를 타임라인에도 남긴다.
94945
+ rejected_stage: application2.rejected_stage
94638
94946
  },
94639
94947
  occurred_at: submittedAtToOccurrence(submittedAt)
94640
94948
  });
@@ -94667,8 +94975,21 @@ function saveInterviewPrep(input) {
94667
94975
  // packages/core/src/summary.ts
94668
94976
  var DEADLINE_ACTIONABLE = ["draft", "planned"];
94669
94977
  var DEADLINE_SOON_DAYS = 14;
94670
- var FOLLOWUP_STALE_DAYS = 10;
94978
+ var SCHEDULE_SOON_DAYS = 7;
94671
94979
  var DAY_MS = 864e5;
94980
+ function collectFutureSchedule(jobs, now2) {
94981
+ const items = [];
94982
+ for (const job of jobs) {
94983
+ for (const s of job.schedule ?? []) {
94984
+ const d_day = daysUntilDate(s.date, now2);
94985
+ if (d_day == null || d_day < 0) continue;
94986
+ items.push({ job, label: s.label, date: s.date, time: s.time, d_day });
94987
+ }
94988
+ }
94989
+ return items.sort(
94990
+ (a, b) => a.date < b.date ? -1 : a.date > b.date ? 1 : (a.time || "99:99").localeCompare(b.time || "99:99")
94991
+ );
94992
+ }
94672
94993
  function daysUntilDate(dateStr, now2) {
94673
94994
  if (!dateStr) return null;
94674
94995
  const d = /* @__PURE__ */ new Date(`${dateStr}T00:00:00`);
@@ -94683,6 +95004,24 @@ function daysSinceTimestamp(iso, now2) {
94683
95004
  if (isNaN(d.getTime())) return null;
94684
95005
  return Math.floor((now2.getTime() - d.getTime()) / DAY_MS);
94685
95006
  }
95007
+ function businessDaysSince(iso, now2) {
95008
+ if (!iso) return null;
95009
+ const start = new Date(iso);
95010
+ if (isNaN(start.getTime())) return null;
95011
+ const s = new Date(start);
95012
+ s.setHours(0, 0, 0, 0);
95013
+ const e = new Date(now2);
95014
+ e.setHours(0, 0, 0, 0);
95015
+ if (e.getTime() <= s.getTime()) return 0;
95016
+ let count = 0;
95017
+ const cursor = new Date(s);
95018
+ while (cursor.getTime() < e.getTime()) {
95019
+ cursor.setDate(cursor.getDate() + 1);
95020
+ const dow = cursor.getDay();
95021
+ if (dow !== 0 && dow !== 6) count++;
95022
+ }
95023
+ return count;
95024
+ }
94686
95025
  function jobWithMeta(job) {
94687
95026
  const app = applicationRepo.getByJob(job.id);
94688
95027
  const fit = fitRepo.getByJob(job.id);
@@ -94694,18 +95033,36 @@ function listRecentActivity(limit = 20) {
94694
95033
  }
94695
95034
  var POST_PASS_STALE_DAYS = 7;
94696
95035
  var POST_INTERVIEW_STALE_DAYS = 7;
95036
+ var FOLLOWUP_DUE_BIZ_DAYS = 5;
95037
+ var FOLLOWUP_OVERDUE_BIZ_DAYS = 8;
95038
+ var FOLLOWUP_ONHOLD_CAL_DAYS = 45;
94697
95039
  function getActionDigest(now2 = /* @__PURE__ */ new Date()) {
94698
95040
  const applications = applicationRepo.list();
94699
95041
  const jobs = jobRepo.list();
94700
95042
  const statusByJob = new Map(applications.map((a) => [a.job_id, a]));
95043
+ const jobById = new Map(jobs.map((j) => [j.id, j]));
95044
+ const preppedJobIds = new Set(interviewRepo.list().map((p) => p.job_id));
94701
95045
  const followups = [];
94702
95046
  for (const a of applications) {
94703
- const job = jobRepo.get(a.job_id);
95047
+ const job = jobById.get(a.job_id);
94704
95048
  if (!job) continue;
94705
95049
  if (a.status === "applied") {
94706
- const days = daysSinceTimestamp(a.applied_at, now2);
94707
- if (days != null && days >= FOLLOWUP_STALE_DAYS)
94708
- 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` });
95050
+ const cal = daysSinceTimestamp(a.applied_at, now2);
95051
+ const biz = businessDaysSince(a.applied_at, now2);
95052
+ if (biz != null && biz >= FOLLOWUP_DUE_BIZ_DAYS) {
95053
+ const overdue = biz >= FOLLOWUP_OVERDUE_BIZ_DAYS;
95054
+ const item = {
95055
+ job,
95056
+ status: a.status,
95057
+ status_label: APPLICATION_STATUS_LABELS[a.status],
95058
+ days_since: cal ?? 0,
95059
+ business_days_since: biz,
95060
+ followup_timing: overdue ? "overdue" : "due",
95061
+ 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)`
95062
+ };
95063
+ if (cal != null && cal >= FOLLOWUP_ONHOLD_CAL_DAYS) item.suggest_on_hold = true;
95064
+ followups.push(item);
95065
+ }
94709
95066
  } else if (a.status === "document_passed") {
94710
95067
  const days = daysSinceTimestamp(a.updated_at, now2);
94711
95068
  if (days != null && days >= POST_PASS_STALE_DAYS)
@@ -94724,14 +95081,52 @@ function getActionDigest(now2 = /* @__PURE__ */ new Date()) {
94724
95081
  if (days_left == null || days_left > DEADLINE_SOON_DAYS) return null;
94725
95082
  return { job, status, status_label: APPLICATION_STATUS_LABELS[status], days_left };
94726
95083
  }).filter((x2) => x2 !== null).sort((a, b) => a.days_left - b.days_left);
94727
- const interview_todo = applications.filter((a) => INTERVIEW_UNLOCK_STATUSES.includes(a.status) && !interviewRepo.getByJob(a.job_id)).map((a) => {
94728
- const job = jobRepo.get(a.job_id);
95084
+ const interview_todo = applications.filter((a) => INTERVIEW_UNLOCK_STATUSES.includes(a.status) && !preppedJobIds.has(a.job_id)).map((a) => {
95085
+ const job = jobById.get(a.job_id);
94729
95086
  return job ? { job, status_label: APPLICATION_STATUS_LABELS[a.status] } : null;
94730
95087
  }).filter((x2) => x2 !== null);
95088
+ const futureSchedule = collectFutureSchedule(jobs, now2);
95089
+ const upcoming_schedule = futureSchedule.filter((x2) => x2.d_day <= SCHEDULE_SOON_DAYS).map((x2) => ({
95090
+ job_id: x2.job.id,
95091
+ company: x2.job.company,
95092
+ position: x2.job.position,
95093
+ label: x2.label,
95094
+ ...x2.time ? { time: x2.time } : {},
95095
+ date: x2.date,
95096
+ d_day: x2.d_day
95097
+ }));
95098
+ const byDate = /* @__PURE__ */ new Map();
95099
+ for (const x2 of futureSchedule) {
95100
+ const arr = byDate.get(x2.date) ?? [];
95101
+ arr.push(x2);
95102
+ byDate.set(x2.date, arr);
95103
+ }
95104
+ const schedule_conflicts = [];
95105
+ for (const [date3, items] of byDate) {
95106
+ const distinctJobs = new Set(items.map((x2) => x2.job.id));
95107
+ if (distinctJobs.size < 2) continue;
95108
+ schedule_conflicts.push({
95109
+ date: date3,
95110
+ items: items.map((x2) => ({
95111
+ job_id: x2.job.id,
95112
+ company: x2.job.company,
95113
+ position: x2.job.position,
95114
+ label: x2.label,
95115
+ ...x2.time ? { time: x2.time } : {}
95116
+ }))
95117
+ });
95118
+ }
95119
+ schedule_conflicts.sort((a, b) => a.date < b.date ? -1 : a.date > b.date ? 1 : 0);
94731
95120
  const today = `${now2.getFullYear()}-${String(now2.getMonth() + 1).padStart(2, "0")}-${String(now2.getDate()).padStart(2, "0")}`;
94732
- return { today, followups, deadlines, interview_todo };
95121
+ return { today, followups, deadlines, interview_todo, upcoming_schedule, schedule_conflicts };
94733
95122
  }
94734
95123
  var normToken = (s) => s.toLowerCase().trim().replace(/^[.,/()\s]+|[.,/()\s]+$/g, "");
95124
+ var ASCII_TOKEN_RE = /^[a-z0-9+#.]+$/;
95125
+ var escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
95126
+ function resumeBodyHasToken(text, token) {
95127
+ if (!ASCII_TOKEN_RE.test(token)) return text.includes(token);
95128
+ return new RegExp(`(?<![a-z0-9])${escapeRegExp(token)}(?![a-z0-9])`, "i").test(text);
95129
+ }
94735
95130
  function prescreenJob(jobId) {
94736
95131
  const job = jobRepo.get(jobId);
94737
95132
  if (!job) return null;
@@ -94740,17 +95135,36 @@ function prescreenJob(jobId) {
94740
95135
  for (const e of experienceRepo.list()) for (const t of e.tech ?? []) userTokens.add(normToken(t));
94741
95136
  for (const p of projectRepo.list()) for (const t of p.tech ?? []) userTokens.add(normToken(t));
94742
95137
  userTokens.delete("");
95138
+ const resume = documentRepo.primary("resume");
95139
+ const resume_checked = resume != null;
95140
+ const resumeText = (resume?.content ?? "").toLowerCase();
94743
95141
  const keywords = (job.keywords ?? []).filter((k) => k && k.trim());
94744
95142
  const matched = [];
94745
95143
  const missing = [];
95144
+ const resume_matched = [];
94746
95145
  for (const kw of keywords) {
94747
95146
  const n = normToken(kw);
94748
- const hit = userTokens.has(n) || // substring match only for tokens ≥3 chars (avoid "c"/"r" matching everything).
95147
+ const skillHit = userTokens.has(n) || // substring match only for tokens ≥3 chars (avoid "c"/"r" matching everything).
94749
95148
  n.length >= 3 && [...userTokens].some((u) => u.length >= 3 && (u.includes(n) || n.includes(u)));
94750
- (hit ? matched : missing).push(kw);
95149
+ (skillHit ? matched : missing).push(kw);
95150
+ if (resume_checked && n.length >= 2 && resumeBodyHasToken(resumeText, n)) resume_matched.push(kw);
94751
95151
  }
94752
95152
  const coverage = keywords.length ? matched.length / keywords.length : null;
94753
- return { job_id: jobId, job_keywords: keywords, matched, missing, coverage, user_skill_count: userTokens.size };
95153
+ const combinedSet = /* @__PURE__ */ new Set([...matched, ...resume_matched]);
95154
+ const combined_matched = keywords.filter((k) => combinedSet.has(k));
95155
+ const combined_coverage = keywords.length ? combined_matched.length / keywords.length : null;
95156
+ return {
95157
+ job_id: jobId,
95158
+ job_keywords: keywords,
95159
+ matched,
95160
+ missing,
95161
+ coverage,
95162
+ user_skill_count: userTokens.size,
95163
+ resume_matched,
95164
+ combined_matched,
95165
+ combined_coverage,
95166
+ resume_checked
95167
+ };
94754
95168
  }
94755
95169
  var PROGRESSION = ["applied", "document_passed", "interview", "final_passed"];
94756
95170
  function getPipelineStats() {
@@ -94779,6 +95193,12 @@ function getPipelineStats() {
94779
95193
  }
94780
95194
  const pct = (n, d) => d > 0 ? Math.round(n / d * 1e3) / 10 : null;
94781
95195
  const cur = (s) => applications.filter((a) => a.status === s).length;
95196
+ const rejectedApps = applications.filter((a) => a.status === "rejected");
95197
+ const rejected_by_stage = REJECTED_STAGES.map((stage) => ({
95198
+ stage,
95199
+ count: rejectedApps.filter((a) => a.rejected_stage === stage).length
95200
+ })).filter((s) => s.count > 0);
95201
+ const rejected_unrecorded = rejectedApps.filter((a) => !a.rejected_stage).length;
94782
95202
  return {
94783
95203
  total: applications.length,
94784
95204
  by_status,
@@ -94790,12 +95210,14 @@ function getPipelineStats() {
94790
95210
  in_progress: cur("applied") + cur("document_passed") + cur("interview"),
94791
95211
  on_hold: cur("on_hold")
94792
95212
  },
95213
+ rejected_by_stage,
95214
+ rejected_unrecorded,
94793
95215
  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."
94794
95216
  };
94795
95217
  }
94796
95218
 
94797
95219
  // packages/core/src/jobsearch.ts
94798
- var UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36";
95220
+ var UA = `careermate/${APP_VERSION} (+https://careermate.life)`;
94799
95221
  function toYmd(v) {
94800
95222
  if (typeof v !== "string" || v.length < 10) return void 0;
94801
95223
  const m = v.match(/^(\d{4}-\d{2}-\d{2})/);
@@ -94885,6 +95307,24 @@ async function searchJobs(query, opts = {}) {
94885
95307
  };
94886
95308
  }
94887
95309
 
95310
+ // packages/core/src/offers.ts
95311
+ function withJob(offer, job) {
95312
+ return { ...offer, company: job?.company ?? "\u2014", position: job?.position ?? "\u2014" };
95313
+ }
95314
+ function saveOffer(input) {
95315
+ const job = jobRepo.get(input.job_id);
95316
+ 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.");
95317
+ return withJob(offerRepo.save(input), job);
95318
+ }
95319
+ function getOffer(jobId) {
95320
+ const offer = offerRepo.getByJob(jobId);
95321
+ if (!offer) return null;
95322
+ return withJob(offer, jobRepo.get(jobId));
95323
+ }
95324
+ function compareOffers() {
95325
+ return offerRepo.list().map((o) => withJob(o, jobRepo.get(o.job_id)));
95326
+ }
95327
+
94888
95328
  // packages/prompts/src/humanize.ts
94889
95329
  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
94890
95330
 
@@ -94913,12 +95353,15 @@ var HUMANIZE_WRITING_GUIDE = `# \uC0AC\uB78C\uC774 \uC4F4 \uAC83\uCC98\uB7FC: \u
94913
95353
  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.
94914
95354
  - "\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.
94915
95355
  - \uBB38\uB2E8\uB9C8\uB2E4 \uAE38\uC774\uC640 \uC2DC\uC791 \uBC29\uC2DD\uC744 \uC77C\uBD80\uB7EC \uB2E4\uB974\uAC8C \uD55C\uB2E4.
95356
+ - \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.
94916
95357
 
94917
95358
  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".
94918
95359
  - \uC811\uC18D\uC5B4 \uC5C6\uC774 \uBB38\uC7A5\uC744 \uC787\uAC70\uB098, \uC0AC\uAC74\uC758 \uD750\uB984 \uC790\uCCB4\uB85C \uC5F0\uACB0\uD55C\uB2E4.
94919
95360
 
94920
95361
  5. \uADE0\uC77C\uD55C \uBB38\uC7A5 \uB9AC\uB4EC \u2014 \uBAA8\uB4E0 \uBB38\uC7A5\uC774 \uBE44\uC2B7\uD55C \uAE38\uC774.
94921
95362
  - \uC9E7\uC740 \uBB38\uC7A5\uACFC \uAE34 \uBB38\uC7A5\uC744 \uC11E\uB294\uB2E4. \uAC00\uB054 \uC544\uC8FC \uC9E7\uAC8C \uB04A\uB294\uB2E4. \uC774\uB807\uAC8C.
95363
+ - \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.
95364
+ - \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.
94922
95365
 
94923
95366
  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".
94924
95367
  - \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.
@@ -94946,6 +95389,7 @@ var STRUCTURED_DOC_WRITING_GUIDE = `# \uAD6C\uC870\uD615 \uBB38\uC11C(\uACBD\uB8
94946
95389
  - \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.
94947
95390
  - \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)
94948
95391
  - 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.
95392
+ - \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.
94949
95393
 
94950
95394
  ## Do / Don't
94951
95395
  - 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.
@@ -95097,11 +95541,11 @@ function renderWorkflowMarkdown(id2) {
95097
95541
  }
95098
95542
 
95099
95543
  // packages/knowledge/src/index.ts
95100
- import fs3 from "node:fs";
95101
- import path3 from "node:path";
95544
+ import fs4 from "node:fs";
95545
+ import path4 from "node:path";
95102
95546
  import { fileURLToPath } from "node:url";
95103
- var here = path3.dirname(fileURLToPath(import.meta.url));
95104
- var CAREER_OS_DIR = BUNDLED ? path3.join(here, "career-os") : path3.resolve(here, "..", "..", "..", "docs", "career-os");
95547
+ var here = path4.dirname(fileURLToPath(import.meta.url));
95548
+ var CAREER_OS_DIR = BUNDLED ? path4.join(here, "career-os") : path4.resolve(here, "..", "..", "..", "docs", "career-os");
95105
95549
  var EXPERT_DOMAINS = [
95106
95550
  "resume",
95107
95551
  "ats",
@@ -95133,7 +95577,7 @@ var cache = /* @__PURE__ */ new Map();
95133
95577
  function readDoc(rel) {
95134
95578
  let cached2 = cache.get(rel);
95135
95579
  if (cached2 === void 0) {
95136
- cached2 = fs3.readFileSync(path3.join(CAREER_OS_DIR, rel), "utf8");
95580
+ cached2 = fs4.readFileSync(path4.join(CAREER_OS_DIR, rel), "utf8");
95137
95581
  cache.set(rel, cached2);
95138
95582
  }
95139
95583
  return cached2;
@@ -95142,7 +95586,7 @@ function getPlaybook(domain) {
95142
95586
  return readDoc(`knowledge/${domain}.md`);
95143
95587
  }
95144
95588
  function getVerifier(id2) {
95145
- return readDoc(path3.join("knowledge", "verifiers", `${id2}.md`));
95589
+ return readDoc(path4.join("knowledge", "verifiers", `${id2}.md`));
95146
95590
  }
95147
95591
  function getEop(feature) {
95148
95592
  return readDoc(`eop/${feature}.md`);
@@ -112765,16 +113209,16 @@ var replacer = ({ json, patch, patchText, context, keepOriginalStyles = true })
112765
113209
  didFindOccurrence: true
112766
113210
  };
112767
113211
  };
112768
- var goToElementFromPath = (json, path8) => {
113212
+ var goToElementFromPath = (json, path9) => {
112769
113213
  let element = json;
112770
- for (let i2 = 1; i2 < path8.length; i2++) {
112771
- const index = path8[i2];
113214
+ for (let i2 = 1; i2 < path9.length; i2++) {
113215
+ const index = path9[i2];
112772
113216
  element = element.elements[index];
112773
113217
  }
112774
113218
  return element;
112775
113219
  };
112776
- var goToParentElementFromPath = (json, path8) => goToElementFromPath(json, path8.slice(0, path8.length - 1));
112777
- var getLastElementIndexFromPath = (path8) => path8[path8.length - 1];
113220
+ var goToParentElementFromPath = (json, path9) => goToElementFromPath(json, path9.slice(0, path9.length - 1));
113221
+ var getLastElementIndexFromPath = (path9) => path9[path9.length - 1];
112778
113222
  var PatchType = {
112779
113223
  /** Replace entire file-level elements (e.g., whole paragraphs) */
112780
113224
  DOCUMENT: "file",
@@ -113334,8 +113778,8 @@ function decodeEntities(input) {
113334
113778
  }
113335
113779
 
113336
113780
  // packages/parsers/src/documents.ts
113337
- import fs4 from "node:fs";
113338
- import path4 from "node:path";
113781
+ import fs5 from "node:fs";
113782
+ import path5 from "node:path";
113339
113783
  import os2 from "node:os";
113340
113784
 
113341
113785
  // node_modules/fflate/esm/index.mjs
@@ -113826,7 +114270,7 @@ var import_mammoth = __toESM(require_lib6(), 1);
113826
114270
  var import_hwp = __toESM(require_cjs(), 1);
113827
114271
  var parseHwp = import_hwp.default?.parse ?? import_hwp.default?.default?.parse;
113828
114272
  var MAX_BYTES = 25 * 1024 * 1024;
113829
- var SENSITIVE_PATH_RE = /(^|[/\\])\.(ssh|aws|gnupg)([/\\]|$)|(^|[/\\])id_(rsa|dsa|ecdsa|ed25519)(\.[\w]+)?$|\.(pem|ppk)$|(^|[/\\])\.env(\.[\w-]+)?$|(^|[/\\])\.(npmrc|netrc|git-credentials)$/i;
114273
+ 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;
113830
114274
  var MAX_HWPX_ENTRY_BYTES = 40 * 1024 * 1024;
113831
114275
  var MAX_HWPX_TOTAL_BYTES = 80 * 1024 * 1024;
113832
114276
  var MAX_PDF_PAGES = 300;
@@ -113840,8 +114284,8 @@ var MD_RE = /\.(md|markdown|mdown|mkd)$/i;
113840
114284
  var LEGACY_BINARY_RE = /\.(docm?|dot[xm]?|pptx?|ppt|xlsx?|xls|odt|ods|odp|rtf|pages|key|numbers)$/i;
113841
114285
  function resolveDocumentPath(input, baseDir) {
113842
114286
  let p = (input ?? "").trim().replace(/^["']|["']$/g, "");
113843
- if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) p = path4.join(os2.homedir(), p.slice(1));
113844
- if (!path4.isAbsolute(p)) p = path4.resolve(baseDir ?? process.cwd(), p);
114287
+ if (p === "~" || p.startsWith("~/") || p.startsWith("~\\")) p = path5.join(os2.homedir(), p.slice(1));
114288
+ if (!path5.isAbsolute(p)) p = path5.resolve(baseDir ?? process.cwd(), p);
113845
114289
  return p;
113846
114290
  }
113847
114291
  async function extractDocument(input, baseDir) {
@@ -113858,7 +114302,7 @@ async function extractDocument(input, baseDir) {
113858
114302
  });
113859
114303
  let realPath = filePath;
113860
114304
  try {
113861
- realPath = fs4.realpathSync.native(filePath);
114305
+ realPath = fs5.realpathSync.native(filePath);
113862
114306
  } catch {
113863
114307
  }
113864
114308
  if (SENSITIVE_PATH_RE.test(filePath) || SENSITIVE_PATH_RE.test(realPath)) {
@@ -113872,7 +114316,7 @@ async function extractDocument(input, baseDir) {
113872
114316
  }
113873
114317
  let stat;
113874
114318
  try {
113875
- stat = fs4.statSync(filePath);
114319
+ stat = fs5.statSync(filePath);
113876
114320
  } catch {
113877
114321
  return base("unsupported", { warnings: [`\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath}`], unsupported: true, errored: true });
113878
114322
  }
@@ -113891,7 +114335,7 @@ async function extractDocument(input, baseDir) {
113891
114335
  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."],
113892
114336
  unsupported: true
113893
114337
  });
113894
- const name = path4.basename(filePath);
114338
+ const name = path5.basename(filePath);
113895
114339
  const bytes = stat.size;
113896
114340
  if (IMAGE_RE.test(name)) {
113897
114341
  return base("image", {
@@ -113905,7 +114349,7 @@ async function extractDocument(input, baseDir) {
113905
114349
  if (TEXT_RE.test(name) || MD_RE.test(name)) {
113906
114350
  let textBuf;
113907
114351
  try {
113908
- textBuf = fs4.readFileSync(filePath);
114352
+ textBuf = fs5.readFileSync(filePath);
113909
114353
  } catch (e) {
113910
114354
  return base("unsupported", { bytes, warnings: [`\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${errMsg(e)}`], unsupported: true, errored: true });
113911
114355
  }
@@ -113920,7 +114364,7 @@ async function extractDocument(input, baseDir) {
113920
114364
  }
113921
114365
  let buf;
113922
114366
  try {
113923
- buf = fs4.readFileSync(filePath);
114367
+ buf = fs5.readFileSync(filePath);
113924
114368
  } catch (e) {
113925
114369
  return base("unsupported", { bytes, warnings: [`\uD30C\uC77C\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${errMsg(e)}`], unsupported: true, errored: true });
113926
114370
  }
@@ -113932,7 +114376,7 @@ async function extractDocument(input, baseDir) {
113932
114376
  return base("unsupported", {
113933
114377
  bytes,
113934
114378
  warnings: [
113935
- `${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.`
114379
+ `${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.`
113936
114380
  ],
113937
114381
  unsupported: true
113938
114382
  });
@@ -114199,18 +114643,18 @@ function toCallToolResult(r) {
114199
114643
 
114200
114644
  // packages/mcp-tools/src/bridge.ts
114201
114645
  import { spawn, spawnSync } from "node:child_process";
114202
- import fs5 from "node:fs";
114203
- import path5 from "node:path";
114646
+ import fs6 from "node:fs";
114647
+ import path6 from "node:path";
114204
114648
  import { fileURLToPath as fileURLToPath2 } from "node:url";
114205
- var __dirname = path5.dirname(fileURLToPath2(import.meta.url));
114206
- var REPO_ROOT = path5.resolve(__dirname, "..", "..", "..");
114649
+ var __dirname = path6.dirname(fileURLToPath2(import.meta.url));
114650
+ var REPO_ROOT = path6.resolve(__dirname, "..", "..", "..");
114207
114651
  var HEALTH_TIMEOUT_MS = 800;
114208
114652
  var pendingStart = null;
114209
114653
  function dashboardLogPath() {
114210
- return path5.join(getDataDir(), "server.log");
114654
+ return path6.join(getDataDir(), "server.log");
114211
114655
  }
114212
114656
  function launchdPlistPath() {
114213
- return path5.join(getDataDir(), "dashboard.launchd.plist");
114657
+ return path6.join(getDataDir(), "dashboard.launchd.plist");
114214
114658
  }
114215
114659
  function launchdLabel() {
114216
114660
  let hash = 5381;
@@ -114306,18 +114750,18 @@ function spawnViaLaunchd(bin, args, cwd, log) {
114306
114750
  const label = launchdLabel();
114307
114751
  const plist = launchdPlistPath();
114308
114752
  try {
114309
- fs5.writeFileSync(plist, renderLaunchdPlist(bin, args, cwd, log), "utf8");
114753
+ fs6.writeFileSync(plist, renderLaunchdPlist(bin, args, cwd, log), "utf8");
114310
114754
  spawnSync("launchctl", ["bootout", `${domain}/${label}`], { stdio: "ignore" });
114311
114755
  const res = spawnSync("launchctl", ["bootstrap", domain, plist], {
114312
114756
  encoding: "utf8",
114313
114757
  timeout: 5e3
114314
114758
  });
114315
114759
  if (res.status === 0) return true;
114316
- fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl bootstrap failed: ${res.stderr || res.stdout || res.status}
114760
+ fs6.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl bootstrap failed: ${res.stderr || res.stdout || res.status}
114317
114761
  `);
114318
114762
  } catch (e) {
114319
114763
  try {
114320
- fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl launch failed: ${e instanceof Error ? e.message : String(e)}
114764
+ fs6.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl launch failed: ${e instanceof Error ? e.message : String(e)}
114321
114765
  `);
114322
114766
  } catch {
114323
114767
  }
@@ -114343,16 +114787,16 @@ async function ensureServer(timeoutMs = 12e3) {
114343
114787
  }
114344
114788
  }
114345
114789
  async function startServerBackground(timeoutMs) {
114346
- const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path5.join(__dirname, "web.mjs")], __dirname] : [
114790
+ const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path6.join(__dirname, "web.mjs")], __dirname] : [
114347
114791
  process.execPath,
114348
- ["--no-warnings", "--experimental-sqlite", "--import", "tsx", path5.join(REPO_ROOT, "apps/web/src/index.ts")],
114792
+ ["--no-warnings", "--experimental-sqlite", "--import", "tsx", path6.join(REPO_ROOT, "apps/web/src/index.ts")],
114349
114793
  REPO_ROOT
114350
114794
  ];
114351
114795
  const log = dashboardLogPath();
114352
114796
  let logFd = null;
114353
114797
  try {
114354
- logFd = fs5.openSync(log, "a");
114355
- fs5.writeSync(logFd, `
114798
+ logFd = fs6.openSync(log, "a");
114799
+ fs6.writeSync(logFd, `
114356
114800
  [${(/* @__PURE__ */ new Date()).toISOString()}] CareerMate dashboard background launch
114357
114801
  `);
114358
114802
  } catch {
@@ -114365,7 +114809,7 @@ async function startServerBackground(timeoutMs) {
114365
114809
  } finally {
114366
114810
  if (logFd !== null) {
114367
114811
  try {
114368
- fs5.closeSync(logFd);
114812
+ fs6.closeSync(logFd);
114369
114813
  } catch {
114370
114814
  }
114371
114815
  }
@@ -114378,7 +114822,7 @@ async function startServerBackground(timeoutMs) {
114378
114822
  }
114379
114823
  if (launchedViaLaunchd) {
114380
114824
  try {
114381
- fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchd did not produce a healthy server; falling back to detached spawn
114825
+ fs6.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchd did not produce a healthy server; falling back to detached spawn
114382
114826
  `);
114383
114827
  } catch {
114384
114828
  }
@@ -114420,8 +114864,8 @@ async function resolveDashboardUrl(hash = "") {
114420
114864
  }
114421
114865
 
114422
114866
  // packages/mcp-tools/src/update.ts
114423
- import fs6 from "node:fs";
114424
- import path6 from "node:path";
114867
+ import fs7 from "node:fs";
114868
+ import path7 from "node:path";
114425
114869
  import { fileURLToPath as fileURLToPath3 } from "node:url";
114426
114870
  import { spawn as spawn2 } from "node:child_process";
114427
114871
  var REGISTRY_URL = "https://registry.npmjs.org/careermate/latest";
@@ -114429,7 +114873,7 @@ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
114429
114873
  var NULL_CACHE_TTL_MS = 30 * 60 * 1e3;
114430
114874
  var FETCH_TIMEOUT_MS = 1500;
114431
114875
  var INSTALL_TIMEOUT_MS = 12e4;
114432
- var cacheFile = () => path6.join(getDataDir(), ".update-check.json");
114876
+ var cacheFile = () => path7.join(getDataDir(), ".update-check.json");
114433
114877
  function checkDisabled() {
114434
114878
  const v = process.env.CAREERMATE_NO_UPDATE_CHECK?.trim().toLowerCase();
114435
114879
  return v === "1" || v === "true";
@@ -114477,14 +114921,14 @@ function comparePrerelease(a, b) {
114477
114921
  }
114478
114922
  function readCache() {
114479
114923
  try {
114480
- return JSON.parse(fs6.readFileSync(cacheFile(), "utf8"));
114924
+ return JSON.parse(fs7.readFileSync(cacheFile(), "utf8"));
114481
114925
  } catch {
114482
114926
  return null;
114483
114927
  }
114484
114928
  }
114485
114929
  function writeCache(c) {
114486
114930
  try {
114487
- fs6.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
114931
+ fs7.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
114488
114932
  } catch {
114489
114933
  }
114490
114934
  }
@@ -114515,13 +114959,13 @@ function refreshInBackground() {
114515
114959
  refreshing = false;
114516
114960
  });
114517
114961
  }
114518
- var __dirname2 = path6.dirname(fileURLToPath3(import.meta.url));
114962
+ var __dirname2 = path7.dirname(fileURLToPath3(import.meta.url));
114519
114963
  function installPrefix() {
114520
114964
  if (!BUNDLED) return null;
114521
- const pkgRoot = path6.resolve(__dirname2, "..");
114522
- const parent = path6.dirname(pkgRoot);
114523
- if (path6.basename(parent) !== "node_modules") return null;
114524
- return path6.dirname(parent);
114965
+ const pkgRoot = path7.resolve(__dirname2, "..");
114966
+ const parent = path7.dirname(pkgRoot);
114967
+ if (path7.basename(parent) !== "node_modules") return null;
114968
+ return path7.dirname(parent);
114525
114969
  }
114526
114970
  function manualCommand() {
114527
114971
  const prefix = installPrefix();
@@ -114654,13 +115098,13 @@ var DASHBOARD_URL = "http://127.0.0.1:4319";
114654
115098
  function listInboxFiles(dir) {
114655
115099
  let names;
114656
115100
  try {
114657
- names = fs7.readdirSync(dir);
115101
+ names = fs8.readdirSync(dir);
114658
115102
  } catch {
114659
115103
  return [];
114660
115104
  }
114661
115105
  let realDir;
114662
115106
  try {
114663
- realDir = fs7.realpathSync.native(dir);
115107
+ realDir = fs8.realpathSync.native(dir);
114664
115108
  } catch {
114665
115109
  realDir = dir;
114666
115110
  }
@@ -114668,12 +115112,12 @@ function listInboxFiles(dir) {
114668
115112
  const out = [];
114669
115113
  for (const name of names) {
114670
115114
  if (SKIP.test(name)) continue;
114671
- const p = path7.join(dir, name);
115115
+ const p = path8.join(dir, name);
114672
115116
  try {
114673
- if (!fs7.statSync(p).isFile()) continue;
114674
- const real = fs7.realpathSync.native(p);
114675
- const rel = path7.relative(realDir, real);
114676
- if (rel.startsWith("..") || path7.isAbsolute(rel)) continue;
115117
+ if (!fs8.statSync(p).isFile()) continue;
115118
+ const real = fs8.realpathSync.native(p);
115119
+ const rel = path8.relative(realDir, real);
115120
+ if (rel.startsWith("..") || path8.isAbsolute(rel)) continue;
114677
115121
  out.push({ name, path: p });
114678
115122
  } catch {
114679
115123
  }
@@ -114701,10 +115145,10 @@ function pickRoute(ctx) {
114701
115145
  return "write_cover_letter";
114702
115146
  }
114703
115147
  function writeExport(result2) {
114704
- const filePath = path7.join(getExportsDir(), result2.filename);
115148
+ const filePath = path8.join(getExportsDir(), result2.filename);
114705
115149
  try {
114706
- if (result2.bytes) fs7.writeFileSync(filePath, result2.bytes);
114707
- else fs7.writeFileSync(filePath, result2.content, "utf8");
115150
+ if (result2.bytes) fs8.writeFileSync(filePath, result2.bytes);
115151
+ else fs8.writeFileSync(filePath, result2.content, "utf8");
114708
115152
  return { path: filePath };
114709
115153
  } catch (e) {
114710
115154
  return { error: `\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}` };
@@ -114724,11 +115168,11 @@ var TOOLS = [
114724
115168
  handler: async (args) => {
114725
115169
  const r = await extractDocument(args.path);
114726
115170
  if (r.errored) {
114727
- return fail(`'${path7.basename(r.path)}'\uC744(\uB97C) \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.
115171
+ return fail(`'${path8.basename(r.path)}'\uC744(\uB97C) \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.
114728
115172
  ${r.warnings.join("\n")}`);
114729
115173
  }
114730
115174
  if (r.unsupported) {
114731
- return ok(`'${path7.basename(r.path)}'\uC740(\uB294) \uD14D\uC2A4\uD2B8\uB85C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
115175
+ return ok(`'${path8.basename(r.path)}'\uC740(\uB294) \uD14D\uC2A4\uD2B8\uB85C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
114732
115176
  ${r.warnings.join("\n")}`, {
114733
115177
  path: r.path,
114734
115178
  format: r.format,
@@ -114743,7 +115187,7 @@ ${r.warnings.join("\n")}`, {
114743
115187
  truncated = true;
114744
115188
  }
114745
115189
  const head = [
114746
- `'${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" : ""}).`,
115190
+ `'${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" : ""}).`,
114747
115191
  ...r.warnings,
114748
115192
  "\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."
114749
115193
  ].join("\n");
@@ -114873,6 +115317,8 @@ ${lines}`;
114873
115317
  },
114874
115318
  {
114875
115319
  name: "save_profile",
115320
+ idempotent: true,
115321
+ // 부분 업서트 — 같은 인자 재호출은 같은 결과
114876
115322
  title: "\uD504\uB85C\uD544 \uC800\uC7A5",
114877
115323
  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.",
114878
115324
  inputSchema: ProfileInputSchema.shape,
@@ -114911,6 +115357,7 @@ ${lines}`;
114911
115357
  },
114912
115358
  {
114913
115359
  name: "delete_resume",
115360
+ destructive: true,
114914
115361
  title: "\uC774\uB825\uC11C/\uBB38\uC11C \uC0AD\uC81C",
114915
115362
  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.",
114916
115363
  inputSchema: {
@@ -114929,6 +115376,8 @@ ${lines}`;
114929
115376
  /* ----------------------------------------------------------- experiences */
114930
115377
  {
114931
115378
  name: "add_experience",
115379
+ idempotent: true,
115380
+ // 회사·직무·입사일 키 업서트(adr_0003) — 재호출 안전
114932
115381
  title: "\uACBD\uB825 \uCD94\uAC00",
114933
115382
  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.",
114934
115383
  inputSchema: ExperienceBatchInputSchema.shape,
@@ -114952,6 +115401,8 @@ ${lines}`;
114952
115401
  /* -------------------------------------------------------------- projects */
114953
115402
  {
114954
115403
  name: "add_project",
115404
+ idempotent: true,
115405
+ // 이름 키 업서트 — 재호출 안전
114955
115406
  title: "\uD504\uB85C\uC81D\uD2B8 \uCD94\uAC00",
114956
115407
  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.",
114957
115408
  inputSchema: ProjectBatchInputSchema.shape,
@@ -114975,6 +115426,8 @@ ${lines}`;
114975
115426
  /* ---------------------------------------------------------------- skills */
114976
115427
  {
114977
115428
  name: "add_skill",
115429
+ idempotent: true,
115430
+ // 이름 키 업서트 — 재호출 안전
114978
115431
  title: "\uAE30\uC220\uC2A4\uD0DD \uCD94\uAC00",
114979
115432
  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.",
114980
115433
  inputSchema: SkillBatchInputSchema.shape,
@@ -114997,6 +115450,7 @@ ${lines}`;
114997
115450
  },
114998
115451
  {
114999
115452
  name: "delete_experience",
115453
+ destructive: true,
115000
115454
  title: "\uACBD\uB825 \uD56D\uBAA9 \uC0AD\uC81C",
115001
115455
  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.",
115002
115456
  inputSchema: {
@@ -115014,6 +115468,7 @@ ${lines}`;
115014
115468
  },
115015
115469
  {
115016
115470
  name: "delete_project",
115471
+ destructive: true,
115017
115472
  title: "\uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9 \uC0AD\uC81C",
115018
115473
  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.",
115019
115474
  inputSchema: {
@@ -115031,6 +115486,7 @@ ${lines}`;
115031
115486
  },
115032
115487
  {
115033
115488
  name: "delete_skill",
115489
+ destructive: true,
115034
115490
  title: "\uAE30\uC220\uC2A4\uD0DD \uD56D\uBAA9 \uC0AD\uC81C",
115035
115491
  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.",
115036
115492
  inputSchema: {
@@ -115079,6 +115535,7 @@ ${lines}`;
115079
115535
  },
115080
115536
  {
115081
115537
  name: "delete_cover_letter",
115538
+ destructive: true,
115082
115539
  title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC0AD\uC81C",
115083
115540
  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.",
115084
115541
  inputSchema: {
@@ -115105,8 +115562,10 @@ ${lines}`;
115105
115562
  /* ------------------------------------------------------------------ jobs */
115106
115563
  {
115107
115564
  name: "save_job_posting",
115565
+ idempotent: true,
115566
+ // 같은 url 업서트 — 재호출 안전
115108
115567
  title: "\uCC44\uC6A9\uACF5\uACE0 \uC800\uC7A5",
115109
- 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.",
115568
+ 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.",
115110
115569
  inputSchema: JobInputSchema.shape,
115111
115570
  handler: (args) => {
115112
115571
  const { job } = saveJobPosting(args);
@@ -115152,6 +115611,7 @@ ${lines}`;
115152
115611
  },
115153
115612
  {
115154
115613
  name: "delete_job_posting",
115614
+ destructive: true,
115155
115615
  title: "\uCC44\uC6A9\uACF5\uACE0 \uC0AD\uC81C",
115156
115616
  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.",
115157
115617
  inputSchema: {
@@ -115237,6 +115697,8 @@ ${lines}`;
115237
115697
  },
115238
115698
  {
115239
115699
  name: "save_fit_analysis",
115700
+ idempotent: true,
115701
+ // 공고당 1건 업서트
115240
115702
  title: "\uC801\uD569\uB3C4 \uBD84\uC11D \uACB0\uACFC \uC800\uC7A5",
115241
115703
  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.",
115242
115704
  inputSchema: FitAnalysisInputSchema.shape,
@@ -115281,27 +115743,41 @@ ${lines}`;
115281
115743
  {
115282
115744
  name: "update_application_status",
115283
115745
  title: "\uC9C0\uC6D0 \uC0C1\uD0DC \uBCC0\uACBD",
115284
- 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.`,
115746
+ 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).`,
115285
115747
  inputSchema: {
115286
115748
  job_id: external_exports.string(),
115287
115749
  status: external_exports.enum(APPLICATION_STATUSES),
115288
115750
  note: external_exports.string().optional(),
115289
- submission: ApplicationSubmissionSchema.optional()
115751
+ submission: ApplicationSubmissionSchema.optional(),
115752
+ rejected_stage: external_exports.enum(REJECTED_STAGES).optional().describe(
115753
+ "\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"
115754
+ )
115290
115755
  },
115291
115756
  handler: (args) => {
115292
115757
  try {
115293
- const res = updateApplicationStatus(args.job_id, args.status, args.note, args.submission);
115294
- const user_message = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.` + (res.hint ? `
115758
+ const res = updateApplicationStatus(args.job_id, args.status, args.note, args.submission, args.rejected_stage);
115759
+ const needsRetro = args.status === "rejected" && !res.application.rejected_stage;
115760
+ 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?";
115761
+ const base = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.`;
115762
+ const user_message = base + (res.hint ? `
115763
+
115764
+ ${res.hint}` : needsRetro ? `
115295
115765
 
115296
- ${res.hint}` : "");
115766
+ ${retro_prompt}` : "");
115767
+ const text = needsRetro ? `${user_message}
115768
+
115769
+ (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;
115297
115770
  return ok(
115298
- user_message,
115771
+ text,
115299
115772
  {
115300
115773
  ...res,
115301
115774
  user_message,
115302
115775
  suggested_next_action: res.hint ? {
115303
115776
  prompt: res.hint,
115304
115777
  next_workflow: "prepare_interview"
115778
+ } : needsRetro ? {
115779
+ prompt: retro_prompt,
115780
+ next_tool: "update_application_status"
115305
115781
  } : null
115306
115782
  }
115307
115783
  );
@@ -115313,6 +115789,8 @@ ${res.hint}` : "");
115313
115789
  /* ---------------------------------------------------------- interview prep */
115314
115790
  {
115315
115791
  name: "save_interview_prep",
115792
+ idempotent: true,
115793
+ // 공고당 1건 업서트(debrief는 병합)
115316
115794
  title: "\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC \uC800\uC7A5",
115317
115795
  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).",
115318
115796
  inputSchema: InterviewPrepInputSchema.shape,
@@ -115446,20 +115924,22 @@ ${res.hint}` : "");
115446
115924
  {
115447
115925
  name: "get_followups",
115448
115926
  title: "\uD6C4\uC18D \uC870\uCE58 \uC54C\uB9BC(\uB193\uCE5C \uC9C0\uC6D0\xB7\uB9C8\uAC10\xB7\uBA74\uC811\uC900\uBE44)",
115449
- 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).',
115927
+ 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).',
115450
115928
  inputSchema: {},
115451
115929
  readOnly: true,
115452
115930
  handler: () => {
115453
115931
  const d = getActionDigest();
115454
- const n = d.followups.length + d.deadlines.length + d.interview_todo.length;
115455
- 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}`;
115932
+ const n = d.followups.length + d.deadlines.length + d.interview_todo.length + d.upcoming_schedule.length;
115933
+ 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}`];
115934
+ if (d.schedule_conflicts.length) parts.push(`\uC77C\uC815\uCDA9\uB3CC ${d.schedule_conflicts.length}`);
115935
+ 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 ")}`;
115456
115936
  return ok(msg, d);
115457
115937
  }
115458
115938
  },
115459
115939
  {
115460
115940
  name: "get_pipeline_stats",
115461
115941
  title: "\uC9C0\uC6D0 \uD30C\uC774\uD504\uB77C\uC778 \uBD84\uC11D(\uAE54\uB54C\uAE30\xB7\uD569\uACA9\uB960)",
115462
- 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.',
115942
+ 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.',
115463
115943
  inputSchema: {},
115464
115944
  readOnly: true,
115465
115945
  handler: () => {
@@ -115467,14 +115947,19 @@ ${res.hint}` : "");
115467
115947
  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);
115468
115948
  const r = s.rates;
115469
115949
  const fmt = (v) => v == null ? "\u2014" : `${v}%`;
115950
+ const rejParts = s.rejected_by_stage.map((x2) => `${REJECTED_STAGE_LABELS[x2.stage]} ${x2.count}`);
115951
+ 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`);
115952
+ const rejLine = rejParts.length ? ` \xB7 \uD0C8\uB77D \uBD84\uD3EC: ${rejParts.join("\xB7")}` : "";
115470
115953
  return ok(
115471
- `\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)`,
115954
+ `\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)`,
115472
115955
  s
115473
115956
  );
115474
115957
  }
115475
115958
  },
115476
115959
  {
115477
115960
  name: "search_jobs",
115961
+ // 외부 공개 잡보드(원티드·점핏)로 검색어가 나간다 — openWorld를 정직하게 표시.
115962
+ openWorld: true,
115478
115963
  title: "\uACF5\uACE0 \uAC80\uC0C9\xB7\uAC00\uC838\uC624\uAE30(\uD0A4\uC6CC\uB4DC)",
115479
115964
  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.`,
115480
115965
  inputSchema: {
@@ -115496,16 +115981,72 @@ ${res.hint}` : "");
115496
115981
  {
115497
115982
  name: "prescreen_job",
115498
115983
  title: "\uACF5\uACE0 \uC0AC\uC804 \uD504\uB9AC\uC2A4\uD06C\uB9B0(\uD0A4\uC6CC\uB4DC \uACB9\uCE68)",
115499
- 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.',
115984
+ 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.',
115500
115985
  inputSchema: { job_id: external_exports.string() },
115501
115986
  readOnly: true,
115502
115987
  handler: (args) => {
115503
115988
  const r = prescreenJob(args.job_id);
115504
115989
  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.");
115505
- if (r.coverage == null)
115990
+ if (r.combined_coverage == null)
115506
115991
  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);
115507
- const pct = Math.round(r.coverage * 100);
115508
- 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);
115992
+ const pct = Math.round(r.combined_coverage * 100);
115993
+ const combinedMissing = r.job_keywords.filter((k) => !r.combined_matched.includes(k));
115994
+ 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.)";
115995
+ return ok(
115996
+ `\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.)`,
115997
+ r
115998
+ );
115999
+ }
116000
+ },
116001
+ {
116002
+ name: "save_offer",
116003
+ idempotent: true,
116004
+ // 공고당 1건 업서트
116005
+ title: "\uC624\uD37C(\uC81C\uC548) \uC800\uC7A5",
116006
+ 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.',
116007
+ inputSchema: OfferInputSchema.shape,
116008
+ handler: (args) => {
116009
+ try {
116010
+ const offer = saveOffer(args);
116011
+ const total = offer.total_comp_annual_est;
116012
+ const statusLabel = offer.offer_status === "accepted" ? " \xB7 \uC218\uB77D" : offer.offer_status === "declined" ? " \xB7 \uAC70\uC808" : "";
116013
+ 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 + ".";
116014
+ const app = applicationRepo.getByJob(offer.job_id);
116015
+ const suggested_next_action = offer.offer_status === "accepted" && app?.status !== "final_passed" ? {
116016
+ prompt: "\uC9C0\uC6D0 \uC0C1\uD0DC\uB3C4 '\uCD5C\uC885 \uD569\uACA9'\uC73C\uB85C \uBC14\uAFC0\uAE4C\uC694?",
116017
+ next_tool: "update_application_status"
116018
+ } : null;
116019
+ const msg = suggested_next_action ? `${user_message}
116020
+
116021
+ \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;
116022
+ return ok(msg, { ...offer, user_message, suggested_next_action });
116023
+ } catch (e) {
116024
+ return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
116025
+ }
116026
+ }
116027
+ },
116028
+ {
116029
+ name: "compare_offers",
116030
+ title: "\uC624\uD37C \uBE44\uAD50",
116031
+ 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.',
116032
+ inputSchema: {},
116033
+ readOnly: true,
116034
+ handler: () => {
116035
+ const offers = compareOffers();
116036
+ 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);
116037
+ return ok(`\uC624\uD37C ${offers.length}\uAC74(\uC810\uC218\uC21C).`, offers);
116038
+ }
116039
+ },
116040
+ {
116041
+ name: "get_offer",
116042
+ title: "\uC624\uD37C \uC870\uD68C(\uACF5\uACE0\uBCC4)",
116043
+ 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.",
116044
+ inputSchema: { job_id: external_exports.string() },
116045
+ readOnly: true,
116046
+ handler: (args) => {
116047
+ const offer = getOffer(args.job_id);
116048
+ if (!offer) return ok("\uC774 \uACF5\uACE0\uC5D0 \uC800\uC7A5\uB41C \uC624\uD37C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", null);
116049
+ return ok(`'${offer.company}' \uC624\uD37C.`, offer);
115509
116050
  }
115510
116051
  },
115511
116052
  /* ------------------------------------------------------- workflow guide */
@@ -115579,7 +116120,7 @@ ${res.hint}` : "");
115579
116120
  {
115580
116121
  name: "validate_cover_letter",
115581
116122
  title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC790\uB3D9 \uC810\uAC80 (\uC800\uC7A5 \uC804 \uBBF8\uB9AC\uBCF4\uAE30)",
115582
- 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.",
116123
+ 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.",
115583
116124
  inputSchema: {
115584
116125
  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)"),
115585
116126
  job_id: external_exports.string().optional().describe("\uC5F0\uACB0\uD560 \uACF5\uACE0(\uC788\uC73C\uBA74 \uACF5\uACE0 \uC218\uCE58\uAE4C\uC9C0 \uBE44\uAD50)"),
@@ -115623,6 +116164,8 @@ ${report.disclaimer}`, {
115623
116164
  },
115624
116165
  {
115625
116166
  name: "set_verify_mode",
116167
+ idempotent: true,
116168
+ // 같은 값 재설정은 무해
115626
116169
  title: "\uC810\uAC80 \uBAA8\uB4DC \uC124\uC815 (\uAE30\uBCF8/\uC5C4\uACA9)",
115627
116170
  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.',
115628
116171
  inputSchema: {
@@ -115661,6 +116204,8 @@ ${report.disclaimer}`, {
115661
116204
  /* ----------------------------------------------------------------- update */
115662
116205
  {
115663
116206
  name: "check_for_update",
116207
+ // npm 레지스트리로 버전 조회가 나간다(사용자 데이터 미전송).
116208
+ openWorld: true,
115664
116209
  title: "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778",
115665
116210
  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.',
115666
116211
  inputSchema: {},
@@ -115684,6 +116229,9 @@ ${report.disclaimer}`, {
115684
116229
  },
115685
116230
  {
115686
116231
  name: "update_careermate",
116232
+ // npm에서 패키지를 내려받아 설치 폴더의 코드를 교체한다.
116233
+ openWorld: true,
116234
+ destructive: true,
115687
116235
  title: "CareerMate \uC5C5\uB370\uC774\uD2B8",
115688
116236
  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.',
115689
116237
  inputSchema: {},
@@ -115713,10 +116261,16 @@ async function main() {
115713
116261
  title: tool.title,
115714
116262
  description: tool.description,
115715
116263
  inputSchema: tool.inputSchema,
116264
+ // 어노테이션은 도구 정의(ToolDef)의 플래그를 그대로 반영한다 — 정직해야 한다.
116265
+ // openWorldHint: search_jobs(잡보드)·check_for_update/update_careermate(npm)처럼
116266
+ // 실제로 외부에 나가는 도구만 true. destructiveHint: 스펙 기본값이 true라
116267
+ // 업서트가 파괴적으로 오분류되지 않도록 비파괴 쓰기에는 명시적 false를 방출한다.
115716
116268
  annotations: {
115717
116269
  title: tool.title,
115718
116270
  readOnlyHint: tool.readOnly ?? false,
115719
- openWorldHint: false
116271
+ openWorldHint: tool.openWorld ?? false,
116272
+ destructiveHint: tool.destructive ?? false,
116273
+ idempotentHint: tool.idempotent ?? false
115720
116274
  }
115721
116275
  },
115722
116276
  async (args) => {