careermate 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp.mjs CHANGED
@@ -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, fs7, exportName) {
6894
+ function addFormats(ajv, list, fs8, exportName) {
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").${exportName}`;
6898
6898
  for (const f of list)
6899
- ajv.addFormat(f, fs7[f]);
6899
+ ajv.addFormat(f, fs8[f]);
6900
6900
  }
6901
6901
  module2.exports = exports2 = formatsPlugin;
6902
6902
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -13333,9 +13333,9 @@ var require_timers = __commonJS({
13333
13333
  clearTimeout(this.handle);
13334
13334
  };
13335
13335
  var afterValue = function(value) {
13336
- return delay(+this).thenReturn(value);
13336
+ return delay2(+this).thenReturn(value);
13337
13337
  };
13338
- var delay = Promise2.delay = function(ms, value) {
13338
+ var delay2 = Promise2.delay = function(ms, value) {
13339
13339
  var ret2;
13340
13340
  var handle;
13341
13341
  if (value !== void 0) {
@@ -13357,7 +13357,7 @@ var require_timers = __commonJS({
13357
13357
  return ret2;
13358
13358
  };
13359
13359
  Promise2.prototype.delay = function(ms) {
13360
- return delay(ms, this);
13360
+ return delay2(ms, this);
13361
13361
  };
13362
13362
  var afterTimeout = function(promise, message, parent) {
13363
13363
  var err2;
@@ -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 fs7 = __require("fs");
35156
+ var fs8 = __require("fs");
35157
35157
  var url = __require("url");
35158
35158
  var os3 = __require("os");
35159
35159
  var dirname = __require("path").dirname;
@@ -35194,7 +35194,7 @@ var require_files = __commonJS({
35194
35194
  read
35195
35195
  };
35196
35196
  }
35197
- var readFile = promises.promisify(fs7.readFile.bind(fs7));
35197
+ var readFile = promises.promisify(fs8.readFile.bind(fs8));
35198
35198
  function uriToPath(uriString, platform) {
35199
35199
  if (!platform) {
35200
35200
  platform = os3.platform();
@@ -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 fs7 = __require("fs");
37939
+ var fs8 = __require("fs");
37940
37940
  var promises = require_promises();
37941
37941
  var zipfile = require_zipfile();
37942
37942
  exports2.openZip = openZip;
37943
- var readFile = promises.promisify(fs7.readFile);
37943
+ var readFile = promises.promisify(fs8.readFile);
37944
37944
  function openZip(options) {
37945
37945
  if (options.path) {
37946
37946
  return readFile(options.path).then(zipfile.openArrayBuffer);
@@ -38665,9 +38665,9 @@ var require_cjs = __commonJS({
38665
38665
  }
38666
38666
  return o;
38667
38667
  }
38668
- var fs7;
38668
+ var fs8;
38669
38669
  function get_fs() {
38670
- return fs7 || (fs7 = require$$0__default["default"]);
38670
+ return fs8 || (fs8 = 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(fs7.readFileSync(filename2), options);
38955
+ return parse4(fs8.readFileSync(filename2), options);
38956
38956
  }
38957
38957
  function read(blob, options) {
38958
38958
  switch (options && options.type || "base64") {
@@ -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
- fs7.writeFileSync(filename2, o);
39270
+ fs8.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
- fs7.writeFileSync(options.filename, o);
39282
+ fs8.writeFileSync(options.filename, o);
39283
39283
  return o;
39284
39284
  case "binary":
39285
39285
  return typeof o == "string" ? o : a2s(o);
@@ -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 fs7 = process.getBuiltinModule("fs/promises");
46976
- const data = await fs7.readFile(url);
46975
+ const fs8 = process.getBuiltinModule("fs/promises");
46976
+ const data = await fs8.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 fs7 = process.getBuiltinModule("fs");
47605
+ const fs8 = process.getBuiltinModule("fs");
47606
47606
  const {
47607
47607
  Readable
47608
47608
  } = process.getBuiltinModule("stream");
47609
- const readStream = fs7.createReadStream(url, opts);
47609
+ const readStream = fs8.createReadStream(url, opts);
47610
47610
  return Readable.toWeb(readStream);
47611
47611
  }
47612
47612
  function getNetworkStream(url) {
@@ -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 fs7 = process.getBuiltinModule("fs/promises");
64607
- fs7.lstat(url).then((stat) => {
64606
+ const fs8 = process.getBuiltinModule("fs/promises");
64607
+ fs8.lstat(url).then((stat) => {
64608
64608
  const readableStream = getReadableStream(url);
64609
64609
  this._reader = readableStream.getReader();
64610
64610
  const {
@@ -66250,9 +66250,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
66250
66250
  return;
66251
66251
  }
66252
66252
  if (reason instanceof RenderingCancelledException) {
66253
- let delay = RENDERING_CANCELLED_TIMEOUT;
66253
+ let delay2 = RENDERING_CANCELLED_TIMEOUT;
66254
66254
  if (reason.extraDelay > 0 && reason.extraDelay < 1e3) {
66255
- delay += reason.extraDelay;
66255
+ delay2 += reason.extraDelay;
66256
66256
  }
66257
66257
  intentState.streamReaderCancelTimeout = setTimeout(() => {
66258
66258
  intentState.streamReaderCancelTimeout = null;
@@ -66261,7 +66261,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
66261
66261
  reason,
66262
66262
  force: true
66263
66263
  });
66264
- }, delay);
66264
+ }, delay2);
66265
66265
  return;
66266
66266
  }
66267
66267
  }
@@ -92956,7 +92956,7 @@ var ApplicationContextSchema = external_exports.object({
92956
92956
  });
92957
92957
 
92958
92958
  // packages/shared/src/build-info.ts
92959
- var APP_VERSION = true ? "0.0.4" : "0.0.0-dev";
92959
+ var APP_VERSION = true ? "0.0.5" : "0.0.0-dev";
92960
92960
  var BUNDLED = true;
92961
92961
 
92962
92962
  // packages/shared/src/index.ts
@@ -93468,6 +93468,7 @@ var coverLetterRepo = {
93468
93468
  remove(id2) {
93469
93469
  const db = getDb();
93470
93470
  return tx(() => {
93471
+ db.prepare(`UPDATE applications SET cover_letter_id=NULL, updated_at=? WHERE cover_letter_id=?`).run(now(), id2);
93471
93472
  db.prepare(`DELETE FROM cover_letter_versions WHERE cover_letter_id=?`).run(id2);
93472
93473
  return db.prepare(`DELETE FROM cover_letters WHERE id=?`).run(id2).changes > 0;
93473
93474
  });
@@ -93556,6 +93557,7 @@ var jobRepo = {
93556
93557
  remove(id2) {
93557
93558
  const db = getDb();
93558
93559
  return tx(() => {
93560
+ db.prepare(`UPDATE cover_letters SET job_id=NULL, updated_at=? WHERE job_id=?`).run(now(), id2);
93559
93561
  db.prepare(`DELETE FROM fit_analyses WHERE job_id=?`).run(id2);
93560
93562
  db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id2);
93561
93563
  db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id2);
@@ -93754,7 +93756,7 @@ var activityRepo = {
93754
93756
  };
93755
93757
 
93756
93758
  // packages/mcp-tools/src/tools.ts
93757
- import fs6 from "node:fs";
93759
+ import fs7 from "node:fs";
93758
93760
  import path7 from "node:path";
93759
93761
 
93760
93762
  // packages/core/src/onboarding.ts
@@ -94234,7 +94236,7 @@ function saveJobPosting(input, id2) {
94234
94236
  }
94235
94237
  function saveFitAnalysis(input) {
94236
94238
  const job = jobRepo.get(input.job_id);
94237
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}. \uBA3C\uC800 save_job_posting\uC73C\uB85C \uACF5\uACE0\uB97C \uC800\uC7A5\uD558\uC138\uC694.`);
94239
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uACF5\uACE0\uB97C \uC800\uC7A5\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.");
94238
94240
  applicationRepo.ensure(job.id, "draft");
94239
94241
  const fit = fitRepo.save(input);
94240
94242
  const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
@@ -94276,7 +94278,7 @@ function saveCoverLetterVersion(input) {
94276
94278
  }
94277
94279
  function updateApplicationStatus(jobId, status, note) {
94278
94280
  const job = jobRepo.get(jobId);
94279
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${jobId}`);
94281
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
94280
94282
  const application = applicationRepo.setStatus(jobId, status, note);
94281
94283
  activityRepo.log(
94282
94284
  "application_status_changed",
@@ -94286,17 +94288,17 @@ function updateApplicationStatus(jobId, status, note) {
94286
94288
  );
94287
94289
  const interview_unlocked = INTERVIEW_UNLOCK_STATUSES.includes(status);
94288
94290
  const hasPrep = !!interviewRepo.getByJob(jobId);
94289
- const hint = interview_unlocked && !hasPrep ? "\uC11C\uB958 \uB2E8\uACC4\uB97C \uD1B5\uACFC\uD588\uC5B4\uC694. \uC774 \uACF5\uACE0 \uAE30\uC900\uC73C\uB85C \uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38\uACFC 1\uBD84 \uC790\uAE30\uC18C\uAC1C\uB97C \uC900\uBE44\uD560\uAE4C\uC694? (save_interview_prep)" : null;
94291
+ const hint = interview_unlocked && !hasPrep ? "\uC11C\uB958 \uB2E8\uACC4\uB97C \uD1B5\uACFC\uD588\uC5B4\uC694. \uC774 \uACF5\uACE0 \uAE30\uC900\uC73C\uB85C \uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38\uACFC 1\uBD84 \uC790\uAE30\uC18C\uAC1C\uB97C \uC900\uBE44\uD560\uAE4C\uC694?" : null;
94290
94292
  return { application, job, interview_unlocked, hint };
94291
94293
  }
94292
94294
  function saveInterviewPrep(input) {
94293
94295
  const job = jobRepo.get(input.job_id);
94294
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}`);
94296
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
94295
94297
  const app = applicationRepo.getByJob(job.id);
94296
94298
  if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
94297
94299
  const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
94298
94300
  throw conflict(
94299
- `\uBA74\uC811 \uC900\uBE44\uB294 '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1 \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC800\uC7A5\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uC0C1\uD0DC: '${current}'. \uBA3C\uC800 update_application_status\uB85C \uC0C1\uD0DC\uB97C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`
94301
+ `\uBA74\uC811 \uC900\uBE44\uB294 '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1 \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC800\uC7A5\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uC0C1\uD0DC: '${current}'. \uBA3C\uC800 \uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1\uC73C\uB85C \uBC14\uAFBC \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`
94300
94302
  );
94301
94303
  }
94302
94304
  const prep = interviewRepo.save(input);
@@ -94394,8 +94396,8 @@ var WORKFLOWS = [
94394
94396
  "\uC544\uC9C1 \uC800\uC7A5\uB418\uC9C0 \uC54A\uC740 \uACF5\uACE0\uB77C\uBA74 `save_job_posting`\uC73C\uB85C \uACF5\uACE0\uB97C \uC800\uC7A5\uD558\uACE0 job_id\uB97C \uD655\uBCF4\uD55C\uB2E4.",
94395
94397
  "\uB3C4\uCD9C\uD55C \uBD84\uC11D \uACB0\uACFC\uB97C `save_fit_analysis`\uB85C \uC800\uC7A5\uD55C\uB2E4(job_id, score, summary, strengths, gaps, matched_keywords, missing_keywords, recommendations).",
94396
94398
  "\uBD84\uC11D \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC124\uBA85\uD55C\uB2E4(\uC798 \uB9DE\uB294 \uC810, \uBCF4\uC644\uD560 \uC810, \uCD94\uCC9C \uC804\uB7B5). \uAE30\uC220 \uC6A9\uC5B4\uB294 \uBE7C\uACE0 \uC804\uB2EC\uD55C\uB2E4.",
94397
- '"\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?"\uB77C\uACE0 \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131\uC744 \uC81C\uC548\uD55C\uB2E4.',
94398
- '\uC0AC\uC6A9\uC790\uAC00 \uB3D9\uC758\uD558\uBA74 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC791\uC131\uD558\uAE30 \uC9C1\uC804\uC5D0 `get_writing_style_guide`\uB97C \uD638\uCD9C\uD574 "AI \uD2F0 \uC548 \uB098\uB294 \uAE00\uC4F0\uAE30 \uADDC\uCE59"\uC744 \uAC00\uC838\uC628\uB2E4.',
94399
+ '\uBD84\uC11D \uB4A4 \uACE7\uC7A5 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC4F0\uC9C0 \uB9D0\uACE0, "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?"\uB77C\uACE0 \uBB3B\uACE0 \uC120\uD0DD\uC9C0\uB97C \uC81C\uC2DC\uD55C\uB2E4: \u2460 "\uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918" \u2461 "\uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918".',
94400
+ '\uC0AC\uC6A9\uC790\uAC00 \u2460\uC744 \uC120\uD0DD\uD558\uBA74 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC791\uC131\uD558\uAE30 \uC9C1\uC804\uC5D0 `get_writing_style_guide`\uB97C \uD638\uCD9C\uD574 "AI \uD2F0 \uC548 \uB098\uB294 \uAE00\uC4F0\uAE30 \uADDC\uCE59"\uC744 \uAC00\uC838\uC628\uB2E4. \u2461\uB97C \uC120\uD0DD\uD558\uBA74 \uC790\uC18C\uC11C \uC791\uC131 \uC5C6\uC774 \uBA48\uCD94\uACE0 \uC800\uC7A5 \uACB0\uACFC\uC640 \uB300\uC2DC\uBCF4\uB4DC \uD655\uC778\uB9CC \uC548\uB0B4\uD55C\uB2E4.',
94399
94401
  "\uC801\uD569\uB3C4 \uBD84\uC11D\xB7\uAE00\uC4F0\uAE30 \uC120\uD638\uC640 \uAE00\uC4F0\uAE30 \uADDC\uCE59\uC744 \uD568\uAED8 \uBC18\uC601\uD574 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC791\uC131\uD558\uACE0(\uBC88\uC5ED\uD22C\xB7\uD074\uB9AC\uC170\xB7\uAE30\uACC4\uC801 \uBCD1\uB82C \uC81C\uAC70, \uC0AC\uC2E4\xB7\uC218\uCE58\uB294 \uADF8\uB300\uB85C), `save_cover_letter_version`\uC73C\uB85C \uC800\uC7A5\uD55C\uB2E4(job_id \uC5F0\uACB0, note\uC5D0 \uC791\uC131 \uC758\uB3C4 \uC694\uC57D).",
94400
94402
  "`open_application`\uC73C\uB85C \uD574\uB2F9 \uC9C0\uC6D0 \uAC74\uC744 \uC5F4\uC5B4 \uC0AC\uC6A9\uC790\uAC00 \uACB0\uACFC\uB97C \uD655\uC778\uD558\uAC8C \uD558\uACE0, \uC9C0\uC6D0 \uC0C1\uD0DC \uBCC0\uACBD(\uC608: \uC9C0\uC6D0 \uC608\uC815/\uC9C0\uC6D0 \uC644\uB8CC)\uC744 \uC81C\uC548\uD55C\uB2E4."
94401
94403
  ]
@@ -95675,14 +95677,94 @@ function toCallToolResult(r) {
95675
95677
  }
95676
95678
 
95677
95679
  // packages/mcp-tools/src/bridge.ts
95678
- import { spawn } from "node:child_process";
95680
+ import { spawn, spawnSync } from "node:child_process";
95681
+ import fs5 from "node:fs";
95679
95682
  import path5 from "node:path";
95680
95683
  import { fileURLToPath as fileURLToPath2 } from "node:url";
95681
95684
  var __dirname = path5.dirname(fileURLToPath2(import.meta.url));
95682
95685
  var REPO_ROOT = path5.resolve(__dirname, "..", "..", "..");
95683
- function liveServer() {
95686
+ var HEALTH_TIMEOUT_MS = 800;
95687
+ var pendingStart = null;
95688
+ function dashboardLogPath() {
95689
+ return path5.join(getDataDir(), "server.log");
95690
+ }
95691
+ function launchdPlistPath() {
95692
+ return path5.join(getDataDir(), "dashboard.launchd.plist");
95693
+ }
95694
+ function launchdLabel() {
95695
+ let hash = 5381;
95696
+ for (const ch of getDataDir()) hash = (hash * 33 ^ ch.charCodeAt(0)) >>> 0;
95697
+ return `life.careermate.dashboard.${hash.toString(16)}`;
95698
+ }
95699
+ function xmlEscape(value) {
95700
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
95701
+ }
95702
+ function plistString(key, value) {
95703
+ return ` <key>${xmlEscape(key)}</key>
95704
+ <string>${xmlEscape(value)}</string>`;
95705
+ }
95706
+ function renderLaunchdPlist(bin, args, cwd, log) {
95707
+ const env2 = {
95708
+ CAREERMATE_DATA_DIR: getDataDir(),
95709
+ CAREERMATE_NO_OPEN: "1"
95710
+ };
95711
+ if (process.env.CAREERMATE_PORT) env2.CAREERMATE_PORT = process.env.CAREERMATE_PORT;
95712
+ if (process.env.PATH) env2.PATH = process.env.PATH;
95713
+ const programArgs = [bin, ...args].map((arg) => ` <string>${xmlEscape(arg)}</string>`).join("\n");
95714
+ const envEntries = Object.entries(env2).map(([key, value]) => `${plistString(key, value)}`).join("\n");
95715
+ return `<?xml version="1.0" encoding="UTF-8"?>
95716
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
95717
+ <plist version="1.0">
95718
+ <dict>
95719
+ <key>Label</key>
95720
+ <string>${xmlEscape(launchdLabel())}</string>
95721
+ <key>ProgramArguments</key>
95722
+ <array>
95723
+ ${programArgs}
95724
+ </array>
95725
+ <key>WorkingDirectory</key>
95726
+ <string>${xmlEscape(cwd)}</string>
95727
+ <key>EnvironmentVariables</key>
95728
+ <dict>
95729
+ ${envEntries}
95730
+ </dict>
95731
+ <key>RunAtLoad</key>
95732
+ <true/>
95733
+ <key>KeepAlive</key>
95734
+ <false/>
95735
+ <key>StandardOutPath</key>
95736
+ <string>${xmlEscape(log)}</string>
95737
+ <key>StandardErrorPath</key>
95738
+ <string>${xmlEscape(log)}</string>
95739
+ </dict>
95740
+ </plist>
95741
+ `;
95742
+ }
95743
+ function delay(ms) {
95744
+ return new Promise((resolve) => setTimeout(resolve, ms));
95745
+ }
95746
+ async function pingDashboard(url, timeoutMs = HEALTH_TIMEOUT_MS) {
95747
+ const controller = new AbortController();
95748
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
95749
+ timer.unref?.();
95750
+ try {
95751
+ const healthUrl = new URL("/api/health", url);
95752
+ const res = await fetch(healthUrl, {
95753
+ signal: controller.signal,
95754
+ headers: { accept: "application/json" }
95755
+ });
95756
+ if (!res.ok) return false;
95757
+ const body = await res.json().catch(() => null);
95758
+ return body?.ok === true;
95759
+ } catch {
95760
+ return false;
95761
+ } finally {
95762
+ clearTimeout(timer);
95763
+ }
95764
+ }
95765
+ async function liveServer() {
95684
95766
  const info2 = readRuntimeInfo();
95685
- if (info2 && isProcessAlive(info2.pid)) return info2;
95767
+ if (info2 && isProcessAlive(info2.pid) && await pingDashboard(info2.url)) return info2;
95686
95768
  return null;
95687
95769
  }
95688
95770
  function spawnDetached(cmd, args, opts = {}) {
@@ -95691,19 +95773,101 @@ function spawnDetached(cmd, args, opts = {}) {
95691
95773
  child.on("error", () => {
95692
95774
  });
95693
95775
  child.unref();
95776
+ return child.pid ?? null;
95694
95777
  } catch {
95778
+ return null;
95695
95779
  }
95696
95780
  }
95781
+ function spawnViaLaunchd(bin, args, cwd, log) {
95782
+ if (process.platform !== "darwin" || process.env.CAREERMATE_LAUNCHER === "detached") return false;
95783
+ if (typeof process.getuid !== "function") return false;
95784
+ const domain = `gui/${process.getuid()}`;
95785
+ const label = launchdLabel();
95786
+ const plist = launchdPlistPath();
95787
+ try {
95788
+ fs5.writeFileSync(plist, renderLaunchdPlist(bin, args, cwd, log), "utf8");
95789
+ spawnSync("launchctl", ["bootout", `${domain}/${label}`], { stdio: "ignore" });
95790
+ const res = spawnSync("launchctl", ["bootstrap", domain, plist], {
95791
+ encoding: "utf8",
95792
+ timeout: 5e3
95793
+ });
95794
+ if (res.status === 0) return true;
95795
+ fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl bootstrap failed: ${res.stderr || res.stdout || res.status}
95796
+ `);
95797
+ } catch (e) {
95798
+ try {
95799
+ fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl launch failed: ${e instanceof Error ? e.message : String(e)}
95800
+ `);
95801
+ } catch {
95802
+ }
95803
+ }
95804
+ return false;
95805
+ }
95806
+ function spawnDashboardDetached(bin, args, cwd, logFd) {
95807
+ spawnDetached(bin, args, {
95808
+ cwd,
95809
+ env: { ...process.env, CAREERMATE_NO_OPEN: "1" },
95810
+ stdio: logFd === null ? "ignore" : ["ignore", logFd, logFd]
95811
+ });
95812
+ }
95697
95813
  async function ensureServer(timeoutMs = 12e3) {
95698
- const existing = liveServer();
95814
+ const existing = await liveServer();
95699
95815
  if (existing) return { url: existing.url };
95700
- const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path5.join(__dirname, "web.mjs")], __dirname] : [process.execPath, ["--no-warnings", "--experimental-sqlite", "--import", "tsx", "apps/web/src/index.ts"], REPO_ROOT];
95701
- spawnDetached(bin, args, { cwd, env: { ...process.env, CAREERMATE_NO_OPEN: "1" } });
95816
+ if (pendingStart) return pendingStart;
95817
+ pendingStart = startServerBackground(timeoutMs);
95818
+ try {
95819
+ return await pendingStart;
95820
+ } finally {
95821
+ pendingStart = null;
95822
+ }
95823
+ }
95824
+ async function startServerBackground(timeoutMs) {
95825
+ const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path5.join(__dirname, "web.mjs")], __dirname] : [
95826
+ process.execPath,
95827
+ ["--no-warnings", "--experimental-sqlite", "--import", "tsx", path5.join(REPO_ROOT, "apps/web/src/index.ts")],
95828
+ REPO_ROOT
95829
+ ];
95830
+ const log = dashboardLogPath();
95831
+ let logFd = null;
95832
+ try {
95833
+ logFd = fs5.openSync(log, "a");
95834
+ fs5.writeSync(logFd, `
95835
+ [${(/* @__PURE__ */ new Date()).toISOString()}] CareerMate dashboard background launch
95836
+ `);
95837
+ } catch {
95838
+ logFd = null;
95839
+ }
95840
+ let launchedViaLaunchd = false;
95841
+ try {
95842
+ launchedViaLaunchd = spawnViaLaunchd(bin, args, cwd, log);
95843
+ if (!launchedViaLaunchd) spawnDashboardDetached(bin, args, cwd, logFd);
95844
+ } finally {
95845
+ if (logFd !== null) {
95846
+ try {
95847
+ fs5.closeSync(logFd);
95848
+ } catch {
95849
+ }
95850
+ }
95851
+ }
95702
95852
  const start = Date.now();
95703
95853
  while (Date.now() - start < timeoutMs) {
95704
- const info2 = liveServer();
95854
+ const info2 = await liveServer();
95705
95855
  if (info2) return { url: info2.url };
95706
- await new Promise((r) => setTimeout(r, 300));
95856
+ await delay(300);
95857
+ }
95858
+ if (launchedViaLaunchd) {
95859
+ try {
95860
+ fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchd did not produce a healthy server; falling back to detached spawn
95861
+ `);
95862
+ } catch {
95863
+ }
95864
+ spawnDashboardDetached(bin, args, cwd, null);
95865
+ const fallbackStart = Date.now();
95866
+ while (Date.now() - fallbackStart < Math.min(timeoutMs, 8e3)) {
95867
+ const info2 = await liveServer();
95868
+ if (info2) return { url: info2.url };
95869
+ await delay(300);
95870
+ }
95707
95871
  }
95708
95872
  return null;
95709
95873
  }
@@ -95727,7 +95891,7 @@ function openInFileManager(dir) {
95727
95891
  else openOsTarget(dir);
95728
95892
  }
95729
95893
  async function resolveDashboardUrl(hash = "") {
95730
- const live = liveServer();
95894
+ const live = await liveServer();
95731
95895
  if (live) return { url: live.url + hash, running: true };
95732
95896
  const started = await ensureServer();
95733
95897
  if (started) return { url: started.url + hash, running: true };
@@ -95735,7 +95899,7 @@ async function resolveDashboardUrl(hash = "") {
95735
95899
  }
95736
95900
 
95737
95901
  // packages/mcp-tools/src/update.ts
95738
- import fs5 from "node:fs";
95902
+ import fs6 from "node:fs";
95739
95903
  import path6 from "node:path";
95740
95904
  import { fileURLToPath as fileURLToPath3 } from "node:url";
95741
95905
  import { spawn as spawn2 } from "node:child_process";
@@ -95792,14 +95956,14 @@ function comparePrerelease(a, b) {
95792
95956
  }
95793
95957
  function readCache() {
95794
95958
  try {
95795
- return JSON.parse(fs5.readFileSync(cacheFile(), "utf8"));
95959
+ return JSON.parse(fs6.readFileSync(cacheFile(), "utf8"));
95796
95960
  } catch {
95797
95961
  return null;
95798
95962
  }
95799
95963
  }
95800
95964
  function writeCache(c) {
95801
95965
  try {
95802
- fs5.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
95966
+ fs6.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
95803
95967
  } catch {
95804
95968
  }
95805
95969
  }
@@ -95956,16 +96120,18 @@ async function runSelfUpdate() {
95956
96120
  ok: true,
95957
96121
  from,
95958
96122
  to: latest,
95959
- message: `careermate\uB97C ${latest ? `v${latest}` : "\uCD5C\uC2E0 \uBC84\uC804"}(\uC73C)\uB85C \uC5C5\uB370\uC774\uD2B8\uD588\uC2B5\uB2C8\uB2E4. \uC801\uC6A9\uD558\uB824\uBA74 AI \uC571(\uB610\uB294 MCP \uC5F0\uACB0)\uC744 \uC7AC\uC2DC\uC791\uD558\uC138\uC694. \uC7AC\uC2DC\uC791 \uC2DC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uB294 \uC790\uB3D9\uC73C\uB85C \uB9C8\uC774\uADF8\uB808\uC774\uC158\uB418\uBA70 \uAE30\uC874 \uB370\uC774\uD130\uB294 \uBCF4\uC874\uB429\uB2C8\uB2E4.`
96123
+ message: `CareerMate\uB97C ${latest ? `v${latest}` : "\uCD5C\uC2E0 \uBC84\uC804"}(\uC73C)\uB85C \uC5C5\uB370\uC774\uD2B8\uD588\uC2B5\uB2C8\uB2E4. \uC801\uC6A9\uD558\uB824\uBA74 AI \uC571(\uB610\uB294 MCP \uC5F0\uACB0)\uC744 \uC7AC\uC2DC\uC791\uD558\uC138\uC694. \uC7AC\uC2DC\uC791 \uC2DC \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uB294 \uC790\uB3D9\uC73C\uB85C \uB9C8\uC774\uADF8\uB808\uC774\uC158\uB418\uBA70 \uAE30\uC874 \uB370\uC774\uD130\uB294 \uBCF4\uC874\uB429\uB2C8\uB2E4.`
95960
96124
  };
95961
96125
  }
95962
96126
 
95963
96127
  // packages/mcp-tools/src/tools.ts
95964
96128
  var STATUS_LIST = APPLICATION_STATUSES.map((s) => `${s}(${APPLICATION_STATUS_LABELS[s]})`).join(", ");
96129
+ var DELETE_CONFIRMATION = "DELETE";
96130
+ var DASHBOARD_URL = "http://127.0.0.1:4319";
95965
96131
  function listInboxFiles(dir) {
95966
96132
  let names;
95967
96133
  try {
95968
- names = fs6.readdirSync(dir);
96134
+ names = fs7.readdirSync(dir);
95969
96135
  } catch {
95970
96136
  return [];
95971
96137
  }
@@ -95975,7 +96141,7 @@ function listInboxFiles(dir) {
95975
96141
  if (SKIP.test(name)) continue;
95976
96142
  const p = path7.join(dir, name);
95977
96143
  try {
95978
- if (fs6.statSync(p).isFile()) out.push({ name, path: p });
96144
+ if (fs7.statSync(p).isFile()) out.push({ name, path: p });
95979
96145
  } catch {
95980
96146
  }
95981
96147
  }
@@ -96138,9 +96304,17 @@ ${lines}`;
96138
96304
  if (upd.update_available) {
96139
96305
  text += `
96140
96306
 
96141
- \u{1F195} \uC0C8 \uBC84\uC804 v${upd.latest} \uC0AC\uC6A9 \uAC00\uB2A5 (\uD604\uC7AC v${upd.current}). \uC0AC\uC6A9\uC790\uAC00 \uC6D0\uD558\uBA74 update_careermate \uB3C4\uAD6C\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC138\uC694.`;
96307
+ \uC0C8 \uBC84\uC804 v${upd.latest}\uC774 \uB098\uC654\uC2B5\uB2C8\uB2E4(\uD604\uC7AC v${upd.current}). \uC0AC\uC6A9\uC790\uAC00 \uC6D0\uD558\uBA74 \uBC14\uB85C \uC5C5\uB370\uC774\uD2B8\uB97C \uC9C4\uD589\uD558\uC138\uC694. \uC5C5\uB370\uC774\uD2B8 \uD6C4\uC5D0\uB294 AI \uC571 \uB610\uB294 MCP \uC5F0\uACB0\uC744 \uC7AC\uC2DC\uC791\uD574\uC57C \uC801\uC6A9\uB429\uB2C8\uB2E4.`;
96142
96308
  }
96143
- return ok(text, { ...s, verify_strict: strict, update: upd });
96309
+ return ok(text, {
96310
+ ...s,
96311
+ verify_strict: strict,
96312
+ update: upd,
96313
+ suggested_next_action: upd.update_available ? {
96314
+ prompt: `\uC0C8 \uBC84\uC804 v${upd.latest}\uC774 \uB098\uC654\uC2B5\uB2C8\uB2E4. \uC9C0\uAE08 \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694?`,
96315
+ next_tool: "update_careermate"
96316
+ } : null
96317
+ });
96144
96318
  }
96145
96319
  },
96146
96320
  /* --------------------------------------------------------------- profile */
@@ -96293,6 +96467,31 @@ ${lines}`;
96293
96467
  }
96294
96468
  }
96295
96469
  },
96470
+ {
96471
+ name: "delete_cover_letter",
96472
+ title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC0AD\uC81C",
96473
+ 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.",
96474
+ inputSchema: {
96475
+ cover_letter_id: external_exports.string(),
96476
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
96477
+ },
96478
+ handler: (args) => {
96479
+ if (args?.confirm !== DELETE_CONFIRMATION) {
96480
+ return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
96481
+ }
96482
+ const cl = coverLetterRepo.get(args.cover_letter_id, false);
96483
+ if (!cl) return fail("\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
96484
+ const removed = coverLetterRepo.remove(cl.id);
96485
+ if (!removed) return fail("\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
96486
+ const user_message = `'${cl.title}' \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC0AD\uC81C\uD588\uC5B4\uC694. \uC5F0\uACB0\uB41C \uC9C0\uC6D0 \uAE30\uB85D\uC5D0\uC11C\uB294 \uC790\uAE30\uC18C\uAC1C\uC11C \uC5F0\uACB0\uB9CC \uBE44\uC6CC\uB480\uC2B5\uB2C8\uB2E4.`;
96487
+ return ok(user_message, {
96488
+ deleted: true,
96489
+ cover_letter_id: cl.id,
96490
+ title: cl.title,
96491
+ user_message
96492
+ });
96493
+ }
96494
+ },
96296
96495
  /* ------------------------------------------------------------------ jobs */
96297
96496
  {
96298
96497
  name: "save_job_posting",
@@ -96301,7 +96500,15 @@ ${lines}`;
96301
96500
  inputSchema: JobInputSchema.shape,
96302
96501
  handler: (args) => {
96303
96502
  const { job } = saveJobPosting(args);
96304
- return ok(`'${job.company} \xB7 ${job.position}' \uACF5\uACE0\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4. (job_id: ${job.id})`, job);
96503
+ const user_message = `'${job.company} \xB7 ${job.position}' \uACF5\uACE0\uB97C \uC800\uC7A5\uD588\uC5B4\uC694.`;
96504
+ return ok(user_message, {
96505
+ ...job,
96506
+ user_message,
96507
+ suggested_next_action: {
96508
+ prompt: "\uC774 \uACF5\uACE0\uC640 \uB0B4 \uD504\uB85C\uD544\uC744 \uBE44\uAD50\uD574 \uC801\uD569\uB3C4\uB97C \uBD84\uC11D\uD560\uAE4C\uC694?",
96509
+ next_workflow: "analyze_job"
96510
+ }
96511
+ });
96305
96512
  }
96306
96513
  },
96307
96514
  {
@@ -96312,7 +96519,7 @@ ${lines}`;
96312
96519
  readOnly: true,
96313
96520
  handler: (args) => {
96314
96521
  const job = jobRepo.get(args.job_id);
96315
- if (!job) return fail(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${args.job_id}`);
96522
+ if (!job) return fail("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
96316
96523
  return ok(`${job.company} \xB7 ${job.position}`, {
96317
96524
  job: jobWithMeta(job),
96318
96525
  fit: fitRepo.getByJob(job.id),
@@ -96333,6 +96540,35 @@ ${lines}`;
96333
96540
  return ok(`\uACF5\uACE0 ${jobs.length}\uAC74`, jobs);
96334
96541
  }
96335
96542
  },
96543
+ {
96544
+ name: "delete_job_posting",
96545
+ title: "\uCC44\uC6A9\uACF5\uACE0 \uC0AD\uC81C",
96546
+ 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.",
96547
+ inputSchema: {
96548
+ job_id: external_exports.string(),
96549
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
96550
+ },
96551
+ handler: (args) => {
96552
+ if (args?.confirm !== DELETE_CONFIRMATION) {
96553
+ return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
96554
+ }
96555
+ const job = jobRepo.get(args.job_id);
96556
+ if (!job) return fail("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
96557
+ const detachedCoverLetters = coverLetterRepo.listByJob(job.id).length;
96558
+ const removed = jobRepo.remove(job.id);
96559
+ if (!removed) return fail("\uACF5\uACE0\uB97C \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
96560
+ const detachedNote = detachedCoverLetters ? ` \uC5F0\uACB0\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C ${detachedCoverLetters}\uAC74\uC740 \uC0AD\uC81C\uD558\uC9C0 \uC54A\uACE0 \uACF5\uACE0 \uC5F0\uACB0\uB9CC \uD574\uC81C\uD588\uC2B5\uB2C8\uB2E4.` : "";
96561
+ const user_message = `'${job.company} \xB7 ${job.position}' \uACF5\uACE0\uB97C \uC0AD\uC81C\uD588\uC5B4\uC694. \uC5F0\uACB0\uB41C \uC9C0\uC6D0 \uC0C1\uD0DC, \uC801\uD569\uB3C4 \uBD84\uC11D, \uBA74\uC811 \uC900\uBE44\uB3C4 \uD568\uAED8 \uC815\uB9AC\uD588\uC2B5\uB2C8\uB2E4.${detachedNote}`;
96562
+ return ok(user_message, {
96563
+ deleted: true,
96564
+ job_id: job.id,
96565
+ company: job.company,
96566
+ position: job.position,
96567
+ detached_cover_letters: detachedCoverLetters,
96568
+ user_message
96569
+ });
96570
+ }
96571
+ },
96336
96572
  /* ----------------------------------------------------- application context */
96337
96573
  {
96338
96574
  name: "get_application_context",
@@ -96355,7 +96591,7 @@ ${lines}`;
96355
96591
  `\uC774\uB825\uC11C ${ctx.resumes.length} \xB7 \uACBD\uB825 ${ctx.experiences.length} \xB7 \uD504\uB85C\uC81D\uD2B8 ${ctx.projects.length} \xB7 \uC2A4\uD0AC ${ctx.skills.length} \xB7 \uC790\uC18C\uC11C ${ctx.cover_letters.length}`,
96356
96592
  ctx.job ? `\uB300\uC0C1 \uACF5\uACE0: ${ctx.job.company} \xB7 ${ctx.job.position}` : "\uB300\uC0C1 \uACF5\uACE0 \uC5C6\uC74C",
96357
96593
  ctx.writing_preferences.preferred_tone ? `\uC120\uD638 \uBB38\uCCB4: ${ctx.writing_preferences.preferred_tone}` : "",
96358
- recommended_route ? `\u{1F4A1} \uB2E4\uC74C \uB2E8\uACC4: get_workflow_guide({ workflow_id: "${recommended_route}" })\uB85C \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28(EOP)\uC640 \uC801\uC6A9\uD560 \uD50C\uB808\uC774\uBD81\xB7\uAC80\uC99D \uC21C\uC11C\uB97C \uBC1B\uC544 \uC801\uC6A9\uD558\uC138\uC694(\uC989\uB2F5 \uAE08\uC9C0).` : "\u{1F4A1} \uCEE4\uB9AC\uC5B4 \uC791\uC5C5\uC744 \uC2DC\uC791\uD558\uB824\uBA74 get_workflow_guide\uB85C \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28\uC640 \uAC80\uC99D \uC21C\uC11C\uB97C \uBA3C\uC800 \uBC1B\uC73C\uC138\uC694."
96594
+ recommended_route ? `\uB0B4\uBD80 \uC548\uB0B4: \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uB3C4\uAD6C\uBA85\uC774\uB098 ID\uB97C \uB9D0\uD558\uC9C0 \uB9D0\uACE0, \uBA3C\uC800 get_workflow_guide({ workflow_id: "${recommended_route}" })\uB85C \uC2E4\uD589 \uC808\uCC28\uC640 \uAC80\uC99D \uC21C\uC11C\uB97C \uBC1B\uC740 \uB4A4 \uC26C\uC6B4 \uB9D0\uB85C \uC548\uB0B4\uD558\uC138\uC694.` : "\uB0B4\uBD80 \uC548\uB0B4: \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uB3C4\uAD6C\uBA85\uC744 \uB9D0\uD558\uC9C0 \uB9D0\uACE0, \uBA3C\uC800 get_workflow_guide\uB85C \uC2E4\uD589 \uC808\uCC28\uC640 \uAC80\uC99D \uC21C\uC11C\uB97C \uBC1B\uC740 \uB4A4 \uC26C\uC6B4 \uB9D0\uB85C \uC548\uB0B4\uD558\uC138\uC694."
96359
96595
  ].filter(Boolean).join("\n");
96360
96596
  return ok(summary, enriched);
96361
96597
  }
@@ -96368,7 +96604,34 @@ ${lines}`;
96368
96604
  handler: (args) => {
96369
96605
  try {
96370
96606
  const fit = saveFitAnalysis(args);
96371
- return ok(`\uC801\uD569\uB3C4 \uBD84\uC11D\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4${fit.score != null ? ` (${fit.score}\uC810)` : ""}.`, fit);
96607
+ const suggested_next_action = {
96608
+ prompt: "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?",
96609
+ options: [
96610
+ {
96611
+ label: "\uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918",
96612
+ next_workflow: "write_cover_letter"
96613
+ },
96614
+ {
96615
+ label: "\uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918",
96616
+ next_workflow: null
96617
+ }
96618
+ ]
96619
+ };
96620
+ const user_message = `\uC801\uD569\uB3C4 \uBD84\uC11D\uC744 \uC800\uC7A5\uD588\uC5B4\uC694${fit.score != null ? ` (${fit.score}\uC810)` : ""}. \uC790\uC138\uD55C \uACB0\uACFC\uB294 \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C\uB3C4 \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4: ${DASHBOARD_URL}`;
96621
+ return ok(
96622
+ [
96623
+ user_message,
96624
+ "",
96625
+ "\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB2E4\uC74C \uC120\uD0DD\uC9C0\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694:",
96626
+ "1. \uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918",
96627
+ "2. \uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918"
96628
+ ].join("\n"),
96629
+ {
96630
+ ...fit,
96631
+ user_message,
96632
+ suggested_next_action
96633
+ }
96634
+ );
96372
96635
  } catch (e) {
96373
96636
  return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
96374
96637
  }
@@ -96387,11 +96650,19 @@ ${lines}`;
96387
96650
  handler: (args) => {
96388
96651
  try {
96389
96652
  const res = updateApplicationStatus(args.job_id, args.status, args.note);
96390
- return ok(
96391
- `\uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4.${res.hint ? `
96653
+ const user_message = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.` + (res.hint ? `
96392
96654
 
96393
- \u{1F4A1} ${res.hint}` : ""}`,
96394
- res
96655
+ ${res.hint}` : "");
96656
+ return ok(
96657
+ user_message,
96658
+ {
96659
+ ...res,
96660
+ user_message,
96661
+ suggested_next_action: res.hint ? {
96662
+ prompt: res.hint,
96663
+ next_workflow: "prepare_interview"
96664
+ } : null
96665
+ }
96395
96666
  );
96396
96667
  } catch (e) {
96397
96668
  return fail(e instanceof Error ? e.message : "\uBCC0\uACBD \uC2E4\uD328");
@@ -96407,7 +96678,8 @@ ${lines}`;
96407
96678
  handler: (args) => {
96408
96679
  try {
96409
96680
  const prep = saveInterviewPrep(args);
96410
- return ok(`\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC9C8\uBB38 ${prep.questions.length}\uAC1C).`, prep);
96681
+ const user_message = `\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD588\uC5B4\uC694. \uC608\uC0C1 \uC9C8\uBB38 ${prep.questions.length}\uAC1C\uAC00 \uC900\uBE44\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`;
96682
+ return ok(user_message, { ...prep, user_message });
96411
96683
  } catch (e) {
96412
96684
  return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
96413
96685
  }
@@ -96432,7 +96704,7 @@ ${lines}`;
96432
96704
  const dir = getExportsDir();
96433
96705
  const filePath = path7.join(dir, result2.filename);
96434
96706
  try {
96435
- fs6.writeFileSync(filePath, result2.content, "utf8");
96707
+ fs7.writeFileSync(filePath, result2.content, "utf8");
96436
96708
  } catch (e) {
96437
96709
  return fail(`\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}`);
96438
96710
  }
@@ -96594,29 +96866,37 @@ ${report.disclaimer}`, report);
96594
96866
  {
96595
96867
  name: "check_for_update",
96596
96868
  title: "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778",
96597
- 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. \uC2E4\uC81C \uC801\uC6A9\uC740 update_careermate\uB85C \uD569\uB2C8\uB2E4.',
96869
+ 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.',
96598
96870
  inputSchema: {},
96599
96871
  readOnly: true,
96600
96872
  handler: async () => {
96601
96873
  const u = await getUpdateStatusAsync();
96602
96874
  if (u.latest === null) return ok("\uC5C5\uB370\uC774\uD2B8 \uD655\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4(\uC624\uD504\uB77C\uC778\uC774\uAC70\uB098 \uC77C\uC2DC\uC801 \uC624\uB958). \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.", u);
96875
+ const user_message = u.update_available ? `\uC0C8 \uBC84\uC804 v${u.latest}\uC774 \uB098\uC654\uC5B4\uC694. \uD604\uC7AC \uBC84\uC804\uC740 v${u.current}\uC785\uB2C8\uB2E4. \uC6D0\uD558\uC2DC\uBA74 \uC9C0\uAE08 \uC5C5\uB370\uC774\uD2B8\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.` : `\uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4 (v${u.current}).`;
96603
96876
  return ok(
96604
- u.update_available ? `\uC0C8 \uBC84\uC804 v${u.latest} \uC0AC\uC6A9 \uAC00\uB2A5 (\uD604\uC7AC v${u.current}). update_careermate\uB85C \uC5C5\uB370\uC774\uD2B8\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.` : `\uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4 (v${u.current}).`,
96605
- u
96877
+ user_message,
96878
+ {
96879
+ ...u,
96880
+ user_message,
96881
+ suggested_next_action: u.update_available ? {
96882
+ prompt: "\uC9C0\uAE08 CareerMate\uB97C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694?",
96883
+ next_tool: "update_careermate"
96884
+ } : null
96885
+ }
96606
96886
  );
96607
96887
  }
96608
96888
  },
96609
96889
  {
96610
96890
  name: "update_careermate",
96611
96891
  title: "CareerMate \uC5C5\uB370\uC774\uD2B8",
96612
- 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"\uB77C\uACE0 \uD560 \uB54C\uB9CC \uD638\uCD9C\uD558\uC138\uC694. \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\uAC00 \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.',
96892
+ 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.',
96613
96893
  inputSchema: {},
96614
96894
  handler: async () => {
96615
96895
  const r = await runSelfUpdate();
96616
96896
  if (!r.ok) return fail(`${r.message}${r.manual ? `
96617
96897
 
96618
96898
  \uC9C1\uC811 \uC2E4\uD589: ${r.manual}` : ""}`);
96619
- return ok(r.message, r);
96899
+ return ok(r.message, { ...r, user_message: r.message });
96620
96900
  }
96621
96901
  }
96622
96902
  ];