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/doctor.mjs CHANGED
@@ -6477,9 +6477,9 @@ var require_timers = __commonJS({
6477
6477
  clearTimeout(this.handle);
6478
6478
  };
6479
6479
  var afterValue = function(value) {
6480
- return delay(+this).thenReturn(value);
6480
+ return delay2(+this).thenReturn(value);
6481
6481
  };
6482
- var delay = Promise2.delay = function(ms, value) {
6482
+ var delay2 = Promise2.delay = function(ms, value) {
6483
6483
  var ret2;
6484
6484
  var handle;
6485
6485
  if (value !== void 0) {
@@ -6501,7 +6501,7 @@ var require_timers = __commonJS({
6501
6501
  return ret2;
6502
6502
  };
6503
6503
  Promise2.prototype.delay = function(ms) {
6504
- return delay(ms, this);
6504
+ return delay2(ms, this);
6505
6505
  };
6506
6506
  var afterTimeout = function(promise, message, parent) {
6507
6507
  var err2;
@@ -28297,7 +28297,7 @@ var require_path_is_absolute = __commonJS({
28297
28297
  var require_files = __commonJS({
28298
28298
  "node_modules/mammoth/lib/docx/files.js"(exports2) {
28299
28299
  "use strict";
28300
- var fs8 = __require("fs");
28300
+ var fs9 = __require("fs");
28301
28301
  var url = __require("url");
28302
28302
  var os3 = __require("os");
28303
28303
  var dirname = __require("path").dirname;
@@ -28338,7 +28338,7 @@ var require_files = __commonJS({
28338
28338
  read
28339
28339
  };
28340
28340
  }
28341
- var readFile = promises.promisify(fs8.readFile.bind(fs8));
28341
+ var readFile = promises.promisify(fs9.readFile.bind(fs9));
28342
28342
  function uriToPath(uriString, platform) {
28343
28343
  if (!platform) {
28344
28344
  platform = os3.platform();
@@ -31080,11 +31080,11 @@ var require_options_reader = __commonJS({
31080
31080
  var require_unzip = __commonJS({
31081
31081
  "node_modules/mammoth/lib/unzip.js"(exports2) {
31082
31082
  "use strict";
31083
- var fs8 = __require("fs");
31083
+ var fs9 = __require("fs");
31084
31084
  var promises = require_promises();
31085
31085
  var zipfile = require_zipfile();
31086
31086
  exports2.openZip = openZip;
31087
- var readFile = promises.promisify(fs8.readFile);
31087
+ var readFile = promises.promisify(fs9.readFile);
31088
31088
  function openZip(options) {
31089
31089
  if (options.path) {
31090
31090
  return readFile(options.path).then(zipfile.openArrayBuffer);
@@ -31809,9 +31809,9 @@ var require_cjs = __commonJS({
31809
31809
  }
31810
31810
  return o;
31811
31811
  }
31812
- var fs8;
31812
+ var fs9;
31813
31813
  function get_fs() {
31814
- return fs8 || (fs8 = require$$0__default["default"]);
31814
+ return fs9 || (fs9 = require$$0__default["default"]);
31815
31815
  }
31816
31816
  function parse2(file, options) {
31817
31817
  if (file[0] == 80 && file[1] == 75) return parse_zip(file, options);
@@ -32096,7 +32096,7 @@ var require_cjs = __commonJS({
32096
32096
  }
32097
32097
  function read_file(filename2, options) {
32098
32098
  get_fs();
32099
- return parse2(fs8.readFileSync(filename2), options);
32099
+ return parse2(fs9.readFileSync(filename2), options);
32100
32100
  }
32101
32101
  function read(blob, options) {
32102
32102
  switch (options && options.type || "base64") {
@@ -32411,7 +32411,7 @@ var require_cjs = __commonJS({
32411
32411
  function write_file(cfb2, filename2, options) {
32412
32412
  get_fs();
32413
32413
  var o = _write(cfb2, options);
32414
- fs8.writeFileSync(filename2, o);
32414
+ fs9.writeFileSync(filename2, o);
32415
32415
  }
32416
32416
  function a2s(o) {
32417
32417
  var out = new Array(o.length);
@@ -32423,7 +32423,7 @@ var require_cjs = __commonJS({
32423
32423
  switch (options && options.type || "buffer") {
32424
32424
  case "file":
32425
32425
  get_fs();
32426
- fs8.writeFileSync(options.filename, o);
32426
+ fs9.writeFileSync(options.filename, o);
32427
32427
  return o;
32428
32428
  case "binary":
32429
32429
  return typeof o == "string" ? o : a2s(o);
@@ -40116,8 +40116,8 @@ function blend(fg, bg, alpha) {
40116
40116
  return Math.round(alpha * fg + (1 - alpha) * bg);
40117
40117
  }
40118
40118
  async function node_utils_fetchData(url) {
40119
- const fs8 = process.getBuiltinModule("fs/promises");
40120
- const data = await fs8.readFile(url);
40119
+ const fs9 = process.getBuiltinModule("fs/promises");
40120
+ const data = await fs9.readFile(url);
40121
40121
  return new Uint8Array(data);
40122
40122
  }
40123
40123
  function initGPU() {
@@ -40746,11 +40746,11 @@ function network_getArrayBuffer(val) {
40746
40746
  return typeof val !== "string" ? val : stringToBytes(val).buffer;
40747
40747
  }
40748
40748
  function getReadableStream(url, opts = null) {
40749
- const fs8 = process.getBuiltinModule("fs");
40749
+ const fs9 = process.getBuiltinModule("fs");
40750
40750
  const {
40751
40751
  Readable
40752
40752
  } = process.getBuiltinModule("stream");
40753
- const readStream = fs8.createReadStream(url, opts);
40753
+ const readStream = fs9.createReadStream(url, opts);
40754
40754
  return Readable.toWeb(readStream);
40755
40755
  }
40756
40756
  function getNetworkStream(url) {
@@ -57747,8 +57747,8 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
57747
57747
  url
57748
57748
  } = stream._source;
57749
57749
  this._isStreamingSupported = !disableStream;
57750
- const fs8 = process.getBuiltinModule("fs/promises");
57751
- fs8.lstat(url).then((stat) => {
57750
+ const fs9 = process.getBuiltinModule("fs/promises");
57751
+ fs9.lstat(url).then((stat) => {
57752
57752
  const readableStream = getReadableStream(url);
57753
57753
  this._reader = readableStream.getReader();
57754
57754
  const {
@@ -59394,9 +59394,9 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
59394
59394
  return;
59395
59395
  }
59396
59396
  if (reason instanceof RenderingCancelledException) {
59397
- let delay = RENDERING_CANCELLED_TIMEOUT;
59397
+ let delay2 = RENDERING_CANCELLED_TIMEOUT;
59398
59398
  if (reason.extraDelay > 0 && reason.extraDelay < 1e3) {
59399
- delay += reason.extraDelay;
59399
+ delay2 += reason.extraDelay;
59400
59400
  }
59401
59401
  intentState.streamReaderCancelTimeout = setTimeout(() => {
59402
59402
  intentState.streamReaderCancelTimeout = null;
@@ -59405,7 +59405,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
59405
59405
  reason,
59406
59406
  force: true
59407
59407
  });
59408
- }, delay);
59408
+ }, delay2);
59409
59409
  return;
59410
59410
  }
59411
59411
  }
@@ -71040,7 +71040,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
71040
71040
  });
71041
71041
 
71042
71042
  // scripts/doctor.ts
71043
- import fs7 from "node:fs";
71043
+ import fs8 from "node:fs";
71044
71044
 
71045
71045
  // packages/db/src/paths.ts
71046
71046
  import os from "node:os";
@@ -75932,7 +75932,7 @@ var ApplicationContextSchema = external_exports.object({
75932
75932
  });
75933
75933
 
75934
75934
  // packages/shared/src/build-info.ts
75935
- var APP_VERSION = true ? "0.0.4" : "0.0.0-dev";
75935
+ var APP_VERSION = true ? "0.0.5" : "0.0.0-dev";
75936
75936
  var BUNDLED = true;
75937
75937
 
75938
75938
  // packages/shared/src/index.ts
@@ -76444,6 +76444,7 @@ var coverLetterRepo = {
76444
76444
  remove(id2) {
76445
76445
  const db = getDb();
76446
76446
  return tx(() => {
76447
+ db.prepare(`UPDATE applications SET cover_letter_id=NULL, updated_at=? WHERE cover_letter_id=?`).run(now(), id2);
76447
76448
  db.prepare(`DELETE FROM cover_letter_versions WHERE cover_letter_id=?`).run(id2);
76448
76449
  return db.prepare(`DELETE FROM cover_letters WHERE id=?`).run(id2).changes > 0;
76449
76450
  });
@@ -76532,6 +76533,7 @@ var jobRepo = {
76532
76533
  remove(id2) {
76533
76534
  const db = getDb();
76534
76535
  return tx(() => {
76536
+ db.prepare(`UPDATE cover_letters SET job_id=NULL, updated_at=? WHERE job_id=?`).run(now(), id2);
76535
76537
  db.prepare(`DELETE FROM fit_analyses WHERE job_id=?`).run(id2);
76536
76538
  db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id2);
76537
76539
  db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id2);
@@ -76730,7 +76732,7 @@ var activityRepo = {
76730
76732
  };
76731
76733
 
76732
76734
  // packages/mcp-tools/src/tools.ts
76733
- import fs6 from "node:fs";
76735
+ import fs7 from "node:fs";
76734
76736
  import path7 from "node:path";
76735
76737
 
76736
76738
  // packages/core/src/onboarding.ts
@@ -77210,7 +77212,7 @@ function saveJobPosting(input, id2) {
77210
77212
  }
77211
77213
  function saveFitAnalysis(input) {
77212
77214
  const job = jobRepo.get(input.job_id);
77213
- 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.`);
77215
+ 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.");
77214
77216
  applicationRepo.ensure(job.id, "draft");
77215
77217
  const fit = fitRepo.save(input);
77216
77218
  const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
@@ -77252,7 +77254,7 @@ function saveCoverLetterVersion(input) {
77252
77254
  }
77253
77255
  function updateApplicationStatus(jobId, status, note) {
77254
77256
  const job = jobRepo.get(jobId);
77255
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${jobId}`);
77257
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
77256
77258
  const application = applicationRepo.setStatus(jobId, status, note);
77257
77259
  activityRepo.log(
77258
77260
  "application_status_changed",
@@ -77262,17 +77264,17 @@ function updateApplicationStatus(jobId, status, note) {
77262
77264
  );
77263
77265
  const interview_unlocked = INTERVIEW_UNLOCK_STATUSES.includes(status);
77264
77266
  const hasPrep = !!interviewRepo.getByJob(jobId);
77265
- 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;
77267
+ 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;
77266
77268
  return { application, job, interview_unlocked, hint };
77267
77269
  }
77268
77270
  function saveInterviewPrep(input) {
77269
77271
  const job = jobRepo.get(input.job_id);
77270
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}`);
77272
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
77271
77273
  const app = applicationRepo.getByJob(job.id);
77272
77274
  if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
77273
77275
  const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
77274
77276
  throw conflict(
77275
- `\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.`
77277
+ `\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.`
77276
77278
  );
77277
77279
  }
77278
77280
  const prep = interviewRepo.save(input);
@@ -77370,8 +77372,8 @@ var WORKFLOWS = [
77370
77372
  "\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.",
77371
77373
  "\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).",
77372
77374
  "\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.",
77373
- '"\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.',
77374
- '\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.',
77375
+ '\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".',
77376
+ '\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.',
77375
77377
  "\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).",
77376
77378
  "`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."
77377
77379
  ]
@@ -78639,14 +78641,94 @@ function fail(text) {
78639
78641
  }
78640
78642
 
78641
78643
  // packages/mcp-tools/src/bridge.ts
78642
- import { spawn } from "node:child_process";
78644
+ import { spawn, spawnSync } from "node:child_process";
78645
+ import fs5 from "node:fs";
78643
78646
  import path5 from "node:path";
78644
78647
  import { fileURLToPath as fileURLToPath2 } from "node:url";
78645
78648
  var __dirname = path5.dirname(fileURLToPath2(import.meta.url));
78646
78649
  var REPO_ROOT = path5.resolve(__dirname, "..", "..", "..");
78647
- function liveServer() {
78650
+ var HEALTH_TIMEOUT_MS = 800;
78651
+ var pendingStart = null;
78652
+ function dashboardLogPath() {
78653
+ return path5.join(getDataDir(), "server.log");
78654
+ }
78655
+ function launchdPlistPath() {
78656
+ return path5.join(getDataDir(), "dashboard.launchd.plist");
78657
+ }
78658
+ function launchdLabel() {
78659
+ let hash = 5381;
78660
+ for (const ch of getDataDir()) hash = (hash * 33 ^ ch.charCodeAt(0)) >>> 0;
78661
+ return `life.careermate.dashboard.${hash.toString(16)}`;
78662
+ }
78663
+ function xmlEscape(value) {
78664
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
78665
+ }
78666
+ function plistString(key, value) {
78667
+ return ` <key>${xmlEscape(key)}</key>
78668
+ <string>${xmlEscape(value)}</string>`;
78669
+ }
78670
+ function renderLaunchdPlist(bin, args, cwd, log) {
78671
+ const env2 = {
78672
+ CAREERMATE_DATA_DIR: getDataDir(),
78673
+ CAREERMATE_NO_OPEN: "1"
78674
+ };
78675
+ if (process.env.CAREERMATE_PORT) env2.CAREERMATE_PORT = process.env.CAREERMATE_PORT;
78676
+ if (process.env.PATH) env2.PATH = process.env.PATH;
78677
+ const programArgs = [bin, ...args].map((arg) => ` <string>${xmlEscape(arg)}</string>`).join("\n");
78678
+ const envEntries = Object.entries(env2).map(([key, value]) => `${plistString(key, value)}`).join("\n");
78679
+ return `<?xml version="1.0" encoding="UTF-8"?>
78680
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
78681
+ <plist version="1.0">
78682
+ <dict>
78683
+ <key>Label</key>
78684
+ <string>${xmlEscape(launchdLabel())}</string>
78685
+ <key>ProgramArguments</key>
78686
+ <array>
78687
+ ${programArgs}
78688
+ </array>
78689
+ <key>WorkingDirectory</key>
78690
+ <string>${xmlEscape(cwd)}</string>
78691
+ <key>EnvironmentVariables</key>
78692
+ <dict>
78693
+ ${envEntries}
78694
+ </dict>
78695
+ <key>RunAtLoad</key>
78696
+ <true/>
78697
+ <key>KeepAlive</key>
78698
+ <false/>
78699
+ <key>StandardOutPath</key>
78700
+ <string>${xmlEscape(log)}</string>
78701
+ <key>StandardErrorPath</key>
78702
+ <string>${xmlEscape(log)}</string>
78703
+ </dict>
78704
+ </plist>
78705
+ `;
78706
+ }
78707
+ function delay(ms) {
78708
+ return new Promise((resolve) => setTimeout(resolve, ms));
78709
+ }
78710
+ async function pingDashboard(url, timeoutMs = HEALTH_TIMEOUT_MS) {
78711
+ const controller = new AbortController();
78712
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
78713
+ timer.unref?.();
78714
+ try {
78715
+ const healthUrl = new URL("/api/health", url);
78716
+ const res = await fetch(healthUrl, {
78717
+ signal: controller.signal,
78718
+ headers: { accept: "application/json" }
78719
+ });
78720
+ if (!res.ok) return false;
78721
+ const body = await res.json().catch(() => null);
78722
+ return body?.ok === true;
78723
+ } catch {
78724
+ return false;
78725
+ } finally {
78726
+ clearTimeout(timer);
78727
+ }
78728
+ }
78729
+ async function liveServer() {
78648
78730
  const info3 = readRuntimeInfo();
78649
- if (info3 && isProcessAlive(info3.pid)) return info3;
78731
+ if (info3 && isProcessAlive(info3.pid) && await pingDashboard(info3.url)) return info3;
78650
78732
  return null;
78651
78733
  }
78652
78734
  function spawnDetached(cmd, args, opts = {}) {
@@ -78655,19 +78737,101 @@ function spawnDetached(cmd, args, opts = {}) {
78655
78737
  child.on("error", () => {
78656
78738
  });
78657
78739
  child.unref();
78740
+ return child.pid ?? null;
78658
78741
  } catch {
78742
+ return null;
78659
78743
  }
78660
78744
  }
78745
+ function spawnViaLaunchd(bin, args, cwd, log) {
78746
+ if (process.platform !== "darwin" || process.env.CAREERMATE_LAUNCHER === "detached") return false;
78747
+ if (typeof process.getuid !== "function") return false;
78748
+ const domain = `gui/${process.getuid()}`;
78749
+ const label = launchdLabel();
78750
+ const plist = launchdPlistPath();
78751
+ try {
78752
+ fs5.writeFileSync(plist, renderLaunchdPlist(bin, args, cwd, log), "utf8");
78753
+ spawnSync("launchctl", ["bootout", `${domain}/${label}`], { stdio: "ignore" });
78754
+ const res = spawnSync("launchctl", ["bootstrap", domain, plist], {
78755
+ encoding: "utf8",
78756
+ timeout: 5e3
78757
+ });
78758
+ if (res.status === 0) return true;
78759
+ fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl bootstrap failed: ${res.stderr || res.stdout || res.status}
78760
+ `);
78761
+ } catch (e) {
78762
+ try {
78763
+ fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchctl launch failed: ${e instanceof Error ? e.message : String(e)}
78764
+ `);
78765
+ } catch {
78766
+ }
78767
+ }
78768
+ return false;
78769
+ }
78770
+ function spawnDashboardDetached(bin, args, cwd, logFd) {
78771
+ spawnDetached(bin, args, {
78772
+ cwd,
78773
+ env: { ...process.env, CAREERMATE_NO_OPEN: "1" },
78774
+ stdio: logFd === null ? "ignore" : ["ignore", logFd, logFd]
78775
+ });
78776
+ }
78661
78777
  async function ensureServer(timeoutMs = 12e3) {
78662
- const existing = liveServer();
78778
+ const existing = await liveServer();
78663
78779
  if (existing) return { url: existing.url };
78664
- 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];
78665
- spawnDetached(bin, args, { cwd, env: { ...process.env, CAREERMATE_NO_OPEN: "1" } });
78780
+ if (pendingStart) return pendingStart;
78781
+ pendingStart = startServerBackground(timeoutMs);
78782
+ try {
78783
+ return await pendingStart;
78784
+ } finally {
78785
+ pendingStart = null;
78786
+ }
78787
+ }
78788
+ async function startServerBackground(timeoutMs) {
78789
+ const [bin, args, cwd] = BUNDLED ? [process.execPath, ["--experimental-sqlite", path5.join(__dirname, "web.mjs")], __dirname] : [
78790
+ process.execPath,
78791
+ ["--no-warnings", "--experimental-sqlite", "--import", "tsx", path5.join(REPO_ROOT, "apps/web/src/index.ts")],
78792
+ REPO_ROOT
78793
+ ];
78794
+ const log = dashboardLogPath();
78795
+ let logFd = null;
78796
+ try {
78797
+ logFd = fs5.openSync(log, "a");
78798
+ fs5.writeSync(logFd, `
78799
+ [${(/* @__PURE__ */ new Date()).toISOString()}] CareerMate dashboard background launch
78800
+ `);
78801
+ } catch {
78802
+ logFd = null;
78803
+ }
78804
+ let launchedViaLaunchd = false;
78805
+ try {
78806
+ launchedViaLaunchd = spawnViaLaunchd(bin, args, cwd, log);
78807
+ if (!launchedViaLaunchd) spawnDashboardDetached(bin, args, cwd, logFd);
78808
+ } finally {
78809
+ if (logFd !== null) {
78810
+ try {
78811
+ fs5.closeSync(logFd);
78812
+ } catch {
78813
+ }
78814
+ }
78815
+ }
78666
78816
  const start = Date.now();
78667
78817
  while (Date.now() - start < timeoutMs) {
78668
- const info3 = liveServer();
78818
+ const info3 = await liveServer();
78669
78819
  if (info3) return { url: info3.url };
78670
- await new Promise((r) => setTimeout(r, 300));
78820
+ await delay(300);
78821
+ }
78822
+ if (launchedViaLaunchd) {
78823
+ try {
78824
+ fs5.appendFileSync(log, `[${(/* @__PURE__ */ new Date()).toISOString()}] launchd did not produce a healthy server; falling back to detached spawn
78825
+ `);
78826
+ } catch {
78827
+ }
78828
+ spawnDashboardDetached(bin, args, cwd, null);
78829
+ const fallbackStart = Date.now();
78830
+ while (Date.now() - fallbackStart < Math.min(timeoutMs, 8e3)) {
78831
+ const info3 = await liveServer();
78832
+ if (info3) return { url: info3.url };
78833
+ await delay(300);
78834
+ }
78671
78835
  }
78672
78836
  return null;
78673
78837
  }
@@ -78691,7 +78855,7 @@ function openInFileManager(dir) {
78691
78855
  else openOsTarget(dir);
78692
78856
  }
78693
78857
  async function resolveDashboardUrl(hash = "") {
78694
- const live = liveServer();
78858
+ const live = await liveServer();
78695
78859
  if (live) return { url: live.url + hash, running: true };
78696
78860
  const started = await ensureServer();
78697
78861
  if (started) return { url: started.url + hash, running: true };
@@ -78699,7 +78863,7 @@ async function resolveDashboardUrl(hash = "") {
78699
78863
  }
78700
78864
 
78701
78865
  // packages/mcp-tools/src/update.ts
78702
- import fs5 from "node:fs";
78866
+ import fs6 from "node:fs";
78703
78867
  import path6 from "node:path";
78704
78868
  import { fileURLToPath as fileURLToPath3 } from "node:url";
78705
78869
  import { spawn as spawn2 } from "node:child_process";
@@ -78756,14 +78920,14 @@ function comparePrerelease(a, b) {
78756
78920
  }
78757
78921
  function readCache() {
78758
78922
  try {
78759
- return JSON.parse(fs5.readFileSync(cacheFile(), "utf8"));
78923
+ return JSON.parse(fs6.readFileSync(cacheFile(), "utf8"));
78760
78924
  } catch {
78761
78925
  return null;
78762
78926
  }
78763
78927
  }
78764
78928
  function writeCache(c) {
78765
78929
  try {
78766
- fs5.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
78930
+ fs6.writeFileSync(cacheFile(), JSON.stringify(c), "utf8");
78767
78931
  } catch {
78768
78932
  }
78769
78933
  }
@@ -78920,16 +79084,18 @@ async function runSelfUpdate() {
78920
79084
  ok: true,
78921
79085
  from,
78922
79086
  to: latest,
78923
- 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.`
79087
+ 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.`
78924
79088
  };
78925
79089
  }
78926
79090
 
78927
79091
  // packages/mcp-tools/src/tools.ts
78928
79092
  var STATUS_LIST = APPLICATION_STATUSES.map((s) => `${s}(${APPLICATION_STATUS_LABELS[s]})`).join(", ");
79093
+ var DELETE_CONFIRMATION = "DELETE";
79094
+ var DASHBOARD_URL = "http://127.0.0.1:4319";
78929
79095
  function listInboxFiles(dir) {
78930
79096
  let names;
78931
79097
  try {
78932
- names = fs6.readdirSync(dir);
79098
+ names = fs7.readdirSync(dir);
78933
79099
  } catch {
78934
79100
  return [];
78935
79101
  }
@@ -78939,7 +79105,7 @@ function listInboxFiles(dir) {
78939
79105
  if (SKIP.test(name)) continue;
78940
79106
  const p = path7.join(dir, name);
78941
79107
  try {
78942
- if (fs6.statSync(p).isFile()) out.push({ name, path: p });
79108
+ if (fs7.statSync(p).isFile()) out.push({ name, path: p });
78943
79109
  } catch {
78944
79110
  }
78945
79111
  }
@@ -79102,9 +79268,17 @@ ${lines}`;
79102
79268
  if (upd.update_available) {
79103
79269
  text += `
79104
79270
 
79105
- \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.`;
79106
- }
79107
- return ok(text, { ...s, verify_strict: strict, update: upd });
79271
+ \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.`;
79272
+ }
79273
+ return ok(text, {
79274
+ ...s,
79275
+ verify_strict: strict,
79276
+ update: upd,
79277
+ suggested_next_action: upd.update_available ? {
79278
+ prompt: `\uC0C8 \uBC84\uC804 v${upd.latest}\uC774 \uB098\uC654\uC2B5\uB2C8\uB2E4. \uC9C0\uAE08 \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694?`,
79279
+ next_tool: "update_careermate"
79280
+ } : null
79281
+ });
79108
79282
  }
79109
79283
  },
79110
79284
  /* --------------------------------------------------------------- profile */
@@ -79257,6 +79431,31 @@ ${lines}`;
79257
79431
  }
79258
79432
  }
79259
79433
  },
79434
+ {
79435
+ name: "delete_cover_letter",
79436
+ title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC0AD\uC81C",
79437
+ 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.",
79438
+ inputSchema: {
79439
+ cover_letter_id: external_exports.string(),
79440
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
79441
+ },
79442
+ handler: (args) => {
79443
+ if (args?.confirm !== DELETE_CONFIRMATION) {
79444
+ return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
79445
+ }
79446
+ const cl = coverLetterRepo.get(args.cover_letter_id, false);
79447
+ if (!cl) return fail("\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
79448
+ const removed = coverLetterRepo.remove(cl.id);
79449
+ if (!removed) return fail("\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
79450
+ 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.`;
79451
+ return ok(user_message, {
79452
+ deleted: true,
79453
+ cover_letter_id: cl.id,
79454
+ title: cl.title,
79455
+ user_message
79456
+ });
79457
+ }
79458
+ },
79260
79459
  /* ------------------------------------------------------------------ jobs */
79261
79460
  {
79262
79461
  name: "save_job_posting",
@@ -79265,7 +79464,15 @@ ${lines}`;
79265
79464
  inputSchema: JobInputSchema.shape,
79266
79465
  handler: (args) => {
79267
79466
  const { job } = saveJobPosting(args);
79268
- return ok(`'${job.company} \xB7 ${job.position}' \uACF5\uACE0\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4. (job_id: ${job.id})`, job);
79467
+ const user_message = `'${job.company} \xB7 ${job.position}' \uACF5\uACE0\uB97C \uC800\uC7A5\uD588\uC5B4\uC694.`;
79468
+ return ok(user_message, {
79469
+ ...job,
79470
+ user_message,
79471
+ suggested_next_action: {
79472
+ prompt: "\uC774 \uACF5\uACE0\uC640 \uB0B4 \uD504\uB85C\uD544\uC744 \uBE44\uAD50\uD574 \uC801\uD569\uB3C4\uB97C \uBD84\uC11D\uD560\uAE4C\uC694?",
79473
+ next_workflow: "analyze_job"
79474
+ }
79475
+ });
79269
79476
  }
79270
79477
  },
79271
79478
  {
@@ -79276,7 +79483,7 @@ ${lines}`;
79276
79483
  readOnly: true,
79277
79484
  handler: (args) => {
79278
79485
  const job = jobRepo.get(args.job_id);
79279
- if (!job) return fail(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${args.job_id}`);
79486
+ if (!job) return fail("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
79280
79487
  return ok(`${job.company} \xB7 ${job.position}`, {
79281
79488
  job: jobWithMeta(job),
79282
79489
  fit: fitRepo.getByJob(job.id),
@@ -79297,6 +79504,35 @@ ${lines}`;
79297
79504
  return ok(`\uACF5\uACE0 ${jobs.length}\uAC74`, jobs);
79298
79505
  }
79299
79506
  },
79507
+ {
79508
+ name: "delete_job_posting",
79509
+ title: "\uCC44\uC6A9\uACF5\uACE0 \uC0AD\uC81C",
79510
+ 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.",
79511
+ inputSchema: {
79512
+ job_id: external_exports.string(),
79513
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
79514
+ },
79515
+ handler: (args) => {
79516
+ if (args?.confirm !== DELETE_CONFIRMATION) {
79517
+ return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
79518
+ }
79519
+ const job = jobRepo.get(args.job_id);
79520
+ if (!job) return fail("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
79521
+ const detachedCoverLetters = coverLetterRepo.listByJob(job.id).length;
79522
+ const removed = jobRepo.remove(job.id);
79523
+ if (!removed) return fail("\uACF5\uACE0\uB97C \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
79524
+ 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.` : "";
79525
+ 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}`;
79526
+ return ok(user_message, {
79527
+ deleted: true,
79528
+ job_id: job.id,
79529
+ company: job.company,
79530
+ position: job.position,
79531
+ detached_cover_letters: detachedCoverLetters,
79532
+ user_message
79533
+ });
79534
+ }
79535
+ },
79300
79536
  /* ----------------------------------------------------- application context */
79301
79537
  {
79302
79538
  name: "get_application_context",
@@ -79319,7 +79555,7 @@ ${lines}`;
79319
79555
  `\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}`,
79320
79556
  ctx.job ? `\uB300\uC0C1 \uACF5\uACE0: ${ctx.job.company} \xB7 ${ctx.job.position}` : "\uB300\uC0C1 \uACF5\uACE0 \uC5C6\uC74C",
79321
79557
  ctx.writing_preferences.preferred_tone ? `\uC120\uD638 \uBB38\uCCB4: ${ctx.writing_preferences.preferred_tone}` : "",
79322
- 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."
79558
+ 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."
79323
79559
  ].filter(Boolean).join("\n");
79324
79560
  return ok(summary, enriched);
79325
79561
  }
@@ -79332,7 +79568,34 @@ ${lines}`;
79332
79568
  handler: (args) => {
79333
79569
  try {
79334
79570
  const fit = saveFitAnalysis(args);
79335
- return ok(`\uC801\uD569\uB3C4 \uBD84\uC11D\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4${fit.score != null ? ` (${fit.score}\uC810)` : ""}.`, fit);
79571
+ const suggested_next_action = {
79572
+ prompt: "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?",
79573
+ options: [
79574
+ {
79575
+ label: "\uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918",
79576
+ next_workflow: "write_cover_letter"
79577
+ },
79578
+ {
79579
+ label: "\uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918",
79580
+ next_workflow: null
79581
+ }
79582
+ ]
79583
+ };
79584
+ 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}`;
79585
+ return ok(
79586
+ [
79587
+ user_message,
79588
+ "",
79589
+ "\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB2E4\uC74C \uC120\uD0DD\uC9C0\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694:",
79590
+ "1. \uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918",
79591
+ "2. \uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918"
79592
+ ].join("\n"),
79593
+ {
79594
+ ...fit,
79595
+ user_message,
79596
+ suggested_next_action
79597
+ }
79598
+ );
79336
79599
  } catch (e) {
79337
79600
  return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
79338
79601
  }
@@ -79351,11 +79614,19 @@ ${lines}`;
79351
79614
  handler: (args) => {
79352
79615
  try {
79353
79616
  const res = updateApplicationStatus(args.job_id, args.status, args.note);
79354
- return ok(
79355
- `\uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4.${res.hint ? `
79617
+ const user_message = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.` + (res.hint ? `
79356
79618
 
79357
- \u{1F4A1} ${res.hint}` : ""}`,
79358
- res
79619
+ ${res.hint}` : "");
79620
+ return ok(
79621
+ user_message,
79622
+ {
79623
+ ...res,
79624
+ user_message,
79625
+ suggested_next_action: res.hint ? {
79626
+ prompt: res.hint,
79627
+ next_workflow: "prepare_interview"
79628
+ } : null
79629
+ }
79359
79630
  );
79360
79631
  } catch (e) {
79361
79632
  return fail(e instanceof Error ? e.message : "\uBCC0\uACBD \uC2E4\uD328");
@@ -79371,7 +79642,8 @@ ${lines}`;
79371
79642
  handler: (args) => {
79372
79643
  try {
79373
79644
  const prep = saveInterviewPrep(args);
79374
- return ok(`\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC9C8\uBB38 ${prep.questions.length}\uAC1C).`, prep);
79645
+ 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.`;
79646
+ return ok(user_message, { ...prep, user_message });
79375
79647
  } catch (e) {
79376
79648
  return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
79377
79649
  }
@@ -79396,7 +79668,7 @@ ${lines}`;
79396
79668
  const dir = getExportsDir();
79397
79669
  const filePath = path7.join(dir, result2.filename);
79398
79670
  try {
79399
- fs6.writeFileSync(filePath, result2.content, "utf8");
79671
+ fs7.writeFileSync(filePath, result2.content, "utf8");
79400
79672
  } catch (e) {
79401
79673
  return fail(`\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}`);
79402
79674
  }
@@ -79558,29 +79830,37 @@ ${report.disclaimer}`, report);
79558
79830
  {
79559
79831
  name: "check_for_update",
79560
79832
  title: "\uC5C5\uB370\uC774\uD2B8 \uD655\uC778",
79561
- 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.',
79833
+ 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.',
79562
79834
  inputSchema: {},
79563
79835
  readOnly: true,
79564
79836
  handler: async () => {
79565
79837
  const u = await getUpdateStatusAsync();
79566
79838
  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);
79839
+ 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}).`;
79567
79840
  return ok(
79568
- 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}).`,
79569
- u
79841
+ user_message,
79842
+ {
79843
+ ...u,
79844
+ user_message,
79845
+ suggested_next_action: u.update_available ? {
79846
+ prompt: "\uC9C0\uAE08 CareerMate\uB97C \uC5C5\uB370\uC774\uD2B8\uD560\uAE4C\uC694?",
79847
+ next_tool: "update_careermate"
79848
+ } : null
79849
+ }
79570
79850
  );
79571
79851
  }
79572
79852
  },
79573
79853
  {
79574
79854
  name: "update_careermate",
79575
79855
  title: "CareerMate \uC5C5\uB370\uC774\uD2B8",
79576
- 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.',
79856
+ 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.',
79577
79857
  inputSchema: {},
79578
79858
  handler: async () => {
79579
79859
  const r = await runSelfUpdate();
79580
79860
  if (!r.ok) return fail(`${r.message}${r.manual ? `
79581
79861
 
79582
79862
  \uC9C1\uC811 \uC2E4\uD589: ${r.manual}` : ""}`);
79583
- return ok(r.message, r);
79863
+ return ok(r.message, { ...r, user_message: r.message });
79584
79864
  }
79585
79865
  }
79586
79866
  ];
@@ -79601,7 +79881,7 @@ async function main() {
79601
79881
  const nodeOk = checkNode();
79602
79882
  const dir = getDataDir();
79603
79883
  try {
79604
- fs7.accessSync(dir, fs7.constants.W_OK);
79884
+ fs8.accessSync(dir, fs8.constants.W_OK);
79605
79885
  ok2(`\uB370\uC774\uD130 \uD3F4\uB354 \uC4F0\uAE30 \uAC00\uB2A5: ${dir}`);
79606
79886
  } catch {
79607
79887
  warn2(`\uB370\uC774\uD130 \uD3F4\uB354\uC5D0 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dir}`);