careermate 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/AGENTS.md +13 -10
  2. package/INSTALL.md +16 -15
  3. package/README.md +24 -24
  4. package/THIRD_PARTY_NOTICES.md +2 -3
  5. package/bin/careermate.mjs +8 -1
  6. package/dist/career-os/README.md +12 -6
  7. package/dist/career-os/eop/career-description.md +139 -0
  8. package/dist/career-os/eop/cover-letter.md +10 -5
  9. package/dist/career-os/eop/fit-analysis.md +16 -6
  10. package/dist/career-os/eop/interview-prep.md +209 -21
  11. package/dist/career-os/eop/job-analysis.md +6 -4
  12. package/dist/career-os/eop/profile-extraction.md +5 -4
  13. package/dist/career-os/knowledge/cover-letter.md +5 -4
  14. package/dist/career-os/knowledge/fit-matching.md +8 -8
  15. package/dist/career-os/knowledge/human-writing.md +2 -0
  16. package/dist/career-os/knowledge/interview-behavioral.md +76 -8
  17. package/dist/career-os/knowledge/interview-technical.md +106 -4
  18. package/dist/career-os/knowledge/linkedin-profile.md +1 -1
  19. package/dist/career-os/knowledge/networking-referrals.md +1 -1
  20. package/dist/career-os/knowledge/offer-evaluation-decision.md +10 -10
  21. package/dist/career-os/knowledge/portfolio.md +3 -3
  22. package/dist/career-os/knowledge/verifiers/ats-compat.md +8 -4
  23. package/dist/career-os/knowledge/verifiers/human-voice.md +4 -2
  24. package/dist/career-os/knowledge/verifiers/recency-staleness.md +1 -1
  25. package/dist/career-os/knowledge/verifiers/responsiveness-on-target.md +2 -2
  26. package/dist/career-os/knowledge/verifiers/truthfulness.md +1 -1
  27. package/dist/doctor.mjs +692 -71
  28. package/dist/mcp.mjs +710 -71
  29. package/dist/migrate.mjs +52 -1
  30. package/dist/public/app.js +42 -16
  31. package/dist/public/i18n/en/applications.js +23 -0
  32. package/dist/public/i18n/en/common.js +22 -0
  33. package/dist/public/i18n/en/documents.js +133 -0
  34. package/dist/public/i18n/en/home.js +75 -0
  35. package/dist/public/i18n/en/interview.js +69 -0
  36. package/dist/public/i18n/en/jobs.js +152 -0
  37. package/dist/public/i18n/en/nav.js +18 -0
  38. package/dist/public/i18n/en/profile.js +201 -0
  39. package/dist/public/i18n/en/settings.js +127 -0
  40. package/dist/public/i18n/en/status.js +22 -0
  41. package/dist/public/i18n/en.js +17 -0
  42. package/dist/public/i18n/ko/applications.js +23 -0
  43. package/dist/public/i18n/ko/common.js +22 -0
  44. package/dist/public/i18n/ko/documents.js +133 -0
  45. package/dist/public/i18n/ko/home.js +63 -0
  46. package/dist/public/i18n/ko/interview.js +69 -0
  47. package/dist/public/i18n/ko/jobs.js +152 -0
  48. package/dist/public/i18n/ko/nav.js +18 -0
  49. package/dist/public/i18n/ko/profile.js +201 -0
  50. package/dist/public/i18n/ko/settings.js +127 -0
  51. package/dist/public/i18n/ko/status.js +23 -0
  52. package/dist/public/i18n/ko.js +18 -0
  53. package/dist/public/i18n.js +55 -0
  54. package/dist/public/index.html +1 -0
  55. package/dist/public/lib.js +24 -17
  56. package/dist/public/pages/applications.js +17 -21
  57. package/dist/public/pages/documents.js +153 -107
  58. package/dist/public/pages/home.js +88 -53
  59. package/dist/public/pages/interview.js +105 -51
  60. package/dist/public/pages/jobs.js +330 -106
  61. package/dist/public/pages/profile.js +157 -152
  62. package/dist/public/pages/settings.js +154 -102
  63. package/dist/public/styles.css +28 -1
  64. package/dist/web.mjs +495 -75
  65. package/docs/FAQ.md +23 -3
  66. package/docs/MCP_TOOLS.md +22 -13
  67. package/docs/START_WORKFLOW.md +3 -3
  68. package/docs/SUPPORTED_AI_APPS.md +13 -0
  69. package/docs/WORKFLOWS.md +77 -20
  70. package/docs/career-os/README.md +12 -6
  71. package/docs/career-os/eop/career-description.md +139 -0
  72. package/docs/career-os/eop/cover-letter.md +10 -5
  73. package/docs/career-os/eop/fit-analysis.md +16 -6
  74. package/docs/career-os/eop/interview-prep.md +209 -21
  75. package/docs/career-os/eop/job-analysis.md +6 -4
  76. package/docs/career-os/eop/profile-extraction.md +5 -4
  77. package/docs/career-os/knowledge/cover-letter.md +5 -4
  78. package/docs/career-os/knowledge/fit-matching.md +8 -8
  79. package/docs/career-os/knowledge/human-writing.md +2 -0
  80. package/docs/career-os/knowledge/interview-behavioral.md +76 -8
  81. package/docs/career-os/knowledge/interview-technical.md +106 -4
  82. package/docs/career-os/knowledge/linkedin-profile.md +1 -1
  83. package/docs/career-os/knowledge/networking-referrals.md +1 -1
  84. package/docs/career-os/knowledge/offer-evaluation-decision.md +10 -10
  85. package/docs/career-os/knowledge/portfolio.md +3 -3
  86. package/docs/career-os/knowledge/verifiers/ats-compat.md +8 -4
  87. package/docs/career-os/knowledge/verifiers/human-voice.md +4 -2
  88. package/docs/career-os/knowledge/verifiers/recency-staleness.md +1 -1
  89. package/docs/career-os/knowledge/verifiers/responsiveness-on-target.md +2 -2
  90. package/docs/career-os/knowledge/verifiers/truthfulness.md +1 -1
  91. package/package.json +2 -2
  92. package/packages/core/src/onboarding.ts +2 -1
  93. package/packages/core/src/services.ts +134 -17
  94. package/packages/core/src/summary.ts +59 -1
  95. package/packages/core/src/verify/lint.ts +4 -0
  96. package/packages/db/src/index.ts +1 -0
  97. package/packages/db/src/repositories.ts +0 -0
  98. package/packages/db/src/schema.ts +17 -0
  99. package/packages/exporters/src/index.ts +28 -17
  100. package/packages/knowledge/src/index.ts +55 -9
  101. package/packages/mcp-tools/src/tools.ts +250 -24
  102. package/packages/mcp-tools/src/update.ts +30 -15
  103. package/packages/parsers/src/documents.ts +12 -1
  104. package/packages/prompts/src/humanize.ts +46 -0
  105. package/packages/prompts/src/index.ts +5 -1
  106. package/packages/prompts/src/install.ts +4 -4
  107. package/packages/prompts/src/onboarding.ts +4 -4
  108. package/packages/prompts/src/system.ts +9 -7
  109. package/packages/shared/src/enums.ts +14 -4
  110. package/packages/shared/src/schemas.ts +41 -0
  111. package/packages/workflows/src/definitions.ts +45 -8
  112. package/scripts/init.ts +284 -13
  113. package/dist/site/analytics.js +0 -97
  114. package/dist/site/favicon.svg +0 -1
  115. package/dist/site/fonts/PretendardVariable.woff2 +0 -0
  116. package/dist/site/icon-180.png +0 -0
  117. package/dist/site/icon-512.png +0 -0
  118. package/dist/site/index.html +0 -465
  119. package/dist/site/landing.js +0 -163
  120. package/dist/site/llms-install.txt +0 -218
  121. package/dist/site/robots.txt +0 -4
  122. package/dist/site/sitemap.xml +0 -21
  123. package/dist/site/start.html +0 -356
  124. package/dist/site/style.css +0 -665
  125. package/dist/site/vercel.json +0 -20
package/dist/mcp.mjs CHANGED
@@ -92431,6 +92431,22 @@ var MIGRATIONS = [
92431
92431
 
92432
92432
  COMMIT;
92433
92433
  PRAGMA foreign_keys=ON;
92434
+ `,
92435
+ // v3 — per-job timeline entries. Document/cover-letter refs live in payload
92436
+ // snapshots instead of FKs so deleting a document does not erase the history.
92437
+ `
92438
+ CREATE TABLE IF NOT EXISTS application_timeline_events (
92439
+ id TEXT PRIMARY KEY,
92440
+ job_id TEXT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
92441
+ type TEXT NOT NULL,
92442
+ title TEXT NOT NULL,
92443
+ summary TEXT,
92444
+ payload TEXT NOT NULL DEFAULT '{}',
92445
+ occurred_at TEXT NOT NULL,
92446
+ created_at TEXT NOT NULL
92447
+ );
92448
+
92449
+ CREATE INDEX IF NOT EXISTS idx_timeline_job ON application_timeline_events(job_id, occurred_at ASC);
92434
92450
  `
92435
92451
  ];
92436
92452
  function migrate(db) {
@@ -92462,6 +92478,12 @@ function getDb() {
92462
92478
  _db = db;
92463
92479
  return db;
92464
92480
  }
92481
+ function closeDb() {
92482
+ if (_db) {
92483
+ _db.close();
92484
+ _db = null;
92485
+ }
92486
+ }
92465
92487
  var _inTx = false;
92466
92488
  function tx(fn) {
92467
92489
  if (_inTx) return fn();
@@ -92591,6 +92613,12 @@ function assertStatusTransition(from, to) {
92591
92613
  }
92592
92614
  }
92593
92615
  var DOCUMENT_KINDS = ["resume", "career_description", "portfolio", "other"];
92616
+ var DOCUMENT_KIND_LABELS = {
92617
+ resume: "\uC774\uB825\uC11C",
92618
+ career_description: "\uACBD\uB825\uAE30\uC220\uC11C",
92619
+ portfolio: "\uD3EC\uD2B8\uD3F4\uB9AC\uC624",
92620
+ other: "\uAE30\uD0C0 \uBB38\uC11C"
92621
+ };
92594
92622
  var CONTENT_SOURCES = ["manual", "upload", "ai", "edit"];
92595
92623
  var ACTIVITY_TYPES = [
92596
92624
  "profile_updated",
@@ -92603,6 +92631,13 @@ var ACTIVITY_TYPES = [
92603
92631
  "interview_prep_saved",
92604
92632
  "document_exported"
92605
92633
  ];
92634
+ var APPLICATION_TIMELINE_TYPES = [
92635
+ "job_saved",
92636
+ "fit_analysis_saved",
92637
+ "cover_letter_version_saved",
92638
+ "application_status_changed",
92639
+ "interview_prep_saved"
92640
+ ];
92606
92641
  var ENTITY_TYPES = [
92607
92642
  "profile",
92608
92643
  "experience",
@@ -92869,11 +92904,19 @@ var ApplicationInputSchema = external_exports.object({
92869
92904
  applied_at: optLine,
92870
92905
  notes: optNote
92871
92906
  });
92907
+ var ApplicationSubmissionSchema = external_exports.object({
92908
+ submitted_at: optLine.describe("\uC2E4\uC81C \uC81C\uCD9C\uC77C YYYY-MM-DD"),
92909
+ channel: optLine.describe("\uC81C\uCD9C \uCC44\uB110(\uC6D0\uD2F0\uB4DC/\uC0AC\uB78C\uC778/\uD68C\uC0AC \uCC44\uC6A9\uD398\uC774\uC9C0 \uB4F1)"),
92910
+ cover_letter_id: optLine,
92911
+ cover_letter_version_id: optLine,
92912
+ document_ids: strList.optional().describe("\uC81C\uCD9C\uD55C \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uBB38\uC11C ID \uBAA9\uB85D")
92913
+ });
92872
92914
  var ApplicationStatusUpdateSchema = external_exports.object({
92873
92915
  job_id: optLine,
92874
92916
  application_id: optLine,
92875
92917
  status: external_exports.enum(APPLICATION_STATUSES),
92876
- note: optNote.describe("\uC0C1\uD0DC \uBCC0\uACBD \uC0AC\uC720/\uBA54\uBAA8")
92918
+ note: optNote.describe("\uC0C1\uD0DC \uBCC0\uACBD \uC0AC\uC720/\uBA54\uBAA8"),
92919
+ submission: ApplicationSubmissionSchema.optional().describe("\uC9C0\uC6D0 \uC644\uB8CC \uAE30\uB85D\uC5D0 \uB0A8\uAE38 \uC81C\uCD9C \uC790\uB8CC \uC2A4\uB0C5\uC0F7")
92877
92920
  });
92878
92921
  var ApplicationRecordSchema = external_exports.object({
92879
92922
  ...baseRecord,
@@ -92891,8 +92934,10 @@ var StarGuideSchema = external_exports.object({
92891
92934
  action: optNote,
92892
92935
  result: optNote
92893
92936
  });
92937
+ var INTERVIEW_CATEGORIES = ["technical", "behavioral"];
92894
92938
  var InterviewQuestionSchema = external_exports.object({
92895
92939
  question: external_exports.string().max(MAX_NOTE),
92940
+ category: external_exports.enum(INTERVIEW_CATEGORIES).optional().describe("\uC9C8\uBB38 \uAC08\uB798: 'technical'(\uAE30\uC220\xB7\uC9C1\uBB34 1\uCC28) | 'behavioral'(\uC778\uC131\xB7\uCEEC\uCC98\uD54F\xB7\uC784\uC6D0 2\uCC28, \uC790\uAE30\uC18C\uAC1C\uC11C \uAE30\uBC18). \uC0DD\uB7B5 \uC2DC \uAE30\uC220\uB85C \uAC04\uC8FC."),
92896
92941
  intent: external_exports.string().max(MAX_NOTE).optional().describe("\uC9C8\uBB38 \uC758\uB3C4"),
92897
92942
  followups: strList.optional().describe("\uC608\uC0C1 \uAF2C\uB9AC \uC9C8\uBB38"),
92898
92943
  answer_outline: optBody.describe("\uB2F5\uBCC0 \uAC00\uC774\uB4DC/\uD575\uC2EC \uD3EC\uC778\uD2B8")
@@ -92920,6 +92965,24 @@ var ActivityRecordSchema = external_exports.object({
92920
92965
  summary: external_exports.string(),
92921
92966
  created_at: isoDateTime
92922
92967
  });
92968
+ var ApplicationTimelineInputSchema = external_exports.object({
92969
+ job_id: reqLine,
92970
+ type: external_exports.enum(APPLICATION_TIMELINE_TYPES),
92971
+ title: reqLine,
92972
+ summary: optNote,
92973
+ payload: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
92974
+ occurred_at: optLine
92975
+ });
92976
+ var ApplicationTimelineRecordSchema = external_exports.object({
92977
+ id,
92978
+ job_id: external_exports.string(),
92979
+ type: external_exports.enum(APPLICATION_TIMELINE_TYPES),
92980
+ title: external_exports.string(),
92981
+ summary: external_exports.string().nullable(),
92982
+ payload: external_exports.record(external_exports.string(), external_exports.unknown()),
92983
+ occurred_at: isoDateTime,
92984
+ created_at: isoDateTime
92985
+ });
92923
92986
  var OnboardingStatusSchema = external_exports.object({
92924
92987
  completed: external_exports.boolean(),
92925
92988
  has_profile: external_exports.boolean(),
@@ -92956,7 +93019,7 @@ var ApplicationContextSchema = external_exports.object({
92956
93019
  });
92957
93020
 
92958
93021
  // packages/shared/src/build-info.ts
92959
- var APP_VERSION = true ? "0.0.5" : "0.0.0-dev";
93022
+ var APP_VERSION = true ? "0.0.7" : "0.0.0-dev";
92960
93023
  var BUNDLED = true;
92961
93024
 
92962
93025
  // packages/shared/src/index.ts
@@ -93050,7 +93113,7 @@ function mapExperience(r) {
93050
93113
  };
93051
93114
  }
93052
93115
  function expKey(company, role, start) {
93053
- return [norm(company), norm(role), norm(start)].join("\0");
93116
+ return [norm(company), norm(role), norm(start)].join("");
93054
93117
  }
93055
93118
  var experienceRepo = {
93056
93119
  list() {
@@ -93561,6 +93624,7 @@ var jobRepo = {
93561
93624
  db.prepare(`DELETE FROM fit_analyses WHERE job_id=?`).run(id2);
93562
93625
  db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id2);
93563
93626
  db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id2);
93627
+ db.prepare(`DELETE FROM application_timeline_events WHERE job_id=?`).run(id2);
93564
93628
  return db.prepare(`DELETE FROM jobs WHERE id=?`).run(id2).changes > 0;
93565
93629
  });
93566
93630
  }
@@ -93673,7 +93737,7 @@ var applicationRepo = {
93673
93737
  const m = { ...app, ...input };
93674
93738
  getDb().prepare(
93675
93739
  `UPDATE applications SET status=?,resume_id=?,cover_letter_id=?,applied_at=?,notes=?,updated_at=? WHERE id=?`
93676
- ).run(m.status, m.resume_id, m.cover_letter_id, m.applied_at, m.notes, now(), app.id);
93740
+ ).run(m.status, m.resume_id ?? null, m.cover_letter_id ?? null, m.applied_at ?? null, m.notes ?? null, now(), app.id);
93677
93741
  return this.get(app.id);
93678
93742
  },
93679
93743
  setStatus(jobId, status, note) {
@@ -93754,6 +93818,48 @@ var activityRepo = {
93754
93818
  return getDb().prepare(`SELECT * FROM activities ORDER BY created_at DESC LIMIT ?`).all(limit);
93755
93819
  }
93756
93820
  };
93821
+ function mapTimelineEvent(r) {
93822
+ return {
93823
+ id: r.id,
93824
+ job_id: r.job_id,
93825
+ type: r.type,
93826
+ title: r.title,
93827
+ summary: r.summary,
93828
+ payload: fromJson(r.payload, {}),
93829
+ occurred_at: r.occurred_at,
93830
+ created_at: r.created_at
93831
+ };
93832
+ }
93833
+ var timelineRepo = {
93834
+ add(input) {
93835
+ const ts = now();
93836
+ const id2 = newId("tl_");
93837
+ getDb().prepare(
93838
+ `INSERT INTO application_timeline_events (id,job_id,type,title,summary,payload,occurred_at,created_at)
93839
+ VALUES (?,?,?,?,?,?,?,?)`
93840
+ ).run(
93841
+ id2,
93842
+ input.job_id,
93843
+ input.type,
93844
+ input.title,
93845
+ input.summary ?? null,
93846
+ toJson(input.payload ?? {}),
93847
+ input.occurred_at ?? ts,
93848
+ ts
93849
+ );
93850
+ return this.get(id2);
93851
+ },
93852
+ get(id2) {
93853
+ const r = getDb().prepare(`SELECT * FROM application_timeline_events WHERE id=?`).get(id2);
93854
+ return r ? mapTimelineEvent(r) : null;
93855
+ },
93856
+ listByJob(jobId) {
93857
+ return getDb().prepare(`SELECT * FROM application_timeline_events WHERE job_id=? ORDER BY occurred_at ASC, created_at ASC`).all(jobId).map(mapTimelineEvent);
93858
+ },
93859
+ remove(id2) {
93860
+ return getDb().prepare(`DELETE FROM application_timeline_events WHERE id=?`).run(id2).changes > 0;
93861
+ }
93862
+ };
93757
93863
 
93758
93864
  // packages/mcp-tools/src/tools.ts
93759
93865
  import fs7 from "node:fs";
@@ -93780,12 +93886,13 @@ function profileCompleteness(p) {
93780
93886
  function getOnboardingStatus() {
93781
93887
  const profile = profileRepo.get();
93782
93888
  const resumes = documentRepo.list("resume");
93889
+ const careerDescriptions = documentRepo.list("career_description");
93783
93890
  const coverLetters = coverLetterRepo.list();
93784
93891
  const experiences = experienceRepo.list();
93785
93892
  const skills = skillRepo.list();
93786
93893
  const jobs = jobRepo.list();
93787
93894
  const has_profile = !!(profile && profile.name);
93788
- const has_resume = resumes.length > 0;
93895
+ const has_resume = resumes.length > 0 || careerDescriptions.length > 0;
93789
93896
  const has_cover_letter = coverLetters.length > 0;
93790
93897
  const has_experience = experiences.length > 0;
93791
93898
  const has_skills = skills.length > 0;
@@ -94198,6 +94305,29 @@ function batchNames(names) {
94198
94305
  if (!shown) return "";
94199
94306
  return `: ${shown}${names.length > 8 ? " \uC678" : ""}`;
94200
94307
  }
94308
+ function coverLetterTimelineRef(coverLetter, version3, requestedVersionId) {
94309
+ const resolvedVersion = version3 ?? coverLetter.versions?.find((v) => v.id === requestedVersionId || v.id === coverLetter.current_version_id) ?? null;
94310
+ return {
94311
+ kind: "cover_letter",
94312
+ id: coverLetter.id,
94313
+ title: coverLetter.title,
94314
+ version_id: resolvedVersion?.id ?? requestedVersionId ?? coverLetter.current_version_id,
94315
+ version_no: resolvedVersion?.version_no ?? coverLetter.version_count
94316
+ };
94317
+ }
94318
+ function documentTimelineRef(doc) {
94319
+ return {
94320
+ kind: "document",
94321
+ id: doc.id,
94322
+ title: doc.title,
94323
+ document_kind: doc.kind
94324
+ };
94325
+ }
94326
+ function submittedAtToOccurrence(value) {
94327
+ if (!value) return void 0;
94328
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return `${value}T00:00:00.000Z`;
94329
+ return void 0;
94330
+ }
94201
94331
  function addExperiences(inputs) {
94202
94332
  const res = experienceRepo.addMany(inputs);
94203
94333
  activityRepo.log(
@@ -94241,6 +94371,17 @@ function saveFitAnalysis(input) {
94241
94371
  const fit = fitRepo.save(input);
94242
94372
  const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
94243
94373
  activityRepo.log("fit_analysis_saved", `${job.company} \xB7 ${job.position} \uC801\uD569\uB3C4 \uBD84\uC11D\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4${scoreText}.`, "fit_analysis", fit.id);
94374
+ timelineRepo.add({
94375
+ job_id: job.id,
94376
+ type: "fit_analysis_saved",
94377
+ title: "\uC801\uD569\uB3C4 \uBD84\uC11D",
94378
+ summary: fit.score != null ? `\uC885\uD569 ${fit.score}\uC810` : fit.summary ?? void 0,
94379
+ payload: {
94380
+ fit_id: fit.id,
94381
+ score: fit.score
94382
+ },
94383
+ occurred_at: fit.updated_at
94384
+ });
94244
94385
  return fit;
94245
94386
  }
94246
94387
  function saveCoverLetterVersion(input) {
@@ -94274,18 +94415,67 @@ function saveCoverLetterVersion(input) {
94274
94415
  "cover_letter",
94275
94416
  coverLetter.id
94276
94417
  );
94418
+ if (coverLetter.job_id) {
94419
+ timelineRepo.add({
94420
+ job_id: coverLetter.job_id,
94421
+ type: "cover_letter_version_saved",
94422
+ title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131",
94423
+ summary: `${coverLetter.title} v${version3.version_no}`,
94424
+ payload: {
94425
+ cover_letter: coverLetterTimelineRef(coverLetter, version3)
94426
+ },
94427
+ occurred_at: version3.created_at
94428
+ });
94429
+ }
94277
94430
  return { coverLetter, version: version3, verification, forced };
94278
94431
  }
94279
- function updateApplicationStatus(jobId, status, note) {
94432
+ function updateApplicationStatus(jobId, status, note, submission) {
94280
94433
  const job = jobRepo.get(jobId);
94281
94434
  if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
94282
- const application = applicationRepo.setStatus(jobId, status, note);
94283
- activityRepo.log(
94284
- "application_status_changed",
94285
- `${job.company} \xB7 ${job.position} \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[status]}'(\uC73C)\uB85C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4.`,
94286
- "application",
94287
- application.id
94288
- );
94435
+ const before2 = applicationRepo.getByJob(jobId);
94436
+ const application = tx(() => {
94437
+ let application2 = applicationRepo.setStatus(jobId, status, note);
94438
+ const submittedDocs = (submission?.document_ids ?? []).map((docId) => documentRepo.get(docId)).filter((doc) => !!doc);
94439
+ const submittedCoverLetter = submission?.cover_letter_id ? coverLetterRepo.get(submission.cover_letter_id, true) : null;
94440
+ if (status === "applied" && submission) {
94441
+ const resumeLike = submittedDocs.find((doc) => doc.kind === "resume" || doc.kind === "career_description") ?? submittedDocs[0] ?? null;
94442
+ application2 = applicationRepo.upsert({
94443
+ job_id: jobId,
94444
+ resume_id: resumeLike?.id ?? application2.resume_id ?? void 0,
94445
+ cover_letter_id: submittedCoverLetter?.id ?? application2.cover_letter_id ?? void 0,
94446
+ applied_at: submission.submitted_at ?? application2.applied_at ?? void 0
94447
+ });
94448
+ }
94449
+ activityRepo.log(
94450
+ "application_status_changed",
94451
+ `${job.company} \xB7 ${job.position} \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[status]}'(\uC73C)\uB85C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4.`,
94452
+ "application",
94453
+ application2.id
94454
+ );
94455
+ const submittedAt = submission?.submitted_at?.trim();
94456
+ const channel = submission?.channel?.trim();
94457
+ const submissionPayload = submission && (submittedAt || channel || submittedCoverLetter || submittedDocs.length > 0) ? {
94458
+ submitted_at: submittedAt || null,
94459
+ channel: channel || null,
94460
+ cover_letter: submittedCoverLetter ? coverLetterTimelineRef(submittedCoverLetter, null, submission.cover_letter_version_id) : null,
94461
+ documents: submittedDocs.map(documentTimelineRef)
94462
+ } : null;
94463
+ timelineRepo.add({
94464
+ job_id: job.id,
94465
+ type: "application_status_changed",
94466
+ title: APPLICATION_STATUS_LABELS[status],
94467
+ summary: channel && status === "applied" ? `${channel}\uB85C \uC81C\uCD9C` : note ?? void 0,
94468
+ payload: {
94469
+ from_status: before2?.status ?? null,
94470
+ status,
94471
+ status_label: APPLICATION_STATUS_LABELS[status],
94472
+ note: note ?? null,
94473
+ submission: submissionPayload
94474
+ },
94475
+ occurred_at: submittedAtToOccurrence(submittedAt)
94476
+ });
94477
+ return application2;
94478
+ });
94289
94479
  const interview_unlocked = INTERVIEW_UNLOCK_STATUSES.includes(status);
94290
94480
  const hasPrep = !!interviewRepo.getByJob(jobId);
94291
94481
  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;
@@ -94294,15 +94484,19 @@ function updateApplicationStatus(jobId, status, note) {
94294
94484
  function saveInterviewPrep(input) {
94295
94485
  const job = jobRepo.get(input.job_id);
94296
94486
  if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
94297
- const app = applicationRepo.getByJob(job.id);
94298
- if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
94299
- const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
94300
- throw conflict(
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.`
94302
- );
94303
- }
94304
94487
  const prep = interviewRepo.save(input);
94305
94488
  activityRepo.log("interview_prep_saved", `${job.company} \xB7 ${job.position} \uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4.`, "interview_prep", prep.id);
94489
+ timelineRepo.add({
94490
+ job_id: job.id,
94491
+ type: "interview_prep_saved",
94492
+ title: "\uBA74\uC811 \uC900\uBE44",
94493
+ summary: prep.questions.length > 0 ? `\uC608\uC0C1 \uC9C8\uBB38 ${prep.questions.length}\uAC1C` : prep.notes ?? void 0,
94494
+ payload: {
94495
+ interview_prep_id: prep.id,
94496
+ question_count: prep.questions.length
94497
+ },
94498
+ occurred_at: prep.updated_at
94499
+ });
94306
94500
  return prep;
94307
94501
  }
94308
94502
 
@@ -94323,6 +94517,8 @@ var HUMANIZE_WRITING_GUIDE = `# \uC0AC\uB78C\uC774 \uC4F4 \uAC83\uCC98\uB7FC: \u
94323
94517
  \uBAA9\uD45C: \uC0AC\uC2E4\uACFC \uACBD\uD5D8\uC740 100% \uADF8\uB300\uB85C \uB450\uBA74\uC11C, "AI\uAC00 \uC4F4 \uD2F0"\uAC00 \uB098\uB294 \uBB38\uC7A5 \uC2B5\uAD00\uB9CC \uC81C\uAC70\uD55C\uB2E4.
94324
94518
  \uAC80\uC5F4\uAE30\uB97C \uC18D\uC774\uB294 \uAC8C \uC544\uB2C8\uB77C, \uCC44\uC6A9 \uB2F4\uB2F9\uC790\uAC00 \uC77D\uAE30\uC5D0 \uC790\uC5F0\uC2A4\uB7FD\uACE0 \uC0AC\uB78C \uB0C4\uC0C8 \uB098\uB294 \uAE00\uC744 \uC4F0\uB294 \uAC83\uC774 \uBAA9\uC801\uC774\uB2E4.
94325
94519
 
94520
+ > **\uC5B8\uC5B4 \uBC94\uC704:** \uC544\uB798 AI-tell \uBAA9\uB85D\uC740 \uD55C\uAD6D\uC5B4 \uAE00 \uAE30\uC900\uC774\uB2E4. \uC601\uC5B4 \uB4F1 \uB2E4\uB978 \uC5B8\uC5B4\uB85C \uC4F8 \uB54C\uB294 \uC774 \uBAA9\uB85D\uC744 \uADF8\uB300\uB85C \uC801\uC6A9\uD558\uC9C0 \uB9D0\uACE0, **\uAC19\uC740 \uC6D0\uB9AC**(\uD074\uB9AC\uC170\xB7\uAE30\uACC4\uC801 \uBCD1\uB82C\xB7\uACFC\uC7A5 \uC218\uC2DD\xB7\uACF5\uD5C8\uD55C \uC77C\uBC18\uB860 \uC81C\uAC70, \uAD6C\uCCB4\uC131, \uC0AC\uC2E4\xB7\uC218\uCE58 \uBCF4\uC874)\uB97C \uADF8 \uC5B8\uC5B4\uC758 \uC790\uC5F0\uC2A4\uB7EC\uC6B4 \uAD00\uC2B5\uC73C\uB85C \uC801\uC6A9\uD55C\uB2E4. \uACB0\uACFC\uBB3C\uC758 \uC5B8\uC5B4\uB294 \uB300\uC0C1 \uACF5\uACE0\xB7\uC2DC\uC7A5\uC744 \uB530\uB978\uB2E4.
94521
+
94326
94522
  ## \uC808\uB300 \uC6D0\uCE59
94327
94523
  - \uC800\uC7A5\uB41C \uC2E4\uC81C \uACBD\uD5D8\xB7\uC218\uCE58\xB7\uD68C\uC0AC\uBA85\xB7\uACE0\uC720\uBA85\uC0AC\xB7\uC9C1\uC811 \uC778\uC6A9\uC740 \uC808\uB300 \uBC14\uAFB8\uAC70\uB098 \uC9C0\uC5B4\uB0B4\uC9C0 \uC54A\uB294\uB2E4. \uBB38\uC7A5 \uACB0\uB9CC \uB2E4\uB4EC\uB294\uB2E4.
94328
94524
  - \uBE48\uCE78\uC774\uB098 \uD655\uC778\uC774 \uD544\uC694\uD55C \uBD80\uBD84\uC740 \uB9E4\uB044\uB7FD\uAC8C \uCC44\uC6CC \uB123\uC9C0 \uB9D0\uACE0 \uADF8\uB300\uB85C \uD45C\uC2DC\uD574 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3B\uB294\uB2E4.
@@ -94364,6 +94560,33 @@ var HUMANIZE_WRITING_GUIDE = `# \uC0AC\uB78C\uC774 \uC4F4 \uAC83\uCC98\uB7FC: \u
94364
94560
  - \uC18C\uB9AC \uB0B4\uC5B4 \uC77D\uC5C8\uC744 \uB54C \uC5B4\uC0C9\uD558\uAC70\uB098 "\uC0AC\uB78C\uC774 \uC774\uB807\uAC8C \uB9D0 \uC548 \uD558\uB294\uB370" \uC2F6\uC740 \uACF3\uC744 \uB2E4\uB4EC\uB294\uB2E4.
94365
94561
  - \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uAC00 \uC6D0\uBCF8\uACFC 100% \uC77C\uCE58\uD558\uB294\uAC00?
94366
94562
  - \uC0AC\uC6A9\uC790\uC758 \uD1A4\uACFC \uAC15\uC870 \uD3EC\uC778\uD2B8\uAC00 \uC0B4\uC544 \uC788\uB294\uAC00?`;
94563
+ var STRUCTURED_DOC_WRITING_GUIDE = `# \uAD6C\uC870\uD615 \uBB38\uC11C(\uACBD\uB825\uAE30\uC220\uC11C\xB7\uC774\uB825\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624) \uBCF4\uC815
94564
+
94565
+ \uACBD\uB825\uAE30\uC220\uC11C\xB7\uC774\uB825\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624\uB294 \uCC44\uC6A9 \uB2F4\uB2F9\uC790\uAC00 \uCC98\uC74C\uBD80\uD130 \uB05D\uAE4C\uC9C0 \uC77D\uB294 \uAE00\uC774 \uC544\uB2C8\uB77C \uBA87 \uCD08 \uC548\uC5D0 "\uC2A4\uCE94"\uD558\uB294 \uBB38\uC11C\uB2E4. \uBD88\uB9BF, \uBC88\uD638 \uB9E4\uAE34 \uC131\uACFC, \uC9C1\uBB34\xB7\uAE30\uAC04\xB7\uC5ED\uD560\uC744 \uAD6C\uBD84\uD55C \uC139\uC158 \uAD6C\uC870\uB294 AI\uAC00 \uC4F4 \uD2F0\uAC00 \uC544\uB2C8\uB77C \uC774 \uBB38\uC11C\uC758 \uC815\uC11D\uC774\uB2E4. \uC790\uAE30\uC18C\uAC1C\uC11C\uCC98\uB7FC \uC904\uAE00(\uC0B0\uBB38)\uB85C \uD480\uC9C0 \uB9D0\uACE0 \uAD6C\uC870\uB97C \uBCF4\uC874\uD55C\uB2E4.
94566
+
94567
+ ## \uC704 8\uAC1C AI-tell \uC911 \uAD6C\uC870\uD615\uC5D0\uC11C \uB2E4\uB974\uAC8C \uC801\uC6A9\uD558\uB294 \uAC83
94568
+ - 3\uBC88(\uAE30\uACC4\uC801 \uBCD1\uB82C)\xB78\uBC88(\uD615\uC2DD \uACFC\uC6A9)\uC740 \uC0B0\uBB38 \uBB38\uC7A5\uC5D0\uB9CC \uC801\uC6A9\uD55C\uB2E4. \uC131\uACFC\uB97C \uB098\uC5F4\uD558\uB294 \uBD88\uB9BF\xB7\uBC88\uD638 \uBAA9\uB85D \uAD6C\uC870 \uC790\uCCB4\uC5D0\uB294 \uC801\uC6A9\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uBD88\uB9BF\uB85C \uB04A\uC5B4 \uC4F0\uB294 \uAC83\uC740 \uAC00\uB3C5\uC131\uC744 \uC704\uD55C \uC815\uC0C1 \uAD6C\uC870\uB2E4.
94569
+ - \uB2E8 \uC758\uBBF8 \uC5C6\uB294 "\uCCAB\uC9F8\xB7\uB458\uC9F8\xB7\uC14B\uC9F8" \uC11C\uC218 \uB098\uC5F4, \uBAA8\uB4E0 \uBD88\uB9BF\uC744 \uB611\uAC19\uC740 \uACE8\uACA9("~\uC744 \uD1B5\uD574 ~\uC744 \uD568")\uC73C\uB85C \uCC0D\uC5B4\uB0B4\uB294 \uAC83, \uACFC\uD55C \uAD75\uAC8C\xB7\uC774\uBAA8\uC9C0\xB7\uAC00\uC6B4\uB383\uC810 \uB5A1\uCE60\uC740 \uAD6C\uC870\uD615\uC5D0\uC11C\uB3C4 \uC81C\uAC70\uD55C\uB2E4.
94570
+
94571
+ ## \uAD6C\uC870\uB294 \uC0B4\uB9AC\uACE0, \uBD88\uB9BF '\uC548\uC758 \uC5B8\uC5B4'\uB9CC \uC0AC\uB78C\uB2F5\uAC8C
94572
+ - \uAC01 \uBD88\uB9BF\uC740 \uB3D9\uC0AC\uB098 \uC218\uCE58\uB85C \uC2DC\uC791\uD558\uACE0, \uAC80\uC99D \uAC00\uB2A5\uD55C \uC0AC\uC2E4(\uC22B\uC790\xB7\uAE30\uAC04\xB7\uB3C4\uAD6C\xB7\uC5ED\uD560\xB7\uACB0\uACFC)\uC744 \uD55C \uC904\uC5D0 \uB2F4\uB294\uB2E4.
94573
+ - \uBD88\uB9BF \uC548\uC758 \uBC88\uC5ED\uD22C\xB7\uD074\uB9AC\uC170\xB7\uACF5\uD5C8\uD55C \uC77C\uBC18\uB860\xB7\uC218\uB3D9\uD0DC\uB294 \uC790\uAE30\uC18C\uAC1C\uC11C\uC640 \uB611\uAC19\uC774 \uC81C\uAC70\uD55C\uB2E4. (\uC608: "\uBD84\uC11D\uC744 \uC9C4\uD589\uD558\uC600\uC2B5\uB2C8\uB2E4" \u2192 "\uBD84\uC11D\uD588\uB2E4", "\uB9CE\uC740 \uC131\uACFC\uB97C \uB0C8\uC2B5\uB2C8\uB2E4" \u2192 \uC2E4\uC81C \uC218\uCE58)
94574
+ - STAR\uC744 \uD55C\uB450 \uC904\uB85C \uC555\uCD95\uD55C\uB2E4: \uBB34\uC5C7\uC744 \uC65C \uD588\uACE0 \uACB0\uACFC \uC218\uCE58\uAC00 \uC5B4\uB5BB\uAC8C \uB410\uB294\uC9C0.
94575
+
94576
+ ## Do / Don't
94577
+ - Do: \uB450\uAD04\uC2DD \uBD88\uB9BF(\uACB0\uACFC\xB7\uC218\uCE58\uB97C \uC55E\uC138\uC6C0), \uC9C1\uBB34\xB7\uAE30\uAC04\xB7\uC5ED\uD560 \uAD6C\uBD84, \uC131\uACFC\uB97C \uC22B\uC790\uB85C.
94578
+ - Don't: \uACBD\uB825\uAE30\uC220\uC11C\uB97C \uC790\uAE30\uC18C\uAC1C\uC11C\uCC98\uB7FC \uC904\uAE00\uB85C \uD3C9\uD0C4\uD654\uD558\uC9C0 \uC54A\uB294\uB2E4. \uBD88\uB9BF\uC744 \uC5C6\uC560 \uC2A4\uCE94\uD558\uAE30 \uC5B4\uB835\uAC8C \uB9CC\uB4E4\uC9C0 \uC54A\uB294\uB2E4. \uB77C\uBCA8\xB7\uAD6C\uC870\uB97C \uB2E4 \uAC77\uC5B4\uB0B4 \uAC00\uB3C5\uC131\uC744 \uB5A8\uC5B4\uB728\uB9AC\uC9C0 \uC54A\uB294\uB2E4.
94579
+
94580
+ \uD575\uC2EC: \uC790\uAE30\uC18C\uAC1C\uC11C\uB294 \uC0AC\uB78C\uC758 \uBAA9\uC18C\uB9AC(\uC0B0\uBB38)\uB85C, \uACBD\uB825\uAE30\uC220\uC11C\xB7\uC774\uB825\uC11C\uB294 \uC0AC\uB78C\uC758 \uC5B8\uC5B4\uB85C \uC4F4 \uAE54\uB054\uD55C \uAD6C\uC870\uB85C. \uB458 \uB2E4 \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 100% \uBCF4\uC874\uD55C\uB2E4.`;
94581
+ var STRUCTURED_DOC_TYPES = /* @__PURE__ */ new Set(["career_description", "resume", "portfolio"]);
94582
+ function getWritingStyleGuide(docType) {
94583
+ if (docType && STRUCTURED_DOC_TYPES.has(docType)) {
94584
+ return `${HUMANIZE_WRITING_GUIDE}
94585
+
94586
+ ${STRUCTURED_DOC_WRITING_GUIDE}`;
94587
+ }
94588
+ return HUMANIZE_WRITING_GUIDE;
94589
+ }
94367
94590
 
94368
94591
  // packages/workflows/src/definitions.ts
94369
94592
  var WORKFLOWS = [
@@ -94374,7 +94597,7 @@ var WORKFLOWS = [
94374
94597
  trigger: '\uC0AC\uC6A9\uC790\uAC00 \uCC98\uC74C CareerMate\uC5D0 \uC5F0\uACB0\uD588\uAC70\uB098, "\uC2DC\uC791\uD558\uC790 / \uC14B\uC5C5\uD574\uC918 / \uD504\uB85C\uD544 \uB4F1\uB85D"\uC774\uB77C\uACE0 \uC694\uCCAD\uD560 \uB54C.',
94375
94598
  steps: [
94376
94599
  "`get_onboarding_status`\uB97C \uD638\uCD9C\uD574 \uD604\uC7AC \uB4F1\uB85D \uC0C1\uD0DC(has_profile, has_resume, has_cover_letter, has_experience, has_skills, profile_completeness, next_steps)\uB97C \uD655\uC778\uD55C\uB2E4.",
94377
- '\uBE44\uC5B4 \uC788\uB294 \uD56D\uBAA9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC548\uB0B4\uD558\uACE0, \uBA3C\uC800 "\uAE30\uC874\uC5D0 \uC791\uC131\uD574 \uB454 \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C \uD30C\uC77C\uC774 \uC788\uB294\uC9C0" \uBB3B\uB294\uB2E4(hwp/docx/pdf \uBAA8\uB450 \uAC00\uB2A5).',
94600
+ '\uBE44\uC5B4 \uC788\uB294 \uD56D\uBAA9\uC744 \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC548\uB0B4\uD558\uACE0, \uBA3C\uC800 "\uAE30\uC874\uC5D0 \uC791\uC131\uD574 \uB454 \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC774 \uC788\uB294\uC9C0" \uBB3B\uB294\uB2E4(hwp/docx/pdf \uBAA8\uB450 \uAC00\uB2A5).',
94378
94601
  '\uD30C\uC77C \uC785\uB825 \uBD84\uAE30: \u2460 \uC0AC\uC6A9\uC790\uAC00 "\uD30C\uC77C\uC774 \uC788\uB2E4"\uACE0 \uD558\uBA74 `open_inbox`\uB85C \uC778\uC785 \uD3F4\uB354\uB97C \uC5F4\uC5B4 \uD30C\uC77C\uC744 \uB123\uAC8C \uD558\uACE0, "\uB2E4 \uB123\uC5C8\uB2E4"\uACE0 \uD558\uBA74 `read_inbox`\uB85C \uBCF8\uBB38\uC744 \uC77D\uB294\uB2E4(pdf\xB7\uC774\uBBF8\uC9C0\uB294 read_inbox\uAC00 \uACBD\uB85C\uB9CC \uC8FC\uBBC0\uB85C \uD074\uB77C\uC774\uC5B8\uD2B8\uC758 \uD30C\uC77C \uC77D\uAE30\uB85C \uC9C1\uC811 \uC77D\uB294\uB2E4). \u2461 \uC0AC\uC6A9\uC790\uAC00 \uD30C\uC77C \uACBD\uB85C\uB97C \uC9C1\uC811 \uC54C\uB824\uC8FC\uBA74 `read_document`\uB85C \uC77D\uB294\uB2E4. \u2462 "\uC5C6\uB2E4/\uC9C1\uC811 \uC785\uB825/\uD14D\uC2A4\uD2B8\uB85C \uBD99\uC5EC\uB123\uAE30"\uBA74 \uD3F4\uB354\uB97C \uC5F4\uC9C0 \uB9D0\uACE0 \uBC1B\uC740 \uD14D\uC2A4\uD2B8\uB97C \uADF8\uB300\uB85C \uC4F4\uB2E4. (\uBB3B\uC9C0\uB3C4 \uC54A\uACE0 open_inbox\uB85C \uD3F4\uB354\uB97C \uC790\uB3D9\uC73C\uB85C \uC5F4\uC9C0 \uC54A\uB294\uB2E4.)',
94379
94602
  "\uC218\uC9D1\uD55C \uC815\uBCF4\uB97C \uAD6C\uC870\uD654\uD574 `save_profile`\uB85C \uC800\uC7A5\uD55C\uB2E4(\uC774\uB984\xB7\uC5F0\uB77D\uCC98\xB7headline\xB7summary\xB7desired_roles\xB7desired_conditions, \uADF8\uB9AC\uACE0 \uAE00\uC4F0\uAE30 \uC120\uD638\uC778 preferred_tone\uACFC emphasis_points \uD3EC\uD568). \uD30C\uC77C \uCD9C\uCC98\uB294 source=upload, \uC9C1\uC811 \uC785\uB825\uC740 manual.",
94380
94603
  "\uC5C5\uB85C\uB4DC\uD55C \uC774\uB825\uC11C/\uACBD\uB825\uAE30\uC220\uC11C/\uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uBCF8\uBB38\uC744 `add_resume`\uB85C \uC800\uC7A5\uD55C\uB2E4(kind\xB7title \uC9C0\uC815, \uB300\uD45C \uBB38\uC11C\uB294 is_primary=true).",
@@ -94395,7 +94618,7 @@ var WORKFLOWS = [
94395
94618
  "\uACF5\uACE0 \uC694\uAD6C\uC0AC\uD56D\uACFC \uC0AC\uC6A9\uC790 \uACBD\uB825/\uC2A4\uD0AC/\uD504\uB85C\uC81D\uD2B8\uB97C \uBE44\uAD50\uD55C\uB2E4: \uC77C\uCE58\uD558\uB294 \uAC15\uC810(strengths)\uACFC \uBD80\uC871\uD55C \uBD80\uBD84(gaps), \uB9E4\uCE6D \uD0A4\uC6CC\uB4DC(matched_keywords)\uC640 \uB204\uB77D \uD0A4\uC6CC\uB4DC(missing_keywords)\uB97C \uB3C4\uCD9C\uD558\uACE0, \uC885\uD569 \uC801\uD569\uB3C4 \uC810\uC218(score 0~100)\uC640 \uC694\uC57D(summary), \uC9C0\uC6D0 \uC804\uB7B5 \uC81C\uC548(recommendations)\uC744 \uC815\uB9AC\uD55C\uB2E4. \uC5C6\uB294 \uACBD\uD5D8\uC744 \uC9C0\uC5B4\uB0B4\uC9C0 \uC54A\uB294\uB2E4.",
94396
94619
  "\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.",
94397
94620
  "\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).",
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.",
94621
+ "\uBD84\uC11D \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \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.",
94399
94622
  '\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
94623
  '\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.',
94401
94624
  "\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).",
@@ -94414,7 +94637,24 @@ var WORKFLOWS = [
94414
94637
  "\uC800\uC7A5\uB41C \uC2E4\uC81C \uACBD\uD5D8\xB7\uC131\uACFC\uB9CC\uC73C\uB85C \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC791\uC131\uD558\uB418, \uC704 \uAE00\uC4F0\uAE30 \uADDC\uCE59\uC744 \uC801\uC6A9\uD574 \uC0AC\uB78C\uC774 \uC4F4 \uB4EF \uC790\uC5F0\uC2A4\uB7FD\uAC8C \uC4F4\uB2E4(\uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uADF8\uB300\uB85C). \uBE48\uCE78\uC774\uB098 \uD655\uC778\uC774 \uD544\uC694\uD55C \uBD80\uBD84\uC740 \uCD94\uCE21\uD558\uC9C0 \uB9D0\uACE0 \uD45C\uC2DC\uD574 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3B\uB294\uB2E4.",
94415
94638
  "\uC644\uC131\uBCF8\uC744 `save_cover_letter_version`\uC73C\uB85C \uC800\uC7A5\uD55C\uB2E4(\uAE30\uC874 \uC790\uAE30\uC18C\uAC1C\uC11C\uBA74 cover_letter_id, \uC0C8\uB85C \uB9CC\uB4E4\uBA74 title, \uACF5\uACE0\uC6A9\uC774\uBA74 job_id, \uBCC0\uACBD \uC694\uC57D\uC740 note, source=ai).",
94416
94639
  "\uC0AC\uC6A9\uC790\uAC00 \uC6D0\uD558\uBA74 `export_cover_letter`\uB85C \uD30C\uC77C\uB85C \uB0B4\uBCF4\uB0B4\uACE0, \uCD94\uAC00 \uC218\uC815 \uC694\uCCAD\uC774 \uC788\uC73C\uBA74 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD55C\uB2E4.",
94417
- "\uC800\uC7A5 \uACB0\uACFC\uB97C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB9AC\uACE0, \uC9C0\uC6D0 \uC0C1\uD0DC \uC5C5\uB370\uC774\uD2B8(`update_application_status`)\uB098 \uBA74\uC811 \uC900\uBE44 \uB4F1 \uB2E4\uC74C \uB2E8\uACC4\uB97C \uC81C\uC548\uD55C\uB2E4."
94640
+ "\uC800\uC7A5 \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC54C\uB9AC\uACE0, \uC9C0\uC6D0 \uC0C1\uD0DC \uC5C5\uB370\uC774\uD2B8(`update_application_status`)\uB098 \uBA74\uC811 \uC900\uBE44 \uB4F1 \uB2E4\uC74C \uB2E8\uACC4\uB97C \uC81C\uC548\uD55C\uB2E4."
94641
+ ]
94642
+ },
94643
+ {
94644
+ id: "write_career_description",
94645
+ title: "\uACBD\uB825\uAE30\uC220\uC11C \uC791\uC131",
94646
+ description: "\uD504\uB85C\uD544\xB7\uC774\uB825\uC11C\xB7\uACBD\uB825\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uC2A4\uD0AC\uC744 \uADFC\uAC70\uB85C \uC2E4\uC81C \uC81C\uCD9C \uAC00\uB2A5\uD55C \uACBD\uB825\uAE30\uC220\uC11C\uB97C \uC791\uC131\uD558\uACE0 \uBB38\uC11C\uD568\uC5D0 \uC800\uC7A5\uD558\uB294 \uD750\uB984.",
94647
+ trigger: '\uC0AC\uC6A9\uC790\uAC00 "\uACBD\uB825\uAE30\uC220\uC11C \uC368\uC918 / \uACBD\uB825\uAE30\uC220\uC11C \uB9CC\uB4E4\uC5B4\uC918 / \uB0B4 \uACBD\uB825 \uC815\uB9AC\uD574\uC918"\uB77C\uACE0 \uC694\uCCAD\uD560 \uB54C.',
94648
+ steps: [
94649
+ "`get_onboarding_status`\uB85C \uC900\uBE44 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uACE0, `get_profile`\xB7`get_resumes`\xB7`get_experiences`\xB7`get_projects`\xB7`get_skills`\uB97C \uD638\uCD9C\uD574 \uAE30\uC874 \uD504\uB85C\uD544, \uC774\uB825\uC11C, \uACBD\uB825\uAE30\uC220\uC11C(kind=career_description), \uACBD\uB825, \uD504\uB85C\uC81D\uD2B8, \uAE30\uC220\uC2A4\uD0DD\uC744 \uBAA8\uB450 \uD655\uC778\uD55C\uB2E4.",
94650
+ "\uC0AC\uC6A9\uC790\uAC00 \uD30C\uC77C \uACBD\uB85C\uB97C \uC8FC\uBA74 `read_document`\uB85C \uC77D\uACE0, \uC778\uC785 \uD3F4\uB354\uB97C \uC0AC\uC6A9\uD588\uB2E4\uBA74 `read_inbox`\uB85C \uBCF8\uBB38\uC744 \uC77D\uB294\uB2E4. \uC774\uBBF8 \uC800\uC7A5\uB41C \uC790\uB8CC\uC640 \uC0C8 \uD30C\uC77C\uC744 \uBE44\uAD50\uD574 \uC911\uBCF5\xB7\uC0C1\uCDA9\uB418\uB294 \uC0AC\uC2E4\uC740 \uD45C\uC2DC\uD558\uACE0 \uCD94\uCE21\uD558\uC9C0 \uC54A\uB294\uB2E4.",
94651
+ '`get_workflow_guide({ workflow_id: "write_career_description" })`\uAC00 \uC548\uB0B4\uD558\uB294 \uC804\uBB38\uAC00 \uC808\uCC28\uB97C \uB530\uB978\uB2E4. \uC791\uC131 \uC9C1\uC804 `get_playbook({ domain: "resume" })`\uC640 `get_playbook({ domain: "ats" })`\uB97C \uBC1B\uC544 \uC2E4\uC81C \uACBD\uB825\uAE30\uC220\uC11C \uD615\uC2DD\uACFC ATS \uCE5C\uD654\uC131\uC744 \uC801\uC6A9\uD55C\uB2E4.',
94652
+ "\uC791\uC131 \uC804\uC5D0 \uBAA9\uC801\uC744 \uD655\uC778\uD55C\uB2E4: \uBC94\uC6A9 \uB9C8\uC2A4\uD130\uBCF8\uC778\uC9C0, \uD2B9\uC815 \uC9C1\uBB34/\uACF5\uACE0\uC6A9\uC778\uC9C0, \uC2E0\uC785/\uBB34\uACBD\uB825\uC774\uB77C \uD504\uB85C\uC81D\uD2B8\xB7\uAD50\uC721\xB7\uD65C\uB3D9 \uC911\uC2EC \uC5ED\uB7C9\uAE30\uC220\uC11C\uB85C \uC4F8\uC9C0, \uBD84\uB7C9\uACFC \uD1A4\uC740 \uC5B4\uB5BB\uAC8C \uD560\uC9C0 \uBB3B\uB294\uB2E4.",
94653
+ "\uACBD\uB825\uAE30\uC220\uC11C\uB294 \uC790\uAE30\uC18C\uAC1C\uC11C\uCC98\uB7FC \uC11C\uC0AC\uB85C \uC4F0\uC9C0 \uC54A\uB294\uB2E4. \uC81C\uBAA9, \uD575\uC2EC \uC694\uC57D, \uD575\uC2EC \uC5ED\uB7C9, \uACBD\uB825/\uD504\uB85C\uC81D\uD2B8\uBCC4 \uC5ED\uD560\xB7\uAE30\uAC04\xB7\uB3C4\uAD6C\xB7\uBB38\uC81C\xB7\uD589\uB3D9\xB7\uC131\uACFC\uB97C \uD45C\uB098 \uBD88\uB9BF \uC911\uC2EC\uC73C\uB85C \uC815\uB9AC\uD55C\uB2E4. \uC218\uCE58\xB7\uD68C\uC0AC\uBA85\xB7\uAE30\uAC04\xB7\uC9C1\uCC45\uC740 \uC800\uC7A5\uB41C \uC0AC\uC2E4\uB9CC \uC4F0\uACE0, \uC5C6\uB294 \uC131\uACFC \uC218\uCE58\uB294 \uB9CC\uB4E4\uC9C0 \uC54A\uB294\uB2E4.",
94654
+ '\uBB34\uACBD\uB825/\uBE44\uAC1C\uBC1C\uC790\uB77C\uBA74 \uACBD\uB825\uC744 \uB0A0\uC870\uD558\uC9C0 \uB9D0\uACE0 \uAD50\uC721\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uC6B4\uC601 \uACBD\uD5D8\xB7\uC5C5\uBB34 \uB3C4\uAD6C\xB7\uD611\uC5C5 \uACBD\uD5D8\xB7\uC131\uACFC \uC99D\uAC70 \uC911\uC2EC\uC758 "\uC5ED\uB7C9\uAE30\uC220\uC11C"\uB85C \uAD6C\uC131\uD55C\uB2E4.',
94655
+ '\uC800\uC7A5 \uC9C1\uC804 `get_verifier({ id: "truthfulness" })`, `get_verifier({ id: "consistency" })`, `get_verifier({ id: "ats-compat" })`\uB97C \uBC1B\uC544 \uB2F9\uC2E0\uC774 \uC9C1\uC811 \uC810\uAC80\uD558\uACE0, \uD5C8\uC704\xB7\uC0C1\uCDA9\xB7\uD0A4\uC6CC\uB4DC \uB204\uB77D\uC744 \uACE0\uCE5C\uB2E4. \uD655\uC778\uC774 \uD544\uC694\uD55C \uC0AC\uC2E4\uC740 \uBCF8\uBB38\uC5D0 \uD45C\uC2DC\uD558\uACE0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3B\uB294\uB2E4.',
94656
+ "\uC644\uC131\uBCF8\uC744 `add_resume`\uC73C\uB85C \uC800\uC7A5\uD55C\uB2E4(kind=career_description, source=ai, title\uC740 \uC0AC\uC6A9 \uBAA9\uC801\uC774 \uB4DC\uB7EC\uB098\uAC8C \uC791\uC131, tags\uC5D0 \uC9C1\uBB34/\uC6A9\uB3C4 \uD0A4\uC6CC\uB4DC \uD3EC\uD568). \uB300\uD45C \uBB38\uC11C\uB85C \uC0BC\uC544\uB3C4 \uB418\uB294\uC9C0 \uD655\uC778\uB41C \uACBD\uC6B0\uC5D0\uB9CC is_primary=true\uB97C \uC4F4\uB2E4.",
94657
+ "\uC800\uC7A5 \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC54C\uB9AC\uACE0, \uBB38\uC11C \uD0ED\uC5D0\uC11C \uD655\uC778\uD560 \uC218 \uC788\uC74C\uC744 \uC548\uB0B4\uD55C\uB2E4. \uD2B9\uC815 \uACF5\uACE0\uC6A9\uC73C\uB85C \uC791\uC131\uD588\uB2E4\uBA74 \uC774\uC5B4\uC11C \uC790\uAE30\uC18C\uAC1C\uC11C\uB098 \uBA74\uC811 \uC900\uBE44\uB97C \uC81C\uC548\uD55C\uB2E4."
94418
94658
  ]
94419
94659
  },
94420
94660
  {
@@ -94425,7 +94665,7 @@ var WORKFLOWS = [
94425
94665
  steps: [
94426
94666
  "\uD544\uC694\uD558\uBA74 `get_application_context` \uB610\uB294 `get_job_posting`\uC73C\uB85C \uB300\uC0C1 \uC9C0\uC6D0 \uAC74\uACFC \uACF5\uACE0\uB97C \uD655\uC778\uD55C\uB2E4.",
94427
94667
  "\uC801\uC808\uD55C \uC0C1\uD0DC\uB85C `update_application_status`\uB97C \uD638\uCD9C\uD55C\uB2E4. 8\uB2E8\uACC4: draft(\uC791\uC131 \uC911), planned(\uC9C0\uC6D0 \uC608\uC815), applied(\uC9C0\uC6D0 \uC644\uB8CC), document_passed(\uC11C\uB958 \uD569\uACA9), interview(\uBA74\uC811 \uC9C4\uD589), final_passed(\uCD5C\uC885 \uD569\uACA9), rejected(\uBD88\uD569\uACA9), on_hold(\uBCF4\uB958). \uBCC0\uACBD \uC0AC\uC720\uB294 note\uC5D0 \uB0A8\uAE34\uB2E4.",
94428
- "\uBCC0\uACBD \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB9B0\uB2E4.",
94668
+ "\uBCC0\uACBD \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC54C\uB9B0\uB2E4.",
94429
94669
  "\uC0C1\uD0DC\uC5D0 \uB9DE\uB294 \uB2E4\uC74C \uB2E8\uACC4\uB97C \uC81C\uC548\uD55C\uB2E4: applied \u2192 \uACB0\uACFC \uAE30\uB2E4\uB9AC\uAE30 \uC548\uB0B4, document_passed/interview \u2192 \uBA74\uC811 \uC900\uBE44(prepare_interview) \uC81C\uC548, final_passed \u2192 \uCD95\uD558 \uBC0F \uB9C8\uBB34\uB9AC, rejected/on_hold \u2192 \uD68C\uACE0 \uB610\uB294 \uB2E4\uB978 \uACF5\uACE0 \uD0D0\uC0C9 \uC81C\uC548.",
94430
94670
  "\uD544\uC694\uD558\uBA74 `open_application`\uC73C\uB85C \uD574\uB2F9 \uC9C0\uC6D0 \uAC74\uC744 \uC5F4\uC5B4 \uBCF4\uC5EC\uC900\uB2E4."
94431
94671
  ]
@@ -94433,14 +94673,29 @@ var WORKFLOWS = [
94433
94673
  {
94434
94674
  id: "prepare_interview",
94435
94675
  title: "\uBA74\uC811 \uC900\uBE44",
94436
- description: "\uC11C\uB958 \uD569\uACA9 \uC774\uD6C4 \uC608\uC0C1 \uC9C8\uBB38\xB7STAR \uB2F5\uBCC0\xB71\uBD84 \uC790\uAE30\uC18C\uAC1C\uB97C \uC900\uBE44\uD574 \uC800\uC7A5\uD558\uB294 \uD750\uB984.",
94437
- trigger: '\uC9C0\uC6D0 \uC0C1\uD0DC\uAC00 \uC11C\uB958 \uD569\uACA9(document_passed) \uB610\uB294 \uBA74\uC811 \uC9C4\uD589(interview)\uC73C\uB85C \uBC14\uB00C\uC5C8\uC744 \uB54C, \uB610\uB294 \uC0AC\uC6A9\uC790\uAC00 "\uBA74\uC811 \uC900\uBE44 \uB3C4\uC640\uC918"\uB77C\uACE0 \uC694\uCCAD\uD560 \uB54C.',
94676
+ description: "\uACF5\uACE0 \uAE30\uC900 \uC608\uC0C1 \uC9C8\uBB38\xB7STAR \uB2F5\uBCC0\xB71\uBD84 \uC790\uAE30\uC18C\uAC1C\uB97C \uC900\uBE44\uD574 \uC800\uC7A5\uD558\uB294 \uD750\uB984.",
94677
+ trigger: '\uC0AC\uC6A9\uC790\uAC00 "\uBA74\uC811 \uC900\uBE44 \uB3C4\uC640\uC918"\uB77C\uACE0 \uC694\uCCAD\uD558\uAC70\uB098, \uC9C0\uC6D0 \uC0C1\uD0DC\uAC00 \uC11C\uB958 \uD569\uACA9(document_passed) \uB610\uB294 \uBA74\uC811 \uC9C4\uD589(interview)\uC73C\uB85C \uBC14\uB00C\uC5C8\uC744 \uB54C.',
94438
94678
  steps: [
94439
94679
  "`get_application_context`(job_id \uD3EC\uD568)\uB97C \uD638\uCD9C\uD574 \uACF5\uACE0, \uD504\uB85C\uD544, \uACBD\uB825/\uD504\uB85C\uC81D\uD2B8, \uC801\uD569\uB3C4 \uBD84\uC11D(\uAC15\uC810\xB7\uAC2D), \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uAC00\uC838\uC628\uB2E4.",
94440
94680
  "\uACF5\uACE0\uC640 \uC0AC\uC6A9\uC790 \uACBD\uD5D8\uC744 \uBC14\uD0D5\uC73C\uB85C \uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38(question, intent, followups, answer_outline)\uC744 \uB3C4\uCD9C\uD55C\uB2E4.",
94441
94681
  "\uD575\uC2EC \uACBD\uD5D8\uC5D0 \uB300\uD55C STAR \uAC00\uC774\uB4DC(question, situation, task, action, result)\uC640 1\uBD84 \uC790\uAE30\uC18C\uAC1C(self_introduction) \uCD08\uC548\uC744 \uC791\uC131\uD55C\uB2E4. \uC2E4\uC81C \uACBD\uD5D8\uB9CC \uC0AC\uC6A9\uD55C\uB2E4.",
94442
94682
  "\uC900\uBE44 \uB0B4\uC6A9\uC744 `save_interview_prep`\uB85C \uC800\uC7A5\uD55C\uB2E4(job_id, questions, star_guides, self_introduction, notes).",
94443
- "\uC900\uBE44 \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC694\uC57D\uD574 \uC804\uB2EC\uD558\uACE0, \uBAA8\uC758 \uBA74\uC811/\uCD94\uAC00 \uC9C8\uBB38 \uC5F0\uC2B5\uC774\uB098 \uC0C1\uD0DC \uC5C5\uB370\uC774\uD2B8(`update_application_status`\uB85C interview/final_passed)\uB97C \uC81C\uC548\uD55C\uB2E4."
94683
+ "\uC900\uBE44 \uB0B4\uC6A9\uC744 \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC694\uC57D\uD574 \uC804\uB2EC\uD558\uACE0, \uBAA8\uC758 \uBA74\uC811/\uCD94\uAC00 \uC9C8\uBB38 \uC5F0\uC2B5\uC774\uB098 \uC0C1\uD0DC \uC5C5\uB370\uC774\uD2B8(`update_application_status`\uB85C interview/final_passed)\uB97C \uC81C\uC548\uD55C\uB2E4."
94684
+ ]
94685
+ },
94686
+ {
94687
+ id: "build_personal_brand",
94688
+ title: "\uD37C\uC2A4\uB110 \uBE0C\uB79C\uB4DC (LinkedIn\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624)",
94689
+ description: "LinkedIn \uD504\uB85C\uD544\uACFC \uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uAC19\uC740 \uC790\uAE30\uD45C\uD604 \uC790\uC0B0\uC744 \uC9C1\uBB34\uC5D0 \uB9DE\uCDB0 \uC791\uC131\xB7\uCD5C\uC801\uD654\uD558\uB294 \uD750\uB984. \uC774\uB825\uC11C \uC784\uD3EC\uD2B8(\uC628\uBCF4\uB529)\uC640 \uBD84\uB9AC\uB41C \uBCC4\uB3C4 \uC791\uC5C5.",
94690
+ trigger: '\uC0AC\uC6A9\uC790\uAC00 "\uB9C1\uD06C\uB4DC\uC778 \uD504\uB85C\uD544 \uBD10\uC918 / \uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uC815\uB9AC\uD574\uC918 / \uD37C\uC2A4\uB110 \uBE0C\uB79C\uB529 \uB3C4\uC640\uC918"\uB77C\uACE0 \uC694\uCCAD\uD560 \uB54C.',
94691
+ steps: [
94692
+ "`get_application_context`(\uAC00\uB2A5\uD558\uBA74 \uB300\uC0C1 job_id \uD3EC\uD568)\uB97C \uD638\uCD9C\uD574 \uD504\uB85C\uD544\xB7\uACBD\uB825\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uC2A4\uD0AC\xB7\uB300\uD45C \uC774\uB825\uC11C\xB7\uAE00\uC4F0\uAE30 \uC120\uD638\uB97C \uAC00\uC838\uC628\uB2E4. \uD0C0\uAE43 \uACF5\uACE0\uAC00 \uC788\uC73C\uBA74 \uADF8 \uD0A4\uC6CC\uB4DC\uB97C \uC0AC\uC804 \uC785\uB825\uC73C\uB85C \uC4F4\uB2E4.",
94693
+ '`get_workflow_guide({ workflow_id: "build_personal_brand" })`\uAC00 \uC548\uB0B4\uD558\uB294 \uC804\uBB38\uAC00 \uC808\uCC28\uB97C \uB530\uB978\uB2E4. \uC791\uC131 \uC9C1\uC804 `get_playbook({ domain: "linkedin-profile" })`\xB7`get_playbook({ domain: "portfolio" })`\uB97C \uBC1B\uC544 \uD5E4\uB4DC\uB77C\uC778\xB7About\xB7Featured\xB73\uB9C9(problem\u2192approach\u2192impact) \uADDC\uBC94\uC744 \uC801\uC6A9\uD55C\uB2E4.',
94694
+ "\uBB34\uC5C7\uC744 \uB9CC\uB4E4\uC9C0 \uD655\uC778\uD55C\uB2E4(LinkedIn \uD504\uB85C\uD544\uB9CC/\uD3EC\uD2B8\uD3F4\uB9AC\uC624\uB9CC/\uB458 \uB2E4, \uD0C0\uAE43 \uC9C1\uBB34, \uAC15\uC870 \uD3EC\uC778\uD2B8). \uC800\uC7A5\uB41C \uC2E4\uC81C \uACBD\uD5D8\xB7\uC131\uACFC\uB9CC \uC4F0\uACE0, \uC5C6\uB294 \uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uCD94\uCE21\uD558\uC9C0 \uB9D0\uACE0 `[\uD655\uC778 \uD544\uC694]`\uB85C \uD45C\uC2DC\uD574 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3B\uB294\uB2E4.",
94695
+ "LinkedIn: \uD5E4\uB4DC\uB77C\uC778(\uD575\uC2EC \uD0A4\uC6CC\uB4DC\uB97C \uC55E 120\uC790\uC5D0 front-load, \uD558\uB4DC \uC0C1\uD55C 220\uC790)\xB7About \uCCAB \uBB38\uC7A5 \uD6C5\xB7\uC815\uB7C9 \uC131\uACFC\xB7\uC9C1\uBB34 \uD0A4\uC6CC\uB4DC\uB97C \uC791\uC131. \uD3EC\uD2B8\uD3F4\uB9AC\uC624: \uC9C1\uBB34\uC5D0 \uB9DE\uB294 3~6\uAC1C \uD56D\uBAA9\uC744 problem\u2192approach\u2192impact 3\uB9C9\uC73C\uB85C \uAD6C\uC131\uD558\uACE0 \uBCF8\uC778 \uAE30\uC5EC(my_role)\uC640 \uC815\uB7C9 \uC784\uD329\uD2B8\uB97C \uBA85\uC2DC\uD55C\uB2E4.",
94696
+ '\uC800\uC7A5 \uC9C1\uC804 `get_verifier({ id: "human-voice" })`\xB7`get_verifier({ id: "truthfulness" })`\xB7`get_verifier({ id: "consistency" })`\uB97C \uBC1B\uC544 \uB2F9\uC2E0\uC774 \uC9C1\uC811 \uC810\uAC80\uD55C\uB2E4(AI \uD2F0\xB7\uD658\uAC01\xB7\uC0AC\uC2E4 \uBCF4\uC874). \uD0A4\uC6CC\uB4DC \uC0AC\uC804\uC774 \uBE44\uBA74 \uD0A4\uC6CC\uB4DC \uCCB4\uD06C\uB294 \uBCF4\uB958\uD55C\uB2E4.',
94697
+ "\uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uBCF8\uBB38\uC740 `add_resume`(kind=portfolio)\uB85C \uC800\uC7A5\uD55C\uB2E4. LinkedIn \uD504\uB85C\uD544 \uCD08\uC548\uC740 \uC0AC\uC6A9\uC790\uAC00 \uBCF5\uC0AC\uD574 \uC4F0\uB3C4\uB85D \uC81C\uC2DC\uD558\uACE0(\uC804\uC6A9 \uC800\uC7A5\xB7\uAC80\uC99D \uB3C4\uAD6C validate_linkedin_profile\uC740 Phase B \uBBF8\uAD6C\uD604), \uC6D0\uD558\uBA74 \uD30C\uC77C\uB85C \uB0B4\uBCF4\uB0B8\uB2E4.",
94698
+ "\uC800\uC7A5 \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC54C\uB9AC\uACE0 \uB300\uC2DC\uBCF4\uB4DC \uB9C1\uD06C\uB97C \uC548\uB0B4\uD558\uBA70, \uB124\uD2B8\uC6CC\uD0B9(\uCD94\uCC9C \uACBD\uB85C)\uC774\uB098 \uACF5\uACE0 \uC9C0\uC6D0\uC73C\uB85C \uC774\uC5B4\uAC08\uC9C0 \uC81C\uC548\uD55C\uB2E4."
94444
94699
  ]
94445
94700
  }
94446
94701
  ];
@@ -94524,22 +94779,64 @@ var CAREER_ROUTES = {
94524
94779
  verifierSequence: ["truthfulness", "consistency"]
94525
94780
  },
94526
94781
  analyze_job: {
94527
- eop: "job-analysis",
94782
+ eop: ["job-analysis", "fit-analysis"],
94528
94783
  expertSequence: ["fit-matching", "company-research"],
94529
94784
  verifierSequence: ["truthfulness", "consistency", "responsiveness-on-target"],
94530
94785
  loop: "draft_verify_revise"
94531
94786
  },
94787
+ // R22(audit): responsiveness-on-target는 한국 문항형 자소서 전용 검증기(문항별 1:1 응답·
94788
+ // 글자수 상·하한 = "핵심 탈락 사유")인데 analyze_job에만 라우팅돼 있어 정작 자소서 자가검증
94789
+ // 루프에서 빠져 있었다. 동문서답·미응답 문항·글자수 초과를 잡도록 write_cover_letter에 추가.
94532
94790
  write_cover_letter: {
94533
94791
  eop: "cover-letter",
94534
94792
  expertSequence: ["cover-letter", "human-writing", "company-research"],
94535
- verifierSequence: ["human-voice", "truthfulness", "ats-compat", "consistency"],
94793
+ verifierSequence: ["human-voice", "truthfulness", "responsiveness-on-target", "ats-compat", "consistency"],
94794
+ loop: "draft_verify_revise"
94795
+ },
94796
+ write_career_description: {
94797
+ eop: "career-description",
94798
+ expertSequence: ["resume", "ats"],
94799
+ verifierSequence: ["truthfulness", "consistency", "ats-compat"],
94536
94800
  loop: "draft_verify_revise"
94537
94801
  },
94538
94802
  prepare_interview: {
94539
94803
  eop: "interview-prep",
94540
- expertSequence: ["interview-behavioral", "interview-technical", "company-research"],
94541
- verifierSequence: ["truthfulness", "consistency"],
94804
+ expertSequence: ["recruiter-screen", "interview-behavioral", "interview-technical", "company-research"],
94805
+ // R22(audit): recency-staleness 추가 — 면접의 "최근 소식"·회사 근황이 낡으면 거짓 신선도가
94806
+ // 되므로(company-research) 저장 전 신선도 점검을 푸시한다.
94807
+ verifierSequence: ["truthfulness", "consistency", "recency-staleness"],
94542
94808
  loop: "draft_verify_revise"
94809
+ },
94810
+ // R7 A2: manage_application_status had no route → rejection-triage / offer-evaluation /
94811
+ // salary-negotiation were orphaned (served only on-demand via get_playbook). Route the
94812
+ // decision-stage playbooks so they are pushed when the user manages application outcomes.
94813
+ // R7 A4: A2 routed the decision-stage playbooks, but the EOP-free lifecycle domains
94814
+ // onboarding-first-90-days and networking-referrals were still on-demand-only. Anchor them
94815
+ // here (the only EOP-free lifecycle route). NOTE: networking's strongest lever is *before*
94816
+ // applied (pre-referral, networking-referrals §2) — this route is the surfacing point, not
94817
+ // the recommended timing, so surface networking early when the user is pre-application.
94818
+ // linkedin-profile/portfolio stay on-demand pending a dedicated personal-brand workflow id
94819
+ // (FIXES.md / STATUS §7.9 follow-up — onboarding EOP is résumé-import-only, so brand-
94820
+ // creation playbooks would blur its stage meaning). Additive wiring (A1/A2 precedent).
94821
+ manage_application_status: {
94822
+ expertSequence: [
94823
+ "rejection-triage-iteration",
94824
+ "offer-evaluation-decision",
94825
+ "salary-negotiation",
94826
+ "onboarding-first-90-days",
94827
+ "networking-referrals"
94828
+ ],
94829
+ // R22(audit): recency-staleness 추가 — 연봉 벤치마크·오퍼 비교는 시점이 지나면 낡은 기준이
94830
+ // 되므로(salary-negotiation/offer-evaluation) 저장 전 신선도 점검을 푸시한다.
94831
+ verifierSequence: ["truthfulness", "consistency", "recency-staleness"]
94832
+ },
94833
+ // R7 후속: linkedin-profile·portfolio는 onboarding(이력서 임포트)과 단계 의미가
94834
+ // 달라(개인 브랜드 자산 *생성*) 전용 워크플로우로 분리(consensus 권고). networking은
94835
+ // manage_application_status에 이미 정박. 저장은 add_resume(kind=portfolio); linkedin
94836
+ // 전용 저장/검증은 Phase B.
94837
+ build_personal_brand: {
94838
+ expertSequence: ["linkedin-profile", "portfolio"],
94839
+ verifierSequence: ["truthfulness", "human-voice", "consistency"]
94543
94840
  }
94544
94841
  };
94545
94842
  function getRoute(id2) {
@@ -94549,12 +94846,15 @@ function renderRouteGuide(id2) {
94549
94846
  const route = CAREER_ROUTES[id2];
94550
94847
  if (!route) return void 0;
94551
94848
  const lines = [];
94552
- if (route.eop) {
94849
+ const eops = route.eop ? Array.isArray(route.eop) ? route.eop : [route.eop] : [];
94850
+ if (eops.length) {
94553
94851
  lines.push("---", "");
94554
94852
  lines.push("## \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28 (EOP) \u2014 \uC989\uB2F5\uD558\uC9C0 \uB9D0\uACE0 \uC774 \uC808\uCC28\uB97C \uB530\uB974\uC138\uC694");
94555
94853
  lines.push("");
94556
- lines.push(getEop(route.eop));
94557
- lines.push("");
94854
+ for (const e of eops) {
94855
+ lines.push(getEop(e));
94856
+ lines.push("");
94857
+ }
94558
94858
  }
94559
94859
  lines.push("## \uC774 \uC791\uC5C5\uC5D0 \uC801\uC6A9\uD560 \uC804\uBB38\uAC00 \uD50C\uB808\uC774\uBD81");
94560
94860
  lines.push("\uC791\uC131 \uC9C1\uC804, \uC544\uB798 \uB3C4\uBA54\uC778\uC758 \uAE4A\uC740 \uC9C0\uC2DD\uC744 \uBC1B\uC544 \uC801\uC6A9\uD558\uC138\uC694:");
@@ -94668,10 +94968,22 @@ function slugify(input, fallback = "document") {
94668
94968
  function joinBlocks(blocks) {
94669
94969
  return blocks.filter((b) => typeof b === "string" && b.trim().length > 0).join("\n\n");
94670
94970
  }
94971
+ function bulletList(items) {
94972
+ const lines = items.filter((i2) => typeof i2 === "string" && i2.trim().length > 0).map((i2) => `- ${i2.trim()}`);
94973
+ return lines.join("\n");
94974
+ }
94671
94975
  function kv(label, value) {
94672
94976
  if (!value || !value.trim()) return "";
94673
94977
  return `**${label}**: ${value.trim()}`;
94674
94978
  }
94979
+ function dateRange(start, end, current = false) {
94980
+ const s = (start ?? "").trim();
94981
+ const e = current ? "\uD604\uC7AC" : (end ?? "").trim();
94982
+ if (!s && !e) return "";
94983
+ if (!s) return e;
94984
+ if (!e) return s;
94985
+ return `${s} ~ ${e}`;
94986
+ }
94675
94987
  function renderInline(escaped) {
94676
94988
  let out = escaped;
94677
94989
  out = out.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
@@ -94780,6 +95092,123 @@ function coverLetterToHtml(cl, opts) {
94780
95092
  content: toPrintableHtml(cl.title, coverLetterBody(cl, opts))
94781
95093
  };
94782
95094
  }
95095
+ function resumeToMarkdown(doc, profile) {
95096
+ const kindLabel = DOCUMENT_KIND_LABELS[doc.kind] ?? doc.kind;
95097
+ const header = [];
95098
+ if (profile?.name) header.push(kv("\uC774\uB984", profile.name));
95099
+ if (profile?.email) header.push(kv("\uC774\uBA54\uC77C", profile.email));
95100
+ if (profile?.phone) header.push(kv("\uC5F0\uB77D\uCC98", profile.phone));
95101
+ const body = joinBlocks([
95102
+ `# ${doc.title}`,
95103
+ `_${kindLabel}_`,
95104
+ header.length ? header.filter(Boolean).join("\n\n") : "",
95105
+ header.length ? "---" : "",
95106
+ (doc.content ?? "").trim() || "_(\uB0B4\uC6A9 \uC5C6\uC74C)_"
95107
+ ]);
95108
+ return {
95109
+ filename: `${slugify(doc.title, doc.kind)}.md`,
95110
+ mimeType: MD_MIME,
95111
+ content: body
95112
+ };
95113
+ }
95114
+ function resumeToHtml(doc, profile) {
95115
+ const md = resumeToMarkdown(doc, profile);
95116
+ return {
95117
+ filename: `${slugify(doc.title, doc.kind)}.html`,
95118
+ mimeType: HTML_MIME,
95119
+ content: toPrintableHtml(doc.title, md.content)
95120
+ };
95121
+ }
95122
+ function profileToMarkdown(profile, experiences = [], projects = [], skills = []) {
95123
+ const name = profile.name?.trim() || "\uC774\uB825\uC11C";
95124
+ const contact = [];
95125
+ if (profile.email) contact.push(profile.email);
95126
+ if (profile.phone) contact.push(profile.phone);
95127
+ if (profile.location) contact.push(profile.location);
95128
+ const links = (profile.links ?? []).filter((l2) => l2.url).map((l2) => `[${l2.label || l2.url}](${l2.url})`);
95129
+ const headerBlocks = joinBlocks([
95130
+ `# ${name}`,
95131
+ profile.headline ? `## ${profile.headline}` : "",
95132
+ contact.length ? contact.join(" \xB7 ") : "",
95133
+ links.length ? links.join(" \xB7 ") : ""
95134
+ ]);
95135
+ const summaryBlock = profile.summary?.trim() ? joinBlocks(["## \uC18C\uAC1C", profile.summary.trim()]) : "";
95136
+ const desiredLines = [];
95137
+ if (profile.desired_roles?.length) {
95138
+ desiredLines.push(kv("\uD76C\uB9DD \uC9C1\uBB34", profile.desired_roles.join(", ")));
95139
+ }
95140
+ if (profile.desired_conditions?.trim()) {
95141
+ desiredLines.push(kv("\uD76C\uB9DD \uC870\uAC74", profile.desired_conditions));
95142
+ }
95143
+ const desiredBlock = desiredLines.length ? joinBlocks(["## \uD76C\uB9DD \uC0AC\uD56D", desiredLines.filter(Boolean).join("\n\n")]) : "";
95144
+ const expBlocks = experiences.length ? joinBlocks([
95145
+ "## \uACBD\uB825",
95146
+ ...experiences.map((e) => {
95147
+ const range2 = dateRange(e.start_date, e.end_date, e.is_current);
95148
+ const titleParts = [e.company, e.role].filter(Boolean).join(" \u2014 ");
95149
+ const subParts = [e.employment_type, range2].filter(Boolean).join(" \xB7 ");
95150
+ return joinBlocks([
95151
+ `### ${titleParts}`,
95152
+ subParts ? `_${subParts}_` : "",
95153
+ e.description?.trim() ? e.description.trim() : "",
95154
+ e.achievements?.length ? bulletList(e.achievements) : "",
95155
+ e.tech?.length ? kv("\uAE30\uC220", e.tech.join(", ")) : ""
95156
+ ]);
95157
+ })
95158
+ ]) : "";
95159
+ const projBlocks = projects.length ? joinBlocks([
95160
+ "## \uD504\uB85C\uC81D\uD2B8",
95161
+ ...projects.map((p) => {
95162
+ const range2 = dateRange(p.start_date, p.end_date);
95163
+ const subParts = [p.role, range2].filter(Boolean).join(" \xB7 ");
95164
+ return joinBlocks([
95165
+ `### ${p.name}`,
95166
+ subParts ? `_${subParts}_` : "",
95167
+ p.description?.trim() ? p.description.trim() : "",
95168
+ p.highlights?.length ? bulletList(p.highlights) : "",
95169
+ p.tech?.length ? kv("\uAE30\uC220", p.tech.join(", ")) : "",
95170
+ p.url ? kv("\uB9C1\uD06C", p.url) : ""
95171
+ ]);
95172
+ })
95173
+ ]) : "";
95174
+ let skillsBlock = "";
95175
+ if (skills.length) {
95176
+ const groups = /* @__PURE__ */ new Map();
95177
+ for (const s of skills) {
95178
+ const cat = s.category?.trim() || "\uAE30\uD0C0";
95179
+ const label = s.level?.trim() ? `${s.name} (${s.level.trim()})` : s.name;
95180
+ if (!groups.has(cat)) groups.set(cat, []);
95181
+ groups.get(cat).push(label);
95182
+ }
95183
+ const lines = [...groups.entries()].map(
95184
+ ([cat, items]) => kv(cat, items.join(", "))
95185
+ );
95186
+ skillsBlock = joinBlocks(["## \uBCF4\uC720 \uAE30\uC220", lines.filter(Boolean).join("\n\n")]);
95187
+ }
95188
+ const content = joinBlocks([
95189
+ headerBlocks,
95190
+ "---",
95191
+ summaryBlock,
95192
+ desiredBlock,
95193
+ expBlocks,
95194
+ projBlocks,
95195
+ skillsBlock
95196
+ ]);
95197
+ return {
95198
+ filename: `${slugify(name, "resume")}-resume.md`,
95199
+ mimeType: MD_MIME,
95200
+ content
95201
+ };
95202
+ }
95203
+ function profileToHtml(profile, experiences = [], projects = [], skills = []) {
95204
+ const md = profileToMarkdown(profile, experiences, projects, skills);
95205
+ const title = profile.name?.trim() || "\uC774\uB825\uC11C";
95206
+ return {
95207
+ filename: `${slugify(title, "resume")}-resume.html`,
95208
+ mimeType: HTML_MIME,
95209
+ content: toPrintableHtml(title, md.content)
95210
+ };
95211
+ }
94783
95212
 
94784
95213
  // packages/parsers/src/html.ts
94785
95214
  var NAMED_ENTITIES = {
@@ -95340,7 +95769,12 @@ async function extractDocument(input, baseDir) {
95340
95769
  path: filePath,
95341
95770
  ...over
95342
95771
  });
95343
- if (SENSITIVE_PATH_RE.test(filePath)) {
95772
+ let realPath = filePath;
95773
+ try {
95774
+ realPath = fs4.realpathSync.native(filePath);
95775
+ } catch {
95776
+ }
95777
+ if (SENSITIVE_PATH_RE.test(filePath) || SENSITIVE_PATH_RE.test(realPath)) {
95344
95778
  return base("unsupported", {
95345
95779
  warnings: [
95346
95780
  "\uBCF4\uC548\uC0C1 \uBBFC\uAC10\uD560 \uC218 \uC788\uB294 \uD30C\uC77C(\uAC1C\uC778 \uD0A4\xB7\uC790\uACA9\uC99D\uBA85 \uB4F1)\uC740 \uC77D\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uC790\uAE30\uC18C\uAC1C\uC11C \uAC19\uC740 \uBB38\uC11C \uD30C\uC77C\uC744 \uC9C0\uC815\uD574 \uC8FC\uC138\uC694."
@@ -96022,14 +96456,20 @@ function getUpdateStatus() {
96022
96456
  if (cacheExpired(cache2)) refreshInBackground();
96023
96457
  return buildStatus(cache2);
96024
96458
  }
96025
- async function getUpdateStatusAsync() {
96459
+ async function getUpdateStatusAsync(force = false) {
96026
96460
  if (checkDisabled()) return buildStatus(null);
96027
- let cache2 = readCache();
96028
- if (cacheExpired(cache2)) {
96029
- cache2 = { checkedAt: Date.now(), latest: await fetchLatest() };
96030
- writeCache(cache2);
96031
- }
96032
- return buildStatus(cache2);
96461
+ const cache2 = readCache();
96462
+ if (!force && !cacheExpired(cache2)) return buildStatus(cache2);
96463
+ const latest = await fetchLatest();
96464
+ if (latest !== null) {
96465
+ const fresh = { checkedAt: Date.now(), latest };
96466
+ writeCache(fresh);
96467
+ return buildStatus(fresh);
96468
+ }
96469
+ if (force) return buildStatus(null);
96470
+ const failed = { checkedAt: Date.now(), latest: null };
96471
+ writeCache(failed);
96472
+ return buildStatus(failed);
96033
96473
  }
96034
96474
  function killTree(child) {
96035
96475
  if (process.platform === "win32" && child.pid) {
@@ -96087,12 +96527,8 @@ async function runSelfUpdate() {
96087
96527
  manual: manualCommand()
96088
96528
  };
96089
96529
  }
96090
- let cache2 = readCache();
96091
- if (cacheExpired(cache2)) {
96092
- cache2 = { checkedAt: Date.now(), latest: await fetchLatest() };
96093
- writeCache(cache2);
96094
- }
96095
- const latest = cache2?.latest ?? null;
96530
+ const latest = await fetchLatest();
96531
+ if (latest !== null) writeCache({ checkedAt: Date.now(), latest });
96096
96532
  if (latest === null) {
96097
96533
  return {
96098
96534
  ok: false,
@@ -96135,13 +96571,23 @@ function listInboxFiles(dir) {
96135
96571
  } catch {
96136
96572
  return [];
96137
96573
  }
96574
+ let realDir;
96575
+ try {
96576
+ realDir = fs7.realpathSync.native(dir);
96577
+ } catch {
96578
+ realDir = dir;
96579
+ }
96138
96580
  const SKIP = /^[.~]|^(thumbs\.db|desktop\.ini)$/i;
96139
96581
  const out = [];
96140
96582
  for (const name of names) {
96141
96583
  if (SKIP.test(name)) continue;
96142
96584
  const p = path7.join(dir, name);
96143
96585
  try {
96144
- if (fs7.statSync(p).isFile()) out.push({ name, path: p });
96586
+ if (!fs7.statSync(p).isFile()) continue;
96587
+ const real = fs7.realpathSync.native(p);
96588
+ const rel = path7.relative(realDir, real);
96589
+ if (rel.startsWith("..") || path7.isAbsolute(rel)) continue;
96590
+ out.push({ name, path: p });
96145
96591
  } catch {
96146
96592
  }
96147
96593
  }
@@ -96211,8 +96657,8 @@ ${r.warnings.join("\n")}`, {
96211
96657
  /* --------------------------------------------------------- inbox (파일 인입) */
96212
96658
  {
96213
96659
  name: "open_inbox",
96214
- title: "\uC774\uB825\uC11C \uC778\uC785 \uD3F4\uB354 \uC5F4\uAE30",
96215
- description: 'CLI \uD074\uB77C\uC774\uC5B8\uD2B8\uB294 \uD30C\uC77C \uCCA8\uBD80\uAC00 \uC548 \uB418\uBBC0\uB85C, \uC0AC\uC6A9\uC790\uAC00 \uAE30\uC874 \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C \uD30C\uC77C(hwp/hwpx/docx/pdf \uB4F1)\uC744 \uB04C\uC5B4\uB2E4 \uB123\uC744 \uC218 \uC788\uB294 \uB85C\uCEEC \uC778\uC785 \uD3F4\uB354(~/.careermate/inbox)\uB97C \uBCF4\uC7A5\uD558\uACE0 \uD30C\uC77C \uD0D0\uC0C9\uAE30\uB85C \uC5FD\uB2C8\uB2E4. \uC628\uBCF4\uB529\uC5D0\uC11C "\uAE30\uC874\uC5D0 \uC791\uC131\uD574 \uB454 \uC774\uB825\uC11C \uD30C\uC77C\uC774 \uC788\uB2E4"\uACE0 \uC0AC\uC6A9\uC790\uAC00 \uB2F5\uD588\uC744 \uB54C\uB9CC \uD638\uCD9C\uD558\uC138\uC694 \u2014 \uBB3B\uC9C0\uB3C4 \uC54A\uACE0 \uD3F4\uB354\uB97C \uC790\uB3D9\uC73C\uB85C \uC5F4\uC9C0 \uB9C8\uC138\uC694. \uD30C\uC77C \uACBD\uB85C\uB97C \uC9C1\uC811 \uC54C\uB824\uC8FC\uB294 \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uC774 \uB3C4\uAD6C \uB300\uC2E0 read_document\uB97C \uC4F0\uC138\uC694. \uD638\uCD9C\uD558\uBA74 \uD3F4\uB354 \uACBD\uB85C\uC640 \uD604\uC7AC \uD3F4\uB354\uC5D0 \uB4E4\uC5B4 \uC788\uB294 \uD30C\uC77C \uBAA9\uB85D\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 \uD30C\uC77C\uC744 \uB2E4 \uB123\uC5C8\uB2E4\uACE0 \uD558\uBA74 read_inbox\uB85C \uBCF8\uBB38\uC744 \uC77D\uC73C\uC138\uC694.',
96660
+ title: "\uBB38\uC11C \uC778\uC785 \uD3F4\uB354 \uC5F4\uAE30",
96661
+ description: 'CLI \uD074\uB77C\uC774\uC5B8\uD2B8\uB294 \uD30C\uC77C \uCCA8\uBD80\uAC00 \uC548 \uB418\uBBC0\uB85C, \uC0AC\uC6A9\uC790\uAC00 \uAE30\uC874 \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624\xB7\uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C(hwp/hwpx/docx/pdf \uB4F1)\uC744 \uB04C\uC5B4\uB2E4 \uB123\uC744 \uC218 \uC788\uB294 \uB85C\uCEEC \uC778\uC785 \uD3F4\uB354(~/.careermate/inbox)\uB97C \uBCF4\uC7A5\uD558\uACE0 \uD30C\uC77C \uD0D0\uC0C9\uAE30\uB85C \uC5FD\uB2C8\uB2E4. \uC628\uBCF4\uB529\uC5D0\uC11C "\uAE30\uC874\uC5D0 \uC791\uC131\uD574 \uB454 \uBB38\uC11C \uD30C\uC77C\uC774 \uC788\uB2E4"\uACE0 \uC0AC\uC6A9\uC790\uAC00 \uB2F5\uD588\uC744 \uB54C\uB9CC \uD638\uCD9C\uD558\uC138\uC694 \u2014 \uBB3B\uC9C0\uB3C4 \uC54A\uACE0 \uD3F4\uB354\uB97C \uC790\uB3D9\uC73C\uB85C \uC5F4\uC9C0 \uB9C8\uC138\uC694. \uD30C\uC77C \uACBD\uB85C\uB97C \uC9C1\uC811 \uC54C\uB824\uC8FC\uB294 \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uC774 \uB3C4\uAD6C \uB300\uC2E0 read_document\uB97C \uC4F0\uC138\uC694. \uD638\uCD9C\uD558\uBA74 \uD3F4\uB354 \uACBD\uB85C\uC640 \uD604\uC7AC \uD3F4\uB354\uC5D0 \uB4E4\uC5B4 \uC788\uB294 \uD30C\uC77C \uBAA9\uB85D\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 \uD30C\uC77C\uC744 \uB2E4 \uB123\uC5C8\uB2E4\uACE0 \uD558\uBA74 read_inbox\uB85C \uBCF8\uBB38\uC744 \uC77D\uC73C\uC138\uC694.',
96216
96662
  inputSchema: {},
96217
96663
  handler: () => {
96218
96664
  const dir = getInboxDir();
@@ -96220,10 +96666,10 @@ ${r.warnings.join("\n")}`, {
96220
96666
  const files = listInboxFiles(dir);
96221
96667
  const list = files.length ? files.map((f) => `- ${f.name}`).join("\n") : "(\uC544\uC9C1 \uBE44\uC5B4 \uC788\uC74C)";
96222
96668
  return ok(
96223
- `\uC774\uB825\uC11C \uC778\uC785 \uD3F4\uB354\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4:
96669
+ `\uBB38\uC11C \uC778\uC785 \uD3F4\uB354\uB97C \uC5F4\uC5C8\uC2B5\uB2C8\uB2E4:
96224
96670
  ${dir}
96225
96671
 
96226
- \uC5EC\uAE30\uC5D0 \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C \uD30C\uC77C\uC744 \uB123\uACE0 "\uB2E4 \uB123\uC5C8\uC5B4"\uB77C\uACE0 \uC54C\uB824\uC8FC\uC138\uC694. (hwp/hwpx/docx\uB294 \uC790\uB3D9\uC73C\uB85C \uD14D\uC2A4\uD2B8\uB97C \uCD94\uCD9C\uD558\uACE0, pdf\xB7\uC774\uBBF8\uC9C0\uB294 \uC81C\uAC00 \uC9C1\uC811 \uC77D\uC2B5\uB2C8\uB2E4.)
96672
+ \uC5EC\uAE30\uC5D0 \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624\xB7\uAE30\uC874 \uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC744 \uB123\uACE0 "\uB2E4 \uB123\uC5C8\uC5B4"\uB77C\uACE0 \uC54C\uB824\uC8FC\uC138\uC694. (hwp\xB7hwpx\xB7docx\xB7pdf\uB294 \uC790\uB3D9\uC73C\uB85C \uD14D\uC2A4\uD2B8\uB97C \uCD94\uCD9C\uD558\uACE0, \uD14D\uC2A4\uD2B8 \uB808\uC774\uC5B4\uAC00 \uC5C6\uB294 \uC774\uBBF8\uC9C0\xB7\uC2A4\uCE94 PDF\uB9CC \uC81C\uAC00 \uC9C1\uC811 \uC77D\uC2B5\uB2C8\uB2E4.)
96227
96673
 
96228
96674
  \uD604\uC7AC \uD30C\uC77C:
96229
96675
  ${list}`,
@@ -96234,7 +96680,7 @@ ${list}`,
96234
96680
  {
96235
96681
  name: "read_inbox",
96236
96682
  title: "\uC778\uC785 \uD3F4\uB354 \uD30C\uC77C \uC77D\uAE30",
96237
- description: '\uC778\uC785 \uD3F4\uB354(~/.careermate/inbox)\uC5D0 \uC0AC\uC6A9\uC790\uAC00 \uB123\uC5B4 \uB454 \uBB38\uC11C\uB4E4\uC758 \uBCF8\uBB38\uC744 \uD55C \uBC88\uC5D0 \uC77D\uC5B4 \uB3CC\uB824\uC90D\uB2C8\uB2E4. open_inbox\uB85C \uD3F4\uB354\uB97C \uC548\uB0B4\uD55C \uB4A4 \uC0AC\uC6A9\uC790\uAC00 "\uB2E4 \uB123\uC5C8\uC5B4"\uB77C\uACE0 \uD558\uBA74 \uD638\uCD9C\uD558\uC138\uC694. docx/hwp/hwpx/\uD14D\uC2A4\uD2B8\uB294 \uD14D\uC2A4\uD2B8\uB97C \uCD94\uCD9C\uD558\uACE0, pdf\xB7\uC774\uBBF8\uC9C0\uB294 \uCD94\uCD9C\uD558\uC9C0 \uC54A\uACE0 \uD30C\uC77C \uACBD\uB85C\uB97C \uB3CC\uB824\uC8FC\uB2C8 AI \uD074\uB77C\uC774\uC5B8\uD2B8\uC758 \uD30C\uC77C \uC77D\uAE30 \uAE30\uB2A5\uC73C\uB85C \uADF8 \uACBD\uB85C\uB97C \uC9C1\uC811 \uC5F4\uC5B4 \uC77D\uC73C\uC138\uC694. \uC77D\uC740 \uB0B4\uC6A9\uC744 \uAD6C\uC870\uD654\uD574 save_profile\xB7add_resume \uB4F1 \uC54C\uB9DE\uC740 \uB3C4\uAD6C\uB85C \uC800\uC7A5\uD558\uC138\uC694(\uD30C\uC77C \uB0B4\uC6A9\uC744 \uC0C8 \uD30C\uC77C\uB85C \uB2E4\uC2DC \uC4F0\uC9C0 \uB9C8\uC138\uC694). filename\uC744 \uC8FC\uBA74 \uD574\uB2F9 \uD30C\uC77C\uB9CC \uC77D\uC2B5\uB2C8\uB2E4.',
96683
+ description: '\uC778\uC785 \uD3F4\uB354(~/.careermate/inbox)\uC5D0 \uC0AC\uC6A9\uC790\uAC00 \uB123\uC5B4 \uB454 \uBB38\uC11C\uB4E4\uC758 \uBCF8\uBB38\uC744 \uD55C \uBC88\uC5D0 \uC77D\uC5B4 \uB3CC\uB824\uC90D\uB2C8\uB2E4. open_inbox\uB85C \uD3F4\uB354\uB97C \uC548\uB0B4\uD55C \uB4A4 \uC0AC\uC6A9\uC790\uAC00 "\uB2E4 \uB123\uC5C8\uC5B4"\uB77C\uACE0 \uD558\uBA74 \uD638\uCD9C\uD558\uC138\uC694. docx\xB7hwp\xB7hwpx\xB7pdf\xB7\uD14D\uC2A4\uD2B8(.txt/.md)\uC5D0\uC11C \uBCF8\uBB38 \uD14D\uC2A4\uD2B8\uB97C \uCD94\uCD9C\uD574 \uB3CC\uB824\uC90D\uB2C8\uB2E4(read_document\uC640 \uB3D9\uC77C\uD55C \uCD94\uCD9C \uC2A4\uD0DD). \uB2E8 \uD14D\uC2A4\uD2B8 \uB808\uC774\uC5B4\uAC00 \uC5C6\uB294 \uC774\uBBF8\uC9C0\xB7\uC2A4\uCE94 PDF\uB294 \uCD94\uCD9C\uB418\uC9C0 \uC54A\uC544(unsupported=true) \uD30C\uC77C \uACBD\uB85C\uB9CC \uB3CC\uB824\uC8FC\uB2C8, \uADF8 \uACBD\uB85C\uB294 AI \uD074\uB77C\uC774\uC5B8\uD2B8\uC758 \uD30C\uC77C \uC77D\uAE30 \uAE30\uB2A5\uC73C\uB85C \uC9C1\uC811 \uC5F4\uC5B4 \uC77D\uC73C\uC138\uC694. \uC77D\uC740 \uB0B4\uC6A9\uC744 \uAD6C\uC870\uD654\uD574 \uD504\uB85C\uD544\uC740 save_profile, \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624\uB294 add_resume, \uAE30\uC874 \uC790\uAE30\uC18C\uAC1C\uC11C\uB294 save_cover_letter_version\uC73C\uB85C \uC800\uC7A5\uD558\uC138\uC694(\uD30C\uC77C \uB0B4\uC6A9\uC744 \uC0C8 \uD30C\uC77C\uB85C \uB2E4\uC2DC \uC4F0\uC9C0 \uB9C8\uC138\uC694). filename\uC744 \uC8FC\uBA74 \uD574\uB2F9 \uD30C\uC77C\uB9CC \uC77D\uC2B5\uB2C8\uB2E4.',
96238
96684
  inputSchema: {
96239
96685
  filename: external_exports.string().optional().describe("\uD2B9\uC815 \uD30C\uC77C\uB9CC \uC77D\uC744 \uB54C \uD30C\uC77C\uBA85(\uB300\uC18C\uBB38\uC790 \uBB34\uC2DC). \uC0DD\uB7B5\uD558\uBA74 \uD3F4\uB354 \uC548 \uBAA8\uB4E0 \uD30C\uC77C\uC744 \uC77D\uC2B5\uB2C8\uB2E4."),
96240
96686
  max_chars: external_exports.number().optional().describe("\uD30C\uC77C\uBCC4 \uBC18\uD658 \uD14D\uC2A4\uD2B8 \uCD5C\uB300 \uAE38\uC774(\uCD08\uACFC \uC2DC \uC798\uB77C\uB0C4). \uAE30\uBCF8 20000\uC790")
@@ -96275,7 +96721,7 @@ ${dir}
96275
96721
  `\uC778\uC785 \uD3F4\uB354 \uD30C\uC77C ${files.length}\uAC74\uC744 \uC77D\uC5C8\uC2B5\uB2C8\uB2E4:
96276
96722
  ${summary}
96277
96723
 
96278
- \uCD94\uCD9C\uB41C \uB0B4\uC6A9\uC744 \uAD6C\uC870\uD654\uD574 save_profile\xB7add_resume \uB4F1\uC73C\uB85C \uC800\uC7A5\uD558\uC138\uC694.`,
96724
+ \uCD94\uCD9C\uB41C \uB0B4\uC6A9\uC744 \uAD6C\uC870\uD654\uD574 \uD504\uB85C\uD544\uC740 save_profile, \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624\uB294 add_resume, \uAE30\uC874 \uC790\uAE30\uC18C\uAC1C\uC11C\uB294 save_cover_letter_version\uC73C\uB85C \uC800\uC7A5\uD558\uC138\uC694.`,
96279
96725
  { dir, files }
96280
96726
  );
96281
96727
  }
@@ -96343,7 +96789,7 @@ ${lines}`;
96343
96789
  {
96344
96790
  name: "add_resume",
96345
96791
  title: "\uC774\uB825\uC11C/\uACBD\uB825\uAE30\uC220\uC11C \uCD94\uAC00",
96346
- description: "\uC774\uB825\uC11C, \uACBD\uB825\uAE30\uC220\uC11C, \uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uB4F1 \uBB38\uC11C\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. content\uC5D0\uB294 \uC0AC\uC6A9\uC790\uAC00 \uC5C5\uB85C\uB4DC/\uBD99\uC5EC\uB123\uC740 \uD14D\uC2A4\uD2B8(\uB610\uB294 \uC815\uB9AC\uD55C Markdown)\uB97C \uB123\uC73C\uC138\uC694. kind \uAE30\uBCF8\uAC12\uC740 resume\uC785\uB2C8\uB2E4. is_primary=true\uB85C \uB300\uD45C \uBB38\uC11C\uB97C \uC9C0\uC815\uD558\uBA74 get_application_context\uC758 primary_resume\uB85C \uB178\uCD9C\uB429\uB2C8\uB2E4.",
96792
+ description: "\uC774\uB825\uC11C, \uACBD\uB825\uAE30\uC220\uC11C, \uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uB4F1 \uBB38\uC11C\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. content\uC5D0\uB294 \uC0AC\uC6A9\uC790\uAC00 \uC5C5\uB85C\uB4DC/\uBD99\uC5EC\uB123\uC740 \uD14D\uC2A4\uD2B8 \uB610\uB294 AI\uAC00 \uC804\uBB38\uAC00 \uC808\uCC28\uB85C \uC815\uB9AC\uD55C Markdown\uC744 \uB123\uC73C\uC138\uC694. \uACBD\uB825\uAE30\uC220\uC11C\uB97C \uC791\uC131\uD574 \uC800\uC7A5\uD560 \uB54C\uB294 kind=career_description, source=ai\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \u26A0\uFE0F content\uC5D0\uB294 \uC774 \uBB38\uC11C \uD55C \uC885\uB958\uC758 \uBCF8\uBB38\uB9CC \uB123\uACE0, \uC790\uAE30\uC18C\uAC1C\uC11C \uB4F1 \uB2E4\uB978 \uC885\uB958 \uBB38\uC11C\uB97C \uAC19\uC740 content\uC5D0 \uC774\uC5B4\uBD99\uC774\uC9C0 \uB9C8\uC138\uC694 \u2014 \uC790\uAE30\uC18C\uAC1C\uC11C\uB294 save_cover_letter_version\uC73C\uB85C \uB530\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4(\uD55C \uBC88\uC5D0 \uB458 \uB2E4 \uC694\uCCAD\uBC1B\uC558\uC5B4\uB3C4 \uD55C \uC800\uC7A5 \uD638\uCD9C\uC5D0 \uB450 \uBB38\uC11C\uB97C \uD569\uCE58\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4). kind \uAE30\uBCF8\uAC12\uC740 resume\uC785\uB2C8\uB2E4. is_primary=true\uB85C \uB300\uD45C \uBB38\uC11C\uB97C \uC9C0\uC815\uD558\uBA74 get_application_context\uC758 primary_resume\uB85C \uB178\uCD9C\uB429\uB2C8\uB2E4.",
96347
96793
  // Reuse the shared (length-bounded, whitespace-rejecting) Document schema so
96348
96794
  // the MCP ingestion path — the primary route for résumé text — inherits the
96349
96795
  // same caps as the web API. kind stays optional here (defaults to resume).
@@ -96367,6 +96813,23 @@ ${lines}`;
96367
96813
  return ok(`\uBB38\uC11C ${docs.length}\uAC74`, docs);
96368
96814
  }
96369
96815
  },
96816
+ {
96817
+ name: "delete_resume",
96818
+ title: "\uC774\uB825\uC11C/\uBB38\uC11C \uC0AD\uC81C",
96819
+ description: "\uC800\uC7A5\uB41C \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624 \uB4F1 \uBB38\uC11C\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 \uBA85\uD655\uD788 \uC0AD\uC81C\uB97C \uC694\uCCAD\uD588\uC744 \uB54C\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_resumes\uB85C \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0 \uC5B4\uB5A4 \uBB38\uC11C\uC778\uC9C0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
96820
+ inputSchema: {
96821
+ document_id: external_exports.string(),
96822
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
96823
+ },
96824
+ handler: (args) => {
96825
+ if (args?.confirm !== DELETE_CONFIRMATION) return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
96826
+ const doc = documentRepo.get(args.document_id);
96827
+ if (!doc) return fail("\uBB38\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
96828
+ if (!documentRepo.remove(doc.id)) return fail("\uBB38\uC11C\uB97C \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
96829
+ const user_message = `'${doc.title}' \uBB38\uC11C\uB97C \uC0AD\uC81C\uD588\uC5B4\uC694.`;
96830
+ return ok(user_message, { deleted: true, document_id: doc.id, user_message });
96831
+ }
96832
+ },
96370
96833
  /* ----------------------------------------------------------- experiences */
96371
96834
  {
96372
96835
  name: "add_experience",
@@ -96436,6 +96899,57 @@ ${lines}`;
96436
96899
  return ok(`\uAE30\uC220 ${list.length}\uAC74`, list);
96437
96900
  }
96438
96901
  },
96902
+ {
96903
+ name: "delete_experience",
96904
+ title: "\uACBD\uB825 \uD56D\uBAA9 \uC0AD\uC81C",
96905
+ description: "\uAD6C\uC870\uD654\uB41C \uACBD\uB825(\uC9C1\uC7A5) \uD56D\uBAA9 \uD558\uB098\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC798\uBABB \uCD94\uCD9C/\uC785\uB825\uB41C \uACBD\uB825\uC744 \uC9C0\uC6B8 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_experiences\uB85C \uBAA9\uB85D\uACFC id\uB97C \uD655\uC778\uD558\uACE0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. (\uB0B4\uC6A9 \uC218\uC815\uC740 add_experience\uB85C \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.) confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
96906
+ inputSchema: {
96907
+ experience_id: external_exports.string(),
96908
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
96909
+ },
96910
+ handler: (args) => {
96911
+ if (args?.confirm !== DELETE_CONFIRMATION) return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
96912
+ const exp = experienceRepo.get(args.experience_id);
96913
+ if (!exp) return fail("\uD574\uB2F9 \uACBD\uB825 \uD56D\uBAA9\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
96914
+ if (!experienceRepo.remove(exp.id)) return fail("\uACBD\uB825 \uD56D\uBAA9\uC744 \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
96915
+ const user_message = `'${exp.company}' \uACBD\uB825\uC744 \uC0AD\uC81C\uD588\uC5B4\uC694.`;
96916
+ return ok(user_message, { deleted: true, experience_id: exp.id, user_message });
96917
+ }
96918
+ },
96919
+ {
96920
+ name: "delete_project",
96921
+ title: "\uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9 \uC0AD\uC81C",
96922
+ description: "\uAD6C\uC870\uD654\uB41C \uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9 \uD558\uB098\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC798\uBABB \uCD94\uCD9C/\uC785\uB825\uB41C \uD504\uB85C\uC81D\uD2B8\uB97C \uC9C0\uC6B8 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_projects\uB85C \uBAA9\uB85D\uACFC id\uB97C \uD655\uC778\uD558\uACE0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. (\uB0B4\uC6A9 \uC218\uC815\uC740 add_project\uB85C \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.) confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
96923
+ inputSchema: {
96924
+ project_id: external_exports.string(),
96925
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
96926
+ },
96927
+ handler: (args) => {
96928
+ if (args?.confirm !== DELETE_CONFIRMATION) return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
96929
+ const proj = projectRepo.get(args.project_id);
96930
+ if (!proj) return fail("\uD574\uB2F9 \uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
96931
+ if (!projectRepo.remove(proj.id)) return fail("\uD504\uB85C\uC81D\uD2B8 \uD56D\uBAA9\uC744 \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
96932
+ const user_message = `'${proj.name}' \uD504\uB85C\uC81D\uD2B8\uB97C \uC0AD\uC81C\uD588\uC5B4\uC694.`;
96933
+ return ok(user_message, { deleted: true, project_id: proj.id, user_message });
96934
+ }
96935
+ },
96936
+ {
96937
+ name: "delete_skill",
96938
+ title: "\uAE30\uC220\uC2A4\uD0DD \uD56D\uBAA9 \uC0AD\uC81C",
96939
+ description: "\uAD6C\uC870\uD654\uB41C \uAE30\uC220\uC2A4\uD0DD \uD56D\uBAA9 \uD558\uB098\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uC798\uBABB \uCD94\uCD9C/\uC785\uB825\uB41C \uAE30\uC220\uC744 \uC9C0\uC6B8 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uBA3C\uC800 get_skills\uB85C \uBAA9\uB85D\uACFC id\uB97C \uD655\uC778\uD558\uACE0 \uC0AC\uC6A9\uC790\uC640 \uD655\uC778\uD55C \uB4A4 \uD638\uCD9C\uD569\uB2C8\uB2E4. (\uB0B4\uC6A9 \uC218\uC815\uC740 add_skill\uB85C \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.) confirm\uC5D0\uB294 DELETE\uB97C \uB123\uC73C\uC138\uC694.",
96940
+ inputSchema: {
96941
+ skill_id: external_exports.string(),
96942
+ confirm: external_exports.string().describe("\uC0AD\uC81C \uD655\uC778\uAC12. \uC815\uD655\uD788 DELETE\uB97C \uB123\uC5B4\uC57C \uC0AD\uC81C\uB429\uB2C8\uB2E4.")
96943
+ },
96944
+ handler: (args) => {
96945
+ if (args?.confirm !== DELETE_CONFIRMATION) return fail("\uC0AD\uC81C\uD558\uB824\uBA74 confirm\uC5D0 DELETE\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.");
96946
+ const skill = skillRepo.get(args.skill_id);
96947
+ if (!skill) return fail("\uD574\uB2F9 \uAE30\uC220 \uD56D\uBAA9\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
96948
+ if (!skillRepo.remove(skill.id)) return fail("\uAE30\uC220 \uD56D\uBAA9\uC744 \uC0AD\uC81C\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
96949
+ const user_message = `'${skill.name}' \uAE30\uC220\uC744 \uC0AD\uC81C\uD588\uC5B4\uC694.`;
96950
+ return ok(user_message, { deleted: true, skill_id: skill.id, user_message });
96951
+ }
96952
+ },
96439
96953
  /* ---------------------------------------------------------- cover letters */
96440
96954
  {
96441
96955
  name: "get_cover_letters",
@@ -96451,7 +96965,7 @@ ${lines}`;
96451
96965
  {
96452
96966
  name: "save_cover_letter_version",
96453
96967
  title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uBC84\uC804 \uC800\uC7A5",
96454
- description: '\uC790\uAE30\uC18C\uAC1C\uC11C\uC758 \uC0C8 \uBC84\uC804\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. CareerMate \uC790\uC18C\uC11C \uC791\uC131 \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC758 \uD575\uC2EC \uC800\uC7A5 \uB2E8\uACC4\uC785\uB2C8\uB2E4. cover_letter_id\uB97C \uC8FC\uBA74 \uAE30\uC874 \uC790\uC18C\uC11C\uC5D0 \uC0C8 \uBC84\uC804\uC744 \uCD94\uAC00\uD558\uACE0, \uC5C6\uC73C\uBA74 \uC0C8 \uC790\uC18C\uC11C\uB97C \uB9CC\uB4E4\uC5B4 v1\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4(\uC774\uB54C title \uAD8C\uC7A5). note\uC5D0 \uC774 \uBC84\uC804\uC758 \uBCC0\uACBD \uC694\uC57D(\uC608: "\uC9C0\uC6D0\uB3D9\uAE30 \uBCF4\uAC15")\uC744 \uB0A8\uAE30\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C \uBC84\uC804 \uD788\uC2A4\uD1A0\uB9AC\uB97C \uC774\uD574\uD558\uAE30 \uC27D\uC2B5\uB2C8\uB2E4. job_id\uB85C \uACF5\uACE0\uC5D0 \uC5F0\uACB0\uD558\uBA74 \uC9C0\uC6D0 \uD56D\uBAA9\uACFC \uC790\uB3D9 \uC5F0\uACB0\uB429\uB2C8\uB2E4. \uC800\uC7A5 \uC9C1\uC804 \uC790\uB3D9\uC73C\uB85C "\uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58" \uC810\uAC80\uC744 \uB3CC\uB9BD\uB2C8\uB2E4 \u2014 \uBCF8\uBB38\uC758 \uC815\uB7C9 \uC218\uCE58\uAC00 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADFC\uAC70\uAC00 \uC5C6\uC73C\uBA74(\uD658\uAC01 \uC758\uC2EC) \uC800\uC7A5\uC774 \uB9C9\uD799\uB2C8\uB2E4. \uADF8\uB54C\uB294 \uC6D0\uBCF8\uC758 \uC2E4\uC81C \uC218\uCE58\uB85C \uACE0\uCE58\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774\uBA74 force:true\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694.',
96968
+ description: '\uC790\uAE30\uC18C\uAC1C\uC11C\uC758 \uC0C8 \uBC84\uC804\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. CareerMate \uC790\uC18C\uC11C \uC791\uC131 \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC758 \uD575\uC2EC \uC800\uC7A5 \uB2E8\uACC4\uC785\uB2C8\uB2E4. \u26A0\uFE0F content\uC5D0\uB294 \uC790\uAE30\uC18C\uAC1C\uC11C \uBCF8\uBB38\uB9CC \uB123\uACE0, \uACBD\uB825\uAE30\uC220\uC11C \uB4F1 \uB2E4\uB978 \uC885\uB958 \uBB38\uC11C\uB97C \uC774\uC5B4\uBD99\uC774\uC9C0 \uB9C8\uC138\uC694(\uACBD\uB825\uAE30\uC220\uC11C\uB294 add_resume(kind=career_description)\uB85C \uB530\uB85C \uC800\uC7A5). cover_letter_id\uB97C \uC8FC\uBA74 \uAE30\uC874 \uC790\uC18C\uC11C\uC5D0 \uC0C8 \uBC84\uC804\uC744 \uCD94\uAC00\uD558\uACE0, \uC5C6\uC73C\uBA74 \uC0C8 \uC790\uC18C\uC11C\uB97C \uB9CC\uB4E4\uC5B4 v1\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4(\uC774\uB54C title \uAD8C\uC7A5). note\uC5D0 \uC774 \uBC84\uC804\uC758 \uBCC0\uACBD \uC694\uC57D(\uC608: "\uC9C0\uC6D0\uB3D9\uAE30 \uBCF4\uAC15")\uC744 \uB0A8\uAE30\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C \uBC84\uC804 \uD788\uC2A4\uD1A0\uB9AC\uB97C \uC774\uD574\uD558\uAE30 \uC27D\uC2B5\uB2C8\uB2E4. job_id\uB85C \uACF5\uACE0\uC5D0 \uC5F0\uACB0\uD558\uBA74 \uC9C0\uC6D0 \uD56D\uBAA9\uACFC \uC790\uB3D9 \uC5F0\uACB0\uB429\uB2C8\uB2E4. \uC800\uC7A5 \uC9C1\uC804 \uC790\uB3D9\uC73C\uB85C "\uADFC\uAC70 \uC5C6\uB294 \uC218\uCE58" \uC810\uAC80\uC744 \uB3CC\uB9BD\uB2C8\uB2E4 \u2014 \uBCF8\uBB38\uC758 \uC815\uB7C9 \uC218\uCE58\uAC00 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADFC\uAC70\uAC00 \uC5C6\uC73C\uBA74(\uD658\uAC01 \uC758\uC2EC) \uC800\uC7A5\uC774 \uB9C9\uD799\uB2C8\uB2E4. \uADF8\uB54C\uB294 \uC6D0\uBCF8\uC758 \uC2E4\uC81C \uC218\uCE58\uB85C \uACE0\uCE58\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774\uBA74 force:true\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694.',
96455
96969
  inputSchema: CoverLetterVersionInputSchema.shape,
96456
96970
  handler: (args) => {
96457
96971
  try {
@@ -96514,7 +97028,7 @@ ${lines}`;
96514
97028
  {
96515
97029
  name: "get_job_posting",
96516
97030
  title: "\uCC44\uC6A9\uACF5\uACE0 \uC0C1\uC138 \uC870\uD68C",
96517
- description: "\uACF5\uACE0 1\uAC74\uC758 \uC0C1\uC138\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. \uACF5\uACE0 \uC815\uBCF4\uC640 \uD568\uAED8 \uC801\uD569\uB3C4 \uBD84\uC11D, \uC9C0\uC6D0 \uC0C1\uD0DC, \uC5F0\uACB0\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C, \uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uD55C \uBC88\uC5D0 \uB3CC\uB824\uC90D\uB2C8\uB2E4.",
97031
+ description: "\uACF5\uACE0 1\uAC74\uC758 \uC0C1\uC138\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. \uACF5\uACE0 \uC815\uBCF4\uC640 \uD568\uAED8 \uC801\uD569\uB3C4 \uBD84\uC11D, \uC9C0\uC6D0 \uC0C1\uD0DC, \uC790\uAE30\uC18C\uAC1C\uC11C, \uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uD55C \uBC88\uC5D0 \uB3CC\uB824\uC90D\uB2C8\uB2E4.",
96518
97032
  inputSchema: { job_id: external_exports.string() },
96519
97033
  readOnly: true,
96520
97034
  handler: (args) => {
@@ -96580,13 +97094,42 @@ ${lines}`;
96580
97094
  const ctx = getApplicationContext({ job_id: args?.job_id });
96581
97095
  const recommended_route = pickRoute(ctx);
96582
97096
  const route = recommended_route ? getRoute(recommended_route) : void 0;
97097
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
97098
+ const ym = (s) => {
97099
+ const m = s ? /^(\d{4})-(\d{2})/.exec(s) : null;
97100
+ return m ? Number(m[1]) * 12 + Number(m[2]) : null;
97101
+ };
97102
+ const monthsBetween = (a, b) => {
97103
+ const x2 = ym(a), y = ym(b);
97104
+ return x2 != null && y != null ? y - x2 : null;
97105
+ };
97106
+ const tenure = ctx.experiences.map((e) => {
97107
+ if (!e.start_date) return null;
97108
+ const asOf = e.is_current ? e.updated_at ?? today : e.end_date;
97109
+ const months = monthsBetween(e.start_date, asOf);
97110
+ if (months == null || months < 0) return null;
97111
+ return {
97112
+ company: e.company,
97113
+ is_current: e.is_current,
97114
+ start_date: e.start_date,
97115
+ as_of: (asOf ?? "").slice(0, 7),
97116
+ // 재직중=updated_at(정보 확인 시점) / 종료=end_date
97117
+ months,
97118
+ years: Math.round(months / 12 * 10) / 10,
97119
+ // 음수 방지(미래 updated_at·시계 오차) — 0 미만은 0으로 클램프
97120
+ data_age_months: e.is_current ? Math.max(0, monthsBetween(e.updated_at, today) ?? 0) : null
97121
+ };
97122
+ }).filter((t) => t != null);
96583
97123
  const enriched = {
96584
97124
  ...ctx,
97125
+ today,
97126
+ tenure,
96585
97127
  recommended_route,
96586
97128
  verifier_sequence: route?.verifierSequence ?? [],
96587
97129
  next_tool: recommended_route ? "get_workflow_guide" : null
96588
97130
  };
96589
97131
  const summary = [
97132
+ `\uC624\uB298 \uB0A0\uC9DC: ${today}. \uACBD\uB825 \uC5F0\uCC28\xB7\uC7AC\uC9C1\uAE30\uAC04\uC740 \uC9C1\uC811 \uACC4\uC0B0\uD558\uC9C0 \uB9D0\uACE0 \uC81C\uACF5\uB41C tenure[](\uC11C\uBC84 \uACC4\uC0B0: \uC7AC\uC9C1\uC911=\uC815\uBCF4\uD655\uC778\uC77C \uAE30\uC900 \uC5F0\uCC28, data_age_months \uD06C\uBA74 \uD604\uC7AC \uC7AC\uC9C1 \uD655\uC778 \uD544\uC694)\uB97C \uADF8\uB300\uB85C \uC4F0\uC138\uC694.`,
96590
97133
  `\uD504\uB85C\uD544: ${ctx.profile?.name ?? "\uBBF8\uC785\uB825"}`,
96591
97134
  `\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}`,
96592
97135
  ctx.job ? `\uB300\uC0C1 \uACF5\uACE0: ${ctx.job.company} \xB7 ${ctx.job.position}` : "\uB300\uC0C1 \uACF5\uACE0 \uC5C6\uC74C",
@@ -96605,7 +97148,7 @@ ${lines}`;
96605
97148
  try {
96606
97149
  const fit = saveFitAnalysis(args);
96607
97150
  const suggested_next_action = {
96608
- prompt: "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?",
97151
+ prompt: "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694? (\uD68C\uC0AC \uC9C0\uC815 \uBB38\uD56D\xB7\uC591\uC2DD\xB7\uAE00\uC790\uC218\uAC00 \uC788\uC73C\uBA74 \uD568\uAED8 \uC54C\uB824\uC8FC\uC2DC\uBA74 \uADF8\uC5D0 \uB9DE\uCDB0 \uC791\uC131\uD569\uB2C8\uB2E4)",
96609
97152
  options: [
96610
97153
  {
96611
97154
  label: "\uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918",
@@ -96624,7 +97167,8 @@ ${lines}`;
96624
97167
  "",
96625
97168
  "\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB2E4\uC74C \uC120\uD0DD\uC9C0\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694:",
96626
97169
  "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"
97170
+ "2. \uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918",
97171
+ "(1\uBC88\uC774\uBA74, \uD68C\uC0AC \uC9C0\uC815 \uBB38\uD56D\xB7\uC591\uC2DD\xB7\uAE00\uC790\uC218\uAC00 \uC788\uC73C\uBA74 \uC54C\uB824\uB2EC\uB77C\uACE0 \uC694\uCCAD\uD558\uC138\uC694 \u2014 \uD55C\uAD6D \uC790\uC18C\uC11C\uB294 \uBB38\uD56D\uD615\uC774 \uB9CE\uC2B5\uB2C8\uB2E4. \uC5C6\uC73C\uBA74 \uC77C\uBC18 \uC591\uC2DD\uC73C\uB85C \uC9C4\uD589\uD569\uB2C8\uB2E4.)"
96628
97172
  ].join("\n"),
96629
97173
  {
96630
97174
  ...fit,
@@ -96645,11 +97189,12 @@ ${lines}`;
96645
97189
  inputSchema: {
96646
97190
  job_id: external_exports.string(),
96647
97191
  status: external_exports.enum(APPLICATION_STATUSES),
96648
- note: external_exports.string().optional()
97192
+ note: external_exports.string().optional(),
97193
+ submission: ApplicationSubmissionSchema.optional()
96649
97194
  },
96650
97195
  handler: (args) => {
96651
97196
  try {
96652
- const res = updateApplicationStatus(args.job_id, args.status, args.note);
97197
+ const res = updateApplicationStatus(args.job_id, args.status, args.note, args.submission);
96653
97198
  const user_message = `\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[args.status]}'(\uC73C)\uB85C \uBC14\uAFE8\uC5B4\uC694.` + (res.hint ? `
96654
97199
 
96655
97200
  ${res.hint}` : "");
@@ -96673,12 +97218,14 @@ ${res.hint}` : "");
96673
97218
  {
96674
97219
  name: "save_interview_prep",
96675
97220
  title: "\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC \uC800\uC7A5",
96676
- description: "\uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38, \uAF2C\uB9AC \uC9C8\uBB38, STAR \uB2F5\uBCC0 \uAC00\uC774\uB4DC, 1\uBD84 \uC790\uAE30\uC18C\uAC1C \uCD08\uC548 \uB4F1 \uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. \uBCF4\uD1B5 \uC9C0\uC6D0 \uC0C1\uD0DC\uAC00 \uC11C\uB958 \uD569\uACA9\uC73C\uB85C \uBC14\uB010 \uB4A4, \uD574\uB2F9 \uACF5\uACE0/\uC9C1\uBB34/\uC790\uC18C\uC11C\uB97C \uADFC\uAC70\uB85C \uB2F9\uC2E0(AI)\uC774 \uC0DD\uC131\uD55C \uC790\uB8CC\uB97C \uC800\uC7A5\uD558\uB294 \uB370 \uC0AC\uC6A9\uD569\uB2C8\uB2E4. job_id\uB294 \uD544\uC218\uC785\uB2C8\uB2E4. \uAC19\uC740 \uACF5\uACE0\uC5D0 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.",
97221
+ description: "\uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38, \uAF2C\uB9AC \uC9C8\uBB38, STAR \uB2F5\uBCC0 \uAC00\uC774\uB4DC, 1\uBD84 \uC790\uAE30\uC18C\uAC1C \uCD08\uC548 \uB4F1 \uD574\uB2F9 \uACF5\uACE0 \uAE30\uC900 \uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. \uACF5\uACE0/\uC9C1\uBB34/\uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uADFC\uAC70\uB85C \uB2F9\uC2E0(AI)\uC774 \uC0DD\uC131\uD55C \uC790\uB8CC\uB97C \uC800\uC7A5\uD558\uB294 \uB370 \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uC9C8\uBB38\uC740 \uB450 \uAC08\uB798\uB85C \uC900\uBE44\uD558\uC138\uC694 \u2014 \uAC01 \uC9C8\uBB38\uC758 category\uB97C 'technical'(\uAE30\uC220\xB7\uC9C1\uBB34 1\uCC28 \uBA74\uC811) \uB610\uB294 'behavioral'(\uC778\uC131\xB7\uCEEC\uCC98\uD54F\xB7\uC784\uC6D0 2\uCC28 \uBA74\uC811, \uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uD504\uB85C\uD544 \uAE30\uBC18)\uB85C \uC9C0\uC815\uD569\uB2C8\uB2E4. \uAC00\uB2A5\uD558\uBA74 \uB450 \uAC08\uB798 \uBAA8\uB450 \uCC44\uC6CC \uD55C \uBC88\uC5D0 \uC800\uC7A5\uD558\uC138\uC694(\uB300\uC2DC\uBCF4\uB4DC\uAC00 \uAE30\uC220/\uC778\uC131\xB7\uCEEC\uCC98\uD54F \uD0ED\uC73C\uB85C \uB098\uB220 \uBCF4\uC5EC\uC90D\uB2C8\uB2E4). job_id\uB294 \uD544\uC218\uC785\uB2C8\uB2E4. \uAC19\uC740 \uACF5\uACE0\uC5D0 \uB2E4\uC2DC \uC800\uC7A5\uD558\uBA74 \uAC31\uC2E0\uB429\uB2C8\uB2E4.",
96677
97222
  inputSchema: InterviewPrepInputSchema.shape,
96678
97223
  handler: (args) => {
96679
97224
  try {
96680
97225
  const prep = saveInterviewPrep(args);
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.`;
97226
+ const tech = prep.questions.filter((q) => (q.category ?? "technical") === "technical").length;
97227
+ const behavioral = prep.questions.filter((q) => q.category === "behavioral").length;
97228
+ const user_message = `\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uB97C \uC800\uC7A5\uD588\uC5B4\uC694. \uC608\uC0C1 \uC9C8\uBB38 ${prep.questions.length}\uAC1C` + (behavioral > 0 ? ` (\uAE30\uC220 ${tech}\uAC1C \xB7 \uC778\uC131\xB7\uCEEC\uCC98\uD54F ${behavioral}\uAC1C)` : "") + "\uAC00 \uC900\uBE44\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.";
96682
97229
  return ok(user_message, { ...prep, user_message });
96683
97230
  } catch (e) {
96684
97231
  return fail(e instanceof Error ? e.message : "\uC800\uC7A5 \uC2E4\uD328");
@@ -96709,6 +97256,65 @@ ${res.hint}` : "");
96709
97256
  return fail(`\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}`);
96710
97257
  }
96711
97258
  return ok(`'${cl.title}'\uB97C ${format.toUpperCase()}\uB85C \uB0B4\uBCF4\uB0C8\uC2B5\uB2C8\uB2E4.
97259
+ \uC800\uC7A5 \uC704\uCE58: ${filePath}`, {
97260
+ path: filePath,
97261
+ filename: result2.filename,
97262
+ content: result2.content
97263
+ });
97264
+ }
97265
+ },
97266
+ {
97267
+ name: "export_resume",
97268
+ title: "\uC774\uB825\uC11C/\uACBD\uB825\uAE30\uC220\uC11C \uB0B4\uBCF4\uB0B4\uAE30",
97269
+ description: '\uC800\uC7A5\uB41C \uC774\uB825\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C \uB4F1 \uBB38\uC11C\uB97C Markdown \uB610\uB294 \uC778\uC1C4\uC6A9 HTML(\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C PDF\uB85C \uC800\uC7A5) \uD30C\uC77C\uB85C \uB0B4\uBCF4\uB0B4 \uB370\uC774\uD130 \uD3F4\uB354\uC758 exports\uC5D0 \uC800\uC7A5\uD558\uACE0, \uD30C\uC77C \uACBD\uB85C\uC640 \uBCF8\uBB38\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 \uACF5\uACE0\uC5D0 \uB9DE\uCDB0 \uC815\uB9AC\uD55C \uC774\uB825\uC11C\uB97C \uBA3C\uC800 add_resume\uB85C \uC800\uC7A5\uD55C \uB4A4, "\uC774\uB825\uC11C \uD30C\uC77C\uB85C \uBC1B\uACE0 \uC2F6\uC5B4"\uB77C\uACE0 \uD560 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. document_id\uB294 get_resumes\uB85C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C \uC9C1\uC811 \uB2E4\uC6B4\uB85C\uB4DC\uD560 \uC218\uB3C4 \uC788\uC2B5\uB2C8\uB2E4.',
97270
+ inputSchema: {
97271
+ document_id: external_exports.string(),
97272
+ format: external_exports.enum(["md", "html"]).optional().describe("\uAE30\uBCF8 md")
97273
+ },
97274
+ handler: (args) => {
97275
+ const doc = documentRepo.get(args.document_id);
97276
+ if (!doc) return fail("\uBB38\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
97277
+ const profile = profileRepo.get();
97278
+ const format = args.format ?? "md";
97279
+ const result2 = format === "html" ? resumeToHtml(doc, profile) : resumeToMarkdown(doc, profile);
97280
+ const dir = getExportsDir();
97281
+ const filePath = path7.join(dir, result2.filename);
97282
+ try {
97283
+ fs7.writeFileSync(filePath, result2.content, "utf8");
97284
+ } catch (e) {
97285
+ return fail(`\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}`);
97286
+ }
97287
+ return ok(`'${doc.title}'\uB97C ${format.toUpperCase()}\uB85C \uB0B4\uBCF4\uB0C8\uC2B5\uB2C8\uB2E4.
97288
+ \uC800\uC7A5 \uC704\uCE58: ${filePath}`, {
97289
+ path: filePath,
97290
+ filename: result2.filename,
97291
+ content: result2.content
97292
+ });
97293
+ }
97294
+ },
97295
+ {
97296
+ name: "export_profile",
97297
+ title: "\uD504\uB85C\uD544\uC744 \uC774\uB825\uC11C\uB85C \uB0B4\uBCF4\uB0B4\uAE30",
97298
+ description: '\uC800\uC7A5\uB41C \uD504\uB85C\uD544\xB7\uACBD\uB825\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uBCF4\uC720 \uAE30\uC220\uC744 \uD55C \uC7A5\uC758 \uC774\uB825\uC11C \uD615\uC2DD\uC73C\uB85C \uBAA8\uC544 Markdown \uB610\uB294 \uC778\uC1C4\uC6A9 HTML(\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C PDF\uB85C \uC800\uC7A5) \uD30C\uC77C\uB85C \uB0B4\uBCF4\uB0B4 \uB370\uC774\uD130 \uD3F4\uB354\uC758 exports\uC5D0 \uC800\uC7A5\uD558\uACE0, \uD30C\uC77C \uACBD\uB85C\uC640 \uBCF8\uBB38\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 "\uB0B4 \uD504\uB85C\uD544\uC744 \uC774\uB825\uC11C\uB85C \uBF51\uC544\uC918"\uCC98\uB7FC \uAD6C\uC870\uD654\uB41C \uB370\uC774\uD130 \uC804\uCCB4\uB97C \uC774\uB825\uC11C\uB85C \uBC1B\uACE0 \uC2F6\uC744 \uB54C \uC0AC\uC6A9\uD558\uC138\uC694. \uD2B9\uC815 \uACF5\uACE0 \uB9DE\uCDA4 \uC774\uB825\uC11C\uB294 add_resume\uB85C \uC815\uB9AC\xB7\uC800\uC7A5\uD55C \uB4A4 export_resume\uB97C \uC4F0\uC138\uC694. \uB300\uC2DC\uBCF4\uB4DC\uC5D0\uC11C \uC9C1\uC811 \uB2E4\uC6B4\uB85C\uB4DC\uD560 \uC218\uB3C4 \uC788\uC2B5\uB2C8\uB2E4.',
97299
+ inputSchema: {
97300
+ format: external_exports.enum(["md", "html"]).optional().describe("\uAE30\uBCF8 md")
97301
+ },
97302
+ handler: (args) => {
97303
+ const profile = profileRepo.get();
97304
+ if (!profile) return fail("\uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uD504\uB85C\uD544\uC744 \uC800\uC7A5\uD558\uC138\uC694.");
97305
+ const format = args.format ?? "md";
97306
+ const experiences = experienceRepo.list();
97307
+ const projects = projectRepo.list();
97308
+ const skills = skillRepo.list();
97309
+ const result2 = format === "html" ? profileToHtml(profile, experiences, projects, skills) : profileToMarkdown(profile, experiences, projects, skills);
97310
+ const dir = getExportsDir();
97311
+ const filePath = path7.join(dir, result2.filename);
97312
+ try {
97313
+ fs7.writeFileSync(filePath, result2.content, "utf8");
97314
+ } catch (e) {
97315
+ return fail(`\uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${e instanceof Error ? e.message : e}`);
97316
+ }
97317
+ return ok(`\uD504\uB85C\uD544\uC744 \uC774\uB825\uC11C(${format.toUpperCase()})\uB85C \uB0B4\uBCF4\uB0C8\uC2B5\uB2C8\uB2E4.
96712
97318
  \uC800\uC7A5 \uC704\uCE58: ${filePath}`, {
96713
97319
  path: filePath,
96714
97320
  filename: result2.filename,
@@ -96760,9 +97366,17 @@ ${res.hint}` : "");
96760
97366
  {
96761
97367
  name: "get_workflow_guide",
96762
97368
  title: "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uAC00\uC774\uB4DC",
96763
- description: '\uCEE4\uB9AC\uC5B4 \uC791\uC5C5(\uACF5\uACE0 \uBD84\uC11D\xB7\uC790\uC18C\uC11C\xB7\uBA74\uC811 \uC900\uBE44 \uB4F1)\uC744 \uC2DC\uC791\uD558\uAE30 \uC9C1\uC804\uC5D0 \uD638\uCD9C\uD558\uC138\uC694. \uD45C\uC900 \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC758 \uB2E8\uACC4\uBCC4 \uC548\uB0B4\uC5D0 \uB354\uD574, \uADF8 \uC791\uC5C5\uC758 "\uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28(EOP)"\uC640 \uC801\uC6A9\uD560 \uC804\uBB38\uAC00 \uD50C\uB808\uC774\uBD81\xB7\uC800\uC7A5 \uC804 \uAC80\uC99D\uAE30 \uC21C\uC11C\uB97C \uD55C \uBC88\uC5D0 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC548\uB0B4\uB41C \uB300\uB85C get_playbook\uC73C\uB85C \uB3C4\uBA54\uC778 \uC9C0\uC2DD\uC744, \uC800\uC7A5 \uC9C1\uC804 get_verifier\uB85C \uAC80\uC99D \uB8E8\uBE0C\uB9AD\uC744 \uBC1B\uC544 \uC801\uC6A9\uD558\uC138\uC694. workflow_id\uB97C \uC0DD\uB7B5\uD558\uBA74 \uC804\uCCB4 \uBAA9\uB85D\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4.',
97369
+ description: '\uCEE4\uB9AC\uC5B4 \uC791\uC5C5(\uACF5\uACE0 \uBD84\uC11D\xB7\uC790\uC18C\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uBA74\uC811 \uC900\uBE44 \uB4F1)\uC744 \uC2DC\uC791\uD558\uAE30 \uC9C1\uC804\uC5D0 \uD638\uCD9C\uD558\uC138\uC694. \uD45C\uC900 \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC758 \uB2E8\uACC4\uBCC4 \uC548\uB0B4\uC5D0 \uB354\uD574, \uADF8 \uC791\uC5C5\uC758 "\uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28(EOP)"\uC640 \uC801\uC6A9\uD560 \uC804\uBB38\uAC00 \uD50C\uB808\uC774\uBD81\xB7\uC800\uC7A5 \uC804 \uAC80\uC99D\uAE30 \uC21C\uC11C\uB97C \uD55C \uBC88\uC5D0 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC548\uB0B4\uB41C \uB300\uB85C get_playbook\uC73C\uB85C \uB3C4\uBA54\uC778 \uC9C0\uC2DD\uC744, \uC800\uC7A5 \uC9C1\uC804 get_verifier\uB85C \uAC80\uC99D \uB8E8\uBE0C\uB9AD\uC744 \uBC1B\uC544 \uC801\uC6A9\uD558\uC138\uC694. workflow_id\uB97C \uC0DD\uB7B5\uD558\uBA74 \uC804\uCCB4 \uBAA9\uB85D\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4.',
96764
97370
  inputSchema: {
96765
- workflow_id: external_exports.enum(["onboarding", "analyze_job", "write_cover_letter", "manage_application_status", "prepare_interview"]).optional()
97371
+ workflow_id: external_exports.enum([
97372
+ "onboarding",
97373
+ "analyze_job",
97374
+ "write_cover_letter",
97375
+ "write_career_description",
97376
+ "manage_application_status",
97377
+ "prepare_interview",
97378
+ "build_personal_brand"
97379
+ ]).optional()
96766
97380
  },
96767
97381
  readOnly: true,
96768
97382
  handler: (args) => {
@@ -96857,10 +97471,23 @@ ${report.disclaimer}`, report);
96857
97471
  {
96858
97472
  name: "get_writing_style_guide",
96859
97473
  title: "AI \uD2F0 \uC548 \uB098\uB294 \uAE00\uC4F0\uAE30 \uAC00\uC774\uB4DC",
96860
- description: '\uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uC790\uAE30 PR\xB7\uC9C0\uC6D0 \uBA54\uC77C \uB4F1 \uC0AC\uB78C\uC774 \uC4F4 \uB4EF\uD55C \uAE00\uC744 \uC791\uC131\uD558\uAE30 \uC9C1\uC804\uC5D0 \uD638\uCD9C\uD558\uC138\uC694. "AI\uAC00 \uC4F4 \uD2F0"\uAC00 \uB098\uB294 \uD55C\uAD6D\uC5B4 \uBB38\uC7A5 \uC2B5\uAD00(\uBC88\uC5ED\uD22C, \uD074\uB9AC\uC170, \uAE30\uACC4\uC801 \uBCD1\uB82C, \uC0C1\uD22C\uC801 \uC5F0\uACB0\uC5B4, \uADE0\uC77C\uD55C \uBB38\uC7A5 \uB9AC\uB4EC \uB4F1)\uC744 \uC81C\uAC70\uD558\uB294 \uC791\uC131 \uADDC\uCE59\uACFC \uC800\uC7A5 \uC804 \uC790\uAC00 \uC810\uAC80 \uD56D\uBAA9\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uADF8\uB300\uB85C \uB450\uACE0 \uBB38\uC7A5 \uACB0\uB9CC \uB2E4\uB4EC\uB3C4\uB85D \uC548\uB0B4\uD569\uB2C8\uB2E4. write_cover_letter / analyze_job \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC5D0\uC11C \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC4F8 \uB54C \uBC18\uB4DC\uC2DC \uD568\uAED8 \uC801\uC6A9\uD558\uC138\uC694.',
96861
- inputSchema: {},
97474
+ description: '\uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uC790\uAE30 PR\xB7\uC9C0\uC6D0 \uBA54\uC77C \uB4F1 \uC0AC\uB78C\uC774 \uC4F4 \uB4EF\uD55C \uAE00\uC744 \uC791\uC131\uD558\uAE30 \uC9C1\uC804\uC5D0 \uD638\uCD9C\uD558\uC138\uC694. "AI\uAC00 \uC4F4 \uD2F0"\uAC00 \uB098\uB294 \uD55C\uAD6D\uC5B4 \uBB38\uC7A5 \uC2B5\uAD00(\uBC88\uC5ED\uD22C, \uD074\uB9AC\uC170, \uAE30\uACC4\uC801 \uBCD1\uB82C, \uC0C1\uD22C\uC801 \uC5F0\uACB0\uC5B4, \uADE0\uC77C\uD55C \uBB38\uC7A5 \uB9AC\uB4EC \uB4F1)\uC744 \uC81C\uAC70\uD558\uB294 \uC791\uC131 \uADDC\uCE59\uACFC \uC800\uC7A5 \uC804 \uC790\uAC00 \uC810\uAC80 \uD56D\uBAA9\uC744 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uADF8\uB300\uB85C \uB450\uACE0 \uBB38\uC7A5 \uACB0\uB9CC \uB2E4\uB4EC\uB3C4\uB85D \uC548\uB0B4\uD569\uB2C8\uB2E4. \uACBD\uB825\uAE30\uC220\uC11C\xB7\uC774\uB825\uC11C\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624\uB97C \uC4F8 \uB54C\uB294 doc_type\uC744 \uD568\uAED8 \uC8FC\uBA74 \uBD88\uB9BF\xB7\uBC88\uD638 \uC131\uACFC \uAC19\uC740 \uAD6C\uC870\uB97C \uC0B0\uBB38\uC73C\uB85C \uD3C9\uD0C4\uD654\uD558\uC9C0 \uC54A\uB3C4\uB85D \uAD6C\uC870 \uBCF4\uC874 \uBCF4\uC815 \uC9C0\uCE68\uC744 \uB354\uD574 \uB3CC\uB824\uC90D\uB2C8\uB2E4. write_cover_letter / write_career_description / analyze_job \uC6CC\uD06C\uD50C\uB85C\uC6B0\uC5D0\uC11C \uAE00\uC744 \uC4F8 \uB54C \uBC18\uB4DC\uC2DC \uD568\uAED8 \uC801\uC6A9\uD558\uC138\uC694.',
97475
+ inputSchema: {
97476
+ doc_type: external_exports.enum([
97477
+ "cover_letter",
97478
+ "self_pr",
97479
+ "application_email",
97480
+ "career_description",
97481
+ "resume",
97482
+ "portfolio"
97483
+ ]).optional().describe(
97484
+ "\uC791\uC131\uD560 \uBB38\uC11C \uC720\uD615. career_description\xB7resume\xB7portfolio\uB294 \uBD88\uB9BF\xB7\uAD6C\uC870\uB97C \uBCF4\uC874\uD558\uB294 \uBCF4\uC815\uC744 \uB354\uD574 \uB3CC\uB824\uC90D\uB2C8\uB2E4. \uC0DD\uB7B5\uD558\uBA74 \uC790\uAE30\uC18C\uAC1C\uC11C \uB4F1 \uC0B0\uBB38\uD615 \uAE30\uC900."
97485
+ )
97486
+ },
96862
97487
  readOnly: true,
96863
- handler: () => ok("AI \uD2F0 \uC548 \uB098\uB294 \uAE00\uC4F0\uAE30 \uAC00\uC774\uB4DC\uB97C \uC801\uC6A9\uD574 \uC791\uC131\uD558\uC138\uC694.", { guide: HUMANIZE_WRITING_GUIDE })
97488
+ handler: (args) => ok("AI \uD2F0 \uC548 \uB098\uB294 \uAE00\uC4F0\uAE30 \uAC00\uC774\uB4DC\uB97C \uC801\uC6A9\uD574 \uC791\uC131\uD558\uC138\uC694.", {
97489
+ guide: getWritingStyleGuide(args?.doc_type)
97490
+ })
96864
97491
  },
96865
97492
  /* ----------------------------------------------------------------- update */
96866
97493
  {
@@ -96870,7 +97497,7 @@ ${report.disclaimer}`, report);
96870
97497
  inputSchema: {},
96871
97498
  readOnly: true,
96872
97499
  handler: async () => {
96873
- const u = await getUpdateStatusAsync();
97500
+ const u = await getUpdateStatusAsync(true);
96874
97501
  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
97502
  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}).`;
96876
97503
  return ok(
@@ -96907,7 +97534,7 @@ async function main() {
96907
97534
  const server = new McpServer(
96908
97535
  { name: "careermate", version: APP_VERSION },
96909
97536
  {
96910
- instructions: "CareerMate\uB294 \uC0AC\uC6A9\uC790\uC758 \uB85C\uCEEC \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC785\uB2C8\uB2E4. \uACF5\uACE0 \uBD84\uC11D\uC774\uB098 \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131 \uC804\uC5D0\uB294 \uD56D\uC0C1 get_application_context\uB97C \uBA3C\uC800 \uD638\uCD9C\uD574 \uC0AC\uC6A9\uC790 \uC815\uBCF4\uB97C \uAC00\uC838\uC624\uACE0(\uC751\uB2F5\uC758 recommended_route\xB7next_tool\uAC00 \uB2E4\uC74C \uD638\uCD9C\uC744 \uC548\uB0B4\uD569\uB2C8\uB2E4), \uC774\uC5B4 get_workflow_guide\uB85C \uADF8 \uC791\uC5C5\uC758 \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28\uC640 \uAC80\uC99D \uC21C\uC11C\uB97C \uBC1B\uC544 \uC801\uC6A9\uD558\uC138\uC694. \uACB0\uACFC\uB294 save_fit_analysis / save_cover_letter_version \uB4F1\uC73C\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD558\uB418, \uC800\uC7A5 \uC9C1\uC804 \uC548\uB0B4\uB41C get_verifier \uB8E8\uBE0C\uB9AD\uC73C\uB85C \uB2F9\uC2E0\uC774 \uC2A4\uC2A4\uB85C \uC810\uAC80\uD558\uC138\uC694 \u2014 CareerMate\uB294 \uC140 \uC218 \uC788\uB294 \uD56D\uBAA9\uB9CC \uB3CC\uB824\uC8FC\uACE0 \uBD84\uC11D\xB7\uC791\uC131\xB7\uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uD569\uB2C8\uB2E4. \uBAA8\uB4E0 \uB370\uC774\uD130\uB294 \uC0AC\uC6A9\uC790 \uB85C\uCEEC\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4."
97537
+ instructions: "CareerMate\uB294 \uC0AC\uC6A9\uC790\uC758 \uB85C\uCEEC \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC785\uB2C8\uB2E4. \uACF5\uACE0 \uBD84\uC11D, \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131, \uACBD\uB825\uAE30\uC220\uC11C \uC791\uC131 \uC804\uC5D0\uB294 \uD56D\uC0C1 \uC800\uC7A5\uB41C \uC0AC\uC6A9\uC790 \uC815\uBCF4\uB97C \uBA3C\uC800 \uAC00\uC838\uC624\uACE0(\uACF5\uACE0 \uC911\uC2EC \uC791\uC5C5\uC740 get_application_context \uC751\uB2F5\uC758 recommended_route\xB7next_tool\uAC00 \uB2E4\uC74C \uD638\uCD9C\uC744 \uC548\uB0B4\uD569\uB2C8\uB2E4), \uC774\uC5B4 get_workflow_guide\uB85C \uADF8 \uC791\uC5C5\uC758 \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28\uC640 \uAC80\uC99D \uC21C\uC11C\uB97C \uBC1B\uC544 \uC801\uC6A9\uD558\uC138\uC694. \uACB0\uACFC\uB294 save_fit_analysis / save_cover_letter_version / add_resume(kind=career_description) \uB4F1\uC73C\uB85C \uB2E4\uC2DC \uC800\uC7A5\uD558\uB418(\uC5EC\uB7EC \uBB38\uC11C\uB97C \uD55C \uBC88\uC5D0 \uC694\uCCAD\uBC1B\uC544\uB3C4 \uAC01 \uBB38\uC11C\uB294 \uAC01\uC790\uC758 \uC800\uC7A5 \uB3C4\uAD6C\uB85C \uB530\uB85C \uC800\uC7A5\uD558\uACE0 \uD55C \uC800\uC7A5 \uBCF8\uBB38\uC5D0 \uB2E4\uB978 \uBB38\uC11C\uB97C \uC774\uC5B4\uBD99\uC774\uC9C0 \uB9C8\uC138\uC694 \u2014 \uACBD\uB825\uAE30\uC220\uC11C=add_resume, \uC790\uAE30\uC18C\uAC1C\uC11C=save_cover_letter_version), \uC800\uC7A5 \uC9C1\uC804 \uC548\uB0B4\uB41C get_verifier \uB8E8\uBE0C\uB9AD\uC73C\uB85C \uB2F9\uC2E0\uC774 \uC2A4\uC2A4\uB85C \uC810\uAC80\uD558\uC138\uC694 \u2014 CareerMate\uB294 \uC140 \uC218 \uC788\uB294 \uD56D\uBAA9\uB9CC \uB3CC\uB824\uC8FC\uACE0 \uBD84\uC11D\xB7\uC791\uC131\xB7\uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uD569\uB2C8\uB2E4. \uBAA8\uB4E0 \uB370\uC774\uD130\uB294 \uC0AC\uC6A9\uC790 \uB85C\uCEEC\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB2F5\uD560 \uB54C\uB294 \uD56D\uC0C1 \uC0AC\uC6A9\uC790\uAC00 \uC4F4 \uC5B8\uC5B4\uB85C \uB2F5\uD558\uC138\uC694 \u2014 \uC774 \uC9C0\uCE68\uACFC \uB3C4\uAD6C \uC751\uB2F5\uC774 \uD55C\uAD6D\uC5B4\uB85C \uC4F0\uC5EC \uC788\uC5B4\uB3C4 \uADF8\uB300\uB85C \uBCF5\uC0AC\uD558\uC9C0 \uB9D0\uACE0 \uC0AC\uC6A9\uC790 \uC5B8\uC5B4\uB85C \uC62E\uACA8 \uC804\uB2EC\uD558\uACE0, \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uBCF4\uC874\uD569\uB2C8\uB2E4."
96911
97538
  }
96912
97539
  );
96913
97540
  for (const tool of TOOLS) {
@@ -96936,6 +97563,18 @@ async function main() {
96936
97563
  }
96937
97564
  const transport = new StdioServerTransport();
96938
97565
  await server.connect(transport);
97566
+ let closing = false;
97567
+ const shutdown = () => {
97568
+ if (closing) return;
97569
+ closing = true;
97570
+ try {
97571
+ closeDb();
97572
+ } catch {
97573
+ }
97574
+ process.exit(0);
97575
+ };
97576
+ process.once("SIGINT", shutdown);
97577
+ process.once("SIGTERM", shutdown);
96939
97578
  console.error(`[careermate-mcp] \uC5F0\uACB0\uB428 \xB7 \uB3C4\uAD6C ${TOOLS.length}\uAC1C \xB7 \uB370\uC774\uD130: ${getDataDir()}`);
96940
97579
  }
96941
97580
  main().catch((err2) => {